diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sd/source/ui | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
712 files changed, 203348 insertions, 0 deletions
diff --git a/sd/source/ui/accessibility/AccessibleDocumentViewBase.cxx b/sd/source/ui/accessibility/AccessibleDocumentViewBase.cxx new file mode 100644 index 000000000..c297184fa --- /dev/null +++ b/sd/source/ui/accessibility/AccessibleDocumentViewBase.cxx @@ -0,0 +1,773 @@ +/* -*- 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 <AccessibleDocumentViewBase.hxx> +#include <com/sun/star/drawing/XDrawView.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/document/XShapeEventBroadcaster.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <rtl/ustrbuf.hxx> +#include <sfx2/viewfrm.hxx> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <sfx2/objsh.hxx> +#include <tools/debug.hxx> + +#include <cppuhelper/queryinterface.hxx> +#include <svx/svdobj.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <Window.hxx> +#include <OutlineViewShell.hxx> + +#include <svx/svdlayer.hxx> +#include <editeng/editobj.hxx> +#include <LayerTabBar.hxx> +#include <svtools/colorcfg.hxx> +#include <ViewShell.hxx> +#include <View.hxx> +#include <drawdoc.hxx> +#include <editeng/outlobj.hxx> +#include <sdpage.hxx> +#include <DrawViewShell.hxx> +#include <PresentationViewShell.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; +using ::com::sun::star::uno::Reference; + +namespace accessibility { + +//===== internal ============================================================ +AccessibleDocumentViewBase::AccessibleDocumentViewBase ( + ::sd::Window* pSdWindow, + ::sd::ViewShell* pViewShell, + const uno::Reference<frame::XController>& rxController, + const uno::Reference<XAccessible>& rxParent) + : AccessibleContextBase (rxParent, + pViewShell->GetDoc()->GetDocumentType() == DocumentType::Impress ? + AccessibleRole::DOCUMENT_PRESENTATION : + AccessibleRole::DOCUMENT), + mxController (rxController), + maViewForwarder ( + static_cast<SdrPaintView*>(pViewShell->GetView()), + *pSdWindow->GetOutDev()) +{ + if (mxController.is()) + mxModel = mxController->getModel(); + + // Fill the shape tree info. + maShapeTreeInfo.SetModelBroadcaster ( + uno::Reference<document::XShapeEventBroadcaster>( + mxModel, uno::UNO_QUERY_THROW)); + maShapeTreeInfo.SetController (mxController); + maShapeTreeInfo.SetSdrView (pViewShell->GetView()); + maShapeTreeInfo.SetWindow (pSdWindow); + maShapeTreeInfo.SetViewForwarder (&maViewForwarder); + + mxWindow = ::VCLUnoHelper::GetInterface (pSdWindow); + mpViewShell = pViewShell; +} + +AccessibleDocumentViewBase::~AccessibleDocumentViewBase() +{ + // At this place we should be disposed. You may want to add a + // corresponding assertion into the destructor of a derived class. +} + +void AccessibleDocumentViewBase::Init() +{ + // Finish the initialization of the shape tree info container. + maShapeTreeInfo.SetDocumentWindow (this); + + // Register as window listener to stay up to date with its size and + // position. + mxWindow->addWindowListener (this); + // Register as focus listener to + mxWindow->addFocusListener (this); + + // Determine the list of shapes on the current page. + uno::Reference<drawing::XShapes> xShapeList; + uno::Reference<drawing::XDrawView> xView (mxController, uno::UNO_QUERY); + if (xView.is()) + xShapeList = xView->getCurrentPage(); + + // Register this object as dispose event listener at the model. + if (mxModel.is()) + mxModel->addEventListener ( + static_cast<awt::XWindowListener*>(this)); + + // Register as property change listener at the controller. + uno::Reference<beans::XPropertySet> xSet (mxController, uno::UNO_QUERY); + if (xSet.is()) + xSet->addPropertyChangeListener ( + "", + static_cast<beans::XPropertyChangeListener*>(this)); + + // Register this object as dispose event listener at the controller. + if (mxController.is()) + mxController->addEventListener ( + static_cast<awt::XWindowListener*>(this)); + + // Register at VCL Window to be informed of activated and deactivated + // OLE objects. + vcl::Window* pWindow = maShapeTreeInfo.GetWindow(); + if (pWindow != nullptr) + { + maWindowLink = LINK( + this, AccessibleDocumentViewBase, WindowChildEventListener); + + pWindow->AddChildEventListener (maWindowLink); + + sal_uInt16 nCount = pWindow->GetChildCount(); + for (sal_uInt16 i=0; i<nCount; i++) + { + vcl::Window* pChildWindow = pWindow->GetChild (i); + if (pChildWindow && + (AccessibleRole::EMBEDDED_OBJECT + ==pChildWindow->GetAccessibleRole())) + { + SetAccessibleOLEObject (pChildWindow->GetAccessible()); + } + } + } + SfxObjectShell* pObjShell = mpViewShell->GetViewFrame()->GetObjectShell(); + if(!pObjShell->IsReadOnly()) + SetState(AccessibleStateType::EDITABLE); +} + +IMPL_LINK(AccessibleDocumentViewBase, WindowChildEventListener, + VclWindowEvent&, rEvent, void) +{ + // DBG_ASSERT( pVclEvent->GetWindow(), "Window???" ); + switch (rEvent.GetId()) + { + case VclEventId::ObjectDying: + { + // Window is dying. Unregister from VCL Window. + // This is also attempted in the disposing() method. + vcl::Window* pWindow = maShapeTreeInfo.GetWindow(); + vcl::Window* pDyingWindow = rEvent.GetWindow(); + if (pWindow==pDyingWindow && pWindow!=nullptr && maWindowLink.IsSet()) + { + pWindow->RemoveChildEventListener (maWindowLink); + maWindowLink = Link<VclWindowEvent&,void>(); + } + } + break; + + case VclEventId::WindowShow: + { + // A new window has been created. Is it an OLE object? + vcl::Window* pChildWindow = static_cast<vcl::Window*>( + rEvent.GetData()); + if (pChildWindow!=nullptr + && (pChildWindow->GetAccessibleRole() + == AccessibleRole::EMBEDDED_OBJECT)) + { + SetAccessibleOLEObject (pChildWindow->GetAccessible()); + } + } + break; + + case VclEventId::WindowHide: + { + // A window has been destroyed. Has that been an OLE + // object? + vcl::Window* pChildWindow = static_cast<vcl::Window*>( + rEvent.GetData()); + if (pChildWindow!=nullptr + && (pChildWindow->GetAccessibleRole() + == AccessibleRole::EMBEDDED_OBJECT)) + { + SetAccessibleOLEObject (nullptr); + } + } + break; + + default: break; + } +} + +//===== IAccessibleViewForwarderListener ==================================== + +void AccessibleDocumentViewBase::ViewForwarderChanged() +{ + // Empty +} + +//===== XAccessibleContext ================================================== + +Reference<XAccessible> SAL_CALL + AccessibleDocumentViewBase::getAccessibleParent() +{ + ThrowIfDisposed (); + + return AccessibleContextBase::getAccessibleParent(); +} + +sal_Int32 SAL_CALL + AccessibleDocumentViewBase::getAccessibleChildCount() +{ + ThrowIfDisposed (); + + if (mxAccessibleOLEObject.is()) + return 1; + else + return 0; +} + +Reference<XAccessible> SAL_CALL + AccessibleDocumentViewBase::getAccessibleChild (sal_Int32 nIndex) +{ + ThrowIfDisposed (); + + ::osl::MutexGuard aGuard (m_aMutex); + if (mxAccessibleOLEObject.is()) + if (nIndex == 0) + return mxAccessibleOLEObject; + + throw lang::IndexOutOfBoundsException ( "no child with index " + OUString::number(nIndex) ); +} + +//===== XAccessibleComponent ================================================ + +/** Iterate over all children and test whether the specified point lies + within one of their bounding boxes. Return the first child for which + this is true. +*/ +uno::Reference<XAccessible > SAL_CALL + AccessibleDocumentViewBase::getAccessibleAtPoint ( + const awt::Point& aPoint) +{ + ThrowIfDisposed (); + + ::osl::MutexGuard aGuard (m_aMutex); + uno::Reference<XAccessible> xChildAtPosition; + + sal_Int32 nChildCount = getAccessibleChildCount (); + for (sal_Int32 i=nChildCount-1; i>=0; --i) + { + Reference<XAccessible> xChild (getAccessibleChild (i)); + if (xChild.is()) + { + Reference<XAccessibleComponent> xChildComponent ( + xChild->getAccessibleContext(), uno::UNO_QUERY); + if (xChildComponent.is()) + { + awt::Rectangle aBBox (xChildComponent->getBounds()); + if ( (aPoint.X >= aBBox.X) + && (aPoint.Y >= aBBox.Y) + && (aPoint.X < aBBox.X+aBBox.Width) + && (aPoint.Y < aBBox.Y+aBBox.Height) ) + { + xChildAtPosition = xChild; + break; + } + } + } + } + + // Have not found a child under the given point. Returning empty + // reference to indicate this. + return xChildAtPosition; +} + +awt::Rectangle SAL_CALL + AccessibleDocumentViewBase::getBounds() +{ + ThrowIfDisposed (); + + // Transform visible area into screen coordinates. + ::tools::Rectangle aVisibleArea ( + maShapeTreeInfo.GetViewForwarder()->GetVisibleArea()); + ::Point aPixelTopLeft ( + maShapeTreeInfo.GetViewForwarder()->LogicToPixel ( + aVisibleArea.TopLeft())); + ::Point aPixelSize ( + maShapeTreeInfo.GetViewForwarder()->LogicToPixel ( + aVisibleArea.BottomRight()) + - aPixelTopLeft); + + // Prepare to subtract the parent position to transform into relative + // coordinates. + awt::Point aParentPosition; + Reference<XAccessible> xParent = getAccessibleParent (); + if (xParent.is()) + { + Reference<XAccessibleComponent> xParentComponent ( + xParent->getAccessibleContext(), uno::UNO_QUERY); + if (xParentComponent.is()) + aParentPosition = xParentComponent->getLocationOnScreen(); + } + + return awt::Rectangle ( + aPixelTopLeft.X() - aParentPosition.X, + aPixelTopLeft.Y() - aParentPosition.Y, + aPixelSize.X(), + aPixelSize.Y()); +} + +awt::Point SAL_CALL + AccessibleDocumentViewBase::getLocation() +{ + ThrowIfDisposed (); + awt::Rectangle aBoundingBox (getBounds()); + return awt::Point (aBoundingBox.X, aBoundingBox.Y); +} + +awt::Point SAL_CALL + AccessibleDocumentViewBase::getLocationOnScreen() +{ + ThrowIfDisposed (); + ::Point aLogicalPoint (maShapeTreeInfo.GetViewForwarder()->GetVisibleArea().TopLeft()); + ::Point aPixelPoint (maShapeTreeInfo.GetViewForwarder()->LogicToPixel (aLogicalPoint)); + return awt::Point (aPixelPoint.X(), aPixelPoint.Y()); +} + +awt::Size SAL_CALL + AccessibleDocumentViewBase::getSize() +{ + ThrowIfDisposed (); + + // Transform visible area into screen coordinates. + ::tools::Rectangle aVisibleArea ( + maShapeTreeInfo.GetViewForwarder()->GetVisibleArea()); + ::Point aPixelTopLeft ( + maShapeTreeInfo.GetViewForwarder()->LogicToPixel ( + aVisibleArea.TopLeft())); + ::Point aPixelSize ( + maShapeTreeInfo.GetViewForwarder()->LogicToPixel ( + aVisibleArea.BottomRight()) + - aPixelTopLeft); + + return awt::Size (aPixelSize.X(), aPixelSize.Y()); +} + +//===== XInterface ========================================================== + +uno::Any SAL_CALL + AccessibleDocumentViewBase::queryInterface (const uno::Type & rType) +{ + uno::Any aReturn = AccessibleContextBase::queryInterface (rType); + if ( ! aReturn.hasValue()) + aReturn = ::cppu::queryInterface (rType, + static_cast<XAccessibleComponent*>(this), + static_cast<XAccessibleSelection*>(this), + static_cast<lang::XEventListener*>( + static_cast<awt::XWindowListener*>(this)), + static_cast<beans::XPropertyChangeListener*>(this), + static_cast<awt::XWindowListener*>(this), + static_cast<awt::XFocusListener*>(this) + ,static_cast<XAccessibleExtendedAttributes*>(this) + ); + return aReturn; +} + +void SAL_CALL + AccessibleDocumentViewBase::acquire() + noexcept +{ + AccessibleContextBase::acquire (); +} + +void SAL_CALL + AccessibleDocumentViewBase::release() + noexcept +{ + AccessibleContextBase::release (); +} + +// XServiceInfo + +OUString SAL_CALL + AccessibleDocumentViewBase::getImplementationName() +{ + return "AccessibleDocumentViewBase"; +} + +css::uno::Sequence< OUString> SAL_CALL + AccessibleDocumentViewBase::getSupportedServiceNames() +{ + ThrowIfDisposed (); + return AccessibleContextBase::getSupportedServiceNames (); +} + +//===== XTypeProvider ======================================================= + +css::uno::Sequence< css::uno::Type> SAL_CALL + AccessibleDocumentViewBase::getTypes() +{ + ThrowIfDisposed (); + + return comphelper::concatSequences( + // Get list of types from the context base implementation, ... + AccessibleContextBase::getTypes(), + // ... get list of types from component base implementation, ... + AccessibleComponentBase::getTypes(), + // ...and add the additional type for the component, ... + css::uno::Sequence { + cppu::UnoType<lang::XEventListener>::get(), + cppu::UnoType<beans::XPropertyChangeListener>::get(), + cppu::UnoType<awt::XWindowListener>::get(), + cppu::UnoType<awt::XFocusListener>::get(), + cppu::UnoType<XAccessibleEventBroadcaster>::get() }); +} + +void AccessibleDocumentViewBase::impl_dispose() +{ + // Unregister from VCL Window. + vcl::Window* pWindow = maShapeTreeInfo.GetWindow(); + if (maWindowLink.IsSet()) + { + if (pWindow) + pWindow->RemoveChildEventListener (maWindowLink); + maWindowLink = Link<VclWindowEvent&,void>(); + } + else + { + DBG_ASSERT (pWindow, "AccessibleDocumentViewBase::disposing"); + } + + // Unregister from window. + if (mxWindow.is()) + { + mxWindow->removeWindowListener (this); + mxWindow->removeFocusListener (this); + mxWindow = nullptr; + } + + // Unregister from the model. + if (mxModel.is()) + mxModel->removeEventListener ( + static_cast<awt::XWindowListener*>(this)); + + // Unregister from the controller. + if (mxController.is()) + { + uno::Reference<beans::XPropertySet> xSet (mxController, uno::UNO_QUERY); + if (xSet.is()) + xSet->removePropertyChangeListener ("", static_cast<beans::XPropertyChangeListener*>(this)); + + mxController->removeEventListener ( + static_cast<awt::XWindowListener*>(this)); + } + + // Propagate change of controller down the shape tree. + maShapeTreeInfo.SetModelBroadcaster (nullptr); + + // Reset the model reference. + mxModel = nullptr; + // Reset the model reference. + mxController = nullptr; + + maShapeTreeInfo.SetDocumentWindow (nullptr); +} + +//===== XEventListener ====================================================== + +void SAL_CALL + AccessibleDocumentViewBase::disposing (const lang::EventObject& rEventObject) +{ + ThrowIfDisposed (); + + // Register this object as dispose event and document::XEventListener + // listener at the model. + + if ( ! rEventObject.Source.is()) + { + // Paranoia. Can this really happen? + } + else if (rEventObject.Source == mxModel || rEventObject.Source == mxController) + { + impl_dispose(); + } +} + +//===== XPropertyChangeListener ============================================= + +void SAL_CALL AccessibleDocumentViewBase::propertyChange (const beans::PropertyChangeEvent& ) +{ + // Empty +} + +//===== XWindowListener ===================================================== + +void SAL_CALL + AccessibleDocumentViewBase::windowResized (const css::awt::WindowEvent& ) +{ + if( IsDisposed() ) + return; + + ViewForwarderChanged(); +} + +void SAL_CALL + AccessibleDocumentViewBase::windowMoved (const css::awt::WindowEvent& ) +{ + if( IsDisposed() ) + return; + + ViewForwarderChanged(); +} + +void SAL_CALL + AccessibleDocumentViewBase::windowShown (const css::lang::EventObject& ) +{ + if( IsDisposed() ) + return; + + ViewForwarderChanged(); +} + +void SAL_CALL + AccessibleDocumentViewBase::windowHidden (const css::lang::EventObject& ) +{ + if( IsDisposed() ) + return; + + ViewForwarderChanged(); +} + +//===== XFocusListener ================================================== + +void AccessibleDocumentViewBase::focusGained (const css::awt::FocusEvent& e) +{ + ThrowIfDisposed (); + if (e.Source == mxWindow) + Activated (); +} + +void AccessibleDocumentViewBase::focusLost (const css::awt::FocusEvent& e) +{ + ThrowIfDisposed (); + if (e.Source == mxWindow) + Deactivated (); +} + +//===== protected internal ================================================== + +// This method is called from the component helper base class while disposing. +void SAL_CALL AccessibleDocumentViewBase::disposing() +{ + impl_dispose(); + + AccessibleContextBase::disposing (); +} + +/// Create a name for this view. +OUString + AccessibleDocumentViewBase::CreateAccessibleName() +{ + return "AccessibleDocumentViewBase"; +} + +void AccessibleDocumentViewBase::Activated() +{ + // Empty. Overwrite to do something useful. +} + +void AccessibleDocumentViewBase::Deactivated() +{ + // Empty. Overwrite to do something useful. +} + +void AccessibleDocumentViewBase::SetAccessibleOLEObject ( + const Reference <XAccessible>& xOLEObject) +{ + // Send child event about removed accessible OLE object if necessary. + if (mxAccessibleOLEObject != xOLEObject) + if (mxAccessibleOLEObject.is()) + CommitChange ( + AccessibleEventId::CHILD, + uno::Any(), + uno::Any (mxAccessibleOLEObject)); + + // Assume that the accessible OLE Object disposes itself correctly. + + { + ::osl::MutexGuard aGuard (m_aMutex); + mxAccessibleOLEObject = xOLEObject; + } + + // Send child event about new accessible OLE object if necessary. + if (mxAccessibleOLEObject.is()) + CommitChange ( + AccessibleEventId::CHILD, + uno::Any (mxAccessibleOLEObject), + uno::Any()); +} + +//===== methods from AccessibleSelectionBase ================================================== + +// return the member maMutex; +::osl::Mutex& + AccessibleDocumentViewBase::implGetMutex() +{ + return m_aMutex; +} + +// return ourself as context in default case +uno::Reference< XAccessibleContext > + AccessibleDocumentViewBase::implGetAccessibleContext() +{ + return this; +} + +// return sal_False in default case +bool + AccessibleDocumentViewBase::implIsSelected( sal_Int32 ) +{ + return false; +} + +// return nothing in default case +void + AccessibleDocumentViewBase::implSelect( sal_Int32, bool ) +{ +} + +uno::Any SAL_CALL AccessibleDocumentViewBase::getExtendedAttributes() +{ + ::osl::MutexGuard aGuard (m_aMutex); + + uno::Any anyAttribute; + OUStringBuffer sValue; + if (auto pDrViewSh = dynamic_cast<::sd::DrawViewShell* > (mpViewShell)) + { + OUString sDisplay; + OUString sName = "page-name:"; + // MT IA2: Not used... + // SdPage* pCurrPge = pDrViewSh->getCurrentPage(); + SdDrawDocument* pDoc = pDrViewSh->GetDoc(); + sDisplay = pDrViewSh->getCurrentPage()->GetName(); + sDisplay = sDisplay.replaceFirst( "\\", "\\\\" ); + sDisplay = sDisplay.replaceFirst( "=", "\\=" ); + sDisplay = sDisplay.replaceFirst( ";", "\\;" ); + sDisplay = sDisplay.replaceFirst( ",", "\\," ); + sDisplay = sDisplay.replaceFirst( ":", "\\:" ); + sValue = sName + sDisplay ; + sValue.append(";page-number:"); + sValue.append(static_cast<sal_Int32>(static_cast<sal_uInt16>((pDrViewSh->getCurrentPage()->GetPageNum()-1)>>1) + 1)); + sValue.append(";total-pages:"); + sValue.append(static_cast<sal_Int32>(pDrViewSh->GetPageTabControl().GetPageCount())); + sValue.append(";"); + if(pDrViewSh->IsLayerModeActive() && pDrViewSh->GetLayerTabControl()) // #i87182# + { + sName = "page-name:"; + sValue = sName; + OUString sLayerName(pDrViewSh->GetLayerTabControl()->GetLayerName(pDrViewSh->GetLayerTabControl()->GetCurPageId()) ); + sDisplay = pDrViewSh->GetLayerTabControl()->GetPageText(pDrViewSh->GetLayerTabControl()->GetCurPageId()); + if( pDoc ) + { + SdrLayerAdmin& rLayerAdmin = pDoc->GetLayerAdmin(); + SdrLayer* aSdrLayer = rLayerAdmin.GetLayer(sLayerName); + if( aSdrLayer ) + { + const OUString& layerAltText = aSdrLayer->GetTitle(); + if (!layerAltText.isEmpty()) + { + sName = " "; + sDisplay += sName + layerAltText; + } + } + } + sDisplay = sDisplay.replaceFirst( "\\", "\\\\" ); + sDisplay = sDisplay.replaceFirst( "=", "\\=" ); + sDisplay = sDisplay.replaceFirst( ";", "\\;" ); + sDisplay = sDisplay.replaceFirst( ",", "\\," ); + sDisplay = sDisplay.replaceFirst( ":", "\\:" ); + sValue.append(sDisplay); + sValue.append(";page-number:"); + sValue.append(static_cast<sal_Int32>(pDrViewSh->GetActiveTabLayerIndex()+1)); + sValue.append(";total-pages:"); + sValue.append(static_cast<sal_Int32>(pDrViewSh->GetLayerTabControl()->GetPageCount())); + sValue.append(";"); + } + } + if (auto pPresViewSh = dynamic_cast<::sd::PresentationViewShell* >(mpViewShell)) + { + SdPage* pCurrPge = pPresViewSh->getCurrentPage(); + SdDrawDocument* pDoc = pPresViewSh->GetDoc(); + SdPage* pNotesPge = pDoc->GetSdPage((pCurrPge->GetPageNum()-1)>>1, PageKind::Notes); + if (pNotesPge) + { + SdrObject* pNotesObj = pNotesPge->GetPresObj(PresObjKind::Notes); + if (pNotesObj) + { + OutlinerParaObject* pPara = pNotesObj->GetOutlinerParaObject(); + if (pPara) + { + sValue.append("note:"); + const EditTextObject& rEdit = pPara->GetTextObject(); + for (sal_Int32 i=0;i<rEdit.GetParagraphCount();i++) + { + OUString strNote = rEdit.GetText(i); + strNote = strNote.replaceFirst( "\\", "\\\\" ); + strNote = strNote.replaceFirst( "=", "\\=" ); + strNote = strNote.replaceFirst( ";", "\\;" ); + strNote = strNote.replaceFirst( ",", "\\," ); + strNote = strNote.replaceFirst( ":", "\\:" ); + sValue.append(strNote); + sValue.append(";");//to divide each paragraph + } + } + } + } + } + if (dynamic_cast<const ::sd::OutlineViewShell* >(mpViewShell ) != nullptr ) + { + SdPage* pCurrPge = mpViewShell->GetActualPage(); + SdDrawDocument* pDoc = mpViewShell->GetDoc(); + if(pCurrPge && pDoc) + { + OUString sDisplay; + sDisplay = pCurrPge->GetName(); + sDisplay = sDisplay.replaceFirst( "=", "\\=" ); + sDisplay = sDisplay.replaceFirst( ";", "\\;" ); + sDisplay = sDisplay.replaceFirst( ",", "\\," ); + sDisplay = sDisplay.replaceFirst( ":", "\\:" ); + sValue = "page-name:" + sDisplay; + sValue.append(";page-number:"); + sValue.append(static_cast<sal_Int32>(static_cast<sal_uInt16>((pCurrPge->GetPageNum()-1)>>1) + 1)); + sValue.append(";total-pages:"); + sValue.append(static_cast<sal_Int32>(pDoc->GetSdPageCount(PageKind::Standard))); + sValue.append(";"); + } + } + if (sValue.getLength()) + anyAttribute <<= sValue.makeStringAndClear(); + return anyAttribute; +} + +sal_Int32 SAL_CALL AccessibleDocumentViewBase::getForeground( ) +{ + return sal_Int32(COL_BLACK); +} + +sal_Int32 SAL_CALL AccessibleDocumentViewBase::getBackground( ) +{ + ThrowIfDisposed (); + ::osl::MutexGuard aGuard (m_aMutex); + return sal_Int32(mpViewShell->GetView()->getColorConfig().GetColorValue( ::svtools::DOCCOLOR ).nColor); +} +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessibleDrawDocumentView.cxx b/sd/source/ui/accessibility/AccessibleDrawDocumentView.cxx new file mode 100644 index 000000000..f6111962a --- /dev/null +++ b/sd/source/ui/accessibility/AccessibleDrawDocumentView.cxx @@ -0,0 +1,777 @@ +/* -*- 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 <AccessibleDrawDocumentView.hxx> +#include <com/sun/star/drawing/ShapeCollection.hpp> +#include <com/sun/star/drawing/XDrawView.hpp> +#include <com/sun/star/drawing/XDrawPagesSupplier.hpp> +#include <com/sun/star/drawing/XShapes.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include <cppuhelper/queryinterface.hxx> +#include <comphelper/processfactory.hxx> +#include <sal/log.hxx> +#include <tools/debug.hxx> + +#include <svx/AccessibleShape.hxx> +#include <svx/ChildrenManager.hxx> +#include <svx/svdobj.hxx> +#include <vcl/svapp.hxx> + +#include <ViewShell.hxx> +#include <View.hxx> +#include <DrawDocShell.hxx> +#include <drawdoc.hxx> +#include <algorithm> +#include <slideshow.hxx> +#include <anminfo.hxx> +#include <AccessiblePageShape.hxx> + +#include <strings.hrc> +#include <sdresid.hxx> +#include <osl/mutex.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::accessibility; + +namespace accessibility { + +namespace { + +struct XShapePosCompareHelper +{ + bool operator() ( const uno::Reference<drawing::XShape>& xshape1, + const uno::Reference<drawing::XShape>& xshape2 ) const + { + // modify the compare method to return the Z-Order, not layout order + SdrObject* pObj1 = SdrObject::getSdrObjectFromXShape(xshape1); + SdrObject* pObj2 = SdrObject::getSdrObjectFromXShape(xshape2); + if(pObj1 && pObj2) + return pObj1->GetOrdNum() < pObj2->GetOrdNum(); + else + return false; + } +}; + +} + +//===== internal ============================================================ + +AccessibleDrawDocumentView::AccessibleDrawDocumentView ( + ::sd::Window* pSdWindow, + ::sd::ViewShell* pViewShell, + const uno::Reference<frame::XController>& rxController, + const uno::Reference<XAccessible>& rxParent) + : AccessibleDocumentViewBase (pSdWindow, pViewShell, rxController, rxParent), + mpSdViewSh( pViewShell ) +{ + UpdateAccessibleName(); +} + +AccessibleDrawDocumentView::~AccessibleDrawDocumentView() +{ + DBG_ASSERT (rBHelper.bDisposed || rBHelper.bInDispose, + "~AccessibleDrawDocumentView: object has not been disposed"); +} + +void AccessibleDrawDocumentView::Init() +{ + AccessibleDocumentViewBase::Init (); + + // Determine the list of shapes on the current page. + uno::Reference<drawing::XShapes> xShapeList; + uno::Reference<drawing::XDrawView> xView (mxController, uno::UNO_QUERY); + if (xView.is()) + xShapeList = xView->getCurrentPage(); + + // Create the children manager. + mpChildrenManager.reset(new ChildrenManager(this, xShapeList, maShapeTreeInfo, *this)); + + rtl::Reference<AccessiblePageShape> xPage(CreateDrawPageShape()); + if (xPage.is()) + { + xPage->Init(); + mpChildrenManager->AddAccessibleShape (xPage); + mpChildrenManager->Update (); + } + + mpChildrenManager->UpdateSelection (); +} + +void AccessibleDrawDocumentView::ViewForwarderChanged() +{ + AccessibleDocumentViewBase::ViewForwarderChanged(); + if (mpChildrenManager != nullptr) + mpChildrenManager->ViewForwarderChanged(); +} + +/** The page shape is created on every call at the moment (provided that + everything goes well). +*/ +rtl::Reference<AccessiblePageShape> AccessibleDrawDocumentView::CreateDrawPageShape() +{ + rtl::Reference<AccessiblePageShape> xShape; + + // Create a shape that represents the actual draw page. + uno::Reference<drawing::XDrawView> xView (mxController, uno::UNO_QUERY); + if (xView.is()) + { + uno::Reference<beans::XPropertySet> xSet ( + uno::Reference<beans::XPropertySet> (xView->getCurrentPage(), uno::UNO_QUERY)); + if (xSet.is()) + { + // Create a rectangle shape that will represent the draw page. + uno::Reference<lang::XMultiServiceFactory> xFactory (mxModel, uno::UNO_QUERY); + uno::Reference<drawing::XShape> xRectangle; + if (xFactory.is()) + xRectangle.set(xFactory->createInstance ("com.sun.star.drawing.RectangleShape"), + uno::UNO_QUERY); + + // Set the shape's size and position. + if (xRectangle.is()) + { + uno::Any aValue; + awt::Point aPosition; + awt::Size aSize; + + // Set size and position of the shape to those of the draw + // page. + aValue = xSet->getPropertyValue ("BorderLeft"); + aValue >>= aPosition.X; + aValue = xSet->getPropertyValue ("BorderTop"); + aValue >>= aPosition.Y; + xRectangle->setPosition (aPosition); + + aValue = xSet->getPropertyValue ("Width"); + aValue >>= aSize.Width; + aValue = xSet->getPropertyValue ("Height"); + aValue >>= aSize.Height; + xRectangle->setSize (aSize); + + // Create the accessible object for the shape and + // initialize it. + xShape = new AccessiblePageShape ( + xView->getCurrentPage(), this, maShapeTreeInfo); + } + } + } + return xShape; +} + +//===== XAccessibleContext ================================================== + +sal_Int32 SAL_CALL + AccessibleDrawDocumentView::getAccessibleChildCount() +{ + ThrowIfDisposed (); + + tools::Long nChildCount = AccessibleDocumentViewBase::getAccessibleChildCount(); + + // Forward request to children manager. + if (mpChildrenManager != nullptr) + nChildCount += mpChildrenManager->GetChildCount(); + + return nChildCount; +} + +uno::Reference<XAccessible> SAL_CALL + AccessibleDrawDocumentView::getAccessibleChild (sal_Int32 nIndex) +{ + ThrowIfDisposed (); + + ::osl::ClearableMutexGuard aGuard (m_aMutex); + + // Take care of children of the base class. + sal_Int32 nCount = AccessibleDocumentViewBase::getAccessibleChildCount(); + if (nCount > 0) + { + if (nIndex < nCount) + return AccessibleDocumentViewBase::getAccessibleChild(nIndex); + else + nIndex -= nCount; + } + + // Create a copy of the pointer to the children manager and release the + // mutex before calling any of its methods. + ChildrenManager* pChildrenManager = mpChildrenManager.get(); + aGuard.clear(); + + // Forward request to children manager. + if (pChildrenManager == nullptr) + throw lang::IndexOutOfBoundsException ( + "no accessible child with index " + OUString::number(nIndex), + static_cast<uno::XWeak*>(this)); + + return pChildrenManager->GetChild (nIndex); +} + +OUString SAL_CALL + AccessibleDrawDocumentView::getAccessibleName() +{ + SolarMutexGuard g; + + OUString sName = SdResId(SID_SD_A11Y_D_PRESENTATION); + ::sd::View* pSdView = static_cast< ::sd::View* >( maShapeTreeInfo.GetSdrView() ); + if ( pSdView ) + { + SdDrawDocument& rDoc = pSdView->GetDoc(); + OUString sFileName = rDoc.getDocAccTitle(); + if ( !sFileName.getLength() ) + { + ::sd::DrawDocShell* pDocSh = pSdView->GetDocSh(); + if ( pDocSh ) + { + sFileName = pDocSh->GetTitle( SFX_TITLE_APINAME ); + } + } + + OUString sReadOnly; + if(rDoc.getDocReadOnly()) + { + sReadOnly = SdResId(SID_SD_A11Y_D_PRESENTATION_READONLY); + } + + if ( sFileName.getLength() ) + { + sName = sFileName + sReadOnly + " - " + sName; + } + } + + return sName; +} + +//===== XEventListener ====================================================== + +void SAL_CALL + AccessibleDrawDocumentView::disposing (const lang::EventObject& rEventObject) +{ + ThrowIfDisposed (); + + AccessibleDocumentViewBase::disposing (rEventObject); + if (rEventObject.Source == mxModel) + { + ::osl::Guard< ::osl::Mutex> aGuard (::osl::Mutex::getGlobalMutex()); + // maShapeTreeInfo has been modified in base class. + if (mpChildrenManager != nullptr) + mpChildrenManager->SetInfo (maShapeTreeInfo); + } +} + +//===== XPropertyChangeListener ============================================= + +void SAL_CALL + AccessibleDrawDocumentView::propertyChange (const beans::PropertyChangeEvent& rEventObject) +{ + ThrowIfDisposed (); + + AccessibleDocumentViewBase::propertyChange (rEventObject); + + // add page switch event for slide show mode + if (rEventObject.PropertyName == "CurrentPage" || + rEventObject.PropertyName == "PageChange") + { + // Update the accessible name to reflect the current slide. + UpdateAccessibleName(); + + // The current page changed. Update the children manager accordingly. + uno::Reference<drawing::XDrawView> xView (mxController, uno::UNO_QUERY); + if (xView.is() && mpChildrenManager!=nullptr) + { + // Inform the children manager to forget all children and give + // him the new ones. + mpChildrenManager->ClearAccessibleShapeList (); + mpChildrenManager->SetShapeList (xView->getCurrentPage()); + + rtl::Reference<AccessiblePageShape> xPage(CreateDrawPageShape ()); + if (xPage.is()) + { + xPage->Init(); + mpChildrenManager->AddAccessibleShape (xPage); + mpChildrenManager->Update (false); + } + } + else + SAL_WARN("sd", "View invalid"); + CommitChange(AccessibleEventId::PAGE_CHANGED,rEventObject.NewValue,rEventObject.OldValue); + } + else if ( rEventObject.PropertyName == "VisibleArea" ) + { + if (mpChildrenManager != nullptr) + mpChildrenManager->ViewForwarderChanged(); + } + else if (rEventObject.PropertyName == "ActiveLayer") + { + CommitChange(AccessibleEventId::PAGE_CHANGED,rEventObject.NewValue,rEventObject.OldValue); + } + else if (rEventObject.PropertyName == "UpdateAcc") + { + // The current page changed. Update the children manager accordingly. + uno::Reference<drawing::XDrawView> xView (mxController, uno::UNO_QUERY); + if (xView.is() && mpChildrenManager!=nullptr) + { + // Inform the children manager to forget all children and give + // him the new ones. + mpChildrenManager->ClearAccessibleShapeList (); + // update the slide show page's accessible info + //mpChildrenManager->SetShapeList (uno::Reference<drawing::XShapes> ( + // xView->getCurrentPage(), uno::UNO_QUERY)); + rtl::Reference< sd::SlideShow > xSlideshow( sd::SlideShow::GetSlideShow( mpSdViewSh->GetViewShellBase() ) ); + if( xSlideshow.is() && xSlideshow->isRunning() && xSlideshow->isFullScreen() ) + { + css::uno::Reference< drawing::XDrawPage > xSlide; + // MT IA2: Not used... + // sal_Int32 currentPageIndex = xSlideshow->getCurrentPageIndex(); + css::uno::Reference< css::presentation::XSlideShowController > xSlideController = xSlideshow->getController(); + if( xSlideController.is() ) + { + xSlide = xSlideController->getCurrentSlide(); + if (xSlide.is()) + { + mpChildrenManager->SetShapeList (xSlide); + } + } + } + rtl::Reference<AccessiblePageShape> xPage(CreateDrawPageShape ()); + if (xPage.is()) + { + xPage->Init(); + mpChildrenManager->AddAccessibleShape (xPage); + mpChildrenManager->Update (false); + } + } + } + else + { + SAL_INFO("sd", "unhandled"); + } +} + +// XServiceInfo + +OUString SAL_CALL + AccessibleDrawDocumentView::getImplementationName() +{ + return "AccessibleDrawDocumentView"; +} + +css::uno::Sequence< OUString> SAL_CALL + AccessibleDrawDocumentView::getSupportedServiceNames() +{ + ThrowIfDisposed(); + const css::uno::Sequence<OUString> vals { "com.sun.star.drawing.AccessibleDrawDocumentView" }; + uno::Sequence<OUString> aServiceNames = + AccessibleDocumentViewBase::getSupportedServiceNames(); + + return comphelper::concatSequences(aServiceNames, vals); +} + +//===== XInterface ========================================================== + +uno::Any SAL_CALL + AccessibleDrawDocumentView::queryInterface (const uno::Type & rType) +{ + uno::Any aReturn = AccessibleDocumentViewBase::queryInterface (rType); + if ( ! aReturn.hasValue()) + aReturn = ::cppu::queryInterface (rType, + static_cast<XAccessibleGroupPosition*>(this) + ); + return aReturn; +} + +void SAL_CALL + AccessibleDrawDocumentView::acquire() + noexcept +{ + AccessibleDocumentViewBase::acquire (); +} +void SAL_CALL + AccessibleDrawDocumentView::release() + noexcept +{ + AccessibleDocumentViewBase::release (); +} +//===== XAccessibleGroupPosition ========================================= +uno::Sequence< sal_Int32 > SAL_CALL + AccessibleDrawDocumentView::getGroupPosition( const uno::Any& rAny ) +{ + SolarMutexGuard g; + + // we will return the: + // [0] group level(always be 0 now) + // [1] similar items counts in the group + // [2] the position of the object in the group + uno::Sequence< sal_Int32 > aRet( 3 ); + //get the xShape of the current selected drawing object + uno::Reference<XAccessibleContext> xAccContent; + rAny >>= xAccContent; + if ( !xAccContent.is() ) + { + return aRet; + } + AccessibleShape* pAcc = comphelper::getFromUnoTunnel<AccessibleShape>( xAccContent ); + if ( !pAcc ) + { + return aRet; + } + uno::Reference< drawing::XShape > xCurShape = pAcc->GetXShape(); + if ( !xCurShape.is() ) + { + return aRet; + } + //find all the child in the page, insert them into a vector and sort + if ( mpChildrenManager == nullptr ) + { + return aRet; + } + std::vector< uno::Reference<drawing::XShape> > vXShapes; + sal_Int32 nCount = mpChildrenManager->GetChildCount(); + //get pointer of SdView & SdrPageView for further use. + SdrPageView* pPV = nullptr; + ::sd::View* pSdView = nullptr; + if ( mpSdViewSh ) + { + pSdView = mpSdViewSh->GetView(); + pPV = pSdView->GetSdrPageView(); + } + for ( sal_Int32 i = 0; i < nCount; i++ ) + { + uno::Reference< drawing::XShape > xShape = mpChildrenManager->GetChildShape(i); + if ( xShape.is() ) + { + //if the object is visible in the page, we add it into the group list. + SdrObject* pObj = SdrObject::getSdrObjectFromXShape(xShape); + if ( pObj && pPV && pSdView && pSdView->IsObjMarkable( pObj, pPV ) ) + { + vXShapes.push_back( xShape ); + } + } + } + std::sort( vXShapes.begin(), vXShapes.end(), XShapePosCompareHelper() ); + //get the index of the selected object in the group + auto aIter = std::find_if(vXShapes.begin(), vXShapes.end(), + [&xCurShape](const uno::Reference<drawing::XShape>& rxShape) { return rxShape.get() == xCurShape.get(); }); + if (aIter != vXShapes.end()) + { + sal_Int32* pArray = aRet.getArray(); + pArray[0] = 1; //it should be 1 based, not 0 based. + pArray[1] = vXShapes.size(); + pArray[2] = static_cast<sal_Int32>(std::distance(vXShapes.begin(), aIter)) + 1; //we start counting position from 1 + } + return aRet; +} + +OUString AccessibleDrawDocumentView::getObjectLink( const uno::Any& rAny ) +{ + SolarMutexGuard g; + + OUString aRet; + //get the xShape of the current selected drawing object + uno::Reference<XAccessibleContext> xAccContent; + rAny >>= xAccContent; + if ( !xAccContent.is() ) + { + return aRet; + } + AccessibleShape* pAcc = comphelper::getFromUnoTunnel<AccessibleShape>( xAccContent ); + if ( !pAcc ) + { + return aRet; + } + uno::Reference< drawing::XShape > xCurShape = pAcc->GetXShape(); + if ( !xCurShape.is() ) + { + return aRet; + } + SdrObject* pObj = SdrObject::getSdrObjectFromXShape(xCurShape); + if (pObj) + { + SdAnimationInfo* pInfo = SdDrawDocument::GetShapeUserData(*pObj); + if( pInfo && (pInfo->meClickAction == presentation::ClickAction_DOCUMENT) ) + aRet = pInfo->GetBookmark(); + } + return aRet; +} + +/// Create a name for this view. +OUString AccessibleDrawDocumentView::CreateAccessibleName() +{ + OUString sName; + + uno::Reference<lang::XServiceInfo> xInfo (mxController, uno::UNO_QUERY); + if (xInfo.is()) + { + uno::Sequence< OUString > aServices( xInfo->getSupportedServiceNames() ); + OUString sFirstService = aServices[0]; + if ( sFirstService == "com.sun.star.drawing.DrawingDocumentDrawView" ) + { + if( aServices.getLength() >= 2 && aServices[1] == "com.sun.star.presentation.PresentationView") + { + SolarMutexGuard aGuard; + + sName = SdResId(SID_SD_A11Y_I_DRAWVIEW_N); + } + else + { + SolarMutexGuard aGuard; + + sName = SdResId(SID_SD_A11Y_D_DRAWVIEW_N); + } + } + else if ( sFirstService == "com.sun.star.presentation.NotesView" ) + { + SolarMutexGuard aGuard; + + sName = SdResId(SID_SD_A11Y_I_NOTESVIEW_N); + } + else if ( sFirstService == "com.sun.star.presentation.HandoutView" ) + { + SolarMutexGuard aGuard; + + sName = SdResId(SID_SD_A11Y_I_HANDOUTVIEW_N); + } + else + { + sName = sFirstService; + } + } + else + { + sName = "AccessibleDrawDocumentView"; + } + return sName; +} + +/** Return selection state of specified child +*/ +bool + AccessibleDrawDocumentView::implIsSelected( sal_Int32 nAccessibleChildIndex ) +{ + const SolarMutexGuard aSolarGuard; + uno::Reference< view::XSelectionSupplier > xSel( mxController, uno::UNO_QUERY ); + bool bRet = false; + + OSL_ENSURE( 0 <= nAccessibleChildIndex, "AccessibleDrawDocumentView::implIsSelected: invalid index!" ); + + if( xSel.is() && ( 0 <= nAccessibleChildIndex ) ) + { + uno::Any aAny( xSel->getSelection() ); + uno::Reference< drawing::XShapes > xShapes; + + aAny >>= xShapes; + + if( xShapes.is() ) + { + AccessibleShape* pAcc = comphelper::getFromUnoTunnel<AccessibleShape>( getAccessibleChild( nAccessibleChildIndex ) ); + + if( pAcc ) + { + uno::Reference< drawing::XShape > xShape( pAcc->GetXShape() ); + + if( xShape.is() ) + { + for( sal_Int32 i = 0, nCount = xShapes->getCount(); ( i < nCount ) && !bRet; ++i ) + if( xShapes->getByIndex( i ) == xShape ) + bRet = true; + } + } + } + } + + return bRet; +} + +/** Select or deselect the specified shapes. The corresponding accessible + shapes are notified over the selection change listeners registered with + the XSelectionSupplier of the controller. +*/ +void + AccessibleDrawDocumentView::implSelect( sal_Int32 nAccessibleChildIndex, bool bSelect ) +{ + const SolarMutexGuard aSolarGuard; + uno::Reference< view::XSelectionSupplier > xSel( mxController, uno::UNO_QUERY ); + + if( !xSel.is() ) + return; + + uno::Any aAny; + + if( ACCESSIBLE_SELECTION_CHILD_ALL == nAccessibleChildIndex ) + { + // Select or deselect all children. + + if( !bSelect ) + xSel->select( aAny ); + else + { + uno::Reference< drawing::XShapes > xShapes = drawing::ShapeCollection::create( + comphelper::getProcessComponentContext()); + + for(sal_Int32 i = 0, nCount = getAccessibleChildCount(); i < nCount; ++i ) + { + AccessibleShape* pAcc = comphelper::getFromUnoTunnel<AccessibleShape>( getAccessibleChild( i ) ); + + if( pAcc && pAcc->GetXShape().is() ) + xShapes->add( pAcc->GetXShape() ); + } + + if( xShapes->getCount() ) + { + xSel->select( Any(xShapes) ); + } + } + } + else if( nAccessibleChildIndex >= 0 ) + { + // Select or deselect only the child with index + // nAccessibleChildIndex. + + AccessibleShape* pAcc = comphelper::getFromUnoTunnel<AccessibleShape>( + getAccessibleChild( nAccessibleChildIndex )); + + // Add or remove the shape that is made accessible from the + // selection of the controller. + if( pAcc ) + { + uno::Reference< drawing::XShape > xShape( pAcc->GetXShape() ); + + if( xShape.is() ) + { + uno::Reference< drawing::XShapes > xShapes; + bool bFound = false; + + aAny = xSel->getSelection(); + aAny >>= xShapes; + + // Search shape to be selected in current selection. + if (xShapes.is()) + { + sal_Int32 nCount = xShapes->getCount(); + for (sal_Int32 i=0; ( i < nCount ) && !bFound; ++i ) + if( xShapes->getByIndex( i ) == xShape ) + bFound = true; + } + else + // Create an empty selection to add the shape to. + xShapes = drawing::ShapeCollection::create( + comphelper::getProcessComponentContext()); + + // Update the selection. + if( !bFound && bSelect ) + xShapes->add( xShape ); + else if( bFound && !bSelect ) + xShapes->remove( xShape ); + + xSel->select( Any(xShapes) ); + } + } + } +} + +void AccessibleDrawDocumentView::Activated() +{ + if (mpChildrenManager == nullptr) + return; + + bool bChange = false; + // When none of the children has the focus then claim it for the + // view. + if ( ! mpChildrenManager->HasFocus()) + { + SetState (AccessibleStateType::FOCUSED); + bChange = true; + } + else + ResetState (AccessibleStateType::FOCUSED); + mpChildrenManager->UpdateSelection(); + // if the child gets focus in UpdateSelection(), needs to reset the focus on document. + if (mpChildrenManager->HasFocus() && bChange) + ResetState (AccessibleStateType::FOCUSED); +} + +void AccessibleDrawDocumentView::Deactivated() +{ + if (mpChildrenManager != nullptr) + mpChildrenManager->RemoveFocus(); + ResetState (AccessibleStateType::FOCUSED); +} + +void AccessibleDrawDocumentView::impl_dispose() +{ + mpChildrenManager.reset(); + AccessibleDocumentViewBase::impl_dispose(); +} + +/** This method is called from the component helper base class while + disposing. +*/ +void SAL_CALL AccessibleDrawDocumentView::disposing() +{ + // Release resources. + mpChildrenManager.reset(); + + // Forward call to base classes. + AccessibleDocumentViewBase::disposing (); +} + +void AccessibleDrawDocumentView::UpdateAccessibleName() +{ + OUString sNewName (CreateAccessibleName() + ": "); + + // Add the number of the current slide. + uno::Reference<drawing::XDrawView> xView (mxController, uno::UNO_QUERY); + if (xView.is()) + { + uno::Reference<beans::XPropertySet> xProperties (xView->getCurrentPage(), UNO_QUERY); + if (xProperties.is()) + try + { + sal_Int16 nPageNumber (0); + if (xProperties->getPropertyValue("Number") >>= nPageNumber) + { + sNewName += OUString::number(nPageNumber); + } + } + catch (const beans::UnknownPropertyException&) + { + } + } + + // Add the number of pages/slides. + Reference<drawing::XDrawPagesSupplier> xPagesSupplier (mxModel, UNO_QUERY); + if (xPagesSupplier.is()) + { + Reference<container::XIndexAccess> xPages = xPagesSupplier->getDrawPages(); + if (xPages.is()) + { + sNewName += " / " + OUString::number(xPages->getCount()); + } + } + + SetAccessibleName (sNewName, AutomaticallyCreated); +} + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessibleOutlineEditSource.cxx b/sd/source/ui/accessibility/AccessibleOutlineEditSource.cxx new file mode 100644 index 000000000..a1a79a678 --- /dev/null +++ b/sd/source/ui/accessibility/AccessibleOutlineEditSource.cxx @@ -0,0 +1,199 @@ +/* -*- 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 <memory> +#include <editeng/unoedhlp.hxx> +#include <svx/svdoutl.hxx> +#include <svx/svdview.hxx> +#include <vcl/window.hxx> +#include <AccessibleOutlineEditSource.hxx> + +namespace accessibility +{ + + AccessibleOutlineEditSource::AccessibleOutlineEditSource( + SdrOutliner& rOutliner, + SdrView& rView, + OutlinerView& rOutlView, + const vcl::Window& rViewWindow ) + : mrView( rView ), + mrWindow( rViewWindow ), + mpOutliner( &rOutliner ), + mpOutlinerView( &rOutlView ), + mTextForwarder( rOutliner, false ), + mViewForwarder( rOutlView ) + { + // register as listener - need to broadcast state change messages + // Moved to ::GetTextForwarder() + //rOutliner.SetNotifyHdl( LINK(this, AccessibleOutlineEditSource, NotifyHdl) ); + StartListening(rOutliner); + } + + AccessibleOutlineEditSource::~AccessibleOutlineEditSource() + { + if( mpOutliner ) + mpOutliner->SetNotifyHdl( Link<EENotify&,void>() ); + Broadcast( TextHint( SfxHintId::Dying ) ); + } + + std::unique_ptr<SvxEditSource> AccessibleOutlineEditSource::Clone() const + { + return std::unique_ptr<SvxEditSource>(new AccessibleOutlineEditSource(*mpOutliner, mrView, *mpOutlinerView, mrWindow)); + } + + SvxTextForwarder* AccessibleOutlineEditSource::GetTextForwarder() + { + // TODO: maybe suboptimal + if( IsValid() ) + { + // Moved here to make sure that + // the NotifyHandler was set on the current object. + mpOutliner->SetNotifyHdl( LINK(this, AccessibleOutlineEditSource, NotifyHdl) ); + return &mTextForwarder; + } + else + return nullptr; + } + + SvxViewForwarder* AccessibleOutlineEditSource::GetViewForwarder() + { + // TODO: maybe suboptimal + if( IsValid() ) + return this; + else + return nullptr; + } + + SvxEditViewForwarder* AccessibleOutlineEditSource::GetEditViewForwarder( bool ) + { + // TODO: maybe suboptimal + if( IsValid() ) + { + // ignore parameter, we're always in edit mode here + return &mViewForwarder; + } + else + return nullptr; + } + + void AccessibleOutlineEditSource::UpdateData() + { + // NOOP, since we're always working on the 'real' outliner, + // i.e. changes are immediately reflected on the screen + } + + SfxBroadcaster& AccessibleOutlineEditSource::GetBroadcaster() const + { + return * const_cast< AccessibleOutlineEditSource* > (this); + } + + bool AccessibleOutlineEditSource::IsValid() const + { + if( mpOutliner && mpOutlinerView ) + { + // Our view still on outliner? + sal_uLong nCurrView, nViews; + + for( nCurrView=0, nViews=mpOutliner->GetViewCount(); nCurrView<nViews; ++nCurrView ) + { + if( mpOutliner->GetView(nCurrView) == mpOutlinerView ) + return true; + } + } + + return false; + } + + Point AccessibleOutlineEditSource::LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const + { + if( IsValid() && mrView.GetModel() ) + { + Point aPoint( OutputDevice::LogicToLogic( rPoint, rMapMode, + MapMode(mrView.GetModel()->GetScaleUnit()) ) ); + MapMode aMapMode(mrWindow.GetMapMode()); + aMapMode.SetOrigin(Point()); + return mrWindow.LogicToPixel( aPoint, aMapMode ); + } + + return Point(); + } + + Point AccessibleOutlineEditSource::PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const + { + if( IsValid() && mrView.GetModel() ) + { + MapMode aMapMode(mrWindow.GetMapMode()); + aMapMode.SetOrigin(Point()); + Point aPoint( mrWindow.PixelToLogic( rPoint, aMapMode ) ); + return OutputDevice::LogicToLogic( aPoint, + MapMode(mrView.GetModel()->GetScaleUnit()), + rMapMode ); + } + + return Point(); + } + + void AccessibleOutlineEditSource::Notify( SfxBroadcaster& rBroadcaster, const SfxHint& rHint ) + { + bool bDispose = false; + + if( &rBroadcaster == mpOutliner ) + { + if( rHint.GetId() == SfxHintId::Dying ) + { + bDispose = true; + mpOutliner = nullptr; + } + } + else + { + if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint) + { + const SdrHint* pSdrHint = static_cast< const SdrHint* >( &rHint ); + if( pSdrHint->GetKind() == SdrHintKind::ModelCleared ) + { + // model is dying under us, going defunc + bDispose = true; + } + } + } + + if( bDispose ) + { + if( mpOutliner ) + mpOutliner->SetNotifyHdl( Link<EENotify&,void>() ); + mpOutliner = nullptr; + mpOutlinerView = nullptr; + Broadcast( TextHint( SfxHintId::Dying ) ); + } + } + + IMPL_LINK(AccessibleOutlineEditSource, NotifyHdl, EENotify&, rNotify, void) + { + ::std::unique_ptr< SfxHint > aHint( SvxEditSourceHelper::EENotification2Hint( &rNotify) ); + + if (aHint) + { + Broadcast(*aHint); + } + } + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessibleOutlineView.cxx b/sd/source/ui/accessibility/AccessibleOutlineView.cxx new file mode 100644 index 000000000..4e020efef --- /dev/null +++ b/sd/source/ui/accessibility/AccessibleOutlineView.cxx @@ -0,0 +1,238 @@ +/* -*- 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 <com/sun/star/accessibility/AccessibleEventId.hpp> + +#include <sal/log.hxx> +#include <vcl/svapp.hxx> +#include <Window.hxx> +#include <OutlineViewShell.hxx> +#include <DrawDocShell.hxx> +#include <OutlineView.hxx> +#include <View.hxx> +#include <AccessibleOutlineView.hxx> +#include <AccessibleOutlineEditSource.hxx> +#include <drawdoc.hxx> +#include <strings.hrc> +#include <sdresid.hxx> + +#include <memory> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +namespace accessibility { + +//===== internal ============================================================ + +AccessibleOutlineView::AccessibleOutlineView ( + ::sd::Window* pSdWindow, + ::sd::OutlineViewShell* pViewShell, + const uno::Reference<frame::XController>& rxController, + const uno::Reference<XAccessible>& rxParent) + : AccessibleDocumentViewBase (pSdWindow, pViewShell, rxController, rxParent), + maTextHelper( ::std::unique_ptr< SvxEditSource >() ) +{ + SolarMutexGuard aGuard; + + // Beware! Here we leave the paths of the UNO API and descend into the + // depths of the core. Necessary for making the edit engine accessible. + if (!pSdWindow) + return; + + ::sd::View* pView = pViewShell->GetView(); + + auto pShellView = dynamic_cast<::sd::OutlineView* >( pView ); + if(!pShellView) + return; + + OutlinerView* pOutlineView = pShellView->GetViewByWindow( pSdWindow ); + SdrOutliner& rOutliner = pShellView->GetOutliner(); + + if( pOutlineView ) + { + maTextHelper.SetEditSource( ::std::unique_ptr< SvxEditSource >( new AccessibleOutlineEditSource( + rOutliner, *pView, *pOutlineView, *pSdWindow ) ) ); + } +} + +AccessibleOutlineView::~AccessibleOutlineView() +{ +} + +void AccessibleOutlineView::Init() +{ + // Set event source _before_ starting to listen + maTextHelper.SetEventSource(this); + + AccessibleDocumentViewBase::Init (); +} + +void AccessibleOutlineView::ViewForwarderChanged() +{ + AccessibleDocumentViewBase::ViewForwarderChanged(); + + UpdateChildren(); +} + +//===== XAccessibleContext ================================================== + +sal_Int32 SAL_CALL + AccessibleOutlineView::getAccessibleChildCount() +{ + ThrowIfDisposed (); + + // forward + return maTextHelper.GetChildCount(); +} + +uno::Reference<XAccessible> SAL_CALL + AccessibleOutlineView::getAccessibleChild (sal_Int32 nIndex) +{ + ThrowIfDisposed (); + // Forward request to children manager. + return maTextHelper.GetChild(nIndex); +} + +OUString SAL_CALL + AccessibleOutlineView::getAccessibleName() +{ + SolarMutexGuard g; + + OUString sName = SdResId(SID_SD_A11Y_D_PRESENTATION); + ::sd::View* pSdView = static_cast< ::sd::View* >( maShapeTreeInfo.GetSdrView() ); + if ( pSdView ) + { + SdDrawDocument& rDoc = pSdView->GetDoc(); + OUString sFileName = rDoc.getDocAccTitle(); + if (sFileName.isEmpty()) + { + ::sd::DrawDocShell* pDocSh = pSdView->GetDocSh(); + if ( pDocSh ) + { + sFileName = pDocSh->GetTitle( SFX_TITLE_APINAME ); + } + } + if (!sFileName.isEmpty()) + { + sName = sFileName + " - " + sName; + } + } + return sName; +} + +//===== XAccessibleEventBroadcaster ======================================== + +void SAL_CALL AccessibleOutlineView::addAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) +{ + // delegate listener handling to children manager. + if ( ! IsDisposed()) + maTextHelper.AddEventListener(xListener); + AccessibleContextBase::addEventListener(xListener); +} + +void SAL_CALL AccessibleOutlineView::removeAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) +{ + // forward + if ( ! IsDisposed()) + maTextHelper.RemoveEventListener(xListener); + AccessibleContextBase::removeEventListener(xListener); +} + +// XServiceInfo + +OUString SAL_CALL + AccessibleOutlineView::getImplementationName() +{ + return "AccessibleOutlineView"; +} + +//===== XEventListener ====================================================== + +//===== protected internal ================================================== + +void AccessibleOutlineView::Activated() +{ + SolarMutexGuard aGuard; + + // delegate listener handling to children manager. + maTextHelper.SetFocus(); +} + +void AccessibleOutlineView::Deactivated() +{ + SolarMutexGuard aGuard; + + // delegate listener handling to children manager. + maTextHelper.SetFocus(false); +} + +void SAL_CALL AccessibleOutlineView::disposing() +{ + // dispose children + maTextHelper.Dispose(); + + AccessibleDocumentViewBase::disposing (); +} + +//===== XPropertyChangeListener ============================================= + +void SAL_CALL + AccessibleOutlineView::propertyChange (const beans::PropertyChangeEvent& rEventObject) +{ + ThrowIfDisposed (); + + AccessibleDocumentViewBase::propertyChange (rEventObject); + + //add page switch event for slide show mode + if (rEventObject.PropertyName == "CurrentPage" || + rEventObject.PropertyName == "PageChange") + { + // The current page changed. Update the children accordingly. + UpdateChildren(); + CommitChange(AccessibleEventId::PAGE_CHANGED,rEventObject.NewValue, rEventObject.OldValue); + } + else if ( rEventObject.PropertyName == "VisibleArea" ) + { + // The visible area changed. Update the children accordingly. + UpdateChildren(); + } + else + { + SAL_INFO("sd", "unhandled"); + } +} + +/// Create a name for this view. +OUString AccessibleOutlineView::CreateAccessibleName() +{ + return SdResId(SID_SD_A11Y_I_OUTLINEVIEW_N); +} + +void AccessibleOutlineView::UpdateChildren() +{ + SolarMutexGuard aGuard; + + // Update visible children + maTextHelper.UpdateChildren(); +} + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessiblePageShape.cxx b/sd/source/ui/accessibility/AccessiblePageShape.cxx new file mode 100644 index 000000000..2900019ae --- /dev/null +++ b/sd/source/ui/accessibility/AccessiblePageShape.cxx @@ -0,0 +1,261 @@ +/* -*- 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 <AccessiblePageShape.hxx> +#include <svx/AccessibleShapeInfo.hxx> +#include <svx/IAccessibleViewForwarder.hxx> +#include <tools/diagnose_ex.h> +#include <tools/gen.hxx> +#include <sal/log.hxx> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/drawing/XMasterPageTarget.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::accessibility; +using ::com::sun::star::uno::Reference; + +namespace accessibility { + +//===== internal ============================================================ + +AccessiblePageShape::AccessiblePageShape ( + const uno::Reference<drawing::XDrawPage>& rxPage, + const uno::Reference<XAccessible>& rxParent, + const AccessibleShapeTreeInfo& rShapeTreeInfo) + : AccessibleShape (AccessibleShapeInfo (nullptr, rxParent), rShapeTreeInfo), + mxPage (rxPage) +{ + // The main part of the initialization is done in the init method which + // has to be called from this constructor's caller. +} + +AccessiblePageShape::~AccessiblePageShape() +{ +} + +//===== XAccessibleContext ================================================== + +sal_Int32 SAL_CALL + AccessiblePageShape::getAccessibleChildCount() +{ + return 0; +} + +/** Forward the request to the shape. Return the requested shape or throw + an exception for a wrong index. +*/ +uno::Reference<XAccessible> SAL_CALL + AccessiblePageShape::getAccessibleChild( sal_Int32 ) +{ + throw lang::IndexOutOfBoundsException ("page shape has no children", + static_cast<uno::XWeak*>(this)); +} + +//===== XAccessibleComponent ================================================ + +awt::Rectangle SAL_CALL AccessiblePageShape::getBounds() +{ + ThrowIfDisposed (); + + awt::Rectangle aBoundingBox; + + if (maShapeTreeInfo.GetViewForwarder() != nullptr) + { + uno::Reference<beans::XPropertySet> xSet (mxPage, uno::UNO_QUERY); + if (xSet.is()) + { + uno::Any aValue; + + aValue = xSet->getPropertyValue ("BorderLeft"); + aValue >>= aBoundingBox.X; + aValue = xSet->getPropertyValue ("BorderTop"); + aValue >>= aBoundingBox.Y; + + aValue = xSet->getPropertyValue ("Width"); + aValue >>= aBoundingBox.Width; + aValue = xSet->getPropertyValue ("Height"); + aValue >>= aBoundingBox.Height; + } + + // Transform coordinates from internal to pixel. + ::Size aPixelSize = maShapeTreeInfo.GetViewForwarder()->LogicToPixel ( + ::Size (aBoundingBox.Width, aBoundingBox.Height)); + ::Point aPixelPosition = maShapeTreeInfo.GetViewForwarder()->LogicToPixel ( + ::Point (aBoundingBox.X, aBoundingBox.Y)); + + // Clip the shape's bounding box with the bounding box of its parent. + Reference<XAccessibleComponent> xParentComponent ( + getAccessibleParent(), uno::UNO_QUERY); + if (xParentComponent.is()) + { + // Make the coordinates relative to the parent. + awt::Point aParentLocation (xParentComponent->getLocationOnScreen()); + int x = aPixelPosition.getX() - aParentLocation.X; + int y = aPixelPosition.getY() - aParentLocation.Y; + + // Clip with parent (with coordinates relative to itself). + ::tools::Rectangle aBBox ( + x, y, x + aPixelSize.getWidth(), y + aPixelSize.getHeight()); + awt::Size aParentSize (xParentComponent->getSize()); + ::tools::Rectangle aParentBBox (0,0, aParentSize.Width, aParentSize.Height); + aBBox = aBBox.GetIntersection (aParentBBox); + aBoundingBox = awt::Rectangle ( + aBBox.Left(), + aBBox.Top(), + aBBox.getWidth(), + aBBox.getHeight()); + } + else + aBoundingBox = awt::Rectangle ( + aPixelPosition.getX(), aPixelPosition.getY(), + aPixelSize.getWidth(), aPixelSize.getHeight()); + } + + return aBoundingBox; +} + +sal_Int32 SAL_CALL AccessiblePageShape::getForeground() +{ + ThrowIfDisposed (); + sal_Int32 nColor (0x0ffffffL); + + try + { + uno::Reference<beans::XPropertySet> aSet (mxPage, uno::UNO_QUERY); + if (aSet.is()) + { + uno::Any aColor = aSet->getPropertyValue ("LineColor"); + aColor >>= nColor; + } + } + catch (const css::beans::UnknownPropertyException&) + { + // Ignore exception and return default color. + } + return nColor; +} + +/** Extract the background color from the Background property of the + draw page or its master page. +*/ +sal_Int32 SAL_CALL AccessiblePageShape::getBackground() +{ + ThrowIfDisposed (); + sal_Int32 nColor (0x01020ffL); + + try + { + uno::Reference<beans::XPropertySet> xSet (mxPage, uno::UNO_QUERY); + if (xSet.is()) + { + uno::Any aBGSet = xSet->getPropertyValue ("Background"); + Reference<beans::XPropertySet> xBGSet (aBGSet, uno::UNO_QUERY); + if ( ! xBGSet.is()) + { + // Draw page has no Background property. Try the master + // page instead. + Reference<drawing::XMasterPageTarget> xTarget (mxPage, uno::UNO_QUERY); + if (xTarget.is()) + { + xSet.set(xTarget->getMasterPage(), uno::UNO_QUERY); + aBGSet = xSet->getPropertyValue ("Background"); + xBGSet.set(aBGSet, uno::UNO_QUERY); + } + } + // Fetch the fill color. Has to be extended to cope with + // gradients, hashes, and bitmaps. + if (xBGSet.is()) + { + uno::Any aColor = xBGSet->getPropertyValue ("FillColor"); + aColor >>= nColor; + } + else + SAL_WARN("sd", "no Background property in page"); + } + } + catch (const css::beans::UnknownPropertyException&) + { + TOOLS_WARN_EXCEPTION("sd", "caught exception due to unknown property"); + // Ignore exception and return default color. + } + return nColor; +} + +// XServiceInfo + +OUString SAL_CALL + AccessiblePageShape::getImplementationName() +{ + ThrowIfDisposed (); + return "AccessiblePageShape"; +} + +css::uno::Sequence< OUString> SAL_CALL + AccessiblePageShape::getSupportedServiceNames() +{ + ThrowIfDisposed (); + return AccessibleShape::getSupportedServiceNames(); +} + +//===== XComponent ========================================================== + +void AccessiblePageShape::dispose() +{ + // Cleanup. + mxShape = nullptr; + + // Call base classes. + AccessibleContextBase::dispose (); +} + +//===== protected internal ================================================== + +OUString + AccessiblePageShape::CreateAccessibleBaseName() +{ + return "PageShape"; +} + +OUString + AccessiblePageShape::CreateAccessibleName() +{ + Reference<beans::XPropertySet> xPageProperties (mxPage, UNO_QUERY); + + // Get name of the current slide. + OUString sCurrentSlideName; + try + { + if (xPageProperties.is()) + { + xPageProperties->getPropertyValue( "LinkDisplayName" ) >>= sCurrentSlideName; + } + } + catch (const beans::UnknownPropertyException&) + { + } + + return CreateAccessibleBaseName()+": "+sCurrentSlideName; +} + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessiblePresentationGraphicShape.cxx b/sd/source/ui/accessibility/AccessiblePresentationGraphicShape.cxx new file mode 100644 index 000000000..a8c37fa89 --- /dev/null +++ b/sd/source/ui/accessibility/AccessiblePresentationGraphicShape.cxx @@ -0,0 +1,76 @@ +/* -*- 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 <AccessiblePresentationGraphicShape.hxx> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/drawing/XShape.hpp> + +#include <SdShapeTypes.hxx> + +#include <svx/ShapeTypeHandler.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +namespace accessibility +{ +//===== internal ============================================================ + +AccessiblePresentationGraphicShape::AccessiblePresentationGraphicShape( + const AccessibleShapeInfo& rShapeInfo, const AccessibleShapeTreeInfo& rShapeTreeInfo) + : AccessibleGraphicShape(rShapeInfo, rShapeTreeInfo) +{ +} + +AccessiblePresentationGraphicShape::~AccessiblePresentationGraphicShape() {} + +// XServiceInfo + +OUString SAL_CALL AccessiblePresentationGraphicShape::getImplementationName() +{ + return "AccessiblePresentationGraphicShape"; +} + +/// Set this object's name if is different to the current name. +OUString AccessiblePresentationGraphicShape::CreateAccessibleBaseName() +{ + OUString sName; + + ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId(mxShape); + switch (nShapeType) + { + case PRESENTATION_GRAPHIC_OBJECT: + sName = "ImpressGraphicObject"; + break; + default: + sName = "UnknownAccessibleImpressShape"; + if (mxShape.is()) + sName += ": " + mxShape->getShapeType(); + } + + return sName; +} + +sal_Int16 SAL_CALL AccessiblePresentationGraphicShape::getAccessibleRole() +{ + return AccessibleRole::GRAPHIC; +} +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessiblePresentationOLEShape.cxx b/sd/source/ui/accessibility/AccessiblePresentationOLEShape.cxx new file mode 100644 index 000000000..411d04af1 --- /dev/null +++ b/sd/source/ui/accessibility/AccessiblePresentationOLEShape.cxx @@ -0,0 +1,84 @@ +/* -*- 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 <AccessiblePresentationOLEShape.hxx> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/drawing/XShape.hpp> + +#include <SdShapeTypes.hxx> + +#include <svx/ShapeTypeHandler.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +namespace accessibility +{ +//===== internal ============================================================ + +AccessiblePresentationOLEShape::AccessiblePresentationOLEShape( + const AccessibleShapeInfo& rShapeInfo, const AccessibleShapeTreeInfo& rShapeTreeInfo) + : AccessibleOLEShape(rShapeInfo, rShapeTreeInfo) +{ +} + +AccessiblePresentationOLEShape::~AccessiblePresentationOLEShape() {} + +// XServiceInfo + +OUString SAL_CALL AccessiblePresentationOLEShape::getImplementationName() +{ + return "AccessiblePresentationOLEShape"; +} + +/// Set this object's name if it is different to the current name. +OUString AccessiblePresentationOLEShape::CreateAccessibleBaseName() +{ + OUString sName; + + ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId(mxShape); + switch (nShapeType) + { + case PRESENTATION_OLE: + sName = "ImpressOLE"; + break; + case PRESENTATION_CHART: + sName = "ImpressChart"; + break; + case PRESENTATION_TABLE: + sName = "ImpressTable"; + break; + default: + sName = "UnknownAccessibleImpressOLEShape"; + if (mxShape.is()) + sName += ": " + mxShape->getShapeType(); + } + + return sName; +} + +// Return this object's role. +sal_Int16 SAL_CALL AccessiblePresentationOLEShape::getAccessibleRole() +{ + return AccessibleRole::EMBEDDED_OBJECT; +} + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessiblePresentationShape.cxx b/sd/source/ui/accessibility/AccessiblePresentationShape.cxx new file mode 100644 index 000000000..e4afe7e9a --- /dev/null +++ b/sd/source/ui/accessibility/AccessiblePresentationShape.cxx @@ -0,0 +1,146 @@ +/* -*- 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 <AccessiblePresentationShape.hxx> + +#include <SdShapeTypes.hxx> + +#include <strings.hrc> +#include <sdresid.hxx> +#include <svx/ShapeTypeHandler.hxx> + +#include <com/sun/star/drawing/XShape.hpp> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +namespace accessibility +{ +//===== internal ============================================================ + +AccessiblePresentationShape::AccessiblePresentationShape( + const AccessibleShapeInfo& rShapeInfo, const AccessibleShapeTreeInfo& rShapeTreeInfo) + : AccessibleShape(rShapeInfo, rShapeTreeInfo) +{ +} + +AccessiblePresentationShape::~AccessiblePresentationShape() {} + +// XServiceInfo + +OUString SAL_CALL AccessiblePresentationShape::getImplementationName() +{ + return "AccessiblePresentationShape"; +} + +/// Set this object's name if is different to the current name. +OUString AccessiblePresentationShape::CreateAccessibleBaseName() +{ + OUString sName; + + ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId(mxShape); + switch (nShapeType) + { + case PRESENTATION_TITLE: + sName = SdResId(SID_SD_A11Y_P_TITLE_N); + break; + case PRESENTATION_OUTLINER: + sName = SdResId(SID_SD_A11Y_P_OUTLINER_N); + break; + case PRESENTATION_SUBTITLE: + sName = SdResId(SID_SD_A11Y_P_SUBTITLE_N); + break; + case PRESENTATION_PAGE: + sName = SdResId(SID_SD_A11Y_P_PAGE_N); + break; + case PRESENTATION_NOTES: + sName = SdResId(SID_SD_A11Y_P_NOTES_N); + break; + case PRESENTATION_HANDOUT: + sName = SdResId(SID_SD_A11Y_P_HANDOUT_N); + break; + case PRESENTATION_HEADER: + sName = SdResId(SID_SD_A11Y_P_HEADER_N); + break; + case PRESENTATION_FOOTER: + sName = SdResId(SID_SD_A11Y_P_FOOTER_N); + break; + case PRESENTATION_DATETIME: + sName = SdResId(SID_SD_A11Y_P_DATE_N); + break; + case PRESENTATION_PAGENUMBER: + sName = SdResId(SID_SD_A11Y_P_NUMBER_N); + break; + default: + sName = SdResId(SID_SD_A11Y_P_UNKNOWN_N); + if (mxShape.is()) + sName += ": " + mxShape->getShapeType(); + } + + return sName; +} + +OUString AccessiblePresentationShape::GetStyle() const +{ + OUString sName; + + ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId(mxShape); + switch (nShapeType) + { + case PRESENTATION_TITLE: + sName = SdResId(SID_SD_A11Y_P_TITLE_N_STYLE); + break; + case PRESENTATION_OUTLINER: + sName = SdResId(SID_SD_A11Y_P_OUTLINER_N_STYLE); + break; + case PRESENTATION_SUBTITLE: + sName = SdResId(SID_SD_A11Y_P_SUBTITLE_N_STYLE); + break; + case PRESENTATION_PAGE: + sName = SdResId(SID_SD_A11Y_P_PAGE_N_STYLE); + break; + case PRESENTATION_NOTES: + sName = SdResId(SID_SD_A11Y_P_NOTES_N_STYLE); + break; + case PRESENTATION_HANDOUT: + sName = SdResId(SID_SD_A11Y_P_HANDOUT_N_STYLE); + break; + case PRESENTATION_FOOTER: + sName = SdResId(SID_SD_A11Y_P_FOOTER_N_STYLE); + break; + case PRESENTATION_HEADER: + sName = SdResId(SID_SD_A11Y_P_HEADER_N_STYLE); + break; + case PRESENTATION_DATETIME: + sName = SdResId(SID_SD_A11Y_P_DATE_N_STYLE); + break; + case PRESENTATION_PAGENUMBER: + sName = SdResId(SID_SD_A11Y_P_NUMBER_N_STYLE); + break; + default: + sName = SdResId(SID_SD_A11Y_P_UNKNOWN_N_STYLE); + if (mxShape.is()) + sName += ": " + mxShape->getShapeType(); + } + + return sName; +} +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessibleSlideSorterObject.cxx b/sd/source/ui/accessibility/AccessibleSlideSorterObject.cxx new file mode 100644 index 000000000..13fc60db0 --- /dev/null +++ b/sd/source/ui/accessibility/AccessibleSlideSorterObject.cxx @@ -0,0 +1,429 @@ +/* -*- 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 <AccessibleSlideSorterObject.hxx> + +#include <SlideSorter.hxx> +#include <controller/SlideSorterController.hxx> +#include <controller/SlsPageSelector.hxx> +#include <controller/SlsFocusManager.hxx> +#include <model/SlideSorterModel.hxx> +#include <model/SlsPageDescriptor.hxx> +#include <view/SlideSorterView.hxx> +#include <view/SlsLayouter.hxx> +#include <view/SlsPageObjectLayouter.hxx> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/IllegalAccessibleComponentStateException.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <comphelper/accessibleeventnotifier.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <unotools/accessiblestatesethelper.hxx> +#include <sal/log.hxx> + +#include <sdpage.hxx> +#include <sdresid.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> + +#include <strings.hrc> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::accessibility; + +namespace accessibility { + +AccessibleSlideSorterObject::AccessibleSlideSorterObject( + const Reference<XAccessible>& rxParent, + ::sd::slidesorter::SlideSorter& rSlideSorter, + sal_uInt16 nPageNumber) + : mxParent(rxParent), + mnPageNumber(nPageNumber), + mrSlideSorter(rSlideSorter), + mnClientId(0) +{ +} + +AccessibleSlideSorterObject::~AccessibleSlideSorterObject() +{ + if ( ! IsDisposed()) + dispose(); +} + +void AccessibleSlideSorterObject::FireAccessibleEvent ( + short nEventId, + const uno::Any& rOldValue, + const uno::Any& rNewValue) +{ + if (mnClientId != 0) + { + AccessibleEventObject aEventObject; + + aEventObject.Source = Reference<XWeak>(this); + aEventObject.EventId = nEventId; + aEventObject.NewValue = rNewValue; + aEventObject.OldValue = rOldValue; + + comphelper::AccessibleEventNotifier::addEvent(mnClientId, aEventObject); + } +} + +void AccessibleSlideSorterObject::disposing(std::unique_lock<std::mutex>&) +{ + // Send a disposing to all listeners. + if (mnClientId != 0) + { + comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing(mnClientId, *this); + mnClientId = 0; + } +} + +//===== XAccessible =========================================================== + +Reference<XAccessibleContext> SAL_CALL + AccessibleSlideSorterObject::getAccessibleContext() +{ + ThrowIfDisposed(); + return this; +} + +//===== XAccessibleContext ==================================================== + +sal_Int32 SAL_CALL AccessibleSlideSorterObject::getAccessibleChildCount() +{ + ThrowIfDisposed(); + return 0; +} + +Reference<XAccessible> SAL_CALL AccessibleSlideSorterObject::getAccessibleChild (sal_Int32 ) +{ + ThrowIfDisposed(); + throw lang::IndexOutOfBoundsException(); +} + +Reference<XAccessible> SAL_CALL AccessibleSlideSorterObject::getAccessibleParent() +{ + ThrowIfDisposed(); + return mxParent; +} + +sal_Int32 SAL_CALL AccessibleSlideSorterObject::getAccessibleIndexInParent() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + sal_Int32 nIndexInParent(-1); + + if (mxParent.is()) + { + Reference<XAccessibleContext> xParentContext (mxParent->getAccessibleContext()); + if (xParentContext.is()) + { + sal_Int32 nChildCount (xParentContext->getAccessibleChildCount()); + for (sal_Int32 i=0; i<nChildCount; ++i) + if (xParentContext->getAccessibleChild(i).get() + == static_cast<XAccessible*>(this)) + { + nIndexInParent = i; + break; + } + } + } + + return nIndexInParent; +} + +sal_Int16 SAL_CALL AccessibleSlideSorterObject::getAccessibleRole() +{ + ThrowIfDisposed(); + return AccessibleRole::SHAPE; +} + +OUString SAL_CALL AccessibleSlideSorterObject::getAccessibleDescription() +{ + ThrowIfDisposed(); + return SdResId(STR_PAGE); +} + +OUString SAL_CALL AccessibleSlideSorterObject::getAccessibleName() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + + SdPage* pPage = GetPage(); + if (pPage != nullptr) + return pPage->GetName(); + else + return OUString(); +} + +Reference<XAccessibleRelationSet> SAL_CALL + AccessibleSlideSorterObject::getAccessibleRelationSet() +{ + ThrowIfDisposed(); + return Reference<XAccessibleRelationSet>(); +} + +Reference<XAccessibleStateSet> SAL_CALL + AccessibleSlideSorterObject::getAccessibleStateSet() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + rtl::Reference<::utl::AccessibleStateSetHelper> pStateSet = new ::utl::AccessibleStateSetHelper(); + + if (mxParent.is()) + { + // Unconditional states. + pStateSet->AddState(AccessibleStateType::SELECTABLE); + pStateSet->AddState(AccessibleStateType::FOCUSABLE); + pStateSet->AddState(AccessibleStateType::ENABLED); + pStateSet->AddState(AccessibleStateType::VISIBLE); + pStateSet->AddState(AccessibleStateType::SHOWING); + pStateSet->AddState(AccessibleStateType::ACTIVE); + pStateSet->AddState(AccessibleStateType::SENSITIVE); + + // Conditional states. + if (mrSlideSorter.GetController().GetPageSelector().IsPageSelected(mnPageNumber)) + pStateSet->AddState(AccessibleStateType::SELECTED); + if (mrSlideSorter.GetController().GetFocusManager().GetFocusedPageIndex() == mnPageNumber) + if (mrSlideSorter.GetController().GetFocusManager().IsFocusShowing()) + pStateSet->AddState(AccessibleStateType::FOCUSED); + } + + return pStateSet; +} + +lang::Locale SAL_CALL AccessibleSlideSorterObject::getLocale() +{ + ThrowIfDisposed(); + // Delegate request to parent. + if (mxParent.is()) + { + Reference<XAccessibleContext> xParentContext (mxParent->getAccessibleContext()); + if (xParentContext.is()) + return xParentContext->getLocale (); + } + + // No locale and no parent. Therefore throw exception to indicate this + // cluelessness. + throw IllegalAccessibleComponentStateException(); +} + +//===== XAccessibleEventBroadcaster =========================================== + +void SAL_CALL AccessibleSlideSorterObject::addAccessibleEventListener( + const Reference<XAccessibleEventListener>& rxListener) +{ + if (!rxListener.is()) + return; + + const std::unique_lock aGuard(m_aMutex); + + if (IsDisposed()) + { + uno::Reference<uno::XInterface> x (static_cast<lang::XComponent *>(this), uno::UNO_QUERY); + rxListener->disposing (lang::EventObject (x)); + } + else + { + if (mnClientId == 0) + mnClientId = comphelper::AccessibleEventNotifier::registerClient(); + comphelper::AccessibleEventNotifier::addEventListener(mnClientId, rxListener); + } +} + +void SAL_CALL AccessibleSlideSorterObject::removeAccessibleEventListener( + const Reference<XAccessibleEventListener>& rxListener) +{ + ThrowIfDisposed(); + if (!(rxListener.is() && mnClientId)) + return; + + const std::unique_lock aGuard(m_aMutex); + + sal_Int32 nListenerCount = comphelper::AccessibleEventNotifier::removeEventListener( mnClientId, rxListener ); + if ( !nListenerCount ) + { + // no listeners anymore + // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client), + // and at least to us not firing any events anymore, in case somebody calls + // NotifyAccessibleEvent, again + comphelper::AccessibleEventNotifier::revokeClient( mnClientId ); + mnClientId = 0; + } +} + +//===== XAccessibleComponent ================================================== + +sal_Bool SAL_CALL AccessibleSlideSorterObject::containsPoint(const awt::Point& aPoint) +{ + ThrowIfDisposed(); + const awt::Size aSize (getSize()); + return (aPoint.X >= 0) + && (aPoint.X < aSize.Width) + && (aPoint.Y >= 0) + && (aPoint.Y < aSize.Height); +} + +Reference<XAccessible> SAL_CALL + AccessibleSlideSorterObject::getAccessibleAtPoint(const awt::Point& ) +{ + return nullptr; +} + +awt::Rectangle SAL_CALL AccessibleSlideSorterObject::getBounds() +{ + ThrowIfDisposed (); + + const SolarMutexGuard aSolarGuard; + + ::tools::Rectangle aBBox ( + mrSlideSorter.GetView().GetLayouter().GetPageObjectLayouter()->GetBoundingBox( + mrSlideSorter.GetModel().GetPageDescriptor(mnPageNumber), + ::sd::slidesorter::view::PageObjectLayouter::Part::PageObject, + ::sd::slidesorter::view::PageObjectLayouter::WindowCoordinateSystem)); + + if (mxParent.is()) + { + Reference<XAccessibleComponent> xParentComponent(mxParent->getAccessibleContext(), UNO_QUERY); + if (xParentComponent.is()) + { + awt::Rectangle aParentBBox (xParentComponent->getBounds()); + aBBox.Intersection(::tools::Rectangle( + aParentBBox.X, + aParentBBox.Y, + aParentBBox.Width, + aParentBBox.Height)); + } + } + + return awt::Rectangle( + aBBox.Left(), + aBBox.Top(), + aBBox.GetWidth(), + aBBox.GetHeight()); +} + +awt::Point SAL_CALL AccessibleSlideSorterObject::getLocation () +{ + ThrowIfDisposed (); + const awt::Rectangle aBBox (getBounds()); + return awt::Point(aBBox.X, aBBox.Y); +} + +awt::Point SAL_CALL AccessibleSlideSorterObject::getLocationOnScreen() +{ + ThrowIfDisposed (); + + const SolarMutexGuard aSolarGuard; + + awt::Point aLocation (getLocation()); + + if (mxParent.is()) + { + Reference<XAccessibleComponent> xParentComponent(mxParent->getAccessibleContext(),UNO_QUERY); + if (xParentComponent.is()) + { + const awt::Point aParentLocationOnScreen(xParentComponent->getLocationOnScreen()); + aLocation.X += aParentLocationOnScreen.X; + aLocation.Y += aParentLocationOnScreen.Y; + } + } + + return aLocation; +} + +awt::Size SAL_CALL AccessibleSlideSorterObject::getSize() +{ + ThrowIfDisposed (); + const awt::Rectangle aBBox (getBounds()); + return awt::Size(aBBox.Width,aBBox.Height); +} + +void SAL_CALL AccessibleSlideSorterObject::grabFocus() +{ + // nothing to do +} + +sal_Int32 SAL_CALL AccessibleSlideSorterObject::getForeground() +{ + ThrowIfDisposed (); + svtools::ColorConfig aColorConfig; + Color nColor = aColorConfig.GetColorValue( svtools::FONTCOLOR ).nColor; + return static_cast<sal_Int32>(nColor); +} + +sal_Int32 SAL_CALL AccessibleSlideSorterObject::getBackground() +{ + ThrowIfDisposed (); + Color nColor = Application::GetSettings().GetStyleSettings().GetWindowColor(); + return sal_Int32(nColor); +} + +// XServiceInfo +OUString SAL_CALL + AccessibleSlideSorterObject::getImplementationName() +{ + return "AccessibleSlideSorterObject"; +} + +sal_Bool SAL_CALL AccessibleSlideSorterObject::supportsService (const OUString& sServiceName) +{ + return cppu::supportsService(this, sServiceName); +} + +uno::Sequence< OUString> SAL_CALL + AccessibleSlideSorterObject::getSupportedServiceNames() +{ + ThrowIfDisposed (); + + return uno::Sequence<OUString> { + OUString("com.sun.star.accessibility.Accessible"), + OUString("com.sun.star.accessibility.AccessibleContext") + }; +} + +void AccessibleSlideSorterObject::ThrowIfDisposed() +{ + if (m_bDisposed) + { + SAL_WARN("sd", "Calling disposed object. Throwing exception:"); + throw lang::DisposedException ("object has been already disposed", + static_cast<uno::XWeak*>(this)); + } +} + +bool AccessibleSlideSorterObject::IsDisposed() const +{ + return m_bDisposed; +} + +SdPage* AccessibleSlideSorterObject::GetPage() const +{ + ::sd::slidesorter::model::SharedPageDescriptor pDescriptor( + mrSlideSorter.GetModel().GetPageDescriptor(mnPageNumber)); + if (pDescriptor) + return pDescriptor->GetPage(); + else + return nullptr; +} + +} // end of namespace ::accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessibleSlideSorterView.cxx b/sd/source/ui/accessibility/AccessibleSlideSorterView.cxx new file mode 100644 index 000000000..87eea89d2 --- /dev/null +++ b/sd/source/ui/accessibility/AccessibleSlideSorterView.cxx @@ -0,0 +1,950 @@ +/* -*- 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 <AccessibleSlideSorterView.hxx> +#include <AccessibleSlideSorterObject.hxx> + +#include <SlideSorter.hxx> +#include <controller/SlideSorterController.hxx> +#include <controller/SlsPageSelector.hxx> +#include <controller/SlsFocusManager.hxx> +#include <controller/SlsSelectionManager.hxx> +#include <view/SlideSorterView.hxx> +#include <model/SlideSorterModel.hxx> +#include <model/SlsPageDescriptor.hxx> + +#include <ViewShell.hxx> +#include <ViewShellHint.hxx> +#include <sdpage.hxx> +#include <drawdoc.hxx> + +#include <sdresid.hxx> +#include <strings.hrc> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <comphelper/accessibleeventnotifier.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <o3tl/safeint.hxx> +#include <unotools/accessiblestatesethelper.hxx> +#include <rtl/ref.hxx> +#include <sal/log.hxx> +#include <i18nlangtag/languagetag.hxx> + +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::accessibility; + +namespace accessibility { + +/** Inner implementation class of the AccessibleSlideSorterView. + + Note that some event broadcasting is done asynchronously because + otherwise it could lead to deadlocks on (at least) some Solaris + machines. Probably (but unverified) this can happen on all GTK based + systems. The asynchronous broadcasting is just a workaround for a + poorly understood problem. +*/ +class AccessibleSlideSorterView::Implementation + : public SfxListener +{ +public: + Implementation ( + AccessibleSlideSorterView& rAccessibleSlideSorter, + ::sd::slidesorter::SlideSorter& rSlideSorter, + vcl::Window* pWindow); + virtual ~Implementation() override; + + void RequestUpdateChildren(); + void Clear(); + sal_Int32 GetVisibleChildCount() const; + AccessibleSlideSorterObject* GetAccessibleChild (sal_Int32 nIndex); + AccessibleSlideSorterObject* GetVisibleChild (sal_Int32 nIndex); + + void ConnectListeners(); + void ReleaseListeners(); + void Notify (SfxBroadcaster& rBroadcaster, const SfxHint& rHint) override; + DECL_LINK(WindowEventListener, VclWindowEvent&, void); + DECL_LINK(SelectionChangeListener, LinkParamNone*, void); + DECL_LINK(BroadcastSelectionChange, void*, void); + DECL_LINK(FocusChangeListener, LinkParamNone*, void); + DECL_LINK(VisibilityChangeListener, LinkParamNone*, void); + DECL_LINK(UpdateChildrenCallback, void*, void); + + void Activated(); +private: + AccessibleSlideSorterView& mrAccessibleSlideSorter; + ::sd::slidesorter::SlideSorter& mrSlideSorter; + typedef ::std::vector<rtl::Reference<AccessibleSlideSorterObject> > PageObjectList; + PageObjectList maPageObjects; + sal_Int32 mnFirstVisibleChild; + sal_Int32 mnLastVisibleChild; + bool mbListeningToDocument; + VclPtr<vcl::Window> mpWindow; + sal_Int32 mnFocusedIndex; + bool mbModelChangeLocked; + ImplSVEvent * mnUpdateChildrenUserEventId; + ImplSVEvent * mnSelectionChangeUserEventId; + + void UpdateChildren(); +}; + +//===== AccessibleSlideSorterView ============================================= + +AccessibleSlideSorterView::AccessibleSlideSorterView( + ::sd::slidesorter::SlideSorter& rSlideSorter, + vcl::Window* pContentWindow) + : AccessibleSlideSorterViewBase(m_aMutex), + mrSlideSorter(rSlideSorter), + mnClientId(0), + mpContentWindow(pContentWindow) +{ +} + +void AccessibleSlideSorterView::Init() +{ + mpImpl.reset(new Implementation(*this,mrSlideSorter,mpContentWindow)); +} + +AccessibleSlideSorterView::~AccessibleSlideSorterView() +{ + Destroyed (); +} + +void AccessibleSlideSorterView::FireAccessibleEvent ( + short nEventId, + const uno::Any& rOldValue, + const uno::Any& rNewValue ) +{ + if (mnClientId != 0) + { + AccessibleEventObject aEventObject; + + aEventObject.Source = Reference<XWeak>(this); + aEventObject.EventId = nEventId; + aEventObject.NewValue = rNewValue; + aEventObject.OldValue = rOldValue; + + comphelper::AccessibleEventNotifier::addEvent (mnClientId, aEventObject); + } +} + +void SAL_CALL AccessibleSlideSorterView::disposing() +{ + if (mnClientId != 0) + { + comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( mnClientId, *this ); + mnClientId = 0; + } + mpImpl.reset(); +} + +AccessibleSlideSorterObject* AccessibleSlideSorterView::GetAccessibleChildImplementation ( + sal_Int32 nIndex) +{ + AccessibleSlideSorterObject* pResult = nullptr; + ::osl::MutexGuard aGuard (m_aMutex); + + if (nIndex>=0 && nIndex<mpImpl->GetVisibleChildCount()) + pResult = mpImpl->GetVisibleChild(nIndex); + + return pResult; +} + +void AccessibleSlideSorterView::Destroyed() +{ + ::osl::MutexGuard aGuard (m_aMutex); + + // Send a disposing to all listeners. + if (mnClientId != 0) + { + comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( mnClientId, *this ); + mnClientId = 0; + } +} + +//===== XAccessible ========================================================= + +Reference<XAccessibleContext > SAL_CALL + AccessibleSlideSorterView::getAccessibleContext() +{ + ThrowIfDisposed (); + return this; +} + +//===== XAccessibleContext ================================================== + +sal_Int32 SAL_CALL AccessibleSlideSorterView::getAccessibleChildCount() +{ + ThrowIfDisposed(); + ::osl::MutexGuard aGuard (m_aMutex); + return mpImpl->GetVisibleChildCount(); +} + +Reference<XAccessible > SAL_CALL + AccessibleSlideSorterView::getAccessibleChild (sal_Int32 nIndex) +{ + ThrowIfDisposed(); + ::osl::MutexGuard aGuard (m_aMutex); + + if (nIndex<0 || nIndex>=mpImpl->GetVisibleChildCount()) + throw lang::IndexOutOfBoundsException(); + + return mpImpl->GetVisibleChild(nIndex); +} + +Reference<XAccessible > SAL_CALL AccessibleSlideSorterView::getAccessibleParent() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + Reference<XAccessible> xParent; + + if (mpContentWindow != nullptr) + { + vcl::Window* pParent = mpContentWindow->GetAccessibleParentWindow(); + if (pParent != nullptr) + xParent = pParent->GetAccessible(); + } + + return xParent; +} + +sal_Int32 SAL_CALL AccessibleSlideSorterView::getAccessibleIndexInParent() +{ + OSL_ASSERT(getAccessibleParent().is()); + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + sal_Int32 nIndexInParent(-1); + + Reference<XAccessibleContext> xParentContext (getAccessibleParent()->getAccessibleContext()); + if (xParentContext.is()) + { + sal_Int32 nChildCount (xParentContext->getAccessibleChildCount()); + for (sal_Int32 i=0; i<nChildCount; ++i) + if (xParentContext->getAccessibleChild(i).get() + == static_cast<XAccessible*>(this)) + { + nIndexInParent = i; + break; + } + } + + return nIndexInParent; +} + +sal_Int16 SAL_CALL AccessibleSlideSorterView::getAccessibleRole() +{ + ThrowIfDisposed(); + return AccessibleRole::DOCUMENT; +} + +OUString SAL_CALL AccessibleSlideSorterView::getAccessibleDescription() +{ + ThrowIfDisposed(); + SolarMutexGuard aGuard; + + return SdResId(SID_SD_A11Y_I_SLIDEVIEW_D); +} + +OUString SAL_CALL AccessibleSlideSorterView::getAccessibleName() +{ + ThrowIfDisposed(); + SolarMutexGuard aGuard; + + return SdResId(SID_SD_A11Y_I_SLIDEVIEW_N); +} + +Reference<XAccessibleRelationSet> SAL_CALL + AccessibleSlideSorterView::getAccessibleRelationSet() +{ + return Reference<XAccessibleRelationSet>(); +} + +Reference<XAccessibleStateSet > SAL_CALL + AccessibleSlideSorterView::getAccessibleStateSet() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + rtl::Reference<::utl::AccessibleStateSetHelper> pStateSet = new ::utl::AccessibleStateSetHelper(); + + pStateSet->AddState(AccessibleStateType::FOCUSABLE); + pStateSet->AddState(AccessibleStateType::SELECTABLE); + pStateSet->AddState(AccessibleStateType::ENABLED); + pStateSet->AddState(AccessibleStateType::ACTIVE); + pStateSet->AddState(AccessibleStateType::MULTI_SELECTABLE); + pStateSet->AddState(AccessibleStateType::OPAQUE); + if (mpContentWindow!=nullptr) + { + if (mpContentWindow->IsVisible()) + pStateSet->AddState(AccessibleStateType::VISIBLE); + if (mpContentWindow->IsReallyVisible()) + pStateSet->AddState(AccessibleStateType::SHOWING); + } + + return pStateSet; +} + +lang::Locale SAL_CALL AccessibleSlideSorterView::getLocale() +{ + ThrowIfDisposed (); + Reference<XAccessibleContext> xParentContext; + Reference<XAccessible> xParent (getAccessibleParent()); + if (xParent.is()) + xParentContext = xParent->getAccessibleContext(); + + if (xParentContext.is()) + return xParentContext->getLocale(); + else + // Strange, no parent! Anyway, return the default locale. + return Application::GetSettings().GetLanguageTag().getLocale(); +} + +void SAL_CALL AccessibleSlideSorterView::addAccessibleEventListener( + const Reference<XAccessibleEventListener >& rxListener) +{ + if (!rxListener.is()) + return; + + const osl::MutexGuard aGuard(m_aMutex); + + if (rBHelper.bDisposed || rBHelper.bInDispose) + { + uno::Reference<uno::XInterface> x (static_cast<lang::XComponent *>(this), uno::UNO_QUERY); + rxListener->disposing (lang::EventObject (x)); + } + else + { + if ( ! mnClientId) + mnClientId = comphelper::AccessibleEventNotifier::registerClient(); + comphelper::AccessibleEventNotifier::addEventListener(mnClientId, rxListener); + } +} + +void SAL_CALL AccessibleSlideSorterView::removeAccessibleEventListener( + const Reference<XAccessibleEventListener >& rxListener) +{ + ThrowIfDisposed(); + if (!rxListener.is()) + return; + + const osl::MutexGuard aGuard(m_aMutex); + + if (mnClientId == 0) + return; + + sal_Int32 nListenerCount = comphelper::AccessibleEventNotifier::removeEventListener( + mnClientId, rxListener ); + if ( !nListenerCount ) + { + // no listeners anymore -> revoke ourself. This may lead to + // the notifier thread dying (if we were the last client), + // and at least to us not firing any events anymore, in case + // somebody calls NotifyAccessibleEvent, again + comphelper::AccessibleEventNotifier::revokeClient( mnClientId ); + mnClientId = 0; + } +} + +//===== XAccessibleComponent ================================================== + +sal_Bool SAL_CALL AccessibleSlideSorterView::containsPoint (const awt::Point& aPoint) +{ + ThrowIfDisposed(); + const awt::Rectangle aBBox (getBounds()); + return (aPoint.X >= 0) + && (aPoint.X < aBBox.Width) + && (aPoint.Y >= 0) + && (aPoint.Y < aBBox.Height); +} + +Reference<XAccessible> SAL_CALL + AccessibleSlideSorterView::getAccessibleAtPoint (const awt::Point& aPoint) +{ + ThrowIfDisposed(); + Reference<XAccessible> xAccessible; + const SolarMutexGuard aSolarGuard; + + const Point aTestPoint (aPoint.X, aPoint.Y); + ::sd::slidesorter::model::SharedPageDescriptor pHitDescriptor ( + mrSlideSorter.GetController().GetPageAt(aTestPoint)); + if (pHitDescriptor) + xAccessible = mpImpl->GetAccessibleChild( + (pHitDescriptor->GetPage()->GetPageNum()-1)/2); + + return xAccessible; +} + +awt::Rectangle SAL_CALL AccessibleSlideSorterView::getBounds() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + awt::Rectangle aBBox; + + if (mpContentWindow != nullptr) + { + const Point aPosition (mpContentWindow->GetPosPixel()); + const Size aSize (mpContentWindow->GetOutputSizePixel()); + + aBBox.X = aPosition.X(); + aBBox.Y = aPosition.Y(); + aBBox.Width = aSize.Width(); + aBBox.Height = aSize.Height(); + } + + return aBBox; +} + +awt::Point SAL_CALL AccessibleSlideSorterView::getLocation() +{ + ThrowIfDisposed(); + awt::Point aLocation; + + if (mpContentWindow != nullptr) + { + const Point aPosition (mpContentWindow->GetPosPixel()); + aLocation.X = aPosition.X(); + aLocation.Y = aPosition.Y(); + } + + return aLocation; +} + +/** Calculate the location on screen from the parent's location on screen + and our own relative location. +*/ +awt::Point SAL_CALL AccessibleSlideSorterView::getLocationOnScreen() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + awt::Point aParentLocationOnScreen; + + Reference<XAccessible> xParent (getAccessibleParent()); + if (xParent.is()) + { + Reference<XAccessibleComponent> xParentComponent ( + xParent->getAccessibleContext(), uno::UNO_QUERY); + if (xParentComponent.is()) + aParentLocationOnScreen = xParentComponent->getLocationOnScreen(); + } + + awt::Point aLocationOnScreen (getLocation()); + aLocationOnScreen.X += aParentLocationOnScreen.X; + aLocationOnScreen.Y += aParentLocationOnScreen.Y; + + return aLocationOnScreen; +} + +awt::Size SAL_CALL AccessibleSlideSorterView::getSize() +{ + ThrowIfDisposed(); + awt::Size aSize; + + if (mpContentWindow != nullptr) + { + const Size aOutputSize (mpContentWindow->GetOutputSizePixel()); + aSize.Width = aOutputSize.Width(); + aSize.Height = aOutputSize.Height(); + } + + return aSize; +} + +void SAL_CALL AccessibleSlideSorterView::grabFocus() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + + if (mpContentWindow) + mpContentWindow->GrabFocus(); +} + +sal_Int32 SAL_CALL AccessibleSlideSorterView::getForeground() +{ + ThrowIfDisposed(); + svtools::ColorConfig aColorConfig; + Color nColor = aColorConfig.GetColorValue( svtools::FONTCOLOR ).nColor; + return static_cast<sal_Int32>(nColor); +} + +sal_Int32 SAL_CALL AccessibleSlideSorterView::getBackground() +{ + ThrowIfDisposed(); + Color nColor = Application::GetSettings().GetStyleSettings().GetWindowColor(); + return sal_Int32(nColor); +} + +//===== XAccessibleSelection ================================================== + +void SAL_CALL AccessibleSlideSorterView::selectAccessibleChild (sal_Int32 nChildIndex) +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + + AccessibleSlideSorterObject* pChild = mpImpl->GetAccessibleChild(nChildIndex); + if (pChild == nullptr) + throw lang::IndexOutOfBoundsException(); + + mrSlideSorter.GetController().GetPageSelector().SelectPage(pChild->GetPageNumber()); +} + +sal_Bool SAL_CALL AccessibleSlideSorterView::isAccessibleChildSelected (sal_Int32 nChildIndex) +{ + ThrowIfDisposed(); + bool bIsSelected = false; + const SolarMutexGuard aSolarGuard; + + AccessibleSlideSorterObject* pChild = mpImpl->GetAccessibleChild(nChildIndex); + if (pChild == nullptr) + throw lang::IndexOutOfBoundsException(); + + bIsSelected = mrSlideSorter.GetController().GetPageSelector().IsPageSelected( + pChild->GetPageNumber()); + + return bIsSelected; +} + +void SAL_CALL AccessibleSlideSorterView::clearAccessibleSelection() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + + mrSlideSorter.GetController().GetPageSelector().DeselectAllPages(); +} + +void SAL_CALL AccessibleSlideSorterView::selectAllAccessibleChildren() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + + mrSlideSorter.GetController().GetPageSelector().SelectAllPages(); +} + +sal_Int32 SAL_CALL AccessibleSlideSorterView::getSelectedAccessibleChildCount() +{ + ThrowIfDisposed (); + const SolarMutexGuard aSolarGuard; + return mrSlideSorter.GetController().GetPageSelector().GetSelectedPageCount(); +} + +Reference<XAccessible > SAL_CALL + AccessibleSlideSorterView::getSelectedAccessibleChild (sal_Int32 nSelectedChildIndex ) +{ + ThrowIfDisposed (); + const SolarMutexGuard aSolarGuard; + Reference<XAccessible> xChild; + + ::sd::slidesorter::controller::PageSelector& rSelector ( + mrSlideSorter.GetController().GetPageSelector()); + sal_Int32 nPageCount(rSelector.GetPageCount()); + sal_Int32 nSelectedCount = 0; + for (sal_Int32 i=0; i<nPageCount; i++) + if (rSelector.IsPageSelected(i)) + { + if (nSelectedCount == nSelectedChildIndex) + { + xChild = mpImpl->GetAccessibleChild(i); + break; + } + ++nSelectedCount; + } + + if ( ! xChild.is() ) + throw lang::IndexOutOfBoundsException(); + + return xChild; +} + +void SAL_CALL AccessibleSlideSorterView::deselectAccessibleChild (sal_Int32 nChildIndex) +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + + AccessibleSlideSorterObject* pChild = mpImpl->GetAccessibleChild(nChildIndex); + if (pChild == nullptr) + throw lang::IndexOutOfBoundsException(); + + mrSlideSorter.GetController().GetPageSelector().DeselectPage(pChild->GetPageNumber()); +} + +// XServiceInfo +OUString SAL_CALL + AccessibleSlideSorterView::getImplementationName() +{ + return "AccessibleSlideSorterView"; +} + +sal_Bool SAL_CALL AccessibleSlideSorterView::supportsService (const OUString& sServiceName) +{ + return cppu::supportsService(this, sServiceName); +} + +uno::Sequence< OUString> SAL_CALL + AccessibleSlideSorterView::getSupportedServiceNames() +{ + ThrowIfDisposed (); + + return uno::Sequence<OUString> { + OUString("com.sun.star.accessibility.Accessible"), + OUString("com.sun.star.accessibility.AccessibleContext"), + OUString("com.sun.star.drawing.AccessibleSlideSorterView") + }; +} + +void AccessibleSlideSorterView::ThrowIfDisposed() +{ + if (rBHelper.bDisposed || rBHelper.bInDispose) + { + SAL_WARN("sd", "Calling disposed object. Throwing exception:"); + throw lang::DisposedException ("object has been already disposed", + static_cast<uno::XWeak*>(this)); + } +} + +//===== AccessibleSlideSorterView::Implementation ============================= + +AccessibleSlideSorterView::Implementation::Implementation ( + AccessibleSlideSorterView& rAccessibleSlideSorter, + ::sd::slidesorter::SlideSorter& rSlideSorter, + vcl::Window* pWindow) + : mrAccessibleSlideSorter(rAccessibleSlideSorter), + mrSlideSorter(rSlideSorter), + mnFirstVisibleChild(0), + mnLastVisibleChild(-1), + mbListeningToDocument(false), + mpWindow(pWindow), + mnFocusedIndex(-1), + mbModelChangeLocked(false), + mnUpdateChildrenUserEventId(nullptr), + mnSelectionChangeUserEventId(nullptr) +{ + ConnectListeners(); + UpdateChildren(); +} + +AccessibleSlideSorterView::Implementation::~Implementation() +{ + if (mnUpdateChildrenUserEventId != nullptr) + Application::RemoveUserEvent(mnUpdateChildrenUserEventId); + if (mnSelectionChangeUserEventId != nullptr) + Application::RemoveUserEvent(mnSelectionChangeUserEventId); + ReleaseListeners(); + Clear(); +} + +void AccessibleSlideSorterView::Implementation::RequestUpdateChildren() +{ + if (mnUpdateChildrenUserEventId == nullptr) + mnUpdateChildrenUserEventId = Application::PostUserEvent( + LINK(this, AccessibleSlideSorterView::Implementation, + UpdateChildrenCallback)); +} + +void AccessibleSlideSorterView::Implementation::UpdateChildren() +{ + //By default, all children should be accessible. So here workaround is to make all children visible. + // MT: This was in UpdateVisibility, which has some similarity, and hg merge automatically has put it here. Correct?! + // In the IA2 CWS, also setting mnFirst/LastVisibleChild was commented out! + mnLastVisibleChild = maPageObjects.size(); + + if (mbModelChangeLocked) + { + // Do nothing right now. When the flag is reset, this method is + // called again. + return; + } + + const Range aRange (mrSlideSorter.GetView().GetVisiblePageRange()); + mnFirstVisibleChild = aRange.Min(); + mnLastVisibleChild = aRange.Max(); + + // Release all children. + Clear(); + + // Create new children for the modified visible range. + maPageObjects.resize(mrSlideSorter.GetModel().GetPageCount()); + + // No Visible children + if (mnFirstVisibleChild == -1 && mnLastVisibleChild == -1) + return; + + for (sal_Int32 nIndex(mnFirstVisibleChild); nIndex<=mnLastVisibleChild; ++nIndex) + GetAccessibleChild(nIndex); +} + +void AccessibleSlideSorterView::Implementation::Clear() +{ + for (auto& rxPageObject : maPageObjects) + if (rxPageObject != nullptr) + { + mrAccessibleSlideSorter.FireAccessibleEvent( + AccessibleEventId::CHILD, + Any(Reference<XAccessible>(rxPageObject)), + Any()); + + Reference<XComponent> xComponent (Reference<XWeak>(rxPageObject), UNO_QUERY); + if (xComponent.is()) + xComponent->dispose(); + rxPageObject = nullptr; + } + maPageObjects.clear(); +} + +sal_Int32 AccessibleSlideSorterView::Implementation::GetVisibleChildCount() const +{ + if (mnFirstVisibleChild<=mnLastVisibleChild && mnFirstVisibleChild>=0) + return mnLastVisibleChild - mnFirstVisibleChild + 1; + else + return 0; +} + +AccessibleSlideSorterObject* AccessibleSlideSorterView::Implementation::GetVisibleChild ( + sal_Int32 nIndex) +{ + assert(nIndex>=0 && nIndex<GetVisibleChildCount()); + + return GetAccessibleChild(nIndex+mnFirstVisibleChild); +} + +AccessibleSlideSorterObject* AccessibleSlideSorterView::Implementation::GetAccessibleChild ( + sal_Int32 nIndex) +{ + AccessibleSlideSorterObject* pChild = nullptr; + + if (nIndex>=0 && o3tl::make_unsigned(nIndex)<maPageObjects.size()) + { + if (maPageObjects[nIndex] == nullptr) + { + ::sd::slidesorter::model::SharedPageDescriptor pDescriptor( + mrSlideSorter.GetModel().GetPageDescriptor(nIndex)); + if (pDescriptor) + { + maPageObjects[nIndex] = new AccessibleSlideSorterObject( + &mrAccessibleSlideSorter, + mrSlideSorter, + (pDescriptor->GetPage()->GetPageNum()-1)/2); + + mrAccessibleSlideSorter.FireAccessibleEvent( + AccessibleEventId::CHILD, + Any(), + Any(Reference<XAccessible>(maPageObjects[nIndex]))); + } + + } + + pChild = maPageObjects[nIndex].get(); + } + else + { + OSL_ASSERT(nIndex>=0 && o3tl::make_unsigned(nIndex)<maPageObjects.size()); + } + + return pChild; +} + +void AccessibleSlideSorterView::Implementation::ConnectListeners() +{ + StartListening (*mrSlideSorter.GetModel().GetDocument()); + if (mrSlideSorter.GetViewShell() != nullptr) + StartListening (*mrSlideSorter.GetViewShell()); + mbListeningToDocument = true; + + if (mpWindow != nullptr) + mpWindow->AddEventListener( + LINK(this,AccessibleSlideSorterView::Implementation,WindowEventListener)); + + mrSlideSorter.GetController().GetSelectionManager()->AddSelectionChangeListener( + LINK(this,AccessibleSlideSorterView::Implementation,SelectionChangeListener)); + mrSlideSorter.GetController().GetFocusManager().AddFocusChangeListener( + LINK(this,AccessibleSlideSorterView::Implementation,FocusChangeListener)); + mrSlideSorter.GetView().AddVisibilityChangeListener( + LINK(this,AccessibleSlideSorterView::Implementation,VisibilityChangeListener)); +} + +void AccessibleSlideSorterView::Implementation::ReleaseListeners() +{ + mrSlideSorter.GetController().GetFocusManager().RemoveFocusChangeListener( + LINK(this,AccessibleSlideSorterView::Implementation,FocusChangeListener)); + mrSlideSorter.GetController().GetSelectionManager()->RemoveSelectionChangeListener( + LINK(this,AccessibleSlideSorterView::Implementation,SelectionChangeListener)); + mrSlideSorter.GetView().RemoveVisibilityChangeListener( + LINK(this,AccessibleSlideSorterView::Implementation,VisibilityChangeListener)); + + if (mpWindow != nullptr) + mpWindow->RemoveEventListener( + LINK(this,AccessibleSlideSorterView::Implementation,WindowEventListener)); + + if (mbListeningToDocument) + { + if (mrSlideSorter.GetViewShell() != nullptr && !IsListening(*mrSlideSorter.GetViewShell())) + { // ??? is it even possible that ConnectListeners is called with no + // view shell and this one with a view shell? + StartListening(*mrSlideSorter.GetViewShell()); + } + EndListening (*mrSlideSorter.GetModel().GetDocument()); + mbListeningToDocument = false; + } +} + +void AccessibleSlideSorterView::Implementation::Notify ( + SfxBroadcaster&, + const SfxHint& rHint) +{ + if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint) + { + const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint); + switch (pSdrHint->GetKind()) + { + case SdrHintKind::PageOrderChange: + RequestUpdateChildren(); + break; + default: + break; + } + } + else if (auto pViewShellHint = dynamic_cast<const sd::ViewShellHint*>(&rHint)) + { + switch (pViewShellHint->GetHintId()) + { + case sd::ViewShellHint::HINT_COMPLEX_MODEL_CHANGE_START: + mbModelChangeLocked = true; + break; + + case sd::ViewShellHint::HINT_COMPLEX_MODEL_CHANGE_END: + mbModelChangeLocked = false; + RequestUpdateChildren(); + break; + default: + break; + } + } +} + +void AccessibleSlideSorterView::SwitchViewActivated() +{ + // Firstly, set focus to view + FireAccessibleEvent(AccessibleEventId::STATE_CHANGED, + Any(), + Any(AccessibleStateType::FOCUSED)); + + mpImpl->Activated(); +} + +void AccessibleSlideSorterView::Implementation::Activated() +{ + mrSlideSorter.GetController().GetFocusManager().ShowFocus(); + +} + +IMPL_LINK(AccessibleSlideSorterView::Implementation, WindowEventListener, VclWindowEvent&, rEvent, void) +{ + switch (rEvent.GetId()) + { + case VclEventId::WindowMove: + case VclEventId::WindowResize: + RequestUpdateChildren(); + break; + + case VclEventId::WindowGetFocus: + case VclEventId::WindowLoseFocus: + mrAccessibleSlideSorter.FireAccessibleEvent( + AccessibleEventId::SELECTION_CHANGED, + Any(), + Any()); + break; + default: + break; + } +} + +IMPL_LINK_NOARG(AccessibleSlideSorterView::Implementation, SelectionChangeListener, LinkParamNone*, void) +{ + if (mnSelectionChangeUserEventId == nullptr) + mnSelectionChangeUserEventId = Application::PostUserEvent( + LINK(this, AccessibleSlideSorterView::Implementation, BroadcastSelectionChange)); +} + +IMPL_LINK_NOARG(AccessibleSlideSorterView::Implementation, BroadcastSelectionChange, void*, void) +{ + mnSelectionChangeUserEventId = nullptr; + mrAccessibleSlideSorter.FireAccessibleEvent( + AccessibleEventId::SELECTION_CHANGED, + Any(), + Any()); +} + +IMPL_LINK_NOARG(AccessibleSlideSorterView::Implementation, FocusChangeListener, LinkParamNone*, void) +{ + sal_Int32 nNewFocusedIndex ( + mrSlideSorter.GetController().GetFocusManager().GetFocusedPageIndex()); + + bool bHasFocus = mrSlideSorter.GetController().GetFocusManager().IsFocusShowing(); + if (!bHasFocus) + nNewFocusedIndex = -1; + + // add a checker whether the focus event is sent out. Only after sent, the mnFocusedIndex should be updated. + bool bSentFocus = false; + if (nNewFocusedIndex == mnFocusedIndex) + return; + + if (mnFocusedIndex >= 0) + { + AccessibleSlideSorterObject* pObject = GetAccessibleChild(mnFocusedIndex); + if (pObject != nullptr) + { + pObject->FireAccessibleEvent( + AccessibleEventId::STATE_CHANGED, + Any(AccessibleStateType::FOCUSED), + Any()); + bSentFocus = true; + } + } + if (nNewFocusedIndex >= 0) + { + AccessibleSlideSorterObject* pObject = GetAccessibleChild(nNewFocusedIndex); + if (pObject != nullptr) + { + pObject->FireAccessibleEvent( + AccessibleEventId::STATE_CHANGED, + Any(), + Any(AccessibleStateType::FOCUSED)); + bSentFocus = true; + } + } + if (bSentFocus) + mnFocusedIndex = nNewFocusedIndex; +} + +IMPL_LINK_NOARG(AccessibleSlideSorterView::Implementation, UpdateChildrenCallback, void*, void) +{ + mnUpdateChildrenUserEventId = nullptr; + UpdateChildren(); +} + +IMPL_LINK_NOARG(AccessibleSlideSorterView::Implementation, VisibilityChangeListener, LinkParamNone*, void) +{ + UpdateChildren(); +} + +} // end of namespace ::accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessibleViewForwarder.cxx b/sd/source/ui/accessibility/AccessibleViewForwarder.cxx new file mode 100644 index 000000000..09225e27f --- /dev/null +++ b/sd/source/ui/accessibility/AccessibleViewForwarder.cxx @@ -0,0 +1,104 @@ +/* -*- 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 <AccessibleViewForwarder.hxx> +#include <svx/svdpntv.hxx> +#include <vcl/window.hxx> +#include <svx/sdrpaintwindow.hxx> +#include <osl/diagnose.h> + +namespace accessibility +{ +/** For the time being, the implementation of this class will not use the + member mrDevice. Instead the device is retrieved from the view + every time it is used. This is necessary because the device has to stay + up-to-date with the current view and the class has to stay compatible. + May change in the future. +*/ + +AccessibleViewForwarder::AccessibleViewForwarder(SdrPaintView* pView, const OutputDevice& rDevice) + : mpView(pView) + , mnWindowId(0) +{ + // Search the output device to determine its id. + for (sal_uInt32 a(0); a < mpView->PaintWindowCount(); a++) + { + SdrPaintWindow* pPaintWindow = mpView->GetPaintWindow(a); + OutputDevice& rOutDev = pPaintWindow->GetOutputDevice(); + + if (&rOutDev == &rDevice) + { + mnWindowId = static_cast<sal_uInt16>(a); + break; + } + } +} + +AccessibleViewForwarder::~AccessibleViewForwarder() +{ + // empty +} + +::tools::Rectangle AccessibleViewForwarder::GetVisibleArea() const +{ + ::tools::Rectangle aVisibleArea; + + if (static_cast<sal_uInt32>(mnWindowId) < mpView->PaintWindowCount()) + { + SdrPaintWindow* pPaintWindow = mpView->GetPaintWindow(static_cast<sal_uInt32>(mnWindowId)); + aVisibleArea = pPaintWindow->GetVisibleArea(); + } + + return aVisibleArea; +} + +/** Transform the given point into pixel coordinates. After the pixel + coordinates of the window origin are added to make the point coordinates + absolute. +*/ +Point AccessibleViewForwarder::LogicToPixel(const Point& rPoint) const +{ + OSL_ASSERT(mpView != nullptr); + if (static_cast<sal_uInt32>(mnWindowId) < mpView->PaintWindowCount()) + { + SdrPaintWindow* pPaintWindow = mpView->GetPaintWindow(static_cast<sal_uInt32>(mnWindowId)); + OutputDevice& rOutDev = pPaintWindow->GetOutputDevice(); + ::tools::Rectangle aBBox(rOutDev.GetOwnerWindow()->GetWindowExtentsRelative(nullptr)); + return rOutDev.LogicToPixel(rPoint) + aBBox.TopLeft(); + } + else + return Point(); +} + +Size AccessibleViewForwarder::LogicToPixel(const Size& rSize) const +{ + OSL_ASSERT(mpView != nullptr); + if (static_cast<sal_uInt32>(mnWindowId) < mpView->PaintWindowCount()) + { + SdrPaintWindow* pPaintWindow = mpView->GetPaintWindow(static_cast<sal_uInt32>(mnWindowId)); + OutputDevice& rOutDev = pPaintWindow->GetOutputDevice(); + return rOutDev.LogicToPixel(rSize); + } + else + return Size(); +} + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/SdShapeTypes.cxx b/sd/source/ui/accessibility/SdShapeTypes.cxx new file mode 100644 index 000000000..7fab0961e --- /dev/null +++ b/sd/source/ui/accessibility/SdShapeTypes.cxx @@ -0,0 +1,132 @@ +/* -*- 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 <svx/ShapeTypeHandler.hxx> +#include <SdShapeTypes.hxx> +#include <AccessiblePresentationShape.hxx> +#include <AccessiblePresentationGraphicShape.hxx> +#include <AccessiblePresentationOLEShape.hxx> + +namespace accessibility { + +static rtl::Reference<AccessibleShape> + CreateSdAccessibleShape ( + const AccessibleShapeInfo& rShapeInfo, + const AccessibleShapeTreeInfo& rShapeTreeInfo, + ShapeTypeId nId) +{ + switch (nId) + { + case PRESENTATION_TITLE: + case PRESENTATION_OUTLINER: + case PRESENTATION_SUBTITLE: + case PRESENTATION_PAGE: + case PRESENTATION_NOTES: + case PRESENTATION_HANDOUT: + case PRESENTATION_HEADER: + case PRESENTATION_FOOTER: + case PRESENTATION_DATETIME: + case PRESENTATION_PAGENUMBER: + return new AccessiblePresentationShape (rShapeInfo, rShapeTreeInfo); + + case PRESENTATION_GRAPHIC_OBJECT: + return new AccessiblePresentationGraphicShape (rShapeInfo, rShapeTreeInfo); + + case PRESENTATION_OLE: + case PRESENTATION_CHART: + case PRESENTATION_TABLE: + return new AccessiblePresentationOLEShape (rShapeInfo, rShapeTreeInfo); + + default: + return new AccessibleShape (rShapeInfo, rShapeTreeInfo); + } +} + +void RegisterImpressShapeTypes() +{ + /** List of shape type descriptors corresponding to the + <type>SdShapeTypes</type> enum. + */ + ShapeTypeDescriptor aSdShapeTypeList[] = { + ShapeTypeDescriptor ( + PRESENTATION_OUTLINER, + "com.sun.star.presentation.OutlinerShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_SUBTITLE, + "com.sun.star.presentation.SubtitleShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_GRAPHIC_OBJECT, + "com.sun.star.presentation.GraphicObjectShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_PAGE, + "com.sun.star.presentation.PageShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_OLE, + "com.sun.star.presentation.OLE2Shape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_CHART, + "com.sun.star.presentation.ChartShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_TABLE, + "com.sun.star.presentation.TableShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_NOTES, + "com.sun.star.presentation.NotesShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_TITLE, + "com.sun.star.presentation.TitleTextShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_HANDOUT, + "com.sun.star.presentation.HandoutShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_HEADER, + "com.sun.star.presentation.HeaderShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_FOOTER, + "com.sun.star.presentation.FooterShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_DATETIME, + "com.sun.star.presentation.DateTimeShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_PAGENUMBER, + "com.sun.star.presentation.SlideNumberShape", + CreateSdAccessibleShape ) + }; + + ShapeTypeHandler::Instance().AddShapeTypeList ( + PRESENTATION_PAGENUMBER - PRESENTATION_OUTLINER + 1, + aSdShapeTypeList); +} + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/animations/CustomAnimationDialog.cxx b/sd/source/ui/animations/CustomAnimationDialog.cxx new file mode 100644 index 000000000..7490a62c5 --- /dev/null +++ b/sd/source/ui/animations/CustomAnimationDialog.cxx @@ -0,0 +1,2090 @@ +/* -*- 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 <config_features.h> + +#include <com/sun/star/presentation/EffectNodeType.hpp> +#include <com/sun/star/animations/Timing.hpp> +#include <com/sun/star/animations/Event.hpp> +#include <com/sun/star/animations/EventTrigger.hpp> +#include <com/sun/star/animations/AnimationFill.hpp> +#include <com/sun/star/presentation/TextAnimationType.hpp> +#include <com/sun/star/animations/ValuePair.hpp> +#include <com/sun/star/awt/FontSlant.hpp> +#include <com/sun/star/awt/FontWeight.hpp> +#include <com/sun/star/awt/FontUnderline.hpp> +#include <com/sun/star/drawing/XDrawPage.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/media/XPlayer.hpp> + +#include <memory> + +#include <comphelper/lok.hxx> +#include <i18nutil/unicode.hxx> +#include <vcl/svapp.hxx> +#include <vcl/stdtext.hxx> +#include <vcl/weld.hxx> +#include <vcl/settings.hxx> + +#include <svtools/ctrltool.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/viewsh.hxx> +#include <tools/debug.hxx> +#include <tools/urlobj.hxx> +#include <tools/diagnose_ex.h> + +#include <editeng/flstitem.hxx> + +#include <svx/colorbox.hxx> +#include <svx/gallery.hxx> + +#include <editeng/editids.hrc> +#include <sdresid.hxx> + +#include "CustomAnimationDialog.hxx" +#include <CustomAnimationPane.hxx> +#include "STLPropertySet.hxx" +#include <CustomAnimationPreset.hxx> + +#include <avmedia/mediawindow.hxx> + +#include <filedlg.hxx> +#include <strings.hrc> +#include <helpids.h> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::animations; +using namespace ::com::sun::star::presentation; + +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::drawing::XShape; +using ::com::sun::star::drawing::XDrawPage; +using ::com::sun::star::beans::XPropertySet; + +namespace sd { + +SdPropertySubControl::SdPropertySubControl(weld::Container* pParent) + : mxBuilder(Application::CreateBuilder(pParent, "modules/simpress/ui/customanimationfragment.ui", + false, reinterpret_cast<sal_uInt64>(SfxViewShell::Current()))) + , mxContainer(mxBuilder->weld_container("EffectFragment")) + , mpParent(pParent) +{ +} + +SdPropertySubControl::~SdPropertySubControl() +{ + mpParent->move(mxContainer.get(), nullptr); +} + +namespace { + +class SdPresetPropertyBox : public SdPropertySubControl +{ +public: + SdPresetPropertyBox(weld::Label* pLabel, weld::Container* pParent, const Any& rValue, const OUString& aPresetId, const Link<LinkParamNone*,void>& rModifyHdl); + + virtual Any getValue() override; + virtual void setValue( const Any& rValue, const OUString& rPresetId ) override; + +private: + std::vector<OUString> maPropertyValues; + Link<LinkParamNone*,void> maModifyLink; + std::unique_ptr<weld::ComboBox> mxControl; + + DECL_LINK(OnSelect, weld::ComboBox&, void); +}; + +} + +SdPresetPropertyBox::SdPresetPropertyBox(weld::Label* pLabel, weld::Container* pParent, const Any& rValue, const OUString& aPresetId, const Link<LinkParamNone*,void>& rModifyHdl) + : SdPropertySubControl(pParent) + , maModifyLink(rModifyHdl) + , mxControl(mxBuilder->weld_combo_box("combo")) +{ + mxControl->connect_changed(LINK(this, SdPresetPropertyBox, OnSelect)); + mxControl->set_help_id(HID_SD_CUSTOMANIMATIONPANE_PRESETPROPERTYBOX); + mxControl->show(); + pLabel->set_mnemonic_widget(mxControl.get()); + setValue(rValue, aPresetId); +} + +IMPL_LINK_NOARG(SdPresetPropertyBox, OnSelect, weld::ComboBox&, void) +{ + maModifyLink.Call(nullptr); +} + +void SdPresetPropertyBox::setValue( const Any& rValue, const OUString& rPresetId ) +{ + if (!mxControl) + return; + + mxControl->freeze(); + mxControl->clear(); + maPropertyValues.clear(); + int nPos = -1; + + const CustomAnimationPresets& rPresets = CustomAnimationPresets::getCustomAnimationPresets(); + CustomAnimationPresetPtr pDescriptor = rPresets.getEffectDescriptor( rPresetId ); + if( pDescriptor ) + { + + OUString aPropertyValue; + rValue >>= aPropertyValue; + + std::vector<OUString> aSubTypes( pDescriptor->getSubTypes() ); + + mxControl->set_sensitive(!aSubTypes.empty()); + + for( const auto& aSubType : aSubTypes ) + { + mxControl->append_text(rPresets.getUINameForProperty(aSubType)); + maPropertyValues.push_back(aSubType); + if (aSubType == aPropertyValue) + nPos = maPropertyValues.size() - 1; + } + } + else + { + mxControl->set_sensitive(false); + } + mxControl->thaw(); + if (nPos != -1) + mxControl->set_active(nPos); +} + +Any SdPresetPropertyBox::getValue() +{ + const int nIndex = mxControl->get_active(); + if (nIndex == -1) + return Any(); + return Any(maPropertyValues[nIndex]); +} + +namespace { + +class SdColorPropertyBox : public SdPropertySubControl +{ +public: + SdColorPropertyBox(weld::Label* pLabel, weld::Container* pParent, weld::Window* pTopLevel, const Any& rValue, const Link<LinkParamNone*,void>& rModifyHdl); + + virtual Any getValue() override; + virtual void setValue( const Any& rValue, const OUString& rPresetId ) override; + +private: + Link<LinkParamNone*,void> maModifyLink; + std::unique_ptr<ColorListBox> mxControl; + + DECL_LINK(OnSelect, ColorListBox&, void); +}; + +} + +SdColorPropertyBox::SdColorPropertyBox(weld::Label* pLabel, weld::Container* pParent, weld::Window* pTopLevel, const Any& rValue, const Link<LinkParamNone*,void>& rModifyHdl) + : SdPropertySubControl(pParent) + , maModifyLink(rModifyHdl) + , mxControl(new ColorListBox(mxBuilder->weld_menu_button("color"), [pTopLevel]{ return pTopLevel; })) +{ + mxControl->SetSelectHdl(LINK(this, SdColorPropertyBox, OnSelect)); + mxControl->set_help_id(HID_SD_CUSTOMANIMATIONPANE_COLORPROPERTYBOX); + pLabel->set_mnemonic_widget(&mxControl->get_widget()); + mxControl->show(); + + Color nColor; + rValue >>= nColor; + mxControl->SelectEntry(nColor); +} + +IMPL_LINK_NOARG(SdColorPropertyBox, OnSelect, ColorListBox&, void) +{ + maModifyLink.Call(nullptr); +} + +void SdColorPropertyBox::setValue( const Any& rValue, const OUString& ) +{ + if (mxControl) + { + Color nColor; + rValue >>= nColor; + + mxControl->SetNoSelection(); + mxControl->SelectEntry(nColor); + } +} + +Any SdColorPropertyBox::getValue() +{ + return Any(sal_Int32(mxControl->GetSelectEntryColor().GetRGBColor())); +} + +namespace { + +class SdFontPropertyBox : public SdPropertySubControl +{ +public: + SdFontPropertyBox(weld::Label* pLabel, weld::Container* pParent, const Any& rValue, const Link<LinkParamNone*,void>& rModifyHdl); + + virtual Any getValue() override; + virtual void setValue(const Any& rValue, const OUString& rPresetId) override; + +private: + Link<LinkParamNone*,void> maModifyHdl; + std::unique_ptr<weld::ComboBox> mxControl; + + DECL_LINK(ControlSelectHdl, weld::ComboBox&, void); +}; + +} + +SdFontPropertyBox::SdFontPropertyBox(weld::Label* pLabel, weld::Container* pParent, const Any& rValue, const Link<LinkParamNone*,void>& rModifyHdl) + : SdPropertySubControl(pParent) + , maModifyHdl(rModifyHdl) + , mxControl(mxBuilder->weld_combo_box("fontname")) +{ + mxControl->connect_changed(LINK(this, SdFontPropertyBox, ControlSelectHdl)); + mxControl->set_help_id(HID_SD_CUSTOMANIMATIONPANE_FONTPROPERTYBOX); + mxControl->show(); + pLabel->set_mnemonic_widget(mxControl.get()); + + const FontList* pFontList = nullptr; + bool bMustDelete = false; + + if (SfxObjectShell* pDocSh = SfxObjectShell::Current()) + { + auto pItem = pDocSh->GetItem( SID_ATTR_CHAR_FONTLIST ); + if (pItem) + pFontList = static_cast<const SvxFontListItem*>(pItem)->GetFontList(); + } + + if (!pFontList) + { + pFontList = new FontList(Application::GetDefaultDevice(), nullptr); + bMustDelete = true; + } + + mxControl->freeze(); + + sal_uInt16 nFontCount = pFontList->GetFontNameCount(); + for (sal_uInt16 i = 0; i < nFontCount; ++i) + { + const FontMetric& rFontMetric = pFontList->GetFontName(i); + mxControl->append_text(rFontMetric.GetFamilyName()); + } + + mxControl->thaw(); + + if( bMustDelete ) + delete pFontList; + + setValue( rValue, OUString() ); +} + +IMPL_LINK_NOARG(SdFontPropertyBox, ControlSelectHdl, weld::ComboBox&, void) +{ + maModifyHdl.Call(nullptr); +} + +void SdFontPropertyBox::setValue( const Any& rValue, const OUString& ) +{ + if (mxControl) + { + OUString aFontName; + rValue >>= aFontName; + mxControl->set_entry_text(aFontName); + } +} + +Any SdFontPropertyBox::getValue() +{ + OUString aFontName(mxControl->get_active_text()); + return Any(aFontName); +} + +namespace { + +class SdCharHeightPropertyBox : public SdPropertySubControl +{ +public: + SdCharHeightPropertyBox(weld::Label* pLabel, weld::Container* pParent, const Any& rValue, const Link<LinkParamNone*,void>& rModifyHdl); + + virtual Any getValue() override; + virtual void setValue( const Any& rValue, const OUString& ) override; + + DECL_LINK(implMenuSelectHdl, const OString& rIdent, void); + +private: + Link<LinkParamNone*,void> maModifyHdl; + std::unique_ptr<weld::MetricSpinButton> mxMetric; + std::unique_ptr<weld::MenuButton> mxControl; + + DECL_LINK(EditModifyHdl, weld::MetricSpinButton&, void); +}; + +} + +SdCharHeightPropertyBox::SdCharHeightPropertyBox(weld::Label* pLabel, weld::Container* pParent, const Any& rValue, const Link<LinkParamNone*,void>& rModifyHdl) + : SdPropertySubControl(pParent) + , maModifyHdl(rModifyHdl) + , mxMetric(mxBuilder->weld_metric_spin_button("fontsize", FieldUnit::PERCENT)) + , mxControl(mxBuilder->weld_menu_button("fontsizemenu")) +{ + mxMetric->connect_value_changed(LINK(this, SdCharHeightPropertyBox, EditModifyHdl)); + mxMetric->set_help_id(HID_SD_CUSTOMANIMATIONPANE_CHARHEIGHTPROPERTYBOX); + mxMetric->show(); + pLabel->set_mnemonic_widget(&mxMetric->get_widget()); + + mxControl->connect_selected(LINK(this, SdCharHeightPropertyBox, implMenuSelectHdl)); + mxControl->set_help_id(HID_SD_CUSTOMANIMATIONPANE_CHARHEIGHTPROPERTYBOX); + mxControl->show(); + + setValue(rValue, OUString()); +} + +IMPL_LINK_NOARG(SdCharHeightPropertyBox, EditModifyHdl, weld::MetricSpinButton&, void) +{ + maModifyHdl.Call(nullptr); +} + +IMPL_LINK(SdCharHeightPropertyBox, implMenuSelectHdl, const OString&, rIdent, void) +{ + sal_Int32 nValue = rIdent.toInt32(); + mxMetric->set_value(nValue, FieldUnit::PERCENT); + EditModifyHdl(*mxMetric); +} + +void SdCharHeightPropertyBox::setValue( const Any& rValue, const OUString& ) +{ + if (mxMetric) + { + double fValue = 0.0; + rValue >>= fValue; + mxMetric->set_value(static_cast<::tools::Long>(fValue * 100.0), FieldUnit::PERCENT); + } +} + +Any SdCharHeightPropertyBox::getValue() +{ + return Any(static_cast<double>(mxMetric->get_value(FieldUnit::PERCENT)) / 100.0); +} + +namespace { + +class SdTransparencyPropertyBox : public SdPropertySubControl +{ +public: + SdTransparencyPropertyBox(weld::Label* pLabel, weld::Container* pParent, const Any& rValue, const Link<LinkParamNone*,void>& rModifyHdl); + + virtual Any getValue() override; + virtual void setValue( const Any& rValue, const OUString& rPresetId ) override; + + DECL_LINK(implMenuSelectHdl, const OString&, void); + DECL_LINK(implModifyHdl, weld::MetricSpinButton&, void); + + void updateMenu(); + +private: + Link<LinkParamNone*,void> maModifyHdl; + + std::unique_ptr<weld::MetricSpinButton> mxMetric; + std::unique_ptr<weld::MenuButton> mxControl; +}; + +} + +SdTransparencyPropertyBox::SdTransparencyPropertyBox(weld::Label* pLabel, weld::Container* pParent, const Any& rValue, const Link<LinkParamNone*,void>& rModifyHdl) + : SdPropertySubControl(pParent) + , maModifyHdl(rModifyHdl) + , mxMetric(mxBuilder->weld_metric_spin_button("transparent", FieldUnit::PERCENT)) + , mxControl(mxBuilder->weld_menu_button("transparentmenu")) +{ + for (sal_Int32 i = 25; i < 101; i += 25) + { + OUString aStr(unicode::formatPercent(i, + Application::GetSettings().GetUILanguageTag())); + mxControl->append_item_check(OUString::number(i), aStr); + } + + mxControl->connect_selected(LINK(this, SdTransparencyPropertyBox, implMenuSelectHdl)); + mxControl->set_help_id(HID_SD_CUSTOMANIMATIONPANE_TRANSPARENCYPROPERTYBOX); + mxControl->show(); + + mxMetric->connect_value_changed(LINK(this, SdTransparencyPropertyBox, implModifyHdl)); + mxMetric->set_help_id(HID_SD_CUSTOMANIMATIONPANE_TRANSPARENCYPROPERTYBOX); + mxMetric->show(); + pLabel->set_mnemonic_widget(&mxMetric->get_widget()); + + setValue(rValue, OUString()); +} + +void SdTransparencyPropertyBox::updateMenu() +{ + sal_Int64 nValue = mxMetric->get_value(FieldUnit::PERCENT); + for (sal_uInt16 i = 25; i < 101; i += 25) + mxControl->set_item_active(OString::number(i), nValue == i); +} + +IMPL_LINK_NOARG(SdTransparencyPropertyBox, implModifyHdl, weld::MetricSpinButton&, void) +{ + updateMenu(); + maModifyHdl.Call(nullptr); +} + +IMPL_LINK(SdTransparencyPropertyBox, implMenuSelectHdl, const OString&, rIdent, void) +{ + auto nValue = rIdent.toInt32(); + if (nValue != mxMetric->get_value(FieldUnit::PERCENT)) + { + mxMetric->set_value(nValue, FieldUnit::PERCENT); + implModifyHdl(*mxMetric); + } +} + +void SdTransparencyPropertyBox::setValue(const Any& rValue, const OUString&) +{ + if (mxMetric) + { + double fValue = 0.0; + rValue >>= fValue; + ::tools::Long nValue = static_cast<::tools::Long>(fValue * 100); + mxMetric->set_value(nValue, FieldUnit::PERCENT); + updateMenu(); + } +} + +Any SdTransparencyPropertyBox::getValue() +{ + return Any(static_cast<double>(mxMetric->get_value(FieldUnit::PERCENT)) / 100.0); +} + +namespace { + +class SdRotationPropertyBox : public SdPropertySubControl +{ +public: + SdRotationPropertyBox(weld::Label* pLabel, weld::Container* pParent, const Any& rValue, const Link<LinkParamNone*,void>& rModifyHdl); + + virtual Any getValue() override; + virtual void setValue( const Any& rValue, const OUString& ) override; + + DECL_LINK(implMenuSelectHdl, const OString&, void); + DECL_LINK(implModifyHdl, weld::MetricSpinButton&, void); + + void updateMenu(); + +private: + Link<LinkParamNone*,void> maModifyHdl; + + std::unique_ptr<weld::MetricSpinButton> mxMetric; + std::unique_ptr<weld::MenuButton> mxControl; +}; + +} + +SdRotationPropertyBox::SdRotationPropertyBox(weld::Label* pLabel, weld::Container* pParent, const Any& rValue, const Link<LinkParamNone*,void>& rModifyHdl) + : SdPropertySubControl(pParent) + , maModifyHdl(rModifyHdl) + , mxMetric(mxBuilder->weld_metric_spin_button("rotate", FieldUnit::DEGREE)) + , mxControl(mxBuilder->weld_menu_button("rotatemenu")) +{ + mxMetric->connect_value_changed(LINK( this, SdRotationPropertyBox, implModifyHdl)); + mxMetric->set_help_id(HID_SD_CUSTOMANIMATIONPANE_ROTATIONPROPERTYBOX); + mxMetric->show(); + pLabel->set_mnemonic_widget(&mxMetric->get_widget()); + + mxControl->connect_selected(LINK(this, SdRotationPropertyBox, implMenuSelectHdl)); + mxControl->set_help_id(HID_SD_CUSTOMANIMATIONPANE_ROTATIONPROPERTYBOX); + mxControl->show(); + + setValue(rValue, OUString()); +} + +void SdRotationPropertyBox::updateMenu() +{ + sal_Int64 nValue = mxMetric->get_value(FieldUnit::DEGREE); + bool bDirection = nValue >= 0; + nValue = (nValue < 0 ? -nValue : nValue); + + mxControl->set_item_active("90", nValue == 90); + mxControl->set_item_active("180", nValue == 180); + mxControl->set_item_active("360", nValue == 360); + mxControl->set_item_active("720", nValue == 720); + + mxControl->set_item_active("closewise", bDirection); + mxControl->set_item_active("counterclock", !bDirection); +} + +IMPL_LINK_NOARG(SdRotationPropertyBox, implModifyHdl, weld::MetricSpinButton&, void) +{ + updateMenu(); + maModifyHdl.Call(nullptr); +} + +IMPL_LINK(SdRotationPropertyBox, implMenuSelectHdl, const OString&, rIdent, void) +{ + auto nValue = mxMetric->get_value(FieldUnit::DEGREE); + bool bDirection = nValue >= 0; + nValue = (nValue < 0 ? -nValue : nValue); + + if (rIdent == "clockwise") + bDirection = true; + else if (rIdent == "counterclock") + bDirection = false; + else + nValue = rIdent.toInt32(); + + if( !bDirection ) + nValue = -nValue; + + if (nValue != mxMetric->get_value(FieldUnit::DEGREE)) + { + mxMetric->set_value(nValue, FieldUnit::DEGREE); + implModifyHdl(*mxMetric); + } +} + +void SdRotationPropertyBox::setValue( const Any& rValue, const OUString& ) +{ + if (mxMetric) + { + double fValue = 0.0; + rValue >>= fValue; + ::tools::Long nValue = static_cast<::tools::Long>(fValue); + mxMetric->set_value(nValue, FieldUnit::DEGREE); + updateMenu(); + } +} + +Any SdRotationPropertyBox::getValue() +{ + return Any(static_cast<double>(mxMetric->get_value(FieldUnit::DEGREE))); +} + +namespace { + +class SdScalePropertyBox : public SdPropertySubControl +{ +public: + SdScalePropertyBox(weld::Label* pLabel, weld::Container* pParent, const Any& rValue, const Link<LinkParamNone*,void>& rModifyHdl); + + virtual Any getValue() override; + virtual void setValue( const Any& rValue, const OUString& ) override; + + DECL_LINK(implMenuSelectHdl, const OString&, void); + DECL_LINK(implModifyHdl, weld::MetricSpinButton&, void); + + void updateMenu(); + +private: + Link<LinkParamNone*,void> maModifyHdl; + int mnDirection; + + std::unique_ptr<weld::MetricSpinButton> mxMetric; + std::unique_ptr<weld::MenuButton> mxControl; +}; + +} + +SdScalePropertyBox::SdScalePropertyBox(weld::Label* pLabel, weld::Container* pParent, const Any& rValue, const Link<LinkParamNone*,void>& rModifyHdl) + : SdPropertySubControl(pParent) + , maModifyHdl( rModifyHdl ) + , mxMetric(mxBuilder->weld_metric_spin_button("scale", FieldUnit::PERCENT)) + , mxControl(mxBuilder->weld_menu_button("scalemenu")) +{ + mxControl->connect_selected(LINK(this, SdScalePropertyBox, implMenuSelectHdl)); + mxControl->set_help_id(HID_SD_CUSTOMANIMATIONPANE_SCALEPROPERTYBOX); + mxControl->show(); + + mxMetric->connect_value_changed(LINK(this, SdScalePropertyBox, implModifyHdl)); + mxMetric->set_help_id(HID_SD_CUSTOMANIMATIONPANE_SCALEPROPERTYBOX); + mxMetric->show(); + pLabel->set_mnemonic_widget(&mxMetric->get_widget()); + + setValue(rValue, OUString()); +} + +void SdScalePropertyBox::updateMenu() +{ + auto nValue = mxMetric->get_value(FieldUnit::PERCENT); + + mxControl->set_item_active("25scale", nValue == 25); + mxControl->set_item_active("50scale", nValue == 50); + mxControl->set_item_active("150scale", nValue == 150); + mxControl->set_item_active("400scale", nValue == 400); + + mxControl->set_item_active("hori", mnDirection == 1); + mxControl->set_item_active("vert", mnDirection == 2); + mxControl->set_item_active("both", mnDirection == 3); +} + +IMPL_LINK_NOARG(SdScalePropertyBox, implModifyHdl, weld::MetricSpinButton&, void) +{ + updateMenu(); + maModifyHdl.Call(nullptr); +} + +IMPL_LINK(SdScalePropertyBox, implMenuSelectHdl, const OString&, rIdent, void) +{ + auto nValue = mxMetric->get_value(FieldUnit::PERCENT); + + int nDirection = mnDirection; + + if (rIdent == "hori") + nDirection = 1; + else if (rIdent == "vert") + nDirection = 2; + else if (rIdent == "both") + nDirection = 3; + else + nValue = rIdent.toInt32(); // Getting here indicates a UI bug and should be handled better + + bool bModified = false; + + if( nDirection != mnDirection ) + { + mnDirection = nDirection; + bModified = true; + } + + if (nValue != mxMetric->get_value(FieldUnit::PERCENT)) + { + mxMetric->set_value(nValue, FieldUnit::PERCENT); + bModified = true; + } + + if(bModified) + { + implModifyHdl(*mxMetric); + updateMenu(); + } +} + +void SdScalePropertyBox::setValue(const Any& rValue, const OUString&) +{ + if (!mxMetric) + return; + + ValuePair aValues; + rValue >>= aValues; + + double fValue1 = 0.0; + double fValue2 = 0.0; + + aValues.First >>= fValue1; + aValues.Second >>= fValue2; + + // 'Size' drop down menu set by mnDirection when loading Grow and Shrink Animation + // Shouldn't compare a float directly to zero... should be fixed with delta epsilon compare + // Might be better to just have a flag in the content.xml for this + if( (fValue1 == 0.0) && (fValue2 == 0.0) ) + mnDirection = 3; // assume 'Both' scaling option when both are zero + else if( (fValue1 != 0.0) && (fValue2 == 0.0) ) + mnDirection = 1; + else if( (fValue1 == 0.0) && (fValue2 != 0.0) ) + mnDirection = 2; + else + mnDirection = 3; + + // Grow and Shrink Animation is a relative change with value stored in content.xml under tag + // smil:by=*,* + // An offset of 1 must be added to properly translate from content.xml to UI value displayed + // e.g. if in content.xml smil:by=0.5,0.5 then 1 + (0.5,0.5) = (1.5,1.5) => grow by 150% of the + // size horizontal and vertical + // e.g. if in content.xml smil:by=-0.5,-0.5 then 1 + (-0.5,-0.5) = (0.5,0.5) => shrink by 50% + // of the size horizontal and vertical + fValue1 += 1; + fValue2 += 1; + + // Determine value from file for UI 'Size' field based on determined mnDirection + ::tools::Long nValue; + if( mnDirection == 1 ) + nValue = static_cast<::tools::Long>(fValue1 * 100.0); + else if( mnDirection == 2 ) + nValue = static_cast<::tools::Long>(fValue2 * 100.0); + else if( mnDirection == 3 ){ + if (fValue1 >= fValue2) + nValue = static_cast<::tools::Long>(fValue1 * 100.0); + else + nValue = static_cast<::tools::Long>(fValue2 * 100.0); + } + else + nValue = static_cast<::tools::Long>(100.0); // default to 100% in UI if something goes wrong + + mxMetric->set_value(nValue, FieldUnit::PERCENT); + updateMenu(); +} + +Any SdScalePropertyBox::getValue() +{ + double fValue1 = static_cast<double>(mxMetric->get_value(FieldUnit::PERCENT)) / 100.0; + + // Grow and Shrink Animation is a relative change with value stored in content.xml under tag + // smil:by=*,* + // An offset of 1 must be subtracted to properly translate UI value displayed and save to + // content.xml + // e.g. if UI value is 150% then 1.5 - 1 = 0.5 and is set to smil:by=0.5,0.5 in content.xml + // e.g. if UI value is 50% then 0.5 - 1 = -0.5 and is set to smil:by=-0.5,-0.5 in content.xml + fValue1 -= 1; + + double fValue2 = fValue1; + + // mnDirection set by 'Size' drop down menu and used to zero out either horizontal or vertical + // scaling depending on what option is selected + if( mnDirection == 1 ) + fValue2 = 0.0; + else if( mnDirection == 2 ) + fValue1 = 0.0; + + ValuePair aValues; + aValues.First <<= fValue1; + aValues.Second <<= fValue2; + + return Any( aValues ); +} + +namespace { + +class SdFontStylePropertyBox : public SdPropertySubControl +{ +public: + SdFontStylePropertyBox(weld::Label* pLabel, weld::Container* pParent, const Any& rValue, const Link<LinkParamNone*,void>& rModifyHdl); + + virtual Any getValue() override; + virtual void setValue( const Any& rValue, const OUString& ) override; + + DECL_LINK(implMenuSelectHdl, const OString&, void); + + void update(); + +private: + float mfFontWeight; + awt::FontSlant meFontSlant; + sal_Int16 mnFontUnderline; + Link<LinkParamNone*,void> maModifyHdl; + + std::unique_ptr<weld::Entry> mxEdit; + std::unique_ptr<weld::MenuButton> mxControl; +}; + +} + +SdFontStylePropertyBox::SdFontStylePropertyBox(weld::Label* pLabel, weld::Container* pParent, const Any& rValue, const Link<LinkParamNone*,void>& rModifyHdl ) + : SdPropertySubControl(pParent) + , maModifyHdl( rModifyHdl ) + , mxEdit(mxBuilder->weld_entry("entry")) + , mxControl(mxBuilder->weld_menu_button("entrymenu")) +{ + mxEdit->set_text(SdResId(STR_CUSTOMANIMATION_SAMPLE)); + mxEdit->set_help_id(HID_SD_CUSTOMANIMATIONPANE_FONTSTYLEPROPERTYBOX); + pLabel->set_mnemonic_widget(mxEdit.get()); + mxEdit->show(); + + mxControl->connect_selected(LINK(this, SdFontStylePropertyBox, implMenuSelectHdl)); + mxControl->set_help_id(HID_SD_CUSTOMANIMATIONPANE_FONTSTYLEPROPERTYBOX); + mxControl->show(); + + setValue(rValue, OUString()); +} + +void SdFontStylePropertyBox::update() +{ + // update menu + mxControl->set_item_active("bold", mfFontWeight == awt::FontWeight::BOLD); + mxControl->set_item_active("italic", meFontSlant == awt::FontSlant_ITALIC); + mxControl->set_item_active("underline", mnFontUnderline != awt::FontUnderline::NONE ); + + // update sample edit + vcl::Font aFont(mxEdit->get_font()); + aFont.SetWeight(mfFontWeight == awt::FontWeight::BOLD ? WEIGHT_BOLD : WEIGHT_NORMAL); + aFont.SetItalic(meFontSlant == awt::FontSlant_ITALIC ? ITALIC_NORMAL : ITALIC_NONE); + aFont.SetUnderline(mnFontUnderline == awt::FontUnderline::NONE ? LINESTYLE_NONE : LINESTYLE_SINGLE); + mxEdit->set_font(aFont); +} + +IMPL_LINK(SdFontStylePropertyBox, implMenuSelectHdl, const OString&, rIdent, void) +{ + if (rIdent == "bold") + { + if( mfFontWeight == awt::FontWeight::BOLD ) + mfFontWeight = awt::FontWeight::NORMAL; + else + mfFontWeight = awt::FontWeight::BOLD; + } + else if (rIdent == "italic") + { + if( meFontSlant == awt::FontSlant_ITALIC ) + meFontSlant = awt::FontSlant_NONE; + else + meFontSlant = awt::FontSlant_ITALIC; + } + else if (rIdent == "underline") + { + if( mnFontUnderline == awt::FontUnderline::SINGLE ) + mnFontUnderline = awt::FontUnderline::NONE; + else + mnFontUnderline = awt::FontUnderline::SINGLE; + } + + update(); + maModifyHdl.Call(nullptr); +} + +void SdFontStylePropertyBox::setValue( const Any& rValue, const OUString& ) +{ + Sequence<Any> aValues; + rValue >>= aValues; + + aValues[0] >>= mfFontWeight; + aValues[1] >>= meFontSlant; + aValues[2] >>= mnFontUnderline; + + update(); +} + +Any SdFontStylePropertyBox::getValue() +{ + Sequence<Any> aValues{ Any(mfFontWeight), Any(meFontSlant), Any(mnFontUnderline) }; + return Any( aValues ); +} + +class CustomAnimationEffectTabPage +{ +public: + CustomAnimationEffectTabPage(weld::Container* pParent, weld::Window* pDialog, const STLPropertySet* pSet); + + void update( STLPropertySet* pSet ); + DECL_LINK(implSelectHdl, weld::ComboBox&, void); + DECL_LINK(implClickHdl, weld::Button&, void); + void implHdl(const weld::Widget*); + +private: + void updateControlStates(); + void fillSoundListBox(); + void clearSoundListBox(); + sal_Int32 getSoundObject( std::u16string_view rStr ); + void openSoundFileDialog(); + void onSoundPreview(); + weld::Window* GetFrameWeld() const { return mpDialog; } + +private: + ::std::vector< OUString > maSoundList; + bool mbHasText; + const STLPropertySet* mpSet; + css::uno::Reference<css::media::XPlayer> mxPlayer; + + weld::Window* mpDialog; + std::unique_ptr<weld::Builder> mxBuilder; + std::unique_ptr<weld::Container> mxContainer; + std::unique_ptr<weld::Widget> mxSettings; + std::unique_ptr<weld::Label> mxFTProperty1; + std::unique_ptr<weld::Container> mxPlaceholderBox; + std::unique_ptr<weld::CheckButton> mxCBSmoothStart; + std::unique_ptr<weld::CheckButton> mxCBSmoothEnd; + std::unique_ptr<weld::Label> mxFTSound; + std::unique_ptr<weld::ComboBox> mxLBSound; + std::unique_ptr<weld::Button> mxPBSoundPreview; + std::unique_ptr<weld::Label> mxFTAfterEffect; + std::unique_ptr<weld::ComboBox> mxLBAfterEffect; + std::unique_ptr<weld::Label> mxFTDimColor; + std::unique_ptr<ColorListBox> mxCLBDimColor; + std::unique_ptr<weld::Label> mxFTTextAnim; + std::unique_ptr<weld::ComboBox> mxLBTextAnim; + std::unique_ptr<weld::MetricSpinButton> mxMFTextDelay; + std::unique_ptr<weld::Label> mxFTTextDelay; + std::unique_ptr<SdPropertySubControl> mxLBSubControl; +}; + +CustomAnimationEffectTabPage::CustomAnimationEffectTabPage(weld::Container* pParent, weld::Window* pDialog, const STLPropertySet* pSet) + : mbHasText(false) + , mpSet(pSet) + , mpDialog(pDialog) + , mxBuilder(Application::CreateBuilder(pParent, "modules/simpress/ui/customanimationeffecttab.ui")) + , mxContainer(mxBuilder->weld_container("EffectTab")) + , mxSettings(mxBuilder->weld_widget("settings")) + , mxFTProperty1(mxBuilder->weld_label("prop_label1")) + , mxPlaceholderBox(mxBuilder->weld_container("placeholder")) + , mxCBSmoothStart(mxBuilder->weld_check_button("smooth_start")) + , mxCBSmoothEnd(mxBuilder->weld_check_button("smooth_end")) + , mxFTSound(mxBuilder->weld_label("sound_label")) + , mxLBSound(mxBuilder->weld_combo_box("sound_list")) + , mxPBSoundPreview(mxBuilder->weld_button("sound_preview")) + , mxFTAfterEffect(mxBuilder->weld_label("aeffect_label")) + , mxLBAfterEffect(mxBuilder->weld_combo_box("aeffect_list")) + , mxFTDimColor(mxBuilder->weld_label("dim_color_label")) + , mxCLBDimColor(new ColorListBox(mxBuilder->weld_menu_button("dim_color_list"), [pDialog]{ return pDialog; })) + , mxFTTextAnim(mxBuilder->weld_label("text_animation_label")) + , mxLBTextAnim(mxBuilder->weld_combo_box("text_animation_list")) + , mxMFTextDelay(mxBuilder->weld_metric_spin_button("text_delay", FieldUnit::PERCENT)) + , mxFTTextDelay(mxBuilder->weld_label("text_delay_label")) +{ + mxCLBDimColor->SelectEntry(COL_BLACK); + + // fill the soundbox + fillSoundListBox(); + + mxLBSound->connect_changed(LINK(this, CustomAnimationEffectTabPage, implSelectHdl)); + mxPBSoundPreview->connect_clicked(LINK(this, CustomAnimationEffectTabPage, implClickHdl)); + + // only show settings if all selected effects have the same preset-id + if( pSet->getPropertyState( nHandlePresetId ) != STLPropertyState::Ambiguous ) + { + OUString aPresetId; + pSet->getPropertyValue( nHandlePresetId ) >>= aPresetId; + + // property 1 + + if( pSet->getPropertyState( nHandleProperty1Type ) != STLPropertyState::Ambiguous ) + { + sal_Int32 nType = 0; + pSet->getPropertyValue( nHandleProperty1Type ) >>= nType; + + if( nType != nPropertyTypeNone ) + { + // set ui name for property at fixed text + OUString aPropertyName( getPropertyName( nType ) ); + + if( !aPropertyName.isEmpty() ) + { + mxSettings->show(); + mxFTProperty1->set_label(aPropertyName); + } + + // get property value + const Any aValue( pSet->getPropertyValue( nHandleProperty1Value ) ); + + // create property sub control + mxLBSubControl = SdPropertySubControl::create(nType, mxFTProperty1.get(), mxPlaceholderBox.get(), mpDialog, aValue, aPresetId, Link<LinkParamNone*,void>()); + } + } + + mxFTProperty1->set_sensitive(mxPlaceholderBox->get_sensitive()); + + // accelerate & decelerate + + if( pSet->getPropertyState( nHandleAccelerate ) == STLPropertyState::Direct ) + { + mxCBSmoothStart->show(); + mxCBSmoothEnd->show(); + + double fTemp = 0.0; + pSet->getPropertyValue( nHandleAccelerate ) >>= fTemp; + mxCBSmoothStart->set_active( fTemp > 0.0 ); + + pSet->getPropertyValue( nHandleDecelerate ) >>= fTemp; + mxCBSmoothEnd->set_active( fTemp > 0.0 ); + } + } + + // init after effect controls + + mxLBAfterEffect->connect_changed(LINK(this, CustomAnimationEffectTabPage, implSelectHdl)); + mxLBTextAnim->connect_changed(LINK(this, CustomAnimationEffectTabPage, implSelectHdl)); + + if( (pSet->getPropertyState( nHandleHasAfterEffect ) != STLPropertyState::Ambiguous) && + (pSet->getPropertyState( nHandleAfterEffectOnNextEffect ) != STLPropertyState::Ambiguous) && + (pSet->getPropertyState( nHandleDimColor ) != STLPropertyState::Ambiguous)) + { + bool bHasAfterEffect = false; + pSet->getPropertyValue( nHandleHasAfterEffect ) >>= bHasAfterEffect; + + sal_Int32 nPos = 0; + if( bHasAfterEffect ) + { + nPos++; + + bool bAfterEffectOnNextClick = false; + pSet->getPropertyValue( nHandleAfterEffectOnNextEffect ) >>= bAfterEffectOnNextClick; + Any aDimColor( pSet->getPropertyValue( nHandleDimColor ) ); + + if( aDimColor.hasValue() ) + { + Color aColor; + aDimColor >>= aColor; + mxCLBDimColor->SelectEntry(aColor); + } + else + { + nPos++; + if( bAfterEffectOnNextClick ) + nPos++; + } + } + + mxLBAfterEffect->set_active(nPos); + } + + if( pSet->getPropertyState( nHandleHasText ) != STLPropertyState::Ambiguous ) + pSet->getPropertyValue( nHandleHasText ) >>= mbHasText; + + if( mbHasText ) + { + if( pSet->getPropertyState( nHandleIterateType ) != STLPropertyState::Ambiguous) + { + int nPos = -1; + + sal_Int32 nIterateType = 0; + pSet->getPropertyValue( nHandleIterateType ) >>= nIterateType; + switch( nIterateType ) + { + case TextAnimationType::BY_PARAGRAPH: nPos = 0; break; + case TextAnimationType::BY_WORD: nPos = 1; break; + case TextAnimationType::BY_LETTER: nPos = 2; break; + } + + mxLBTextAnim->set_active(nPos); + } + + if( pSet->getPropertyState( nHandleIterateInterval ) != STLPropertyState::Default ) + { + double fIterateInterval = 0.0; + pSet->getPropertyValue( nHandleIterateInterval ) >>= fIterateInterval; + mxMFTextDelay->set_value(static_cast<::tools::Long>(fIterateInterval*10), FieldUnit::NONE); + } + } + else + { + mxFTTextAnim->set_sensitive(false); + mxLBTextAnim->set_sensitive(false); + mxMFTextDelay->set_sensitive(false); + mxFTTextDelay->set_sensitive(false); + + } + + if( pSet->getPropertyState( nHandleSoundURL ) != STLPropertyState::Ambiguous ) + { + sal_Int32 nPos = 0; + + const Any aValue( pSet->getPropertyValue( nHandleSoundURL ) ); + + if( aValue.getValueType() == ::cppu::UnoType<sal_Bool>::get() ) + { + nPos = 1; + } + else + { + OUString aSoundURL; + aValue >>= aSoundURL; + + if( !aSoundURL.isEmpty() ) + { + sal_uLong i; + for( i = 0; i < maSoundList.size(); i++ ) + { + OUString aString = maSoundList[ i ]; + if( aString == aSoundURL ) + { + nPos = static_cast<sal_Int32>(i)+2; + break; + } + } + + if( nPos == 0 ) + { + nPos = static_cast<sal_Int32>(maSoundList.size())+2; + maSoundList.push_back( aSoundURL ); + INetURLObject aURL( aSoundURL ); + mxLBSound->insert_text(nPos, aURL.GetBase()); + } + } + } + + if( nPos != -1) + mxLBSound->set_active(nPos); + } + + updateControlStates(); + +} + +void CustomAnimationEffectTabPage::updateControlStates() +{ + auto nPos = mxLBAfterEffect->get_active(); + mxCLBDimColor->set_sensitive( nPos == 1 ); + mxFTDimColor->set_sensitive( nPos == 1 ); + + if( mbHasText ) + { + nPos = mxLBTextAnim->get_active(); + mxMFTextDelay->set_sensitive( nPos != 0 ); + mxFTTextDelay->set_sensitive( nPos != 0 ); + } + + if (comphelper::LibreOfficeKit::isActive()) + { + mxFTSound->hide(); + mxLBSound->hide(); + mxPBSoundPreview->hide(); + } + else + { + nPos = mxLBSound->get_active(); + mxPBSoundPreview->set_sensitive( nPos >= 2 ); + } + +} + +IMPL_LINK(CustomAnimationEffectTabPage, implClickHdl, weld::Button&, rBtn, void) +{ + implHdl(&rBtn); +} + +IMPL_LINK(CustomAnimationEffectTabPage, implSelectHdl, weld::ComboBox&, rListBox, void) +{ + implHdl(&rListBox); +} + +void CustomAnimationEffectTabPage::implHdl(const weld::Widget* pControl) +{ + if (pControl == mxLBTextAnim.get()) + { + if (mxMFTextDelay->get_value(FieldUnit::NONE) == 0) + mxMFTextDelay->set_value(100, FieldUnit::NONE); + } + else if (pControl == mxLBSound.get()) + { + auto nPos = mxLBSound->get_active(); + if (nPos == (mxLBSound->get_count() - 1)) + { + openSoundFileDialog(); + } + } + else if (pControl == mxPBSoundPreview.get()) + { + onSoundPreview(); + } + + updateControlStates(); +} + +void CustomAnimationEffectTabPage::update( STLPropertySet* pSet ) +{ + if (mxLBSubControl) + { + Any aNewValue(mxLBSubControl->getValue()); + Any aOldValue; + if( mpSet->getPropertyState( nHandleProperty1Value ) != STLPropertyState::Ambiguous) + aOldValue = mpSet->getPropertyValue( nHandleProperty1Value ); + + if( aOldValue != aNewValue ) + pSet->setPropertyValue( nHandleProperty1Value, aNewValue ); + } + + if (mxCBSmoothStart->get_visible()) + { + // set selected value for accelerate if different than in original set + + double fTemp = mxCBSmoothStart->get_active() ? 0.5 : 0.0; + + double fOldTemp = 0.0; + if(mpSet->getPropertyState( nHandleAccelerate ) != STLPropertyState::Ambiguous) + mpSet->getPropertyValue( nHandleAccelerate ) >>= fOldTemp; + else + fOldTemp = -2.0; + + if( fOldTemp != fTemp ) + pSet->setPropertyValue( nHandleAccelerate, Any( fTemp ) ); + + // set selected value for decelerate if different than in original set + fTemp = mxCBSmoothEnd->get_active() ? 0.5 : 0.0; + + if(mpSet->getPropertyState( nHandleDecelerate ) != STLPropertyState::Ambiguous) + mpSet->getPropertyValue( nHandleDecelerate ) >>= fOldTemp; + else + fOldTemp = -2.0; + + if( fOldTemp != fTemp ) + pSet->setPropertyValue( nHandleDecelerate, Any( fTemp ) ); + } + + auto nPos = mxLBAfterEffect->get_active(); + if (nPos != -1) + { + bool bAfterEffect = nPos != 0; + + bool bOldAfterEffect = false; + + if(mpSet->getPropertyState( nHandleHasAfterEffect ) != STLPropertyState::Ambiguous) + mpSet->getPropertyValue( nHandleHasAfterEffect ) >>= bOldAfterEffect; + else + bOldAfterEffect = !bAfterEffect; + + if( bOldAfterEffect != bAfterEffect ) + pSet->setPropertyValue( nHandleHasAfterEffect, Any( bAfterEffect ) ); + + Any aDimColor; + if( nPos == 1 ) + { + Color aSelectedColor = mxCLBDimColor->GetSelectEntryColor(); + aDimColor <<= aSelectedColor.GetRGBColor(); + } + + if( (mpSet->getPropertyState( nHandleDimColor ) == STLPropertyState::Ambiguous) || + (mpSet->getPropertyValue( nHandleDimColor ) != aDimColor) ) + pSet->setPropertyValue( nHandleDimColor, aDimColor ); + + bool bAfterEffectOnNextEffect = nPos != 2; + bool bOldAfterEffectOnNextEffect = !bAfterEffectOnNextEffect; + + if( mpSet->getPropertyState( nHandleAfterEffectOnNextEffect ) != STLPropertyState::Ambiguous) + mpSet->getPropertyValue( nHandleAfterEffectOnNextEffect ) >>= bOldAfterEffectOnNextEffect; + + if( bAfterEffectOnNextEffect != bOldAfterEffectOnNextEffect ) + pSet->setPropertyValue( nHandleAfterEffectOnNextEffect, Any( bAfterEffectOnNextEffect ) ); + } + + nPos = mxLBTextAnim->get_active(); + if (nPos != -1) + { + sal_Int16 nIterateType; + + switch( nPos ) + { + case 1: nIterateType = TextAnimationType::BY_WORD; break; + case 2: nIterateType = TextAnimationType::BY_LETTER; break; + default: + nIterateType = TextAnimationType::BY_PARAGRAPH; + } + + sal_Int16 nOldIterateType = nIterateType-1; + + if(mpSet->getPropertyState( nHandleIterateType ) != STLPropertyState::Ambiguous) + mpSet->getPropertyValue( nHandleIterateType ) >>= nOldIterateType; + + if( nIterateType != nOldIterateType ) + pSet->setPropertyValue( nHandleIterateType, Any( nIterateType ) ); + } + + { + double fIterateInterval = static_cast<double>(mxMFTextDelay->get_value(FieldUnit::NONE)) / 10; + double fOldIterateInterval = -1.0; + + if( mpSet->getPropertyState( nHandleIterateInterval ) != STLPropertyState::Ambiguous ) + mpSet->getPropertyValue( nHandleIterateInterval ) >>= fOldIterateInterval; + + if( fIterateInterval != fOldIterateInterval ) + pSet->setPropertyValue( nHandleIterateInterval, Any( fIterateInterval ) ); + } + + nPos = mxLBSound->get_active(); + if (nPos == -1) + return; + + Any aNewSoundURL, aOldSoundURL( Any( sal_Int32(0) ) ); + + if( nPos == 0 ) + { + // 0 means no sound, so leave any empty + } + else if( nPos == 1 ) + { + // this means stop sound + aNewSoundURL <<= true; + } + else + { + OUString aSoundURL( maSoundList[ nPos-2 ] ); + aNewSoundURL <<= aSoundURL; + } + + if( mpSet->getPropertyState( nHandleSoundURL ) != STLPropertyState::Ambiguous ) + aOldSoundURL = mpSet->getPropertyValue( nHandleSoundURL ); + + if( aNewSoundURL != aOldSoundURL ) + pSet->setPropertyValue( nHandleSoundURL, aNewSoundURL ); +} + +void CustomAnimationEffectTabPage::fillSoundListBox() +{ + GalleryExplorer::FillObjList( GALLERY_THEME_SOUNDS, maSoundList ); + GalleryExplorer::FillObjList( GALLERY_THEME_USERSOUNDS, maSoundList ); + + mxLBSound->append_text( SdResId(STR_CUSTOMANIMATION_NO_SOUND) ); + mxLBSound->append_text( SdResId(STR_CUSTOMANIMATION_STOP_PREVIOUS_SOUND) ); + for(const OUString & rString : maSoundList) + { + INetURLObject aURL( rString ); + mxLBSound->append_text( aURL.GetBase() ); + } + mxLBSound->append_text( SdResId(STR_CUSTOMANIMATION_BROWSE_SOUND) ); +} + +void CustomAnimationEffectTabPage::clearSoundListBox() +{ + maSoundList.clear(); + mxLBSound->clear(); +} + +sal_Int32 CustomAnimationEffectTabPage::getSoundObject( std::u16string_view rStr ) +{ + size_t i; + const size_t nCount = maSoundList.size(); + for( i = 0; i < nCount; i++ ) + { + if( maSoundList[ i ].equalsIgnoreAsciiCase(rStr) ) + return i+2; + } + + return -1; +} + +void CustomAnimationEffectTabPage::openSoundFileDialog() +{ + SdOpenSoundFileDialog aFileDialog(GetFrameWeld()); + + bool bValidSoundFile = false; + bool bQuitLoop = false; + ::tools::Long nPos = 0; + + while( !bQuitLoop && (aFileDialog.Execute() == ERRCODE_NONE) ) + { + OUString aFile = aFileDialog.GetPath(); + nPos = getSoundObject( aFile ); + + if( nPos < 0 ) // not in Soundliste + { + // try to insert in Gallery + if( GalleryExplorer::InsertURL( GALLERY_THEME_USERSOUNDS, aFile ) ) + { + clearSoundListBox(); + fillSoundListBox(); + + nPos = getSoundObject( aFile ); + DBG_ASSERT( nPos >= 0, "sd::CustomAnimationEffectTabPage::openSoundFileDialog(), Recently inserted sound not in list!" ); + + bValidSoundFile=true; + bQuitLoop=true; + } + else + { + OUString aStrWarning(SdResId(STR_WARNING_NOSOUNDFILE)); + aStrWarning = aStrWarning.replaceFirst("%", aFile); + std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(nullptr, + VclMessageType::Warning, VclButtonsType::NONE, + aStrWarning)); + xWarn->add_button(GetStandardText(StandardButtonType::Retry), RET_RETRY); + xWarn->add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL); + bQuitLoop = xWarn->run() != RET_RETRY; + + bValidSoundFile=false; + } + } + else + { + bValidSoundFile=true; + bQuitLoop=true; + } + } + + if( !bValidSoundFile ) + nPos = 0; + + mxLBSound->set_active(nPos); +} + +void CustomAnimationEffectTabPage::onSoundPreview() +{ +#if HAVE_FEATURE_AVMEDIA + const auto nPos = mxLBSound->get_active(); + + if( nPos >= 2 ) try + { + const OUString aSoundURL( maSoundList[ nPos-2 ] ); + mxPlayer.set( avmedia::MediaWindow::createPlayer( aSoundURL, "" ), uno::UNO_SET_THROW ); + mxPlayer->start(); + } + catch( uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "CustomAnimationEffectTabPage::onSoundPreview()" ); + } +#endif +} + +class CustomAnimationDurationTabPage +{ +public: + CustomAnimationDurationTabPage(weld::Container* pParent, const STLPropertySet* pSet); + + void update( STLPropertySet* pSet ); + + DECL_LINK(implControlHdl, weld::ComboBox&, void); + DECL_LINK(DurationModifiedHdl, weld::MetricSpinButton&, void); + +private: + const STLPropertySet* mpSet; + + std::unique_ptr<weld::Builder> mxBuilder; + std::unique_ptr<weld::Container> mxContainer; + std::unique_ptr<weld::Label> mxFTStart; + std::unique_ptr<weld::ComboBox> mxLBStart; + std::unique_ptr<weld::Label> mxFTStartDelay; + std::unique_ptr<weld::MetricSpinButton> mxMFStartDelay; + std::unique_ptr<weld::Label> mxFTDuration; + std::unique_ptr<weld::MetricSpinButton> mxCBXDuration; + std::unique_ptr<weld::Label> mxFTRepeat; + std::unique_ptr<weld::ComboBox> mxCBRepeat; + std::unique_ptr<weld::CheckButton> mxCBXRewind; + std::unique_ptr<weld::RadioButton> mxRBClickSequence; + std::unique_ptr<weld::RadioButton> mxRBInteractive; + std::unique_ptr<weld::ComboBox> mxLBTrigger; +}; + +CustomAnimationDurationTabPage::CustomAnimationDurationTabPage(weld::Container* pParent, const STLPropertySet* pSet) + : mpSet(pSet) + , mxBuilder(Application::CreateBuilder(pParent, "modules/simpress/ui/customanimationtimingtab.ui")) + , mxContainer(mxBuilder->weld_container("TimingTab")) + , mxFTStart(mxBuilder->weld_label("start_label")) + , mxLBStart(mxBuilder->weld_combo_box("start_list")) + , mxFTStartDelay(mxBuilder->weld_label("delay_label")) + , mxMFStartDelay(mxBuilder->weld_metric_spin_button("delay_value", FieldUnit::SECOND)) + , mxFTDuration(mxBuilder->weld_label("duration_label")) + , mxCBXDuration(mxBuilder->weld_metric_spin_button("anim_duration", FieldUnit::SECOND)) + , mxFTRepeat(mxBuilder->weld_label("repeat_label")) + , mxCBRepeat(mxBuilder->weld_combo_box("repeat_list")) + , mxCBXRewind(mxBuilder->weld_check_button("rewind")) + , mxRBClickSequence(mxBuilder->weld_radio_button("rb_click_sequence")) + , mxRBInteractive(mxBuilder->weld_radio_button("rb_interactive")) + , mxLBTrigger(mxBuilder->weld_combo_box("trigger_list")) +{ + mxLBTrigger->set_size_request(mxLBTrigger->get_approximate_digit_width() * 20, -1); + + fillRepeatComboBox(*mxCBRepeat); + + mxLBTrigger->connect_changed(LINK(this, CustomAnimationDurationTabPage, implControlHdl)); + mxCBXDuration->connect_value_changed(LINK( this, CustomAnimationDurationTabPage, DurationModifiedHdl)); + + if( pSet->getPropertyState( nHandleStart ) != STLPropertyState::Ambiguous ) + { + sal_Int16 nStart = 0; + pSet->getPropertyValue( nHandleStart ) >>= nStart; + sal_Int32 nPos = 0; + switch( nStart ) + { + case EffectNodeType::WITH_PREVIOUS: nPos = 1; break; + case EffectNodeType::AFTER_PREVIOUS: nPos = 2; break; + } + mxLBStart->set_active(nPos); + } + + if( pSet->getPropertyState( nHandleBegin ) != STLPropertyState::Ambiguous ) + { + double fBegin = 0.0; + pSet->getPropertyValue( nHandleBegin ) >>= fBegin; + mxMFStartDelay->set_value(static_cast<::tools::Long>(fBegin*10), FieldUnit::NONE); + } + + if( pSet->getPropertyState( nHandleDuration ) != STLPropertyState::Ambiguous ) + { + double fDuration = 0.0; + pSet->getPropertyValue( nHandleDuration ) >>= fDuration; + + if( fDuration == 0.001 ) + { + mxFTDuration->set_sensitive(false); + mxCBXDuration->set_sensitive(false); + mxFTRepeat->set_sensitive(false); + mxCBRepeat->set_sensitive(false); + mxCBXRewind->set_sensitive(false); + } + else + { + mxCBXDuration->set_value(fDuration * 100.0, FieldUnit::NONE); + } + } + + if( pSet->getPropertyState( nHandleRepeat ) != STLPropertyState::Ambiguous ) + { + Any aRepeatCount( pSet->getPropertyValue( nHandleRepeat ) ); + if( (aRepeatCount.getValueType() == ::cppu::UnoType<double>::get()) || !aRepeatCount.hasValue() ) + { + double fRepeat = 0.0; + if( aRepeatCount.hasValue() ) + aRepeatCount >>= fRepeat; + + auto nPos = -1; + + if( fRepeat == 0 ) + nPos = 0; + else if( fRepeat == 2.0 ) + nPos = 1; + else if( fRepeat == 3.0 ) + nPos = 2; + else if( fRepeat == 4.0 ) + nPos = 3; + else if( fRepeat == 5.0 ) + nPos = 4; + else if( fRepeat == 10.0 ) + nPos = 5; + + if (nPos != -1) + mxCBRepeat->set_active(nPos); + else + mxCBRepeat->set_entry_text(OUString::number(fRepeat)); + } + else if( aRepeatCount.getValueType() == ::cppu::UnoType<Timing>::get() ) + { + Any aEnd; + if( pSet->getPropertyState( nHandleEnd ) != STLPropertyState::Ambiguous ) + aEnd = pSet->getPropertyValue( nHandleEnd ); + + mxCBRepeat->set_active(aEnd.hasValue() ? 6 : 7); + } + } + + if( pSet->getPropertyState( nHandleRewind ) != STLPropertyState::Ambiguous ) + { + sal_Int16 nFill = 0; + if( pSet->getPropertyValue( nHandleRewind ) >>= nFill ) + { + mxCBXRewind->set_active(nFill == AnimationFill::REMOVE); + } + else + { + mxCBXRewind->set_state(TRISTATE_INDET); + } + } + + Reference< XShape > xTrigger; + + if( pSet->getPropertyState( nHandleTrigger ) != STLPropertyState::Ambiguous ) + { + pSet->getPropertyValue( nHandleTrigger ) >>= xTrigger; + + mxRBInteractive->set_active(xTrigger.is()); + mxRBClickSequence->set_active(!xTrigger.is()); + } + + Reference< XDrawPage > xCurrentPage; + pSet->getPropertyValue( nHandleCurrentPage ) >>= xCurrentPage; + if( !xCurrentPage.is() ) + return; + + static const OUStringLiteral aStrIsEmptyPresObj( u"IsEmptyPresentationObject" ); + + sal_Int32 nShape, nCount = xCurrentPage->getCount(); + for( nShape = 0; nShape < nCount; nShape++ ) + { + Reference< XShape > xShape( xCurrentPage->getByIndex( nShape ), UNO_QUERY ); + + if( !xShape.is() ) + continue; + + Reference< XPropertySet > xSet( xShape, UNO_QUERY ); + if( xSet.is() && xSet->getPropertySetInfo()->hasPropertyByName( aStrIsEmptyPresObj ) ) + { + bool bIsEmpty = false; + xSet->getPropertyValue( aStrIsEmptyPresObj ) >>= bIsEmpty; + if( bIsEmpty ) + continue; + } + + OUString aDescription( getShapeDescription( xShape, true ) ); + mxLBTrigger->append(OUString::number(nShape), aDescription); + auto nPos = mxLBTrigger->get_count() - 1; + if (xShape == xTrigger) + mxLBTrigger->set_active(nPos); + } +} + +IMPL_LINK_NOARG(CustomAnimationDurationTabPage, implControlHdl, weld::ComboBox&, void) +{ + mxRBInteractive->set_active(true); + assert(!mxRBClickSequence->get_active()); +} + +IMPL_LINK_NOARG(CustomAnimationDurationTabPage, DurationModifiedHdl, weld::MetricSpinButton&, void) +{ + if (!mxCBXDuration->get_text().isEmpty()) + { + double duration_value = static_cast<double>(mxCBXDuration->get_value(FieldUnit::NONE)); + if(duration_value <= 0.0) + mxCBXDuration->set_value(1, FieldUnit::NONE); + else + mxCBXDuration->set_value(duration_value, FieldUnit::NONE); + } +} + +void CustomAnimationDurationTabPage::update( STLPropertySet* pSet ) +{ + auto nPos = mxLBStart->get_active(); + if (nPos != -1) + { + sal_Int16 nStart; + sal_Int16 nOldStart = -1; + + switch( nPos ) + { + case 1: nStart = EffectNodeType::WITH_PREVIOUS; break; + case 2: nStart = EffectNodeType::AFTER_PREVIOUS; break; + default: + nStart = EffectNodeType::ON_CLICK; break; + } + + if(mpSet->getPropertyState( nHandleStart ) != STLPropertyState::Ambiguous) + mpSet->getPropertyValue( nHandleStart ) >>= nOldStart; + + if( nStart != nOldStart ) + pSet->setPropertyValue( nHandleStart, Any( nStart ) ); + } + + { + double fBegin = static_cast<double>(mxMFStartDelay->get_value(FieldUnit::NONE)) / 10.0; + double fOldBegin = -1.0; + + if( mpSet->getPropertyState( nHandleBegin ) != STLPropertyState::Ambiguous ) + mpSet->getPropertyValue( nHandleBegin ) >>= fOldBegin; + + if( fBegin != fOldBegin ) + pSet->setPropertyValue( nHandleBegin, Any( fBegin ) ); + } + + nPos = mxCBRepeat->get_active(); + if (nPos != -1 || !mxCBRepeat->get_active_text().isEmpty()) + { + Any aRepeatCount; + Any aEnd; + + switch( nPos ) + { + case 0: + break; + + case 6: + { + Event aEvent; + aEvent.Trigger = EventTrigger::ON_NEXT; + aEvent.Repeat = 0; + aEnd <<= aEvent; + } + [[fallthrough]]; + case 7: + aRepeatCount <<= Timing_INDEFINITE; + break; + default: + { + OUString aText(mxCBRepeat->get_text(nPos)); + if( !aText.isEmpty() ) + aRepeatCount <<= aText.toDouble(); + } + } + + Any aOldRepeatCount( aRepeatCount ); + if( mpSet->getPropertyState( nHandleRepeat ) != STLPropertyState::Ambiguous ) + aOldRepeatCount = mpSet->getPropertyValue( nHandleRepeat ); + + if( aRepeatCount != aOldRepeatCount ) + pSet->setPropertyValue( nHandleRepeat, aRepeatCount ); + + Any aOldEnd( aEnd ); + if( mpSet->getPropertyState( nHandleEnd ) != STLPropertyState::Ambiguous ) + aOldEnd = mpSet->getPropertyValue( nHandleEnd ); + + if( aEnd != aOldEnd ) + pSet->setPropertyValue( nHandleEnd, aEnd ); + } + + double fDuration = -1.0; + + if (!mxCBXDuration->get_text().isEmpty()) + { + double duration_value = static_cast<double>(mxCBXDuration->get_value(FieldUnit::NONE)); + + if(duration_value > 0) + fDuration = duration_value/100.0; + } + + if( fDuration != -1.0 ) + { + double fOldDuration = -1; + + if( mpSet->getPropertyState( nHandleDuration ) != STLPropertyState::Ambiguous ) + mpSet->getPropertyValue( nHandleDuration ) >>= fOldDuration; + + if( fDuration != fOldDuration ) + pSet->setPropertyValue( nHandleDuration, Any( fDuration ) ); + } + + if (mxCBXRewind->get_state() != TRISTATE_INDET) + { + sal_Int16 nFill = mxCBXRewind->get_active() ? AnimationFill::REMOVE : AnimationFill::HOLD; + + bool bSet = true; + + if( mpSet->getPropertyState( nHandleRewind ) != STLPropertyState::Ambiguous ) + { + sal_Int16 nOldFill = 0; + mpSet->getPropertyValue( nHandleRewind ) >>= nOldFill; + bSet = nFill != nOldFill; + } + + if( bSet ) + pSet->setPropertyValue( nHandleRewind, Any( nFill ) ); + } + + Reference< XShape > xTrigger; + + if (mxRBInteractive->get_active()) + { + nPos = mxLBTrigger->get_active(); + if (nPos != -1) + { + sal_Int32 nShape = mxLBTrigger->get_id(nPos).toInt32(); + + Reference< XDrawPage > xCurrentPage; + mpSet->getPropertyValue( nHandleCurrentPage ) >>= xCurrentPage; + + if( xCurrentPage.is() && (nShape >= 0) && (nShape < xCurrentPage->getCount()) ) + xCurrentPage->getByIndex( nShape ) >>= xTrigger; + } + } + + if (xTrigger.is() || mxRBClickSequence->get_active()) + { + Any aNewValue( xTrigger ); + Any aOldValue; + + if( mpSet->getPropertyState( nHandleTrigger ) != STLPropertyState::Ambiguous ) + aOldValue = mpSet->getPropertyValue( nHandleTrigger ); + + if( aNewValue != aOldValue ) + pSet->setPropertyValue( nHandleTrigger, aNewValue ); + } +} + +class CustomAnimationTextAnimTabPage +{ +public: + CustomAnimationTextAnimTabPage(weld::Container* pParent, const STLPropertySet* pSet); + + void update( STLPropertySet* pSet ); + + void updateControlStates(); + DECL_LINK(implSelectHdl, weld::ComboBox&, void); + +private: + const STLPropertySet* mpSet; + bool mbHasVisibleShapes; + + std::unique_ptr<weld::Builder> mxBuilder; + std::unique_ptr<weld::Container> mxContainer; + std::unique_ptr<weld::Label> mxFTGroupText; + std::unique_ptr<weld::ComboBox> mxLBGroupText; + std::unique_ptr<weld::CheckButton> mxCBXGroupAuto; + std::unique_ptr<weld::MetricSpinButton> mxMFGroupAuto; + std::unique_ptr<weld::CheckButton> mxCBXAnimateForm; + std::unique_ptr<weld::CheckButton> mxCBXReverse; +}; + +CustomAnimationTextAnimTabPage::CustomAnimationTextAnimTabPage(weld::Container* pParent, const STLPropertySet* pSet) + : mpSet(pSet) + , mbHasVisibleShapes(true) + , mxBuilder(Application::CreateBuilder(pParent, "modules/simpress/ui/customanimationtexttab.ui")) + , mxContainer(mxBuilder->weld_container("TextAnimationTab")) + , mxFTGroupText(mxBuilder->weld_label("group_text_label")) + , mxLBGroupText(mxBuilder->weld_combo_box("group_text_list")) + , mxCBXGroupAuto(mxBuilder->weld_check_button("auto_after")) + , mxMFGroupAuto(mxBuilder->weld_metric_spin_button("auto_after_value",FieldUnit::SECOND)) + , mxCBXAnimateForm(mxBuilder->weld_check_button("animate_shape")) + , mxCBXReverse(mxBuilder->weld_check_button("reverse_order")) +{ + mxLBGroupText->connect_changed(LINK(this, CustomAnimationTextAnimTabPage, implSelectHdl)); + + if( pSet->getPropertyState( nHandleTextGrouping ) != STLPropertyState::Ambiguous ) + { + sal_Int32 nTextGrouping = 0; + if( pSet->getPropertyValue( nHandleTextGrouping ) >>= nTextGrouping ) + mxLBGroupText->set_active(nTextGrouping + 1); + } + + if( pSet->getPropertyState( nHandleHasVisibleShape ) != STLPropertyState::Ambiguous ) + pSet->getPropertyValue( nHandleHasVisibleShape ) >>= mbHasVisibleShapes; + + if( pSet->getPropertyState( nHandleTextGroupingAuto ) != STLPropertyState::Ambiguous ) + { + double fTextGroupingAuto = 0.0; + if( pSet->getPropertyValue( nHandleTextGroupingAuto ) >>= fTextGroupingAuto ) + { + mxCBXGroupAuto->set_active(fTextGroupingAuto >= 0.0); + if( fTextGroupingAuto >= 0.0 ) + mxMFGroupAuto->set_value(static_cast<::tools::Long>(fTextGroupingAuto*10), FieldUnit::NONE); + } + } + else + { + mxCBXGroupAuto->set_state( TRISTATE_INDET ); + } + + mxCBXAnimateForm->set_state( TRISTATE_INDET ); + if( pSet->getPropertyState( nHandleAnimateForm ) != STLPropertyState::Ambiguous ) + { + bool bAnimateForm = false; + if( pSet->getPropertyValue( nHandleAnimateForm ) >>= bAnimateForm ) + { + mxCBXAnimateForm->set_active( bAnimateForm ); + } + } + else + { + mxCBXAnimateForm->set_sensitive(false); + } + + mxCBXReverse->set_state(TRISTATE_INDET); + if( pSet->getPropertyState( nHandleTextReverse ) != STLPropertyState::Ambiguous ) + { + bool bTextReverse = false; + if( pSet->getPropertyValue( nHandleTextReverse ) >>= bTextReverse ) + { + mxCBXReverse->set_active( bTextReverse ); + } + } + + if( pSet->getPropertyState( nHandleMaxParaDepth ) == STLPropertyState::Direct ) + { + sal_Int32 nMaxParaDepth = 0; + pSet->getPropertyValue( nHandleMaxParaDepth ) >>= nMaxParaDepth; + nMaxParaDepth += 1; + + sal_Int32 nPos = 6; + while( (nPos > 2) && (nPos > nMaxParaDepth) ) + { + mxLBGroupText->remove(nPos); + nPos--; + } + } + + updateControlStates(); +} + +void CustomAnimationTextAnimTabPage::update( STLPropertySet* pSet ) +{ + auto nPos = mxLBGroupText->get_active(); + if (nPos != -1) + { + sal_Int32 nTextGrouping = nPos - 1; + sal_Int32 nOldGrouping = -2; + + if(mpSet->getPropertyState( nHandleTextGrouping ) != STLPropertyState::Ambiguous) + mpSet->getPropertyValue( nHandleTextGrouping ) >>= nOldGrouping; + + if( nTextGrouping != nOldGrouping ) + pSet->setPropertyValue( nHandleTextGrouping, Any( nTextGrouping ) ); + } + + if (nPos != 0) + { + bool bTextReverse = mxCBXReverse->get_active(); + bool bOldTextReverse = !bTextReverse; + + if(mpSet->getPropertyState( nHandleTextReverse ) != STLPropertyState::Ambiguous) + mpSet->getPropertyValue( nHandleTextReverse ) >>= bOldTextReverse; + + if( bTextReverse != bOldTextReverse ) + pSet->setPropertyValue( nHandleTextReverse, Any( bTextReverse ) ); + + if( nPos > 1 ) + { + double fTextGroupingAuto = mxCBXGroupAuto->get_active() ? mxMFGroupAuto->get_value(FieldUnit::NONE) / 10.0 : -1.0; + double fOldTextGroupingAuto = -2.0; + + if(mpSet->getPropertyState( nHandleTextGroupingAuto ) != STLPropertyState::Ambiguous) + mpSet->getPropertyValue( nHandleTextGroupingAuto ) >>= fOldTextGroupingAuto; + + if( fTextGroupingAuto != fOldTextGroupingAuto ) + pSet->setPropertyValue( nHandleTextGroupingAuto, Any( fTextGroupingAuto ) ); + } + } + //#i120049# impress crashes when modifying the "Random effects" animation + //effect's trigger condition to "Start effect on click of". + //If this control is disabled, we should ignore its value + if (mxCBXAnimateForm->get_sensitive()) + { + bool bAnimateForm = mxCBXAnimateForm->get_active(); + bool bOldAnimateForm = !bAnimateForm; + + if(mpSet->getPropertyState( nHandleAnimateForm ) != STLPropertyState::Ambiguous) + mpSet->getPropertyValue( nHandleAnimateForm ) >>= bOldAnimateForm; + + if( bAnimateForm != bOldAnimateForm ) + pSet->setPropertyValue( nHandleAnimateForm, Any( bAnimateForm ) ); + } +} + +void CustomAnimationTextAnimTabPage::updateControlStates() +{ + auto nPos = mxLBGroupText->get_active(); + + mxCBXGroupAuto->set_sensitive( nPos > 1 ); + mxMFGroupAuto->set_sensitive( nPos > 1 ); + mxCBXReverse->set_sensitive( nPos > 0 ); + + if( !mbHasVisibleShapes && nPos > 0 ) + { + mxCBXAnimateForm->set_active(false); + mxCBXAnimateForm->set_sensitive(false); + } + else + { + mxCBXAnimateForm->set_sensitive(true); + } +} + +IMPL_LINK_NOARG(CustomAnimationTextAnimTabPage, implSelectHdl, weld::ComboBox&, void) +{ + updateControlStates(); +} + +CustomAnimationDialog::CustomAnimationDialog(weld::Window* pParent, std::unique_ptr<STLPropertySet> pSet, const OString& rPage) + : GenericDialogController(pParent, "modules/simpress/ui/customanimationproperties.ui", "CustomAnimationProperties") + , mxSet(std::move(pSet)) + , mxTabControl(m_xBuilder->weld_notebook("tabcontrol")) + , mxDurationTabPage(new CustomAnimationDurationTabPage(mxTabControl->get_page("timing"), mxSet.get())) + , mxEffectTabPage(new CustomAnimationEffectTabPage(mxTabControl->get_page("effect"), m_xDialog.get(), mxSet.get())) +{ + bool bHasText = false; + if( mxSet->getPropertyState( nHandleHasText ) != STLPropertyState::Ambiguous ) + mxSet->getPropertyValue( nHandleHasText ) >>= bHasText; + + if( bHasText ) + { + mxTextAnimTabPage.reset(new CustomAnimationTextAnimTabPage(mxTabControl->get_page("textanim"), mxSet.get())); + } + else + { + mxTabControl->remove_page("textanim"); + } + + if (!rPage.isEmpty()) + mxTabControl->set_current_page(rPage); +} + +CustomAnimationDialog::~CustomAnimationDialog() +{ +} + +STLPropertySet* CustomAnimationDialog::getResultSet() +{ + mxResultSet = createDefaultSet(); + + mxEffectTabPage->update( mxResultSet.get() ); + mxDurationTabPage->update( mxResultSet.get() ); + if (mxTextAnimTabPage) + mxTextAnimTabPage->update( mxResultSet.get() ); + + return mxResultSet.get(); +} + +std::unique_ptr<STLPropertySet> CustomAnimationDialog::createDefaultSet() +{ + Any aEmpty; + + std::unique_ptr<STLPropertySet> pSet(new STLPropertySet()); + pSet->setPropertyDefaultValue( nHandleMaxParaDepth, Any( sal_Int32(-1) ) ); + + pSet->setPropertyDefaultValue( nHandleHasAfterEffect, Any( false ) ); + pSet->setPropertyDefaultValue( nHandleAfterEffectOnNextEffect, Any( false ) ); + pSet->setPropertyDefaultValue( nHandleDimColor, aEmpty ); + pSet->setPropertyDefaultValue( nHandleIterateType, Any( sal_Int16(0) ) ); + pSet->setPropertyDefaultValue( nHandleIterateInterval, Any( 0.0 ) ); + + pSet->setPropertyDefaultValue( nHandleStart, Any( sal_Int16(EffectNodeType::ON_CLICK) ) ); + pSet->setPropertyDefaultValue( nHandleBegin, Any( 0.0 ) ); + pSet->setPropertyDefaultValue( nHandleDuration, Any( 2.0 ) ); + pSet->setPropertyDefaultValue( nHandleRepeat, aEmpty ); + pSet->setPropertyDefaultValue( nHandleRewind, Any( AnimationFill::HOLD ) ); + + pSet->setPropertyDefaultValue( nHandleEnd, aEmpty ); + + pSet->setPropertyDefaultValue( nHandlePresetId, aEmpty ); + pSet->setPropertyDefaultValue( nHandleProperty1Type, Any( nPropertyTypeNone ) ); + pSet->setPropertyDefaultValue( nHandleProperty1Value, aEmpty ); + pSet->setPropertyDefaultValue( nHandleProperty2Type, Any( nPropertyTypeNone ) ); + pSet->setPropertyDefaultValue( nHandleProperty2Value, aEmpty ); + pSet->setPropertyDefaultValue( nHandleAccelerate, aEmpty ); + pSet->setPropertyDefaultValue( nHandleDecelerate, aEmpty ); + pSet->setPropertyDefaultValue( nHandleAutoReverse, aEmpty ); + pSet->setPropertyDefaultValue( nHandleTrigger, aEmpty ); + + pSet->setPropertyDefaultValue( nHandleHasText, Any( false ) ); + pSet->setPropertyDefaultValue( nHandleHasVisibleShape, Any( false ) ); + pSet->setPropertyDefaultValue( nHandleTextGrouping, Any( sal_Int32(-1) ) ); + pSet->setPropertyDefaultValue( nHandleAnimateForm, Any( true ) ); + pSet->setPropertyDefaultValue( nHandleTextGroupingAuto, Any( -1.0 ) ); + pSet->setPropertyDefaultValue( nHandleTextReverse, Any( false ) ); + + pSet->setPropertyDefaultValue( nHandleCurrentPage, aEmpty ); + + pSet->setPropertyDefaultValue( nHandleSoundURL, aEmpty ); + pSet->setPropertyDefaultValue( nHandleSoundVolume, Any( 1.0) ); + pSet->setPropertyDefaultValue( nHandleSoundEndAfterSlide, Any( sal_Int32(0) ) ); + + pSet->setPropertyDefaultValue( nHandleCommand, Any( sal_Int16(0) ) ); + return pSet; +} + +std::unique_ptr<SdPropertySubControl> SdPropertySubControl::create(sal_Int32 nType, weld::Label* pLabel, weld::Container* pParent, weld::Window* pTopLevel, const Any& rValue, const OUString& rPresetId, const Link<LinkParamNone*,void>& rModifyHdl) +{ + std::unique_ptr<SdPropertySubControl> pSubControl; + switch( nType ) + { + case nPropertyTypeDirection: + case nPropertyTypeSpokes: + case nPropertyTypeZoom: + pSubControl.reset( new SdPresetPropertyBox( pLabel, pParent, rValue, rPresetId, rModifyHdl ) ); + break; + + case nPropertyTypeColor: + case nPropertyTypeFillColor: + case nPropertyTypeFirstColor: + case nPropertyTypeCharColor: + case nPropertyTypeLineColor: + pSubControl.reset( new SdColorPropertyBox( pLabel, pParent, pTopLevel, rValue, rModifyHdl ) ); + break; + + case nPropertyTypeFont: + pSubControl.reset( new SdFontPropertyBox( pLabel, pParent, rValue, rModifyHdl ) ); + break; + + case nPropertyTypeCharHeight: + pSubControl.reset( new SdCharHeightPropertyBox( pLabel, pParent, rValue, rModifyHdl ) ); + break; + + case nPropertyTypeRotate: + pSubControl.reset( new SdRotationPropertyBox( pLabel, pParent, rValue, rModifyHdl ) ); + break; + + case nPropertyTypeTransparency: + pSubControl.reset( new SdTransparencyPropertyBox( pLabel, pParent, rValue, rModifyHdl ) ); + break; + + case nPropertyTypeScale: + pSubControl.reset( new SdScalePropertyBox( pLabel, pParent, rValue, rModifyHdl ) ); + break; + + case nPropertyTypeCharDecoration: + pSubControl.reset( new SdFontStylePropertyBox( pLabel, pParent, rValue, rModifyHdl ) ); + break; + } + + return pSubControl; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/animations/CustomAnimationDialog.hxx b/sd/source/ui/animations/CustomAnimationDialog.hxx new file mode 100644 index 000000000..b8a8abcff --- /dev/null +++ b/sd/source/ui/animations/CustomAnimationDialog.hxx @@ -0,0 +1,141 @@ +/* -*- 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 . + */ + +#pragma once + +#include <vcl/weld.hxx> + +namespace sd { + +// property handles +const sal_Int32 nHandleSound = 0; +const sal_Int32 nHandleHasAfterEffect = 1; +const sal_Int32 nHandleIterateType = 2; +const sal_Int32 nHandleIterateInterval = 3; +const sal_Int32 nHandleStart = 4; +const sal_Int32 nHandleBegin = 5; +const sal_Int32 nHandleDuration = 6; +const sal_Int32 nHandleRepeat = 7; +const sal_Int32 nHandleRewind = 8; +const sal_Int32 nHandleEnd = 9; +const sal_Int32 nHandleAfterEffectOnNextEffect = 10; +const sal_Int32 nHandleDimColor = 11; +const sal_Int32 nHandleMaxParaDepth = 12; +const sal_Int32 nHandlePresetId = 13; +const sal_Int32 nHandleProperty1Type = 14; +const sal_Int32 nHandleProperty1Value = 15; +const sal_Int32 nHandleProperty2Type = 16; +const sal_Int32 nHandleProperty2Value = 17; + +const sal_Int32 nHandleAccelerate = 18; +const sal_Int32 nHandleDecelerate = 19; +const sal_Int32 nHandleAutoReverse = 20; +const sal_Int32 nHandleTrigger = 21; + +const sal_Int32 nHandleHasText = 22; +const sal_Int32 nHandleTextGrouping = 23; +const sal_Int32 nHandleAnimateForm = 24; +const sal_Int32 nHandleTextGroupingAuto = 25; +const sal_Int32 nHandleTextReverse = 26; + +const sal_Int32 nHandleCurrentPage = 27; +const sal_Int32 nHandleSoundURL = 28; +const sal_Int32 nHandleSoundVolume = 29; +const sal_Int32 nHandleSoundEndAfterSlide = 30; + +const sal_Int32 nHandleCommand = 31; + +const sal_Int32 nHandleHasVisibleShape = 32; + +const sal_Int32 nPropertyTypeNone = 0; +const sal_Int32 nPropertyTypeDirection = 1; +const sal_Int32 nPropertyTypeSpokes = 2; +const sal_Int32 nPropertyTypeFirstColor = 3; +const sal_Int32 nPropertyTypeSecondColor = 4; +const sal_Int32 nPropertyTypeZoom = 5; +const sal_Int32 nPropertyTypeFillColor = 6; +const sal_Int32 nPropertyTypeColorStyle = 7; +const sal_Int32 nPropertyTypeFont = 8; +const sal_Int32 nPropertyTypeCharHeight = 9; +const sal_Int32 nPropertyTypeCharColor = 10; +const sal_Int32 nPropertyTypeCharHeightStyle = 11; +const sal_Int32 nPropertyTypeCharDecoration = 12; +const sal_Int32 nPropertyTypeLineColor = 13; +const sal_Int32 nPropertyTypeRotate = 14; +const sal_Int32 nPropertyTypeColor = 15; +const sal_Int32 nPropertyTypeAccelerate = 16; +const sal_Int32 nPropertyTypeDecelerate = 17; +const sal_Int32 nPropertyTypeAutoReverse = 18; +const sal_Int32 nPropertyTypeTransparency = 19; +const sal_Int32 nPropertyTypeFontStyle = 20; +const sal_Int32 nPropertyTypeScale = 21; + +class SdPropertySubControl +{ +public: + explicit SdPropertySubControl(weld::Container* pParent); + virtual ~SdPropertySubControl(); + + virtual css::uno::Any getValue() = 0; + virtual void setValue( const css::uno::Any& rValue, const OUString& rPresetId ) = 0; + + static std::unique_ptr<SdPropertySubControl> + create( sal_Int32 nType, + weld::Label* pLabel, + weld::Container* pParent, + weld::Window* pTopLevel, + const css::uno::Any& rValue, + const OUString& rPresetId, + const Link<LinkParamNone*,void>& rModifyHdl ); + +protected: + std::unique_ptr<weld::Builder> mxBuilder; + std::unique_ptr<weld::Container> mxContainer; + weld::Container* mpParent; +}; + +class CustomAnimationDurationTabPage; +class CustomAnimationEffectTabPage; +class CustomAnimationTextAnimTabPage; +class STLPropertySet; + +class CustomAnimationDialog : public weld::GenericDialogController +{ +public: + CustomAnimationDialog(weld::Window* pParent, std::unique_ptr<STLPropertySet> pSet, const OString& Page); + virtual ~CustomAnimationDialog() override; + + STLPropertySet* getResultSet(); + STLPropertySet* getPropertySet() const { return mxSet.get(); } + + static std::unique_ptr<STLPropertySet> createDefaultSet(); + +private: + std::unique_ptr<STLPropertySet> mxSet; + std::unique_ptr<STLPropertySet> mxResultSet; + + std::unique_ptr<weld::Notebook> mxTabControl; + std::unique_ptr<CustomAnimationDurationTabPage> mxDurationTabPage; + std::unique_ptr<CustomAnimationEffectTabPage> mxEffectTabPage; + std::unique_ptr<CustomAnimationTextAnimTabPage> mxTextAnimTabPage; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/animations/CustomAnimationList.cxx b/sd/source/ui/animations/CustomAnimationList.cxx new file mode 100644 index 000000000..cc85ed74f --- /dev/null +++ b/sd/source/ui/animations/CustomAnimationList.cxx @@ -0,0 +1,1231 @@ +/* -*- 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 <com/sun/star/document/XActionLockable.hpp> +#include <com/sun/star/drawing/XDrawPage.hpp> +#include <com/sun/star/drawing/XShapes.hpp> +#include <com/sun/star/presentation/ShapeAnimationSubType.hpp> +#include <com/sun/star/presentation/EffectNodeType.hpp> +#include <com/sun/star/presentation/ParagraphTarget.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <com/sun/star/presentation/EffectPresetClass.hpp> +#include <com/sun/star/presentation/EffectCommands.hpp> +#include <com/sun/star/text/XTextRange.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <comphelper/scopeguard.hxx> +#include <CustomAnimationList.hxx> +#include <CustomAnimationPreset.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/event.hxx> +#include <vcl/image.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weldutils.hxx> +#include <tools/debug.hxx> +#include <tools/gen.hxx> +#include <tools/diagnose_ex.h> + +#include <sdresid.hxx> + +#include <strings.hrc> +#include <bitmaps.hlst> + +#include <algorithm> +#include <memory> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::presentation; + +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::XInterface; +using ::com::sun::star::text::XTextRange; +using ::com::sun::star::drawing::XShape; +using ::com::sun::star::drawing::XShapes; +using ::com::sun::star::drawing::XDrawPage; +using ::com::sun::star::container::XChild; +using ::com::sun::star::container::XIndexAccess; +using ::com::sun::star::container::XEnumerationAccess; +using ::com::sun::star::container::XEnumeration; +using ::com::sun::star::beans::XPropertySet; +using ::com::sun::star::beans::XPropertySetInfo; + +namespace sd { + +// go recursively through all shapes in the given XShapes collection and return true as soon as the +// given shape is found. nIndex is incremented for each shape with the same shape type as the given +// shape is found until the given shape is found. +static bool getShapeIndex( const Reference< XShapes >& xShapes, const Reference< XShape >& xShape, sal_Int32& nIndex ) +{ + const sal_Int32 nCount = xShapes->getCount(); + sal_Int32 n; + for( n = 0; n < nCount; n++ ) + { + Reference< XShape > xChild; + xShapes->getByIndex( n ) >>= xChild; + if( xChild == xShape ) + return true; + + if( xChild->getShapeType() == xShape->getShapeType() ) + nIndex++; + + Reference< XShapes > xChildContainer( xChild, UNO_QUERY ); + if( xChildContainer.is() ) + { + if( getShapeIndex( xChildContainer, xShape, nIndex ) ) + return true; + } + } + + return false; +} + +// returns the index of the shape type from the given shape +static sal_Int32 getShapeIndex( const Reference< XShape >& xShape ) +{ + Reference< XChild > xChild( xShape, UNO_QUERY ); + Reference< XShapes > xPage; + + while( xChild.is() && !xPage.is() ) + { + Reference< XInterface > x( xChild->getParent() ); + xChild.set( x, UNO_QUERY ); + Reference< XDrawPage > xTestPage( x, UNO_QUERY ); + if( xTestPage.is() ) + xPage.set( x, UNO_QUERY ); + } + + sal_Int32 nIndex = 1; + + if( xPage.is() && getShapeIndex( xPage, xShape, nIndex ) ) + return nIndex; + else + return -1; +} + +OUString getShapeDescription( const Reference< XShape >& xShape, bool bWithText ) +{ + OUString aDescription; + Reference< XPropertySet > xSet( xShape, UNO_QUERY ); + bool bAppendIndex = true; + + if(xSet.is()) try + { + Reference<XPropertySetInfo> xInfo(xSet->getPropertySetInfo()); + if (xInfo.is()) + { + static const OUStringLiteral aPropName1(u"Name"); + if(xInfo->hasPropertyByName(aPropName1)) + xSet->getPropertyValue(aPropName1) >>= aDescription; + + bAppendIndex = aDescription.isEmpty(); + + static const OUStringLiteral aPropName2(u"UINameSingular"); + if(xInfo->hasPropertyByName(aPropName2)) + xSet->getPropertyValue(aPropName2) >>= aDescription; + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::getShapeDescription()" ); + } + + if (bAppendIndex) + { + aDescription += " " + OUString::number(getShapeIndex(xShape)); + } + + if( bWithText ) + { + Reference< XTextRange > xText( xShape, UNO_QUERY ); + if( xText.is() ) + { + OUString aText( xText->getString() ); + if( !aText.isEmpty() ) + { + aDescription += ": "; + + aText = aText.replace( '\n', ' ' ); + aText = aText.replace( '\r', ' ' ); + + aDescription += aText; + } + } + } + return aDescription; +} + +static OUString getDescription( const Any& rTarget, bool bWithText ) +{ + OUString aDescription; + + if( rTarget.getValueType() == ::cppu::UnoType<ParagraphTarget>::get() ) + { + ParagraphTarget aParaTarget; + rTarget >>= aParaTarget; + + css::uno::Reference<css::document::XActionLockable> xLockable(aParaTarget.Shape, css::uno::UNO_QUERY); + if (xLockable.is()) + xLockable->addActionLock(); + comphelper::ScopeGuard aGuard([&xLockable]() + { + if (xLockable.is()) + xLockable->removeActionLock(); + }); + + Reference< XEnumerationAccess > xText( aParaTarget.Shape, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xText->createEnumeration(), css::uno::UNO_SET_THROW ); + sal_Int32 nPara = aParaTarget.Paragraph; + + while( xEnumeration->hasMoreElements() && nPara ) + { + xEnumeration->nextElement(); + nPara--; + } + + DBG_ASSERT( xEnumeration->hasMoreElements(), "sd::CustomAnimationEffect::prepareText(), paragraph out of range!" ); + + if( xEnumeration->hasMoreElements() ) + { + Reference< XTextRange > xParagraph; + xEnumeration->nextElement() >>= xParagraph; + + if( xParagraph.is() ) + aDescription = xParagraph->getString(); + } + } + else + { + Reference< XShape > xShape; + rTarget >>= xShape; + if( xShape.is() ) + aDescription = getShapeDescription( xShape, bWithText ); + } + + return aDescription; +} + +class CustomAnimationListEntryItem +{ +public: + CustomAnimationListEntryItem(const OUString& aDescription, + const CustomAnimationEffectPtr& pEffect); + const CustomAnimationEffectPtr& getEffect() const { return mpEffect; } + + Size GetSize(const vcl::RenderContext& rRenderContext); + void Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect, bool bSelected); + void PaintEffect(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect, bool bSelected); + void PaintTrigger(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect); + +private: + OUString msDescription; + OUString msEffectName; + CustomAnimationEffectPtr mpEffect; + +public: + static const ::tools::Long nIconWidth = 19; + static const ::tools::Long nItemMinHeight = 38; +}; + +CustomAnimationListEntryItem::CustomAnimationListEntryItem(const OUString& aDescription, const CustomAnimationEffectPtr& pEffect) + : msDescription(aDescription) + , mpEffect(pEffect) +{ + if (!mpEffect) + return; + switch (mpEffect->getPresetClass()) + { + case EffectPresetClass::ENTRANCE: + msEffectName = SdResId(STR_CUSTOMANIMATION_ENTRANCE); break; + case EffectPresetClass::EXIT: + msEffectName = SdResId(STR_CUSTOMANIMATION_EXIT); break; + case EffectPresetClass::EMPHASIS: + msEffectName = SdResId(STR_CUSTOMANIMATION_EMPHASIS); break; + case EffectPresetClass::MOTIONPATH: + msEffectName = SdResId(STR_CUSTOMANIMATION_MOTION_PATHS); break; + default: + msEffectName = SdResId(STR_CUSTOMANIMATION_MISC); break; + } + msEffectName = msEffectName.replaceFirst( "%1" , CustomAnimationPresets::getCustomAnimationPresets().getUINameForPresetId(mpEffect->getPresetId())); +} + +IMPL_STATIC_LINK(CustomAnimationList, CustomRenderHdl, weld::TreeView::render_args, aPayload, void) +{ + vcl::RenderContext& rRenderContext = std::get<0>(aPayload); + const ::tools::Rectangle& rRect = std::get<1>(aPayload); + bool bSelected = std::get<2>(aPayload); + const OUString& rId = std::get<3>(aPayload); + + CustomAnimationListEntryItem* pItem = weld::fromId<CustomAnimationListEntryItem*>(rId); + + pItem->Paint(rRenderContext, rRect, bSelected); +} + +IMPL_STATIC_LINK(CustomAnimationList, CustomGetSizeHdl, weld::TreeView::get_size_args, aPayload, Size) +{ + vcl::RenderContext& rRenderContext = aPayload.first; + const OUString& rId = aPayload.second; + + CustomAnimationListEntryItem* pItem = weld::fromId<CustomAnimationListEntryItem*>(rId); + if (!pItem) + return Size(CustomAnimationListEntryItem::nIconWidth, CustomAnimationListEntryItem::nItemMinHeight); + return pItem->GetSize(rRenderContext); +} + +Size CustomAnimationListEntryItem::GetSize(const vcl::RenderContext& rRenderContext) +{ + auto width = rRenderContext.GetTextWidth( msDescription ) + nIconWidth; + if (width < (rRenderContext.GetTextWidth( msEffectName ) + 2*nIconWidth)) + width = rRenderContext.GetTextWidth( msEffectName ) + 2*nIconWidth; + + Size aSize(width, rRenderContext.GetTextHeight()); + if (aSize.Height() < nItemMinHeight) + aSize.setHeight(nItemMinHeight); + return aSize; +} + +void CustomAnimationListEntryItem::PaintTrigger(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect) +{ + Size aSize(rRect.GetSize()); + + ::tools::Rectangle aOutRect(rRect); + + // fill the background + Color aColor(rRenderContext.GetSettings().GetStyleSettings().GetDialogColor()); + + rRenderContext.Push(); + rRenderContext.SetFillColor(aColor); + rRenderContext.SetLineColor(); + rRenderContext.DrawRect(aOutRect); + + // Erase the four corner pixels to make the rectangle appear rounded. + rRenderContext.SetLineColor(rRenderContext.GetSettings().GetStyleSettings().GetWindowColor()); + rRenderContext.DrawPixel(aOutRect.TopLeft()); + rRenderContext.DrawPixel(Point(aOutRect.Right(), aOutRect.Top())); + rRenderContext.DrawPixel(Point(aOutRect.Left(), aOutRect.Bottom())); + rRenderContext.DrawPixel(Point(aOutRect.Right(), aOutRect.Bottom())); + + // draw the category title + + int nVertBorder = ((aSize.Height() - rRenderContext.GetTextHeight()) >> 1); + int nHorzBorder = rRenderContext.LogicToPixel(Size(3, 3), MapMode(MapUnit::MapAppFont)).Width(); + + aOutRect.AdjustLeft(nHorzBorder ); + aOutRect.AdjustRight( -nHorzBorder ); + aOutRect.AdjustTop( nVertBorder ); + aOutRect.AdjustBottom( -nVertBorder ); + + rRenderContext.DrawText(aOutRect, rRenderContext.GetEllipsisString(msDescription, aOutRect.GetWidth())); + rRenderContext.Pop(); +} + +void CustomAnimationListEntryItem::PaintEffect(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect, bool bSelected) +{ + rRenderContext.Push(vcl::PushFlags::TEXTCOLOR); + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + if (bSelected) + rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor()); + else + rRenderContext.SetTextColor(rStyleSettings.GetDialogTextColor()); + + Point aPos(rRect.TopLeft()); + int nItemHeight = rRect.GetHeight(); + + sal_Int16 nNodeType = mpEffect->getNodeType(); + if (nNodeType == EffectNodeType::ON_CLICK ) + { + rRenderContext.DrawImage(aPos, Image(StockImage::Yes, BMP_CUSTOMANIMATION_ON_CLICK)); + } + else if (nNodeType == EffectNodeType::AFTER_PREVIOUS) + { + rRenderContext.DrawImage(aPos, Image(StockImage::Yes, BMP_CUSTOMANIMATION_AFTER_PREVIOUS)); + } + else if (nNodeType == EffectNodeType::WITH_PREVIOUS) + { + //FIXME With previous image not defined in CustomAnimation.src + } + + aPos.AdjustX(nIconWidth); + + //TODO, full width of widget ? + rRenderContext.DrawText(aPos, rRenderContext.GetEllipsisString(msDescription, rRect.GetWidth())); + + aPos.AdjustY(nIconWidth); + + OUString sImage; + switch (mpEffect->getPresetClass()) + { + case EffectPresetClass::ENTRANCE: + sImage = BMP_CUSTOMANIMATION_ENTRANCE_EFFECT; break; + case EffectPresetClass::EXIT: + sImage = BMP_CUSTOMANIMATION_EXIT_EFFECT; break; + case EffectPresetClass::EMPHASIS: + sImage = BMP_CUSTOMANIMATION_EMPHASIS_EFFECT; break; + case EffectPresetClass::MOTIONPATH: + sImage = BMP_CUSTOMANIMATION_MOTION_PATH; break; + case EffectPresetClass::OLEACTION: + sImage = BMP_CUSTOMANIMATION_OLE; break; + case EffectPresetClass::MEDIACALL: + switch (mpEffect->getCommand()) + { + case EffectCommands::TOGGLEPAUSE: + sImage = BMP_CUSTOMANIMATION_MEDIA_PAUSE; break; + case EffectCommands::STOP: + sImage = BMP_CUSTOMANIMATION_MEDIA_STOP; break; + case EffectCommands::PLAY: + default: + sImage = BMP_CUSTOMANIMATION_MEDIA_PLAY; break; + } + break; + default: + break; + } + + if (!sImage.isEmpty()) + { + Image aImage(StockImage::Yes, sImage); + Point aImagePos(aPos); + aImagePos.AdjustY((nItemHeight/2 - aImage.GetSizePixel().Height()) >> 1 ); + rRenderContext.DrawImage(aImagePos, aImage); + } + + aPos.AdjustX(nIconWidth ); + aPos.AdjustY((nItemHeight/2 - rRenderContext.GetTextHeight()) >> 1 ); + + rRenderContext.DrawText(aPos, rRenderContext.GetEllipsisString(msEffectName, rRect.GetWidth())); + rRenderContext.Pop(); +} + +void CustomAnimationListEntryItem::Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect, bool bSelected) +{ + if (mpEffect) + PaintEffect(rRenderContext, rRect, bSelected); + else + PaintTrigger(rRenderContext, rRect); +} + +CustomAnimationList::CustomAnimationList(std::unique_ptr<weld::TreeView> xTreeView, + std::unique_ptr<weld::Label> xLabel, + std::unique_ptr<weld::Widget> xScrolledWindow) + : mxTreeView(std::move(xTreeView)) + , maDropTargetHelper(*this) + , mxEmptyLabel(std::move(xLabel)) + , mxEmptyLabelParent(std::move(xScrolledWindow)) + , mbIgnorePaint(false) + , mpController(nullptr) + , mnLastGroupId(0) + , mnPostExpandEvent(nullptr) + , mnPostCollapseEvent(nullptr) +{ + mxEmptyLabel->set_stack_background(); + + mxTreeView->set_selection_mode(SelectionMode::Multiple); + mxTreeView->connect_changed(LINK(this, CustomAnimationList, SelectHdl)); + mxTreeView->connect_key_press(LINK(this, CustomAnimationList, KeyInputHdl)); + mxTreeView->connect_popup_menu(LINK(this, CustomAnimationList, CommandHdl)); + mxTreeView->connect_row_activated(LINK(this, CustomAnimationList, DoubleClickHdl)); + mxTreeView->connect_expanding(LINK(this, CustomAnimationList, ExpandHdl)); + mxTreeView->connect_collapsing(LINK(this, CustomAnimationList, CollapseHdl)); + mxTreeView->connect_drag_begin(LINK(this, CustomAnimationList, DragBeginHdl)); + mxTreeView->connect_custom_get_size(LINK(this, CustomAnimationList, CustomGetSizeHdl)); + mxTreeView->connect_custom_render(LINK(this, CustomAnimationList, CustomRenderHdl)); + mxTreeView->set_column_custom_renderer(0, true); +} + +CustomAnimationListDropTarget::CustomAnimationListDropTarget(CustomAnimationList& rTreeView) + : DropTargetHelper(rTreeView.get_widget().get_drop_target()) + , m_rTreeView(rTreeView) +{ +} + +sal_Int8 CustomAnimationListDropTarget::AcceptDrop(const AcceptDropEvent& rEvt) +{ + sal_Int8 nAccept = m_rTreeView.AcceptDrop(rEvt); + + if (nAccept != DND_ACTION_NONE) + { + // to enable the autoscroll when we're close to the edges + weld::TreeView& rWidget = m_rTreeView.get_widget(); + rWidget.get_dest_row_at_pos(rEvt.maPosPixel, nullptr, true); + } + + return nAccept; +} + +sal_Int8 CustomAnimationListDropTarget::ExecuteDrop(const ExecuteDropEvent& rEvt) +{ + return m_rTreeView.ExecuteDrop(rEvt); +} + +// D'n'D #1: Record selected effects for drag'n'drop. +IMPL_LINK(CustomAnimationList, DragBeginHdl, bool&, rUnsetDragIcon, bool) +{ + rUnsetDragIcon = false; + + // Record which effects are selected: + // Since NextSelected(..) iterates through the selected items in the order they + // were selected, create a sorted list for simpler drag'n'drop algorithms. + mDndEffectsSelected.clear(); + mxTreeView->selected_foreach([this](weld::TreeIter& rEntry){ + mDndEffectsSelected.emplace_back(mxTreeView->make_iterator(&rEntry)); + return false; + }); + + // Note: pEntry is the effect with focus (if multiple effects are selected) + mxDndEffectDragging = mxTreeView->make_iterator(); + if (!mxTreeView->get_cursor(mxDndEffectDragging.get())) + mxDndEffectDragging.reset(); + + // Allow normal processing. + return false; +} + +// D'n'D #3: Called each time mouse moves during drag +sal_Int8 CustomAnimationList::AcceptDrop( const AcceptDropEvent& rEvt ) +{ + sal_Int8 ret = DND_ACTION_NONE; + + const bool bIsMove = DND_ACTION_MOVE == rEvt.mnAction; + if (mxDndEffectDragging && !rEvt.mbLeaving && bIsMove) + ret = DND_ACTION_MOVE; + return ret; +} + +// D'n'D #5: Tell model to update effect order. +sal_Int8 CustomAnimationList::ExecuteDrop(const ExecuteDropEvent& rEvt) +{ + std::unique_ptr<weld::TreeIter> xDndEffectInsertBefore(mxTreeView->make_iterator()); + if (!mxTreeView->get_dest_row_at_pos(rEvt.maPosPixel, xDndEffectInsertBefore.get(), true)) + xDndEffectInsertBefore.reset(); + + const bool bMovingEffect = ( mxDndEffectDragging != nullptr ); + const bool bMoveNotSelf = !xDndEffectInsertBefore || (mxDndEffectDragging && mxTreeView->iter_compare(*xDndEffectInsertBefore, *mxDndEffectDragging) != 0); + const bool bHaveSequence(mpMainSequence); + + if( bMovingEffect && bMoveNotSelf && bHaveSequence ) + { + CustomAnimationListEntryItem* pTarget = xDndEffectInsertBefore ? + weld::fromId<CustomAnimationListEntryItem*>(mxTreeView->get_id(*xDndEffectInsertBefore)) : + nullptr; + + // Build list of effects + std::vector< CustomAnimationEffectPtr > aEffects; + for( const auto &pEntry : mDndEffectsSelected ) + { + CustomAnimationListEntryItem* pCustomAnimationEffect = weld::fromId<CustomAnimationListEntryItem*>(mxTreeView->get_id(*pEntry)); + aEffects.push_back(pCustomAnimationEffect->getEffect()); + } + + // Callback to observer to have it update the model. + // If pTarget is null, pass nullptr to indicate end of list. + mpController->onDragNDropComplete( + std::move(aEffects), + pTarget ? pTarget->getEffect() : nullptr ); + + // Reset selection + mxTreeView->select(*mxDndEffectDragging); + Select(); + } + + // NOTE: Don't call default handler because all required + // move operations have been completed here to update the model. + return DND_ACTION_NONE; +} + +CustomAnimationList::~CustomAnimationList() +{ + if (mnPostExpandEvent) + { + Application::RemoveUserEvent(mnPostExpandEvent); + mnPostExpandEvent = nullptr; + } + + if (mnPostCollapseEvent) + { + Application::RemoveUserEvent(mnPostCollapseEvent); + mnPostCollapseEvent = nullptr; + } + + if( mpMainSequence ) + mpMainSequence->removeListener( this ); + + clear(); +} + +IMPL_LINK(CustomAnimationList, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + const int nKeyCode = rKEvt.GetKeyCode().GetCode(); + switch (nKeyCode) + { + case KEY_DELETE: + mpController->onContextMenu("remove"); + return true; + case KEY_INSERT: + mpController->onContextMenu("create"); + return true; + case KEY_SPACE: + { + std::unique_ptr<weld::TreeIter> xEntry = mxTreeView->make_iterator(); + if (mxTreeView->get_cursor(xEntry.get())) + { + auto aRect = mxTreeView->get_row_area(*xEntry); + const Point aPos(aRect.getWidth() / 2, aRect.getHeight() / 2); + const CommandEvent aCEvt(aPos, CommandEventId::ContextMenu); + CommandHdl(aCEvt); + return true; + } + } + } + return false; +} + +/** selects or deselects the given effect. + Selections of other effects are not changed */ +void CustomAnimationList::select( const CustomAnimationEffectPtr& pEffect ) +{ + CustomAnimationListEntryItem* pEntry = nullptr; + + std::unique_ptr<weld::TreeIter> xEntry = mxTreeView->make_iterator(); + if (mxTreeView->get_iter_first(*xEntry)) + { + do + { + CustomAnimationListEntryItem* pTestEntry = weld::fromId<CustomAnimationListEntryItem*>(mxTreeView->get_id(*xEntry)); + if (pTestEntry->getEffect() == pEffect) + { + mxTreeView->select(*xEntry); + mxTreeView->scroll_to_row(*xEntry); + pEntry = pTestEntry; + break; + } + } while (mxTreeView->iter_next(*xEntry)); + } + + if( !pEntry ) + { + append( pEffect ); + select( pEffect ); + } +} + +void CustomAnimationList::clear() +{ + mxEntries.clear(); + mxTreeView->clear(); + + mxEmptyLabelParent->show(); + mxTreeView->hide(); + + mxLastParentEntry.reset(); + mxLastTargetShape = nullptr; +} + +void CustomAnimationList::update( const MainSequencePtr& pMainSequence ) +{ + if( mpMainSequence ) + mpMainSequence->removeListener( this ); + + mpMainSequence = pMainSequence; + update(); + + if( mpMainSequence ) + mpMainSequence->addListener( this ); +} + +struct stl_append_effect_func +{ + explicit stl_append_effect_func( CustomAnimationList& rList ) : mrList( rList ) {} + void operator()(const CustomAnimationEffectPtr& pEffect); + CustomAnimationList& mrList; +}; + +void stl_append_effect_func::operator()(const CustomAnimationEffectPtr& pEffect) +{ + mrList.append( pEffect ); +} + +void CustomAnimationList::update() +{ + mbIgnorePaint = true; + + std::vector< CustomAnimationEffectPtr > aVisible; + std::vector< CustomAnimationEffectPtr > aSelected; + CustomAnimationEffectPtr aCurrent; + + CustomAnimationEffectPtr pFirstSelEffect; + CustomAnimationEffectPtr pLastSelEffect; + ::tools::Long nFirstVis = -1; + ::tools::Long nLastVis = -1; + ::tools::Long nFirstSelOld = -1; + ::tools::Long nLastSelOld = -1; + + std::unique_ptr<weld::TreeIter> xEntry = mxTreeView->make_iterator(); + + if( mpMainSequence ) + { + std::unique_ptr<weld::TreeIter> xLastSelectedEntry; + std::unique_ptr<weld::TreeIter> xLastVisibleEntry; + + // save selection, current, and expand (visible) states + mxTreeView->all_foreach([this, &aVisible, &nFirstVis, &xLastVisibleEntry, + &aSelected, &nFirstSelOld, &pFirstSelEffect, &xLastSelectedEntry](weld::TreeIter& rEntry){ + CustomAnimationListEntryItem* pEntry = weld::fromId<CustomAnimationListEntryItem*>(mxTreeView->get_id(rEntry)); + CustomAnimationEffectPtr pEffect(pEntry->getEffect()); + if (pEffect) + { + if (weld::IsEntryVisible(*mxTreeView, rEntry)) + { + aVisible.push_back(pEffect); + // save scroll position + if (nFirstVis == -1) + nFirstVis = weld::GetAbsPos(*mxTreeView, rEntry); + if (!xLastVisibleEntry) + xLastVisibleEntry = mxTreeView->make_iterator(&rEntry); + else + mxTreeView->copy_iterator(rEntry, *xLastVisibleEntry); + } + + if (mxTreeView->is_selected(rEntry)) + { + aSelected.push_back(pEffect); + if (nFirstSelOld == -1) + { + pFirstSelEffect = pEffect; + nFirstSelOld = weld::GetAbsPos(*mxTreeView, rEntry); + } + if (!xLastSelectedEntry) + xLastSelectedEntry = mxTreeView->make_iterator(&rEntry); + else + mxTreeView->copy_iterator(rEntry, *xLastSelectedEntry); + } + } + + return false; + }); + + if (xLastSelectedEntry) + { + CustomAnimationListEntryItem* pEntry = weld::fromId<CustomAnimationListEntryItem*>(mxTreeView->get_id(*xLastSelectedEntry)); + pLastSelEffect = pEntry->getEffect(); + nLastSelOld = weld::GetAbsPos(*mxTreeView, *xLastSelectedEntry); + } + + if (xLastVisibleEntry) + nLastVis = weld::GetAbsPos(*mxTreeView, *xLastVisibleEntry); + + if (mxTreeView->get_cursor(xEntry.get())) + { + CustomAnimationListEntryItem* pEntry = weld::fromId<CustomAnimationListEntryItem*>(mxTreeView->get_id(*xEntry)); + aCurrent = pEntry->getEffect(); + } + } + + // rebuild list + + mxTreeView->freeze(); + + clear(); + + if (mpMainSequence) + { + std::for_each( mpMainSequence->getBegin(), mpMainSequence->getEnd(), stl_append_effect_func( *this ) ); + mxLastParentEntry.reset(); + + auto rInteractiveSequenceVector = mpMainSequence->getInteractiveSequenceVector(); + + for (InteractiveSequencePtr const& pIS : rInteractiveSequenceVector) + { + Reference< XShape > xShape( pIS->getTriggerShape() ); + if( xShape.is() ) + { + OUString aDescription = SdResId(STR_CUSTOMANIMATION_TRIGGER) + ": " + + getShapeDescription( xShape, false ); + + mxEntries.emplace_back(std::make_unique<CustomAnimationListEntryItem>(aDescription, nullptr)); + + OUString sId(weld::toId(mxEntries.back().get())); + mxTreeView->insert(nullptr, -1, &aDescription, &sId, nullptr, nullptr, false, nullptr); + std::for_each( pIS->getBegin(), pIS->getEnd(), stl_append_effect_func( *this ) ); + mxLastParentEntry.reset(); + } + } + } + + mxTreeView->thaw(); + + if (mxTreeView->n_children()) + { + mxEmptyLabelParent->hide(); + mxTreeView->show(); + } + + if (mpMainSequence) + { + ::tools::Long nFirstSelNew = -1; + ::tools::Long nLastSelNew = -1; + + std::vector<std::unique_ptr<weld::TreeIter>> aNewSelection; + + // restore selection state, expand state, and current-entry (under cursor) + if (mxTreeView->get_iter_first(*xEntry)) + { + do + { + CustomAnimationListEntryItem* pEntry = weld::fromId<CustomAnimationListEntryItem*>(mxTreeView->get_id(*xEntry)); + + CustomAnimationEffectPtr pEffect( pEntry->getEffect() ); + if (pEffect) + { + // Any effects that were visible should still be visible, so expand their parents. + // (a previously expanded parent may have moved leaving a child to now be the new parent to expand) + if( std::find( aVisible.begin(), aVisible.end(), pEffect ) != aVisible.end() ) + { + if (mxTreeView->get_iter_depth(*xEntry)) + { + std::unique_ptr<weld::TreeIter> xParentEntry = mxTreeView->make_iterator(xEntry.get()); + mxTreeView->iter_parent(*xParentEntry); + mxTreeView->expand_row(*xParentEntry); + } + } + + if( std::find( aSelected.begin(), aSelected.end(), pEffect ) != aSelected.end() ) + aNewSelection.emplace_back(mxTreeView->make_iterator(xEntry.get())); + + // Restore the cursor, as it may deselect other effects wait until + // after the loop to reset the selection + if( pEffect == aCurrent ) + mxTreeView->set_cursor(*xEntry); + + if (pEffect == pFirstSelEffect) + nFirstSelNew = weld::GetAbsPos(*mxTreeView, *xEntry); + + if (pEffect == pLastSelEffect) + nLastSelNew = weld::GetAbsPos(*mxTreeView, *xEntry); + } + } while (mxTreeView->iter_next(*xEntry)); + } + + // tdf#147032 unselect what previous set_cursor may have caused to get selected as a side-effect + mxTreeView->unselect_all(); + for (const auto& rEntry : aNewSelection) + mxTreeView->select(*rEntry); + + // Scroll to a selected entry, depending on where the selection moved. + const bool bMoved = nFirstSelNew != nFirstSelOld; + const bool bMovedUp = nFirstSelNew < nFirstSelOld; + const bool bMovedDown = nFirstSelNew > nFirstSelOld; + + if( bMoved && nLastSelOld < nFirstVis && nLastSelNew < nFirstVis ) + { + // The selection is above the visible area. + // Scroll up to show the last few selected entries. + if( nLastSelNew - (nLastVis - nFirstVis) > nFirstSelNew) + { + // The entries in the selection range can't fit in view. + // Scroll so the last selected entry is last in view. + mxTreeView->vadjustment_set_value(nLastSelNew - (nLastVis - nFirstVis)); + } + else + mxTreeView->vadjustment_set_value(nFirstSelNew); + } + else if( bMoved && nFirstSelOld > nLastVis && nFirstSelNew > nLastVis ) + { + // The selection is below the visible area. + // Scroll down to the first few selected entries. + mxTreeView->vadjustment_set_value(nFirstSelNew); + } + else if( bMovedUp && nFirstSelOld <= nFirstVis ) + { + // A visible entry has moved up out of view; scroll up one. + mxTreeView->vadjustment_set_value(nFirstVis - 1); + } + else if( bMovedDown && nLastSelOld >= nLastVis ) + { + // An entry has moved down out of view; scroll down one. + mxTreeView->vadjustment_set_value(nFirstVis + 1); + } + else if ( nFirstVis != -1 ) + { + // The selection is still in view, or it hasn't moved. + mxTreeView->vadjustment_set_value(nFirstVis); + } + } + + mbIgnorePaint = false; + + Select(); +} + +void CustomAnimationList::append( CustomAnimationEffectPtr pEffect ) +{ + Any aTarget( pEffect->getTarget() ); + if( !aTarget.hasValue() ) + return; + + try + { + // create a ui description + OUString aDescription = getDescription(aTarget, pEffect->getTargetSubItem() != ShapeAnimationSubType::ONLY_BACKGROUND); + + std::unique_ptr<weld::TreeIter> xParentEntry; + + Reference< XShape > xTargetShape( pEffect->getTargetShape() ); + sal_Int32 nGroupId = pEffect->getGroupId(); + + // if this effect has the same target and group-id as the last root effect, + // the last root effect is also this effects parent + if (mxLastParentEntry && nGroupId != -1 && mxLastTargetShape == xTargetShape && mnLastGroupId == nGroupId) + xParentEntry = mxTreeView->make_iterator(mxLastParentEntry.get()); + + // create an entry for the effect + std::unique_ptr<weld::TreeIter> xEntry = mxTreeView->make_iterator(); + + mxEntries.emplace_back(std::make_unique<CustomAnimationListEntryItem>(aDescription, pEffect)); + + OUString sId(weld::toId(mxEntries.back().get())); + + if (xParentEntry) + { + // add a subentry + mxTreeView->insert(xParentEntry.get(), -1, &aDescription, &sId, nullptr, nullptr, false, xEntry.get()); + } + else + { + // add a root entry + mxTreeView->insert(nullptr, -1, &aDescription, &sId, nullptr, nullptr, false, xEntry.get()); + + // and the new root entry becomes the possible next group header + mxLastTargetShape = xTargetShape; + mnLastGroupId = nGroupId; + mxLastParentEntry = std::move(xEntry); + } + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationList::append()" ); + } +} + +static void selectShape(weld::TreeView* pTreeList, const Reference< XShape >& xShape ) +{ + std::unique_ptr<weld::TreeIter> xEntry = pTreeList->make_iterator(); + if (!pTreeList->get_iter_first(*xEntry)) + return; + + bool bFirstEntry = true; + + do + { + CustomAnimationListEntryItem* pEntry = weld::fromId<CustomAnimationListEntryItem*>(pTreeList->get_id(*xEntry)); + CustomAnimationEffectPtr pEffect(pEntry->getEffect()); + if (pEffect) + { + if (pEffect->getTarget() == xShape) + { + pTreeList->select(*xEntry); + if (bFirstEntry) + { + pTreeList->scroll_to_row(*xEntry); + bFirstEntry = false; + } + } + } + } while (pTreeList->iter_next(*xEntry)); +} + +void CustomAnimationList::onSelectionChanged(const Any& rSelection) +{ + try + { + mxTreeView->unselect_all(); + + if (rSelection.hasValue()) + { + Reference< XIndexAccess > xShapes(rSelection, UNO_QUERY); + if( xShapes.is() ) + { + sal_Int32 nCount = xShapes->getCount(); + sal_Int32 nIndex; + for( nIndex = 0; nIndex < nCount; nIndex++ ) + { + Reference< XShape > xShape( xShapes->getByIndex( nIndex ), UNO_QUERY ); + if( xShape.is() ) + selectShape(mxTreeView.get(), xShape); + } + } + else + { + Reference< XShape > xShape(rSelection, UNO_QUERY); + if( xShape.is() ) + selectShape(mxTreeView.get(), xShape); + } + } + + Select(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationList::onSelectionChanged()" ); + } +} + +IMPL_LINK_NOARG(CustomAnimationList, SelectHdl, weld::TreeView&, void) +{ + Select(); +} + +// Notify controller to refresh UI when we are notified of selection change from base class +void CustomAnimationList::Select() +{ + if( mbIgnorePaint ) + return; + mpController->onSelect(); +} + +IMPL_LINK_NOARG(CustomAnimationList, PostExpandHdl, void*, void) +{ + std::unique_ptr<weld::TreeIter> xEntry = mxTreeView->make_iterator(); + if (mxTreeView->get_selected(xEntry.get())) + { + for (bool bChild = mxTreeView->iter_children(*xEntry); bChild; bChild = mxTreeView->iter_next_sibling(*xEntry)) + { + if (!mxTreeView->is_selected(*xEntry)) + mxTreeView->select(*xEntry); + } + } + + // Notify controller that selection has changed (it should update the UI) + mpController->onSelect(); + + mnPostExpandEvent = nullptr; +} + +IMPL_LINK(CustomAnimationList, ExpandHdl, const weld::TreeIter&, rParent, bool) +{ + // If expanded entry is selected, then select its children too afterwards. + if (mxTreeView->is_selected(rParent) && !mnPostExpandEvent) { + mnPostExpandEvent = Application::PostUserEvent(LINK(this, CustomAnimationList, PostExpandHdl)); + } + + return true; +} + +IMPL_LINK_NOARG(CustomAnimationList, PostCollapseHdl, void*, void) +{ + // Deselect all entries as SvTreeListBox::Collapse selects the last + // entry to have focus (or its parent), which is not desired + mxTreeView->unselect_all(); + + // Restore selection state for entries which are still visible + for (const auto &pEntry : lastSelectedEntries) + { + if (weld::IsEntryVisible(*mxTreeView, *pEntry)) + mxTreeView->select(*pEntry); + } + + lastSelectedEntries.clear(); + + // Notify controller that selection has changed (it should update the UI) + mpController->onSelect(); + + mnPostCollapseEvent = nullptr; +} + +IMPL_LINK_NOARG(CustomAnimationList, CollapseHdl, const weld::TreeIter&, bool) +{ + if (!mnPostCollapseEvent) + { + // weld::TreeView::collapse() discards multi-selection state + // of list entries, so first save current selection state + mxTreeView->selected_foreach([this](weld::TreeIter& rEntry){ + lastSelectedEntries.emplace_back(mxTreeView->make_iterator(&rEntry)); + return false; + }); + + mnPostCollapseEvent = Application::PostUserEvent(LINK(this, CustomAnimationList, PostCollapseHdl)); + } + + // Execute collapse on base class + return true; +} + +bool CustomAnimationList::isExpanded( const CustomAnimationEffectPtr& pEffect ) const +{ + bool bExpanded = true; // we assume expanded by default + + std::unique_ptr<weld::TreeIter> xEntry = mxTreeView->make_iterator(); + if (mxTreeView->get_iter_first(*xEntry)) + { + do + { + CustomAnimationListEntryItem* pEntry = + weld::fromId<CustomAnimationListEntryItem*>(mxTreeView->get_id(*xEntry)); + if (pEntry->getEffect() == pEffect) + { + if (mxTreeView->get_iter_depth(*xEntry)) // no parent, keep expanded default of true + { + std::unique_ptr<weld::TreeIter> xParentEntry = mxTreeView->make_iterator(xEntry.get()); + if (mxTreeView->iter_parent(*xParentEntry)) + bExpanded = mxTreeView->get_row_expanded(*xParentEntry); + } + break; + } + } while (mxTreeView->iter_next(*xEntry)); + } + + return bExpanded; +} + +bool CustomAnimationList::isVisible(const CustomAnimationEffectPtr& pEffect) const +{ + std::unique_ptr<weld::TreeIter> xEntry = mxTreeView->make_iterator(); + if (mxTreeView->get_iter_first(*xEntry)) + { + do + { + CustomAnimationListEntryItem* pTestEntry = weld::fromId<CustomAnimationListEntryItem*>(mxTreeView->get_id(*xEntry)); + if (pTestEntry->getEffect() == pEffect) + return weld::IsEntryVisible(*mxTreeView, *xEntry); + } while (mxTreeView->iter_next(*xEntry)); + } + return true; +} + +EffectSequence CustomAnimationList::getSelection() const +{ + EffectSequence aSelection; + + mxTreeView->selected_foreach([this, &aSelection](weld::TreeIter& rEntry){ + CustomAnimationListEntryItem* pEntry = weld::fromId<CustomAnimationListEntryItem*>(mxTreeView->get_id(rEntry)); + CustomAnimationEffectPtr pEffect(pEntry->getEffect()); + if (pEffect) + aSelection.push_back(pEffect); + + // if the selected effect is not expanded and has children + // we say that the children are automatically selected + if (!mxTreeView->get_row_expanded(rEntry) && mxTreeView->iter_has_child(rEntry)) + { + std::unique_ptr<weld::TreeIter> xChild = mxTreeView->make_iterator(&rEntry); + (void)mxTreeView->iter_children(*xChild); + + do + { + if (!mxTreeView->is_selected(*xChild)) + { + CustomAnimationListEntryItem* pChild = weld::fromId<CustomAnimationListEntryItem*>(mxTreeView->get_id(*xChild)); + const CustomAnimationEffectPtr& pChildEffect( pChild->getEffect() ); + if( pChildEffect ) + aSelection.push_back( pChildEffect ); + } + } while (mxTreeView->iter_next_sibling(*xChild)); + } + + return false; + }); + + return aSelection; +} + +IMPL_LINK_NOARG(CustomAnimationList, DoubleClickHdl, weld::TreeView&, bool) +{ + mpController->onDoubleClick(); + return false; +} + +IMPL_LINK(CustomAnimationList, CommandHdl, const CommandEvent&, rCEvt, bool) +{ + if (rCEvt.GetCommand() != CommandEventId::ContextMenu) + return false; + + if (rCEvt.IsMouseEvent()) + { + ::Point aPos = rCEvt.GetMousePosPixel(); + std::unique_ptr<weld::TreeIter> xIter(mxTreeView->make_iterator()); + if (mxTreeView->get_dest_row_at_pos(aPos, xIter.get(), false) && !mxTreeView->is_selected(*xIter)) + { + mxTreeView->unselect_all(); + mxTreeView->set_cursor(*xIter); + mxTreeView->select(*xIter); + SelectHdl(*mxTreeView); + } + } + + if (!mxTreeView->get_selected(nullptr)) + return false; + + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(mxTreeView.get(), "modules/simpress/ui/effectmenu.ui")); + std::unique_ptr<weld::Menu> xMenu = xBuilder->weld_menu("menu"); + + sal_Int16 nNodeType = -1; + sal_Int16 nEntries = 0; + + mxTreeView->selected_foreach([this, &nNodeType, &nEntries](weld::TreeIter& rEntry){ + CustomAnimationListEntryItem* pEntry = weld::fromId<CustomAnimationListEntryItem*>(mxTreeView->get_id(rEntry)); + CustomAnimationEffectPtr pEffect(pEntry->getEffect()); + + nEntries++; + if (pEffect) + { + if( nNodeType == -1 ) + { + nNodeType = pEffect->getNodeType(); + } + else + { + if( nNodeType != pEffect->getNodeType() ) + { + nNodeType = -1; + return true; + } + } + } + + return false; + }); + + xMenu->set_active("onclick", nNodeType == EffectNodeType::ON_CLICK); + xMenu->set_active("withprev", nNodeType == EffectNodeType::WITH_PREVIOUS); + xMenu->set_active("afterprev", nNodeType == EffectNodeType::AFTER_PREVIOUS); + xMenu->set_sensitive("options", nEntries == 1); + xMenu->set_sensitive("timing", nEntries == 1); + + OString sCommand = xMenu->popup_at_rect(mxTreeView.get(), ::tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1))); + if (!sCommand.isEmpty()) + ExecuteContextMenuAction(sCommand); + + return true; +} + +void CustomAnimationList::ExecuteContextMenuAction(const OString& rIdent) +{ + mpController->onContextMenu(rIdent); +} + +void CustomAnimationList::notify_change() +{ + update(); + mpController->onSelect(); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/animations/CustomAnimationPane.cxx b/sd/source/ui/animations/CustomAnimationPane.cxx new file mode 100644 index 000000000..0910ba96e --- /dev/null +++ b/sd/source/ui/animations/CustomAnimationPane.cxx @@ -0,0 +1,2578 @@ +/* -*- 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 <com/sun/star/presentation/EffectPresetClass.hpp> +#include <com/sun/star/animations/XAnimationNodeSupplier.hpp> +#include <com/sun/star/animations/AnimationNodeType.hpp> +#include <com/sun/star/animations/ParallelTimeContainer.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include <com/sun/star/document/XActionLockable.hpp> +#include <com/sun/star/drawing/XDrawView.hpp> +#include <com/sun/star/drawing/XShape.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/presentation/EffectNodeType.hpp> +#include <com/sun/star/presentation/EffectCommands.hpp> +#include <com/sun/star/animations/AnimationTransformType.hpp> +#include <com/sun/star/text/XTextRangeCompare.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/presentation/ParagraphTarget.hpp> +#include <com/sun/star/text/XText.hpp> +#include <com/sun/star/drawing/LineStyle.hpp> +#include <com/sun/star/drawing/FillStyle.hpp> +#include <comphelper/processfactory.hxx> +#include <comphelper/scopeguard.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <tools/debug.hxx> +#include "STLPropertySet.hxx" +#include <CustomAnimationPane.hxx> +#include "CustomAnimationDialog.hxx" +#include <CustomAnimationList.hxx> +#include "motionpathtag.hxx" +#include <CustomAnimationPreset.hxx> + +#include <comphelper/lok.hxx> +#include <comphelper/sequence.hxx> +#include <sfx2/frame.hxx> +#include <tools/diagnose_ex.h> + +#include <svx/svxids.hrc> +#include <DrawDocShell.hxx> +#include <ViewShellBase.hxx> +#include <DrawViewShell.hxx> +#include <DrawController.hxx> +#include <sdresid.hxx> +#include <drawview.hxx> +#include <slideshow.hxx> +#include <undoanim.hxx> +#include <optsitem.hxx> +#include <sdmod.hxx> +#include <framework/FrameworkHelper.hxx> + +#include <EventMultiplexer.hxx> + +#include <strings.hrc> +#include <sdpage.hxx> +#include <app.hrc> + +#include <svx/strings.hrc> +#include <svx/dialmgr.hxx> + +#include <algorithm> +#include <memory> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::animations; +using namespace ::com::sun::star::presentation; +using namespace ::com::sun::star::text; + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing; +using ::com::sun::star::view::XSelectionSupplier; +using ::com::sun::star::beans::XPropertySet; +using ::com::sun::star::container::XIndexAccess; +using ::com::sun::star::container::XEnumerationAccess; +using ::com::sun::star::container::XEnumeration; +using ::com::sun::star::text::XText; +using ::sd::framework::FrameworkHelper; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Exception; + +namespace sd { + +void fillRepeatComboBox(weld::ComboBox& rBox) +{ + OUString aNone( SdResId( STR_CUSTOMANIMATION_REPEAT_NONE ) ); + rBox.append_text(aNone); + rBox.append_text(OUString::number(2)); + rBox.append_text(OUString::number(3)); + rBox.append_text(OUString::number(4)); + rBox.append_text(OUString::number(5)); + rBox.append_text(OUString::number(10)); + + OUString aUntilClick( SdResId( STR_CUSTOMANIMATION_REPEAT_UNTIL_NEXT_CLICK ) ); + rBox.append_text(aUntilClick); + + OUString aEndOfSlide( SdResId( STR_CUSTOMANIMATION_REPEAT_UNTIL_END_OF_SLIDE ) ); + rBox.append_text(aEndOfSlide); +} + +CustomAnimationPane::CustomAnimationPane( weld::Widget* pParent, ViewShellBase& rBase ) + : PanelLayout(pParent, "CustomAnimationsPanel", "modules/simpress/ui/customanimationspanel.ui") + , mrBase(rBase) + // load resources + , mxFTAnimation(m_xBuilder->weld_label("effectlabel")) + , mxCustomAnimationList(new CustomAnimationList(m_xBuilder->weld_tree_view("custom_animation_list"), + m_xBuilder->weld_label("custom_animation_label"), + m_xBuilder->weld_widget("custom_animation_label_parent"))) + , mxPBAddEffect(m_xBuilder->weld_button("add_effect")) + , mxPBRemoveEffect(m_xBuilder->weld_button("remove_effect")) + , mxPBMoveUp(m_xBuilder->weld_button("move_up")) + , mxPBMoveDown(m_xBuilder->weld_button("move_down")) + , mxFTCategory(m_xBuilder->weld_label("categorylabel")) + , mxLBCategory(m_xBuilder->weld_combo_box("categorylb")) + , mxFTEffect(m_xBuilder->weld_label("effect_label")) + , mxLBAnimation(m_xBuilder->weld_tree_view("effect_list")) + , mxFTStart(m_xBuilder->weld_label("start_effect")) + , mxLBStart(m_xBuilder->weld_combo_box("start_effect_list")) + , mxFTProperty(m_xBuilder->weld_label("effect_property")) + , mxPlaceholderBox(m_xBuilder->weld_container("placeholder")) + , mxPBPropertyMore(m_xBuilder->weld_button("more_properties")) + , mxFTDuration(m_xBuilder->weld_label("effect_duration")) + , mxCBXDuration(m_xBuilder->weld_metric_spin_button("anim_duration", FieldUnit::SECOND)) + , mxFTStartDelay(m_xBuilder->weld_label("delay_label")) + , mxMFStartDelay(m_xBuilder->weld_metric_spin_button("delay_value", FieldUnit::SECOND)) + , mxCBAutoPreview(m_xBuilder->weld_check_button("auto_preview")) + , mxPBPlay(m_xBuilder->weld_button("play")) + , maIdle("sd idle treeview select") + , mnLastSelectedAnimation(-1) + , mnPropertyType(nPropertyTypeNone) + , mnCurvePathPos(-1) + , mnPolygonPathPos(-1) + , mnFreeformPathPos(-1) + , maLateInitTimer("sd CustomAnimationPane maLateInitTimer") +{ + initialize(); +} + +css::ui::LayoutSize CustomAnimationPane::GetHeightForWidth(const sal_Int32 /*nWidth*/) +{ + sal_Int32 nMinimumHeight = get_preferred_size().Height(); + return css::ui::LayoutSize(nMinimumHeight, -1, nMinimumHeight); +} + +void CustomAnimationPane::initialize() +{ + mxLBAnimation->connect_changed(LINK(this, CustomAnimationPane, AnimationSelectHdl)); + mxCustomAnimationList->setController( static_cast<ICustomAnimationListController*> ( this ) ); + mxCustomAnimationList->set_size_request(mxCustomAnimationList->get_approximate_digit_width() * 15, + mxCustomAnimationList->get_height_rows(4)); + + mxLBAnimation->set_size_request(mxLBAnimation->get_approximate_digit_width() * 15, + mxLBAnimation->get_height_rows(4)); + + maStrProperty = mxFTProperty->get_label(); + + mxPBAddEffect->connect_clicked( LINK( this, CustomAnimationPane, implClickHdl ) ); + mxPBRemoveEffect->connect_clicked( LINK( this, CustomAnimationPane, implClickHdl ) ); + mxLBStart->connect_changed( LINK( this, CustomAnimationPane, implControlListBoxHdl ) ); + mxCBXDuration->connect_value_changed(LINK( this, CustomAnimationPane, DurationModifiedHdl)); + mxPBPropertyMore->connect_clicked( LINK( this, CustomAnimationPane, implClickHdl ) ); + mxPBMoveUp->connect_clicked( LINK( this, CustomAnimationPane, implClickHdl ) ); + mxPBMoveDown->connect_clicked( LINK( this, CustomAnimationPane, implClickHdl ) ); + mxPBPlay->connect_clicked( LINK( this, CustomAnimationPane, implClickHdl ) ); + mxCBAutoPreview->connect_toggled( LINK( this, CustomAnimationPane, implToggleHdl ) ); + mxLBCategory->connect_changed( LINK(this, CustomAnimationPane, UpdateAnimationLB) ); + mxMFStartDelay->connect_value_changed( LINK(this, CustomAnimationPane, DelayModifiedHdl) ); + mxMFStartDelay->connect_focus_out(LINK( this, CustomAnimationPane, DelayLoseFocusHdl)); + + maIdle.SetPriority(TaskPriority::DEFAULT); + maIdle.SetInvokeHandler(LINK(this, CustomAnimationPane, SelectionHandler)); + + maStrModify = mxFTEffect->get_label(); + + // get current controller and initialize listeners + try + { + mxView.set(mrBase.GetController(), UNO_QUERY); + addListener(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationPane::CustomAnimationPane()" ); + } + + // tdf#137637 keep user selection during initialization + ScopeLockGuard aGuard(maSelectionLock); + // get current page and update custom animation list + onChangeCurrentPage(); + + // Wait a short time before the presets list is created. This gives the + // system time to paint the control. + maLateInitTimer.SetTimeout(100); + maLateInitTimer.SetInvokeHandler(LINK(this, CustomAnimationPane, lateInitCallback)); + maLateInitTimer.Start(); +} + +CustomAnimationPane::~CustomAnimationPane() +{ + maLateInitTimer.Stop(); + + removeListener(); + + MotionPathTagVector aTags; + aTags.swap( maMotionPathTags ); + for (auto const& tag : aTags) + tag->Dispose(); + + mxPBAddEffect.reset(); + mxPBRemoveEffect.reset(); + mxFTEffect.reset(); + mxFTStart.reset(); + mxLBStart.reset(); + mxLBSubControl.reset(); + mxFTProperty.reset(); + mxPlaceholderBox.reset(); + mxPBPropertyMore.reset(); + mxFTDuration.reset(); + mxCBXDuration.reset(); + mxFTStartDelay.reset(); + mxMFStartDelay.reset(); + mxCustomAnimationList.reset(); + mxPBMoveUp.reset(); + mxPBMoveDown.reset(); + mxPBPlay.reset(); + mxCBAutoPreview.reset(); + mxFTCategory.reset(); + mxLBCategory.reset(); + mxFTAnimation.reset(); + mxLBAnimation.reset(); +} + +void CustomAnimationPane::addUndo() +{ + SfxUndoManager* pManager = mrBase.GetDocShell()->GetUndoManager(); + if( pManager ) + { + SdPage* pPage = SdPage::getImplementation( mxCurrentPage ); + if( pPage ) + pManager->AddUndoAction( std::make_unique<UndoAnimation>( mrBase.GetDocShell()->GetDoc(), pPage ) ); + } +} + +void CustomAnimationPane::addListener() +{ + Link<tools::EventMultiplexerEvent&,void> aLink( LINK(this,CustomAnimationPane,EventMultiplexerListener) ); + mrBase.GetEventMultiplexer()->AddEventListener(aLink); +} + +void CustomAnimationPane::removeListener() +{ + Link<tools::EventMultiplexerEvent&,void> aLink( LINK(this,CustomAnimationPane,EventMultiplexerListener) ); + mrBase.GetEventMultiplexer()->RemoveEventListener( aLink ); +} + +IMPL_LINK(CustomAnimationPane,EventMultiplexerListener, + tools::EventMultiplexerEvent&, rEvent, void) +{ + switch (rEvent.meEventId) + { + case EventMultiplexerEventId::EditViewSelection: + onSelectionChanged(); + break; + + case EventMultiplexerEventId::CurrentPageChanged: + onChangeCurrentPage(); + break; + + case EventMultiplexerEventId::MainViewAdded: + // At this moment the controller may not yet been set at model + // or ViewShellBase. Take it from the view shell passed with + // the event. + if (mrBase.GetMainViewShell() != nullptr) + { + if( mrBase.GetMainViewShell()->GetShellType() == ViewShell::ST_IMPRESS ) + { + mxView.set(mrBase.GetDrawController(), UNO_QUERY); + onSelectionChanged(); + onChangeCurrentPage(); + break; + } + } + [[fallthrough]]; + case EventMultiplexerEventId::MainViewRemoved: + mxView = nullptr; + mxCurrentPage = nullptr; + updateControls(); + break; + + case EventMultiplexerEventId::Disposing: + mxView.clear(); + onSelectionChanged(); + onChangeCurrentPage(); + break; + case EventMultiplexerEventId::EndTextEdit: + if (mpMainSequence && rEvent.mpUserData) + mxCustomAnimationList->update( mpMainSequence ); + break; + default: break; + } +} + +static sal_Int32 getPropertyType( std::u16string_view rProperty ) +{ + if ( rProperty == u"Direction" ) + return nPropertyTypeDirection; + + if ( rProperty == u"Spokes" ) + return nPropertyTypeSpokes; + + if ( rProperty == u"Zoom" ) + return nPropertyTypeZoom; + + if ( rProperty == u"Accelerate" ) + return nPropertyTypeAccelerate; + + if ( rProperty == u"Decelerate" ) + return nPropertyTypeDecelerate; + + if ( rProperty == u"Color1" ) + return nPropertyTypeFirstColor; + + if ( rProperty == u"Color2" ) + return nPropertyTypeSecondColor; + + if ( rProperty == u"FillColor" ) + return nPropertyTypeFillColor; + + if ( rProperty == u"ColorStyle" ) + return nPropertyTypeColorStyle; + + if ( rProperty == u"AutoReverse" ) + return nPropertyTypeAutoReverse; + + if ( rProperty == u"FontStyle" ) + return nPropertyTypeFont; + + if ( rProperty == u"CharColor" ) + return nPropertyTypeCharColor; + + if ( rProperty == u"CharHeight" ) + return nPropertyTypeCharHeight; + + if ( rProperty == u"CharDecoration" ) + return nPropertyTypeCharDecoration; + + if ( rProperty == u"LineColor" ) + return nPropertyTypeLineColor; + + if ( rProperty == u"Rotate" ) + return nPropertyTypeRotate; + + if ( rProperty == u"Transparency" ) + return nPropertyTypeTransparency; + + if ( rProperty == u"Color" ) + return nPropertyTypeColor; + + if ( rProperty == u"Scale" ) + return nPropertyTypeScale; + + return nPropertyTypeNone; +} + +OUString getPropertyName( sal_Int32 nPropertyType ) +{ + switch( nPropertyType ) + { + case nPropertyTypeDirection: + return SdResId(STR_CUSTOMANIMATION_DIRECTION_PROPERTY); + + case nPropertyTypeSpokes: + return SdResId(STR_CUSTOMANIMATION_SPOKES_PROPERTY); + + case nPropertyTypeFirstColor: + return SdResId(STR_CUSTOMANIMATION_FIRST_COLOR_PROPERTY); + + case nPropertyTypeSecondColor: + return SdResId(STR_CUSTOMANIMATION_SECOND_COLOR_PROPERTY); + + case nPropertyTypeZoom: + return SdResId(STR_CUSTOMANIMATION_ZOOM_PROPERTY); + + case nPropertyTypeFillColor: + return SdResId(STR_CUSTOMANIMATION_FILL_COLOR_PROPERTY); + + case nPropertyTypeColorStyle: + return SdResId(STR_CUSTOMANIMATION_STYLE_PROPERTY); + + case nPropertyTypeFont: + return SdResId(STR_CUSTOMANIMATION_FONT_PROPERTY); + + case nPropertyTypeCharHeight: + return SdResId(STR_CUSTOMANIMATION_SIZE_PROPERTY); + + case nPropertyTypeCharColor: + return SdResId(STR_CUSTOMANIMATION_FONT_COLOR_PROPERTY); + + case nPropertyTypeCharHeightStyle: + return SdResId(STR_CUSTOMANIMATION_FONT_SIZE_STYLE_PROPERTY); + + case nPropertyTypeCharDecoration: + return SdResId(STR_CUSTOMANIMATION_FONT_STYLE_PROPERTY); + + case nPropertyTypeLineColor: + return SdResId(STR_CUSTOMANIMATION_LINE_COLOR_PROPERTY); + + case nPropertyTypeRotate: + return SdResId(STR_CUSTOMANIMATION_AMOUNT_PROPERTY); + + case nPropertyTypeColor: + return SdResId(STR_CUSTOMANIMATION_COLOR_PROPERTY); + + case nPropertyTypeTransparency: + return SdResId(STR_CUSTOMANIMATION_AMOUNT_PROPERTY); + + case nPropertyTypeScale: + return SdResId(STR_CUSTOMANIMATION_SCALE_PROPERTY); + } + + return OUString(); +} + +void CustomAnimationPane::updateControls() +{ + mxFTDuration->set_sensitive(mxView.is()); + mxCBXDuration->set_sensitive(mxView.is()); + mxCustomAnimationList->set_sensitive(mxView.is()); + if (comphelper::LibreOfficeKit::isActive()) + { + mxPBPlay->hide(); + mxCBAutoPreview->set_active(false); + mxCBAutoPreview->hide(); + } + else + { + mxPBPlay->set_sensitive(mxView.is()); + mxCBAutoPreview->set_sensitive(mxView.is()); + } + + if (!mxView.is()) + { + mxPBAddEffect->set_sensitive(false); + mxPBRemoveEffect->set_sensitive(false); + mxFTStart->set_sensitive(false); + mxLBStart->set_sensitive(false); + mxPBPropertyMore->set_sensitive(false); + mxPlaceholderBox->set_sensitive(false); + mxFTProperty->set_sensitive(false); + mxFTCategory->set_sensitive(false); + mxLBCategory->set_sensitive(false); + mxFTAnimation->set_sensitive(false); + mxLBAnimation->set_sensitive(false); + mxFTStartDelay->set_sensitive(false); + mxMFStartDelay->set_sensitive(false); + mxLBAnimation->clear(); + mnLastSelectedAnimation = -1; + mxCustomAnimationList->clear(); + return; + } + + const int nSelectionCount = maListSelection.size(); + + mxPBAddEffect->set_sensitive( maViewSelection.hasValue() ); + mxPBRemoveEffect->set_sensitive(nSelectionCount != 0); + bool bIsSelected = (nSelectionCount > 0); + + if(bIsSelected) + { + mxFTAnimation->set_sensitive(true); + mxLBAnimation->set_sensitive(true); + } + else + { + mxFTAnimation->set_sensitive(false); + mxLBAnimation->set_sensitive(false); + mxLBAnimation->clear(); + mnLastSelectedAnimation = -1; + } + + mxLBCategory->set_sensitive(bIsSelected); + mxFTCategory->set_sensitive(bIsSelected); + + mxFTStart->set_sensitive(nSelectionCount > 0); + mxLBStart->set_sensitive(nSelectionCount > 0); + mxPlaceholderBox->set_sensitive(nSelectionCount > 0); + mxPBPropertyMore->set_sensitive(nSelectionCount > 0); + mxFTStartDelay->set_sensitive(nSelectionCount > 0); + mxMFStartDelay->set_sensitive(nSelectionCount > 0); + + mxFTProperty->set_label(maStrProperty); + + sal_Int32 nOldPropertyType = mnPropertyType; + + mnPropertyType = nPropertyTypeNone; + + if(bIsSelected) + { + CustomAnimationEffectPtr pEffect = maListSelection.front(); + + OUString aUIName( CustomAnimationPresets::getCustomAnimationPresets().getUINameForPresetId( pEffect->getPresetId() ) ); + + OUString aTemp( maStrModify ); + + if( !aUIName.isEmpty() ) + { + aTemp += " " + aUIName; + mxFTEffect->set_label( aTemp ); + } + + Any aValue; + CustomAnimationPresetPtr pDescriptor = CustomAnimationPresets::getCustomAnimationPresets().getEffectDescriptor( pEffect->getPresetId() ); + if (pDescriptor) + { + std::vector<OUString> aProperties( pDescriptor->getProperties() ); + if( !aProperties.empty() ) + { + mnPropertyType = getPropertyType( aProperties.front() ); + + mxFTProperty->set_label( getPropertyName( mnPropertyType ) ); + + aValue = getProperty1Value( mnPropertyType, pEffect ); + } + } + + sal_Int32 nNewPropertyType = mnPropertyType; + // if there is no value, then the control will be disabled, just show a disabled Direction box in that + // case to have something to fill the space + if (!aValue.hasValue()) + nNewPropertyType = nPropertyTypeDirection; + + if (!mxLBSubControl || nOldPropertyType != nNewPropertyType) + { + // for LOK destroy old widgets first + mxLBSubControl.reset(nullptr); + // then create new control, to keep correct pointers for actions + mxLBSubControl = SdPropertySubControl::create(nNewPropertyType, mxFTProperty.get(), mxPlaceholderBox.get(), GetFrameWeld(), aValue, pEffect->getPresetId(), LINK(this, CustomAnimationPane, implPropertyHdl)); + } + else + { + mxLBSubControl->setValue(aValue, pEffect->getPresetId()); + } + + bool bEnable = aValue.hasValue(); + mxPlaceholderBox->set_sensitive( bEnable ); + mxFTProperty->set_sensitive( bEnable ); + + if (!pDescriptor) + { + mxPBPropertyMore->set_sensitive( false ); + mxFTStartDelay->set_sensitive( false ); + mxMFStartDelay->set_sensitive( false ); + } + sal_Int32 nCategoryPos = -1; + switch(pEffect->getPresetClass()) + { + case EffectPresetClass::ENTRANCE: nCategoryPos = 0; break; + case EffectPresetClass::EMPHASIS: nCategoryPos = 1; break; + case EffectPresetClass::EXIT: nCategoryPos = 2; break; + case EffectPresetClass::MOTIONPATH: nCategoryPos = 3; break; + default: + break; + } + switch(pEffect->getCommand()) + { + case EffectCommands::TOGGLEPAUSE: + case EffectCommands::STOP: + case EffectCommands::PLAY: + nCategoryPos = 4; break; + default: + break; + } + mxLBCategory->set_active(nCategoryPos); + + fillAnimationLB( pEffect->hasText() ); + + OUString rsPresetId = pEffect->getPresetId(); + sal_Int32 nAnimationPos = mxLBAnimation->n_children(); + while( nAnimationPos-- ) + { + auto pEntryData = weld::fromId<CustomAnimationPresetPtr*>(mxLBAnimation->get_id(nAnimationPos)); + if (pEntryData) + { + CustomAnimationPresetPtr& pPtr = *pEntryData; + if( pPtr && pPtr->getPresetId() == rsPresetId ) + { + mxLBAnimation->select( nAnimationPos ); + mnLastSelectedAnimation = nAnimationPos; + break; + } + } + } + + // If preset id is missing and category is motion path. + if (nAnimationPos < 0 && nCategoryPos == 3) + { + if (rsPresetId == "libo-motionpath-curve") + { + mxLBAnimation->select(mnCurvePathPos); + mnLastSelectedAnimation = mnCurvePathPos; + } + else if (rsPresetId == "libo-motionpath-polygon") + { + mxLBAnimation->select(mnPolygonPathPos); + mnLastSelectedAnimation = mnPolygonPathPos; + } + else if (rsPresetId == "libo-motionpath-freeform-line") + { + mxLBAnimation->select(mnFreeformPathPos); + mnLastSelectedAnimation = mnFreeformPathPos; + } + } + + sal_uInt16 nPos = 0xffff; + + sal_Int16 nNodeType = pEffect->getNodeType(); + switch( nNodeType ) + { + case EffectNodeType::ON_CLICK: nPos = 0; break; + case EffectNodeType::WITH_PREVIOUS: nPos = 1; break; + case EffectNodeType::AFTER_PREVIOUS: nPos = 2; break; + } + + mxLBStart->set_active( nPos ); + + double fDuration = pEffect->getDuration(); + const bool bHasSpeed = fDuration > 0.001; + + mxFTDuration->set_sensitive(bHasSpeed); + mxCBXDuration->set_sensitive(bHasSpeed); + + if( bHasSpeed ) + { + mxCBXDuration->set_value(fDuration*100.0, FieldUnit::NONE); + } + + mxPBPropertyMore->set_sensitive(true); + + mxFTStartDelay->set_sensitive(true); + mxMFStartDelay->set_sensitive(true); + double fBegin = pEffect->getBegin(); + mxMFStartDelay->set_value(fBegin*10.0, FieldUnit::NONE); + } + else + { + // use an empty direction box to fill the space + if (!mxLBSubControl || (nOldPropertyType != nPropertyTypeDirection && nOldPropertyType != nPropertyTypeNone)) + { + // for LOK destroy old widgets first + mxLBSubControl.reset(nullptr); + // then create new control, to keep correct pointers for actions + mxLBSubControl = SdPropertySubControl::create(nPropertyTypeDirection, mxFTProperty.get(), mxPlaceholderBox.get(), GetFrameWeld(), uno::Any(), OUString(), LINK(this, CustomAnimationPane, implPropertyHdl)); + } + else + mxLBSubControl->setValue(uno::Any(), OUString()); + + mxPlaceholderBox->set_sensitive(false); + mxFTProperty->set_sensitive(false); + mxFTStartDelay->set_sensitive(false); + mxMFStartDelay->set_sensitive(false); + mxPBPropertyMore->set_sensitive(false); + mxFTDuration->set_sensitive(false); + mxCBXDuration->set_sensitive(false); + mxCBXDuration->set_text(OUString()); + mxFTEffect->set_label(maStrModify); + } + + bool bEnableUp = true; + bool bEnableDown = true; + if( nSelectionCount == 0 ) + { + bEnableUp = false; + bEnableDown = false; + } + else + { + if( mpMainSequence->find( maListSelection.front() ) == mpMainSequence->getBegin() ) + bEnableUp = false; + + EffectSequence::iterator aIter( mpMainSequence->find( maListSelection.back() ) ); + if( aIter == mpMainSequence->getEnd() ) + { + bEnableDown = false; + } + else + { + do + { + ++aIter; + } + while( (aIter != mpMainSequence->getEnd()) && !(mxCustomAnimationList->isExpanded(*aIter) ) ); + + if( aIter == mpMainSequence->getEnd() ) + bEnableDown = false; + } + + if( bEnableUp || bEnableDown ) + { + MainSequenceRebuildGuard aGuard( mpMainSequence ); + + EffectSequenceHelper* pSequence = nullptr; + for( const CustomAnimationEffectPtr& pEffect : maListSelection ) + { + if( pEffect ) + { + if( pSequence == nullptr ) + { + pSequence = pEffect->getEffectSequence(); + } + else + { + if( pSequence != pEffect->getEffectSequence() ) + { + bEnableUp = false; + bEnableDown = false; + break; + } + } + } + } + } + } + + mxPBMoveUp->set_sensitive(mxView.is() && bEnableUp); + mxPBMoveDown->set_sensitive(mxView.is() && bEnableDown); + + SdOptions* pOptions = SD_MOD()->GetSdOptions(DocumentType::Impress); + mxCBAutoPreview->set_active(pOptions->IsPreviewChangedEffects()); + + updateMotionPathTags(); +} + +static bool updateMotionPathImpl( CustomAnimationPane& rPane, ::sd::View& rView, EffectSequence::iterator aIter, const EffectSequence::iterator& aEnd, MotionPathTagVector& rOldTags, MotionPathTagVector& rNewTags ) +{ + bool bChanges = false; + while( aIter != aEnd ) + { + CustomAnimationEffectPtr pEffect( *aIter++ ); + if( pEffect && pEffect->getPresetClass() == css::presentation::EffectPresetClass::MOTIONPATH ) + { + rtl::Reference< MotionPathTag > xMotionPathTag; + // first try to find if there is already a tag for this + auto aMIter = std::find_if(rOldTags.begin(), rOldTags.end(), + [&pEffect](const rtl::Reference<MotionPathTag>& xTag) { return xTag->getEffect() == pEffect; }); + if (aMIter != rOldTags.end()) + { + rtl::Reference< MotionPathTag > xTag( *aMIter ); + if( !xTag->isDisposed() ) + { + xMotionPathTag = xTag; + rOldTags.erase( aMIter ); + } + } + + // if not found, create new one + if( !xMotionPathTag.is() ) + { + xMotionPathTag.set( new MotionPathTag( rPane, rView, pEffect ) ); + bChanges = true; + } + + if( xMotionPathTag.is() ) + rNewTags.push_back( xMotionPathTag ); + } + } + + return bChanges; +} + +void CustomAnimationPane::updateMotionPathTags() +{ + bool bChanges = false; + + MotionPathTagVector aTags; + aTags.swap( maMotionPathTags ); + + ::sd::View* pView = nullptr; + + if( mxView.is() ) + { + std::shared_ptr<ViewShell> xViewShell( mrBase.GetMainViewShell() ); + if( xViewShell ) + pView = xViewShell->GetView(); + } + + if (mpMainSequence && pView) + { + bChanges = updateMotionPathImpl( *this, *pView, mpMainSequence->getBegin(), mpMainSequence->getEnd(), aTags, maMotionPathTags ); + + auto rInteractiveSequenceVector = mpMainSequence->getInteractiveSequenceVector(); + for (InteractiveSequencePtr const& pIS : rInteractiveSequenceVector) + { + bChanges |= updateMotionPathImpl( *this, *pView, pIS->getBegin(), pIS->getEnd(), aTags, maMotionPathTags ); + } + } + + if( !aTags.empty() ) + { + bChanges = true; + for( rtl::Reference< MotionPathTag >& xTag : aTags ) + { + xTag->Dispose(); + } + } + + if( bChanges && pView ) + pView->updateHandles(); +} + +void CustomAnimationPane::onSelectionChanged() +{ + if( maSelectionLock.isLocked() ) + return; + + ScopeLockGuard aGuard( maSelectionLock ); + + if( mxView.is() ) try + { + Reference< XSelectionSupplier > xSel( mxView, UNO_QUERY_THROW ); + maViewSelection = xSel->getSelection(); + mxCustomAnimationList->onSelectionChanged( maViewSelection ); + updateControls(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationPane::onSelectionChanged()" ); + } +} + +void CustomAnimationPane::onDoubleClick() +{ + showOptions(); +} + +void CustomAnimationPane::onContextMenu(const OString &rIdent) +{ + if (rIdent == "onclick") + onChangeStart( EffectNodeType::ON_CLICK ); + else if (rIdent == "withprev") + onChangeStart( EffectNodeType::WITH_PREVIOUS ); + else if (rIdent == "afterprev") + onChangeStart( EffectNodeType::AFTER_PREVIOUS ); + else if (rIdent == "options") + showOptions(); + else if (rIdent == "timing") + showOptions("timing"); + else if (rIdent == "remove") + onRemove(); + else if (rIdent == "create" && maViewSelection.hasValue()) + onAdd(); + updateControls(); +} + +static void addValue( const std::unique_ptr<STLPropertySet>& pSet, sal_Int32 nHandle, const Any& rValue ) +{ + switch( pSet->getPropertyState( nHandle ) ) + { + case STLPropertyState::Ambiguous: + // value is already ambiguous, do nothing + break; + case STLPropertyState::Direct: + // set to ambiguous if existing value is different + if( rValue != pSet->getPropertyValue( nHandle ) ) + pSet->setPropertyState( nHandle, STLPropertyState::Ambiguous ); + break; + case STLPropertyState::Default: + // just set new value + pSet->setPropertyValue( nHandle, rValue ); + break; + } +} + +static sal_Int32 calcMaxParaDepth( const Reference< XShape >& xTargetShape ) +{ + sal_Int32 nMaxParaDepth = -1; + + if( xTargetShape.is() ) + { + Reference< XEnumerationAccess > xText( xTargetShape, UNO_QUERY ); + if( xText.is() ) + { + Reference< XPropertySet > xParaSet; + + Reference< XEnumeration > xEnumeration( xText->createEnumeration(), UNO_SET_THROW ); + while( xEnumeration->hasMoreElements() ) + { + xEnumeration->nextElement() >>= xParaSet; + if( xParaSet.is() ) + { + sal_Int32 nParaDepth = 0; + xParaSet->getPropertyValue( "NumberingLevel" ) >>= nParaDepth; + + if( nParaDepth > nMaxParaDepth ) + nMaxParaDepth = nParaDepth; + } + } + } + } + + return nMaxParaDepth + 1; +} + +Any CustomAnimationPane::getProperty1Value( sal_Int32 nType, const CustomAnimationEffectPtr& pEffect ) +{ + switch( nType ) + { + case nPropertyTypeDirection: + case nPropertyTypeSpokes: + case nPropertyTypeZoom: + return Any( pEffect->getPresetSubType() ); + + case nPropertyTypeColor: + case nPropertyTypeFillColor: + case nPropertyTypeFirstColor: + case nPropertyTypeSecondColor: + case nPropertyTypeCharColor: + case nPropertyTypeLineColor: + { + const sal_Int32 nIndex = (nPropertyTypeFirstColor == nType) ? 0 : 1; + return pEffect->getColor( nIndex ); + } + + case nPropertyTypeFont: + return pEffect->getProperty( AnimationNodeType::SET, u"CharFontName" , EValue::To ); + + case nPropertyTypeCharHeight: + { + static const OUStringLiteral aAttributeName( u"CharHeight" ); + Any aValue( pEffect->getProperty( AnimationNodeType::SET, aAttributeName, EValue::To ) ); + if( !aValue.hasValue() ) + aValue = pEffect->getProperty( AnimationNodeType::ANIMATE, aAttributeName, EValue::To ); + return aValue; + } + + case nPropertyTypeRotate: + return pEffect->getTransformationProperty( AnimationTransformType::ROTATE, EValue::By); + + case nPropertyTypeTransparency: + return pEffect->getProperty( AnimationNodeType::SET, u"Opacity" , EValue::To ); + + case nPropertyTypeScale: + return pEffect->getTransformationProperty( AnimationTransformType::SCALE, EValue::By ); + + case nPropertyTypeCharDecoration: + { + Sequence< Any > aValues{ + pEffect->getProperty( AnimationNodeType::SET, u"CharWeight" , EValue::To ), + pEffect->getProperty( AnimationNodeType::SET, u"CharPosture" , EValue::To ), + pEffect->getProperty( AnimationNodeType::SET, u"CharUnderline" , EValue::To ) + }; + return Any( aValues ); + } + } + + Any aAny; + return aAny; +} + +bool CustomAnimationPane::setProperty1Value( sal_Int32 nType, const CustomAnimationEffectPtr& pEffect, const Any& rValue ) +{ + bool bEffectChanged = false; + switch( nType ) + { + case nPropertyTypeDirection: + case nPropertyTypeSpokes: + case nPropertyTypeZoom: + { + OUString aPresetSubType; + rValue >>= aPresetSubType; + if( aPresetSubType != pEffect->getPresetSubType() ) + { + CustomAnimationPresets::getCustomAnimationPresets().changePresetSubType( pEffect, aPresetSubType ); + bEffectChanged = true; + } + } + break; + + case nPropertyTypeFillColor: + case nPropertyTypeColor: + case nPropertyTypeFirstColor: + case nPropertyTypeSecondColor: + case nPropertyTypeCharColor: + case nPropertyTypeLineColor: + { + const sal_Int32 nIndex = (nPropertyTypeFirstColor == nType) ? 0 : 1; + Any aOldColor( pEffect->getColor( nIndex ) ); + if( aOldColor != rValue ) + { + pEffect->setColor( nIndex, rValue ); + bEffectChanged = true; + } + } + break; + + case nPropertyTypeFont: + bEffectChanged = pEffect->setProperty( AnimationNodeType::SET, u"CharFontName" , EValue::To, rValue ); + break; + + case nPropertyTypeCharHeight: + { + static const OUStringLiteral aAttributeName( u"CharHeight" ); + bEffectChanged = pEffect->setProperty( AnimationNodeType::SET, aAttributeName, EValue::To, rValue ); + if( !bEffectChanged ) + bEffectChanged = pEffect->setProperty( AnimationNodeType::ANIMATE, aAttributeName, EValue::To, rValue ); + } + break; + case nPropertyTypeRotate: + bEffectChanged = pEffect->setTransformationProperty( AnimationTransformType::ROTATE, EValue::By , rValue ); + break; + + case nPropertyTypeTransparency: + bEffectChanged = pEffect->setProperty( AnimationNodeType::SET, u"Opacity" , EValue::To, rValue ); + break; + + case nPropertyTypeScale: + bEffectChanged = pEffect->setTransformationProperty( AnimationTransformType::SCALE, EValue::By, rValue ); + break; + + case nPropertyTypeCharDecoration: + { + Sequence< Any > aValues(3); + rValue >>= aValues; + bEffectChanged = pEffect->setProperty( AnimationNodeType::SET, u"CharWeight" , EValue::To, aValues[0] ); + bEffectChanged |= pEffect->setProperty( AnimationNodeType::SET, u"CharPosture" , EValue::To, aValues[1] ); + bEffectChanged |= pEffect->setProperty( AnimationNodeType::SET, u"CharUnderline" , EValue::To, aValues[2] ); + } + break; + + } + + return bEffectChanged; +} + +static bool hasVisibleShape( const Reference< XShape >& xShape ) +{ + try + { + const OUString sShapeType( xShape->getShapeType() ); + + if( sShapeType == "com.sun.star.presentation.TitleTextShape" || sShapeType == "com.sun.star.presentation.OutlinerShape" || + sShapeType == "com.sun.star.presentation.SubtitleShape" || sShapeType == "com.sun.star.drawing.TextShape" ) + { + Reference< XPropertySet > xSet( xShape, UNO_QUERY_THROW ); + + FillStyle eFillStyle; + xSet->getPropertyValue( "FillStyle" ) >>= eFillStyle; + + css::drawing::LineStyle eLineStyle; + xSet->getPropertyValue( "LineStyle" ) >>= eLineStyle; + + return eFillStyle != FillStyle_NONE || eLineStyle != css::drawing::LineStyle_NONE; + } + } + catch( Exception& ) + { + } + return true; +} + +std::unique_ptr<STLPropertySet> CustomAnimationPane::createSelectionSet() +{ + std::unique_ptr<STLPropertySet> pSet = CustomAnimationDialog::createDefaultSet(); + + pSet->setPropertyValue( nHandleCurrentPage, Any( mxCurrentPage ) ); + + sal_Int32 nMaxParaDepth = 0; + + // get options from selected effects + const CustomAnimationPresets& rPresets (CustomAnimationPresets::getCustomAnimationPresets()); + for( CustomAnimationEffectPtr& pEffect : maListSelection ) + { + EffectSequenceHelper* pEffectSequence = pEffect->getEffectSequence(); + if( !pEffectSequence ) + pEffectSequence = mpMainSequence.get(); + + if( pEffect->hasText() ) + { + sal_Int32 n = calcMaxParaDepth(pEffect->getTargetShape()); + if( n > nMaxParaDepth ) + nMaxParaDepth = n; + } + + addValue( pSet, nHandleHasAfterEffect, Any( pEffect->hasAfterEffect() ) ); + addValue( pSet, nHandleAfterEffectOnNextEffect, Any( pEffect->IsAfterEffectOnNext() ) ); + addValue( pSet, nHandleDimColor, pEffect->getDimColor() ); + addValue( pSet, nHandleIterateType, Any( pEffect->getIterateType() ) ); + + // convert absolute time to percentage value + // This calculation is done in float to avoid some rounding artifacts. + float fIterateInterval = static_cast<float>(pEffect->getIterateInterval()); + if( pEffect->getDuration() ) + fIterateInterval = static_cast<float>(fIterateInterval / pEffect->getDuration() ); + fIterateInterval *= 100.0; + addValue( pSet, nHandleIterateInterval, Any( static_cast<double>(fIterateInterval) ) ); + + addValue( pSet, nHandleBegin, Any( pEffect->getBegin() ) ); + addValue( pSet, nHandleDuration, Any( pEffect->getDuration() ) ); + addValue( pSet, nHandleStart, Any( pEffect->getNodeType() ) ); + addValue( pSet, nHandleRepeat, pEffect->getRepeatCount() ); + addValue( pSet, nHandleEnd, pEffect->getEnd() ); + addValue( pSet, nHandleRewind, Any( pEffect->getFill() ) ); + + addValue( pSet, nHandlePresetId, Any( pEffect->getPresetId() ) ); + + addValue( pSet, nHandleHasText, Any( pEffect->hasText() ) ); + + addValue( pSet, nHandleHasVisibleShape, Any( hasVisibleShape( pEffect->getTargetShape() ) ) ); + + Any aSoundSource; + if( pEffect->getAudio().is() ) + { + aSoundSource = pEffect->getAudio()->getSource(); + addValue( pSet, nHandleSoundVolume, Any( pEffect->getAudio()->getVolume() ) ); +// todo addValue( pSet, nHandleSoundEndAfterSlide, makeAny( pEffect->getAudio()->getEndAfterSlide() ) ); +// this is now stored at the XCommand parameter sequence + } + else if( pEffect->getCommand() == EffectCommands::STOPAUDIO ) + { + aSoundSource <<= true; + } + addValue( pSet, nHandleSoundURL, aSoundSource ); + + sal_Int32 nGroupId = pEffect->getGroupId(); + CustomAnimationTextGroupPtr pTextGroup; + if( nGroupId != -1 ) + pTextGroup = pEffectSequence->findGroup( nGroupId ); + + addValue( pSet, nHandleTextGrouping, Any( pTextGroup ? pTextGroup->getTextGrouping() : sal_Int32(-1) ) ); + addValue( pSet, nHandleAnimateForm, Any( !pTextGroup || pTextGroup->getAnimateForm() ) ); + addValue( pSet, nHandleTextGroupingAuto, Any( pTextGroup ? pTextGroup->getTextGroupingAuto() : -1.0 ) ); + addValue( pSet, nHandleTextReverse, Any( pTextGroup && pTextGroup->getTextReverse() ) ); + + if( pEffectSequence->getSequenceType() == EffectNodeType::INTERACTIVE_SEQUENCE ) + { + InteractiveSequence* pIS = static_cast< InteractiveSequence* >( pEffectSequence ); + addValue( pSet, nHandleTrigger, Any( pIS->getTriggerShape() ) ); + } + + CustomAnimationPresetPtr pDescriptor = rPresets.getEffectDescriptor( pEffect->getPresetId() ); + if( pDescriptor ) + { + sal_Int32 nType = nPropertyTypeNone; + + std::vector<OUString> aProperties( pDescriptor->getProperties() ); + if( !aProperties.empty() ) + nType = getPropertyType( aProperties.front() ); + + if( nType != nPropertyTypeNone ) + { + addValue( pSet, nHandleProperty1Type, Any( nType ) ); + addValue( pSet, nHandleProperty1Value, getProperty1Value( nType, pEffect ) ); + } + + if( pDescriptor->hasProperty( u"Accelerate" ) ) + { + addValue( pSet, nHandleAccelerate, Any( pEffect->getAcceleration() ) ); + } + + if( pDescriptor->hasProperty( u"Decelerate" ) ) + { + addValue( pSet, nHandleDecelerate, Any( pEffect->getDecelerate() ) ); + } + + if( pDescriptor->hasProperty( u"AutoReverse" ) ) + { + addValue( pSet, nHandleAutoReverse, Any( pEffect->getAutoReverse() ) ); + } + } + } + + addValue( pSet, nHandleMaxParaDepth, Any( nMaxParaDepth ) ); + + return pSet; +} + +void CustomAnimationPane::changeSelection( STLPropertySet const * pResultSet, STLPropertySet const * pOldSet ) +{ + // change selected effect + bool bChanged = false; + + MainSequenceRebuildGuard aGuard( mpMainSequence ); + + for( CustomAnimationEffectPtr& pEffect : maListSelection ) + { + DBG_ASSERT( pEffect->getEffectSequence(), "sd::CustomAnimationPane::changeSelection(), dead effect in selection!" ); + if( !pEffect->getEffectSequence() ) + continue; + + double fDuration = 0.0; // we might need this for iterate-interval + if( pResultSet->getPropertyState( nHandleDuration ) == STLPropertyState::Direct ) + { + pResultSet->getPropertyValue( nHandleDuration ) >>= fDuration; + } + else + { + fDuration = pEffect->getDuration(); + } + + if( pResultSet->getPropertyState( nHandleIterateType ) == STLPropertyState::Direct ) + { + sal_Int16 nIterateType = 0; + pResultSet->getPropertyValue( nHandleIterateType ) >>= nIterateType; + if( pEffect->getIterateType() != nIterateType ) + { + pEffect->setIterateType( nIterateType ); + bChanged = true; + } + } + + if( pEffect->getIterateType() ) + { + if( pResultSet->getPropertyState( nHandleIterateInterval ) == STLPropertyState::Direct ) + { + double fIterateInterval = 0.0; + pResultSet->getPropertyValue( nHandleIterateInterval ) >>= fIterateInterval; + if( pEffect->getIterateInterval() != fIterateInterval ) + { + const double f = fIterateInterval * pEffect->getDuration() / 100; + pEffect->setIterateInterval( f ); + bChanged = true; + } + } + } + + double fBegin = 0.0; + + if( pResultSet->getPropertyState( nHandleBegin ) == STLPropertyState::Direct ) + pResultSet->getPropertyValue( nHandleBegin ) >>= fBegin; + else + fBegin = pEffect->getBegin(); + + if( pEffect->getBegin() != fBegin && pResultSet->getPropertyState( nHandleBegin ) == STLPropertyState::Direct) + { + pEffect->setBegin( fBegin ); + bChanged = true; + } + + if( pResultSet->getPropertyState( nHandleDuration ) == STLPropertyState::Direct ) + { + if( pEffect->getDuration() != fDuration ) + { + pEffect->setDuration( fDuration ); + bChanged = true; + } + } + + if( pResultSet->getPropertyState( nHandleStart ) == STLPropertyState::Direct ) + { + sal_Int16 nNodeType = 0; + pResultSet->getPropertyValue( nHandleStart ) >>= nNodeType; + if( pEffect->getNodeType() != nNodeType ) + { + pEffect->setNodeType( nNodeType ); + bChanged = true; + } + } + + if( pResultSet->getPropertyState( nHandleRepeat ) == STLPropertyState::Direct ) + { + Any aRepeatCount( pResultSet->getPropertyValue( nHandleRepeat ) ); + if( aRepeatCount != pEffect->getRepeatCount() ) + { + pEffect->setRepeatCount( aRepeatCount ); + bChanged = true; + } + } + + if( pResultSet->getPropertyState( nHandleEnd ) == STLPropertyState::Direct ) + { + Any aEndValue( pResultSet->getPropertyValue( nHandleEnd ) ); + if( pEffect->getEnd() != aEndValue ) + { + pEffect->setEnd( aEndValue ); + bChanged = true; + } + } + + if( pResultSet->getPropertyState( nHandleRewind ) == STLPropertyState::Direct ) + { + sal_Int16 nFill = 0; + pResultSet->getPropertyValue( nHandleRewind ) >>= nFill; + if( pEffect->getFill() != nFill ) + { + pEffect->setFill( nFill ); + bChanged = true; + } + } + + if( pResultSet->getPropertyState( nHandleHasAfterEffect ) == STLPropertyState::Direct ) + { + bool bHasAfterEffect = false; + if( pResultSet->getPropertyValue( nHandleHasAfterEffect ) >>= bHasAfterEffect ) + { + if( pEffect->hasAfterEffect() != bHasAfterEffect ) + { + pEffect->setHasAfterEffect( bHasAfterEffect ); + bChanged = true; + } + } + } + + if( pResultSet->getPropertyState( nHandleAfterEffectOnNextEffect ) == STLPropertyState::Direct ) + { + bool bAfterEffectOnNextEffect = false; + if( (pResultSet->getPropertyValue( nHandleAfterEffectOnNextEffect ) >>= bAfterEffectOnNextEffect) + && (pEffect->IsAfterEffectOnNext() != bAfterEffectOnNextEffect) ) + { + pEffect->setAfterEffectOnNext( bAfterEffectOnNextEffect ); + bChanged = true; + } + } + + if( pResultSet->getPropertyState( nHandleDimColor ) == STLPropertyState::Direct ) + { + Any aDimColor( pResultSet->getPropertyValue( nHandleDimColor ) ); + if( pEffect->getDimColor() != aDimColor ) + { + pEffect->setDimColor( aDimColor ); + bChanged = true; + } + } + + if( pResultSet->getPropertyState( nHandleAccelerate ) == STLPropertyState::Direct ) + { + double fAccelerate = 0.0; + pResultSet->getPropertyValue( nHandleAccelerate ) >>= fAccelerate; + if( pEffect->getAcceleration() != fAccelerate ) + { + pEffect->setAcceleration( fAccelerate ); + bChanged = true; + } + } + + if( pResultSet->getPropertyState( nHandleDecelerate ) == STLPropertyState::Direct ) + { + double fDecelerate = 0.0; + pResultSet->getPropertyValue( nHandleDecelerate ) >>= fDecelerate; + if( pEffect->getDecelerate() != fDecelerate ) + { + pEffect->setDecelerate( fDecelerate ); + bChanged = true; + } + } + + if( pResultSet->getPropertyState( nHandleAutoReverse ) == STLPropertyState::Direct ) + { + bool bAutoReverse = false; + pResultSet->getPropertyValue( nHandleAutoReverse ) >>= bAutoReverse; + if( pEffect->getAutoReverse() != bAutoReverse ) + { + pEffect->setAutoReverse( bAutoReverse ); + bChanged = true; + } + } + + if( pResultSet->getPropertyState( nHandleProperty1Value ) == STLPropertyState::Direct ) + { + sal_Int32 nType = 0; + pOldSet->getPropertyValue( nHandleProperty1Type ) >>= nType; + + bChanged |= setProperty1Value( nType, pEffect, pResultSet->getPropertyValue( nHandleProperty1Value ) ); + } + + if( pResultSet->getPropertyState( nHandleSoundURL ) == STLPropertyState::Direct ) + { + const Any aSoundSource( pResultSet->getPropertyValue( nHandleSoundURL ) ); + + if( aSoundSource.getValueType() == ::cppu::UnoType<sal_Bool>::get() ) + { + pEffect->setStopAudio(); + bChanged = true; + } + else + { + OUString aSoundURL; + aSoundSource >>= aSoundURL; + + if( !aSoundURL.isEmpty() ) + { + if( !pEffect->getAudio().is() ) + { + pEffect->createAudio( aSoundSource ); + bChanged = true; + } + else + { + if( pEffect->getAudio()->getSource() != aSoundSource ) + { + pEffect->getAudio()->setSource( aSoundSource ); + bChanged = true; + } + } + } + else + { + if( pEffect->getAudio().is() || pEffect->getStopAudio() ) + { + pEffect->removeAudio(); + bChanged = true; + } + } + } + } + + if( pResultSet->getPropertyState( nHandleTrigger ) == STLPropertyState::Direct ) + { + Reference< XShape > xTriggerShape; + pResultSet->getPropertyValue( nHandleTrigger ) >>= xTriggerShape; + bChanged |= mpMainSequence->setTrigger( pEffect, xTriggerShape ); + } + } + + const bool bHasTextGrouping = pResultSet->getPropertyState( nHandleTextGrouping ) == STLPropertyState::Direct; + const bool bHasAnimateForm = pResultSet->getPropertyState( nHandleAnimateForm ) == STLPropertyState::Direct; + const bool bHasTextGroupingAuto = pResultSet->getPropertyState( nHandleTextGroupingAuto ) == STLPropertyState::Direct; + const bool bHasTextReverse = pResultSet->getPropertyState( nHandleTextReverse ) == STLPropertyState::Direct; + + if( bHasTextGrouping || bHasAnimateForm || bHasTextGroupingAuto || bHasTextReverse ) + { + // we need to do a second pass for text grouping options + // since changing them can cause effects to be removed + // or replaced, we do this after we applied all other options + // above + + sal_Int32 nTextGrouping = 0; + bool bAnimateForm = true, bTextReverse = false; + double fTextGroupingAuto = -1.0; + + if( bHasTextGrouping ) + pResultSet->getPropertyValue(nHandleTextGrouping) >>= nTextGrouping; + else + pOldSet->getPropertyValue(nHandleTextGrouping) >>= nTextGrouping; + + if( bHasAnimateForm ) + pResultSet->getPropertyValue(nHandleAnimateForm) >>= bAnimateForm; + else + pOldSet->getPropertyValue(nHandleAnimateForm) >>= bAnimateForm; + + if( bHasTextGroupingAuto ) + pResultSet->getPropertyValue(nHandleTextGroupingAuto) >>= fTextGroupingAuto; + else + pOldSet->getPropertyValue(nHandleTextGroupingAuto) >>= fTextGroupingAuto; + + if( bHasTextReverse ) + pResultSet->getPropertyValue(nHandleTextReverse) >>= bTextReverse; + else + pOldSet->getPropertyValue(nHandleTextReverse) >>= bTextReverse; + + EffectSequence const aSelectedEffects( maListSelection ); + for( CustomAnimationEffectPtr const& pEffect : aSelectedEffects ) + { + EffectSequenceHelper* pEffectSequence = pEffect->getEffectSequence(); + if( !pEffectSequence ) + pEffectSequence = mpMainSequence.get(); + + sal_Int32 nGroupId = pEffect->getGroupId(); + CustomAnimationTextGroupPtr pTextGroup; + if( nGroupId != -1 ) + { + // use existing group + pTextGroup = pEffectSequence->findGroup( nGroupId ); + } + else + { + // somethings changed so we need a group now + pTextGroup = pEffectSequence->createTextGroup( pEffect, nTextGrouping, fTextGroupingAuto, bAnimateForm, bTextReverse ); + bChanged = true; + } + + //#i119988# + /************************************************************************/ + /* + Note, the setAnimateForm means set the animation from TextGroup to Object's Shape + And on the UI in means "Animate attached shape" in "Effect Option" dialog + The setTextGrouping means set animation to Object's Text, + the nTextGrouping is Text Animation Type + nTextGrouping = -1 is "As one Object", means no text animation. + + The previous call order first do the setTextGrouping and then do the setAnimateForm, + that will cause such defect: in the setTextGrouping, the effect has been removed, + but in setAnimateForm still need this effect, then a NULL pointer of that effect will + be gotten, and cause crash. + + []bHasAnimateForm means the UI has changed, bAnimateForm is it value + + So if create a new textgroup animation, the following animation will never be run! + Since the \A1\B0Animate attached shape\A1\B1 is default checked. + And the bHasAnimateForm default is false, and if user uncheck it the value bAnimateForm will be false, + it same as the TextGroup\A1\AFs default value, also could not be run setAnimateForm. + if( bHasAnimateForm ) + { + if( pTextGroup->getAnimateForm() != bAnimateForm ) + { + pEffectSequence->setAnimateForm( pTextGroup, bAnimateForm ); + bChanged = true; + } + } + + In setTextGrouping, there are three case: + 1. Create new text effects for empty TextGroup + 2. Remove all text effects of TextGroup (nTextGrouping == -1) + 3. Change all the text effects\A1\AF start type + + So here is the right logic: + If set the animation from text to shape and remove text animation, + should do setAnimateForm first, then do setTextGrouping. + Other case,do setTextGrouping first, then do setAnimateForm. + + */ + /************************************************************************/ + + bool bDoSetAnimateFormFirst = false; + bool bNeedDoSetAnimateForm = false; + + if( bHasAnimateForm ) + { + if( pTextGroup && pTextGroup->getAnimateForm() != bAnimateForm ) + { + if( (pTextGroup->getTextGrouping() >= 0) && (nTextGrouping == -1 ) ) + { + bDoSetAnimateFormFirst = true; + } + bNeedDoSetAnimateForm = true; + } + } + + if (bDoSetAnimateFormFirst) + { + pEffectSequence->setAnimateForm( pTextGroup, bAnimateForm ); + bChanged = true; + } + + if( bHasTextGrouping ) + { + if( pTextGroup && pTextGroup->getTextGrouping() != nTextGrouping ) + { + pEffectSequence->setTextGrouping( pTextGroup, nTextGrouping ); + + // All the effects of the outline object is removed so we need to + // put it back. OTOH, the shape object that still has effects + // in the text group is fine. + if (nTextGrouping == -1 && pTextGroup->getEffects().empty()) + { + pEffect->setTarget(Any(pEffect->getTargetShape())); + pEffect->setGroupId(-1); + mpMainSequence->append(pEffect); + } + + bChanged = true; + } + } + + if (!bDoSetAnimateFormFirst && bNeedDoSetAnimateForm) + { + if( pTextGroup ) + { + pEffectSequence->setAnimateForm( pTextGroup, bAnimateForm ); + bChanged = true; + } + } + + if( bHasTextGroupingAuto ) + { + if( pTextGroup && pTextGroup->getTextGroupingAuto() != fTextGroupingAuto ) + { + pEffectSequence->setTextGroupingAuto( pTextGroup, fTextGroupingAuto ); + bChanged = true; + } + } + + if( bHasTextReverse ) + { + if( pTextGroup && pTextGroup->getTextReverse() != bTextReverse ) + { + pEffectSequence->setTextReverse( pTextGroup, bTextReverse ); + bChanged = true; + } + } + } + } + + if( bChanged ) + { + mpMainSequence->rebuild(); + updateControls(); + mrBase.GetDocShell()->SetModified(); + } +} + +void CustomAnimationPane::showOptions(const OString& rPage) +{ + std::unique_ptr<STLPropertySet> xSet = createSelectionSet(); + + auto xDlg = std::make_shared<CustomAnimationDialog>(GetFrameWeld(), std::move(xSet), rPage); + + weld::DialogController::runAsync(xDlg, [xDlg, this](sal_Int32 nResult){ + if (nResult ) + { + addUndo(); + changeSelection(xDlg->getResultSet(), xDlg->getPropertySet()); + updateControls(); + } + }); +} + +void CustomAnimationPane::onChangeCurrentPage() +{ + if( !mxView.is() ) + return; + + try + { + Reference< XDrawPage > xNewPage( mxView->getCurrentPage() ); + if( xNewPage != mxCurrentPage ) + { + mxCurrentPage = xNewPage; + SdPage* pPage = SdPage::getImplementation( mxCurrentPage ); + if( pPage ) + { + mpMainSequence = pPage->getMainSequence(); + mxCustomAnimationList->update( mpMainSequence ); + } + updateControls(); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationPane::onChangeCurrentPage()" ); + } +} + +static bool getTextSelection( const Any& rSelection, Reference< XShape >& xShape, std::vector< sal_Int16 >& rParaList ) +{ + Reference< XTextRange > xSelectedText; + rSelection >>= xSelectedText; + if( xSelectedText.is() ) try + { + xShape.set( xSelectedText->getText(), UNO_QUERY_THROW ); + + css::uno::Reference<css::document::XActionLockable> xLockable(xShape, css::uno::UNO_QUERY); + if (xLockable.is()) + xLockable->addActionLock(); + comphelper::ScopeGuard aGuard([&xLockable]() + { + if (xLockable.is()) + xLockable->removeActionLock(); + }); + + Reference< XTextRangeCompare > xTextRangeCompare( xShape, UNO_QUERY_THROW ); + Reference< XEnumerationAccess > xParaEnumAccess( xShape, UNO_QUERY_THROW ); + Reference< XEnumeration > xParaEnum( xParaEnumAccess->createEnumeration(), UNO_SET_THROW ); + Reference< XTextRange > xRange; + Reference< XTextRange > xStart( xSelectedText->getStart() ); + Reference< XTextRange > xEnd( xSelectedText->getEnd() ); + + if( xTextRangeCompare->compareRegionEnds( xStart, xEnd ) < 0 ) + { + Reference< XTextRange > xTemp( xStart ); + xStart = xEnd; + xEnd = xTemp; + } + + sal_Int16 nPara = 0; + while( xParaEnum->hasMoreElements() ) + { + xParaEnum->nextElement() >>= xRange; + + // break if start of selection is prior to end of current paragraph + if( xRange.is() && (xTextRangeCompare->compareRegionEnds( xStart, xRange ) >= 0 ) ) + break; + + nPara++; + } + + while( xRange.is() ) + { + if( xRange.is() && !xRange->getString().isEmpty() ) + rParaList.push_back( nPara ); + + // break if end of selection is before or at end of current paragraph + if( xRange.is() && xTextRangeCompare->compareRegionEnds( xEnd, xRange ) >= 0 ) + break; + + nPara++; + + if( xParaEnum->hasMoreElements() ) + xParaEnum->nextElement() >>= xRange; + else + xRange.clear(); + } + + return true; + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationPane::getTextSelection()" ); + } + + return false; +} + +namespace +{ + Reference<XShape> getTargetShape(const Any& rTarget) + { + Reference<XShape> xShape; + rTarget >>= xShape; + if( !xShape.is() ) + { + ParagraphTarget aParaTarget; + if (rTarget >>= aParaTarget) + xShape = aParaTarget.Shape; + } + return xShape; + } +} + +void CustomAnimationPane::onAdd() +{ + bool bHasText = true; + + // first create vector of targets for dialog preview + std::vector< Any > aTargets; + + // gather shapes from the selection + Reference< XSelectionSupplier > xSel( mxView, UNO_QUERY_THROW ); + maViewSelection = xSel->getSelection(); + + if( maViewSelection.getValueType() == cppu::UnoType<XShapes>::get()) + { + Reference< XIndexAccess > xShapes; + maViewSelection >>= xShapes; + + sal_Int32 nCount = xShapes->getCount(); + aTargets.reserve( nCount ); + for( sal_Int32 nIndex = 0; nIndex < nCount; nIndex++ ) + { + Any aTarget( xShapes->getByIndex( nIndex ) ); + aTargets.push_back( aTarget ); + if( bHasText ) + { + Reference< XText > xText; + aTarget >>= xText; + if( !xText.is() || xText->getString().isEmpty() ) + bHasText = false; + } + } + } + else if ( maViewSelection.getValueType() == cppu::UnoType<XShape>::get()) + { + aTargets.push_back( maViewSelection ); + Reference< XText > xText; + maViewSelection >>= xText; + if( !xText.is() || xText->getString().isEmpty() ) + bHasText = false; + } + else if ( maViewSelection.getValueType() == cppu::UnoType<XTextCursor>::get()) + { + Reference< XShape > xShape; + std::vector< sal_Int16 > aParaList; + if( getTextSelection( maViewSelection, xShape, aParaList ) ) + { + ParagraphTarget aParaTarget; + aParaTarget.Shape = xShape; + + for( const auto& rPara : aParaList ) + { + aParaTarget.Paragraph = rPara; + aTargets.push_back( Any( aParaTarget ) ); + } + } + } + else + { + OSL_FAIL("sd::CustomAnimationPane::onAdd(), unknown view selection!" ); + return; + } + + CustomAnimationPresetPtr pDescriptor; + mxFTCategory->set_sensitive(true); + mxFTAnimation->set_sensitive(true); + + bool bCategoryReset = false; + + if (!mxLBCategory->get_sensitive() || mxLBCategory->get_active() == -1) + { + mxLBCategory->set_sensitive(true); + mxLBCategory->set_active(0); + bCategoryReset = true; + } + + if (bCategoryReset || !mxLBAnimation->get_sensitive() || + mxLBAnimation->get_selected_index() == -1) + { + mxLBAnimation->set_sensitive(true); + + sal_Int32 nFirstEffect = fillAnimationLB(bHasText); + if (nFirstEffect == -1) + return; + + mxLBAnimation->select(nFirstEffect); + mnLastSelectedAnimation = nFirstEffect; + } + + auto pEntryData = weld::fromId<CustomAnimationPresetPtr*>(mxLBAnimation->get_selected_id()); + if (pEntryData) + pDescriptor = *pEntryData; + + if( pDescriptor ) + { + const double fDuration = pDescriptor->getDuration(); + mxCBXDuration->set_value(fDuration*100.0, FieldUnit::NONE); + bool bHasSpeed = pDescriptor->getDuration() > 0.001; + mxCBXDuration->set_sensitive( bHasSpeed ); + mxFTDuration->set_sensitive( bHasSpeed ); + + mxCustomAnimationList->unselect_all(); + + // gather shapes from the selection + bool bFirst = true; + for( const auto& rTarget : aTargets ) + { + css::uno::Reference<css::document::XActionLockable> xLockable(getTargetShape(rTarget), css::uno::UNO_QUERY); + if (xLockable.is()) + xLockable->addActionLock(); + comphelper::ScopeGuard aGuard([&xLockable]() + { + if (xLockable.is()) + xLockable->removeActionLock(); + }); + + CustomAnimationEffectPtr pCreated = mpMainSequence->append( pDescriptor, rTarget, fDuration ); + + // if only one shape with text and no fill or outline is selected, animate only by first level paragraphs + if( bHasText && (aTargets.size() == 1) ) + { + Reference< XShape > xShape( rTarget, UNO_QUERY ); + if( xShape.is() && !hasVisibleShape( xShape ) ) + { + mpMainSequence->createTextGroup( pCreated, 1, -1.0, false, false ); + } + } + + if( bFirst ) + bFirst = false; + else + pCreated->setNodeType( EffectNodeType::WITH_PREVIOUS ); + + if( pCreated ) + mxCustomAnimationList->select( pCreated ); + } + } + + PathKind ePathKind = getCreatePathKind(); + + if (ePathKind != PathKind::NONE) + { + createPath( ePathKind, aTargets, 0.0 ); + updateMotionPathTags(); + } + + addUndo(); + mrBase.GetDocShell()->SetModified(); + + updateControls(); + + SlideShow::Stop( mrBase ); +} + +void CustomAnimationPane::onRemove() +{ + if( maListSelection.empty() ) + return; + + addUndo(); + + MainSequenceRebuildGuard aGuard( mpMainSequence ); + + EffectSequence aList( maListSelection ); + + for( CustomAnimationEffectPtr& pEffect : aList ) + { + if( pEffect->getEffectSequence() ) + pEffect->getEffectSequence()->remove( pEffect ); + } + + maListSelection.clear(); + mrBase.GetDocShell()->SetModified(); +} + +void CustomAnimationPane::remove( CustomAnimationEffectPtr const & pEffect ) +{ + if( pEffect->getEffectSequence() ) + { + addUndo(); + pEffect->getEffectSequence()->remove( pEffect ); + mrBase.GetDocShell()->SetModified(); + } +} + +void CustomAnimationPane::onChangeStart() +{ + sal_Int16 nNodeType; + switch( mxLBStart->get_active() ) + { + case 0: nNodeType = EffectNodeType::ON_CLICK; break; + case 1: nNodeType = EffectNodeType::WITH_PREVIOUS; break; + case 2: nNodeType = EffectNodeType::AFTER_PREVIOUS; break; + default: + return; + } + + onChangeStart( nNodeType ); +} + +void CustomAnimationPane::onChangeStart( sal_Int16 nNodeType ) +{ + addUndo(); + + MainSequenceRebuildGuard aGuard( mpMainSequence ); + + bool bNeedRebuild = false; + + for( CustomAnimationEffectPtr& pEffect : maListSelection ) + { + if( pEffect->getNodeType() != nNodeType ) + { + pEffect->setNodeType( nNodeType ); + bNeedRebuild = true; + } + } + + if( bNeedRebuild ) + { + mpMainSequence->rebuild(); + updateControls(); + mrBase.GetDocShell()->SetModified(); + } +} + +void CustomAnimationPane::onChangeSpeed() +{ + double fDuration = getDuration(); + + if(fDuration < 0) + return; + else + { + addUndo(); + + MainSequenceRebuildGuard aGuard( mpMainSequence ); + + // change selected effect + for( CustomAnimationEffectPtr& pEffect : maListSelection ) + { + pEffect->setDuration( fDuration ); + } + + mpMainSequence->rebuild(); + updateControls(); + mrBase.GetDocShell()->SetModified(); + } +} + +double CustomAnimationPane::getDuration() const +{ + double fDuration = 0; + + if (!mxCBXDuration->get_text().isEmpty()) + fDuration = mxCBXDuration->get_value(FieldUnit::NONE) / 100.0; + + return fDuration; +} + +PathKind CustomAnimationPane::getCreatePathKind() const +{ + PathKind eKind = PathKind::NONE; + + if (mxLBAnimation->count_selected_rows() == 1 && + mxLBCategory->get_active() == gnMotionPathPos) + { + const sal_Int32 nPos = mxLBAnimation->get_selected_index(); + if( nPos == mnCurvePathPos ) + { + eKind = PathKind::CURVE; + } + else if( nPos == mnPolygonPathPos ) + { + eKind = PathKind::POLYGON; + } + else if( nPos == mnFreeformPathPos ) + { + eKind = PathKind::FREEFORM; + } + } + + return eKind; +} + +void CustomAnimationPane::createPath( PathKind eKind, std::vector< Any >& rTargets, double fDuration) +{ + sal_uInt16 nSID = 0; + + switch( eKind ) + { + case PathKind::CURVE: nSID = SID_DRAW_BEZIER_NOFILL; break; + case PathKind::POLYGON: nSID = SID_DRAW_POLYGON_NOFILL; break; + case PathKind::FREEFORM: nSID = SID_DRAW_FREELINE_NOFILL; break; + default: break; + } + + if( !nSID ) + return; + + DrawViewShell* pViewShell = dynamic_cast< DrawViewShell* >( + FrameworkHelper::Instance(mrBase)->GetViewShell(FrameworkHelper::msCenterPaneURL).get()); + + if( pViewShell ) + { + DrawView* pView = pViewShell->GetDrawView(); + if( pView ) + pView->UnmarkAllObj(); + + std::vector< Any > aTargets( 1, Any( fDuration ) ); + aTargets.insert( aTargets.end(), rTargets.begin(), rTargets.end() ); + Sequence< Any > aTargetSequence( comphelper::containerToSequence( aTargets ) ); + const SfxUnoAnyItem aItem( SID_ADD_MOTION_PATH, Any( aTargetSequence ) ); + pViewShell->GetViewFrame()->GetDispatcher()->ExecuteList( nSID, SfxCallMode::ASYNCHRON, {&aItem} ); + } +} + + +/// this link is called when the property box is modified by the user +IMPL_LINK_NOARG(CustomAnimationPane, implPropertyHdl, LinkParamNone*, void) +{ + if (!mxLBSubControl) + return; + + addUndo(); + + MainSequenceRebuildGuard aGuard( mpMainSequence ); + + const Any aValue(mxLBSubControl->getValue()); + + bool bNeedUpdate = false; + + // change selected effect + for( const CustomAnimationEffectPtr& pEffect : maListSelection ) + { + if( setProperty1Value( mnPropertyType, pEffect, aValue ) ) + bNeedUpdate = true; + } + + if( bNeedUpdate ) + { + mpMainSequence->rebuild(); + updateControls(); + mrBase.GetDocShell()->SetModified(); + } + + onPreview( false ); +} + +IMPL_LINK_NOARG(CustomAnimationPane, DelayModifiedHdl, weld::MetricSpinButton&, void) +{ + addUndo(); +} + +IMPL_LINK_NOARG(CustomAnimationPane, DelayLoseFocusHdl, weld::Widget&, void) +{ + double fBegin = mxMFStartDelay->get_value(FieldUnit::NONE); + + //sequence rebuild only when the control loses focus + MainSequenceRebuildGuard aGuard( mpMainSequence ); + + // change selected effect + for( CustomAnimationEffectPtr& pEffect : maListSelection ) + { + pEffect->setBegin( fBegin/10.0 ); + } + + mpMainSequence->rebuild(); + updateControls(); + mrBase.GetDocShell()->SetModified(); +} + +IMPL_LINK_NOARG(CustomAnimationPane, AnimationSelectHdl, weld::TreeView&, void) +{ + maIdle.Start(); +} + +IMPL_LINK_NOARG(CustomAnimationPane, SelectionHandler, Timer*, void) +{ + if (mxLBAnimation->has_grab()) // tdf#136474 try again later + { + maIdle.Start(); + return; + } + + int nSelected = mxLBAnimation->get_selected_index(); + if (nSelected == -1) + return; + + // tdf#99137, the selected entry may also be a subcategory title, so not an effect + // just skip it and move to the next one in this case + if (mxLBAnimation->get_text_emphasis(nSelected, 0)) + { + if (nSelected == 0 || nSelected > mnLastSelectedAnimation) + mxLBAnimation->select(++nSelected); + else + mxLBAnimation->select(--nSelected); + } + + mnLastSelectedAnimation = nSelected; + + CustomAnimationPresetPtr* pPreset = weld::fromId<CustomAnimationPresetPtr*>(mxLBAnimation->get_id(nSelected)); + PathKind ePathKind = getCreatePathKind(); + + if ( ePathKind != PathKind::NONE ) + { + std::vector< Any > aTargets; + MainSequenceRebuildGuard aGuard( mpMainSequence ); + + for( const CustomAnimationEffectPtr& pEffect : maListSelection ) + { + aTargets.push_back( pEffect->getTarget() ); + + EffectSequenceHelper* pEffectSequence = pEffect->getEffectSequence(); + if( !pEffectSequence ) + pEffectSequence = mpMainSequence.get(); + + // delete the old animation, new one will be appended + // by createPath and SID_ADD_MOTION_PATH therein + pEffectSequence->remove( pEffect ); + } + + createPath( ePathKind, aTargets, 0.0 ); + updateMotionPathTags(); + return; + } + + CustomAnimationPresetPtr pDescriptor(*pPreset); + const double fDuration = (*pPreset)->getDuration(); + MainSequenceRebuildGuard aGuard( mpMainSequence ); + + // get selected effect + for( const CustomAnimationEffectPtr& pEffect : maListSelection ) + { + // Dispose the deprecated motion path tag. It will be rebuilt later. + if (pEffect->getPresetClass() == css::presentation::EffectPresetClass::MOTIONPATH) + { + for (auto const& xTag: maMotionPathTags) + { + if(xTag->getEffect() == pEffect && !xTag->isDisposed()) + xTag->Dispose(); + } + } + + EffectSequenceHelper* pEffectSequence = pEffect->getEffectSequence(); + if( !pEffectSequence ) + pEffectSequence = mpMainSequence.get(); + + pEffectSequence->replace( pEffect, pDescriptor, fDuration ); + } + + addUndo(); + onPreview(false); +} + +IMPL_LINK_NOARG(CustomAnimationPane, UpdateAnimationLB, weld::ComboBox&, void) +{ + //FIXME: first effect only? what if there is more? + CustomAnimationEffectPtr pEffect = maListSelection.front(); + fillAnimationLB( pEffect->hasText() ); +} + +IMPL_LINK_NOARG(CustomAnimationPane, DurationModifiedHdl, weld::MetricSpinButton&, void) +{ + if (!mxCBXDuration->get_text().isEmpty()) + { + double duration_value = static_cast<double>(mxCBXDuration->get_value(FieldUnit::NONE)); + if(duration_value <= 0.0) + { + mxCBXDuration->set_value(1, FieldUnit::NONE); + } + onChangeSpeed(); + } +} + +namespace +{ + void InsertCategory(weld::TreeView& rLBAnimation, const OUString& rMotionPathLabel) + { + int nRow = rLBAnimation.n_children(); + rLBAnimation.append_text(rMotionPathLabel); + rLBAnimation.set_text_emphasis(nRow, true, 0); + rLBAnimation.set_text_align(nRow, 0.5, 0); + } +} + +sal_Int32 CustomAnimationPane::fillAnimationLB( bool bHasText ) +{ + PresetCategoryList rCategoryList; + sal_uInt16 nPosition = mxLBCategory->get_active(); + const CustomAnimationPresets& rPresets (CustomAnimationPresets::getCustomAnimationPresets()); + switch(nPosition) + { + case 0:rCategoryList = rPresets.getEntrancePresets();break; + case 1:rCategoryList = rPresets.getEmphasisPresets();break; + case 2:rCategoryList = rPresets.getExitPresets();break; + case 3:rCategoryList = rPresets.getMotionPathsPresets();break; + case 4:rCategoryList = rPresets.getMiscPresets();break; + } + + sal_Int32 nFirstEffect = -1; + + int nOldEntryCount = mxLBAnimation->n_children(); + int nOldScrollPos = mxLBAnimation->vadjustment_get_value(); + + mxLBAnimation->freeze(); + mxLBAnimation->clear(); + mnLastSelectedAnimation = -1; + + if (nPosition == gnMotionPathPos) + { + OUString sMotionPathLabel( SdResId( STR_CUSTOMANIMATION_USERPATH ) ); + InsertCategory(*mxLBAnimation, sMotionPathLabel); + mnCurvePathPos = mxLBAnimation->n_children(); + mxLBAnimation->append_text( SvxResId(STR_ObjNameSingulCOMBLINE) ); + mxLBAnimation->set_text_emphasis(mnCurvePathPos, false, 0); + mnPolygonPathPos = mnCurvePathPos + 1; + mxLBAnimation->append_text( SvxResId(STR_ObjNameSingulPOLY) ); + mxLBAnimation->set_text_emphasis(mnPolygonPathPos, false, 0); + mnFreeformPathPos = mnPolygonPathPos + 1; + mxLBAnimation->append_text( SvxResId(STR_ObjNameSingulFREELINE) ); + mxLBAnimation->set_text_emphasis(mnFreeformPathPos, false, 0); + } + + for (const PresetCategoryPtr& pCategory : rCategoryList) + { + if( pCategory ) + { + InsertCategory(*mxLBAnimation, pCategory->maLabel); + + int nPos = mxLBAnimation->n_children(); + + std::vector< CustomAnimationPresetPtr > aSortedVector = + pCategory->maEffects; + + for( const CustomAnimationPresetPtr& pDescriptor : aSortedVector ) + { + // ( !isTextOnly || ( isTextOnly && bHasText ) ) <=> !isTextOnly || bHasText + if( pDescriptor && ( !pDescriptor->isTextOnly() || bHasText ) ) + { + auto pCustomPtr = new CustomAnimationPresetPtr(pDescriptor); + OUString sId = weld::toId(pCustomPtr); + mxLBAnimation->append(sId, pDescriptor->getLabel()); + mxLBAnimation->set_text_emphasis(nPos, false, 0); + + if (nFirstEffect == -1) + nFirstEffect = nPos; + + ++nPos; + } + } + } + } + + mxLBAnimation->thaw(); + + if (mxLBAnimation->n_children() == nOldEntryCount) + mxLBAnimation->vadjustment_set_value(nOldScrollPos); + + return nFirstEffect; +} + +IMPL_LINK(CustomAnimationPane, implToggleHdl, weld::Toggleable&, rBtn, void) +{ + implControlHdl(&rBtn); +} + +IMPL_LINK(CustomAnimationPane, implClickHdl, weld::Button&, rBtn, void) +{ + implControlHdl(&rBtn); +} + +IMPL_LINK( CustomAnimationPane, implControlListBoxHdl, weld::ComboBox&, rListBox, void ) +{ + implControlHdl(&rListBox); +} + +/// this link is called when one of the controls is modified +void CustomAnimationPane::implControlHdl(const weld::Widget* pControl) +{ + if (pControl == mxPBAddEffect.get()) + onAdd(); + else if (pControl == mxPBRemoveEffect.get()) + onRemove(); + else if (pControl == mxLBStart.get()) + onChangeStart(); + else if (pControl == mxPBPropertyMore.get()) + showOptions(); + else if (pControl == mxPBMoveUp.get()) + moveSelection( true ); + else if (pControl == mxPBMoveDown.get()) + moveSelection( false ); + else if (pControl == mxPBPlay.get()) + onPreview( true ); + else if (pControl == mxCBAutoPreview.get()) + { + SdOptions* pOptions = SD_MOD()->GetSdOptions(DocumentType::Impress); + pOptions->SetPreviewChangedEffects(mxCBAutoPreview->get_active()); + } +} + +IMPL_LINK_NOARG(CustomAnimationPane, lateInitCallback, Timer *, void) +{ + // Call getPresets() to initiate the (expensive) construction of the + // presets list. + CustomAnimationPresets::getCustomAnimationPresets(); + + // update selection and control states + onSelectionChanged(); +} + +void CustomAnimationPane::moveSelection( bool bUp ) +{ + if( maListSelection.empty() ) + return; + + EffectSequenceHelper* pSequence = maListSelection.front()->getEffectSequence(); + if( pSequence == nullptr ) + return; + + addUndo(); + + bool bChanged = false; + + MainSequenceRebuildGuard aGuard( mpMainSequence ); + EffectSequence& rEffectSequence = pSequence->getSequence(); + + if( bUp ) + { + for( const CustomAnimationEffectPtr& pEffect : maListSelection ) + { + EffectSequence::iterator aUpEffectPos( pSequence->find( pEffect ) ); + // coverity[copy_paste_error : FALSE] - this is correct, checking if it exists + if( aUpEffectPos != rEffectSequence.end() ) + { + EffectSequence::iterator aInsertPos( rEffectSequence.erase( aUpEffectPos ) ); + + if( aInsertPos != rEffectSequence.begin() ) + { + --aInsertPos; + while( (aInsertPos != rEffectSequence.begin()) && !mxCustomAnimationList->isExpanded(*aInsertPos)) + --aInsertPos; + rEffectSequence.insert( aInsertPos, pEffect ); + } + else + { + rEffectSequence.push_front( pEffect ); + } + bChanged = true; + } + } + } + else + { + EffectSequence::reverse_iterator aIter( maListSelection.rbegin() ); + const EffectSequence::reverse_iterator aEnd( maListSelection.rend() ); + + while( aIter != aEnd ) + { + CustomAnimationEffectPtr pEffect = *aIter++; + + EffectSequence::iterator aDownEffectPos( pSequence->find( pEffect ) ); + // coverity[copy_paste_error : FALSE] - this is correct, checking if it exists + if( aDownEffectPos != rEffectSequence.end() ) + { + EffectSequence::iterator aInsertPos( rEffectSequence.erase( aDownEffectPos ) ); + + if( aInsertPos != rEffectSequence.end() ) + { + ++aInsertPos; + // Advance over rolled-up (un-expanded) items, unless we just moved it there. + while( (aInsertPos != rEffectSequence.end()) + && !mxCustomAnimationList->isExpanded(*aInsertPos) + && (std::find(maListSelection.begin(), maListSelection.end(), *aInsertPos) + == maListSelection.end()) + ) + ++aInsertPos; + rEffectSequence.insert( aInsertPos, pEffect ); + } + else + { + rEffectSequence.push_back( pEffect ); + } + bChanged = true; + } + } + } + + if( bChanged ) + { + mpMainSequence->rebuild(); + updateControls(); + mrBase.GetDocShell()->SetModified(); + } +} + +void CustomAnimationPane::onPreview( bool bForcePreview ) +{ + if (!bForcePreview && !mxCBAutoPreview->get_active()) + return; + + // No preview in LOK. + if (comphelper::LibreOfficeKit::isActive()) + return; + + if( maListSelection.empty() ) + { + rtl::Reference< MotionPathTag > xMotionPathTag; + auto aIter = std::find_if(maMotionPathTags.begin(), maMotionPathTags.end(), + [](const MotionPathTagVector::value_type& rxMotionPathTag) { return rxMotionPathTag->isSelected(); }); + if (aIter != maMotionPathTags.end()) + xMotionPathTag = *aIter; + + if( xMotionPathTag.is() ) + { + MainSequencePtr pSequence = std::make_shared<MainSequence>(); + pSequence->append( xMotionPathTag->getEffect()->clone() ); + preview( pSequence->getRootNode() ); + } + else + { + Reference< XAnimationNodeSupplier > xNodeSupplier( mxCurrentPage, UNO_QUERY ); + if( !xNodeSupplier.is() ) + return; + + preview( xNodeSupplier->getAnimationNode() ); + } + } + else + { + MainSequencePtr pSequence = std::make_shared<MainSequence>(); + + for( const CustomAnimationEffectPtr& pEffect : maListSelection ) + { + pSequence->append( pEffect->clone() ); + } + + preview( pSequence->getRootNode() ); + } +} + +void CustomAnimationPane::preview( const Reference< XAnimationNode >& xAnimationNode ) +{ + Reference< XParallelTimeContainer > xRoot = ParallelTimeContainer::create( ::comphelper::getProcessComponentContext() ); + Sequence< css::beans::NamedValue > aUserData + { { "node-type", css::uno::Any(css::presentation::EffectNodeType::TIMING_ROOT) } }; + xRoot->setUserData( aUserData ); + xRoot->appendChild( xAnimationNode ); + + SlideShow::StartPreview( mrBase, mxCurrentPage, xRoot ); +} + +// ICustomAnimationListController +void CustomAnimationPane::onSelect() +{ + maListSelection = mxCustomAnimationList->getSelection(); + updateControls(); + + // mark shapes from selected effects + if( maSelectionLock.isLocked() ) + return; + + // tdf#145030 if nothing is selected in the effects list, leave the selection of + // objects in the slide untouched + if (maListSelection.empty()) + return; + + ScopeLockGuard aGuard( maSelectionLock ); + DrawViewShell* pViewShell = dynamic_cast< DrawViewShell* >( + FrameworkHelper::Instance(mrBase)->GetViewShell(FrameworkHelper::msCenterPaneURL).get()); + DrawView* pView = pViewShell ? pViewShell->GetDrawView() : nullptr; + + if( pView ) + { + pView->UnmarkAllObj(); + for( const CustomAnimationEffectPtr& pEffect : maListSelection ) + { + Reference< XShape > xShape( pEffect->getTargetShape() ); + SdrObject* pObj = SdrObject::getSdrObjectFromXShape(xShape); + if( pObj ) + pView->MarkObj(pObj, pView->GetSdrPageView()); + } + } +} + +// ICustomAnimationListController +// pEffectInsertBefore may be null if moving to end of list. +void CustomAnimationPane::onDragNDropComplete(std::vector< CustomAnimationEffectPtr > pEffectsDragged, CustomAnimationEffectPtr pEffectInsertBefore) +{ + if ( !mpMainSequence ) + return; + + addUndo(); + + MainSequenceRebuildGuard aGuard( mpMainSequence ); + + // Move all selected effects + for( auto const& pEffectDragged : pEffectsDragged ) + { + // Move this dragged effect and any hidden sub-effects + EffectSequence::iterator aIter = mpMainSequence->find( pEffectDragged ); + const EffectSequence::iterator aEnd( mpMainSequence->getEnd() ); + + while( aIter != aEnd ) + { + CustomAnimationEffectPtr pEffect = *aIter++; + + // Update model with new location (function triggers a rebuild) + // target may be null, which will insert at the end. + mpMainSequence->moveToBeforeEffect( pEffect, pEffectInsertBefore ); + // Done moving effect and its hidden sub-effects when *next* effect is visible. + if (aIter != aEnd && mxCustomAnimationList->isVisible(*aIter)) + break; + } + } + + updateControls(); + mrBase.GetDocShell()->SetModified(); +} + +void CustomAnimationPane::updatePathFromMotionPathTag( const rtl::Reference< MotionPathTag >& xTag ) +{ + MainSequenceRebuildGuard aGuard( mpMainSequence ); + if( !xTag.is() ) + return; + + SdrPathObj* pPathObj = xTag->getPathObj(); + CustomAnimationEffectPtr pEffect = xTag->getEffect(); + if( (pPathObj != nullptr) && pEffect ) + { + SfxUndoManager* pManager = mrBase.GetDocShell()->GetUndoManager(); + if( pManager ) + { + SdPage* pPage = SdPage::getImplementation( mxCurrentPage ); + if( pPage ) + pManager->AddUndoAction( std::make_unique<UndoAnimationPath>( mrBase.GetDocShell()->GetDoc(), pPage, pEffect->getNode() ) ); + } + + pEffect->updatePathFromSdrPathObj( *pPathObj ); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/animations/STLPropertySet.cxx b/sd/source/ui/animations/STLPropertySet.cxx new file mode 100644 index 000000000..592d7639c --- /dev/null +++ b/sd/source/ui/animations/STLPropertySet.cxx @@ -0,0 +1,113 @@ +/* -*- 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 "STLPropertySet.hxx" +#include <sal/log.hxx> + +using com::sun::star::uno::Any; + +namespace sd +{ + +STLPropertySet::STLPropertySet() +{ +} + +STLPropertySet::~STLPropertySet() +{ +} + +void STLPropertySet::setPropertyDefaultValue( sal_Int32 nHandle, const Any& rValue ) +{ + STLPropertyMapEntry aEntry( rValue ); + maPropertyMap[ nHandle ] = aEntry; +} + +void STLPropertySet::setPropertyValue( sal_Int32 nHandle, const Any& rValue ) +{ + PropertyMapIter aIter; + if( findProperty( nHandle, aIter ) ) + { + (*aIter).second.mnState = STLPropertyState::Direct; + (*aIter).second.maValue = rValue; + } + else + { + SAL_WARN("sd", "sd::STLPropertySet::setPropertyValue(), unknown property!"); + } +} + +Any STLPropertySet::getPropertyValue( sal_Int32 nHandle ) const +{ + PropertyMapConstIter aIter; + if( findProperty( nHandle, aIter ) ) + { + return (*aIter).second.maValue; + } + else + { + SAL_WARN("sd", "sd::STLPropertySet::getPropertyValue(), unknown property!"); + + Any aAny; + return aAny; + } +} + +STLPropertyState STLPropertySet::getPropertyState( sal_Int32 nHandle ) const +{ + PropertyMapConstIter aIter; + if( findProperty( nHandle, aIter ) ) + { + return (*aIter).second.mnState; + } + else + { + SAL_WARN("sd", "sd::STLPropertySet::getPropertyState(), unknown property!"); + return STLPropertyState::Ambiguous; + } +} + +void STLPropertySet::setPropertyState( sal_Int32 nHandle, STLPropertyState nState ) +{ + PropertyMapIter aIter; + if( findProperty( nHandle, aIter ) ) + { + (*aIter).second.mnState = nState; + } + else + { + SAL_WARN("sd","sd::STLPropertySet::setPropertyState(), unknown property!"); + } +} + +bool STLPropertySet::findProperty( sal_Int32 nHandle, PropertyMapIter& rIter ) +{ + rIter = maPropertyMap.find(nHandle); + return( rIter != maPropertyMap.end() ); +} + +bool STLPropertySet::findProperty( sal_Int32 nHandle, PropertyMapConstIter& rIter ) const +{ + rIter = maPropertyMap.find(nHandle); + return( rIter != maPropertyMap.end() ); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/animations/STLPropertySet.hxx b/sd/source/ui/animations/STLPropertySet.hxx new file mode 100644 index 000000000..3096e7c78 --- /dev/null +++ b/sd/source/ui/animations/STLPropertySet.hxx @@ -0,0 +1,73 @@ +/* -*- 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 . + */ + +#pragma once + +#include <map> +#include <com/sun/star/uno/Any.hxx> + +namespace sd +{ + +enum class STLPropertyState { + Default = 0, + Direct = 1, + Ambiguous = 3 +}; + +struct STLPropertyMapEntry +{ + css::uno::Any maValue; + STLPropertyState mnState; + + STLPropertyMapEntry() + : mnState( STLPropertyState::Ambiguous ) {} + explicit STLPropertyMapEntry(css::uno::Any aValue) + : maValue( aValue ), mnState( STLPropertyState::Default ) {} + +}; + +typedef std::map<sal_Int32, STLPropertyMapEntry > PropertyMap; +typedef PropertyMap::iterator PropertyMapIter; +typedef PropertyMap::const_iterator PropertyMapConstIter; + +class STLPropertySet +{ +public: + STLPropertySet(); + ~STLPropertySet(); + + void setPropertyDefaultValue( sal_Int32 nHandle, const css::uno::Any& rValue ); + void setPropertyValue( sal_Int32 nHandle, const css::uno::Any& rValue ); + css::uno::Any getPropertyValue( sal_Int32 nHandle ) const; + + STLPropertyState getPropertyState( sal_Int32 nHandle ) const; + void setPropertyState( sal_Int32 nHandle, STLPropertyState nState ); + +private: + bool findProperty( sal_Int32 nHandle, PropertyMapIter& rIter ); + bool findProperty( sal_Int32 nHandle, PropertyMapConstIter& rIter ) const; + +private: + PropertyMap maPropertyMap; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/animations/SlideTransitionPane.cxx b/sd/source/ui/animations/SlideTransitionPane.cxx new file mode 100644 index 000000000..846f21c34 --- /dev/null +++ b/sd/source/ui/animations/SlideTransitionPane.cxx @@ -0,0 +1,1155 @@ +/* -*- 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 <com/sun/star/drawing/XDrawView.hpp> +#include <SlideTransitionPane.hxx> + +#include <TransitionPreset.hxx> +#include <sdresid.hxx> +#include <ViewShellBase.hxx> +#include <DrawDocShell.hxx> +#include <SlideSorterViewShell.hxx> +#include <drawdoc.hxx> +#include <sdmod.hxx> +#include <sdpage.hxx> +#include <filedlg.hxx> +#include <strings.hrc> +#include <EventMultiplexer.hxx> + +#include <comphelper/lok.hxx> +#include <sal/log.hxx> +#include <tools/debug.hxx> +#include <svx/gallery.hxx> +#include <vcl/stdtext.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <tools/urlobj.hxx> +#include <slideshow.hxx> +#include <sdundogr.hxx> +#include <undoanim.hxx> +#include <optsitem.hxx> + +#include <o3tl/safeint.hxx> + +#include <algorithm> + +using namespace ::com::sun::star; + +using ::com::sun::star::uno::Reference; + + +namespace sd::impl +{ +struct TransitionEffect +{ + TransitionEffect() : + mnType( 0 ), + mnSubType( 0 ), + mbDirection( true ), + mnFadeColor( 0 ) + { + init(); + } + explicit TransitionEffect( const ::sd::TransitionPreset & rPreset ) : + mnType( rPreset.getTransition()), + mnSubType( rPreset.getSubtype()), + mbDirection( rPreset.getDirection()), + mnFadeColor( rPreset.getFadeColor()) + { + init(); + } + explicit TransitionEffect( const SdPage & rPage ) : + mnType( rPage.getTransitionType() ), + mnSubType( rPage.getTransitionSubtype() ), + mbDirection( rPage.getTransitionDirection() ), + mnFadeColor( rPage.getTransitionFadeColor() ) + { + init(); + + mfDuration = rPage.getTransitionDuration(); + mfTime = rPage.GetTime(); + mePresChange = rPage.GetPresChange(); + mbSoundOn = rPage.IsSoundOn(); + maSound = rPage.GetSoundFile(); + mbLoopSound = rPage.IsLoopSound(); + mbStopSound = rPage.IsStopSound(); + } + + void init() + { + mfDuration = 2.0; + mfTime = 0.0; + mePresChange = PresChange::Manual; + mbSoundOn = false; + mbLoopSound = false; + mbStopSound = false; + + mbEffectAmbiguous = false; + mbDurationAmbiguous = false; + mbTimeAmbiguous = false; + mbPresChangeAmbiguous = false; + mbSoundAmbiguous = false; + mbLoopSoundAmbiguous = false; + } + + void setAllAmbiguous() + { + mbEffectAmbiguous = true; + mbDurationAmbiguous = true; + mbTimeAmbiguous = true; + mbPresChangeAmbiguous = true; + mbSoundAmbiguous = true; + mbLoopSoundAmbiguous = true; + } + + bool operator == ( const ::sd::TransitionPreset & rPreset ) const + { + return + (mnType == rPreset.getTransition()) && + (mnSubType == rPreset.getSubtype()) && + (mbDirection == rPreset.getDirection()) && + (mnFadeColor == rPreset.getFadeColor()); + } + + void applyTo( SdPage & rOutPage ) const + { + if( ! mbEffectAmbiguous ) + { + rOutPage.setTransitionType( mnType ); + rOutPage.setTransitionSubtype( mnSubType ); + rOutPage.setTransitionDirection( mbDirection ); + rOutPage.setTransitionFadeColor( mnFadeColor ); + } + + if( ! mbDurationAmbiguous ) + rOutPage.setTransitionDuration( mfDuration ); + if( ! mbTimeAmbiguous ) + rOutPage.SetTime( mfTime ); + if( ! mbPresChangeAmbiguous ) + rOutPage.SetPresChange( mePresChange ); + if( ! mbSoundAmbiguous ) + { + if( mbStopSound ) + { + rOutPage.SetStopSound( true ); + rOutPage.SetSound( false ); + } + else + { + rOutPage.SetStopSound( false ); + rOutPage.SetSound( mbSoundOn ); + rOutPage.SetSoundFile( maSound ); + } + } + if( ! mbLoopSoundAmbiguous ) + rOutPage.SetLoopSound( mbLoopSound ); + } + + void compareWith( const SdPage & rPage ) + { + TransitionEffect aOtherEffect( rPage ); + mbEffectAmbiguous = mbEffectAmbiguous || aOtherEffect.mbEffectAmbiguous + || (mnType != aOtherEffect.mnType) + || (mnSubType != aOtherEffect.mnSubType) + || (mbDirection != aOtherEffect.mbDirection) + || (mnFadeColor != aOtherEffect.mnFadeColor); + + mbDurationAmbiguous = mbDurationAmbiguous || aOtherEffect.mbDurationAmbiguous || mfDuration != aOtherEffect.mfDuration; + mbTimeAmbiguous = mbTimeAmbiguous || aOtherEffect.mbTimeAmbiguous || mfTime != aOtherEffect.mfTime; + mbPresChangeAmbiguous = mbPresChangeAmbiguous || aOtherEffect.mbPresChangeAmbiguous || mePresChange != aOtherEffect.mePresChange; + mbSoundAmbiguous = mbSoundAmbiguous || aOtherEffect.mbSoundAmbiguous || mbSoundOn != aOtherEffect.mbSoundOn; +#if 0 + // Weird leftover isolated expression with no effect, introduced in 2007 in + // CWS impress122. Ifdeffed out to avoid compiler warning, kept here in case + // somebody who understands this code notices and understands what the + // "right" thing to do might be. + (!mbStopSound && !aOtherEffect.mbStopSound && maSound != aOtherEffect.maSound) || (mbStopSound != aOtherEffect.mbStopSound); +#endif + mbLoopSoundAmbiguous = mbLoopSoundAmbiguous || aOtherEffect.mbLoopSoundAmbiguous || mbLoopSound != aOtherEffect.mbLoopSound; + } + + // effect + sal_Int16 mnType; + sal_Int16 mnSubType; + bool mbDirection; + sal_Int32 mnFadeColor; + + // other settings + double mfDuration; + double mfTime; + PresChange mePresChange; + bool mbSoundOn; + OUString maSound; + bool mbLoopSound; + bool mbStopSound; + + bool mbEffectAmbiguous; + bool mbDurationAmbiguous; + bool mbTimeAmbiguous; + bool mbPresChangeAmbiguous; + bool mbSoundAmbiguous; + bool mbLoopSoundAmbiguous; +}; + +} // namespace sd::impl + +// Local Helper Functions +namespace +{ + +void lcl_ApplyToPages( + const ::sd::slidesorter::SharedPageSelection& rpPages, + const ::sd::impl::TransitionEffect & rEffect ) +{ + for( const auto& rpPage : *rpPages ) + { + rEffect.applyTo( *rpPage ); + } +} + +void lcl_CreateUndoForPages( + const ::sd::slidesorter::SharedPageSelection& rpPages, + ::sd::ViewShellBase const & rBase ) +{ + ::sd::DrawDocShell* pDocSh = rBase.GetDocShell(); + if (!pDocSh) + return; + SfxUndoManager* pManager = pDocSh->GetUndoManager(); + if (!pManager) + return; + SdDrawDocument* pDoc = pDocSh->GetDoc(); + if (!pDoc) + return; + + OUString aComment( SdResId(STR_UNDO_SLIDE_PARAMS) ); + pManager->EnterListAction(aComment, aComment, 0, rBase.GetViewShellId()); + std::unique_ptr<SdUndoGroup> pUndoGroup(new SdUndoGroup( pDoc )); + pUndoGroup->SetComment( aComment ); + + for( const auto& rpPage : *rpPages ) + { + pUndoGroup->AddAction( new sd::UndoTransition( pDoc, rpPage ) ); + } + + pManager->AddUndoAction( std::move(pUndoGroup) ); + pManager->LeaveListAction(); +} + +struct lcl_EqualsSoundFileName +{ + explicit lcl_EqualsSoundFileName( const OUString & rStr ) : + maStr( rStr ) + {} + + bool operator() ( const OUString & rStr ) const + { + // note: formerly this was a case insensitive search for all + // platforms. It seems more sensible to do this platform-dependent + INetURLObject aURL(rStr); +#if defined(_WIN32) + return maStr.equalsIgnoreAsciiCase( aURL.GetBase() ); +#else + return maStr == aURL.GetBase(); +#endif + } + +private: + OUString maStr; +}; + +// returns -1 if no object was found +bool lcl_findSoundInList( const ::std::vector< OUString > & rSoundList, + std::u16string_view rFileName, + ::std::vector< OUString >::size_type & rOutPosition ) +{ + INetURLObject aURL(rFileName); + ::std::vector< OUString >::const_iterator aIt = + ::std::find_if( rSoundList.begin(), rSoundList.end(), + lcl_EqualsSoundFileName( aURL.GetBase())); + if( aIt != rSoundList.end()) + { + rOutPosition = ::std::distance( rSoundList.begin(), aIt ); + return true; + } + + return false; +} + +OUString lcl_getSoundFileURL( + const ::std::vector< OUString > & rSoundList, + const weld::ComboBox& rListBox ) +{ + sal_Int32 nPos = rListBox.get_active(); + // the first three entries are no actual sounds + if( nPos >= 3 ) + { + DBG_ASSERT( static_cast<sal_uInt32>(rListBox.get_count() - 3) == rSoundList.size(), + "Sound list-box is not synchronized to sound list" ); + nPos -= 3; + if( rSoundList.size() > o3tl::make_unsigned(nPos) ) + return rSoundList[ nPos ]; + } + + return OUString(); +} + +struct lcl_AppendSoundToListBox +{ + explicit lcl_AppendSoundToListBox(weld::ComboBox& rListBox) + : mrListBox( rListBox ) + {} + + void operator() ( std::u16string_view rString ) const + { + INetURLObject aURL( rString ); + mrListBox.append_text( aURL.GetBase() ); + } + +private: + weld::ComboBox& mrListBox; +}; + +void lcl_FillSoundListBox( + const ::std::vector< OUString > & rSoundList, + weld::ComboBox& rOutListBox ) +{ + sal_Int32 nCount = rOutListBox.get_count(); + + // keep first three entries + for( sal_Int32 i=nCount - 1; i>=3; --i ) + rOutListBox.remove( i ); + + ::std::for_each( rSoundList.begin(), rSoundList.end(), + lcl_AppendSoundToListBox( rOutListBox )); +} + +/// Returns an offset into the list of transition presets +size_t getPresetOffset( const sd::impl::TransitionEffect &rEffect ) +{ + const sd::TransitionPresetList& rPresetList = + sd::TransitionPreset::getTransitionPresetList(); + + size_t nIdx = 0; + for( const auto& aIt: rPresetList ) + { + if( rEffect.operator==( *aIt )) + break; + nIdx++; + } + return nIdx; +} + +} // anonymous namespace + +namespace sd +{ + +class TransitionPane : public ValueSet +{ +public: + explicit TransitionPane(std::unique_ptr<weld::ScrolledWindow> pScrolledWindow) + : ValueSet(std::move(pScrolledWindow)) + { + } + + void Recalculate() + { + GetScrollBar()->set_vpolicy(VclPolicyType::AUTOMATIC); + RecalculateItemSizes(); + } + + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override + { + Size aSize = pDrawingArea->get_ref_device().LogicToPixel(Size(70, 88), MapMode(MapUnit::MapAppFont)); + pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); + ValueSet::SetDrawingArea(pDrawingArea); + SetOutputSizePixel(aSize); + + SetStyle(GetStyle() | WB_ITEMBORDER | WB_FLATVALUESET | WB_VSCROLL); + EnableFullItemMode( false ); + SetColCount(3); + } +}; + +// SlideTransitionPane +SlideTransitionPane::SlideTransitionPane( + weld::Widget* pParent, + ViewShellBase & rBase) : + PanelLayout( pParent, "SlideTransitionsPanel", "modules/simpress/ui/slidetransitionspanel.ui" ), + mrBase( rBase ), + mpDrawDoc( rBase.GetDocShell() ? rBase.GetDocShell()->GetDoc() : nullptr ), + mbHasSelection( false ), + mbUpdatingControls( false ), + mbIsMainViewChangePending( false ), + maLateInitTimer("sd SlideTransitionPane maLateInitTimer") +{ + Initialize(mpDrawDoc); +} + +css::ui::LayoutSize SlideTransitionPane::GetHeightForWidth(const sal_Int32 /*nWidth*/) +{ + sal_Int32 nMinimumHeight = get_preferred_size().Height(); + return css::ui::LayoutSize(nMinimumHeight, -1, nMinimumHeight); +} + +constexpr sal_uInt16 nNoneId = std::numeric_limits<sal_uInt16>::max(); + +void SlideTransitionPane::Initialize(SdDrawDocument* pDoc) +{ + mxFT_VARIANT = m_xBuilder->weld_label("variant_label"); + mxLB_VARIANT = m_xBuilder->weld_combo_box("variant_list"); + mxFT_duration = m_xBuilder->weld_label("duration_label"); + mxCBX_duration = m_xBuilder->weld_metric_spin_button("transition_duration", FieldUnit::SECOND); + mxFT_SOUND = m_xBuilder->weld_label("sound_label"); + mxLB_SOUND = m_xBuilder->weld_combo_box("sound_list"); + mxCB_LOOP_SOUND = m_xBuilder->weld_check_button("loop_sound"); + mxRB_ADVANCE_ON_MOUSE = m_xBuilder->weld_radio_button("rb_mouse_click"); + mxRB_ADVANCE_AUTO = m_xBuilder->weld_radio_button("rb_auto_after"); + mxMF_ADVANCE_AUTO_AFTER = m_xBuilder->weld_metric_spin_button("auto_after_value", FieldUnit::SECOND); + mxPB_APPLY_TO_ALL = m_xBuilder->weld_button("apply_to_all"); + mxPB_PLAY = m_xBuilder->weld_button("play"); + mxCB_AUTO_PREVIEW = m_xBuilder->weld_check_button("auto_preview"); + + auto nMax = mxMF_ADVANCE_AUTO_AFTER->get_max(FieldUnit::SECOND); + mxMF_ADVANCE_AUTO_AFTER->set_max(99, FieldUnit::SECOND); + int nWidthChars = mxMF_ADVANCE_AUTO_AFTER->get_width_chars(); + mxMF_ADVANCE_AUTO_AFTER->set_max(nMax, FieldUnit::SECOND); + mxMF_ADVANCE_AUTO_AFTER->set_width_chars(nWidthChars); + mxCBX_duration->set_width_chars(nWidthChars); + + mxVS_TRANSITION_ICONS.reset(new TransitionPane(m_xBuilder->weld_scrolled_window("transitions_iconswin", true))); + mxVS_TRANSITION_ICONSWin.reset(new weld::CustomWeld(*m_xBuilder, "transitions_icons", *mxVS_TRANSITION_ICONS)); + + if( pDoc ) + mxModel.set( pDoc->getUnoModel(), uno::UNO_QUERY ); + // TODO: get correct view + if( mxModel.is()) + mxView.set( mxModel->getCurrentController(), uno::UNO_QUERY ); + + // dummy list box of slide transitions for startup. + mxVS_TRANSITION_ICONS->InsertItem( + nNoneId, Image( StockImage::Yes, "sd/cmd/transition-none.png" ), + SdResId( STR_SLIDETRANSITION_NONE ), + VALUESET_APPEND, /* show legend */ true ); + mxVS_TRANSITION_ICONS->Recalculate(); + + // set defaults + mxCB_AUTO_PREVIEW->set_active(true); // automatic preview on + + // update control states before adding handlers + updateControls(); + + // set handlers + mxPB_APPLY_TO_ALL->connect_clicked( LINK( this, SlideTransitionPane, ApplyToAllButtonClicked )); + mxPB_PLAY->connect_clicked( LINK( this, SlideTransitionPane, PlayButtonClicked )); + + mxVS_TRANSITION_ICONS->SetSelectHdl( LINK( this, SlideTransitionPane, TransitionSelected )); + + mxLB_VARIANT->connect_changed( LINK( this, SlideTransitionPane, VariantListBoxSelected )); + mxCBX_duration->connect_value_changed(LINK( this, SlideTransitionPane, DurationModifiedHdl)); + mxCBX_duration->connect_focus_out(LINK( this, SlideTransitionPane, DurationLoseFocusHdl)); + mxLB_SOUND->connect_changed( LINK( this, SlideTransitionPane, SoundListBoxSelected )); + mxCB_LOOP_SOUND->connect_toggled( LINK( this, SlideTransitionPane, LoopSoundBoxChecked )); + + mxRB_ADVANCE_ON_MOUSE->connect_toggled( LINK( this, SlideTransitionPane, AdvanceSlideRadioButtonToggled )); + mxRB_ADVANCE_AUTO->connect_toggled( LINK( this, SlideTransitionPane, AdvanceSlideRadioButtonToggled )); + mxMF_ADVANCE_AUTO_AFTER->connect_value_changed( LINK( this, SlideTransitionPane, AdvanceTimeModified )); + mxCB_AUTO_PREVIEW->connect_toggled( LINK( this, SlideTransitionPane, AutoPreviewClicked )); + addListener(); + + maLateInitTimer.SetTimeout(200); + maLateInitTimer.SetInvokeHandler(LINK(this, SlideTransitionPane, LateInitCallback)); + maLateInitTimer.Start(); +} + +SlideTransitionPane::~SlideTransitionPane() +{ + maLateInitTimer.Stop(); + removeListener(); + mxVS_TRANSITION_ICONSWin.reset(); + mxVS_TRANSITION_ICONS.reset(); + mxFT_VARIANT.reset(); + mxLB_VARIANT.reset(); + mxFT_duration.reset(); + mxCBX_duration.reset(); + mxFT_SOUND.reset(); + mxLB_SOUND.reset(); + mxCB_LOOP_SOUND.reset(); + mxRB_ADVANCE_ON_MOUSE.reset(); + mxRB_ADVANCE_AUTO.reset(); + mxMF_ADVANCE_AUTO_AFTER.reset(); + mxPB_APPLY_TO_ALL.reset(); + mxPB_PLAY.reset(); + mxCB_AUTO_PREVIEW.reset(); +} + +void SlideTransitionPane::onSelectionChanged() +{ + updateControls(); +} + +void SlideTransitionPane::onChangeCurrentPage() +{ + updateControls(); +} + +::sd::slidesorter::SharedPageSelection SlideTransitionPane::getSelectedPages() const +{ + ::sd::slidesorter::SlideSorterViewShell * pSlideSorterViewShell + = ::sd::slidesorter::SlideSorterViewShell::GetSlideSorter(mrBase); + std::shared_ptr<sd::slidesorter::SlideSorterViewShell::PageSelection> pSelection; + + if( pSlideSorterViewShell ) + { + pSelection = pSlideSorterViewShell->GetPageSelection(); + } + else + { + pSelection = std::make_shared<sd::slidesorter::SlideSorterViewShell::PageSelection>(); + if( mxView.is() ) + { + SdPage* pPage = SdPage::getImplementation( mxView->getCurrentPage() ); + if( pPage ) + pSelection->push_back(pPage); + } + } + + return pSelection; +} + +void SlideTransitionPane::updateControls() +{ + ::sd::slidesorter::SharedPageSelection pSelectedPages(getSelectedPages()); + if( pSelectedPages->empty()) + { + mbHasSelection = false; + return; + } + mbHasSelection = true; + + DBG_ASSERT( ! mbUpdatingControls, "Multiple Control Updates" ); + mbUpdatingControls = true; + + // get model data for first page + SdPage * pFirstPage = pSelectedPages->front(); + DBG_ASSERT( pFirstPage, "Invalid Page" ); + + impl::TransitionEffect aEffect( *pFirstPage ); + + // merge with other pages + + // start with second page (note aIt != aEndIt, because ! aSelectedPages.empty()) + for( const auto& rpPage : *pSelectedPages ) + { + if( rpPage ) + aEffect.compareWith( *rpPage ); + } + + // detect current slide effect + if( aEffect.mbEffectAmbiguous ) + { + SAL_WARN( "sd.transitions", "Unusual, ambiguous transition effect" ); + mxVS_TRANSITION_ICONS->SelectItem(nNoneId); + } + else + { + // ToDo: That 0 is "no transition" is documented nowhere except in the + // CTOR of sdpage + if( aEffect.mnType == 0 ) + mxVS_TRANSITION_ICONS->SelectItem(nNoneId); + else + updateVariants( getPresetOffset( aEffect ) ); + } + + if( aEffect.mbDurationAmbiguous ) + { + mxCBX_duration->set_text(""); +//TODO mxCBX_duration->SetNoSelection(); + } + else + { + mxCBX_duration->set_value( (aEffect.mfDuration)*100.0, FieldUnit::SECOND ); + } + + if( aEffect.mbSoundAmbiguous ) + { + mxLB_SOUND->set_active(-1); + maCurrentSoundFile.clear(); + } + else + { + maCurrentSoundFile.clear(); + if( aEffect.mbStopSound ) + { + mxLB_SOUND->set_active( 1 ); + } + else if( aEffect.mbSoundOn && !aEffect.maSound.isEmpty() ) + { + std::vector<OUString>::size_type nPos = 0; + if( lcl_findSoundInList( maSoundList, aEffect.maSound, nPos )) + { + mxLB_SOUND->set_active( nPos + 3 ); + maCurrentSoundFile = aEffect.maSound; + } + } + else + { + mxLB_SOUND->set_active( 0 ); + } + } + + if( aEffect.mbLoopSoundAmbiguous ) + { + mxCB_LOOP_SOUND->set_state(TRISTATE_INDET); + } + else + { + mxCB_LOOP_SOUND->set_active(aEffect.mbLoopSound); + } + + if( aEffect.mbPresChangeAmbiguous ) + { + mxRB_ADVANCE_ON_MOUSE->set_active( false ); + mxRB_ADVANCE_AUTO->set_active( false ); + } + else + { + mxRB_ADVANCE_ON_MOUSE->set_active( aEffect.mePresChange == PresChange::Manual ); + mxRB_ADVANCE_AUTO->set_active( aEffect.mePresChange == PresChange::Auto ); + mxMF_ADVANCE_AUTO_AFTER->set_value(aEffect.mfTime * 100.0, FieldUnit::SECOND); + } + + if (comphelper::LibreOfficeKit::isActive()) + { + mxPB_PLAY->hide(); + mxCB_AUTO_PREVIEW->set_active(false); + mxCB_AUTO_PREVIEW->hide(); + mxFT_SOUND->hide(); + mxLB_SOUND->hide(); + mxCB_LOOP_SOUND->hide(); + } + else + { + SdOptions* pOptions = SD_MOD()->GetSdOptions(DocumentType::Impress); + mxCB_AUTO_PREVIEW->set_active( pOptions->IsPreviewTransitions() ); + } + + mbUpdatingControls = false; + + updateControlState(); +} + +void SlideTransitionPane::updateControlState() +{ + mxVS_TRANSITION_ICONSWin->set_sensitive( mbHasSelection ); + mxLB_VARIANT->set_sensitive( mbHasSelection && mxLB_VARIANT->get_count() > 0 ); + mxCBX_duration->set_sensitive( mbHasSelection ); + mxLB_SOUND->set_sensitive( mbHasSelection ); + mxCB_LOOP_SOUND->set_sensitive( mbHasSelection && (mxLB_SOUND->get_active() > 2)); + mxRB_ADVANCE_ON_MOUSE->set_sensitive( mbHasSelection ); + mxRB_ADVANCE_AUTO->set_sensitive( mbHasSelection ); + mxMF_ADVANCE_AUTO_AFTER->set_sensitive( mbHasSelection && mxRB_ADVANCE_AUTO->get_active()); + + mxPB_APPLY_TO_ALL->set_sensitive( mbHasSelection ); + mxPB_PLAY->set_sensitive( mbHasSelection ); + mxCB_AUTO_PREVIEW->set_sensitive( mbHasSelection ); +} + +void SlideTransitionPane::updateSoundList() +{ + maSoundList.clear(); + + GalleryExplorer::FillObjList( GALLERY_THEME_SOUNDS, maSoundList ); + GalleryExplorer::FillObjList( GALLERY_THEME_USERSOUNDS, maSoundList ); + + lcl_FillSoundListBox( maSoundList, *mxLB_SOUND ); +} + +void SlideTransitionPane::openSoundFileDialog() +{ + if( ! mxLB_SOUND->get_sensitive()) + return; + + SdOpenSoundFileDialog aFileDialog(GetFrameWeld()); + + DBG_ASSERT( mxLB_SOUND->get_active() == 2, + "Dialog should only open when \"Other sound\" is selected" ); + + bool bValidSoundFile( false ); + bool bQuitLoop( false ); + + while( ! bQuitLoop && + aFileDialog.Execute() == ERRCODE_NONE ) + { + OUString aFile = aFileDialog.GetPath(); + std::vector<OUString>::size_type nPos = 0; + bValidSoundFile = lcl_findSoundInList( maSoundList, aFile, nPos ); + + if( bValidSoundFile ) + { + bQuitLoop = true; + } + else // not in sound list + { + // try to insert into gallery + if( GalleryExplorer::InsertURL( GALLERY_THEME_USERSOUNDS, aFile ) ) + { + updateSoundList(); + bValidSoundFile = lcl_findSoundInList( maSoundList, aFile, nPos ); + DBG_ASSERT( bValidSoundFile, "Adding sound to gallery failed" ); + + bQuitLoop = true; + } + else + { + OUString aStrWarning(SdResId(STR_WARNING_NOSOUNDFILE)); + aStrWarning = aStrWarning.replaceFirst("%", aFile); + std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(nullptr, + VclMessageType::Warning, VclButtonsType::NONE, + aStrWarning)); + xWarn->add_button(GetStandardText(StandardButtonType::Retry), RET_RETRY); + xWarn->add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL); + bQuitLoop = (xWarn->run() != RET_RETRY); + + bValidSoundFile = false; + } + } + + if( bValidSoundFile ) + // skip first three entries in list + mxLB_SOUND->set_active( nPos + 3 ); + } + + if( bValidSoundFile ) + return; + + if( !maCurrentSoundFile.isEmpty() ) + { + std::vector<OUString>::size_type nPos = 0; + if( lcl_findSoundInList( maSoundList, maCurrentSoundFile, nPos )) + mxLB_SOUND->set_active( nPos + 3 ); + else + mxLB_SOUND->set_active( 0 ); // NONE + } + else + mxLB_SOUND->set_active( 0 ); // NONE +} + +impl::TransitionEffect SlideTransitionPane::getTransitionEffectFromControls() const +{ + impl::TransitionEffect aResult; + aResult.setAllAmbiguous(); + + bool bNoneSelected = mxVS_TRANSITION_ICONS->IsNoSelection() || mxVS_TRANSITION_ICONS->GetSelectedItemId() == nNoneId; + + // check first (aResult might be overwritten) + if( mxVS_TRANSITION_ICONSWin->get_sensitive() && + !bNoneSelected && + mxVS_TRANSITION_ICONS->GetSelectedItemId() > 0 ) + { + const sd::TransitionPresetList& rPresetList = sd::TransitionPreset::getTransitionPresetList(); + auto aSelected = rPresetList.begin(); + std::advance( aSelected, mxVS_TRANSITION_ICONS->GetSelectedItemId() - 1); + + if (mxLB_VARIANT->get_active() == -1) + { + // Transition with just one effect. + aResult = impl::TransitionEffect( **aSelected ); + aResult.setAllAmbiguous(); + } + else + { + int nVariant = 0; + bool bFound = false; + for( const auto& aIter: rPresetList ) + { + if( aIter->getSetId() == (*aSelected)->getSetId() ) + { + if( mxLB_VARIANT->get_active() == nVariant) + { + aResult = impl::TransitionEffect( *aIter ); + aResult.setAllAmbiguous(); + bFound = true; + break; + } + else + { + nVariant++; + } + } + } + if( !bFound ) + { + aResult.mnType = 0; + } + } + aResult.mbEffectAmbiguous = false; + } + else if (bNoneSelected) + { + aResult.mbEffectAmbiguous = false; + } + + //duration + + if( mxCBX_duration->get_sensitive() && (!(mxCBX_duration->get_text()).isEmpty()) ) + { + aResult.mfDuration = static_cast<double>(mxCBX_duration->get_value(FieldUnit::SECOND))/100.0; + aResult.mbDurationAmbiguous = false; + } + + // slide-advance mode + if( mxRB_ADVANCE_ON_MOUSE->get_sensitive() && mxRB_ADVANCE_AUTO->get_sensitive() && + (mxRB_ADVANCE_ON_MOUSE->get_active() || mxRB_ADVANCE_AUTO->get_active())) + { + if( mxRB_ADVANCE_ON_MOUSE->get_active()) + aResult.mePresChange = PresChange::Manual; + else + { + aResult.mePresChange = PresChange::Auto; + if( mxMF_ADVANCE_AUTO_AFTER->get_sensitive()) + { + aResult.mfTime = static_cast<double>(mxMF_ADVANCE_AUTO_AFTER->get_value(FieldUnit::SECOND) ) / 100.0 ; + aResult.mbTimeAmbiguous = false; + } + } + + aResult.mbPresChangeAmbiguous = false; + } + + // sound + if( mxLB_SOUND->get_sensitive()) + { + maCurrentSoundFile.clear(); + sal_Int32 nPos = mxLB_SOUND->get_active(); + if (nPos != -1) + { + aResult.mbStopSound = nPos == 1; + aResult.mbSoundOn = nPos > 1; + if( aResult.mbStopSound ) + { + aResult.maSound.clear(); + aResult.mbSoundAmbiguous = false; + } + else + { + aResult.maSound = lcl_getSoundFileURL(maSoundList, *mxLB_SOUND); + aResult.mbSoundAmbiguous = false; + maCurrentSoundFile = aResult.maSound; + } + } + } + + // sound loop + if( mxCB_LOOP_SOUND->get_sensitive() ) + { + aResult.mbLoopSound = mxCB_LOOP_SOUND->get_active(); + aResult.mbLoopSoundAmbiguous = false; + } + + return aResult; +} + +void SlideTransitionPane::applyToSelectedPages(bool bPreview = true) +{ + if( mbUpdatingControls ) + return; + + vcl::Window *pFocusWindow = Application::GetFocusWindow(); + + ::sd::slidesorter::SharedPageSelection pSelectedPages( getSelectedPages()); + impl::TransitionEffect aEffect = getTransitionEffectFromControls(); + if( ! pSelectedPages->empty()) + { + lcl_CreateUndoForPages( pSelectedPages, mrBase ); + lcl_ApplyToPages( pSelectedPages, aEffect ); + mrBase.GetDocShell()->SetModified(); + } + if( mxCB_AUTO_PREVIEW->get_sensitive() && + mxCB_AUTO_PREVIEW->get_active() && bPreview) + { + if (aEffect.mnType) // mnType = 0 denotes no transition + playCurrentEffect(); + else if( mxView.is() ) + SlideShow::Stop( mrBase ); + } + + if (pFocusWindow) + pFocusWindow->GrabFocus(); +} + +void SlideTransitionPane::playCurrentEffect() +{ + if( mxView.is() ) + { + + Reference< css::animations::XAnimationNode > xNode; + SlideShow::StartPreview( mrBase, mxView->getCurrentPage(), xNode ); + } +} + +void SlideTransitionPane::addListener() +{ + Link<tools::EventMultiplexerEvent&,void> aLink( LINK(this,SlideTransitionPane,EventMultiplexerListener) ); + mrBase.GetEventMultiplexer()->AddEventListener( aLink ); +} + +void SlideTransitionPane::removeListener() +{ + Link<tools::EventMultiplexerEvent&,void> aLink( LINK(this,SlideTransitionPane,EventMultiplexerListener) ); + mrBase.GetEventMultiplexer()->RemoveEventListener( aLink ); +} + +IMPL_LINK(SlideTransitionPane,EventMultiplexerListener, + tools::EventMultiplexerEvent&, rEvent, void) +{ + switch (rEvent.meEventId) + { + case EventMultiplexerEventId::EditViewSelection: + onSelectionChanged(); + break; + + case EventMultiplexerEventId::CurrentPageChanged: + case EventMultiplexerEventId::SlideSortedSelection: + onChangeCurrentPage(); + break; + + case EventMultiplexerEventId::MainViewRemoved: + mxView.clear(); + onSelectionChanged(); + onChangeCurrentPage(); + break; + + case EventMultiplexerEventId::MainViewAdded: + mbIsMainViewChangePending = true; + break; + + case EventMultiplexerEventId::ConfigurationUpdated: + if (mbIsMainViewChangePending) + { + mbIsMainViewChangePending = false; + + // At this moment the controller may not yet been set at + // model or ViewShellBase. Take it from the view shell + // passed with the event. + if (mrBase.GetMainViewShell() != nullptr) + { + mxView.set(mrBase.GetController(), css::uno::UNO_QUERY); + onSelectionChanged(); + onChangeCurrentPage(); + } + } + break; + + default: + if (rEvent.meEventId != EventMultiplexerEventId::Disposing) + { + onSelectionChanged(); + onChangeCurrentPage(); + } + break; + } +} + +IMPL_LINK_NOARG(SlideTransitionPane, ApplyToAllButtonClicked, weld::Button&, void) +{ + DBG_ASSERT( mpDrawDoc, "Invalid Draw Document!" ); + if( !mpDrawDoc ) + return; + + ::sd::slidesorter::SharedPageSelection pPages = + std::make_shared<::sd::slidesorter::SlideSorterViewShell::PageSelection>(); + + sal_uInt16 nPageCount = mpDrawDoc->GetSdPageCount( PageKind::Standard ); + pPages->reserve( nPageCount ); + for( sal_uInt16 i=0; i<nPageCount; ++i ) + { + SdPage * pPage = mpDrawDoc->GetSdPage( i, PageKind::Standard ); + if( pPage ) + pPages->push_back( pPage ); + } + + if( ! pPages->empty()) + { + lcl_CreateUndoForPages( pPages, mrBase ); + lcl_ApplyToPages( pPages, getTransitionEffectFromControls() ); + } +} + +IMPL_LINK_NOARG(SlideTransitionPane, PlayButtonClicked, weld::Button&, void) +{ + playCurrentEffect(); +} + +IMPL_LINK_NOARG(SlideTransitionPane, TransitionSelected, ValueSet*, void) +{ + updateVariants( mxVS_TRANSITION_ICONS->GetSelectedItemId() - 1 ); + applyToSelectedPages(); +} + +/// we use an integer offset into the list of transition presets +void SlideTransitionPane::updateVariants( size_t nPresetOffset ) +{ + const sd::TransitionPresetList& rPresetList = sd::TransitionPreset::getTransitionPresetList(); + mxLB_VARIANT->clear(); + mxVS_TRANSITION_ICONS->SelectItem(nNoneId); + + if( nPresetOffset >= rPresetList.size() ) + { + mxLB_VARIANT->set_sensitive( false ); + } + else + { + auto pFound = rPresetList.begin(); + std::advance( pFound, nPresetOffset ); + + // Fill in the variant listbox + size_t nFirstItem = 0, nItem = 1; + for( const auto& aIt: rPresetList ) + { + if( aIt->getSetId() == (*pFound)->getSetId() ) + { + if (!nFirstItem) + nFirstItem = nItem; + if( !aIt->getVariantLabel().isEmpty() ) + { + mxLB_VARIANT->append_text( aIt->getVariantLabel() ); + if( *pFound == aIt ) + mxLB_VARIANT->set_active( mxLB_VARIANT->get_count()-1 ); + } + } + nItem++; + } + + if( mxLB_VARIANT->get_count() == 0 ) + mxLB_VARIANT->set_sensitive( false ); + else + mxLB_VARIANT->set_sensitive(true); + + // item has the id of the first transition from this set. + mxVS_TRANSITION_ICONS->SelectItem( nFirstItem ); + } +} + +IMPL_LINK_NOARG(SlideTransitionPane, AdvanceSlideRadioButtonToggled, weld::Toggleable&, void) +{ + updateControlState(); + applyToSelectedPages(false); +} + +IMPL_LINK_NOARG(SlideTransitionPane, AdvanceTimeModified, weld::MetricSpinButton&, void) +{ + applyToSelectedPages(false); +} + +IMPL_LINK_NOARG(SlideTransitionPane, VariantListBoxSelected, weld::ComboBox&, void) +{ + applyToSelectedPages(); +} + +IMPL_LINK_NOARG(SlideTransitionPane, DurationModifiedHdl, weld::MetricSpinButton&, void) +{ + double duration_value = static_cast<double>(mxCBX_duration->get_value(FieldUnit::SECOND)); + if (duration_value <= 0.0) + mxCBX_duration->set_value(0, FieldUnit::SECOND); + else + mxCBX_duration->set_value(duration_value, FieldUnit::SECOND); + + applyToSelectedPages(); +} + +IMPL_LINK_NOARG(SlideTransitionPane, DurationLoseFocusHdl, weld::Widget&, void) +{ + applyToSelectedPages(); +} + +IMPL_LINK_NOARG(SlideTransitionPane, SoundListBoxSelected, weld::ComboBox&, void) +{ + sal_Int32 nPos = mxLB_SOUND->get_active(); + if( nPos == 2 ) + { + // other sound... + openSoundFileDialog(); + } + updateControlState(); + applyToSelectedPages(); +} + +IMPL_LINK_NOARG(SlideTransitionPane, LoopSoundBoxChecked, weld::Toggleable&, void) +{ + applyToSelectedPages(); +} + +IMPL_LINK_NOARG(SlideTransitionPane, AutoPreviewClicked, weld::Toggleable&, void) +{ + SdOptions* pOptions = SD_MOD()->GetSdOptions(DocumentType::Impress); + pOptions->SetPreviewTransitions( mxCB_AUTO_PREVIEW->get_active() ); +} + +IMPL_LINK_NOARG(SlideTransitionPane, LateInitCallback, Timer *, void) +{ + const TransitionPresetList& rPresetList = TransitionPreset::getTransitionPresetList(); + + size_t nPresetOffset = 0; + for( const TransitionPresetPtr& pPreset: rPresetList ) + { + const OUString sLabel( pPreset->getSetLabel() ); + if( !sLabel.isEmpty() ) + { + if( m_aNumVariants.find( pPreset->getSetId() ) == m_aNumVariants.end() ) + { + OUString sImageName("sd/cmd/transition-" + pPreset->getSetId() + ".png"); + BitmapEx aIcon( sImageName ); + if ( aIcon.IsEmpty() ) // need a fallback + sImageName = "sd/cmd/transition-none.png"; + + mxVS_TRANSITION_ICONS->InsertItem( + nPresetOffset + 1, Image(StockImage::Yes, sImageName), sLabel, + VALUESET_APPEND, /* show legend */ true ); + + m_aNumVariants[ pPreset->getSetId() ] = 1; + } + else + { + m_aNumVariants[ pPreset->getSetId() ]++; + } + } + nPresetOffset++; + } + mxVS_TRANSITION_ICONS->Recalculate(); + + SAL_INFO( "sd.transitions", "Item transition offsets in ValueSet:"); + for( size_t i = 0; i < mxVS_TRANSITION_ICONS->GetItemCount(); ++i ) + SAL_INFO( "sd.transitions", i << ":" << mxVS_TRANSITION_ICONS->GetItemId( i ) ); + + nPresetOffset = 0; + SAL_INFO( "sd.transitions", "Transition presets by offsets:"); + for( const auto& aIter: rPresetList ) + { + SAL_INFO( "sd.transitions", nPresetOffset++ << " " << + aIter->getPresetId() << ": " << aIter->getSetId() ); + } + + updateSoundList(); + updateControls(); +} + +} // namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/animations/motionpathtag.cxx b/sd/source/ui/animations/motionpathtag.cxx new file mode 100644 index 000000000..ced685395 --- /dev/null +++ b/sd/source/ui/animations/motionpathtag.cxx @@ -0,0 +1,1200 @@ +/* -*- 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 <com/sun/star/util/XChangesNotifier.hpp> + +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> + +#include <sfx2/viewfrm.hxx> +#include <sfx2/dispatch.hxx> + +#include <svx/svdpagv.hxx> +#include <svx/sdrpagewindow.hxx> +#include <svx/sdrpaintwindow.hxx> +#include <svx/svdopath.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xlineit0.hxx> +#include <svx/xlndsit.hxx> +#include <svx/xlnclit.hxx> +#include <svx/xlnstit.hxx> +#include <svx/xlnedit.hxx> +#include <svx/xlnstwit.hxx> +#include <svx/xlnedwit.hxx> +#include <svx/xlnstcit.hxx> +#include <svx/xlnedcit.hxx> +#include <svx/xlntrit.hxx> +#include <svx/svxids.hrc> +#include <svx/polypolygoneditor.hxx> +#include <svx/svddrgmt.hxx> + +#include <CustomAnimationPane.hxx> +#include <View.hxx> +#include "motionpathtag.hxx" +#include <ViewShell.hxx> +#include <Window.hxx> + +#include <svx/sdr/contact/viewcontact.hxx> +#include <svx/sdr/overlay/overlayprimitive2dsequenceobject.hxx> + +using sdr::PolyPolygonEditor; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::drawing; + +namespace sd +{ + +const sal_uInt32 SMART_TAG_HDL_NUM = SAL_MAX_UINT32; +const int DRGPIX = 2; // Drag MinMove in Pixel + +namespace { + +class PathDragMove : public SdrDragMove +{ +private: + basegfx::B2DPolyPolygon maPathPolyPolygon; + +protected: + virtual void createSdrDragEntries() override; + +public: + PathDragMove(SdrDragView& rNewView, + const rtl::Reference <MotionPathTag >& xTag, + const basegfx::B2DPolyPolygon& rPathPolyPolygon) + : SdrDragMove(rNewView), + maPathPolyPolygon(rPathPolyPolygon), + mxTag( xTag ) + {} + + PathDragMove(SdrDragView& rNewView, + const rtl::Reference <MotionPathTag >& xTag) + : SdrDragMove(rNewView), + mxTag( xTag ) + {} + + virtual bool BeginSdrDrag() override; + virtual bool EndSdrDrag(bool bCopy) override; + + rtl::Reference <MotionPathTag > mxTag; +}; + +} + +void PathDragMove::createSdrDragEntries() +{ + // call parent + SdrDragMove::createSdrDragEntries(); + + if(maPathPolyPolygon.count()) + { + addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPolyPolygon(maPathPolyPolygon))); + } +} + +bool PathDragMove::BeginSdrDrag() +{ + if( mxTag.is() ) + { + SdrPathObj* pPathObj = mxTag->getPathObj(); + if( pPathObj ) + { + DragStat().SetActionRect(pPathObj->GetCurrentBoundRect()); + } + } + Show(); + return true; +} + +bool PathDragMove::EndSdrDrag(bool /*bCopy*/) +{ + Hide(); + if( mxTag.is() ) + mxTag->MovePath( DragStat().GetDX(), DragStat().GetDY() ); + return true; +} + +namespace { + +class PathDragResize : public SdrDragResize +{ +private: + basegfx::B2DPolyPolygon maPathPolyPolygon; + +protected: + virtual void createSdrDragEntries() override; + +public: + PathDragResize(SdrDragView& rNewView, + const rtl::Reference <MotionPathTag >& xTag, + const basegfx::B2DPolyPolygon& rPathPolyPolygon) + : SdrDragResize(rNewView), + maPathPolyPolygon(rPathPolyPolygon), + mxTag( xTag ) + {} + + PathDragResize(SdrDragView& rNewView, + const rtl::Reference <MotionPathTag >& xTag) + : SdrDragResize(rNewView), + mxTag( xTag ) + {} + + virtual bool EndSdrDrag(bool bCopy) override; + rtl::Reference <MotionPathTag > mxTag; +}; + +} + +void PathDragResize::createSdrDragEntries() +{ + // call parent + SdrDragResize::createSdrDragEntries(); + + if(maPathPolyPolygon.count()) + { + addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPolyPolygon(maPathPolyPolygon))); + } +} + +bool PathDragResize::EndSdrDrag(bool /*bCopy*/) +{ + Hide(); + if( mxTag.is() ) + { + SdrPathObj* pPathObj = mxTag->getPathObj(); + if( pPathObj ) + { + const Point aRef( DragStat().GetRef1() ); + basegfx::B2DHomMatrix aTrans(basegfx::utils::createTranslateB2DHomMatrix(-aRef.X(), -aRef.Y())); + aTrans.scale(double(aXFact), double(aYFact)); + aTrans.translate(aRef.X(), aRef.Y()); + basegfx::B2DPolyPolygon aDragPoly(pPathObj->GetPathPoly()); + aDragPoly.transform(aTrans); + pPathObj->SetPathPoly( aDragPoly ); + } + } + return true; +} + +namespace { + +class PathDragObjOwn : public SdrDragObjOwn +{ +private: + basegfx::B2DPolyPolygon maPathPolyPolygon; + +protected: + virtual void createSdrDragEntries() override; + +public: + PathDragObjOwn(SdrDragView& rNewView, + const basegfx::B2DPolyPolygon& rPathPolyPolygon) + : SdrDragObjOwn(rNewView), + maPathPolyPolygon(rPathPolyPolygon) + {} + + explicit PathDragObjOwn(SdrDragView& rNewView) + : SdrDragObjOwn(rNewView) + {} + + virtual bool EndSdrDrag(bool bCopy) override; +}; + +} + +void PathDragObjOwn::createSdrDragEntries() +{ + // call parent + SdrDragObjOwn::createSdrDragEntries(); + + if(maPathPolyPolygon.count()) + { + addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPolyPolygon(maPathPolyPolygon))); + } +} + +bool PathDragObjOwn::EndSdrDrag(bool /*bCopy*/) +{ + Hide(); + + SdrObject* pObj = GetDragObj(); + + if(pObj && pObj->applySpecialDrag(DragStat())) + { + pObj->SetChanged(); + pObj->BroadcastObjectChange(); + return true; + } + else + { + return false; + } +} + +namespace { + +class SdPathHdl : public SmartHdl +{ +public: + SdPathHdl( const SmartTagReference& xTag, SdrPathObj* mpPathObj ); + + virtual void CreateB2dIAObject() override; + virtual bool IsFocusHdl() const override; + +private: + SdrPathObj* mpPathObj; +}; + +} + +SdPathHdl::SdPathHdl( const SmartTagReference& xTag, SdrPathObj* pPathObj ) +: SmartHdl( xTag, pPathObj->GetCurrentBoundRect().TopLeft(), SdrHdlKind::SmartTag ) +, mpPathObj( pPathObj ) +{ +} + +void SdPathHdl::CreateB2dIAObject() +{ + // first throw away old one + GetRidOfIAObject(); + + if(!pHdlList) + return; + + SdrMarkView* pView = pHdlList->GetView(); + + if(!pView || pView->areMarkHandlesHidden()) + return; + + SdrPageView* pPageView = pView->GetSdrPageView(); + + if(!pPageView) + return; + + for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++) + { + const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b); + + if(rPageWindow.GetPaintWindow().OutputToWindow()) + { + const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager(); + if (xManager.is() && mpPathObj) + { + const sdr::contact::ViewContact& rVC = mpPathObj->GetViewContact(); + drawinglayer::primitive2d::Primitive2DContainer aSequence; + rVC.getViewIndependentPrimitive2DContainer(aSequence); + std::unique_ptr<sdr::overlay::OverlayObject> pNew(new sdr::overlay::OverlayPrimitive2DSequenceObject(std::move(aSequence))); + + // OVERLAYMANAGER + insertNewlyCreatedOverlayObjectForSdrHdl( + std::move(pNew), + rPageWindow.GetObjectContact(), + *xManager); + } + } + } +} + +bool SdPathHdl::IsFocusHdl() const +{ + return false; +} + +MotionPathTag::MotionPathTag( CustomAnimationPane& rPane, ::sd::View& rView, const CustomAnimationEffectPtr& pEffect ) +: SmartTag( rView ) +, mrPane( rPane ) +, mpEffect( pEffect ) +, mxOrigin( pEffect->getTargetShape() ) +, msLastPath( pEffect->getPath() ) +, mbInUpdatePath( false ) +{ + mpPathObj = mpEffect->createSdrPathObjFromPath(rView.getSdrModelFromSdrView()); + mxPolyPoly = mpPathObj->GetPathPoly(); + if (mxOrigin.is()) + maOriginPos = mxOrigin->getPosition(); + + XDash aDash( css::drawing::DashStyle_RECT, 1, 80, 1, 80, 80); + OUString aEmpty( "?" ); + mpPathObj->SetMergedItem( XLineDashItem( aEmpty, aDash ) ); + mpPathObj->SetMergedItem( XLineStyleItem( drawing::LineStyle_DASH ) ); + mpPathObj->SetMergedItem( XLineColorItem(aEmpty, COL_GRAY) ); + mpPathObj->SetMergedItem( XFillStyleItem( drawing::FillStyle_NONE ) ); + + ::basegfx::B2DPolygon aStartArrow; + aStartArrow.append(::basegfx::B2DPoint(20.0, 0.0)); + aStartArrow.append(::basegfx::B2DPoint(0.0, 0.0)); + aStartArrow.append(::basegfx::B2DPoint(10.0, 30.0)); + aStartArrow.setClosed(true); + mpPathObj->SetMergedItem(XLineStartItem(aEmpty,::basegfx::B2DPolyPolygon(aStartArrow))); + mpPathObj->SetMergedItem(XLineStartWidthItem(400)); + mpPathObj->SetMergedItem(XLineStartCenterItem(true)); + + updatePathAttributes(); + + mpPathObj->SetMergedItem(XLineTransparenceItem(50)); + + mpMark.reset(new SdrMark( mpPathObj, mrView.GetSdrPageView() )); + + mpPathObj->AddListener( *this ); + + Reference< XChangesNotifier > xNotifier( mpEffect->getNode(), UNO_QUERY ); + if( xNotifier.is() ) + { + xNotifier->addChangesListener( this ); + } +} + +MotionPathTag::~MotionPathTag() +{ + DBG_ASSERT( mpPathObj == nullptr, "sd::MotionPathTag::~MotionPathTag(), dispose me first!" ); + Dispose(); +} + +void MotionPathTag::updatePathAttributes() +{ + ::basegfx::B2DPolygon aCandidate; + if( mxPolyPoly.count() ) + { + aCandidate = mxPolyPoly.getB2DPolygon(0); + ::basegfx::utils::checkClosed( aCandidate ); + } + + if( !aCandidate.isClosed() ) + { + ::basegfx::B2DPolygon aEndArrow; + aEndArrow.append(::basegfx::B2DPoint(10.0, 0.0)); + aEndArrow.append(::basegfx::B2DPoint(0.0, 30.0)); + aEndArrow.append(::basegfx::B2DPoint(20.0, 30.0)); + aEndArrow.setClosed(true); + mpPathObj->SetMergedItem(XLineEndItem("?",::basegfx::B2DPolyPolygon(aEndArrow))); + mpPathObj->SetMergedItem(XLineEndWidthItem(400)); + mpPathObj->SetMergedItem(XLineEndCenterItem(true)); + } + else + { + mpPathObj->SetMergedItem(XLineEndItem()); + } +} + +void MotionPathTag::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint ) +{ + if( !(mpPathObj && !mbInUpdatePath && rHint.GetId() == SfxHintId::ThisIsAnSdrHint && mpEffect) ) + return; + + if( mxPolyPoly != mpPathObj->GetPathPoly() ) + { + mbInUpdatePath = true; + mxPolyPoly = mpPathObj->GetPathPoly(); + rtl::Reference< MotionPathTag > xTag( this ); + mrPane.updatePathFromMotionPathTag( xTag ); + msLastPath = mpEffect->getPath(); + updatePathAttributes(); + mbInUpdatePath = false; + } +} + +void MotionPathTag::MovePath( int nDX, int nDY ) +{ + if( mpPathObj ) + { + mpPathObj->Move( Size( nDX, nDY ) ); + mrView.updateHandles(); + } +} + +/** returns true if the MotionPathTag handled the event. */ +bool MotionPathTag::MouseButtonDown( const MouseEvent& rMEvt, SmartHdl& rHdl ) +{ + if( !mpPathObj ) + return false; + + if( !isSelected() ) + { + SmartTagReference xTag( this ); + mrView.getSmartTags().select( xTag ); + selectionChanged(); + return true; + } + else + { + if( rMEvt.IsLeft() && (rMEvt.GetClicks() == 2) ) + { + mrView.GetViewShell()->GetViewFrame()->GetDispatcher()->Execute(SID_BEZIER_EDIT, SfxCallMode::ASYNCHRON); + return true; + } + else if( rMEvt.IsLeft() ) + { + OutputDevice* pOut = mrView.GetViewShell()->GetActiveWindow()->GetOutDev(); + Point aMDPos( pOut->PixelToLogic( rMEvt.GetPosPixel() ) ); + + if( !mrView.IsFrameDragSingles() && mrView.IsInsObjPointMode() && (rHdl.GetObjHdlNum() == SMART_TAG_HDL_NUM) ) + { + // insert a point in edit mode + const bool bNewObj = rMEvt.IsMod1(); + + mrView.BrkAction(); + + Point aPt(aMDPos); // - pMarkedPV->GetOffset()); + + if(bNewObj) + aPt = mrView.GetSnapPos(aPt,mrView.GetSdrPageView()); + + bool bClosed0(mpPathObj->IsClosedObj()); + + sal_uInt32 nInsPointNum = mpPathObj->NbcInsPointOld(aPt, bNewObj); + + if(bClosed0 != mpPathObj->IsClosedObj()) + { + // Obj was closed implicit + // object changed + mpPathObj->SetChanged(); + mpPathObj->BroadcastObjectChange(); + } + + if(0xffffffff != nInsPointNum) + { + mrView.UnmarkAllPoints(); + mrView.updateHandles(); + + bool bRet = mrView.BegDragObj(aMDPos, pOut, mrView.GetHdl(nInsPointNum+1), 0, new PathDragObjOwn( mrView ) ); + + if (bRet) + { + const_cast< SdrDragStat* >( &mrView.GetDragStat() )->SetMinMoved(); + mrView.MovDragObj(aMDPos); + } + } + return true; + } + else + { + SmartHdl* pHdl = &rHdl; + if (!mrView.IsPointMarked(*pHdl) || rMEvt.IsShift()) + { + if (!rMEvt.IsShift()) + { + mrView.UnmarkAllPoints(); + pHdl = dynamic_cast< SmartHdl* >( mrView.PickHandle(aMDPos) ); + } + else + { + if (mrView.IsPointMarked(*pHdl) ) + { + mrView.UnmarkPoint(*pHdl); + pHdl = nullptr; + } + else + { + pHdl = dynamic_cast< SmartHdl* >( mrView.PickHandle(aMDPos) ); + } + } + + if (pHdl) + mrView.MarkPoint(*pHdl); + } + + if( pHdl && !rMEvt.IsRight() ) + { + mrView.BrkAction(); + const sal_uInt16 nDrgLog = static_cast<sal_uInt16>(pOut->PixelToLogic(Size(DRGPIX,0)).Width()); + + rtl::Reference< MotionPathTag > xTag( this ); + SdrDragMethod* pDragMethod; + + // #i95646# add DragPoly as geometry to each local SdrDragMethod to be able + // to create the needed local SdrDragEntry for it in createSdrDragEntries() + const basegfx::B2DPolyPolygon aDragPoly(mpPathObj->GetPathPoly()); + + if( (pHdl->GetKind() == SdrHdlKind::Move) || (pHdl->GetKind() == SdrHdlKind::SmartTag) ) + { + pDragMethod = new PathDragMove( mrView, xTag, aDragPoly ); + pHdl->SetPos( aMDPos ); + } + else if( pHdl->GetKind() == SdrHdlKind::Poly ) + { + pDragMethod = new PathDragObjOwn( mrView, aDragPoly ); + } + else + { + pDragMethod = new PathDragResize( mrView, xTag, aDragPoly ); + } + + mrView.BegDragObj(aMDPos, nullptr, pHdl, nDrgLog, pDragMethod ); + } + return true; + } + } + } + + return false; +} + +/** returns true if the SmartTag consumes this event. */ +bool MotionPathTag::KeyInput( const KeyEvent& rKEvt ) +{ + if( !mpPathObj ) + return false; + + sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode(); + switch( nCode ) + { + case KEY_DELETE: + return OnDelete(); + + case KEY_DOWN: + case KEY_UP: + case KEY_LEFT: + case KEY_RIGHT: + return OnMove( rKEvt ); + + case KEY_ESCAPE: + { + SmartTagReference xThis( this ); + mrView.getSmartTags().deselect(); + return true; + } + + case KEY_TAB: + return OnTabHandles( rKEvt ); + + case KEY_SPACE: + return OnMarkHandle( rKEvt ); + + default: + break; + } + return false; +} + +bool MotionPathTag::OnDelete() +{ + mrPane.remove( mpEffect ); + return true; +} + +bool MotionPathTag::OnTabHandles( const KeyEvent& rKEvt ) +{ + if(rKEvt.GetKeyCode().IsMod1() || rKEvt.GetKeyCode().IsMod2()) + { + const SdrHdlList& rHdlList = mrView.GetHdlList(); + bool bForward(!rKEvt.GetKeyCode().IsShift()); + + const_cast<SdrHdlList&>(rHdlList).TravelFocusHdl(bForward); + + // guarantee visibility of focused handle + SdrHdl* pHdl = rHdlList.GetFocusHdl(); + + if(pHdl) + { + Window* pWindow = mrView.GetViewShell()->GetActiveWindow(); + if( pWindow ) + { + Point aHdlPosition(pHdl->GetPos()); + ::tools::Rectangle aVisRect(aHdlPosition - Point(100, 100), Size(200, 200)); + mrView.MakeVisible(aVisRect, *pWindow); + } + } + + return true; + } + + return false; +} + +bool MotionPathTag::OnMarkHandle( const KeyEvent& rKEvt ) +{ + const SdrHdlList& rHdlList = mrView.GetHdlList(); + SdrHdl* pHdl = rHdlList.GetFocusHdl(); + + if(pHdl && pHdl->GetKind() == SdrHdlKind::Poly ) + { + // rescue ID of point with focus + sal_uInt32 nPol(pHdl->GetPolyNum()); + sal_uInt32 nPnt(pHdl->GetPointNum()); + + if(mrView.IsPointMarked(*pHdl)) + { + if(rKEvt.GetKeyCode().IsShift()) + { + mrView.UnmarkPoint(*pHdl); + } + } + else + { + if(!rKEvt.GetKeyCode().IsShift()) + { + mrView.UnmarkAllPoints(); + } + mrView.MarkPoint(*pHdl); + } + + if(nullptr == rHdlList.GetFocusHdl()) + { + // restore point with focus + SdrHdl* pNewOne = nullptr; + + for(size_t a = 0; !pNewOne && a < rHdlList.GetHdlCount(); ++a) + { + SdrHdl* pAct = rHdlList.GetHdl(a); + + if(pAct && pAct->GetKind() == SdrHdlKind::Poly && pAct->GetPolyNum() == nPol && pAct->GetPointNum() == nPnt) + pNewOne = pAct; + } + + if(pNewOne) + const_cast<SdrHdlList&>(rHdlList).SetFocusHdl(pNewOne); + } + } + + return true; +} + +bool MotionPathTag::OnMove( const KeyEvent& rKEvt ) +{ + ::tools::Long nX = 0; + ::tools::Long nY = 0; + + switch( rKEvt.GetKeyCode().GetCode() ) + { + case KEY_UP: nY = -1; break; + case KEY_DOWN: nY = 1; break; + case KEY_LEFT: nX = -1; break; + case KEY_RIGHT: nX = 1; break; + default: break; + } + + if(rKEvt.GetKeyCode().IsMod2()) + { + OutputDevice* pOut = mrView.GetViewShell()->GetActiveWindow()->GetOutDev(); + Size aLogicSizeOnePixel = pOut ? pOut->PixelToLogic(Size(1,1)) : Size(100, 100); + nX *= aLogicSizeOnePixel.Width(); + nY *= aLogicSizeOnePixel.Height(); + } + else + { + // old, fixed move distance + nX *= 100; + nY *= 100; + } + + if( nX || nY ) + { + // in point edit mode move the handle with the focus + const SdrHdlList& rHdlList = mrView.GetHdlList(); + SdrHdl* pHdl = rHdlList.GetFocusHdl(); + + if(pHdl) + { + // now move the Handle (nX, nY) + Point aStartPoint(pHdl->GetPos()); + Point aEndPoint(pHdl->GetPos() + Point(nX, nY)); + + // start dragging + rtl::Reference< MotionPathTag > xTag( this ); + SdrDragMethod* pDragMethod = nullptr; + if( (pHdl->GetKind() == SdrHdlKind::Move) || (pHdl->GetKind() == SdrHdlKind::SmartTag) ) + { + pDragMethod = new PathDragMove( mrView, xTag ); + } + else if( pHdl->GetKind() == SdrHdlKind::Poly ) + { + pDragMethod = new PathDragObjOwn( mrView ); + } + else if( pHdl->GetKind() != SdrHdlKind::BezierWeight ) + { + pDragMethod = new PathDragResize( mrView, xTag ); + } + mrView.BegDragObj(aStartPoint, nullptr, pHdl, 0, pDragMethod); + + if(mrView.IsDragObj()) + { + bool bWasNoSnap = mrView.GetDragStat().IsNoSnap(); + bool bWasSnapEnabled = mrView.IsSnapEnabled(); + + // switch snapping off + if(!bWasNoSnap) + const_cast<SdrDragStat&>(mrView.GetDragStat()).SetNoSnap(); + if(bWasSnapEnabled) + mrView.SetSnapEnabled(false); + + mrView.MovAction(aEndPoint); + mrView.EndDragObj(); + + // restore snap + if(!bWasNoSnap) + const_cast<SdrDragStat&>(mrView.GetDragStat()).SetNoSnap(bWasNoSnap); + if(bWasSnapEnabled) + mrView.SetSnapEnabled(bWasSnapEnabled); + } + } + else + { + // move the path + MovePath( nX, nY ); + } + } + + return true; +} + +sal_Int32 MotionPathTag::GetMarkablePointCount() const +{ + if( mpPathObj && isSelected() ) + { + return mpPathObj->GetPointCount(); + } + else + { + return 0; + } +} + +sal_Int32 MotionPathTag::GetMarkedPointCount() const +{ + if( mpMark ) + { + const SdrUShortCont& rPts = mpMark->GetMarkedPoints(); + return rPts.size(); + } + else + { + return 0; + } +} + +bool MotionPathTag::MarkPoint(SdrHdl& rHdl, bool bUnmark ) +{ + bool bRet=false; + if( mpPathObj && mrView.IsPointMarkable( rHdl ) && (rHdl.GetKind() != SdrHdlKind::SmartTag) ) + { + SmartHdl* pSmartHdl = dynamic_cast< SmartHdl* >( &rHdl ); + if( pSmartHdl && pSmartHdl->getTag().get() == this ) + { + if (mrView.MarkPointHelper(&rHdl,mpMark.get(),bUnmark)) + { + mrView.MarkListHasChanged(); + bRet=true; + } + } + } + return bRet; +} + +bool MotionPathTag::MarkPoints(const ::tools::Rectangle* pRect, bool bUnmark ) +{ + bool bChgd=false; + + if( mpPathObj && isSelected() ) + { + size_t nHdlNum = mrView.GetHdlList().GetHdlCount(); + if ( nHdlNum <= 1 ) + return false; + + while( --nHdlNum > 0 ) + { + SmartHdl* pHdl = dynamic_cast< SmartHdl* >( mrView.GetHdl( nHdlNum ) ); + + if( pHdl && (pHdl->getTag().get() == this) && mrView.IsPointMarkable(*pHdl) && pHdl->IsSelected() == bUnmark) + { + Point aPos(pHdl->GetPos()); + if( pRect==nullptr || pRect->Contains(aPos)) + { + if( mrView.MarkPointHelper(pHdl,mpMark.get(),bUnmark) ) + bChgd=true; + } + } + } + + if(bChgd) + mrView.MarkListHasChanged(); + } + + return bChgd; +} + +bool MotionPathTag::getContext( SdrViewContext& rContext ) +{ + if( mpPathObj && isSelected() && !mrView.IsFrameDragSingles() ) + { + rContext = SdrViewContext::PointEdit; + return true; + } + else + { + return false; + } +} + +void MotionPathTag::CheckPossibilities() +{ + if( !(mpPathObj && isSelected()) ) + return; + + mrView.SetMoveAllowed( true ); + mrView.SetMoveProtected( false ); + mrView.SetResizeFreeAllowed( true ); + mrView.SetResizePropAllowed( true ); + mrView.SetResizeProtected( false ); + + if( !mrView.IsFrameDragSingles() ) + { + bool b1stSmooth(true); + bool b1stSegm(true); + bool bCurve(false); + bool bSmoothFuz(false); + bool bSegmFuz(false); + basegfx::B2VectorContinuity eSmooth = basegfx::B2VectorContinuity::NONE; + + mrView.CheckPolyPossibilitiesHelper( mpMark.get(), b1stSmooth, b1stSegm, bCurve, bSmoothFuz, bSegmFuz, eSmooth ); + } +} + +void MotionPathTag::addCustomHandles( SdrHdlList& rHandlerList ) +{ + if( !mpPathObj ) + return; + + css::awt::Point aPos; + if (mxOrigin.is()) + aPos = mxOrigin->getPosition(); + if( (aPos.X != maOriginPos.X) || (aPos.Y != maOriginPos.Y) ) + { + const basegfx::B2DHomMatrix aTransform(basegfx::utils::createTranslateB2DHomMatrix( + aPos.X - maOriginPos.X, aPos.Y - maOriginPos.Y)); + mxPolyPoly.transform( aTransform ); + mpPathObj->SetPathPoly( mxPolyPoly ); + maOriginPos = aPos; + } + + SmartTagReference xThis( this ); + std::unique_ptr<SdPathHdl> pHdl(new SdPathHdl( xThis, mpPathObj )); + pHdl->SetObjHdlNum( SMART_TAG_HDL_NUM ); + pHdl->SetPageView( mrView.GetSdrPageView() ); + pHdl->SetObj(mpPathObj); + rHandlerList.AddHdl( std::move(pHdl) ); + + if( !isSelected() ) + return; + + mrView.GetSdrPageView()->SetHasMarkedObj(true); + + if( !mrView.IsFrameDragSingles() ) + { + SdrHdlList aTemp( rHandlerList.GetView() ); + mpPathObj->AddToHdlList( aTemp ); + const SdrUShortCont& rMrkPnts = mpMark->GetMarkedPoints(); + + for( size_t nHandle = 0; nHandle < aTemp.GetHdlCount(); ++nHandle ) + { + SdrHdl* pTempHdl = aTemp.GetHdl( nHandle ); + + SmartHdl* pSmartHdl = new SmartHdl( xThis, mpPathObj, pTempHdl->GetPos(), pTempHdl->GetKind() ); + pSmartHdl->SetObjHdlNum( static_cast<sal_uInt32>(nHandle) ); + pSmartHdl->SetPolyNum( pTempHdl->GetPolyNum() ); + pSmartHdl->SetPointNum( pTempHdl->GetPointNum() ); + pSmartHdl->SetPlusHdl( pTempHdl->IsPlusHdl() ); + pSmartHdl->SetSourceHdlNum( pTempHdl->GetSourceHdlNum() ); + pSmartHdl->SetPageView( mrView.GetSdrPageView() ); + + rHandlerList.AddHdl( std::unique_ptr<SmartHdl>(pSmartHdl) ); + + const bool bSelected = rMrkPnts.find( sal_uInt16(nHandle) ) != rMrkPnts.end(); + pSmartHdl->SetSelected(bSelected); + + if( mrView.IsPlusHandlesAlwaysVisible() || bSelected ) + { + SdrHdlList plusList(nullptr); + mpPathObj->AddToPlusHdlList(plusList, *pSmartHdl); + sal_uInt32 nPlusHdlCnt=plusList.GetHdlCount(); + for (sal_uInt32 nPlusNum=0; nPlusNum<nPlusHdlCnt; nPlusNum++) + { + SdrHdl* pPlusHdl = plusList.GetHdl(nPlusNum); + pPlusHdl->SetObj(mpPathObj); + pPlusHdl->SetPageView(mrView.GetSdrPageView()); + pPlusHdl->SetPlusHdl(true); + } + plusList.MoveTo(rHandlerList); + } + } + } + else + { + ::tools::Rectangle aRect(mpPathObj->GetCurrentBoundRect()); + + if(!aRect.IsEmpty()) + { + size_t nCount = rHandlerList.GetHdlCount(); + + bool bWdt0=aRect.Left()==aRect.Right(); + bool bHgt0=aRect.Top()==aRect.Bottom(); + if (bWdt0 && bHgt0) + { + rHandlerList.AddHdl(std::make_unique<SmartHdl>( xThis, mpPathObj, aRect.TopLeft(),SdrHdlKind::UpperLeft)); + } + else if (bWdt0 || bHgt0) + { + rHandlerList.AddHdl(std::make_unique<SmartHdl>( xThis, mpPathObj, aRect.TopLeft() ,SdrHdlKind::UpperLeft)); + rHandlerList.AddHdl(std::make_unique<SmartHdl>( xThis, mpPathObj, aRect.BottomRight(),SdrHdlKind::LowerRight)); + } + else // !bWdt0 && !bHgt0 + { + rHandlerList.AddHdl(std::make_unique<SmartHdl>( xThis, mpPathObj, aRect.TopLeft() ,SdrHdlKind::UpperLeft)); + rHandlerList.AddHdl(std::make_unique<SmartHdl>( xThis, mpPathObj, aRect.TopCenter() ,SdrHdlKind::Upper)); + rHandlerList.AddHdl(std::make_unique<SmartHdl>( xThis, mpPathObj, aRect.TopRight() ,SdrHdlKind::UpperRight)); + rHandlerList.AddHdl(std::make_unique<SmartHdl>( xThis, mpPathObj, aRect.LeftCenter() ,SdrHdlKind::Left )); + rHandlerList.AddHdl(std::make_unique<SmartHdl>( xThis, mpPathObj, aRect.RightCenter() ,SdrHdlKind::Right)); + rHandlerList.AddHdl(std::make_unique<SmartHdl>( xThis, mpPathObj, aRect.BottomLeft() ,SdrHdlKind::LowerLeft)); + rHandlerList.AddHdl(std::make_unique<SmartHdl>( xThis, mpPathObj, aRect.BottomCenter(),SdrHdlKind::Lower)); + rHandlerList.AddHdl(std::make_unique<SmartHdl>( xThis, mpPathObj, aRect.BottomRight() ,SdrHdlKind::LowerRight)); + } + + while( nCount < rHandlerList.GetHdlCount() ) + { + rHandlerList.GetHdl(nCount++)->SetPageView( mrView.GetSdrPageView() ); + } + } + } +} + +void MotionPathTag::disposing() +{ + Reference< XChangesNotifier > xNotifier( mpEffect->getNode(), UNO_QUERY ); + if( xNotifier.is() ) + { + xNotifier->removeChangesListener( this ); + } + + if( mpPathObj ) + { + SdrObject* pTemp(mpPathObj); + mpPathObj = nullptr; + mrView.updateHandles(); + + // always use SdrObject::Free(...) for SdrObjects (!) + SdrObject::Free(pTemp); + } + + mpMark.reset(); + + SmartTag::disposing(); +} + +void MotionPathTag::deselect() +{ + SmartTag::deselect(); + + if( mpMark ) + { + SdrUShortCont& rPts = mpMark->GetMarkedPoints(); + rPts.clear(); + } + + selectionChanged(); +} + +void MotionPathTag::selectionChanged() +{ + if( mrView.GetViewShell() && mrView.GetViewShell()->GetViewFrame() ) + { + SfxBindings& rBindings = mrView.GetViewShell()->GetViewFrame()->GetBindings(); + rBindings.InvalidateAll(true); + } +} + +// IPolyPolygonEditorController + +void MotionPathTag::DeleteMarkedPoints() +{ + if( !(mpPathObj && IsDeleteMarkedPointsPossible()) ) + return; + + mrView.BrkAction(); + + SdrUShortCont& rPts = mpMark->GetMarkedPoints(); + PolyPolygonEditor aEditor( mpPathObj->GetPathPoly()); + if (aEditor.DeletePoints(rPts)) + { + if( aEditor.GetPolyPolygon().count() ) + { + mpPathObj->SetPathPoly( aEditor.GetPolyPolygon() ); + } + + mrView.UnmarkAllPoints(); + mrView.MarkListHasChanged(); + mrView.updateHandles(); + } +} + +bool MotionPathTag::IsDeleteMarkedPointsPossible() const +{ + return mpPathObj && isSelected() && (GetMarkedPointCount() != 0); +} + +void MotionPathTag::RipUpAtMarkedPoints() +{ + // not supported for motion path +} + +bool MotionPathTag::IsRipUpAtMarkedPointsPossible() const +{ + // not supported for motion path + return false; +} + +bool MotionPathTag::IsSetMarkedSegmentsKindPossible() const +{ + if( mpPathObj ) + return mrView.IsSetMarkedSegmentsKindPossible(); + else + return false; +} + +SdrPathSegmentKind MotionPathTag::GetMarkedSegmentsKind() const +{ + if( mpPathObj ) + return mrView.GetMarkedSegmentsKind(); + else + return SdrPathSegmentKind::Line; +} + +void MotionPathTag::SetMarkedSegmentsKind(SdrPathSegmentKind eKind) +{ + if(mpPathObj && isSelected() && (GetMarkedPointCount() != 0)) + { + SdrUShortCont& rPts = mpMark->GetMarkedPoints(); + PolyPolygonEditor aEditor( mpPathObj->GetPathPoly() ); + if (aEditor.SetSegmentsKind(eKind, rPts)) + { + mpPathObj->SetPathPoly(aEditor.GetPolyPolygon()); + mrView.MarkListHasChanged(); + mrView.updateHandles(); + } + } +} + +bool MotionPathTag::IsSetMarkedPointsSmoothPossible() const +{ + if( mpPathObj ) + return mrView.IsSetMarkedPointsSmoothPossible(); + else + return false; +} + +SdrPathSmoothKind MotionPathTag::GetMarkedPointsSmooth() const +{ + if( mpPathObj ) + return mrView.GetMarkedPointsSmooth(); + else + return SdrPathSmoothKind::Angular; +} + +void MotionPathTag::SetMarkedPointsSmooth(SdrPathSmoothKind eKind) +{ + basegfx::B2VectorContinuity eFlags; + + if(SdrPathSmoothKind::Angular == eKind) + { + eFlags = basegfx::B2VectorContinuity::NONE; + } + else if(SdrPathSmoothKind::Asymmetric == eKind) + { + eFlags = basegfx::B2VectorContinuity::C1; + } + else if(SdrPathSmoothKind::Symmetric == eKind) + { + eFlags = basegfx::B2VectorContinuity::C2; + } + else + { + return; + } + + if(mpPathObj && mpMark && isSelected() && (GetMarkedPointCount() != 0)) + { + SdrUShortCont& rPts = mpMark->GetMarkedPoints(); + PolyPolygonEditor aEditor( mpPathObj->GetPathPoly()); + if (aEditor.SetPointsSmooth(eFlags, rPts)) + { + mpPathObj->SetPathPoly(aEditor.GetPolyPolygon()); + mrView.MarkListHasChanged(); + mrView.updateHandles(); + } + } +} + +bool MotionPathTag::IsOpenCloseMarkedObjectsPossible() const +{ + // not supported for motion path + return false; +} + +SdrObjClosedKind MotionPathTag::GetMarkedObjectsClosedState() const +{ + // not supported for motion path + return SdrObjClosedKind::Open; +} + +// XChangesListener +void SAL_CALL MotionPathTag::changesOccurred( const ChangesEvent& /*Event*/ ) +{ + if( mpPathObj && !mbInUpdatePath && (mpEffect->getPath() != msLastPath) ) + { + mbInUpdatePath =true; + msLastPath = mpEffect->getPath(); + mpEffect->updateSdrPathObjFromPath( *mpPathObj ); + mbInUpdatePath = false; + updatePathAttributes(); + mrView.updateHandles(); + } +} + +void SAL_CALL MotionPathTag::disposing( const EventObject& /*Source*/ ) +{ + if( mpPathObj ) + Dispose(); +} + +Any SAL_CALL MotionPathTag::queryInterface( const css::uno::Type& aType ) +{ + if( aType == cppu::UnoType<XChangesListener>::get() ) + return Any( Reference< XChangesListener >( this ) ); + if( aType == cppu::UnoType<XEventListener>::get() ) + return Any( Reference< XEventListener >( this ) ); + if( aType == cppu::UnoType<XInterface>::get() ) + return Any( Reference< XInterface >( this ) ); + + return Any(); +} + +void SAL_CALL MotionPathTag::acquire() noexcept +{ + SimpleReferenceComponent::acquire(); +} + +void SAL_CALL MotionPathTag::release( ) noexcept +{ + SimpleReferenceComponent::release(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/animations/motionpathtag.hxx b/sd/source/ui/animations/motionpathtag.hxx new file mode 100644 index 000000000..715ce4268 --- /dev/null +++ b/sd/source/ui/animations/motionpathtag.hxx @@ -0,0 +1,114 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/util/XChangesListener.hpp> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <smarttag.hxx> +#include <CustomAnimationList.hxx> + +namespace com::sun::star::drawing { class XShape; } +class SdrPathObj; + +namespace sd { + +class View; +class CustomAnimationPane; + +/// Base class for all functions. +class MotionPathTag final : public SmartTag, public IPolyPolygonEditorController, public SfxListener, public css::util::XChangesListener +{ +public: + MotionPathTag( CustomAnimationPane& rPane, ::sd::View& rView, const CustomAnimationEffectPtr& pEffect ); + virtual ~MotionPathTag() override; + + SdrPathObj* getPathObj() const { return mpPathObj; } + + /// @return true if the SmartTag handled the event. + virtual bool MouseButtonDown( const MouseEvent&, SmartHdl& ) override; + + /// @return true if the SmartTag consumes this event. + virtual bool KeyInput( const KeyEvent& rKEvt ) override; + + // callbacks from sdr view + virtual sal_Int32 GetMarkablePointCount() const override; + virtual sal_Int32 GetMarkedPointCount() const override; + virtual bool MarkPoint(SdrHdl& rHdl, bool bUnmark) override; + virtual void CheckPossibilities() override; + virtual bool MarkPoints(const ::tools::Rectangle* pRect, bool bUnmark) override; + + const CustomAnimationEffectPtr& getEffect() const { return mpEffect; } + + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override; + + // IPolyPolygonEditorController + virtual void DeleteMarkedPoints() override; + virtual bool IsDeleteMarkedPointsPossible() const override; + + virtual void RipUpAtMarkedPoints() override; + virtual bool IsRipUpAtMarkedPointsPossible() const override; + + virtual bool IsSetMarkedSegmentsKindPossible() const override; + virtual SdrPathSegmentKind GetMarkedSegmentsKind() const override; + virtual void SetMarkedSegmentsKind(SdrPathSegmentKind eKind) override; + + virtual bool IsSetMarkedPointsSmoothPossible() const override; + virtual SdrPathSmoothKind GetMarkedPointsSmooth() const override; + virtual void SetMarkedPointsSmooth(SdrPathSmoothKind eKind) override; + + virtual bool IsOpenCloseMarkedObjectsPossible() const override; + virtual SdrObjClosedKind GetMarkedObjectsClosedState() const override; + + void MovePath( int nDX, int nDY ); + bool OnDelete(); + bool OnTabHandles( const KeyEvent& rKEvt ); + bool OnMarkHandle( const KeyEvent& rKEvt ); + bool OnMove( const KeyEvent& rKEvt ); + + // XChangesListener + virtual void SAL_CALL changesOccurred( const css::util::ChangesEvent& Event ) override; + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override; + virtual void SAL_CALL acquire( ) noexcept override; + virtual void SAL_CALL release( ) noexcept override; + +private: + virtual void addCustomHandles( SdrHdlList& rHandlerList ) override; + virtual bool getContext( SdrViewContext& rContext ) override; + virtual void disposing() override; + virtual void deselect() override; + + void updatePathAttributes(); + void selectionChanged(); + + CustomAnimationPane& mrPane; + CustomAnimationEffectPtr mpEffect; + ::basegfx::B2DPolyPolygon mxPolyPoly; + css::uno::Reference< css::drawing::XShape > mxOrigin; + SdrPathObj* mpPathObj; + css::awt::Point maOriginPos; + std::unique_ptr<SdrMark> mpMark; + OUString msLastPath; + bool mbInUpdatePath; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/annotations/annotationmanager.cxx b/sd/source/ui/annotations/annotationmanager.cxx new file mode 100644 index 000000000..ab9fe0c1a --- /dev/null +++ b/sd/source/ui/annotations/annotationmanager.cxx @@ -0,0 +1,1220 @@ +/* -*- 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 <com/sun/star/drawing/XDrawView.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/geometry/RealPoint2D.hpp> +#include <com/sun/star/text/XText.hpp> +#include <com/sun/star/document/XEventBroadcaster.hpp> +#include <com/sun/star/office/XAnnotationAccess.hpp> +#include <comphelper/lok.hxx> +#include <svx/svxids.hrc> + +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> + +#include <sal/macros.h> +#include <svl/itempool.hxx> +#include <svl/intitem.hxx> +#include <unotools/localedatawrapper.hxx> +#include <unotools/useroptions.hxx> +#include <unotools/syslocale.hxx> +#include <unotools/saveopt.hxx> + +#include <tools/datetime.hxx> +#include <tools/UnitConversion.hxx> +#include <tools/diagnose_ex.h> + +#include <sfx2/viewfrm.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/request.hxx> +#include <sfx2/dispatch.hxx> + +#include <editeng/editeng.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/outlobj.hxx> +#include <editeng/postitem.hxx> + +#include <svx/postattr.hxx> + +#include <annotationmanager.hxx> +#include "annotationmanagerimpl.hxx" +#include "annotationwindow.hxx" +#include <strings.hrc> + +#include <Annotation.hxx> +#include <DrawDocShell.hxx> +#include <DrawViewShell.hxx> +#include <sdresid.hxx> +#include <EventMultiplexer.hxx> +#include <ViewShellBase.hxx> +#include <sdpage.hxx> +#include <drawdoc.hxx> +#include <textapi.hxx> +#include <optsitem.hxx> +#include <sdmod.hxx> + +#include <memory> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::document; +using namespace ::com::sun::star::geometry; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::view; +using namespace ::com::sun::star::style; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::ui; +using namespace ::com::sun::star::task; +using namespace ::com::sun::star::office; + +namespace sd { + +SfxItemPool* GetAnnotationPool() +{ + static rtl::Reference<SfxItemPool> s_pAnnotationPool; + if( !s_pAnnotationPool ) + { + s_pAnnotationPool = EditEngine::CreatePool(); + s_pAnnotationPool->SetPoolDefaultItem(SvxFontHeightItem(423,100,EE_CHAR_FONTHEIGHT)); + + vcl::Font aAppFont( Application::GetSettings().GetStyleSettings().GetAppFont() ); + s_pAnnotationPool->SetPoolDefaultItem(SvxFontItem(aAppFont.GetFamilyType(),aAppFont.GetFamilyName(),"",PITCH_DONTKNOW,RTL_TEXTENCODING_DONTKNOW,EE_CHAR_FONTINFO)); + } + + return s_pAnnotationPool.get(); +} + +static SfxBindings* getBindings( ViewShellBase const & rBase ) +{ + if( rBase.GetMainViewShell() && rBase.GetMainViewShell()->GetViewFrame() ) + return &rBase.GetMainViewShell()->GetViewFrame()->GetBindings(); + + return nullptr; +} + +static SfxDispatcher* getDispatcher( ViewShellBase const & rBase ) +{ + if( rBase.GetMainViewShell() && rBase.GetMainViewShell()->GetViewFrame() ) + return rBase.GetMainViewShell()->GetViewFrame()->GetDispatcher(); + + return nullptr; +} + +css::util::DateTime getCurrentDateTime() +{ + DateTime aCurrentDate( DateTime::SYSTEM ); + return css::util::DateTime( 0, aCurrentDate.GetSec(), + aCurrentDate.GetMin(), aCurrentDate.GetHour(), + aCurrentDate.GetDay(), aCurrentDate.GetMonth(), + aCurrentDate.GetYear(), false ); +} + +OUString getAnnotationDateTimeString( const Reference< XAnnotation >& xAnnotation ) +{ + OUString sRet; + if( xAnnotation.is() ) + { + const SvtSysLocale aSysLocale; + const LocaleDataWrapper& rLocalData = aSysLocale.GetLocaleData(); + + css::util::DateTime aDateTime( xAnnotation->getDateTime() ); + + Date aSysDate( Date::SYSTEM ); + Date aDate( aDateTime.Day, aDateTime.Month, aDateTime.Year ); + if (aDate==aSysDate) + sRet = SdResId(STR_ANNOTATION_TODAY); + else if (aDate == (aSysDate-1)) + sRet = SdResId(STR_ANNOTATION_YESTERDAY); + else if (aDate.IsValidAndGregorian() ) + sRet = rLocalData.getDate(aDate); + + ::tools::Time aTime( aDateTime ); + if(aTime.GetTime() != 0) + sRet += " " + rLocalData.getTime( aTime,false ); + } + return sRet; +} + +AnnotationManagerImpl::AnnotationManagerImpl( ViewShellBase& rViewShellBase ) +: mrBase( rViewShellBase ) +, mpDoc( rViewShellBase.GetDocument() ) +, mbShowAnnotations( true ) +, mnUpdateTagsEvent( nullptr ) +{ + SdOptions* pOptions = SD_MOD()->GetSdOptions(mpDoc->GetDocumentType()); + if( pOptions ) + mbShowAnnotations = pOptions->IsShowComments(); +} + +void AnnotationManagerImpl::init() +{ + // get current controller and initialize listeners + try + { + addListener(); + mxView.set(mrBase.GetController(), UNO_QUERY); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::AnnotationManagerImpl::AnnotationManagerImpl()" ); + } + + try + { + Reference<XEventBroadcaster> xModel (mrBase.GetDocShell()->GetModel(), UNO_QUERY_THROW ); + Reference<XEventListener> xListener( this ); + xModel->addEventListener( xListener ); + } + catch( Exception& ) + { + } +} + +// WeakComponentImplHelper +void AnnotationManagerImpl::disposing (std::unique_lock<std::mutex>&) +{ + try + { + Reference<XEventBroadcaster> xModel (mrBase.GetDocShell()->GetModel(), UNO_QUERY_THROW ); + Reference<XEventListener> xListener( this ); + xModel->removeEventListener( xListener ); + } + catch( Exception& ) + { + } + + removeListener(); + DisposeTags(); + + if( mnUpdateTagsEvent ) + { + Application::RemoveUserEvent( mnUpdateTagsEvent ); + mnUpdateTagsEvent = nullptr; + } + + mxView.clear(); + mxCurrentPage.clear(); +} + +// XEventListener +void SAL_CALL AnnotationManagerImpl::notifyEvent( const css::document::EventObject& aEvent ) +{ + if( !(aEvent.EventName == "OnAnnotationInserted" || aEvent.EventName == "OnAnnotationRemoved" || aEvent.EventName == "OnAnnotationChanged") ) + return; + + // AnnotationInsertion and modification is not handled here because when + // a new annotation is inserted, it consists of OnAnnotationInserted + // followed by a chain of OnAnnotationChanged (called for setting each + // of the annotation attributes - author, text etc.). This is not what a + // LOK client wants. So only handle removal here as annotation removal + // consists of only one event - 'OnAnnotationRemoved' + if ( aEvent.EventName == "OnAnnotationRemoved" ) + { + Reference< XAnnotation > xAnnotation( aEvent.Source, uno::UNO_QUERY ); + if ( xAnnotation.is() ) + { + LOKCommentNotify(CommentNotificationType::Remove, &mrBase, xAnnotation); + } + } + + UpdateTags(); +} + +void SAL_CALL AnnotationManagerImpl::disposing( const css::lang::EventObject& /*Source*/ ) +{ +} + +Reference<XAnnotation> AnnotationManagerImpl::GetAnnotationById(sal_uInt32 nAnnotationId) +{ + SdPage* pPage = nullptr; + do + { + pPage = GetNextPage(pPage, true); + if( pPage && !pPage->getAnnotations().empty() ) + { + AnnotationVector aAnnotations(pPage->getAnnotations()); + auto iter = std::find_if(aAnnotations.begin(), aAnnotations.end(), + [nAnnotationId](const Reference<XAnnotation>& xAnnotation) { + return sd::getAnnotationId(xAnnotation) == nAnnotationId; + }); + if (iter != aAnnotations.end()) + return *iter; + } + } while( pPage ); + + Reference<XAnnotation> xAnnotationEmpty; + return xAnnotationEmpty; +} + +void AnnotationManagerImpl::ShowAnnotations( bool bShow ) +{ + // enforce show annotations if a new annotation is inserted + if( mbShowAnnotations != bShow ) + { + mbShowAnnotations = bShow; + + SdOptions* pOptions = SD_MOD()->GetSdOptions(mpDoc->GetDocumentType()); + if( pOptions ) + pOptions->SetShowComments( mbShowAnnotations ); + + UpdateTags(); + } +} + +void AnnotationManagerImpl::ExecuteAnnotation(SfxRequest const & rReq ) +{ + switch( rReq.GetSlot() ) + { + case SID_INSERT_POSTIT: + ExecuteInsertAnnotation( rReq ); + break; + case SID_DELETE_POSTIT: + case SID_DELETEALL_POSTIT: + case SID_DELETEALLBYAUTHOR_POSTIT: + ExecuteDeleteAnnotation( rReq ); + break; + case SID_EDIT_POSTIT: + ExecuteEditAnnotation( rReq ); + break; + case SID_PREVIOUS_POSTIT: + case SID_NEXT_POSTIT: + SelectNextAnnotation( rReq.GetSlot() == SID_NEXT_POSTIT ); + break; + case SID_REPLYTO_POSTIT: + ExecuteReplyToAnnotation( rReq ); + break; + case SID_TOGGLE_NOTES: + ShowAnnotations( !mbShowAnnotations ); + break; + } +} + +void AnnotationManagerImpl::ExecuteInsertAnnotation(SfxRequest const & rReq) +{ + if (!comphelper::LibreOfficeKit::isActive() || comphelper::LibreOfficeKit::isTiledAnnotations()) + ShowAnnotations(true); + + const SfxItemSet* pArgs = rReq.GetArgs(); + OUString sText; + if (pArgs) + { + if (const SfxStringItem* pPoolItem = pArgs->GetItemIfSet(SID_ATTR_POSTIT_TEXT)) + { + sText = pPoolItem->GetValue(); + } + } + + InsertAnnotation(sText); +} + +void AnnotationManagerImpl::ExecuteDeleteAnnotation(SfxRequest const & rReq) +{ + ShowAnnotations( true ); + + const SfxItemSet* pArgs = rReq.GetArgs(); + + switch( rReq.GetSlot() ) + { + case SID_DELETEALL_POSTIT: + DeleteAllAnnotations(); + break; + case SID_DELETEALLBYAUTHOR_POSTIT: + if( pArgs ) + { + const SfxPoolItem* pPoolItem = nullptr; + if( SfxItemState::SET == pArgs->GetItemState( SID_DELETEALLBYAUTHOR_POSTIT, true, &pPoolItem ) ) + { + OUString sAuthor( static_cast<const SfxStringItem*>( pPoolItem )->GetValue() ); + DeleteAnnotationsByAuthor( sAuthor ); + } + } + break; + case SID_DELETE_POSTIT: + { + Reference< XAnnotation > xAnnotation; + sal_uInt32 nId = 0; + if( pArgs ) + { + const SfxPoolItem* pPoolItem = nullptr; + if( SfxItemState::SET == pArgs->GetItemState( SID_DELETE_POSTIT, true, &pPoolItem ) ) + static_cast<const SfxUnoAnyItem*>(pPoolItem)->GetValue() >>= xAnnotation; + if( SfxItemState::SET == pArgs->GetItemState( SID_ATTR_POSTIT_ID, true, &pPoolItem ) ) + nId = static_cast<const SvxPostItIdItem*>(pPoolItem)->GetValue().toUInt32(); + } + + if (nId != 0) + xAnnotation = GetAnnotationById(nId); + else if( !xAnnotation.is() ) + GetSelectedAnnotation( xAnnotation ); + + DeleteAnnotation( xAnnotation ); + } + break; + } + + UpdateTags(); +} + +void AnnotationManagerImpl::ExecuteEditAnnotation(SfxRequest const & rReq) +{ + const SfxItemSet* pArgs = rReq.GetArgs(); + Reference< XAnnotation > xAnnotation; + OUString sText; + sal_Int32 nPositionX = -1; + sal_Int32 nPositionY = -1; + + if (!pArgs) + return; + + if (mpDoc->IsUndoEnabled()) + mpDoc->BegUndo(SdResId(STR_ANNOTATION_UNDO_EDIT)); + + if (const SvxPostItIdItem* pPoolItem = pArgs->GetItemIfSet(SID_ATTR_POSTIT_ID)) + { + sal_uInt32 nId = pPoolItem->GetValue().toUInt32(); + xAnnotation = GetAnnotationById(nId); + } + if (const SfxStringItem* pPoolItem = pArgs->GetItemIfSet(SID_ATTR_POSTIT_TEXT)) + sText = pPoolItem->GetValue(); + + if (const SfxInt32Item* pPoolItem = pArgs->GetItemIfSet(SID_ATTR_POSTIT_POSITION_X)) + nPositionX = pPoolItem->GetValue(); + + if (const SfxInt32Item* pPoolItem = pArgs->GetItemIfSet(SID_ATTR_POSTIT_POSITION_Y)) + nPositionY = pPoolItem->GetValue(); + + if (xAnnotation.is()) + { + CreateChangeUndo(xAnnotation); + + if (nPositionX >= 0 && nPositionY >= 0) + { + double fX = convertTwipToMm100(nPositionX) / 100.0; + double fY = convertTwipToMm100(nPositionY) / 100.0; + xAnnotation->setPosition({fX, fY}); + } + + if (!sText.isEmpty()) + { + // TODO: Not allow other authors to change others' comments ? + Reference<XText> xText(xAnnotation->getTextRange()); + xText->setString(sText); + } + + LOKCommentNotifyAll(CommentNotificationType::Modify, xAnnotation); + } + + if (mpDoc->IsUndoEnabled()) + mpDoc->EndUndo(); + + UpdateTags(true); +} + +void AnnotationManagerImpl::InsertAnnotation(const OUString& rText) +{ + SdPage* pPage = GetCurrentPage(); + if( !pPage ) + return; + + if( mpDoc->IsUndoEnabled() ) + mpDoc->BegUndo( SdResId( STR_ANNOTATION_UNDO_INSERT ) ); + + // find free space for new annotation + int y = 0, x = 0; + + AnnotationVector aAnnotations( pPage->getAnnotations() ); + if( !aAnnotations.empty() ) + { + const int page_width = pPage->GetSize().Width(); + const int width = 1000; + const int height = 800; + ::tools::Rectangle aTagRect; + + while( true ) + { + ::tools::Rectangle aNewRect( x, y, x + width - 1, y + height - 1 ); + bool bFree = true; + + for( const auto& rxAnnotation : aAnnotations ) + { + RealPoint2D aPoint( rxAnnotation->getPosition() ); + aTagRect.SetLeft( sal::static_int_cast< ::tools::Long >( aPoint.X * 100.0 ) ); + aTagRect.SetTop( sal::static_int_cast< ::tools::Long >( aPoint.Y * 100.0 ) ); + aTagRect.SetRight( aTagRect.Left() + width - 1 ); + aTagRect.SetBottom( aTagRect.Top() + height - 1 ); + + if( aNewRect.Overlaps( aTagRect ) ) + { + bFree = false; + break; + } + } + + if( !bFree ) + { + x += width; + if( x > page_width ) + { + x = 0; + y += height; + } + } + else + { + break; + } + } + } + + Reference< XAnnotation > xAnnotation; + pPage->createAnnotation( xAnnotation ); + + OUString sAuthor; + if (comphelper::LibreOfficeKit::isActive()) + sAuthor = mrBase.GetMainViewShell()->GetView()->GetAuthor(); + else + { + SvtUserOptions aUserOptions; + sAuthor = aUserOptions.GetFullName(); + xAnnotation->setInitials( aUserOptions.GetID() ); + } + + if (!rText.isEmpty()) + { + Reference<XText> xText(xAnnotation->getTextRange()); + xText->setString(rText); + } + + // set current author to new annotation + xAnnotation->setAuthor( sAuthor ); + // set current time to new annotation + xAnnotation->setDateTime( getCurrentDateTime() ); + + // set position + RealPoint2D aPos( static_cast<double>(x) / 100.0, static_cast<double>(y) / 100.0 ); + xAnnotation->setPosition( aPos ); + + if( mpDoc->IsUndoEnabled() ) + mpDoc->EndUndo(); + + // Tell our LOK clients about new comment added + LOKCommentNotifyAll(CommentNotificationType::Add, xAnnotation); + + UpdateTags(true); + SelectAnnotation( xAnnotation, true ); +} + +void AnnotationManagerImpl::ExecuteReplyToAnnotation( SfxRequest const & rReq ) +{ + Reference< XAnnotation > xAnnotation; + const SfxItemSet* pArgs = rReq.GetArgs(); + OUString sReplyText; + if( pArgs ) + { + const SfxPoolItem* pPoolItem = nullptr; + if( SfxItemState::SET == pArgs->GetItemState( SID_ATTR_POSTIT_ID, true, &pPoolItem ) ) + { + sal_uInt32 nReplyId = 0; // Id of the comment to reply to + nReplyId = static_cast<const SvxPostItIdItem*>(pPoolItem)->GetValue().toUInt32(); + xAnnotation = GetAnnotationById(nReplyId); + } + else if( SfxItemState::SET == pArgs->GetItemState( rReq.GetSlot(), true, &pPoolItem ) ) + static_cast<const SfxUnoAnyItem*>( pPoolItem )->GetValue() >>= xAnnotation; + + if( SfxItemState::SET == pArgs->GetItemState( SID_ATTR_POSTIT_TEXT, true, &pPoolItem ) ) + sReplyText = static_cast<const SvxPostItTextItem*>( pPoolItem )->GetValue(); + } + + TextApiObject* pTextApi = getTextApiObject( xAnnotation ); + if( !pTextApi ) + return; + + ::Outliner aOutliner( GetAnnotationPool(),OutlinerMode::TextObject ); + + SdDrawDocument::SetCalcFieldValueHdl( &aOutliner ); + aOutliner.SetUpdateLayout( true ); + + OUString aStr(SdResId(STR_ANNOTATION_REPLY)); + OUString sAuthor( xAnnotation->getAuthor() ); + if( sAuthor.isEmpty() ) + sAuthor = SdResId( STR_ANNOTATION_NOAUTHOR ); + + aStr = aStr.replaceFirst("%1", sAuthor) + + " (" + getAnnotationDateTimeString( xAnnotation ) + "): \""; + + OUString sQuote( pTextApi->GetText() ); + + if( sQuote.isEmpty() ) + sQuote = "..."; + aStr += sQuote + "\"\n"; + + for( sal_Int32 nIdx = 0; nIdx >= 0; ) + aOutliner.Insert( aStr.getToken( 0, '\n', nIdx ), EE_PARA_APPEND, -1 ); + + if( aOutliner.GetParagraphCount() > 1 ) + { + SfxItemSet aAnswerSet( aOutliner.GetEmptyItemSet() ); + aAnswerSet.Put(SvxPostureItem(ITALIC_NORMAL,EE_CHAR_ITALIC)); + + ESelection aSel; + aSel.nEndPara = aOutliner.GetParagraphCount()-2; + aSel.nEndPos = aOutliner.GetText( aOutliner.GetParagraph( aSel.nEndPara ) ).getLength(); + + aOutliner.QuickSetAttribs( aAnswerSet, aSel ); + } + + if (!sReplyText.isEmpty()) + aOutliner.Insert(sReplyText); + + std::optional< OutlinerParaObject > pOPO( aOutliner.CreateParaObject() ); + pTextApi->SetText(*pOPO); + + OUString sReplyAuthor; + if (comphelper::LibreOfficeKit::isActive()) + sReplyAuthor = mrBase.GetMainViewShell()->GetView()->GetAuthor(); + else + { + SvtUserOptions aUserOptions; + sReplyAuthor = aUserOptions.GetFullName(); + xAnnotation->setInitials( aUserOptions.GetID() ); + } + + xAnnotation->setAuthor( sReplyAuthor ); + // set current time to reply + xAnnotation->setDateTime( getCurrentDateTime() ); + + // Tell our LOK clients about this (comment modification) + LOKCommentNotifyAll(CommentNotificationType::Modify, xAnnotation); + + UpdateTags(true); + SelectAnnotation( xAnnotation, true ); +} + +void AnnotationManagerImpl::DeleteAnnotation( const Reference< XAnnotation >& xAnnotation ) +{ + SdPage* pPage = GetCurrentPage(); + + if( xAnnotation.is() && pPage ) + { + if( mpDoc->IsUndoEnabled() ) + mpDoc->BegUndo( SdResId( STR_ANNOTATION_UNDO_DELETE ) ); + + pPage->removeAnnotation( xAnnotation ); + + if( mpDoc->IsUndoEnabled() ) + mpDoc->EndUndo(); + + UpdateTags(); + } +} + +void AnnotationManagerImpl::DeleteAnnotationsByAuthor( std::u16string_view sAuthor ) +{ + if( mpDoc->IsUndoEnabled() ) + mpDoc->BegUndo( SdResId( STR_ANNOTATION_UNDO_DELETE ) ); + + SdPage* pPage = nullptr; + do + { + pPage = GetNextPage( pPage, true ); + + if( pPage && !pPage->getAnnotations().empty() ) + { + AnnotationVector aAnnotations( pPage->getAnnotations() ); + for( Reference< XAnnotation >& xAnnotation : aAnnotations ) + { + if( xAnnotation->getAuthor() == sAuthor ) + { + if( mxSelectedAnnotation == xAnnotation ) + mxSelectedAnnotation.clear(); + pPage->removeAnnotation( xAnnotation ); + } + } + } + } while( pPage ); + + if( mpDoc->IsUndoEnabled() ) + mpDoc->EndUndo(); +} + +void AnnotationManagerImpl::DeleteAllAnnotations() +{ + if( mpDoc->IsUndoEnabled() ) + mpDoc->BegUndo( SdResId( STR_ANNOTATION_UNDO_DELETE ) ); + + SdPage* pPage = nullptr; + do + { + pPage = GetNextPage( pPage, true ); + + if( pPage && !pPage->getAnnotations().empty() ) + { + + AnnotationVector aAnnotations( pPage->getAnnotations() ); + for( const auto& rxAnnotation : aAnnotations ) + { + pPage->removeAnnotation( rxAnnotation ); + } + } + } + while( pPage ); + + mxSelectedAnnotation.clear(); + + if( mpDoc->IsUndoEnabled() ) + mpDoc->EndUndo(); +} + +void AnnotationManagerImpl::GetAnnotationState(SfxItemSet& rSet) +{ + SdPage* pCurrentPage = GetCurrentPage(); + + const bool bReadOnly = mrBase.GetDocShell()->IsReadOnly(); + const bool bWrongPageKind = (pCurrentPage == nullptr) || (pCurrentPage->GetPageKind() != PageKind::Standard); + + const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion( GetODFSaneDefaultVersion() ); + + if (bReadOnly || bWrongPageKind || (nCurrentODFVersion <= SvtSaveOptions::ODFSVER_012)) + rSet.DisableItem( SID_INSERT_POSTIT ); + + rSet.Put(SfxBoolItem(SID_TOGGLE_NOTES, mbShowAnnotations)); + + Reference< XAnnotation > xAnnotation; + GetSelectedAnnotation( xAnnotation ); + + // Don't disable these slot in case of LOK, as postit doesn't need to + // selected before doing an operation on it in LOK + if( (!xAnnotation.is() && !comphelper::LibreOfficeKit::isActive()) || bReadOnly ) + { + rSet.DisableItem( SID_DELETE_POSTIT ); + rSet.DisableItem( SID_EDIT_POSTIT ); + } + + SdPage* pPage = nullptr; + + bool bHasAnnotations = false; + do + { + pPage = GetNextPage( pPage, true ); + + if( pPage && !pPage->getAnnotations().empty() ) + bHasAnnotations = true; + } + while( pPage && !bHasAnnotations ); + + if( !bHasAnnotations || bReadOnly ) + { + rSet.DisableItem( SID_DELETEALL_POSTIT ); + } + + if( bWrongPageKind || !bHasAnnotations ) + { + rSet.DisableItem( SID_PREVIOUS_POSTIT ); + rSet.DisableItem( SID_NEXT_POSTIT ); + } +} + +void AnnotationManagerImpl::SelectNextAnnotation(bool bForward) +{ + ShowAnnotations( true ); + + Reference< XAnnotation > xCurrent; + GetSelectedAnnotation( xCurrent ); + SdPage* pPage = GetCurrentPage(); + if( !pPage ) + return; + + AnnotationVector aAnnotations( pPage->getAnnotations() ); + + if( bForward ) + { + if( xCurrent.is() ) + { + auto iter = std::find(aAnnotations.begin(), aAnnotations.end(), xCurrent); + if (iter != aAnnotations.end()) + { + ++iter; + if( iter != aAnnotations.end() ) + { + SelectAnnotation( *iter ); + return; + } + } + } + else if( !aAnnotations.empty() ) + { + SelectAnnotation( *(aAnnotations.begin()) ); + return; + } + } + else + { + if( xCurrent.is() ) + { + auto iter = std::find(aAnnotations.begin(), aAnnotations.end(), xCurrent); + if (iter != aAnnotations.end() && iter != aAnnotations.begin()) + { + --iter; + SelectAnnotation( *iter ); + return; + } + } + else if( !aAnnotations.empty() ) + { + AnnotationVector::iterator iter( aAnnotations.end() ); + SelectAnnotation( *(--iter) ); + return; + } + } + + mxSelectedAnnotation.clear(); + do + { + do + { + pPage = GetNextPage( pPage, bForward ); + + if( pPage && !pPage->getAnnotations().empty() ) + { + // switch to next/previous slide with annotations + std::shared_ptr<DrawViewShell> pDrawViewShell(std::dynamic_pointer_cast<DrawViewShell>(mrBase.GetMainViewShell())); + if (pDrawViewShell != nullptr) + { + pDrawViewShell->ChangeEditMode(pPage->IsMasterPage() ? EditMode::MasterPage : EditMode::Page, false); + pDrawViewShell->SwitchPage((pPage->GetPageNum() - 1) >> 1); + + SfxDispatcher* pDispatcher = getDispatcher( mrBase ); + if( pDispatcher ) + pDispatcher->Execute( bForward ? SID_NEXT_POSTIT : SID_PREVIOUS_POSTIT ); + + return; + } + } + } + while( pPage ); + + // The question text depends on the search direction. + bool bImpress = mpDoc->GetDocumentType() == DocumentType::Impress; + TranslateId pStringId; + if(bForward) + pStringId = bImpress ? STR_ANNOTATION_WRAP_FORWARD : STR_ANNOTATION_WRAP_FORWARD_DRAW; + else + pStringId = bImpress ? STR_ANNOTATION_WRAP_BACKWARD : STR_ANNOTATION_WRAP_BACKWARD_DRAW; + + // Pop up question box that asks the user whether to wrap around. + // The dialog is made modal with respect to the whole application. + std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(nullptr, + VclMessageType::Question, VclButtonsType::YesNo, + SdResId(pStringId))); + xQueryBox->set_default_response(RET_YES); + if (xQueryBox->run() != RET_YES) + break; + } + while( true ); +} + +void AnnotationManagerImpl::onTagSelected( AnnotationTag const & rTag ) +{ + mxSelectedAnnotation = rTag.GetAnnotation(); + invalidateSlots(); +} + +void AnnotationManagerImpl::onTagDeselected( AnnotationTag const & rTag ) +{ + if( rTag.GetAnnotation() == mxSelectedAnnotation ) + { + mxSelectedAnnotation.clear(); + invalidateSlots(); + } +} + +void AnnotationManagerImpl::SelectAnnotation( const css::uno::Reference< css::office::XAnnotation >& xAnnotation, bool bEdit /* = sal_False */ ) +{ + mxSelectedAnnotation = xAnnotation; + + auto iter = std::find_if(maTagVector.begin(), maTagVector.end(), + [&xAnnotation](const rtl::Reference<AnnotationTag>& rxTag) { return rxTag->GetAnnotation() == xAnnotation; }); + if (iter != maTagVector.end()) + { + SmartTagReference xTag( *iter ); + mrBase.GetMainViewShell()->GetView()->getSmartTags().select( xTag ); + (*iter)->OpenPopup( bEdit ); + } +} + +void AnnotationManagerImpl::GetSelectedAnnotation( css::uno::Reference< css::office::XAnnotation >& xAnnotation ) +{ + xAnnotation = mxSelectedAnnotation; +} + +void AnnotationManagerImpl::invalidateSlots() +{ + SfxBindings* pBindings = getBindings( mrBase ); + if( pBindings ) + { + pBindings->Invalidate( SID_INSERT_POSTIT ); + pBindings->Invalidate( SID_DELETE_POSTIT ); + pBindings->Invalidate( SID_DELETEALL_POSTIT ); + pBindings->Invalidate( SID_PREVIOUS_POSTIT ); + pBindings->Invalidate( SID_NEXT_POSTIT ); + pBindings->Invalidate( SID_UNDO ); + pBindings->Invalidate( SID_REDO ); + } +} + +void AnnotationManagerImpl::onSelectionChanged() +{ + if( !(mxView.is() && mrBase.GetDrawView()) ) + return; + + try + { + Reference< XAnnotationAccess > xPage( mxView->getCurrentPage(), UNO_QUERY ); + + if( xPage != mxCurrentPage ) + { + mxCurrentPage = xPage; + + UpdateTags(true); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::AnnotationManagerImpl::onSelectionChanged()" ); + } +} + +void AnnotationManagerImpl::UpdateTags( bool bSynchron ) +{ + if( bSynchron ) + { + if( mnUpdateTagsEvent ) + Application::RemoveUserEvent( mnUpdateTagsEvent ); + + UpdateTagsHdl(nullptr); + } + else + { + if( !mnUpdateTagsEvent && mxView.is() ) + mnUpdateTagsEvent = Application::PostUserEvent( LINK( this, AnnotationManagerImpl, UpdateTagsHdl ) ); + } +} + +IMPL_LINK_NOARG(AnnotationManagerImpl, UpdateTagsHdl, void*, void) +{ + mnUpdateTagsEvent = nullptr; + DisposeTags(); + + if( mbShowAnnotations ) + CreateTags(); + + if( mrBase.GetDrawView() ) + static_cast< ::sd::View* >( mrBase.GetDrawView() )->updateHandles(); + + invalidateSlots(); +} + +void AnnotationManagerImpl::CreateTags() +{ + if( !(mxCurrentPage.is() && mpDoc) ) + return; + + auto xViewShell = mrBase.GetMainViewShell(); + if (!xViewShell) + return; + + try + { + int nIndex = 1; + maFont = Application::GetSettings().GetStyleSettings().GetAppFont(); + + rtl::Reference< AnnotationTag > xSelectedTag; + + Reference< XAnnotationEnumeration > xEnum( mxCurrentPage->createAnnotationEnumeration() ); + while( xEnum->hasMoreElements() ) + { + Reference< XAnnotation > xAnnotation( xEnum->nextElement() ); + Color aColor( GetColorLight( mpDoc->GetAnnotationAuthorIndex( xAnnotation->getAuthor() ) ) ); + rtl::Reference< AnnotationTag > xTag( new AnnotationTag( *this, *xViewShell->GetView(), xAnnotation, aColor, nIndex++, maFont ) ); + maTagVector.push_back(xTag); + + if( xAnnotation == mxSelectedAnnotation ) + { + xSelectedTag = xTag; + } + } + + if( xSelectedTag.is() ) + { + SmartTagReference xTag( xSelectedTag ); + mrBase.GetMainViewShell()->GetView()->getSmartTags().select( xTag ); + } + else + { + // no tag, no selection! + mxSelectedAnnotation.clear(); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::AnnotationManagerImpl::onSelectionChanged()" ); + } +} + +void AnnotationManagerImpl::DisposeTags() +{ + for (auto& rxTag : maTagVector) + { + rxTag->Dispose(); + } + + maTagVector.clear(); +} + +void AnnotationManagerImpl::addListener() +{ + Link<tools::EventMultiplexerEvent&,void> aLink( LINK(this,AnnotationManagerImpl,EventMultiplexerListener) ); + mrBase.GetEventMultiplexer()->AddEventListener(aLink); +} + +void AnnotationManagerImpl::removeListener() +{ + Link<tools::EventMultiplexerEvent&,void> aLink( LINK(this,AnnotationManagerImpl,EventMultiplexerListener) ); + mrBase.GetEventMultiplexer()->RemoveEventListener( aLink ); +} + +IMPL_LINK(AnnotationManagerImpl,EventMultiplexerListener, + tools::EventMultiplexerEvent&, rEvent, void) +{ + switch (rEvent.meEventId) + { + case EventMultiplexerEventId::CurrentPageChanged: + case EventMultiplexerEventId::EditViewSelection: + onSelectionChanged(); + break; + + case EventMultiplexerEventId::MainViewRemoved: + mxView.clear(); + onSelectionChanged(); + break; + + case EventMultiplexerEventId::MainViewAdded: + mxView.set( mrBase.GetController(), UNO_QUERY ); + onSelectionChanged(); + break; + + default: break; + } +} + +void AnnotationManagerImpl::ExecuteAnnotationTagContextMenu(const Reference<XAnnotation>& xAnnotation, weld::Widget* pParent, const ::tools::Rectangle& rContextRect) +{ + SfxDispatcher* pDispatcher( getDispatcher( mrBase ) ); + if( !pDispatcher ) + return; + + const bool bReadOnly = mrBase.GetDocShell()->IsReadOnly(); + + if (bReadOnly) + return; + + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pParent, "modules/simpress/ui/annotationtagmenu.ui")); + std::unique_ptr<weld::Menu> xMenu(xBuilder->weld_menu("menu")); + + SvtUserOptions aUserOptions; + OUString sCurrentAuthor( aUserOptions.GetFullName() ); + OUString sAuthor( xAnnotation->getAuthor() ); + + OUString aStr(xMenu->get_label(".uno:DeleteAllAnnotationByAuthor")); + OUString aReplace( sAuthor ); + if( aReplace.isEmpty() ) + aReplace = SdResId( STR_ANNOTATION_NOAUTHOR ); + aStr = aStr.replaceFirst("%1", aReplace); + xMenu->set_label(".uno:DeleteAllAnnotationByAuthor", aStr); + + bool bShowReply = sAuthor != sCurrentAuthor; + xMenu->set_visible(".uno:ReplyToAnnotation", bShowReply); + xMenu->set_visible("separator", bShowReply); + xMenu->set_visible(".uno:DeleteAnnotation", xAnnotation.is()); + + auto sId = xMenu->popup_at_rect(pParent, rContextRect); + + if (sId == ".uno:ReplyToAnnotation") + { + const SfxUnoAnyItem aItem( SID_REPLYTO_POSTIT, Any( xAnnotation ) ); + pDispatcher->ExecuteList(SID_REPLYTO_POSTIT, + SfxCallMode::ASYNCHRON, { &aItem }); + } + else if (sId == ".uno:DeleteAnnotation") + { + const SfxUnoAnyItem aItem( SID_DELETE_POSTIT, Any( xAnnotation ) ); + pDispatcher->ExecuteList(SID_DELETE_POSTIT, SfxCallMode::ASYNCHRON, + { &aItem }); + } + else if (sId == ".uno:DeleteAllAnnotationByAuthor") + { + const SfxStringItem aItem( SID_DELETEALLBYAUTHOR_POSTIT, sAuthor ); + pDispatcher->ExecuteList( SID_DELETEALLBYAUTHOR_POSTIT, + SfxCallMode::ASYNCHRON, { &aItem }); + } + else if (sId == ".uno:DeleteAllAnnotation") + pDispatcher->Execute( SID_DELETEALL_POSTIT ); +} + +Color AnnotationManagerImpl::GetColor(sal_uInt16 aAuthorIndex) +{ + if (!Application::GetSettings().GetStyleSettings().GetHighContrastMode()) + { + static const Color aArrayNormal[] = { + COL_AUTHOR1_NORMAL, COL_AUTHOR2_NORMAL, COL_AUTHOR3_NORMAL, + COL_AUTHOR4_NORMAL, COL_AUTHOR5_NORMAL, COL_AUTHOR6_NORMAL, + COL_AUTHOR7_NORMAL, COL_AUTHOR8_NORMAL, COL_AUTHOR9_NORMAL }; + + return aArrayNormal[ aAuthorIndex % SAL_N_ELEMENTS( aArrayNormal ) ]; + } + + return COL_WHITE; +} + +Color AnnotationManagerImpl::GetColorLight(sal_uInt16 aAuthorIndex) +{ + if (!Application::GetSettings().GetStyleSettings().GetHighContrastMode()) + { + static const Color aArrayLight[] = { + COL_AUTHOR1_LIGHT, COL_AUTHOR2_LIGHT, COL_AUTHOR3_LIGHT, + COL_AUTHOR4_LIGHT, COL_AUTHOR5_LIGHT, COL_AUTHOR6_LIGHT, + COL_AUTHOR7_LIGHT, COL_AUTHOR8_LIGHT, COL_AUTHOR9_LIGHT }; + + return aArrayLight[ aAuthorIndex % SAL_N_ELEMENTS( aArrayLight ) ]; + } + + return COL_WHITE; +} + +Color AnnotationManagerImpl::GetColorDark(sal_uInt16 aAuthorIndex) +{ + if (!Application::GetSettings().GetStyleSettings().GetHighContrastMode()) + { + static const Color aArrayAnkor[] = { + COL_AUTHOR1_DARK, COL_AUTHOR2_DARK, COL_AUTHOR3_DARK, + COL_AUTHOR4_DARK, COL_AUTHOR5_DARK, COL_AUTHOR6_DARK, + COL_AUTHOR7_DARK, COL_AUTHOR8_DARK, COL_AUTHOR9_DARK }; + + return aArrayAnkor[ aAuthorIndex % SAL_N_ELEMENTS( aArrayAnkor ) ]; + } + + return COL_WHITE; +} + +SdPage* AnnotationManagerImpl::GetNextPage( SdPage const * pPage, bool bForward ) +{ + if( pPage == nullptr ) + { + if (bForward) + return mpDoc->GetSdPage(0, PageKind::Standard ); // first page + else + return mpDoc->GetMasterSdPage( mpDoc->GetMasterSdPageCount(PageKind::Standard) - 1, PageKind::Standard ); // last page + } + + sal_uInt16 nPageNum = (pPage->GetPageNum() - 1) >> 1; + + // first all non master pages + if( !pPage->IsMasterPage() ) + { + if( bForward ) + { + if( nPageNum >= mpDoc->GetSdPageCount(PageKind::Standard)-1 ) + { + // we reached end of draw pages, start with master pages (skip handout master for draw) + return mpDoc->GetMasterSdPage( (mpDoc->GetDocumentType() == DocumentType::Impress) ? 0 : 1, PageKind::Standard ); + } + nPageNum++; + } + else + { + if( nPageNum == 0 ) + return nullptr; // we are already on the first draw page, finished + + nPageNum--; + } + return mpDoc->GetSdPage(nPageNum, PageKind::Standard); + } + else + { + if( bForward ) + { + if( nPageNum >= mpDoc->GetMasterSdPageCount(PageKind::Standard)-1 ) + { + return nullptr; // we reached the end, there is nothing more to see here + } + nPageNum++; + } + else + { + if( nPageNum == (mpDoc->GetDocumentType() == DocumentType::Impress ? 0 : 1) ) + { + // we reached beginning of master pages, start with end if pages + return mpDoc->GetSdPage( mpDoc->GetSdPageCount(PageKind::Standard)-1, PageKind::Standard ); + } + + nPageNum--; + } + return mpDoc->GetMasterSdPage(nPageNum,PageKind::Standard); + } +} + +SdPage* AnnotationManagerImpl::GetCurrentPage() +{ + if (mrBase.GetMainViewShell()) + return mrBase.GetMainViewShell()->getCurrentPage(); + return nullptr; +} + +AnnotationManager::AnnotationManager( ViewShellBase& rViewShellBase ) +: mxImpl( new AnnotationManagerImpl( rViewShellBase ) ) +{ + mxImpl->init(); +} + +AnnotationManager::~AnnotationManager() +{ + mxImpl->dispose(); +} + +void AnnotationManager::ExecuteAnnotation(SfxRequest const & rRequest) +{ + mxImpl->ExecuteAnnotation( rRequest ); +} + +void AnnotationManager::GetAnnotationState(SfxItemSet& rItemSet) +{ + mxImpl->GetAnnotationState(rItemSet); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/annotations/annotationmanagerimpl.hxx b/sd/source/ui/annotations/annotationmanagerimpl.hxx new file mode 100644 index 000000000..c5871d90c --- /dev/null +++ b/sd/source/ui/annotations/annotationmanagerimpl.hxx @@ -0,0 +1,141 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/document/XEventListener.hpp> + +#include <rtl/ustring.hxx> + +#include <comphelper/compbase.hxx> + +#include "annotationtag.hxx" + +namespace com::sun::star::drawing { class XDrawView; } +namespace com::sun::star::office { class XAnnotationAccess; } +namespace com::sun::star::office { class XAnnotation; } + +class SfxRequest; +class SdPage; +class SdDrawDocument; +struct ImplSVEvent; + +namespace sd +{ + +class ViewShellBase; + +namespace tools { +class EventMultiplexerEvent; +} + +typedef comphelper::WeakComponentImplHelper < + css::document::XEventListener + > AnnotationManagerImplBase; + +class AnnotationManagerImpl : public AnnotationManagerImplBase +{ +public: + explicit AnnotationManagerImpl( ViewShellBase& rViewShellBase ); + + void init(); + + // WeakComponentImplHelper + virtual void disposing (std::unique_lock<std::mutex>&) override; + + // XEventListener + virtual void SAL_CALL notifyEvent( const css::document::EventObject& Event ) override; + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + void ExecuteAnnotation (SfxRequest const & rRequest); + void GetAnnotationState (SfxItemSet& rItemSet); + + void ExecuteInsertAnnotation(SfxRequest const & rReq); + void ExecuteDeleteAnnotation(SfxRequest const & rReq); + void ExecuteEditAnnotation(SfxRequest const & rReq); + void ExecuteReplyToAnnotation(SfxRequest const & rReq); + + void SelectNextAnnotation(bool bForward); + + void SelectAnnotation( const css::uno::Reference< css::office::XAnnotation >& xAnnotation, bool bEdit = false ); + void GetSelectedAnnotation( css::uno::Reference< css::office::XAnnotation >& xAnnotation ); + + void InsertAnnotation(const OUString& rText); + void DeleteAnnotation( const css::uno::Reference< css::office::XAnnotation >& xAnnotation ); + void DeleteAnnotationsByAuthor( std::u16string_view sAuthor ); + void DeleteAllAnnotations(); + + void ExecuteAnnotationTagContextMenu(const css::uno::Reference<css::office::XAnnotation>& xAnnotation, weld::Widget* pParent, const ::tools::Rectangle& rContextRect); + + static Color GetColorDark(sal_uInt16 aAuthorIndex); + static Color GetColorLight(sal_uInt16 aAuthorIndex); + static Color GetColor(sal_uInt16 aAuthorIndex); + + // callbacks + void onTagSelected( AnnotationTag const & rTag ); + void onTagDeselected( AnnotationTag const & rTag ); + + void onSelectionChanged(); + + void addListener(); + void removeListener(); + + void invalidateSlots(); + + DECL_LINK(EventMultiplexerListener, tools::EventMultiplexerEvent&, void); + DECL_LINK(UpdateTagsHdl, void *, void); + + void UpdateTags(bool bSynchron = false); + void CreateTags(); + void DisposeTags(); + + SdPage* GetNextPage( SdPage const * pPage, bool bForward ); + + SdPage* GetCurrentPage(); + + SdDrawDocument* GetDoc() { return mpDoc; } + + void ShowAnnotations(bool bShow); + +private: + ViewShellBase& mrBase; + SdDrawDocument* mpDoc; + + std::vector< rtl::Reference< AnnotationTag > > maTagVector; + + css::uno::Reference< css::drawing::XDrawView > mxView; + css::uno::Reference< css::office::XAnnotationAccess > mxCurrentPage; + css::uno::Reference< css::office::XAnnotation > mxSelectedAnnotation; + + bool mbShowAnnotations; + ImplSVEvent * mnUpdateTagsEvent; + vcl::Font maFont; + + css::uno::Reference<css::office::XAnnotation> GetAnnotationById(sal_uInt32 nAnnotationId); +}; + +OUString getAnnotationDateTimeString( const css::uno::Reference< css::office::XAnnotation >& xAnnotation ); + +SfxItemPool* GetAnnotationPool(); + +css::util::DateTime getCurrentDateTime(); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/annotations/annotationtag.cxx b/sd/source/ui/annotations/annotationtag.cxx new file mode 100644 index 000000000..cfd632dcc --- /dev/null +++ b/sd/source/ui/annotations/annotationtag.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 <com/sun/star/geometry/RealPoint2D.hpp> +#include <com/sun/star/office/XAnnotation.hpp> + +#include <rtl/ustrbuf.hxx> + +#include <vcl/commandevent.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <vcl/weldutils.hxx> + +#include <svx/sdr/overlay/overlayanimatedbitmapex.hxx> +#include <svx/sdr/overlay/overlaybitmapex.hxx> +#include <svx/sdr/overlay/overlaypolypolygon.hxx> +#include <svx/svdpagv.hxx> +#include <svx/sdrpagewindow.hxx> +#include <svx/sdrpaintwindow.hxx> +#include <svx/svddrgmt.hxx> +#include <tools/debug.hxx> + +#include <View.hxx> +#include <sdresid.hxx> +#include <strings.hrc> +#include "annotationmanagerimpl.hxx" +#include "annotationwindow.hxx" +#include "annotationtag.hxx" +#include <Annotation.hxx> +#include <ViewShell.hxx> +#include <Window.hxx> +#include <drawdoc.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::office; +using namespace ::com::sun::star::geometry; + +namespace sd +{ + +const sal_uInt32 SMART_TAG_HDL_NUM = SAL_MAX_UINT32; +const int DRGPIX = 2; // Drag MinMove in Pixel + +static OUString getInitials( const OUString& rName ) +{ + OUStringBuffer sInitials; + + const sal_Unicode * pStr = rName.getStr(); + sal_Int32 nLength = rName.getLength(); + + while( nLength ) + { + // skip whitespace + while( nLength && (*pStr <= ' ') ) + { + nLength--; pStr++; + } + + // take letter + if( nLength ) + { + sInitials.append(*pStr); + nLength--; pStr++; + } + + // skip letters until whitespace + while( nLength && (*pStr > ' ') ) + { + nLength--; pStr++; + } + } + + return sInitials.makeStringAndClear(); +} + +namespace { + +class AnnotationDragMove : public SdrDragMove +{ +public: + AnnotationDragMove(SdrDragView& rNewView, const rtl::Reference <AnnotationTag >& xTag); + virtual bool BeginSdrDrag() override; + virtual bool EndSdrDrag(bool bCopy) override; + virtual void MoveSdrDrag(const Point& rNoSnapPnt) override; + virtual void CancelSdrDrag() override; + +private: + rtl::Reference <AnnotationTag > mxTag; + Point maOrigin; +}; + +} + +AnnotationDragMove::AnnotationDragMove(SdrDragView& rNewView, const rtl::Reference <AnnotationTag >& xTag) +: SdrDragMove(rNewView) +, mxTag( xTag ) +{ +} + +bool AnnotationDragMove::BeginSdrDrag() +{ + DragStat().SetRef1(GetDragHdl()->GetPos()); + DragStat().SetShown(!DragStat().IsShown()); + + maOrigin = GetDragHdl()->GetPos(); + DragStat().SetActionRect(::tools::Rectangle(maOrigin,maOrigin)); + + return true; +} + +void AnnotationDragMove::MoveSdrDrag(const Point& rNoSnapPnt) +{ + Point aPnt(rNoSnapPnt); + + if (DragStat().CheckMinMoved(rNoSnapPnt)) + { + if (aPnt!=DragStat().GetNow()) + { + Hide(); + DragStat().NextMove(aPnt); + GetDragHdl()->SetPos( maOrigin + Point( DragStat().GetDX(), DragStat().GetDY() ) ); + Show(); + DragStat().SetActionRect(::tools::Rectangle(aPnt,aPnt)); + } + } +} + +bool AnnotationDragMove::EndSdrDrag(bool /*bCopy*/) +{ + Hide(); + if( mxTag.is() ) + mxTag->Move( DragStat().GetDX(), DragStat().GetDY() ); + return true; +} + +void AnnotationDragMove::CancelSdrDrag() +{ + Hide(); +} + +namespace { + +class AnnotationHdl : public SmartHdl +{ +public: + AnnotationHdl( const SmartTagReference& xTag, const Reference< XAnnotation >& xAnnotation, const Point& rPnt ); + + virtual void CreateB2dIAObject() override; + virtual bool IsFocusHdl() const override; + +private: + Reference< XAnnotation > mxAnnotation; + rtl::Reference< AnnotationTag > mxTag; +}; + +} + +AnnotationHdl::AnnotationHdl( const SmartTagReference& xTag, const Reference< XAnnotation >& xAnnotation, const Point& rPnt ) +: SmartHdl( xTag, rPnt, SdrHdlKind::SmartTag ) +, mxAnnotation( xAnnotation ) +, mxTag( dynamic_cast< AnnotationTag* >( xTag.get() ) ) +{ +} + +void AnnotationHdl::CreateB2dIAObject() +{ + // first throw away old one + GetRidOfIAObject(); + + if (!mxAnnotation.is()) + return; + + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + + const Point aTagPos( GetPos() ); + basegfx::B2DPoint aPosition( aTagPos.X(), aTagPos.Y() ); + + const bool bFocused = IsFocusHdl() && pHdlList && (pHdlList->GetFocusHdl() == this); + + BitmapEx aBitmapEx( mxTag->CreateAnnotationBitmap(mxTag->isSelected()) ); + BitmapEx aBitmapEx2; + if( bFocused ) + aBitmapEx2 = mxTag->CreateAnnotationBitmap(!mxTag->isSelected() ); + + if(!pHdlList) + return; + + SdrMarkView* pView = pHdlList->GetView(); + + if(!pView || pView->areMarkHandlesHidden()) + return; + + SdrPageView* pPageView = pView->GetSdrPageView(); + + if(!pPageView) + return; + + for(sal_uInt32 b = 0; b < pPageView->PageWindowCount(); b++) + { + // const SdrPageViewWinRec& rPageViewWinRec = rPageViewWinList[b]; + const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b); + + SdrPaintWindow& rPaintWindow = rPageWindow.GetPaintWindow(); + const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager(); + if(rPaintWindow.OutputToWindow() && xManager.is() ) + { + std::unique_ptr<sdr::overlay::OverlayObject> pOverlayObject; + + auto* pAnnotation = dynamic_cast<sd::Annotation*>(mxAnnotation.get()); + + if (pAnnotation && pAnnotation->hasCustomAnnotationMarker()) + { + CustomAnnotationMarker& rCustomAnnotationMarker = pAnnotation->getCustomAnnotationMarker(); + + auto& rPolygons = rCustomAnnotationMarker.maPolygons; + if (!rPolygons.empty()) + { + basegfx::B2DPolyPolygon aPolyPolygon; + for (auto const & rPolygon : rPolygons) + aPolyPolygon.append(rPolygon); + + pOverlayObject.reset(new sdr::overlay::OverlayPolyPolygon( + aPolyPolygon, + rCustomAnnotationMarker.maLineColor, + rCustomAnnotationMarker.mnLineWidth, + rCustomAnnotationMarker.maFillColor)); + } + } + else + { + // animate focused handles + if(bFocused) + { + const sal_uInt64 nBlinkTime = rStyleSettings.GetCursorBlinkTime(); + + pOverlayObject.reset(new sdr::overlay::OverlayAnimatedBitmapEx(aPosition, aBitmapEx, aBitmapEx2, nBlinkTime, 0, 0, 0, 0 )); + } + else + { + pOverlayObject.reset(new sdr::overlay::OverlayBitmapEx( aPosition, aBitmapEx, 0, 0 )); + } + } + + // OVERLAYMANAGER + insertNewlyCreatedOverlayObjectForSdrHdl( + std::move(pOverlayObject), + rPageWindow.GetObjectContact(), + *xManager); + } + } +} + +bool AnnotationHdl::IsFocusHdl() const +{ + return true; +} + +AnnotationTag::AnnotationTag( AnnotationManagerImpl& rManager, ::sd::View& rView, const Reference< XAnnotation >& xAnnotation, Color const & rColor, int nIndex, const vcl::Font& rFont ) +: SmartTag( rView ) +, mrManager( rManager ) +, mxAnnotation( xAnnotation ) +, maColor( rColor ) +, mnIndex( nIndex ) +, mrFont( rFont ) +, mpListenWindow( nullptr ) +{ +} + +AnnotationTag::~AnnotationTag() +{ + DBG_ASSERT( !mxAnnotation.is(), "sd::AnnotationTag::~AnnotationTag(), dispose me first!" ); + Dispose(); +} + +/** returns true if the AnnotationTag handled the event. */ +bool AnnotationTag::MouseButtonDown( const MouseEvent& rMEvt, SmartHdl& /*rHdl*/ ) +{ + if( !mxAnnotation.is() ) + return false; + + bool bRet = false; + if( !isSelected() ) + { + SmartTagReference xTag( this ); + mrView.getSmartTags().select( xTag ); + bRet = true; + } + + if( rMEvt.IsLeft() && !rMEvt.IsRight() ) + { + vcl::Window* pWindow = mrView.GetViewShell()->GetActiveWindow(); + if( pWindow ) + { + maMouseDownPos = pWindow->PixelToLogic( rMEvt.GetPosPixel() ); + + if( mpListenWindow ) + mpListenWindow->RemoveEventListener( LINK(this, AnnotationTag, WindowEventHandler)); + + mpListenWindow = pWindow; + mpListenWindow->AddEventListener( LINK(this, AnnotationTag, WindowEventHandler)); + } + + bRet = true; + } + + return bRet; +} + +/** returns true if the SmartTag consumes this event. */ +bool AnnotationTag::KeyInput( const KeyEvent& rKEvt ) +{ + if( !mxAnnotation.is() ) + return false; + + sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode(); + switch( nCode ) + { + case KEY_DELETE: + mrManager.DeleteAnnotation( mxAnnotation ); + return true; + + case KEY_DOWN: + case KEY_UP: + case KEY_LEFT: + case KEY_RIGHT: + return OnMove( rKEvt ); + + case KEY_ESCAPE: + { + SmartTagReference xThis( this ); + mrView.getSmartTags().deselect(); + return true; + } + + case KEY_TAB: + mrManager.SelectNextAnnotation(!rKEvt.GetKeyCode().IsShift()); + return true; + + case KEY_RETURN: + case KEY_SPACE: + OpenPopup( true ); + return true; + + default: + return false; + } +} + +/** returns true if the SmartTag consumes this event. */ +bool AnnotationTag::Command( const CommandEvent& rCEvt ) +{ + if (rCEvt.GetCommand() != CommandEventId::ContextMenu) + return false; + if (vcl::Window* pWindow = mrView.GetViewShell()->GetActiveWindow()) + { + ::tools::Rectangle aContextRect(rCEvt.GetMousePosPixel(),Size(1,1)); + weld::Window* pParent = weld::GetPopupParent(*pWindow, aContextRect); + mrManager.ExecuteAnnotationTagContextMenu(mxAnnotation, pParent, aContextRect); + return true; + } + return false; +} + +void AnnotationTag::Move( int nDX, int nDY ) +{ + if( !mxAnnotation.is() ) + return; + + if( mrManager.GetDoc()->IsUndoEnabled() ) + mrManager.GetDoc()->BegUndo( SdResId( STR_ANNOTATION_UNDO_MOVE ) ); + + RealPoint2D aPosition( mxAnnotation->getPosition() ); + aPosition.X += static_cast<double>(nDX) / 100.0; + aPosition.Y += static_cast<double>(nDY) / 100.0; + mxAnnotation->setPosition( aPosition ); + + if( mrManager.GetDoc()->IsUndoEnabled() ) + mrManager.GetDoc()->EndUndo(); + + mrView.updateHandles(); +} + +bool AnnotationTag::OnMove( const KeyEvent& rKEvt ) +{ + ::tools::Long nX = 0; + ::tools::Long nY = 0; + + switch( rKEvt.GetKeyCode().GetCode() ) + { + case KEY_UP: nY = -1; break; + case KEY_DOWN: nY = 1; break; + case KEY_LEFT: nX = -1; break; + case KEY_RIGHT: nX = 1; break; + default: break; + } + + if(rKEvt.GetKeyCode().IsMod2()) + { + OutputDevice* pOut = mrView.GetViewShell()->GetActiveWindow()->GetOutDev(); + Size aLogicSizeOnePixel = pOut ? pOut->PixelToLogic(Size(1,1)) : Size(100, 100); + nX *= aLogicSizeOnePixel.Width(); + nY *= aLogicSizeOnePixel.Height(); + } + else + { + // old, fixed move distance + nX *= 100; + nY *= 100; + } + + if( nX || nY ) + { + // move the annotation + Move( nX, nY ); + } + + return true; +} + +void AnnotationTag::CheckPossibilities() +{ +} + +sal_Int32 AnnotationTag::GetMarkablePointCount() const +{ + return 0; +} + +sal_Int32 AnnotationTag::GetMarkedPointCount() const +{ + return 0; +} + +bool AnnotationTag::MarkPoint(SdrHdl& /*rHdl*/, bool /*bUnmark*/ ) +{ + return false; +} + +bool AnnotationTag::MarkPoints(const ::tools::Rectangle* /*pRect*/, bool /*bUnmark*/ ) +{ + return false; +} + +bool AnnotationTag::getContext( SdrViewContext& /*rContext*/ ) +{ + return false; +} + +void AnnotationTag::addCustomHandles( SdrHdlList& rHandlerList ) +{ + if( !mxAnnotation.is() ) + return; + + SmartTagReference xThis( this ); + std::unique_ptr<AnnotationHdl> pHdl(new AnnotationHdl( xThis, mxAnnotation, Point() )); + pHdl->SetObjHdlNum( SMART_TAG_HDL_NUM ); + pHdl->SetPageView( mrView.GetSdrPageView() ); + + RealPoint2D aPosition( mxAnnotation->getPosition() ); + Point aBasePos( static_cast<::tools::Long>(aPosition.X * 100.0), static_cast<::tools::Long>(aPosition.Y * 100.0) ); + pHdl->SetPos( aBasePos ); + + rHandlerList.AddHdl( std::move(pHdl) ); +} + +void AnnotationTag::disposing() +{ + if( mpListenWindow ) + { + mpListenWindow->RemoveEventListener( LINK(this, AnnotationTag, WindowEventHandler)); + } + + mxAnnotation.clear(); + ClosePopup(); + SmartTag::disposing(); +} + +void AnnotationTag::select() +{ + SmartTag::select(); + + mrManager.onTagSelected( *this ); + + vcl::Window* pWindow = mrView.GetViewShell()->GetActiveWindow(); + if( pWindow ) + { + RealPoint2D aPosition( mxAnnotation->getPosition() ); + Point aPos( static_cast<::tools::Long>(aPosition.X * 100.0), static_cast<::tools::Long>(aPosition.Y * 100.0) ); + + ::tools::Rectangle aVisRect( aPos, pWindow->PixelToLogic(maSize) ); + mrView.MakeVisible(aVisRect, *pWindow); + } +} + +void AnnotationTag::deselect() +{ + SmartTag::deselect(); + + ClosePopup(); + + mrManager.onTagDeselected( *this ); +} + +BitmapEx AnnotationTag::CreateAnnotationBitmap( bool bSelected ) +{ + ScopedVclPtrInstance< VirtualDevice > pVDev; + + OUString sInitials(mxAnnotation->getInitials()); + if (sInitials.isEmpty()) + sInitials = getInitials(mxAnnotation->getAuthor()); + + OUString sAuthor(sInitials + " " + OUString::number(mnIndex)); + + pVDev->SetFont( mrFont ); + + const int BORDER_X = 4; // pixels + const int BORDER_Y = 4; // pixels + + maSize = Size( pVDev->GetTextWidth( sAuthor ) + 2*BORDER_X, pVDev->GetTextHeight() + 2*BORDER_Y ); + pVDev->SetOutputSizePixel( maSize, false ); + + Color aBorderColor( maColor ); + + if( bSelected ) + { + aBorderColor.Invert(); + } + else + { + if( maColor.IsDark() ) + { + aBorderColor.IncreaseLuminance( 32 ); + } + else + { + aBorderColor.DecreaseLuminance( 32 ); + } + } + + Point aPos; + ::tools::Rectangle aBorderRect( aPos, maSize ); + pVDev->SetLineColor(aBorderColor); + pVDev->SetFillColor(maColor); + pVDev->DrawRect( aBorderRect ); + + pVDev->SetTextColor( maColor.IsDark() ? COL_WHITE : COL_BLACK ); + pVDev->DrawText( Point( BORDER_X, BORDER_Y ), sAuthor ); + + return pVDev->GetBitmapEx( aPos, maSize ); +} + +void AnnotationTag::OpenPopup( bool bEdit ) +{ + if( !mxAnnotation.is() ) + return; + + if( !mpAnnotationWindow ) + { + OutputDevice* pOut = getView().GetFirstOutputDevice(); + vcl::Window* pWindow = pOut ? pOut->GetOwnerWindow() : nullptr; + if( pWindow ) + { + RealPoint2D aPosition( mxAnnotation->getPosition() ); + Point aPos(pWindow->LogicToPixel( Point( static_cast<::tools::Long>(aPosition.X * 100.0), static_cast<::tools::Long>(aPosition.Y * 100.0) ) ) ); + + aPos.AdjustX(4 ); // magic! + aPos.AdjustY(1 ); + + ::tools::Rectangle aRect( aPos, maSize ); + + weld::Window* pParent = weld::GetPopupParent(*pWindow, aRect); + mpAnnotationWindow.reset(new AnnotationWindow(pParent, aRect, mrView.GetDocSh(), mxAnnotation)); + mpAnnotationWindow->connect_closed(LINK(this, AnnotationTag, PopupModeEndHdl)); + } + } + + if (bEdit && mpAnnotationWindow) + mpAnnotationWindow->StartEdit(); +} + +IMPL_LINK_NOARG(AnnotationTag, PopupModeEndHdl, weld::Popover&, void) +{ + ClosePopup(); +} + +void AnnotationTag::ClosePopup() +{ + if (mpAnnotationWindow) + { + mpAnnotationWindow->SaveToDocument(); + mpAnnotationWindow.reset(); + } +} + +IMPL_LINK(AnnotationTag, WindowEventHandler, VclWindowEvent&, rEvent, void) +{ + vcl::Window* pWindow = rEvent.GetWindow(); + + if( !pWindow ) + return; + + if( pWindow != mpListenWindow ) + return; + + switch( rEvent.GetId() ) + { + case VclEventId::WindowMouseButtonUp: + { + // if we stop pressing the button without a mouse move we open the popup + mpListenWindow->RemoveEventListener( LINK(this, AnnotationTag, WindowEventHandler)); + mpListenWindow = nullptr; + if( !mpAnnotationWindow ) + OpenPopup(false); + } + break; + case VclEventId::WindowMouseMove: + { + // if we move the mouse after a button down we want to start dragging + mpListenWindow->RemoveEventListener( LINK(this, AnnotationTag, WindowEventHandler)); + mpListenWindow = nullptr; + + SdrHdl* pHdl = mrView.PickHandle(maMouseDownPos); + if( pHdl ) + { + mrView.BrkAction(); + const sal_uInt16 nDrgLog = static_cast<sal_uInt16>(pWindow->PixelToLogic(Size(DRGPIX,0)).Width()); + + rtl::Reference< AnnotationTag > xTag( this ); + + SdrDragMethod* pDragMethod = new AnnotationDragMove( mrView, xTag ); + mrView.BegDragObj(maMouseDownPos, nullptr, pHdl, nDrgLog, pDragMethod ); + } + } + break; + case VclEventId::ObjectDying: + mpListenWindow = nullptr; + break; + default: break; + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/annotations/annotationtag.hxx b/sd/source/ui/annotations/annotationtag.hxx new file mode 100644 index 000000000..23dcde13a --- /dev/null +++ b/sd/source/ui/annotations/annotationtag.hxx @@ -0,0 +1,89 @@ +/* -*- 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 . + */ + +#pragma once + +#include <vcl/vclevent.hxx> + +#include <smarttag.hxx> +#include "annotationwindow.hxx" + +namespace com::sun::star::office { class XAnnotation; } + +namespace sd { + +class View; +class AnnotationManagerImpl; + +class AnnotationTag final : public SmartTag +{ +public: + AnnotationTag( AnnotationManagerImpl& rManager, ::sd::View& rView, const css::uno::Reference< css::office::XAnnotation >& xAnnotation, Color const & rColor, int nIndex, const vcl::Font& rFont ); + virtual ~AnnotationTag() override; + + /// @return true if the SmartTag handled the event. + virtual bool MouseButtonDown( const MouseEvent&, SmartHdl& ) override; + + /// @return true if the SmartTag consumes this event. + virtual bool KeyInput( const KeyEvent& rKEvt ) override; + + /// @return true if the SmartTag consumes this event. + virtual bool Command( const CommandEvent& rCEvt ) override; + + // callbacks from sdr view + virtual sal_Int32 GetMarkablePointCount() const override; + virtual sal_Int32 GetMarkedPointCount() const override; + virtual bool MarkPoint(SdrHdl& rHdl, bool bUnmark) override; + virtual void CheckPossibilities() override; + virtual bool MarkPoints(const ::tools::Rectangle* pRect, bool bUnmark) override; + + void Move( int nDX, int nDY ); + bool OnMove( const KeyEvent& rKEvt ); + + BitmapEx CreateAnnotationBitmap(bool); + + const css::uno::Reference< css::office::XAnnotation >& GetAnnotation() const { return mxAnnotation; } + + void OpenPopup( bool bEdit ); + void ClosePopup(); + +private: + virtual void addCustomHandles( SdrHdlList& rHandlerList ) override; + virtual bool getContext( SdrViewContext& rContext ) override; + virtual void disposing() override; + virtual void select() override; + virtual void deselect() override; + + DECL_LINK( WindowEventHandler, VclWindowEvent&, void ); + DECL_LINK(PopupModeEndHdl, weld::Popover&, void); + + AnnotationManagerImpl& mrManager; + css::uno::Reference< css::office::XAnnotation > mxAnnotation; + std::unique_ptr<AnnotationWindow> mpAnnotationWindow; + Color maColor; + int mnIndex; + const vcl::Font& mrFont; + Size maSize; + VclPtr<vcl::Window> mpListenWindow; + Point maMouseDownPos; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/annotations/annotationwindow.cxx b/sd/source/ui/annotations/annotationwindow.cxx new file mode 100644 index 000000000..6c1210575 --- /dev/null +++ b/sd/source/ui/annotations/annotationwindow.cxx @@ -0,0 +1,802 @@ +/* -*- 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 <editeng/eeitem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/langitem.hxx> +#include <editeng/editview.hxx> +#include <editeng/editstat.hxx> +#include <editeng/outliner.hxx> +#include <editeng/editeng.hxx> +#include <editeng/outlobj.hxx> +#include <editeng/postitem.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/crossedoutitem.hxx> +#include <svx/svxids.hrc> +#include <unotools/useroptions.hxx> + +#include <sfx2/viewfrm.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> +#include <svl/stritem.hxx> + +#include <vcl/commandevent.hxx> +#include <vcl/commandinfoprovider.hxx> +#include <vcl/vclenum.hxx> +#include <vcl/svapp.hxx> +#include <vcl/gradient.hxx> +#include <vcl/settings.hxx> +#include <vcl/ptrstyle.hxx> + +#include <strings.hrc> +#include "annotationwindow.hxx" +#include "annotationmanagerimpl.hxx" + +#include <com/sun/star/office/XAnnotation.hpp> +#include <DrawDocShell.hxx> +#include <ViewShell.hxx> +#include <drawdoc.hxx> +#include <textapi.hxx> +#include <sdresid.hxx> + +#include <memory> + +using namespace ::sd; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::office; +using namespace ::com::sun::star::text; + +#define METABUTTON_WIDTH 16 +#define METABUTTON_HEIGHT 18 +#define POSTIT_META_HEIGHT sal_Int32(30) + +namespace sd { + +void AnnotationTextWindow::Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect) +{ + Size aSize = GetOutputSizePixel(); + + const bool bHighContrast = Application::GetSettings().GetStyleSettings().GetHighContrastMode(); + if (!bHighContrast) + { + rRenderContext.DrawGradient(::tools::Rectangle(Point(0,0), rRenderContext.PixelToLogic(aSize)), + Gradient(GradientStyle::Linear, mrContents.maColorLight, mrContents.maColor)); + } + + DoPaint(rRenderContext, rRect); +} + +void AnnotationTextWindow::EditViewScrollStateChange() +{ + mrContents.SetScrollbar(); +} + +bool AnnotationTextWindow::KeyInput(const KeyEvent& rKeyEvt) +{ + const vcl::KeyCode& rKeyCode = rKeyEvt.GetKeyCode(); + sal_uInt16 nKey = rKeyCode.GetCode(); + + bool bDone = false; + + if ((rKeyCode.IsMod1() && rKeyCode.IsMod2()) && ((nKey == KEY_PAGEUP) || (nKey == KEY_PAGEDOWN))) + { + SfxDispatcher* pDispatcher = mrContents.DocShell()->GetViewShell()->GetViewFrame()->GetDispatcher(); + if( pDispatcher ) + pDispatcher->Execute( nKey == KEY_PAGEDOWN ? SID_NEXT_POSTIT : SID_PREVIOUS_POSTIT ); + bDone = true; + } + else if (nKey == KEY_INSERT) + { + if (!rKeyCode.IsMod1() && !rKeyCode.IsMod2()) + mrContents.ToggleInsMode(); + bDone = true; + } + else + { + ::tools::Long aOldHeight = mrContents.GetPostItTextHeight(); + + /// HACK: need to switch off processing of Undo/Redo in Outliner + if ( !( (nKey == KEY_Z || nKey == KEY_Y) && rKeyCode.IsMod1()) ) + { + bool bIsProtected = mrContents.IsProtected(); + if (!bIsProtected || !EditEngine::DoesKeyChangeText(rKeyEvt) ) + { + if (EditView* pEditView = GetEditView()) + { + bDone = pEditView->PostKeyEvent(rKeyEvt); + if (!bDone && rKeyEvt.GetKeyCode().IsMod1() && !rKeyEvt.GetKeyCode().IsMod2()) + { + if (nKey == KEY_A) + { + EditEngine* pEditEngine = GetEditEngine(); + sal_Int32 nPar = pEditEngine->GetParagraphCount(); + if (nPar) + { + sal_Int32 nLen = pEditEngine->GetTextLen(nPar - 1); + pEditView->SetSelection(ESelection(0, 0, nPar - 1, nLen)); + } + bDone = true; + } + } + } + } + } + if (bDone) + { + mrContents.ResizeIfNecessary(aOldHeight, mrContents.GetPostItTextHeight()); + } + } + + return bDone; +} + +AnnotationTextWindow::AnnotationTextWindow(AnnotationWindow& rContents) + : mrContents(rContents) +{ +} + +EditView* AnnotationTextWindow::GetEditView() const +{ + OutlinerView* pOutlinerView = mrContents.GetOutlinerView(); + if (!pOutlinerView) + return nullptr; + return &pOutlinerView->GetEditView(); +} + +EditEngine* AnnotationTextWindow::GetEditEngine() const +{ + OutlinerView* pOutlinerView = mrContents.GetOutlinerView(); + if (!pOutlinerView) + return nullptr; + return pOutlinerView->GetEditView().GetEditEngine(); +} + +void AnnotationTextWindow::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + Size aSize(0, 0); + pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); + + SetOutputSizePixel(aSize); + + weld::CustomWidgetController::SetDrawingArea(pDrawingArea); + + EnableRTL(false); + + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + Color aBgColor = rStyleSettings.GetWindowColor(); + + OutputDevice& rDevice = pDrawingArea->get_ref_device(); + + rDevice.SetMapMode(MapMode(MapUnit::Map100thMM)); + rDevice.SetBackground(aBgColor); + + Size aOutputSize(rDevice.PixelToLogic(aSize)); + + EditView* pEditView = GetEditView(); + pEditView->setEditViewCallbacks(this); + + EditEngine* pEditEngine = GetEditEngine(); + pEditEngine->SetPaperSize(aOutputSize); + pEditEngine->SetRefDevice(&rDevice); + + pEditView->SetOutputArea(::tools::Rectangle(Point(0, 0), aOutputSize)); + pEditView->SetBackgroundColor(aBgColor); + + pDrawingArea->set_cursor(PointerStyle::Text); + + InitAccessible(); +} + +// see SwAnnotationWin in sw for something similar +AnnotationWindow::AnnotationWindow(weld::Window* pParent, const ::tools::Rectangle& rRect, + DrawDocShell* pDocShell, + const Reference<XAnnotation>& xAnnotation) + : mxBuilder(Application::CreateBuilder(pParent, "modules/simpress/ui/annotation.ui")) + , mxPopover(mxBuilder->weld_popover("Annotation")) + , mxContainer(mxBuilder->weld_widget("container")) + , mpDocShell(pDocShell) + , mpDoc(pDocShell->GetDoc()) + , mbReadonly(pDocShell->IsReadOnly()) + , mbProtected(false) +{ + mxContainer->set_size_request(320, 240); + mxPopover->popup_at_rect(pParent, rRect); + + InitControls(); + setAnnotation(xAnnotation); + FillMenuButton(); + + DoResize(); + + mxTextControl->GrabFocus(); +} + +AnnotationWindow::~AnnotationWindow() +{ +} + +void AnnotationWindow::InitControls() +{ + // window control for author and date + mxMeta = mxBuilder->weld_label("meta"); + mxMeta->set_direction(AllSettings::GetLayoutRTL()); + + maLabelFont = Application::GetSettings().GetStyleSettings().GetLabelFont(); + maLabelFont.SetFontHeight(8); + + // we should leave this setting alone, but for this we need a better layout algo + // with variable meta size height + mxMeta->set_font(maLabelFont); + + mpOutliner.reset( new ::Outliner(GetAnnotationPool(),OutlinerMode::TextObject) ); + SdDrawDocument::SetCalcFieldValueHdl( mpOutliner.get() ); + mpOutliner->SetUpdateLayout( true ); + + if (OutputDevice* pDev = mpDoc->GetRefDevice()) + mpOutliner->SetRefDevice( pDev ); + + mpOutlinerView.reset( new OutlinerView ( mpOutliner.get(), nullptr) ); + mpOutliner->InsertView(mpOutlinerView.get() ); + + //create Scrollbars + mxVScrollbar = mxBuilder->weld_scrolled_window("scrolledwindow", true); + + // actual window which holds the user text + mxTextControl.reset(new AnnotationTextWindow(*this)); + mxTextControlWin.reset(new weld::CustomWeld(*mxBuilder, "editview", *mxTextControl)); + mxTextControl->SetPointer(PointerStyle::Text); + + Rescale(); + OutputDevice& rDevice = mxTextControl->GetDrawingArea()->get_ref_device(); + + mxVScrollbar->set_direction(false); + mxVScrollbar->connect_vadjustment_changed(LINK(this, AnnotationWindow, ScrollHdl)); + + mpOutlinerView->SetBackgroundColor(COL_TRANSPARENT); + mpOutlinerView->SetOutputArea(rDevice.PixelToLogic(::tools::Rectangle(0, 0, 1, 1))); + + mxMenuButton = mxBuilder->weld_menu_button("menubutton"); + if (mbReadonly) + mxMenuButton->hide(); + else + { + mxMenuButton->set_size_request(METABUTTON_WIDTH, METABUTTON_HEIGHT); + mxMenuButton->connect_selected(LINK(this, AnnotationWindow, MenuItemSelectedHdl)); + } + + EEControlBits nCntrl = mpOutliner->GetControlWord(); + nCntrl |= EEControlBits::PASTESPECIAL | EEControlBits::AUTOCORRECT | EEControlBits::USECHARATTRIBS | EEControlBits::NOCOLORS; + mpOutliner->SetControlWord(nCntrl); + + mpOutliner->SetModifyHdl( Link<LinkParamNone*,void>() ); + mpOutliner->EnableUndo( false ); + + mpOutliner->ClearModifyFlag(); + mpOutliner->GetUndoManager().Clear(); + mpOutliner->EnableUndo( true ); + + SetLanguage(SvxLanguageItem(mpDoc->GetLanguage(EE_CHAR_LANGUAGE), SID_ATTR_LANGUAGE)); + + mxTextControl->GrabFocus(); +} + +IMPL_LINK(AnnotationWindow, MenuItemSelectedHdl, const OString&, rIdent, void) +{ + SfxDispatcher* pDispatcher = mpDocShell->GetViewShell()->GetViewFrame()->GetDispatcher(); + if (!pDispatcher) + return; + + if (rIdent == ".uno:ReplyToAnnotation") + { + const SfxUnoAnyItem aItem( SID_REPLYTO_POSTIT, Any( mxAnnotation ) ); + pDispatcher->ExecuteList(SID_REPLYTO_POSTIT, + SfxCallMode::ASYNCHRON, { &aItem }); + } + else if (rIdent == ".uno:DeleteAnnotation") + { + const SfxUnoAnyItem aItem( SID_DELETE_POSTIT, Any( mxAnnotation ) ); + pDispatcher->ExecuteList(SID_DELETE_POSTIT, SfxCallMode::ASYNCHRON, + { &aItem }); + } + else if (rIdent == ".uno:DeleteAllAnnotationByAuthor") + { + const SfxStringItem aItem( SID_DELETEALLBYAUTHOR_POSTIT, mxAnnotation->getAuthor() ); + pDispatcher->ExecuteList( SID_DELETEALLBYAUTHOR_POSTIT, + SfxCallMode::ASYNCHRON, { &aItem }); + } + else if (rIdent == ".uno:DeleteAllAnnotation") + pDispatcher->Execute( SID_DELETEALL_POSTIT ); +} + +void AnnotationWindow::FillMenuButton() +{ + SvtUserOptions aUserOptions; + OUString sCurrentAuthor( aUserOptions.GetFullName() ); + OUString sAuthor( mxAnnotation->getAuthor() ); + + OUString aStr(mxMenuButton->get_item_label(".uno:DeleteAllAnnotationByAuthor")); + OUString aReplace( sAuthor ); + if( aReplace.isEmpty() ) + aReplace = SdResId( STR_ANNOTATION_NOAUTHOR ); + aStr = aStr.replaceFirst("%1", aReplace); + mxMenuButton->set_item_label(".uno:DeleteAllAnnotationByAuthor", aStr); + + bool bShowReply = sAuthor != sCurrentAuthor && !mbReadonly; + mxMenuButton->set_item_visible(".uno:ReplyToAnnotation", bShowReply); + mxMenuButton->set_item_visible("separator", bShowReply); + mxMenuButton->set_item_visible(".uno:DeleteAnnotation", mxAnnotation.is() && !mbReadonly); + mxMenuButton->set_item_visible(".uno:DeleteAllAnnotationByAuthor", !mbReadonly); + mxMenuButton->set_item_visible(".uno:DeleteAllAnnotation", !mbReadonly); +} + +void AnnotationWindow::StartEdit() +{ + GetOutlinerView()->SetSelection(ESelection(EE_PARA_MAX_COUNT,EE_TEXTPOS_MAX_COUNT,EE_PARA_MAX_COUNT,EE_TEXTPOS_MAX_COUNT)); + GetOutlinerView()->ShowCursor(); +} + +void AnnotationWindow::SetMapMode(const MapMode& rNewMapMode) +{ + OutputDevice& rDevice = mxTextControl->GetDrawingArea()->get_ref_device(); + rDevice.SetMapMode(rNewMapMode); +} + +void AnnotationWindow::Rescale() +{ + MapMode aMode(MapUnit::Map100thMM); + aMode.SetOrigin( Point() ); + mpOutliner->SetRefMapMode( aMode ); + SetMapMode( aMode ); + + if (mxMeta) + { + vcl::Font aFont = maLabelFont; + sal_Int32 nHeight = ::tools::Long(aFont.GetFontHeight() * aMode.GetScaleY()); + aFont.SetFontHeight( nHeight ); + mxMeta->set_font(aFont); + } +} + +void AnnotationWindow::DoResize() +{ + OutputDevice& rDevice = mxTextControl->GetDrawingArea()->get_ref_device(); + + ::tools::Long aHeight = mxContainer->get_preferred_size().Height(); + ::tools::ULong aWidth = mxContainer->get_preferred_size().Width(); + + aHeight -= POSTIT_META_HEIGHT; + + mpOutliner->SetPaperSize( rDevice.PixelToLogic( Size(aWidth, aHeight) ) ) ; + ::tools::Long aTextHeight = rDevice.LogicToPixel(mpOutliner->CalcTextSize()).Height(); + + if( aTextHeight > aHeight ) + { + const int nThickness = mxVScrollbar->get_scroll_thickness(); + if (nThickness) + { + // we need vertical scrollbars and have to reduce the width + aWidth -= nThickness; + mpOutliner->SetPaperSize(rDevice.PixelToLogic(Size(aWidth, aHeight))); + } + mxVScrollbar->set_vpolicy(VclPolicyType::ALWAYS); + } + else + { + mxVScrollbar->set_vpolicy(VclPolicyType::NEVER); + } + + ::tools::Rectangle aOutputArea = rDevice.PixelToLogic(::tools::Rectangle(0, 0, aWidth, aHeight)); + if (mxVScrollbar->get_vpolicy() == VclPolicyType::NEVER) + { + // if we do not have a scrollbar anymore, we want to see the complete text + mpOutlinerView->SetVisArea(aOutputArea); + } + mpOutlinerView->SetOutputArea(aOutputArea); + mpOutlinerView->ShowCursor(true, true); + + int nUpper = mpOutliner->GetTextHeight(); + int nCurrentDocPos = mpOutlinerView->GetVisArea().Top(); + int nStepIncrement = mpOutliner->GetTextHeight() / 10; + int nPageIncrement = rDevice.PixelToLogic(Size(0,aHeight)).Height() * 8 / 10; + int nPageSize = rDevice.PixelToLogic(Size(0,aHeight)).Height(); + + /* limit the page size to below nUpper because gtk's gtk_scrolled_window_start_deceleration has + effectively... + + lower = gtk_adjustment_get_lower + upper = gtk_adjustment_get_upper - gtk_adjustment_get_page_size + + and requires that upper > lower or the deceleration animation never ends + */ + nPageSize = std::min(nPageSize, nUpper); + + mxVScrollbar->vadjustment_configure(nCurrentDocPos, 0, nUpper, + nStepIncrement, nPageIncrement, nPageSize); +} + +void AnnotationWindow::SetScrollbar() +{ + mxVScrollbar->vadjustment_set_value(mpOutlinerView->GetVisArea().Top()); +} + +void AnnotationWindow::ResizeIfNecessary(::tools::Long aOldHeight, ::tools::Long aNewHeight) +{ + if (aOldHeight != aNewHeight) + DoResize(); + else + SetScrollbar(); +} + +void AnnotationWindow::SetLanguage(const SvxLanguageItem &aNewItem) +{ + mpOutliner->SetModifyHdl( Link<LinkParamNone*,void>() ); + ESelection aOld = GetOutlinerView()->GetSelection(); + + ESelection aNewSelection( 0, 0, mpOutliner->GetParagraphCount()-1, EE_TEXTPOS_ALL ); + GetOutlinerView()->SetSelection( aNewSelection ); + SfxItemSet aEditAttr(GetOutlinerView()->GetAttribs()); + aEditAttr.Put(aNewItem); + GetOutlinerView()->SetAttribs( aEditAttr ); + + GetOutlinerView()->SetSelection(aOld); + + mxTextControl->Invalidate(); +} + +void AnnotationWindow::ToggleInsMode() +{ + if( mpOutlinerView ) + { + SfxBindings &rBnd = mpDocShell->GetViewShell()->GetViewFrame()->GetBindings(); + rBnd.Invalidate(SID_ATTR_INSERT); + rBnd.Update(SID_ATTR_INSERT); + } +} + +::tools::Long AnnotationWindow::GetPostItTextHeight() +{ + OutputDevice& rDevice = mxTextControl->GetDrawingArea()->get_ref_device(); + return mpOutliner ? rDevice.LogicToPixel(mpOutliner->CalcTextSize()).Height() : 0; +} + +IMPL_LINK(AnnotationWindow, ScrollHdl, weld::ScrolledWindow&, rScrolledWindow, void) +{ + ::tools::Long nDiff = GetOutlinerView()->GetEditView().GetVisArea().Top() - rScrolledWindow.vadjustment_get_value(); + GetOutlinerView()->Scroll( 0, nDiff ); +} + +TextApiObject* getTextApiObject( const Reference< XAnnotation >& xAnnotation ) +{ + if( xAnnotation.is() ) + { + Reference< XText > xText( xAnnotation->getTextRange() ); + return TextApiObject::getImplementation( xText ); + } + return nullptr; +} + +void AnnotationWindow::setAnnotation( const Reference< XAnnotation >& xAnnotation ) +{ + if( (xAnnotation == mxAnnotation) || !xAnnotation.is() ) + return; + + mxAnnotation = xAnnotation; + + SetColor(); + + SvtUserOptions aUserOptions; + mbProtected = aUserOptions.GetFullName() != xAnnotation->getAuthor(); + + mpOutliner->Clear(); + TextApiObject* pTextApi = getTextApiObject( mxAnnotation ); + + if( pTextApi ) + { + std::optional< OutlinerParaObject > pOPO( pTextApi->CreateText() ); + mpOutliner->SetText(*pOPO); + } + + mpOutliner->ClearModifyFlag(); + mpOutliner->GetUndoManager().Clear(); + +//TODO Invalidate(); + + OUString sMeta( xAnnotation->getAuthor() ); + OUString sDateTime( getAnnotationDateTimeString(xAnnotation) ); + + if( !sDateTime.isEmpty() ) + { + if( !sMeta.isEmpty() ) + sMeta += "\n"; + + sMeta += sDateTime; + } + mxMeta->set_label(sMeta); +} + +void AnnotationWindow::SetColor() +{ + sal_uInt16 nAuthorIdx = mpDoc->GetAnnotationAuthorIndex( mxAnnotation->getAuthor() ); + + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + const bool bHighContrast = rStyleSettings.GetHighContrastMode(); + if( bHighContrast ) + { + maColor = rStyleSettings.GetWindowColor(); + maColorDark = maColor; + maColorLight = rStyleSettings.GetWindowTextColor(); + } + else + { + maColor = AnnotationManagerImpl::GetColor( nAuthorIdx ); + maColorDark = AnnotationManagerImpl::GetColorDark( nAuthorIdx ); + maColorLight = AnnotationManagerImpl::GetColorLight( nAuthorIdx ); + } + + { + SvtAccessibilityOptions aOptions; + mpOutliner->ForceAutoColor( bHighContrast || aOptions.GetIsAutomaticFontColor() ); + } + + mxPopover->set_background(maColor); + mxMenuButton->set_background(maColor); + + mxMeta->set_font_color(bHighContrast ? maColorLight : maColorDark); + + mxVScrollbar->customize_scrollbars(maColorLight, + maColorDark, + maColor); + mxVScrollbar->set_scroll_thickness(GetPrefScrollbarWidth()); +} + +void AnnotationWindow::SaveToDocument() +{ + Reference< XAnnotation > xAnnotation( mxAnnotation ); + + // write changed text back to annotation + if (mpOutliner->IsModified()) + { + TextApiObject* pTextApi = getTextApiObject( xAnnotation ); + + if( pTextApi ) + { + std::optional<OutlinerParaObject> pOPO = mpOutliner->CreateParaObject(); + if( pOPO ) + { + if( mpDoc->IsUndoEnabled() ) + mpDoc->BegUndo( SdResId( STR_ANNOTATION_UNDO_EDIT ) ); + + pTextApi->SetText( *pOPO ); + pOPO.reset(); + + // set current time to changed annotation + xAnnotation->setDateTime( getCurrentDateTime() ); + + if( mpDoc->IsUndoEnabled() ) + mpDoc->EndUndo(); + + mpDocShell->SetModified(); + } + + } + } + mpOutliner->ClearModifyFlag(); + + mpOutliner->GetUndoManager().Clear(); +} + +bool AnnotationTextWindow::Command(const CommandEvent& rCEvt) +{ + if (rCEvt.GetCommand() == CommandEventId::ContextMenu) + { + const bool bReadOnly = mrContents.DocShell()->IsReadOnly(); + if (bReadOnly) + return true; + + SfxDispatcher* pDispatcher = mrContents.DocShell()->GetViewShell()->GetViewFrame()->GetDispatcher(); + if( !pDispatcher ) + return true; + + if (IsMouseCaptured()) + { + // so the menu can capture it and the EditView doesn't get the button release and change its + // selection on a successful button click + ReleaseMouse(); + } + + ::tools::Rectangle aRect(rCEvt.GetMousePosPixel(), Size(1, 1)); + weld::Widget* pPopupParent = GetDrawingArea(); + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pPopupParent, "modules/simpress/ui/annotationtagmenu.ui")); + std::unique_ptr<weld::Menu> xMenu(xBuilder->weld_menu("menu")); + + auto xAnnotation = mrContents.getAnnotation(); + + SvtUserOptions aUserOptions; + OUString sCurrentAuthor( aUserOptions.GetFullName() ); + OUString sAuthor( xAnnotation->getAuthor() ); + + OUString aStr(xMenu->get_label(".uno:DeleteAllAnnotationByAuthor")); + OUString aReplace( sAuthor ); + if( aReplace.isEmpty() ) + aReplace = SdResId( STR_ANNOTATION_NOAUTHOR ); + aStr = aStr.replaceFirst("%1", aReplace); + xMenu->set_label(".uno:DeleteAllAnnotationByAuthor", aStr); + + bool bShowReply = sAuthor != sCurrentAuthor && !bReadOnly; + xMenu->set_visible(".uno:ReplyToAnnotation", bShowReply); + xMenu->set_visible("separator", bShowReply); + xMenu->set_visible(".uno:DeleteAnnotation", xAnnotation.is() && !bReadOnly); + xMenu->set_visible(".uno:DeleteAllAnnotationByAuthor", !bReadOnly); + xMenu->set_visible(".uno:DeleteAllAnnotation", !bReadOnly); + + int nInsertPos = 2; + + auto xFrame = mrContents.DocShell()->GetViewShell()->GetViewFrame()->GetFrame().GetFrameInterface(); + OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(xFrame)); + + bool bEditable = !mrContents.IsProtected() && !bReadOnly; + if (bEditable) + { + SfxItemSet aSet(mrContents.GetOutlinerView()->GetAttribs()); + + xMenu->insert(nInsertPos++, ".uno:Bold", + vcl::CommandInfoProvider::GetMenuLabelForCommand( + vcl::CommandInfoProvider::GetCommandProperties(".uno:Bold", aModuleName)), + nullptr, nullptr, vcl::CommandInfoProvider::GetXGraphicForCommand(".uno:Bold", xFrame), + TRISTATE_TRUE); + + if ( aSet.GetItemState( EE_CHAR_WEIGHT ) == SfxItemState::SET ) + { + if( aSet.Get( EE_CHAR_WEIGHT ).GetWeight() == WEIGHT_BOLD ) + xMenu->set_active(".uno:Bold", true); + } + + xMenu->insert(nInsertPos++, ".uno:Italic", + vcl::CommandInfoProvider::GetMenuLabelForCommand( + vcl::CommandInfoProvider::GetCommandProperties(".uno:Italic", aModuleName)), + nullptr, nullptr, vcl::CommandInfoProvider::GetXGraphicForCommand(".uno:Italic", xFrame), + TRISTATE_TRUE); + + if ( aSet.GetItemState( EE_CHAR_ITALIC ) == SfxItemState::SET ) + { + if( aSet.Get( EE_CHAR_ITALIC ).GetPosture() != ITALIC_NONE ) + xMenu->set_active(".uno:Italic", true); + + } + + xMenu->insert(nInsertPos++, ".uno:Underline", + vcl::CommandInfoProvider::GetMenuLabelForCommand( + vcl::CommandInfoProvider::GetCommandProperties(".uno:Underline", aModuleName)), + nullptr, nullptr, vcl::CommandInfoProvider::GetXGraphicForCommand(".uno:Underline", xFrame), + TRISTATE_TRUE); + + if ( aSet.GetItemState( EE_CHAR_UNDERLINE ) == SfxItemState::SET ) + { + if( aSet.Get( EE_CHAR_UNDERLINE ).GetLineStyle() != LINESTYLE_NONE ) + xMenu->set_active(".uno:Underline", true); + } + + xMenu->insert(nInsertPos++, ".uno:Strikeout", + vcl::CommandInfoProvider::GetMenuLabelForCommand( + vcl::CommandInfoProvider::GetCommandProperties(".uno:Strikeout", aModuleName)), + nullptr, nullptr, vcl::CommandInfoProvider::GetXGraphicForCommand(".uno:Strikeout", xFrame), + TRISTATE_TRUE); + + if ( aSet.GetItemState( EE_CHAR_STRIKEOUT ) == SfxItemState::SET ) + { + if( aSet.Get( EE_CHAR_STRIKEOUT ).GetStrikeout() != STRIKEOUT_NONE ) + xMenu->set_active(".uno:Strikeout", true); + } + + xMenu->insert_separator(nInsertPos++, "separator2"); + } + + xMenu->insert(nInsertPos++, ".uno:Copy", + vcl::CommandInfoProvider::GetMenuLabelForCommand( + vcl::CommandInfoProvider::GetCommandProperties(".uno:Copy", aModuleName)), + nullptr, nullptr, vcl::CommandInfoProvider::GetXGraphicForCommand(".uno:Copy", xFrame), + TRISTATE_INDET); + + xMenu->insert(nInsertPos++, ".uno:Paste", + vcl::CommandInfoProvider::GetMenuLabelForCommand( + vcl::CommandInfoProvider::GetCommandProperties(".uno:Paste", aModuleName)), + nullptr, nullptr, vcl::CommandInfoProvider::GetXGraphicForCommand(".uno:Paste", xFrame), + TRISTATE_INDET); + + bool bCanPaste = false; + if (bEditable) + { + TransferableDataHelper aDataHelper(TransferableDataHelper::CreateFromClipboard(GetClipboard())); + bCanPaste = aDataHelper.GetFormatCount() != 0; + } + + xMenu->insert_separator(nInsertPos++, "separator3"); + + xMenu->set_sensitive(".uno:Copy", mrContents.GetOutlinerView()->HasSelection()); + xMenu->set_sensitive(".uno:Paste", bCanPaste); + + auto sId = xMenu->popup_at_rect(pPopupParent, aRect); + + if (sId == ".uno:ReplyToAnnotation") + { + const SfxUnoAnyItem aItem( SID_REPLYTO_POSTIT, Any( xAnnotation ) ); + pDispatcher->ExecuteList(SID_REPLYTO_POSTIT, + SfxCallMode::ASYNCHRON, { &aItem }); + } + else if (sId == ".uno:DeleteAnnotation") + { + const SfxUnoAnyItem aItem( SID_DELETE_POSTIT, Any( xAnnotation ) ); + pDispatcher->ExecuteList(SID_DELETE_POSTIT, SfxCallMode::ASYNCHRON, + { &aItem }); + } + else if (sId == ".uno:DeleteAllAnnotationByAuthor") + { + const SfxStringItem aItem( SID_DELETEALLBYAUTHOR_POSTIT, sAuthor ); + pDispatcher->ExecuteList( SID_DELETEALLBYAUTHOR_POSTIT, + SfxCallMode::ASYNCHRON, { &aItem }); + } + else if (sId == ".uno:DeleteAllAnnotation") + pDispatcher->Execute( SID_DELETEALL_POSTIT ); + else if (sId == ".uno:Copy") + { + mrContents.GetOutlinerView()->Copy(); + } + else if (sId == ".uno:Paste") + { + mrContents.GetOutlinerView()->PasteSpecial(); + mrContents.DoResize(); + } + else if (!sId.isEmpty()) + { + SfxItemSet aEditAttr(mrContents.GetOutlinerView()->GetAttribs()); + SfxItemSet aNewAttr(mrContents.GetOutliner()->GetEmptyItemSet()); + + if (sId == ".uno:Bold") + { + FontWeight eFW = aEditAttr.Get( EE_CHAR_WEIGHT ).GetWeight(); + aNewAttr.Put( SvxWeightItem( eFW == WEIGHT_NORMAL ? WEIGHT_BOLD : WEIGHT_NORMAL, EE_CHAR_WEIGHT ) ); + } + else if (sId == ".uno:Italic") + { + FontItalic eFI = aEditAttr.Get( EE_CHAR_ITALIC ).GetPosture(); + aNewAttr.Put( SvxPostureItem( eFI == ITALIC_NORMAL ? ITALIC_NONE : ITALIC_NORMAL, EE_CHAR_ITALIC ) ); + } + else if (sId == ".uno:Underline") + { + FontLineStyle eFU = aEditAttr. Get( EE_CHAR_UNDERLINE ).GetLineStyle(); + aNewAttr.Put( SvxUnderlineItem( eFU == LINESTYLE_SINGLE ? LINESTYLE_NONE : LINESTYLE_SINGLE, EE_CHAR_UNDERLINE ) ); + } + else if (sId == ".uno:Strikeout") + { + FontStrikeout eFSO = aEditAttr.Get( EE_CHAR_STRIKEOUT ).GetStrikeout(); + aNewAttr.Put( SvxCrossedOutItem( eFSO == STRIKEOUT_SINGLE ? STRIKEOUT_NONE : STRIKEOUT_SINGLE, EE_CHAR_STRIKEOUT ) ); + } + + mrContents.GetOutlinerView()->SetAttribs( aNewAttr ); + } + + return true; + } + return WeldEditView::Command(rCEvt); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/annotations/annotationwindow.hxx b/sd/source/ui/annotations/annotationwindow.hxx new file mode 100644 index 000000000..558cc6165 --- /dev/null +++ b/sd/source/ui/annotations/annotationwindow.hxx @@ -0,0 +1,143 @@ +/* -*- 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 . + */ + +#pragma once + +#include <vcl/weld.hxx> +#include <tools/long.hxx> +#include <svx/weldeditview.hxx> + +namespace com::sun::star::office { class XAnnotation; } + +class OutlinerView; +class Outliner; +class SvxLanguageItem; +class SdDrawDocument; + +namespace sd { + +class AnnotationManagerImpl; +class DrawDocShell; +class TextApiObject; + +class AnnotationWindow; + +class AnnotationTextWindow : public WeldEditView +{ +private: + AnnotationWindow& mrContents; + +public: + AnnotationTextWindow(AnnotationWindow& rContents); + + virtual EditView* GetEditView() const override; + + virtual EditEngine* GetEditEngine() const override; + + virtual void EditViewScrollStateChange() override; + + void SetDrawingArea(weld::DrawingArea* pDrawingArea) override; + + virtual void Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect) override; + virtual bool KeyInput(const KeyEvent& rKeyEvt) override; + virtual bool Command(const CommandEvent& rCEvt) override; +}; + +class AnnotationWindow final +{ +private: + std::unique_ptr<weld::Builder> mxBuilder; + std::unique_ptr<weld::Popover> mxPopover; + std::unique_ptr<weld::Widget> mxContainer; + + DrawDocShell* mpDocShell; + SdDrawDocument* mpDoc; + + bool mbReadonly; + bool mbProtected; + + css::uno::Reference< css::office::XAnnotation > mxAnnotation; + +public: + Color maColor; + Color maColorDark; + Color maColorLight; + +private: + vcl::Font maLabelFont; + + std::unique_ptr<OutlinerView> mpOutlinerView; + std::unique_ptr<::Outliner> mpOutliner; + + std::unique_ptr<weld::ScrolledWindow> mxVScrollbar; + std::unique_ptr<WeldEditView> mxTextControl; + std::unique_ptr<weld::CustomWeld> mxTextControlWin; + std::unique_ptr<weld::Label> mxMeta; + std::unique_ptr<weld::MenuButton> mxMenuButton; + + DECL_LINK(ScrollHdl, weld::ScrolledWindow&, void); + DECL_LINK(MenuItemSelectedHdl, const OString&, void); + + void FillMenuButton(); + void InitControls(); + + void SetMapMode(const MapMode& rNewMapMode); + void setAnnotation(const css::uno::Reference<css::office::XAnnotation>& xAnnotation); + + static sal_Int32 GetPrefScrollbarWidth() { return 16; } +public: + AnnotationWindow(weld::Window* pParent, const ::tools::Rectangle& rRect, DrawDocShell* pDocShell, + const css::uno::Reference<css::office::XAnnotation>& xAnnotation); + + void connect_closed(const Link<weld::Popover&, void>& rLink) { mxPopover->connect_closed(rLink); } + + void DoResize(); + void ResizeIfNecessary(::tools::Long aOldHeight, ::tools::Long aNewHeight); + void SetScrollbar(); + void StartEdit(); + + const css::uno::Reference<css::office::XAnnotation>& getAnnotation() const { return mxAnnotation; } + + void SaveToDocument(); + + ::tools::Long GetPostItTextHeight(); + + DrawDocShell* DocShell() { return mpDocShell; } + + void SetLanguage(const SvxLanguageItem &aNewItem); + + void Rescale(); + + void ToggleInsMode(); + + bool IsProtected() const { return mbProtected; } + + OutlinerView* GetOutlinerView() { return mpOutlinerView.get();} + ::Outliner* GetOutliner() { return mpOutliner.get();} + ~AnnotationWindow(); + + void SetColor(); +}; + +TextApiObject* getTextApiObject( const css::uno::Reference< css::office::XAnnotation >& xAnnotation ); + + +} // namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/app/optsitem.cxx b/sd/source/ui/app/optsitem.cxx new file mode 100644 index 000000000..5baff32e2 --- /dev/null +++ b/sd/source/ui/app/optsitem.cxx @@ -0,0 +1,1407 @@ +/* -*- 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 <sal/config.h> + +#include <o3tl/any.hxx> +#include <svx/svdmodel.hxx> +#include <svx/svxids.hrc> +#include <tools/debug.hxx> +#include <tools/helpers.hxx> +#include <unotools/localedatawrapper.hxx> +#include <unotools/syslocale.hxx> +#include <osl/diagnose.h> + +#include <optsitem.hxx> +#include <FrameView.hxx> +#include <sdattr.hrc> + +using namespace ::utl; +using namespace ::com::sun::star::uno; + +template< class T > static T getSafeValue( const Any& rAny ) +{ + T value = T(); + bool bOk = (rAny >>= value); + + DBG_ASSERT( bOk, "SdOptionsItem, wrong type from configuration!" ); + + return value; +} + + +SdOptionsItem::SdOptionsItem( const SdOptionsGeneric& rParent, const OUString& rSubTree ) : + ConfigItem ( rSubTree ), + mrParent ( rParent ) +{ +} + +SdOptionsItem::~SdOptionsItem() +{ +} + +void SdOptionsItem::ImplCommit() +{ + if( IsModified() ) + mrParent.Commit( *this ); +}; + +void SdOptionsItem::Notify( const css::uno::Sequence<OUString>& ) +{} + +Sequence< Any > SdOptionsItem::GetProperties( const Sequence< OUString >& rNames ) +{ + return ConfigItem::GetProperties( rNames ); +} + +bool SdOptionsItem::PutProperties( const Sequence< OUString >& rNames, const Sequence< Any>& rValues ) +{ + return ConfigItem::PutProperties( rNames, rValues ); +} + +SdOptionsGeneric::SdOptionsGeneric(bool bImpress, const OUString& rSubTree) + : maSubTree(rSubTree) + , mbImpress(bImpress) + , mbInit(rSubTree.isEmpty()) + , mbEnableModify(false) +{ +} + +SdOptionsGeneric::SdOptionsGeneric(SdOptionsGeneric const & rSource) +{ + operator=(rSource); +} + +SdOptionsGeneric& SdOptionsGeneric::operator=(SdOptionsGeneric const & rSource) +{ + if (this != &rSource) + { + maSubTree = rSource.maSubTree; + mpCfgItem.reset(rSource.mpCfgItem ? new SdOptionsItem(*rSource.mpCfgItem) : nullptr ); + mbImpress = rSource.mbImpress; + mbInit = rSource.mbInit; + mbEnableModify = rSource.mbEnableModify; + } + return *this; +} + +void SdOptionsGeneric::Init() const +{ + if( mbInit ) + return; + + SdOptionsGeneric* pThis = const_cast<SdOptionsGeneric*>(this); + + if( !mpCfgItem ) + pThis->mpCfgItem.reset( new SdOptionsItem( *this, maSubTree ) ); + assert(mpCfgItem && "mpCfgItem is set by now"); + + const Sequence< OUString > aNames( GetPropertyNames() ); + const Sequence< Any > aValues = mpCfgItem->GetProperties( aNames ); + + if( aNames.hasElements() && ( aValues.getLength() == aNames.getLength() ) ) + { + const Any* pValues = aValues.getConstArray(); + + pThis->EnableModify( false ); + pThis->mbInit = pThis->ReadData( pValues ); + pThis->EnableModify( true ); + } + else + pThis->mbInit = true; +} + +SdOptionsGeneric::~SdOptionsGeneric() +{ +} + +void SdOptionsGeneric::Commit( SdOptionsItem& rCfgItem ) const +{ + const Sequence< OUString > aNames( GetPropertyNames() ); + Sequence< Any > aValues( aNames.getLength() ); + + if( aNames.hasElements() ) + { + if( WriteData( aValues.getArray() ) ) + rCfgItem.PutProperties( aNames, aValues ); + else + { + OSL_FAIL( "PutProperties failed" ); + } + } +} + +Sequence< OUString > SdOptionsGeneric::GetPropertyNames() const +{ + sal_uLong nCount; + const char** ppPropNames; + + GetPropNameArray( ppPropNames, nCount ); + + Sequence< OUString > aNames( nCount ); + OUString* pNames = aNames.getArray(); + + for( sal_uLong i = 0; i < nCount; i++ ) + pNames[ i ] = OUString::createFromAscii( ppPropNames[ i ] ); + + return aNames; +} + +void SdOptionsGeneric::Store() +{ + if( mpCfgItem ) + mpCfgItem->Commit(); +} + +bool SdOptionsGeneric::isMetricSystem() +{ + SvtSysLocale aSysLocale; + MeasurementSystem eSys = aSysLocale.GetLocaleData().getMeasurementSystemEnum(); + + return ( eSys == MeasurementSystem::Metric ); +} + +/************************************************************************* +|* +|* SdOptionsLayout +|* +\************************************************************************/ + +SdOptionsLayout::SdOptionsLayout(bool bImpress, bool bUseConfig) : + SdOptionsGeneric( bImpress, bUseConfig ? + ( bImpress ? + OUString( "Office.Impress/Layout" ) : + OUString( "Office.Draw/Layout" ) ) : + OUString() ), + bRuler( true ), + bMoveOutline( true ), + bDragStripes( false ), + bHandlesBezier( false ), + bHelplines( true ), + nMetric(static_cast<sal_uInt16>(isMetricSystem() ? FieldUnit::CM : FieldUnit::INCH)), + nDefTab( 1250 ) +{ + EnableModify( true ); +} + +bool SdOptionsLayout::operator==( const SdOptionsLayout& rOpt ) const +{ + return( IsRulerVisible() == rOpt.IsRulerVisible() && + IsMoveOutline() == rOpt.IsMoveOutline() && + IsDragStripes() == rOpt.IsDragStripes() && + IsHandlesBezier() == rOpt.IsHandlesBezier() && + IsHelplines() == rOpt.IsHelplines() && + GetMetric() == rOpt.GetMetric() && + GetDefTab() == rOpt.GetDefTab() ); +} + +void SdOptionsLayout::GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const +{ + if( isMetricSystem() ) + { + static const char* aPropNamesMetric[] = + { + "Display/Ruler", + "Display/Bezier", + "Display/Contour", + "Display/Guide", + "Display/Helpline", + "Other/MeasureUnit/Metric", + "Other/TabStop/Metric" + }; + ppNames = aPropNamesMetric; + rCount = SAL_N_ELEMENTS(aPropNamesMetric); + } + else + { + static const char* aPropNamesNonMetric[] = + { + "Display/Ruler", + "Display/Bezier", + "Display/Contour", + "Display/Guide", + "Display/Helpline", + "Other/MeasureUnit/NonMetric", + "Other/TabStop/NonMetric" + }; + ppNames = aPropNamesNonMetric; + rCount = SAL_N_ELEMENTS(aPropNamesNonMetric); + } +} + +bool SdOptionsLayout::ReadData( const Any* pValues ) +{ + if( pValues[0].hasValue() ) SetRulerVisible( *o3tl::doAccess<bool>(pValues[ 0 ]) ); + if( pValues[1].hasValue() ) SetHandlesBezier( *o3tl::doAccess<bool>(pValues[ 1 ]) ); + if( pValues[2].hasValue() ) SetMoveOutline( *o3tl::doAccess<bool>(pValues[ 2 ]) ); + if( pValues[3].hasValue() ) SetDragStripes( *o3tl::doAccess<bool>(pValues[ 3 ]) ); + if( pValues[4].hasValue() ) SetHelplines( *o3tl::doAccess<bool>(pValues[ 4 ]) ); + if( pValues[5].hasValue() ) SetMetric( static_cast<sal_uInt16>(*o3tl::doAccess<sal_Int32>(pValues[ 5 ])) ); + if( pValues[6].hasValue() ) SetDefTab( static_cast<sal_uInt16>(*o3tl::doAccess<sal_Int32>(pValues[ 6 ])) ); + + return true; +} + +bool SdOptionsLayout::WriteData( Any* pValues ) const +{ + pValues[ 0 ] <<= IsRulerVisible(); + pValues[ 1 ] <<= IsHandlesBezier(); + pValues[ 2 ] <<= IsMoveOutline(); + pValues[ 3 ] <<= IsDragStripes(); + pValues[ 4 ] <<= IsHelplines(); + pValues[ 5 ] <<= static_cast<sal_Int32>(GetMetric()); + pValues[ 6 ] <<= static_cast<sal_Int32>(GetDefTab()); + + return true; +} + +/************************************************************************* +|* +|* SdOptionsLayoutItem +|* +\************************************************************************/ + +SdOptionsLayoutItem::SdOptionsLayoutItem() +: SfxPoolItem ( ATTR_OPTIONS_LAYOUT ) +, maOptionsLayout ( false, false ) +{ +} + +SdOptionsLayoutItem::SdOptionsLayoutItem( SdOptions const * pOpts, ::sd::FrameView const * pView ) +: SfxPoolItem ( ATTR_OPTIONS_LAYOUT ) +, maOptionsLayout ( false, false ) +{ + if( pOpts ) + { + maOptionsLayout.SetMetric( pOpts->GetMetric() ); + maOptionsLayout.SetDefTab( pOpts->GetDefTab() ); + } + + if( pView ) + { + maOptionsLayout.SetRulerVisible( pView->HasRuler() ); + maOptionsLayout.SetMoveOutline( !pView->IsNoDragXorPolys() ); + maOptionsLayout.SetDragStripes( pView->IsDragStripes() ); + maOptionsLayout.SetHandlesBezier( pView->IsPlusHandlesAlwaysVisible() ); + maOptionsLayout.SetHelplines( pView->IsHlplVisible() ); + } + else if( pOpts ) + { + maOptionsLayout.SetRulerVisible( pOpts->IsRulerVisible() ); + maOptionsLayout.SetMoveOutline( pOpts->IsMoveOutline() ); + maOptionsLayout.SetDragStripes( pOpts->IsDragStripes() ); + maOptionsLayout.SetHandlesBezier( pOpts->IsHandlesBezier() ); + maOptionsLayout.SetHelplines( pOpts->IsHelplines() ); + } +} + +SdOptionsLayoutItem* SdOptionsLayoutItem::Clone( SfxItemPool* ) const +{ + return new SdOptionsLayoutItem( *this ); +} + +bool SdOptionsLayoutItem::operator==( const SfxPoolItem& rAttr ) const +{ + assert(SfxPoolItem::operator==(rAttr)); + return maOptionsLayout == static_cast<const SdOptionsLayoutItem&>(rAttr).maOptionsLayout; +} + +void SdOptionsLayoutItem::SetOptions( SdOptions* pOpts ) const +{ + if( pOpts ) + { + pOpts->SetRulerVisible( maOptionsLayout.IsRulerVisible() ); + pOpts->SetMoveOutline( maOptionsLayout.IsMoveOutline() ); + pOpts->SetDragStripes( maOptionsLayout.IsDragStripes() ); + pOpts->SetHandlesBezier( maOptionsLayout.IsHandlesBezier() ); + pOpts->SetHelplines( maOptionsLayout.IsHelplines() ); + pOpts->SetMetric( maOptionsLayout.GetMetric() ); + pOpts->SetDefTab( maOptionsLayout.GetDefTab() ); + } +} + +/************************************************************************* +|* +|* SdOptionsContents +|* +\************************************************************************/ + +SdOptionsContents::SdOptionsContents(bool bImpress) : + SdOptionsGeneric( bImpress, bImpress ? + OUString( "Office.Impress/Content" ) : + OUString( "Office.Draw/Content" ) ) +{ + EnableModify( true ); +} + +bool SdOptionsContents::operator==(const SdOptionsContents&) const +{ + return true; +} + +void SdOptionsContents::GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const +{ + static const char* aPropNames[] = + { + "Display/PicturePlaceholder", + "Display/ContourMode", + "Display/LineContour", + "Display/TextPlaceholder" + }; + + rCount = SAL_N_ELEMENTS(aPropNames); + ppNames = aPropNames; +} + +bool SdOptionsContents::ReadData(const Any*) +{ + return true; +} + +bool SdOptionsContents::WriteData( Any* pValues ) const +{ + //#i80528# no draft anymore + pValues[ 0 ] <<= false; + pValues[ 1 ] <<= false; + pValues[ 2 ] <<= false; + pValues[ 3 ] <<= false; + + return true; +} +/************************************************************************* +|* +|* SdOptionsMisc +|* +\************************************************************************/ + +SdOptionsMisc::SdOptionsMisc( bool bImpress, bool bUseConfig ) : + SdOptionsGeneric( bImpress, bUseConfig ? + ( bImpress ? + OUString( "Office.Impress/Misc" ) : + OUString( "Office.Draw/Misc" ) ) : + OUString() ), + nDefaultObjectSizeWidth(8000), + nDefaultObjectSizeHeight(5000), + bStartWithTemplate( false ), + bMarkedHitMovesAlways( true ), + bMoveOnlyDragging( false ), + bCrookNoContortion( false ), + bQuickEdit( IsImpress() ), + bMasterPageCache( true ), + bDragWithCopy( false ), + bPickThrough( true ), + bDoubleClickTextEdit( true ), + bClickChangeRotation( false ), + bEnableSdremote( false ), + bEnablePresenterScreen( true), + bSolidDragging( true ), + bSummationOfParagraphs( false ), + bTabBarVisible( true ), + bShowUndoDeleteWarning( true ), + bSlideshowRespectZOrder( true ), + bShowComments( true ), + bPreviewNewEffects( true ), + bPreviewChangedEffects( false ), + bPreviewTransitions( true ), + mnDisplay( 0 ), + mnPenColor( 0xff0000 ), + mnPenWidth( 150.0 ), + + // The default for 6.1-and-above documents is to use printer-independent + // formatting. + mnPrinterIndependentLayout (1) +{ + EnableModify( true ); +} + +bool SdOptionsMisc::operator==( const SdOptionsMisc& rOpt ) const +{ + return( IsStartWithTemplate() == rOpt.IsStartWithTemplate() && + IsMarkedHitMovesAlways() == rOpt.IsMarkedHitMovesAlways() && + IsMoveOnlyDragging() == rOpt.IsMoveOnlyDragging() && + IsCrookNoContortion() == rOpt.IsCrookNoContortion() && + IsQuickEdit() == rOpt.IsQuickEdit() && + IsMasterPagePaintCaching() == rOpt.IsMasterPagePaintCaching() && + IsDragWithCopy() == rOpt.IsDragWithCopy() && + IsPickThrough() == rOpt.IsPickThrough() && + IsDoubleClickTextEdit() == rOpt.IsDoubleClickTextEdit() && + IsClickChangeRotation() == rOpt.IsClickChangeRotation() && + IsEnableSdremote() == rOpt.IsEnableSdremote() && + IsEnablePresenterScreen() == rOpt.IsEnablePresenterScreen()&& + IsSummationOfParagraphs() == rOpt.IsSummationOfParagraphs() && + IsTabBarVisible() == rOpt.IsTabBarVisible() && + IsSolidDragging() == rOpt.IsSolidDragging() && + IsShowUndoDeleteWarning() == rOpt.IsShowUndoDeleteWarning() && + IsSlideshowRespectZOrder() == rOpt.IsSlideshowRespectZOrder() && + GetPrinterIndependentLayout() == rOpt.GetPrinterIndependentLayout() && + GetDefaultObjectSizeWidth() == rOpt.GetDefaultObjectSizeWidth() && + GetDefaultObjectSizeHeight() == rOpt.GetDefaultObjectSizeHeight() && + + IsPreviewNewEffects() == rOpt.IsPreviewNewEffects() && + IsPreviewChangedEffects() == rOpt.IsPreviewChangedEffects() && + IsPreviewTransitions() == rOpt.IsPreviewTransitions() && + GetDisplay() == rOpt.GetDisplay() && + IsShowComments() == rOpt.IsShowComments() && + GetPresentationPenColor() == rOpt.GetPresentationPenColor() && + GetPresentationPenWidth() == rOpt.GetPresentationPenWidth() + ); +} + +void SdOptionsMisc::GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const +{ + static const char* aPropNames[] = + { + "ObjectMoveable", + "NoDistort", + "TextObject/QuickEditing", + "BackgroundCache", + "CopyWhileMoving", + "TextObject/Selectable", + "DclickTextedit", + "RotateClick", + "Preview", + "ModifyWithAttributes", + "DefaultObjectSize/Width", + "DefaultObjectSize/Height", + + "Compatibility/PrinterIndependentLayout", + + "ShowComments", + + // just for impress + "NewDoc/AutoPilot", + "Compatibility/AddBetween", + "ShowUndoDeleteWarning", + "SlideshowRespectZOrder", + + "PreviewNewEffects", + "PreviewChangedEffects", + "PreviewTransitions", + + "Display", + + "PenColor", + "PenWidth", + "Start/EnableSdremote", + "Start/EnablePresenterScreen", + "TabBarVisible" + }; + + rCount = ( IsImpress() ? SAL_N_ELEMENTS(aPropNames) : 14 ); + ppNames = aPropNames; +} + +bool SdOptionsMisc::ReadData( const Any* pValues ) +{ + if( pValues[0].hasValue() ) SetMarkedHitMovesAlways( *o3tl::doAccess<bool>(pValues[ 0 ]) ); + if( pValues[1].hasValue() ) SetCrookNoContortion( *o3tl::doAccess<bool>(pValues[ 1 ]) ); + if( pValues[2].hasValue() ) SetQuickEdit( *o3tl::doAccess<bool>(pValues[ 2 ]) ); + if( pValues[3].hasValue() ) SetMasterPagePaintCaching( *o3tl::doAccess<bool>(pValues[ 3 ]) ); + if( pValues[4].hasValue() ) SetDragWithCopy( *o3tl::doAccess<bool>(pValues[ 4 ]) ); + if( pValues[5].hasValue() ) SetPickThrough( *o3tl::doAccess<bool>(pValues[ 5 ]) ); + if( pValues[6].hasValue() ) SetDoubleClickTextEdit( *o3tl::doAccess<bool>(pValues[ 6 ]) ); + if( pValues[7].hasValue() ) SetClickChangeRotation( *o3tl::doAccess<bool>(pValues[ 7 ]) ); + if( pValues[9].hasValue() ) SetSolidDragging( *o3tl::doAccess<bool>(pValues[ 9 ]) ); + if( pValues[10].hasValue() ) SetDefaultObjectSizeWidth( *o3tl::doAccess<sal_Int32>(pValues[ 10 ]) ); + if( pValues[11].hasValue() ) SetDefaultObjectSizeHeight( *o3tl::doAccess<sal_Int32>(pValues[ 11 ]) ); + if( pValues[12].hasValue() ) SetPrinterIndependentLayout( *o3tl::doAccess<sal_uInt16>(pValues[ 12 ]) ); + + if( pValues[13].hasValue() ) + SetShowComments( *o3tl::doAccess<bool>(pValues[ 13 ]) ); + + // just for Impress + if (IsImpress()) + { + if( pValues[14].hasValue() ) + SetStartWithTemplate( *o3tl::doAccess<bool>(pValues[ 14 ]) ); + if( pValues[15].hasValue() ) + SetSummationOfParagraphs( *o3tl::doAccess<bool>(pValues[ 15 ]) ); + if( pValues[16].hasValue() ) + SetShowUndoDeleteWarning( *o3tl::doAccess<bool>(pValues[ 16 ]) ); + + if( pValues[17].hasValue() ) + SetSlideshowRespectZOrder(*o3tl::doAccess<bool>(pValues[ 17 ])); + + if( pValues[18].hasValue() ) + SetPreviewNewEffects(*o3tl::doAccess<bool>(pValues[ 18 ])); + + if( pValues[19].hasValue() ) + SetPreviewChangedEffects(*o3tl::doAccess<bool>(pValues[ 19 ])); + + if( pValues[20].hasValue() ) + SetPreviewTransitions(*o3tl::doAccess<bool>(pValues[ 20 ])); + + if( pValues[21].hasValue() ) + SetDisplay(*o3tl::doAccess<sal_Int32>(pValues[ 21 ])); + + if( pValues[22].hasValue() ) + SetPresentationPenColor( getSafeValue< sal_Int32 >( pValues[ 22 ] ) ); + + if( pValues[23].hasValue() ) + SetPresentationPenWidth( getSafeValue< double >( pValues[ 23 ] ) ); + + if( pValues[24].hasValue() ) + SetEnableSdremote( *o3tl::doAccess<bool>(pValues[ 24 ]) ); + + if( pValues[25].hasValue() ) + SetEnablePresenterScreen( *o3tl::doAccess<bool>(pValues[ 25 ]) ); + + if( pValues[26].hasValue() ) { + SetTabBarVisible( *o3tl::doAccess<bool>(pValues[ 26 ]) ); + } + } + + return true; +} + +bool SdOptionsMisc::WriteData( Any* pValues ) const +{ + pValues[ 0 ] <<= IsMarkedHitMovesAlways(); + pValues[ 1 ] <<= IsCrookNoContortion(); + pValues[ 2 ] <<= IsQuickEdit(); + pValues[ 3 ] <<= IsMasterPagePaintCaching(); + pValues[ 4 ] <<= IsDragWithCopy(); + pValues[ 5 ] <<= IsPickThrough(); + pValues[ 6 ] <<= IsDoubleClickTextEdit(); + pValues[ 7 ] <<= IsClickChangeRotation(); + // The preview is not supported anymore. Use a dummy value. + pValues[ 8 ] <<= double(0);// GetPreviewQuality(); + pValues[ 9 ] <<= IsSolidDragging(); + pValues[ 10 ] <<= GetDefaultObjectSizeWidth(); + pValues[ 11 ] <<= GetDefaultObjectSizeHeight(); + pValues[ 12 ] <<= GetPrinterIndependentLayout(); + pValues[ 13 ] <<= IsShowComments(); + + // just for Impress + if (IsImpress()) + { + pValues[ 14 ] <<= IsStartWithTemplate(); + pValues[ 15 ] <<= IsSummationOfParagraphs(); + pValues[ 16 ] <<= IsShowUndoDeleteWarning(); + pValues[ 17 ] <<= IsSlideshowRespectZOrder(); + + pValues[ 18 ] <<= IsPreviewNewEffects(); + pValues[ 19 ] <<= IsPreviewChangedEffects(); + pValues[ 20 ] <<= IsPreviewTransitions(); + + pValues[ 21 ] <<= GetDisplay(); + + pValues[ 22 ] <<= GetPresentationPenColor(); + pValues[ 23 ] <<= GetPresentationPenWidth(); + pValues[ 24 ] <<= IsEnableSdremote(); + pValues[ 25 ] <<= IsEnablePresenterScreen(); + pValues[ 26 ] <<= IsTabBarVisible(); + } + + return true; +} + +/************************************************************************* +|* +|* SdOptionsMiscItem +|* +\************************************************************************/ + +SdOptionsMiscItem::SdOptionsMiscItem() +: SfxPoolItem ( ATTR_OPTIONS_MISC ) +, maOptionsMisc ( false, false ) +{ +} + +SdOptionsMiscItem::SdOptionsMiscItem( SdOptions const * pOpts, ::sd::FrameView const * pView ) +: SfxPoolItem ( ATTR_OPTIONS_MISC ) +, maOptionsMisc ( false, false ) +{ + if( pOpts ) + { + maOptionsMisc.SetStartWithTemplate( pOpts->IsStartWithTemplate() ); + maOptionsMisc.SetEnableSdremote( pOpts->IsEnableSdremote() ); + maOptionsMisc.SetEnablePresenterScreen( pOpts->IsEnablePresenterScreen() ); + maOptionsMisc.SetSummationOfParagraphs( pOpts->IsSummationOfParagraphs() ); + maOptionsMisc.SetTabBarVisible( pOpts->IsTabBarVisible() ); + maOptionsMisc.SetShowUndoDeleteWarning( pOpts->IsShowUndoDeleteWarning() ); + maOptionsMisc.SetPrinterIndependentLayout( pOpts->GetPrinterIndependentLayout() ); + maOptionsMisc.SetDefaultObjectSizeWidth( pOpts->GetDefaultObjectSizeWidth() ); + maOptionsMisc.SetDefaultObjectSizeHeight( pOpts->GetDefaultObjectSizeHeight() ); + + maOptionsMisc.SetPreviewNewEffects(pOpts->IsPreviewNewEffects()); + maOptionsMisc.SetPreviewChangedEffects(pOpts->IsPreviewChangedEffects()); + maOptionsMisc.SetPreviewTransitions(pOpts->IsPreviewTransitions()); + + maOptionsMisc.SetDisplay(pOpts->GetDisplay()); + maOptionsMisc.SetShowComments( pOpts->IsShowComments() ); + + maOptionsMisc.SetPresentationPenColor(pOpts->GetPresentationPenColor() ); + maOptionsMisc.SetPresentationPenWidth(pOpts->GetPresentationPenWidth() ); + } + + if( pView ) + { + maOptionsMisc.SetMarkedHitMovesAlways( pView->IsMarkedHitMovesAlways() ); + maOptionsMisc.SetMoveOnlyDragging( pView->IsMoveOnlyDragging() ); + maOptionsMisc.SetCrookNoContortion( pView->IsCrookNoContortion() ); + maOptionsMisc.SetQuickEdit( pView->IsQuickEdit() ); + + // #i26631# + maOptionsMisc.SetMasterPagePaintCaching( pView->IsMasterPagePaintCaching() ); + + maOptionsMisc.SetDragWithCopy( pView->IsDragWithCopy() ); + maOptionsMisc.SetPickThrough( pView->GetModel()->IsPickThroughTransparentTextFrames() ); + maOptionsMisc.SetDoubleClickTextEdit( pView->IsDoubleClickTextEdit() ); + maOptionsMisc.SetClickChangeRotation( pView->IsClickChangeRotation() ); + maOptionsMisc.SetSolidDragging( pView->IsSolidDragging() ); + } + else if( pOpts ) + { + maOptionsMisc.SetMarkedHitMovesAlways( pOpts->IsMarkedHitMovesAlways() ); + maOptionsMisc.SetMoveOnlyDragging( pOpts->IsMoveOnlyDragging() ); + maOptionsMisc.SetCrookNoContortion( pOpts->IsCrookNoContortion() ); + maOptionsMisc.SetQuickEdit( pOpts->IsQuickEdit() ); + maOptionsMisc.SetMasterPagePaintCaching( pOpts->IsMasterPagePaintCaching() ); + maOptionsMisc.SetDragWithCopy( pOpts->IsDragWithCopy() ); + maOptionsMisc.SetPickThrough( pOpts->IsPickThrough() ); + maOptionsMisc.SetDoubleClickTextEdit( pOpts->IsDoubleClickTextEdit() ); + maOptionsMisc.SetClickChangeRotation( pOpts->IsClickChangeRotation() ); + maOptionsMisc.SetSolidDragging( pOpts->IsSolidDragging() ); + } +} + +SdOptionsMiscItem* SdOptionsMiscItem::Clone( SfxItemPool* ) const +{ + return new SdOptionsMiscItem( *this ); +} + +bool SdOptionsMiscItem::operator==( const SfxPoolItem& rAttr ) const +{ + assert(SfxPoolItem::operator==(rAttr)); + return maOptionsMisc == static_cast<const SdOptionsMiscItem&>(rAttr).maOptionsMisc; +} + +void SdOptionsMiscItem::SetOptions( SdOptions* pOpts ) const +{ + if( !pOpts ) + return; + + pOpts->SetStartWithTemplate( maOptionsMisc.IsStartWithTemplate() ); + pOpts->SetMarkedHitMovesAlways( maOptionsMisc.IsMarkedHitMovesAlways() ); + pOpts->SetMoveOnlyDragging( maOptionsMisc.IsMoveOnlyDragging() ); + pOpts->SetCrookNoContortion( maOptionsMisc.IsCrookNoContortion() ); + pOpts->SetQuickEdit( maOptionsMisc.IsQuickEdit() ); + pOpts->SetMasterPagePaintCaching( maOptionsMisc.IsMasterPagePaintCaching() ); + pOpts->SetDragWithCopy( maOptionsMisc.IsDragWithCopy() ); + pOpts->SetPickThrough( maOptionsMisc.IsPickThrough() ); + pOpts->SetDoubleClickTextEdit( maOptionsMisc.IsDoubleClickTextEdit() ); + pOpts->SetClickChangeRotation( maOptionsMisc.IsClickChangeRotation() ); + pOpts->SetEnableSdremote( maOptionsMisc.IsEnableSdremote() ); + pOpts->SetEnablePresenterScreen( maOptionsMisc.IsEnablePresenterScreen() ); + pOpts->SetSummationOfParagraphs( maOptionsMisc.IsSummationOfParagraphs() ); + pOpts->SetTabBarVisible( maOptionsMisc.IsTabBarVisible() ); + + pOpts->SetSolidDragging( maOptionsMisc.IsSolidDragging() ); + pOpts->SetShowUndoDeleteWarning( maOptionsMisc.IsShowUndoDeleteWarning() ); + pOpts->SetPrinterIndependentLayout( maOptionsMisc.GetPrinterIndependentLayout() ); + pOpts->SetShowComments( maOptionsMisc.IsShowComments() ); + pOpts->SetDefaultObjectSizeWidth( maOptionsMisc.GetDefaultObjectSizeWidth() ); + pOpts->SetDefaultObjectSizeHeight( maOptionsMisc.GetDefaultObjectSizeHeight() ); + + pOpts->SetPreviewNewEffects( maOptionsMisc.IsPreviewNewEffects() ); + pOpts->SetPreviewChangedEffects( maOptionsMisc.IsPreviewChangedEffects() ); + pOpts->SetPreviewTransitions( maOptionsMisc.IsPreviewTransitions() ); + + pOpts->SetDisplay( maOptionsMisc.GetDisplay() ); + + pOpts->SetPresentationPenColor( maOptionsMisc.GetPresentationPenColor() ); + pOpts->SetPresentationPenWidth( maOptionsMisc.GetPresentationPenWidth() ); +} + +/************************************************************************* +|* +|* SdOptionsSnap +|* +\************************************************************************/ + +SdOptionsSnap::SdOptionsSnap( bool bImpress, bool bUseConfig ) : + SdOptionsGeneric( bImpress, bUseConfig ? + ( bImpress ? + OUString( "Office.Impress/Snap" ) : + OUString( "Office.Draw/Snap" ) ) : + OUString() ), + bSnapHelplines( true ), + bSnapBorder( true ), + bSnapFrame( false ), + bSnapPoints( false ), + bOrtho( false ), + bBigOrtho( true ), + bRotate( false ), + nSnapArea( 5 ), + nAngle( 1500 ), + nBezAngle( 1500 ) + +{ + EnableModify( true ); +} + +bool SdOptionsSnap::operator==( const SdOptionsSnap& rOpt ) const +{ + return( IsSnapHelplines() == rOpt.IsSnapHelplines() && + IsSnapBorder() == rOpt.IsSnapBorder() && + IsSnapFrame() == rOpt.IsSnapFrame() && + IsSnapPoints() == rOpt.IsSnapPoints() && + IsOrtho() == rOpt.IsOrtho() && + IsBigOrtho() == rOpt.IsBigOrtho() && + IsRotate() == rOpt.IsRotate() && + GetSnapArea() == rOpt.GetSnapArea() && + GetAngle() == rOpt.GetAngle() && + GetEliminatePolyPointLimitAngle() == rOpt.GetEliminatePolyPointLimitAngle() ); +} + +void SdOptionsSnap::GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const +{ + static const char* aPropNames[] = + { + "Object/SnapLine", + "Object/PageMargin", + "Object/ObjectFrame", + "Object/ObjectPoint", + "Position/CreatingMoving", + "Position/ExtendEdges", + "Position/Rotating", + "Object/Range", + "Position/RotatingValue", + "Position/PointReduction" + }; + + rCount = SAL_N_ELEMENTS(aPropNames); + ppNames = aPropNames; +} + +bool SdOptionsSnap::ReadData( const Any* pValues ) +{ + if( pValues[0].hasValue() ) SetSnapHelplines( *o3tl::doAccess<bool>(pValues[ 0 ]) ); + if( pValues[1].hasValue() ) SetSnapBorder( *o3tl::doAccess<bool>(pValues[ 1 ]) ); + if( pValues[2].hasValue() ) SetSnapFrame( *o3tl::doAccess<bool>(pValues[ 2 ]) ); + if( pValues[3].hasValue() ) SetSnapPoints( *o3tl::doAccess<bool>(pValues[ 3 ]) ); + if( pValues[4].hasValue() ) SetOrtho( *o3tl::doAccess<bool>(pValues[ 4 ]) ); + if( pValues[5].hasValue() ) SetBigOrtho( *o3tl::doAccess<bool>(pValues[ 5 ]) ); + if( pValues[6].hasValue() ) SetRotate( *o3tl::doAccess<bool>(pValues[ 6 ]) ); + if( pValues[7].hasValue() ) SetSnapArea( static_cast<sal_Int16>(*o3tl::doAccess<sal_Int32>(pValues[ 7 ])) ); + if( pValues[8].hasValue() ) SetAngle( Degree100(*o3tl::doAccess<sal_Int32>(pValues[ 8 ])) ); + if( pValues[9].hasValue() ) SetEliminatePolyPointLimitAngle( Degree100(*o3tl::doAccess<sal_Int32>(pValues[ 9 ])) ); + + return true; +} + +bool SdOptionsSnap::WriteData( Any* pValues ) const +{ + pValues[ 0 ] <<= IsSnapHelplines(); + pValues[ 1 ] <<= IsSnapBorder(); + pValues[ 2 ] <<= IsSnapFrame(); + pValues[ 3 ] <<= IsSnapPoints(); + pValues[ 4 ] <<= IsOrtho(); + pValues[ 5 ] <<= IsBigOrtho(); + pValues[ 6 ] <<= IsRotate(); + pValues[ 7 ] <<= static_cast<sal_Int32>(GetSnapArea()); + pValues[ 8 ] <<= static_cast<sal_Int32>(GetAngle().get()); + pValues[ 9 ] <<= static_cast<sal_Int32>(GetEliminatePolyPointLimitAngle().get()); + + return true; +} + +/************************************************************************* +|* +|* SdOptionsSnapItem +|* +\************************************************************************/ + +SdOptionsSnapItem::SdOptionsSnapItem() +: SfxPoolItem ( ATTR_OPTIONS_SNAP ) +, maOptionsSnap ( false, false ) +{ +} + +SdOptionsSnapItem::SdOptionsSnapItem( SdOptions const * pOpts, ::sd::FrameView const * pView ) +: SfxPoolItem ( ATTR_OPTIONS_SNAP ) +, maOptionsSnap ( false, false ) +{ + if( pView ) + { + maOptionsSnap.SetSnapHelplines( pView->IsHlplSnap() ); + maOptionsSnap.SetSnapBorder( pView->IsBordSnap() ); + maOptionsSnap.SetSnapFrame( pView->IsOFrmSnap() ); + maOptionsSnap.SetSnapPoints( pView->IsOPntSnap() ); + maOptionsSnap.SetOrtho( pView->IsOrtho() ); + maOptionsSnap.SetBigOrtho( pView->IsBigOrtho() ); + maOptionsSnap.SetRotate( pView->IsAngleSnapEnabled() ); + maOptionsSnap.SetSnapArea( pView->GetSnapMagneticPixel() ); + maOptionsSnap.SetAngle( pView->GetSnapAngle() ); + maOptionsSnap.SetEliminatePolyPointLimitAngle( pView->GetEliminatePolyPointLimitAngle() ); + } + else if( pOpts ) + { + maOptionsSnap.SetSnapHelplines( pOpts->IsSnapHelplines() ); + maOptionsSnap.SetSnapBorder( pOpts->IsSnapBorder() ); + maOptionsSnap.SetSnapFrame( pOpts->IsSnapFrame() ); + maOptionsSnap.SetSnapPoints( pOpts->IsSnapPoints() ); + maOptionsSnap.SetOrtho( pOpts->IsOrtho() ); + maOptionsSnap.SetBigOrtho( pOpts->IsBigOrtho() ); + maOptionsSnap.SetRotate( pOpts->IsRotate() ); + maOptionsSnap.SetSnapArea( pOpts->GetSnapArea() ); + maOptionsSnap.SetAngle( pOpts->GetAngle() ); + maOptionsSnap.SetEliminatePolyPointLimitAngle( pOpts->GetEliminatePolyPointLimitAngle() ); + } +} + +SdOptionsSnapItem* SdOptionsSnapItem::Clone( SfxItemPool* ) const +{ + return new SdOptionsSnapItem( *this ); +} + +bool SdOptionsSnapItem::operator==( const SfxPoolItem& rAttr ) const +{ + assert(SfxPoolItem::operator==(rAttr)); + return maOptionsSnap == static_cast<const SdOptionsSnapItem&>(rAttr).maOptionsSnap; +} + +void SdOptionsSnapItem::SetOptions( SdOptions* pOpts ) const +{ + if( !pOpts ) + return; + + pOpts->SetSnapHelplines( maOptionsSnap.IsSnapHelplines() ); + pOpts->SetSnapBorder( maOptionsSnap.IsSnapBorder() ); + pOpts->SetSnapFrame( maOptionsSnap.IsSnapFrame() ); + pOpts->SetSnapPoints( maOptionsSnap.IsSnapPoints() ); + pOpts->SetOrtho( maOptionsSnap.IsOrtho() ); + pOpts->SetBigOrtho( maOptionsSnap.IsBigOrtho() ); + pOpts->SetRotate( maOptionsSnap.IsRotate() ); + pOpts->SetSnapArea( maOptionsSnap.GetSnapArea() ); + pOpts->SetAngle( maOptionsSnap.GetAngle() ); + pOpts->SetEliminatePolyPointLimitAngle( maOptionsSnap.GetEliminatePolyPointLimitAngle() ); +} + +/************************************************************************* +|* +|* SdOptionsZoom +|* +\************************************************************************/ + +SdOptionsZoom::SdOptionsZoom( bool bImpress ) : + SdOptionsGeneric( bImpress, bImpress ? + OUString() : + OUString("Office.Draw/Zoom") ), + nX( 1 ), + nY( 1 ) + +{ + EnableModify( true ); +} + +void SdOptionsZoom::GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const +{ + static const char* aPropNames[] = + { + "ScaleX", + "ScaleY" + }; + + rCount = !IsImpress() ? SAL_N_ELEMENTS(aPropNames) : 0; + ppNames = aPropNames; +} + +bool SdOptionsZoom::ReadData( const Any* pValues ) +{ + sal_Int32 x = 1, y = 1; + + if( pValues[0].hasValue() ) x = *o3tl::doAccess<sal_Int32>(pValues[ 0 ]); + if( pValues[1].hasValue() ) y = *o3tl::doAccess<sal_Int32>(pValues[ 1 ]); + + SetScale( x, y ); + + return true; +} + +bool SdOptionsZoom::WriteData( Any* pValues ) const +{ + sal_Int32 x, y; + + GetScale( x, y ); + + pValues[ 0 ] <<= x; + pValues[ 1 ] <<= y; + + return true; +} + +/************************************************************************* +|* +|* SdOptionsGrid +|* +\************************************************************************/ + +SdOptionsGrid::SdOptionsGrid(bool bImpress) : + SdOptionsGeneric( bImpress, + bImpress ? + OUString( "Office.Impress/Grid" ) : + OUString( "Office.Draw/Grid" ) + ) +{ + EnableModify( false ); + SetDefaults(); + EnableModify( true ); +} + +SdOptionsGrid::~SdOptionsGrid() +{ +} + +void SdOptionsGrid::SetDefaults() +{ + const sal_uInt32 nVal = 1000; + + SetFieldDivisionX( nVal ); + SetFieldDivisionY( nVal ); + SetFieldDrawX( nVal ); + SetFieldDrawY( nVal ); + SetFieldSnapX( nVal ); + SetFieldSnapY( nVal ); + SetUseGridSnap( false ); + SetSynchronize( true ); + SetGridVisible( false ); + SetEqualGrid( true ); +} + +void SdOptionsGrid::GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const +{ + if( isMetricSystem() ) + { + static const char* aPropNamesMetric[] = + { + "Resolution/XAxis/Metric", + "Resolution/YAxis/Metric", + "Subdivision/XAxis", + "Subdivision/YAxis", + "SnapGrid/XAxis/Metric", + "SnapGrid/YAxis/Metric", + "Option/SnapToGrid", + "Option/Synchronize", + "Option/VisibleGrid", + "SnapGrid/Size" + }; + ppNames = aPropNamesMetric; + rCount = SAL_N_ELEMENTS(aPropNamesMetric); + } + else + { + static const char* aPropNamesNonMetric[] = + { + "Resolution/XAxis/NonMetric", + "Resolution/YAxis/NonMetric", + "Subdivision/XAxis", + "Subdivision/YAxis", + "SnapGrid/XAxis/NonMetric", + "SnapGrid/YAxis/NonMetric", + "Option/SnapToGrid", + "Option/Synchronize", + "Option/VisibleGrid", + "SnapGrid/Size" + }; + ppNames = aPropNamesNonMetric; + rCount = SAL_N_ELEMENTS(aPropNamesNonMetric); + } +} + +bool SdOptionsGrid::ReadData( const Any* pValues ) +{ + if( pValues[0].hasValue() ) SetFieldDrawX( *o3tl::doAccess<sal_Int32>(pValues[ 0 ]) ); + if( pValues[1].hasValue() ) SetFieldDrawY( *o3tl::doAccess<sal_Int32>(pValues[ 1 ]) ); + + if( pValues[2].hasValue() ) + { + const sal_uInt32 nDivX = FRound( *o3tl::doAccess<double>(pValues[ 2 ]) ); + SetFieldDivisionX( SvxOptionsGrid::GetFieldDrawX() / ( nDivX + 1 ) ); + } + + if( pValues[3].hasValue() ) + { + const sal_uInt32 nDivY = FRound( *o3tl::doAccess<double>(pValues[ 3 ]) ); + SetFieldDivisionY( SvxOptionsGrid::GetFieldDrawY() / ( nDivY + 1 ) ); + } + + if( pValues[4].hasValue() ) SetFieldSnapX( *o3tl::doAccess<sal_Int32>(pValues[ 4 ]) ); + if( pValues[5].hasValue() ) SetFieldSnapY( *o3tl::doAccess<sal_Int32>(pValues[ 5 ]) ); + if( pValues[6].hasValue() ) SetUseGridSnap( *o3tl::doAccess<bool>(pValues[ 6 ]) ); + if( pValues[7].hasValue() ) SetSynchronize( *o3tl::doAccess<bool>(pValues[ 7 ]) ); + if( pValues[8].hasValue() ) SetGridVisible( *o3tl::doAccess<bool>(pValues[ 8 ]) ); + if( pValues[9].hasValue() ) SetEqualGrid( *o3tl::doAccess<bool>(pValues[ 9 ]) ); + + return true; +} + +bool SdOptionsGrid::WriteData( Any* pValues ) const +{ + pValues[ 0 ] <<= static_cast<sal_Int32>(GetFieldDrawX()); + pValues[ 1 ] <<= static_cast<sal_Int32>(GetFieldDrawY()); + pValues[ 2 ] <<= ( GetFieldDivisionX() ? ( static_cast<double>(GetFieldDrawX()) / GetFieldDivisionX() - 1.0 ) : double(0) ); + pValues[ 3 ] <<= ( GetFieldDivisionY() ? ( static_cast<double>(GetFieldDrawY()) / GetFieldDivisionY() - 1.0 ) : double(0) ); + pValues[ 4 ] <<= static_cast<sal_Int32>(GetFieldSnapX()); + pValues[ 5 ] <<= static_cast<sal_Int32>(GetFieldSnapY()); + pValues[ 6 ] <<= IsUseGridSnap(); + pValues[ 7 ] <<= IsSynchronize(); + pValues[ 8 ] <<= IsGridVisible(); + pValues[ 9 ] <<= IsEqualGrid(); + + return true; +} + +/************************************************************************* +|* +|* SdOptionsGridItem +|* +\************************************************************************/ + +SdOptionsGridItem::SdOptionsGridItem( SdOptions const * pOpts ) : + SvxGridItem( SID_ATTR_GRID_OPTIONS ) +{ + SetSynchronize( pOpts->IsSynchronize() ); + SetEqualGrid( pOpts->IsEqualGrid() ); + + SetFieldDrawX( pOpts->GetFieldDrawX() ); + SetFieldDrawY( pOpts->GetFieldDrawY() ); + SetFieldDivisionX( pOpts->GetFieldDivisionX() ? ( pOpts->GetFieldDrawX() / pOpts->GetFieldDivisionX() - 1 ) : 0 ); + SetFieldDivisionY( pOpts->GetFieldDivisionY() ? ( pOpts->GetFieldDrawY() / pOpts->GetFieldDivisionY() - 1 ) : 0 ); + SetFieldSnapX( pOpts->GetFieldSnapX() ); + SetFieldSnapY( pOpts->GetFieldSnapY() ); + SetUseGridSnap( pOpts->IsUseGridSnap() ); + SetGridVisible( pOpts->IsGridVisible() ); +} + +void SdOptionsGridItem::SetOptions( SdOptions* pOpts ) const +{ + pOpts->SetFieldDrawX( GetFieldDrawX() ); + pOpts->SetFieldDivisionX( GetFieldDrawX() / ( GetFieldDivisionX() + 1 ) ); + pOpts->SetFieldDrawY( GetFieldDrawY() ); + pOpts->SetFieldDivisionY( GetFieldDrawY() / ( GetFieldDivisionY() + 1 ) ); + pOpts->SetFieldSnapX( GetFieldSnapX() ); + pOpts->SetFieldSnapY( GetFieldSnapY() ); + pOpts->SetUseGridSnap( GetUseGridSnap() ); + pOpts->SetSynchronize( GetSynchronize() ); + pOpts->SetGridVisible( GetGridVisible() ); + pOpts->SetEqualGrid( GetEqualGrid() ); +} + +/************************************************************************* +|* +|* SdOptionsPrint +|* +\************************************************************************/ + +SdOptionsPrint::SdOptionsPrint( bool bImpress, bool bUseConfig ) : + SdOptionsGeneric( bImpress, bUseConfig ? + ( bImpress ? + OUString( "Office.Impress/Print" ) : + OUString( "Office.Draw/Print" ) ) : + OUString() ), + bDraw( true ), + bNotes( false ), + bHandout( false ), + bOutline( false ), + bDate( false ), + bTime( false ), + bPagename( false ), + bHiddenPages( true ), + bPagesize( false ), + bPagetile( false ), + bWarningPrinter( true ), + bWarningSize( false ), + bWarningOrientation( false ), + bBooklet( false ), + bFront( true ), + bBack( true ), + bCutPage( false ), + bPaperbin( false ), + mbHandoutHorizontal( true ), + mnHandoutPages( 6 ), + nQuality( 0 ) +{ + EnableModify( true ); +} + +bool SdOptionsPrint::operator==( const SdOptionsPrint& rOpt ) const +{ + return( IsDraw() == rOpt.IsDraw() && + IsNotes() == rOpt.IsNotes() && + IsHandout() == rOpt.IsHandout() && + IsOutline() == rOpt.IsOutline() && + IsDate() == rOpt.IsDate() && + IsTime() == rOpt.IsTime() && + IsPagename() == rOpt.IsPagename() && + IsHiddenPages() == rOpt.IsHiddenPages() && + IsPagesize() == rOpt.IsPagesize() && + IsPagetile() == rOpt.IsPagetile() && + IsWarningPrinter() == rOpt.IsWarningPrinter() && + IsWarningSize() == rOpt.IsWarningSize() && + IsWarningOrientation() == rOpt.IsWarningOrientation() && + IsBooklet() == rOpt.IsBooklet() && + IsFrontPage() == rOpt.IsFrontPage() && + IsBackPage() == rOpt.IsBackPage() && + IsCutPage() == rOpt.IsCutPage() && + IsPaperbin() == rOpt.IsPaperbin() && + GetOutputQuality() == rOpt.GetOutputQuality() && + IsHandoutHorizontal() == rOpt.IsHandoutHorizontal() && + GetHandoutPages() == rOpt.GetHandoutPages() ); +} + +void SdOptionsPrint::GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const +{ + if (IsImpress()) + { + static const char* aImpressPropNames[] = + { + "Other/Date", + "Other/Time", + "Other/PageName", + "Other/HiddenPage", + "Page/PageSize", + "Page/PageTile", + // bWarningPrinter + // bWarningSize + // bWarningOrientation + "Page/Booklet", + "Page/BookletFront", + "Page/BookletBack", + // bCutPage + "Other/FromPrinterSetup", + "Other/Quality", + "Content/Presentation", + "Content/Note", + "Content/Handout", + "Content/Outline", + "Other/HandoutHorizontal", + "Other/PagesPerHandout" + }; + rCount = SAL_N_ELEMENTS(aImpressPropNames); + ppNames = aImpressPropNames; + } + else + { + static const char* aDrawPropNames[] = + { + "Other/Date", + "Other/Time", + "Other/PageName", + "Other/HiddenPage", + "Page/PageSize", + "Page/PageTile", + // bWarningPrinter + // bWarningSize + // bWarningOrientation + "Page/Booklet", + "Page/BookletFront", + "Page/BookletBack", + // bCutPage + "Other/FromPrinterSetup", + "Other/Quality", + "Content/Drawing", + }; + rCount = SAL_N_ELEMENTS(aDrawPropNames); + ppNames = aDrawPropNames; + } +} + +bool SdOptionsPrint::ReadData( const Any* pValues ) +{ + if( pValues[0].hasValue() ) SetDate( *o3tl::doAccess<bool>(pValues[ 0 ]) ); + if( pValues[1].hasValue() ) SetTime( *o3tl::doAccess<bool>(pValues[ 1 ]) ); + if( pValues[2].hasValue() ) SetPagename( *o3tl::doAccess<bool>(pValues[ 2 ]) ); + if( pValues[3].hasValue() ) SetHiddenPages( *o3tl::doAccess<bool>(pValues[ 3 ]) ); + if( pValues[4].hasValue() ) SetPagesize( *o3tl::doAccess<bool>(pValues[ 4 ]) ); + if( pValues[5].hasValue() ) SetPagetile( *o3tl::doAccess<bool>(pValues[ 5 ]) ); + if( pValues[6].hasValue() ) SetBooklet( *o3tl::doAccess<bool>(pValues[ 6 ]) ); + if( pValues[7].hasValue() ) SetFrontPage( *o3tl::doAccess<bool>(pValues[ 7 ]) ); + if( pValues[8].hasValue() ) SetBackPage( *o3tl::doAccess<bool>(pValues[ 8 ]) ); + if( pValues[9].hasValue() ) SetPaperbin( *o3tl::doAccess<bool>(pValues[ 9 ]) ); + if( pValues[10].hasValue() ) SetOutputQuality( static_cast<sal_uInt16>(*o3tl::doAccess<sal_Int32>(pValues[ 10 ])) ); + if( pValues[11].hasValue() ) SetDraw( *o3tl::doAccess<bool>(pValues[ 11 ]) ); + + // just for impress + if (IsImpress()) + { + if( pValues[12].hasValue() ) SetNotes( *o3tl::doAccess<bool>(pValues[ 12 ]) ); + if( pValues[13].hasValue() ) SetHandout( *o3tl::doAccess<bool>(pValues[ 13 ]) ); + if( pValues[14].hasValue() ) SetOutline( *o3tl::doAccess<bool>(pValues[ 14 ]) ); + if( pValues[15].hasValue() ) SetHandoutHorizontal( *o3tl::doAccess<bool>(pValues[15]) ); + if( pValues[16].hasValue() ) SetHandoutPages( static_cast<sal_uInt16>(*o3tl::doAccess<sal_Int32>(pValues[16])) ); + } + + return true; +} + +bool SdOptionsPrint::WriteData( Any* pValues ) const +{ + pValues[ 0 ] <<= IsDate(); + pValues[ 1 ] <<= IsTime(); + pValues[ 2 ] <<= IsPagename(); + pValues[ 3 ] <<= IsHiddenPages(); + pValues[ 4 ] <<= IsPagesize(); + pValues[ 5 ] <<= IsPagetile(); + pValues[ 6 ] <<= IsBooklet(); + pValues[ 7 ] <<= IsFrontPage(); + pValues[ 8 ] <<= IsBackPage(); + pValues[ 9 ] <<= IsPaperbin(); + pValues[ 10 ] <<= static_cast<sal_Int32>(GetOutputQuality()); + pValues[ 11 ] <<= IsDraw(); + + // just for impress + if (IsImpress()) + { + pValues[ 12 ] <<= IsNotes(); + pValues[ 13 ] <<= IsHandout(); + pValues[ 14 ] <<= IsOutline(); + pValues[ 15 ] <<= IsHandoutHorizontal(); + pValues[ 16 ] <<= GetHandoutPages(); + } + + return true; +} + +/************************************************************************* +|* +|* SdOptionsPrintItem +|* +\************************************************************************/ + +SdOptionsPrintItem::SdOptionsPrintItem() +: SfxPoolItem ( ATTR_OPTIONS_PRINT ) +, maOptionsPrint ( false, false ) +{ +} + +SdOptionsPrintItem::SdOptionsPrintItem( SdOptions const * pOpts ) +: SfxPoolItem ( ATTR_OPTIONS_PRINT ) +, maOptionsPrint ( false, false ) +{ + if( !pOpts ) + return; + + maOptionsPrint.SetDraw( pOpts->IsDraw() ); + maOptionsPrint.SetNotes( pOpts->IsNotes() ); + maOptionsPrint.SetHandout( pOpts->IsHandout() ); + maOptionsPrint.SetOutline( pOpts->IsOutline() ); + maOptionsPrint.SetDate( pOpts->IsDate() ); + maOptionsPrint.SetTime( pOpts->IsTime() ); + maOptionsPrint.SetPagename( pOpts->IsPagename() ); + maOptionsPrint.SetHiddenPages( pOpts->IsHiddenPages() ); + maOptionsPrint.SetPagesize( pOpts->IsPagesize() ); + maOptionsPrint.SetPagetile( pOpts->IsPagetile() ); + maOptionsPrint.SetWarningPrinter( pOpts->IsWarningPrinter() ); + maOptionsPrint.SetWarningSize( pOpts->IsWarningSize() ); + maOptionsPrint.SetWarningOrientation( pOpts->IsWarningOrientation() ); + maOptionsPrint.SetBooklet( pOpts->IsBooklet() ); + maOptionsPrint.SetFrontPage( pOpts->IsFrontPage() ); + maOptionsPrint.SetBackPage( pOpts->IsBackPage() ); + maOptionsPrint.SetCutPage( pOpts->IsCutPage() ); + maOptionsPrint.SetPaperbin( pOpts->IsPaperbin() ); + maOptionsPrint.SetOutputQuality( pOpts->GetOutputQuality() ); +} + +SdOptionsPrintItem* SdOptionsPrintItem::Clone( SfxItemPool* ) const +{ + return new SdOptionsPrintItem( *this ); +} + +bool SdOptionsPrintItem::operator==( const SfxPoolItem& rAttr ) const +{ + assert(SfxPoolItem::operator==(rAttr)); + return maOptionsPrint == static_cast<const SdOptionsPrintItem&>(rAttr).maOptionsPrint; +} + +void SdOptionsPrintItem::SetOptions( SdOptions* pOpts ) const +{ + if( !pOpts ) + return; + + pOpts->SetDraw( maOptionsPrint.IsDraw() ); + pOpts->SetNotes( maOptionsPrint.IsNotes() ); + pOpts->SetHandout( maOptionsPrint.IsHandout() ); + pOpts->SetOutline( maOptionsPrint.IsOutline() ); + pOpts->SetDate( maOptionsPrint.IsDate() ); + pOpts->SetTime( maOptionsPrint.IsTime() ); + pOpts->SetPagename( maOptionsPrint.IsPagename() ); + pOpts->SetHiddenPages( maOptionsPrint.IsHiddenPages() ); + pOpts->SetPagesize( maOptionsPrint.IsPagesize() ); + pOpts->SetPagetile( maOptionsPrint.IsPagetile() ); + pOpts->SetWarningPrinter( maOptionsPrint.IsWarningPrinter() ); + pOpts->SetWarningSize( maOptionsPrint.IsWarningSize() ); + pOpts->SetWarningOrientation( maOptionsPrint.IsWarningOrientation() ); + pOpts->SetBooklet( maOptionsPrint.IsBooklet() ); + pOpts->SetFrontPage( maOptionsPrint.IsFrontPage() ); + pOpts->SetBackPage( maOptionsPrint.IsBackPage() ); + pOpts->SetCutPage( maOptionsPrint.IsCutPage() ); + pOpts->SetPaperbin( maOptionsPrint.IsPaperbin() ); + pOpts->SetOutputQuality( maOptionsPrint.GetOutputQuality() ); +} + +/************************************************************************* +|* +|* SdOptions +|* +\************************************************************************/ + +SdOptions::SdOptions(bool bImpress) : + SdOptionsLayout( bImpress, true ), + SdOptionsContents( bImpress ), + SdOptionsMisc( bImpress, true ), + SdOptionsSnap( bImpress, true ), + SdOptionsZoom( bImpress ), + SdOptionsGrid( bImpress ), + SdOptionsPrint( bImpress, true ) +{ +} + +SdOptions::~SdOptions() +{ +} + +void SdOptions::StoreConfig() +{ + SdOptionsLayout::Store(); + SdOptionsContents::Store(); + SdOptionsMisc::Store(); + SdOptionsSnap::Store(); + SdOptionsZoom::Store(); + SdOptionsGrid::Store(); + SdOptionsPrint::Store(); +} + +sal_Int32 SdOptionsMisc::GetDisplay() const +{ + Init(); + return mnDisplay; +} + +void SdOptionsMisc::SetDisplay( sal_Int32 nDisplay ) +{ + if( mnDisplay != nDisplay ) + { + OptionsChanged(); + mnDisplay = nDisplay; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/app/scalectrl.cxx b/sd/source/ui/app/scalectrl.cxx new file mode 100644 index 000000000..0444163b5 --- /dev/null +++ b/sd/source/ui/app/scalectrl.cxx @@ -0,0 +1,108 @@ +/* -*- 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 <scalectrl.hxx> + +#include <vcl/commandevent.hxx> +#include <vcl/status.hxx> +#include <vcl/weldutils.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/stritem.hxx> +#include <sfx2/sfxsids.hrc> + +#include <ViewShellBase.hxx> +#include <drawdoc.hxx> +#include <app.hrc> +#include <sdresid.hxx> +#include <strings.hrc> + +SFX_IMPL_STATUSBAR_CONTROL(SdScaleControl, SfxStringItem); + +// class SdScaleControl ------------------------------------------ +SdScaleControl::SdScaleControl(sal_uInt16 _nSlotId, sal_uInt16 _nId, StatusBar& rStb) + : SfxStatusBarControl(_nSlotId, _nId, rStb) +{ + GetStatusBar().SetQuickHelpText(GetId(), SdResId(STR_SCALE_TOOLTIP)); +} + +SdScaleControl::~SdScaleControl() {} + +void SdScaleControl::StateChangedAtStatusBarControl(sal_uInt16 /*nSID*/, SfxItemState eState, + const SfxPoolItem* pState) +{ + if (eState != SfxItemState::DEFAULT || pState->IsVoidItem()) + return; + auto pStringItem = dynamic_cast<const SfxStringItem*>(pState); + GetStatusBar().SetItemText(GetId(), pStringItem->GetValue()); +} + +void SdScaleControl::Command(const CommandEvent& rCEvt) +{ + if (rCEvt.GetCommand() != CommandEventId::ContextMenu + || GetStatusBar().GetItemText(GetId()).isEmpty()) + return; + + SfxViewFrame* pViewFrame = SfxViewFrame::Current(); + + sd::ViewShellBase* pViewShellBase = sd::ViewShellBase::GetViewShellBase(pViewFrame); + if (!pViewShellBase) + return; + + SdDrawDocument* pDoc = pViewShellBase->GetDocument(); + if (!pDoc) + return; + + std::unique_ptr<weld::Builder> xBuilder( + Application::CreateBuilder(nullptr, "modules/simpress/ui/masterpagemenu.ui")); + std::unique_ptr<weld::Menu> xPopup(xBuilder->weld_menu("menu")); + + sal_uInt16 aTable[12] = { 1, 2, 4, 5, 8, 10, 16, 20, 30, 40, 50, 100 }; + + for (sal_uInt16 i = 11; i > 0; i--) + xPopup->append(OUString::number(12 - i), OUString::number(aTable[i]) + ":1"); + for (sal_uInt16 i = 0; i < 12; i++) + xPopup->append(OUString::number(12 + i), "1:" + OUString::number(aTable[i])); + + ::tools::Rectangle aRect(rCEvt.GetMousePosPixel(), Size(1, 1)); + weld::Window* pParent = weld::GetPopupParent(GetStatusBar(), aRect); + OString sResult = xPopup->popup_at_rect(pParent, aRect); + if (sResult.isEmpty()) + return; + + sal_Int32 i = sResult.toUInt32(); + sal_Int32 nX; + sal_Int32 nY; + if (i > 11) + nX = 1; + else + nX = aTable[(12 - i) % 12]; + if (i > 11) + nY = aTable[i % 12]; + else + nY = 1; + pDoc->SetUIScale(Fraction(nX, nY)); + + SfxBindings& pBindings = pViewFrame->GetBindings(); + pBindings.Invalidate(SID_SCALE); //update statusbar + pBindings.Invalidate(SID_ATTR_METRIC); //update sidebar + pViewShellBase->UpdateBorder(true); // update ruler +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/app/sddll.cxx b/sd/source/ui/app/sddll.cxx new file mode 100644 index 000000000..4e20d0997 --- /dev/null +++ b/sd/source/ui/app/sddll.cxx @@ -0,0 +1,269 @@ +/* -*- 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 <config_features.h> + +#include <avmedia/mediaplayer.hxx> +#include <avmedia/mediatoolbox.hxx> +#include <unotools/configmgr.hxx> +#include <unotools/moduleoptions.hxx> +#include <svx/fmobjfac.hxx> +#include <svx/objfac3d.hxx> +#include <vcl/svapp.hxx> + +#include <registerinterfaces.hxx> +#include <sddll.hxx> +#include <app.hrc> +#include <AnimationChildWindow.hxx> +#include <BezierObjectBar.hxx> +#include <diactrl.hxx> +#include <DrawDocShell.hxx> +#include <FactoryIds.hxx> +#include <gluectrl.hxx> +#include <GraphicDocShell.hxx> +#include <GraphicObjectBar.hxx> +#include <GraphicViewShell.hxx> +#include <GraphicViewShellBase.hxx> +#include <ImpressViewShellBase.hxx> +#include <PresentationViewShell.hxx> +#include <PresentationViewShellBase.hxx> +#include <MediaObjectBar.hxx> +#include <NavigatorChildWindow.hxx> +#include <OutlineViewShell.hxx> +#include <OutlineViewShellBase.hxx> +#include <PaneChildWindows.hxx> +#include <SpellDialogChildWindow.hxx> +#include <SlideSorterViewShell.hxx> +#include <SlideSorterViewShellBase.hxx> +#include <SdShapeTypes.hxx> +#include <TextObjectBar.hxx> +#include <tmplctrl.hxx> +#include <scalectrl.hxx> + +#include <svx/svxids.hrc> +#include <svx/bmpmask.hxx> +#include <svx/clipboardctl.hxx> +#include <svx/f3dchild.hxx> +#include <svx/fillctrl.hxx> +#include <svx/fontwork.hxx> +#include <svx/formatpaintbrushctrl.hxx> +#include <svx/grafctrl.hxx> +#include <svx/hyperdlg.hxx> +#include <svx/imapdlg.hxx> +#include <svx/linectrl.hxx> +#include <svx/modctrl.hxx> +#include <svx/pszctrl.hxx> +#include <svx/srchdlg.hxx> +#include <svx/SvxColorChildWindow.hxx> +#include <svx/xmlsecctrl.hxx> +#include <svx/zoomctrl.hxx> +#include <svx/zoomsliderctrl.hxx> +#include <svx/tbxctl.hxx> +#include <sfx2/sidebar/SidebarChildWindow.hxx> +#include <sfx2/devtools/DevelopmentToolChildWindow.hxx> +#include <comphelper/lok.hxx> +#include <sdabstdlg.hxx> +#include <sdfilter.hxx> +#include <sdmod.hxx> + +using namespace ::com::sun::star; + +// Register all Factories +void SdDLL::RegisterFactorys() +{ + if (utl::ConfigManager::IsFuzzing() || SvtModuleOptions().IsImpress()) + { + ::sd::ImpressViewShellBase::RegisterFactory ( + ::sd::IMPRESS_FACTORY_ID); + ::sd::SlideSorterViewShellBase::RegisterFactory ( + ::sd::SLIDE_SORTER_FACTORY_ID); + ::sd::OutlineViewShellBase::RegisterFactory ( + ::sd::OUTLINE_FACTORY_ID); + ::sd::PresentationViewShellBase::RegisterFactory ( + ::sd::PRESENTATION_FACTORY_ID); + } + if (!utl::ConfigManager::IsFuzzing() && SvtModuleOptions().IsDraw()) + { + ::sd::GraphicViewShellBase::RegisterFactory (::sd::DRAW_FACTORY_ID); + } +} + +// Register all Interfaces + +void SdDLL::RegisterInterfaces(const SdModule* pMod) +{ + // Module + SdModule::RegisterInterface(pMod); + + // View shell base. + ::sd::ViewShellBase::RegisterInterface(pMod); + + // DocShells + ::sd::DrawDocShell::RegisterInterface(pMod); + ::sd::GraphicDocShell::RegisterInterface(pMod); + + // Impress ViewShells + ::sd::DrawViewShell::RegisterInterface(pMod); + ::sd::OutlineViewShell::RegisterInterface(pMod); + ::sd::PresentationViewShell::RegisterInterface(pMod); + + // Draw ViewShell + ::sd::GraphicViewShell::RegisterInterface(pMod); + + // Impress ObjectShells + ::sd::BezierObjectBar::RegisterInterface(pMod); + ::sd::TextObjectBar::RegisterInterface(pMod); + ::sd::GraphicObjectBar::RegisterInterface(pMod); + + // Media ObjectShell + ::sd::MediaObjectBar::RegisterInterface(pMod); + + // Table ObjectShell + ::sd::ui::table::RegisterInterfaces(pMod); + + // View shells for the side panes. + ::sd::slidesorter::SlideSorterViewShell::RegisterInterface (pMod); +} + +// Register all Controllers + +void SdDLL::RegisterControllers(SdModule* pMod) +{ + SdTbxCtlDiaPages::RegisterControl( SID_PAGES_PER_ROW, pMod ); + SdTbxCtlGlueEscDir::RegisterControl( SID_GLUE_ESCDIR, pMod ); + + ::sd::AnimationChildWindow::RegisterChildWindow(false, pMod); + + Svx3DChildWindow::RegisterChildWindow(false, pMod); + SvxFontWorkChildWindow::RegisterChildWindow(false, pMod); + SvxColorChildWindow::RegisterChildWindow(false, pMod, SfxChildWindowFlags::TASK); + SvxSearchDialogWrapper::RegisterChildWindow(false, pMod); + SvxBmpMaskChildWindow::RegisterChildWindow(false, pMod); + SvxIMapDlgChildWindow::RegisterChildWindow(false, pMod); + SvxHlinkDlgWrapper::RegisterChildWindow(false, pMod); + ::sd::SpellDialogChildWindow::RegisterChildWindow( + false, pMod, comphelper::LibreOfficeKit::isActive() ? SfxChildWindowFlags::NEVERCLONE + : SfxChildWindowFlags::NONE); +#if HAVE_FEATURE_AVMEDIA + ::avmedia::MediaPlayer::RegisterChildWindow(false, pMod); +#endif + ::sd::LeftPaneImpressChildWindow::RegisterChildWindow(false, pMod); + ::sd::LeftPaneDrawChildWindow::RegisterChildWindow(false, pMod); + ::sfx2::sidebar::SidebarChildWindow::RegisterChildWindow(false, pMod); + DevelopmentToolChildWindow::RegisterChildWindow(false, pMod); + + ::sd::SdNavigatorWrapper::RegisterChildWindow(false, pMod, SfxChildWindowFlags::NEVERHIDE); + + SvxFillToolBoxControl::RegisterControl(0, pMod); + SvxLineWidthToolBoxControl::RegisterControl(0, pMod); + + SvxGrafModeToolBoxControl::RegisterControl( SID_ATTR_GRAF_MODE, pMod ); + SvxGrafRedToolBoxControl::RegisterControl( SID_ATTR_GRAF_RED, pMod ); + SvxGrafGreenToolBoxControl::RegisterControl( SID_ATTR_GRAF_GREEN, pMod ); + SvxGrafBlueToolBoxControl::RegisterControl( SID_ATTR_GRAF_BLUE, pMod ); + SvxGrafLuminanceToolBoxControl::RegisterControl( SID_ATTR_GRAF_LUMINANCE, pMod ); + SvxGrafContrastToolBoxControl::RegisterControl( SID_ATTR_GRAF_CONTRAST, pMod ); + SvxGrafGammaToolBoxControl::RegisterControl( SID_ATTR_GRAF_GAMMA, pMod ); + SvxGrafTransparenceToolBoxControl::RegisterControl( SID_ATTR_GRAF_TRANSPARENCE, pMod ); + + // register StatusBarControls + SvxZoomPageStatusBarControl::RegisterControl( SID_ZOOM_ENTIRE_PAGE, pMod ); + SvxZoomStatusBarControl::RegisterControl( SID_ATTR_ZOOM, pMod ); + SvxPosSizeStatusBarControl::RegisterControl( SID_ATTR_SIZE, pMod ); + SvxModifyControl::RegisterControl( SID_DOC_MODIFIED, pMod ); + SvxZoomSliderControl::RegisterControl( SID_ATTR_ZOOMSLIDER, pMod ); + + svx::FormatPaintBrushToolBoxControl::RegisterControl(SID_FORMATPAINTBRUSH, pMod ); + + SvxClipBoardControl::RegisterControl( SID_PASTE, pMod ); + SvxClipBoardControl::RegisterControl( SID_PASTE_UNFORMATTED, pMod ); + +#if HAVE_FEATURE_AVMEDIA + ::avmedia::MediaToolBoxControl::RegisterControl( SID_AVMEDIA_TOOLBOX, pMod ); +#endif + XmlSecStatusBarControl::RegisterControl( SID_SIGNATURE, pMod ); + SdTemplateControl::RegisterControl( SID_STATUS_LAYOUT, pMod ); + SdScaleControl::RegisterControl( SID_SCALE, pMod ); + SvxTbxCtlDraw::RegisterControl(SID_INSERT_DRAW, pMod ); +} + +void SdDLL::Init() +{ + if ( SfxApplication::GetModule(SfxToolsModule::Draw) ) // Module already active + return; + + SfxObjectFactory* pDrawFact = nullptr; + SfxObjectFactory* pImpressFact = nullptr; + + if (utl::ConfigManager::IsFuzzing() || SvtModuleOptions().IsImpress()) + pImpressFact = &::sd::DrawDocShell::Factory(); + + if (!utl::ConfigManager::IsFuzzing() && SvtModuleOptions().IsDraw()) + pDrawFact = &::sd::GraphicDocShell::Factory(); + + auto pUniqueModule = std::make_unique<SdModule>(pImpressFact, pDrawFact); + SdModule* pModule = pUniqueModule.get(); + SfxApplication::SetModule(SfxToolsModule::Draw, std::move(pUniqueModule)); + + if (!utl::ConfigManager::IsFuzzing() && SvtModuleOptions().IsImpress()) + { + // Register the Impress shape types in order to make the shapes accessible. + ::accessibility::RegisterImpressShapeTypes (); + ::sd::DrawDocShell::Factory().SetDocumentServiceName( "com.sun.star.presentation.PresentationDocument" ); + } + + if (!utl::ConfigManager::IsFuzzing() && SvtModuleOptions().IsDraw()) + { + ::sd::GraphicDocShell::Factory().SetDocumentServiceName( "com.sun.star.drawing.DrawingDocument" ); + } + + // register your view-factories here + RegisterFactorys(); + + // register your shell-interfaces here + RegisterInterfaces(pModule); + + // register your controllers here + RegisterControllers(pModule); + + // register 3D-object-factory + E3dObjFactory(); + + // register css::form::component::Form-Object-Factory + FmFormObjFactory(); + + // register your exotic remote controls here +#ifdef ENABLE_SDREMOTE + if (!utl::ConfigManager::IsFuzzing() && !Application::IsHeadlessModeEnabled()) + RegisterRemotes(); +#endif +} + +#ifndef DISABLE_DYNLOADING + +extern "C" SAL_DLLPUBLIC_EXPORT +void lok_preload_hook() +{ + SdFilter::Preload(); + SdAbstractDialogFactory::Create(); +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/app/sdmod.cxx b/sd/source/ui/app/sdmod.cxx new file mode 100644 index 000000000..c7d56831d --- /dev/null +++ b/sd/source/ui/app/sdmod.cxx @@ -0,0 +1,216 @@ +/* -*- 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 <unotools/pathoptions.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <unotools/resmgr.hxx> +#include <tools/urlobj.hxx> +#include <vcl/virdev.hxx> +#include <vcl/svapp.hxx> +#include <svl/numformat.hxx> +#include <svl/intitem.hxx> +#include <sfx2/msg.hxx> +#include <sfx2/objface.hxx> +#include <comphelper/processfactory.hxx> +#include <svtools/ehdl.hxx> + +#include <svx/svxids.hrc> +#include <svl/srchitem.hxx> +#include <svx/svxerr.hxx> + +#include <svtools/colorcfg.hxx> + +#include <sdmod.hxx> +#include <sdresid.hxx> +#include <optsitem.hxx> +#include <DrawDocShell.hxx> +#include <drawdoc.hxx> +#include <errhdl.hrc> + +#define ShellClass_SdModule +#include <sdslots.hxx> + +SFX_IMPL_INTERFACE(SdModule, SfxModule) + +void SdModule::InitInterface_Impl() +{ + GetStaticInterface()->RegisterStatusBar(StatusBarId::DrawStatusBar); +} + +// Ctor +SdModule::SdModule(SfxObjectFactory* pFact1, SfxObjectFactory* pFact2 ) +: SfxModule("sd", {pFact1, pFact2}), + pTransferClip(nullptr), + pTransferDrag(nullptr), + pTransferSelection(nullptr), + pImpressOptions(nullptr), + pDrawOptions(nullptr), + bWaterCan(false), + mbEventListenerAdded(false), + mpColorConfig(new svtools::ColorConfig) +{ + SetName( "StarDraw" ); // Do not translate! + pSearchItem.reset( new SvxSearchItem(SID_SEARCH_ITEM) ); + pSearchItem->SetAppFlag(SvxSearchApp::DRAW); + StartListening( *SfxGetpApp() ); + SvxErrorHandler::ensure(); + mpErrorHdl.reset( new SfxErrorHandler(RID_SD_ERRHDL, ErrCodeArea::Sd, ErrCodeArea::Sd, GetResLocale()) ); + + // Create a new ref device and (by calling SetReferenceDevice()) + // set its resolution to 600 DPI. This leads to a visually better + // formatting of text in small sizes (6 point and below.) + mpVirtualRefDevice.reset(VclPtr<VirtualDevice>::Create()); + mpVirtualRefDevice->SetMapMode(MapMode(MapUnit::Map100thMM)); + mpVirtualRefDevice->SetReferenceDevice ( VirtualDevice::RefDevMode::Dpi600 ); +} + +OUString SdResId(TranslateId aId) +{ + return Translate::get(aId, SD_MOD()->GetResLocale()); +} + +OUString SdResId(TranslateNId aContextSingularPlural, int nCardinality) +{ + return Translate::nget(aContextSingularPlural, nCardinality, SD_MOD()->GetResLocale()); +} + +// Dtor +SdModule::~SdModule() +{ + pSearchItem.reset(); + pNumberFormatter.reset(); + + if (mbEventListenerAdded) + { + Application::RemoveEventListener( LINK( this, SdModule, EventListenerHdl ) ); + } + + mpErrorHdl.reset(); + mpVirtualRefDevice.disposeAndClear(); +} + +void SdModule::SetSearchItem(std::unique_ptr<SvxSearchItem> pItem) +{ + pSearchItem = std::move(pItem); +} + +/// get notifications +void SdModule::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if( rHint.GetId() == SfxHintId::Deinitializing ) + { + delete pImpressOptions; + pImpressOptions = nullptr; + delete pDrawOptions; + pDrawOptions = nullptr; + } +} + +/// Return options +SdOptions* SdModule::GetSdOptions(DocumentType eDocType) +{ + SdOptions* pOptions = nullptr; + + if (eDocType == DocumentType::Draw) + { + if (!pDrawOptions) + pDrawOptions = new SdOptions(false); + + pOptions = pDrawOptions; + } + else if (eDocType == DocumentType::Impress) + { + if (!pImpressOptions) + pImpressOptions = new SdOptions(true); + + pOptions = pImpressOptions; + } + if( pOptions ) + { + sal_uInt16 nMetric = pOptions->GetMetric(); + + ::sd::DrawDocShell* pDocSh = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + SdDrawDocument* pDoc = nullptr; + if (pDocSh) + pDoc = pDocSh->GetDoc(); + + if( nMetric != 0xffff && pDoc && eDocType == pDoc->GetDocumentType() ) + PutItem( SfxUInt16Item( SID_ATTR_METRIC, nMetric ) ); + } + + return pOptions; +} + +/** + * Open and return option stream for internal options; + * if the stream is opened for reading but does not exist, an 'empty' + * RefObject is returned + */ +tools::SvRef<SotStorageStream> SdModule::GetOptionStream( std::u16string_view rOptionName, + SdOptionStreamMode eMode ) +{ + ::sd::DrawDocShell* pDocSh = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + tools::SvRef<SotStorageStream> xStm; + + if( pDocSh ) + { + DocumentType eType = pDocSh->GetDoc()->GetDocumentType(); + + if( !xOptionStorage.is() ) + { + INetURLObject aURL( SvtPathOptions().GetUserConfigPath() ); + + aURL.Append( u"drawing.cfg" ); + + std::unique_ptr<SvStream> pStm = ::utl::UcbStreamHelper::CreateStream( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::READWRITE ); + + if( pStm ) + xOptionStorage = new SotStorage( pStm.release(), true ); + } + + OUString aStmName; + + if( DocumentType::Draw == eType ) + aStmName = "Draw_"; + else + aStmName = "Impress_"; + + aStmName += rOptionName; + + if( SdOptionStreamMode::Store == eMode || xOptionStorage->IsContained( aStmName ) ) + xStm = xOptionStorage->OpenSotStream( aStmName ); + } + + return xStm; +} + +SvNumberFormatter* SdModule::GetNumberFormatter() +{ + if( !pNumberFormatter ) + pNumberFormatter.reset( new SvNumberFormatter( ::comphelper::getProcessComponentContext(), LANGUAGE_SYSTEM ) ); + + return pNumberFormatter.get(); +} + +svtools::ColorConfig& SdModule::GetColorConfig() +{ + return *mpColorConfig; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/app/sdmod1.cxx b/sd/source/ui/app/sdmod1.cxx new file mode 100644 index 000000000..573ee8530 --- /dev/null +++ b/sd/source/ui/app/sdmod1.cxx @@ -0,0 +1,638 @@ +/* -*- 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 <svl/lckbitem.hxx> +#include <svl/intitem.hxx> +#include <sfx2/frame.hxx> +#include <sfx2/viewfrm.hxx> +#include <unotools/moduleoptions.hxx> +#include <framework/FrameworkHelper.hxx> +#include <osl/diagnose.h> +#include <vcl/commandevent.hxx> +#include <vcl/svapp.hxx> +#include <vcl/errinf.hxx> +#include <editeng/langitem.hxx> +#include <vcl/weld.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/request.hxx> +#include <sfx2/templatedlg.hxx> +#include <editeng/eeitem.hxx> + +#include <svx/svxids.hrc> +#include <strings.hrc> + +#include <sdmod.hxx> +#include <pres.hxx> +#include <optsitem.hxx> +#include <ViewShell.hxx> +#include <DrawDocShell.hxx> +#include <drawdoc.hxx> +#include <sdresid.hxx> +#include <OutlineView.hxx> +#include <OutlineViewShell.hxx> +#include <ViewShellBase.hxx> +#include <FactoryIds.hxx> +#include <memory> +#include <slideshow.hxx> + +using ::sd::framework::FrameworkHelper; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::frame::XFrame; + +namespace { + +class OutlineToImpressFinalizer final +{ +public: + OutlineToImpressFinalizer ( + ::sd::ViewShellBase& rBase, + SdDrawDocument& rDocument, + SvLockBytes const & rBytes); + void operator() (bool bEventSeen); +private: + ::sd::ViewShellBase& mrBase; + SdDrawDocument& mrDocument; + std::shared_ptr<SvMemoryStream> mpStream; +}; + +} //end of anonymous namespace + +void SdModule::Execute(SfxRequest& rReq) +{ + const SfxItemSet* pSet = rReq.GetArgs(); + sal_uLong nSlotId = rReq.GetSlot(); + + switch ( nSlotId ) + { + case SID_NEWDOC: + { + SfxGetpApp()->ExecuteSlot(rReq, SfxGetpApp()->GetInterface()); + } + break; + + case SID_AUTOSPELL_CHECK: + { + // automatic spell checker + const SfxBoolItem* pItem; + if( pSet && (pItem = pSet->GetItemIfSet( SID_AUTOSPELL_CHECK, false ) ) ) + { + bool bOnlineSpelling = pItem->GetValue(); + // save at document: + ::sd::DrawDocShell* pDocSh = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + if( pDocSh ) + { + SdDrawDocument* pDoc = pDocSh->GetDoc(); + pDoc->SetOnlineSpell( bOnlineSpelling ); + } + } + } + break; + + case SID_ATTR_METRIC: + { + const SfxUInt16Item* pItem; + if ( pSet && (pItem = pSet->GetItemIfSet( SID_ATTR_METRIC ) ) ) + { + FieldUnit eUnit = static_cast<FieldUnit>(pItem->GetValue()); + switch( eUnit ) + { + case FieldUnit::MM: // only the units which are also in the dialog + case FieldUnit::CM: + case FieldUnit::INCH: + case FieldUnit::PICA: + case FieldUnit::POINT: + { + ::sd::DrawDocShell* pDocSh = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + if(pDocSh) + { + DocumentType eDocType = pDocSh->GetDoc()->GetDocumentType(); + + PutItem( *pItem ); + SdOptions* pOptions = GetSdOptions( eDocType ); + if(pOptions) + pOptions->SetMetric( static_cast<sal_uInt16>(eUnit) ); + rReq.Done(); + } + } + break; + default: + break; + } + } + + } + break; + + case SID_ATTR_LANGUAGE: + case SID_ATTR_CHAR_CJK_LANGUAGE: + case SID_ATTR_CHAR_CTL_LANGUAGE: + { + const SfxPoolItem* pItem; + if( pSet && + ( + SfxItemState::SET == pSet->GetItemState(SID_ATTR_LANGUAGE, false, &pItem ) || + SfxItemState::SET == pSet->GetItemState(SID_ATTR_CHAR_CJK_LANGUAGE, false, &pItem ) || + SfxItemState::SET == pSet->GetItemState(SID_ATTR_CHAR_CTL_LANGUAGE, false, &pItem ) + ) + ) + { + // save at the document: + ::sd::DrawDocShell* pDocSh = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + if ( pDocSh ) + { + LanguageType eLanguage = static_cast<const SvxLanguageItem*>(pItem)->GetValue(); + SdDrawDocument* pDoc = pDocSh->GetDoc(); + + if( nSlotId == sal_uInt16(SID_ATTR_CHAR_CJK_LANGUAGE) ) + pDoc->SetLanguage( eLanguage, EE_CHAR_LANGUAGE_CJK ); + else if( nSlotId == sal_uInt16(SID_ATTR_CHAR_CTL_LANGUAGE) ) + pDoc->SetLanguage( eLanguage, EE_CHAR_LANGUAGE_CTL ); + else + pDoc->SetLanguage( eLanguage, EE_CHAR_LANGUAGE ); + + if( pDoc->GetOnlineSpell() ) + { + pDoc->StopOnlineSpelling(); + pDoc->StartOnlineSpelling(); + } + } + } + } + break; + + case SID_NEWSD: + { + SfxFrame* pFrame = ExecuteNewDocument( rReq ); + // if a frame was created, set it as return value + if(pFrame) + rReq.SetReturnValue(SfxFrameItem(0, pFrame)); + } + + break; + + case SID_OPENHYPERLINK: + case SID_OPENDOC: + { + bool bIntercept = false; + ::sd::DrawDocShell* pDocShell = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + if (pDocShell) + { + ::sd::ViewShell* pViewShell = pDocShell->GetViewShell(); + if (pViewShell) + { + if( sd::SlideShow::IsRunning( pViewShell->GetViewShellBase() ) ) + { + // Prevent documents from opening while the slide + // show is running, except when this request comes + // from a shape interaction. + if (rReq.GetArgs() == nullptr) + { + bIntercept = true; + } + } + } + } + + if (!bIntercept) + { + SfxGetpApp()->ExecuteSlot(rReq, SfxGetpApp()->GetInterface()); + } + else + { + std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(nullptr, + VclMessageType::Warning, VclButtonsType::Ok, SdResId(STR_CANT_PERFORM_IN_LIVEMODE))); + + xErrorBox->run(); + + const SfxLinkItem* pLinkItem = rReq.GetArg<SfxLinkItem>(SID_DONELINK); + if( pLinkItem ) + pLinkItem->GetValue().Call( nullptr ); + } + } + break; + + case SID_OUTLINE_TO_IMPRESS: + OutlineToImpress (rReq); + break; + + default: + break; + } +} + +bool SdModule::OutlineToImpress(SfxRequest const & rRequest) +{ + const SfxItemSet* pSet = rRequest.GetArgs(); + + if (pSet) + { + SvLockBytes* pBytes = static_cast<const SfxLockBytesItem&>(pSet->Get(SID_OUTLINE_TO_IMPRESS)).GetValue(); + + if (pBytes) + { + SfxObjectShellLock xDocShell; + ::sd::DrawDocShell* pDocSh; + xDocShell = pDocSh = new ::sd::DrawDocShell( + SfxObjectCreateMode::STANDARD, false, DocumentType::Impress); + + pDocSh->DoInitNew(); + SdDrawDocument* pDoc = pDocSh->GetDoc(); + if(pDoc) + { + pDoc->CreateFirstPages(); + pDoc->StopWorkStartupDelay(); + } + + const SfxFrameItem* pFrmItem = rRequest.GetArg<SfxFrameItem>(SID_DOCFRAME); + SfxViewFrame::LoadDocumentIntoFrame( *pDocSh, pFrmItem, ::sd::OUTLINE_FACTORY_ID ); + + ::sd::ViewShell* pViewSh = pDocSh->GetViewShell(); + + if (pViewSh && pDoc) + { + // AutoLayouts have to be finished + pDoc->StopWorkStartupDelay(); + + SfxViewFrame* pViewFrame = pViewSh->GetViewFrame(); + + // When the view frame has not been just created we have + // to switch synchronously to the outline view. + // (Otherwise the request will be ignored anyway.) + ::sd::ViewShellBase* pBase + = dynamic_cast< ::sd::ViewShellBase*>(pViewFrame->GetViewShell()); + if (pBase != nullptr) + { + std::shared_ptr<FrameworkHelper> pHelper ( + FrameworkHelper::Instance(*pBase)); + pHelper->RequestView( + FrameworkHelper::msOutlineViewURL, + FrameworkHelper::msCenterPaneURL); + + pHelper->RunOnResourceActivation( + FrameworkHelper::CreateResourceId( + FrameworkHelper::msOutlineViewURL, + FrameworkHelper::msCenterPaneURL), + OutlineToImpressFinalizer(*pBase, *pDoc, *pBytes)); + } + } + } + } + + return rRequest.IsDone(); +} + +void SdModule::GetState(SfxItemSet& rItemSet) +{ + if( SfxItemState::DEFAULT == rItemSet.GetItemState( SID_ATTR_METRIC ) ) + { + ::sd::DrawDocShell* pDocSh = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + if(pDocSh) + { + DocumentType eDocType = pDocSh->GetDoc()->GetDocumentType(); + + SdOptions* pOptions = GetSdOptions(eDocType); + rItemSet.Put( SfxUInt16Item( SID_ATTR_METRIC, pOptions->GetMetric() ) ); + } + } + + // state of SID_OPENDOC is determined by the base class + if (rItemSet.GetItemState(SID_OPENDOC) != SfxItemState::UNKNOWN) + { + const SfxPoolItem* pItem = SfxGetpApp()->GetSlotState(SID_OPENDOC, SfxGetpApp()->GetInterface()); + if (pItem) + rItemSet.Put(*pItem); + } + + // state of SID_OPENHYPERLINK is determined by the base class + if (rItemSet.GetItemState(SID_OPENHYPERLINK) != SfxItemState::UNKNOWN) + { + const SfxPoolItem* pItem = SfxGetpApp()->GetSlotState(SID_OPENHYPERLINK, SfxGetpApp()->GetInterface()); + if (pItem) + rItemSet.Put(*pItem); + } + + if( SfxItemState::DEFAULT == rItemSet.GetItemState( SID_AUTOSPELL_CHECK ) ) + { + ::sd::DrawDocShell* pDocSh = + dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + if( pDocSh ) + { + SdDrawDocument* pDoc = pDocSh->GetDoc(); + rItemSet.Put( SfxBoolItem( SID_AUTOSPELL_CHECK, pDoc->GetOnlineSpell() ) ); + } + } + + if( SfxItemState::DEFAULT == rItemSet.GetItemState( SID_ATTR_LANGUAGE ) ) + { + ::sd::DrawDocShell* pDocSh = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + if( pDocSh ) + rItemSet.Put( SvxLanguageItem( pDocSh->GetDoc()->GetLanguage( EE_CHAR_LANGUAGE ), SID_ATTR_LANGUAGE ) ); + } + + if( SfxItemState::DEFAULT == rItemSet.GetItemState( SID_ATTR_CHAR_CJK_LANGUAGE ) ) + { + ::sd::DrawDocShell* pDocSh = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + if( pDocSh ) + rItemSet.Put( SvxLanguageItem( pDocSh->GetDoc()->GetLanguage( EE_CHAR_LANGUAGE_CJK ), SID_ATTR_CHAR_CJK_LANGUAGE ) ); + } + + if( SfxItemState::DEFAULT == rItemSet.GetItemState( SID_ATTR_CHAR_CTL_LANGUAGE ) ) + { + ::sd::DrawDocShell* pDocSh = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + if( pDocSh ) + rItemSet.Put( SvxLanguageItem( pDocSh->GetDoc()->GetLanguage( EE_CHAR_LANGUAGE_CTL ), SID_ATTR_CHAR_CTL_LANGUAGE ) ); + } + + if ( mbEventListenerAdded ) + return; + + ::sd::DrawDocShell* pDocShell = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + if( pDocShell ) // Impress or Draw ? + { + ::sd::ViewShell* pViewShell = pDocShell->GetViewShell(); + + if( pViewShell && (pDocShell->GetDocumentType() == DocumentType::Impress) ) + { + // add our event listener as soon as possible + Application::AddEventListener( LINK( this, SdModule, EventListenerHdl ) ); + mbEventListenerAdded = true; + } + } +} + +IMPL_STATIC_LINK( SdModule, EventListenerHdl, VclSimpleEvent&, rSimpleEvent, void ) +{ + if( !((rSimpleEvent.GetId() == VclEventId::WindowCommand) && static_cast<VclWindowEvent*>(&rSimpleEvent)->GetData()) ) + return; + + const CommandEvent& rEvent = *static_cast<const CommandEvent*>(static_cast<VclWindowEvent*>(&rSimpleEvent)->GetData()); + + if( rEvent.GetCommand() != CommandEventId::Media ) + return; + + CommandMediaData* pMediaData = rEvent.GetMediaData(); + pMediaData->SetPassThroughToOS(false); + switch (pMediaData->GetMediaId()) + { + case MediaCommand::Play: + { + ::sd::DrawDocShell* pDocShell = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + if( pDocShell ) // Impress or Draw ? + { + ::sd::ViewShell* pViewShell = pDocShell->GetViewShell(); + + // #i97925# start the presentation if and only if an Impress document is focused + if( pViewShell && (pDocShell->GetDocumentType() == DocumentType::Impress) ) + pViewShell->GetViewFrame()->GetDispatcher()->Execute( SID_PRESENTATION ); + } + break; + } + default: + pMediaData->SetPassThroughToOS(true); + break; + } +} + + +SfxFrame* SdModule::CreateFromTemplate(const OUString& rTemplatePath, const Reference<XFrame>& i_rFrame, + const bool bReplaceable) +{ + SfxFrame* pFrame = nullptr; + + SfxObjectShellLock xDocShell; + + std::unique_ptr<SfxItemSet> pSet(new SfxAllItemSet( SfxGetpApp()->GetPool() )); + pSet->Put( SfxBoolItem( SID_TEMPLATE, true ) ); + + ErrCode lErr = SfxGetpApp()->LoadTemplate( xDocShell, rTemplatePath, std::move(pSet) ); + + SfxObjectShell* pDocShell = xDocShell; + + if( lErr ) + { + ErrorHandler::HandleError(lErr); + } + else if( pDocShell ) + { + if (pDocShell->GetMedium() && pDocShell->GetMedium()->GetItemSet()) + pDocShell->GetMedium()->GetItemSet()->Put(SfxBoolItem(SID_REPLACEABLE, bReplaceable)); + SfxViewFrame* pViewFrame = SfxViewFrame::LoadDocumentIntoFrame( *pDocShell, i_rFrame ); + OSL_ENSURE( pViewFrame, "SdModule::CreateFromTemplate: no view frame - was the document really loaded?" ); + pFrame = pViewFrame ? &pViewFrame->GetFrame() : nullptr; + } + + return pFrame; + +} + +SfxFrame* SdModule::ExecuteNewDocument( SfxRequest const & rReq ) +{ + SfxFrame* pFrame = nullptr; + if ( SvtModuleOptions().IsImpress() ) + { + Reference< XFrame > xTargetFrame; + const SfxUnoFrameItem* pFrmItem = rReq.GetArg<SfxUnoFrameItem>(SID_FILLFRAME); + if ( pFrmItem ) + xTargetFrame = pFrmItem->GetFrame(); + + SdOptions* pOpt = GetSdOptions(DocumentType::Impress); + bool bStartWithTemplate = pOpt->IsStartWithTemplate(); + + bool bNewDocDirect = rReq.GetSlot() == SID_NEWSD; + + if( bNewDocDirect ) + { + //we start without wizard + + //check whether we should load a template document + OUString aStandardTemplate( SfxObjectFactory::GetStandardTemplate( u"com.sun.star.presentation.PresentationDocument" ) ); + + if( !aStandardTemplate.isEmpty() ) + { + //load a template document + pFrame = CreateFromTemplate(aStandardTemplate, xTargetFrame, true); + } + else + { + //create an empty document + pFrame = CreateEmptyDocument( xTargetFrame ); + } + } + + if (bStartWithTemplate) + { + //Launch TemplateSelectionDialog + SfxTemplateSelectionDlg aTemplDlg(SfxGetpApp()->GetTopWindow()); + aTemplDlg.run(); + + //check to disable the dialog + pOpt->SetStartWithTemplate( aTemplDlg.IsStartWithTemplate() ); + + //pFrame is loaded with the desired template + if (!aTemplDlg.getTemplatePath().isEmpty()) + pFrame = CreateFromTemplate(aTemplDlg.getTemplatePath(), xTargetFrame, false); + + // show tip-of-the-day dialog if it was deferred because SfxTemplateSelectionDlg + // was open + if (pFrame && SfxApplication::IsTipOfTheDayDue() && !SfxApplication::IsHeadlessOrUITest()) + { + if (SfxDispatcher* pDispatcher = GetDispatcher()) + { + // tdf#127946 pass in argument for dialog parent + SfxUnoFrameItem aDocFrame(SID_FILLFRAME, pFrame->GetFrameInterface()); + pDispatcher->ExecuteList(SID_TIPOFTHEDAY, SfxCallMode::SLOT, {}, { &aDocFrame }); + } + } + } + } + + return pFrame; +} + +SfxFrame* SdModule::CreateEmptyDocument( const Reference< XFrame >& i_rFrame ) +{ + SfxFrame* pFrame = nullptr; + + SfxObjectShellLock xDocShell; + ::sd::DrawDocShell* pNewDocSh; + xDocShell = pNewDocSh = new ::sd::DrawDocShell(SfxObjectCreateMode::STANDARD,false,DocumentType::Impress); + pNewDocSh->DoInitNew(); + SdDrawDocument* pDoc = pNewDocSh->GetDoc(); + if (pDoc) + { + pDoc->CreateFirstPages(); + pDoc->StopWorkStartupDelay(); + } + if (pNewDocSh->GetMedium() && pNewDocSh->GetMedium()->GetItemSet()) + pNewDocSh->GetMedium()->GetItemSet()->Put(SfxBoolItem(SID_REPLACEABLE, true)); + + SfxViewFrame* pViewFrame = SfxViewFrame::LoadDocumentIntoFrame( *pNewDocSh, i_rFrame ); + OSL_ENSURE( pViewFrame, "SdModule::CreateEmptyDocument: no view frame - was the document really loaded?" ); + pFrame = pViewFrame ? &pViewFrame->GetFrame() : nullptr; + + return pFrame; +} + +//===== OutlineToImpressFinalize ============================================== + +namespace { + +OutlineToImpressFinalizer::OutlineToImpressFinalizer ( + ::sd::ViewShellBase& rBase, + SdDrawDocument& rDocument, + SvLockBytes const & rBytes) + : mrBase(rBase), + mrDocument(rDocument) +{ + // The given stream has a lifetime shorter than this new + // OutlineToImpressFinalizer object. Therefore a local copy of the + // stream is created. + const SvStream* pStream (rBytes.GetStream()); + if (pStream == nullptr) + return; + + // Create a memory stream and prepare to fill it with the content of + // the original stream. + mpStream = std::make_shared<SvMemoryStream>(); + static const std::size_t nBufferSize = 4096; + ::std::unique_ptr<sal_Int8[]> pBuffer (new sal_Int8[nBufferSize]); + + sal_uInt64 nReadPosition(0); + bool bLoop (true); + while (bLoop) + { + // Read the next part of the original stream. + std::size_t nReadByteCount (0); + const ErrCode nErrorCode ( + rBytes.ReadAt( + nReadPosition, + pBuffer.get(), + nBufferSize, + &nReadByteCount)); + + // Check the error code and stop copying the stream data when an + // error has occurred. + if (nErrorCode == ERRCODE_NONE) + { + if (nReadByteCount == 0) + bLoop = false; + } + else if (nErrorCode == ERRCODE_IO_PENDING) + ; + else + { + bLoop = false; + nReadByteCount = 0; + } + + // Append the read bytes to the end of the memory stream. + if (nReadByteCount > 0) + { + mpStream->WriteBytes(pBuffer.get(), nReadByteCount); + nReadPosition += nReadByteCount; + } + } + + // Rewind the memory stream so that in the operator() method its + // content is properly read. + mpStream->Seek(STREAM_SEEK_TO_BEGIN); +} + +void OutlineToImpressFinalizer::operator() (bool) +{ + // Fetch the new outline view shell. + ::sd::OutlineViewShell* pOutlineShell + = dynamic_cast<sd::OutlineViewShell*>(FrameworkHelper::Instance(mrBase)->GetViewShell(FrameworkHelper::msCenterPaneURL).get()); + + if (pOutlineShell != nullptr && mpStream != nullptr) + { + sd::OutlineView* pView = static_cast<sd::OutlineView*>(pOutlineShell->GetView()); + // mba: the stream can't contain any relative URLs, because we don't + // have any information about a BaseURL! + pOutlineShell->ReadRtf(*mpStream); + + // Call UpdatePreview once for every slide to resync the + // document with the outliner of the OutlineViewShell. + sal_uInt16 nPageCount (mrDocument.GetSdPageCount(PageKind::Standard)); + for (sal_uInt16 nIndex=0; nIndex<nPageCount; nIndex++) + { + SdPage* pPage = mrDocument.GetSdPage(nIndex, PageKind::Standard); + // Make the page the actual page so that the + // following UpdatePreview() call accesses the + // correct paragraphs. + pView->SetActualPage(pPage); + pOutlineShell->UpdatePreview(pPage); + } + // Select the first slide. + SdPage* pPage = mrDocument.GetSdPage(0, PageKind::Standard); + pView->SetActualPage(pPage); + pOutlineShell->UpdatePreview(pPage); + } + + // Undo-Stack needs to be cleared, else the user may remove the + // only drawpage and this is a state we cannot handle ATM. + ::sd::DrawDocShell* pDocShell = mrDocument.GetDocSh(); + if( pDocShell ) + pDocShell->ClearUndoBuffer(); +} + +} // end of anonymous namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/app/sdmod2.cxx b/sd/source/ui/app/sdmod2.cxx new file mode 100644 index 000000000..cccf42517 --- /dev/null +++ b/sd/source/ui/app/sdmod2.cxx @@ -0,0 +1,809 @@ +/* -*- 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 <editeng/flditem.hxx> +#include <editeng/CustomPropertyField.hxx> +#include <sfx2/printer.hxx> +#include <sfx2/styfitem.hxx> +#include <svl/inethist.hxx> +#include <svl/poolitem.hxx> +#include <svl/flagitem.hxx> +#include <unotools/useroptions.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/docfile.hxx> +#include <osl/diagnose.h> + +#include <editeng/measfld.hxx> +#include <editeng/editstat.hxx> + +#include <svx/svxids.hrc> +#include <svx/dialogs.hrc> +#include <svx/svdotext.hxx> + +#include <sfx2/sfxdlg.hxx> + +#include <sdmod.hxx> +#include <app.hrc> +#include <family.hrc> +#include <strings.hrc> +#include <sdattr.hrc> + +#include <bitmaps.hlst> +#include <ViewShell.hxx> +#include <FrameView.hxx> +#include <optsitem.hxx> +#include <DrawDocShell.hxx> +#include <drawdoc.hxx> +#include <Outliner.hxx> +#include <sdresid.hxx> +#include <pres.hxx> +#include <OutlineViewShell.hxx> +#include <OutlineView.hxx> +#include <ViewShellBase.hxx> +#include <sdpage.hxx> +#include <sdabstdlg.hxx> +#include <svl/intitem.hxx> + +/** retrieves the page that is currently painted. This will only be the master page + if the current drawn view only shows the master page*/ +static SdPage* GetCurrentPage( sd::ViewShell const * pViewSh, EditFieldInfo const * pInfo, bool& bMasterView ) +{ + if( !pInfo ) + return nullptr; + + bMasterView = false; + SdPage* pPage = dynamic_cast< SdPage* >( pInfo->GetSdrPage() ); + SdrOutliner* pOutliner = dynamic_cast< SdrOutliner* >( pInfo->GetOutliner() ); + + // special case, someone already set the current page on the EditFieldInfo + // This is used from the svx::UnoGraphicsExporter f.e. + if( pPage ) + { + bMasterView = false; + return pPage; + } + + // first try to check if we are inside the outline view + sd::OutlineView* pSdView = nullptr; + if( auto pOutlineViewShell = dynamic_cast<const sd::OutlineViewShell* >(pViewSh) ) + pSdView = static_cast<sd::OutlineView*>(pOutlineViewShell->GetView()); + + if (pSdView != nullptr && (pOutliner == &pSdView->GetOutliner())) + { + // outline mode + int nPgNum = 0; + Outliner& rOutl = pSdView->GetOutliner(); + tools::Long nPos = pInfo->GetPara(); + sal_Int32 nParaPos = 0; + + for( Paragraph* pPara = rOutl.GetParagraph( 0 ); pPara && nPos >= 0; pPara = rOutl.GetParagraph( ++nParaPos ), nPos-- ) + { + if( Outliner::HasParaFlag( pPara, ParaFlag::ISPAGE ) ) + nPgNum++; + } + + pPage = pViewSh->GetDoc()->GetSdPage( static_cast<sal_uInt16>(nPgNum), PageKind::Standard ); + } + else + { + // draw mode, slide mode and preview. Get the processed page from the outliner + if(pOutliner) + { + pPage = dynamic_cast< SdPage* >(const_cast< SdrPage* >(pOutliner->getVisualizedPage())); + } + + // The path using GetPaintingPageView() and GetCurrentPaintingDisplayInfo() + // is no longer needed. I debugged and checked all usages of PageNumber decompositions + // which all use the new possibility of setting the visualized page at the SdrOutliner. + + // if all else failed, geht the current page from the object that is + // currently formatted from the document + if(!pPage) + { + const SdrTextObj* pTextObj = (pViewSh && pViewSh->GetDoc()) ? pViewSh->GetDoc()->GetFormattingTextObj() : nullptr; + + if( pTextObj ) + { + pPage = dynamic_cast< SdPage* >( pTextObj->getSdrPageFromSdrObject() ); + } + } + + if(pPage) + { + bMasterView = pPage->IsMasterPage(); + } + } + + return pPage; +} + +/** + * Link for CalcFieldValue of Outliners + */ +IMPL_LINK(SdModule, CalcFieldValueHdl, EditFieldInfo*, pInfo, void) +{ + if (!pInfo) + return; + + const SvxFieldData* pField = pInfo->GetField().GetField(); + ::sd::DrawDocShell* pDocShell = nullptr; + SdDrawDocument* pDoc = nullptr; + + SdrOutliner* pSdrOutliner = dynamic_cast< SdrOutliner* >( pInfo->GetOutliner() ); + if( pSdrOutliner ) + { + const SdrTextObj* pTextObj = pSdrOutliner->GetTextObj(); + + if( pTextObj ) + pDoc = dynamic_cast< SdDrawDocument* >( &pTextObj->getSdrModelFromSdrObject() ); + + if( pDoc ) + pDocShell = pDoc->GetDocSh(); + } + + if( !pDocShell ) + pDocShell = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + + const SvxDateField* pDateField = nullptr; + const SvxExtTimeField* pExtTimeField = nullptr; + const SvxExtFileField* pExtFileField = nullptr; + const SvxAuthorField* pAuthorField = nullptr; + const SvxURLField* pURLField = nullptr; + + const editeng::CustomPropertyField* pCustomPropertyField = nullptr; + + if( (pDateField = dynamic_cast< const SvxDateField* >(pField)) != nullptr ) + { + LanguageType eLang = pInfo->GetOutliner()->GetLanguage( pInfo->GetPara(), pInfo->GetPos() ); + pInfo->SetRepresentation( pDateField->GetFormatted( *GetNumberFormatter(), eLang ) ); + } + else if( (pExtTimeField = dynamic_cast< const SvxExtTimeField *>(pField)) != nullptr ) + { + LanguageType eLang = pInfo->GetOutliner()->GetLanguage( pInfo->GetPara(), pInfo->GetPos() ); + pInfo->SetRepresentation( pExtTimeField->GetFormatted( *GetNumberFormatter(), eLang ) ); + } + else if( (pExtFileField = dynamic_cast< const SvxExtFileField * >(pField)) != nullptr ) + { + if( pDocShell && (pExtFileField->GetType() != SvxFileType::Fix) ) + { + OUString aName; + if( pDocShell->HasName() ) + aName = pDocShell->GetMedium()->GetName(); + else + aName = pDocShell->GetName(); + + const_cast< SvxExtFileField* >(pExtFileField)->SetFile( aName ); + } + pInfo->SetRepresentation( pExtFileField->GetFormatted() ); + + } + else if( (pAuthorField = dynamic_cast< const SvxAuthorField* >( pField )) != nullptr ) + { + if( pAuthorField->GetType() != SvxAuthorType::Fix ) + { + SvtUserOptions aUserOptions; + SvxAuthorField aAuthorField( + aUserOptions.GetFirstName(), aUserOptions.GetLastName(), aUserOptions.GetID(), + pAuthorField->GetType(), pAuthorField->GetFormat() ); + + *const_cast< SvxAuthorField* >(pAuthorField) = aAuthorField; + } + pInfo->SetRepresentation( pAuthorField->GetFormatted() ); + + } + else if( dynamic_cast< const SvxPageField* >(pField) ) + { + OUString aRepresentation(" "); + + ::sd::ViewShell* pViewSh = pDocShell ? pDocShell->GetViewShell() : nullptr; + if(pViewSh == nullptr) + { + ::sd::ViewShellBase* pBase = dynamic_cast< ::sd::ViewShellBase *>( SfxViewShell::Current() ); + if(pBase) + pViewSh = pBase->GetMainViewShell().get(); + } + if( !pDoc && pViewSh ) + pDoc = pViewSh->GetDoc(); + + bool bMasterView; + SdPage* pPage = GetCurrentPage( pViewSh, pInfo, bMasterView ); + + if( pPage && pDoc && !bMasterView ) + { + int nPgNum; + + if( (pPage->GetPageKind() == PageKind::Handout) && pViewSh ) + { + nPgNum = pViewSh->GetPrintedHandoutPageNum(); + } + else + { + nPgNum = (pPage->GetPageNum() - 1) / 2 + 1; + } + aRepresentation = pDoc->CreatePageNumValue(static_cast<sal_uInt16>(nPgNum)); + } + else + aRepresentation = SdResId(STR_FIELD_PLACEHOLDER_NUMBER); + + pInfo->SetRepresentation( aRepresentation ); + } + else if( dynamic_cast< const SvxPageTitleField* >(pField) ) + { + OUString aRepresentation(" "); + + ::sd::ViewShell* pViewSh = pDocShell ? pDocShell->GetViewShell() : nullptr; + if(pViewSh == nullptr) + { + ::sd::ViewShellBase* pBase = dynamic_cast< ::sd::ViewShellBase *>( SfxViewShell::Current() ); + if(pBase) + pViewSh = pBase->GetMainViewShell().get(); + } + if( !pDoc && pViewSh ) + pDoc = pViewSh->GetDoc(); + + bool bMasterView; + SdPage* pPage = GetCurrentPage( pViewSh, pInfo, bMasterView ); + + if( pPage && pDoc && !bMasterView ) + { + aRepresentation = pPage->GetName(); + } + else + { + DocumentType eDocType = pDoc ? pDoc->GetDocumentType() : DocumentType::Impress; + aRepresentation = ( ( eDocType == DocumentType::Impress ) + ? SdResId(STR_FIELD_PLACEHOLDER_SLIDENAME) + : SdResId(STR_FIELD_PLACEHOLDER_PAGENAME) ); + } + + pInfo->SetRepresentation( aRepresentation ); + } + else if( dynamic_cast< const SvxPagesField* >(pField) ) + { + OUString aRepresentation(" "); + + ::sd::ViewShell* pViewSh = pDocShell ? pDocShell->GetViewShell() : nullptr; + if(pViewSh == nullptr) + { + ::sd::ViewShellBase* pBase = dynamic_cast< ::sd::ViewShellBase *>( SfxViewShell::Current() ); + if(pBase) + pViewSh = pBase->GetMainViewShell().get(); + } + if( !pDoc && pViewSh ) + pDoc = pViewSh->GetDoc(); + + bool bMasterView; + SdPage* pPage = GetCurrentPage( pViewSh, pInfo, bMasterView ); + + sal_uInt16 nPageCount = 0; + + if( !bMasterView ) + { + if( pPage && (pPage->GetPageKind() == PageKind::Handout) && pViewSh ) + { + nPageCount = pViewSh->GetPrintedHandoutPageCount(); + } + else if( pDoc ) + { + nPageCount = pDoc->GetActiveSdPageCount(); + } + } + + if( nPageCount > 0 ) + aRepresentation = pDoc->CreatePageNumValue(nPageCount); + else + aRepresentation = SdResId(STR_FIELD_PLACEHOLDER_COUNT); + + pInfo->SetRepresentation( aRepresentation ); + } + else if( (pURLField = dynamic_cast< const SvxURLField* >(pField)) != nullptr ) + { + switch ( pURLField->GetFormat() ) + { + case SvxURLFormat::AppDefault: //!!! adjustable at App??? + case SvxURLFormat::Repr: + pInfo->SetRepresentation( pURLField->GetRepresentation() ); + break; + + case SvxURLFormat::Url: + pInfo->SetRepresentation( pURLField->GetURL() ); + break; + } + + const OUString& aURL = pURLField->GetURL(); + + svtools::ColorConfig aConfig; + svtools::ColorConfigEntry eEntry = + INetURLHistory::GetOrCreate()->QueryUrl( aURL ) ? svtools::LINKSVISITED : svtools::LINKS; + pInfo->SetTextColor( aConfig.GetColorValue(eEntry).nColor ); + } + else if ( dynamic_cast< const SdrMeasureField* >(pField)) + { + pInfo->SetFieldColor(std::optional<Color>()); // clear the field color + } + else if ((pCustomPropertyField = dynamic_cast<const editeng::CustomPropertyField*>(pField)) != nullptr) + { + try + { + SfxObjectShell* pObjSh = SfxObjectShell::Current(); + if (pObjSh && pObjSh->IsLoadingFinished()) + { + auto pNonConstCustomPropertyField = const_cast<editeng::CustomPropertyField*>(pCustomPropertyField); + OUString sCurrent = pNonConstCustomPropertyField->GetFormatted(pObjSh->getDocProperties()); + pInfo->SetRepresentation(sCurrent); + } + else + pInfo->SetRepresentation(pCustomPropertyField->GetCurrentPresentation()); + } + catch (...) + { + pInfo->SetRepresentation(pCustomPropertyField->GetCurrentPresentation()); + } + } + else + { + OUString aRepresentation; + + bool bHeaderField = dynamic_cast< const SvxHeaderField* >( pField ) != nullptr; + bool bFooterField = !bHeaderField && (dynamic_cast< const SvxFooterField* >( pField ) != nullptr ); + bool bDateTimeField = !bHeaderField && !bFooterField && (dynamic_cast< const SvxDateTimeField* >( pField ) != nullptr); + + if( bHeaderField || bFooterField || bDateTimeField ) + { + sd::ViewShell* pViewSh = pDocShell ? pDocShell->GetViewShell() : nullptr; + bool bMasterView = false; + SdPage* pPage = GetCurrentPage( pViewSh, pInfo, bMasterView ); + + if( (pPage == nullptr) || bMasterView ) + { + if( bHeaderField ) + aRepresentation = SdResId(STR_FIELD_PLACEHOLDER_HEADER); + else if (bFooterField ) + aRepresentation = SdResId(STR_FIELD_PLACEHOLDER_FOOTER); + else if (bDateTimeField ) + aRepresentation = SdResId(STR_FIELD_PLACEHOLDER_DATETIME); + } + else + { + const sd::HeaderFooterSettings &rSettings = pPage->getHeaderFooterSettings(); + + if( bHeaderField ) + { + aRepresentation = rSettings.maHeaderText; + } + else if( bFooterField ) + { + aRepresentation = rSettings.maFooterText; + } + else if( bDateTimeField ) + { + if( rSettings.mbDateTimeIsFixed ) + { + aRepresentation = rSettings.maDateTimeText; + } + else + { + DateTime aDateTime( DateTime::SYSTEM ); + LanguageType eLang = pInfo->GetOutliner()->GetLanguage( pInfo->GetPara(), pInfo->GetPos() ); + aRepresentation = SvxDateTimeField::GetFormatted( aDateTime, aDateTime, + rSettings.meDateFormat, rSettings.meTimeFormat, *GetNumberFormatter(), eLang ); + } + } + } + } + else + { + OSL_FAIL("sd::SdModule::CalcFieldValueHdl(), unknown field type!"); + } + + if( aRepresentation.isEmpty() ) // TODO: Edit engine doesn't handle empty fields? + aRepresentation = " "; + pInfo->SetRepresentation( aRepresentation ); + } +} + +/** + * virtual methods for option dialog + */ +std::optional<SfxItemSet> SdModule::CreateItemSet( sal_uInt16 nSlot ) +{ + ::sd::FrameView* pFrameView = nullptr; + ::sd::DrawDocShell* pDocSh = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + SdDrawDocument* pDoc = nullptr; + + // Here we set the DocType of the option dialog (not document!) + DocumentType eDocType = DocumentType::Impress; + if( nSlot == SID_SD_GRAPHIC_OPTIONS ) + eDocType = DocumentType::Draw; + + if (pDocSh) + { + pDoc = pDocSh->GetDoc(); + + // If the option dialog is identical to the document type, + // we can pass the FrameView too: + if( pDoc && eDocType == pDoc->GetDocumentType() ) + pFrameView = pDocSh->GetFrameView(); + + ::sd::ViewShell* pViewShell = pDocSh->GetViewShell(); + if (pViewShell != nullptr) + pViewShell->WriteFrameViewData(); + } + + SdOptions* pOptions = GetSdOptions(eDocType); + + // Pool has by default MapUnit Twips (Awgh!) + SfxItemPool& rPool = GetPool(); + rPool.SetDefaultMetric( MapUnit::Map100thMM ); + + SfxItemSetFixed< + SID_ATTR_GRID_OPTIONS, SID_ATTR_GRID_OPTIONS, + SID_ATTR_METRIC, SID_ATTR_METRIC, + SID_ATTR_DEFTABSTOP, SID_ATTR_DEFTABSTOP, + ATTR_OPTIONS_LAYOUT, ATTR_OPTIONS_SCALE_END> aRet(rPool); + + // TP_OPTIONS_LAYOUT: + aRet.Put( SdOptionsLayoutItem( pOptions, pFrameView ) ); + + sal_uInt16 nDefTab = 0; + if( pFrameView) + nDefTab = pDoc->GetDefaultTabulator(); + else + nDefTab = pOptions->GetDefTab(); + aRet.Put( SfxUInt16Item( SID_ATTR_DEFTABSTOP, nDefTab ) ); + + FieldUnit nMetric = FieldUnit(0xffff); + if( pFrameView) + nMetric = pDoc->GetUIUnit(); + else + nMetric = static_cast<FieldUnit>(pOptions->GetMetric()); + + if( nMetric == FieldUnit(0xffff) ) + nMetric = GetFieldUnit(); + + aRet.Put( SfxUInt16Item( SID_ATTR_METRIC, static_cast<sal_uInt16>(nMetric) ) ); + + // TP_OPTIONS_MISC: + SdOptionsMiscItem aSdOptionsMiscItem( pOptions, pFrameView ); + if ( pFrameView ) + { + aSdOptionsMiscItem.GetOptionsMisc().SetSummationOfParagraphs( pDoc->IsSummationOfParagraphs() ); + aSdOptionsMiscItem.GetOptionsMisc().SetPrinterIndependentLayout ( + static_cast<sal_uInt16>(pDoc->GetPrinterIndependentLayout())); + } + aRet.Put( aSdOptionsMiscItem ); + + // TP_OPTIONS_SNAP: + aRet.Put( SdOptionsSnapItem( pOptions, pFrameView ) ); + + // TP_SCALE: + sal_uInt32 nW = 10; + sal_uInt32 nH = 10; + sal_Int32 nX; + sal_Int32 nY; + if( pDocSh ) + { + SdrPage* pPage = pDoc->GetSdPage(0, PageKind::Standard); + Size aSize(pPage->GetSize()); + nW = aSize.Width(); + nH = aSize.Height(); + } + + if(pFrameView) + { + const Fraction& rFraction = pDoc->GetUIScale(); + nX=rFraction.GetNumerator(); + nY=rFraction.GetDenominator(); + } + else + { + // Get options from configuration file + pOptions->GetScale( nX, nY ); + } + + aRet.Put( SfxInt32Item( ATTR_OPTIONS_SCALE_X, nX ) ); + aRet.Put( SfxInt32Item( ATTR_OPTIONS_SCALE_Y, nY ) ); + aRet.Put( SfxUInt32Item( ATTR_OPTIONS_SCALE_WIDTH, nW ) ); + aRet.Put( SfxUInt32Item( ATTR_OPTIONS_SCALE_HEIGHT, nH ) ); + + // TP_OPTIONS_PRINT: + aRet.Put( SdOptionsPrintItem( pOptions ) ); + + // RID_SVXPAGE_GRID: + aRet.Put( SdOptionsGridItem( pOptions ) ); + + return aRet; +} + +void SdModule::ApplyItemSet( sal_uInt16 nSlot, const SfxItemSet& rSet ) +{ + bool bNewDefTab = false; + bool bNewPrintOptions = false; + bool bMiscOptions = false; + + ::sd::DrawDocShell* pDocSh = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + SdDrawDocument* pDoc = nullptr; + // Here we set the DocType of the option dialog (not document!) + DocumentType eDocType = DocumentType::Impress; + if( nSlot == SID_SD_GRAPHIC_OPTIONS ) + eDocType = DocumentType::Draw; + + ::sd::ViewShell* pViewShell = nullptr; + + if (pDocSh) + { + pDoc = pDocSh->GetDoc(); + + pViewShell = pDocSh->GetViewShell(); + if (pViewShell != nullptr) + pViewShell->WriteFrameViewData(); + } + SdOptions* pOptions = GetSdOptions(eDocType); + // Grid + if( const SdOptionsGridItem* pGridItem = static_cast<const SdOptionsGridItem*>(rSet.GetItemIfSet( SID_ATTR_GRID_OPTIONS, false )) ) + { + pGridItem->SetOptions( pOptions ); + } + + // Layout + if( const SdOptionsLayoutItem* pLayoutItem = rSet.GetItemIfSet( ATTR_OPTIONS_LAYOUT, false )) + { + pLayoutItem->SetOptions( pOptions ); + } + + // Metric + if( const SfxUInt16Item* pItem = rSet.GetItemIfSet( SID_ATTR_METRIC, false ) ) + { + if( pDoc && eDocType == pDoc->GetDocumentType() ) + PutItem( *pItem ); + pOptions->SetMetric( pItem->GetValue() ); + } + sal_uInt16 nDefTab = pOptions->GetDefTab(); + // Default-Tabulator + if( const SfxUInt16Item* pItem = rSet.GetItemIfSet( SID_ATTR_DEFTABSTOP, false ) ) + { + nDefTab = pItem->GetValue(); + pOptions->SetDefTab( nDefTab ); + + bNewDefTab = true; + } + + // Scale + if( const SfxInt32Item* pItem = rSet.GetItemIfSet( ATTR_OPTIONS_SCALE_X, false ) ) + { + sal_Int32 nX = pItem->GetValue(); + pItem = rSet.GetItemIfSet( ATTR_OPTIONS_SCALE_Y, false ); + if( pItem ) + { + sal_Int32 nY = pItem->GetValue(); + pOptions->SetScale( nX, nY ); + + // Apply to document only if doc type match + if( pDocSh && pDoc && eDocType == pDoc->GetDocumentType() ) + { + pDoc->SetUIScale( Fraction( nX, nY ) ); + if( pViewShell ) + pViewShell->SetRuler( pViewShell->HasRuler() ); + } + } + } + + // Misc + const SdOptionsMiscItem* pMiscItem = rSet.GetItemIfSet( ATTR_OPTIONS_MISC, false); + if( pMiscItem ) + { + pMiscItem->SetOptions( pOptions ); + bMiscOptions = true; + } + + // Snap + const SdOptionsSnapItem* pSnapItem = rSet.GetItemIfSet( ATTR_OPTIONS_SNAP, false ); + if( pSnapItem ) + { + pSnapItem->SetOptions( pOptions ); + } + + SfxItemSetFixed<SID_PRINTER_NOTFOUND_WARN, SID_PRINTER_NOTFOUND_WARN, + SID_PRINTER_CHANGESTODOC, SID_PRINTER_CHANGESTODOC, + ATTR_OPTIONS_PRINT, ATTR_OPTIONS_PRINT> aPrintSet( GetPool() ); + + // Print + const SdOptionsPrintItem* pPrintItem = rSet.GetItemIfSet( ATTR_OPTIONS_PRINT, false); + if( pPrintItem ) + { + pPrintItem->SetOptions( pOptions ); + + // set PrintOptionsSet + SdOptionsPrintItem aPrintItem( pOptions ); + SfxFlagItem aFlagItem( SID_PRINTER_CHANGESTODOC ); + SfxPrinterChangeFlags nFlags = + (aPrintItem.GetOptionsPrint().IsWarningSize() ? SfxPrinterChangeFlags::CHG_SIZE : SfxPrinterChangeFlags::NONE) | + (aPrintItem.GetOptionsPrint().IsWarningOrientation() ? SfxPrinterChangeFlags::CHG_ORIENTATION : SfxPrinterChangeFlags::NONE); + aFlagItem.SetValue( static_cast<int>(nFlags) ); + + aPrintSet.Put( aPrintItem ); + aPrintSet.Put( SfxBoolItem( SID_PRINTER_NOTFOUND_WARN, aPrintItem.GetOptionsPrint().IsWarningPrinter() ) ); + aPrintSet.Put( aFlagItem ); + + bNewPrintOptions = true; + } + + // Only if also the document type matches... + if( pDocSh && pDoc && eDocType == pDoc->GetDocumentType() ) + { + if( bNewPrintOptions ) + { + pDocSh->GetPrinter(true)->SetOptions( aPrintSet ); + } + + // set DefTab at Model + if( bNewDefTab ) + { + SdDrawDocument* pDocument = pDocSh->GetDoc(); + pDocument->SetDefaultTabulator( nDefTab ); + + SdOutliner* pOutl = pDocument->GetOutliner( false ); + if( pOutl ) + pOutl->SetDefTab( nDefTab ); + + SdOutliner* pInternalOutl = pDocument->GetInternalOutliner( false ); + if( pInternalOutl ) + pInternalOutl->SetDefTab( nDefTab ); + } + if ( bMiscOptions ) + { + pDoc->SetSummationOfParagraphs( pMiscItem->GetOptionsMisc().IsSummationOfParagraphs() ); + EEControlBits nSum = pMiscItem->GetOptionsMisc().IsSummationOfParagraphs() ? EEControlBits::ULSPACESUMMATION : EEControlBits::NONE; + EEControlBits nCntrl; + + SdDrawDocument* pDocument = pDocSh->GetDoc(); + SdrOutliner& rOutl = pDocument->GetDrawOutliner(); + nCntrl = rOutl.GetControlWord() &~ EEControlBits::ULSPACESUMMATION; + rOutl.SetControlWord( nCntrl | nSum ); + SdOutliner* pOutl = pDocument->GetOutliner( false ); + if( pOutl ) + { + nCntrl = pOutl->GetControlWord() &~ EEControlBits::ULSPACESUMMATION; + pOutl->SetControlWord( nCntrl | nSum ); + } + pOutl = pDocument->GetInternalOutliner( false ); + if( pOutl ) + { + nCntrl = pOutl->GetControlWord() &~ EEControlBits::ULSPACESUMMATION; + pOutl->SetControlWord( nCntrl | nSum ); + } + + // Set printer independent layout mode. + if( pDoc->GetPrinterIndependentLayout() != pMiscItem->GetOptionsMisc().GetPrinterIndependentLayout() ) + pDoc->SetPrinterIndependentLayout (pMiscItem->GetOptionsMisc().GetPrinterIndependentLayout()); + } + } + + pOptions->StoreConfig(); + + // Only if also the document type matches... + if( pDocSh && pDoc && eDocType == pDoc->GetDocumentType() ) + { + FieldUnit eUIUnit = static_cast<FieldUnit>(pOptions->GetMetric()); + pDoc->SetUIUnit(eUIUnit); + + if (pViewShell) + { + // make sure no one is in text edit mode, cause there + // are some pointers remembered else (!) + if(pViewShell->GetView()) + pViewShell->GetView()->SdrEndTextEdit(); + + ::sd::FrameView* pFrame = pViewShell->GetFrameView(); + pFrame->Update(pOptions); + pViewShell->ReadFrameViewData(pFrame); + pViewShell->SetUIUnit(eUIUnit); + pViewShell->SetDefTabHRuler( nDefTab ); + } + } + + if( pViewShell && pViewShell->GetViewFrame() ) + pViewShell->GetViewFrame()->GetBindings().InvalidateAll( true ); +} + +std::unique_ptr<SfxTabPage> SdModule::CreateTabPage( sal_uInt16 nId, weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet ) +{ + std::unique_ptr<SfxTabPage> xRet; + SfxAllItemSet aSet(*(rSet.GetPool())); + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + + switch(nId) + { + case SID_SD_TP_CONTENTS: + case SID_SI_TP_CONTENTS: + { + ::CreateTabPage fnCreatePage = pFact->GetSdOptionsContentsTabPageCreatorFunc(); + if( fnCreatePage ) + xRet = (*fnCreatePage)( pPage, pController, &rSet ); + } + break; + case SID_SD_TP_SNAP: + case SID_SI_TP_SNAP: + { + ::CreateTabPage fnCreatePage = pFact->GetSdOptionsSnapTabPageCreatorFunc(); + if( fnCreatePage ) + xRet = (*fnCreatePage)( pPage, pController, &rSet ); + } + break; + case SID_SD_TP_PRINT: + case SID_SI_TP_PRINT: + { + ::CreateTabPage fnCreatePage = pFact->GetSdPrintOptionsTabPageCreatorFunc(); + if( fnCreatePage ) + { + xRet = (*fnCreatePage)( pPage, pController, &rSet ); + if(SID_SD_TP_PRINT == nId) + aSet.Put (SfxUInt32Item(SID_SDMODE_FLAG,SD_DRAW_MODE)); + xRet->PageCreated(aSet); + } + } + break; + case SID_SI_TP_MISC: + case SID_SD_TP_MISC: + { + ::CreateTabPage fnCreatePage = pFact->GetSdOptionsMiscTabPageCreatorFunc(); + if( fnCreatePage ) + { + xRet = (*fnCreatePage)( pPage, pController, &rSet ); + if(SID_SD_TP_MISC == nId) + aSet.Put (SfxUInt32Item(SID_SDMODE_FLAG,SD_DRAW_MODE)); + else + aSet.Put (SfxUInt32Item(SID_SDMODE_FLAG,SD_IMPRESS_MODE)); + xRet->PageCreated(aSet); + } + } + break; + case RID_SVXPAGE_TEXTANIMATION : + { + SfxAbstractDialogFactory* pSfxFact = SfxAbstractDialogFactory::Create(); + ::CreateTabPage fnCreatePage = pSfxFact->GetTabPageCreatorFunc( nId ); + if ( fnCreatePage ) + xRet = (*fnCreatePage)( pPage, pController, &rSet ); + } + break; + } + DBG_ASSERT( xRet, "SdModule::CreateTabPage(): no valid ID for TabPage!" ); + + return xRet; +} + +std::optional<SfxStyleFamilies> SdModule::CreateStyleFamilies() +{ + SfxStyleFamilies aStyleFamilies; + + aStyleFamilies.emplace_back(SfxStyleFamily::Para, + SdResId(STR_GRAPHICS_STYLE_FAMILY), + BMP_STYLES_FAMILY_GRAPHICS, + RID_GRAPHICSTYLEFAMILY, SD_MOD()->GetResLocale()); + + aStyleFamilies.emplace_back(SfxStyleFamily::Pseudo, + SdResId(STR_PRESENTATIONS_STYLE_FAMILY), + BMP_STYLES_FAMILY_PRESENTATIONS, + RID_PRESENTATIONSTYLEFAMILY, SD_MOD()->GetResLocale()); + + return aStyleFamilies; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/app/sdpopup.cxx b/sd/source/ui/app/sdpopup.cxx new file mode 100644 index 000000000..4aafd2848 --- /dev/null +++ b/sd/source/ui/app/sdpopup.cxx @@ -0,0 +1,318 @@ +/* -*- 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 <sal/config.h> + +#include <string_view> + +#include <editeng/flditem.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/docfile.hxx> +#include <unotools/useroptions.hxx> +#include <vcl/svapp.hxx> + +#include <strings.hrc> +#include <sdpopup.hxx> +#include <sdresid.hxx> +#include <sdmod.hxx> +#include <DrawDocShell.hxx> + +/* + * Popup menu for editing of field command + */ +SdFieldPopup::SdFieldPopup(const SvxFieldData* pInField, LanguageType eLanguage) + : m_xBuilder(Application::CreateBuilder(nullptr, "modules/simpress/ui/fieldmenu.ui")) + , m_xPopup(m_xBuilder->weld_menu("menu")) + , m_pField(pInField) +{ + Fill(eLanguage); +} + +SdFieldPopup::~SdFieldPopup() +{ +} + +void SdFieldPopup::Fill( LanguageType eLanguage ) +{ + sal_uInt16 nID = 1; + m_xPopup->append_radio(OUString::number(nID++), SdResId(STR_FIX)); + m_xPopup->append_radio(OUString::number(nID++), SdResId(STR_VAR)); + m_xPopup->append_separator("separator1"); + + if( auto pDateField = dynamic_cast< const SvxDateField *>( m_pField ) ) + { + SvxDateField aDateField( *pDateField ); + + if (pDateField->GetType() == SvxDateType::Fix) + m_xPopup->set_active("1", true); + else + m_xPopup->set_active("2", true); + + //SvxDateFormat::AppDefault, // is not used + //SvxDateFormat::System, // is not used + m_xPopup->append_radio(OUString::number(nID++), SdResId(STR_STANDARD_SMALL)); + m_xPopup->append_radio(OUString::number(nID++), SdResId(STR_STANDARD_BIG)); + + SvNumberFormatter* pNumberFormatter = SD_MOD()->GetNumberFormatter(); + aDateField.SetFormat( SvxDateFormat::A ); // 13.02.96 + m_xPopup->append_radio(OUString::number(nID++), aDateField.GetFormatted(*pNumberFormatter, eLanguage)); + aDateField.SetFormat( SvxDateFormat::B ); // 13.02.1996 + m_xPopup->append_radio(OUString::number(nID++), aDateField.GetFormatted(*pNumberFormatter, eLanguage)); + aDateField.SetFormat( SvxDateFormat::C ); // 13.Feb 1996 + m_xPopup->append_radio(OUString::number(nID++), aDateField.GetFormatted(*pNumberFormatter, eLanguage)); + + aDateField.SetFormat( SvxDateFormat::D ); // 13.Februar 1996 + m_xPopup->append_radio(OUString::number(nID++), aDateField.GetFormatted(*pNumberFormatter, eLanguage)); + aDateField.SetFormat( SvxDateFormat::E ); // Die, 13.Februar 1996 + m_xPopup->append_radio(OUString::number(nID++), aDateField.GetFormatted(*pNumberFormatter, eLanguage)); + aDateField.SetFormat( SvxDateFormat::F ); // Dienstag, 13.Februar 1996 + m_xPopup->append_radio(OUString::number(nID++), aDateField.GetFormatted(*pNumberFormatter, eLanguage)); + + m_xPopup->set_active(OString::number(static_cast<sal_uInt16>( pDateField->GetFormat() ) + 1), true); // - 2 + 3 ! + } + else if( auto pTimeField = dynamic_cast< const SvxExtTimeField *>( m_pField ) ) + { + SvxExtTimeField aTimeField( *pTimeField ); + + if( pTimeField->GetType() == SvxTimeType::Fix ) + m_xPopup->set_active("1", true); + else + m_xPopup->set_active("2", true); + + //SvxTimeFormat::AppDefault, // is not used + //SvxTimeFormat::System, // is not used + m_xPopup->append_radio(OUString::number(nID++), SdResId(STR_STANDARD_NORMAL)); + + SvNumberFormatter* pNumberFormatter = SD_MOD()->GetNumberFormatter(); + aTimeField.SetFormat( SvxTimeFormat::HH24_MM ); // 13:49 + m_xPopup->append_radio(OUString::number(nID++), aTimeField.GetFormatted(*pNumberFormatter, eLanguage)); + aTimeField.SetFormat( SvxTimeFormat::HH24_MM_SS ); // 13:49:38 + m_xPopup->append_radio(OUString::number(nID++), aTimeField.GetFormatted(*pNumberFormatter, eLanguage)); + aTimeField.SetFormat( SvxTimeFormat::HH24_MM_SS_00 ); // 13:49:38.78 + m_xPopup->append_radio(OUString::number(nID++), aTimeField.GetFormatted(*pNumberFormatter, eLanguage)); + + aTimeField.SetFormat( SvxTimeFormat::HH12_MM ); // 01:49 + m_xPopup->append_radio(OUString::number(nID++), aTimeField.GetFormatted(*pNumberFormatter, eLanguage)); + aTimeField.SetFormat( SvxTimeFormat::HH12_MM_SS ); // 01:49:38 + m_xPopup->append_radio(OUString::number(nID++), aTimeField.GetFormatted(*pNumberFormatter, eLanguage)); + aTimeField.SetFormat( SvxTimeFormat::HH12_MM_SS_00 ); // 01:49:38.78 + m_xPopup->append_radio(OUString::number(nID++), aTimeField.GetFormatted(*pNumberFormatter, eLanguage)); + //SvxTimeFormat::HH12_MM_AMPM, // 01:49 PM + //SvxTimeFormat::HH12_MM_SS_AMPM, // 01:49:38 PM + //SvxTimeFormat::HH12_MM_SS_00_AMPM // 01:49:38.78 PM + + m_xPopup->set_active(OString::number(static_cast<sal_uInt16>( pTimeField->GetFormat() ) + 1), true); // - 2 + 3 ! + } + else if( auto pFileField = dynamic_cast< const SvxExtFileField *>( m_pField ) ) + { + //SvxExtFileField aFileField( *pFileField ); + + if( pFileField->GetType() == SvxFileType::Fix ) + m_xPopup->set_active("1", true); + else + m_xPopup->set_active("2", true); + + m_xPopup->append_radio(OUString::number(nID++), SdResId(STR_FILEFORMAT_NAME_EXT)); + m_xPopup->append_radio(OUString::number(nID++), SdResId(STR_FILEFORMAT_FULLPATH)); + m_xPopup->append_radio(OUString::number(nID++), SdResId(STR_FILEFORMAT_PATH)); + m_xPopup->append_radio(OUString::number(nID++), SdResId(STR_FILEFORMAT_NAME)); + + m_xPopup->set_active(OString::number(static_cast<sal_uInt16>( pFileField->GetFormat() ) + 3), true); + } + else if( auto pAuthorField = dynamic_cast< const SvxAuthorField *>( m_pField ) ) + { + SvxAuthorField aAuthorField( *pAuthorField ); + + if( pAuthorField->GetType() == SvxAuthorType::Fix ) + m_xPopup->set_active("1", true); + else + m_xPopup->set_active("2", true); + + for( sal_uInt16 i = 0; i < 4; i++ ) + { + aAuthorField.SetFormat( static_cast<SvxAuthorFormat>(i) ); + m_xPopup->append_radio(OUString::number(nID++), aAuthorField.GetFormatted()); + } + m_xPopup->set_active(OString::number(static_cast<sal_uInt16>( pAuthorField->GetFormat() ) + 3), true); + } +} + +void SdFieldPopup::Execute(weld::Window* pParent, const tools::Rectangle& rRect) +{ + OString sIdent = m_xPopup->popup_at_rect(pParent, rRect); + if (sIdent.isEmpty()) + return; + + if (sIdent == "1" || sIdent == "2") + { + m_xPopup->set_active("1", sIdent == "1"); + m_xPopup->set_active("2", sIdent == "2"); + } + else + { + int nCount = m_xPopup->n_children(); + for (int i = 3; i < nCount; i++) + m_xPopup->set_active( + OString::number(i), sIdent == std::string_view(OString::number(i))); + } +} + +/** + * Returns a new field, owned by caller. + * Returns NULL if nothing changed. + */ +SvxFieldData* SdFieldPopup::GetField() +{ + SvxFieldData* pNewField = nullptr; + + sal_uInt16 nCount = m_xPopup->n_children(); + + if( auto pDateField = dynamic_cast< const SvxDateField *>( m_pField ) ) + { + SvxDateType eType; + SvxDateFormat eFormat; + sal_uInt16 i; + + if (m_xPopup->get_active("1")) + eType = SvxDateType::Fix; + else + eType = SvxDateType::Var; + + for( i = 3; i < nCount; i++ ) + { + if (m_xPopup->get_active(OString::number(i))) + break; + } + eFormat = static_cast<SvxDateFormat>( i - 1 ); + + if( pDateField->GetFormat() != eFormat || + pDateField->GetType() != eType ) + { + pNewField = new SvxDateField( *pDateField ); + static_cast<SvxDateField*>( pNewField )->SetType( eType ); + static_cast<SvxDateField*>( pNewField )->SetFormat( eFormat ); + + if( (pDateField->GetType() == SvxDateType::Var) && (eType == SvxDateType::Fix) ) + { + Date aDate( Date::SYSTEM ); + static_cast<SvxDateField*>( pNewField )->SetFixDate( aDate ); + } + } + } + else if( auto pTimeField = dynamic_cast< const SvxExtTimeField *>( m_pField ) ) + { + SvxTimeType eType; + SvxTimeFormat eFormat; + sal_uInt16 i; + + if (m_xPopup->get_active("1")) + eType = SvxTimeType::Fix; + else + eType = SvxTimeType::Var; + + for( i = 3; i < nCount; i++ ) + { + if (m_xPopup->get_active(OString::number(i))) + break; + } + eFormat = static_cast<SvxTimeFormat>( i - 1 ); + + if( pTimeField->GetFormat() != eFormat || + pTimeField->GetType() != eType ) + { + pNewField = new SvxExtTimeField( *pTimeField ); + static_cast<SvxExtTimeField*>( pNewField )->SetType( eType ); + static_cast<SvxExtTimeField*>( pNewField )->SetFormat( eFormat ); + + if( (pTimeField->GetType() == SvxTimeType::Var) && (eType == SvxTimeType::Fix) ) + { + tools::Time aTime( tools::Time::SYSTEM ); + static_cast<SvxExtTimeField*>( pNewField )->SetFixTime( aTime ); + } + + } + } + else if( auto pFileField = dynamic_cast< const SvxExtFileField *>( m_pField ) ) + { + SvxFileType eType; + SvxFileFormat eFormat; + sal_uInt16 i; + + if (m_xPopup->get_active("1")) + eType = SvxFileType::Fix; + else + eType = SvxFileType::Var; + + for( i = 3; i < nCount; i++ ) + { + if (m_xPopup->get_active(OString::number(i))) + break; + } + eFormat = static_cast<SvxFileFormat>( i - 3 ); + + if( pFileField->GetFormat() != eFormat || + pFileField->GetType() != eType ) + { + ::sd::DrawDocShell* pDocSh = dynamic_cast<::sd::DrawDocShell* >( SfxObjectShell::Current() ); + + if( pDocSh ) + { + OUString aName; + if( pDocSh->HasName() ) + aName = pDocSh->GetMedium()->GetName(); + + // Get current filename, not the one stored in the old field + pNewField = new SvxExtFileField( aName ); + static_cast<SvxExtFileField*>( pNewField )->SetType( eType ); + static_cast<SvxExtFileField*>( pNewField )->SetFormat( eFormat ); + } + } + } + else if( auto pAuthorField = dynamic_cast< const SvxAuthorField *>( m_pField ) ) + { + SvxAuthorType eType; + SvxAuthorFormat eFormat; + sal_uInt16 i; + + if (m_xPopup->get_active("1")) + eType = SvxAuthorType::Fix; + else + eType = SvxAuthorType::Var; + + for( i = 3; i < nCount; i++ ) + { + if (m_xPopup->get_active(OString::number(i))) + break; + } + eFormat = static_cast<SvxAuthorFormat>( i - 3 ); + + if( pAuthorField->GetFormat() != eFormat || + pAuthorField->GetType() != eType ) + { + // Get current state of address, not the old one + SvtUserOptions aUserOptions; + pNewField = new SvxAuthorField( aUserOptions.GetFirstName(), aUserOptions.GetLastName(), aUserOptions.GetID() ); + static_cast<SvxAuthorField*>( pNewField )->SetType( eType ); + static_cast<SvxAuthorField*>( pNewField )->SetFormat( eFormat ); + } + } + return pNewField; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/app/sdxfer.cxx b/sd/source/ui/app/sdxfer.cxx new file mode 100644 index 000000000..67016fd19 --- /dev/null +++ b/sd/source/ui/app/sdxfer.cxx @@ -0,0 +1,807 @@ +/* -*- 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 <sal/config.h> + +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/embed/XTransactedObject.hpp> +#include <com/sun/star/embed/XEmbedPersist.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <comphelper/fileformat.h> +#include <unotools/ucbstreamhelper.hxx> +#include <unotools/tempfile.hxx> +#include <editeng/flditem.hxx> +#include <svx/svdpagv.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdograf.hxx> +#include <svx/svdotext.hxx> +#include <editeng/outlobj.hxx> +#include <sot/storage.hxx> +#include <editeng/editobj.hxx> +#include <o3tl/safeint.hxx> +#include <svx/svdobjkind.hxx> +#include <svx/svdouno.hxx> +#include <svx/ImageMapInfo.hxx> +#include <sot/formats.hxx> +#include <svl/urlbmk.hxx> +#include <tools/diagnose_ex.h> + +#include <com/sun/star/form/FormButtonType.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <unotools/streamwrap.hxx> + +#include <svx/svdotable.hxx> +#include <svx/unomodel.hxx> +#include <svx/svditer.hxx> +#include <sfx2/docfile.hxx> +#include <comphelper/storagehelper.hxx> +#include <comphelper/servicehelper.hxx> +#include <svtools/embedtransfer.hxx> +#include <DrawDocShell.hxx> +#include <View.hxx> +#include <sdmod.hxx> +#include <sdpage.hxx> +#include <drawdoc.hxx> +#include <stlpool.hxx> +#include <sdxfer.hxx> +#include <unomodel.hxx> +#include <vcl/virdev.hxx> +#include <vcl/svapp.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::datatransfer; +using namespace ::com::sun::star::datatransfer::clipboard; + +constexpr sal_uInt32 SDTRANSFER_OBJECTTYPE_DRAWMODEL = 1; +constexpr sal_uInt32 SDTRANSFER_OBJECTTYPE_DRAWOLE = 2; + +SdTransferable::SdTransferable( SdDrawDocument* pSrcDoc, ::sd::View* pWorkView, bool bInitOnGetData ) +: mpPageDocShell( nullptr ) +, mpSdView( pWorkView ) +, mpSdViewIntern( pWorkView ) +, mpSdDrawDocument( nullptr ) +, mpSdDrawDocumentIntern( nullptr ) +, mpSourceDoc( pSrcDoc ) +, mpVDev( nullptr ) +, mbInternalMove( false ) +, mbOwnDocument( false ) +, mbOwnView( false ) +, mbLateInit( bInitOnGetData ) +, mbPageTransferable( false ) +, mbPageTransferablePersistent( false ) +{ + if( mpSourceDoc ) + StartListening( *mpSourceDoc ); + + if( pWorkView ) + StartListening( *pWorkView ); + + if( !mbLateInit ) + CreateData(); +} + +SdTransferable::~SdTransferable() +{ + SolarMutexGuard g; + + if( mpSourceDoc ) + EndListening( *mpSourceDoc ); + + if( mpSdView ) + EndListening( *const_cast< sd::View *>( mpSdView) ); + + ObjectReleased(); + + if( mbOwnView ) + delete mpSdViewIntern; + + mpOLEDataHelper.reset(); + + if( maDocShellRef.is() ) + { + SfxObjectShell* pObj = maDocShellRef.get(); + ::sd::DrawDocShell* pDocSh = static_cast< ::sd::DrawDocShell*>(pObj); + pDocSh->DoClose(); + } + + maDocShellRef.clear(); + + if( mbOwnDocument ) + delete mpSdDrawDocumentIntern; + + mpGraphic.reset(); + mpBookmark.reset(); + mpImageMap.reset(); + + mpVDev.disposeAndClear(); + mpObjDesc.reset(); + + //call explicitly at end of dtor to be covered by above SolarMutex + maUserData.clear(); +} + +void SdTransferable::CreateObjectReplacement( SdrObject* pObj ) +{ + if( !pObj ) + return; + + mpOLEDataHelper.reset(); + mpGraphic.reset(); + mpBookmark.reset(); + mpImageMap.reset(); + + if( auto pOleObj = dynamic_cast< SdrOle2Obj* >( pObj ) ) + { + try + { + uno::Reference < embed::XEmbeddedObject > xObj = pOleObj->GetObjRef(); + uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY ); + if( xObj.is() && xPersist.is() && xPersist->hasEntry() ) + { + mpOLEDataHelper.reset( new TransferableDataHelper( new SvEmbedTransferHelper( xObj, pOleObj->GetGraphic(), pOleObj->GetAspect() ) ) ); + + // TODO/LATER: the standalone handling of the graphic should not be used any more in future + // The EmbedDataHelper should bring the graphic in future + const Graphic* pObjGr = pOleObj->GetGraphic(); + if ( pObjGr ) + mpGraphic.reset( new Graphic( *pObjGr ) ); + } + } + catch( uno::Exception& ) + {} + } + else if( dynamic_cast< const SdrGrafObj *>( pObj ) != nullptr && (mpSourceDoc && !SdDrawDocument::GetAnimationInfo( pObj )) ) + { + mpGraphic.reset( new Graphic( static_cast< SdrGrafObj* >( pObj )->GetTransformedGraphic() ) ); + } + else if( pObj->IsUnoObj() && SdrInventor::FmForm == pObj->GetObjInventor() && ( pObj->GetObjIdentifier() == SdrObjKind::FormButton ) ) + { + SdrUnoObj* pUnoCtrl = static_cast< SdrUnoObj* >( pObj ); + + if (SdrInventor::FmForm == pUnoCtrl->GetObjInventor()) + { + const Reference< css::awt::XControlModel >& xControlModel( pUnoCtrl->GetUnoControlModel() ); + + if( !xControlModel.is() ) + return; + + Reference< css::beans::XPropertySet > xPropSet( xControlModel, UNO_QUERY ); + + if( !xPropSet.is() ) + return; + + css::form::FormButtonType eButtonType; + Any aTmp( xPropSet->getPropertyValue( "ButtonType" ) ); + + if( aTmp >>= eButtonType ) + { + OUString aLabel, aURL; + + xPropSet->getPropertyValue( "Label" ) >>= aLabel; + xPropSet->getPropertyValue( "TargetURL" ) >>= aURL; + + mpBookmark.reset( new INetBookmark( aURL, aLabel ) ); + } + } + } + else if( auto pTextObj = dynamic_cast< SdrTextObj *>( pObj ) ) + { + const OutlinerParaObject* pPara; + + if( (pPara = pTextObj->GetOutlinerParaObject()) != nullptr ) + { + const SvxFieldItem* pField; + + if( (pField = pPara->GetTextObject().GetField()) != nullptr ) + { + const SvxFieldData* pData = pField->GetField(); + + if( auto pURL = dynamic_cast< const SvxURLField *>( pData ) ) + { + // #i63399# This special code identifies TextFrames which have just a URL + // as content and directly add this to the clipboard, probably to avoid adding + // an unnecessary DrawObject to the target where paste may take place. This is + // wanted only for SdrObjects with no fill and no line, else it is necessary to + // use the whole SdrObject. Test here for Line/FillStyle and take shortcut only + // when both are unused + if(!pObj->HasFillStyle() && !pObj->HasLineStyle()) + { + mpBookmark.reset( new INetBookmark( pURL->GetURL(), pURL->GetRepresentation() ) ); + } + } + } + } + } + + SvxIMapInfo* pInfo = SvxIMapInfo::GetIMapInfo( pObj ); + + if( pInfo ) + mpImageMap.reset( new ImageMap( pInfo->GetImageMap() ) ); +} + +void SdTransferable::CreateData() +{ + if( mpSdDrawDocument && !mpSdViewIntern ) + { + mbOwnView = true; + + SdPage* pPage = mpSdDrawDocument->GetSdPage(0, PageKind::Standard); + + if( pPage && 1 == pPage->GetObjCount() ) + CreateObjectReplacement( pPage->GetObj( 0 ) ); + + mpVDev = VclPtr<VirtualDevice>::Create( *Application::GetDefaultDevice() ); + mpVDev->SetMapMode( MapMode( mpSdDrawDocumentIntern->GetScaleUnit(), Point(), mpSdDrawDocumentIntern->GetScaleFraction(), mpSdDrawDocumentIntern->GetScaleFraction() ) ); + mpSdViewIntern = new ::sd::View( *mpSdDrawDocumentIntern, mpVDev ); + mpSdViewIntern->EndListening(*mpSdDrawDocumentIntern ); + mpSdViewIntern->hideMarkHandles(); + SdrPageView* pPageView = mpSdViewIntern->ShowSdrPage(pPage); + mpSdViewIntern->MarkAllObj(pPageView); + } + else if( mpSdView && !mpSdDrawDocumentIntern ) + { + const SdrMarkList& rMarkList = mpSdView->GetMarkedObjectList(); + + if( rMarkList.GetMarkCount() == 1 ) + CreateObjectReplacement( rMarkList.GetMark( 0 )->GetMarkedSdrObj() ); + + if( mpSourceDoc ) + mpSourceDoc->CreatingDataObj(this); + mpSdDrawDocumentIntern = static_cast<SdDrawDocument*>( mpSdView->CreateMarkedObjModel().release() ); + if( mpSourceDoc ) + mpSourceDoc->CreatingDataObj(nullptr); + + if( !maDocShellRef.is() && mpSdDrawDocumentIntern->GetDocSh() ) + maDocShellRef = mpSdDrawDocumentIntern->GetDocSh(); + + if( !maDocShellRef.is() ) + { + OSL_FAIL( "SdTransferable::CreateData(), failed to create a model with persist, clipboard operation will fail for OLE objects!" ); + mbOwnDocument = true; + } + + // Use dimension of source page + SdrPageView* pPgView = mpSdView->GetSdrPageView(); + SdPage* pOldPage = static_cast<SdPage*>( pPgView->GetPage() ); + SdrModel* pOldModel = mpSdView->GetModel(); + SdStyleSheetPool* pOldStylePool = static_cast<SdStyleSheetPool*>( pOldModel->GetStyleSheetPool() ); + SdStyleSheetPool* pNewStylePool = static_cast<SdStyleSheetPool*>( mpSdDrawDocumentIntern->GetStyleSheetPool() ); + SdPage* pPage = mpSdDrawDocumentIntern->GetSdPage( 0, PageKind::Standard ); + OUString aOldLayoutName( pOldPage->GetLayoutName() ); + + pPage->SetSize( pOldPage->GetSize() ); + pPage->SetLayoutName( aOldLayoutName ); + pNewStylePool->CopyGraphicSheets( *pOldStylePool ); + pNewStylePool->CopyCellSheets( *pOldStylePool ); + pNewStylePool->CopyTableStyles( *pOldStylePool ); + sal_Int32 nPos = aOldLayoutName.indexOf( SD_LT_SEPARATOR ); + if( nPos != -1 ) + aOldLayoutName = aOldLayoutName.copy( 0, nPos ); + StyleSheetCopyResultVector aCreatedSheets; + pNewStylePool->CopyLayoutSheets( aOldLayoutName, *pOldStylePool, aCreatedSheets ); + } + + // set VisArea and adjust objects if necessary + if( !(maVisArea.IsEmpty() && + mpSdDrawDocumentIntern && mpSdViewIntern && + mpSdDrawDocumentIntern->GetPageCount()) ) + return; + + SdPage* pPage = mpSdDrawDocumentIntern->GetSdPage( 0, PageKind::Standard ); + + if( 1 == mpSdDrawDocumentIntern->GetPageCount() ) + { + // #112978# need to use GetAllMarkedBoundRect instead of GetAllMarkedRect to get + // fat lines correctly + maVisArea = mpSdViewIntern->GetAllMarkedBoundRect(); + Point aOrigin( maVisArea.TopLeft() ); + Size aVector( -aOrigin.X(), -aOrigin.Y() ); + + for( size_t nObj = 0, nObjCount = pPage->GetObjCount(); nObj < nObjCount; ++nObj ) + { + SdrObject* pObj = pPage->GetObj( nObj ); + pObj->NbcMove( aVector ); + } + } + else + maVisArea.SetSize( pPage->GetSize() ); + + // output is at the zero point + maVisArea.SetPos( Point() ); +} + +static bool lcl_HasOnlyControls( SdrModel* pModel ) +{ + bool bOnlyControls = false; // default if there are no objects + + if ( pModel ) + { + SdrPage* pPage = pModel->GetPage(0); + if (pPage) + { + SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups ); + SdrObject* pObj = aIter.Next(); + if ( pObj ) + { + bOnlyControls = true; // only set if there are any objects at all + while ( pObj ) + { + if (dynamic_cast< const SdrUnoObj *>( pObj ) == nullptr) + { + bOnlyControls = false; + break; + } + pObj = aIter.Next(); + } + } + } + } + + return bOnlyControls; +} + +static bool lcl_HasOnlyOneTable( SdrModel* pModel ) +{ + if ( pModel ) + { + SdrPage* pPage = pModel->GetPage(0); + if (pPage && pPage->GetObjCount() == 1 ) + { + if( dynamic_cast< sdr::table::SdrTableObj* >( pPage->GetObj(0) ) != nullptr ) + return true; + } + } + return false; +} + +void SdTransferable::AddSupportedFormats() +{ + if( mbPageTransferable && !mbPageTransferablePersistent ) + return; + + if( !mbLateInit ) + CreateData(); + + if( mpObjDesc ) + AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ); + + if( mpOLEDataHelper ) + { + AddFormat( SotClipboardFormatId::EMBED_SOURCE ); + + DataFlavorExVector aVector( mpOLEDataHelper->GetDataFlavorExVector() ); + + for( const auto& rItem : aVector ) + AddFormat( rItem ); + } + else if( mpGraphic ) + { + // #i25616# + AddFormat( SotClipboardFormatId::DRAWING ); + + AddFormat( SotClipboardFormatId::SVXB ); + + if( mpGraphic->GetType() == GraphicType::Bitmap ) + { + AddFormat( SotClipboardFormatId::PNG ); + AddFormat( SotClipboardFormatId::BITMAP ); + AddFormat( SotClipboardFormatId::GDIMETAFILE ); + } + else + { + AddFormat( SotClipboardFormatId::GDIMETAFILE ); + AddFormat( SotClipboardFormatId::PNG ); + AddFormat( SotClipboardFormatId::BITMAP ); + } + } + else if( mpBookmark ) + { + AddFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ); + AddFormat( SotClipboardFormatId::STRING ); + } + else + { + AddFormat( SotClipboardFormatId::EMBED_SOURCE ); + AddFormat( SotClipboardFormatId::DRAWING ); + if( !mpSdDrawDocument || !lcl_HasOnlyControls( mpSdDrawDocument ) ) + { + AddFormat( SotClipboardFormatId::GDIMETAFILE ); + AddFormat( SotClipboardFormatId::PNG ); + AddFormat( SotClipboardFormatId::BITMAP ); + } + + if( lcl_HasOnlyOneTable( mpSdDrawDocument ) ) { + AddFormat( SotClipboardFormatId::RTF ); + AddFormat( SotClipboardFormatId::RICHTEXT ); + } + } + + if( mpImageMap ) + AddFormat( SotClipboardFormatId::SVIM ); +} + +bool SdTransferable::GetData( const DataFlavor& rFlavor, const OUString& rDestDoc ) +{ + if (SD_MOD()==nullptr) + return false; + + SotClipboardFormatId nFormat = SotExchange::GetFormat( rFlavor ); + bool bOK = false; + + CreateData(); + + if( nFormat == SotClipboardFormatId::RTF && lcl_HasOnlyOneTable( mpSdDrawDocument ) ) + { + bOK = SetTableRTF( mpSdDrawDocument ); + } + else if( mpOLEDataHelper && mpOLEDataHelper->HasFormat( rFlavor ) ) + { + // TODO/LATER: support all the graphical formats, the embedded object scenario should not have separated handling + if( nFormat == SotClipboardFormatId::GDIMETAFILE && mpGraphic ) + bOK = SetGDIMetaFile( mpGraphic->GetGDIMetaFile() ); + else + bOK = SetAny( mpOLEDataHelper->GetAny(rFlavor, rDestDoc) ); + } + else if( HasFormat( nFormat ) ) + { + if( ( nFormat == SotClipboardFormatId::LINKSRCDESCRIPTOR || nFormat == SotClipboardFormatId::OBJECTDESCRIPTOR ) && mpObjDesc ) + { + bOK = SetTransferableObjectDescriptor( *mpObjDesc ); + } + else if( nFormat == SotClipboardFormatId::DRAWING ) + { + SfxObjectShellRef aOldRef( maDocShellRef ); + + maDocShellRef.clear(); + + if( mpSdViewIntern ) + { + SdDrawDocument& rInternDoc = mpSdViewIntern->GetDoc(); + rInternDoc.CreatingDataObj(this); + SdDrawDocument* pDoc = dynamic_cast< SdDrawDocument* >( mpSdViewIntern->CreateMarkedObjModel().release() ); + rInternDoc.CreatingDataObj(nullptr); + + bOK = SetObject( pDoc, SDTRANSFER_OBJECTTYPE_DRAWMODEL, rFlavor ); + + if( maDocShellRef.is() ) + { + maDocShellRef->DoClose(); + } + else + { + delete pDoc; + } + } + + maDocShellRef = aOldRef; + } + else if( nFormat == SotClipboardFormatId::GDIMETAFILE ) + { + if (mpSdViewIntern) + { + const bool bToggleOnlineSpell = mpSdDrawDocumentIntern && mpSdDrawDocumentIntern->GetOnlineSpell(); + if (bToggleOnlineSpell) + mpSdDrawDocumentIntern->SetOnlineSpell(false); + bOK = SetGDIMetaFile( mpSdViewIntern->GetMarkedObjMetaFile( true ) ); + if (bToggleOnlineSpell) + mpSdDrawDocumentIntern->SetOnlineSpell(true); + } + } + else if( SotClipboardFormatId::BITMAP == nFormat || SotClipboardFormatId::PNG == nFormat ) + { + if (mpSdViewIntern) + { + const bool bToggleOnlineSpell = mpSdDrawDocumentIntern && mpSdDrawDocumentIntern->GetOnlineSpell(); + if (bToggleOnlineSpell) + mpSdDrawDocumentIntern->SetOnlineSpell(false); + bOK = SetBitmapEx( mpSdViewIntern->GetMarkedObjBitmapEx(true), rFlavor ); + if (bToggleOnlineSpell) + mpSdDrawDocumentIntern->SetOnlineSpell(true); + } + } + else if( ( nFormat == SotClipboardFormatId::STRING ) && mpBookmark ) + { + bOK = SetString( mpBookmark->GetURL() ); + } + else if( ( nFormat == SotClipboardFormatId::SVXB ) && mpGraphic ) + { + bOK = SetGraphic( *mpGraphic ); + } + else if( ( nFormat == SotClipboardFormatId::SVIM ) && mpImageMap ) + { + bOK = SetImageMap( *mpImageMap ); + } + else if( mpBookmark ) + { + bOK = SetINetBookmark( *mpBookmark, rFlavor ); + } + else if( nFormat == SotClipboardFormatId::EMBED_SOURCE ) + { + if( mpSdDrawDocumentIntern ) + { + if( !maDocShellRef.is() ) + { + maDocShellRef = new ::sd::DrawDocShell( + mpSdDrawDocumentIntern, + SfxObjectCreateMode::EMBEDDED, + true, + mpSdDrawDocumentIntern->GetDocumentType()); + mbOwnDocument = false; + maDocShellRef->DoInitNew(); + } + + maDocShellRef->SetVisArea( maVisArea ); + bOK = SetObject( maDocShellRef.get(), SDTRANSFER_OBJECTTYPE_DRAWOLE, rFlavor ); + } + } + } + + return bOK; +} + +bool SdTransferable::WriteObject( tools::SvRef<SotTempStream>& rxOStm, void* pObject, sal_uInt32 nObjectType, const DataFlavor& ) +{ + bool bRet = false; + + switch( nObjectType ) + { + case SDTRANSFER_OBJECTTYPE_DRAWMODEL: + { + try + { + static const bool bDontBurnInStyleSheet = ( getenv( "AVOID_BURN_IN_FOR_GALLERY_THEME" ) != nullptr ); + SdDrawDocument* pDoc = static_cast<SdDrawDocument*>(pObject); + if ( !bDontBurnInStyleSheet ) + pDoc->BurnInStyleSheetAttributes(); + rxOStm->SetBufferSize( 16348 ); + + Reference< XComponent > xComponent( new SdXImpressDocument( pDoc, true ) ); + pDoc->setUnoModel( Reference< XInterface >::query( xComponent ) ); + + { + css::uno::Reference<css::io::XOutputStream> xDocOut( new utl::OOutputStreamWrapper( *rxOStm ) ); + SvxDrawingLayerExport( pDoc, xDocOut, xComponent, (pDoc->GetDocumentType() == DocumentType::Impress) ? "com.sun.star.comp.Impress.XMLClipboardExporter" : "com.sun.star.comp.DrawingLayer.XMLExporter" ); + } + + xComponent->dispose(); + bRet = ( rxOStm->GetError() == ERRCODE_NONE ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SdTransferable::WriteObject()" ); + bRet = false; + } + } + break; + + case SDTRANSFER_OBJECTTYPE_DRAWOLE: + { + SfxObjectShell* pEmbObj = static_cast<SfxObjectShell*>(pObject); + ::utl::TempFile aTempFile; + aTempFile.EnableKillingFile(); + + try + { + uno::Reference< embed::XStorage > xWorkStore = + ::comphelper::OStorageHelper::GetStorageFromURL( aTempFile.GetURL(), embed::ElementModes::READWRITE ); + + // write document storage + pEmbObj->SetupStorage( xWorkStore, SOFFICE_FILEFORMAT_CURRENT, false ); + // mba: no relative URLs for clipboard! + SfxMedium aMedium( xWorkStore, OUString() ); + pEmbObj->DoSaveObjectAs( aMedium, false ); + pEmbObj->DoSaveCompleted(); + + uno::Reference< embed::XTransactedObject > xTransact( xWorkStore, uno::UNO_QUERY ); + if ( xTransact.is() ) + xTransact->commit(); + + std::unique_ptr<SvStream> pSrcStm = ::utl::UcbStreamHelper::CreateStream( aTempFile.GetURL(), StreamMode::READ ); + if( pSrcStm ) + { + rxOStm->SetBufferSize( 0xff00 ); + rxOStm->WriteStream( *pSrcStm ); + pSrcStm.reset(); + } + + bRet = true; + } + catch ( Exception& ) + {} + } + + break; + + default: + break; + } + + return bRet; +} + +void SdTransferable::DragFinished( sal_Int8 nDropAction ) +{ + if( mpSdView ) + const_cast< ::sd::View* >(mpSdView)->DragFinished( nDropAction ); +} + +void SdTransferable::ObjectReleased() +{ + SdModule *pModule = SD_MOD(); + if (!pModule) + return; + + if( this == pModule->pTransferClip ) + pModule->pTransferClip = nullptr; + + if( this == pModule->pTransferDrag ) + pModule->pTransferDrag = nullptr; + + if( this == pModule->pTransferSelection ) + pModule->pTransferSelection = nullptr; +} + +void SdTransferable::SetObjectDescriptor( std::unique_ptr<TransferableObjectDescriptor> pObjDesc ) +{ + mpObjDesc = std::move(pObjDesc); + PrepareOLE( *mpObjDesc ); +} + +void SdTransferable::SetPageBookmarks( std::vector<OUString> && rPageBookmarks, bool bPersistent ) +{ + if( !mpSourceDoc ) + return; + + if( mpSdViewIntern ) + mpSdViewIntern->HideSdrPage(); + + mpSdDrawDocument->ClearModel(false); + + mpPageDocShell = nullptr; + + maPageBookmarks.clear(); + + if( bPersistent ) + { + mpSdDrawDocument->CreateFirstPages(mpSourceDoc); + mpSdDrawDocument->InsertBookmarkAsPage( rPageBookmarks, nullptr, false, true, 1, true, + mpSourceDoc->GetDocSh(), true, true, false ); + } + else + { + mpPageDocShell = mpSourceDoc->GetDocSh(); + maPageBookmarks = std::move(rPageBookmarks); + } + + if( mpSdViewIntern ) + { + SdPage* pPage = mpSdDrawDocument->GetSdPage( 0, PageKind::Standard ); + + if( pPage ) + { + mpSdViewIntern->MarkAllObj( mpSdViewIntern->ShowSdrPage( pPage ) ); + } + } + + // set flags for page transferable; if ( mbPageTransferablePersistent == sal_False ), + // don't offer any formats => it's just for internal purposes + mbPageTransferable = true; + mbPageTransferablePersistent = bPersistent; +} + +sal_Int64 SAL_CALL SdTransferable::getSomething( const css::uno::Sequence< sal_Int8 >& rId ) +{ + return comphelper::getSomethingImpl(rId, this); +} + +void SdTransferable::AddUserData (const std::shared_ptr<UserData>& rpData) +{ + maUserData.push_back(rpData); +} + +sal_Int32 SdTransferable::GetUserDataCount() const +{ + return maUserData.size(); +} + +std::shared_ptr<SdTransferable::UserData> SdTransferable::GetUserData (const sal_Int32 nIndex) const +{ + if (nIndex>=0 && o3tl::make_unsigned(nIndex)<maUserData.size()) + return maUserData[nIndex]; + else + return std::shared_ptr<UserData>(); +} + +const css::uno::Sequence< sal_Int8 >& SdTransferable::getUnoTunnelId() +{ + static const comphelper::UnoIdInit theSdTransferableUnoTunnelId; + return theSdTransferableUnoTunnelId.getSeq(); +} + +SdTransferable* SdTransferable::getImplementation( const Reference< XInterface >& rxData ) noexcept +{ + try + { + return comphelper::getFromUnoTunnel<SdTransferable>(rxData); + } + catch( const css::uno::Exception& ) + { + } + return nullptr; +} + +void SdTransferable::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) +{ + if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint) + { + const SdrHint* pSdrHint = static_cast< const SdrHint* >( &rHint ); + if( SdrHintKind::ModelCleared == pSdrHint->GetKind() ) + { + EndListening(*mpSourceDoc); + mpSourceDoc = nullptr; + } + } + else + { + if( rHint.GetId() == SfxHintId::Dying ) + { + if( &rBC == mpSourceDoc ) + mpSourceDoc = nullptr; + if( &rBC == mpSdViewIntern ) + mpSdViewIntern = nullptr; + if( &rBC == mpSdView ) + mpSdView = nullptr; + } + } +} + +void SdTransferable::SetView(const ::sd::View* pView) +{ + if (mpSdView) + EndListening(*const_cast<sd::View*>(mpSdView)); + mpSdView = pView; + if (mpSdView) + StartListening(*const_cast<sd::View*>(mpSdView)); +} + +bool SdTransferable::SetTableRTF( SdDrawDocument* pModel ) +{ + if ( pModel ) + { + SdrPage* pPage = pModel->GetPage(0); + if (pPage && pPage->GetObjCount() == 1 ) + { + sdr::table::SdrTableObj* pTableObj = dynamic_cast< sdr::table::SdrTableObj* >( pPage->GetObj(0) ); + if( pTableObj ) + { + SvMemoryStream aMemStm( 65535, 65535 ); + sdr::table::ExportAsRTF( aMemStm, *pTableObj ); + return SetAny( Any( Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemStm.GetData() ), aMemStm.TellEnd() ) ) ); + } + } + } + + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/app/tmplctrl.cxx b/sd/source/ui/app/tmplctrl.cxx new file mode 100644 index 000000000..1f645bf66 --- /dev/null +++ b/sd/source/ui/app/tmplctrl.cxx @@ -0,0 +1,110 @@ +/* -*- 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/commandevent.hxx> +#include <vcl/status.hxx> +#include <vcl/weldutils.hxx> +#include <svl/stritem.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> + +#include <tmplctrl.hxx> +#include <ViewShellBase.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <sdattr.hrc> +#include <app.hrc> +#include <sdresid.hxx> +#include <strings.hrc> + +SFX_IMPL_STATUSBAR_CONTROL( SdTemplateControl, SfxStringItem ); + +// class SdTemplateControl ------------------------------------------ +SdTemplateControl::SdTemplateControl( sal_uInt16 _nSlotId, + sal_uInt16 _nId, + StatusBar& rStb ) : + SfxStatusBarControl( _nSlotId, _nId, rStb ) +{ + GetStatusBar().SetQuickHelpText(GetId(), SdResId(STR_STATUSBAR_MASTERPAGE)); +} + +SdTemplateControl::~SdTemplateControl() +{ +} + +void SdTemplateControl::StateChangedAtStatusBarControl( + sal_uInt16 /*nSID*/, SfxItemState eState, const SfxPoolItem* pState ) +{ + if( eState != SfxItemState::DEFAULT || pState->IsVoidItem() ) + GetStatusBar().SetItemText( GetId(), OUString() ); + else if ( auto pStringItem = dynamic_cast< const SfxStringItem *>( pState ) ) + { + msTemplate = pStringItem->GetValue(); + GetStatusBar().SetItemText( GetId(), msTemplate ); + } +} + +void SdTemplateControl::Paint( const UserDrawEvent& ) +{ +} + +void SdTemplateControl::Command( const CommandEvent& rCEvt ) +{ + if ( rCEvt.GetCommand() != CommandEventId::ContextMenu || GetStatusBar().GetItemText( GetId() ).isEmpty() ) + return; + + SfxViewFrame* pViewFrame = SfxViewFrame::Current(); + + sd::ViewShellBase* pViewShellBase = sd::ViewShellBase::GetViewShellBase( pViewFrame ); + if( !pViewShellBase ) + return; + + SdDrawDocument* pDoc = pViewShellBase->GetDocument(); + if( !pDoc ) + return; + + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, "modules/simpress/ui/masterpagemenu.ui")); + std::unique_ptr<weld::Menu> xPopup(xBuilder->weld_menu("menu")); + + const sal_uInt16 nMasterCount = pDoc->GetMasterSdPageCount(PageKind::Standard); + + for (sal_uInt16 nPage = 0; nPage < nMasterCount; ++nPage) + { + SdPage* pMaster = pDoc->GetMasterSdPage(nPage, PageKind::Standard); + if (!pMaster) + continue; + xPopup->append(OUString::number(nPage), pMaster->GetName()); + } + + ::tools::Rectangle aRect(rCEvt.GetMousePosPixel(), Size(1, 1)); + weld::Window* pParent = weld::GetPopupParent(GetStatusBar(), aRect); + OString sResult = xPopup->popup_at_rect(pParent, aRect); + if (!sResult.isEmpty()) + { + sal_uInt16 nCurrId = sResult.toUInt32(); + SdPage* pMaster = pDoc->GetMasterSdPage(nCurrId, PageKind::Standard); + SfxStringItem aStyle( ATTR_PRESLAYOUT_NAME, pMaster->GetName() ); + pViewFrame->GetDispatcher()->ExecuteList( + SID_PRESENTATION_LAYOUT, SfxCallMode::SLOT, { &aStyle }); + pViewFrame->GetBindings().Invalidate(SID_PRESENTATION_LAYOUT); + pViewFrame->GetBindings().Invalidate(SID_STATUS_LAYOUT); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/controller/displaymodecontroller.cxx b/sd/source/ui/controller/displaymodecontroller.cxx new file mode 100644 index 000000000..81ad2d19e --- /dev/null +++ b/sd/source/ui/controller/displaymodecontroller.cxx @@ -0,0 +1,264 @@ +/* -*- 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/. + */ + +#include <svtools/popupwindowcontroller.hxx> +#include <svtools/toolbarmenu.hxx> +#include <svtools/valueset.hxx> +#include <vcl/toolbox.hxx> + +#include <strings.hrc> + +#include <bitmaps.hlst> +#include <sdresid.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::beans; + +namespace sd +{ + +// Component to select which display mode has to be used. +// Composed of a dropdown button in the toolbar and a +// popup menu to select the value + +namespace { + +class DisplayModeController : public svt::PopupWindowController +{ +public: + explicit DisplayModeController( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + + virtual std::unique_ptr<WeldToolbarPopup> weldPopupWindow() override; + virtual VclPtr<vcl::Window> createVclPopupWindow( vcl::Window* pParent ) override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + void setToolboxItemImage(const OUString& rImage); +}; + +class DisplayModeToolbarMenu final : public WeldToolbarPopup +{ +public: + DisplayModeToolbarMenu(DisplayModeController* pControl, weld::Widget* pParent); + virtual void GrabFocus() override + { + mxDisplayModeSet1->GrabFocus(); + } + +private: + rtl::Reference<DisplayModeController> mxControl; + std::unique_ptr<weld::Frame> mxFrame1; + std::unique_ptr<ValueSet> mxDisplayModeSet1; + std::unique_ptr<weld::CustomWeld> mxDisplayModeSetWin1; + std::unique_ptr<weld::Frame> mxFrame2; + std::unique_ptr<ValueSet> mxDisplayModeSet2; + std::unique_ptr<weld::CustomWeld> mxDisplayModeSetWin2; + + DECL_LINK(SelectValueSetHdl, ValueSet*, void); +}; + +struct snew_slide_value_info +{ + sal_uInt16 mnId; + OUString msBmpResId; + TranslateId mpStrResId; + const char* msUnoCommand; +}; + +} + +const snew_slide_value_info editmodes[] = +{ + {1, + BMP_DISPLAYMODE_SLIDE, + STR_NORMAL_MODE, + ".uno:NormalMultiPaneGUI" }, + {2, + BMP_DISPLAYMODE_OUTLINE, + STR_OUTLINE_MODE, + ".uno:OutlineMode" }, + {3, + BMP_DISPLAYMODE_NOTES, + STR_NOTES_MODE, + ".uno:NotesMode" }, + {4, + BMP_DISPLAYMODE_SLIDE_SORTER, + STR_SLIDE_SORTER_MODE, + ".uno:DiaMode" }, + {0, "", {}, "" } +}; + +const snew_slide_value_info mastermodes[] = +{ + {5, + BMP_DISPLAYMODE_SLIDE_MASTER, + STR_SLIDE_MASTER_MODE, + ".uno:SlideMasterPage" }, + {6, + BMP_DISPLAYMODE_NOTES_MASTER, + STR_NOTES_MASTER_MODE, + ".uno:NotesMasterPage" }, + {7, + BMP_DISPLAYMODE_HANDOUT_MASTER, + STR_HANDOUT_MASTER_MODE, + ".uno:HandoutMode" }, + {0, "", {}, "" } +}; + + +static void fillLayoutValueSet(ValueSet* pValue, const snew_slide_value_info* pInfo) +{ + Size aLayoutItemSize; + for( ; pInfo->mnId; pInfo++ ) + { + OUString aText(SdResId(pInfo->mpStrResId)); + BitmapEx aBmp(pInfo->msBmpResId); + + pValue->InsertItem(pInfo->mnId, Image(aBmp), aText); + + aLayoutItemSize.setWidth( std::max( aLayoutItemSize.Width(), aBmp.GetSizePixel().Width() ) ); + aLayoutItemSize.setHeight( std::max( aLayoutItemSize.Height(), aBmp.GetSizePixel().Height() ) ); + } + + aLayoutItemSize = pValue->CalcItemSizePixel( aLayoutItemSize ); + Size aSize(pValue->CalcWindowSizePixel(aLayoutItemSize)); + + const sal_Int32 LAYOUT_BORDER_PIX = 7; + aSize.AdjustWidth((pValue->GetColCount() + 1) * LAYOUT_BORDER_PIX ); + aSize.AdjustHeight((pValue->GetLineCount() +1) * LAYOUT_BORDER_PIX ); + + pValue->GetDrawingArea()->set_size_request(aSize.Width(), aSize.Height()); + pValue->SetOutputSizePixel(aSize); +} + +DisplayModeToolbarMenu::DisplayModeToolbarMenu(DisplayModeController* pControl, weld::Widget* pParent) + : WeldToolbarPopup(pControl->getFrameInterface(), pParent, "modules/simpress/ui/displaywindow.ui", "DisplayWindow") + , mxControl(pControl) + , mxFrame1(m_xBuilder->weld_frame("editframe")) + , mxDisplayModeSet1(new ValueSet(nullptr)) + , mxDisplayModeSetWin1(new weld::CustomWeld(*m_xBuilder, "valueset1", *mxDisplayModeSet1)) + , mxFrame2(m_xBuilder->weld_frame("masterframe")) + , mxDisplayModeSet2(new ValueSet(nullptr)) + , mxDisplayModeSetWin2(new weld::CustomWeld(*m_xBuilder, "valueset2", *mxDisplayModeSet2)) +{ + mxDisplayModeSet1->SetStyle(WB_TABSTOP | WB_MENUSTYLEVALUESET | WB_FLATVALUESET | WB_NOBORDER | WB_NO_DIRECTSELECT); + mxDisplayModeSet1->SetStyle(WB_TABSTOP | WB_MENUSTYLEVALUESET | WB_FLATVALUESET | WB_NOBORDER | WB_NO_DIRECTSELECT); + + mxDisplayModeSet1->SetSelectHdl( LINK( this, DisplayModeToolbarMenu, SelectValueSetHdl ) ); + mxDisplayModeSet2->SetSelectHdl( LINK( this, DisplayModeToolbarMenu, SelectValueSetHdl ) ); + + sal_Int16 nColCount = 2; + + mxDisplayModeSet1->SetColCount( nColCount ); + fillLayoutValueSet( mxDisplayModeSet1.get(), &editmodes[0] ); + + mxDisplayModeSet2->SetColCount( nColCount ); + fillLayoutValueSet( mxDisplayModeSet2.get(), &mastermodes[0] ); +} + +IMPL_LINK( DisplayModeToolbarMenu, SelectValueSetHdl, ValueSet*, pControl, void ) +{ + OUString sCommandURL; + OUString sImage; + + if( pControl == mxDisplayModeSet1.get() ) { + sCommandURL = OUString::createFromAscii(editmodes[mxDisplayModeSet1->GetSelectedItemId() - 1 ].msUnoCommand); + sImage = editmodes[mxDisplayModeSet1->GetSelectedItemId() - 1 ].msBmpResId; + } + else if( pControl == mxDisplayModeSet2.get() ) { + sCommandURL = OUString::createFromAscii(mastermodes[mxDisplayModeSet2->GetSelectedItemId() - 5 ].msUnoCommand); + sImage = mastermodes[mxDisplayModeSet2->GetSelectedItemId() - 5 ].msBmpResId; + } + + if (!sCommandURL.isEmpty()) + mxControl->dispatchCommand( sCommandURL, Sequence< PropertyValue >() ); + + mxControl->setToolboxItemImage(sImage); + mxControl->EndPopupMode(); +} + +DisplayModeController::DisplayModeController( const css::uno::Reference< css::uno::XComponentContext >& rxContext ) +: svt::PopupWindowController( rxContext, Reference< frame::XFrame >(), OUString() ) +{ +} + +void SAL_CALL DisplayModeController::initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) +{ + svt::PopupWindowController::initialize( aArguments ); + ToolBox* pToolBox = nullptr; + ToolBoxItemId nId; + if ( getToolboxId( nId, &pToolBox ) ) + pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | ToolBoxItemBits::DROPDOWNONLY ); + setToolboxItemImage(BMP_DISPLAYMODE_SLIDE); +} + +std::unique_ptr<WeldToolbarPopup> DisplayModeController::weldPopupWindow() +{ + return std::make_unique<sd::DisplayModeToolbarMenu>(this, m_pToolbar); +} + +VclPtr<vcl::Window> DisplayModeController::createVclPopupWindow( vcl::Window* pParent ) +{ + mxInterimPopover = VclPtr<InterimToolbarPopup>::Create(getFrameInterface(), pParent, + std::make_unique<sd::DisplayModeToolbarMenu>(this, pParent->GetFrameWeld())); + + mxInterimPopover->Show(); + + return mxInterimPopover; +} + +void DisplayModeController::setToolboxItemImage(const OUString& rImage) +{ + ToolBoxItemId nId; + ToolBox* pToolBox = nullptr; + if (!getToolboxId( nId, &pToolBox )) + return; + + BitmapEx aBmp(rImage); + int targetSize = (pToolBox->GetToolboxButtonSize() == ToolBoxButtonSize::Large) ? 32 : 16; + double scale = 1.0f; + Size size = aBmp.GetSizePixel(); + if (size.Width() > targetSize) + scale = static_cast<double>(targetSize) / static_cast<double>(size.Width()); + if (size.Height() > targetSize) + scale = ::std::min( scale, static_cast<double>(targetSize) / static_cast<double>(size.Height()) ); + aBmp.Scale( scale, scale ); + pToolBox->SetItemImage( nId, Image( aBmp ) ); +} + +// XServiceInfo + +OUString SAL_CALL DisplayModeController::getImplementationName() +{ + return "com.sun.star.comp.sd.DisplayModeController"; +} + +Sequence< OUString > SAL_CALL DisplayModeController::getSupportedServiceNames( ) +{ + css::uno::Sequence<OUString> aRet { "com.sun.star.frame.ToolbarController" }; + return aRet; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT ::com::sun::star::uno::XInterface* +com_sun_star_comp_sd_DisplayModeController_get_implementation( css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new sd::DisplayModeController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/controller/slidelayoutcontroller.cxx b/sd/source/ui/controller/slidelayoutcontroller.cxx new file mode 100644 index 000000000..251548a22 --- /dev/null +++ b/sd/source/ui/controller/slidelayoutcontroller.cxx @@ -0,0 +1,380 @@ +/* -*- 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 <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/drawing/DrawViewMode.hpp> + +#include <comphelper/propertyvalue.hxx> +#include <vcl/commandinfoprovider.hxx> +#include <vcl/toolbox.hxx> + +#include <svl/cjkoptions.hxx> + +#include <svtools/toolbarmenu.hxx> +#include <svtools/valueset.hxx> + +#include <xmloff/autolayout.hxx> + +#include <strings.hrc> + +#include <bitmaps.hlst> +#include <sdresid.hxx> +#include "slidelayoutcontroller.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::beans; + +namespace sd +{ + +namespace { + +class LayoutToolbarMenu : public WeldToolbarPopup +{ +public: + LayoutToolbarMenu(SlideLayoutController* pController, weld::Widget* pParent, const bool bInsertPage, const OUString& rCommand); + virtual void GrabFocus() override + { + mxLayoutSet1->GrabFocus(); + } + +protected: + DECL_LINK(SelectToolbarMenuHdl, weld::Button&, void); + DECL_LINK(SelectValueSetHdl, ValueSet*, void); + void SelectHdl(AutoLayout eLayout); +private: + rtl::Reference<SlideLayoutController> mxControl; + bool const mbInsertPage; + std::unique_ptr<weld::Frame> mxFrame1; + std::unique_ptr<ValueSet> mxLayoutSet1; + std::unique_ptr<weld::CustomWeld> mxLayoutSetWin1; + std::unique_ptr<weld::Frame> mxFrame2; + std::unique_ptr<ValueSet> mxLayoutSet2; + std::unique_ptr<weld::CustomWeld> mxLayoutSetWin2; + std::unique_ptr<weld::Button> mxMoreButton; +}; + +struct snew_slide_value_info_layout +{ + rtl::OUStringConstExpr msBmpResId; + TranslateId mpStrResId; + AutoLayout maAutoLayout; +}; + +} + +constexpr OUStringLiteral EMPTY = u""; + +const snew_slide_value_info_layout notes[] = +{ + {BMP_SLIDEN_01, STR_AUTOLAYOUT_NOTES, AUTOLAYOUT_NOTES}, + {EMPTY, {}, AUTOLAYOUT_NONE}, +}; + +const snew_slide_value_info_layout handout[] = +{ + {BMP_SLIDEH_01, STR_AUTOLAYOUT_HANDOUT1, AUTOLAYOUT_HANDOUT1}, + {BMP_SLIDEH_02, STR_AUTOLAYOUT_HANDOUT2, AUTOLAYOUT_HANDOUT2}, + {BMP_SLIDEH_03, STR_AUTOLAYOUT_HANDOUT3, AUTOLAYOUT_HANDOUT3}, + {BMP_SLIDEH_04, STR_AUTOLAYOUT_HANDOUT4, AUTOLAYOUT_HANDOUT4}, + {BMP_SLIDEH_06, STR_AUTOLAYOUT_HANDOUT6, AUTOLAYOUT_HANDOUT6}, + {BMP_SLIDEH_09, STR_AUTOLAYOUT_HANDOUT9, AUTOLAYOUT_HANDOUT9}, + {EMPTY, {}, AUTOLAYOUT_NONE}, +}; + +const snew_slide_value_info_layout standard[] = +{ + {BMP_LAYOUT_EMPTY, STR_AUTOLAYOUT_NONE, AUTOLAYOUT_NONE }, + {BMP_LAYOUT_HEAD03, STR_AUTOLAYOUT_TITLE, AUTOLAYOUT_TITLE }, + {BMP_LAYOUT_HEAD02, STR_AUTOLAYOUT_CONTENT, AUTOLAYOUT_TITLE_CONTENT }, + {BMP_LAYOUT_HEAD02A, STR_AUTOLAYOUT_2CONTENT, AUTOLAYOUT_TITLE_2CONTENT }, + {BMP_LAYOUT_HEAD01, STR_AUTOLAYOUT_ONLY_TITLE, AUTOLAYOUT_TITLE_ONLY }, + {BMP_LAYOUT_TEXTONLY, STR_AUTOLAYOUT_ONLY_TEXT, AUTOLAYOUT_ONLY_TEXT }, + {BMP_LAYOUT_HEAD03B, STR_AUTOLAYOUT_2CONTENT_CONTENT, AUTOLAYOUT_TITLE_2CONTENT_CONTENT }, + {BMP_LAYOUT_HEAD03C, STR_AUTOLAYOUT_CONTENT_2CONTENT, AUTOLAYOUT_TITLE_CONTENT_2CONTENT }, + {BMP_LAYOUT_HEAD03A, STR_AUTOLAYOUT_2CONTENT_OVER_CONTENT,AUTOLAYOUT_TITLE_2CONTENT_OVER_CONTENT }, + {BMP_LAYOUT_HEAD02B, STR_AUTOLAYOUT_CONTENT_OVER_CONTENT, AUTOLAYOUT_TITLE_CONTENT_OVER_CONTENT }, + {BMP_LAYOUT_HEAD04, STR_AUTOLAYOUT_4CONTENT, AUTOLAYOUT_TITLE_4CONTENT }, + {BMP_LAYOUT_HEAD06, STR_AUTOLAYOUT_6CONTENT, AUTOLAYOUT_TITLE_6CONTENT }, + {EMPTY, {}, AUTOLAYOUT_NONE} +}; + +const snew_slide_value_info_layout v_standard[] = +{ + // vertical + {BMP_LAYOUT_VERTICAL02, STR_AL_VERT_TITLE_TEXT_CHART, AUTOLAYOUT_VTITLE_VCONTENT_OVER_VCONTENT }, + {BMP_LAYOUT_VERTICAL01, STR_AL_VERT_TITLE_VERT_OUTLINE, AUTOLAYOUT_VTITLE_VCONTENT }, + {BMP_LAYOUT_HEAD02, STR_AL_TITLE_VERT_OUTLINE, AUTOLAYOUT_TITLE_VCONTENT }, + {BMP_LAYOUT_HEAD02A, STR_AL_TITLE_VERT_OUTLINE_CLIPART, AUTOLAYOUT_TITLE_2VTEXT }, + {EMPTY, {}, AUTOLAYOUT_NONE} +}; + +static void fillLayoutValueSet( ValueSet* pValue, const snew_slide_value_info_layout* pInfo ) +{ + Size aLayoutItemSize; + for( ; pInfo->mpStrResId; pInfo++ ) + { + OUString aText(SdResId(pInfo->mpStrResId)); + Image aImg(StockImage::Yes, pInfo->msBmpResId); + pValue->InsertItem(static_cast<sal_uInt16>(pInfo->maAutoLayout)+1, aImg, aText); + aLayoutItemSize.setWidth( std::max( aLayoutItemSize.Width(), aImg.GetSizePixel().Width() ) ); + aLayoutItemSize.setHeight( std::max( aLayoutItemSize.Height(), aImg.GetSizePixel().Height() ) ); + } + + aLayoutItemSize = pValue->CalcItemSizePixel( aLayoutItemSize ); + Size aSize(pValue->CalcWindowSizePixel(aLayoutItemSize)); + + const sal_Int32 LAYOUT_BORDER_PIX = 7; + + aSize.AdjustWidth((pValue->GetColCount() + 1) * LAYOUT_BORDER_PIX); + aSize.AdjustHeight((pValue->GetLineCount() +1) * LAYOUT_BORDER_PIX); + + pValue->GetDrawingArea()->set_size_request(aSize.Width(), aSize.Height()); + pValue->SetOutputSizePixel(aSize); +} + +LayoutToolbarMenu::LayoutToolbarMenu(SlideLayoutController* pControl, weld::Widget* pParent, const bool bInsertPage, const OUString& rCommand) + : WeldToolbarPopup(pControl->getFrameInterface(), pParent, "modules/simpress/ui/layoutwindow.ui", "LayoutWindow") + , mxControl(pControl) + , mbInsertPage(bInsertPage) + , mxFrame1(m_xBuilder->weld_frame("horiframe")) + , mxLayoutSet1(new ValueSet(nullptr)) + , mxLayoutSetWin1(new weld::CustomWeld(*m_xBuilder, "valueset1", *mxLayoutSet1)) + , mxFrame2(m_xBuilder->weld_frame("vertframe")) + , mxLayoutSet2(new ValueSet(nullptr)) + , mxLayoutSetWin2(new weld::CustomWeld(*m_xBuilder, "valueset2", *mxLayoutSet2)) + , mxMoreButton(m_xBuilder->weld_button("more")) +{ + mxLayoutSet1->SetStyle(WB_TABSTOP | WB_MENUSTYLEVALUESET | WB_FLATVALUESET | WB_NOBORDER | WB_NO_DIRECTSELECT); + mxLayoutSet2->SetStyle(WB_TABSTOP | WB_MENUSTYLEVALUESET | WB_FLATVALUESET | WB_NOBORDER | WB_NO_DIRECTSELECT); + + DrawViewMode eMode = DrawViewMode_DRAW; + + // find out which view is running + if( m_xFrame.is() ) try + { + Reference< XPropertySet > xControllerSet( m_xFrame->getController(), UNO_QUERY_THROW ); + xControllerSet->getPropertyValue( "DrawViewMode" ) >>= eMode; + } + catch( Exception& ) + { + OSL_ASSERT(false); + } + + const bool bVerticalEnabled = SvtCJKOptions::IsVerticalTextEnabled(); + + mxLayoutSet1->SetSelectHdl( LINK( this, LayoutToolbarMenu, SelectValueSetHdl ) ); + + const snew_slide_value_info_layout* pInfo = nullptr; + sal_Int16 nColCount = 4; + switch( eMode ) + { + case DrawViewMode_DRAW: pInfo = &standard[0]; break; + case DrawViewMode_HANDOUT: pInfo = &handout[0]; nColCount = 2; break; + case DrawViewMode_NOTES: pInfo = ¬es[0]; nColCount = 1; break; + default: assert(false); // can't happen, will crash later otherwise + } + + mxLayoutSet1->SetColCount( nColCount ); + + fillLayoutValueSet( mxLayoutSet1.get(), pInfo ); + + bool bUseUILabel = (bVerticalEnabled && eMode == DrawViewMode_DRAW); + if (!bUseUILabel) + { + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rCommand, mxControl->getModuleName()); + mxFrame1->set_label(vcl::CommandInfoProvider::GetLabelForCommand(aProperties)); + } + + if (bVerticalEnabled && eMode == DrawViewMode_DRAW) + { + mxLayoutSet2->SetSelectHdl( LINK( this, LayoutToolbarMenu, SelectValueSetHdl ) ); + mxLayoutSet2->SetColCount( 4 ); + mxLayoutSet2->EnableFullItemMode( false ); + + fillLayoutValueSet( mxLayoutSet2.get(), &v_standard[0] ); + + mxFrame2->show(); + } + + if( eMode != DrawViewMode_DRAW ) + return; + + if( !m_xFrame.is() ) + return; + + OUString sSlotStr; + + if( bInsertPage ) + sSlotStr = ".uno:DuplicatePage"; + else + sSlotStr = ".uno:Undo"; + + css::uno::Reference<css::graphic::XGraphic> xSlotImage = vcl::CommandInfoProvider::GetXGraphicForCommand(sSlotStr, m_xFrame); + + OUString sSlotTitle; + if( bInsertPage ) + { + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(sSlotStr, mxControl->getModuleName()); + sSlotTitle = vcl::CommandInfoProvider::GetLabelForCommand(aProperties); + } + else + sSlotTitle = SdResId( STR_RESET_LAYOUT ); + + mxMoreButton->set_label(sSlotTitle); + mxMoreButton->set_image(xSlotImage); + mxMoreButton->connect_clicked(LINK(this, LayoutToolbarMenu, SelectToolbarMenuHdl)); + mxMoreButton->show(); +} + +IMPL_LINK(LayoutToolbarMenu, SelectValueSetHdl, ValueSet*, pLayoutSet, void) +{ + SelectHdl(static_cast<AutoLayout>(pLayoutSet->GetSelectedItemId()-1)); +} + +IMPL_LINK_NOARG(LayoutToolbarMenu, SelectToolbarMenuHdl, weld::Button&, void) +{ + SelectHdl(AUTOLAYOUT_END); +} + +void LayoutToolbarMenu::SelectHdl(AutoLayout eLayout) +{ + Sequence< PropertyValue > aArgs; + + OUString sCommandURL( mxControl->getCommandURL() ); + + if( eLayout != AUTOLAYOUT_END ) + { + aArgs = { comphelper::makePropertyValue("WhatLayout", static_cast<sal_Int32>(eLayout)) }; + } + else if( mbInsertPage ) + { + sCommandURL = ".uno:DuplicatePage"; + } + + mxControl->dispatchCommand( sCommandURL, aArgs ); + + mxControl->EndPopupMode(); +} + + +/// @throws css::uno::RuntimeException +static OUString SlideLayoutController_getImplementationName() +{ + return "com.sun.star.comp.sd.SlideLayoutController"; +} + +/// @throws RuntimeException +static Sequence< OUString > SlideLayoutController_getSupportedServiceNames() +{ + Sequence<OUString> aSNS { "com.sun.star.frame.ToolbarController" }; + return aSNS; +} + +/// @throws css::uno::RuntimeException +static OUString InsertSlideController_getImplementationName() +{ + return "com.sun.star.comp.sd.InsertSlideController"; +} + +/// @throws RuntimeException +static Sequence< OUString > InsertSlideController_getSupportedServiceNames() +{ + Sequence<OUString> aSNS { "com.sun.star.frame.ToolbarController" }; + return aSNS; +} + +SlideLayoutController::SlideLayoutController(const Reference< uno::XComponentContext >& rxContext, bool bInsertPage) + : svt::PopupWindowController(rxContext, nullptr, OUString()) + , mbInsertPage(bInsertPage) +{ +} + +void SAL_CALL SlideLayoutController::initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) +{ + svt::PopupWindowController::initialize( aArguments ); + + ToolBox* pToolBox = nullptr; + ToolBoxItemId nId; + if ( getToolboxId( nId, &pToolBox ) ) + { + if ( mbInsertPage ) + pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | ToolBoxItemBits::DROPDOWN ); + else + pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | ToolBoxItemBits::DROPDOWNONLY ); + } +} + +std::unique_ptr<WeldToolbarPopup> SlideLayoutController::weldPopupWindow() +{ + return std::make_unique<sd::LayoutToolbarMenu>(this, m_pToolbar, mbInsertPage, m_aCommandURL); +} + +VclPtr<vcl::Window> SlideLayoutController::createVclPopupWindow( vcl::Window* pParent ) +{ + mxInterimPopover = VclPtr<InterimToolbarPopup>::Create(getFrameInterface(), pParent, + std::make_unique<sd::LayoutToolbarMenu>(this, pParent->GetFrameWeld(), mbInsertPage, m_aCommandURL)); + + mxInterimPopover->Show(); + + return mxInterimPopover; +} + +// XServiceInfo + +OUString SAL_CALL SlideLayoutController::getImplementationName() +{ + if( mbInsertPage ) + return InsertSlideController_getImplementationName(); + else + return SlideLayoutController_getImplementationName(); +} + +Sequence< OUString > SAL_CALL SlideLayoutController::getSupportedServiceNames( ) +{ + if( mbInsertPage ) + return InsertSlideController_getSupportedServiceNames(); + else + return SlideLayoutController_getSupportedServiceNames(); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_sd_SlideLayoutController_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new sd::SlideLayoutController(context, false)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_sd_InsertSlideController_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new sd::SlideLayoutController(context, true)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/controller/slidelayoutcontroller.hxx b/sd/source/ui/controller/slidelayoutcontroller.hxx new file mode 100644 index 000000000..ae4a3a09f --- /dev/null +++ b/sd/source/ui/controller/slidelayoutcontroller.hxx @@ -0,0 +1,47 @@ +/* -*- 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 . + */ + +#pragma once + +#include <svtools/popupwindowcontroller.hxx> + +namespace sd +{ +class SlideLayoutController : public svt::PopupWindowController +{ +public: + SlideLayoutController(const css::uno::Reference<css::uno::XComponentContext>& rxContext, + bool bInsertPage); + + virtual std::unique_ptr<WeldToolbarPopup> weldPopupWindow() override; + virtual VclPtr<vcl::Window> createVclPopupWindow(vcl::Window* pParent) override; + + // XInitialization + virtual void SAL_CALL initialize(const css::uno::Sequence<css::uno::Any>& aArguments) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + +private: + bool mbInsertPage; +}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/AnimationChildWindow.cxx b/sd/source/ui/dlg/AnimationChildWindow.cxx new file mode 100644 index 000000000..2f221fc9e --- /dev/null +++ b/sd/source/ui/dlg/AnimationChildWindow.cxx @@ -0,0 +1,50 @@ +/* -*- 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 <AnimationChildWindow.hxx> + +#include <app.hrc> +#include <animobjs.hxx> +#include <sfx2/childwin.hxx> + +namespace sd { + +SFX_IMPL_DOCKINGWINDOW_WITHID(AnimationChildWindow, SID_ANIMATION_OBJECTS) + +/** + * Derivative from SfxChildWindow as "container" for animator + */ +AnimationChildWindow::AnimationChildWindow( + vcl::Window* _pParent, + sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo* pInfo ) + : SfxChildWindow( _pParent, nId ) +{ + VclPtr<AnimationWindow> pAnimWin = VclPtr<AnimationWindow>::Create(pBindings, this, _pParent); + SetWindow(pAnimWin); + + pAnimWin->Initialize( pInfo ); + + SetHideNotDelete( true ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/BulletAndPositionDlg.cxx b/sd/source/ui/dlg/BulletAndPositionDlg.cxx new file mode 100644 index 000000000..384b477e4 --- /dev/null +++ b/sd/source/ui/dlg/BulletAndPositionDlg.cxx @@ -0,0 +1,1293 @@ +/* -*- 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 <tools/mapunit.hxx> +#include <tools/urlobj.hxx> +#include <editeng/numitem.hxx> +#include <svl/eitem.hxx> +#include <svl/itempool.hxx> +#include <svx/colorbox.hxx> +#include <svx/strarray.hxx> +#include <svx/gallery.hxx> +#include <editeng/brushitem.hxx> +#include <svl/intitem.hxx> +#include <vcl/graph.hxx> +#include <svtools/unitconv.hxx> +#include <svx/svxids.hrc> + +#include <algorithm> +#include <memory> +#include <vector> +#include <sfx2/opengrf.hxx> + +#include <strings.hrc> +#include <svl/stritem.hxx> +#include <sal/log.hxx> +#include <vcl/virdev.hxx> +#include <svx/SvxNumOptionsTabPageHelper.hxx> +#include <View.hxx> +#include <drawdoc.hxx> +#include <cui/cuicharmap.hxx> +#include <BulletAndPositionDlg.hxx> +#include <sdresid.hxx> +#include <DrawViewShell.hxx> + +#define SHOW_NUMBERING 0 +#define SHOW_BULLET 1 +#define SHOW_BITMAP 2 + +#define MAX_BMP_WIDTH 16 +#define MAX_BMP_HEIGHT 16 + +static bool bLastRelative = false; + +static const vcl::Font& lcl_GetDefaultBulletFont() +{ + static vcl::Font aDefBulletFont = []() { + vcl::Font tmp("OpenSymbol", "", Size(0, 14)); + tmp.SetCharSet(RTL_TEXTENCODING_SYMBOL); + tmp.SetFamily(FAMILY_DONTKNOW); + tmp.SetPitch(PITCH_DONTKNOW); + tmp.SetWeight(WEIGHT_DONTKNOW); + tmp.SetTransparent(true); + return tmp; + }(); + return aDefBulletFont; +} + +class SdDrawDocument; + +SvxBulletAndPositionDlg::SvxBulletAndPositionDlg(weld::Window* pWindow, const SfxItemSet& rSet, + const ::sd::View* pView) + : GenericDialogController(pWindow, "cui/ui/bulletandposition.ui", "BulletAndPosition") + , aInvalidateTimer("sd SvxBulletAndPositionDlg aInvalidateTimer") + , rFirstStateSet(rSet) + , bLastWidthModified(false) + , bModified(false) + , bInInitControl(false) + , bLabelAlignmentPosAndSpaceModeActive(false) + , bApplyToMaster(false) + , nBullet(0xff) + , nActNumLvl(1) + , p_Window(pWindow) + , nNumItemId(SID_ATTR_NUMBERING_RULE) + , m_xGrid(m_xBuilder->weld_widget("grid2")) + , m_xLevelLB(m_xBuilder->weld_tree_view("levellb")) + , m_xFmtLB(m_xBuilder->weld_combo_box("numfmtlb")) + , m_xPrefixFT(m_xBuilder->weld_label("prefixft")) + , m_xPrefixED(m_xBuilder->weld_entry("prefix")) + , m_xSuffixFT(m_xBuilder->weld_label("suffixft")) + , m_xSuffixED(m_xBuilder->weld_entry("suffix")) + , m_xBeforeAfter(m_xBuilder->weld_frame("beforeafter")) + , m_xBulColorFT(m_xBuilder->weld_label("colorft")) + , m_xBulColLB(new ColorListBox(m_xBuilder->weld_menu_button("color"), + [this] { return m_xDialog.get(); })) + , m_xBulRelSizeFT(m_xBuilder->weld_label("relsizeft")) + , m_xBulRelSizeMF(m_xBuilder->weld_metric_spin_button("relsize", FieldUnit::PERCENT)) + , m_xStartFT(m_xBuilder->weld_label("startatft")) + , m_xStartED(m_xBuilder->weld_spin_button("startat")) + , m_xBulletFT(m_xBuilder->weld_label("bulletft")) + , m_xBulletPB(m_xBuilder->weld_button("bullet")) + , m_xBitmapMB(m_xBuilder->weld_menu_button("bitmap")) + , m_xWidthFT(m_xBuilder->weld_label("widthft")) + , m_xWidthMF(m_xBuilder->weld_metric_spin_button("widthmf", FieldUnit::CM)) + , m_xHeightFT(m_xBuilder->weld_label("heightft")) + , m_xHeightMF(m_xBuilder->weld_metric_spin_button("heightmf", FieldUnit::CM)) + , m_xRatioCB(m_xBuilder->weld_check_button("keepratio")) + , m_xPreviewWIN(new weld::CustomWeld(*m_xBuilder, "preview", m_aPreviewWIN)) + , m_xDistBorderFT(m_xBuilder->weld_label("indent")) + , m_xDistBorderMF(m_xBuilder->weld_metric_spin_button("indentmf", FieldUnit::CM)) + , m_xRelativeCB(m_xBuilder->weld_check_button("relative")) + , m_xIndentFT(m_xBuilder->weld_label("numberingwidth")) + , m_xIndentMF(m_xBuilder->weld_metric_spin_button("numberingwidthmf", FieldUnit::CM)) + , m_xLeftTB(m_xBuilder->weld_toggle_button("left")) + , m_xCenterTB(m_xBuilder->weld_toggle_button("center")) + , m_xRightTB(m_xBuilder->weld_toggle_button("right")) + , m_xSlideRB(m_xBuilder->weld_radio_button("sliderb")) + , m_xSelectionRB(m_xBuilder->weld_radio_button("selectionrb")) + , m_xApplyToMaster(m_xBuilder->weld_toggle_button("applytomaster")) + , m_xReset(m_xBuilder->weld_button("reset")) +{ + m_xBulColLB->SetSlotId(SID_ATTR_CHAR_COLOR); + m_xBulRelSizeMF->set_min(SVX_NUM_REL_SIZE_MIN, FieldUnit::PERCENT); + m_xBulRelSizeMF->set_increments(5, 50, FieldUnit::PERCENT); + aActBulletFont = lcl_GetDefaultBulletFont(); + + m_xBulletPB->connect_clicked(LINK(this, SvxBulletAndPositionDlg, BulletHdl_Impl)); + m_xFmtLB->connect_changed(LINK(this, SvxBulletAndPositionDlg, NumberTypeSelectHdl_Impl)); + m_xBitmapMB->connect_selected(LINK(this, SvxBulletAndPositionDlg, GraphicHdl_Impl)); + m_xBitmapMB->connect_toggled(LINK(this, SvxBulletAndPositionDlg, PopupActivateHdl_Impl)); + m_xLevelLB->set_selection_mode(SelectionMode::Multiple); + m_xLevelLB->connect_changed(LINK(this, SvxBulletAndPositionDlg, LevelHdl_Impl)); + m_xWidthMF->connect_value_changed(LINK(this, SvxBulletAndPositionDlg, SizeHdl_Impl)); + m_xHeightMF->connect_value_changed(LINK(this, SvxBulletAndPositionDlg, SizeHdl_Impl)); + m_xRatioCB->connect_toggled(LINK(this, SvxBulletAndPositionDlg, RatioHdl_Impl)); + m_xStartED->connect_value_changed(LINK(this, SvxBulletAndPositionDlg, SpinModifyHdl_Impl)); + m_xPrefixED->connect_changed(LINK(this, SvxBulletAndPositionDlg, EditModifyHdl_Impl)); + m_xSuffixED->connect_changed(LINK(this, SvxBulletAndPositionDlg, EditModifyHdl_Impl)); + m_xBulRelSizeMF->connect_value_changed(LINK(this, SvxBulletAndPositionDlg, BulRelSizeHdl_Impl)); + m_xBulColLB->SetSelectHdl(LINK(this, SvxBulletAndPositionDlg, BulColorHdl_Impl)); + m_xLeftTB->connect_toggled(LINK(this, SvxBulletAndPositionDlg, SelectLeftAlignmentHdl_Impl)); + m_xCenterTB->connect_toggled( + LINK(this, SvxBulletAndPositionDlg, SelectCenterAlignmentHdl_Impl)); + m_xRightTB->connect_toggled(LINK(this, SvxBulletAndPositionDlg, SelectRightAlignmentHdl_Impl)); + m_xApplyToMaster->connect_toggled(LINK(this, SvxBulletAndPositionDlg, ApplyToMasterHdl_Impl)); + m_xReset->connect_clicked(LINK(this, SvxBulletAndPositionDlg, ResetHdl_Impl)); + + aInvalidateTimer.SetInvokeHandler( + LINK(this, SvxBulletAndPositionDlg, PreviewInvalidateHdl_Impl)); + aInvalidateTimer.SetTimeout(50); + + eCoreUnit = rSet.GetPool()->GetMetric(rSet.GetPool()->GetWhich(SID_ATTR_NUMBERING_RULE)); + + // Fill ListBox with predefined / translated numbering types. + sal_uInt32 nCount = SvxNumberingTypeTable::Count(); + for (sal_uInt32 i = 0; i < nCount; ++i) + { + m_xFmtLB->append(OUString::number(SvxNumberingTypeTable::GetValue(i)), + SvxNumberingTypeTable::GetString(i)); + } + + // Get advanced numbering types from the component. + // Watch out for the ugly + // 136 == 0x88 == SVX_NUM_BITMAP|0x80 == SVX_NUM_BITMAP|LINK_TOKEN + // to not remove that. + SvxNumOptionsTabPageHelper::GetI18nNumbering(*m_xFmtLB, (SVX_NUM_BITMAP | LINK_TOKEN)); + + m_xFmtLB->set_active(0); + m_xRelativeCB->set_active(true); + + Link<weld::MetricSpinButton&, void> aLk3 + = LINK(this, SvxBulletAndPositionDlg, DistanceHdl_Impl); + m_xDistBorderMF->connect_value_changed(aLk3); + m_xIndentMF->connect_value_changed(aLk3); + + m_xRelativeCB->connect_toggled(LINK(this, SvxBulletAndPositionDlg, RelativeHdl_Impl)); + m_xRelativeCB->set_active(bLastRelative); + + Size aSize(m_xGrid->get_preferred_size()); + m_xGrid->set_size_request(aSize.Width(), -1); + + // PageCreated + FieldUnit eMetric = pView->GetDoc().GetUIUnit(); + SfxAllItemSet aSet(*(rSet.GetPool())); + aSet.Put(SfxUInt16Item(SID_METRIC_ITEM, static_cast<sal_uInt16>(eMetric))); + + const SfxStringItem* pNumCharFmt = aSet.GetItem<SfxStringItem>(SID_NUM_CHAR_FMT, false); + const SfxUInt16Item* pMetricItem = aSet.GetItem<SfxUInt16Item>(SID_METRIC_ITEM, false); + + if (pNumCharFmt) + SetCharFmt(pNumCharFmt->GetValue()); + + if (pMetricItem) + SetMetric(static_cast<FieldUnit>(pMetricItem->GetValue())); + + // tdf#130526: Hide "Apply To Master"-button in Draw and rename "Slide" to "Page" + DocumentType aDocumentType = pView->GetDoc().GetDocumentType(); + if (aDocumentType == DocumentType::Draw) + { + m_xApplyToMaster->hide(); + m_xSlideRB->set_label(SdResId(STR_PAGE_NAME)); + } + // tdf#137406: Crash when clicking "Apply to Master" in Slide Master mode on Bullets and Numbering dialog + EditMode aEditmode = static_cast<::sd::DrawViewShell*>(pView->GetViewShell())->GetEditMode(); + if (aDocumentType == DocumentType::Impress && aEditmode == EditMode::MasterPage) + m_xApplyToMaster->hide(); + + // End PageCreated + + Reset(&rSet); + + // ActivatePage part + + const SfxItemSet* pExampleSet = &rSet; + sal_uInt16 nTmpNumLvl = 1; + bool bPreset = false; + if (pExampleSet) + { + if (const SfxBoolItem* pItem = pExampleSet->GetItemIfSet(SID_PARAM_NUM_PRESET, false)) + bPreset = pItem->GetValue(); + if (const SfxUInt16Item* pItem = pExampleSet->GetItemIfSet(SID_PARAM_CUR_NUM_LEVEL, false)) + nTmpNumLvl = pItem->GetValue(); + } + if (const SvxNumBulletItem* pItem = rSet.GetItemIfSet(nNumItemId, false)) + { + pSaveNum.reset(new SvxNumRule(pItem->GetNumRule())); + } + + bModified = (!pActNum->Get(0) || bPreset); + if (*pActNum != *pSaveNum || nActNumLvl != nTmpNumLvl) + { + nActNumLvl = nTmpNumLvl; + sal_uInt16 nMask = 1; + if (nActNumLvl == SAL_MAX_UINT16) + m_xLevelLB->select(pActNum->GetLevelCount()); + if (nActNumLvl != SAL_MAX_UINT16) + { + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + { + if (nActNumLvl & nMask) + m_xLevelLB->select(i); + nMask <<= 1; + } + } + *pActNum = *pSaveNum; + + m_xRelativeCB->set_sensitive(nActNumLvl != 1); + + InitPosAndSpaceMode(); + InitControls(); + } + + m_aPreviewWIN.SetLevel(nActNumLvl); + m_aPreviewWIN.Invalidate(); + + // End of the ActivatePage part +} + +SvxBulletAndPositionDlg::~SvxBulletAndPositionDlg() {} + +void SvxBulletAndPositionDlg::SetMetric(FieldUnit eMetric) +{ + if (eMetric == FieldUnit::MM) + { + m_xWidthMF->set_digits(1); + m_xHeightMF->set_digits(1); + m_xDistBorderMF->set_digits(1); + m_xIndentMF->set_digits(1); + } + m_xWidthMF->set_unit(eMetric); + m_xHeightMF->set_unit(eMetric); + m_xDistBorderMF->set_unit(eMetric); + m_xIndentMF->set_unit(eMetric); +} + +SfxItemSet* SvxBulletAndPositionDlg::GetOutputItemSet(SfxItemSet* pSet) +{ + pSet->Put(SfxUInt16Item(SID_PARAM_CUR_NUM_LEVEL, nActNumLvl)); + if (bModified && pActNum) + { + *pSaveNum = *pActNum; + pSet->Put(SvxNumBulletItem(*pSaveNum, nNumItemId)); + pSet->Put(SfxBoolItem(SID_PARAM_NUM_PRESET, false)); + } + return pSet; +}; + +bool SvxBulletAndPositionDlg::IsApplyToMaster() const { return bApplyToMaster; } +bool SvxBulletAndPositionDlg::IsSlideScope() const { return m_xSlideRB->get_active(); } + +void SvxBulletAndPositionDlg::Reset(const SfxItemSet* rSet) +{ + const SvxNumBulletItem* pItem = rSet->GetItemIfSet(SID_ATTR_NUMBERING_RULE, false); + // in Draw the item exists as WhichId, in Writer only as SlotId + if (!pItem) + { + nNumItemId = rSet->GetPool()->GetWhich(SID_ATTR_NUMBERING_RULE); + pItem = rSet->GetItemIfSet(nNumItemId, false); + + if (!pItem) + { + pItem = &rSet->Get(nNumItemId); + } + } + DBG_ASSERT(pItem, "no item found!"); + pSaveNum.reset(new SvxNumRule(pItem->GetNumRule())); + + // insert levels + if (!m_xLevelLB->n_children()) + { + for (sal_uInt16 i = 1; i <= pSaveNum->GetLevelCount(); i++) + m_xLevelLB->append_text(OUString::number(i)); + if (pSaveNum->GetLevelCount() > 1) + { + OUString sEntry = "1 - " + OUString::number(pSaveNum->GetLevelCount()); + m_xLevelLB->append_text(sEntry); + m_xLevelLB->select_text(sEntry); + } + else + m_xLevelLB->select(0); + } + else + m_xLevelLB->select(m_xLevelLB->n_children() - 1); + + sal_uInt16 nMask = 1; + m_xLevelLB->unselect_all(); + if (nActNumLvl == SAL_MAX_UINT16) + { + m_xLevelLB->select(pSaveNum->GetLevelCount()); + } + else + { + for (sal_uInt16 i = 0; i < pSaveNum->GetLevelCount(); i++) + { + if (nActNumLvl & nMask) + m_xLevelLB->select(i); + nMask <<= 1; + } + } + + if (!pActNum) + pActNum.reset(new SvxNumRule(*pSaveNum)); + else if (*pSaveNum != *pActNum) + *pActNum = *pSaveNum; + m_aPreviewWIN.SetNumRule(pActNum.get()); + + bool bContinuous = pActNum->IsFeatureSupported(SvxNumRuleFlags::CONTINUOUS); + + // again misusage: in Draw there is numeration only until the bitmap + // without SVX_NUM_NUMBER_NONE + //remove types that are unsupported by Draw/Impress + if (!bContinuous) + { + sal_Int32 nFmtCount = m_xFmtLB->get_count(); + for (sal_Int32 i = nFmtCount; i; i--) + { + sal_uInt16 nEntryData = m_xFmtLB->get_id(i - 1).toUInt32(); + if (/*SVX_NUM_NUMBER_NONE == nEntryData ||*/ + (SVX_NUM_BITMAP | LINK_TOKEN) == nEntryData) + m_xFmtLB->remove(i - 1); + } + } + //one must be enabled + if (!pActNum->IsFeatureSupported(SvxNumRuleFlags::ENABLE_LINKED_BMP)) + { + auto nPos = m_xFmtLB->find_id(OUString::number(SVX_NUM_BITMAP | LINK_TOKEN)); + if (nPos != -1) + m_xFmtLB->remove(nPos); + } + else if (!pActNum->IsFeatureSupported(SvxNumRuleFlags::ENABLE_EMBEDDED_BMP)) + { + auto nPos = m_xFmtLB->find_id(OUString::number(SVX_NUM_BITMAP)); + if (nPos != -1) + m_xFmtLB->remove(nPos); + } + + // MegaHack: because of a not-fixable 'design mistake/error' in Impress + // delete all kinds of numeric enumerations + if (pActNum->IsFeatureSupported(SvxNumRuleFlags::NO_NUMBERS)) + { + sal_Int32 nFmtCount = m_xFmtLB->get_count(); + for (sal_Int32 i = nFmtCount; i; i--) + { + sal_uInt16 nEntryData = m_xFmtLB->get_id(i - 1).toUInt32(); + if (/*nEntryData >= SVX_NUM_CHARS_UPPER_LETTER &&*/ nEntryData <= SVX_NUM_NUMBER_NONE) + m_xFmtLB->remove(i - 1); + } + } + + InitPosAndSpaceMode(); + + InitControls(); + bModified = false; +} + +void SvxBulletAndPositionDlg::InitControls() +{ + bInInitControl = true; + + const bool bRelative = !bLabelAlignmentPosAndSpaceModeActive && m_xRelativeCB->get_sensitive() + && m_xRelativeCB->get_active(); + const bool bSingleSelection + = m_xLevelLB->count_selected_rows() == 1 && SAL_MAX_UINT16 != nActNumLvl; + + m_xDistBorderMF->set_sensitive(!bLabelAlignmentPosAndSpaceModeActive + && (bSingleSelection || bRelative)); + m_xDistBorderFT->set_sensitive(!bLabelAlignmentPosAndSpaceModeActive + && (bSingleSelection || bRelative)); + + bool bShowBullet = true; + bool bShowBitmap = true; + bool bSameType = true; + bool bSameStart = true; + bool bSamePrefix = true; + bool bSameSuffix = true; + bool bSameSize = true; + bool bSameBulColor = true; + bool bSameBulRelSize = true; + bool bSameDistBorderNum = !bLabelAlignmentPosAndSpaceModeActive; + bool bSetDistEmpty = false; + bool bSameIndent = !bLabelAlignmentPosAndSpaceModeActive; + + const SvxNumberFormat* aNumFmtArr[SVX_MAX_NUM]; + SvxAdjust eFirstAdjust = SvxAdjust::Left; + Size aFirstSize(0, 0); + sal_uInt16 nMask = 1; + sal_uInt16 nLvl = SAL_MAX_UINT16; + + bool bBullColor = pActNum->IsFeatureSupported(SvxNumRuleFlags::BULLET_COLOR); + bool bBullRelSize = pActNum->IsFeatureSupported(SvxNumRuleFlags::BULLET_REL_SIZE); + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + { + aNumFmtArr[i] = &pActNum->GetLevel(i); + + if (nActNumLvl & nMask) + { + bShowBullet &= aNumFmtArr[i]->GetNumberingType() == SVX_NUM_CHAR_SPECIAL; + bShowBitmap &= (aNumFmtArr[i]->GetNumberingType() & (~LINK_TOKEN)) == SVX_NUM_BITMAP; + eFirstAdjust = aNumFmtArr[i]->GetNumAdjust(); + if (SAL_MAX_UINT16 == nLvl) + { + nLvl = i; + if (bShowBitmap) + aFirstSize = aNumFmtArr[i]->GetGraphicSize(); + } + if (i > nLvl) + { + bSameType + &= aNumFmtArr[i]->GetNumberingType() == aNumFmtArr[nLvl]->GetNumberingType(); + bSameStart = aNumFmtArr[i]->GetStart() == aNumFmtArr[nLvl]->GetStart(); + + bSamePrefix = aNumFmtArr[i]->GetPrefix() == aNumFmtArr[nLvl]->GetPrefix(); + bSameSuffix = aNumFmtArr[i]->GetSuffix() == aNumFmtArr[nLvl]->GetSuffix(); + //bSameAdjust &= eFirstAdjust == aNumFmtArr[i]->GetNumAdjust(); + if (bShowBitmap && bSameSize) + bSameSize &= aNumFmtArr[i]->GetGraphicSize() == aFirstSize; + bSameBulColor + &= aNumFmtArr[i]->GetBulletColor() == aNumFmtArr[nLvl]->GetBulletColor(); + bSameBulRelSize + &= aNumFmtArr[i]->GetBulletRelSize() == aNumFmtArr[nLvl]->GetBulletRelSize(); + bSameIndent //? + &= aNumFmtArr[i]->GetFirstLineOffset() + == aNumFmtArr[nLvl]->GetFirstLineOffset(); + } + } + + nMask <<= 1; + } + SwitchNumberType(bShowBullet ? 1 : bShowBitmap ? 2 : 0); + + sal_uInt16 nNumberingType; + if (nLvl != SAL_MAX_UINT16) + nNumberingType = aNumFmtArr[nLvl]->GetNumberingType(); + else + { + nNumberingType = SVX_NUM_NUMBER_NONE; + bSameDistBorderNum = false; + bSameIndent = false; + bSameBulRelSize = false; + bSameBulColor = false; + bSameStart = false; + bSamePrefix = false; + bSameSuffix = false; + } + + CheckForStartValue_Impl(nNumberingType); + + if (bShowBitmap) + { + if (bSameSize) + { + SetMetricValue(*m_xHeightMF, aFirstSize.Height(), eCoreUnit); + SetMetricValue(*m_xWidthMF, aFirstSize.Width(), eCoreUnit); + } + else + { + m_xHeightMF->set_text(""); + m_xWidthMF->set_text(""); + } + } + + if (bSameType) + { + sal_uInt16 nLBData = nNumberingType; + m_xFmtLB->set_active_id(OUString::number(nLBData)); + } + else + m_xFmtLB->set_active(-1); + + if (bBullRelSize) + { + if (bSameBulRelSize) + m_xBulRelSizeMF->set_value(aNumFmtArr[nLvl]->GetBulletRelSize(), FieldUnit::PERCENT); + else + m_xBulRelSizeMF->set_text(""); + } + if (bBullColor) + { + if (bSameBulColor) + m_xBulColLB->SelectEntry(aNumFmtArr[nLvl]->GetBulletColor()); + else + m_xBulColLB->SetNoSelection(); + } + switch (nBullet) + { + case SHOW_NUMBERING: + if (bSameStart) + { + m_xStartED->set_value(aNumFmtArr[nLvl]->GetStart()); + } + else + m_xStartED->set_text(""); + break; + case SHOW_BULLET: + break; + case SHOW_BITMAP: + break; + } + + switch (eFirstAdjust) + { + case SvxAdjust::Left: + m_xLeftTB->set_active(true); + m_xCenterTB->set_active(false); + m_xRightTB->set_active(false); + break; + case SvxAdjust::Center: + m_xLeftTB->set_active(false); + m_xCenterTB->set_active(true); + m_xRightTB->set_active(false); + break; + case SvxAdjust::Right: + m_xLeftTB->set_active(false); + m_xCenterTB->set_active(false); + m_xRightTB->set_active(true); + break; + default: + break; + } + + if (bSamePrefix) + m_xPrefixED->set_text(aNumFmtArr[nLvl]->GetPrefix()); + else + m_xPrefixED->set_text(""); + if (bSameSuffix) + m_xSuffixED->set_text(aNumFmtArr[nLvl]->GetSuffix()); + else + m_xSuffixED->set_text(""); + + if (bSameDistBorderNum) + { + tools::Long nDistBorderNum; + if (bRelative) + { + nDistBorderNum = static_cast<tools::Long>(aNumFmtArr[nLvl]->GetAbsLSpace()) + + aNumFmtArr[nLvl]->GetFirstLineOffset(); + if (nLvl) + nDistBorderNum -= static_cast<tools::Long>(aNumFmtArr[nLvl - 1]->GetAbsLSpace()) + + aNumFmtArr[nLvl - 1]->GetFirstLineOffset(); + } + else + { + nDistBorderNum = static_cast<tools::Long>(aNumFmtArr[nLvl]->GetAbsLSpace()) + + aNumFmtArr[nLvl]->GetFirstLineOffset(); + } + SetMetricValue(*m_xDistBorderMF, nDistBorderNum, eCoreUnit); + } + else + bSetDistEmpty = true; + + if (bSetDistEmpty) + m_xDistBorderMF->set_text(""); + + if (bSameIndent) + SetMetricValue(*m_xIndentMF, -aNumFmtArr[nLvl]->GetFirstLineOffset(), eCoreUnit); + else + m_xIndentMF->set_text(""); + + m_xSelectionRB->set_active(true); + + m_aPreviewWIN.SetLevel(nActNumLvl); + m_aPreviewWIN.Invalidate(); + bInInitControl = false; +} + +// 0 - Number; 1 - Bullet; 2 - Bitmap +void SvxBulletAndPositionDlg::SwitchNumberType(sal_uInt8 nType) +{ + if (nBullet == nType) + return; + nBullet = nType; + bool bBullet = (nType == SHOW_BULLET); + bool bBitmap = (nType == SHOW_BITMAP); + bool bEnableBitmap = (nType == SHOW_BITMAP); + bool bNumeric = !(bBitmap || bBullet); + m_xPrefixFT->set_visible(bNumeric); + m_xPrefixED->set_visible(bNumeric); + m_xSuffixFT->set_visible(bNumeric); + m_xSuffixED->set_visible(bNumeric); + m_xBeforeAfter->set_visible(bNumeric); + + m_xStartFT->set_visible(!(bBullet || bBitmap)); + m_xStartED->set_visible(!(bBullet || bBitmap)); + + m_xBulletFT->set_visible(bBullet); + m_xBulletPB->set_visible(bBullet); + bool bBullColor = pActNum->IsFeatureSupported(SvxNumRuleFlags::BULLET_COLOR); + m_xBulColorFT->set_visible(!bBitmap && bBullColor); + m_xBulColLB->set_visible(!bBitmap && bBullColor); + bool bBullResSize = pActNum->IsFeatureSupported(SvxNumRuleFlags::BULLET_REL_SIZE); + m_xBulRelSizeFT->set_visible(!bBitmap && bBullResSize); + m_xBulRelSizeMF->set_visible(!bBitmap && bBullResSize); + + m_xBitmapMB->set_visible(bBitmap); + + m_xWidthFT->set_visible(bBitmap); + m_xWidthMF->set_visible(bBitmap); + m_xHeightFT->set_visible(bBitmap); + m_xHeightMF->set_visible(bBitmap); + m_xRatioCB->set_visible(bBitmap); + + m_xWidthFT->set_sensitive(bEnableBitmap); + m_xWidthMF->set_sensitive(bEnableBitmap); + m_xHeightFT->set_sensitive(bEnableBitmap); + m_xHeightMF->set_sensitive(bEnableBitmap); + m_xRatioCB->set_sensitive(bEnableBitmap); +} + +void SvxBulletAndPositionDlg::CheckForStartValue_Impl(sal_uInt16 nNumberingType) +{ + bool bIsNull = m_xStartED->get_value() == 0; + bool bNoZeroAllowed = nNumberingType < SVX_NUM_ARABIC + || SVX_NUM_CHARS_UPPER_LETTER_N == nNumberingType + || SVX_NUM_CHARS_LOWER_LETTER_N == nNumberingType; + m_xStartED->set_min(bNoZeroAllowed ? 1 : 0); + if (bIsNull && bNoZeroAllowed) + SpinModifyHdl_Impl(*m_xStartED); +} + +IMPL_LINK(SvxBulletAndPositionDlg, LevelHdl_Impl, weld::TreeView&, rBox, void) +{ + sal_uInt16 nSaveNumLvl = nActNumLvl; + nActNumLvl = 0; + auto aSelectedRows = rBox.get_selected_rows(); + if (std::find(aSelectedRows.begin(), aSelectedRows.end(), pActNum->GetLevelCount()) + != aSelectedRows.end() + && (aSelectedRows.size() == 1 || nSaveNumLvl != 0xffff)) + { + nActNumLvl = 0xFFFF; + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + rBox.unselect(i); + } + else if (!aSelectedRows.empty()) + { + sal_uInt16 nMask = 1; + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + { + if (std::find(aSelectedRows.begin(), aSelectedRows.end(), i) != aSelectedRows.end()) + nActNumLvl |= nMask; + nMask <<= 1; + } + rBox.unselect(pActNum->GetLevelCount()); + } + else + nActNumLvl = nSaveNumLvl; + + InitControls(); +} + +IMPL_LINK_NOARG(SvxBulletAndPositionDlg, PreviewInvalidateHdl_Impl, Timer*, void) +{ + m_aPreviewWIN.Invalidate(); +} + +IMPL_LINK(SvxBulletAndPositionDlg, NumberTypeSelectHdl_Impl, weld::ComboBox&, rBox, void) +{ + bool bBmp = false; + sal_uInt16 nMask = 1; + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + { + if (nActNumLvl & nMask) + { + SvxNumberFormat aNumFmt(pActNum->GetLevel(i)); + // PAGEDESC does not exist + SvxNumType nNumType = static_cast<SvxNumType>(rBox.get_active_id().toUInt32()); + aNumFmt.SetNumberingType(nNumType); + sal_uInt16 nNumberingType = aNumFmt.GetNumberingType(); + if (SVX_NUM_BITMAP == (nNumberingType & (~LINK_TOKEN))) + { + bBmp |= nullptr != aNumFmt.GetBrush(); + aNumFmt.SetIncludeUpperLevels(0); + aNumFmt.SetListFormat("", "", i); + if (!bBmp) + aNumFmt.SetGraphic(""); + pActNum->SetLevel(i, aNumFmt); + SwitchNumberType(SHOW_BITMAP); + } + else if (SVX_NUM_CHAR_SPECIAL == nNumberingType) + { + aNumFmt.SetIncludeUpperLevels(0); + aNumFmt.SetListFormat("", "", i); + if (!aNumFmt.GetBulletFont()) + aNumFmt.SetBulletFont(&aActBulletFont); + if (!aNumFmt.GetBulletChar()) + aNumFmt.SetBulletChar(SVX_DEF_BULLET); + pActNum->SetLevel(i, aNumFmt); + SwitchNumberType(SHOW_BULLET); + // allocation of the drawing pattern is automatic + } + else + { + aNumFmt.SetListFormat(m_xPrefixED->get_text(), m_xSuffixED->get_text(), i); + SwitchNumberType(SHOW_NUMBERING); + pActNum->SetLevel(i, aNumFmt); + CheckForStartValue_Impl(nNumberingType); + + // allocation of the drawing pattern is automatic + } + } + nMask <<= 1; + } + + SetModified(); +} + +IMPL_LINK(SvxBulletAndPositionDlg, BulColorHdl_Impl, ColorListBox&, rColorBox, void) +{ + Color nSetColor = rColorBox.GetSelectEntryColor(); + + sal_uInt16 nMask = 1; + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + { + if (nActNumLvl & nMask) + { + SvxNumberFormat aNumFmt(pActNum->GetLevel(i)); + aNumFmt.SetBulletColor(nSetColor); + pActNum->SetLevel(i, aNumFmt); + } + nMask <<= 1; + } + SetModified(); +} + +IMPL_LINK(SvxBulletAndPositionDlg, BulRelSizeHdl_Impl, weld::MetricSpinButton&, rField, void) +{ + sal_uInt16 nRelSize = rField.get_value(FieldUnit::PERCENT); + + sal_uInt16 nMask = 1; + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + { + if (nActNumLvl & nMask) + { + SvxNumberFormat aNumFmt(pActNum->GetLevel(i)); + aNumFmt.SetBulletRelSize(nRelSize); + pActNum->SetLevel(i, aNumFmt); + } + nMask <<= 1; + } + SetModified(); +} + +IMPL_LINK(SvxBulletAndPositionDlg, GraphicHdl_Impl, const OString&, rIdent, void) +{ + OUString aGrfName; + Size aSize; + bool bSucc(false); + SvxOpenGraphicDialog aGrfDlg(SdResId(RID_SVXSTR_EDIT_GRAPHIC), p_Window); + + OString sNumber; + if (rIdent.startsWith("gallery", &sNumber)) + { + auto idx = sNumber.toUInt32(); + if (idx < aGrfNames.size()) + { + aGrfName = aGrfNames[idx]; + Graphic aGraphic; + if (GalleryExplorer::GetGraphicObj(GALLERY_THEME_BULLETS, idx, &aGraphic)) + { + aSize = SvxNumberFormat::GetGraphicSizeMM100(&aGraphic); + bSucc = true; + } + } + } + else if (rIdent == "fromfile") + { + aGrfDlg.EnableLink(false); + aGrfDlg.AsLink(false); + if (!aGrfDlg.Execute()) + { + // memorize selected filter + aGrfName = aGrfDlg.GetPath(); + + Graphic aGraphic; + if (!aGrfDlg.GetGraphic(aGraphic)) + { + aSize = SvxNumberFormat::GetGraphicSizeMM100(&aGraphic); + bSucc = true; + } + } + } + if (!bSucc) + return; + + aSize = OutputDevice::LogicToLogic(aSize, MapMode(MapUnit::Map100thMM), MapMode(eCoreUnit)); + + sal_uInt16 nMask = 1; + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + { + if (nActNumLvl & nMask) + { + SvxNumberFormat aNumFmt(pActNum->GetLevel(i)); + aNumFmt.SetCharFormatName(m_sNumCharFmtName); + aNumFmt.SetGraphic(aGrfName); + + // set size for a later comparison + const SvxBrushItem* pBrushItem = aNumFmt.GetBrush(); + // initiate asynchronous loading + sal_Int16 eOrient = aNumFmt.GetVertOrient(); + aNumFmt.SetGraphicBrush(pBrushItem, &aSize, &eOrient); + aInitSize[i] = aNumFmt.GetGraphicSize(); + + pActNum->SetLevel(i, aNumFmt); + } + nMask <<= 1; + } + m_xRatioCB->set_sensitive(true); + m_xWidthFT->set_sensitive(true); + m_xHeightFT->set_sensitive(true); + m_xWidthMF->set_sensitive(true); + m_xHeightMF->set_sensitive(true); + SetMetricValue(*m_xWidthMF, aSize.Width(), eCoreUnit); + SetMetricValue(*m_xHeightMF, aSize.Height(), eCoreUnit); + + SetModified(); + //needed due to asynchronous loading of graphics in the SvxBrushItem + aInvalidateTimer.Start(); +} + +IMPL_LINK_NOARG(SvxBulletAndPositionDlg, PopupActivateHdl_Impl, weld::Toggleable&, void) +{ + if (m_xGalleryMenu) + return; + + m_xGalleryMenu = m_xBuilder->weld_menu("gallerysubmenu"); + weld::WaitObject aWait(p_Window); + + if (!GalleryExplorer::FillObjList(GALLERY_THEME_BULLETS, aGrfNames)) + return; + + GalleryExplorer::BeginLocking(GALLERY_THEME_BULLETS); + + Graphic aGraphic; + OUString sGrfName; + ScopedVclPtrInstance<VirtualDevice> pVD; + size_t i = 0; + for (const auto& grfName : aGrfNames) + { + sGrfName = grfName; + OUString sItemId = "gallery" + OUString::number(i); + INetURLObject aObj(sGrfName); + if (aObj.GetProtocol() == INetProtocol::File) + sGrfName = aObj.PathToFileName(); + if (GalleryExplorer::GetGraphicObj(GALLERY_THEME_BULLETS, i, &aGraphic)) + { + BitmapEx aBitmap(aGraphic.GetBitmapEx()); + Size aSize(aBitmap.GetSizePixel()); + if (aSize.Width() > MAX_BMP_WIDTH || aSize.Height() > MAX_BMP_HEIGHT) + { + bool bWidth = aSize.Width() > aSize.Height(); + double nScale = bWidth + ? double(MAX_BMP_WIDTH) / static_cast<double>(aSize.Width()) + : double(MAX_BMP_HEIGHT) / static_cast<double>(aSize.Height()); + aBitmap.Scale(nScale, nScale); + } + pVD->SetOutputSizePixel(aBitmap.GetSizePixel(), false); + pVD->DrawBitmapEx(Point(), aBitmap); + + // We want to show only icon names not full path. + aObj.removeExtension(); + OUString sIconName = aObj.GetLastName(INetURLObject::DecodeMechanism::WithCharset); + + m_xGalleryMenu->append(sItemId, sIconName, *pVD); + } + else + { + m_xGalleryMenu->append(sItemId, sGrfName); + } + ++i; + } + GalleryExplorer::EndLocking(GALLERY_THEME_BULLETS); +} + +IMPL_LINK_NOARG(SvxBulletAndPositionDlg, BulletHdl_Impl, weld::Button&, void) +{ + SvxCharacterMap aMap(p_Window, nullptr, nullptr); + + sal_uInt16 nMask = 1; + std::optional<vcl::Font> pFmtFont; + bool bSameBullet = true; + sal_UCS4 cBullet = 0; + bool bFirst = true; + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + { + if (nActNumLvl & nMask) + { + const SvxNumberFormat& rCurFmt = pActNum->GetLevel(i); + if (bFirst) + { + cBullet = rCurFmt.GetBulletChar(); + } + else if (rCurFmt.GetBulletChar() != cBullet) + { + bSameBullet = false; + break; + } + if (!pFmtFont) + pFmtFont = rCurFmt.GetBulletFont(); + bFirst = false; + } + nMask <<= 1; + } + + if (pFmtFont) + aMap.SetCharFont(*pFmtFont); + else + aMap.SetCharFont(aActBulletFont); + if (bSameBullet) + aMap.SetChar(cBullet); + if (aMap.run() != RET_OK) + return; + + // change Font Numrules + aActBulletFont = aMap.GetCharFont(); + + sal_uInt16 _nMask = 1; + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + { + if (nActNumLvl & _nMask) + { + SvxNumberFormat aNumFmt(pActNum->GetLevel(i)); + aNumFmt.SetBulletFont(&aActBulletFont); + aNumFmt.SetBulletChar(aMap.GetChar()); + pActNum->SetLevel(i, aNumFmt); + } + _nMask <<= 1; + } + + SetModified(); +} + +IMPL_LINK(SvxBulletAndPositionDlg, SizeHdl_Impl, weld::MetricSpinButton&, rField, void) +{ + bool bWidth = &rField == m_xWidthMF.get(); + bLastWidthModified = bWidth; + bool bRatio = m_xRatioCB->get_active(); + tools::Long nWidthVal = static_cast<tools::Long>( + m_xWidthMF->denormalize(m_xWidthMF->get_value(FieldUnit::MM_100TH))); + tools::Long nHeightVal = static_cast<tools::Long>( + m_xHeightMF->denormalize(m_xHeightMF->get_value(FieldUnit::MM_100TH))); + nWidthVal = OutputDevice::LogicToLogic(nWidthVal, MapUnit::Map100thMM, eCoreUnit); + nHeightVal = OutputDevice::LogicToLogic(nHeightVal, MapUnit::Map100thMM, eCoreUnit); + double fSizeRatio; + + bool bRepaint = false; + sal_uInt16 nMask = 1; + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + { + if (nActNumLvl & nMask) + { + SvxNumberFormat aNumFmt(pActNum->GetLevel(i)); + if (SVX_NUM_BITMAP == (aNumFmt.GetNumberingType() & (~LINK_TOKEN))) + { + Size aSize(aNumFmt.GetGraphicSize()); + Size aSaveSize(aSize); + + if (aInitSize[i].Height()) + fSizeRatio = static_cast<double>(aInitSize[i].Width()) + / static_cast<double>(aInitSize[i].Height()); + else + fSizeRatio = double(1); + + if (bWidth) + { + tools::Long nDelta = nWidthVal - aInitSize[i].Width(); + aSize.setWidth(nWidthVal); + if (bRatio) + { + aSize.setHeight( + aInitSize[i].Height() + + static_cast<tools::Long>(static_cast<double>(nDelta) / fSizeRatio)); + m_xHeightMF->set_value(m_xHeightMF->normalize(OutputDevice::LogicToLogic( + aSize.Height(), eCoreUnit, MapUnit::Map100thMM)), + FieldUnit::MM_100TH); + } + } + else + { + tools::Long nDelta = nHeightVal - aInitSize[i].Height(); + aSize.setHeight(nHeightVal); + if (bRatio) + { + aSize.setWidth( + aInitSize[i].Width() + + static_cast<tools::Long>(static_cast<double>(nDelta) * fSizeRatio)); + m_xWidthMF->set_value(m_xWidthMF->normalize(OutputDevice::LogicToLogic( + aSize.Width(), eCoreUnit, MapUnit::Map100thMM)), + FieldUnit::MM_100TH); + } + } + const SvxBrushItem* pBrushItem = aNumFmt.GetBrush(); + sal_Int16 eOrient = aNumFmt.GetVertOrient(); + if (aSize != aSaveSize) + bRepaint = true; + aNumFmt.SetGraphicBrush(pBrushItem, &aSize, &eOrient); + pActNum->SetLevel(i, aNumFmt); + } + } + nMask <<= 1; + } + SetModified(bRepaint); +} + +IMPL_LINK(SvxBulletAndPositionDlg, RatioHdl_Impl, weld::Toggleable&, rBox, void) +{ + if (rBox.get_active()) + { + if (bLastWidthModified) + SizeHdl_Impl(*m_xWidthMF); + else + SizeHdl_Impl(*m_xHeightMF); + } +} + +IMPL_LINK(SvxBulletAndPositionDlg, SelectLeftAlignmentHdl_Impl, weld::Toggleable&, rButton, void) +{ + if (rButton.get_active()) + { + SetAlignmentHdl_Impl(SvxAdjust::Left); + + m_xCenterTB->set_active(false); + m_xRightTB->set_active(false); + + SetModified(); + } +} + +IMPL_LINK(SvxBulletAndPositionDlg, SelectCenterAlignmentHdl_Impl, weld::Toggleable&, rButton, void) +{ + if (rButton.get_active()) + { + SetAlignmentHdl_Impl(SvxAdjust::Center); + + m_xLeftTB->set_active(false); + m_xRightTB->set_active(false); + + SetModified(); + } +} + +IMPL_LINK(SvxBulletAndPositionDlg, SelectRightAlignmentHdl_Impl, weld::Toggleable&, rButton, void) +{ + if (rButton.get_active()) + { + SetAlignmentHdl_Impl(SvxAdjust::Right); + + m_xLeftTB->set_active(false); + m_xCenterTB->set_active(false); + + SetModified(); + } +} + +void SvxBulletAndPositionDlg::SetAlignmentHdl_Impl(SvxAdjust eAdjust) +{ + sal_uInt16 nMask = 1; + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + { + if (nActNumLvl & nMask) + { + SvxNumberFormat aNumFmt(pActNum->GetLevel(i)); + aNumFmt.SetNumAdjust(eAdjust); + pActNum->SetLevel(i, aNumFmt); + } + nMask <<= 1; + } +} + +IMPL_LINK(SvxBulletAndPositionDlg, ApplyToMasterHdl_Impl, weld::Toggleable&, rButton, void) +{ + bApplyToMaster = rButton.get_active(); +} + +IMPL_LINK_NOARG(SvxBulletAndPositionDlg, ResetHdl_Impl, weld::Button&, void) +{ + Reset(&rFirstStateSet); +} + +IMPL_LINK(SvxBulletAndPositionDlg, EditModifyHdl_Impl, weld::Entry&, rEdit, void) +{ + EditModifyHdl_Impl(&rEdit); +} + +IMPL_LINK(SvxBulletAndPositionDlg, SpinModifyHdl_Impl, weld::SpinButton&, rSpinButton, void) +{ + EditModifyHdl_Impl(&rSpinButton); +} + +IMPL_LINK(SvxBulletAndPositionDlg, DistanceHdl_Impl, weld::MetricSpinButton&, rFld, void) +{ + if (bInInitControl) + return; + tools::Long nValue = GetCoreValue(rFld, eCoreUnit); + sal_uInt16 nMask = 1; + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + { + if (nActNumLvl & nMask) + { + SvxNumberFormat aNumFmt(pActNum->GetLevel(i)); + if (&rFld == m_xDistBorderMF.get()) + { + if (m_xRelativeCB->get_active()) + { + if (0 == i) + { + auto const nTmp = aNumFmt.GetFirstLineOffset(); + aNumFmt.SetAbsLSpace(nValue - nTmp); + } + else + { + tools::Long nTmp = pActNum->GetLevel(i - 1).GetAbsLSpace() + + pActNum->GetLevel(i - 1).GetFirstLineOffset() + - pActNum->GetLevel(i).GetFirstLineOffset(); + + aNumFmt.SetAbsLSpace(nValue + nTmp); + } + } + else + { + aNumFmt.SetAbsLSpace(nValue - aNumFmt.GetFirstLineOffset()); + } + } + else if (&rFld == m_xIndentMF.get()) + { + // together with the FirstLineOffset the AbsLSpace must be changed, too + tools::Long nDiff = nValue + aNumFmt.GetFirstLineOffset(); + auto const nAbsLSpace = aNumFmt.GetAbsLSpace(); + aNumFmt.SetAbsLSpace(nAbsLSpace + nDiff); + aNumFmt.SetFirstLineOffset(-nValue); + } + + pActNum->SetLevel(i, aNumFmt); + } + nMask <<= 1; + } + + SetModified(); + if (!m_xDistBorderMF->get_sensitive()) + { + m_xDistBorderMF->set_text(""); + } + + sal_Int32 aLastLevelLSpace + = pActNum->GetLevel(pActNum->GetLevelCount() - 1).GetAbsLSpace() / 40; + m_aPreviewWIN.set_size_request(aLastLevelLSpace, 300); +} + +IMPL_LINK(SvxBulletAndPositionDlg, RelativeHdl_Impl, weld::Toggleable&, rBox, void) +{ + bool bOn = rBox.get_active(); + bool bSingleSelection = m_xLevelLB->count_selected_rows() == 1 && SAL_MAX_UINT16 != nActNumLvl; + bool bSetValue = false; + tools::Long nValue = 0; + if (bOn || bSingleSelection) + { + sal_uInt16 nMask = 1; + bool bFirst = true; + bSetValue = true; + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + { + if (nActNumLvl & nMask) + { + const SvxNumberFormat& rNumFmt = pActNum->GetLevel(i); + if (bFirst) + { + nValue = rNumFmt.GetAbsLSpace() + rNumFmt.GetFirstLineOffset(); + if (bOn && i) + nValue -= (pActNum->GetLevel(i - 1).GetAbsLSpace() + + pActNum->GetLevel(i - 1).GetFirstLineOffset()); + } + else + bSetValue = nValue + == (rNumFmt.GetAbsLSpace() + rNumFmt.GetFirstLineOffset()) + - (pActNum->GetLevel(i - 1).GetAbsLSpace() + + pActNum->GetLevel(i - 1).GetFirstLineOffset()); + bFirst = false; + } + nMask <<= 1; + } + } + if (bSetValue) + SetMetricValue(*m_xDistBorderMF, nValue, eCoreUnit); + else + m_xDistBorderMF->set_text(""); + m_xDistBorderMF->set_sensitive(bOn || bSingleSelection); + m_xDistBorderFT->set_sensitive(bOn || bSingleSelection); + bLastRelative = bOn; +} + +void SvxBulletAndPositionDlg::EditModifyHdl_Impl(const weld::Entry* pEdit) +{ + bool bPrefixOrSuffix = (pEdit == m_xPrefixED.get()) || (pEdit == m_xSuffixED.get()); + bool bStart = pEdit == m_xStartED.get(); + sal_uInt16 nMask = 1; + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + { + if (nActNumLvl & nMask) + { + SvxNumberFormat aNumFmt(pActNum->GetLevel(i)); + if (bPrefixOrSuffix) + aNumFmt.SetListFormat(m_xPrefixED->get_text(), m_xSuffixED->get_text(), i); + else if (bStart) + aNumFmt.SetStart(m_xStartED->get_value()); + pActNum->SetLevel(i, aNumFmt); + } + nMask <<= 1; + } + SetModified(); +} + +void SvxBulletAndPositionDlg::SetModified(bool bRepaint) +{ + bModified = true; + if (bRepaint) + { + m_aPreviewWIN.SetLevel(nActNumLvl); + m_aPreviewWIN.Invalidate(); + } +} + +void SvxBulletAndPositionDlg::InitPosAndSpaceMode() +{ + if (pActNum == nullptr) + { + SAL_WARN("cui.tabpages", "<SvxNumPositionTabPage::InitPosAndSpaceMode()> - misusage of " + "method -> <pAktNum> has to be already set!"); + return; + } + + SvxNumberFormat::SvxNumPositionAndSpaceMode ePosAndSpaceMode = SvxNumberFormat::LABEL_ALIGNMENT; + sal_uInt16 nMask = 1; + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); ++i) + { + if (nActNumLvl & nMask) + { + SvxNumberFormat aNumFmt(pActNum->GetLevel(i)); + ePosAndSpaceMode = aNumFmt.GetPositionAndSpaceMode(); + if (ePosAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT) + { + break; + } + } + nMask <<= 1; + } + + bLabelAlignmentPosAndSpaceModeActive = ePosAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/LayerTabBar.cxx b/sd/source/ui/dlg/LayerTabBar.cxx new file mode 100644 index 000000000..41cc90ac7 --- /dev/null +++ b/sd/source/ui/dlg/LayerTabBar.cxx @@ -0,0 +1,437 @@ +/* -*- 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 <LayerTabBar.hxx> +#include <svx/svdlayer.hxx> +#include <svx/svdpagv.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> + +#include <helpids.h> +#include <app.hrc> +#include <strings.hrc> + +#include <DrawViewShell.hxx> +#include <View.hxx> +#include <drawdoc.hxx> +#include <sdresid.hxx> +#include <unokywds.hxx> +#include <DrawDocShell.hxx> +#include <drawview.hxx> +#include <undolayer.hxx> + +namespace sd { + +/** + * default constructor + */ +LayerTabBar::LayerTabBar(DrawViewShell* pViewSh, vcl::Window* pParent) + : TabBar( pParent, WinBits( WB_BORDER | WB_3DLOOK | WB_SCROLL ) ), + DropTargetHelper( this ), + pDrViewSh(pViewSh) +{ + EnableEditMode(); + SetSizePixel(Size(0, 0)); + SetMaxPageWidth( 150 ); + SetHelpId( HID_SD_TABBAR_LAYERS ); +} + +LayerTabBar::~LayerTabBar() +{ + disposeOnce(); +} + +void LayerTabBar::dispose() +{ + DropTargetHelper::dispose(); + TabBar::dispose(); +} + +OUString LayerTabBar::convertToLocalizedName(const OUString& rName) +{ + if ( rName == sUNO_LayerName_background ) + return SdResId( STR_LAYER_BCKGRND ); + + if ( rName == sUNO_LayerName_background_objects ) + return SdResId( STR_LAYER_BCKGRNDOBJ ); + + if ( rName == sUNO_LayerName_layout ) + return SdResId( STR_LAYER_LAYOUT ); + + if ( rName == sUNO_LayerName_controls ) + return SdResId( STR_LAYER_CONTROLS ); + + if ( rName == sUNO_LayerName_measurelines ) + return SdResId( STR_LAYER_MEASURELINES ); + + return rName; +} + +// Use a method name, that is specific to LayerTabBar to make code better readable +OUString LayerTabBar::GetLayerName(sal_uInt16 nPageId) const +{ + return GetAuxiliaryText(nPageId); +} + +void LayerTabBar::SetLayerName( sal_uInt16 nPageId, const OUString& rText ) +{ + SetAuxiliaryText(nPageId, rText); +} + +// Here "Page" is a tab in the LayerTabBar. +void LayerTabBar::InsertPage( sal_uInt16 nPageId, const OUString& rText, + TabBarPageBits nBits, sal_uInt16 nPos) +{ + OUString sLocalizedName(convertToLocalizedName(rText)); + TabBar::InsertPage(nPageId, sLocalizedName, nBits, nPos ); + SetLayerName(nPageId, rText); +} + +void LayerTabBar::SetPageText( sal_uInt16 nPageId, const OUString& rText ) +{ + OUString sLocalizedName(convertToLocalizedName(rText)); + SetLayerName(nPageId, rText); + TabBar::SetPageText(nPageId, sLocalizedName); +} + +bool LayerTabBar::IsLocalizedNameOfStandardLayer(std::u16string_view rName) +{ + return ( rName == SdResId(STR_LAYER_LAYOUT) + || rName == SdResId(STR_LAYER_CONTROLS) + || rName == SdResId(STR_LAYER_MEASURELINES) + || rName == SdResId(STR_LAYER_BCKGRND) + || rName == SdResId(STR_LAYER_BCKGRNDOBJ) ); +} + +bool LayerTabBar::IsRealNameOfStandardLayer(std::u16string_view rName) +{ + return ( rName == sUNO_LayerName_layout + || rName == sUNO_LayerName_controls + || rName == sUNO_LayerName_measurelines + || rName == sUNO_LayerName_background + || rName == sUNO_LayerName_background_objects ); +} + +void LayerTabBar::Select() +{ + SfxDispatcher* pDispatcher = pDrViewSh->GetViewFrame()->GetDispatcher(); + pDispatcher->Execute(SID_SWITCHLAYER, SfxCallMode::ASYNCHRON); +} + +void LayerTabBar::MouseButtonDown(const MouseEvent& rMEvt) +{ + bool bSetPageID=false; + + if (rMEvt.IsLeft()) + { + Point aPosPixel = rMEvt.GetPosPixel(); + sal_uInt16 aTabId = GetPageId( PixelToLogic(aPosPixel) ); + if (aTabId == 0) + { + SfxDispatcher* pDispatcher = pDrViewSh->GetViewFrame()->GetDispatcher(); + pDispatcher->Execute(SID_INSERTLAYER, SfxCallMode::SYNCHRON); + + bSetPageID=true; + } + else if (rMEvt.IsMod2()) + { + // direct editing of tab text + // make sure the clicked tab is the current tab otherwise Edit() acts on the wrong tab + if ( aTabId != GetCurPageId()) + { + MouseEvent aSyntheticEvent (rMEvt.GetPosPixel(), 1, MouseEventModifiers::SYNTHETIC, MOUSE_LEFT, 0); + TabBar::MouseButtonDown(aSyntheticEvent); + } + } + else if (rMEvt.IsMod1() || rMEvt.IsShift()) + { + // keyboard Shortcuts to change layer attributes + + OUString aName(GetLayerName(aTabId)); + SdrPageView* pPV = pDrViewSh->GetView()->GetSdrPageView(); + + // Save old state + + bool bOldPrintable = pPV->IsLayerPrintable(aName); + bool bOldVisible = pPV->IsLayerVisible(aName); + bool bOldLocked = pPV->IsLayerLocked(aName); + + bool bNewPrintable = bOldPrintable; + bool bNewVisible = bOldVisible; + bool bNewLocked = bOldLocked; + + if (rMEvt.IsMod1() && rMEvt.IsShift()) + { + // Shift+Ctrl: Toggle between layer printable / not printable + bNewPrintable = !bOldPrintable; + pPV->SetLayerPrintable(aName, bNewPrintable); + } + else if (rMEvt.IsShift()) + { + // Shift: Toggle between layer visible / hidden + bNewVisible = !bOldVisible; + pPV->SetLayerVisible(aName, bNewVisible); + } + else // if (rMEvt.IsMod1()) + { + // Ctrl: Toggle between layer locked / unlocked + bNewLocked = !bOldLocked; + pPV->SetLayerLocked(aName, bNewLocked); + } + + pDrViewSh->ResetActualLayer(); + + // Add Undo action + + ::sd::View* pView = pDrViewSh->GetView(); + DrawView* pDrView = dynamic_cast<DrawView*>(pView); + + SdDrawDocument& rDoc = pView->GetDoc(); + SdrLayer* pLayer = rDoc.GetLayerAdmin().GetLayer(aName); + + if (pLayer) + { + assert (pDrView && "Change layer attribute undo action is only working with a SdDrawView"); + if(pDrView) + { + SfxUndoManager* pManager = rDoc.GetDocSh()->GetUndoManager(); + std::unique_ptr<SdLayerModifyUndoAction> pAction(new SdLayerModifyUndoAction( + &rDoc, + pLayer, + aName, + pLayer->GetTitle(), + pLayer->GetDescription(), + bOldVisible, + bOldLocked, + bOldPrintable, + aName, + pLayer->GetTitle(), + pLayer->GetDescription(), + bNewVisible, + bNewLocked, + bNewPrintable + )); + pManager->AddUndoAction(std::move(pAction)); + } + } + + // Mark document changed + + pView->GetDoc().SetChanged(); + } + } + + // If you insert a new layer you must not call TabBar::MouseButtonDown(rMEvt); + // because you want to activate the new layer + if( !bSetPageID ) + TabBar::MouseButtonDown(rMEvt); +} + +void LayerTabBar::DoubleClick() +{ + if (GetCurPageId() != 0) + { + SfxDispatcher* pDispatcher = pDrViewSh->GetViewFrame()->GetDispatcher(); + pDispatcher->Execute( SID_MODIFYLAYER, SfxCallMode::SYNCHRON ); + } +} + +/** + * AcceptDrop-Event + */ + +sal_Int8 LayerTabBar::AcceptDrop( const AcceptDropEvent& rEvt ) +{ + sal_Int8 nRet = DND_ACTION_NONE; + + if( rEvt.mbLeaving ) + EndSwitchPage(); + + if( !pDrViewSh->GetDocSh()->IsReadOnly() ) + { + Point aPos( PixelToLogic( rEvt.maPosPixel ) ); + OUString sLayerName( GetLayerName(GetPageId(aPos)) ); + SdrLayerID nLayerId = pDrViewSh->GetView()->GetDoc().GetLayerAdmin().GetLayerID(sLayerName); + + nRet = pDrViewSh->AcceptDrop( rEvt, *this, nullptr, SDRPAGE_NOTFOUND, nLayerId ); + + SwitchPage( aPos ); + } + + return nRet; +} + +/** + * ExecuteDrop-Event + */ +sal_Int8 LayerTabBar::ExecuteDrop( const ExecuteDropEvent& rEvt ) +{ + Point aPos( PixelToLogic(rEvt.maPosPixel) ); + OUString sLayerName( GetLayerName(GetPageId(aPos)) ); + SdrLayerID nLayerId = pDrViewSh->GetView()->GetDoc().GetLayerAdmin().GetLayerID(sLayerName); + + sal_Int8 nRet = pDrViewSh->ExecuteDrop( rEvt, *this, nullptr, SDRPAGE_NOTFOUND, nLayerId ); + + EndSwitchPage(); + + return nRet; + +} + +void LayerTabBar::Command(const CommandEvent& rCEvt) +{ + if ( rCEvt.GetCommand() == CommandEventId::ContextMenu ) + { + SfxDispatcher* pDispatcher = pDrViewSh->GetViewFrame()->GetDispatcher(); + pDispatcher->ExecutePopup("layertab"); + } +} + +bool LayerTabBar::StartRenaming() +{ + bool bOK = true; + OUString aLayerName = GetLayerName( GetEditPageId() ); + + if ( IsRealNameOfStandardLayer(aLayerName)) + { + // It is not allowed to change these names + bOK = false; + } + else + { + ::sd::View* pView = pDrViewSh->GetView(); + + if ( pView->IsTextEdit() ) + { + pView->SdrEndTextEdit(); + } + } + + return bOK; +} + +TabBarAllowRenamingReturnCode LayerTabBar::AllowRenaming() +{ + bool bOK = true; + + // Check if names already exists + ::sd::View* pView = pDrViewSh->GetView(); + SdDrawDocument& rDoc = pView->GetDoc(); + OUString aLayerName = pView->GetActiveLayer(); + SdrLayerAdmin& rLayerAdmin = rDoc.GetLayerAdmin(); + OUString aNewName( GetEditText() ); + + if (aNewName.isEmpty() || + (rLayerAdmin.GetLayer( aNewName ) && aLayerName != aNewName) ) + { + // Name already exists. + std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(pDrViewSh->GetViewFrame()->GetFrameWeld(), + VclMessageType::Warning, VclButtonsType::Ok, + SdResId(STR_WARN_NAME_DUPLICATE))); + xWarn->run(); + bOK = false; + } + + if (bOK) + { + if ( IsLocalizedNameOfStandardLayer(aNewName) || IsRealNameOfStandardLayer(aNewName) ) + { + // Standard layer names may not be changed. + bOK = false; + } + } + + return bOK ? TABBAR_RENAMING_YES : TABBAR_RENAMING_NO; +} + +void LayerTabBar::EndRenaming() +{ + if( IsEditModeCanceled() ) + return; + + ::sd::View* pView = pDrViewSh->GetView(); + DrawView* pDrView = dynamic_cast<DrawView*>( pView ); + + SdDrawDocument& rDoc = pView->GetDoc(); + OUString aLayerName = pView->GetActiveLayer(); + SdrLayerAdmin& rLayerAdmin = rDoc.GetLayerAdmin(); + SdrLayer* pLayer = rLayerAdmin.GetLayer(aLayerName); + + if (!pLayer) + return; + + OUString aNewName( GetEditText() ); + assert (pDrView && "Rename layer undo action is only working with a SdDrawView"); + if( pDrView ) + { + SfxUndoManager* pManager = rDoc.GetDocSh()->GetUndoManager(); + std::unique_ptr<SdLayerModifyUndoAction> pAction(new SdLayerModifyUndoAction( + &rDoc, + pLayer, + aLayerName, + pLayer->GetTitle(), + pLayer->GetDescription(), + pDrView->IsLayerVisible(aLayerName), + pDrView->IsLayerLocked(aLayerName), + pDrView->IsLayerPrintable(aLayerName), + aNewName, + pLayer->GetTitle(), + pLayer->GetDescription(), + pDrView->IsLayerVisible(aLayerName), + pDrView->IsLayerLocked(aLayerName), + pDrView->IsLayerPrintable(aLayerName) + )); + pManager->AddUndoAction( std::move(pAction) ); + } + + // First notify View since SetName() calls ResetActualLayer() and + // the View then already has to know the Layer + pView->SetActiveLayer(aNewName); + pLayer->SetName(aNewName); + rDoc.SetChanged(); +} + +void LayerTabBar::ActivatePage() +{ + if (pDrViewSh!=nullptr) + { + + SfxDispatcher* pDispatcher = pDrViewSh->GetViewFrame()->GetDispatcher(); + pDispatcher->Execute(SID_SWITCHLAYER, SfxCallMode::ASYNCHRON); + } +} + +void LayerTabBar::SendActivatePageEvent() +{ + CallEventListeners (VclEventId::TabbarPageActivated, + reinterpret_cast<void*>(GetCurPageId())); +} + +void LayerTabBar::SendDeactivatePageEvent() +{ + CallEventListeners (VclEventId::TabbarPageDeactivated, + reinterpret_cast<void*>(GetCurPageId())); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/NavigatorChildWindow.cxx b/sd/source/ui/dlg/NavigatorChildWindow.cxx new file mode 100644 index 000000000..6055c238a --- /dev/null +++ b/sd/source/ui/dlg/NavigatorChildWindow.cxx @@ -0,0 +1,100 @@ +/* -*- 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 <NavigatorChildWindow.hxx> +#include <navigatr.hxx> +#include <app.hrc> +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/sfxsids.hrc> +#include <svl/eitem.hxx> + +namespace sd { + +static void RequestNavigatorUpdate (SfxBindings const * pBindings) +{ + if (pBindings != nullptr + && pBindings->GetDispatcher() != nullptr) + { + SfxBoolItem aItem (SID_NAVIGATOR_INIT, true); + pBindings->GetDispatcher()->ExecuteList( + SID_NAVIGATOR_INIT, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aItem }); + } +} + +SdNavigatorFloat::SdNavigatorFloat(SfxBindings* _pBindings, SfxChildWindow* _pMgr, + vcl::Window* _pParent, SfxChildWinInfo* pInfo) + : SfxNavigator(_pBindings, _pMgr, _pParent, pInfo) + , m_xNavWin(std::make_unique<SdNavigatorWin>(m_xContainer.get(), _pBindings, this)) + , m_bSetInitialFocusOnActivate(true) +{ + m_xNavWin->SetUpdateRequestFunctor( + [_pBindings] () { return RequestNavigatorUpdate(_pBindings); }); + + SetMinOutputSizePixel(GetOptimalSize()); +} + +void SdNavigatorFloat::Activate() +{ + SfxNavigator::Activate(); + // tdf#141708 defer grabbing focus to preferred widget until the float is + // first activated + if (m_bSetInitialFocusOnActivate) + { + m_xNavWin->FirstFocus(); + m_bSetInitialFocusOnActivate = false; + } +} + +void SdNavigatorFloat::InitTreeLB(const SdDrawDocument* pDoc) +{ + m_xNavWin->InitTreeLB(pDoc); +} + +void SdNavigatorFloat::FreshTree(const SdDrawDocument* pDoc) +{ + m_xNavWin->FreshTree(pDoc); +} + +void SdNavigatorFloat::dispose() +{ + m_xNavWin.reset(); + SfxNavigator::dispose(); +} + +SdNavigatorFloat::~SdNavigatorFloat() +{ + disposeOnce(); +} + +SFX_IMPL_DOCKINGWINDOW(SdNavigatorWrapper, SID_NAVIGATOR); + +SdNavigatorWrapper::SdNavigatorWrapper(vcl::Window *_pParent, sal_uInt16 nId, + SfxBindings* pBindings, SfxChildWinInfo* pInfo) + : SfxNavigatorWrapper(_pParent, nId) +{ + SetWindow(VclPtr<SdNavigatorFloat>::Create(pBindings, this, _pParent, pInfo)); + Initialize(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/PaneChildWindows.cxx b/sd/source/ui/dlg/PaneChildWindows.cxx new file mode 100644 index 000000000..7f73e005b --- /dev/null +++ b/sd/source/ui/dlg/PaneChildWindows.cxx @@ -0,0 +1,107 @@ +/* -*- 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 <PaneChildWindows.hxx> +#include <PaneDockingWindow.hxx> +#include <ViewShellBase.hxx> +#include <framework/FrameworkHelper.hxx> +#include <app.hrc> +#include <strings.hrc> +#include <sdresid.hxx> + +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> + +namespace sd { + + +SFX_IMPL_DOCKINGWINDOW_WITHID(LeftPaneImpressChildWindow, SID_LEFT_PANE_IMPRESS) +SFX_IMPL_DOCKINGWINDOW_WITHID(LeftPaneDrawChildWindow, SID_LEFT_PANE_DRAW) + +//===== PaneChildWindow ======================================================= +PaneChildWindow::PaneChildWindow ( + vcl::Window* pParentWindow, + sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo* pInfo, + TranslateId pTitleBarResId) + : SfxChildWindow (pParentWindow, nId) +{ + SetWindow( VclPtr<PaneDockingWindow>::Create( + pBindings, + this, + pParentWindow, + SdResId(pTitleBarResId))); + SetAlignment(SfxChildAlignment::LEFT); + SfxDockingWindow* pDockingWindow = static_cast<SfxDockingWindow*>(GetWindow()); + pDockingWindow->EnableInput(); + pDockingWindow->Initialize(pInfo); + SetHideNotDelete(true); + + ViewShellBase* pBase = ViewShellBase::GetViewShellBase(pBindings->GetDispatcher()->GetFrame()); + if (pBase != nullptr) + { + framework::FrameworkHelper::Instance(*pBase)->UpdateConfiguration(); + } +} + +PaneChildWindow::~PaneChildWindow() +{ + ViewShellBase* pBase = nullptr; + PaneDockingWindow* pDockingWindow = dynamic_cast<PaneDockingWindow*>(GetWindow()); + if (pDockingWindow != nullptr) + pBase = ViewShellBase::GetViewShellBase( + pDockingWindow->GetBindings().GetDispatcher()->GetFrame()); + if (pBase != nullptr) + framework::FrameworkHelper::Instance(*pBase)->UpdateConfiguration(); +} + +//===== LeftPaneImpressChildWindow ============================================ +LeftPaneImpressChildWindow::LeftPaneImpressChildWindow ( + vcl::Window* pParentWindow, + sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo* pInfo) + : PaneChildWindow( + pParentWindow, + nId, + pBindings, + pInfo, + STR_LEFT_PANE_IMPRESS_TITLE) +{ +} + +//===== LeftPaneDrawChildWindow =============================================== +LeftPaneDrawChildWindow::LeftPaneDrawChildWindow ( + vcl::Window* pParentWindow, + sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo* pInfo) + : PaneChildWindow( + pParentWindow, + nId, + pBindings, + pInfo, + STR_LEFT_PANE_DRAW_TITLE) +{ +} + +} // end of namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/PaneDockingWindow.cxx b/sd/source/ui/dlg/PaneDockingWindow.cxx new file mode 100644 index 000000000..6f7332ad9 --- /dev/null +++ b/sd/source/ui/dlg/PaneDockingWindow.cxx @@ -0,0 +1,127 @@ +/* -*- 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 <PaneDockingWindow.hxx> +#include <ViewShellBase.hxx> +#include <framework/FrameworkHelper.hxx> + +#include <sfx2/dispatch.hxx> +#include <vcl/splitwin.hxx> +#include <tools/wintypes.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; +using ::sd::TitledDockingWindow; + +namespace sd { + +PaneDockingWindow::PaneDockingWindow( + SfxBindings *_pBindings, SfxChildWindow *pChildWindow, vcl::Window* pParent, + const OUString& rsTitle ) + : TitledDockingWindow(_pBindings, pChildWindow, pParent) +{ + SetTitle(rsTitle); + SetSizePixel(LogicToPixel(Size(80,200), MapMode(MapUnit::MapAppFont))); +} + +PaneDockingWindow::~PaneDockingWindow() +{ +} + +void PaneDockingWindow::StateChanged( StateChangedType nType ) +{ + switch (nType) + { + case StateChangedType::InitShow: + Resize(); + GetContentWindow().SetStyle(GetContentWindow().GetStyle() | WB_DIALOGCONTROL); + break; + + case StateChangedType::Visible: + { + // The visibility of the docking window has changed. Tell the + // ConfigurationController so that it can activate or deactivate + // a/the view for the pane. + // Without this the side panes remain empty after closing an + // in-place slide show. + ViewShellBase* pBase = ViewShellBase::GetViewShellBase( + GetBindings().GetDispatcher()->GetFrame()); + if (pBase != nullptr) + { + framework::FrameworkHelper::Instance(*pBase)->UpdateConfiguration(); + } + } + break; + + default:; + } + SfxDockingWindow::StateChanged (nType); +} + +void PaneDockingWindow::MouseButtonDown (const MouseEvent& rEvent) +{ + if (rEvent.GetButtons() == MOUSE_LEFT) + { + // For some strange reason we have to set the WB_DIALOGCONTROL at + // the content window in order to have it pass focus to its content + // window. Without setting this flag here that works only on views + // that have not been taken from the cash and relocated to this pane + // docking window. + GetContentWindow().SetStyle(GetContentWindow().GetStyle() | WB_DIALOGCONTROL); + GetContentWindow().GrabFocus(); + } + SfxDockingWindow::MouseButtonDown(rEvent); +} + +void PaneDockingWindow::SetValidSizeRange (const Range& rValidSizeRange) +{ + SplitWindow* pSplitWindow = dynamic_cast<SplitWindow*>(GetParent()); + if (pSplitWindow == nullptr) + return; + + const sal_uInt16 nId (pSplitWindow->GetItemId(static_cast< vcl::Window*>(this))); + const sal_uInt16 nSetId (pSplitWindow->GetSet(nId)); + // Because the PaneDockingWindow paints its own decoration, we have + // to compensate the valid size range for that. + const SvBorder aBorder (GetDecorationBorder()); + sal_Int32 nCompensation (pSplitWindow->IsHorizontal() + ? aBorder.Top() + aBorder.Bottom() + : aBorder.Left() + aBorder.Right()); + pSplitWindow->SetItemSizeRange( + nSetId, + Range( + rValidSizeRange.Min() + nCompensation, + rValidSizeRange.Max() + nCompensation)); +} + +PaneDockingWindow::Orientation PaneDockingWindow::GetOrientation() const +{ + SplitWindow* pSplitWindow = dynamic_cast<SplitWindow*>(GetParent()); + if (pSplitWindow == nullptr) + return UnknownOrientation; + else if (pSplitWindow->IsHorizontal()) + return HorizontalOrientation; + else + return VerticalOrientation; +} + +} // end of namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/PaneShells.cxx b/sd/source/ui/dlg/PaneShells.cxx new file mode 100644 index 000000000..8870d1186 --- /dev/null +++ b/sd/source/ui/dlg/PaneShells.cxx @@ -0,0 +1,79 @@ +/* -*- 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 <PaneShells.hxx> + +#include <PaneChildWindows.hxx> + +#include <sfx2/msg.hxx> +#include <sfx2/objface.hxx> + +namespace sd { + +//===== LeftImpressPaneShell ================================================== + +static SfxSlot aLeftImpressPaneShellSlots_Impl[] = +{ + { 0, SfxGroupId::NONE, SfxSlotMode::NONE, 0, 0, nullptr, nullptr, nullptr, nullptr, nullptr, 0, SfxDisableFlags::NONE, nullptr } +}; + +SFX_IMPL_INTERFACE(LeftImpressPaneShell, SfxShell) + +void LeftImpressPaneShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterChildWindow(::sd::LeftPaneImpressChildWindow::GetChildWindowId()); +} + + +LeftImpressPaneShell::LeftImpressPaneShell() +{ + SetName("LeftImpressPane"); +} + +LeftImpressPaneShell::~LeftImpressPaneShell() +{ +} + +//===== LeftDrawPaneShell ===================================================== + +static SfxSlot aLeftDrawPaneShellSlots_Impl[] = +{ + { 0, SfxGroupId::NONE, SfxSlotMode::NONE, 0, 0, nullptr, nullptr, nullptr, nullptr, nullptr, 0, SfxDisableFlags::NONE, nullptr } +}; + +SFX_IMPL_INTERFACE(LeftDrawPaneShell, SfxShell) + +void LeftDrawPaneShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterChildWindow(::sd::LeftPaneDrawChildWindow::GetChildWindowId()); +} + + +LeftDrawPaneShell::LeftDrawPaneShell() +{ + SetName("LeftDrawPane"); +} + +LeftDrawPaneShell::~LeftDrawPaneShell() +{ +} + +} // end of namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/PhotoAlbumDialog.cxx b/sd/source/ui/dlg/PhotoAlbumDialog.cxx new file mode 100644 index 000000000..f63afe7bb --- /dev/null +++ b/sd/source/ui/dlg/PhotoAlbumDialog.cxx @@ -0,0 +1,775 @@ +/* -*- 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/. +*/ + +#include <comphelper/namedvaluecollection.hxx> +#include <comphelper/processfactory.hxx> +#include <svl/itemset.hxx> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#include <com/sun/star/graphic/GraphicProvider.hpp> +#include <com/sun/star/drawing/XDrawPagesSupplier.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> + +#include <sfx2/filedlghelper.hxx> +#include <tools/urlobj.hxx> + +#include <unotools/ucbstreamhelper.hxx> +#include <vcl/graphicfilter.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xfltrit.hxx> +#include <svx/xflclit.hxx> +#include <tools/diagnose_ex.h> +#include <xmloff/autolayout.hxx> + +#include "PhotoAlbumDialog.hxx" +#include <strings.hrc> +#include <sdresid.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> + +namespace sd +{ + +SdPhotoAlbumDialog::SdPhotoAlbumDialog(weld::Window* pWindow, SdDrawDocument* pActDoc) + : GenericDialogController(pWindow, "modules/simpress/ui/photoalbum.ui", "PhotoAlbumCreatorDialog") + , m_pDoc(pActDoc) + , m_aImg(m_xDialog.get()) + , m_xCancelBtn(m_xBuilder->weld_button("cancel")) + , m_xCreateBtn(m_xBuilder->weld_button("ok")) + , m_xAddBtn(m_xBuilder->weld_button("add_btn")) + , m_xUpBtn(m_xBuilder->weld_button("up_btn")) + , m_xDownBtn(m_xBuilder->weld_button("down_btn")) + , m_xRemoveBtn(m_xBuilder->weld_button("rem_btn")) + , m_xImagesLst(m_xBuilder->weld_tree_view("images_tree")) + , m_xImg(new weld::CustomWeld(*m_xBuilder, "preview_img", m_aImg)) + , m_xInsTypeCombo(m_xBuilder->weld_combo_box("opt_combo")) + , m_xASRCheck(m_xBuilder->weld_check_button("asr_check")) + , m_xASRCheckCrop(m_xBuilder->weld_check_button("asr_check_crop")) + , m_xCapCheck(m_xBuilder->weld_check_button("cap_check")) + , m_xInsertAsLinkCheck(m_xBuilder->weld_check_button("insert_as_link_check")) +{ + m_xCancelBtn->connect_clicked(LINK(this, SdPhotoAlbumDialog, CancelHdl)); + m_xCreateBtn->connect_clicked(LINK(this, SdPhotoAlbumDialog, CreateHdl)); + + m_xAddBtn->connect_clicked(LINK(this, SdPhotoAlbumDialog, FileHdl)); + m_xUpBtn->connect_clicked(LINK(this, SdPhotoAlbumDialog, UpHdl)); + m_xUpBtn->set_sensitive(false); + m_xDownBtn->connect_clicked(LINK(this, SdPhotoAlbumDialog, DownHdl)); + m_xDownBtn->set_sensitive(false); + m_xRemoveBtn->connect_clicked(LINK(this, SdPhotoAlbumDialog, RemoveHdl)); + m_xRemoveBtn->set_sensitive(false); + m_xImagesLst->connect_changed(LINK(this, SdPhotoAlbumDialog, SelectHdl)); + m_xInsTypeCombo->connect_changed(LINK(this, SdPhotoAlbumDialog, TypeSelectHdl)); + + m_pGraphicFilter = new GraphicFilter; + m_xAddBtn->grab_focus(); +} + +SdPhotoAlbumDialog::~SdPhotoAlbumDialog() +{ +} + +IMPL_LINK_NOARG(SdPhotoAlbumDialog, CancelHdl, weld::Button&, void) +{ + m_xDialog->response(RET_CANCEL); +} + +IMPL_LINK_NOARG(SdPhotoAlbumDialog, CreateHdl, weld::Button&, void) +{ + if (m_xImagesLst->n_children() == 0) + { + std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Warning, VclButtonsType::Ok, + SdResId(STR_PHOTO_ALBUM_EMPTY_WARNING))); + xWarn->run(); + } + else + { + Reference< drawing::XDrawPagesSupplier > xDPS( m_pDoc->getUnoModel(), uno::UNO_QUERY ); + Reference< drawing::XDrawPages > xDrawPages = xDPS->getDrawPages(); + Reference< lang::XMultiServiceFactory > xShapeFactory( m_pDoc->getUnoModel(), uno::UNO_QUERY ); + + Reference< XComponentContext > xContext(::comphelper::getProcessComponentContext()); + Reference< graphic::XGraphicProvider> xProvider(graphic::GraphicProvider::create(xContext)); + + // determine if to use Captions (use TitleObject) and choose the correct AutoLayout + // from the beginning + const bool bCreateCaptions(m_xCapCheck->get_active()); + const bool bInsertAsLink(m_xInsertAsLinkCheck->get_active()); + const AutoLayout aAutoLayout(bCreateCaptions ? AUTOLAYOUT_TITLE_ONLY : AUTOLAYOUT_NONE); + + // get the option + const int nOpt = m_xInsTypeCombo->get_active(); + if (nOpt == ONE_IMAGE) + { + for( sal_Int32 i = 0; i < m_xImagesLst->n_children(); ++i ) + { + OUString sUrl = m_xImagesLst->get_id(i); + + Reference< drawing::XDrawPage > xSlide = appendNewSlide(aAutoLayout, xDrawPages); + Reference< beans::XPropertySet > xSlideProps( xSlide, uno::UNO_QUERY ); + Reference< graphic::XGraphic > xGraphic = createXGraphicFromUrl(sUrl, xProvider); + + Graphic aGraphic(xGraphic); + if (bInsertAsLink) + aGraphic.setOriginURL(sUrl); + + // Save the original size, multiplied with 100 + ::awt::Size aPicSize(aGraphic.GetSizePixel().Width()*100, aGraphic.GetSizePixel().Height()*100); + + Reference< drawing::XShape > xShape( + xShapeFactory->createInstance("com.sun.star.drawing.GraphicObjectShape"), + uno::UNO_QUERY); + + Reference< beans::XPropertySet > xProps( xShape, uno::UNO_QUERY ); + xProps->setPropertyValue("Graphic", ::uno::Any(xGraphic)); + + ::awt::Size aPageSize; + + xSlideProps->getPropertyValue( + "Width") >>= aPageSize.Width; + xSlideProps->getPropertyValue( + "Height") >>= aPageSize.Height; + + ::awt::Point aPicPos; + + if (m_xASRCheck->get_active() && !m_xASRCheckCrop->get_active()) + { + // Resize the image, with keeping ASR + aPicSize = createASRSize(aPicSize, aPageSize); + } + else if (m_xASRCheckCrop->get_active()) + { + aPicSize = createASRSizeCrop(aPicSize, aPageSize); + } + + xShape->setSize(aPicSize); + aPicPos.X = (aPageSize.Width - aPicSize.Width)/2; + aPicPos.Y = (aPageSize.Height - aPicSize.Height)/2; + + xShape->setPosition(aPicPos); + try + { + xSlide->add(xShape); + if (bCreateCaptions) + createCaption( aPageSize ); + } + catch (const css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "" ); + } + } + } + else if( nOpt == TWO_IMAGES ) + { + for( sal_Int32 i = 0; i < m_xImagesLst->n_children(); i+=2 ) + { + // create the slide + Reference< drawing::XDrawPage > xSlide = appendNewSlide(aAutoLayout, xDrawPages); + Reference< beans::XPropertySet > xSlideProps( xSlide, uno::UNO_QUERY ); + //Slide dimensions + ::awt::Size aPageSize; + + xSlideProps->getPropertyValue( + "Width") >>= aPageSize.Width; + xSlideProps->getPropertyValue( + "Height") >>= aPageSize.Height; + + // grab the left one + OUString sUrl1 = m_xImagesLst->get_id(i); + // grab the right one + OUString sUrl2 = m_xImagesLst->get_id(i+1); + + if( !sUrl1.isEmpty() ) + { + Reference< graphic::XGraphic > xGraphic = createXGraphicFromUrl(sUrl1, xProvider); + + Graphic aGraphic(xGraphic); + if (bInsertAsLink) + aGraphic.setOriginURL(sUrl1); + // Save the original size, multiplied with 100 + ::awt::Size aPicSize(aGraphic.GetSizePixel().Width()*100, aGraphic.GetSizePixel().Height()*100); + + Reference< drawing::XShape > xShape( + xShapeFactory->createInstance("com.sun.star.drawing.GraphicObjectShape"), + uno::UNO_QUERY); + + Reference< beans::XPropertySet > xProps( xShape, uno::UNO_QUERY ); + xProps->setPropertyValue("Graphic", ::uno::Any(xGraphic)); + + ::awt::Point aPicPos; + + if (m_xASRCheck->get_active()) + { + // Resize the image, with keeping ASR + aPicSize = createASRSize(aPicSize, ::awt::Size(aPageSize.Width/2 - 100, aPageSize.Height/2 - 100)); + } + else + { + aPicSize.Width = aPageSize.Width/2 - 100; + aPicSize.Height = aPageSize.Height/2 - 100; + } + xShape->setSize(aPicSize); + aPicPos.X = (aPageSize.Width/4 - aPicSize.Width/2); + aPicPos.Y = aPageSize.Height/2 - aPicSize.Height/2; + + xShape->setPosition(aPicPos); + try + { + xSlide->add(xShape); + } + catch (const css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "" ); + } + } + + if( !sUrl2.isEmpty() ) + { + Reference< graphic::XGraphic > xGraphic = createXGraphicFromUrl(sUrl2, xProvider); + + Graphic aGraphic(xGraphic); + if (bInsertAsLink) + aGraphic.setOriginURL(sUrl2); + // Save the original size, multiplied with 100 + ::awt::Size aPicSize(aGraphic.GetSizePixel().Width()*100, aGraphic.GetSizePixel().Height()*100); + + Reference< drawing::XShape > xShape( + xShapeFactory->createInstance("com.sun.star.drawing.GraphicObjectShape"), + uno::UNO_QUERY); + + Reference< beans::XPropertySet > xProps( xShape, uno::UNO_QUERY ); + xProps->setPropertyValue("Graphic", ::uno::Any(xGraphic)); + + ::awt::Point aPicPos; + + if (m_xASRCheck->get_active()) + { + // Resize the image, with keeping ASR + aPicSize = createASRSize(aPicSize, ::awt::Size(aPageSize.Width/2 - 100, aPageSize.Height/2 - 100)); + } + else + { + aPicSize.Width = aPageSize.Width/2 - 100; + aPicSize.Height = aPageSize.Height/2 - 100; + } + xShape->setSize(aPicSize); + aPicPos.X = (aPageSize.Width/4 - aPicSize.Width/2) + aPageSize.Width/2; + aPicPos.Y = aPageSize.Height/2 - aPicSize.Height/2; + + xShape->setPosition(aPicPos); + + try + { + xSlide->add(xShape); + if(bCreateCaptions) + createCaption( aPageSize ); + } + catch (const css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "" ); + } + } + } + } + else if( nOpt == FOUR_IMAGES ) + { + for( sal_Int32 i = 0; i < m_xImagesLst->n_children(); i+=4 ) + { + // create the slide + Reference< drawing::XDrawPage > xSlide = appendNewSlide(aAutoLayout, xDrawPages); + Reference< beans::XPropertySet > xSlideProps( xSlide, uno::UNO_QUERY ); + //Slide dimensions + ::awt::Size aPageSize; + + xSlideProps->getPropertyValue( + "Width") >>= aPageSize.Width; + xSlideProps->getPropertyValue( + "Height") >>= aPageSize.Height; + + // grab the upper left one + OUString sUrl1 = m_xImagesLst->get_id(i); + + // grab the upper right one + OUString sUrl2 = m_xImagesLst->get_id(i+1); + + // grab the lower left one + OUString sUrl3 = m_xImagesLst->get_id(i+2); + + // grab the lower right one + OUString sUrl4 = m_xImagesLst->get_id(i+3); + + if( !sUrl1.isEmpty() ) + { + Reference< graphic::XGraphic > xGraphic = createXGraphicFromUrl(sUrl1, xProvider); + + Graphic aGraphic(xGraphic); + if (bInsertAsLink) + aGraphic.setOriginURL(sUrl1); + // Save the original size, multiplied with 100 + ::awt::Size aPicSize(aGraphic.GetSizePixel().Width()*100, aGraphic.GetSizePixel().Height()*100); + + Reference< drawing::XShape > xShape( + xShapeFactory->createInstance("com.sun.star.drawing.GraphicObjectShape"), + uno::UNO_QUERY); + + Reference< beans::XPropertySet > xProps( xShape, uno::UNO_QUERY ); + xProps->setPropertyValue("Graphic", ::uno::Any(xGraphic)); + + ::awt::Point aPicPos; + + if (m_xASRCheck->get_active()) + { + // Resize the image, with keeping ASR + aPicSize = createASRSize(aPicSize, ::awt::Size(aPageSize.Width/2 - 100, aPageSize.Height/2 - 100)); + } + else + { + aPicSize.Width = aPageSize.Width/2 - 100; + aPicSize.Height = aPageSize.Height/2 - 100; + } + xShape->setSize(aPicSize); + aPicPos.X = (aPageSize.Width/4 - aPicSize.Width/2); + aPicPos.Y = aPageSize.Height/4 - aPicSize.Height/2; + + xShape->setPosition(aPicPos); + try + { + xSlide->add(xShape); + } + catch (const css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "" ); + } + } + if( !sUrl2.isEmpty() ) + { + Reference< graphic::XGraphic > xGraphic = createXGraphicFromUrl(sUrl2, xProvider); + + Graphic aGraphic(xGraphic); + if (bInsertAsLink) + aGraphic.setOriginURL(sUrl2); + // Save the original size, multiplied with 100 + ::awt::Size aPicSize(aGraphic.GetSizePixel().Width()*100, aGraphic.GetSizePixel().Height()*100); + + Reference< drawing::XShape > xShape( + xShapeFactory->createInstance("com.sun.star.drawing.GraphicObjectShape"), + uno::UNO_QUERY); + + Reference< beans::XPropertySet > xProps( xShape, uno::UNO_QUERY ); + xProps->setPropertyValue("Graphic", ::uno::Any(xGraphic)); + + ::awt::Point aPicPos; + + if (m_xASRCheck->get_active()) + { + // Resize the image, with keeping ASR + aPicSize = createASRSize(aPicSize, ::awt::Size(aPageSize.Width/2 - 100, aPageSize.Height/2 - 100)); + } + else + { + aPicSize.Width = aPageSize.Width/2 - 100; + aPicSize.Height = aPageSize.Height/2 - 100; + } + xShape->setSize(aPicSize); + aPicPos.X = (aPageSize.Width/4 - aPicSize.Width/2) + aPageSize.Width/2; + aPicPos.Y = aPageSize.Height/4 - aPicSize.Height/2; + + xShape->setPosition(aPicPos); + try + { + xSlide->add(xShape); + } + catch (const css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "" ); + } + } + if( !sUrl3.isEmpty() ) + { + Reference< graphic::XGraphic > xGraphic = createXGraphicFromUrl(sUrl3, xProvider); + + Graphic aGraphic(xGraphic); + if (bInsertAsLink) + aGraphic.setOriginURL(sUrl3); + // Save the original size, multiplied with 100 + ::awt::Size aPicSize(aGraphic.GetSizePixel().Width()*100, aGraphic.GetSizePixel().Height()*100); + + Reference< drawing::XShape > xShape( + xShapeFactory->createInstance("com.sun.star.drawing.GraphicObjectShape"), + uno::UNO_QUERY); + + Reference< beans::XPropertySet > xProps( xShape, uno::UNO_QUERY ); + xProps->setPropertyValue("Graphic", ::uno::Any(xGraphic)); + + ::awt::Point aPicPos; + + if (m_xASRCheck->get_active()) + { + // Resize the image, with keeping ASR + aPicSize = createASRSize(aPicSize, ::awt::Size(aPageSize.Width/2 - 100, aPageSize.Height/2 - 100)); + } + else + { + aPicSize.Width = aPageSize.Width/2 - 100; + aPicSize.Height = aPageSize.Height/2 - 100; + } + xShape->setSize(aPicSize); + aPicPos.X = (aPageSize.Width/4 - aPicSize.Width/2); + aPicPos.Y = aPageSize.Height/4 - aPicSize.Height/2 + aPageSize.Height/2; + + xShape->setPosition(aPicPos); + try + { + xSlide->add(xShape); + } + catch (const css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "" ); + } + } + if( !sUrl4.isEmpty() ) + { + Reference< graphic::XGraphic > xGraphic = createXGraphicFromUrl(sUrl4, xProvider); + + Graphic aGraphic(xGraphic); + if (bInsertAsLink) + aGraphic.setOriginURL(sUrl4); + // Save the original size, multiplied with 100 + ::awt::Size aPicSize(aGraphic.GetSizePixel().Width()*100, aGraphic.GetSizePixel().Height()*100); + + Reference< drawing::XShape > xShape( + xShapeFactory->createInstance("com.sun.star.drawing.GraphicObjectShape"), + uno::UNO_QUERY); + + Reference< beans::XPropertySet > xProps( xShape, uno::UNO_QUERY ); + xProps->setPropertyValue("Graphic", ::uno::Any(xGraphic)); + + ::awt::Point aPicPos; + + if (m_xASRCheck->get_active()) + { + // Resize the image, with keeping ASR + aPicSize = createASRSize(aPicSize, ::awt::Size(aPageSize.Width/2 - 100, aPageSize.Height/2 - 100)); + } + else + { + aPicSize.Width = aPageSize.Width/2 - 100; + aPicSize.Height = aPageSize.Height/2 - 100; + } + xShape->setSize(aPicSize); + aPicPos.X = (aPageSize.Width/4 - aPicSize.Width/2) + aPageSize.Width/2; + aPicPos.Y = aPageSize.Height/4 - aPicSize.Height/2 + aPageSize.Height/2; + + xShape->setPosition(aPicPos); + try + { + xSlide->add(xShape); + if(bCreateCaptions) + createCaption( aPageSize ); + } + catch (const css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "" ); + } + } + } + } + else + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Info, VclButtonsType::Ok, + "Function is not implemented!")); + xInfoBox->run(); + } + m_xDialog->response(RET_OK); + } +} + +IMPL_LINK_NOARG(SdPhotoAlbumDialog, FileHdl, weld::Button&, void) +{ + ::sfx2::FileDialogHelper aDlg( + css::ui::dialogs::TemplateDescription::FILEOPEN_PREVIEW, + FileDialogFlags::Graphic | FileDialogFlags::MultiSelection, m_xDialog.get()); + aDlg.SetContext(sfx2::FileDialogHelper::ImpressPhotoDialog); + + if ( aDlg.Execute() == ERRCODE_NONE ) + { + const Sequence< OUString > aFilesArr = aDlg.GetSelectedFiles(); + for ( const auto& rFile : aFilesArr ) + { + // Store full path, show filename only. Use INetURLObject to display spaces in filename correctly + INetURLObject aUrl(rFile); + m_xImagesLst->append(aUrl.GetMainURL(INetURLObject::DecodeMechanism::NONE), aUrl.GetLastName(INetURLObject::DecodeMechanism::WithCharset), ""); + } + } + EnableDisableButtons(); +} + +IMPL_LINK_NOARG(SdPhotoAlbumDialog, UpHdl, weld::Button&, void) +{ + const int nActPos = m_xImagesLst->get_selected_index(); + if (nActPos != -1 && nActPos != 0) + { + OUString sActEntry(m_xImagesLst->get_text(nActPos)); + // actual data + OUString sAct(m_xImagesLst->get_id(nActPos)); + + OUString sUpperEntry(m_xImagesLst->get_text(nActPos - 1)); + // upper data + OUString sUpper(m_xImagesLst->get_id(nActPos - 1)); + + m_xImagesLst->remove_text(sActEntry); + m_xImagesLst->remove_text(sUpperEntry); + + m_xImagesLst->insert(nActPos - 1, sActEntry, &sAct, nullptr, nullptr); + m_xImagesLst->insert(nActPos, sUpperEntry, &sUpper, nullptr, nullptr); + + m_xImagesLst->select(nActPos - 1); + } + + EnableDisableButtons(); +} + +IMPL_LINK_NOARG(SdPhotoAlbumDialog, DownHdl, weld::Button&, void) +{ + const int nActPos = m_xImagesLst->get_selected_index(); + if (!m_xImagesLst->get_text(nActPos + 1).isEmpty()) + { + OUString sActEntry(m_xImagesLst->get_selected_text()); + OUString sAct(m_xImagesLst->get_selected_id()); + + OUString sDownEntry(m_xImagesLst->get_text(nActPos + 1)); + OUString sDown(m_xImagesLst->get_id(nActPos + 1)); + + m_xImagesLst->remove_text(sActEntry); + m_xImagesLst->remove_text(sDownEntry); + + m_xImagesLst->insert(nActPos, sDownEntry, &sDown, nullptr, nullptr); + m_xImagesLst->insert(nActPos + 1, sActEntry, &sAct, nullptr, nullptr); + + m_xImagesLst->select(nActPos + 1); + } + EnableDisableButtons(); +} + +IMPL_LINK_NOARG(SdPhotoAlbumDialog, RemoveHdl, weld::Button&, void) +{ + m_xImagesLst->remove(m_xImagesLst->get_selected_index()); + m_aImg.SetGraphic(Graphic()); + + EnableDisableButtons(); +} + +IMPL_LINK_NOARG(SdPhotoAlbumDialog, SelectHdl, weld::TreeView&, void) +{ + OUString sImgUrl = m_xImagesLst->get_selected_id(); + + if (sImgUrl != SdResId(STR_PHOTO_ALBUM_TEXTBOX)) + { + Graphic aGraphic; + INetURLObject aURLObj( sImgUrl ); + + sal_uInt16 nFilter = GRFILTER_FORMAT_DONTKNOW; + + if ( aURLObj.HasError() || INetProtocol::NotValid == aURLObj.GetProtocol() ) + { + aURLObj.SetSmartProtocol( INetProtocol::File ); + aURLObj.SetSmartURL( sImgUrl ); + } + + GraphicFilterImportFlags nFilterImportFlags = GraphicFilterImportFlags::SetLogsizeForJpeg; + // remote? + if ( INetProtocol::File != aURLObj.GetProtocol() ) + { + std::unique_ptr<SvStream> pStream = ::utl::UcbStreamHelper::CreateStream( sImgUrl, StreamMode::READ ); + + if( pStream ) + m_pGraphicFilter->ImportGraphic( aGraphic, sImgUrl, *pStream, nFilter, nullptr, nFilterImportFlags ); + else + m_pGraphicFilter->ImportGraphic( aGraphic, aURLObj, nFilter, nullptr, nFilterImportFlags ); + } + else + { + m_pGraphicFilter->ImportGraphic( aGraphic, aURLObj, nFilter, nullptr, nFilterImportFlags ); + } + + BitmapEx aBmp = aGraphic.GetBitmapEx(); + sal_Int32 nBmpWidth = aBmp.GetSizePixel().Width(); + sal_Int32 nBmpHeight = aBmp.GetSizePixel().Height(); + + double nXRatio = double(200) / nBmpWidth; + double nYRatio = double(150) / nBmpHeight; + if ( nXRatio < nYRatio ) + aBmp.Scale( nXRatio, nXRatio ); + else + aBmp.Scale( nYRatio, nYRatio ); + + aBmp.Convert( BmpConversion::N24Bit ); + m_aImg.SetGraphic(Graphic(aBmp)); + } + else + { + m_aImg.SetGraphic(Graphic()); + } + EnableDisableButtons(); +} + +IMPL_LINK_NOARG(SdPhotoAlbumDialog, TypeSelectHdl, weld::ComboBox&, void) +{ + // Enable "Fill Slide" only for one image + // If we want to have it for other images too, we need to implement the actual cropping. + bool const bEnable = m_xInsTypeCombo->get_active() == ONE_IMAGE; + m_xASRCheckCrop->set_sensitive(bEnable); + if (!bEnable) + m_xASRCheckCrop->set_active(false); +} + +Reference< drawing::XDrawPage > SdPhotoAlbumDialog::appendNewSlide(AutoLayout aLayout, + const Reference< drawing::XDrawPages >& xDrawPages +) +{ + // Create the slide + Reference< drawing::XDrawPage > xSlide = xDrawPages->insertNewByIndex( xDrawPages->getCount() ); + SdPage* pSlide = m_pDoc->GetSdPage( m_pDoc->GetSdPageCount(PageKind::Standard)-1, PageKind::Standard); + pSlide->SetAutoLayout(aLayout, true); // Set the layout here + return xSlide; +} + +awt::Size SdPhotoAlbumDialog::createASRSize(const awt::Size& aPicSize, const awt::Size& aMaxSize) +{ + double resizeWidth = aPicSize.Width; + double resizeHeight = aPicSize.Height; + double aspect = resizeWidth/resizeHeight; + + if( resizeWidth > aMaxSize.Width ) + { + resizeWidth = aMaxSize.Width; + resizeHeight = resizeWidth / aspect; + } + + if( resizeHeight > aMaxSize.Height ) + { + aspect = resizeWidth/resizeHeight; + resizeHeight = aMaxSize.Height; + resizeWidth = resizeHeight * aspect; + } + return awt::Size(resizeWidth, resizeHeight); +} + +awt::Size SdPhotoAlbumDialog::createASRSizeCrop(const awt::Size& aPicSize, const awt::Size& aMaxSize) +{ + double resizeWidth = aPicSize.Width; + double resizeHeight = aPicSize.Height; + double imgAspect = resizeWidth / resizeHeight; + double windowAspectRatio = static_cast<double>(aMaxSize.Width) / aMaxSize.Height ; + + + //When both sides of an image are bigger than canvas size, image would be downscaled. + if( resizeWidth > aMaxSize.Width && resizeHeight > aMaxSize.Height ) + { + if( imgAspect > windowAspectRatio ) + { + resizeHeight = aMaxSize.Height; + resizeWidth = aMaxSize.Height * imgAspect; + } + else + { + resizeHeight = aMaxSize.Width / imgAspect; + resizeWidth = aMaxSize.Width; + } + + } + //In all other cases image is upscaled + else + { + if( imgAspect > windowAspectRatio ) + { + resizeHeight = aMaxSize.Height; + resizeWidth = aMaxSize.Height * imgAspect; + } + else + { + resizeWidth = aMaxSize.Width; + resizeHeight = aMaxSize.Width / imgAspect; + } + } + return awt::Size(resizeWidth, resizeHeight); +} + +void SdPhotoAlbumDialog::createCaption(const awt::Size& aPageSize ) +{ + Point CapPos; + Size CapSize; + + CapSize.setWidth( aPageSize.Width ); + CapSize.setHeight( aPageSize.Height/6 ); + CapPos.setX( 0 ); + CapPos.setY( aPageSize.Height - CapSize.Height() ); + SdPage* pSlide = m_pDoc->GetSdPage( m_pDoc->GetSdPageCount(PageKind::Standard)-1, PageKind::Standard ); + + // try to get existing PresObj + const ::tools::Rectangle rRect(CapPos,CapSize); + SdrObject* pSdrObj = pSlide->GetPresObj(PresObjKind::Title); + + if(!pSdrObj) + { + // if not exists, create. Beware: It is already inserted to the SdPage + pSdrObj = pSlide->CreatePresObj(PresObjKind::Title,false,rRect); + } + else + { + // if exists, bring to front and position it + const size_t nObjNum(pSlide->GetObjCount()); + + if(nObjNum) + { + pSlide->SetObjectOrdNum(pSdrObj->GetOrdNum(), nObjNum - 1); + } + + pSdrObj->SetSnapRect(rRect); + } + + if(pSdrObj) + { + // set color, style and some transparency + SfxItemSet aSet(m_pDoc->GetItemPool() ); + + aSet.Put( XFillStyleItem(drawing::FillStyle_SOLID) ); + aSet.Put( XFillColorItem( "", COL_BLACK ) ); + aSet.Put( XFillTransparenceItem( 20 ) ); + pSdrObj->SetMergedItemSetAndBroadcast(aSet); + } +} + +Reference< graphic::XGraphic> SdPhotoAlbumDialog::createXGraphicFromUrl(const OUString& sUrl, + const Reference< graphic::XGraphicProvider>& xProvider +) +{ + // The same as above, except this returns an XGraphic from the image URL + ::comphelper::NamedValueCollection aMediaProperties; + aMediaProperties.put( "URL", sUrl ); + Reference< graphic::XGraphic> xGraphic = + xProvider->queryGraphic( aMediaProperties.getPropertyValues() ); + return xGraphic; +} + +void SdPhotoAlbumDialog::EnableDisableButtons() +{ + m_xRemoveBtn->set_sensitive(m_xImagesLst->count_selected_rows() > 0); + m_xUpBtn->set_sensitive(m_xImagesLst->count_selected_rows() > 0 && + m_xImagesLst->get_selected_index() != 0); + m_xDownBtn->set_sensitive(m_xImagesLst->count_selected_rows() > 0 && + m_xImagesLst->get_selected_index() < m_xImagesLst->n_children() - 1); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/PhotoAlbumDialog.hxx b/sd/source/ui/dlg/PhotoAlbumDialog.hxx new file mode 100644 index 000000000..f84ff5cc3 --- /dev/null +++ b/sd/source/ui/dlg/PhotoAlbumDialog.hxx @@ -0,0 +1,91 @@ +/* -*- 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/. +*/ + +#pragma once + +#include <tools/link.hxx> + +#include <vcl/weld.hxx> +#include <svx/graphctl.hxx> +#include <xmloff/autolayout.hxx> + +#include <com/sun/star/awt/Size.hpp> + +namespace com::sun::star::drawing { class XDrawPage; } +namespace com::sun::star::drawing { class XDrawPages; } +namespace com::sun::star::graphic { class XGraphicProvider; } + +class SdDrawDocument; +class GraphicFilter; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd +{ + +class SdPhotoAlbumDialog : public weld::GenericDialogController +{ +public: + SdPhotoAlbumDialog(weld::Window* pWindow, SdDrawDocument* pActDoc); + virtual ~SdPhotoAlbumDialog() override; + +private: + SdDrawDocument* m_pDoc; + GraphicFilter* m_pGraphicFilter; + + GraphCtrl m_aImg; + + std::unique_ptr<weld::Button> m_xCancelBtn; + std::unique_ptr<weld::Button> m_xCreateBtn; + std::unique_ptr<weld::Button> m_xAddBtn; + std::unique_ptr<weld::Button> m_xUpBtn; + std::unique_ptr<weld::Button> m_xDownBtn; + std::unique_ptr<weld::Button> m_xRemoveBtn; + std::unique_ptr<weld::TreeView> m_xImagesLst; + std::unique_ptr<weld::CustomWeld> m_xImg; + std::unique_ptr<weld::ComboBox> m_xInsTypeCombo; + std::unique_ptr<weld::CheckButton> m_xASRCheck; + std::unique_ptr<weld::CheckButton> m_xASRCheckCrop; + std::unique_ptr<weld::CheckButton> m_xCapCheck; + std::unique_ptr<weld::CheckButton> m_xInsertAsLinkCheck; + + DECL_LINK(CancelHdl, weld::Button&, void); + DECL_LINK(CreateHdl, weld::Button&, void); + + DECL_LINK(FileHdl, weld::Button&, void); + DECL_LINK(UpHdl, weld::Button&, void); + DECL_LINK(DownHdl, weld::Button&, void); + DECL_LINK(RemoveHdl, weld::Button&, void); + + DECL_LINK(SelectHdl, weld::TreeView&, void); + DECL_LINK(TypeSelectHdl, weld::ComboBox&, void); + + Reference< drawing::XDrawPage > appendNewSlide(AutoLayout aLayout, + const Reference< drawing::XDrawPages >& xDrawPages); + + static awt::Size createASRSize(const awt::Size& aPicSize, const awt::Size& aMaxSize); + static awt::Size createASRSizeCrop(const awt::Size& aPicSize, const awt::Size& aMaxSize); + void createCaption(const awt::Size& aPageSize); + static Reference< graphic::XGraphic> createXGraphicFromUrl(const OUString& sUrl, + const Reference< graphic::XGraphicProvider>& xProvider); + + void EnableDisableButtons(); + + enum SlideImageLayout + { + ONE_IMAGE=0, + TWO_IMAGES, + FOUR_IMAGES + }; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/RemoteDialog.cxx b/sd/source/ui/dlg/RemoteDialog.cxx new file mode 100644 index 000000000..e28f57ecd --- /dev/null +++ b/sd/source/ui/dlg/RemoteDialog.cxx @@ -0,0 +1,49 @@ +/* -*- 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/. + */ + +#include "RemoteDialog.hxx" +#include <RemoteServer.hxx> + +using namespace ::sd; +using namespace ::std; + +RemoteDialog::RemoteDialog(weld::Window* pWindow) + : GenericDialogController(pWindow, "modules/simpress/ui/remotedialog.ui", "RemoteDialog") + , m_xButtonConnect(m_xBuilder->weld_button("ok")) + , m_xClientBox(new sd::ClientBox(m_xBuilder->weld_scrolled_window("scroll"), + m_xBuilder->weld_container("tree"))) +{ + m_xButtonConnect->connect_clicked(LINK(this, RemoteDialog, HandleConnectButton)); +} + +RemoteDialog::~RemoteDialog() {} + +IMPL_LINK_NOARG(RemoteDialog, HandleConnectButton, weld::Button&, void) +{ + weld::WaitObject(m_xDialog.get()); +#if defined(ENABLE_SDREMOTE) + auto xEntry = m_xClientBox->GetActiveEntry(); + if (!xEntry) + return; + OUString aPin = xEntry->m_xPinBox->get_text(); + if (RemoteServer::connectClient(xEntry->m_xClientInfo, aPin)) + m_xDialog->response(RET_OK); +#endif +} + +short RemoteDialog::run() +{ + short nRet = weld::GenericDialogController::run(); +#ifdef ENABLE_SDREMOTE + RemoteServer::restoreDiscoverable(); +#endif + return nRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/RemoteDialog.hxx b/sd/source/ui/dlg/RemoteDialog.hxx new file mode 100644 index 000000000..6c3f94ff8 --- /dev/null +++ b/sd/source/ui/dlg/RemoteDialog.hxx @@ -0,0 +1,32 @@ +/* -*- 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/. + */ +#pragma once + +#include <vcl/weld.hxx> + +#include "RemoteDialogClientBox.hxx" + +namespace sd +{ +class RemoteDialog : public weld::GenericDialogController +{ +private: + std::unique_ptr<weld::Button> m_xButtonConnect; + std::unique_ptr<ClientBox> m_xClientBox; + + DECL_LINK(HandleConnectButton, weld::Button&, void); + +public: + explicit RemoteDialog(weld::Window* pWindow); + virtual short run() override; + virtual ~RemoteDialog() override; +}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/RemoteDialogClientBox.cxx b/sd/source/ui/dlg/RemoteDialogClientBox.cxx new file mode 100644 index 000000000..2371c4fb8 --- /dev/null +++ b/sd/source/ui/dlg/RemoteDialogClientBox.cxx @@ -0,0 +1,134 @@ +/* -*- 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 <vector> + +#include "RemoteDialogClientBox.hxx" +#include <RemoteServer.hxx> + +#include <vcl/svapp.hxx> + +namespace sd { + +// struct ClientBoxEntry + +ClientBoxEntry::ClientBoxEntry(ClientBox* pClientBox, + const std::shared_ptr<ClientInfo>& pClientInfo) + : m_xBuilder(Application::CreateBuilder(pClientBox->GetContainer(), "modules/simpress/ui/clientboxfragment.ui")) + , m_xContainer(m_xBuilder->weld_container("ClientboxFragment")) + , m_xDeviceName(m_xBuilder->weld_label("name")) + , m_xPinLabel(m_xBuilder->weld_label("pinlabel")) + , m_xPinBox(m_xBuilder->weld_entry("pin")) + , m_xDeauthoriseButton(m_xBuilder->weld_button("button")) + , m_xClientInfo(pClientInfo) + , m_pClientBox(pClientBox) +{ + m_xDeviceName->set_label(m_xClientInfo->mName); + m_xDeauthoriseButton->connect_clicked(LINK(this, ClientBoxEntry, DeauthoriseHdl)); + m_xDeauthoriseButton->set_visible(m_xClientInfo->mbIsAlreadyAuthorised); + m_xPinBox->set_visible(!m_xClientInfo->mbIsAlreadyAuthorised); + m_xPinLabel->set_visible(!m_xClientInfo->mbIsAlreadyAuthorised); + + m_xDeauthoriseButton->connect_focus_in(LINK(this, ClientBoxEntry, FocusHdl)); + m_xPinBox->connect_focus_in(LINK(this, ClientBoxEntry, FocusHdl)); +} + +ClientBoxEntry::~ClientBoxEntry() +{ + m_pClientBox->GetContainer()->move(m_xContainer.get(), nullptr); +} + +// ClientBox +ClientBox::ClientBox(std::unique_ptr<weld::ScrolledWindow> xScroll, + std::unique_ptr<weld::Container> xContents) + : m_xScroll(std::move(xScroll)) + , m_xContents(std::move(xContents)) + , m_pActive(nullptr) +{ + Size aSize(m_xScroll->get_approximate_digit_width() * 40, + m_xScroll->get_text_height() * 16); + m_xScroll->set_size_request(aSize.Width(), aSize.Height()); + + m_xContents->set_stack_background(); + + populateEntries(); +} + +ClientBox::~ClientBox() +{ +} + +ClientBoxEntry* ClientBox::GetActiveEntry() +{ + return m_pActive; +} + +void ClientBox::addEntry( const std::shared_ptr<ClientInfo>& pClientInfo ) +{ + TClientBoxEntry xEntry = std::make_shared<ClientBoxEntry>(this, pClientInfo); + m_vEntries.push_back(xEntry); +} + +void ClientBox::setActive(ClientBoxEntry* pClientEntry) +{ + m_pActive = pClientEntry; +} + +void ClientBox::clearEntries() +{ + m_vEntries.clear(); + m_pActive = nullptr; +} + +void ClientBox::populateEntries() +{ + clearEntries(); + +#ifdef ENABLE_SDREMOTE + RemoteServer::ensureDiscoverable(); + + std::vector< std::shared_ptr< ClientInfo > > aClients( RemoteServer::getClients() ); + + for ( const auto& rxClient : aClients ) + { + addEntry( rxClient ); + } +#endif + +} + +IMPL_LINK_NOARG(ClientBoxEntry, DeauthoriseHdl, weld::Button&, void) +{ +#ifdef ENABLE_SDREMOTE + RemoteServer::deauthoriseClient(m_xClientInfo); +#endif + m_pClientBox->populateEntries(); +} + +IMPL_LINK_NOARG(ClientBoxEntry, FocusHdl, weld::Widget&, void) +{ + if (ClientBoxEntry* pOldEntry = m_pClientBox->GetActiveEntry()) + pOldEntry->m_xContainer->set_stack_background(); + m_pClientBox->setActive(this); + m_xContainer->set_highlight_background(); +} + +} //namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/RemoteDialogClientBox.hxx b/sd/source/ui/dlg/RemoteDialogClientBox.hxx new file mode 100644 index 000000000..c7a916dc9 --- /dev/null +++ b/sd/source/ui/dlg/RemoteDialogClientBox.hxx @@ -0,0 +1,85 @@ +/* -*- 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 . + */ + +#pragma once + +#include <vcl/weld.hxx> + +#include <memory> + +namespace sd { + +#define SMALL_ICON_SIZE 16 +#define TOP_OFFSET 5 +#define ICON_HEIGHT 42 +#define ICON_WIDTH 47 +#define ICON_OFFSET 72 +#define RIGHT_ICON_OFFSET 5 +#define SPACE_BETWEEN 3 + +class ClientBox; +struct ClientBoxEntry; +struct ClientInfo; + +typedef std::shared_ptr<ClientBoxEntry> TClientBoxEntry; + +struct ClientBoxEntry +{ + std::unique_ptr<weld::Builder> m_xBuilder; + std::unique_ptr<weld::Container> m_xContainer; + std::unique_ptr<weld::Label> m_xDeviceName; + std::unique_ptr<weld::Label> m_xPinLabel; + std::unique_ptr<weld::Entry> m_xPinBox; + std::unique_ptr<weld::Button> m_xDeauthoriseButton; + + std::shared_ptr<ClientInfo> m_xClientInfo; + ClientBox* m_pClientBox; + + DECL_LINK(DeauthoriseHdl, weld::Button&, void); + DECL_LINK(FocusHdl, weld::Widget&, void); + + ClientBoxEntry(ClientBox* pClientBox, const std::shared_ptr<ClientInfo>& pClientInfo); + ~ClientBoxEntry(); +}; + +class ClientBox +{ + std::unique_ptr<weld::ScrolledWindow> m_xScroll; + std::unique_ptr<weld::Container> m_xContents; + + std::vector< TClientBoxEntry > m_vEntries; + ClientBoxEntry* m_pActive; + +public: + ClientBox(std::unique_ptr<weld::ScrolledWindow> xScroll, std::unique_ptr<weld::Container> xContents); + weld::Container* GetContainer() { return m_xContents.get(); } + ~ClientBox(); + + ClientBoxEntry* GetActiveEntry(); + + void addEntry(const std::shared_ptr<ClientInfo>& pClientInfo); + void setActive(ClientBoxEntry* pClientData); + void clearEntries(); + + void populateEntries(); +}; + +} // end namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/SpellDialogChildWindow.cxx b/sd/source/ui/dlg/SpellDialogChildWindow.cxx new file mode 100644 index 000000000..c87919346 --- /dev/null +++ b/sd/source/ui/dlg/SpellDialogChildWindow.cxx @@ -0,0 +1,172 @@ +/* -*- 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 <SpellDialogChildWindow.hxx> +#include <svx/svxids.hrc> + +#include <ViewShell.hxx> +#include <ViewShellBase.hxx> +#include <DrawViewShell.hxx> +#include <OutlineViewShell.hxx> +#include <Outliner.hxx> +#include <drawdoc.hxx> + +namespace sd { + +SFX_IMPL_CHILDWINDOW_WITHID(SpellDialogChildWindow, SID_SPELL_DIALOG) + +SpellDialogChildWindow::SpellDialogChildWindow ( + vcl::Window* _pParent, + sal_uInt16 nId, + SfxBindings* pBindings, + SAL_UNUSED_PARAMETER SfxChildWinInfo* /*pInfo*/) + : svx::SpellDialogChildWindow (_pParent, nId, pBindings), + mpSdOutliner (nullptr), + mbOwnOutliner (false) +{ + ProvideOutliner(); +} + +SpellDialogChildWindow::~SpellDialogChildWindow() +{ + EndSpellingAndClearOutliner(); +} + +SfxChildWinInfo SpellDialogChildWindow::GetInfo() const +{ + return svx::SpellDialogChildWindow::GetInfo(); +} + +void SpellDialogChildWindow::InvalidateSpellDialog() +{ + svx::SpellDialogChildWindow::InvalidateSpellDialog(); +} + +svx::SpellPortions SpellDialogChildWindow::GetNextWrongSentence( bool /*bRecheck*/ ) +{ + svx::SpellPortions aResult; + + if (mpSdOutliner != nullptr) + { + ProvideOutliner(); + aResult = mpSdOutliner->GetNextSpellSentence(); + } + return aResult; +} + +void SpellDialogChildWindow::ApplyChangedSentence ( + const svx::SpellPortions& rChanged, bool bRecheck ) +{ + if (mpSdOutliner != nullptr) + { + OutlinerView* pOutlinerView = mpSdOutliner->GetView(0); + if (pOutlinerView != nullptr) + mpSdOutliner->ApplyChangedSentence ( + pOutlinerView->GetEditView(), + rChanged, bRecheck); + } +} + +void SpellDialogChildWindow::GetFocus() +{ + // In order to detect a cursor movement we could compare the + // currently selected text shape with the one that was selected + // when LoseFocus() was called the last time. + // For the time being we instead rely on the DetectChange() method + // in the SdOutliner class. +} + +void SpellDialogChildWindow::LoseFocus() +{ +} + +void SpellDialogChildWindow::EndSpellingAndClearOutliner() +{ + if (!mpSdOutliner) + return; + EndListening(*mpSdOutliner->GetDoc()); + mpSdOutliner->EndSpelling(); + if (mbOwnOutliner) + delete mpSdOutliner; + mpSdOutliner = nullptr; + mbOwnOutliner = false; +} + +void SpellDialogChildWindow::Notify(SfxBroadcaster&, const SfxHint& rHint) +{ + if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint) + return; + const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint); + if (SdrHintKind::ModelCleared == pSdrHint->GetKind()) + { + EndSpellingAndClearOutliner(); + } +} + +void SpellDialogChildWindow::ProvideOutliner() +{ + ViewShellBase* pViewShellBase = dynamic_cast<ViewShellBase*>( SfxViewShell::Current() ); + + if (pViewShellBase == nullptr) + return; + + ViewShell* pViewShell = pViewShellBase->GetMainViewShell().get(); + // If there already exists an outliner that has been created + // for another view shell then destroy it first. + if (mpSdOutliner != nullptr) + if(( dynamic_cast< const DrawViewShell *>( pViewShell ) != nullptr && ! mbOwnOutliner) + || (dynamic_cast< const OutlineViewShell *>( pViewShell ) != nullptr && mbOwnOutliner)) + { + EndSpellingAndClearOutliner(); + } + + // Now create/get an outliner if none is present. + if (mpSdOutliner != nullptr) + return; + + if( dynamic_cast< const DrawViewShell *>( pViewShell ) != nullptr) + { + // We need an outliner for the spell check so we have + // to create one. + mbOwnOutliner = true; + SdDrawDocument *pDoc = pViewShell->GetDoc(); + mpSdOutliner = new SdOutliner(pDoc, OutlinerMode::TextObject); + StartListening(*pDoc); + } + else if( dynamic_cast< const OutlineViewShell *>( pViewShell ) != nullptr) + { + // An outline view is already visible. The SdOutliner + // will use it instead of creating its own. + mbOwnOutliner = false; + SdDrawDocument *pDoc = pViewShell->GetDoc(); + mpSdOutliner = pDoc->GetOutliner(); + StartListening(*pDoc); + } + + // Initialize spelling. + if (mpSdOutliner != nullptr) + { + mpSdOutliner->PrepareSpelling(); + mpSdOutliner->StartSpelling(); + } +} + +} // end of namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/TemplateScanner.cxx b/sd/source/ui/dlg/TemplateScanner.cxx new file mode 100644 index 000000000..afd23ff85 --- /dev/null +++ b/sd/source/ui/dlg/TemplateScanner.cxx @@ -0,0 +1,342 @@ +/* -*- 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 <TemplateScanner.hxx> + +#include <comphelper/processfactory.hxx> +#include <comphelper/documentconstants.hxx> + +#include <sfx2/doctempl.hxx> +#include <com/sun/star/frame/DocumentTemplates.hpp> +#include <com/sun/star/frame/XDocumentTemplates.hpp> +#include <com/sun/star/ucb/XContentAccess.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/sdbc/XRow.hpp> + +#include <set> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace { + +constexpr OUStringLiteral TITLE = u"Title"; + +class FolderDescriptor +{ +public: + FolderDescriptor ( + int nPriority, + const OUString& rsContentIdentifier, + const Reference<css::ucb::XCommandEnvironment>& rxFolderEnvironment) + : mnPriority(nPriority), + msContentIdentifier(rsContentIdentifier), + mxFolderEnvironment(rxFolderEnvironment) + { } + int mnPriority; + OUString msContentIdentifier; + // Reference<sdbc::XResultSet> mxFolderResultSet; + Reference<css::ucb::XCommandEnvironment> mxFolderEnvironment; + + class Comparator + { + public: + bool operator() (const FolderDescriptor& r1, const FolderDescriptor& r2) const + { return r1.mnPriority < r2.mnPriority; } + }; +}; + +/** Use a heuristic based on the URL of a top-level template folder to + assign a priority that is used to sort the folders. +*/ +int Classify (std::u16string_view rsURL) +{ + int nPriority (0); + + if (rsURL.empty()) + nPriority = 100; + else if (rsURL.find(u"presnt") != std::u16string_view::npos) + { + nPriority = 30; + } + else if (rsURL.find(u"layout") != std::u16string_view::npos) + { + nPriority = 20; + } + else if (rsURL.find(u"educate") != std::u16string_view::npos) + { + nPriority = 40; + } + else if (rsURL.find(u"finance") != std::u16string_view::npos) + { + nPriority = 40; + } + else + { + // All other folders are taken for user supplied and have the + // highest priority. + nPriority = 10; + } + + return nPriority; +} + +} // end of anonymous namespace + +namespace sd +{ + +class TemplateScanner::FolderDescriptorList + : public ::std::multiset<FolderDescriptor,FolderDescriptor::Comparator> +{ +}; + +TemplateScanner::TemplateScanner() + : meState(INITIALIZE_SCANNING), + mpFolderDescriptors(new FolderDescriptorList) +{ + // empty; +} + +TemplateScanner::~TemplateScanner() +{ +} + +TemplateScanner::State TemplateScanner::GetTemplateRoot() +{ + Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + Reference<frame::XDocumentTemplates> xTemplates = frame::DocumentTemplates::create(xContext); + mxTemplateRoot = xTemplates->getContent(); + + return INITIALIZE_FOLDER_SCANNING; +} + +TemplateScanner::State TemplateScanner::InitializeEntryScanning() +{ + State eNextState (SCAN_ENTRY); + + if (maFolderContent.isFolder()) + { + mxEntryEnvironment.clear(); + + // Create a cursor to iterate over the templates in this folders. + // We are interested only in three properties: the entry's name, + // its URL, and its content type. + mxEntryResultSet.set( maFolderContent.createCursor({ TITLE, "TargetURL", "TypeDescription" }, ::ucbhelper::INCLUDE_DOCUMENTS_ONLY)); + } + else + eNextState = ERROR; + + return eNextState; +} + +TemplateScanner::State TemplateScanner::ScanEntry() +{ + State eNextState (ERROR); + + Reference<css::ucb::XContentAccess> xContentAccess (mxEntryResultSet, UNO_QUERY); + Reference<css::sdbc::XRow> xRow (mxEntryResultSet, UNO_QUERY); + + if (xContentAccess.is() && xRow.is() && mxEntryResultSet.is()) + { + if (mxEntryResultSet->next()) + { + OUString sTitle (xRow->getString (1)); + OUString sTargetURL (xRow->getString (2)); + OUString sContentType (xRow->getString (3)); + + OUString aId = xContentAccess->queryContentIdentifierString(); + ::ucbhelper::Content aContent(aId, mxEntryEnvironment, comphelper::getProcessComponentContext()); + if (aContent.isDocument ()) + { + // Check whether the entry is an impress template. If so + // add a new entry to the resulting list (which is created + // first if necessary). + // These strings are used to find impress templates in the tree of + // template files. Should probably be determined dynamically. + if ( (sContentType == MIMETYPE_OASIS_OPENDOCUMENT_PRESENTATION_TEMPLATE_ASCII) + || (sContentType == MIMETYPE_OASIS_OPENDOCUMENT_PRESENTATION_ASCII) + || (sContentType == "application/vnd.stardivision.impress") + || (sContentType == MIMETYPE_VND_SUN_XML_IMPRESS_ASCII) + // The following id comes from the bugdoc in #i2764#. + || (sContentType == "Impress 2.0")) + { + OUString sLocalisedTitle = SfxDocumentTemplates::ConvertResourceString(sTitle); + mpTemplateEntries.push_back(std::make_unique<TemplateEntry>(sLocalisedTitle, sTargetURL)); + } + } + + // Continue scanning entries. + eNextState = SCAN_ENTRY; + } + else + { + // Continue with scanning the next folder. + eNextState = SCAN_FOLDER; + } + } + + return eNextState; +} + +TemplateScanner::State TemplateScanner::InitializeFolderScanning() +{ + State eNextState (ERROR); + + mxFolderResultSet.clear(); + + try + { + // Create content for template folders. + mxFolderEnvironment.clear(); + ::ucbhelper::Content aTemplateDir (mxTemplateRoot, mxFolderEnvironment, comphelper::getProcessComponentContext()); + + // Create a cursor to iterate over the template folders. + mxFolderResultSet.set( aTemplateDir.createCursor({ TITLE, "TargetDirURL" }, ::ucbhelper::INCLUDE_FOLDERS_ONLY)); + if (mxFolderResultSet.is()) + eNextState = GATHER_FOLDER_LIST; + } + catch (css::uno::Exception&) + { + eNextState = ERROR; + } + + return eNextState; +} + +TemplateScanner::State TemplateScanner::GatherFolderList() +{ + State eNextState (ERROR); + + Reference<css::ucb::XContentAccess> xContentAccess (mxFolderResultSet, UNO_QUERY); + if (xContentAccess.is() && mxFolderResultSet.is()) + { + while (mxFolderResultSet->next()) + { + Reference<sdbc::XRow> xRow (mxFolderResultSet, UNO_QUERY); + if (xRow.is()) + { + OUString sTargetDir (xRow->getString (2)); + OUString aId = xContentAccess->queryContentIdentifierString(); + + mpFolderDescriptors->insert( + FolderDescriptor( + Classify(sTargetDir), + aId, + mxFolderEnvironment)); + } + } + + eNextState = SCAN_FOLDER; + } + + return eNextState; +} + +TemplateScanner::State TemplateScanner::ScanFolder() +{ + State eNextState (ERROR); + + if (!mpFolderDescriptors->empty()) + { + FolderDescriptor aDescriptor (*mpFolderDescriptors->begin()); + mpFolderDescriptors->erase(mpFolderDescriptors->begin()); + + OUString aId (aDescriptor.msContentIdentifier); + + maFolderContent = ::ucbhelper::Content (aId, aDescriptor.mxFolderEnvironment, comphelper::getProcessComponentContext()); + if (maFolderContent.isFolder()) + { + // Scan the folder and insert it into the list of template + // folders. + // Continue with scanning all entries in the folder. + mpTemplateEntries.clear(); + eNextState = INITIALIZE_ENTRY_SCAN; + } + } + else + { + eNextState = DONE; + } + + return eNextState; +} + +void TemplateScanner::RunNextStep() +{ + switch (meState) + { + case INITIALIZE_SCANNING: + meState = GetTemplateRoot(); + break; + + case INITIALIZE_FOLDER_SCANNING: + meState = InitializeFolderScanning(); + break; + + case SCAN_FOLDER: + meState = ScanFolder(); + break; + + case GATHER_FOLDER_LIST: + meState = GatherFolderList(); + break; + + case INITIALIZE_ENTRY_SCAN: + meState = InitializeEntryScanning(); + break; + + case SCAN_ENTRY: + meState = ScanEntry(); + break; + default: + break; + } + + switch (meState) + { + case DONE: + case ERROR: + mxTemplateRoot.clear(); + mxFolderEnvironment.clear(); + mxEntryEnvironment.clear(); + mxFolderResultSet.clear(); + mxEntryResultSet.clear(); + break; + default: + break; + } +} + +bool TemplateScanner::HasNextStep() +{ + switch (meState) + { + case DONE: + case ERROR: + return false; + + default: + return true; + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/animobjs.cxx b/sd/source/ui/dlg/animobjs.cxx new file mode 100644 index 000000000..b70848e23 --- /dev/null +++ b/sd/source/ui/dlg/animobjs.cxx @@ -0,0 +1,1134 @@ +/* -*- 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 <time.h> +#include <svl/eitem.hxx> +#include <svl/intitem.hxx> +#include <svx/svdograf.hxx> +#include <svx/svdogrp.hxx> +#include <svx/svdpagv.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/progress.hxx> +#include <vcl/help.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <vcl/virdev.hxx> + +#include <anminfo.hxx> +#include <animobjs.hxx> +#include <app.hrc> +#include <strings.hrc> +#include <sdresid.hxx> +#include <View.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> + +#include <ViewShell.hxx> + +#include <vcl/settings.hxx> + +#include <EffectMigration.hxx> + +#include <algorithm> + +using namespace ::com::sun::star; + +namespace sd { + +/** + * SdDisplay - Control + */ +SdDisplay::SdDisplay() + : aScale(1, 1) +{ +} + +SdDisplay::~SdDisplay() +{ +} + +void SdDisplay::SetBitmapEx( BitmapEx const * pBmpEx ) +{ + if( pBmpEx ) + { + aBitmapEx = *pBmpEx; + } + else + { + const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings(); + const Color aFillColor = rStyles.GetFieldColor(); + aBitmapEx.Erase(aFillColor); + } +} + +void SdDisplay::Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle&) +{ + rRenderContext.Push(vcl::PushFlags::MAPMODE); + + rRenderContext.SetMapMode(MapMode(MapUnit::MapPixel)); + const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings(); + rRenderContext.SetBackground( Wallpaper( rStyles.GetFieldColor() ) ); + rRenderContext.Erase(); + + Point aPt; + Size aSize = GetOutputSizePixel(); + + Size aBmpSize = aBitmapEx.GetBitmap().GetSizePixel(); + aBmpSize.setWidth( static_cast<::tools::Long>( static_cast<double>(aBmpSize.Width()) * static_cast<double>(aScale) ) ); + aBmpSize.setHeight( static_cast<::tools::Long>( static_cast<double>(aBmpSize.Height()) * static_cast<double>(aScale) ) ); + + if( aBmpSize.Width() < aSize.Width() ) + aPt.setX( ( aSize.Width() - aBmpSize.Width() ) / 2 ); + if( aBmpSize.Height() < aSize.Height() ) + aPt.setY( ( aSize.Height() - aBmpSize.Height() ) / 2 ); + + aBitmapEx.Draw(&rRenderContext, aPt, aBmpSize); + + rRenderContext.Pop(); +} + +void SdDisplay::SetScale( const Fraction& rFrac ) +{ + aScale = rFrac; +} + +void SdDisplay::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + CustomWidgetController::SetDrawingArea(pDrawingArea); + Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(147, 87), MapMode(MapUnit::MapAppFont))); + pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); + SetOutputSizePixel(aSize); +} + +const size_t AnimationWindow::EMPTY_FRAMELIST = std::numeric_limits<size_t>::max(); + +/** + * AnimationWindow - FloatingWindow + */ +AnimationWindow::AnimationWindow(SfxBindings* pInBindings, SfxChildWindow *pCW, vcl::Window* pParent) + : SfxDockingWindow(pInBindings, pCW, pParent, + "DockingAnimation", "modules/simpress/ui/dockinganimation.ui") + , m_xCtlDisplay(new SdDisplay) + , m_xCtlDisplayWin(new weld::CustomWeld(*m_xBuilder, "preview", *m_xCtlDisplay)) + , m_xBtnFirst(m_xBuilder->weld_button("first")) + , m_xBtnReverse(m_xBuilder->weld_button("prev")) + , m_xBtnStop(m_xBuilder->weld_button("stop")) + , m_xBtnPlay(m_xBuilder->weld_button("next")) + , m_xBtnLast(m_xBuilder->weld_button("last")) + , m_xNumFldBitmap(m_xBuilder->weld_spin_button("numbitmap")) + , m_xTimeField(m_xBuilder->weld_formatted_spin_button("duration")) + , m_xFormatter(new weld::TimeFormatter(*m_xTimeField)) + , m_xLbLoopCount(m_xBuilder->weld_combo_box("loopcount")) + , m_xBtnGetOneObject(m_xBuilder->weld_button("getone")) + , m_xBtnGetAllObjects(m_xBuilder->weld_button("getall")) + , m_xBtnRemoveBitmap(m_xBuilder->weld_button("delone")) + , m_xBtnRemoveAll(m_xBuilder->weld_button("delall")) + , m_xFiCount(m_xBuilder->weld_label("count")) + , m_xRbtGroup(m_xBuilder->weld_radio_button("group")) + , m_xRbtBitmap(m_xBuilder->weld_radio_button("bitmap")) + , m_xFtAdjustment(m_xBuilder->weld_label("alignmentft")) + , m_xLbAdjustment(m_xBuilder->weld_combo_box("alignment")) + , m_xBtnCreateGroup(m_xBuilder->weld_button("create")) + , m_xBtnHelp(m_xBuilder->weld_button("help")) + , m_nCurrentFrame(EMPTY_FRAMELIST) + , bMovie(false) + , bAllObjects(false) +{ + SetText(SdResId(STR_ANIMATION_DIALOG_TITLE)); + + m_xFormatter->SetDuration(true); + m_xFormatter->SetTimeFormat(TimeFieldFormat::F_SEC_CS); + m_xFormatter->EnableEmptyField(false); + + // create new document with page + pMyDoc.reset( new SdDrawDocument(DocumentType::Impress, nullptr) ); + rtl::Reference<SdPage> pPage = pMyDoc->AllocSdPage(false); + pMyDoc->InsertPage(pPage.get()); + + pControllerItem.reset( new AnimationControllerItem( SID_ANIMATOR_STATE, this, pInBindings ) ); + + m_xBtnFirst->connect_clicked( LINK( this, AnimationWindow, ClickFirstHdl ) ); + m_xBtnReverse->connect_clicked( LINK( this, AnimationWindow, ClickPlayHdl ) ); + m_xBtnStop->connect_clicked( LINK( this, AnimationWindow, ClickStopHdl ) ); + m_xBtnPlay->connect_clicked( LINK( this, AnimationWindow, ClickPlayHdl ) ); + m_xBtnLast->connect_clicked( LINK( this, AnimationWindow, ClickLastHdl ) ); + + m_xBtnGetOneObject->connect_clicked( LINK( this, AnimationWindow, ClickGetObjectHdl ) ); + m_xBtnGetAllObjects->connect_clicked( LINK( this, AnimationWindow, ClickGetObjectHdl ) ); + m_xBtnRemoveBitmap->connect_clicked( LINK( this, AnimationWindow, ClickRemoveBitmapHdl ) ); + m_xBtnRemoveAll->connect_clicked( LINK( this, AnimationWindow, ClickRemoveBitmapHdl ) ); + + m_xRbtGroup->connect_toggled( LINK( this, AnimationWindow, ClickRbtHdl ) ); + m_xRbtBitmap->connect_toggled( LINK( this, AnimationWindow, ClickRbtHdl ) ); + m_xBtnCreateGroup->connect_clicked( LINK( this, AnimationWindow, ClickCreateGroupHdl ) ); + m_xBtnHelp->connect_clicked( LINK( this, AnimationWindow, ClickHelpHdl ) ); + m_xNumFldBitmap->connect_value_changed( LINK( this, AnimationWindow, ModifyBitmapHdl ) ); + m_xTimeField->connect_value_changed( LINK( this, AnimationWindow, ModifyTimeHdl ) ); + + SetMinOutputSizePixel(GetOptimalSize()); + + ResetAttrs(); + + // the animator is empty; no animation group can be created + m_xBtnCreateGroup->set_sensitive(false); +} + +AnimationWindow::~AnimationWindow() +{ + disposeOnce(); +} + +void AnimationWindow::dispose() +{ + pControllerItem.reset(); + + m_FrameList.clear(); + m_nCurrentFrame = EMPTY_FRAMELIST; + + // delete the clones + pMyDoc.reset(); + + m_xCtlDisplayWin.reset(); + m_xCtlDisplay.reset(); + m_xBtnFirst.reset(); + m_xBtnReverse.reset(); + m_xBtnStop.reset(); + m_xBtnPlay.reset(); + m_xBtnLast.reset(); + m_xNumFldBitmap.reset(); + m_xFormatter.reset(); + m_xTimeField.reset(); + m_xLbLoopCount.reset(); + m_xBtnGetOneObject.reset(); + m_xBtnGetAllObjects.reset(); + m_xBtnRemoveBitmap.reset(); + m_xBtnRemoveAll.reset(); + m_xFiCount.reset(); + m_xRbtGroup.reset(); + m_xRbtBitmap.reset(); + m_xFtAdjustment.reset(); + m_xLbAdjustment.reset(); + m_xBtnCreateGroup.reset(); + m_xBtnHelp.reset(); + SfxDockingWindow::dispose(); +} + +IMPL_LINK_NOARG(AnimationWindow, ClickFirstHdl, weld::Button&, void) +{ + m_nCurrentFrame = (m_FrameList.empty()) ? EMPTY_FRAMELIST : 0; + UpdateControl(); +} + +IMPL_LINK_NOARG(AnimationWindow, ClickStopHdl, weld::Button&, void) +{ + bMovie = false; +} + +IMPL_LINK( AnimationWindow, ClickPlayHdl, weld::Button&, rButton, void ) +{ + ScopeLockGuard aGuard( maPlayLock ); + + bMovie = true; + bool bDisableCtrls = false; + size_t const nCount = m_FrameList.size(); + bool bReverse = &rButton == m_xBtnReverse.get(); + + // it is difficult to find it later on + bool bRbtGroupEnabled = m_xRbtGroup->get_sensitive(); + bool bBtnGetAllObjectsEnabled = m_xBtnGetAllObjects->get_sensitive(); + bool bBtnGetOneObjectEnabled = m_xBtnGetOneObject->get_sensitive(); + + // calculate overall time + ::tools::Time aTime( 0 ); + ::tools::Long nFullTime; + if( m_xRbtBitmap->get_active() ) + { + for (size_t i = 0; i < nCount; ++i) + { + aTime += m_FrameList[i].second; + } + nFullTime = aTime.GetMSFromTime(); + } + else + { + nFullTime = nCount * 100; + aTime.MakeTimeFromMS( nFullTime ); + } + + // StatusBarManager from 1 second + std::unique_ptr<SfxProgress> pProgress; + if( nFullTime >= 1000 ) + { + bDisableCtrls = true; + m_xBtnStop->set_sensitive(true); + pProgress.reset(new SfxProgress( nullptr, "Animator:", nFullTime )); // "Animator:" here we should think about something smart + } + + sal_uLong nTmpTime = 0; + size_t i = 0; + bool bCount = i < nCount; + if( bReverse ) + { + i = nCount - 1; + } + while( bCount && bMovie ) + { + // make list and view consistent + assert(i < m_FrameList.size()); + m_nCurrentFrame = i; + + UpdateControl(bDisableCtrls); + + if( m_xRbtBitmap->get_active() ) + { + ::tools::Time const & rTime = m_FrameList[i].second; + + m_xFormatter->SetTime( rTime ); + sal_uLong nTime = rTime.GetMSFromTime(); + + WaitInEffect( nTime, nTmpTime, pProgress.get() ); + nTmpTime += nTime; + } + else + { + WaitInEffect( 100, nTmpTime, pProgress.get() ); + nTmpTime += 100; + } + if( bReverse ) + { + if (i == 0) + { + // Terminate loop. + bCount = false; + } + else + { + --i; + } + } + else + { + i++; + if (i >= nCount) + { + // Terminate loop. + bCount = false; + // Move i back into valid range. + i = nCount - 1; + } + } + } + + // to re-enable the controls + bMovie = false; + if (nCount > 0) + { + assert(i == m_nCurrentFrame); + UpdateControl(); + } + + if( pProgress ) + { + pProgress.reset(); + m_xBtnStop->set_sensitive(false); + } + + m_xRbtGroup->set_sensitive( bRbtGroupEnabled ); + m_xBtnGetAllObjects->set_sensitive( bBtnGetAllObjectsEnabled ); + m_xBtnGetOneObject->set_sensitive( bBtnGetOneObjectEnabled ); +} + +IMPL_LINK_NOARG(AnimationWindow, ClickLastHdl, weld::Button&, void) +{ + m_nCurrentFrame = + (m_FrameList.empty()) ? EMPTY_FRAMELIST : m_FrameList.size() - 1 ; + UpdateControl(); +} + +IMPL_LINK_NOARG(AnimationWindow, ClickRbtHdl, weld::Toggleable&, void) +{ + if (m_FrameList.empty() || m_xRbtGroup->get_active()) + { + m_xTimeField->set_text( OUString() ); + m_xTimeField->set_sensitive( false ); + m_xLbLoopCount->set_sensitive( false ); + } + else if (m_xRbtBitmap->get_active()) + { + sal_uLong n = m_xNumFldBitmap->get_value(); + if( n > 0 ) + { + ::tools::Time const & rTime = m_FrameList[n - 1].second; + m_xFormatter->SetTime( rTime ); + m_xFormatter->ReFormat(); + } + m_xTimeField->set_sensitive(true); + m_xLbLoopCount->set_sensitive(true); + } +} + +IMPL_LINK(AnimationWindow, ClickHelpHdl, weld::Button&, rButton, void) +{ + if (Help* pHelp = Application::GetHelp()) + pHelp->Start(OUString::fromUtf8(m_xContainer->get_help_id()), &rButton); +} + +IMPL_LINK( AnimationWindow, ClickGetObjectHdl, weld::Button&, rBtn, void ) +{ + bAllObjects = &rBtn == m_xBtnGetAllObjects.get(); + + // Code now in AddObj() + SfxBoolItem aItem( SID_ANIMATOR_ADD, true ); + + GetBindings().GetDispatcher()->ExecuteList( + SID_ANIMATOR_ADD, SfxCallMode::SLOT | SfxCallMode::RECORD, { &aItem }); +} + +IMPL_LINK( AnimationWindow, ClickRemoveBitmapHdl, weld::Button&, rBtn, void ) +{ + SdPage* pPage = pMyDoc->GetSdPage(0, PageKind::Standard); + SdrObject* pObject; + + // tdf#95298 check m_nCurrentFrame for EMPTY_FRAMELIST to avoid out-of-bound array access + if (&rBtn == m_xBtnRemoveBitmap.get() && EMPTY_FRAMELIST != m_nCurrentFrame) + { + m_FrameList.erase(m_FrameList.begin() + m_nCurrentFrame); + + pObject = pPage->GetObj(m_nCurrentFrame); + // Through acquisition of the AnimatedGIFs, objects does not need to + // exist. + if( pObject ) + { + pObject = pPage->RemoveObject(m_nCurrentFrame); + DBG_ASSERT(pObject, "Clone not found during deletion"); + SdrObject::Free( pObject ); + pPage->RecalcObjOrdNums(); + } + + if (m_nCurrentFrame >= m_FrameList.size()) + { + // tdf#95298 last frame was deleted, try to use the one before it or go on empty state + m_nCurrentFrame = m_FrameList.empty() ? EMPTY_FRAMELIST : m_FrameList.size() - 1; + } + } + else // delete everything + { + std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Warning, VclButtonsType::YesNo, + SdResId(STR_ASK_DELETE_ALL_PICTURES))); + short nReturn = xWarn->run(); + + if( nReturn == RET_YES ) + { + // clear frame list + for (size_t i = m_FrameList.size(); i > 0; ) + { + --i; + pObject = pPage->GetObj( i ); + if( pObject ) + { + pObject = pPage->RemoveObject( i ); + DBG_ASSERT(pObject, "Clone not found during deletion"); + SdrObject::Free( pObject ); + //pPage->RecalcObjOrdNums(); + } + } + m_FrameList.clear(); + m_nCurrentFrame = EMPTY_FRAMELIST; + } + } + + // can we create an animation group + if (m_FrameList.empty()) + { + m_xBtnCreateGroup->set_sensitive(false); + // if previous disabled by acquisition of AnimatedGIFs: + //m_xRbtBitmap->set_sensitive(true); + m_xRbtGroup->set_sensitive(true); + } + + // calculate and set zoom for DisplayWin + Fraction aFrac(GetScale()); + m_xCtlDisplay->SetScale(aFrac); + + UpdateControl(); +} + +IMPL_LINK_NOARG(AnimationWindow, ClickCreateGroupHdl, weld::Button&, void) +{ + // Code now in CreatePresObj() + SfxBoolItem aItem( SID_ANIMATOR_CREATE, true ); + + GetBindings().GetDispatcher()->ExecuteList(SID_ANIMATOR_CREATE, + SfxCallMode::SLOT | SfxCallMode::RECORD, { &aItem }); +} + +IMPL_LINK_NOARG(AnimationWindow, ModifyBitmapHdl, weld::SpinButton&, void) +{ + sal_uLong nBmp = m_xNumFldBitmap->get_value(); + + if (nBmp > m_FrameList.size()) + { + nBmp = m_FrameList.size(); + } + + m_nCurrentFrame = nBmp - 1; + + UpdateControl(); +} + +IMPL_LINK_NOARG(AnimationWindow, ModifyTimeHdl, weld::FormattedSpinButton&, void) +{ + sal_uLong nPos = m_xNumFldBitmap->get_value() - 1; + + ::tools::Time & rTime = m_FrameList[nPos].second; + + rTime = m_xFormatter->GetTime(); +} + +void AnimationWindow::UpdateControl(bool const bDisableCtrls) +{ + // tdf#95298 check m_nCurrentFrame for EMPTY_FRAMELIST to avoid out-of-bound array access + if (!m_FrameList.empty() && EMPTY_FRAMELIST != m_nCurrentFrame) + { + BitmapEx & rBmp(m_FrameList[m_nCurrentFrame].first); + + SdPage* pPage = pMyDoc->GetSdPage(0, PageKind::Standard); + SdrObject *const pObject = pPage->GetObj(m_nCurrentFrame); + if( pObject ) + { + ScopedVclPtrInstance< VirtualDevice > pVD; + ::tools::Rectangle aObjRect( pObject->GetCurrentBoundRect() ); + Size aObjSize( aObjRect.GetSize() ); + Point aOrigin( Point( -aObjRect.Left(), -aObjRect.Top() ) ); + MapMode aMap( pVD->GetMapMode() ); + aMap.SetMapUnit( MapUnit::Map100thMM ); + aMap.SetOrigin( aOrigin ); + pVD->SetMapMode( aMap ); + pVD->SetOutputSize( aObjSize ); + const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings(); + pVD->SetBackground( Wallpaper( rStyles.GetFieldColor() ) ); + pVD->SetDrawMode( rStyles.GetHighContrastMode() + ? sd::OUTPUT_DRAWMODE_CONTRAST + : sd::OUTPUT_DRAWMODE_COLOR ); + pVD->Erase(); + pObject->SingleObjectPainter( *pVD ); + rBmp = pVD->GetBitmapEx( aObjRect.TopLeft(), aObjSize ); + } + + m_xCtlDisplay->SetBitmapEx(&rBmp); + } + else + { + m_xCtlDisplay->SetBitmapEx(nullptr); + } + + m_xCtlDisplay->Invalidate(); + + m_xFiCount->set_label(OUString::number( + m_FrameList.size())); + + if (!m_FrameList.empty() && !bMovie) + { + size_t nIndex = m_nCurrentFrame + 1; + m_xNumFldBitmap->set_value(nIndex); + + // if there is at least 1 object in the list + m_xBtnFirst->set_sensitive(true); + m_xBtnReverse->set_sensitive(true); + m_xBtnPlay->set_sensitive(true); + m_xBtnLast->set_sensitive(true); + m_xNumFldBitmap->set_sensitive(true); + m_xTimeField->set_sensitive(true); + m_xLbLoopCount->set_sensitive(true); + m_xBtnRemoveBitmap->set_sensitive(true); + m_xBtnRemoveAll->set_sensitive(true); + } + else + { + // if no object is in the list + m_xBtnFirst->set_sensitive( false ); + m_xBtnReverse->set_sensitive( false ); + m_xBtnPlay->set_sensitive( false ); + m_xBtnLast->set_sensitive( false ); + m_xNumFldBitmap->set_sensitive( false ); + m_xTimeField->set_sensitive( false ); + m_xLbLoopCount->set_sensitive( false ); + m_xBtnRemoveBitmap->set_sensitive( false ); + m_xBtnRemoveAll->set_sensitive( false ); + } + + if( bMovie && bDisableCtrls ) + { + m_xBtnGetOneObject->set_sensitive( false ); + m_xBtnGetAllObjects->set_sensitive( false ); + m_xRbtGroup->set_sensitive( false ); + m_xRbtBitmap->set_sensitive( false ); + m_xBtnCreateGroup->set_sensitive( false ); + m_xFtAdjustment->set_sensitive( false ); + m_xLbAdjustment->set_sensitive( false ); + } + else + { + // enable 'group object' only if it is not an Animated GIF + if (m_FrameList.empty()) + { + m_xRbtGroup->set_sensitive(true); + } + + m_xRbtBitmap->set_sensitive(true); + m_xBtnCreateGroup->set_sensitive(!m_FrameList.empty()); + m_xFtAdjustment->set_sensitive(true); + m_xLbAdjustment->set_sensitive(true); + } + + ClickRbtHdl(*m_xRbtGroup); +} + +void AnimationWindow::ResetAttrs() +{ + m_xRbtGroup->set_active(true); + m_xLbAdjustment->set_active( BA_CENTER ); + // LoopCount + m_xLbLoopCount->set_active( m_xLbLoopCount->get_count() - 1); + + UpdateControl(); +} + +void AnimationWindow::WaitInEffect( sal_uLong nMilliSeconds, sal_uLong nTime, + SfxProgress* pProgress ) const +{ + sal_uInt64 aEnd = ::tools::Time::GetSystemTicks() + nMilliSeconds; + sal_uInt64 aCurrent = ::tools::Time::GetSystemTicks(); + while (aCurrent < aEnd) + { + aCurrent = ::tools::Time::GetSystemTicks(); + + if( pProgress ) + pProgress->SetState( nTime + nMilliSeconds + aCurrent - aEnd ); + + Application::Reschedule(); + + if( !bMovie ) + return; + } +} + +Fraction AnimationWindow::GetScale() +{ + Fraction aFrac; + size_t const nCount = m_FrameList.size(); + if (nCount > 0) + { + Size aBmpSize(0, 0); + for (size_t i = 0; i < nCount; i++) + { + BitmapEx const & rBitmap = m_FrameList[i].first; + Size aTempSize( rBitmap.GetBitmap().GetSizePixel() ); + aBmpSize.setWidth( std::max( aBmpSize.Width(), aTempSize.Width() ) ); + aBmpSize.setHeight( std::max( aBmpSize.Height(), aTempSize.Height() ) ); + } + + aBmpSize.AdjustWidth(10 ); + aBmpSize.AdjustHeight(10 ); + + Size aDisplaySize(m_xCtlDisplay->GetOutputSizePixel()); + + aFrac = Fraction( std::min( static_cast<double>(aDisplaySize.Width()) / static_cast<double>(aBmpSize.Width()), + static_cast<double>(aDisplaySize.Height()) / static_cast<double>(aBmpSize.Height()) ) ); + } + return aFrac; +} + +void AnimationWindow::Resize() +{ + SfxDockingWindow::Resize(); + Fraction aFrac(GetScale()); + m_xCtlDisplay->SetScale(aFrac); +} + +bool AnimationWindow::Close() +{ + if( maPlayLock.isLocked() ) + { + return false; + } + else + { + SfxBoolItem aItem( SID_ANIMATION_OBJECTS, false ); + + GetBindings().GetDispatcher()->ExecuteList( + SID_ANIMATION_OBJECTS, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aItem }); + + SfxDockingWindow::Close(); + + return true; + } +} + +void AnimationWindow::AddObj (::sd::View& rView ) +{ + // finish text entry mode to ensure that bitmap is identical with object + if( rView.IsTextEdit() ) + rView.SdrEndTextEdit(); + + // clone object(s) and insert the clone(s) into the list + const SdrMarkList& rMarkList = rView.GetMarkedObjectList(); + const size_t nMarkCount = rMarkList.GetMarkCount(); + SdPage* pPage = pMyDoc->GetSdPage(0, PageKind::Standard); + const size_t nCloneCount = pPage->GetObjCount(); + + if (nMarkCount <= 0) + return; + + // If it is ONE animation object or one group object, which was + // 'individually taken', we insert the objects separately + bool bAnimObj = false; + if( nMarkCount == 1 ) + { + SdrMark* pMark = rMarkList.GetMark(0); + SdrObject* pObject = pMark->GetMarkedSdrObj(); + SdAnimationInfo* pAnimInfo = SdDrawDocument::GetAnimationInfo( pObject ); + SdrInventor nInv = pObject->GetObjInventor(); + SdrObjKind nId = pObject->GetObjIdentifier(); + + // Animated Bitmap (GIF) + if( nInv == SdrInventor::Default && nId == SdrObjKind::Graphic && static_cast<SdrGrafObj*>( pObject )->IsAnimated() ) + { + const SdrGrafObj* pGrafObj = static_cast<SdrGrafObj*>(pObject); + Graphic aGraphic( pGrafObj->GetTransformedGraphic() ); + sal_uInt16 nCount = 0; + + if( aGraphic.IsAnimated() ) + nCount = aGraphic.GetAnimation().Count(); + + if( nCount > 0 ) + { + const Animation aAnimation( aGraphic.GetAnimation() ); + + for( sal_uInt16 i = 0; i < nCount; i++ ) + { + const AnimationBitmap& rAnimationBitmap = aAnimation.Get( i ); + + // LoopCount + if( i == 0 ) + { + sal_uInt32 nLoopCount = aAnimation.GetLoopCount(); + + if( !nLoopCount ) // endless + m_xLbLoopCount->set_active( m_xLbLoopCount->get_count() - 1); + else + m_xLbLoopCount->set_active_text(OUString::number( nLoopCount ) ); + } + + ::tools::Long nTime = rAnimationBitmap.mnWait; + ::tools::Time aTime( 0, 0, nTime / 100, nTime % 100 ); + size_t nIndex = m_nCurrentFrame + 1; + m_FrameList.insert( + m_FrameList.begin() + nIndex, + ::std::make_pair(rAnimationBitmap.maBitmapEx, aTime)); + + // increment => next one inserted after this one + ++m_nCurrentFrame; + } + // if an animated GIF is taken, only such one can be created + m_xRbtBitmap->set_active(true); + m_xRbtGroup->set_sensitive( false ); + bAnimObj = true; + } + } + else if( bAllObjects || ( pAnimInfo && pAnimInfo->mbIsMovie ) ) + { + // several objects + SdrObjList* pObjList = static_cast<SdrObjGroup*>(pObject)->GetSubList(); + + for( size_t nObject = 0; nObject < pObjList->GetObjCount(); ++nObject ) + { + SdrObject* pSnapShot(pObjList->GetObj(nObject)); + BitmapEx aBitmapEx(SdrExchangeView::GetObjGraphic(*pSnapShot).GetBitmapEx()); + size_t nIndex = m_nCurrentFrame + 1; + m_FrameList.insert( + m_FrameList.begin() + nIndex, + ::std::make_pair(aBitmapEx, m_xFormatter->GetTime())); + + // increment => next one inserted after this one + ++m_nCurrentFrame; + + // Clone + pPage->InsertObject( + pSnapShot->CloneSdrObject(pPage->getSdrModelFromSdrPage()), + m_nCurrentFrame); + } + bAnimObj = true; + } + } + // also one single animated object + if( !bAnimObj && !( bAllObjects && nMarkCount > 1 ) ) + { + BitmapEx aBitmapEx(rView.GetAllMarkedGraphic().GetBitmapEx()); + + ::tools::Time aTime( m_xFormatter->GetTime() ); + + size_t nIndex = m_nCurrentFrame + 1; + m_FrameList.insert( + m_FrameList.begin() + nIndex, + ::std::make_pair(aBitmapEx, aTime)); + } + + // one single object + if( nMarkCount == 1 && !bAnimObj ) + { + SdrMark* pMark = rMarkList.GetMark(0); + SdrObject* pObject = pMark->GetMarkedSdrObj(); + SdrObject* pClone(pObject->CloneSdrObject(pPage->getSdrModelFromSdrPage())); + size_t nIndex = m_nCurrentFrame + 1; + pPage->InsertObject(pClone, nIndex); + } + // several objects: group the clones + else if (nMarkCount > 1) + { + // take objects separately + if( bAllObjects ) + { + for( size_t nObject= 0; nObject < nMarkCount; ++nObject ) + { + // Clone + SdrObject* pObject(rMarkList.GetMark(nObject)->GetMarkedSdrObj()); + BitmapEx aBitmapEx(SdrExchangeView::GetObjGraphic(*pObject).GetBitmapEx()); + size_t nIndex = m_nCurrentFrame + 1; + m_FrameList.insert( + m_FrameList.begin() + nIndex, + ::std::make_pair(aBitmapEx, m_xFormatter->GetTime())); + + // increment => next one inserted after this one + ++m_nCurrentFrame; + + pPage->InsertObject( + pObject->CloneSdrObject(pPage->getSdrModelFromSdrPage()), + m_nCurrentFrame); + } + bAnimObj = true; // that we don't change again + } + else + { + SdrObjGroup* pCloneGroup = new SdrObjGroup(rView.getSdrModelFromSdrView()); + SdrObjList* pObjList = pCloneGroup->GetSubList(); + + for (size_t nObject= 0; nObject < nMarkCount; ++nObject) + { + pObjList->InsertObject( + rMarkList.GetMark(nObject)->GetMarkedSdrObj()->CloneSdrObject( + pPage->getSdrModelFromSdrPage())); + } + + size_t nIndex = m_nCurrentFrame + 1; + pPage->InsertObject(pCloneGroup, nIndex); + } + } + + if( !bAnimObj ) + { + ++m_nCurrentFrame; + } + + // if there was nothing in the animator before but now is something + // there, we can create an animation group + if (nCloneCount == 0 && !m_FrameList.empty()) + { + m_xBtnCreateGroup->set_sensitive(true); + } + + // calculate and set zoom for DisplayWin + Fraction aFrac( GetScale() ); + m_xCtlDisplay->SetScale(aFrac); + + UpdateControl(); +} + +void AnimationWindow::CreateAnimObj (::sd::View& rView ) +{ + vcl::Window* pOutWin = rView.GetFirstOutputDevice()->GetOwnerWindow(); // GetWin( 0 ); + DBG_ASSERT( pOutWin, "Window does not exist!" ); + + // find window center + const MapMode aMap100( MapUnit::Map100thMM ); + Size aMaxSizeLog; + Size aMaxSizePix; + Size aTemp( pOutWin->GetOutputSizePixel() ); + const Point aWindowCenter( pOutWin->PixelToLogic( Point( aTemp.Width() >> 1, aTemp.Height() >> 1 ) ) ); + const OutputDevice* pDefDev = Application::GetDefaultDevice(); + const size_t nCount = m_FrameList.size(); + BitmapAdjustment eBA = static_cast<BitmapAdjustment>(m_xLbAdjustment->get_active()); + + // find biggest bitmap + for (size_t i = 0; i < nCount; ++i) + { + const BitmapEx& rBmpEx = m_FrameList[i].first; + const Graphic aGraphic( rBmpEx ); + Size aTmpSizeLog; + const Size aTmpSizePix( rBmpEx.GetSizePixel() ); + + if ( aGraphic.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel ) + aTmpSizeLog = pDefDev->PixelToLogic( aGraphic.GetPrefSize(), aMap100 ); + else + aTmpSizeLog = OutputDevice::LogicToLogic( aGraphic.GetPrefSize(), aGraphic.GetPrefMapMode(), aMap100 ); + + aMaxSizeLog.setWidth( std::max( aMaxSizeLog.Width(), aTmpSizeLog.Width() ) ); + aMaxSizeLog.setHeight( std::max( aMaxSizeLog.Height(), aTmpSizeLog.Height() ) ); + + aMaxSizePix.setWidth( std::max( aMaxSizePix.Width(), aTmpSizePix.Width() ) ); + aMaxSizePix.setHeight( std::max( aMaxSizePix.Height(), aTmpSizePix.Height() ) ); + } + + SdrPageView* pPV = rView.GetSdrPageView(); + + if( m_xRbtBitmap->get_active() ) + { + // create bitmap group (Animated GIF) + Animation aAnimation; + Point aPt; + + for (size_t i = 0; i < nCount; ++i) + { + ::tools::Time const & rTime = m_FrameList[i].second; + ::tools::Long nTime = rTime.GetNanoSec(); + nTime += rTime.GetSec() * 100; + + BitmapEx const & rBitmapEx = m_FrameList[i].first; + + // calculate offset for the specified direction + const Size aBitmapSize( rBitmapEx.GetSizePixel() ); + + switch( eBA ) + { + case BA_LEFT_UP: + break; + + case BA_LEFT: + aPt.setY( (aMaxSizePix.Height() - aBitmapSize.Height()) >> 1 ); + break; + + case BA_LEFT_DOWN: + aPt.setY( aMaxSizePix.Height() - aBitmapSize.Height() ); + break; + + case BA_UP: + aPt.setX( (aMaxSizePix.Width() - aBitmapSize.Width()) >> 1 ); + break; + + case BA_CENTER: + aPt.setX( (aMaxSizePix.Width() - aBitmapSize.Width()) >> 1 ); + aPt.setY( (aMaxSizePix.Height() - aBitmapSize.Height()) >> 1 ); + break; + + case BA_DOWN: + aPt.setX( (aMaxSizePix.Width() - aBitmapSize.Width()) >> 1 ); + aPt.setY( aMaxSizePix.Height() - aBitmapSize.Height() ); + break; + + case BA_RIGHT_UP: + aPt.setX( aMaxSizePix.Width() - aBitmapSize.Width() ); + break; + + case BA_RIGHT: + aPt.setX( aMaxSizePix.Width() - aBitmapSize.Width() ); + aPt.setY( (aMaxSizePix.Height() - aBitmapSize.Height()) >> 1 ); + break; + + case BA_RIGHT_DOWN: + aPt.setX( aMaxSizePix.Width() - aBitmapSize.Width() ); + aPt.setY( aMaxSizePix.Height() - aBitmapSize.Height() ); + break; + + } + + // find LoopCount (number of passes) + AnimationBitmap aAnimationBitmap; + sal_uInt32 nLoopCount = 0; + sal_Int32 nPos = m_xLbLoopCount->get_active(); + + if( nPos != -1 && nPos != m_xLbLoopCount->get_count() - 1 ) // endless + nLoopCount = m_xLbLoopCount->get_active_text().toUInt32(); + + aAnimationBitmap.maBitmapEx = rBitmapEx; + aAnimationBitmap.maPositionPixel = aPt; + aAnimationBitmap.maSizePixel = aBitmapSize; + aAnimationBitmap.mnWait = nTime; + aAnimationBitmap.meDisposal = Disposal::Back; + aAnimationBitmap.mbUserInput = false; + + aAnimation.Insert( aAnimationBitmap ); + aAnimation.SetDisplaySizePixel( aMaxSizePix ); + aAnimation.SetLoopCount( nLoopCount ); + } + + SdrGrafObj* pGrafObj = new SdrGrafObj( + rView.getSdrModelFromSdrView(), + Graphic(aAnimation)); + const Point aOrg( aWindowCenter.X() - ( aMaxSizeLog.Width() >> 1 ), aWindowCenter.Y() - ( aMaxSizeLog.Height() >> 1 ) ); + + pGrafObj->SetLogicRect( ::tools::Rectangle( aOrg, aMaxSizeLog ) ); + rView.InsertObjectAtView( pGrafObj, *pPV, SdrInsertFlags::SETDEFLAYER); + } + else + { + // calculate offset for the specified direction + Size aOffset; + SdrObject * pClone = nullptr; + SdPage* pPage = pMyDoc->GetSdPage(0, PageKind::Standard); + + for (size_t i = 0; i < nCount; ++i) + { + pClone = pPage->GetObj(i); + ::tools::Rectangle aRect( pClone->GetSnapRect() ); + + switch( eBA ) + { + case BA_LEFT_UP: + break; + + case BA_LEFT: + aOffset.setHeight( (aMaxSizeLog.Height() - aRect.GetHeight()) / 2 ); + break; + + case BA_LEFT_DOWN: + aOffset.setHeight( aMaxSizeLog.Height() - aRect.GetHeight() ); + break; + + case BA_UP: + aOffset.setWidth( (aMaxSizeLog.Width() - aRect.GetWidth()) / 2 ); + break; + + case BA_CENTER: + aOffset.setWidth( (aMaxSizeLog.Width() - aRect.GetWidth()) / 2 ); + aOffset.setHeight( (aMaxSizeLog.Height() - aRect.GetHeight()) / 2 ); + break; + + case BA_DOWN: + aOffset.setWidth( (aMaxSizeLog.Width() - aRect.GetWidth()) / 2 ); + aOffset.setHeight( aMaxSizeLog.Height() - aRect.GetHeight() ); + break; + + case BA_RIGHT_UP: + aOffset.setWidth( aMaxSizeLog.Width() - aRect.GetWidth() ); + break; + + case BA_RIGHT: + aOffset.setWidth( aMaxSizeLog.Width() - aRect.GetWidth() ); + aOffset.setHeight( (aMaxSizeLog.Height() - aRect.GetHeight()) / 2 ); + break; + + case BA_RIGHT_DOWN: + aOffset.setWidth( aMaxSizeLog.Width() - aRect.GetWidth() ); + aOffset.setHeight( aMaxSizeLog.Height() - aRect.GetHeight() ); + break; + + } + // Unfortunately, SetSnapRect is not implemented for ellipses !!! + Point aMovePt( aWindowCenter + Point( aOffset.Width(), aOffset.Height() ) - aRect.TopLeft() ); + Size aMoveSize( aMovePt.X(), aMovePt.Y() ); + pClone->NbcMove( aMoveSize ); + } + + // #i42894# Caution(!) variable pPage looks right, but it is a page from the local + // document the dialog is using (!), so get the target page from the target view + SdPage* pTargetSdPage = dynamic_cast< SdPage* >(rView.GetSdrPageView() ? rView.GetSdrPageView()->GetPage() : nullptr); + + if(pTargetSdPage) + { + // create animation group + SdrObjGroup* pGroup = new SdrObjGroup(rView.getSdrModelFromSdrView()); + SdrObjList* pObjList = pGroup->GetSubList(); + + for (size_t i = 0; i < nCount; ++i) + { + // the clone remains in the animation; we insert a clone of the + // clone into the group + pClone = pPage->GetObj(i); + SdrObject* pCloneOfClone(pClone->CloneSdrObject(pTargetSdPage->getSdrModelFromSdrPage())); + //SdrObject* pCloneOfClone = pPage->GetObj(i)->Clone(); + pObjList->InsertObject(pCloneOfClone); + } + + // until now the top left corner of the group is in the window center; + // correct the position by half of the size of the group + aTemp = aMaxSizeLog; + aTemp.setHeight( - aTemp.Height() / 2 ); + aTemp.setWidth( - aTemp.Width() / 2 ); + pGroup->NbcMove(aTemp); + + // #i42894# create needed SMIL stuff and move child objects to page directly (see + // comments at EffectMigration::CreateAnimatedGroup why this has to be done). + EffectMigration::CreateAnimatedGroup(*pGroup, *pTargetSdPage); + + // #i42894# if that worked, delete the group again + if(!pGroup->GetSubList()->GetObjCount()) + { + // always use SdrObject::Free(...) for SdrObjects (!) + SdrObject* pTemp(pGroup); + SdrObject::Free(pTemp); + } + } + } + + ClickFirstHdl(*m_xBtnFirst); +} + +void AnimationWindow::DataChanged( const DataChangedEvent& rDCEvt ) +{ + SfxDockingWindow::DataChanged( rDCEvt ); + + if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) ) + { + UpdateControl(); + } +} + +/** + * ControllerItem for Animator + */ +AnimationControllerItem::AnimationControllerItem( + sal_uInt16 _nId, + AnimationWindow* pAnimWin, + SfxBindings* _pBindings) + : SfxControllerItem( _nId, *_pBindings ), + pAnimationWin( pAnimWin ) +{ +} + +void AnimationControllerItem::StateChangedAtToolBoxControl( sal_uInt16 nSId, + SfxItemState eState, const SfxPoolItem* pItem ) +{ + if( eState >= SfxItemState::DEFAULT && nSId == SID_ANIMATOR_STATE ) + { + const SfxUInt16Item* pStateItem = dynamic_cast< const SfxUInt16Item*>( pItem ); + assert(pStateItem); //SfxUInt16Item expected + if (pStateItem) + { + sal_uInt16 nState = pStateItem->GetValue(); + pAnimationWin->m_xBtnGetOneObject->set_sensitive( nState & 1 ); + pAnimationWin->m_xBtnGetAllObjects->set_sensitive( nState & 2 ); + } + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/assclass.cxx b/sd/source/ui/dlg/assclass.cxx new file mode 100644 index 000000000..4d48a327f --- /dev/null +++ b/sd/source/ui/dlg/assclass.cxx @@ -0,0 +1,160 @@ +/* -*- 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 <tools/debug.hxx> +#include <vcl/weld.hxx> + +#include <assclass.hxx> + +Assistent::Assistent(int nNoOfPages) + : mnPages(nNoOfPages), mnCurrentPage(1) +{ + if(mnPages > MAX_PAGES) + mnPages = MAX_PAGES; + + mpPageStatus.reset(new bool[mnPages]); + + for(int i=0; i < mnPages; ++i) + mpPageStatus[i] = true; +} + +bool Assistent::InsertControl(int nDestPage, weld::Widget* pUsedControl) +{ + DBG_ASSERT( (nDestPage > 0) && (nDestPage <= mnPages), "Page not available!"); + + if((nDestPage>0)&&(nDestPage<=mnPages)) + { + maPages[nDestPage-1].emplace_back(pUsedControl); + pUsedControl->hide(); + pUsedControl->set_sensitive(false); + return true; + } + + return false; +} + +void Assistent::NextPage() +{ + if(mnCurrentPage<mnPages) + { + int nPage = mnCurrentPage+1; + while(nPage <= mnPages && !mpPageStatus[nPage-1]) + nPage++; + + if(nPage <= mnPages) + GotoPage(nPage); + } +} + +void Assistent::PreviousPage() +{ + if(mnCurrentPage>1) + { + int nPage = mnCurrentPage-1; + while(nPage >= 0 && !mpPageStatus[nPage-1]) + nPage--; + + if(nPage >= 0) + GotoPage(nPage); + } +} + +bool Assistent::GotoPage(const int nPageToGo) +{ + DBG_ASSERT( (nPageToGo > 0) && (nPageToGo <= mnPages), "Page not available!"); + + if((nPageToGo>0)&&(nPageToGo<=mnPages)&&mpPageStatus[nPageToGo-1]) + { + int nIndex=mnCurrentPage-1; + + for(auto& rxPage : maPages[nIndex]) + { + rxPage->set_sensitive(false); + rxPage->hide(); + } + + mnCurrentPage=nPageToGo; + nIndex=mnCurrentPage-1; + + for(auto& rxPage : maPages[nIndex]) + { + rxPage->set_sensitive(true); + rxPage->show(); + } + + return true; + } + + return false; +} + +bool Assistent::IsLastPage() const +{ + if(mnCurrentPage == mnPages) + return true; + + int nPage = mnCurrentPage+1; + while(nPage <= mnPages && !mpPageStatus[nPage-1]) + nPage++; + + return nPage > mnPages; +} + +bool Assistent::IsFirstPage() const +{ + if(mnCurrentPage == 1) + return true; + + int nPage = mnCurrentPage-1; + while(nPage > 0 && !mpPageStatus[nPage-1]) + nPage--; + + return nPage == 0; +} + +bool Assistent::IsEnabled( int nPage ) const +{ + DBG_ASSERT( (nPage>0) && (nPage <= mnPages), "Page not available!" ); + + return (nPage>0) && (nPage <= mnPages && mpPageStatus[nPage-1]); +} + +void Assistent::EnablePage( int nPage ) +{ + DBG_ASSERT( (nPage>0) && (nPage <= mnPages), "Page not available!" ); + + if((nPage>0) && (nPage < mnPages && !mpPageStatus[nPage-1])) + { + mpPageStatus[nPage-1] = true; + } +} + +void Assistent::DisablePage( int nPage ) +{ + DBG_ASSERT( (nPage>0) && (nPage <= mnPages), "Page not available!" ); + + if((nPage>0) && (nPage <= mnPages && mpPageStatus[nPage-1])) + { + mpPageStatus[nPage-1] = false; + if(mnCurrentPage == nPage) + GotoPage(1); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/brkdlg.cxx b/sd/source/ui/dlg/brkdlg.cxx new file mode 100644 index 000000000..bc1d0f5cf --- /dev/null +++ b/sd/source/ui/dlg/brkdlg.cxx @@ -0,0 +1,156 @@ +/* -*- 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 <BreakDlg.hxx> +#include <sfx2/progress.hxx> + +#include <svx/svdetc.hxx> +#include <vcl/scheduler.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> + +#include <sdresid.hxx> +#include <drawview.hxx> +#include <strings.hrc> +#include <DrawDocShell.hxx> + +namespace sd { + +/** + * dialog to split metafiles + */ + +BreakDlg::BreakDlg(weld::Window* pWindow, DrawView* pDrView, DrawDocShell* pShell, + sal_uLong nSumActionCount, sal_uLong nObjCount) + : SfxDialogController(pWindow, "modules/sdraw/ui/breakdialog.ui", "BreakDialog") + , m_xFiObjInfo(m_xBuilder->weld_label("metafiles")) + , m_xFiActInfo(m_xBuilder->weld_label("metaobjects")) + , m_xFiInsInfo(m_xBuilder->weld_label("drawingobjects")) + , m_xBtnCancel(m_xBuilder->weld_button("cancel")) + , m_pDrView(pDrView) + , m_bCancel(false) + , m_aUpdateIdle( "sd::BreakDlg m_aUpdateIdle" ) +{ + m_aUpdateIdle.SetPriority( TaskPriority::REPAINT ); + m_aUpdateIdle.SetInvokeHandler( LINK( this, BreakDlg, InitialUpdate ) ); + + m_xBtnCancel->connect_clicked(LINK(this, BreakDlg, CancelButtonHdl)); + + m_xProgress.reset(new SfxProgress(pShell, SdResId(STR_BREAK_METAFILE), nSumActionCount*3)); + + m_xProgrInfo.reset(new SvdProgressInfo(LINK(this, BreakDlg, UpDate))); + // every action is edited 3 times in DoImport() + m_xProgrInfo->Init( nObjCount ); +} + +// Control-Handler for cancel button +IMPL_LINK_NOARG(BreakDlg, CancelButtonHdl, weld::Button&, void) +{ + m_bCancel = true; + m_xBtnCancel->set_sensitive(false); +} + +/** + * The working function has to call the UpDate method periodically. + * With the first call, the overall number of actions is provided. + * Every following call should contain the finished actions since the + * last call of UpDate. + */ +IMPL_LINK( BreakDlg, UpDate, void*, nInit, bool ) +{ + if (!m_xProgrInfo) + return true; + + // update status bar or show an error message? + if(nInit == reinterpret_cast<void*>(1)) + { + std::unique_ptr<weld::MessageDialog> xErrBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Warning, VclButtonsType::Ok, + SdResId(STR_BREAK_FAIL))); + xErrBox->run(); + } + else + { + if (m_xProgress) + m_xProgress->SetState(m_xProgrInfo->GetSumCurAction()); + } + + // which object is shown at the moment? + OUString info = OUString::number(m_xProgrInfo->GetCurObj()) + + "/" + + OUString::number(m_xProgrInfo->GetObjCount()); + m_xFiObjInfo->set_label(info); + + // how many actions are started? + if (m_xProgrInfo->GetActionCount() == 0) + { + m_xFiActInfo->set_label( OUString() ); + } + else + { + info = OUString::number(m_xProgrInfo->GetCurAction()) + + "/" + + OUString::number(m_xProgrInfo->GetActionCount()); + m_xFiActInfo->set_label(info); + } + + // and inserted???? + if (m_xProgrInfo->GetInsertCount() == 0) + { + m_xFiInsInfo->set_label( OUString() ); + } + else + { + info = OUString::number(m_xProgrInfo->GetCurInsert()) + + "/" + + OUString::number(m_xProgrInfo->GetInsertCount()); + m_xFiInsInfo->set_label(info); + } + + // make sure dialog gets painted, it is intended to + // show the progress to the user. Also necessary to + // provide a clickable cancel button + Scheduler::ProcessEventsToIdle(); + + // return okay-value (-> !cancel) + return !m_bCancel; +} + +/** + * open a modal dialog and start a timer which calls the working function after + * the opening of the dialog + */ +short BreakDlg::run() +{ + m_aUpdateIdle.Start(); + return SfxDialogController::run(); +} + +/** + * link-method which starts the working function + */ +IMPL_LINK_NOARG(BreakDlg, InitialUpdate, Timer *, void) +{ + m_pDrView->DoImportMarkedMtf(m_xProgrInfo.get()); + m_xDialog->response(RET_OK); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/copydlg.cxx b/sd/source/ui/dlg/copydlg.cxx new file mode 100644 index 000000000..1fa8c2dab --- /dev/null +++ b/sd/source/ui/dlg/copydlg.cxx @@ -0,0 +1,263 @@ +/* -*- 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 <copydlg.hxx> +#include <svx/colorbox.hxx> +#include <svx/svdpagv.hxx> +#include <svx/sdangitm.hxx> +#include <sfx2/module.hxx> +#include <svx/xcolit.hxx> +#include <svl/intitem.hxx> + +#include <unotools/viewoptions.hxx> +#include <svtools/unitconv.hxx> +#include <o3tl/string_view.hxx> + +#include <sdattr.hrc> +#include <View.hxx> +#include <drawdoc.hxx> + + +namespace sd { + +constexpr char TOKEN = ';'; + +CopyDlg::CopyDlg(weld::Window* pWindow, const SfxItemSet& rInAttrs, ::sd::View* pInView) + : SfxDialogController(pWindow, "modules/sdraw/ui/copydlg.ui", "DuplicateDialog") + , mrOutAttrs(rInAttrs) + , maUIScale(pInView->GetDoc().GetUIScale()) + , mpView(pInView) + , m_xNumFldCopies(m_xBuilder->weld_spin_button("copies")) + , m_xBtnSetViewData(m_xBuilder->weld_button("viewdata")) + , m_xMtrFldMoveX(m_xBuilder->weld_metric_spin_button("x", FieldUnit::CM)) + , m_xMtrFldMoveY(m_xBuilder->weld_metric_spin_button("y", FieldUnit::CM)) + , m_xMtrFldAngle(m_xBuilder->weld_metric_spin_button("angle", FieldUnit::DEGREE)) + , m_xMtrFldWidth(m_xBuilder->weld_metric_spin_button("width", FieldUnit::CM)) + , m_xMtrFldHeight(m_xBuilder->weld_metric_spin_button("height", FieldUnit::CM)) + , m_xFtEndColor(m_xBuilder->weld_label("endlabel")) + , m_xBtnSetDefault(m_xBuilder->weld_button("default")) + , m_xLbStartColor(new ColorListBox(m_xBuilder->weld_menu_button("start"), [this]{ return m_xDialog.get(); } )) + , m_xLbEndColor(new ColorListBox(m_xBuilder->weld_menu_button("end"), [this]{ return m_xDialog.get(); } )) +{ + m_xLbStartColor->SetSelectHdl( LINK( this, CopyDlg, SelectColorHdl ) ); + m_xBtnSetViewData->connect_clicked( LINK( this, CopyDlg, SetViewData ) ); + m_xBtnSetDefault->connect_clicked( LINK( this, CopyDlg, SetDefault ) ); + + FieldUnit eFUnit( SfxModule::GetCurrentFieldUnit() ); + + SetFieldUnit( *m_xMtrFldMoveX, eFUnit, true ); + SetFieldUnit( *m_xMtrFldMoveY, eFUnit, true ); + SetFieldUnit( *m_xMtrFldWidth, eFUnit, true ); + SetFieldUnit( *m_xMtrFldHeight, eFUnit, true ); + + Reset(); +} + +CopyDlg::~CopyDlg() +{ + SvtViewOptions aDlgOpt(EViewType::Dialog, OStringToOUString(m_xDialog->get_help_id(), RTL_TEXTENCODING_UTF8)); + OUString sStr = + OUString::number(m_xNumFldCopies->get_value()) + OUStringChar(TOKEN) + + OUString::number(m_xMtrFldMoveX->get_value(FieldUnit::NONE)) + OUStringChar(TOKEN) + + OUString::number(m_xMtrFldMoveY->get_value(FieldUnit::NONE)) + OUStringChar(TOKEN) + + OUString::number(m_xMtrFldAngle->get_value(FieldUnit::NONE)) + OUStringChar(TOKEN) + + OUString::number(m_xMtrFldWidth->get_value(FieldUnit::NONE)) + OUStringChar(TOKEN) + + OUString::number(m_xMtrFldHeight->get_value(FieldUnit::NONE)) + OUStringChar(TOKEN) + + OUString::number(static_cast<sal_uInt32>(m_xLbStartColor->GetSelectEntryColor())) + OUStringChar(TOKEN) + + OUString::number(static_cast<sal_uInt32>(m_xLbEndColor->GetSelectEntryColor())); + aDlgOpt.SetUserItem("UserItem", css::uno::Any(sStr)); +} + +/** + * reads provided item set or evaluate ini string + */ +void CopyDlg::Reset() +{ + // Set Min/Max values + ::tools::Rectangle aRect = mpView->GetAllMarkedRect(); + Size aPageSize = mpView->GetSdrPageView()->GetPage()->GetSize(); + + // tdf#125011 draw/impress sizes are in mm_100th already, "normalize" to + // decimal shift by number of decimal places the widgets are using (2) then + // scale by the ui scaling factor + auto nPageWidth = tools::Long(m_xMtrFldMoveX->normalize(aPageSize.Width()) / maUIScale); + auto nPageHeight = tools::Long(m_xMtrFldMoveX->normalize(aPageSize.Height()) / maUIScale); + auto nRectWidth = tools::Long(m_xMtrFldMoveX->normalize(aRect.GetWidth()) / maUIScale); + auto nRectHeight = tools::Long(m_xMtrFldMoveX->normalize(aRect.GetHeight()) / maUIScale); + + m_xMtrFldMoveX->set_range(-nPageWidth, nPageWidth, FieldUnit::MM_100TH); + m_xMtrFldMoveY->set_range(-nPageHeight, nPageHeight, FieldUnit::MM_100TH); + m_xMtrFldWidth->set_range(-nRectWidth, nPageWidth, FieldUnit::MM_100TH); + m_xMtrFldHeight->set_range(-nRectHeight, nPageHeight, FieldUnit::MM_100TH); + + OUString aStr; + SvtViewOptions aDlgOpt(EViewType::Dialog, OStringToOUString(m_xDialog->get_help_id(), RTL_TEXTENCODING_UTF8)); + if (aDlgOpt.Exists()) + { + css::uno::Any aUserItem = aDlgOpt.GetUserItem("UserItem"); + aUserItem >>= aStr; + } + + if (aStr.isEmpty()) + { + if( const SfxUInt16Item* pPoolItem = mrOutAttrs.GetItemIfSet( ATTR_COPY_NUMBER ) ) + m_xNumFldCopies->set_value(pPoolItem->GetValue()); + else + m_xNumFldCopies->set_value(1); + + tools::Long nMoveX = 500; + if( const SfxInt32Item* pPoolItem = mrOutAttrs.GetItemIfSet( ATTR_COPY_MOVE_X ) ) + nMoveX = pPoolItem->GetValue(); + SetMetricValue( *m_xMtrFldMoveX, tools::Long(nMoveX / maUIScale), MapUnit::Map100thMM); + + tools::Long nMoveY = 500; + if( const SfxInt32Item* pPoolItem = mrOutAttrs.GetItemIfSet( ATTR_COPY_MOVE_Y ) ) + nMoveY = pPoolItem->GetValue(); + SetMetricValue( *m_xMtrFldMoveY, tools::Long(nMoveY / maUIScale), MapUnit::Map100thMM); + + if( const SdrAngleItem* pPoolItem = mrOutAttrs.GetItemIfSet( ATTR_COPY_ANGLE ) ) + m_xMtrFldAngle->set_value( pPoolItem->GetValue().get(), FieldUnit::NONE); + else + m_xMtrFldAngle->set_value(0, FieldUnit::NONE); + + tools::Long nWidth = 0; + if( const SfxInt32Item* pPoolItem = mrOutAttrs.GetItemIfSet( ATTR_COPY_WIDTH ) ) + nWidth = pPoolItem->GetValue(); + SetMetricValue( *m_xMtrFldWidth, tools::Long(nWidth / maUIScale), MapUnit::Map100thMM); + + tools::Long nHeight = 0; + if( const SfxInt32Item* pPoolItem = mrOutAttrs.GetItemIfSet( ATTR_COPY_HEIGHT ) ) + nHeight = pPoolItem->GetValue(); + SetMetricValue( *m_xMtrFldHeight, tools::Long(nHeight / maUIScale), MapUnit::Map100thMM); + + if( const XColorItem* pPoolItem = mrOutAttrs.GetItemIfSet( ATTR_COPY_START_COLOR ) ) + { + Color aColor = pPoolItem->GetColorValue(); + m_xLbStartColor->SelectEntry( aColor ); + m_xLbEndColor->SelectEntry( aColor ); + } + else + { + m_xLbStartColor->SetNoSelection(); + m_xLbEndColor->SetNoSelection(); + m_xLbEndColor->set_sensitive(false); + m_xFtEndColor->set_sensitive(false); + } + } + else + { + sal_Int32 nIdx {0}; + m_xNumFldCopies->set_value(o3tl::toInt64(o3tl::getToken(aStr, 0, TOKEN, nIdx))); + m_xMtrFldMoveX->set_value(o3tl::toInt64(o3tl::getToken(aStr, 0, TOKEN, nIdx)), FieldUnit::NONE); + m_xMtrFldMoveY->set_value(o3tl::toInt64(o3tl::getToken(aStr, 0, TOKEN, nIdx)), FieldUnit::NONE); + m_xMtrFldAngle->set_value(o3tl::toInt64(o3tl::getToken(aStr, 0, TOKEN, nIdx)), FieldUnit::NONE); + m_xMtrFldWidth->set_value(o3tl::toInt64(o3tl::getToken(aStr, 0, TOKEN, nIdx)), FieldUnit::NONE); + m_xMtrFldHeight->set_value(o3tl::toInt64(o3tl::getToken(aStr, 0, TOKEN, nIdx)), FieldUnit::NONE); + m_xLbStartColor->SelectEntry( Color( ColorTransparency, o3tl::toUInt32(o3tl::getToken(aStr, 0, TOKEN, nIdx)) ) ); + m_xLbEndColor->SelectEntry( Color( ColorTransparency, o3tl::toUInt32(o3tl::getToken(aStr, 0, TOKEN, nIdx)) ) ); + } + +} + +/** + * fills provided item set with dialog box attributes + */ +void CopyDlg::GetAttr( SfxItemSet& rOutAttrs ) +{ + tools::Long nMoveX = tools::Long( GetCoreValue( *m_xMtrFldMoveX, MapUnit::Map100thMM) * maUIScale); + tools::Long nMoveY = tools::Long( GetCoreValue( *m_xMtrFldMoveY, MapUnit::Map100thMM) * maUIScale); + tools::Long nHeight = tools::Long( GetCoreValue( *m_xMtrFldHeight, MapUnit::Map100thMM) * maUIScale); + tools::Long nWidth = tools::Long( GetCoreValue( *m_xMtrFldWidth, MapUnit::Map100thMM) * maUIScale); + + rOutAttrs.Put( SfxUInt16Item( ATTR_COPY_NUMBER, static_cast<sal_uInt16>(m_xNumFldCopies->get_value()) ) ); + rOutAttrs.Put( SfxInt32Item( ATTR_COPY_MOVE_X, nMoveX ) ); + rOutAttrs.Put( SfxInt32Item( ATTR_COPY_MOVE_Y, nMoveY ) ); + rOutAttrs.Put( SdrAngleItem( ATTR_COPY_ANGLE, Degree100(static_cast<sal_Int32>(m_xMtrFldAngle->get_value(FieldUnit::DEGREE))) ) ); + rOutAttrs.Put( SfxInt32Item( ATTR_COPY_WIDTH, nWidth ) ); + rOutAttrs.Put( SfxInt32Item( ATTR_COPY_HEIGHT, nHeight ) ); + + NamedColor aColor = m_xLbStartColor->GetSelectedEntry(); + rOutAttrs.Put(XColorItem(ATTR_COPY_START_COLOR, aColor.second, aColor.first)); + aColor = m_xLbEndColor->GetSelectedEntry(); + rOutAttrs.Put(XColorItem(ATTR_COPY_END_COLOR, aColor.second, aColor.first)); +} + +/** + * enables and selects end color LB + */ +IMPL_LINK_NOARG(CopyDlg, SelectColorHdl, ColorListBox&, void) +{ + const Color aColor = m_xLbStartColor->GetSelectEntryColor(); + + if (!m_xLbEndColor->get_sensitive()) + { + m_xLbEndColor->SelectEntry(aColor); + m_xLbEndColor->set_sensitive(true); + m_xFtEndColor->set_sensitive(true); + } +} + +/** + * sets values of selection + */ +IMPL_LINK_NOARG(CopyDlg, SetViewData, weld::Button&, void) +{ + ::tools::Rectangle aRect = mpView->GetAllMarkedRect(); + + SetMetricValue( *m_xMtrFldMoveX, tools::Long( aRect.GetWidth() / + maUIScale ), MapUnit::Map100thMM); + SetMetricValue( *m_xMtrFldMoveY, tools::Long( aRect.GetHeight() / + maUIScale ), MapUnit::Map100thMM); + + // sets color attribute + if( const XColorItem* pPoolItem = mrOutAttrs.GetItemIfSet( ATTR_COPY_START_COLOR ) ) + { + Color aColor = pPoolItem->GetColorValue(); + m_xLbStartColor->SelectEntry( aColor ); + } +} + +/** + * resets values to default + */ +IMPL_LINK_NOARG(CopyDlg, SetDefault, weld::Button&, void) +{ + m_xNumFldCopies->set_value(1); + + tools::Long nValue = 500; + SetMetricValue( *m_xMtrFldMoveX, tools::Long(nValue / maUIScale), MapUnit::Map100thMM); + SetMetricValue( *m_xMtrFldMoveY, tools::Long(nValue / maUIScale), MapUnit::Map100thMM); + + nValue = 0; + m_xMtrFldAngle->set_value(nValue, FieldUnit::DEGREE); + SetMetricValue( *m_xMtrFldWidth, tools::Long(nValue / maUIScale), MapUnit::Map100thMM); + SetMetricValue( *m_xMtrFldHeight, tools::Long(nValue / maUIScale), MapUnit::Map100thMM); + + // set color attribute + if( const XColorItem* pPoolItem = mrOutAttrs.GetItemIfSet( ATTR_COPY_START_COLOR ) ) + { + Color aColor = pPoolItem->GetColorValue(); + m_xLbStartColor->SelectEntry( aColor ); + m_xLbEndColor->SelectEntry( aColor ); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/custsdlg.cxx b/sd/source/ui/dlg/custsdlg.cxx new file mode 100644 index 000000000..bc421d7e4 --- /dev/null +++ b/sd/source/ui/dlg/custsdlg.cxx @@ -0,0 +1,478 @@ +/* -*- 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 <custsdlg.hxx> + +#include <strings.hrc> +#include <sdresid.hxx> + +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <cusshow.hxx> +#include <customshowlist.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <unotools/charclass.hxx> +#include <tools/debug.hxx> + +// SdCustomShowDlg +SdCustomShowDlg::SdCustomShowDlg(weld::Window* pWindow, SdDrawDocument& rDrawDoc) + : GenericDialogController(pWindow, "modules/simpress/ui/customslideshows.ui", "CustomSlideShows") + , rDoc(rDrawDoc) + , pCustomShowList(nullptr) + , m_xLbCustomShows(m_xBuilder->weld_tree_view("customshowlist")) + , m_xBtnNew(m_xBuilder->weld_button("new")) + , m_xBtnEdit(m_xBuilder->weld_button("edit")) + , m_xBtnRemove(m_xBuilder->weld_button("delete")) + , m_xBtnCopy(m_xBuilder->weld_button("copy")) + , m_xBtnHelp(m_xBuilder->weld_button("help")) + , m_xBtnStartShow(m_xBuilder->weld_button("startshow")) + , m_xBtnOK(m_xBuilder->weld_button("ok")) +{ + m_xLbCustomShows->set_size_request(m_xLbCustomShows->get_approximate_digit_width() * 32, + m_xLbCustomShows->get_height_rows(8)); + + Link<weld::Button&,void> aLink( LINK( this, SdCustomShowDlg, ClickButtonHdl ) ); + m_xBtnNew->connect_clicked( aLink ); + m_xBtnEdit->connect_clicked( aLink ); + m_xBtnRemove->connect_clicked( aLink ); + m_xBtnCopy->connect_clicked( aLink ); + m_xLbCustomShows->connect_changed( LINK( this, SdCustomShowDlg, SelectListBoxHdl ) ); + + m_xBtnStartShow->connect_clicked( LINK( this, SdCustomShowDlg, StartShowHdl ) ); // for test + + // get CustomShow list of docs + pCustomShowList = rDoc.GetCustomShowList(); + if( pCustomShowList ) + { + tools::Long nPosToSelect = pCustomShowList->GetCurPos(); + // fill ListBox with CustomShows + for( SdCustomShow* pCustomShow = pCustomShowList->First(); + pCustomShow != nullptr; + pCustomShow = pCustomShowList->Next() ) + { + m_xLbCustomShows->append_text(pCustomShow->GetName()); + } + m_xLbCustomShows->select(nPosToSelect); + pCustomShowList->Seek( nPosToSelect ); + } + + CheckState(); +} + +SdCustomShowDlg::~SdCustomShowDlg() +{ +} + +void SdCustomShowDlg::CheckState() +{ + int nPos = m_xLbCustomShows->get_selected_index(); + + bool bEnable = nPos != -1; + m_xBtnEdit->set_sensitive( bEnable ); + m_xBtnRemove->set_sensitive( bEnable ); + m_xBtnCopy->set_sensitive( bEnable ); + m_xBtnStartShow->set_sensitive(bEnable); + + if (bEnable && pCustomShowList) + pCustomShowList->Seek( nPos ); +} + +IMPL_LINK( SdCustomShowDlg, ClickButtonHdl, weld::Button&, r, void ) +{ + SelectHdl(&r); +} + +IMPL_LINK( SdCustomShowDlg, SelectListBoxHdl, weld::TreeView&, rListBox, void ) +{ + SelectHdl(&rListBox); +} + +void SdCustomShowDlg::SelectHdl(void const *p) +{ + // new CustomShow + if (p == m_xBtnNew.get()) + { + std::unique_ptr<SdCustomShow> pCustomShow; + SdDefineCustomShowDlg aDlg(m_xDialog.get(), rDoc, pCustomShow); + if (aDlg.run() == RET_OK) + { + if( pCustomShow ) + { + if( !pCustomShowList ) + pCustomShowList = rDoc.GetCustomShowList( true ); + + SdCustomShow* pCustomShowTmp = pCustomShow.get(); + pCustomShowList->push_back( std::move(pCustomShow) ); + pCustomShowList->Last(); + m_xLbCustomShows->append_text( pCustomShowTmp->GetName() ); + m_xLbCustomShows->select_text( pCustomShowTmp->GetName() ); + } + } + } + // edit CustomShow + else if( p == m_xBtnEdit.get() ) + { + int nPos = m_xLbCustomShows->get_selected_index(); + if (nPos != -1) + { + DBG_ASSERT( pCustomShowList, "pCustomShowList does not exist" ); + std::unique_ptr<SdCustomShow>& pCustomShow = (*pCustomShowList)[ nPos ]; + SdDefineCustomShowDlg aDlg(m_xDialog.get(), rDoc, pCustomShow); + + if (aDlg.run() == RET_OK) + { + pCustomShowList->Seek(nPos); + m_xLbCustomShows->remove(nPos); + m_xLbCustomShows->insert_text(nPos, pCustomShow->GetName()); + m_xLbCustomShows->select(nPos); + } + } + } + // delete CustomShow + else if( p == m_xBtnRemove.get() ) + { + int nPos = m_xLbCustomShows->get_selected_index(); + if (nPos != -1) + { + pCustomShowList->erase( pCustomShowList->begin() + nPos ); + m_xLbCustomShows->remove(nPos); + m_xLbCustomShows->select(nPos == 0 ? nPos : nPos - 1); + } + } + // copy CustomShow + else if( p == m_xBtnCopy.get() ) + { + int nPos = m_xLbCustomShows->get_selected_index(); + if (nPos != -1) + { + std::unique_ptr<SdCustomShow> pShow(new SdCustomShow( *(*pCustomShowList)[nPos] )); + OUString aStr( pShow->GetName() ); + OUString aStrCopy( SdResId( STR_COPY_CUSTOMSHOW ) ); + + sal_Int32 nStrPos = aStr.indexOf( aStrCopy ); + sal_Int32 nNum = 1; + if( nStrPos < 0 ) + { + aStr += " (" + aStrCopy + OUString::number( nNum ) + ")"; + nStrPos = aStr.indexOf( aStrCopy ); + } + nStrPos = nStrPos + aStrCopy.getLength(); + // that we do not access into the nirvana (--> endless loop) + if( nStrPos >= aStr.getLength() ) + { + aStr += " " + OUString::number( nNum ); + } + + // check name... + bool bDifferent = false; + //long nPosToSelect = pCustomShowList->GetCurPos(); + while( !bDifferent ) + { + bDifferent = true; + for( SdCustomShow* pCustomShow = pCustomShowList->First(); + pCustomShow != nullptr && bDifferent; + pCustomShow = pCustomShowList->Next() ) + { + if( aStr == pCustomShow->GetName() ) + bDifferent = false; + } + if( !bDifferent ) + { + // replace number by a number increased by 1 + + const CharClass* pCharClass = rDoc.GetCharClass(); + while( pCharClass->isDigit( aStr, nStrPos ) ) + aStr = aStr.replaceAt( nStrPos, 1, u"" ); + aStr = aStr.subView( 0, nStrPos) + OUString::number( ++nNum ) + aStr.subView( nStrPos); + } + + } + //pCustomShowList->Seek( nPosToSelect ); + pShow->SetName( aStr ); + + auto pShowTmp = pShow.get(); + pCustomShowList->push_back( std::move(pShow) ); + pCustomShowList->Last(); + m_xLbCustomShows->append_text(pShowTmp->GetName()); + m_xLbCustomShows->select_text(pShowTmp->GetName()); + } + } + else if( p == m_xLbCustomShows.get() ) + { + int nPos = m_xLbCustomShows->get_selected_index(); + if (nPos != -1) + pCustomShowList->Seek(nPos); + } + + CheckState(); +} + +// StartShow-Hdl +IMPL_LINK_NOARG(SdCustomShowDlg, StartShowHdl, weld::Button&, void) +{ + m_xDialog->response(RET_YES); +} + +// CheckState +bool SdCustomShowDlg::IsCustomShow() const +{ + if (!pCustomShowList->empty()) + return true; + else + return false; +} + +// SdDefineCustomShowDlg +SdDefineCustomShowDlg::SdDefineCustomShowDlg(weld::Window* pWindow, SdDrawDocument& rDrawDoc, std::unique_ptr<SdCustomShow>& rpCS) + : GenericDialogController(pWindow, "modules/simpress/ui/definecustomslideshow.ui", "DefineCustomSlideShow") + , rDoc(rDrawDoc) + , rpCustomShow(rpCS) + , bModified(false) + , m_xEdtName(m_xBuilder->weld_entry("customname")) + , m_xLbPages(m_xBuilder->weld_tree_view("pages")) + , m_xBtnAdd(m_xBuilder->weld_button("add")) + , m_xBtnRemove(m_xBuilder->weld_button("remove")) + , m_xLbCustomPages(m_xBuilder->weld_tree_view("custompages")) + , m_xDropTargetHelper(new weld::ReorderingDropTarget(*m_xLbCustomPages)) + , m_xBtnOK(m_xBuilder->weld_button("ok")) + , m_xBtnCancel(m_xBuilder->weld_button("cancel")) + , m_xBtnHelp(m_xBuilder->weld_button("help")) +{ + Link<weld::Button&,void> aLink = LINK( this, SdDefineCustomShowDlg, ClickButtonHdl ); + m_xBtnAdd->connect_clicked( aLink ); + m_xBtnRemove->connect_clicked( aLink ); + m_xEdtName->connect_changed( LINK( this, SdDefineCustomShowDlg, ClickButtonEditHdl ) ); + m_xLbPages->connect_changed( LINK( this, SdDefineCustomShowDlg, ClickButtonHdl4 ) ); // because of status + m_xLbCustomPages->connect_changed( LINK( this, SdDefineCustomShowDlg, ClickButtonHdl3 ) ); // because of status + + m_xBtnOK->connect_clicked( LINK( this, SdDefineCustomShowDlg, OKHdl ) ); + + m_xLbPages->set_selection_mode(SelectionMode::Multiple); + + // shape 'em a bit + m_xLbPages->set_size_request(m_xLbPages->get_approximate_digit_width() * 24, m_xLbPages->get_height_rows(10)); + m_xLbCustomPages->set_size_request(m_xLbPages->get_approximate_digit_width() * 24, m_xLbCustomPages->get_height_rows(10)); + + // fill Listbox with page names of Docs + for( tools::Long nPage = 0; + nPage < rDoc.GetSdPageCount( PageKind::Standard ); + nPage++ ) + { + SdPage* pPage = rDoc.GetSdPage( static_cast<sal_uInt16>(nPage), PageKind::Standard ); + m_xLbPages->append_text(pPage->GetName()); + } + // aLbPages.SelectEntryPos( 0 ); + + if( rpCustomShow ) + { + aOldName = rpCustomShow->GetName(); + m_xEdtName->set_text( aOldName ); + + // fill ListBox with CustomShow pages + for( const auto& rpPage : rpCustomShow->PagesVector() ) + { + m_xLbCustomPages->append(weld::toId(rpPage), rpPage->GetName(), ""); + } + } + else + { + rpCustomShow.reset(new SdCustomShow); + m_xEdtName->set_text( SdResId( STR_NEW_CUSTOMSHOW ) ); + m_xEdtName->select_region(0, -1); + rpCustomShow->SetName( m_xEdtName->get_text() ); + } + + m_xBtnOK->set_sensitive( false ); + CheckState(); +} + +SdDefineCustomShowDlg::~SdDefineCustomShowDlg() +{ +} + +// CheckState +void SdDefineCustomShowDlg::CheckState() +{ + bool bPages = m_xLbPages->count_selected_rows() > 0; + bool bCSPages = m_xLbCustomPages->get_selected_index() != -1; + bool bCount = m_xLbCustomPages->n_children() > 0; + + m_xBtnOK->set_sensitive( bCount ); + m_xBtnAdd->set_sensitive( bPages ); + m_xBtnRemove->set_sensitive( bCSPages ); +} + +IMPL_LINK( SdDefineCustomShowDlg, ClickButtonHdl, weld::Button&, rWidget, void ) +{ + ClickButtonHdl2(&rWidget); +} + +IMPL_LINK( SdDefineCustomShowDlg, ClickButtonHdl3, weld::TreeView&, rWidget, void ) +{ + ClickButtonHdl2(&rWidget); +} + +IMPL_LINK( SdDefineCustomShowDlg, ClickButtonHdl4, weld::TreeView&, rListBox, void ) +{ + ClickButtonHdl2(&rListBox); +} + +IMPL_LINK( SdDefineCustomShowDlg, ClickButtonEditHdl, weld::Entry&, rEdit, void ) +{ + ClickButtonHdl2(&rEdit); +} + +// ButtonHdl() +void SdDefineCustomShowDlg::ClickButtonHdl2(void const * p) +{ + if( p == m_xBtnAdd.get() ) + { + auto aRows = m_xLbPages->get_selected_rows(); + if (!aRows.empty()) + { + int nPosCP = m_xLbCustomPages->get_selected_index(); + if (nPosCP != -1) + ++nPosCP; + + for (auto i : aRows) + { + OUString aStr = m_xLbPages->get_text(i); + SdPage* pPage = rDoc.GetSdPage(i, PageKind::Standard); + OUString sId(weld::toId(pPage)); + m_xLbCustomPages->insert(nPosCP, aStr, &sId, nullptr, nullptr); + m_xLbCustomPages->select(nPosCP != -1 ? nPosCP : m_xLbCustomPages->n_children() - 1); + + if (nPosCP != -1) + ++nPosCP; + } + bModified = true; + } + } + else if (p == m_xBtnRemove.get()) + { + int nPos = m_xLbCustomPages->get_selected_index(); + if (nPos != -1) + { + m_xLbCustomPages->remove(nPos); + m_xLbCustomPages->select(nPos == 0 ? nPos : nPos - 1); + bModified = true; + } + } + else if( p == m_xEdtName.get() ) + { + bModified = true; + } + + CheckState(); +} + +/** + * Checks the page pointer of the Show since entries can be moved and copied + * by TreeLB. + */ +void SdDefineCustomShowDlg::CheckCustomShow() +{ + bool bDifferent = false; + + // compare count + size_t nCount = m_xLbCustomPages->n_children(); + if (rpCustomShow->PagesVector().size() != nCount) + { + rpCustomShow->PagesVector().clear(); + bDifferent = true; + } + + // compare page pointer + if( !bDifferent ) + { + size_t i = 0; + for (const auto& rpPage : rpCustomShow->PagesVector()) + { + SdPage* pPage = weld::fromId<SdPage*>(m_xLbCustomPages->get_id(i)); + if (rpPage != pPage) + { + rpCustomShow->PagesVector().clear(); + bDifferent = true; + break; + } + + ++i; + } + } + + // set new page pointer + if( bDifferent ) + { + for (size_t i = 0; i < nCount; ++i) + { + SdPage* pPage = weld::fromId<SdPage*>(m_xLbCustomPages->get_id(i)); + rpCustomShow->PagesVector().push_back(pPage); + } + bModified = true; + } + + // compare name and set name if necessary + OUString aStr( m_xEdtName->get_text() ); + if( rpCustomShow->GetName() != aStr ) + { + rpCustomShow->SetName( aStr ); + bModified = true; + } +} + +// OK-Hdl +IMPL_LINK_NOARG(SdDefineCustomShowDlg, OKHdl, weld::Button&, void) +{ + // check name... + bool bDifferent = true; + SdCustomShowList* pCustomShowList = rDoc.GetCustomShowList(); + if( pCustomShowList ) + { + OUString aName( m_xEdtName->get_text() ); + SdCustomShow* pCustomShow; + + tools::Long nPosToSelect = pCustomShowList->GetCurPos(); + for( pCustomShow = pCustomShowList->First(); + pCustomShow != nullptr; + pCustomShow = pCustomShowList->Next() ) + { + if( aName == pCustomShow->GetName() && aName != aOldName ) + bDifferent = false; + } + pCustomShowList->Seek( nPosToSelect ); + } + + if( bDifferent ) + { + CheckCustomShow(); + + m_xDialog->response(RET_OK); + } + else + { + std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Warning, VclButtonsType::Ok, + SdResId(STR_WARN_NAME_DUPLICATE))); + xWarn->run(); + m_xEdtName->grab_focus(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/diactrl.cxx b/sd/source/ui/dlg/diactrl.cxx new file mode 100644 index 000000000..233550809 --- /dev/null +++ b/sd/source/ui/dlg/diactrl.cxx @@ -0,0 +1,185 @@ +/* -*- 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 <sal/config.h> + +#include <comphelper/propertyvalue.hxx> +#include <vcl/fieldvalues.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <vcl/toolbox.hxx> +#include <svl/intitem.hxx> + +#include <strings.hrc> + +#include <diactrl.hxx> + +#include <sdresid.hxx> +#include <app.hrc> + +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/frame/XFrame.hpp> + +using namespace ::com::sun::star; + +SFX_IMPL_TOOLBOX_CONTROL( SdTbxCtlDiaPages, SfxUInt16Item ) + +namespace +{ + OUString format_number(int nSlides) + { + OUString aSlides(SdResId(STR_SLIDES, nSlides)); + return aSlides.replaceFirst("%1", OUString::number(nSlides)); + } +} + +// SdPagesField +SdPagesField::SdPagesField( vcl::Window* pParent, + const uno::Reference< frame::XFrame >& rFrame ) + : InterimItemWindow(pParent, "modules/simpress/ui/pagesfieldbox.ui", "PagesFieldBox") + , m_xWidget(m_xBuilder->weld_spin_button("pagesfield")) + , m_xFrame(rFrame) +{ + InitControlBase(m_xWidget.get()); + + // set parameter of MetricFields + m_xWidget->set_digits(0); + m_xWidget->set_range(1, 15); + m_xWidget->set_increments(1, 5); + m_xWidget->connect_value_changed(LINK(this, SdPagesField, ModifyHdl)); + m_xWidget->connect_output(LINK(this, SdPagesField, OutputHdl)); + m_xWidget->connect_input(LINK(this, SdPagesField, spin_button_input)); + m_xWidget->connect_key_press(LINK(this, SdPagesField, KeyInputHdl)); + + auto width = std::max(m_xWidget->get_pixel_size(format_number(1)).Width(), + m_xWidget->get_pixel_size(format_number(15)).Width()); + int chars = ceil(width / m_xWidget->get_approximate_digit_width()); + m_xWidget->set_width_chars(chars); + + SetSizePixel(m_xWidget->get_preferred_size()); +} + +IMPL_LINK(SdPagesField, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + return ChildKeyInput(rKEvt); +} + +void SdPagesField::dispose() +{ + m_xWidget.reset(); + InterimItemWindow::dispose(); +} + +SdPagesField::~SdPagesField() +{ + disposeOnce(); +} + +void SdPagesField::set_sensitive(bool bSensitive) +{ + Enable(bSensitive); + m_xWidget->set_sensitive(bSensitive); + if (!bSensitive) + m_xWidget->set_text(""); +} + +void SdPagesField::UpdatePagesField( const SfxUInt16Item* pItem ) +{ + if (pItem) + m_xWidget->set_value(pItem->GetValue()); + else + m_xWidget->set_text(OUString()); +} + +IMPL_STATIC_LINK(SdPagesField, OutputHdl, weld::SpinButton&, rSpinButton, void) +{ + rSpinButton.set_text(format_number(rSpinButton.get_value())); +} + +IMPL_LINK(SdPagesField, spin_button_input, int*, result, bool) +{ + const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper(); + double fResult(0.0); + bool bRet = vcl::TextToValue(m_xWidget->get_text(), fResult, 0, m_xWidget->get_digits(), rLocaleData, FieldUnit::NONE); + if (bRet) + { + if (fResult > SAL_MAX_INT32) + fResult = SAL_MAX_INT32; + else if (fResult < SAL_MIN_INT32) + fResult = SAL_MIN_INT32; + *result = fResult; + } + return bRet; +} + +IMPL_LINK_NOARG(SdPagesField, ModifyHdl, weld::SpinButton&, void) +{ + SfxUInt16Item aItem(SID_PAGES_PER_ROW, m_xWidget->get_value()); + + uno::Any a; + aItem.QueryValue( a ); + uno::Sequence< beans::PropertyValue > aArgs{ comphelper::makePropertyValue("PagesPerRow", a) }; + SfxToolBoxControl::Dispatch( ::uno::Reference< ::frame::XDispatchProvider >( m_xFrame->getController(), ::uno::UNO_QUERY ), + ".uno:PagesPerRow", + aArgs ); +} + +SdTbxCtlDiaPages::SdTbxCtlDiaPages( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx ) : + SfxToolBoxControl( nSlotId, nId, rTbx ) +{ +} + +SdTbxCtlDiaPages::~SdTbxCtlDiaPages() +{ +} + +void SdTbxCtlDiaPages::StateChangedAtToolBoxControl( sal_uInt16, + SfxItemState eState, const SfxPoolItem* pState ) +{ + SdPagesField* pFld = static_cast<SdPagesField*>( GetToolBox().GetItemWindow( GetId() ) ); + DBG_ASSERT( pFld, "Window not found" ); + + if ( eState == SfxItemState::DISABLED ) + { + pFld->set_sensitive(false); + } + else + { + pFld->set_sensitive(true); + + const SfxUInt16Item* pItem = nullptr; + if ( eState == SfxItemState::DEFAULT ) + { + pItem = dynamic_cast< const SfxUInt16Item* >( pState ); + DBG_ASSERT( pItem, "sd::SdTbxCtlDiaPages::StateChanged(), wrong item type!" ); + } + + pFld->UpdatePagesField( pItem ); + } +} + +VclPtr<InterimItemWindow> SdTbxCtlDiaPages::CreateItemWindow( vcl::Window* pParent ) +{ + VclPtr<SdPagesField> pWindow = VclPtr<SdPagesField>::Create(pParent, m_xFrame); + pWindow->Show(); + + return pWindow; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/dlgchar.cxx b/sd/source/ui/dlg/dlgchar.cxx new file mode 100644 index 000000000..df1b24179 --- /dev/null +++ b/sd/source/ui/dlg/dlgchar.cxx @@ -0,0 +1,70 @@ +/* -*- 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 <svx/dialogs.hrc> +#include <editeng/flstitem.hxx> +#include <svx/flagsdef.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/sfxdlg.hxx> + +#include <dlg_char.hxx> +#include <svx/svxids.hrc> +#include <svl/intitem.hxx> + +/** + * Constructor of tab dialog: append pages to dialog + */ +SdCharDlg::SdCharDlg(weld::Window* pParent, const SfxItemSet* pAttr, + const SfxObjectShell* pDocShell) + : SfxTabDialogController(pParent, "modules/sdraw/ui/drawchardialog.ui", + "DrawCharDialog", pAttr) + , rDocShell(*pDocShell) +{ + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + + AddTabPage("RID_SVXPAGE_CHAR_NAME", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_CHAR_NAME), nullptr); + AddTabPage("RID_SVXPAGE_CHAR_EFFECTS", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_CHAR_EFFECTS), nullptr); + AddTabPage("RID_SVXPAGE_CHAR_POSITION", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_CHAR_POSITION), nullptr); + AddTabPage("RID_SVXPAGE_BKG", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_BKG), nullptr); +} + +void SdCharDlg::PageCreated(const OString& rId, SfxTabPage &rPage) +{ + SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool())); + if (rId == "RID_SVXPAGE_CHAR_NAME") + { + SvxFontListItem aItem(* static_cast<const SvxFontListItem*>( rDocShell.GetItem( SID_ATTR_CHAR_FONTLIST) ) ); + + aSet.Put (SvxFontListItem( aItem.GetFontList(), SID_ATTR_CHAR_FONTLIST)); + rPage.PageCreated(aSet); + } + else if (rId == "RID_SVXPAGE_CHAR_EFFECTS") + { + // Opt in for character transparency. + aSet.Put(SfxUInt32Item(SID_FLAG_TYPE, SVX_ENABLE_CHAR_TRANSPARENCY)); + rPage.PageCreated(aSet); + } + else if (rId == "RID_SVXPAGE_BKG") + { + aSet.Put(SfxUInt32Item(SID_FLAG_TYPE,static_cast<sal_uInt32>(SvxBackgroundTabFlags::SHOW_CHAR_BKGCOLOR))); + rPage.PageCreated(aSet); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/dlgfield.cxx b/sd/source/ui/dlg/dlgfield.cxx new file mode 100644 index 000000000..75263a17c --- /dev/null +++ b/sd/source/ui/dlg/dlgfield.cxx @@ -0,0 +1,301 @@ +/* -*- 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 <editeng/eeitem.hxx> +#include <editeng/flditem.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/docfile.hxx> +#include <svl/itemset.hxx> +#include <svx/langbox.hxx> +#include <editeng/langitem.hxx> +#include <unotools/useroptions.hxx> + +#include <strings.hrc> +#include <sdresid.hxx> +#include <sdmod.hxx> +#include <dlgfield.hxx> +#include <DrawDocShell.hxx> + +/** + * dialog to edit field commands + */ +SdModifyFieldDlg::SdModifyFieldDlg(weld::Window* pWindow, const SvxFieldData* pInField, const SfxItemSet& rSet) + : GenericDialogController(pWindow, "modules/simpress/ui/dlgfield.ui", "EditFieldsDialog") + , m_aInputSet(rSet) + , m_pField(pInField) + , m_xRbtFix(m_xBuilder->weld_radio_button("fixedRB")) + , m_xRbtVar(m_xBuilder->weld_radio_button("varRB")) + , m_xLbLanguage(new SvxLanguageBox(m_xBuilder->weld_combo_box("languageLB"))) + , m_xLbFormat(m_xBuilder->weld_combo_box("formatLB")) +{ + m_xLbLanguage->SetLanguageList( SvxLanguageListFlags::ALL|SvxLanguageListFlags::ONLY_KNOWN, false ); + m_xLbLanguage->connect_changed(LINK(this, SdModifyFieldDlg, LanguageChangeHdl)); + FillControls(); +} + +SdModifyFieldDlg::~SdModifyFieldDlg() +{ +} + +/** + * Returns the new field, owned by caller. + * Returns NULL if nothing has changed. + */ +SvxFieldData* SdModifyFieldDlg::GetField() +{ + SvxFieldData* pNewField = nullptr; + + if( m_xRbtFix->get_state_changed_from_saved() || + m_xRbtVar->get_state_changed_from_saved() || + m_xLbFormat->get_value_changed_from_saved() ) + { + if( auto pDateField = dynamic_cast< const SvxDateField *>( m_pField ) ) + { + SvxDateType eType; + SvxDateFormat eFormat; + + if( m_xRbtFix->get_active() ) + eType = SvxDateType::Fix; + else + eType = SvxDateType::Var; + + eFormat = static_cast<SvxDateFormat>( m_xLbFormat->get_active() + 2 ); + + pNewField = new SvxDateField( *pDateField ); + static_cast<SvxDateField*>( pNewField )->SetType( eType ); + static_cast<SvxDateField*>( pNewField )->SetFormat( eFormat ); + } + else if( auto pTimeField = dynamic_cast< const SvxExtTimeField *>( m_pField ) ) + { + SvxTimeType eType; + SvxTimeFormat eFormat; + + if( m_xRbtFix->get_active() ) + eType = SvxTimeType::Fix; + else + eType = SvxTimeType::Var; + + eFormat = static_cast<SvxTimeFormat>( m_xLbFormat->get_active() + 2 ); + + pNewField = new SvxExtTimeField( *pTimeField ); + static_cast<SvxExtTimeField*>( pNewField )->SetType( eType ); + static_cast<SvxExtTimeField*>( pNewField )->SetFormat( eFormat ); + } + else if( dynamic_cast< const SvxExtFileField *>( m_pField ) != nullptr ) + { + SvxFileType eType; + SvxFileFormat eFormat; + + if( m_xRbtFix->get_active() ) + eType = SvxFileType::Fix; + else + eType = SvxFileType::Var; + + eFormat = static_cast<SvxFileFormat>( m_xLbFormat->get_active() ); + + ::sd::DrawDocShell* pDocSh = dynamic_cast< ::sd::DrawDocShell* >(SfxObjectShell::Current() ); + + if( pDocSh ) + { + OUString aName; + if( pDocSh->HasName() ) + aName = pDocSh->GetMedium()->GetName(); + + // Get current filename, not the one stored in the old field + pNewField = new SvxExtFileField( aName ); + static_cast<SvxExtFileField*>( pNewField )->SetType( eType ); + static_cast<SvxExtFileField*>( pNewField )->SetFormat( eFormat ); + } + } + else if( dynamic_cast< const SvxAuthorField *>( m_pField ) != nullptr ) + { + SvxAuthorType eType; + SvxAuthorFormat eFormat; + + if( m_xRbtFix->get_active() ) + eType = SvxAuthorType::Fix; + else + eType = SvxAuthorType::Var; + + eFormat = static_cast<SvxAuthorFormat>( m_xLbFormat->get_active() ); + + // Get current state of address, not the old one + SvtUserOptions aUserOptions; + pNewField = new SvxAuthorField( aUserOptions.GetFirstName(), aUserOptions.GetLastName(), aUserOptions.GetID() ); + static_cast<SvxAuthorField*>( pNewField )->SetType( eType ); + static_cast<SvxAuthorField*>( pNewField )->SetFormat( eFormat ); + } + } + + return pNewField; +} + +void SdModifyFieldDlg::FillFormatList() +{ + LanguageType eLangType = m_xLbLanguage->get_active_id(); + + m_xLbFormat->clear(); + + if( auto pDateField = dynamic_cast< const SvxDateField *>( m_pField ) ) + { + SvxDateField aDateField( *pDateField ); + + //SvxDateFormat::AppDefault, // not used + //SvxDateFormat::System, // not used + m_xLbFormat->append_text( SdResId( STR_STANDARD_SMALL ) ); + m_xLbFormat->append_text( SdResId( STR_STANDARD_BIG ) ); + + SvNumberFormatter* pNumberFormatter = SD_MOD()->GetNumberFormatter(); + aDateField.SetFormat( SvxDateFormat::A ); // 13.02.96 + m_xLbFormat->append_text( aDateField.GetFormatted( *pNumberFormatter, eLangType ) ); + aDateField.SetFormat( SvxDateFormat::B ); // 13.02.1996 + m_xLbFormat->append_text( aDateField.GetFormatted( *pNumberFormatter, eLangType ) ); + aDateField.SetFormat( SvxDateFormat::C ); // 13.Feb 1996 + m_xLbFormat->append_text( aDateField.GetFormatted( *pNumberFormatter, eLangType ) ); + aDateField.SetFormat( SvxDateFormat::D ); // 13.Februar 1996 + m_xLbFormat->append_text( aDateField.GetFormatted( *pNumberFormatter, eLangType ) ); + aDateField.SetFormat( SvxDateFormat::E ); // Die, 13.Februar 1996 + m_xLbFormat->append_text( aDateField.GetFormatted( *pNumberFormatter, eLangType ) ); + aDateField.SetFormat( SvxDateFormat::F ); // Dienstag, 13.Februar 1996 + m_xLbFormat->append_text( aDateField.GetFormatted( *pNumberFormatter, eLangType ) ); + + m_xLbFormat->set_active( static_cast<sal_uInt16>(pDateField->GetFormat()) - 2 ); + } + else if( auto pTimeField = dynamic_cast< const SvxExtTimeField *>( m_pField ) ) + { + SvxExtTimeField aTimeField( *pTimeField ); + + //SvxTimeFormat::AppDefault, // not used + //SvxTimeFormat::System, // not used + m_xLbFormat->append_text( SdResId( STR_STANDARD_NORMAL ) ); + + SvNumberFormatter* pNumberFormatter = SD_MOD()->GetNumberFormatter(); + aTimeField.SetFormat( SvxTimeFormat::HH24_MM ); // 13:49 + m_xLbFormat->append_text( aTimeField.GetFormatted( *pNumberFormatter, eLangType ) ); + aTimeField.SetFormat( SvxTimeFormat::HH24_MM_SS ); // 13:49:38 + m_xLbFormat->append_text( aTimeField.GetFormatted( *pNumberFormatter, eLangType ) ); + aTimeField.SetFormat( SvxTimeFormat::HH24_MM_SS_00 ); // 13:49:38.78 + m_xLbFormat->append_text( aTimeField.GetFormatted( *pNumberFormatter, eLangType ) ); + aTimeField.SetFormat( SvxTimeFormat::HH12_MM ); // 01:49 + m_xLbFormat->append_text( aTimeField.GetFormatted( *pNumberFormatter, eLangType ) ); + aTimeField.SetFormat( SvxTimeFormat::HH12_MM_SS ); // 01:49:38 + m_xLbFormat->append_text( aTimeField.GetFormatted( *pNumberFormatter, eLangType ) ); + aTimeField.SetFormat( SvxTimeFormat::HH12_MM_SS_00 ); // 01:49:38.78 + m_xLbFormat->append_text( aTimeField.GetFormatted( *pNumberFormatter, eLangType ) ); + //SvxTimeFormat::HH12_MM_AMPM, // 01:49 PM + //SvxTimeFormat::HH12_MM_SS_AMPM, // 01:49:38 PM + //SvxTimeFormat::HH12_MM_SS_00_AMPM // 01:49:38.78 PM + + m_xLbFormat->set_active( static_cast<sal_uInt16>(pTimeField->GetFormat()) - 2 ); + } + else if( auto pFileField = dynamic_cast< const SvxExtFileField *>( m_pField ) ) + { + m_xLbFormat->append_text( SdResId( STR_FILEFORMAT_NAME_EXT ) ); + m_xLbFormat->append_text( SdResId( STR_FILEFORMAT_FULLPATH ) ); + m_xLbFormat->append_text( SdResId( STR_FILEFORMAT_PATH ) ); + m_xLbFormat->append_text( SdResId( STR_FILEFORMAT_NAME ) ); + + m_xLbFormat->set_active( static_cast<sal_uInt16>( pFileField->GetFormat() ) ); + } + else if( auto pAuthorField = dynamic_cast< const SvxAuthorField *>( m_pField ) ) + { + SvxAuthorField aAuthorField( *pAuthorField ); + + for( sal_uInt16 i = 0; i < 4; i++ ) + { + aAuthorField.SetFormat( static_cast<SvxAuthorFormat>(i) ); + m_xLbFormat->append_text( aAuthorField.GetFormatted() ); + } + + m_xLbFormat->set_active( static_cast<sal_uInt16>( pAuthorField->GetFormat() ) ); + + } + +} + +void SdModifyFieldDlg::FillControls() +{ + m_xLbFormat->clear(); + + if( auto pDateField = dynamic_cast< const SvxDateField *>( m_pField ) ) + { + if( pDateField->GetType() == SvxDateType::Fix ) + m_xRbtFix->set_active(true); + else + m_xRbtVar->set_active(true); + } + else if( auto pTimeField = dynamic_cast< const SvxExtTimeField *>( m_pField ) ) + { + if( pTimeField->GetType() == SvxTimeType::Fix ) + m_xRbtFix->set_active(true); + else + m_xRbtVar->set_active(true); + } + else if( auto pFileField = dynamic_cast< const SvxExtFileField *>( m_pField ) ) + { + if( pFileField->GetType() == SvxFileType::Fix ) + m_xRbtFix->set_active(true); + else + m_xRbtVar->set_active(true); + } + else if( auto pAuthorField = dynamic_cast< const SvxAuthorField *>( m_pField ) ) + { + if( pAuthorField->GetType() == SvxAuthorType::Fix ) + m_xRbtFix->set_active(true); + else + m_xRbtVar->set_active(true); + } + m_xRbtFix->save_state(); + m_xRbtVar->save_state(); + + if( const SvxLanguageItem* pItem = m_aInputSet.GetItemIfSet(EE_CHAR_LANGUAGE ) ) + m_xLbLanguage->set_active_id(pItem->GetLanguage()); + + m_xLbLanguage->save_active_id(); + + FillFormatList(); + m_xLbFormat->save_value(); +} + +IMPL_LINK_NOARG(SdModifyFieldDlg, LanguageChangeHdl, weld::ComboBox&, void) +{ + FillFormatList(); +} + +SfxItemSet SdModifyFieldDlg::GetItemSet() const +{ + SfxItemSet aOutput( *m_aInputSet.GetPool(), svl::Items<EE_CHAR_LANGUAGE, EE_CHAR_LANGUAGE_CTL> ); + + if (m_xLbLanguage->get_active_id_changed_from_saved()) + { + LanguageType eLangType = m_xLbLanguage->get_active_id(); + SvxLanguageItem aItem( eLangType, EE_CHAR_LANGUAGE ); + aOutput.Put( aItem ); + + SvxLanguageItem aItemCJK( eLangType, EE_CHAR_LANGUAGE_CJK ); + aOutput.Put( aItemCJK ); + + SvxLanguageItem aItemCTL( eLangType, EE_CHAR_LANGUAGE_CTL ); + aOutput.Put( aItemCTL ); + } + + return aOutput; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/dlgolbul.cxx b/sd/source/ui/dlg/dlgolbul.cxx new file mode 100644 index 000000000..41c00efa8 --- /dev/null +++ b/sd/source/ui/dlg/dlgolbul.cxx @@ -0,0 +1,172 @@ +/* -*- 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 <memory> +#include <OutlineBulletDlg.hxx> + +#include <svx/svxids.hrc> +#include <editeng/eeitem.hxx> + +#include <editeng/numitem.hxx> + +#include <tools/debug.hxx> +#include <svx/dialogs.hrc> +#include <svx/svdmark.hxx> +#include <View.hxx> +#include <svx/svdobj.hxx> +#include <svl/style.hxx> +#include <svl/intitem.hxx> +#include <drawdoc.hxx> + +#include <strings.hxx> +#include <bulmaper.hxx> +#include <DrawDocShell.hxx> + +namespace sd { + +/** + * Constructor of tab dialog: append pages to the dialog + */ +OutlineBulletDlg::OutlineBulletDlg(weld::Window* pParent, const SfxItemSet* pAttr, ::sd::View* pView) + : SfxTabDialogController(pParent, "modules/sdraw/ui/bulletsandnumbering.ui", "BulletsAndNumberingDialog") + , m_aInputSet(*pAttr) + , m_bTitle(false) + , m_pSdView(pView) +{ + m_aInputSet.MergeRange(SID_PARAM_NUM_PRESET, SID_PARAM_CUR_NUM_LEVEL); + m_aInputSet.Put(*pAttr); + + m_xOutputSet.reset( new SfxItemSet( *pAttr ) ); + m_xOutputSet->ClearItem(); + + bool bOutliner = false; + + // special treatment if a title object is selected + if (pView) + { + const SdrMarkList& rMarkList = pView->GetMarkedObjectList(); + const size_t nCount = rMarkList.GetMarkCount(); + for(size_t nNum = 0; nNum < nCount; ++nNum) + { + SdrObject* pObj = rMarkList.GetMark(nNum)->GetMarkedSdrObj(); + if( pObj->GetObjInventor() == SdrInventor::Default ) + { + switch(pObj->GetObjIdentifier()) + { + case SdrObjKind::TitleText: + m_bTitle = true; + break; + case SdrObjKind::OutlineText: + bOutliner = true; + break; + default: + break; + } + } + } + } + + if( SfxItemState::SET != m_aInputSet.GetItemState(EE_PARA_NUMBULLET)) + { + const SvxNumBulletItem *pItem = nullptr; + if(bOutliner) + { + SfxStyleSheetBasePool* pSSPool = pView->GetDocSh()->GetStyleSheetPool(); + SfxStyleSheetBase* pFirstStyleSheet = pSSPool->Find( STR_LAYOUT_OUTLINE + " 1", SfxStyleFamily::Pseudo); + if( pFirstStyleSheet ) + pItem = pFirstStyleSheet->GetItemSet().GetItemIfSet(EE_PARA_NUMBULLET, false); + } + + if( pItem == nullptr ) + pItem = m_aInputSet.GetPool()->GetSecondaryPool()->GetPoolDefaultItem(EE_PARA_NUMBULLET); + + DBG_ASSERT( pItem, "No EE_PARA_NUMBULLET in Pool! [CL]" ); + + m_aInputSet.Put(pItem->CloneSetWhich(EE_PARA_NUMBULLET)); + } + + if (m_bTitle && m_aInputSet.GetItemState(EE_PARA_NUMBULLET) == SfxItemState::SET ) + { + const SvxNumBulletItem* pItem = m_aInputSet.GetItem<SvxNumBulletItem>(EE_PARA_NUMBULLET); + const SvxNumRule& rRule = pItem->GetNumRule(); + SvxNumRule aNewRule( rRule ); + aNewRule.SetFeatureFlag( SvxNumRuleFlags::NO_NUMBERS ); + + SvxNumBulletItem aNewItem( std::move(aNewRule), EE_PARA_NUMBULLET ); + m_aInputSet.Put(aNewItem); + } + + SetInputSet(&m_aInputSet); + + if (m_bTitle) + RemoveTabPage("singlenum"); + + AddTabPage("customize", RID_SVXPAGE_NUM_OPTIONS); + AddTabPage("position", RID_SVXPAGE_NUM_POSITION); +} + +OutlineBulletDlg::~OutlineBulletDlg() +{ +} + +void OutlineBulletDlg::PageCreated(const OString& rId, SfxTabPage &rPage) +{ + if (!m_pSdView) + return; + if (rId == "customize") + { + FieldUnit eMetric = m_pSdView->GetDoc().GetUIUnit(); + SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool())); + aSet.Put ( SfxUInt16Item(SID_METRIC_ITEM,static_cast<sal_uInt16>(eMetric))); + rPage.PageCreated(aSet); + } + else if (rId == "position") + { + FieldUnit eMetric = m_pSdView->GetDoc().GetUIUnit(); + SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool())); + aSet.Put ( SfxUInt16Item(SID_METRIC_ITEM,static_cast<sal_uInt16>(eMetric))); + rPage.PageCreated(aSet); + } +} + +const SfxItemSet* OutlineBulletDlg::GetBulletOutputItemSet() const +{ + SfxItemSet aSet(*GetOutputItemSet()); + m_xOutputSet->Put(aSet); + + const SfxPoolItem *pItem = nullptr; + if( SfxItemState::SET == m_xOutputSet->GetItemState(m_xOutputSet->GetPool()->GetWhich(SID_ATTR_NUMBERING_RULE), false, &pItem )) + { + SdBulletMapper::MapFontsInNumRule(const_cast<SvxNumRule&>(static_cast<const SvxNumBulletItem*>(pItem)->GetNumRule()), *m_xOutputSet); + // #i35937 - removed EE_PARA_BULLETSTATE setting + } + + if (m_bTitle && m_xOutputSet->GetItemState(EE_PARA_NUMBULLET) == SfxItemState::SET) + { + const SvxNumBulletItem* pBulletItem = m_xOutputSet->GetItem<SvxNumBulletItem>(EE_PARA_NUMBULLET); + SvxNumRule& rRule = const_cast<SvxNumRule&>(pBulletItem->GetNumRule()); + rRule.SetFeatureFlag( SvxNumRuleFlags::NO_NUMBERS, false ); + } + + return m_xOutputSet.get(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/dlgpage.cxx b/sd/source/ui/dlg/dlgpage.cxx new file mode 100644 index 000000000..e3bc5978b --- /dev/null +++ b/sd/source/ui/dlg/dlgpage.cxx @@ -0,0 +1,116 @@ +/* -*- 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 <svl/intitem.hxx> +#include <svx/dialogs.hrc> +#include <svx/svxids.hrc> +#include <svx/drawitem.hxx> +#include <i18nutil/paper.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/sfxdlg.hxx> + +#include <dlgpage.hxx> +#include <sdresid.hxx> +#include <strings.hrc> + +#include <svl/eitem.hxx> +#include <svx/flagsdef.hxx> + +/** + * Constructor of tab dialog: appends pages to the dialog + */ +SdPageDlg::SdPageDlg(SfxObjectShell const* pDocSh, weld::Window* pParent, const SfxItemSet* pAttr, + bool bAreaPage, bool bIsImpressDoc, bool bIsImpressMaster) + : SfxTabDialogController(pParent, "modules/sdraw/ui/drawpagedialog.ui", "DrawPageDialog", pAttr) + , mbIsImpressDoc(bIsImpressDoc) +{ + SvxColorListItem const* pColorListItem = pDocSh->GetItem(SID_COLOR_TABLE); + SvxGradientListItem const* pGradientListItem = pDocSh->GetItem(SID_GRADIENT_LIST); + SvxBitmapListItem const* pBitmapListItem = pDocSh->GetItem(SID_BITMAP_LIST); + SvxPatternListItem const* pPatternListItem = pDocSh->GetItem(SID_PATTERN_LIST); + SvxHatchListItem const* pHatchListItem = pDocSh->GetItem(SID_HATCH_LIST); + + mpColorList = pColorListItem->GetColorList(); + mpGradientList = pGradientListItem->GetGradientList(); + mpHatchingList = pHatchListItem->GetHatchList(); + mpBitmapList = pBitmapListItem->GetBitmapList(); + mpPatternList = pPatternListItem->GetPatternList(); + + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + + AddTabPage("RID_SVXPAGE_PAGE", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_PAGE), nullptr); + AddTabPage("RID_SVXPAGE_AREA", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_AREA), nullptr); + AddTabPage("RID_SVXPAGE_TRANSPARENCE", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_TRANSPARENCE), + nullptr); + AddTabPage("RID_SVXPAGE_THEME", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_THEME), nullptr); + + if (!bAreaPage) // I have to add the page before I remove it ! + { + RemoveTabPage("RID_SVXPAGE_AREA"); + RemoveTabPage("RID_SVXPAGE_TRANSPARENCE"); + } + + if (!bIsImpressMaster) + { + // Only slide masters can have a theme. + RemoveTabPage("RID_SVXPAGE_THEME"); + } + + if (mbIsImpressDoc) + { + set_title(SdResId(STR_SLIDE_SETUP_TITLE)); + m_xTabCtrl->set_tab_label_text("RID_SVXPAGE_PAGE", SdResId(STR_SLIDE_NAME)); + } +} + +void SdPageDlg::PageCreated(const OString& rId, SfxTabPage& rPage) +{ + SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool())); + if (rId == "RID_SVXPAGE_PAGE") + { + aSet.Put(SfxUInt16Item(sal_uInt16(SID_ENUM_PAGE_MODE), SVX_PAGE_MODE_PRESENTATION)); + aSet.Put(SfxUInt16Item(SID_PAPER_START, PAPER_A0)); + aSet.Put(SfxUInt16Item(SID_PAPER_END, PAPER_E)); + + if (mbIsImpressDoc) + aSet.Put(SfxBoolItem(SID_IMPRESS_DOC, true)); + + rPage.PageCreated(aSet); + } + else if (rId == "RID_SVXPAGE_AREA") + { + aSet.Put(SvxColorListItem(mpColorList, SID_COLOR_TABLE)); + aSet.Put(SvxGradientListItem(mpGradientList, SID_GRADIENT_LIST)); + aSet.Put(SvxHatchListItem(mpHatchingList, SID_HATCH_LIST)); + aSet.Put(SvxBitmapListItem(mpBitmapList, SID_BITMAP_LIST)); + aSet.Put(SvxPatternListItem(mpPatternList, SID_PATTERN_LIST)); + aSet.Put(SfxUInt16Item(SID_PAGE_TYPE, 0)); + aSet.Put(SfxUInt16Item(SID_DLG_TYPE, 1)); + aSet.Put(SfxUInt16Item(SID_TABPAGE_POS, 0)); + rPage.PageCreated(aSet); + } + else if (rId == "RID_SVXPAGE_TRANSPARENCE") + { + aSet.Put(SfxUInt16Item(SID_PAGE_TYPE, 0)); + aSet.Put(SfxUInt16Item(SID_DLG_TYPE, 1)); + rPage.PageCreated(aSet); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/dlgsnap.cxx b/sd/source/ui/dlg/dlgsnap.cxx new file mode 100644 index 000000000..9b1383089 --- /dev/null +++ b/sd/source/ui/dlg/dlgsnap.cxx @@ -0,0 +1,185 @@ +/* -*- 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 <svx/svxids.hrc> +#include <svx/svdpagv.hxx> +#include <svl/intitem.hxx> +#include <svl/itempool.hxx> +#include <svtools/unitconv.hxx> +#include <tools/debug.hxx> + +#include <sdattr.hrc> +#include <View.hxx> +#include <drawdoc.hxx> +#include <dlgsnap.hxx> +#include <sdenumdef.hxx> + +/** + * dialog to adjust grid (scarcely ESO!) + */ +SdSnapLineDlg::SdSnapLineDlg(weld::Window* pWindow, const SfxItemSet& rInAttrs, ::sd::View const * pView) + : GenericDialogController(pWindow, "modules/sdraw/ui/dlgsnap.ui", "SnapObjectDialog") + , aUIScale(pView->GetDoc().GetUIScale()) + , m_xFtX(m_xBuilder->weld_label("xlabel")) + , m_xMtrFldX(m_xBuilder->weld_metric_spin_button("x", FieldUnit::CM)) + , m_xFtY(m_xBuilder->weld_label("ylabel")) + , m_xMtrFldY(m_xBuilder->weld_metric_spin_button("y", FieldUnit::CM)) + , m_xRadioGroup(m_xBuilder->weld_widget("radiogroup")) + , m_xRbPoint(m_xBuilder->weld_radio_button("point")) + , m_xRbVert(m_xBuilder->weld_radio_button("vert")) + , m_xRbHorz(m_xBuilder->weld_radio_button("horz")) + , m_xBtnDelete(m_xBuilder->weld_button("delete")) +{ + m_xRbHorz->connect_toggled(LINK(this, SdSnapLineDlg, ToggleHdl)); + m_xRbVert->connect_toggled(LINK(this, SdSnapLineDlg, ToggleHdl)); + m_xRbPoint->connect_toggled(LINK(this, SdSnapLineDlg, ToggleHdl)); + + m_xBtnDelete->connect_clicked(LINK(this, SdSnapLineDlg, ClickHdl)); + + FieldUnit eUIUnit = pView->GetDoc().GetUIUnit(); + SetFieldUnit(*m_xMtrFldX, eUIUnit, true); + SetFieldUnit(*m_xMtrFldY, eUIUnit, true); + + // get WorkArea + ::tools::Rectangle aWorkArea = pView->GetWorkArea(); + + // determine PoolUnit + SfxItemPool* pPool = rInAttrs.GetPool(); + DBG_ASSERT( pPool, "Where's the Pool?" ); + MapUnit ePoolUnit = pPool->GetMetric( SID_ATTR_FILL_HATCH ); + + // #i48497# Consider page origin + SdrPageView* pPV = pView->GetSdrPageView(); + Point aLeftTop(aWorkArea.Left()+1, aWorkArea.Top()+1); + pPV->LogicToPagePos(aLeftTop); + Point aRightBottom(aWorkArea.Right()-2, aWorkArea.Bottom()-2); + pPV->LogicToPagePos(aRightBottom); + + // determine max and min values depending on + // WorkArea, PoolUnit and FieldUnit: + auto const map = [ePoolUnit](std::unique_ptr<weld::MetricSpinButton> const & msb, tools::Long value) { + auto const n1 = OutputDevice::LogicToLogic(value, ePoolUnit, MapUnit::Map100thMM); + auto const n2 = msb->normalize(n1); + auto const n3 = msb->convert_value_from(n2, FieldUnit::MM_100TH); + auto const n4 = msb->convert_value_to(n3, FieldUnit::NONE); + return n4; + }; + m_xMtrFldX->set_range(map(m_xMtrFldX, sal_Int32(aLeftTop.X() / aUIScale)), + map(m_xMtrFldX, sal_Int32(aRightBottom.X() / aUIScale)), + FieldUnit::NONE); + m_xMtrFldY->set_range(map(m_xMtrFldY, sal_Int32(aLeftTop.Y() / aUIScale)), + map(m_xMtrFldY, sal_Int32(aRightBottom.Y() / aUIScale)), + FieldUnit::NONE); + + // set values + nXValue = static_cast<const SfxInt32Item&>( rInAttrs.Get(ATTR_SNAPLINE_X)).GetValue(); + nYValue = static_cast<const SfxInt32Item&>( rInAttrs.Get(ATTR_SNAPLINE_Y)).GetValue(); + nXValue = sal_Int32(nXValue / aUIScale); + nYValue = sal_Int32(nYValue / aUIScale); + SetMetricValue(*m_xMtrFldX, nXValue, MapUnit::Map100thMM); + SetMetricValue(*m_xMtrFldY, nYValue, MapUnit::Map100thMM); + + m_xRbPoint->set_active(true); +} + +SdSnapLineDlg::~SdSnapLineDlg() +{ +} + +/** + * fills provided item sets with dialog box attributes + */ +IMPL_LINK(SdSnapLineDlg, ToggleHdl, weld::Toggleable&, rBtn, void) +{ + if (!rBtn.get_active()) + return; + if (m_xRbPoint->get_active()) + SetInputFields(true, true); + else if (m_xRbHorz->get_active()) + SetInputFields(false, true); + else if (m_xRbVert->get_active()) + SetInputFields(true, false); +} + +IMPL_LINK( SdSnapLineDlg, ClickHdl, weld::Button&, rBtn, void ) +{ + if (&rBtn == m_xBtnDelete.get()) + m_xDialog->response(RET_SNAP_DELETE); +} + +/** + * fills provided item sets with dialog box attributes + */ +void SdSnapLineDlg::GetAttr(SfxItemSet& rOutAttrs) +{ + SnapKind eKind; + + if (m_xRbHorz->get_active()) eKind = SnapKind::Horizontal; + else if (m_xRbVert->get_active()) eKind = SnapKind::Vertical; + else eKind = SnapKind::Point; + + nXValue = sal_Int32(GetCoreValue(*m_xMtrFldX, MapUnit::Map100thMM) * aUIScale); + nYValue = sal_Int32(GetCoreValue(*m_xMtrFldY, MapUnit::Map100thMM) * aUIScale); + + rOutAttrs.Put(SfxUInt16Item(ATTR_SNAPLINE_KIND, static_cast<sal_uInt16>(eKind))); + rOutAttrs.Put(SfxInt32Item(ATTR_SNAPLINE_X, nXValue)); + rOutAttrs.Put(SfxInt32Item(ATTR_SNAPLINE_Y, nYValue)); +} + +void SdSnapLineDlg::HideRadioGroup() +{ + m_xRadioGroup->hide(); +} + +/** + * disable X or Y input fields + */ +void SdSnapLineDlg::SetInputFields(bool bEnableX, bool bEnableY) +{ + if ( bEnableX ) + { + if (!m_xMtrFldX->get_sensitive()) + m_xMtrFldX->set_value(nXValue, FieldUnit::NONE); + m_xMtrFldX->set_sensitive(true); + m_xFtX->set_sensitive(true); + } + else if (m_xMtrFldX->get_sensitive()) + { + nXValue = m_xMtrFldX->get_value(FieldUnit::NONE); + m_xMtrFldX->set_text(OUString()); + m_xMtrFldX->set_sensitive(false); + m_xFtX->set_sensitive(false); + } + if ( bEnableY ) + { + if (!m_xMtrFldY->get_sensitive()) + m_xMtrFldY->set_value(nYValue, FieldUnit::NONE); + m_xMtrFldY->set_sensitive(true); + m_xFtY->set_sensitive(true); + } + else if (m_xMtrFldY->get_sensitive()) + { + nYValue = m_xMtrFldY->get_value(FieldUnit::NONE); + m_xMtrFldY->set_text(OUString()); + m_xMtrFldY->set_sensitive(false); + m_xFtY->set_sensitive(false); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/filedlg.cxx b/sd/source/ui/dlg/filedlg.cxx new file mode 100644 index 000000000..b2087c408 --- /dev/null +++ b/sd/source/ui/dlg/filedlg.cxx @@ -0,0 +1,267 @@ +/* -*- 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 <config_features.h> + +#include <com/sun/star/media/XPlayer.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#include <com/sun/star/ui/dialogs/XFilePicker3.hpp> +#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp> +#include <com/sun/star/ui/dialogs/FilePickerEvent.hpp> +#include <vcl/idle.hxx> +#include <osl/diagnose.h> +#include <vcl/svapp.hxx> +#include <sfx2/filedlghelper.hxx> +#include <avmedia/mediawindow.hxx> +#include <filedlg.hxx> +#include <sdresid.hxx> +#include <strings.hrc> + +// ----------- SdFileDialog_Imp --------------------------- + +class SdFileDialog_Imp : public sfx2::FileDialogHelper +{ +private: + friend class SdOpenSoundFileDialog; + + css::uno::Reference< css::ui::dialogs::XFilePickerControlAccess > mxControlAccess; + + css::uno::Reference< css::media::XPlayer > mxPlayer; + ImplSVEvent * mnPlaySoundEvent; + bool mbLabelPlaying; + Idle maUpdateIdle; + + DECL_LINK( PlayMusicHdl, void *, void ); + DECL_LINK( IsMusicStoppedHdl, Timer *, void ); + +public: + explicit SdFileDialog_Imp(weld::Window *pParent); + virtual ~SdFileDialog_Imp() override; + + // overwritten from FileDialogHelper, to receive user feedback + virtual void ControlStateChanged( const css::ui::dialogs::FilePickerEvent& aEvent ) override; +}; + +void SdFileDialog_Imp::ControlStateChanged( const css::ui::dialogs::FilePickerEvent& aEvent ) +{ + SolarMutexGuard aGuard; + + switch( aEvent.ElementId ) + { + case css::ui::dialogs::ExtendedFilePickerElementIds::PUSHBUTTON_PLAY: + if( mxControlAccess.is() ) + { + if( mnPlaySoundEvent ) + Application::RemoveUserEvent( mnPlaySoundEvent ); + + mnPlaySoundEvent = Application::PostUserEvent( LINK( this, SdFileDialog_Imp, PlayMusicHdl ) ); + } + break; + } +} + +IMPL_LINK_NOARG(SdFileDialog_Imp, PlayMusicHdl, void*, void) +{ + maUpdateIdle.Stop(); + mnPlaySoundEvent = nullptr; + + if (mxPlayer.is()) + { + if (mxPlayer->isPlaying()) + mxPlayer->stop(); + mxPlayer.clear(); + } + +#if HAVE_FEATURE_AVMEDIA + if( mbLabelPlaying ) + { + try + { + mxControlAccess->setLabel( css::ui::dialogs::ExtendedFilePickerElementIds::PUSHBUTTON_PLAY, + SdResId( STR_PLAY ) ); + mbLabelPlaying = false; + } + catch(const css::lang::IllegalArgumentException&) + { +#ifdef DBG_UTIL + OSL_FAIL( "Cannot access play button" ); +#endif + } + } + else + { + OUString aUrl( GetPath() ); + if ( !aUrl.isEmpty() ) + { + try + { + mxPlayer.set( avmedia::MediaWindow::createPlayer( aUrl, "" ), css::uno::UNO_SET_THROW ); + mxPlayer->start(); + maUpdateIdle.Start(); + } + catch (const css::uno::Exception&) + { + mxPlayer.clear(); + } + + if (mxPlayer.is()) + { + try + { + mxControlAccess->setLabel( css::ui::dialogs::ExtendedFilePickerElementIds::PUSHBUTTON_PLAY, + SdResId( STR_STOP ) ); + mbLabelPlaying = true; + } + catch (const css::lang::IllegalArgumentException&) + { +#ifdef DBG_UTIL + OSL_FAIL( "Cannot access play button" ); +#endif + } + } + } + } +#endif +} + +IMPL_LINK_NOARG(SdFileDialog_Imp, IsMusicStoppedHdl, Timer *, void) +{ + SolarMutexGuard aGuard; + + if (mxPlayer.is() && mxPlayer->isPlaying() && + mxPlayer->getMediaTime() < mxPlayer->getDuration()) + { + maUpdateIdle.Start(); + return; + } + + if( !mxControlAccess.is() ) + return; + + try + { + mxControlAccess->setLabel( css::ui::dialogs::ExtendedFilePickerElementIds::PUSHBUTTON_PLAY, + SdResId( STR_PLAY ) ); + mbLabelPlaying = false; + } + catch (const css::lang::IllegalArgumentException&) + { +#ifdef DBG_UTIL + OSL_FAIL( "Cannot access play button" ); +#endif + } +} + +SdFileDialog_Imp::SdFileDialog_Imp(weld::Window* pParent) + : FileDialogHelper(css::ui::dialogs::TemplateDescription::FILEOPEN_LINK_PLAY, FileDialogFlags::NONE, pParent) + , mnPlaySoundEvent(nullptr) + , mbLabelPlaying(false) + , maUpdateIdle( "SdFileDialog_Imp maUpdateIdle" ) +{ + maUpdateIdle.SetInvokeHandler(LINK(this, SdFileDialog_Imp, IsMusicStoppedHdl)); + + css::uno::Reference < css::ui::dialogs::XFilePicker3 > xFileDlg = GetFilePicker(); + + // get the control access + mxControlAccess.set( xFileDlg, css::uno::UNO_QUERY ); + + if( !mxControlAccess.is() ) + return; + + try + { + mxControlAccess->setLabel( css::ui::dialogs::ExtendedFilePickerElementIds::PUSHBUTTON_PLAY, + SdResId( STR_PLAY ) ); + } + catch (const css::lang::IllegalArgumentException&) + { +#ifdef DBG_UTIL + OSL_FAIL( "Cannot set play button label" ); +#endif + } +} + +SdFileDialog_Imp::~SdFileDialog_Imp() +{ + if( mnPlaySoundEvent ) + Application::RemoveUserEvent( mnPlaySoundEvent ); +} + +// ----------- SdOpenSoundFileDialog ----------------------- + +// these are simple forwarders +SdOpenSoundFileDialog::SdOpenSoundFileDialog(weld::Window *pParent) + : mpImpl(new SdFileDialog_Imp(pParent)) +{ + OUString aDescr = SdResId(STR_ALL_FILES); + mpImpl->AddFilter( aDescr, "*.*"); + mpImpl->SetContext(sfx2::FileDialogHelper::DrawImpressOpenSound); + + // setup filter +#if defined UNX + aDescr = SdResId(STR_AU_FILE); + mpImpl->AddFilter( aDescr, "*.au;*.snd"); + aDescr = SdResId(STR_VOC_FILE); + mpImpl->AddFilter( aDescr, "*.voc"); + aDescr = SdResId(STR_WAV_FILE); + mpImpl->AddFilter( aDescr, "*.wav"); + aDescr = SdResId(STR_AIFF_FILE); + mpImpl->AddFilter( aDescr, "*.aiff"); + aDescr = SdResId(STR_SVX_FILE); + mpImpl->AddFilter( aDescr, "*.svx"); +#else + aDescr = SdResId(STR_WAV_FILE); + mpImpl->AddFilter( aDescr, "*.wav;*.mp3;*.ogg" ); + aDescr = SdResId(STR_MIDI_FILE); + mpImpl->AddFilter( aDescr, "*.mid" ); +#endif +} + +SdOpenSoundFileDialog::~SdOpenSoundFileDialog() +{ +} + +ErrCode SdOpenSoundFileDialog::Execute() +{ + return mpImpl->Execute(); +} + +OUString SdOpenSoundFileDialog::GetPath() const +{ + return mpImpl->GetPath(); +} + +void SdOpenSoundFileDialog::SetPath( const OUString& rPath ) +{ + mpImpl->SetDisplayDirectory( rPath ); +} + +// WIP, please don't remove, dear Clang plugins +bool SdOpenSoundFileDialog::IsInsertAsLinkSelected() const +{ + bool bInsertAsLinkSelected = false; + css::uno::Reference<css::ui::dialogs::XFilePicker3> const xFilePicker(mpImpl->GetFilePicker()); + css::uno::Reference<css::ui::dialogs::XFilePickerControlAccess> const xControlAccess(xFilePicker, css::uno::UNO_QUERY_THROW); + xControlAccess->getValue(css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_LINK, 0) >>= bInsertAsLinkSelected; + return bInsertAsLinkSelected; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/gluectrl.cxx b/sd/source/ui/dlg/gluectrl.cxx new file mode 100644 index 000000000..a6baf3e92 --- /dev/null +++ b/sd/source/ui/dlg/gluectrl.cxx @@ -0,0 +1,200 @@ +/* -*- 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 <sal/config.h> + +#include <comphelper/propertyvalue.hxx> +#include <svx/svdglue.hxx> +#include <svl/intitem.hxx> +#include <vcl/toolbox.hxx> + +#include <strings.hrc> +#include <gluectrl.hxx> +#include <sdresid.hxx> +#include <app.hrc> + +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/frame/XFrame.hpp> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::frame; + +// at the moment, Joe only supports the methods specified below +#define ESCDIR_COUNT 5 +const SdrEscapeDirection aEscDirArray[] = +{ + SdrEscapeDirection::SMART, + SdrEscapeDirection::LEFT, + SdrEscapeDirection::RIGHT, + SdrEscapeDirection::TOP, + SdrEscapeDirection::BOTTOM +}; + +SFX_IMPL_TOOLBOX_CONTROL( SdTbxCtlGlueEscDir, SfxUInt16Item ) + +/** + * Constructor for gluepoint escape direction Listbox + */ +GlueEscDirLB::GlueEscDirLB(vcl::Window* pParent, const Reference<XFrame>& rFrame) + : InterimItemWindow(pParent, "modules/simpress/ui/gluebox.ui", "GlueBox") + , m_xFrame(rFrame) + , m_xWidget(m_xBuilder->weld_combo_box("gluetype")) +{ + InitControlBase(m_xWidget.get()); + + Fill(); + + m_xWidget->connect_changed(LINK(this, GlueEscDirLB, SelectHdl)); + m_xWidget->connect_key_press(LINK(this, GlueEscDirLB, KeyInputHdl)); + + SetSizePixel(m_xWidget->get_preferred_size()); + + Show(); +} + +void GlueEscDirLB::dispose() +{ + m_xWidget.reset(); + InterimItemWindow::dispose(); +} + +GlueEscDirLB::~GlueEscDirLB() +{ + disposeOnce(); +} + +void GlueEscDirLB::set_sensitive(bool bSensitive) +{ + Enable(bSensitive); + m_xWidget->set_sensitive(bSensitive); +} + +IMPL_LINK(GlueEscDirLB, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + return ChildKeyInput(rKEvt); +} + +/** + * Determines the escape direction and sends the corresponding slot + */ +IMPL_LINK(GlueEscDirLB, SelectHdl, weld::ComboBox&, rBox, void) +{ + sal_Int32 nPos = rBox.get_active(); + SfxUInt16Item aItem( SID_GLUE_ESCDIR, static_cast<sal_uInt16>(aEscDirArray[ nPos ]) ); + + if ( m_xFrame.is() ) + { + Any a; + aItem.QueryValue( a ); + Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue("GlueEscapeDirection", a) }; + SfxToolBoxControl::Dispatch( Reference< XDispatchProvider >( m_xFrame->getController(), UNO_QUERY ), + ".uno:GlueEscapeDirection", + aArgs ); + } +} + +/** + * Fills the Listbox with strings + */ +void GlueEscDirLB::Fill() +{ + m_xWidget->append_text( SdResId( STR_GLUE_ESCDIR_SMART ) ); + m_xWidget->append_text( SdResId( STR_GLUE_ESCDIR_LEFT ) ); + m_xWidget->append_text( SdResId( STR_GLUE_ESCDIR_RIGHT ) ); + m_xWidget->append_text( SdResId( STR_GLUE_ESCDIR_TOP ) ); + m_xWidget->append_text( SdResId( STR_GLUE_ESCDIR_BOTTOM ) ); + /* + m_xWidget->append_text( SdResId( STR_GLUE_ESCDIR_LO ) ); + m_xWidget->append_text( SdResId( STR_GLUE_ESCDIR_LU ) ); + m_xWidget->append_text( SdResId( STR_GLUE_ESCDIR_RO ) ); + m_xWidget->append_text( SdResId( STR_GLUE_ESCDIR_RU ) ); + m_xWidget->append_text( SdResId( STR_GLUE_ESCDIR_HORZ ) ); + m_xWidget->append_text( SdResId( STR_GLUE_ESCDIR_VERT ) ); + m_xWidget->append_text( SdResId( STR_GLUE_ESCDIR_ALL ) ); + */ +} + +/** + * Constructor for gluepoint escape direction toolbox control + */ +SdTbxCtlGlueEscDir::SdTbxCtlGlueEscDir( + sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx ) : + SfxToolBoxControl( nSlotId, nId, rTbx ) +{ +} + +/** + * Represents state in the listbox of the controller + */ +void SdTbxCtlGlueEscDir::StateChangedAtToolBoxControl( sal_uInt16 nSId, + SfxItemState eState, const SfxPoolItem* pState ) +{ + if( eState == SfxItemState::DEFAULT ) + { + GlueEscDirLB* pGlueEscDirLB = static_cast<GlueEscDirLB*> ( GetToolBox(). + GetItemWindow( GetId() ) ); + if( pGlueEscDirLB ) + { + if( pState ) + { + pGlueEscDirLB->set_sensitive(true); + if ( IsInvalidItem( pState ) ) + { + pGlueEscDirLB->set_active(-1); + } + else + { + SdrEscapeDirection nEscDir = static_cast<SdrEscapeDirection>(static_cast<const SfxUInt16Item*>( pState )->GetValue()); + pGlueEscDirLB->set_active( GetEscDirPos( nEscDir ) ); + } + } + else + { + pGlueEscDirLB->set_sensitive(false); + pGlueEscDirLB->set_active(-1); + } + } + } + + SfxToolBoxControl::StateChangedAtToolBoxControl( nSId, eState, pState ); +} + +VclPtr<InterimItemWindow> SdTbxCtlGlueEscDir::CreateItemWindow( vcl::Window *pParent ) +{ + if( GetSlotId() == SID_GLUE_ESCDIR ) + return VclPtr<GlueEscDirLB>::Create( pParent, m_xFrame ).get(); + + return VclPtr<InterimItemWindow>(); +} + +/** + * Returns position in the array for EscDir (Mapping for Listbox) + */ +sal_uInt16 SdTbxCtlGlueEscDir::GetEscDirPos( SdrEscapeDirection nEscDir ) +{ + for( sal_uInt16 i = 0; i < ESCDIR_COUNT; i++ ) + { + if( aEscDirArray[ i ] == nEscDir ) + return i; + } + return 99; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/headerfooterdlg.cxx b/sd/source/ui/dlg/headerfooterdlg.cxx new file mode 100644 index 000000000..703f2f598 --- /dev/null +++ b/sd/source/ui/dlg/headerfooterdlg.cxx @@ -0,0 +1,759 @@ +/* -*- 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 <editeng/eeitem.hxx> +#include <editeng/flditem.hxx> +#include <editeng/langitem.hxx> +#include <svx/langbox.hxx> +#include <svx/svdotext.hxx> +#include <editeng/editeng.hxx> +#include <editeng/outlobj.hxx> +#include <sfx2/viewfrm.hxx> +#include <tools/debug.hxx> + +#include <Outliner.hxx> +#include <headerfooterdlg.hxx> +#include <DrawDocShell.hxx> +#include <drawdoc.hxx> +#include <ViewShell.hxx> +#include <sdmod.hxx> + +// preview control for presentation layout +#include <tools/color.hxx> +#include <i18nlangtag/mslangid.hxx> +#include <svtools/colorcfg.hxx> +#include <vcl/customweld.hxx> +#include <vcl/decoview.hxx> +#include <vcl/svapp.hxx> + +#include <undoheaderfooter.hxx> +#include <sdundogr.hxx> + +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> + +namespace sd +{ + +namespace { + +class PresLayoutPreview : public weld::CustomWidgetController +{ +private: + SdPage* mpMaster; + HeaderFooterSettings maSettings; + Size maPageSize; + ::tools::Rectangle maOutRect; + +private: + void Paint(vcl::RenderContext& rRenderContext, SdrTextObj const * pObj, bool bVisible, bool bDotted = false); + +public: + explicit PresLayoutPreview(); + + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override; + + virtual void Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect) override; + + void init(SdPage* pMaster); + void update(HeaderFooterSettings const & rSettings); +}; + +} + +} + +// tab page for slide & header'n'notes + +namespace sd +{ + +const int nDateTimeFormatsCount = 12; + +namespace { + +struct DateAndTimeFormat { + SvxDateFormat meDateFormat; + SvxTimeFormat meTimeFormat; +}; + +} + +DateAndTimeFormat const nDateTimeFormats[nDateTimeFormatsCount] = +{ + { SvxDateFormat::A, SvxTimeFormat::AppDefault }, + { SvxDateFormat::B, SvxTimeFormat::AppDefault }, + { SvxDateFormat::C, SvxTimeFormat::AppDefault }, + { SvxDateFormat::D, SvxTimeFormat::AppDefault }, + { SvxDateFormat::E, SvxTimeFormat::AppDefault }, + { SvxDateFormat::F, SvxTimeFormat::AppDefault }, + + { SvxDateFormat::A, SvxTimeFormat::HH24_MM }, + { SvxDateFormat::A, SvxTimeFormat::HH12_MM }, + + { SvxDateFormat::AppDefault, SvxTimeFormat::HH24_MM }, + { SvxDateFormat::AppDefault, SvxTimeFormat::HH24_MM_SS }, + + { SvxDateFormat::AppDefault, SvxTimeFormat::HH12_MM }, + { SvxDateFormat::AppDefault, SvxTimeFormat::HH12_MM_SS }, +}; + +class HeaderFooterTabPage +{ +private: + SdDrawDocument* mpDoc; + LanguageType meOldLanguage; + bool mbHandoutMode; + + std::unique_ptr<weld::Builder> mxBuilder; + std::unique_ptr<weld::Container> mxContainer; + std::unique_ptr<weld::Label> mxFTIncludeOn; + std::unique_ptr<weld::CheckButton> mxCBHeader; + std::unique_ptr<weld::Widget> mxHeaderBox; + std::unique_ptr<weld::Entry> mxTBHeader; + std::unique_ptr<weld::CheckButton> mxCBDateTime; + std::unique_ptr<weld::RadioButton> mxRBDateTimeFixed; + std::unique_ptr<weld::RadioButton> mxRBDateTimeAutomatic; + std::unique_ptr<weld::Entry> mxTBDateTimeFixed; + std::unique_ptr<weld::ComboBox> mxCBDateTimeFormat; + std::unique_ptr<weld::Label> mxFTDateTimeLanguage; + std::unique_ptr<SvxLanguageBox> mxCBDateTimeLanguage; + std::unique_ptr<weld::CheckButton> mxCBFooter; + std::unique_ptr<weld::Widget> mxFooterBox; + std::unique_ptr<weld::Entry> mxTBFooter; + std::unique_ptr<weld::CheckButton> mxCBSlideNumber; + std::unique_ptr<weld::CheckButton> mxCBNotOnTitle; + std::unique_ptr<weld::Label> mxReplacementA; + std::unique_ptr<weld::Label> mxReplacementB; + std::unique_ptr<PresLayoutPreview> mxCTPreview; + std::unique_ptr<weld::CustomWeld> mxCTPreviewWin; + + + DECL_LINK( UpdateOnToggleHdl, weld::Toggleable&, void ); + DECL_LINK( LanguageChangeHdl, weld::ComboBox&, void ); + + void FillFormatList(sal_Int32 nSelectedPos); + void GetOrSetDateTimeLanguage( LanguageType &rLanguage, bool bSet ); + void GetOrSetDateTimeLanguage( LanguageType &rLanguage, bool bSet, SdPage* pPage ); + +public: + HeaderFooterTabPage(weld::Container* pParent, SdDrawDocument* pDoc, SdPage* pActualPage, bool bHandoutMode ); + + void init( const HeaderFooterSettings& rSettings, bool bNotOnTitle ); + void getData( HeaderFooterSettings& rSettings, bool& rNotOnTitle ); + void update(); +}; + +} + +using namespace ::sd; + +HeaderFooterDialog::HeaderFooterDialog(ViewShell* pViewShell, weld::Window* pParent, SdDrawDocument* pDoc, SdPage* pCurrentPage) + : GenericDialogController(pParent, "modules/simpress/ui/headerfooterdialog.ui", "HeaderFooterDialog") + , mpDoc( pDoc ) + , mpCurrentPage( pCurrentPage ) + , mpViewShell( pViewShell ) + , mxTabCtrl(m_xBuilder->weld_notebook("tabcontrol")) + , mxPBApplyToAll(m_xBuilder->weld_button("apply_all")) + , mxPBApply(m_xBuilder->weld_button("apply")) + , mxPBCancel(m_xBuilder->weld_button("cancel")) +{ + SdPage* pSlide; + SdPage* pNotes; + if( pCurrentPage->GetPageKind() == PageKind::Standard ) + { + pSlide = pCurrentPage; + pNotes = static_cast<SdPage*>(pDoc->GetPage( pCurrentPage->GetPageNum() + 1 )); + } + else if( pCurrentPage->GetPageKind() == PageKind::Notes ) + { + pNotes = pCurrentPage; + pSlide = static_cast<SdPage*>(pDoc->GetPage( pCurrentPage->GetPageNum() -1 )); + mpCurrentPage = pSlide; + } + else + { + // handout + pSlide = pDoc->GetSdPage( 0, PageKind::Standard ); + pNotes = pDoc->GetSdPage( 0, PageKind::Notes ); + mpCurrentPage = nullptr; + } + + mxSlideTabPage.reset(new HeaderFooterTabPage(mxTabCtrl->get_page("slides"), pDoc, pSlide, false)); + mxNotesHandoutsTabPage.reset(new HeaderFooterTabPage(mxTabCtrl->get_page("notes"), pDoc, pNotes, true)); + + pDoc->StopWorkStartupDelay(); + mxTabCtrl->show(); + + ActivatePageHdl(mxTabCtrl->get_current_page_ident()); + + mxTabCtrl->connect_enter_page( LINK( this, HeaderFooterDialog, ActivatePageHdl ) ); + + mxPBApplyToAll->connect_clicked( LINK( this, HeaderFooterDialog, ClickApplyToAllHdl ) ); + mxPBApply->connect_clicked( LINK( this, HeaderFooterDialog, ClickApplyHdl ) ); + mxPBCancel->connect_clicked( LINK( this, HeaderFooterDialog, ClickCancelHdl ) ); + + maSlideSettings = pSlide->getHeaderFooterSettings(); + + const HeaderFooterSettings& rTitleSettings = mpDoc->GetSdPage(0, PageKind::Standard)->getHeaderFooterSettings(); + bool bNotOnTitle = !rTitleSettings.mbFooterVisible && !rTitleSettings.mbSlideNumberVisible && !rTitleSettings.mbDateTimeVisible; + + mxSlideTabPage->init( maSlideSettings, bNotOnTitle ); + + maNotesHandoutSettings = pNotes->getHeaderFooterSettings(); + mxNotesHandoutsTabPage->init( maNotesHandoutSettings, false ); +} + +HeaderFooterDialog::~HeaderFooterDialog() +{ +} + +IMPL_LINK(HeaderFooterDialog, ActivatePageHdl, const OString&, rIdent, void) +{ + mxPBApply->set_visible(rIdent == "slides"); + mxPBApply->set_sensitive(mpCurrentPage != nullptr); +} + +IMPL_LINK_NOARG(HeaderFooterDialog, ClickApplyToAllHdl, weld::Button&, void) +{ + ApplyToAll(); +} + +IMPL_LINK_NOARG(HeaderFooterDialog, ClickApplyHdl, weld::Button&, void) +{ + Apply(); +} + +IMPL_LINK_NOARG(HeaderFooterDialog, ClickCancelHdl, weld::Button&, void) +{ + m_xDialog->response(RET_CANCEL); +} + +short HeaderFooterDialog::run() +{ + short nRet = GenericDialogController::run(); + if (nRet) + mpViewShell->GetDocSh()->SetModified(); + return nRet; +} + +void HeaderFooterDialog::ApplyToAll() +{ + OString tabId = mxTabCtrl->get_current_page_ident(); + apply(true, tabId == "slides"); + m_xDialog->response(RET_OK); +} + +void HeaderFooterDialog::Apply() +{ + OString tabId = mxTabCtrl->get_current_page_ident(); + apply(false, tabId == "slides"); + m_xDialog->response(RET_OK); +} + +void HeaderFooterDialog::apply( bool bToAll, bool bForceSlides ) +{ + std::unique_ptr<SdUndoGroup> pUndoGroup(new SdUndoGroup(mpDoc)); + OUString aComment( m_xDialog->get_title() ); + pUndoGroup->SetComment( aComment ); + + HeaderFooterSettings aNewSettings; + bool bNewNotOnTitle; + + // change slide settings first ... + + mxSlideTabPage->getData( aNewSettings, bNewNotOnTitle ); + + // only if we pressed apply or apply all on the slide tab page or if the slide settings + // have been changed + if( bForceSlides || !(aNewSettings == maSlideSettings) ) + { + // apply to all slides + if( bToAll ) + { + int nPageCount = mpDoc->GetSdPageCount( PageKind::Standard ); + int nPage; + for( nPage = 0; nPage < nPageCount; nPage++ ) + { + SdPage* pPage = mpDoc->GetSdPage( static_cast<sal_uInt16>(nPage), PageKind::Standard ); + change( pUndoGroup.get(), pPage, aNewSettings ); + } + } + else + { + // apply only to the current slide + DBG_ASSERT( mpCurrentPage && mpCurrentPage->GetPageKind() == PageKind::Standard, "no current page to apply to!" ); + if( mpCurrentPage && (mpCurrentPage->GetPageKind() == PageKind::Standard) ) + { + change( pUndoGroup.get(), mpCurrentPage, aNewSettings ); + } + } + } + + // if we don't want to have header&footer on the first slide + if( bNewNotOnTitle ) + { + // just hide them, plain simple UI feature + HeaderFooterSettings aTempSettings = mpDoc->GetSdPage( 0, PageKind::Standard )->getHeaderFooterSettings(); + + aTempSettings.mbFooterVisible = false; + aTempSettings.mbSlideNumberVisible = false; + aTempSettings.mbDateTimeVisible = false; + + change( pUndoGroup.get(), mpDoc->GetSdPage( 0, PageKind::Standard ), aTempSettings ); + } + + // now notes settings + + mxNotesHandoutsTabPage->getData( aNewSettings, bNewNotOnTitle ); + + // only if we pressed apply or apply all on the notes tab page or if the notes settings + // have been changed + if( !bForceSlides || !(aNewSettings == maNotesHandoutSettings) ) + { + // first set to all notes pages + int nPageCount = mpDoc->GetSdPageCount( PageKind::Notes ); + int nPage; + for( nPage = 0; nPage < nPageCount; nPage++ ) + { + SdPage* pPage = mpDoc->GetSdPage( static_cast<sal_uInt16>(nPage), PageKind::Notes ); + + change( pUndoGroup.get(), pPage, aNewSettings ); + } + + // and last but not least to the handout page + change( pUndoGroup.get(), mpDoc->GetMasterSdPage( 0, PageKind::Handout ), aNewSettings ); + } + + // give the undo group to the undo manager + mpViewShell->GetViewFrame()->GetObjectShell()->GetUndoManager()->AddUndoAction(std::move(pUndoGroup)); +} + +void HeaderFooterDialog::change( SdUndoGroup* pUndoGroup, SdPage* pPage, const HeaderFooterSettings& rNewSettings ) +{ + pUndoGroup->AddAction(new SdHeaderFooterUndoAction(mpDoc, pPage, rNewSettings )); + pPage->setHeaderFooterSettings( rNewSettings ); +} + +HeaderFooterTabPage::HeaderFooterTabPage(weld::Container* pParent, SdDrawDocument* pDoc, SdPage* pActualPage, bool bHandoutMode) + : mpDoc(pDoc) + , mbHandoutMode(bHandoutMode) + , mxBuilder(Application::CreateBuilder(pParent, "modules/simpress/ui/headerfootertab.ui")) + , mxContainer(mxBuilder->weld_container("HeaderFooterTab")) + , mxFTIncludeOn(mxBuilder->weld_label("include_label")) + , mxCBHeader(mxBuilder->weld_check_button("header_cb" )) + , mxHeaderBox(mxBuilder->weld_widget("header_box")) + , mxTBHeader(mxBuilder->weld_entry("header_text")) + , mxCBDateTime(mxBuilder->weld_check_button("datetime_cb")) + , mxRBDateTimeFixed(mxBuilder->weld_radio_button("rb_fixed")) + , mxRBDateTimeAutomatic(mxBuilder->weld_radio_button("rb_auto")) + , mxTBDateTimeFixed(mxBuilder->weld_entry("datetime_value")) + , mxCBDateTimeFormat(mxBuilder->weld_combo_box("datetime_format_list")) + , mxFTDateTimeLanguage(mxBuilder->weld_label("language_label")) + , mxCBDateTimeLanguage(new SvxLanguageBox(mxBuilder->weld_combo_box("language_list"))) + , mxCBFooter(mxBuilder->weld_check_button("footer_cb")) + , mxFooterBox(mxBuilder->weld_widget("footer_box" )) + , mxTBFooter(mxBuilder->weld_entry("footer_text")) + , mxCBSlideNumber(mxBuilder->weld_check_button("slide_number")) + , mxCBNotOnTitle(mxBuilder->weld_check_button("not_on_title")) + , mxReplacementA(mxBuilder->weld_label("replacement_a")) + , mxReplacementB(mxBuilder->weld_label("replacement_b")) + , mxCTPreview(new PresLayoutPreview) + , mxCTPreviewWin(new weld::CustomWeld(*mxBuilder, "preview", *mxCTPreview)) +{ + mxCTPreview->init( pActualPage ? + (pActualPage->IsMasterPage() ? pActualPage : static_cast<SdPage*>(&(pActualPage->TRG_GetMasterPage()))) : + (pDoc->GetMasterSdPage( 0, bHandoutMode ? PageKind::Notes : PageKind::Standard )) ); + + if( mbHandoutMode ) + { + OUString sPageNo = mxReplacementA->get_label(); + mxCBSlideNumber->set_label( sPageNo ); + + OUString sFrameTitle = mxReplacementB->get_label(); + mxFTIncludeOn->set_label( sFrameTitle ); + } + + mxCBHeader->set_visible( mbHandoutMode ); + mxHeaderBox->set_visible( mbHandoutMode ); + mxCBNotOnTitle->set_visible( !mbHandoutMode ); + + mxCBDateTime->connect_toggled( LINK( this, HeaderFooterTabPage, UpdateOnToggleHdl ) ); + mxRBDateTimeFixed->connect_toggled( LINK( this, HeaderFooterTabPage, UpdateOnToggleHdl ) ); + mxRBDateTimeAutomatic->connect_toggled( LINK( this, HeaderFooterTabPage, UpdateOnToggleHdl ) ); + mxCBFooter->connect_toggled( LINK( this, HeaderFooterTabPage, UpdateOnToggleHdl ) ); + mxCBHeader->connect_toggled( LINK( this, HeaderFooterTabPage, UpdateOnToggleHdl ) ); + mxCBSlideNumber->connect_toggled( LINK( this, HeaderFooterTabPage, UpdateOnToggleHdl ) ); + + mxCBDateTimeLanguage->SetLanguageList( SvxLanguageListFlags::ALL|SvxLanguageListFlags::ONLY_KNOWN, false, false ); + mxCBDateTimeLanguage->connect_changed( LINK( this, HeaderFooterTabPage, LanguageChangeHdl ) ); + + GetOrSetDateTimeLanguage( meOldLanguage, false ); + meOldLanguage = MsLangId::getRealLanguage( meOldLanguage ); + mxCBDateTimeLanguage->set_active_id( meOldLanguage ); + + FillFormatList(0); +} + +IMPL_LINK_NOARG(HeaderFooterTabPage, LanguageChangeHdl, weld::ComboBox&, void) +{ + FillFormatList( mxCBDateTimeFormat->get_active() ); +} + +void HeaderFooterTabPage::FillFormatList( sal_Int32 nSelectedPos ) +{ + LanguageType eLanguage = mxCBDateTimeLanguage->get_active_id(); + + mxCBDateTimeFormat->clear(); + + DateTime aDateTime( DateTime::SYSTEM ); + + for (int nFormat = 0; nFormat < nDateTimeFormatsCount; ++nFormat) + { + OUString aStr( SvxDateTimeField::GetFormatted( + aDateTime, aDateTime, + nDateTimeFormats[nFormat].meDateFormat, nDateTimeFormats[nFormat].meTimeFormat, + *(SD_MOD()->GetNumberFormatter()), eLanguage ) ); + mxCBDateTimeFormat->append_text(aStr); + if (nFormat == nSelectedPos) + mxCBDateTimeFormat->set_active(nFormat); + } +} + +void HeaderFooterTabPage::init( const HeaderFooterSettings& rSettings, bool bNotOnTitle ) +{ + mxCBDateTime->set_active( rSettings.mbDateTimeVisible ); + mxRBDateTimeFixed->set_active( rSettings.mbDateTimeIsFixed ); + mxRBDateTimeAutomatic->set_active( !rSettings.mbDateTimeIsFixed ); + mxTBDateTimeFixed->set_text( rSettings.maDateTimeText ); + + mxCBHeader->set_active( rSettings.mbHeaderVisible ); + mxTBHeader->set_text( rSettings.maHeaderText ); + + mxCBFooter->set_active( rSettings.mbFooterVisible ); + mxTBFooter->set_text( rSettings.maFooterText ); + + mxCBSlideNumber->set_active( rSettings.mbSlideNumberVisible ); + + mxCBNotOnTitle->set_active( bNotOnTitle ); + + mxCBDateTimeLanguage->set_active_id( meOldLanguage ); + + for (sal_Int32 nPos = 0, nEntryCount = mxCBDateTimeFormat->get_count(); nPos < nEntryCount; ++nPos) + { + if( nDateTimeFormats[nPos].meDateFormat == rSettings.meDateFormat && nDateTimeFormats[nPos].meTimeFormat == rSettings.meTimeFormat ) + { + mxCBDateTimeFormat->set_active(nPos); + break; + } + } + + update(); +} + +void HeaderFooterTabPage::getData( HeaderFooterSettings& rSettings, bool& rNotOnTitle ) +{ + rSettings.mbDateTimeVisible = mxCBDateTime->get_active(); + rSettings.mbDateTimeIsFixed = mxRBDateTimeFixed->get_active(); + rSettings.maDateTimeText = mxTBDateTimeFixed->get_text(); + rSettings.mbFooterVisible = mxCBFooter->get_active(); + rSettings.maFooterText = mxTBFooter->get_text(); + rSettings.mbSlideNumberVisible = mxCBSlideNumber->get_active(); + rSettings.mbHeaderVisible = mxCBHeader->get_active(); + rSettings.maHeaderText = mxTBHeader->get_text(); + + int nPos = mxCBDateTimeFormat->get_active(); + if (nPos != -1) + { + rSettings.meDateFormat = nDateTimeFormats[nPos].meDateFormat; + rSettings.meTimeFormat = nDateTimeFormats[nPos].meTimeFormat; + } + + LanguageType eLanguage = mxCBDateTimeLanguage->get_active_id(); + if( eLanguage != meOldLanguage ) + GetOrSetDateTimeLanguage( eLanguage, true ); + + rNotOnTitle = mxCBNotOnTitle->get_active(); +} + +void HeaderFooterTabPage::update() +{ + mxRBDateTimeFixed->set_sensitive( mxCBDateTime->get_active() ); + mxTBDateTimeFixed->set_sensitive( mxRBDateTimeFixed->get_active() && mxCBDateTime->get_active() ); + mxRBDateTimeAutomatic->set_sensitive( mxCBDateTime->get_active() ); + mxCBDateTimeFormat->set_sensitive( mxCBDateTime->get_active() && mxRBDateTimeAutomatic->get_active() ); + mxFTDateTimeLanguage->set_sensitive( mxCBDateTime->get_active() && mxRBDateTimeAutomatic->get_active() ); + mxCBDateTimeLanguage->set_sensitive( mxCBDateTime->get_active() && mxRBDateTimeAutomatic->get_active() ); + mxFooterBox->set_sensitive( mxCBFooter->get_active() ); + mxHeaderBox->set_sensitive( mxCBHeader->get_active() ); + + HeaderFooterSettings aSettings; + bool bNotOnTitle; + getData( aSettings, bNotOnTitle ); + mxCTPreview->update( aSettings ); +} + +IMPL_LINK_NOARG(HeaderFooterTabPage, UpdateOnToggleHdl, weld::Toggleable&, void) +{ + update(); +} + +void HeaderFooterTabPage::GetOrSetDateTimeLanguage( LanguageType &rLanguage, bool bSet ) +{ + if( mbHandoutMode ) + { + // if set, set it on all notes master pages + if( bSet ) + { + sal_uInt16 nPageCount = mpDoc->GetMasterSdPageCount( PageKind::Notes ); + sal_uInt16 nPage; + for( nPage = 0; nPage < nPageCount; nPage++ ) + { + GetOrSetDateTimeLanguage( rLanguage, bSet, mpDoc->GetMasterSdPage( nPage, PageKind::Notes ) ); + } + } + + // #i119985# and set it, or just get it from the notes master page + GetOrSetDateTimeLanguage( rLanguage, bSet, mpDoc->GetMasterSdPage( 0, PageKind::Notes ) ); + } + else + { + // get the language from the first master page + // or set it to all master pages + sal_uInt16 nPageCount = bSet ? mpDoc->GetMasterSdPageCount( PageKind::Notes ) : 1; + sal_uInt16 nPage; + for( nPage = 0; nPage < nPageCount; nPage++ ) + { + GetOrSetDateTimeLanguage( rLanguage, bSet, mpDoc->GetMasterSdPage( nPage, PageKind::Standard ) ); + } + } +} + +void HeaderFooterTabPage::GetOrSetDateTimeLanguage( LanguageType &rLanguage, bool bSet, SdPage* pPage ) +{ + if( !pPage ) + return; + + SdrTextObj* pObj = static_cast<SdrTextObj*>(pPage->GetPresObj( PresObjKind::DateTime )); + if( !pObj ) + return; + + Outliner* pOutl = mpDoc->GetInternalOutliner(); + pOutl->Init( OutlinerMode::TextObject ); + OutlinerMode nOutlMode = pOutl->GetOutlinerMode(); + + EditEngine* pEdit = const_cast< EditEngine* >(&pOutl->GetEditEngine()); + + OutlinerParaObject* pOPO = pObj->GetOutlinerParaObject(); + if( pOPO ) + pOutl->SetText( *pOPO ); + + EPosition aDateFieldPosition; + bool bHasDateFieldItem = false; + + sal_Int32 nParaCount = pEdit->GetParagraphCount(); + for (sal_Int32 nPara = 0; (nPara < nParaCount) && !bHasDateFieldItem; ++nPara) + { + sal_uInt16 nFieldCount = pEdit->GetFieldCount(nPara); + for (sal_uInt16 nField = 0; (nField < nFieldCount); ++nField) + { + EFieldInfo aFieldInfo = pEdit->GetFieldInfo(nPara, nField); + if (aFieldInfo.pFieldItem) + { + const SvxFieldData* pFieldData = aFieldInfo.pFieldItem->GetField(); + if (dynamic_cast<const SvxDateTimeField*>(pFieldData) != nullptr || + dynamic_cast<const SvxDateField*>(pFieldData) != nullptr) + { + bHasDateFieldItem = true; + aDateFieldPosition = aFieldInfo.aPosition; + break; + } + } + } + } + + if (bHasDateFieldItem) + { + if( bSet ) + { + SfxItemSet aSet(pEdit->GetAttribs(aDateFieldPosition.nPara, + aDateFieldPosition.nIndex, + aDateFieldPosition.nIndex+1, + GetAttribsFlags::CHARATTRIBS)); + + SvxLanguageItem aItem( rLanguage, EE_CHAR_LANGUAGE ); + aSet.Put( aItem ); + + SvxLanguageItem aItemCJK( rLanguage, EE_CHAR_LANGUAGE_CJK ); + aSet.Put( aItemCJK ); + + SvxLanguageItem aItemCTL( rLanguage, EE_CHAR_LANGUAGE_CTL ); + aSet.Put( aItemCTL ); + + ESelection aSel(aDateFieldPosition.nPara, aDateFieldPosition.nIndex, + aDateFieldPosition.nPara, aDateFieldPosition.nIndex+1 ); + pEdit->QuickSetAttribs( aSet, aSel ); + + pObj->SetOutlinerParaObject( pOutl->CreateParaObject() ); + pOutl->UpdateFields(); + } + else + { + rLanguage = pOutl->GetLanguage(aDateFieldPosition.nPara, + aDateFieldPosition.nIndex ); + } + } + + pOutl->Clear(); + pOutl->Init( nOutlMode ); +} + +PresLayoutPreview::PresLayoutPreview() + : mpMaster(nullptr) +{ +} + +void PresLayoutPreview::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(80, 80), MapMode(MapUnit::MapAppFont))); + pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); + CustomWidgetController::SetDrawingArea(pDrawingArea); + SetOutputSizePixel(aSize); +} + +void PresLayoutPreview::init( SdPage *pMaster ) +{ + mpMaster = pMaster; + maPageSize = pMaster->GetSize(); +} + +void PresLayoutPreview::update( HeaderFooterSettings const & rSettings ) +{ + maSettings = rSettings; + Invalidate(); +} + +void PresLayoutPreview::Paint(vcl::RenderContext& rRenderContext, SdrTextObj const * pObj, bool bVisible, bool bDotted /* = false*/ ) +{ + // get object transformation + basegfx::B2DHomMatrix aObjectTransform; + basegfx::B2DPolyPolygon aObjectPolyPolygon; + pObj->TRGetBaseGeometry(aObjectTransform, aObjectPolyPolygon); + + // build complete transformation by adding view transformation from + // logic page coordinates to local pixel coordinates + const double fScaleX(static_cast<double>(maOutRect.getWidth()) / static_cast<double>(maPageSize.Width())); + const double fScaleY(static_cast<double>(maOutRect.getHeight()) / static_cast<double>(maPageSize.Height())); + aObjectTransform.scale(fScaleX, fScaleY); + aObjectTransform.translate(maOutRect.Left(), maOutRect.Top()); + + // create geometry using unit range and object transform + basegfx::B2DPolyPolygon aGeometry(basegfx::utils::createUnitPolygon()); + aGeometry.transform(aObjectTransform); + + // apply line pattern if wanted + if (bDotted) + { + static const double fFactor(1.0); + std::vector<double> aPattern + { + 3.0 * fFactor, + 1.0 * fFactor + }; + + basegfx::B2DPolyPolygon aDashed; + basegfx::utils::applyLineDashing(aGeometry, aPattern, &aDashed); + aGeometry = aDashed; + } + + // determine line color + svtools::ColorConfig aColorConfig; + svtools::ColorConfigValue aColor( aColorConfig.GetColorValue( bVisible ? svtools::FONTCOLOR : svtools::OBJECTBOUNDARIES ) ); + + // paint at OutDev + rRenderContext.SetLineColor(aColor.nColor); + rRenderContext.SetFillColor(); + + for (sal_uInt32 a(0); a < aGeometry.count(); a++) + { + rRenderContext.DrawPolyLine(aGeometry.getB2DPolygon(a)); + } +} + +void PresLayoutPreview::Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle&) +{ + rRenderContext.Push(); + + maOutRect = ::tools::Rectangle(Point(0,0), rRenderContext.GetOutputSize()); + + // calculate page size with correct aspect ratio + int nWidth, nHeight; + if( maPageSize.Width() > maPageSize.Height() ) + { + nWidth = maOutRect.GetWidth(); + nHeight = maPageSize.Width() == 0 ? 0 : tools::Long( static_cast<double>(nWidth * maPageSize.Height()) / static_cast<double>(maPageSize.Width()) ); + } + else + { + nHeight = maOutRect.GetHeight(); + nWidth = maPageSize.Height() == 0 ? 0 : tools::Long( static_cast<double>(nHeight * maPageSize.Width()) / static_cast<double>(maPageSize.Height()) ); + } + + maOutRect.AdjustLeft((maOutRect.GetWidth() - nWidth) >> 1 ); + maOutRect.SetRight( maOutRect.Left() + nWidth - 1 ); + maOutRect.AdjustTop((maOutRect.GetHeight() - nHeight) >> 1 ); + maOutRect.SetBottom( maOutRect.Top() + nHeight - 1 ); + + // draw decoration frame + DecorationView aDecoView(&rRenderContext); + maOutRect = aDecoView.DrawFrame(maOutRect, DrawFrameStyle::In); + + // draw page background + rRenderContext.SetFillColor(COL_WHITE); + rRenderContext.DrawRect(maOutRect); + + // paint presentation objects from masterpage + if (nullptr != mpMaster) + { + SdrTextObj* pMasterTitle = static_cast<SdrTextObj*>(mpMaster->GetPresObj(PresObjKind::Title)); + SdrTextObj* pMasterOutline = static_cast<SdrTextObj*>(mpMaster->GetPresObj(mpMaster->GetPageKind() == PageKind::Notes ? PresObjKind::Notes : PresObjKind::Outline)); + SdrTextObj* pHeader = static_cast<SdrTextObj*>(mpMaster->GetPresObj(PresObjKind::Header)); + SdrTextObj* pFooter = static_cast<SdrTextObj*>(mpMaster->GetPresObj(PresObjKind::Footer)); + SdrTextObj* pDate = static_cast<SdrTextObj*>(mpMaster->GetPresObj(PresObjKind::DateTime)); + SdrTextObj* pNumber = static_cast<SdrTextObj*>(mpMaster->GetPresObj(PresObjKind::SlideNumber)); + + if (pMasterTitle) + Paint(rRenderContext, pMasterTitle, true, true); + if (pMasterOutline) + Paint(rRenderContext, pMasterOutline, true, true); + if (pHeader) + Paint(rRenderContext, pHeader, maSettings.mbHeaderVisible); + if (pFooter) + Paint(rRenderContext, pFooter, maSettings.mbFooterVisible); + if (pDate) + Paint(rRenderContext, pDate, maSettings.mbDateTimeVisible); + if (pNumber) + Paint(rRenderContext, pNumber, maSettings.mbSlideNumberVisible); + } + + rRenderContext.Pop(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/ins_paste.cxx b/sd/source/ui/dlg/ins_paste.cxx new file mode 100644 index 000000000..f1020c0cb --- /dev/null +++ b/sd/source/ui/dlg/ins_paste.cxx @@ -0,0 +1,34 @@ +/* -*- 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 <ins_paste.hxx> + +SdInsertPasteDlg::SdInsertPasteDlg(weld::Window* pWindow) + : GenericDialogController(pWindow, "modules/simpress/ui/insertslides.ui", "InsertSlidesDialog") + , m_xRbBefore(m_xBuilder->weld_radio_button("before")) + , m_xRbAfter(m_xBuilder->weld_radio_button("after")) +{ + m_xRbAfter->set_active(true); +} + +SdInsertPasteDlg::~SdInsertPasteDlg() {} + +bool SdInsertPasteDlg::IsInsertBefore() const { return m_xRbBefore->get_active(); } + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/inspagob.cxx b/sd/source/ui/dlg/inspagob.cxx new file mode 100644 index 000000000..5c984dd23 --- /dev/null +++ b/sd/source/ui/dlg/inspagob.cxx @@ -0,0 +1,126 @@ +/* -*- 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 <inspagob.hxx> +#include <sdtreelb.hxx> + +#include <strings.hrc> + +#include <bitmaps.hlst> +#include <sdresid.hxx> +#include <drawdoc.hxx> +#include <DrawDocShell.hxx> +#include <ViewShell.hxx> + +SdInsertPagesObjsDlg::SdInsertPagesObjsDlg( + weld::Window* pWindow, const SdDrawDocument* pInDoc, + SfxMedium* pSfxMedium, const OUString& rFileName ) + : GenericDialogController(pWindow, "modules/sdraw/ui/insertslidesdialog.ui", "InsertSlidesDialog") + , m_pMedium(pSfxMedium) + , m_pDoc(pInDoc) + , m_rName(rFileName) + , m_xLbTree(new SdPageObjsTLV(m_xBuilder->weld_tree_view("tree"))) + , m_xCbxLink(m_xBuilder->weld_check_button("links")) + , m_xCbxMasters(m_xBuilder->weld_check_button("backgrounds")) +{ + m_xLbTree->set_size_request(m_xLbTree->get_approximate_digit_width() * 48, + m_xLbTree->get_height_rows(12)); + + m_xLbTree->SetViewFrame( pInDoc->GetDocSh()->GetViewShell()->GetViewFrame() ); + + m_xLbTree->connect_changed(LINK(this, SdInsertPagesObjsDlg, SelectObjectHdl)); + + // insert text + if (!m_pMedium) + m_xDialog->set_title(SdResId(STR_INSERT_TEXT)); + else if (m_pDoc && m_pDoc->GetDocumentType() == DocumentType::Draw) + m_xDialog->set_title(SdResId(STR_INSERT_PAGES)); + + Reset(); +} + +SdInsertPagesObjsDlg::~SdInsertPagesObjsDlg() +{ +} + +/** + * Fills the TreeLB dependent on the medium. Is not medium available, then + * it is a text and not a draw document. + */ +void SdInsertPagesObjsDlg::Reset() +{ + if( m_pMedium ) + { + m_xLbTree->set_selection_mode(SelectionMode::Multiple); + + // transfer ownership of Medium + m_xLbTree->Fill( m_pDoc, m_pMedium, m_rName ); + } + else + { + m_xLbTree->InsertEntry(m_rName, BMP_DOC_TEXT); + } + + m_xCbxMasters->set_active(true); +} + +std::vector<OUString> SdInsertPagesObjsDlg::GetList( const sal_uInt16 nType ) +{ + // With Draw documents, we have to return NULL on selection of the document + if( m_pMedium ) + { + // to ensure that bookmarks are opened + // (when the whole document is selected) + m_xLbTree->GetBookmarkDoc(); + + // If the document is selected (too) or nothing is selected, + // the whole document is inserted (but not more!) + std::unique_ptr<weld::TreeIter> xIter(m_xLbTree->make_iterator()); + if (!m_xLbTree->get_iter_first(*xIter) || m_xLbTree->is_selected(*xIter)) + return std::vector<OUString>(); + } + + return m_xLbTree->GetSelectEntryList( nType ); +} + +/** + * is link checked + */ +bool SdInsertPagesObjsDlg::IsLink() const +{ + return m_xCbxLink->get_active(); +} + +/** + * is link checked + */ +bool SdInsertPagesObjsDlg::IsRemoveUnnecessaryMasterPages() const +{ + return m_xCbxMasters->get_active(); +} + +/** + * Enabled and selects end-color-LB + */ +IMPL_LINK_NOARG(SdInsertPagesObjsDlg, SelectObjectHdl, weld::TreeView&, void) +{ + m_xCbxLink->set_sensitive(m_xLbTree->IsLinkableSelected()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/layeroptionsdlg.cxx b/sd/source/ui/dlg/layeroptionsdlg.cxx new file mode 100644 index 000000000..a70c71f47 --- /dev/null +++ b/sd/source/ui/dlg/layeroptionsdlg.cxx @@ -0,0 +1,62 @@ +/* -*- 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 <svl/itemset.hxx> + +#include <sdattr.hxx> +#include <layeroptionsdlg.hxx> + +SdInsertLayerDlg::SdInsertLayerDlg(weld::Window* pWindow, const SfxItemSet& rInAttrs, + bool bDeletable, const OUString& rStr) + : GenericDialogController(pWindow, "modules/sdraw/ui/insertlayer.ui", "InsertLayerDialog") + , m_xEdtName(m_xBuilder->weld_entry("name")) + , m_xEdtTitle(m_xBuilder->weld_entry("title")) + , m_xEdtDesc(m_xBuilder->weld_text_view("textview")) + , m_xCbxVisible(m_xBuilder->weld_check_button("visible")) + , m_xCbxPrintable(m_xBuilder->weld_check_button("printable")) + , m_xCbxLocked(m_xBuilder->weld_check_button("locked")) + , m_xNameFrame(m_xBuilder->weld_widget("nameframe")) +{ + m_xDialog->set_title(rStr); + + m_xEdtName->set_text( static_cast<const SfxStringItem&>( rInAttrs.Get( ATTR_LAYER_NAME ) ).GetValue() ); + m_xEdtTitle->set_text( static_cast<const SfxStringItem&>( rInAttrs.Get( ATTR_LAYER_TITLE ) ).GetValue() ); + m_xEdtDesc->set_text( static_cast<const SfxStringItem&>( rInAttrs.Get( ATTR_LAYER_DESC ) ).GetValue() ); + m_xEdtDesc->set_size_request(-1, m_xEdtDesc->get_height_rows(4)); + m_xCbxVisible->set_active( static_cast<const SfxBoolItem&>( rInAttrs.Get( ATTR_LAYER_VISIBLE ) ).GetValue() ); + m_xCbxPrintable->set_active( static_cast<const SfxBoolItem&>( rInAttrs.Get( ATTR_LAYER_PRINTABLE ) ).GetValue() ); + m_xCbxLocked->set_active( static_cast<const SfxBoolItem&>( rInAttrs.Get( ATTR_LAYER_LOCKED ) ).GetValue() ); + m_xNameFrame->set_sensitive(bDeletable); +} + +SdInsertLayerDlg::~SdInsertLayerDlg() +{ +} + +void SdInsertLayerDlg::GetAttr( SfxItemSet& rAttrs ) +{ + rAttrs.Put( makeSdAttrLayerName( m_xEdtName->get_text() ) ); + rAttrs.Put( makeSdAttrLayerTitle( m_xEdtTitle->get_text() ) ); + rAttrs.Put( makeSdAttrLayerDesc( m_xEdtDesc->get_text() ) ); + rAttrs.Put( makeSdAttrLayerVisible( m_xCbxVisible->get_active() ) ); + rAttrs.Put( makeSdAttrLayerPrintable( m_xCbxPrintable->get_active() ) ); + rAttrs.Put( makeSdAttrLayerLocked( m_xCbxLocked->get_active() ) ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/masterlayoutdlg.cxx b/sd/source/ui/dlg/masterlayoutdlg.cxx new file mode 100644 index 000000000..ce4e069b0 --- /dev/null +++ b/sd/source/ui/dlg/masterlayoutdlg.cxx @@ -0,0 +1,133 @@ +/* -*- 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 <masterlayoutdlg.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <osl/diagnose.h> + +using namespace ::sd; + +MasterLayoutDialog::MasterLayoutDialog(weld::Window* pParent, SdDrawDocument* pDoc, SdPage* pCurrentPage) + : GenericDialogController(pParent, "modules/simpress/ui/masterlayoutdlg.ui", "MasterLayoutDialog") + , mpDoc(pDoc) + , mpCurrentPage(pCurrentPage) + , mxCBDate(m_xBuilder->weld_check_button("datetime")) + , mxCBPageNumber(m_xBuilder->weld_check_button("pagenumber")) + , mxCBSlideNumber(m_xBuilder->weld_check_button("slidenumber")) + , mxCBHeader(m_xBuilder->weld_check_button("header")) + , mxCBFooter(m_xBuilder->weld_check_button("footer")) +{ + if( mpCurrentPage && !mpCurrentPage->IsMasterPage() ) + { + mpCurrentPage = static_cast<SdPage*>(&(mpCurrentPage->TRG_GetMasterPage())); + } + + if( mpCurrentPage == nullptr ) + { + mpCurrentPage = pDoc->GetMasterSdPage( 0, PageKind::Standard ); + OSL_FAIL( "MasterLayoutDialog::MasterLayoutDialog() - no current page?" ); + } + + switch( mpCurrentPage->GetPageKind() ) + { + case PageKind::Standard: + { + mxCBHeader->set_sensitive(false); + mxCBPageNumber->set_label(mxCBSlideNumber->get_label()); + break; + } + case PageKind::Notes: + break; + case PageKind::Handout: + break; + } + + mbOldHeader = mpCurrentPage->GetPresObj( PresObjKind::Header ) != nullptr; + mbOldDate = mpCurrentPage->GetPresObj( PresObjKind::DateTime ) != nullptr; + mbOldFooter = mpCurrentPage->GetPresObj( PresObjKind::Footer ) != nullptr; + mbOldPageNumber = mpCurrentPage->GetPresObj( PresObjKind::SlideNumber ) != nullptr; + + mxCBHeader->set_active( mbOldHeader ); + mxCBDate->set_active( mbOldDate ); + mxCBFooter->set_active( mbOldFooter ); + mxCBPageNumber->set_active( mbOldPageNumber ); +} + +MasterLayoutDialog::~MasterLayoutDialog() +{ +} + +short MasterLayoutDialog::run() +{ + if (GenericDialogController::run() == RET_OK) + applyChanges(); + return RET_OK; +} + +void MasterLayoutDialog::applyChanges() +{ + mpDoc->BegUndo(m_xDialog->get_title()); + + if( (mpCurrentPage->GetPageKind() != PageKind::Standard) && (mbOldHeader != mxCBHeader->get_active() ) ) + { + if( mbOldHeader ) + remove( PresObjKind::Header ); + else + create( PresObjKind::Header ); + } + + if( mbOldFooter != mxCBFooter->get_active() ) + { + if( mbOldFooter ) + remove( PresObjKind::Footer ); + else + create( PresObjKind::Footer ); + } + + if( mbOldDate != mxCBDate->get_active() ) + { + if( mbOldDate ) + remove( PresObjKind::DateTime ); + else + create( PresObjKind::DateTime ); + } + + if( mbOldPageNumber != mxCBPageNumber->get_active() ) + { + if( mbOldPageNumber ) + remove( PresObjKind::SlideNumber ); + else + create( PresObjKind::SlideNumber ); + } + + mpDoc->EndUndo(); +} + +void MasterLayoutDialog::create(PresObjKind eKind) +{ + mpCurrentPage->CreateDefaultPresObj(eKind); +} + +void MasterLayoutDialog::remove( PresObjKind eKind ) +{ + mpCurrentPage->DestroyDefaultPresObj(eKind); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/morphdlg.cxx b/sd/source/ui/dlg/morphdlg.cxx new file mode 100644 index 000000000..c0d7f4e5a --- /dev/null +++ b/sd/source/ui/dlg/morphdlg.cxx @@ -0,0 +1,107 @@ +/* -*- 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 <morphdlg.hxx> + +#include <sdmod.hxx> +#include <sdiocmpt.hxx> +#include <svx/xdef.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xlineit0.hxx> +#include <svx/svdobj.hxx> +#include <svl/itemset.hxx> +#include <com/sun/star/drawing/LineStyle.hpp> + +using namespace com::sun::star; + +namespace sd { + +MorphDlg::MorphDlg(weld::Window* pParent, const SdrObject* pObj1, const SdrObject* pObj2 ) + : GenericDialogController(pParent, "modules/sdraw/ui/crossfadedialog.ui", "CrossFadeDialog") + , m_xMtfSteps(m_xBuilder->weld_spin_button("increments")) + , m_xCbxAttributes(m_xBuilder->weld_check_button("attributes")) + , m_xCbxOrientation(m_xBuilder->weld_check_button("orientation")) +{ + LoadSettings(); + + SfxItemPool & rPool = pObj1->GetObjectItemPool(); + SfxItemSet aSet1( rPool ); + SfxItemSet aSet2( rPool ); + + aSet1.Put(pObj1->GetMergedItemSet()); + aSet2.Put(pObj2->GetMergedItemSet()); + + const drawing::LineStyle eLineStyle1 = aSet1.Get( XATTR_LINESTYLE ).GetValue(); + const drawing::LineStyle eLineStyle2 = aSet2.Get( XATTR_LINESTYLE ).GetValue(); + const drawing::FillStyle eFillStyle1 = aSet1.Get( XATTR_FILLSTYLE ).GetValue(); + const drawing::FillStyle eFillStyle2 = aSet2.Get( XATTR_FILLSTYLE ).GetValue(); + + if ( ( ( eLineStyle1 == drawing::LineStyle_NONE ) || ( eLineStyle2 == drawing::LineStyle_NONE ) ) && + ( ( eFillStyle1 != drawing::FillStyle_SOLID ) || ( eFillStyle2 != drawing::FillStyle_SOLID ) ) ) + { + m_xCbxAttributes->set_sensitive(false); + } +} + +MorphDlg::~MorphDlg() +{ +} + +void MorphDlg::LoadSettings() +{ + tools::SvRef<SotStorageStream> xIStm( SD_MOD()->GetOptionStream( SD_OPTION_MORPHING , + SdOptionStreamMode::Load ) ); + sal_uInt16 nSteps; + bool bOrient, bAttrib; + + if( xIStm.is() ) + { + SdIOCompat aCompat( *xIStm, StreamMode::READ ); + + xIStm->ReadUInt16( nSteps ).ReadCharAsBool( bOrient ).ReadCharAsBool( bAttrib ); + } + else + { + nSteps = 16; + bOrient = bAttrib = true; + } + + m_xMtfSteps->set_value(nSteps); + m_xCbxOrientation->set_active(bOrient); + m_xCbxAttributes->set_active(bAttrib); +} + +void MorphDlg::SaveSettings() const +{ + tools::SvRef<SotStorageStream> xOStm( SD_MOD()->GetOptionStream( SD_OPTION_MORPHING , + SdOptionStreamMode::Store ) ); + + if( xOStm.is() ) + { + SdIOCompat aCompat( *xOStm, StreamMode::WRITE, 1 ); + + xOStm->WriteUInt16( m_xMtfSteps->get_value() ) + .WriteBool( m_xCbxOrientation->get_active() ) + .WriteBool( m_xCbxAttributes->get_active() ); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/navigatr.cxx b/sd/source/ui/dlg/navigatr.cxx new file mode 100644 index 000000000..78525efe5 --- /dev/null +++ b/sd/source/ui/dlg/navigatr.cxx @@ -0,0 +1,735 @@ +/* -*- 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 <memory> +#include <sal/config.h> + +#include <osl/file.hxx> +#include <tools/urlobj.hxx> +#include <sfx2/fcontnr.hxx> +#include <svl/stritem.hxx> +#include <sfx2/docfile.hxx> +#include <svl/intitem.hxx> +#include <sfx2/dispatch.hxx> + +#include <sfx2/viewfrm.hxx> + +#include <pres.hxx> +#include <navigatr.hxx> +#include <pgjump.hxx> +#include <app.hrc> + +#include <bitmaps.hlst> +#include <drawdoc.hxx> +#include <DrawDocShell.hxx> +#include <ViewShell.hxx> +#include <ViewShellBase.hxx> +#include <slideshow.hxx> +#include <FrameView.hxx> +#include <Window.hxx> + +#include <DrawViewShell.hxx> + +/** + * SdNavigatorWin - FloatingWindow + */ +SdNavigatorWin::SdNavigatorWin(weld::Widget* pParent, SfxBindings* pInBindings, SfxNavigator* pNavigatorDlg) + : PanelLayout(pParent, "NavigatorPanel", "modules/simpress/ui/navigatorpanel.ui") + , mxToolbox(m_xBuilder->weld_toolbar("toolbox")) + , mxTlbObjects(new SdPageObjsTLV(m_xBuilder->weld_tree_view("tree"))) + , mxLbDocs(m_xBuilder->weld_combo_box("documents")) + , mxDragModeMenu(m_xBuilder->weld_menu("dragmodemenu")) + , mxShapeMenu(m_xBuilder->weld_menu("shapemenu")) + , mxNavigatorDlg(pNavigatorDlg) + , mbDocImported ( false ) + // On changes of the DragType: adjust SelectionMode of TLB! + , meDragType ( NAVIGATOR_DRAGTYPE_EMBEDDED ) + , mpBindings ( pInBindings ) +{ + mxTlbObjects->SetViewFrame( mpBindings->GetDispatcher()->GetFrame() ); + + mxTlbObjects->connect_row_activated(LINK(this, SdNavigatorWin, ClickObjectHdl)); + mxTlbObjects->set_selection_mode(SelectionMode::Single); + + mxToolbox->connect_clicked(LINK(this, SdNavigatorWin, SelectToolboxHdl)); + mxToolbox->connect_menu_toggled(LINK(this, SdNavigatorWin, DropdownClickToolBoxHdl)); + + mxToolbox->set_item_menu("dragmode", mxDragModeMenu.get()); + mxDragModeMenu->connect_activate(LINK(this, SdNavigatorWin, MenuSelectHdl)); + + // Shape filter drop down menu. + mxToolbox->set_item_menu("shapes", mxShapeMenu.get()); + mxShapeMenu->connect_activate(LINK(this, SdNavigatorWin, ShapeFilterCallback)); + + mxTlbObjects->SetSdNavigator(this); + + // DragTypeListBox + mxLbDocs->set_size_request(42, -1); // set a nominal width so it takes width of surroundings + mxLbDocs->connect_changed(LINK(this, SdNavigatorWin, SelectDocumentHdl)); + + SetDragImage(); + + mxToolbox->connect_key_press(LINK(this, SdNavigatorWin, KeyInputHdl)); + mxTlbObjects->connect_key_press(LINK(this, SdNavigatorWin, KeyInputHdl)); + mxLbDocs->connect_key_press(LINK(this, SdNavigatorWin, KeyInputHdl)); +} + +void SdNavigatorWin::FirstFocus() +{ + // set focus to listbox, otherwise it is in the toolbox which is only useful + // for keyboard navigation + mxTlbObjects->grab_focus(); +} + +weld::Window* SdNavigatorWin::GetFrameWeld() const +{ + if (mxNavigatorDlg) + return mxNavigatorDlg->GetFrameWeld(); + return PanelLayout::GetFrameWeld(); +} + +void SdNavigatorWin::SetUpdateRequestFunctor(const UpdateRequestFunctor& rUpdateRequest) +{ + mpNavigatorCtrlItem.reset( new SdNavigatorControllerItem(SID_NAVIGATOR_STATE, this, mpBindings, rUpdateRequest) ); + mpPageNameCtrlItem.reset( new SdPageNameControllerItem(SID_NAVIGATOR_PAGENAME, this, mpBindings) ); + + // InitTlb; is initiated over Slot + if (rUpdateRequest) + rUpdateRequest(); +} + +SdNavigatorWin::~SdNavigatorWin() +{ + mpNavigatorCtrlItem.reset(); + mpPageNameCtrlItem.reset(); + mxDragModeMenu.reset(); + mxShapeMenu.reset(); + mxToolbox.reset(); + mxTlbObjects.reset(); + mxLbDocs.reset(); +} + +//when object is marked , fresh the corresponding entry tree . +void SdNavigatorWin::FreshTree( const SdDrawDocument* pDoc ) +{ + SdDrawDocument* pNonConstDoc = const_cast<SdDrawDocument*>(pDoc); // const as const can... + sd::DrawDocShell* pDocShell = pNonConstDoc->GetDocSh(); + const OUString& aDocShName( pDocShell->GetName() ); + OUString aDocName = pDocShell->GetMedium()->GetName(); + mxTlbObjects->Fill( pDoc, false, aDocName ); // Only normal pages + RefreshDocumentLB(); + mxLbDocs->set_active_text(aDocShName); +} + +void SdNavigatorWin::InitTreeLB( const SdDrawDocument* pDoc ) +{ + SdDrawDocument* pNonConstDoc = const_cast<SdDrawDocument*>(pDoc); // const as const can... + ::sd::DrawDocShell* pDocShell = pNonConstDoc->GetDocSh(); + OUString aDocShName( pDocShell->GetName() ); + ::sd::ViewShell* pViewShell = pDocShell->GetViewShell(); + + // Restore the 'ShowAllShapes' flag from the last time (in this session) + // that the navigator was shown. + if (pViewShell != nullptr) + { + ::sd::FrameView* pFrameView = pViewShell->GetFrameView(); + if (pFrameView != nullptr) + mxTlbObjects->SetShowAllShapes(pFrameView->IsNavigatorShowingAllShapes(), false); + } + + // Disable the shape filter drop down menu when there is a running slide + // show. + if (pViewShell!=nullptr && sd::SlideShow::IsRunning( pViewShell->GetViewShellBase() )) + mxToolbox->set_item_sensitive("shapes", false); + else + mxToolbox->set_item_sensitive("shapes", true); + + if( !mxTlbObjects->IsEqualToDoc( pDoc ) ) + { + OUString aDocName = pDocShell->GetMedium()->GetName(); + mxTlbObjects->clear(); + mxTlbObjects->Fill( pDoc, false, aDocName ); // only normal pages + + RefreshDocumentLB(); + mxLbDocs->set_active_text(aDocShName); + } + else + { + mxLbDocs->set_active(-1); + mxLbDocs->set_active_text(aDocShName); + +// commented in order to fix 30246 +// if( mxLbDocs->get_active() == -1 ) + { + RefreshDocumentLB(); + mxLbDocs->set_active_text(aDocShName); + } + } + + SfxViewFrame* pViewFrame = ( ( pViewShell && pViewShell->GetViewFrame() ) ? pViewShell->GetViewFrame() : SfxViewFrame::Current() ); + if( pViewFrame ) + pViewFrame->GetBindings().Invalidate(SID_NAVIGATOR_PAGENAME, true, true); +} + +/** + * DragType is set on dependence if a Drag is even possible. For example, + * under certain circumstances, it is not allowed to drag graphics (#31038#). + */ +NavigatorDragType SdNavigatorWin::GetNavigatorDragType() +{ + NavigatorDragType eDT = meDragType; + NavDocInfo* pInfo = GetDocInfo(); + + if( ( eDT == NAVIGATOR_DRAGTYPE_LINK ) && ( ( pInfo && !pInfo->HasName() ) || !mxTlbObjects->IsLinkableSelected() ) ) + eDT = NAVIGATOR_DRAGTYPE_NONE; + + return eDT; +} + +SdPageObjsTLV& SdNavigatorWin::GetObjects() +{ + return *mxTlbObjects; +} + +IMPL_LINK(SdNavigatorWin, SelectToolboxHdl, const OString&, rCommand, void) +{ + PageJump ePage = PAGE_NONE; + + if (rCommand == "first") + ePage = PAGE_FIRST; + else if (rCommand == "previous") + ePage = PAGE_PREVIOUS; + else if (rCommand == "next") + ePage = PAGE_NEXT; + else if (rCommand == "last") + ePage = PAGE_LAST; + else if (rCommand == "dragmode") + mxToolbox->set_menu_item_active("dragmode", !mxToolbox->get_menu_item_active("dragmode")); + else if (rCommand == "shapes") + mxToolbox->set_menu_item_active("shapes", !mxToolbox->get_menu_item_active("shapes")); + + if (ePage != PAGE_NONE) + { + SfxUInt16Item aItem( SID_NAVIGATOR_PAGE, static_cast<sal_uInt16>(ePage) ); + mpBindings->GetDispatcher()->ExecuteList(SID_NAVIGATOR_PAGE, + SfxCallMode::SLOT | SfxCallMode::RECORD, { &aItem }); + } +} + +IMPL_LINK(SdNavigatorWin, DropdownClickToolBoxHdl, const OString&, rCommand, void) +{ + if (!mxToolbox->get_menu_item_active(rCommand)) + return; + + if (rCommand == "dragmode") + { + NavDocInfo* pInfo = GetDocInfo(); + if( ( pInfo && !pInfo->HasName() ) || !mxTlbObjects->IsLinkableSelected() ) + { + mxDragModeMenu->set_sensitive(OString::number(NAVIGATOR_DRAGTYPE_LINK), false); + mxDragModeMenu->set_sensitive(OString::number(NAVIGATOR_DRAGTYPE_URL), false); + meDragType = NAVIGATOR_DRAGTYPE_EMBEDDED; + } + + mxDragModeMenu->set_active(OString::number(meDragType), true); + } + else if (rCommand == "shapes") + { + bool bAll = mxTlbObjects->GetShowAllShapes(); + mxShapeMenu->set_active("named", !bAll); + mxShapeMenu->set_active("all", bAll); + } +} + +IMPL_LINK_NOARG(SdNavigatorWin, ClickObjectHdl, weld::TreeView&, bool) +{ + if( !mbDocImported || mxLbDocs->get_active() != 0 ) + { + NavDocInfo* pInfo = GetDocInfo(); + + // if it is the active window, we jump to the page + if( pInfo && pInfo->IsActive() ) + { + OUString aStr(mxTlbObjects->get_selected_text()); + + if( !aStr.isEmpty() ) + { + SfxStringItem aItem( SID_NAVIGATOR_OBJECT, aStr ); + mpBindings->GetDispatcher()->ExecuteList( + SID_NAVIGATOR_OBJECT, + SfxCallMode::SLOT | SfxCallMode::RECORD, { &aItem }); + + // moved here from SetGetFocusHdl. Reset the + // focus only if something has been selected in the + // document. + SfxViewShell* pCurSh = SfxViewShell::Current(); + + if ( pCurSh ) + { + vcl::Window* pShellWnd = pCurSh->GetWindow(); + if ( pShellWnd ) + pShellWnd->GrabFocus(); + } + + // We navigated to an object, but the current shell may be + // still the slide sorter. Explicitly try to grab the draw + // shell focus, so follow-up operations work with the object + // and not with the whole slide. + sd::DrawDocShell* pDocShell = pInfo->mpDocShell; + if (pDocShell) + { + sd::ViewShell* pViewShell = pDocShell->GetViewShell(); + if (pViewShell) + { + vcl::Window* pWindow = pViewShell->GetActiveWindow(); + if (pWindow) + pWindow->GrabFocus(); + } + } + + if (!mxTlbObjects->IsNavigationGrabsFocus()) + // This is the case when keyboard navigation inside the + // navigator should continue to work. + mxTlbObjects->grab_focus(); + } + } + } + return false; +} + +IMPL_LINK_NOARG(SdNavigatorWin, SelectDocumentHdl, weld::ComboBox&, void) +{ + OUString aStrLb = mxLbDocs->get_active_text(); + tools::Long nPos = mxLbDocs->get_active(); + bool bFound = false; + ::sd::DrawDocShell* pDocShell = nullptr; + NavDocInfo* pInfo = GetDocInfo(); + + // is it a dragged object? + if( mbDocImported && nPos == 0 ) + { + // construct document in TLB + InsertFile( aStrLb ); + } + else if (pInfo) + { + pDocShell = pInfo->mpDocShell; + + bFound = true; + } + + if( bFound ) + { + SdDrawDocument* pDoc = pDocShell->GetDoc(); + if( !mxTlbObjects->IsEqualToDoc( pDoc ) ) + { + SdDrawDocument* pNonConstDoc = pDoc; // const as const can... + ::sd::DrawDocShell* pNCDocShell = pNonConstDoc->GetDocSh(); + OUString aDocName = pNCDocShell->GetMedium()->GetName(); + mxTlbObjects->clear(); + mxTlbObjects->Fill( pDoc, false, aDocName ); // only normal pages + } + } + + // check if link or url is possible + if( ( pInfo && !pInfo->HasName() ) || !mxTlbObjects->IsLinkableSelected() || ( meDragType != NAVIGATOR_DRAGTYPE_EMBEDDED ) ) + { + meDragType = NAVIGATOR_DRAGTYPE_EMBEDDED; + SetDragImage(); + } +} + +/** + * Set DrageType and set image accordingly to it. + */ +IMPL_LINK(SdNavigatorWin, MenuSelectHdl, const OString&, rIdent, void) +{ + sal_uInt32 nMenuId = rIdent.toUInt32(); + + NavigatorDragType eDT = static_cast<NavigatorDragType>(nMenuId); + if( meDragType == eDT ) + return; + + meDragType = eDT; + SetDragImage(); + + if( meDragType == NAVIGATOR_DRAGTYPE_URL ) + { + // patch, prevents endless loop + if (mxTlbObjects->count_selected_rows() > 1) + mxTlbObjects->unselect_all(); + + mxTlbObjects->set_selection_mode(SelectionMode::Single); + } + else + mxTlbObjects->set_selection_mode(SelectionMode::Multiple); +} + +IMPL_LINK( SdNavigatorWin, ShapeFilterCallback, const OString&, rIdent, void ) +{ + bool bShowAllShapes(mxTlbObjects->GetShowAllShapes()); + if (rIdent == "named") + bShowAllShapes = false; + else if (rIdent == "all") + bShowAllShapes = true; + else + OSL_FAIL("SdNavigatorWin::ShapeFilterCallback called for unknown menu entry"); + + mxTlbObjects->SetShowAllShapes(bShowAllShapes, true); + + // Remember the selection in the FrameView. + NavDocInfo* pInfo = GetDocInfo(); + if (pInfo == nullptr) + return; + + ::sd::DrawDocShell* pDocShell = pInfo->mpDocShell; + if (pDocShell != nullptr) + { + ::sd::ViewShell* pViewShell = pDocShell->GetViewShell(); + if (pViewShell != nullptr) + { + ::sd::FrameView* pFrameView = pViewShell->GetFrameView(); + if (pFrameView != nullptr) + { + pFrameView->SetIsNavigatorShowingAllShapes(bShowAllShapes); + } + } + } +} + +bool SdNavigatorWin::InsertFile(const OUString& rFileName) +{ + INetURLObject aURL( rFileName ); + + if( aURL.GetProtocol() == INetProtocol::NotValid ) + { + OUString aURLStr; + osl::FileBase::getFileURLFromSystemPath( rFileName, aURLStr ); + aURL = INetURLObject( aURLStr ); + } + + // get adjusted FileName + OUString aFileName( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + + if (aFileName.isEmpty()) + { + // show actual document again + maDropFileName = aFileName; + } + else + { + // show dragged-in document + std::shared_ptr<const SfxFilter> pFilter; + ErrCode nErr = ERRCODE_NONE; + + if (aFileName != maDropFileName) + { + SfxMedium aMed(aFileName, (StreamMode::READ | StreamMode::SHARE_DENYNONE)); + SfxFilterMatcher aMatch( "simpress" ); + aMed.UseInteractionHandler( true ); + nErr = aMatch.GuessFilter(aMed, pFilter); + } + + if ((pFilter && !nErr) || aFileName == maDropFileName) + { + // The medium may be opened with READ/WRITE. Therefore, we first + // check if it contains a Storage. + std::unique_ptr<SfxMedium> xMedium(new SfxMedium(aFileName, + StreamMode::READ | StreamMode::NOCREATE)); + + if (xMedium->IsStorage()) + { + // Now depending on mode: + // mxTlbObjects->set_selection_mode(SelectionMode::Multiple); + // handover of ownership of xMedium; + SdDrawDocument* pDropDoc = mxTlbObjects->GetBookmarkDoc(xMedium.release()); + + if (pDropDoc) + { + mxTlbObjects->clear(); + maDropFileName = aFileName; + + if( !mxTlbObjects->IsEqualToDoc( pDropDoc ) ) + { + // only normal pages + mxTlbObjects->Fill(pDropDoc, false, maDropFileName); + RefreshDocumentLB( &maDropFileName ); + } + } + } + else + { + return false; + } + } + else + { + return false; + } + } + + return true; +} + +void SdNavigatorWin::RefreshDocumentLB( const OUString* pDocName ) +{ + sal_Int32 nPos = 0; + + if( pDocName ) + { + if( mbDocImported ) + mxLbDocs->remove(0); + + mxLbDocs->insert_text(0, *pDocName); + mbDocImported = true; + } + else + { + nPos = mxLbDocs->get_active(); + if (nPos == -1) + nPos = 0; + + OUString aStr; + if( mbDocImported ) + aStr = mxLbDocs->get_text(0); + + mxLbDocs->clear(); + + // delete list of DocInfos + maDocList.clear(); + + if( mbDocImported ) + mxLbDocs->insert_text(0, aStr); + + ::sd::DrawDocShell* pCurrentDocShell = + dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + SfxObjectShell* pSfxDocShell = SfxObjectShell::GetFirst([](const SfxObjectShell*){return true;}, false); + while( pSfxDocShell ) + { + ::sd::DrawDocShell* pDocShell = dynamic_cast< ::sd::DrawDocShell *>( pSfxDocShell ); + if( pDocShell && !pDocShell->IsInDestruction() && ( pDocShell->GetCreateMode() != SfxObjectCreateMode::EMBEDDED ) ) + { + NavDocInfo aInfo ; + aInfo.mpDocShell = pDocShell; + + SfxMedium *pMedium = pDocShell->GetMedium(); + aStr = pMedium ? pMedium->GetName() : OUString(); + if( !aStr.isEmpty() ) + aInfo.SetName( true ); + else + aInfo.SetName( false ); + // at the moment, we use the name of the shell again (i.e. + // without path) since Koose thinks it is an error if the path + // is shown in url notation! + aStr = pDocShell->GetName(); + + mxLbDocs->append_text(aStr); + + if( pDocShell == pCurrentDocShell ) + aInfo.SetActive( true ); + else + aInfo.SetActive( false ); + + maDocList.push_back( aInfo ); + } + pSfxDocShell = SfxObjectShell::GetNext( *pSfxDocShell, [](const SfxObjectShell*){return true;}, false ); + } + } + mxLbDocs->set_active(nPos); +} + +OUString SdNavigatorWin::GetDragTypeSdBmpId(NavigatorDragType eDT) +{ + switch( eDT ) + { + case NAVIGATOR_DRAGTYPE_NONE: + return OUString(); + case NAVIGATOR_DRAGTYPE_URL: + return BMP_HYPERLINK; + case NAVIGATOR_DRAGTYPE_EMBEDDED: + return BMP_EMBEDDED; + case NAVIGATOR_DRAGTYPE_LINK: + return BMP_LINK; + default: OSL_FAIL( "No resource for DragType available!" ); + } + return OUString(); +} + +NavDocInfo* SdNavigatorWin::GetDocInfo() +{ + sal_uInt32 nPos = mxLbDocs->get_active(); + + if( mbDocImported ) + { + if( nPos == 0 ) + { + return nullptr; + } + nPos--; + } + + return nPos < maDocList.size() ? &(maDocList[ nPos ]) : nullptr; +} + +/** + * catch ESCAPE in order to end show + */ +IMPL_LINK(SdNavigatorWin, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + bool bConsumed = false; + + if (KEY_ESCAPE == rKEvt.GetKeyCode().GetCode()) + { + // during drag'n'drop we just stop the drag but do not close the navigator + if (!SdPageObjsTLV::IsInDrag()) + { + ::sd::ViewShellBase* pBase = ::sd::ViewShellBase::GetViewShellBase( mpBindings->GetDispatcher()->GetFrame()); + if (pBase) + sd::SlideShow::Stop(*pBase); + bConsumed = true; + } + } + + return bConsumed; +} + +void SdNavigatorWin::SetDragImage() +{ + mxToolbox->set_item_icon_name("dragmode", GetDragTypeSdBmpId(meDragType)); +} + +/** + * ControllerItem for Navigator + */ +SdNavigatorControllerItem::SdNavigatorControllerItem( + sal_uInt16 _nId, + SdNavigatorWin* pNavWin, + SfxBindings* _pBindings, + const SdNavigatorWin::UpdateRequestFunctor& rUpdateRequest) + : SfxControllerItem( _nId, *_pBindings ), + pNavigatorWin( pNavWin ), + maUpdateRequest(rUpdateRequest) +{ +} + +void SdNavigatorControllerItem::StateChangedAtToolBoxControl( sal_uInt16 nSId, + SfxItemState eState, const SfxPoolItem* pItem ) +{ + if( eState < SfxItemState::DEFAULT || nSId != SID_NAVIGATOR_STATE ) + return; + + // only if doc in LB is the active + NavDocInfo* pInfo = pNavigatorWin->GetDocInfo(); + if( !(pInfo && pInfo->IsActive()) ) + return; + + if (::sd::DrawDocShell* pDrawDocShell = pInfo->GetDrawDocShell()) + { + const auto pDrawViewShell = + static_cast<::sd::DrawViewShell *>(pDrawDocShell->GetViewShell()); + if (pDrawViewShell) + { + bool bEditModePage(pDrawViewShell->GetEditMode() == EditMode::Page); + pNavigatorWin->mxToolbox->set_sensitive(bEditModePage); + pNavigatorWin->mxLbDocs->set_sensitive(bEditModePage); + pNavigatorWin->mxTlbObjects->set_sensitive(bEditModePage); + } + } + + const SfxUInt32Item& rStateItem = dynamic_cast<const SfxUInt32Item&>(*pItem); + NavState nState = static_cast<NavState>(rStateItem.GetValue()); + + // First + if (nState & NavState::BtnFirstEnabled && + !pNavigatorWin->mxToolbox->get_item_sensitive("first")) + pNavigatorWin->mxToolbox->set_item_sensitive("first", true); + if (nState & NavState::BtnFirstDisabled && + pNavigatorWin->mxToolbox->get_item_sensitive("first")) + pNavigatorWin->mxToolbox->set_item_sensitive("first", false); + + // Prev + if (nState & NavState::BtnPrevEnabled && + !pNavigatorWin->mxToolbox->get_item_sensitive("previous")) + pNavigatorWin->mxToolbox->set_item_sensitive("previous", true); + if (nState & NavState::BtnPrevDisabled && + pNavigatorWin->mxToolbox->get_item_sensitive("previous")) + pNavigatorWin->mxToolbox->set_item_sensitive("previous", false); + + // Last + if (nState & NavState::BtnLastEnabled && + !pNavigatorWin->mxToolbox->get_item_sensitive("last")) + pNavigatorWin->mxToolbox->set_item_sensitive("last", true); + if (nState & NavState::BtnLastDisabled && + pNavigatorWin->mxToolbox->get_item_sensitive("last")) + pNavigatorWin->mxToolbox->set_item_sensitive("last", false); + + // Next + if (nState & NavState::BtnNextEnabled && + !pNavigatorWin->mxToolbox->get_item_sensitive("next")) + pNavigatorWin->mxToolbox->set_item_sensitive("next", true); + if (nState & NavState::BtnNextDisabled && + pNavigatorWin->mxToolbox->get_item_sensitive("next")) + pNavigatorWin->mxToolbox->set_item_sensitive("next", false); + + if (nState & NavState::TableUpdate) + { + // InitTlb; is initiated by Slot + if (maUpdateRequest) + maUpdateRequest(); + } +} + +/** + * ControllerItem for Navigator to show page in TreeLB + */ +SdPageNameControllerItem::SdPageNameControllerItem( + sal_uInt16 _nId, + SdNavigatorWin* pNavWin, + SfxBindings* _pBindings) + : SfxControllerItem( _nId, *_pBindings ), + pNavigatorWin( pNavWin ) +{ +} + +void SdPageNameControllerItem::StateChangedAtToolBoxControl( sal_uInt16 nSId, + SfxItemState eState, const SfxPoolItem* pItem ) +{ + if( eState < SfxItemState::DEFAULT || nSId != SID_NAVIGATOR_PAGENAME ) + return; + + // only if doc in LB is the active + NavDocInfo* pInfo = pNavigatorWin->GetDocInfo(); + if( !(pInfo && pInfo->IsActive()) ) + return; + + const SfxStringItem& rStateItem = dynamic_cast<const SfxStringItem&>(*pItem); + const OUString& aPageName = rStateItem.GetValue(); + + if( !pNavigatorWin->mxTlbObjects->HasSelectedChildren( aPageName ) ) + { + if (pNavigatorWin->mxTlbObjects->get_selection_mode() == SelectionMode::Multiple) + { + // because otherwise it is always additional select + pNavigatorWin->mxTlbObjects->unselect_all(); + } + pNavigatorWin->mxTlbObjects->SelectEntry( aPageName ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/paragr.cxx b/sd/source/ui/dlg/paragr.cxx new file mode 100644 index 000000000..be9df558e --- /dev/null +++ b/sd/source/ui/dlg/paragr.cxx @@ -0,0 +1,169 @@ +/* -*- 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 <svl/cjkoptions.hxx> +#include <svl/eitem.hxx> +#include <svl/intitem.hxx> + +#include <svx/dialogs.hrc> +#include <svx/svxids.hrc> +#include <svx/flagsdef.hxx> +#include <svx/xcolit.hxx> + +#include <paragr.hxx> +#include <sdattr.hrc> + +namespace { + +class SdParagraphNumTabPage : public SfxTabPage +{ +public: + SdParagraphNumTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet); + static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet ); + + static WhichRangesContainer GetRanges(); + + virtual bool FillItemSet( SfxItemSet* rSet ) override; + virtual void Reset( const SfxItemSet* rSet ) override; + +private: + bool mbModified; + std::unique_ptr<weld::CheckButton> m_xNewStartCB; + std::unique_ptr<weld::CheckButton> m_xNewStartNumberCB; + std::unique_ptr<weld::SpinButton> m_xNewStartNF; + + DECL_LINK( ImplNewStartHdl, weld::Toggleable&, void ); +}; + +} + +SdParagraphNumTabPage::SdParagraphNumTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rAttr) + : SfxTabPage(pPage, pController, "modules/sdraw/ui/paranumberingtab.ui", "DrawParaNumbering", &rAttr) + , mbModified(false) + , m_xNewStartCB(m_xBuilder->weld_check_button("checkbuttonCB_NEW_START")) + , m_xNewStartNumberCB(m_xBuilder->weld_check_button("checkbuttonCB_NUMBER_NEW_START")) + , m_xNewStartNF(m_xBuilder->weld_spin_button("spinbuttonNF_NEW_START")) +{ + m_xNewStartCB->connect_toggled(LINK(this, SdParagraphNumTabPage, ImplNewStartHdl)); + m_xNewStartNumberCB->connect_toggled(LINK(this, SdParagraphNumTabPage, ImplNewStartHdl)); +} + +std::unique_ptr<SfxTabPage> SdParagraphNumTabPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet * rAttrSet) +{ + return std::make_unique<SdParagraphNumTabPage>(pPage, pController, *rAttrSet); +} + +WhichRangesContainer SdParagraphNumTabPage::GetRanges() +{ + return WhichRangesContainer(svl::Items<ATTR_PARANUMBERING_START, ATTR_PARANUMBERING_END>); +} + +bool SdParagraphNumTabPage::FillItemSet( SfxItemSet* rSet ) +{ + if (m_xNewStartCB->get_state_changed_from_saved() || + m_xNewStartNumberCB->get_state_changed_from_saved()|| + m_xNewStartNF->get_value_changed_from_saved()) + { + mbModified = true; + bool bNewStartChecked = TRISTATE_TRUE == m_xNewStartCB->get_state(); + bool bNumberNewStartChecked = TRISTATE_TRUE == m_xNewStartNumberCB->get_state(); + rSet->Put(SfxBoolItem(ATTR_NUMBER_NEWSTART, bNewStartChecked)); + + const sal_Int16 nStartAt = static_cast<sal_Int16>(m_xNewStartNF->get_value()); + rSet->Put(SfxInt16Item(ATTR_NUMBER_NEWSTART_AT, bNumberNewStartChecked && bNewStartChecked ? nStartAt : -1)); + } + + return mbModified; +} + +void SdParagraphNumTabPage::Reset( const SfxItemSet* rSet ) +{ + SfxItemState eItemState = rSet->GetItemState( ATTR_NUMBER_NEWSTART ); + if(eItemState > SfxItemState::DEFAULT ) + { + const SfxBoolItem& rStart = rSet->Get(ATTR_NUMBER_NEWSTART); + m_xNewStartCB->set_state( rStart.GetValue() ? TRISTATE_TRUE : TRISTATE_FALSE ); + } + else + { + m_xNewStartCB->set_state(TRISTATE_INDET); + m_xNewStartCB->set_sensitive(false); + } + m_xNewStartCB->save_state(); + + eItemState = rSet->GetItemState( ATTR_NUMBER_NEWSTART_AT); + if( eItemState > SfxItemState::DEFAULT ) + { + sal_Int16 nNewStart = rSet->Get(ATTR_NUMBER_NEWSTART_AT).GetValue(); + m_xNewStartNumberCB->set_active(-1 != nNewStart); + if(-1 == nNewStart) + nNewStart = 1; + + m_xNewStartNF->set_value(nNewStart); + } + else + { + m_xNewStartCB->set_state(TRISTATE_INDET); + } + ImplNewStartHdl(*m_xNewStartCB); + m_xNewStartNF->save_value(); + m_xNewStartNumberCB->save_state(); + mbModified = false; +} + +IMPL_LINK_NOARG(SdParagraphNumTabPage, ImplNewStartHdl, weld::Toggleable&, void) +{ + bool bEnable = m_xNewStartCB->get_active(); + m_xNewStartNumberCB->set_sensitive(bEnable); + m_xNewStartNF->set_sensitive(bEnable && m_xNewStartNumberCB->get_active()); +} + +SdParagraphDlg::SdParagraphDlg(weld::Window* pParent, const SfxItemSet* pAttr) + : SfxTabDialogController(pParent, "modules/sdraw/ui/drawparadialog.ui", + "DrawParagraphPropertiesDialog", pAttr) +{ + AddTabPage( "labelTP_PARA_STD", RID_SVXPAGE_STD_PARAGRAPH); + + if( SvtCJKOptions::IsAsianTypographyEnabled() ) + AddTabPage( "labelTP_PARA_ASIAN", RID_SVXPAGE_PARA_ASIAN); + else + RemoveTabPage( "labelTP_PARA_ASIAN" ); + + AddTabPage( "labelTP_PARA_ALIGN", RID_SVXPAGE_ALIGN_PARAGRAPH); + + static const bool bShowParaNumbering = ( getenv( "SD_SHOW_NUMBERING_PAGE" ) != nullptr ); + if( bShowParaNumbering ) + AddTabPage( "labelNUMBERING", SdParagraphNumTabPage::Create, SdParagraphNumTabPage::GetRanges ); + else + RemoveTabPage( "labelNUMBERING" ); + + AddTabPage("labelTP_TABULATOR", RID_SVXPAGE_TABULATOR); +} + +void SdParagraphDlg::PageCreated(const OString& rId, SfxTabPage &rPage) +{ + SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool())); + if (rId == "labelTP_PARA_STD") + { + aSet.Put(SfxUInt32Item(SID_SVXSTDPARAGRAPHTABPAGE_ABSLINEDIST, MM50/2)); + rPage.PageCreated(aSet); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/present.cxx b/sd/source/ui/dlg/present.cxx new file mode 100644 index 000000000..73932ba62 --- /dev/null +++ b/sd/source/ui/dlg/present.cxx @@ -0,0 +1,323 @@ +/* -*- 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 <svl/itemset.hxx> +#include <svl/intitem.hxx> +#include <svl/eitem.hxx> +#include <svl/stritem.hxx> +#include <vcl/svapp.hxx> + +#include <sdattr.hrc> +#include <present.hxx> +#include <cusshow.hxx> +#include <customshowlist.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; + +SdStartPresentationDlg::SdStartPresentationDlg(weld::Window* pWindow, const SfxItemSet& rInAttrs, + const std::vector<OUString> &rPageNames, SdCustomShowList* pCSList) + : GenericDialogController(pWindow, "modules/simpress/ui/presentationdialog.ui", "PresentationDialog") + , pCustomShowList(pCSList) + , rOutAttrs(rInAttrs) + , mnMonitors(0) + , m_xRbtAll(m_xBuilder->weld_radio_button("allslides")) + , m_xRbtAtDia(m_xBuilder->weld_radio_button("from")) + , m_xRbtCustomshow(m_xBuilder->weld_radio_button("customslideshow")) + , m_xLbDias(m_xBuilder->weld_combo_box("from_cb")) + , m_xLbCustomshow(m_xBuilder->weld_combo_box("customslideshow_cb")) + , m_xRbtStandard(m_xBuilder->weld_radio_button("default")) + , m_xRbtWindow(m_xBuilder->weld_radio_button("window")) + , m_xRbtAuto(m_xBuilder->weld_radio_button("auto")) + , m_xTmfPause(m_xBuilder->weld_formatted_spin_button("pauseduration")) + , m_xFormatter(new weld::TimeFormatter(*m_xTmfPause)) + , m_xCbxAutoLogo(m_xBuilder->weld_check_button("showlogo")) + , m_xCbxManuel(m_xBuilder->weld_check_button("manualslides")) + , m_xCbxMousepointer(m_xBuilder->weld_check_button("pointervisible")) + , m_xCbxPen(m_xBuilder->weld_check_button("pointeraspen")) + , m_xCbxAnimationAllowed(m_xBuilder->weld_check_button("animationsallowed")) + , m_xCbxChangePage(m_xBuilder->weld_check_button("changeslidesbyclick")) + , m_xCbxAlwaysOnTop(m_xBuilder->weld_check_button("alwaysontop")) + , m_xFtMonitor(m_xBuilder->weld_label("presdisplay_label")) + , m_xLBMonitor(m_xBuilder->weld_combo_box("presdisplay_cb")) + , m_xMonitor(m_xBuilder->weld_label("monitor_str")) + , m_xAllMonitors(m_xBuilder->weld_label("allmonitors_str")) + , m_xMonitorExternal(m_xBuilder->weld_label("externalmonitor_str")) + , m_xExternal(m_xBuilder->weld_label("external_str")) +{ + m_xFormatter->SetExtFormat(ExtTimeFieldFormat::LongDuration); + m_xFormatter->EnableEmptyField(false); + + Link<weld::Toggleable&,void> aLink( LINK( this, SdStartPresentationDlg, ChangeRangeHdl ) ); + + m_xRbtAll->connect_toggled( aLink ); + m_xRbtAtDia->connect_toggled( aLink ); + m_xRbtCustomshow->connect_toggled( aLink ); + + aLink = LINK( this, SdStartPresentationDlg, ClickWindowPresentationHdl ); + m_xRbtStandard->connect_toggled( aLink ); + m_xRbtWindow->connect_toggled( aLink ); + m_xRbtAuto->connect_toggled( aLink ); + + m_xTmfPause->connect_value_changed( LINK( this, SdStartPresentationDlg, ChangePauseHdl ) ); + + // fill Listbox with page names + for (const auto& rPageName : rPageNames) + m_xLbDias->append_text(rPageName); + + if( pCustomShowList ) + { + sal_uInt16 nPosToSelect = pCustomShowList->GetCurPos(); + SdCustomShow* pCustomShow; + // fill Listbox with CustomShows + for( pCustomShow = pCustomShowList->First(); + pCustomShow != nullptr; + pCustomShow = pCustomShowList->Next() ) + { + m_xLbCustomshow->append_text( pCustomShow->GetName() ); + } + m_xLbCustomshow->set_active( nPosToSelect ); + pCustomShowList->Seek( nPosToSelect ); + } + else + m_xRbtCustomshow->set_sensitive(false); + + if( static_cast<const SfxBoolItem&>( rOutAttrs.Get( ATTR_PRESENT_CUSTOMSHOW ) ).GetValue() && pCSList ) + m_xRbtCustomshow->set_active(true); + else if( static_cast<const SfxBoolItem&>( rOutAttrs.Get( ATTR_PRESENT_ALL ) ).GetValue() ) + m_xRbtAll->set_active(true); + else + m_xRbtAtDia->set_active(true); + + m_xLbDias->set_active_text( static_cast<const SfxStringItem&>( rOutAttrs.Get( ATTR_PRESENT_DIANAME ) ).GetValue() ); + m_xCbxManuel->set_active( static_cast<const SfxBoolItem&>( rOutAttrs.Get( ATTR_PRESENT_MANUEL ) ).GetValue() ); + m_xCbxMousepointer->set_active( static_cast<const SfxBoolItem&>( rOutAttrs.Get( ATTR_PRESENT_MOUSE ) ).GetValue() ); + m_xCbxPen->set_active( static_cast<const SfxBoolItem&>( rOutAttrs.Get( ATTR_PRESENT_PEN ) ).GetValue() ); + m_xCbxAnimationAllowed->set_active( static_cast<const SfxBoolItem&>( rOutAttrs.Get( ATTR_PRESENT_ANIMATION_ALLOWED ) ).GetValue() ); + m_xCbxChangePage->set_active( static_cast<const SfxBoolItem&>( rOutAttrs.Get( ATTR_PRESENT_CHANGE_PAGE ) ).GetValue() ); + m_xCbxAlwaysOnTop->set_active( static_cast<const SfxBoolItem&>( rOutAttrs.Get( ATTR_PRESENT_ALWAYS_ON_TOP ) ).GetValue() ); + + const bool bEndless = static_cast<const SfxBoolItem&>( rOutAttrs.Get( ATTR_PRESENT_ENDLESS ) ).GetValue(); + const bool bWindow = !static_cast<const SfxBoolItem&>( rOutAttrs.Get( ATTR_PRESENT_FULLSCREEN ) ).GetValue(); + const tools::Long nPause = static_cast<const SfxUInt32Item&>( rOutAttrs.Get( ATTR_PRESENT_PAUSE_TIMEOUT ) ).GetValue(); + + m_xFormatter->SetTime( tools::Time( 0, 0, nPause ) ); + // set cursor in timefield to end + m_xTmfPause->set_position(-1); + + m_xCbxAutoLogo->set_active( static_cast<const SfxBoolItem&>( rOutAttrs.Get( ATTR_PRESENT_SHOW_PAUSELOGO ) ).GetValue() ); + + if( bWindow ) + m_xRbtWindow->set_active(true); + else if( bEndless ) + m_xRbtAuto->set_active(true); + else + m_xRbtStandard->set_active(true); + + InitMonitorSettings(); + + ChangeRangeHdl(*m_xRbtCustomshow); + + ClickWindowPresentationHdl(*m_xRbtStandard); + ChangePause(); +} + +SdStartPresentationDlg::~SdStartPresentationDlg() +{ +} + +OUString SdStartPresentationDlg::GetDisplayName( sal_Int32 nDisplay, + DisplayType eType ) +{ + OUString aName; + + switch ( eType ) + { + case EXTERNAL_IS_NUMBER: + aName = m_xExternal->get_label(); + break; + case MONITOR_IS_EXTERNAL: + aName = m_xMonitorExternal->get_label(); + break; + default: + case MONITOR_NORMAL: + aName = m_xMonitor->get_label(); + break; + } + aName = aName.replaceFirst( "%1", OUString::number( nDisplay ) ); + + return aName; +} + +/// Store display index together with name in user data +sal_Int32 SdStartPresentationDlg::InsertDisplayEntry(const OUString &aName, + sal_Int32 nDisplay) +{ + m_xLBMonitor->append(OUString::number(nDisplay), aName); + return m_xLBMonitor->get_count() - 1; +} + +void SdStartPresentationDlg::InitMonitorSettings() +{ + try + { + m_xFtMonitor->show(); + m_xLBMonitor->show(); + + mnMonitors = Application::GetScreenCount(); + + if( mnMonitors <= 1 ) + { + m_xFtMonitor->set_sensitive( false ); + m_xLBMonitor->set_sensitive( false ); + } + else + { + bool bUnifiedDisplay = Application::IsUnifiedDisplay(); + sal_Int32 nExternalIndex = Application::GetDisplayExternalScreen(); + + sal_Int32 nSelectedIndex (-1); + sal_Int32 nDefaultExternalIndex (-1); + const sal_Int32 nDefaultSelectedDisplay ( + static_cast<const SfxInt32Item&>( rOutAttrs.Get( ATTR_PRESENT_DISPLAY ) ).GetValue()); + + // Un-conditionally add a version for '0' the default external display + sal_Int32 nInsertedEntry; + + // Initial entry - the auto-detected external monitor + OUString aName = GetDisplayName( nExternalIndex + 1, EXTERNAL_IS_NUMBER); + nInsertedEntry = InsertDisplayEntry( aName, 0 ); + if( nDefaultSelectedDisplay == 0) + nSelectedIndex = nInsertedEntry; + + // The user data contains the real setting + for( sal_Int32 nDisplay = 0; nDisplay < mnMonitors; nDisplay++ ) + { + aName = GetDisplayName( nDisplay + 1, + nDisplay == nExternalIndex ? + MONITOR_IS_EXTERNAL : MONITOR_NORMAL ); + nInsertedEntry = InsertDisplayEntry( aName, nDisplay + 1 ); + + // Remember the index of the default selection. + if( nDisplay + 1 == nDefaultSelectedDisplay ) + nSelectedIndex = nInsertedEntry; + + // Remember index of the default display. + if( nDisplay == nExternalIndex ) + nDefaultExternalIndex = nInsertedEntry; + } + + if( bUnifiedDisplay ) + { + nInsertedEntry = InsertDisplayEntry( m_xAllMonitors->get_label(), -1 ); + if( nDefaultSelectedDisplay == -1 ) + nSelectedIndex = nInsertedEntry; + } + + if (nSelectedIndex < 0) + { + if (nExternalIndex < 0) + nSelectedIndex = 0; + else + nSelectedIndex = nDefaultExternalIndex; + } + + m_xLBMonitor->set_active(nSelectedIndex); + } + } + catch( Exception& ) + { + } +} + +/** + * sets the selected attributes of the dialog + */ +void SdStartPresentationDlg::GetAttr( SfxItemSet& rAttr ) +{ + rAttr.Put( SfxBoolItem ( ATTR_PRESENT_ALL, m_xRbtAll->get_active() ) ); + rAttr.Put( SfxBoolItem ( ATTR_PRESENT_CUSTOMSHOW, m_xRbtCustomshow->get_active() ) ); + rAttr.Put( SfxStringItem ( ATTR_PRESENT_DIANAME, m_xLbDias->get_active_text() ) ); + rAttr.Put( SfxBoolItem ( ATTR_PRESENT_MANUEL, m_xCbxManuel->get_active() ) ); + rAttr.Put( SfxBoolItem ( ATTR_PRESENT_MOUSE, m_xCbxMousepointer->get_active() ) ); + rAttr.Put( SfxBoolItem ( ATTR_PRESENT_PEN, m_xCbxPen->get_active() ) ); + rAttr.Put( SfxBoolItem ( ATTR_PRESENT_ANIMATION_ALLOWED, m_xCbxAnimationAllowed->get_active() ) ); + rAttr.Put( SfxBoolItem ( ATTR_PRESENT_CHANGE_PAGE, m_xCbxChangePage->get_active() ) ); + rAttr.Put( SfxBoolItem ( ATTR_PRESENT_ALWAYS_ON_TOP, m_xCbxAlwaysOnTop->get_active() ) ); + rAttr.Put( SfxBoolItem ( ATTR_PRESENT_FULLSCREEN, !m_xRbtWindow->get_active() ) ); + rAttr.Put( SfxBoolItem ( ATTR_PRESENT_ENDLESS, m_xRbtAuto->get_active() ) ); + rAttr.Put( SfxUInt32Item ( ATTR_PRESENT_PAUSE_TIMEOUT, m_xFormatter->GetTime().GetMSFromTime() / 1000 ) ); + rAttr.Put( SfxBoolItem ( ATTR_PRESENT_SHOW_PAUSELOGO, m_xCbxAutoLogo->get_active() ) ); + + int nPos = m_xLBMonitor->get_active(); + if (nPos != -1) + rAttr.Put(SfxInt32Item(ATTR_PRESENT_DISPLAY, m_xLBMonitor->get_id(nPos).toInt32())); + + nPos = m_xLbCustomshow->get_active(); + if (nPos != -1) + pCustomShowList->Seek( nPos ); +} + +/** + * Handler: Enabled/Disabled Listbox "Dias" + */ +IMPL_LINK_NOARG(SdStartPresentationDlg, ChangeRangeHdl, weld::Toggleable&, void) +{ + m_xLbDias->set_sensitive( m_xRbtAtDia->get_active() ); + m_xLbCustomshow->set_sensitive( m_xRbtCustomshow->get_active() ); +} + +/** + * Handler: Enabled/Disabled Checkbox "AlwaysOnTop" + */ +IMPL_LINK_NOARG(SdStartPresentationDlg, ClickWindowPresentationHdl, weld::Toggleable&, void) +{ + const bool bAuto = m_xRbtAuto->get_active(); + const bool bWindow = m_xRbtWindow->get_active(); + + m_xTmfPause->set_sensitive( bAuto ); + m_xCbxAutoLogo->set_sensitive( bAuto && ( m_xFormatter->GetTime().GetMSFromTime() > 0 ) ); + + const bool bDisplay = !bWindow && ( mnMonitors > 1 ); + m_xFtMonitor->set_sensitive( bDisplay ); + m_xLBMonitor->set_sensitive( bDisplay ); + + if( bWindow ) + { + m_xCbxAlwaysOnTop->set_sensitive(false); + m_xCbxAlwaysOnTop->set_active(false); + } + else + m_xCbxAlwaysOnTop->set_sensitive(true); +} + +/** + * Handler: Enabled/Disabled Checkbox "AlwaysOnTop" + */ +IMPL_LINK_NOARG(SdStartPresentationDlg, ChangePauseHdl, weld::FormattedSpinButton&, void) +{ + ChangePause(); +} + +void SdStartPresentationDlg::ChangePause() +{ + m_xCbxAutoLogo->set_sensitive(m_xRbtAuto->get_active() && ( m_xFormatter->GetTime().GetMSFromTime() > 0 )); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/prltempl.cxx b/sd/source/ui/dlg/prltempl.cxx new file mode 100644 index 000000000..869f13c83 --- /dev/null +++ b/sd/source/ui/dlg/prltempl.cxx @@ -0,0 +1,305 @@ +/* -*- 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 <sal/log.hxx> +#include <svx/dialogs.hrc> +#include <svx/svxids.hrc> +#include <editeng/flstitem.hxx> +#include <svx/drawitem.hxx> +#include <svl/style.hxx> +#include <svx/svdobjkind.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/numitem.hxx> +#include <svl/cjkoptions.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/sfxdlg.hxx> + +#include <strings.hrc> +#include <sdresid.hxx> +#include <prltempl.hxx> +#include <bulmaper.hxx> +#include <svl/intitem.hxx> +#include <svx/flagsdef.hxx> + +#define IS_OUTLINE(x) (x >= PresentationObjects::Outline_1 && x <= PresentationObjects::Outline_9) + +/** + * Constructor of Tab dialog: appends pages to the dialog + */ +SdPresLayoutTemplateDlg::SdPresLayoutTemplateDlg(SfxObjectShell const * pDocSh, + weld::Window* pParent, + bool bBackground, + SfxStyleSheetBase& rStyleBase, + PresentationObjects _ePO, + SfxStyleSheetBasePool* pSSPool) + : SfxTabDialogController(pParent, "modules/sdraw/ui/drawprtldialog.ui", "DrawPRTLDialog") + , mpDocShell(pDocSh) + , ePO(_ePO) + , aInputSet(*rStyleBase.GetItemSet().GetPool(), svl::Items<SID_PARAM_NUM_PRESET, SID_PARAM_CUR_NUM_LEVEL>) +{ + const SfxItemSet* pOrgSet(&rStyleBase.GetItemSet()); + + if( IS_OUTLINE(ePO)) + { + // Unfortunately, the Itemsets of our style sheets are not discrete... + const WhichRangesContainer& pPtr = pOrgSet->GetRanges(); + sal_uInt16 p1, p2; + for( sal_Int32 i = 0; i < pPtr.size(); ++i ) + { + p1 = pPtr[i].first; + p2 = pPtr[i].second; + + // first, we make it discrete + while(i < pPtr.size() - 1 && (pPtr[i+1].first - p2 == 1)) + { + p2 = pPtr[i+1].second; + ++i; + } + aInputSet.MergeRange( p1, p2 ); + } + + aInputSet.Put( rStyleBase.GetItemSet() ); + + // need parent-relationship + const SfxItemSet* pParentItemSet = rStyleBase.GetItemSet().GetParent(); + if( pParentItemSet ) + aInputSet.SetParent( pParentItemSet ); + + pOutSet.reset( new SfxItemSet( rStyleBase.GetItemSet() ) ); + pOutSet->ClearItem(); + + // If there is no bullet item in this stylesheet, we get it + // from 'Outline 1' style sheet. + const SfxPoolItem *pItem = nullptr; + if( SfxItemState::SET != aInputSet.GetItemState(EE_PARA_NUMBULLET, false, &pItem )) + { + OUString aStyleName(SdResId(STR_PSEUDOSHEET_OUTLINE) + " 1"); + SfxStyleSheetBase* pFirstStyleSheet = pSSPool->Find( aStyleName, SfxStyleFamily::Pseudo); + + if(pFirstStyleSheet) + if( SfxItemState::SET == pFirstStyleSheet->GetItemSet().GetItemState(EE_PARA_NUMBULLET, false, &pItem) ) + aInputSet.Put( *pItem ); + } + + // preselect selected layer in dialog + aInputSet.Put( SfxUInt16Item( SID_PARAM_CUR_NUM_LEVEL, 1<<GetOutlineLevel())); + + SetInputSet(&aInputSet); + } + else { + SetInputSet(pOrgSet); + } + + SvxColorListItem const *pColorListItem = mpDocShell->GetItem( SID_COLOR_TABLE ); + SvxGradientListItem const *pGradientListItem = mpDocShell->GetItem( SID_GRADIENT_LIST ); + SvxBitmapListItem const *pBitmapListItem = mpDocShell->GetItem( SID_BITMAP_LIST ); + SvxPatternListItem const *pPatternListItem = mpDocShell->GetItem( SID_PATTERN_LIST ); + SvxHatchListItem const *pHatchListItem = mpDocShell->GetItem( SID_HATCH_LIST ); + SvxDashListItem const *pDashListItem = mpDocShell->GetItem( SID_DASH_LIST ); + SvxLineEndListItem const *pLineEndListItem = mpDocShell->GetItem( SID_LINEEND_LIST ); + + pColorTab = pColorListItem->GetColorList(); + pDashList = pDashListItem->GetDashList(); + pLineEndList = pLineEndListItem->GetLineEndList(); + pGradientList = pGradientListItem->GetGradientList(); + pHatchingList = pHatchListItem->GetHatchList(); + pBitmapList = pBitmapListItem->GetBitmapList(); + pPatternList = pPatternListItem->GetPatternList(); + + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + + AddTabPage( "RID_SVXPAGE_LINE", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_LINE ), nullptr ); + AddTabPage( "RID_SVXPAGE_AREA", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_AREA ), nullptr ); + AddTabPage( "RID_SVXPAGE_SHADOW", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_SHADOW ), nullptr ); + AddTabPage( "RID_SVXPAGE_TRANSPARENCE", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_TRANSPARENCE ), nullptr ); + AddTabPage( "RID_SVXPAGE_CHAR_NAME", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_CHAR_NAME ), nullptr ); + AddTabPage( "RID_SVXPAGE_CHAR_EFFECTS", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_CHAR_EFFECTS ), nullptr ); + AddTabPage( "RID_SVXPAGE_STD_PARAGRAPH", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_STD_PARAGRAPH ), nullptr ); + AddTabPage( "RID_SVXPAGE_TEXTATTR", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_TEXTATTR ), nullptr ); + AddTabPage( "RID_SVXPAGE_PICK_BULLET", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_PICK_BULLET ), nullptr ); + AddTabPage( "RID_SVXPAGE_PICK_SINGLE_NUM", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_PICK_SINGLE_NUM ), nullptr ); + AddTabPage( "RID_SVXPAGE_PICK_BMP", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_PICK_BMP ), nullptr ); + AddTabPage( "RID_SVXPAGE_NUM_OPTIONS", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_NUM_OPTIONS ), nullptr ); + AddTabPage( "RID_SVXPAGE_TABULATOR", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_TABULATOR ), nullptr ); + AddTabPage( "RID_SVXPAGE_PARA_ASIAN", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_PARA_ASIAN ), nullptr ); + AddTabPage( "RID_SVXPAGE_ALIGN_PARAGRAPH", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_ALIGN_PARAGRAPH ), nullptr ); + AddTabPage( "RID_SVXPAGE_BKG", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_BKG ), nullptr); + + if( !SvtCJKOptions::IsAsianTypographyEnabled() ) + RemoveTabPage( "RID_SVXPAGE_PARA_ASIAN" ); + + if (bBackground) + { + RemoveTabPage( "RID_SVXPAGE_LINE"); + + RemoveTabPage( "RID_SVXPAGE_SHADOW"); + RemoveTabPage( "RID_SVXPAGE_TRANSPARENCE"); + RemoveTabPage( "RID_SVXPAGE_CHAR_NAME"); + RemoveTabPage( "RID_SVXPAGE_CHAR_EFFECTS"); + RemoveTabPage( "RID_SVXPAGE_STD_PARAGRAPH"); + RemoveTabPage( "RID_SVXPAGE_TEXTATTR"); + RemoveTabPage( "RID_SVXPAGE_PICK_BULLET"); + RemoveTabPage( "RID_SVXPAGE_PICK_SINGLE_NUM"); + RemoveTabPage( "RID_SVXPAGE_PICK_BMP"); + RemoveTabPage( "RID_SVXPAGE_NUM_OPTIONS"); + RemoveTabPage( "RID_SVXPAGE_TABULATOR"); + RemoveTabPage( "RID_SVXPAGE_ALIGN_PARAGRAPH"); + RemoveTabPage( "RID_SVXPAGE_PARA_ASIAN" ); + RemoveTabPage( "RID_SVXPAGE_BKG" ); + } + + // set title and add corresponding pages to dialog + OUString aTitle; + + switch( ePO ) + { + case PresentationObjects::Title: + aTitle = SdResId(STR_PSEUDOSHEET_TITLE); + break; + + case PresentationObjects::Subtitle: + aTitle = SdResId(STR_PSEUDOSHEET_SUBTITLE); + break; + + case PresentationObjects::Background: + aTitle = SdResId(STR_PSEUDOSHEET_BACKGROUND); + break; + + case PresentationObjects::BackgroundObjects: + aTitle = SdResId(STR_PSEUDOSHEET_BACKGROUNDOBJECTS); + break; + + case PresentationObjects::Outline_1: + case PresentationObjects::Outline_2: + case PresentationObjects::Outline_3: + case PresentationObjects::Outline_4: + case PresentationObjects::Outline_5: + case PresentationObjects::Outline_6: + case PresentationObjects::Outline_7: + case PresentationObjects::Outline_8: + case PresentationObjects::Outline_9: + aTitle = SdResId(STR_PSEUDOSHEET_OUTLINE) + " " + + OUString::number( static_cast<int>(ePO) - static_cast<int>(PresentationObjects::Outline_1) + 1 ); + break; + + case PresentationObjects::Notes: + aTitle = SdResId(STR_PSEUDOSHEET_NOTES); + break; + } + m_xDialog->set_title(aTitle); +} + +SdPresLayoutTemplateDlg::~SdPresLayoutTemplateDlg() +{ +} + +void SdPresLayoutTemplateDlg::PageCreated(const OString& rId, SfxTabPage &rPage) +{ + SfxAllItemSet aSet(*(aInputSet.GetPool())); + + if (rId == "RID_SVXPAGE_LINE") + { + aSet.Put (SvxColorListItem(pColorTab,SID_COLOR_TABLE)); + aSet.Put (SvxDashListItem(pDashList,SID_DASH_LIST)); + aSet.Put (SvxLineEndListItem(pLineEndList,SID_LINEEND_LIST)); + aSet.Put (SfxUInt16Item(SID_DLG_TYPE,1)); + rPage.PageCreated(aSet); + } + else if (rId == "RID_SVXPAGE_AREA") + { + aSet.Put (SvxColorListItem(pColorTab,SID_COLOR_TABLE)); + aSet.Put (SvxGradientListItem(pGradientList,SID_GRADIENT_LIST)); + aSet.Put (SvxHatchListItem(pHatchingList,SID_HATCH_LIST)); + aSet.Put (SvxBitmapListItem(pBitmapList,SID_BITMAP_LIST)); + aSet.Put (SvxPatternListItem(pPatternList,SID_PATTERN_LIST)); + aSet.Put (SfxUInt16Item(SID_PAGE_TYPE,0)); + aSet.Put (SfxUInt16Item(SID_DLG_TYPE,1)); + aSet.Put (SfxUInt16Item(SID_TABPAGE_POS,0)); + rPage.PageCreated(aSet); + } + else if (rId == "RID_SVXPAGE_SHADOW") + { + aSet.Put (SvxColorListItem(pColorTab,SID_COLOR_TABLE)); + aSet.Put (SfxUInt16Item(SID_PAGE_TYPE,0)); + aSet.Put (SfxUInt16Item(SID_DLG_TYPE,1)); + rPage.PageCreated(aSet); + } + else if (rId == "RID_SVXPAGE_TRANSPARENCE") + { + aSet.Put (SfxUInt16Item(SID_PAGE_TYPE,0)); + aSet.Put (SfxUInt16Item(SID_DLG_TYPE,1)); + rPage.PageCreated(aSet); + } + else if (rId == "RID_SVXPAGE_CHAR_NAME") + { + SvxFontListItem aItem(*static_cast<const SvxFontListItem*>(mpDocShell->GetItem( SID_ATTR_CHAR_FONTLIST) ) ); + aSet.Put (SvxFontListItem( aItem.GetFontList(), SID_ATTR_CHAR_FONTLIST)); + rPage.PageCreated(aSet); + } + else if (rId == "RID_SVXPAGE_CHAR_EFFECTS") + { + rPage.PageCreated(aSet); + } + else if (rId == "RID_SVXPAGE_TEXTATTR") + { + aSet.Put(CntUInt16Item(SID_SVXTEXTATTRPAGE_OBJKIND, static_cast<sal_uInt16>(SdrObjKind::Text))); + rPage.PageCreated(aSet); + } + else if (rId == "RID_SVXPAGE_BKG") + { + aSet.Put(SfxUInt32Item(SID_FLAG_TYPE,static_cast<sal_uInt32>(SvxBackgroundTabFlags::SHOW_CHAR_BKGCOLOR))); + rPage.PageCreated(aSet); + } +} + +const SfxItemSet* SdPresLayoutTemplateDlg::GetOutputItemSet() const +{ + if (pOutSet) + { + pOutSet->Put(*SfxTabDialogController::GetOutputItemSet()); + + const SvxNumBulletItem *pSvxNumBulletItem = pOutSet->GetItemIfSet(EE_PARA_NUMBULLET, false); + if (pSvxNumBulletItem) + SdBulletMapper::MapFontsInNumRule( const_cast<SvxNumRule&>(pSvxNumBulletItem->GetNumRule()), *pOutSet ); + return pOutSet.get(); + } + else + return SfxTabDialogController::GetOutputItemSet(); +} + +sal_uInt16 SdPresLayoutTemplateDlg::GetOutlineLevel() const +{ + switch( ePO ) + { + case PresentationObjects::Outline_1: return 0; + case PresentationObjects::Outline_2: return 1; + case PresentationObjects::Outline_3: return 2; + case PresentationObjects::Outline_4: return 3; + case PresentationObjects::Outline_5: return 4; + case PresentationObjects::Outline_6: return 5; + case PresentationObjects::Outline_7: return 6; + case PresentationObjects::Outline_8: return 7; + case PresentationObjects::Outline_9: return 8; + default: + SAL_WARN( "sd", "Wrong Po! [CL]"); + } + return 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/prntopts.cxx b/sd/source/ui/dlg/prntopts.cxx new file mode 100644 index 000000000..4b50875bb --- /dev/null +++ b/sd/source/ui/dlg/prntopts.cxx @@ -0,0 +1,235 @@ +/* -*- 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 <sdattr.hrc> +#include <optsitem.hxx> +#include <prntopts.hxx> +#include <app.hrc> +#include <svl/intitem.hxx> + +/** + * dialog to adjust print options + */ +SdPrintOptions::SdPrintOptions(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rInAttrs) + : SfxTabPage(pPage, pController, "modules/simpress/ui/prntopts.ui", "prntopts", &rInAttrs) + , m_xFrmContent(m_xBuilder->weld_frame("contentframe")) + , m_xCbxDraw(m_xBuilder->weld_check_button("drawingcb")) + , m_xCbxNotes(m_xBuilder->weld_check_button("notecb")) + , m_xCbxHandout(m_xBuilder->weld_check_button("handoutcb")) + , m_xCbxOutline(m_xBuilder->weld_check_button("outlinecb")) + , m_xRbtColor(m_xBuilder->weld_radio_button("defaultrb")) + , m_xRbtGrayscale(m_xBuilder->weld_radio_button("grayscalerb")) + , m_xRbtBlackWhite(m_xBuilder->weld_radio_button("blackwhiterb")) + , m_xCbxPagename(m_xBuilder->weld_check_button("pagenmcb")) + , m_xCbxDate(m_xBuilder->weld_check_button("datecb")) + , m_xCbxTime(m_xBuilder->weld_check_button("timecb")) + , m_xCbxHiddenPages(m_xBuilder->weld_check_button("hiddenpgcb")) + , m_xRbtDefault(m_xBuilder->weld_radio_button("pagedefaultrb")) + , m_xRbtPagesize(m_xBuilder->weld_radio_button("fittopgrb")) + , m_xRbtPagetile(m_xBuilder->weld_radio_button("tilepgrb")) + , m_xRbtBooklet(m_xBuilder->weld_radio_button("brouchrb")) + , m_xCbxFront(m_xBuilder->weld_check_button("frontcb")) + , m_xCbxBack(m_xBuilder->weld_check_button("backcb")) + , m_xCbxPaperbin(m_xBuilder->weld_check_button("papertryfrmprntrcb")) +{ + Link<weld::Toggleable&,void> aLink = LINK( this, SdPrintOptions, ClickBookletHdl ); + m_xRbtDefault->connect_toggled( aLink ); + m_xRbtPagesize->connect_toggled( aLink ); + m_xRbtPagetile->connect_toggled( aLink ); + m_xRbtBooklet->connect_toggled( aLink ); + + aLink = LINK( this, SdPrintOptions, ClickCheckboxHdl ); + m_xCbxDraw->connect_toggled( aLink ); + m_xCbxNotes->connect_toggled( aLink ); + m_xCbxHandout->connect_toggled( aLink ); + m_xCbxOutline->connect_toggled( aLink ); + +#ifndef MACOSX + SetDrawMode(); +#endif +} + +SdPrintOptions::~SdPrintOptions() +{ +} + +bool SdPrintOptions::FillItemSet( SfxItemSet* rAttrs ) +{ + if( m_xCbxDraw->get_state_changed_from_saved() || + m_xCbxNotes->get_state_changed_from_saved() || + m_xCbxHandout->get_state_changed_from_saved() || + m_xCbxOutline->get_state_changed_from_saved() || + m_xCbxDate->get_state_changed_from_saved() || + m_xCbxTime->get_state_changed_from_saved() || + m_xCbxPagename->get_state_changed_from_saved() || + m_xCbxHiddenPages->get_state_changed_from_saved() || + m_xRbtPagesize->get_state_changed_from_saved() || + m_xRbtPagetile->get_state_changed_from_saved() || + m_xRbtBooklet->get_state_changed_from_saved() || + m_xCbxFront->get_state_changed_from_saved() || + m_xCbxBack->get_state_changed_from_saved() || + m_xCbxPaperbin->get_state_changed_from_saved() || + m_xRbtColor->get_state_changed_from_saved() || + m_xRbtGrayscale->get_state_changed_from_saved()|| + m_xRbtBlackWhite->get_state_changed_from_saved()) + { + SdOptionsPrintItem aOptions; + + aOptions.GetOptionsPrint().SetDraw( m_xCbxDraw->get_active() ); + aOptions.GetOptionsPrint().SetNotes( m_xCbxNotes->get_active() ); + aOptions.GetOptionsPrint().SetHandout( m_xCbxHandout->get_active() ); + aOptions.GetOptionsPrint().SetOutline( m_xCbxOutline->get_active() ); + aOptions.GetOptionsPrint().SetDate( m_xCbxDate->get_active() ); + aOptions.GetOptionsPrint().SetTime( m_xCbxTime->get_active() ); + aOptions.GetOptionsPrint().SetPagename( m_xCbxPagename->get_active() ); + aOptions.GetOptionsPrint().SetHiddenPages( m_xCbxHiddenPages->get_active() ); + aOptions.GetOptionsPrint().SetPagesize( m_xRbtPagesize->get_active() ); + aOptions.GetOptionsPrint().SetPagetile( m_xRbtPagetile->get_active() ); + aOptions.GetOptionsPrint().SetBooklet( m_xRbtBooklet->get_active() ); + aOptions.GetOptionsPrint().SetFrontPage( m_xCbxFront->get_active() ); + aOptions.GetOptionsPrint().SetBackPage( m_xCbxBack->get_active() ); + aOptions.GetOptionsPrint().SetPaperbin( m_xCbxPaperbin->get_active() ); + + sal_uInt16 nQuality = 0; // Standard, also Color + if( m_xRbtGrayscale->get_active() ) + nQuality = 1; + if( m_xRbtBlackWhite->get_active() ) + nQuality = 2; + aOptions.GetOptionsPrint().SetOutputQuality( nQuality ); + + rAttrs->Put( aOptions ); + + return true; + } + return false; +} + +void SdPrintOptions::Reset( const SfxItemSet* rAttrs ) +{ + const SdOptionsPrintItem* pPrintOpts = rAttrs->GetItemIfSet( ATTR_OPTIONS_PRINT, false); + if( pPrintOpts ) + { + m_xCbxDraw->set_active( pPrintOpts->GetOptionsPrint().IsDraw() ); + m_xCbxNotes->set_active( pPrintOpts->GetOptionsPrint().IsNotes() ); + m_xCbxHandout->set_active( pPrintOpts->GetOptionsPrint().IsHandout() ); + m_xCbxOutline->set_active( pPrintOpts->GetOptionsPrint().IsOutline() ); + m_xCbxDate->set_active( pPrintOpts->GetOptionsPrint().IsDate() ); + m_xCbxTime->set_active( pPrintOpts->GetOptionsPrint().IsTime() ); + m_xCbxPagename->set_active( pPrintOpts->GetOptionsPrint().IsPagename() ); + m_xCbxHiddenPages->set_active( pPrintOpts->GetOptionsPrint().IsHiddenPages() ); + m_xRbtPagesize->set_active( pPrintOpts->GetOptionsPrint().IsPagesize() ); + m_xRbtPagetile->set_active( pPrintOpts->GetOptionsPrint().IsPagetile() ); + m_xRbtBooklet->set_active( pPrintOpts->GetOptionsPrint().IsBooklet() ); + m_xCbxFront->set_active( pPrintOpts->GetOptionsPrint().IsFrontPage() ); + m_xCbxBack->set_active( pPrintOpts->GetOptionsPrint().IsBackPage() ); + m_xCbxPaperbin->set_active( pPrintOpts->GetOptionsPrint().IsPaperbin() ); + + if( !m_xRbtPagesize->get_active() && + !m_xRbtPagetile->get_active() && + !m_xRbtBooklet->get_active() ) + { + m_xRbtDefault->set_active(true); + } + + sal_uInt16 nQuality = pPrintOpts->GetOptionsPrint().GetOutputQuality(); + if( nQuality == 0 ) + m_xRbtColor->set_active(true); + else if( nQuality == 1 ) + m_xRbtGrayscale->set_active(true); + else + m_xRbtBlackWhite->set_active(true); + } + m_xCbxDraw->save_state(); + m_xCbxNotes->save_state(); + m_xCbxHandout->save_state(); + m_xCbxOutline->save_state(); + m_xCbxDate->save_state(); + m_xCbxTime->save_state(); + m_xCbxPagename->save_state(); + m_xCbxHiddenPages->save_state(); + m_xRbtPagesize->save_state(); + m_xRbtPagetile->save_state(); + m_xRbtBooklet->save_state(); + m_xCbxPaperbin->save_state(); + m_xRbtColor->save_state(); + m_xRbtGrayscale->save_state(); + m_xRbtBlackWhite->save_state(); + + updateControls(); +} + +std::unique_ptr<SfxTabPage> SdPrintOptions::Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rOutAttrs ) +{ + return std::make_unique<SdPrintOptions>( pPage, pController, *rOutAttrs ); +} + +IMPL_LINK(SdPrintOptions, ClickCheckboxHdl, weld::Toggleable&, rCbx, void) +{ + // there must be at least one of them checked + if( !m_xCbxDraw->get_active() && !m_xCbxNotes->get_active() && !m_xCbxOutline->get_active() && !m_xCbxHandout->get_active() ) + rCbx.set_active(true); + + updateControls(); +} + +IMPL_LINK_NOARG(SdPrintOptions, ClickBookletHdl, weld::Toggleable&, void) +{ + updateControls(); +} + +void SdPrintOptions::updateControls() +{ + m_xCbxFront->set_sensitive(m_xRbtBooklet->get_active()); + m_xCbxBack->set_sensitive(m_xRbtBooklet->get_active()); + + m_xCbxDate->set_sensitive( !m_xRbtBooklet->get_active() ); + m_xCbxTime->set_sensitive( !m_xRbtBooklet->get_active() ); + + m_xCbxPagename->set_sensitive( !m_xRbtBooklet->get_active() && (m_xCbxDraw->get_active() || m_xCbxNotes->get_active() || m_xCbxOutline->get_active()) ); +} + +void SdPrintOptions::SetDrawMode() +{ + if (m_xCbxNotes->get_visible()) + { + m_xFrmContent->hide(); + } +} + +void SdPrintOptions::PageCreated (const SfxAllItemSet& +#ifdef MACOSX + aSet +#endif + ) +{ +#ifdef MACOSX + const SfxUInt32Item* pFlagItem = aSet.GetItem<SfxUInt32Item>(SID_SDMODE_FLAG, false); + if (pFlagItem) + { + sal_uInt32 nFlags=pFlagItem->GetValue(); + if ( ( nFlags & SD_DRAW_MODE ) == SD_DRAW_MODE ) + SetDrawMode(); + } +#else + SetDrawMode(); +#endif +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/sdabstdlg.cxx b/sd/source/ui/dlg/sdabstdlg.cxx new file mode 100644 index 000000000..2b686a3e8 --- /dev/null +++ b/sd/source/ui/dlg/sdabstdlg.cxx @@ -0,0 +1,55 @@ +/* -*- 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 <sdabstdlg.hxx> + +#include <osl/module.hxx> + +typedef SdAbstractDialogFactory* (*SdFuncPtrCreateDialogFactory)(); + +#ifndef DISABLE_DYNLOADING + +extern "C" { +static void thisModule() {} +} + +#else + +extern "C" SdAbstractDialogFactory* SdCreateDialogFactory(); + +#endif + +SdAbstractDialogFactory* SdAbstractDialogFactory::Create() +{ + SdFuncPtrCreateDialogFactory fp = nullptr; +#ifndef DISABLE_DYNLOADING + static ::osl::Module aDialogLibrary; + static constexpr OUStringLiteral sLibName(u"" SDUI_DLL_NAME); + if (aDialogLibrary.is() || aDialogLibrary.loadRelative(&thisModule, sLibName)) + fp = reinterpret_cast<SdAbstractDialogFactory*(SAL_CALL*)()>( + aDialogLibrary.getFunctionSymbol("SdCreateDialogFactory")); +#else + fp = SdCreateDialogFactory; +#endif + if (fp) + return fp(); + return nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/sddlgfact.cxx b/sd/source/ui/dlg/sddlgfact.cxx new file mode 100644 index 000000000..c526df75d --- /dev/null +++ b/sd/source/ui/dlg/sddlgfact.cxx @@ -0,0 +1,739 @@ +/* -*- 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 "sddlgfact.hxx" +#include <BreakDlg.hxx> +#include <copydlg.hxx> +#include <custsdlg.hxx> +#include <dlg_char.hxx> +#include <dlgpage.hxx> +#include <dlgfield.hxx> +#include <dlgsnap.hxx> +#include <layeroptionsdlg.hxx> +#include <inspagob.hxx> +#include <morphdlg.hxx> +#include <OutlineBulletDlg.hxx> +#include <paragr.hxx> +#include <present.hxx> +#include "RemoteDialog.hxx" +#include <prltempl.hxx> +#include <sdpreslt.hxx> +#include <tabtempl.hxx> +#include <tpaction.hxx> +#include <vectdlg.hxx> +#include <tpoption.hxx> +#include <prntopts.hxx> +#include <pubdlg.hxx> +#include <masterlayoutdlg.hxx> +#include <headerfooterdlg.hxx> +#include "PhotoAlbumDialog.hxx" +#include <vcl/virdev.hxx> + +short AbstractSvxBulletAndPositionDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short SdAbstractGenericDialog_Impl::Execute() +{ + return m_xDlg->run(); +} + +BitmapEx SdAbstractGenericDialog_Impl::createScreenshot() const +{ + VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString SdAbstractGenericDialog_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +const SfxItemSet* AbstractSvxBulletAndPositionDlg_Impl::GetOutputItemSet( SfxItemSet* pSet ) const +{ + return m_xDlg->GetOutputItemSet( pSet ); +} + +bool AbstractSvxBulletAndPositionDlg_Impl::IsApplyToMaster() +{ + return m_xDlg->IsApplyToMaster(); +} + +bool AbstractSvxBulletAndPositionDlg_Impl::IsSlideScope() +{ + return m_xDlg->IsSlideScope(); +} + +short AbstractCopyDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSdCustomShowDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short SdPresLayoutTemplateDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +bool SdPresLayoutTemplateDlg_Impl::StartExecuteAsync(AsyncContext &rCtx) +{ + return SfxTabDialogController::runAsync(m_xDlg, rCtx.maEndDialogFn); +} + +short AbstractSdModifyFieldDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSdSnapLineDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSdInsertLayerDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSdInsertPagesObjsDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractMorphDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSdStartPresDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSdPresLayoutDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short SdAbstractSfxDialog_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSdVectorizeDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSdPublishingDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractHeaderFooterDialog_Impl::Execute() +{ + return m_xDlg->run(); +} + +bool AbstractHeaderFooterDialog_Impl::StartExecuteAsync(AsyncContext &rCtx) +{ + return weld::DialogController::runAsync(m_xDlg, rCtx.maEndDialogFn); +} + +BitmapEx AbstractHeaderFooterDialog_Impl::createScreenshot() const +{ + VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractHeaderFooterDialog_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +short AbstractBulletDialog_Impl::Execute() +{ + return m_xDlg->run(); +} + +bool AbstractBulletDialog_Impl::StartExecuteAsync(AsyncContext &rCtx) +{ + return SfxTabDialogController::runAsync(m_xDlg, rCtx.maEndDialogFn); +} + +AbstractBreakDlg_Impl::AbstractBreakDlg_Impl(std::unique_ptr<::sd::BreakDlg> pDlg) + : m_xDlg(std::move(pDlg)) +{ +} + +short AbstractBreakDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +BitmapEx AbstractBreakDlg_Impl::createScreenshot() const +{ + VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractBreakDlg_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +AbstractMasterLayoutDialog_Impl::AbstractMasterLayoutDialog_Impl(std::unique_ptr<::sd::MasterLayoutDialog> pDlg) + : m_xDlg(std::move(pDlg)) +{ +} + +short AbstractMasterLayoutDialog_Impl::Execute() +{ + return m_xDlg->run(); +} + +BitmapEx AbstractMasterLayoutDialog_Impl::createScreenshot() const +{ + VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractMasterLayoutDialog_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +void AbstractCopyDlg_Impl::GetAttr( SfxItemSet& rOutAttrs ) +{ + m_xDlg->GetAttr( rOutAttrs ); +} + +BitmapEx AbstractCopyDlg_Impl::createScreenshot() const +{ + VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractCopyDlg_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +bool AbstractSdCustomShowDlg_Impl::IsCustomShow() const +{ + return m_xDlg->IsCustomShow(); +} + +BitmapEx AbstractSdCustomShowDlg_Impl::createScreenshot() const +{ + VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractSdCustomShowDlg_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +short SdAbstractTabController_Impl::Execute() +{ + return m_xDlg->run(); +} + +void SdAbstractTabController_Impl::SetCurPageId( const OString &rName ) +{ + m_xDlg->SetCurPageId( rName ); +} + +const SfxItemSet* SdAbstractTabController_Impl::GetOutputItemSet() const +{ + return m_xDlg->GetOutputItemSet(); +} + +WhichRangesContainer SdAbstractTabController_Impl::GetInputRanges(const SfxItemPool& pItem ) +{ + return m_xDlg->GetInputRanges( pItem ); +} + +void SdAbstractTabController_Impl::SetInputSet( const SfxItemSet* pInSet ) +{ + m_xDlg->SetInputSet( pInSet ); +} + +bool SdAbstractTabController_Impl::StartExecuteAsync(AsyncContext &rCtx) +{ + return SfxTabDialogController::runAsync(m_xDlg, rCtx.maEndDialogFn); +} + +//From class Window. +void SdAbstractTabController_Impl::SetText( const OUString& rStr ) +{ + m_xDlg->set_title(rStr); +} + +BitmapEx SdAbstractTabController_Impl::createScreenshot() const +{ + VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString SdAbstractTabController_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +void AbstractBulletDialog_Impl::SetCurPageId( const OString& rName ) +{ + m_xDlg->SetCurPageId( rName ); +} + +const SfxItemSet* AbstractBulletDialog_Impl::GetOutputItemSet() const +{ + return static_cast< ::sd::OutlineBulletDlg*>(m_xDlg.get())->GetBulletOutputItemSet(); +} + +WhichRangesContainer AbstractBulletDialog_Impl::GetInputRanges(const SfxItemPool& pItem ) +{ + return m_xDlg->GetInputRanges(pItem); +} + +void AbstractBulletDialog_Impl::SetInputSet( const SfxItemSet* pInSet ) +{ + m_xDlg->SetInputSet(pInSet); +} + +void AbstractBulletDialog_Impl::SetText( const OUString& rStr ) +{ + m_xDlg->set_title(rStr); +} + +BitmapEx AbstractBulletDialog_Impl::createScreenshot() const +{ + VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractBulletDialog_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +void SdPresLayoutTemplateDlg_Impl::SetCurPageId( const OString& rName ) +{ + m_xDlg->SetCurPageId( rName ); +} + +const SfxItemSet* SdPresLayoutTemplateDlg_Impl::GetOutputItemSet() const +{ + return m_xDlg->GetOutputItemSet(); +} + +WhichRangesContainer SdPresLayoutTemplateDlg_Impl::GetInputRanges(const SfxItemPool& pItem ) +{ + return m_xDlg->GetInputRanges( pItem ); +} + +void SdPresLayoutTemplateDlg_Impl::SetInputSet( const SfxItemSet* pInSet ) +{ + m_xDlg->SetInputSet( pInSet ); +} + +void SdPresLayoutTemplateDlg_Impl::SetText( const OUString& rStr ) +{ + m_xDlg->set_title(rStr); +} + +BitmapEx SdPresLayoutTemplateDlg_Impl::createScreenshot() const +{ + VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString SdPresLayoutTemplateDlg_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +SvxFieldData* AbstractSdModifyFieldDlg_Impl::GetField() +{ + return m_xDlg->GetField(); +} + +SfxItemSet AbstractSdModifyFieldDlg_Impl::GetItemSet() +{ + return m_xDlg->GetItemSet(); +} + +BitmapEx AbstractSdModifyFieldDlg_Impl::createScreenshot() const +{ + VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractSdModifyFieldDlg_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +void AbstractSdSnapLineDlg_Impl::GetAttr(SfxItemSet& rOutAttrs) +{ + m_xDlg->GetAttr(rOutAttrs); +} + +void AbstractSdSnapLineDlg_Impl::HideRadioGroup() +{ + m_xDlg->HideRadioGroup(); +} + +void AbstractSdSnapLineDlg_Impl::HideDeleteBtn() +{ + m_xDlg->HideDeleteBtn(); +} + +void AbstractSdSnapLineDlg_Impl::SetInputFields(bool bEnableX, bool bEnableY) +{ + m_xDlg->SetInputFields(bEnableX, bEnableY); +} + +void AbstractSdSnapLineDlg_Impl::SetText( const OUString& rStr ) +{ + m_xDlg->set_title(rStr); +} + +BitmapEx AbstractSdSnapLineDlg_Impl::createScreenshot() const +{ + VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractSdSnapLineDlg_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +void AbstractSdInsertLayerDlg_Impl::GetAttr( SfxItemSet& rOutAttrs ) +{ + m_xDlg->GetAttr(rOutAttrs); +} + +void AbstractSdInsertLayerDlg_Impl::SetHelpId( const OString& rHelpId ) +{ + m_xDlg->set_help_id(rHelpId); +} + +BitmapEx AbstractSdInsertLayerDlg_Impl::createScreenshot() const +{ + VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractSdInsertLayerDlg_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +std::vector<OUString> AbstractSdInsertPagesObjsDlg_Impl::GetList(const sal_uInt16 nType) +{ + return m_xDlg->GetList(nType); +} + +bool AbstractSdInsertPagesObjsDlg_Impl::IsLink() +{ + return m_xDlg->IsLink(); +} + +bool AbstractSdInsertPagesObjsDlg_Impl::IsRemoveUnnecessaryMasterPages() const +{ + return m_xDlg->IsRemoveUnnecessaryMasterPages(); +} + +BitmapEx AbstractSdInsertPagesObjsDlg_Impl::createScreenshot() const +{ + VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractSdInsertPagesObjsDlg_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +void AbstractMorphDlg_Impl::SaveSettings() const +{ + m_xDlg->SaveSettings(); +} + +sal_uInt16 AbstractMorphDlg_Impl::GetFadeSteps() const +{ + return m_xDlg->GetFadeSteps(); +} + +bool AbstractMorphDlg_Impl::IsAttributeFade() const +{ + return m_xDlg->IsAttributeFade(); +} + +bool AbstractMorphDlg_Impl::IsOrientationFade() const +{ + return m_xDlg->IsOrientationFade(); +} + +BitmapEx AbstractMorphDlg_Impl::createScreenshot() const +{ + VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractMorphDlg_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +void AbstractSdStartPresDlg_Impl::GetAttr( SfxItemSet& rOutAttrs ) +{ + m_xDlg->GetAttr(rOutAttrs); +} + +BitmapEx AbstractSdStartPresDlg_Impl::createScreenshot() const +{ + VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractSdStartPresDlg_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +void AbstractSdPresLayoutDlg_Impl::GetAttr( SfxItemSet& rOutAttrs ) +{ + m_xDlg->GetAttr(rOutAttrs); +} + +BitmapEx AbstractSdPresLayoutDlg_Impl::createScreenshot() const +{ + VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractSdPresLayoutDlg_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +const SfxItemSet* SdAbstractSfxDialog_Impl::GetOutputItemSet() const +{ + return m_xDlg->GetOutputItemSet(); +} + +void SdAbstractSfxDialog_Impl::SetText( const OUString& rStr ) +{ + m_xDlg->set_title(rStr); +} + +const GDIMetaFile& AbstractSdVectorizeDlg_Impl::GetGDIMetaFile() const +{ + return m_xDlg->GetGDIMetaFile(); +} + +BitmapEx AbstractSdVectorizeDlg_Impl::createScreenshot() const +{ + VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractSdVectorizeDlg_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +void AbstractSdPublishingDlg_Impl::GetParameterSequence( css::uno::Sequence< css::beans::PropertyValue >& rParams ) +{ + m_xDlg->GetParameterSequence( rParams ); +} + +BitmapEx AbstractSdPublishingDlg_Impl::createScreenshot() const +{ + VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractSdPublishingDlg_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +//-------------- SdAbstractDialogFactory implementation-------------- + +VclPtr<AbstractSvxBulletAndPositionDlg> SdAbstractDialogFactory_Impl::CreateSvxBulletAndPositionDlg(weld::Window* pParent, const SfxItemSet* pAttr, ::sd::View* pView) +{ + return VclPtr<AbstractSvxBulletAndPositionDlg_Impl>::Create(std::make_unique<SvxBulletAndPositionDlg>(pParent, *pAttr, pView)); +} + +VclPtr<VclAbstractDialog> SdAbstractDialogFactory_Impl::CreateBreakDlg( + weld::Window* pParent, + ::sd::DrawView* pDrView, + ::sd::DrawDocShell* pShell, + sal_uLong nSumActionCount, + sal_uLong nObjCount ) +{ + return VclPtr<AbstractBreakDlg_Impl>::Create(std::make_unique<::sd::BreakDlg>(pParent, pDrView, pShell, nSumActionCount, nObjCount)); +} + +VclPtr<AbstractCopyDlg> SdAbstractDialogFactory_Impl::CreateCopyDlg(weld::Window* pParent, + const SfxItemSet& rInAttrs, + ::sd::View* pView ) +{ + return VclPtr<AbstractCopyDlg_Impl>::Create(std::make_unique<::sd::CopyDlg>(pParent, rInAttrs, pView)); +} + +VclPtr<AbstractSdCustomShowDlg> SdAbstractDialogFactory_Impl::CreateSdCustomShowDlg(weld::Window* pParent, SdDrawDocument& rDrawDoc ) +{ + return VclPtr<AbstractSdCustomShowDlg_Impl>::Create(std::make_unique<SdCustomShowDlg>(pParent, rDrawDoc)); +} + +VclPtr<SfxAbstractTabDialog> SdAbstractDialogFactory_Impl::CreateSdTabCharDialog(weld::Window* pParent, const SfxItemSet* pAttr, SfxObjectShell* pDocShell) +{ + return VclPtr<SdAbstractTabController_Impl>::Create(std::make_shared<SdCharDlg>(pParent, pAttr, pDocShell)); +} + +VclPtr<SfxAbstractTabDialog> SdAbstractDialogFactory_Impl::CreateSdTabPageDialog(weld::Window* pParent, const SfxItemSet* pAttr, SfxObjectShell* pDocShell, bool bAreaPage, bool bIsImpressDoc, bool bIsImpressMaster ) +{ + return VclPtr<SdAbstractTabController_Impl>::Create(std::make_shared<SdPageDlg>(pDocShell, pParent, pAttr, bAreaPage, bIsImpressDoc, bIsImpressMaster)); +} + +VclPtr<AbstractSdModifyFieldDlg> SdAbstractDialogFactory_Impl::CreateSdModifyFieldDlg(weld::Window* pParent, const SvxFieldData* pInField, const SfxItemSet& rSet) +{ + return VclPtr<AbstractSdModifyFieldDlg_Impl>::Create(std::make_unique<SdModifyFieldDlg>(pParent, pInField, rSet)); +} + +VclPtr<AbstractSdSnapLineDlg> SdAbstractDialogFactory_Impl::CreateSdSnapLineDlg(weld::Window* pParent, const SfxItemSet& rInAttrs, ::sd::View* pView) +{ + return VclPtr<AbstractSdSnapLineDlg_Impl>::Create(std::make_unique<SdSnapLineDlg>(pParent, rInAttrs, pView)); +} + +VclPtr<AbstractSdInsertLayerDlg> SdAbstractDialogFactory_Impl::CreateSdInsertLayerDlg(weld::Window* pParent, const SfxItemSet& rInAttrs, bool bDeletable, const OUString& aStr) +{ + return VclPtr<AbstractSdInsertLayerDlg_Impl>::Create(std::make_unique<SdInsertLayerDlg>(pParent, rInAttrs, bDeletable, aStr)); +} + +VclPtr<AbstractSdInsertPagesObjsDlg> SdAbstractDialogFactory_Impl::CreateSdInsertPagesObjsDlg(weld::Window* pParent, const SdDrawDocument* pDoc, SfxMedium* pSfxMedium, const OUString& rFileName) +{ + return VclPtr<AbstractSdInsertPagesObjsDlg_Impl>::Create(std::make_unique<SdInsertPagesObjsDlg>(pParent, pDoc, pSfxMedium, rFileName)); +} + +VclPtr<AbstractMorphDlg> SdAbstractDialogFactory_Impl::CreateMorphDlg(weld::Window* pParent, const SdrObject* pObj1, const SdrObject* pObj2) +{ + return VclPtr<AbstractMorphDlg_Impl>::Create(std::make_unique<::sd::MorphDlg>(pParent, pObj1, pObj2)); +} + +VclPtr<SfxAbstractTabDialog> SdAbstractDialogFactory_Impl::CreateSdOutlineBulletTabDlg(weld::Window* pParent, const SfxItemSet* pAttr, ::sd::View* pView) +{ + return VclPtr<AbstractBulletDialog_Impl>::Create(std::make_shared<::sd::OutlineBulletDlg>(pParent, pAttr, pView)); +} + +VclPtr<SfxAbstractTabDialog> SdAbstractDialogFactory_Impl::CreateSdParagraphTabDlg(weld::Window* pParent, const SfxItemSet* pAttr ) +{ + return VclPtr<SdAbstractTabController_Impl>::Create(std::make_shared<SdParagraphDlg>(pParent, pAttr)); +} + +VclPtr<AbstractSdStartPresDlg> SdAbstractDialogFactory_Impl::CreateSdStartPresentationDlg(weld::Window* pParent, + const SfxItemSet& rInAttrs, const std::vector<OUString> &rPageNames, SdCustomShowList* pCSList) +{ + return VclPtr<AbstractSdStartPresDlg_Impl>::Create(std::make_unique<SdStartPresentationDlg>(pParent, rInAttrs, rPageNames, pCSList)); +} + +VclPtr<VclAbstractDialog> SdAbstractDialogFactory_Impl::CreateRemoteDialog(weld::Window* pParent) +{ + return VclPtr<SdAbstractGenericDialog_Impl>::Create(std::make_unique<::sd::RemoteDialog>(pParent)); +} + +VclPtr<SfxAbstractTabDialog> SdAbstractDialogFactory_Impl::CreateSdPresLayoutTemplateDlg(SfxObjectShell* pDocSh, weld::Window* pParent, bool bBackgroundDlg, SfxStyleSheetBase& rStyleBase, PresentationObjects ePO, SfxStyleSheetBasePool* pSSPool) +{ + return VclPtr<SdPresLayoutTemplateDlg_Impl>::Create(std::make_shared<SdPresLayoutTemplateDlg>(pDocSh, pParent, bBackgroundDlg, rStyleBase, ePO, pSSPool)); +} + +VclPtr<AbstractSdPresLayoutDlg> SdAbstractDialogFactory_Impl::CreateSdPresLayoutDlg(weld::Window* pParent, ::sd::DrawDocShell* pDocShell, const SfxItemSet& rInAttrs) +{ + return VclPtr<AbstractSdPresLayoutDlg_Impl>::Create(std::make_unique<SdPresLayoutDlg>(pDocShell, pParent, rInAttrs)); +} + +VclPtr<SfxAbstractTabDialog> SdAbstractDialogFactory_Impl::CreateSdTabTemplateDlg(weld::Window* pParent, const SfxObjectShell* pDocShell, SfxStyleSheetBase& rStyleBase, SdrModel* pModel, SdrView* pView) +{ + return VclPtr<SdAbstractTabController_Impl>::Create(std::make_shared<SdTabTemplateDlg>(pParent, pDocShell, rStyleBase, pModel, pView)); +} + +VclPtr<SfxAbstractDialog> SdAbstractDialogFactory_Impl::CreatSdActionDialog(weld::Window* pParent, const SfxItemSet* pAttr, ::sd::View* pView ) +{ + return VclPtr<SdAbstractSfxDialog_Impl>::Create(std::make_unique<SdActionDlg>(pParent, pAttr, pView)); +} + +VclPtr<AbstractSdVectorizeDlg> SdAbstractDialogFactory_Impl::CreateSdVectorizeDlg(weld::Window* pParent, const Bitmap& rBmp, ::sd::DrawDocShell* pDocShell) +{ + return VclPtr<AbstractSdVectorizeDlg_Impl>::Create(std::make_unique<SdVectorizeDlg>(pParent, rBmp, pDocShell)); +} + +VclPtr<AbstractSdPublishingDlg> SdAbstractDialogFactory_Impl::CreateSdPublishingDlg(weld::Window* pParent, DocumentType eDocType) +{ + return VclPtr<AbstractSdPublishingDlg_Impl>::Create(std::make_unique<SdPublishingDlg>(pParent, eDocType)); +} + +// Factories for TabPages +CreateTabPage SdAbstractDialogFactory_Impl::GetSdOptionsContentsTabPageCreatorFunc() +{ + return SdTpOptionsContents::Create; +} + +CreateTabPage SdAbstractDialogFactory_Impl::GetSdPrintOptionsTabPageCreatorFunc() +{ + return SdPrintOptions::Create; +} + +CreateTabPage SdAbstractDialogFactory_Impl::GetSdOptionsMiscTabPageCreatorFunc() +{ + return SdTpOptionsMisc::Create; +} + +CreateTabPage SdAbstractDialogFactory_Impl::GetSdOptionsSnapTabPageCreatorFunc() +{ + return SdTpOptionsSnap::Create; +} + +VclPtr<VclAbstractDialog> SdAbstractDialogFactory_Impl::CreateMasterLayoutDialog(weld::Window* pParent, SdDrawDocument* pDoc, SdPage* pCurrentPage) +{ + return VclPtr<AbstractMasterLayoutDialog_Impl>::Create(std::make_unique<::sd::MasterLayoutDialog>(pParent, pDoc, pCurrentPage)); +} + +VclPtr<AbstractHeaderFooterDialog> SdAbstractDialogFactory_Impl::CreateHeaderFooterDialog(sd::ViewShell* pViewShell, + weld::Window* pParent, SdDrawDocument* pDoc, SdPage* pCurrentPage) +{ + return VclPtr<AbstractHeaderFooterDialog_Impl>::Create(std::make_shared<::sd::HeaderFooterDialog>(pViewShell, pParent, pDoc, pCurrentPage)); +} + +VclPtr<VclAbstractDialog> SdAbstractDialogFactory_Impl::CreateSdPhotoAlbumDialog(weld::Window* pParent, SdDrawDocument* pDoc) +{ + return VclPtr<SdAbstractGenericDialog_Impl>::Create(std::make_unique<sd::SdPhotoAlbumDialog>(pParent, pDoc)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/sddlgfact.hxx b/sd/source/ui/dlg/sddlgfact.hxx new file mode 100644 index 000000000..8da5c74dd --- /dev/null +++ b/sd/source/ui/dlg/sddlgfact.hxx @@ -0,0 +1,448 @@ +/* -*- 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 . + */ +#pragma once + +#include <sdabstdlg.hxx> +#include <sfx2/basedlgs.hxx> +#include <sfx2/sfxdlg.hxx> +#include <svx/svxdlg.hxx> + +#include <morphdlg.hxx> +#include <copydlg.hxx> +#include <BreakDlg.hxx> +#include <headerfooterdlg.hxx> +#include <masterlayoutdlg.hxx> +#include <custsdlg.hxx> +#include <layeroptionsdlg.hxx> +#include <inspagob.hxx> +#include <dlgfield.hxx> +#include <sdpreslt.hxx> +#include <prltempl.hxx> +#include <pubdlg.hxx> +#include <dlgsnap.hxx> +#include <present.hxx> +#include <vectdlg.hxx> +#include <BulletAndPositionDlg.hxx> + +//namespace sd { +// class MorphDlg; +// class CopyDlg; +// class BreakDlg; +// class HeaderFooterDialog; +// class MasterLayoutDialog; +//} + +class SvxBulletAndPositionDlg; + +/// Provides managing and getting information from the numbering and position dialog. +class AbstractSvxBulletAndPositionDlg_Impl :public AbstractSvxBulletAndPositionDlg +{ + std::unique_ptr<SvxBulletAndPositionDlg> m_xDlg; +public: + explicit AbstractSvxBulletAndPositionDlg_Impl(std::unique_ptr<SvxBulletAndPositionDlg> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual const SfxItemSet* GetOutputItemSet( SfxItemSet* ) const override ; + virtual bool IsApplyToMaster() override; + virtual bool IsSlideScope() override; +}; + +class SdAbstractGenericDialog_Impl : public VclAbstractDialog +{ + std::unique_ptr<weld::GenericDialogController> m_xDlg; +public: + explicit SdAbstractGenericDialog_Impl(std::unique_ptr<weld::GenericDialogController> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractMasterLayoutDialog_Impl : public VclAbstractDialog +{ +private: + std::unique_ptr<sd::MasterLayoutDialog> m_xDlg; +public: + AbstractMasterLayoutDialog_Impl(std::unique_ptr<::sd::MasterLayoutDialog> pDlg); + virtual short Execute() override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractBreakDlg_Impl : public VclAbstractDialog +{ +private: + std::unique_ptr<sd::BreakDlg> m_xDlg; +public: + AbstractBreakDlg_Impl(std::unique_ptr<::sd::BreakDlg> pDlg); + virtual short Execute() override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractCopyDlg_Impl : public AbstractCopyDlg +{ +private: + std::unique_ptr<sd::CopyDlg> m_xDlg; +public: + AbstractCopyDlg_Impl(std::unique_ptr<::sd::CopyDlg> pDlg) + : m_xDlg(std::move(pDlg)) + { + } + virtual short Execute() override; + virtual void GetAttr( SfxItemSet& rOutAttrs ) override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractSdCustomShowDlg_Impl : public AbstractSdCustomShowDlg +{ +private: + std::unique_ptr<SdCustomShowDlg> m_xDlg; +public: + AbstractSdCustomShowDlg_Impl(std::unique_ptr<SdCustomShowDlg> pDlg) + : m_xDlg(std::move(pDlg)) + { + } + virtual short Execute() override; + virtual bool IsCustomShow() const override ; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class SdAbstractTabController_Impl : public SfxAbstractTabDialog +{ + std::shared_ptr<SfxTabDialogController> m_xDlg; +public: + explicit SdAbstractTabController_Impl(std::shared_ptr<SfxTabDialogController> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual bool StartExecuteAsync(AsyncContext &rCtx) override; + virtual void SetCurPageId( const OString &rName ) override; + virtual const SfxItemSet* GetOutputItemSet() const override; + virtual WhichRangesContainer GetInputRanges( const SfxItemPool& pItem ) override; + virtual void SetInputSet( const SfxItemSet* pInSet ) override; + virtual void SetText( const OUString& rStr ) override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractBulletDialog_Impl : public SfxAbstractTabDialog +{ + std::shared_ptr<SfxTabDialogController> m_xDlg; +public: + explicit AbstractBulletDialog_Impl(std::shared_ptr<SfxTabDialogController> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual bool StartExecuteAsync(AsyncContext &rCtx) override; + virtual void SetCurPageId( const OString& rName ) override; + virtual const SfxItemSet* GetOutputItemSet() const override; + virtual WhichRangesContainer GetInputRanges( const SfxItemPool& pItem ) override; + virtual void SetInputSet( const SfxItemSet* pInSet ) override; + virtual void SetText( const OUString& rStr ) override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class SdPresLayoutTemplateDlg_Impl : public SfxAbstractTabDialog +{ + std::shared_ptr<SdPresLayoutTemplateDlg> m_xDlg; +public: + explicit SdPresLayoutTemplateDlg_Impl(std::shared_ptr<SdPresLayoutTemplateDlg> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual bool StartExecuteAsync(AsyncContext &rCtx) override; + virtual void SetCurPageId( const OString& rName ) override; + virtual const SfxItemSet* GetOutputItemSet() const override; + virtual WhichRangesContainer GetInputRanges( const SfxItemPool& pItem ) override; + virtual void SetInputSet( const SfxItemSet* pInSet ) override; + virtual void SetText( const OUString& rStr ) override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractSdModifyFieldDlg_Impl : public AbstractSdModifyFieldDlg +{ +private: + std::unique_ptr<SdModifyFieldDlg> m_xDlg; +public: + AbstractSdModifyFieldDlg_Impl(std::unique_ptr<SdModifyFieldDlg> pDlg) + : m_xDlg(std::move(pDlg)) + { + } + virtual short Execute() override; + virtual SvxFieldData* GetField() override; + virtual SfxItemSet GetItemSet() override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractSdSnapLineDlg_Impl : public AbstractSdSnapLineDlg +{ +private: + std::unique_ptr<SdSnapLineDlg> m_xDlg; +public: + AbstractSdSnapLineDlg_Impl(std::unique_ptr<SdSnapLineDlg> pDlg) + : m_xDlg(std::move(pDlg)) + { + } + virtual short Execute() override; + virtual void GetAttr(SfxItemSet& rOutAttrs) override; + virtual void HideRadioGroup() override; + virtual void HideDeleteBtn() override; + virtual void SetInputFields(bool bEnableX, bool bEnableY) override; + virtual void SetText( const OUString& rStr ) override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractSdInsertLayerDlg_Impl : public AbstractSdInsertLayerDlg +{ +private: + std::unique_ptr<SdInsertLayerDlg> m_xDlg; +public: + AbstractSdInsertLayerDlg_Impl(std::unique_ptr<SdInsertLayerDlg> pDlg) + : m_xDlg(std::move(pDlg)) + { + } + virtual short Execute() override; + virtual void GetAttr( SfxItemSet& rOutAttrs ) override ; + virtual void SetHelpId( const OString& rHelpId ) override ; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractSdInsertPagesObjsDlg_Impl : public AbstractSdInsertPagesObjsDlg +{ +private: + std::unique_ptr<SdInsertPagesObjsDlg> m_xDlg; +public: + AbstractSdInsertPagesObjsDlg_Impl(std::unique_ptr<SdInsertPagesObjsDlg> pDlg) + : m_xDlg(std::move(pDlg)) + { + } + virtual short Execute() override; + virtual std::vector<OUString> GetList ( const sal_uInt16 nType ) override; + virtual bool IsLink() override; + virtual bool IsRemoveUnnecessaryMasterPages() const override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractMorphDlg_Impl : public AbstractMorphDlg +{ +private: + std::unique_ptr<sd::MorphDlg> m_xDlg; +public: + AbstractMorphDlg_Impl(std::unique_ptr<::sd::MorphDlg> pDlg) + : m_xDlg(std::move(pDlg)) + { + } + virtual short Execute() override; + virtual void SaveSettings() const override; + virtual sal_uInt16 GetFadeSteps() const override; + virtual bool IsAttributeFade() const override ; + virtual bool IsOrientationFade() const override ; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractSdStartPresDlg_Impl : public AbstractSdStartPresDlg +{ +private: + std::unique_ptr<SdStartPresentationDlg> m_xDlg; +public: + AbstractSdStartPresDlg_Impl(std::unique_ptr<SdStartPresentationDlg> pDlg) + : m_xDlg(std::move(pDlg)) + { + } + virtual short Execute() override; + virtual void GetAttr( SfxItemSet& rOutAttrs ) override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractSdPresLayoutDlg_Impl : public AbstractSdPresLayoutDlg +{ +private: + std::unique_ptr<SdPresLayoutDlg> m_xDlg; +public: + AbstractSdPresLayoutDlg_Impl(std::unique_ptr<SdPresLayoutDlg> pDlg) + : m_xDlg(std::move(pDlg)) + { + } + virtual short Execute() override; + virtual void GetAttr(SfxItemSet& rOutAttrs) override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class SdAbstractSfxDialog_Impl : public SfxAbstractDialog +{ +private: + std::unique_ptr<SfxSingleTabDialogController> m_xDlg; +public: + SdAbstractSfxDialog_Impl(std::unique_ptr<SfxSingleTabDialogController> pDlg) + : m_xDlg(std::move(pDlg)) + { + } + virtual short Execute() override; + virtual const SfxItemSet* GetOutputItemSet() const override; + virtual void SetText( const OUString& rStr ) override; +}; + +class AbstractSdVectorizeDlg_Impl :public AbstractSdVectorizeDlg +{ +private: + std::unique_ptr<SdVectorizeDlg> m_xDlg; +public: + AbstractSdVectorizeDlg_Impl(std::unique_ptr<SdVectorizeDlg> pDlg) + : m_xDlg(std::move(pDlg)) + { + } + virtual short Execute() override; + virtual const GDIMetaFile& GetGDIMetaFile() const override ; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractSdPublishingDlg_Impl :public AbstractSdPublishingDlg +{ +private: + std::unique_ptr<SdPublishingDlg> m_xDlg; +public: + AbstractSdPublishingDlg_Impl(std::unique_ptr<SdPublishingDlg> pDlg) + : m_xDlg(std::move(pDlg)) + { + } + virtual short Execute() override; + virtual void GetParameterSequence( css::uno::Sequence< css::beans::PropertyValue >& rParams ) override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractHeaderFooterDialog_Impl :public AbstractHeaderFooterDialog +{ +private: + std::shared_ptr<::sd::HeaderFooterDialog> m_xDlg; +public: + AbstractHeaderFooterDialog_Impl(std::shared_ptr<::sd::HeaderFooterDialog> pDlg) + : m_xDlg(std::move(pDlg)) + { + } + virtual short Execute() override; + virtual bool StartExecuteAsync(AsyncContext &rCtx) override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +//AbstractDialogFactory_Impl implementations +class SdAbstractDialogFactory_Impl : public SdAbstractDialogFactory +{ + +public: + virtual ~SdAbstractDialogFactory_Impl() {} + + virtual VclPtr<AbstractSvxBulletAndPositionDlg> CreateSvxBulletAndPositionDlg(weld::Window* pParent, const SfxItemSet* pAttr, ::sd::View* pView) override; + virtual VclPtr<VclAbstractDialog> CreateBreakDlg(weld::Window* pWindow, ::sd::DrawView* pDrView, ::sd::DrawDocShell* pShell, sal_uLong nSumActionCount, sal_uLong nObjCount) override; + virtual VclPtr<AbstractCopyDlg> CreateCopyDlg(weld::Window* pParent, const SfxItemSet& rInAttrs, ::sd::View* pView) override; + virtual VclPtr<AbstractSdCustomShowDlg> CreateSdCustomShowDlg(weld::Window* pParent, SdDrawDocument& rDrawDoc) override; + virtual VclPtr<SfxAbstractTabDialog> CreateSdTabCharDialog(weld::Window* pWindow, const SfxItemSet* pAttr, SfxObjectShell* pDocShell) override; + virtual VclPtr<SfxAbstractTabDialog> CreateSdTabPageDialog(weld::Window* pWindow, const SfxItemSet* pAttr, SfxObjectShell* pDocShell, bool bAreaPage, bool bIsImpressDoc, bool bIsImpressMaster) override; + virtual VclPtr<AbstractSdModifyFieldDlg> CreateSdModifyFieldDlg(weld::Window* pWindow, const SvxFieldData* pInField, const SfxItemSet& rSet) override; + virtual VclPtr<AbstractSdSnapLineDlg> CreateSdSnapLineDlg(weld::Window* pParent, const SfxItemSet& rInAttrs, ::sd::View* pView) override; + virtual VclPtr<AbstractSdInsertLayerDlg> CreateSdInsertLayerDlg(weld::Window* pParent, const SfxItemSet& rInAttrs, bool bDeletable, const OUString& aStr) override; + virtual VclPtr<AbstractSdInsertPagesObjsDlg> CreateSdInsertPagesObjsDlg(weld::Window* pParent, const SdDrawDocument* pDoc, SfxMedium* pSfxMedium, const OUString& rFileName ) override; + virtual VclPtr<AbstractMorphDlg> CreateMorphDlg(weld::Window* pParent, const SdrObject* pObj1, const SdrObject* pObj2) override; + virtual VclPtr<SfxAbstractTabDialog> CreateSdOutlineBulletTabDlg(weld::Window* pParent, const SfxItemSet* pAttr, ::sd::View* pView) override; + virtual VclPtr<SfxAbstractTabDialog> CreateSdParagraphTabDlg(weld::Window* pParent, const SfxItemSet* pAttr) override; + virtual VclPtr<AbstractSdStartPresDlg> CreateSdStartPresentationDlg(weld::Window* pWindow, const SfxItemSet& rInAttrs, + const std::vector<OUString> &rPageNames, SdCustomShowList* pCSList ) override; + virtual VclPtr<VclAbstractDialog> CreateRemoteDialog(weld::Window* pWindow) override; // add for RemoteDialog + virtual VclPtr<SfxAbstractTabDialog> CreateSdPresLayoutTemplateDlg(SfxObjectShell* pDocSh, weld::Window* pParent, bool bBackgroundDlg, SfxStyleSheetBase& rStyleBase, PresentationObjects ePO, SfxStyleSheetBasePool* pSSPool) override; + virtual VclPtr<AbstractSdPresLayoutDlg> CreateSdPresLayoutDlg(weld::Window* pParent, ::sd::DrawDocShell* pDocShell, const SfxItemSet& rInAttrs) override; + virtual VclPtr<SfxAbstractTabDialog> CreateSdTabTemplateDlg(weld::Window* pParent, const SfxObjectShell* pDocShell, SfxStyleSheetBase& rStyleBase, SdrModel* pModel, SdrView* pView ) override; + virtual VclPtr<SfxAbstractDialog> CreatSdActionDialog(weld::Window* pParent, const SfxItemSet* pAttr, ::sd::View* pView) override; + virtual VclPtr<AbstractSdVectorizeDlg> CreateSdVectorizeDlg(weld::Window* pParent, const Bitmap& rBmp, ::sd::DrawDocShell* pDocShell) override; + virtual VclPtr<AbstractSdPublishingDlg> CreateSdPublishingDlg(weld::Window* pWindow, DocumentType eDocType) override; + + virtual VclPtr<VclAbstractDialog> CreateSdPhotoAlbumDialog(weld::Window* pWindow, SdDrawDocument* pDoc) override; + + virtual VclPtr<VclAbstractDialog> CreateMasterLayoutDialog(weld::Window* pParent, SdDrawDocument* pDoc, SdPage*) override; + + virtual VclPtr<AbstractHeaderFooterDialog> CreateHeaderFooterDialog(sd::ViewShell* pViewShell, + weld::Window* pParent, + SdDrawDocument* pDoc, + SdPage* pCurrentPage) override; + + // For TabPage + virtual CreateTabPage GetSdOptionsContentsTabPageCreatorFunc() override; + virtual CreateTabPage GetSdPrintOptionsTabPageCreatorFunc() override; + virtual CreateTabPage GetSdOptionsMiscTabPageCreatorFunc() override; + virtual CreateTabPage GetSdOptionsSnapTabPageCreatorFunc() override; + +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/sdpreslt.cxx b/sd/source/ui/dlg/sdpreslt.cxx new file mode 100644 index 000000000..dd54611e2 --- /dev/null +++ b/sd/source/ui/dlg/sdpreslt.cxx @@ -0,0 +1,267 @@ +/* -*- 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 <svl/itemset.hxx> +#include <svl/eitem.hxx> +#include <svl/stritem.hxx> +#include <sfx2/new.hxx> +#include <svtools/valueset.hxx> +#include <tools/debug.hxx> +#include <vcl/image.hxx> + +#include <strings.hrc> + +#include <bitmaps.hlst> +#include <sdpreslt.hxx> +#include <sdattr.hrc> +#include <sdresid.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <DrawDocShell.hxx> +#include <memory> + +SdPresLayoutDlg::SdPresLayoutDlg(::sd::DrawDocShell* pDocShell, + weld::Window* pWindow, const SfxItemSet& rInAttrs) + : GenericDialogController(pWindow, "modules/simpress/ui/slidedesigndialog.ui", "SlideDesignDialog") + , mpDocSh(pDocShell) + , mrOutAttrs(rInAttrs) + , maStrNone(SdResId(STR_NULL)) + , m_xCbxMasterPage(m_xBuilder->weld_check_button("masterpage")) + , m_xCbxCheckMasters(m_xBuilder->weld_check_button("checkmasters")) + , m_xBtnLoad(m_xBuilder->weld_button("load")) + , m_xVS(new ValueSet(m_xBuilder->weld_scrolled_window("selectwin", true))) + , m_xVSWin(new weld::CustomWeld(*m_xBuilder, "select", *m_xVS)) +{ + m_xVSWin->set_size_request(m_xBtnLoad->get_approximate_digit_width() * 60, + m_xBtnLoad->get_text_height() * 20); + + m_xVS->SetDoubleClickHdl(LINK(this, SdPresLayoutDlg, ClickLayoutHdl)); + m_xBtnLoad->connect_clicked(LINK(this, SdPresLayoutDlg, ClickLoadHdl)); + + Reset(); +} + +SdPresLayoutDlg::~SdPresLayoutDlg() +{ +} + +/** + * Initialize + */ +void SdPresLayoutDlg::Reset() +{ + tools::Long nName; + + // replace master page + if( const SfxBoolItem* pPoolItem = mrOutAttrs.GetItemIfSet( ATTR_PRESLAYOUT_MASTER_PAGE, false ) ) + { + bool bMasterPage = pPoolItem->GetValue(); + m_xCbxMasterPage->set_sensitive( !bMasterPage ); + m_xCbxMasterPage->set_active( bMasterPage ); + } + + // remove not used master pages + m_xCbxCheckMasters->set_active(false); + + if( const SfxStringItem* pPoolItem = mrOutAttrs.GetItemIfSet(ATTR_PRESLAYOUT_NAME) ) + maName = pPoolItem->GetValue(); + else + maName.clear(); + + FillValueSet(); + + mnLayoutCount = maLayoutNames.size(); + for( nName = 0; nName < mnLayoutCount; nName++ ) + { + if (maLayoutNames[nName] == maName) + break; + } + DBG_ASSERT(nName < mnLayoutCount, "Layout not found"); + + m_xVS->SelectItem(static_cast<sal_uInt16>(nName) + 1); // Indices of the ValueSets start at 1 + +} + +/** + * Fills the provided Item-Set with dialog box attributes + */ +void SdPresLayoutDlg::GetAttr(SfxItemSet& rOutAttrs) +{ + short nId = m_xVS->GetSelectedItemId(); + bool bLoad = nId > mnLayoutCount; + rOutAttrs.Put( SfxBoolItem( ATTR_PRESLAYOUT_LOAD, bLoad ) ); + + OUString aLayoutName; + + if( bLoad ) + { + aLayoutName = maName + "#" + maLayoutNames[ nId - 1 ]; + } + else if (nId) + { + aLayoutName = maLayoutNames[ nId - 1 ]; + if( aLayoutName == maStrNone ) + aLayoutName.clear(); // that way we encode "- nothing -" (see below) + } + + rOutAttrs.Put( SfxStringItem( ATTR_PRESLAYOUT_NAME, aLayoutName ) ); + rOutAttrs.Put( SfxBoolItem( ATTR_PRESLAYOUT_MASTER_PAGE, m_xCbxMasterPage->get_active() ) ); + rOutAttrs.Put( SfxBoolItem( ATTR_PRESLAYOUT_CHECK_MASTERS, m_xCbxCheckMasters->get_active() ) ); +} + +/** + * Fills ValueSet with bitmaps + */ +void SdPresLayoutDlg::FillValueSet() +{ + m_xVS->SetStyle(m_xVS->GetStyle() | WB_ITEMBORDER | WB_DOUBLEBORDER + | WB_VSCROLL | WB_NAMEFIELD); + + m_xVS->SetColCount(2); + m_xVS->SetLineCount(2); + m_xVS->SetExtraSpacing(2); + + SdDrawDocument* pDoc = mpDocSh->GetDoc(); + + sal_uInt16 nCount = pDoc->GetMasterPageCount(); + + for (sal_uInt16 nLayout = 0; nLayout < nCount; nLayout++) + { + SdPage* pMaster = static_cast<SdPage*>(pDoc->GetMasterPage(nLayout)); + if (pMaster->GetPageKind() == PageKind::Standard) + { + OUString aLayoutName(pMaster->GetLayoutName()); + aLayoutName = aLayoutName.copy(0, aLayoutName.indexOf(SD_LT_SEPARATOR)); + maLayoutNames.push_back(aLayoutName); + + Image aBitmap(mpDocSh->GetPagePreviewBitmap(pMaster)); + m_xVS->InsertItem(static_cast<sal_uInt16>(maLayoutNames.size()), aBitmap, aLayoutName); + } + } + + m_xVS->Show(); +} + +/** + * DoubleClick handler + */ +IMPL_LINK_NOARG(SdPresLayoutDlg, ClickLayoutHdl, ValueSet*, void) +{ + m_xDialog->response(RET_OK); +} + +/** + * Click handler for load button + */ +IMPL_LINK_NOARG(SdPresLayoutDlg, ClickLoadHdl, weld::Button&, void) +{ + SfxNewFileDialog aDlg(m_xDialog.get(), SfxNewFileDialogMode::Preview); + aDlg.set_title(SdResId(STR_LOAD_PRESENTATION_LAYOUT)); + sal_uInt16 nResult = aDlg.run(); + + bool bCancel = false; + + switch (nResult) + { + case RET_OK: + { + if (aDlg.IsTemplate()) + { + maName = aDlg.GetTemplateFileName(); + } + else + { + // that way we encode "- nothing -" + maName.clear(); + } + } + break; + + default: + bCancel = true; + } + + if( bCancel ) + return; + + // check if template already exists + OUString aCompareStr(maName); + if (aCompareStr.isEmpty()) + aCompareStr = maStrNone; + + auto it = std::find(maLayoutNames.begin(), maLayoutNames.end(), aCompareStr); + if (it != maLayoutNames.end()) + { + sal_uInt16 aPos = static_cast<sal_uInt16>(std::distance(maLayoutNames.begin(), it)); + // select template + m_xVS->SelectItem( aPos + 1 ); + } + else + { + // load document in order to determine preview bitmap (if template is selected) + if (!maName.isEmpty()) + { + // determine document in order to call OpenBookmarkDoc + SdDrawDocument* pDoc = mpDocSh->GetDoc(); + SdDrawDocument* pTemplDoc = pDoc->OpenBookmarkDoc( maName ); + + if (pTemplDoc) + { + ::sd::DrawDocShell* pTemplDocSh= pTemplDoc->GetDocSh(); + + sal_uInt16 nCount = pTemplDoc->GetMasterPageCount(); + + for (sal_uInt16 nLayout = 0; nLayout < nCount; nLayout++) + { + SdPage* pMaster = static_cast<SdPage*>( pTemplDoc->GetMasterPage(nLayout) ); + if (pMaster->GetPageKind() == PageKind::Standard) + { + OUString aLayoutName(pMaster->GetLayoutName()); + aLayoutName = aLayoutName.copy(0, aLayoutName.indexOf(SD_LT_SEPARATOR)); + maLayoutNames.push_back(aLayoutName); + + Image aBitmap(pTemplDocSh->GetPagePreviewBitmap(pMaster)); + m_xVS->InsertItem(static_cast<sal_uInt16>(maLayoutNames.size()), aBitmap, aLayoutName); + } + } + } + else + { + bCancel = true; + } + + pDoc->CloseBookmarkDoc(); + } + else + { + // empty layout + maLayoutNames.push_back(maStrNone); + m_xVS->InsertItem( static_cast<sal_uInt16>(maLayoutNames.size()), + Image(BMP_SLIDE_NONE), maStrNone ); + } + + if (!bCancel) + { + // select template + m_xVS->SelectItem( static_cast<sal_uInt16>(maLayoutNames.size()) ); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/sdtreelb.cxx b/sd/source/ui/dlg/sdtreelb.cxx new file mode 100644 index 000000000..c15a2ea09 --- /dev/null +++ b/sd/source/ui/dlg/sdtreelb.cxx @@ -0,0 +1,1206 @@ +/* -*- 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 <sal/log.hxx> +#include <sal/types.h> +#include <sot/formats.hxx> +#include <vcl/weld.hxx> +#include <svx/svditer.hxx> +#include <sfx2/docfile.hxx> +#include <svx/svdoole2.hxx> +#include <vcl/svapp.hxx> +#include <cusshow.hxx> + +#include <sfx2/viewfrm.hxx> + +#include <sdtreelb.hxx> +#include <DrawDocShell.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <sdmod.hxx> +#include <sdresid.hxx> +#include <navigatr.hxx> +#include <strings.hrc> + +#include <bitmaps.hlst> +#include <customshowlist.hxx> +#include <ViewShell.hxx> +#include <DrawController.hxx> +#include <ViewShellBase.hxx> + +#include <com/sun/star/embed/XEmbedPersist.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <svtools/acceleratorexecute.hxx> +#include <svtools/embedtransfer.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/processfactory.hxx> + + +using namespace com::sun::star; + +bool SdPageObjsTLV::bIsInDrag = false; + +bool SdPageObjsTLV::IsInDrag() +{ + return bIsInDrag; +} + +SotClipboardFormatId SdPageObjsTLV::SdPageObjsTransferable::mnListBoxDropFormatId = static_cast<SotClipboardFormatId>(SAL_MAX_UINT32); + +SdPageObjsTLV::SdPageObjsTransferable::SdPageObjsTransferable( + const INetBookmark& rBookmark, + ::sd::DrawDocShell& rDocShell, + NavigatorDragType eDragType) + : SdTransferable(rDocShell.GetDoc(), nullptr, true), + maBookmark( rBookmark ), + mrDocShell( rDocShell ), + meDragType( eDragType ) +{ +} + +SdPageObjsTLV::SdPageObjsTransferable::~SdPageObjsTransferable() +{ +} + +void SdPageObjsTLV::SdPageObjsTransferable::AddSupportedFormats() +{ + AddFormat(SotClipboardFormatId::NETSCAPE_BOOKMARK); + AddFormat(SotClipboardFormatId::TREELISTBOX); + AddFormat(GetListBoxDropFormatId()); +} + +bool SdPageObjsTLV::SdPageObjsTransferable::GetData( const css::datatransfer::DataFlavor& rFlavor, const OUString& /*rDestDoc*/ ) +{ + SotClipboardFormatId nFormatId = SotExchange::GetFormat( rFlavor ); + switch (nFormatId) + { + case SotClipboardFormatId::NETSCAPE_BOOKMARK: + SetINetBookmark( maBookmark, rFlavor ); + return true; + + case SotClipboardFormatId::TREELISTBOX: + { + css::uno::Any aTreeListBoxData; // empty for now + SetAny(aTreeListBoxData); + return true; + } + + default: + return false; + } +} + +void SdPageObjsTLV::SdPageObjsTransferable::DragFinished( sal_Int8 nDropAction ) +{ + SdPageObjsTLV::OnDragFinished(); + SdTransferable::DragFinished(nDropAction); +} + +sal_Int64 SAL_CALL SdPageObjsTLV::SdPageObjsTransferable::getSomething( const css::uno::Sequence< sal_Int8 >& rId ) +{ + return comphelper::getSomethingImpl(rId, this, + comphelper::FallbackToGetSomethingOf<SdTransferable>{}); +} + +const css::uno::Sequence<sal_Int8>& SdPageObjsTLV::SdPageObjsTransferable::getUnoTunnelId() +{ + static const comphelper::UnoIdInit theSdPageObjsTLBUnoTunnelId; + return theSdPageObjsTLBUnoTunnelId.getSeq(); +} + +SdPageObjsTLV::SdPageObjsTransferable* SdPageObjsTLV::SdPageObjsTransferable::getImplementation( const css::uno::Reference< css::uno::XInterface >& rxData ) + noexcept +{ + try + { + return comphelper::getFromUnoTunnel<SdPageObjsTLV::SdPageObjsTransferable>(rxData); + } + catch( const css::uno::Exception& ) + { + } + return nullptr; +} + +SotClipboardFormatId SdPageObjsTLV::SdPageObjsTransferable::GetListBoxDropFormatId() +{ + if (mnListBoxDropFormatId == static_cast<SotClipboardFormatId>(SAL_MAX_UINT32)) + mnListBoxDropFormatId = SotExchange::RegisterFormatMimeType("application/x-openoffice-treelistbox-moveonly;windows_formatname=\"SV_LBOX_DD_FORMAT_MOVE\""); + return mnListBoxDropFormatId; +} + +/** + * @return true if children of the specified string are selected + */ +bool SdPageObjsTLV::HasSelectedChildren( std::u16string_view rName ) +{ + bool bChildren = false; + + if( !rName.empty() ) + { + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + OUString aTmp; + + if (m_xTreeView->get_iter_first(*xEntry)) + { + do + { + aTmp = m_xTreeView->get_text(*xEntry); + if (aTmp == rName) + { + + // see if any of the selected nodes are subchildren of this node + m_xTreeView->selected_foreach([this, &bChildren, &xEntry](weld::TreeIter& rEntry){ + std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(&rEntry)); + while (!bChildren && m_xTreeView->iter_parent(*xParent)) + bChildren = m_xTreeView->iter_compare(*xParent, *xEntry) == 0; + return bChildren; + }); + + break; + } + } + while (m_xTreeView->iter_next(*xEntry)); + } + } + + return bChildren; +} + +void SdPageObjsTLV::SetShowAllShapes ( + const bool bShowAllShapes, + const bool bFillList) +{ + m_bShowAllShapes = bShowAllShapes; + if (bFillList) + { + if (m_pMedium == nullptr) + Fill(m_pDoc, m_bShowAllPages, m_aDocName); + else + Fill(m_pDoc, m_pMedium, m_aDocName); + } +} + +bool SdPageObjsTLV::IsEqualToShapeList(std::unique_ptr<weld::TreeIter>& rEntry, const SdrObjList& rList, + std::u16string_view rListName) +{ + if (!rEntry) + return false; + OUString aName = m_xTreeView->get_text(*rEntry); + + if (rListName != aName) + return false; + + if (!m_xTreeView->iter_next(*rEntry)) + rEntry.reset(); + + SdrObjListIter aIter(&rList, + !rList.HasObjectNavigationOrder() /* use navigation order, if available */, + SdrIterMode::Flat); + + while (aIter.IsMore()) + { + SdrObject* pObj = aIter.Next(); + + const OUString aObjectName(GetObjectName(pObj)); + + if (!aObjectName.isEmpty()) + { + if (!rEntry) + return false; + + aName = m_xTreeView->get_text(*rEntry); + + if (aObjectName != aName) + return false; + + if (pObj->IsGroupObject()) + { + bool bRet = IsEqualToShapeList(rEntry, *pObj->GetSubList(), aObjectName); + if (!bRet) + return false; + } + else + { + if (!m_xTreeView->iter_next(*rEntry)) + rEntry.reset(); + } + } + } + + return true; +} + +/** + * Checks if the pages (PageKind::Standard) of a doc and the objects on the pages + * are identical to the TreeLB. + * If a doc is provided, this will be the used doc (important by more than + * one document). + */ +bool SdPageObjsTLV::IsEqualToDoc( const SdDrawDocument* pInDoc ) +{ + if( pInDoc ) + m_pDoc = pInDoc; + + if( !m_pDoc ) + return false; + + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (!m_xTreeView->get_iter_first(*xEntry)) + xEntry.reset(); + + // compare all pages including the objects + sal_uInt16 nPage = 0; + const sal_uInt16 nMaxPages = m_pDoc->GetPageCount(); + + while( nPage < nMaxPages ) + { + const SdPage* pPage = static_cast<const SdPage*>( m_pDoc->GetPage( nPage ) ); + if( pPage->GetPageKind() == PageKind::Standard ) + { + bool bRet = IsEqualToShapeList(xEntry, *pPage, pPage->GetName()); + if (!bRet) + return false; + } + nPage++; + } + // If there are still entries in the listbox, + // then objects (with names) or pages were deleted + return !xEntry; +} + +IMPL_LINK(SdPageObjsTLV, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode(); + if (m_xAccel->execute(rKeyCode)) + { + // the accelerator consumed the event + return true; + } + if (rKeyCode.GetCode() == KEY_RETURN) + { + std::unique_ptr<weld::TreeIter> xCursor(m_xTreeView->make_iterator()); + if (m_xTreeView->get_cursor(xCursor.get()) && m_xTreeView->iter_has_child(*xCursor)) + { + if (m_xTreeView->get_row_expanded(*xCursor)) + m_xTreeView->collapse_row(*xCursor); + else + m_xTreeView->expand_row(*xCursor); + } + m_aRowActivatedHdl.Call(*m_xTreeView); + return true; + } + return m_aKeyPressHdl.Call(rKEvt); +} + +IMPL_LINK(SdPageObjsTLV, MousePressHdl, const MouseEvent&, rMEvt, bool) +{ + m_bSelectionHandlerNavigates = rMEvt.GetClicks() == 1; + m_bNavigationGrabsFocus = rMEvt.GetClicks() != 1; + return false; +} + +IMPL_LINK_NOARG(SdPageObjsTLV, MouseReleaseHdl, const MouseEvent&, bool) +{ + m_bSelectionHandlerNavigates = false; + m_bNavigationGrabsFocus = true; + return false; +} + +IMPL_LINK(SdPageObjsTLV, DragBeginHdl, bool&, rUnsetDragIcon, bool) +{ + rUnsetDragIcon = false; + return StartDrag(); +} + +namespace +{ + bool CanDragSource(const weld::TreeView& rTreeView) + { + std::unique_ptr<weld::TreeIter> xSource(rTreeView.make_iterator()); + if (!rTreeView.get_selected(xSource.get())) + return false; + + std::unique_ptr<weld::TreeIter> xSourceParent(rTreeView.make_iterator(xSource.get())); + bool bSourceHasParent = rTreeView.iter_parent(*xSourceParent); + // level 1 objects only + if (!bSourceHasParent || rTreeView.get_iter_depth(*xSourceParent)) + return false; + + SdrObject* pSourceObject = weld::fromId<SdrObject*>(rTreeView.get_id(*xSource)); + if (pSourceObject == reinterpret_cast<SdrObject*>(1)) + pSourceObject = nullptr; + + if (!pSourceObject) + return false; + + SdrPage* pObjectList = pSourceObject->getSdrPageFromSdrObject(); + if (!pObjectList) + return false; + + return true; + } +} + +/** + * StartDrag-Request + */ +bool SdPageObjsTLV::StartDrag() +{ + return !CanDragSource(*m_xTreeView) || DoDrag(); +} + +/** + * Begin drag + */ +bool SdPageObjsTLV::DoDrag() +{ + if (!m_pNavigator) + return true; + + if (!m_xHelper) + return true; + + // Get the view. + ::sd::DrawDocShell* pDocShell = m_pDoc->GetDocSh(); + ::sd::ViewShell* pViewShell = GetViewShellForDocShell(*pDocShell); + if (pViewShell == nullptr) + { + OSL_ASSERT(pViewShell!=nullptr); + return true; + } + sd::View* pView = pViewShell->GetView(); + if (pView == nullptr) + { + OSL_ASSERT(pView!=nullptr); + return true; + } + + bIsInDrag = true; + + std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator(); + bool bUserData = m_xTreeView->get_cursor(xEntry.get()); + + SdrObject* pObject = nullptr; + sal_Int64 nUserData = bUserData ? m_xTreeView->get_id(*xEntry).toInt64() : 0; + if (nUserData != 1) + pObject = reinterpret_cast<SdrObject*>(nUserData); + if (pObject != nullptr) + { + // For shapes without a user supplied name (the automatically + // created name does not count), a different drag and drop technique + // is used. + if (GetObjectName(pObject, false).isEmpty()) + { + AddShapeToTransferable(*m_xHelper, *pObject); + m_xHelper->SetView(pView); + SD_MOD()->pTransferDrag = m_xHelper.get(); + } + + // Unnamed shapes have to be selected to be recognized by the + // current drop implementation. In order to have a consistent + // behaviour for all shapes, every shape that is to be dragged is + // selected first. + SdrPageView* pPageView = pView->GetSdrPageView(); + pView->UnmarkAllObj(pPageView); + pView->MarkObj(pObject, pPageView); + } + else + { + m_xHelper->SetView(pView); + SD_MOD()->pTransferDrag = m_xHelper.get(); + } + + return false; +} + +void SdPageObjsTLV::OnDragFinished() +{ + bIsInDrag = false; +} + +SdPageObjsTLVDropTarget::SdPageObjsTLVDropTarget(weld::TreeView& rTreeView) + : DropTargetHelper(rTreeView.get_drop_target()) + , m_rTreeView(rTreeView) +{ +} + +/** + * AcceptDrop-Event + */ +sal_Int8 SdPageObjsTLVDropTarget::AcceptDrop(const AcceptDropEvent& rEvt) +{ + weld::TreeView* pSource = m_rTreeView.get_drag_source(); + // only dragging within the same widget allowed + if (!pSource || pSource != &m_rTreeView) + return DND_ACTION_NONE; + + std::unique_ptr<weld::TreeIter> xTarget(m_rTreeView.make_iterator()); + if (!m_rTreeView.get_dest_row_at_pos(rEvt.maPosPixel, xTarget.get(), true)) + return DND_ACTION_NONE; + + std::unique_ptr<weld::TreeIter> xSource(m_rTreeView.make_iterator()); + if (!m_rTreeView.get_selected(xSource.get())) + return DND_ACTION_NONE; + + std::unique_ptr<weld::TreeIter> xTargetParent(m_rTreeView.make_iterator(xTarget.get())); + while (m_rTreeView.get_iter_depth(*xTargetParent)) + m_rTreeView.iter_parent(*xTargetParent); + + std::unique_ptr<weld::TreeIter> xSourceParent(m_rTreeView.make_iterator(xSource.get())); + while (m_rTreeView.get_iter_depth(*xSourceParent)) + m_rTreeView.iter_parent(*xSourceParent); + + // can only drop within the same page + if (m_rTreeView.iter_compare(*xTargetParent, *xSourceParent) != 0) + return DND_ACTION_NONE; + + return DND_ACTION_MOVE; +} + +/** + * ExecuteDrop-Event + */ +sal_Int8 SdPageObjsTLVDropTarget::ExecuteDrop( const ExecuteDropEvent& rEvt ) +{ + weld::TreeView* pSource = m_rTreeView.get_drag_source(); + // only dragging within the same widget allowed + if (!pSource || pSource != &m_rTreeView) + return DND_ACTION_NONE; + + std::unique_ptr<weld::TreeIter> xSource(m_rTreeView.make_iterator()); + if (!m_rTreeView.get_selected(xSource.get())) + return DND_ACTION_NONE; + + std::unique_ptr<weld::TreeIter> xTarget(m_rTreeView.make_iterator()); + if (!m_rTreeView.get_dest_row_at_pos(rEvt.maPosPixel, xTarget.get(), true)) + return DND_ACTION_NONE; + int nTargetPos = m_rTreeView.get_iter_index_in_parent(*xTarget) + 1; + + SdrObject* pTargetObject = weld::fromId<SdrObject*>(m_rTreeView.get_id(*xTarget)); + SdrObject* pSourceObject = weld::fromId<SdrObject*>(m_rTreeView.get_id(*xSource)); + if (pSourceObject == reinterpret_cast<SdrObject*>(1)) + pSourceObject = nullptr; + + if (pTargetObject != nullptr && pSourceObject != nullptr) + { + SdrPage* pObjectList = pSourceObject->getSdrPageFromSdrObject(); + if (pObjectList != nullptr) + { + sal_uInt32 nNewPosition; + if (pTargetObject == reinterpret_cast<SdrObject*>(1)) + { + nNewPosition = 0; + nTargetPos = 0; + } + else + nNewPosition = pTargetObject->GetNavigationPosition() + 1; + pObjectList->SetObjectNavigationPosition(*pSourceObject, nNewPosition); + } + + std::unique_ptr<weld::TreeIter> xSourceParent(m_rTreeView.make_iterator(xSource.get())); + m_rTreeView.iter_parent(*xSourceParent); + + m_rTreeView.move_subtree(*xSource, xSourceParent.get(), nTargetPos); + } + + return DND_ACTION_NONE; +} + +void SdPageObjsTLV::AddShapeToTransferable ( + SdTransferable& rTransferable, + const SdrObject& rObject) const +{ + std::unique_ptr<TransferableObjectDescriptor> pObjectDescriptor(new TransferableObjectDescriptor); + bool bIsDescriptorFillingPending (true); + + const SdrOle2Obj* pOleObject = dynamic_cast<const SdrOle2Obj*>(&rObject); + if (pOleObject != nullptr && pOleObject->GetObjRef().is()) + { + // If object has no persistence it must be copied as part of the document + try + { + uno::Reference< embed::XEmbedPersist > xPersObj (pOleObject->GetObjRef(), uno::UNO_QUERY ); + if (xPersObj.is() && xPersObj->hasEntry()) + { + SvEmbedTransferHelper::FillTransferableObjectDescriptor( + *pObjectDescriptor, + pOleObject->GetObjRef(), + pOleObject->GetGraphic(), + pOleObject->GetAspect()); + bIsDescriptorFillingPending = false; + } + } + catch( uno::Exception& ) + { + } + } + + ::sd::DrawDocShell* pDocShell = m_pDoc->GetDocSh(); + if (bIsDescriptorFillingPending && pDocShell!=nullptr) + { + pDocShell->FillTransferableObjectDescriptor(*pObjectDescriptor); + } + + Point aDragPos (rObject.GetCurrentBoundRect().Center()); + pObjectDescriptor->maDragStartPos = aDragPos; + if (pDocShell != nullptr) + pObjectDescriptor->maDisplayName = pDocShell->GetMedium()->GetURLObject().GetURLNoPass(); + else + pObjectDescriptor->maDisplayName.clear(); + + rTransferable.SetStartPos(aDragPos); + rTransferable.SetObjectDescriptor( std::move(pObjectDescriptor) ); +} + +::sd::ViewShell* SdPageObjsTLV::GetViewShellForDocShell (::sd::DrawDocShell& rDocShell) +{ + { + ::sd::ViewShell* pViewShell = rDocShell.GetViewShell(); + if (pViewShell != nullptr) + return pViewShell; + } + + try + { + // Get a component enumeration from the desktop and search it for documents. + uno::Reference<uno::XComponentContext> xContext( ::comphelper::getProcessComponentContext()); + + uno::Reference<frame::XDesktop2> xDesktop = frame::Desktop::create(xContext); + + if ( ! xDesktop.is()) + return nullptr; + + uno::Reference<container::XIndexAccess> xFrameAccess = xDesktop->getFrames(); + if ( ! xFrameAccess.is()) + return nullptr; + + for (sal_Int32 nIndex=0,nCount=xFrameAccess->getCount(); nIndex<nCount; ++nIndex) + { + uno::Reference<frame::XFrame> xFrame; + if ( ! (xFrameAccess->getByIndex(nIndex) >>= xFrame)) + continue; + + auto xController = xFrame->getController(); + ::sd::DrawController* pController = dynamic_cast<sd::DrawController*>(xController.get()); + if (pController == nullptr) + continue; + ::sd::ViewShellBase* pBase = pController->GetViewShellBase(); + if (pBase == nullptr) + continue; + if (pBase->GetDocShell() != &rDocShell) + continue; + + const std::shared_ptr<sd::ViewShell> pViewShell (pBase->GetMainViewShell()); + if (pViewShell) + return pViewShell.get(); + } + } + catch (uno::Exception &) + { + // When there is an exception then simply use the default value of + // bIsEnabled and disable the controls. + } + return nullptr; +} + +SdPageObjsTLV::SdPageObjsTLV(std::unique_ptr<weld::TreeView> xTreeView) + : m_xTreeView(std::move(xTreeView)) + , m_xScratchIter(m_xTreeView->make_iterator()) + , m_xDropTargetHelper(new SdPageObjsTLVDropTarget(*m_xTreeView)) + , m_xAccel(::svt::AcceleratorExecute::createAcceleratorHelper()) + , m_pNavigator(nullptr) + , m_pDoc(nullptr) + , m_pBookmarkDoc(nullptr) + , m_pMedium(nullptr) + , m_pOwnMedium(nullptr) + , m_bLinkableSelected(false) + , m_bShowAllShapes(false) + , m_bShowAllPages(false) + , m_bSelectionHandlerNavigates(false) + , m_bNavigationGrabsFocus(true) + , m_eSelectionMode(SelectionMode::Single) + , m_nSelectEventId(nullptr) + , m_nRowActivateEventId(nullptr) +{ + m_xTreeView->connect_expanding(LINK(this, SdPageObjsTLV, RequestingChildrenHdl)); + m_xTreeView->connect_changed(LINK(this, SdPageObjsTLV, SelectHdl)); + m_xTreeView->connect_row_activated(LINK(this, SdPageObjsTLV, RowActivatedHdl)); + m_xTreeView->connect_drag_begin(LINK(this, SdPageObjsTLV, DragBeginHdl)); + m_xTreeView->connect_key_press(LINK(this, SdPageObjsTLV, KeyInputHdl)); + m_xTreeView->connect_mouse_press(LINK(this, SdPageObjsTLV, MousePressHdl)); + m_xTreeView->connect_mouse_release(LINK(this, SdPageObjsTLV, MouseReleaseHdl)); + + m_xTreeView->set_size_request(m_xTreeView->get_approximate_digit_width() * 28, + m_xTreeView->get_text_height() * 8); +} + +IMPL_LINK_NOARG(SdPageObjsTLV, SelectHdl, weld::TreeView&, void) +{ + if (m_nSelectEventId) + Application::RemoveUserEvent(m_nSelectEventId); + // post the event to process select event after mouse press event + m_nSelectEventId = Application::PostUserEvent(LINK(this, SdPageObjsTLV, AsyncSelectHdl)); +} + +IMPL_LINK_NOARG(SdPageObjsTLV, RowActivatedHdl, weld::TreeView&, bool) +{ + if (m_nRowActivateEventId) + Application::RemoveUserEvent(m_nRowActivateEventId); + // post the event to process row activate after mouse press event + m_nRowActivateEventId = Application::PostUserEvent(LINK(this, SdPageObjsTLV, AsyncRowActivatedHdl)); + return true; +} + +IMPL_LINK_NOARG(SdPageObjsTLV, AsyncSelectHdl, void*, void) +{ + Select(); +} + +void SdPageObjsTLV::Select() +{ + m_nSelectEventId = nullptr; + + m_bLinkableSelected = true; + + m_xTreeView->selected_foreach([this](weld::TreeIter& rEntry){ + if (m_xTreeView->get_id(rEntry).toInt64() == 0) + m_bLinkableSelected = false; + return false; + }); + + m_aChangeHdl.Call(*m_xTreeView); + + if (m_bSelectionHandlerNavigates) + { + // Page items in the tree are given user data value 1. + // Drawing object items are given user data value of the object pointer they represent. + sal_Int64 nUserData = m_xTreeView->get_selected_id().toInt64(); + if (nUserData != 1) + { + SdrObject* pObject = reinterpret_cast<SdrObject*>(nUserData); + if (pObject && pObject->GetName().isEmpty()) + { + const bool bUndo = pObject->getSdrModelFromSdrObject().IsUndoEnabled(); + pObject->getSdrModelFromSdrObject().EnableUndo(false); + pObject->SetName(m_xTreeView->get_selected_text(), false); + pObject->getSdrModelFromSdrObject().EnableUndo(bUndo); + m_aRowActivatedHdl.Call(*m_xTreeView); + pObject->getSdrModelFromSdrObject().EnableUndo(false); + pObject->SetName(OUString(), false); + pObject->getSdrModelFromSdrObject().EnableUndo(bUndo); + } + else + m_aRowActivatedHdl.Call(*m_xTreeView); + } + else + m_aRowActivatedHdl.Call(*m_xTreeView); + } + + if (!m_pNavigator) + { + m_xHelper.clear(); + return; + } + + ::sd::DrawDocShell* pDocShell = m_pDoc->GetDocSh(); + OUString aURL = INetURLObject(pDocShell->GetMedium()->GetPhysicalName(), INetProtocol::File).GetMainURL(INetURLObject::DecodeMechanism::NONE); + NavigatorDragType eDragType = m_pNavigator->GetNavigatorDragType(); + + OUString sSelectedEntry = m_xTreeView->get_selected_text(); + aURL += "#" + sSelectedEntry; + + INetBookmark aBookmark(aURL, sSelectedEntry); + sal_Int8 nDNDActions = DND_ACTION_COPYMOVE; + + if( eDragType == NAVIGATOR_DRAGTYPE_LINK ) + nDNDActions = DND_ACTION_LINK; // Either COPY *or* LINK, never both! + else if (m_pDoc->GetSdPageCount(PageKind::Standard) == 1) + { + // Can not move away the last slide in a document. + nDNDActions = DND_ACTION_COPY; + } + + // object is destroyed by internal reference mechanism + m_xHelper.set(new SdPageObjsTLV::SdPageObjsTransferable(aBookmark, *pDocShell, eDragType)); + rtl::Reference<TransferDataContainer> xHelper(m_xHelper); + m_xTreeView->enable_drag_source(xHelper, nDNDActions); +} + +IMPL_LINK_NOARG(SdPageObjsTLV, AsyncRowActivatedHdl, void*, void) +{ + m_nRowActivateEventId = nullptr; + m_aRowActivatedHdl.Call(*m_xTreeView); +} + +OUString SdPageObjsTLV::GetObjectName( + const SdrObject* pObject, + const bool bCreate) const +{ + OUString aRet; + + if ( pObject ) + { + aRet = pObject->GetName(); + + if (aRet.isEmpty()) + if (auto pOleObj = dynamic_cast<const SdrOle2Obj* >(pObject)) + aRet = pOleObj->GetPersistName(); + } + + if (bCreate + && m_bShowAllShapes + && aRet.isEmpty() + && pObject!=nullptr) + { + aRet = SdResId(STR_NAVIGATOR_SHAPE_BASE_NAME) + " (" + pObject->TakeObjNameSingul() +")"; + aRet = aRet.replaceFirst("%1", OUString::number(pObject->GetOrdNum() + 1)); + } + + return aRet; +} + +std::vector<OUString> SdPageObjsTLV::GetSelectEntryList(const int nDepth) const +{ + std::vector<OUString> aEntries; + + m_xTreeView->selected_foreach([this, nDepth, &aEntries](weld::TreeIter& rEntry){ + int nListDepth = m_xTreeView->get_iter_depth(rEntry); + if (nListDepth == nDepth) + aEntries.push_back(m_xTreeView->get_text(rEntry)); + return false; + }); + + return aEntries; +} + +/** + * Checks if it is a draw file and opens the BookmarkDoc depending of + * the provided Docs + */ +SdDrawDocument* SdPageObjsTLV::GetBookmarkDoc(SfxMedium* pMed) +{ + if ( + !m_pBookmarkDoc || + (pMed && (!m_pOwnMedium || m_pOwnMedium->GetName() != pMed->GetName())) + ) + { + // create a new BookmarkDoc if now one exists or if a new Medium is provided + if (m_pOwnMedium != pMed) + { + CloseBookmarkDoc(); + } + + if (pMed) + { + // it looks that it is undefined if a Medium was set by Fill() already + DBG_ASSERT( !m_pMedium, "SfxMedium confusion!" ); + delete m_pMedium; + m_pMedium = nullptr; + + // take over this Medium (currently used only be Navigator) + m_pOwnMedium = pMed; + } + + DBG_ASSERT( m_pMedium || pMed, "No SfxMedium provided!" ); + + if( pMed ) + { + // in this mode the document is also owned and controlled by this instance + m_xBookmarkDocShRef = new ::sd::DrawDocShell(SfxObjectCreateMode::STANDARD, true, DocumentType::Impress); + if (m_xBookmarkDocShRef->DoLoad(pMed)) + m_pBookmarkDoc = m_xBookmarkDocShRef->GetDoc(); + else + m_pBookmarkDoc = nullptr; + } + else if ( m_pMedium ) + // in this mode the document is owned and controlled by the SdDrawDocument + // it can be released by calling the corresponding CloseBookmarkDoc method + // successful creation of a document makes this the owner of the medium + m_pBookmarkDoc = const_cast<SdDrawDocument*>(m_pDoc)->OpenBookmarkDoc(m_pMedium); + + if ( !m_pBookmarkDoc ) + { + std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(m_xTreeView.get(), + VclMessageType::Warning, VclButtonsType::Ok, SdResId(STR_READ_DATA_ERROR))); + xErrorBox->run(); + m_pMedium = nullptr; //On failure the SfxMedium is invalid + } + } + + return m_pBookmarkDoc; +} + +/** + * Entries are inserted only by request (double click) + */ +IMPL_LINK(SdPageObjsTLV, RequestingChildrenHdl, const weld::TreeIter&, rFileEntry, bool) +{ + if (!m_xTreeView->iter_has_child(rFileEntry)) + { + if (GetBookmarkDoc()) + { + SdrObject* pObj = nullptr; + + OUString sImgPage(BMP_PAGE); + OUString sImgPageObjs(BMP_PAGEOBJS); + OUString sImgObjects(BMP_OBJECTS); + OUString sImgOle(BMP_OLE); + OUString sImgGraphic(BMP_GRAPHIC); + + // document name already inserted + + // only insert all "normal" ? slides with objects + sal_uInt16 nPage = 0; + const sal_uInt16 nMaxPages = m_pBookmarkDoc->GetPageCount(); + + std::unique_ptr<weld::TreeIter> xPageEntry; + while (nPage < nMaxPages) + { + SdPage* pPage = static_cast<SdPage*>(m_pBookmarkDoc->GetPage(nPage)); + if (pPage->GetPageKind() == PageKind::Standard) + { + OUString sId(OUString::number(1)); + m_xTreeView->insert(&rFileEntry, -1, &pPage->GetName(), &sId, + nullptr, nullptr, false, m_xScratchIter.get()); + m_xTreeView->set_image(*m_xScratchIter, sImgPage); + + if (!xPageEntry) + { + xPageEntry = m_xTreeView->make_iterator(&rFileEntry); + (void)m_xTreeView->iter_children(*xPageEntry); + } + else + (void)m_xTreeView->iter_next_sibling(*xPageEntry); + + SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups ); + + while( aIter.IsMore() ) + { + pObj = aIter.Next(); + OUString aStr( GetObjectName( pObj ) ); + if( !aStr.isEmpty() ) + { + if( pObj->GetObjInventor() == SdrInventor::Default && pObj->GetObjIdentifier() == SdrObjKind::OLE2 ) + { + m_xTreeView->insert(xPageEntry.get(), -1, &aStr, nullptr, + nullptr, nullptr, false, m_xScratchIter.get()); + m_xTreeView->set_image(*m_xScratchIter, sImgOle); + } + else if( pObj->GetObjInventor() == SdrInventor::Default && pObj->GetObjIdentifier() == SdrObjKind::Graphic ) + { + m_xTreeView->insert(xPageEntry.get(), -1, &aStr, nullptr, + nullptr, nullptr, false, m_xScratchIter.get()); + m_xTreeView->set_image(*m_xScratchIter, sImgGraphic); + } + else + { + m_xTreeView->insert(xPageEntry.get(), -1, &aStr, nullptr, + nullptr, nullptr, false, m_xScratchIter.get()); + m_xTreeView->set_image(*m_xScratchIter, sImgObjects); + } + } + } + if (m_xTreeView->iter_has_child(*xPageEntry)) + { + m_xTreeView->set_image(*xPageEntry, sImgPageObjs); + } + } + nPage++; + } + } + } + return true; +} + +void SdPageObjsTLV::SetSdNavigator(SdNavigatorWin* pNavigator) +{ + m_pNavigator = pNavigator; +} + +void SdPageObjsTLV::SetViewFrame(const SfxViewFrame* pViewFrame) +{ + sd::ViewShellBase* pBase = sd::ViewShellBase::GetViewShellBase(pViewFrame); + std::shared_ptr<sd::ViewShell> xViewShell = pBase->GetMainViewShell(); + SAL_WARN_IF(!xViewShell, "sd", "null pBaseViewFrame"); + const css::uno::Reference< css::frame::XFrame > xFrame = xViewShell ? xViewShell->GetViewFrame()->GetFrame().GetFrameInterface() : nullptr; + m_xAccel->init(::comphelper::getProcessComponentContext(), xFrame); +} + +/** + * Close and delete bookmark document + */ +void SdPageObjsTLV::CloseBookmarkDoc() +{ + if (m_xBookmarkDocShRef.is()) + { + m_xBookmarkDocShRef->DoClose(); + m_xBookmarkDocShRef.clear(); + + // Medium is owned by document, so it's destroyed already + m_pOwnMedium = nullptr; + } + else if (m_pBookmarkDoc) + { + DBG_ASSERT(!m_pOwnMedium, "SfxMedium confusion!"); + if (m_pDoc) + { + // The document owns the Medium, so the Medium will be invalid after closing the document + const_cast<SdDrawDocument*>(m_pDoc)->CloseBookmarkDoc(); + m_pMedium = nullptr; + } + } + else + { + // perhaps mpOwnMedium provided, but no successful creation of BookmarkDoc + delete m_pOwnMedium; + m_pOwnMedium = nullptr; + } + + m_pBookmarkDoc = nullptr; +} + +bool SdPageObjsTLV::PageBelongsToCurrentShow(const SdPage* pPage) const +{ + // Return <TRUE/> as default when there is no custom show or when none + // is used. The page does then belong to the standard show. + bool bBelongsToShow = true; + + if (m_pDoc->getPresentationSettings().mbCustomShow) + { + // Get the current custom show. + SdCustomShow* pCustomShow = nullptr; + SdCustomShowList* pShowList = const_cast<SdDrawDocument*>(m_pDoc)->GetCustomShowList(); + if (pShowList != nullptr) + { + sal_uLong nCurrentShowIndex = pShowList->GetCurPos(); + pCustomShow = (*pShowList)[nCurrentShowIndex].get(); + } + + // Check whether the given page is part of that custom show. + if (pCustomShow != nullptr) + { + bBelongsToShow = false; + size_t nPageCount = pCustomShow->PagesVector().size(); + for (size_t i=0; i<nPageCount && !bBelongsToShow; i++) + if (pPage == pCustomShow->PagesVector()[i]) + bBelongsToShow = true; + } + } + + return bBelongsToShow; +} + +void SdPageObjsTLV::AddShapeList ( + const SdrObjList& rList, + const SdrObject* pShape, + const OUString& rsName, + const bool bIsExcluded, + const weld::TreeIter* pParentEntry) +{ + OUString aIcon(BMP_PAGE); + if (bIsExcluded) + aIcon = BMP_PAGE_EXCLUDED; + else if (pShape != nullptr) + aIcon = BMP_GROUP; + + OUString aUserData("1"); + if (pShape != nullptr) + aUserData = weld::toId(pShape); + + std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator(); + InsertEntry(pParentEntry, aUserData, rsName, aIcon, xEntry.get()); + + SdrObjListIter aIter( + &rList, + !rList.HasObjectNavigationOrder() /* use navigation order, if available */, + SdrIterMode::Flat); + + while( aIter.IsMore() ) + { + SdrObject* pObj = aIter.Next(); + OSL_ASSERT(pObj!=nullptr); + + // Get the shape name. + OUString aStr (GetObjectName( pObj ) ); + OUString sId(weld::toId(pObj)); + + if( !aStr.isEmpty() ) + { + if( pObj->GetObjInventor() == SdrInventor::Default && pObj->GetObjIdentifier() == SdrObjKind::OLE2 ) + { + InsertEntry(xEntry.get(), sId, aStr, BMP_OLE); + } + else if( pObj->GetObjInventor() == SdrInventor::Default && pObj->GetObjIdentifier() == SdrObjKind::Graphic ) + { + InsertEntry(xEntry.get(), sId, aStr, BMP_GRAPHIC); + } + else if (pObj->IsGroupObject()) + { + AddShapeList( + *pObj->GetSubList(), + pObj, + aStr, + false, + xEntry.get()); + } + else + { + InsertEntry(xEntry.get(), sId, aStr, BMP_OBJECTS); + } + } + } + + if (!m_xTreeView->iter_has_child(*xEntry)) + return; + + if (bIsExcluded) + m_xTreeView->set_image(*xEntry, BMP_PAGEOBJS_EXCLUDED); + else + m_xTreeView->set_image(*xEntry, BMP_PAGEOBJS); + m_xTreeView->expand_row(*xEntry); +} + +/** + * Fill TreeLB with pages and objects + */ +void SdPageObjsTLV::Fill(const SdDrawDocument* pInDoc, bool bAllPages, const OUString& rDocName) +{ + OUString aSelection = m_xTreeView->get_selected_text(); + clear(); + + m_pDoc = pInDoc; + m_aDocName = rDocName; + m_bShowAllPages = bAllPages; + m_pMedium = nullptr; + + // first insert all pages including objects + sal_uInt16 nPage = 0; + const sal_uInt16 nMaxPages = m_pDoc->GetPageCount(); + + while( nPage < nMaxPages ) + { + const SdPage* pPage = static_cast<const SdPage*>( m_pDoc->GetPage( nPage ) ); + if( (m_bShowAllPages || pPage->GetPageKind() == PageKind::Standard) + && (pPage->GetPageKind() != PageKind::Handout) ) //#94954# never list the normal handout page ( handout-masterpage is used instead ) + { + bool bPageExcluded = pPage->IsExcluded(); + + bool bPageBelongsToShow = PageBelongsToCurrentShow (pPage); + bPageExcluded |= !bPageBelongsToShow; + + AddShapeList(*pPage, nullptr, pPage->GetName(), bPageExcluded, nullptr); + } + nPage++; + } + + // then insert all master pages including objects + if( m_bShowAllPages ) + { + nPage = 0; + const sal_uInt16 nMaxMasterPages = m_pDoc->GetMasterPageCount(); + + while( nPage < nMaxMasterPages ) + { + const SdPage* pPage = static_cast<const SdPage*>( m_pDoc->GetMasterPage( nPage ) ); + AddShapeList(*pPage, nullptr, pPage->GetName(), false, nullptr); + nPage++; + } + } + if (!aSelection.isEmpty()) + { + m_xTreeView->all_foreach([this, &aSelection](weld::TreeIter& rEntry){ + if (m_xTreeView->get_text(rEntry) == aSelection) + { + m_xTreeView->select(rEntry); + return true; + } + return false; + }); + } +} + +/** + * We insert only the first entry. Children are created on demand. + */ +void SdPageObjsTLV::Fill( const SdDrawDocument* pInDoc, SfxMedium* pInMedium, + const OUString& rDocName ) +{ + m_pDoc = pInDoc; + + // this object now owns the Medium + m_pMedium = pInMedium; + m_aDocName = rDocName; + + OUString sId(OUString::number(1)); + // insert document name + m_xTreeView->insert(nullptr, -1, &m_aDocName, &sId, nullptr, nullptr, true, m_xScratchIter.get()); + m_xTreeView->set_image(*m_xScratchIter, BMP_DOC_OPEN); +} + +/** + * select an entry in TreeLB + */ +bool SdPageObjsTLV::SelectEntry( std::u16string_view rName ) +{ + bool bFound = false; + + if (!rName.empty()) + { + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + OUString aTmp; + + if (m_xTreeView->get_iter_first(*xEntry)) + { + do + { + aTmp = m_xTreeView->get_text(*xEntry); + if (aTmp == rName) + { + m_xTreeView->set_cursor(*xEntry); + m_xTreeView->select(*xEntry); + bFound = true; + break; + } + } + while (m_xTreeView->iter_next(*xEntry)); + } + } + + return bFound; +} + +SdPageObjsTLV::~SdPageObjsTLV() +{ + if (m_nSelectEventId) + Application::RemoveUserEvent(m_nSelectEventId); + if (m_nRowActivateEventId) + Application::RemoveUserEvent(m_nRowActivateEventId); + + if (m_pBookmarkDoc) + CloseBookmarkDoc(); + else + { + // no document was created from m_pMedium, so this object is still the owner of it + delete m_pMedium; + } + m_xAccel.reset(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/sduiexp.cxx b/sd/source/ui/dlg/sduiexp.cxx new file mode 100644 index 000000000..62901c70d --- /dev/null +++ b/sd/source/ui/dlg/sduiexp.cxx @@ -0,0 +1,33 @@ +/* -*- 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 "sddlgfact.hxx" +#include <sal/types.h> + +class SdAbstractDialogFactory; + +extern "C" { +SAL_DLLPUBLIC_EXPORT SdAbstractDialogFactory* SdCreateDialogFactory() +{ + static SdAbstractDialogFactory_Impl aFactory; + return &aFactory; +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/tabtempl.cxx b/sd/source/ui/dlg/tabtempl.cxx new file mode 100644 index 000000000..e5db39e68 --- /dev/null +++ b/sd/source/ui/dlg/tabtempl.cxx @@ -0,0 +1,160 @@ +/* -*- 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 <editeng/flstitem.hxx> + +#include <svx/svxids.hrc> + +#include <svx/drawitem.hxx> +#include <svl/intitem.hxx> +#include <svx/ofaitem.hxx> +#include <svx/svdmodel.hxx> +#include <svl/cjkoptions.hxx> +#include <sfx2/objsh.hxx> +#include <svx/dialogs.hrc> +#include <svl/style.hxx> + +#include <tabtempl.hxx> +#include <svx/flagsdef.hxx> + +/** + * Constructor of the Tab dialog: appends pages to the dialog + */ +SdTabTemplateDlg::SdTabTemplateDlg(weld::Window* pParent, + const SfxObjectShell* pDocShell, + SfxStyleSheetBase& rStyleBase, + SdrModel const * pModel, + SdrView* pView) + : SfxStyleDialogController(pParent, "modules/simpress/ui/templatedialog.ui", + "TemplateDialog", rStyleBase) + , rDocShell(*pDocShell) + , pSdrView(pView) + , pColorList(pModel->GetColorList()) + , pGradientList(pModel->GetGradientList()) + , pHatchingList(pModel->GetHatchList()) + , pBitmapList(pModel->GetBitmapList()) + , pPatternList(pModel->GetPatternList()) + , pDashList(pModel->GetDashList()) + , pLineEndList(pModel->GetLineEndList()) +{ + // fill Listbox and set Select-Handler + + AddTabPage("line", RID_SVXPAGE_LINE); + AddTabPage("area", RID_SVXPAGE_AREA); + AddTabPage("shadowing", RID_SVXPAGE_SHADOW); + AddTabPage("transparency", RID_SVXPAGE_TRANSPARENCE); + AddTabPage("font", RID_SVXPAGE_CHAR_NAME); + AddTabPage("fonteffect", RID_SVXPAGE_CHAR_EFFECTS); + AddTabPage("background", RID_SVXPAGE_BKG); + AddTabPage("indents", RID_SVXPAGE_STD_PARAGRAPH); + AddTabPage("text", RID_SVXPAGE_TEXTATTR); + AddTabPage("animation", RID_SVXPAGE_TEXTANIMATION); + AddTabPage("dimensioning", RID_SVXPAGE_MEASURE); + AddTabPage("connector", RID_SVXPAGE_CONNECTION); + AddTabPage("alignment", RID_SVXPAGE_ALIGN_PARAGRAPH); + AddTabPage("tabs", RID_SVXPAGE_TABULATOR); + if( SvtCJKOptions::IsAsianTypographyEnabled() ) + AddTabPage("asiantypo", RID_SVXPAGE_PARA_ASIAN); + else + RemoveTabPage("asiantypo"); +} + +void SdTabTemplateDlg::PageCreated(const OString& rId, SfxTabPage &rPage) +{ + SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool())); + if (rId == "line") + { + aSet.Put (SvxColorListItem(pColorList,SID_COLOR_TABLE)); + aSet.Put (SvxDashListItem(pDashList,SID_DASH_LIST)); + aSet.Put (SvxLineEndListItem(pLineEndList,SID_LINEEND_LIST)); + aSet.Put (SfxUInt16Item(SID_DLG_TYPE,1)); + rPage.PageCreated(aSet); + } + else if (rId == "area") + { + aSet.Put (SvxColorListItem(pColorList,SID_COLOR_TABLE)); + aSet.Put (SvxGradientListItem(pGradientList,SID_GRADIENT_LIST)); + aSet.Put (SvxHatchListItem(pHatchingList,SID_HATCH_LIST)); + aSet.Put (SvxBitmapListItem(pBitmapList,SID_BITMAP_LIST)); + aSet.Put (SfxUInt16Item(SID_PAGE_TYPE,0)); + aSet.Put (SfxUInt16Item(SID_DLG_TYPE,1)); + aSet.Put (SfxUInt16Item(SID_TABPAGE_POS,0)); + aSet.Put (SvxPatternListItem(pPatternList,SID_PATTERN_LIST)); + rPage.PageCreated(aSet); + } + else if (rId == "shadowing") + { + aSet.Put (SvxColorListItem(pColorList,SID_COLOR_TABLE)); + aSet.Put (SfxUInt16Item(SID_PAGE_TYPE,0)); + aSet.Put (SfxUInt16Item(SID_DLG_TYPE,1)); + rPage.PageCreated(aSet); + } + else if (rId == "transparency") + { + aSet.Put (SfxUInt16Item(SID_PAGE_TYPE,0)); + aSet.Put (SfxUInt16Item(SID_DLG_TYPE,1)); + rPage.PageCreated(aSet); + } + else if (rId == "font") + { + SvxFontListItem aItem(*static_cast<const SvxFontListItem*>( + rDocShell.GetItem( SID_ATTR_CHAR_FONTLIST) ) ); + + aSet.Put (SvxFontListItem( aItem.GetFontList(), SID_ATTR_CHAR_FONTLIST)); + rPage.PageCreated(aSet); + } + else if (rId == "fonteffect") + { + rPage.PageCreated(aSet); + } + else if (rId == "background") + { + aSet.Put(SfxUInt32Item(SID_FLAG_TYPE,static_cast<sal_uInt32>(SvxBackgroundTabFlags::SHOW_CHAR_BKGCOLOR))); + rPage.PageCreated(aSet); + } + else if (rId == "text") + { + rPage.PageCreated(aSet); + } + else if (rId == "dimensioning") + { + aSet.Put (OfaPtrItem(SID_OBJECT_LIST,pSdrView)); + rPage.PageCreated(aSet); + } + else if (rId == "connector") + { + aSet.Put (OfaPtrItem(SID_OBJECT_LIST,pSdrView)); + rPage.PageCreated(aSet); + } +} + +void SdTabTemplateDlg::RefreshInputSet() +{ + SfxItemSet* pInputSet = GetInputSetImpl(); + + if( pInputSet ) + { + pInputSet->ClearItem(); + pInputSet->SetParent( GetStyleSheet().GetItemSet().GetParent() ); + } + else + SetInputSet(&GetStyleSheet().GetItemSet()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/titledockwin.cxx b/sd/source/ui/dlg/titledockwin.cxx new file mode 100644 index 000000000..f2aa744c2 --- /dev/null +++ b/sd/source/ui/dlg/titledockwin.cxx @@ -0,0 +1,261 @@ +/* -*- 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/bindings.hxx> +#include <sfx2/dispatch.hxx> +#include <svl/eitem.hxx> +#include <vcl/event.hxx> +#include <vcl/settings.hxx> +#include <vcl/toolbox.hxx> + +#include <bitmaps.hlst> +#include <strings.hrc> +#include <sdresid.hxx> +#include <titledockwin.hxx> + +namespace sd +{ + //= TitledDockingWindow + TitledDockingWindow::TitledDockingWindow( SfxBindings* i_pBindings, SfxChildWindow* i_pChildWindow, vcl::Window* i_pParent ) + :SfxDockingWindow( i_pBindings, i_pChildWindow, i_pParent, WB_MOVEABLE|WB_CLOSEABLE|WB_DOCKABLE|WB_HIDE|WB_3DLOOK ) + ,m_aToolbox( VclPtr<ToolBox>::Create(this) ) + ,m_aContentWindow( VclPtr<vcl::Window>::Create(this, WB_DIALOGCONTROL) ) + ,m_aBorder( 3, 1, 3, 3 ) + ,m_nTitleBarHeight(0) + { + SetBackground( Wallpaper() ); + + m_aToolbox->SetSelectHdl( LINK( this, TitledDockingWindow, OnToolboxItemSelected ) ); + m_aToolbox->SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetDialogColor() ) ); + m_aToolbox->Show(); + impl_resetToolBox(); + + m_aContentWindow->Show(); + } + + TitledDockingWindow::~TitledDockingWindow() + { + disposeOnce(); + } + + void TitledDockingWindow::dispose() + { + m_aToolbox.disposeAndClear(); + m_aContentWindow.disposeAndClear(); + SfxDockingWindow::dispose(); + } + + void TitledDockingWindow::SetTitle( const OUString& i_rTitle ) + { + m_sTitle = i_rTitle; + Invalidate(); + } + + + void TitledDockingWindow::SetText( const OUString& i_rText ) + { + SfxDockingWindow::SetText( i_rText ); + if ( m_sTitle.isEmpty() ) + // our text is used as title, too => repaint + Invalidate(); + } + + + void TitledDockingWindow::Resize() + { + SfxDockingWindow::Resize(); + impl_layout(); + } + + + void TitledDockingWindow::impl_layout() + { + m_aToolbox->ShowItem( ToolBoxItemId(1), !IsFloatingMode() ); + + const Size aToolBoxSize( m_aToolbox->CalcWindowSizePixel() ); + Size aWindowSize( GetOutputSizePixel() ); + + // position the tool box + m_nTitleBarHeight = GetSettings().GetStyleSettings().GetTitleHeight(); + if ( aToolBoxSize.Height() > m_nTitleBarHeight ) + m_nTitleBarHeight = aToolBoxSize.Height(); + m_aToolbox->SetPosSizePixel( + Point( + aWindowSize.Width() - aToolBoxSize.Width(), + ( m_nTitleBarHeight - aToolBoxSize.Height() ) / 2 + ), + aToolBoxSize + ); + + // Place the content window. + if ( m_nTitleBarHeight < aToolBoxSize.Height() ) + m_nTitleBarHeight = aToolBoxSize.Height(); + aWindowSize.AdjustHeight( -m_nTitleBarHeight ); + m_aContentWindow->SetPosSizePixel( + Point( m_aBorder.Left(), m_nTitleBarHeight + m_aBorder.Top() ), + Size( + aWindowSize.Width() - m_aBorder.Left() - m_aBorder.Right(), + aWindowSize.Height() - m_aBorder.Top() - m_aBorder.Bottom() + ) + ); + } + + void TitledDockingWindow::ApplySettings(vcl::RenderContext& rRenderContext) + { + const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings(); + + // Font + ApplyControlFont(rRenderContext, rStyleSettings.GetAppFont()); + + // Color + ApplyControlForeground(rRenderContext, rStyleSettings.GetButtonTextColor()); + rRenderContext.SetTextFillColor(); + } + + void TitledDockingWindow::Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& i_rArea) + { + const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings(); + + SfxDockingWindow::Paint(rRenderContext, i_rArea); + + rRenderContext.Push(vcl::PushFlags::FONT | vcl::PushFlags::FILLCOLOR | vcl::PushFlags::LINECOLOR); + + rRenderContext.SetFillColor(rStyleSettings.GetDialogColor()); + rRenderContext.SetLineColor(); + + // bold font + vcl::Font aFont(rRenderContext.GetFont()); + aFont.SetWeight(WEIGHT_BOLD); + rRenderContext.SetFont(aFont); + + // Set border values. + Size aWindowSize(GetOutputSizePixel()); + int nOuterLeft = 0; + int nInnerLeft = nOuterLeft + m_aBorder.Left() - 1; + int nOuterRight = aWindowSize.Width() - 1; + int nInnerRight = nOuterRight - m_aBorder.Right() + 1; + int nInnerTop = m_nTitleBarHeight + m_aBorder.Top() - 1; + int nOuterBottom = aWindowSize.Height() - 1; + int nInnerBottom = nOuterBottom - m_aBorder.Bottom() + 1; + + // Paint title bar background. + ::tools::Rectangle aTitleBarBox(::tools::Rectangle(nOuterLeft, 0, nOuterRight, nInnerTop - 1)); + rRenderContext.DrawRect(aTitleBarBox); + + if (nInnerLeft > nOuterLeft) + rRenderContext.DrawRect(::tools::Rectangle(nOuterLeft, nInnerTop, nInnerLeft, nInnerBottom)); + if (nOuterRight > nInnerRight) + rRenderContext.DrawRect(::tools::Rectangle(nInnerRight, nInnerTop, nOuterRight, nInnerBottom)); + if (nInnerBottom < nOuterBottom) + rRenderContext.DrawRect(::tools::Rectangle(nOuterLeft, nInnerBottom, nOuterRight, nOuterBottom)); + + // Paint bevel border. + rRenderContext.SetFillColor(); + rRenderContext.SetLineColor(rStyleSettings.GetShadowColor()); + if (m_aBorder.Top() > 0) + rRenderContext.DrawLine(Point(nInnerLeft, nInnerTop), Point(nInnerLeft, nInnerBottom)); + if (m_aBorder.Left() > 0) + rRenderContext.DrawLine(Point(nInnerLeft, nInnerTop), Point(nInnerRight, nInnerTop)); + + rRenderContext.SetLineColor(rStyleSettings.GetLightColor()); + if (m_aBorder.Bottom() > 0) + rRenderContext.DrawLine(Point(nInnerRight, nInnerBottom), Point(nInnerLeft, nInnerBottom)); + if (m_aBorder.Right() > 0) + rRenderContext.DrawLine(Point(nInnerRight, nInnerBottom), Point(nInnerRight, nInnerTop)); + + // Paint title bar text. + rRenderContext.SetLineColor(rStyleSettings.GetActiveTextColor()); + aTitleBarBox.AdjustLeft(3 ); + rRenderContext.DrawText(aTitleBarBox, + !m_sTitle.isEmpty() ? m_sTitle : GetText(), + DrawTextFlags::Left | DrawTextFlags::VCenter | DrawTextFlags::MultiLine | DrawTextFlags::WordBreak); + + // Restore original values of the output device. + rRenderContext.Pop(); + } + + + void TitledDockingWindow::impl_resetToolBox() + { + m_aToolbox->Clear(); + + // Get the closer bitmap and set it as right most button. + m_aToolbox->InsertItem(ToolBoxItemId(1), Image(StockImage::Yes, SFX_BMP_CLOSE_DOC)); + m_aToolbox->SetQuickHelpText(ToolBoxItemId(1), SdResId(STR_CLOSE_PANE)); + m_aToolbox->ShowItem( ToolBoxItemId(1) ); + } + + + IMPL_LINK( TitledDockingWindow, OnToolboxItemSelected, ToolBox*, pToolBox, void ) + { + const ToolBoxItemId nId = pToolBox->GetCurItemId(); + + if ( nId == ToolBoxItemId(1) ) + { + // the closer + EndTracking(); + const sal_uInt16 nChildWindowId( GetChildWindow_Impl()->GetType() ); + const SfxBoolItem aVisibility( nChildWindowId, false ); + GetBindings().GetDispatcher()->ExecuteList( + nChildWindowId, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aVisibility } + ); + } + } + + + void TitledDockingWindow::StateChanged( StateChangedType i_nType ) + { + switch ( i_nType ) + { + case StateChangedType::InitShow: + impl_layout(); + break; + default:; + } + SfxDockingWindow::StateChanged( i_nType ); + } + + void TitledDockingWindow::DataChanged( const DataChangedEvent& i_rDataChangedEvent ) + { + SfxDockingWindow::DataChanged( i_rDataChangedEvent ); + + switch ( i_rDataChangedEvent.GetType() ) + { + case DataChangedEventType::SETTINGS: + if ( !( i_rDataChangedEvent.GetFlags() & AllSettingsFlags::STYLE ) ) + break; + [[fallthrough]]; + case DataChangedEventType::FONTS: + case DataChangedEventType::FONTSUBSTITUTION: + { + impl_layout(); + Invalidate(); + } + break; + default: break; + } + } + + +} // namespace sfx2 + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/tpaction.cxx b/sd/source/ui/dlg/tpaction.cxx new file mode 100644 index 000000000..f89fa51f5 --- /dev/null +++ b/sd/source/ui/dlg/tpaction.cxx @@ -0,0 +1,801 @@ +/* -*- 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 <svx/svxids.hrc> +#include <com/sun/star/presentation/ClickAction.hpp> +#include <com/sun/star/embed/NeedsRunningStateException.hpp> +#include <com/sun/star/embed/VerbDescriptor.hpp> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <comphelper/string.hxx> +#include <com/sun/star/embed/VerbAttributes.hpp> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> + +#include <sdattr.hrc> +#include <sfx2/sfxresid.hxx> +#include <sfx2/strings.hrc> +#include <o3tl/safeint.hxx> +#include <tools/debug.hxx> +#include <sfx2/app.hxx> +#include <svx/svdograf.hxx> +#include <svl/stritem.hxx> +#include <svx/svdoole2.hxx> +#include <sfx2/docfile.hxx> +#include <svx/xtable.hxx> +#include <vcl/mnemonic.hxx> +#include <svl/intitem.hxx> +#include <svl/urihelper.hxx> +#include <sfx2/filedlghelper.hxx> +#include <svx/drawitem.hxx> +#include <osl/diagnose.h> +#include <o3tl/string_view.hxx> +#include <View.hxx> +#include <sdresid.hxx> +#include <tpaction.hxx> +#include <ViewShell.hxx> +#include <drawdoc.hxx> +#include <DrawDocShell.hxx> +#include <strings.hrc> + +#include <filedlg.hxx> + +#include <algorithm> + +using namespace ::com::sun::star; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; + +#define DOCUMENT_TOKEN '#' + +// XML content stream +constexpr OUStringLiteral pStarDrawXMLContent( u"content.xml" ); + +/** + * Constructor of the Tab dialog: appends the pages to the dialog + */ +SdActionDlg::SdActionDlg(weld::Window* pParent, const SfxItemSet* pAttr, ::sd::View const * pView) + : SfxSingleTabDialogController(pParent, pAttr, "modules/simpress/ui/interactiondialog.ui", + "InteractionDialog") +{ + std::unique_ptr<SfxTabPage> xNewPage = SdTPAction::Create(get_content_area(), this, *pAttr); + + // formerly in PageCreated + static_cast<SdTPAction*>( xNewPage.get() )->SetView( pView ); + static_cast<SdTPAction*>( xNewPage.get() )->Construct(); + + SetTabPage(std::move(xNewPage)); +} + +/** + * Action-TabPage + */ +SdTPAction::SdTPAction(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rInAttrs) + : SfxTabPage(pPage, pController, "modules/simpress/ui/interactionpage.ui", "InteractionPage", &rInAttrs) + , mpView(nullptr) + , mpDoc(nullptr) + , bTreeUpdated(false) + , m_xLbAction(m_xBuilder->weld_combo_box("listbox")) + , m_xFtTree(m_xBuilder->weld_label("fttree")) + , m_xLbTree(new SdPageObjsTLV(m_xBuilder->weld_tree_view("tree"))) + , m_xLbTreeDocument(new SdPageObjsTLV(m_xBuilder->weld_tree_view("treedoc"))) + , m_xLbOLEAction(m_xBuilder->weld_tree_view("oleaction")) + , m_xFrame(m_xBuilder->weld_frame("frame")) + , m_xEdtSound(m_xBuilder->weld_entry("sound")) + , m_xEdtBookmark(m_xBuilder->weld_entry("bookmark")) + , m_xEdtDocument(m_xBuilder->weld_entry("document")) + , m_xEdtProgram(m_xBuilder->weld_entry("program")) + , m_xEdtMacro(m_xBuilder->weld_entry("macro")) + , m_xBtnSearch(m_xBuilder->weld_button("browse")) + , m_xBtnSeek(m_xBuilder->weld_button("find")) +{ + m_xLbOLEAction->set_size_request(m_xLbOLEAction->get_approximate_digit_width() * 48, + m_xLbOLEAction->get_height_rows(12)); + + m_xBtnSearch->connect_clicked( LINK( this, SdTPAction, ClickSearchHdl ) ); + m_xBtnSeek->connect_clicked( LINK( this, SdTPAction, ClickSearchHdl ) ); + + // this page needs ExchangeSupport + SetExchangeSupport(); + + m_xLbAction->connect_changed( LINK( this, SdTPAction, ClickActionHdl ) ); + m_xLbTree->connect_changed( LINK( this, SdTPAction, SelectTreeHdl ) ); + m_xEdtDocument->connect_focus_out( LINK( this, SdTPAction, CheckFileHdl ) ); + m_xEdtMacro->connect_focus_out( LINK( this, SdTPAction, CheckFileHdl ) ); + + //Lock to initial max size + Size aSize(m_xContainer->get_preferred_size()); + m_xContainer->set_size_request(aSize.Width(), aSize.Height()); + + ClickActionHdl( *m_xLbAction ); +} + +SdTPAction::~SdTPAction() +{ +} + +void SdTPAction::SetView( const ::sd::View* pSdView ) +{ + mpView = pSdView; + + // get ColorTable and fill ListBox + ::sd::DrawDocShell* pDocSh = mpView->GetDocSh(); + if( pDocSh && pDocSh->GetViewShell() ) + { + mpDoc = pDocSh->GetDoc(); + SfxViewFrame* pFrame = pDocSh->GetViewShell()->GetViewFrame(); + m_xLbTree->SetViewFrame( pFrame ); + m_xLbTreeDocument->SetViewFrame( pFrame ); + + pColList = pDocSh->GetItem( SID_COLOR_TABLE )->GetColorList(); + DBG_ASSERT( pColList.is(), "No color table available!" ); + } + else + { + OSL_FAIL("sd::SdTPAction::SetView(), no docshell or viewshell?"); + } +} + +void SdTPAction::Construct() +{ + // fill OLE-Actionlistbox + SdrOle2Obj* pOleObj = nullptr; + SdrGrafObj* pGrafObj = nullptr; + bool bOLEAction = false; + + if ( mpView->AreObjectsMarked() ) + { + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + if (rMarkList.GetMarkCount() == 1) + { + SdrMark* pMark = rMarkList.GetMark(0); + SdrObject* pObj = pMark->GetMarkedSdrObj(); + + SdrInventor nInv = pObj->GetObjInventor(); + SdrObjKind nSdrObjKind = pObj->GetObjIdentifier(); + + if (nInv == SdrInventor::Default && nSdrObjKind == SdrObjKind::OLE2) + { + pOleObj = static_cast<SdrOle2Obj*>(pObj); + } + else if (nInv == SdrInventor::Default && nSdrObjKind == SdrObjKind::Graphic) + { + pGrafObj = static_cast<SdrGrafObj*>(pObj); + } + } + } + if( pGrafObj ) + { + bOLEAction = true; + + aVerbVector.push_back( 0 ); + m_xLbOLEAction->append_text( MnemonicGenerator::EraseAllMnemonicChars( SdResId( STR_EDIT_OBJ ) ) ); + } + else if( pOleObj ) + { + const uno::Reference < embed::XEmbeddedObject >& xObj = pOleObj->GetObjRef(); + if ( xObj.is() ) + { + bOLEAction = true; + uno::Sequence < embed::VerbDescriptor > aVerbs; + try + { + aVerbs = xObj->getSupportedVerbs(); + } + catch ( embed::NeedsRunningStateException& ) + { + xObj->changeState( embed::EmbedStates::RUNNING ); + aVerbs = xObj->getSupportedVerbs(); + } + + for( const embed::VerbDescriptor& aVerb : std::as_const(aVerbs) ) + { + if( aVerb.VerbAttributes & embed::VerbAttributes::MS_VERBATTR_ONCONTAINERMENU ) + { + OUString aTmp( aVerb.VerbName ); + aVerbVector.push_back( aVerb.VerbID ); + m_xLbOLEAction->append_text( MnemonicGenerator::EraseAllMnemonicChars( aTmp ) ); + } + } + } + } + + maCurrentActions.push_back( presentation::ClickAction_NONE ); + maCurrentActions.push_back( presentation::ClickAction_PREVPAGE ); + maCurrentActions.push_back( presentation::ClickAction_NEXTPAGE ); + maCurrentActions.push_back( presentation::ClickAction_FIRSTPAGE ); + maCurrentActions.push_back( presentation::ClickAction_LASTPAGE ); + maCurrentActions.push_back( presentation::ClickAction_BOOKMARK ); + maCurrentActions.push_back( presentation::ClickAction_DOCUMENT ); + maCurrentActions.push_back( presentation::ClickAction_SOUND ); + if( bOLEAction && m_xLbOLEAction->n_children() ) + maCurrentActions.push_back( presentation::ClickAction_VERB ); + maCurrentActions.push_back( presentation::ClickAction_PROGRAM ); + maCurrentActions.push_back( presentation::ClickAction_MACRO ); + maCurrentActions.push_back( presentation::ClickAction_STOPPRESENTATION ); + + // fill Action-Listbox + for (const presentation::ClickAction & rAction : maCurrentActions) + { + TranslateId pRId = GetClickActionSdResId(rAction); + m_xLbAction->append_text(SdResId(pRId)); + } + +} + +bool SdTPAction::FillItemSet( SfxItemSet* rAttrs ) +{ + bool bModified = false; + presentation::ClickAction eCA = presentation::ClickAction_NONE; + + if (m_xLbAction->get_active() != -1) + eCA = GetActualClickAction(); + + if( m_xLbAction->get_value_changed_from_saved() ) + { + rAttrs->Put( SfxUInt16Item( ATTR_ACTION, static_cast<sal_uInt16>(eCA) ) ); + bModified = true; + } + else + rAttrs->InvalidateItem( ATTR_ACTION ); + + OUString aFileName = GetEditText( true ); + if( aFileName.isEmpty() ) + rAttrs->InvalidateItem( ATTR_ACTION_FILENAME ); + else + { + if( mpDoc && mpDoc->GetDocSh() && mpDoc->GetDocSh()->GetMedium() ) + { + OUString aBaseURL = mpDoc->GetDocSh()->GetMedium()->GetBaseURL(); + if( eCA == presentation::ClickAction_SOUND || + eCA == presentation::ClickAction_DOCUMENT || + eCA == presentation::ClickAction_PROGRAM ) + aFileName = ::URIHelper::SmartRel2Abs( INetURLObject(aBaseURL), aFileName, URIHelper::GetMaybeFileHdl(), true, false, + INetURLObject::EncodeMechanism::WasEncoded, + INetURLObject::DecodeMechanism::Unambiguous ); + + rAttrs->Put( SfxStringItem( ATTR_ACTION_FILENAME, aFileName ) ); + bModified = true; + } + else + { + OSL_FAIL("sd::SdTPAction::FillItemSet(), I need a medium!"); + } + } + + return bModified; +} + +void SdTPAction::Reset( const SfxItemSet* rAttrs ) +{ + presentation::ClickAction eCA = presentation::ClickAction_NONE; + OUString aFileName; + + // m_xLbAction + if( rAttrs->GetItemState( ATTR_ACTION ) != SfxItemState::DONTCARE ) + { + eCA = static_cast<presentation::ClickAction>(static_cast<const SfxUInt16Item&>( rAttrs-> + Get( ATTR_ACTION ) ).GetValue()); + SetActualClickAction( eCA ); + } + else + m_xLbAction->set_active(-1); + + // m_xEdtSound + if( rAttrs->GetItemState( ATTR_ACTION_FILENAME ) != SfxItemState::DONTCARE ) + { + aFileName = static_cast<const SfxStringItem&>( rAttrs->Get( ATTR_ACTION_FILENAME ) ).GetValue(); + SetEditText( aFileName ); + } + + switch( eCA ) + { + case presentation::ClickAction_BOOKMARK: + { + if (!m_xLbTree->SelectEntry(aFileName)) + m_xLbTree->unselect_all(); + } + break; + + case presentation::ClickAction_DOCUMENT: + { + if( comphelper::string::getTokenCount(aFileName, DOCUMENT_TOKEN) == 2 ) + m_xLbTreeDocument->SelectEntry( o3tl::getToken(aFileName, 1, DOCUMENT_TOKEN ) ); + } + break; + + default: + break; + } + ClickActionHdl( *m_xLbAction ); + + m_xLbAction->save_value(); + m_xEdtSound->save_value(); +} + +void SdTPAction::ActivatePage( const SfxItemSet& ) +{ +} + +DeactivateRC SdTPAction::DeactivatePage( SfxItemSet* pPageSet ) +{ + if( pPageSet ) + FillItemSet( pPageSet ); + + return DeactivateRC::LeavePage; +} + +std::unique_ptr<SfxTabPage> SdTPAction::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rAttrs) +{ + return std::make_unique<SdTPAction>( pPage, pController, rAttrs ); +} + +void SdTPAction::UpdateTree() +{ + if( !bTreeUpdated && mpDoc && mpDoc->GetDocSh() && mpDoc->GetDocSh()->GetMedium() ) + { + m_xLbTree->Fill( mpDoc, true, mpDoc->GetDocSh()->GetMedium()->GetName() ); + bTreeUpdated = true; + } +} + +void SdTPAction::OpenFileDialog() +{ + // Soundpreview only for interaction with sound + presentation::ClickAction eCA = GetActualClickAction(); + bool bSound = ( eCA == presentation::ClickAction_SOUND ); + bool bPage = ( eCA == presentation::ClickAction_BOOKMARK ); + bool bDocument = ( eCA == presentation::ClickAction_DOCUMENT || + eCA == presentation::ClickAction_PROGRAM ); + bool bMacro = ( eCA == presentation::ClickAction_MACRO ); + + if( bPage ) + { + // search in the TreeLB for the specified object + m_xLbTree->SelectEntry(GetEditText()); + } + else + { + OUString aFile( GetEditText() ); + + if (bSound) + { + SdOpenSoundFileDialog aFileDialog(GetFrameWeld()); + + if( !aFile.isEmpty() ) + aFileDialog.SetPath( aFile ); + + if( aFileDialog.Execute() == ERRCODE_NONE ) + { + aFile = aFileDialog.GetPath(); + SetEditText( aFile ); + } + } + else if (bMacro) + { + // choose macro dialog + OUString aScriptURL = SfxApplication::ChooseScript(GetFrameWeld()); + + if ( !aScriptURL.isEmpty() ) + { + SetEditText( aScriptURL ); + } + } + else + { + sfx2::FileDialogHelper aFileDialog( + ui::dialogs::TemplateDescription::FILEOPEN_READONLY_VERSION, + FileDialogFlags::NONE, GetFrameWeld()); + aFileDialog.SetContext(sfx2::FileDialogHelper::ImpressClickAction); + + // The following is a workaround for #i4306#: + // The addition of the implicitly existing "all files" + // filter makes the (Windows system) open file dialog follow + // links on the desktop to directories. + aFileDialog.AddFilter ( + SfxResId(STR_SFX_FILTERNAME_ALL), + "*.*"); + + if( aFileDialog.Execute() == ERRCODE_NONE ) + { + aFile = aFileDialog.GetPath(); + SetEditText( aFile ); + } + if( bDocument ) + CheckFileHdl( *m_xEdtDocument ); + } + } +} + +IMPL_LINK_NOARG(SdTPAction, ClickSearchHdl, weld::Button&, void) +{ + OpenFileDialog(); +} + +IMPL_LINK_NOARG(SdTPAction, ClickActionHdl, weld::ComboBox&, void) +{ + presentation::ClickAction eCA = GetActualClickAction(); + + // hide controls we don't need + switch( eCA ) + { + case presentation::ClickAction_NONE: + case presentation::ClickAction_INVISIBLE: + case presentation::ClickAction_PREVPAGE: + case presentation::ClickAction_NEXTPAGE: + case presentation::ClickAction_FIRSTPAGE: + case presentation::ClickAction_LASTPAGE: + case presentation::ClickAction_STOPPRESENTATION: + default: + m_xFtTree->hide(); + m_xLbTree->hide(); + m_xLbTreeDocument->hide(); + m_xLbOLEAction->hide(); + + m_xFrame->hide(); + m_xEdtSound->hide(); + m_xEdtBookmark->hide(); + m_xEdtDocument->hide(); + m_xEdtProgram->hide(); + m_xEdtMacro->hide(); + m_xBtnSearch->hide(); + m_xBtnSeek->hide(); + break; + + case presentation::ClickAction_SOUND: + case presentation::ClickAction_PROGRAM: + case presentation::ClickAction_MACRO: + m_xFtTree->hide(); + m_xLbTree->hide(); + m_xLbTreeDocument->hide(); + m_xLbOLEAction->hide(); + + m_xEdtDocument->hide(); + + if( eCA == presentation::ClickAction_MACRO ) + { + m_xEdtSound->hide(); + m_xEdtProgram->hide(); + } + else if( eCA == presentation::ClickAction_PROGRAM ) + { + m_xEdtSound->hide(); + m_xEdtMacro->hide(); + } + else if( eCA == presentation::ClickAction_SOUND ) + { + m_xEdtProgram->hide(); + m_xEdtMacro->hide(); + } + + m_xBtnSeek->hide(); + break; + + case presentation::ClickAction_DOCUMENT: + m_xLbTree->hide(); + m_xLbOLEAction->hide(); + + m_xEdtSound->hide(); + m_xEdtProgram->hide(); + m_xEdtMacro->hide(); + m_xEdtBookmark->hide(); + m_xBtnSeek->hide(); + break; + + case presentation::ClickAction_BOOKMARK: + m_xLbTreeDocument->hide(); + m_xLbOLEAction->hide(); + m_xEdtSound->hide(); + m_xEdtDocument->hide(); + m_xEdtProgram->hide(); + m_xEdtMacro->hide(); + m_xBtnSearch->hide(); + break; + + case presentation::ClickAction_VERB: + m_xLbTree->hide(); + m_xEdtDocument->hide(); + m_xEdtProgram->hide(); + m_xEdtBookmark->hide(); + m_xEdtMacro->hide(); + m_xBtnSearch->hide(); + m_xFrame->hide(); + m_xEdtSound->hide(); + m_xBtnSeek->hide(); + break; + } + + // show controls we do need + switch( eCA ) + { + case presentation::ClickAction_NONE: + case presentation::ClickAction_INVISIBLE: + case presentation::ClickAction_PREVPAGE: + case presentation::ClickAction_NEXTPAGE: + case presentation::ClickAction_FIRSTPAGE: + case presentation::ClickAction_LASTPAGE: + case presentation::ClickAction_STOPPRESENTATION: + // none + break; + + case presentation::ClickAction_SOUND: + m_xFrame->show(); + m_xEdtSound->show(); + m_xEdtSound->set_sensitive(true); + m_xBtnSearch->show(); + m_xBtnSearch->set_sensitive(true); + m_xFrame->set_label( SdResId( STR_EFFECTDLG_SOUND ) ); + break; + + case presentation::ClickAction_PROGRAM: + case presentation::ClickAction_MACRO: + m_xFrame->show(); + m_xBtnSearch->show(); + m_xBtnSearch->set_sensitive(true); + if( eCA == presentation::ClickAction_MACRO ) + { + m_xEdtMacro->show(); + m_xFrame->set_label( SdResId( STR_EFFECTDLG_MACRO ) ); + } + else + { + m_xEdtProgram->show(); + m_xFrame->set_label( SdResId( STR_EFFECTDLG_PROGRAM ) ); + } + break; + + case presentation::ClickAction_DOCUMENT: + m_xFtTree->show(); + m_xLbTreeDocument->show(); + + m_xFrame->show(); + m_xEdtDocument->show(); + m_xBtnSearch->show(); + m_xBtnSearch->set_sensitive(true); + + m_xFtTree->set_label( SdResId( STR_EFFECTDLG_JUMP ) ); + m_xFrame->set_label( SdResId( STR_EFFECTDLG_DOCUMENT ) ); + + CheckFileHdl( *m_xEdtDocument ); + break; + + case presentation::ClickAction_VERB: + m_xFtTree->show(); + m_xLbOLEAction->show(); + + m_xFtTree->set_label( SdResId( STR_EFFECTDLG_ACTION ) ); + break; + + case presentation::ClickAction_BOOKMARK: + UpdateTree(); + + m_xFtTree->show(); + m_xLbTree->show(); + + m_xFrame->show(); + m_xEdtBookmark->show(); + m_xBtnSeek->show(); + + m_xFtTree->set_label( SdResId( STR_EFFECTDLG_JUMP ) ); + m_xFrame->set_label( SdResId( STR_EFFECTDLG_PAGE_OBJECT ) ); + break; + default: + break; + } +} + +IMPL_LINK_NOARG(SdTPAction, SelectTreeHdl, weld::TreeView&, void) +{ + m_xEdtBookmark->set_text( m_xLbTree->get_selected_text() ); +} + +IMPL_LINK_NOARG(SdTPAction, CheckFileHdl, weld::Widget&, void) +{ + OUString aFile( GetEditText() ); + + if( aFile == aLastFile ) + return; + + bool bHideTreeDocument = true; + + if (mpDoc) + { + // check if it is a valid draw file + SfxMedium aMedium( aFile, + StreamMode::READ | StreamMode::NOCREATE ); + + if( aMedium.IsStorage() ) + { + weld::WaitObject aWait(GetFrameWeld()); + + // is it a draw file? + // open with READ, otherwise the Storages might write into the file! + uno::Reference < embed::XStorage > xStorage = aMedium.GetStorage(); + DBG_ASSERT( xStorage.is(), "No storage!" ); + + if (xStorage.is()) + { + try + { + if (xStorage->hasByName(pStarDrawXMLContent)) + { + if (SdDrawDocument* pBookmarkDoc = mpDoc->OpenBookmarkDoc(aFile)) + { + aLastFile = aFile; + + m_xLbTreeDocument->clear(); + m_xLbTreeDocument->Fill(pBookmarkDoc, true, aFile); + mpDoc->CloseBookmarkDoc(); + m_xLbTreeDocument->show(); + bHideTreeDocument = false; + } + } + } + catch (...) + { + } + } + } + } + + if (bHideTreeDocument) + m_xLbTreeDocument->hide(); +} + +presentation::ClickAction SdTPAction::GetActualClickAction() +{ + presentation::ClickAction eCA = presentation::ClickAction_NONE; + int nPos = m_xLbAction->get_active(); + if (nPos != -1 && o3tl::make_unsigned(nPos) < maCurrentActions.size()) + eCA = maCurrentActions[ nPos ]; + return eCA; +} + +void SdTPAction::SetActualClickAction( presentation::ClickAction eCA ) +{ + std::vector<css::presentation::ClickAction>::const_iterator pIter = + std::find(maCurrentActions.begin(),maCurrentActions.end(),eCA); + + if ( pIter != maCurrentActions.end() ) + m_xLbAction->set_active(pIter-maCurrentActions.begin()); +} + +void SdTPAction::SetEditText( OUString const & rStr ) +{ + presentation::ClickAction eCA = GetActualClickAction(); + OUString aText(rStr); + + // possibly convert URI back to system path + switch( eCA ) + { + case presentation::ClickAction_DOCUMENT: + if( comphelper::string::getTokenCount(rStr, DOCUMENT_TOKEN) == 2 ) + aText = rStr.getToken( 0, DOCUMENT_TOKEN ); + + [[fallthrough]]; + case presentation::ClickAction_SOUND: + case presentation::ClickAction_PROGRAM: + { + INetURLObject aURL( aText ); + + // try to convert to system path + OUString aTmpStr(aURL.getFSysPath(FSysStyle::Detect)); + + if( !aTmpStr.isEmpty() ) + aText = aTmpStr; // was a system path + } + break; + default: + break; + } + + // set the string to the corresponding control + switch( eCA ) + { + case presentation::ClickAction_SOUND: + m_xEdtSound->set_text(aText ); + break; + case presentation::ClickAction_VERB: + { + ::std::vector< tools::Long >::iterator aFound( ::std::find( aVerbVector.begin(), aVerbVector.end(), rStr.toInt32() ) ); + if( aFound != aVerbVector.end() ) + m_xLbOLEAction->select(aFound - aVerbVector.begin()); + } + break; + case presentation::ClickAction_PROGRAM: + m_xEdtProgram->set_text( aText ); + break; + case presentation::ClickAction_MACRO: + m_xEdtMacro->set_text( aText ); + break; + case presentation::ClickAction_DOCUMENT: + m_xEdtDocument->set_text( aText ); + break; + case presentation::ClickAction_BOOKMARK: + m_xEdtBookmark->set_text( aText ); + break; + default: + break; + } +} + +OUString SdTPAction::GetEditText( bool bFullDocDestination ) +{ + OUString aStr; + presentation::ClickAction eCA = GetActualClickAction(); + + switch( eCA ) + { + case presentation::ClickAction_SOUND: + aStr = m_xEdtSound->get_text(); + break; + case presentation::ClickAction_VERB: + { + const int nPos = m_xLbOLEAction->get_selected_index(); + if (nPos != -1 && o3tl::make_unsigned(nPos) < aVerbVector.size() ) + aStr = OUString::number( aVerbVector[ nPos ] ); + return aStr; + } + case presentation::ClickAction_DOCUMENT: + aStr = m_xEdtDocument->get_text(); + break; + + case presentation::ClickAction_PROGRAM: + aStr = m_xEdtProgram->get_text(); + break; + + case presentation::ClickAction_MACRO: + { + return m_xEdtMacro->get_text(); + } + + case presentation::ClickAction_BOOKMARK: + return m_xEdtBookmark->get_text(); + + default: + break; + } + + // validate file URI + INetURLObject aURL( aStr ); + OUString aBaseURL; + if( mpDoc && mpDoc->GetDocSh() && mpDoc->GetDocSh()->GetMedium() ) + aBaseURL = mpDoc->GetDocSh()->GetMedium()->GetBaseURL(); + + if( !aStr.isEmpty() && aURL.GetProtocol() == INetProtocol::NotValid ) + aURL = INetURLObject( ::URIHelper::SmartRel2Abs( INetURLObject(aBaseURL), aStr, URIHelper::GetMaybeFileHdl() ) ); + + // get adjusted file name + aStr = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + if( bFullDocDestination && + eCA == presentation::ClickAction_DOCUMENT && + m_xLbTreeDocument->get_visible() && + m_xLbTreeDocument->get_selected() ) + { + OUString aTmpStr( m_xLbTreeDocument->get_selected_text() ); + if( !aTmpStr.isEmpty() ) + { + aStr += OUStringChar(DOCUMENT_TOKEN) + aTmpStr; + } + } + + return aStr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/tpoption.cxx b/sd/source/ui/dlg/tpoption.cxx new file mode 100644 index 000000000..0c534682d --- /dev/null +++ b/sd/source/ui/dlg/tpoption.cxx @@ -0,0 +1,618 @@ +/* -*- 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 <com/sun/star/document/PrinterIndependentLayout.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <comphelper/processfactory.hxx> +#include <comphelper/string.hxx> +#include <com/sun/star/uno/Exception.hpp> +#include <sfx2/module.hxx> +#include <svx/svxids.hrc> +#include <svx/strarray.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <svtools/unitconv.hxx> + +#include <sdattr.hrc> +#include <sdresid.hxx> +#include <optsitem.hxx> +#include <tpoption.hxx> +#include <strings.hrc> +#include <app.hrc> +#include <svl/intitem.hxx> +#include <o3tl/string_view.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +SdTpOptionsSnap::SdTpOptionsSnap(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rInAttrs) + : SvxGridTabPage(pPage, pController, rInAttrs) +{ + m_xSnapFrames->show(); +} + +SdTpOptionsSnap::~SdTpOptionsSnap() +{ +} + +bool SdTpOptionsSnap::FillItemSet( SfxItemSet* rAttrs ) +{ + SvxGridTabPage::FillItemSet(rAttrs); + SdOptionsSnapItem aOptsItem; + + aOptsItem.GetOptionsSnap().SetSnapHelplines( m_xCbxSnapHelplines->get_active() ); + aOptsItem.GetOptionsSnap().SetSnapBorder( m_xCbxSnapBorder->get_active() ); + aOptsItem.GetOptionsSnap().SetSnapFrame( m_xCbxSnapFrame->get_active() ); + aOptsItem.GetOptionsSnap().SetSnapPoints( m_xCbxSnapPoints->get_active() ); + aOptsItem.GetOptionsSnap().SetOrtho( m_xCbxOrtho->get_active() ); + aOptsItem.GetOptionsSnap().SetBigOrtho( m_xCbxBigOrtho->get_active() ); + aOptsItem.GetOptionsSnap().SetRotate( m_xCbxRotate->get_active() ); + aOptsItem.GetOptionsSnap().SetSnapArea(static_cast<sal_Int16>(m_xMtrFldSnapArea->get_value(FieldUnit::PIXEL))); + aOptsItem.GetOptionsSnap().SetAngle(Degree100(m_xMtrFldAngle->get_value(FieldUnit::DEGREE))); + aOptsItem.GetOptionsSnap().SetEliminatePolyPointLimitAngle(Degree100(m_xMtrFldBezAngle->get_value(FieldUnit::DEGREE))); + + rAttrs->Put( aOptsItem ); + + // we get a possible existing GridItem, this ensures that we do not set + // some default values by accident + return true; +} + +void SdTpOptionsSnap::Reset( const SfxItemSet* rAttrs ) +{ + SvxGridTabPage::Reset(rAttrs); + + SdOptionsSnapItem aOptsItem( rAttrs->Get( ATTR_OPTIONS_SNAP ) ); + + m_xCbxSnapHelplines->set_active( aOptsItem.GetOptionsSnap().IsSnapHelplines() ); + m_xCbxSnapBorder->set_active( aOptsItem.GetOptionsSnap().IsSnapBorder() ); + m_xCbxSnapFrame->set_active( aOptsItem.GetOptionsSnap().IsSnapFrame() ); + m_xCbxSnapPoints->set_active( aOptsItem.GetOptionsSnap().IsSnapPoints() ); + m_xCbxOrtho->set_active( aOptsItem.GetOptionsSnap().IsOrtho() ); + m_xCbxBigOrtho->set_active( aOptsItem.GetOptionsSnap().IsBigOrtho() ); + m_xCbxRotate->set_active( aOptsItem.GetOptionsSnap().IsRotate() ); + m_xMtrFldSnapArea->set_value(aOptsItem.GetOptionsSnap().GetSnapArea(), FieldUnit::PIXEL); + m_xMtrFldAngle->set_value(aOptsItem.GetOptionsSnap().GetAngle().get(), FieldUnit::DEGREE); + m_xMtrFldBezAngle->set_value(aOptsItem.GetOptionsSnap().GetEliminatePolyPointLimitAngle().get(), FieldUnit::DEGREE); + + ClickRotateHdl_Impl(*m_xCbxRotate); +} + +std::unique_ptr<SfxTabPage> SdTpOptionsSnap::Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrs ) +{ + return std::make_unique<SdTpOptionsSnap>(pPage, pController, *rAttrs); +} + +/************************************************************************* +|* +|* TabPage to adjust the content options +|* +\************************************************************************/ +SdTpOptionsContents::SdTpOptionsContents(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rInAttrs) + : SfxTabPage(pPage, pController, "modules/simpress/ui/sdviewpage.ui", "SdViewPage", &rInAttrs) + , m_xCbxRuler(m_xBuilder->weld_check_button("ruler")) + , m_xCbxDragStripes(m_xBuilder->weld_check_button("dragstripes")) + , m_xCbxHandlesBezier(m_xBuilder->weld_check_button("handlesbezier")) + , m_xCbxMoveOutline(m_xBuilder->weld_check_button("moveoutline")) +{ +} + +SdTpOptionsContents::~SdTpOptionsContents() +{ +} + +bool SdTpOptionsContents::FillItemSet( SfxItemSet* rAttrs ) +{ + bool bModified = false; + + if( m_xCbxRuler->get_state_changed_from_saved() || + m_xCbxMoveOutline->get_state_changed_from_saved() || + m_xCbxDragStripes->get_state_changed_from_saved() || + m_xCbxHandlesBezier->get_state_changed_from_saved() ) + { + SdOptionsLayoutItem aOptsItem; + + aOptsItem.GetOptionsLayout().SetRulerVisible( m_xCbxRuler->get_active() ); + aOptsItem.GetOptionsLayout().SetMoveOutline( m_xCbxMoveOutline->get_active() ); + aOptsItem.GetOptionsLayout().SetDragStripes( m_xCbxDragStripes->get_active() ); + aOptsItem.GetOptionsLayout().SetHandlesBezier( m_xCbxHandlesBezier->get_active() ); + + rAttrs->Put( aOptsItem ); + bModified = true; + } + return bModified; +} + +void SdTpOptionsContents::Reset( const SfxItemSet* rAttrs ) +{ + SdOptionsLayoutItem aLayoutItem( rAttrs->Get( ATTR_OPTIONS_LAYOUT ) ); + + m_xCbxRuler->set_active( aLayoutItem.GetOptionsLayout().IsRulerVisible() ); + m_xCbxMoveOutline->set_active( aLayoutItem.GetOptionsLayout().IsMoveOutline() ); + m_xCbxDragStripes->set_active( aLayoutItem.GetOptionsLayout().IsDragStripes() ); + m_xCbxHandlesBezier->set_active( aLayoutItem.GetOptionsLayout().IsHandlesBezier() ); + + m_xCbxRuler->save_state(); + m_xCbxMoveOutline->save_state(); + m_xCbxDragStripes->save_state(); + m_xCbxHandlesBezier->save_state(); +} + +std::unique_ptr<SfxTabPage> SdTpOptionsContents::Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrs ) +{ + return std::make_unique<SdTpOptionsContents>(pPage, pController, *rAttrs); +} + +/************************************************************************* +|* +|* TabPage to adjust the misc options +|* +\************************************************************************/ +#define TABLE_COUNT 12 +#define TOKEN ':' + +SdTpOptionsMisc::SdTpOptionsMisc(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rInAttrs) + : SfxTabPage(pPage, pController, "modules/simpress/ui/optimpressgeneralpage.ui", "OptSavePage", &rInAttrs) + , nWidth(0) + , nHeight(0) + , m_xCbxQuickEdit(m_xBuilder->weld_check_button("qickedit")) + , m_xCbxPickThrough(m_xBuilder->weld_check_button("textselected")) + , m_xNewDocumentFrame(m_xBuilder->weld_frame("newdocumentframe")) + , m_xCbxStartWithTemplate(m_xBuilder->weld_check_button("startwithwizard")) + , m_xCbxMasterPageCache(m_xBuilder->weld_check_button("backgroundback")) + , m_xCbxCopy(m_xBuilder->weld_check_button("copywhenmove")) + , m_xCbxMarkedHitMovesAlways(m_xBuilder->weld_check_button("objalwymov")) + , m_xPresentationFrame(m_xBuilder->weld_frame("presentationframe")) + , m_xLbMetric(m_xBuilder->weld_combo_box("units")) + , m_xMtrFldTabstop(m_xBuilder->weld_metric_spin_button("metricFields", FieldUnit::MM)) + , m_xCbxEnableSdremote(m_xBuilder->weld_check_button("enremotcont")) + , m_xCbxEnablePresenterScreen(m_xBuilder->weld_check_button("enprsntcons")) + , m_xCbxUsePrinterMetrics(m_xBuilder->weld_check_button("printermetrics")) + , m_xCbxCompatibility(m_xBuilder->weld_check_button("cbCompatibility")) + , m_xScaleFrame(m_xBuilder->weld_frame("scaleframe")) + , m_xCbScale(m_xBuilder->weld_combo_box("scaleBox")) + , m_xNewDocLb(m_xBuilder->weld_label("newdoclbl")) + , m_xFiInfo1(m_xBuilder->weld_label("info1")) + , m_xMtrFldOriginalWidth(m_xBuilder->weld_metric_spin_button("metricWidthFields", FieldUnit::MM)) + , m_xWidthLb(m_xBuilder->weld_label("widthlbl")) + , m_xHeightLb(m_xBuilder->weld_label("heightlbl")) + , m_xFiInfo2(m_xBuilder->weld_label("info2")) + , m_xMtrFldOriginalHeight(m_xBuilder->weld_metric_spin_button("metricHeightFields", FieldUnit::MM)) + , m_xCbxDistort(m_xBuilder->weld_check_button("distortcb")) + , m_xMtrFldInfo1(m_xBuilder->weld_metric_spin_button("metricInfo1Fields", FieldUnit::MM)) + , m_xMtrFldInfo2(m_xBuilder->weld_metric_spin_button("metricInfo2Fields", FieldUnit::MM)) +{ + SetExchangeSupport(); + + // set metric + FieldUnit eFUnit; + + sal_uInt16 nWhich = GetWhich( SID_ATTR_METRIC ); + if ( rInAttrs.GetItemState( nWhich ) >= SfxItemState::DEFAULT ) + { + const SfxUInt16Item& rItem = static_cast<const SfxUInt16Item&>(rInAttrs.Get( nWhich )); + eFUnit = static_cast<FieldUnit>(rItem.GetValue()); + } + else + eFUnit = SfxModule::GetCurrentFieldUnit(); + + SetFieldUnit( *m_xMtrFldTabstop , eFUnit ); + // tdf#148292 - avoid right frame to change position depending on width of this control + m_xMtrFldTabstop->set_size_request(150, -1); + // Impress is default mode, let' hide the entire scale frame etc. + m_xCbxDistort->hide(); + m_xScaleFrame->hide(); + + // fill ListBox with metrics + for (sal_uInt32 i = 0; i < SvxFieldUnitTable::Count(); ++i) + { + OUString sMetric = SvxFieldUnitTable::GetString(i); + sal_uInt32 nFieldUnit = sal_uInt32(SvxFieldUnitTable::GetValue(i)); + m_xLbMetric->append(OUString::number(nFieldUnit), sMetric); + } + m_xLbMetric->connect_changed( LINK( this, SdTpOptionsMisc, SelectMetricHdl_Impl ) ); + + SetFieldUnit( *m_xMtrFldOriginalWidth, eFUnit ); + SetFieldUnit( *m_xMtrFldOriginalHeight, eFUnit ); + m_xMtrFldOriginalWidth->set_max(999999999, FieldUnit::NONE); + m_xMtrFldOriginalHeight->set_max(999999999, FieldUnit::NONE); + + // temporary fields for info texts (for formatting/calculation) + m_xMtrFldInfo1->set_unit( eFUnit ); + m_xMtrFldInfo1->set_max(999999999, FieldUnit::NONE); + m_xMtrFldInfo1->set_digits( 2 ); + m_xMtrFldInfo2->set_unit( eFUnit ); + m_xMtrFldInfo2->set_max(999999999, FieldUnit::NONE); + m_xMtrFldInfo2->set_digits( 2 ); + + // determine PoolUnit + SfxItemPool* pPool = rInAttrs.GetPool(); + DBG_ASSERT( pPool, "Where is the Pool?" ); + ePoolUnit = pPool->GetMetric( SID_ATTR_FILL_HATCH ); + + // Fill the CB + sal_uInt16 aTable[ TABLE_COUNT ] = + { 1, 2, 4, 5, 8, 10, 16, 20, 30, 40, 50, 100 }; + + for( sal_uInt16 i = TABLE_COUNT-1; i > 0 ; i-- ) + m_xCbScale->append_text( GetScale( 1, aTable[i] ) ); + for( sal_uInt16 i = 0; i < TABLE_COUNT; i++ ) + m_xCbScale->append_text( GetScale( aTable[i], 1 ) ); +} + +SdTpOptionsMisc::~SdTpOptionsMisc() +{ +} + +void SdTpOptionsMisc::ActivatePage( const SfxItemSet& rSet ) +{ + // We have to call save_state again since it can happen that the value + // has no effect on other TabPages + m_xLbMetric->save_value(); + // change metric if necessary (since TabPage is in the Dialog where + // the metric is set) + const SfxUInt16Item* pAttr = rSet.GetItemIfSet( SID_ATTR_METRIC , false ); + if( !pAttr ) + return; + + FieldUnit eFUnit = static_cast<FieldUnit>(static_cast<tools::Long>(pAttr->GetValue())); + + if( eFUnit == m_xMtrFldOriginalWidth->get_unit() ) + return; + + // set metrics + sal_Int64 nVal = m_xMtrFldOriginalWidth->denormalize( m_xMtrFldOriginalWidth->get_value( FieldUnit::TWIP ) ); + SetFieldUnit( *m_xMtrFldOriginalWidth, eFUnit, true ); + m_xMtrFldOriginalWidth->set_value( m_xMtrFldOriginalWidth->normalize( nVal ), FieldUnit::TWIP ); + + nVal = m_xMtrFldOriginalHeight->denormalize( m_xMtrFldOriginalHeight->get_value( FieldUnit::TWIP ) ); + SetFieldUnit( *m_xMtrFldOriginalHeight, eFUnit, true ); + m_xMtrFldOriginalHeight->set_value( m_xMtrFldOriginalHeight->normalize( nVal ), FieldUnit::TWIP ); + + if( nWidth == 0 || nHeight == 0 ) + return; + + m_xMtrFldInfo1->set_unit( eFUnit ); + m_xMtrFldInfo2->set_unit( eFUnit ); + + SetMetricValue( *m_xMtrFldInfo1, nWidth, ePoolUnit ); + aInfo1 = m_xMtrFldInfo1->get_text(); + m_xFiInfo1->set_label( aInfo1 ); + + SetMetricValue( *m_xMtrFldInfo2, nHeight, ePoolUnit ); + aInfo2 = m_xMtrFldInfo2->get_text(); + m_xFiInfo2->set_label( aInfo2 ); +} + +DeactivateRC SdTpOptionsMisc::DeactivatePage( SfxItemSet* pActiveSet ) +{ + // check parser + sal_Int32 nX, nY; + if( SetScale( m_xCbScale->get_active_text(), nX, nY ) ) + { + if( pActiveSet ) + FillItemSet( pActiveSet ); + return DeactivateRC::LeavePage; + } + + std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Warning, VclButtonsType::YesNo, + SdResId(STR_WARN_SCALE_FAIL))); + if (xWarn->run() == RET_YES) + return DeactivateRC::KeepPage; + + if( pActiveSet ) + FillItemSet( pActiveSet ); + + return DeactivateRC::LeavePage; +} + +bool SdTpOptionsMisc::FillItemSet( SfxItemSet* rAttrs ) +{ + bool bModified = false; + + if( m_xCbxStartWithTemplate->get_state_changed_from_saved() || + m_xCbxMarkedHitMovesAlways->get_state_changed_from_saved() || + m_xCbxQuickEdit->get_state_changed_from_saved() || + m_xCbxPickThrough->get_state_changed_from_saved() || + m_xCbxMasterPageCache->get_state_changed_from_saved() || + m_xCbxCopy->get_state_changed_from_saved() || + m_xCbxEnableSdremote->get_state_changed_from_saved() || + m_xCbxEnablePresenterScreen->get_state_changed_from_saved() || + m_xCbxCompatibility->get_state_changed_from_saved() || + m_xCbxUsePrinterMetrics->get_state_changed_from_saved() || + m_xCbxDistort->get_state_changed_from_saved()) + { + SdOptionsMiscItem aOptsItem; + + aOptsItem.GetOptionsMisc().SetStartWithTemplate( m_xCbxStartWithTemplate->get_active() ); + aOptsItem.GetOptionsMisc().SetMarkedHitMovesAlways( m_xCbxMarkedHitMovesAlways->get_active() ); + aOptsItem.GetOptionsMisc().SetQuickEdit( m_xCbxQuickEdit->get_active() ); + aOptsItem.GetOptionsMisc().SetPickThrough( m_xCbxPickThrough->get_active() ); + aOptsItem.GetOptionsMisc().SetMasterPagePaintCaching( m_xCbxMasterPageCache->get_active() ); + aOptsItem.GetOptionsMisc().SetDragWithCopy( m_xCbxCopy->get_active() ); + aOptsItem.GetOptionsMisc().SetEnableSdremote( m_xCbxEnableSdremote->get_active() ); + aOptsItem.GetOptionsMisc().SetEnablePresenterScreen( m_xCbxEnablePresenterScreen->get_active() ); + aOptsItem.GetOptionsMisc().SetSummationOfParagraphs( m_xCbxCompatibility->get_active() ); + aOptsItem.GetOptionsMisc().SetPrinterIndependentLayout ( + m_xCbxUsePrinterMetrics->get_active() + ? css::document::PrinterIndependentLayout::DISABLED + : css::document::PrinterIndependentLayout::ENABLED); + aOptsItem.GetOptionsMisc().SetCrookNoContortion( m_xCbxDistort->get_active() ); + rAttrs->Put( aOptsItem ); + + bModified = true; + } + + // metric + if (m_xLbMetric->get_value_changed_from_saved()) + { + const sal_Int32 nMPos = m_xLbMetric->get_active(); + sal_uInt16 nFieldUnit = m_xLbMetric->get_id(nMPos).toUInt32(); + rAttrs->Put( SfxUInt16Item( GetWhich( SID_ATTR_METRIC ), nFieldUnit ) ); + bModified = true; + } + + // tabulator space + if( m_xMtrFldTabstop->get_value_changed_from_saved() ) + { + MapUnit eUnit = rAttrs->GetPool()->GetMetric( SID_ATTR_DEFTABSTOP ); + SfxUInt16Item aDef( SID_ATTR_DEFTABSTOP, static_cast<sal_uInt16>(GetCoreValue( *m_xMtrFldTabstop, eUnit )) ); + rAttrs->Put( aDef ); + bModified = true; + } + + sal_Int32 nX, nY; + if( SetScale( m_xCbScale->get_active_text(), nX, nY ) ) + { + rAttrs->Put( SfxInt32Item( ATTR_OPTIONS_SCALE_X, nX ) ); + rAttrs->Put( SfxInt32Item( ATTR_OPTIONS_SCALE_Y, nY ) ); + + bModified = true; + } + + return bModified; +} + +void SdTpOptionsMisc::Reset( const SfxItemSet* rAttrs ) +{ + SdOptionsMiscItem aOptsItem( rAttrs->Get( ATTR_OPTIONS_MISC ) ); + + m_xCbxStartWithTemplate->set_active( aOptsItem.GetOptionsMisc().IsStartWithTemplate() ); + m_xCbxMarkedHitMovesAlways->set_active( aOptsItem.GetOptionsMisc().IsMarkedHitMovesAlways() ); + m_xCbxQuickEdit->set_active( aOptsItem.GetOptionsMisc().IsQuickEdit() ); + m_xCbxPickThrough->set_active( aOptsItem.GetOptionsMisc().IsPickThrough() ); + m_xCbxMasterPageCache->set_active( aOptsItem.GetOptionsMisc().IsMasterPagePaintCaching() ); + m_xCbxCopy->set_active( aOptsItem.GetOptionsMisc().IsDragWithCopy() ); + m_xCbxEnableSdremote->set_active( aOptsItem.GetOptionsMisc().IsEnableSdremote() ); + m_xCbxEnablePresenterScreen->set_active( aOptsItem.GetOptionsMisc().IsEnablePresenterScreen() ); + m_xCbxCompatibility->set_active( aOptsItem.GetOptionsMisc().IsSummationOfParagraphs() ); + m_xCbxUsePrinterMetrics->set_active( aOptsItem.GetOptionsMisc().GetPrinterIndependentLayout()==1 ); + m_xCbxDistort->set_active( aOptsItem.GetOptionsMisc().IsCrookNoContortion() ); + m_xCbxStartWithTemplate->save_state(); + m_xCbxMarkedHitMovesAlways->save_state(); + m_xCbxQuickEdit->save_state(); + m_xCbxPickThrough->save_state(); + + m_xCbxMasterPageCache->save_state(); + m_xCbxCopy->save_state(); + m_xCbxEnableSdremote->save_state(); + m_xCbxEnablePresenterScreen->save_state(); + m_xCbxCompatibility->save_state(); + m_xCbxUsePrinterMetrics->save_state(); + m_xCbxDistort->save_state(); + + // metric + sal_uInt16 nWhich = GetWhich( SID_ATTR_METRIC ); + m_xLbMetric->set_active(-1); + + if ( rAttrs->GetItemState( nWhich ) >= SfxItemState::DEFAULT ) + { + const SfxUInt16Item& rItem = static_cast<const SfxUInt16Item&>(rAttrs->Get( nWhich )); + sal_uInt32 nFieldUnit = static_cast<sal_uInt32>(rItem.GetValue()); + + for (sal_Int32 i = 0, nEntryCount = m_xLbMetric->get_count(); i < nEntryCount; ++i) + { + if (m_xLbMetric->get_id(i).toUInt32() == nFieldUnit) + { + m_xLbMetric->set_active( i ); + break; + } + } + } + + // tabulator space + constexpr auto nWhich2 = SID_ATTR_DEFTABSTOP; + if( rAttrs->GetItemState( nWhich2 ) >= SfxItemState::DEFAULT ) + { + MapUnit eUnit = rAttrs->GetPool()->GetMetric( nWhich2 ); + const SfxUInt16Item& rItem = rAttrs->Get( nWhich2 ); + SetMetricValue( *m_xMtrFldTabstop, rItem.GetValue(), eUnit ); + } + m_xLbMetric->save_value(); + m_xMtrFldTabstop->save_value(); + //Scale + sal_Int32 nX = rAttrs->Get( ATTR_OPTIONS_SCALE_X ).GetValue(); + sal_Int32 nY = rAttrs->Get( ATTR_OPTIONS_SCALE_Y ).GetValue(); + nWidth = static_cast<const SfxUInt32Item&>( rAttrs-> + Get( ATTR_OPTIONS_SCALE_WIDTH ) ).GetValue(); + nHeight = static_cast<const SfxUInt32Item&>( rAttrs-> + Get( ATTR_OPTIONS_SCALE_HEIGHT ) ).GetValue(); + + m_xCbScale->set_entry_text( GetScale( nX, nY ) ); + + m_xMtrFldOriginalWidth->hide(); + m_xMtrFldOriginalWidth->set_text( aInfo1 ); // empty + m_xMtrFldOriginalHeight->hide(); + m_xMtrFldOriginalHeight->set_text( aInfo2 ); //empty + m_xFiInfo1->hide(); + m_xFiInfo2->hide(); + + UpdateCompatibilityControls (); +} + +std::unique_ptr<SfxTabPage> SdTpOptionsMisc::Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrs ) +{ + return std::make_unique<SdTpOptionsMisc>( pPage, pController, *rAttrs ); +} + +IMPL_LINK_NOARG(SdTpOptionsMisc, SelectMetricHdl_Impl, weld::ComboBox&, void) +{ + int nPos = m_xLbMetric->get_active(); + if (nPos != -1) + { + FieldUnit eUnit = static_cast<FieldUnit>(m_xLbMetric->get_id(nPos).toUInt32()); + sal_Int64 nVal = + m_xMtrFldTabstop->denormalize(m_xMtrFldTabstop->get_value(FieldUnit::TWIP)); + SetFieldUnit( *m_xMtrFldTabstop, eUnit ); + m_xMtrFldTabstop->set_value( m_xMtrFldTabstop->normalize( nVal ), FieldUnit::TWIP ); + } +} + +void SdTpOptionsMisc::SetImpressMode() +{ +#ifndef ENABLE_SDREMOTE + m_xCbxEnableSdremote->hide(); +#else + (void) this; // loplugin:staticmethods +#endif +} + +void SdTpOptionsMisc::SetDrawMode() +{ + m_xScaleFrame->show(); + m_xNewDocumentFrame->hide(); + m_xCbxEnableSdremote->hide(); + m_xCbxEnablePresenterScreen->hide(); + m_xCbxCompatibility->hide(); + m_xNewDocLb->hide(); + m_xCbScale->show(); + m_xPresentationFrame->hide(); + m_xMtrFldInfo1->hide(); + m_xMtrFldInfo2->hide(); + m_xWidthLb->hide(); + m_xHeightLb->hide(); + m_xFiInfo1->show(); + m_xMtrFldOriginalWidth->show(); + m_xFiInfo2->show(); + m_xMtrFldOriginalHeight->show(); + m_xCbxDistort->show(); + m_xCbxCompatibility->hide(); +} + +OUString SdTpOptionsMisc::GetScale( sal_Int32 nX, sal_Int32 nY ) +{ + return OUString::number(nX) + OUStringChar(TOKEN) + OUString::number(nY); +} + +bool SdTpOptionsMisc::SetScale( std::u16string_view aScale, sal_Int32& rX, sal_Int32& rY ) +{ + if (aScale.empty()) + return false; + + sal_Int32 nIdx {0}; + + std::u16string_view aTmp(o3tl::getToken(aScale, 0, TOKEN, nIdx)); + if (nIdx<0) + return false; // we expect another token! + + if (!comphelper::string::isdigitAsciiString(aTmp)) + return false; + + rX = static_cast<tools::Long>(o3tl::toInt32(aTmp)); + if( rX == 0 ) + return false; + + aTmp = o3tl::getToken(aScale, 0, TOKEN, nIdx); + if (nIdx>=0) + return false; // we require just 2 tokens! + + if (!comphelper::string::isdigitAsciiString(aTmp)) + return false; + + rY = static_cast<tools::Long>(o3tl::toInt32(aTmp)); + return rY != 0; +} + +void SdTpOptionsMisc::UpdateCompatibilityControls() +{ + // Disable the compatibility controls by default. Enable them only when + // there is at least one open document. + bool bIsEnabled = false; + + try + { + // Get a component enumeration from the desktop and search it for documents. + Reference<uno::XComponentContext> xContext( ::comphelper::getProcessComponentContext()); + do + { + Reference<frame::XDesktop2> xDesktop = frame::Desktop::create(xContext); + + Reference<container::XEnumerationAccess> xComponents = + xDesktop->getComponents(); + if ( ! xComponents.is()) + break; + + Reference<container::XEnumeration> xEnumeration ( + xComponents->createEnumeration()); + if ( ! xEnumeration.is()) + break; + + while (xEnumeration->hasMoreElements()) + { + Reference<frame::XModel> xModel (xEnumeration->nextElement(), UNO_QUERY); + if (xModel.is()) + { + // There is at least one model/document: Enable the compatibility controls. + bIsEnabled = true; + break; + } + } + + } + while (false); // One 'loop'. + } + catch (const uno::Exception&) + { + // When there is an exception then simply use the default value of + // bIsEnabled and disable the controls. + } + + m_xCbxCompatibility->set_sensitive(bIsEnabled); + m_xCbxUsePrinterMetrics->set_sensitive(bIsEnabled); +} + +void SdTpOptionsMisc::PageCreated(const SfxAllItemSet& aSet) +{ + const SfxUInt32Item* pFlagItem = aSet.GetItem<SfxUInt32Item>(SID_SDMODE_FLAG, false); + if (pFlagItem) + { + sal_uInt32 nFlags=pFlagItem->GetValue(); + if ( ( nFlags & SD_DRAW_MODE ) == SD_DRAW_MODE ) + SetDrawMode(); + if ( ( nFlags & SD_IMPRESS_MODE ) == SD_IMPRESS_MODE ) + SetImpressMode(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/unchss.cxx b/sd/source/ui/dlg/unchss.cxx new file mode 100644 index 000000000..7d963cddf --- /dev/null +++ b/sd/source/ui/dlg/unchss.cxx @@ -0,0 +1,119 @@ +/* -*- 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 <svl/itemset.hxx> +#include <svl/style.hxx> +#include <svl/hint.hxx> +#include <svx/svdobj.hxx> +#include <svx/svdpool.hxx> +#include <tools/debug.hxx> + +#include <unchss.hxx> + +#include <strings.hrc> +#include <glob.hxx> +#include <sdresid.hxx> +#include <drawdoc.hxx> +#include <stlsheet.hxx> +#include <strings.hxx> + +StyleSheetUndoAction::StyleSheetUndoAction(SdDrawDocument* pTheDoc, + SfxStyleSheet* pTheStyleSheet, + const SfxItemSet* pTheNewItemSet) : + SdUndoAction(pTheDoc) +{ + DBG_ASSERT(pTheStyleSheet, "Undo without StyleSheet ???"); + mpStyleSheet = pTheStyleSheet; + + // Create ItemSets; Attention, it is possible that the new one is from a, + // different pool. Therefore we clone it with its items. + mpNewSet = std::make_unique<SfxItemSet>(static_cast<SfxItemPool&>(SdrObject::GetGlobalDrawObjectItemPool()), pTheNewItemSet->GetRanges()); + SdrModel::MigrateItemSet( pTheNewItemSet, mpNewSet.get(), pTheDoc ); + + mpOldSet = std::make_unique<SfxItemSet>(static_cast<SfxItemPool&>(SdrObject::GetGlobalDrawObjectItemPool()), mpStyleSheet->GetItemSet().GetRanges()); + SdrModel::MigrateItemSet( &mpStyleSheet->GetItemSet(), mpOldSet.get(), pTheDoc ); + + OUString aComment(SdResId(STR_UNDO_CHANGE_PRES_OBJECT)); + OUString aName(mpStyleSheet->GetName()); + + // delete layout name and separator + sal_Int32 nPos = aName.indexOf(SD_LT_SEPARATOR); + if (nPos != -1) + aName = aName.copy(nPos + SD_LT_SEPARATOR.getLength()); + + if (aName == STR_LAYOUT_TITLE) + { + aName = SdResId(STR_PSEUDOSHEET_TITLE); + } + else if (aName == STR_LAYOUT_SUBTITLE) + { + aName = SdResId(STR_PSEUDOSHEET_SUBTITLE); + } + else if (aName == STR_LAYOUT_BACKGROUND) + { + aName = SdResId(STR_PSEUDOSHEET_BACKGROUND); + } + else if (aName == STR_LAYOUT_BACKGROUNDOBJECTS) + { + aName = SdResId(STR_PSEUDOSHEET_BACKGROUNDOBJECTS); + } + else if (aName == STR_LAYOUT_NOTES) + { + aName = SdResId(STR_PSEUDOSHEET_NOTES); + } + else + { + OUString aOutlineStr(SdResId(STR_PSEUDOSHEET_OUTLINE)); + nPos = aName.indexOf(aOutlineStr); + if (nPos != -1) + { + std::u16string_view aNumStr(aName.subView(aOutlineStr.getLength())); + aName = STR_LAYOUT_OUTLINE + aNumStr; + } + } + + // replace placeholder with template name + SetComment(aComment.replaceFirst("$", aName)); +} + +void StyleSheetUndoAction::Undo() +{ + SfxItemSet aNewSet( mpDoc->GetItemPool(), mpOldSet->GetRanges() ); + SdrModel::MigrateItemSet( mpOldSet.get(), &aNewSet, mpDoc ); + + mpStyleSheet->GetItemSet().Set(aNewSet); + if( mpStyleSheet->GetFamily() == SfxStyleFamily::Pseudo ) + static_cast<SdStyleSheet*>(mpStyleSheet)->GetRealStyleSheet()->Broadcast(SfxHint(SfxHintId::DataChanged)); + else + mpStyleSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); +} + +void StyleSheetUndoAction::Redo() +{ + SfxItemSet aNewSet( mpDoc->GetItemPool(), mpOldSet->GetRanges() ); + SdrModel::MigrateItemSet( mpNewSet.get(), &aNewSet, mpDoc ); + + mpStyleSheet->GetItemSet().Set(aNewSet); + if( mpStyleSheet->GetFamily() == SfxStyleFamily::Pseudo ) + static_cast<SdStyleSheet*>(mpStyleSheet)->GetRealStyleSheet()->Broadcast(SfxHint(SfxHintId::DataChanged)); + else + mpStyleSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/vectdlg.cxx b/sd/source/ui/dlg/vectdlg.cxx new file mode 100644 index 000000000..2db041e7e --- /dev/null +++ b/sd/source/ui/dlg/vectdlg.cxx @@ -0,0 +1,336 @@ +/* -*- 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/vclenum.hxx> +#include <vcl/BitmapReadAccess.hxx> +#include <vcl/metaact.hxx> +#include <vcl/BitmapSimpleColorQuantizationFilter.hxx> +#include <vcl/svapp.hxx> + +#include <DrawDocShell.hxx> +#include <sdmod.hxx> +#include <sdiocmpt.hxx> +#include <vectdlg.hxx> + +#define VECTORIZE_MAX_EXTENT 512 + +SdVectorizeDlg::SdVectorizeDlg(weld::Window* pParent, const Bitmap& rBmp, ::sd::DrawDocShell* pDocShell) + : GenericDialogController(pParent, "modules/sdraw/ui/vectorize.ui", "VectorizeDialog") + , m_pDocSh(pDocShell) + , aBmp(rBmp) + , m_aBmpWin(m_xDialog.get()) + , m_aMtfWin(m_xDialog.get()) + , m_xNmLayers(m_xBuilder->weld_spin_button("colors")) + , m_xMtReduce(m_xBuilder->weld_metric_spin_button("points", FieldUnit::PIXEL)) + , m_xFtFillHoles(m_xBuilder->weld_label("tilesft")) + , m_xMtFillHoles(m_xBuilder->weld_metric_spin_button("tiles", FieldUnit::PIXEL)) + , m_xCbFillHoles(m_xBuilder->weld_check_button("fillholes")) + , m_xBmpWin(new weld::CustomWeld(*m_xBuilder, "source", m_aBmpWin)) + , m_xMtfWin(new weld::CustomWeld(*m_xBuilder, "vectorized", m_aMtfWin)) + , m_xPrgs(m_xBuilder->weld_progress_bar("progressbar")) + , m_xBtnOK(m_xBuilder->weld_button("ok")) + , m_xBtnPreview(m_xBuilder->weld_button("preview")) +{ + const int nWidth = m_xFtFillHoles->get_approximate_digit_width() * 32; + const int nHeight = m_xFtFillHoles->get_text_height() * 16; + m_xBmpWin->set_size_request(nWidth, nHeight); + m_xMtfWin->set_size_request(nWidth, nHeight); + + m_xBtnPreview->connect_clicked( LINK( this, SdVectorizeDlg, ClickPreviewHdl ) ); + m_xBtnOK->connect_clicked( LINK( this, SdVectorizeDlg, ClickOKHdl ) ); + m_xNmLayers->connect_value_changed( LINK( this, SdVectorizeDlg, ModifyHdl ) ); + m_xMtReduce->connect_value_changed( LINK( this, SdVectorizeDlg, MetricModifyHdl ) ); + m_xMtFillHoles->connect_value_changed( LINK( this, SdVectorizeDlg, MetricModifyHdl ) ); + m_xCbFillHoles->connect_toggled( LINK( this, SdVectorizeDlg, ToggleHdl ) ); + + LoadSettings(); + InitPreviewBmp(); +} + +SdVectorizeDlg::~SdVectorizeDlg() +{ +} + +::tools::Rectangle SdVectorizeDlg::GetRect( const Size& rDispSize, const Size& rBmpSize ) +{ + ::tools::Rectangle aRect; + + if( rBmpSize.Width() && rBmpSize.Height() && rDispSize.Width() && rDispSize.Height() ) + { + Size aBmpSize( rBmpSize ); + const double fGrfWH = static_cast<double>(aBmpSize.Width()) / aBmpSize.Height(); + const double fWinWH = static_cast<double>(rDispSize.Width()) / rDispSize.Height(); + + if( fGrfWH < fWinWH ) + { + aBmpSize.setWidth( static_cast<tools::Long>( rDispSize.Height() * fGrfWH ) ); + aBmpSize.setHeight( rDispSize.Height() ); + } + else + { + aBmpSize.setWidth( rDispSize.Width() ); + aBmpSize.setHeight( static_cast<tools::Long>( rDispSize.Width() / fGrfWH) ); + } + + const Point aBmpPos( ( rDispSize.Width() - aBmpSize.Width() ) >> 1, + ( rDispSize.Height() - aBmpSize.Height() ) >> 1 ); + + aRect = ::tools::Rectangle( aBmpPos, aBmpSize ); + } + + return aRect; +} + +void SdVectorizeDlg::InitPreviewBmp() +{ + const ::tools::Rectangle aRect( GetRect( m_aBmpWin.GetOutputSizePixel(), aBmp.GetSizePixel() ) ); + + aPreviewBmp = aBmp; + aPreviewBmp.Scale( aRect.GetSize() ); + m_aBmpWin.SetGraphic(BitmapEx(aPreviewBmp)); +} + +Bitmap SdVectorizeDlg::GetPreparedBitmap( Bitmap const & rBmp, Fraction& rScale ) +{ + Bitmap aNew( rBmp ); + const Size aSizePix( aNew.GetSizePixel() ); + + if( aSizePix.Width() > VECTORIZE_MAX_EXTENT || aSizePix.Height() > VECTORIZE_MAX_EXTENT ) + { + const ::tools::Rectangle aRect( GetRect( Size( VECTORIZE_MAX_EXTENT, VECTORIZE_MAX_EXTENT ), aSizePix ) ); + rScale = Fraction( aSizePix.Width(), aRect.GetWidth() ); + aNew.Scale( aRect.GetSize() ); + } + else + rScale = Fraction( 1, 1 ); + + BitmapEx aNewBmpEx(aNew); + BitmapFilter::Filter(aNewBmpEx, BitmapSimpleColorQuantizationFilter(m_xNmLayers->get_value())); + aNew = aNewBmpEx.GetBitmap(); + + return aNew; +} + +void SdVectorizeDlg::Calculate( Bitmap const & rBmp, GDIMetaFile& rMtf ) +{ + m_pDocSh->SetWaitCursor( true ); + m_xPrgs->set_percentage(0); + + Fraction aScale; + Bitmap aTmp( GetPreparedBitmap( rBmp, aScale ) ); + + if( !aTmp.IsEmpty() ) + { + const Link<::tools::Long,void> aPrgsHdl( LINK( this, SdVectorizeDlg, ProgressHdl ) ); + aTmp.Vectorize( rMtf, static_cast<sal_uInt8>(m_xMtReduce->get_value(FieldUnit::NONE)), &aPrgsHdl ); + + if (m_xCbFillHoles->get_active()) + { + GDIMetaFile aNewMtf; + Bitmap::ScopedReadAccess pRAcc(aTmp); + + if( pRAcc ) + { + const tools::Long nWidth = pRAcc->Width(); + const tools::Long nHeight = pRAcc->Height(); + const tools::Long nTileX = m_xMtFillHoles->get_value(FieldUnit::NONE); + const tools::Long nTileY = m_xMtFillHoles->get_value(FieldUnit::NONE); + assert(nTileX && "div-by-zero"); + const tools::Long nCountX = nWidth / nTileX; + assert(nTileY && "div-by-zero"); + const tools::Long nCountY = nHeight / nTileY; + const tools::Long nRestX = nWidth % nTileX; + const tools::Long nRestY = nHeight % nTileY; + + MapMode aMap( rMtf.GetPrefMapMode() ); + aNewMtf.SetPrefSize( rMtf.GetPrefSize() ); + aNewMtf.SetPrefMapMode( aMap ); + + for( tools::Long nTY = 0; nTY < nCountY; nTY++ ) + { + const tools::Long nY = nTY * nTileY; + + for( tools::Long nTX = 0; nTX < nCountX; nTX++ ) + AddTile( pRAcc.get(), aNewMtf, nTX * nTileX, nTY * nTileY, nTileX, nTileY ); + + if( nRestX ) + AddTile( pRAcc.get(), aNewMtf, nCountX * nTileX, nY, nRestX, nTileY ); + } + + if( nRestY ) + { + const tools::Long nY = nCountY * nTileY; + + for( tools::Long nTX = 0; nTX < nCountX; nTX++ ) + AddTile( pRAcc.get(), aNewMtf, nTX * nTileX, nY, nTileX, nRestY ); + + if( nRestX ) + AddTile( pRAcc.get(), aNewMtf, nCountX * nTileX, nCountY * nTileY, nRestX, nRestY ); + } + + pRAcc.reset(); + + for( size_t n = 0, nCount = rMtf.GetActionSize(); n < nCount; n++ ) + aNewMtf.AddAction( rMtf.GetAction( n )->Clone() ); + + aMap.SetScaleX( aMap.GetScaleX() * aScale ); + aMap.SetScaleY( aMap.GetScaleY() * aScale ); + aNewMtf.SetPrefMapMode( aMap ); + rMtf = aNewMtf; + } + } + } + + m_xPrgs->set_percentage(0); + m_pDocSh->SetWaitCursor( false ); +} + +void SdVectorizeDlg::AddTile( BitmapReadAccess const * pRAcc, GDIMetaFile& rMtf, + tools::Long nPosX, tools::Long nPosY, tools::Long nWidth, tools::Long nHeight ) +{ + sal_uLong nSumR = 0, nSumG = 0, nSumB = 0; + const tools::Long nRight = nPosX + nWidth - 1; + const tools::Long nBottom = nPosY + nHeight - 1; + const double fMult = 1.0 / ( nWidth * nHeight ); + + for( tools::Long nY = nPosY; nY <= nBottom; nY++ ) + { + for( tools::Long nX = nPosX; nX <= nRight; nX++ ) + { + const BitmapColor aPixel( pRAcc->GetColor( nY, nX ) ); + + nSumR += aPixel.GetRed(); + nSumG += aPixel.GetGreen(); + nSumB += aPixel.GetBlue(); + } + } + + const Color aColor( static_cast<sal_uInt8>(FRound( nSumR * fMult )), + static_cast<sal_uInt8>(FRound( nSumG * fMult )), + static_cast<sal_uInt8>(FRound( nSumB * fMult )) ); + + ::tools::Rectangle aRect( Point( nPosX, nPosY ), Size( nWidth + 1, nHeight + 1 ) ); + const Size& rMaxSize = rMtf.GetPrefSize(); + + aRect = Application::GetDefaultDevice()->PixelToLogic(aRect, rMtf.GetPrefMapMode()); + + if( aRect.Right() > ( rMaxSize.Width() - 1 ) ) + aRect.SetRight( rMaxSize.Width() - 1 ); + + if( aRect.Bottom() > ( rMaxSize.Height() - 1 ) ) + aRect.SetBottom( rMaxSize.Height() - 1 ); + + rMtf.AddAction( new MetaLineColorAction( aColor, true ) ); + rMtf.AddAction( new MetaFillColorAction( aColor, true ) ); + rMtf.AddAction( new MetaRectAction( aRect ) ); +} + +IMPL_LINK( SdVectorizeDlg, ProgressHdl, tools::Long, nData, void ) +{ + m_xPrgs->set_percentage(nData); +} + +IMPL_LINK_NOARG(SdVectorizeDlg, ClickPreviewHdl, weld::Button&, void) +{ + Calculate( aBmp, aMtf ); + m_aMtfWin.SetGraphic( aMtf ); + m_xBtnPreview->set_sensitive(false); +} + +IMPL_LINK_NOARG(SdVectorizeDlg, ClickOKHdl, weld::Button&, void) +{ + if (m_xBtnPreview->get_sensitive()) + Calculate( aBmp, aMtf ); + + SaveSettings(); + m_xDialog->response(RET_OK); +} + +IMPL_LINK( SdVectorizeDlg, ToggleHdl, weld::Toggleable&, rCb, void ) +{ + if (rCb.get_active()) + { + m_xFtFillHoles->set_sensitive(true); + m_xMtFillHoles->set_sensitive(true); + } + else + { + m_xFtFillHoles->set_sensitive(false); + m_xMtFillHoles->set_sensitive(false); + } + + m_xBtnPreview->set_sensitive(true); +} + +IMPL_LINK_NOARG(SdVectorizeDlg, ModifyHdl, weld::SpinButton&, void) +{ + m_xBtnPreview->set_sensitive(true); +} + +IMPL_LINK_NOARG(SdVectorizeDlg, MetricModifyHdl, weld::MetricSpinButton&, void) +{ + m_xBtnPreview->set_sensitive(true); +} + +void SdVectorizeDlg::LoadSettings() +{ + tools::SvRef<SotStorageStream> xIStm( SD_MOD()->GetOptionStream( + SD_OPTION_VECTORIZE , + SdOptionStreamMode::Load ) ); + sal_uInt16 nLayers; + sal_uInt16 nReduce; + sal_uInt16 nFillHoles; + bool bFillHoles; + + if( xIStm.is() ) + { + SdIOCompat aCompat( *xIStm, StreamMode::READ ); + xIStm->ReadUInt16( nLayers ).ReadUInt16( nReduce ).ReadUInt16( nFillHoles ).ReadCharAsBool( bFillHoles ); + } + else + { + nLayers = 8; + nReduce = 0; + nFillHoles = 32; + bFillHoles = false; + } + + m_xNmLayers->set_value(nLayers); + m_xMtReduce->set_value(nReduce, FieldUnit::NONE); + m_xMtFillHoles->set_value(nFillHoles, FieldUnit::NONE); + m_xCbFillHoles->set_active(bFillHoles); + + ToggleHdl(*m_xCbFillHoles); +} + +void SdVectorizeDlg::SaveSettings() const +{ + tools::SvRef<SotStorageStream> xOStm( SD_MOD()->GetOptionStream( + SD_OPTION_VECTORIZE , + SdOptionStreamMode::Store ) ); + + if( xOStm.is() ) + { + SdIOCompat aCompat( *xOStm, StreamMode::WRITE, 1 ); + xOStm->WriteUInt16( m_xNmLayers->get_value() ).WriteUInt16(m_xMtReduce->get_value(FieldUnit::NONE)); + xOStm->WriteUInt16( m_xMtFillHoles->get_value(FieldUnit::NONE) ).WriteBool(m_xCbFillHoles->get_active()); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/docshell/docshel2.cxx b/sd/source/ui/docshell/docshel2.cxx new file mode 100644 index 000000000..160c64a66 --- /dev/null +++ b/sd/source/ui/docshell/docshel2.cxx @@ -0,0 +1,416 @@ +/* -*- 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 <memory> +#include <DrawDocShell.hxx> +#include <svx/svdpagv.hxx> +#include <svx/svxdlg.hxx> +#include <o3tl/string_view.hxx> + +#include <helpids.h> +#include <ViewShell.hxx> +#include <FrameView.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <ClientView.hxx> +#include <Window.hxx> +#include <strings.hrc> + +#include <sdresid.hxx> +#include <fupoor.hxx> +#include <vcl/svapp.hxx> +#include <vcl/virdev.hxx> +#include <rtl/character.hxx> +#include <tools/debug.hxx> + +namespace sd { + +/** + * Drawing of DocShell (with the helper class SdDrawViewShell) + */ +void DrawDocShell::Draw(OutputDevice* pOut, const JobSetup&, sal_uInt16 nAspect) +{ + if (nAspect == ASPECT_THUMBNAIL) + { + // THUMBNAIL: here we may can set the draft mode + } + + std::optional<ClientView> pView( std::in_place, this, pOut ); + + pView->SetHlplVisible(false); + pView->SetGridVisible(false); + pView->SetBordVisible(false); + pView->SetPageVisible(false); + pView->SetGlueVisible(false); + + SdPage* pSelectedPage = nullptr; + + const std::vector<std::unique_ptr<sd::FrameView>> &rViews = mpDoc->GetFrameViewList(); + if( !rViews.empty() ) + { + sd::FrameView* pFrameView = rViews[0].get(); + if( pFrameView->GetPageKind() == PageKind::Standard ) + { + sal_uInt16 nSelectedPage = pFrameView->GetSelectedPage(); + pSelectedPage = mpDoc->GetSdPage(nSelectedPage, PageKind::Standard); + } + } + + if( nullptr == pSelectedPage ) + { + SdPage* pPage = nullptr; + sal_uInt16 nPageCnt = mpDoc->GetSdPageCount(PageKind::Standard); + + for (sal_uInt16 i = 0; i < nPageCnt; i++) + { + pPage = mpDoc->GetSdPage(i, PageKind::Standard); + + if ( pPage->IsSelected() ) + pSelectedPage = pPage; + } + + if( nullptr == pSelectedPage ) + pSelectedPage = mpDoc->GetSdPage(0, PageKind::Standard); + } + + ::tools::Rectangle aVisArea = GetVisArea(nAspect); + pOut->IntersectClipRegion(aVisArea); + pView->ShowSdrPage(pSelectedPage); + + if (pOut->GetOutDevType() == OUTDEV_WINDOW) + return; + + MapMode aOldMapMode = pOut->GetMapMode(); + + if (pOut->GetOutDevType() == OUTDEV_PRINTER) + { + MapMode aMapMode = aOldMapMode; + Point aOrigin = aMapMode.GetOrigin(); + aOrigin.AdjustX(1 ); + aOrigin.AdjustY(1 ); + aMapMode.SetOrigin(aOrigin); + pOut->SetMapMode(aMapMode); + } + + vcl::Region aRegion(aVisArea); + pView->CompleteRedraw(pOut, aRegion); + + if (pOut->GetOutDevType() == OUTDEV_PRINTER) + { + pOut->SetMapMode(aOldMapMode); + } +} + +::tools::Rectangle DrawDocShell::GetVisArea(sal_uInt16 nAspect) const +{ + ::tools::Rectangle aVisArea; + + if( ( ASPECT_THUMBNAIL == nAspect ) || ( ASPECT_DOCPRINT == nAspect ) ) + { + // provide size of first page + aVisArea.SetSize(mpDoc->GetSdPage(0, PageKind::Standard)->GetSize()); + } + else + { + aVisArea = SfxObjectShell::GetVisArea(nAspect); + } + + if (aVisArea.IsEmpty() && mpViewShell) + { + vcl::Window* pWin = mpViewShell->GetActiveWindow(); + + if (pWin) + { + aVisArea = pWin->PixelToLogic(::tools::Rectangle(Point(0,0), pWin->GetOutputSizePixel())); + } + } + + return aVisArea; +} + +void DrawDocShell::Connect(ViewShell* pViewSh) +{ + mpViewShell = pViewSh; +} + +void DrawDocShell::Disconnect(ViewShell const * pViewSh) +{ + if (mpViewShell == pViewSh) + { + mpViewShell = nullptr; + } +} + +FrameView* DrawDocShell::GetFrameView() +{ + FrameView* pFrameView = nullptr; + + if (mpViewShell) + { + pFrameView = mpViewShell->GetFrameView(); + } + + return pFrameView; +} + +/** + * Creates a bitmap of an arbitrary page + */ +BitmapEx DrawDocShell::GetPagePreviewBitmap(SdPage* pPage) +{ + const sal_uInt16 nMaxEdgePixel = 90; + MapMode aMapMode( MapUnit::Map100thMM ); + const Size aSize( pPage->GetSize() ); + const Point aNullPt; + ScopedVclPtrInstance< VirtualDevice > pVDev( *Application::GetDefaultDevice() ); + + pVDev->SetMapMode( aMapMode ); + + const Size aPixSize( pVDev->LogicToPixel( aSize ) ); + const sal_uLong nMaxEdgePix = std::max( aPixSize.Width(), aPixSize.Height() ); + Fraction aFrac( nMaxEdgePixel, nMaxEdgePix ); + + aMapMode.SetScaleX( aFrac ); + aMapMode.SetScaleY( aFrac ); + pVDev->SetMapMode( aMapMode ); + pVDev->SetOutputSize( aSize ); + + // that we also get the dark lines at the right and bottom page margin + aFrac = Fraction( nMaxEdgePixel - 1, nMaxEdgePix ); + aMapMode.SetScaleX( aFrac ); + aMapMode.SetScaleY( aFrac ); + pVDev->SetMapMode( aMapMode ); + + std::optional<ClientView> pView( std::in_place, this, pVDev ); + FrameView* pFrameView = GetFrameView(); + pView->ShowSdrPage( pPage ); + + if ( GetFrameView() ) + { + // initialize the drawing-(screen) attributes + pView->SetGridCoarse( pFrameView->GetGridCoarse() ); + pView->SetGridFine( pFrameView->GetGridFine() ); + pView->SetSnapGridWidth(pFrameView->GetSnapGridWidthX(), pFrameView->GetSnapGridWidthY()); + pView->SetGridVisible( pFrameView->IsGridVisible() ); + pView->SetGridFront( pFrameView->IsGridFront() ); + pView->SetSnapAngle( pFrameView->GetSnapAngle() ); + pView->SetGridSnap( pFrameView->IsGridSnap() ); + pView->SetBordSnap( pFrameView->IsBordSnap() ); + pView->SetHlplSnap( pFrameView->IsHlplSnap() ); + pView->SetOFrmSnap( pFrameView->IsOFrmSnap() ); + pView->SetOPntSnap( pFrameView->IsOPntSnap() ); + pView->SetOConSnap( pFrameView->IsOConSnap() ); + pView->SetDragStripes( pFrameView->IsDragStripes() ); + pView->SetFrameDragSingles( pFrameView->IsFrameDragSingles() ); + pView->SetSnapMagneticPixel( pFrameView->GetSnapMagneticPixel() ); + pView->SetMarkedHitMovesAlways( pFrameView->IsMarkedHitMovesAlways() ); + pView->SetMoveOnlyDragging( pFrameView->IsMoveOnlyDragging() ); + pView->SetSlantButShear( pFrameView->IsSlantButShear() ); + pView->SetNoDragXorPolys( pFrameView->IsNoDragXorPolys() ); + pView->SetCrookNoContortion( pFrameView->IsCrookNoContortion() ); + pView->SetAngleSnapEnabled( pFrameView->IsAngleSnapEnabled() ); + pView->SetBigOrtho( pFrameView->IsBigOrtho() ); + pView->SetOrtho( pFrameView->IsOrtho() ); + + SdrPageView* pPageView = pView->GetSdrPageView(); + + if (pPageView) + { + if ( pPageView->GetVisibleLayers() != pFrameView->GetVisibleLayers() ) + pPageView->SetVisibleLayers( pFrameView->GetVisibleLayers() ); + + if ( pPageView->GetPrintableLayers() != pFrameView->GetPrintableLayers() ) + pPageView->SetPrintableLayers( pFrameView->GetPrintableLayers() ); + + if ( pPageView->GetLockedLayers() != pFrameView->GetLockedLayers() ) + pPageView->SetLockedLayers( pFrameView->GetLockedLayers() ); + + pPageView->SetHelpLines( pFrameView->GetStandardHelpLines() ); + } + + if ( pView->GetActiveLayer() != pFrameView->GetActiveLayer() ) + pView->SetActiveLayer( pFrameView->GetActiveLayer() ); + } + + pView->CompleteRedraw( pVDev, vcl::Region(::tools::Rectangle(aNullPt, aSize)) ); + + // IsRedrawReady() always gives sal_True while ( !pView->IsRedrawReady() ) {} + pView.reset(); + + pVDev->SetMapMode( MapMode() ); + + BitmapEx aPreview( pVDev->GetBitmapEx( aNullPt, pVDev->GetOutputSizePixel() ) ); + + DBG_ASSERT(!aPreview.IsEmpty(), "Preview-Bitmap could not be generated"); + + return aPreview; +} + +/** + * Checks if the page exists. If so, we force the user to enter a not yet used + * name. + * @return sal_False if the user cancels the action. + */ +bool DrawDocShell::CheckPageName(weld::Window* pWin, OUString& rName) +{ + const OUString aStrForDlg( rName ); + bool bIsNameValid = IsNewPageNameValid( rName, true ); + + if( ! bIsNameValid ) + { + OUString aDesc; + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + + if (GetDocumentType() == DocumentType::Draw) + aDesc = SdResId( STR_WARN_PAGE_EXISTS_DRAW ); + else + aDesc = SdResId( STR_WARN_PAGE_EXISTS ); + + ScopedVclPtr<AbstractSvxNameDialog> aNameDlg(pFact->CreateSvxNameDialog(pWin, aStrForDlg, aDesc)); + aNameDlg->SetEditHelpId( HID_SD_NAMEDIALOG_PAGE ); + + aNameDlg->SetCheckNameHdl( LINK( this, DrawDocShell, RenameSlideHdl ) ); + + rtl::Reference<FuPoor> xFunc( mpViewShell->GetCurrentFunction() ); + if( xFunc.is() ) + xFunc->cancel(); + + if( aNameDlg->Execute() == RET_OK ) + { + aNameDlg->GetName( rName ); + bIsNameValid = IsNewPageNameValid( rName ); + } + } + + return bIsNameValid; +} + +bool DrawDocShell::IsNewPageNameValid( OUString & rInOutPageName, bool bResetStringIfStandardName /* = false */ ) +{ + bool bCanUseNewName = false; + + // check if name is something like 'Slide n' + OUString aStrPage(SdResId(STR_SD_PAGE) + " "); + + bool bIsStandardName = false; + + // prevent also _future_ slide names of the form "'STR_SD_PAGE' + ' ' + '[0-9]+|[a-z]|[A-Z]|[CDILMVX]+|[cdilmvx]+'" + // (arabic, lower- and upper case single letter, lower- and upper case roman numbers) + if (rInOutPageName.startsWith(aStrPage) && + rInOutPageName.getLength() > aStrPage.getLength()) + { + sal_Int32 nIdx{ aStrPage.getLength() }; + std::u16string_view sRemainder = o3tl::getToken(rInOutPageName, 0, ' ', nIdx); + if (!sRemainder.empty() && sRemainder[0] >= '0' && sRemainder[0] <= '9') + { + // check for arabic numbering + + size_t nIndex = 1; + // skip all following numbers + while (nIndex < sRemainder.size() && + sRemainder[nIndex] >= '0' && sRemainder[nIndex] <= '9') + { + nIndex++; + } + + // EOL? Reserved name! + if (nIndex >= sRemainder.size()) + { + bIsStandardName = true; + } + } + else if (sRemainder.size() == 1 && + rtl::isAsciiLowerCase(sRemainder[0])) + { + // lower case, single character: reserved + bIsStandardName = true; + } + else if (sRemainder.size() == 1 && + rtl::isAsciiUpperCase(sRemainder[0])) + { + // upper case, single character: reserved + bIsStandardName = true; + } + else + { + // check for upper/lower case roman numbering + OUString sReserved("cdilmvx"); + + // skip all following characters contained in one reserved class + if (sReserved.indexOf(sRemainder[0]) == -1) + sReserved = sReserved.toAsciiUpperCase(); + + size_t nIndex = 0; + while (nIndex < sRemainder.size() && + sReserved.indexOf(sRemainder[nIndex]) != -1) + { + nIndex++; + } + + // EOL? Reserved name! + if (nIndex >= sRemainder.size()) + { + bIsStandardName = true; + } + } + } + + if( bIsStandardName ) + { + if( bResetStringIfStandardName ) + { + // this is for insertion of slides from other files with standard + // name. They get a new standard name, if the string is set to an + // empty one. + rInOutPageName.clear(); + bCanUseNewName = true; + } + else + bCanUseNewName = false; + } + else + { + if (!rInOutPageName.isEmpty()) + { + bool bOutDummy; + sal_uInt16 nExistingPageNum = mpDoc->GetPageByName( rInOutPageName, bOutDummy ); + bCanUseNewName = ( nExistingPageNum == SDRPAGE_NOTFOUND ); + } + else + bCanUseNewName = false; + } + + return bCanUseNewName; +} + +bool DrawDocShell::IsPageNameUnique( std::u16string_view rPageName ) const +{ + return mpDoc->IsPageNameUnique(rPageName); +} + +IMPL_LINK( DrawDocShell, RenameSlideHdl, AbstractSvxNameDialog&, rDialog, bool ) +{ + OUString aNewName; + rDialog.GetName( aNewName ); + return IsNewPageNameValid( aNewName ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/docshell/docshel3.cxx b/sd/source/ui/docshell/docshel3.cxx new file mode 100644 index 000000000..be045818a --- /dev/null +++ b/sd/source/ui/docshell/docshel3.cxx @@ -0,0 +1,443 @@ +/* -*- 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 <DrawDocShell.hxx> + +#include <svx/svxids.hrc> + +#include <svx/ofaitem.hxx> +#include <svl/stritem.hxx> +#include <svl/srchitem.hxx> +#include <svl/languageoptions.hxx> +#include <svtools/langtab.hxx> +#include <sfx2/request.hxx> +#include <sfx2/sfxdlg.hxx> +#include <sfx2/viewfrm.hxx> +#include <vcl/abstdlg.hxx> +#include <svx/drawitem.hxx> +#include <editeng/langitem.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/outlobj.hxx> +#include <editeng/editobj.hxx> +#include <com/sun/star/i18n/TextConversionOption.hpp> +#include <sfx2/notebookbar/SfxNotebookBar.hxx> +#include <editeng/editeng.hxx> +#include <osl/diagnose.h> + +#include <sdmod.hxx> +#include <drawdoc.hxx> +#include <fusearch.hxx> +#include <ViewShell.hxx> +#include <slideshow.hxx> +#include <fuhhconv.hxx> +#include <memory> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; + +namespace sd { + +static void lcl_setLanguageForObj( SdrObject *pObj, LanguageType nLang, bool bLanguageNone ) +{ + const sal_uInt16 aLangWhichId_EE[3] = + { + EE_CHAR_LANGUAGE, + EE_CHAR_LANGUAGE_CJK, + EE_CHAR_LANGUAGE_CTL + }; + + if( bLanguageNone ) + nLang = LANGUAGE_NONE; + + if( nLang != LANGUAGE_DONTKNOW ) + { + if( nLang == LANGUAGE_NONE ) + { + for(sal_uInt16 n : aLangWhichId_EE) + pObj->SetMergedItem( SvxLanguageItem( nLang, n ) ); + } + else + { + sal_uInt16 nLangWhichId = 0; + SvtScriptType nScriptType = SvtLanguageOptions::GetScriptTypeOfLanguage( nLang ); + switch (nScriptType) + { + case SvtScriptType::LATIN : nLangWhichId = EE_CHAR_LANGUAGE; break; + case SvtScriptType::ASIAN : nLangWhichId = EE_CHAR_LANGUAGE_CJK; break; + case SvtScriptType::COMPLEX : nLangWhichId = EE_CHAR_LANGUAGE_CTL; break; + default: + OSL_FAIL("unexpected case" ); + return; + } + pObj->SetMergedItem( SvxLanguageItem( nLang, nLangWhichId ) ); + + // Reset shape text language to default, so it inherits the shape language set above. + OutlinerParaObject* pOutliner = pObj->GetOutlinerParaObject(); + if (pOutliner) + { + EditTextObject& rEditTextObject + = const_cast<EditTextObject&>(pOutliner->GetTextObject()); + for (sal_uInt16 n : aLangWhichId_EE) + { + rEditTextObject.RemoveCharAttribs(n); + } + } + } + } + else // Reset to default + { + for(sal_uInt16 n : aLangWhichId_EE) + pObj->ClearMergedItem( n ); + } +} + +static void lcl_setLanguage( const SdDrawDocument *pDoc, std::u16string_view rLanguage, bool bLanguageNone = false ) +{ + LanguageType nLang = SvtLanguageTable::GetLanguageType( rLanguage ); + + // Do it for SdDrawDocument->SetLanguage as well? + + sal_uInt16 nPageCount = pDoc->GetPageCount(); // Pick All Pages + for( sal_uInt16 nPage = 0; nPage < nPageCount; nPage++ ) + { + const SdrPage *pPage = pDoc->GetPage( nPage ); + const size_t nObjCount = pPage->GetObjCount(); + for( size_t nObj = 0; nObj < nObjCount; ++nObj ) + { + SdrObject *pObj = pPage->GetObj( nObj ); + if (pObj->GetObjIdentifier() != SdrObjKind::Page) + lcl_setLanguageForObj( pObj, nLang, bLanguageNone ); + } + } +} + +/** + * Handles SFX-Requests + */ +void DrawDocShell::Execute( SfxRequest& rReq ) +{ + if(mpViewShell && SlideShow::IsRunning( mpViewShell->GetViewShellBase() )) + { + // during a running presentation no slot will be executed + return; + } + + switch ( rReq.GetSlot() ) + { + case SID_SEARCH_ITEM: + { + const SfxItemSet* pReqArgs = rReq.GetArgs(); + + if (pReqArgs) + { + const SvxSearchItem & rSearchItem = pReqArgs->Get(SID_SEARCH_ITEM); + + SD_MOD()->SetSearchItem(std::unique_ptr<SvxSearchItem>(rSearchItem.Clone())); + } + + rReq.Done(); + } + break; + + case FID_SEARCH_ON: + { + // no action needed + rReq.Done(); + } + break; + + case FID_SEARCH_OFF: + { + if (mpViewShell) + { + sd::View* pView = mpViewShell->GetView(); + if (pView) + { + auto& rFunctionContext = pView->getSearchContext(); + rtl::Reference<FuSearch>& xFuSearch(rFunctionContext.getFunctionSearch()); + + if (xFuSearch.is()) + { + // End Search&Replace in all docshells + SfxObjectShell* pFirstShell = SfxObjectShell::GetFirst(); + SfxObjectShell* pShell = pFirstShell; + + while (pShell) + { + auto pDrawDocShell = dynamic_cast<DrawDocShell*>(pShell); + if (pDrawDocShell) + pDrawDocShell->CancelSearching(); + + pShell = SfxObjectShell::GetNext(*pShell); + + if (pShell == pFirstShell) + pShell = nullptr; + } + + rFunctionContext.resetSearchFunction(); + Invalidate(); + rReq.Done(); + } + } + } + } + break; + + case FID_SEARCH_NOW: + { + const SfxItemSet* pReqArgs = rReq.GetArgs(); + + if (pReqArgs && mpViewShell) + { + sd::View* pView = mpViewShell->GetView(); + if (pView) + { + rtl::Reference<FuSearch> & xFuSearch = pView->getSearchContext().getFunctionSearch(); + + if (!xFuSearch.is()) + { + xFuSearch = rtl::Reference<FuSearch>( + FuSearch::createPtr(mpViewShell, + mpViewShell->GetActiveWindow(), + pView, mpDoc, rReq)); + + pView->getSearchContext().setSearchFunction(xFuSearch); + } + + if (xFuSearch.is()) + { + const SvxSearchItem& rSearchItem = pReqArgs->Get(SID_SEARCH_ITEM); + + SD_MOD()->SetSearchItem(std::unique_ptr<SvxSearchItem>(rSearchItem.Clone())); + xFuSearch->SearchAndReplace(&rSearchItem); + } + } + } + + rReq.Done(); + } + break; + + case SID_CLOSEDOC: + { + ExecuteSlot(rReq, SfxObjectShell::GetStaticInterface()); + } + break; + + case SID_GET_COLORLIST: + { + const SvxColorListItem* pColItem = GetItem( SID_COLOR_TABLE ); + const XColorListRef& pList = pColItem->GetColorList(); + rReq.SetReturnValue( OfaXColorListItem( SID_GET_COLORLIST, pList ) ); + } + break; + + case SID_VERSION: + { + ExecuteSlot( rReq, SfxObjectShell::GetStaticInterface() ); + } + break; + + case SID_HANGUL_HANJA_CONVERSION: + { + if( mpViewShell ) + { + rtl::Reference<FuPoor> aFunc( FuHangulHanjaConversion::Create( mpViewShell, mpViewShell->GetActiveWindow(), mpViewShell->GetView(), mpDoc, rReq ) ); + static_cast< FuHangulHanjaConversion* >( aFunc.get() )->StartConversion( LANGUAGE_KOREAN, LANGUAGE_KOREAN, nullptr, i18n::TextConversionOption::CHARACTER_BY_CHARACTER, true ); + } + } + break; + + case SID_CHINESE_CONVERSION: + { + if( mpViewShell ) + { + rtl::Reference<FuPoor> aFunc( FuHangulHanjaConversion::Create( mpViewShell, mpViewShell->GetActiveWindow(), mpViewShell->GetView(), mpDoc, rReq ) ); + static_cast< FuHangulHanjaConversion* >( aFunc.get() )->StartChineseConversion(); + } + } + break; + case SID_LANGUAGE_STATUS: + { + OUString aNewLangTxt; + const SfxStringItem* pItem = rReq.GetArg<SfxStringItem>(SID_LANGUAGE_STATUS); + if (pItem) + aNewLangTxt = pItem->GetValue(); + + if (aNewLangTxt == "*" ) + { + // open the dialog "Tools/Options/Language Settings - Language" + if (mpViewShell) + { + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateVclDialog( mpViewShell->GetFrameWeld(), SID_LANGUAGE_OPTIONS )); + pDlg->Execute(); + } + } + else + { + if( mpViewShell ) + { + // setting the new language... + if (!aNewLangTxt.isEmpty()) + { + static const OUStringLiteral aSelectionLangPrefix(u"Current_"); + static const OUStringLiteral aParagraphLangPrefix(u"Paragraph_"); + static const OUStringLiteral aDocumentLangPrefix(u"Default_"); + + bool bSelection = false; + bool bParagraph = false; + + SdDrawDocument* pDoc = mpViewShell->GetDoc(); + sal_Int32 nPos = -1; + if (-1 != (nPos = aNewLangTxt.indexOf( aDocumentLangPrefix ))) + { + aNewLangTxt = aNewLangTxt.replaceAt( nPos, aDocumentLangPrefix.getLength(), u"" ); + + if (aNewLangTxt == "LANGUAGE_NONE") + lcl_setLanguage( pDoc, u"", true ); + else if (aNewLangTxt == "RESET_LANGUAGES") + lcl_setLanguage( pDoc, u"" ); + else + lcl_setLanguage( pDoc, aNewLangTxt ); + } + else if (-1 != (nPos = aNewLangTxt.indexOf( aSelectionLangPrefix ))) + { + bSelection = true; + aNewLangTxt = aNewLangTxt.replaceAt( nPos, aSelectionLangPrefix.getLength(), u"" ); + } + else if (-1 != (nPos = aNewLangTxt.indexOf( aParagraphLangPrefix ))) + { + bParagraph = true; + aNewLangTxt = aNewLangTxt.replaceAt( nPos, aParagraphLangPrefix.getLength(), u"" ); + } + + if (bSelection || bParagraph) + { + SdrView* pSdrView = mpViewShell->GetDrawView(); + if (!pSdrView) + return; + + EditView& rEditView = pSdrView->GetTextEditOutlinerView()->GetEditView(); + const LanguageType nLangToUse = SvtLanguageTable::GetLanguageType( aNewLangTxt ); + SvtScriptType nScriptType = SvtLanguageOptions::GetScriptTypeOfLanguage( nLangToUse ); + + SfxItemSet aAttrs = rEditView.GetEditEngine()->GetEmptyItemSet(); + if (nScriptType == SvtScriptType::LATIN) + aAttrs.Put( SvxLanguageItem( nLangToUse, EE_CHAR_LANGUAGE ) ); + if (nScriptType == SvtScriptType::COMPLEX) + aAttrs.Put( SvxLanguageItem( nLangToUse, EE_CHAR_LANGUAGE_CTL ) ); + if (nScriptType == SvtScriptType::ASIAN) + aAttrs.Put( SvxLanguageItem( nLangToUse, EE_CHAR_LANGUAGE_CJK ) ); + ESelection aOldSel; + if (bParagraph) + { + ESelection aSel = rEditView.GetSelection(); + aOldSel = aSel; + aSel.nStartPos = 0; + aSel.nEndPos = EE_TEXTPOS_ALL; + rEditView.SetSelection( aSel ); + } + + rEditView.SetAttribs( aAttrs ); + if (bParagraph) + rEditView.SetSelection( aOldSel ); + } + + if ( pDoc->GetOnlineSpell() ) + { + pDoc->StartOnlineSpelling(); + } + } + } + } + Broadcast(SfxHint(SfxHintId::LanguageChanged)); + } + break; + case SID_SPELLCHECK_IGNORE_ALL: + { + if (!mpViewShell) + return; + SdrView* pSdrView = mpViewShell->GetDrawView(); + if (!pSdrView) + return; + + EditView& rEditView = pSdrView->GetTextEditOutlinerView()->GetEditView(); + OUString sIgnoreText; + const SfxStringItem* pItem2 = rReq.GetArg<SfxStringItem>(FN_PARAM_1); + if (pItem2) + sIgnoreText = pItem2->GetValue(); + + if(sIgnoreText == "Spelling") + { + ESelection aOldSel = rEditView.GetSelection(); + rEditView.SpellIgnoreWord(); + rEditView.SetSelection( aOldSel ); + } + } + break; + case SID_SPELLCHECK_APPLY_SUGGESTION: + { + if (!mpViewShell) + return; + SdrView* pSdrView = mpViewShell->GetDrawView(); + if (!pSdrView) + return; + + EditView& rEditView = pSdrView->GetTextEditOutlinerView()->GetEditView(); + OUString sApplyText; + const SfxStringItem* pItem2 = rReq.GetArg<SfxStringItem>(FN_PARAM_1); + if (pItem2) + sApplyText = pItem2->GetValue(); + + static const OUStringLiteral sSpellingRule(u"Spelling_"); + sal_Int32 nPos = 0; + if(-1 != (nPos = sApplyText.indexOf( sSpellingRule ))) + { + sApplyText = sApplyText.replaceAt(nPos, sSpellingRule.getLength(), u""); + rEditView.InsertText( sApplyText ); + } + } + break; + + case SID_NOTEBOOKBAR: + { + const SfxStringItem* pFile = rReq.GetArg<SfxStringItem>( SID_NOTEBOOKBAR ); + + if ( mpViewShell ) + { + SfxBindings& rBindings( mpViewShell->GetFrame()->GetBindings() ); + + if ( sfx2::SfxNotebookBar::IsActive() ) + sfx2::SfxNotebookBar::ExecMethod( rBindings, pFile ? pFile->GetValue() : "" ); + else + sfx2::SfxNotebookBar::CloseMethod( rBindings ); + } + } + break; + + default: + break; + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/docshell/docshel4.cxx b/sd/source/ui/docshell/docshel4.cxx new file mode 100644 index 000000000..6fe599e44 --- /dev/null +++ b/sd/source/ui/docshell/docshel4.cxx @@ -0,0 +1,1002 @@ +/* -*- 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 <sal/config.h> +#include <sal/log.hxx> + +#include <memory> +#include <utility> + +#include <DrawDocShell.hxx> +#include <com/sun/star/document/PrinterIndependentLayout.hpp> +#include <editeng/outlobj.hxx> +#include <tools/urlobj.hxx> +#include <svx/svxids.hrc> +#include <editeng/editeng.hxx> +#include <editeng/editstat.hxx> +#include <editeng/flstitem.hxx> +#include <svl/flagitem.hxx> +#include <sot/storage.hxx> +#include <sfx2/dinfdlg.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/dispatch.hxx> +#include <svx/svdotext.hxx> +#include <sfx2/printer.hxx> +#include <svtools/ctrltool.hxx> +#include <comphelper/classids.hxx> +#include <sot/formats.hxx> +#include <sfx2/viewfrm.hxx> +#include <vcl/syswin.hxx> +#include <com/sun/star/drawing/XDrawPage.hpp> +#include <com/sun/star/drawing/XDrawView.hpp> + +#include <app.hrc> +#include <strings.hrc> +#include <FrameView.hxx> +#include <optsitem.hxx> +#include <Outliner.hxx> +#include <sdattr.hrc> +#include <drawdoc.hxx> +#include <ViewShell.hxx> +#include <sdmod.hxx> +#include <View.hxx> +#include <EffectMigration.hxx> +#include <CustomAnimationEffect.hxx> +#include <sdpage.hxx> +#include <sdresid.hxx> +#include <DrawViewShell.hxx> +#include <ViewShellBase.hxx> +#include <OutlineView.hxx> +#include <OutlineViewShell.hxx> +#include <sdxmlwrp.hxx> +#include <sdpptwrp.hxx> +#include <sdcgmfilter.hxx> +#include <sdgrffilter.hxx> +#include <sdhtmlfilter.hxx> +#include <sdpdffilter.hxx> +#include <framework/FrameworkHelper.hxx> +#include <o3tl/string_view.hxx> + +#include <sfx2/zoomitem.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using ::sd::framework::FrameworkHelper; + +// PowerPoint-Filter +constexpr OUStringLiteral pFilterPowerPoint97( u"MS PowerPoint 97" ); +constexpr OUStringLiteral pFilterPowerPoint97Template( u"MS PowerPoint 97 Vorlage" ); +constexpr OUStringLiteral pFilterPowerPoint97AutoPlay( u"MS PowerPoint 97 AutoPlay" ); + +namespace sd { + +/** + * Creates (if necessary) and returns a SfxPrinter + */ +SfxPrinter* DrawDocShell::GetPrinter(bool bCreate) +{ + if (bCreate && !mpPrinter) + { + // create ItemSet with special pool area + auto pSet = std::make_unique<SfxItemSetFixed< + SID_PRINTER_NOTFOUND_WARN, SID_PRINTER_NOTFOUND_WARN, + SID_PRINTER_CHANGESTODOC, SID_PRINTER_CHANGESTODOC, + ATTR_OPTIONS_PRINT, ATTR_OPTIONS_PRINT>>( GetPool() ); + // set PrintOptionsSet + SdOptionsPrintItem aPrintItem( SD_MOD()->GetSdOptions(mpDoc->GetDocumentType()) ); + SfxFlagItem aFlagItem( SID_PRINTER_CHANGESTODOC ); + SfxPrinterChangeFlags nFlags = + (aPrintItem.GetOptionsPrint().IsWarningSize() ? SfxPrinterChangeFlags::CHG_SIZE : SfxPrinterChangeFlags::NONE) | + (aPrintItem.GetOptionsPrint().IsWarningOrientation() ? SfxPrinterChangeFlags::CHG_ORIENTATION : SfxPrinterChangeFlags::NONE); + aFlagItem.SetValue( static_cast<int>(nFlags) ); + + pSet->Put( aPrintItem ); + pSet->Put( SfxBoolItem( SID_PRINTER_NOTFOUND_WARN, aPrintItem.GetOptionsPrint().IsWarningPrinter() ) ); + pSet->Put( aFlagItem ); + + mpPrinter = VclPtr<SfxPrinter>::Create(std::move(pSet)); + mbOwnPrinter = true; + + // set output quality + sal_uInt16 nQuality = aPrintItem.GetOptionsPrint().GetOutputQuality(); + + DrawModeFlags nMode = DrawModeFlags::Default; + // 1 == Grayscale, 2 == Black & White (with grayscale images) + if( nQuality == 1 ) + nMode = DrawModeFlags::GrayLine | DrawModeFlags::GrayFill | DrawModeFlags::GrayText | DrawModeFlags::GrayBitmap | DrawModeFlags::GrayGradient; + else if( nQuality == 2 ) + nMode = DrawModeFlags::BlackLine | DrawModeFlags::WhiteFill | DrawModeFlags::BlackText | DrawModeFlags::GrayBitmap | DrawModeFlags::WhiteGradient; + + mpPrinter->SetDrawMode( nMode ); + + MapMode aMM (mpPrinter->GetMapMode()); + aMM.SetMapUnit(MapUnit::Map100thMM); + mpPrinter->SetMapMode(aMM); + UpdateRefDevice(); + } + return mpPrinter; +} + +/** + * Set new SfxPrinter (transfer of ownership) + */ +void DrawDocShell::SetPrinter(SfxPrinter *pNewPrinter) +{ + if ( mpViewShell ) + { + ::sd::View* pView = mpViewShell->GetView(); + if ( pView->IsTextEdit() ) + pView->SdrEndTextEdit(); + } + + if ( mpPrinter && mbOwnPrinter && (mpPrinter.get() != pNewPrinter) ) + mpPrinter.disposeAndClear(); + + mpPrinter = pNewPrinter; + mbOwnPrinter = true; + if ( mpDoc->GetPrinterIndependentLayout() == css::document::PrinterIndependentLayout::DISABLED ) + UpdateFontList(); + UpdateRefDevice(); +} + +void DrawDocShell::UpdateFontList() +{ + mpFontList.reset(); + OutputDevice* pRefDevice = nullptr; + if ( mpDoc->GetPrinterIndependentLayout() == css::document::PrinterIndependentLayout::DISABLED ) + pRefDevice = GetPrinter(true); + else + pRefDevice = SD_MOD()->GetVirtualRefDevice(); + mpFontList.reset( new FontList(pRefDevice, nullptr) ); + SvxFontListItem aFontListItem( mpFontList.get(), SID_ATTR_CHAR_FONTLIST ); + PutItem( aFontListItem ); +} + +Printer* DrawDocShell::GetDocumentPrinter() +{ + return GetPrinter(false); +} + +void DrawDocShell::OnDocumentPrinterChanged(Printer* pNewPrinter) +{ + // if we already have a printer, see if it's the same + if( mpPrinter ) + { + // easy case + if( mpPrinter == pNewPrinter ) + return; + + // compare if it's the same printer with the same job setup + if( (mpPrinter->GetName() == pNewPrinter->GetName()) && + (mpPrinter->GetJobSetup() == pNewPrinter->GetJobSetup())) + return; + } + + SfxPrinter* const pSfxPrinter = dynamic_cast<SfxPrinter*>(pNewPrinter); + if (pSfxPrinter) + { + SetPrinter(pSfxPrinter); + + // container owns printer + mbOwnPrinter = false; + } +} + +void DrawDocShell::UpdateRefDevice() +{ + if( !mpDoc ) + return; + + // Determine the device for which the output will be formatted. + VclPtr< OutputDevice > pRefDevice; + switch (mpDoc->GetPrinterIndependentLayout()) + { + case css::document::PrinterIndependentLayout::DISABLED: + pRefDevice = mpPrinter.get(); + break; + + case css::document::PrinterIndependentLayout::ENABLED: + pRefDevice = SD_MOD()->GetVirtualRefDevice(); + break; + + default: + // We are confronted with an invalid or un-implemented + // layout mode. Use the printer as formatting device + // as a fall-back. + SAL_WARN( "sd", "DrawDocShell::UpdateRefDevice(): Unexpected printer layout mode"); + + pRefDevice = mpPrinter.get(); + break; + } + mpDoc->SetRefDevice( pRefDevice.get() ); + + SdOutliner* pOutl = mpDoc->GetOutliner( false ); + + if( pOutl ) + pOutl->SetRefDevice( pRefDevice ); + + SdOutliner* pInternalOutl = mpDoc->GetInternalOutliner( false ); + + if( pInternalOutl ) + pInternalOutl->SetRefDevice( pRefDevice ); +} + +/** + * Creates new document, opens streams + */ +bool DrawDocShell::InitNew( const css::uno::Reference< css::embed::XStorage >& xStorage ) +{ + bool bRet = SfxObjectShell::InitNew( xStorage ); + + ::tools::Rectangle aVisArea( Point(0, 0), Size(14100, 10000) ); + SetVisArea(aVisArea); + + if (bRet) + { + if( !mbSdDataObj ) + mpDoc->NewOrLoadCompleted(DocCreationMode::New); // otherwise calling + // NewOrLoadCompleted(NEW_LOADED) in + // SdDrawDocument::AllocModel() + } + return bRet; +} + +/** + * loads pools and document + */ +bool DrawDocShell::Load( SfxMedium& rMedium ) +{ + // If this is an ODF file being loaded, then by default, use legacy processing + // for tdf#99729 (if required, it will be overridden in *::ReadUserDataSequence()) + if (IsOwnStorageFormat(rMedium)) + { + mpDoc->SetAnchoredTextOverflowLegacy(true); + } + + bool bRet = false; + bool bStartPresentation = false; + ErrCode nError = ERRCODE_NONE; + + SfxItemSet* pSet = rMedium.GetItemSet(); + + if( pSet ) + { + if( ( SfxItemState::SET == pSet->GetItemState(SID_PREVIEW ) ) && pSet->Get( SID_PREVIEW ).GetValue() ) + { + mpDoc->SetStarDrawPreviewMode( true ); + } + + if( SfxItemState::SET == pSet->GetItemState(SID_DOC_STARTPRESENTATION)&& + pSet->Get( SID_DOC_STARTPRESENTATION ).GetValue() ) + { + bStartPresentation = true; + mpDoc->SetStartWithPresentation( true ); + } + } + + bRet = SfxObjectShell::Load( rMedium ); + if (bRet) + { + comphelper::EmbeddedObjectContainer& rEmbeddedObjectContainer = getEmbeddedObjectContainer(); + rEmbeddedObjectContainer.setUserAllowsLinkUpdate(false); + bRet = SdXMLFilter( rMedium, *this, SdXMLFilterMode::Normal, SotStorage::GetVersion( rMedium.GetStorage() ) ).Import( nError ); + } + + if( bRet ) + { + // for legacy markup in OOoXML filter, convert the animations now + EffectMigration::DocumentLoaded(*GetDoc()); + UpdateTablePointers(); + + // If we're an embedded OLE object, use tight bounds + // for our visArea. No point in showing the user lots of empty + // space. Had to remove the check for empty VisArea below, + // since XML load always sets a VisArea before. + //TODO/LATER: looks a little bit strange! + if( ( GetCreateMode() == SfxObjectCreateMode::EMBEDDED ) && SfxObjectShell::GetVisArea( ASPECT_CONTENT ).IsEmpty() ) + { + SdPage* pPage = mpDoc->GetSdPage( 0, PageKind::Standard ); + + if( pPage ) + SetVisArea( pPage->GetAllObjBoundRect() ); + } + + FinishedLoading(); + + const INetURLObject aUrl; + SfxObjectShell::SetAutoLoad( aUrl, 0, false ); + } + else + { + if( nError == ERRCODE_IO_BROKENPACKAGE ) + SetError(ERRCODE_IO_BROKENPACKAGE); + + // TODO/LATER: correct error handling?! + //pStore->SetError(SVSTREAM_WRONGVERSION); + else + SetError(ERRCODE_ABORT); + } + + // tell SFX to change viewshell when in preview mode + if( IsPreview() || bStartPresentation ) + { + SfxItemSet *pMediumSet = GetMedium()->GetItemSet(); + if( pMediumSet ) + pMediumSet->Put( SfxUInt16Item( SID_VIEW_ID, bStartPresentation ? 1 : 5 ) ); + } + + return bRet; +} + +/** + * loads content for organizer + */ +bool DrawDocShell::LoadFrom( SfxMedium& rMedium ) +{ + std::unique_ptr<weld::WaitObject> pWait; + if( mpViewShell ) + pWait.reset(new weld::WaitObject(mpViewShell->GetFrameWeld())); + + mpDoc->NewOrLoadCompleted( DocCreationMode::New ); + mpDoc->CreateFirstPages(); + mpDoc->StopWorkStartupDelay(); + + // TODO/LATER: nobody is interested in the error code?! + ErrCode nError = ERRCODE_NONE; + bool bRet = SdXMLFilter( rMedium, *this, SdXMLFilterMode::Organizer, SotStorage::GetVersion( rMedium.GetStorage() ) ).Import( nError ); + + // tell SFX to change viewshell when in preview mode + if( IsPreview() ) + { + SfxItemSet *pSet = GetMedium()->GetItemSet(); + + if( pSet ) + pSet->Put( SfxUInt16Item( SID_VIEW_ID, 5 ) ); + } + + return bRet; +} + +/** + * load from 3rd party format + */ +bool DrawDocShell::ImportFrom(SfxMedium &rMedium, + uno::Reference<text::XTextRange> const& xInsertPosition) +{ + const OUString aFilterName( rMedium.GetFilter()->GetFilterName() ); + if (aFilterName == "Impress MS PowerPoint 2007 XML" || + aFilterName == "Impress MS PowerPoint 2007 XML AutoPlay" || + aFilterName == "Impress MS PowerPoint 2007 XML VBA") + { + // As this is a MSFT format, we should use the "MS Compat" + // mode for spacing before and after paragraphs. + + // This is copied from what is done for .ppt import in + // ImplSdPPTImport::Import() in sd/source/filter/ppt/pptin.cxx + // in. We need to tell both the edit engine of the draw outliner, + // and the document, to do "summation of paragraphs". + SdrOutliner& rOutl = mpDoc->GetDrawOutliner(); + EEControlBits nControlWord = rOutl.GetEditEngine().GetControlWord(); + nControlWord |= EEControlBits::ULSPACESUMMATION; + const_cast<EditEngine&>(rOutl.GetEditEngine()).SetControlWord( nControlWord ); + + mpDoc->SetSummationOfParagraphs(); + } + + if (aFilterName == "Impress MS PowerPoint 2007 XML" || + aFilterName == "Impress MS PowerPoint 2007 XML AutoPlay" || + aFilterName == "Impress MS PowerPoint 2007 XML VBA" || + aFilterName == "Impress Office Open XML") + { + // We need to be able to set the default tab size for each text object. + // This is possible at the moment only for the whole document. See + // TextParagraphPropertiesContext constructor. So default tab width + // of the LibreOffice is 1270 but MSO is 2540 on general settings. + mpDoc->SetDefaultTabulator( 2540 ); + } + + const bool bRet = SfxObjectShell::ImportFrom(rMedium, xInsertPosition); + + SfxItemSet* pSet = rMedium.GetItemSet(); + if( pSet ) + { + if( SfxItemState::SET == pSet->GetItemState(SID_DOC_STARTPRESENTATION)&& + pSet->Get( SID_DOC_STARTPRESENTATION ).GetValue() ) + { + mpDoc->SetStartWithPresentation( true ); + + // tell SFX to change viewshell when in preview mode + if( IsPreview() ) + { + SfxItemSet *pMediumSet = GetMedium()->GetItemSet(); + if( pMediumSet ) + pMediumSet->Put( SfxUInt16Item( SID_VIEW_ID, 1 ) ); + } + } + } + + return bRet; +} + +/** + * load from a foreign format + */ +bool DrawDocShell::ConvertFrom( SfxMedium& rMedium ) +{ + const OUString aFilterName( rMedium.GetFilter()->GetFilterName() ); + bool bRet = false; + bool bStartPresentation = false; + + SetWaitCursor( true ); + + SfxItemSet* pSet = rMedium.GetItemSet(); + if( pSet ) + { + if( ( SfxItemState::SET == pSet->GetItemState(SID_PREVIEW ) ) && pSet->Get( SID_PREVIEW ).GetValue() ) + { + mpDoc->SetStarDrawPreviewMode( true ); + } + + if( SfxItemState::SET == pSet->GetItemState(SID_DOC_STARTPRESENTATION)&& + pSet->Get( SID_DOC_STARTPRESENTATION ).GetValue() ) + { + bStartPresentation = true; + mpDoc->SetStartWithPresentation( true ); + } + } + + if( aFilterName == pFilterPowerPoint97 + || aFilterName == pFilterPowerPoint97Template + || aFilterName == pFilterPowerPoint97AutoPlay) + { + mpDoc->StopWorkStartupDelay(); + bRet = SdPPTFilter( rMedium, *this ).Import(); + } + else if (aFilterName.indexOf("impress8") >= 0 || + aFilterName.indexOf("draw8") >= 0) + { + // TODO/LATER: nobody is interested in the error code?! + mpDoc->CreateFirstPages(); + mpDoc->StopWorkStartupDelay(); + ErrCode nError = ERRCODE_NONE; + bRet = SdXMLFilter( rMedium, *this ).Import( nError ); + + } + else if (aFilterName.indexOf("StarOffice XML (Draw)") >= 0 || + aFilterName.indexOf("StarOffice XML (Impress)") >= 0) + { + // TODO/LATER: nobody is interested in the error code?! + mpDoc->CreateFirstPages(); + mpDoc->StopWorkStartupDelay(); + ErrCode nError = ERRCODE_NONE; + bRet = SdXMLFilter( rMedium, *this, SdXMLFilterMode::Normal, SOFFICE_FILEFORMAT_60 ).Import( nError ); + } + else if (aFilterName == "CGM - Computer Graphics Metafile") + { + mpDoc->CreateFirstPages(); + mpDoc->StopWorkStartupDelay(); + bRet = SdCGMFilter( rMedium, *this ).Import(); + } + else if (aFilterName == "draw_pdf_import") + { + mpDoc->CreateFirstPages(); + mpDoc->StopWorkStartupDelay(); + bRet = SdPdfFilter(rMedium, *this).Import(); + } + else + { + mpDoc->CreateFirstPages(); + mpDoc->StopWorkStartupDelay(); + bRet = SdGRFFilter( rMedium, *this ).Import(); + } + + FinishedLoading(); + + // tell SFX to change viewshell when in preview mode + if( IsPreview() ) + { + SfxItemSet *pMediumSet = GetMedium()->GetItemSet(); + + if( pMediumSet ) + pMediumSet->Put( SfxUInt16Item( SID_VIEW_ID, 5 ) ); + } + SetWaitCursor( false ); + + // tell SFX to change viewshell when in preview mode + if( IsPreview() || bStartPresentation ) + { + SfxItemSet *pMediumSet = GetMedium()->GetItemSet(); + if( pMediumSet ) + pMediumSet->Put( SfxUInt16Item( SID_VIEW_ID, bStartPresentation ? 1 : 5 ) ); + } + + return bRet; +} + +/** + * Writes pools and document to the open streams + */ +bool DrawDocShell::Save() +{ + mpDoc->StopWorkStartupDelay(); + + //TODO/LATER: why this?! + if( GetCreateMode() == SfxObjectCreateMode::STANDARD ) + SfxObjectShell::SetVisArea( ::tools::Rectangle() ); + + bool bRet = SfxObjectShell::Save(); + + if( bRet ) + bRet = SdXMLFilter( *GetMedium(), *this, SdXMLFilterMode::Normal, SotStorage::GetVersion( GetMedium()->GetStorage() ) ).Export(); + + return bRet; +} + +/** + * Writes pools and document to the provided storage + */ +bool DrawDocShell::SaveAs( SfxMedium& rMedium ) +{ + mpDoc->setDocAccTitle(OUString()); + if (SfxViewFrame* pFrame1 = SfxViewFrame::GetFirst(this)) + { + if (vcl::Window* pSysWin = pFrame1->GetWindow().GetSystemWindow()) + { + pSysWin->SetAccessibleName(OUString()); + } + } + mpDoc->StopWorkStartupDelay(); + + //With custom animation, if Outliner is modified, update text before saving + if( mpViewShell ) + { + SdPage* pPage = mpViewShell->getCurrentPage(); + if( pPage && pPage->getMainSequence()->getCount() ) + { + SdrObject* pObj = mpViewShell->GetView()->GetTextEditObject(); + SdrOutliner* pOutl = mpViewShell->GetView()->GetTextEditOutliner(); + if( pObj && pOutl && pOutl->IsModified() ) + { + std::optional<OutlinerParaObject> pNewText = pOutl->CreateParaObject( 0, pOutl->GetParagraphCount() ); + pObj->SetOutlinerParaObject( std::move(pNewText) ); + pOutl->ClearModifyFlag(); + } + } + } + + //TODO/LATER: why this?! + if( GetCreateMode() == SfxObjectCreateMode::STANDARD ) + SfxObjectShell::SetVisArea( ::tools::Rectangle() ); + + bool bRet = SfxObjectShell::SaveAs( rMedium ); + + if( bRet ) + bRet = SdXMLFilter( rMedium, *this, SdXMLFilterMode::Normal, SotStorage::GetVersion( rMedium.GetStorage() ) ).Export(); + + if( GetError() == ERRCODE_NONE ) + SetError(ERRCODE_NONE); + + return bRet; +} + +/** + * save to foreign format + */ +bool DrawDocShell::ConvertTo( SfxMedium& rMedium ) +{ + bool bRet = false; + + if( mpDoc->GetPageCount() ) + { + std::shared_ptr<const SfxFilter> pMediumFilter = rMedium.GetFilter(); + const OUString aTypeName( pMediumFilter->GetTypeName() ); + std::unique_ptr<SdFilter> xFilter; + + if( aTypeName.indexOf( "graphic_HTML" ) >= 0 ) + { + xFilter = std::make_unique<SdHTMLFilter>(rMedium, *this); + } + else if( aTypeName.indexOf( "MS_PowerPoint_97" ) >= 0 ) + { + xFilter = std::make_unique<SdPPTFilter>(rMedium, *this); + static_cast<SdPPTFilter*>(xFilter.get())->PreSaveBasic(); + } + else if ( aTypeName.indexOf( "CGM_Computer_Graphics_Metafile" ) >= 0 ) + { + xFilter = std::make_unique<SdCGMFilter>(rMedium, *this); + } + else if( aTypeName.indexOf( "draw8" ) >= 0 || + aTypeName.indexOf( "impress8" ) >= 0 ) + { + xFilter = std::make_unique<SdXMLFilter>(rMedium, *this); + } + else if( aTypeName.indexOf( "StarOffice_XML_Impress" ) >= 0 || + aTypeName.indexOf( "StarOffice_XML_Draw" ) >= 0 ) + { + xFilter = std::make_unique<SdXMLFilter>(rMedium, *this, SdXMLFilterMode::Normal, SOFFICE_FILEFORMAT_60); + } + else + { + xFilter = std::make_unique<SdGRFFilter>(rMedium, *this); + } + + if (xFilter) + { + if ( mpViewShell ) + { + ::sd::View* pView = mpViewShell->GetView(); + if ( pView->IsTextEdit() ) + pView->SdrEndTextEdit(); + } + + bRet = xFilter->Export(); + } + } + + return bRet; +} + +/** + * Reopen own streams to ensure that nobody else can prevent use from opening + * them. + */ +bool DrawDocShell::SaveCompleted( const css::uno::Reference< css::embed::XStorage >& xStorage ) +{ + bool bRet = false; + + if( SfxObjectShell::SaveCompleted(xStorage) ) + { + mpDoc->NbcSetChanged( false ); + + if( mpViewShell ) + { + if( dynamic_cast< OutlineViewShell *>( mpViewShell ) != nullptr ) + static_cast<OutlineView*>(mpViewShell->GetView()) + ->GetOutliner().ClearModifyFlag(); + + SdrOutliner* pOutl = mpViewShell->GetView()->GetTextEditOutliner(); + if( pOutl ) + { + SdrObject* pObj = mpViewShell->GetView()->GetTextEditObject(); + if( pObj ) + pObj->NbcSetOutlinerParaObject( pOutl->CreateParaObject() ); + + pOutl->ClearModifyFlag(); + } + } + + bRet = true; + + SfxViewFrame* pFrame = ( mpViewShell && mpViewShell->GetViewFrame() ) ? + mpViewShell->GetViewFrame() : + SfxViewFrame::Current(); + + if( pFrame ) + pFrame->GetBindings().Invalidate( SID_NAVIGATOR_STATE, true ); + } + return bRet; +} + +SfxStyleSheetBasePool* DrawDocShell::GetStyleSheetPool() +{ + return mpDoc->GetStyleSheetPool(); +} + +void DrawDocShell::GotoBookmark(std::u16string_view rBookmark) +{ + auto pDrawViewShell = dynamic_cast<DrawViewShell *>( mpViewShell ); + if (!pDrawViewShell) + return; + + ViewShellBase& rBase (mpViewShell->GetViewShellBase()); + + bool bIsMasterPage = false; + sal_uInt16 nPageNumber = SDRPAGE_NOTFOUND; + SdrObject* pObj = nullptr; + + static constexpr std::u16string_view sInteraction( u"action?" ); + if ( o3tl::starts_with(rBookmark, sInteraction ) ) + { + static constexpr std::u16string_view sJump( u"jump=" ); + if ( o3tl::starts_with(rBookmark.substr( sInteraction.size() ), sJump ) ) + { + std::u16string_view aDestination( rBookmark.substr( sInteraction.size() + sJump.size() ) ); + if ( o3tl::starts_with(aDestination, u"firstslide" ) ) + { + nPageNumber = 1; + } + else if ( o3tl::starts_with(aDestination, u"lastslide" ) ) + { + nPageNumber = mpDoc->GetPageCount() - 2; + } + else if ( o3tl::starts_with(aDestination, u"previousslide" ) ) + { + SdPage* pPage = pDrawViewShell->GetActualPage(); + nPageNumber = pPage->GetPageNum(); + nPageNumber = nPageNumber > 2 ? nPageNumber - 2 : SDRPAGE_NOTFOUND; + } + else if ( o3tl::starts_with(aDestination, u"nextslide" ) ) + { + SdPage* pPage = pDrawViewShell->GetActualPage(); + nPageNumber = pPage->GetPageNum() + 2; + if ( nPageNumber >= mpDoc->GetPageCount() ) + nPageNumber = SDRPAGE_NOTFOUND; + } + } + } + else + { + // Is the bookmark a page? + nPageNumber = mpDoc->GetPageByName( rBookmark, bIsMasterPage ); + + if (nPageNumber == SDRPAGE_NOTFOUND) + { + // Is the bookmark an object? + pObj = mpDoc->GetObj(rBookmark); + + if (pObj) + { + nPageNumber = pObj->getSdrPageFromSdrObject()->GetPageNum(); + } + } + } + if (nPageNumber != SDRPAGE_NOTFOUND) + { + // Jump to the bookmarked page. This is done in three steps. + + SdPage* pPage; + if (bIsMasterPage) + pPage = static_cast<SdPage*>( mpDoc->GetMasterPage(nPageNumber) ); + else + pPage = static_cast<SdPage*>( mpDoc->GetPage(nPageNumber) ); + + // 1.) Change the view shell to the edit view, the notes view, + // or the handout view. + PageKind eNewPageKind = pPage->GetPageKind(); + + if( (eNewPageKind != PageKind::Standard) && (mpDoc->GetDocumentType() == DocumentType::Draw) ) + return; + + if (eNewPageKind != pDrawViewShell->GetPageKind()) + { + // change work area + GetFrameView()->SetPageKind(eNewPageKind); + OUString sViewURL; + switch (eNewPageKind) + { + case PageKind::Standard: + sViewURL = FrameworkHelper::msImpressViewURL; + break; + case PageKind::Notes: + sViewURL = FrameworkHelper::msNotesViewURL; + break; + case PageKind::Handout: + sViewURL = FrameworkHelper::msHandoutViewURL; + break; + default: + break; + } + if (!sViewURL.isEmpty()) + { + std::shared_ptr<FrameworkHelper> pHelper ( + FrameworkHelper::Instance(rBase)); + pHelper->RequestView( + sViewURL, + FrameworkHelper::msCenterPaneURL); + pHelper->WaitForUpdate(); + + // Get the new DrawViewShell. + mpViewShell = pHelper->GetViewShell(FrameworkHelper::msCenterPaneURL).get(); + pDrawViewShell = dynamic_cast<sd::DrawViewShell*>(mpViewShell); + } + else + { + pDrawViewShell = nullptr; + } + } + + if (pDrawViewShell != nullptr) + { + setEditMode(pDrawViewShell, bIsMasterPage); + + // Make the bookmarked page the current page. This is done + // by using the API because this takes care of all the + // little things to be done. Especially writing the view + // data to the frame view. + sal_uInt16 nSdPgNum = (nPageNumber - 1) / 2; + Reference<drawing::XDrawView> xController (rBase.GetController(), UNO_QUERY); + if (xController.is()) + { + Reference<drawing::XDrawPage> xDrawPage (pPage->getUnoPage(), UNO_QUERY); + xController->setCurrentPage (xDrawPage); + } + else + { + // As a fall back switch to the page via the core. + DBG_ASSERT (xController.is(), + "DrawDocShell::GotoBookmark: can't switch page via API"); + pDrawViewShell->SwitchPage(nSdPgNum); + } + + if (pDrawViewShell->GetDispatcher()) + { + // show page + SvxZoomItem aZoom; + aZoom.SetType( SvxZoomType::WHOLEPAGE ); + pDrawViewShell->GetDispatcher()->ExecuteList(SID_ATTR_ZOOM, SfxCallMode::ASYNCHRON, { &aZoom }); + } + + if (pObj != nullptr) + { + // select object + pDrawViewShell->GetView()->UnmarkAll(); + pDrawViewShell->GetView()->MarkObj( + pObj, + pDrawViewShell->GetView()->GetSdrPageView()); + } + } + } + + SfxBindings& rBindings = ((pDrawViewShell && pDrawViewShell->GetViewFrame()!=nullptr) + ? pDrawViewShell->GetViewFrame() + : SfxViewFrame::Current() )->GetBindings(); + + rBindings.Invalidate(SID_NAVIGATOR_STATE, true); + rBindings.Invalidate(SID_NAVIGATOR_PAGENAME); +} + +/** + * If it should become a document template. + */ +bool DrawDocShell::SaveAsOwnFormat( SfxMedium& rMedium ) +{ + + std::shared_ptr<const SfxFilter> pFilter = rMedium.GetFilter(); + + if (pFilter->IsOwnTemplateFormat()) + { + /* now the StarDraw specialty: + we assign known layout names to the layout template of the first + page, we set the layout names of the affected masterpages and pages. + We inform all text objects of the affected standard, note and + masterpages about the name change. + */ + + OUString aLayoutName; + + SfxStringItem const * pLayoutItem = rMedium.GetItemSet()->GetItemIfSet(SID_TEMPLATE_NAME, false); + if( pLayoutItem ) + { + aLayoutName = pLayoutItem->GetValue(); + } + else + { + INetURLObject aURL( rMedium.GetName() ); + aURL.removeExtension(); + aLayoutName = aURL.getName(); + } + + if (aLayoutName.isEmpty()) + { + sal_uInt32 nCount = mpDoc->GetMasterSdPageCount(PageKind::Standard); + for (sal_uInt32 i = 0; i < nCount; ++i) + { + OUString aOldPageLayoutName = mpDoc->GetMasterSdPage(i, PageKind::Standard)->GetLayoutName(); + OUString aNewLayoutName = aLayoutName; + // Don't add suffix for the first master page + if( i > 0 ) + aNewLayoutName += OUString::number(i); + + mpDoc->RenameLayoutTemplate(aOldPageLayoutName, aNewLayoutName); + } + } + } + + return SfxObjectShell::SaveAsOwnFormat(rMedium); +} + +void DrawDocShell::FillClass(SvGlobalName* pClassName, + SotClipboardFormatId* pFormat, + OUString* pFullTypeName, + sal_Int32 nFileFormat, + bool bTemplate /* = false */) const +{ + if (nFileFormat == SOFFICE_FILEFORMAT_60) + { + if ( meDocType == DocumentType::Draw ) + { + *pClassName = SvGlobalName(SO3_SDRAW_CLASSID_60); + *pFormat = SotClipboardFormatId::STARDRAW_60; + *pFullTypeName = SdResId(STR_GRAPHIC_DOCUMENT_FULLTYPE_60); + } + else + { + *pClassName = SvGlobalName(SO3_SIMPRESS_CLASSID_60); + *pFormat = SotClipboardFormatId::STARIMPRESS_60; + *pFullTypeName = SdResId(STR_IMPRESS_DOCUMENT_FULLTYPE_60); + } + } + else if (nFileFormat == SOFFICE_FILEFORMAT_8) + { + if ( meDocType == DocumentType::Draw ) + { + *pClassName = SvGlobalName(SO3_SDRAW_CLASSID_60); + *pFormat = bTemplate ? SotClipboardFormatId::STARDRAW_8_TEMPLATE : SotClipboardFormatId::STARDRAW_8; + *pFullTypeName = SdResId(STR_GRAPHIC_DOCUMENT_FULLTYPE_80); // HACK: method will be removed with new storage API + } + else + { + *pClassName = SvGlobalName(SO3_SIMPRESS_CLASSID_60); + *pFormat = bTemplate ? SotClipboardFormatId::STARIMPRESS_8_TEMPLATE : SotClipboardFormatId::STARIMPRESS_8; + *pFullTypeName = SdResId(STR_IMPRESS_DOCUMENT_FULLTYPE_80); // HACK: method will be removed with new storage API + } + } +} + +OutputDevice* DrawDocShell::GetDocumentRefDev() +{ + OutputDevice* pReferenceDevice = SfxObjectShell::GetDocumentRefDev (); + // Only when our parent does not have a reference device then we return + // our own. + if (pReferenceDevice == nullptr && mpDoc != nullptr) + pReferenceDevice = mpDoc->GetRefDevice (); + return pReferenceDevice; +} + +/** executes the SID_OPENDOC slot to let the framework open a document + with the given URL and this document as a referer */ +void DrawDocShell::OpenBookmark( const OUString& rBookmarkURL ) +{ + SfxStringItem aStrItem( SID_FILE_NAME, rBookmarkURL ); + SfxStringItem aReferer( SID_REFERER, GetMedium()->GetName() ); + const SfxPoolItem* ppArgs[] = { &aStrItem, &aReferer, nullptr }; + ( mpViewShell ? mpViewShell->GetViewFrame() : SfxViewFrame::Current() )->GetBindings().Execute( SID_OPENHYPERLINK, ppArgs ); +} + +std::shared_ptr<SfxDocumentInfoDialog> DrawDocShell::CreateDocumentInfoDialog(weld::Window* pParent, const SfxItemSet &rSet) +{ + std::shared_ptr<SfxDocumentInfoDialog> xDlg = std::make_shared<SfxDocumentInfoDialog>(pParent, rSet); + DrawDocShell* pDocSh = dynamic_cast<DrawDocShell*>(SfxObjectShell::Current()); + if( pDocSh == this ) + { + xDlg->AddFontTabPage(); + } + return xDlg; +} + +void DrawDocShell::setEditMode(DrawViewShell* pDrawViewShell, bool isMasterPage) +{ + // Set the edit mode to either the normal edit mode or the + // master page mode. + EditMode eNewEditMode = EditMode::Page; + if (isMasterPage) + { + eNewEditMode = EditMode::MasterPage; + } + + if (eNewEditMode != pDrawViewShell->GetEditMode()) + { + // Set EditMode + pDrawViewShell->ChangeEditMode(eNewEditMode, false); + } +} +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/docshell/docshell.cxx b/sd/source/ui/docshell/docshell.cxx new file mode 100644 index 000000000..78279687a --- /dev/null +++ b/sd/source/ui/docshell/docshell.cxx @@ -0,0 +1,515 @@ +/* -*- 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 <DrawDocShell.hxx> + +#include <officecfg/Office/Common.hxx> +#include <unotools/configmgr.hxx> + +#include <sfx2/docfac.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/svxids.hrc> +#include <svl/srchitem.hxx> +#include <svx/srchdlg.hxx> +#include <svx/svdoutl.hxx> +#include <svx/svditer.hxx> +#include <editeng/flstitem.hxx> +#include <editeng/eeitem.hxx> +#include <svl/eitem.hxx> +#include <svl/intitem.hxx> +#include <sfx2/printer.hxx> +#include <svx/drawitem.hxx> +#include <sfx2/dispatch.hxx> +#include <svl/whiter.hxx> +#include <svl/itempool.hxx> +#include <svl/stritem.hxx> +#include <svtools/ctrltool.hxx> +#include <svtools/langtab.hxx> +#include <comphelper/classids.hxx> +#include <svl/cjkoptions.hxx> +#include <svl/visitem.hxx> + +#include <app.hrc> +#include <sdmod.hxx> +#include <View.hxx> +#include <drawdoc.hxx> + +#include <ViewShell.hxx> +#include <unomodel.hxx> +#include <undo/undomanager.hxx> +#include <undo/undofactory.hxx> +#include <OutlineView.hxx> +#include <ViewShellBase.hxx> +#include <sfx2/notebookbar/SfxNotebookBar.hxx> +#include <comphelper/lok.hxx> +#include <DrawViewShell.hxx> +#include <sdpage.hxx> + +using namespace sd; +#define ShellClass_DrawDocShell +#include <sdslots.hxx> + +SFX_IMPL_SUPERCLASS_INTERFACE(DrawDocShell, SfxObjectShell); + +void DrawDocShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterChildWindow(SvxSearchDialogWrapper::GetChildWindowId()); +} + +namespace sd { + +/** + * slotmaps and definitions of SFX + */ + +SFX_IMPL_OBJECTFACTORY( + DrawDocShell, + SvGlobalName(SO3_SIMPRESS_CLASSID), + "simpress" ) + +void DrawDocShell::Construct( bool bClipboard ) +{ + mbInDestruction = false; + SetSlotFilter(); // resets the filter + + mbOwnDocument = mpDoc == nullptr; + if( mbOwnDocument ) + mpDoc = new SdDrawDocument(meDocType, this); + + // The document has been created so we can call UpdateRefDevice() to set + // the document's ref device. + UpdateRefDevice(); + + SetBaseModel( new SdXImpressDocument( this, bClipboard ) ); + SetPool( &mpDoc->GetItemPool() ); + std::unique_ptr<sd::UndoManager> pUndoManager(new sd::UndoManager); + pUndoManager->SetDocShell(this); + mpUndoManager = std::move(pUndoManager); + + if (!utl::ConfigManager::IsFuzzing() + && officecfg::Office::Common::Undo::Steps::get() < 1) + { + mpUndoManager->EnableUndo(false); // tdf#108863 disable if 0 steps + } + mpDoc->SetSdrUndoManager( mpUndoManager.get() ); + mpDoc->SetSdrUndoFactory( new sd::UndoFactory ); + UpdateTablePointers(); + SetStyleFamily(SfxStyleFamily::Pseudo); +} + +DrawDocShell::DrawDocShell(SfxObjectCreateMode eMode, + bool bDataObject, + DocumentType eDocumentType) : + SfxObjectShell( eMode == SfxObjectCreateMode::INTERNAL ? SfxObjectCreateMode::EMBEDDED : eMode), + mpDoc(nullptr), + mpPrinter(nullptr), + mpViewShell(nullptr), + meDocType(eDocumentType), + mbSdDataObj(bDataObject), + mbOwnPrinter(false) +{ + Construct( eMode == SfxObjectCreateMode::INTERNAL ); +} + +DrawDocShell::DrawDocShell( SfxModelFlags nModelCreationFlags, bool bDataObject, DocumentType eDocumentType ) : + SfxObjectShell( nModelCreationFlags ), + mpDoc(nullptr), + mpPrinter(nullptr), + mpViewShell(nullptr), + meDocType(eDocumentType), + mbSdDataObj(bDataObject), + mbOwnPrinter(false) +{ + Construct( false ); +} + +DrawDocShell::DrawDocShell(SdDrawDocument* pDoc, SfxObjectCreateMode eMode, + bool bDataObject, + DocumentType eDocumentType) : + SfxObjectShell(eMode == SfxObjectCreateMode::INTERNAL ? SfxObjectCreateMode::EMBEDDED : eMode), + mpDoc(pDoc), + mpPrinter(nullptr), + mpViewShell(nullptr), + meDocType(eDocumentType), + mbSdDataObj(bDataObject), + mbOwnPrinter(false) +{ + Construct( eMode == SfxObjectCreateMode::INTERNAL ); +} + +DrawDocShell::~DrawDocShell() +{ + // Tell all listeners that the doc shell is about to be + // destroyed. This has been introduced for the PreviewRenderer to + // free its view (that uses the item poll of the doc shell) but + // may be useful in other places as well. + Broadcast(SfxHint(SfxHintId::Dying)); + + mbInDestruction = true; + + if (mpViewShell) + { + auto* pView = mpViewShell->GetView(); + if (pView) + { + auto & pSearchContext = pView->getSearchContext(); + pSearchContext.resetSearchFunction(); + } + } + + mpFontList.reset(); + + if( mpDoc ) + mpDoc->SetSdrUndoManager( nullptr ); + mpUndoManager.reset(); + + if (mbOwnPrinter) + mpPrinter.disposeAndClear(); + + if( mbOwnDocument ) + delete mpDoc; + + // that the navigator get informed about the disappearance of the document + SfxBoolItem aItem(SID_NAVIGATOR_INIT, true); + SfxViewFrame* pFrame = mpViewShell ? mpViewShell->GetFrame() : GetFrame(); + + if( !pFrame ) + pFrame = SfxViewFrame::GetFirst( this ); + + if( pFrame ) + { + pFrame->GetDispatcher()->ExecuteList( + SID_NAVIGATOR_INIT, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aItem }); + } +} + +void DrawDocShell::GetState(SfxItemSet &rSet) +{ + + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + + while ( nWhich ) + { + sal_uInt16 nSlotId = SfxItemPool::IsWhich(nWhich) + ? GetPool().GetSlotId(nWhich) + : nWhich; + + switch ( nSlotId ) + { + case SID_ATTR_CHAR_FONTLIST: + rSet.Put( SvxFontListItem( mpFontList.get(), nSlotId ) ); + break; + + case SID_SEARCH_ITEM: + { + rSet.Put( *SD_MOD()->GetSearchItem() ); + } + break; + + case SID_CLOSEDOC: + GetSlotState(SID_CLOSEDOC, SfxObjectShell::GetInterface(), &rSet); + break; + + case SID_SEARCH_OPTIONS: + { + SearchOptionFlags nOpt = SearchOptionFlags::SEARCH | + SearchOptionFlags::WHOLE_WORDS | + SearchOptionFlags::BACKWARDS | + SearchOptionFlags::REG_EXP | + SearchOptionFlags::EXACT | + SearchOptionFlags::SIMILARITY | + SearchOptionFlags::SELECTION; + + if (!IsReadOnly()) + { + nOpt |= SearchOptionFlags::REPLACE; + nOpt |= SearchOptionFlags::REPLACE_ALL; + } + + rSet.Put(SfxUInt16Item(nWhich, static_cast<sal_uInt16>(nOpt))); + } + break; + + case SID_VERSION: + { + GetSlotState( SID_VERSION, SfxObjectShell::GetInterface(), &rSet ); + } + break; + + case SID_CHINESE_CONVERSION: + case SID_HANGUL_HANJA_CONVERSION: + { + rSet.Put(SfxVisibilityItem(nWhich, SvtCJKOptions::IsAnyEnabled())); + } + break; + case SID_LANGUAGE_STATUS: + { + SdrObject* pObj = nullptr; + bool bLanguageFound = false; + OutlinerParaObject* pParaObj = nullptr; + LanguageType eLanguage( LANGUAGE_DONTKNOW ); + sal_uInt16 nCount = mpDoc->GetPageCount(); + for ( sal_uInt16 itPage = 0; itPage < nCount && !bLanguageFound; itPage++ ) + { + SdrObjListIter aListIter(mpDoc->GetPage(itPage), SdrIterMode::DeepWithGroups); + while ( aListIter.IsMore() && !bLanguageFound ) + { + pObj = aListIter.Next(); + if ( pObj ) + { + pParaObj = pObj->GetOutlinerParaObject(); + if ( pParaObj ) + { + SdrOutliner aOutliner(&mpDoc->GetPool(), OutlinerMode::TextObject); + aOutliner.SetText(*pParaObj); + eLanguage = aOutliner.GetLanguage(0, 0); + bLanguageFound = eLanguage != LANGUAGE_DONTKNOW; + } + } + } + } + + if ( eLanguage == LANGUAGE_DONTKNOW ) + { + eLanguage = mpDoc->GetLanguage( EE_CHAR_LANGUAGE ); + } + + OUString aLanguage = SvtLanguageTable::GetLanguageString(eLanguage); + if (comphelper::LibreOfficeKit::isActive()) + { + if (eLanguage == LANGUAGE_DONTKNOW) + { + aLanguage += ";-"; + } + else + { + aLanguage += ";" + LanguageTag(eLanguage).getBcp47(false); + } + } + rSet.Put(SfxStringItem(nWhich, aLanguage)); + } + break; + + case SID_NOTEBOOKBAR: + { + if (mpViewShell) + { + bool bImpress = mpDoc->GetDocumentType() == DocumentType::Impress; + bool bVisible = false; + if(bImpress) + { + bVisible = sfx2::SfxNotebookBar::StateMethod(mpViewShell->GetFrame()->GetBindings(), + u"modules/simpress/ui/"); + } + else + { + bVisible = sfx2::SfxNotebookBar::StateMethod(mpViewShell->GetFrame()->GetBindings(), + u"modules/sdraw/ui/"); + } + rSet.Put( SfxBoolItem( SID_NOTEBOOKBAR, bVisible ) ); + } + } + break; + + default: + break; + } + nWhich = aIter.NextWhich(); + } + + SfxViewFrame* pFrame = SfxViewFrame::Current(); + + if (pFrame) + { + if (rSet.GetItemState(SID_RELOAD) != SfxItemState::UNKNOWN) + { + pFrame->GetSlotState(SID_RELOAD, + pFrame->GetInterface(), &rSet); + } + } +} + +void DrawDocShell::Activate( bool bMDI) +{ + if (bMDI) + { + ApplySlotFilter(); + mpDoc->StartOnlineSpelling(); + } +} + +void DrawDocShell::Deactivate( bool ) +{ +} + +SfxUndoManager* DrawDocShell::GetUndoManager() +{ + return mpUndoManager.get(); +} + +void DrawDocShell::UpdateTablePointers() +{ + PutItem( SvxColorListItem( mpDoc->GetColorList(), SID_COLOR_TABLE ) ); + PutItem( SvxGradientListItem( mpDoc->GetGradientList(), SID_GRADIENT_LIST ) ); + PutItem( SvxHatchListItem( mpDoc->GetHatchList(), SID_HATCH_LIST ) ); + PutItem( SvxBitmapListItem( mpDoc->GetBitmapList(), SID_BITMAP_LIST ) ); + PutItem( SvxPatternListItem( mpDoc->GetPatternList(), SID_PATTERN_LIST ) ); + PutItem( SvxDashListItem( mpDoc->GetDashList(), SID_DASH_LIST ) ); + PutItem( SvxLineEndListItem( mpDoc->GetLineEndList(), SID_LINEEND_LIST ) ); + + UpdateFontList(); +} + +void DrawDocShell::CancelSearching() +{ + if (mpViewShell) + { + auto* pView = mpViewShell->GetView(); + if (pView) + { + auto & pSearchContext = pView->getSearchContext(); + pSearchContext.resetSearchFunction(); + } + } +} + +/** + * apply configured slot filters + */ +void DrawDocShell::ApplySlotFilter() const +{ + SfxViewShell* pTestViewShell = SfxViewShell::GetFirst(); + + while( pTestViewShell ) + { + if( pTestViewShell->GetObjectShell() + == this + && pTestViewShell->GetViewFrame() + && pTestViewShell->GetViewFrame()->GetDispatcher() ) + { + SfxDispatcher* pDispatcher = pTestViewShell->GetViewFrame()->GetDispatcher(); + + if( !mpFilterSIDs.empty() ) + pDispatcher->SetSlotFilter( mbFilterEnable ? SfxSlotFilterState::ENABLED : SfxSlotFilterState::DISABLED, mpFilterSIDs ); + else + pDispatcher->SetSlotFilter(); + + if( pDispatcher->GetBindings() ) + pDispatcher->GetBindings()->InvalidateAll( true ); + } + + pTestViewShell = SfxViewShell::GetNext( *pTestViewShell ); + } +} + +void DrawDocShell::SetModified( bool bSet /* = true */ ) +{ + SfxObjectShell::SetModified( bSet ); + + // change model state, too + // only set the changed state if modification is enabled + if( IsEnableSetModified() ) + { + if ( mpDoc ) + mpDoc->NbcSetChanged( bSet ); + + Broadcast( SfxHint( SfxHintId::DocChanged ) ); + } +} + +/** + * Callback for ExecuteSpellPopup() + */ +// ExecuteSpellPopup now handled by DrawDocShell. This is necessary +// to get hands on the outliner and the text object. +IMPL_LINK(DrawDocShell, OnlineSpellCallback, SpellCallbackInfo&, rInfo, void) +{ + SdrObject* pObj = nullptr; + SdrOutliner* pOutl = nullptr; + + if(GetViewShell()) + { + pOutl = GetViewShell()->GetView()->GetTextEditOutliner(); + pObj = GetViewShell()->GetView()->GetTextEditObject(); + } + + mpDoc->ImpOnlineSpellCallback(&rInfo, pObj, pOutl); +} + +void DrawDocShell::ClearUndoBuffer() +{ + // clear possible undo buffers of outliners + SfxViewFrame* pSfxViewFrame = SfxViewFrame::GetFirst(this, false); + while(pSfxViewFrame) + { + ViewShellBase* pViewShellBase = dynamic_cast< ViewShellBase* >( pSfxViewFrame->GetViewShell() ); + if( pViewShellBase ) + { + std::shared_ptr<ViewShell> pViewSh( pViewShellBase->GetMainViewShell() ); + if( pViewSh ) + { + ::sd::View* pView = pViewSh->GetView(); + if( pView ) + { + pView->SdrEndTextEdit(); + sd::OutlineView* pOutlView = dynamic_cast< sd::OutlineView* >( pView ); + if( pOutlView ) + { + pOutlView->GetOutliner().GetUndoManager().Clear(); + } + } + } + } + pSfxViewFrame = SfxViewFrame::GetNext(*pSfxViewFrame, this, false); + } + + SfxUndoManager* pUndoManager = GetUndoManager(); + if(pUndoManager && pUndoManager->GetUndoActionCount()) + pUndoManager->Clear(); +} + +std::vector<Color> DrawDocShell::GetThemeColors() +{ + auto pViewShell = dynamic_cast<sd::DrawViewShell*>(GetViewShell()); + if (!pViewShell) + { + return {}; + } + + SdPage* pPage = pViewShell->getCurrentPage(); + svx::Theme* pTheme = pPage->getSdrPageProperties().GetTheme(); + if (!pPage->IsMasterPage()) + { + pTheme = pPage->TRG_GetMasterPage().getSdrPageProperties().GetTheme(); + } + + if (!pTheme) + { + return {}; + } + + return pTheme->GetColors(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/docshell/grdocsh.cxx b/sd/source/ui/docshell/grdocsh.cxx new file mode 100644 index 000000000..f0f5af956 --- /dev/null +++ b/sd/source/ui/docshell/grdocsh.cxx @@ -0,0 +1,61 @@ +/* -*- 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 <svx/svxids.hrc> +#include <tools/globname.hxx> + +#include <comphelper/classids.hxx> + +#include <sfx2/objface.hxx> + +#include <GraphicDocShell.hxx> +#include <DrawDocShell.hxx> + +using namespace sd; +#define ShellClass_GraphicDocShell +#include <sdgslots.hxx> + +namespace sd +{ +SFX_IMPL_SUPERCLASS_INTERFACE(GraphicDocShell, SfxObjectShell) + +void GraphicDocShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterChildWindow(SID_SEARCH_DLG); +} + +SFX_IMPL_OBJECTFACTORY(GraphicDocShell, SvGlobalName(SO3_SDRAW_CLASSID_60), "sdraw") + +GraphicDocShell::GraphicDocShell(SfxObjectCreateMode eMode) + : DrawDocShell(eMode, /*bDataObject*/ true, DocumentType::Draw) +{ + SetStyleFamily(SfxStyleFamily::Para); +} + +GraphicDocShell::GraphicDocShell(SfxModelFlags nModelCreationFlags) + : DrawDocShell(nModelCreationFlags, /*bDataObject*/ false, DocumentType::Draw) +{ + SetStyleFamily(SfxStyleFamily::Para); +} + +GraphicDocShell::~GraphicDocShell() {} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/docshell/sdclient.cxx b/sd/source/ui/docshell/sdclient.cxx new file mode 100644 index 000000000..02521c257 --- /dev/null +++ b/sd/source/ui/docshell/sdclient.cxx @@ -0,0 +1,184 @@ +/* -*- 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 <Client.hxx> +#include <svx/svdoole2.hxx> +#include <tools/debug.hxx> + +#include <ViewShell.hxx> +#include <View.hxx> +#include <vcl/svapp.hxx> + +using namespace com::sun::star; + +namespace sd { + +Client::Client(SdrOle2Obj* pObj, ViewShell* pViewShell, vcl::Window* pWindow) : + SfxInPlaceClient(pViewShell->GetViewShell(), pWindow, pObj->GetAspect() ), + mpViewShell(pViewShell), + pSdrOle2Obj(pObj) +{ + SetObject( pObj->GetObjRef() ); + DBG_ASSERT( GetObject().is(), "No object connected!" ); +} + +Client::~Client() +{ +} + +/** + * If IP active, then we get this request to increase the visible section of the + * object. + */ +void Client::RequestNewObjectArea( ::tools::Rectangle& aObjRect ) +{ + ::sd::View* pView = mpViewShell->GetView(); + + bool bSizeProtect = false; + bool bPosProtect = false; + + const SdrMarkList& rMarkList = pView->GetMarkedObjectList(); + if (rMarkList.GetMarkCount() == 1) + { + SdrMark* pMark = rMarkList.GetMark(0); + SdrObject* pObj = pMark->GetMarkedSdrObj(); + + // no need to check for changes, this method is called only if the area really changed + bSizeProtect = pObj->IsResizeProtect(); + bPosProtect = pObj->IsMoveProtect(); + } + + ::tools::Rectangle aOldRect = GetObjArea(); + if ( bPosProtect ) + aObjRect.SetPos( aOldRect.TopLeft() ); + + if ( bSizeProtect ) + aObjRect.SetSize( aOldRect.GetSize() ); + + ::tools::Rectangle aWorkArea( pView->GetWorkArea() ); + if ( aWorkArea.Contains(aObjRect) || bPosProtect || aObjRect == aOldRect ) + return; + + // correct position + Point aPos = aObjRect.TopLeft(); + Size aSize = aObjRect.GetSize(); + Point aWorkAreaTL = aWorkArea.TopLeft(); + Point aWorkAreaBR = aWorkArea.BottomRight(); + + aPos.setX( std::max(aPos.X(), aWorkAreaTL.X()) ); + aPos.setX( std::min(aPos.X(), aWorkAreaBR.X()-aSize.Width()) ); + aPos.setY( std::max(aPos.Y(), aWorkAreaTL.Y()) ); + aPos.setY( std::min(aPos.Y(), aWorkAreaBR.Y()-aSize.Height()) ); + + aObjRect.SetPos(aPos); +} + +void Client::ObjectAreaChanged() +{ + ::sd::View* pView = mpViewShell->GetView(); + const SdrMarkList& rMarkList = pView->GetMarkedObjectList(); + if (rMarkList.GetMarkCount() != 1) + return; + + SdrMark* pMark = rMarkList.GetMark(0); + SdrOle2Obj* pObj = dynamic_cast< SdrOle2Obj* >(pMark->GetMarkedSdrObj()); + + if(!pObj) + return; + + // no need to check for changes, this method is called only if the area really changed + ::tools::Rectangle aNewRectangle(GetScaledObjArea()); + + // #i118524# if sheared/rotated, center to non-rotated LogicRect + pObj->setSuppressSetVisAreaSize(true); + + if(pObj->GetGeoStat().nRotationAngle || pObj->GetGeoStat().nShearAngle) + { + pObj->SetLogicRect( aNewRectangle ); + + const ::tools::Rectangle& rBoundRect = pObj->GetCurrentBoundRect(); + const Point aDelta(aNewRectangle.Center() - rBoundRect.Center()); + + aNewRectangle.Move(aDelta.X(), aDelta.Y()); + } + + pObj->SetLogicRect( aNewRectangle ); + pObj->setSuppressSetVisAreaSize(false); +} + +void Client::ViewChanged() +{ + if ( GetAspect() == embed::Aspects::MSOLE_ICON ) + { + // the iconified object seems not to need such a scaling handling + // since the replacement image and the size a completely controlled by the container + // TODO/LATER: when the icon exchange is implemented the scaling handling might be required again here + + pSdrOle2Obj->ActionChanged(); // draw needs it to remove lines in slide preview + return; + } + + //TODO/LATER: should we try to avoid the recalculation of the visareasize + //if we know that it didn't change? + if (!mpViewShell->GetActiveWindow()) + return; + + ::sd::View* pView = mpViewShell->GetView(); + if (!pView) + return; + + ::tools::Rectangle aLogicRect( pSdrOle2Obj->GetLogicRect() ); + Size aLogicSize( aLogicRect.GetWidth(), aLogicRect.GetHeight() ); + + if( pSdrOle2Obj->IsChart() ) + { + //charts never should be stretched see #i84323# for example + pSdrOle2Obj->SetLogicRect( ::tools::Rectangle( aLogicRect.TopLeft(), aLogicSize ) ); + pSdrOle2Obj->BroadcastObjectChange(); + return; + } + + // TODO/LEAN: maybe we can do this without requesting the VisualArea? + // working with the visual area might need running state, so the object may switch itself to this state + MapMode aMap100( MapUnit::Map100thMM ); + ::tools::Rectangle aVisArea; + Size aSize = pSdrOle2Obj->GetOrigObjSize( &aMap100 ); + + aVisArea.SetSize( aSize ); + Size aScaledSize( static_cast< ::tools::Long >( GetScaleWidth() * Fraction( aVisArea.GetWidth() ) ), + static_cast< ::tools::Long >( GetScaleHeight() * Fraction( aVisArea.GetHeight() ) ) ); + + // react to the change if the difference is bigger than one pixel + Size aPixelDiff = + Application::GetDefaultDevice()->LogicToPixel( + Size( aLogicRect.GetWidth() - aScaledSize.Width(), + aLogicRect.GetHeight() - aScaledSize.Height() ), + aMap100 ); + if( aPixelDiff.Width() || aPixelDiff.Height() ) + { + pSdrOle2Obj->SetLogicRect( ::tools::Rectangle( aLogicRect.TopLeft(), aScaledSize ) ); + pSdrOle2Obj->BroadcastObjectChange(); + } + else + pSdrOle2Obj->ActionChanged(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ChangeRequestQueue.cxx b/sd/source/ui/framework/configuration/ChangeRequestQueue.cxx new file mode 100644 index 000000000..0168c162b --- /dev/null +++ b/sd/source/ui/framework/configuration/ChangeRequestQueue.cxx @@ -0,0 +1,28 @@ +/* -*- 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 "ChangeRequestQueue.hxx" + +namespace sd::framework +{ +ChangeRequestQueue::ChangeRequestQueue() {} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ChangeRequestQueue.hxx b/sd/source/ui/framework/configuration/ChangeRequestQueue.hxx new file mode 100644 index 000000000..e60b5b527 --- /dev/null +++ b/sd/source/ui/framework/configuration/ChangeRequestQueue.hxx @@ -0,0 +1,48 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/uno/Reference.hxx> + +#include <queue> + +namespace com::sun::star::drawing::framework +{ +class XConfigurationChangeRequest; +} + +namespace sd::framework +{ +/** The ChangeRequestQueue stores the pending requests for changes to the + requested configuration. It is the task of the + ChangeRequestQueueProcessor to process these requests. +*/ +class ChangeRequestQueue + : public ::std::queue<css::uno::Reference<css::drawing::framework::XConfigurationChangeRequest>> +{ +public: + /** Create an empty queue. + */ + ChangeRequestQueue(); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ChangeRequestQueueProcessor.cxx b/sd/source/ui/framework/configuration/ChangeRequestQueueProcessor.cxx new file mode 100644 index 000000000..da633a540 --- /dev/null +++ b/sd/source/ui/framework/configuration/ChangeRequestQueueProcessor.cxx @@ -0,0 +1,180 @@ +/* -*- 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 "debugtrace.hxx" +#include "ChangeRequestQueueProcessor.hxx" +#include "ConfigurationTracer.hxx" + +#include "ConfigurationUpdater.hxx" + +#include <vcl/svapp.hxx> +#include <sal/log.hxx> +#include <com/sun/star/container/XNamed.hpp> +#include <com/sun/star/drawing/framework/XConfigurationChangeRequest.hpp> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace { + +#if DEBUG_SD_CONFIGURATION_TRACE + +void TraceRequest (const Reference<XConfigurationChangeRequest>& rxRequest) +{ + Reference<container::XNamed> xNamed (rxRequest, UNO_QUERY); + if (xNamed.is()) + SAL_INFO("sd.fwk", __func__ << ": " << xNamed->getName()); +} + +#endif + +} // end of anonymous namespace + +namespace sd::framework { + +ChangeRequestQueueProcessor::ChangeRequestQueueProcessor ( + const std::shared_ptr<ConfigurationUpdater>& rpConfigurationUpdater) + : mnUserEventId(nullptr), + mpConfigurationUpdater(rpConfigurationUpdater) +{ +} + +ChangeRequestQueueProcessor::~ChangeRequestQueueProcessor() +{ + if (mnUserEventId != nullptr) + Application::RemoveUserEvent(mnUserEventId); +} + +void ChangeRequestQueueProcessor::SetConfiguration ( + const Reference<XConfiguration>& rxConfiguration) +{ + ::osl::MutexGuard aGuard (maMutex); + + mxConfiguration = rxConfiguration; + StartProcessing(); +} + +void ChangeRequestQueueProcessor::AddRequest ( + const Reference<XConfigurationChangeRequest>& rxRequest) +{ + ::osl::MutexGuard aGuard (maMutex); + +#if DEBUG_SD_CONFIGURATION_TRACE + if (maQueue.empty()) + { + SAL_INFO("sd.fwk", __func__ << ": Adding requests to empty queue"); + ConfigurationTracer::TraceConfiguration( + mxConfiguration, "current configuration of queue processor"); + } + SAL_INFO("sd.fwk", __func__ << ": Adding request"); + TraceRequest(rxRequest); +#endif + + maQueue.push(rxRequest); + StartProcessing(); +} + +void ChangeRequestQueueProcessor::StartProcessing() +{ + ::osl::MutexGuard aGuard (maMutex); + + if (mnUserEventId == nullptr + && mxConfiguration.is() + && ! maQueue.empty()) + { + SAL_INFO("sd.fwk", __func__ << ": ChangeRequestQueueProcessor scheduling processing"); + mnUserEventId = Application::PostUserEvent( + LINK(this,ChangeRequestQueueProcessor,ProcessEvent)); + } +} + +IMPL_LINK_NOARG(ChangeRequestQueueProcessor, ProcessEvent, void*, void) +{ + ::osl::MutexGuard aGuard (maMutex); + + mnUserEventId = nullptr; + + ProcessOneEvent(); + + if ( ! maQueue.empty()) + { + // Schedule the processing of the next event. + StartProcessing(); + } +} + +void ChangeRequestQueueProcessor::ProcessOneEvent() +{ + ::osl::MutexGuard aGuard (maMutex); + + SAL_INFO("sd.fwk", __func__ << ": ProcessOneEvent"); + + if (!mxConfiguration.is() || maQueue.empty()) + return; + + // Get and remove the first entry from the queue. + Reference<XConfigurationChangeRequest> xRequest (maQueue.front()); + maQueue.pop(); + + // Execute the change request. + if (xRequest.is()) + { +#if DEBUG_SD_CONFIGURATION_TRACE + TraceRequest(xRequest); +#endif + xRequest->execute(mxConfiguration); + } + + if (!maQueue.empty()) + return; + + SAL_INFO("sd.fwk", __func__ << ": All requests are processed"); + // The queue is empty so tell the ConfigurationManager to update + // its state. + if (mpConfigurationUpdater != nullptr) + { +#if DEBUG_SD_CONFIGURATION_TRACE + ConfigurationTracer::TraceConfiguration ( + mxConfiguration, "updating to configuration"); +#endif + mpConfigurationUpdater->RequestUpdate(mxConfiguration); + } +} + +bool ChangeRequestQueueProcessor::IsEmpty() const +{ + return maQueue.empty(); +} + +void ChangeRequestQueueProcessor::ProcessUntilEmpty() +{ + while ( ! IsEmpty()) + ProcessOneEvent(); +} + +void ChangeRequestQueueProcessor::Clear() +{ + ::osl::MutexGuard aGuard (maMutex); + ChangeRequestQueue().swap(maQueue); +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ChangeRequestQueueProcessor.hxx b/sd/source/ui/framework/configuration/ChangeRequestQueueProcessor.hxx new file mode 100644 index 000000000..4afd5af76 --- /dev/null +++ b/sd/source/ui/framework/configuration/ChangeRequestQueueProcessor.hxx @@ -0,0 +1,126 @@ +/* -*- 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 . + */ + +#pragma once + +#include "ChangeRequestQueue.hxx" +#include <osl/mutex.hxx> + +#include <tools/link.hxx> + +#include <memory> + +namespace com::sun::star::drawing::framework +{ +class XConfiguration; +} +namespace com::sun::star::drawing::framework +{ +class XConfigurationChangeRequest; +} + +struct ImplSVEvent; + +namespace sd::framework +{ +class ConfigurationUpdater; + +/** The ChangeRequestQueueProcessor owns the ChangeRequestQueue and + processes the configuration change requests. + + When after processing one entry the queue is empty then the + XConfigurationController::update() method is called so that the changes + made to the local XConfiguration reference are reflected by the UI. + + Queue entries are processed asynchronously by calling PostUserEvent(). +*/ +class ChangeRequestQueueProcessor +{ +public: + /** The queue processor is created with a reference to an + ConfigurationController so that its UpdateConfiguration() method can + be called when the queue becomes empty. + */ + explicit ChangeRequestQueueProcessor(const std::shared_ptr<ConfigurationUpdater>& rpUpdater); + ~ChangeRequestQueueProcessor(); + + /** Sets the configuration who will be changed by subsequent change + requests. This method should be called only by the configuration + controller who owns the configuration. + */ + void SetConfiguration( + const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration); + + /** The given request is appended to the end of the queue and will + eventually be processed when all other entries in front of it have + been processed. + */ + void AddRequest( + const css::uno::Reference<css::drawing::framework::XConfigurationChangeRequest>& rxRequest); + + /** Returns </sal_True> when the queue is empty. + */ + bool IsEmpty() const; + + /** Process all events in the queue synchronously. + + <p>This method is typically called when the framework is shut down + to establish an empty configuration.</p> + */ + void ProcessUntilEmpty(); + + /** Process the first event in queue. + */ + void ProcessOneEvent(); + + /** Remove all events from the queue. + + <p>This method is typically called when the framework is shut down + to avoid the processing of still pending activation requests.</p> + */ + void Clear(); + +private: + mutable ::osl::Mutex maMutex; + + ChangeRequestQueue maQueue; + + /** The id returned by the last PostUserEvent() call. This id is stored + so that a pending user event can be removed when the queue processor + is destroyed. + */ + ImplSVEvent* mnUserEventId; + + css::uno::Reference<css::drawing::framework::XConfiguration> mxConfiguration; + + std::shared_ptr<ConfigurationUpdater> mpConfigurationUpdater; + + /** Initiate the processing of the entries in the queue. The actual + processing starts asynchronously. + */ + void StartProcessing(); + + /** Callback function for the PostUserEvent() call. + */ + DECL_LINK(ProcessEvent, void*, void); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/Configuration.cxx b/sd/source/ui/framework/configuration/Configuration.cxx new file mode 100644 index 000000000..7b813a42b --- /dev/null +++ b/sd/source/ui/framework/configuration/Configuration.cxx @@ -0,0 +1,311 @@ +/* -*- 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 <framework/Configuration.hxx> + +#include <framework/FrameworkHelper.hxx> + +#include <com/sun/star/drawing/framework/ConfigurationChangeEvent.hpp> +#include <com/sun/star/drawing/framework/XConfigurationControllerBroadcaster.hpp> +#include <comphelper/sequence.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; +using ::sd::framework::FrameworkHelper; + +namespace { +/** Use the XResourceId::compareTo() method to implement a compare operator + for STL containers. +*/ +class XResourceIdLess +{ +public: + bool operator () (const Reference<XResourceId>& rId1, const Reference<XResourceId>& rId2) const + { + return rId1->compareTo(rId2) == -1; + } +}; + +} // end of anonymous namespace + +namespace sd::framework { + +class Configuration::ResourceContainer + : public ::std::set<Reference<XResourceId>, XResourceIdLess> +{ +public: + ResourceContainer() {} +}; + +//===== Configuration ========================================================= + +Configuration::Configuration ( + const Reference<XConfigurationControllerBroadcaster>& rxBroadcaster, + bool bBroadcastRequestEvents) + : mpResourceContainer(new ResourceContainer()), + mxBroadcaster(rxBroadcaster), + mbBroadcastRequestEvents(bBroadcastRequestEvents) +{ +} + +Configuration::Configuration ( + const Reference<XConfigurationControllerBroadcaster>& rxBroadcaster, + bool bBroadcastRequestEvents, + const ResourceContainer& rResourceContainer) + : mpResourceContainer(new ResourceContainer(rResourceContainer)), + mxBroadcaster(rxBroadcaster), + mbBroadcastRequestEvents(bBroadcastRequestEvents) +{ +} + +Configuration::~Configuration() +{ +} + +void Configuration::disposing(std::unique_lock<std::mutex>&) +{ + mpResourceContainer->clear(); + mxBroadcaster = nullptr; +} + +//----- XConfiguration -------------------------------------------------------- + +void SAL_CALL Configuration::addResource (const Reference<XResourceId>& rxResourceId) +{ + ThrowIfDisposed(); + + if ( ! rxResourceId.is() || rxResourceId->getResourceURL().isEmpty()) + throw css::lang::IllegalArgumentException(); + + if (mpResourceContainer->insert(rxResourceId).second) + { + SAL_INFO("sd.fwk", __func__ << ": Configuration::addResource() " << + FrameworkHelper::ResourceIdToString(rxResourceId)); + PostEvent(rxResourceId, true); + } +} + +void SAL_CALL Configuration::removeResource (const Reference<XResourceId>& rxResourceId) +{ + ThrowIfDisposed(); + + if ( ! rxResourceId.is() || rxResourceId->getResourceURL().isEmpty()) + throw css::lang::IllegalArgumentException(); + + ResourceContainer::iterator iResource (mpResourceContainer->find(rxResourceId)); + if (iResource != mpResourceContainer->end()) + { + SAL_INFO("sd.fwk", __func__ << ": Configuration::removeResource() " << + FrameworkHelper::ResourceIdToString(rxResourceId)); + PostEvent(rxResourceId,false); + mpResourceContainer->erase(iResource); + } +} + +Sequence<Reference<XResourceId> > SAL_CALL Configuration::getResources ( + const Reference<XResourceId>& rxAnchorId, + const OUString& rsResourceURLPrefix, + AnchorBindingMode eMode) +{ + std::unique_lock aGuard (m_aMutex); + ThrowIfDisposed(); + + const bool bFilterResources (!rsResourceURLPrefix.isEmpty()); + + // Collect the matching resources in a vector. + ::std::vector<Reference<XResourceId> > aResources; + for (const auto& rxResource : *mpResourceContainer) + { + if ( ! rxResource->isBoundTo(rxAnchorId,eMode)) + continue; + + if (bFilterResources) + { + // Apply the given resource prefix as filter. + + // Make sure that the resource is bound directly to the anchor. + if (eMode != AnchorBindingMode_DIRECT + && ! rxResource->isBoundTo(rxAnchorId, AnchorBindingMode_DIRECT)) + { + continue; + } + + // Make sure that the resource URL matches the given prefix. + if ( ! rxResource->getResourceURL().match(rsResourceURLPrefix)) + { + continue; + } + } + + aResources.push_back(rxResource); + } + + return comphelper::containerToSequence(aResources); +} + +sal_Bool SAL_CALL Configuration::hasResource (const Reference<XResourceId>& rxResourceId) +{ + std::unique_lock aGuard (m_aMutex); + ThrowIfDisposed(); + + return rxResourceId.is() + && mpResourceContainer->find(rxResourceId) != mpResourceContainer->end(); +} + +//----- XCloneable ------------------------------------------------------------ + +Reference<util::XCloneable> SAL_CALL Configuration::createClone() +{ + std::unique_lock aGuard (m_aMutex); + ThrowIfDisposed(); + + return new Configuration( + mxBroadcaster, + mbBroadcastRequestEvents, + *mpResourceContainer); +} + +//----- XNamed ---------------------------------------------------------------- + +OUString SAL_CALL Configuration::getName() +{ + std::unique_lock aGuard (m_aMutex); + OUStringBuffer aString; + + if (m_bDisposed) + aString.append("DISPOSED "); + aString.append("Configuration["); + + ResourceContainer::const_iterator iResource; + for (iResource=mpResourceContainer->begin(); + iResource!=mpResourceContainer->end(); + ++iResource) + { + if (iResource != mpResourceContainer->begin()) + aString.append(", "); + aString.append(FrameworkHelper::ResourceIdToString(*iResource)); + } + aString.append("]"); + + return aString.makeStringAndClear(); +} + +void SAL_CALL Configuration::setName (const OUString&) +{ + // ignored. +} + +OUString Configuration::getImplementationName() +{ + return + "com.sun.star.comp.Draw.framework.configuration.Configuration"; +} + +sal_Bool Configuration::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence<OUString> Configuration::getSupportedServiceNames() +{ + return css::uno::Sequence<OUString>{ + "com.sun.star.drawing.framework.Configuration"}; +} + +void Configuration::PostEvent ( + const Reference<XResourceId>& rxResourceId, + const bool bActivation) +{ + OSL_ASSERT(rxResourceId.is()); + + if (!mxBroadcaster.is()) + return; + + ConfigurationChangeEvent aEvent; + aEvent.ResourceId = rxResourceId; + if (bActivation) + if (mbBroadcastRequestEvents) + aEvent.Type = FrameworkHelper::msResourceActivationRequestEvent; + else + aEvent.Type = FrameworkHelper::msResourceActivationEvent; + else + if (mbBroadcastRequestEvents) + aEvent.Type = FrameworkHelper::msResourceDeactivationRequestEvent; + else + aEvent.Type = FrameworkHelper::msResourceDeactivationEvent; + aEvent.Configuration = this; + + mxBroadcaster->notifyEvent(aEvent); +} + +void Configuration::ThrowIfDisposed() const +{ + if (m_bDisposed) + { + throw lang::DisposedException ("Configuration object has already been disposed", + const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this))); + } +} + +bool AreConfigurationsEquivalent ( + const Reference<XConfiguration>& rxConfiguration1, + const Reference<XConfiguration>& rxConfiguration2) +{ + if (rxConfiguration1.is() != rxConfiguration2.is()) + return false; + if ( ! rxConfiguration1.is() && ! rxConfiguration2.is()) + return true; + + // Get the lists of resources from the two given configurations. + const Sequence<Reference<XResourceId> > aResources1( + rxConfiguration1->getResources( + nullptr, OUString(), AnchorBindingMode_INDIRECT)); + const Sequence<Reference<XResourceId> > aResources2( + rxConfiguration2->getResources( + nullptr, OUString(), AnchorBindingMode_INDIRECT)); + + // When the number of resources differ then the configurations can not + // be equivalent. + // Comparison of the two lists of resource ids relies on their + // ordering. + return std::equal(aResources1.begin(), aResources1.end(), aResources2.begin(), aResources2.end(), + [](const Reference<XResourceId>& a, const Reference<XResourceId>& b) { + if (a.is() && b.is()) + return a->compareTo(b) == 0; + return a.is() == b.is(); + }); +} + +} // end of namespace sd::framework + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_framework_configuration_Configuration_get_implementation( + css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new sd::framework::Configuration(nullptr, false)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ConfigurationClassifier.cxx b/sd/source/ui/framework/configuration/ConfigurationClassifier.cxx new file mode 100644 index 000000000..99fc1297d --- /dev/null +++ b/sd/source/ui/framework/configuration/ConfigurationClassifier.cxx @@ -0,0 +1,167 @@ +/* -*- 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 "ConfigurationClassifier.hxx" + +#include <framework/FrameworkHelper.hxx> +#include <com/sun/star/drawing/framework/XConfiguration.hpp> +#include <sal/log.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace sd::framework { + +ConfigurationClassifier::ConfigurationClassifier ( + const Reference<XConfiguration>& rxConfiguration1, + const Reference<XConfiguration>& rxConfiguration2) + : mxConfiguration1(rxConfiguration1), + mxConfiguration2(rxConfiguration2) +{ +} + +bool ConfigurationClassifier::Partition() +{ + maC1minusC2.clear(); + maC2minusC1.clear(); + + PartitionResources( + mxConfiguration1->getResources(nullptr, OUString(), AnchorBindingMode_DIRECT), + mxConfiguration2->getResources(nullptr, OUString(), AnchorBindingMode_DIRECT)); + + return !maC1minusC2.empty() || !maC2minusC1.empty(); +} + +void ConfigurationClassifier::PartitionResources ( + const css::uno::Sequence<Reference<XResourceId> >& rS1, + const css::uno::Sequence<Reference<XResourceId> >& rS2) +{ + ResourceIdVector aC1minusC2; + ResourceIdVector aC2minusC1; + ResourceIdVector aC1andC2; + + // Classify the resources in the configurations that are not bound to + // other resources. + ClassifyResources( + rS1, + rS2, + aC1minusC2, + aC2minusC1, + aC1andC2); + + SAL_INFO("sd.fwk", __func__ << ": copying resource ids to C1-C2"); + CopyResources(aC1minusC2, mxConfiguration1, maC1minusC2); + SAL_INFO("sd.fwk", __func__ << ": copying resource ids to C2-C1"); + CopyResources(aC2minusC1, mxConfiguration2, maC2minusC1); + + // Process the unique resources that belong to both configurations. + for (const auto& rxResource : aC1andC2) + { + PartitionResources( + mxConfiguration1->getResources(rxResource, OUString(), AnchorBindingMode_DIRECT), + mxConfiguration2->getResources(rxResource, OUString(), AnchorBindingMode_DIRECT)); + } +} + +void ConfigurationClassifier::ClassifyResources ( + const css::uno::Sequence<Reference<XResourceId> >& rS1, + const css::uno::Sequence<Reference<XResourceId> >& rS2, + ResourceIdVector& rS1minusS2, + ResourceIdVector& rS2minusS1, + ResourceIdVector& rS1andS2) +{ + // Find all elements in rS1 and place them in rS1minusS2 or rS1andS2 + // depending on whether they are in rS2 or not. + for (const Reference<XResourceId>& rA1 : rS1) + { + bool bFound = std::any_of(rS2.begin(), rS2.end(), + [&rA1](const Reference<XResourceId>& rA2) { + return rA1->getResourceURL() == rA2->getResourceURL(); }); + + if (bFound) + rS1andS2.push_back(rA1); + else + rS1minusS2.push_back(rA1); + } + + // Find all elements in rS2 that are not in rS1. The elements that are + // in both rS1 and rS2 have been handled above and are therefore ignored + // here. + for (const Reference<XResourceId>& rA2 : rS2) + { + bool bFound = std::any_of(rS1.begin(), rS1.end(), + [&rA2](const Reference<XResourceId>& rA1) { + return rA2->getResourceURL() == rA1->getResourceURL(); }); + + if ( ! bFound) + rS2minusS1.push_back(rA2); + } +} + +void ConfigurationClassifier::CopyResources ( + const ResourceIdVector& rSource, + const Reference<XConfiguration>& rxConfiguration, + ResourceIdVector& rTarget) +{ + // Copy all resources bound to the ones in aC1minusC2Unique to rC1minusC2. + for (const auto& rxResource : rSource) + { + const Sequence<Reference<XResourceId> > aBoundResources ( + rxConfiguration->getResources( + rxResource, + OUString(), + AnchorBindingMode_INDIRECT)); + const sal_Int32 nL (aBoundResources.getLength()); + + rTarget.reserve(rTarget.size() + 1 + nL); + rTarget.push_back(rxResource); + + SAL_INFO("sd.fwk", __func__ << ": copying " << + FrameworkHelper::ResourceIdToString(rxResource)); + + for (const Reference<XResourceId>& rBoundResource : aBoundResources) + { + rTarget.push_back(rBoundResource); + SAL_INFO("sd.fwk", __func__ << ": copying " << + FrameworkHelper::ResourceIdToString(rBoundResource)); + } + } +} + +#if DEBUG_SD_CONFIGURATION_TRACE + +void ConfigurationClassifier::TraceResourceIdVector ( + const char* pMessage, + const ResourceIdVector& rResources) +{ + + SAL_INFO("sd.fwk", __func__ << ": " << pMessage); + for (const auto& rxResource : rResources) + { + OUString sResource (FrameworkHelper::ResourceIdToString(rxResource)); + SAL_INFO("sd.fwk", __func__ << ": " << sResource); + } +} + +#endif + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ConfigurationClassifier.hxx b/sd/source/ui/framework/configuration/ConfigurationClassifier.hxx new file mode 100644 index 000000000..e9384713b --- /dev/null +++ b/sd/source/ui/framework/configuration/ConfigurationClassifier.hxx @@ -0,0 +1,165 @@ +/* -*- 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 . + */ + +#pragma once + +#include "debugtrace.hxx" +#include <com/sun/star/uno/Reference.hxx> + +#include <vector> + +namespace com::sun::star::drawing::framework +{ +class XConfiguration; +} +namespace com::sun::star::drawing::framework +{ +class XResourceId; +} + +namespace sd::framework +{ +/** A ConfigurationClassifier object compares two configurations of + resources and gives access to the differences. It is used mainly when + changes to the current configuration have been requested and the various + resource controllers have to be supplied with the set of resources that + are to be activated or deactivated. +*/ +class ConfigurationClassifier +{ +public: + /** Create a new ConfigurationClassifier object that will compare the + two given configurations. + */ + ConfigurationClassifier( + const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration1, + const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration2); + + /** Calculate three lists of resource ids. These contain the resources + that belong to one configuration but not the other, or that belong + to both configurations. + @return + When the two configurations differ then return <TRUE/>. When + they are equivalent then return <FALSE/>. + */ + bool Partition(); + + typedef ::std::vector<css::uno::Reference<css::drawing::framework::XResourceId>> + ResourceIdVector; + + /** Return the resources that belong to the configuration given as + rxConfiguration1 to the constructor but that do not belong to + rxConfiguration2. + @return + A reference to the, possibly empty, list of resources is + returned. This reference remains valid as long as the called + ConfigurationClassifier object stays alive. + */ + const ResourceIdVector& GetC1minusC2() const { return maC1minusC2; } + + /** Return the resources that belong to the configuration given as + rxConfiguration2 to the constructor but that do not belong to + rxConfiguration1. + @return + A reference to the, possibly empty, list of resources is + returned. This reference remains valid as long as the called + ConfigurationClassifier object stays alive. + */ + const ResourceIdVector& GetC2minusC1() const { return maC2minusC1; } + +#if DEBUG_SD_CONFIGURATION_TRACE + + /** Return the resources that belong to both the configurations that + where given to the constructor. + @return + A reference to the, possibly empty, list of resources is + returned. This reference remains valid as long as the called + ConfigurationClassifier object stays alive. + */ + const ResourceIdVector& GetC1andC2() const { return maC1andC2; } + + static void TraceResourceIdVector(const char* pMessage, const ResourceIdVector& rResources); + +#endif + +private: + css::uno::Reference<css::drawing::framework::XConfiguration> mxConfiguration1; + css::uno::Reference<css::drawing::framework::XConfiguration> mxConfiguration2; + + /** After the call to Classify() this vector holds all elements from + mxConfiguration1 that are not in mxConfiguration2. + */ + ResourceIdVector maC1minusC2; + + /** After the call to Classify() this vector holds all elements from + mxConfiguration2 that are not in mxConfiguration1. + */ + ResourceIdVector maC2minusC1; + + /** Put all the elements in the two given sequences of resource ids and + copy them into one of the resource id result vectors maC1minusC2, + maC2minusC1, and maC1andC2. This is done by using only the resource + URLs for classification. Therefore this method calls itself + recursively. + @param rS1 + One sequence of XResourceId objects. + @param rS2 + Another sequence of XResourceId objects. + */ + void PartitionResources( + const css::uno::Sequence<css::uno::Reference<css::drawing::framework::XResourceId>>& rS1, + const css::uno::Sequence<css::uno::Reference<css::drawing::framework::XResourceId>>& rS2); + + /** Compare the given sequences of resource ids and put their elements + in one of three vectors depending on whether an element belongs to + both sequences or to one but not the other. Note that only the + resource URLs of the XResourceId objects are used for the + classification. + @param rS1 + One sequence of XResourceId objects. + @param rS2 + Another sequence of XResourceId objects. + */ + static void ClassifyResources( + const css::uno::Sequence<css::uno::Reference<css::drawing::framework::XResourceId>>& rS1, + const css::uno::Sequence<css::uno::Reference<css::drawing::framework::XResourceId>>& rS2, + ResourceIdVector& rS1minusS2, ResourceIdVector& rS2minusS1, ResourceIdVector& rS1andS2); + + /** Copy the resources given in rSource to the list of resources + specified by rTarget. Resources bound to the ones in rSource, + either directly or indirectly, are copied as well. + @param rSource + All resources and the ones bound to them, either directly or + indirectly, are copied. + @param rxConfiguration + This configuration is used to determine the resources bound to + the ones in rSource. + @param rTarget + This list is filled with resources from rSource and the ones + bound to them. + */ + static void CopyResources( + const ResourceIdVector& rSource, + const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration, + ResourceIdVector& rTarget); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ConfigurationController.cxx b/sd/source/ui/framework/configuration/ConfigurationController.cxx new file mode 100644 index 000000000..3fc95adb9 --- /dev/null +++ b/sd/source/ui/framework/configuration/ConfigurationController.cxx @@ -0,0 +1,541 @@ +/* -*- 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 <framework/ConfigurationController.hxx> +#include <framework/Configuration.hxx> +#include <framework/FrameworkHelper.hxx> +#include "ConfigurationUpdater.hxx" +#include "ConfigurationControllerBroadcaster.hxx" +#include "ConfigurationTracer.hxx" +#include "GenericConfigurationChangeRequest.hxx" +#include "ConfigurationControllerResourceManager.hxx" +#include "ResourceFactoryManager.hxx" +#include "UpdateRequest.hxx" +#include "ChangeRequestQueueProcessor.hxx" +#include "ConfigurationClassifier.hxx" +#include <com/sun/star/drawing/framework/XControllerManager.hpp> +#include <com/sun/star/frame/XController.hpp> + +#include <sal/log.hxx> +#include <osl/mutex.hxx> +#include <vcl/svapp.hxx> +#include <memory> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; +using ::sd::framework::FrameworkHelper; + +namespace sd::framework { + +//----- ConfigurationController::Implementation ------------------------------- + +class ConfigurationController::Implementation +{ +public: + Implementation ( + ConfigurationController& rController, + const Reference<frame::XController>& rxController); + + Reference<XControllerManager> mxControllerManager; + + /** The Broadcaster class implements storing and calling of listeners. + */ + std::shared_ptr<ConfigurationControllerBroadcaster> mpBroadcaster; + + /** The requested configuration which is modified (asynchronously) by + calls to requestResourceActivation() and + requestResourceDeactivation(). The mpConfigurationUpdater makes the + current configuration reflect the content of this one. + */ + css::uno::Reference<css::drawing::framework::XConfiguration> mxRequestedConfiguration; + + std::shared_ptr<ResourceFactoryManager> mpResourceFactoryContainer; + + std::shared_ptr<ConfigurationControllerResourceManager> mpResourceManager; + + std::shared_ptr<ConfigurationUpdater> mpConfigurationUpdater; + + /** The queue processor owns the queue of configuration change request + objects and processes the objects. + */ + std::unique_ptr<ChangeRequestQueueProcessor> mpQueueProcessor; + + std::shared_ptr<ConfigurationUpdaterLock> mpConfigurationUpdaterLock; + + sal_Int32 mnLockCount; +}; + +//===== ConfigurationController::Lock ========================================= + +ConfigurationController::Lock::Lock (const Reference<XConfigurationController>& rxController) + : mxController(rxController) +{ + OSL_ASSERT(mxController.is()); + + if (mxController.is()) + mxController->lock(); +} + +ConfigurationController::Lock::~Lock() +{ + if (mxController.is()) + mxController->unlock(); +} + +//===== ConfigurationController =============================================== + +ConfigurationController::ConfigurationController() noexcept + : ConfigurationControllerInterfaceBase(m_aMutex) + , mbIsDisposed(false) +{ +} + +ConfigurationController::~ConfigurationController() noexcept +{ +} + +void SAL_CALL ConfigurationController::disposing() +{ + if (mpImplementation == nullptr) + return; + + SAL_INFO("sd.fwk", __func__ << ": ConfigurationController::disposing"); + SAL_INFO("sd.fwk", __func__ << ": requesting empty configuration"); + // To destroy all resources an empty configuration is requested and then, + // synchronously, all resulting requests are processed. + mpImplementation->mpQueueProcessor->Clear(); + restoreConfiguration(new Configuration(this,false)); + mpImplementation->mpQueueProcessor->ProcessUntilEmpty(); + SAL_INFO("sd.fwk", __func__ << ": all requests processed"); + + // Now that all resources have been deactivated, mark the controller as + // disposed. + mbIsDisposed = true; + + // Release the listeners. + lang::EventObject aEvent; + aEvent.Source = uno::Reference<uno::XInterface>(static_cast<cppu::OWeakObject*>(this)); + + { + const SolarMutexGuard aSolarGuard; + mpImplementation->mpBroadcaster->DisposeAndClear(); + } + + mpImplementation->mpQueueProcessor.reset(); + mpImplementation->mxRequestedConfiguration = nullptr; + mpImplementation.reset(); +} + +void ConfigurationController::ProcessEvent() +{ + if (mpImplementation != nullptr) + { + OSL_ASSERT(mpImplementation->mpQueueProcessor != nullptr); + + mpImplementation->mpQueueProcessor->ProcessOneEvent(); + } +} + +void ConfigurationController::RequestSynchronousUpdate() +{ + if (mpImplementation == nullptr) + return; + if (mpImplementation->mpQueueProcessor == nullptr) + return; + mpImplementation->mpQueueProcessor->ProcessUntilEmpty(); +} + +//----- XConfigurationControllerBroadcaster ----------------------------------- + +void SAL_CALL ConfigurationController::addConfigurationChangeListener ( + const Reference<XConfigurationChangeListener>& rxListener, + const OUString& rsEventType, + const Any& rUserData) +{ + ::osl::MutexGuard aGuard (m_aMutex); + + ThrowIfDisposed(); + OSL_ASSERT(mpImplementation != nullptr); + mpImplementation->mpBroadcaster->AddListener(rxListener, rsEventType, rUserData); +} + +void SAL_CALL ConfigurationController::removeConfigurationChangeListener ( + const Reference<XConfigurationChangeListener>& rxListener) +{ + ::osl::MutexGuard aGuard (m_aMutex); + + ThrowIfDisposed(); + mpImplementation->mpBroadcaster->RemoveListener(rxListener); +} + +void SAL_CALL ConfigurationController::notifyEvent ( + const ConfigurationChangeEvent& rEvent) +{ + ThrowIfDisposed(); + mpImplementation->mpBroadcaster->NotifyListeners(rEvent); +} + +//----- XConfigurationController ---------------------------------------------- + +void SAL_CALL ConfigurationController::lock() +{ + OSL_ASSERT(mpImplementation != nullptr); + OSL_ASSERT(mpImplementation->mpConfigurationUpdater != nullptr); + + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + + ++mpImplementation->mnLockCount; + if (mpImplementation->mpConfigurationUpdaterLock == nullptr) + mpImplementation->mpConfigurationUpdaterLock + = mpImplementation->mpConfigurationUpdater->GetLock(); +} + +void SAL_CALL ConfigurationController::unlock() +{ + ::osl::MutexGuard aGuard (m_aMutex); + + // Allow unlocking while the ConfigurationController is being disposed + // (but not when that is done and the controller is disposed.) + if (rBHelper.bDisposed) + ThrowIfDisposed(); + + OSL_ASSERT(mpImplementation->mnLockCount>0); + --mpImplementation->mnLockCount; + if (mpImplementation->mnLockCount == 0) + mpImplementation->mpConfigurationUpdaterLock.reset(); +} + +void SAL_CALL ConfigurationController::requestResourceActivation ( + const Reference<XResourceId>& rxResourceId, + ResourceActivationMode eMode) +{ + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + + // Check whether we are being disposed. This is handled differently + // then being completely disposed because the first thing disposing() + // does is to deactivate all remaining resources. This is done via + // regular methods which must not throw DisposedExceptions. Therefore + // we just return silently during that stage. + if (rBHelper.bInDispose) + { + SAL_INFO("sd.fwk", __func__ << ": ConfigurationController::requestResourceActivation(): ignoring " << + FrameworkHelper::ResourceIdToString(rxResourceId)); + return; + } + + SAL_INFO("sd.fwk", __func__ << ": ConfigurationController::requestResourceActivation() " << + FrameworkHelper::ResourceIdToString(rxResourceId)); + + if (!rxResourceId.is()) + return; + + if (eMode == ResourceActivationMode_REPLACE) + { + // Get a list of the matching resources and create deactivation + // requests for them. + const Sequence<Reference<XResourceId> > aResourceList ( + mpImplementation->mxRequestedConfiguration->getResources( + rxResourceId->getAnchor(), + rxResourceId->getResourceTypePrefix(), + AnchorBindingMode_DIRECT)); + + for (const auto& rResource : aResourceList) + { + // Do not request the deactivation of the resource for which + // this method was called. Doing it would not change the + // outcome but would result in unnecessary work. + if (rxResourceId->compareTo(rResource) == 0) + continue; + + // Request the deactivation of a resource and all resources + // linked to it. + requestResourceDeactivation(rResource); + } + } + + Reference<XConfigurationChangeRequest> xRequest( + new GenericConfigurationChangeRequest( + rxResourceId, + GenericConfigurationChangeRequest::Activation)); + postChangeRequest(xRequest); +} + +void SAL_CALL ConfigurationController::requestResourceDeactivation ( + const Reference<XResourceId>& rxResourceId) +{ + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + + SAL_INFO("sd.fwk", __func__ << ": ConfigurationController::requestResourceDeactivation() " << + FrameworkHelper::ResourceIdToString(rxResourceId)); + + if (!rxResourceId.is()) + return; + + // Request deactivation of all resources linked to the specified one + // as well. + const Sequence<Reference<XResourceId> > aLinkedResources ( + mpImplementation->mxRequestedConfiguration->getResources( + rxResourceId, + OUString(), + AnchorBindingMode_DIRECT)); + for (const auto& rLinkedResource : aLinkedResources) + { + // We do not add deactivation requests directly but call this + // method recursively, so that when one time there are resources + // linked to linked resources, these are handled correctly, too. + requestResourceDeactivation(rLinkedResource); + } + + // Add a deactivation request for the specified resource. + Reference<XConfigurationChangeRequest> xRequest( + new GenericConfigurationChangeRequest( + rxResourceId, + GenericConfigurationChangeRequest::Deactivation)); + postChangeRequest(xRequest); +} + +Reference<XResource> SAL_CALL ConfigurationController::getResource ( + const Reference<XResourceId>& rxResourceId) +{ + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + + ConfigurationControllerResourceManager::ResourceDescriptor aDescriptor ( + mpImplementation->mpResourceManager->GetResource(rxResourceId)); + return aDescriptor.mxResource; +} + +void SAL_CALL ConfigurationController::update() +{ + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + + if (mpImplementation->mpQueueProcessor->IsEmpty()) + { + // The queue is empty. Add another request that does nothing but + // asynchronously trigger a request for an update. + mpImplementation->mpQueueProcessor->AddRequest(new UpdateRequest()); + } + else + { + // The queue is not empty, so we rely on the queue processor to + // request an update automatically when the queue becomes empty. + } +} + +sal_Bool SAL_CALL ConfigurationController::hasPendingRequests() +{ + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + + return ! mpImplementation->mpQueueProcessor->IsEmpty(); +} + +void SAL_CALL ConfigurationController::postChangeRequest ( + const Reference<XConfigurationChangeRequest>& rxRequest) +{ + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + + mpImplementation->mpQueueProcessor->AddRequest(rxRequest); +} + +Reference<XConfiguration> SAL_CALL ConfigurationController::getRequestedConfiguration() +{ + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + + if (mpImplementation->mxRequestedConfiguration.is()) + return Reference<XConfiguration>( + mpImplementation->mxRequestedConfiguration->createClone(), UNO_QUERY); + else + return Reference<XConfiguration>(); +} + +Reference<XConfiguration> SAL_CALL ConfigurationController::getCurrentConfiguration() +{ + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + + Reference<XConfiguration> xCurrentConfiguration( + mpImplementation->mpConfigurationUpdater->GetCurrentConfiguration()); + if (xCurrentConfiguration.is()) + return Reference<XConfiguration>(xCurrentConfiguration->createClone(), UNO_QUERY); + else + return Reference<XConfiguration>(); +} + +/** The given configuration is restored by generating the appropriate set of + activation and deactivation requests. +*/ +void SAL_CALL ConfigurationController::restoreConfiguration ( + const Reference<XConfiguration>& rxNewConfiguration) +{ + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + + // We will probably be making a couple of activation and deactivation + // requests so lock the configuration controller and let it later update + // all changes at once. + std::shared_ptr<ConfigurationUpdaterLock> pLock ( + mpImplementation->mpConfigurationUpdater->GetLock()); + + // Get lists of resources that are to be activated or deactivated. + Reference<XConfiguration> xCurrentConfiguration (mpImplementation->mxRequestedConfiguration); +#if OSL_DEBUG_LEVEL >=1 + SAL_INFO("sd.fwk", __func__ << ": ConfigurationController::restoreConfiguration("); + ConfigurationTracer::TraceConfiguration(rxNewConfiguration, "requested configuration"); + ConfigurationTracer::TraceConfiguration(xCurrentConfiguration, "current configuration"); +#endif + ConfigurationClassifier aClassifier (rxNewConfiguration, xCurrentConfiguration); + aClassifier.Partition(); +#if DEBUG_SD_CONFIGURATION_TRACE + aClassifier.TraceResourceIdVector( + "requested but not current resources:\n", aClassifier.GetC1minusC2()); + aClassifier.TraceResourceIdVector( + "current but not requested resources:\n", aClassifier.GetC2minusC1()); + aClassifier.TraceResourceIdVector( + "requested and current resources:\n", aClassifier.GetC1andC2()); +#endif + + // Request the deactivation of resources that are not requested in the + // new configuration. + const ConfigurationClassifier::ResourceIdVector& rResourcesToDeactivate ( + aClassifier.GetC2minusC1()); + for (const auto& rxResource : rResourcesToDeactivate) + { + requestResourceDeactivation(rxResource); + } + + // Request the activation of resources that are requested in the + // new configuration but are not part of the current configuration. + const ConfigurationClassifier::ResourceIdVector& rResourcesToActivate ( + aClassifier.GetC1minusC2()); + for (const auto& rxResource : rResourcesToActivate) + { + requestResourceActivation(rxResource, ResourceActivationMode_ADD); + } + + pLock.reset(); +} + +//----- XResourceFactoryManager ----------------------------------------------- + +void SAL_CALL ConfigurationController::addResourceFactory( + const OUString& sResourceURL, + const Reference<XResourceFactory>& rxResourceFactory) +{ + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + mpImplementation->mpResourceFactoryContainer->AddFactory(sResourceURL, rxResourceFactory); +} + +void SAL_CALL ConfigurationController::removeResourceFactoryForURL( + const OUString& sResourceURL) +{ + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + mpImplementation->mpResourceFactoryContainer->RemoveFactoryForURL(sResourceURL); +} + +void SAL_CALL ConfigurationController::removeResourceFactoryForReference( + const Reference<XResourceFactory>& rxResourceFactory) +{ + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + mpImplementation->mpResourceFactoryContainer->RemoveFactoryForReference(rxResourceFactory); +} + +Reference<XResourceFactory> SAL_CALL ConfigurationController::getResourceFactory ( + const OUString& sResourceURL) +{ + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + + return mpImplementation->mpResourceFactoryContainer->GetFactory(sResourceURL); +} + +//----- XInitialization ------------------------------------------------------- + +void SAL_CALL ConfigurationController::initialize (const Sequence<Any>& aArguments) +{ + ::osl::MutexGuard aGuard (m_aMutex); + + if (aArguments.getLength() == 1) + { + const SolarMutexGuard aSolarGuard; + + mpImplementation.reset(new Implementation( + *this, + Reference<frame::XController>(aArguments[0], UNO_QUERY_THROW))); + } +} + +void ConfigurationController::ThrowIfDisposed () const +{ + if (mbIsDisposed) + { + throw lang::DisposedException ("ConfigurationController object has already been disposed", + const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this))); + } + + if (mpImplementation == nullptr) + { + OSL_ASSERT(mpImplementation != nullptr); + throw RuntimeException("ConfigurationController not initialized", + const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this))); + } +} + +//===== ConfigurationController::Implementation =============================== + +ConfigurationController::Implementation::Implementation ( + ConfigurationController& rController, + const Reference<frame::XController>& rxController) + : mxControllerManager(rxController, UNO_QUERY_THROW), + mpBroadcaster(std::make_shared<ConfigurationControllerBroadcaster>(&rController)), + mxRequestedConfiguration(new Configuration(&rController, true)), + mpResourceFactoryContainer(std::make_shared<ResourceFactoryManager>(mxControllerManager)), + mpResourceManager( + std::make_shared<ConfigurationControllerResourceManager>(mpResourceFactoryContainer,mpBroadcaster)), + mpConfigurationUpdater( + std::make_shared<ConfigurationUpdater>(mpBroadcaster, mpResourceManager,mxControllerManager)), + mpQueueProcessor(new ChangeRequestQueueProcessor(mpConfigurationUpdater)), + mnLockCount(0) +{ + mpQueueProcessor->SetConfiguration(mxRequestedConfiguration); +} + +} // end of namespace sd::framework + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_framework_configuration_ConfigurationController_get_implementation( + css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new sd::framework::ConfigurationController()); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ConfigurationControllerBroadcaster.cxx b/sd/source/ui/framework/configuration/ConfigurationControllerBroadcaster.cxx new file mode 100644 index 000000000..5d9f22255 --- /dev/null +++ b/sd/source/ui/framework/configuration/ConfigurationControllerBroadcaster.cxx @@ -0,0 +1,192 @@ +/* -*- 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 "ConfigurationControllerBroadcaster.hxx" +#include <com/sun/star/drawing/framework/XConfigurationChangeListener.hpp> +#include <com/sun/star/drawing/framework/XConfigurationController.hpp> +#include <com/sun/star/drawing/framework/XResource.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <tools/diagnose_ex.h> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace sd::framework { + +ConfigurationControllerBroadcaster::ConfigurationControllerBroadcaster ( + const Reference<XConfigurationController>& rxController) + : mxConfigurationController(rxController) +{ +} + +void ConfigurationControllerBroadcaster::AddListener( + const Reference<XConfigurationChangeListener>& rxListener, + const OUString& rsEventType, + const Any& rUserData) +{ + if ( ! rxListener.is()) + throw lang::IllegalArgumentException("invalid listener", + mxConfigurationController, + 0); + + maListenerMap.try_emplace(rsEventType); + + ListenerDescriptor aDescriptor; + aDescriptor.mxListener = rxListener; + aDescriptor.maUserData = rUserData; + maListenerMap[rsEventType].push_back(aDescriptor); +} + +void ConfigurationControllerBroadcaster::RemoveListener( + const Reference<XConfigurationChangeListener>& rxListener) +{ + if ( ! rxListener.is()) + throw lang::IllegalArgumentException("invalid listener", + mxConfigurationController, + 0); + + ListenerList::iterator iList; + for (auto& rMap : maListenerMap) + { + iList = std::find_if(rMap.second.begin(), rMap.second.end(), + [&rxListener](const ListenerDescriptor& rList) { return rList.mxListener == rxListener; }); + if (iList != rMap.second.end()) + rMap.second.erase(iList); + } +} + +void ConfigurationControllerBroadcaster::NotifyListeners ( + const ListenerList& rList, + const ConfigurationChangeEvent& rEvent) +{ + // Create a local copy of the event in which the user data is modified + // for every listener. + ConfigurationChangeEvent aEvent (rEvent); + + for (const auto& rListener : rList) + { + try + { + aEvent.UserData = rListener.maUserData; + rListener.mxListener->notifyConfigurationChange(aEvent); + } + catch (const lang::DisposedException& rException) + { + // When the exception comes from the listener itself then + // unregister it. + if (rException.Context == rListener.mxListener) + RemoveListener(rListener.mxListener); + } + catch (const RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } + } +} + +void ConfigurationControllerBroadcaster::NotifyListeners (const ConfigurationChangeEvent& rEvent) +{ + // Notify the specialized listeners. + ListenerMap::const_iterator iMap (maListenerMap.find(rEvent.Type)); + if (iMap != maListenerMap.end()) + { + // Create a local list of the listeners to avoid problems with + // concurrent changes and to be able to remove disposed listeners. + ListenerList aList (iMap->second.begin(), iMap->second.end()); + NotifyListeners(aList,rEvent); + } + + // Notify the universal listeners. + iMap = maListenerMap.find(OUString()); + if (iMap != maListenerMap.end()) + { + // Create a local list of the listeners to avoid problems with + // concurrent changes and to be able to remove disposed listeners. + ListenerList aList (iMap->second.begin(), iMap->second.end()); + NotifyListeners(aList,rEvent); + } +} + +void ConfigurationControllerBroadcaster::NotifyListeners ( + const OUString& rsEventType, + const Reference<XResourceId>& rxResourceId, + const Reference<XResource>& rxResourceObject) +{ + ConfigurationChangeEvent aEvent; + aEvent.Type = rsEventType; + aEvent.ResourceId = rxResourceId; + aEvent.ResourceObject = rxResourceObject; + try + { + NotifyListeners(aEvent); + } + catch (const lang::DisposedException&) + { + } +} + +void ConfigurationControllerBroadcaster::DisposeAndClear() +{ + lang::EventObject aEvent; + aEvent.Source = mxConfigurationController; + while (!maListenerMap.empty()) + { + ListenerMap::iterator iMap (maListenerMap.begin()); + if (iMap == maListenerMap.end()) + break; + + // When the first vector is empty then remove it from the map. + if (iMap->second.empty()) + { + maListenerMap.erase(iMap); + continue; + } + else + { + Reference<XConfigurationChangeListener> xListener ( + iMap->second.front().mxListener ); + if (xListener.is()) + { + // Tell the listener that the configuration controller is + // being disposed and remove the listener (for all event + // types). + try + { + RemoveListener(xListener); + xListener->disposing(aEvent); + } + catch (const RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } + } + else + { + // Remove just this reference to the listener. + iMap->second.erase(iMap->second.begin()); + } + } + } +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ConfigurationControllerBroadcaster.hxx b/sd/source/ui/framework/configuration/ConfigurationControllerBroadcaster.hxx new file mode 100644 index 000000000..5dfd6843d --- /dev/null +++ b/sd/source/ui/framework/configuration/ConfigurationControllerBroadcaster.hxx @@ -0,0 +1,138 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/uno/Reference.hxx> + +#include <unordered_map> +#include <vector> + +namespace com::sun::star::drawing::framework { class XConfigurationChangeListener; } +namespace com::sun::star::drawing::framework { class XConfigurationController; } +namespace com::sun::star::drawing::framework { class XResource; } +namespace com::sun::star::drawing::framework { class XResourceId; } +namespace com::sun::star::drawing::framework { struct ConfigurationChangeEvent; } + +namespace sd::framework { + +/** This class manages the set of XConfigurationChangeListeners and + calls them when the ConfigurationController wants to broadcast an + event. + + For every registered combination of listener and event type a user data + object is stored. This user data object is then given to the listener + whenever it is called for an event. With this the listener can use + a switch statement to handle different event types. +*/ +class ConfigurationControllerBroadcaster +{ +public: + /** The given controller is used as origin of thrown exceptions. + */ + explicit ConfigurationControllerBroadcaster ( + const css::uno::Reference< + css::drawing::framework::XConfigurationController>& rxController); + + /** Add a listener for one type of event. When one listener is + interested in more than one event type this method has to be called + once for every event type. Alternatively it can register as + universal listener that will be called for all event types. + @param rxListener + A valid reference to a listener. + @param rsEventType + The type of event that the listener will be called for. The + empty string is a special value in that the listener will be + called for all event types. + @param rUserData + This object is passed to the listener whenever it is called for + the specified event type. For different event types different + user data objects can be provided. + @throws IllegalArgumentException + when an empty listener reference is given. + */ + void AddListener( + const css::uno::Reference< + css::drawing::framework::XConfigurationChangeListener>& rxListener, + const OUString& rsEventType, + const css::uno::Any& rUserData); + + /** Remove all references to the given listener. When one listener has + been registered for more than one type of event then it is removed + for all of them. + @param rxListener + A valid reference to a listener. + @throws IllegalArgumentException + when an empty listener reference is given. + */ + void RemoveListener( + const css::uno::Reference< + css::drawing::framework::XConfigurationChangeListener>& rxListener); + + /** Broadcast the given event to all listeners that have been registered + for its type of event as well as all universal listeners. + + When calling a listener results in a DisposedException being thrown + the listener is unregistered automatically. + */ + void NotifyListeners ( + const css::drawing::framework::ConfigurationChangeEvent& rEvent); + + /** This convenience variant of NotifyListeners create the event from + the given arguments. + */ + void NotifyListeners ( + const OUString& rsEventType, + const css::uno::Reference<css::drawing::framework::XResourceId>& rxResourceId, + const css::uno::Reference<css::drawing::framework::XResource>& rxResourceObject); + + /** Call all listeners and inform them that the + ConfigurationController is being disposed. When this method returns + the list of registered listeners is empty. Further calls to + RemoveListener() are not necessary but do not result in an error. + */ + void DisposeAndClear(); + +private: + css::uno::Reference<css::drawing::framework::XConfigurationController> mxConfigurationController; + class ListenerDescriptor + { + public: + css::uno::Reference<css::drawing::framework::XConfigurationChangeListener> mxListener; + css::uno::Any maUserData; + }; + typedef std::vector<ListenerDescriptor> ListenerList; + typedef std::unordered_map + <OUString, + ListenerList> ListenerMap; + ListenerMap maListenerMap; + + /** Broadcast the given event to all listeners in the given list. + + When calling a listener results in a DisposedException being thrown + the listener is unregistered automatically. + */ + void NotifyListeners ( + const ListenerList& rList, + const css::drawing::framework::ConfigurationChangeEvent& rEvent); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ConfigurationControllerResourceManager.cxx b/sd/source/ui/framework/configuration/ConfigurationControllerResourceManager.cxx new file mode 100644 index 000000000..904011d7d --- /dev/null +++ b/sd/source/ui/framework/configuration/ConfigurationControllerResourceManager.cxx @@ -0,0 +1,303 @@ +/* -*- 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 "ConfigurationControllerResourceManager.hxx" +#include "ConfigurationControllerBroadcaster.hxx" +#include "ResourceFactoryManager.hxx" +#include <framework/FrameworkHelper.hxx> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/drawing/framework/XConfiguration.hpp> +#include <com/sun/star/drawing/framework/XResourceFactory.hpp> +#include <tools/diagnose_ex.h> +#include <sal/log.hxx> +#include <algorithm> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace sd::framework { + +//===== ConfigurationControllerResourceManager ================================ + +ConfigurationControllerResourceManager::ConfigurationControllerResourceManager ( + const std::shared_ptr<ResourceFactoryManager>& rpResourceFactoryContainer, + const std::shared_ptr<ConfigurationControllerBroadcaster>& rpBroadcaster) + : maResourceMap(ResourceComparator()), + mpResourceFactoryContainer(rpResourceFactoryContainer), + mpBroadcaster(rpBroadcaster) +{ +} + +ConfigurationControllerResourceManager::~ConfigurationControllerResourceManager() +{ +} + +ConfigurationControllerResourceManager::ResourceDescriptor + ConfigurationControllerResourceManager::GetResource ( + const Reference<XResourceId>& rxResourceId) +{ + ::osl::MutexGuard aGuard (maMutex); + ResourceMap::const_iterator iResource (maResourceMap.find(rxResourceId)); + if (iResource != maResourceMap.end()) + return iResource->second; + else + return ResourceDescriptor(); +} + +void ConfigurationControllerResourceManager::ActivateResources ( + const ::std::vector<Reference<XResourceId> >& rResources, + const Reference<XConfiguration>& rxConfiguration) +{ + ::osl::MutexGuard aGuard (maMutex); + // Iterate in normal order over the resources that are to be + // activated so that resources on which others depend are activated + // before the depending resources are activated. + for (const Reference<XResourceId>& xResource : rResources) + ActivateResource(xResource, rxConfiguration); +} + +void ConfigurationControllerResourceManager::DeactivateResources ( + const ::std::vector<Reference<XResourceId> >& rResources, + const Reference<XConfiguration>& rxConfiguration) +{ + ::osl::MutexGuard aGuard (maMutex); + // Iterate in reverse order over the resources that are to be + // deactivated so that resources on which others depend are deactivated + // only when the depending resources have already been deactivated. + ::std::for_each( + rResources.rbegin(), + rResources.rend(), + [&] (Reference<XResourceId> const& xResource) { + return DeactivateResource(xResource, rxConfiguration); + } ); +} + +/* In this method we do following steps. + 1. Get the factory with which the resource will be created. + 2. Create the resource. + 3. Add the resource to the URL->Object map of the configuration + controller. + 4. Add the resource id to the current configuration. + 5. Notify listeners. +*/ +void ConfigurationControllerResourceManager::ActivateResource ( + const Reference<XResourceId>& rxResourceId, + const Reference<XConfiguration>& rxConfiguration) +{ + if ( ! rxResourceId.is()) + { + OSL_ASSERT(rxResourceId.is()); + return; + } + + SAL_INFO("sd.fwk", __func__ << ": activating resource " << + FrameworkHelper::ResourceIdToString(rxResourceId)); + + // 1. Get the factory. + const OUString sResourceURL (rxResourceId->getResourceURL()); + Reference<XResourceFactory> xFactory (mpResourceFactoryContainer->GetFactory(sResourceURL)); + if ( ! xFactory.is()) + { + SAL_INFO("sd.fwk", __func__ << ": no factory found for " << sResourceURL); + return; + } + + try + { + // 2. Create the resource. + Reference<XResource> xResource; + try + { + xResource = xFactory->createResource(rxResourceId); + } + catch (lang::DisposedException&) + { + // The factory is disposed and can be removed from the list + // of registered factories. + mpResourceFactoryContainer->RemoveFactoryForReference(xFactory); + } + catch (Exception&) {} + + if (xResource.is()) + { + SAL_INFO("sd.fwk", __func__ << ": successfully created"); + // 3. Add resource to URL->Object map. + AddResource(xResource, xFactory); + + // 4. Add resource id to current configuration. + rxConfiguration->addResource(rxResourceId); + + // 5. Notify the new resource to listeners of the ConfigurationController. + mpBroadcaster->NotifyListeners( + FrameworkHelper::msResourceActivationEvent, + rxResourceId, + xResource); + } + else + { + SAL_INFO("sd.fwk", __func__ << ": resource creation failed"); + } + } + catch (RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } +} + +/* In this method we do following steps. + 1. Remove the resource from the URL->Object map of the configuration + controller. + 2. Notify listeners that deactivation has started. + 3. Remove the resource id from the current configuration. + 4. Release the resource. + 5. Notify listeners about that deactivation is completed. +*/ +void ConfigurationControllerResourceManager::DeactivateResource ( + const Reference<XResourceId>& rxResourceId, + const Reference<XConfiguration>& rxConfiguration) +{ + if ( ! rxResourceId.is()) + return; + +#if OSL_DEBUG_LEVEL >= 1 + bool bSuccess (false); +#endif + try + { + // 1. Remove resource from URL->Object map. + ResourceDescriptor aDescriptor (RemoveResource(rxResourceId)); + + if (aDescriptor.mxResource.is() && aDescriptor.mxResourceFactory.is()) + { + // 2. Notify listeners that the resource is being deactivated. + mpBroadcaster->NotifyListeners( + FrameworkHelper::msResourceDeactivationEvent, + rxResourceId, + aDescriptor.mxResource); + + // 3. Remove resource id from current configuration. + rxConfiguration->removeResource(rxResourceId); + + // 4. Release the resource. + try + { + aDescriptor.mxResourceFactory->releaseResource(aDescriptor.mxResource); + } + catch (const lang::DisposedException& rException) + { + if ( ! rException.Context.is() + || rException.Context == aDescriptor.mxResourceFactory) + { + // The factory is disposed and can be removed from the + // list of registered factories. + mpResourceFactoryContainer->RemoveFactoryForReference( + aDescriptor.mxResourceFactory); + } + } + +#if OSL_DEBUG_LEVEL >= 1 + bSuccess = true; +#endif + } + } + catch (RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } + + // 5. Notify listeners that the resource is being deactivated. + mpBroadcaster->NotifyListeners( + FrameworkHelper::msResourceDeactivationEndEvent, + rxResourceId, + nullptr); + +#if OSL_DEBUG_LEVEL >= 1 + if (bSuccess) + SAL_INFO("sd.fwk", __func__ << ": successfully deactivated " << + FrameworkHelper::ResourceIdToString(rxResourceId)); + else + SAL_INFO("sd.fwk", __func__ << ": activating resource " << + FrameworkHelper::ResourceIdToString(rxResourceId) + << " failed"); +#endif +} + +void ConfigurationControllerResourceManager::AddResource ( + const Reference<XResource>& rxResource, + const Reference<XResourceFactory>& rxFactory) +{ + if ( ! rxResource.is()) + { + OSL_ASSERT(rxResource.is()); + return; + } + + // Add the resource to the resource container. + ResourceDescriptor aDescriptor; + aDescriptor.mxResource = rxResource; + aDescriptor.mxResourceFactory = rxFactory; + maResourceMap[rxResource->getResourceId()] = aDescriptor; + +#if OSL_DEBUG_LEVEL >= 2 + SAL_INFO("sd.fwk", __func__ << ": ConfigurationControllerResourceManager::AddResource(): added " << + FrameworkHelper::ResourceIdToString(rxResource->getResourceId()) << + " -> " << rxResource.get()); +#endif +} + +ConfigurationControllerResourceManager::ResourceDescriptor + ConfigurationControllerResourceManager::RemoveResource ( + const Reference<XResourceId>& rxResourceId) +{ + ResourceDescriptor aDescriptor; + + ResourceMap::iterator iResource (maResourceMap.find(rxResourceId)); + if (iResource != maResourceMap.end()) + { +#if OSL_DEBUG_LEVEL >= 2 + SAL_INFO("sd.fwk", __func__ << ": ConfigurationControllerResourceManager::RemoveResource(): removing " << + FrameworkHelper::ResourceIdToString(rxResourceId) << + " -> " << &iResource); +#endif + + aDescriptor = iResource->second; + maResourceMap.erase(rxResourceId); + } + + return aDescriptor; +} + +//===== ConfigurationControllerResourceManager::ResourceComparator ============ + +bool ConfigurationControllerResourceManager::ResourceComparator::operator() ( + const Reference<XResourceId>& rxId1, + const Reference<XResourceId>& rxId2) const +{ + if (rxId1.is() && rxId2.is()) + return rxId1->compareTo(rxId2)<0; + else if (rxId1.is()) + return true; + else + return false; +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ConfigurationControllerResourceManager.hxx b/sd/source/ui/framework/configuration/ConfigurationControllerResourceManager.hxx new file mode 100644 index 000000000..f3a3d6d76 --- /dev/null +++ b/sd/source/ui/framework/configuration/ConfigurationControllerResourceManager.hxx @@ -0,0 +1,141 @@ +/* -*- 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 . + */ + +#pragma once + +#include <osl/mutex.hxx> + +#include <com/sun/star/uno/Reference.hxx> + +#include <map> +#include <memory> +#include <vector> + +namespace com::sun::star::drawing::framework { class XConfiguration; } +namespace com::sun::star::drawing::framework { class XResourceFactory; } +namespace com::sun::star::drawing::framework { class XResource; } +namespace com::sun::star::drawing::framework { class XResourceId; } + +namespace sd::framework { + +class ConfigurationControllerBroadcaster; +class ResourceFactoryManager; + +/** Manage the set of active resources. Activate and deactivate resources. +*/ +class ConfigurationControllerResourceManager +{ +public: + /** For every active resource both the resource itself as well as its + creating factory are remembered, so that on deactivation, the + resource can be deactivated by this factory. + */ + class ResourceDescriptor + { + public: + css::uno::Reference<css::drawing::framework::XResource> mxResource; + css::uno::Reference<css::drawing::framework::XResourceFactory> mxResourceFactory; + }; + + /** A new ResourceManager object is created with the resource factory + container for creating resources and the event broadcaster for + notifying ConfigurationChangeListeners of activated or deactivated + resources. + */ + ConfigurationControllerResourceManager ( + const std::shared_ptr<ResourceFactoryManager>& rpResourceFactoryContainer, + const std::shared_ptr<ConfigurationControllerBroadcaster>& rpBroadcaster); + + ~ConfigurationControllerResourceManager(); + + /// Forbid copy construction and copy assignment + ConfigurationControllerResourceManager(const ConfigurationControllerResourceManager&) = delete; + ConfigurationControllerResourceManager& operator=(const ConfigurationControllerResourceManager&) = delete; + + /** Activate all the resources that are specified by resource ids in + rResources. The resource ids of activated resources are added to + the given configuration. Activated resources are notified to all + interested ConfigurationChangeListeners. + */ + void ActivateResources ( + const ::std::vector< + css::uno::Reference<css::drawing::framework::XResourceId> >& rResources, + const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration); + + /** Deactivate all the resources that are specified by resource ids in + rResources. The resource ids of deactivated resources are removed + from the given configuration. Activated resources are notified to all + interested ConfigurationChangeListeners. + */ + void DeactivateResources ( + const ::std::vector< + css::uno::Reference<css::drawing::framework::XResourceId> >& rResources, + const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration); + + /** Return the descriptor for the specified resource. + @return + When there is no active resource for the given resource id then + an empty descriptor is returned. + */ + ResourceDescriptor GetResource ( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxResourceId); + +private: + osl::Mutex maMutex; + + class ResourceComparator + { + public: + bool operator() ( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxId1, + const css::uno::Reference<css::drawing::framework::XResourceId>& rxId2) const; + }; + + typedef ::std::map< + css::uno::Reference<css::drawing::framework::XResourceId>, + ResourceDescriptor, + ResourceComparator> ResourceMap; + ResourceMap maResourceMap; + + std::shared_ptr<ResourceFactoryManager> mpResourceFactoryContainer; + + /** This broadcaster is used to notify the activation and deactivation + of resources. + */ + std::shared_ptr<ConfigurationControllerBroadcaster> mpBroadcaster; + + void ActivateResource ( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxResourceId, + const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration); + + void DeactivateResource ( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxResourceId, + const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration); + + void AddResource ( + const css::uno::Reference<css::drawing::framework::XResource>& rxResource, + const css::uno::Reference<css::drawing::framework::XResourceFactory>& rxFactory); + + ResourceDescriptor RemoveResource ( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxResourceId); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ConfigurationTracer.cxx b/sd/source/ui/framework/configuration/ConfigurationTracer.cxx new file mode 100644 index 000000000..00ddd5ff1 --- /dev/null +++ b/sd/source/ui/framework/configuration/ConfigurationTracer.cxx @@ -0,0 +1,73 @@ +/* -*- 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 "ConfigurationTracer.hxx" + +#include <com/sun/star/drawing/framework/XConfiguration.hpp> +#include <sal/log.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace sd::framework { + +void ConfigurationTracer::TraceConfiguration ( + const Reference<XConfiguration>& rxConfiguration, + const char* pMessage) +{ +#if OSL_DEBUG_LEVEL >=1 + SAL_INFO("sd.ui","" << pMessage << " at " << rxConfiguration.get() << " {"); + if (rxConfiguration.is()) + { + TraceBoundResources(rxConfiguration, nullptr, 0); + } + else + { + SAL_INFO("sd.ui"," empty"); + } + SAL_INFO("sd.ui","}"); +#else + (void)rxConfiguration; + (void)pMessage; +#endif +} + +#if OSL_DEBUG_LEVEL >=1 +void ConfigurationTracer::TraceBoundResources ( + const Reference<XConfiguration>& rxConfiguration, + const Reference<XResourceId>& rxResourceId, + const int nIndentation) +{ + const Sequence<Reference<XResourceId> > aResourceList ( + rxConfiguration->getResources(rxResourceId, OUString(), AnchorBindingMode_DIRECT)); + static const OUStringLiteral sIndentation (u" "); + for (Reference<XResourceId> const & resourceId : aResourceList) + { + OUString sLine (resourceId->getResourceURL()); + for (int i=0; i<nIndentation; ++i) + sLine = sIndentation + sLine; + SAL_INFO("sd.ui", "" << sLine); + TraceBoundResources(rxConfiguration, resourceId, nIndentation+1); + } +} +#endif + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ConfigurationTracer.hxx b/sd/source/ui/framework/configuration/ConfigurationTracer.hxx new file mode 100644 index 000000000..337fae569 --- /dev/null +++ b/sd/source/ui/framework/configuration/ConfigurationTracer.hxx @@ -0,0 +1,58 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/types.h> + +namespace com::sun::star::drawing::framework +{ +class XConfiguration; +} +namespace com::sun::star::drawing::framework +{ +class XResourceId; +} +namespace com::sun::star::uno +{ +template <typename> class Reference; +} + +namespace sd::framework +{ +/** Print debug information about configurations to the standard error + output channel. +*/ +class ConfigurationTracer +{ +public: + static void TraceConfiguration( + const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration, + const char* pMessage); +#if OSL_DEBUG_LEVEL >= 1 + static void TraceBoundResources( + const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration, + const css::uno::Reference<css::drawing::framework::XResourceId>& rxResourceId, + const int nIndentation); +#endif +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ConfigurationUpdater.cxx b/sd/source/ui/framework/configuration/ConfigurationUpdater.cxx new file mode 100644 index 000000000..96ac74186 --- /dev/null +++ b/sd/source/ui/framework/configuration/ConfigurationUpdater.cxx @@ -0,0 +1,376 @@ +/* -*- 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 "ConfigurationUpdater.hxx" +#include "ConfigurationTracer.hxx" +#include "ConfigurationClassifier.hxx" +#include "ConfigurationControllerBroadcaster.hxx" +#include "ConfigurationControllerResourceManager.hxx" +#include <framework/Configuration.hxx> +#include <framework/FrameworkHelper.hxx> + +#include <com/sun/star/drawing/framework/XControllerManager.hpp> +#include <comphelper/scopeguard.hxx> +#include <tools/diagnose_ex.h> +#include <sal/log.hxx> + + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; +using ::sd::framework::FrameworkHelper; +using ::std::vector; + +namespace { +const sal_Int32 snShortTimeout (100); +const sal_Int32 snNormalTimeout (1000); +const sal_Int32 snLongTimeout (10000); +const sal_Int32 snShortTimeoutCountThreshold (1); +const sal_Int32 snNormalTimeoutCountThreshold (5); +} + +namespace sd::framework { + +//===== ConfigurationUpdaterLock ============================================== + +class ConfigurationUpdaterLock +{ +public: + explicit ConfigurationUpdaterLock (ConfigurationUpdater& rUpdater) + : mrUpdater(rUpdater) { mrUpdater.LockUpdates(); } + ~ConfigurationUpdaterLock() { mrUpdater.UnlockUpdates(); } +private: + ConfigurationUpdater& mrUpdater; +}; + +//===== ConfigurationUpdater ================================================== + +ConfigurationUpdater::ConfigurationUpdater ( + const std::shared_ptr<ConfigurationControllerBroadcaster>& rpBroadcaster, + const std::shared_ptr<ConfigurationControllerResourceManager>& rpResourceManager, + const Reference<XControllerManager>& rxControllerManager) + : mpBroadcaster(rpBroadcaster), + mxCurrentConfiguration(Reference<XConfiguration>(new Configuration(nullptr, false))), + mbUpdatePending(false), + mbUpdateBeingProcessed(false), + mnLockCount(0), + maUpdateTimer("sd::ConfigurationUpdater maUpdateTimer"), + mnFailedUpdateCount(0), + mpResourceManager(rpResourceManager) +{ + // Prepare the timer that is started when after an update the current + // and the requested configuration differ. With the timer we try + // updates until the two configurations are the same. + maUpdateTimer.SetTimeout(snNormalTimeout); + maUpdateTimer.SetInvokeHandler(LINK(this,ConfigurationUpdater,TimeoutHandler)); + mxControllerManager = rxControllerManager; +} + +ConfigurationUpdater::~ConfigurationUpdater() +{ + maUpdateTimer.Stop(); +} + +void ConfigurationUpdater::RequestUpdate ( + const Reference<XConfiguration>& rxRequestedConfiguration) +{ + mxRequestedConfiguration = rxRequestedConfiguration; + + // Find out whether we really can update the configuration. + if (IsUpdatePossible()) + { + SAL_INFO("sd.fwk", __func__ << ": UpdateConfiguration start"); + + // Call UpdateConfiguration while that is possible and while someone + // set mbUpdatePending to true in the middle of it. + do + { + UpdateConfiguration(); + } + while (mbUpdatePending && IsUpdatePossible()); + } + else + { + mbUpdatePending = true; + SAL_INFO("sd.fwk", __func__ << ": scheduling update for later"); + } +} + +bool ConfigurationUpdater::IsUpdatePossible() const +{ + return ! mbUpdateBeingProcessed + && mxControllerManager.is() + && mnLockCount==0 + && mxRequestedConfiguration.is() + && mxCurrentConfiguration.is(); +} + +void ConfigurationUpdater::UpdateConfiguration() +{ + SAL_INFO("sd.fwk", __func__ << ": UpdateConfiguration update"); + SetUpdateBeingProcessed(true); + comphelper::ScopeGuard aScopeGuard ( + [this] () { return this->SetUpdateBeingProcessed(false); }); + + try + { + mbUpdatePending = false; + + CleanRequestedConfiguration(); + ConfigurationClassifier aClassifier(mxRequestedConfiguration, mxCurrentConfiguration); + if (aClassifier.Partition()) + { +#if DEBUG_SD_CONFIGURATION_TRACE + SAL_INFO("sd.fwk", __func__ << ": ConfigurationUpdater::UpdateConfiguration("); + ConfigurationTracer::TraceConfiguration( + mxRequestedConfiguration, "requested configuration"); + ConfigurationTracer::TraceConfiguration( + mxCurrentConfiguration, "current configuration"); +#endif + // Notify the beginning of the update. + ConfigurationChangeEvent aEvent; + aEvent.Type = FrameworkHelper::msConfigurationUpdateStartEvent; + aEvent.Configuration = mxRequestedConfiguration; + mpBroadcaster->NotifyListeners(aEvent); + + // Do the actual update. All exceptions are caught and ignored, + // so that the end of the update is notified always. + try + { + if (mnLockCount == 0) + UpdateCore(aClassifier); + } + catch(const RuntimeException&) + { + } + + // Notify the end of the update. + aEvent.Type = FrameworkHelper::msConfigurationUpdateEndEvent; + mpBroadcaster->NotifyListeners(aEvent); + + CheckUpdateSuccess(); + } + else + { + SAL_INFO("sd.fwk", __func__ << ": nothing to do"); +#if DEBUG_SD_CONFIGURATION_TRACE + ConfigurationTracer::TraceConfiguration( + mxRequestedConfiguration, "requested configuration"); + ConfigurationTracer::TraceConfiguration( + mxCurrentConfiguration, "current configuration"); +#endif + } + } + catch(const RuntimeException &) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } + + SAL_INFO("sd.fwk", __func__ << ": ConfigurationUpdater::UpdateConfiguration)"); + SAL_INFO("sd.fwk", __func__ << ": UpdateConfiguration end"); +} + +void ConfigurationUpdater::CleanRequestedConfiguration() +{ + if (!mxControllerManager.is()) + return; + + // Request the deactivation of pure anchors that have no child. + vector<Reference<XResourceId> > aResourcesToDeactivate; + CheckPureAnchors(mxRequestedConfiguration, aResourcesToDeactivate); + if (!aResourcesToDeactivate.empty()) + { + Reference<XConfigurationController> xCC ( + mxControllerManager->getConfigurationController()); + for (const auto& rxId : aResourcesToDeactivate) + if (rxId.is()) + xCC->requestResourceDeactivation(rxId); + } +} + +void ConfigurationUpdater::CheckUpdateSuccess() +{ + // When the two configurations differ then start the timer to call + // another update later. + if ( ! AreConfigurationsEquivalent(mxCurrentConfiguration, mxRequestedConfiguration)) + { + if (mnFailedUpdateCount <= snShortTimeoutCountThreshold) + maUpdateTimer.SetTimeout(snShortTimeout); + else if (mnFailedUpdateCount < snNormalTimeoutCountThreshold) + maUpdateTimer.SetTimeout(snNormalTimeout); + else + maUpdateTimer.SetTimeout(snLongTimeout); + ++mnFailedUpdateCount; + maUpdateTimer.Start(); + } + else + { + // Update was successful. Reset the failed update count. + mnFailedUpdateCount = 0; + } +} + +void ConfigurationUpdater::UpdateCore (const ConfigurationClassifier& rClassifier) +{ + try + { +#if DEBUG_SD_CONFIGURATION_TRACE + rClassifier.TraceResourceIdVector( + "requested but not current resources:", rClassifier.GetC1minusC2()); + rClassifier.TraceResourceIdVector( + "current but not requested resources:", rClassifier.GetC2minusC1()); + rClassifier.TraceResourceIdVector( + "requested and current resources:", rClassifier.GetC1andC2()); +#endif + + // Updating of the sub controllers is done in two steps. In the + // first the sub controllers typically shut down resources that are + // not requested anymore. In the second the sub controllers + // typically set up resources that have been newly requested. + mpResourceManager->DeactivateResources(rClassifier.GetC2minusC1(), mxCurrentConfiguration); + mpResourceManager->ActivateResources(rClassifier.GetC1minusC2(), mxCurrentConfiguration); + +#if DEBUG_SD_CONFIGURATION_TRACE + SAL_INFO("sd.fwk", __func__ << ": ConfigurationController::UpdateConfiguration)"); + ConfigurationTracer::TraceConfiguration( + mxRequestedConfiguration, "requested configuration"); + ConfigurationTracer::TraceConfiguration( + mxCurrentConfiguration, "current configuration"); +#endif + + // Deactivate pure anchors that have no child. + vector<Reference<XResourceId> > aResourcesToDeactivate; + CheckPureAnchors(mxCurrentConfiguration, aResourcesToDeactivate); + if (!aResourcesToDeactivate.empty()) + mpResourceManager->DeactivateResources(aResourcesToDeactivate, mxCurrentConfiguration); + } + catch(const RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } +} + +void ConfigurationUpdater::CheckPureAnchors ( + const Reference<XConfiguration>& rxConfiguration, + vector<Reference<XResourceId> >& rResourcesToDeactivate) +{ + if ( ! rxConfiguration.is()) + return; + + // Get a list of all resources in the configuration. + Sequence<Reference<XResourceId> > aResources( + rxConfiguration->getResources( + nullptr, OUString(), AnchorBindingMode_INDIRECT)); + auto aResourcesRange = asNonConstRange(aResources); + sal_Int32 nCount (aResources.getLength()); + + // Prepare the list of pure anchors that have to be deactivated. + rResourcesToDeactivate.clear(); + + // Iterate over the list in reverse order because when there is a chain + // of pure anchors with only the last one having no child then the whole + // list has to be deactivated. + sal_Int32 nIndex (nCount-1); + while (nIndex >= 0) + { + const Reference<XResourceId> xResourceId (aResources[nIndex]); + const Reference<XResource> xResource ( + mpResourceManager->GetResource(xResourceId).mxResource); + bool bDeactiveCurrentResource (false); + + // Skip all resources that are no pure anchors. + if (xResource.is() && xResource->isAnchorOnly()) + { + // When xResource is not an anchor of the next resource in + // the list then it is the anchor of no resource at all. + if (nIndex == nCount-1) + { + // No following anchors, deactivate this one, then remove it + // from the list. + bDeactiveCurrentResource = true; + } + else + { + const Reference<XResourceId> xPrevResourceId (aResources[nIndex+1]); + if ( ! xPrevResourceId.is() + || ! xPrevResourceId->isBoundTo(xResourceId, AnchorBindingMode_DIRECT)) + { + // The previous resource (id) does not exist or is not bound to + // the current anchor. + bDeactiveCurrentResource = true; + } + } + } + + if (bDeactiveCurrentResource) + { + SAL_INFO("sd.fwk", __func__ << ": deactivating pure anchor " << + FrameworkHelper::ResourceIdToString(xResourceId) << + "because it has no children"); + // Erase element from current configuration. + for (sal_Int32 nI=nIndex; nI<nCount-2; ++nI) + aResourcesRange[nI] = aResources[nI+1]; + nCount -= 1; + + rResourcesToDeactivate.push_back(xResourceId); + } + nIndex -= 1; + } +} + +void ConfigurationUpdater::LockUpdates() +{ + ++mnLockCount; +} + +void ConfigurationUpdater::UnlockUpdates() +{ + --mnLockCount; + if (mnLockCount == 0 && mbUpdatePending) + { + RequestUpdate(mxRequestedConfiguration); + } +} + +std::shared_ptr<ConfigurationUpdaterLock> ConfigurationUpdater::GetLock() +{ + return std::make_shared<ConfigurationUpdaterLock>(*this); +} + +void ConfigurationUpdater::SetUpdateBeingProcessed (bool bValue) +{ + mbUpdateBeingProcessed = bValue; +} + +IMPL_LINK_NOARG(ConfigurationUpdater, TimeoutHandler, Timer *, void) +{ + if ( ! mbUpdateBeingProcessed + && mxCurrentConfiguration.is() + && mxRequestedConfiguration.is()) + { + if ( ! AreConfigurationsEquivalent(mxCurrentConfiguration, mxRequestedConfiguration)) + { + RequestUpdate(mxRequestedConfiguration); + } + } +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ConfigurationUpdater.hxx b/sd/source/ui/framework/configuration/ConfigurationUpdater.hxx new file mode 100644 index 000000000..9fba364b1 --- /dev/null +++ b/sd/source/ui/framework/configuration/ConfigurationUpdater.hxx @@ -0,0 +1,209 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/uno/Reference.hxx> +#include <vcl/timer.hxx> +#include <memory> +#include <vector> + +namespace com::sun::star::drawing::framework +{ +class XConfiguration; +} +namespace com::sun::star::drawing::framework +{ +class XControllerManager; +} +namespace com::sun::star::drawing::framework +{ +class XResourceId; +} + +namespace sd::framework +{ +class ConfigurationClassifier; +class ConfigurationUpdaterLock; +class ConfigurationControllerResourceManager; +class ConfigurationControllerBroadcaster; + +/** This is a helper class for the ConfigurationController. It handles the + update of the current configuration so that it looks like a requested + configuration. An update is made by activating or deactivating drawing + framework resources. + + When an update is not successful, i.e. after the update the current + configuration is not equivalent to the requested configuration, then a + timer is started to repeat the update after a short time. +*/ +class ConfigurationUpdater +{ +public: + /** Create a new ConfigurationUpdater object that notifies configuration + changes and the start and end of updates via the given broadcaster. + */ + ConfigurationUpdater( + const std::shared_ptr<ConfigurationControllerBroadcaster>& rpBroadcaster, + const std::shared_ptr<ConfigurationControllerResourceManager>& rpResourceManager, + const css::uno::Reference<css::drawing::framework::XControllerManager>& + rxControllerManager); + ~ConfigurationUpdater(); + + /** Request an update of the current configuration so that it looks like + the given requested configuration. It checks whether an update of + the current configuration can be done. Calls UpdateConfiguration() + if that is the case. Otherwise it schedules a later call to + UpdateConfiguration(). + */ + void RequestUpdate(const css::uno::Reference<css::drawing::framework::XConfiguration>& + rxRequestedConfiguration); + + const css::uno::Reference<css::drawing::framework::XConfiguration>& + GetCurrentConfiguration() const + { + return mxCurrentConfiguration; + } + + friend class ConfigurationUpdaterLock; + /** Return a lock of the called ConfigurationUpdater. While the + returned object exists no update of the current configuration is + made. + */ + std::shared_ptr<ConfigurationUpdaterLock> GetLock(); + +private: + /** A reference to the XControllerManager is kept so that + UpdateConfiguration() has access to the other sub controllers. + */ + css::uno::Reference<css::drawing::framework::XControllerManager> mxControllerManager; + + std::shared_ptr<ConfigurationControllerBroadcaster> mpBroadcaster; + + /** The current configuration holds the resources that are currently + active. It is modified during an update. + */ + css::uno::Reference<css::drawing::framework::XConfiguration> mxCurrentConfiguration; + + /** The requested configuration holds the resources that have been + requested to activate or to deactivate since the last update. It is + (usually) not modified during an update. This configuration is + maintained by the ConfigurationController and given to the + ConfigurationUpdater in the RequestUpdate() method. + */ + css::uno::Reference<css::drawing::framework::XConfiguration> mxRequestedConfiguration; + + /** This flag is set to </sal_True> when an update of the current + configuration was requested (because the last request in the queue + was processed) but could not be executed because the + ConfigurationController was locked. A call to UpdateConfiguration() + resets the flag to </sal_False>. + */ + bool mbUpdatePending; + + /** This flag is set to </sal_True> while the UpdateConfiguration() method + is running. It is used to prevent reentrance problems with this + method. + */ + bool mbUpdateBeingProcessed; + + /** The ConfigurationController is locked when this count has a value + larger then zero. If the controller is locked then updates of the + current configuration are not made. + */ + sal_Int32 mnLockCount; + + /** This timer is used to check from time to time whether the requested + configuration and the current configuration are identical and request + an update when they are not. + This is used to overcome problems with resources that become + available asynchronously. + */ + Timer maUpdateTimer; + + /** The number of failed updates (those after which the current + configuration is not equivalent to the requested configuration) is + used to determine how long to wait before another update is made. + */ + sal_Int32 mnFailedUpdateCount; + + std::shared_ptr<ConfigurationControllerResourceManager> mpResourceManager; + + /** This method does the main work of an update. It calls the sub + controllers that are responsible for the various types of resources + and tells them to update their active resources. It notifies + listeners about the start and end of the configuration update. + */ + void UpdateConfiguration(); + + /** Basically calls UpdaterStart() andUpdateEnd() and makes some debug + output. + */ + void UpdateCore(const ConfigurationClassifier& rClassifier); + + /** Check for all pure anchors if they have at least one child. + Childless pure anchors are deactivated. + This affects only the current configuration. + */ + void CheckPureAnchors( + const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration, + ::std::vector<css::uno::Reference<css::drawing::framework::XResourceId>>& + rResourcesToDeactivate); + + /** Remove from the requested configuration all pure anchors that have no + child. Requested but not yet activated anchors can not be removed + because without the actual resource the 'pureness' of an anchor can + not be determined. + */ + void CleanRequestedConfiguration(); + + /** Check the success of a recently executed configuration update. + When the update failed then start the timer. + */ + void CheckUpdateSuccess(); + + /** This method sets the mbUpdateBeingProcessed member that is used to + prevent reentrance problems. This method allows function objects + easily and safely to modify the variable. + */ + void SetUpdateBeingProcessed(bool bValue); + + /** Return whether it is possible to do an update of the configuration. + This takes into account whether another update is currently being + executed, the lock count, and whether the configuration controller + is still valid. + */ + bool IsUpdatePossible() const; + + /** Lock updates of the current configuration. For intermediate requests + for updates mbUpdatePending is set to <TRUE/>. + */ + void LockUpdates(); + + /** When an update was requested since the last LockUpdates() call then + RequestUpdate() is called. + */ + void UnlockUpdates(); + + DECL_LINK(TimeoutHandler, Timer*, void); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/GenericConfigurationChangeRequest.cxx b/sd/source/ui/framework/configuration/GenericConfigurationChangeRequest.cxx new file mode 100644 index 000000000..fa6d41503 --- /dev/null +++ b/sd/source/ui/framework/configuration/GenericConfigurationChangeRequest.cxx @@ -0,0 +1,81 @@ +/* -*- 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 <sal/config.h> + +#include <string_view> + +#include "GenericConfigurationChangeRequest.hxx" + +#include <framework/FrameworkHelper.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace sd::framework { + +GenericConfigurationChangeRequest::GenericConfigurationChangeRequest ( + const Reference<XResourceId>& rxResourceId, + const Mode eMode) + : mxResourceId(rxResourceId), + meMode(eMode) +{ + if ( ! rxResourceId.is() || rxResourceId->getResourceURL().isEmpty()) + throw css::lang::IllegalArgumentException(); +} + +GenericConfigurationChangeRequest::~GenericConfigurationChangeRequest() noexcept +{ +} + +void SAL_CALL GenericConfigurationChangeRequest::execute ( + const Reference<XConfiguration>& rxConfiguration) +{ + if (!rxConfiguration.is()) + return; + + switch (meMode) + { + case Activation: + rxConfiguration->addResource(mxResourceId); + break; + + case Deactivation: + rxConfiguration->removeResource(mxResourceId); + break; + } +} + +OUString SAL_CALL GenericConfigurationChangeRequest::getName() +{ + return OUString::Concat("GenericConfigurationChangeRequest ") + + (meMode==Activation + ? std::u16string_view(u"activate ") : std::u16string_view(u"deactivate ")) + + FrameworkHelper::ResourceIdToString(mxResourceId); +} + +void SAL_CALL GenericConfigurationChangeRequest::setName (const OUString&) +{ + // Ignored. +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/GenericConfigurationChangeRequest.hxx b/sd/source/ui/framework/configuration/GenericConfigurationChangeRequest.hxx new file mode 100644 index 000000000..3caa7a8ca --- /dev/null +++ b/sd/source/ui/framework/configuration/GenericConfigurationChangeRequest.hxx @@ -0,0 +1,98 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/drawing/framework/XConfigurationChangeRequest.hpp> +#include <com/sun/star/container/XNamed.hpp> +#include <comphelper/compbase.hxx> + +namespace com::sun::star::drawing::framework { class XConfiguration; } +namespace com::sun::star::drawing::framework { class XResourceId; } + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XConfigurationChangeRequest, + css::container::XNamed + > GenericConfigurationChangeRequestInterfaceBase; + +/** This implementation of the XConfigurationChangeRequest interface + represents a single explicit request for a configuration change. On its + execution it may result in other, implicit, configuration changes. For + example this is the case when the deactivation of a unique resource is + requested: the resources linked to it have to be deactivated as well. +*/ +class GenericConfigurationChangeRequest final + : public GenericConfigurationChangeRequestInterfaceBase +{ +public: + /** This enum specified whether the activation or deactivation of a + resource is requested. + */ + enum Mode { Activation, Deactivation }; + + /** Create a new object that represents the request for activation or + deactivation of the specified resource. + @param rxsResourceId + Id of the resource that is to be activated or deactivated. + @param eMode + The mode specifies whether to activate or to deactivate the + resource. + @throws css::css::lang::IllegalArgumentException + */ + GenericConfigurationChangeRequest ( + const css::uno::Reference<css::drawing::framework::XResourceId>& + rxResourceId, + const Mode eMode); + + virtual ~GenericConfigurationChangeRequest() noexcept override; + + // XConfigurationChangeOperation + + /** The requested configuration change is executed on the given + configuration. Additionally to the explicitly requested change + other changes have to be made as well. See class description for an + example. + @param rxConfiguration + The configuration to which the requested change is made. + */ + virtual void SAL_CALL execute ( + const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration) override; + + // XNamed + + /** Return a human readable string representation. This is used for + debugging purposes. + */ + virtual OUString SAL_CALL getName() override; + + /** This call is ignored because the XNamed interface is (mis)used to + give access to a human readable name for debugging purposes. + */ + virtual void SAL_CALL setName (const OUString& rName) override; + +private: + const css::uno::Reference<css::drawing::framework::XResourceId> mxResourceId; + const Mode meMode; +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ResourceFactoryManager.cxx b/sd/source/ui/framework/configuration/ResourceFactoryManager.cxx new file mode 100644 index 000000000..4817c1360 --- /dev/null +++ b/sd/source/ui/framework/configuration/ResourceFactoryManager.cxx @@ -0,0 +1,197 @@ +/* -*- 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 "ResourceFactoryManager.hxx" +#include <tools/wldcrd.hxx> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/util/URLTransformer.hpp> +#include <com/sun/star/drawing/framework/XControllerManager.hpp> +#include <comphelper/processfactory.hxx> +#include <sal/log.hxx> + +#include <algorithm> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +#undef VERBOSE +//#define VERBOSE 1 + +namespace sd::framework { + +ResourceFactoryManager::ResourceFactoryManager (const Reference<XControllerManager>& rxManager) + : mxControllerManager(rxManager) +{ + // Create the URL transformer. + Reference<uno::XComponentContext> xContext(::comphelper::getProcessComponentContext()); + mxURLTransformer = util::URLTransformer::create(xContext); +} + +ResourceFactoryManager::~ResourceFactoryManager() +{ + for (auto& rXInterfaceResource : maFactoryMap) + { + Reference<lang::XComponent> xComponent (rXInterfaceResource.second, UNO_QUERY); + rXInterfaceResource.second = nullptr; + if (xComponent.is()) + xComponent->dispose(); + } + + Reference<lang::XComponent> xComponent (mxURLTransformer, UNO_QUERY); + if (xComponent.is()) + xComponent->dispose(); +} + +void ResourceFactoryManager::AddFactory ( + const OUString& rsURL, + const Reference<XResourceFactory>& rxFactory) +{ + if ( ! rxFactory.is()) + throw lang::IllegalArgumentException(); + if (rsURL.isEmpty()) + throw lang::IllegalArgumentException(); + + std::scoped_lock aGuard (maMutex); + + if (rsURL.indexOf('*') >= 0 || rsURL.indexOf('?') >= 0) + { + // The URL is a URL pattern not a single URL. + maFactoryPatternList.emplace_back(rsURL, rxFactory); + +#if defined VERBOSE && VERBOSE>=1 + SAL_INFO("sd","ResourceFactoryManager::AddFactory pattern " << rsURL << std::hex << rxFactory.get()); +#endif + } + else + { + maFactoryMap[rsURL] = rxFactory; + +#if defined VERBOSE && VERBOSE>=1 + SAL_INFO("sd", "ResourceFactoryManager::AddFactory fixed " << rsURL << " 0x" << std::hex << rxFactory.get()); +#endif + } +} + +void ResourceFactoryManager::RemoveFactoryForURL ( + const OUString& rsURL) +{ + if (rsURL.isEmpty()) + throw lang::IllegalArgumentException(); + + std::scoped_lock aGuard (maMutex); + + FactoryMap::iterator iFactory (maFactoryMap.find(rsURL)); + if (iFactory != maFactoryMap.end()) + { + maFactoryMap.erase(iFactory); + } + else + { + // The URL may be a pattern. Look that up. + auto iPattern = std::find_if(maFactoryPatternList.begin(), maFactoryPatternList.end(), + [&rsURL](const FactoryPatternList::value_type& rPattern) { return rPattern.first == rsURL; }); + if (iPattern != maFactoryPatternList.end()) + { + // Found the pattern. Remove it. + maFactoryPatternList.erase(iPattern); + } + } +} + +void ResourceFactoryManager::RemoveFactoryForReference( + const Reference<XResourceFactory>& rxFactory) +{ + std::scoped_lock aGuard (maMutex); + + // Collect a list with all keys that map to the given factory. + ::std::vector<OUString> aKeys; + for (const auto& rFactory : maFactoryMap) + if (rFactory.second == rxFactory) + aKeys.push_back(rFactory.first); + + // Remove the entries whose keys we just have collected. + for (const auto& rKey : aKeys) + maFactoryMap.erase(rKey); + + // Remove the pattern entries whose factories are identical to the given + // factory. + maFactoryPatternList.erase( + std::remove_if( + maFactoryPatternList.begin(), + maFactoryPatternList.end(), + [&] (FactoryPatternList::value_type const& it) { return it.second == rxFactory; }), + maFactoryPatternList.end()); +} + +Reference<XResourceFactory> ResourceFactoryManager::GetFactory ( + const OUString& rsCompleteURL) +{ + OUString sURLBase (rsCompleteURL); + if (mxURLTransformer.is()) + { + util::URL aURL; + aURL.Complete = rsCompleteURL; + if (mxURLTransformer->parseStrict(aURL)) + sURLBase = aURL.Main; + } + + Reference<XResourceFactory> xFactory = FindFactory(sURLBase); + + if ( ! xFactory.is() && mxControllerManager.is()) + { + Reference<XModuleController> xModuleController(mxControllerManager->getModuleController()); + if (xModuleController.is()) + { + // Ask the module controller to provide a factory of the + // requested view type. Note that this can (and should) cause + // intermediate calls to AddFactory(). + xModuleController->requestResource(sURLBase); + + xFactory = FindFactory(sURLBase); + } + } + + return xFactory; +} + +Reference<XResourceFactory> ResourceFactoryManager::FindFactory (const OUString& rsURLBase) +{ + std::scoped_lock aGuard (maMutex); + FactoryMap::const_iterator iFactory (maFactoryMap.find(rsURLBase)); + if (iFactory != maFactoryMap.end()) + return iFactory->second; + else + { + // Check the URL patterns. + auto iPattern = std::find_if(maFactoryPatternList.begin(), maFactoryPatternList.end(), + [&rsURLBase](const FactoryPatternList::value_type& rPattern) { + WildCard aWildCard (rPattern.first); + return aWildCard.Matches(rsURLBase); + }); + if (iPattern != maFactoryPatternList.end()) + return iPattern->second; + } + return nullptr; +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ResourceFactoryManager.hxx b/sd/source/ui/framework/configuration/ResourceFactoryManager.hxx new file mode 100644 index 000000000..61daf383b --- /dev/null +++ b/sd/source/ui/framework/configuration/ResourceFactoryManager.hxx @@ -0,0 +1,120 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <mutex> +#include <unordered_map> +#include <utility> +#include <vector> + +#include <com/sun/star/uno/Reference.hxx> +#include <rtl/ustring.hxx> + +namespace com::sun::star::drawing::framework { class XControllerManager; } +namespace com::sun::star::drawing::framework { class XResourceFactory; } +namespace com::sun::star::util { class XURLTransformer; } + +namespace sd::framework { + +/** Container of resource factories of the drawing framework. +*/ +class ResourceFactoryManager +{ +public: + explicit ResourceFactoryManager ( + const css::uno::Reference<css::drawing::framework::XControllerManager>& rxManager); + + ~ResourceFactoryManager(); + + /** Register a resource factory for one type of resource. + @param rsURL + The type of the resource that will be created by the factory. + @param rxFactory + The factory that will create resource objects of the specified type. + @throws css::uno::RuntimeException + */ + void AddFactory ( + const OUString& rsURL, + const css::uno::Reference<css::drawing::framework::XResourceFactory>& rxFactory); + + /** Unregister the specified factory. + @param rsURL + Unregister only the factory for this URL. When the same factory + is registered for other URLs then these remain registered. + @throws css::uno::RuntimeException + */ + void RemoveFactoryForURL( + const OUString& rsURL); + + /** Unregister the specified factory. + @param rxFactory + Unregister the this factory for all URLs that it has been + registered for. + @throws css::uno::RuntimeException + */ + void RemoveFactoryForReference( + const css::uno::Reference<css::drawing::framework::XResourceFactory>& rxFactory); + + /** Return a factory that can create resources specified by the given URL. + @param rsCompleteURL + This URL specifies the type of the resource. It may contain arguments. + @return + When a factory for the specified URL has been registered by a + previous call to AddFactory() then a reference to that factory + is returned. Otherwise an empty reference is returned. + @throws css::uno::RuntimeException + */ + css::uno::Reference<css::drawing::framework::XResourceFactory> GetFactory ( + const OUString& rsURL); + +private: + std::mutex maMutex; + typedef std::unordered_map< + OUString, + css::uno::Reference<css::drawing::framework::XResourceFactory> > FactoryMap; + FactoryMap maFactoryMap; + + typedef ::std::vector< + ::std::pair< + OUString, + css::uno::Reference<css::drawing::framework::XResourceFactory> > > + FactoryPatternList; + FactoryPatternList maFactoryPatternList; + + css::uno::Reference<css::drawing::framework::XControllerManager> mxControllerManager; + css::uno::Reference<css::util::XURLTransformer> mxURLTransformer; + + /** Look up the factory for the given URL. + @param rsURLBase + The css::tools::URL.Main part of a URL. Arguments have to be + stripped off by the caller. + @return + When the factory has not yet been added then return NULL. + @throws css::uno::RuntimeException + */ + css::uno::Reference<css::drawing::framework::XResourceFactory> FindFactory ( + const OUString& rsURLBase); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ResourceId.cxx b/sd/source/ui/framework/configuration/ResourceId.cxx new file mode 100644 index 000000000..1845b353f --- /dev/null +++ b/sd/source/ui/framework/configuration/ResourceId.cxx @@ -0,0 +1,503 @@ +/* -*- 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 <framework/ResourceId.hxx> +#include <tools/SdGlobalResourceContainer.hxx> +#include <com/sun/star/util/URLTransformer.hpp> +#include <comphelper/processfactory.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weakref.hxx> +#include <rtl/ref.hxx> + +#include <algorithm> + +namespace com::sun::star::uno { class XComponentContext; } + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::drawing::framework; + +/** When the USE_OPTIMIZATIONS symbol is defined then at some optimizations + are activated that work only together with XResourceId objects that are + implemented by the ResourceId class. For other implementations of when + the USE_OPTIMIZATIONS symbol is not defined then alternative code is + used instead. +*/ +#define USE_OPTIMIZATIONS + +namespace sd::framework { + +//===== ResourceId ============================================================ + +WeakReference<util::XURLTransformer> ResourceId::mxURLTransformerWeak; + +ResourceId::ResourceId() + : maResourceURLs(0) +{ +} + +ResourceId::ResourceId ( + std::vector<OUString>&& rResourceURLs) + : maResourceURLs(std::move(rResourceURLs)) +{ + ParseResourceURL(); +} + +ResourceId::ResourceId ( + const OUString& rsResourceURL) + : maResourceURLs(1, rsResourceURL) +{ + // Handle the special case of an empty resource URL. + if (rsResourceURL.isEmpty()) + maResourceURLs.clear(); + ParseResourceURL(); +} + +ResourceId::ResourceId ( + const OUString& rsResourceURL, + const OUString& rsAnchorURL) + : maResourceURLs(2) +{ + maResourceURLs[0] = rsResourceURL; + maResourceURLs[1] = rsAnchorURL; + ParseResourceURL(); +} + +ResourceId::ResourceId ( + const OUString& rsResourceURL, + const OUString& rsFirstAnchorURL, + const Sequence<OUString>& rAnchorURLs) + : maResourceURLs(2+rAnchorURLs.getLength()) +{ + maResourceURLs[0] = rsResourceURL; + maResourceURLs[1] = rsFirstAnchorURL; + std::copy(rAnchorURLs.begin(), rAnchorURLs.end(), std::next(maResourceURLs.begin(), 2)); + ParseResourceURL(); +} + +ResourceId::~ResourceId() +{ + mpURL.reset(); +} + +OUString SAL_CALL + ResourceId::getResourceURL() +{ + if (!maResourceURLs.empty()) + return maResourceURLs[0]; + else + return OUString(); +} + +util::URL SAL_CALL + ResourceId::getFullResourceURL() +{ + if (mpURL != nullptr) + return *mpURL; + + Reference<util::XURLTransformer> xURLTransformer (mxURLTransformerWeak); + if (xURLTransformer.is() && !maResourceURLs.empty() ) + { + mpURL.reset(new util::URL); + mpURL->Complete = maResourceURLs[0]; + xURLTransformer->parseStrict(*mpURL); + return *mpURL; + } + + util::URL aURL; + if (!maResourceURLs.empty()) + aURL.Complete = maResourceURLs[0]; + return aURL; +} + +sal_Bool SAL_CALL + ResourceId::hasAnchor() +{ + return maResourceURLs.size()>1; +} + +Reference<XResourceId> SAL_CALL + ResourceId::getAnchor() +{ + ::rtl::Reference<ResourceId> rResourceId (new ResourceId()); + const sal_Int32 nAnchorCount (maResourceURLs.size()-1); + if (nAnchorCount > 0) + { + rResourceId->maResourceURLs.resize(nAnchorCount); + for (sal_Int32 nIndex=0; nIndex<nAnchorCount; ++nIndex) + rResourceId->maResourceURLs[nIndex] = maResourceURLs[nIndex+1]; + } + return rResourceId; +} + +Sequence<OUString> SAL_CALL + ResourceId::getAnchorURLs() +{ + const sal_Int32 nAnchorCount (maResourceURLs.size() - 1); + if (nAnchorCount > 0) + { + Sequence<OUString> aAnchorURLs (nAnchorCount); + std::copy_n(maResourceURLs.begin() + 1, nAnchorCount, aAnchorURLs.getArray()); + return aAnchorURLs; + } + else + return Sequence<OUString>(); +} + +OUString SAL_CALL + ResourceId::getResourceTypePrefix() +{ + if (!maResourceURLs.empty() ) + { + // Return the "private:resource/<type>/" prefix. + + // Get the prefix that ends with the second "/". + const OUString& rsResourceURL (maResourceURLs[0]); + sal_Int32 nPrefixEnd (rsResourceURL.indexOf('/')); + if (nPrefixEnd >= 0) + nPrefixEnd = rsResourceURL.indexOf('/', nPrefixEnd+1) + 1; + else + nPrefixEnd = 0; + + return rsResourceURL.copy(0,nPrefixEnd); + } + else + return OUString(); +} + +sal_Int16 SAL_CALL + ResourceId::compareTo (const Reference<XResourceId>& rxResourceId) +{ + sal_Int16 nResult (0); + + if ( ! rxResourceId.is()) + { + // The empty reference is interpreted as empty resource id object. + if (!maResourceURLs.empty()) + nResult = +1; + else + nResult = 0; + } + else + { + ResourceId* pId = nullptr; +#ifdef USE_OPTIMIZATIONS + pId = dynamic_cast<ResourceId*>(rxResourceId.get()); +#endif + if (pId != nullptr) + { + // We have direct access to the implementation of the given + // resource id object. + nResult = CompareToLocalImplementation(*pId); + } + else + { + // We have to do the comparison via the UNO interface of the + // given resource id object. + nResult = CompareToExternalImplementation(rxResourceId); + } + } + + return nResult; +} + +sal_Int16 ResourceId::CompareToLocalImplementation (const ResourceId& rId) const +{ + sal_Int16 nResult (0); + + const sal_uInt32 nLocalURLCount (maResourceURLs.size()); + const sal_uInt32 nURLCount(rId.maResourceURLs.size()); + + // Start comparison with the top most anchors. + for (sal_Int32 nIndex=nURLCount-1,nLocalIndex=nLocalURLCount-1; + nIndex>=0 && nLocalIndex>=0; + --nIndex,--nLocalIndex) + { + const OUString sLocalURL (maResourceURLs[nLocalIndex]); + const OUString sURL (rId.maResourceURLs[nIndex]); + const sal_Int32 nLocalResult (sURL.compareTo(sLocalURL)); + if (nLocalResult != 0) + { + if (nLocalResult < 0) + nResult = -1; + else + nResult = +1; + break; + } + } + + if (nResult == 0) + { + // No difference found yet. When the lengths are the same then the + // two resource ids are equivalent. Otherwise the shorter comes + // first. + if (nLocalURLCount != nURLCount) + { + if (nLocalURLCount < nURLCount) + nResult = -1; + else + nResult = +1; + } + } + + return nResult; +} + +sal_Int16 ResourceId::CompareToExternalImplementation (const Reference<XResourceId>& rxId) const +{ + sal_Int16 nResult (0); + + const Sequence<OUString> aAnchorURLs (rxId->getAnchorURLs()); + const sal_uInt32 nLocalURLCount (maResourceURLs.size()); + const sal_uInt32 nURLCount(1+aAnchorURLs.getLength()); + + // Start comparison with the top most anchors. + sal_Int32 nLocalResult (0); + for (sal_Int32 nIndex=nURLCount-1,nLocalIndex=nLocalURLCount-1; + nIndex>=0&&nLocalIndex>=0; + --nIndex,--nLocalIndex) + { + if (nIndex == 0 ) + nLocalResult = maResourceURLs[nIndex].compareTo(rxId->getResourceURL()); + else + nLocalResult = maResourceURLs[nIndex].compareTo(aAnchorURLs[nIndex-1]); + if (nLocalResult != 0) + { + if (nLocalResult < 0) + nResult = -1; + else + nResult = +1; + break; + } + } + + if (nResult == 0) + { + // No difference found yet. When the lengths are the same then the + // two resource ids are equivalent. Otherwise the shorter comes + // first. + if (nLocalURLCount != nURLCount) + { + if (nLocalURLCount < nURLCount) + nResult = -1; + else + nResult = +1; + } + } + + return nResult; +} + +sal_Bool SAL_CALL + ResourceId::isBoundTo ( + const Reference<XResourceId>& rxResourceId, + AnchorBindingMode eMode) +{ + if ( ! rxResourceId.is()) + { + // An empty reference is interpreted as empty resource id. + return IsBoundToAnchor(nullptr, nullptr, eMode); + } + + ResourceId* pId = nullptr; +#ifdef USE_OPTIMIZATIONS + pId = dynamic_cast<ResourceId*>(rxResourceId.get()); +#endif + if (pId != nullptr) + { + return IsBoundToAnchor(pId->maResourceURLs, eMode); + } + else + { + const OUString sResourceURL (rxResourceId->getResourceURL()); + const Sequence<OUString> aAnchorURLs (rxResourceId->getAnchorURLs()); + return IsBoundToAnchor(&sResourceURL, &aAnchorURLs, eMode); + } +} + +sal_Bool SAL_CALL + ResourceId::isBoundToURL ( + const OUString& rsAnchorURL, + AnchorBindingMode eMode) +{ + return IsBoundToAnchor(&rsAnchorURL, nullptr, eMode); +} + +Reference<XResourceId> SAL_CALL + ResourceId::clone() +{ + return new ResourceId(std::vector(maResourceURLs)); +} + +//----- XInitialization ------------------------------------------------------- + +void SAL_CALL ResourceId::initialize (const Sequence<Any>& aArguments) +{ + for (const auto& rArgument : aArguments) + { + OUString sResourceURL; + if (rArgument >>= sResourceURL) + maResourceURLs.push_back(sResourceURL); + else + { + Reference<XResourceId> xAnchor; + if (rArgument >>= xAnchor) + { + if (xAnchor.is()) + { + maResourceURLs.push_back(xAnchor->getResourceURL()); + const Sequence<OUString> aAnchorURLs (xAnchor->getAnchorURLs()); + maResourceURLs.insert( maResourceURLs.end(), aAnchorURLs.begin(), aAnchorURLs.end() ); + } + } + } + } + ParseResourceURL(); +} + +OUString ResourceId::getImplementationName() +{ + return "com.sun.star.comp.Draw.framework.ResourceId"; +} + +sal_Bool ResourceId::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence<OUString> ResourceId::getSupportedServiceNames() +{ + return css::uno::Sequence<OUString>{ + "com.sun.star.drawing.framework.ResourceId"}; +} + +/** When eMode is DIRECTLY then the anchor of the called object and the + anchor represented by the given sequence of anchor URLs have to be + identical. When eMode is RECURSIVE then the anchor of the called + object has to start with the given anchor URLs. +*/ +bool ResourceId::IsBoundToAnchor ( + const OUString* psFirstAnchorURL, + const Sequence<OUString>* paAnchorURLs, + AnchorBindingMode eMode) const +{ + const sal_uInt32 nLocalAnchorURLCount (maResourceURLs.size() - 1); + const bool bHasFirstAnchorURL (psFirstAnchorURL!=nullptr); + const sal_uInt32 nAnchorURLCount ((bHasFirstAnchorURL?1:0) + + (paAnchorURLs!=nullptr ? paAnchorURLs->getLength() : 0)); + + // Check the lengths. + if (nLocalAnchorURLCount<nAnchorURLCount || + (eMode==AnchorBindingMode_DIRECT && nLocalAnchorURLCount!=nAnchorURLCount)) + { + return false; + } + + // Compare the nAnchorURLCount bottom-most anchor URLs of this resource + // id and the given anchor. + sal_uInt32 nOffset = 0; + if (paAnchorURLs != nullptr) + { + sal_uInt32 nCount = paAnchorURLs->getLength(); + while (nOffset < nCount) + { + if ( maResourceURLs[nLocalAnchorURLCount - nOffset] != + (*paAnchorURLs)[nCount - 1 - nOffset] ) + { + return false; + } + ++nOffset; + } + } + if (bHasFirstAnchorURL) + { + if ( *psFirstAnchorURL != maResourceURLs[nLocalAnchorURLCount - nOffset] ) + return false; + } + + return true; +} + +bool ResourceId::IsBoundToAnchor ( + const ::std::vector<OUString>& rAnchorURLs, + AnchorBindingMode eMode) const +{ + const sal_uInt32 nLocalAnchorURLCount (maResourceURLs.size() - 1); + const sal_uInt32 nAnchorURLCount (rAnchorURLs.size()); + + // Check the lengths. + if (nLocalAnchorURLCount<nAnchorURLCount || + (eMode==AnchorBindingMode_DIRECT && nLocalAnchorURLCount!=nAnchorURLCount)) + { + return false; + } + + // Compare the nAnchorURLCount bottom-most anchor URLs of this resource + // id and the given anchor. + for (sal_uInt32 nOffset=0; nOffset<nAnchorURLCount; ++nOffset) + { + if ( maResourceURLs[nLocalAnchorURLCount - nOffset] != + rAnchorURLs[nAnchorURLCount - 1 - nOffset] ) + { + return false; + } + } + + return true; +} + +void ResourceId::ParseResourceURL() +{ + ::osl::Guard< ::osl::Mutex > aGuard (::osl::Mutex::getGlobalMutex()); + Reference<util::XURLTransformer> xURLTransformer (mxURLTransformerWeak); + if ( ! xURLTransformer.is()) + { + // Create the URL transformer. + Reference<uno::XComponentContext> xContext(::comphelper::getProcessComponentContext()); + xURLTransformer.set(util::URLTransformer::create(xContext)); + mxURLTransformerWeak = xURLTransformer; + SdGlobalResourceContainer::Instance().AddResource( + Reference<XInterface>(xURLTransformer,UNO_QUERY)); + } + + if (xURLTransformer.is() && !maResourceURLs.empty() ) + { + mpURL.reset(new util::URL); + mpURL->Complete = maResourceURLs[0]; + xURLTransformer->parseStrict(*mpURL); + if (mpURL->Main == maResourceURLs[0]) + mpURL.reset(); + else + maResourceURLs[0] = mpURL->Main; + } +} + +} // end of namespace sd::framework + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_framework_ResourceID_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new sd::framework::ResourceId()); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/UpdateRequest.cxx b/sd/source/ui/framework/configuration/UpdateRequest.cxx new file mode 100644 index 000000000..b6c5e8c42 --- /dev/null +++ b/sd/source/ui/framework/configuration/UpdateRequest.cxx @@ -0,0 +1,47 @@ +/* -*- 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 "UpdateRequest.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace sd::framework +{ +UpdateRequest::UpdateRequest() noexcept {} + +UpdateRequest::~UpdateRequest() noexcept {} + +void SAL_CALL UpdateRequest::execute(const Reference<XConfiguration>&) +{ + // Do nothing here. The configuration is updated when the request queue + // becomes empty. +} + +OUString SAL_CALL UpdateRequest::getName() { return "UpdateRequest"; } + +void SAL_CALL UpdateRequest::setName(const OUString&) +{ + // Ignored. +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/UpdateRequest.hxx b/sd/source/ui/framework/configuration/UpdateRequest.hxx new file mode 100644 index 000000000..712167154 --- /dev/null +++ b/sd/source/ui/framework/configuration/UpdateRequest.hxx @@ -0,0 +1,70 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/drawing/framework/XConfigurationChangeRequest.hpp> +#include <com/sun/star/container/XNamed.hpp> +#include <comphelper/compbase.hxx> + +namespace com::sun::star::drawing::framework { class XConfiguration; } + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XConfigurationChangeRequest, + css::container::XNamed + > UpdateRequestInterfaceBase; + +/** This update request is used to request configuration updates + asynchronous when no other requests are being processed. When there are + other requests then we can simply wait until the last one is executed: + the configuration is updated when the request queue becomes empty. This + is use by this implementation as well. The execute() method does not + really do anything. This request just triggers the update of the + configuration when it is removed as last request from the queue. +*/ +class UpdateRequest final + : public UpdateRequestInterfaceBase +{ +public: + UpdateRequest() noexcept; + virtual ~UpdateRequest() noexcept override; + + // XConfigurationChangeOperation + + virtual void SAL_CALL execute ( + const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration) override; + + // XNamed + + /** Return a human readable string representation. This is used for + debugging purposes. + */ + virtual OUString SAL_CALL getName() override; + + /** This call is ignored because the XNamed interface is (mis)used to + give access to a human readable name for debugging purposes. + */ + virtual void SAL_CALL setName (const OUString& rName) override; +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/debugtrace.hxx b/sd/source/ui/framework/configuration/debugtrace.hxx new file mode 100644 index 000000000..b520d0ff3 --- /dev/null +++ b/sd/source/ui/framework/configuration/debugtrace.hxx @@ -0,0 +1,15 @@ +/* -*- 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/. + */ + +#pragma once + +/// Centrally define activation of configuration debug traces. +#define DEBUG_SD_CONFIGURATION_TRACE 0 + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/BasicPaneFactory.cxx b/sd/source/ui/framework/factories/BasicPaneFactory.cxx new file mode 100644 index 000000000..c01d315a3 --- /dev/null +++ b/sd/source/ui/framework/factories/BasicPaneFactory.cxx @@ -0,0 +1,432 @@ +/* -*- 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 <memory> +#include <sal/config.h> + +#include "BasicPaneFactory.hxx" + +#include "ChildWindowPane.hxx" +#include "FrameWindowPane.hxx" +#include "FullScreenPane.hxx" + +#include <comphelper/servicehelper.hxx> +#include <framework/FrameworkHelper.hxx> +#include <PaneShells.hxx> +#include <ViewShellBase.hxx> +#include <PaneChildWindows.hxx> +#include <DrawController.hxx> +#include <com/sun/star/drawing/framework/XControllerManager.hpp> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::drawing::framework; + +using ::sd::framework::FrameworkHelper; + +namespace { + enum PaneId { + CenterPaneId, + FullScreenPaneId, + LeftImpressPaneId, + LeftDrawPaneId + }; + + const sal_Int32 gnConfigurationUpdateStartEvent(0); + const sal_Int32 gnConfigurationUpdateEndEvent(1); +} + +namespace sd::framework { + +/** Store URL, XPane reference and (local) PaneId for every pane factory + that is registered at the PaneController. +*/ +class BasicPaneFactory::PaneDescriptor +{ +public: + OUString msPaneURL; + Reference<XResource> mxPane; + PaneId mePaneId; + /** The mbReleased flag is set when the pane has been released. Some + panes are just hidden and destroyed. When the pane is reused this + flag is reset. + */ + bool mbIsReleased; + + bool CompareURL(std::u16string_view rsPaneURL) const { return msPaneURL == rsPaneURL; } + bool ComparePane(const Reference<XResource>& rxPane) const { return mxPane == rxPane; } +}; + +class BasicPaneFactory::PaneContainer + : public ::std::vector<PaneDescriptor> +{ +public: + PaneContainer() {} +}; + +//===== PaneFactory =========================================================== + +BasicPaneFactory::BasicPaneFactory ( + const Reference<XComponentContext>& rxContext) + : mxComponentContext(rxContext), + mpViewShellBase(nullptr), + mpPaneContainer(new PaneContainer) +{ +} + +BasicPaneFactory::~BasicPaneFactory() +{ +} + +void BasicPaneFactory::disposing(std::unique_lock<std::mutex>&) +{ + Reference<XConfigurationController> xCC (mxConfigurationControllerWeak); + if (xCC.is()) + { + xCC->removeResourceFactoryForReference(this); + xCC->removeConfigurationChangeListener(this); + mxConfigurationControllerWeak.clear(); + } + + for (const auto& rDescriptor : *mpPaneContainer) + { + if (rDescriptor.mbIsReleased) + { + Reference<XComponent> xComponent (rDescriptor.mxPane, UNO_QUERY); + if (xComponent.is()) + { + xComponent->removeEventListener(this); + xComponent->dispose(); + } + } + } +} + +void SAL_CALL BasicPaneFactory::initialize (const Sequence<Any>& aArguments) +{ + if (!aArguments.hasElements()) + return; + + try + { + // Get the XController from the first argument. + Reference<frame::XController> xController (aArguments[0], UNO_QUERY_THROW); + + // Tunnel through the controller to obtain access to the ViewShellBase. + try + { + Reference<lang::XUnoTunnel> xTunnel (xController, UNO_QUERY_THROW); + if (auto pController = comphelper::getFromUnoTunnel<DrawController>(xTunnel)) + mpViewShellBase = pController->GetViewShellBase(); + } + catch(RuntimeException&) + {} + + Reference<XControllerManager> xCM (xController, UNO_QUERY_THROW); + Reference<XConfigurationController> xCC (xCM->getConfigurationController()); + mxConfigurationControllerWeak = xCC; + + // Add pane factories for the two left panes (one for Impress and one for + // Draw) and the center pane. + if (xController.is() && xCC.is()) + { + PaneDescriptor aDescriptor; + aDescriptor.msPaneURL = FrameworkHelper::msCenterPaneURL; + aDescriptor.mePaneId = CenterPaneId; + aDescriptor.mbIsReleased = false; + mpPaneContainer->push_back(aDescriptor); + xCC->addResourceFactory(aDescriptor.msPaneURL, this); + + aDescriptor.msPaneURL = FrameworkHelper::msFullScreenPaneURL; + aDescriptor.mePaneId = FullScreenPaneId; + mpPaneContainer->push_back(aDescriptor); + xCC->addResourceFactory(aDescriptor.msPaneURL, this); + + aDescriptor.msPaneURL = FrameworkHelper::msLeftImpressPaneURL; + aDescriptor.mePaneId = LeftImpressPaneId; + mpPaneContainer->push_back(aDescriptor); + xCC->addResourceFactory(aDescriptor.msPaneURL, this); + + aDescriptor.msPaneURL = FrameworkHelper::msLeftDrawPaneURL; + aDescriptor.mePaneId = LeftDrawPaneId; + mpPaneContainer->push_back(aDescriptor); + xCC->addResourceFactory(aDescriptor.msPaneURL, this); + } + + // Register as configuration change listener. + if (xCC.is()) + { + xCC->addConfigurationChangeListener( + this, + FrameworkHelper::msConfigurationUpdateStartEvent, + Any(gnConfigurationUpdateStartEvent)); + xCC->addConfigurationChangeListener( + this, + FrameworkHelper::msConfigurationUpdateEndEvent, + Any(gnConfigurationUpdateEndEvent)); + } + } + catch (RuntimeException&) + { + Reference<XConfigurationController> xCC (mxConfigurationControllerWeak); + if (xCC.is()) + xCC->removeResourceFactoryForReference(this); + } +} + +//===== XPaneFactory ========================================================== + +Reference<XResource> SAL_CALL BasicPaneFactory::createResource ( + const Reference<XResourceId>& rxPaneId) +{ + ThrowIfDisposed(); + + Reference<XResource> xPane; + + // Based on the ResourceURL of the given ResourceId look up the + // corresponding factory descriptor. + PaneContainer::iterator iDescriptor ( + ::std::find_if ( + mpPaneContainer->begin(), + mpPaneContainer->end(), + [&] (PaneDescriptor const& rPane) { + return rPane.CompareURL(rxPaneId->getResourceURL()); + } )); + + if (iDescriptor == mpPaneContainer->end()) + { + // The requested pane can not be created by any of the factories + // managed by the called BasicPaneFactory object. + throw lang::IllegalArgumentException("BasicPaneFactory::createPane() called for unknown resource id", + nullptr, + 0); + } + + if (iDescriptor->mxPane.is()) + { + // The pane has already been created and is still active (has + // not yet been released). This should not happen. + xPane = iDescriptor->mxPane; + } + else + { + // Create a new pane. + switch (iDescriptor->mePaneId) + { + case CenterPaneId: + xPane = CreateFrameWindowPane(rxPaneId); + break; + + case FullScreenPaneId: + xPane = CreateFullScreenPane(mxComponentContext, rxPaneId); + break; + + case LeftImpressPaneId: + case LeftDrawPaneId: + xPane = CreateChildWindowPane( + rxPaneId, + *iDescriptor); + break; + } + iDescriptor->mxPane = xPane; + + // Listen for the pane being disposed. + Reference<lang::XComponent> xComponent (xPane, UNO_QUERY); + if (xComponent.is()) + xComponent->addEventListener(this); + } + iDescriptor->mbIsReleased = false; + + + return xPane; +} + +void SAL_CALL BasicPaneFactory::releaseResource ( + const Reference<XResource>& rxPane) +{ + ThrowIfDisposed(); + + // Based on the given XPane reference look up the corresponding factory + // descriptor. + PaneContainer::iterator iDescriptor ( + ::std::find_if( + mpPaneContainer->begin(), + mpPaneContainer->end(), + [&] (PaneDescriptor const& rPane) { return rPane.ComparePane(rxPane); } )); + + if (iDescriptor == mpPaneContainer->end()) + { + // The given XPane reference is either empty or the pane was not + // created by any of the factories managed by the called + // BasicPaneFactory object. + throw lang::IllegalArgumentException("BasicPaneFactory::releasePane() called for pane that was not created by same factory.", + nullptr, + 0); + } + + // The given pane was created by one of the factories. Child + // windows are just hidden and will be reused when requested later. + // Other windows are disposed and their reference is reset so that + // on the next createPane() call for the same pane type the pane is + // created anew. + ChildWindowPane* pChildWindowPane = dynamic_cast<ChildWindowPane*>(rxPane.get()); + if (pChildWindowPane != nullptr) + { + iDescriptor->mbIsReleased = true; + pChildWindowPane->Hide(); + } + else + { + iDescriptor->mxPane = nullptr; + Reference<XComponent> xComponent (rxPane, UNO_QUERY); + if (xComponent.is()) + { + // We are disposing the pane and do not have to be informed of + // that. + xComponent->removeEventListener(this); + xComponent->dispose(); + } + } + +} + +//===== XConfigurationChangeListener ========================================== + +void SAL_CALL BasicPaneFactory::notifyConfigurationChange ( + const ConfigurationChangeEvent& /* rEvent */ ) +{ + // FIXME: nothing to do +} + +//===== lang::XEventListener ================================================== + +void SAL_CALL BasicPaneFactory::disposing ( + const lang::EventObject& rEventObject) +{ + if (mxConfigurationControllerWeak.get() == rEventObject.Source) + { + mxConfigurationControllerWeak.clear(); + } + else + { + // Has one of the panes been disposed? If so, then release the + // reference to that pane, but not the pane descriptor. + Reference<XResource> xPane (rEventObject.Source, UNO_QUERY); + PaneContainer::iterator iDescriptor ( + ::std::find_if ( + mpPaneContainer->begin(), + mpPaneContainer->end(), + [&] (PaneDescriptor const& rPane) { return rPane.ComparePane(xPane); } )); + if (iDescriptor != mpPaneContainer->end()) + { + iDescriptor->mxPane = nullptr; + } + } +} + +Reference<XResource> BasicPaneFactory::CreateFrameWindowPane ( + const Reference<XResourceId>& rxPaneId) +{ + Reference<XResource> xPane; + + if (mpViewShellBase != nullptr) + { + xPane = new FrameWindowPane(rxPaneId, mpViewShellBase->GetViewWindow()); + } + + return xPane; +} + +Reference<XResource> BasicPaneFactory::CreateFullScreenPane ( + const Reference<XComponentContext>& rxComponentContext, + const Reference<XResourceId>& rxPaneId) +{ + Reference<XResource> xPane ( + new FullScreenPane( + rxComponentContext, + rxPaneId, + mpViewShellBase->GetViewWindow())); + + return xPane; +} + +Reference<XResource> BasicPaneFactory::CreateChildWindowPane ( + const Reference<XResourceId>& rxPaneId, + const PaneDescriptor& rDescriptor) +{ + Reference<XResource> xPane; + + if (mpViewShellBase != nullptr) + { + // Create the corresponding shell and determine the id of the child window. + sal_uInt16 nChildWindowId = 0; + ::std::unique_ptr<SfxShell> pShell; + switch (rDescriptor.mePaneId) + { + case LeftImpressPaneId: + pShell.reset(new LeftImpressPaneShell()); + nChildWindowId = ::sd::LeftPaneImpressChildWindow::GetChildWindowId(); + break; + + case LeftDrawPaneId: + pShell.reset(new LeftDrawPaneShell()); + nChildWindowId = ::sd::LeftPaneDrawChildWindow::GetChildWindowId(); + break; + + default: + break; + } + + // With shell and child window id create the ChildWindowPane + // wrapper. + if (pShell != nullptr) + { + xPane = new ChildWindowPane( + rxPaneId, + nChildWindowId, + *mpViewShellBase, + std::move(pShell)); + } + } + + return xPane; +} + +void BasicPaneFactory::ThrowIfDisposed() const +{ + if (m_bDisposed) + { + throw lang::DisposedException ("BasicPaneFactory object has already been disposed", + const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this))); + } +} + +} // end of namespace sd::framework + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_framework_BasicPaneFactory_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new sd::framework::BasicPaneFactory(context)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/BasicPaneFactory.hxx b/sd/source/ui/framework/factories/BasicPaneFactory.hxx new file mode 100644 index 000000000..317776e48 --- /dev/null +++ b/sd/source/ui/framework/factories/BasicPaneFactory.hxx @@ -0,0 +1,131 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/drawing/framework/XResourceFactory.hpp> +#include <com/sun/star/drawing/framework/XConfigurationChangeListener.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <comphelper/compbase.hxx> +#include <cppuhelper/weakref.hxx> + +#include <memory> + +namespace com::sun::star::drawing::framework { class XConfigurationController; } +namespace com::sun::star::uno { class XComponentContext; } + +namespace sd { + +class ViewShellBase; +} + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < + css::lang::XInitialization, + css::drawing::framework::XResourceFactory, + css::drawing::framework::XConfigurationChangeListener + > BasicPaneFactoryInterfaceBase; + +/** This factory provides the frequently used standard panes + private:resource/pane/CenterPane + private:resource/pane/FullScreenPane + private:resource/pane/LeftImpressPane + private:resource/pane/LeftDrawPane + There are two left panes because this is (seems to be) the only way to + show different titles for the left pane in Draw and Impress. +*/ +class BasicPaneFactory + : public BasicPaneFactoryInterfaceBase +{ +public: + explicit BasicPaneFactory ( + const css::uno::Reference<css::uno::XComponentContext>& rxContext); + virtual ~BasicPaneFactory() override; + + virtual void disposing(std::unique_lock<std::mutex>&) override; + + // XInitialization + + virtual void SAL_CALL initialize( + const css::uno::Sequence<css::uno::Any>& aArguments) override; + + // XResourceFactory + + virtual css::uno::Reference<css::drawing::framework::XResource> + SAL_CALL createResource ( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxPaneId) override; + + virtual void SAL_CALL + releaseResource ( + const css::uno::Reference<css::drawing::framework::XResource>& rxPane) override; + + // XConfigurationChangeListener + + virtual void SAL_CALL notifyConfigurationChange ( + const css::drawing::framework::ConfigurationChangeEvent& rEvent) override; + + // lang::XEventListener + + virtual void SAL_CALL disposing ( + const css::lang::EventObject& rEventObject) override; + +private: + css::uno::Reference<css::uno::XComponentContext> mxComponentContext; + css::uno::WeakReference<css::drawing::framework::XConfigurationController> + mxConfigurationControllerWeak; + ViewShellBase* mpViewShellBase; + class PaneDescriptor; + class PaneContainer; + std::unique_ptr<PaneContainer> mpPaneContainer; + + /** Create a new instance of FrameWindowPane. + @param rPaneId + There is only one frame window so this id is just checked to + have the correct value. + */ + css::uno::Reference<css::drawing::framework::XResource> + CreateFrameWindowPane ( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxPaneId); + + /** Create a new pane that represents the center pane in full screen + mode. + */ + css::uno::Reference<css::drawing::framework::XResource> + CreateFullScreenPane ( + const css::uno::Reference<css::uno::XComponentContext>& rxComponentContext, + const css::uno::Reference<css::drawing::framework::XResourceId>& rxPaneId); + + /** Create a new instance of ChildWindowPane. + @param rPaneId + The ResourceURL member defines which side pane to create. + */ + css::uno::Reference<css::drawing::framework::XResource> + CreateChildWindowPane ( + const css::uno::Reference< + css::drawing::framework::XResourceId>& rxPaneId, + const PaneDescriptor& rDescriptor); + + /// @throws css::lang::DisposedException + void ThrowIfDisposed() const; +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/BasicToolBarFactory.cxx b/sd/source/ui/framework/factories/BasicToolBarFactory.cxx new file mode 100644 index 000000000..af79a88ea --- /dev/null +++ b/sd/source/ui/framework/factories/BasicToolBarFactory.cxx @@ -0,0 +1,161 @@ +/* -*- 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 "BasicToolBarFactory.hxx" + +#include <ViewTabBar.hxx> +#include <framework/FrameworkHelper.hxx> +#include <unotools/mediadescriptor.hxx> + +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/drawing/framework/XConfigurationController.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/drawing/framework/XControllerManager.hpp> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::drawing::framework; + +namespace sd::framework { + +//===== BasicToolBarFactory =================================================== + +BasicToolBarFactory::BasicToolBarFactory () +{ +} + +BasicToolBarFactory::~BasicToolBarFactory() +{ +} + +void BasicToolBarFactory::disposing(std::unique_lock<std::mutex>&) +{ + Shutdown(); +} + +void BasicToolBarFactory::Shutdown() +{ + Reference<lang::XComponent> xComponent (mxConfigurationController, UNO_QUERY); + if (xComponent.is()) + xComponent->removeEventListener(static_cast<lang::XEventListener*>(this)); + if (mxConfigurationController.is()) + { + mxConfigurationController->removeResourceFactoryForReference(this); + mxConfigurationController = nullptr; + } +} + +//----- XInitialization ------------------------------------------------------- + +void SAL_CALL BasicToolBarFactory::initialize (const Sequence<Any>& aArguments) +{ + if (!aArguments.hasElements()) + return; + + try + { + // Get the XController from the first argument. + mxController.set(aArguments[0], UNO_QUERY_THROW); + + utl::MediaDescriptor aDescriptor (mxController->getModel()->getArgs()); + if ( ! aDescriptor.getUnpackedValueOrDefault( + utl::MediaDescriptor::PROP_PREVIEW, + false)) + { + // Register the factory for its supported tool bars. + Reference<XControllerManager> xControllerManager(mxController, UNO_QUERY_THROW); + mxConfigurationController = xControllerManager->getConfigurationController(); + if (mxConfigurationController.is()) + { + mxConfigurationController->addResourceFactory( + FrameworkHelper::msViewTabBarURL, this); + } + + Reference<lang::XComponent> xComponent (mxConfigurationController, UNO_QUERY); + if (xComponent.is()) + xComponent->addEventListener(static_cast<lang::XEventListener*>(this)); + } + else + { + // The view shell is in preview mode and thus does not need + // the view tab bar. + mxConfigurationController = nullptr; + } + } + catch (RuntimeException&) + { + Shutdown(); + throw; + } +} + +//----- lang::XEventListener -------------------------------------------------- + +void SAL_CALL BasicToolBarFactory::disposing ( + const lang::EventObject& rEventObject) +{ + if (rEventObject.Source == mxConfigurationController) + mxConfigurationController = nullptr; +} + +//===== XPaneFactory ========================================================== + +Reference<XResource> SAL_CALL BasicToolBarFactory::createResource ( + const Reference<XResourceId>& rxToolBarId) +{ + ThrowIfDisposed(); + + if (rxToolBarId->getResourceURL() != FrameworkHelper::msViewTabBarURL) + throw lang::IllegalArgumentException(); + + Reference<XResource> xToolBar = new ViewTabBar(rxToolBarId, mxController); + return xToolBar; +} + +void SAL_CALL BasicToolBarFactory::releaseResource ( + const Reference<XResource>& rxToolBar) +{ + ThrowIfDisposed(); + + Reference<XComponent> xComponent (rxToolBar, UNO_QUERY); + if (xComponent.is()) + xComponent->dispose(); +} + +void BasicToolBarFactory::ThrowIfDisposed() const +{ + if (m_bDisposed) + { + throw lang::DisposedException ("BasicToolBarFactory object has already been disposed", + const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this))); + } +} + +} // end of namespace sd::framework + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_framework_BasicToolBarFactory_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new sd::framework::BasicToolBarFactory); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/BasicToolBarFactory.hxx b/sd/source/ui/framework/factories/BasicToolBarFactory.hxx new file mode 100644 index 000000000..fdaf92788 --- /dev/null +++ b/sd/source/ui/framework/factories/BasicToolBarFactory.hxx @@ -0,0 +1,84 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/drawing/framework/XResourceFactory.hpp> +#include <comphelper/compbase.hxx> + +namespace com::sun::star::frame { class XController; } +namespace com::sun::star::drawing::framework { class XResourceId; } +namespace com::sun::star::drawing::framework { class XConfigurationController; } + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XResourceFactory, + css::lang::XInitialization, + css::lang::XEventListener + > BasicToolBarFactoryInterfaceBase; + +/** This factory provides some of the frequently used tool bars: + private:resource/toolbar/ViewTabBar +*/ +class BasicToolBarFactory + : public BasicToolBarFactoryInterfaceBase +{ +public: + BasicToolBarFactory (); + virtual ~BasicToolBarFactory() override; + + virtual void disposing(std::unique_lock<std::mutex>&) override; + + // ToolBarFactory + + virtual css::uno::Reference<css::drawing::framework::XResource> SAL_CALL + createResource ( + const css::uno::Reference< + css::drawing::framework::XResourceId>& rxToolBarId) override; + + virtual void SAL_CALL + releaseResource ( + const css::uno::Reference<css::drawing::framework::XResource>& + rxToolBar) override; + + // XInitialization + + virtual void SAL_CALL initialize( + const css::uno::Sequence<css::uno::Any>& aArguments) override; + + // lang::XEventListener + + virtual void SAL_CALL disposing ( + const css::lang::EventObject& rEventObject) override; + +private: + css::uno::Reference<css::drawing::framework::XConfigurationController> mxConfigurationController; + css::uno::Reference<css::frame::XController> mxController; + + void Shutdown(); + + /// @throws css::lang::DisposedException + void ThrowIfDisposed() const; +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/BasicViewFactory.cxx b/sd/source/ui/framework/factories/BasicViewFactory.cxx new file mode 100644 index 000000000..425cb4446 --- /dev/null +++ b/sd/source/ui/framework/factories/BasicViewFactory.cxx @@ -0,0 +1,518 @@ +/* -*- 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 "BasicViewFactory.hxx" + +#include <framework/ViewShellWrapper.hxx> +#include <framework/FrameworkHelper.hxx> +#include <com/sun/star/drawing/framework/XControllerManager.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <framework/Pane.hxx> +#include <DrawController.hxx> +#include <ViewShellBase.hxx> +#include <ViewShellManager.hxx> +#include <DrawDocShell.hxx> +#include <DrawViewShell.hxx> +#include <GraphicViewShell.hxx> +#include <OutlineViewShell.hxx> +#include <PresentationViewShell.hxx> +#include <SlideSorterViewShell.hxx> +#include <FrameView.hxx> +#include <Window.hxx> + +#include <comphelper/servicehelper.hxx> +#include <sfx2/viewfrm.hxx> +#include <vcl/wrkwin.hxx> +#include <toolkit/helper/vclunohelper.hxx> + + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::drawing::framework; + +using ::sd::framework::FrameworkHelper; + +namespace sd::framework { + +//===== ViewDescriptor ======================================================== + +class BasicViewFactory::ViewDescriptor +{ +public: + Reference<XResource> mxView; + std::shared_ptr<sd::ViewShell> mpViewShell; + Reference<XResourceId> mxViewId; + static bool CompareView (const std::shared_ptr<ViewDescriptor>& rpDescriptor, + const Reference<XResource>& rxView) + { return rpDescriptor->mxView.get() == rxView.get(); } +}; + +//===== BasicViewFactory::ViewShellContainer ================================== + +class BasicViewFactory::ViewShellContainer + : public ::std::vector<std::shared_ptr<ViewDescriptor> > +{ +public: + ViewShellContainer() {}; +}; + +class BasicViewFactory::ViewCache + : public ::std::vector<std::shared_ptr<ViewDescriptor> > +{ +public: + ViewCache() {}; +}; + +//===== ViewFactory =========================================================== + +BasicViewFactory::BasicViewFactory () + : mpViewShellContainer(new ViewShellContainer()), + mpBase(nullptr), + mpFrameView(nullptr), + mpWindow(VclPtr<WorkWindow>::Create(nullptr,WB_STDWORK)), + mpViewCache(std::make_shared<ViewCache>()), + mxLocalPane(new Pane(Reference<XResourceId>(), mpWindow.get())) +{ +} + +BasicViewFactory::~BasicViewFactory() +{ +} + +void BasicViewFactory::disposing(std::unique_lock<std::mutex>&) +{ + // Disconnect from the frame view. + if (mpFrameView != nullptr) + { + mpFrameView->Disconnect(); + mpFrameView = nullptr; + } + + // Release the view cache. + for (const auto& rxView : *mpViewCache) + { + ReleaseView(rxView, true); + } + + // Release the view shell container. At this point no one other than us + // should hold references to the view shells (at the moment this is a + // trivial requirement, because no one other than us holds a shared + // pointer). + // ViewShellContainer::const_iterator iView; + for (const auto& rxView : *mpViewShellContainer) + { + OSL_ASSERT(rxView->mpViewShell.use_count() == 1); + } + mpViewShellContainer.reset(); +} + +Reference<XResource> SAL_CALL BasicViewFactory::createResource ( + const Reference<XResourceId>& rxViewId) +{ + Reference<XResource> xView; + const bool bIsCenterPane ( + rxViewId->isBoundToURL(FrameworkHelper::msCenterPaneURL, AnchorBindingMode_DIRECT)); + + // Get the pane for the anchor URL. + Reference<XPane> xPane; + if (mxConfigurationController.is()) + xPane.set(mxConfigurationController->getResource(rxViewId->getAnchor()), UNO_QUERY); + + // For main views use the frame view of the last main view. + ::sd::FrameView* pFrameView = nullptr; + if (xPane.is() && bIsCenterPane) + { + pFrameView = mpFrameView; + } + + // Get Window pointer for XWindow of the pane. + vcl::Window* pWindow = nullptr; + if (xPane.is()) + pWindow = VCLUnoHelper::GetWindow(xPane->getWindow()); + + // Get the view frame. + SfxViewFrame* pFrame = nullptr; + if (mpBase != nullptr) + pFrame = mpBase->GetViewFrame(); + + if (pFrame != nullptr && mpBase!=nullptr && pWindow!=nullptr) + { + // Try to get the view from the cache. + std::shared_ptr<ViewDescriptor> pDescriptor (GetViewFromCache(rxViewId, xPane)); + + // When the requested view is not in the cache then create a new view. + if (pDescriptor == nullptr) + { + pDescriptor = CreateView(rxViewId, *pFrame, *pWindow, xPane, pFrameView, bIsCenterPane); + } + + if (pDescriptor != nullptr) + xView = pDescriptor->mxView; + + mpViewShellContainer->push_back(pDescriptor); + + if (bIsCenterPane) + ActivateCenterView(pDescriptor); + else + pWindow->Resize(); + } + + return xView; +} + +void SAL_CALL BasicViewFactory::releaseResource (const Reference<XResource>& rxView) +{ + if ( ! rxView.is()) + throw lang::IllegalArgumentException(); + + if (!rxView.is() || !mpBase) + return; + + ViewShellContainer::iterator iViewShell ( + ::std::find_if( + mpViewShellContainer->begin(), + mpViewShellContainer->end(), + [&] (std::shared_ptr<ViewDescriptor> const& pVD) { + return ViewDescriptor::CompareView(pVD, rxView); + } )); + if (iViewShell == mpViewShellContainer->end()) + { + throw lang::IllegalArgumentException(); + } + + std::shared_ptr<ViewShell> pViewShell ((*iViewShell)->mpViewShell); + + if ((*iViewShell)->mxViewId->isBoundToURL( + FrameworkHelper::msCenterPaneURL, AnchorBindingMode_DIRECT)) + { + // Obtain a pointer to and connect to the frame view of the + // view. The next view, that is created, will be + // initialized with this frame view. + if (mpFrameView == nullptr) + { + mpFrameView = pViewShell->GetFrameView(); + if (mpFrameView) + mpFrameView->Connect(); + } + + // With the view in the center pane the sub controller is + // released, too. + mpBase->GetDrawController().SetSubController( + Reference<drawing::XDrawSubController>()); + + SfxViewShell* pSfxViewShell = pViewShell->GetViewShell(); + if (pSfxViewShell != nullptr) + pSfxViewShell->DisconnectAllClients(); + } + + ReleaseView(*iViewShell, false); + + mpViewShellContainer->erase(iViewShell); +} + +void SAL_CALL BasicViewFactory::initialize (const Sequence<Any>& aArguments) +{ + if (!aArguments.hasElements()) + return; + + try + { + // Get the XController from the first argument. + Reference<frame::XController> xController (aArguments[0], UNO_QUERY_THROW); + + // Tunnel through the controller to obtain a ViewShellBase. + Reference<lang::XUnoTunnel> xTunnel (xController, UNO_QUERY_THROW); + ::sd::DrawController* pController = comphelper::getFromUnoTunnel<sd::DrawController>(xTunnel); + if (pController != nullptr) + mpBase = pController->GetViewShellBase(); + + // Register the factory for its supported views. + Reference<XControllerManager> xCM (xController,UNO_QUERY_THROW); + mxConfigurationController = xCM->getConfigurationController(); + if ( ! mxConfigurationController.is()) + throw RuntimeException(); + mxConfigurationController->addResourceFactory(FrameworkHelper::msImpressViewURL, this); + mxConfigurationController->addResourceFactory(FrameworkHelper::msDrawViewURL, this); + mxConfigurationController->addResourceFactory(FrameworkHelper::msOutlineViewURL, this); + mxConfigurationController->addResourceFactory(FrameworkHelper::msNotesViewURL, this); + mxConfigurationController->addResourceFactory(FrameworkHelper::msHandoutViewURL, this); + mxConfigurationController->addResourceFactory(FrameworkHelper::msPresentationViewURL, this); + mxConfigurationController->addResourceFactory(FrameworkHelper::msSlideSorterURL, this); + } + catch (RuntimeException&) + { + mpBase = nullptr; + if (mxConfigurationController.is()) + mxConfigurationController->removeResourceFactoryForReference(this); + throw; + } +} + +std::shared_ptr<BasicViewFactory::ViewDescriptor> BasicViewFactory::CreateView ( + const Reference<XResourceId>& rxViewId, + SfxViewFrame& rFrame, + vcl::Window& rWindow, + const Reference<XPane>& rxPane, + FrameView* pFrameView, + const bool bIsCenterPane) +{ + auto pDescriptor = std::make_shared<ViewDescriptor>(); + + pDescriptor->mpViewShell = CreateViewShell( + rxViewId, + rFrame, + rWindow, + pFrameView); + pDescriptor->mxViewId = rxViewId; + + if (pDescriptor->mpViewShell != nullptr) + { + pDescriptor->mpViewShell->Init(bIsCenterPane); + mpBase->GetViewShellManager()->ActivateViewShell(pDescriptor->mpViewShell.get()); + + Reference<awt::XWindow> xWindow(rxPane->getWindow()); + rtl::Reference<ViewShellWrapper> wrapper(new ViewShellWrapper( + pDescriptor->mpViewShell, + rxViewId, + xWindow)); + + // register ViewShellWrapper on pane window + if (xWindow.is()) + { + xWindow->addWindowListener(wrapper); + if (pDescriptor->mpViewShell != nullptr) + { + pDescriptor->mpViewShell->Resize(); + } + } + + pDescriptor->mxView = wrapper.get(); + } + + return pDescriptor; +} + +std::shared_ptr<ViewShell> BasicViewFactory::CreateViewShell ( + const Reference<XResourceId>& rxViewId, + SfxViewFrame& rFrame, + vcl::Window& rWindow, + FrameView* pFrameView) +{ + std::shared_ptr<ViewShell> pViewShell; + const OUString& rsViewURL (rxViewId->getResourceURL()); + if (rsViewURL == FrameworkHelper::msImpressViewURL) + { + pViewShell = + std::make_shared<DrawViewShell>( + *mpBase, + &rWindow, + PageKind::Standard, + pFrameView); + pViewShell->GetContentWindow()->set_id("impress_win"); + } + else if (rsViewURL == FrameworkHelper::msDrawViewURL) + { + pViewShell = std::shared_ptr<GraphicViewShell>( + new GraphicViewShell(*mpBase, &rWindow, pFrameView), + o3tl::default_delete<GraphicViewShell>()); + pViewShell->GetContentWindow()->set_id("draw_win"); + } + else if (rsViewURL == FrameworkHelper::msOutlineViewURL) + { + pViewShell = + std::make_shared<OutlineViewShell>( + &rFrame, + *mpBase, + &rWindow, + pFrameView); + pViewShell->GetContentWindow()->set_id("outline_win"); + } + else if (rsViewURL == FrameworkHelper::msNotesViewURL) + { + pViewShell = + std::make_shared<DrawViewShell>( + *mpBase, + &rWindow, + PageKind::Notes, + pFrameView); + pViewShell->GetContentWindow()->set_id("notes_win"); + } + else if (rsViewURL == FrameworkHelper::msHandoutViewURL) + { + pViewShell = + std::make_shared<DrawViewShell>( + *mpBase, + &rWindow, + PageKind::Handout, + pFrameView); + pViewShell->GetContentWindow()->set_id("handout_win"); + } + else if (rsViewURL == FrameworkHelper::msPresentationViewURL) + { + pViewShell = + std::make_shared<PresentationViewShell>( + *mpBase, + &rWindow, + pFrameView); + pViewShell->GetContentWindow()->set_id("presentation_win"); + } + else if (rsViewURL == FrameworkHelper::msSlideSorterURL) + { + pViewShell = ::sd::slidesorter::SlideSorterViewShell::Create ( + &rFrame, + *mpBase, + &rWindow, + pFrameView); + pViewShell->GetContentWindow()->set_id("slidesorter"); + } + + return pViewShell; +} + +void BasicViewFactory::ReleaseView ( + const std::shared_ptr<ViewDescriptor>& rpDescriptor, + bool bDoNotCache) +{ + bool bIsCacheable (!bDoNotCache && IsCacheable(rpDescriptor)); + + if (bIsCacheable) + { + Reference<XRelocatableResource> xResource (rpDescriptor->mxView, UNO_QUERY); + if (xResource.is()) + { + if (mxLocalPane.is()) + if (xResource->relocateToAnchor(mxLocalPane)) + mpViewCache->push_back(rpDescriptor); + else + bIsCacheable = false; + else + bIsCacheable = false; + } + else + { + bIsCacheable = false; + } + } + + if ( ! bIsCacheable) + { + // Shut down the current view shell. + rpDescriptor->mpViewShell->Shutdown (); + mpBase->GetDocShell()->Disconnect(rpDescriptor->mpViewShell.get()); + mpBase->GetViewShellManager()->DeactivateViewShell(rpDescriptor->mpViewShell.get()); + + Reference<XComponent> xComponent (rpDescriptor->mxView, UNO_QUERY); + if (xComponent.is()) + xComponent->dispose(); + } +} + +bool BasicViewFactory::IsCacheable (const std::shared_ptr<ViewDescriptor>& rpDescriptor) +{ + bool bIsCacheable (false); + + Reference<XRelocatableResource> xResource (rpDescriptor->mxView, UNO_QUERY); + if (xResource.is()) + { + static ::std::vector<Reference<XResourceId> > s_aCacheableResources = [&]() + { + ::std::vector<Reference<XResourceId> > tmp; + FrameworkHelper::Instance(*mpBase); + + // The slide sorter and the task panel are cacheable and relocatable. + tmp.push_back(FrameworkHelper::CreateResourceId( + FrameworkHelper::msSlideSorterURL, FrameworkHelper::msLeftDrawPaneURL)); + tmp.push_back(FrameworkHelper::CreateResourceId( + FrameworkHelper::msSlideSorterURL, FrameworkHelper::msLeftImpressPaneURL)); + return tmp; + }(); + + bIsCacheable = std::any_of(s_aCacheableResources.begin(), s_aCacheableResources.end(), + [&rpDescriptor](const Reference<XResourceId>& rxId) { return rxId->compareTo(rpDescriptor->mxViewId) == 0; }); + } + + return bIsCacheable; +} + +std::shared_ptr<BasicViewFactory::ViewDescriptor> BasicViewFactory::GetViewFromCache ( + const Reference<XResourceId>& rxViewId, + const Reference<XPane>& rxPane) +{ + std::shared_ptr<ViewDescriptor> pDescriptor; + + // Search for the requested view in the cache. + ViewCache::iterator iEntry = std::find_if(mpViewCache->begin(), mpViewCache->end(), + [&rxViewId](const ViewCache::value_type& rxEntry) { return rxEntry->mxViewId->compareTo(rxViewId) == 0; }); + if (iEntry != mpViewCache->end()) + { + pDescriptor = *iEntry; + mpViewCache->erase(iEntry); + } + + // When the view has been found then relocate it to the given pane and + // remove it from the cache. + if (pDescriptor != nullptr) + { + bool bRelocationSuccessful (false); + Reference<XRelocatableResource> xResource (pDescriptor->mxView, UNO_QUERY); + if (xResource.is() && rxPane.is()) + { + if (xResource->relocateToAnchor(rxPane)) + bRelocationSuccessful = true; + } + + if ( ! bRelocationSuccessful) + { + ReleaseView(pDescriptor, true); + pDescriptor.reset(); + } + } + + return pDescriptor; +} + +void BasicViewFactory::ActivateCenterView ( + const std::shared_ptr<ViewDescriptor>& rpDescriptor) +{ + mpBase->GetDocShell()->Connect(rpDescriptor->mpViewShell.get()); + + // During the creation of the new sub-shell, resize requests were not + // forwarded to it because it was not yet registered. Therefore, we + // have to request a resize now. + rpDescriptor->mpViewShell->UIFeatureChanged(); + if (mpBase->GetDocShell()->IsInPlaceActive()) + mpBase->GetViewFrame()->Resize(true); + + mpBase->GetDrawController().SetSubController( + rpDescriptor->mpViewShell->CreateSubController()); +} + +} // end of namespace sd::framework + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_framework_BasicViewFactory_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new sd::framework::BasicViewFactory); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/BasicViewFactory.hxx b/sd/source/ui/framework/factories/BasicViewFactory.hxx new file mode 100644 index 000000000..ccd5cbbda --- /dev/null +++ b/sd/source/ui/framework/factories/BasicViewFactory.hxx @@ -0,0 +1,129 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/drawing/framework/XResourceFactory.hpp> +#include <com/sun/star/lang/XInitialization.hpp> + +#include <comphelper/compbase.hxx> + +#include <vcl/vclptr.hxx> +#include <memory> + +namespace com::sun::star::drawing::framework { class XConfigurationController; } +namespace com::sun::star::drawing::framework { class XPane; } + +namespace sd { +class ViewShell; +class ViewShellBase; +class FrameView; +} +class SfxViewFrame; +namespace vcl { class Window; } + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XResourceFactory, + css::lang::XInitialization + > BasicViewFactoryInterfaceBase; + +/** Factory for the frequently used standard views of the drawing framework: + private:resource/view/ + private:resource/view/ImpressView + private:resource/view/GraphicView + private:resource/view/OutlineView + private:resource/view/NotesView + private:resource/view/HandoutView + private:resource/view/SlideSorter + private:resource/view/PresentationView + private:resource/view/TaskPane + For some views in some panes this class also acts as a cache. +*/ +class BasicViewFactory + : public BasicViewFactoryInterfaceBase +{ +public: + BasicViewFactory (); + virtual ~BasicViewFactory() override; + + virtual void disposing(std::unique_lock<std::mutex>&) override; + + // XViewFactory + + virtual css::uno::Reference<css::drawing::framework::XResource> + SAL_CALL createResource ( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxViewId) override; + + virtual void SAL_CALL releaseResource ( + const css::uno::Reference<css::drawing::framework::XResource>& xView) override; + + // XInitialization + + virtual void SAL_CALL initialize( + const css::uno::Sequence<css::uno::Any>& aArguments) override; + +private: + css::uno::Reference<css::drawing::framework::XConfigurationController> + mxConfigurationController; + class ViewDescriptor; + class ViewShellContainer; + std::unique_ptr<ViewShellContainer> mpViewShellContainer; + ViewShellBase* mpBase; + FrameView* mpFrameView; + + class ViewCache; + ScopedVclPtr<vcl::Window> mpWindow; + std::shared_ptr<ViewCache> mpViewCache; + + css::uno::Reference<css::drawing::framework::XPane> mxLocalPane; + + std::shared_ptr<ViewDescriptor> CreateView ( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxViewId, + SfxViewFrame& rFrame, + vcl::Window& rWindow, + const css::uno::Reference<css::drawing::framework::XPane>& rxPane, + FrameView* pFrameView, + const bool bIsCenterView); + + std::shared_ptr<ViewShell> CreateViewShell ( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxViewId, + SfxViewFrame& rFrame, + vcl::Window& rWindow, + FrameView* pFrameView); + + void ActivateCenterView ( + const std::shared_ptr<ViewDescriptor>& rpDescriptor); + + void ReleaseView ( + const std::shared_ptr<ViewDescriptor>& rpDescriptor, + bool bDoNotCache); + + bool IsCacheable ( + const std::shared_ptr<ViewDescriptor>& rpDescriptor); + + std::shared_ptr<ViewDescriptor> GetViewFromCache ( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxViewId, + const css::uno::Reference<css::drawing::framework::XPane>& rxPane); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/ChildWindowPane.cxx b/sd/source/ui/framework/factories/ChildWindowPane.cxx new file mode 100644 index 000000000..136f6fcb7 --- /dev/null +++ b/sd/source/ui/framework/factories/ChildWindowPane.cxx @@ -0,0 +1,219 @@ +/* -*- 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 <memory> +#include <sal/config.h> +#include <sal/log.hxx> + +#include <utility> + +#include "ChildWindowPane.hxx" + +#include <PaneDockingWindow.hxx> +#include <ViewShellBase.hxx> +#include <ViewShellManager.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <sfx2/viewfrm.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace sd::framework { + +ChildWindowPane::ChildWindowPane ( + const Reference<XResourceId>& rxPaneId, + sal_uInt16 nChildWindowId, + ViewShellBase& rViewShellBase, + ::std::unique_ptr<SfxShell> && pShell) + : ChildWindowPaneInterfaceBase(rxPaneId,nullptr), + mnChildWindowId(nChildWindowId), + mrViewShellBase(rViewShellBase), + mpShell(std::move(pShell)), + mbHasBeenActivated(false) +{ + mrViewShellBase.GetViewShellManager()->ActivateShell(mpShell.get()); + + SfxViewFrame* pViewFrame = mrViewShellBase.GetViewFrame(); + if (pViewFrame == nullptr) + return; + + if (mrViewShellBase.IsActive()) + { + if (pViewFrame->KnowsChildWindow(mnChildWindowId)) + { + if (pViewFrame->HasChildWindow(mnChildWindowId)) + { + // The ViewShellBase has already been activated. Make + // the child window visible as soon as possible. + pViewFrame->SetChildWindow(mnChildWindowId, true); + } + else + { + // The window is created asynchronously. Rely on the + // ConfigurationUpdater to try another update, and with + // that another request for this window, in a short + // time. + } + } + else + { + SAL_WARN("sd", "ChildWindowPane:not known"); + } + } + else + { + // The ViewShellBase has not yet been activated. Hide the + // window and wait a little before it is made visible. See + // comments in the GetWindow() method for an explanation. + pViewFrame->SetChildWindow(mnChildWindowId, false); + } +} + +ChildWindowPane::~ChildWindowPane() +{ +} + +void ChildWindowPane::Hide() +{ + SfxViewFrame* pViewFrame = mrViewShellBase.GetViewFrame(); + if (pViewFrame != nullptr) + if (pViewFrame->KnowsChildWindow(mnChildWindowId)) + if (pViewFrame->HasChildWindow(mnChildWindowId)) + pViewFrame->SetChildWindow(mnChildWindowId, false); + + // Release the window because when the child window is shown again it + // may use a different window. + mxWindow = nullptr; +} + +void SAL_CALL ChildWindowPane::disposing() +{ + ::osl::MutexGuard aGuard (m_aMutex); + + mrViewShellBase.GetViewShellManager()->DeactivateShell(mpShell.get()); + mpShell.reset(); + + if (mxWindow.is()) + { + mxWindow->removeEventListener(this); + } + + Pane::disposing(); +} + +vcl::Window* ChildWindowPane::GetWindow() +{ + do + { + if (mxWindow.is()) + // Window already exists => nothing to do. + break; + + // When the window is not yet present then obtain it only when the + // shell has already been activated. The activation is not + // necessary for the code to work properly but is used to optimize + // the layouting and displaying of the window. When it is made + // visible too early then some layouting seems to be made twice or at + // an inconvenient time and the overall process of initializing the + // Impress takes longer. + if (!mbHasBeenActivated && mpShell != nullptr && !mpShell->IsActive()) + break; + + mbHasBeenActivated = true; + SfxViewFrame* pViewFrame = mrViewShellBase.GetViewFrame(); + if (pViewFrame == nullptr) + break; + // The view frame has to know the child window. This can be the + // case, when for example the document is in read-only mode: the + // task pane is then not available. + if ( ! pViewFrame->KnowsChildWindow(mnChildWindowId)) + break; + + pViewFrame->SetChildWindow(mnChildWindowId, true); + SfxChildWindow* pChildWindow = pViewFrame->GetChildWindow(mnChildWindowId); + if (pChildWindow == nullptr) + if (pViewFrame->HasChildWindow(mnChildWindowId)) + { + // The child window is not yet visible. Ask the view frame + // to show it and try again to get access to the child + // window. + pViewFrame->ShowChildWindow(mnChildWindowId); + pChildWindow = pViewFrame->GetChildWindow(mnChildWindowId); + } + + // When the child window is still not visible then we have to try later. + if (pChildWindow == nullptr) + break; + + // From the child window get the docking window and from that the + // content window that is the container for the actual content. + PaneDockingWindow* pDockingWindow = dynamic_cast<PaneDockingWindow*>( + pChildWindow->GetWindow()); + if (pDockingWindow == nullptr) + break; + + // At last, we have access to the window and its UNO wrapper. + mpWindow = &pDockingWindow->GetContentWindow(); + mxWindow = VCLUnoHelper::GetInterface(mpWindow); + + // Register as window listener to be informed when the child window + // is hidden. + if (mxWindow.is()) + mxWindow->addEventListener(this); + } + while (false); + + return mpWindow; +} + +Reference<awt::XWindow> SAL_CALL ChildWindowPane::getWindow() +{ + if (mpWindow == nullptr || ! mxWindow.is()) + GetWindow(); + return Pane::getWindow(); +} + +IMPLEMENT_FORWARD_XINTERFACE2( + ChildWindowPane, + ChildWindowPaneInterfaceBase, + Pane); +IMPLEMENT_FORWARD_XTYPEPROVIDER2( + ChildWindowPane, + ChildWindowPaneInterfaceBase, + Pane); + +//----- XEventListener -------------------------------------------------------- + +void SAL_CALL ChildWindowPane::disposing (const lang::EventObject& rEvent) +{ + ThrowIfDisposed(); + + if (rEvent.Source == mxWindow) + { + // The window is gone but the pane remains alive. The next call to + // GetWindow() may create the window anew. + mxWindow = nullptr; + mpWindow = nullptr; + } +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/ChildWindowPane.hxx b/sd/source/ui/framework/factories/ChildWindowPane.hxx new file mode 100644 index 000000000..082177757 --- /dev/null +++ b/sd/source/ui/framework/factories/ChildWindowPane.hxx @@ -0,0 +1,101 @@ +/* -*- 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 . + */ + +#pragma once + +#include <framework/Pane.hxx> + +#include <com/sun/star/lang/XEventListener.hpp> +#include <cppuhelper/implbase.hxx> +#include <comphelper/uno3.hxx> +#include <memory> + +class SfxShell; + +namespace sd { class ViewShellBase; } +namespace com::sun::star::awt { class XWindow; } +namespace com::sun::star::drawing::framework { class XResourceId; } + +namespace sd::framework { + +typedef ::cppu::ImplInheritanceHelper < + ::sd::framework::Pane, + css::lang::XEventListener + > ChildWindowPaneInterfaceBase; + +/** The ChildWindowPane listens to the child window and disposes itself when + the child window becomes inaccessible. This happens for instance when a + document is made read-only and the task pane is turned off. +*/ +class ChildWindowPane + : public ChildWindowPaneInterfaceBase +{ +public: + ChildWindowPane ( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxPaneId, + sal_uInt16 nChildWindowId, + ViewShellBase& rViewShellBase, + ::std::unique_ptr<SfxShell> && pShell); + virtual ~ChildWindowPane() override; + + /** Hide the pane. To make the pane visible again, call GetWindow(). + */ + void Hide(); + + virtual void SAL_CALL disposing() override; + + /** This returns the content window when the child window is already + visible. Otherwise <NULL/> is returned. In that case a later call + may return the requested window (making a child window visible is an + asynchronous process.) + Note that GetWindow() may return different Window pointers when + Hide() is called in between. + */ + virtual vcl::Window* GetWindow() override; + + /** The local getWindow() first calls GetWindow() to provide a valid + window pointer before forwarding the call to the base class. + */ + virtual css::uno::Reference<css::awt::XWindow> + SAL_CALL getWindow() override; + + DECLARE_XINTERFACE() + DECLARE_XTYPEPROVIDER() + + // XEventListener + + virtual void SAL_CALL disposing( + const css::lang::EventObject& rEvent) override; + +private: + sal_uInt16 mnChildWindowId; + ViewShellBase& mrViewShellBase; + ::std::unique_ptr<SfxShell> mpShell; + + /** This flag is set when the pane shell has been activated at least + once. It is used to optimize the start-up performance (by not + showing the window too early) and by not delaying its creation at + later times. + */ + bool mbHasBeenActivated; +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/FrameWindowPane.cxx b/sd/source/ui/framework/factories/FrameWindowPane.cxx new file mode 100644 index 000000000..1f4b387d8 --- /dev/null +++ b/sd/source/ui/framework/factories/FrameWindowPane.cxx @@ -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 . + */ + +#include "FrameWindowPane.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace sd::framework +{ +FrameWindowPane::FrameWindowPane(const Reference<XResourceId>& rxPaneId, vcl::Window* pWindow) + : Pane(rxPaneId, pWindow) +{ +} + +FrameWindowPane::~FrameWindowPane() noexcept {} + +sal_Bool SAL_CALL FrameWindowPane::isAnchorOnly() { return false; } + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/FrameWindowPane.hxx b/sd/source/ui/framework/factories/FrameWindowPane.hxx new file mode 100644 index 000000000..67da37fdf --- /dev/null +++ b/sd/source/ui/framework/factories/FrameWindowPane.hxx @@ -0,0 +1,50 @@ +/* -*- 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 . + */ + +#pragma once + +#include <framework/Pane.hxx> + +namespace com::sun::star::drawing::framework +{ +class XResourceId; +} + +namespace sd::framework +{ +/** This subclass is not necessary anymore. We can remove it if that + remains so. +*/ +class FrameWindowPane : public Pane +{ +public: + FrameWindowPane(const css::uno::Reference<css::drawing::framework::XResourceId>& rxPaneId, + vcl::Window* pWindow); + virtual ~FrameWindowPane() noexcept override; + + /** A frame window typically can (and should) exists on its own without + children, if only to visualize that something (a view) is missing. + Therefore this method always returns <FALSE/>. + */ + virtual sal_Bool SAL_CALL isAnchorOnly() override; +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/FullScreenPane.cxx b/sd/source/ui/framework/factories/FullScreenPane.cxx new file mode 100644 index 000000000..dbf34213f --- /dev/null +++ b/sd/source/ui/framework/factories/FullScreenPane.cxx @@ -0,0 +1,226 @@ +/* -*- 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 "FullScreenPane.hxx" +#include <vcl/vclevent.hxx> +#include <vcl/wrkwin.hxx> +#include <o3tl/string_view.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/util/URL.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace sd::framework { + +FullScreenPane::FullScreenPane ( + const Reference<XComponentContext>& rxComponentContext, + const Reference<XResourceId>& rxPaneId, + const vcl::Window* pViewShellWindow) + : FrameWindowPane(rxPaneId,nullptr), + mxComponentContext(rxComponentContext) +{ + vcl::Window* pParent = nullptr; + mpWorkWindow.reset(VclPtr<WorkWindow>::Create( + + pParent, + 0)); // For debugging (non-fullscreen) use WB_BORDER | WB_MOVEABLE | WB_SIZEABLE)); + + if ( ! rxPaneId.is()) + throw lang::IllegalArgumentException(); + + sal_Int32 nScreenNumber = 1; + ExtractArguments(rxPaneId, nScreenNumber); + + if (!mpWorkWindow) + return; + + // Create a new top-level window that is displayed full screen. + mpWorkWindow->ShowFullScreenMode(true, nScreenNumber); + // For debugging (non-fullscreen) use mpWorkWindow->SetScreenNumber(nScreenNumber); + mpWorkWindow->SetMenuBarMode(MenuBarMode::Hide); + mpWorkWindow->SetBorderStyle(WindowBorderStyle::REMOVEBORDER); + mpWorkWindow->SetBackground(Wallpaper()); + // Don't show the window right now in order to allow the setting of an + // accessibility object: accessibility objects are typically + // requested by AT-tools when the window is shown. Chaining it + // afterwards may or may not work. + + // Add resize listener at the work window. + Link<VclWindowEvent&,void> aWindowEventHandler (LINK(this, FullScreenPane, WindowEventHandler)); + mpWorkWindow->AddEventListener(aWindowEventHandler); + + // Set title and icon of the new window to those of the current window + // of the view shell. + if (pViewShellWindow != nullptr) + { + const SystemWindow* pSystemWindow = pViewShellWindow->GetSystemWindow(); + mpWorkWindow->SetText(pSystemWindow->GetText()); + mpWorkWindow->SetIcon(pSystemWindow->GetIcon()); + } + + // For some reason the VCL canvas can not paint into a WorkWindow. + // Therefore a child window is created that covers the WorkWindow + // completely. + mpWindow = VclPtr<vcl::Window>::Create(mpWorkWindow.get()); + mpWindow->SetPosSizePixel(Point(0,0), mpWorkWindow->GetSizePixel()); + mpWindow->SetBackground(Wallpaper()); + mxWindow = VCLUnoHelper::GetInterface(mpWindow); + + // Create the canvas. + mxCanvas = CreateCanvas(); + + mpWindow->GrabFocus(); +} + +FullScreenPane::~FullScreenPane() noexcept +{ +} + +void SAL_CALL FullScreenPane::disposing() +{ + mpWindow.disposeAndClear(); + + if (mpWorkWindow) + { + Link<VclWindowEvent&,void> aWindowEventHandler (LINK(this, FullScreenPane, WindowEventHandler)); + mpWorkWindow->RemoveEventListener(aWindowEventHandler); + mpWorkWindow.disposeAndClear(); + } + + FrameWindowPane::disposing(); +} + +//----- XPane ----------------------------------------------------------------- + +sal_Bool SAL_CALL FullScreenPane::isVisible() +{ + ThrowIfDisposed(); + + if (mpWindow != nullptr) + return mpWindow->IsReallyVisible(); + else + return false; +} + +void SAL_CALL FullScreenPane::setVisible (const sal_Bool bIsVisible) +{ + ThrowIfDisposed(); + + if (mpWindow != nullptr) + mpWindow->Show(bIsVisible); + if (mpWorkWindow != nullptr) + mpWorkWindow->Show(bIsVisible); +} + +Reference<css::accessibility::XAccessible> SAL_CALL FullScreenPane::getAccessible() +{ + ThrowIfDisposed(); + + if (mpWorkWindow != nullptr) + return mpWorkWindow->GetAccessible(false); + else + return nullptr; +} + +void SAL_CALL FullScreenPane::setAccessible ( + const Reference<css::accessibility::XAccessible>& rxAccessible) +{ + ThrowIfDisposed(); + + if (mpWindow == nullptr) + return; + + Reference<lang::XInitialization> xInitializable (rxAccessible, UNO_QUERY); + if (xInitializable.is()) + { + vcl::Window* pParentWindow = mpWindow->GetParent(); + Reference<css::accessibility::XAccessible> xAccessibleParent; + if (pParentWindow != nullptr) + xAccessibleParent = pParentWindow->GetAccessible(); + Sequence<Any> aArguments{ Any(xAccessibleParent) }; + xInitializable->initialize(aArguments); + } + GetWindow()->SetAccessible(rxAccessible); +} + +IMPL_LINK(FullScreenPane, WindowEventHandler, VclWindowEvent&, rEvent, void) +{ + switch (rEvent.GetId()) + { + case VclEventId::WindowResize: + GetWindow()->SetPosPixel(Point(0,0)); + GetWindow()->SetSizePixel(Size( + mpWorkWindow->GetSizePixel().Width(), + mpWorkWindow->GetSizePixel().Height())); + break; + + case VclEventId::ObjectDying: + mpWorkWindow.disposeAndClear(); + break; + + default: break; + } +} + +Reference<rendering::XCanvas> FullScreenPane::CreateCanvas() +{ + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(mxWindow); + if (!pWindow) + throw RuntimeException(); + + Sequence<Any> aArg{ // common: first any is VCL pointer to window (for VCL canvas) + Any(reinterpret_cast<sal_Int64>(pWindow.get())), + Any(css::awt::Rectangle()), + Any(false), + Any(mxWindow) + }; + + Reference<lang::XMultiServiceFactory> xFactory ( + mxComponentContext->getServiceManager(), UNO_QUERY_THROW); + return Reference<rendering::XCanvas>( + xFactory->createInstanceWithArguments("com.sun.star.rendering.SpriteCanvas.VCL", + aArg), + UNO_QUERY); +} + +void FullScreenPane::ExtractArguments ( + const Reference<XResourceId>& rxPaneId, + sal_Int32& rnScreenNumberReturnValue) +{ + // Extract arguments from the resource URL. + const util::URL aURL = rxPaneId->getFullResourceURL(); + for (sal_Int32 nIndex{ 0 }; nIndex >= 0; ) + { + const std::u16string_view aToken = o3tl::getToken(aURL.Arguments, 0, '&', nIndex); + std::u16string_view sValue; + if (o3tl::starts_with(aToken, u"ScreenNumber=", &sValue)) + { + rnScreenNumberReturnValue = o3tl::toInt32(sValue); + } + } +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/FullScreenPane.hxx b/sd/source/ui/framework/factories/FullScreenPane.hxx new file mode 100644 index 000000000..b33804ee5 --- /dev/null +++ b/sd/source/ui/framework/factories/FullScreenPane.hxx @@ -0,0 +1,85 @@ +/* -*- 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 . + */ + +#pragma once + +#include "FrameWindowPane.hxx" +#include <tools/link.hxx> +#include <vcl/wrkwin.hxx> + +class VclWindowEvent; + +namespace vcl { class Window; } +namespace com::sun::star::uno { class XComponentContext; } + +namespace sd::framework { + +/** The full screen pane creates a pane that covers the complete application + window, i.e. that hides menu bar, tool bars, status bars. +*/ +class FullScreenPane + : public FrameWindowPane +{ +public: + /** Create a new full screen pane. + @param rxComponentContext + Used for creating a new canvas. + @param rxPaneId + The resource id of the new pane. + @param pViewShellWindow + The top-level parent of this window is used to obtain title and + icon for the new top-level window. + */ + FullScreenPane ( + const css::uno::Reference<css::uno::XComponentContext>& rxComponentContext, + const css::uno::Reference<css::drawing::framework::XResourceId>& rxPaneId, + const vcl::Window* pViewShellWindow); + virtual ~FullScreenPane() noexcept override; + + virtual void SAL_CALL disposing() override; + + //----- XPane ------------------------------------------------------------- + + virtual sal_Bool SAL_CALL isVisible() override; + + virtual void SAL_CALL setVisible (sal_Bool bIsVisible) override; + + virtual css::uno::Reference<css::accessibility::XAccessible> SAL_CALL getAccessible() override; + + virtual void SAL_CALL setAccessible ( + const css::uno::Reference<css::accessibility::XAccessible>& rxAccessible) override; + + DECL_LINK(WindowEventHandler, VclWindowEvent&, void); + +protected: + virtual css::uno::Reference<css::rendering::XCanvas> + CreateCanvas() override; + +private: + css::uno::Reference<css::uno::XComponentContext> mxComponentContext; + VclPtr<WorkWindow> mpWorkWindow; + + static void ExtractArguments ( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxPaneId, + sal_Int32& rnScreenNumberReturnValue); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/Pane.cxx b/sd/source/ui/framework/factories/Pane.cxx new file mode 100644 index 000000000..a188f0e11 --- /dev/null +++ b/sd/source/ui/framework/factories/Pane.cxx @@ -0,0 +1,178 @@ +/* -*- 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 <framework/Pane.hxx> + +#include <osl/mutex.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <vcl/window.hxx> +#include <cppcanvas/vclfactory.hxx> +#include <com/sun/star/rendering/XSpriteCanvas.hpp> +#include <comphelper/servicehelper.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace sd::framework { + +Pane::Pane ( + const Reference<XResourceId>& rxPaneId, + vcl::Window* pWindow) + noexcept + : PaneInterfaceBase(m_aMutex), + mxPaneId(rxPaneId), + mpWindow(pWindow), + mxWindow(VCLUnoHelper::GetInterface(pWindow)) +{ +} + +Pane::~Pane() +{ +} + +void Pane::disposing() +{ + mxWindow = nullptr; + mpWindow = nullptr; +} + +vcl::Window* Pane::GetWindow() +{ + if (mxWindow.is()) + return mpWindow; + else + return nullptr; +} + +//----- XPane ----------------------------------------------------------------- + +Reference<awt::XWindow> SAL_CALL Pane::getWindow() +{ + ThrowIfDisposed(); + + return mxWindow; +} + +Reference<rendering::XCanvas> SAL_CALL Pane::getCanvas() +{ + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + + if ( ! mxCanvas.is()) + mxCanvas = CreateCanvas(); + + return mxCanvas; +} + +//----- XPane2 ---------------------------------------------------------------- + +sal_Bool SAL_CALL Pane::isVisible() +{ + ThrowIfDisposed(); + + const vcl::Window* pWindow = GetWindow(); + if (pWindow != nullptr) + return pWindow->IsVisible(); + else + return false; +} + +void SAL_CALL Pane::setVisible (sal_Bool bIsVisible) +{ + ThrowIfDisposed(); + + vcl::Window* pWindow = GetWindow(); + if (pWindow != nullptr) + pWindow->Show(bIsVisible); +} + +Reference<css::accessibility::XAccessible> SAL_CALL Pane::getAccessible() +{ + ThrowIfDisposed(); + vcl::Window* pWindow = GetWindow(); + if (pWindow != nullptr) + return pWindow->GetAccessible(false); + else + return nullptr; +} + +void SAL_CALL Pane::setAccessible ( + const Reference<css::accessibility::XAccessible>& rxAccessible) +{ + ThrowIfDisposed(); + vcl::Window* pWindow = GetWindow(); + if (pWindow != nullptr) + pWindow->SetAccessible(rxAccessible); +} + +//----- XResource ------------------------------------------------------------- + +Reference<XResourceId> SAL_CALL Pane::getResourceId() +{ + ThrowIfDisposed(); + + return mxPaneId; +} + +sal_Bool SAL_CALL Pane::isAnchorOnly() +{ + return true; +} + +//----- XUnoTunnel ------------------------------------------------------------ + +const Sequence<sal_Int8>& Pane::getUnoTunnelId() +{ + static const comphelper::UnoIdInit thePaneUnoTunnelId; + return thePaneUnoTunnelId.getSeq(); +} + +sal_Int64 SAL_CALL Pane::getSomething (const Sequence<sal_Int8>& rId) +{ + return comphelper::getSomethingImpl(rId, this); +} + +Reference<rendering::XCanvas> Pane::CreateCanvas() +{ + Reference<rendering::XCanvas> xCanvas; + + if (mpWindow != nullptr) + { + ::cppcanvas::SpriteCanvasSharedPtr pCanvas ( + cppcanvas::VCLFactory::createSpriteCanvas(*mpWindow)); + if (pCanvas) + xCanvas.set(pCanvas->getUNOSpriteCanvas()); + } + + return xCanvas; +} + +void Pane::ThrowIfDisposed() const +{ + if (rBHelper.bDisposed || rBHelper.bInDispose) + { + throw lang::DisposedException ("Pane object has already been disposed", + const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this))); + } +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/PresentationFactory.cxx b/sd/source/ui/framework/factories/PresentationFactory.cxx new file mode 100644 index 000000000..8cf603809 --- /dev/null +++ b/sd/source/ui/framework/factories/PresentationFactory.cxx @@ -0,0 +1,192 @@ +/* -*- 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 <framework/PresentationFactory.hxx> + +#include <DrawController.hxx> +#include <com/sun/star/drawing/framework/XControllerManager.hpp> +#include <com/sun/star/drawing/framework/XView.hpp> +#include <comphelper/servicehelper.hxx> +#include <comphelper/compbase.hxx> +#include <tools/diagnose_ex.h> +#include <slideshow.hxx> + +namespace com::sun::star::uno { class XComponentContext; } + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::drawing::framework; + + +namespace sd::framework { + +namespace { + +typedef comphelper::WeakComponentImplHelper<lang::XInitialization> PresentationFactoryProviderInterfaceBase; + +class PresentationFactoryProvider + : public PresentationFactoryProviderInterfaceBase +{ +public: + PresentationFactoryProvider (); + + // XInitialization + + virtual void SAL_CALL initialize( + const css::uno::Sequence<css::uno::Any>& aArguments) override; +}; + +typedef comphelper::WeakComponentImplHelper<XView> PresentationViewInterfaceBase; + +/** The PresentationView is not an actual view, it is a marker whose + existence in a configuration indicates that a slideshow is running + (in another application window). +*/ +class PresentationView + : public PresentationViewInterfaceBase +{ +public: + explicit PresentationView (const Reference<XResourceId>& rxViewId) + : mxResourceId(rxViewId) {}; + + // XView + + virtual Reference<XResourceId> SAL_CALL getResourceId() override + { return mxResourceId; }; + + virtual sal_Bool SAL_CALL isAnchorOnly() override + { return false; } + +private: + Reference<XResourceId> mxResourceId; +}; + +} // end of anonymous namespace. + +//===== PresentationFactory =================================================== + +constexpr OUStringLiteral gsPresentationViewURL = u"private:resource/view/Presentation"; + +PresentationFactory::PresentationFactory ( + const Reference<frame::XController>& rxController) + : mxController(rxController) +{ +} + +PresentationFactory::~PresentationFactory() +{ +} + +//----- XViewFactory ---------------------------------------------------------- + +Reference<XResource> SAL_CALL PresentationFactory::createResource ( + const Reference<XResourceId>& rxViewId) +{ + ThrowIfDisposed(); + + if (rxViewId.is()) + if ( ! rxViewId->hasAnchor() && rxViewId->getResourceURL() == gsPresentationViewURL) + return new PresentationView(rxViewId); + + return Reference<XResource>(); +} + +void SAL_CALL PresentationFactory::releaseResource ( + const Reference<XResource>&) +{ + ThrowIfDisposed(); + + auto pController = comphelper::getFromUnoTunnel<sd::DrawController>(mxController); + if (pController != nullptr) + { + ViewShellBase* pBase = pController->GetViewShellBase(); + if (pBase != nullptr) + SlideShow::Stop( *pBase ); + } +} + +//===== XConfigurationChangeListener ========================================== + +void SAL_CALL PresentationFactory::notifyConfigurationChange ( + const ConfigurationChangeEvent&) +{} + +//===== lang::XEventListener ================================================== + +void SAL_CALL PresentationFactory::disposing ( + const lang::EventObject&) +{} + +void PresentationFactory::ThrowIfDisposed() const +{ + if (m_bDisposed) + { + throw lang::DisposedException ("PresentationFactory object has already been disposed", + const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this))); + } +} + +namespace { + +//===== PresentationFactoryProvider =========================================== + +PresentationFactoryProvider::PresentationFactoryProvider () +{ +} + +// XInitialization + +void SAL_CALL PresentationFactoryProvider::initialize( + const Sequence<Any>& aArguments) +{ + if (!aArguments.hasElements()) + return; + + try + { + // Get the XController from the first argument. + Reference<frame::XController> xController (aArguments[0], UNO_QUERY_THROW); + Reference<XControllerManager> xCM (xController, UNO_QUERY_THROW); + Reference<XConfigurationController> xCC (xCM->getConfigurationController()); + if (xCC.is()) + xCC->addResourceFactory( + gsPresentationViewURL, + new PresentationFactory(xController)); + } + catch (RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } +} + +} // end of anonymous namespace. + +} // end of namespace sd::framework + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_framework_PresentationFactoryProvider_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new sd::framework::PresentationFactoryProvider); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/ViewShellWrapper.cxx b/sd/source/ui/framework/factories/ViewShellWrapper.cxx new file mode 100644 index 000000000..8f0fcd976 --- /dev/null +++ b/sd/source/ui/framework/factories/ViewShellWrapper.cxx @@ -0,0 +1,252 @@ +/* -*- 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 <framework/ViewShellWrapper.hxx> +#include <sdpage.hxx> +#include <ViewShell.hxx> + +#include <SlideSorter.hxx> +#include <SlideSorterViewShell.hxx> +#include <controller/SlsPageSelector.hxx> +#include <controller/SlideSorterController.hxx> +#include <model/SlsPageEnumerationProvider.hxx> +#include <model/SlsPageDescriptor.hxx> + +#include <com/sun/star/drawing/framework/XPane.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> + +#include <toolkit/helper/vclunohelper.hxx> +#include <comphelper/servicehelper.hxx> +#include <sal/log.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +using ::com::sun::star::awt::XWindow; + +namespace sd::framework { + +ViewShellWrapper::ViewShellWrapper ( + const std::shared_ptr<ViewShell>& pViewShell, + const Reference<XResourceId>& rxViewId, + const Reference<awt::XWindow>& rxWindow) + : mpViewShell(pViewShell), + mpSlideSorterViewShell( + std::dynamic_pointer_cast< ::sd::slidesorter::SlideSorterViewShell >( pViewShell )), + mxViewId(rxViewId), + mxWindow(rxWindow) +{ +} + +ViewShellWrapper::~ViewShellWrapper() +{ +} + +void ViewShellWrapper::disposing(std::unique_lock<std::mutex>&) +{ + SAL_INFO("sd.ui", "disposing ViewShellWrapper " << this); + Reference<awt::XWindow> xWindow (mxWindow); + if (xWindow.is()) + { + SAL_INFO( + "sd.ui", + "removing ViewShellWrapper " << this << " from window listener at " + << mxWindow.get()); + xWindow->removeWindowListener(this); + } + + mpSlideSorterViewShell.reset(); + mpViewShell.reset(); +} + +uno::Any SAL_CALL ViewShellWrapper::queryInterface( const uno::Type & rType ) +{ + if( mpSlideSorterViewShell && + rType == cppu::UnoType<view::XSelectionSupplier>::get() ) + { + uno::Reference<view::XSelectionSupplier> xSupplier( this ); + return Any(xSupplier); + } + else + return ViewShellWrapperInterfaceBase::queryInterface( rType ); +} + +//----- XResource ------------------------------------------------------------- + +Reference<XResourceId> SAL_CALL ViewShellWrapper::getResourceId() +{ + return mxViewId; +} + +sal_Bool SAL_CALL ViewShellWrapper::isAnchorOnly() +{ + return false; +} + +//----- XSelectionSupplier -------------------------------------------------- + +sal_Bool SAL_CALL ViewShellWrapper::select( const css::uno::Any& aSelection ) +{ + if (!mpSlideSorterViewShell) + return false; + + ::sd::slidesorter::controller::SlideSorterController& rSlideSorterController + = mpSlideSorterViewShell->GetSlideSorter().GetController(); + ::sd::slidesorter::controller::PageSelector& rSelector (rSlideSorterController.GetPageSelector()); + rSelector.DeselectAllPages(); + Sequence<Reference<drawing::XDrawPage> > xPages; + aSelection >>= xPages; + for (const auto& rPage : std::as_const(xPages)) + { + Reference<beans::XPropertySet> xSet (rPage, UNO_QUERY); + if (xSet.is()) + { + try + { + Any aNumber = xSet->getPropertyValue("Number"); + sal_Int32 nPageNumber = 0; + aNumber >>= nPageNumber; + nPageNumber -=1; // Transform 1-based page numbers to 0-based ones. + rSelector.SelectPage(nPageNumber); + } + catch (const RuntimeException&) + { + } + } + } + + return true; +} + +uno::Any SAL_CALL ViewShellWrapper::getSelection() +{ + Any aResult; + + if (!mpSlideSorterViewShell) + return aResult; + + slidesorter::model::PageEnumeration aSelectedPages ( + slidesorter::model::PageEnumerationProvider::CreateSelectedPagesEnumeration( + mpSlideSorterViewShell->GetSlideSorter().GetModel())); + int nSelectedPageCount ( + mpSlideSorterViewShell->GetSlideSorter().GetController().GetPageSelector().GetSelectedPageCount()); + + Sequence<Reference<XInterface> > aPages(nSelectedPageCount); + auto aPagesRange = asNonConstRange(aPages); + int nIndex = 0; + while (aSelectedPages.HasMoreElements() && nIndex<nSelectedPageCount) + { + slidesorter::model::SharedPageDescriptor pDescriptor (aSelectedPages.GetNextElement()); + aPagesRange[nIndex++] = pDescriptor->GetPage()->getUnoPage(); + } + aResult <<= aPages; + + return aResult; +} + +void SAL_CALL ViewShellWrapper::addSelectionChangeListener( const uno::Reference< view::XSelectionChangeListener >& ) +{ +} + +void SAL_CALL ViewShellWrapper::removeSelectionChangeListener( const uno::Reference< view::XSelectionChangeListener >& ) +{ +} + +//----- XRelocatableResource -------------------------------------------------- + +sal_Bool SAL_CALL ViewShellWrapper::relocateToAnchor ( + const Reference<XResource>& xResource) +{ + bool bResult (false); + + Reference<XPane> xPane (xResource, UNO_QUERY); + if (xPane.is()) + { + // Detach from the window of the old pane. + Reference<awt::XWindow> xWindow (mxWindow); + if (xWindow.is()) + xWindow->removeWindowListener(this); + mxWindow = nullptr; + + if (mpViewShell != nullptr) + { + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xPane->getWindow()); + if (pWindow && mpViewShell->RelocateToParentWindow(pWindow)) + { + bResult = true; + + // Attach to the window of the new pane. + xWindow = xPane->getWindow(); + if (xWindow.is()) + { + xWindow->addWindowListener(this); + mpViewShell->Resize(); + } + } + } + } + + return bResult; +} + +//----- XUnoTunnel ------------------------------------------------------------ + +const Sequence<sal_Int8>& ViewShellWrapper::getUnoTunnelId() +{ + static const comphelper::UnoIdInit theViewShellWrapperUnoTunnelId; + return theViewShellWrapperUnoTunnelId.getSeq(); +} + +sal_Int64 SAL_CALL ViewShellWrapper::getSomething (const Sequence<sal_Int8>& rId) +{ + return comphelper::getSomethingImpl(rId, this); +} + +//===== awt::XWindowListener ================================================== + +void SAL_CALL ViewShellWrapper::windowResized (const awt::WindowEvent&) +{ + ViewShell* pViewShell (mpViewShell.get()); + if (pViewShell != nullptr) + pViewShell->Resize(); +} + +void SAL_CALL ViewShellWrapper::windowMoved (const awt::WindowEvent&) {} + +void SAL_CALL ViewShellWrapper::windowShown (const lang::EventObject&) +{ + ViewShell* pViewShell (mpViewShell.get()); + if (pViewShell != nullptr) + pViewShell->Resize(); +} + +void SAL_CALL ViewShellWrapper::windowHidden (const lang::EventObject&) {} + +//===== XEventListener ======================================================== + +void SAL_CALL ViewShellWrapper::disposing (const lang::EventObject& rEvent) +{ + if (rEvent.Source == mxWindow) + mxWindow = nullptr; +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/CenterViewFocusModule.cxx b/sd/source/ui/framework/module/CenterViewFocusModule.cxx new file mode 100644 index 000000000..e36f95e33 --- /dev/null +++ b/sd/source/ui/framework/module/CenterViewFocusModule.cxx @@ -0,0 +1,151 @@ +/* -*- 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 "CenterViewFocusModule.hxx" + +#include <framework/FrameworkHelper.hxx> +#include <framework/ViewShellWrapper.hxx> + +#include <DrawController.hxx> +#include <ViewShellBase.hxx> +#include <ViewShellManager.hxx> +#include <com/sun/star/drawing/framework/XControllerManager.hpp> +#include <com/sun/star/drawing/framework/XConfigurationController.hpp> +#include <comphelper/servicehelper.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +using ::sd::framework::FrameworkHelper; + +namespace sd::framework { + +//===== CenterViewFocusModule ==================================================== + +CenterViewFocusModule::CenterViewFocusModule (Reference<frame::XController> const & rxController) + : mbValid(false), + mpBase(nullptr), + mbNewViewCreated(false) +{ + Reference<XControllerManager> xControllerManager (rxController, UNO_QUERY); + if (xControllerManager.is()) + { + mxConfigurationController = xControllerManager->getConfigurationController(); + + // Tunnel through the controller to obtain a ViewShellBase. + auto pController = comphelper::getFromUnoTunnel<sd::DrawController>(rxController); + if (pController != nullptr) + mpBase = pController->GetViewShellBase(); + + // Check, if all required objects do exist. + if (mxConfigurationController.is() && mpBase!=nullptr) + { + mbValid = true; + } + } + + if (mbValid) + { + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msConfigurationUpdateEndEvent, + Any()); + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceActivationEvent, + Any()); + } +} + +CenterViewFocusModule::~CenterViewFocusModule() +{ +} + +void CenterViewFocusModule::disposing(std::unique_lock<std::mutex>&) +{ + if (mxConfigurationController.is()) + mxConfigurationController->removeConfigurationChangeListener(this); + + mbValid = false; + mxConfigurationController = nullptr; + mpBase = nullptr; +} + +void SAL_CALL CenterViewFocusModule::notifyConfigurationChange ( + const ConfigurationChangeEvent& rEvent) +{ + if (mbValid) + { + if (rEvent.Type == FrameworkHelper::msConfigurationUpdateEndEvent) + { + HandleNewView(rEvent.Configuration); + } + else if (rEvent.Type == FrameworkHelper::msResourceActivationEvent) + { + if (rEvent.ResourceId->getResourceURL().match(FrameworkHelper::msViewURLPrefix)) + mbNewViewCreated = true; + } + } +} + +void CenterViewFocusModule::HandleNewView ( + const Reference<XConfiguration>& rxConfiguration) +{ + if (!mbNewViewCreated) + return; + + mbNewViewCreated = false; + // Make the center pane the active one. Tunnel through the + // controller to obtain a ViewShell pointer. + + Sequence<Reference<XResourceId> > xViewIds (rxConfiguration->getResources( + FrameworkHelper::CreateResourceId(FrameworkHelper::msCenterPaneURL), + FrameworkHelper::msViewURLPrefix, + AnchorBindingMode_DIRECT)); + Reference<XView> xView; + if (xViewIds.hasElements()) + xView.set( mxConfigurationController->getResource(xViewIds[0]),UNO_QUERY); + if (mpBase!=nullptr) + { + auto pViewShellWrapper = comphelper::getFromUnoTunnel<ViewShellWrapper>(xView); + if (pViewShellWrapper != nullptr) + { + std::shared_ptr<ViewShell> pViewShell = pViewShellWrapper->GetViewShell(); + if (pViewShell != nullptr) + mpBase->GetViewShellManager()->MoveToTop(*pViewShell); + } + } +} + +void SAL_CALL CenterViewFocusModule::disposing ( + const lang::EventObject& rEvent) +{ + if (mxConfigurationController.is()) + if (rEvent.Source == mxConfigurationController) + { + mbValid = false; + mxConfigurationController = nullptr; + mpBase = nullptr; + } +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/CenterViewFocusModule.hxx b/sd/source/ui/framework/module/CenterViewFocusModule.hxx new file mode 100644 index 000000000..c6d5d348e --- /dev/null +++ b/sd/source/ui/framework/module/CenterViewFocusModule.hxx @@ -0,0 +1,90 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/drawing/framework/XConfigurationChangeListener.hpp> +#include <comphelper/compbase.hxx> + +namespace com::sun::star::drawing::framework +{ +class XConfigurationController; +} +namespace com::sun::star::frame +{ +class XController; +} + +namespace sd +{ +class ViewShellBase; +} + +namespace sd::framework +{ +typedef comphelper::WeakComponentImplHelper<css::drawing::framework::XConfigurationChangeListener> + CenterViewFocusModuleInterfaceBase; + +/** This module waits for new views to be created for the center pane and + then moves the center view to the top most place on the shell stack. As + we are moving away from the shell stack this module may become obsolete + or has to be modified. +*/ +class CenterViewFocusModule final : public CenterViewFocusModuleInterfaceBase +{ +public: + explicit CenterViewFocusModule( + css::uno::Reference<css::frame::XController> const& rxController); + virtual ~CenterViewFocusModule() override; + + virtual void disposing(std::unique_lock<std::mutex>&) override; + + // XConfigurationChangeListener + + virtual void SAL_CALL notifyConfigurationChange( + const css::drawing::framework::ConfigurationChangeEvent& rEvent) override; + + // XEventListener + + virtual void SAL_CALL disposing(const css::lang::EventObject& rEvent) override; + +private: + class ViewShellContainer; + + bool mbValid; + css::uno::Reference<css::drawing::framework::XConfigurationController> + mxConfigurationController; + ViewShellBase* mpBase; + /** This flag indicates whether in the last configuration change cycle a + new view has been created and thus the center view has to be moved + to the top of the shell stack. + */ + bool mbNewViewCreated; + + /** At the end of an update of the current configuration this method + handles a new view in the center pane by moving the associated view + shell to the top of the shell stack. + */ + void HandleNewView( + const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/DrawModule.cxx b/sd/source/ui/framework/module/DrawModule.cxx new file mode 100644 index 000000000..17f4671fc --- /dev/null +++ b/sd/source/ui/framework/module/DrawModule.cxx @@ -0,0 +1,41 @@ +/* -*- 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 <framework/DrawModule.hxx> + +#include <framework/FrameworkHelper.hxx> +#include "CenterViewFocusModule.hxx" +#include "SlideSorterModule.hxx" +#include "ToolBarModule.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::framework +{ +void DrawModule::Initialize(Reference<frame::XController> const& rxController) +{ + new sd::framework::CenterViewFocusModule(rxController); + new sd::framework::SlideSorterModule(rxController, FrameworkHelper::msLeftDrawPaneURL); + new ToolBarModule(rxController); +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/ImpressModule.cxx b/sd/source/ui/framework/module/ImpressModule.cxx new file mode 100644 index 000000000..139b250fd --- /dev/null +++ b/sd/source/ui/framework/module/ImpressModule.cxx @@ -0,0 +1,51 @@ +/* -*- 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 <framework/ImpressModule.hxx> + +#include <framework/FrameworkHelper.hxx> +#include "ViewTabBarModule.hxx" +#include "CenterViewFocusModule.hxx" +#include "SlideSorterModule.hxx" +#include "ToolBarModule.hxx" +#include "ShellStackGuard.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::framework { + +void ImpressModule::Initialize (Reference<frame::XController> const & rxController) +{ + new CenterViewFocusModule(rxController); + new ViewTabBarModule( + rxController, + FrameworkHelper::CreateResourceId( + FrameworkHelper::msViewTabBarURL, + FrameworkHelper::msCenterPaneURL)); + new SlideSorterModule( + rxController, + FrameworkHelper::msLeftImpressPaneURL); + new ToolBarModule(rxController); + new ShellStackGuard(rxController); +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/ModuleController.cxx b/sd/source/ui/framework/module/ModuleController.cxx new file mode 100644 index 000000000..acd12ec8a --- /dev/null +++ b/sd/source/ui/framework/module/ModuleController.cxx @@ -0,0 +1,244 @@ +/* -*- 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 <framework/ModuleController.hxx> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/container/XNameAccess.hpp> + +#include <tools/ConfigurationAccess.hxx> +#include <comphelper/processfactory.hxx> + +#include <tools/diagnose_ex.h> +#include <sal/log.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; +using ::sd::tools::ConfigurationAccess; + +namespace sd::framework { + +const sal_uInt32 snFactoryPropertyCount (2); +const sal_uInt32 snStartupPropertyCount (1); + +//===== ModuleController ====================================================== +Reference<XModuleController> ModuleController::CreateInstance ( + const Reference<XComponentContext>& rxContext) +{ + return new ModuleController(rxContext); +} + +ModuleController::ModuleController (const Reference<XComponentContext>& rxContext) +{ + /** Load a list of URL to service mappings from the + /org.openoffice.Office.Impress/MultiPaneGUI/Framework/ResourceFactories + configuration entry. The mappings are stored in the + mpResourceToFactoryMap member. + */ + try + { + ConfigurationAccess aConfiguration ( + rxContext, + "/org.openoffice.Office.Impress/", + ConfigurationAccess::READ_ONLY); + Reference<container::XNameAccess> xFactories ( + aConfiguration.GetConfigurationNode("MultiPaneGUI/Framework/ResourceFactories"), + UNO_QUERY); + ::std::vector<OUString> aProperties (snFactoryPropertyCount); + aProperties[0] = "ServiceName"; + aProperties[1] = "ResourceList"; + ConfigurationAccess::ForAll( + xFactories, + aProperties, + [this] (OUString const&, ::std::vector<Any> const& xs) { + return this->ProcessFactory(xs); + } ); + } + catch (Exception&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } +} + +ModuleController::~ModuleController() noexcept +{ +} + +void ModuleController::disposing(std::unique_lock<std::mutex>&) +{ + // Break the cyclic reference back to DrawController object + maLoadedFactories.clear(); + maResourceToFactoryMap.clear(); + mxController.clear(); +} + +void ModuleController::ProcessFactory (const ::std::vector<Any>& rValues) +{ + OSL_ASSERT(rValues.size() == snFactoryPropertyCount); + + // Get the service name of the factory. + OUString sServiceName; + rValues[0] >>= sServiceName; + + // Get all resource URLs that are created by the factory. + Reference<container::XNameAccess> xResources (rValues[1], UNO_QUERY); + ::std::vector<OUString> aURLs; + tools::ConfigurationAccess::FillList( + xResources, + "URL", + aURLs); + + SAL_INFO("sd.fwk", __func__ << ": ModuleController::adding factory " << sServiceName); + + // Add the resource URLs to the map. + for (const auto& rResource : aURLs) + { + maResourceToFactoryMap[rResource] = sServiceName; + SAL_INFO("sd.fwk", __func__ << ": " << rResource); + } +} + +void ModuleController::InstantiateStartupServices() +{ + try + { + tools::ConfigurationAccess aConfiguration ( + "/org.openoffice.Office.Impress/", + tools::ConfigurationAccess::READ_ONLY); + Reference<container::XNameAccess> xFactories ( + aConfiguration.GetConfigurationNode("MultiPaneGUI/Framework/StartupServices"), + UNO_QUERY); + ::std::vector<OUString> aProperties (snStartupPropertyCount); + aProperties[0] = "ServiceName"; + tools::ConfigurationAccess::ForAll( + xFactories, + aProperties, + [this] (OUString const&, ::std::vector<Any> const& xs) { + return this->ProcessStartupService(xs); + } ); + } + catch (Exception&) + { + SAL_WARN("sd.fwk", "ERROR in ModuleController::InstantiateStartupServices"); + } +} + +void ModuleController::ProcessStartupService (const ::std::vector<Any>& rValues) +{ + OSL_ASSERT(rValues.size() == snStartupPropertyCount); + + try + { + // Get the service name of the startup service. + OUString sServiceName; + rValues[0] >>= sServiceName; + + // Instantiate service. + Reference<uno::XComponentContext> xContext = + ::comphelper::getProcessComponentContext(); + + // Create the startup service. + Sequence<Any> aArguments{ Any(mxController) }; + // Note that when the new object will be destroyed at the end of + // this scope when it does not register itself anywhere. + // Typically it will add itself as ConfigurationChangeListener + // at the configuration controller. + xContext->getServiceManager()->createInstanceWithArgumentsAndContext(sServiceName, aArguments, xContext); + + SAL_INFO("sd.fwk", __func__ << ": ModuleController::created startup service " << sServiceName); + } + catch (Exception&) + { + SAL_WARN("sd.fwk", "ERROR in ModuleController::ProcessStartupServices"); + } +} + +//----- XModuleController ----------------------------------------------------- + +void SAL_CALL ModuleController::requestResource (const OUString& rsResourceURL) +{ + auto iFactory = maResourceToFactoryMap.find(rsResourceURL); + if (iFactory == maResourceToFactoryMap.end()) + return; + + // Check that the factory has already been loaded and not been + // destroyed in the meantime. + Reference<XInterface> xFactory; + auto iLoadedFactory = maLoadedFactories.find(iFactory->second); + if (iLoadedFactory != maLoadedFactories.end()) + xFactory.set(iLoadedFactory->second, UNO_QUERY); + if ( xFactory.is()) + return; + + // Create a new instance of the factory. + Reference<uno::XComponentContext> xContext = + ::comphelper::getProcessComponentContext(); + + // Create the factory service. + Sequence<Any> aArguments{ Any(mxController) }; + try + { + xFactory = xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + iFactory->second, + aArguments, + xContext); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("sd.fwk", "caught exception while creating factory"); + } + + // Remember that this factory has been instanced. + maLoadedFactories[iFactory->second] = xFactory; +} + +//----- XInitialization ------------------------------------------------------- + +void SAL_CALL ModuleController::initialize (const Sequence<Any>& aArguments) +{ + if (aArguments.hasElements()) + { + try + { + // Get the XController from the first argument. + mxController.set(aArguments[0], UNO_QUERY_THROW); + + InstantiateStartupServices(); + } + catch (RuntimeException&) + {} + } +} + +} // end of namespace sd::framework + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_framework_module_ModuleController_get_implementation( + css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + css::uno::Reference< css::uno::XInterface > xModCont ( sd::framework::ModuleController::CreateInstance(context) ); + xModCont->acquire(); + return xModCont.get(); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/PresentationModule.cxx b/sd/source/ui/framework/module/PresentationModule.cxx new file mode 100644 index 000000000..fb0ac0558 --- /dev/null +++ b/sd/source/ui/framework/module/PresentationModule.cxx @@ -0,0 +1,36 @@ +/* -*- 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 <framework/PresentationModule.hxx> + +#include "CenterViewFocusModule.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::framework +{ +void PresentationModule::Initialize(Reference<frame::XController> const& rxController) +{ + new sd::framework::CenterViewFocusModule(rxController); +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/ShellStackGuard.cxx b/sd/source/ui/framework/module/ShellStackGuard.cxx new file mode 100644 index 000000000..83d73b055 --- /dev/null +++ b/sd/source/ui/framework/module/ShellStackGuard.cxx @@ -0,0 +1,150 @@ +/* -*- 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 "ShellStackGuard.hxx" + +#include <framework/ConfigurationController.hxx> +#include <framework/FrameworkHelper.hxx> + +#include <DrawController.hxx> +#include <ViewShellBase.hxx> +#include <sfx2/printer.hxx> +#include <com/sun/star/drawing/framework/XControllerManager.hpp> +#include <comphelper/servicehelper.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +using ::sd::framework::FrameworkHelper; + +namespace sd::framework { + +//===== CenterViewFocusModule ==================================================== + +ShellStackGuard::ShellStackGuard (Reference<frame::XController> const & rxController) + : mpBase(nullptr), + maPrinterPollingIdle("sd ShellStackGuard PrinterPollingIdle") +{ + Reference<XControllerManager> xControllerManager (rxController, UNO_QUERY); + if (xControllerManager.is()) + { + mxConfigurationController = xControllerManager->getConfigurationController(); + + // Tunnel through the controller to obtain a ViewShellBase. + auto pController = comphelper::getFromUnoTunnel<sd::DrawController>(rxController); + if (pController != nullptr) + mpBase = pController->GetViewShellBase(); + } + + if (mxConfigurationController.is()) + { + // Listen for update starts so that the following update can be + // prevented in case of a printing printer. + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msConfigurationUpdateStartEvent, + Any()); + + // Prepare the printer polling. + maPrinterPollingIdle.SetInvokeHandler(LINK(this,ShellStackGuard,TimeoutHandler)); + } +} + +ShellStackGuard::~ShellStackGuard() +{ +} + +void ShellStackGuard::disposing(std::unique_lock<std::mutex>&) +{ + if (mxConfigurationController) + { + mxConfigurationController->removeConfigurationChangeListener(this); + mxConfigurationController = nullptr; + } + mpBase = nullptr; +} + +void SAL_CALL ShellStackGuard::notifyConfigurationChange ( + const ConfigurationChangeEvent& rEvent) +{ + if (rEvent.Type == FrameworkHelper::msConfigurationUpdateStartEvent) + { + if (mpUpdateLock == nullptr && IsPrinting()) + { + // Prevent configuration updates while the printer is printing. + mpUpdateLock.reset(new ConfigurationController::Lock(mxConfigurationController)); + + // Start polling for the printer having finished printing. + maPrinterPollingIdle.Start(); + } + } +} + +void SAL_CALL ShellStackGuard::disposing ( + const lang::EventObject& rEvent) +{ + if (mxConfigurationController.is()) + if (rEvent.Source == mxConfigurationController) + { + mxConfigurationController = nullptr; + mpBase = nullptr; + } +} + +IMPL_LINK(ShellStackGuard, TimeoutHandler, Timer*, pIdle, void) +{ +#ifdef DEBUG + OSL_ASSERT(pIdle==&maPrinterPollingIdle); +#else + (void)pIdle; +#endif + if (mpUpdateLock == nullptr) + return; + + if ( ! IsPrinting()) + { + // Printing finished. Release the update lock. + mpUpdateLock.reset(); + } + else + { + // Wait long for the printing to finish. + maPrinterPollingIdle.Start(); + } +} + +bool ShellStackGuard::IsPrinting() const +{ + if (mpBase != nullptr) + { + SfxPrinter* pPrinter = mpBase->GetPrinter(); + if (pPrinter != nullptr + && pPrinter->IsPrinting()) + { + return true; + } + } + + return false; +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/ShellStackGuard.hxx b/sd/source/ui/framework/module/ShellStackGuard.hxx new file mode 100644 index 000000000..72b7ed2c6 --- /dev/null +++ b/sd/source/ui/framework/module/ShellStackGuard.hxx @@ -0,0 +1,94 @@ +/* -*- 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 . + */ + +#pragma once + +#include <framework/ConfigurationController.hxx> + +#include <com/sun/star/drawing/framework/XConfigurationChangeListener.hpp> + +#include <vcl/idle.hxx> +#include <comphelper/compbase.hxx> +#include <memory> + +namespace com::sun::star::drawing::framework +{ +class XConfigurationController; +} +namespace com::sun::star::frame +{ +class XController; +} + +namespace sd +{ +class ViewShellBase; +} + +namespace sd::framework +{ +typedef comphelper::WeakComponentImplHelper<css::drawing::framework::XConfigurationChangeListener> + ShellStackGuardInterfaceBase; + +/** This module locks updates of the current configuration in situations + when the shell stack must not be modified. + + On every start of a configuration update the ShellStackGuard checks the + printer. If it is printing the configuration update is locked. It then + polls the printer and unlocks updates when printing finishes. + + When in the future there are no resources left that use shells then this + module can be removed. +*/ +class ShellStackGuard : public ShellStackGuardInterfaceBase +{ +public: + explicit ShellStackGuard(css::uno::Reference<css::frame::XController> const& rxController); + virtual ~ShellStackGuard() override; + + virtual void disposing(std::unique_lock<std::mutex>&) override; + + // XConfigurationChangeListener + + virtual void SAL_CALL notifyConfigurationChange( + const css::drawing::framework::ConfigurationChangeEvent& rEvent) override; + + // XEventListener + + virtual void SAL_CALL disposing(const css::lang::EventObject& rEvent) override; + +private: + css::uno::Reference<css::drawing::framework::XConfigurationController> + mxConfigurationController; + ViewShellBase* mpBase; + std::unique_ptr<ConfigurationController::Lock> mpUpdateLock; + Idle maPrinterPollingIdle; + + DECL_LINK(TimeoutHandler, Timer*, void); + + /** Return <TRUE/> when the printer is printing. Return <FALSE/> when + the printer is not printing, or there is no printer, or something + else went wrong. + */ + bool IsPrinting() const; +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/SlideSorterModule.cxx b/sd/source/ui/framework/module/SlideSorterModule.cxx new file mode 100644 index 000000000..dbe30f0d3 --- /dev/null +++ b/sd/source/ui/framework/module/SlideSorterModule.cxx @@ -0,0 +1,313 @@ +/* -*- 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 "SlideSorterModule.hxx" + +#include <framework/FrameworkHelper.hxx> +#include <framework/ConfigurationController.hxx> +#include <com/sun/star/drawing/framework/XTabBar.hpp> +#include <com/sun/star/drawing/framework/TabBarButton.hpp> +#include <com/sun/star/drawing/framework/XControllerManager.hpp> +#include <com/sun/star/frame/XController.hpp> + +#include <strings.hrc> +#include <sdresid.hxx> +#include <svtools/slidesorterbaropt.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +using ::sd::framework::FrameworkHelper; + +namespace { + const sal_Int32 ResourceActivationRequestEvent = 0; + const sal_Int32 ResourceDeactivationRequestEvent = 1; +} + +namespace sd::framework { + +//===== SlideSorterModule ================================================== + +SlideSorterModule::SlideSorterModule ( + const Reference<frame::XController>& rxController, + const OUString& rsLeftPaneURL) + : mxResourceId(FrameworkHelper::CreateResourceId(FrameworkHelper::msSlideSorterURL, rsLeftPaneURL)), + mxMainViewAnchorId(FrameworkHelper::CreateResourceId(FrameworkHelper::msCenterPaneURL)), + mxViewTabBarId(FrameworkHelper::CreateResourceId( + FrameworkHelper::msViewTabBarURL, + FrameworkHelper::msCenterPaneURL)), + mxControllerManager(rxController,UNO_QUERY) +{ + Reference<XControllerManager> xControllerManager (rxController, UNO_QUERY); + if (xControllerManager.is()) + { + mxConfigurationController = xControllerManager->getConfigurationController(); + + if (mxConfigurationController.is()) + { + uno::Reference<lang::XComponent> const xComppnent( + mxConfigurationController, UNO_QUERY_THROW); + xComppnent->addEventListener(this); + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceActivationRequestEvent, + Any(ResourceActivationRequestEvent)); + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceDeactivationRequestEvent, + Any(ResourceDeactivationRequestEvent)); + } + } + if (!mxConfigurationController.is()) + return; + + UpdateViewTabBar(nullptr); + + if (SvtSlideSorterBarOptions().GetVisibleImpressView()) + AddActiveMainView(FrameworkHelper::msImpressViewURL); + if (SvtSlideSorterBarOptions().GetVisibleOutlineView()) + AddActiveMainView(FrameworkHelper::msOutlineViewURL); + if (SvtSlideSorterBarOptions().GetVisibleNotesView()) + AddActiveMainView(FrameworkHelper::msNotesViewURL); + if (SvtSlideSorterBarOptions().GetVisibleHandoutView()) + AddActiveMainView(FrameworkHelper::msHandoutViewURL); + if (SvtSlideSorterBarOptions().GetVisibleSlideSorterView()) + AddActiveMainView(FrameworkHelper::msSlideSorterURL); + if (SvtSlideSorterBarOptions().GetVisibleDrawView()) + AddActiveMainView(FrameworkHelper::msDrawViewURL); + + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceActivationEvent, + Any()); +} + +SlideSorterModule::~SlideSorterModule() +{ +} + +void SlideSorterModule::SaveResourceState() +{ + SvtSlideSorterBarOptions().SetVisibleImpressView(IsResourceActive(FrameworkHelper::msImpressViewURL)); + SvtSlideSorterBarOptions().SetVisibleOutlineView(IsResourceActive(FrameworkHelper::msOutlineViewURL)); + SvtSlideSorterBarOptions().SetVisibleNotesView(IsResourceActive(FrameworkHelper::msNotesViewURL)); + SvtSlideSorterBarOptions().SetVisibleHandoutView(IsResourceActive(FrameworkHelper::msHandoutViewURL)); + SvtSlideSorterBarOptions().SetVisibleSlideSorterView(IsResourceActive(FrameworkHelper::msSlideSorterURL)); + SvtSlideSorterBarOptions().SetVisibleDrawView(IsResourceActive(FrameworkHelper::msDrawViewURL)); +} + +void SAL_CALL SlideSorterModule::notifyConfigurationChange ( + const ConfigurationChangeEvent& rEvent) +{ + if (rEvent.Type == FrameworkHelper::msResourceActivationEvent) + { + if (rEvent.ResourceId->compareTo(mxViewTabBarId) == 0) + { + // Update the view tab bar because the view tab bar has just + // become active. + UpdateViewTabBar(Reference<XTabBar>(rEvent.ResourceObject,UNO_QUERY)); + } + else if (rEvent.ResourceId->getResourceTypePrefix() == + FrameworkHelper::msViewURLPrefix + && rEvent.ResourceId->isBoundTo( + FrameworkHelper::CreateResourceId(FrameworkHelper::msCenterPaneURL), + AnchorBindingMode_DIRECT)) + { + // Update the view tab bar because the view in the center pane + // has changed. + UpdateViewTabBar(nullptr); + } + return; + } + + OSL_ASSERT(rEvent.ResourceId.is()); + sal_Int32 nEventType = 0; + rEvent.UserData >>= nEventType; + switch (nEventType) + { + case ResourceActivationRequestEvent: + if (rEvent.ResourceId->isBoundToURL( + FrameworkHelper::msCenterPaneURL, + AnchorBindingMode_DIRECT)) + { + // A resource directly bound to the center pane has been + // requested. + if (rEvent.ResourceId->getResourceTypePrefix() == + FrameworkHelper::msViewURLPrefix) + { + // The requested resource is a view. Show or hide the + // resource managed by this ResourceManager accordingly. + HandleMainViewSwitch( + rEvent.ResourceId->getResourceURL(), + true); + } + } + else if (rEvent.ResourceId->compareTo(mxResourceId) == 0) + { + // The resource managed by this ResourceManager has been + // explicitly been requested (maybe by us). Remember this + // setting. + HandleResourceRequest(true, rEvent.Configuration); + } + break; + + case ResourceDeactivationRequestEvent: + if (rEvent.ResourceId->compareTo(mxMainViewAnchorId) == 0) + { + HandleMainViewSwitch( + OUString(), + false); + } + else if (rEvent.ResourceId->compareTo(mxResourceId) == 0) + { + // The resource managed by this ResourceManager has been + // explicitly been requested to be hidden (maybe by us). + // Remember this setting. + HandleResourceRequest(false, rEvent.Configuration); + } + break; + } +} + +void SlideSorterModule::UpdateViewTabBar (const Reference<XTabBar>& rxTabBar) +{ + if ( ! mxControllerManager.is()) + return; + + Reference<XTabBar> xBar (rxTabBar); + if ( ! xBar.is()) + { + Reference<XConfigurationController> xCC ( + mxControllerManager->getConfigurationController()); + if (xCC.is()) + xBar.set(xCC->getResource(mxViewTabBarId), UNO_QUERY); + } + + if (!xBar.is()) + return; + + TabBarButton aButtonA; + aButtonA.ResourceId = FrameworkHelper::CreateResourceId( + FrameworkHelper::msSlideSorterURL, + FrameworkHelper::msCenterPaneURL); + aButtonA.ButtonLabel = SdResId(STR_SLIDE_SORTER_MODE); + + TabBarButton aButtonB; + aButtonB.ResourceId = FrameworkHelper::CreateResourceId( + FrameworkHelper::msHandoutViewURL, + FrameworkHelper::msCenterPaneURL); + + if ( ! xBar->hasTabBarButton(aButtonA)) + xBar->addTabBarButtonAfter(aButtonA, aButtonB); +} + +void SlideSorterModule::AddActiveMainView ( + const OUString& rsMainViewURL) +{ + maActiveMainViewContainer.insert(rsMainViewURL); +} + +bool SlideSorterModule::IsResourceActive ( + const OUString& rsMainViewURL) +{ + return (maActiveMainViewContainer.find(rsMainViewURL) != maActiveMainViewContainer.end()); +} + +void SlideSorterModule::disposing(std::unique_lock<std::mutex>&) +{ + if (mxConfigurationController.is()) + { + uno::Reference<lang::XComponent> const xComponent(mxConfigurationController, UNO_QUERY); + if (xComponent.is()) + xComponent->removeEventListener(this); + + mxConfigurationController->removeConfigurationChangeListener(this); + mxConfigurationController = nullptr; + } +} + +void SlideSorterModule::HandleMainViewSwitch ( + const OUString& rsViewURL, + const bool bIsActivated) +{ + if (bIsActivated) + msCurrentMainViewURL = rsViewURL; + else + msCurrentMainViewURL.clear(); + + if (!mxConfigurationController.is()) + return; + + ConfigurationController::Lock aLock (mxConfigurationController); + + if (maActiveMainViewContainer.find(msCurrentMainViewURL) + != maActiveMainViewContainer.end()) + { + // Activate resource. + mxConfigurationController->requestResourceActivation( + mxResourceId->getAnchor(), + ResourceActivationMode_ADD); + mxConfigurationController->requestResourceActivation( + mxResourceId, + ResourceActivationMode_REPLACE); + } + else + { + mxConfigurationController->requestResourceDeactivation(mxResourceId); + } +} + +void SlideSorterModule::HandleResourceRequest( + bool bActivation, + const Reference<XConfiguration>& rxConfiguration) +{ + Sequence<Reference<XResourceId> > aCenterViews = rxConfiguration->getResources( + FrameworkHelper::CreateResourceId(FrameworkHelper::msCenterPaneURL), + FrameworkHelper::msViewURLPrefix, + AnchorBindingMode_DIRECT); + if (aCenterViews.getLength() == 1) + { + if (bActivation) + { + maActiveMainViewContainer.insert(aCenterViews[0]->getResourceURL()); + } + else + { + maActiveMainViewContainer.erase(aCenterViews[0]->getResourceURL()); + } + } +} + +void SAL_CALL SlideSorterModule::disposing ( + const lang::EventObject& rEvent) +{ + if (mxConfigurationController.is() + && rEvent.Source == mxConfigurationController) + { + SaveResourceState(); + // Without the configuration controller this class can do nothing. + mxConfigurationController = nullptr; + dispose(); + } +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/SlideSorterModule.hxx b/sd/source/ui/framework/module/SlideSorterModule.hxx new file mode 100644 index 000000000..bec9f5c3c --- /dev/null +++ b/sd/source/ui/framework/module/SlideSorterModule.hxx @@ -0,0 +1,97 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/drawing/framework/XConfigurationChangeListener.hpp> +#include <comphelper/compbase.hxx> +#include <memory> +#include <set> + +namespace com::sun::star::drawing::framework { class XConfigurationController; } +namespace com::sun::star::drawing::framework { class XControllerManager; } +namespace com::sun::star::drawing::framework { class XTabBar; } +namespace com::sun::star::frame { class XController; } + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XConfigurationChangeListener + > SlideSorterModuleBase; + +/** This module is responsible for showing the slide sorter bar and the + slide sorter view in the center pane. + + Manage the activation state of one resource depending on the view in the + center pane. The ResourceManager remembers in which configuration to + activate and in which to deactivate the resource. When the resource is + deactivated or activated manually by the user then the ResourceManager + detects this and remembers it for the future. +*/ +class SlideSorterModule final + : public SlideSorterModuleBase +{ +public: + SlideSorterModule ( + const css::uno::Reference<css::frame::XController>& rxController, + const OUString& rsLeftPaneURL); + virtual ~SlideSorterModule() override; + + /** Remember the given URL as one of a center pane view for which to + activate the resource managed by the called object. + */ + void AddActiveMainView (const OUString& rsMainViewURL); + bool IsResourceActive (const OUString& rsMainViewURL); + void SaveResourceState(); + + virtual void disposing(std::unique_lock<std::mutex>&) override; + + // XConfigurationChangeListener + virtual void SAL_CALL notifyConfigurationChange ( + const css::drawing::framework::ConfigurationChangeEvent& rEvent) override; + + // XEventListener + virtual void SAL_CALL disposing ( + const css::lang::EventObject& rEvent) override; + +private: + css::uno::Reference<css::drawing::framework::XConfigurationController> + mxConfigurationController; + ::std::set<OUString> maActiveMainViewContainer; + /// The resource managed by this class. + css::uno::Reference<css::drawing::framework::XResourceId> mxResourceId; + /// The anchor of the main view. + css::uno::Reference<css::drawing::framework::XResourceId> mxMainViewAnchorId; + OUString msCurrentMainViewURL; + css::uno::Reference<css::drawing::framework::XResourceId> mxViewTabBarId; + css::uno::Reference<css::drawing::framework::XControllerManager> mxControllerManager; + + void HandleMainViewSwitch ( + const OUString& rsViewURL, + const bool bIsActivated); + void HandleResourceRequest( + bool bActivation, + const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration); + void UpdateViewTabBar ( + const css::uno::Reference<css::drawing::framework::XTabBar>& rxViewTabBar); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/ToolBarModule.cxx b/sd/source/ui/framework/module/ToolBarModule.cxx new file mode 100644 index 000000000..3cecf7b03 --- /dev/null +++ b/sd/source/ui/framework/module/ToolBarModule.cxx @@ -0,0 +1,191 @@ +/* -*- 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 "ToolBarModule.hxx" +#include <ViewShellBase.hxx> +#include <DrawController.hxx> +#include <comphelper/servicehelper.hxx> +#include <framework/FrameworkHelper.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +using ::sd::framework::FrameworkHelper; + +namespace { + const sal_Int32 gnConfigurationUpdateStartEvent(0); + const sal_Int32 gnConfigurationUpdateEndEvent(1); + const sal_Int32 gnResourceActivationRequestEvent(2); + const sal_Int32 gnResourceDeactivationRequestEvent(3); +} + +namespace sd::framework { + +//===== ToolBarModule ========================================================= + +ToolBarModule::ToolBarModule ( + const Reference<frame::XController>& rxController) + : mpBase(nullptr), + mbMainViewSwitchUpdatePending(false) +{ + // Tunnel through the controller to obtain a ViewShellBase. + auto pController = comphelper::getFromUnoTunnel<sd::DrawController>(rxController); + if (pController != nullptr) + mpBase = pController->GetViewShellBase(); + + Reference<XControllerManager> xControllerManager (rxController, UNO_QUERY); + if (!xControllerManager.is()) + return; + + mxConfigurationController = xControllerManager->getConfigurationController(); + if (!mxConfigurationController.is()) + return; + + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msConfigurationUpdateStartEvent, + Any(gnConfigurationUpdateStartEvent)); + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msConfigurationUpdateEndEvent, + Any(gnConfigurationUpdateEndEvent)); + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceActivationRequestEvent, + Any(gnResourceActivationRequestEvent)); + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceDeactivationRequestEvent, + Any(gnResourceDeactivationRequestEvent)); +} + +ToolBarModule::~ToolBarModule() +{ +} + +void ToolBarModule::disposing(std::unique_lock<std::mutex>&) +{ + if (mxConfigurationController.is()) + { + mxConfigurationController->removeConfigurationChangeListener(this); + mxConfigurationController = nullptr; + } +} + +void SAL_CALL ToolBarModule::notifyConfigurationChange ( + const ConfigurationChangeEvent& rEvent) +{ + if (!mxConfigurationController.is()) + return; + + sal_Int32 nEventType = 0; + rEvent.UserData >>= nEventType; + switch (nEventType) + { + case gnConfigurationUpdateStartEvent: + HandleUpdateStart(); + break; + + case gnConfigurationUpdateEndEvent: + HandleUpdateEnd(); + break; + + case gnResourceActivationRequestEvent: + case gnResourceDeactivationRequestEvent: + // Remember the request for the activation or deactivation + // of the center pane view. When that happens then on end + // of the next configuration update the set of visible tool + // bars will be updated. + if ( ! mbMainViewSwitchUpdatePending) + if (rEvent.ResourceId->getResourceURL().match( + FrameworkHelper::msViewURLPrefix) + && rEvent.ResourceId->isBoundToURL( + FrameworkHelper::msCenterPaneURL, AnchorBindingMode_DIRECT)) + { + mbMainViewSwitchUpdatePending = true; + } + break; + } +} + +void ToolBarModule::HandleUpdateStart() +{ + // Lock the ToolBarManager and tell it to lock the ViewShellManager as + // well. This way the ToolBarManager can optimize the releasing of + // locks and arranging of updates of both tool bars and the view shell + // stack. + if (mpBase != nullptr) + { + std::shared_ptr<ToolBarManager> pToolBarManager (mpBase->GetToolBarManager()); + mpToolBarManagerLock.reset(new ToolBarManager::UpdateLock(pToolBarManager)); + pToolBarManager->LockViewShellManager(); + } +} + +void ToolBarModule::HandleUpdateEnd() +{ + if (mbMainViewSwitchUpdatePending) + { + mbMainViewSwitchUpdatePending = false; + // Update the set of visible tool bars and deactivate those that are + // no longer visible. This is done before the old view shell is + // destroyed in order to avoid unnecessary updates of those tool + // bars. + std::shared_ptr<ToolBarManager> pToolBarManager (mpBase->GetToolBarManager()); + std::shared_ptr<FrameworkHelper> pFrameworkHelper ( + FrameworkHelper::Instance(*mpBase)); + ViewShell* pViewShell + = pFrameworkHelper->GetViewShell(FrameworkHelper::msCenterPaneURL).get(); + if (pViewShell != nullptr) + { + pToolBarManager->MainViewShellChanged(*pViewShell); + pToolBarManager->SelectionHasChanged( + *pViewShell, + *pViewShell->GetView()); + pToolBarManager->PreUpdate(); + } + else + { + pToolBarManager->MainViewShellChanged(); + pToolBarManager->PreUpdate(); + } + } + + // Releasing the update lock of the ToolBarManager will let the + // ToolBarManager with the help of the ViewShellManager take care of + // updating tool bars and view shell with the minimal amount of + // shell stack modifications and tool bar updates. + mpToolBarManagerLock.reset(); +} + +void SAL_CALL ToolBarModule::disposing (const lang::EventObject& rEvent) +{ + if (mxConfigurationController.is() + && rEvent.Source == mxConfigurationController) + { + // Without the configuration controller this class can do nothing. + mxConfigurationController = nullptr; + dispose(); + } +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/ToolBarModule.hxx b/sd/source/ui/framework/module/ToolBarModule.hxx new file mode 100644 index 000000000..f9189657d --- /dev/null +++ b/sd/source/ui/framework/module/ToolBarModule.hxx @@ -0,0 +1,81 @@ +/* -*- 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 . + */ + +#pragma once + +#include <ToolBarManager.hxx> +#include <com/sun/star/drawing/framework/XConfigurationChangeListener.hpp> +#include <comphelper/compbase.hxx> +#include <o3tl/deleter.hxx> +#include <memory> + +namespace com::sun::star::drawing::framework { class XConfigurationController; } +namespace com::sun::star::frame { class XController; } + +namespace sd { +class ViewShellBase; +} + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XConfigurationChangeListener + > ToolBarModuleInterfaceBase; + +/** This module is responsible for locking the ToolBarManager during + configuration updates and for triggering ToolBarManager updates. +*/ +class ToolBarModule final + : public ToolBarModuleInterfaceBase +{ +public: + /** Create a new module. + @param rxController + This is the access point to the drawing framework. + */ + explicit ToolBarModule ( + const css::uno::Reference<css::frame::XController>& rxController); + virtual ~ToolBarModule() override; + + virtual void disposing(std::unique_lock<std::mutex>&) override; + + // XConfigurationChangeListener + + virtual void SAL_CALL notifyConfigurationChange ( + const css::drawing::framework::ConfigurationChangeEvent& rEvent) override; + + // XEventListener + + virtual void SAL_CALL disposing ( + const css::lang::EventObject& rEvent) override; + +private: + css::uno::Reference< + css::drawing::framework::XConfigurationController> mxConfigurationController; + ViewShellBase* mpBase; + std::unique_ptr<ToolBarManager::UpdateLock, o3tl::default_delete<ToolBarManager::UpdateLock>> mpToolBarManagerLock; + bool mbMainViewSwitchUpdatePending; + + void HandleUpdateStart(); + void HandleUpdateEnd(); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/ViewTabBarModule.cxx b/sd/source/ui/framework/module/ViewTabBarModule.cxx new file mode 100644 index 000000000..4f5dd4828 --- /dev/null +++ b/sd/source/ui/framework/module/ViewTabBarModule.cxx @@ -0,0 +1,180 @@ +/* -*- 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 "ViewTabBarModule.hxx" + +#include <framework/FrameworkHelper.hxx> +#include <com/sun/star/drawing/framework/XControllerManager.hpp> +#include <com/sun/star/drawing/framework/XTabBar.hpp> +#include <com/sun/star/frame/XController.hpp> + +#include <strings.hrc> +#include <sdresid.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +using ::sd::framework::FrameworkHelper; + +namespace { + +const sal_Int32 ResourceActivationRequestEvent = 0; +const sal_Int32 ResourceDeactivationRequestEvent = 1; +const sal_Int32 ResourceActivationEvent = 2; + +} + +namespace sd::framework { + +//===== ViewTabBarModule ================================================== + +ViewTabBarModule::ViewTabBarModule ( + const Reference<frame::XController>& rxController, + const Reference<XResourceId>& rxViewTabBarId) + : mxViewTabBarId(rxViewTabBarId) +{ + Reference<XControllerManager> xControllerManager (rxController, UNO_QUERY); + + if (!xControllerManager.is()) + return; + + mxConfigurationController = xControllerManager->getConfigurationController(); + if (!mxConfigurationController.is()) + return; + + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceActivationRequestEvent, + Any(ResourceActivationRequestEvent)); + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceDeactivationRequestEvent, + Any(ResourceDeactivationRequestEvent)); + + UpdateViewTabBar(nullptr); + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceActivationEvent, + Any(ResourceActivationEvent)); +} + +ViewTabBarModule::~ViewTabBarModule() +{ +} + +void ViewTabBarModule::disposing(std::unique_lock<std::mutex>&) +{ + if (mxConfigurationController.is()) + { + mxConfigurationController->removeConfigurationChangeListener(this); + mxConfigurationController = nullptr; + } +} + +void SAL_CALL ViewTabBarModule::notifyConfigurationChange ( + const ConfigurationChangeEvent& rEvent) +{ + if (!mxConfigurationController.is()) + return; + + sal_Int32 nEventType = 0; + rEvent.UserData >>= nEventType; + switch (nEventType) + { + case ResourceActivationRequestEvent: + if (mxViewTabBarId->isBoundTo(rEvent.ResourceId, AnchorBindingMode_DIRECT)) + { + mxConfigurationController->requestResourceActivation( + mxViewTabBarId, + ResourceActivationMode_ADD); + } + break; + + case ResourceDeactivationRequestEvent: + if (mxViewTabBarId->isBoundTo(rEvent.ResourceId, AnchorBindingMode_DIRECT)) + { + mxConfigurationController->requestResourceDeactivation(mxViewTabBarId); + } + break; + + case ResourceActivationEvent: + if (rEvent.ResourceId->compareTo(mxViewTabBarId) == 0) + { + UpdateViewTabBar(Reference<XTabBar>(rEvent.ResourceObject,UNO_QUERY)); + } + } +} + +void SAL_CALL ViewTabBarModule::disposing ( + const lang::EventObject& rEvent) +{ + if (mxConfigurationController.is() + && rEvent.Source == mxConfigurationController) + { + // Without the configuration controller this class can do nothing. + mxConfigurationController = nullptr; + dispose(); + } +} + +void ViewTabBarModule::UpdateViewTabBar (const Reference<XTabBar>& rxTabBar) +{ + if (!mxConfigurationController.is()) + return; + + Reference<XTabBar> xBar (rxTabBar); + if ( ! xBar.is()) + xBar.set( mxConfigurationController->getResource(mxViewTabBarId), UNO_QUERY); + + if (!xBar.is()) + return; + + TabBarButton aEmptyButton; + + Reference<XResourceId> xAnchor (mxViewTabBarId->getAnchor()); + + TabBarButton aImpressViewButton; + aImpressViewButton.ResourceId = FrameworkHelper::CreateResourceId( + FrameworkHelper::msImpressViewURL, + xAnchor); + aImpressViewButton.ButtonLabel = SdResId(STR_NORMAL_MODE); + if ( ! xBar->hasTabBarButton(aImpressViewButton)) + xBar->addTabBarButtonAfter(aImpressViewButton, aEmptyButton); + + TabBarButton aOutlineViewButton; + aOutlineViewButton.ResourceId = FrameworkHelper::CreateResourceId( + FrameworkHelper::msOutlineViewURL, + xAnchor); + aOutlineViewButton.ButtonLabel = SdResId(STR_OUTLINE_MODE); + if ( ! xBar->hasTabBarButton(aOutlineViewButton)) + xBar->addTabBarButtonAfter(aOutlineViewButton, aImpressViewButton); + + TabBarButton aNotesViewButton; + aNotesViewButton.ResourceId = FrameworkHelper::CreateResourceId( + FrameworkHelper::msNotesViewURL, + xAnchor); + aNotesViewButton.ButtonLabel = SdResId(STR_NOTES_MODE); + if ( ! xBar->hasTabBarButton(aNotesViewButton)) + xBar->addTabBarButtonAfter(aNotesViewButton, aOutlineViewButton); +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/ViewTabBarModule.hxx b/sd/source/ui/framework/module/ViewTabBarModule.hxx new file mode 100644 index 000000000..bfb252b8d --- /dev/null +++ b/sd/source/ui/framework/module/ViewTabBarModule.hxx @@ -0,0 +1,83 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/drawing/framework/XConfigurationChangeListener.hpp> +#include <comphelper/compbase.hxx> + +namespace com::sun::star::drawing::framework { class XConfigurationController; } +namespace com::sun::star::drawing::framework { class XTabBar; } +namespace com::sun::star::frame { class XController; } + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XConfigurationChangeListener + > ViewTabBarModuleInterfaceBase; + +/** This module is responsible for showing the ViewTabBar above the view in + the center pane. +*/ +class ViewTabBarModule + : public ViewTabBarModuleInterfaceBase +{ +public: + /** Create a new module that controls the view tab bar above the view + in the specified pane. + @param rxController + This is the access point to the drawing framework. + @param rxViewTabBarId + This ResourceId specifies which tab bar is to be managed by the + new module. + */ + ViewTabBarModule ( + const css::uno::Reference<css::frame::XController>& rxController, + const css::uno::Reference< + css::drawing::framework::XResourceId>& rxViewTabBarId); + virtual ~ViewTabBarModule() override; + + virtual void disposing(std::unique_lock<std::mutex>&) override; + + // XConfigurationChangeListener + + virtual void SAL_CALL notifyConfigurationChange ( + const css::drawing::framework::ConfigurationChangeEvent& rEvent) override; + + // XEventListener + + virtual void SAL_CALL disposing ( + const css::lang::EventObject& rEvent) override; + +private: + css::uno::Reference< + css::drawing::framework::XConfigurationController> mxConfigurationController; + css::uno::Reference<css::drawing::framework::XResourceId> mxViewTabBarId; + + /** This is the place where the view tab bar is filled. Only missing + buttons are added, so it is safe to call this method multiple + times. + */ + void UpdateViewTabBar ( + const css::uno::Reference<css::drawing::framework::XTabBar>& rxTabBar); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/tools/FrameworkHelper.cxx b/sd/source/ui/framework/tools/FrameworkHelper.cxx new file mode 100644 index 000000000..dceecd510 --- /dev/null +++ b/sd/source/ui/framework/tools/FrameworkHelper.cxx @@ -0,0 +1,952 @@ +/* -*- 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 <osl/time.h> + +#include <framework/FrameworkHelper.hxx> + +#include <framework/ConfigurationController.hxx> +#include <framework/ResourceId.hxx> +#include <framework/ViewShellWrapper.hxx> +#include <ViewShellBase.hxx> +#include <DrawViewShell.hxx> +#include <ViewShellHint.hxx> +#include <app.hrc> +#include <com/sun/star/drawing/framework/XControllerManager.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <comphelper/servicehelper.hxx> +#include <comphelper/compbase.hxx> +#include <svl/lstner.hxx> +#include <rtl/ustrbuf.hxx> + +#include <sfx2/request.hxx> + +#include <vcl/svapp.hxx> +#include <osl/doublecheckedlocking.h> +#include <osl/getglobalmutex.hxx> +#include <tools/diagnose_ex.h> +#include <memory> +#include <unordered_map> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace { + +//----- CallbackCaller -------------------------------------------------------- + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XConfigurationChangeListener + > CallbackCallerInterfaceBase; + +/** A CallbackCaller registers as listener at an XConfigurationController + object and waits for the notification of one type of event. When that + event is received, or when the CallbackCaller detects at its + construction that the event will not be sent in the near future, the + actual callback object is called and the CallbackCaller destroys itself. +*/ +class CallbackCaller + : public CallbackCallerInterfaceBase +{ +public: + /** Create a new CallbackCaller object. This object controls its own + lifetime by acquiring a reference to itself in the constructor. + When it detects that the event will not be notified in the near + future (because the queue of pending configuration change operations + is empty and therefore no event will be sent int the near future, it + does not acquires a reference and thus initiates its destruction in + the constructor.) + @param rBase + This ViewShellBase object is used to determine the + XConfigurationController at which to register. + @param rsEventType + The event type which the callback is waiting for. + @param pCallback + The callback object which is to be notified. The caller will + typically release his reference to the caller so that when the + CallbackCaller dies (after having called the callback) the + callback is destroyed. + */ + CallbackCaller ( + const ::sd::ViewShellBase& rBase, + const OUString& rsEventType, + const ::sd::framework::FrameworkHelper::ConfigurationChangeEventFilter& rFilter, + const ::sd::framework::FrameworkHelper::Callback& rCallback); + + virtual void disposing(std::unique_lock<std::mutex>&) override; + // XEventListener + virtual void SAL_CALL disposing (const lang::EventObject& rEvent) override; + // XConfigurationChangeListener + virtual void SAL_CALL notifyConfigurationChange (const ConfigurationChangeEvent& rEvent) override; + +private: + OUString msEventType; + Reference<XConfigurationController> mxConfigurationController; + ::sd::framework::FrameworkHelper::ConfigurationChangeEventFilter maFilter; + ::sd::framework::FrameworkHelper::Callback maCallback; +}; + +//----- LifetimeController ---------------------------------------------------- + +typedef comphelper::WeakComponentImplHelper < + css::lang::XEventListener + > LifetimeControllerInterfaceBase; + +/** This class helps controlling the lifetime of the + FrameworkHelper. Register at a ViewShellBase object and an XController + object and call Dispose() at the associated FrameworkHelper object when + one of them and Release() when both of them are destroyed. +*/ +class LifetimeController + : public LifetimeControllerInterfaceBase, + public SfxListener +{ +public: + explicit LifetimeController (::sd::ViewShellBase& rBase); + virtual ~LifetimeController() override; + + /** XEventListener. This method is called when the frame::XController + is being destroyed. + */ + using WeakComponentImplHelperBase::disposing; + virtual void SAL_CALL disposing (const lang::EventObject& rEvent) override; + + /** This method is called when the ViewShellBase is being destroyed. + */ + virtual void Notify (SfxBroadcaster& rBroadcaster, const SfxHint& rHint) override; + +private: + ::sd::ViewShellBase& mrBase; + bool mbListeningToViewShellBase; + bool mbListeningToController; + + /** When one or both of the mbListeningToViewShellBase and + mbListeningToController members were modified then call this method + to either dispose or release the associated FrameworkHelper. + */ + void Update(); +}; + +} // end of anonymous namespace + +namespace sd::framework { + +namespace { + + class FrameworkHelperAllPassFilter + { + public: + bool operator() (const css::drawing::framework::ConfigurationChangeEvent&) { return true; } + }; + + class FrameworkHelperResourceIdFilter + { + public: + explicit FrameworkHelperResourceIdFilter ( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxResourceId); + bool operator() (const css::drawing::framework::ConfigurationChangeEvent& rEvent) + { return mxResourceId.is() && rEvent.ResourceId.is() + && mxResourceId->compareTo(rEvent.ResourceId) == 0; } + private: + css::uno::Reference<css::drawing::framework::XResourceId> mxResourceId; + }; + +} // end of anonymous namespace + +// Pane URLS. + +const OUString FrameworkHelper::msCenterPaneURL( msPaneURLPrefix + "CenterPane"); +const OUString FrameworkHelper::msFullScreenPaneURL( msPaneURLPrefix + "FullScreenPane"); +const OUString FrameworkHelper::msLeftImpressPaneURL( msPaneURLPrefix + "LeftImpressPane"); +const OUString FrameworkHelper::msLeftDrawPaneURL( msPaneURLPrefix + "LeftDrawPane"); + +// View URLs. + +const OUString FrameworkHelper::msImpressViewURL( msViewURLPrefix + "ImpressView"); +const OUString FrameworkHelper::msDrawViewURL( msViewURLPrefix + "GraphicView"); +const OUString FrameworkHelper::msOutlineViewURL( msViewURLPrefix + "OutlineView"); +const OUString FrameworkHelper::msNotesViewURL( msViewURLPrefix + "NotesView"); +const OUString FrameworkHelper::msHandoutViewURL( msViewURLPrefix + "HandoutView"); +const OUString FrameworkHelper::msSlideSorterURL( msViewURLPrefix + "SlideSorter"); +const OUString FrameworkHelper::msPresentationViewURL( msViewURLPrefix + "PresentationView"); +const OUString FrameworkHelper::msSidebarViewURL( msViewURLPrefix + "SidebarView"); + +// Tool bar URLs. + +const OUString FrameworkHelper::msViewTabBarURL( msToolBarURLPrefix + "ViewTabBar"); + +//----- helper ---------------------------------------------------------------- +namespace +{ + ::std::shared_ptr< ViewShell > lcl_getViewShell( const Reference< XResource >& i_rViewShellWrapper ) + { + ::std::shared_ptr< ViewShell > pViewShell; + try + { + Reference<lang::XUnoTunnel> xViewTunnel( i_rViewShellWrapper, UNO_QUERY_THROW ); + if (auto pWrapper = comphelper::getFromUnoTunnel<ViewShellWrapper>(xViewTunnel)) + pViewShell = pWrapper->GetViewShell(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } + return pViewShell; + } + Reference< XResource > lcl_getFirstViewInPane( const Reference< XConfigurationController >& i_rConfigController, + const Reference< XResourceId >& i_rPaneId ) + { + try + { + Reference< XConfiguration > xConfiguration( i_rConfigController->getRequestedConfiguration(), UNO_SET_THROW ); + Sequence< Reference< XResourceId > > aViewIds( xConfiguration->getResources( + i_rPaneId, FrameworkHelper::msViewURLPrefix, AnchorBindingMode_DIRECT ) ); + if ( aViewIds.hasElements() ) + return i_rConfigController->getResource( aViewIds[0] ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } + return nullptr; + } +} + +//----- FrameworkHelper::ViewURLMap ------------------------------------------- + +/** The ViewURLMap is used to translate between the view URLs used by the + drawing framework and the enums defined in the ViewShell class. +*/ +class FrameworkHelper::ViewURLMap + : public std::unordered_map< + OUString, + ViewShell::ShellType> +{ +public: + ViewURLMap() {} +}; + +//----- Framework::DisposeListener --------------------------------------------- + +namespace { + typedef comphelper::WeakComponentImplHelper < + css::lang::XEventListener + > FrameworkHelperDisposeListenerInterfaceBase; +} + +class FrameworkHelper::DisposeListener + : public FrameworkHelperDisposeListenerInterfaceBase +{ +public: + explicit DisposeListener (const ::std::shared_ptr<FrameworkHelper>& rpHelper); + + virtual void disposing(std::unique_lock<std::mutex>&) override; + + virtual void SAL_CALL disposing (const lang::EventObject& rEventObject) override; + +private: + ::std::shared_ptr<FrameworkHelper> mpHelper; +}; + +//----- FrameworkHelper::Deleter ---------------------------------------------- + +class FrameworkHelper::Deleter +{ +public: + void operator()(FrameworkHelper* pObject) + { + delete pObject; + } +}; + +//----- FrameworkHelper ------------------------------------------------------- + +FrameworkHelper::ViewURLMap FrameworkHelper::maViewURLMap; + +FrameworkHelper::InstanceMap FrameworkHelper::maInstanceMap; + +::std::shared_ptr<FrameworkHelper> FrameworkHelper::Instance (ViewShellBase& rBase) +{ + + ::std::shared_ptr<FrameworkHelper> pHelper; + + InstanceMap::const_iterator iHelper (maInstanceMap.find(&rBase)); + if (iHelper == maInstanceMap.end()) + { + ::osl::GetGlobalMutex aMutexFunctor; + ::osl::MutexGuard aGuard (aMutexFunctor()); + if (iHelper == maInstanceMap.end()) + { + pHelper = ::std::shared_ptr<FrameworkHelper>( + new FrameworkHelper(rBase), + FrameworkHelper::Deleter()); + pHelper->Initialize(); + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + maInstanceMap[&rBase] = pHelper; + } + } + else + { + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + pHelper = iHelper->second; + } + + return pHelper; +} + +void FrameworkHelper::DisposeInstance (const ViewShellBase& rBase) +{ + InstanceMap::iterator iHelper (maInstanceMap.find(&rBase)); + if (iHelper != maInstanceMap.end()) + { + iHelper->second->Dispose(); + } +} + +void FrameworkHelper::ReleaseInstance (const ViewShellBase& rBase) +{ + InstanceMap::iterator iHelper (maInstanceMap.find(&rBase)); + if (iHelper != maInstanceMap.end()) + maInstanceMap.erase(iHelper); +} + +FrameworkHelper::FrameworkHelper (ViewShellBase& rBase) + : mrBase(rBase) +{ + Reference<XControllerManager> xControllerManager (rBase.GetController(), UNO_QUERY); + if (xControllerManager.is()) + { + mxConfigurationController = xControllerManager->getConfigurationController(); + } + + new LifetimeController(mrBase); +} + +void FrameworkHelper::Initialize() +{ + mxDisposeListener = new DisposeListener(shared_from_this()); +} + +FrameworkHelper::~FrameworkHelper() +{ +} + +void FrameworkHelper::Dispose() +{ + if (mxDisposeListener.is()) + mxDisposeListener->dispose(); + mxConfigurationController = nullptr; +} + +bool FrameworkHelper::IsValid() const +{ + return mxConfigurationController.is(); +} + +::std::shared_ptr<ViewShell> FrameworkHelper::GetViewShell (const OUString& rsPaneURL) +{ + if ( !mxConfigurationController.is() ) + return ::std::shared_ptr<ViewShell>(); + + Reference<XResourceId> xPaneId( CreateResourceId( rsPaneURL ) ); + return lcl_getViewShell( lcl_getFirstViewInPane( mxConfigurationController, xPaneId ) ); +} + +::std::shared_ptr<ViewShell> FrameworkHelper::GetViewShell (const Reference<XView>& rxView) +{ + return lcl_getViewShell( rxView ); +} + +Reference<XView> FrameworkHelper::GetView (const Reference<XResourceId>& rxPaneOrViewId) +{ + Reference<XView> xView; + + if ( ! rxPaneOrViewId.is() || ! mxConfigurationController.is()) + return nullptr; + + try + { + if (rxPaneOrViewId->getResourceURL().match(msViewURLPrefix)) + { + xView.set( mxConfigurationController->getResource( rxPaneOrViewId ), UNO_QUERY ); + } + else + { + xView.set( lcl_getFirstViewInPane( mxConfigurationController, rxPaneOrViewId ), UNO_QUERY ); + } + } + catch (lang::DisposedException&) + { + Dispose(); + } + catch (RuntimeException&) + { + } + + return xView; +} + +Reference<XResourceId> FrameworkHelper::RequestView ( + const OUString& rsResourceURL, + const OUString& rsAnchorURL) +{ + Reference<XResourceId> xViewId; + + try + { + if (mxConfigurationController.is()) + { + mxConfigurationController->requestResourceActivation( + CreateResourceId(rsAnchorURL), + ResourceActivationMode_ADD); + xViewId = CreateResourceId(rsResourceURL, rsAnchorURL); + mxConfigurationController->requestResourceActivation( + xViewId, + ResourceActivationMode_REPLACE); + } + } + catch (lang::DisposedException&) + { + Dispose(); + xViewId = nullptr; + } + catch (RuntimeException&) + { + xViewId = nullptr; + } + + return xViewId; +} + +ViewShell::ShellType FrameworkHelper::GetViewId (const OUString& rsViewURL) +{ + if (maViewURLMap.empty()) + { + maViewURLMap[msImpressViewURL] = ViewShell::ST_IMPRESS; + maViewURLMap[msDrawViewURL] = ViewShell::ST_DRAW; + maViewURLMap[msOutlineViewURL] = ViewShell::ST_OUTLINE; + maViewURLMap[msNotesViewURL] = ViewShell::ST_NOTES; + maViewURLMap[msHandoutViewURL] = ViewShell::ST_HANDOUT; + maViewURLMap[msSlideSorterURL] = ViewShell::ST_SLIDE_SORTER; + maViewURLMap[msPresentationViewURL] = ViewShell::ST_PRESENTATION; + maViewURLMap[msSidebarViewURL] = ViewShell::ST_SIDEBAR; + } + ViewURLMap::const_iterator iView (maViewURLMap.find(rsViewURL)); + if (iView != maViewURLMap.end()) + return iView->second; + else + return ViewShell::ST_NONE; +} + +OUString FrameworkHelper::GetViewURL (ViewShell::ShellType eType) +{ + switch (eType) + { + case ViewShell::ST_IMPRESS : return msImpressViewURL; + case ViewShell::ST_DRAW : return msDrawViewURL; + case ViewShell::ST_OUTLINE : return msOutlineViewURL; + case ViewShell::ST_NOTES : return msNotesViewURL; + case ViewShell::ST_HANDOUT : return msHandoutViewURL; + case ViewShell::ST_SLIDE_SORTER : return msSlideSorterURL; + case ViewShell::ST_PRESENTATION : return msPresentationViewURL; + case ViewShell::ST_SIDEBAR : return msSidebarViewURL; + default: + return OUString(); + } +} + +namespace +{ + +void updateEditMode(const Reference<XView> &xView, const EditMode eEMode, bool updateFrameView) +{ + // Ensure we have the expected edit mode + // The check is only for DrawViewShell as OutlineViewShell + // and SlideSorterViewShell have no master mode + const ::std::shared_ptr<ViewShell> pCenterViewShell (FrameworkHelper::GetViewShell(xView)); + DrawViewShell* pDrawViewShell + = dynamic_cast<DrawViewShell*>(pCenterViewShell.get()); + if (pDrawViewShell != nullptr) + { + pCenterViewShell->Broadcast ( + ViewShellHint(ViewShellHint::HINT_CHANGE_EDIT_MODE_START)); + + pDrawViewShell->ChangeEditMode(eEMode, pDrawViewShell->IsLayerModeActive()); + if (updateFrameView) + pDrawViewShell->WriteFrameViewData(); + + pCenterViewShell->Broadcast ( + ViewShellHint(ViewShellHint::HINT_CHANGE_EDIT_MODE_END)); + } +} + +void asyncUpdateEditMode(FrameworkHelper* const pHelper, const EditMode eEMode) +{ + Reference<XResourceId> xPaneId ( + FrameworkHelper::CreateResourceId(framework::FrameworkHelper::msCenterPaneURL)); + Reference<XView> xView (pHelper->GetView(xPaneId)); + updateEditMode(xView, eEMode, true); +} + +} + +void FrameworkHelper::HandleModeChangeSlot ( + sal_uInt16 nSlotId, + SfxRequest const & rRequest) +{ + if ( ! mxConfigurationController.is()) + return; + + // Parameters are allowed for NotesMasterPage and SlideMasterPage + // for these command, transfor xxxxMasterPage with param = false + // to ActivatexxxxxMode + if (nSlotId == SID_NOTES_MASTER_MODE || nSlotId == SID_SLIDE_MASTER_MODE) + { + const SfxItemSet* pRequestArguments = rRequest.GetArgs(); + if (pRequestArguments) + { + const SfxBoolItem* pIsActive = rRequest.GetArg<SfxBoolItem>(nSlotId); + if (!pIsActive->GetValue ()) + { + if (nSlotId == SID_NOTES_MASTER_MODE) + nSlotId = SID_NOTES_MODE; + else + nSlotId = SID_NORMAL_MULTI_PANE_GUI; + } + } + } + + try + { + if ( ! mxConfigurationController.is()) + throw RuntimeException(); + + Reference<XResourceId> xPaneId ( + CreateResourceId(framework::FrameworkHelper::msCenterPaneURL)); + Reference<XView> xView (GetView(xPaneId)); + + // Compute requested view + OUString sRequestedView; + switch (nSlotId) + { + // draw + case SID_DRAWINGMODE: + // impress + case SID_NORMAL_MULTI_PANE_GUI: + case SID_SLIDE_MASTER_MODE: + sRequestedView = FrameworkHelper::msImpressViewURL; + break; + + case SID_NOTES_MODE: + case SID_NOTES_MASTER_MODE: + sRequestedView = FrameworkHelper::msNotesViewURL; + break; + + case SID_HANDOUT_MASTER_MODE: + sRequestedView = FrameworkHelper::msHandoutViewURL; + break; + + case SID_SLIDE_SORTER_MULTI_PANE_GUI: + case SID_SLIDE_SORTER_MODE: + sRequestedView = FrameworkHelper::msSlideSorterURL; + break; + + case SID_OUTLINE_MODE: + sRequestedView = FrameworkHelper::msOutlineViewURL; + break; + } + + // Compute requested mode + EditMode eEMode = EditMode::Page; + if (nSlotId == SID_SLIDE_MASTER_MODE + || nSlotId == SID_NOTES_MASTER_MODE + || nSlotId == SID_HANDOUT_MASTER_MODE) + eEMode = EditMode::MasterPage; + // Ensure we have the expected view shell + if (!(xView.is() && xView->getResourceId()->getResourceURL() == sRequestedView)) + + { + const auto xId = CreateResourceId(sRequestedView, msCenterPaneURL); + mxConfigurationController->requestResourceActivation( + xId, + ResourceActivationMode_REPLACE); + RunOnResourceActivation(xId, std::bind(&asyncUpdateEditMode, this, eEMode)); + } + else + { + updateEditMode(xView, eEMode, false); + } + } + catch (RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } +} + +void FrameworkHelper::RunOnConfigurationEvent( + const OUString& rsEventType, + const Callback& rCallback) +{ + RunOnEvent( + rsEventType, + FrameworkHelperAllPassFilter(), + rCallback); +} + +void FrameworkHelper::RunOnResourceActivation( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxResourceId, + const Callback& rCallback) +{ + if (mxConfigurationController.is() + && mxConfigurationController->getResource(rxResourceId).is()) + { + rCallback(false); + } + else + { + RunOnEvent( + msResourceActivationEvent, + FrameworkHelperResourceIdFilter(rxResourceId), + rCallback); + } +} + +namespace { + +/** A callback that sets a flag to a specified value when the callback is + called. +*/ +class FlagUpdater +{ +public: + explicit FlagUpdater (bool& rFlag) : mrFlag(rFlag) {} + void operator() (bool) const {mrFlag = true;} +private: + bool& mrFlag; +}; + +} + +void FrameworkHelper::RequestSynchronousUpdate() +{ + rtl::Reference<ConfigurationController> pCC ( + dynamic_cast<ConfigurationController*>(mxConfigurationController.get())); + if (pCC.is()) + pCC->RequestSynchronousUpdate(); +} + +void FrameworkHelper::WaitForEvent (const OUString& rsEventType) const +{ + bool bConfigurationUpdateSeen (false); + + RunOnEvent( + rsEventType, + FrameworkHelperAllPassFilter(), + FlagUpdater(bConfigurationUpdateSeen)); + + sal_uInt32 nStartTime = osl_getGlobalTimer(); + while ( ! bConfigurationUpdateSeen) + { + Application::Reschedule(); + + if( (osl_getGlobalTimer() - nStartTime) > 60000 ) + { + OSL_FAIL("FrameworkHelper::WaitForEvent(), no event for a minute? giving up!"); + break; + } + } +} + +void FrameworkHelper::WaitForUpdate() const +{ + WaitForEvent(msConfigurationUpdateEndEvent); +} + +void FrameworkHelper::RunOnEvent( + const OUString& rsEventType, + const ConfigurationChangeEventFilter& rFilter, + const Callback& rCallback) const +{ + new CallbackCaller(mrBase,rsEventType,rFilter,rCallback); +} + +void FrameworkHelper::disposing (const lang::EventObject& rEventObject) +{ + if (rEventObject.Source == mxConfigurationController) + mxConfigurationController = nullptr; +} + +void FrameworkHelper::UpdateConfiguration() +{ + if (!mxConfigurationController.is()) + return; + + try + { + if (mxConfigurationController.is()) + mxConfigurationController->update(); + } + catch (lang::DisposedException&) + { + Dispose(); + } + catch (RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } +} + +OUString FrameworkHelper::ResourceIdToString (const Reference<XResourceId>& rxResourceId) +{ + OUStringBuffer sString; + if (rxResourceId.is()) + { + sString.append(rxResourceId->getResourceURL()); + if (rxResourceId->hasAnchor()) + { + const Sequence<OUString> aAnchorURLs (rxResourceId->getAnchorURLs()); + for (const auto& rAnchorURL : aAnchorURLs) + { + sString.append(" | "); + sString.append(rAnchorURL); + } + } + } + return sString.makeStringAndClear(); +} + +Reference<XResourceId> FrameworkHelper::CreateResourceId (const OUString& rsResourceURL) +{ + return new ::sd::framework::ResourceId(rsResourceURL); +} + +Reference<XResourceId> FrameworkHelper::CreateResourceId ( + const OUString& rsResourceURL, + const OUString& rsAnchorURL) +{ + return new ::sd::framework::ResourceId(rsResourceURL, rsAnchorURL); +} + +Reference<XResourceId> FrameworkHelper::CreateResourceId ( + const OUString& rsResourceURL, + const Reference<XResourceId>& rxAnchorId) +{ + if (rxAnchorId.is()) + return new ::sd::framework::ResourceId( + rsResourceURL, + rxAnchorId->getResourceURL(), + rxAnchorId->getAnchorURLs()); + else + return new ::sd::framework::ResourceId(rsResourceURL); +} + +//----- FrameworkHelper::DisposeListener -------------------------------------- + +FrameworkHelper::DisposeListener::DisposeListener ( + const ::std::shared_ptr<FrameworkHelper>& rpHelper) + : mpHelper(rpHelper) +{ + Reference<XComponent> xComponent (mpHelper->mxConfigurationController, UNO_QUERY); + if (xComponent.is()) + xComponent->addEventListener(this); +} + +void FrameworkHelper::DisposeListener::disposing(std::unique_lock<std::mutex>&) +{ + Reference<XComponent> xComponent (mpHelper->mxConfigurationController, UNO_QUERY); + if (xComponent.is()) + xComponent->removeEventListener(this); + + mpHelper.reset(); +} + +void SAL_CALL FrameworkHelper::DisposeListener::disposing (const lang::EventObject& rEventObject) +{ + if (mpHelper != nullptr) + mpHelper->disposing(rEventObject); +} + +//===== FrameworkHelperResourceIdFilter ======================================= + +FrameworkHelperResourceIdFilter::FrameworkHelperResourceIdFilter ( + const Reference<XResourceId>& rxResourceId) + : mxResourceId(rxResourceId) +{ +} + +} // end of namespace sd::framework + +namespace { + +//===== CallbackCaller ======================================================== + +CallbackCaller::CallbackCaller ( + const ::sd::ViewShellBase& rBase, + const OUString& rsEventType, + const ::sd::framework::FrameworkHelper::ConfigurationChangeEventFilter& rFilter, + const ::sd::framework::FrameworkHelper::Callback& rCallback) + : msEventType(rsEventType), + maFilter(rFilter), + maCallback(rCallback) +{ + try + { + Reference<XControllerManager> xControllerManager (rBase.GetController(), UNO_QUERY_THROW); + mxConfigurationController = xControllerManager->getConfigurationController(); + if (mxConfigurationController.is()) + { + if (mxConfigurationController->hasPendingRequests()) + mxConfigurationController->addConfigurationChangeListener(this,msEventType,Any()); + else + { + // There are no requests waiting to be processed. Therefore + // no event, especially not the one we are waiting for, will + // be sent in the near future and the callback would never be + // called. + // Call the callback now and tell him that the event it is + // waiting for was not sent. + mxConfigurationController = nullptr; + maCallback(false); + } + } + } + catch (RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } +} + +void CallbackCaller::disposing(std::unique_lock<std::mutex>&) +{ + try + { + if (mxConfigurationController.is()) + { + Reference<XConfigurationController> xCC (mxConfigurationController); + mxConfigurationController = nullptr; + xCC->removeConfigurationChangeListener(this); + } + } + catch (RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } +} + +void SAL_CALL CallbackCaller::disposing (const lang::EventObject& rEvent) +{ + if (rEvent.Source == mxConfigurationController) + { + mxConfigurationController = nullptr; + maCallback(false); + } +} + +void SAL_CALL CallbackCaller::notifyConfigurationChange ( + const ConfigurationChangeEvent& rEvent) +{ + if (!(rEvent.Type == msEventType && maFilter(rEvent))) + return; + + maCallback(true); + if (mxConfigurationController.is()) + { + // Reset the reference to the configuration controller so that + // dispose() will not try to remove the listener a second time. + Reference<XConfigurationController> xCC (mxConfigurationController); + mxConfigurationController = nullptr; + + // Removing this object from the controller may very likely lead + // to its destruction, so no calls after that. + xCC->removeConfigurationChangeListener(this); + } +} + +//----- LifetimeController ------------------------------------------------- + +LifetimeController::LifetimeController (::sd::ViewShellBase& rBase) + : mrBase(rBase), + mbListeningToViewShellBase(false), + mbListeningToController(false) +{ + // Register as listener at the ViewShellBase. Because that is not done + // via a reference we have to increase the reference count manually. + // This is necessary even though listening to the XController did + // increase the reference count because the controller may release its + // reference to us before the ViewShellBase is destroyed. + StartListening(mrBase); + acquire(); + mbListeningToViewShellBase = true; + + Reference<XComponent> xComponent = rBase.GetController(); + if (xComponent.is()) + { + xComponent->addEventListener(this); + mbListeningToController = true; + } +} + +LifetimeController::~LifetimeController() +{ + OSL_ASSERT(!mbListeningToController && !mbListeningToViewShellBase); +} + +void SAL_CALL LifetimeController::disposing (const lang::EventObject&) +{ + mbListeningToController = false; + Update(); +} + +void LifetimeController::Notify (SfxBroadcaster&, const SfxHint& rHint) +{ + if (rHint.GetId() == SfxHintId::Dying) + { + mbListeningToViewShellBase = false; + Update(); + release(); + } +} + +void LifetimeController::Update() +{ + if (mbListeningToViewShellBase && mbListeningToController) + { + // Both the controller and the ViewShellBase are alive. Keep + // waiting for their destruction. + } + else if (mbListeningToViewShellBase) + { + // The controller has been destroyed but the ViewShellBase is still + // alive. Dispose the associated FrameworkHelper but keep it around + // so that no new instance is created for the dying framework. + ::sd::framework::FrameworkHelper::DisposeInstance(mrBase); + } + else + { + // Both the controller and the ViewShellBase have been destroyed. + // Remove the FrameworkHelper so that the next call its Instance() + // method can create a new instance. + ::sd::framework::FrameworkHelper::ReleaseInstance(mrBase); + } +} + +} // end of anonymous namespace. + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/bulmaper.cxx b/sd/source/ui/func/bulmaper.cxx new file mode 100644 index 000000000..67a667891 --- /dev/null +++ b/sd/source/ui/func/bulmaper.cxx @@ -0,0 +1,104 @@ +/* -*- 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 <editeng/editids.hrc> + +//-> Fonts & Items +#include <vcl/font.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/crossedoutitem.hxx> +#include <editeng/postitem.hxx> +#include <editeng/contouritem.hxx> +#include <editeng/shdditem.hxx> + +//<- Fonts & Items +#include <svl/itemset.hxx> +#include <svl/itempool.hxx> +#include <editeng/numitem.hxx> + +#include <bulmaper.hxx> + +#define GetWhich(nSlot) rSet.GetPool()->GetWhich( nSlot ) + +void SdBulletMapper::MapFontsInNumRule( SvxNumRule& aNumRule, const SfxItemSet& rSet ) +{ + const sal_uInt16 nCount = aNumRule.GetLevelCount(); + for( sal_uInt16 nLevel = 0; nLevel < nCount; nLevel++ ) + { + const SvxNumberFormat& rSrcLevel = aNumRule.GetLevel(nLevel); + SvxNumberFormat aNewLevel( rSrcLevel ); + + if(rSrcLevel.GetNumberingType() != css::style::NumberingType::CHAR_SPECIAL && + rSrcLevel.GetNumberingType() != css::style::NumberingType::NUMBER_NONE ) + { + // if enumeration instead bullet is chosen, adjust bullet font to template font + + // to be implemented if module supports CJK + + vcl::Font aMyFont; + const SvxFontItem& rFItem = + static_cast<const SvxFontItem&>(rSet.Get(GetWhich( sal_uInt16(SID_ATTR_CHAR_FONT) ))); + aMyFont.SetFamily(rFItem.GetFamily()); + aMyFont.SetFamilyName(rFItem.GetFamilyName()); + aMyFont.SetCharSet(rFItem.GetCharSet()); + aMyFont.SetPitch(rFItem.GetPitch()); + + const SvxFontHeightItem& rFHItem = + static_cast<const SvxFontHeightItem&>(rSet.Get(GetWhich( sal_uInt16(SID_ATTR_CHAR_FONTHEIGHT) ))); + aMyFont.SetFontSize(Size(0, rFHItem.GetHeight())); + + const SvxWeightItem& rWItem = + static_cast<const SvxWeightItem&>(rSet.Get(GetWhich( sal_uInt16(SID_ATTR_CHAR_WEIGHT) ))); + aMyFont.SetWeight(rWItem.GetWeight()); + + const SvxPostureItem& rPItem = + static_cast<const SvxPostureItem&>(rSet.Get(GetWhich( sal_uInt16(SID_ATTR_CHAR_POSTURE) ))); + aMyFont.SetItalic(rPItem.GetPosture()); + + const SvxUnderlineItem& rUItem = rSet.Get(GetWhich(SID_ATTR_CHAR_UNDERLINE)); + aMyFont.SetUnderline(rUItem.GetLineStyle()); + + const SvxOverlineItem& rOItem = static_cast<const SvxOverlineItem&>(rSet.Get(GetWhich(SID_ATTR_CHAR_OVERLINE))); + aMyFont.SetOverline(rOItem.GetLineStyle()); + + const SvxCrossedOutItem& rCOItem = static_cast<const SvxCrossedOutItem&>(rSet.Get(GetWhich(SID_ATTR_CHAR_STRIKEOUT))); + aMyFont.SetStrikeout(rCOItem.GetStrikeout()); + + const SvxContourItem& rCItem = static_cast<const SvxContourItem&>(rSet.Get(GetWhich(SID_ATTR_CHAR_CONTOUR))); + aMyFont.SetOutline(rCItem.GetValue()); + + const SvxShadowedItem& rSItem = static_cast<const SvxShadowedItem&>(rSet.Get(GetWhich(SID_ATTR_CHAR_SHADOWED))); + aMyFont.SetShadow(rSItem.GetValue()); + + aNewLevel.SetBulletFont(&aMyFont); + aNumRule.SetLevel(nLevel, aNewLevel ); + } + else if( rSrcLevel.GetNumberingType() == css::style::NumberingType::CHAR_SPECIAL ) + { + aNewLevel.SetPrefix(""); + aNewLevel.SetSuffix(""); + aNumRule.SetLevel(nLevel, aNewLevel ); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuarea.cxx b/sd/source/ui/func/fuarea.cxx new file mode 100644 index 000000000..8dd7543e3 --- /dev/null +++ b/sd/source/ui/func/fuarea.cxx @@ -0,0 +1,99 @@ +/* -*- 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 <fuarea.hxx> + +#include <svx/svxids.hrc> +#include <sfx2/request.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/bindings.hxx> +#include <ViewShell.hxx> + +#include <drawdoc.hxx> +#include <View.hxx> +#include <svx/svxdlg.hxx> + +namespace sd { + +FuArea::FuArea( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* _pView, SdDrawDocument* pDoc, SfxRequest& rReq) +: FuPoor(pViewSh, pWin, _pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuArea::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* _pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuArea( pViewSh, pWin, _pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuArea::DoExecute( SfxRequest& rReq ) +{ + rReq.Ignore (); + + const SfxItemSet* pArgs = rReq.GetArgs(); + if (pArgs) + return; + + SfxItemSet aNewAttr( mpDoc->GetPool() ); + mpView->GetAttributes( aNewAttr ); + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + bool bHasSlideBackground = mpViewShell->GetDoc()->GetDocumentType() == DocumentType::Impress; + VclPtr<AbstractSvxAreaTabDialog> pDlg( + pFact->CreateSvxAreaTabDialog(mpViewShell->GetFrameWeld(), &aNewAttr, mpDoc, true, bHasSlideBackground)); + + pDlg->StartExecuteAsync([pDlg, pView = this->mpView, pViewShell = this->mpViewShell](sal_Int32 nResult){ + if (nResult == RET_OK) + { + pView->SetAttributes (*(pDlg->GetOutputItemSet ())); + + // attributes changed, update Listboxes in Objectbars + static const sal_uInt16 SidArray[] = { + SID_ATTR_FILL_STYLE, + SID_ATTR_FILL_COLOR, + SID_ATTR_FILL_GRADIENT, + SID_ATTR_FILL_HATCH, + SID_ATTR_FILL_BITMAP, + SID_ATTR_FILL_TRANSPARENCE, + SID_ATTR_FILL_FLOATTRANSPARENCE, + SID_ATTR_FILL_USE_SLIDE_BACKGROUND, + 0 }; + + pViewShell->GetViewFrame()->GetBindings().Invalidate( SidArray ); + } + + // deferred until the dialog ends + pViewShell->Cancel(); + + pDlg->disposeOnce(); + }); +} + +void FuArea::Activate() +{ +} + +void FuArea::Deactivate() +{ +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fubullet.cxx b/sd/source/ui/func/fubullet.cxx new file mode 100644 index 000000000..ab0cf7de8 --- /dev/null +++ b/sd/source/ui/func/fubullet.cxx @@ -0,0 +1,330 @@ +/* -*- 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 <fubullet.hxx> + +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <editeng/eeitem.hxx> +#include <svl/poolitem.hxx> +#include <editeng/fontitem.hxx> +#include <OutlineView.hxx> +#include <OutlineViewShell.hxx> +#include <DrawViewShell.hxx> +#include <ViewShellBase.hxx> +#include <Window.hxx> +#include <drawdoc.hxx> +#include <strings.hrc> +#include <sdresid.hxx> +#include <svx/svdoutl.hxx> +#include <sfx2/request.hxx> +#include <svl/ctloptions.hxx> +#include <svl/stritem.hxx> +#include <tools/debug.hxx> + +#include <svx/svxdlg.hxx> +#include <svx/svxids.hrc> + +namespace sd { + +const sal_Unicode CHAR_HARDBLANK = u'\x00A0'; +const sal_Unicode CHAR_HARDHYPHEN = u'\x2011'; +const sal_Unicode CHAR_SOFTHYPHEN = u'\x00AD'; +const sal_Unicode CHAR_RLM = u'\x200F'; +const sal_Unicode CHAR_LRM = u'\x200E'; +const sal_Unicode CHAR_ZWSP = u'\x200B'; +const sal_Unicode CHAR_WJ = u'\x2060'; +const sal_Unicode CHAR_NNBSP = u'\x202F'; //NARROW NO-BREAK SPACE + + +FuBullet::FuBullet ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* _pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, _pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuBullet::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuBullet( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuBullet::DoExecute( SfxRequest& rReq ) +{ + if( rReq.GetSlot() == SID_CHARMAP ) + InsertSpecialCharacter(rReq); + else + { + sal_Unicode cMark = 0; + switch( rReq.GetSlot() ) + { + case FN_INSERT_SOFT_HYPHEN: cMark = CHAR_SOFTHYPHEN ; break; + case FN_INSERT_HARDHYPHEN: cMark = CHAR_HARDHYPHEN ; break; + case FN_INSERT_HARD_SPACE: cMark = CHAR_HARDBLANK ; break; + case FN_INSERT_NNBSP: cMark = CHAR_NNBSP ; break; + case SID_INSERT_RLM : cMark = CHAR_RLM ; break; + case SID_INSERT_LRM : cMark = CHAR_LRM ; break; + case SID_INSERT_ZWSP : cMark = CHAR_ZWSP ; break; + case SID_INSERT_WJ: cMark = CHAR_WJ; break; + } + + DBG_ASSERT( cMark != 0, "FuBullet::FuBullet(), illegal slot used!" ); + + if( cMark ) + InsertFormattingMark( cMark ); + } + +} + +void FuBullet::InsertFormattingMark( sal_Unicode cMark ) +{ + OutlinerView* pOV = nullptr; + ::Outliner* pOL = nullptr; + + // depending on ViewShell set Outliner and OutlinerView + if( dynamic_cast< const DrawViewShell *>( mpViewShell ) != nullptr) + { + pOV = mpView->GetTextEditOutlinerView(); + if (pOV) + pOL = mpView->GetTextEditOutliner(); + } + else if( dynamic_cast< const OutlineViewShell *>( mpViewShell ) != nullptr) + { + pOL = &static_cast<OutlineView*>(mpView)->GetOutliner(); + pOV = static_cast<OutlineView*>(mpView)->GetViewByWindow( + mpViewShell->GetActiveWindow()); + } + + // insert string + if(!(pOV && pOL)) + return; + + // prevent flickering + pOV->HideCursor(); + pOL->SetUpdateLayout(false); + + // remove old selected text + pOV->InsertText( "" ); + + // prepare undo + SfxUndoManager& rUndoMgr = pOL->GetUndoManager(); + rUndoMgr.EnterListAction(SdResId(STR_UNDO_INSERT_SPECCHAR), + "", 0, mpViewShell->GetViewShellBase().GetViewShellId() ); + + // insert given text + OUString aStr( cMark ); + pOV->InsertText( aStr, true); + + ESelection aSel = pOV->GetSelection(); + aSel.nStartPara = aSel.nEndPara; + aSel.nStartPos = aSel.nEndPos; + pOV->SetSelection(aSel); + + rUndoMgr.LeaveListAction(); + + // restart repainting + pOL->SetUpdateLayout(true); + pOV->ShowCursor(); +} + +void FuBullet::InsertSpecialCharacter( SfxRequest const & rReq ) +{ + const SfxItemSet *pArgs = rReq.GetArgs(); + const SfxStringItem* pItem = nullptr; + if( pArgs ) + pItem = pArgs->GetItemIfSet(SID_CHARMAP, false); + + OUString aChars; + vcl::Font aFont; + if ( pItem ) + { + aChars = pItem->GetValue(); + const SfxStringItem* pFontItem = pArgs->GetItemIfSet( SID_ATTR_SPECIALCHAR, false ); + if ( pFontItem ) + { + const OUString& aFontName = pFontItem->GetValue(); + aFont = vcl::Font( aFontName, Size(1,1) ); + } + else + { + SfxItemSet aFontAttr( mpDoc->GetPool() ); + mpView->GetAttributes( aFontAttr ); + const SvxFontItem* pFItem = aFontAttr.GetItem( SID_ATTR_CHAR_FONT ); + if( pFItem ) + aFont = vcl::Font( pFItem->GetFamilyName(), pFItem->GetStyleName(), Size( 1, 1 ) ); + } + } + + if (aChars.isEmpty()) + { + SfxAllItemSet aSet( mpDoc->GetPool() ); + aSet.Put( SfxBoolItem( FN_PARAM_1, false ) ); + + SfxItemSet aFontAttr( mpDoc->GetPool() ); + mpView->GetAttributes( aFontAttr ); + const SvxFontItem* pFontItem = aFontAttr.GetItem( SID_ATTR_CHAR_FONT ); + if( pFontItem ) + aSet.Put( *pFontItem ); + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + auto xFrame = mpViewShell ? mpViewShell->GetFrame()->GetFrame().GetFrameInterface() : nullptr; + ScopedVclPtr<SfxAbstractDialog> pDlg( pFact->CreateCharMapDialog(mpView->GetViewShell()->GetFrameWeld(), aSet, + xFrame) ); + + // If a character is selected, it can be shown + // pDLg->SetFont( ); + // pDlg->SetChar( ); + pDlg->Execute(); + return; + } + + if (aChars.isEmpty()) + return; + + OutlinerView* pOV = nullptr; + ::Outliner* pOL = nullptr; + + // determine depending on ViewShell Outliner and OutlinerView + if(dynamic_cast< const DrawViewShell *>( mpViewShell )) + { + pOV = mpView->GetTextEditOutlinerView(); + if (pOV) + { + pOL = mpView->GetTextEditOutliner(); + } + } + else if(dynamic_cast< const OutlineViewShell *>( mpViewShell )) + { + pOL = &static_cast<OutlineView*>(mpView)->GetOutliner(); + pOV = static_cast<OutlineView*>(mpView)->GetViewByWindow( + mpViewShell->GetActiveWindow()); + } + + // insert special character + if (!pOV) + return; + + // prevent flicker + pOV->HideCursor(); + pOL->SetUpdateLayout(false); + + /* remember old attributes: + To do that, remove selected area before (it has to go anyway). + With that, we get unique attributes (and since there is no + DeleteSelected() in OutlinerView, it is deleted by inserting an + empty string). */ + pOV->InsertText( "" ); + + SfxItemSetFixed<EE_CHAR_FONTINFO, EE_CHAR_FONTINFO> aOldSet( mpDoc->GetPool() ); + aOldSet.Put( pOV->GetAttribs() ); + + SfxUndoManager& rUndoMgr = pOL->GetUndoManager(); + ViewShellId nViewShellId = mpViewShell ? mpViewShell->GetViewShellBase().GetViewShellId() : ViewShellId(-1); + rUndoMgr.EnterListAction(SdResId(STR_UNDO_INSERT_SPECCHAR), + "", 0, nViewShellId ); + pOV->InsertText(aChars, true); + + // set attributes (set font) + SfxItemSet aSet(pOL->GetEmptyItemSet()); + SvxFontItem aFontItem (aFont.GetFamilyType(), aFont.GetFamilyName(), + aFont.GetStyleName(), aFont.GetPitch(), + aFont.GetCharSet(), + EE_CHAR_FONTINFO); + aSet.Put(aFontItem); + aFontItem.SetWhich(EE_CHAR_FONTINFO_CJK); + aSet.Put(aFontItem); + aFontItem.SetWhich(EE_CHAR_FONTINFO_CTL); + aSet.Put(aFontItem); + pOV->SetAttribs(aSet); + + ESelection aSel = pOV->GetSelection(); + aSel.nStartPara = aSel.nEndPara; + aSel.nStartPos = aSel.nEndPos; + pOV->SetSelection(aSel); + + // do not go ahead with setting attributes of special characters + pOV->GetOutliner()->QuickSetAttribs(aOldSet, aSel); + + rUndoMgr.LeaveListAction(); + + // show it again + pOL->SetUpdateLayout(true); + pOV->ShowCursor(); +} + +void FuBullet::GetSlotState( SfxItemSet& rSet, ViewShell const * pViewShell, SfxViewFrame* pViewFrame ) +{ + if( !(SfxItemState::DEFAULT == rSet.GetItemState( SID_CHARMAP ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_CHARMAP_CONTROL ) || + SfxItemState::DEFAULT == rSet.GetItemState( FN_INSERT_SOFT_HYPHEN ) || + SfxItemState::DEFAULT == rSet.GetItemState( FN_INSERT_HARDHYPHEN ) || + SfxItemState::DEFAULT == rSet.GetItemState( FN_INSERT_HARD_SPACE ) || + SfxItemState::DEFAULT == rSet.GetItemState( FN_INSERT_NNBSP ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_INSERT_RLM ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_INSERT_LRM ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_INSERT_WJ ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_INSERT_ZWSP ))) + return; + + ::sd::View* pView = pViewShell ? pViewShell->GetView() : nullptr; + OutlinerView* pOLV = pView ? pView->GetTextEditOutlinerView() : nullptr; + + const bool bTextEdit = pOLV; + + SvtCTLOptions aCTLOptions; + const bool bCtlEnabled = aCTLOptions.IsCTLFontEnabled(); + + if(!bTextEdit ) + { + rSet.DisableItem(FN_INSERT_SOFT_HYPHEN); + rSet.DisableItem(FN_INSERT_HARDHYPHEN); + rSet.DisableItem(FN_INSERT_HARD_SPACE); + rSet.DisableItem(FN_INSERT_NNBSP); + rSet.DisableItem(SID_INSERT_WJ); + rSet.DisableItem(SID_INSERT_ZWSP); + } + + if( !bTextEdit && (dynamic_cast<OutlineViewShell const *>( pViewShell ) == nullptr) ) + { + rSet.DisableItem(SID_CHARMAP); + rSet.DisableItem(SID_CHARMAP_CONTROL); + } + + if(!bTextEdit || !bCtlEnabled ) + { + rSet.DisableItem(SID_INSERT_RLM); + rSet.DisableItem(SID_INSERT_LRM); + } + + if( pViewFrame ) + { + SfxBindings& rBindings = pViewFrame->GetBindings(); + + rBindings.SetVisibleState( SID_INSERT_RLM, bCtlEnabled ); + rBindings.SetVisibleState( SID_INSERT_LRM, bCtlEnabled ); + } +} +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuchar.cxx b/sd/source/ui/func/fuchar.cxx new file mode 100644 index 000000000..3935f64a1 --- /dev/null +++ b/sd/source/ui/func/fuchar.cxx @@ -0,0 +1,139 @@ +/* -*- 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 <fuchar.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/sfxdlg.hxx> + +#include <svx/svxids.hrc> +#include <editeng/eeitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/brushitem.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/request.hxx> +#include <View.hxx> +#include <drawdoc.hxx> +#include <ViewShell.hxx> +#include <DrawDocShell.hxx> +#include <sdabstdlg.hxx> + +namespace sd { + + +FuChar::FuChar ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuChar::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuChar( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuChar::DoExecute( SfxRequest& rReq ) +{ + const SfxItemSet* pArgs = rReq.GetArgs(); + + if( !pArgs ) + { + SfxItemSet aEditAttr( mpDoc->GetPool() ); + mpView->GetAttributes( aEditAttr ); + + SfxItemSetFixed<XATTR_FILLSTYLE, XATTR_FILLCOLOR, EE_ITEMS_START, EE_ITEMS_END> aNewAttr(mpViewShell->GetPool()); + aNewAttr.Put( aEditAttr, false ); + + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractTabDialog> pDlg( pFact->CreateSdTabCharDialog(mpViewShell->GetFrameWeld(), &aNewAttr, mpDoc->GetDocSh() ) ); + if (rReq.GetSlot() == SID_CHAR_DLG_EFFECT) + { + pDlg->SetCurPageId("RID_SVXPAGE_CHAR_EFFECTS"); + } + + sal_uInt16 nResult = pDlg->Execute(); + + if( nResult != RET_OK ) + return; + + const SfxItemSet* pOutputSet = pDlg->GetOutputItemSet(); + SfxItemSet aOtherSet( *pOutputSet ); + + // and now the reverse process + const SvxBrushItem* pBrushItem = aOtherSet.GetItem<SvxBrushItem>( SID_ATTR_BRUSH_CHAR ); + + if ( pBrushItem ) + { + SvxColorItem aBackColorItem( pBrushItem->GetColor(), EE_CHAR_BKGCOLOR ); + aOtherSet.ClearItem( SID_ATTR_BRUSH_CHAR ); + aOtherSet.Put( aBackColorItem ); + } + + rReq.Done( aOtherSet ); + pArgs = rReq.GetArgs(); + } + mpView->SetAttributes(*pArgs); + + // invalidate the Slots which are in DrTxtObjBar + static const sal_uInt16 SidArray[] = { + SID_ATTR_CHAR_FONT, + SID_ATTR_CHAR_POSTURE, + SID_ATTR_CHAR_WEIGHT, + SID_ATTR_CHAR_SHADOWED, + SID_ATTR_CHAR_STRIKEOUT, + SID_ATTR_CHAR_UNDERLINE, + SID_ATTR_CHAR_FONTHEIGHT, + SID_ATTR_CHAR_COLOR, + SID_ATTR_CHAR_KERNING, + SID_ATTR_CHAR_CASEMAP, + SID_SET_SUPER_SCRIPT, + SID_SET_SUB_SCRIPT, + SID_ATTR_CHAR_BACK_COLOR, + 0 }; + + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SidArray ); + + if( mpDoc->GetOnlineSpell() ) + { + if( SfxItemState::SET == pArgs->GetItemState(EE_CHAR_LANGUAGE, false ) || + SfxItemState::SET == pArgs->GetItemState(EE_CHAR_LANGUAGE_CJK, false ) || + SfxItemState::SET == pArgs->GetItemState(EE_CHAR_LANGUAGE_CTL, false ) ) + { + mpDoc->StopOnlineSpelling(); + mpDoc->StartOnlineSpelling(); + } + } +} + +void FuChar::Activate() +{ +} + +void FuChar::Deactivate() +{ +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fucon3d.cxx b/sd/source/ui/func/fucon3d.cxx new file mode 100644 index 000000000..fb844548f --- /dev/null +++ b/sd/source/ui/func/fucon3d.cxx @@ -0,0 +1,474 @@ +/* -*- 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 <fucon3d.hxx> + +#include <svx/svxids.hrc> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <tools/poly.hxx> + +#include <svx/xlineit0.hxx> +#include <svx/scene3d.hxx> +#include <svx/sphere3d.hxx> +#include <svx/cube3d.hxx> +#include <svx/lathe3d.hxx> +#include <svx/camera3d.hxx> + +#include <vcl/weld.hxx> + +#include <app.hrc> + +#include <View.hxx> +#include <Window.hxx> +#include <ViewShell.hxx> +#include <drawdoc.hxx> +#include <ViewShellBase.hxx> +#include <ToolBarManager.hxx> +#include <svx/svx3ditems.hxx> + +#include <basegfx/polygon/b2dpolygontools.hxx> + +using namespace com::sun::star; + +namespace sd { + + +FuConstruct3dObject::FuConstruct3dObject ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuConstruct(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuConstruct3dObject::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, bool bPermanent ) +{ + FuConstruct3dObject* pFunc; + rtl::Reference<FuPoor> xFunc( pFunc = new FuConstruct3dObject( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + pFunc->SetPermanent(bPermanent); + return xFunc; +} + +void FuConstruct3dObject::DoExecute( SfxRequest& rReq ) +{ + FuConstruct::DoExecute( rReq ); + mpViewShell->GetViewShellBase().GetToolBarManager()->SetToolBar( + ToolBarManager::ToolBarGroup::Function, + ToolBarManager::msDrawingObjectToolBar); +} + +E3dCompoundObject* FuConstruct3dObject::ImpCreateBasic3DShape() +{ + E3dCompoundObject* p3DObj = nullptr; + + switch (nSlotId) + { + default: + case SID_3D_CUBE: + { + p3DObj = new E3dCubeObj( + mpView->getSdrModelFromSdrView(), + mpView->Get3DDefaultAttributes(), + ::basegfx::B3DPoint(-2500, -2500, -2500), + ::basegfx::B3DVector(5000, 5000, 5000)); + break; + } + + case SID_3D_SPHERE: + { + p3DObj = new E3dSphereObj( + mpView->getSdrModelFromSdrView(), + mpView->Get3DDefaultAttributes(), + ::basegfx::B3DPoint(0, 0, 0), + ::basegfx::B3DVector(5000, 5000, 5000)); + break; + } + + case SID_3D_SHELL: + { + XPolygon aXPoly(Point (0, 1250), 2500, 2500, 0_deg100, 9000_deg100, false); + aXPoly.Scale(5.0, 5.0); + + ::basegfx::B2DPolygon aB2DPolygon(aXPoly.getB2DPolygon()); + if(aB2DPolygon.areControlPointsUsed()) + { + aB2DPolygon = ::basegfx::utils::adaptiveSubdivideByAngle(aB2DPolygon); + } + p3DObj = new E3dLatheObj( + mpView->getSdrModelFromSdrView(), + mpView->Get3DDefaultAttributes(), + ::basegfx::B2DPolyPolygon(aB2DPolygon)); + + /* this is an open object, therefore it has to be handled double- + sided by default */ + p3DObj->SetMergedItem(makeSvx3DDoubleSidedItem(true)); + break; + } + + case SID_3D_HALF_SPHERE: + { + XPolygon aXPoly(Point (0, 1250), 2500, 2500, 0_deg100, 9000_deg100, false); + aXPoly.Scale(5.0, 5.0); + + aXPoly.Insert(0, Point (2400*5, 1250*5), PolyFlags::Normal); + aXPoly.Insert(0, Point (2000*5, 1250*5), PolyFlags::Normal); + aXPoly.Insert(0, Point (1500*5, 1250*5), PolyFlags::Normal); + aXPoly.Insert(0, Point (1000*5, 1250*5), PolyFlags::Normal); + aXPoly.Insert(0, Point (500*5, 1250*5), PolyFlags::Normal); + aXPoly.Insert(0, Point (250*5, 1250*5), PolyFlags::Normal); + aXPoly.Insert(0, Point (50*5, 1250*5), PolyFlags::Normal); + aXPoly.Insert(0, Point (0, 1250*5), PolyFlags::Normal); + + ::basegfx::B2DPolygon aB2DPolygon(aXPoly.getB2DPolygon()); + if(aB2DPolygon.areControlPointsUsed()) + { + aB2DPolygon = ::basegfx::utils::adaptiveSubdivideByAngle(aB2DPolygon); + } + p3DObj = new E3dLatheObj( + mpView->getSdrModelFromSdrView(), + mpView->Get3DDefaultAttributes(), + ::basegfx::B2DPolyPolygon(aB2DPolygon)); + break; + } + + case SID_3D_TORUS: + { + ::basegfx::B2DPolygon aB2DPolygon(::basegfx::utils::createPolygonFromCircle(::basegfx::B2DPoint(1000.0, 0.0), 500.0)); + if(aB2DPolygon.areControlPointsUsed()) + { + aB2DPolygon = ::basegfx::utils::adaptiveSubdivideByAngle(aB2DPolygon); + } + p3DObj = new E3dLatheObj( + mpView->getSdrModelFromSdrView(), + mpView->Get3DDefaultAttributes(), + ::basegfx::B2DPolyPolygon(aB2DPolygon)); + break; + } + + case SID_3D_CYLINDER: + { + ::basegfx::B2DPolygon aInnerPoly; + + aInnerPoly.append(::basegfx::B2DPoint(0, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(50*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(100*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(200*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(300*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(400*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(450*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(500*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(500*5, -1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(450*5, -1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(400*5, -1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(300*5, -1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(200*5, -1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(100*5, -1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(50*5, -1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(0, -1000*5)); + aInnerPoly.setClosed(true); + + p3DObj = new E3dLatheObj( + mpView->getSdrModelFromSdrView(), + mpView->Get3DDefaultAttributes(), + ::basegfx::B2DPolyPolygon(aInnerPoly)); + break; + } + + case SID_3D_CONE: + { + ::basegfx::B2DPolygon aInnerPoly; + + aInnerPoly.append(::basegfx::B2DPoint(0, -1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(25*5, -900*5)); + aInnerPoly.append(::basegfx::B2DPoint(50*5, -800*5)); + aInnerPoly.append(::basegfx::B2DPoint(100*5, -600*5)); + aInnerPoly.append(::basegfx::B2DPoint(200*5, -200*5)); + aInnerPoly.append(::basegfx::B2DPoint(300*5, 200*5)); + aInnerPoly.append(::basegfx::B2DPoint(400*5, 600*5)); + aInnerPoly.append(::basegfx::B2DPoint(500*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(400*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(300*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(200*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(100*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(50*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(0, 1000*5)); + aInnerPoly.setClosed(true); + + p3DObj = new E3dLatheObj( + mpView->getSdrModelFromSdrView(), + mpView->Get3DDefaultAttributes(), + ::basegfx::B2DPolyPolygon(aInnerPoly)); + break; + } + + case SID_3D_PYRAMID: + { + ::basegfx::B2DPolygon aInnerPoly; + + aInnerPoly.append(::basegfx::B2DPoint(0, -1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(25*5, -900*5)); + aInnerPoly.append(::basegfx::B2DPoint(50*5, -800*5)); + aInnerPoly.append(::basegfx::B2DPoint(100*5, -600*5)); + aInnerPoly.append(::basegfx::B2DPoint(200*5, -200*5)); + aInnerPoly.append(::basegfx::B2DPoint(300*5, 200*5)); + aInnerPoly.append(::basegfx::B2DPoint(400*5, 600*5)); + aInnerPoly.append(::basegfx::B2DPoint(500*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(400*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(300*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(200*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(100*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(50*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(0, 1000*5)); + aInnerPoly.setClosed(true); + + p3DObj = new E3dLatheObj( + mpView->getSdrModelFromSdrView(), + mpView->Get3DDefaultAttributes(), + ::basegfx::B2DPolyPolygon(aInnerPoly)); + p3DObj->SetMergedItem(makeSvx3DHorizontalSegmentsItem(4)); + break; + } + } + + return p3DObj; +} + +void FuConstruct3dObject::ImpPrepareBasic3DShape(E3dCompoundObject const * p3DObj, E3dScene *pScene) +{ + Camera3D aCamera = pScene->GetCamera (); + + // get transformed BoundVolume of the new object + basegfx::B3DRange aBoundVol; + basegfx::B3DRange aObjVol(p3DObj->GetBoundVolume()); + aObjVol.transform(p3DObj->GetTransform()); + aBoundVol.expand(aObjVol); + double fDeepth(aBoundVol.getDepth()); + + aCamera.SetPRP(::basegfx::B3DPoint(0.0, 0.0, 1000.0)); + aCamera.SetPosition(::basegfx::B3DPoint(0.0, 0.0, mpView->GetDefaultCamPosZ() + fDeepth / 2)); + aCamera.SetFocalLength(mpView->GetDefaultCamFocal()); + pScene->SetCamera(aCamera); + basegfx::B3DHomMatrix aTransformation; + + switch (nSlotId) + { + case SID_3D_CUBE: + { + aTransformation.rotate(basegfx::deg2rad(20), 0.0, 0.0); + } + break; + + case SID_3D_SPHERE: + { + } + break; + + case SID_3D_SHELL: + case SID_3D_HALF_SPHERE: + { + aTransformation.rotate(basegfx::deg2rad(200), 0.0, 0.0); + } + break; + + case SID_3D_CYLINDER: + case SID_3D_CONE: + case SID_3D_PYRAMID: + { + } + break; + + case SID_3D_TORUS: + { + aTransformation.rotate(basegfx::deg2rad(90), 0.0, 0.0); + } + break; + + default: + { + } + break; + } + + pScene->SetTransform(aTransformation * pScene->GetTransform()); + + SfxItemSet aAttr (mpViewShell->GetPool()); + pScene->SetMergedItemSetAndBroadcast(aAttr); +} + +bool FuConstruct3dObject::MouseButtonDown(const MouseEvent& rMEvt) +{ + bool bReturn = FuConstruct::MouseButtonDown(rMEvt); + + if ( rMEvt.IsLeft() && !mpView->IsAction() ) + { + Point aPnt( mpWindow->PixelToLogic( rMEvt.GetPosPixel() ) ); + + mpWindow->CaptureMouse(); + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + + weld::WaitObject aWait(mpViewShell->GetFrameWeld()); + + E3dCompoundObject* p3DObj = ImpCreateBasic3DShape(); + E3dScene* pScene = mpView->SetCurrent3DObj(p3DObj); + + ImpPrepareBasic3DShape(p3DObj, pScene); + bReturn = mpView->BegCreatePreparedObject(aPnt, nDrgLog, pScene); + + SdrObject* pObj = mpView->GetCreateObj(); + + if (pObj) + { + SfxItemSet aAttr(mpDoc->GetPool()); + SetStyleSheet(aAttr, pObj); + + // extract LineStyle + aAttr.Put(XLineStyleItem (drawing::LineStyle_NONE)); + + pObj->SetMergedItemSet(aAttr); + } + } + + return bReturn; +} + +bool FuConstruct3dObject::MouseButtonUp(const MouseEvent& rMEvt) +{ + bool bReturn = false; + + if ( mpView->IsCreateObj() && rMEvt.IsLeft() ) + { + if( mpView->EndCreateObj( SdrCreateCmd::ForceEnd ) ) + { + bReturn = true; + } + else + { + //Drag was too small to create object, so insert default object at click pos + Point aClickPos(mpWindow->PixelToLogic(rMEvt.GetPosPixel())); + sal_uInt32 nDefaultObjectSize(1000); + sal_Int32 nCenterOffset(-sal_Int32(nDefaultObjectSize / 2)); + aClickPos.AdjustX(nCenterOffset); + aClickPos.AdjustY(nCenterOffset); + + SdrPageView *pPV = mpView->GetSdrPageView(); + + if(mpView->IsSnapEnabled()) + aClickPos = mpView->GetSnapPos(aClickPos, pPV); + + ::tools::Rectangle aNewObjectRectangle(aClickPos, Size(nDefaultObjectSize, nDefaultObjectSize)); + SdrObjectUniquePtr pObjDefault = CreateDefaultObject(nSlotId, aNewObjectRectangle); + + bReturn = mpView->InsertObjectAtView(pObjDefault.release(), *pPV); + } + } + bReturn = FuConstruct::MouseButtonUp(rMEvt) || bReturn; + + if (!bPermanent) + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + + return bReturn; +} + +void FuConstruct3dObject::Activate() +{ + mpView->SetCurrentObj(SdrObjKind::NONE); + + FuConstruct::Activate(); +} + +SdrObjectUniquePtr FuConstruct3dObject::CreateDefaultObject(const sal_uInt16 nID, const ::tools::Rectangle& rRectangle) +{ + + E3dCompoundObject* p3DObj = ImpCreateBasic3DShape(); + + // E3dView::SetCurrent3DObj part + // get transformed BoundVolume of the object + basegfx::B3DRange aObjVol(p3DObj->GetBoundVolume()); + aObjVol.transform(p3DObj->GetTransform()); + basegfx::B3DRange aVolume(aObjVol); + double fW(aVolume.getWidth()); + double fH(aVolume.getHeight()); + ::tools::Rectangle a3DRect(0, 0, static_cast<::tools::Long>(fW), static_cast<::tools::Long>(fH)); + std::unique_ptr< E3dScene, SdrObjectFreeOp > pScene(new E3dScene(*mpDoc)); + + // copied code from E3dView::InitScene + double fCamZ(aVolume.getMaxZ() + ((fW + fH) / 4.0)); + Camera3D aCam(pScene->GetCamera()); + aCam.SetAutoAdjustProjection(false); + aCam.SetViewWindow(- fW / 2, - fH / 2, fW, fH); + ::basegfx::B3DPoint aLookAt; + double fDefaultCamPosZ = mpView->GetDefaultCamPosZ(); + ::basegfx::B3DPoint aCamPos(0.0, 0.0, fCamZ < fDefaultCamPosZ ? fDefaultCamPosZ : fCamZ); + aCam.SetPosAndLookAt(aCamPos, aLookAt); + aCam.SetFocalLength(mpView->GetDefaultCamFocal()); + pScene->SetCamera(aCam); + pScene->InsertObject(p3DObj); + pScene->NbcSetSnapRect(a3DRect); + ImpPrepareBasic3DShape(p3DObj, pScene.get()); + SfxItemSet aAttr(mpDoc->GetPool()); + SetStyleSheet(aAttr, p3DObj); + aAttr.Put(XLineStyleItem (drawing::LineStyle_NONE)); + p3DObj->SetMergedItemSet(aAttr); + + // make object interactive at once + pScene->SetBoundAndSnapRectsDirty(); + + // Take care of restrictions for the rectangle + ::tools::Rectangle aRect(rRectangle); + + switch(nID) + { + case SID_3D_CUBE: + case SID_3D_SPHERE: + case SID_3D_TORUS: + { + // force quadratic + ImpForceQuadratic(aRect); + break; + } + + case SID_3D_SHELL: + case SID_3D_HALF_SPHERE: + { + // force horizontal layout + break; + } + + case SID_3D_CYLINDER: + case SID_3D_CONE: + case SID_3D_PYRAMID: + { + // force vertical layout + break; + } + } + + // use changed rectangle, not original one + pScene->SetLogicRect(aRect); + + return SdrObjectUniquePtr(pScene.release()); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuconarc.cxx b/sd/source/ui/func/fuconarc.cxx new file mode 100644 index 000000000..b5be93f9b --- /dev/null +++ b/sd/source/ui/func/fuconarc.cxx @@ -0,0 +1,254 @@ +/* -*- 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 <fuconarc.hxx> +#include <svx/svdpagv.hxx> +#include <svx/svdocirc.hxx> +#include <sfx2/request.hxx> +#include <svl/intitem.hxx> +#include <sfx2/dispatch.hxx> +#include <svx/svdobj.hxx> +#include <sfx2/viewfrm.hxx> +#include <osl/diagnose.h> + +#include <svx/svxids.hrc> + +#include <Window.hxx> +#include <drawdoc.hxx> + +#include <View.hxx> +#include <ViewShell.hxx> +#include <ViewShellBase.hxx> +#include <ToolBarManager.hxx> + +#include <svx/sxciaitm.hxx> +#include <svx/xfillit0.hxx> + +using namespace com::sun::star; + +namespace sd { + + +FuConstructArc::FuConstructArc ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq ) + : FuConstruct( pViewSh, pWin, pView, pDoc, rReq ) +{ +} + +rtl::Reference<FuPoor> FuConstructArc::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, bool bPermanent ) +{ + FuConstructArc* pFunc; + rtl::Reference<FuPoor> xFunc( pFunc = new FuConstructArc( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + pFunc->SetPermanent(bPermanent); + return xFunc; +} + +void FuConstructArc::DoExecute( SfxRequest& rReq ) +{ + FuConstruct::DoExecute( rReq ); + + mpViewShell->GetViewShellBase().GetToolBarManager()->SetToolBar( + ToolBarManager::ToolBarGroup::Function, + ToolBarManager::msDrawingObjectToolBar); + + const SfxItemSet *pArgs = rReq.GetArgs (); + + if (!pArgs) + return; + + const SfxUInt32Item* pCenterX = rReq.GetArg<SfxUInt32Item>(ID_VAL_CENTER_X); + const SfxUInt32Item* pCenterY = rReq.GetArg<SfxUInt32Item>(ID_VAL_CENTER_Y); + const SfxUInt32Item* pAxisX = rReq.GetArg<SfxUInt32Item>(ID_VAL_AXIS_X); + const SfxUInt32Item* pAxisY = rReq.GetArg<SfxUInt32Item>(ID_VAL_AXIS_Y); + const SfxUInt32Item* pPhiStart = rReq.GetArg<SfxUInt32Item>(ID_VAL_ANGLESTART); + const SfxUInt32Item* pPhiEnd = rReq.GetArg<SfxUInt32Item>(ID_VAL_ANGLEEND); + + ::tools::Rectangle aNewRectangle (pCenterX->GetValue () - pAxisX->GetValue () / 2, + pCenterY->GetValue () - pAxisY->GetValue () / 2, + pCenterX->GetValue () + pAxisX->GetValue () / 2, + pCenterY->GetValue () + pAxisY->GetValue () / 2); + + Activate(); // sets aObjKind + SdrCircObj* pNewCircle = + new SdrCircObj( + mpView->getSdrModelFromSdrView(), + ToSdrCircKind(mpView->GetCurrentObjIdentifier()), + aNewRectangle, + Degree100(pPhiStart->GetValue() * 10), + Degree100(pPhiEnd->GetValue() * 10)); + SdrPageView *pPV = mpView->GetSdrPageView(); + + mpView->InsertObjectAtView(pNewCircle, *pPV, SdrInsertFlags::SETDEFLAYER); +} + +bool FuConstructArc::MouseButtonDown( const MouseEvent& rMEvt ) +{ + bool bReturn = FuConstruct::MouseButtonDown( rMEvt ); + + if ( rMEvt.IsLeft() && !mpView->IsAction() ) + { + Point aPnt( mpWindow->PixelToLogic( rMEvt.GetPosPixel() ) ); + mpWindow->CaptureMouse(); + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + mpView->BegCreateObj(aPnt, nullptr, nDrgLog); + + SdrObject* pObj = mpView->GetCreateObj(); + + if (pObj) + { + SfxItemSet aAttr(mpDoc->GetPool()); + SetStyleSheet(aAttr, pObj); + + pObj->SetMergedItemSet(aAttr); + } + + bReturn = true; + } + return bReturn; +} + +bool FuConstructArc::MouseButtonUp( const MouseEvent& rMEvt ) +{ + bool bReturn = false; + bool bCreated = false; + + if ( mpView->IsCreateObj() && rMEvt.IsLeft() ) + { + const size_t nCount = mpView->GetSdrPageView()->GetObjList()->GetObjCount(); + + if (mpView->EndCreateObj(SdrCreateCmd::NextPoint) ) + { + if (nCount != mpView->GetSdrPageView()->GetObjList()->GetObjCount()) + { + bCreated = true; + } + } + + bReturn = true; + } + + bReturn = FuConstruct::MouseButtonUp (rMEvt) || bReturn; + + if (!bPermanent && bCreated) + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + + return bReturn; +} + +void FuConstructArc::Activate() +{ + SdrObjKind aObjKind; + + switch( nSlotId ) + { + case SID_DRAW_ARC : + case SID_DRAW_CIRCLEARC: + { + aObjKind = SdrObjKind::CircleArc; + } + break; + + case SID_DRAW_PIE : + case SID_DRAW_PIE_NOFILL : + case SID_DRAW_CIRCLEPIE : + case SID_DRAW_CIRCLEPIE_NOFILL: + { + aObjKind = SdrObjKind::CircleSection; + } + break; + + case SID_DRAW_ELLIPSECUT : + case SID_DRAW_ELLIPSECUT_NOFILL: + case SID_DRAW_CIRCLECUT : + case SID_DRAW_CIRCLECUT_NOFILL : + { + aObjKind = SdrObjKind::CircleCut; + } + break; + + default: + { + aObjKind = SdrObjKind::CircleArc; + } + break; + } + + mpView->SetCurrentObj(aObjKind); + + FuConstruct::Activate(); +} + +SdrObjectUniquePtr FuConstructArc::CreateDefaultObject(const sal_uInt16 nID, const ::tools::Rectangle& rRectangle) +{ + + SdrObjectUniquePtr pObj(SdrObjFactory::MakeNewObject( + mpView->getSdrModelFromSdrView(), + mpView->GetCurrentObjInventor(), + mpView->GetCurrentObjIdentifier())); + + if(pObj) + { + if( dynamic_cast< const SdrCircObj *>( pObj.get() ) != nullptr) + { + ::tools::Rectangle aRect(rRectangle); + + if(SID_DRAW_ARC == nID || + SID_DRAW_CIRCLEARC == nID || + SID_DRAW_CIRCLEPIE == nID || + SID_DRAW_CIRCLEPIE_NOFILL == nID || + SID_DRAW_CIRCLECUT == nID || + SID_DRAW_CIRCLECUT_NOFILL == nID) + { + // force quadratic + ImpForceQuadratic(aRect); + } + + pObj->SetLogicRect(aRect); + + SfxItemSet aAttr(mpDoc->GetPool()); + aAttr.Put(makeSdrCircStartAngleItem(9000_deg100)); + aAttr.Put(makeSdrCircEndAngleItem(0_deg100)); + + if(SID_DRAW_PIE_NOFILL == nID || + SID_DRAW_CIRCLEPIE_NOFILL == nID || + SID_DRAW_ELLIPSECUT_NOFILL == nID || + SID_DRAW_CIRCLECUT_NOFILL == nID) + { + aAttr.Put(XFillStyleItem(drawing::FillStyle_NONE)); + } + + pObj->SetMergedItemSet(aAttr); + } + else + { + OSL_FAIL("Object is NO circle object"); + } + } + + return pObj; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuconbez.cxx b/sd/source/ui/func/fuconbez.cxx new file mode 100644 index 000000000..b123e9c2d --- /dev/null +++ b/sd/source/ui/func/fuconbez.cxx @@ -0,0 +1,556 @@ +/* -*- 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 <com/sun/star/presentation/EffectNodeType.hpp> + +#include <fuconbez.hxx> +#include <svx/svdopath.hxx> +#include <svl/intitem.hxx> +#include <sfx2/dispatch.hxx> +#include <svx/svdobj.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/request.hxx> +#include <sfx2/viewfrm.hxx> +#include <osl/diagnose.h> + +#include <svx/svxids.hrc> +#include <svx/svdpagv.hxx> +#include <svx/xlnclit.hxx> +#include <svx/xlntrit.hxx> +#include <svx/xlnwtit.hxx> + +#include <app.hrc> +#include <ViewShell.hxx> +#include <ViewShellBase.hxx> +#include <View.hxx> +#include <Window.hxx> +#include <ToolBarManager.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> + +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> + +#include <CustomAnimationEffect.hxx> + +using namespace ::com::sun::star::uno; + +namespace sd { + +/*//Extra attributes coming from parameters + sal_uInt16 mnTransparence; // Default: 0 + OUString msColor; // Default: "" + sal_uInt16 mnWidth; // Default: 0 + OUString msShapeName; // Default: ""*/ +FuConstructBezierPolygon::FuConstructBezierPolygon ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuConstruct(pViewSh, pWin, pView, pDoc, rReq), + nEditMode(SID_BEZIER_MOVE), + mnTransparence(0), + mnWidth(0) +{ +} + +namespace{ + +/// Checks to see if the request has a parameter of IsSticky:bool=true +/// It means that the selected command/button will stay selected after use +bool isSticky(const SfxRequest& rReq) +{ + const SfxItemSet *pArgs = rReq.GetArgs (); + if (pArgs) + { + const SfxBoolItem* pIsSticky = rReq.GetArg<SfxBoolItem>(FN_PARAM_4); + if (pIsSticky && pIsSticky->GetValue()) + return true; + } + + return false; +} + +} + +rtl::Reference<FuPoor> FuConstructBezierPolygon::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, bool bPermanent ) +{ + FuConstructBezierPolygon* pFunc; + rtl::Reference<FuPoor> xFunc( pFunc = new FuConstructBezierPolygon( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + pFunc->SetPermanent(bPermanent || isSticky(rReq)); + return xFunc; +} + +void FuConstructBezierPolygon::DoExecute( SfxRequest& rReq ) +{ + FuConstruct::DoExecute( rReq ); + + const SfxItemSet* pArgs = rReq.GetArgs(); + + if( !pArgs ) + return; + + const SfxUnoAnyItem* pPoolItem = pArgs->GetItemIfSet( SID_ADD_MOTION_PATH ); + if( pPoolItem ) + maTargets = pPoolItem->GetValue(); + + if (nSlotId != SID_DRAW_FREELINE_NOFILL) + return; + + const SfxUInt16Item* pTransparence = rReq.GetArg<SfxUInt16Item>(FN_PARAM_1); + const SfxStringItem* pColor = rReq.GetArg<SfxStringItem>(FN_PARAM_2); + const SfxUInt16Item* pWidth = rReq.GetArg<SfxUInt16Item>(FN_PARAM_3); + const SfxStringItem* pShapeName = rReq.GetArg<SfxStringItem>(SID_SHAPE_NAME); + + if (pTransparence && pTransparence->GetValue() > 0) + { + mnTransparence = pTransparence->GetValue(); + } + if (pColor && !pColor->GetValue().isEmpty()) + { + msColor = pColor->GetValue(); + } + if (pWidth && pWidth->GetValue() > 0) + { + mnWidth = pWidth->GetValue(); + } + if (pShapeName && !pShapeName->GetValue().isEmpty()) + { + msShapeName = pShapeName->GetValue(); + } +} + +bool FuConstructBezierPolygon::MouseButtonDown(const MouseEvent& rMEvt) +{ + bool bReturn = FuConstruct::MouseButtonDown(rMEvt); + + SdrViewEvent aVEvt; + SdrHitKind eHit = mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + + if (eHit == SdrHitKind::Handle || rMEvt.IsMod1()) + { + mpView->SetEditMode(SdrViewEditMode::Edit); + } + else + { + mpView->SetEditMode(SdrViewEditMode::Create); + } + + if (aVEvt.meEvent == SdrEventKind::BeginTextEdit) + { + // here, we do not allow text input + aVEvt.meEvent = SdrEventKind::BeginDragObj; + mpView->EnableExtendedMouseEventDispatcher(false); + } + else + { + mpView->EnableExtendedMouseEventDispatcher(true); + } + + if (eHit == SdrHitKind::MarkedObject && nEditMode == SID_BEZIER_INSERT) + { + // insert gluepoint + mpView->BegInsObjPoint(aMDPos, rMEvt.IsMod1()); + } + else + { + mpView->MouseButtonDown(rMEvt, mpWindow->GetOutDev()); + + SdrObject* pObj = mpView->GetCreateObj(); + + if (pObj) + { + SfxItemSet aAttr(mpDoc->GetPool()); + SetStyleSheet(aAttr, pObj); + SetAttributes(aAttr, pObj); + pObj->SetMergedItemSet(aAttr); + } + } + + return bReturn; +} + +bool FuConstructBezierPolygon::MouseButtonUp(const MouseEvent& rMEvt ) +{ + bool bReturn = false; + bool bCreated = false; + + SdrViewEvent aVEvt; + mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONUP, aVEvt); + + const size_t nCount = mpView->GetSdrPageView()->GetObjList()->GetObjCount(); + + if (mpView->IsInsObjPoint()) + { + mpView->EndInsObjPoint(SdrCreateCmd::ForceEnd); + } + else + { + mpView->MouseButtonUp(rMEvt, mpWindow->GetOutDev()); + } + + if (aVEvt.meEvent == SdrEventKind::EndCreate) + { + bReturn = true; + + if (nCount+1 == mpView->GetSdrPageView()->GetObjList()->GetObjCount()) + { + bCreated = true; + } + + // trick to suppress FuDraw::DoubleClick + bMBDown = false; + + } + + bReturn = FuConstruct::MouseButtonUp(rMEvt) || bReturn; + + bool bDeleted = false; + if( bCreated && maTargets.hasValue() ) + { + SdrPathObj* pPathObj = dynamic_cast< SdrPathObj* >( mpView->GetSdrPageView()->GetObjList()->GetObj( nCount ) ); + SdPage* pPage = dynamic_cast< SdPage* >( pPathObj ? pPathObj->getSdrPageFromSdrObject() : nullptr ); + if( pPage ) + { + std::shared_ptr< sd::MainSequence > pMainSequence( pPage->getMainSequence() ); + if( pMainSequence ) + { + Sequence< Any > aTargets; + maTargets >>= aTargets; + + sal_Int32 nTCount = aTargets.getLength(); + if( nTCount > 1 ) + { + const Any* pTarget = aTargets.getConstArray(); + double fDuration = 0.0; + *pTarget++ >>= fDuration; + bool bFirst = true; + + OUString sPresetId; + switch(nSlotId) + { + case SID_DRAW_BEZIER_NOFILL: + sPresetId = "libo-motionpath-curve"; + break; + case SID_DRAW_POLYGON_NOFILL: + sPresetId = "libo-motionpath-polygon"; + break; + case SID_DRAW_FREELINE_NOFILL: + sPresetId = "libo-motionpath-freeform-line"; + break; + } + + while( --nTCount ) + { + CustomAnimationEffectPtr pCreated( pMainSequence->append( *pPathObj, *pTarget++, fDuration, sPresetId) ); + if( bFirst ) + bFirst = false; + else + pCreated->setNodeType( css::presentation::EffectNodeType::WITH_PREVIOUS ); + } + } + } + } + mpView->DeleteMarked(); + bDeleted = true; + } + + if ((!bPermanent && bCreated) || bDeleted) + { + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + } + + return bReturn; +} + +void FuConstructBezierPolygon::Activate() +{ + mpView->EnableExtendedMouseEventDispatcher(true); + + SdrObjKind eKind; + + switch (nSlotId) + { + case SID_DRAW_POLYGON_NOFILL: + case SID_DRAW_XPOLYGON_NOFILL: + { + eKind = SdrObjKind::PolyLine; + } + break; + + case SID_DRAW_POLYGON: + case SID_DRAW_XPOLYGON: + { + eKind = SdrObjKind::Polygon; + } + break; + + case SID_DRAW_BEZIER_NOFILL: + { + eKind = SdrObjKind::PathLine; + } + break; + + case SID_DRAW_BEZIER_FILL: + { + eKind = SdrObjKind::PathFill; + } + break; + + case SID_DRAW_FREELINE_NOFILL: + { + eKind = SdrObjKind::FreehandLine; + } + break; + + case SID_DRAW_FREELINE: + { + eKind = SdrObjKind::FreehandFill; + } + break; + + default: + { + eKind = SdrObjKind::PathLine; + } + break; + } + + mpView->SetCurrentObj(eKind); + + FuConstruct::Activate(); +} + +void FuConstructBezierPolygon::Deactivate() +{ + mpView->EnableExtendedMouseEventDispatcher(false); + + FuConstruct::Deactivate(); +} + +void FuConstructBezierPolygon::SelectionHasChanged() +{ + FuDraw::SelectionHasChanged(); + + mpViewShell->GetViewShellBase().GetToolBarManager()->SelectionHasChanged( + *mpViewShell, + *mpView); +} + +namespace { +/// Returns the color based on the color names listed in core/include/tools/color.hxx +/// Feel free to extend with more color names from color.hxx +Color strToColor(std::u16string_view sColor) +{ + Color aColor = COL_AUTO; + + if (sColor == u"COL_GRAY") + aColor = COL_GRAY; + else if (sColor == u"COL_GRAY3") + aColor = COL_GRAY3; + else if (sColor == u"COL_GRAY7") + aColor = COL_GRAY7; + + return aColor; +} +} + +void FuConstructBezierPolygon::SetAttributes(SfxItemSet& rAttr, SdrObject *pObj) +{ + if (nSlotId == SID_DRAW_FREELINE_NOFILL) + { + if (mnTransparence > 0 && mnTransparence <= 100) + rAttr.Put(XLineTransparenceItem(mnTransparence)); + if (!msColor.isEmpty()) + rAttr.Put(XLineColorItem(OUString(), strToColor(msColor))); + if (mnWidth > 0) + rAttr.Put(XLineWidthItem(mnWidth)); + if (!msShapeName.isEmpty()) + pObj->SetName(msShapeName); + } +} + +/** + * Set current bezier edit mode + */ +void FuConstructBezierPolygon::SetEditMode(sal_uInt16 nMode) +{ + nEditMode = nMode; + ForcePointer(); + + SfxBindings& rBindings = mpViewShell->GetViewFrame()->GetBindings(); + rBindings.Invalidate(SID_BEZIER_MOVE); + rBindings.Invalidate(SID_BEZIER_INSERT); +} + +SdrObjectUniquePtr FuConstructBezierPolygon::CreateDefaultObject(const sal_uInt16 nID, const ::tools::Rectangle& rRectangle) +{ + // case SID_DRAW_POLYGON: + // case SID_DRAW_POLYGON_NOFILL: + // case SID_DRAW_XPOLYGON: + // case SID_DRAW_XPOLYGON_NOFILL: + // case SID_DRAW_FREELINE: + // case SID_DRAW_FREELINE_NOFILL: + // case SID_DRAW_BEZIER_FILL: // BASIC + // case SID_DRAW_BEZIER_NOFILL: // BASIC + + SdrObjectUniquePtr pObj(SdrObjFactory::MakeNewObject( + mpView->getSdrModelFromSdrView(), + mpView->GetCurrentObjInventor(), + mpView->GetCurrentObjIdentifier())); + + if(pObj) + { + if( auto pPathObj = dynamic_cast< SdrPathObj *>( pObj.get() ) ) + { + basegfx::B2DPolyPolygon aPoly; + + switch(nID) + { + case SID_DRAW_BEZIER_FILL: + { + const sal_Int32 nWdt(rRectangle.GetWidth() / 2); + const sal_Int32 nHgt(rRectangle.GetHeight() / 2); + const basegfx::B2DPolygon aInnerPoly(basegfx::utils::createPolygonFromEllipse(basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Center().Y()), nWdt, nHgt)); + + aPoly.append(aInnerPoly); + break; + } + case SID_DRAW_BEZIER_NOFILL: + { + basegfx::B2DPolygon aInnerPoly; + + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left(), rRectangle.Bottom())); + + const basegfx::B2DPoint aCenterBottom(rRectangle.Center().X(), rRectangle.Bottom()); + aInnerPoly.appendBezierSegment( + aCenterBottom, + aCenterBottom, + basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Center().Y())); + + const basegfx::B2DPoint aCenterTop(rRectangle.Center().X(), rRectangle.Top()); + aInnerPoly.appendBezierSegment( + aCenterTop, + aCenterTop, + basegfx::B2DPoint(rRectangle.Right(), rRectangle.Top())); + + aPoly.append(aInnerPoly); + break; + } + case SID_DRAW_FREELINE: + case SID_DRAW_FREELINE_NOFILL: + { + basegfx::B2DPolygon aInnerPoly; + + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left(), rRectangle.Bottom())); + + aInnerPoly.appendBezierSegment( + basegfx::B2DPoint(rRectangle.Left(), rRectangle.Top()), + basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Top()), + basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Center().Y())); + + aInnerPoly.appendBezierSegment( + basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Bottom()), + basegfx::B2DPoint(rRectangle.Right(), rRectangle.Bottom()), + basegfx::B2DPoint(rRectangle.Right(), rRectangle.Top())); + + if(SID_DRAW_FREELINE == nID) + { + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Right(), rRectangle.Bottom())); + } + else + { + aInnerPoly.setClosed(true); + } + + aPoly.append(aInnerPoly); + break; + } + case SID_DRAW_XPOLYGON: + case SID_DRAW_XPOLYGON_NOFILL: + { + basegfx::B2DPolygon aInnerPoly; + + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left(), rRectangle.Bottom())); + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left(), rRectangle.Top())); + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Top())); + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Center().Y())); + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Right(), rRectangle.Center().Y())); + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Right(), rRectangle.Bottom())); + + if(SID_DRAW_XPOLYGON_NOFILL == nID) + { + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Bottom())); + } + else + { + aInnerPoly.setClosed(true); + } + + aPoly.append(aInnerPoly); + break; + } + case SID_DRAW_POLYGON: + case SID_DRAW_POLYGON_NOFILL: + { + basegfx::B2DPolygon aInnerPoly; + const sal_Int32 nWdt(rRectangle.GetWidth()); + const sal_Int32 nHgt(rRectangle.GetHeight()); + + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left(), rRectangle.Bottom())); + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left() + (nWdt * 30) / 100, rRectangle.Top() + (nHgt * 70) / 100)); + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left(), rRectangle.Top() + (nHgt * 15) / 100)); + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left() + (nWdt * 65) / 100, rRectangle.Top())); + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left() + nWdt, rRectangle.Top() + (nHgt * 30) / 100)); + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left() + (nWdt * 80) / 100, rRectangle.Top() + (nHgt * 50) / 100)); + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left() + (nWdt * 80) / 100, rRectangle.Top() + (nHgt * 75) / 100)); + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Bottom(), rRectangle.Right())); + + if(SID_DRAW_POLYGON_NOFILL == nID) + { + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Bottom())); + } + else + { + aInnerPoly.setClosed(true); + } + + aPoly.append(aInnerPoly); + break; + } + } + + pPathObj->SetPathPoly(aPoly); + } + else + { + OSL_FAIL("Object is NO path object"); + } + + pObj->SetLogicRect(rRectangle); + } + + return pObj; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuconcs.cxx b/sd/source/ui/func/fuconcs.cxx new file mode 100644 index 000000000..806960dd2 --- /dev/null +++ b/sd/source/ui/func/fuconcs.cxx @@ -0,0 +1,261 @@ +/* -*- 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 <fuconcs.hxx> +#include <rtl/ustring.hxx> + +#include <svx/svxids.hrc> + +#include <sfx2/viewfrm.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/request.hxx> +#include <editeng/adjustitem.hxx> +#include <editeng/eeitem.hxx> +#include <svx/svdoashp.hxx> +#include <svx/sdtagitm.hxx> + +#include <ViewShell.hxx> +#include <ViewShellBase.hxx> +#include <ToolBarManager.hxx> +#include <svx/gallery.hxx> +#include <svx/sdooitm.hxx> +#include <svl/itempool.hxx> +#include <svl/stritem.hxx> + +#include <View.hxx> +#include <Window.hxx> +#include <drawdoc.hxx> + +namespace sd { + + +FuConstructCustomShape::FuConstructCustomShape ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq ) : + FuConstruct(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuConstructCustomShape::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, bool bPermanent ) +{ + FuConstructCustomShape* pFunc; + rtl::Reference<FuPoor> xFunc( pFunc = new FuConstructCustomShape( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + pFunc->SetPermanent( bPermanent ); + return xFunc; +} + +void FuConstructCustomShape::DoExecute( SfxRequest& rReq ) +{ + FuConstruct::DoExecute( rReq ); + + const SfxItemSet* pArgs = rReq.GetArgs(); + if ( pArgs ) + { + const SfxStringItem& rItm = static_cast<const SfxStringItem&>(pArgs->Get( rReq.GetSlot() )); + aCustomShape = rItm.GetValue(); + } + + mpViewShell->GetViewShellBase().GetToolBarManager()->SetToolBar( + ToolBarManager::ToolBarGroup::Function, + ToolBarManager::msDrawingObjectToolBar); +} + +bool FuConstructCustomShape::MouseButtonDown(const MouseEvent& rMEvt) +{ + bool bReturn = FuConstruct::MouseButtonDown(rMEvt); + + if ( rMEvt.IsLeft() && !mpView->IsAction() ) + { + Point aPnt( mpWindow->PixelToLogic( rMEvt.GetPosPixel() ) ); + + mpWindow->CaptureMouse(); + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + + mpView->BegCreateObj(aPnt, nullptr, nDrgLog); + + SdrObject* pObj = mpView->GetCreateObj(); + if ( pObj ) + { + SetAttributes( pObj ); + bool bForceFillStyle = true; + bool bForceNoFillStyle = false; + if ( static_cast<SdrObjCustomShape*>(pObj)->UseNoFillStyle() ) + { + bForceFillStyle = false; + bForceNoFillStyle = true; + } + SfxItemSet aAttr(mpDoc->GetPool()); + SetStyleSheet( aAttr, pObj, bForceFillStyle, bForceNoFillStyle ); + pObj->SetMergedItemSet(aAttr); + } + } + + return bReturn; +} + +bool FuConstructCustomShape::MouseButtonUp(const MouseEvent& rMEvt) +{ + bool bReturn(false); + + if(mpView->IsCreateObj() && rMEvt.IsLeft()) + { + SdrObject* pObj = mpView->GetCreateObj(); + if( pObj && mpView->EndCreateObj( SdrCreateCmd::ForceEnd ) ) + { + bReturn = true; + } + else + { + //Drag was too small to create object, so insert default object at click pos + Point aClickPos(mpWindow->PixelToLogic(rMEvt.GetPosPixel())); + sal_uInt32 nDefaultObjectSize(1000); + sal_Int32 nCenterOffset(-sal_Int32(nDefaultObjectSize / 2)); + aClickPos.AdjustX(nCenterOffset); + aClickPos.AdjustY(nCenterOffset); + + SdrPageView *pPV = mpView->GetSdrPageView(); + if(mpView->IsSnapEnabled()) + aClickPos = mpView->GetSnapPos(aClickPos, pPV); + + ::tools::Rectangle aNewObjectRectangle(aClickPos, Size(nDefaultObjectSize, nDefaultObjectSize)); + SdrObjectUniquePtr pObjDefault = CreateDefaultObject(nSlotId, aNewObjectRectangle); + + bReturn = mpView->InsertObjectAtView(pObjDefault.release(), *pPV); + } + } + bReturn = FuConstruct::MouseButtonUp (rMEvt) || bReturn; + + if (!bPermanent) + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + + return bReturn; +} + +void FuConstructCustomShape::Activate() +{ + mpView->SetCurrentObj( SdrObjKind::CustomShape ); + FuConstruct::Activate(); +} + +/** + * set attribute for the object to be created + */ +void FuConstructCustomShape::SetAttributes( SdrObject* pObj ) +{ + bool bAttributesAppliedFromGallery = false; + + if ( GalleryExplorer::GetSdrObjCount( GALLERY_THEME_POWERPOINT ) ) + { + std::vector< OUString > aObjList; + if ( GalleryExplorer::FillObjListTitle( GALLERY_THEME_POWERPOINT, aObjList ) ) + { + for ( std::vector<OUString>::size_type i = 0; i < aObjList.size(); i++ ) + { + if ( aObjList[ i ].equalsIgnoreAsciiCase( aCustomShape ) ) + { + FmFormModel aFormModel; + SfxItemPool& rPool(aFormModel.GetItemPool()); + rPool.FreezeIdRanges(); + + if ( GalleryExplorer::GetSdrObj( GALLERY_THEME_POWERPOINT, i, &aFormModel ) ) + { + const SdrPage* pPage = aFormModel.GetPage( 0 ); + if ( pPage ) + { + const SdrObject* pSourceObj = pPage->GetObj( 0 ); + if( pSourceObj ) + { + const SfxItemSet& rSource = pSourceObj->GetMergedItemSet(); + SfxItemSetFixed< + // Ranges from SdrAttrObj: + SDRATTR_START, SDRATTR_SHADOW_LAST, + SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST, + SDRATTR_TEXTDIRECTION, + SDRATTR_TEXTDIRECTION, + // Graphic attributes, 3D properties, + // CustomShape properties: + SDRATTR_GRAF_FIRST, + SDRATTR_CUSTOMSHAPE_LAST, + // Range from SdrTextObj: + EE_ITEMS_START, EE_ITEMS_END> aDest(pObj->getSdrModelFromSdrObject().GetItemPool()); + aDest.Set( rSource ); + pObj->SetMergedItemSet( aDest ); + // Enables Word-wrap by default (tdf#134369) + pObj->SetMergedItem( SdrOnOffItem( SDRATTR_TEXT_WORDWRAP, true ) ); + Degree100 nAngle = pSourceObj->GetRotateAngle(); + if ( nAngle ) + pObj->NbcRotate( pObj->GetSnapRect().Center(), nAngle ); + bAttributesAppliedFromGallery = true; + } + } + } + break; + } + } + } + } + if ( !bAttributesAppliedFromGallery ) + { + pObj->SetMergedItem( SvxAdjustItem( SvxAdjust::Center, EE_PARA_JUST ) ); + pObj->SetMergedItem( SdrTextVertAdjustItem( SDRTEXTVERTADJUST_CENTER ) ); + pObj->SetMergedItem( SdrTextHorzAdjustItem( SDRTEXTHORZADJUST_BLOCK ) ); + pObj->SetMergedItem( makeSdrTextAutoGrowHeightItem( false ) ); + static_cast<SdrObjCustomShape*>(pObj)->MergeDefaultAttributes( &aCustomShape ); + } +} + +const OUString& FuConstructCustomShape::GetShapeType() const +{ + return aCustomShape; +} + +SdrObjectUniquePtr FuConstructCustomShape::CreateDefaultObject(const sal_uInt16, const ::tools::Rectangle& rRectangle) +{ + SdrObjectUniquePtr pObj(SdrObjFactory::MakeNewObject( + mpView->getSdrModelFromSdrView(), + mpView->GetCurrentObjInventor(), + mpView->GetCurrentObjIdentifier())); + + if( pObj ) + { + ::tools::Rectangle aRect( rRectangle ); + if ( doConstructOrthogonal() ) + ImpForceQuadratic( aRect ); + pObj->SetLogicRect( aRect ); + SetAttributes( pObj.get() ); + SfxItemSet aAttr(mpDoc->GetPool()); + SetStyleSheet(aAttr, pObj.get()); + pObj->SetMergedItemSet(aAttr); + } + return pObj; +} + +// #i33136# +bool FuConstructCustomShape::doConstructOrthogonal() const +{ + return SdrObjCustomShape::doConstructOrthogonal(aCustomShape); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuconnct.cxx b/sd/source/ui/func/fuconnct.cxx new file mode 100644 index 000000000..fc95b4907 --- /dev/null +++ b/sd/source/ui/func/fuconnct.cxx @@ -0,0 +1,71 @@ +/* -*- 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 <fuconnct.hxx> +#include <sfx2/request.hxx> +#include <View.hxx> +#include <drawdoc.hxx> +#include <svx/svxdlg.hxx> +#include <svx/dialogs.hrc> + +namespace sd { + + +FuConnectionDlg::FuConnectionDlg ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuConnectionDlg::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuConnectionDlg( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuConnectionDlg::DoExecute( SfxRequest& rReq ) +{ + SfxItemSet aNewAttr( mpDoc->GetPool() ); + mpView->GetAttributes( aNewAttr ); + + const SfxItemSet* pArgs = rReq.GetArgs(); + + if( !pArgs ) + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractDialog> pDlg(pFact->CreateSfxDialog(rReq.GetFrameWeld(), aNewAttr, mpView, RID_SVXPAGE_CONNECTION)); + + if( pDlg->Execute() == RET_OK ) + { + rReq.Done( *pDlg->GetOutputItemSet() ); + pArgs = rReq.GetArgs(); + } + } + if( pArgs ) + mpView->SetAttributes( *pArgs ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuconrec.cxx b/sd/source/ui/func/fuconrec.cxx new file mode 100644 index 000000000..d93ef2849 --- /dev/null +++ b/sd/source/ui/func/fuconrec.cxx @@ -0,0 +1,1096 @@ +/* -*- 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 <fuconrec.hxx> +#include <svx/svdpagv.hxx> + +#include <svx/svxids.hrc> +#include <svx/strings.hrc> +#include <svx/dialmgr.hxx> + +#include <app.hrc> +#include <svl/itemset.hxx> +#include <svx/xlineit0.hxx> +#include <svx/xlnstwit.hxx> +#include <svx/xlnedwit.hxx> +#include <svx/xlnedit.hxx> +#include <svx/xlnstit.hxx> +#include <svx/xlnwtit.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/sdtmfitm.hxx> +#include <svx/sxekitm.hxx> +#include <svx/sderitm.hxx> +#include <sfx2/dispatch.hxx> +#include <svx/svdopath.hxx> +#include <svx/svdocirc.hxx> +#include <svl/intitem.hxx> +#include <sfx2/request.hxx> +#include <editeng/adjustitem.hxx> +#include <editeng/eeitem.hxx> +#include <svx/xtable.hxx> +#include <svx/xfltrit.hxx> +#include <svx/xflclit.hxx> +#include <svx/sdtagitm.hxx> +#include <svx/sdtditm.hxx> + +#include <svx/svdocapt.hxx> + +#include <svx/svdomeas.hxx> +#include <ViewShell.hxx> +#include <ViewShellBase.hxx> +#include <ToolBarManager.hxx> +#include <editeng/writingmodeitem.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <svx/xfillit0.hxx> +#include <svx/signaturelinehelper.hxx> +#include <osl/diagnose.h> + +#include <sdresid.hxx> +#include <View.hxx> +#include <sdpage.hxx> +#include <Window.hxx> +#include <drawdoc.hxx> +#include <unokywds.hxx> + +#include <strings.hrc> + +using namespace com::sun::star; + +namespace sd { + + +FuConstructRectangle::FuConstructRectangle ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuConstruct(pViewSh, pWin, pView, pDoc, rReq) + , mnFillTransparence(0) + , mnLineStyle(SAL_MAX_UINT16) +{ +} + +namespace{ + +/// Checks to see if the request has a parameter of IsSticky:bool=true +/// It means that the selected command/button will stay selected after use +bool isSticky(const SfxRequest& rReq) +{ + const SfxItemSet *pArgs = rReq.GetArgs (); + if (pArgs) + { + const SfxBoolItem* pIsSticky = rReq.GetArg<SfxBoolItem>(FN_PARAM_4); + if (pIsSticky && pIsSticky->GetValue()) + return true; + } + + return false; +} + +} + +rtl::Reference<FuPoor> FuConstructRectangle::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, bool bPermanent ) +{ + FuConstructRectangle* pFunc; + rtl::Reference<FuPoor> xFunc( pFunc = new FuConstructRectangle( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + pFunc->SetPermanent(bPermanent || isSticky(rReq)); + return xFunc; +} + +void FuConstructRectangle::DoExecute( SfxRequest& rReq ) +{ + FuConstruct::DoExecute( rReq ); + + mpViewShell->GetViewShellBase().GetToolBarManager()->SetToolBar( + ToolBarManager::ToolBarGroup::Function, + ToolBarManager::msDrawingObjectToolBar); + + const SfxItemSet *pArgs = rReq.GetArgs (); + + if (pArgs) + { + switch (nSlotId) + { + case SID_DRAW_ELLIPSE : + { + const SfxUInt32Item* pCenterX = rReq.GetArg<SfxUInt32Item>(ID_VAL_CENTER_X); + const SfxUInt32Item* pCenterY = rReq.GetArg<SfxUInt32Item>(ID_VAL_CENTER_Y); + const SfxUInt32Item* pAxisX = rReq.GetArg<SfxUInt32Item>(ID_VAL_AXIS_X); + const SfxUInt32Item* pAxisY = rReq.GetArg<SfxUInt32Item>(ID_VAL_AXIS_Y); + + if (!pCenterX || !pCenterY || !pAxisX || !pAxisY) + break; + + ::tools::Rectangle aNewRectangle (pCenterX->GetValue () - pAxisX->GetValue () / 2, + pCenterY->GetValue () - pAxisY->GetValue () / 2, + pCenterX->GetValue () + pAxisX->GetValue () / 2, + pCenterY->GetValue () + pAxisY->GetValue () / 2); + SdrCircObj *pNewCircle = new SdrCircObj( + mpView->getSdrModelFromSdrView(), + SdrCircKind::Full, + aNewRectangle); + SdrPageView *pPV = mpView->GetSdrPageView(); + + mpView->InsertObjectAtView(pNewCircle, *pPV, SdrInsertFlags::SETDEFLAYER | SdrInsertFlags::SETDEFATTR); + } + break; + + case SID_DRAW_RECT : + { + const SfxUInt32Item* pMouseStartX = rReq.GetArg<SfxUInt32Item>(ID_VAL_MOUSESTART_X); + const SfxUInt32Item* pMouseStartY = rReq.GetArg<SfxUInt32Item>(ID_VAL_MOUSESTART_Y); + const SfxUInt32Item* pMouseEndX = rReq.GetArg<SfxUInt32Item>(ID_VAL_MOUSEEND_X); + const SfxUInt32Item* pMouseEndY = rReq.GetArg<SfxUInt32Item>(ID_VAL_MOUSEEND_Y); + const SfxUInt16Item* pFillTransparence = rReq.GetArg<SfxUInt16Item>(FN_PARAM_1); + const SfxStringItem* pFillColor = rReq.GetArg<SfxStringItem>(FN_PARAM_2); + const SfxUInt16Item* pLineStyle = rReq.GetArg<SfxUInt16Item>(FN_PARAM_3); + const SfxStringItem* pShapeName = rReq.GetArg<SfxStringItem>(SID_SHAPE_NAME); + + if (pFillTransparence && pFillTransparence->GetValue() > 0) + { + mnFillTransparence = pFillTransparence->GetValue(); + } + if (pFillColor && !pFillColor->GetValue().isEmpty()) + { + msFillColor = pFillColor->GetValue(); + } + if (pLineStyle) + { + mnLineStyle = pLineStyle->GetValue(); + } + if (pShapeName && !pShapeName->GetValue().isEmpty()) + { + msShapeName = pShapeName->GetValue(); + } + + if (!pMouseStartX || !pMouseStartY || !pMouseEndX || !pMouseEndY) + break; + + ::tools::Rectangle aNewRectangle (pMouseStartX->GetValue (), + pMouseStartY->GetValue (), + pMouseEndX->GetValue (), + pMouseEndY->GetValue ()); + SdrRectObj *pNewRect = new SdrRectObj( + mpView->getSdrModelFromSdrView(), + aNewRectangle); + SdrPageView *pPV = mpView->GetSdrPageView(); + + mpView->InsertObjectAtView(pNewRect, *pPV, SdrInsertFlags::SETDEFLAYER | SdrInsertFlags::SETDEFATTR); + } + break; + } + } + + if (nSlotId == SID_TOOL_CONNECTOR || + nSlotId == SID_CONNECTOR_ARROW_START || + nSlotId == SID_CONNECTOR_ARROW_END || + nSlotId == SID_CONNECTOR_ARROWS || + nSlotId == SID_CONNECTOR_CIRCLE_START || + nSlotId == SID_CONNECTOR_CIRCLE_END || + nSlotId == SID_CONNECTOR_CIRCLES || + nSlotId == SID_CONNECTOR_LINE || + nSlotId == SID_CONNECTOR_LINE_ARROW_START || + nSlotId == SID_CONNECTOR_LINE_ARROW_END || + nSlotId == SID_CONNECTOR_LINE_ARROWS || + nSlotId == SID_CONNECTOR_LINE_CIRCLE_START || + nSlotId == SID_CONNECTOR_LINE_CIRCLE_END || + nSlotId == SID_CONNECTOR_LINE_CIRCLES || + nSlotId == SID_CONNECTOR_CURVE || + nSlotId == SID_CONNECTOR_CURVE_ARROW_START || + nSlotId == SID_CONNECTOR_CURVE_ARROW_END || + nSlotId == SID_CONNECTOR_CURVE_ARROWS || + nSlotId == SID_CONNECTOR_CURVE_CIRCLE_START || + nSlotId == SID_CONNECTOR_CURVE_CIRCLE_END || + nSlotId == SID_CONNECTOR_CURVE_CIRCLES || + nSlotId == SID_CONNECTOR_LINES || + nSlotId == SID_CONNECTOR_LINES_ARROW_START || + nSlotId == SID_CONNECTOR_LINES_ARROW_END || + nSlotId == SID_CONNECTOR_LINES_ARROWS || + nSlotId == SID_CONNECTOR_LINES_CIRCLE_START || + nSlotId == SID_CONNECTOR_LINES_CIRCLE_END || + nSlotId == SID_CONNECTOR_LINES_CIRCLES || + nSlotId == SID_LINE_ARROW_START || + nSlotId == SID_LINE_ARROW_END || + nSlotId == SID_LINE_ARROWS || + nSlotId == SID_LINE_ARROW_CIRCLE || + nSlotId == SID_LINE_CIRCLE_ARROW || + nSlotId == SID_LINE_ARROW_SQUARE || + nSlotId == SID_LINE_SQUARE_ARROW ) + { + mpView->UnmarkAll(); + } +} + +bool FuConstructRectangle::MouseButtonDown(const MouseEvent& rMEvt) +{ + bool bReturn = FuConstruct::MouseButtonDown(rMEvt); + + if ( rMEvt.IsLeft() && !mpView->IsAction() ) + { + Point aPnt( mpWindow->PixelToLogic( rMEvt.GetPosPixel() ) ); + + mpWindow->CaptureMouse(); + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + + if (mpView->GetCurrentObjIdentifier() == SdrObjKind::Caption) + { + Size aCaptionSize(846, 846); // (4x2)cm + bReturn = mpView->BegCreateCaptionObj(aPnt, aCaptionSize, + nullptr, nDrgLog); + } + else + { + mpView->BegCreateObj(aPnt, nullptr, nDrgLog); + } + + SdrObject* pObj = mpView->GetCreateObj(); + + if (pObj) + { + SfxItemSet aAttr(mpDoc->GetPool()); + SetStyleSheet(aAttr, pObj); + SetAttributes(aAttr, pObj); + SetLineEnds(aAttr, *pObj); + pObj->SetMergedItemSet(aAttr); + + if( nSlotId == SID_DRAW_CAPTION_VERTICAL ) + static_cast<SdrTextObj*>(pObj)->SetVerticalWriting( true ); + } + } + return bReturn; +} + +bool FuConstructRectangle::MouseButtonUp(const MouseEvent& rMEvt) +{ + bool bReturn(false); + + if(mpView->IsCreateObj() && rMEvt.IsLeft()) + { + SdrObject* pObj = mpView->GetCreateObj(); + + if(pObj && mpView->EndCreateObj(SdrCreateCmd::ForceEnd)) + { + if(SID_DRAW_MEASURELINE == nSlotId) + { + SdrLayerAdmin& rAdmin = mpDoc->GetLayerAdmin(); + pObj->SetLayer(rAdmin.GetLayerID(sUNO_LayerName_measurelines)); + } + + // init text position when vertical caption object is created + if( dynamic_cast< const SdrCaptionObj *>( pObj ) != nullptr && SID_DRAW_CAPTION_VERTICAL == nSlotId) + { + // draw text object, needs to be initialized when vertical text is used + SfxItemSet aSet(pObj->GetMergedItemSet()); + + aSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_CENTER)); + aSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT)); + + // Correct the value of SDRATTR_TEXTDIRECTION to avoid SetItemSet + // calling SetVerticalWriting() again since this item may not yet + // be set at the object and thus may differ from vertical state of + // the object. + aSet.Put(SvxWritingModeItem(css::text::WritingMode_TB_RL, SDRATTR_TEXTDIRECTION)); + pObj->SetMergedItemSet(aSet); + } + + bReturn = true; + } + else + { + //Drag was too small to create object, so insert default object at click pos + Point aClickPos(mpWindow->PixelToLogic(rMEvt.GetPosPixel())); + sal_uInt32 nDefaultObjectSize(1500); + sal_Int32 nCenterOffset(-sal_Int32(nDefaultObjectSize / 2)); + aClickPos.AdjustX(nCenterOffset); + aClickPos.AdjustY(nCenterOffset); + + SdrPageView *pPV = mpView->GetSdrPageView(); + if(mpView->IsSnapEnabled()) + aClickPos = mpView->GetSnapPos(aClickPos, pPV); + + ::tools::Rectangle aNewObjectRectangle(aClickPos, Size(nDefaultObjectSize, nDefaultObjectSize)); + SdrObjectUniquePtr pObjDefault = CreateDefaultObject(nSlotId, aNewObjectRectangle); + + bReturn = mpView->InsertObjectAtView(pObjDefault.release(), *pPV); + } + } + + bReturn = FuConstruct::MouseButtonUp (rMEvt) || bReturn; + + if (!bPermanent) + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + + return bReturn; +} + +void FuConstructRectangle::Activate() +{ + SdrObjKind aObjKind; + + switch (nSlotId) + { + case SID_LINE_ARROW_START: + case SID_LINE_ARROW_END: + case SID_LINE_ARROWS: + case SID_LINE_ARROW_CIRCLE: + case SID_LINE_CIRCLE_ARROW: + case SID_LINE_ARROW_SQUARE: + case SID_LINE_SQUARE_ARROW: + mpView->SetGlueVisible(); + [[fallthrough]]; + case SID_DRAW_LINE : + case SID_DRAW_XLINE: + aObjKind = SdrObjKind::Line; + break; + + case SID_DRAW_MEASURELINE: + { + aObjKind = SdrObjKind::Measure; + } + break; + + case SID_DRAW_RECT : + case SID_DRAW_RECT_NOFILL : + case SID_DRAW_RECT_ROUND : + case SID_DRAW_RECT_ROUND_NOFILL: + case SID_DRAW_SQUARE : + case SID_DRAW_SQUARE_NOFILL : + case SID_DRAW_SQUARE_ROUND : + case SID_DRAW_SQUARE_ROUND_NOFILL: + { + aObjKind = SdrObjKind::Rectangle; + } + break; + + case SID_DRAW_ELLIPSE : + case SID_DRAW_ELLIPSE_NOFILL: + case SID_DRAW_CIRCLE : + case SID_DRAW_CIRCLE_NOFILL : + { + aObjKind = SdrObjKind::CircleOrEllipse; + } + break; + + case SID_DRAW_CAPTION: + case SID_DRAW_CAPTION_VERTICAL: + { + aObjKind = SdrObjKind::Caption; + } + break; + + case SID_TOOL_CONNECTOR: + case SID_CONNECTOR_ARROW_START: + case SID_CONNECTOR_ARROW_END: + case SID_CONNECTOR_ARROWS: + case SID_CONNECTOR_CIRCLE_START: + case SID_CONNECTOR_CIRCLE_END: + case SID_CONNECTOR_CIRCLES: + case SID_CONNECTOR_LINE: + case SID_CONNECTOR_LINE_ARROW_START: + case SID_CONNECTOR_LINE_ARROW_END: + case SID_CONNECTOR_LINE_ARROWS: + case SID_CONNECTOR_LINE_CIRCLE_START: + case SID_CONNECTOR_LINE_CIRCLE_END: + case SID_CONNECTOR_LINE_CIRCLES: + case SID_CONNECTOR_CURVE: + case SID_CONNECTOR_CURVE_ARROW_START: + case SID_CONNECTOR_CURVE_ARROW_END: + case SID_CONNECTOR_CURVE_ARROWS: + case SID_CONNECTOR_CURVE_CIRCLE_START: + case SID_CONNECTOR_CURVE_CIRCLE_END: + case SID_CONNECTOR_CURVE_CIRCLES: + case SID_CONNECTOR_LINES: + case SID_CONNECTOR_LINES_ARROW_START: + case SID_CONNECTOR_LINES_ARROW_END: + case SID_CONNECTOR_LINES_ARROWS: + case SID_CONNECTOR_LINES_CIRCLE_START: + case SID_CONNECTOR_LINES_CIRCLE_END: + case SID_CONNECTOR_LINES_CIRCLES: + { + aObjKind = SdrObjKind::Edge; + mpView->SetGlueVisible(); + } + break; + case SID_INSERT_SIGNATURELINE: + { + aObjKind = SdrObjKind::Graphic; + } + break; + + default: + { + aObjKind = SdrObjKind::Rectangle; + } + break; + } + + mpView->SetCurrentObj(aObjKind); + + FuConstruct::Activate(); +} + +void FuConstructRectangle::Deactivate() +{ + if( nSlotId == SID_TOOL_CONNECTOR || + nSlotId == SID_CONNECTOR_ARROW_START || + nSlotId == SID_CONNECTOR_ARROW_END || + nSlotId == SID_CONNECTOR_ARROWS || + nSlotId == SID_CONNECTOR_CIRCLE_START || + nSlotId == SID_CONNECTOR_CIRCLE_END || + nSlotId == SID_CONNECTOR_CIRCLES || + nSlotId == SID_CONNECTOR_LINE || + nSlotId == SID_CONNECTOR_LINE_ARROW_START || + nSlotId == SID_CONNECTOR_LINE_ARROW_END || + nSlotId == SID_CONNECTOR_LINE_ARROWS || + nSlotId == SID_CONNECTOR_LINE_CIRCLE_START || + nSlotId == SID_CONNECTOR_LINE_CIRCLE_END || + nSlotId == SID_CONNECTOR_LINE_CIRCLES || + nSlotId == SID_CONNECTOR_CURVE || + nSlotId == SID_CONNECTOR_CURVE_ARROW_START || + nSlotId == SID_CONNECTOR_CURVE_ARROW_END || + nSlotId == SID_CONNECTOR_CURVE_ARROWS || + nSlotId == SID_CONNECTOR_CURVE_CIRCLE_START || + nSlotId == SID_CONNECTOR_CURVE_CIRCLE_END || + nSlotId == SID_CONNECTOR_CURVE_CIRCLES || + nSlotId == SID_CONNECTOR_LINES || + nSlotId == SID_CONNECTOR_LINES_ARROW_START || + nSlotId == SID_CONNECTOR_LINES_ARROW_END || + nSlotId == SID_CONNECTOR_LINES_ARROWS || + nSlotId == SID_CONNECTOR_LINES_CIRCLE_START || + nSlotId == SID_CONNECTOR_LINES_CIRCLE_END || + nSlotId == SID_CONNECTOR_LINES_CIRCLES || + nSlotId == SID_LINE_ARROW_START || + nSlotId == SID_LINE_ARROW_END || + nSlotId == SID_LINE_ARROWS || + nSlotId == SID_LINE_ARROW_CIRCLE || + nSlotId == SID_LINE_CIRCLE_ARROW || + nSlotId == SID_LINE_ARROW_SQUARE || + nSlotId == SID_LINE_SQUARE_ARROW ) + { + mpView->SetGlueVisible( false ); + } + FuConstruct::Deactivate(); + + if (nSlotId != SID_INSERT_SIGNATURELINE) + { + return; + } + + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + if (rMarkList.GetMarkCount() < 1) + { + // Just switching pages, no signature rectangle yet. + return; + } + + // Finished drawing a signature rectangle, now set it up. + if (!mpViewShell) + { + return; + } + + uno::Reference<security::XCertificate> xCertificate + = svx::SignatureLineHelper::getSignatureCertificate(mpViewShell->GetObjectShell(), + mpViewShell->GetFrameWeld()); + if (!xCertificate.is()) + { + return; + } + + svx::SignatureLineHelper::setShapeCertificate(mpView, xCertificate); + + // Update infobar to offer "finish signing". + SfxViewFrame* pFrame = mpViewShell->GetViewFrame(); + if (pFrame && pFrame->HasInfoBarWithID(u"readonly")) + { + pFrame->RemoveInfoBar(u"readonly"); + pFrame->AppendReadOnlyInfobar(); + } +} + +namespace { +/// Returns the color based on the color names listed in core/include/tools/color.hxx +/// Feel free to extend with more color names from color.hxx +Color strToColor(std::u16string_view sColor) +{ + Color aColor = COL_AUTO; + + if (sColor == u"COL_GRAY") + aColor = COL_GRAY; + else if (sColor == u"COL_GRAY3") + aColor = COL_GRAY3; + else if (sColor == u"COL_GRAY7") + aColor = COL_GRAY7; + + return aColor; +} +} + +/** + * set attribute for the object to be created + */ +void FuConstructRectangle::SetAttributes(SfxItemSet& rAttr, SdrObject* pObj) +{ + if (nSlotId == SID_DRAW_RECT_ROUND || + nSlotId == SID_DRAW_RECT_ROUND_NOFILL || + nSlotId == SID_DRAW_SQUARE_ROUND || + nSlotId == SID_DRAW_SQUARE_ROUND_NOFILL) + { + // round corner + rAttr.Put(makeSdrEckenradiusItem(500)); + } + else if (nSlotId == SID_CONNECTOR_LINE || + nSlotId == SID_CONNECTOR_LINE_ARROW_START || + nSlotId == SID_CONNECTOR_LINE_ARROW_END || + nSlotId == SID_CONNECTOR_LINE_ARROWS || + nSlotId == SID_CONNECTOR_LINE_CIRCLE_START || + nSlotId == SID_CONNECTOR_LINE_CIRCLE_END || + nSlotId == SID_CONNECTOR_LINE_CIRCLES) + { + // direct connector + rAttr.Put(SdrEdgeKindItem(SdrEdgeKind::OneLine)); + } + else if (nSlotId == SID_CONNECTOR_LINES || + nSlotId == SID_CONNECTOR_LINES_ARROW_START || + nSlotId == SID_CONNECTOR_LINES_ARROW_END || + nSlotId == SID_CONNECTOR_LINES_ARROWS || + nSlotId == SID_CONNECTOR_LINES_CIRCLE_START || + nSlotId == SID_CONNECTOR_LINES_CIRCLE_END || + nSlotId == SID_CONNECTOR_LINES_CIRCLES) + { + // line connector + rAttr.Put(SdrEdgeKindItem(SdrEdgeKind::ThreeLines)); + } + else if (nSlotId == SID_CONNECTOR_CURVE || + nSlotId == SID_CONNECTOR_CURVE_ARROW_START || + nSlotId == SID_CONNECTOR_CURVE_ARROW_END || + nSlotId == SID_CONNECTOR_CURVE_ARROWS || + nSlotId == SID_CONNECTOR_CURVE_CIRCLE_START || + nSlotId == SID_CONNECTOR_CURVE_CIRCLE_END || + nSlotId == SID_CONNECTOR_CURVE_CIRCLES) + { + // curve connector + rAttr.Put(SdrEdgeKindItem(SdrEdgeKind::Bezier)); + } + else if ( nSlotId == SID_DRAW_CAPTION || nSlotId == SID_DRAW_CAPTION_VERTICAL ) + { + // legend object + Size aSize(pObj->GetLogicRect().GetSize()); + rAttr.Put( makeSdrTextMinFrameHeightItem( aSize.Height() ) ); + rAttr.Put( makeSdrTextMinFrameWidthItem( aSize.Width() ) ); + rAttr.Put( makeSdrTextAutoGrowHeightItem( true ) ); + rAttr.Put( makeSdrTextAutoGrowWidthItem( true ) ); + + // Support full with for vertical caption objects, too + if(SID_DRAW_CAPTION == nSlotId) + rAttr.Put( SdrTextHorzAdjustItem( SDRTEXTHORZADJUST_BLOCK ) ); + else + rAttr.Put( SdrTextVertAdjustItem( SDRTEXTVERTADJUST_BLOCK ) ); + + rAttr.Put( SvxAdjustItem( SvxAdjust::Center, EE_PARA_JUST ) ); + rAttr.Put( makeSdrTextLeftDistItem( 100 ) ); + rAttr.Put( makeSdrTextRightDistItem( 100 ) ); + rAttr.Put( makeSdrTextUpperDistItem( 100 ) ); + rAttr.Put( makeSdrTextLowerDistItem( 100 ) ); + } + else if (nSlotId == SID_DRAW_MEASURELINE) + { + // dimension line + SdPage* pPage = static_cast<SdPage*>( mpView->GetSdrPageView()->GetPage() ); + OUString aName(SdResId(STR_POOLSHEET_MEASURE)); + SfxStyleSheet* pSheet( + static_cast< SfxStyleSheet* >( + pPage->getSdrModelFromSdrPage().GetStyleSheetPool()->Find(aName, SfxStyleFamily::Para))); + DBG_ASSERT(pSheet, "StyleSheet missing"); + + if (pSheet) + { + pObj->SetStyleSheet(pSheet, false); + } + + SdrLayerAdmin& rAdmin = mpDoc->GetLayerAdmin(); + pObj->SetLayer(rAdmin.GetLayerID(sUNO_LayerName_measurelines)); + } + else if (nSlotId == SID_DRAW_RECT) + { + if (mnFillTransparence > 0 && mnFillTransparence <= 100) + rAttr.Put(XFillTransparenceItem(mnFillTransparence)); + if (!msFillColor.isEmpty()) + rAttr.Put(XFillColorItem(OUString(), strToColor(msFillColor))); + if (!msShapeName.isEmpty()) + pObj->SetName(msShapeName); + + switch(mnLineStyle) + { + case 0: + rAttr.Put( XLineStyleItem(css::drawing::LineStyle_NONE ) ); + break; + case 1: + rAttr.Put( XLineStyleItem(css::drawing::LineStyle_SOLID ) ); + break; + case 2: + rAttr.Put( XLineStyleItem(css::drawing::LineStyle_DASH ) ); + break; + default: + // Leave it to the defaults + break; + } + } + else if (nSlotId == SID_INSERT_SIGNATURELINE) + { + // Avoid the default solid fill and line, we'll set a graphic instead. + rAttr.Put(XFillStyleItem(drawing::FillStyle_NONE)); + rAttr.Put(XLineStyleItem(drawing::LineStyle_NONE)); + } +} + +/** + * set line starts and ends for the object to be created + */ +static ::basegfx::B2DPolyPolygon getPolygon(TranslateId pResId, const SdrModel& rModel) +{ + ::basegfx::B2DPolyPolygon aRetval; + XLineEndListRef pLineEndList(rModel.GetLineEndList()); + + if( pLineEndList.is() ) + { + OUString aArrowName(SvxResId(pResId)); + ::tools::Long nCount = pLineEndList->Count(); + ::tools::Long nIndex; + for( nIndex = 0; nIndex < nCount; nIndex++ ) + { + const XLineEndEntry* pEntry = pLineEndList->GetLineEnd(nIndex); + if( pEntry->GetName() == aArrowName ) + { + aRetval = pEntry->GetLineEnd(); + break; + } + } + } + + return aRetval; +} + +void FuConstructRectangle::SetLineEnds(SfxItemSet& rAttr, SdrObject const & rObj) +{ + if ( !((rObj.GetObjIdentifier() == SdrObjKind::Edge && + nSlotId != SID_TOOL_CONNECTOR && + nSlotId != SID_CONNECTOR_LINE && + nSlotId != SID_CONNECTOR_LINES && + nSlotId != SID_CONNECTOR_CURVE) || + nSlotId == SID_LINE_ARROW_START || + nSlotId == SID_LINE_ARROW_END || + nSlotId == SID_LINE_ARROWS || + nSlotId == SID_LINE_ARROW_CIRCLE || + nSlotId == SID_LINE_CIRCLE_ARROW || + nSlotId == SID_LINE_ARROW_SQUARE || + nSlotId == SID_LINE_SQUARE_ARROW) ) + return; + + // set attributes of line start and ends + SdrModel& rModel(rObj.getSdrModelFromSdrObject()); + + // arrowhead + ::basegfx::B2DPolyPolygon aArrow( getPolygon( RID_SVXSTR_ARROW, rModel ) ); + if( !aArrow.count() ) + { + ::basegfx::B2DPolygon aNewArrow; + aNewArrow.append(::basegfx::B2DPoint(10.0, 0.0)); + aNewArrow.append(::basegfx::B2DPoint(0.0, 30.0)); + aNewArrow.append(::basegfx::B2DPoint(20.0, 30.0)); + aNewArrow.setClosed(true); + aArrow.append(aNewArrow); + } + + // Circles + ::basegfx::B2DPolyPolygon aCircle( getPolygon( RID_SVXSTR_CIRCLE, rModel ) ); + if( !aCircle.count() ) + { + ::basegfx::B2DPolygon aNewCircle = ::basegfx::utils::createPolygonFromEllipse(::basegfx::B2DPoint(0.0, 0.0), 250.0, 250.0); + aNewCircle.setClosed(true); + aCircle.append(aNewCircle); + } + + // Square + ::basegfx::B2DPolyPolygon aSquare( getPolygon( RID_SVXSTR_SQUARE, rModel ) ); + if( !aSquare.count() ) + { + ::basegfx::B2DPolygon aNewSquare; + aNewSquare.append(::basegfx::B2DPoint(0.0, 0.0)); + aNewSquare.append(::basegfx::B2DPoint(10.0, 0.0)); + aNewSquare.append(::basegfx::B2DPoint(10.0, 10.0)); + aNewSquare.append(::basegfx::B2DPoint(0.0, 10.0)); + aNewSquare.setClosed(true); + aSquare.append(aNewSquare); + } + + SfxItemSet aSet( mpDoc->GetPool() ); + mpView->GetAttributes( aSet ); + + // #i3908# Here, the default Line Start/End width for arrow construction is + // set. To have the same value in all situations (construction) in i3908 + // it was decided to change the default to 0.03 cm for all situations. + ::tools::Long nWidth = 300; // (1/100th mm) + + // determine line width and calculate with it the line end width + if( aSet.GetItemState( XATTR_LINEWIDTH ) != SfxItemState::DONTCARE ) + { + ::tools::Long nValue = aSet.Get( XATTR_LINEWIDTH ).GetValue(); + if( nValue > 0 ) + nWidth = nValue * 3; + } + + switch (nSlotId) + { + case SID_CONNECTOR_ARROWS: + case SID_CONNECTOR_LINE_ARROWS: + case SID_CONNECTOR_LINES_ARROWS: + case SID_CONNECTOR_CURVE_ARROWS: + case SID_LINE_ARROWS: + { + // connector with arrow ends + rAttr.Put(XLineStartItem(SvxResId(RID_SVXSTR_ARROW), aArrow)); + rAttr.Put(XLineStartWidthItem(nWidth)); + rAttr.Put(XLineEndItem(SvxResId(RID_SVXSTR_ARROW), aArrow)); + rAttr.Put(XLineEndWidthItem(nWidth)); + } + break; + + case SID_CONNECTOR_ARROW_START: + case SID_CONNECTOR_LINE_ARROW_START: + case SID_CONNECTOR_LINES_ARROW_START: + case SID_CONNECTOR_CURVE_ARROW_START: + case SID_LINE_ARROW_START: + case SID_LINE_ARROW_CIRCLE: + case SID_LINE_ARROW_SQUARE: + { + // connector with arrow start + rAttr.Put(XLineStartItem(SvxResId(RID_SVXSTR_ARROW), aArrow)); + rAttr.Put(XLineStartWidthItem(nWidth)); + } + break; + + case SID_CONNECTOR_ARROW_END: + case SID_CONNECTOR_LINE_ARROW_END: + case SID_CONNECTOR_LINES_ARROW_END: + case SID_CONNECTOR_CURVE_ARROW_END: + case SID_LINE_ARROW_END: + case SID_LINE_CIRCLE_ARROW: + case SID_LINE_SQUARE_ARROW: + { + // connector with arrow end + rAttr.Put(XLineEndItem(SvxResId(RID_SVXSTR_ARROW), aArrow)); + rAttr.Put(XLineEndWidthItem(nWidth)); + } + break; + + case SID_CONNECTOR_CIRCLES: + case SID_CONNECTOR_LINE_CIRCLES: + case SID_CONNECTOR_LINES_CIRCLES: + case SID_CONNECTOR_CURVE_CIRCLES: + { + // connector with circle ends + rAttr.Put(XLineStartItem(SvxResId(RID_SVXSTR_CIRCLE), aCircle)); + rAttr.Put(XLineStartWidthItem(nWidth)); + rAttr.Put(XLineEndItem(SvxResId(RID_SVXSTR_CIRCLE), aCircle)); + rAttr.Put(XLineEndWidthItem(nWidth)); + } + break; + + case SID_CONNECTOR_CIRCLE_START: + case SID_CONNECTOR_LINE_CIRCLE_START: + case SID_CONNECTOR_LINES_CIRCLE_START: + case SID_CONNECTOR_CURVE_CIRCLE_START: + { + // connector with circle start + rAttr.Put(XLineStartItem(SvxResId(RID_SVXSTR_CIRCLE), aCircle)); + rAttr.Put(XLineStartWidthItem(nWidth)); + } + break; + + case SID_CONNECTOR_CIRCLE_END: + case SID_CONNECTOR_LINE_CIRCLE_END: + case SID_CONNECTOR_LINES_CIRCLE_END: + case SID_CONNECTOR_CURVE_CIRCLE_END: + { + // connector with circle ends + rAttr.Put(XLineEndItem(SvxResId(RID_SVXSTR_CIRCLE), aCircle)); + rAttr.Put(XLineEndWidthItem(nWidth)); + } + break; + } + + // and again, for the still missing ends + switch (nSlotId) + { + case SID_LINE_ARROW_CIRCLE: + { + // circle end + rAttr.Put(XLineEndItem(SvxResId(RID_SVXSTR_CIRCLE), aCircle)); + rAttr.Put(XLineEndWidthItem(nWidth)); + } + break; + + case SID_LINE_CIRCLE_ARROW: + { + // circle start + rAttr.Put(XLineStartItem(SvxResId(RID_SVXSTR_CIRCLE), aCircle)); + rAttr.Put(XLineStartWidthItem(nWidth)); + } + break; + + case SID_LINE_ARROW_SQUARE: + { + // square end + rAttr.Put(XLineEndItem(SvxResId(RID_SVXSTR_SQUARE), aSquare)); + rAttr.Put(XLineEndWidthItem(nWidth)); + } + break; + + case SID_LINE_SQUARE_ARROW: + { + // square start + rAttr.Put(XLineStartItem(SvxResId(RID_SVXSTR_SQUARE), aSquare)); + rAttr.Put(XLineStartWidthItem(nWidth)); + } + break; + } +} + +SdrObjectUniquePtr FuConstructRectangle::CreateDefaultObject(const sal_uInt16 nID, const ::tools::Rectangle& rRectangle) +{ + DBG_ASSERT( (nID != SID_DRAW_FONTWORK) && (nID != SID_DRAW_FONTWORK_VERTICAL ), "FuConstRectangle::CreateDefaultObject can not create Fontwork shapes!" ); + + // case SID_DRAW_LINE: + // case SID_DRAW_XLINE: + // case SID_DRAW_MEASURELINE: + // case SID_LINE_ARROW_START: + // case SID_LINE_ARROW_END: + // case SID_LINE_ARROWS: + // case SID_LINE_ARROW_CIRCLE: + // case SID_LINE_CIRCLE_ARROW: + // case SID_LINE_ARROW_SQUARE: + // case SID_LINE_SQUARE_ARROW: + // case SID_DRAW_RECT: + // case SID_DRAW_RECT_NOFILL: + // case SID_DRAW_RECT_ROUND: + // case SID_DRAW_RECT_ROUND_NOFILL: + // case SID_DRAW_SQUARE: + // case SID_DRAW_SQUARE_NOFILL: + // case SID_DRAW_SQUARE_ROUND: + // case SID_DRAW_SQUARE_ROUND_NOFILL: + // case SID_DRAW_ELLIPSE: + // case SID_DRAW_ELLIPSE_NOFILL: + // case SID_DRAW_CIRCLE: + // case SID_DRAW_CIRCLE_NOFILL: + // case SID_DRAW_CAPTION: + // case SID_DRAW_CAPTION_VERTICAL: + // case SID_TOOL_CONNECTOR: + // case SID_CONNECTOR_ARROW_START: + // case SID_CONNECTOR_ARROW_END: + // case SID_CONNECTOR_ARROWS: + // case SID_CONNECTOR_CIRCLE_START: + // case SID_CONNECTOR_CIRCLE_END: + // case SID_CONNECTOR_CIRCLES: + // case SID_CONNECTOR_LINE: + // case SID_CONNECTOR_LINE_ARROW_START: + // case SID_CONNECTOR_LINE_ARROW_END: + // case SID_CONNECTOR_LINE_ARROWS: + // case SID_CONNECTOR_LINE_CIRCLE_START: + // case SID_CONNECTOR_LINE_CIRCLE_END: + // case SID_CONNECTOR_LINE_CIRCLES: + // case SID_CONNECTOR_CURVE: + // case SID_CONNECTOR_CURVE_ARROW_START: + // case SID_CONNECTOR_CURVE_ARROW_END: + // case SID_CONNECTOR_CURVE_ARROWS: + // case SID_CONNECTOR_CURVE_CIRCLE_START: + // case SID_CONNECTOR_CURVE_CIRCLE_END: + // case SID_CONNECTOR_CURVE_CIRCLES: + // case SID_CONNECTOR_LINES: + // case SID_CONNECTOR_LINES_ARROW_START: + // case SID_CONNECTOR_LINES_ARROW_END: + // case SID_CONNECTOR_LINES_ARROWS: + // case SID_CONNECTOR_LINES_CIRCLE_START: + // case SID_CONNECTOR_LINES_CIRCLE_END: + // case SID_CONNECTOR_LINES_CIRCLES: + + SdrObjectUniquePtr pObj(SdrObjFactory::MakeNewObject( + mpView->getSdrModelFromSdrView(), + mpView->GetCurrentObjInventor(), + mpView->GetCurrentObjIdentifier())); + + if(pObj) + { + ::tools::Rectangle aRect(rRectangle); + + if(SID_DRAW_SQUARE == nID || + SID_DRAW_SQUARE_NOFILL == nID || + SID_DRAW_SQUARE_ROUND == nID || + SID_DRAW_SQUARE_ROUND_NOFILL == nID || + SID_DRAW_CIRCLE == nID || + SID_DRAW_CIRCLE_NOFILL == nID) + { + // force quadratic + ImpForceQuadratic(aRect); + } + + Point aStart = aRect.TopLeft(); + Point aEnd = aRect.BottomRight(); + + switch(nID) + { + case SID_DRAW_LINE: + case SID_DRAW_XLINE: + case SID_LINE_ARROW_START: + case SID_LINE_ARROW_END: + case SID_LINE_ARROWS: + case SID_LINE_ARROW_CIRCLE: + case SID_LINE_CIRCLE_ARROW: + case SID_LINE_ARROW_SQUARE: + case SID_LINE_SQUARE_ARROW: + { + if( auto pPathObj = dynamic_cast<SdrPathObj *>( pObj.get() ) ) + { + sal_Int32 nYMiddle((aRect.Top() + aRect.Bottom()) / 2); + + ::basegfx::B2DPolygon aB2DPolygon; + aB2DPolygon.append(::basegfx::B2DPoint(aStart.X(), nYMiddle)); + aB2DPolygon.append(::basegfx::B2DPoint(aEnd.X(), nYMiddle)); + pPathObj->SetPathPoly(::basegfx::B2DPolyPolygon(aB2DPolygon)); + } + else + { + OSL_FAIL("Object is NO line object"); + } + + break; + } + + case SID_DRAW_MEASURELINE: + { + if( auto pMeasureObj = dynamic_cast< SdrMeasureObj *>( pObj.get() ) ) + { + sal_Int32 nYMiddle((aRect.Top() + aRect.Bottom()) / 2); + pMeasureObj->SetPoint(Point(aStart.X(), nYMiddle), 0); + pMeasureObj->SetPoint(Point(aEnd.X(), nYMiddle), 1); + } + else + { + OSL_FAIL("Object is NO measure object"); + } + + break; + } + + case SID_TOOL_CONNECTOR: + case SID_CONNECTOR_ARROW_START: + case SID_CONNECTOR_ARROW_END: + case SID_CONNECTOR_ARROWS: + case SID_CONNECTOR_CIRCLE_START: + case SID_CONNECTOR_CIRCLE_END: + case SID_CONNECTOR_CIRCLES: + case SID_CONNECTOR_LINE: + case SID_CONNECTOR_LINE_ARROW_START: + case SID_CONNECTOR_LINE_ARROW_END: + case SID_CONNECTOR_LINE_ARROWS: + case SID_CONNECTOR_LINE_CIRCLE_START: + case SID_CONNECTOR_LINE_CIRCLE_END: + case SID_CONNECTOR_LINE_CIRCLES: + case SID_CONNECTOR_CURVE: + case SID_CONNECTOR_CURVE_ARROW_START: + case SID_CONNECTOR_CURVE_ARROW_END: + case SID_CONNECTOR_CURVE_ARROWS: + case SID_CONNECTOR_CURVE_CIRCLE_START: + case SID_CONNECTOR_CURVE_CIRCLE_END: + case SID_CONNECTOR_CURVE_CIRCLES: + case SID_CONNECTOR_LINES: + case SID_CONNECTOR_LINES_ARROW_START: + case SID_CONNECTOR_LINES_ARROW_END: + case SID_CONNECTOR_LINES_ARROWS: + case SID_CONNECTOR_LINES_CIRCLE_START: + case SID_CONNECTOR_LINES_CIRCLE_END: + case SID_CONNECTOR_LINES_CIRCLES: + { + if( auto pEdgeObj = dynamic_cast< SdrEdgeObj *>( pObj.get() ) ) + { + pEdgeObj->SetTailPoint(false, aStart); + pEdgeObj->SetTailPoint(true, aEnd); + } + else + { + OSL_FAIL("Object is NO connector object"); + } + + break; + } + case SID_DRAW_CAPTION: + case SID_DRAW_CAPTION_VERTICAL: + { + if( auto pCaptionObj = dynamic_cast< SdrCaptionObj *>( pObj.get() ) ) + { + bool bIsVertical(SID_DRAW_CAPTION_VERTICAL == nID); + + pCaptionObj->SetVerticalWriting(bIsVertical); + + if(bIsVertical) + { + SfxItemSet aSet(pObj->GetMergedItemSet()); + aSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_CENTER)); + aSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT)); + pObj->SetMergedItemSet(aSet); + } + + // The default text is not inserted anymore. + + pCaptionObj->SetLogicRect(aRect); + pCaptionObj->SetTailPos( + aRect.TopLeft() - Point(aRect.GetWidth() / 2, aRect.GetHeight() / 2)); + } + else + { + OSL_FAIL("Object is NO caption object"); + } + + break; + } + + default: + { + pObj->SetLogicRect(aRect); + + break; + } + } + + SfxItemSet aAttr(mpDoc->GetPool()); + SetStyleSheet(aAttr, pObj.get()); + SetAttributes(aAttr, pObj.get()); + SetLineEnds(aAttr, *pObj); + pObj->SetMergedItemSet(aAttr); + } + + return pObj; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuconstr.cxx b/sd/source/ui/func/fuconstr.cxx new file mode 100644 index 000000000..9d6f36320 --- /dev/null +++ b/sd/source/ui/func/fuconstr.cxx @@ -0,0 +1,414 @@ +/* -*- 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 <fuconstr.hxx> + +#include <svx/svxids.hrc> +#include <svx/svdpagv.hxx> +#include <svx/xdef.hxx> +#include <svx/xfillit0.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <tools/debug.hxx> +#include <svx/xflclit.hxx> +#include <svx/xlineit0.hxx> +#include <svx/xlnclit.hxx> + +#include <app.hrc> +#include <strings.hrc> +#include <strings.hxx> +#include <fudraw.hxx> +#include <View.hxx> +#include <Window.hxx> +#include <ViewShell.hxx> +#include <drawdoc.hxx> +#include <FrameView.hxx> +#include <sdpage.hxx> +#include <sdresid.hxx> +#include <glob.hxx> +#include <comphelper/lok.hxx> + +using namespace com::sun::star; + +namespace sd { + + +FuConstruct::FuConstruct ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuDraw(pViewSh, pWin, pView, pDoc, rReq), + bSelectionChanged(false) +{ +} + +bool FuConstruct::MouseButtonDown(const MouseEvent& rMEvt) +{ + bool bReturn = FuDraw::MouseButtonDown(rMEvt); + + bMBDown = true; + bSelectionChanged = false; + + if ( mpView->IsAction() ) + { + return true; + } + + bFirstMouseMove = true; + aDragTimer.Start(); + + aMDPos = mpWindow->PixelToLogic( rMEvt.GetPosPixel() ); + sal_uInt16 nHitLog = sal_uInt16 (mpWindow->PixelToLogic(Size(HITPIX,0)).Width()); + + if (rMEvt.IsLeft() && mpView->IsExtendedMouseEventDispatcherEnabled()) + { + mpWindow->CaptureMouse(); + + SdrHdl* pHdl = mpView->PickHandle(aMDPos); + + if ( pHdl != nullptr || mpView->IsMarkedHit(aMDPos, nHitLog) ) + { + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + mpView->BegDragObj(aMDPos, nullptr, pHdl, nDrgLog); + bReturn = true; + } + else if ( mpView->AreObjectsMarked() ) + { + mpView->UnmarkAll(); + bReturn = true; + } + } + + return bReturn; +} + +bool FuConstruct::MouseMove(const MouseEvent& rMEvt) +{ + FuDraw::MouseMove(rMEvt); + + if (aDragTimer.IsActive() ) + { + if( bFirstMouseMove ) + bFirstMouseMove = false; + else + aDragTimer.Stop(); + } + + Point aPix(rMEvt.GetPosPixel()); + Point aPnt( mpWindow->PixelToLogic(aPix) ); + + if ( mpView->IsAction() ) + { + ForceScroll(aPix); + mpView->MovAction(aPnt); + } + + return true; +} + +bool FuConstruct::MouseButtonUp(const MouseEvent& rMEvt) +{ + bool bReturn = true; + + if (aDragTimer.IsActive() ) + { + aDragTimer.Stop(); + bIsInDragMode = false; + } + + FuDraw::MouseButtonUp(rMEvt); + + Point aPnt( mpWindow->PixelToLogic( rMEvt.GetPosPixel() ) ); + + if ( mpView && mpView->IsDragObj() ) + { + FrameView* pFrameView = mpViewShell->GetFrameView(); + bool bDragWithCopy = (rMEvt.IsMod1() && pFrameView->IsDragWithCopy()); + + if (bDragWithCopy) + { + bDragWithCopy = !mpView->IsPresObjSelected(false); + } + + mpView->SetDragWithCopy(bDragWithCopy); + mpView->EndDragObj( mpView->IsDragWithCopy() ); + } + else if ( mpView && mpView->IsMarkObj() ) + { + mpView->EndMarkObj(); + } + else + { + bReturn = false; + } + + if ( mpView && !mpView->IsAction() ) + { + mpWindow->ReleaseMouse(); + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + + if ( !mpView->AreObjectsMarked() ) + { + SdrPageView* pPV; + sal_uInt16 nHitLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(HITPIX,0)).Width() ); + + SdrObject* pObj = mpView->PickObj(aPnt, mpView->getHitTolLog(), pPV); + if (!pObj) + { + mpView->MarkObj(aPnt, nHitLog); + } + + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + } + else if (rMEvt.IsLeft() && !rMEvt.IsShift() && !rMEvt.IsMod1() && !rMEvt.IsMod2() && + !bSelectionChanged && + std::abs(aPnt.X() - aMDPos.X()) < nDrgLog && + std::abs(aPnt.Y() - aMDPos.Y()) < nDrgLog) + { + // toggle between selection and rotation + SdrObject* pSingleObj = nullptr; + + if (mpView->GetMarkedObjectList().GetMarkCount()==1) + { + pSingleObj = mpView->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj(); + } + + const bool bTiledRendering = comphelper::LibreOfficeKit::isActive(); + if (!bTiledRendering && (mpView->GetDragMode() == SdrDragMode::Move && mpView->IsRotateAllowed() && + (mpViewShell->GetFrameView()->IsClickChangeRotation() || + (pSingleObj && pSingleObj->GetObjInventor()==SdrInventor::E3d)))) + { + mpView->SetDragMode(SdrDragMode::Rotate); + } + else + { + mpView->SetDragMode(SdrDragMode::Move); + } + } + } + + sal_uInt16 nClicks = rMEvt.GetClicks(); + + if (nClicks == 2 && rMEvt.IsLeft() && bMBDown && + !rMEvt.IsMod1() && !rMEvt.IsMod2() && !rMEvt.IsShift() ) + { + DoubleClick(rMEvt); + } + bMBDown = false; + + return bReturn; +} + +void FuConstruct::Activate() +{ + mpView->SetEditMode(SdrViewEditMode::Create); + FuDraw::Activate(); +} + +void FuConstruct::Deactivate() +{ + FuDraw::Deactivate(); + mpView->SetEditMode(SdrViewEditMode::Edit); +} + +/** + * set style sheet for the object to be created + */ +void FuConstruct::SetStyleSheet(SfxItemSet& rAttr, SdrObject* pObj) +{ + bool bUseFillStyle, bUseNoFillStyle; + bUseFillStyle = bUseNoFillStyle = false; + + switch( nSlotId ) + { + case SID_DRAW_RECT: + case SID_DRAW_RECT_ROUND: + case SID_DRAW_SQUARE: + case SID_DRAW_SQUARE_ROUND: + case SID_DRAW_ELLIPSE: + case SID_DRAW_PIE: + case SID_DRAW_ELLIPSECUT: + case SID_DRAW_CIRCLE: + case SID_DRAW_CIRCLEPIE: + case SID_DRAW_CIRCLECUT: + case SID_DRAW_POLYGON: + case SID_DRAW_XPOLYGON: + case SID_DRAW_FREELINE: + case SID_DRAW_BEZIER_FILL: + { + bUseFillStyle = true; + break; + } + case SID_DRAW_RECT_NOFILL: + case SID_DRAW_RECT_ROUND_NOFILL: + case SID_DRAW_SQUARE_NOFILL: + case SID_DRAW_SQUARE_ROUND_NOFILL: + case SID_DRAW_ELLIPSE_NOFILL: + case SID_DRAW_PIE_NOFILL: + case SID_DRAW_ELLIPSECUT_NOFILL: + case SID_DRAW_CIRCLE_NOFILL: + case SID_DRAW_CIRCLEPIE_NOFILL: + case SID_DRAW_CIRCLECUT_NOFILL: + case SID_DRAW_POLYGON_NOFILL: + case SID_DRAW_XPOLYGON_NOFILL: + case SID_DRAW_FREELINE_NOFILL: + case SID_DRAW_LINE: + case SID_DRAW_XLINE: + case SID_CONNECTOR_ARROW_START: + case SID_CONNECTOR_ARROW_END: + case SID_CONNECTOR_ARROWS: + case SID_CONNECTOR_CIRCLE_START: + case SID_CONNECTOR_CIRCLE_END: + case SID_CONNECTOR_CIRCLES: + case SID_CONNECTOR_LINE: + case SID_CONNECTOR_LINE_ARROW_START: + case SID_CONNECTOR_LINE_ARROW_END: + case SID_CONNECTOR_LINE_ARROWS: + case SID_CONNECTOR_LINE_CIRCLE_START: + case SID_CONNECTOR_LINE_CIRCLE_END: + case SID_CONNECTOR_LINE_CIRCLES: + case SID_CONNECTOR_CURVE: + case SID_CONNECTOR_CURVE_ARROW_START: + case SID_CONNECTOR_CURVE_ARROW_END: + case SID_CONNECTOR_CURVE_ARROWS: + case SID_CONNECTOR_CURVE_CIRCLE_START: + case SID_CONNECTOR_CURVE_CIRCLE_END: + case SID_CONNECTOR_CURVE_CIRCLES: + case SID_CONNECTOR_LINES: + case SID_CONNECTOR_LINES_ARROW_START: + case SID_CONNECTOR_LINES_ARROW_END: + case SID_CONNECTOR_LINES_ARROWS: + case SID_CONNECTOR_LINES_CIRCLE_START: + case SID_CONNECTOR_LINES_CIRCLE_END: + case SID_CONNECTOR_LINES_CIRCLES: + case SID_DRAW_BEZIER_NOFILL: + case SID_LINE_ARROW_END: + { + bUseNoFillStyle = true; + break; + } + } + SetStyleSheet( rAttr, pObj, bUseFillStyle, bUseNoFillStyle ); +} + +void FuConstruct::SetStyleSheet( SfxItemSet& rAttr, SdrObject* pObj, + const bool bForceFillStyle, const bool bForceNoFillStyle ) +{ + SdPage* pPage = static_cast<SdPage*>(mpView->GetSdrPageView()->GetPage()); + if ( pPage->IsMasterPage() && pPage->GetPageKind() == PageKind::Standard && + mpDoc->GetDocumentType() == DocumentType::Impress ) + { + /********************************************** + * Objects was created on the slide master page + ***********************************************/ + OUString aName( pPage->GetLayoutName() ); + sal_Int32 n = aName.indexOf(SD_LT_SEPARATOR) + SD_LT_SEPARATOR.getLength(); + aName = OUString::Concat(aName.subView(0, n)) + STR_LAYOUT_BACKGROUNDOBJECTS; + SfxStyleSheet* pSheet( + static_cast< SfxStyleSheet* >( + pPage->getSdrModelFromSdrPage().GetStyleSheetPool()->Find(aName, SfxStyleFamily::Page))); + DBG_ASSERT(pSheet, "StyleSheet missing"); + if (pSheet) + { + // applying style sheet for background objects + pObj->SetStyleSheet(pSheet, false); + SfxItemSet& rSet = pSheet->GetItemSet(); + const XFillStyleItem& rFillStyle = rSet.Get(XATTR_FILLSTYLE); + if ( bForceFillStyle ) + { + if (rFillStyle.GetValue() == drawing::FillStyle_NONE) + rAttr.Put(XFillStyleItem(drawing::FillStyle_SOLID)); + } + else if ( bForceNoFillStyle ) + { + if (rFillStyle.GetValue() != drawing::FillStyle_NONE) + rAttr.Put(XFillStyleItem(drawing::FillStyle_NONE)); + } + } + } + else + { + /*********************************** + * object was created on normal page + ************************************/ + if ( bForceNoFillStyle ) + { + OUString aName(SdResId(STR_POOLSHEET_OBJWITHOUTFILL)); + SfxStyleSheet* pSheet( + static_cast< SfxStyleSheet* >( + pPage->getSdrModelFromSdrPage().GetStyleSheetPool()->Find(aName, SfxStyleFamily::Para))); + DBG_ASSERT(pSheet, "Stylesheet missing"); + if (pSheet) + { + pObj->SetStyleSheet(pSheet, false); + SfxItemSet aAttr(mpView->GetDefaultAttr()); + aAttr.Put(pSheet->GetItemSet().Get(XATTR_FILLSTYLE)); + pObj->SetMergedItemSet(aAttr); + } + else + { + SfxItemSet aAttr(mpView->GetDefaultAttr()); + rAttr.Put(XFillStyleItem(drawing::FillStyle_NONE)); + pObj->SetMergedItemSet(aAttr); + } + } + else + { + // Creating an object with fill. + SdrPage* pThemePage = pPage; + if (pThemePage->TRG_HasMasterPage()) + { + pThemePage = &pThemePage->TRG_GetMasterPage(); + } + + svx::Theme* pTheme = pThemePage->getSdrPageProperties().GetTheme(); + if (pTheme) + { + // We construct an object on a page where the master page has a theme. Take the + // accent1 color from that theme, make sure it has priority over the shape's + // document-global style. + SfxItemSet aAttr(mpView->GetDefaultAttr()); + + aAttr.Put(XFillStyleItem(css::drawing::FillStyle_SOLID)); + + svx::ThemeColorType eColorType = svx::ThemeColorType::ACCENT1; + Color aColor = pTheme->GetColor(eColorType); + XFillColorItem aFillColorItem("", aColor); + aFillColorItem.GetThemeColor().SetThemeIndex(static_cast<sal_Int16>(eColorType)); + aAttr.Put(aFillColorItem); + + aAttr.Put(XLineStyleItem(css::drawing::LineStyle_SOLID)); + + // Line color is 50% darker than the fill color. + aColor.ApplyTintOrShade(-5000); + XLineColorItem aLineColorItem("", aColor); + // TODO no theme or theme effect for line colors yet. + aAttr.Put(aLineColorItem); + + pObj->SetMergedItemSet(aAttr); + } + } + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuconuno.cxx b/sd/source/ui/func/fuconuno.cxx new file mode 100644 index 000000000..afa4523c6 --- /dev/null +++ b/sd/source/ui/func/fuconuno.cxx @@ -0,0 +1,150 @@ +/* -*- 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 <fuconuno.hxx> +#include <rtl/ustring.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/request.hxx> +#include <svl/intitem.hxx> +#include <svx/svxids.hrc> +#include <vcl/ptrstyle.hxx> + +#include <ViewShell.hxx> +#include <View.hxx> +#include <Window.hxx> +#include <ViewShellBase.hxx> +#include <ToolBarManager.hxx> +#include <unokywds.hxx> + + +namespace sd { + + +FuConstructUnoControl::FuConstructUnoControl ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuConstruct(pViewSh, pWin, pView, pDoc, rReq) + , nInventor(SdrInventor::Unknown) + , nIdentifier(SdrObjKind::NONE) +{ +} + +rtl::Reference<FuPoor> FuConstructUnoControl::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, bool bPermanent ) +{ + FuConstructUnoControl* pFunc; + rtl::Reference<FuPoor> xFunc( pFunc = new FuConstructUnoControl( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + pFunc->SetPermanent(bPermanent); + return xFunc; +} + +void FuConstructUnoControl::DoExecute( SfxRequest& rReq ) +{ + FuConstruct::DoExecute( rReq ); + + const SfxUInt32Item* pInventorItem = rReq.GetArg<SfxUInt32Item>(SID_FM_CONTROL_INVENTOR); + const SfxUInt16Item* pIdentifierItem = rReq.GetArg<SfxUInt16Item>(SID_FM_CONTROL_IDENTIFIER); + if( pInventorItem ) + nInventor = static_cast<SdrInventor>(pInventorItem->GetValue()); + if( pIdentifierItem ) + nIdentifier = static_cast<SdrObjKind>(pIdentifierItem->GetValue()); + + mpViewShell->GetViewShellBase().GetToolBarManager()->SetToolBar( + ToolBarManager::ToolBarGroup::Function, + ToolBarManager::msDrawingObjectToolBar); +} + +bool FuConstructUnoControl::MouseButtonDown(const MouseEvent& rMEvt) +{ + bool bReturn = FuConstruct::MouseButtonDown(rMEvt); + + if ( rMEvt.IsLeft() && !mpView->IsAction() ) + { + Point aPnt( mpWindow->PixelToLogic( rMEvt.GetPosPixel() ) ); + mpWindow->CaptureMouse(); + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + mpView->BegCreateObj(aPnt, nullptr, nDrgLog); + bReturn = true; + } + return bReturn; +} + +bool FuConstructUnoControl::MouseButtonUp(const MouseEvent& rMEvt) +{ + bool bReturn = false; + + if ( mpView->IsCreateObj() && rMEvt.IsLeft() ) + { + mpView->EndCreateObj(SdrCreateCmd::ForceEnd); + bReturn = true; + } + + bReturn = (FuConstruct::MouseButtonUp(rMEvt) || bReturn); + + if (!bPermanent) + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + + return bReturn; +} + +void FuConstructUnoControl::Activate() +{ + mpView->SetCurrentObj( nIdentifier, nInventor ); + + aNewPointer = PointerStyle::DrawRect; + aOldPointer = mpWindow->GetPointer(); + mpWindow->SetPointer( aNewPointer ); + + aOldLayer = mpView->GetActiveLayer(); + mpView->SetActiveLayer(sUNO_LayerName_controls); + + FuConstruct::Activate(); +} + +void FuConstructUnoControl::Deactivate() +{ + FuConstruct::Deactivate(); + mpView->SetActiveLayer( aOldLayer ); + mpWindow->SetPointer( aOldPointer ); +} + +SdrObjectUniquePtr FuConstructUnoControl::CreateDefaultObject(const sal_uInt16, const ::tools::Rectangle& rRectangle) +{ + // case SID_FM_CREATE_CONTROL: + + SdrObjectUniquePtr pObj(SdrObjFactory::MakeNewObject( + mpView->getSdrModelFromSdrView(), + mpView->GetCurrentObjInventor(), + mpView->GetCurrentObjIdentifier())); + + if(pObj) + { + pObj->SetLogicRect(rRectangle); + } + + return pObj; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fucopy.cxx b/sd/source/ui/func/fucopy.cxx new file mode 100644 index 000000000..99e5f7b87 --- /dev/null +++ b/sd/source/ui/func/fucopy.cxx @@ -0,0 +1,288 @@ +/* -*- 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 <fucopy.hxx> +#include <sfx2/progress.hxx> +#include <svl/intitem.hxx> + +#include <sdattr.hrc> +#include <sdresid.hxx> +#include <strings.hrc> +#include <ViewShell.hxx> +#include <View.hxx> +#include <drawdoc.hxx> +#include <DrawDocShell.hxx> +#include <svx/svdobj.hxx> +#include <svx/xcolit.hxx> +#include <svx/xflclit.hxx> +#include <svx/xdef.hxx> +#include <svx/xfillit0.hxx> +#include <svx/sdangitm.hxx> +#include <sfx2/request.hxx> +#include <sdabstdlg.hxx> +#include <memory> + +using namespace com::sun::star; + +namespace sd { + + +FuCopy::FuCopy ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuCopy::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuCopy( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuCopy::DoExecute( SfxRequest& rReq ) +{ + if( !mpView->AreObjectsMarked() ) + return; + + // Undo + OUString aString = mpView->GetDescriptionOfMarkedObjects() + + " " + SdResId( STR_UNDO_COPYOBJECTS ); + mpView->BegUndo( aString ); + + const SfxItemSet* pArgs = rReq.GetArgs(); + + if( !pArgs ) + { + SfxItemSetFixed<ATTR_COPY_START, ATTR_COPY_END> aSet( mpViewShell->GetPool() ); + + // indicate color attribute + SfxItemSet aAttr( mpDoc->GetPool() ); + mpView->GetAttributes( aAttr ); + + if( const XFillStyleItem* pFillStyleItem = aAttr.GetItemIfSet( XATTR_FILLSTYLE ) ) + { + drawing::FillStyle eStyle = pFillStyleItem->GetValue(); + + const XFillColorItem* pFillColorItem; + if( eStyle == drawing::FillStyle_SOLID && + (pFillColorItem = aAttr.GetItemIfSet( XATTR_FILLCOLOR )) ) + { + XColorItem aXColorItem( ATTR_COPY_START_COLOR, pFillColorItem->GetName(), + pFillColorItem->GetColorValue() ); + aSet.Put( aXColorItem ); + + } + } + + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractCopyDlg> pDlg(pFact->CreateCopyDlg(mpViewShell->GetFrameWeld(), aSet, mpView )); + + sal_uInt16 nResult = pDlg->Execute(); + + switch( nResult ) + { + case RET_OK: + pDlg->GetAttr( aSet ); + rReq.Done( aSet ); + pArgs = rReq.GetArgs(); + break; + + default: + { + pDlg.disposeAndClear(); + mpView->EndUndo(); + return; // Cancel + } + } + } + + ::tools::Rectangle aRect; + sal_Int32 lWidth = 0, lHeight = 0, lSizeX = 0, lSizeY = 0; + Degree100 lAngle(0); + sal_uInt16 nNumber = 0; + Color aStartColor, aEndColor; + bool bColor = false; + + if (pArgs) + { + // Count + if( const SfxUInt16Item* pPoolItem = pArgs->GetItemIfSet( ATTR_COPY_NUMBER ) ) + nNumber = pPoolItem->GetValue(); + + // translation + if( const SfxInt32Item* pPoolItem = pArgs->GetItemIfSet( ATTR_COPY_MOVE_X ) ) + lSizeX = pPoolItem->GetValue(); + if( const SfxInt32Item* pPoolItem = pArgs->GetItemIfSet( ATTR_COPY_MOVE_Y ) ) + lSizeY = pPoolItem->GetValue(); + if( const SdrAngleItem* pPoolItem = pArgs->GetItemIfSet( ATTR_COPY_ANGLE ) ) + lAngle = pPoolItem->GetValue(); + + // scale + if( const SfxInt32Item* pPoolItem = pArgs->GetItemIfSet( ATTR_COPY_WIDTH ) ) + lWidth = pPoolItem->GetValue(); + if( const SfxInt32Item* pPoolItem = pArgs->GetItemIfSet( ATTR_COPY_HEIGHT ) ) + lHeight = pPoolItem->GetValue(); + + // start/end color + if( const XColorItem* pPoolItem = pArgs->GetItemIfSet( ATTR_COPY_START_COLOR ) ) + { + aStartColor = pPoolItem->GetColorValue(); + bColor = true; + } + if( const XColorItem* pPoolItem = pArgs->GetItemIfSet( ATTR_COPY_END_COLOR ) ) + { + aEndColor = pPoolItem->GetColorValue(); + if( aStartColor == aEndColor ) + bColor = false; + } + } + + // remove handles + //HMHmpView->HideMarkHdl(); + + std::unique_ptr<SfxProgress> pProgress; + bool bWaiting = false; + + if( nNumber > 1 ) + { + OUString aStr = SdResId( STR_OBJECTS ) + + " " + SdResId( STR_UNDO_COPYOBJECTS ); + + pProgress.reset(new SfxProgress( mpDocSh, aStr, nNumber )); + mpDocSh->SetWaitCursor( true ); + bWaiting = true; + } + + const SdrMarkList aMarkList( mpView->GetMarkedObjectList() ); + const size_t nMarkCount = aMarkList.GetMarkCount(); + SdrObject* pObj = nullptr; + + // calculate number of possible copies + aRect = mpView->GetAllMarkedRect(); + + if( lWidth < 0 ) + { + ::tools::Long nTmp = ( aRect.Right() - aRect.Left() ) / -lWidth; + nNumber = static_cast<sal_uInt16>(std::min( nTmp, static_cast<::tools::Long>(nNumber) )); + } + + if( lHeight < 0 ) + { + ::tools::Long nTmp = ( aRect.Bottom() - aRect.Top() ) / -lHeight; + nNumber = static_cast<sal_uInt16>(std::min( nTmp, static_cast<::tools::Long>(nNumber) )); + } + + for( sal_uInt16 i = 1; i <= nNumber; i++ ) + { + if( pProgress ) + pProgress->SetState( i ); + + aRect = mpView->GetAllMarkedRect(); + + if( ( 1 == i ) && bColor ) + { + SfxItemSetFixed<XATTR_FILLSTYLE, XATTR_FILLCOLOR> aNewSet( mpViewShell->GetPool() ); + aNewSet.Put( XFillStyleItem( drawing::FillStyle_SOLID ) ); + aNewSet.Put( XFillColorItem( OUString(), aStartColor ) ); + mpView->SetAttributes( aNewSet ); + } + + // make a copy of selected objects + mpView->CopyMarked(); + + // get newly selected objects + SdrMarkList aCopyMarkList( mpView->GetMarkedObjectList() ); + const size_t nCopyMarkCount = aMarkList.GetMarkCount(); + + // set protection flags at marked copies to null + for( size_t j = 0; j < nCopyMarkCount; ++j ) + { + pObj = aCopyMarkList.GetMark( j )->GetMarkedSdrObj(); + + if( pObj ) + { + pObj->SetMoveProtect( false ); + pObj->SetResizeProtect( false ); + } + } + + Fraction aWidth( aRect.Right() - aRect.Left() + lWidth, aRect.Right() - aRect.Left() ); + Fraction aHeight( aRect.Bottom() - aRect.Top() + lHeight, aRect.Bottom() - aRect.Top() ); + + if( mpView->IsResizeAllowed() ) + mpView->ResizeAllMarked( aRect.TopLeft(), aWidth, aHeight ); + + if( mpView->IsRotateAllowed() ) + mpView->RotateAllMarked( aRect.Center(), lAngle ); + + if( mpView->IsMoveAllowed() ) + mpView->MoveAllMarked( Size( lSizeX, lSizeY ) ); + + // set protection flags at marked copies to original values + if( nMarkCount == nCopyMarkCount ) + { + for( size_t j = 0; j < nMarkCount; ++j ) + { + SdrObject* pSrcObj = aMarkList.GetMark( j )->GetMarkedSdrObj(); + SdrObject* pDstObj = aCopyMarkList.GetMark( j )->GetMarkedSdrObj(); + + if( pSrcObj && pDstObj && + ( pSrcObj->GetObjInventor() == pDstObj->GetObjInventor() ) && + ( pSrcObj->GetObjIdentifier() == pDstObj->GetObjIdentifier() ) ) + { + pDstObj->SetMoveProtect( pSrcObj->IsMoveProtect() ); + pDstObj->SetResizeProtect( pSrcObj->IsResizeProtect() ); + } + } + } + + if( bColor ) + { + // probably room for optimizations, but may can lead to rounding errors + sal_uInt8 nRed = aStartColor.GetRed() + static_cast<sal_uInt8>( ( static_cast<::tools::Long>(aEndColor.GetRed()) - static_cast<::tools::Long>(aStartColor.GetRed()) ) * static_cast<::tools::Long>(i) / static_cast<::tools::Long>(nNumber) ); + sal_uInt8 nGreen = aStartColor.GetGreen() + static_cast<sal_uInt8>( ( static_cast<::tools::Long>(aEndColor.GetGreen()) - static_cast<::tools::Long>(aStartColor.GetGreen()) ) * static_cast<::tools::Long>(i) / static_cast<::tools::Long>(nNumber) ); + sal_uInt8 nBlue = aStartColor.GetBlue() + static_cast<sal_uInt8>( ( static_cast<::tools::Long>(aEndColor.GetBlue()) - static_cast<::tools::Long>(aStartColor.GetBlue()) ) * static_cast<::tools::Long>(i) / static_cast<::tools::Long>(nNumber) ); + Color aNewColor( nRed, nGreen, nBlue ); + SfxItemSetFixed<XATTR_FILLSTYLE, XATTR_FILLCOLOR> aNewSet( mpViewShell->GetPool() ); + aNewSet.Put( XFillStyleItem( drawing::FillStyle_SOLID ) ); + aNewSet.Put( XFillColorItem( OUString(), aNewColor ) ); + mpView->SetAttributes( aNewSet ); + } + } + + pProgress.reset(); + + if ( bWaiting ) + mpDocSh->SetWaitCursor( false ); + + // show handles + mpView->AdjustMarkHdl(); //HMH sal_True ); + //HMHpView->ShowMarkHdl(); + + mpView->EndUndo(); +} + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fucushow.cxx b/sd/source/ui/func/fucushow.cxx new file mode 100644 index 000000000..eb3b12211 --- /dev/null +++ b/sd/source/ui/func/fucushow.cxx @@ -0,0 +1,91 @@ +/* -*- 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 <fucushow.hxx> + +#include <svx/svxids.hrc> + +#include <ViewShell.hxx> +#include <Window.hxx> +#include <drawdoc.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> + +#include <sdabstdlg.hxx> + +namespace sd { + + +FuCustomShowDlg::FuCustomShowDlg ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor( pViewSh, pWin, pView, pDoc, rReq ) +{ +} + +rtl::Reference<FuPoor> FuCustomShowDlg::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuCustomShowDlg( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuCustomShowDlg::DoExecute( SfxRequest& ) +{ + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + vcl::Window* pWin = mpViewShell->GetActiveWindow(); + ScopedVclPtr<AbstractSdCustomShowDlg> pDlg( pFact->CreateSdCustomShowDlg(pWin ? pWin->GetFrameWeld() : nullptr, *mpDoc) ); + sal_uInt16 nRet = pDlg->Execute(); + mpDoc->SetChanged(); + sd::PresentationSettings& rSettings = mpDoc->getPresentationSettings(); + + if( nRet == RET_YES ) + { + // If the custom show is not set by default + if (!rSettings.mbCustomShow) + { + rSettings.mbStartCustomShow = true; + rSettings.mbCustomShow = pDlg->IsCustomShow(); + } + + mpViewShell->SetStartShowWithDialog(true); + + mpViewShell->GetViewFrame()->GetDispatcher()->Execute( SID_PRESENTATION, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD ); + } + if (nRet == RET_OK) + { + if (mpDoc->GetCustomShowList()) + { + if (!pDlg->IsCustomShow()) + { + rSettings.mbCustomShow = false; + rSettings.mbAll = true; + } + } + } + pDlg.disposeAndClear(); +} + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fudraw.cxx b/sd/source/ui/func/fudraw.cxx new file mode 100644 index 000000000..8beb753f6 --- /dev/null +++ b/sd/source/ui/func/fudraw.cxx @@ -0,0 +1,820 @@ +/* -*- 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 <sal/config.h> + +#include <vcl/svapp.hxx> +#include <vcl/ptrstyle.hxx> +#include <editeng/flditem.hxx> +#include <svx/svdogrp.hxx> +#include <tools/urlobj.hxx> +#include <vcl/help.hxx> +#include <svx/bmpmask.hxx> +#include <svx/svdotext.hxx> +#include <svx/ImageMapInfo.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/sfxhelp.hxx> +#include <svx/svdpagv.hxx> +#include <vcl/imapobj.hxx> +#include <svx/svxids.hrc> +#include <svx/obj3d.hxx> +#include <svx/scene3d.hxx> +#include <sfx2/viewfrm.hxx> + +#include <strings.hrc> + + +#include <sdmod.hxx> +#include <fudraw.hxx> +#include <ViewShell.hxx> +#include <FrameView.hxx> +#include <View.hxx> +#include <Window.hxx> +#include <drawdoc.hxx> +#include <DrawDocShell.hxx> +#include <sdresid.hxx> +#include <fusel.hxx> +#include <vcl/weld.hxx> +#include <svx/sdrhittesthelper.hxx> + +using namespace ::com::sun::star; + +namespace sd { + + +/** + * Base-class for all drawmodul-specific functions + */ +FuDraw::FuDraw(ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, + SdDrawDocument* pDoc, SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) + , aNewPointer(PointerStyle::Arrow) + , aOldPointer(PointerStyle::Arrow) + , bMBDown(false) + , bDragHelpLine(false) + , nHelpLine(0) + , bPermanent(false) +{ +} + +FuDraw::~FuDraw() +{ + mpView->BrkAction(); +} + + +/** + * Code shared by MouseButtonDown and MouseMove + */ +void FuDraw::DoModifiers(const MouseEvent& rMEvt, bool bSnapModPressed) +{ + FrameView* pFrameView = mpViewShell->GetFrameView(); + bool bGridSnap = pFrameView->IsGridSnap(); + bGridSnap = (bSnapModPressed != bGridSnap); + + if (mpView->IsGridSnap() != bGridSnap) + mpView->SetGridSnap(bGridSnap); + + bool bBordSnap = pFrameView->IsBordSnap(); + bBordSnap = (bSnapModPressed != bBordSnap); + + if (mpView->IsBordSnap() != bBordSnap) + mpView->SetBordSnap(bBordSnap); + + bool bHlplSnap = pFrameView->IsHlplSnap(); + bHlplSnap = (bSnapModPressed != bHlplSnap); + + if (mpView->IsHlplSnap() != bHlplSnap) + mpView->SetHlplSnap(bHlplSnap); + + bool bOFrmSnap = pFrameView->IsOFrmSnap(); + bOFrmSnap = (bSnapModPressed != bOFrmSnap); + + if (mpView->IsOFrmSnap() != bOFrmSnap) + mpView->SetOFrmSnap(bOFrmSnap); + + bool bOPntSnap = pFrameView->IsOPntSnap(); + bOPntSnap = (bSnapModPressed != bOPntSnap); + + if (mpView->IsOPntSnap() != bOPntSnap) + mpView->SetOPntSnap(bOPntSnap); + + bool bOConSnap = pFrameView->IsOConSnap(); + bOConSnap = (bSnapModPressed != bOConSnap); + + if (mpView->IsOConSnap() != bOConSnap) + mpView->SetOConSnap(bOConSnap); + + bool bAngleSnap = rMEvt.IsShift() == !pFrameView->IsAngleSnapEnabled(); + + if (mpView->IsAngleSnapEnabled() != bAngleSnap) + mpView->SetAngleSnapEnabled(bAngleSnap); + + bool bCenter = rMEvt.IsMod2(); + + if ( mpView->IsCreate1stPointAsCenter() != bCenter || + mpView->IsResizeAtCenter() != bCenter ) + { + mpView->SetCreate1stPointAsCenter(bCenter); + mpView->SetResizeAtCenter(bCenter); + } +} + + +bool FuDraw::MouseButtonDown(const MouseEvent& rMEvt) +{ + // remember button state for creation of own MouseEvents + SetMouseButtonCode(rMEvt.GetButtons()); + + bool bReturn = false; + bDragHelpLine = false; + aMDPos = mpWindow->PixelToLogic( rMEvt.GetPosPixel() ); + + if ( rMEvt.IsLeft() ) + { + FrameView* pFrameView = mpViewShell->GetFrameView(); + + bool bOrtho = false; + + bool bRestricted = true; + + if (mpView->IsDragObj()) + { + // object is dragged (move, resize,...) + const SdrHdl* pHdl = mpView->GetDragStat().GetHdl(); + + if (!pHdl || (!pHdl->IsCornerHdl() && !pHdl->IsVertexHdl())) + { + // Move + bRestricted = false; + } + } + + // #i33136# + if(bRestricted && doConstructOrthogonal()) + { + // Restrict movement: + // rectangle->square, ellipse->circle, etc. + bOrtho = !rMEvt.IsShift(); + } + else + { + bOrtho = rMEvt.IsShift() != pFrameView->IsOrtho(); + } + if (!mpView->IsSnapEnabled()) + mpView->SetSnapEnabled(true); + + bool bSnapModPressed = rMEvt.IsMod1(); + if (mpView->IsOrtho() != bOrtho) + mpView->SetOrtho(bOrtho); + + DoModifiers(rMEvt, bSnapModPressed); + + SdrPageView* pPV = nullptr; + sal_uInt16 nHitLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(HITPIX,0)).Width() ); + + // look only for HelpLines when they are visible (!) + bool bHelpLine(false); + if(mpView->IsHlplVisible()) + bHelpLine = mpView->PickHelpLine(aMDPos, nHitLog, *mpWindow->GetOutDev(), nHelpLine, pPV); + bool bHitHdl = (mpView->PickHandle(aMDPos) != nullptr); + + if ( bHelpLine + && !mpView->IsCreateObj() + && ((mpView->GetEditMode() == SdrViewEditMode::Edit && !bHitHdl) || (rMEvt.IsShift() && bSnapModPressed)) ) + { + mpWindow->CaptureMouse(); + mpView->BegDragHelpLine(nHelpLine, pPV); + bDragHelpLine = mpView->IsDragHelpLine(); + bReturn = true; + } + } + ForcePointer(&rMEvt); + + return bReturn; +} + +bool FuDraw::MouseMove(const MouseEvent& rMEvt) +{ + FrameView* pFrameView = mpViewShell->GetFrameView(); + Point aPos = mpWindow->PixelToLogic( rMEvt.GetPosPixel() ); + + bool bOrtho = false; + bool bRestricted = true; + + if (mpView->IsDragObj()) + { + // object is dragged (move, resize, ...) + const SdrHdl* pHdl = mpView->GetDragStat().GetHdl(); + + if (!pHdl || (!pHdl->IsCornerHdl() && !pHdl->IsVertexHdl())) + { + // Move + bRestricted = false; + } + } + + if (mpView->IsAction()) + { + // #i33136# and fdo#88339 + if(bRestricted && doConstructOrthogonal()) + { + // Scale proportionally by default: + // rectangle->square, ellipse->circle, images, etc. + bOrtho = !rMEvt.IsShift(); + } + else + { + bOrtho = rMEvt.IsShift() != pFrameView->IsOrtho(); + } + + bool bSnapModPressed = rMEvt.IsMod2(); + mpView->SetDragWithCopy(rMEvt.IsMod1() && pFrameView->IsDragWithCopy()); + + if (mpView->IsOrtho() != bOrtho) + mpView->SetOrtho(bOrtho); + DoModifiers(rMEvt, bSnapModPressed); + + + if ( mpView->IsDragHelpLine() ) + mpView->MovDragHelpLine(aPos); + } + + bool bReturn = mpView->MouseMove(rMEvt, mpWindow->GetOutDev()); + + if (mpView->IsAction()) + { + // Because the flag set back if necessary in MouseMove + if (mpView->IsOrtho() != bOrtho) + mpView->SetOrtho(bOrtho); + } + + ForcePointer(&rMEvt); + + return bReturn; +} + +bool FuDraw::MouseButtonUp(const MouseEvent& rMEvt) +{ + if (mpView && mpView->IsDragHelpLine()) + mpView->EndDragHelpLine(); + + if ( bDragHelpLine ) + { + ::tools::Rectangle aOutputArea(Point(0,0), mpWindow->GetOutputSizePixel()); + + if (mpView && !aOutputArea.Contains(rMEvt.GetPosPixel())) + mpView->GetSdrPageView()->DeleteHelpLine(nHelpLine); + + mpWindow->ReleaseMouse(); + } + + if (mpView) + { + FrameView* pFrameView = mpViewShell->GetFrameView(); + mpView->SetOrtho( pFrameView->IsOrtho() ); + mpView->SetAngleSnapEnabled( pFrameView->IsAngleSnapEnabled() ); + mpView->SetSnapEnabled(true); + mpView->SetCreate1stPointAsCenter(false); + mpView->SetResizeAtCenter(false); + mpView->SetDragWithCopy(pFrameView->IsDragWithCopy()); + mpView->SetGridSnap(pFrameView->IsGridSnap()); + mpView->SetBordSnap(pFrameView->IsBordSnap()); + mpView->SetHlplSnap(pFrameView->IsHlplSnap()); + mpView->SetOFrmSnap(pFrameView->IsOFrmSnap()); + mpView->SetOPntSnap(pFrameView->IsOPntSnap()); + mpView->SetOConSnap(pFrameView->IsOConSnap()); + } + + bIsInDragMode = false; + ForcePointer(&rMEvt); + FuPoor::MouseButtonUp(rMEvt); + + return false; +} + +/** + * Process keyboard input + * @returns sal_True if a KeyEvent is being processed, sal_False otherwise + */ +bool FuDraw::KeyInput(const KeyEvent& rKEvt) +{ + bool bReturn = false; + + switch ( rKEvt.GetKeyCode().GetCode() ) + { + case KEY_ESCAPE: + { + bReturn = FuDraw::cancel(); + } + break; + + case KEY_DELETE: + case KEY_BACKSPACE: + { + if (!mpDocSh->IsReadOnly()) + { + if (mpView->IsPresObjSelected(false, true, false, true)) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(mpWindow->GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + // wait-mousepointer while deleting object + weld::WaitObject aWait(mpViewShell->GetFrameWeld()); + // delete object + mpView->DeleteMarked(); + } + } + bReturn = true; + } + break; + + case KEY_TAB: + { + vcl::KeyCode aCode = rKEvt.GetKeyCode(); + + if ( !aCode.IsMod1() && !aCode.IsMod2() ) + { + // Moved next line which was a bugfix itself into + // the scope which really does the object selection travel + // and thus is allowed to call SelectionHasChanged(). + + // Switch to FuSelect. + mpViewShell->GetViewFrame()->GetDispatcher()->Execute( + SID_OBJECT_SELECT, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + + // changeover to the next object + if(!mpView->MarkNextObj( !aCode.IsShift() )) + { + //If there is only one object, don't do the UnmarkAllObj() & MarkNextObj(). + if ( mpView->HasMultipleMarkableObjects() && mpView->AreObjectsMarked() ) + { + // No next object: go over open end and get first from + // the other side + mpView->UnmarkAllObj(); + mpView->MarkNextObj(!aCode.IsShift()); + } + } + + if(mpView->AreObjectsMarked()) + mpView->MakeVisible(mpView->GetAllMarkedRect(), *mpWindow); + + bReturn = true; + } + } + break; + + case KEY_END: + { + vcl::KeyCode aCode = rKEvt.GetKeyCode(); + + if ( aCode.IsMod1() ) + { + // mark last object + mpView->UnmarkAllObj(); + mpView->MarkNextObj(); + + if(mpView->AreObjectsMarked()) + mpView->MakeVisible(mpView->GetAllMarkedRect(), *mpWindow); + + bReturn = true; + } + } + break; + + case KEY_HOME: + { + vcl::KeyCode aCode = rKEvt.GetKeyCode(); + + if ( aCode.IsMod1() ) + { + // mark first object + mpView->UnmarkAllObj(); + mpView->MarkNextObj(true); + + if(mpView->AreObjectsMarked()) + mpView->MakeVisible(mpView->GetAllMarkedRect(), *mpWindow); + + bReturn = true; + } + } + break; + + default: + break; + } + + if (!bReturn) + { + bReturn = FuPoor::KeyInput(rKEvt); + } + else + { + mpWindow->ReleaseMouse(); + } + + return bReturn; +} + +void FuDraw::Activate() +{ + FuPoor::Activate(); + ForcePointer(); +} + +/** + * Toggle mouse-pointer + */ +void FuDraw::ForcePointer(const MouseEvent* pMEvt) +{ + Point aPnt; + sal_uInt16 nModifier = 0; + bool bLeftDown = false; + bool bDefPointer = true; + + if (pMEvt) + { + aPnt = mpWindow->PixelToLogic(pMEvt->GetPosPixel()); + nModifier = pMEvt->GetModifier(); + bLeftDown = pMEvt->IsLeft(); + } + else + { + aPnt = mpWindow->PixelToLogic(mpWindow->GetPointerPosPixel()); + } + + if (mpView->IsDragObj()) + { + if (SD_MOD()->GetWaterCan() && !mpView->PickHandle(aPnt)) + { + // water can mode + bDefPointer = false; + mpWindow->SetPointer(PointerStyle::Fill); + } + } + else + { + SdrHdl* pHdl = mpView->PickHandle(aPnt); + + if (SD_MOD()->GetWaterCan() && !pHdl) + { + // water can mode + bDefPointer = false; + mpWindow->SetPointer(PointerStyle::Fill); + } + else if (!pHdl && + mpViewShell->GetViewFrame()->HasChildWindow(SvxBmpMaskChildWindow::GetChildWindowId())) + { + // pipette mode + SfxChildWindow* pWnd = mpViewShell->GetViewFrame()->GetChildWindow(SvxBmpMaskChildWindow::GetChildWindowId()); + SvxBmpMask* pMask = pWnd ? static_cast<SvxBmpMask*>(pWnd->GetWindow()) : nullptr; + if (pMask && pMask->IsEyedropping()) + { + bDefPointer = false; + mpWindow->SetPointer(PointerStyle::RefHand); + } + } + else if (!mpView->IsAction()) + { + SdrObject* pObj = nullptr; + SdrPageView* pPV = nullptr; + SdrViewEvent aVEvt; + SdrHitKind eHit = SdrHitKind::NONE; + SdrDragMode eDragMode = mpView->GetDragMode(); + + if (pMEvt) + { + eHit = mpView->PickAnything(*pMEvt, SdrMouseEventKind::MOVE, aVEvt); + } + + if ((eDragMode == SdrDragMode::Rotate) && (eHit == SdrHitKind::MarkedObject)) + { + // The goal of this request is show always the rotation arrow for 3D-objects at rotation mode + // Independent of the settings at Tools->Options->Draw "Objects always moveable" + // 2D-objects acquit in another way. Otherwise, the rotation of 3d-objects around any axes + // wouldn't be possible per default. + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + SdrObject* pObject = rMarkList.GetMark(0)->GetMarkedSdrObj(); + if ((dynamic_cast<const E3dObject* >(pObject) != nullptr) && (rMarkList.GetMarkCount() == 1)) + { + mpWindow->SetPointer(PointerStyle::Rotate); + bDefPointer = false; // Otherwise it'll be called Joe's routine and the mousepointer will reconfigurate again + } + } + + if (eHit == SdrHitKind::NONE) + { + // found nothing -> look after at the masterpage + pObj = mpView->PickObj(aPnt, mpView->getHitTolLog(), pPV, SdrSearchOptions::ALSOONMASTER); + } + else if (eHit == SdrHitKind::UnmarkedObject) + { + pObj = aVEvt.mpObj; + } + else if (eHit == SdrHitKind::TextEditObj && dynamic_cast< const FuSelection *>( this ) != nullptr) + { + SdrObjKind nSdrObjKind = aVEvt.mpObj->GetObjIdentifier(); + + if ( nSdrObjKind != SdrObjKind::Text && + nSdrObjKind != SdrObjKind::TitleText && + nSdrObjKind != SdrObjKind::OutlineText && + aVEvt.mpObj->IsEmptyPresObj() ) + { + pObj = nullptr; + bDefPointer = false; + mpWindow->SetPointer(PointerStyle::Arrow); + } + } + + if (pObj && pMEvt && !pMEvt->IsMod2() + && dynamic_cast<const FuSelection*>(this) != nullptr) + { + // test for ImageMap + bDefPointer = !SetPointer(pObj, aPnt); + + if (bDefPointer + && (dynamic_cast<const SdrObjGroup*>(pObj) != nullptr + || dynamic_cast<const E3dScene*>(pObj) != nullptr)) + { + // take a glance into the group + pObj = mpView->PickObj(aPnt, mpView->getHitTolLog(), pPV, + SdrSearchOptions::ALSOONMASTER | SdrSearchOptions::DEEP); + if (pObj) + bDefPointer = !SetPointer(pObj, aPnt); + } + } + } + } + + if (bDefPointer) + { + mpWindow->SetPointer(mpView->GetPreferredPointer( + aPnt, mpWindow->GetOutDev(), nModifier, bLeftDown)); + } +} + +/** + * Set cursor to pointer when in clickable area of an ImageMap + * + * @return True when pointer was set + */ +bool FuDraw::SetPointer(const SdrObject* pObj, const Point& rPos) +{ + bool bImageMapInfo = SvxIMapInfo::GetIMapInfo(pObj) != nullptr; + + if (!bImageMapInfo) + return false; + + const SdrLayerIDSet* pVisiLayer = &mpView->GetSdrPageView()->GetVisibleLayers(); + sal_uInt16 nHitLog(sal_uInt16(mpWindow->PixelToLogic(Size(HITPIX, 0)).Width())); + ::tools::Long n2HitLog(nHitLog * 2); + Point aHitPosR(rPos); + Point aHitPosL(rPos); + Point aHitPosT(rPos); + Point aHitPosB(rPos); + + aHitPosR.AdjustX(n2HitLog); + aHitPosL.AdjustX(-n2HitLog); + aHitPosT.AdjustY(n2HitLog); + aHitPosB.AdjustY(-n2HitLog); + + if (!pObj->IsClosedObj() + || (SdrObjectPrimitiveHit(*pObj, aHitPosR, nHitLog, *mpView->GetSdrPageView(), pVisiLayer, + false) + && SdrObjectPrimitiveHit(*pObj, aHitPosL, nHitLog, *mpView->GetSdrPageView(), + pVisiLayer, false) + && SdrObjectPrimitiveHit(*pObj, aHitPosT, nHitLog, *mpView->GetSdrPageView(), + pVisiLayer, false) + && SdrObjectPrimitiveHit(*pObj, aHitPosB, nHitLog, *mpView->GetSdrPageView(), + pVisiLayer, false))) + { + // hit inside the object (without margin) or open object + if (SvxIMapInfo::GetHitIMapObject(pObj, rPos)) + { + mpWindow->SetPointer(PointerStyle::RefHand); + return true; + } + } + + return false; +} + +/** + * Response of doubleclick + */ +void FuDraw::DoubleClick(const MouseEvent& rMEvt) +{ + sal_uInt16 nHitLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(HITPIX,0)).Width() ); + + if ( mpView->AreObjectsMarked() ) + { + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + if (rMarkList.GetMarkCount() == 1) + { + SdrMark* pMark = rMarkList.GetMark(0); + SdrObject* pObj = pMark->GetMarkedSdrObj(); + + SdrInventor nInv = pObj->GetObjInventor(); + SdrObjKind nSdrObjKind = pObj->GetObjIdentifier(); + + if (nInv == SdrInventor::Default && nSdrObjKind == SdrObjKind::OLE2) + { + // activate OLE-object + SfxInt16Item aItem(SID_OBJECT, 0); + mpViewShell->GetViewFrame()-> + GetDispatcher()->ExecuteList(SID_OBJECT, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aItem }); + } + else if (nInv == SdrInventor::Default && nSdrObjKind == SdrObjKind::Graphic && pObj->IsEmptyPresObj() ) + { + mpViewShell->GetViewFrame()-> + GetDispatcher()->Execute( SID_INSERT_GRAPHIC, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD ); + } + else if ( ( dynamic_cast< const SdrTextObj *>( pObj ) != nullptr || dynamic_cast< const SdrObjGroup *>( pObj ) != nullptr ) && + !SD_MOD()->GetWaterCan() && + mpViewShell->GetFrameView()->IsDoubleClickTextEdit() && + !mpDocSh->IsReadOnly()) + { + SfxUInt16Item aItem(SID_TEXTEDIT, 2); + mpViewShell->GetViewFrame()->GetDispatcher()->ExecuteList( + SID_TEXTEDIT, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aItem }); + } + else if (nInv == SdrInventor::Default && nSdrObjKind == SdrObjKind::Group) + { + // hit group -> select subobject + mpView->UnMarkAll(); + mpView->MarkObj(aMDPos, nHitLog, rMEvt.IsShift(), true); + } + } + } + else + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); +} + +bool FuDraw::RequestHelp(const HelpEvent& rHEvt) +{ + bool bReturn = false; + + if (Help::IsBalloonHelpEnabled() || Help::IsQuickHelpEnabled()) + { + SdrViewEvent aVEvt; + + MouseEvent aMEvt(mpWindow->GetPointerPosPixel(), 1, MouseEventModifiers::NONE, MOUSE_LEFT); + + SdrHitKind eHit = mpView->PickAnything(aMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + + SdrObject* pObj = aVEvt.mpObj; + + if (eHit != SdrHitKind::NONE && pObj != nullptr) + { + Point aPosPixel = rHEvt.GetMousePosPixel(); + + bReturn = SetHelpText(pObj, aPosPixel, aVEvt); + + if (!bReturn && (dynamic_cast< const SdrObjGroup *>( pObj ) != nullptr || dynamic_cast< const E3dScene* >(pObj) != nullptr)) + { + // take a glance into the group + SdrPageView* pPV = nullptr; + + Point aPos(mpWindow->PixelToLogic(mpWindow->ScreenToOutputPixel(aPosPixel))); + + pObj = mpView->PickObj(aPos, mpView->getHitTolLog(), pPV, SdrSearchOptions::ALSOONMASTER | SdrSearchOptions::DEEP); + if (pObj) + bReturn = SetHelpText(pObj, aPosPixel, aVEvt); + } + } + } + + if (!bReturn) + { + bReturn = FuPoor::RequestHelp(rHEvt); + } + + if (!bReturn) + bReturn = mpView->RequestHelp(rHEvt); + + return bReturn; +} + +bool FuDraw::SetHelpText(const SdrObject* pObj, const Point& rPosPixel, const SdrViewEvent& rVEvt) +{ + OUString aHelpText; + Point aPos(mpWindow->PixelToLogic(mpWindow->ScreenToOutputPixel(rPosPixel))); + IMapObject* pIMapObj = SvxIMapInfo::GetHitIMapObject(pObj, aPos); + + if (!rVEvt.mpURLField && !pIMapObj) + return false; + + OUString aURL; + if (rVEvt.mpURLField) + aURL = INetURLObject::decode(rVEvt.mpURLField->GetURL(), + INetURLObject::DecodeMechanism::WithCharset); + else if (pIMapObj) + { + aURL = pIMapObj->GetAltText() + + " (" + + INetURLObject::decode(pIMapObj->GetURL(), + INetURLObject::DecodeMechanism::WithCharset) + + ")"; + } + else + return false; + + aHelpText = SfxHelp::GetURLHelpText(aURL); + + if (aHelpText.isEmpty()) + return false; + + ::tools::Rectangle aLogicPix = mpWindow->LogicToPixel(pObj->GetLogicRect()); + ::tools::Rectangle aScreenRect(mpWindow->OutputToScreenPixel(aLogicPix.TopLeft()), + mpWindow->OutputToScreenPixel(aLogicPix.BottomRight())); + + if (Help::IsBalloonHelpEnabled()) + Help::ShowBalloon( static_cast<vcl::Window*>(mpWindow), rPosPixel, aScreenRect, aHelpText); + else if (Help::IsQuickHelpEnabled()) + Help::ShowQuickHelp( static_cast<vcl::Window*>(mpWindow), aScreenRect, aHelpText); + + return true; +} + +/** is called when the current function should be aborted. <p> + This is used when a function gets a KEY_ESCAPE but can also + be called directly. + + @returns true if an active function was aborted +*/ +bool FuDraw::cancel() +{ + bool bReturn = false; + + if ( mpView->IsAction() ) + { + mpView->BrkAction(); + bReturn = true; + } + else if ( mpView->IsTextEdit() ) + { + mpView->SdrEndTextEdit(); + bReturn = true; + + SfxBindings& rBindings = mpViewShell->GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_DEC_INDENT ); + rBindings.Invalidate( SID_INC_INDENT ); + rBindings.Invalidate( SID_PARASPACE_INCREASE ); + rBindings.Invalidate( SID_PARASPACE_DECREASE ); + } + else if ( mpView->AreObjectsMarked() ) + { + const SdrHdlList& rHdlList = mpView->GetHdlList(); + SdrHdl* pHdl = rHdlList.GetFocusHdl(); + + if(pHdl) + { + const_cast<SdrHdlList&>(rHdlList).ResetFocusHdl(); + } + else + { + mpView->UnmarkAll(); + } + + // Switch to FuSelect. + mpViewShell->GetViewFrame()->GetDispatcher()->Execute( + SID_OBJECT_SELECT, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + + bReturn = true; + } + + return bReturn; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fudspord.cxx b/sd/source/ui/func/fudspord.cxx new file mode 100644 index 000000000..f129c523c --- /dev/null +++ b/sd/source/ui/func/fudspord.cxx @@ -0,0 +1,131 @@ +/* -*- 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 <fudspord.hxx> + +#include <vcl/ptrstyle.hxx> + +#include <app.hrc> +#include <fupoor.hxx> +#include <ViewShell.hxx> +#include <View.hxx> +#include <Window.hxx> + +namespace sd { + + +FuDisplayOrder::FuDisplayOrder( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq) +: FuPoor(pViewSh, pWin, pView, pDoc, rReq) +, maPtr(PointerStyle::Arrow) +, mpRefObj(nullptr) +{ +} + +FuDisplayOrder::~FuDisplayOrder() +{ +} + +void FuDisplayOrder::implClearOverlay() +{ + mpOverlay.reset(); +} + +rtl::Reference<FuPoor> FuDisplayOrder::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuDisplayOrder( pViewSh, pWin, pView, pDoc, rReq ) ); + return xFunc; +} + +bool FuDisplayOrder::MouseButtonDown(const MouseEvent& rMEvt) +{ + // remember button state for creation of own MouseEvents + SetMouseButtonCode(rMEvt.GetButtons()); + + return true; +} + +bool FuDisplayOrder::MouseMove(const MouseEvent& rMEvt) +{ + SdrPageView* pPV; + Point aPnt( mpWindow->PixelToLogic( rMEvt.GetPosPixel() ) ); + + SdrObject* pPickObj = mpView->PickObj(aPnt, mpView->getHitTolLog(), pPV); + if (pPickObj) + { + if (mpRefObj != pPickObj) + { + // delete current overlay + implClearOverlay(); + + // create new one + mpOverlay.reset( new SdrDropMarkerOverlay(*mpView, *pPickObj) ); + + // remember referenced object + mpRefObj = pPickObj; + } + } + else + { + mpRefObj = nullptr; + implClearOverlay(); + } + + return true; +} + +bool FuDisplayOrder::MouseButtonUp(const MouseEvent& rMEvt) +{ + // remember button state for creation of own MouseEvents + SetMouseButtonCode(rMEvt.GetButtons()); + + SdrPageView* pPV = nullptr; + Point aPnt( mpWindow->PixelToLogic( rMEvt.GetPosPixel() ) ); + + mpRefObj = mpView->PickObj(aPnt, mpView->getHitTolLog(), pPV); + if (mpRefObj) + { + if (nSlotId == SID_BEFORE_OBJ) + { + mpView->PutMarkedInFrontOfObj(mpRefObj); + } + else + { + mpView->PutMarkedBehindObj(mpRefObj); + } + } + + mpViewShell->Cancel(); + + return true; +} + +void FuDisplayOrder::Activate() +{ + maPtr = mpWindow->GetPointer(); + mpWindow->SetPointer( PointerStyle::RefHand ); +} + +void FuDisplayOrder::Deactivate() +{ + mpWindow->SetPointer( maPtr ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuediglu.cxx b/sd/source/ui/func/fuediglu.cxx new file mode 100644 index 000000000..5d9d61447 --- /dev/null +++ b/sd/source/ui/func/fuediglu.cxx @@ -0,0 +1,471 @@ +/* -*- 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 <fuediglu.hxx> +#include <svl/eitem.hxx> +#include <svx/svdglue.hxx> +#include <sfx2/request.hxx> + +#include <app.hrc> + +#include <Window.hxx> +#include <View.hxx> +#include <ViewShell.hxx> +#include <ViewShellBase.hxx> +#include <ToolBarManager.hxx> + +namespace sd { + + +FuEditGluePoints::FuEditGluePoints ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuDraw(pViewSh, pWin, pView, pDoc, rReq) + //Add Shift+UP/DOWN/LEFT/RIGHT key to move the position of insert point, + //and SHIFT+ENTER key to decide the position and draw the new insert point + ,bBeginInsertPoint(false), + oldPoint(0,0) +{ +} + +rtl::Reference<FuPoor> FuEditGluePoints::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, bool bPermanent ) +{ + FuEditGluePoints* pFunc; + rtl::Reference<FuPoor> xFunc( pFunc = new FuEditGluePoints( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + pFunc->SetPermanent( bPermanent ); + return xFunc; +} + +void FuEditGluePoints::DoExecute( SfxRequest& rReq ) +{ + FuDraw::DoExecute( rReq ); + mpView->SetInsGluePointMode(false); + mpViewShell->GetViewShellBase().GetToolBarManager()->AddToolBar( + ToolBarManager::ToolBarGroup::Function, + ToolBarManager::msGluePointsToolBar); +} + +FuEditGluePoints::~FuEditGluePoints() +{ + mpView->BrkAction(); + mpView->UnmarkAllGluePoints(); + mpView->SetInsGluePointMode(false); +} + +bool FuEditGluePoints::MouseButtonDown(const MouseEvent& rMEvt) +{ + mpView->SetActualWin( mpWindow->GetOutDev() ); + + bool bReturn = FuDraw::MouseButtonDown(rMEvt); + + if (mpView->IsAction()) + { + if (rMEvt.IsRight()) + mpView->BckAction(); + + return true; + } + + if (rMEvt.IsLeft()) + { + bReturn = true; + sal_uInt16 nHitLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(HITPIX,0)).Width() ); + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + mpWindow->CaptureMouse(); + + SdrViewEvent aVEvt; + SdrHitKind eHit = mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + + if (eHit == SdrHitKind::Handle) + { + // drag handle + SdrHdl* pHdl = aVEvt.mpHdl; + + if (mpView->IsGluePointMarked(aVEvt.mpObj, aVEvt.mnGlueId) && rMEvt.IsShift()) + { + mpView->UnmarkGluePoint(aVEvt.mpObj, aVEvt.mnGlueId); + pHdl = nullptr; + } + + if (pHdl) + { + // drag handle + mpView->BegDragObj(aMDPos, nullptr, aVEvt.mpHdl, nDrgLog); + } + } + else if (eHit == SdrHitKind::MarkedObject && mpView->IsInsGluePointMode()) + { + // insert gluepoints + mpView->BegInsGluePoint(aMDPos); + } + else if (eHit == SdrHitKind::MarkedObject && rMEvt.IsMod1()) + { + // select gluepoints + if (!rMEvt.IsShift()) + mpView->UnmarkAllGluePoints(); + + mpView->BegMarkGluePoints(aMDPos); + } + else if (eHit == SdrHitKind::MarkedObject && !rMEvt.IsShift() && !rMEvt.IsMod2()) + { + // move object + mpView->BegDragObj(aMDPos, nullptr, nullptr, nDrgLog); + } + else if (eHit == SdrHitKind::Gluepoint) + { + // select gluepoints + if (!rMEvt.IsShift()) + mpView->UnmarkAllGluePoints(); + + mpView->MarkGluePoint(aVEvt.mpObj, aVEvt.mnGlueId, false); + SdrHdl* pHdl = mpView->GetGluePointHdl(aVEvt.mpObj, aVEvt.mnGlueId); + + if (pHdl) + { + mpView->BegDragObj(aMDPos, nullptr, pHdl, nDrgLog); + } + } + else + { + // select or drag object + if (!rMEvt.IsShift() && !rMEvt.IsMod2() && eHit == SdrHitKind::UnmarkedObject) + { + mpView->UnmarkAllObj(); + } + + bool bMarked = false; + + if (!rMEvt.IsMod1()) + { + if (rMEvt.IsMod2()) + { + bMarked = mpView->MarkNextObj(aMDPos, nHitLog, rMEvt.IsShift()); + } + else + { + bMarked = mpView->MarkObj(aMDPos, nHitLog, rMEvt.IsShift()); + } + } + + if (bMarked && + (!rMEvt.IsShift() || eHit == SdrHitKind::MarkedObject)) + { + // move object + mpView->BegDragObj(aMDPos, nullptr, aVEvt.mpHdl, nDrgLog); + } + else if (mpView->AreObjectsMarked()) + { + // select gluepoint + if (!rMEvt.IsShift()) + mpView->UnmarkAllGluePoints(); + + mpView->BegMarkGluePoints(aMDPos); + } + else + { + // select object + mpView->BegMarkObj(aMDPos); + } + } + + ForcePointer(&rMEvt); + } + + return bReturn; +} + +bool FuEditGluePoints::MouseMove(const MouseEvent& rMEvt) +{ + mpView->SetActualWin( mpWindow->GetOutDev() ); + + FuDraw::MouseMove(rMEvt); + + if (mpView->IsAction()) + { + Point aPix(rMEvt.GetPosPixel()); + Point aPnt( mpWindow->PixelToLogic(aPix) ); + ForceScroll(aPix); + mpView->MovAction(aPnt); + } + + ForcePointer(&rMEvt); + + return true; +} + +bool FuEditGluePoints::MouseButtonUp(const MouseEvent& rMEvt) +{ + mpView->SetActualWin( mpWindow->GetOutDev() ); + + bool bReturn = false; + + if (mpView->IsAction()) + { + bReturn = true; + mpView->EndAction(); + } + + FuDraw::MouseButtonUp(rMEvt); + + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + Point aPos = mpWindow->PixelToLogic( rMEvt.GetPosPixel() ); + + if (std::abs(aMDPos.X() - aPos.X()) < nDrgLog && + std::abs(aMDPos.Y() - aPos.Y()) < nDrgLog && + !rMEvt.IsShift() && !rMEvt.IsMod2()) + { + SdrViewEvent aVEvt; + SdrHitKind eHit = mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + + if (eHit == SdrHitKind::NONE) + { + // click on position: deselect + mpView->UnmarkAllObj(); + } + } + + mpWindow->ReleaseMouse(); + + return bReturn; +} + +/** + * Process keyboard input + * @returns sal_True if a KeyEvent is being processed, sal_False otherwise + */ +bool FuEditGluePoints::KeyInput(const KeyEvent& rKEvt) +{ + mpView->SetActualWin( mpWindow->GetOutDev() ); + + //Add Shift+UP/DOWN/LEFT/RIGHT key to move the position of insert point, + //and SHIFT+ENTER key to decide the position and draw the new insert point + + bool bReturn = false; + + switch (rKEvt.GetKeyCode().GetCode()) + { + case KEY_UP: + case KEY_DOWN: + case KEY_LEFT: + case KEY_RIGHT: + { + if(rKEvt.GetKeyCode().IsShift()&& mpView->IsInsGluePointMode() ){ + ::tools::Long nX = 0; + ::tools::Long nY = 0; + sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode(); + if (nCode == KEY_UP) + { + // scroll up + nX = 0; + nY =-1; + } + else if (nCode == KEY_DOWN) + { + // scroll down + nX = 0; + nY = 1; + } + else if (nCode == KEY_LEFT) + { + // scroll left + nX =-1; + nY = 0; + } + else if (nCode == KEY_RIGHT) + { + // scroll right + nX = 1; + nY = 0; + } + Point centerPoint; + ::tools::Rectangle rect = mpView->GetMarkedObjRect(); + centerPoint = mpWindow->LogicToPixel(rect.Center()); + Point aPoint = bBeginInsertPoint? oldPoint:centerPoint; + Point ePoint = aPoint + Point(nX,nY); + mpWindow->SetPointerPosPixel(ePoint); + //simulate mouse move action + MouseEvent eMevt(ePoint, 1, MouseEventModifiers::DRAGMOVE, MOUSE_LEFT, 0); + MouseMove(eMevt); + oldPoint = ePoint; + bBeginInsertPoint = true; + bReturn = true; + } + } + break; + case KEY_RETURN: + if(rKEvt.GetKeyCode().IsShift() && mpView->IsInsGluePointMode() ) + { + if(bBeginInsertPoint) + { + mpWindow->SetPointerPosPixel(oldPoint); + //simulate mouse button down action + MouseEvent aMevt(oldPoint, 1, + MouseEventModifiers::SIMPLEMOVE | MouseEventModifiers::DRAGMOVE, + MOUSE_LEFT, KEY_SHIFT); + // MT IA2: Not used? + // sal_uInt16 ubuttons = aMevt.GetButtons(); + // sal_uInt16 uMod = aMevt.GetModifier(); + MouseButtonDown(aMevt); + mpWindow->CaptureMouse(); + //simulate mouse button up action + MouseEvent rMEvt(oldPoint+Point(0,0), 1, + MouseEventModifiers::SIMPLEMOVE | MouseEventModifiers::ENTERWINDOW, + MOUSE_LEFT, KEY_SHIFT); + MouseButtonUp(rMEvt); + bReturn= true; + } + } + break; + } + + if(!bReturn) + bReturn = FuDraw::KeyInput(rKEvt); + + return bReturn; +} + +//Add Shift+UP/DOWN/LEFT/RIGHT key to move the position of insert point, and +//SHIFT+ENTER key to decide the position and draw the new insert point +void FuEditGluePoints::ForcePointer(const MouseEvent* pMEvt) +{ + if(bBeginInsertPoint && pMEvt) + { + MouseEvent aMEvt(pMEvt->GetPosPixel(), pMEvt->GetClicks(), + pMEvt->GetMode(), pMEvt->GetButtons(), pMEvt->GetModifier() & ~KEY_SHIFT); + FuDraw::ForcePointer(&aMEvt); + } + else + { + FuDraw::ForcePointer(pMEvt); + } +} + +bool FuEditGluePoints::Command(const CommandEvent& rCEvt) +{ + mpView->SetActualWin( mpWindow->GetOutDev() ); + return FuPoor::Command( rCEvt ); +} + +void FuEditGluePoints::Activate() +{ + mpView->SetGluePointEditMode(); + FuDraw::Activate(); +} + +void FuEditGluePoints::Deactivate() +{ + mpView->SetGluePointEditMode( false ); + FuDraw::Deactivate(); +} + +void FuEditGluePoints::ReceiveRequest(SfxRequest& rReq) +{ + switch (rReq.GetSlot()) + { + case SID_GLUE_INSERT_POINT: + { + mpView->SetInsGluePointMode(!mpView->IsInsGluePointMode()); + } + break; + + case SID_GLUE_ESCDIR_LEFT: + { + mpView->SetMarkedGluePointsEscDir( SdrEscapeDirection::LEFT, + !mpView->IsMarkedGluePointsEscDir( SdrEscapeDirection::LEFT ) ); + } + break; + + case SID_GLUE_ESCDIR_RIGHT: + { + mpView->SetMarkedGluePointsEscDir( SdrEscapeDirection::RIGHT, + !mpView->IsMarkedGluePointsEscDir( SdrEscapeDirection::RIGHT ) ); + } + break; + + case SID_GLUE_ESCDIR_TOP: + { + mpView->SetMarkedGluePointsEscDir( SdrEscapeDirection::TOP, + !mpView->IsMarkedGluePointsEscDir( SdrEscapeDirection::TOP ) ); + } + break; + + case SID_GLUE_ESCDIR_BOTTOM: + { + mpView->SetMarkedGluePointsEscDir( SdrEscapeDirection::BOTTOM, + !mpView->IsMarkedGluePointsEscDir( SdrEscapeDirection::BOTTOM ) ); + } + break; + + case SID_GLUE_PERCENT: + { + const SfxItemSet* pSet = rReq.GetArgs(); + const SfxPoolItem& rItem = pSet->Get(SID_GLUE_PERCENT); + bool bPercent = static_cast<const SfxBoolItem&>(rItem).GetValue(); + mpView->SetMarkedGluePointsPercent(bPercent); + } + break; + + case SID_GLUE_HORZALIGN_CENTER: + { + mpView->SetMarkedGluePointsAlign(false, SdrAlign::HORZ_CENTER); + } + break; + + case SID_GLUE_HORZALIGN_LEFT: + { + mpView->SetMarkedGluePointsAlign(false, SdrAlign::HORZ_LEFT); + } + break; + + case SID_GLUE_HORZALIGN_RIGHT: + { + mpView->SetMarkedGluePointsAlign(false, SdrAlign::HORZ_RIGHT); + } + break; + + case SID_GLUE_VERTALIGN_CENTER: + { + mpView->SetMarkedGluePointsAlign(true, SdrAlign::VERT_CENTER); + } + break; + + case SID_GLUE_VERTALIGN_TOP: + { + mpView->SetMarkedGluePointsAlign(true, SdrAlign::VERT_TOP); + } + break; + + case SID_GLUE_VERTALIGN_BOTTOM: + { + mpView->SetMarkedGluePointsAlign(true, SdrAlign::VERT_BOTTOM); + } + break; + } + + // at the end, call base class + FuPoor::ReceiveRequest(rReq); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuexecuteinteraction.cxx b/sd/source/ui/func/fuexecuteinteraction.cxx new file mode 100644 index 000000000..d1956fcf5 --- /dev/null +++ b/sd/source/ui/func/fuexecuteinteraction.cxx @@ -0,0 +1,237 @@ +/* -*- 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 <fuexecuteinteraction.hxx> + +#include <app.hrc> +#include <config_features.h> +#include <avmedia/mediawindow.hxx> +#include <basic/sbstar.hxx> +#include <sfx2/app.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/sfxsids.hrc> +#include <sfx2/viewfrm.hxx> +#include <svl/intitem.hxx> +#include <svl/stritem.hxx> +#include <svl/urihelper.hxx> +#include <tools/urlobj.hxx> +#include <o3tl/string_view.hxx> + +#include <DrawViewShell.hxx> +#include <GraphicDocShell.hxx> +#include <ViewShell.hxx> +#include <anminfo.hxx> +#include <drawdoc.hxx> +#include <drawview.hxx> +#include <pgjump.hxx> + +#include <com/sun/star/media/XPlayer.hpp> + +using namespace css; + +namespace sd +{ +FuExecuteInteraction::FuExecuteInteraction(ViewShell* pViewSh, ::sd::Window* pWin, + ::sd::View* pView, SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuExecuteInteraction::Create(ViewShell* pViewSh, ::sd::Window* pWin, + ::sd::View* pView, SdDrawDocument* pDoc, + SfxRequest& rReq) +{ + rtl::Reference<FuPoor> xFunc(new FuExecuteInteraction(pViewSh, pWin, pView, pDoc, rReq)); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuExecuteInteraction::DoExecute(SfxRequest&) +{ + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + if (rMarkList.GetMarkCount() != 1) + return; + + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + + if (dynamic_cast<const GraphicDocShell*>(mpDocSh) != nullptr + || dynamic_cast<const DrawView*>(mpView) == nullptr) + return; + + SdAnimationInfo* pInfo = SdDrawDocument::GetAnimationInfo(pObj); + if (!pInfo) + return; + + switch (pInfo->meClickAction) + { + case presentation::ClickAction_BOOKMARK: + { + // Jump to Bookmark (Page or Object) + SfxStringItem aItem(SID_NAVIGATOR_OBJECT, pInfo->GetBookmark()); + mpViewShell->GetViewFrame()->GetDispatcher()->ExecuteList( + SID_NAVIGATOR_OBJECT, SfxCallMode::SLOT | SfxCallMode::RECORD, { &aItem }); + } + break; + + case presentation::ClickAction_DOCUMENT: + { + OUString sBookmark(pInfo->GetBookmark()); + // Jump to document + if (!sBookmark.isEmpty()) + { + SfxStringItem aReferer(SID_REFERER, mpDocSh->GetMedium()->GetName()); + SfxStringItem aStrItem(SID_FILE_NAME, sBookmark); + SfxViewFrame* pFrame = mpViewShell->GetViewFrame(); + SfxFrameItem aFrameItem(SID_DOCFRAME, pFrame); + SfxBoolItem aBrowseItem(SID_BROWSE, true); + pFrame->GetDispatcher()->ExecuteList( + SID_OPENDOC, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aStrItem, &aFrameItem, &aBrowseItem, &aReferer }); + } + } + break; + + case presentation::ClickAction_PREVPAGE: + { + // Jump to the previous page + SfxUInt16Item aItem(SID_NAVIGATOR_PAGE, PAGE_PREVIOUS); + mpViewShell->GetViewFrame()->GetDispatcher()->ExecuteList( + SID_NAVIGATOR_PAGE, SfxCallMode::SLOT | SfxCallMode::RECORD, { &aItem }); + } + break; + + case presentation::ClickAction_NEXTPAGE: + { + // Jump to the next page + SfxUInt16Item aItem(SID_NAVIGATOR_PAGE, PAGE_NEXT); + mpViewShell->GetViewFrame()->GetDispatcher()->ExecuteList( + SID_NAVIGATOR_PAGE, SfxCallMode::SLOT | SfxCallMode::RECORD, { &aItem }); + } + break; + + case presentation::ClickAction_FIRSTPAGE: + { + // Jump to the first page + SfxUInt16Item aItem(SID_NAVIGATOR_PAGE, PAGE_FIRST); + mpViewShell->GetViewFrame()->GetDispatcher()->ExecuteList( + SID_NAVIGATOR_PAGE, SfxCallMode::SLOT | SfxCallMode::RECORD, { &aItem }); + } + break; + + case presentation::ClickAction_LASTPAGE: + { + // Jump to the last page + SfxUInt16Item aItem(SID_NAVIGATOR_PAGE, PAGE_LAST); + mpViewShell->GetViewFrame()->GetDispatcher()->ExecuteList( + SID_NAVIGATOR_PAGE, SfxCallMode::SLOT | SfxCallMode::RECORD, { &aItem }); + } + break; + + case presentation::ClickAction_SOUND: + { +#if HAVE_FEATURE_AVMEDIA + try + { + mxPlayer.set(avmedia::MediaWindow::createPlayer(pInfo->GetBookmark(), "" /*TODO?*/), + uno::UNO_SET_THROW); + mxPlayer->start(); + } + catch (uno::Exception&) + { + } +#endif + } + break; + + case presentation::ClickAction_VERB: + { + // Assign verb + mpView->UnmarkAll(); + mpView->MarkObj(pObj, mpView->GetSdrPageView()); + DrawViewShell* pDrViewSh = static_cast<DrawViewShell*>(mpViewShell); + pDrViewSh->DoVerb(static_cast<sal_Int16>(pInfo->mnVerb)); + } + break; + + case presentation::ClickAction_PROGRAM: + { + OUString aBaseURL = GetDocSh()->GetMedium()->GetBaseURL(); + INetURLObject aURL(::URIHelper::SmartRel2Abs( + INetURLObject(aBaseURL), pInfo->GetBookmark(), URIHelper::GetMaybeFileHdl(), true, + false, INetURLObject::EncodeMechanism::WasEncoded, + INetURLObject::DecodeMechanism::Unambiguous)); + + if (INetProtocol::File == aURL.GetProtocol()) + { + SfxStringItem aUrl(SID_FILE_NAME, + aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE)); + SfxBoolItem aBrowsing(SID_BROWSE, true); + + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + if (pViewFrm) + pViewFrm->GetDispatcher()->ExecuteList( + SID_OPENDOC, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aUrl, &aBrowsing }); + } + } + break; + +#if HAVE_FEATURE_SCRIPTING + case presentation::ClickAction_MACRO: + { + // Execute macro + OUString aMacro = pInfo->GetBookmark(); + + if (SfxApplication::IsXScriptURL(aMacro)) + { + uno::Any aRet; + uno::Sequence<sal_Int16> aOutArgsIndex; + uno::Sequence<uno::Any> aParams; + uno::Sequence<uno::Any> aOutArgs; + + mpDocSh->CallXScript(aMacro, aParams, aRet, aOutArgsIndex, aOutArgs); + } + else + { + // aMacro has got following format: + // "Macroname.Modulname.Libname.Documentname" or + // "Macroname.Modulname.Libname.Applicationname" + sal_Int32 nIdx{ 0 }; + const std::u16string_view aMacroName = o3tl::getToken(aMacro, 0, '.', nIdx); + const std::u16string_view aModulName = o3tl::getToken(aMacro, 0, '.', nIdx); + + // Currently the "Call" method only resolves modulename+macroname + mpDocSh->GetBasic()->Call(OUString::Concat(aModulName) + "." + aMacroName); + } + } + break; +#endif + + default: + break; + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuexpand.cxx b/sd/source/ui/func/fuexpand.cxx new file mode 100644 index 000000000..822174ed9 --- /dev/null +++ b/sd/source/ui/func/fuexpand.cxx @@ -0,0 +1,256 @@ +/* -*- 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 <fuexpand.hxx> + +#include <sfx2/viewfrm.hxx> +#include <svx/svdotext.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xlineit0.hxx> +#include <svx/svdundo.hxx> +#include <editeng/outlobj.hxx> +#include <svx/svdetc.hxx> +#include <xmloff/autolayout.hxx> +#include <sal/log.hxx> + +#include <app.hrc> +#include <strings.hrc> +#include <pres.hxx> +#include <View.hxx> +#include <sdpage.hxx> +#include <Outliner.hxx> +#include <drawdoc.hxx> +#include <ViewShell.hxx> +#include <sdresid.hxx> +#include <sdmod.hxx> +#include <sfx2/dispatch.hxx> +#include <editeng/eeitem.hxx> + +using namespace com::sun::star; + +namespace sd { + + +FuExpandPage::FuExpandPage ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuExpandPage::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuExpandPage( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuExpandPage::DoExecute( SfxRequest& ) +{ + if ( mpView && mpView->IsTextEdit() ) + mpView->SdrEndTextEdit(); + + // find selected page (only standard pages) + SdPage* pActualPage = nullptr; + sal_uInt16 i = 0; + sal_uInt16 nCount = mpDoc->GetSdPageCount(PageKind::Standard); + + while (!pActualPage && i < nCount) + { + if (mpDoc->GetSdPage(i, PageKind::Standard)->IsSelected()) + { + pActualPage = mpDoc->GetSdPage(i, PageKind::Standard); + } + + i++; + } + + if (!pActualPage) + return; + + SdOutliner aOutliner( mpDoc, OutlinerMode::OutlineObject ); + aOutliner.SetUpdateLayout(false); + aOutliner.EnableUndo(false); + + if (mpDocSh) + aOutliner.SetRefDevice( SD_MOD()->GetVirtualRefDevice() ); + + aOutliner.SetDefTab( mpDoc->GetDefaultTabulator() ); + aOutliner.SetStyleSheetPool(static_cast<SfxStyleSheetPool*>(mpDoc->GetStyleSheetPool())); + + SdrLayerIDSet aVisibleLayers = pActualPage->TRG_GetMasterPageVisibleLayers(); + sal_uInt16 nActualPageNum = pActualPage->GetPageNum(); + SdPage* pActualNotesPage = static_cast<SdPage*>(mpDoc->GetPage(nActualPageNum + 1)); + SdrTextObj* pActualOutline = static_cast<SdrTextObj*>(pActualPage->GetPresObj(PresObjKind::Outline)); + + if (pActualOutline) + { + const bool bUndo = mpView->IsUndoEnabled(); + + if( bUndo ) + mpView->BegUndo(SdResId(STR_UNDO_EXPAND_PAGE)); + + // set current structuring-object into outliner + OutlinerParaObject* pParaObj = pActualOutline->GetOutlinerParaObject(); + aOutliner.SetText(*pParaObj); + + // remove hard paragraph- and character attributes + SfxItemSetFixed<EE_ITEMS_START, EE_ITEMS_END> aEmptyEEAttr(mpDoc->GetPool()); + sal_Int32 nParaCount1 = aOutliner.GetParagraphCount(); + + for (sal_Int32 nPara = 0; nPara < nParaCount1; nPara++) + { + aOutliner.RemoveCharAttribs(nPara); + aOutliner.SetParaAttribs(nPara, aEmptyEEAttr); + } + + sal_uInt16 nPos = 2; + Paragraph* pPara = aOutliner.GetParagraph( 0 ); + + while (pPara) + { + sal_Int32 nParaPos = aOutliner.GetAbsPos( pPara ); + sal_Int16 nDepth = aOutliner.GetDepth( nParaPos ); + if ( nDepth == 0 ) + { + // page with title & structuring! + rtl::Reference<SdPage> pPage = mpDoc->AllocSdPage(false); + pPage->SetSize(pActualPage->GetSize() ); + pPage->SetBorder(pActualPage->GetLeftBorder(), + pActualPage->GetUpperBorder(), + pActualPage->GetRightBorder(), + pActualPage->GetLowerBorder() ); + pPage->SetName(OUString()); + + // insert page after current page + mpDoc->InsertPage(pPage.get(), nActualPageNum + nPos); + nPos++; + + if( bUndo ) + mpView->AddUndo(mpDoc->GetSdrUndoFactory().CreateUndoNewPage(*pPage)); + + // use MasterPage of the current page + pPage->TRG_SetMasterPage(pActualPage->TRG_GetMasterPage()); + pPage->SetLayoutName(pActualPage->GetLayoutName()); + pPage->SetAutoLayout(AUTOLAYOUT_TITLE_CONTENT, true); + pPage->TRG_SetMasterPageVisibleLayers(aVisibleLayers); + + // notes-page + rtl::Reference<SdPage> pNotesPage = mpDoc->AllocSdPage(false); + pNotesPage->SetSize(pActualNotesPage->GetSize()); + pNotesPage->SetBorder(pActualNotesPage->GetLeftBorder(), + pActualNotesPage->GetUpperBorder(), + pActualNotesPage->GetRightBorder(), + pActualNotesPage->GetLowerBorder() ); + pNotesPage->SetPageKind(PageKind::Notes); + pNotesPage->SetName(OUString()); + + // insert page after current page + mpDoc->InsertPage(pNotesPage.get(), nActualPageNum + nPos); + nPos++; + + if( bUndo ) + mpView->AddUndo(mpDoc->GetSdrUndoFactory().CreateUndoNewPage(*pNotesPage)); + + // use MasterPage of the current page + pNotesPage->TRG_SetMasterPage(pActualNotesPage->TRG_GetMasterPage()); + pNotesPage->SetLayoutName(pActualNotesPage->GetLayoutName()); + pNotesPage->SetAutoLayout(pActualNotesPage->GetAutoLayout(), true); + pNotesPage->TRG_SetMasterPageVisibleLayers(aVisibleLayers); + + // create title text objects + SdrTextObj* pTextObj = static_cast<SdrTextObj*>(pPage->GetPresObj(PresObjKind::Title)); + SAL_WARN_IF(!pTextObj, "sd.core", "worrying lack of PresObjKind::Title object"); + if (!pTextObj) + continue; + + std::optional<OutlinerParaObject> pOutlinerParaObject = aOutliner.CreateParaObject( nParaPos, 1); + pOutlinerParaObject->SetOutlinerMode(OutlinerMode::TitleObject); + + if( pOutlinerParaObject->GetDepth(0) != -1 ) + { + std::unique_ptr<SdrOutliner> pTempOutl = SdrMakeOutliner(OutlinerMode::TitleObject, *mpDoc); + + pTempOutl->SetText( *pOutlinerParaObject ); + + pOutlinerParaObject.reset(); + + pTempOutl->SetDepth( pTempOutl->GetParagraph( 0 ), -1 ); + + pOutlinerParaObject = pTempOutl->CreateParaObject(); + } + + pTextObj->SetOutlinerParaObject(std::move(pOutlinerParaObject)); + + pTextObj->SetEmptyPresObj(false); + + SfxStyleSheet* pSheet = pPage->GetStyleSheetForPresObj(PresObjKind::Title); + pTextObj->NbcSetStyleSheet(pSheet, false); + + SdrTextObj* pOutlineObj = nullptr; + sal_Int32 nChildCount = aOutliner.GetChildCount(pPara); + if (nChildCount > 0) + pOutlineObj = static_cast<SdrTextObj*>( pPage->GetPresObj(PresObjKind::Outline) ); + if (pOutlineObj) + { + // create structuring text objects + std::optional<OutlinerParaObject> pOPO = aOutliner.CreateParaObject(++nParaPos, nChildCount); + + std::unique_ptr<SdrOutliner> pTempOutl = SdrMakeOutliner(OutlinerMode::OutlineObject, *mpDoc); + pTempOutl->SetText( *pOPO ); + + sal_Int32 nParaCount2 = pTempOutl->GetParagraphCount(); + sal_Int32 nPara; + for( nPara = 0; nPara < nParaCount2; nPara++ ) + { + pTempOutl->SetDepth ( + pTempOutl->GetParagraph( nPara ), + pTempOutl->GetDepth( nPara ) - 1); + } + + pOPO = pTempOutl->CreateParaObject(); + pTempOutl.reset(); + + pOutlineObj->SetOutlinerParaObject( std::move(pOPO) ); + pOutlineObj->SetEmptyPresObj(false); + + // remove hard attributes (Flag to sal_True) + SfxItemSet aAttr(mpDoc->GetPool()); + aAttr.Put(XLineStyleItem(drawing::LineStyle_NONE)); + aAttr.Put(XFillStyleItem(drawing::FillStyle_NONE)); + pOutlineObj->SetMergedItemSet(aAttr); + } + } + + pPara = aOutliner.GetParagraph( ++nParaPos ); + } + + if( bUndo ) + mpView->EndUndo(); + } + + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_DELETE_PAGE, SfxCallMode::SYNCHRON | SfxCallMode::RECORD); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuformatpaintbrush.cxx b/sd/source/ui/func/fuformatpaintbrush.cxx new file mode 100644 index 000000000..40bde764f --- /dev/null +++ b/sd/source/ui/func/fuformatpaintbrush.cxx @@ -0,0 +1,276 @@ +/* -*- 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/request.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> + +#include <svx/svxids.hrc> +#include <svx/svdotable.hxx> +#include <svx/svdundo.hxx> +#include <editeng/outliner.hxx> +#include <vcl/ptrstyle.hxx> + +#include <fuformatpaintbrush.hxx> +#include <drawview.hxx> +#include <DrawViewShell.hxx> +#include <FrameView.hxx> +#include <drawdoc.hxx> +#include <ViewShellBase.hxx> + +#include <Window.hxx> + +namespace sd { + + +FuFormatPaintBrush::FuFormatPaintBrush( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +: FuText(pViewSh, pWin, pView, pDoc, rReq) +, mbPermanent( false ) +, mbOldIsQuickTextEditMode( true ) +{ +} + +rtl::Reference<FuPoor> FuFormatPaintBrush::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuFormatPaintBrush( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute( rReq ); + return xFunc; +} + +void FuFormatPaintBrush::DoExecute( SfxRequest& rReq ) +{ + const SfxItemSet *pArgs = rReq.GetArgs(); + if( pArgs && pArgs->Count() >= 1 ) + { + mbPermanent = pArgs->Get(SID_FORMATPAINTBRUSH).GetValue(); + } + + if( mpView ) + { + mpView->TakeFormatPaintBrush( mxItemSet ); + } +} + +void FuFormatPaintBrush::implcancel() +{ + if( mpViewShell && mpViewShell->GetViewFrame() ) + { + SfxViewFrame* pViewFrame = mpViewShell->GetViewFrame(); + pViewFrame->GetBindings().Invalidate(SID_FORMATPAINTBRUSH); + pViewFrame->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + } +} + +static void unmarkimpl( SdrView* pView ) +{ + pView->SdrEndTextEdit(); + pView->UnMarkAll(); +} + +bool FuFormatPaintBrush::MouseButtonDown(const MouseEvent& rMEvt) +{ + if(mpView&&mpWindow) + { + SdrViewEvent aVEvt; + SdrHitKind eHit = mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + + if( (eHit == SdrHitKind::TextEdit) || (eHit == SdrHitKind::TextEditObj && ( mpViewShell->GetFrameView()->IsQuickEdit() || dynamic_cast<sdr::table::SdrTableObj*>(aVEvt.mpObj) != nullptr ) )) + { + SdrPageView* pPV=nullptr; + sal_uInt16 nHitLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(HITPIX,0)).Width() ); + SdrObject* pPickObj = mpView->PickObj(mpWindow->PixelToLogic(rMEvt.GetPosPixel()),nHitLog, pPV, SdrSearchOptions::PICKMARKABLE); + if( (pPickObj != nullptr) && !pPickObj->IsEmptyPresObj() ) + { + // if we text hit another shape than the one currently selected, unselect the old one now + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + if( rMarkList.GetMarkCount() > 0 ) + { + if( rMarkList.GetMarkCount() == 1 ) + { + if( rMarkList.GetMark(0)->GetMarkedSdrObj() != pPickObj ) + { + + // if current selected shape is not that of the hit text edit, deselect it + unmarkimpl( mpView ); + } + } + else + { + // more than one shape selected, deselect all of them + unmarkimpl( mpView ); + } + } + MouseEvent aMEvt( rMEvt.GetPosPixel(), rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(), 0 ); + return FuText::MouseButtonDown(aMEvt); + } + + if (aVEvt.mpObj == nullptr) + aVEvt.mpObj = pPickObj; + } + + unmarkimpl( mpView ); + + if (aVEvt.mpObj) + { + sal_uInt16 nHitLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(HITPIX,0)).Width() ); + mpView->MarkObj(mpWindow->PixelToLogic( rMEvt.GetPosPixel() ), nHitLog, false/*bToggle*/); + return true; + } + } + return false; +} + +bool FuFormatPaintBrush::MouseMove(const MouseEvent& rMEvt) +{ + bool bReturn = false; + if( mpWindow && mpView ) + { + if ( mpView->IsTextEdit() ) + { + bReturn = FuText::MouseMove( rMEvt ); + mpWindow->SetPointer(PointerStyle::Fill); + } + else + { + sal_uInt16 nHitLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(HITPIX,0)).Width() ); + SdrPageView* pPV=nullptr; + SdrObject* pObj = mpView->PickObj(mpWindow->PixelToLogic( rMEvt.GetPosPixel() ),nHitLog, pPV, SdrSearchOptions::PICKMARKABLE); + if (pObj && HasContentForThisType(pObj->GetObjInventor(),pObj->GetObjIdentifier()) ) + mpWindow->SetPointer(PointerStyle::Fill); + else + mpWindow->SetPointer(PointerStyle::Arrow); + } + } + return bReturn; +} + +bool FuFormatPaintBrush::MouseButtonUp(const MouseEvent& rMEvt) +{ + if( mxItemSet && mpView && mpView->AreObjectsMarked() ) + { + bool bNoCharacterFormats = false; + bool bNoParagraphFormats = false; + { + if( (rMEvt.GetModifier()&KEY_MOD1) && (rMEvt.GetModifier()&KEY_SHIFT) ) + bNoCharacterFormats = true; + else if( rMEvt.GetModifier() & KEY_MOD1 ) + bNoParagraphFormats = true; + } + + OutlinerView* pOLV = mpView->GetTextEditOutlinerView(); + if( pOLV ) + pOLV->MouseButtonUp(rMEvt); + + Paste( bNoCharacterFormats, bNoParagraphFormats ); + if(mpViewShell) + mpViewShell->GetViewFrame()->GetBindings().Invalidate(SID_FORMATPAINTBRUSH); + + if( mbPermanent ) + return true; + } + + implcancel(); + return true; +} + +bool FuFormatPaintBrush::KeyInput(const KeyEvent& rKEvt) +{ + if (rKEvt.GetKeyCode().GetCode() == KEY_ESCAPE) + { + implcancel(); + return true; + } + return FuPoor::KeyInput(rKEvt); +} + +void FuFormatPaintBrush::Activate() +{ + mbOldIsQuickTextEditMode = mpViewShell->GetFrameView()->IsQuickEdit(); + if( !mbOldIsQuickTextEditMode ) + { + mpViewShell->GetFrameView()->SetQuickEdit(true); + mpView->SetQuickTextEditMode(true); + } +} + +void FuFormatPaintBrush::Deactivate() +{ + if( !mbOldIsQuickTextEditMode ) + { + mpViewShell->GetFrameView()->SetQuickEdit(false); + mpView->SetQuickTextEditMode(false); + } +} + +bool FuFormatPaintBrush::HasContentForThisType( SdrInventor nObjectInventor, SdrObjKind nObjectIdentifier ) const +{ + if (mxItemSet == nullptr) + return false; + if( !mpView || (!SdrObjEditView::SupportsFormatPaintbrush( nObjectInventor, nObjectIdentifier) ) ) + return false; + return true; +} + +void FuFormatPaintBrush::Paste( bool bNoCharacterFormats, bool bNoParagraphFormats ) +{ + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + if( !(mxItemSet && ( rMarkList.GetMarkCount() == 1 )) ) + return; + + SdrObject* pObj( nullptr ); + bool bUndo = mpDoc->IsUndoEnabled(); + + if( bUndo && !mpView->GetTextEditOutlinerView() ) + pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + + // n685123: ApplyFormatPaintBrush itself would store undo information + // except in a few cases (?) + if( pObj ) + { + OUString sLabel( mpViewShell->GetViewShellBase().RetrieveLabelFromCommand(".uno:FormatPaintbrush" ) ); + mpDoc->BegUndo( sLabel ); + if (dynamic_cast< sdr::table::SdrTableObj* >( pObj ) == nullptr) + mpDoc->AddUndo( mpDoc->GetSdrUndoFactory().CreateUndoAttrObject( *pObj, false, true ) ); + } + + mpView->ApplyFormatPaintBrush( *mxItemSet, bNoCharacterFormats, bNoParagraphFormats ); + + if( pObj ) + { + mpDoc->EndUndo(); + } +} + +/* static */ void FuFormatPaintBrush::GetMenuState( DrawViewShell const & rDrawViewShell, SfxItemSet &rSet ) +{ + const SdrMarkList& rMarkList = rDrawViewShell.GetDrawView()->GetMarkedObjectList(); + + if( rMarkList.GetMarkCount() == 1 ) + { + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + if( pObj && SdrObjEditView::SupportsFormatPaintbrush(pObj->GetObjInventor(),pObj->GetObjIdentifier()) ) + return; + } + rSet.DisableItem( SID_FORMATPAINTBRUSH ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuhhconv.cxx b/sd/source/ui/func/fuhhconv.cxx new file mode 100644 index 000000000..a312439bb --- /dev/null +++ b/sd/source/ui/func/fuhhconv.cxx @@ -0,0 +1,256 @@ +/* -*- 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 <com/sun/star/i18n/TextConversionOption.hpp> + +#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp> +#include <com/sun/star/lang/XMultiComponentFactory.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <comphelper/propertysequence.hxx> +#include <cppuhelper/bootstrap.hxx> +#include <svl/style.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/langitem.hxx> +#include <editeng/fontitem.hxx> + +#include <fuhhconv.hxx> +#include <drawdoc.hxx> +#include <Outliner.hxx> +#include <DrawViewShell.hxx> +#include <OutlineViewShell.hxx> +#include <Window.hxx> +#include <ViewShellBase.hxx> + +#include <sdresid.hxx> +#include <strings.hrc> + +class SfxRequest; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; + +namespace sd { + + +FuHangulHanjaConversion::FuHangulHanjaConversion ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDocument, + SfxRequest& rReq ) + : FuPoor(pViewSh, pWin, pView, pDocument, rReq), + pSdOutliner(nullptr), + bOwnOutliner(false) +{ + if ( dynamic_cast< const DrawViewShell *>( mpViewShell ) != nullptr ) + { + bOwnOutliner = true; + pSdOutliner = new SdOutliner( mpDoc, OutlinerMode::TextObject ); + } + else if ( dynamic_cast< const OutlineViewShell *>( mpViewShell ) != nullptr ) + { + bOwnOutliner = false; + pSdOutliner = mpDoc->GetOutliner(); + } + + if (pSdOutliner) + pSdOutliner->PrepareSpelling(); +} + +FuHangulHanjaConversion::~FuHangulHanjaConversion() +{ + if (pSdOutliner) + pSdOutliner->EndConversion(); + + if (bOwnOutliner) + delete pSdOutliner; +} + +rtl::Reference<FuPoor> FuHangulHanjaConversion::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuHangulHanjaConversion( pViewSh, pWin, pView, pDoc, rReq ) ); + return xFunc; +} + +/** + * Search and replace + */ +void FuHangulHanjaConversion::StartConversion( LanguageType nSourceLanguage, LanguageType nTargetLanguage, + const vcl::Font *pTargetFont, sal_Int32 nOptions, bool bIsInteractive ) +{ + + mpView->BegUndo(SdResId(STR_UNDO_HANGULHANJACONVERSION)); + + ViewShellBase* pBase = dynamic_cast<ViewShellBase*>( SfxViewShell::Current() ); + if (pBase != nullptr) + mpViewShell = pBase->GetMainViewShell().get(); + + if( mpViewShell ) + { + if ( pSdOutliner && dynamic_cast< const DrawViewShell *>( mpViewShell ) != nullptr && !bOwnOutliner ) + { + pSdOutliner->EndConversion(); + + bOwnOutliner = true; + pSdOutliner = new SdOutliner( mpDoc, OutlinerMode::TextObject ); + pSdOutliner->BeginConversion(); + } + else if ( pSdOutliner && dynamic_cast< const OutlineViewShell *>( mpViewShell ) != nullptr && bOwnOutliner ) + { + pSdOutliner->EndConversion(); + delete pSdOutliner; + + bOwnOutliner = false; + pSdOutliner = mpDoc->GetOutliner(); + pSdOutliner->BeginConversion(); + } + + if (pSdOutliner) + pSdOutliner->StartConversion(nSourceLanguage, nTargetLanguage, pTargetFont, nOptions, bIsInteractive ); + } + + // Due to changing between edit mode, notes mode, and handout mode the + // view has most likely changed. Get the new one. + mpViewShell = pBase ? pBase->GetMainViewShell().get() : nullptr; + if (mpViewShell != nullptr) + { + mpView = mpViewShell->GetView(); + mpWindow = mpViewShell->GetActiveWindow(); + } + else + { + mpView = nullptr; + mpWindow = nullptr; + } + + if (mpView != nullptr) + mpView->EndUndo(); +} + +void FuHangulHanjaConversion::ConvertStyles( LanguageType nTargetLanguage, const vcl::Font *pTargetFont ) +{ + if( !mpDoc ) + return; + + SfxStyleSheetBasePool* pStyleSheetPool = mpDoc->GetStyleSheetPool(); + if( !pStyleSheetPool ) + return; + + SfxStyleSheetBase* pStyle = pStyleSheetPool->First(SfxStyleFamily::All); + while( pStyle ) + { + SfxItemSet& rSet = pStyle->GetItemSet(); + + const bool bHasParent = !pStyle->GetParent().isEmpty(); + + if( !bHasParent || rSet.GetItemState( EE_CHAR_LANGUAGE_CJK, false ) == SfxItemState::SET ) + rSet.Put( SvxLanguageItem( nTargetLanguage, EE_CHAR_LANGUAGE_CJK ) ); + + if( pTargetFont && + ( !bHasParent || rSet.GetItemState( EE_CHAR_FONTINFO_CJK, false ) == SfxItemState::SET ) ) + { + // set new font attribute + SvxFontItem aFontItem( rSet.Get( EE_CHAR_FONTINFO_CJK ) ); + aFontItem.SetFamilyName( pTargetFont->GetFamilyName()); + aFontItem.SetFamily( pTargetFont->GetFamilyType()); + aFontItem.SetStyleName( pTargetFont->GetStyleName()); + aFontItem.SetPitch( pTargetFont->GetPitch()); + aFontItem.SetCharSet( pTargetFont->GetCharSet()); + rSet.Put( aFontItem ); + } + + pStyle = pStyleSheetPool->Next(); + } + + mpDoc->SetLanguage( nTargetLanguage, EE_CHAR_LANGUAGE_CJK ); +} + +void FuHangulHanjaConversion::StartChineseConversion() +{ + //open ChineseTranslationDialog + Reference< XComponentContext > xContext( + ::cppu::defaultBootstrap_InitialComponentContext() ); //@todo get context from calc if that has one + if(!xContext.is()) + return; + + Reference< lang::XMultiComponentFactory > xMCF( xContext->getServiceManager() ); + if(!xMCF.is()) + return; + + Reference< ui::dialogs::XExecutableDialog > xDialog( + xMCF->createInstanceWithContext("com.sun.star.linguistic2.ChineseTranslationDialog" + , xContext), UNO_QUERY); + Reference< lang::XInitialization > xInit( xDialog, UNO_QUERY ); + if( xInit.is() ) + { + // initialize dialog + Reference< awt::XWindow > xDialogParentWindow; + Sequence<Any> aSeq(comphelper::InitAnyPropertySequence( + { + {"ParentWindow", uno::Any(xDialogParentWindow)} + })); + xInit->initialize( aSeq ); + + //execute dialog + sal_Int16 nDialogRet = xDialog->execute(); + if( RET_OK == nDialogRet ) + { + //get some parameters from the dialog + bool bToSimplified = true; + bool bUseVariants = true; + bool bCommonTerms = true; + Reference< beans::XPropertySet > xProp( xDialog, UNO_QUERY ); + if( xProp.is() ) + { + try + { + xProp->getPropertyValue( "IsDirectionToSimplified" ) >>= bToSimplified; + xProp->getPropertyValue( "IsUseCharacterVariants" ) >>= bUseVariants; + xProp->getPropertyValue( "IsTranslateCommonTerms" ) >>= bCommonTerms; + } + catch( Exception& ) + { + } + } + + //execute translation + LanguageType nSourceLang = bToSimplified ? LANGUAGE_CHINESE_TRADITIONAL : LANGUAGE_CHINESE_SIMPLIFIED; + LanguageType nTargetLang = bToSimplified ? LANGUAGE_CHINESE_SIMPLIFIED : LANGUAGE_CHINESE_TRADITIONAL; + sal_Int32 nOptions = bUseVariants ? i18n::TextConversionOption::USE_CHARACTER_VARIANTS : 0; + if( !bCommonTerms ) + nOptions = nOptions | i18n::TextConversionOption::CHARACTER_BY_CHARACTER; + + vcl::Font aTargetFont = OutputDevice::GetDefaultFont( + DefaultFontType::CJK_PRESENTATION, + nTargetLang, GetDefaultFontFlags::OnlyOne ); + + StartConversion( nSourceLang, nTargetLang, &aTargetFont, nOptions, false ); + ConvertStyles( nTargetLang, &aTargetFont ); + } + } + Reference< lang::XComponent > xComponent( xDialog, UNO_QUERY ); + if( xComponent.is() ) + xComponent->dispose(); +} +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuinsert.cxx b/sd/source/ui/func/fuinsert.cxx new file mode 100644 index 000000000..919814d56 --- /dev/null +++ b/sd/source/ui/func/fuinsert.cxx @@ -0,0 +1,767 @@ +/* -*- 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 <config_features.h> + +#include <fuinsert.hxx> +#include <comphelper/storagehelper.hxx> +#include <comphelper/propertysequence.hxx> +#include <editeng/sizeitem.hxx> +#include <officecfg/Office/Common.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <svx/svxdlg.hxx> +#include <com/sun/star/chart2/XChartDocument.hpp> +#include <com/sun/star/embed/EmbedVerbs.hpp> +#include <com/sun/star/embed/NoVisualAreaSizeException.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/media/XPlayer.hpp> + +#include <svl/stritem.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/msgpool.hxx> +#include <sfx2/msg.hxx> +#include <svtools/insdlg.hxx> +#include <sfx2/request.hxx> +#include <svl/globalnameitem.hxx> +#include <svtools/embedhlp.hxx> +#include <svx/linkwarn.hxx> +#include <avmedia/mediawindow.hxx> +#include <comphelper/classids.hxx> +#include <svtools/sfxecode.hxx> +#include <vcl/transfer.hxx> +#include <svl/urlbmk.hxx> +#include <svx/svdobj.hxx> +#include <svx/svdograf.hxx> +#include <svx/svdoole2.hxx> +#include <sot/formats.hxx> +#include <svx/svdpagv.hxx> +#include <sfx2/opengrf.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/charthelper.hxx> +#include <svx/svxids.hrc> + +#include <sdresid.hxx> +#include <View.hxx> +#include <sdmod.hxx> +#include <Window.hxx> +#include <DrawViewShell.hxx> +#include <DrawDocShell.hxx> +#include <GraphicDocShell.hxx> +#include <strings.hrc> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <sdgrffilter.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <vcl/errinf.hxx> +#include <vcl/graphicfilter.hxx> + +#include <vcl/GraphicNativeTransform.hxx> +#include <vcl/GraphicNativeMetadata.hxx> + +#include <comphelper/lok.hxx> + +using namespace com::sun::star; + +namespace sd { + + +FuInsertGraphic::FuInsertGraphic ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq, + bool replaceExistingImage) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq), + mbReplaceExistingImage(replaceExistingImage) +{ +} + +rtl::Reference<FuPoor> FuInsertGraphic::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, + SdDrawDocument* pDoc, SfxRequest& rReq, bool replaceExistingImage ) +{ + rtl::Reference<FuPoor> xFunc( new FuInsertGraphic( pViewSh, pWin, pView, pDoc, rReq, replaceExistingImage ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuInsertGraphic::DoExecute( SfxRequest& rReq ) +{ + OUString aFileName; + Graphic aGraphic; + + bool bAsLink = false; + ErrCode nError = ERRCODE_GRFILTER_OPENERROR; + + const SfxItemSet* pArgs = rReq.GetArgs(); + const SfxPoolItem* pItem; + + if ( pArgs && + pArgs->GetItemState( SID_INSERT_GRAPHIC, true, &pItem ) == SfxItemState::SET ) + { + aFileName = static_cast<const SfxStringItem*>(pItem)->GetValue(); + + OUString aFilterName; + if ( const SfxStringItem* pFilterItem = pArgs->GetItemIfSet( FN_PARAM_FILTER ) ) + aFilterName = pFilterItem->GetValue(); + + if ( pArgs->GetItemState( FN_PARAM_1, true, &pItem ) == SfxItemState::SET ) + bAsLink = static_cast<const SfxBoolItem*>(pItem)->GetValue(); + + nError = GraphicFilter::LoadGraphic( aFileName, aFilterName, aGraphic, &GraphicFilter::GetGraphicFilter() ); + } + else + { + SvxOpenGraphicDialog aDlg(SdResId(STR_INSERTGRAPHIC), mpWindow ? mpWindow->GetFrameWeld() : nullptr); + + if( aDlg.Execute() != ERRCODE_NONE ) + return; // cancel dialog + + nError = aDlg.GetGraphic(aGraphic); + bAsLink = aDlg.IsAsLink(); + aFileName = aDlg.GetPath(); + } + + if( nError == ERRCODE_NONE ) + { + GraphicNativeMetadata aMetadata; + if ( aMetadata.read(aGraphic) ) + { + const Degree10 aRotation = aMetadata.getRotation(); + if (aRotation) + { + GraphicNativeTransform aTransform( aGraphic ); + aTransform.rotate( aRotation ); + } + } + if( dynamic_cast< DrawViewShell *>( mpViewShell ) ) + { + sal_Int8 nAction = DND_ACTION_COPY; + SdrObject* pPickObj = nullptr; + if (mbReplaceExistingImage) + pPickObj = mpView->GetSelectedSingleObject( mpView->GetPage() ); + if (pPickObj) + nAction = DND_ACTION_LINK; + else + { + pPickObj = mpView->GetEmptyPresentationObject( PresObjKind::Graphic ); + if (pPickObj) + nAction = DND_ACTION_LINK; + } + + Point aPos = mpWindow->GetVisibleCenter(); + SdrGrafObj* pGrafObj = mpView->InsertGraphic(aGraphic, nAction, aPos, pPickObj, nullptr); + + if(pGrafObj && bAsLink ) + { + // really store as link only? + if( officecfg::Office::Common::Misc::ShowLinkWarningDialog::get() ) + { + SvxLinkWarningDialog aWarnDlg(mpWindow->GetFrameWeld(), aFileName); + if (aWarnDlg.run() != RET_OK) + return; // don't store as link + } + + // store as link + pGrafObj->SetGraphicLink(aFileName); + } + } + } + else + { + SdGRFFilter::HandleGraphicFilterError( nError, GraphicFilter::GetGraphicFilter().GetLastError() ); + } +} + +FuInsertClipboard::FuInsertClipboard ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuInsertClipboard::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuInsertClipboard( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuInsertClipboard::DoExecute( SfxRequest& ) +{ + TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( mpWindow ) ); + SotClipboardFormatId nFormatId; + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractPasteDialog> pDlg(pFact->CreatePasteDialog(mpViewShell->GetFrameWeld())); + pDlg->Insert( SotClipboardFormatId::EMBED_SOURCE, OUString() ); + pDlg->Insert( SotClipboardFormatId::LINK_SOURCE, OUString() ); + pDlg->Insert( SotClipboardFormatId::DRAWING, OUString() ); + pDlg->Insert( SotClipboardFormatId::SVXB, OUString() ); + pDlg->Insert( SotClipboardFormatId::GDIMETAFILE, OUString() ); + pDlg->Insert( SotClipboardFormatId::BITMAP, OUString() ); + pDlg->Insert( SotClipboardFormatId::NETSCAPE_BOOKMARK, OUString() ); + pDlg->Insert( SotClipboardFormatId::STRING, OUString() ); + pDlg->Insert( SotClipboardFormatId::HTML, OUString() ); + pDlg->Insert( SotClipboardFormatId::RTF, OUString() ); + pDlg->Insert( SotClipboardFormatId::RICHTEXT, OUString() ); + pDlg->Insert( SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT, OUString() ); + + //TODO/MBA: testing + nFormatId = pDlg->GetFormat( aDataHelper ); + if( nFormatId == SotClipboardFormatId::NONE || !aDataHelper.GetTransferable().is() ) + return; + + sal_Int8 nAction = DND_ACTION_COPY; + DrawViewShell* pDrViewSh = nullptr; + + if (!mpView->InsertData( aDataHelper, + mpWindow->PixelToLogic( ::tools::Rectangle( Point(), mpWindow->GetOutputSizePixel() ).Center() ), + nAction, false, nFormatId )) + { + pDrViewSh = dynamic_cast<DrawViewShell*>(mpViewShell); + } + + if (!pDrViewSh) + return; + + INetBookmark aINetBookmark( "", "" ); + + if( ( aDataHelper.HasFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ) && + aDataHelper.GetINetBookmark( SotClipboardFormatId::NETSCAPE_BOOKMARK, aINetBookmark ) ) || + ( aDataHelper.HasFormat( SotClipboardFormatId::FILEGRPDESCRIPTOR ) && + aDataHelper.GetINetBookmark( SotClipboardFormatId::FILEGRPDESCRIPTOR, aINetBookmark ) ) || + ( aDataHelper.HasFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) && + aDataHelper.GetINetBookmark( SotClipboardFormatId::UNIFORMRESOURCELOCATOR, aINetBookmark ) ) ) + { + pDrViewSh->InsertURLField( aINetBookmark.GetURL(), aINetBookmark.GetDescription(), "" ); + } +} + +FuInsertOLE::FuInsertOLE ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuInsertOLE::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuInsertOLE( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuInsertOLE::DoExecute( SfxRequest& rReq ) +{ + if ( nSlotId == SID_ATTR_TABLE || + nSlotId == SID_INSERT_DIAGRAM || + nSlotId == SID_INSERT_MATH ) + { + PresObjKind ePresObjKind = (nSlotId == SID_INSERT_DIAGRAM) ? PresObjKind::Chart : PresObjKind::Object; + + SdrObject* pPickObj = mpView->GetEmptyPresentationObject( ePresObjKind ); + + // insert diagram or Calc table + OUString aObjName; + SvGlobalName aName; + if (nSlotId == SID_INSERT_DIAGRAM) + aName = SvGlobalName( SO3_SCH_CLASSID); + else if (nSlotId == SID_ATTR_TABLE) + aName = SvGlobalName(SO3_SC_CLASSID); + else if (nSlotId == SID_INSERT_MATH) + aName = SvGlobalName(SO3_SM_CLASSID); + + uno::Reference < embed::XEmbeddedObject > xObj = mpViewShell->GetViewFrame()->GetObjectShell()-> + GetEmbeddedObjectContainer().CreateEmbeddedObject( aName.GetByteSequence(), aObjName ); + if ( xObj.is() ) + { + // Create default chart type. + uno::Reference<chart2::XChartDocument> xChartDoc(xObj->getComponent(), uno::UNO_QUERY); + if (xChartDoc.is()) + xChartDoc->createDefaultChart(); + + sal_Int64 nAspect = embed::Aspects::MSOLE_CONTENT; + + MapUnit aUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( nAspect ) ); + + ::tools::Rectangle aRect; + if( pPickObj ) + { + aRect = pPickObj->GetLogicRect(); + + awt::Size aSz; + aSz.Width = aRect.GetWidth(); + aSz.Height = aRect.GetHeight(); + xObj->setVisualAreaSize( nAspect, aSz ); + } + else + { + awt::Size aSz; + try + { + aSz = xObj->getVisualAreaSize( nAspect ); + } + catch ( embed::NoVisualAreaSizeException& ) + { + // the default size will be set later + } + + Size aSize( aSz.Width, aSz.Height ); + + if (aSize.IsEmpty()) + { + // rectangle with balanced edge ratio + aSize.setWidth( 14100 ); + aSize.setHeight( 10000 ); + Size aTmp = OutputDevice::LogicToLogic(aSize, MapMode(MapUnit::Map100thMM), MapMode(aUnit)); + aSz.Width = aTmp.Width(); + aSz.Height = aTmp.Height(); + xObj->setVisualAreaSize( nAspect, aSz ); + } + else + { + aSize = OutputDevice::LogicToLogic(aSize, MapMode(aUnit), MapMode(MapUnit::Map100thMM)); + } + + Point aPos = mpWindow->GetVisibleCenter(); + aPos.AdjustX( -(aSize.Width() / 2) ); + aPos.AdjustY( -(aSize.Height() / 2) ); + aRect = ::tools::Rectangle(aPos, aSize); + } + + SdrOle2Obj* pOleObj = new SdrOle2Obj( + mpView->getSdrModelFromSdrView(), + svt::EmbeddedObjectRef( xObj, nAspect ), + aObjName, + aRect); + SdrPageView* pPV = mpView->GetSdrPageView(); + + // if we have a pick obj we need to make this new ole a pres obj replacing the current pick obj + if( pPickObj ) + { + SdPage* pPage = static_cast< SdPage* >(pPickObj->getSdrPageFromSdrObject()); + if(pPage && pPage->IsPresObj(pPickObj)) + { + pPage->InsertPresObj( pOleObj, ePresObjKind ); + pOleObj->SetUserCall(pPickObj->GetUserCall()); + } + + // #i123468# we need to end text edit before replacing the object. There cannot yet + // being text typed (else it would not be an EmptyPresObj anymore), but it may be + // in text edit mode + if (mpView->IsTextEdit()) + { + mpView->SdrEndTextEdit(); + } + } + + bool bRet = true; + if( pPickObj ) + mpView->ReplaceObjectAtView(pPickObj, *pPV, pOleObj ); + else + bRet = mpView->InsertObjectAtView(pOleObj, *pPV, SdrInsertFlags::SETDEFLAYER); + + if (bRet && !comphelper::LibreOfficeKit::isActive()) + { + // Let the chart be activated after the inserting (unless + // via LibreOfficeKit) + if (nSlotId == SID_INSERT_DIAGRAM) + { + pOleObj->SetProgName( "StarChart"); + } + else if (nSlotId == SID_ATTR_TABLE) + { + pOleObj->SetProgName( "StarCalc" ); + } + else if (nSlotId == SID_INSERT_MATH) + { + pOleObj->SetProgName( "StarMath" ); + } + + pOleObj->SetLogicRect(aRect); + Size aTmp( OutputDevice::LogicToLogic(aRect.GetSize(), MapMode(MapUnit::Map100thMM), MapMode(aUnit)) ); + awt::Size aVisualSize; + aVisualSize.Width = aTmp.Width(); + aVisualSize.Height = aTmp.Height(); + xObj->setVisualAreaSize( nAspect, aVisualSize ); + mpViewShell->ActivateObject(pOleObj, embed::EmbedVerbs::MS_OLEVERB_SHOW); + + if (nSlotId == SID_INSERT_DIAGRAM) + { + // note, that this call modified the chart model which + // results in a change notification. So call this after + // everything else is finished. + ChartHelper::AdaptDefaultsForChart( xObj ); + } + } + } + else + { + ErrorHandler::HandleError(* new StringErrorInfo(ERRCODE_SFX_OLEGENERAL, + "" ) ); + } + } + else + { + // insert object + sal_Int64 nAspect = embed::Aspects::MSOLE_CONTENT; + bool bCreateNew = false; + uno::Reference < embed::XEmbeddedObject > xObj; + uno::Reference < embed::XStorage > xStorage = comphelper::OStorageHelper::GetTemporaryStorage(); + SvObjectServerList aServerLst; + OUString aName; + + OUString aIconMediaType; + uno::Reference< io::XInputStream > xIconMetaFile; + + const SfxGlobalNameItem* pNameItem = rReq.GetArg<SfxGlobalNameItem>(SID_INSERT_OBJECT); + if ( nSlotId == SID_INSERT_OBJECT && pNameItem ) + { + const SvGlobalName& aClassName = pNameItem->GetValue(); + xObj = mpViewShell->GetViewFrame()->GetObjectShell()-> + GetEmbeddedObjectContainer().CreateEmbeddedObject( aClassName.GetByteSequence(), aName ); + } + else + { + switch ( nSlotId ) + { + case SID_INSERT_OBJECT : + { + aServerLst.FillInsertObjects(); + if (mpDoc->GetDocumentType() == DocumentType::Draw) + { + aServerLst.Remove( GraphicDocShell::Factory().GetClassId() ); + } + else + { + aServerLst.Remove( DrawDocShell::Factory().GetClassId() ); + } + + [[fallthrough]]; + } + case SID_INSERT_FLOATINGFRAME : + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractInsertObjectDialog> pDlg( + pFact->CreateInsertObjectDialog( mpViewShell->GetFrameWeld(), SD_MOD()->GetSlotPool()->GetSlot(nSlotId)->GetCommandString(), + xStorage, &aServerLst )); + pDlg->Execute(); + bCreateNew = pDlg->IsCreateNew(); + xObj = pDlg->GetObject(); + + xIconMetaFile = pDlg->GetIconIfIconified( &aIconMediaType ); + if ( xIconMetaFile.is() ) + nAspect = embed::Aspects::MSOLE_ICON; + + if ( xObj.is() ) + mpViewShell->GetObjectShell()->GetEmbeddedObjectContainer().InsertEmbeddedObject( xObj, aName ); + + break; + } + } + } + + try + { + if (xObj.is()) + { + bool bInsertNewObject = true; + + Size aSize; + MapUnit aMapUnit = MapUnit::Map100thMM; + if ( nAspect != embed::Aspects::MSOLE_ICON ) + { + awt::Size aSz; + try + { + aSz = xObj->getVisualAreaSize( nAspect ); + } + catch( embed::NoVisualAreaSizeException& ) + { + // the default size will be set later + } + + aSize =Size( aSz.Width, aSz.Height ); + + aMapUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( nAspect ) ); + if (aSize.IsEmpty()) + { + // rectangle with balanced edge ratio + aSize.setWidth( 14100 ); + aSize.setHeight( 10000 ); + Size aTmp = OutputDevice::LogicToLogic(aSize, MapMode(MapUnit::Map100thMM), MapMode(aMapUnit)); + aSz.Width = aTmp.Width(); + aSz.Height = aTmp.Height(); + xObj->setVisualAreaSize( nAspect, aSz ); + } + else + { + aSize = OutputDevice::LogicToLogic(aSize, MapMode(aMapUnit), MapMode(MapUnit::Map100thMM)); + } + } + + if ( mpView->AreObjectsMarked() ) + { + // as an empty OLE object available? + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + if (rMarkList.GetMarkCount() == 1) + { + SdrMark* pMark = rMarkList.GetMark(0); + SdrObject* pObj = pMark->GetMarkedSdrObj(); + + if (pObj->GetObjInventor() == SdrInventor::Default && + pObj->GetObjIdentifier() == SdrObjKind::OLE2) + { + if ( !static_cast<SdrOle2Obj*>(pObj)->GetObjRef().is() ) + { + // the empty OLE object gets a new IPObj + bInsertNewObject = false; + pObj->SetEmptyPresObj(false); + static_cast<SdrOle2Obj*>(pObj)->SetOutlinerParaObject(std::nullopt); + static_cast<SdrOle2Obj*>(pObj)->SetObjRef(xObj); + static_cast<SdrOle2Obj*>(pObj)->SetPersistName(aName); + static_cast<SdrOle2Obj*>(pObj)->SetName(aName); + static_cast<SdrOle2Obj*>(pObj)->SetAspect(nAspect); + ::tools::Rectangle aRect = static_cast<SdrOle2Obj*>(pObj)->GetLogicRect(); + + if ( nAspect == embed::Aspects::MSOLE_ICON ) + { + if( xIconMetaFile.is() ) + static_cast<SdrOle2Obj*>(pObj)->SetGraphicToObj( xIconMetaFile, aIconMediaType ); + } + else + { + Size aTmp = OutputDevice::LogicToLogic(aRect.GetSize(), MapMode(MapUnit::Map100thMM), MapMode(aMapUnit)); + awt::Size aSz( aTmp.Width(), aTmp.Height() ); + xObj->setVisualAreaSize( nAspect, aSz ); + } + } + } + } + } + + if (bInsertNewObject) + { + // we create a new OLE object + SdrPageView* pPV = mpView->GetSdrPageView(); + Size aPageSize = pPV->GetPage()->GetSize(); + + // get the size from the iconified object + ::svt::EmbeddedObjectRef aObjRef( xObj, nAspect ); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + { + aObjRef.SetGraphicStream( xIconMetaFile, aIconMediaType ); + MapMode aMapMode( MapUnit::Map100thMM ); + aSize = aObjRef.GetSize( &aMapMode ); + } + + Point aPnt ((aPageSize.Width() - aSize.Width()) / 2, + (aPageSize.Height() - aSize.Height()) / 2); + ::tools::Rectangle aRect (aPnt, aSize); + SdrOle2Obj* pObj = new SdrOle2Obj( + mpView->getSdrModelFromSdrView(), + aObjRef, + aName, + aRect); + + if( mpView->InsertObjectAtView(pObj, *pPV, SdrInsertFlags::SETDEFLAYER) ) + { + // Math objects change their object size during InsertObject. + // New size must be set in SdrObject, or a wrong scale will be set at + // ActivateObject. + + if ( nAspect != embed::Aspects::MSOLE_ICON ) + { + try + { + awt::Size aSz = xObj->getVisualAreaSize( nAspect ); + + Size aNewSize = OutputDevice::LogicToLogic( Size( aSz.Width, aSz.Height ), + MapMode( aMapUnit ), MapMode( MapUnit::Map100thMM ) ); + if ( aNewSize != aSize ) + { + aRect.SetSize( aNewSize ); + pObj->SetLogicRect( aRect ); + } + } + catch( embed::NoVisualAreaSizeException& ) + {} + } + + if (bCreateNew) + { + pObj->SetLogicRect(aRect); + + if ( nAspect != embed::Aspects::MSOLE_ICON ) + { + Size aTmp = OutputDevice::LogicToLogic(aRect.GetSize(), MapMode(MapUnit::Map100thMM), MapMode(aMapUnit)); + awt::Size aSz( aTmp.Width(), aTmp.Height() ); + xObj->setVisualAreaSize( nAspect, aSz ); + } + + mpViewShell->ActivateObject(pObj, embed::EmbedVerbs::MS_OLEVERB_SHOW); + } + + Size aVisSizePixel = mpWindow->GetOutputSizePixel(); + ::tools::Rectangle aVisAreaWin = mpWindow->PixelToLogic( ::tools::Rectangle( Point(0,0), aVisSizePixel) ); + mpViewShell->VisAreaChanged(aVisAreaWin); + mpDocSh->SetVisArea(aVisAreaWin); + } + } + } + } + catch (uno::Exception&) + { + // For some reason the object can not be inserted. For example + // because it is password protected and is not properly unlocked. + } + } +} + +FuInsertAVMedia::FuInsertAVMedia( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuInsertAVMedia::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuInsertAVMedia( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuInsertAVMedia::DoExecute( SfxRequest& rReq ) +{ +#if HAVE_FEATURE_AVMEDIA + OUString aURL; + const SfxItemSet* pReqArgs = rReq.GetArgs(); + bool bAPI = false; + + const SvxSizeItem* pSizeItem = rReq.GetArg<SvxSizeItem>(FN_PARAM_1); + const SfxBoolItem* pLinkItem = rReq.GetArg<SfxBoolItem>(FN_PARAM_2); + const bool bSizeUnknown = !pSizeItem; + Size aPrefSize; + + if( pReqArgs ) + { + const SfxStringItem* pStringItem = dynamic_cast<const SfxStringItem*>( &pReqArgs->Get( rReq.GetSlot() ) ); + + if( pStringItem ) + { + aURL = pStringItem->GetValue(); + bAPI = !aURL.isEmpty(); + } + } + + bool bLink(pLinkItem ? pLinkItem->GetValue() : true); + if (!(bAPI + || ::avmedia::MediaWindow::executeMediaURLDialog(mpWindow ? mpWindow->GetFrameWeld() : nullptr, aURL, & bLink) + )) + return; + + if (!bSizeUnknown) + { + aPrefSize = pSizeItem->GetSize(); + } + else + { + // If we don't have a size then try and find that out, the resulted might be deliver async, so dispatch a follow up + // effort to insert the video, this time with a size. + if( mpWindow ) + mpWindow->EnterWait(); + + css::uno::Reference<css::frame::XDispatchProvider> xDispatchProvider(mpViewShell->GetViewFrame()->GetFrame().GetFrameInterface(), css::uno::UNO_QUERY); + + rtl::Reference<avmedia::PlayerListener> xPlayerListener(new avmedia::PlayerListener( + [xDispatchProvider, aURL, bLink](const css::uno::Reference<css::media::XPlayer>& rPlayer){ + css::awt::Size aSize = rPlayer->getPreferredPlayerWindowSize(); + avmedia::MediaWindow::dispatchInsertAVMedia(xDispatchProvider, aSize, aURL, bLink); + })); + + const bool bIsMediaURL = ::avmedia::MediaWindow::isMediaURL(aURL, "", true, xPlayerListener); + + if( mpWindow ) + mpWindow->LeaveWait(); + + if (!bIsMediaURL && !bAPI) + ::avmedia::MediaWindow::executeFormatErrorBox(mpWindow->GetFrameWeld()); + + return; + } + + InsertMediaURL(aURL, aPrefSize, bLink); + +#else + (void)rReq; +#endif +} + +#if HAVE_FEATURE_AVMEDIA +void FuInsertAVMedia::InsertMediaURL(const OUString& rURL, const Size& rPrefSize, bool bLink) +{ + if( mpWindow ) + mpWindow->EnterWait(); + + Point aPos; + Size aSize; + sal_Int8 nAction = DND_ACTION_COPY; + + if (rPrefSize.Width() && rPrefSize.Height()) + { + if( mpWindow ) + aSize = mpWindow->PixelToLogic(rPrefSize, MapMode(MapUnit::Map100thMM)); + else + aSize = Application::GetDefaultDevice()->PixelToLogic(rPrefSize, MapMode(MapUnit::Map100thMM)); + } + else + aSize = Size( 5000, 5000 ); + + if( mpWindow ) + { + aPos = mpWindow->PixelToLogic( ::tools::Rectangle( aPos, mpWindow->GetOutputSizePixel() ).Center() ); + aPos.AdjustX( -(aSize.Width() >> 1) ); + aPos.AdjustY( -(aSize.Height() >> 1) ); + } + + mpView->InsertMediaURL(rURL, nAction, aPos, aSize, bLink); + + if( mpWindow ) + mpWindow->LeaveWait(); +} +#endif + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuinsfil.cxx b/sd/source/ui/func/fuinsfil.cxx new file mode 100644 index 000000000..6569514cf --- /dev/null +++ b/sd/source/ui/func/fuinsfil.cxx @@ -0,0 +1,725 @@ +/* -*- 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 <fuinsfil.hxx> +#include <vcl/svapp.hxx> +#include <sfx2/progress.hxx> +#include <editeng/outliner.hxx> +#include <editeng/outlobj.hxx> +#include <editeng/editeng.hxx> +#include <svl/stritem.hxx> +#include <sfx2/request.hxx> +#include <sfx2/app.hxx> +#include <vcl/weld.hxx> +#include <svx/svdorect.hxx> +#include <svx/svdundo.hxx> +#include <svx/svdoutl.hxx> +#include <sfx2/filedlghelper.hxx> +#include <sot/formats.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/fcontnr.hxx> +#include <svx/svdpagv.hxx> +#include <svx/svxids.hrc> +#include <tools/debug.hxx> +#include <com/sun/star/ui/dialogs/XFilterManager.hpp> +#include <com/sun/star/ui/dialogs/XFilePicker.hpp> +#include <com/sun/star/ui/dialogs/XFilePicker3.hpp> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> + +#include <sdresid.hxx> +#include <drawdoc.hxx> +#include <Window.hxx> +#include <View.hxx> +#include <strings.hrc> +#include <sdmod.hxx> +#include <sdpage.hxx> +#include <ViewShellBase.hxx> +#include <DrawViewShell.hxx> +#include <OutlineView.hxx> +#include <DrawDocShell.hxx> +#include <GraphicDocShell.hxx> +#include <app.hrc> +#include <Outliner.hxx> +#include <sdabstdlg.hxx> +#include <memory> + +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::ui::dialogs; +using namespace ::com::sun::star; + +typedef ::std::pair< OUString, OUString > FilterDesc; + +namespace +{ + +OUString lcl_GetExtensionsList ( ::std::vector< FilterDesc > const& rFilterDescList ) +{ + OUStringBuffer aExtensions; + + for (const auto& rFilterDesc : rFilterDescList) + { + OUString sWildcard = rFilterDesc.second; + + if ( aExtensions.indexOf( sWildcard ) == -1 ) + { + if ( !aExtensions.isEmpty() ) + aExtensions.append(";"); + aExtensions.append(sWildcard); + } + + } + + return aExtensions.makeStringAndClear(); +} + +void lcl_AddFilter ( ::std::vector< FilterDesc >& rFilterDescList, + const std::shared_ptr<const SfxFilter>& pFilter ) +{ + if (pFilter) + rFilterDescList.emplace_back( pFilter->GetUIName(), pFilter->GetDefaultExtension() ); +} + +} + +namespace sd { + + +FuInsertFile::FuInsertFile ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuInsertFile::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuInsertFile( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuInsertFile::DoExecute( SfxRequest& rReq ) +{ + SfxFilterMatcher& rMatcher = SfxGetpApp()->GetFilterMatcher(); + ::std::vector< FilterDesc > aFilterVector; + ::std::vector< OUString > aOtherFilterVector; + const SfxItemSet* pArgs = rReq.GetArgs (); + + FuInsertFile::GetSupportedFilterVector( aOtherFilterVector ); + + if (!pArgs) + { + sfx2::FileDialogHelper aFileDialog( + ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE, + FileDialogFlags::Insert, mpWindow ? mpWindow->GetFrameWeld() : nullptr); + aFileDialog.SetContext(sfx2::FileDialogHelper::DrawImpressInsertFile); + Reference< XFilePicker > xFilePicker( aFileDialog.GetFilePicker() ); + Reference< XFilterManager > xFilterManager( xFilePicker, UNO_QUERY ); + OUString aOwnCont; + OUString aOtherCont; + + aFileDialog.SetTitle( SdResId(STR_DLG_INSERT_PAGES_FROM_FILE) ); + + if( mpDoc->GetDocumentType() == DocumentType::Impress ) + { + aOwnCont = "simpress"; + aOtherCont = "sdraw"; + } + else + { + aOtherCont = "simpress"; + aOwnCont = "sdraw" ; + } + + SfxFilterMatcher aMatch( aOwnCont ); + + if( xFilterManager.is() ) + { + // Get filter for current format + try + { + // Get main filter + std::shared_ptr<const SfxFilter> pFilter = SfxFilter::GetDefaultFilterFromFactory( aOwnCont ); + lcl_AddFilter( aFilterVector, pFilter ); + + // get template filter + if( mpDoc->GetDocumentType() == DocumentType::Impress ) + pFilter = DrawDocShell::Factory().GetTemplateFilter(); + else + pFilter = GraphicDocShell::Factory().GetTemplateFilter(); + lcl_AddFilter( aFilterVector, pFilter ); + + // get cross filter + pFilter = SfxFilter::GetDefaultFilterFromFactory( aOtherCont ); + lcl_AddFilter( aFilterVector, pFilter ); + + // get Powerpoint filter + pFilter = aMatch.GetFilter4Extension( ".ppt" ); + lcl_AddFilter( aFilterVector, pFilter ); + + // Get other draw/impress filters + pFilter = aMatch.GetFilter4ClipBoardId( SotClipboardFormatId::STARIMPRESS_60, SfxFilterFlags::IMPORT, SfxFilterFlags::TEMPLATEPATH ); + lcl_AddFilter( aFilterVector, pFilter ); + + pFilter = aMatch.GetFilter4ClipBoardId( SotClipboardFormatId::STARIMPRESS_60, SfxFilterFlags::TEMPLATEPATH ); + lcl_AddFilter( aFilterVector, pFilter ); + + pFilter = aMatch.GetFilter4ClipBoardId( SotClipboardFormatId::STARDRAW_60, SfxFilterFlags::IMPORT, SfxFilterFlags::TEMPLATEPATH ); + lcl_AddFilter( aFilterVector, pFilter ); + + pFilter = aMatch.GetFilter4ClipBoardId( SotClipboardFormatId::STARDRAW_60, SfxFilterFlags::TEMPLATEPATH ); + lcl_AddFilter( aFilterVector, pFilter ); + + pFilter = aMatch.GetFilter4ClipBoardId( SotClipboardFormatId::STARDRAW, SfxFilterFlags::IMPORT, SfxFilterFlags::TEMPLATEPATH ); + lcl_AddFilter( aFilterVector, pFilter ); + + pFilter = aMatch.GetFilter4ClipBoardId( SotClipboardFormatId::STARDRAW, SfxFilterFlags::TEMPLATEPATH ); + lcl_AddFilter( aFilterVector, pFilter ); + + // add additional supported filters + for( const auto& rOtherFilter : aOtherFilterVector ) + { + if( ( pFilter = rMatcher.GetFilter4Mime( rOtherFilter ) ) != nullptr ) + lcl_AddFilter( aFilterVector, pFilter ); + } + + // set "All supported formats" as the default filter + OUString aAllSpec( SdResId( STR_ALL_SUPPORTED_FORMATS ) ); + OUString aExtensions = lcl_GetExtensionsList( aFilterVector ); + OUString aGUIName = aAllSpec + " (" + aExtensions + ")"; + + xFilterManager->appendFilter( aGUIName, aExtensions ); + xFilterManager->setCurrentFilter( aAllSpec ); + + // append individual filters + for( const auto& rFilter : aFilterVector ) + { + xFilterManager->appendFilter( rFilter.first, rFilter.second ); + } + + // end with "All files" as fallback + xFilterManager->appendFilter( SdResId( STR_ALL_FILES ), "*.*" ); + } + catch (const IllegalArgumentException&) + { + } + } + + if( aFileDialog.Execute() != ERRCODE_NONE ) + return; + else + { + aFilterName = aFileDialog.GetCurrentFilter(); + aFile = aFileDialog.GetPath(); + } + } + else + { + const SfxStringItem* pFileName = rReq.GetArg<SfxStringItem>(ID_VAL_DUMMY0); + const SfxStringItem* pFilterName = rReq.GetArg<SfxStringItem>(ID_VAL_DUMMY1); + + aFile = pFileName->GetValue (); + + if( pFilterName ) + aFilterName = pFilterName->GetValue (); + } + + mpDocSh->SetWaitCursor( true ); + + std::unique_ptr<SfxMedium> xMedium(new SfxMedium(aFile, StreamMode::READ | StreamMode::NOCREATE)); + std::shared_ptr<const SfxFilter> pFilter; + + SfxGetpApp()->GetFilterMatcher().GuessFilter(*xMedium, pFilter); + + bool bDrawMode = dynamic_cast< const DrawViewShell *>( mpViewShell ) != nullptr; + bool bInserted = false; + + if( pFilter ) + { + xMedium->SetFilter( pFilter ); + aFilterName = pFilter->GetFilterName(); + + if( xMedium->IsStorage() || ( xMedium->GetInStream() && SotStorage::IsStorageFile( xMedium->GetInStream() ) ) ) + { + if ( pFilter->GetServiceName() == "com.sun.star.presentation.PresentationDocument" || + pFilter->GetServiceName() == "com.sun.star.drawing.DrawingDocument" ) + { + // Draw, Impress or PowerPoint document + // the ownership of the Medium is transferred + if( bDrawMode ) + InsSDDinDrMode(xMedium.release()); + else + InsSDDinOlMode(xMedium.release()); + + // ownership of pMedium has changed in this case + bInserted = true; + } + } + else + { + bool bFound = ( ::std::find( aOtherFilterVector.begin(), aOtherFilterVector.end(), pFilter->GetMimeType() ) != aOtherFilterVector.end() ); + if( !bFound && + ( aFilterName.indexOf( "Text" ) != -1 || + aFilterName.indexOf( "Rich" ) != -1 || + aFilterName.indexOf( "RTF" ) != -1 || + aFilterName.indexOf( "HTML" ) != -1 ) ) + { + bFound = true; + } + + if( bFound ) + { + if( bDrawMode ) + InsTextOrRTFinDrMode(xMedium.get()); + else + InsTextOrRTFinOlMode(xMedium.get()); + + bInserted = true; + xMedium.reset(); + } + } + } + + mpDocSh->SetWaitCursor( false ); + + if( !bInserted ) + { + std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(mpWindow->GetFrameWeld(), + VclMessageType::Warning, VclButtonsType::Ok, SdResId(STR_READ_DATA_ERROR))); + xErrorBox->run(); + } +} + +bool FuInsertFile::InsSDDinDrMode(SfxMedium* pMedium) +{ + bool bOK = false; + + mpDocSh->SetWaitCursor( false ); + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + weld::Window* pParent = mpViewShell ? mpViewShell->GetFrameWeld() : nullptr; + ScopedVclPtr<AbstractSdInsertPagesObjsDlg> pDlg( pFact->CreateSdInsertPagesObjsDlg(pParent, mpDoc, pMedium, aFile) ); + + sal_uInt16 nRet = pDlg->Execute(); + + mpDocSh->SetWaitCursor( true ); + + if( nRet == RET_OK ) + { + /* list with page names (if NULL, then all pages) + First, insert pages */ + std::vector<OUString> aBookmarkList = pDlg->GetList( 1 ); // pages + bool bLink = pDlg->IsLink(); + SdPage* pPage = nullptr; + ::sd::View* pView = mpViewShell ? mpViewShell->GetView() : nullptr; + + if (pView) + { + if( auto pOutlineView = dynamic_cast<OutlineView *>( pView )) + { + pPage = pOutlineView->GetActualPage(); + } + else + { + pPage = static_cast<SdPage*>(pView->GetSdrPageView()->GetPage()); + } + } + + sal_uInt16 nPos = 0xFFFF; + + if (pPage && !pPage->IsMasterPage()) + { + if (pPage->GetPageKind() == PageKind::Standard) + { + nPos = pPage->GetPageNum() + 2; + } + else if (pPage->GetPageKind() == PageKind::Notes) + { + nPos = pPage->GetPageNum() + 1; + } + } + + bool bNameOK; + std::vector<OUString> aExchangeList; + std::vector<OUString> aObjectBookmarkList = pDlg->GetList( 2 ); // objects + + /* if pBookmarkList is NULL, we insert selected pages, and/or selected + objects or everything. */ + if( !aBookmarkList.empty() || aObjectBookmarkList.empty() ) + { + /* To ensure that all page names are unique, we check the ones we + want to insert and insert them into a substitution list if + necessary. + bNameOK is sal_False if the user has canceled. */ + bNameOK = mpView->GetExchangeList( aExchangeList, aBookmarkList, 0 ); + + if( bNameOK ) + bOK = mpDoc->InsertBookmarkAsPage( aBookmarkList, &aExchangeList, + bLink, false/*bReplace*/, nPos, + false, nullptr, true, true, false ); + + aBookmarkList.clear(); + aExchangeList.clear(); + } + + // to ensure ... (see above) + bNameOK = mpView->GetExchangeList( aExchangeList, aObjectBookmarkList, 1 ); + + if( bNameOK ) + bOK = mpDoc->InsertBookmarkAsObject( aObjectBookmarkList, aExchangeList, + nullptr, nullptr, false ); + + if( pDlg->IsRemoveUnnecessaryMasterPages() ) + mpDoc->RemoveUnnecessaryMasterPages(); + } + + return bOK; +} + +void FuInsertFile::InsTextOrRTFinDrMode(SfxMedium* pMedium) +{ + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSdInsertPagesObjsDlg> pDlg( pFact->CreateSdInsertPagesObjsDlg(mpViewShell->GetFrameWeld(), mpDoc, nullptr, aFile) ); + + mpDocSh->SetWaitCursor( false ); + + sal_uInt16 nRet = pDlg->Execute(); + mpDocSh->SetWaitCursor( true ); + + if( nRet != RET_OK ) + return; + + // selected file format: text, RTF or HTML (default is text) + EETextFormat nFormat = EETextFormat::Text; + + if( aFilterName.indexOf( "Rich") != -1 ) + nFormat = EETextFormat::Rtf; + else if( aFilterName.indexOf( "HTML" ) != -1 ) + nFormat = EETextFormat::Html; + + /* create our own outline since: + - it is possible that the document outliner is actually used in the + structuring mode + - the draw outliner of the drawing engine has to draw something in + between + - the global outliner could be used in SdPage::CreatePresObj */ + SdOutliner aOutliner( mpDoc, OutlinerMode::TextObject ); + + // set reference device + aOutliner.SetRefDevice( SD_MOD()->GetVirtualRefDevice() ); + + SdPage* pPage = static_cast<DrawViewShell*>(mpViewShell)->GetActualPage(); + aLayoutName = pPage->GetLayoutName(); + sal_Int32 nIndex = aLayoutName.indexOf(SD_LT_SEPARATOR); + if( nIndex != -1 ) + aLayoutName = aLayoutName.copy(0, nIndex); + + aOutliner.SetPaperSize(pPage->GetSize()); + + SvStream* pStream = pMedium->GetInStream(); + assert(pStream && "No InStream!"); + pStream->Seek( 0 ); + + ErrCode nErr = aOutliner.Read( *pStream, pMedium->GetBaseURL(), nFormat, mpDocSh->GetHeaderAttributes() ); + + if (nErr || aOutliner.GetEditEngine().GetText().isEmpty()) + { + std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(mpWindow->GetFrameWeld(), + VclMessageType::Warning, VclButtonsType::Ok, SdResId(STR_READ_DATA_ERROR))); + xErrorBox->run(); + } + else + { + // is it a master page? + if (static_cast<DrawViewShell*>(mpViewShell)->GetEditMode() == EditMode::MasterPage && + !pPage->IsMasterPage()) + { + pPage = static_cast<SdPage*>(&(pPage->TRG_GetMasterPage())); + } + + assert(pPage && "page not found"); + + // if editing is going on right now, let it flow into this text object + OutlinerView* pOutlinerView = mpView->GetTextEditOutlinerView(); + if( pOutlinerView ) + { + SdrObject* pObj = mpView->GetTextEditObject(); + if( pObj && + pObj->GetObjInventor() == SdrInventor::Default && + pObj->GetObjIdentifier() == SdrObjKind::TitleText && + aOutliner.GetParagraphCount() > 1 ) + { + // in title objects, only one paragraph is allowed + while ( aOutliner.GetParagraphCount() > 1 ) + { + Paragraph* pPara = aOutliner.GetParagraph( 0 ); + sal_uLong nLen = aOutliner.GetText( pPara ).getLength(); + aOutliner.QuickDelete( ESelection( 0, nLen, 1, 0 ) ); + aOutliner.QuickInsertLineBreak( ESelection( 0, nLen, 0, nLen ) ); + } + } + } + + std::optional<OutlinerParaObject> pOPO = aOutliner.CreateParaObject(); + + if (pOutlinerView) + { + pOutlinerView->InsertText(*pOPO); + } + else + { + SdrRectObj* pTO = new SdrRectObj( + mpView->getSdrModelFromSdrView(), + SdrObjKind::Text); + pTO->SetOutlinerParaObject(std::move(pOPO)); + + const bool bUndo = mpView->IsUndoEnabled(); + if( bUndo ) + mpView->BegUndo(SdResId(STR_UNDO_INSERT_TEXTFRAME)); + pPage->InsertObject(pTO); + + /* can be bigger as the maximal allowed size: + limit object size if necessary */ + Size aSize(aOutliner.CalcTextSize()); + Size aMaxSize = mpDoc->GetMaxObjSize(); + aSize.setHeight( std::min(aSize.Height(), aMaxSize.Height()) ); + aSize.setWidth( std::min(aSize.Width(), aMaxSize.Width()) ); + aSize = mpWindow->LogicToPixel(aSize); + + // put it at the center of the window + Size aTemp(mpWindow->GetOutputSizePixel()); + Point aPos(aTemp.Width() / 2, aTemp.Height() / 2); + aPos.AdjustX( -(aSize.Width() / 2) ); + aPos.AdjustY( -(aSize.Height() / 2) ); + aSize = mpWindow->PixelToLogic(aSize); + aPos = mpWindow->PixelToLogic(aPos); + pTO->SetLogicRect(::tools::Rectangle(aPos, aSize)); + + if (pDlg->IsLink()) + { + pTO->SetTextLink(aFile, aFilterName ); + } + + if( bUndo ) + { + mpView->AddUndo(mpDoc->GetSdrUndoFactory().CreateUndoInsertObject(*pTO)); + mpView->EndUndo(); + } + } + } +} + +void FuInsertFile::InsTextOrRTFinOlMode(SfxMedium* pMedium) +{ + // selected file format: text, RTF or HTML (default is text) + EETextFormat nFormat = EETextFormat::Text; + + if( aFilterName.indexOf( "Rich") != -1 ) + nFormat = EETextFormat::Rtf; + else if( aFilterName.indexOf( "HTML" ) != -1 ) + nFormat = EETextFormat::Html; + + ::Outliner& rDocliner = static_cast<OutlineView*>(mpView)->GetOutliner(); + + std::vector<Paragraph*> aSelList; + rDocliner.GetView(0)->CreateSelectionList(aSelList); + + Paragraph* pPara = aSelList.empty() ? nullptr : *(aSelList.begin()); + + // what should we insert? + while (pPara && !Outliner::HasParaFlag(pPara, ParaFlag::ISPAGE)) + pPara = rDocliner.GetParent(pPara); + + sal_Int32 nTargetPos = rDocliner.GetAbsPos(pPara) + 1; + + // apply layout of predecessor page + sal_uInt16 nPage = 0; + pPara = rDocliner.GetParagraph( rDocliner.GetAbsPos( pPara ) - 1 ); + while (pPara) + { + sal_Int32 nPos = rDocliner.GetAbsPos( pPara ); + if ( Outliner::HasParaFlag( pPara, ParaFlag::ISPAGE ) ) + nPage++; + pPara = rDocliner.GetParagraph( nPos - 1 ); + } + SdPage* pPage = mpDoc->GetSdPage(nPage, PageKind::Standard); + aLayoutName = pPage->GetLayoutName(); + sal_Int32 nIndex = aLayoutName.indexOf(SD_LT_SEPARATOR); + if( nIndex != -1 ) + aLayoutName = aLayoutName.copy(0, nIndex); + + /* create our own outline since: + - it is possible that the document outliner is actually used in the + structuring mode + - the draw outliner of the drawing engine has to draw something in + between + - the global outliner could be used in SdPage::CreatePresObj */ + ::Outliner aOutliner( &mpDoc->GetItemPool(), OutlinerMode::OutlineObject ); + aOutliner.SetStyleSheetPool(static_cast<SfxStyleSheetPool*>(mpDoc->GetStyleSheetPool())); + + // set reference device + aOutliner.SetRefDevice(SD_MOD()->GetVirtualRefDevice()); + aOutliner.SetPaperSize(Size(0x7fffffff, 0x7fffffff)); + + SvStream* pStream = pMedium->GetInStream(); + DBG_ASSERT( pStream, "No InStream!" ); + pStream->Seek( 0 ); + + ErrCode nErr = aOutliner.Read(*pStream, pMedium->GetBaseURL(), nFormat, mpDocSh->GetHeaderAttributes()); + + if (nErr || aOutliner.GetEditEngine().GetText().isEmpty()) + { + std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(mpWindow->GetFrameWeld(), + VclMessageType::Warning, VclButtonsType::Ok, SdResId(STR_READ_DATA_ERROR))); + xErrorBox->run(); + } + else + { + sal_Int32 nParaCount = aOutliner.GetParagraphCount(); + + // for progress bar: number of level-0-paragraphs + sal_uInt16 nNewPages = 0; + pPara = aOutliner.GetParagraph( 0 ); + while (pPara) + { + sal_Int32 nPos = aOutliner.GetAbsPos( pPara ); + if( Outliner::HasParaFlag( pPara, ParaFlag::ISPAGE ) ) + nNewPages++; + pPara = aOutliner.GetParagraph( ++nPos ); + } + + mpDocSh->SetWaitCursor( false ); + + std::optional<SfxProgress> pProgress( std::in_place, mpDocSh, SdResId(STR_CREATE_PAGES), nNewPages); + pProgress->SetState( 0, 100 ); + + nNewPages = 0; + + ViewShellId nViewShellId = mpViewShell ? mpViewShell->GetViewShellBase().GetViewShellId() : ViewShellId(-1); + rDocliner.GetUndoManager().EnterListAction( + SdResId(STR_UNDO_INSERT_FILE), OUString(), 0, nViewShellId ); + + sal_Int32 nSourcePos = 0; + SfxStyleSheet* pStyleSheet = pPage->GetStyleSheetForPresObj( PresObjKind::Outline ); + Paragraph* pSourcePara = aOutliner.GetParagraph( 0 ); + while (pSourcePara) + { + sal_Int32 nPos = aOutliner.GetAbsPos( pSourcePara ); + sal_Int16 nDepth = aOutliner.GetDepth( nPos ); + + // only take the last paragraph if it is filled + if (nSourcePos < nParaCount - 1 || + !aOutliner.GetText(pSourcePara).isEmpty()) + { + rDocliner.Insert( aOutliner.GetText(pSourcePara), nTargetPos, nDepth ); + OUString aStyleSheetName( pStyleSheet->GetName() ); + aStyleSheetName = aStyleSheetName.subView( 0, aStyleSheetName.getLength()-1 ) + + OUString::number( nDepth <= 0 ? 1 : nDepth+1 ); + SfxStyleSheetBasePool* pStylePool = mpDoc->GetStyleSheetPool(); + SfxStyleSheet* pOutlStyle = static_cast<SfxStyleSheet*>( pStylePool->Find( aStyleSheetName, pStyleSheet->GetFamily() ) ); + rDocliner.SetStyleSheet( nTargetPos, pOutlStyle ); + } + + if( Outliner::HasParaFlag( pSourcePara, ParaFlag::ISPAGE ) ) + { + nNewPages++; + pProgress->SetState( nNewPages ); + } + + pSourcePara = aOutliner.GetParagraph( ++nPos ); + nTargetPos++; + nSourcePos++; + } + + rDocliner.GetUndoManager().LeaveListAction(); + + pProgress.reset(); + + mpDocSh->SetWaitCursor( true ); + } +} + +bool FuInsertFile::InsSDDinOlMode(SfxMedium* pMedium) +{ + OutlineView* pOlView = static_cast<OutlineView*>(mpView); + + // transfer Outliner content to SdDrawDocument + pOlView->PrepareClose(); + + // read in like in the character mode + if (InsSDDinDrMode(pMedium)) + { + ::Outliner* pOutliner = pOlView->GetViewByWindow(mpWindow)->GetOutliner(); + + // cut notification links temporarily + Link<Outliner::ParagraphHdlParam,void> aOldParagraphInsertedHdl = pOutliner->GetParaInsertedHdl(); + pOutliner->SetParaInsertedHdl( Link<Outliner::ParagraphHdlParam,void>()); + Link<Outliner::ParagraphHdlParam,void> aOldParagraphRemovingHdl = pOutliner->GetParaRemovingHdl(); + pOutliner->SetParaRemovingHdl( Link<Outliner::ParagraphHdlParam,void>()); + Link<Outliner::DepthChangeHdlParam,void> aOldDepthChangedHdl = pOutliner->GetDepthChangedHdl(); + pOutliner->SetDepthChangedHdl( Link<::Outliner::DepthChangeHdlParam,void>()); + Link<::Outliner*,void> aOldBeginMovingHdl = pOutliner->GetBeginMovingHdl(); + pOutliner->SetBeginMovingHdl( Link<::Outliner*,void>()); + Link<::Outliner*,void> aOldEndMovingHdl = pOutliner->GetEndMovingHdl(); + pOutliner->SetEndMovingHdl( Link<::Outliner*,void>()); + + Link<EditStatus&,void> aOldStatusEventHdl = pOutliner->GetStatusEventHdl(); + pOutliner->SetStatusEventHdl(Link<EditStatus&,void>()); + + pOutliner->Clear(); + pOlView->FillOutliner(); + + // set links again + pOutliner->SetParaInsertedHdl(aOldParagraphInsertedHdl); + pOutliner->SetParaRemovingHdl(aOldParagraphRemovingHdl); + pOutliner->SetDepthChangedHdl(aOldDepthChangedHdl); + pOutliner->SetBeginMovingHdl(aOldBeginMovingHdl); + pOutliner->SetEndMovingHdl(aOldEndMovingHdl); + pOutliner->SetStatusEventHdl(aOldStatusEventHdl); + + return true; + } + else + return false; +} + +void FuInsertFile::GetSupportedFilterVector( ::std::vector< OUString >& rFilterVector ) +{ + SfxFilterMatcher& rMatcher = SfxGetpApp()->GetFilterMatcher(); + std::shared_ptr<const SfxFilter> pSearchFilter; + + rFilterVector.clear(); + + if( ( pSearchFilter = rMatcher.GetFilter4Mime( "text/plain" )) != nullptr ) + rFilterVector.push_back( pSearchFilter->GetMimeType() ); + + if( ( pSearchFilter = rMatcher.GetFilter4Mime( "application/rtf" ) ) != nullptr ) + rFilterVector.push_back( pSearchFilter->GetMimeType() ); + + if( ( pSearchFilter = rMatcher.GetFilter4Mime( "text/html" ) ) != nullptr ) + rFilterVector.push_back( pSearchFilter->GetMimeType() ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuline.cxx b/sd/source/ui/func/fuline.cxx new file mode 100644 index 000000000..da9cc795f --- /dev/null +++ b/sd/source/ui/func/fuline.cxx @@ -0,0 +1,109 @@ +/* -*- 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 <fuline.hxx> + +#include <svx/svxids.hrc> +#include <sfx2/request.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <ViewShell.hxx> +#include <View.hxx> +#include <drawdoc.hxx> +#include <svx/svxdlg.hxx> + +namespace sd { + + +FuLine::FuLine ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuLine::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuLine( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuLine::DoExecute( SfxRequest& rReq ) +{ + rReq.Ignore(); + + const SfxItemSet* pArgs = rReq.GetArgs(); + if (pArgs) + return; + + const SdrObject* pObj = nullptr; + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + if( rMarkList.GetMarkCount() == 1 ) + pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + + SfxItemSet aNewAttr( mpDoc->GetPool() ); + mpView->GetAttributes( aNewAttr ); + + bool bHasMarked = mpView->AreObjectsMarked(); + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + VclPtr<SfxAbstractTabDialog> pDlg( pFact->CreateSvxLineTabDialog(mpViewShell->GetFrameWeld(), &aNewAttr, mpDoc, pObj, bHasMarked) ); + + pDlg->StartExecuteAsync([pDlg, this](sal_Int32 nResult){ + if (nResult == RET_OK) + { + mpView->SetAttributes (*(pDlg->GetOutputItemSet ())); + + // some attributes are changed, we have to update the listboxes in the objectbars + static const sal_uInt16 SidArray[] = { + SID_ATTR_LINE_STYLE, // ( SID_SVX_START + 169 ) + SID_ATTR_LINE_DASH, // ( SID_SVX_START + 170 ) + SID_ATTR_LINE_WIDTH, // ( SID_SVX_START + 171 ) + SID_ATTR_LINE_COLOR, // ( SID_SVX_START + 172 ) + SID_ATTR_LINE_START, // ( SID_SVX_START + 173 ) + SID_ATTR_LINE_END, // ( SID_SVX_START + 174 ) + SID_ATTR_LINE_TRANSPARENCE, // (SID_SVX_START+1107) + SID_ATTR_LINE_JOINT, // (SID_SVX_START+1110) + SID_ATTR_LINE_CAP, // (SID_SVX_START+1111) + 0 }; + + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SidArray ); + } + + // deferred until the dialog ends + mpViewShell->Cancel(); + + pDlg->disposeOnce(); + }); +} + +void FuLine::Activate() +{ +} + +void FuLine::Deactivate() +{ +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fulinend.cxx b/sd/source/ui/func/fulinend.cxx new file mode 100644 index 000000000..34fe63161 --- /dev/null +++ b/sd/source/ui/func/fulinend.cxx @@ -0,0 +1,154 @@ +/* -*- 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 <fulinend.hxx> +#include <svx/xtable.hxx> +#include <svx/svxdlg.hxx> +#include <svx/svdobj.hxx> +#include <svx/svdopath.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> + +#include <strings.hrc> +#include <helpids.h> +#include <sdresid.hxx> +#include <drawdoc.hxx> +#include <View.hxx> +#include <Window.hxx> +#include <memory> + +namespace sd { + + +FuLineEnd::FuLineEnd(ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, + SdDrawDocument* pDoc, SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuLineEnd::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuLineEnd( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuLineEnd::DoExecute( SfxRequest& ) +{ + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + if( rMarkList.GetMarkCount() != 1 ) + return; + + const SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + const SdrObject* pNewObj; + SdrObjectUniquePtr pConvPolyObj; + + if( dynamic_cast< const SdrPathObj *>( pObj ) != nullptr ) + { + pNewObj = pObj; + } + else + { + SdrObjTransformInfoRec aInfoRec; + pObj->TakeObjInfo( aInfoRec ); + + if( aInfoRec.bCanConvToPath && + pObj->GetObjInventor() == SdrInventor::Default && + pObj->GetObjIdentifier() != SdrObjKind::Group ) + // bCanConvToPath is sal_True for group objects, + // but it crashes on ConvertToPathObj()! + { + pConvPolyObj = pObj->ConvertToPolyObj( true, false ); + pNewObj = pConvPolyObj.get(); + + if( !pNewObj || dynamic_cast< const SdrPathObj *>( pNewObj ) == nullptr ) + return; // Cancel, additional security, but it does not help + // for group objects + } + else return; // Cancel + } + + const ::basegfx::B2DPolyPolygon aPolyPolygon = static_cast<const SdrPathObj*>(pNewObj)->GetPathPoly(); + + // Delete the created poly-object + pConvPolyObj.reset(); + + XLineEndListRef pLineEndList = mpDoc->GetLineEndList(); + + OUString aNewName( SdResId( STR_LINEEND ) ); + OUString aDesc( SdResId( STR_DESC_LINEEND ) ); + OUString aName; + + ::tools::Long nCount = pLineEndList->Count(); + ::tools::Long j = 1; + bool bDifferent = false; + + while( !bDifferent ) + { + aName = aNewName + " " + OUString::number(j++); + bDifferent = true; + for( ::tools::Long i = 0; i < nCount && bDifferent; i++ ) + { + if( aName == pLineEndList->GetLineEnd( i )->GetName() ) + bDifferent = false; + } + } + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSvxNameDialog> pDlg( pFact->CreateSvxNameDialog( nullptr, aName, aDesc ) ); + + pDlg->SetEditHelpId( HID_SD_NAMEDIALOG_LINEEND ); + + if( pDlg->Execute() != RET_OK ) + return; + + pDlg->GetName( aName ); + bDifferent = true; + + for( ::tools::Long i = 0; i < nCount && bDifferent; i++ ) + { + if( aName == pLineEndList->GetLineEnd( i )->GetName() ) + bDifferent = false; + } + + if( bDifferent ) + { + pLineEndList->Insert(std::make_unique<XLineEndEntry>(aPolyPolygon, aName)); + } + else + { + std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(mpWindow ? mpWindow->GetFrameWeld() : nullptr, + VclMessageType::Warning, VclButtonsType::Ok, + SdResId(STR_WARN_NAME_DUPLICATE))); + xWarn->run(); + } +} + +void FuLineEnd::Activate() +{ +} + +void FuLineEnd::Deactivate() +{ +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fulink.cxx b/sd/source/ui/func/fulink.cxx new file mode 100644 index 000000000..8a6d726de --- /dev/null +++ b/sd/source/ui/func/fulink.cxx @@ -0,0 +1,65 @@ +/* -*- 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 <fulink.hxx> + +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> + +#include <svx/svxdlg.hxx> + +#include <drawdoc.hxx> +#include <ViewShell.hxx> +#include <app.hrc> + +class SfxRequest; + +namespace sd { + + +FuLink::FuLink ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq ) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuLink::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuLink( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuLink::DoExecute( SfxRequest& ) +{ + sfx2::LinkManager* pLinkManager = mpDoc->GetLinkManager(); + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractLinksDialog> pDlg(pFact->CreateLinksDialog(mpViewShell->GetFrameWeld(), pLinkManager)); + pDlg->Execute(); + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_MANAGE_LINKS ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fumeasur.cxx b/sd/source/ui/func/fumeasur.cxx new file mode 100644 index 000000000..27afd0f7a --- /dev/null +++ b/sd/source/ui/func/fumeasur.cxx @@ -0,0 +1,72 @@ +/* -*- 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 <fumeasur.hxx> +#include <sfx2/request.hxx> +#include <View.hxx> +#include <drawdoc.hxx> +#include <svx/svxdlg.hxx> +#include <svx/dialogs.hrc> + +namespace sd { + + +FuMeasureDlg::FuMeasureDlg ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuMeasureDlg::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuMeasureDlg( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuMeasureDlg::DoExecute( SfxRequest& rReq ) +{ + SfxItemSet aNewAttr( mpDoc->GetPool() ); + mpView->GetAttributes( aNewAttr ); + + const SfxItemSet* pArgs = rReq.GetArgs(); + + if( !pArgs ) + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractDialog> pDlg(pFact->CreateSfxDialog(rReq.GetFrameWeld(), aNewAttr, mpView, RID_SVXPAGE_MEASURE)); + + if( pDlg->Execute() == RET_OK ) + { + rReq.Done( *pDlg->GetOutputItemSet() ); + pArgs = rReq.GetArgs(); + } + } + + if( pArgs ) + mpView->SetAttributes( *pArgs ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fumorph.cxx b/sd/source/ui/func/fumorph.cxx new file mode 100644 index 000000000..c2f94b440 --- /dev/null +++ b/sd/source/ui/func/fumorph.cxx @@ -0,0 +1,508 @@ +/* -*- 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 <fumorph.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xlineit0.hxx> +#include <svx/xlnclit.hxx> +#include <svx/xlnwtit.hxx> +#include <svx/xflclit.hxx> +#include <svx/svdopath.hxx> +#include <svx/svdogrp.hxx> +#include <editeng/eeitem.hxx> + +#include <View.hxx> +#include <Window.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> + +#include <strings.hrc> +#include <sdresid.hxx> + +#include <sdabstdlg.hxx> + +#include <svx/svditer.hxx> + +#include <basegfx/color/bcolor.hxx> +#include <com/sun/star/drawing/LineStyle.hpp> + +using namespace com::sun::star; + +namespace sd { + +FuMorph::FuMorph ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq ) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuMorph::Create( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq +) +{ + rtl::Reference<FuPoor> xFunc( new FuMorph( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuMorph::DoExecute( SfxRequest& ) +{ + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + if(rMarkList.GetMarkCount() != 2) + return; + + // create clones + SdrObject* pObj1 = rMarkList.GetMark(0)->GetMarkedSdrObj(); + SdrObject* pObj2 = rMarkList.GetMark(1)->GetMarkedSdrObj(); + SdrObject* pCloneObj1(pObj1->CloneSdrObject(pObj1->getSdrModelFromSdrObject())); + SdrObject* pCloneObj2(pObj2->CloneSdrObject(pObj2->getSdrModelFromSdrObject())); + + // delete text at clone, otherwise we do not get a correct PathObj + pCloneObj1->SetOutlinerParaObject(std::nullopt); + pCloneObj2->SetOutlinerParaObject(std::nullopt); + + // create path objects + SdrObjectUniquePtr pPolyObj1 = pCloneObj1->ConvertToPolyObj(false, false); + SdrObjectUniquePtr pPolyObj2 = pCloneObj2->ConvertToPolyObj(false, false); + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractMorphDlg> pDlg( pFact->CreateMorphDlg(mpWindow ? mpWindow->GetFrameWeld() : nullptr, pObj1, pObj2) ); + if(pPolyObj1 && pPolyObj2 && (pDlg->Execute() == RET_OK)) + { + B2DPolyPolygonList_impl aPolyPolyList; + ::basegfx::B2DPolyPolygon aPolyPoly1; + ::basegfx::B2DPolyPolygon aPolyPoly2; + + pDlg->SaveSettings(); + + // #i48168# Not always is the pPolyObj1/pPolyObj2 a SdrPathObj, it may also be a group object + // containing SdrPathObjs. To get the polygons, I add two iters here + SdrObjListIter aIter1(*pPolyObj1); + SdrObjListIter aIter2(*pPolyObj2); + + while(aIter1.IsMore()) + { + SdrObject* pObj = aIter1.Next(); + if(auto pPathObj = dynamic_cast< SdrPathObj *>( pObj )) + aPolyPoly1.append(pPathObj->GetPathPoly()); + } + + while(aIter2.IsMore()) + { + SdrObject* pObj = aIter2.Next(); + if(auto pPathObj = dynamic_cast< SdrPathObj *>( pObj )) + aPolyPoly2.append(pPathObj->GetPathPoly()); + } + + // perform morphing + if(aPolyPoly1.count() && aPolyPoly2.count()) + { + aPolyPoly1 = ::basegfx::utils::correctOrientations(aPolyPoly1); + aPolyPoly1.removeDoublePoints(); + ::basegfx::B2VectorOrientation eIsClockwise1(::basegfx::utils::getOrientation(aPolyPoly1.getB2DPolygon(0))); + + aPolyPoly2 = ::basegfx::utils::correctOrientations(aPolyPoly2); + aPolyPoly2.removeDoublePoints(); + ::basegfx::B2VectorOrientation eIsClockwise2(::basegfx::utils::getOrientation(aPolyPoly2.getB2DPolygon(0))); + + // set same orientation + if(eIsClockwise1 != eIsClockwise2) + aPolyPoly2.flip(); + + // force same poly count + if(aPolyPoly1.count() < aPolyPoly2.count()) + ImpAddPolys(aPolyPoly1, aPolyPoly2); + else if(aPolyPoly2.count() < aPolyPoly1.count()) + ImpAddPolys(aPolyPoly2, aPolyPoly1); + + // use orientation flag from dialog + if(!pDlg->IsOrientationFade()) + aPolyPoly2.flip(); + + // force same point counts + for( sal_uInt32 a(0); a < aPolyPoly1.count(); a++ ) + { + ::basegfx::B2DPolygon aSub1(aPolyPoly1.getB2DPolygon(a)); + ::basegfx::B2DPolygon aSub2(aPolyPoly2.getB2DPolygon(a)); + + if(aSub1.count() < aSub2.count()) + ImpEqualizePolyPointCount(aSub1, aSub2); + else if(aSub2.count() < aSub1.count()) + ImpEqualizePolyPointCount(aSub2, aSub1); + + aPolyPoly1.setB2DPolygon(a, aSub1); + aPolyPoly2.setB2DPolygon(a, aSub2); + } + + ImpMorphPolygons(aPolyPoly1, aPolyPoly2, pDlg->GetFadeSteps(), aPolyPolyList); + + OUString aString = mpView->GetDescriptionOfMarkedObjects() + + " " + SdResId(STR_UNDO_MORPHING); + + mpView->BegUndo(aString); + ImpInsertPolygons(aPolyPolyList, pDlg->IsAttributeFade(), pObj1, pObj2); + mpView->EndUndo(); + } + } + SdrObject::Free( pCloneObj1 ); + SdrObject::Free( pCloneObj2 ); +} + +static ::basegfx::B2DPolygon ImpGetExpandedPolygon( + const ::basegfx::B2DPolygon& rCandidate, + sal_uInt32 nNum +) +{ + if(rCandidate.count() && nNum && rCandidate.count() != nNum) + { + // length of step in dest poly + ::basegfx::B2DPolygon aRetval; + const double fStep(::basegfx::utils::getLength(rCandidate) / static_cast<double>(rCandidate.isClosed() ? nNum : nNum - 1)); + double fDestPos(0.0); + double fSrcPos(0.0); + sal_uInt32 nSrcPos(0); + sal_uInt32 nSrcPosNext((nSrcPos + 1 == rCandidate.count()) ? 0 : nSrcPos + 1); + double fNextSrcLen(::basegfx::B2DVector(rCandidate.getB2DPoint(nSrcPos) - rCandidate.getB2DPoint(nSrcPosNext)).getLength()); + + for(sal_uInt32 b(0); b < nNum; b++) + { + // calc fDestPos in source + while(fSrcPos + fNextSrcLen < fDestPos) + { + fSrcPos += fNextSrcLen; + nSrcPos++; + nSrcPosNext = (nSrcPos + 1 == rCandidate.count()) ? 0 : nSrcPos + 1; + fNextSrcLen = ::basegfx::B2DVector(rCandidate.getB2DPoint(nSrcPos) - rCandidate.getB2DPoint(nSrcPosNext)).getLength(); + } + + // fDestPos is between fSrcPos and (fSrcPos + fNextSrcLen) + const double fLenA((fDestPos - fSrcPos) / fNextSrcLen); + const ::basegfx::B2DPoint aOld1(rCandidate.getB2DPoint(nSrcPos)); + const ::basegfx::B2DPoint aOld2(rCandidate.getB2DPoint(nSrcPosNext)); + ::basegfx::B2DPoint aNewPoint(basegfx::interpolate(aOld1, aOld2, fLenA)); + aRetval.append(aNewPoint); + + // next step + fDestPos += fStep; + } + + if(aRetval.count() >= 3) + { + aRetval.setClosed(rCandidate.isClosed()); + } + + return aRetval; + } + else + { + return rCandidate; + } +} + +/** + * make the point count of the polygons equal in adding points + */ +void FuMorph::ImpEqualizePolyPointCount( + ::basegfx::B2DPolygon& rSmall, + const ::basegfx::B2DPolygon& rBig +) +{ + // create poly with equal point count + const sal_uInt32 nCnt(rBig.count()); + ::basegfx::B2DPolygon aPoly1(ImpGetExpandedPolygon(rSmall, nCnt)); + + // create transformation for rBig to do the compare + const ::basegfx::B2DRange aSrcSize(::basegfx::utils::getRange(rBig)); + const ::basegfx::B2DPoint aSrcPos(aSrcSize.getCenter()); + const ::basegfx::B2DRange aDstSize(::basegfx::utils::getRange(rSmall)); + const ::basegfx::B2DPoint aDstPos(aDstSize.getCenter()); + + basegfx::B2DHomMatrix aTrans(basegfx::utils::createTranslateB2DHomMatrix(-aSrcPos.getX(), -aSrcPos.getY())); + aTrans.scale(aDstSize.getWidth() / aSrcSize.getWidth(), aDstSize.getHeight() / aSrcSize.getHeight()); + aTrans.translate(aDstPos.getX(), aDstPos.getY()); + + // transpose points to have smooth linear blending + ::basegfx::B2DPolygon aPoly2; + aPoly2.append(::basegfx::B2DPoint(), nCnt); + sal_uInt32 nInd(ImpGetNearestIndex(aPoly1, aTrans * rBig.getB2DPoint(0))); + + for(sal_uInt32 a(0); a < nCnt; a++) + { + aPoly2.setB2DPoint((a + nCnt - nInd) % nCnt, aPoly1.getB2DPoint(a)); + } + + aPoly2.setClosed(rBig.isClosed()); + rSmall = aPoly2; +} + +sal_uInt32 FuMorph::ImpGetNearestIndex( + const ::basegfx::B2DPolygon& rPoly, + const ::basegfx::B2DPoint& rPos +) +{ + double fMinDist = 0.0; + sal_uInt32 nActInd = 0; + + for(sal_uInt32 a(0); a < rPoly.count(); a++) + { + double fNewDist(::basegfx::B2DVector(rPoly.getB2DPoint(a) - rPos).getLength()); + + if(!a || fNewDist < fMinDist) + { + fMinDist = fNewDist; + nActInd = a; + } + } + + return nActInd; +} + +/** + * add to a point reduced polys until count is same + */ +void FuMorph::ImpAddPolys( + ::basegfx::B2DPolyPolygon& rSmaller, + const ::basegfx::B2DPolyPolygon& rBigger +) +{ + while(rSmaller.count() < rBigger.count()) + { + const ::basegfx::B2DPolygon& aToBeCopied(rBigger.getB2DPolygon(rSmaller.count())); + const ::basegfx::B2DRange aToBeCopiedPolySize(::basegfx::utils::getRange(aToBeCopied)); + ::basegfx::B2DPoint aNewPoint(aToBeCopiedPolySize.getCenter()); + ::basegfx::B2DPolygon aNewPoly; + + const ::basegfx::B2DRange aSrcSize(::basegfx::utils::getRange(rBigger.getB2DPolygon(0))); + const ::basegfx::B2DPoint aSrcPos(aSrcSize.getCenter()); + const ::basegfx::B2DRange aDstSize(::basegfx::utils::getRange(rSmaller.getB2DPolygon(0))); + const ::basegfx::B2DPoint aDstPos(aDstSize.getCenter()); + aNewPoint = aNewPoint - aSrcPos + aDstPos; + + for(sal_uInt32 a(0); a < aToBeCopied.count(); a++) + { + aNewPoly.append(aNewPoint); + } + + rSmaller.append(aNewPoly); + } +} + +/** + * create group object with morphed polygons + */ +void FuMorph::ImpInsertPolygons( + B2DPolyPolygonList_impl& rPolyPolyList3D, + bool bAttributeFade, + const SdrObject* pObj1, + const SdrObject* pObj2 +) +{ + Color aStartFillCol; + Color aEndFillCol; + Color aStartLineCol; + Color aEndLineCol; + ::tools::Long nStartLineWidth = 0; + ::tools::Long nEndLineWidth = 0; + SdrPageView* pPageView = mpView->GetSdrPageView(); + SfxItemPool & rPool = pObj1->GetObjectItemPool(); + SfxItemSetFixed<SDRATTR_START,SDRATTR_NOTPERSIST_FIRST-1,EE_ITEMS_START,EE_ITEMS_END> aSet1( rPool ); + SfxItemSet aSet2( aSet1 ); + bool bLineColor = false; + bool bFillColor = false; + bool bLineWidth = false; + bool bIgnoreLine = false; + bool bIgnoreFill = false; + + aSet1.Put(pObj1->GetMergedItemSet()); + aSet2.Put(pObj2->GetMergedItemSet()); + + const drawing::LineStyle eLineStyle1 = aSet1.Get(XATTR_LINESTYLE).GetValue(); + const drawing::LineStyle eLineStyle2 = aSet2.Get(XATTR_LINESTYLE).GetValue(); + const drawing::FillStyle eFillStyle1 = aSet1.Get(XATTR_FILLSTYLE).GetValue(); + const drawing::FillStyle eFillStyle2 = aSet2.Get(XATTR_FILLSTYLE).GetValue(); + + if ( bAttributeFade ) + { + if ( ( eLineStyle1 != drawing::LineStyle_NONE ) && ( eLineStyle2 != drawing::LineStyle_NONE ) ) + { + bLineWidth = bLineColor = true; + + aStartLineCol = aSet1.Get(XATTR_LINECOLOR).GetColorValue(); + aEndLineCol = aSet2.Get(XATTR_LINECOLOR).GetColorValue(); + + nStartLineWidth = aSet1.Get(XATTR_LINEWIDTH).GetValue(); + nEndLineWidth = aSet2.Get(XATTR_LINEWIDTH).GetValue(); + } + else if ( ( eLineStyle1 == drawing::LineStyle_NONE ) && ( eLineStyle2 == drawing::LineStyle_NONE ) ) + bIgnoreLine = true; + + if ( ( eFillStyle1 == drawing::FillStyle_SOLID ) && ( eFillStyle2 == drawing::FillStyle_SOLID ) ) + { + bFillColor = true; + aStartFillCol = aSet1.Get(XATTR_FILLCOLOR).GetColorValue(); + aEndFillCol = aSet2.Get(XATTR_FILLCOLOR).GetColorValue(); + } + else if ( ( eFillStyle1 == drawing::FillStyle_NONE ) && ( eFillStyle2 == drawing::FillStyle_NONE ) ) + bIgnoreFill = true; + } + + if ( !pPageView ) + return; + + SfxItemSet aSet( aSet1 ); + std::unique_ptr<SdrObjGroup, SdrObjectFreeOp> xObjGroup(new SdrObjGroup(mpView->getSdrModelFromSdrView())); + SdrObjList* pObjList = xObjGroup->GetSubList(); + const size_t nCount = rPolyPolyList3D.size(); + const double fStep = 1. / ( nCount + 1 ); + const double fDelta = nEndLineWidth - nStartLineWidth; + double fFactor = fStep; + + aSet.Put( XLineStyleItem( drawing::LineStyle_SOLID ) ); + aSet.Put( XFillStyleItem( drawing::FillStyle_SOLID ) ); + + for ( size_t i = 0; i < nCount; i++, fFactor += fStep ) + { + const ::basegfx::B2DPolyPolygon& rPolyPoly3D = rPolyPolyList3D[ i ]; + SdrPathObj* pNewObj = new SdrPathObj( + mpView->getSdrModelFromSdrView(), + SdrObjKind::Polygon, + rPolyPoly3D); + + // line color + if ( bLineColor ) + { + const basegfx::BColor aLineColor(basegfx::interpolate(aStartLineCol.getBColor(), aEndLineCol.getBColor(), fFactor)); + aSet.Put( XLineColorItem( "", Color(aLineColor))); + } + else if ( bIgnoreLine ) + aSet.Put( XLineStyleItem( drawing::LineStyle_NONE ) ); + + // fill color + if ( bFillColor ) + { + const basegfx::BColor aFillColor(basegfx::interpolate(aStartFillCol.getBColor(), aEndFillCol.getBColor(), fFactor)); + aSet.Put( XFillColorItem( "", Color(aFillColor))); + } + else if ( bIgnoreFill ) + aSet.Put( XFillStyleItem( drawing::FillStyle_NONE ) ); + + // line width + if ( bLineWidth ) + aSet.Put( XLineWidthItem( nStartLineWidth + static_cast<::tools::Long>( fFactor * fDelta + 0.5 ) ) ); + + pNewObj->SetMergedItemSetAndBroadcast(aSet); + + pObjList->InsertObject( pNewObj ); + } + + if ( nCount ) + { + pObjList->InsertObject( + pObj1->CloneSdrObject(pObj1->getSdrModelFromSdrObject()), + 0 ); + pObjList->InsertObject( + pObj2->CloneSdrObject(pObj2->getSdrModelFromSdrObject()) ); + + mpView->DeleteMarked(); + mpView->InsertObjectAtView(xObjGroup.release(), *pPageView, SdrInsertFlags:: SETDEFLAYER); + } +} + +/** + * create single morphed PolyPolygon + */ +::basegfx::B2DPolyPolygon FuMorph::ImpCreateMorphedPolygon( + const ::basegfx::B2DPolyPolygon& rPolyPolyStart, + const ::basegfx::B2DPolyPolygon& rPolyPolyEnd, + double fMorphingFactor +) +{ + ::basegfx::B2DPolyPolygon aNewPolyPolygon; + const double fFactor = 1.0 - fMorphingFactor; + + for(sal_uInt32 a(0); a < rPolyPolyStart.count(); a++) + { + const ::basegfx::B2DPolygon& aPolyStart(rPolyPolyStart.getB2DPolygon(a)); + const ::basegfx::B2DPolygon& aPolyEnd(rPolyPolyEnd.getB2DPolygon(a)); + const sal_uInt32 nCount(aPolyStart.count()); + ::basegfx::B2DPolygon aNewPolygon; + + for(sal_uInt32 b(0); b < nCount; b++) + { + const ::basegfx::B2DPoint& aPtStart(aPolyStart.getB2DPoint(b)); + const ::basegfx::B2DPoint& aPtEnd(aPolyEnd.getB2DPoint(b)); + aNewPolygon.append(aPtEnd + ((aPtStart - aPtEnd) * fFactor)); + } + + aNewPolygon.setClosed(aPolyStart.isClosed() && aPolyEnd.isClosed()); + aNewPolyPolygon.append(aNewPolygon); + } + + return aNewPolyPolygon; +} + +/** + * create morphed PolyPolygons + */ +void FuMorph::ImpMorphPolygons( + const ::basegfx::B2DPolyPolygon& rPolyPoly1, + const ::basegfx::B2DPolyPolygon& rPolyPoly2, + const sal_uInt16 nSteps, + B2DPolyPolygonList_impl& rPolyPolyList3D +) +{ + if(!nSteps) + return; + + const ::basegfx::B2DRange aStartPolySize(::basegfx::utils::getRange(rPolyPoly1)); + const ::basegfx::B2DPoint aStartCenter(aStartPolySize.getCenter()); + const ::basegfx::B2DRange aEndPolySize(::basegfx::utils::getRange(rPolyPoly2)); + const ::basegfx::B2DPoint aEndCenter(aEndPolySize.getCenter()); + const ::basegfx::B2DPoint aDelta(aEndCenter - aStartCenter); + const double fFactor(1.0 / (nSteps + 1)); + double fValue(0.0); + + for(sal_uInt16 i(0); i < nSteps; i++) + { + fValue += fFactor; + ::basegfx::B2DPolyPolygon aNewPolyPoly2D = ImpCreateMorphedPolygon(rPolyPoly1, rPolyPoly2, fValue); + + const ::basegfx::B2DRange aNewPolySize(::basegfx::utils::getRange(aNewPolyPoly2D)); + const ::basegfx::B2DPoint aNewS(aNewPolySize.getCenter()); + const ::basegfx::B2DPoint aRealS(aStartCenter + (aDelta * fValue)); + const ::basegfx::B2DPoint aDiff(aRealS - aNewS); + + aNewPolyPoly2D.transform(basegfx::utils::createTranslateB2DHomMatrix(aDiff)); + rPolyPolyList3D.push_back( std::move(aNewPolyPoly2D) ); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/funavig.cxx b/sd/source/ui/func/funavig.cxx new file mode 100644 index 000000000..bd0cdb7c3 --- /dev/null +++ b/sd/source/ui/func/funavig.cxx @@ -0,0 +1,154 @@ +/* -*- 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 <funavig.hxx> +#include <sfx2/viewfrm.hxx> + +#include <app.hrc> +#include <sdpage.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/request.hxx> +#include <drawdoc.hxx> +#include <DrawViewShell.hxx> +#include <ViewShell.hxx> +#include <slideshow.hxx> + +namespace sd { + + +FuNavigation::FuNavigation ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuNavigation::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuNavigation( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuNavigation::DoExecute( SfxRequest& rReq ) +{ + bool bSlideShow = SlideShow::IsRunning( mpViewShell->GetViewShellBase() ); + + switch ( rReq.GetSlot() ) + { + case SID_GO_TO_FIRST_PAGE: + { + if (!mpView->IsTextEdit() + && dynamic_cast< const DrawViewShell *>( mpViewShell ) != nullptr + && !bSlideShow) + { + // jump to first page + static_cast<DrawViewShell*>(mpViewShell)->SwitchPage(0); + } + } + break; + + case SID_GO_TO_PREVIOUS_PAGE: + { + if( !bSlideShow) + if( auto pDrawViewShell = dynamic_cast<DrawViewShell *>( mpViewShell ) ) + { + // With no modifier pressed we move to the previous + // slide. + mpView->SdrEndTextEdit(); + + // Previous page. + SdPage* pPage = pDrawViewShell->GetActualPage(); + sal_uInt16 nSdPage = (pPage->GetPageNum() - 1) / 2; + + if (nSdPage > 0) + { + // Switch the page and send events regarding + // deactivation the old page and activating the new + // one. + TabControl& rPageTabControl = + static_cast<DrawViewShell*>(mpViewShell) + ->GetPageTabControl(); + if (rPageTabControl.IsReallyShown()) + rPageTabControl.SendDeactivatePageEvent (); + static_cast<DrawViewShell*>(mpViewShell)->SwitchPage(nSdPage - 1); + if (rPageTabControl.IsReallyShown()) + rPageTabControl.SendActivatePageEvent (); + } + } + } + break; + + case SID_GO_TO_NEXT_PAGE: + { + if( !bSlideShow) + if( auto pDrawViewShell = dynamic_cast<DrawViewShell *>( mpViewShell )) + { + // With no modifier pressed we move to the next slide. + mpView->SdrEndTextEdit(); + + // Next page. + SdPage* pPage = pDrawViewShell->GetActualPage(); + sal_uInt16 nSdPage = (pPage->GetPageNum() - 1) / 2; + + if (nSdPage < mpDoc->GetSdPageCount(pPage->GetPageKind()) - 1) + { + // Switch the page and send events regarding + // deactivation the old page and activating the new + // one. + TabControl& rPageTabControl = + static_cast<DrawViewShell*>(mpViewShell)->GetPageTabControl(); + if (rPageTabControl.IsReallyShown()) + rPageTabControl.SendDeactivatePageEvent (); + static_cast<DrawViewShell*>(mpViewShell)->SwitchPage(nSdPage + 1); + if (rPageTabControl.IsReallyShown()) + rPageTabControl.SendActivatePageEvent (); + } + } + } + break; + + case SID_GO_TO_LAST_PAGE: + { + if (!mpView->IsTextEdit() && !bSlideShow) + if (auto pDrawViewShell = dynamic_cast<DrawViewShell *>( mpViewShell )) + { + // jump to last page + SdPage* pPage = pDrawViewShell->GetActualPage(); + pDrawViewShell->SwitchPage(mpDoc->GetSdPageCount( + pPage->GetPageKind()) - 1); + } + } + break; + } + // Refresh toolbar icons + SfxBindings& rBindings = mpViewShell->GetViewFrame()->GetBindings(); + rBindings.Invalidate(SID_GO_TO_FIRST_PAGE); + rBindings.Invalidate(SID_GO_TO_PREVIOUS_PAGE); + rBindings.Invalidate(SID_GO_TO_NEXT_PAGE); + rBindings.Invalidate(SID_GO_TO_LAST_PAGE); +} + + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuoaprms.cxx b/sd/source/ui/func/fuoaprms.cxx new file mode 100644 index 000000000..0feaabfb4 --- /dev/null +++ b/sd/source/ui/func/fuoaprms.cxx @@ -0,0 +1,800 @@ +/* -*- 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 <fuoaprms.hxx> +#include <sdattr.hrc> + +#include <editeng/colritem.hxx> +#include <svx/svdundo.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/request.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/sfxdlg.hxx> +#include <svl/intitem.hxx> +#include <svl/stritem.hxx> +#include <svx/svdopath.hxx> +#include <tools/debug.hxx> + +#include <strings.hrc> +#include <drawdoc.hxx> +#include <ViewShell.hxx> +#include <ViewShellBase.hxx> +#include <anminfo.hxx> +#include <unoaprms.hxx> +#include <sdundogr.hxx> +#include <View.hxx> +#include <sdabstdlg.hxx> +#include <sdresid.hxx> +#include <tools/helpers.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <memory> + +using namespace ::com::sun::star; + +namespace sd { + + +#define ATTR_MISSING 0 ///< Attribute missing +#define ATTR_MIXED 1 ///< Attribute ambiguous (on multi-selection) +#define ATTR_SET 2 ///< Attribute unique + +FuObjectAnimationParameters::FuObjectAnimationParameters ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuObjectAnimationParameters::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuObjectAnimationParameters( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuObjectAnimationParameters::DoExecute( SfxRequest& rReq ) +{ + SfxUndoManager* pUndoMgr = mpViewShell->GetViewFrame()->GetObjectShell()->GetUndoManager(); + + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + const size_t nCount = rMarkList.GetMarkCount(); + + short nAnimationSet = ATTR_MISSING; + short nEffectSet = ATTR_MISSING; + short nTextEffectSet = ATTR_MISSING; + short nSpeedSet = ATTR_MISSING; + short nFadeColorSet = ATTR_MISSING; + short nFadeOutSet = ATTR_MISSING; + short nInvisibleSet = ATTR_MISSING; + short nSoundOnSet = ATTR_MISSING; + short nSoundFileSet = ATTR_MISSING; + short nPlayFullSet = ATTR_MISSING; + short nClickActionSet = ATTR_MISSING; + short nBookmarkSet = ATTR_MISSING; + + short nSecondEffectSet = ATTR_MISSING; + short nSecondSpeedSet = ATTR_MISSING; + short nSecondSoundOnSet = ATTR_MISSING; + short nSecondPlayFullSet = ATTR_MISSING; + + // defaults (for Undo-Action) + presentation::AnimationEffect eEffect = presentation::AnimationEffect_NONE; + presentation::AnimationEffect eTextEffect = presentation::AnimationEffect_NONE; + presentation::AnimationSpeed eSpeed = presentation::AnimationSpeed_MEDIUM; + bool bActive = false; + bool bFadeOut = false; + Color aFadeColor = COL_LIGHTGRAY; + bool bInvisible = false; + bool bSoundOn = false; + OUString aSound; + bool bPlayFull = false; + presentation::ClickAction eClickAction = presentation::ClickAction_NONE; + OUString aBookmark; + + presentation::AnimationEffect eSecondEffect = presentation::AnimationEffect_NONE; + presentation::AnimationSpeed eSecondSpeed = presentation::AnimationSpeed_MEDIUM; + bool bSecondSoundOn = false; + bool bSecondPlayFull = false; + + SdAnimationInfo* pInfo; + SdrMark* pMark; + + // inspect first object + pMark = rMarkList.GetMark(0); + pInfo = SdDrawDocument::GetAnimationInfo(pMark->GetMarkedSdrObj()); + if( pInfo ) + { + bActive = pInfo->mbActive; + nAnimationSet = ATTR_SET; + + eEffect = pInfo->meEffect; + nEffectSet = ATTR_SET; + + eTextEffect = pInfo->meTextEffect; + nTextEffectSet = ATTR_SET; + + eSpeed = pInfo->meSpeed; + nSpeedSet = ATTR_SET; + + bFadeOut = pInfo->mbDimPrevious; + nFadeOutSet = ATTR_SET; + + aFadeColor = pInfo->maDimColor; + nFadeColorSet = ATTR_SET; + + bInvisible = pInfo->mbDimHide; + nInvisibleSet = ATTR_SET; + + bSoundOn = pInfo->mbSoundOn; + nSoundOnSet = ATTR_SET; + + aSound = pInfo->maSoundFile; + nSoundFileSet = ATTR_SET; + + bPlayFull = pInfo->mbPlayFull; + nPlayFullSet = ATTR_SET; + + eClickAction = pInfo->meClickAction; + nClickActionSet = ATTR_SET; + + aBookmark = pInfo->GetBookmark(); + nBookmarkSet = ATTR_SET; + + eSecondEffect = pInfo->meSecondEffect; + nSecondEffectSet = ATTR_SET; + + eSecondSpeed = pInfo->meSecondSpeed; + nSecondSpeedSet = ATTR_SET; + + bSecondSoundOn = pInfo->mbSecondSoundOn; + nSecondSoundOnSet = ATTR_SET; + + bSecondPlayFull = pInfo->mbSecondPlayFull; + nSecondPlayFullSet = ATTR_SET; + } + + // if necessary, inspect more objects + for( size_t nObject = 1; nObject < nCount; ++nObject ) + { + pMark = rMarkList.GetMark( nObject ); + SdrObject* pObject = pMark->GetMarkedSdrObj(); + pInfo = SdDrawDocument::GetAnimationInfo(pObject); + if( pInfo ) + { + if( bActive != pInfo->mbActive ) + nAnimationSet = ATTR_MIXED; + + if( eEffect != pInfo->meEffect ) + nEffectSet = ATTR_MIXED; + + if( eTextEffect != pInfo->meTextEffect ) + nTextEffectSet = ATTR_MIXED; + + if( eSpeed != pInfo->meSpeed ) + nSpeedSet = ATTR_MIXED; + + if( bFadeOut != pInfo->mbDimPrevious ) + nFadeOutSet = ATTR_MIXED; + + if( aFadeColor != pInfo->maDimColor ) + nFadeColorSet = ATTR_MIXED; + + if( bInvisible != pInfo->mbDimHide ) + nInvisibleSet = ATTR_MIXED; + + if( bSoundOn != pInfo->mbSoundOn ) + nSoundOnSet = ATTR_MIXED; + + if( aSound != pInfo->maSoundFile ) + nSoundFileSet = ATTR_MIXED; + + if( bPlayFull != pInfo->mbPlayFull ) + nPlayFullSet = ATTR_MIXED; + + if( eClickAction != pInfo->meClickAction ) + nClickActionSet = ATTR_MIXED; + + if( aBookmark != pInfo->GetBookmark() ) + nBookmarkSet = ATTR_MIXED; + + if( eSecondEffect != pInfo->meSecondEffect ) + nSecondEffectSet = ATTR_MIXED; + + if( eSecondSpeed != pInfo->meSecondSpeed ) + nSecondSpeedSet = ATTR_MIXED; + + if( bSecondSoundOn != pInfo->mbSecondSoundOn ) + nSecondSoundOnSet = ATTR_MIXED; + + if( bSecondPlayFull != pInfo->mbSecondPlayFull ) + nSecondPlayFullSet = ATTR_MIXED; + } + else + { + if (nAnimationSet == ATTR_SET && bActive) + nAnimationSet = ATTR_MIXED; + + if (nEffectSet == ATTR_SET && eEffect != presentation::AnimationEffect_NONE) + nEffectSet = ATTR_MIXED; + + if (nTextEffectSet == ATTR_SET && eTextEffect != presentation::AnimationEffect_NONE) + nTextEffectSet = ATTR_MIXED; + + if (nSpeedSet == ATTR_SET) + nSpeedSet = ATTR_MIXED; + + if (nFadeOutSet == ATTR_SET && bFadeOut) + nFadeOutSet = ATTR_MIXED; + + if (nFadeColorSet == ATTR_SET) + nFadeColorSet = ATTR_MIXED; + + if (nInvisibleSet == ATTR_SET && bInvisible) + nInvisibleSet = ATTR_MIXED; + + if (nSoundOnSet == ATTR_SET && bSoundOn) + nSoundOnSet = ATTR_MIXED; + + if (nSoundFileSet == ATTR_SET) + nSoundFileSet = ATTR_MIXED; + + if (nPlayFullSet == ATTR_SET && bPlayFull) + nPlayFullSet = ATTR_MIXED; + + if (nClickActionSet == ATTR_SET && eClickAction != presentation::ClickAction_NONE) + nClickActionSet = ATTR_MIXED; + + if (nBookmarkSet == ATTR_SET) + nBookmarkSet = ATTR_MIXED; + + if (nSecondEffectSet == ATTR_SET && eSecondEffect != presentation::AnimationEffect_NONE) + nSecondEffectSet = ATTR_MIXED; + + if (nSecondSpeedSet == ATTR_SET) + nSecondSpeedSet = ATTR_MIXED; + + if (nSecondSoundOnSet == ATTR_SET && bSecondSoundOn) + nSecondSoundOnSet = ATTR_MIXED; + + if (nSecondPlayFullSet == ATTR_SET && bSecondPlayFull) + nSecondPlayFullSet = ATTR_MIXED; + } + } + + /* Exactly two objects with path effect? + Then, only the animation info at the moved object is valid. */ + if (nCount == 2) + { + SdrObject* pObject1 = rMarkList.GetMark(0)->GetMarkedSdrObj(); + SdrObject* pObject2 = rMarkList.GetMark(1)->GetMarkedSdrObj(); + SdrObjKind eKind1 = pObject1->GetObjIdentifier(); + SdrObjKind eKind2 = pObject2->GetObjIdentifier(); + SdAnimationInfo* pInfo1 = SdDrawDocument::GetAnimationInfo(pObject1); + SdAnimationInfo* pInfo2 = SdDrawDocument::GetAnimationInfo(pObject2); + pInfo = nullptr; + + if (pObject1->GetObjInventor() == SdrInventor::Default && + ((eKind1 == SdrObjKind::Line) || // 2 point line + (eKind1 == SdrObjKind::PolyLine) || // Polygon + (eKind1 == SdrObjKind::PathLine)) && // Bezier curve + (pInfo2 && pInfo2->meEffect == presentation::AnimationEffect_PATH)) + { + pInfo = pInfo2; + } + + if (pObject2->GetObjInventor() == SdrInventor::Default && + ((eKind2 == SdrObjKind::Line) || // 2 point line + (eKind2 == SdrObjKind::PolyLine) || // Polygon + (eKind2 == SdrObjKind::PathLine)) && // Bezier curve + (pInfo1 && pInfo1->meEffect == presentation::AnimationEffect_PATH)) + { + pInfo = pInfo1; + } + + if (pInfo) + { + bActive = pInfo->mbActive; nAnimationSet = ATTR_SET; + eEffect = pInfo->meEffect; nEffectSet = ATTR_SET; + eTextEffect = pInfo->meTextEffect; nTextEffectSet = ATTR_SET; + eSpeed = pInfo->meSpeed; nSpeedSet = ATTR_SET; + bFadeOut = pInfo->mbDimPrevious; nFadeOutSet = ATTR_SET; + aFadeColor = pInfo->maDimColor; nFadeColorSet = ATTR_SET; + bInvisible = pInfo->mbDimHide; nInvisibleSet = ATTR_SET; + bSoundOn = pInfo->mbSoundOn; nSoundOnSet = ATTR_SET; + aSound = pInfo->maSoundFile; nSoundFileSet = ATTR_SET; + bPlayFull = pInfo->mbPlayFull; nPlayFullSet = ATTR_SET; + eClickAction = pInfo->meClickAction; nClickActionSet = ATTR_SET; + aBookmark = pInfo->GetBookmark(); nBookmarkSet = ATTR_SET; + eSecondEffect = pInfo->meSecondEffect; nSecondEffectSet = ATTR_SET; + eSecondSpeed = pInfo->meSecondSpeed; nSecondSpeedSet = ATTR_SET; + bSecondSoundOn = pInfo->mbSecondSoundOn; nSecondSoundOnSet = ATTR_SET; + bSecondPlayFull = pInfo->mbSecondPlayFull; nSecondPlayFullSet = ATTR_SET; + } + } + + const SfxItemSet* pArgs = rReq.GetArgs(); + + if(!pArgs) + { + // fill ItemSet for dialog + SfxItemSetFixed<ATTR_ANIMATION_START, ATTR_ACTION_END> aSet(mpDoc->GetPool()); + + // fill the set + if (nAnimationSet == ATTR_SET) + aSet.Put( SfxBoolItem( ATTR_ANIMATION_ACTIVE, bActive)); + else if (nAnimationSet == ATTR_MIXED) + aSet.InvalidateItem(ATTR_ANIMATION_ACTIVE); + else + aSet.Put(SfxBoolItem(ATTR_ANIMATION_ACTIVE, false)); + + if (nEffectSet == ATTR_SET) + aSet.Put(SfxUInt16Item(ATTR_ANIMATION_EFFECT, static_cast<sal_uInt16>(eEffect))); + else if (nEffectSet == ATTR_MIXED) + aSet.InvalidateItem( ATTR_ANIMATION_EFFECT ); + else + aSet.Put(SfxUInt16Item(ATTR_ANIMATION_EFFECT, sal_uInt16(presentation::AnimationEffect_NONE))); + + if (nTextEffectSet == ATTR_SET) + aSet.Put(SfxUInt16Item(ATTR_ANIMATION_TEXTEFFECT, static_cast<sal_uInt16>(eTextEffect))); + else if (nTextEffectSet == ATTR_MIXED) + aSet.InvalidateItem( ATTR_ANIMATION_TEXTEFFECT ); + else + aSet.Put(SfxUInt16Item(ATTR_ANIMATION_TEXTEFFECT, sal_uInt16(presentation::AnimationEffect_NONE))); + + if (nSpeedSet == ATTR_SET) + aSet.Put(SfxUInt16Item(ATTR_ANIMATION_SPEED, static_cast<sal_uInt16>(eSpeed))); + else + aSet.InvalidateItem(ATTR_ANIMATION_SPEED); + + if (nFadeOutSet == ATTR_SET) + aSet.Put(SfxBoolItem(ATTR_ANIMATION_FADEOUT, bFadeOut)); + else if (nFadeOutSet == ATTR_MIXED) + aSet.InvalidateItem(ATTR_ANIMATION_FADEOUT); + else + aSet.Put(SfxBoolItem(ATTR_ANIMATION_FADEOUT, false)); + + if (nFadeColorSet == ATTR_SET) + aSet.Put(SvxColorItem(aFadeColor, ATTR_ANIMATION_COLOR)); + else if (nFadeColorSet == ATTR_MIXED) + aSet.InvalidateItem(ATTR_ANIMATION_COLOR); + else + aSet.Put(SvxColorItem(COL_LIGHTGRAY, ATTR_ANIMATION_COLOR)); + + if (nInvisibleSet == ATTR_SET) + aSet.Put(SfxBoolItem(ATTR_ANIMATION_INVISIBLE, bInvisible)); + else if (nInvisibleSet == ATTR_MIXED) + aSet.InvalidateItem(ATTR_ANIMATION_INVISIBLE); + else + aSet.Put(SfxBoolItem(ATTR_ANIMATION_INVISIBLE, false)); + + if (nSoundOnSet == ATTR_SET) + aSet.Put(SfxBoolItem(ATTR_ANIMATION_SOUNDON, bSoundOn)); + else if (nSoundOnSet == ATTR_MIXED) + aSet.InvalidateItem(ATTR_ANIMATION_SOUNDON); + else + aSet.Put(SfxBoolItem(ATTR_ANIMATION_SOUNDON, false)); + + if (nSoundFileSet == ATTR_SET) + aSet.Put(SfxStringItem(ATTR_ANIMATION_SOUNDFILE, aSound)); + else + aSet.InvalidateItem(ATTR_ANIMATION_SOUNDFILE); + + if (nPlayFullSet == ATTR_SET) + aSet.Put(SfxBoolItem(ATTR_ANIMATION_PLAYFULL, bPlayFull)); + else if (nPlayFullSet == ATTR_MIXED) + aSet.InvalidateItem(ATTR_ANIMATION_PLAYFULL); + else + aSet.Put(SfxBoolItem(ATTR_ANIMATION_PLAYFULL, false)); + + if (nClickActionSet == ATTR_SET) + aSet.Put(SfxUInt16Item(ATTR_ACTION, static_cast<sal_uInt16>(eClickAction))); + else if (nClickActionSet == ATTR_MIXED) + aSet.InvalidateItem(ATTR_ACTION); + else + aSet.Put(SfxUInt16Item(ATTR_ACTION, sal_uInt16(presentation::ClickAction_NONE))); + + if (nBookmarkSet == ATTR_SET) + aSet.Put(SfxStringItem(ATTR_ACTION_FILENAME, aBookmark)); + else + aSet.InvalidateItem(ATTR_ACTION_FILENAME); + + if (nSecondEffectSet == ATTR_SET) + aSet.Put(SfxUInt16Item(ATTR_ACTION_EFFECT, static_cast<sal_uInt16>(eSecondEffect))); + else if (nSecondEffectSet == ATTR_MIXED) + aSet.InvalidateItem( ATTR_ACTION_EFFECT ); + else + aSet.Put(SfxUInt16Item(ATTR_ACTION_EFFECT, sal_uInt16(presentation::AnimationEffect_NONE))); + + if (nSecondSpeedSet == ATTR_SET) + aSet.Put(SfxUInt16Item(ATTR_ACTION_EFFECTSPEED, static_cast<sal_uInt16>(eSecondSpeed))); + else + aSet.InvalidateItem(ATTR_ACTION_EFFECTSPEED); + + if (nSecondSoundOnSet == ATTR_SET) + aSet.Put(SfxBoolItem(ATTR_ACTION_SOUNDON, bSecondSoundOn)); + else if (nSecondSoundOnSet == ATTR_MIXED) + aSet.InvalidateItem(ATTR_ACTION_SOUNDON); + else + aSet.Put(SfxBoolItem(ATTR_ACTION_SOUNDON, false)); + + if (nSecondPlayFullSet == ATTR_SET) + aSet.Put(SfxBoolItem(ATTR_ACTION_PLAYFULL, bSecondPlayFull)); + else if (nSecondPlayFullSet == ATTR_MIXED) + aSet.InvalidateItem(ATTR_ACTION_PLAYFULL); + else + aSet.Put(SfxBoolItem(ATTR_ACTION_PLAYFULL, false)); + + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractDialog> pDlg( pFact->CreatSdActionDialog(mpViewShell->GetFrameWeld(), &aSet, mpView) ); + + short nResult = pDlg->Execute(); + + if( nResult != RET_OK ) + return; + + rReq.Done( *( pDlg->GetOutputItemSet() ) ); + pArgs = rReq.GetArgs(); + } + + // evaluation of the ItemSets + if (pArgs->GetItemState(ATTR_ANIMATION_ACTIVE) == SfxItemState::SET) + { + bActive = static_cast<const SfxBoolItem&>(pArgs->Get(ATTR_ANIMATION_ACTIVE)).GetValue(); + nAnimationSet = ATTR_SET; + } + else + nAnimationSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ANIMATION_EFFECT) == SfxItemState::SET) + { + eEffect = static_cast<presentation::AnimationEffect>(static_cast<const SfxUInt16Item&>( pArgs-> + Get(ATTR_ANIMATION_EFFECT)).GetValue()); + nEffectSet = ATTR_SET; + } + else + nEffectSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ANIMATION_TEXTEFFECT) == SfxItemState::SET) + { + eTextEffect = static_cast<presentation::AnimationEffect>(static_cast<const SfxUInt16Item&>( pArgs-> + Get(ATTR_ANIMATION_TEXTEFFECT)).GetValue()); + nTextEffectSet = ATTR_SET; + } + else + nTextEffectSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ANIMATION_SPEED) == SfxItemState::SET) + { + eSpeed = static_cast<presentation::AnimationSpeed>(static_cast<const SfxUInt16Item&>( pArgs-> + Get(ATTR_ANIMATION_SPEED)).GetValue()); + nSpeedSet = ATTR_SET; + } + else + nSpeedSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ANIMATION_FADEOUT) == SfxItemState::SET) + { + bFadeOut = static_cast<const SfxBoolItem&>(pArgs->Get(ATTR_ANIMATION_FADEOUT)).GetValue(); + nFadeOutSet = ATTR_SET; + } + else + nFadeOutSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ANIMATION_INVISIBLE) == SfxItemState::SET) + { + bInvisible = static_cast<const SfxBoolItem&>(pArgs->Get(ATTR_ANIMATION_INVISIBLE)).GetValue(); + nInvisibleSet = ATTR_SET; + } + else + nInvisibleSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ANIMATION_SOUNDON) == SfxItemState::SET) + { + bSoundOn = static_cast<const SfxBoolItem&>(pArgs->Get(ATTR_ANIMATION_SOUNDON)).GetValue(); + nSoundOnSet = ATTR_SET; + } + else + nSoundOnSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ANIMATION_SOUNDFILE) == SfxItemState::SET) + { + aSound = static_cast<const SfxStringItem&>(pArgs->Get(ATTR_ANIMATION_SOUNDFILE)).GetValue(); + nSoundFileSet = ATTR_SET; + } + else + nSoundFileSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ANIMATION_COLOR) == SfxItemState::SET) + { + aFadeColor = static_cast<const SvxColorItem&>(pArgs->Get(ATTR_ANIMATION_COLOR)).GetValue(); + nFadeColorSet = ATTR_SET; + } + else + nFadeColorSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ANIMATION_PLAYFULL) == SfxItemState::SET) + { + bPlayFull = static_cast<const SfxBoolItem&>(pArgs->Get(ATTR_ANIMATION_PLAYFULL)).GetValue(); + nPlayFullSet = ATTR_SET; + } + else + nPlayFullSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ACTION) == SfxItemState::SET) + { + eClickAction = static_cast<presentation::ClickAction>(static_cast<const SfxUInt16Item&>(pArgs-> + Get(ATTR_ACTION)).GetValue()); + nClickActionSet = ATTR_SET; + } + else + nClickActionSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ACTION_FILENAME) == SfxItemState::SET) + { + aBookmark = static_cast<const SfxStringItem&>(pArgs-> + Get(ATTR_ACTION_FILENAME)).GetValue(); + nBookmarkSet = ATTR_SET; + } + else + nBookmarkSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ACTION_EFFECT) == SfxItemState::SET) + { + eSecondEffect = static_cast<presentation::AnimationEffect>(static_cast<const SfxUInt16Item&>( pArgs-> + Get(ATTR_ACTION_EFFECT)).GetValue()); + nSecondEffectSet = ATTR_SET; + } + else + nSecondEffectSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ACTION_EFFECTSPEED) == SfxItemState::SET) + { + eSecondSpeed = static_cast<presentation::AnimationSpeed>(static_cast<const SfxUInt16Item&>( pArgs-> + Get(ATTR_ACTION_EFFECTSPEED)).GetValue()); + nSecondSpeedSet = ATTR_SET; + } + else + nSecondSpeedSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ACTION_SOUNDON) == SfxItemState::SET) + { + bSecondSoundOn = static_cast<const SfxBoolItem&>(pArgs->Get(ATTR_ACTION_SOUNDON)).GetValue(); + nSecondSoundOnSet = ATTR_SET; + } + else + nSecondSoundOnSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ACTION_PLAYFULL) == SfxItemState::SET) + { + bSecondPlayFull = static_cast<const SfxBoolItem&>(pArgs->Get(ATTR_ACTION_PLAYFULL)).GetValue(); + nSecondPlayFullSet = ATTR_SET; + } + else + nSecondPlayFullSet = ATTR_MISSING; + + // if any attribute is chosen + if (!(nEffectSet == ATTR_SET || + nTextEffectSet == ATTR_SET || + nSpeedSet == ATTR_SET || + nAnimationSet == ATTR_SET || + nFadeOutSet == ATTR_SET || + nFadeColorSet == ATTR_SET || + nInvisibleSet == ATTR_SET || + nSoundOnSet == ATTR_SET || + nSoundFileSet == ATTR_SET || + nPlayFullSet == ATTR_SET || + nClickActionSet == ATTR_SET || + nBookmarkSet == ATTR_SET || + nSecondEffectSet == ATTR_SET || + nSecondSpeedSet == ATTR_SET || + nSecondSoundOnSet == ATTR_SET || + nSecondPlayFullSet == ATTR_SET)) + return; + + // String for undo-group and list-action + OUString aComment(SdResId(STR_UNDO_ANIMATION)); + + // with 'following curves', we have an additional UndoAction + // therefore cling? here + pUndoMgr->EnterListAction(aComment, aComment, 0, mpViewShell->GetViewShellBase().GetViewShellId()); + + // create undo group + std::unique_ptr<SdUndoGroup> pUndoGroup(new SdUndoGroup(mpDoc)); + pUndoGroup->SetComment(aComment); + + // for the path effect, remember some stuff + SdrPathObj* pPath = nullptr; + if (eEffect == presentation::AnimationEffect_PATH && nEffectSet == ATTR_SET) + { + DBG_ASSERT(nCount == 2, "This effect expects two selected objects"); + SdrObject* pObject1 = rMarkList.GetMark(0)->GetMarkedSdrObj(); + SdrObject* pObject2 = rMarkList.GetMark(1)->GetMarkedSdrObj(); + SdrObjKind eKind1 = pObject1->GetObjIdentifier(); + SdrObjKind eKind2 = pObject2->GetObjIdentifier(); + SdrObject* pRunningObj = nullptr; + + if (pObject1->GetObjInventor() == SdrInventor::Default && + ((eKind1 == SdrObjKind::Line) || // 2 point line + (eKind1 == SdrObjKind::PolyLine) || // Polygon + (eKind1 == SdrObjKind::PathLine))) // Bezier curve + { + pPath = static_cast<SdrPathObj*>(pObject1); + pRunningObj = pObject2; + } + + if (pObject2->GetObjInventor() == SdrInventor::Default && + ((eKind2 == SdrObjKind::Line) || // 2 point line + (eKind2 == SdrObjKind::PolyLine) || // Polygon + (eKind2 == SdrObjKind::PathLine))) // Bezier curve + { + pPath = static_cast<SdrPathObj*>(pObject2); + pRunningObj = pObject1; + } + + assert(pRunningObj && pPath && "no curve found"); + + // push the running object to the end of the curve + if (pRunningObj) + { + ::tools::Rectangle aCurRect(pRunningObj->GetLogicRect()); + Point aCurCenter(aCurRect.Center()); + const ::basegfx::B2DPolyPolygon& rPolyPolygon = pPath->GetPathPoly(); + sal_uInt32 nNoOfPolygons(rPolyPolygon.count()); + const ::basegfx::B2DPolygon& aPolygon(rPolyPolygon.getB2DPolygon(nNoOfPolygons - 1)); + sal_uInt32 nPoints(aPolygon.count()); + const ::basegfx::B2DPoint aNewB2DCenter(aPolygon.getB2DPoint(nPoints - 1)); + const Point aNewCenter(FRound(aNewB2DCenter.getX()), FRound(aNewB2DCenter.getY())); + Size aDistance(aNewCenter.X() - aCurCenter.X(), aNewCenter.Y() - aCurCenter.Y()); + pRunningObj->Move(aDistance); + + pUndoMgr->AddUndoAction(mpDoc->GetSdrUndoFactory().CreateUndoMoveObject( *pRunningObj, aDistance)); + } + } + + for (size_t nObject = 0; nObject < nCount; ++nObject) + { + SdrObject* pObject = rMarkList.GetMark(nObject)->GetMarkedSdrObj(); + + pInfo = SdDrawDocument::GetAnimationInfo(pObject); + + bool bCreated = false; + if( !pInfo ) + { + pInfo = SdDrawDocument::GetShapeUserData(*pObject,true); + bCreated = true; + } + + // path object for 'following curves'? + if (eEffect == presentation::AnimationEffect_PATH && pObject == pPath) + { + SdAnimationPrmsUndoAction* pAction = new SdAnimationPrmsUndoAction + (mpDoc, pObject, bCreated); + pAction->SetActive(pInfo->mbActive, pInfo->mbActive); + pAction->SetEffect(pInfo->meEffect, pInfo->meEffect); + pAction->SetTextEffect(pInfo->meTextEffect, pInfo->meTextEffect); + pAction->SetSpeed(pInfo->meSpeed, pInfo->meSpeed); + pAction->SetDim(pInfo->mbDimPrevious, pInfo->mbDimPrevious); + pAction->SetDimColor(pInfo->maDimColor, pInfo->maDimColor); + pAction->SetDimHide(pInfo->mbDimHide, pInfo->mbDimHide); + pAction->SetSoundOn(pInfo->mbSoundOn, pInfo->mbSoundOn); + pAction->SetSound(pInfo->maSoundFile, pInfo->maSoundFile); + pAction->SetPlayFull(pInfo->mbPlayFull, pInfo->mbPlayFull); + pAction->SetClickAction(pInfo->meClickAction, pInfo->meClickAction); + pAction->SetBookmark(pInfo->GetBookmark(), pInfo->GetBookmark()); + pAction->SetVerb(pInfo->mnVerb, pInfo->mnVerb); + pAction->SetSecondEffect(pInfo->meSecondEffect, pInfo->meSecondEffect); + pAction->SetSecondSpeed(pInfo->meSecondSpeed, pInfo->meSecondSpeed); + pAction->SetSecondSoundOn(pInfo->mbSecondSoundOn, pInfo->mbSecondSoundOn); + pAction->SetSecondPlayFull(pInfo->mbSecondPlayFull, pInfo->mbSecondPlayFull); + pUndoGroup->AddAction(pAction); + + } + else + { + + // create undo action with old and new sizes + SdAnimationPrmsUndoAction* pAction = new SdAnimationPrmsUndoAction + (mpDoc, pObject, bCreated); + pAction->SetActive(pInfo->mbActive, bActive); + pAction->SetEffect(pInfo->meEffect, eEffect); + pAction->SetTextEffect(pInfo->meTextEffect, eTextEffect); + pAction->SetSpeed(pInfo->meSpeed, eSpeed); + pAction->SetDim(pInfo->mbDimPrevious, bFadeOut); + pAction->SetDimColor(pInfo->maDimColor, aFadeColor); + pAction->SetDimHide(pInfo->mbDimHide, bInvisible); + pAction->SetSoundOn(pInfo->mbSoundOn, bSoundOn); + pAction->SetSound(pInfo->maSoundFile, aSound); + pAction->SetPlayFull(pInfo->mbPlayFull, bPlayFull); + pAction->SetClickAction(pInfo->meClickAction, eClickAction); + pAction->SetBookmark(pInfo->GetBookmark(), aBookmark); + pAction->SetVerb(pInfo->mnVerb, static_cast<sal_uInt16>(pInfo->GetBookmark().toInt32()) ); + pAction->SetSecondEffect(pInfo->meSecondEffect, eSecondEffect); + pAction->SetSecondSpeed(pInfo->meSecondSpeed, eSecondSpeed); + pAction->SetSecondSoundOn(pInfo->mbSecondSoundOn, bSecondSoundOn); + pAction->SetSecondPlayFull(pInfo->mbSecondPlayFull,bSecondPlayFull); + pUndoGroup->AddAction(pAction); + + // insert new values at info block of the object + if (nAnimationSet == ATTR_SET) + pInfo->mbActive = bActive; + + if (nEffectSet == ATTR_SET) + pInfo->meEffect = eEffect; + + if (nTextEffectSet == ATTR_SET) + pInfo->meTextEffect = eTextEffect; + + if (nSpeedSet == ATTR_SET) + pInfo->meSpeed = eSpeed; + + if (nFadeOutSet == ATTR_SET) + pInfo->mbDimPrevious = bFadeOut; + + if (nFadeColorSet == ATTR_SET) + pInfo->maDimColor = aFadeColor; + + if (nInvisibleSet == ATTR_SET) + pInfo->mbDimHide = bInvisible; + + if (nSoundOnSet == ATTR_SET) + pInfo->mbSoundOn = bSoundOn; + + if (nSoundFileSet == ATTR_SET) + pInfo->maSoundFile = aSound; + + if (nPlayFullSet == ATTR_SET) + pInfo->mbPlayFull = bPlayFull; + + if (nClickActionSet == ATTR_SET) + pInfo->meClickAction = eClickAction; + + if (nBookmarkSet == ATTR_SET) + pInfo->SetBookmark( aBookmark ); + + if (nSecondEffectSet == ATTR_SET) + pInfo->meSecondEffect = eSecondEffect; + + if (nSecondSpeedSet == ATTR_SET) + pInfo->meSecondSpeed = eSecondSpeed; + + if (nSecondSoundOnSet == ATTR_SET) + pInfo->mbSecondSoundOn = bSecondSoundOn; + + if (nSecondPlayFullSet == ATTR_SET) + pInfo->mbSecondPlayFull = bSecondPlayFull; + + if (eClickAction == presentation::ClickAction_VERB) + pInfo->mnVerb = static_cast<sal_uInt16>(aBookmark.toInt32()); + } + } + // Set the Undo Group in of the Undo Manager + pUndoMgr->AddUndoAction(std::move(pUndoGroup)); + pUndoMgr->LeaveListAction(); + + // Model changed + mpDoc->SetChanged(); + // not seen, therefore we do not need to invalidate at the bindings +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuolbull.cxx b/sd/source/ui/func/fuolbull.cxx new file mode 100644 index 000000000..beb57db5b --- /dev/null +++ b/sd/source/ui/func/fuolbull.cxx @@ -0,0 +1,340 @@ +/* -*- 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 <fuolbull.hxx> +#include <svl/intitem.hxx> +#include <svl/stritem.hxx> +#include <editeng/outliner.hxx> +#include <editeng/eeitem.hxx> +#include <sfx2/request.hxx> +#include <editeng/numitem.hxx> +#include <strings.hxx> + +#include <svx/svxids.hrc> +#include <OutlineView.hxx> +#include <DrawDocShell.hxx> +#include <DrawViewShell.hxx> +#include <Window.hxx> +#include <drawdoc.hxx> +#include <sdabstdlg.hxx> +#include <svx/nbdtmg.hxx> +#include <svx/nbdtmgfact.hxx> +#include <svx/svdoutl.hxx> +#include <memory> + +using namespace svx::sidebar; +namespace sd { + +FuBulletAndPosition::FuBulletAndPosition(ViewShell* pViewShell, ::sd::Window* pWindow, + ::sd::View* pView, SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewShell, pWindow, pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuBulletAndPosition::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuBulletAndPosition( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuBulletAndPosition::DoExecute( SfxRequest& rReq ) +{ + const sal_uInt16 nSId = rReq.GetSlot(); + if ( nSId == FN_SVX_SET_BULLET || nSId == FN_SVX_SET_NUMBER ) + { + SetCurrentBulletsNumbering(rReq); + return; + } + + const SfxItemSet* pArgs = rReq.GetArgs(); + const SfxStringItem* pPageItem = SfxItemSet::GetItem<SfxStringItem>(pArgs, FN_PARAM_1, false); + + if ( pArgs && !pPageItem ) + { + /* not direct to pOlView; therefore, SdDrawView::SetAttributes can catch + changes to master page and redirect to a template */ + mpView->SetAttributes(*pArgs); + return; + } + + // fill ItemSet for Dialog + SfxItemSet aEditAttr( mpDoc->GetPool() ); + mpView->GetAttributes( aEditAttr ); + + SfxItemSetFixed<EE_PARA_NUMBULLET, EE_PARA_BULLET> aNewAttr( mpViewShell->GetPool() ); + aNewAttr.Put( aEditAttr, false ); + + auto pView = mpView; + + // create and execute dialog + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSvxBulletAndPositionDlg> pDlg(pFact->CreateSvxBulletAndPositionDlg(mpViewShell->GetFrameWeld(), &aNewAttr, mpView)); + sal_uInt16 nResult = pDlg->Execute(); + + if( nResult == RET_OK ) + { + OutlinerView* pOLV = pView->GetTextEditOutlinerView(); + + std::unique_ptr<OutlineViewModelChangeGuard, o3tl::default_delete<OutlineViewModelChangeGuard>> aGuard; + + if (OutlineView* pOutlineView = dynamic_cast<OutlineView*>(pView)) + { + pOLV = pOutlineView->GetViewByWindow(mpViewShell->GetActiveWindow()); + aGuard.reset(new OutlineViewModelChangeGuard(*pOutlineView)); + } + + if( pOLV ) + pOLV->EnsureNumberingIsOn(); + + const SfxItemSet pOutputSet( *pDlg->GetOutputItemSet( &aNewAttr ) ); + pView->SetAttributes(pOutputSet, /*bReplaceAll=*/false, /*bSlide*/ pDlg->IsSlideScope(), /*bMaster=*/pDlg->IsApplyToMaster()); + } + + rReq.Done(); +} + +void FuBulletAndPosition::SetCurrentBulletsNumbering(SfxRequest& rReq) +{ + if (!mpDoc || !mpView) + return; + + const sal_uInt16 nSId = rReq.GetSlot(); + if ( nSId != FN_SVX_SET_BULLET && nSId != FN_SVX_SET_NUMBER ) + { + // unexpected SfxRequest + return; + } + + const SfxUInt16Item* pItem = rReq.GetArg<SfxUInt16Item>(nSId); + if ( !pItem ) + { + rReq.Done(); + return; + } + + SfxItemSetFixed<EE_ITEMS_START, EE_ITEMS_END> aNewAttr( mpViewShell->GetPool() ); + { + SfxItemSet aEditAttr( mpDoc->GetPool() ); + mpView->GetAttributes( aEditAttr ); + aNewAttr.Put( aEditAttr, false ); + } + + const DrawViewShell* pDrawViewShell = dynamic_cast< DrawViewShell* >(mpViewShell); + //Init bullet level in "Customize" tab page in bullet dialog in master page view + const bool bInMasterView = pDrawViewShell && pDrawViewShell->GetEditMode() == EditMode::MasterPage; + if ( bInMasterView ) + { + SdrObject* pObj = mpView->GetTextEditObject(); + if( pObj && pObj->GetObjIdentifier() == SdrObjKind::OutlineText ) + { + const sal_uInt16 nLevel = mpView->GetSelectionLevel(); + if( nLevel != 0xFFFF ) + { + //save the itemset value + SfxItemSet aStoreSet( aNewAttr ); + aNewAttr.ClearItem(); + //extend range + aNewAttr.MergeRange( SID_PARAM_NUM_PRESET, SID_PARAM_CUR_NUM_LEVEL ); + aNewAttr.Put( aStoreSet ); + //put current level user selected + aNewAttr.Put( SfxUInt16Item( SID_PARAM_CUR_NUM_LEVEL, nLevel ) ); + } + } + } + + sal_uInt16 nIdx = pItem->GetValue(); + bool bToggle = false; + if( nIdx == sal_uInt16(0xFFFF) ) + { + // If the nIdx is (sal_uInt16)0xFFFF, means set bullet status to on/off + nIdx = 1; + bToggle = true; + } + nIdx--; + + TypedWhichId<SvxNumBulletItem> nNumItemId = SID_ATTR_NUMBERING_RULE; + const SfxPoolItem* pTmpItem = GetNumBulletItem( aNewAttr, nNumItemId ); + std::unique_ptr<SvxNumRule> pNumRule; + if ( pTmpItem ) + { + pNumRule.reset(new SvxNumRule(static_cast<const SvxNumBulletItem*>(pTmpItem)->GetNumRule())); + + // get numbering rule corresponding to <nIdx> and apply the needed number formats to <pNumRule> + NBOTypeMgrBase* pNumRuleMgr = + NBOutlineTypeMgrFact::CreateInstance( + nSId == FN_SVX_SET_BULLET ? NBOType::Bullets : NBOType::Numbering ); + if ( pNumRuleMgr ) + { + sal_uInt16 nActNumLvl = sal_uInt16(0xFFFF); + if(const SfxUInt16Item* pNumLevelItem = aNewAttr.GetItemIfSet(SID_PARAM_CUR_NUM_LEVEL, false)) + nActNumLvl = pNumLevelItem->GetValue(); + + pNumRuleMgr->SetItems(&aNewAttr); + SvxNumRule aTmpRule( *pNumRule ); + if ( nSId == FN_SVX_SET_BULLET && bToggle && nIdx==0 ) + { + // for toggling bullets get default numbering rule + pNumRuleMgr->ApplyNumRule( aTmpRule, nIdx, nActNumLvl, true ); + } + else + { + pNumRuleMgr->ApplyNumRule( aTmpRule, nIdx, nActNumLvl ); + } + + sal_uInt16 nMask = 1; + for(sal_uInt16 i = 0; i < pNumRule->GetLevelCount(); i++) + { + if(nActNumLvl & nMask) + { + const SvxNumberFormat& aFmt(aTmpRule.GetLevel(i)); + pNumRule->SetLevel(i, aFmt); + } + nMask <<= 1; + } + } + } + + OutlinerView* pOLV = mpView->GetTextEditOutlinerView(); + std::unique_ptr<OutlineViewModelChangeGuard, o3tl::default_delete<OutlineViewModelChangeGuard>> aGuard; + if (OutlineView* pView = dynamic_cast<OutlineView*>(mpView)) + { + pOLV = pView->GetViewByWindow(mpViewShell->GetActiveWindow()); + aGuard.reset(new OutlineViewModelChangeGuard(*pView)); + } + + SdrOutliner* pOwner = bInMasterView ? mpView->GetTextEditOutliner() : nullptr; + const bool bOutlinerUndoEnabled = pOwner && !pOwner->IsInUndo() && pOwner->IsUndoEnabled(); + SdrModel* pSdrModel = bInMasterView ? mpView->GetModel() : nullptr; + const bool bModelUndoEnabled = pSdrModel && pSdrModel->IsUndoEnabled(); + + if ( bOutlinerUndoEnabled ) + { + pOwner->UndoActionStart( OLUNDO_ATTR ); + } + else if ( bModelUndoEnabled ) + { + pSdrModel->BegUndo(); + } + + if ( pOLV ) + { + pOLV->ToggleBulletsNumbering( bToggle, nSId == FN_SVX_SET_BULLET, bInMasterView ? nullptr : pNumRule.get() ); + } + else + { + mpView->ChangeMarkedObjectsBulletsNumbering( bToggle, nSId == FN_SVX_SET_BULLET, bInMasterView ? nullptr : pNumRule.get() ); + } + + if (bInMasterView && pNumRule) + { + SfxItemSetFixed<EE_ITEMS_START, EE_ITEMS_END> aSetAttr( mpViewShell->GetPool() ); + aSetAttr.Put(SvxNumBulletItem( *pNumRule, nNumItemId )); + mpView->SetAttributes(aSetAttr); + } + + if( bOutlinerUndoEnabled ) + { + pOwner->UndoActionEnd(); + } + else if ( bModelUndoEnabled ) + { + pSdrModel->EndUndo(); + } + + pNumRule.reset(); + rReq.Done(); +} + +const SvxNumBulletItem* FuBulletAndPosition::GetNumBulletItem(SfxItemSet& aNewAttr, TypedWhichId<SvxNumBulletItem>& nNumItemId) +{ + const SvxNumBulletItem* pTmpItem = aNewAttr.GetItemIfSet(nNumItemId, false); + + if(pTmpItem) + return pTmpItem; + + nNumItemId = aNewAttr.GetPool()->GetWhich(SID_ATTR_NUMBERING_RULE); + pTmpItem = aNewAttr.GetItemIfSet(nNumItemId, false); + if (pTmpItem) + return pTmpItem; + + bool bOutliner = false; + bool bTitle = false; + + if( mpView ) + { + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + const size_t nCount = rMarkList.GetMarkCount(); + + for(size_t nNum = 0; nNum < nCount; ++nNum) + { + SdrObject* pObj = rMarkList.GetMark(nNum)->GetMarkedSdrObj(); + if( pObj->GetObjInventor() == SdrInventor::Default ) + { + switch(pObj->GetObjIdentifier()) + { + case SdrObjKind::TitleText: + bTitle = true; + break; + case SdrObjKind::OutlineText: + bOutliner = true; + break; + default: + break; + } + } + } + } + + const SvxNumBulletItem *pItem = nullptr; + if(bOutliner) + { + SfxStyleSheetBasePool* pSSPool = mpView->GetDocSh()->GetStyleSheetPool(); + SfxStyleSheetBase* pFirstStyleSheet = pSSPool->Find( STR_LAYOUT_OUTLINE + " 1", SfxStyleFamily::Pseudo); + if( pFirstStyleSheet ) + pItem = pFirstStyleSheet->GetItemSet().GetItemIfSet(EE_PARA_NUMBULLET, false); + } + + if( pItem == nullptr ) + pItem = aNewAttr.GetPool()->GetSecondaryPool()->GetPoolDefaultItem(EE_PARA_NUMBULLET); + + //DBG_ASSERT( pItem, "No EE_PARA_NUMBULLET in the Pool!" ); + + aNewAttr.Put(pItem->CloneSetWhich(EE_PARA_NUMBULLET)); + + if(bTitle && aNewAttr.GetItemState(EE_PARA_NUMBULLET) == SfxItemState::SET ) + { + const SvxNumBulletItem* pBulletItem = aNewAttr.GetItem(EE_PARA_NUMBULLET); + const SvxNumRule& rLclRule = pBulletItem->GetNumRule(); + SvxNumRule aNewRule( rLclRule ); + aNewRule.SetFeatureFlag( SvxNumRuleFlags::NO_NUMBERS ); + + SvxNumBulletItem aNewItem( std::move(aNewRule), EE_PARA_NUMBULLET ); + aNewAttr.Put(aNewItem); + } + + pTmpItem = aNewAttr.GetItemIfSet(nNumItemId, false); + return pTmpItem; +} + + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuoltext.cxx b/sd/source/ui/func/fuoltext.cxx new file mode 100644 index 000000000..fe64cac47 --- /dev/null +++ b/sd/source/ui/func/fuoltext.cxx @@ -0,0 +1,305 @@ +/* -*- 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 <fuoltext.hxx> + +#include <sfx2/viewfrm.hxx> +#include <editeng/outliner.hxx> +#include <editeng/flditem.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/dispatch.hxx> +#include <tools/debug.hxx> +#include <svl/stritem.hxx> + +#include <svx/svxids.hrc> +#include <app.hrc> +#include <OutlineView.hxx> +#include <Window.hxx> +#include <DrawDocShell.hxx> +#include <ViewShell.hxx> +#include <OutlineViewShell.hxx> + +#include <memory> + +namespace sd { + +const sal_uInt16 SidArray[] = { + SID_STYLE_FAMILY2, + SID_STYLE_FAMILY3, + SID_STYLE_FAMILY5, + SID_STYLE_UPDATE_BY_EXAMPLE, + SID_CUT, + SID_COPY, + SID_PASTE, + SID_SELECTALL, + SID_ATTR_CHAR_FONT, + SID_ATTR_CHAR_POSTURE, + SID_ATTR_CHAR_WEIGHT, + SID_ATTR_CHAR_SHADOWED, + SID_ATTR_CHAR_STRIKEOUT, + SID_ATTR_CHAR_UNDERLINE, + SID_ATTR_CHAR_FONTHEIGHT, + SID_ATTR_CHAR_COLOR, + SID_ATTR_CHAR_KERNING, + SID_OUTLINE_UP, + SID_OUTLINE_DOWN, + SID_OUTLINE_LEFT, + SID_OUTLINE_RIGHT, + //SID_OUTLINE_FORMAT, + SID_OUTLINE_COLLAPSE_ALL, + //SID_OUTLINE_BULLET, + SID_OUTLINE_COLLAPSE, + SID_OUTLINE_EXPAND_ALL, + SID_OUTLINE_EXPAND, + SID_SET_SUPER_SCRIPT, + SID_SET_SUB_SCRIPT, + SID_HYPERLINK_GETLINK, + SID_DEC_INDENT, + SID_INC_INDENT, + SID_PARASPACE_INCREASE, + SID_PARASPACE_DECREASE, + SID_SCALE, + SID_STATUS_PAGE, + SID_STATUS_LAYOUT, + SID_EXPAND_PAGE, + SID_SUMMARY_PAGE, + 0 }; + + +FuOutlineText::FuOutlineText(ViewShell* pViewShell, ::sd::Window* pWindow, + ::sd::View* pView, SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewShell, pWindow, pView, pDoc, rReq), + pOutlineViewShell (static_cast<OutlineViewShell*>(pViewShell)), + pOutlineView (static_cast<OutlineView*>(pView)) +{ +} + +/** + * forward to OutlinerView + */ +bool FuOutlineText::Command(const CommandEvent& rCEvt) +{ + bool bResult = false; + + OutlinerView* pOlView = + static_cast<OutlineView*>(mpView)->GetViewByWindow(mpWindow); + DBG_ASSERT (pOlView, "no OutlineView found"); + + if (pOlView) + { + pOlView->Command(rCEvt); // unfortunately, we do not get a return value + bResult = true; + } + return bResult; +} + + +rtl::Reference<FuPoor> FuOutlineText::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuOutlineText( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute( rReq ); + return xFunc; +} + +bool FuOutlineText::MouseButtonDown(const MouseEvent& rMEvt) +{ + mpWindow->GrabFocus(); + + bool bReturn = pOutlineView->GetViewByWindow(mpWindow)->MouseButtonDown(rMEvt); + + if (bReturn) + { + // Now the attributes of the current text position can be different + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SidArray ); + } + else + { + bReturn = FuPoor::MouseButtonDown(rMEvt); + } + + return bReturn; +} + +bool FuOutlineText::MouseMove(const MouseEvent& rMEvt) +{ + bool bReturn = pOutlineView->GetViewByWindow(mpWindow)->MouseMove(rMEvt); + + if (!bReturn) + { + bReturn = FuPoor::MouseMove(rMEvt); + } + + return bReturn; +} + +bool FuOutlineText::MouseButtonUp(const MouseEvent& rMEvt) +{ + bool bReturn = pOutlineView->GetViewByWindow(mpWindow)->MouseButtonUp(rMEvt); + + if (bReturn) + { + // Now the attributes of the current text position can be different + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SidArray ); + } + else + { + const SvxFieldItem* pFieldItem = pOutlineView->GetViewByWindow( mpWindow )->GetFieldUnderMousePointer(); + if( pFieldItem ) + { + const SvxFieldData* pField = pFieldItem->GetField(); + + if( auto pURLField = dynamic_cast< const SvxURLField *>( pField ) ) + { + bReturn = true; + mpWindow->ReleaseMouse(); + SfxStringItem aStrItem( SID_FILE_NAME, pURLField->GetURL() ); + SfxStringItem aReferer( SID_REFERER, mpDocSh->GetMedium()->GetName() ); + SfxBoolItem aBrowseItem( SID_BROWSE, true ); + SfxViewFrame* pFrame = mpViewShell->GetViewFrame(); + + if ( rMEvt.IsMod1() ) + { + // open in new frame + pFrame->GetDispatcher()->ExecuteList(SID_OPENDOC, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aStrItem, &aBrowseItem, &aReferer }); + } + else + { + // open in current frame + SfxFrameItem aFrameItem( SID_DOCFRAME, pFrame ); + pFrame->GetDispatcher()->ExecuteList(SID_OPENDOC, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aStrItem, &aFrameItem, &aBrowseItem, &aReferer }); + } + } + } + } + + if( !bReturn ) + bReturn = FuPoor::MouseButtonUp(rMEvt); + + return bReturn; +} + +/** + * Process keyboard input + * @returns sal_True if a KeyEvent is being processed, sal_False otherwise + */ +bool FuOutlineText::KeyInput(const KeyEvent& rKEvt) +{ + bool bReturn = false; + + sal_uInt16 nKeyGroup = rKEvt.GetKeyCode().GetGroup(); + if( !mpDocSh->IsReadOnly() || nKeyGroup == KEYGROUP_CURSOR ) + { + mpWindow->GrabFocus(); + + std::unique_ptr<OutlineViewModelChangeGuard, o3tl::default_delete<OutlineViewModelChangeGuard>> aGuard; + if( (nKeyGroup != KEYGROUP_CURSOR) && (nKeyGroup != KEYGROUP_FKEYS) ) + aGuard.reset( new OutlineViewModelChangeGuard( *pOutlineView ) ); + + bReturn = pOutlineView->GetViewByWindow(mpWindow)->PostKeyEvent(rKEvt); + + if (bReturn) + { + UpdateForKeyPress (rKEvt); + } + else + { + bReturn = FuPoor::KeyInput(rKEvt); + } + } + + return bReturn; +} + +void FuOutlineText::UpdateForKeyPress (const KeyEvent& rEvent) +{ + // Attributes at the current text position may have changed. + mpViewShell->GetViewFrame()->GetBindings().Invalidate(SidArray); + + bool bUpdatePreview = true; + switch (rEvent.GetKeyCode().GetCode()) + { + // When just the cursor has been moved the preview only changes when + // it moved to entries of another page. To prevent unnecessary + // updates we check this here. This is an early rejection test, so + // missing a key is not a problem. + case KEY_UP: + case KEY_DOWN: + case KEY_LEFT: + case KEY_RIGHT: + case KEY_HOME: + case KEY_END: + case KEY_PAGEUP: + case KEY_PAGEDOWN: + { + SdPage* pCurrentPage = pOutlineViewShell->GetActualPage(); + bUpdatePreview = (pCurrentPage != pOutlineViewShell->GetActualPage()); + } + break; + } + if (bUpdatePreview) + pOutlineViewShell->UpdatePreview (pOutlineViewShell->GetActualPage()); +} + +/** + * Cut object to clipboard + */ +void FuOutlineText::DoCut() +{ + pOutlineView->GetViewByWindow(mpWindow)->Cut(); +} + +/** + * Copy object to clipboard + */ +void FuOutlineText::DoCopy() +{ + pOutlineView->GetViewByWindow(mpWindow)->Copy(); +} + +/** + * Paste object from clipboard + */ +void FuOutlineText::DoPaste() +{ + pOutlineView->GetViewByWindow(mpWindow)->PasteSpecial(); +} + +/** + * Paste object as unformatted text from clipboard + */ +void FuOutlineText::DoPasteUnformatted() +{ + TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( mpViewShell->GetActiveWindow() ) ); + if (aDataHelper.GetTransferable().is()) + { + OUString aText; + if (aDataHelper.GetString(SotClipboardFormatId::STRING, aText)) + pOutlineView->GetViewByWindow(mpWindow)->InsertText(aText); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fupage.cxx b/sd/source/ui/func/fupage.cxx new file mode 100644 index 000000000..5427e6b7d --- /dev/null +++ b/sd/source/ui/func/fupage.cxx @@ -0,0 +1,648 @@ +/* -*- 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 <fupage.hxx> + +// arrange Tab-Page + +#include <sfx2/sfxdlg.hxx> +#include <svx/pageitem.hxx> +#include <svx/svxids.hrc> +#include <svl/itempool.hxx> +#include <svl/grabbagitem.hxx> +#include <sfx2/request.hxx> +#include <vcl/prntypes.hxx> +#include <vcl/graphicfilter.hxx> +#include <stlsheet.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/frmdiritem.hxx> +#include <svx/graphichelper.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xbtmpit.hxx> +#include <svx/xflbstit.hxx> +#include <svx/xflbmtit.hxx> +#include <svx/xflgrit.hxx> +#include <svx/xflhtit.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/lrspitem.hxx> +#include <svx/sdr/properties/properties.hxx> +#include <editeng/shaditem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/sizeitem.hxx> +#include <editeng/pbinitem.hxx> +#include <sfx2/opengrf.hxx> +#include <sal/log.hxx> + +#include <strings.hrc> +#include <sdpage.hxx> +#include <View.hxx> +#include <Window.hxx> +#include <pres.hxx> +#include <drawdoc.hxx> +#include <DrawDocShell.hxx> +#include <ViewShell.hxx> +#include <DrawViewShell.hxx> +#include <app.hrc> +#include <unchss.hxx> +#include <undoback.hxx> +#include <sdabstdlg.hxx> +#include <sdresid.hxx> + +#include <memory> + +using namespace com::sun::star; + +namespace sd { + +// 50 cm 28350 +// adapted from writer +#define MAXHEIGHT 28350 +#define MAXWIDTH 28350 + + +static void mergeItemSetsImpl( SfxItemSet& rTarget, const SfxItemSet& rSource ) +{ + const WhichRangesContainer& rRanges = rSource.GetRanges(); + sal_uInt16 p1, p2; + for (sal_Int32 i = 0; i < rRanges.size(); ++i) + { + p1 = rRanges[i].first; + p2 = rRanges[i].second; + + // make ranges discrete + while(i < rRanges.size()-1 && (rRanges[i+1].first - p2 == 1)) + { + p2 = rRanges[i+1].second; + ++i; + } + rTarget.MergeRange( p1, p2 ); + } + + rTarget.Put(rSource); +} + +FuPage::FuPage( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, + SdDrawDocument* pDoc, SfxRequest& rReq ) +: FuPoor(pViewSh, pWin, pView, pDoc, rReq), + mrReq(rReq), + mpArgs( rReq.GetArgs() ), + mbPageBckgrdDeleted( false ), + mbMasterPage( false ), + mbDisplayBackgroundTabPage( true ), + mpPage(nullptr), + mpDrawViewShell(nullptr) +{ +} + +rtl::Reference<FuPoor> FuPage::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuPage( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuPage::DoExecute(SfxRequest& rReq) +{ + mpDrawViewShell = dynamic_cast<DrawViewShell*>(mpViewShell); + DBG_ASSERT( mpDrawViewShell, "sd::FuPage::FuPage(), called without a current DrawViewShell!" ); + + if( mpDrawViewShell ) + { + mbMasterPage = mpDrawViewShell->GetEditMode() == EditMode::MasterPage; + // we don't really want to format page background with SID_ATTR_PAGE[_SIZE] slots + mbDisplayBackgroundTabPage = ( mpDrawViewShell->GetPageKind() == PageKind::Standard) && + ( nSlotId != SID_ATTR_PAGE_SIZE) && ( nSlotId != SID_ATTR_PAGE ); + mpPage = mpDrawViewShell->getCurrentPage(); + } + + if( !mpPage ) + return; + + // if there are no arguments given, open the dialog + if (!mpArgs || mpArgs->GetItemState(SID_SELECT_BACKGROUND) == SfxItemState::SET) + { + mpView->SdrEndTextEdit(); + mpArgs = ExecuteDialog(mpWindow ? mpWindow->GetFrameWeld() : nullptr, rReq); + } + + // if we now have arguments, apply them to current page + if( mpArgs ) + { + ApplyItemSet( mpArgs ); + } +} + +FuPage::~FuPage() +{ +} + +void FuPage::Activate() +{ +} + +void FuPage::Deactivate() +{ +} + +void MergePageBackgroundFilling(SdPage *pPage, SdStyleSheet *pStyleSheet, bool bMasterPage, SfxItemSet& rMergedAttr) +{ + if (bMasterPage) + { + if (pStyleSheet) + mergeItemSetsImpl(rMergedAttr, pStyleSheet->GetItemSet()); + } + else + { + // Only this page, get attributes for background fill + const SfxItemSet& rBackgroundAttributes = pPage->getSdrPageProperties().GetItemSet(); + + if(drawing::FillStyle_NONE != rBackgroundAttributes.Get(XATTR_FILLSTYLE).GetValue()) + { + // page attributes are used, take them + rMergedAttr.Put(rBackgroundAttributes); + } + else + { + if(pStyleSheet + && drawing::FillStyle_NONE != pStyleSheet->GetItemSet().Get(XATTR_FILLSTYLE).GetValue()) + { + // if the page has no fill style, use the settings from the + // background stylesheet (if used) + mergeItemSetsImpl(rMergedAttr, pStyleSheet->GetItemSet()); + } + else + { + // no fill style from page, start with no fill style + rMergedAttr.Put(XFillStyleItem(drawing::FillStyle_NONE)); + } + } + } +} + +const SfxItemSet* FuPage::ExecuteDialog(weld::Window* pParent, const SfxRequest& rReq) +{ + if (!mpDrawViewShell) + return nullptr; + + SfxItemSetFixed< + XATTR_FILL_FIRST, XATTR_FILL_LAST, + EE_PARA_WRITINGDIR, EE_PARA_WRITINGDIR, + SID_ATTR_BORDER_OUTER, SID_ATTR_BORDER_OUTER, + SID_ATTR_BORDER_SHADOW, SID_ATTR_BORDER_SHADOW, + SID_ATTR_PAGE, SID_ATTR_PAGE_SHARED, + SID_ATTR_CHAR_GRABBAG, SID_ATTR_CHAR_GRABBAG, + SID_ATTR_PAGE_COLOR, SID_ATTR_PAGE_FILLSTYLE + > aNewAttr(mpDoc->GetPool()); + // Keep it sorted + aNewAttr.MergeRange(mpDoc->GetPool().GetWhich(SID_ATTR_LRSPACE), + mpDoc->GetPool().GetWhich(SID_ATTR_ULSPACE)); + + // Retrieve additional data for dialog + + SvxShadowItem aShadowItem(SID_ATTR_BORDER_SHADOW); + aNewAttr.Put( aShadowItem ); + SvxBoxItem aBoxItem( SID_ATTR_BORDER_OUTER ); + aNewAttr.Put( aBoxItem ); + + aNewAttr.Put( SvxFrameDirectionItem( + mpDoc->GetDefaultWritingMode() == css::text::WritingMode_RL_TB ? SvxFrameDirection::Horizontal_RL_TB : SvxFrameDirection::Horizontal_LR_TB, + EE_PARA_WRITINGDIR ) ); + + // Retrieve page-data for dialog + + SvxPageItem aPageItem( SID_ATTR_PAGE ); + aPageItem.SetDescName( mpPage->GetName() ); + aPageItem.SetPageUsage( SvxPageUsage::All ); + aPageItem.SetLandscape( mpPage->GetOrientation() == Orientation::Landscape ); + aPageItem.SetNumType( mpDoc->GetPageNumType() ); + aNewAttr.Put( aPageItem ); + + // size + maSize = mpPage->GetSize(); + SvxSizeItem aSizeItem( SID_ATTR_PAGE_SIZE, maSize ); + aNewAttr.Put( aSizeItem ); + + // Max size + SvxSizeItem aMaxSizeItem( SID_ATTR_PAGE_MAXSIZE, Size( MAXWIDTH, MAXHEIGHT ) ); + aNewAttr.Put( aMaxSizeItem ); + + // paperbin + SvxPaperBinItem aPaperBinItem( SID_ATTR_PAGE_PAPERBIN, static_cast<sal_uInt8>(mpPage->GetPaperBin()) ); + aNewAttr.Put( aPaperBinItem ); + + SvxLRSpaceItem aLRSpaceItem( static_cast<sal_uInt16>(mpPage->GetLeftBorder()), static_cast<sal_uInt16>(mpPage->GetRightBorder()), 0, 0, mpDoc->GetPool().GetWhich(SID_ATTR_LRSPACE)); + aNewAttr.Put( aLRSpaceItem ); + + SvxULSpaceItem aULSpaceItem( static_cast<sal_uInt16>(mpPage->GetUpperBorder()), static_cast<sal_uInt16>(mpPage->GetLowerBorder()), mpDoc->GetPool().GetWhich(SID_ATTR_ULSPACE)); + aNewAttr.Put( aULSpaceItem ); + + // Application + bool bScale = mpDoc->GetDocumentType() != DocumentType::Draw; + aNewAttr.Put( SfxBoolItem( SID_ATTR_PAGE_EXT1, bScale ) ); + + bool bFullSize = mpPage->IsMasterPage() ? + mpPage->IsBackgroundFullSize() : static_cast<SdPage&>(mpPage->TRG_GetMasterPage()).IsBackgroundFullSize(); + + SfxGrabBagItem grabBag(SID_ATTR_CHAR_GRABBAG); + grabBag.GetGrabBag()["BackgroundFullSize"] <<= bFullSize; + + if (mpDoc->GetDocumentType() == DocumentType::Impress && mpPage->IsMasterPage()) + { + // A master slide may have a theme. + svx::Theme* pTheme = mpPage->getSdrPageProperties().GetTheme(); + if (pTheme) + { + uno::Any aTheme; + pTheme->ToAny(aTheme); + grabBag.GetGrabBag()["Theme"] = aTheme; + } + } + + aNewAttr.Put(grabBag); + + // Merge ItemSet for dialog + + const WhichRangesContainer& rRanges = aNewAttr.GetRanges(); + sal_uInt16 p1 = rRanges[0].first, p2 = rRanges[0].second; + sal_Int32 idx = 1; + while(idx < rRanges.size() && (rRanges[idx].first - p2 == 1)) + { + p2 = rRanges[idx].second; + ++idx; + } + SfxItemSet aMergedAttr( *aNewAttr.GetPool(), p1, p2 ); + + mergeItemSetsImpl( aMergedAttr, aNewAttr ); + + SdStyleSheet* pStyleSheet = mpPage->getPresentationStyle(HID_PSEUDOSHEET_BACKGROUND); + + // merge page background filling to the dialogs input set + if( mbDisplayBackgroundTabPage ) + { + MergePageBackgroundFilling(mpPage, pStyleSheet, mbMasterPage, aMergedAttr); + } + + std::optional< SfxItemSet > pTempSet; + + const sal_uInt16 nId = GetSlotID(); + if (nId == SID_SAVE_BACKGROUND) + { + const XFillStyleItem& rStyleItem = aMergedAttr.Get(XATTR_FILLSTYLE); + if (drawing::FillStyle_BITMAP == rStyleItem.GetValue()) + { + const XFillBitmapItem& rBitmap = aMergedAttr.Get(XATTR_FILLBITMAP); + const GraphicObject& rGraphicObj = rBitmap.GetGraphicObject(); + GraphicHelper::ExportGraphic(pParent, rGraphicObj.GetGraphic(), ""); + } + } + else if (nId == SID_SELECT_BACKGROUND) + { + Graphic aGraphic; + ErrCode nError = ERRCODE_GRFILTER_OPENERROR; + + const SfxItemSet* pArgs = rReq.GetArgs(); + const SfxPoolItem* pItem; + + if (pArgs && pArgs->GetItemState(SID_SELECT_BACKGROUND, true, &pItem) == SfxItemState::SET) + { + OUString aFileName(static_cast<const SfxStringItem*>(pItem)->GetValue()); + OUString aFilterName; + + if (const SfxStringItem* pFilterItem = pArgs->GetItemIfSet(FN_PARAM_FILTER)) + aFilterName = pFilterItem->GetValue(); + + nError = GraphicFilter::LoadGraphic(aFileName, aFilterName, aGraphic, + &GraphicFilter::GetGraphicFilter()); + } + else + { + SvxOpenGraphicDialog aDlg(SdResId(STR_SET_BACKGROUND_PICTURE), pParent); + + nError = aDlg.Execute(); + if (nError == ERRCODE_NONE) + { + nError = aDlg.GetGraphic(aGraphic); + } + } + + if (nError == ERRCODE_NONE) + { + pTempSet.emplace( mpDoc->GetPool(), svl::Items<XATTR_FILL_FIRST, XATTR_FILL_LAST> ); + + pTempSet->Put( XFillStyleItem( drawing::FillStyle_BITMAP ) ); + + // MigrateItemSet makes sure the XFillBitmapItem will have a unique name + SfxItemSetFixed<XATTR_FILLBITMAP, XATTR_FILLBITMAP> aMigrateSet( mpDoc->GetPool() ); + aMigrateSet.Put(XFillBitmapItem("background", aGraphic)); + SdrModel::MigrateItemSet( &aMigrateSet, &*pTempSet, mpDoc ); + + pTempSet->Put( XFillBmpStretchItem( true )); + pTempSet->Put( XFillBmpTileItem( false )); + } + } + + else + { + bool bIsImpressDoc = mpDrawViewShell->GetDoc()->GetDocumentType() == DocumentType::Impress; + + // create the dialog + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractTabDialog> pDlg( pFact->CreateSdTabPageDialog(mpViewShell->GetFrameWeld(), &aMergedAttr, mpDocSh, mbDisplayBackgroundTabPage, bIsImpressDoc, mbMasterPage) ); + if( pDlg->Execute() == RET_OK ) + pTempSet.emplace( *pDlg->GetOutputItemSet() ); + } + + if (pTempSet && pStyleSheet) + { + pStyleSheet->AdjustToFontHeight(*pTempSet); + + if( mbDisplayBackgroundTabPage ) + { + // if some fillstyle-items are not set in the dialog, then + // try to use the items before + bool bChanges = false; + for( sal_uInt16 i=XATTR_FILL_FIRST; i<XATTR_FILL_LAST; i++ ) + { + if( aMergedAttr.GetItemState( i ) != SfxItemState::DEFAULT ) + { + if( pTempSet->GetItemState( i ) == SfxItemState::DEFAULT ) + pTempSet->Put( aMergedAttr.Get( i ) ); + else + if( aMergedAttr.GetItem( i ) != pTempSet->GetItem( i ) ) + bChanges = true; + } + } + + // if the background for this page was set to invisible, the background-object has to be deleted, too. + const XFillStyleItem* pTempFillStyleItem = pTempSet->GetItem<XFillStyleItem>(XATTR_FILLSTYLE); + assert(pTempFillStyleItem); + if (pTempFillStyleItem->GetValue() == drawing::FillStyle_NONE) + mbPageBckgrdDeleted = true; + else + { + if (pTempSet->GetItemState(XATTR_FILLSTYLE) == SfxItemState::DEFAULT) + { + const XFillStyleItem* pMergedFillStyleItem = aMergedAttr.GetItem<XFillStyleItem>(XATTR_FILLSTYLE); + assert(pMergedFillStyleItem); + if (pMergedFillStyleItem->GetValue() == drawing::FillStyle_NONE) + mbPageBckgrdDeleted = true; + } + } + + const XFillGradientItem* pTempGradItem = pTempSet->GetItem<XFillGradientItem>(XATTR_FILLGRADIENT); + if (pTempGradItem && pTempGradItem->GetName().isEmpty()) + { + // MigrateItemSet guarantees unique gradient names + SfxItemSetFixed<XATTR_FILLGRADIENT, XATTR_FILLGRADIENT> aMigrateSet( mpDoc->GetPool() ); + aMigrateSet.Put( XFillGradientItem("gradient", pTempGradItem->GetGradientValue()) ); + SdrModel::MigrateItemSet( &aMigrateSet, &*pTempSet, mpDoc); + } + + const XFillHatchItem* pTempHatchItem = pTempSet->GetItem<XFillHatchItem>(XATTR_FILLHATCH); + if (pTempHatchItem && pTempHatchItem->GetName().isEmpty()) + { + // MigrateItemSet guarantees unique hatch names + SfxItemSetFixed<XATTR_FILLHATCH, XATTR_FILLHATCH> aMigrateSet( mpDoc->GetPool() ); + aMigrateSet.Put( XFillHatchItem("hatch", pTempHatchItem->GetHatchValue()) ); + SdrModel::MigrateItemSet( &aMigrateSet, &*pTempSet, mpDoc); + } + + if( !mbMasterPage && bChanges && mbPageBckgrdDeleted ) + { + mpBackgroundObjUndoAction.reset( new SdBackgroundObjUndoAction( + *mpDoc, *mpPage, mpPage->getSdrPageProperties().GetItemSet()) ); + + if(!mpPage->IsMasterPage()) + { + // on normal pages, switch off fill attribute usage + SdrPageProperties& rPageProperties = mpPage->getSdrPageProperties(); + rPageProperties.ClearItem( XATTR_FILLBITMAP ); + rPageProperties.ClearItem( XATTR_FILLGRADIENT ); + rPageProperties.ClearItem( XATTR_FILLHATCH ); + rPageProperties.PutItem(XFillStyleItem(drawing::FillStyle_NONE)); + } + } + + + /* Special treatment: reset the INVALIDS to + NULL-Pointer (otherwise INVALIDs or pointer point + to DefaultItems in the template; both would + prevent the attribute inheritance) */ + pTempSet->ClearInvalidItems(); + + if( mbMasterPage ) + { + mpDocSh->GetUndoManager()->AddUndoAction(std::make_unique<StyleSheetUndoAction>( + mpDoc, static_cast<SfxStyleSheet*>(pStyleSheet), &(*pTempSet))); + pStyleSheet->GetItemSet().Put( *pTempSet ); + sdr::properties::CleanupFillProperties( pStyleSheet->GetItemSet() ); + pStyleSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); + } + + // if background filling is set to master pages then clear from page set + if( mbMasterPage ) + { + for( sal_uInt16 nWhich = XATTR_FILL_FIRST; nWhich <= XATTR_FILL_LAST; nWhich++ ) + { + pTempSet->ClearItem( nWhich ); + } + pTempSet->Put(XFillStyleItem(drawing::FillStyle_NONE)); + } + + if( const SvxFrameDirectionItem* pItem = pTempSet->GetItemIfSet( EE_PARA_WRITINGDIR, false ) ) + { + SvxFrameDirection nVal = pItem->GetValue(); + mpDoc->SetDefaultWritingMode( nVal == SvxFrameDirection::Horizontal_RL_TB ? css::text::WritingMode_RL_TB : css::text::WritingMode_LR_TB ); + } + + mpDoc->SetChanged(); + + // BackgroundFill of Masterpage: no hard attributes allowed + SdrPage& rUsedMasterPage = mpPage->IsMasterPage() ? *mpPage : mpPage->TRG_GetMasterPage(); + OSL_ENSURE(rUsedMasterPage.IsMasterPage(), "No MasterPage (!)"); + rUsedMasterPage.getSdrPageProperties().ClearItem(); + OSL_ENSURE(nullptr != rUsedMasterPage.getSdrPageProperties().GetStyleSheet(), + "MasterPage without StyleSheet detected (!)"); + } + + aNewAttr.Put(*pTempSet); + mrReq.Done( aNewAttr ); + + return mrReq.GetArgs(); + } + else + { + return nullptr; + } +} + +void FuPage::ApplyItemSet( const SfxItemSet* pArgs ) +{ + if (!pArgs || !mpDrawViewShell) + return; + + // Set new page-attributes + PageKind ePageKind = mpDrawViewShell->GetPageKind(); + const SfxPoolItem* pPoolItem; + bool bSetPageSizeAndBorder = false; + Size aNewSize(maSize); + sal_Int32 nLeft = -1, nRight = -1, nUpper = -1, nLower = -1; + bool bScaleAll = true; + Orientation eOrientation = mpPage->GetOrientation(); + SdPage* pMasterPage = mpPage->IsMasterPage() ? mpPage : &static_cast<SdPage&>(mpPage->TRG_GetMasterPage()); + bool bFullSize = pMasterPage->IsBackgroundFullSize(); + sal_uInt16 nPaperBin = mpPage->GetPaperBin(); + + if( pArgs->GetItemState(SID_ATTR_PAGE, true, &pPoolItem) == SfxItemState::SET ) + { + mpDoc->SetPageNumType(static_cast<const SvxPageItem*>(pPoolItem)->GetNumType()); + + eOrientation = static_cast<const SvxPageItem*>(pPoolItem)->IsLandscape() ? + Orientation::Landscape : Orientation::Portrait; + + if( mpPage->GetOrientation() != eOrientation ) + bSetPageSizeAndBorder = true; + + mpDrawViewShell->ResetActualPage(); + } + + if( pArgs->GetItemState(SID_ATTR_PAGE_SIZE, true, &pPoolItem) == SfxItemState::SET ) + { + aNewSize = static_cast<const SvxSizeItem*>(pPoolItem)->GetSize(); + + if( mpPage->GetSize() != aNewSize ) + bSetPageSizeAndBorder = true; + } + + if( pArgs->GetItemState(mpDoc->GetPool().GetWhich(SID_ATTR_LRSPACE), + true, &pPoolItem) == SfxItemState::SET ) + { + nLeft = static_cast<const SvxLRSpaceItem*>(pPoolItem)->GetLeft(); + nRight = static_cast<const SvxLRSpaceItem*>(pPoolItem)->GetRight(); + + if( mpPage->GetLeftBorder() != nLeft || mpPage->GetRightBorder() != nRight ) + bSetPageSizeAndBorder = true; + + } + + if( pArgs->GetItemState(mpDoc->GetPool().GetWhich(SID_ATTR_ULSPACE), + true, &pPoolItem) == SfxItemState::SET ) + { + nUpper = static_cast<const SvxULSpaceItem*>(pPoolItem)->GetUpper(); + nLower = static_cast<const SvxULSpaceItem*>(pPoolItem)->GetLower(); + + if( mpPage->GetUpperBorder() != nUpper || mpPage->GetLowerBorder() != nLower ) + bSetPageSizeAndBorder = true; + } + + if( pArgs->GetItemState(mpDoc->GetPool().GetWhich(SID_ATTR_PAGE_EXT1), true, &pPoolItem) == SfxItemState::SET ) + { + bScaleAll = static_cast<const SfxBoolItem*>(pPoolItem)->GetValue(); + } + + if (SfxItemState::SET == pArgs->GetItemState(SID_ATTR_CHAR_GRABBAG, true, &pPoolItem)) + { + SfxGrabBagItem const*const pGrabBag(static_cast<SfxGrabBagItem const*>(pPoolItem)); + if (pGrabBag->GetGrabBag().find("BackgroundFullSize")->second >>= bFullSize) + { + if (pMasterPage->IsBackgroundFullSize() != bFullSize) + { + bSetPageSizeAndBorder = true; + } + } + + if (mpDoc->GetDocumentType() == DocumentType::Impress && mpPage->IsMasterPage()) + { + // The item set may have a theme. + auto it = pGrabBag->GetGrabBag().find("Theme"); + if (it != pGrabBag->GetGrabBag().end()) + { + std::unique_ptr<svx::Theme> pTheme = svx::Theme::FromAny(it->second); + pMasterPage->getSdrPageProperties().SetTheme(std::move(pTheme)); + } + else + { + SAL_WARN("sd.ui", "FuPage::ApplyItemSet: got no theme"); + } + } + } + + // Paper Bin + if( pArgs->GetItemState(mpDoc->GetPool().GetWhich(SID_ATTR_PAGE_PAPERBIN), true, &pPoolItem) == SfxItemState::SET ) + { + nPaperBin = static_cast<const SvxPaperBinItem*>(pPoolItem)->GetValue(); + + if( mpPage->GetPaperBin() != nPaperBin ) + bSetPageSizeAndBorder = true; + } + + if (nLeft == -1 && nUpper != -1) + { + bSetPageSizeAndBorder = true; + nLeft = mpPage->GetLeftBorder(); + nRight = mpPage->GetRightBorder(); + } + else if (nLeft != -1 && nUpper == -1) + { + bSetPageSizeAndBorder = true; + nUpper = mpPage->GetUpperBorder(); + nLower = mpPage->GetLowerBorder(); + } + + if( bSetPageSizeAndBorder || !mbMasterPage ) + mpDrawViewShell->SetPageSizeAndBorder(ePageKind, aNewSize, nLeft, nRight, nUpper, nLower, bScaleAll, eOrientation, nPaperBin, bFullSize ); + + // if bMasterPage==sal_False then create a background-object for this page with the + // properties set in the dialog before, but if mbPageBckgrdDeleted==sal_True then + // the background of this page was set to invisible, so it would be a mistake + // to create a new background-object for this page ! + + if( mbDisplayBackgroundTabPage ) + { + if( !mbMasterPage && !mbPageBckgrdDeleted ) + { + // Only this page + mpBackgroundObjUndoAction.reset( new SdBackgroundObjUndoAction( + *mpDoc, *mpPage, mpPage->getSdrPageProperties().GetItemSet()) ); + SfxItemSet aSet( *pArgs ); + sdr::properties::CleanupFillProperties(aSet); + mpPage->getSdrPageProperties().ClearItem(); + mpPage->getSdrPageProperties().PutItemSet(aSet); + } + } + + // add undo action for background object + if( mpBackgroundObjUndoAction ) + { + // set merge flag, because a SdUndoGroupAction could have been inserted before + mpDocSh->GetUndoManager()->AddUndoAction( std::move(mpBackgroundObjUndoAction), true ); + } + + // Objects can not be bigger than ViewSize + Size aPageSize = mpDoc->GetSdPage(0, ePageKind)->GetSize(); + Size aViewSize(aPageSize.Width() * 3, aPageSize.Height() * 2); + mpDoc->SetMaxObjSize(aViewSize); + + // if necessary, we tell Preview the new context + mpDrawViewShell->UpdatePreview( mpDrawViewShell->GetActualPage() ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuparagr.cxx b/sd/source/ui/func/fuparagr.cxx new file mode 100644 index 000000000..ac5d87636 --- /dev/null +++ b/sd/source/ui/func/fuparagr.cxx @@ -0,0 +1,162 @@ +/* -*- 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 <fuparagr.hxx> +#include <editeng/eeitem.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/request.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/sfxdlg.hxx> +#include <svx/svxids.hrc> +#include <editeng/editdata.hxx> +#include <editeng/lrspitem.hxx> +#include <svx/svdoutl.hxx> +#include <svl/intitem.hxx> + +#include <View.hxx> +#include <ViewShell.hxx> +#include <drawdoc.hxx> +#include <sdabstdlg.hxx> +#include <sdattr.hrc> + +namespace sd { + + +FuParagraph::FuParagraph ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuParagraph::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuParagraph( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuParagraph::DoExecute( SfxRequest& rReq ) +{ + const SfxItemSet* pArgs = rReq.GetArgs(); + + OutlinerView* pOutlView = mpView->GetTextEditOutlinerView(); + ::Outliner* pOutliner = mpView->GetTextEditOutliner(); + + if( !pArgs ) + { + SfxItemSet aEditAttr( mpDoc->GetPool() ); + mpView->GetAttributes( aEditAttr ); + SfxItemPool *pPool = aEditAttr.GetPool(); + SfxItemSetFixed<EE_ITEMS_START, EE_ITEMS_END, + SID_ATTR_TABSTOP_OFFSET, SID_ATTR_TABSTOP_OFFSET, + ATTR_PARANUMBERING_START, ATTR_PARANUMBERING_END> aNewAttr( *pPool ); + + aNewAttr.Put( aEditAttr ); + + // left border is offset + const ::tools::Long nOff = aNewAttr.Get( EE_PARA_LRSPACE ).GetTextLeft(); + // conversion since TabulatorTabPage always uses Twips! + SfxInt32Item aOff( SID_ATTR_TABSTOP_OFFSET, nOff ); + aNewAttr.Put( aOff ); + + if( pOutlView && pOutliner ) + { + ESelection eSelection = pOutlView->GetSelection(); + aNewAttr.Put( SfxInt16Item( ATTR_NUMBER_NEWSTART_AT, pOutliner->GetNumberingStartValue( eSelection.nStartPara ) ) ); + aNewAttr.Put( SfxBoolItem( ATTR_NUMBER_NEWSTART, pOutliner->IsParaIsNumberingRestart( eSelection.nStartPara ) ) ); + } + + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateSdParagraphTabDlg(mpViewShell->GetFrameWeld(), &aNewAttr)); + + sal_uInt16 nResult = pDlg->Execute(); + + switch( nResult ) + { + case RET_OK: + { + rReq.Done( *( pDlg->GetOutputItemSet() ) ); + + pArgs = rReq.GetArgs(); + } + break; + + default: + return; // Cancel + } + } + mpView->SetAttributes( *pArgs ); + + if( pOutlView && pOutliner ) + { + ESelection eSelection = pOutlView->GetSelection(); + + if( const SfxBoolItem* pItem = pArgs->GetItemIfSet( ATTR_NUMBER_NEWSTART, false ) ) + { + const bool bNewStart = pItem->GetValue(); + pOutliner->SetParaIsNumberingRestart( eSelection.nStartPara, bNewStart ); + } + + if( const SfxInt16Item* pItem = pArgs->GetItemIfSet( ATTR_NUMBER_NEWSTART_AT, false ) ) + { + const sal_Int16 nStartAt = pItem->GetValue(); + pOutliner->SetNumberingStartValue( eSelection.nStartPara, nStartAt ); + } + } + + // invalidate slots + static const sal_uInt16 SidArray[] = { + SID_ATTR_TABSTOP, + SID_ATTR_PARA_ADJUST_LEFT, + SID_ATTR_PARA_ADJUST_RIGHT, + SID_ATTR_PARA_ADJUST_CENTER, + SID_ATTR_PARA_ADJUST_BLOCK, + SID_ATTR_PARA_LINESPACE, + SID_ATTR_PARA_LINESPACE_10, + SID_ATTR_PARA_LINESPACE_15, + SID_ATTR_PARA_LINESPACE_20, + SID_ATTR_PARA_ULSPACE, + SID_ATTR_PARA_LRSPACE, + SID_DEC_INDENT, + SID_INC_INDENT, + SID_ATTR_PARA_LEFT_TO_RIGHT, + SID_ATTR_PARA_RIGHT_TO_LEFT, + SID_RULER_TEXT_RIGHT_TO_LEFT, + SID_PARASPACE_INCREASE, + SID_PARASPACE_DECREASE, + 0 }; + + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SidArray ); +} + +void FuParagraph::Activate() +{ +} + +void FuParagraph::Deactivate() +{ +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fupoor.cxx b/sd/source/ui/func/fupoor.cxx new file mode 100644 index 000000000..e901d07a6 --- /dev/null +++ b/sd/source/ui/func/fupoor.cxx @@ -0,0 +1,1135 @@ +/* -*- 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 <fupoor.hxx> + +#include <svx/svxids.hrc> +#include <svx/svdpagv.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdograf.hxx> +#include <vcl/seleng.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/request.hxx> +#include <svl/stritem.hxx> + +#include <app.hrc> +#include <fusel.hxx> +#include <sdpage.hxx> +#include <DrawViewShell.hxx> +#include <Window.hxx> +#include <drawdoc.hxx> +#include <DrawDocShell.hxx> +#include <zoomlist.hxx> +#include <slideshow.hxx> +#include <LayerTabBar.hxx> + +#include <com/sun/star/embed/EmbedVerbs.hpp> + +#include <sfx2/viewfrm.hxx> + +#include <svx/svditer.hxx> + +#include <editeng/editeng.hxx> + +using namespace ::com::sun::star; + +namespace sd { + + +FuPoor::FuPoor ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDrDoc, + SfxRequest& rReq) + : mpView(pView), + mpViewShell(pViewSh), + mpWindow(pWin), + mpDocSh( pDrDoc->GetDocSh() ), + mpDoc(pDrDoc), + nSlotId( rReq.GetSlot() ), + aScrollTimer("sd FuPoor aScrollTimer"), + aDragTimer("sd FuPoor aDragTimer"), + bIsInDragMode(false), + bNoScrollUntilInside (true), + aDelayToScrollTimer("sd FuPoor aDelayToScrollTimer"), + bScrollable (false), + bDelayActive (false), + bFirstMouseMove (false), + // remember MouseButton state + mnCode(0) +{ + ReceiveRequest(rReq); + + aScrollTimer.SetInvokeHandler( LINK(this, FuPoor, ScrollHdl) ); + aScrollTimer.SetTimeout(SELENG_AUTOREPEAT_INTERVAL); + + aDragTimer.SetInvokeHandler( LINK(this, FuPoor, DragHdl) ); + aDragTimer.SetTimeout(SELENG_DRAGDROP_TIMEOUT); + + aDelayToScrollTimer.SetInvokeHandler( LINK(this, FuPoor, DelayHdl) ); + aDelayToScrollTimer.SetTimeout(2000); +} + +FuPoor::~FuPoor() +{ + aDragTimer.Stop(); + aScrollTimer.Stop(); + aDelayToScrollTimer.Stop(); +} + +void FuPoor::Activate() +{ +} + +void FuPoor::Deactivate() +{ + aDragTimer.Stop(); + aScrollTimer.Stop(); + aDelayToScrollTimer.Stop (); + bScrollable = bDelayActive = false; + + if (mpWindow && mpWindow->IsMouseCaptured()) + mpWindow->ReleaseMouse(); +} + +void FuPoor::SetWindow(::sd::Window* pWin) +{ + mpWindow = pWin; +} + +/** + * scroll when approached the border of the window; is called by MouseMove + */ +void FuPoor::ForceScroll(const Point& aPixPos) +{ + aScrollTimer.Stop(); + + if ( mpView->IsDragHelpLine() || mpView->IsSetPageOrg() || + SlideShow::IsRunning( mpViewShell->GetViewShellBase() ) ) + return; + + Point aPos = mpWindow->OutputToScreenPixel(aPixPos); + const ::tools::Rectangle& rRect = mpViewShell->GetAllWindowRect(); + + if ( bNoScrollUntilInside ) + { + if ( rRect.Contains(aPos) ) + bNoScrollUntilInside = false; + } + else + { + short dx = 0, dy = 0; + + if ( aPos.X() <= rRect.Left() ) dx = -1; + if ( aPos.X() >= rRect.Right() ) dx = 1; + if ( aPos.Y() <= rRect.Top() ) dy = -1; + if ( aPos.Y() >= rRect.Bottom() ) dy = 1; + + if ( dx != 0 || dy != 0 ) + { + if (bScrollable) + { + // scroll action in derived class + mpViewShell->ScrollLines(dx, dy); + aScrollTimer.Start(); + } + else if (! bDelayActive) StartDelayToScrollTimer (); + } + } +} + +/** + * timer handler for window scrolling + */ +IMPL_LINK_NOARG(FuPoor, ScrollHdl, Timer *, void) +{ + Point aPnt(mpWindow->GetPointerPosPixel()); + + // use remembered MouseButton state to create correct + // MouseEvents for this artificial MouseMove. + MouseMove(MouseEvent(aPnt, 1, MouseEventModifiers::NONE, GetMouseButtonCode())); +} + +/** + * handle keyboard events + * @returns sal_True if the event was handled, sal_False otherwise + */ +bool FuPoor::KeyInput(const KeyEvent& rKEvt) +{ + sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode(); + bool bReturn = false; + bool bSlideShow = SlideShow::IsRunning( mpViewShell->GetViewShellBase() ); + + switch (nCode) + { + case KEY_RETURN: + { + if(rKEvt.GetKeyCode().IsMod1()) + { + if( auto pDrawViewShell = dynamic_cast<DrawViewShell *>( mpViewShell )) + { + SdPage* pActualPage = pDrawViewShell->GetActualPage(); + SdrTextObj* pCandidate = nullptr; + + if(pActualPage) + { + SdrObjListIter aIter(pActualPage, SdrIterMode::DeepNoGroups); + + while(aIter.IsMore() && !pCandidate) + { + SdrObject* pObj = aIter.Next(); + + if(auto pTextObj = dynamic_cast<SdrTextObj *>( pObj )) + { + SdrInventor nInv(pObj->GetObjInventor()); + SdrObjKind nKnd(pObj->GetObjIdentifier()); + + if(SdrInventor::Default == nInv && + (SdrObjKind::TitleText == nKnd || SdrObjKind::OutlineText == nKnd || SdrObjKind::Text == nKnd)) + { + pCandidate = pTextObj; + } + } + } + } + + if(pCandidate) + { + mpView->UnMarkAll(); + mpView->MarkObj(pCandidate, mpView->GetSdrPageView()); + + mpViewShell->GetViewFrame()->GetDispatcher()->Execute( + SID_ATTR_CHAR, SfxCallMode::ASYNCHRON); + } + else + { + // insert a new page with the same page layout + mpViewShell->GetViewFrame()->GetDispatcher()->Execute( + SID_INSERTPAGE_QUICK, SfxCallMode::ASYNCHRON); + } + + // consumed + bReturn = true; + } + } + else + { + // activate OLE object on RETURN for selected object + // activate text edit on RETURN for selected object + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + if( !mpView->IsTextEdit() && 1 == rMarkList.GetMarkCount() ) + { + SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj(); + + if( dynamic_cast< const SdrOle2Obj* >( pObj ) && !mpDocSh->IsUIActive() ) + { + //HMHmpView->HideMarkHdl(); + mpViewShell->ActivateObject(static_cast<SdrOle2Obj*>(pObj), css::embed::EmbedVerbs::MS_OLEVERB_PRIMARY); + } + else if( pObj && pObj->IsEmptyPresObj() && dynamic_cast< const SdrGrafObj *>( pObj ) != nullptr ) + { + mpViewShell->GetViewFrame()->GetDispatcher()->Execute( SID_INSERT_GRAPHIC, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD ); + } + else + { + mpViewShell->GetViewFrame()->GetDispatcher()->Execute( SID_ATTR_CHAR, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD ); + } + + // consumed + bReturn = true; + } + } + } + break; + + case KEY_TAB: + { + // handle Mod1 and Mod2 to get travelling running on different systems + if(rKEvt.GetKeyCode().IsMod1() || rKEvt.GetKeyCode().IsMod2()) + { + // do something with a selected handle? + const SdrHdlList& rHdlList = mpView->GetHdlList(); + bool bForward(!rKEvt.GetKeyCode().IsShift()); + + const_cast<SdrHdlList&>(rHdlList).TravelFocusHdl(bForward); + + // guarantee visibility of focused handle + SdrHdl* pHdl = rHdlList.GetFocusHdl(); + + if(pHdl) + { + Point aHdlPosition(pHdl->GetPos()); + ::tools::Rectangle aVisRect(aHdlPosition - Point(100, 100), Size(200, 200)); + mpView->MakeVisible(aVisRect, *mpWindow); + } + + // consumed + bReturn = true; + } + } + break; + + case KEY_ESCAPE: + { + bReturn = FuPoor::cancel(); + } + break; + + case KEY_ADD: + { + if (!mpView->IsTextEdit() && !bSlideShow && !mpDocSh->IsUIActive()) + { + // increase zoom + mpViewShell->SetZoom(mpWindow->GetZoom() * 3 / 2); + + if( auto pViewShell = dynamic_cast<DrawViewShell *>( mpViewShell )) + pViewShell->SetZoomOnPage(false); + + bReturn = true; + } + } + break; + + case KEY_SUBTRACT: + { + if (!mpView->IsTextEdit() && !bSlideShow && !mpDocSh->IsUIActive()) + { + // decrease zoom + mpViewShell->SetZoom(mpWindow->GetZoom() * 2 / 3); + + if( auto pViewShell = dynamic_cast<DrawViewShell *>( mpViewShell )) + pViewShell->SetZoomOnPage(false); + + bReturn = true; + } + } + break; + + case KEY_MULTIPLY: + { + if (!mpView->IsTextEdit() && !bSlideShow) + { + // zoom to page + mpViewShell->GetViewFrame()->GetDispatcher()-> + Execute(SID_SIZE_PAGE, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + bReturn = true; + } + } + break; + + case KEY_DIVIDE: + { + if (!mpView->IsTextEdit() && !bSlideShow) + { + // zoom to selected objects + mpViewShell->GetViewFrame()->GetDispatcher()-> + Execute(SID_SIZE_OPTIMAL, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + bReturn = true; + } + } + break; + + case KEY_POINT: + { + ZoomList* pZoomList = mpViewShell->GetZoomList(); + + if (!mpView->IsTextEdit() && pZoomList->IsNextPossible() && !bSlideShow && !mpDocSh->IsUIActive()) + { + // use next ZoomRect + mpViewShell->SetZoomRect(pZoomList->GetNextZoomRect()); + bReturn = true; + } + } + break; + + case KEY_COMMA: + { + ZoomList* pZoomList = mpViewShell->GetZoomList(); + + if (!mpView->IsTextEdit() && pZoomList->IsPreviousPossible() && !bSlideShow && !mpDocSh->IsUIActive()) + { + // use previous ZoomRect + mpViewShell->SetZoomRect(pZoomList->GetPreviousZoomRect()); + bReturn = true; + } + } + break; + + case KEY_HOME: + { + if (!mpView->IsTextEdit() && !bSlideShow) + if (auto pDrawViewShell = dynamic_cast<DrawViewShell *>( mpViewShell )) + { + // jump to first page + pDrawViewShell->SwitchPage(0); + bReturn = true; + } + } + break; + + case KEY_END: + { + if (!mpView->IsTextEdit() && !bSlideShow) + if (auto pDrawViewShell = dynamic_cast<DrawViewShell *>( mpViewShell )) + { + // jump to last page + SdPage* pPage = pDrawViewShell->GetActualPage(); + pDrawViewShell->SwitchPage(mpDoc->GetSdPageCount( + pPage->GetPageKind()) - 1); + bReturn = true; + } + } + break; + + case KEY_PAGEUP: + { + if( rKEvt.GetKeyCode().IsMod1() && rKEvt.GetKeyCode().IsMod2() ) + break; + if( bSlideShow) + break; + + if( auto pDrawViewShell = dynamic_cast<DrawViewShell *>( mpViewShell ) ) + { + // The page-up key switches layers or pages depending on the + // modifier key. + if ( ! rKEvt.GetKeyCode().GetModifier()) + { + // With no modifier pressed we move to the previous + // slide. + mpView->SdrEndTextEdit(); + + // Previous page. + bReturn = true; + SdPage* pPage = pDrawViewShell->GetActualPage(); + sal_uInt16 nSdPage = (pPage->GetPageNum() - 1) / 2; + + if (nSdPage > 0) + { + // Switch the page and send events regarding + // deactivation the old page and activating the new + // one. + TabControl& rPageTabControl = + pDrawViewShell->GetPageTabControl(); + if (rPageTabControl.IsReallyShown()) + rPageTabControl.SendDeactivatePageEvent (); + pDrawViewShell->SwitchPage(nSdPage - 1); + if (rPageTabControl.IsReallyShown()) + rPageTabControl.SendActivatePageEvent (); + } + } + else if (rKEvt.GetKeyCode().IsMod1()) + { + // With the CONTROL modifier we switch layers. + if (pDrawViewShell->IsLayerModeActive()) + { + // Moves to the previous layer. + SwitchLayer (-1); + } + } + } + } + break; + + case KEY_PAGEDOWN: + { + if( rKEvt.GetKeyCode().IsMod1() && rKEvt.GetKeyCode().IsMod2() ) + break; + if(dynamic_cast< const DrawViewShell *>( mpViewShell ) != nullptr && !bSlideShow) + { + // The page-down key switches layers or pages depending on the + // modifier key. + if ( ! rKEvt.GetKeyCode().GetModifier()) + { + // With no modifier pressed we move to the next slide. + mpView->SdrEndTextEdit(); + + // Next page. + bReturn = true; + SdPage* pPage = static_cast<DrawViewShell*>(mpViewShell)->GetActualPage(); + sal_uInt16 nSdPage = (pPage->GetPageNum() - 1) / 2; + + if (nSdPage < mpDoc->GetSdPageCount(pPage->GetPageKind()) - 1) + { + // Switch the page and send events regarding + // deactivation the old page and activating the new + // one. + TabControl& rPageTabControl = + static_cast<DrawViewShell*>(mpViewShell)->GetPageTabControl(); + if (rPageTabControl.IsReallyShown()) + rPageTabControl.SendDeactivatePageEvent (); + static_cast<DrawViewShell*>(mpViewShell)->SwitchPage(nSdPage + 1); + if (rPageTabControl.IsReallyShown()) + rPageTabControl.SendActivatePageEvent (); + } + } + else if (rKEvt.GetKeyCode().IsMod1()) + { + // With the CONTROL modifier we switch layers. + if (static_cast<DrawViewShell*>(mpViewShell)->IsLayerModeActive()) + { + // With the layer mode active pressing page-down + // moves to the next layer. + SwitchLayer (+1); + } + } + } + } + break; + + // change select state when focus is on poly point + case KEY_SPACE: + { + const SdrHdlList& rHdlList = mpView->GetHdlList(); + SdrHdl* pHdl = rHdlList.GetFocusHdl(); + + if(pHdl) + { + if(pHdl->GetKind() == SdrHdlKind::Poly) + { + // rescue ID of point with focus + sal_uInt32 nPol(pHdl->GetPolyNum()); + sal_uInt32 nPnt(pHdl->GetPointNum()); + + if(mpView->IsPointMarked(*pHdl)) + { + if(rKEvt.GetKeyCode().IsShift()) + { + mpView->UnmarkPoint(*pHdl); + } + } + else + { + if(!rKEvt.GetKeyCode().IsShift()) + { + mpView->UnmarkAllPoints(); + } + + mpView->MarkPoint(*pHdl); + } + + if(nullptr == rHdlList.GetFocusHdl()) + { + // restore point with focus + SdrHdl* pNewOne = nullptr; + + for(size_t a = 0; !pNewOne && a < rHdlList.GetHdlCount(); ++a) + { + SdrHdl* pAct = rHdlList.GetHdl(a); + + if(pAct + && pAct->GetKind() == SdrHdlKind::Poly + && pAct->GetPolyNum() == nPol + && pAct->GetPointNum() == nPnt) + { + pNewOne = pAct; + } + } + + if(pNewOne) + { + const_cast<SdrHdlList&>(rHdlList).SetFocusHdl(pNewOne); + } + } + + bReturn = true; + } + } + } + break; + + case KEY_UP: + case KEY_DOWN: + case KEY_LEFT: + case KEY_RIGHT: + { + if (!mpView->IsTextEdit() && !bSlideShow) + { + ::tools::Long nX = 0; + ::tools::Long nY = 0; + + if (nCode == KEY_UP) + { + // scroll up + nX = 0; + nY =-1; + } + else if (nCode == KEY_DOWN) + { + // scroll down + nX = 0; + nY = 1; + } + else if (nCode == KEY_LEFT) + { + // scroll left + nX =-1; + nY = 0; + } + else if (nCode == KEY_RIGHT) + { + // scroll right + nX = 1; + nY = 0; + } + + if (mpView->AreObjectsMarked() && !rKEvt.GetKeyCode().IsMod1() && + !mpDocSh->IsReadOnly()) + { + const SdrHdlList& rHdlList = mpView->GetHdlList(); + SdrHdl* pHdl = rHdlList.GetFocusHdl(); + + bool bIsMoveOfConnectedHandle(false); + bool bOldSuppress = false; + SdrEdgeObj* pEdgeObj = nullptr; + if(pHdl) + pEdgeObj = dynamic_cast<SdrEdgeObj *>( pHdl->GetObj() ); + + if(pEdgeObj && 0 == pHdl->GetPolyNum()) + { + if(0 == pHdl->GetPointNum()) + { + if(pEdgeObj->GetConnection(true).GetObject()) + { + bIsMoveOfConnectedHandle = true; + } + } + if(1 == pHdl->GetPointNum()) + { + if(pEdgeObj->GetConnection(false).GetObject()) + { + bIsMoveOfConnectedHandle = true; + } + } + } + + if(pEdgeObj) + { + // Suppress default connects to inside object and object center + bOldSuppress = pEdgeObj->GetSuppressDefaultConnect(); + pEdgeObj->SetSuppressDefaultConnect(true); + } + + if(bIsMoveOfConnectedHandle) + { + sal_uInt16 nMarkHdSiz(mpView->GetMarkHdlSizePixel()); + Size aHalfConSiz(nMarkHdSiz + 1, nMarkHdSiz + 1); + aHalfConSiz = mpWindow->PixelToLogic(aHalfConSiz); + + if(100 < aHalfConSiz.Width()) + nX *= aHalfConSiz.Width(); + else + nX *= 100; + + if(100 < aHalfConSiz.Height()) + nY *= aHalfConSiz.Height(); + else + nY *= 100; + } + else if(rKEvt.GetKeyCode().IsMod2()) + { + // move in 1 pixel distance + Size aLogicSizeOnePixel = mpWindow->PixelToLogic(Size(1,1)); + nX *= aLogicSizeOnePixel.Width(); + nY *= aLogicSizeOnePixel.Height(); + } + else if(rKEvt.GetKeyCode().IsShift()) + { + nX *= 1000; + nY *= 1000; + } + else + { + // old, fixed move distance + nX *= 100; + nY *= 100; + } + + if(nullptr == pHdl) + { + // only take action when move is allowed + if(mpView->IsMoveAllowed()) + { + // restrict movement to WorkArea + const ::tools::Rectangle& rWorkArea = mpView->GetWorkArea(); + + if(!rWorkArea.IsEmpty()) + { + ::tools::Rectangle aMarkRect(mpView->GetMarkedObjRect()); + aMarkRect.Move(nX, nY); + + if(!aMarkRect.Contains(rWorkArea)) + { + if(aMarkRect.Left() < rWorkArea.Left()) + { + nX += rWorkArea.Left() - aMarkRect.Left(); + } + + if(aMarkRect.Right() > rWorkArea.Right()) + { + nX -= aMarkRect.Right() - rWorkArea.Right(); + } + + if(aMarkRect.Top() < rWorkArea.Top()) + { + nY += rWorkArea.Top() - aMarkRect.Top(); + } + + if(aMarkRect.Bottom() > rWorkArea.Bottom()) + { + nY -= aMarkRect.Bottom() - rWorkArea.Bottom(); + } + } + } + + // no handle selected + if(0 != nX || 0 != nY) + { + mpView->MoveAllMarked(Size(nX, nY)); + + mpView->MakeVisible(mpView->GetAllMarkedRect(), *mpWindow); + } + } + } + else + { + // move handle with index nHandleIndex + if (nX || nY) + { + // now move the Handle (nX, nY) + Point aStartPoint(pHdl->GetPos()); + Point aEndPoint(pHdl->GetPos() + Point(nX, nY)); + const SdrDragStat& rDragStat = mpView->GetDragStat(); + + // start dragging + mpView->BegDragObj(aStartPoint, nullptr, pHdl, 0); + + if(mpView->IsDragObj()) + { + bool bWasNoSnap = rDragStat.IsNoSnap(); + bool bWasSnapEnabled = mpView->IsSnapEnabled(); + + // switch snapping off + if(!bWasNoSnap) + const_cast<SdrDragStat&>(rDragStat).SetNoSnap(); + if(bWasSnapEnabled) + mpView->SetSnapEnabled(false); + + mpView->MovAction(aEndPoint); + mpView->EndDragObj(); + + // restore snap + if(!bWasNoSnap) + const_cast<SdrDragStat&>(rDragStat).SetNoSnap(bWasNoSnap); + if(bWasSnapEnabled) + mpView->SetSnapEnabled(bWasSnapEnabled); + } + + // make moved handle visible + ::tools::Rectangle aVisRect(aEndPoint - Point(100, 100), Size(200, 200)); + mpView->MakeVisible(aVisRect, *mpWindow); + } + } + + if(pEdgeObj) + { + // Restore original suppress value + pEdgeObj->SetSuppressDefaultConnect(bOldSuppress); + } + } + else + { + // scroll page + mpViewShell->ScrollLines(nX, nY); + } + + bReturn = true; + } + } + break; + } + + if (bReturn) + { + mpWindow->ReleaseMouse(); + } + + // when a text-editable object is selected and the + // input character is printable, activate text edit on that object + // and feed character to object + if(!bReturn && !mpDocSh->IsReadOnly()) + { + if (!mpView->IsTextEdit()) + { + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + if(1 == rMarkList.GetMarkCount()) + { + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + + // #i118485# allow TextInput for OLEs, too + if( dynamic_cast< const SdrTextObj *>( pObj ) != nullptr && pObj->HasTextEdit()) + { + // use common IsSimpleCharInput from the EditEngine. + bool bPrintable(EditEngine::IsSimpleCharInput(rKEvt)); + + if(bPrintable) + { + // try to activate textedit mode for the selected object + SfxStringItem aInputString(SID_ATTR_CHAR, OUString(rKEvt.GetCharCode())); + + mpViewShell->GetViewFrame()->GetDispatcher()->ExecuteList( + SID_ATTR_CHAR, + SfxCallMode::ASYNCHRON, + { &aInputString }); + + // consumed + bReturn = true; + } + } + } + else + { + // test if there is a title object there. If yes, try to + // set it to edit mode and start typing... + DrawViewShell* pDrawViewShell = dynamic_cast<DrawViewShell*>(mpViewShell); + if (pDrawViewShell && EditEngine::IsSimpleCharInput(rKEvt)) + { + SdPage* pActualPage = pDrawViewShell->GetActualPage(); + SdrTextObj* pCandidate = nullptr; + + if(pActualPage) + { + SdrObjListIter aIter(pActualPage, SdrIterMode::DeepNoGroups); + + while(aIter.IsMore() && !pCandidate) + { + SdrObject* pObj = aIter.Next(); + + if(auto pTextObj = dynamic_cast< SdrTextObj *>( pObj )) + { + SdrInventor nInv(pObj->GetObjInventor()); + SdrObjKind nKnd(pObj->GetObjIdentifier()); + + if(SdrInventor::Default == nInv && SdrObjKind::TitleText == nKnd) + { + pCandidate = pTextObj; + } + } + } + } + + // when candidate found and candidate is untouched, start editing text... + if(pCandidate && pCandidate->IsEmptyPresObj()) + { + mpView->UnMarkAll(); + mpView->MarkObj(pCandidate, mpView->GetSdrPageView()); + SfxStringItem aInputString(SID_ATTR_CHAR, OUString(rKEvt.GetCharCode())); + + mpViewShell->GetViewFrame()->GetDispatcher()->ExecuteList( + SID_ATTR_CHAR, + SfxCallMode::ASYNCHRON, + { &aInputString }); + + // consumed + bReturn = true; + } + } + } + } + } + + return bReturn; +} + +bool FuPoor::MouseMove(const MouseEvent& ) +{ + return false; +} + +void FuPoor::SelectionHasChanged() +{ + const SdrHdlList& rHdlList = mpView->GetHdlList(); + const_cast<SdrHdlList&>(rHdlList).ResetFocusHdl(); +} + +/** + * Cut object to clipboard + */ +void FuPoor::DoCut() +{ + if (mpView) + { + mpView->DoCut(); + } +} + +/** + * Copy object to clipboard + */ +void FuPoor::DoCopy() +{ + if (mpView) + { + mpView->DoCopy(); + } +} + +/** + * Paste object from clipboard + */ +void FuPoor::DoPaste() +{ + if (mpView) + { + mpView->DoPaste(mpWindow); + } +} + +/** + * Paste unformatted text from clipboard + */ +void FuPoor::DoPasteUnformatted() +{ + if (mpView) + { + TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( mpViewShell->GetActiveWindow() ) ); + if (aDataHelper.GetTransferable().is()) + { + sal_Int8 nAction = DND_ACTION_COPY; + mpView->InsertData( aDataHelper, + mpWindow->PixelToLogic( ::tools::Rectangle( Point(), mpWindow->GetOutputSizePixel() ).Center() ), + nAction, false, SotClipboardFormatId::STRING); + } + } +} + +/** + * Timer handler for Drag&Drop + */ +IMPL_LINK_NOARG(FuPoor, DragHdl, Timer *, void) +{ + if( !mpView ) + return; + + sal_uInt16 nHitLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(HITPIX,0)).Width() ); + SdrHdl* pHdl = mpView->PickHandle(aMDPos); + + if ( pHdl==nullptr && mpView->IsMarkedHit(aMDPos, nHitLog) + && !mpView->IsPresObjSelected(false) ) + { + mpWindow->ReleaseMouse(); + bIsInDragMode = true; + mpView->StartDrag( aMDPos, mpWindow ); + } +} + +bool FuPoor::Command(const CommandEvent& rCEvt) +{ + return mpView->Command(rCEvt,mpWindow); +} + +/** + * Timer handler for window scrolling + */ +IMPL_LINK_NOARG(FuPoor, DelayHdl, Timer *, void) +{ + aDelayToScrollTimer.Stop (); + bScrollable = true; + + Point aPnt(mpWindow->GetPointerPosPixel()); + + // use remembered MouseButton state to create correct + // MouseEvents for this artificial MouseMove. + MouseMove(MouseEvent(aPnt, 1, MouseEventModifiers::NONE, GetMouseButtonCode())); +} + +bool FuPoor::MouseButtonUp (const MouseEvent& rMEvt) +{ + // remember button state for creation of own MouseEvents + SetMouseButtonCode(rMEvt.GetButtons()); + + aDelayToScrollTimer.Stop (); + bScrollable = bDelayActive = false; + return bScrollable; +} + +bool FuPoor::MouseButtonDown(const MouseEvent& rMEvt) +{ + // remember button state for creation of own MouseEvents + SetMouseButtonCode(rMEvt.GetButtons()); + + return false; +} + +void FuPoor::StartDelayToScrollTimer () +{ + bDelayActive = true; + aDelayToScrollTimer.Start (); +} + +bool FuPoor::RequestHelp(const HelpEvent& rHEvt) +{ + bool bReturn = false; + + SdrPageView* pPV = mpView->GetSdrPageView(); + + if (pPV) + { + SdPage* pPage = static_cast<SdPage*>( pPV->GetPage() ); + + if (pPage) + { + bReturn = FmFormPage::RequestHelp(mpWindow, mpView, rHEvt); + } + } + + return bReturn; +} + +void FuPoor::ReceiveRequest(SfxRequest& /*rReq*/) +{ +} + +SdrObjectUniquePtr FuPoor::CreateDefaultObject(const sal_uInt16, const ::tools::Rectangle& ) +{ + // empty base implementation + return nullptr; +} + +void FuPoor::ImpForceQuadratic(::tools::Rectangle& rRect) +{ + if(rRect.GetWidth() > rRect.GetHeight()) + { + rRect = ::tools::Rectangle( + Point(rRect.Left() + ((rRect.GetWidth() - rRect.GetHeight()) / 2), rRect.Top()), + Size(rRect.GetHeight(), rRect.GetHeight())); + } + else + { + rRect = ::tools::Rectangle( + Point(rRect.Left(), rRect.Top() + ((rRect.GetHeight() - rRect.GetWidth()) / 2)), + Size(rRect.GetWidth(), rRect.GetWidth())); + } +} + +void FuPoor::SwitchLayer (sal_Int32 nOffset) +{ + auto pDrawViewShell = dynamic_cast<DrawViewShell *>( mpViewShell ); + if(!pDrawViewShell) + return; + + // Calculate the new index. + sal_Int32 nIndex = pDrawViewShell->GetActiveTabLayerIndex() + nOffset; + + // Make sure the new index lies inside the range of valid indices. + if (nIndex < 0) + nIndex = 0; + else if (nIndex >= pDrawViewShell->GetTabLayerCount ()) + nIndex = pDrawViewShell->GetTabLayerCount() - 1; + + // Set the new active layer. + if (nIndex != pDrawViewShell->GetActiveTabLayerIndex ()) + { + LayerTabBar* pLayerTabControl = + static_cast<DrawViewShell*>(mpViewShell)->GetLayerTabControl(); + if (pLayerTabControl != nullptr) + pLayerTabControl->SendDeactivatePageEvent (); + + pDrawViewShell->SetActiveTabLayerIndex (nIndex); + + if (pLayerTabControl != nullptr) + pLayerTabControl->SendActivatePageEvent (); + } +} + +/** is called when the current function should be aborted. <p> + This is used when a function gets a KEY_ESCAPE but can also + be called directly. + + @returns true if an active function was aborted +*/ +bool FuPoor::cancel() +{ + if ( dynamic_cast< const FuSelection *>( this ) == nullptr ) + { + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + return true; + } + + return false; +} + +// #i33136# +bool FuPoor::doConstructOrthogonal() const +{ + // Check whether a media object is selected + bool bResizeKeepRatio = false; + // tdf#89758 Avoid interactive crop preview from being proportionally scaled by default. + if (mpView->AreObjectsMarked() && mpView->GetDragMode() != SdrDragMode::Crop) + { + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + if (rMarkList.GetMarkCount() == 1) + { + SdrObjKind aObjIdentifier = rMarkList.GetMark(0)->GetMarkedSdrObj()->GetObjIdentifier(); + bResizeKeepRatio = aObjIdentifier == SdrObjKind::Graphic || + aObjIdentifier == SdrObjKind::Media || + aObjIdentifier == SdrObjKind::OLE2; + } + } + SdrHdl* pHdl = mpView->PickHandle(aMDPos); + // Resize proportionally when media is selected and the user drags on a corner + if (pHdl) + bResizeKeepRatio = bResizeKeepRatio && pHdl->IsCornerHdl(); + + return ( + bResizeKeepRatio || + SID_DRAW_XLINE == nSlotId || + SID_DRAW_CIRCLEARC == nSlotId || + SID_DRAW_SQUARE == nSlotId || + SID_DRAW_SQUARE_NOFILL == nSlotId || + SID_DRAW_SQUARE_ROUND == nSlotId || + SID_DRAW_SQUARE_ROUND_NOFILL == nSlotId || + SID_DRAW_CIRCLE == nSlotId || + SID_DRAW_CIRCLE_NOFILL == nSlotId || + SID_DRAW_CIRCLEPIE == nSlotId || + SID_DRAW_CIRCLEPIE_NOFILL == nSlotId || + SID_DRAW_CIRCLECUT == nSlotId || + SID_DRAW_CIRCLECUT_NOFILL == nSlotId || + SID_DRAW_XPOLYGON == nSlotId || + SID_DRAW_XPOLYGON_NOFILL == nSlotId || + SID_3D_CUBE == nSlotId || + SID_3D_SPHERE == nSlotId || + SID_3D_SHELL == nSlotId || + SID_3D_HALF_SPHERE == nSlotId || + SID_3D_TORUS == nSlotId || + SID_3D_CYLINDER == nSlotId || + SID_3D_CONE == nSlotId || + SID_3D_PYRAMID == nSlotId); +} + +void FuPoor::DoExecute( SfxRequest& ) +{ +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuprlout.cxx b/sd/source/ui/func/fuprlout.cxx new file mode 100644 index 000000000..c436b78f0 --- /dev/null +++ b/sd/source/ui/func/fuprlout.cxx @@ -0,0 +1,277 @@ +/* -*- 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 <fuprlout.hxx> +#include <sfx2/dispatch.hxx> + +#include <sfx2/viewfrm.hxx> +#include <sfx2/request.hxx> +#include <svl/stritem.hxx> + +#include <sdattr.hrc> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <pres.hxx> +#include <DrawViewShell.hxx> +#include <View.hxx> +#include <glob.hxx> +#include <app.hrc> +#include <DrawDocShell.hxx> +#include <SlideSorterViewShell.hxx> +#include <Window.hxx> +#include <drawview.hxx> +#include <sdabstdlg.hxx> +#include <memory> + +namespace sd +{ + + +#define DOCUMENT_TOKEN '#' + +FuPresentationLayout::FuPresentationLayout ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuPresentationLayout::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuPresentationLayout( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuPresentationLayout::DoExecute( SfxRequest& rReq ) +{ + // prevent selected objects or objects which are under editing from disappearing + mpView->SdrEndTextEdit(); + + if(mpView->GetSdrPageView()) + { + mpView->UnmarkAll(); + } + + bool bError = false; + + /* if we are on a master page, the changes apply for all pages and notes- + pages who are using the relevant layout */ + bool bOnMaster = false; + if (DrawViewShell *pShell = dynamic_cast<DrawViewShell*>(mpViewShell)) + { + EditMode eEditMode = pShell->GetEditMode(); + if (eEditMode == EditMode::MasterPage) + bOnMaster = true; + } + + std::vector<SdPage*> aUnselect; + if (!bOnMaster) + { + //We later rely on IsSelected, so transfer the selection here + //into the document + slidesorter::SlideSorterViewShell* pSlideSorterViewShell + = slidesorter::SlideSorterViewShell::GetSlideSorter(mpViewShell->GetViewShellBase()); + if (pSlideSorterViewShell) + { + std::shared_ptr<slidesorter::SlideSorterViewShell::PageSelection> xSelection( + pSlideSorterViewShell->GetPageSelection()); + if (xSelection) + { + for (SdPage *pPage : *xSelection) + { + if (pPage->IsSelected() || pPage->GetPageKind() != PageKind::Standard) + continue; + mpDoc->SetSelected(pPage, true); + aUnselect.push_back(pPage); + } + } + } + } + + std::vector<SdPage*> aSelectedPages; + std::vector<sal_uInt16> aSelectedPageNums; + // determine the active pages + for (sal_uInt16 nPage = 0; nPage < mpDoc->GetSdPageCount(PageKind::Standard); nPage++) + { + SdPage* pPage = mpDoc->GetSdPage(nPage, PageKind::Standard); + if (pPage->IsSelected()) + { + aSelectedPages.push_back(pPage); + aSelectedPageNums.push_back(nPage); + } + } + + bool bMasterPage = bOnMaster; + bool bCheckMasters = false; + + // call dialog + bool bLoad = false; // appear the new master pages? + OUString aFile; + + SfxItemSetFixed<ATTR_PRESLAYOUT_START, ATTR_PRESLAYOUT_END> aSet(mpDoc->GetPool() ); + + aSet.Put( SfxBoolItem( ATTR_PRESLAYOUT_LOAD, bLoad)); + aSet.Put( SfxBoolItem( ATTR_PRESLAYOUT_MASTER_PAGE, bMasterPage ) ); + aSet.Put( SfxBoolItem( ATTR_PRESLAYOUT_CHECK_MASTERS, bCheckMasters ) ); + + if (!aSelectedPages.empty()) + { + OUString aOldLayoutName(aSelectedPages.back()->GetLayoutName()); + sal_Int32 nPos = aOldLayoutName.indexOf(SD_LT_SEPARATOR); + if (nPos != -1) + aOldLayoutName = aOldLayoutName.copy(0, nPos); + aSet.Put(SfxStringItem(ATTR_PRESLAYOUT_NAME, aOldLayoutName)); + } + + const SfxItemSet *pArgs = rReq.GetArgs (); + + if (pArgs) + { + if (pArgs->GetItemState(ATTR_PRESLAYOUT_LOAD) == SfxItemState::SET) + bLoad = static_cast<const SfxBoolItem&>(pArgs->Get(ATTR_PRESLAYOUT_LOAD)).GetValue(); + if( pArgs->GetItemState( ATTR_PRESLAYOUT_MASTER_PAGE ) == SfxItemState::SET ) + bMasterPage = pArgs->Get( ATTR_PRESLAYOUT_MASTER_PAGE ).GetValue(); + if( pArgs->GetItemState( ATTR_PRESLAYOUT_CHECK_MASTERS ) == SfxItemState::SET ) + bCheckMasters = static_cast<const SfxBoolItem&>( pArgs->Get( ATTR_PRESLAYOUT_CHECK_MASTERS ) ).GetValue(); + if (pArgs->GetItemState(ATTR_PRESLAYOUT_NAME) == SfxItemState::SET) + aFile = pArgs->Get(ATTR_PRESLAYOUT_NAME).GetValue(); + } + else + { + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSdPresLayoutDlg> pDlg(pFact->CreateSdPresLayoutDlg(mpWindow ? mpWindow->GetFrameWeld() : nullptr, mpDocSh, aSet)); + + sal_uInt16 nResult = pDlg->Execute(); + + switch (nResult) + { + case RET_OK: + { + pDlg->GetAttr(aSet); + if (aSet.GetItemState(ATTR_PRESLAYOUT_LOAD) == SfxItemState::SET) + bLoad = static_cast<const SfxBoolItem&>(aSet.Get(ATTR_PRESLAYOUT_LOAD)).GetValue(); + if( aSet.GetItemState( ATTR_PRESLAYOUT_MASTER_PAGE ) == SfxItemState::SET ) + bMasterPage = aSet.Get( ATTR_PRESLAYOUT_MASTER_PAGE ).GetValue(); + if( aSet.GetItemState( ATTR_PRESLAYOUT_CHECK_MASTERS ) == SfxItemState::SET ) + bCheckMasters = static_cast<const SfxBoolItem&>(aSet.Get( ATTR_PRESLAYOUT_CHECK_MASTERS ) ).GetValue(); + if (aSet.GetItemState(ATTR_PRESLAYOUT_NAME) == SfxItemState::SET) + aFile = aSet.Get(ATTR_PRESLAYOUT_NAME).GetValue(); + } + break; + + default: + bError = true; + } + } + + if (bError) + return; + + mpDocSh->SetWaitCursor( true ); + + /* Here, we only exchange masterpages, therefore the current page + remains the current page. To prevent calling PageOrderChangedHint + during insertion and extraction of the masterpages, we block. */ + /* That isn't quite right. If the masterpageview is active and you are + removing a masterpage, it's possible that you are removing the + current masterpage. So you have to call ResetActualPage ! */ + if( dynamic_cast< const DrawViewShell *>( mpViewShell ) != nullptr && !bCheckMasters ) + static_cast<DrawView*>(mpView)->BlockPageOrderChangedHint(true); + + if (bLoad) + { + sal_Int32 nIdx{ 0 }; + OUString aFileName = aFile.getToken(0, DOCUMENT_TOKEN, nIdx); + SdDrawDocument* pTempDoc = mpDoc->OpenBookmarkDoc( aFileName ); + + // #69581: If I chose the standard-template I got no filename and so I get no + // SdDrawDocument-Pointer. But the method SetMasterPage is able to handle + // a NULL-pointer as a Standard-template ( look at SdDrawDocument::SetMasterPage ) + OUString aLayoutName; + if( pTempDoc ) + aLayoutName = aFile.getToken(0, DOCUMENT_TOKEN, nIdx); + for (auto nSelectedPage : aSelectedPageNums) + mpDoc->SetMasterPage(nSelectedPage, aLayoutName, pTempDoc, bMasterPage, bCheckMasters); + mpDoc->CloseBookmarkDoc(); + } + else + { + // use master page with the layout name aFile from current Doc + for (auto nSelectedPage : aSelectedPageNums) + mpDoc->SetMasterPage(nSelectedPage, aFile, mpDoc, bMasterPage, bCheckMasters); + } + + // remove blocking + if( dynamic_cast< const DrawViewShell *>( mpViewShell ) != nullptr && !bCheckMasters ) + static_cast<DrawView*>(mpView)->BlockPageOrderChangedHint(false); + + // if the master page was visible, show it again + if (!aSelectedPages.empty()) + { + if (bOnMaster) + { + if( auto pDrawViewShell = dynamic_cast<DrawViewShell *>( mpViewShell )) + { + ::sd::View* pView = pDrawViewShell->GetView(); + for (auto pSelectedPage : aSelectedPages) + { + sal_uInt16 nPgNum = pSelectedPage->TRG_GetMasterPage().GetPageNum(); + + if (static_cast<DrawViewShell*>(mpViewShell)->GetPageKind() == PageKind::Notes) + nPgNum++; + + pView->HideSdrPage(); + pView->ShowSdrPage(pView->GetModel()->GetMasterPage(nPgNum)); + } + } + + // force update of TabBar + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_MASTERPAGE, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + } + else + { + for (auto pSelectedPage : aSelectedPages) + pSelectedPage->SetAutoLayout(pSelectedPage->GetAutoLayout()); + } + } + + //Undo transfer to document selection + for (auto pPage : aUnselect) + mpDoc->SetSelected(pPage, false); + + + // fake a mode change to repaint the page tab bar + if( auto pDrawViewSh = dynamic_cast<DrawViewShell *>( mpViewShell ) ) + { + EditMode eMode = pDrawViewSh->GetEditMode(); + bool bLayer = pDrawViewSh->IsLayerModeActive(); + pDrawViewSh->ChangeEditMode( eMode, !bLayer ); + pDrawViewSh->ChangeEditMode( eMode, bLayer ); + } + + mpDocSh->SetWaitCursor( false ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuprobjs.cxx b/sd/source/ui/func/fuprobjs.cxx new file mode 100644 index 000000000..6042d1fbc --- /dev/null +++ b/sd/source/ui/func/fuprobjs.cxx @@ -0,0 +1,154 @@ +/* -*- 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 <fuprobjs.hxx> + +#include <svl/stritem.hxx> +#include <svl/style.hxx> +#include <editeng/outliner.hxx> +#include <svl/hint.hxx> +#include <tools/debug.hxx> + +#include <app.hrc> + +#include <strings.hxx> + +#include <drawdoc.hxx> +#include <sfx2/sfxdlg.hxx> +#include <DrawDocShell.hxx> +#include <OutlineView.hxx> +#include <OutlineViewShell.hxx> +#include <ViewShell.hxx> +#include <Window.hxx> +#include <glob.hxx> +#include <prlayout.hxx> +#include <unchss.hxx> +#include <sdabstdlg.hxx> +#include <memory> + +namespace sd { + + +FuPresentationObjects::FuPresentationObjects ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuPresentationObjects::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuPresentationObjects( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuPresentationObjects::DoExecute( SfxRequest& ) +{ + OutlineViewShell* pOutlineViewShell = dynamic_cast< OutlineViewShell* >( mpViewShell ); + DBG_ASSERT( pOutlineViewShell, "sd::FuPresentationObjects::DoExecute(), does not work without an OutlineViewShell!"); + if( !pOutlineViewShell ) + return; + + /* does the selections end in a unique presentation layout? + if not, it is not allowed to edit the templates */ + SfxItemSetFixed<SID_STATUS_LAYOUT, SID_STATUS_LAYOUT> aSet(mpDoc->GetItemPool() ); + pOutlineViewShell->GetStatusBarState( aSet ); + OUString aLayoutName = static_cast<const SfxStringItem&>(aSet.Get(SID_STATUS_LAYOUT)).GetValue(); + DBG_ASSERT(!aLayoutName.isEmpty(), "Layout not defined"); + + bool bUnique = false; + sal_Int16 nDepth, nTmp; + OutlineView* pOlView = static_cast<OutlineView*>(pOutlineViewShell->GetView()); + OutlinerView* pOutlinerView = pOlView->GetViewByWindow( static_cast<Window*>(mpWindow) ); + ::Outliner* pOutl = pOutlinerView->GetOutliner(); + + std::vector<Paragraph*> aSelList; + pOutlinerView->CreateSelectionList(aSelList); + + Paragraph* pPara = aSelList.empty() ? nullptr : aSelList.front(); + + nDepth = pOutl->GetDepth(pOutl->GetAbsPos( pPara ) ); + bool bPage = ::Outliner::HasParaFlag( pPara, ParaFlag::ISPAGE ); + + for( const auto& rpPara : aSelList ) + { + nTmp = pOutl->GetDepth( pOutl->GetAbsPos( rpPara ) ); + + if( nDepth != nTmp ) + { + bUnique = false; + break; + } + + if( ::Outliner::HasParaFlag( rpPara, ParaFlag::ISPAGE ) != bPage ) + { + bUnique = false; + break; + } + bUnique = true; + } + + if( !bUnique ) + return; + + OUString aStyleName = aLayoutName + SD_LT_SEPARATOR; + PresentationObjects ePO; + + if( bPage ) + { + ePO = PresentationObjects::Title; + aStyleName += STR_LAYOUT_TITLE; + } + else + { + ePO = static_cast<PresentationObjects>( static_cast<int>(PresentationObjects::Outline_1) + nDepth - 1 ); + aStyleName += STR_LAYOUT_OUTLINE + " " + OUString::number(nDepth); + } + + SfxStyleSheetBasePool* pStyleSheetPool = mpDocSh->GetStyleSheetPool(); + SfxStyleSheetBase* pStyleSheet = pStyleSheetPool->Find( aStyleName, SfxStyleFamily::Page ); + DBG_ASSERT(pStyleSheet, "StyleSheet missing"); + + if( !pStyleSheet ) + return; + + SfxStyleSheetBase& rStyleSheet = *pStyleSheet; + + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateSdPresLayoutTemplateDlg(mpDocSh, mpViewShell->GetFrameWeld(), + false, rStyleSheet, ePO, pStyleSheetPool)); + if( pDlg->Execute() == RET_OK ) + { + const SfxItemSet* pOutSet = pDlg->GetOutputItemSet(); + // Undo-Action + mpDocSh->GetUndoManager()->AddUndoAction( + std::make_unique<StyleSheetUndoAction>(mpDoc, static_cast<SfxStyleSheet*>(pStyleSheet), pOutSet)); + + pStyleSheet->GetItemSet().Put( *pOutSet ); + static_cast<SfxStyleSheet*>( pStyleSheet )->Broadcast( SfxHint( SfxHintId::DataChanged ) ); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuscale.cxx b/sd/source/ui/func/fuscale.cxx new file mode 100644 index 000000000..d4730b243 --- /dev/null +++ b/sd/source/ui/func/fuscale.cxx @@ -0,0 +1,179 @@ +/* -*- 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 <fuscale.hxx> + +#include <svx/svxids.hrc> + +#include <app.hrc> +#include <View.hxx> +#include <Window.hxx> +#include <OutlineViewShell.hxx> +#include <drawdoc.hxx> +#include <DrawViewShell.hxx> +#include <ViewShell.hxx> +#include <fuzoom.hxx> + +#include <svx/svdpagv.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/zoomitem.hxx> +#include <sfx2/request.hxx> +#include <svx/svxdlg.hxx> +#include <memory> + +namespace sd { + + +FuScale::FuScale ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuScale::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuScale( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuScale::DoExecute( SfxRequest& rReq ) +{ + sal_Int16 nValue; + + const SfxItemSet* pArgs = rReq.GetArgs(); + + if( !pArgs ) + { + SfxItemSetFixed<SID_ATTR_ZOOM, SID_ATTR_ZOOM> aNewAttr( mpDoc->GetPool() ); + std::unique_ptr<SvxZoomItem> pZoomItem; + SvxZoomEnableFlags nZoomValues = SvxZoomEnableFlags::ALL; + + nValue = static_cast<sal_Int16>(mpWindow->GetZoom()); + + // zoom on page size? + if( dynamic_cast< DrawViewShell *>( mpViewShell ) && + static_cast<DrawViewShell*>(mpViewShell)->IsZoomOnPage() ) + { + pZoomItem.reset(new SvxZoomItem( SvxZoomType::WHOLEPAGE, nValue )); + } + else + { + pZoomItem.reset(new SvxZoomItem( SvxZoomType::PERCENT, nValue )); + } + + // limit range + if( mpViewShell ) + { + if( dynamic_cast< DrawViewShell *>( mpViewShell ) != nullptr ) + { + SdrPageView* pPageView = mpView->GetSdrPageView(); + if( pPageView && pPageView->GetObjList()->GetObjCount() == 0 ) + { + nZoomValues &= ~SvxZoomEnableFlags::OPTIMAL; + } + } + else if( dynamic_cast< OutlineViewShell *>( mpViewShell ) != nullptr ) + { + nZoomValues &= ~SvxZoomEnableFlags::OPTIMAL; + nZoomValues &= ~SvxZoomEnableFlags::WHOLEPAGE; + nZoomValues &= ~SvxZoomEnableFlags::PAGEWIDTH; + } + } + + pZoomItem->SetValueSet( nZoomValues ); + aNewAttr.Put( std::move(pZoomItem) ); + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSvxZoomDialog> pDlg(pFact->CreateSvxZoomDialog(rReq.GetFrameWeld(), aNewAttr)); + pDlg->SetLimits( static_cast<sal_uInt16>(mpWindow->GetMinZoom()), static_cast<sal_uInt16>(mpWindow->GetMaxZoom()) ); + sal_uInt16 nResult = pDlg->Execute(); + switch( nResult ) + { + case RET_CANCEL: + { + rReq.Ignore (); + return; // Cancel + } + default: + { + rReq.Ignore (); + } + break; + } + + const SfxItemSet aArgs (*(pDlg->GetOutputItemSet ())); + + pDlg.disposeAndClear(); + + if (!mpViewShell) + return; + + switch ( aArgs.Get (SID_ATTR_ZOOM).GetType ()) + { + case SvxZoomType::PERCENT: + { + nValue = aArgs.Get (SID_ATTR_ZOOM).GetValue (); + + mpViewShell->SetZoom( nValue ); + + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SidArrayZoom ); + } + break; + + case SvxZoomType::OPTIMAL: + { + if( dynamic_cast< DrawViewShell *>( mpViewShell ) != nullptr ) + { + // name confusion: SID_SIZE_ALL -> zoom onto all objects + // --> the program offers it as optimal + mpViewShell->GetViewFrame()->GetDispatcher()->Execute( SID_SIZE_ALL, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + } + } + break; + + case SvxZoomType::PAGEWIDTH: + mpViewShell->GetViewFrame()->GetDispatcher()->Execute( SID_SIZE_PAGE_WIDTH, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + break; + + case SvxZoomType::WHOLEPAGE: + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_SIZE_PAGE, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + break; + default: + break; + } + } + else if(mpViewShell && (pArgs->Count () == 1)) + { + const SfxUInt32Item* pScale = rReq.GetArg<SfxUInt32Item>(ID_VAL_ZOOM); + mpViewShell->SetZoom (pScale->GetValue ()); + + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SidArrayZoom ); + } + +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fusearch.cxx b/sd/source/ui/func/fusearch.cxx new file mode 100644 index 000000000..73a112bf4 --- /dev/null +++ b/sd/source/ui/func/fusearch.cxx @@ -0,0 +1,140 @@ +/* -*- 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 <fusearch.hxx> + +#include <sfx2/viewfrm.hxx> + +#include <sfx2/bindings.hxx> +#include <fupoor.hxx> +#include <drawdoc.hxx> +#include <app.hrc> +#include <Outliner.hxx> +#include <DrawDocShell.hxx> +#include <DrawViewShell.hxx> +#include <OutlineViewShell.hxx> +#include <ViewShellBase.hxx> + +class SfxRequest; + +namespace sd { + +const sal_uInt16 SidArraySpell[] = { + SID_DRAWINGMODE, + SID_OUTLINE_MODE, + SID_SLIDE_SORTER_MODE, + SID_NOTES_MODE, + SID_HANDOUT_MASTER_MODE, + SID_SLIDE_MASTER_MODE, + SID_NOTES_MASTER_MODE, + 0 }; + +FuSearch::FuSearch ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq ) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq), + m_pSdOutliner(nullptr), + m_bOwnOutliner(false) +{ +} + +FuSearch* FuSearch::createPtr(ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq) +{ + FuSearch* xFunc( new FuSearch( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuSearch::DoExecute( SfxRequest& ) +{ + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SidArraySpell ); + + if ( dynamic_cast< const DrawViewShell *>( mpViewShell ) != nullptr ) + { + m_bOwnOutliner = true; + m_pSdOutliner = new SdOutliner( mpDoc, OutlinerMode::TextObject ); + } + else if ( dynamic_cast< const OutlineViewShell *>( mpViewShell ) != nullptr ) + { + m_bOwnOutliner = false; + m_pSdOutliner = mpDoc->GetOutliner(); + } + + if (m_pSdOutliner) + m_pSdOutliner->PrepareSpelling(); +} + +FuSearch::~FuSearch() +{ + if ( ! mpDocSh->IsInDestruction() && mpDocSh->GetViewShell()!=nullptr) + mpDocSh->GetViewShell()->GetViewFrame()->GetBindings().Invalidate( SidArraySpell ); + + if (m_pSdOutliner) + m_pSdOutliner->EndSpelling(); + + if (m_bOwnOutliner) + delete m_pSdOutliner; +} + +void FuSearch::SearchAndReplace( const SvxSearchItem* pSearchItem ) +{ + ViewShellBase* pBase = dynamic_cast<ViewShellBase*>( SfxViewShell::Current() ); + ViewShell* pViewShell = nullptr; + if (pBase != nullptr) + pViewShell = pBase->GetMainViewShell().get(); + + if (pViewShell == nullptr) + return; + + if (m_pSdOutliner && dynamic_cast<const DrawViewShell*>(pViewShell) && !m_bOwnOutliner) + { + m_pSdOutliner->EndSpelling(); + + m_bOwnOutliner = true; + m_pSdOutliner = new SdOutliner(mpDoc, OutlinerMode::TextObject); + m_pSdOutliner->PrepareSpelling(); + } + else if (m_pSdOutliner && dynamic_cast<const OutlineViewShell*>(pViewShell) && m_bOwnOutliner) + { + m_pSdOutliner->EndSpelling(); + delete m_pSdOutliner; + + m_bOwnOutliner = false; + m_pSdOutliner = mpDoc->GetOutliner(); + m_pSdOutliner->PrepareSpelling(); + } + + if (m_pSdOutliner) + { + bool bEndSpelling = m_pSdOutliner->StartSearchAndReplace(pSearchItem); + + if (bEndSpelling) + { + m_pSdOutliner->EndSpelling(); + m_pSdOutliner->PrepareSpelling(); + } + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fusel.cxx b/sd/source/ui/func/fusel.cxx new file mode 100644 index 000000000..a525f3bfc --- /dev/null +++ b/sd/source/ui/func/fusel.cxx @@ -0,0 +1,1328 @@ +/* -*- 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 <fusel.hxx> +#include <svx/svddrgmt.hxx> +#include <svx/svdpagv.hxx> +#include <svx/svdogrp.hxx> +#include <svx/scene3d.hxx> +#include <vcl/imapobj.hxx> +#include <unotools/securityoptions.hxx> +#include <svx/svxids.hrc> +#include <svx/xfillit0.hxx> +#include <svx/ImageMapInfo.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/stritem.hxx> +#include <svl/intitem.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/docfile.hxx> +#include <editeng/flditem.hxx> + +#include <svx/svdotable.hxx> + +#include <app.hrc> + +#include <sdmod.hxx> +#include <DrawDocShell.hxx> +#include <stlpool.hxx> +#include <fudraw.hxx> +#include <ViewShell.hxx> +#include <ViewShellBase.hxx> +#include <FrameView.hxx> +#include <View.hxx> +#include <Window.hxx> +#include <drawdoc.hxx> +#include <DrawViewShell.hxx> +#include <ToolBarManager.hxx> +#include <Client.hxx> + +#include <svx/svdundo.hxx> + +#include <svx/sdrhittesthelper.hxx> +#include <svx/diagram/IDiagramHelper.hxx> + +#include <LibreOfficeKit/LibreOfficeKitEnums.h> +#include <comphelper/lok.hxx> + +using namespace ::com::sun::star; + +namespace sd { + +FuSelection::FuSelection ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuDraw(pViewSh, pWin, pView, pDoc, rReq), + bTempRotation(false), + bSelectionChanged(false), + pHdl(nullptr), + bSuppressChangesOfSelection(false), + bMirrorSide0(false), + nEditMode(SID_BEZIER_MOVE), + pWaterCanCandidate(nullptr) + //Add Shift+UP/DOWN/LEFT/RIGHT key to move the position of insert point, + //and SHIFT+ENTER key to decide the position and draw the new insert point + ,bBeginInsertPoint(false), + oldPoint(0,0) + ,bMovedToCenterPoint(false) +{ +} + +rtl::Reference<FuPoor> FuSelection::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuSelection( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuSelection::DoExecute( SfxRequest& rReq ) +{ + FuDraw::DoExecute( rReq ); + + // Select object bar + SelectionHasChanged(); +} + +FuSelection::~FuSelection() +{ + mpView->UnmarkAllPoints(); + mpView->ResetCreationActive(); + + if ( mpView->GetDragMode() != SdrDragMode::Move ) + { + mpView->SetDragMode(SdrDragMode::Move); + } +} + +namespace { + bool lcl_followHyperlinkAllowed(const MouseEvent& rMEvt) { + if (!rMEvt.IsMod1() && SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::CtrlClickHyperlink)) + return false; + if (rMEvt.IsMod1() && !SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::CtrlClickHyperlink)) + return false; + return true; + } +} + +bool FuSelection::MouseButtonDown(const MouseEvent& rMEvt) +{ + pHdl = nullptr; + bool bReturn = FuDraw::MouseButtonDown(rMEvt); + bool bWaterCan = SD_MOD()->GetWaterCan(); + const bool bReadOnly = mpDocSh->IsReadOnly(); + // When the right mouse button is pressed then only select objects + // (and deselect others) as a preparation for showing the context + // menu. + const bool bSelectionOnly = rMEvt.IsRight(); + + bMBDown = true; + bSelectionChanged = false; + + if ( mpView->IsAction() ) + { + if ( rMEvt.IsRight() ) + mpView->BckAction(); + return true; + } + + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + sal_uInt16 nHitLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(HITPIX,0)).Width() ); + + if (comphelper::LibreOfficeKit::isActive()) + { + // When tiled rendering, we always work in logic units, use the non-pixel constants. + nDrgLog = DRGLOG; + nHitLog = HITLOG; + } + + // The following code is executed for right clicks as well as for left + // clicks in order to modify the selection for the right button as a + // preparation for the context menu. The functions BegMarkObject() and + // BegDragObject(), however, are not called for right clicks because a) + // it makes no sense and b) to have IsAction() return sal_False when called + // from Command() which is a prerequisite for the context menu. + if ((rMEvt.IsLeft() || rMEvt.IsRight()) + && !mpView->IsAction() + && (mpView->IsFrameDragSingles() || !mpView->HasMarkablePoints())) + { + /****************************************************************** + * NO BEZIER_EDITOR + ******************************************************************/ + mpWindow->CaptureMouse(); + pHdl = mpView->PickHandle(aMDPos); + + Degree100 nAngle0 = GetAngle(aMDPos - mpView->GetRef1()); + nAngle0 -= 27000_deg100; + nAngle0 = NormAngle36000(nAngle0); + bMirrorSide0 = nAngle0 < 18000_deg100; + + if (!pHdl && mpView->Is3DRotationCreationActive()) + { + /****************************************************************** + * If 3D-rotation bodies are about to be created, + * end creation now. + ******************************************************************/ + bSuppressChangesOfSelection = true; + mpWindow->EnterWait(); + mpView->End3DCreation(); + bSuppressChangesOfSelection = false; + mpView->ResetCreationActive(); + mpWindow->LeaveWait(); + } + + bool bTextEdit = false; + SdrViewEvent aVEvt; + SdrHitKind eHit = mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + + if (eHit == SdrHitKind::TextEditObj && (mpViewShell->GetFrameView()->IsQuickEdit() || dynamic_cast< sdr::table::SdrTableObj* >(aVEvt.mpObj) != nullptr)) + { + bTextEdit = true; + } + + // When clicking into a URl field, also go to text edit mode (when not following the link) + if (!bTextEdit && eHit == SdrHitKind::UrlField && !rMEvt.IsMod2() && !lcl_followHyperlinkAllowed(rMEvt)) + bTextEdit = true; + + bool bPreventModify = mpDocSh->IsReadOnly(); + if (bPreventModify && mpDocSh->GetSignPDFCertificate().is()) + { + // If the just added signature line shape is selected, allow moving / resizing it. + bPreventModify = false; + } + + if(!bTextEdit + && !bPreventModify + && ((mpView->IsMarkedHit(aMDPos, nHitLog) && !rMEvt.IsShift() && !rMEvt.IsMod2()) || pHdl != nullptr) + && (rMEvt.GetClicks() != 2) + ) + { + if (!pHdl && mpView->Is3DRotationCreationActive()) + { + // Switch between 3D-rotation body -> selection + mpView->ResetCreationActive(); + } + else if (bWaterCan) + { + // Remember the selected object for proper handling in + // MouseButtonUp(). + pWaterCanCandidate = pickObject (aMDPos); + } + else + { + // hit handle or marked object + bFirstMouseMove = true; + aDragTimer.Start(); + } + + if ( ! rMEvt.IsRight()) + if (mpView->BegDragObj(aMDPos, nullptr, pHdl, nDrgLog)) + mpView->GetDragMethod()->SetShiftPressed( rMEvt.IsShift() ); + bReturn = true; + } + else + { + SdrPageView* pPV = nullptr; + SdrObject* pObj = !rMEvt.IsMod2() ? mpView->PickObj(aMDPos, mpView->getHitTolLog(), pPV, SdrSearchOptions::PICKMACRO) : nullptr; + if (pObj) + { + mpView->BegMacroObj(aMDPos, nHitLog, pObj, pPV, mpWindow); + bReturn = true; + } + else if ( bTextEdit ) + { + SdrObjKind nSdrObjKind = aVEvt.mpObj->GetObjIdentifier(); + + if (aVEvt.mpObj->GetObjInventor() == SdrInventor::Default && + (nSdrObjKind == SdrObjKind::Text || + nSdrObjKind == SdrObjKind::TitleText || + nSdrObjKind == SdrObjKind::OutlineText || + !aVEvt.mpObj->IsEmptyPresObj())) + { + // Seamless Editing: branch to text input + if (!rMEvt.IsShift()) + mpView->UnmarkAll(); + + SfxUInt16Item aItem(SID_TEXTEDIT, 1); + mpViewShell->GetViewFrame()->GetDispatcher()-> + ExecuteList(SID_TEXTEDIT, + SfxCallMode::SYNCHRON | SfxCallMode::RECORD, + { &aItem }); + return bReturn; // CAUTION, due to the synchronous slot the object is deleted now + } + } + else if ( !rMEvt.IsMod2() && rMEvt.GetClicks() == 1 && + aVEvt.meEvent == SdrEventKind::ExecuteUrl ) + { + mpWindow->ReleaseMouse(); + + // If tiled rendering, let client handles URL execution and early returns. + if (comphelper::LibreOfficeKit::isActive()) + { + SfxViewShell& rSfxViewShell = mpViewShell->GetViewShellBase(); + rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED, aVEvt.mpURLField->GetURL().toUtf8().getStr()); + return true; + } + + if (!lcl_followHyperlinkAllowed(rMEvt)) + return true; + + SfxStringItem aStrItem(SID_FILE_NAME, aVEvt.mpURLField->GetURL()); + SfxStringItem aReferer(SID_REFERER, mpDocSh->GetMedium()->GetName()); + SfxBoolItem aBrowseItem( SID_BROWSE, true ); + SfxViewFrame* pFrame = mpViewShell->GetViewFrame(); + mpWindow->ReleaseMouse(); + + if (rMEvt.IsMod1()) + { + // Open in new frame + pFrame->GetDispatcher()->ExecuteList(SID_OPENDOC, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aStrItem, &aBrowseItem, &aReferer }); + } + else + { + // Open in current frame + SfxFrameItem aFrameItem(SID_DOCFRAME, pFrame); + pFrame->GetDispatcher()->ExecuteList(SID_OPENDOC, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aStrItem, &aFrameItem, &aBrowseItem, &aReferer }); + } + + bReturn = true; + } + else if(!rMEvt.IsMod2() + && dynamic_cast< const DrawViewShell *>( mpViewShell ) != nullptr + ) + { + pObj = mpView->PickObj(aMDPos, mpView->getHitTolLog(), pPV, SdrSearchOptions::ALSOONMASTER); + if (pObj) + { + // Handle ImageMap click when not just selecting + if (!bSelectionOnly) + { + if (lcl_followHyperlinkAllowed(rMEvt)) + bReturn = HandleImageMapClick(pObj, aMDPos); + } + + if (!bReturn + && (dynamic_cast<const SdrObjGroup*>(pObj) != nullptr + || dynamic_cast<const E3dScene*>(pObj) != nullptr)) + { + if (rMEvt.GetClicks() == 1) + { + // Look into the group + pObj = mpView->PickObj(aMDPos, mpView->getHitTolLog(), pPV, + SdrSearchOptions::ALSOONMASTER + | SdrSearchOptions::DEEP); + if (pObj && lcl_followHyperlinkAllowed(rMEvt)) + bReturn = HandleImageMapClick(pObj, aMDPos); + } + else if (!bReadOnly && rMEvt.GetClicks() == 2) + { + // New: double click on selected Group object + // enter group + if (!bSelectionOnly + && pObj->getSdrPageFromSdrObject() == pPV->GetPage()) + bReturn = pPV->EnterGroup(pObj); + } + } + } + + // #i71727# replaced else here with two possibilities, once the original else (!pObj) + // and also ignoring the found object when it's on a masterpage + if(!pObj || (pObj->getSdrPageFromSdrObject() && pObj->getSdrPageFromSdrObject()->IsMasterPage())) + { + if(mpView->IsGroupEntered() && 2 == rMEvt.GetClicks()) + { + // New: double click on empty space/on obj on MasterPage, leave group + mpView->LeaveOneGroup(); + bReturn = true; + } + } + } + + if (!bReturn) + { + if (bWaterCan) + { + if ( ! (rMEvt.IsShift() || rMEvt.IsMod2())) + { + // Find the object under the current mouse position + // and store it for the MouseButtonUp() method to + // evaluate. + pWaterCanCandidate = pickObject (aMDPos); + } + } + else + { + bReturn = true; + bool bDeactivateOLE = false; + + if ( !rMEvt.IsShift() && !rMEvt.IsMod2() ) + { + OSL_ASSERT (mpViewShell->GetViewShell()!=nullptr); + Client* pIPClient = static_cast<Client*>( + mpViewShell->GetViewShell()->GetIPClient()); + + if (pIPClient && pIPClient->IsObjectInPlaceActive()) + { + // OLE-object gets deactivated in subsequent UnmarkAll() + bDeactivateOLE = true; + } + + mpView->UnmarkAll(); + } + + bool bMarked = false; + + if ( !rMEvt.IsMod1() && !bDeactivateOLE) + { + if ( rMEvt.IsMod2() ) + { + bMarked = mpView->MarkNextObj(aMDPos, nHitLog, rMEvt.IsShift() ); + } + else + { + bool bToggle = false; + + if (rMEvt.IsShift() && mpView->GetMarkedObjectList().GetMarkCount() > 1) + { + // No Toggle on single selection + bToggle = true; + } + + bMarked = mpView->MarkObj(aMDPos, nHitLog, bToggle); + } + } + + if( !bDeactivateOLE ) + { + if ( !bReadOnly && + bMarked && + (!rMEvt.IsShift() || mpView->IsMarkedHit(aMDPos, nHitLog))) + { + /********************************************************** + * Move object + **********************************************************/ + aDragTimer.Start(); + + pHdl=mpView->PickHandle(aMDPos); + if ( ! rMEvt.IsRight()) + mpView->BegDragObj(aMDPos, nullptr, pHdl, nDrgLog); + } + else + { + /********************************************************** + * Select object + **********************************************************/ + if ( ! rMEvt.IsRight()) + mpView->BegMarkObj(aMDPos); + } + } + + if( bMarked && bTempRotation && (nSlotId == SID_OBJECT_ROTATE) && !rMEvt.IsShift() && (rMEvt.GetClicks() != 2) ) + { + nSlotId = SID_OBJECT_SELECT; + Activate(); + } + } + } + } + } + else if ( !bReadOnly + && (rMEvt.IsLeft() || rMEvt.IsRight()) + && !mpView->IsAction()) + { + /********************************************************************** + * BEZIER-EDITOR + **********************************************************************/ + mpWindow->CaptureMouse(); + SdrViewEvent aVEvt; + SdrHitKind eHit = mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + + if (eHit == SdrHitKind::Handle && aVEvt.mpHdl->GetKind() == SdrHdlKind::BezierWeight) + { + /****************************************************************** + * Drag Handle + ******************************************************************/ + if ( ! rMEvt.IsRight()) + mpView->BegDragObj(aMDPos, nullptr, aVEvt.mpHdl, nDrgLog); + } + else if (eHit == SdrHitKind::MarkedObject && nEditMode == SID_BEZIER_INSERT) + { + /****************************************************************** + * Insert gluepoint + ******************************************************************/ + mpView->BegInsObjPoint(aMDPos, rMEvt.IsMod1()); + } + else if (eHit == SdrHitKind::MarkedObject && rMEvt.IsMod1()) + { + /****************************************************************** + * Select gluepoint + ******************************************************************/ + if (!rMEvt.IsShift()) + mpView->UnmarkAllPoints(); + + if ( ! rMEvt.IsRight()) + mpView->BegMarkPoints(aMDPos); + } + else if (eHit == SdrHitKind::MarkedObject && !rMEvt.IsShift() && !rMEvt.IsMod2()) + { + /****************************************************************** + * Move object + ******************************************************************/ + if ( ! rMEvt.IsRight()) + mpView->BegDragObj(aMDPos, nullptr, nullptr, nDrgLog); + } + else if (eHit == SdrHitKind::Handle) + { + /****************************************************************** + * Select gluepoint + ******************************************************************/ + if (!mpView->IsPointMarked(*aVEvt.mpHdl) || rMEvt.IsShift()) + { + if (!rMEvt.IsShift()) + { + mpView->UnmarkAllPoints(); + pHdl = mpView->PickHandle(aMDPos); + } + else + { + if (mpView->IsPointMarked(*aVEvt.mpHdl)) + { + mpView->UnmarkPoint(*aVEvt.mpHdl); + pHdl = nullptr; + } + else + { + pHdl = mpView->PickHandle(aMDPos); + } + } + + if (pHdl) + { + mpView->MarkPoint(*pHdl); + if ( ! rMEvt.IsRight()) + mpView->BegDragObj(aMDPos, nullptr, pHdl, nDrgLog); + + } + } + else + { + // Point IS marked and NO shift is pressed. Start + // dragging of selected point(s) + pHdl = mpView->PickHandle(aMDPos); + if(pHdl && ! rMEvt.IsRight()) + mpView->BegDragObj(aMDPos, nullptr, pHdl, nDrgLog); + } + } + else + { + /****************************************************************** + * Select or drag object + ******************************************************************/ + if (!rMEvt.IsShift() && !rMEvt.IsMod2() && eHit == SdrHitKind::UnmarkedObject) + { + mpView->UnmarkAllObj(); + } + + bool bMarked = false; + + if (!rMEvt.IsMod1()) + { + if (rMEvt.IsMod2()) + { + bMarked = mpView->MarkNextObj(aMDPos, nHitLog, rMEvt.IsShift()); + } + else + { + bMarked = mpView->MarkObj(aMDPos, nHitLog, rMEvt.IsShift()); + } + } + + if (bMarked && + (!rMEvt.IsShift() || eHit == SdrHitKind::MarkedObject)) + { + // Move object + if ( ! rMEvt.IsRight()) + mpView->BegDragObj(aMDPos, nullptr, aVEvt.mpHdl, nDrgLog); + } + else if (mpView->AreObjectsMarked()) + { + /************************************************************** + * Select gluepoint + **************************************************************/ + if (!rMEvt.IsShift()) + mpView->UnmarkAllPoints(); + + if ( ! rMEvt.IsRight()) + mpView->BegMarkPoints(aMDPos); + } + else + { + /************************************************************** + * Select object + **************************************************************/ + if ( ! rMEvt.IsRight()) + mpView->BegMarkObj(aMDPos); + } + + ForcePointer(&rMEvt); + } + } + + if (!bIsInDragMode) + { + ForcePointer(&rMEvt); + } + + return bReturn; +} + +bool FuSelection::MouseMove(const MouseEvent& rMEvt) +{ + bool bReturn = FuDraw::MouseMove(rMEvt); + + if (aDragTimer.IsActive()) + { + if(bFirstMouseMove) + { + bFirstMouseMove = false; + } + else + { + aDragTimer.Stop(); + } + } + + if (mpView->IsAction()) + { + Point aPix(rMEvt.GetPosPixel()); + Point aPnt(mpWindow->PixelToLogic(aPix)); + + ForceScroll(aPix); + + if (mpView->IsInsObjPoint()) + { + mpView->MovInsObjPoint(aPnt); + } + else + { + mpView->MovAction(aPnt); + } + } + + ForcePointer(&rMEvt); + + return bReturn; +} + +bool FuSelection::MouseButtonUp(const MouseEvent& rMEvt) +{ + bool bReturn = false; + // When the right mouse button is pressed then only select objects + // (and deselect others) as a preparation for showing the context + // menu. + const bool bSelectionOnly = rMEvt.IsRight(); + + if (aDragTimer.IsActive() ) + { + aDragTimer.Stop(); + bIsInDragMode = false; + } + + if( !mpView ) + return false; + + Point aPnt( mpWindow->PixelToLogic( rMEvt.GetPosPixel() ) ); + sal_uInt16 nHitLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(HITPIX,0)).Width() ); + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + + if (mpView->IsFrameDragSingles() || !mpView->HasMarkablePoints()) + { + /********************************************************************** + * NO BEZIER_EDITOR + **********************************************************************/ + if ( mpView->IsDragObj() ) + { + /****************************************************************** + * Object was moved + ******************************************************************/ + FrameView* pFrameView = mpViewShell->GetFrameView(); + bool bDragWithCopy = (rMEvt.IsMod1() && pFrameView->IsDragWithCopy()); + + if (bDragWithCopy) + { + bDragWithCopy = !mpView->IsPresObjSelected(false); + } + + mpView->SetDragWithCopy(bDragWithCopy); + bool bWasDragged(mpView->EndDragObj( mpView->IsDragWithCopy() )); + + mpView->ForceMarkedToAnotherPage(); + + if (!rMEvt.IsShift() && !rMEvt.IsMod1() && !rMEvt.IsMod2() && + !bSelectionChanged && + std::abs(aPnt.X() - aMDPos.X()) < nDrgLog && + std::abs(aPnt.Y() - aMDPos.Y()) < nDrgLog) + { + /************************************************************* + * If a user wants to click on an object in front of a marked + * one, he releases the mouse button immediately + **************************************************************/ + SdrPageView* pPV; + SdrObject* pObj = mpView->PickObj(aMDPos, mpView->getHitTolLog(), pPV, SdrSearchOptions::ALSOONMASTER | SdrSearchOptions::BEFOREMARK); + if (pObj && pPV->IsObjMarkable(pObj)) + { + mpView->UnmarkAllObj(); + mpView->MarkObj(pObj,pPV); + return true; + } + + // check for single object selected + SdrObject* pSingleObj = nullptr; + + if (mpView->GetMarkedObjectList().GetMarkCount()==1) + { + pSingleObj = mpView->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj(); + } + + // Check for click on svx::diagram::DiagramFrameHdl + // - if we hit a SdrHdl + // - if it was not moved + // - if single object is selected + // - and it is a Diagram + if(pHdl && !bWasDragged && nullptr != pSingleObj && pSingleObj->isDiagram()) + { + svx::diagram::DiagramFrameHdl* pDiagramFrameHdl(dynamic_cast<svx::diagram::DiagramFrameHdl*>(pHdl)); + if(nullptr != pDiagramFrameHdl) + { + // let the DiagramFrameHdl decide what to do + svx::diagram::DiagramFrameHdl::clicked(aPnt); + } + } + + /************************************************************** + * Toggle between selection and rotation + **************************************************************/ + if (nSlotId == SID_OBJECT_SELECT + && !comphelper::LibreOfficeKit::isActive() + && mpView->IsRotateAllowed() + + && (rMEvt.GetClicks() != 2) + && (mpViewShell->GetFrameView()->IsClickChangeRotation() + || (pSingleObj + && pSingleObj->GetObjInventor()==SdrInventor::E3d)) + && ! bSelectionOnly) + + { + bTempRotation = true; + nSlotId = SID_OBJECT_ROTATE; + Activate(); + } + else if (nSlotId == SID_OBJECT_ROTATE) + { + nSlotId = SID_OBJECT_SELECT; + Activate(); + } + } + else if (nSlotId == SID_CONVERT_TO_3D_LATHE) + { + if (!pHdl) + { + bSuppressChangesOfSelection = true; + mpView->Start3DCreation(); + bSuppressChangesOfSelection = false; + } + else if (pHdl->GetKind() != SdrHdlKind::MirrorAxis && + pHdl->GetKind() != SdrHdlKind::Ref1 && + pHdl->GetKind() != SdrHdlKind::Ref2 && mpView->Is3DRotationCreationActive()) + { + /********************************************************* + * If 3D-rotation bodies are about to be created, + * end creation now + **********************************************************/ + Degree100 nAngle1 = GetAngle(aPnt - mpView->GetRef1()); + nAngle1 -= 27000_deg100; + nAngle1 = NormAngle36000(nAngle1); + bool bMirrorSide1 = nAngle1 < 18000_deg100; + + if (bMirrorSide0 != bMirrorSide1) + { + bSuppressChangesOfSelection = true; + mpWindow->EnterWait(); + mpView->End3DCreation(); + bSuppressChangesOfSelection = false; + nSlotId = SID_OBJECT_SELECT; + mpWindow->LeaveWait(); + Activate(); + } + } + } + } + else if (rMEvt.IsMod1() + && !rMEvt.IsMod2() + && std::abs(aPnt.X() - aMDPos.X()) < nDrgLog + && std::abs(aPnt.Y() - aMDPos.Y()) < nDrgLog) + { + // Enter group + mpView->MarkObj(aPnt, nHitLog, rMEvt.IsShift(), rMEvt.IsMod1()); + } + + if (mpView->IsAction() ) + { + mpView->EndAction(); + } + + if( SD_MOD()->GetWaterCan() ) + { + if( rMEvt.IsRight() ) + { + // In watering-can mode, on press onto right mouse button, an undo is executed + mpViewShell->GetViewFrame()->GetDispatcher()->Execute( SID_UNDO, SfxCallMode::ASYNCHRON ); + } + else if (pWaterCanCandidate != nullptr) + { + // Is the candidate object still under the mouse? + if (pickObject (aPnt) == pWaterCanCandidate) + { + SdStyleSheetPool* pPool = static_cast<SdStyleSheetPool*>( + mpDocSh->GetStyleSheetPool()); + if (pPool != nullptr) + { + SfxStyleSheet* pStyleSheet = static_cast<SfxStyleSheet*>( + pPool->GetActualStyleSheet()); + if (pStyleSheet != nullptr && mpView->IsUndoEnabled() ) + { + // Added UNDOs for the WaterCan mode. This was never done in + // the past, thus it was missing all the time. + std::unique_ptr<SdrUndoAction> pUndoAttr = mpDoc->GetSdrUndoFactory().CreateUndoAttrObject(*pWaterCanCandidate, true, true); + mpView->BegUndo(pUndoAttr->GetComment()); + mpView->AddUndo(mpDoc->GetSdrUndoFactory().CreateUndoGeoObject(*pWaterCanCandidate)); + mpView->AddUndo(std::move(pUndoAttr)); + + pWaterCanCandidate->SetStyleSheet (pStyleSheet, false); + + mpView->EndUndo(); + } + } + } + } + // else when there has been no object under the mouse when the + // button was pressed then nothing happens even when there is + // one now. + } + + sal_uInt16 nClicks = rMEvt.GetClicks(); + + if (nClicks == 2 && rMEvt.IsLeft() && bMBDown && + !rMEvt.IsMod1() && !rMEvt.IsShift() ) + { + DoubleClick(rMEvt); + } + + bMBDown = false; + + ForcePointer(&rMEvt); + pHdl = nullptr; + mpWindow->ReleaseMouse(); + SdrObject* pSingleObj = nullptr; + const size_t nMarkCount = mpView->GetMarkedObjectList().GetMarkCount(); + + if (nMarkCount==1) + { + pSingleObj = mpView->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj(); + } + + if ( (nSlotId != SID_OBJECT_SELECT && nMarkCount==0) || + ( mpView->GetDragMode() == SdrDragMode::Crook && + !mpView->IsCrookAllowed( mpView->IsCrookNoContortion() ) ) || + ( mpView->GetDragMode() == SdrDragMode::Shear && + !mpView->IsShearAllowed() && !mpView->IsDistortAllowed() ) || + ( nSlotId==SID_CONVERT_TO_3D_LATHE && pSingleObj && + (pSingleObj->GetObjInventor() != SdrInventor::Default || + pSingleObj->GetObjIdentifier() == SdrObjKind::Measure) ) ) + { + bReturn = true; + ForcePointer(&rMEvt); + pHdl = nullptr; + mpWindow->ReleaseMouse(); + FuDraw::MouseButtonUp(rMEvt); + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::SYNCHRON); + return bReturn; // CAUTION, due to the synchronous slot, the object is deleted now. + } + + FuDraw::MouseButtonUp(rMEvt); + } + else + { + /********************************************************************** + * BEZIER_EDITOR + **********************************************************************/ + if ( mpView->IsAction() ) + { + if ( mpView->IsInsObjPoint() ) + { + mpView->EndInsObjPoint(SdrCreateCmd::ForceEnd); + } + else if ( mpView->IsDragObj() ) + { + FrameView* pFrameView = mpViewShell->GetFrameView(); + bool bDragWithCopy = (rMEvt.IsMod1() && pFrameView->IsDragWithCopy()); + + if (bDragWithCopy) + { + bDragWithCopy = !mpView->IsPresObjSelected(false); + } + + mpView->SetDragWithCopy(bDragWithCopy); + mpView->EndDragObj( mpView->IsDragWithCopy() ); + } + else + { + mpView->EndAction(); + + sal_uInt16 nDrgLog2 = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + Point aPos = mpWindow->PixelToLogic( rMEvt.GetPosPixel() ); + + if (std::abs(aMDPos.X() - aPos.X()) < nDrgLog2 && + std::abs(aMDPos.Y() - aPos.Y()) < nDrgLog2 && + !rMEvt.IsShift() && !rMEvt.IsMod2()) + { + SdrViewEvent aVEvt; + SdrHitKind eHit = mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + + if (eHit == SdrHitKind::NONE) + { + // Click on the same place - unselect + mpView->UnmarkAllObj(); + } + } + } + } + else if (!rMEvt.IsShift() && rMEvt.IsMod1() && !rMEvt.IsMod2() && + std::abs(aPnt.X() - aMDPos.X()) < nDrgLog && + std::abs(aPnt.Y() - aMDPos.Y()) < nDrgLog) + { + // Enter group + mpView->MarkObj(aPnt, nHitLog, false, rMEvt.IsMod1()); + } + + ForcePointer(&rMEvt); + pHdl = nullptr; + mpWindow->ReleaseMouse(); + + FuDraw::MouseButtonUp(rMEvt); + } + + return bReturn; +} + +/** + * Process keyboard input + * @returns sal_True if a KeyEvent is being processed, sal_False otherwise + */ +bool FuSelection::KeyInput(const KeyEvent& rKEvt) +{ + bool bReturn = false; + + switch (rKEvt.GetKeyCode().GetCode()) + { + case KEY_ESCAPE: + { + bReturn = FuSelection::cancel(); + } + break; + //add keyboard operation for insert points in drawing curve + case KEY_UP: + case KEY_DOWN: + case KEY_LEFT: + case KEY_RIGHT: + { + if(rKEvt.GetKeyCode().IsShift()&&(nEditMode == SID_BEZIER_INSERT)){ + ::tools::Long nX = 0; + ::tools::Long nY = 0; + sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode(); + if (nCode == KEY_UP) + { + // scroll up + nX = 0; + nY =-1; + } + else if (nCode == KEY_DOWN) + { + // scroll down + nX = 0; + nY = 1; + } + else if (nCode == KEY_LEFT) + { + // scroll left + nX =-1; + nY = 0; + } + else if (nCode == KEY_RIGHT) + { + // scroll right + nX = 1; + nY = 0; + } + + Point centerPoint; + ::tools::Rectangle rect = mpView->GetMarkedObjRect(); + centerPoint = mpWindow->LogicToPixel(rect.Center()); + Point aPoint = bMovedToCenterPoint? oldPoint:centerPoint; + Point ePoint = aPoint + Point(nX,nY); + mpWindow->SetPointerPosPixel(ePoint); + //simulate mouse move action + MouseEvent eMevt(ePoint, 1, MouseEventModifiers::DRAGMOVE, MOUSE_LEFT, 0); + MouseMove(eMevt); + oldPoint = ePoint; + bMovedToCenterPoint = true; + bReturn = true; + } + } + break; + case KEY_RETURN: + if(rKEvt.GetKeyCode().IsShift()&&(nEditMode == SID_BEZIER_INSERT)) + { + if(!bBeginInsertPoint) + { + //simulate mouse button down action + MouseEvent aMevt(oldPoint, 1, + MouseEventModifiers::SIMPLEMOVE | MouseEventModifiers::DRAGMOVE, + MOUSE_LEFT, KEY_SHIFT); + MouseButtonDown(aMevt); + mpWindow->CaptureMouse(); + bBeginInsertPoint = true; + } + else + { + //simulate mouse button up action + MouseEvent rMEvt(oldPoint, 1, + MouseEventModifiers::SIMPLEMOVE | MouseEventModifiers::ENTERWINDOW, + MOUSE_LEFT, KEY_SHIFT); + MouseButtonUp(rMEvt); + bBeginInsertPoint = false; + } + bReturn= true; + } + break; + } + if (!bReturn) + { + bReturn = FuDraw::KeyInput(rKEvt); + + if(mpView->GetMarkedObjectList().GetMarkCount() == 0) + { + mpView->ResetCreationActive(); + + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + } + } + + return bReturn; + +} + +void FuSelection::Activate() +{ + SdrDragMode eMode; + mpView->ResetCreationActive(); + mpView->SetEditMode(SdrViewEditMode::Edit); + + switch( nSlotId ) + { + case SID_OBJECT_ROTATE: + { + eMode = SdrDragMode::Rotate; + + if ( mpView->GetDragMode() != eMode ) + mpView->SetDragMode(eMode); + } + break; + + case SID_OBJECT_MIRROR: + { + eMode = SdrDragMode::Mirror; + + if ( mpView->GetDragMode() != eMode ) + mpView->SetDragMode(eMode); + } + break; + + case SID_OBJECT_CROP: + { + eMode = SdrDragMode::Crop; + + if ( mpView->GetDragMode() != eMode ) + mpView->SetDragMode(eMode); + } + break; + + case SID_OBJECT_TRANSPARENCE: + { + eMode = SdrDragMode::Transparence; + + if ( mpView->GetDragMode() != eMode ) + mpView->SetDragMode(eMode); + } + break; + + case SID_OBJECT_GRADIENT: + { + eMode = SdrDragMode::Gradient; + + if ( mpView->GetDragMode() != eMode ) + mpView->SetDragMode(eMode); + } + break; + + case SID_OBJECT_SHEAR: + { + eMode = SdrDragMode::Shear; + + if ( mpView->GetDragMode() != eMode ) + mpView->SetDragMode(eMode); + } + break; + + case SID_OBJECT_CROOK_ROTATE: + { + eMode = SdrDragMode::Crook; + + if ( mpView->GetDragMode() != eMode ) + { + mpView->SetDragMode(eMode); + mpView->SetCrookMode(SdrCrookMode::Rotate); + } + } + break; + + case SID_OBJECT_CROOK_SLANT: + { + eMode = SdrDragMode::Crook; + + if ( mpView->GetDragMode() != eMode ) + { + mpView->SetDragMode(eMode); + mpView->SetCrookMode(SdrCrookMode::Slant); + } + } + break; + + case SID_OBJECT_CROOK_STRETCH: + { + eMode = SdrDragMode::Crook; + + if ( mpView->GetDragMode() != eMode ) + { + mpView->SetDragMode(eMode); + mpView->SetCrookMode(SdrCrookMode::Stretch); + } + } + break; + + case SID_CONVERT_TO_3D_LATHE: + { + eMode = SdrDragMode::Mirror; + bSuppressChangesOfSelection = true; + + if ( mpView->GetDragMode() != eMode ) + mpView->SetDragMode(eMode); + + if (!mpView->Is3DRotationCreationActive()) + mpView->Start3DCreation(); + + bSuppressChangesOfSelection = false; + } + break; + + default: + { + eMode = SdrDragMode::Move; + + if ( mpView->GetDragMode() != eMode ) + mpView->SetDragMode(eMode); + } + break; + } + + if (nSlotId != SID_OBJECT_ROTATE) + { + bTempRotation = false; + } + + FuDraw::Activate(); +} + +void FuSelection::SelectionHasChanged() +{ + bSelectionChanged = true; + + FuDraw::SelectionHasChanged(); + + if (mpView->Is3DRotationCreationActive() && !bSuppressChangesOfSelection) + { + // Switch rotation body -> selection + mpView->ResetCreationActive(); + nSlotId = SID_OBJECT_SELECT; + Activate(); + } + + // Activate the right tool bar for the current context of the view. + mpViewShell->GetViewShellBase().GetToolBarManager()->SelectionHasChanged(*mpViewShell, *mpView); +} + +/** + * Set current bezier edit mode + */ +void FuSelection::SetEditMode(sal_uInt16 nMode) +{ + nEditMode = nMode; + + if (nEditMode == SID_BEZIER_INSERT) + { + mpView->SetInsObjPointMode(true); + } + else + { + mpView->SetInsObjPointMode(false); + } + + ForcePointer(); + + SfxBindings& rBindings = mpViewShell->GetViewFrame()->GetBindings(); + rBindings.Invalidate(SID_BEZIER_MOVE); + rBindings.Invalidate(SID_BEZIER_INSERT); +} + +/** + * Execute ImageMap interaction + */ +bool FuSelection::HandleImageMapClick(const SdrObject* pObj, const Point& rPos) +{ + bool bClosed = pObj->IsClosedObj(); + bool bFilled = false; + + if (bClosed) + { + SfxItemSet aSet(mpDoc->GetPool()); + + aSet.Put(pObj->GetMergedItemSet()); + + const XFillStyleItem& rFillStyle = aSet.Get(XATTR_FILLSTYLE); + bFilled = rFillStyle.GetValue() != drawing::FillStyle_NONE; + } + + const SdrLayerIDSet* pVisiLayer = &mpView->GetSdrPageView()->GetVisibleLayers(); + sal_uInt16 nHitLog = sal_uInt16(mpWindow->PixelToLogic(Size(HITPIX, 0)).Width()); + const ::tools::Long n2HitLog = nHitLog * 2; + Point aHitPosR(rPos); + Point aHitPosL(rPos); + Point aHitPosT(rPos); + Point aHitPosB(rPos); + + aHitPosR.AdjustX(n2HitLog); + aHitPosL.AdjustX(-n2HitLog); + aHitPosT.AdjustY(n2HitLog); + aHitPosB.AdjustY(-n2HitLog); + + if (!bClosed || !bFilled + || (SdrObjectPrimitiveHit(*pObj, aHitPosR, nHitLog, *mpView->GetSdrPageView(), pVisiLayer, + false) + && SdrObjectPrimitiveHit(*pObj, aHitPosL, nHitLog, *mpView->GetSdrPageView(), + pVisiLayer, false) + && SdrObjectPrimitiveHit(*pObj, aHitPosT, nHitLog, *mpView->GetSdrPageView(), + pVisiLayer, false) + && SdrObjectPrimitiveHit(*pObj, aHitPosB, nHitLog, *mpView->GetSdrPageView(), + pVisiLayer, false))) + { + if (SvxIMapInfo::GetIMapInfo(pObj)) + { + const IMapObject* pIMapObj = SvxIMapInfo::GetHitIMapObject(pObj, rPos); + + if (pIMapObj && !pIMapObj->GetURL().isEmpty()) + { + // Jump to Document + mpWindow->ReleaseMouse(); + SfxStringItem aStrItem(SID_FILE_NAME, pIMapObj->GetURL()); + SfxStringItem aReferer(SID_REFERER, mpDocSh->GetMedium()->GetName()); + SfxViewFrame* pFrame = mpViewShell->GetViewFrame(); + SfxFrameItem aFrameItem(SID_DOCFRAME, pFrame); + SfxBoolItem aBrowseItem(SID_BROWSE, true); + mpWindow->ReleaseMouse(); + pFrame->GetDispatcher()->ExecuteList( + SID_OPENDOC, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aStrItem, &aFrameItem, &aBrowseItem, &aReferer }); + + return true; + } + } + } + + return false; +} + +/** is called when the current function should be aborted. <p> + This is used when a function gets a KEY_ESCAPE but can also + be called directly. + + @returns true if an active function was aborted +*/ +bool FuSelection::cancel() +{ + if (mpView->Is3DRotationCreationActive()) + { + mpView->ResetCreationActive(); + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + return true; + } + else + { + return false; + } +} + +SdrObject* FuSelection::pickObject (const Point& rTestPoint) +{ + SdrPageView* pPageView; + sal_uInt16 nHitLog = sal_uInt16 (mpWindow->PixelToLogic(Size(HITPIX,0)).Width()); + return mpView->PickObj(rTestPoint, nHitLog, pPageView, SdrSearchOptions::PICKMARKABLE); +} + +void FuSelection::ForcePointer(const MouseEvent* pMEvt) +{ + if(bMovedToCenterPoint && !bBeginInsertPoint && pMEvt) + { + MouseEvent aMEvt(pMEvt->GetPosPixel(), pMEvt->GetClicks(), + pMEvt->GetMode(), pMEvt->GetButtons(), pMEvt->GetModifier() & ~KEY_SHIFT); + FuDraw::ForcePointer(&aMEvt); + } + else + { + FuDraw::ForcePointer(pMEvt); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fusldlg.cxx b/sd/source/ui/func/fusldlg.cxx new file mode 100644 index 000000000..c0269b08a --- /dev/null +++ b/sd/source/ui/func/fusldlg.cxx @@ -0,0 +1,226 @@ +/* -*- 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 <fusldlg.hxx> +#include <svl/eitem.hxx> +#include <svl/itemset.hxx> +#include <svl/stritem.hxx> +#include <svl/intitem.hxx> + +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <sdresid.hxx> +#include <strings.hrc> +#include <sdattr.hrc> +#include <sdmod.hxx> +#include <Window.hxx> +#include <optsitem.hxx> +#include <sdabstdlg.hxx> + +namespace sd { + +#define ITEMVALUE(ItemSet,Id,Cast) static_cast<const Cast&>((ItemSet).Get(Id)).GetValue() + + +FuSlideShowDlg::FuSlideShowDlg ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor( pViewSh, pWin, pView, pDoc, rReq ) +{ +} + +rtl::Reference<FuPoor> FuSlideShowDlg::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuSlideShowDlg( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuSlideShowDlg::DoExecute( SfxRequest& ) +{ + PresentationSettings& rPresentationSettings = mpDoc->getPresentationSettings(); + + SfxItemSetFixed<ATTR_PRESENT_START, ATTR_PRESENT_END> aDlgSet( mpDoc->GetPool() ); + std::vector<OUString> aPageNameList(mpDoc->GetSdPageCount( PageKind::Standard )); + const OUString& rPresPage = rPresentationSettings.maPresPage; + OUString aFirstPage; + SdPage* pPage = nullptr; + ::tools::Long nPage; + + for( nPage = mpDoc->GetSdPageCount( PageKind::Standard ) - 1; nPage >= 0; nPage-- ) + { + pPage = mpDoc->GetSdPage( static_cast<sal_uInt16>(nPage), PageKind::Standard ); + OUString aStr( pPage->GetName() ); + + if ( aStr.isEmpty() ) + { + aStr = SdResId( STR_PAGE ) + OUString::number( nPage + 1 ); + } + + aPageNameList[ nPage ] = aStr; + + // is this our (existing) first page? + if ( rPresPage == aStr ) + aFirstPage = rPresPage; + else if ( pPage->IsSelected() && aFirstPage.isEmpty() ) + aFirstPage = aStr; + } + SdCustomShowList* pCustomShowList = mpDoc->GetCustomShowList(); // No Create + + if( aFirstPage.isEmpty() && pPage ) + aFirstPage = pPage->GetName(); + + aDlgSet.Put( SfxBoolItem( ATTR_PRESENT_ALL, rPresentationSettings.mbAll ) ); + aDlgSet.Put( SfxBoolItem( ATTR_PRESENT_CUSTOMSHOW, rPresentationSettings.mbCustomShow ) ); + aDlgSet.Put( SfxStringItem( ATTR_PRESENT_DIANAME, aFirstPage ) ); + aDlgSet.Put( SfxBoolItem( ATTR_PRESENT_ENDLESS, rPresentationSettings.mbEndless ) ); + aDlgSet.Put( SfxBoolItem( ATTR_PRESENT_MANUEL, rPresentationSettings.mbManual ) ); + aDlgSet.Put( SfxBoolItem( ATTR_PRESENT_MOUSE, rPresentationSettings.mbMouseVisible ) ); + aDlgSet.Put( SfxBoolItem( ATTR_PRESENT_PEN, rPresentationSettings.mbMouseAsPen ) ); + aDlgSet.Put( SfxBoolItem( ATTR_PRESENT_ANIMATION_ALLOWED, rPresentationSettings.mbAnimationAllowed ) ); + aDlgSet.Put( SfxBoolItem( ATTR_PRESENT_CHANGE_PAGE, !rPresentationSettings.mbLockedPages ) ); + aDlgSet.Put( SfxBoolItem( ATTR_PRESENT_ALWAYS_ON_TOP, rPresentationSettings.mbAlwaysOnTop ) ); + aDlgSet.Put( SfxBoolItem( ATTR_PRESENT_FULLSCREEN, rPresentationSettings.mbFullScreen ) ); + aDlgSet.Put( SfxUInt32Item( ATTR_PRESENT_PAUSE_TIMEOUT, rPresentationSettings.mnPauseTimeout ) ); + aDlgSet.Put( SfxBoolItem( ATTR_PRESENT_SHOW_PAUSELOGO, rPresentationSettings.mbShowPauseLogo ) ); + + SdOptions* pOptions = SD_MOD()->GetSdOptions(DocumentType::Impress); + aDlgSet.Put( SfxInt32Item( ATTR_PRESENT_DISPLAY, pOptions->GetDisplay() ) ); + + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSdStartPresDlg> pDlg( pFact->CreateSdStartPresentationDlg(mpWindow ? mpWindow->GetFrameWeld() : nullptr, aDlgSet, aPageNameList, pCustomShowList) ); + if( pDlg->Execute() != RET_OK ) + return; + + ::tools::Long nValue32; + bool bValue; + bool bValuesChanged = false; + + pDlg->GetAttr( aDlgSet ); + + bValue = ITEMVALUE( aDlgSet, ATTR_PRESENT_ALL, SfxBoolItem ); + if ( bValue != rPresentationSettings.mbAll ) + { + bValuesChanged = true; + rPresentationSettings.mbAll = bValue; + // remove any previous existing slide + rPresentationSettings.maPresPage.clear(); + } + + if (!rPresentationSettings.mbAll) + { + OUString aPage = ITEMVALUE( aDlgSet, ATTR_PRESENT_DIANAME, SfxStringItem ); + if( aPage != rPresentationSettings.maPresPage ) + { + bValuesChanged = true; + rPresentationSettings.maPresPage = aPage; + } + } + + bValue = ITEMVALUE( aDlgSet, ATTR_PRESENT_CUSTOMSHOW, SfxBoolItem ); + if ( bValue != rPresentationSettings.mbCustomShow ) + { + bValuesChanged = true; + rPresentationSettings.mbCustomShow = bValue; + rPresentationSettings.mbStartCustomShow = false; + } + + bValue = ITEMVALUE( aDlgSet, ATTR_PRESENT_ENDLESS, SfxBoolItem ); + if ( bValue != rPresentationSettings.mbEndless ) + { + bValuesChanged = true; + rPresentationSettings.mbEndless = bValue; + } + + bValue = ITEMVALUE( aDlgSet, ATTR_PRESENT_MANUEL, SfxBoolItem ); + if ( bValue != rPresentationSettings.mbManual ) + { + bValuesChanged = true; + rPresentationSettings.mbManual = bValue; + } + + bValue = ITEMVALUE( aDlgSet, ATTR_PRESENT_MOUSE, SfxBoolItem ); + if ( bValue != rPresentationSettings.mbMouseVisible ) + { + bValuesChanged = true; + rPresentationSettings.mbMouseVisible = bValue; + } + + bValue = ITEMVALUE( aDlgSet, ATTR_PRESENT_PEN, SfxBoolItem ); + if ( bValue != rPresentationSettings.mbMouseAsPen ) + { + bValuesChanged = true; + rPresentationSettings.mbMouseAsPen = bValue; + } + + bValue = !ITEMVALUE( aDlgSet, ATTR_PRESENT_CHANGE_PAGE, SfxBoolItem ); + if ( bValue != rPresentationSettings.mbLockedPages ) + { + bValuesChanged = true; + rPresentationSettings.mbLockedPages = bValue; + } + + bValue = ITEMVALUE( aDlgSet, ATTR_PRESENT_ANIMATION_ALLOWED, SfxBoolItem ); + if ( bValue != rPresentationSettings.mbAnimationAllowed ) + { + bValuesChanged = true; + rPresentationSettings.mbAnimationAllowed = bValue; + } + + bValue = ITEMVALUE( aDlgSet, ATTR_PRESENT_ALWAYS_ON_TOP, SfxBoolItem ); + if ( bValue != rPresentationSettings.mbAlwaysOnTop ) + { + bValuesChanged = true; + rPresentationSettings.mbAlwaysOnTop = bValue; + } + + bValue = ITEMVALUE( aDlgSet, ATTR_PRESENT_FULLSCREEN, SfxBoolItem ); + if ( bValue != rPresentationSettings.mbFullScreen ) + { + bValuesChanged = true; + rPresentationSettings.mbFullScreen = bValue; + } + + nValue32 = ITEMVALUE( aDlgSet, ATTR_PRESENT_PAUSE_TIMEOUT, SfxUInt32Item ); + if( nValue32 != rPresentationSettings.mnPauseTimeout ) + { + bValuesChanged = true; + rPresentationSettings.mnPauseTimeout = nValue32; + } + + bValue = ITEMVALUE( aDlgSet, ATTR_PRESENT_SHOW_PAUSELOGO, SfxBoolItem ); + if ( bValue != rPresentationSettings.mbShowPauseLogo ) + { + bValuesChanged = true; + rPresentationSettings.mbShowPauseLogo = bValue; + } + + pOptions->SetDisplay( ITEMVALUE( aDlgSet, ATTR_PRESENT_DISPLAY, SfxInt32Item ) ); + + // is something has changed, we set the modified flag + if ( bValuesChanged ) + mpDoc->SetChanged(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fusnapln.cxx b/sd/source/ui/func/fusnapln.cxx new file mode 100644 index 000000000..ee51d78ce --- /dev/null +++ b/sd/source/ui/func/fusnapln.cxx @@ -0,0 +1,196 @@ +/* -*- 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 <fusnapln.hxx> +#include <svl/intitem.hxx> +#include <sfx2/request.hxx> +#include <svx/svxids.hrc> + +#include <strings.hrc> +#include <sdattr.hrc> + +#include <View.hxx> +#include <ViewShell.hxx> +#include <DrawViewShell.hxx> +#include <Window.hxx> +#include <sdenumdef.hxx> +#include <sdresid.hxx> +#include <sdabstdlg.hxx> +#include <svx/svdpagv.hxx> + +namespace sd { + + +FuSnapLine::FuSnapLine(ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, + SdDrawDocument* pDoc, SfxRequest& rReq) : + FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuSnapLine::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuSnapLine( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuSnapLine::DoExecute( SfxRequest& rReq ) +{ + const SfxItemSet* pArgs = rReq.GetArgs(); + sal_uInt16 nHelpLine = 0; + bool bCreateNew = true; + + // Get index of snap line or snap point from the request. + const SfxUInt32Item* pHelpLineIndex = rReq.GetArg<SfxUInt32Item>(ID_VAL_INDEX); + if (pHelpLineIndex != nullptr) + { + nHelpLine = static_cast<sal_uInt16>(pHelpLineIndex->GetValue()); + // Reset the argument pointer to trigger the display of the dialog. + pArgs = nullptr; + } + + SdrPageView* pPV = mpView->GetSdrPageView(); + + if (!pArgs) + { + SfxItemSetFixed<ATTR_SNAPLINE_START, ATTR_SNAPLINE_END> aNewAttr(mpViewShell->GetPool()); + bool bLineExist (false); + Point aLinePos; + + if (pHelpLineIndex == nullptr) + { + // The index of the snap line is not provided as argument to the + // request. Determine it from the mouse position. + + aLinePos = static_cast<DrawViewShell*>(mpViewShell)->GetMousePos(); + + if ( aLinePos.X() >= 0 ) + { + aLinePos = mpWindow->PixelToLogic(aLinePos); + sal_uInt16 nHitLog = static_cast<sal_uInt16>(mpWindow->PixelToLogic(Size(HITPIX,0)).Width()); + bLineExist = mpView->PickHelpLine(aLinePos, nHitLog, *mpWindow->GetOutDev(), nHelpLine, pPV); + if ( bLineExist ) + aLinePos = (pPV->GetHelpLines())[nHelpLine].GetPos(); + else + pPV = mpView->GetSdrPageView(); + + pPV->LogicToPagePos(aLinePos); + } + else + aLinePos = Point(0,0); + } + else + { + assert(pPV!=nullptr); + aLinePos = (pPV->GetHelpLines())[nHelpLine].GetPos(); + pPV->LogicToPagePos(aLinePos); + bLineExist = true; + } + aNewAttr.Put(SfxInt32Item(ATTR_SNAPLINE_X, aLinePos.X())); + aNewAttr.Put(SfxInt32Item(ATTR_SNAPLINE_Y, aLinePos.Y())); + + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + vcl::Window* pWin = mpViewShell->GetActiveWindow(); + ScopedVclPtr<AbstractSdSnapLineDlg> pDlg( pFact->CreateSdSnapLineDlg(pWin ? pWin->GetFrameWeld() : nullptr, aNewAttr, mpView) ); + + if ( bLineExist ) + { + pDlg->HideRadioGroup(); + + const SdrHelpLine& rHelpLine = (pPV->GetHelpLines())[nHelpLine]; + + if ( rHelpLine.GetKind() == SdrHelpLineKind::Point ) + { + pDlg->SetText(SdResId(STR_SNAPDLG_SETPOINT)); + pDlg->SetInputFields(true, true); + } + else + { + pDlg->SetText(SdResId(STR_SNAPDLG_SETLINE)); + + if ( rHelpLine.GetKind() == SdrHelpLineKind::Vertical ) + pDlg->SetInputFields(true, false); + else + pDlg->SetInputFields(false, true); + } + bCreateNew = false; + } + else + pDlg->HideDeleteBtn(); + + sal_uInt16 nResult = pDlg->Execute(); + + pDlg->GetAttr(aNewAttr); + pDlg.disposeAndClear(); + + switch( nResult ) + { + case RET_OK: + rReq.Done(aNewAttr); + pArgs = rReq.GetArgs(); + break; + + case RET_SNAP_DELETE: + // delete snap object + if ( !bCreateNew ) + pPV->DeleteHelpLine(nHelpLine); + [[fallthrough]]; + default: + return; + } + } + Point aHlpPos; + + aHlpPos.setX( static_cast<const SfxInt32Item&>( pArgs->Get(ATTR_SNAPLINE_X)).GetValue() ); + aHlpPos.setY( static_cast<const SfxInt32Item&>( pArgs->Get(ATTR_SNAPLINE_Y)).GetValue() ); + pPV->PagePosToLogic(aHlpPos); + + if ( bCreateNew ) + { + SdrHelpLineKind eKind; + + pPV = mpView->GetSdrPageView(); + + switch ( static_cast<SnapKind>(static_cast<const SfxUInt16Item&>( + pArgs->Get(ATTR_SNAPLINE_KIND)).GetValue()) ) + { + case SnapKind::Horizontal : eKind = SdrHelpLineKind::Horizontal; break; + case SnapKind::Vertical : eKind = SdrHelpLineKind::Vertical; break; + default : eKind = SdrHelpLineKind::Point; break; + } + pPV->InsertHelpLine(SdrHelpLine(eKind, aHlpPos)); + } + else + { + const SdrHelpLine& rHelpLine = (pPV->GetHelpLines())[nHelpLine]; + pPV->SetHelpLine(nHelpLine, SdrHelpLine(rHelpLine.GetKind(), aHlpPos)); + } +} + +void FuSnapLine::Activate() +{ +} + +void FuSnapLine::Deactivate() +{ +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fusumry.cxx b/sd/source/ui/func/fusumry.cxx new file mode 100644 index 000000000..9b160099c --- /dev/null +++ b/sd/source/ui/func/fusumry.cxx @@ -0,0 +1,229 @@ +/* -*- 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 <fusumry.hxx> +#include <editeng/eeitem.hxx> +#include <svx/svdotext.hxx> +#include <svx/svdundo.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xlineit0.hxx> +#include <editeng/outlobj.hxx> +#include <xmloff/autolayout.hxx> + +#include <strings.hrc> + +#include <pres.hxx> +#include <View.hxx> +#include <sdpage.hxx> +#include <Outliner.hxx> +#include <drawdoc.hxx> +#include <ViewShell.hxx> +#include <sdmod.hxx> +#include <sdresid.hxx> +#include <DrawViewShell.hxx> + +using namespace com::sun::star; + +namespace sd { + + +FuSummaryPage::FuSummaryPage ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuSummaryPage::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuSummaryPage( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuSummaryPage::DoExecute( SfxRequest& ) +{ + std::unique_ptr<SdOutliner> pOutl; + rtl::Reference<SdPage> pSummaryPage; + sal_uInt16 i = 0; + sal_uInt16 nFirstPage = SDRPAGE_NOTFOUND; + sal_uInt16 nSelectedPages = 0; + sal_uInt16 nCount = mpDoc->GetSdPageCount(PageKind::Standard); + + while (i < nCount && nSelectedPages <= 1) + { + /* How many pages are selected? + exactly one: pool everything from this page + otherwise: only pool the selected pages */ + SdPage* pActualPage = mpDoc->GetSdPage(i, PageKind::Standard); + + if (pActualPage->IsSelected()) + { + if (nFirstPage == SDRPAGE_NOTFOUND) + { + nFirstPage = i; + } + + nSelectedPages++; + } + + i++; + } + + bool bBegUndo = false; + + SfxStyleSheet* pStyle = nullptr; + + for (i = nFirstPage; i < nCount; i++) + { + SdPage* pActualPage = mpDoc->GetSdPage(i, PageKind::Standard); + + if (nSelectedPages <= 1 || pActualPage->IsSelected()) + { + SdPage* pActualNotesPage = mpDoc->GetSdPage(i, PageKind::Notes); + SdrTextObj* pTextObj = static_cast<SdrTextObj*>( pActualPage->GetPresObj(PresObjKind::Title) ); + + if (pTextObj && !pTextObj->IsEmptyPresObj()) + { + if (!pSummaryPage) + { + // insert "table of content"-page and create outliner + const bool bUndo = mpView->IsUndoEnabled(); + + if( bUndo ) + { + mpView->BegUndo(SdResId(STR_UNDO_SUMMARY_PAGE)); + bBegUndo = true; + } + + SdrLayerIDSet aVisibleLayers = pActualPage->TRG_GetMasterPageVisibleLayers(); + + // page with title & structuring! + pSummaryPage = mpDoc->AllocSdPage(false); + pSummaryPage->SetSize(pActualPage->GetSize() ); + pSummaryPage->SetBorder(pActualPage->GetLeftBorder(), + pActualPage->GetUpperBorder(), + pActualPage->GetRightBorder(), + pActualPage->GetLowerBorder() ); + + // insert page at the back + mpDoc->InsertPage(pSummaryPage.get(), nCount * 2 + 1); + if( bUndo ) + mpView->AddUndo(mpDoc->GetSdrUndoFactory().CreateUndoNewPage(*pSummaryPage)); + + // use MasterPage of the current page + pSummaryPage->TRG_SetMasterPage(pActualPage->TRG_GetMasterPage()); + pSummaryPage->SetLayoutName(pActualPage->GetLayoutName()); + pSummaryPage->SetAutoLayout(AUTOLAYOUT_TITLE_CONTENT, true); + pSummaryPage->TRG_SetMasterPageVisibleLayers(aVisibleLayers); + pSummaryPage->setHeaderFooterSettings(pActualPage->getHeaderFooterSettings()); + + // notes-page + rtl::Reference<SdPage> pNotesPage = mpDoc->AllocSdPage(false); + pNotesPage->SetSize(pActualNotesPage->GetSize()); + pNotesPage->SetBorder(pActualNotesPage->GetLeftBorder(), + pActualNotesPage->GetUpperBorder(), + pActualNotesPage->GetRightBorder(), + pActualNotesPage->GetLowerBorder() ); + pNotesPage->SetPageKind(PageKind::Notes); + + // insert page at the back + mpDoc->InsertPage(pNotesPage.get(), nCount * 2 + 2); + + if( bUndo ) + mpView->AddUndo(mpDoc->GetSdrUndoFactory().CreateUndoNewPage(*pNotesPage)); + + // use MasterPage of the current page + pNotesPage->TRG_SetMasterPage(pActualNotesPage->TRG_GetMasterPage()); + pNotesPage->SetLayoutName(pActualNotesPage->GetLayoutName()); + pNotesPage->SetAutoLayout(pActualNotesPage->GetAutoLayout(), true); + pNotesPage->TRG_SetMasterPageVisibleLayers(aVisibleLayers); + pNotesPage->setHeaderFooterSettings(pActualNotesPage->getHeaderFooterSettings()); + + pOutl.reset(new SdOutliner( mpDoc, OutlinerMode::OutlineObject )); + pOutl->SetUpdateLayout(false); + pOutl->EnableUndo(false); + + if (mpDocSh) + pOutl->SetRefDevice(SD_MOD()->GetVirtualRefDevice()); + + pOutl->SetDefTab( mpDoc->GetDefaultTabulator() ); + pOutl->SetStyleSheetPool(static_cast<SfxStyleSheetPool*>(mpDoc->GetStyleSheetPool())); + pStyle = pSummaryPage->GetStyleSheetForPresObj( PresObjKind::Outline ); + pOutl->SetStyleSheet( 0, pStyle ); + } + + // add text + OutlinerParaObject* pParaObj = pTextObj->GetOutlinerParaObject(); + // #118876#, check if the OutlinerParaObject is created successfully + if( pParaObj ) + { + pParaObj->SetOutlinerMode( OutlinerMode::OutlineObject ); + pOutl->AddText(*pParaObj); + } + } + } + } + + if (!pSummaryPage) + return; + + SdrTextObj* pTextObj = static_cast<SdrTextObj*>( pSummaryPage->GetPresObj(PresObjKind::Outline) ); + + if (!pTextObj) + return; + + // remove hard break- and character attributes + SfxItemSetFixed<EE_ITEMS_START, EE_ITEMS_END> aEmptyEEAttr(mpDoc->GetPool()); + sal_Int32 nParaCount = pOutl->GetParagraphCount(); + + for (sal_Int32 nPara = 0; nPara < nParaCount; nPara++) + { + pOutl->SetStyleSheet( nPara, pStyle ); + pOutl->RemoveCharAttribs(nPara); + pOutl->SetParaAttribs(nPara, aEmptyEEAttr); + pOutl->SetDepth(pOutl->GetParagraph(nPara), 0); + } + + pTextObj->SetOutlinerParaObject( pOutl->CreateParaObject() ); + pTextObj->SetEmptyPresObj(false); + + // remove hard attributes (Flag to sal_True) + SfxItemSet aAttr(mpDoc->GetPool()); + aAttr.Put(XLineStyleItem(drawing::LineStyle_NONE)); + aAttr.Put(XFillStyleItem(drawing::FillStyle_NONE)); + pTextObj->SetMergedItemSet(aAttr); + + if( bBegUndo ) + mpView->EndUndo(); + pOutl.reset(); + + DrawViewShell* pDrawViewShell= dynamic_cast< DrawViewShell* >( mpViewShell ); + if(pDrawViewShell) + { + pDrawViewShell->SwitchPage( (pSummaryPage->GetPageNum() - 1) / 2); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/futempl.cxx b/sd/source/ui/func/futempl.cxx new file mode 100644 index 000000000..2c0c22ecd --- /dev/null +++ b/sd/source/ui/func/futempl.cxx @@ -0,0 +1,638 @@ +/* -*- 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 <com/sun/star/style/XStyleFamiliesSupplier.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/frame/XModel.hpp> + +#include <futempl.hxx> + +#include <svx/svxids.hrc> +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> +#include <editeng/eeitem.hxx> +#include <sfx2/request.hxx> +#include <sfx2/sfxdlg.hxx> +#include <editeng/numitem.hxx> +#include <svx/svdopage.hxx> +#include <editeng/colritem.hxx> +#include <editeng/brushitem.hxx> +#include <svx/svditer.hxx> +#include <svx/sdr/properties/properties.hxx> +#include <svl/intitem.hxx> + +#include <sfx2/viewfrm.hxx> +#include <svx/xlndsit.hxx> +#include <svx/xlnstit.hxx> +#include <svx/xlnedit.hxx> +#include <svx/xbtmpit.hxx> +#include <svx/xflgrit.hxx> +#include <svx/xflftrit.hxx> +#include <svx/xflhtit.hxx> +#include <o3tl/string_view.hxx> +#include <app.hrc> +#include <stlsheet.hxx> +#include <sdpage.hxx> +#include <stlpool.hxx> +#include <sdmod.hxx> +#include <View.hxx> +#include <drawdoc.hxx> +#include <DrawDocShell.hxx> +#include <DrawViewShell.hxx> +#include <ViewShell.hxx> + +#include <strings.hrc> +#include <prlayout.hxx> +#include <sdresid.hxx> +#include <OutlineView.hxx> +#include <sdabstdlg.hxx> +#include <memory> + +using namespace com::sun::star::uno; +using namespace com::sun::star::container; +using namespace com::sun::star::beans; +using namespace com::sun::star::style; + +namespace sd +{ + + +FuTemplate::FuTemplate ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq ) + : FuPoor( pViewSh, pWin, pView, pDoc, rReq ) +{ +} + +rtl::Reference<FuPoor> FuTemplate::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuTemplate( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuTemplate::DoExecute( SfxRequest& rReq ) +{ + const SfxItemSet* pArgs = rReq.GetArgs(); + sal_uInt16 nSId = rReq.GetSlot(); + + // get StyleSheet parameter + SfxStyleSheetBasePool* pSSPool = mpDoc->GetDocSh()->GetStyleSheetPool(); + SfxStyleSheetBase* pStyleSheet = nullptr; + + const SfxPoolItem* pItem; + SfxStyleFamily nFamily = SfxStyleFamily(USHRT_MAX); + if( pArgs && SfxItemState::SET == pArgs->GetItemState( SID_STYLE_FAMILY, + false, &pItem )) + { + nFamily = static_cast<SfxStyleFamily>( pArgs->Get( SID_STYLE_FAMILY ).GetValue()); + } + else if( pArgs && SfxItemState::SET == pArgs->GetItemState( SID_STYLE_FAMILYNAME, + false, &pItem )) + { + OUString sFamily = pArgs->Get( SID_STYLE_FAMILYNAME ).GetValue(); + if (sFamily == "graphics") + nFamily = SfxStyleFamily::Para; + else + nFamily = SfxStyleFamily::Pseudo; + } + + OUString aStyleName; + sal_uInt16 nRetMask = static_cast<sal_uInt16>(SfxStyleSearchBits::All); + + switch( nSId ) + { + case SID_STYLE_APPLY: + case SID_STYLE_EDIT: + case SID_STYLE_DELETE: + case SID_STYLE_HIDE: + case SID_STYLE_SHOW: + case SID_STYLE_FAMILY: + case SID_STYLE_NEW_BY_EXAMPLE: + { + const SfxStringItem* pNameItem = rReq.GetArg<SfxStringItem>(SID_APPLY_STYLE); + const SfxStringItem* pFamilyItem = rReq.GetArg<SfxStringItem>(SID_STYLE_FAMILYNAME); + if ( pFamilyItem && pNameItem ) + { + try + { + Reference< XStyleFamiliesSupplier > xModel(mpDoc->GetDocSh()->GetModel(), UNO_QUERY_THROW ); + Reference< XNameAccess > xCont( xModel->getStyleFamilies() ); + Reference< XNameAccess > xStyles( xCont->getByName(pFamilyItem->GetValue()), UNO_QUERY_THROW ); + Reference< XPropertySet > xInfo( xStyles->getByName( pNameItem->GetValue() ), UNO_QUERY_THROW ); + + OUString aUIName; + xInfo->getPropertyValue( "DisplayName" ) >>= aUIName; + if ( !aUIName.isEmpty() ) + rReq.AppendItem( SfxStringItem( nSId, aUIName ) ); + } + catch( Exception& ) + { + } + } + + if (pArgs && pArgs->GetItemState(nSId) == SfxItemState::SET) + aStyleName = static_cast<const SfxStringItem &>( pArgs->Get( nSId ) ).GetValue(); + } + } + + switch( nSId ) + { + case SID_STYLE_NEW: + { + SfxStyleSheetBase *p = pSSPool->Find(aStyleName, nFamily ); + if(p) + { + pSSPool->Remove(p); + p = nullptr; + } + pStyleSheet = &pSSPool->Make( aStyleName, nFamily, SfxStyleSearchBits::UserDefined ); + + if (pArgs && pArgs->GetItemState(SID_STYLE_REFERENCE) == SfxItemState::SET) + { + OUString aParentName( pArgs->Get(SID_STYLE_REFERENCE).GetValue()); + pStyleSheet->SetParent(aParentName); + } + else + { + pStyleSheet->SetParent(SdResId(STR_STANDARD_STYLESHEET_NAME)); + } + } + break; + + case SID_STYLE_NEW_BY_EXAMPLE: + { + // at the moment, the dialog to enter the name of the template is still opened + SfxStyleSheetBase *p = pSSPool->Find(aStyleName, nFamily ); + if(p) + { + pSSPool->Remove(p); + p = nullptr; + } + pStyleSheet = &pSSPool->Make( aStyleName, nFamily, SfxStyleSearchBits::UserDefined ); + pStyleSheet->SetParent(SdResId(STR_STANDARD_STYLESHEET_NAME)); + } + break; + + case SID_STYLE_EDIT: + pStyleSheet = pSSPool->Find( aStyleName, nFamily); + break; + + case SID_STYLE_DELETE: + pStyleSheet = pSSPool->Find( aStyleName, nFamily); + if( pStyleSheet ) + { + pSSPool->Remove( pStyleSheet ); + nRetMask = sal_uInt16(true); + mpDoc->SetChanged(); + } + else + { + nRetMask = sal_uInt16(false); + } + break; + + case SID_STYLE_HIDE: + case SID_STYLE_SHOW: + pStyleSheet = pSSPool->Find( aStyleName, nFamily); + pStyleSheet->SetHidden( nSId == SID_STYLE_HIDE ); + nRetMask = sal_uInt16(true); + break; + + case SID_STYLE_APPLY: + // apply the template to the document + pStyleSheet = pSSPool->Find( aStyleName, nFamily); + + // do not set presentation styles, they will be set implicit + if ( pStyleSheet && pStyleSheet->GetFamily() != SfxStyleFamily::Pseudo ) + { + SfxStyleSheet* pOldStyleSheet = mpView->GetStyleSheet(); + OUString aStr; + + if( // if the object had no style sheet, allow all + !pOldStyleSheet || + + // allow if old and new style sheet has same family + pStyleSheet->GetFamily() == pOldStyleSheet->GetFamily() || + + // allow if old was background objects and new is graphics + (pStyleSheet->GetFamily() == SfxStyleFamily::Para && pOldStyleSheet->GetHelpId( aStr ) == HID_PSEUDOSHEET_BACKGROUNDOBJECTS) || + + // allow if old was presentation and we are a drawing document + (pOldStyleSheet->GetFamily() == SfxStyleFamily::Page && mpDoc->GetDocumentType() == DocumentType::Draw) ) + { + mpView->SetStyleSheet( static_cast<SfxStyleSheet*>(pStyleSheet)); + mpDoc->SetChanged(); + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_STYLE_FAMILY2 ); + } + } + break; + + case SID_STYLE_WATERCAN: + { + if( !SD_MOD()->GetWaterCan() ) + { + if (pArgs && pArgs->GetItemState( nSId ) == SfxItemState::SET) + { + aStyleName = static_cast<const SfxStringItem &>( pArgs->Get( nSId ) ).GetValue(); + SD_MOD()->SetWaterCan( true ); + pStyleSheet = pSSPool->Find( aStyleName, nFamily); + } + // no presentation object templates, they are only allowed implicitly + if( pStyleSheet && pStyleSheet->GetFamily() != SfxStyleFamily::Pseudo ) + { + static_cast<SdStyleSheetPool*>( pSSPool )->SetActualStyleSheet( pStyleSheet ); + + // we switch explicitly into selection mode + mpViewShell->GetViewFrame()->GetDispatcher()->Execute( SID_OBJECT_SELECT, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD ); + + } + else + SD_MOD()->SetWaterCan( false ); + } + else + { + SD_MOD()->SetWaterCan( false ); + // we have to re-enable to tools-bar + mpViewShell->Invalidate(); + } + } + break; + + default: + break; + } + + switch( nSId ) + { + case SID_STYLE_NEW: + case SID_STYLE_EDIT: + { + PresentationObjects ePO = PresentationObjects::Outline_1; + + if( pStyleSheet ) + { + ScopedVclPtr<SfxAbstractTabDialog> pStdDlg; + ScopedVclPtr<SfxAbstractTabDialog> pPresDlg; + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + + SfxStyleFamily eFamily = pStyleSheet->GetFamily(); + + if (eFamily == SfxStyleFamily::Para) + { + pStdDlg.disposeAndReset(pFact ? pFact->CreateSdTabTemplateDlg(mpViewShell->GetFrameWeld(), mpDoc->GetDocSh(), *pStyleSheet, mpDoc, mpView) : nullptr); + } + else if (eFamily == SfxStyleFamily::Pseudo) + { + OUString aName(pStyleSheet->GetName()); + bool bBackground = false; + bool bOldDocInOtherLanguage = false; + + if (aName == SdResId(STR_PSEUDOSHEET_TITLE)) + { + ePO = PresentationObjects::Title; + } + else if (aName == SdResId(STR_PSEUDOSHEET_SUBTITLE)) + { + ePO = PresentationObjects::Subtitle; + } + else if (aName == + SdResId(STR_PSEUDOSHEET_BACKGROUND)) + { + bBackground = true; + ePO = PresentationObjects::Background; + } + else if (aName == + SdResId(STR_PSEUDOSHEET_BACKGROUNDOBJECTS)) + { + ePO = PresentationObjects::BackgroundObjects; + } + else if (aName == + SdResId(STR_PSEUDOSHEET_NOTES)) + { + ePO = PresentationObjects::Notes; + } + else if(aName.indexOf(SdResId(STR_PSEUDOSHEET_OUTLINE)) != -1) + { + OUString aOutlineStr(SdResId(STR_PSEUDOSHEET_OUTLINE)); + // determine number, mind the blank between name and number + std::u16string_view aNumStr(aName.subView(aOutlineStr.getLength() + 1)); + sal_uInt16 nLevel = static_cast<sal_uInt16>(o3tl::toInt32(aNumStr)); + switch (nLevel) + { + case 1: ePO = PresentationObjects::Outline_1; break; + case 2: ePO = PresentationObjects::Outline_2; break; + case 3: ePO = PresentationObjects::Outline_3; break; + case 4: ePO = PresentationObjects::Outline_4; break; + case 5: ePO = PresentationObjects::Outline_5; break; + case 6: ePO = PresentationObjects::Outline_6; break; + case 7: ePO = PresentationObjects::Outline_7; break; + case 8: ePO = PresentationObjects::Outline_8; break; + case 9: ePO = PresentationObjects::Outline_9; break; + } + } + else + { + OSL_FAIL("StyleSheet from older version with different language"); + bOldDocInOtherLanguage = true; + } + + if( !bOldDocInOtherLanguage ) + { + pPresDlg.disposeAndReset(pFact ? pFact->CreateSdPresLayoutTemplateDlg(mpDocSh, mpViewShell->GetFrameWeld(), bBackground, *pStyleSheet, ePO, pSSPool ) : nullptr); + } + } + + sal_uInt16 nResult = RET_CANCEL; + const SfxItemSet* pOutSet = nullptr; + if (pStdDlg) + { + nResult = pStdDlg->Execute(); + pOutSet = pStdDlg->GetOutputItemSet(); + } + else if( pPresDlg ) + { + nResult = pPresDlg->Execute(); + pOutSet = pPresDlg->GetOutputItemSet(); + } + + switch( nResult ) + { + case RET_OK: + { + nRetMask = static_cast<sal_uInt16>(pStyleSheet->GetMask()); + + if (eFamily == SfxStyleFamily::Pseudo) + { + SfxItemSet aTempSet(*pOutSet); + /* Extract SvxBrushItem out of set and insert SvxColorItem */ + const SvxBrushItem* pBrushItem = aTempSet.GetItem<SvxBrushItem>( SID_ATTR_BRUSH_CHAR ); + + if ( pBrushItem ) + { + SvxColorItem aBackColorItem(pBrushItem->GetColor(), EE_CHAR_BKGCOLOR); + aTempSet.ClearItem( EE_CHAR_BKGCOLOR ); + aTempSet.Put( aBackColorItem ); + } + static_cast<SdStyleSheet*>(pStyleSheet)->AdjustToFontHeight(aTempSet); + + /* Special treatment: reset the INVALIDS to + NULL-Pointer (otherwise INVALIDs or pointer point + to DefaultItems in the template; both would + prevent the attribute inheritance) */ + aTempSet.ClearInvalidItems(); + + // EE_PARA_NUMBULLET item is only valid in first outline template + if( (ePO >= PresentationObjects::Outline_2) && (ePO <= PresentationObjects::Outline_9) ) + { + if (aTempSet.GetItemState(EE_PARA_NUMBULLET) == SfxItemState::SET) + { + SvxNumRule aRule(aTempSet.GetItem<SvxNumBulletItem>(EE_PARA_NUMBULLET)->GetNumRule()); + + OUString sStyleName(SdResId(STR_PSEUDOSHEET_OUTLINE) + " 1"); + SfxStyleSheetBase* pFirstStyleSheet = pSSPool->Find( sStyleName, SfxStyleFamily::Pseudo); + + if(pFirstStyleSheet) + { + pFirstStyleSheet->GetItemSet().Put( SvxNumBulletItem( aRule, EE_PARA_NUMBULLET )); + SdStyleSheet* pRealSheet = static_cast<SdStyleSheet*>(pFirstStyleSheet)->GetRealStyleSheet(); + pRealSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); + } + + aTempSet.ClearItem( EE_PARA_NUMBULLET ); + } + } + + pStyleSheet->GetItemSet().Put(aTempSet); + SdStyleSheet::BroadcastSdStyleSheetChange(pStyleSheet, ePO, pSSPool); + } + + SfxItemSet& rAttr = pStyleSheet->GetItemSet(); + + sdr::properties::CleanupFillProperties( rAttr ); + + // check for unique names of named items for xml + if( rAttr.GetItemState( XATTR_FILLBITMAP ) == SfxItemState::SET ) + { + const SfxPoolItem* pOldItem = rAttr.GetItem( XATTR_FILLBITMAP ); + std::unique_ptr<SfxPoolItem> pNewItem = static_cast<const XFillBitmapItem*>(pOldItem)->checkForUniqueItem( mpDoc ); + if( pNewItem ) + { + rAttr.Put( std::move(pNewItem) ); + } + } + if( rAttr.GetItemState( XATTR_LINEDASH ) == SfxItemState::SET ) + { + const SfxPoolItem* pOldItem = rAttr.GetItem( XATTR_LINEDASH ); + std::unique_ptr<SfxPoolItem> pNewItem = static_cast<const XLineDashItem*>(pOldItem)->checkForUniqueItem( mpDoc ); + if( pNewItem ) + { + rAttr.Put( std::move(pNewItem) ); + } + } + if( rAttr.GetItemState( XATTR_LINESTART ) == SfxItemState::SET ) + { + const SfxPoolItem* pOldItem = rAttr.GetItem( XATTR_LINESTART ); + std::unique_ptr<SfxPoolItem> pNewItem = static_cast<const XLineStartItem*>(pOldItem)->checkForUniqueItem( mpDoc ); + if( pNewItem ) + { + rAttr.Put( std::move(pNewItem) ); + } + } + if( rAttr.GetItemState( XATTR_LINEEND ) == SfxItemState::SET ) + { + const SfxPoolItem* pOldItem = rAttr.GetItem( XATTR_LINEEND ); + std::unique_ptr<SfxPoolItem> pNewItem = static_cast<const XLineEndItem*>(pOldItem)->checkForUniqueItem( mpDoc ); + if( pNewItem ) + { + rAttr.Put( std::move(pNewItem) ); + } + } + if( rAttr.GetItemState( XATTR_FILLGRADIENT ) == SfxItemState::SET ) + { + const SfxPoolItem* pOldItem = rAttr.GetItem( XATTR_FILLGRADIENT ); + std::unique_ptr<SfxPoolItem> pNewItem = static_cast<const XFillGradientItem*>(pOldItem)->checkForUniqueItem( mpDoc ); + if( pNewItem ) + { + rAttr.Put( std::move(pNewItem) ); + } + } + if( rAttr.GetItemState( XATTR_FILLFLOATTRANSPARENCE ) == SfxItemState::SET ) + { + const SfxPoolItem* pOldItem = rAttr.GetItem( XATTR_FILLFLOATTRANSPARENCE ); + std::unique_ptr<SfxPoolItem> pNewItem = static_cast<const XFillFloatTransparenceItem*>(pOldItem)->checkForUniqueItem( mpDoc ); + if( pNewItem ) + { + rAttr.Put( std::move(pNewItem) ); + } + } + if( rAttr.GetItemState( XATTR_FILLHATCH ) == SfxItemState::SET ) + { + const SfxPoolItem* pOldItem = rAttr.GetItem( XATTR_FILLHATCH ); + std::unique_ptr<SfxPoolItem> pNewItem = static_cast<const XFillHatchItem*>(pOldItem)->checkForUniqueItem( mpDoc ); + if( pNewItem ) + { + rAttr.Put( std::move(pNewItem) ); + } + } + + static_cast<SfxStyleSheet*>( pStyleSheet )->Broadcast( SfxHint( SfxHintId::DataChanged ) ); + + DrawViewShell* pDrawViewShell = dynamic_cast< DrawViewShell* >( mpViewShell ); + if( pDrawViewShell ) + { + PageKind ePageKind = pDrawViewShell->GetPageKind(); + if( ePageKind == PageKind::Notes || ePageKind == PageKind::Handout ) + { + SdPage* pPage = mpViewShell->GetActualPage(); + + if(pDrawViewShell->GetEditMode() == EditMode::MasterPage) + { + pPage = static_cast<SdPage*>((&(pPage->TRG_GetMasterPage()))); + } + + if( pPage ) + { + SdrObjListIter aIter( pPage ); + while( aIter.IsMore() ) + { + SdrObject* pObj = aIter.Next(); + if( dynamic_cast< const SdrPageObj *>( pObj ) != nullptr ) + { + // repaint only + pObj->ActionChanged(); + // pObj->SendRepaintBroadcast(); + } + } + } + } + } + + if( mpDoc->GetOnlineSpell() ) + { + if( SfxItemState::SET == rAttr.GetItemState(EE_CHAR_LANGUAGE, false ) || + SfxItemState::SET == rAttr.GetItemState(EE_CHAR_LANGUAGE_CJK, false ) || + SfxItemState::SET == rAttr.GetItemState(EE_CHAR_LANGUAGE_CTL, false ) ) + { + mpDoc->StopOnlineSpelling(); + mpDoc->StartOnlineSpelling(); + } + } + + mpDoc->SetChanged(); + } + break; + + default: + { + if( nSId == SID_STYLE_NEW ) + pSSPool->Remove( pStyleSheet ); + } + return; // Cancel + } + } + } + break; + + case SID_STYLE_NEW_BY_EXAMPLE: + { + if( pStyleSheet ) + { + nRetMask = static_cast<sal_uInt16>(pStyleSheet->GetMask()); + SfxItemSet aCoreSet( mpDoc->GetPool() ); + mpView->GetAttributes( aCoreSet, true ); + + // if the object had a template, this becomes parent of the new template + SfxStyleSheet* pOldStyle = mpView->GetStyleSheet(); + + // if pOldStyle == pStyleSheet -> recursion + if( pOldStyle != pStyleSheet ) + { + if (pOldStyle) + { + pStyleSheet->SetParent(pOldStyle->GetName()); + } + + SfxItemSet* pStyleSet = &pStyleSheet->GetItemSet(); + pStyleSet->Put(aCoreSet); + + /* apply template (but not when somebody is editing a text. + To do this, the edit engine had to be capable to use + templates on a character level. */ + if (!mpView->GetTextEditObject()) + { + mpView->SetStyleSheet( static_cast<SfxStyleSheet*>(pStyleSheet)); + } + + static_cast<SfxStyleSheet*>( pStyleSheet )->Broadcast( SfxHint( SfxHintId::DataChanged ) ); + mpDoc->SetChanged(); + + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_STYLE_FAMILY2 ); + } + } + } + break; + + case SID_STYLE_UPDATE_BY_EXAMPLE: + { + if ((mpView->AreObjectsMarked() && mpView->GetMarkedObjectList().GetMarkCount() == 1) || + dynamic_cast< const OutlineView *>( mpView ) != nullptr) + { + pStyleSheet = mpView->GetStyleSheet(); + + if( pStyleSheet ) + { + nRetMask = static_cast<sal_uInt16>(pStyleSheet->GetMask()); + SfxItemSet aCoreSet( mpDoc->GetPool() ); + mpView->GetAttributes( aCoreSet ); + + SfxItemSet* pStyleSet = &pStyleSheet->GetItemSet(); + pStyleSet->Put( aCoreSet ); + + mpView->SetStyleSheet( static_cast<SfxStyleSheet*>(pStyleSheet)); + + static_cast<SfxStyleSheet*>( pStyleSheet )->Broadcast( SfxHint( SfxHintId::DataChanged ) ); + mpDoc->SetChanged(); + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_STYLE_FAMILY2 ); + } + } + } + break; + + } + if( nRetMask != static_cast<sal_uInt16>(SfxStyleSearchBits::All) ) + rReq.SetReturnValue( SfxUInt16Item( nSId, nRetMask ) ); +} + +void FuTemplate::Activate() +{ +} + +void FuTemplate::Deactivate() +{ +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/futext.cxx b/sd/source/ui/func/futext.cxx new file mode 100644 index 000000000..725bf96e1 --- /dev/null +++ b/sd/source/ui/func/futext.cxx @@ -0,0 +1,1464 @@ +/* -*- 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 <futext.hxx> +#include <editeng/eeitem.hxx> +#include <svx/sdrpagewindow.hxx> +#include <svx/sdrpaintwindow.hxx> +#include <tools/urlobj.hxx> +#include <vcl/help.hxx> +#include <editeng/fhgtitem.hxx> +#include <svl/intitem.hxx> +#include <svl/stritem.hxx> +#include <svx/svdotext.hxx> +#include <editeng/flditem.hxx> +#include <svl/style.hxx> +#include <svx/svdpagv.hxx> +#include <svx/sdtmfitm.hxx> +#include <svx/sdtagitm.hxx> +#include <svx/sdtfsitm.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/request.hxx> +#include <editeng/editeng.hxx> +#include <svx/svdoutl.hxx> +#include <svx/svxids.hrc> +#include <svx/sdr/overlay/overlaymanager.hxx> +#include <sfx2/docfile.hxx> +#include <editeng/outlobj.hxx> +#include <osl/diagnose.h> + +#include <editeng/frmdiritem.hxx> + +#include <svx/svdetc.hxx> +#include <editeng/editview.hxx> + +#include <sdresid.hxx> +#include <app.hrc> + +#include <ViewShell.hxx> +#include <ViewShellBase.hxx> +#include <View.hxx> +#include <Window.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <FrameView.hxx> +#include <ToolBarManager.hxx> +#include <DrawDocShell.hxx> +#include <strings.hrc> +#include <pres.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::linguistic2; + +namespace sd { + +const sal_uInt16 SidArray[] = { + SID_STYLE_FAMILY2, // 5542 + SID_STYLE_FAMILY5, // 5545 + SID_REDO, // 5700 + SID_UNDO, // 5701 + SID_CUT, // 5710 + SID_COPY, // 5711 + SID_ATTR_TABSTOP, // 10002 + SID_ATTR_CHAR_FONT, // 10007 + SID_ATTR_CHAR_POSTURE, // 10008 + SID_ATTR_CHAR_WEIGHT, // 10009 + SID_ATTR_CHAR_SHADOWED, // 10010 + SID_ATTR_CHAR_STRIKEOUT, // 10013 + SID_ATTR_CHAR_UNDERLINE, // 10014 + SID_ATTR_CHAR_FONTHEIGHT, // 10015 + SID_ATTR_CHAR_COLOR, // 10017 + SID_ATTR_CHAR_KERNING, // 10018 + SID_ATTR_CHAR_CASEMAP, // 10019 + SID_ATTR_PARA_ADJUST_LEFT, // 10028 + SID_ATTR_PARA_ADJUST_RIGHT, // 10029 + SID_ATTR_PARA_ADJUST_CENTER, // 10030 + SID_ATTR_PARA_ADJUST_BLOCK, // 10031 + SID_ATTR_PARA_LINESPACE_10, // 10034 + SID_ATTR_PARA_LINESPACE_15, // 10035 + SID_ATTR_PARA_LINESPACE_20, // 10036 + SID_ATTR_PARA_ULSPACE, // 10042 + SID_ATTR_PARA_LRSPACE, // 10043 + SID_ATTR_TRANSFORM_POS_X, // 10088 + SID_ATTR_TRANSFORM_POS_Y, // 10089 + SID_ATTR_TRANSFORM_WIDTH, // 10090 + SID_ATTR_TRANSFORM_HEIGHT, // 10091 + SID_ATTR_TRANSFORM_ROT_X, // 10093 + SID_ATTR_TRANSFORM_ROT_Y, // 10094 + SID_ATTR_TRANSFORM_ANGLE, // 10095 //Added + SID_OUTLINE_UP, // 10150 + SID_OUTLINE_DOWN, // 10151 + SID_OUTLINE_LEFT, // 10152 + SID_OUTLINE_RIGHT, // 10153 + SID_ATTR_TRANSFORM_PROTECT_POS, // 10236 + SID_ATTR_TRANSFORM_PROTECT_SIZE, // 10237 //Added + SID_FORMTEXT_STYLE, // 10257 + SID_SET_SUPER_SCRIPT, // 10294 + SID_SET_SUB_SCRIPT, // 10295 + SID_ATTR_TRANSFORM_AUTOWIDTH, // 10310 + SID_ATTR_TRANSFORM_AUTOHEIGHT, // 10311 //Added + SID_HYPERLINK_GETLINK, // 10361 + SID_DEC_INDENT, // 10461 + SID_INC_INDENT, // 10462 + SID_CHARMAP, // 10503 + SID_TEXTDIRECTION_LEFT_TO_RIGHT, // 10907 + SID_TEXTDIRECTION_TOP_TO_BOTTOM, // 10908 + SID_ATTR_PARA_LEFT_TO_RIGHT, // 10950 + SID_ATTR_PARA_RIGHT_TO_LEFT, // 10951 + SID_PARASPACE_INCREASE, // 11145 + SID_PARASPACE_DECREASE, // 11146 + FN_NUM_BULLET_ON, // 20138 + 0 }; + + +/** + * base class for text functions + */ +FuText::FuText( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +: FuConstruct(pViewSh, pWin, pView, pDoc, rReq) +, bFirstObjCreated(false) +, bJustEndedEdit(false) +, rRequest (rReq) +{ +} + +rtl::Reference<FuPoor> FuText::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuText( pViewSh, pWin, pView, pDoc, rReq ) ); + return xFunc; +} + +void FuText::disposing() +{ + if(mpView) + { + if(mpView->SdrEndTextEdit() == SdrEndTextEditKind::Deleted) + mxTextObj.reset(nullptr); + + // reset the RequestHandler of the used Outliner to the handler of the document + ::Outliner* pOutliner = mpView->GetTextEditOutliner(); + + if (pOutliner) + pOutliner->SetStyleSheetPool(static_cast<SfxStyleSheetPool*>(mpDoc->GetStyleSheetPool())); + } +} + +/************************************************************************* +|* +|* Execute functionality of this class: +|* +|* #71422: Start the functionality of this class in this method +|* and not in the ctor. +|* If you construct an object of this class and you put the +|* address of this object to pFuActual you've got a problem, +|* because some methods inside DoExecute use the pFuActual-Pointer. +|* If the code inside DoExecute is executed inside the ctor, +|* the value of pFuActual is not right. And the value will not +|* be right until the ctor finished !!! +|* +\************************************************************************/ +void FuText::DoExecute( SfxRequest& ) +{ + mpViewShell->GetViewShellBase().GetToolBarManager()->SetToolBarShell( + ToolBarManager::ToolBarGroup::Function, + ToolbarId::Draw_Text_Toolbox_Sd); + + mpView->SetCurrentObj(SdrObjKind::Text); + mpView->SetEditMode(SdrViewEditMode::Edit); + + MouseEvent aMEvt(mpWindow->GetPointerPosPixel()); + + if (nSlotId == SID_TEXTEDIT) + { + // Try to select an object + SdrPageView* pPV = mpView->GetSdrPageView(); + SdrViewEvent aVEvt; + mpView->PickAnything(aMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + mpView->MarkObj(aVEvt.mpRootObj, pPV); + + mxTextObj.reset( dynamic_cast< SdrTextObj* >( aVEvt.mpObj ) ); + } + else if (mpView->AreObjectsMarked()) + { + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + if (rMarkList.GetMarkCount() == 1) + { + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + mxTextObj.reset( dynamic_cast< SdrTextObj* >( pObj ) ); + } + } + + // check for table + if (mpView->AreObjectsMarked()) + { + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + if (rMarkList.GetMarkCount() == 1) + { + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + if( pObj && (pObj->GetObjInventor() == SdrInventor::Default ) && (pObj->GetObjIdentifier() == SdrObjKind::Table) ) + { + mpViewShell->GetViewShellBase().GetToolBarManager()->AddToolBarShell(ToolBarManager::ToolBarGroup::Function, ToolbarId::Draw_Table_Toolbox); + } + } + } + + bool bQuickDrag = true; + + const SfxItemSet* pArgs = rRequest.GetArgs(); + + if (pArgs + + // test for type before using + && SID_TEXTEDIT == nSlotId + && SfxItemState::SET == pArgs->GetItemState(SID_TEXTEDIT) + + && static_cast<const SfxUInt16Item&>(pArgs->Get(SID_TEXTEDIT)).GetValue() == 2) + { + // Selection by doubleclick -> don't allow QuickDrag + bQuickDrag = false; + } + + SetInEditMode(aMEvt, bQuickDrag); +} + +bool FuText::MouseButtonDown(const MouseEvent& rMEvt) +{ + bMBDown = true; + bJustEndedEdit = false; + + bool bReturn = FuDraw::MouseButtonDown(rMEvt); + + SdrViewEvent aVEvt; + SdrHitKind eHit = mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + + // handle URL also during the text editing + if (rMEvt.GetClicks() == 1 && rMEvt.IsLeft() && rMEvt.IsMod1()) + { + OutlinerView* pOLV = mpView->GetTextEditOutlinerView(); + + if (mxTextObj.is() && pOLV && pOLV->GetFieldUnderMousePointer()) + { + const SvxFieldItem* pFieldItem = pOLV->GetFieldUnderMousePointer(); + if (pFieldItem) + { + const SvxFieldData* pField = pFieldItem->GetField(); + + if (auto pURLField = dynamic_cast< const SvxURLField *>( pField )) + { + eHit = SdrHitKind::MarkedObject; + aVEvt.meEvent = SdrEventKind::ExecuteUrl; + aVEvt.mpURLField = pURLField; + } + } + } + } + + if (eHit == SdrHitKind::TextEdit) + { + // hit text -> SdrView handles event + if (mpView->MouseButtonDown(rMEvt, mpWindow->GetOutDev())) + return true; + } + + if (rMEvt.GetClicks() == 1) + { + if (mpView->IsTextEdit() && eHit != SdrHitKind::MarkedObject && eHit != SdrHitKind::Handle) + { + // finish text input + if(mpView->SdrEndTextEdit() == SdrEndTextEditKind::Deleted) + { + /* Bugfix from MBA: during a double click onto the unused? area + in text mode, we get with the second click eHit = + SdrHitKind::TextEditObj since it goes to the TextObject which was + created with the first click. But this is removed by + SdrEndTextEdit since it is empty. But it is still in the mark + list. The call MarkObj further below accesses then the dead + object. As a simple fix, we determine eHit after + SdrEndTextEdit again, this returns then SdrHitKind::NONE. */ + mxTextObj.reset(nullptr); + eHit = mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + } + + mpView->SetCurrentObj(SdrObjKind::Text); + mpView->SetEditMode(SdrViewEditMode::Edit); + } + + if (rMEvt.IsLeft() || rMEvt.IsRight()) + { + mpWindow->CaptureMouse(); + SdrPageView* pPV = mpView->GetSdrPageView(); + + if (eHit == SdrHitKind::TextEdit) + { + SetInEditMode(rMEvt, false); + } + else + { + // Don't remark table when clicking in it, mark change triggers a lot of updating + bool bMarkChanges = true; + rtl::Reference< sdr::SelectionController > xSelectionController(mpView->getSelectionController()); + if (eHit == SdrHitKind::TextEditObj && xSelectionController.is()) + { + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + if (rMarkList.GetMarkCount() == 1 && rMarkList.GetMark(0)->GetMarkedSdrObj() == aVEvt.mpRootObj) + bMarkChanges = false; + } + + if (eHit != SdrHitKind::Handle) + { + // deselect selection + if (!rMEvt.IsShift() && eHit == SdrHitKind::TextEditObj) + { + if(bMarkChanges) + { + mpView->UnmarkAll(); + mpView->SetDragMode(SdrDragMode::Move); + } + } + } + + if ( aVEvt.meEvent == SdrEventKind::ExecuteUrl || + eHit == SdrHitKind::Handle || + eHit == SdrHitKind::MarkedObject || + eHit == SdrHitKind::TextEditObj || + ( eHit == SdrHitKind::UnmarkedObject && bFirstObjCreated && + !bPermanent ) ) + { + // Handle, hit marked or unmarked object + if (eHit == SdrHitKind::TextEditObj) + { + /* hit text of unmarked object: + select object and set to EditMode */ + if (bMarkChanges) + mpView->MarkObj(aVEvt.mpRootObj, pPV); + + if (auto pSdrTextObj = dynamic_cast<SdrTextObj*>(aVEvt.mpObj)) + { + mxTextObj.reset( pSdrTextObj ); + } + + SetInEditMode(rMEvt, true); + } + else if (aVEvt.meEvent == SdrEventKind::ExecuteUrl && !rMEvt.IsMod2()) + { + // execute URL + mpWindow->ReleaseMouse(); + SfxStringItem aStrItem(SID_FILE_NAME, aVEvt.mpURLField->GetURL()); + SfxStringItem aReferer(SID_REFERER, mpDocSh->GetMedium()->GetName()); + SfxBoolItem aBrowseItem( SID_BROWSE, true ); + SfxViewFrame* pFrame = mpViewShell->GetViewFrame(); + mpWindow->ReleaseMouse(); + + if (rMEvt.IsMod1()) + { + // open in new frame + pFrame->GetDispatcher()->ExecuteList(SID_OPENDOC, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aStrItem, &aBrowseItem, &aReferer }); + } + else + { + // open in current frame + SfxFrameItem aFrameItem(SID_DOCFRAME, pFrame); + pFrame->GetDispatcher()->ExecuteList(SID_OPENDOC, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aStrItem, &aFrameItem, &aBrowseItem, &aReferer }); + } + } + else + { + // drag object or handle + + // #i78748# + // do the EndTextEdit first, it will delete the handles and force a + // recreation. This will make aVEvt.mpHdl to point to a deleted handle, + // thus it is necessary to reset it and to get it again. + + // #i112855# + // cl: I'm not sure why we checked here also for mxTextObj->GetOutlinerParaObject + // this caused SdrEndTextEdit() to be called also when not in text editing and + // this does not make sense and caused troubles. (see issue 112855) + + if( mpView->IsTextEdit() ) + { + mpView->SdrEndTextEdit(); + bJustEndedEdit = true; + + if(aVEvt.mpHdl) + { + // force new handle identification, the pointer will be dead here + // since SdrEndTextEdit has reset (deleted) the handles. + aVEvt.mpHdl = nullptr; + mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + } + } + + if (!aVEvt.mpHdl) + { + if( eHit == SdrHitKind::UnmarkedObject ) + { + if ( !rMEvt.IsShift() ) + mpView->UnmarkAll(); + + mpView->MarkObj(aVEvt.mpRootObj, pPV); + } + + // Drag object + bFirstMouseMove = true; + aDragTimer.Start(); + } + + if ( ! rMEvt.IsRight()) + { + // we need to pick again since SdrEndTextEdit can rebuild the handles list + eHit = mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + if( (eHit == SdrHitKind::Handle) || (eHit == SdrHitKind::MarkedObject) ) + { + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + mpView->BegDragObj(aMDPos, nullptr, aVEvt.mpHdl, nDrgLog); + } + } + bReturn = true; + } + } + else if ( nSlotId != SID_TEXTEDIT && + (bPermanent || !bFirstObjCreated) ) + { + // create object + mpView->SetCurrentObj(SdrObjKind::Text); + mpView->SetEditMode(SdrViewEditMode::Create); + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + mpView->BegCreateObj(aMDPos, nullptr, nDrgLog); + } + else + { + // select + if( !rMEvt.IsShift() ) + mpView->UnmarkAll(); + + mpView->BegMarkObj( aMDPos ); + } + } + } + } + else if ( rMEvt.GetClicks() == 2 && !mpView->IsTextEdit() ) + { + MouseEvent aMEvt( mpWindow->GetPointerPosPixel() ); + SetInEditMode( aMEvt, false ); + } + + if (!bIsInDragMode) + { + ForcePointer(&rMEvt); + mpViewShell->GetViewFrame()->GetBindings().Invalidate(SidArray); + } + + return bReturn; +} + +bool FuText::MouseMove(const MouseEvent& rMEvt) +{ + bool bReturn = FuDraw::MouseMove(rMEvt); + + if (aDragTimer.IsActive() ) + { + if( bFirstMouseMove ) + bFirstMouseMove = false; + else + aDragTimer.Stop(); + } + + if (!bReturn && mpView->IsAction() && !mpDocSh->IsReadOnly()) + { + Point aPix(rMEvt.GetPosPixel()); + Point aPnt(mpWindow->PixelToLogic(aPix)); + + ForceScroll(aPix); + mpView->MovAction(aPnt); + } + + ForcePointer(&rMEvt); + + return bReturn; +} + +void FuText::ImpSetAttributesForNewTextObject(SdrTextObj* pTxtObj) +{ + if(mpDoc->GetDocumentType() == DocumentType::Impress) + { + if( nSlotId == SID_ATTR_CHAR ) + { + /* Create Impress text object (rescales to line height) + We get the correct height during the subsequent creation of the + object, otherwise we draw too much */ + SfxItemSet aSet(mpViewShell->GetPool()); + aSet.Put(makeSdrTextMinFrameHeightItem(0)); + aSet.Put(makeSdrTextAutoGrowWidthItem(false)); + aSet.Put(makeSdrTextAutoGrowHeightItem(true)); + pTxtObj->SetMergedItemSet(aSet); + pTxtObj->AdjustTextFrameWidthAndHeight(); + aSet.Put(makeSdrTextMaxFrameHeightItem(pTxtObj->GetLogicRect().GetSize().Height())); + pTxtObj->SetMergedItemSet(aSet); + const SfxViewShell* pCurrentViewShell = SfxViewShell::Current(); + if (pCurrentViewShell && (pCurrentViewShell->isLOKMobilePhone() || pCurrentViewShell->isLOKTablet())) + pTxtObj->SetText(SdResId(STR_PRESOBJ_TEXT_EDIT_MOBILE)); + } + else if( nSlotId == SID_ATTR_CHAR_VERTICAL ) + { + SfxItemSet aSet(mpViewShell->GetPool()); + aSet.Put(makeSdrTextMinFrameWidthItem(0)); + aSet.Put(makeSdrTextAutoGrowWidthItem(true)); + aSet.Put(makeSdrTextAutoGrowHeightItem(false)); + + // Needs to be set since default is SDRTEXTHORZADJUST_BLOCK + aSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT)); + pTxtObj->SetMergedItemSet(aSet); + pTxtObj->AdjustTextFrameWidthAndHeight(); + aSet.Put(makeSdrTextMaxFrameWidthItem(pTxtObj->GetLogicRect().GetSize().Width())); + pTxtObj->SetMergedItemSet(aSet); + } + } + else + { + if( nSlotId == SID_ATTR_CHAR_VERTICAL ) + { + // draw text object, needs to be initialized when vertical text is used + SfxItemSet aSet(mpViewShell->GetPool()); + + aSet.Put(makeSdrTextAutoGrowWidthItem(true)); + aSet.Put(makeSdrTextAutoGrowHeightItem(false)); + + // Set defaults for vertical click-n'drag text object, pool defaults are: + // SdrTextVertAdjustItem: SDRTEXTVERTADJUST_TOP + // SdrTextHorzAdjustItem: SDRTEXTHORZADJUST_BLOCK + // Analog to that: + aSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BLOCK)); + aSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT)); + + pTxtObj->SetMergedItemSet(aSet); + } + } +} + +void FuText::ImpSetAttributesFitToSize(SdrTextObj* pTxtObj) +{ + // FitToSize (fit to frame) + SfxItemSetFixed<SDRATTR_TEXT_AUTOGROWHEIGHT, SDRATTR_TEXT_AUTOGROWWIDTH> aSet(mpViewShell->GetPool()); + aSet.Put(SdrTextFitToSizeTypeItem(drawing::TextFitToSizeType_PROPORTIONAL)); + aSet.Put(makeSdrTextAutoGrowHeightItem(false)); + aSet.Put(makeSdrTextAutoGrowWidthItem(false)); + pTxtObj->SetMergedItemSet(aSet); + pTxtObj->AdjustTextFrameWidthAndHeight(); +} + +void FuText::ImpSetAttributesFitToSizeVertical(SdrTextObj* pTxtObj) +{ + SfxItemSetFixed<SDRATTR_TEXT_AUTOGROWHEIGHT, SDRATTR_TEXT_AUTOGROWWIDTH> aSet(mpViewShell->GetPool()); + aSet.Put(SdrTextFitToSizeTypeItem(drawing::TextFitToSizeType_PROPORTIONAL)); + aSet.Put(makeSdrTextAutoGrowHeightItem(false)); + aSet.Put(makeSdrTextAutoGrowWidthItem(false)); + pTxtObj->SetMergedItemSet(aSet); + pTxtObj->AdjustTextFrameWidthAndHeight(); +} + +void FuText::ImpSetAttributesFitCommon(SdrTextObj* pTxtObj) +{ + // Normal Textobject + if (mpDoc->GetDocumentType() != DocumentType::Impress) + return; + + if( nSlotId == SID_ATTR_CHAR ) + { + // Impress text object (rescales to line height) + SfxItemSet aSet(mpViewShell->GetPool()); + aSet.Put(makeSdrTextMinFrameHeightItem(0)); + aSet.Put(makeSdrTextMaxFrameHeightItem(0)); + aSet.Put(makeSdrTextAutoGrowHeightItem(true)); + aSet.Put(makeSdrTextAutoGrowWidthItem(false)); + pTxtObj->SetMergedItemSet(aSet); + } + else if( nSlotId == SID_ATTR_CHAR_VERTICAL ) + { + SfxItemSet aSet(mpViewShell->GetPool()); + aSet.Put(makeSdrTextMinFrameWidthItem(0)); + aSet.Put(makeSdrTextMaxFrameWidthItem(0)); + aSet.Put(makeSdrTextAutoGrowWidthItem(true)); + aSet.Put(makeSdrTextAutoGrowHeightItem(false)); + pTxtObj->SetMergedItemSet(aSet); + } + + pTxtObj->AdjustTextFrameWidthAndHeight(); +} + +bool FuText::MouseButtonUp(const MouseEvent& rMEvt) +{ + bool bReturn = false; + if (aDragTimer.IsActive()) + { + aDragTimer.Stop(); + bIsInDragMode = false; + } + + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SidArray ); + + Point aPnt( mpWindow->PixelToLogic( rMEvt.GetPosPixel() ) ); + + if( (mpView && mpView->MouseButtonUp(rMEvt, mpWindow->GetOutDev())) || rMEvt.GetClicks() == 2 ) + return true; // handle event from SdrView + + bool bEmptyTextObj = false; + + if (mxTextObj.is()) + { + bool bReset = true; + + if (mpView) + { + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + if (rMarkList.GetMarkCount() == 1 + && ( rMarkList.GetMark(0)->GetMarkedSdrObj() == mxTextObj.get()) ) + { + if( mxTextObj.is() && !GetTextObj()->GetOutlinerParaObject() ) + bEmptyTextObj = true; + else + bFirstObjCreated = true; + bReset = false; + } + } + + if (bReset) + { + mxTextObj.reset(nullptr); + } + } + + if( mpView && mpView->IsDragObj()) + { + // object was moved + FrameView* pFrameView = mpViewShell->GetFrameView(); + bool bDragWithCopy = (rMEvt.IsMod1() && pFrameView->IsDragWithCopy()); + + if (bDragWithCopy) + { + bDragWithCopy = !mpView->IsPresObjSelected(false); + } + + mpView->SetDragWithCopy(bDragWithCopy); + mpView->EndDragObj( mpView->IsDragWithCopy() ); + mpView->ForceMarkedToAnotherPage(); + mpView->SetCurrentObj(SdrObjKind::Text); + + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + + if (bJustEndedEdit) + { + bJustEndedEdit = false; + FuPoor::cancel(); + } + if ((rMEvt.GetClicks() != 2) && + !rMEvt.IsShift() && !rMEvt.IsMod1() && !rMEvt.IsMod2() && !rMEvt.IsRight() && + std::abs(aPnt.X() - aMDPos.X()) < nDrgLog && + std::abs(aPnt.Y() - aMDPos.Y()) < nDrgLog) + { + /************************************************************* + * From text mode, you don't want to rotate immediately. + **************************************************************/ + SdrPageView* pPV; + SdrObject* pObj = mpView->PickObj(aMDPos, mpView->getHitTolLog(), pPV, SdrSearchOptions::ALSOONMASTER | SdrSearchOptions::BEFOREMARK); + if (pObj && pPV->IsObjMarkable(pObj)) + { + mpView->UnmarkAllObj(); + mpView->MarkObj(pObj,pPV); + return bReturn; + } + } + } + else if( mpView && mpView->IsCreateObj() && rMEvt.IsLeft()) + { + // object was created + mxTextObj.reset( dynamic_cast< SdrTextObj* >( mpView->GetCreateObj() ) ); + + if( mxTextObj.is() ) + { + //AW outliner needs to be set to vertical when there is no + // outliner object up to now; also it needs to be set back to not + // vertical when there was a vertical one used last time. + OutlinerParaObject* pOPO = GetTextObj()->GetOutlinerParaObject(); + SdrOutliner& rOutl(mxTextObj->getSdrModelFromSdrObject().GetDrawOutliner(GetTextObj())); + bool bVertical((pOPO && pOPO->IsEffectivelyVertical()) + || nSlotId == SID_ATTR_CHAR_VERTICAL + || nSlotId == SID_TEXT_FITTOSIZE_VERTICAL); + rOutl.SetVertical(bVertical); + + // Before ImpSetAttributesForNewTextObject the vertical writing mode + // needs to be set at the object. This is done here at the OutlinerParaObject + // directly to not mirror the layout text items involved. These items will be set + // from ImpSetAttributesForNewTextObject and below. + OutlinerParaObject* pPara = GetTextObj()->GetOutlinerParaObject(); + + if(!pPara) + { + GetTextObj()->ForceOutlinerParaObject(); + pPara = GetTextObj()->GetOutlinerParaObject(); + } + + if(pPara && bVertical != pPara->IsEffectivelyVertical()) + { + // set ParaObject orientation accordingly + pPara->SetVertical(bVertical); + } + + ImpSetAttributesForNewTextObject(GetTextObj()); + } + + if (!mpView->EndCreateObj(SdrCreateCmd::ForceEnd)) + { + // it was not possible to create text object + mxTextObj.reset(nullptr); + } + else if (nSlotId == SID_TEXT_FITTOSIZE) + { + ImpSetAttributesFitToSize(GetTextObj()); + + SetInEditMode(rMEvt, false); + } + else if ( nSlotId == SID_TEXT_FITTOSIZE_VERTICAL ) + { + ImpSetAttributesFitToSizeVertical(GetTextObj()); + + SetInEditMode(rMEvt, false); + } + else + { + ImpSetAttributesFitCommon(GetTextObj()); + + // thereby the handles and the gray frame are correct + mpView->AdjustMarkHdl(); + mpView->PickHandle(aPnt); + SetInEditMode(rMEvt, false); + } + } + else if ( mpView && mpView->IsAction()) + { + mpView->EndAction(); + } + + ForcePointer(&rMEvt); + mpWindow->ReleaseMouse(); + sal_uInt16 nDrgLog1 = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + + if ( mpView && !mpView->AreObjectsMarked() && + std::abs(aMDPos.X() - aPnt.X()) < nDrgLog1 && + std::abs(aMDPos.Y() - aPnt.Y()) < nDrgLog1 && + !rMEvt.IsShift() && !rMEvt.IsMod2() ) + { + SdrPageView* pPV2 = mpView->GetSdrPageView(); + SdrViewEvent aVEvt; + mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + mpView->MarkObj(aVEvt.mpRootObj, pPV2); + } + + if ( !mxTextObj.is() && mpView ) + { + if ( ( (!bEmptyTextObj && bPermanent) || + (!bFirstObjCreated && !bPermanent) ) && + !mpDocSh->IsReadOnly() && + nSlotId != SID_TEXTEDIT ) + { + // text body (left-justified AutoGrow) + mpView->SetCurrentObj(SdrObjKind::Text); + mpView->SetEditMode(SdrViewEditMode::Create); + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + mpView->BegCreateObj(aMDPos, nullptr, nDrgLog); + + bool bSnapEnabled = mpView->IsSnapEnabled(); + + if (bSnapEnabled) + mpView->SetSnapEnabled(false); + + aPnt.AdjustX(nDrgLog + nDrgLog ); + aPnt.AdjustY(nDrgLog + nDrgLog ); + mpView->MovAction(aPnt); + + mxTextObj.reset( dynamic_cast< SdrTextObj* >( mpView->GetCreateObj() ) ); + + if(mxTextObj.is()) + { + GetTextObj()->SetDisableAutoWidthOnDragging(true); + } + + if(!mpView->EndCreateObj(SdrCreateCmd::ForceEnd)) + { + mxTextObj.reset(nullptr); + } + + if(bSnapEnabled) + mpView->SetSnapEnabled(bSnapEnabled); + + if(mxTextObj.is()) + { + SfxItemSet aSet(mpViewShell->GetPool()); + aSet.Put(makeSdrTextMinFrameHeightItem(0)); + aSet.Put(makeSdrTextMinFrameWidthItem(0)); + aSet.Put(makeSdrTextAutoGrowHeightItem(true)); + aSet.Put(makeSdrTextAutoGrowWidthItem(true)); + + if(nSlotId == SID_ATTR_CHAR_VERTICAL) + { + // Here, all items which need to be different from pool default need to be set + // again on the newly created text object. + // Since this is a simple click text object, it is first created, then SetVertical() + // is used, then ImpSetAttributesForNewTextObject is called and then the object is + // deleted again since not the minimum drag distance was travelled. Then, a new + // click text object is created and thus all that stuff needs to be set again here. + + // Before using the new object the vertical writing mode + // needs to be set. This is done here at the OutlinerParaObject + // directly to not mirror the layout text items involved. These items will be set + // below. + OutlinerParaObject* pPara = GetTextObj()->GetOutlinerParaObject(); + + if(!pPara) + { + GetTextObj()->ForceOutlinerParaObject(); + pPara = GetTextObj()->GetOutlinerParaObject(); + } + + if(pPara && !pPara->IsEffectivelyVertical()) + { + // set ParaObject orientation accordingly + pPara->SetVertical(true); + } + + aSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT)); + + // Analog to the else case below, for vertical simple click texts + // one of the default set items from ImpSetAttributesForNewTextObject + // needs to be adapted to non-block mode. + const SfxItemSet& rSet = mpView->GetDefaultAttr(); + SvxFrameDirection eDirection = rSet.Get(EE_PARA_WRITINGDIR).GetValue(); + + if(SvxFrameDirection::Horizontal_RL_TB == eDirection || SvxFrameDirection::Vertical_RL_TB == eDirection) + { + aSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BOTTOM)); + } + else + { + aSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_TOP)); + } + } + else + { + // This is for Format/Page settings. Since this also leads + // to the object defaults to be changed, i think this code can be + // removed. CL. wanted to take a look before adding this. + + // Look in the object defaults if left-to-right is wanted. If + // yes, set text anchoring to right to let the box grow to left. + const SfxItemSet& rSet = mpView->GetDefaultAttr(); + SvxFrameDirection eDirection = rSet.Get(EE_PARA_WRITINGDIR).GetValue(); + + if(SvxFrameDirection::Horizontal_RL_TB == eDirection) + { + aSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT)); + } + else + { + aSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_LEFT)); + } + } + + GetTextObj()->SetMergedItemSet(aSet); + GetTextObj()->SetDisableAutoWidthOnDragging(true); + SetInEditMode(rMEvt, false); + } + + bFirstObjCreated = true; + } + else + { + // switch to selection + if (mpView->SdrEndTextEdit() == SdrEndTextEditKind::Deleted) + { + mxTextObj.reset(nullptr); + } + + mpViewShell->GetViewFrame()->GetDispatcher()->Execute( SID_OBJECT_SELECT, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD ); + } + } + if (bJustEndedEdit) + { + bJustEndedEdit = false; + FuPoor::cancel(); + } + bMBDown = false; + FuConstruct::MouseButtonUp(rMEvt); + return bReturn; +} + +/** + * handle keyboard events + * @returns sal_True if the event was handled, sal_False otherwise + */ +bool FuText::KeyInput(const KeyEvent& rKEvt) +{ + bool bReturn = false; + + vcl::KeyCode nCode = rKEvt.GetKeyCode(); + bool bShift = nCode.IsShift(); + + if(mxTextObj.is()) + { + // maybe object is deleted, test if it's equal to the selected object + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + SdrObject* pSelectedObj = nullptr; + + if(1 == rMarkList.GetMarkCount()) + { + SdrMark* pMark = rMarkList.GetMark(0); + pSelectedObj = pMark->GetMarkedSdrObj(); + } + + if(mxTextObj.get() != pSelectedObj) + { + mxTextObj.reset(nullptr); + } + } + + if ( mxTextObj.is() && mxTextObj->GetObjInventor() == SdrInventor::Default && mxTextObj->GetObjIdentifier() == SdrObjKind::TitleText && rKEvt.GetKeyCode().GetCode() == KEY_RETURN ) + { + // title text object: always soft breaks + bShift = true; + } + + sal_uInt16 nKey = nCode.GetCode(); + vcl::KeyCode aKeyCode (nKey, bShift, nCode.IsMod1(), nCode.IsMod2(), nCode.IsMod3() ); + KeyEvent aKEvt(rKEvt.GetCharCode(), aKeyCode); + + bool bOK = true; + + if (mpDocSh->IsReadOnly()) + { + bOK = !EditEngine::DoesKeyChangeText(aKEvt); + } + if( aKeyCode.GetCode() == KEY_PAGEUP || aKeyCode.GetCode() == KEY_PAGEDOWN ) + { + bOK = false; // default handling in base class + } + + if (bOK && mpView->KeyInput(aKEvt, mpWindow) ) + { + bReturn = true; + + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SidArray ); + + } + else if (aKeyCode == KEY_ESCAPE) + { + bReturn = cancel(); + } + + if( bPermanent ) + { + mpView->SetCurrentObj(SdrObjKind::Text); + mpView->SetEditMode(SdrViewEditMode::Create); + } + + if (!bReturn) + { + bReturn = FuDraw::KeyInput(aKEvt); + } + + return bReturn; +} + +void FuText::Activate() +{ + mpView->SetQuickTextEditMode(mpViewShell->GetFrameView()->IsQuickEdit()); + + // #i89661# it's no longer necessary to make it so big here, it's fine tuned + // for text objects in SdrMarkView::CheckSingleSdrObjectHit + mpView->SetHitTolerancePixel( 2 * HITPIX ); + + OutlinerView* pOLV = mpView->GetTextEditOutlinerView(); + + if (pOLV) + pOLV->ShowCursor(/*bGotoCursor=*/true, /*bActivate=*/true); + + FuConstruct::Activate(); + + if( pOLV ) + mpView->SetEditMode(SdrViewEditMode::Edit); +} + +void FuText::Deactivate() +{ + OutlinerView* pOLV = mpView->GetTextEditOutlinerView(); + + if (pOLV) + pOLV->HideCursor(/*bDeactivate=*/true); + + mpView->SetHitTolerancePixel( HITPIX ); + + FuConstruct::Deactivate(); +} + +/** + * Sets the object into the edit mode. + */ +void FuText::SetInEditMode(const MouseEvent& rMEvt, bool bQuickDrag) +{ + SdrPageView* pPV = mpView->GetSdrPageView(); + if( mxTextObj.is() && (mxTextObj->getSdrPageFromSdrObject() == pPV->GetPage()) ) + { + mpView->SetCurrentObj(SdrObjKind::Text); + + if( bPermanent ) + mpView->SetEditMode(SdrViewEditMode::Create); + else + mpView->SetEditMode(SdrViewEditMode::Edit); + + bool bEmptyOutliner = false; + + if (!GetTextObj()->GetOutlinerParaObject() && mpView->GetTextEditOutliner()) + { + ::Outliner* pOutl = mpView->GetTextEditOutliner(); + sal_Int32 nParagraphCnt = pOutl->GetParagraphCount(); + Paragraph* p1stPara = pOutl->GetParagraph( 0 ); + + if (nParagraphCnt==1 && p1stPara) + { + // with only one paragraph + if (pOutl->GetText(p1stPara).isEmpty()) + { + bEmptyOutliner = true; + } + } + } + + if (GetTextObj() != mpView->GetTextEditObject() || bEmptyOutliner) + { + SdrInventor nInv = mxTextObj->GetObjInventor(); + SdrObjKind nSdrObjKind = mxTextObj->GetObjIdentifier(); + + if (nInv == SdrInventor::Default && GetTextObj()->HasTextEdit() && + (nSdrObjKind == SdrObjKind::Text || + nSdrObjKind == SdrObjKind::TitleText || + nSdrObjKind == SdrObjKind::OutlineText || !mxTextObj->IsEmptyPresObj() ) ) + { + // create new outliner (owned by SdrObjEditView) + std::unique_ptr<SdrOutliner> pOutl = SdrMakeOutliner(OutlinerMode::OutlineObject, *mpDoc); + + if (bEmptyOutliner) + mpView->SdrEndTextEdit(true); + + SdrTextObj* pTextObj = GetTextObj(); + if( pTextObj ) + { + OutlinerParaObject* pOPO = pTextObj->GetOutlinerParaObject(); + if( pOPO && pOPO->IsEffectivelyVertical() ) + { + pOutl->SetVertical(pOPO->GetVertical()); + pOutl->SetRotation(pOPO->GetRotation()); + } + else if (nSlotId == SID_ATTR_CHAR_VERTICAL || nSlotId == SID_TEXT_FITTOSIZE_VERTICAL) + pOutl->SetVertical( true ); + + if( pTextObj->getTextCount() > 1 ) + { + Point aPix(rMEvt.GetPosPixel()); + Point aPnt(mpWindow->PixelToLogic(aPix)); + pTextObj->setActiveText( pTextObj->CheckTextHit(aPnt ) ); + } + + if (mpView->SdrBeginTextEdit(pTextObj, pPV, mpWindow, true, pOutl.release()) && mxTextObj->GetObjInventor() == SdrInventor::Default) + { + //tdf#102293 flush overlay before going on to pass clicks down to + //the outline view which will want to paint selections + for (sal_uInt32 b = 0; b < pPV->PageWindowCount(); ++b) + { + const SdrPageWindow& rPageWindow = *pPV->GetPageWindow(b); + if (!rPageWindow.GetPaintWindow().OutputToWindow()) + continue; + const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager(); + if (!xManager.is()) + continue; + xManager->flush(); + } + + bFirstObjCreated = true; + DeleteDefaultText(); + + OutlinerView* pOLV = mpView->GetTextEditOutlinerView(); + + nSdrObjKind = mxTextObj->GetObjIdentifier(); + + SdrViewEvent aVEvt; + SdrHitKind eHit = mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + + if (eHit == SdrHitKind::TextEdit) + { + // hit text + if (nSdrObjKind == SdrObjKind::Text || + nSdrObjKind == SdrObjKind::TitleText || + nSdrObjKind == SdrObjKind::OutlineText || + nSdrObjKind == SdrObjKind::Table || + nSlotId == SID_TEXTEDIT || + !bQuickDrag) + { + pOLV->MouseButtonDown(rMEvt); + pOLV->MouseMove(rMEvt); + pOLV->MouseButtonUp(rMEvt); + } + + if (mpViewShell->GetFrameView()->IsQuickEdit() && bQuickDrag && GetTextObj()->GetOutlinerParaObject()) + { + pOLV->MouseButtonDown(rMEvt); + } + } + else + { + // Move cursor to end of text + ESelection aNewSelection(EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND, EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND); + if (pOLV != nullptr) + pOLV->SetSelection(aNewSelection); + } + } + else + { + mpView->RestoreDefaultText( mxTextObj.get() ); + } + } + } + } + } + else + { + mxTextObj.reset(nullptr); + } +} + +/** + * Text entry is started, if necessary delete the default text. + */ +void FuText::DeleteDefaultText() +{ + if ( !(mxTextObj.is() && mxTextObj->IsEmptyPresObj()) ) + return; + + SdPage* pPage = static_cast<SdPage*>( mxTextObj->getSdrPageFromSdrObject() ); + + if (!pPage) + return; + + PresObjKind ePresObjKind = pPage->GetPresObjKind(mxTextObj.get()); + + if ( !(ePresObjKind == PresObjKind::Title || + ePresObjKind == PresObjKind::Outline || + ePresObjKind == PresObjKind::Notes || + ePresObjKind == PresObjKind::Text ) || + pPage->IsMasterPage() ) + return; + + ::Outliner* pOutliner = mpView->GetTextEditOutliner(); + SfxStyleSheet* pSheet = pOutliner->GetStyleSheet( 0 ); + bool bIsUndoEnabled = pOutliner->IsUndoEnabled(); + if( bIsUndoEnabled ) + pOutliner->EnableUndo(false); + + pOutliner->SetText( OUString(), pOutliner->GetParagraph( 0 ) ); + + if( bIsUndoEnabled ) + pOutliner->EnableUndo(true); + + if (pSheet && + (ePresObjKind == PresObjKind::Notes || ePresObjKind == PresObjKind::Text)) + pOutliner->SetStyleSheet(0, pSheet); + + mxTextObj->SetEmptyPresObj(true); +} + +bool FuText::RequestHelp(const HelpEvent& rHEvt) +{ + bool bReturn = false; + + OutlinerView* pOLV = mpView->GetTextEditOutlinerView(); + + if ((Help::IsBalloonHelpEnabled() || Help::IsQuickHelpEnabled()) && + mxTextObj.is() && pOLV && pOLV->GetFieldUnderMousePointer()) + { + OUString aHelpText; + const SvxFieldItem* pFieldItem = pOLV->GetFieldUnderMousePointer(); + const SvxFieldData* pField = pFieldItem->GetField(); + + if (auto pURLField = dynamic_cast< const SvxURLField *>( pField )) + { + // URL-Field + aHelpText = INetURLObject::decode( pURLField->GetURL(), INetURLObject::DecodeMechanism::WithCharset ); + } + if (!aHelpText.isEmpty()) + { + ::tools::Rectangle aLogicPix = mpWindow->LogicToPixel(mxTextObj->GetLogicRect()); + ::tools::Rectangle aScreenRect(mpWindow->OutputToScreenPixel(aLogicPix.TopLeft()), + mpWindow->OutputToScreenPixel(aLogicPix.BottomRight())); + + if (Help::IsBalloonHelpEnabled()) + { + Help::ShowBalloon( static_cast<vcl::Window*>(mpWindow), rHEvt.GetMousePosPixel(), aScreenRect, aHelpText); + bReturn = true; + } + else if (Help::IsQuickHelpEnabled()) + { + Help::ShowQuickHelp( static_cast<vcl::Window*>(mpWindow), aScreenRect, aHelpText); + bReturn = true; + } + } + } + + if (!bReturn) + { + bReturn = FuConstruct::RequestHelp(rHEvt); + } + + return bReturn; +} + +void FuText::ReceiveRequest(SfxRequest& rReq) +{ + nSlotId = rReq.GetSlot(); + + // then we call the base class (besides others, nSlotId is NOT set there) + FuPoor::ReceiveRequest(rReq); + + if (!(nSlotId == SID_TEXTEDIT || mpViewShell->GetFrameView()->IsQuickEdit() || SID_ATTR_CHAR == nSlotId)) + return; + + MouseEvent aMEvt(mpWindow->GetPointerPosPixel()); + + mxTextObj.reset(nullptr); + + if (nSlotId == SID_TEXTEDIT) + { + // are we currently editing? + mxTextObj.reset( mpView->GetTextEditObject() ); + + if (!mxTextObj.is()) + { + // Try to select an object + SdrPageView* pPV = mpView->GetSdrPageView(); + SdrViewEvent aVEvt; + mpView->PickAnything(aMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + mpView->MarkObj(aVEvt.mpRootObj, pPV); + + if (auto pSdrTextObj = dynamic_cast<SdrTextObj*>(aVEvt.mpObj)) + { + mxTextObj.reset( pSdrTextObj ); + } + } + } + else if (mpView->AreObjectsMarked()) + { + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + if (rMarkList.GetMarkCount() == 1) + { + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + + if( auto pTextObj = dynamic_cast<SdrTextObj *>( pObj )) + { + mxTextObj.reset( pTextObj ); + } + } + } + + bool bQuickDrag = true; + + const SfxItemSet* pArgs = rReq.GetArgs(); + + if (pArgs + + // test for type before using + && SID_TEXTEDIT == nSlotId + && SfxItemState::SET == pArgs->GetItemState(SID_TEXTEDIT) + + && static_cast<const SfxUInt16Item&>( pArgs->Get(SID_TEXTEDIT)).GetValue() == 2) + { + // selection with double click -> do not allow QuickDrag + bQuickDrag = false; + } + + SetInEditMode(aMEvt, bQuickDrag); +} + +void FuText::DoubleClick(const MouseEvent& ) +{ + // Nothing to do +} + +/** Removed the insertion of default text and putting a new text + object directly into edit mode. +*/ +SdrObjectUniquePtr FuText::CreateDefaultObject(const sal_uInt16 nID, const ::tools::Rectangle& rRectangle) +{ + SdrObjectUniquePtr pObj( SdrObjFactory::MakeNewObject( + mpView->getSdrModelFromSdrView(), + mpView->GetCurrentObjInventor(), + mpView->GetCurrentObjIdentifier(), + nullptr) ); + + if(pObj) + { + if( auto pText = dynamic_cast< SdrTextObj *>( pObj.get() ) ) + { + pText->SetLogicRect(rRectangle); + + bool bVertical = (SID_ATTR_CHAR_VERTICAL == nID || SID_TEXT_FITTOSIZE_VERTICAL == nID); + pText->SetVerticalWriting(bVertical); + + ImpSetAttributesForNewTextObject(pText); + + if (nSlotId == SID_TEXT_FITTOSIZE) + { + ImpSetAttributesFitToSize(pText); + } + else if ( nSlotId == SID_TEXT_FITTOSIZE_VERTICAL ) + { + ImpSetAttributesFitToSizeVertical(pText); + } + else + { + ImpSetAttributesFitCommon(pText); + } + + // Put text object into edit mode. + SdrPageView* pPV = mpView->GetSdrPageView(); + mpView->SdrBeginTextEdit(pText, pPV); + } + else + { + OSL_FAIL("Object is NO text object"); + } + } + + return pObj; +} + +/** is called when the current function should be aborted. <p> + This is used when a function gets a KEY_ESCAPE but can also + be called directly. + + @returns true if an active function was aborted +*/ +bool FuText::cancel() +{ + if ( mpView->IsTextEdit() ) + { + if(mpView->SdrEndTextEdit() == SdrEndTextEditKind::Deleted) + mxTextObj.reset(nullptr); + + mpView->SetCurrentObj(SdrObjKind::Text); + mpView->SetEditMode(SdrViewEditMode::Edit); + return true; + } + else + { + return false; + } +} + +void FuText::ChangeFontSize( bool bGrow, OutlinerView* pOLV, const FontList* pFontList, ::sd::View* pView ) +{ + if( !pFontList || !pView ) + return; + + if( pOLV ) + { + pOLV->GetEditView().ChangeFontSize( bGrow, pFontList ); + } + else + { + + pView->BegUndo(SdResId(bGrow ? STR_GROW_FONT_SIZE : STR_SHRINK_FONT_SIZE)); + const SdrMarkList& rMarkList = pView->GetMarkedObjectList(); + for( size_t nMark = 0; nMark < rMarkList.GetMarkCount(); ++nMark ) + { + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( rMarkList.GetMark(nMark)->GetMarkedSdrObj() ); + if( pTextObj ) + { + rtl::Reference<sdr::SelectionController> xSelectionController(pView->getSelectionController()); + if (xSelectionController.is() && xSelectionController->ChangeFontSize(bGrow, pFontList)) + { + continue; + } + for( sal_Int32 nText = 0; nText < pTextObj->getTextCount(); nText++ ) + { + pTextObj->setActiveText( nText ); + + // Put text object into edit mode. + SdrPageView* pPV = pView->GetSdrPageView(); + pView->SdrBeginTextEdit(pTextObj, pPV); + + pOLV = pView->GetTextEditOutlinerView(); + if( pOLV ) + { + EditEngine* pEditEngine = pOLV->GetEditView().GetEditEngine(); + if( pEditEngine ) + { + ESelection aSel; + aSel.nEndPara = pEditEngine->GetParagraphCount()-1; + aSel.nEndPos = pEditEngine->GetTextLen(aSel.nEndPara); + pOLV->SetSelection(aSel); + } + + ChangeFontSize( bGrow, pOLV, pFontList, pView ); + } + + pView->SdrEndTextEdit(); + } + + SfxItemSet aShapeSet( pTextObj->GetMergedItemSet() ); + if( EditView::ChangeFontSize( bGrow, aShapeSet, pFontList ) ) + { + pTextObj->SetObjectItemNoBroadcast( aShapeSet.Get( EE_CHAR_FONTHEIGHT ) ); + pTextObj->SetObjectItemNoBroadcast( aShapeSet.Get( EE_CHAR_FONTHEIGHT_CJK ) ); + pTextObj->SetObjectItemNoBroadcast( aShapeSet.Get( EE_CHAR_FONTHEIGHT_CTL ) ); + } + } + } + pView->EndUndo(); + } +} + +void FuText::InvalidateBindings() +{ + mpViewShell->GetViewFrame()->GetBindings().Invalidate(SidArray); +} + + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/futhes.cxx b/sd/source/ui/func/futhes.cxx new file mode 100644 index 000000000..78d2ae693 --- /dev/null +++ b/sd/source/ui/func/futhes.cxx @@ -0,0 +1,132 @@ +/* -*- 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 <futhes.hxx> + +#include <editeng/outliner.hxx> +#include <sfx2/request.hxx> +#include <svx/svdobj.hxx> +#include <svx/svdotext.hxx> +#include <editeng/eeitem.hxx> +#include <tools/debug.hxx> + +#include <svx/svxerr.hxx> +#include <svx/dialmgr.hxx> +#include <editeng/unolingu.hxx> +#include <vcl/weld.hxx> +#include <drawdoc.hxx> +#include <View.hxx> +#include <Outliner.hxx> +#include <DrawViewShell.hxx> +#include <OutlineViewShell.hxx> +#include <Window.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::linguistic2; + +class SfxRequest; + +namespace sd { + + +FuThesaurus::FuThesaurus( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, + SdDrawDocument* pDoc, SfxRequest& rReq ) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuThesaurus::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuThesaurus( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuThesaurus::DoExecute(SfxRequest& rReq) +{ + SfxErrorContext aContext(ERRCTX_SVX_LINGU_THESAURUS, OUString(), + mpWindow->GetFrameWeld(), RID_SVXERRCTX, SvxResLocale()); + + if (dynamic_cast< DrawViewShell *>( mpViewShell )) + { + SdrTextObj* pTextObj = nullptr; + + if ( mpView->AreObjectsMarked() ) + { + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + if ( rMarkList.GetMarkCount() == 1 ) + { + SdrMark* pMark = rMarkList.GetMark(0); + SdrObject* pObj = pMark->GetMarkedSdrObj(); + + pTextObj = dynamic_cast<SdrTextObj *>( pObj ); + } + } + + ::Outliner* pOutliner = mpView->GetTextEditOutliner(); + const OutlinerView* pOutlView = mpView->GetTextEditOutlinerView(); + + if ( pTextObj && pOutliner && pOutlView ) + { + if ( !pOutliner->GetSpeller().is() ) + { + Reference< XSpellChecker1 > xSpellChecker( LinguMgr::GetSpellChecker() ); + if ( xSpellChecker.is() ) + pOutliner->SetSpeller( xSpellChecker ); + + Reference< XHyphenator > xHyphenator( LinguMgr::GetHyphenator() ); + if( xHyphenator.is() ) + pOutliner->SetHyphenator( xHyphenator ); + + pOutliner->SetDefaultLanguage( mpDoc->GetLanguage( EE_CHAR_LANGUAGE ) ); + } + + EESpellState eState = const_cast<OutlinerView*>(pOutlView)->StartThesaurus(rReq.GetFrameWeld()); + DBG_ASSERT(eState != EESpellState::NoSpeller, "No SpellChecker"); + } + } + else if (dynamic_cast< OutlineViewShell *>( mpViewShell )) + { + Outliner* pOutliner = mpDoc->GetOutliner(); + OutlinerView* pOutlView = pOutliner->GetView(0); + + if ( !pOutliner->GetSpeller().is() ) + { + Reference< XSpellChecker1 > xSpellChecker( LinguMgr::GetSpellChecker() ); + if ( xSpellChecker.is() ) + pOutliner->SetSpeller( xSpellChecker ); + + Reference< XHyphenator > xHyphenator( LinguMgr::GetHyphenator() ); + if( xHyphenator.is() ) + pOutliner->SetHyphenator( xHyphenator ); + + pOutliner->SetDefaultLanguage( mpDoc->GetLanguage( EE_CHAR_LANGUAGE ) ); + } + + EESpellState eState = pOutlView->StartThesaurus(rReq.GetFrameWeld()); + DBG_ASSERT(eState != EESpellState::NoSpeller, "No SpellChecker"); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/futransf.cxx b/sd/source/ui/func/futransf.cxx new file mode 100644 index 000000000..8c565a3b8 --- /dev/null +++ b/sd/source/ui/func/futransf.cxx @@ -0,0 +1,132 @@ +/* -*- 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 <futransf.hxx> + +#include <svx/svxids.hrc> +#include <sfx2/request.hxx> + +#include <strings.hrc> +#include <ViewShell.hxx> +#include <View.hxx> +#include <sdresid.hxx> +#include <drawdoc.hxx> +#include <svx/svxdlg.hxx> +#include <comphelper/lok.hxx> + +#include <memory> + +using namespace sd; + +FuTransform::FuTransform(ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, + SdDrawDocument* pDoc, SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuTransform::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuTransform( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +namespace { + +void setUndo(::sd::View* pView, const SfxItemSet* pArgs, bool addPageMargin) +{ + // Undo + OUString aString = pView->GetDescriptionOfMarkedObjects() + + " " + SdResId(STR_TRANSFORM); + pView->BegUndo(aString); + pView->SetGeoAttrToMarked(*pArgs, addPageMargin); + pView->SetAttributes(*pArgs); + pView->EndUndo(); +} + +} + +void FuTransform::DoExecute( SfxRequest& rReq ) +{ + if (!mpView->AreObjectsMarked()) + return; + + const SfxItemSet* pArgs = rReq.GetArgs(); + + if (pArgs) + { + // If this comes from LOK, that means the shape is moved by mouse + // only then pArgs is pre-set. + setUndo(mpView, pArgs, comphelper::LibreOfficeKit::isActive()); + return; + } + + // --------- itemset for size and position -------- + SfxItemSet aSet( mpView->GetGeoAttrFromMarked() ); + VclPtr<SfxAbstractTabDialog> pDlg; + + bool bWelded = false; + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + if( rMarkList.GetMarkCount() == 1 && + pObj->GetObjInventor() == SdrInventor::Default && + pObj->GetObjIdentifier() == SdrObjKind::Caption ) + { + // --------- itemset for caption -------- + SfxItemSet aNewAttr( mpDoc->GetPool() ); + mpView->GetAttributes( aNewAttr ); + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + pDlg.reset(pFact->CreateCaptionDialog(mpViewShell->GetFrameWeld(), mpView)); + + const WhichRangesContainer& pRange = pDlg->GetInputRanges( *aNewAttr.GetPool() ); + SfxItemSet aCombSet( *aNewAttr.GetPool(), pRange ); + aCombSet.Put( aNewAttr ); + aCombSet.Put( aSet ); + pDlg->SetInputSet( &aCombSet ); + } + else + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + pDlg.reset(pFact->CreateSvxTransformTabDialog(mpViewShell->GetFrameWeld(), &aSet, mpView)); + bWelded = true; + } + + assert(pDlg && "there must be a dialog at this point"); + + auto pRequest = std::make_shared<SfxRequest>(rReq); + rReq.Ignore(); // the 'old' request is not relevant any more + + pDlg->StartExecuteAsync([bWelded, pDlg, pRequest, this](sal_Int32 nResult){ + if (nResult == RET_OK) + { + pRequest->Done(*(pDlg->GetOutputItemSet())); + // Page margin is already calculated at this point. + setUndo(mpView, pRequest->GetArgs(), false); + } + + // deferred until the dialog ends + mpViewShell->Invalidate(SID_RULER_OBJECT); + mpViewShell->Cancel(); + if (bWelded) + pDlg->disposeOnce(); + }); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/futxtatt.cxx b/sd/source/ui/func/futxtatt.cxx new file mode 100644 index 000000000..56f8c2569 --- /dev/null +++ b/sd/source/ui/func/futxtatt.cxx @@ -0,0 +1,80 @@ +/* -*- 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 <futxtatt.hxx> +#include <sfx2/request.hxx> + +#include <svx/svxdlg.hxx> +#include <View.hxx> +#include <drawdoc.hxx> + +namespace sd { + + +FuTextAttrDlg::FuTextAttrDlg ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuTextAttrDlg::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuTextAttrDlg( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuTextAttrDlg::DoExecute( SfxRequest& rReq ) +{ + SfxItemSet aNewAttr( mpDoc->GetPool() ); + mpView->GetAttributes( aNewAttr ); + + const SfxItemSet* pArgs = rReq.GetArgs(); + + if( !pArgs ) + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateTextTabDialog(rReq.GetFrameWeld(), &aNewAttr, mpView)); + + sal_uInt16 nResult = pDlg->Execute(); + + switch( nResult ) + { + case RET_OK: + { + rReq.Done( *( pDlg->GetOutputItemSet() ) ); + + pArgs = rReq.GetArgs(); + } + break; + + default: + return; // Cancel + } + } + mpView->SetAttributes( *pArgs ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuvect.cxx b/sd/source/ui/func/fuvect.cxx new file mode 100644 index 000000000..64840810c --- /dev/null +++ b/sd/source/ui/func/fuvect.cxx @@ -0,0 +1,87 @@ +/* -*- 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 <fuvect.hxx> +#include <svx/svdograf.hxx> + +#include <View.hxx> +#include <Window.hxx> +#include <strings.hrc> +#include <sdresid.hxx> +#include <sdabstdlg.hxx> + +namespace sd +{ + + +FuVectorize::FuVectorize ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor (pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference<FuPoor> FuVectorize::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuVectorize( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuVectorize::DoExecute( SfxRequest& ) +{ + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + if( rMarkList.GetMarkCount() != 1 ) + return; + + SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj(); + + auto pSdrGrafObj = dynamic_cast< const SdrGrafObj *>( pObj ); + if( !pSdrGrafObj ) + return; + + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSdVectorizeDlg> pDlg( + pFact->CreateSdVectorizeDlg(mpWindow ? mpWindow->GetFrameWeld() : nullptr, + pSdrGrafObj->GetGraphic().GetBitmapEx().GetBitmap(), mpDocSh ) ); + if( pDlg->Execute() != RET_OK ) + return; + + const GDIMetaFile& rMtf = pDlg->GetGDIMetaFile(); + SdrPageView* pPageView = mpView->GetSdrPageView(); + + if( pPageView && rMtf.GetActionSize() ) + { + SdrGrafObj* pVectObj = static_cast<SdrGrafObj*>( pObj->CloneSdrObject(pObj->getSdrModelFromSdrObject()) ); + OUString aStr = mpView->GetDescriptionOfMarkedObjects() + + " " + SdResId( STR_UNDO_VECTORIZE ); + mpView->BegUndo( aStr ); + pVectObj->SetGraphic( rMtf ); + mpView->ReplaceObjectAtView( pObj, *pPageView, pVectObj ); + mpView->EndUndo(); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuzoom.cxx b/sd/source/ui/func/fuzoom.cxx new file mode 100644 index 000000000..871d594d3 --- /dev/null +++ b/sd/source/ui/func/fuzoom.cxx @@ -0,0 +1,219 @@ +/* -*- 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 <fuzoom.hxx> + +#include <svx/svxids.hrc> +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <app.hrc> +#include <svx/svdpagv.hxx> +#include <vcl/ptrstyle.hxx> + +#include <ViewShell.hxx> +#include <View.hxx> +#include <Window.hxx> +#include <zoomlist.hxx> + +namespace sd { + +const sal_uInt16 SidArrayZoom[] = { + SID_ATTR_ZOOM, + SID_ZOOM_OUT, + SID_ZOOM_IN, + 0 }; + + +FuZoom::FuZoom( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq), + bVisible(false), + bStartDrag(false), + aPtr(PointerStyle::Arrow) +{ +} + +FuZoom::~FuZoom() +{ + if (bVisible) + { + // Hide ZoomRect + mpViewShell->DrawMarkRect(aZoomRect); + + bVisible = false; + bStartDrag = false; + } +} + +rtl::Reference<FuPoor> FuZoom::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference<FuPoor> xFunc( new FuZoom( pViewSh, pWin, pView, pDoc, rReq ) ); + return xFunc; +} + +bool FuZoom::MouseButtonDown(const MouseEvent& rMEvt) +{ + // remember button state for creation of own MouseEvents + SetMouseButtonCode(rMEvt.GetButtons()); + + mpWindow->CaptureMouse(); + bStartDrag = true; + + aBeginPosPix = rMEvt.GetPosPixel(); + aBeginPos = mpWindow->PixelToLogic(aBeginPosPix); + aZoomRect.SetSize( Size( 0, 0 ) ); + aZoomRect.SetPos( aBeginPos ); + + return true; +} + +bool FuZoom::MouseMove(const MouseEvent& rMEvt) +{ + if (rMEvt.IsShift()) + mpWindow->SetPointer(PointerStyle::Hand); + else if (nSlotId != SID_ZOOM_PANNING) + mpWindow->SetPointer(PointerStyle::Magnify); + + if (bStartDrag) + { + if (bVisible) + { + mpViewShell->DrawMarkRect(aZoomRect); + } + + Point aPosPix = rMEvt.GetPosPixel(); + ForceScroll(aPosPix); + + aEndPos = mpWindow->PixelToLogic(aPosPix); + aBeginPos = mpWindow->PixelToLogic(aBeginPosPix); + + if (nSlotId == SID_ZOOM_PANNING || (rMEvt.IsShift() && !bVisible) ) + { + // Panning + + Point aScroll = aBeginPos - aEndPos; + + if (aScroll.X() != 0 || aScroll.Y() != 0) + { + Size aWorkSize = mpView->GetWorkArea().GetSize(); + Size aPageSize = mpView->GetSdrPageView()->GetPage()->GetSize(); + if (aWorkSize.Width() != 0 && aWorkSize.Height() != 0 && + aPageSize.Width() != 0 && aPageSize.Height() != 0) + { + aScroll.setX( aScroll.X() / ( aWorkSize.Width() / aPageSize.Width()) ); + aScroll.setY( aScroll.Y() / ( aWorkSize.Height() / aPageSize.Height()) ); + mpViewShell->Scroll(aScroll.X(), aScroll.Y()); + aBeginPosPix = aPosPix; + } + } + } + else + { + ::tools::Rectangle aRect(aBeginPos, aEndPos); + aZoomRect = aRect; + aZoomRect.Justify(); + mpViewShell->DrawMarkRect(aZoomRect); + bVisible = true; + } + } + + return bStartDrag; +} + +bool FuZoom::MouseButtonUp(const MouseEvent& rMEvt) +{ + // remember button state for creation of own MouseEvents + SetMouseButtonCode(rMEvt.GetButtons()); + + if (bVisible) + { + // Hide ZoomRect + mpViewShell->DrawMarkRect(aZoomRect); + bVisible = false; + } + + Point aPosPix = rMEvt.GetPosPixel(); + + if(SID_ZOOM_PANNING != nSlotId && !rMEvt.IsShift()) + { + // Zoom + Size aZoomSizePixel = mpWindow->LogicToPixel(aZoomRect).GetSize(); + sal_uLong nTol = DRGPIX + DRGPIX; + + if ( ( aZoomSizePixel.Width() < static_cast<::tools::Long>(nTol) && aZoomSizePixel.Height() < static_cast<::tools::Long>(nTol) ) || rMEvt.IsMod1() ) + { + // click at place: double zoom factor + Point aPos = mpWindow->PixelToLogic(aPosPix); + Size aSize = mpWindow->PixelToLogic(mpWindow->GetOutputSizePixel()); + if ( rMEvt.IsMod1() ) + { + aSize.setWidth( aSize.Width() * 2 ); + aSize.setHeight( aSize.Height() * 2 ); + } + else + { + aSize.setWidth( aSize.Width() / 2 ); + aSize.setHeight( aSize.Height() / 2 ); + } + aPos.AdjustX( -(aSize.Width() / 2) ); + aPos.AdjustY( -(aSize.Height() / 2) ); + aZoomRect.SetPos(aPos); + aZoomRect.SetSize(aSize); + } + + mpViewShell->SetZoomRect(aZoomRect); + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SidArrayZoom ); + } + + ::tools::Rectangle aVisAreaWin = mpWindow->PixelToLogic(::tools::Rectangle(Point(0,0), + mpWindow->GetOutputSizePixel())); + mpViewShell->GetZoomList()->InsertZoomRect(aVisAreaWin); + + bStartDrag = false; + mpWindow->ReleaseMouse(); + + return true; +} + +void FuZoom::Activate() +{ + aPtr = mpWindow->GetPointer(); + + if (nSlotId == SID_ZOOM_PANNING) + { + mpWindow->SetPointer(PointerStyle::Hand); + } + else + { + mpWindow->SetPointer(PointerStyle::Magnify); + } +} + +void FuZoom::Deactivate() +{ + mpWindow->SetPointer( aPtr ); + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SidArrayZoom ); +} +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/sdundogr.cxx b/sd/source/ui/func/sdundogr.cxx new file mode 100644 index 000000000..a2e97386d --- /dev/null +++ b/sd/source/ui/func/sdundogr.cxx @@ -0,0 +1,66 @@ +/* -*- 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 <sdundogr.hxx> +#include <tools/long.hxx> + +SdUndoGroup::~SdUndoGroup() = default; + +bool SdUndoGroup::Merge(SfxUndoAction* pNextAction) +{ + bool bRet = false; + + if (auto pSdUndoAction = dynamic_cast<SdUndoAction*>(pNextAction)) + { + SdUndoAction* pClone = pSdUndoAction->Clone(); + + if (pClone) + { + AddAction(pClone); + bRet = true; + } + } + + return bRet; +} + +/** + * Undo, reverse order of execution + */ +void SdUndoGroup::Undo() +{ + ::tools::Long nLast = aCtn.size(); + for (::tools::Long nAction = nLast - 1; nAction >= 0; nAction--) + { + aCtn[nAction]->Undo(); + } +} + +void SdUndoGroup::Redo() +{ + size_t nLast = aCtn.size(); + for (size_t nAction = 0; nAction < nLast; nAction++) + { + aCtn[nAction]->Redo(); + } +} + +void SdUndoGroup::AddAction(SdUndoAction* pAction) { aCtn.emplace_back(pAction); } + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/smarttag.cxx b/sd/source/ui/func/smarttag.cxx new file mode 100644 index 000000000..ff5382116 --- /dev/null +++ b/sd/source/ui/func/smarttag.cxx @@ -0,0 +1,333 @@ +/* -*- 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/commandevent.hxx> + +#include <ViewShell.hxx> +#include <smarttag.hxx> +#include <Window.hxx> +#include <View.hxx> + +namespace sd +{ + +SmartTag::SmartTag( ::sd::View& rView ) +: mrView( rView ) +, mbSelected( false ) +{ + SmartTagReference xThis( this ); + mrView.getSmartTags().add( xThis ); +} + +SmartTag::~SmartTag() +{ +} + +bool SmartTag::MouseButtonDown( const MouseEvent&, SmartHdl& ) +{ + return false; +} + +/** returns true if the SmartTag consumes this event. */ +bool SmartTag::KeyInput( const KeyEvent& /*rKEvt*/ ) +{ + return false; +} + +/** returns true if the SmartTag consumes this event. */ +bool SmartTag::Command( const CommandEvent& /*rCEvt*/ ) +{ + return false; +} + +void SmartTag::addCustomHandles( SdrHdlList& /*rHandlerList*/ ) +{ +} + +void SmartTag::select() +{ + mbSelected = true; +} + +void SmartTag::deselect() +{ + mbSelected = false; +} + +void SmartTag::disposing() +{ + SmartTagReference xThis( this ); + mrView.getSmartTags().remove( xThis ); +} + +bool SmartTag::getContext( SdrViewContext& /*rContext*/ ) +{ + return false; +} + +sal_Int32 SmartTag::GetMarkablePointCount() const +{ + return 0; +} + +sal_Int32 SmartTag::GetMarkedPointCount() const +{ + return 0; +} + +bool SmartTag::MarkPoint(SdrHdl& /*rHdl*/, bool /*bUnmark*/ ) +{ + return false; +} + +bool SmartTag::MarkPoints(const ::tools::Rectangle* /*pRect*/, bool /*bUnmark*/ ) +{ + return false; +} + +void SmartTag::CheckPossibilities() +{ +} + +SmartTagSet::SmartTagSet( View& rView ) +: mrView( rView ) +{ +} + +SmartTagSet::~SmartTagSet() +{ +} + +void SmartTagSet::add( const SmartTagReference& xTag ) +{ + maSet.insert( xTag ); + mrView.InvalidateAllWin(); + + if( xTag == mxMouseOverTag ) + mxMouseOverTag.clear(); + + if( xTag == mxSelectedTag ) + mxSelectedTag.clear(); +} + +void SmartTagSet::remove( const SmartTagReference& xTag ) +{ + std::set< SmartTagReference >::iterator aIter( maSet.find( xTag ) ); + if( aIter != maSet.end() ) + maSet.erase( aIter ); + mrView.InvalidateAllWin(); + + if( xTag == mxMouseOverTag ) + mxMouseOverTag.clear(); + + if( xTag == mxSelectedTag ) + mxSelectedTag.clear(); +} + +void SmartTagSet::Dispose() +{ + std::set< SmartTagReference > aSet; + aSet.swap( maSet ); + for( auto& rxItem : aSet ) + rxItem->Dispose(); + mrView.InvalidateAllWin(); + mxMouseOverTag.clear(); + mxSelectedTag.clear(); +} + +void SmartTagSet::select( const SmartTagReference& xTag ) +{ + if( mxSelectedTag == xTag ) + return; + + if( mxSelectedTag.is() ) + mxSelectedTag->deselect(); + + mxSelectedTag = xTag; + mxSelectedTag->select(); + mrView.SetPossibilitiesDirty(); + if( mrView.GetMarkedObjectCount() > 0 ) + mrView.UnmarkAllObj(); + else + mrView.updateHandles(); +} + +void SmartTagSet::deselect() +{ + if( mxSelectedTag.is() ) + { + mxSelectedTag->deselect(); + mxSelectedTag.clear(); + mrView.SetPossibilitiesDirty(); + mrView.updateHandles(); + } +} + +bool SmartTagSet::MouseButtonDown( const MouseEvent& rMEvt ) +{ + Point aMDPos( mrView.GetViewShell()->GetActiveWindow()->PixelToLogic( rMEvt.GetPosPixel() ) ); + SdrHdl* pHdl = mrView.PickHandle(aMDPos); + + // check if a smart tag is selected and no handle is hit + if( mxSelectedTag.is() && !pHdl ) + { + // deselect smart tag + deselect(); + return false; + } + + // if a smart tag handle is hit, forward event to its smart tag + SmartHdl* pSmartHdl = dynamic_cast< SmartHdl* >( pHdl ); + if(pSmartHdl && pSmartHdl->getTag().is() ) + { + SmartTagReference xTag( pSmartHdl->getTag() ); + return xTag->MouseButtonDown( rMEvt, *pSmartHdl ); + } + + return false; +} + +bool SmartTagSet::KeyInput( const KeyEvent& rKEvt ) +{ + if( mxSelectedTag.is() ) + return mxSelectedTag->KeyInput( rKEvt ); + else if( rKEvt.GetKeyCode().GetCode() == KEY_SPACE ) + { + SmartHdl* pSmartHdl = dynamic_cast< SmartHdl* >( mrView.GetHdlList().GetFocusHdl() ); + if( pSmartHdl ) + { + const_cast< SdrHdlList& >( mrView.GetHdlList() ).ResetFocusHdl(); + const SmartTagReference& xTag( pSmartHdl->getTag() ); + select( xTag ); + return true; + } + } + + return false; +} + +/** returns true if the SmartTag consumes this event. */ +bool SmartTagSet::Command( const CommandEvent& rCEvt ) +{ + if( rCEvt.IsMouseEvent() ) + { + Point aMDPos( mrView.GetViewShell()->GetActiveWindow()->PixelToLogic( rCEvt.GetMousePosPixel() ) ); + SdrHdl* pHdl = mrView.PickHandle(aMDPos); + + if( pHdl ) + { + // if a smart tag handle is hit, forward event to its smart tag + SmartHdl* pSmartHdl = dynamic_cast< SmartHdl* >( pHdl ); + if(pSmartHdl && pSmartHdl->getTag().is() ) + { + const SmartTagReference& xTag( pSmartHdl->getTag() ); + return xTag->Command( rCEvt ); + } + } + } + else + { + if( mxSelectedTag.is() ) + return mxSelectedTag->Command( rCEvt ); + + } + + return false; +} + +void SmartTagSet::addCustomHandles( SdrHdlList& rHandlerList ) +{ + for( auto& rxItem : maSet ) + rxItem->addCustomHandles( rHandlerList ); +} + +/** returns true if the currently selected smart tag has + a special context, returned in rContext. */ +bool SmartTagSet::getContext( SdrViewContext& rContext ) const +{ + if( mxSelectedTag.is() ) + return mxSelectedTag->getContext( rContext ); + else + return false; +} + +// support point editing + +bool SmartTagSet::HasMarkablePoints() const +{ + return GetMarkablePointCount() != 0; +} + +sal_uLong SmartTagSet::GetMarkablePointCount() const +{ + if( mxSelectedTag.is() ) + return mxSelectedTag->GetMarkablePointCount(); + return 0; +} + +bool SmartTagSet::HasMarkedPoints() const +{ + return GetMarkedPointCount() != 0; +} + +sal_uLong SmartTagSet::GetMarkedPointCount() const +{ + if( mxSelectedTag.is() ) + return mxSelectedTag->GetMarkedPointCount(); + else + return 0; +} + +bool SmartTagSet::MarkPoint(SdrHdl& rHdl, bool bUnmark ) +{ + if( mxSelectedTag.is() ) + return mxSelectedTag->MarkPoint( rHdl, bUnmark ); + + return false; +} + +bool SmartTagSet::MarkPoints(const ::tools::Rectangle* pRect, bool bUnmark) +{ + if( mxSelectedTag.is() ) + return mxSelectedTag->MarkPoints( pRect, bUnmark ); + return false; +} + +void SmartTagSet::CheckPossibilities() +{ + if( mxSelectedTag.is() ) + mxSelectedTag->CheckPossibilities(); +} + +SmartHdl::SmartHdl( const SmartTagReference& xTag, SdrObject* pObject, const Point& rPnt, SdrHdlKind eNewKind /*=SdrHdlKind::Move*/ ) +: SdrHdl( rPnt, eNewKind ) +, mxSmartTag( xTag ) +{ + SetObj( pObject ); +} + +SmartHdl::SmartHdl( const SmartTagReference& xTag, const Point& rPnt, SdrHdlKind eNewKind /*=SdrHdlKind::Move*/ ) +: SdrHdl( rPnt, eNewKind ) +, mxSmartTag( xTag ) +{ +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/undoback.cxx b/sd/source/ui/func/undoback.cxx new file mode 100644 index 000000000..768ca2ec2 --- /dev/null +++ b/sd/source/ui/func/undoback.cxx @@ -0,0 +1,105 @@ +/* -*- 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 <memory> +#include <undoback.hxx> + +#include <sdpage.hxx> +#include <sdresid.hxx> +#include <strings.hrc> + +#include <com/sun/star/drawing/FillStyle.hpp> + +#include <svl/itemset.hxx> + +#include <svx/xdef.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xbtmpit.hxx> + +SdBackgroundObjUndoAction::SdBackgroundObjUndoAction( + SdDrawDocument& rDoc, + SdPage& rPage, + const SfxItemSet& rItemSet) +: SdUndoAction(&rDoc), + mrPage(rPage), + mpItemSet(std::make_unique<SfxItemSet>(rItemSet)), + mbHasFillBitmap(false) +{ + OUString aString( SdResId( STR_UNDO_CHANGE_PAGEFORMAT ) ); + SetComment( aString ); + saveFillBitmap(*mpItemSet); +} + +void SdBackgroundObjUndoAction::ImplRestoreBackgroundObj() +{ + std::unique_ptr<SfxItemSet> pNew = std::make_unique<SfxItemSet>(mrPage.getSdrPageProperties().GetItemSet()); + mrPage.getSdrPageProperties().ClearItem(); + if (bool(mpFillBitmapItem)) + restoreFillBitmap(*mpItemSet); + mpFillBitmapItem.reset(); + mbHasFillBitmap = false; + mrPage.getSdrPageProperties().PutItemSet(*mpItemSet); + mpItemSet = std::move(pNew); + saveFillBitmap(*mpItemSet); + + // tell the page that it's visualization has changed + mrPage.ActionChanged(); +} + +void SdBackgroundObjUndoAction::Undo() +{ + ImplRestoreBackgroundObj(); +} + +void SdBackgroundObjUndoAction::Redo() +{ + ImplRestoreBackgroundObj(); +} + +SdUndoAction* SdBackgroundObjUndoAction::Clone() const +{ + std::unique_ptr<SdBackgroundObjUndoAction> pCopy = std::make_unique<SdBackgroundObjUndoAction>(*mpDoc, mrPage, *mpItemSet); + if (mpFillBitmapItem) + pCopy->mpFillBitmapItem.reset(mpFillBitmapItem->Clone()); + pCopy->mbHasFillBitmap = mbHasFillBitmap; + return pCopy.release(); +} + +void SdBackgroundObjUndoAction::saveFillBitmap(SfxItemSet &rItemSet) +{ + if (const XFillBitmapItem *pItem = rItemSet.GetItemIfSet(XATTR_FILLBITMAP, false)) + mpFillBitmapItem.reset(pItem->Clone()); + if (bool(mpFillBitmapItem)) + { + if (const XFillStyleItem* pItem = rItemSet.GetItemIfSet(XATTR_FILLSTYLE, false)) + mbHasFillBitmap = pItem->GetValue() == css::drawing::FillStyle_BITMAP; + rItemSet.ClearItem(XATTR_FILLBITMAP); + if (mbHasFillBitmap) + rItemSet.ClearItem(XATTR_FILLSTYLE); + } +} + +void SdBackgroundObjUndoAction::restoreFillBitmap(SfxItemSet &rItemSet) +{ + rItemSet.Put(*mpFillBitmapItem); + if (mbHasFillBitmap) + rItemSet.Put(XFillStyleItem(css::drawing::FillStyle_BITMAP)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/undoheaderfooter.cxx b/sd/source/ui/func/undoheaderfooter.cxx new file mode 100644 index 000000000..e0183dac3 --- /dev/null +++ b/sd/source/ui/func/undoheaderfooter.cxx @@ -0,0 +1,53 @@ +/* -*- 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/dispatch.hxx> +#include <sfx2/viewfrm.hxx> + +#include <app.hrc> +#include <undoheaderfooter.hxx> + + +SdHeaderFooterUndoAction::SdHeaderFooterUndoAction( SdDrawDocument* pDoc, SdPage* pPage, const sd::HeaderFooterSettings& rNewSettings ) +: SdUndoAction(pDoc), + mpPage(pPage), + maOldSettings(pPage->getHeaderFooterSettings()), + maNewSettings(rNewSettings) +{ +} + +SdHeaderFooterUndoAction::~SdHeaderFooterUndoAction() +{ +} + +void SdHeaderFooterUndoAction::Undo() +{ + mpPage->setHeaderFooterSettings( maOldSettings ); + if (SfxViewFrame* pViewFrm = SfxViewFrame::Current()) + pViewFrm->GetDispatcher()->Execute( SID_SWITCHPAGE, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD ); +} + +void SdHeaderFooterUndoAction::Redo() +{ + mpPage->setHeaderFooterSettings( maNewSettings ); + if (SfxViewFrame* pViewFrm = SfxViewFrame::Current()) + pViewFrm->GetDispatcher()->Execute( SID_SWITCHPAGE, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/undolayer.cxx b/sd/source/ui/func/undolayer.cxx new file mode 100644 index 000000000..b29142ee8 --- /dev/null +++ b/sd/source/ui/func/undolayer.cxx @@ -0,0 +1,78 @@ +/* -*- 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 <undolayer.hxx> + +#include <DrawDocShell.hxx> +#include <drawdoc.hxx> +#include <DrawViewShell.hxx> +#include <strings.hrc> +#include <sdresid.hxx> + + +SdLayerModifyUndoAction::SdLayerModifyUndoAction( + SdDrawDocument* _pDoc, SdrLayer* pLayer, + const OUString& rOldLayerName, const OUString& rOldLayerTitle, const OUString& rOldLayerDesc, bool bOldIsVisible, bool bOldIsLocked, bool bOldIsPrintable, + const OUString& rNewLayerName, const OUString& rNewLayerTitle, const OUString& rNewLayerDesc, bool bNewIsVisible, bool bNewIsLocked, bool bNewIsPrintable ) +: SdUndoAction( _pDoc ), + mpLayer( pLayer ), + maOldLayerName( rOldLayerName ), + maOldLayerTitle( rOldLayerTitle ), + maOldLayerDesc( rOldLayerDesc ), + mbOldIsVisible( bOldIsVisible ), + mbOldIsLocked( bOldIsLocked ), + mbOldIsPrintable( bOldIsPrintable ), + maNewLayerName( rNewLayerName ), + maNewLayerTitle( rNewLayerTitle ), + maNewLayerDesc( rNewLayerDesc ), + mbNewIsVisible( bNewIsVisible ), + mbNewIsLocked( bNewIsLocked ), + mbNewIsPrintable( bNewIsPrintable ) +{ + OUString aString(SdResId(STR_MODIFYLAYER)); + SetComment(aString); +} + +void SdLayerModifyUndoAction::Undo() +{ + ::sd::DrawDocShell* pDocSh = mpDoc->GetDocSh(); + if( pDocSh ) + { + ::sd::DrawViewShell* pDrViewSh = dynamic_cast< ::sd::DrawViewShell*> ( pDocSh->GetViewShell() ); + if( pDrViewSh ) + { + pDrViewSh->ModifyLayer( mpLayer, maOldLayerName, maOldLayerTitle, maOldLayerDesc, mbOldIsVisible, mbOldIsLocked, mbOldIsPrintable ); + } + } +} + +void SdLayerModifyUndoAction::Redo() +{ + ::sd::DrawDocShell* pDocSh = mpDoc->GetDocSh(); + if( pDocSh ) + { + ::sd::DrawViewShell* pDrViewSh = dynamic_cast< ::sd::DrawViewShell* >( pDocSh->GetViewShell() ); + if( pDrViewSh ) + { + pDrViewSh->ModifyLayer( mpLayer, maNewLayerName, maNewLayerTitle, maNewLayerDesc, mbNewIsVisible, mbNewIsLocked, mbNewIsPrintable ); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/undopage.cxx b/sd/source/ui/func/undopage.cxx new file mode 100644 index 000000000..174747bf6 --- /dev/null +++ b/sd/source/ui/func/undopage.cxx @@ -0,0 +1,99 @@ +/* -*- 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 <undopage.hxx> +#include <sdpage.hxx> + + +SdPageFormatUndoAction::~SdPageFormatUndoAction() +{ +} + +void SdPageFormatUndoAction::Undo() +{ + ::tools::Rectangle aOldBorderRect(mnOldLeft, mnOldUpper, mnOldRight, mnOldLower); + mpPage->ScaleObjects(maOldSize, aOldBorderRect, mbNewScale); + mpPage->SetSize(maOldSize); + mpPage->SetLeftBorder(mnOldLeft); + mpPage->SetRightBorder(mnOldRight); + mpPage->SetUpperBorder(mnOldUpper); + mpPage->SetLowerBorder(mnOldLower); + mpPage->SetOrientation(meOldOrientation); + mpPage->SetPaperBin( mnOldPaperBin ); + + mpPage->SetBackgroundFullSize( mbOldFullSize ); + if( !mpPage->IsMasterPage() ) + static_cast<SdPage&>( mpPage->TRG_GetMasterPage() ).SetBackgroundFullSize( mbOldFullSize ); + +} + +void SdPageFormatUndoAction::Redo() +{ + ::tools::Rectangle aNewBorderRect(mnNewLeft, mnNewUpper, mnNewRight, mnNewLower); + mpPage->ScaleObjects(maNewSize, aNewBorderRect, mbNewScale); + mpPage->SetSize(maNewSize); + mpPage->SetLeftBorder(mnNewLeft); + mpPage->SetRightBorder(mnNewRight); + mpPage->SetUpperBorder(mnNewUpper); + mpPage->SetLowerBorder(mnNewLower); + mpPage->SetOrientation(meNewOrientation); + mpPage->SetPaperBin( mnNewPaperBin ); + + mpPage->SetBackgroundFullSize( mbNewFullSize ); + if( !mpPage->IsMasterPage() ) + static_cast<SdPage&>( mpPage->TRG_GetMasterPage() ).SetBackgroundFullSize( mbNewFullSize ); + +} + +SdPageLRUndoAction::~SdPageLRUndoAction() +{ +} + +void SdPageLRUndoAction::Undo() +{ + mpPage->SetLeftBorder(mnOldLeft); + mpPage->SetRightBorder(mnOldRight); +} + +void SdPageLRUndoAction::Redo() +{ + mpPage->SetLeftBorder(mnNewLeft); + mpPage->SetRightBorder(mnNewRight); +} + +SdPageULUndoAction::~SdPageULUndoAction() +{ +} + +void SdPageULUndoAction::Undo() +{ + mpPage->SetUpperBorder(mnOldUpper); + mpPage->SetLowerBorder(mnOldLower); +} + +/** + * UL-Redo() + */ +void SdPageULUndoAction::Redo() +{ + mpPage->SetUpperBorder(mnNewUpper); + mpPage->SetLowerBorder(mnNewLower); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/unmovss.cxx b/sd/source/ui/func/unmovss.cxx new file mode 100644 index 000000000..d21f83b39 --- /dev/null +++ b/sd/source/ui/func/unmovss.cxx @@ -0,0 +1,95 @@ +/* -*- 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 <unmovss.hxx> +#include <drawdoc.hxx> +#include <stlsheet.hxx> +#include <stlpool.hxx> + +SdMoveStyleSheetsUndoAction::SdMoveStyleSheetsUndoAction( SdDrawDocument* pTheDoc, StyleSheetCopyResultVector& rTheStyles, bool bInserted) +: SdUndoAction(pTheDoc) +, mbMySheets( !bInserted ) +{ + maStyles.swap( rTheStyles ); + + maListOfChildLists.resize( maStyles.size() ); + // create list with lists of style sheet children + std::size_t i = 0; + for (const auto& a : maStyles) + { + maListOfChildLists[i++] = SdStyleSheetPool::CreateChildList(a.m_xStyleSheet.get()); + } +} + +void SdMoveStyleSheetsUndoAction::Undo() +{ + SfxStyleSheetBasePool* pPool = mpDoc->GetStyleSheetPool(); + + if (mbMySheets) + { + // the styles have to be inserted in the pool + + // first insert all styles to the pool + for (auto& a : maStyles) + { + if (!a.m_bCreatedByCopy) // tdf#119259, existed before this action, so leave it alone + continue; + pPool->Insert(a.m_xStyleSheet.get()); + } + + // now assign the children again + std::vector< SdStyleSheetVector >::iterator childlistiter( maListOfChildLists.begin() ); + for (const auto& a : maStyles) + { + OUString aParent(a.m_xStyleSheet->GetName()); + for( auto& rxChild : *childlistiter ) + { + rxChild->SetParent(aParent); + } + ++childlistiter; + } + } + else + { + // remove the styles again from the pool + for (auto& a : maStyles) + { + if (!a.m_bCreatedByCopy) // tdf#119259, existed before this action, so leave it alone + continue; + pPool->Remove(a.m_xStyleSheet.get()); + } + } + mbMySheets = !mbMySheets; +} + +void SdMoveStyleSheetsUndoAction::Redo() +{ + Undo(); +} + +SdMoveStyleSheetsUndoAction::~SdMoveStyleSheetsUndoAction() +{ +} + +OUString SdMoveStyleSheetsUndoAction::GetComment() const +{ + return OUString(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/unoaprms.cxx b/sd/source/ui/func/unoaprms.cxx new file mode 100644 index 000000000..3bf7d98a6 --- /dev/null +++ b/sd/source/ui/func/unoaprms.cxx @@ -0,0 +1,96 @@ +/* -*- 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 <drawdoc.hxx> +#include <unoaprms.hxx> +#include <anminfo.hxx> + + +void SdAnimationPrmsUndoAction::Undo() +{ + // no new info created: restore data + if (!bInfoCreated) + { + SdDrawDocument* pDoc(dynamic_cast< SdDrawDocument* >(&pObject->getSdrModelFromSdrObject())); + SdAnimationInfo* pInfo = pDoc ? SdDrawDocument::GetAnimationInfo(pObject) : nullptr; + if (pInfo) + { + pInfo->mbActive = bOldActive; + pInfo->meEffect = eOldEffect; + pInfo->meTextEffect = eOldTextEffect; + pInfo->meSpeed = eOldSpeed; + pInfo->mbDimPrevious = bOldDimPrevious; + pInfo->maDimColor = aOldDimColor; + pInfo->mbDimHide = bOldDimHide; + pInfo->mbSoundOn = bOldSoundOn; + pInfo->maSoundFile = aOldSoundFile; + pInfo->mbPlayFull = bOldPlayFull; + pInfo->meClickAction = eOldClickAction; + pInfo->SetBookmark( aOldBookmark ); + pInfo->mnVerb = nOldVerb; + + pInfo->meSecondEffect = eOldSecondEffect; + pInfo->meSecondSpeed = eOldSecondSpeed; + pInfo->mbSecondSoundOn = bOldSecondSoundOn; + pInfo->mbSecondPlayFull = bOldSecondPlayFull; + } + } + // info was created by action: delete info + else + { + pObject->DeleteUserData(0); + } + // force ModelHasChanged() in order to update effect window (animation order) + pObject->SetChanged(); + pObject->BroadcastObjectChange(); +} + +void SdAnimationPrmsUndoAction::Redo() +{ + SdAnimationInfo* pInfo = SdDrawDocument::GetShapeUserData(*pObject,true); + + pInfo->mbActive = bNewActive; + pInfo->meEffect = eNewEffect; + pInfo->meTextEffect = eNewTextEffect; + pInfo->meSpeed = eNewSpeed; + pInfo->mbDimPrevious = bNewDimPrevious; + pInfo->maDimColor = aNewDimColor; + pInfo->mbDimHide = bNewDimHide; + pInfo->mbSoundOn = bNewSoundOn; + pInfo->maSoundFile = aNewSoundFile; + pInfo->mbPlayFull = bNewPlayFull; + pInfo->meClickAction = eNewClickAction; + pInfo->SetBookmark( aNewBookmark ); + pInfo->mnVerb = nNewVerb; + + pInfo->meSecondEffect = eNewSecondEffect; + pInfo->meSecondSpeed = eNewSecondSpeed; + pInfo->mbSecondSoundOn = bNewSecondSoundOn; + pInfo->mbSecondPlayFull = bNewSecondPlayFull; + + // force ModelHasChanged() in order to update effect window (animation order) + pObject->SetChanged(); + pObject->BroadcastObjectChange(); +} + +SdAnimationPrmsUndoAction::~SdAnimationPrmsUndoAction() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/unprlout.cxx b/sd/source/ui/func/unprlout.cxx new file mode 100644 index 000000000..218883349 --- /dev/null +++ b/sd/source/ui/func/unprlout.cxx @@ -0,0 +1,73 @@ +/* -*- 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 <tools/debug.hxx> + +#include <unprlout.hxx> + +#include <strings.hrc> +#include <sdpage.hxx> +#include <sdresid.hxx> + + +SdPresentationLayoutUndoAction::SdPresentationLayoutUndoAction( + SdDrawDocument* pTheDoc, + const OUString& aTheOldLayoutName, + const OUString& aTheNewLayoutName, + AutoLayout eTheOldAutoLayout, + AutoLayout eTheNewAutoLayout, + bool bSet, + SdPage* pThePage): + SdUndoAction(pTheDoc) +{ + aOldLayoutName = aTheOldLayoutName; + aNewLayoutName = aTheNewLayoutName; + eOldAutoLayout = eTheOldAutoLayout; + eNewAutoLayout = eTheNewAutoLayout; + bSetAutoLayout = bSet; + + DBG_ASSERT(pThePage, "No Page set!"); + pPage = pThePage; + aComment = SdResId(STR_UNDO_SET_PRESLAYOUT); +} + +void SdPresentationLayoutUndoAction::Undo() +{ + pPage->SetPresentationLayout(aOldLayoutName, true, true, true); + if (bSetAutoLayout) + pPage->SetAutoLayout(eOldAutoLayout, true); +} + +void SdPresentationLayoutUndoAction::Redo() +{ + pPage->SetPresentationLayout(aNewLayoutName); + if (bSetAutoLayout) + pPage->SetAutoLayout(eNewAutoLayout, true); +} + +SdPresentationLayoutUndoAction::~SdPresentationLayoutUndoAction() +{ +} + +OUString SdPresentationLayoutUndoAction::GetComment() const +{ + return aComment; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/AccessibleDocumentViewBase.hxx b/sd/source/ui/inc/AccessibleDocumentViewBase.hxx new file mode 100644 index 000000000..0db25b689 --- /dev/null +++ b/sd/source/ui/inc/AccessibleDocumentViewBase.hxx @@ -0,0 +1,324 @@ +/* -*- 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_SD_SOURCE_UI_INC_ACCESSIBLEDOCUMENTVIEWBASE_HXX +#define INCLUDED_SD_SOURCE_UI_INC_ACCESSIBLEDOCUMENTVIEWBASE_HXX + +#include <editeng/AccessibleContextBase.hxx> +#include <editeng/AccessibleComponentBase.hxx> +#include <editeng/AccessibleSelectionBase.hxx> +#include "AccessibleViewForwarder.hxx" +#include <svx/AccessibleShapeTreeInfo.hxx> +#include <svx/IAccessibleViewForwarderListener.hxx> + +#include <com/sun/star/awt/XWindowListener.hpp> +#include <com/sun/star/awt/XFocusListener.hpp> +#include <com/sun/star/beans/XPropertyChangeListener.hpp> +#include <tools/link.hxx> + +#include <com/sun/star/accessibility/XAccessibleExtendedAttributes.hpp> + +#include "Window.hxx" + +namespace com::sun::star::accessibility { class XAccessible; } +namespace com::sun::star::frame { class XModel; } +namespace com::sun::star::awt { class XWindow; } + +class VclWindowEvent; + +namespace sd { +class ViewShell; +} + +namespace accessibility { + +/** Base class for the various document views of the Draw and + Impress applications. + + <p>The different view modes of the Draw and Impress applications + are made accessible by derived classes. When the view mode is + changed then the object representing the document view is + disposed and replaced by a new instance of the then appropriate + derived class.</p> + + <p>This base class also manages an optionally active accessible OLE + object. If you overwrite the <member>getAccessibleChildCount</member> + and <member>getAccessibleChild</member> methods then make sure to first + call the corresponding method of this class and adapt your child count + and indices accordingly. Only one active OLE object is allowed at a + time. This class does not listen for disposing calls at the moment + because it does not use the accessible OLE object directly and trusts on + getting informed through VCL window events.</p> + + <p>This class implements three kinds of listeners: + <ol><li>The property change listener is not used directly but exists as + convenience for derived classes. May be moved to those classes + instead.</li> + <li>As window listener it waits for changes of the window geometry and + forwards those as view forwarder changes.</li> + <li>As focus listener it keeps track of the focus to give this class and + derived classes the opportunity to set and remove the focus to/from + shapes.</li> + </ol> + </p> +*/ +class AccessibleDocumentViewBase + : public AccessibleContextBase, + public AccessibleComponentBase, + public AccessibleSelectionBase, + public IAccessibleViewForwarderListener, + public css::beans::XPropertyChangeListener, + public css::awt::XWindowListener, + public css::awt::XFocusListener, + public css::accessibility::XAccessibleExtendedAttributes +{ +public: + //===== internal ======================================================== + + /** Create a new object. Note that the caller has to call the + Init method directly after this constructor has finished. + @param pSdWindow + The window whose content is to be made accessible. + @param pViewShell + The view shell associated with the given window. + @param rxController + The controller from which to get the model. + @param rxParent + The accessible parent of the new object. Note that this parent does + not necessarily correspond with the parent of the given window. + */ + AccessibleDocumentViewBase ( + ::sd::Window* pSdWindow, + ::sd::ViewShell* pViewShell, + const css::uno::Reference<css::frame::XController>& rxController, + const css::uno::Reference<css::accessibility::XAccessible>& rxParent); + + virtual ~AccessibleDocumentViewBase() override; + + /** Initialize a new object. Call this method directly after creating a + new object. It finished the initialization begun in the constructor + but which needs a fully created object. + */ + virtual void Init(); + + /** Define callback for listening to window child events of VCL. + Listen for creation or destruction of OLE objects. + */ + DECL_LINK( WindowChildEventListener, VclWindowEvent&, void ); + + //===== IAccessibleViewForwarderListener ================================ + + /** A view forwarder change is signalled for instance when any of the + window events is received. Thus, instead of overriding the four + windowResized... methods it will be sufficient in most cases just to + override this method. + */ + virtual void ViewForwarderChanged() override; + + //===== XAccessibleContext ============================================== + + virtual css::uno::Reference<css::accessibility::XAccessible> SAL_CALL + getAccessibleParent() override; + + /** This implementation returns either 1 or 0 depending on whether there + is an active accessible OLE object or not. + */ + virtual sal_Int32 SAL_CALL + getAccessibleChildCount() override; + + /** This implementation either returns the active accessible OLE object + if it exists and the given index is 0 or throws an exception. + */ + virtual css::uno::Reference<css::accessibility::XAccessible> SAL_CALL + getAccessibleChild (sal_Int32 nIndex) override; + + //===== XAccessibleComponent ============================================ + + virtual css::uno::Reference<css::accessibility::XAccessible > SAL_CALL + getAccessibleAtPoint (const css::awt::Point& aPoint) override; + + virtual css::awt::Rectangle SAL_CALL getBounds() override; + + virtual css::awt::Point SAL_CALL getLocation() override; + + virtual css::awt::Point SAL_CALL getLocationOnScreen() override; + + virtual css::awt::Size SAL_CALL getSize() override; + + //===== XInterface ====================================================== + + virtual css::uno::Any SAL_CALL + queryInterface (const css::uno::Type & rType) override; + + virtual void SAL_CALL + acquire() + noexcept override; + + virtual void SAL_CALL + release() + noexcept override; + + //===== XServiceInfo ==================================================== + + /** Returns an identifier for the implementation of this object. + */ + virtual OUString SAL_CALL + getImplementationName() override; + + virtual css::uno::Sequence< OUString> SAL_CALL + getSupportedServiceNames() override; + + //===== XTypeProvider =================================================== + + virtual css::uno::Sequence< css::uno::Type> SAL_CALL + getTypes() override; + + //===== lang::XEventListener ============================================ + + virtual void SAL_CALL + disposing (const css::lang::EventObject& rEventObject) override; + + //===== XPropertyChangeListener ========================================= + + virtual void SAL_CALL + propertyChange (const css::beans::PropertyChangeEvent& rEventObject) override; + + //===== XWindowListener ================================================= + + virtual void SAL_CALL + windowResized (const css::awt::WindowEvent& e) override; + + virtual void SAL_CALL + windowMoved (const css::awt::WindowEvent& e) override; + + virtual void SAL_CALL + windowShown (const css::lang::EventObject& e) override; + + virtual void SAL_CALL + windowHidden (const css::lang::EventObject& e) override; + + //===== XFocusListener ================================================= + + virtual void SAL_CALL focusGained (const css::awt::FocusEvent& e) override; + virtual void SAL_CALL focusLost (const css::awt::FocusEvent& e) override; + //----------------------------xAttribute---------------------------- + virtual css::uno::Any SAL_CALL getExtendedAttributes() override; + ::sd::ViewShell* mpViewShell; +private: + + // return the member maMutex; + virtual ::osl::Mutex& + implGetMutex() override; + + // return ourself as context in default case + virtual css::uno::Reference< css::accessibility::XAccessibleContext > + implGetAccessibleContext() override; + + // return sal_False in default case + virtual bool + implIsSelected( sal_Int32 nAccessibleChildIndex ) override; + + // return nothing in default case + virtual void + implSelect( sal_Int32 nAccessibleChildIndex, bool bSelect ) override; + +protected: + /// The API window that is made accessible. + css::uno::Reference< css::awt::XWindow> + mxWindow; + + /// The controller of the window in which this view is displayed. + css::uno::Reference< css::frame::XController> + mxController; + + /// Model of the document. + css::uno::Reference < css::frame::XModel> + mxModel; + + // Bundle of information that is passed down the shape tree. + AccessibleShapeTreeInfo maShapeTreeInfo; + + /// The view forwarder passed to the children manager. + AccessibleViewForwarder maViewForwarder; + + /** Accessible OLE object. Set or removed by the + <member>SetAccessibleOLEObject</member> method. + */ + css::uno::Reference< css::accessibility::XAccessible> + mxAccessibleOLEObject; + + Link<VclWindowEvent&,void> maWindowLink; + + // This method is called from the component helper base class while + // disposing. + virtual void SAL_CALL disposing() override; + + /** Create a name string. The current name is not modified and, + therefore, no events are sent. This method is usually called once + by the <member>getAccessibleName</member> method of the base class. + @return + A name string. + */ + virtual OUString + CreateAccessibleName () override; + + /** This method is called when (after) the frame containing this + document has been activated. Can be used to send FOCUSED state + changes for the currently selected element. + + Note: Currently used as a substitute for FocusGained. Should be + renamed in the future. + */ + virtual void Activated(); + + /** This method is called when (before or after?) the frame containing + this document has been deactivated. Can be used to send FOCUSED + state changes for the currently selected element. + + Note: Currently used as a substitute for FocusLost. Should be + renamed in the future. + */ + virtual void Deactivated(); + + /** Set or remove the currently active accessible OLE object. + @param xOLEObject + If this is a valid reference then a child event is send that + informs the listeners of a new child. If there has already been + an active accessible OLE object then this is removed first and + appropriate events are sent. + + If this is an empty reference then the currently active + accessible OLE object (if there is one) is removed. + */ + void SetAccessibleOLEObject ( + const css::uno::Reference<css::accessibility::XAccessible>& xOLEObject); + +public: + void SwitchViewActivated() { Activated(); } + virtual sal_Int32 SAL_CALL getForeground( ) override; + + virtual sal_Int32 SAL_CALL getBackground( ) override; + virtual void impl_dispose(); +}; + +} // end of namespace accessibility + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/AccessibleDrawDocumentView.hxx b/sd/source/ui/inc/AccessibleDrawDocumentView.hxx new file mode 100644 index 000000000..202edd0ea --- /dev/null +++ b/sd/source/ui/inc/AccessibleDrawDocumentView.hxx @@ -0,0 +1,165 @@ +/* -*- 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 . + */ + +#pragma once + +#include "AccessibleDocumentViewBase.hxx" + +#include <com/sun/star/accessibility/XAccessibleGroupPosition.hpp> + +namespace accessibility { class AccessiblePageShape; } +namespace accessibility { class ChildrenManager; } + +namespace accessibility { + +/** This class makes draw documents in the general view modes + accessible. It passes all shapes on the current draw page to a + children manager and additionally creates a new shape that + represents the actual draw page. + + Please see the documentation of the base class for further + explanations of the individual methods. +*/ +class AccessibleDrawDocumentView final : + public AccessibleDocumentViewBase + ,public css::accessibility::XAccessibleGroupPosition +{ +public: + //===== internal ======================================================== + + AccessibleDrawDocumentView (::sd::Window* pSdWindow, + ::sd::ViewShell* pViewShell, + const css::uno::Reference<css::frame::XController>& rxController, + const css::uno::Reference<css::accessibility::XAccessible>& rxParent); + + virtual ~AccessibleDrawDocumentView() override; + + /** Complete the initialization begun in the constructor. + */ + virtual void Init() override; + + //===== IAccessibleViewForwarderListener ================================ + + virtual void ViewForwarderChanged() override; + + //===== XAccessibleContext ============================================== + + virtual sal_Int32 SAL_CALL + getAccessibleChildCount() override; + + virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL + getAccessibleChild (sal_Int32 nIndex) override; + + virtual OUString SAL_CALL + getAccessibleName() override; + + //===== lang::XEventListener ============================================ + + virtual void SAL_CALL + disposing (const css::lang::EventObject& rEventObject) override; + + //===== XPropertyChangeListener ========================================= + + virtual void SAL_CALL + propertyChange (const css::beans::PropertyChangeEvent& rEventObject) override; + //===== XInterface ====================================================== + + virtual css::uno::Any SAL_CALL + queryInterface (const css::uno::Type & rType) override; + + virtual void SAL_CALL + acquire() + noexcept override; + + virtual void SAL_CALL + release() + noexcept override; + + //===== XAccessibleGroupPosition ========================================= + virtual css::uno::Sequence< sal_Int32 > SAL_CALL + getGroupPosition( const css::uno::Any& rAny ) override; + virtual OUString SAL_CALL getObjectLink( const css::uno::Any& accoject ) override; + +private: + + //===== XServiceInfo ==================================================== + + virtual OUString SAL_CALL + getImplementationName() override; + + virtual css::uno::Sequence< OUString> SAL_CALL + getSupportedServiceNames() override; + + virtual bool + implIsSelected( sal_Int32 nAccessibleChildIndex ) override; + + /** Select or deselect the specified child or all children if the given + index has the special value ACCESSIBLE_SELECTION_CHILD_ALL. + Selecting or deselecting a child sets or resets the + <const>SELECTED</const> state and selects or deselects the UNO shape + being made accessible by the child. + @param nAccessibleChildIndex + Index of the child to select or deselect. If the parameter has + the value ACCESSIBLE_SELECTION_CHILD_ALL then all children are + selected or deselected. + @param bSelect + Indicates whether to select or deselect the specified child + reps. children. + */ + virtual void + implSelect( sal_Int32 nAccessibleChildIndex, bool bSelect ) override; + + ::sd::ViewShell* mpSdViewSh; + + /** This object manages the shapes of the represented draw page. It is + responsible to determine the visible shapes and create on demand the + accessible objects representing them. + */ + std::unique_ptr<ChildrenManager> mpChildrenManager; + + // This method is called from the component helper base class while + // disposing. + virtual void SAL_CALL disposing() override; + + /** Create a shape the represents the page as seen on the screen. + */ + rtl::Reference<AccessiblePageShape> CreateDrawPageShape(); + + /// Create an accessible name that contains the current view mode. + virtual OUString + CreateAccessibleName () override; + + /** Make sure that the currently focused shape sends a FOCUSED state + change event indicating that it has (regained) the focus. + */ + virtual void Activated() override; + + /** Make sure that the currently focused shape sends a FOCUSED state + change event indicating that it has lost the focus. + */ + virtual void Deactivated() override; + + virtual void impl_dispose() override; + + void UpdateAccessibleName(); +}; + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/AccessibleOutlineEditSource.hxx b/sd/source/ui/inc/AccessibleOutlineEditSource.hxx new file mode 100644 index 000000000..d13d27e97 --- /dev/null +++ b/sd/source/ui/inc/AccessibleOutlineEditSource.hxx @@ -0,0 +1,90 @@ +/* -*- 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 . + */ + +#pragma once + +#include <svl/SfxBroadcaster.hxx> +#include <svl/lstner.hxx> +#include <tools/link.hxx> +#include <editeng/unoedsrc.hxx> +#include <editeng/unoforou.hxx> +#include <editeng/unoviwou.hxx> + +class OutlinerView; +class SdrOutliner; +class SdrView; +namespace vcl { class Window; } + +namespace accessibility +{ + /** Implementation of the SvxEditSource interface in the SdOutlineView + + This class connects the SdOutlineView and its EditEngine + outliner with the AccessibleTextHelper, which provides all + necessary functionality to make the outliner text accessible + + @see SvxEditSource + @see SvxViewForwarder + */ + class AccessibleOutlineEditSource final : public SvxEditSource, public SvxViewForwarder, public SfxBroadcaster, public SfxListener + { + public: + /// Create an SvxEditSource interface for the given Outliner + AccessibleOutlineEditSource( + SdrOutliner& rOutliner, + SdrView& rView, + OutlinerView& rOutlView, + const vcl::Window& rViewWindow ); + virtual ~AccessibleOutlineEditSource() override; + + /// This method is disabled and always returns NULL + virtual std::unique_ptr<SvxEditSource> Clone() const override; + virtual SvxTextForwarder* GetTextForwarder() override; + virtual SvxViewForwarder* GetViewForwarder() override; + virtual SvxEditViewForwarder* GetEditViewForwarder( bool bCreate = false ) override; + virtual void UpdateData() override; + virtual SfxBroadcaster& GetBroadcaster() const override; + + // the view forwarder + virtual bool IsValid() const override; + virtual Point LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const override; + virtual Point PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const override; + + // SfxListener + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override; + + private: + AccessibleOutlineEditSource( const AccessibleOutlineEditSource& ) = delete; + AccessibleOutlineEditSource& operator=( const AccessibleOutlineEditSource& ) = delete; + + DECL_LINK( NotifyHdl, EENotify&, void ); + + SdrView& mrView; + const vcl::Window& mrWindow; + SdrOutliner* mpOutliner; + OutlinerView* mpOutlinerView; + + SvxOutlinerForwarder mTextForwarder; + SvxDrawOutlinerViewForwarder mViewForwarder; + + }; + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/AccessibleOutlineView.hxx b/sd/source/ui/inc/AccessibleOutlineView.hxx new file mode 100644 index 000000000..5fa1df7c5 --- /dev/null +++ b/sd/source/ui/inc/AccessibleOutlineView.hxx @@ -0,0 +1,119 @@ +/* -*- 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 . + */ + +#pragma once + +#include "AccessibleDocumentViewBase.hxx" +#include <svx/AccessibleTextHelper.hxx> + +namespace sd { class OutlineViewShell; } + +namespace accessibility { + +/** This class makes the Impress outline view accessible. + + Please see the documentation of the base class for further + explanations of the individual methods. This class is a mere + wrapper around the AccessibleTextHelper class; as basically the + Outline View is a big Outliner. +*/ +class AccessibleOutlineView final + : public AccessibleDocumentViewBase +{ +public: + AccessibleOutlineView ( + ::sd::Window* pSdWindow, + ::sd::OutlineViewShell* pViewShell, + const css::uno::Reference<css::frame::XController>& rxController, + const css::uno::Reference<css::accessibility::XAccessible>& rxParent); + + virtual ~AccessibleOutlineView() override; + + /** Complete the initialization begun in the constructor. + */ + virtual void Init() override; + + //===== IAccessibleViewForwarderListener ================================ + + virtual void ViewForwarderChanged() override; + + //===== XAccessibleContext ============================================== + + virtual sal_Int32 SAL_CALL + getAccessibleChildCount() override; + + virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL + getAccessibleChild (sal_Int32 nIndex) override; + virtual OUString SAL_CALL + getAccessibleName() override; + //===== XAccessibleEventBroadcaster ======================================== + + virtual void SAL_CALL + addAccessibleEventListener ( + const css::uno::Reference<css::accessibility::XAccessibleEventListener >& xListener) override; + + virtual void SAL_CALL + removeAccessibleEventListener ( + const css::uno::Reference<css::accessibility::XAccessibleEventListener >& xListener) override; + + //===== XServiceInfo ==================================================== + + /** Returns an identifier for the implementation of this object. + */ + virtual OUString SAL_CALL + getImplementationName() override; + + //===== lang::XEventListener ============================================ + + using AccessibleDocumentViewBase::disposing; + + //===== XPropertyChangeListener ========================================= + + virtual void SAL_CALL + propertyChange (const css::beans::PropertyChangeEvent& rEventObject) override; + +private: + + // overridden to detect focus changes + virtual void Activated() override; + + // overridden to detect focus changes + virtual void Deactivated() override; + + // declared, but not defined + AccessibleOutlineView( const AccessibleOutlineView& ); + AccessibleOutlineView& operator= ( const AccessibleOutlineView& ); + + // This method is called from the component helper base class while disposing. + virtual void SAL_CALL disposing() override; + + /// Create an accessible name that contains the current view mode. + virtual OUString + CreateAccessibleName () override; + + /// Invalidate text helper, updates visible children + void UpdateChildren(); + + AccessibleTextHelper maTextHelper; + +}; + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/AccessiblePageShape.hxx b/sd/source/ui/inc/AccessiblePageShape.hxx new file mode 100644 index 000000000..164fb96fe --- /dev/null +++ b/sd/source/ui/inc/AccessiblePageShape.hxx @@ -0,0 +1,117 @@ +/* -*- 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 . + */ + +#pragma once + +#include <svx/AccessibleShape.hxx> + +namespace com::sun::star::accessibility { class XAccessible; } +namespace com::sun::star::drawing { class XDrawPage; } +namespace accessibility { class AccessibleShapeTreeInfo; } + +namespace accessibility { + +/** A page shape represents the actual page as seen on the screen. +*/ +class AccessiblePageShape + : public AccessibleShape +{ +public: + //===== internal ======================================================== + + /** Create a new accessible object that makes the given shape accessible. + @param rxParent + The accessible parent object. It will be used, for example when + the <member>getIndexInParent</member> method is called. + @param rShapeTreeInfo + Bundle of information passed to this shape and all of its descendants. + @attention + Always call the <member>init</member> method after creating a + new accessible shape. This is one way to overcome the potential + problem of registering the new object with e.g. event + broadcasters. That would delete the new object if a broadcaster + would not keep a strong reference to the new object. + */ + AccessiblePageShape ( + const css::uno::Reference<css::drawing::XDrawPage>& rxPage, + const css::uno::Reference<css::accessibility::XAccessible>& rxParent, + const AccessibleShapeTreeInfo& rShapeTreeInfo); + + virtual ~AccessiblePageShape() override; + + //===== XAccessibleContext ============================================== + + /// Returns always 0 because there can be no children. + virtual sal_Int32 SAL_CALL + getAccessibleChildCount() override; + + /** Return the specified child. + @param nIndex + Index of the requested child. + @return + Reference of the requested child which is the accessible object + of a visible shape. + @throws IndexOutOfBoundsException + Throws always an exception because there are no children. + */ + virtual css::uno::Reference<css::accessibility::XAccessible> SAL_CALL + getAccessibleChild (sal_Int32 nIndex) override; + + //===== XAccessibleComponent ============================================ + + virtual css::awt::Rectangle SAL_CALL getBounds() override; + + virtual sal_Int32 SAL_CALL getForeground() override; + + virtual sal_Int32 SAL_CALL getBackground() override; + + //===== XComponent ====================================================== + + virtual void SAL_CALL + dispose() override; + + //===== XServiceInfo ==================================================== + + virtual OUString SAL_CALL + getImplementationName() override; + + virtual css::uno::Sequence< OUString> SAL_CALL + getSupportedServiceNames() override; + + using AccessibleShape::disposing; + +protected: + /** Create a base name string that contains the accessible name. + */ + virtual OUString + CreateAccessibleBaseName() override; + + virtual OUString + CreateAccessibleName() override; + +private: + css::uno::Reference<css::drawing::XDrawPage> mxPage; + + AccessiblePageShape (const AccessiblePageShape&) = delete; + AccessibleShape& operator= (const AccessiblePageShape&) = delete; +}; + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/AccessiblePresentationGraphicShape.hxx b/sd/source/ui/inc/AccessiblePresentationGraphicShape.hxx new file mode 100644 index 000000000..91e573835 --- /dev/null +++ b/sd/source/ui/inc/AccessiblePresentationGraphicShape.hxx @@ -0,0 +1,60 @@ +/* -*- 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 . + */ + +#pragma once + +#include <svx/AccessibleGraphicShape.hxx> + +namespace accessibility { class AccessibleShapeInfo; } +namespace accessibility { class AccessibleShapeTreeInfo; } + +namespace accessibility { + +/** This class makes Impress shapes accessible. +*/ +class AccessiblePresentationGraphicShape + : public AccessibleGraphicShape +{ +public: + //===== internal ======================================================== + AccessiblePresentationGraphicShape ( + const AccessibleShapeInfo& rShapeInfo, + const AccessibleShapeTreeInfo& rShapeTreeInfo); + virtual ~AccessiblePresentationGraphicShape() override; + + //===== XServiceInfo ==================================================== + + /** Returns an identifier for the implementation of this object. + */ + virtual OUString SAL_CALL + getImplementationName() override; + + //===== internal ======================================================== + + /// Create a name string that contains the accessible name. + virtual OUString + CreateAccessibleBaseName () override; + + /// Return this object's role. + virtual sal_Int16 SAL_CALL getAccessibleRole () override; +}; + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/AccessiblePresentationOLEShape.hxx b/sd/source/ui/inc/AccessiblePresentationOLEShape.hxx new file mode 100644 index 000000000..a8ac60deb --- /dev/null +++ b/sd/source/ui/inc/AccessiblePresentationOLEShape.hxx @@ -0,0 +1,57 @@ +/* -*- 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 . + */ + +#pragma once + +#include <svx/AccessibleOLEShape.hxx> + +namespace accessibility { + +/** This class makes Impress shapes accessible. +*/ +class AccessiblePresentationOLEShape + : public AccessibleOLEShape +{ +public: + //===== internal ======================================================== + AccessiblePresentationOLEShape ( + const AccessibleShapeInfo& rShapeInfo, + const AccessibleShapeTreeInfo& rShapeTreeInfo); + virtual ~AccessiblePresentationOLEShape() override; + + //===== XServiceInfo ==================================================== + + /** Returns an identifier for the implementation of this object. + */ + virtual OUString SAL_CALL + getImplementationName() override; + + //===== internal ======================================================== + + /// Create a name string that contains the accessible name. + virtual OUString + CreateAccessibleBaseName () override; + + /// Return this object's role. + virtual sal_Int16 SAL_CALL getAccessibleRole () override; +}; + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/AccessiblePresentationShape.hxx b/sd/source/ui/inc/AccessiblePresentationShape.hxx new file mode 100644 index 000000000..4a6447ae9 --- /dev/null +++ b/sd/source/ui/inc/AccessiblePresentationShape.hxx @@ -0,0 +1,61 @@ +/* -*- 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 . + */ + +#pragma once + +#include <svx/AccessibleShape.hxx> + +namespace accessibility { + +/** This class makes Impress shapes accessible. +*/ +class AccessiblePresentationShape + : public AccessibleShape +{ +public: + //===== internal ======================================================== + AccessiblePresentationShape ( + const AccessibleShapeInfo& rShapeInfo, + const AccessibleShapeTreeInfo& rShapeTreeInfo); + virtual ~AccessiblePresentationShape() override; + + //===== XServiceInfo ==================================================== + + /** Returns an identifier for the implementation of this object. + */ + virtual OUString SAL_CALL + getImplementationName() override; + + //===== internal ======================================================== + + /// Create a name string that contains the accessible name. + virtual OUString + CreateAccessibleBaseName () override; + + OUString GetStyle() const override; + +private: + AccessiblePresentationShape (const AccessiblePresentationShape&) = delete; + + AccessiblePresentationShape& operator= (const AccessiblePresentationShape&) = delete; +}; + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/AccessibleSlideSorterObject.hxx b/sd/source/ui/inc/AccessibleSlideSorterObject.hxx new file mode 100644 index 000000000..6da56a152 --- /dev/null +++ b/sd/source/ui/inc/AccessibleSlideSorterObject.hxx @@ -0,0 +1,189 @@ +/* -*- 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 . + */ + +#pragma once + +#include <comphelper/compbase.hxx> +#include <com/sun/star/accessibility/XAccessible.hpp> +#include <com/sun/star/accessibility/XAccessibleContext.hpp> +#include <com/sun/star/accessibility/XAccessibleComponent.hpp> +#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> + +class SdPage; +namespace sd::slidesorter { class SlideSorter; } + +namespace accessibility { + +typedef comphelper::WeakComponentImplHelper< + css::accessibility::XAccessible, + css::accessibility::XAccessibleEventBroadcaster, + css::accessibility::XAccessibleContext, + css::accessibility::XAccessibleComponent, + css::lang::XServiceInfo > AccessibleSlideSorterObjectBase; + +/** This class makes page objects of the slide sorter accessible. +*/ +class AccessibleSlideSorterObject + : public AccessibleSlideSorterObjectBase +{ +public: + /** Create a new object that represents a page object in the slide + sorter. + @param rxParent + The accessible parent. + @param rSlideSorter + The slide sorter whose model manages the page. + @param nPageNumber + The number of the page in the range [0,nPageCount). + */ + AccessibleSlideSorterObject( + const css::uno::Reference<css::accessibility::XAccessible >& rxParent, + ::sd::slidesorter::SlideSorter& rSlideSorter, + sal_uInt16 nPageNumber); + virtual ~AccessibleSlideSorterObject() override; + + /** Return the page that is made accessible by the called object. + */ + SdPage* GetPage() const; + + /** The page number as given to the constructor. + */ + sal_uInt16 GetPageNumber() const { return mnPageNumber;} + + void FireAccessibleEvent ( + short nEventId, + const css::uno::Any& rOldValue, + const css::uno::Any& rNewValue); + + virtual void disposing(std::unique_lock<std::mutex>&) override; + + //===== XAccessible ======================================================= + + virtual css::uno::Reference<css::accessibility::XAccessibleContext > SAL_CALL + getAccessibleContext() override; + + //===== XAccessibleEventBroadcaster ======================================= + virtual void SAL_CALL + addAccessibleEventListener( + const css::uno::Reference<css::accessibility::XAccessibleEventListener >& rxListener) override; + + virtual void SAL_CALL + removeAccessibleEventListener( + const css::uno::Reference<css::accessibility::XAccessibleEventListener >& rxListener ) override; + + //===== XAccessibleContext ============================================== + + virtual sal_Int32 SAL_CALL + getAccessibleChildCount() override; + + virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL + getAccessibleChild (sal_Int32 nIndex) override; + + virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL + getAccessibleParent() override; + + virtual sal_Int32 SAL_CALL + getAccessibleIndexInParent() override; + + virtual sal_Int16 SAL_CALL + getAccessibleRole() override; + + virtual OUString SAL_CALL + getAccessibleDescription() override; + + virtual OUString SAL_CALL + getAccessibleName() override; + + virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet> SAL_CALL + getAccessibleRelationSet() override; + + virtual css::uno::Reference< css::accessibility::XAccessibleStateSet> SAL_CALL + getAccessibleStateSet() override; + + virtual css::lang::Locale SAL_CALL + getLocale() override; + + //===== XAccessibleComponent ================================================ + + virtual sal_Bool SAL_CALL containsPoint ( + const css::awt::Point& aPoint) override; + + virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL + getAccessibleAtPoint ( + const css::awt::Point& aPoint) override; + + virtual css::awt::Rectangle SAL_CALL getBounds() override; + + virtual css::awt::Point SAL_CALL getLocation() override; + + virtual css::awt::Point SAL_CALL getLocationOnScreen() override; + + virtual css::awt::Size SAL_CALL getSize() override; + + virtual void SAL_CALL grabFocus() override; + + virtual sal_Int32 SAL_CALL getForeground() override; + + virtual sal_Int32 SAL_CALL getBackground() override; + + //===== XServiceInfo ==================================================== + + /** Returns an identifier for the implementation of this object. + */ + virtual OUString SAL_CALL + getImplementationName() override; + + /** Return whether the specified service is supported by this class. + */ + virtual sal_Bool SAL_CALL + supportsService (const OUString& sServiceName) override; + + /** Returns a list of all supported services. + */ + virtual css::uno::Sequence< OUString> SAL_CALL + getSupportedServiceNames() override; + +private: + css::uno::Reference<css::accessibility::XAccessible> mxParent; + sal_uInt16 mnPageNumber; + ::sd::slidesorter::SlideSorter& mrSlideSorter; + sal_uInt32 mnClientId; + + /** Check whether or not the object has been disposed (or is in the + state of being disposed). If that is the case then + DisposedException is thrown to inform the (indirect) caller of the + foul deed. + + @throws css::lang::DisposedException + */ + void ThrowIfDisposed(); + + /** Check whether or not the object has been disposed (or is in the + state of being disposed). + + @return sal_True, if the object is disposed or in the course + of being disposed. Otherwise, sal_False is returned. + */ + bool IsDisposed() const; +}; + +} // end of namespace ::accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/AccessibleSlideSorterView.hxx b/sd/source/ui/inc/AccessibleSlideSorterView.hxx new file mode 100644 index 000000000..85003b72d --- /dev/null +++ b/sd/source/ui/inc/AccessibleSlideSorterView.hxx @@ -0,0 +1,255 @@ +/* -*- 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 . + */ + +#pragma once + +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/compbase.hxx> +#include <com/sun/star/accessibility/XAccessible.hpp> +#include <com/sun/star/accessibility/XAccessibleContext.hpp> +#include <com/sun/star/accessibility/XAccessibleComponent.hpp> +#include <com/sun/star/accessibility/XAccessibleSelection.hpp> +#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <vcl/vclptr.hxx> +#include <vcl/window.hxx> + +#include <memory> + +namespace sd::slidesorter { class SlideSorter; } + +namespace accessibility { + +class AccessibleSlideSorterObject; + +typedef ::cppu::WeakComponentImplHelper< + css::accessibility::XAccessible, + css::accessibility::XAccessibleEventBroadcaster, + css::accessibility::XAccessibleContext, + css::accessibility::XAccessibleComponent, + css::accessibility::XAccessibleSelection, + css::lang::XServiceInfo + > AccessibleSlideSorterViewBase; + +/** This class makes the SlideSorterViewShell accessible. It uses objects + of the AccessibleSlideSorterObject class to the make the page objects + accessible. +*/ +class AccessibleSlideSorterView + : public cppu::BaseMutex, + public AccessibleSlideSorterViewBase +{ +public: + AccessibleSlideSorterView( + ::sd::slidesorter::SlideSorter& rSlideSorter, + vcl::Window* pParentWindow); + + void Init(); + + virtual ~AccessibleSlideSorterView() override; + + /** This method acts like a dispose call. It sends a disposing to all + of its listeners. It may be called twice. + */ + void Destroyed(); + + void FireAccessibleEvent ( + short nEventId, + const css::uno::Any& rOldValue, + const css::uno::Any& rNewValue); + + virtual void SAL_CALL disposing() override; + + /** Return the implementation object of the specified child. + @param nIndex + Index of the child for which to return the implementation object. + */ + AccessibleSlideSorterObject* GetAccessibleChildImplementation (sal_Int32 nIndex); + + //===== XAccessible ======================================================= + + virtual css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL + getAccessibleContext() override; + + //===== XAccessibleEventBroadcaster ======================================= + virtual void SAL_CALL + addAccessibleEventListener( + const css::uno::Reference< css::accessibility::XAccessibleEventListener >& rxListener) override; + + virtual void SAL_CALL + removeAccessibleEventListener( + const css::uno::Reference< css::accessibility::XAccessibleEventListener >& rxListener ) override; + + //===== XAccessibleContext ============================================== + + /// Return the number of currently visible children. + virtual sal_Int32 SAL_CALL + getAccessibleChildCount() override; + + /// Return the specified child or throw exception. + virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL + getAccessibleChild (sal_Int32 nIndex) override; + + /// Return a reference to the parent. + virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL + getAccessibleParent() override; + + /// Return this objects index among the parents children. + virtual sal_Int32 SAL_CALL + getAccessibleIndexInParent() override; + + /// Return this object's role. + virtual sal_Int16 SAL_CALL + getAccessibleRole() override; + + /// Return this object's description. + virtual OUString SAL_CALL + getAccessibleDescription() override; + + /// Return the object's current name. + virtual OUString SAL_CALL + getAccessibleName() override; + + /// Return NULL to indicate that an empty relation set. + virtual css::uno::Reference< + css::accessibility::XAccessibleRelationSet> SAL_CALL + getAccessibleRelationSet() override; + + /// Return the set of current states. + virtual css::uno::Reference< + css::accessibility::XAccessibleStateSet> SAL_CALL + getAccessibleStateSet() override; + + /** Return the parents locale or throw exception if this object has no + parent yet/anymore. + */ + virtual css::lang::Locale SAL_CALL + getLocale() override; + + //===== XAccessibleComponent ================================================ + + /** The default implementation uses the result of + <member>getBounds</member> to determine whether the given point lies + inside this object. + */ + virtual sal_Bool SAL_CALL containsPoint ( + const css::awt::Point& aPoint) override; + + /** The default implementation returns an empty reference. + */ + virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL + getAccessibleAtPoint ( + const css::awt::Point& aPoint) override; + + /** The default implementation returns an empty rectangle. + */ + virtual css::awt::Rectangle SAL_CALL getBounds() override; + + /** The default implementation uses the result of + <member>getBounds</member> to determine the location. + */ + virtual css::awt::Point SAL_CALL getLocation() override; + + /** The default implementation returns an empty position, i.e. the + * result of the default constructor of <type>css::awt::Point</type>. + */ + virtual css::awt::Point SAL_CALL getLocationOnScreen() override; + + /** The default implementation uses the result of + <member>getBounds</member> to determine the size. + */ + virtual css::awt::Size SAL_CALL getSize() override; + + /** The default implementation does nothing. + */ + virtual void SAL_CALL grabFocus() override; + + /** Returns black as the default foreground color. + */ + virtual sal_Int32 SAL_CALL getForeground() override; + + /** Returns white as the default background color. + */ + virtual sal_Int32 SAL_CALL getBackground() override; + + //===== XAccessibleSelection ============================================== + + virtual void SAL_CALL + selectAccessibleChild (sal_Int32 nChildIndex) override; + + virtual sal_Bool SAL_CALL + isAccessibleChildSelected( sal_Int32 nChildIndex ) override; + + virtual void SAL_CALL + clearAccessibleSelection( ) override; + + virtual void SAL_CALL + selectAllAccessibleChildren( ) override; + + virtual sal_Int32 SAL_CALL + getSelectedAccessibleChildCount( ) override; + + virtual css::uno::Reference< + css::accessibility::XAccessible > SAL_CALL + getSelectedAccessibleChild( sal_Int32 nSelectedChildIndex ) override; + + virtual void SAL_CALL + deselectAccessibleChild( sal_Int32 nSelectedChildIndex ) override; + + //===== XServiceInfo ==================================================== + + /** Returns an identifier for the implementation of this object. + */ + virtual OUString SAL_CALL + getImplementationName() override; + + /** Return whether the specified service is supported by this class. + */ + virtual sal_Bool SAL_CALL + supportsService (const OUString& sServiceName) override; + + /** Returns a list of all supported services. + */ + virtual css::uno::Sequence< OUString> SAL_CALL + getSupportedServiceNames() override; + + void SwitchViewActivated(); +private: + class Implementation; + ::std::unique_ptr<Implementation> mpImpl; + + ::sd::slidesorter::SlideSorter& mrSlideSorter; + + sal_uInt32 mnClientId; + + VclPtr<vcl::Window> mpContentWindow; + + /** Check whether or not the object has been disposed (or is in the + state of being disposed). If that is the case then + DisposedException is thrown to inform the (indirect) caller of the + foul deed. + + @throws css::lang::DisposedException + */ + void ThrowIfDisposed(); +}; + +} // end of namespace ::accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/AccessibleViewForwarder.hxx b/sd/source/ui/inc/AccessibleViewForwarder.hxx new file mode 100644 index 000000000..c791921e6 --- /dev/null +++ b/sd/source/ui/inc/AccessibleViewForwarder.hxx @@ -0,0 +1,92 @@ +/* -*- 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 . + */ + +#pragma once + +#include <svx/IAccessibleViewForwarder.hxx> + +class SdrPaintView; +class OutputDevice; + +namespace accessibility +{ +/** <p>This class provides the means to transform between internal coordinates + and screen coordinates without giving direct access to the underlying + view. It represents a certain window. A call to + <method>GetVisArea</method> returns the corresponding visible + rectangle.</p> + + @attention + Note, that modifications of the underlying view that lead to + different transformations between internal and screen coordinates or + change the validity of the forwarder have to be signaled separately. +*/ +class AccessibleViewForwarder final : public IAccessibleViewForwarder +{ +public: + //===== internal ======================================================== + + AccessibleViewForwarder(SdrPaintView* pView, const OutputDevice& rDevice); + + virtual ~AccessibleViewForwarder() override; + + //===== IAccessibleViewforwarder ======================================== + + /** Returns the area of the underlying document that is visible in the + * corresponding window. + + @return + The rectangle of the visible part of the document. + */ + virtual ::tools::Rectangle GetVisibleArea() const override; + + /** Transform the specified point from internal coordinates to an + absolute screen position. + + @param rPoint + Point in internal coordinates. + + @return + The same point but in screen coordinates relative to the upper + left corner of the (current) screen. + */ + virtual Point LogicToPixel(const Point& rPoint) const override; + + /** Transform the specified size from internal coordinates to a screen + * position. + + @param rSize + Size in internal coordinates. + + @return + The same size but in screen coordinates. + */ + virtual Size LogicToPixel(const Size& rSize) const override; + +private: + SdrPaintView* mpView; + sal_uInt16 mnWindowId; + + AccessibleViewForwarder(AccessibleViewForwarder const&) = delete; + AccessibleViewForwarder& operator=(AccessibleViewForwarder const&) = delete; +}; + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/AnimationChildWindow.hxx b/sd/source/ui/inc/AnimationChildWindow.hxx new file mode 100644 index 000000000..1223dfdbd --- /dev/null +++ b/sd/source/ui/inc/AnimationChildWindow.hxx @@ -0,0 +1,45 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/childwin.hxx> +#include <sal/types.h> + +namespace vcl { class Window; } +class SfxBindings; + +namespace sd { + +class AnimationChildWindow + : public SfxChildWindow +{ +public: + AnimationChildWindow( + vcl::Window*, + sal_uInt16, + SfxBindings*, + SfxChildWinInfo*); + + SFX_DECL_CHILDWINDOW_WITHID(AnimationChildWindow); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/BezierObjectBar.hxx b/sd/source/ui/inc/BezierObjectBar.hxx new file mode 100644 index 000000000..a030576eb --- /dev/null +++ b/sd/source/ui/inc/BezierObjectBar.hxx @@ -0,0 +1,51 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/shell.hxx> +#include <glob.hxx> + +namespace sd +{ +class View; +class ViewShell; + +class BezierObjectBar final : public SfxShell +{ +public: + SFX_DECL_INTERFACE(SD_IF_SDDRAWBEZIEROBJECTBAR) + + BezierObjectBar(ViewShell* pSdViewShell, View* pSdView); + virtual ~BezierObjectBar() override; + + void GetAttrState(SfxItemSet& rSet); + void Execute(SfxRequest& rReq); + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + + View* mpView; + ViewShell* mpViewSh; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/BreakDlg.hxx b/sd/source/ui/inc/BreakDlg.hxx new file mode 100644 index 000000000..ee2a8b15d --- /dev/null +++ b/sd/source/ui/inc/BreakDlg.hxx @@ -0,0 +1,64 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/basedlgs.hxx> +#include <sfx2/progress.hxx> +#include <svx/svdetc.hxx> +#include <vcl/idle.hxx> + +namespace sd +{ +class DrawDocShell; +class DrawView; + +/** + * dialog to break meta files + */ +class BreakDlg : public SfxDialogController +{ +public: + BreakDlg(weld::Window* pWindow, DrawView* pDrView, DrawDocShell* pShell, + sal_uLong nSumActionCount, sal_uLong nObjCount); + + virtual short run() override; + +private: + std::unique_ptr<weld::Label> m_xFiObjInfo; + std::unique_ptr<weld::Label> m_xFiActInfo; + std::unique_ptr<weld::Label> m_xFiInsInfo; + std::unique_ptr<weld::Button> m_xBtnCancel; + + DrawView* m_pDrView; + + bool m_bCancel; + + Idle m_aUpdateIdle; + std::unique_ptr<SvdProgressInfo> m_xProgrInfo; + std::unique_ptr<SfxProgress> m_xProgress; + + DECL_LINK(CancelButtonHdl, weld::Button&, void); + DECL_LINK(UpDate, void*, bool); + DECL_LINK(InitialUpdate, Timer*, void); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/BulletAndPositionDlg.hxx b/sd/source/ui/inc/BulletAndPositionDlg.hxx new file mode 100644 index 000000000..6dde73753 --- /dev/null +++ b/sd/source/ui/inc/BulletAndPositionDlg.hxx @@ -0,0 +1,157 @@ +/* -*- 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 . + */ + +#pragma once + +#include <vector> +#include <memory> + +#include <editeng/numdef.hxx> +#include <editeng/svxenum.hxx> +#include <vcl/weld.hxx> +#include "View.hxx" +#include <cui/numberingpreview.hxx> + +#define MN_GALLERY_ENTRY 100 + +class ColorListBox; +class SvxNumValueSet; +class SvxNumRule; +class SvxBmpNumValueSet; +class SvxBrushItem; +class SdDrawDocument; + +namespace sd +{ +class View; +} + +/// Main class for handling the bullets, numbering format and their position. +class SvxBulletAndPositionDlg : public weld::GenericDialogController +{ + OUString m_sNumCharFmtName; + + Timer aInvalidateTimer; + + std::unique_ptr<SvxNumRule> pActNum; + std::unique_ptr<SvxNumRule> pSaveNum; + const SfxItemSet& rFirstStateSet; + + Size aInitSize[SVX_MAX_NUM]; + + bool bLastWidthModified : 1; + bool bModified : 1; + bool bInInitControl : 1; // workaround for Modify-error, is said to be corrected from 391 on + bool bLabelAlignmentPosAndSpaceModeActive; + bool bApplyToMaster; + + std::vector<OUString> aGrfNames; + vcl::Font aActBulletFont; + + sal_uInt8 nBullet; + sal_uInt16 nActNumLvl; + weld::Window* p_Window; + TypedWhichId<SvxNumBulletItem> nNumItemId; + MapUnit eCoreUnit; + + SvxNumberingPreview m_aPreviewWIN; + std::unique_ptr<weld::Widget> m_xGrid; + std::unique_ptr<weld::TreeView> m_xLevelLB; + std::unique_ptr<weld::ComboBox> m_xFmtLB; + std::unique_ptr<weld::Label> m_xPrefixFT; + std::unique_ptr<weld::Entry> m_xPrefixED; + std::unique_ptr<weld::Label> m_xSuffixFT; + std::unique_ptr<weld::Entry> m_xSuffixED; + std::unique_ptr<weld::Frame> m_xBeforeAfter; + std::unique_ptr<weld::Label> m_xBulColorFT; + std::unique_ptr<ColorListBox> m_xBulColLB; + std::unique_ptr<weld::Label> m_xBulRelSizeFT; + std::unique_ptr<weld::MetricSpinButton> m_xBulRelSizeMF; + std::unique_ptr<weld::Label> m_xStartFT; + std::unique_ptr<weld::SpinButton> m_xStartED; + std::unique_ptr<weld::Label> m_xBulletFT; + std::unique_ptr<weld::Button> m_xBulletPB; + std::unique_ptr<weld::MenuButton> m_xBitmapMB; + std::unique_ptr<weld::Label> m_xWidthFT; + std::unique_ptr<weld::MetricSpinButton> m_xWidthMF; + std::unique_ptr<weld::Label> m_xHeightFT; + std::unique_ptr<weld::MetricSpinButton> m_xHeightMF; + std::unique_ptr<weld::CheckButton> m_xRatioCB; + std::unique_ptr<weld::Menu> m_xGalleryMenu; + std::unique_ptr<weld::CustomWeld> m_xPreviewWIN; + std::unique_ptr<weld::Label> m_xDistBorderFT; + std::unique_ptr<weld::MetricSpinButton> m_xDistBorderMF; + std::unique_ptr<weld::CheckButton> m_xRelativeCB; + std::unique_ptr<weld::Label> m_xIndentFT; + std::unique_ptr<weld::MetricSpinButton> m_xIndentMF; + std::unique_ptr<weld::Toggleable> m_xLeftTB; + std::unique_ptr<weld::Toggleable> m_xCenterTB; + std::unique_ptr<weld::Toggleable> m_xRightTB; + std::unique_ptr<weld::RadioButton> m_xSlideRB; + std::unique_ptr<weld::RadioButton> m_xSelectionRB; + std::unique_ptr<weld::Toggleable> m_xApplyToMaster; + std::unique_ptr<weld::Button> m_xReset; + + void InitControls(); + /** To switch between the numbering type + 0 - Number; + 1 - Bullet; + 2 - Bitmap; */ + void SwitchNumberType(sal_uInt8 nType); + void CheckForStartValue_Impl(sal_uInt16 nNumberingType); + + DECL_LINK(NumberTypeSelectHdl_Impl, weld::ComboBox&, void); + DECL_LINK(LevelHdl_Impl, weld::TreeView&, void); + DECL_LINK(PopupActivateHdl_Impl, weld::Toggleable&, void); + DECL_LINK(GraphicHdl_Impl, const OString&, void); + DECL_LINK(BulletHdl_Impl, weld::Button&, void); + DECL_LINK(SizeHdl_Impl, weld::MetricSpinButton&, void); + DECL_LINK(RatioHdl_Impl, weld::Toggleable&, void); + DECL_LINK(EditModifyHdl_Impl, weld::Entry&, void); + DECL_LINK(SpinModifyHdl_Impl, weld::SpinButton&, void); + DECL_LINK(BulColorHdl_Impl, ColorListBox&, void); + DECL_LINK(BulRelSizeHdl_Impl, weld::MetricSpinButton&, void); + DECL_LINK(PreviewInvalidateHdl_Impl, Timer*, void); + DECL_LINK(DistanceHdl_Impl, weld::MetricSpinButton&, void); + DECL_LINK(RelativeHdl_Impl, weld::Toggleable&, void); + DECL_LINK(SelectLeftAlignmentHdl_Impl, weld::Toggleable&, void); + DECL_LINK(SelectCenterAlignmentHdl_Impl, weld::Toggleable&, void); + DECL_LINK(SelectRightAlignmentHdl_Impl, weld::Toggleable&, void); + DECL_LINK(ApplyToMasterHdl_Impl, weld::Toggleable&, void); + DECL_LINK(ResetHdl_Impl, weld::Button&, void); + void EditModifyHdl_Impl(const weld::Entry*); + void InitPosAndSpaceMode(); + void SetAlignmentHdl_Impl(SvxAdjust); + +public: + SvxBulletAndPositionDlg(weld::Window* pWindow, const SfxItemSet& rSet, const ::sd::View* pView); + virtual ~SvxBulletAndPositionDlg() override; + + SfxItemSet* GetOutputItemSet(SfxItemSet* rSet); + bool IsApplyToMaster() const; + bool IsSlideScope() const; + void Reset(const SfxItemSet* rSet); + + void SetCharFmt(const OUString& rNumName) { m_sNumCharFmtName = rNumName; } + void SetMetric(FieldUnit eSet); + + void SetModified(bool bRepaint = true); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/Client.hxx b/sd/source/ui/inc/Client.hxx new file mode 100644 index 000000000..6b999068e --- /dev/null +++ b/sd/source/ui/inc/Client.hxx @@ -0,0 +1,45 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/ipclient.hxx> +class SdrOle2Obj; + +namespace sd +{ +class ViewShell; + +class Client : public SfxInPlaceClient +{ + ViewShell* mpViewShell; + SdrOle2Obj* pSdrOle2Obj; + + virtual void ObjectAreaChanged() override; + virtual void RequestNewObjectArea(::tools::Rectangle&) override; + virtual void ViewChanged() override; + +public: + Client(SdrOle2Obj* pObj, ViewShell* pSdViewShell, vcl::Window* pWindow); + virtual ~Client() override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/ClientView.hxx b/sd/source/ui/inc/ClientView.hxx new file mode 100644 index 000000000..7a52053ba --- /dev/null +++ b/sd/source/ui/inc/ClientView.hxx @@ -0,0 +1,43 @@ +/* -*- 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 . + */ + +#pragma once + +#include "drawview.hxx" + +namespace sd +{ +/** + * The SdClientView is used for DrawDocShell::Draw() + */ +class ClientView : public DrawView +{ +public: + ClientView(DrawDocShell* pDocSh, OutputDevice* pOutDev); + virtual ~ClientView() override; + + /* if the view should not do an Invalidate() on the windows, you have to + override the following two methods and do something different */ + virtual void InvalidateOneWin(OutputDevice& rWin) override; + virtual void InvalidateOneWin(OutputDevice& rWin, const ::tools::Rectangle& rRect) override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/CustomAnimationList.hxx b/sd/source/ui/inc/CustomAnimationList.hxx new file mode 100644 index 000000000..ca9673fd7 --- /dev/null +++ b/sd/source/ui/inc/CustomAnimationList.hxx @@ -0,0 +1,169 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <memory> + +#include <vcl/transfer.hxx> +#include <vcl/weld.hxx> +#include <CustomAnimationEffect.hxx> + +namespace com::sun::star::drawing { class XShape; } + +struct ImplSVEvent; + +namespace sd { + +typedef std::shared_ptr< CustomAnimationEffect > CustomAnimationEffectPtr; + +class ICustomAnimationListController +{ +public: + virtual void onSelect() = 0; + virtual void onDoubleClick() = 0; + virtual void onContextMenu(const OString &rIdent) = 0; + virtual void onDragNDropComplete( std::vector< CustomAnimationEffectPtr > pEffectsDragged, CustomAnimationEffectPtr pEffectInsertBefore ) = 0; + virtual ~ICustomAnimationListController() {} +}; + +class CustomAnimationList; +class CustomAnimationListEntryItem; + +class CustomAnimationListDropTarget : public DropTargetHelper +{ +private: + CustomAnimationList& m_rTreeView; + + virtual sal_Int8 AcceptDrop( const AcceptDropEvent& rEvt ) override; + virtual sal_Int8 ExecuteDrop( const ExecuteDropEvent& rEvt ) override; + +public: + CustomAnimationListDropTarget(CustomAnimationList& rTreeView); +}; + +class CustomAnimationList : public ISequenceListener +{ + friend class CustomAnimationListEntryItem; + friend struct stl_append_effect_func; + +public: + explicit CustomAnimationList(std::unique_ptr<weld::TreeView> xTreeView, + std::unique_ptr<weld::Label> xLabel, + std::unique_ptr<weld::Widget> xScrolledWindow); + virtual ~CustomAnimationList(); + + // methods + + /** selects or deselects the given effect. + Selections of other effects are not changed */ + void select( const CustomAnimationEffectPtr& pEffect ); + + /** populates the list with all effects from the given MainSequence */ + void update( const MainSequencePtr& pMainSequence ); + + void update(); + + EffectSequence getSelection() const; + + // events + void onSelectionChanged(const css::uno::Any& rSelection); + + void Select(); + + virtual void notify_change() override; + + bool isExpanded( const CustomAnimationEffectPtr& pEffect ) const; + bool isVisible( const CustomAnimationEffectPtr& pEffect ) const; + + // clears all entries from the listbox + void clear(); + + void setController( ICustomAnimationListController* pController ) + { + mpController = pController; + }; + + sal_Int8 AcceptDrop(const AcceptDropEvent& rEvt); + sal_Int8 ExecuteDrop(const ExecuteDropEvent& rEvt); + + void set_sensitive(bool bSensitive) { mxTreeView->set_sensitive(bSensitive); } + int get_height_rows(int nRows) { return mxTreeView->get_height_rows(nRows); } + int get_approximate_digit_width() const { return mxTreeView->get_approximate_digit_width(); } + void set_size_request(int nWidth, int nHeight) + { + mxTreeView->set_size_request(nWidth, nHeight); + mxEmptyLabel->set_size_request(nWidth, nHeight); + } + void unselect_all() { mxTreeView->unselect_all(); } + weld::TreeView& get_widget() { return *mxTreeView; } + + DECL_LINK(KeyInputHdl, const KeyEvent&, bool); + DECL_LINK(ExpandHdl, const weld::TreeIter&, bool); + DECL_LINK(PostExpandHdl, void*, void); + DECL_LINK(CollapseHdl, const weld::TreeIter&, bool); + DECL_LINK(PostCollapseHdl, void*, void); + +private: + std::unique_ptr<weld::TreeView> mxTreeView; + CustomAnimationListDropTarget maDropTargetHelper; + std::unique_ptr<weld::Label> mxEmptyLabel; + std::unique_ptr<weld::Widget> mxEmptyLabelParent; + std::vector<std::unique_ptr<CustomAnimationListEntryItem>> mxEntries; + std::vector<std::unique_ptr<weld::TreeIter>> lastSelectedEntries; + + bool mbIgnorePaint; + + DECL_LINK(SelectHdl, weld::TreeView&, void); + DECL_LINK(CommandHdl, const CommandEvent&, bool); + DECL_LINK(DoubleClickHdl, weld::TreeView&, bool); + DECL_LINK(DragBeginHdl, bool&, bool); + DECL_STATIC_LINK(CustomAnimationList, CustomRenderHdl, weld::TreeView::render_args, void); + DECL_STATIC_LINK(CustomAnimationList, CustomGetSizeHdl, weld::TreeView::get_size_args, Size); + + void ExecuteContextMenuAction(const OString& rSelectedPopupEntry); + + /** appends the given effect to the list*/ + void append( CustomAnimationEffectPtr pEffect ); + + ICustomAnimationListController* mpController; + + MainSequencePtr mpMainSequence; + + css::uno::Reference< css::drawing::XShape > mxLastTargetShape; + sal_Int32 mnLastGroupId; + ImplSVEvent* mnPostExpandEvent; + ImplSVEvent* mnPostCollapseEvent; + + std::unique_ptr<weld::TreeIter> mxLastParentEntry; + + // drag & drop + std::unique_ptr<weld::TreeIter> mxDndEffectDragging; + std::vector<std::unique_ptr<weld::TreeIter>> mDndEffectsSelected; +}; + +OUString getPropertyName( sal_Int32 nPropertyType ); + +OUString getShapeDescription( const css::uno::Reference< css::drawing::XShape >& xShape, bool bWithText ); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/CustomAnimationPane.hxx b/sd/source/ui/inc/CustomAnimationPane.hxx new file mode 100644 index 000000000..5e2d69658 --- /dev/null +++ b/sd/source/ui/inc/CustomAnimationPane.hxx @@ -0,0 +1,179 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/sidebar/ILayoutableWindow.hxx> +#include <sfx2/sidebar/PanelLayout.hxx> +#include <vcl/idle.hxx> +#include "CustomAnimationList.hxx" +#include <misc/scopelock.hxx> + +#include <vector> + +namespace com::sun::star::drawing { class XDrawPage; } +namespace com::sun::star::drawing { class XDrawView; } +namespace weld { class ComboBox; } +namespace com::sun::star::animations { class XAnimationNode; } +namespace sd::tools { class EventMultiplexerEvent; } + +enum class PathKind { NONE, CURVE, POLYGON, FREEFORM }; + +namespace sd { + +class MotionPathTag; +class SdPropertySubControl; +class STLPropertySet; +class ViewShellBase; + +typedef std::vector< rtl::Reference< MotionPathTag > > MotionPathTagVector; + +class CustomAnimationPane : public PanelLayout + , public sfx2::sidebar::ILayoutableWindow + , public ICustomAnimationListController +{ + friend class MotionPathTag; +public: + CustomAnimationPane(weld::Widget* pParent, ViewShellBase& rBase); + virtual ~CustomAnimationPane() override; + + // ILayoutableWindow + virtual css::ui::LayoutSize GetHeightForWidth (const sal_Int32 nWidth) override; + + // callbacks + void onSelectionChanged(); + void onChangeCurrentPage(); + void onAdd(); + void onRemove(); + void onChangeStart(); + void onChangeStart( sal_Int16 nNodeType ); + void onChangeSpeed(); + + // methods + void preview( const css::uno::Reference< css::animations::XAnimationNode >& xAnimationNode ); + void remove( CustomAnimationEffectPtr const & pEffect ); + + // ICustomAnimationListController + virtual void onSelect() override; + virtual void onDoubleClick() override; + virtual void onContextMenu(const OString& rIdent) override; + virtual void onDragNDropComplete( std::vector< CustomAnimationEffectPtr > pEffectsDragged, CustomAnimationEffectPtr pEffectInsertBefore ) override; + + void addUndo(); + + double getDuration() const; + void updatePathFromMotionPathTag( const rtl::Reference< MotionPathTag >& xTag ); + +private: + void initialize(); + void addListener(); + void removeListener(); + void updateControls(); + void updateMotionPathTags(); + + void showOptions(const OString& sPage = OString()); + void moveSelection( bool bUp ); + void onPreview( bool bForcePreview ); + + std::unique_ptr<STLPropertySet> createSelectionSet(); + void changeSelection( STLPropertySet const * pResultSet, STLPropertySet const * pOldSet ); + + static css::uno::Any getProperty1Value( sal_Int32 nType, const CustomAnimationEffectPtr& pEffect ); + static bool setProperty1Value( sal_Int32 nType, const CustomAnimationEffectPtr& pEffect, const css::uno::Any& rValue ); + sal_Int32 fillAnimationLB( bool bHasText ); + PathKind getCreatePathKind() const; + void createPath( PathKind eKind, std::vector< ::com::sun::star::uno::Any >& rTargets, double fDuration ); + + DECL_LINK( implControlListBoxHdl, weld::ComboBox&, void ); + DECL_LINK( implClickHdl, weld::Button&, void ); + DECL_LINK( implToggleHdl, weld::Toggleable&, void ); + DECL_LINK( implPropertyHdl, LinkParamNone*, void ); + DECL_LINK( EventMultiplexerListener, tools::EventMultiplexerEvent&, void ); + DECL_LINK( lateInitCallback, Timer *, void ); + DECL_LINK( DurationModifiedHdl, weld::MetricSpinButton&, void ); + DECL_LINK( DelayModifiedHdl, weld::MetricSpinButton&, void ); + DECL_LINK( DelayLoseFocusHdl, weld::Widget&, void ); + DECL_LINK( UpdateAnimationLB, weld::ComboBox&, void ); + DECL_LINK( AnimationSelectHdl, weld::TreeView&, void ); + DECL_LINK( SelectionHandler, Timer*, void ); + void implControlHdl(const weld::Widget* pControl); + +private: + ViewShellBase& mrBase; + + // UI Elements + std::unique_ptr<weld::Label> mxFTAnimation; + std::unique_ptr<CustomAnimationList> mxCustomAnimationList; + std::unique_ptr<weld::Button> mxPBAddEffect; + std::unique_ptr<weld::Button> mxPBRemoveEffect; + std::unique_ptr<weld::Button> mxPBMoveUp; + std::unique_ptr<weld::Button> mxPBMoveDown; + std::unique_ptr<weld::Label> mxFTCategory; + std::unique_ptr<weld::ComboBox> mxLBCategory; + std::unique_ptr<weld::Label> mxFTEffect; + std::unique_ptr<weld::TreeView> mxLBAnimation; + std::unique_ptr<weld::Label> mxFTStart; + std::unique_ptr<weld::ComboBox> mxLBStart; + std::unique_ptr<weld::Label> mxFTProperty; + std::unique_ptr<SdPropertySubControl> mxLBSubControl; + std::unique_ptr<weld::Container> mxPlaceholderBox; + std::unique_ptr<weld::Button> mxPBPropertyMore; + std::unique_ptr<weld::Label> mxFTDuration; + std::unique_ptr<weld::MetricSpinButton> mxCBXDuration; + std::unique_ptr<weld::Label> mxFTStartDelay; + std::unique_ptr<weld::MetricSpinButton> mxMFStartDelay; + std::unique_ptr<weld::CheckButton> mxCBAutoPreview; + std::unique_ptr<weld::Button> mxPBPlay; + + Idle maIdle; + + OUString maStrModify; + OUString maStrProperty; + + sal_Int32 mnLastSelectedAnimation; + sal_Int32 mnPropertyType; + static sal_Int32 const gnMotionPathPos = 3; + sal_Int32 mnCurvePathPos; + sal_Int32 mnPolygonPathPos; + sal_Int32 mnFreeformPathPos; + + EffectSequence maListSelection; + css::uno::Any maViewSelection; + + MainSequencePtr mpMainSequence; + + css::uno::Reference< css::drawing::XDrawPage > mxCurrentPage; + css::uno::Reference< css::drawing::XDrawView > mxView; + + /** The CustomAnimationPresets is initialized either on demand or + after a short time after the construction of a new object of this + class. This timer is responsible for the later. + */ + Timer maLateInitTimer; + + MotionPathTagVector maMotionPathTags; + + ScopeLock maSelectionLock; +}; + +void fillRepeatComboBox(weld::ComboBox& rBox); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/DocumentRenderer.hxx b/sd/source/ui/inc/DocumentRenderer.hxx new file mode 100644 index 000000000..7cbeefc79 --- /dev/null +++ b/sd/source/ui/inc/DocumentRenderer.hxx @@ -0,0 +1,63 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/view/XRenderable.hpp> +#include <comphelper/compbase.hxx> +#include <memory> + +namespace sd { class ViewShellBase; } + +namespace sd { + +typedef comphelper::WeakComponentImplHelper < + css::view::XRenderable + > DocumentRendererInterfaceBase; + +class DocumentRenderer final + : public DocumentRendererInterfaceBase +{ +public: + DocumentRenderer (ViewShellBase& rBase); + virtual ~DocumentRenderer() override; + + // XRenderable + virtual sal_Int32 SAL_CALL getRendererCount ( + const css::uno::Any& aSelection, + const css::uno::Sequence<css::beans::PropertyValue >& xOptions) override; + + virtual css::uno::Sequence<css::beans::PropertyValue> SAL_CALL getRenderer ( + sal_Int32 nRenderer, + const css::uno::Any& rSelection, + const css::uno::Sequence<css::beans::PropertyValue>& rxOptions) override; + + virtual void SAL_CALL render ( + sal_Int32 nRenderer, + const css::uno::Any& rSelection, + const css::uno::Sequence<css::beans::PropertyValue>& rxOptions) override; + +private: + class Implementation; + std::unique_ptr<Implementation> mpImpl; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/DrawController.hxx b/sd/source/ui/inc/DrawController.hxx new file mode 100644 index 000000000..2c15e26eb --- /dev/null +++ b/sd/source/ui/inc/DrawController.hxx @@ -0,0 +1,327 @@ +/* -*- 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 . + */ + +#pragma once + +#include <cppuhelper/propshlp.hxx> +#include <sfx2/sfxbasecontroller.hxx> +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include <com/sun/star/view/XFormLayerAccess.hpp> +#include <com/sun/star/drawing/XDrawView.hpp> +#include <com/sun/star/drawing/framework/XControllerManager.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <comphelper/uno3.hxx> +#include <cppuhelper/implbase.hxx> +#include <unotools/weakref.hxx> +#include <tools/gen.hxx> +#include <memory> +#include <vector> + +namespace com::sun::star::drawing { class XDrawSubController; } +namespace com::sun::star::drawing::framework { class XConfigurationController; } +namespace com::sun::star::drawing::framework { class XModuleController; } +namespace com::sun::star::drawing { class XLayer; } +namespace osl { class Mutex; } + +class SdPage; + +namespace sd { + +typedef ::cppu::ImplInheritanceHelper < + SfxBaseController, + css::view::XSelectionSupplier, + css::lang::XServiceInfo, + css::drawing::XDrawView, + css::view::XSelectionChangeListener, + css::view::XFormLayerAccess, + css::drawing::framework::XControllerManager, + css::lang::XUnoTunnel + > DrawControllerInterfaceBase; + +class BroadcastHelperOwner +{ +public: + explicit BroadcastHelperOwner (::osl::Mutex& rMutex) : maBroadcastHelper(rMutex) {}; + ::cppu::OBroadcastHelper maBroadcastHelper; +}; + +class ViewShellBase; + +/** The DrawController is the UNO controller for Impress and Draw. It + relies objects that implement the DrawSubController interface for view + specific behaviour. The life time of the DrawController is roughly that + of ViewShellBase but note that the DrawController can (in the case of a + reload) outlive the ViewShellBase. + + The implementation of the XControllerManager interface is not yet in its + final form. +*/ +class DrawController final + : public DrawControllerInterfaceBase, + private BroadcastHelperOwner, + public ::cppu::OPropertySetHelper +{ +public: + enum PropertyHandle { + PROPERTY_WORKAREA = 0, + PROPERTY_SUB_CONTROLLER = 1, + PROPERTY_CURRENTPAGE = 2, + PROPERTY_MASTERPAGEMODE = 3, + PROPERTY_LAYERMODE = 4, + PROPERTY_ACTIVE_LAYER = 5, + PROPERTY_ZOOMTYPE = 6, + PROPERTY_ZOOMVALUE = 7, + PROPERTY_VIEWOFFSET = 8, + PROPERTY_DRAWVIEWMODE = 9 + ,PROPERTY_UPDATEACC = 10 + ,PROPERTY_PAGE_CHANGE = 11 + }; + + /** Create a new DrawController object for the given ViewShellBase. + */ + explicit DrawController (ViewShellBase& rBase) noexcept; + + virtual ~DrawController() noexcept override; + + /** Replace the currently used sub controller with the given one. This + new sub controller is used from now on for the view (that is the + main view shell to be precise) specific tasks. Call this method + with a suitable sub controller whenever the view shell in the center + pane is exchanged. + @param pSubController + The ViewShell specific sub controller or NULL when (temporarily + while switching to another one) there is no ViewShell displayed + in the center pane. + */ + void SetSubController ( + const css::uno::Reference<css::drawing::XDrawSubController>& rxSubController); + + /** Call this method when the VisArea has changed. + */ + void FireVisAreaChanged (const ::tools::Rectangle& rVisArea) noexcept; + + /** Call this method when the selection has changed. + */ + void FireSelectionChangeListener() noexcept; + + /** Call this method when the edit mode has changed. + */ + void FireChangeEditMode (bool bMasterPageMode) noexcept; + + /** Call this method when the layer mode has changed. + */ + void FireChangeLayerMode (bool bLayerMode) noexcept; + + /** Call this method when there is a new current page. + */ + void FireSwitchCurrentPage (SdPage* pCurrentPage) noexcept; + + /** Broadcast a sidebar context change that is caused by a view + switch. + */ + void BroadcastContextChange() const; + void NotifyAccUpdate(); + void fireChangeLayer( css::uno::Reference< css::drawing::XLayer>* pCurrentLayer ) noexcept; + // change the parameter to int + //void fireSwitchCurrentPage( String pageName) throw(); + void fireSwitchCurrentPage( sal_Int32 pageIndex) noexcept; + bool IsDisposing() const { return mbDisposing; } + + /** Return a pointer to the ViewShellBase object that the DrawController + is connected to. + @return + The returned pointer is <NULL/> after a call to + ReleaseViewShellBase(). + */ + ViewShellBase* GetViewShellBase() { return mpBase;} + + /** This method is typically called from the destructor of ViewShellBase + to tell the DrawController that it and its members must not access + the ViewShellBase anymore. + After this call the DrawController is semi-disposed. + */ + void ReleaseViewShellBase(); + + static const css::uno::Sequence<sal_Int8>& getUnoTunnelId(); + + DECLARE_XINTERFACE() + DECLARE_XTYPEPROVIDER() + + // XComponent + virtual void SAL_CALL dispose() override; + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override; + + // XController + virtual sal_Bool SAL_CALL suspend( sal_Bool Suspend ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XSelectionSupplier + virtual sal_Bool SAL_CALL select( const css::uno::Any& aSelection ) override; + virtual css::uno::Any SAL_CALL getSelection( ) override; + virtual void SAL_CALL addSelectionChangeListener( const css::uno::Reference< css::view::XSelectionChangeListener >& xListener ) override; + virtual void SAL_CALL removeSelectionChangeListener( const css::uno::Reference< css::view::XSelectionChangeListener >& xListener ) override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + // XFormLayerAccess + virtual css::uno::Reference< css::form::runtime::XFormController > SAL_CALL getFormController( const css::uno::Reference< css::form::XForm >& Form ) override; + virtual sal_Bool SAL_CALL isFormDesignMode( ) override; + virtual void SAL_CALL setFormDesignMode( sal_Bool DesignMode ) override; + + // XControlAccess + virtual css::uno::Reference< css::awt::XControl > SAL_CALL getControl( const css::uno::Reference< css::awt::XControlModel >& xModel ) override; + + // XDrawView + virtual void SAL_CALL + setCurrentPage ( + const css::uno::Reference< + css::drawing::XDrawPage >& xPage) override; + + virtual css::uno::Reference< + css::drawing::XDrawPage > SAL_CALL + getCurrentPage() override; + + // lang::XEventListener + virtual void SAL_CALL + disposing (const css::lang::EventObject& rEventObject) override; + + // view::XSelectionChangeListener + virtual void SAL_CALL + selectionChanged (const css::lang::EventObject& rEvent) override; + + // XControllerManager + + virtual css::uno::Reference<css::drawing::framework::XConfigurationController> SAL_CALL + getConfigurationController() override; + + virtual css::uno::Reference<css::drawing::framework::XModuleController> SAL_CALL + getModuleController() override; + + // XUnoTunnel + + virtual sal_Int64 SAL_CALL getSomething (const css::uno::Sequence<sal_Int8>& rId) override; + +private: + /** This method must return the name to index table. This table + contains all property names and types of this object. + */ + virtual ::cppu::IPropertyArrayHelper & SAL_CALL getInfoHelper() override; + + static void FillPropertyTable ( + ::std::vector< css::beans::Property>& rProperties); + + /** + * The same as getFastPropertyValue, but return the value through + * rValue and nHandle is always valid. + */ + virtual void SAL_CALL getFastPropertyValue( + css::uno::Any& rValue, + sal_Int32 nHandle ) const override; + + /** Convert the value rValue and return the result in rConvertedValue and the + old value in rOldValue. + After this call the vetoable listeners are notified. + + @param rConvertedValue + The converted value. Only set if return is true. + @param rOldValue + The old value. Only set if return is true. + @param nHandle + The handle of the property. + @return + <TRUE/> if the value is converted successfully. + @throws IllegalArgumentException + */ + virtual sal_Bool SAL_CALL convertFastPropertyValue( + css::uno::Any & rConvertedValue, + css::uno::Any & rOldValue, + sal_Int32 nHandle, + const css::uno::Any& rValue ) override; + + /** The same as setFastPropertyValue, but no exception is thrown and nHandle + is always valid. You must not broadcast the changes in this method. + */ + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle, + const css::uno::Any& rValue ) override; + + /** When the called object has been disposed already this method throws + a Disposed exception and does not return. + + @throws css::lang::DisposedException + */ + void ThrowIfDisposed() const; + + using cppu::OPropertySetHelper::disposing; + using cppu::OPropertySetHelper::getFastPropertyValue; + + css::uno::Reference< css::drawing::XLayer>* mpCurrentLayer; + + const css::uno::Type m_aSelectionTypeIdentifier; + + /** This pointer to the ViewShellBase can be NULL (after a call to + ReleaseViewShellBase()). + */ + ViewShellBase* mpBase; + + ::tools::Rectangle maLastVisArea; + ::unotools::WeakReference<SdPage> mpCurrentPage; + bool mbMasterPageMode; + bool mbLayerMode; + + /** This flag indicates whether the called DrawController is being + disposed or already has been disposed. + */ + bool mbDisposing; + + ::std::unique_ptr< ::cppu::IPropertyArrayHelper> mpPropertyArrayHelper; + + /** The current sub controller. May be NULL. + */ + css::uno::Reference<css::drawing::XDrawSubController> mxSubController; + + css::uno::Reference< + css::drawing::framework::XConfigurationController> mxConfigurationController; + css::uno::Reference< + css::drawing::framework::XModuleController> mxModuleController; + + /** Send an event to all relevant property listeners that a + property has changed its value. The fire() method of the + OPropertySetHelper is wrapped by this method to handle + exceptions thrown by called listeners. + */ + void FirePropertyChange ( + sal_Int32 nHandle, + const css::uno::Any& rNewValue, + const css::uno::Any& rOldValue); + + void ProvideFrameworkControllers(); + void DisposeFrameworkControllers(); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/DrawDocShell.hxx b/sd/source/ui/inc/DrawDocShell.hxx new file mode 100644 index 000000000..15fa5ebd4 --- /dev/null +++ b/sd/source/ui/inc/DrawDocShell.hxx @@ -0,0 +1,235 @@ +/* -*- 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 . + */ + +#pragma once + +#include <o3tl/span.hxx> +#include <sfx2/docfac.hxx> +#include <sfx2/objsh.hxx> +#include <svl/style.hxx> + +#include <glob.hxx> +#include <pres.hxx> +#include <sddllapi.h> +#include "fupoor.hxx" + +class FontList; +class SdDrawDocument; +class SdPage; +class SfxPrinter; +struct SpellCallbackInfo; +class AbstractSvxNameDialog; +class SfxUndoManager; + +namespace sd { + +class FrameView; +class ViewShell; +class DrawViewShell; + +// DrawDocShell +class SD_DLLPUBLIC DrawDocShell : public SfxObjectShell +{ +public: + SFX_DECL_INTERFACE(SD_IF_SDDRAWDOCSHELL) + SFX_DECL_OBJECTFACTORY(); + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + DrawDocShell ( + SfxObjectCreateMode eMode, + bool bSdDataObj, + DocumentType); + + DrawDocShell ( + SfxModelFlags nModelCreationFlags, + bool bSdDataObj, + DocumentType); + + DrawDocShell ( + SdDrawDocument* pDoc, + SfxObjectCreateMode eMode, + bool bSdDataObj, + DocumentType); + virtual ~DrawDocShell() override; + + void UpdateRefDevice(); + virtual void Activate( bool bMDI ) override; + virtual void Deactivate( bool bMDI ) override; + virtual bool InitNew( const css::uno::Reference< css::embed::XStorage >& xStorage ) override; + virtual bool ImportFrom(SfxMedium &rMedium, + css::uno::Reference<css::text::XTextRange> const& xInsertPosition) + override; + virtual bool ConvertFrom( SfxMedium &rMedium ) override; + virtual bool Save() override; + virtual bool SaveAsOwnFormat( SfxMedium& rMedium ) override; + virtual bool ConvertTo( SfxMedium &rMedium ) override; + virtual bool SaveCompleted( const css::uno::Reference< css::embed::XStorage >& xStorage ) override; + + virtual bool Load( SfxMedium &rMedium ) override; + virtual bool LoadFrom( SfxMedium& rMedium ) override; + virtual bool SaveAs( SfxMedium &rMedium ) override; + + virtual ::tools::Rectangle GetVisArea(sal_uInt16 nAspect) const override; + virtual void Draw(OutputDevice*, const JobSetup& rSetup, sal_uInt16 nAspect) override; + virtual SfxUndoManager* GetUndoManager() override; + virtual Printer* GetDocumentPrinter() override; + virtual void OnDocumentPrinterChanged(Printer* pNewPrinter) override; + virtual SfxStyleSheetBasePool* GetStyleSheetPool() override; + virtual void FillClass(SvGlobalName* pClassName, SotClipboardFormatId* pFormat, OUString* pFullTypeName, sal_Int32 nFileFormat, bool bTemplate = false ) const override; + virtual void SetModified( bool = true ) override; + virtual std::shared_ptr<SfxDocumentInfoDialog> CreateDocumentInfoDialog(weld::Window* pParent, + const SfxItemSet &rSet) override; + + using SfxObjectShell::GetVisArea; + using SfxShell::GetViewShell; + + sd::ViewShell* GetViewShell() { return mpViewShell; } + ::sd::FrameView* GetFrameView(); + + SdDrawDocument* GetDoc() { return mpDoc;} + DocumentType GetDocumentType() const { return meDocType; } + + SfxPrinter* GetPrinter(bool bCreate); + void SetPrinter(SfxPrinter *pNewPrinter); + void UpdateFontList(); + + bool IsInDestruction() const { return mbInDestruction; } + + void CancelSearching(); + + void Execute( SfxRequest& rReq ); + void GetState(SfxItemSet&); + + void Connect(sd::ViewShell* pViewSh); + void Disconnect(sd::ViewShell const * pViewSh); + void UpdateTablePointers(); + + void GotoBookmark(std::u16string_view rBookmark); + + BitmapEx GetPagePreviewBitmap(SdPage* pPage); + + /** checks, if the given name is a valid new name for a slide + + <p>If the name is invalid, an <type>SvxNameDialog</type> pops up that + queries again for a new name until it is ok or the user chose + Cancel.</p> + + @param pWin is necessary to pass to the <type>SvxNameDialog</type> in + case an invalid name was entered. + @param rName the new name that is to be set for a slide. This string + may be set to an empty string (see below). + + @return sal_True, if the new name is unique. Note that if the user entered + a default name of a not-yet-existing slide (e.g. 'Slide 17'), + sal_True is returned, but rName is set to an empty string. + */ + bool CheckPageName(weld::Window* pWin, OUString& rName ); + + void SetSlotFilter(bool bEnable = false, o3tl::span<sal_uInt16 const> pSIDs = o3tl::span<sal_uInt16 const>()) { mbFilterEnable = bEnable; mpFilterSIDs = pSIDs; } + void ApplySlotFilter() const; + + SfxStyleFamily GetStyleFamily() const { return mnStyleFamily; } + void SetStyleFamily( SfxStyleFamily nSF ) { mnStyleFamily = nSF; } + + /** executes the SID_OPENDOC slot to let the framework open a document + with the given URL and this document as a referer */ + void OpenBookmark( const OUString& rBookmarkURL ); + + /** checks, if the given name is a valid new name for a slide + + <p>This method does not pop up any dialog (like CheckPageName).</p> + + @param rInOutPageName the new name for a slide that is to be renamed. + This string will be set to an empty string if + bResetStringIfStandardName is true and the name is of the + form of any, possibly not-yet existing, standard slide + (e.g. 'Slide 17') + + @param bResetStringIfStandardName if true allows setting rInOutPageName + to an empty string, which returns true and implies that the + slide will later on get a new standard name (with a free + slide number). + + @return true, if the new name is unique. If bResetStringIfStandardName + is true, the return value is also true, if the slide name is + a standard name (see above) + */ + bool IsNewPageNameValid( OUString & rInOutPageName, bool bResetStringIfStandardName = false ); + + /** checks, if the given name is a *unique* name for an *existing* slide + + @param rPageName the name of an existing slide + + @return true, if the name is unique and the slide exists + */ + bool IsPageNameUnique(std::u16string_view rPagName) const; + + /** Return the reference device for the current document. When the + inherited implementation returns a device then this is passed to the + caller. Otherwise the returned value depends on the printer + independent layout mode and will usually be either a printer or a + virtual device used for screen rendering. + @return + Returns NULL when the current document has no reference device. + */ + virtual OutputDevice* GetDocumentRefDev() override; + + DECL_DLLPRIVATE_LINK( RenameSlideHdl, AbstractSvxNameDialog&, bool ); + + // ExecuteSpellPopup now handled by DrawDocShell + DECL_DLLPRIVATE_LINK( OnlineSpellCallback, SpellCallbackInfo&, void ); + + void ClearUndoBuffer(); + + std::vector<Color> GetThemeColors() override; + +protected: + + SdDrawDocument* mpDoc; + std::unique_ptr<SfxUndoManager> mpUndoManager; + VclPtr<SfxPrinter> mpPrinter; + ::sd::ViewShell* mpViewShell; + std::unique_ptr<FontList> mpFontList; + DocumentType meDocType; + SfxStyleFamily mnStyleFamily; + o3tl::span<sal_uInt16 const> + mpFilterSIDs; + bool mbFilterEnable; + bool mbSdDataObj; + bool mbInDestruction; + bool mbOwnPrinter; + + bool mbOwnDocument; // if true, we own mpDoc and will delete it in our d'tor + void Construct(bool bClipboard); +private: + static void setEditMode(DrawViewShell* pDrawViewShell, bool isMasterPage); +}; + +#ifndef SV_DECL_DRAW_DOC_SHELL_DEFINED +#define SV_DECL_DRAW_DOC_SHELL_DEFINED +typedef ::tools::SvRef<DrawDocShell> DrawDocShellRef; +#endif + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/DrawSubController.hxx b/sd/source/ui/inc/DrawSubController.hxx new file mode 100644 index 000000000..d748d6378 --- /dev/null +++ b/sd/source/ui/inc/DrawSubController.hxx @@ -0,0 +1,46 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/drawing/XDrawSubController.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <cppuhelper/compbase.hxx> + +namespace sd { + + class DrawSubControllerInterfaceBase : public ::cppu::WeakComponentImplHelper< + css::drawing::XDrawSubController, + css::lang::XServiceInfo > + { + public: + DrawSubControllerInterfaceBase( ::osl::Mutex& aMutex ) + : ::cppu::WeakComponentImplHelper< + css::drawing::XDrawSubController, + css::lang::XServiceInfo >( aMutex ) {} + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override = 0; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override = 0; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override = 0; + }; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/DrawViewShell.hxx b/sd/source/ui/inc/DrawViewShell.hxx new file mode 100644 index 000000000..c56a0f33e --- /dev/null +++ b/sd/source/ui/inc/DrawViewShell.hxx @@ -0,0 +1,513 @@ +/* -*- 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 . + */ + +#pragma once + +#include <memory> +#include "ViewShell.hxx" +#include "tools/AsynchronousCall.hxx" +#include "TabControl.hxx" +#include <glob.hxx> +#include <pres.hxx> +#include <unotools/caserotate.hxx> +#include <unotools/options.hxx> +#include <sddllapi.h> + +namespace svx::sidebar { class SelectionChangeHandler; } +namespace com::sun::star::lang { class XEventListener; } +namespace com::sun::star::scanner { class XScannerManager2; } + +class Outliner; +class SdPage; +class SdStyleSheet; +class SdrExternalToolEdit; +class TabBar; +class SdrObject; +class SdrPageView; +class TransferableDataHelper; +class TransferableClipboardListener; +class AbstractSvxNameDialog; +class SdrLayer; +class SvxClipboardFormatItem; +struct ESelection; +class AbstractSvxObjectNameDialog; + +namespace sd { + +class DrawView; +class LayerTabBar; +class Ruler; +class AnnotationManager; +class ViewOverlayManager; + +#define CHECK_RANGE(nMin, nValue, nMax) ((nValue >= nMin) && (nValue <= nMax)) + +/** Base class of the stacked shells that provide graphical views to + Draw and Impress documents and editing functionality. In contrast + to this other stacked shells are responsible for showing an + overview over several slides or a textual + overview over the text in an Impress document (OutlineViewShell). +*/ +class SAL_DLLPUBLIC_RTTI DrawViewShell + : public ViewShell, + public SfxListener, + public utl::ConfigurationListener +{ +public: + SFX_DECL_INTERFACE(SD_IF_SDDRAWVIEWSHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + /** Create a new stackable shell that may take some information + (e.g. the frame view) from the given previous shell. + @param ePageKind + This parameter gives the initial page kind that the new shell + will show. + @param pFrameView + The frame view that makes it possible to pass information from + one view shell to the next. + */ + DrawViewShell ( + ViewShellBase& rViewShellBase, + vcl::Window* pParentWindow, + PageKind ePageKind, + FrameView* pFrameView); + + virtual ~DrawViewShell() override; + + virtual void Init (bool bIsMainViewShell) override; + + virtual void Shutdown() override; + + void PrePaint() override; + virtual void Paint(const ::tools::Rectangle& rRect, ::sd::Window* pWin) override; + + /** Arrange and resize the GUI elements like rulers, sliders, and + buttons as well as the actual document view according to the size of + the enclosing window and current sizes of buttons, rulers, and + sliders. + */ + virtual void ArrangeGUIElements() override; + + void HidePage(); + + virtual bool KeyInput(const KeyEvent& rKEvt, ::sd::Window* pWin) override; + virtual void MouseMove(const MouseEvent& rMEvt, ::sd::Window* pWin) override; + virtual void MouseButtonUp(const MouseEvent& rMEvt, ::sd::Window* pWin) override; + virtual void MouseButtonDown(const MouseEvent& rMEvt, ::sd::Window* pWin) override; + virtual void Command(const CommandEvent& rCEvt, ::sd::Window* pWin) override; + bool IsMouseButtonDown() const { return mbMouseButtonDown; } + bool IsMouseSelecting() const { return mbMouseSelecting; } + + virtual void Resize() override; + + void ShowMousePosInfo(const ::tools::Rectangle& rRect, ::sd::Window const * pWin); + + virtual void ChangeEditMode (EditMode eMode, bool bIsLayerModeActive); + + virtual void SetZoom( ::tools::Long nZoom ) override; + virtual void SetZoomRect( const ::tools::Rectangle& rZoomRect ) override; + + void InsertURLField(const OUString& rURL, const OUString& rText, const OUString& rTarget); + void InsertURLButton(const OUString& rURL, const OUString& rText, const OUString& rTarget, + const Point* pPos); + + void SelectionHasChanged(); + void ModelHasChanged(); + virtual void Activate(bool bIsMDIActivate) override; + virtual void Deactivate(bool IsMDIActivate) override; + virtual void UIActivating( SfxInPlaceClient* ) override; + virtual void UIDeactivated( SfxInPlaceClient* ) override; + OUString GetSelectionText( bool bCompleteWords ); + bool HasSelection( bool bText ) const; + + //If we are editing a PresObjKind::Outline return the Outliner and fill rSel + //with the current selection + ::Outliner* GetOutlinerForMasterPageOutlineTextObj(ESelection &rSel); + + void ExecCtrl(SfxRequest& rReq); + void GetCtrlState(SfxItemSet& rSet); + void GetDrawAttrState(SfxItemSet& rSet); + void GetMenuState(SfxItemSet& rSet); + void GetTableMenuState(SfxItemSet& rSet); + /** Set the items of the given item set that are related to + switching the editing mode to the correct values. + <p>This function also sets the states of the mode buttons + (those at the upper right corner) accordingly.</p> + */ + void GetModeSwitchingMenuState (SfxItemSet &rSet); + void GetAttrState(SfxItemSet& rSet); + void GetSnapItemState(SfxItemSet& rSet); + + void SetPageProperties (SfxRequest& rReq); + void GetPageProperties(SfxItemSet& rSet); + void GetMarginProperties(SfxItemSet& rSet); + + void GetState (SfxItemSet& rSet); + void Execute (SfxRequest& rReq); + + void ExecStatusBar(SfxRequest& rReq); + void GetStatusBarState(SfxItemSet& rSet); + + void ExecOptionsBar(SfxRequest& rReq); + void GetOptionsBarState(SfxItemSet& rSet); + + void ExecRuler(SfxRequest& rReq); + void GetRulerState(SfxItemSet& rSet); + + void ExecFormText(SfxRequest& rReq); + void GetFormTextState(SfxItemSet& rSet); + + void ExecAnimationWin(SfxRequest& rReq); + void GetAnimationWinState(SfxItemSet& rSet); + + void ExecNavigatorWin(SfxRequest& rReq); + void GetNavigatorWinState(SfxItemSet& rSet); + + void ExecutePropPanelAttr (SfxRequest const & rReq); + void GetStatePropPanelAttr(SfxItemSet& rSet); + + void ExecEffectWin(SfxRequest& rReq); + + void Update3DWindow(); + void AssignFrom3DWindow(); + + void ExecGallery(SfxRequest const & rReq); + + void ExecBmpMask( SfxRequest const & rReq ); + void GetBmpMaskState( SfxItemSet& rSet ); + + void ExecIMap( SfxRequest const & rReq ); + void GetIMapState( SfxItemSet& rSet ); + + void FuTemporary(SfxRequest& rReq); + void FuPermanent(SfxRequest& rReq); + void FuSupport(SfxRequest& rReq); + void FuDeleteSelectedObjects(); + void FuSupportRotate(SfxRequest const & rReq); + void FuTable(SfxRequest& rReq); + + void AttrExec (SfxRequest& rReq); + void AttrState (SfxItemSet& rSet); + + void ExecGoToNextPage (SfxRequest& rReq); + void GetStateGoToNextPage (SfxItemSet& rSet); + + void ExecGoToPreviousPage (SfxRequest& rReq); + void GetStateGoToPreviousPage (SfxItemSet& rSet); + + void ExecGoToFirstPage (SfxRequest& rReq); + void GetStateGoToFirstPage (SfxItemSet& rSet); + + void ExecGoToLastPage (SfxRequest& rReq); + void GetStateGoToLastPage (SfxItemSet& rSet); + + SD_DLLPUBLIC void ExecChar(SfxRequest& rReq); + + void ExecuteAnnotation (SfxRequest const & rRequest); + void GetAnnotationState (SfxItemSet& rItemSet); + + void StartRulerDrag (const Ruler& rRuler, const MouseEvent& rMEvt); + + virtual bool PrepareClose( bool bUI = true ) override; + + PageKind GetPageKind() const { return mePageKind; } + void SetPageKind( PageKind ePageKind ) { mePageKind = ePageKind; } + const Point& GetMousePos() const { return maMousePos; } + + EditMode GetEditMode() const { return meEditMode; } + virtual SdPage* GetActualPage() override { return mpActualPage; } + + /// inherited from sd::ViewShell + virtual SdPage* getCurrentPage() const override; + + void ResetActualPage(); + void ResetActualLayer(); + bool SwitchPage(sal_uInt16 nPage, bool bAllowChangeFocus = true); + bool IsSwitchPageAllowed() const; + + /** + * Mark the desired page as selected (1), deselected (0), toggle (2). + * nPage refers to the page in question. + */ + bool SelectPage(sal_uInt16 nPage, sal_uInt16 nSelect); + bool IsSelected(sal_uInt16 nPage); + bool IsVisible(sal_uInt16 nPage); + + void GotoBookmark(std::u16string_view rBookmark); + //Realize multi-selection of objects, If object is marked, the + //corresponding entry is set true, else the corresponding entry is set + //false. + void FreshNavigatrTree(); + void MakeVisible(const ::tools::Rectangle& rRect, vcl::Window& rWin); + + virtual void ReadFrameViewData(FrameView* pView) override; + virtual void WriteFrameViewData() override; + + virtual ErrCode DoVerb(sal_Int32 nVerb) override; + virtual bool ActivateObject(SdrOle2Obj* pObj, sal_Int32 nVerb) override; + + void SetZoomOnPage( bool bZoom ) { mbZoomOnPage = bZoom; } + bool IsZoomOnPage() const { return mbZoomOnPage; } + static void CheckLineTo (SfxRequest& rReq); + void SetChildWindowState( SfxItemSet& rSet ); + + void UpdateIMapDlg( SdrObject* pObj ); + + void LockInput(); + void UnlockInput(); + bool IsInputLocked() const { return mnLockCount > 0; } + + sal_uInt16 GetCurPagePos() const { return maTabControl->GetCurPagePos(); } + + /** Show controls of the UI or hide them, depending on the given flag. + Do not call this method directly. Call the method at ViewShellBase + instead. + */ + virtual void ShowUIControls (bool bVisible) override; + + void ScannerEvent(); + + bool IsLayerModeActive() const { return mbIsLayerModeActive;} + + virtual sal_Int8 AcceptDrop( const AcceptDropEvent& rEvt, DropTargetHelper& rTargetHelper, + ::sd::Window* pTargetWindow, sal_uInt16 nPage, SdrLayerID nLayer ) override; + virtual sal_Int8 ExecuteDrop( const ExecuteDropEvent& rEvt, DropTargetHelper& rTargetHelper, + ::sd::Window* pTargetWindow, sal_uInt16 nPage, SdrLayerID nLayer ) override; + + virtual void WriteUserDataSequence ( css::uno::Sequence < css::beans::PropertyValue >& ) override; + virtual void ReadUserDataSequence ( const css::uno::Sequence < css::beans::PropertyValue >& ) override; + + virtual void VisAreaChanged(const ::tools::Rectangle& rRect) override; + + /** Create an accessible object representing the specified window. + @param pWindow + The returned object makes the document displayed in this window + accessible. + @return + Returns an <type>AccessibleDrawDocumentView</type> object. + */ + virtual css::uno::Reference<css::accessibility::XAccessible> + CreateAccessibleDocumentView (::sd::Window* pWindow) override; + + /** Return the number of layers managed by the layer tab control. This + will usually differ from the number of layers managed by the layer + administrator. + @return + The number of layers managed by the layer tab control. The + returned value is independent of whether the layer mode is + currently active and the tab control is visible. + */ + int GetTabLayerCount() const; + + /** Return the numerical id of the currently active layer as seen by the + layer tab control. + @return + The returned id is a number between zero (inclusive) and the + number of layers as returned by the + <member>GetTabLayerCount</member> method (exclusive). + */ + int GetActiveTabLayerIndex() const; + + /** Set the active layer at the layer tab control and update the control + accordingly to reflect the change on screen. + @param nId + The id is expected to be a number between zero (inclusive) and + the number of layers as returned by the + <member>GetTabLayerCount</member> method (exclusive). Note that + Invalid values are ignored. No exception is thrown in that case. + */ + void SetActiveTabLayerIndex (int nId); + + /** Return a pointer to the tab control for pages. + */ + TabControl& GetPageTabControl() { return *maTabControl; } + + /** Return a pointer to the tab control for layers. + */ + SD_DLLPUBLIC LayerTabBar* GetLayerTabControl(); // export for unit test + + /** Renames the given slide using an SvxNameDialog + + @param nPageId the index of the page in the SdTabControl. + @param rName the new name of the slide. + + @return false, if the new name is invalid for some reason. + + <p>Implemented in <code>drviews8.cxx</code>.</p> + */ + bool RenameSlide( sal_uInt16 nPageId, const OUString & rName ); + + /** modifies the given layer with the given values */ + void ModifyLayer( SdrLayer* pLayer, const OUString& rLayerName, const OUString& rLayerTitle, const OUString& rLayerDesc, bool bIsVisible, bool bIsLocked, bool bIsPrintable ); + + virtual css::uno::Reference<css::drawing::XDrawSubController> CreateSubController() override; + + DrawView* GetDrawView() const { return mpDrawView.get(); } + + /** Relocation to a new parent window is not supported for DrawViewShell + objects so this method always returns <FALSE/>. + */ + virtual bool RelocateToParentWindow (vcl::Window* pParentWindow) override; + + OUString const & GetSidebarContextName() const; + + bool IsInSwitchPage() const { return mbIsInSwitchPage; } + + //move this method to ViewShell. + //void NotifyAccUpdate(); +protected: + std::unique_ptr<DrawView> mpDrawView; + SdPage* mpActualPage; + ::tools::Rectangle maMarkRect; + Point maMousePos; + VclPtr<TabControl> maTabControl; + EditMode meEditMode; + PageKind mePageKind; + // tdf#137445 at context menu popup time set if the EditHyperlink entry + // should be disabled and use that state if queried about it if + // EditHyperlink is dispatched from the menu. So ignoring where the mouse + // currently happens to be when the menu was dismissed. + std::optional<bool> moAtContextMenu_DisableEditHyperlink; + bool mbZoomOnPage; + bool mbIsRulerDrag; + sal_uLong mnLockCount; + bool mbReadOnly; + static bool mbPipette; + + DECL_DLLPRIVATE_LINK( ClipboardChanged, TransferableDataHelper*, void ); + DECL_DLLPRIVATE_LINK( TabSplitHdl, TabBar *, void ); + DECL_DLLPRIVATE_LINK( NameObjectHdl, AbstractSvxObjectNameDialog&, bool ); + DECL_DLLPRIVATE_LINK( RenameSlideHdl, AbstractSvxNameDialog&, bool ); + + void DeleteActualPage(); + void DeleteActualLayer(); + + virtual VclPtr<SvxRuler> CreateHRuler(::sd::Window* pWin) override; + virtual VclPtr<SvxRuler> CreateVRuler(::sd::Window* pWin) override; + virtual void UpdateHRuler() override; + virtual void UpdateVRuler() override; + virtual void SetZoomFactor(const Fraction& rZoomX, const Fraction& rZoomY) override; + + void SetupPage( Size const &rSize, ::tools::Long nLeft, ::tools::Long nRight, ::tools::Long nUpper, ::tools::Long nLower, + bool bSize, bool bMargin, bool bScaleAll ); + + void GetMenuStateSel(SfxItemSet& rSet); + +private: + /** This flag controls whether the layer mode is active, i.e. the layer + dialog is visible. + */ + bool mbIsLayerModeActive; + + /** This item contains the clipboard formats of the current clipboard + content that are supported both by that content and by the + DrawViewShell. + */ + ::std::unique_ptr<SvxClipboardFormatItem> mpCurrentClipboardFormats; + + /** On some occasions it is necessary to make SwitchPage calls + asynchronously. + */ + tools::AsynchronousCall maAsynchronousSwitchPageCall; + + /** This flag is used to prevent nested calls to SwitchPage(). + */ + bool mbIsInSwitchPage; + + RotateTransliteration m_aRotateCase; + + /** Listen for selection changes and broadcast context changes for the sidebar. + */ + ::rtl::Reference<svx::sidebar::SelectionChangeHandler> mpSelectionChangeHandler; + + void Construct (DrawDocShell* pDocSh, PageKind ePageKind); + + /** Depending on the given request create a new page or duplicate an + existing one. See ViewShell::CreateOrDuplicatePage() for more + information. + */ + virtual SdPage* CreateOrDuplicatePage ( + SfxRequest& rRequest, + PageKind ePageKind, + SdPage* pPage, + const sal_Int32 nInsertPosition = -1) override; + + void DuplicateSelectedSlides (SfxRequest& rRequest); + + css::uno::Reference< css::scanner::XScannerManager2 > mxScannerManager; + css::uno::Reference< css::lang::XEventListener > mxScannerListener; + rtl::Reference<TransferableClipboardListener> mxClipEvtLstnr; + bool mbPastePossible; + bool mbMouseButtonDown; + bool mbMouseSelecting; + + virtual void Notify (SfxBroadcaster& rBC, const SfxHint& rHint) override; + + /** Stop a running slide show. + */ + void StopSlideShow(); + + /** Show the context menu for snap lines and points. Because snap lines + can not be selected the index of the snap line/point for which the + popup menu is opened has to be passed to the processing slot + handlers. This can be done only by manually showing the popup menu. + @param pParent + The parent for the context menu. + @param rRect + The location at which to display the context menu. + @param rPageView + The page view is used to access the help lines. + @param nSnapLineIndex + Index of the snap line or snap point for which to show the + context menu. + */ + void ShowSnapLineContextMenu(weld::Window* pParent, const ::tools::Rectangle& rRect, + SdrPageView& rPageView, const sal_uInt16 nSnapLineIndex); + + using ViewShell::Notify; + + ::std::unique_ptr< AnnotationManager > mpAnnotationManager; + ::std::unique_ptr< ViewOverlayManager > mpViewOverlayManager; + + std::vector<std::unique_ptr<SdrExternalToolEdit>> m_ExternalEdits; + + virtual void ConfigurationChanged( utl::ConfigurationBroadcaster* pCb, ConfigurationHints ) override; + + void ConfigureAppBackgroundColor( svtools::ColorConfig* pColorConfig = nullptr ); + + /// return true if "Edit Hyperlink" in context menu should be disabled + bool ShouldDisableEditHyperlink() const; + /// force "Edit Hyperlink" to true, with the expectation that SID_EDIT_HYPERLINK is + /// later Invalidated to reset it back to its natural value + void EnableEditHyperlink(); + + // The colour of the area behind the slide (used to be called "Wiese") + Color mnAppBackgroundColor; +}; + + /// Merge the background properties together and deposit the result in rMergeAttr + void MergePageBackgroundFilling(SdPage *pPage, SdStyleSheet *pStyleSheet, bool bMasterPage, SfxItemSet& rMergedAttr); + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/EventMultiplexer.hxx b/sd/source/ui/inc/EventMultiplexer.hxx new file mode 100644 index 000000000..d6d79d11b --- /dev/null +++ b/sd/source/ui/inc/EventMultiplexer.hxx @@ -0,0 +1,172 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <rtl/ref.hxx> + +template <typename Arg, typename Ret> class Link; + +namespace sd +{ +class ViewShellBase; +} + +enum class EventMultiplexerEventId +{ + /** The EventMultiplexer itself is being disposed. Called for a live + EventMultiplexer. Removing a listener as response is not necessary, + though. + */ + Disposing, + + /** The selection in the center pane has changed. + */ + EditViewSelection, + + /** The selection in the slide sorter has changed, regardless of whether + the slide sorter is displayed in the left pane or the center pane. + */ + SlideSortedSelection, + + /** The current page has changed. + */ + CurrentPageChanged, + + /** The current MainViewShell (the ViewShell displayed in the center + pane) has been removed. + */ + MainViewRemoved, + + /** A new ViewShell has been made the MainViewShell. + */ + MainViewAdded, + + /** A new ViewShell is being displayed in one of the panes. Note that + for the ViewShell in the center pane both this event type and + EventId::MainViewAdded is broadcasted. + */ + ViewAdded, + + /** Edit mode was (or is being) switched to normal mode. Find + EventId::EditModeMaster below. + */ + EditModeNormal, + + /** One or more pages have been inserted into or deleted from the model. + */ + PageOrder, + + /** Text editing in one of the shapes in the MainViewShell has started. + */ + BeginTextEdit, + + /** Text editing in one of the shapes in the MainViewShell has ended. + */ + EndTextEdit, + + /** A UNO controller has been attached to the UNO frame. + */ + ControllerAttached, + + /** A UNO controller has been detached to the UNO frame. + */ + ControllerDetached, + + /** The state of a shape has changed. The page is available in the user data. + */ + ShapeChanged, + + /** A shape has been inserted to a page. The page is available in the + user data. + */ + ShapeInserted, + + /** A shape has been removed from a page. The page is available in the + user data. + */ + ShapeRemoved, + + /** A configuration update has been completed. + */ + ConfigurationUpdated, + + /** Edit mode was (or is being) switched to master mode. + */ + EditModeMaster, +}; + +namespace sd::tools +{ +class EventMultiplexerEvent +{ +public: + EventMultiplexerEventId meEventId; + const void* mpUserData; + + EventMultiplexerEvent(EventMultiplexerEventId eEventId, const void* pUserData); +}; + +/** This convenience class makes it easy to listen to various events that + originally are broadcasted via different channels. + + There is usually one EventMultiplexer instance per ViewShellBase(). + Call the laters GetEventMultiplexer() method to get access to that + instance. +*/ +class EventMultiplexer +{ +public: + /** Create new EventMultiplexer for the given ViewShellBase object. + */ + EventMultiplexer(ViewShellBase& rBase); + ~EventMultiplexer(); + + /** Add an event listener that will be informed about the specified + event types. + @param rCallback + The callback to call as soon as one of the event specified by + aEventTypeSet is received by the EventMultiplexer. + */ + void AddEventListener(const Link<EventMultiplexerEvent&, void>& rCallback); + + /** Remove an event listener for the specified event types. + */ + void RemoveEventListener(const Link<EventMultiplexerEvent&, void>& rCallback); + + /** This method is used for out-of-line events. An event of the + specified type will be sent to all listeners that are registered for + that type. + @param eEventId + The type of the event. + @param pUserData + Some data sent to the listeners along with the event. + */ + void MultiplexEvent(EventMultiplexerEventId eEventId, void const* pUserData); + +private: + class Implementation; + rtl::Reference<Implementation> mpImpl; +}; + +} // end of namespace ::sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/FormShellManager.hxx b/sd/source/ui/inc/FormShellManager.hxx new file mode 100644 index 000000000..b2c03b3de --- /dev/null +++ b/sd/source/ui/inc/FormShellManager.hxx @@ -0,0 +1,139 @@ +/* -*- 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 . + */ + +#pragma once + +#include "ViewShellManager.hxx" + +#include <tools/link.hxx> +#include <svl/lstner.hxx> +#include <vcl/vclptr.hxx> + +class VclWindowEvent; +class FmFormShell; +namespace vcl +{ +class Window; +} + +namespace sd::tools +{ +class EventMultiplexerEvent; +} + +namespace sd +{ +class ViewShellBase; + +/** This simple class is responsible for putting the form shell above or + below the main view shell on the shell stack maintained by the ObjectBarManager. + + The form shell is moved above the view shell when the form shell is + activated, i.e. the FormControlActivated handler is called. + + It is moved below the view shell when the main window of the + main view shell is focused. + + The form shell is created and destroyed by the ViewShellManager by using + a factory object provided by the FormShellManager. +*/ +class FormShellManager : public SfxListener +{ +public: + FormShellManager(ViewShellBase& rBase); + virtual ~FormShellManager() override; + + /** Typically called by a ShellFactory. It tells the + FormShellManager which form shell to manage. + @param pFormShell + This may be <NULL/> to disconnect the ViewShellManager from the + form shell. + */ + void SetFormShell(FmFormShell* pFormShell); + + /** Return the form shell last set with SetFormShell(). + @return + The result may be <NULL/> when the SetFormShell() method has not + yet been called or was last called with <NULL/>. + */ + FmFormShell* GetFormShell() { return mpFormShell; } + +private: + ViewShellBase& mrBase; + + /** Ownership of the form shell lies with the ViewShellManager. This + reference is kept so that the FormShellManager can detect when a new + form shell is passed to SetFormShell(). + */ + FmFormShell* mpFormShell; + + /** Remember whether the form shell is currently above or below the main + view shell. + */ + bool mbFormShellAboveViewShell; + + /** The factory is remembered so that it removed from the + ViewShellManager when the FormShellManager is destroyed. + */ + ViewShellManager::SharedShellFactory mpSubShellFactory; + + bool mbIsMainViewChangePending; + + VclPtr<vcl::Window> mpMainViewShellWindow; + + /** Register at window of center pane and at the form shell that + represents the form tool bar. The former informs this manager about + the deselection of the form shell. The later informs about its + selection. + */ + void RegisterAtCenterPane(); + + /** Unregister the listeners that were registered in + RegisterAtCenterPane(). + */ + void UnregisterAtCenterPane(); + + /** This call back is called by the application window (among others) + when the window gets the focus. In this case the form shell is + moved to the bottom of the shell stack. + */ + DECL_LINK(WindowEventHandler, VclWindowEvent&, void); + + /** This call back is called when view in the center pane is replaced. + When this happens then we unregister at the window of the old and + register at the window of the new shell. + */ + DECL_LINK(ConfigurationUpdateHandler, ::sd::tools::EventMultiplexerEvent&, void); + + /** This call back is called by the form shell when it gets the focus. + In this case the form shell is moved to the top of the shell stack. + */ + DECL_LINK(FormControlActivated, LinkParamNone*, void); + + /** This method is called by the form shell when that is destroyed. It + acts as a last resort against referencing a dead form shell. With + the factory working properly this method should not be necessary + (and may be removed in the future.) + */ + virtual void Notify(SfxBroadcaster& rBC, const SfxHint& rHint) override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/FrameView.hxx b/sd/source/ui/inc/FrameView.hxx new file mode 100644 index 000000000..8226746a3 --- /dev/null +++ b/sd/source/ui/inc/FrameView.hxx @@ -0,0 +1,213 @@ +/* -*- 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 . + */ + +#pragma once + +#include "ViewShell.hxx" +#include <svx/svdview.hxx> +#include <pres.hxx> + +class SdDrawDocument; +class SdOptions; + +namespace sd { + +/** + * View for MDIFrame + */ +class SD_DLLPUBLIC FrameView + : public SdrView +{ +public: + FrameView(SdDrawDocument* pDrawDoc, FrameView* pFrameView = nullptr ); + FrameView(const FrameView& rFrameView); + virtual ~FrameView() override; + + void Connect(); + void Disconnect(); + + void Update(SdOptions const * pOptions); + + void SetStandardHelpLines(const SdrHelpLineList& rHelpLines) + { maStandardHelpLines = rHelpLines; } + const SdrHelpLineList& GetStandardHelpLines() const { return maStandardHelpLines; } + void SetNotesHelpLines(const SdrHelpLineList& rHelpLines) + { maNotesHelpLines = rHelpLines; } + const SdrHelpLineList& GetNotesHelpLines() const { return maNotesHelpLines; } + void SetHandoutHelpLines(const SdrHelpLineList& rHelpLines) + { maHandoutHelpLines = rHelpLines; } + const SdrHelpLineList& GetHandoutHelpLines() const { return maHandoutHelpLines; } + + void SetVisibleLayers(const SdrLayerIDSet& rVisibleLayers) + { maVisibleLayers = rVisibleLayers; } + const SdrLayerIDSet& GetVisibleLayers() const { return maVisibleLayers; } + + void SetLockedLayers(const SdrLayerIDSet& rLockedLayers) + { maLockedLayers = rLockedLayers; } + const SdrLayerIDSet& GetLockedLayers() const { return maLockedLayers; } + + void SetPrintableLayers(const SdrLayerIDSet& rPrintableLayers) + { maPrintableLayers = rPrintableLayers; } + const SdrLayerIDSet& GetPrintableLayers() const { return maPrintableLayers; } + + void SetRuler(const bool bRulerOn) + { mbRuler = bRulerOn; } + bool HasRuler() const { return mbRuler; } + + void SetNoColors(const bool bNoCol) + { mbNoColors = bNoCol; } + bool IsNoColors() const { return mbNoColors; } + + void SetNoAttribs(const bool bNoAttr) + { mbNoAttribs = bNoAttr; } + bool IsNoAttribs() const { return mbNoAttribs; } + + void SetVisArea(const ::tools::Rectangle& rVisArea) + { maVisArea = rVisArea; } + const ::tools::Rectangle& GetVisArea() const { return maVisArea; } + + void SetPageKind(PageKind eKind) { mePageKind = eKind; } + PageKind GetPageKind() const { return mePageKind; } + + /** is used in FrameView::ReadUserDataSequence() only to store the + page kind that was selected while last saving this document */ + void SetPageKindOnLoad(PageKind eKind) { mePageKindOnLoad = eKind; } + + /** can be used to get the page kind that was selected on last save of this document */ + PageKind GetPageKindOnLoad() const { return mePageKindOnLoad; } + + void SetSelectedPage (sal_uInt16 nPage); + sal_uInt16 GetSelectedPage () const { return mnSelectedPage;} + + /** is used in FrameView::ReadUserDataSequence() only to store the + page that was selected while last saving this document */ + void SetSelectedPageOnLoad (sal_uInt16 nPage) { mnSelectedPageOnLoad = nPage; } + + /** can be used to get the page that was selected on last save of this document */ + sal_uInt16 GetSelectedPageOnLoad () const { return mnSelectedPageOnLoad; } + + void SetViewShEditMode(EditMode eMode); + EditMode GetViewShEditMode () const; + + /** Remember the edit mode of the main view shell at the time when the + document is loaded. + */ + void SetViewShEditModeOnLoad (const EditMode eMode); + + /** Return the value of the edit mode as it was when the document was + loaded. + */ + EditMode GetViewShEditModeOnLoad() const { return meEditModeOnLoad;} + + void SetLayerMode(bool bMode) + { mbLayerMode = bMode; } + bool IsLayerMode() const { return mbLayerMode; } + + void SetQuickEdit(bool bQEdit) + { mbQuickEdit = bQEdit; } + bool IsQuickEdit() const { return mbQuickEdit; } + + void SetDoubleClickTextEdit( bool bOn ) { mbDoubleClickTextEdit = bOn; } + bool IsDoubleClickTextEdit() const { return mbDoubleClickTextEdit; } + + void SetClickChangeRotation( bool bOn ) { mbClickChangeRotation = bOn; } + bool IsClickChangeRotation() const { return mbClickChangeRotation; } + + /** Remember the type of the view shell that was (or soon will be) + previously associated with this frame view. + @param eType + The type of the previous view shell or ViewShell::ST_NONE to + indicate that there is no previous view shell. + */ + void SetPreviousViewShellType (ViewShell::ShellType eType); + + /** Return the type of the view shell previously associated with this + frame view. + */ + ViewShell::ShellType GetPreviousViewShellType() const { return mePreviousViewShellType;} + + /** Remember the type of the view shell at the time when the document is + loaded or, rather, when the ViewShellBase is constructed. + */ + void SetViewShellTypeOnLoad (ViewShell::ShellType eType); + + ViewShell::ShellType GetViewShellTypeOnLoad() const { return meViewShellTypeOnLoad;} + + void SetPresentationViewShellId(sal_uInt16 nId) + { mnPresViewShellId = nId; } + sal_uInt16 GetPresentationViewShellId() const { return mnPresViewShellId; } + + void SetSlidesPerRow(sal_uInt16 nSlides) { mnSlidesPerRow = nSlides; } + sal_uInt16 GetSlidesPerRow() const { return mnSlidesPerRow; } + + void SetDrawMode(DrawModeFlags nNewDrawMode) { mnDrawMode = nNewDrawMode; }; + DrawModeFlags GetDrawMode() const { return mnDrawMode; }; + + void SetIsNavigatorShowingAllShapes (const bool bIsNavigatorShowingAllShapes); + bool IsNavigatorShowingAllShapes() const { return mbIsNavigatorShowingAllShapes;} + + void WriteUserDataSequence ( css::uno::Sequence < css::beans::PropertyValue >& ); + void ReadUserDataSequence ( const css::uno::Sequence < css::beans::PropertyValue >& ); + +private: + sal_uInt16 mnRefCount; + bool mbRuler; + SdrLayerIDSet maVisibleLayers; + SdrLayerIDSet maLockedLayers; + SdrLayerIDSet maPrintableLayers; + SdrHelpLineList maStandardHelpLines; + SdrHelpLineList maNotesHelpLines; + SdrHelpLineList maHandoutHelpLines; + bool mbNoColors; ///< structuring mode + bool mbNoAttribs; ///< structuring mode + ::tools::Rectangle maVisArea; ///< visible area + PageKind mePageKind; ///< kind of page (standard, notes, handout) + sal_uInt16 mnSelectedPage; + PageKind mePageKindOnLoad; + sal_uInt16 mnSelectedPageOnLoad; + EditMode mePageEditMode; ///< edit mode in drawing mode (Page/MasterPage) + // EditMode meStandardEditMode; ///< edit mode in drawing mode (Page/MasterPage) + // EditMode meNotesEditMode; ///< edit mode in notes mode (Page/MasterPage) + // EditMode meHandoutEditMode; ///< edit mode in handout mode (Page/MasterPage) + EditMode meEditModeOnLoad; + bool mbLayerMode; ///< layer on/off + bool mbQuickEdit; ///< QuickEdit on/off + bool mbDoubleClickTextEdit; ///< text mode after double click + bool mbClickChangeRotation; ///< single click switches between selection/rotation mode + sal_uInt16 mnPresViewShellId; ///< ViewShell from which the presentation was started + sal_uInt16 mnSlidesPerRow; ///< slides per row on the slide-desk + DrawModeFlags mnDrawMode; ///< draw mode for the normal window + /** Remember whether the navigator shows all shapes (<TRUE/>) or only + the names ones (<FALSE/>). Not persistent. + */ + bool mbIsNavigatorShowingAllShapes; + + /** The type of the previous view shell. The (default) value + ViewShell::ST_NONE indicates that there was no previous view shell. + Note that this value is used only temporarily and is not saved or + restored. + */ + ViewShell::ShellType mePreviousViewShellType; + + ViewShell::ShellType meViewShellTypeOnLoad; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/GraphicDocShell.hxx b/sd/source/ui/inc/GraphicDocShell.hxx new file mode 100644 index 000000000..40df981cf --- /dev/null +++ b/sd/source/ui/inc/GraphicDocShell.hxx @@ -0,0 +1,54 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/docfac.hxx> +#include <sfx2/objsh.hxx> +#include "DrawDocShell.hxx" +#include <glob.hxx> +#include <sddllapi.h> + +namespace sd +{ +/** + * document shell for draw documents + */ +class SD_DLLPUBLIC GraphicDocShell : public DrawDocShell +{ +public: + SFX_DECL_INTERFACE(SD_IF_SDGRAPHICDOCSHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + SFX_DECL_OBJECTFACTORY(); + + GraphicDocShell(SfxObjectCreateMode eMode); + + GraphicDocShell(SfxModelFlags nModelCreationFlags); + + virtual ~GraphicDocShell() override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/GraphicObjectBar.hxx b/sd/source/ui/inc/GraphicObjectBar.hxx new file mode 100644 index 000000000..7d53a86d2 --- /dev/null +++ b/sd/source/ui/inc/GraphicObjectBar.hxx @@ -0,0 +1,54 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/shell.hxx> +#include <glob.hxx> + +namespace sd { + +class View; +class ViewShell; + +class GraphicObjectBar final + : public SfxShell +{ +public: + SFX_DECL_INTERFACE( SD_IF_SDDRAWGRAFOBJECTBAR ) + + GraphicObjectBar (const ViewShell* pSdViewShell, ::sd::View* pSdView); + virtual ~GraphicObjectBar() override; + + void GetAttrState( SfxItemSet& rSet ); + void Execute( SfxRequest& rReq ); + + void GetFilterState( SfxItemSet& rSet ); + void ExecuteFilter( SfxRequest const & rReq ); + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + + ::sd::View* mpView; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/GraphicViewShell.hxx b/sd/source/ui/inc/GraphicViewShell.hxx new file mode 100644 index 000000000..d730c2dd9 --- /dev/null +++ b/sd/source/ui/inc/GraphicViewShell.hxx @@ -0,0 +1,72 @@ +/* -*- 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 . + */ + +#pragma once + +#include "DrawViewShell.hxx" + +namespace vcl +{ +class Window; +} + +namespace sd +{ +/** View shell of the Draw application. + + <p>This class is an example of how not to do it: specialization by + inheritance. A graphic view shell is similar to a draw view shell + but lacks some of its features. Thus is should be at most a base + class of DrawViewShell. There even is special case code in + ViewShell that turns off some of the features for GraphicViewShell + instances.</p> +*/ +class SAL_DLLPUBLIC_RTTI GraphicViewShell final : public DrawViewShell +{ +public: + SFX_DECL_VIEWFACTORY(GraphicViewShell); + SFX_DECL_INTERFACE(SD_IF_SDGRAPHICVIEWSHELL) + + /** Create a new view shell for the Draw application. + @param rViewShellBase + The new object will be stacked on this view shell base. + @param pFrameView + The frame view that makes it possible to pass information from + one view shell to the next. + */ + GraphicViewShell(ViewShellBase& rViewShellBase, vcl::Window* pParentWindow, + FrameView* pFrameView); + + virtual ~GraphicViewShell() override; + + /** Override this method in order to have the layer mode always active. + */ + virtual void ChangeEditMode(EditMode eMode, bool bIsLayerModeActive) override; + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + + void ConstructGraphicViewShell(); + virtual void ArrangeGUIElements() override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/GraphicViewShellBase.hxx b/sd/source/ui/inc/GraphicViewShellBase.hxx new file mode 100644 index 000000000..89a96cf51 --- /dev/null +++ b/sd/source/ui/inc/GraphicViewShellBase.hxx @@ -0,0 +1,50 @@ +/* -*- 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 . + */ + +#pragma once + +#include "ViewShellBase.hxx" + +namespace sd +{ +/** This class exists to be able to register another factory that + creates the view shell for the Draw application. +*/ +class GraphicViewShellBase : public ViewShellBase +{ +public: + SFX_DECL_VIEWFACTORY(GraphicViewShellBase); + + /** This constructor is used by the view factory of the SFX + macros. + */ + GraphicViewShellBase(SfxViewFrame* pFrame, SfxViewShell* pOldShell); + virtual ~GraphicViewShellBase() override; + + /** Callback function for general slot calls. + */ + virtual void Execute(SfxRequest& rRequest) override; + +protected: + virtual void InitializeFramework() override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/ImpressViewShellBase.hxx b/sd/source/ui/inc/ImpressViewShellBase.hxx new file mode 100644 index 000000000..80070e7c8 --- /dev/null +++ b/sd/source/ui/inc/ImpressViewShellBase.hxx @@ -0,0 +1,50 @@ +/* -*- 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 . + */ + +#pragma once + +#include "ViewShellBase.hxx" + +namespace sd +{ +/** This class implements a few features that exist only for the Impress + application. +*/ +class ImpressViewShellBase : public ViewShellBase +{ +public: + SFX_DECL_VIEWFACTORY(ImpressViewShellBase); + + /** This constructor is used by the view factory of the SFX + macros. + */ + ImpressViewShellBase(SfxViewFrame* pFrame, SfxViewShell* pOldShell); + virtual ~ImpressViewShellBase() override; + + /** Callback function for general slot calls. + */ + virtual void Execute(SfxRequest& rRequest) override; + +protected: + virtual void InitializeFramework() override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/LayerTabBar.hxx b/sd/source/ui/inc/LayerTabBar.hxx new file mode 100644 index 000000000..297a9302d --- /dev/null +++ b/sd/source/ui/inc/LayerTabBar.hxx @@ -0,0 +1,108 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <string_view> + +#include <svtools/tabbar.hxx> +#include <vcl/transfer.hxx> +#include <sddllapi.h> + +namespace sd { + +/** + * TabBar for layer administration + */ +class DrawViewShell; + +class SAL_DLLPUBLIC_RTTI LayerTabBar final + : public TabBar, + public DropTargetHelper +{ +public: + LayerTabBar ( + DrawViewShell* pDrViewSh, + vcl::Window* pParent); + virtual void dispose() override; + virtual ~LayerTabBar() override; + + /** Inform all listeners of this control that the current layer has been + activated. Call this method after switching the current layer and is + not done elsewhere (like when using ctrl + page up/down keys). + */ + void SendActivatePageEvent(); + + /** Inform all listeners of this control that the current layer has been + deactivated. Call this method before switching the current layer + and is not done elsewhere (like when using ctrl page up/down keys). + */ + void SendDeactivatePageEvent(); + + // Expects not-localized, real layer name in rText. Generates a localized layer name + // that will be displayed on the tab of the LayerTabBar and writes the real name + // to maAuxiliaryText. In case you want no entry in maAuxiliaryText, use method from TabBar. + virtual void InsertPage( sal_uInt16 nPageId, const OUString& rText, + TabBarPageBits nBits = TabBarPageBits::NONE, + sal_uInt16 nPos = TabBar::APPEND ) override; + virtual void SetPageText( sal_uInt16 nPageId, const OUString& rText ) override; + + // Returns the real layer name if exists and empty OUString otherwise. + OUString GetLayerName(sal_uInt16 nPageId) const; + + // Used e.g. in DeleteActualLayer() to test whether deleting is allowed. + static bool IsRealNameOfStandardLayer(std::u16string_view rName); + + // Used e.g. in validity test of user entered names + static bool IsLocalizedNameOfStandardLayer(std::u16string_view rName); + + // In case rName is one of the sUNO_LayerName_*, it generates a localized name, + // otherwise it returns value of rName. + static OUString convertToLocalizedName(const OUString& rName); + + // TabBar + virtual void Select() override; + virtual void DoubleClick() override; + + SD_DLLPUBLIC virtual void MouseButtonDown(const MouseEvent& rMEvt) override; // export for unit test + + virtual void Command(const CommandEvent& rCEvt) override; + + virtual bool StartRenaming() override; + virtual TabBarAllowRenamingReturnCode AllowRenaming() override; + virtual void EndRenaming() override; + + virtual void ActivatePage() override; + + // DropTargetHelper + virtual sal_Int8 AcceptDrop( const AcceptDropEvent& rEvt ) override; + virtual sal_Int8 ExecuteDrop( const ExecuteDropEvent& rEvt ) override; + +private: + DrawViewShell* pDrViewSh; + + // Expects not-localized, real layer name in rText and writes it to maAuxiliaryText. + void SetLayerName( sal_uInt16 nPageId, const OUString& rText ); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/MasterPageObserver.hxx b/sd/source/ui/inc/MasterPageObserver.hxx new file mode 100644 index 000000000..96f4a3741 --- /dev/null +++ b/sd/source/ui/inc/MasterPageObserver.hxx @@ -0,0 +1,119 @@ +/* -*- 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 . + */ + +#pragma once + +#include <rtl/ustring.hxx> +#include <tools/link.hxx> +#include "tools/SdGlobalResourceContainer.hxx" +#include <memory> +#include <set> + +namespace osl +{ +class Mutex; +} + +class SdDrawDocument; + +namespace sd +{ +class MasterPageObserverEvent; + +/** This singleton observes all registered documents for changes in the used + master pages and in turn informs its listeners about it. One such + listener is the master page selector control in the tool panel that + shows the recently used master pages. +*/ +class MasterPageObserver : public SdGlobalResource +{ +public: + typedef ::std::set<OUString> MasterPageNameSet; + + /** Return the single instance of this class. + */ + static MasterPageObserver& Instance(); + + /** The master page observer will listen to events of this document and + detect changes of the use of master pages. + */ + void RegisterDocument(SdDrawDocument& rDocument); + + /** The master page observer will stop to listen to events of this + document. + */ + void UnregisterDocument(SdDrawDocument& rDocument); + + /** Add a listener that is informed of master pages that are newly + assigned to slides or become unassigned. + @param rEventListener + The event listener to call for future events. Call + RemoveEventListener() before the listener is destroyed. + */ + void AddEventListener(const Link<MasterPageObserverEvent&, void>& rEventListener); + + /** Remove the given listener from the list of listeners. + @param rEventListener + After this method returns the given listener is not called back + from this object. Passing a listener that has not + been registered before is safe and is silently ignored. + */ + void RemoveEventListener(const Link<MasterPageObserverEvent&, void>& rEventListener); + +private: + class Implementation; + ::std::unique_ptr<Implementation> mpImpl; + + MasterPageObserver(); + virtual ~MasterPageObserver() override; + + MasterPageObserver(const MasterPageObserver&) = delete; + + MasterPageObserver& operator=(const MasterPageObserver&) = delete; +}; + +/** Objects of this class are sent to listeners of the MasterPageObserver + singleton when the list of master pages of one document has changed. +*/ +class MasterPageObserverEvent +{ +public: + enum EventType + { + /// Master page already exists when document is registered. + ET_MASTER_PAGE_EXISTS, + /// Master page has been added to a document. + ET_MASTER_PAGE_ADDED, + /// Master page has been removed from to a document. + ET_MASTER_PAGE_REMOVED + }; + + EventType meType; + const OUString& mrMasterPageName; + + MasterPageObserverEvent(EventType eType, const OUString& rMasterPageName) + : meType(eType) + , mrMasterPageName(rMasterPageName) + { + } +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/MediaObjectBar.hxx b/sd/source/ui/inc/MediaObjectBar.hxx new file mode 100644 index 000000000..b7c56ef00 --- /dev/null +++ b/sd/source/ui/inc/MediaObjectBar.hxx @@ -0,0 +1,56 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/shell.hxx> +#include <glob.hxx> + +class SfxInterface; +class SfxItemSet; +class SfxModule; +class SfxRequest; + +namespace sd { + +class View; +class ViewShell; + +class MediaObjectBar final + : public SfxShell +{ +public: + SFX_DECL_INTERFACE( SD_IF_SDDRAWMEDIAOBJECTBAR ) + + MediaObjectBar (const ViewShell* pSdViewShell, ::sd::View* pSdView); + virtual ~MediaObjectBar() override; + + void GetState( SfxItemSet& rSet ); + void Execute( SfxRequest const & rReq ); + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + + ::sd::View* mpView; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/NavigatorChildWindow.hxx b/sd/source/ui/inc/NavigatorChildWindow.hxx new file mode 100644 index 000000000..4199cab67 --- /dev/null +++ b/sd/source/ui/inc/NavigatorChildWindow.hxx @@ -0,0 +1,40 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/childwin.hxx> +#include <sfx2/navigat.hxx> + +namespace vcl { class Window; } +class SfxBindings; + +namespace sd { + +class SdNavigatorWrapper final : public SfxNavigatorWrapper +{ +public: + SdNavigatorWrapper(vcl::Window *pParent, sal_uInt16 nId, + SfxBindings* pBindings, SfxChildWinInfo* pInfo); + SFX_DECL_CHILDWINDOW(SdNavigatorWrapper); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/OutlineBulletDlg.hxx b/sd/source/ui/inc/OutlineBulletDlg.hxx new file mode 100644 index 000000000..512d45f9a --- /dev/null +++ b/sd/source/ui/inc/OutlineBulletDlg.hxx @@ -0,0 +1,51 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/tabdlg.hxx> + +namespace sd +{ +class View; + +/** + * Bullet-Tab-Dialog + */ +class OutlineBulletDlg : public SfxTabDialogController +{ +public: + OutlineBulletDlg(weld::Window* pParent, const SfxItemSet* pAttr, ::sd::View* pView); + virtual ~OutlineBulletDlg() override; + + const SfxItemSet* GetBulletOutputItemSet() const; + +protected: + virtual void PageCreated(const OString& rId, SfxTabPage& rPage) override; + +private: + SfxItemSet m_aInputSet; + std::unique_ptr<SfxItemSet> m_xOutputSet; + bool m_bTitle; + ::sd::View* m_pSdView; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/OutlineView.hxx b/sd/source/ui/inc/OutlineView.hxx new file mode 100644 index 000000000..058f6323a --- /dev/null +++ b/sd/source/ui/inc/OutlineView.hxx @@ -0,0 +1,230 @@ +/* -*- 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 . + */ + +#pragma once + +#include <memory> +#include <vcl/image.hxx> +#include <vcl/vclevent.hxx> +#include <editeng/lrspitem.hxx> +#include <o3tl/deleter.hxx> +#include "View.hxx" + +class SdPage; +class SdrPage; +class SdrTextObj; +class SfxProgress; +struct PasteOrDropInfos; +class EditView; + +namespace sd::tools { + class EventMultiplexerEvent; +} + +namespace sd { + +class DrawDocShell; +class OutlineViewShell; +class OutlineViewModelChangeGuard; + +const int MAX_OUTLINERVIEWS = 4; + +/** + * Derivative of ::sd::View for the outline mode +|* +\************************************************************************/ + +class OutlineView + : public ::sd::View +{ + friend class OutlineViewModelChangeGuard; +public: + OutlineView (DrawDocShell& rDocSh, + vcl::Window* pWindow, + OutlineViewShell& rOutlineViewSh); + virtual ~OutlineView() override; + + /** This method is called by the view shell that owns the view to tell + the view that it can safely connect to the application. + This method must not be called before the view shell is on the shell + stack. + */ + void ConnectToApplication(); + void DisconnectFromApplication(); + + + static SdrTextObj* GetTitleTextObject(SdrPage const * pPage); + static SdrTextObj* GetOutlineTextObject(SdrPage const * pPage); + + static SdrTextObj* CreateTitleTextObject(SdPage* pPage); + static SdrTextObj* CreateOutlineTextObject(SdPage* pPage); + + virtual void AddWindowToPaintView(OutputDevice* pWin, vcl::Window* pWindow) override; + virtual void DeleteWindowFromPaintView(OutputDevice* pWin) override; + + OutlinerView* GetViewByWindow(vcl::Window const * pWin) const; + SdrOutliner& GetOutliner() { return mrOutliner; } + + Paragraph* GetPrevTitle(const Paragraph* pPara); + Paragraph* GetNextTitle(const Paragraph* pPara); + SdPage* GetActualPage(); + SdPage* GetPageForParagraph( Paragraph* pPara ); + Paragraph* GetParagraphForPage( ::Outliner const & rOutl, SdPage const * pPage ); + + /** selects the paragraph for the given page at the outliner view*/ + void SetActualPage( SdPage const * pActual ); + + void Paint (const ::tools::Rectangle& rRect, ::sd::Window const * pWin); + + // Callbacks for LINKs + DECL_LINK( ParagraphInsertedHdl, ::Outliner::ParagraphHdlParam, void ); + DECL_LINK( ParagraphRemovingHdl, ::Outliner::ParagraphHdlParam, void ); + DECL_LINK( DepthChangedHdl, ::Outliner::DepthChangeHdlParam, void ); + DECL_LINK( StatusEventHdl, EditStatus&, void ); + DECL_LINK( BeginMovingHdl, ::Outliner *, void ); + DECL_LINK( EndMovingHdl, ::Outliner *, void ); + DECL_LINK( RemovingPagesHdl, OutlinerView *, bool ); + DECL_LINK( IndentingPagesHdl, OutlinerView *, bool ); + DECL_LINK( BeginDropHdl, EditView*, void ); + DECL_LINK( EndDropHdl, EditView*, void ); + DECL_LINK( PaintingFirstLineHdl, PaintFirstLineInfo*, void ); + + sal_uLong GetPaperWidth() const { return mnPaperWidth;} + + void PrepareClose(); + + virtual void GetAttributes( SfxItemSet& rTargetSet, bool bOnlyHardAttr = false ) const override; + virtual bool SetAttributes(const SfxItemSet& rSet, bool bReplaceAll = false, bool bSlide = false, bool bMaster = false) override; + + void FillOutliner(); + void SetLinks(); + void ResetLinks() const; + + SfxStyleSheet* GetStyleSheet() const override; + + void SetSelectedPages(); + + virtual sal_Int8 AcceptDrop ( + const AcceptDropEvent& rEvt, + DropTargetHelper& rTargetHelper, + SdrLayerID nLayer) override; + virtual sal_Int8 ExecuteDrop ( + const ExecuteDropEvent& rEvt, + ::sd::Window* pTargetWindow, + sal_uInt16 nPage, + SdrLayerID nLayer) override; + + // Re-implement GetScriptType for this view to get correct results + virtual SvtScriptType GetScriptType() const override; + + /** After this method has been called with <TRUE/> following changes of + the current page are ignored in that the corresponding text is not + selected. + This is used to suppress unwanted side effects between selection and + cursor position. + */ + void IgnoreCurrentPageChanges (bool bIgnore); + + /** creates and inserts an empty slide for the given paragraph. */ + SdPage* InsertSlideForParagraph( Paragraph* pPara ); + + void UpdateParagraph( sal_Int32 nPara ); + +protected: + virtual void OnBeginPasteOrDrop( PasteOrDropInfos* pInfo ) override; + virtual void OnEndPasteOrDrop( PasteOrDropInfos* pInfo ) override; + +private: + /** call this method before you do anything that can modify the outliner + and or the drawing document model. It will create needed undo actions */ + void BeginModelChange(); + + /** call this method after BeginModelChange(), when all possible model + changes are done. */ + void EndModelChange(); + + /** merge edit engine undo actions if possible */ + void TryToMergeUndoActions(); + + /** updates all changes in the outliner model to the draw model */ + void UpdateDocument(); + + OutlineViewShell& mrOutlineViewShell; + SdrOutliner& mrOutliner; + std::unique_ptr<OutlinerView> mpOutlinerViews[MAX_OUTLINERVIEWS]; + + std::vector<Paragraph*> maOldParaOrder; + std::vector<Paragraph*> maSelectedParas; + + sal_Int32 mnPagesToProcess; // for the progress bar + sal_Int32 mnPagesProcessed; + + bool mbFirstPaint; + + sal_uLong mnPaperWidth; + + std::unique_ptr<SfxProgress> mpProgress; + + /** stores the last used document color. + this is changed in onUpdateStyleSettings() + */ + Color maDocColor; + + /** updates the high contrast settings and document color if they changed. + @param bForceUpdate forces the method to set all style settings + */ + void onUpdateStyleSettings( bool bForceUpdate ); + + /** this link is called from the vcl application when the stylesettings + change. Its only purpose is to call onUpdateStyleSettings() then. + */ + DECL_LINK( AppEventListenerHdl, VclSimpleEvent&, void ); + + DECL_LINK(EventMultiplexerListener, sd::tools::EventMultiplexerEvent&, void); + + /** holds a model guard during drag and drop between BeginMovingHdl and EndMovingHdl */ + std::unique_ptr<OutlineViewModelChangeGuard, o3tl::default_delete<OutlineViewModelChangeGuard>> maDragAndDropModelGuard; + + SvxLRSpaceItem maLRSpaceItem; + Image maSlideImage; +}; + +// calls IgnoreCurrentPageChangesLevel with true in ctor and with false in dtor +class OutlineViewPageChangesGuard +{ +public: + OutlineViewPageChangesGuard( OutlineView* pView ); + ~OutlineViewPageChangesGuard(); +private: + OutlineView* mpView; +}; + +// calls BeginModelChange() on c'tor and EndModelChange() on d'tor +class OutlineViewModelChangeGuard +{ +public: + OutlineViewModelChangeGuard( OutlineView& rView ); + ~OutlineViewModelChangeGuard() COVERITY_NOEXCEPT_FALSE; +private: + OutlineView& mrView; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/OutlineViewShell.hxx b/sd/source/ui/inc/OutlineViewShell.hxx new file mode 100644 index 000000000..6bc230189 --- /dev/null +++ b/sd/source/ui/inc/OutlineViewShell.hxx @@ -0,0 +1,163 @@ +/* -*- 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 . + */ + +#pragma once + +#include "ViewShell.hxx" +#include <glob.hxx> + +class SdPage; +class TransferableDataHelper; +class TransferableClipboardListener; + +namespace sd { class OutlineView; } + +namespace sd { + +/** Show a textual overview of the text contents of all slides. +*/ +class OutlineViewShell + : public ViewShell +{ +public: + + SFX_DECL_VIEWFACTORY(OutlineViewShell); + SFX_DECL_INTERFACE(SD_IF_SDOUTLINEVIEWSHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + /** Create a new view shell for the outline mode. + @param rViewShellBase + The new object will be stacked on this view shell base. + @param pFrameView + The frame view that makes it possible to pass information from + one view shell to the next. + */ + OutlineViewShell ( + SfxViewFrame* pFrame, + ViewShellBase& rViewShellBase, + vcl::Window* pParentWindow, + FrameView* pFrameView); + + virtual ~OutlineViewShell() override; + + virtual void Shutdown() override; + + virtual void Paint(const ::tools::Rectangle& rRect, ::sd::Window* pWin) override; + + /** Arrange and resize the GUI elements like rulers, sliders, and + buttons as well as the actual document view according to the size of + the enclosing window and current sizes of buttons, rulers, and + sliders. + */ + virtual void ArrangeGUIElements() override; + + virtual bool PrepareClose( bool bUI = true ) override; + + virtual void VirtHScrollHdl(ScrollBar* pHScroll) override; + virtual void VirtVScrollHdl(ScrollBar* pVHScroll) override; + + virtual void Activate( bool IsMDIActivate ) override; + virtual void Deactivate( bool IsMDIActivate ) override; + + virtual SdPage* GetActualPage() override; + + /// inherited from sd::ViewShell + virtual SdPage* getCurrentPage() const override; + + void ExecCtrl(SfxRequest &rReq); + void GetCtrlState(SfxItemSet &rSet); + // FIXME non-virtual override??? + void GetMenuState(SfxItemSet &rSet); + void GetAttrState(SfxItemSet &rSet); + void GetState (SfxItemSet& rSet); + + static void ExecStatusBar(SfxRequest& rReq); + void GetStatusBarState(SfxItemSet& rSet); + + void FuTemporary(SfxRequest &rReq); + void FuTemporaryModify(SfxRequest &rReq); + void FuPermanent(SfxRequest &rReq); + void FuSupport(SfxRequest &rReq); + + virtual void SetZoom(::tools::Long nZoom) override; + virtual void SetZoomRect(const ::tools::Rectangle& rZoomRect) override; + + void Execute(SfxRequest& rReq); + + virtual void ReadFrameViewData(FrameView* pView) override; + virtual void WriteFrameViewData() override; + + virtual void Command( const CommandEvent& rCEvt, ::sd::Window* pWin ) override; + virtual bool KeyInput(const KeyEvent& rKEvt, ::sd::Window* pWin) override; + virtual void MouseButtonUp(const MouseEvent& rMEvt, ::sd::Window* pWin) override; + + ErrCode ReadRtf(SvStream& rInput); + + virtual void WriteUserDataSequence ( css::uno::Sequence < css::beans::PropertyValue >& ) override; + virtual void ReadUserDataSequence ( const css::uno::Sequence < css::beans::PropertyValue >& ) override; + + /** this method is called when the visible area of the view from this viewshell is changed */ + virtual void VisAreaChanged(const ::tools::Rectangle& rRect) override; + + /** Create an accessible object representing the specified window. + @param pWindow + The returned object makes the document displayed in this window + accessible. + @return + Returns an <type>AccessibleDrawDocumentView</type> object. + */ + virtual css::uno::Reference<css::accessibility::XAccessible> + CreateAccessibleDocumentView (::sd::Window* pWindow) override; + + /** Update the preview to show the specified page. + */ + virtual void UpdatePreview (SdPage* pPage) override; + + virtual css::uno::Reference<css::drawing::XDrawSubController> CreateSubController() override; + + /** Make the given page the new current page. This method + notifies the controller and adapts the selection of the + model. + @param pPage + The new current page. Pass NULL when there is no current page. + */ + void SetCurrentPage (SdPage* pPage); + + void UpdateTitleObject( SdPage* pPage, Paragraph const * pPara ); + void UpdateOutlineObject( SdPage* pPage, Paragraph* pPara ); + +private: + OUString m_StrOldPageName; + std::unique_ptr<OutlineView> pOlView; + SdPage* pLastPage; // For efficient processing of the preview + rtl::Reference<TransferableClipboardListener> mxClipEvtLstnr; + bool bPastePossible; + bool mbInitialized; + + void Construct(); + DECL_LINK( ClipboardChanged, TransferableDataHelper*, void ); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/OutlineViewShellBase.hxx b/sd/source/ui/inc/OutlineViewShellBase.hxx new file mode 100644 index 000000000..13527d80d --- /dev/null +++ b/sd/source/ui/inc/OutlineViewShellBase.hxx @@ -0,0 +1,43 @@ +/* -*- 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 . + */ + +#pragma once + +#include "ImpressViewShellBase.hxx" + +namespace sd +{ +/** This class exists to be able to register a factory that + creates an outline view shell as default. +*/ +class OutlineViewShellBase : public ImpressViewShellBase +{ +public: + SFX_DECL_VIEWFACTORY(OutlineViewShellBase); + + /** This constructor is used by the view factory of the SFX + macros. + */ + OutlineViewShellBase(SfxViewFrame* pFrame, SfxViewShell* pOldShell); + virtual ~OutlineViewShellBase() override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/OutlinerIteratorImpl.hxx b/sd/source/ui/inc/OutlinerIteratorImpl.hxx new file mode 100644 index 000000000..00be547c0 --- /dev/null +++ b/sd/source/ui/inc/OutlinerIteratorImpl.hxx @@ -0,0 +1,239 @@ +/* -*- 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 . + */ + +#pragma once + +#include <OutlinerIterator.hxx> +#include <memory> + +class SdDrawDocument; +class SdPage; +class SdrObjListIter; + +namespace sd { + +class ViewShell; + +namespace outliner { + +/** Base class for the polymorphic implementation class of the + <type>Iterator</type> class. The iterators based on this class are + basically uni directional iterators. Their direction can, however, be + reversed at any point of their life time. +*/ +class IteratorImplBase +{ +public: + /** The constructor stores the given arguments to be used by the derived + classes. + @param pDocument + The document provides the information to be iterated on. + @param pViewShellWeak + Some information has to be taken from the view shell. + @param bDirectionIsForward + This flag defines the iteration direction. When <TRUE/> then + the direction is forwards otherwise it is backwards. + */ + IteratorImplBase (SdDrawDocument* pDocument, + const std::weak_ptr<ViewShell>& rpViewShellWeak, + bool bDirectionIsForward); + IteratorImplBase (SdDrawDocument* pDocument, + const std::weak_ptr<ViewShell>& rpViewShellWeak, + bool bDirectionIsForward, PageKind ePageKind, EditMode eEditMode); + virtual ~IteratorImplBase(); + + /** Advance to the next text of the current object or to the next object. + This takes the iteration direction into + account. The new object pointed to can be retrieved (among other + information) by calling the <member>GetPosition</member> method. + */ + virtual void GotoNextText() = 0; + /** Return an object that describes the current object. + @return + The returned object describes the current object pointed to by + the iterator. See the description of + <type>IteratorPosition</type> for details on the available + information. + */ + virtual const IteratorPosition& GetPosition(); + /** Create an exact copy of this object. No argument should be + specified when called from the outside. It then creates an object + first and passes that to the inherited <member>Clone()</member> + methods to fill in class specific information. + @return + Returns a copy of this object. When this method is called with + an argument then this value will be returned. + */ + virtual IteratorImplBase* Clone (IteratorImplBase* pObject=nullptr) const; + /** Test the equality of the this object and the given iterator. Two + iterators are taken to be equal when they point to the same object. + Iteration direction is not taken into account. + @param rIterator + The iterator to compare to. + @return + When both iterators are equal <TRUE/> is returned, <FALSE/> otherwise. + */ + virtual bool operator== (const IteratorImplBase& rIterator) const; + /** This method is used by the equality operator. It is part of a "multimethod" pattern. + @param rIterator + The iterator to compare to. + @return + Returns <TRUE/> when both iterators point to the same object. + */ + virtual bool IsEqualSelection(const IteratorImplBase& rIterator) const; + /** Reverse the direction of iteration. The current object stays the same. + */ + virtual void Reverse(); + +protected: + /// The current position as returned by <member>GetPosition()</member>. + IteratorPosition maPosition; + /// The document on whose data the iterator operates. + SdDrawDocument* mpDocument; + /// Necessary secondary source of information. + std::weak_ptr<ViewShell> mpViewShellWeak; + /// Specifies the search direction. + bool mbDirectionIsForward; +}; + +/** Iterator all objects that belong to the current mark list + a.k.a. selection. It is assumed that all marked objects belong to the + same page. It is further assumed that the mark list does not change + while an iterator is alive. It is therefore the responsibility of an + iterator's owner to handle the case of a changed mark list. + + <p>For documentation of the methods please refer to the base class + <type>IteratorImplBase</type>.</p> +*/ +class SelectionIteratorImpl + : public IteratorImplBase +{ +public: + SelectionIteratorImpl ( + const ::std::vector< ::tools::WeakReference<SdrObject> >& rObjectList, + sal_Int32 nObjectIndex, + SdDrawDocument* pDocument, + const std::weak_ptr<ViewShell>& rpViewShellWeak, + bool bDirectionIsForward); + SelectionIteratorImpl (const SelectionIteratorImpl& rObject); + virtual ~SelectionIteratorImpl() override; + + virtual void GotoNextText() override; + virtual const IteratorPosition& GetPosition() override; + virtual IteratorImplBase* Clone (IteratorImplBase* pObject = nullptr) const override; + virtual bool operator== (const IteratorImplBase& rIterator) const override; + +private: + const ::std::vector<::tools::WeakReference<SdrObject>>& mrObjectList; + sal_Int32 mnObjectIndex; + + /** Compare the given iterator with this object. This method handles + only the case that the given iterator is an instance of this class. + @param rIterator + The iterator to compare to. + @return + Returns <TRUE/> when both iterators point to the same object. + */ + virtual bool IsEqualSelection(const IteratorImplBase& rIterator) const override; + + IteratorImplBase& operator= (const IteratorImplBase& rIterator); +}; + +/** Iterator for iteration over all objects in a single view. On reaching + the last object on the last page (or the first object on the first page) + the view is *not* switched. Further calls to the + <member>GotoNextObject()</member> method will be ignored. + + <p>For documentation of the methods please refer to the base class + <type>IteratorImplBase</type>.</p> +*/ +class ViewIteratorImpl : public IteratorImplBase +{ +public: + ViewIteratorImpl ( + sal_Int32 nPageIndex, + SdDrawDocument* pDocument, + const std::weak_ptr<ViewShell>& rpViewShellWeak, + bool bDirectionIsForward); + ViewIteratorImpl ( + sal_Int32 nPageIndex, + SdDrawDocument* pDocument, + const std::weak_ptr<ViewShell>& rpViewShellWeak, + bool bDirectionIsForward, + PageKind ePageKind, + EditMode eEditMode); + virtual ~ViewIteratorImpl() override; + + virtual void GotoNextText() override; + virtual IteratorImplBase* Clone (IteratorImplBase* pObject = nullptr) const override; + virtual void Reverse() override; + +protected: + /** Set up page pointer and object list iterator for the specified + page. + @param nPageIndex + Index of the new page. It may lie outside the valid range for + page indices. + */ + void SetPage (sal_Int32 nPageIndex); + +private: + /// Indicates whether a page changed occurred on switching to current page. + bool mbPageChangeOccurred; + /// Pointer to the page associated with the current page index. May be NULL. + SdPage* mpPage; + /// Iterator of all objects on the current page. + std::unique_ptr<SdrObjListIter> mpObjectIterator; + + // Don't use this operator. + ViewIteratorImpl& operator= (const ViewIteratorImpl&) = delete; +}; + +/** Iterator for iteration over all objects in all views. It automatically + switches views when reaching the end/beginning of a view. + + <p>For documentation of the methods please refer to the base class + <type>IteratorImplBase</type>.</p> +*/ +class DocumentIteratorImpl : public ViewIteratorImpl +{ +public: + DocumentIteratorImpl ( + sal_Int32 nPageIndex, + PageKind ePageKind, + EditMode eEditMode, + SdDrawDocument* pDocument, + const std::weak_ptr<ViewShell>& rpViewShellWeak, + bool bDirectionIsForward); + virtual ~DocumentIteratorImpl() override; + + virtual void GotoNextText() override; + virtual IteratorImplBase* Clone (IteratorImplBase* pObject = nullptr) const override; + +private: + /// Number of pages in the view that is specified by <member>maPosition</member>. + sal_Int32 mnPageCount; + + // Don't use this operator. + DocumentIteratorImpl& operator= (const DocumentIteratorImpl& ) = delete; +}; + +} } // end of namespace ::sd::outliner + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/PaneChildWindows.hxx b/sd/source/ui/inc/PaneChildWindows.hxx new file mode 100644 index 000000000..e323353ee --- /dev/null +++ b/sd/source/ui/inc/PaneChildWindows.hxx @@ -0,0 +1,65 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/childwin.hxx> +#include <unotools/resmgr.hxx> + +namespace sd { + +/// Base class of Impress and Draw left sidebars/panes. +class PaneChildWindow + : public SfxChildWindow +{ +public: + PaneChildWindow ( + vcl::Window* pParentWindow, + sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo* pInfo, + TranslateId pTitleBarResId); + virtual ~PaneChildWindow() override; +}; + +/// The slide-sorter sidebar (on the left) in Impress. +class LeftPaneImpressChildWindow + : public PaneChildWindow +{ +public: + LeftPaneImpressChildWindow(vcl::Window* pParentWindow, sal_uInt16 nId, SfxBindings* pBindings, + SfxChildWinInfo* pInfo); + + SFX_DECL_CHILDWINDOW_WITHID(LeftPaneImpressChildWindow); +}; + +/// The pages sidebar (on the left) in Draw. +class LeftPaneDrawChildWindow + : public PaneChildWindow +{ +public: + LeftPaneDrawChildWindow(vcl::Window* pParentWindow, sal_uInt16 nId, SfxBindings* pBindings, + SfxChildWinInfo* pInfo); + + SFX_DECL_CHILDWINDOW_WITHID(LeftPaneDrawChildWindow); +}; + +} // end of namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/PaneDockingWindow.hxx b/sd/source/ui/inc/PaneDockingWindow.hxx new file mode 100644 index 000000000..c69cb6f94 --- /dev/null +++ b/sd/source/ui/inc/PaneDockingWindow.hxx @@ -0,0 +1,66 @@ +/* -*- 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 . + */ + +#pragma once + +#include "titledockwin.hxx" + +namespace sd { + + class PaneDockingWindow : public ::sd::TitledDockingWindow +{ +public: + /** Create a new docking window. + @param pBindings + Used, among others, to determine the ViewShellBase and + PaneManager that manage the new docking window. + @param pChildWindow + This child window is the logical container for the new docking + window. + @param pParent + The parent window of the new docking window. + @param rsTitle + the initial title + */ + PaneDockingWindow ( + SfxBindings *pBindings, + SfxChildWindow *pChildWindow, + vcl::Window* pParent, + const OUString& rsTitle); + + virtual ~PaneDockingWindow() override; + virtual void StateChanged( StateChangedType nType ) override; + virtual void MouseButtonDown (const MouseEvent& rEvent) override; + /** When docked the given range is passed to the parent SplitWindow. + */ + void SetValidSizeRange (const Range& rValidSizeRange); + + enum Orientation { HorizontalOrientation, VerticalOrientation, UnknownOrientation }; + /** When the PaneDockingWindow is docked and managed by a split window + it can derive its orientation from the orientation of the split + window and return either HorizontalOrientation or + VerticalOrientation. + Otherwise UnknownOrientation is returned. + */ + Orientation GetOrientation() const; +}; + +} // end of namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/PaneShells.hxx b/sd/source/ui/inc/PaneShells.hxx new file mode 100644 index 000000000..fe5809a8f --- /dev/null +++ b/sd/source/ui/inc/PaneShells.hxx @@ -0,0 +1,63 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/shell.hxx> +#include <glob.hxx> + +namespace sd +{ +/** Shell that displays the left pane for Impress. The shell does not do + anything else and has especially no slots. +*/ +class LeftImpressPaneShell : public SfxShell +{ +public: + SFX_DECL_INTERFACE(SD_IF_SDLEFTIMPRESSPANESHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + LeftImpressPaneShell(); + virtual ~LeftImpressPaneShell() override; +}; + +/** Shell that displays the left pane for Draw. The shell does not do + anything else and has especially no slots. +*/ +class LeftDrawPaneShell : public SfxShell +{ +public: + SFX_DECL_INTERFACE(SD_IF_SDLEFTDRAWPANESHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + LeftDrawPaneShell(); + virtual ~LeftDrawPaneShell() override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/PresentationViewShell.hxx b/sd/source/ui/inc/PresentationViewShell.hxx new file mode 100644 index 000000000..f37b31e08 --- /dev/null +++ b/sd/source/ui/inc/PresentationViewShell.hxx @@ -0,0 +1,70 @@ +/* -*- 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 . + */ + +#pragma once + +#include "DrawViewShell.hxx" + +namespace sd +{ +/** This view shell is responsible for showing the presentation of an + Impress document. +*/ +class PresentationViewShell : public DrawViewShell +{ +public: + SFX_DECL_INTERFACE(SD_IF_SDPRESVIEWSHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + PresentationViewShell(ViewShellBase& rViewShellBase, vcl::Window* pParentWindow, + FrameView* pFrameView); + virtual ~PresentationViewShell() override; + + /** This method is used by a simple class that passes some + arguments from the creator of the new view shell to the new view + shell object by waiting for its asynchronous creation. + @param pFrameView + The frame view that is typically used by the creating object and + that shall be shared by the created view shell. + */ + void FinishInitialization(FrameView* pFrameView); + + virtual void Resize() override; + +protected: + virtual VclPtr<SvxRuler> CreateHRuler(::sd::Window* pWin) override; + virtual VclPtr<SvxRuler> CreateVRuler(::sd::Window* pWin) override; + +private: + ::tools::Rectangle maOldVisArea; + ImplSVEvent* mnAbortSlideShowEvent; + + virtual void Activate(bool bIsMDIActivate) override; + virtual void Paint(const ::tools::Rectangle& rRect, ::sd::Window* pWin) override; + + DECL_LINK(AbortSlideShowHdl, void*, void); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/PresentationViewShellBase.hxx b/sd/source/ui/inc/PresentationViewShellBase.hxx new file mode 100644 index 000000000..684e5ee61 --- /dev/null +++ b/sd/source/ui/inc/PresentationViewShellBase.hxx @@ -0,0 +1,46 @@ +/* -*- 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 . + */ + +#pragma once + +#include "ViewShellBase.hxx" + +namespace sd +{ +/** This class exists to be able to register another factory that + creates the view shell for the presentation. +*/ +class PresentationViewShellBase : public ViewShellBase +{ +public: + SFX_DECL_VIEWFACTORY(PresentationViewShellBase); + + /** This constructor is used by the view factory of the SFX + macros. + */ + PresentationViewShellBase(SfxViewFrame* pFrame, SfxViewShell* pOldShell); + virtual ~PresentationViewShellBase() override; + +protected: + virtual void InitializeFramework() override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/PreviewRenderer.hxx b/sd/source/ui/inc/PreviewRenderer.hxx new file mode 100644 index 000000000..245f0b638 --- /dev/null +++ b/sd/source/ui/inc/PreviewRenderer.hxx @@ -0,0 +1,141 @@ +/* -*- 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 . + */ + +#pragma once + +#include <vcl/image.hxx> +#include <memory> + +#include <svl/lstner.hxx> + +class SdPage; +class VirtualDevice; + +namespace sd { + +class DrawDocShell; +class DrawView; + +class PreviewRenderer + : public SfxListener +{ +public: + /** Create a new preview renderer that takes some of its initial values + from the given output device. + @param bPaintFrame + When <TRUE/> (the default) then a frame is painted around the + preview. This makes the actual preview smaller. + */ + PreviewRenderer(const bool bPaintFrame = true); + + virtual ~PreviewRenderer() override; + + /** Render a page with the given pixel size. + Use this version when only the width of the preview is known to the + caller. The height is then calculated according to the aspect + ratio of the given page. + @param pPage + The page to render. + @param nWidth + The width of the preview in device coordinates. + The high contrast mode of the application is + ignored and the preview is rendered in normal mode. + */ + Image RenderPage ( + const SdPage* pPage, + const sal_Int32 nWidth); + + /** Render a page with the given pixel size. + @param pPage + The page to render. + @param aPreviewPixelSize + The size in device coordinates of the preview. + @param bObeyHighContrastMode + When <FALSE/> then the high contrast mode of the application is + ignored and the preview is rendered in normal mode. When + <TRUE/> and high contrast mode is active then the preview is + rendered in high contrast mode. + @param bDisplayPresentationObjects + When <FALSE/> then the PresObj place holders are not displayed + in the returned preview. + */ + Image RenderPage ( + const SdPage* pPage, + const Size aPreviewPixelSize, + const bool bObeyHighContrastMode, + const bool bDisplayPresentationObjects = true); + + /** Render an image that contains the given substitution text instead of a + slide preview. + @param aPreviewPixelSize + The size in device coordinates of the image. + */ + Image RenderSubstitution ( + const Size& rPreviewPixelSize, + const OUString& sSubstitutionText); + + /** Scale the given bitmap by keeping its aspect ratio to the desired + width. Add a frame to it afterwards. + */ + Image ScaleBitmap ( + const BitmapEx& rBitmap, + int nWidth); + +protected: + virtual void Notify(SfxBroadcaster& rBC, const SfxHint& rHint) override; + +private: + ScopedVclPtr<VirtualDevice> mpPreviewDevice; + ::std::unique_ptr<DrawView> mpView; + DrawDocShell* mpDocShellOfView; + const Color maFrameColor; + const bool mbHasFrame; + static const int snSubstitutionTextSize; + // Width of the frame that is painted around the preview. + static const int snFrameWidth; + + bool Initialize ( + const SdPage* pPage, + const Size& rPixelSize, + const bool bObeyHighContrastMode); + void PaintPage ( + const SdPage* pPage, + const bool bDisplayPresentationObjects); + void PaintSubstitutionText (const OUString& rSubstitutionText); + void PaintFrame(); + + /** Set up the map mode so that the given page is renderer into a bitmap + with the specified width. + @param rPage + The page for which the preview is created. + @param rPixelSize + The size of the resulting preview bitmap. Note that this size + includes the frame. The actual preview is smaller accordingly. + */ + void SetupOutputSize (const SdPage& rPage, const Size& rPixelSize); + + /** When mpView is empty then create a new view and initialize it. + Otherwise just initialize it. + */ + void ProvideView (DrawDocShell* pDocShell); +}; + +} // end of namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/RemoteServer.hxx b/sd/source/ui/inc/RemoteServer.hxx new file mode 100644 index 000000000..965bf7a27 --- /dev/null +++ b/sd/source/ui/inc/RemoteServer.hxx @@ -0,0 +1,88 @@ +/* -*- 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/. + */ +#pragma once + +#include <memory> +#include <vector> + +#include <osl/socket_decl.hxx> +#include <salhelper/thread.hxx> + +#include <sddllapi.h> + +namespace osl { class Mutex; } +namespace com::sun::star::presentation { class XSlideShowController; } +namespace com::sun::star::uno { template <class interface_type> class Reference; } + +/** +* The port for use for the main communication between LibO and remote control app. +*/ +#define PORT 1599 + +namespace sd +{ + class BufferedStreamSocket; + class Communicator; + + struct ClientInfo + { + OUString mName; + + bool mbIsAlreadyAuthorised; + + ClientInfo( const OUString& rName, + const bool bIsAlreadyAuthorised ) : + mName( rName ), + mbIsAlreadyAuthorised( bIsAlreadyAuthorised ) {} + + virtual ~ClientInfo() {}; + }; + + struct ClientInfoInternal; + + class RemoteServer final : public salhelper::Thread + { + public: + // Internal setup + static void setup(); + + // For slideshowimpl to inform us. + static void presentationStarted( const css::uno::Reference< + css::presentation::XSlideShowController > &rController ); + static void presentationStopped(); + + // For the control dialog + SD_DLLPUBLIC static std::vector< std::shared_ptr< ClientInfo > > getClients(); + SD_DLLPUBLIC static bool connectClient( const std::shared_ptr< ClientInfo >& pClient, + std::u16string_view aPin ); + SD_DLLPUBLIC static void deauthoriseClient( const std::shared_ptr< ClientInfo >& pClient ); + + /// ensure that discoverability (eg. for Bluetooth) is enabled + SD_DLLPUBLIC static void ensureDiscoverable(); + /// restore the state of discoverability from before ensureDiscoverable + SD_DLLPUBLIC static void restoreDiscoverable(); + + // For the communicator + static void removeCommunicator( Communicator const * pCommunicator ); + private: + RemoteServer(); + virtual ~RemoteServer() override; + static RemoteServer *spServer; + static ::osl::Mutex sDataMutex; + static ::std::vector<Communicator*> sCommunicators; + osl::AcceptorSocket mSocket; + + ::std::vector< std::shared_ptr< ClientInfoInternal > > mAvailableClients; + + void execute() override; + void handleAcceptedConnection( BufferedStreamSocket *pSocket ) ; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/Ruler.hxx b/sd/source/ui/inc/Ruler.hxx new file mode 100644 index 000000000..5cf1d18bb --- /dev/null +++ b/sd/source/ui/inc/Ruler.hxx @@ -0,0 +1,62 @@ +/* -*- 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 . + */ + +#pragma once + +#include <svx/ruler.hxx> + +namespace sd { + +class DrawViewShell; +class RulerCtrlItem; +class Window; + +class Ruler final + : public SvxRuler +{ +public: + Ruler ( + DrawViewShell& rViewSh, + vcl::Window* pParent, + ::sd::Window* pWin, + SvxRulerSupportFlags nRulerFlags, + SfxBindings& rBindings, + WinBits nWinStyle); + virtual ~Ruler() override; + virtual void dispose() override; + + void SetNullOffset(const Point& rOffset); + + bool IsHorizontal() const { return bHorz; } + + using ::Ruler::SetNullOffset; + +private: + DrawViewShell* pDrViewShell; + std::unique_ptr<RulerCtrlItem> pCtrlItem; + bool bHorz; + + virtual void MouseButtonDown(const MouseEvent& rMEvt) override; + virtual void Command(const CommandEvent& rCEvt) override; + virtual void ExtraDown() override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/SdUnoDrawView.hxx b/sd/source/ui/inc/SdUnoDrawView.hxx new file mode 100644 index 000000000..6b62e4cb2 --- /dev/null +++ b/sd/source/ui/inc/SdUnoDrawView.hxx @@ -0,0 +1,116 @@ +/* -*- 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 . + */ + +#pragma once + +#include "DrawSubController.hxx" +#include <cppuhelper/basemutex.hxx> + +class SdXImpressDocument; +namespace com::sun::star::drawing { class XLayer; } + +namespace sd { + +class DrawViewShell; +class View; + +/** This class implements the DrawViewShell specific part of the controller. +*/ +class SdUnoDrawView final + : private cppu::BaseMutex, + public DrawSubControllerInterfaceBase +{ +public: + SdUnoDrawView ( + DrawViewShell& rViewShell, + View& rView) noexcept; + virtual ~SdUnoDrawView() noexcept override; + + // XSelectionSupplier + + virtual sal_Bool SAL_CALL select ( + const css::uno::Any& aSelection) override; + + virtual css::uno::Any SAL_CALL getSelection() override; + + virtual void SAL_CALL addSelectionChangeListener ( + const css::uno::Reference<css::view::XSelectionChangeListener>& rxListener) override; + + virtual void SAL_CALL removeSelectionChangeListener ( + const css::uno::Reference<css::view::XSelectionChangeListener>& rxListener) override; + + // XDrawView + + virtual void SAL_CALL setCurrentPage ( + const css::uno::Reference<css::drawing::XDrawPage >& xPage) override; + + virtual css::uno::Reference<css::drawing::XDrawPage> SAL_CALL getCurrentPage() override; + + // XFastPropertySet + + virtual void SAL_CALL setFastPropertyValue ( + sal_Int32 nHandle, + const css::uno::Any& rValue) override; + + virtual css::uno::Any SAL_CALL getFastPropertyValue ( + sal_Int32 nHandle) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + /** Return a reference to the active layer object. + @return + The returned value may be empty when the internal state of this + view is not valid (like during destruction.) + */ + css::uno::Reference< css::drawing::XLayer> getActiveLayer() const; + +private: + bool getMasterPageMode() const noexcept; + void setMasterPageMode(bool MasterPageMode_) noexcept; + bool getLayerMode() const noexcept; + void setLayerMode(bool LayerMode_) noexcept; + /** Make the specified object the active layer. + @param rxLayer + The new layer object. + @throws css::uno::RuntimeException + */ + void setActiveLayer (const css::uno::Reference< css::drawing::XLayer>& rxLayer); + + void SetZoom( sal_Int16 nZoom ); + sal_Int16 GetZoom() const; + + void SetViewOffset(const css::awt::Point& rWinPos ); + css::awt::Point GetViewOffset() const; + + void SetZoomType( sal_Int16 nType ); + + css::uno::Any getDrawViewMode() const; + + SdXImpressDocument* GetModel() const noexcept; + + DrawViewShell& mrDrawViewShell; + sd::View& mrView; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/SdUnoOutlineView.hxx b/sd/source/ui/inc/SdUnoOutlineView.hxx new file mode 100644 index 000000000..2789cabee --- /dev/null +++ b/sd/source/ui/inc/SdUnoOutlineView.hxx @@ -0,0 +1,82 @@ +/* -*- 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 . + */ + +#pragma once + +#include "DrawSubController.hxx" +#include <cppuhelper/basemutex.hxx> + +namespace sd { + +class OutlineViewShell; + +/** This class implements the OutlineViewShell specific part of the controller. +*/ +class SdUnoOutlineView final + : private cppu::BaseMutex, + public DrawSubControllerInterfaceBase +{ +public: + SdUnoOutlineView ( + OutlineViewShell& rViewShell) noexcept; + virtual ~SdUnoOutlineView() noexcept override; + + virtual void SAL_CALL disposing() override; + + // XSelectionSupplier + + virtual sal_Bool SAL_CALL select ( + const css::uno::Any& aSelection) override; + + virtual css::uno::Any SAL_CALL getSelection() override; + + virtual void SAL_CALL addSelectionChangeListener ( + const css::uno::Reference<css::view::XSelectionChangeListener>& rxListener) override; + + virtual void SAL_CALL removeSelectionChangeListener ( + const css::uno::Reference<css::view::XSelectionChangeListener>& rxListener) override; + + // XDrawView + + virtual void SAL_CALL setCurrentPage ( + const css::uno::Reference<css::drawing::XDrawPage >& xPage) override; + + virtual css::uno::Reference<css::drawing::XDrawPage> SAL_CALL getCurrentPage() override; + + // XFastPropertySet + + virtual void SAL_CALL setFastPropertyValue ( + sal_Int32 nHandle, + const css::uno::Any& rValue) override; + + virtual css::uno::Any SAL_CALL getFastPropertyValue ( + sal_Int32 nHandle) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + +private: + OutlineViewShell& mrOutlineViewShell; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/SdUnoSlideView.hxx b/sd/source/ui/inc/SdUnoSlideView.hxx new file mode 100644 index 000000000..7ca40a1ab --- /dev/null +++ b/sd/source/ui/inc/SdUnoSlideView.hxx @@ -0,0 +1,82 @@ +/* -*- 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 . + */ + +#pragma once + +#include "DrawSubController.hxx" +#include <cppuhelper/basemutex.hxx> + +namespace sd::slidesorter { class SlideSorter; } +namespace com::sun::star::drawing { class XDrawPage; } + +namespace sd { + +/** This class implements the SlideSorter specific part of the + controller. + */ +class SdUnoSlideView final + : private cppu::BaseMutex, + public DrawSubControllerInterfaceBase +{ +public: + SdUnoSlideView ( + slidesorter::SlideSorter& rSlideSorter) noexcept; + virtual ~SdUnoSlideView() noexcept override; + + // XSelectionSupplier + + virtual sal_Bool SAL_CALL select (const css::uno::Any& aSelection) override; + + virtual css::uno::Any SAL_CALL getSelection() override; + + virtual void SAL_CALL addSelectionChangeListener ( + const css::uno::Reference<css::view::XSelectionChangeListener>& rxListener) override; + + virtual void SAL_CALL removeSelectionChangeListener ( + const css::uno::Reference<css::view::XSelectionChangeListener>& rxListener) override; + + // XDrawView + + virtual void SAL_CALL setCurrentPage ( + const css::uno::Reference< css::drawing::XDrawPage >& xPage) override; + + virtual css::uno::Reference< css::drawing::XDrawPage > SAL_CALL + getCurrentPage() override; + + // XFastPropertySet + + virtual void SAL_CALL setFastPropertyValue ( + sal_Int32 nHandle, + const css::uno::Any& rValue) override; + + virtual css::uno::Any SAL_CALL getFastPropertyValue ( + sal_Int32 nHandle) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + +private: + slidesorter::SlideSorter& mrSlideSorter; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/ShellFactory.hxx b/sd/source/ui/inc/ShellFactory.hxx new file mode 100644 index 000000000..fc05c41ab --- /dev/null +++ b/sd/source/ui/inc/ShellFactory.hxx @@ -0,0 +1,52 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/toolbarids.hxx> + +namespace sd +{ +typedef ToolbarId ShellId; + +template <class ShellType> class ShellFactory +{ +public: + /** This abstract virtual class needs a destructor so that the + destructors of derived classes are called. + */ + virtual ~ShellFactory(){}; + + /** Create a new instance of a view shell for the given id that will + be stacked onto the given view shell base. + @return + Return the new view shell or NULL when a creation is not + possible. + */ + virtual ShellType* CreateShell(ShellId nId) = 0; + + /** Tell the factory that a shell is no longer in use. It may destroy + it or put it for future use in a cache. + */ + virtual void ReleaseShell(ShellType* pShell) = 0; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/SlideSorter.hxx b/sd/source/ui/inc/SlideSorter.hxx new file mode 100644 index 000000000..9ed70cf9b --- /dev/null +++ b/sd/source/ui/inc/SlideSorter.hxx @@ -0,0 +1,248 @@ +/* -*- 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 . + */ + +#pragma once + +#include <cppuhelper/weakref.hxx> +#include <vcl/scrbar.hxx> +#include <sddllapi.h> +#include <memory> + +namespace vcl { class Window; } +namespace com::sun::star::frame { class XController; } +namespace rtl { template <class reference_type> class Reference; } + +namespace sd { +class ViewShell; +class ViewShellBase; +class Window; +class FuPoor; +} + +namespace sd::slidesorter::model { class SlideSorterModel; } + +namespace sd::slidesorter::view { + class SlideSorterView; + class Theme; +} + +namespace sd::slidesorter::controller { + class SlideSorterController; + class SlotManager; + class Properties; +} + +namespace sd::slidesorter { + +/** Show previews for all the slides in a document and allow the user to + insert or delete slides and modify the order of the slides. + + This class is a facade for the model, view, and controller classes. + It is a hub that allows access to the various parts of a slide sorter. + + Note that this class is not in its final state. +*/ +class SlideSorter final +{ + friend class controller::SlotManager; +public: + ~SlideSorter(); + + /// Forbid copy construction and copy assignment + SlideSorter(const SlideSorter&) = delete; + SlideSorter& operator=(const SlideSorter&) = delete; + + /** Return whether the called SlideSorter object is valid and calling + its Get(Model,View,Controller) methods is safe. When <FALSE/> is + called then no other methods should be called. + Calling this method should be necessary only during startup and + shutdown (when that can be detected). + */ + bool IsValid() const { return mbIsValid;} + + /** Create a new slide sorter that is strongly coupled to the given view + shell. Use this function for a slide sorter in the left pane. + @param rViewShell + Typically a SlideSorterViewShell object. + @param rpContentWindow + Typically the content window of the ViewShell. + @param rpHorizontalScrollBar + Typically the horizontal scroll bar of the ViewShell. + @param rpVerticalScrollBar + Typically the vertical scroll bar of the ViewShell. + @param rpScrollBarBox + The little square enclosed by the two scroll bars. Typically + the one from the ViewShell. + */ + static std::shared_ptr<SlideSorter> CreateSlideSorter ( + ViewShell& rViewShell, + sd::Window* pContentWindow, + ScrollBar* pHorizontalScrollBar, + ScrollBar* pVerticalScrollBar, + ScrollBarBox* pScrollBarBox); + + /** Create a new slide sorter that is loosely coupled to the given view + shell. The view shell may even be missing. + @param rBase + ViewShellBase object of the enclosing application. + @param pViewShell + Supply when at hand. + @param rParentWindow + The parent window of the internally created content window and + scroll bars. + */ + static std::shared_ptr<SlideSorter> CreateSlideSorter ( + ViewShellBase& rBase, + vcl::Window& rParentWindow); + + /** Return the control of the vertical scroll bar. + */ + const VclPtr<ScrollBar>& GetVerticalScrollBar() const { return mpVerticalScrollBar;} + + /** Return the control of the horizontal scroll bar. + */ + const VclPtr<ScrollBar>& GetHorizontalScrollBar() const { return mpHorizontalScrollBar;} + + /** Return the scroll bar filler that paints the little square that is + enclosed by the two scroll bars. + */ + const VclPtr<ScrollBarBox>& GetScrollBarFiller (void) const { return mpScrollBarBox;} + + /** Return the content window. This is a sibling and is geometrically + enclosed by the scroll bars. + */ + const VclPtr<sd::Window>& GetContentWindow() const { return mpContentWindow;} + + model::SlideSorterModel& GetModel() const; + + view::SlideSorterView& GetView() const; + + // Exported for unit test + SD_DLLPUBLIC controller::SlideSorterController& GetController() const; + + /** Return the view shell that was given at construction. + @return + May be empty. + */ + ViewShell* GetViewShell() const { return mpViewShell;} + + /** Return the XController object of the main view. + */ + css::uno::Reference<css::frame::XController> + GetXController() const; + + /** Return the ViewShellBase object. + @return + May be empty. + */ + ViewShellBase* GetViewShellBase() const { return mpViewShellBase;} + + void Paint (const ::tools::Rectangle& rRepaintArea); + + /** Place and size the controls and windows. You may want to call this + method when something has changed that for instance affects the + visibility state of the scroll bars. + */ + void ArrangeGUIElements ( + const Point& rOffset, + const Size& rSize); + + void RelocateToWindow (vcl::Window* pWindow); + + /** Set the current function at the view shell or, when it is not + present, set it at the content window. This method supports the use + of functions even when there is no SlideSorterViewShell. + */ + void SetCurrentFunction (const rtl::Reference<FuPoor>& rpFunction); + + /** Return a collection of properties that are used throughout the slide + sorter. + */ + std::shared_ptr<controller::Properties> const & GetProperties() const; + + /** Return the active theme which gives access to colors and fonts. + */ + std::shared_ptr<view::Theme> const & GetTheme() const; + +private: + /** This virtual method makes it possible to create a specialization of + the slide sorter view shell that works with its own implementation + of model, view, and controller. The default implementation simply + calls the CreateModel(), CreateView(), and CreateController() + methods in this order. + */ + void CreateModelViewController(); + + /** Create the model for the view shell. When called from the default + implementation of CreateModelViewController() then neither view nor + controller do exist. Test their pointers when in doubt. + */ + model::SlideSorterModel* CreateModel(); + + bool mbIsValid; + + std::unique_ptr<controller::SlideSorterController> mpSlideSorterController; + std::unique_ptr<model::SlideSorterModel> mpSlideSorterModel; + std::unique_ptr<view::SlideSorterView> mpSlideSorterView; + css::uno::WeakReference<css::frame::XController> mxControllerWeak; + ViewShell* mpViewShell; + ViewShellBase* mpViewShellBase; + VclPtr<sd::Window> mpContentWindow; + VclPtr<ScrollBar> mpHorizontalScrollBar; + VclPtr<ScrollBar> mpVerticalScrollBar; + VclPtr<ScrollBarBox> mpScrollBarBox; + + /** Some slide sorter wide properties that are used in different + classes. + */ + std::shared_ptr<controller::Properties> mpProperties; + std::shared_ptr<view::Theme> mpTheme; + + SlideSorter ( + ViewShell& rViewShell, + sd::Window* pContentWindow, + ScrollBar* pHorizontalScrollBar, + ScrollBar* pVerticalScrollBar, + ScrollBarBox* pScrollBarBox); + SlideSorter ( + ViewShellBase& rBase, + vcl::Window& rParentWindow); + + void Init(); + /** Create the controls for the slide sorter. This are the tab bar + for switching the edit mode, the scroll bar, and the actual + slide sorter view window. + This method is usually called exactly one time from the + constructor. + */ + void SetupControls(); + + /** This method is usually called exactly one time from the + constructor. + */ + void SetupListeners(); + + /** Release the listeners that have been installed in SetupListeners(). + */ + void ReleaseListeners(); +}; + +} // end of namespace ::sd::slidesorter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/SlideSorterViewShell.hxx b/sd/source/ui/inc/SlideSorterViewShell.hxx new file mode 100644 index 000000000..64808d434 --- /dev/null +++ b/sd/source/ui/inc/SlideSorterViewShell.hxx @@ -0,0 +1,232 @@ +/* -*- 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 . + */ + +#pragma once + +#include "ViewShell.hxx" +#include <glob.hxx> +#include <sfx2/shell.hxx> +#include <sddllapi.h> +#include <memory> +#include <vector> + +namespace sd::slidesorter::controller { class SlotManager; } + +namespace sd::slidesorter { + +class SlideSorter; + +class SAL_DLLPUBLIC_RTTI SlideSorterViewShell final + : public ViewShell +{ + friend class controller::SlotManager; + +public: + SFX_DECL_INTERFACE(SD_IF_SDSLIDESORTERVIEWSHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + static std::shared_ptr<SlideSorterViewShell> Create( + SfxViewFrame* pFrame, + ViewShellBase& rViewShellBase, + vcl::Window* pParentWindow, + FrameView* pFrameView); + + virtual ~SlideSorterViewShell() override; + + /** Late initialization that has to be called after a new instance has + completed its construction. + */ + virtual void Init (bool bIsMainViewShell) override; + + /** Return a slide sorter that is currently displayed in one of the + panes that belong to the given ViewShellBase object. + When there is only one slide sorter visible then that one is + returned. When two (or more) are visible then the one in the center + pane is returned. When no slidesorter is visible then NULL is + returned. + */ + // Exported for unit test + SD_DLLPUBLIC static SlideSorterViewShell* GetSlideSorter(ViewShellBase& rBase); + + virtual SdPage* GetActualPage() override; + + /// inherited from sd::ViewShell + virtual SdPage* getCurrentPage() const override; + + void ExecCtrl (SfxRequest& rRequest); + void GetCtrlState (SfxItemSet &rSet); + void FuSupport (SfxRequest& rRequest); + void FuTemporary (SfxRequest& rRequest); + void GetStatusBarState (SfxItemSet& rSet); + void FuPermanent (SfxRequest& rRequest); + void GetAttrState (SfxItemSet& rSet); + static void ExecStatusBar (SfxRequest& rRequest); + virtual void Command (const CommandEvent& rEvent, ::sd::Window* pWindow) override; + void GetMenuState (SfxItemSet &rSet); + void GetClipboardState (SfxItemSet &rSet); + + virtual void ReadFrameViewData (FrameView* pView) override; + virtual void WriteFrameViewData() override; + + /** Set the zoom factor. The given value is clipped against an upper + bound. + @param nZoom + An integer percent value, i.e. nZoom/100 is the actual zoom + factor. + */ + virtual void SetZoom (::tools::Long nZoom) override; + virtual void SetZoomRect (const ::tools::Rectangle& rZoomRect) override; + + /** This is a callback method used by the active window to delegate its + Paint() call to. This view shell itself delegates it to the view. + */ + virtual void Paint(const ::tools::Rectangle& rRect, ::sd::Window* pWin) override; + + /** Place and size the controls and windows. You may want to call this + method when something has changed that for instance affects the + visibility state of the scroll bars. + */ + virtual void ArrangeGUIElements() override; + + virtual void Activate (bool IsMDIActivate) override; + virtual void Deactivate (bool IsMDIActivate) override; + + /** Move slides up and down. Mainly uno commands. */ + void ExecMovePageUp (SfxRequest& rReq); + void GetStateMovePageUp (SfxItemSet& rSet); + + void ExecMovePageDown (SfxRequest& rReq); + void GetStateMovePageDown (SfxItemSet& rSet); + + void ExecMovePageFirst (SfxRequest& rReq); + void GetStateMovePageFirst (SfxItemSet& rSet); + + void ExecMovePageLast (SfxRequest& rReq); + void GetStateMovePageLast (SfxItemSet& rSet); + + + //===== Drag and Drop ===================================================== + + void StartDrag ( + const Point& rDragPt, + vcl::Window* pWindow ); + virtual sal_Int8 AcceptDrop ( + const AcceptDropEvent& rEvt, + DropTargetHelper& rTargetHelper, + ::sd::Window* pTargetWindow, + sal_uInt16 nPage, + SdrLayerID nLayer ) override; + virtual sal_Int8 ExecuteDrop ( + const ExecuteDropEvent& rEvt, + DropTargetHelper& rTargetHelper, + ::sd::Window* pTargetWindow, + sal_uInt16 nPage, + SdrLayerID nLayer) override; + + typedef ::std::vector<SdPage*> PageSelection; + + /** Return the set of selected pages. + */ + std::shared_ptr<PageSelection> GetPageSelection() const; + + void SetPageSelection (const std::shared_ptr<PageSelection>& rSelection); + + /** Add a listener that is called when the selection of the slide sorter + changes. + @param rListener + When this method is called multiple times for the same listener + the second and all following calls are ignored. Each listener + is added only once. + */ + void AddSelectionChangeListener (const Link<LinkParamNone*,void>& rListener); + + /** Remove a listener that was called when the selection of the slide + sorter changes. + @param rListener + It is safe to pass a listener that was not added are has been + removed previously. Such calls are ignored. + */ + void RemoveSelectionChangeListener (const Link<LinkParamNone*,void>& rListener); + + virtual css::uno::Reference<css::drawing::XDrawSubController> CreateSubController() override; + + /** Create an accessible object representing the specified window. + @param pWindow + The returned object makes the document displayed in this window + accessible. + @return + Returns an <type>AccessibleSlideSorterView</type> object. + */ + virtual css::uno::Reference<css::accessibility::XAccessible> + CreateAccessibleDocumentView (::sd::Window* pWindow) override; + // handle SlideSorterView specially because AccessibleSlideSorterView doesn't inherit from AccessibleDocumentViewBase + virtual void SwitchViewFireFocus( const css::uno::Reference< css::accessibility::XAccessible >& xAcc ) override; + + // Exported for unit test + SD_DLLPUBLIC SlideSorter& GetSlideSorter() const; + + /** Try to relocate all toplevel window elements to the given parent + window. + */ + virtual bool RelocateToParentWindow (vcl::Window* pParentWindow) override; + +private: + + /** Override this method to handle a missing tool bar correctly. + This is the case when the slide sorter is not the main view shell. + */ + virtual SfxUndoManager* ImpGetUndoManager() const override; + + std::shared_ptr<SlideSorter> mpSlideSorter; + bool mbIsArrangeGUIElementsPending; + + SlideSorterViewShell ( + SfxViewFrame* pFrame, + ViewShellBase& rViewShellBase, + vcl::Window* pParentWindow, + FrameView* pFrameView); + void Initialize(); + + /** This method overwrites the one from our base class: We do our own + scroll bar and the base class call is thus unnecessary. It simply + calls UpdateScrollBars(false). + */ + virtual void UpdateScrollBars() override; + + void PostMoveSlidesActions(const std::shared_ptr<SlideSorterViewShell::PageSelection> &rpSelection); + + void MainViewEndEditAndUnmarkAll(); + + /** Select the same pages in the document as are selected in the + SlideSorterViewShell + + return the page numbers of the first and last selected pages + */ + std::pair<sal_uInt16, sal_uInt16> SyncPageSelectionToDocument(const std::shared_ptr<SlideSorterViewShell::PageSelection> &rpSelection); +}; + +typedef std::shared_ptr<SlideSorterViewShell::PageSelection> SharedPageSelection; + +} // end of namespace ::sd::slidesorter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/SlideSorterViewShellBase.hxx b/sd/source/ui/inc/SlideSorterViewShellBase.hxx new file mode 100644 index 000000000..e1ca1b57b --- /dev/null +++ b/sd/source/ui/inc/SlideSorterViewShellBase.hxx @@ -0,0 +1,43 @@ +/* -*- 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 . + */ + +#pragma once + +#include "ImpressViewShellBase.hxx" + +namespace sd +{ +/** This class exists to be able to register a factory that creates a + slide sorter view shell as default. +*/ +class SlideSorterViewShellBase final : public ImpressViewShellBase +{ +public: + SFX_DECL_VIEWFACTORY(SlideSorterViewShellBase); + + /** This constructor is used by the view factory of the SFX + macros. + */ + SlideSorterViewShellBase(SfxViewFrame* pFrame, SfxViewShell* pOldShell); + virtual ~SlideSorterViewShellBase() override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/SlideTransitionPane.hxx b/sd/source/ui/inc/SlideTransitionPane.hxx new file mode 100644 index 000000000..2b6ea8f93 --- /dev/null +++ b/sd/source/ui/inc/SlideTransitionPane.hxx @@ -0,0 +1,137 @@ +/* -*- 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 . + */ +#pragma once + +#include "SlideSorterViewShell.hxx" + +#include <svtools/valueset.hxx> +#include <sfx2/sidebar/ILayoutableWindow.hxx> +#include <sfx2/sidebar/PanelLayout.hxx> +#include <vcl/weld.hxx> + +#include <vector> +#include <map> + +class SdDrawDocument; + +namespace com::sun::star::drawing { class XDrawView; } +namespace com::sun::star::frame { class XModel; } +namespace sd::tools { class EventMultiplexerEvent; } + +namespace sd +{ + +class TransitionPane; +class ViewShellBase; + +namespace impl +{ + struct TransitionEffect; +} + +class SlideTransitionPane final : public PanelLayout + , public sfx2::sidebar::ILayoutableWindow +{ +public: + explicit SlideTransitionPane( + weld::Widget* pParent, + ViewShellBase & rBase); + virtual ~SlideTransitionPane() override; + + // ILayoutableWindow + virtual css::ui::LayoutSize GetHeightForWidth (const sal_Int32 nWidth) override; + + void onSelectionChanged(); + void onChangeCurrentPage(); + +private: + void updateControls(); + void updateControlState(); + void updateVariants(size_t nPresetOffset); + + void updateSoundList(); + void openSoundFileDialog(); + + impl::TransitionEffect getTransitionEffectFromControls() const; + + void applyToSelectedPages(bool bPreview); + void playCurrentEffect(); + + void addListener(); + void removeListener(); + + ::sd::slidesorter::SharedPageSelection getSelectedPages() const; + + void Initialize(SdDrawDocument* pDoc); + + DECL_LINK( ApplyToAllButtonClicked, weld::Button&, void ); + DECL_LINK( PlayButtonClicked, weld::Button&, void ); + DECL_LINK( AutoPreviewClicked, weld::Toggleable&, void ); + + DECL_LINK( TransitionSelected, ValueSet*, void ); + DECL_LINK( AdvanceSlideRadioButtonToggled, weld::Toggleable&, void ); + DECL_LINK( AdvanceTimeModified, weld::MetricSpinButton&, void ); + DECL_LINK( VariantListBoxSelected, weld::ComboBox&, void ); + DECL_LINK( DurationModifiedHdl, weld::MetricSpinButton&, void ); + DECL_LINK( DurationLoseFocusHdl, weld::Widget&, void ); + DECL_LINK( SoundListBoxSelected, weld::ComboBox&, void ); + DECL_LINK( LoopSoundBoxChecked, weld::Toggleable&, void ); + DECL_LINK( EventMultiplexerListener, tools::EventMultiplexerEvent&, void ); + DECL_LINK(LateInitCallback, Timer *, void); + + ViewShellBase & mrBase; + SdDrawDocument * mpDrawDoc; + + std::unique_ptr<TransitionPane> mxVS_TRANSITION_ICONS; + std::unique_ptr<weld::CustomWeld> mxVS_TRANSITION_ICONSWin; + std::unique_ptr<weld::Label> mxFT_VARIANT; + std::unique_ptr<weld::ComboBox> mxLB_VARIANT; + std::unique_ptr<weld::Label> mxFT_duration; + std::unique_ptr<weld::MetricSpinButton> mxCBX_duration; + std::unique_ptr<weld::Label> mxFT_SOUND; + std::unique_ptr<weld::ComboBox> mxLB_SOUND; + std::unique_ptr<weld::CheckButton> mxCB_LOOP_SOUND; + std::unique_ptr<weld::RadioButton> mxRB_ADVANCE_ON_MOUSE; + std::unique_ptr<weld::RadioButton> mxRB_ADVANCE_AUTO; + std::unique_ptr<weld::MetricSpinButton> mxMF_ADVANCE_AUTO_AFTER; + std::unique_ptr<weld::Button> mxPB_APPLY_TO_ALL; + std::unique_ptr<weld::Button> mxPB_PLAY; + std::unique_ptr<weld::CheckButton> mxCB_AUTO_PREVIEW; + + css::uno::Reference< css::drawing::XDrawView > mxView; + css::uno::Reference< css::frame::XModel > mxModel; + + bool mbHasSelection; + bool mbUpdatingControls; + bool mbIsMainViewChangePending; + + std::vector<OUString> maSoundList; + mutable OUString maCurrentSoundFile; + + // How many variants each transition set has + std::map< OUString, int > m_aNumVariants; + + Timer maLateInitTimer; +}; + +} // namespace sd + +// INCLUDED_SD_SOURCE_UI_ANIMATIONS_SLIDETRANSITIONPANE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/SpellDialogChildWindow.hxx b/sd/source/ui/inc/SpellDialogChildWindow.hxx new file mode 100644 index 000000000..3d2163a7e --- /dev/null +++ b/sd/source/ui/inc/SpellDialogChildWindow.hxx @@ -0,0 +1,86 @@ +/* -*- 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 . + */ + +#pragma once + +#include <svx/SpellDialogChildWindow.hxx> +#include <svl/lstner.hxx> + +class SdOutliner; + +namespace sd +{ +/** This derivation of the svx::SpellDialogChildWindow base class + provides Draw and Impress specific implementations of + GetNextWrongSentence() and ApplyChangedSentence(). +*/ +class SpellDialogChildWindow final : public svx::SpellDialogChildWindow, public SfxListener +{ +public: + SpellDialogChildWindow(vcl::Window* pParent, sal_uInt16 nId, SfxBindings* pBindings, + SfxChildWinInfo* pInfo); + virtual ~SpellDialogChildWindow() override; + + /** This method makes the one from the base class public so that + it can be called from the view shell when one is created. + */ + void InvalidateSpellDialog(); + + // SfxListener + virtual void Notify(SfxBroadcaster& rBC, const SfxHint& rHint) override; + + SFX_DECL_CHILDWINDOW_WITHID(SpellDialogChildWindow); + +private: + /** Iterate over the sentences in all text shapes and stop at the + next sentence with spelling errors. While doing so the view + mode may be changed and text shapes are set into edit mode. + */ + virtual svx::SpellPortions GetNextWrongSentence(bool bRecheck) override; + + /** This method is responsible for merging corrections made in the + spelling dialog back into the document. + */ + virtual void ApplyChangedSentence(const svx::SpellPortions& rChanged, bool bRecheck) override; + virtual void GetFocus() override; + virtual void LoseFocus() override; + + /** This outliner is used to do the main work of iterating over a + document and finding sentences with spelling errors. + */ + SdOutliner* mpSdOutliner; + + /** When this flag is <TRUE/> then eventually we have to destroy + the outliner in mpSdOutliner. + */ + bool mbOwnOutliner; + + /** Provide an outliner in the mpSdOutliner data member. When the + view shell has changed since the last call this include the + deletion/release of formerly created/obtained one prior to + construction/obtaining of a new one. + */ + void ProvideOutliner(); + + void EndSpellingAndClearOutliner(); +}; + +} // end of namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/TabControl.hxx b/sd/source/ui/inc/TabControl.hxx new file mode 100644 index 000000000..5e5eba7bb --- /dev/null +++ b/sd/source/ui/inc/TabControl.hxx @@ -0,0 +1,107 @@ +/* -*- 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 . + */ + +#pragma once + +#include <svtools/tabbar.hxx> +#include <vcl/transfer.hxx> + +namespace sd { + +/** + * TabControl-Class for page switch + */ + +class DrawViewShell; + +class TabControl final + : public TabBar, + public DragSourceHelper, + public DropTargetHelper +{ +public: + TabControl (DrawViewShell* pDrViewSh, vcl::Window* pParent); + virtual void dispose() override; + virtual ~TabControl() override; + + /** Inform all listeners of this control that the current page has been + activated. Call this method after switching the current page and is + not done elsewhere (like when using page up/down keys). + */ + void SendActivatePageEvent(); + + /** Inform all listeners of this control that the current page has been + deactivated. Call this method before switching the current page and + is not done elsewhere (like when using page up/down keys). + */ + void SendDeactivatePageEvent(); + +private: + DrawViewShell* pDrViewSh; + bool bInternalMove; + + // TabBar + virtual void Select() override; + virtual void DoubleClick() override; + virtual void MouseButtonDown(const MouseEvent& rMEvt) override; + + virtual void Command(const CommandEvent& rCEvt) override; + + virtual bool StartRenaming() override; + virtual TabBarAllowRenamingReturnCode AllowRenaming() override; + virtual void EndRenaming() override; + + virtual void ActivatePage() override; + virtual bool DeactivatePage() override; + + // DragSourceHelper + virtual void StartDrag( sal_Int8 nAction, const Point& rPosPixel ) override; + + // DropTargetHelper + virtual sal_Int8 AcceptDrop( const AcceptDropEvent& rEvt ) override; + virtual sal_Int8 ExecuteDrop( const ExecuteDropEvent& rEvt ) override; + + // nested class to implement the TransferableHelper + class TabControlTransferable final : public TransferableHelper + { + public: + explicit TabControlTransferable( TabControl& rParent ) : + mrParent( rParent ) {} + private: + + TabControl& mrParent; + + virtual ~TabControlTransferable() override; + + virtual void AddSupportedFormats() override; + virtual bool GetData( const css::datatransfer::DataFlavor& rFlavor, const OUString& rDestDoc ) override; + virtual void DragFinished( sal_Int8 nDropAction ) override; + + }; + + friend class TabControl::TabControlTransferable; + + void DragFinished(); + + using TabBar::StartDrag; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/TableDesignPane.hxx b/sd/source/ui/inc/TableDesignPane.hxx new file mode 100644 index 000000000..042eb6137 --- /dev/null +++ b/sd/source/ui/inc/TableDesignPane.hxx @@ -0,0 +1,118 @@ +/* -*- 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 . + */ + +#pragma once + +#include <svtools/valueset.hxx> +#include <sfx2/sidebar/ILayoutableWindow.hxx> +#include <sfx2/sidebar/PanelLayout.hxx> +#include <vcl/weld.hxx> + +namespace com::sun::star::beans { class XPropertySet; } +namespace com::sun::star::container { class XIndexAccess; } +namespace com::sun::star::drawing { class XDrawView; } + +namespace sd +{ + +namespace tools { +class EventMultiplexerEvent; +} + +class ViewShellBase; + +enum TableCheckBox : sal_uInt16 +{ + CB_HEADER_ROW = 0, + CB_TOTAL_ROW = 1, + CB_BANDED_ROWS = 2, + CB_FIRST_COLUMN = 3, + CB_LAST_COLUMN = 4, + CB_BANDED_COLUMNS = 5, + CB_COUNT = CB_BANDED_COLUMNS + 1 +}; + +class TableValueSet final : public ValueSet +{ +private: + bool m_bModal; +public: + TableValueSet(std::unique_ptr<weld::ScrolledWindow> pScrolledWindow); + virtual void Resize() override; + virtual void StyleUpdated() override; + void updateSettings(); + void setModal(bool bModal) { m_bModal = bModal; } +}; + +class TableDesignWidget final +{ +public: + TableDesignWidget(weld::Builder& rBuilder, ViewShellBase& rBase); + ~TableDesignWidget(); + + // callbacks + void onSelectionChanged(); + + void ApplyOptions(); + void ApplyStyle(); + +private: + void addListener(); + void removeListener(); + void updateControls(); + + void FillDesignPreviewControl(); + + DECL_LINK(EventMultiplexerListener, tools::EventMultiplexerEvent&, void); + DECL_LINK(implValueSetHdl, ValueSet*, void); + DECL_LINK(implCheckBoxHdl, weld::Toggleable&, void); + + ViewShellBase& mrBase; + + std::unique_ptr<TableValueSet> m_xValueSet; + std::unique_ptr<weld::CustomWeld> m_xValueSetWin; + std::unique_ptr<weld::CheckButton> m_aCheckBoxes[CB_COUNT]; + + css::uno::Reference< css::beans::XPropertySet > mxSelectedTable; + css::uno::Reference< css::drawing::XDrawView > mxView; + css::uno::Reference< css::container::XIndexAccess > mxTableFamily; +}; + +class TableDesignPane final : public PanelLayout + , public sfx2::sidebar::ILayoutableWindow +{ +private: + std::unique_ptr<TableDesignWidget> m_xImpl; +public: + TableDesignPane( weld::Widget* pParent, ViewShellBase& rBase ) + : PanelLayout(pParent, "TableDesignPanel", + "modules/simpress/ui/tabledesignpanel.ui") + , m_xImpl(new TableDesignWidget(*m_xBuilder, rBase)) + { + } + virtual css::ui::LayoutSize GetHeightForWidth(const sal_Int32 /*nWidth*/) override + { + sal_Int32 nMinimumHeight = get_preferred_size().Height(); + return css::ui::LayoutSize(nMinimumHeight, -1, nMinimumHeight); + } +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/TemplateScanner.hxx b/sd/source/ui/inc/TemplateScanner.hxx new file mode 100644 index 000000000..034f000a6 --- /dev/null +++ b/sd/source/ui/inc/TemplateScanner.hxx @@ -0,0 +1,175 @@ +/* -*- 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 . + */ + +#pragma once + +#include "tools/AsynchronousTask.hxx" +#include <ucbhelper/content.hxx> +#include <com/sun/star/uno/Reference.h> + +#include <memory> +#include <vector> + +namespace com::sun::star::ucb +{ +class XContent; +class XCommandEnvironment; +} + +namespace com::sun::star::sdbc +{ +class XResultSet; +} + +namespace sd +{ +/** Representation of a template or layout file. +*/ +class TemplateEntry +{ +public: + TemplateEntry(const OUString& rsTitle, const OUString& rsPath) + : msTitle(rsTitle) + , msPath(rsPath) + { + } + + OUString msTitle; + OUString msPath; +}; + +/** This class scans the template folders for impress templates. There are + two ways to use this class. + 1. The old and deprecated way is to call Scan() to scan all templates + and collect the supported ones in a tree structure. This structure is + returned by GetFolderList(). + 2. The new way implements the AsynchronousTask interface. Call + RunNextStep() as long HasNextStep() returns <TRUE/>. After every step + GetLastAddedEntry() returns the template that was scanned (and has a + supported format) last. When a step does not add a new template then + the value of the previous step is returned. +*/ +class TemplateScanner final : public ::sd::tools::AsynchronousTask +{ +public: + /** Create a new template scanner and prepare but do not execute the scanning. + */ + TemplateScanner(); + + /** The destructor deletes any remaining entries of the local list of + templates. + */ + virtual ~TemplateScanner(); + + /** Implementation of the AsynchronousTask interface method. + */ + virtual void RunNextStep() override; + + /** Implementation of the AsynchronousTask interface method. + */ + virtual bool HasNextStep() override; + + /** Return the TemplateDir object that was last added to + mpTemplateEntries. + @return + <nullptr/> is returned either before the template scanning is + started or after it has ended. + */ + const TemplateEntry* GetLastAddedEntry() const + { + return mpTemplateEntries.empty() ? nullptr : mpTemplateEntries.back().get(); + } + +private: + /** The current state determines which step will be executed next by + RunNextStep(). + */ + enum State + { + INITIALIZE_SCANNING, + INITIALIZE_FOLDER_SCANNING, + GATHER_FOLDER_LIST, + SCAN_FOLDER, + INITIALIZE_ENTRY_SCAN, + SCAN_ENTRY, + DONE, + ERROR + }; + State meState; + + ::ucbhelper::Content maFolderContent; + ::std::vector<std::unique_ptr<TemplateEntry>> mpTemplateEntries; + + /** The folders that are collected by GatherFolderList(). + */ + class FolderDescriptorList; + std::unique_ptr<FolderDescriptorList> mpFolderDescriptors; + + /** Set of state variables used by the methods + InitializeFolderScanning(), GatherFolderList(), ScanFolder(), + InitializeEntryScanning(), and ScanEntry(). + */ + css::uno::Reference<css::ucb::XContent> mxTemplateRoot; + css::uno::Reference<css::ucb::XCommandEnvironment> mxFolderEnvironment; + css::uno::Reference<css::ucb::XCommandEnvironment> mxEntryEnvironment; + css::uno::Reference<css::sdbc::XResultSet> mxFolderResultSet; + css::uno::Reference<css::sdbc::XResultSet> mxEntryResultSet; + + /** Obtain the root folder of the template folder hierarchy. The result + is stored in mxTemplateRoot for later use. + */ + State GetTemplateRoot(); + + /** Initialize the scanning of folders. This is called exactly once. + @return + Returns one of the two states ERROR or GATHER_FOLDER_LIST. + */ + State InitializeFolderScanning(); + + /** Collect all available top-level folders in an ordered list which can + then be processed by ScanFolder(). + @return + Returns one of the two states ERROR or SCAN_FOLDER. + */ + State GatherFolderList(); + + /** From the list of top-level folders collected by GatherFolderList() + the one with highest priority is processed. + @return + Returns one of the states ERROR, DONE, or INITIALIZE_ENTRY_SCAN. + */ + State ScanFolder(); + + /** Initialize the scanning of entries of a top-level folder. + @return + Returns one of the states ERROR or SCAN_ENTRY. + */ + State InitializeEntryScanning(); + + /** Scan one entry. When this entry matches the recognized template + types it is appended to the result set. + @return + Returns one of the states ERROR, SCAN_ENTRY, or SCAN_FOLDER. + */ + State ScanEntry(); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/TextObjectBar.hxx b/sd/source/ui/inc/TextObjectBar.hxx new file mode 100644 index 000000000..61394834f --- /dev/null +++ b/sd/source/ui/inc/TextObjectBar.hxx @@ -0,0 +1,58 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/shell.hxx> +#include <glob.hxx> + +namespace sd { + +class View; +class ViewShell; + +class TextObjectBar final + : public SfxShell +{ +public: + SFX_DECL_INTERFACE(SD_IF_SDDRAWTEXTOBJECTBAR) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + TextObjectBar ( + ViewShell* pSdViewShell, + SfxItemPool& rItemPool, + ::sd::View* pSdView); + virtual ~TextObjectBar() override; + + void GetAttrState( SfxItemSet& rSet ); + void GetCharState( SfxItemSet& rSet ); + void Execute( SfxRequest &rReq ); + +private: + ViewShell* mpViewShell; + ::sd::View* mpView; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/ToolBarManager.hxx b/sd/source/ui/inc/ToolBarManager.hxx new file mode 100644 index 000000000..45f4532fb --- /dev/null +++ b/sd/source/ui/inc/ToolBarManager.hxx @@ -0,0 +1,273 @@ +/* -*- 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 . + */ + +#pragma once + +#include "ShellFactory.hxx" +#include <rtl/ustring.hxx> + +#include <sal/types.h> +#include <memory> + +class SdrView; +namespace sd { class ViewShell; } +namespace sd::tools { class EventMultiplexer; } + +namespace sd { + +class ViewShellBase; +class ViewShellManager; + +/** Manage the set of visible tool bars (and object bars). Usually they + belong to the current view in the center pane. + + Tool bars are managed in groups. Each group can be set, reset, or + modified independently of the others. This allows for instance to + replace the toolbars associated with the current function independently + from those associated with the main view. + + The ToolBarManager has two high level methods which contain the + knowledge about which tool bars to show in a specific context. + When the view in the center pane changes then MainViewShellChanged() + sets up the tool bars for the new view. On changes of the selection the + SelectionHasChanged() method shows the tool bars for the new context. + + The update of the actually visible tool bars to the set currently + required by the main view shell and its functions is divided into two + parts, PreUpdate() and PostUpdate(). This are to be called before + respectively after the update of the view shell stack. The reason for + this is to save time by not updating tool bars that will not be visible + in a short time on a view shell switch. +*/ +class ToolBarManager + : public std::enable_shared_from_this<ToolBarManager> +{ +public: + /** Use this method instead of the constructor to create new objects of + this class. + */ + static std::shared_ptr<ToolBarManager> Create ( + ViewShellBase& rBase, + const std::shared_ptr<tools::EventMultiplexer>& rpMultiplexer, + const std::shared_ptr<ViewShellManager>& rpViewShellManager); + + ~ToolBarManager(); + + /** Call this method prior to the destructor to prevent the + ToolBarManager from accessing the ViewShellManager or the + XLayoutManager when those are possibly not well and alive anymore + (like during the destruction of the ViewShellBase.) + */ + void Shutdown(); + + /** When the view in the center pane changes then this method sets up + the initial set of tool bars for the new view. + The ToolBarManager listens for view switching itself and then calls + MainViewShellChanged(). Calling this method from the outside should + not be necessary. + @param nShellType + The type of the new main view shell. + */ + void MainViewShellChanged (); + void MainViewShellChanged (const ViewShell& rMainViewShell); + + /** Call this method when the selection has changed to update the more + temporary tool bars (those in the ToolBarGroup::Function group.) + */ + void SelectionHasChanged ( + const ViewShell& rViewShell, + const SdrView& rView); + + /** The set of tool bars that are handled by this manager class. + */ + constexpr static OUStringLiteral msToolBar = u"toolbar"; // Draw_Toolbox_Sd, 23011 + constexpr static OUStringLiteral msOptionsToolBar = u"optionsbar"; + // Draw_Options_Toolbox, 23020 + constexpr static OUStringLiteral msCommonTaskToolBar = u"commontaskbar"; + // Draw_CommonTask_Toolbox, 23021 + constexpr static OUStringLiteral msViewerToolBar = u"viewerbar"; // Draw_Viewer_Toolbox, 23023 + constexpr static OUStringLiteral msSlideSorterToolBar = u"slideviewtoolbar"; + // Slide_Toolbox, 23012 + constexpr static OUStringLiteral msSlideSorterObjectBar = u"slideviewobjectbar"; + // Slide_Obj_Toolbox, 23014 + constexpr static OUStringLiteral msOutlineToolBar = u"outlinetoolbar"; // Outline_Toolbox, 23017 + constexpr static OUStringLiteral msMasterViewToolBar = u"masterviewtoolbar"; + // SID_MASTERPAGE, 27053 + constexpr static OUStringLiteral msDrawingObjectToolBar = u"drawingobjectbar"; + // Draw_Obj_Toolbox, 23013 + constexpr static OUStringLiteral msGluePointsToolBar = u"gluepointsobjectbar"; + // Gluepoints_Toolbox, 23019 + constexpr static OUStringLiteral msTextObjectBar = u"textobjectbar"; + // Draw_Text_Toolbox_Sd, 23016 + constexpr static OUStringLiteral msBezierObjectBar = u"bezierobjectbar"; + // Bezier_Toolbox_Sd, 23015 + constexpr static OUStringLiteral msGraphicObjectBar = u"graphicobjectbar"; + // Draw_Graf_Toolbox, 23030 + constexpr static OUStringLiteral msMediaObjectBar = u"mediaobjectbar"; + // Draw_Media_Toolbox, 23031 + constexpr static OUStringLiteral msTableObjectBar = u"tableobjectbar"; + // Draw_Table_Toolbox, 23018 + + /** The set of tool bar groups. + */ + enum class ToolBarGroup { + Permanent, + Function, + CommonTask, + MasterMode, + LAST = MasterMode + }; + + /** Reset the set of visible object bars in the specified group. Tool + bars in other groups are not affected. + @param rParentShell + When this shell is not the main view then the method returns + immediately. + @param eGroup + Only the tool bars in this group are rest. + */ + void ResetToolBars (ToolBarGroup eGroup); + + /** Reset all tool bars, regardless of the group they belong to. + @param rParentShell + When this shell is not the main view then the method returns + immediately. + */ + void ResetAllToolBars(); + + /** Add the tool bar with the given name to the specified group of tool + bars. + @param rParentShell + When this shell is not the main view then the method returns + immediately. + @param eGroup + The new tool bar is added to this group. + @param rsToolBarName + The base name of the tool bar. A proper prefix (like + private:resource/toolbar/) is added. The name may be one of the + ones defined above. Other names are allowed as well. + */ + void AddToolBar ( + ToolBarGroup eGroup, + const OUString& rsToolBarName); + + /** Add the tool bar shell to the shell stack. This method basically + forwards the call to the ViewShellManager. + For some tool bar shells additional tool bars are made visible. + @param rParentShell + When this shell is not the main view then the method returns + immediately. + @param eGroup + The group is used for the actual tool bars. + @param nToolBarId + Id of the tool bar shell. + */ + void AddToolBarShell ( + ToolBarGroup eGroup, + ShellId nToolBarId); + + /** Remove the tool bar with the given name from the specified group. + If the tool bar is not visible then nothing happens. + If the tool bar is a member of another group then nothing happens + either. + */ + void RemoveToolBar ( + ToolBarGroup eGroup, + const OUString& rsToolBarName); + + /** This is basically a shortcut for ResetToolBars(),AddToolBar(). The + main difference is, that all sub shells of the specified parent + shell are deactivated as well. + @param rParentShell + When this shell is not the main view then the method returns + immediately. + @param eGroup + The new tool bar is added to this group. + @param rsToolBarName + The base name of the tool bar. A proper prefix (like + private:resource/toolbar/) is added. The name may be one of the + ones defined above. Other names are allowed as well. + */ + void SetToolBar ( + ToolBarGroup eGroup, + const OUString& rsToolBarName); + + /** This is basically a shortcut for ResetToolBars(),AddToolBar(). The + main difference is, that all sub shells of the specified parent + shell are deactivated as well. + @param rParentShell + When this shell is not the main view then the method returns + immediately. + @param rParentShell + When this shell is not the main view then the method returns + immediately. + @param eGroup + The group is currently not used. + @param nToolBarId + Id of the tool bar shell. + */ + void SetToolBarShell ( + ToolBarGroup eGroup, + ShellId nToolBarId); + + void PreUpdate(); + + /** Request an update of the active tool bars. The update is made + asynchronously. + */ + void RequestUpdate(); + + /** This is a hint for the ToolBarManager to improve the performance + when it updates its tool bars when its own lock is released. Taking + control of the release of the update lock of the ViewShellManager + avoids some shell stack modifications and tool bar updates. + */ + void LockViewShellManager(); + + /** Use this class to prevent the visible tool bars from being updated + (and thus causing repaints and GUI rearrangements) when several tool + bar operations are made in a row. + */ + class UpdateLock { public: + UpdateLock(const std::shared_ptr<ToolBarManager>& rpManager) + : mpManager(rpManager) { mpManager->LockUpdate(); } + ~UpdateLock() COVERITY_NOEXCEPT_FALSE { mpManager->UnlockUpdate(); } + private: + std::shared_ptr<ToolBarManager> mpManager; + }; + friend class UpdateLock; + + void ToolBarsDestroyed(); + +private: + class Implementation; + std::unique_ptr<Implementation> mpImpl; + + /** The ViewShellBase is used to get the XLayoutManager and to determine + the plug in mode. + */ + ToolBarManager(); + + void LockUpdate(); + void UnlockUpdate(); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/View.hxx b/sd/source/ui/inc/View.hxx new file mode 100644 index 000000000..4e530e3f9 --- /dev/null +++ b/sd/source/ui/inc/View.hxx @@ -0,0 +1,300 @@ +/* -*- 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 . + */ + +#pragma once + +#include <memory> +#include <pres.hxx> +#include <tools/gen.hxx> +#include <vcl/transfer.hxx> +#include <svx/fmview.hxx> +#include <svx/svdpage.hxx> +#include <vcl/idle.hxx> + +#include "smarttag.hxx" +#include "fusearch.hxx" + +class SdDrawDocument; +class SdPage; +class SdrOle2Obj; +class SdrGrafObj; +class SdrMediaObj; +class OutputDevice; +class ImageMap; +class Graphic; +class SdrOutliner; + +namespace avmedia { class PlayerListener; } + +namespace sd { + +class DrawDocShell; +class ViewShell; +class Window; +class ViewClipboard; + +//For master view we want to force that master +//textboxes have readonly text, because the +//text is the auto-generated click-here-to-edit +//and it doesn't help to change it +class OutlinerMasterViewFilter +{ +private: + SdrOutliner *m_pOutl; + bool m_bReadOnly; +public: + OutlinerMasterViewFilter() + : m_pOutl(nullptr) + , m_bReadOnly(false) + { + } + void Start(SdrOutliner *pOutl); + void End(); +}; + +class SearchContext +{ +private: + rtl::Reference<FuSearch> maFunctionSearch; + +public: + rtl::Reference<FuSearch>& getFunctionSearch() + { + return maFunctionSearch; + } + + void setSearchFunction(rtl::Reference<FuSearch> const & xFunction) + { + resetSearchFunction(); + maFunctionSearch = xFunction; + } + + void resetSearchFunction() + { + if (maFunctionSearch.is()) + maFunctionSearch->Dispose(); + } +}; + +class SAL_DLLPUBLIC_RTTI View : public FmFormView +{ +public: + + View ( + SdDrawDocument& rDrawDoc, + OutputDevice* pOutDev, + ViewShell* pViewSh=nullptr); + virtual ~View() override; + + void CompleteRedraw( OutputDevice* pOutDev, const vcl::Region& rReg, sdr::contact::ViewObjectContactRedirector* pRedirector = nullptr) override; + + virtual void GetAttributes( SfxItemSet& rTargetSet, bool bOnlyHardAttr = false ) const; + virtual bool SetAttributes(const SfxItemSet& rSet, bool bReplaceAll = false, bool bSlide = false, bool bMaster = false); + virtual void MarkListHasChanged() override; + void SelectAll(); + void DoCut(); + void DoCopy(); + void DoPaste(::sd::Window* pWindow=nullptr); + virtual void DoConnect(SdrOle2Obj* pOleObj) override; + virtual bool SetStyleSheet(SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr = false); + void StartDrag( const Point& rStartPos, vcl::Window* pWindow ); + virtual void DragFinished( sal_Int8 nDropAction ); + virtual sal_Int8 AcceptDrop ( + const AcceptDropEvent& rEvt, + DropTargetHelper& rTargetHelper, + SdrLayerID nLayer); + virtual sal_Int8 ExecuteDrop ( + const ExecuteDropEvent& rEvt, + ::sd::Window* pTargetWindow, + sal_uInt16 nPage, + SdrLayerID nLayer); + + css::uno::Reference<css::datatransfer::XTransferable> + CreateClipboardDataObject (); + css::uno::Reference<css::datatransfer::XTransferable> + CreateDragDataObject (::sd::View*, vcl::Window& rWindow, + const Point& rDragPos); + css::uno::Reference<css::datatransfer::XTransferable> + CreateSelectionDataObject (::sd::View*); + + // update clipboard to what is selected + void UpdateSelectionClipboard(); + + // release content of clipboard, if we own the content + void ClearSelectionClipboard(); + + DrawDocShell* GetDocSh() const { return mpDocSh; } + inline SdDrawDocument& GetDoc() const; + ViewShell* GetViewShell() const { return mpViewSh; } + SfxViewShell* GetSfxViewShell() const override; + + // Create a local UndoManager + std::unique_ptr<SdrUndoManager> createLocalTextUndoManager() override; + + virtual bool SdrBeginTextEdit(SdrObject* pObj, SdrPageView* pPV = nullptr, vcl::Window* pWin = nullptr, bool bIsNewObj = false, + SdrOutliner* pGivenOutliner = nullptr, OutlinerView* pGivenOutlinerView = nullptr, + bool bDontDeleteOutliner = false, bool bOnlyOneView = false, bool bGrabFocus = true) override; + + virtual SdrEndTextEditKind SdrEndTextEdit(bool bDontDeleteReally = false) override; + + bool RestoreDefaultText( SdrTextObj* pTextObj ); + + bool InsertData( const TransferableDataHelper& rDataHelper, + const Point& rPos, sal_Int8& rDnDAction, bool bDrag, + SotClipboardFormatId nFormat = SotClipboardFormatId::NONE, + sal_uInt16 nPage = SDRPAGE_NOTFOUND, SdrLayerID nLayer = SDRLAYER_NOTFOUND ); + /** gets the metafile from the given transferable helper and insert it as a graphic shape. + @param bOptimize if set to true, the metafile is analyzed and if only one bitmap action is + present, then is inserted as a single graphic. + */ + bool InsertMetaFile( const TransferableDataHelper& rDataHelper, + const Point& rInsertPos, + ImageMap const * pImageMap, bool bOptimize ); + SdrGrafObj* InsertGraphic( const Graphic& rGraphic, + sal_Int8& rAction, const Point& rPos, + SdrObject* pSelectedObj, ImageMap const * pImageMap ); + void InsertMediaURL( const OUString& rMediaURL, sal_Int8& rAction, + const Point& rPos, const Size& rSize, + bool const bLink ); + SdrMediaObj* InsertMediaObj( const OUString& rURL, const OUString& rMimeType, sal_Int8& rAction, + const Point& rPos, const Size& rSize ); + + bool PasteRTFTable( const ::tools::SvRef<SotTempStream>& xStm, SdrPage* pPage, SdrInsertFlags nPasteOptions ); + + bool IsPresObjSelected(bool bOnPage = true, bool bOnMasterPage = true, bool bCheckPresObjListOnly = false, bool bCheckLayoutOnly = false) const; + + void SetMarkedOriginalSize(); + + bool IsMorphingAllowed() const; + bool IsVectorizeAllowed() const; + + virtual SfxStyleSheet* GetStyleSheet() const; + + /** return parameter: + pExchangeList == NULL -> all names are unique + bNameOK == false -> cancel by user + nType == 0 -> pages + nType == 1 -> objects + nType == 2 -> pages and objects */ + + bool GetExchangeList( std::vector<OUString> &rExchangeList, + std::vector<OUString> &rBookmarkList, + const sal_uInt16 nType ); + + virtual void onAccessibilityOptionsChanged() override; + + /** returns true if we have an undo manager and there is an open list undo action */ + bool isRecordingUndo() const; + + virtual void AddCustomHdl() override; + + SmartTagSet& getSmartTags() { return maSmartTags; } + void updateHandles(); + + virtual SdrViewContext GetContext() const override; + virtual bool HasMarkablePoints() const override; + virtual sal_Int32 GetMarkablePointCount() const override; + virtual bool HasMarkedPoints() const override; + virtual bool MarkPoint(SdrHdl& rHdl, bool bUnmark=false) override; + virtual void CheckPossibilities() override; + virtual bool MarkPoints(const ::tools::Rectangle* pRect, bool bUnmark) override; + using SdrMarkView::MarkPoints; + + bool ShouldToggleOn( + const bool bBulletOnOffMode, + const bool bNormalBullet); + + /** change the bullets/numbering of the marked objects + + @param bToggle + true: just toggle the current bullets/numbering on --> off resp. off --> on + + @param bHandleBullets + true: handle bullets + false: handle numbering + + @param pNumRule + numbering rule which needs to be applied. can be 0. + */ + void ChangeMarkedObjectsBulletsNumbering( + const bool bToggle, + const bool bHandleBullets, + const SvxNumRule* pNumRule); + + void SetPossibilitiesDirty() { m_bPossibilitiesDirty = true; } + void SetMoveAllowed( bool bSet ) { m_bMoveAllowed = bSet; } + void SetMoveProtected( bool bSet ) { m_bMoveProtect = bSet; } + void SetResizeFreeAllowed( bool bSet ) { m_bResizeFreeAllowed = bSet; } + void SetResizePropAllowed( bool bSet ) { m_bResizePropAllowed = bSet; } + void SetResizeProtected( bool bSet ) { m_bResizeProtect = bSet; } + + SdrObject* GetEmptyPresentationObject( PresObjKind eKind ); + SdPage* GetPage(); + SdrObject* GetSelectedSingleObject(SdPage const * pPage); + void SetAuthor(const OUString& rAuthor) { m_sAuthor = rAuthor; } + const OUString& GetAuthor() const { return m_sAuthor; } + + SearchContext& getSearchContext() { return maSearchContext; } +protected: + DECL_DLLPRIVATE_LINK( OnParagraphInsertedHdl, ::Outliner::ParagraphHdlParam, void ); + DECL_DLLPRIVATE_LINK( OnParagraphRemovingHdl, ::Outliner::ParagraphHdlParam, void ); + + virtual void OnBeginPasteOrDrop( PasteOrDropInfos* pInfo ) override; + virtual void OnEndPasteOrDrop( PasteOrDropInfos* pInfo ) override; + + SdDrawDocument& mrDoc; + DrawDocShell* mpDocSh; + ViewShell* mpViewSh; + std::unique_ptr<SdrMarkList> mpDragSrcMarkList; + SdrObject* mpDropMarkerObj; + std::unique_ptr<SdrDropMarkerOverlay> mpDropMarker; + sal_uInt16 mnDragSrcPgNum; + Point maDropPos; + ::std::vector<OUString> maDropFileVector; + sal_Int8 mnAction; + Idle maDropErrorIdle; + Idle maDropInsertFileIdle; + rtl::Reference<avmedia::PlayerListener> mxDropMediaSizeListener; + sal_uInt16 mnLockRedrawSmph; + bool mbIsDropAllowed; + + DECL_DLLPRIVATE_LINK( DropErrorHdl, Timer*, void ); + DECL_DLLPRIVATE_LINK( DropInsertFileHdl, Timer*, void ); + DECL_DLLPRIVATE_LINK( ExecuteNavigatorDrop, void*, void ); + + void ImplClearDrawDropMarker(); + + SmartTagSet maSmartTags; + +private: + ::std::unique_ptr<ViewClipboard> mpClipboard; + OutlinerMasterViewFilter maMasterViewFilter; + SearchContext maSearchContext; + + OUString m_sAuthor; +}; + +SdDrawDocument& View::GetDoc() const +{ + return mrDoc; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/ViewClipboard.hxx b/sd/source/ui/inc/ViewClipboard.hxx new file mode 100644 index 000000000..f16c0ad33 --- /dev/null +++ b/sd/source/ui/inc/ViewClipboard.hxx @@ -0,0 +1,78 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/types.h> + +class SdPage; +class SdTransferable; + +namespace sd +{ +class View; + +/** Handle clipboard related tasks for the draw view. +*/ +class ViewClipboard +{ +public: + ViewClipboard(::sd::View& rView); + virtual ~ViewClipboard(); + + /** Handle the drop of a drag-and-drop action where the transferable + contains a set of pages. + */ + void HandlePageDrop(const SdTransferable& rTransferable); + +protected: + ::sd::View& mrView; + + /** Return the first master page of the given transferable. When the + bookmark list of the transferable contains at least one non-master + page then NULL is returned. + */ + static SdPage* GetFirstMasterPage(const SdTransferable& rTransferable); + + /** Assign the (first) master page of the given transferable to the + (...) slide. + */ + void AssignMasterPage(const SdTransferable& rTransferable, SdPage const* pMasterPage); + + /** Return an index of a page after which the pages of the transferable + are to be inserted into the target document. + */ + virtual sal_uInt16 DetermineInsertPosition(); + + /** Insert the slides in the given transferable behind the last selected + slide or, when the selection is empty, behind the last slide. + @param rTransferable + This transferable defines which pages to insert. + @param nInsertPosition + The pages of the transferable will be inserted behind the page + with this index. + @return + Returns the number of inserted slides. + */ + sal_uInt16 InsertSlides(const SdTransferable& rTransferable, sal_uInt16 nInsertPosition); +}; + +} // end of namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/ViewShell.hxx b/sd/source/ui/inc/ViewShell.hxx new file mode 100644 index 000000000..1eeede9e2 --- /dev/null +++ b/sd/source/ui/inc/ViewShell.hxx @@ -0,0 +1,559 @@ +/* -*- 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 . + */ + +#pragma once + +#include <rtl/ref.hxx> + +#include <sfx2/viewsh.hxx> +#include <svl/typedwhich.hxx> +#include <vcl/prntypes.hxx> +#include <vcl/scrbar.hxx> +#include <o3tl/deleter.hxx> +#include <pres.hxx> +#include "View.hxx" +#include "fupoor.hxx" +#include <sddllapi.h> + +#include <memory> + +class SdPage; +class SvxRuler; +class SdrOle2Obj; // for the ones, who have undefined parts of SVDRAW +class SdDrawDocument; +class SvxNumBulletItem; + +namespace weld +{ + class Window; +} + +namespace com::sun::star::drawing { class XDrawSubController; } + +namespace sd { + +class DrawDocShell; +class FrameView; +class LayerTabBar; +class ViewShellBase; +class Window; +class WindowUpdater; +class ZoomList; + +#undef OUTPUT_DRAWMODE_COLOR +#undef OUTPUT_DRAWMODE_CONTRAST + +const DrawModeFlags OUTPUT_DRAWMODE_COLOR = DrawModeFlags::Default; +const DrawModeFlags OUTPUT_DRAWMODE_GRAYSCALE + = DrawModeFlags::GrayLine | DrawModeFlags::GrayFill + | DrawModeFlags::BlackText | DrawModeFlags::GrayBitmap + | DrawModeFlags::GrayGradient; +const DrawModeFlags OUTPUT_DRAWMODE_BLACKWHITE + = DrawModeFlags::BlackLine | DrawModeFlags::BlackText + | DrawModeFlags::WhiteFill | DrawModeFlags::GrayBitmap + | DrawModeFlags::WhiteGradient; +const DrawModeFlags OUTPUT_DRAWMODE_CONTRAST + = DrawModeFlags::SettingsLine | DrawModeFlags::SettingsFill + | DrawModeFlags::SettingsText | DrawModeFlags::SettingsGradient; + +/** Base class of the stacked shell hierarchy. + + <p>Despite its name this class is not a descendant of SfxViewShell + but of SfxShell. Its name expresses the fact that it acts like a + view shell. Being a stacked shell rather than being an actual view shell + there can be several instances of this class that + <ul> + <li>all are based on the same view shell and thus show the same + document and share common view functionality and</li> + <li>are all visible at the same time and live in the same + frame.</li> + <ul></p> + + <p>This class replaces the former ViewShell class.</p> +*/ +class SAL_DLLPUBLIC_RTTI ViewShell + : public SfxShell +{ +public: + enum ShellType { + ST_NONE, + ST_DRAW, // The Draw application. + ST_IMPRESS, // Main view of the Impress application. + ST_NOTES, + ST_HANDOUT, + ST_OUTLINE, + ST_SLIDE_SORTER, + ST_PRESENTATION, + ST_SIDEBAR + }; + static const int MAX_HSPLIT_CNT = 1; + static const int MAX_VSPLIT_CNT = 1; + static const int MIN_SCROLLBAR_SIZE = 50; + + + ViewShell ( + vcl::Window* pParentWindow, + ViewShellBase& rViewShellBase); + virtual ~ViewShell() override; + + /** The Init method has to be called from the outside directly + after a new object of this class has been created. It can be + used for that part of the initialisation that can be run only + after the creation of the new object is finished. This + includes registration as listener at event broadcasters. + + Derived classes should call this method at the head of their + Init() methods. + @param bIsMainViewShell + This flag tells the Init() method whether the new ViewShell will + be the main view shell. + */ + virtual void Init (bool bIsMainViewShell); + + /** The Exit() method has to be called before the destructor so that the + view shell is still a valid object and can safely call methods that + rely on that. + */ + void Exit(); + + void Cancel(); + + /** Return the window that is the parent of all controls of this view + shell. This may or may not be the window of the frame. + */ + vcl::Window* GetParentWindow() const { return mpParentWindow; } + + sd::Window* GetContentWindow() const; + + ::sd::View* GetView() const { return mpView; } + inline SdrView* GetDrawView() const; + SD_DLLPUBLIC DrawDocShell* GetDocSh() const; + + SdDrawDocument* GetDoc() const; + + SD_DLLPUBLIC SfxViewFrame* GetViewFrame() const; + + /** The active window is usually the mpContentWindow. When there is a + show running then the active window is a ShowWindow. + */ + ::sd::Window* GetActiveWindow() const { return mpActiveWindow;} + SD_DLLPUBLIC weld::Window* GetFrameWeld() const; + + /** Set the active window. When the shell is displayed in the center + pane then the window of the ViewShellBase is also set to the given + window. + */ + void SetActiveWindow (::sd::Window* pWindow); + + /** Return the rectangle that encloses all windows of the view. That + excludes the controls in the frame like rulers, scroll bars, tab + bar, and buttons. + @return + The rectangle is returned in screen coordinates, i.e. pixel + values relative to the upper left corner of the screen?. + */ + const ::tools::Rectangle& GetAllWindowRect(); + + // Mouse- & Key-Events + virtual void PrePaint(); + virtual void Paint (const ::tools::Rectangle& rRect, ::sd::Window* pWin); + virtual bool KeyInput(const KeyEvent& rKEvt, ::sd::Window* pWin); + virtual void MouseMove(const MouseEvent& rMEvt, ::sd::Window* pWin); + virtual void MouseButtonUp(const MouseEvent& rMEvt, ::sd::Window* pWin); + virtual void MouseButtonDown(const MouseEvent& rMEvt, ::sd::Window* pWin); + virtual void Command(const CommandEvent& rCEvt, ::sd::Window* pWin); + bool RequestHelp( const HelpEvent& rEvt ); + bool Notify( NotifyEvent const & rNEvt, ::sd::Window* pWin ); + + bool HandleScrollCommand(const CommandEvent& rCEvt, ::sd::Window* pWin); + + void SetUIUnit(FieldUnit eUnit); + void SetDefTabHRuler( sal_uInt16 nDefTab ); + + const SvxNumBulletItem* GetNumBulletItem(SfxItemSet& aNewAttr, TypedWhichId<SvxNumBulletItem>& nNumItemId); + + bool HasRuler() const { return mbHasRulers;} + void SetRuler(bool bRuler); + // Hides horizontal, vertical scrollbar as well as scrollbox + void SetScrollBarsVisible(bool bVisible); + + /** Set internal values of all scroll bars that determine thumb size and + position. The external values like size and position of the scroll + bar controls are not modified. + */ + virtual void UpdateScrollBars(); + void Scroll(::tools::Long nX, ::tools::Long nY); + void ScrollLines(::tools::Long nX, ::tools::Long nY); + virtual void SetZoom(::tools::Long nZoom); + ::tools::Long GetZoom() const; + virtual void SetZoomRect(const ::tools::Rectangle& rZoomRect); + void InitWindows(const Point& rViewOrigin, const Size& rViewSize, + const Point& rWinPos, bool bUpdate = false); + void InvalidateWindows(); + /** This method is still used by the OutlineViewShell to update the + model according to the content of the outline view. This in turn + updates the previews in the slide sorter. + */ + virtual void UpdatePreview (SdPage* pPage); + + void DrawMarkRect(const ::tools::Rectangle& rRect) const; + + void ExecReq( SfxRequest &rReq ); + + ZoomList* GetZoomList() { return mpZoomList.get();} + + FrameView* GetFrameView() { return mpFrameView; } + /** Setting a frame view triggers ReadFrameViewData() for the new + frame. + @param pFrameView + The new frame view that replaces the old one. + */ + void SetFrameView (FrameView* pFrameView); + virtual void ReadFrameViewData(FrameView* pView); + virtual void WriteFrameViewData(); + void WriteUserData(); + void ReadUserData(); + + virtual bool ActivateObject(SdrOle2Obj* pObj, sal_Int32 nVerb); + + /** @returns + current or selected page or 0. This method + will fail in master page mode. + + @deprecated, please use getCurrentPage(); + */ + virtual SdPage* GetActualPage() = 0; + + /** @returns + current or selected page or 0. + */ + virtual SdPage* getCurrentPage() const = 0; + + const rtl::Reference<FuPoor>& GetOldFunction() const { return mxOldFunction; } + bool HasOldFunction() const { return mxOldFunction.is(); } + const rtl::Reference<FuPoor>& GetCurrentFunction() const { return mxCurrentFunction; } + bool HasCurrentFunction( sal_uInt16 nSID ) { return mxCurrentFunction.is() && (mxCurrentFunction->GetSlotID() == nSID ); } + bool HasCurrentFunction() const { return mxCurrentFunction.is(); } + + void SetCurrentFunction(const rtl::Reference<FuPoor>& xFunction); + void SetOldFunction(const rtl::Reference<FuPoor>& xFunction); + void DeactivateCurrentFunction( bool bPermanent = false ); + + void SetPageSizeAndBorder(PageKind ePageKind, const Size& rNewSize, + ::tools::Long nLeft, ::tools::Long nRight, ::tools::Long nUpper, ::tools::Long nLower, + bool bScaleAll, Orientation eOrient, sal_uInt16 nPaperBin, + bool bBackgroundFullSize ); + + void SetStartShowWithDialog( bool bIn ) { mbStartShowWithDialog = bIn; } + bool IsStartShowWithDialog() const { return mbStartShowWithDialog; } + + sal_uInt16 GetPrintedHandoutPageNum() const { return mnPrintedHandoutPageNum; } + void SetPrintedHandoutPageNum (sal_uInt16 nPageNumber) {mnPrintedHandoutPageNum=nPageNumber; } + + sal_uInt16 GetPrintedHandoutPageCount() const { return mnPrintedHandoutPageCount; } + void SetPrintedHandoutPageCount (sal_uInt16 nPageCount) {mnPrintedHandoutPageCount=nPageCount; } + + virtual bool PrepareClose( bool bUI = true ); + + void GetMenuState(SfxItemSet& rSet); + + virtual sal_Int8 AcceptDrop( const AcceptDropEvent& rEvt, DropTargetHelper& rTargetHelper, + ::sd::Window* pTargetWindow, sal_uInt16 nPage, SdrLayerID nLayer ); + virtual sal_Int8 ExecuteDrop( const ExecuteDropEvent& rEvt, DropTargetHelper& rTargetHelper, + ::sd::Window* pTargetWindow, sal_uInt16 nPage, SdrLayerID nLayer ); + + virtual void WriteUserDataSequence ( css::uno::Sequence < css::beans::PropertyValue >& ); + virtual void ReadUserDataSequence ( const css::uno::Sequence < css::beans::PropertyValue >& ); + + /** this method is called when the visible area of the view from this viewshell is changed */ + virtual void VisAreaChanged(const ::tools::Rectangle& rRect); + + /** Create an accessible object representing the specified window. + Override this method to provide view mode specific objects. The + default implementation returns an empty reference. + @param pWindow + Make the document displayed in this window accessible. + @return + This default implementation returns an empty reference. + */ + virtual css::uno::Reference<css::accessibility::XAccessible> + CreateAccessibleDocumentView (::sd::Window* pWindow); + + virtual void SwitchViewFireFocus( const css::uno::Reference< css::accessibility::XAccessible >& xAcc ); + void SwitchActiveViewFireFocus( ); + // Move these two methods from DrawViewShell to enable slide show view + void NotifyAccUpdate(); + void fireSwitchCurrentPage(sal_Int32 pageIndex); + void SetWinViewPos(const Point& rWinPos); + Point const & GetWinViewPos() const; + Point const & GetViewOrigin() const; + + /** Return the window updater of this view shell. + @return + In rare circumstances the returned pointer may be <null/>, + i.e. when no memory is available anymore. + */ + ::sd::WindowUpdater* GetWindowUpdater() const; + + /** Return the border that is drawn around the actual document view. + The border contains typically rulers and scroll bars. + */ + SvBorder GetBorder(); + + /** Notify the view shell that its parent window has been resized. + The ViewShell places and resizes its UI elements accordingly. + The new size can be obtained from the parent window. + */ + virtual void Resize(); + + /** Set position and size of the GUI elements that are controlled by + the view shell like rulers and scroll bars as well as the actual + document view according to the position and size that were given + with the last Resize() call. + */ + virtual void ArrangeGUIElements(); + + // virtual void OuterResizePixel(const Point &rPos, const Size &rSize); + // virtual void InnerResizePixel(const Point &rPos, const Size &rSize); + + // Exported for unit test + SD_DLLPUBLIC ViewShellBase& GetViewShellBase() const; + + /** Return <TRUE/> when the called view shell is the main sub shell of + its ViewShellBase object, i.e. is display in the center pane. This + convenience function is equivalent to comparing the this pointer to + the result of ViewShellBase::GetViewShell(PT_CENTER). + */ + bool IsMainViewShell() const; + + /** Set or reset the flag that indicates whether the called shell is the + one displayed in the center pane. By default this flag is set to + <FALSE/>. For the main view shell it thus has to be set to <TRUE/>. + */ + void SetIsMainViewShell (bool bIsMainViewShell); + + /** Return a sub controller that implements the view shell specific + part of the DrawController. + */ + virtual css::uno::Reference<css::drawing::XDrawSubController> CreateSubController() = 0; + + /** Return the type of the shell. + */ + SD_DLLPUBLIC ShellType GetShellType() const; //Export for unit test + + /** This method is more or less an alias to Deactivate(). It is called + before an object of this class is taken from the stack of view + shells. + + <p>When this method is not called before a view shell is taken from + a stack then the Deactivate() call from the SFX as a response to + RemoveSubShell() comes too late when the view shell is not on the + stack anymore.</p> + */ + virtual void Shutdown(); + + /** This function is called from the underlying ViewShellBase + object to handle a verb execution request. + */ + virtual ErrCode DoVerb(sal_Int32 nVerb); + + virtual void UIActivating( SfxInPlaceClient* ); + virtual void UIDeactivated( SfxInPlaceClient* ); + + /** Show controls of the UI or hide them, depending on the given flag. + As a result the border is adapted. + */ + virtual void ShowUIControls (bool bVisible); + bool IsPageFlipMode() const; + + /** Set the given window as new parent window. This is not possible for + all views, so the return value tells the caller if the relocation + was successful. + */ + virtual bool RelocateToParentWindow (vcl::Window* pParentWindow); + + /** Depending on the given request create a new page or duplicate an + existing one. A new page is created behind the given slide. + @param rRequest + The request as passed to an Execute() method. Its arguments are + evaluated. Its slot id determines whether to create or + duplicate a slide. + @param pPage + This page is either duplicated or becomes the predecessor of the + new slide. If NULL a duplication request is ignored. A new + slide is inserted as first slide. + @param nInsertPosition + When -1 (the default) then insert after pPage. Otherwise insert + before the given index (of a standard page). + @return + The new slide is returned. If for some reason a new page can + not be created then NULL is returned. + */ + virtual SdPage* CreateOrDuplicatePage ( + SfxRequest& rRequest, + PageKind ePageKind, + SdPage* pPage, + const sal_Int32 nInsertPosition = -1); + + /// Allows adjusting the point or mark of the selection to a document coordinate. + void SetCursorMm100Position(const Point& rPosition, bool bPoint, bool bClearMark); + /// Gets the current selection + css::uno::Reference<css::datatransfer::XTransferable> GetSelectionTransferrable() const; + /// Allows starting or ending a graphic move or resize action. + void SetGraphicMm100Position(bool bStart, const Point& rPosition); + + class Implementation; + +protected: + /** must be called in the beginning of each subclass d'tor. + disposes and clears both current and old function. */ + void DisposeFunctions(); + + friend class ViewShellBase; + + /** Window inside the rulers and scroll bars that shows a view of the + document. + */ + + VclPtr<sd::Window> mpContentWindow; + + /// Horizontal scroll bar for the current slide is displayed when needed. + VclPtr<ScrollBar> mpHorizontalScrollBar; + /// Vertical scroll bar for whole document is always visible. + VclPtr<ScrollBar> mpVerticalScrollBar; + /// Horizontal ruler is not shown by default. + VclPtr<SvxRuler> mpHorizontalRuler; + /// Vertical ruler is not shown by default. + VclPtr<SvxRuler> mpVerticalRuler; + /// Filler of the little square enclosed by the two scroll bars. + VclPtr<ScrollBarBox> mpScrollBarBox; + /// Layer tab bar. + VclPtr<LayerTabBar> mpLayerTabBar; + + /// This flag controls whether the rulers are visible. + bool mbHasRulers; + + /// The active window. + VclPtr< ::sd::Window> mpActiveWindow; + ::sd::View* mpView; + FrameView* mpFrameView; + + rtl::Reference<FuPoor> mxCurrentFunction; + rtl::Reference<FuPoor> mxOldFunction; + std::unique_ptr<ZoomList> mpZoomList; + + Point maViewPos; + Size maViewSize; + Size maScrBarWH; + + bool mbStartShowWithDialog; // presentation is started by dialog + sal_uInt16 mnPrintedHandoutPageNum; // Page number of the handout page that is to be printed. + sal_uInt16 mnPrintedHandoutPageCount; // Page count of the handout pages that are to be printed. + + //af bool bPrintDirectSelected; // Print only selected objects in direct print + //afString sPageRange; // pagerange if selected objects in direct print + + /** Area covered by all windows, i.e. the area of the parent window + without the controls at the borders like rulers, scroll bars, tab + bar, buttons. + This rectangle may be set in window coordinates (i.e. pixel values + relative to the parent window). It is transformed by every call to + GetAllWindowRectangle() into screen coordinates (relative to the + upper left corner of the screen. + */ + ::tools::Rectangle maAllWindowRectangle; + + /// The type of the shell. Returned by GetShellType(). + ShellType meShellType; + + std::unique_ptr<Implementation, o3tl::default_delete<Implementation>> mpImpl; + + // Support methods for centralized UNDO/REDO + virtual SfxUndoManager* ImpGetUndoManager() const; + void ImpGetUndoStrings(SfxItemSet &rSet) const; + void ImpGetRedoStrings(SfxItemSet &rSet) const; + void ImpSidUndo(SfxRequest& rReq); + void ImpSidRedo(SfxRequest& rReq); + + DECL_DLLPRIVATE_LINK( HScrollHdl, ScrollBar *, void ); + DECL_DLLPRIVATE_LINK( VScrollHdl, ScrollBar *, void ); + + // virtual scroll handler, here, derivative classes can add themselves here + virtual void VirtHScrollHdl(ScrollBar* pHScroll); + virtual void VirtVScrollHdl(ScrollBar* pVScroll); + + // virtual functions ruler handling + virtual VclPtr<SvxRuler> CreateHRuler(::sd::Window* pWin); + virtual VclPtr<SvxRuler> CreateVRuler(::sd::Window* pWin); + virtual void UpdateHRuler(); + virtual void UpdateVRuler(); + + virtual void Activate(bool IsMDIActivate) override; + virtual void Deactivate(bool IsMDIActivate) override; + + virtual void SetZoomFactor( const Fraction &rZoomX, + const Fraction &rZoomY ); + + /** + This must be called after the ctor, but before anything else. + It's the part of construction that is dependent + on showing the top-level window. + + Showing a window with a11y enabled causes various callbacks + to be triggered. + + Due to the "virtual methods are not virtual during constructors" + problem, this is a disaster to call from the ctor + + i.e. construct calls Show, and if a11y is enabled this + reenters the not-fully constructed object and calls + CreateAccessibleDocumentView, so if construct is called + from the ctor then if a derived class is constructed the base-case + CreateAccessibleDocumentView is used, not the derived + CreateAccessibleDocumentView. i.e. run smoketest under a11y with + debugging assertions enabled + */ + void doShow(); + +private: + VclPtr<vcl::Window> mpParentWindow; + /** This window updater is used to keep all relevant windows up to date + with reference to the digit language used to display digits in text + shapes. + */ + ::std::unique_ptr< ::sd::WindowUpdater> mpWindowUpdater; + + /** Code common to all constructors. It generally is a bad idea + to call this function from outside a constructor. + */ + void construct(); + + /** Create the rulers. + */ + void SetupRulers(); +}; + +SdrView* ViewShell::GetDrawView() const +{ + return static_cast<SdrView*>(mpView); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/ViewShellBase.hxx b/sd/source/ui/inc/ViewShellBase.hxx new file mode 100644 index 000000000..eab26ec8a --- /dev/null +++ b/sd/source/ui/inc/ViewShellBase.hxx @@ -0,0 +1,246 @@ +/* -*- 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 . + */ + +#pragma once + +#include <glob.hxx> +#include <sfx2/viewsh.hxx> +#include <memory> + +class SdDrawDocument; +class SfxRequest; + +namespace sd::tools { +class EventMultiplexer; +} + +namespace sd { + +class DrawController; +class DrawDocShell; +class FormShellManager; +class ToolBarManager; +class ViewShell; +class ViewShellManager; +class ViewTabBar; + +/** SfxViewShell descendant that the stacked Draw/Impress shells are + based on. + + <p>The "base" part of the name does not mean that this is a base + class of some class hierarchy. It rather is the base of the + stacked shells.</p> + + <p>This class starts as a new and relatively small class. Over + time as much code as possible should be moved from the stacked + shells to this class.</p> +*/ +class ViewShellBase + : public SfxViewShell +{ +public: + SFX_DECL_INTERFACE(SD_IF_SDVIEWSHELLBASE) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + /** This constructor is used by the view factory of the SFX macros. + Note that LateInit() has to be called after the constructor + terminates and before doing anything else. + */ + ViewShellBase ( + SfxViewFrame *pFrame, + SfxViewShell* pOldShell); + + virtual ~ViewShellBase() override; + + /** This method is part of the object construction. It HAS to be called + after the constructor has created a new object. + */ + void LateInit (const OUString& rsDefaultView); + + std::shared_ptr<ViewShellManager> const & GetViewShellManager() const; + + /** Return the main view shell stacked on the called ViewShellBase + object. This is usually the view shell displayed in the center + pane. + */ + std::shared_ptr<ViewShell> GetMainViewShell() const; + + /** When given a view frame this static method returns the + corresponding sd::ViewShellBase object. + @return + When the SfxViewShell of the given frame is not a + ViewShellBase object then NULL is returned. + */ + static ViewShellBase* GetViewShellBase (SfxViewFrame const * pFrame); + + DrawDocShell* GetDocShell() const { return mpDocShell;} + SdDrawDocument* GetDocument() const { return mpDocument;} + + /** Callback function for general slot calls. At the moment these are + slots for switching the pane docking windows on and off. + */ + virtual void Execute (SfxRequest& rRequest); + + /** Callback function for retrieving item values related to certain + slots. This is the companion of Execute() and handles the slots + concerned with showing the pane docking windows. + */ + void GetState (SfxItemSet& rSet); + + /* override these from SfxViewShell */ + virtual OUString GetSelectionText(bool = false, bool bOnlyASample = false) override; + virtual bool HasSelection(bool = true ) const override; + + SvBorder GetBorder (bool bOuterResize); + virtual void InnerResizePixel (const Point& rOrigin, const Size& rSize, bool inplaceEditModeChange) override; + virtual void OuterResizePixel (const Point& rOrigin, const Size& rSize) override; + + /** This call is forwarded to the main sub-shell. + */ + virtual ErrCode DoVerb(sal_Int32 nVerb) override; + + /** Return a new renderer that can be used for example for printing the + document. + */ + virtual css::uno::Reference<css::view::XRenderable> GetRenderable() override; + + /// Forwarded to the print manager. + virtual SfxPrinter* GetPrinter (bool bCreate = false) override; + + /// Forwarded to the print manager. + virtual sal_uInt16 SetPrinter ( + SfxPrinter* pNewPrinter, + SfxPrinterChangeFlags nDiffFlags = SFX_PRINTER_ALL) override; + + /// Forward methods to main sub shell. + virtual void WriteUserDataSequence ( + css::uno::Sequence< css::beans::PropertyValue >&) override; + + /** Pass the given properties to the main view shell. After that we + ensure that the right view shell type is displayed in the center + pane. + */ + virtual void ReadUserDataSequence ( + const css::uno::Sequence< css::beans::PropertyValue >&) override; + + virtual void UIActivating( SfxInPlaceClient* ) override; + virtual void UIDeactivated( SfxInPlaceClient* ) override; + virtual void Activate (bool IsMDIActivate) override; + using SfxViewShell::Deactivate; + virtual void SetZoomFactor ( + const Fraction &rZoomX, + const Fraction &rZoomY) override; + virtual bool PrepareClose (bool bUI = true) override; + virtual void WriteUserData (OUString&, bool bBrowse = false) override; + virtual void ReadUserData (const OUString&, bool bBrowse = false) override; + virtual SdrView* GetDrawView() const override; + + /** When <TRUE/> is given, then the mouse shape is set to hour glass (or + whatever the busy shape looks like on the system.) + */ + void SetBusyState (bool bBusy); + + /** Call this method when the controls of this view shell or the + embedded sub shell need to be rearranged. This is necessary + e.g. when the border has been modified (UpdateBorder() calls this + method). + + This method is like ResizePixel() with no arguments. + */ + void Rearrange(); + + /** Update the border that is set with SfxViewShell::SetBorderPixel(). + This is done by adding the border used by the ViewShellBase itself + with the border used by the main view shell. + + @param bForce if true the borders are also updated if old border + and new border are same. + */ + void UpdateBorder ( bool bForce = false ); + + /** With this method the UI controls can be turned on or off. It is + used by the FuSlideShow to hide the UI controls while showing a + non-full-screen or in-window presentation in the center pane. + */ + void ShowUIControls (bool bVisible); + + /** Return an event multiplexer. It is a single class that forwards + events from various sources. This method must not be called before + LateInit() has terminated. + */ + std::shared_ptr<tools::EventMultiplexer> const & GetEventMultiplexer() const; + + /** returns the complete area of the current view relative to the frame + window + */ + const ::tools::Rectangle& getClientRectangle() const; + + std::shared_ptr<ToolBarManager> const & GetToolBarManager() const; + std::shared_ptr<FormShellManager> const & GetFormShellManager() const; + + DrawController& GetDrawController() const; + + void SetViewTabBar (const ::rtl::Reference<ViewTabBar>& rViewTabBar); + + /** Return the window that is used by the main view shell to display its + view and other UI elements, like scroll bars and rulers. Ownership + of that window remains with the called ViewShellBase object. + */ + vcl::Window* GetViewWindow(); + + /** returns the ui descriptive name for the given uno slot. The result is taken from the configuration + and not cached, so do not use it excessive (f.e. in status updates) */ + OUString RetrieveLabelFromCommand( const OUString& aCmdURL ) const; + /// See SfxViewShell::getPart(). + int getPart() const override; + /// See SfxViewShell::NotifyCursor(). + void NotifyCursor(SfxViewShell* pViewShell) const override; + + void setLOKVisibleArea(const ::tools::Rectangle& rArea) { maLOKVisibleArea = rArea; } + virtual ::tools::Rectangle getLOKVisibleArea() const override { return maLOKVisibleArea; } + +protected: + + virtual void Notify(SfxBroadcaster& rBC, const SfxHint& rHint) override; + + virtual void InitializeFramework(); + +private: + class Implementation; + std::unique_ptr<Implementation> mpImpl; + DrawDocShell* mpDocShell; + SdDrawDocument* mpDocument; + ::tools::Rectangle maLOKVisibleArea; + + /** Determine from the properties of the document shell the initial type + of the view shell in the center pane. We use this method to avoid + starting with the wrong type. When ReadUserDataSequence() is called + we check that the right type is active and change again if that is + not the case because something went wrong. + */ + OUString GetInitialViewShellType() const; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/ViewShellHint.hxx b/sd/source/ui/inc/ViewShellHint.hxx new file mode 100644 index 000000000..05a0c8328 --- /dev/null +++ b/sd/source/ui/inc/ViewShellHint.hxx @@ -0,0 +1,57 @@ +/* -*- 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 . + */ + +#pragma once + +#include <svl/hint.hxx> + +namespace sd +{ +/** Local derivation of the SfxHint class that defines some hint ids that + are used by the ViewShell class and its descendants. +*/ +class ViewShellHint final : public SfxHint +{ +public: + enum HintId + { + // Indicate that a page resize is about to begin. + HINT_PAGE_RESIZE_START, + // Indicate that a page resize has been completed. + HINT_PAGE_RESIZE_END, + // Indicate that an edit mode change is about to begin. + HINT_CHANGE_EDIT_MODE_START, + // Indicate that an edit mode change has been completed. + HINT_CHANGE_EDIT_MODE_END, + + HINT_COMPLEX_MODEL_CHANGE_START, + HINT_COMPLEX_MODEL_CHANGE_END + }; + + ViewShellHint(HintId nHintId); + + HintId GetHintId() const { return meHintId; } + +private: + HintId meHintId; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/ViewShellImplementation.hxx b/sd/source/ui/inc/ViewShellImplementation.hxx new file mode 100644 index 000000000..b4a02c3d5 --- /dev/null +++ b/sd/source/ui/inc/ViewShellImplementation.hxx @@ -0,0 +1,150 @@ +/* -*- 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 . + */ + +#pragma once + +#include "ViewShell.hxx" +#include "ViewShellManager.hxx" +#include "ToolBarManager.hxx" +#include <o3tl/deleter.hxx> +#include <memory> + +class SvxIMapDlg; + +namespace sd +{ +/** This class contains (will contain) the implementation of methods that + have not be accessible from the outside. +*/ +class ViewShell::Implementation +{ +public: + bool mbIsMainViewShell; + /// Set to true when the ViewShell::Init() method has been called. + bool mbIsInitialized; + /** Set to true while ViewShell::ArrangeGUIElements() is being + executed. It is used as guard against recursive execution. + */ + bool mbArrangeActive; + + /** Remember a link to the sub shell factory, so that it can be + unregistered at the ViewShellManager when a ViewShell is deleted. + */ + ViewShellManager::SharedShellFactory mpSubShellFactory; + + /** This update lock for the ToolBarManager exists in order to avoid + problems with tool bars being displayed while the mouse button is + pressed. With docked tool bars this can lead to a size change of + the view. This would change the relative mouse coordinates and thus + interpret every mouse click as move command. + */ + class ToolBarManagerLock + { + public: + /** Create a new instance. This allows the mpSelf member to be set + automatically. + */ + static std::shared_ptr<ToolBarManagerLock> + Create(const std::shared_ptr<ToolBarManager>& rpManager); + /** Release the lock. When the UI is captured + (Application::IsUICaptured() returns <TRUE/>) then the lock is + released later asynchronously. + @param bForce + When this flag is <TRUE/> then the lock is released even + when IsUICaptured() returns <TRUE/>. + */ + void Release(bool bForce = false); + DECL_DLLPRIVATE_LINK(TimeoutCallback, Timer*, void); + + private: + ::std::unique_ptr<ToolBarManager::UpdateLock, + o3tl::default_delete<ToolBarManager::UpdateLock>> + mpLock; + /** The timer is used both as a safe guard to unlock the update lock + when Release() is not called explicitly. It is also used to + defer the release of the lock to a time when the UI is not + captured. + */ + Timer maTimer; + /** The shared_ptr to this allows the ToolBarManagerLock to control + its own lifetime. This, of course, does work only when no one + holds another shared_ptr longer than only temporary. + */ + std::shared_ptr<ToolBarManagerLock> mpSelf; + ToolBarManagerLock(const std::shared_ptr<sd::ToolBarManager>& rpManager); + ~ToolBarManagerLock(); + + class Deleter; + friend class Deleter; + }; + // The member is not a unique_ptr because it takes over its own life time + // control. + std::weak_ptr<ToolBarManagerLock> mpUpdateLockForMouse; + + Implementation(ViewShell& rViewShell); + ~Implementation() COVERITY_NOEXCEPT_FALSE; + + /** Process the SID_MODIFY slot. + */ + void ProcessModifyPageSlot(SfxRequest& rRequest, SdPage* pCurrentPage, PageKind ePageKind); + + /** Assign the given layout to the given page. This method is at the + moment merely a front end for ProcessModifyPageSlot. + @param pPage + If a NULL pointer is given then this call is ignored. + */ + void AssignLayout(SfxRequest const& rRequest, PageKind ePageKind); + + /** Determine the view id of the view shell. This corresponds to the + view id stored in the SfxViewFrame class. + + We can not use the view of that class because with the introduction + of the multi pane GUI we do not switch the SfxViewShell anymore when + switching the view in the center pane. The view id of the + SfxViewFrame is thus not modified and we can not set it from the + outside. + + The view id is still needed for the SFX to determine on start up + (e.g. after loading a document) which ViewShellBase sub class to + use. These sub classes--like OutlineViewShellBase--exist only to be + used by the SFX as factories. They only set the initial pane + configuration, nothing more. + + So what we do here in essence is to return one of the + ViewShellFactoryIds that can be used to select the factory that + creates the ViewShellBase subclass with the initial pane + configuration that has in the center pane a view shell of the same + type as mrViewShell. + */ + SfxInterfaceId GetViewId() const; + + /** Return a pointer to the image map dialog that is displayed in some + child window. + @return + Returns <NULL/> when the image map dialog is not available. + */ + static SvxIMapDlg* GetImageMapDialog(); + +private: + ViewShell& mrViewShell; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/ViewShellManager.hxx b/sd/source/ui/inc/ViewShellManager.hxx new file mode 100644 index 000000000..a2c8f1ef2 --- /dev/null +++ b/sd/source/ui/inc/ViewShellManager.hxx @@ -0,0 +1,195 @@ +/* -*- 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 . + */ + +#pragma once + +#include "ShellFactory.hxx" +#include <o3tl/deleter.hxx> +#include <memory> + +class FmFormShell; +class SfxShell; + +namespace sd +{ +class ViewShell; +class ViewShellBase; + +/** The ViewShellManager has the responsibility to manage the view shells + and sub shells on the SFX shell stack. They form a two level hierarchy + (the underlying ViewShellBase, the only true SfxViewShell descendant, + forms a third level.) On the first level there are the view shells + (what formerly was called view shell, anyway; nowadays they are derived + from SfxShell) and shells for panes. On the second level there are sub + shells (also derived from SfxShell) that usually are tool bars. + + <p>On the SFX shell stack the regular sub shells are placed above their + view shells. The FormShell is a special case. With the SetFormShell() + method it can be placed directly above or below one of the view + shells.</p> + + <p>Shells managed by this class are created by factories or are given + directly to Activate... methods. For the sub shells there is one + factory for every view shell. Factories are added or removed via the + (Add|Remove)SubShellFactory() methods. The FormShell is managed with the + factory of its view shell.</p> +*/ +class ViewShellManager +{ +public: + typedef std::shared_ptr<ShellFactory<SfxShell>> SharedShellFactory; + + ViewShellManager(ViewShellBase& rBase); + + /** Before the destructor is called the method Shutdown() has to have + been called. + */ + ~ViewShellManager(); + + /** Tell a ViewShellManager object to prepare to be deleted, i.e. to + destroy all of its resources and to ignore all following calls. + Use this when the owner of the view shell manager is about being + destroyed but the view shell manager itself can not yet be deleted. + */ + void Shutdown(); + + /** Set the factory for sub shells of the specified view shell. + */ + void AddSubShellFactory(ViewShell const* pViewShell, const SharedShellFactory& rpFactory); + void RemoveSubShellFactory(ViewShell const* pViewShell, const SharedShellFactory& rpFactory); + + /** Activate the given view shell. + */ + void ActivateViewShell(ViewShell* pViewShell); + + /** Activate the given shell which is not a view shell. For view shells + use the ActivateViewShell() method. + */ + void ActivateShell(SfxShell* pShell); + + /** Deactivate the specified shell, i.e. take it and all of its + object bars from the shell stack. + @param pShell + The shell to deactivate. + */ + void DeactivateViewShell(const ViewShell* pShell); + + /** Deactivate the specified shell. The shell is not destroyed. + */ + void DeactivateShell(const SfxShell* pShell); + + /** Associate the form shell with a view shell and their relative + position. This method does not change the shell stack, it just + stores the given values for the next shell stack update. + @param pParentShell + The view shell of the form shell. + @param pFormShell + The form shell. + @param bAbove + When <TRUE/> then the form shell will be placed directly above + pViewShell on the SFX shell stack. Otherwise the form shell is + placed directly below the view shell. + */ + void SetFormShell(const ViewShell* pParentShell, FmFormShell* pFormShell, bool bAbove); + + /** Activate the specified shell as sub shell for the given view shell. + The sub shell factory associated with the view shell is used to + create the sub shell. + @param rParentShell + The new sub shell will be placed above this view shell. + @param nId + This id is used only with the factory registered for the parent + view shell. + */ + void ActivateSubShell(const ViewShell& rParentShell, ShellId nId); + + /** Deactivate the specified sub shell. + */ + void DeactivateSubShell(const ViewShell& rParentShell, ShellId nId); + + /** Send all sub shells of the specified view shell an Invalidate() + call. This does not modify the shell stack. + */ + void InvalidateAllSubShells(ViewShell const* pViewShell); + + /** Move the specified view shell to the top most position on the stack + of view shells in relation to the other view shells. After this the + only shells that are higher on the stack are its object bars. + + Call this method after a focus change to bring a view mode view + shell and is associated tool bar shells to the top of the + stack. + + The mbKeepMainViewShellOnTop flag is not obeyed. + + @param nId + The id of the shell to move to the top. + */ + void MoveToTop(const ViewShell& rShell); + + /** Return the first, i.e. top most, view shell that has been activated + under the given id. + @param nId + The id of the shell for which to return a pointer. + @return + When the specified shell is currently not active then NULL is + returned. + */ + SfxShell* GetShell(ShellId nId) const; + + /** Return the top-most shell on the SFX shell stack regardless of + whether that is a view shell or a sub shell. + */ + SfxShell* GetTopShell() const; + + /** Return the top-most active view shell on the internal shell stack. + */ + SfxShell* GetTopViewShell() const; + + /** Use this class to safely lock updates of the view shell stack. + */ + class UpdateLock + { + public: + UpdateLock(const std::shared_ptr<ViewShellManager>& rpManager) + : mpManager(rpManager) + { + mpManager->LockUpdate(); + } + ~UpdateLock() COVERITY_NOEXCEPT_FALSE { mpManager->UnlockUpdate(); } + + private: + std::shared_ptr<ViewShellManager> mpManager; + }; + friend class UpdateLock; + +private: + class Implementation; + std::unique_ptr<ViewShellManager::Implementation, + o3tl::default_delete<ViewShellManager::Implementation>> + mpImpl; + bool mbValid; + + void LockUpdate(); + void UnlockUpdate(); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/ViewTabBar.hxx b/sd/source/ui/inc/ViewTabBar.hxx new file mode 100644 index 000000000..ca9db932e --- /dev/null +++ b/sd/source/ui/inc/ViewTabBar.hxx @@ -0,0 +1,184 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/drawing/framework/TabBarButton.hpp> +#include <com/sun/star/drawing/framework/XTabBar.hpp> +#include <com/sun/star/drawing/framework/XToolBar.hpp> +#include <com/sun/star/drawing/framework/XConfigurationChangeListener.hpp> +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <comphelper/compbase.hxx> +#include <vcl/InterimItemWindow.hxx> + +#include <vector> + +namespace com::sun::star::drawing::framework { class XConfigurationController; } +namespace com::sun::star::drawing::framework { class XResourceId; } +namespace com::sun::star::drawing::framework { struct ConfigurationChangeEvent; } +namespace com::sun::star::frame { class XController; } +namespace vcl { class Window; } + +namespace sd { + class ViewShellBase; + class ViewTabBar; +} + +namespace sd { + +class TabBarControl final : public InterimItemWindow +{ +public: + TabBarControl(vcl::Window* pParentWindow, const ::rtl::Reference<ViewTabBar>& rpViewTabBar); + virtual void dispose() override; + virtual ~TabBarControl() override; + weld::Notebook& GetNotebook() { return *mxTabControl; } + int GetAllocatedWidth() const { return mnAllocatedWidth; } +private: + std::unique_ptr<weld::Notebook> mxTabControl; + ::rtl::Reference<ViewTabBar> mpViewTabBar; + int mnAllocatedWidth; + + DECL_LINK(ActivatePageHdl, const OString&, void); + DECL_LINK(NotebookSizeAllocHdl, const Size&, void); +}; + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XToolBar, + css::drawing::framework::XTabBar, + css::drawing::framework::XConfigurationChangeListener, + css::lang::XUnoTunnel + > ViewTabBarInterfaceBase; + +/** Tab control for switching between views in the center pane. +*/ +class ViewTabBar final + : public ViewTabBarInterfaceBase +{ +public: + ViewTabBar ( + const css::uno::Reference< css::drawing::framework::XResourceId>& rxViewTabBarId, + const css::uno::Reference< css::frame::XController>& rxController); + virtual ~ViewTabBar() override; + + virtual void disposing(std::unique_lock<std::mutex>&) override; + + const VclPtr<TabBarControl>& GetTabControl() const { return mpTabControl; } + + bool ActivatePage(size_t nIndex); + + //----- drawing::framework::XConfigurationChangeListener ------------------ + + virtual void SAL_CALL + notifyConfigurationChange ( + const css::drawing::framework::ConfigurationChangeEvent& rEvent) override; + + //----- XEventListener ---------------------------------------------------- + + virtual void SAL_CALL disposing( + const css::lang::EventObject& rEvent) override; + + //----- XTabBar ----------------------------------------------------------- + + virtual void + SAL_CALL addTabBarButtonAfter ( + const css::drawing::framework::TabBarButton& rButton, + const css::drawing::framework::TabBarButton& rAnchor) override; + + virtual void + SAL_CALL appendTabBarButton ( + const css::drawing::framework::TabBarButton& rButton) override; + + virtual void + SAL_CALL removeTabBarButton ( + const css::drawing::framework::TabBarButton& rButton) override; + + virtual sal_Bool + SAL_CALL hasTabBarButton ( + const css::drawing::framework::TabBarButton& rButton) override; + + virtual css::uno::Sequence<css::drawing::framework::TabBarButton> + SAL_CALL getTabBarButtons() override; + + //----- XResource --------------------------------------------------------- + + virtual css::uno::Reference< + css::drawing::framework::XResourceId> SAL_CALL getResourceId() override; + + virtual sal_Bool SAL_CALL isAnchorOnly() override; + + //----- XUnoTunnel -------------------------------------------------------- + + static const css::uno::Sequence<sal_Int8>& getUnoTunnelId(); + + virtual sal_Int64 SAL_CALL getSomething (const css::uno::Sequence<sal_Int8>& rId) override; + + /** The returned value is calculated as the difference between the + total height of the control and the height of its first tab page. + This can be considered a hack. + This procedure works only when the control is visible. Calling this + method when the control is not visible results in returning a + default value. + To be on the safe side wait for this control to become visible and + the call this method again. + */ + int GetHeight() const; + + void UpdateActiveButton(); + + void AddTabBarButton ( + const css::drawing::framework::TabBarButton& rButton, + const css::drawing::framework::TabBarButton& rAnchor); + void AddTabBarButton ( + const css::drawing::framework::TabBarButton& rButton); + void RemoveTabBarButton ( + const css::drawing::framework::TabBarButton& rButton); + bool HasTabBarButton ( + const css::drawing::framework::TabBarButton& rButton); + css::uno::Sequence<css::drawing::framework::TabBarButton> + GetTabBarButtons(); + +private: + VclPtr<TabBarControl> mpTabControl; + css::uno::Reference<css::frame::XController> mxController; + css::uno::Reference<css::drawing::framework::XConfigurationController> mxConfigurationController; + typedef ::std::vector<css::drawing::framework::TabBarButton> TabBarButtonList; + TabBarButtonList maTabBarButtons; + css::uno::Reference<css::drawing::framework::XResourceId> mxViewTabBarId; + ViewShellBase* mpViewShellBase; + int mnNoteBookWidthPadding; + + void AddTabBarButton ( + const css::drawing::framework::TabBarButton& rButton, + sal_Int32 nPosition); + void UpdateTabBarButtons(); + + /** This method is called from the constructor to get the window for an + anchor ResourceId and pass it to our base class. It has to be + static because it must not access any of the, not yet initialized + members. + */ + static vcl::Window* GetAnchorWindow( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxViewTabBarId, + const css::uno::Reference<css::frame::XController>& rxController); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/Window.hxx b/sd/source/ui/inc/Window.hxx new file mode 100644 index 000000000..f1beddbc6 --- /dev/null +++ b/sd/source/ui/inc/Window.hxx @@ -0,0 +1,213 @@ +/* -*- 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 . + */ + +#pragma once + +#include <tools/gen.hxx> +#include <tools/long.hxx> +#include <vcl/window.hxx> +#include <vcl/transfer.hxx> + +class OutlinerView; + +namespace sd +{ +class ViewShell; + +/** An SdWindow contains the actual working area of ViewShell. + + <p>The zoom factor used by this class controls how much the page and the + shapes on it are scaled down (<100%) or up (>100%) when displayed on the + output device represented by the <type>OutputDevice</type>base class. A + zoom factor of 100% would result (with a correctly set DPI value for an + output device) in a one to one mapping of the internal coordinates that + are stored in 100th of mm. The zoom factor is stored in the map mode + member of the <type>OutputDevice</type> base class. It is calculated to + be an integer percent value. +*/ +class Window : public vcl::Window, public ::DropTargetHelper +{ +public: + Window(vcl::Window* pParent); + virtual ~Window() override; + virtual void dispose() override; + + void SetViewShell(ViewShell* pViewSh); + ViewShell* GetViewShell(); + + /** Set the zoom factor to the specified value and center the display + area around the zoom center. + @param nZoom + The zoom factor is given as integral percent value. + */ + void SetZoomIntegral(::tools::Long nZoom); + + /** This internally used method performs the actual adaptation of the + window's map mode to the specified zoom factor. + @param nZoom + The zoom factor is given as integral percent value. + @return + When the given zoom factor lies outside the valid range enclosed + by the minimal zoom factor previously calculated by + <member>CalcMinZoom</member> and a constant upper value it is + forced into that interval. Therefore the returned value is a + valid zoom factor. + */ + ::tools::Long SetZoomFactor(::tools::Long nZoom); + + /** This method is called when the whole page shall be displayed in the + window. Position and zoom factor are set so that the given + rectangle is displayed as large as possible in the window while at + the same time maintaining the rectangle's aspect ratio and adding a + small space at all its four sides (about 3% of width and height). + The map mode is adapted accordingly. + @param rZoomRect + The rectangle is expected to be given relative to the upper left + corner of the window in logical coordinates (100th of mm). + @return + The new zoom factor is returned as integral percent value. + */ + ::tools::Long SetZoomRect(const ::tools::Rectangle& rZoomRect); + + ::tools::Long GetZoomForRect(const ::tools::Rectangle& rZoomRect); + + void SetMinZoomAutoCalc(bool bAuto); + + /** Calculate the minimal zoom factor as the value at which the + application area would completely fill the window. All values set + manually or programmatically are set to this value if they are + smaller. If the currently used zoom factor is smaller than the minimal zoom + factor than set the minimal zoom factor as the new current zoom + factor. + + <p>This calculation is performed only when the + <member>bMinZoomAutoCalc</member> is set (to <TRUE/>).</p> + */ + void CalcMinZoom(); + void SetMinZoom(::tools::Long nMin); + ::tools::Long GetMinZoom() const { return mnMinZoom; } + void SetMaxZoom(::tools::Long nMax); + ::tools::Long GetMaxZoom() const { return mnMaxZoom; } + + ::tools::Long GetZoom() const; + + const Point& GetWinViewPos() const { return maWinPos; } + const Point& GetViewOrigin() const { return maViewOrigin; } + const Size& GetViewSize() const { return maViewSize; } + void SetWinViewPos(const Point& rPnt); + void SetViewOrigin(const Point& rPnt); + void SetViewSize(const Size& rSize); + void SetCenterAllowed(bool bIsAllowed); + + /** Calculate origin of the map mode according to the size of the view + and window (its size in model coordinates; that takes the zoom + factor into account), and the bCenterAllowed flag. When it is not + set then nothing is changed. When in any direction the window is + larger than the view or the value of aWinPos in this direction is -1 + then the window is centered in this direction. + */ + void UpdateMapOrigin(bool bInvalidate = true); + + void UpdateMapMode(); + + double GetVisibleX() const; // interface for ScrollBars + double GetVisibleY() const; + void SetVisibleXY(double fX, double fY); + double GetVisibleWidth() const; + double GetVisibleHeight() const; + Point GetVisibleCenter(); + double GetScrlLineWidth() const; + double GetScrlLineHeight() const; + double GetScrlPageWidth() const; + double GetScrlPageHeight() const; + void GrabFocus(); + virtual void DataChanged(const DataChangedEvent& rDCEvt) override; + + // DropTargetHelper + virtual sal_Int8 AcceptDrop(const AcceptDropEvent& rEvt) override; + virtual sal_Int8 ExecuteDrop(const ExecuteDropEvent& rEvt) override; + + /** The DropScroll() method is used by AcceptDrop() to scroll the + content of the window while dragging and dropping. With this method + you can control whether DropScroll() shall be used. + */ + void SetUseDropScroll(bool bUseDropScroll); + void DropScroll(const Point& rMousePos); + virtual void KeyInput(const KeyEvent& rKEvt) override; + +private: + OutlinerView* GetOutlinerView() const; + +protected: + Point maWinPos; + Point maViewOrigin; + Size maViewSize; + Size maPrevSize; // contains previous window size in logical coords + sal_uInt16 mnMinZoom; + sal_uInt16 mnMaxZoom; + + /** This flag tells whether to re-calculate the minimal zoom factor + depending on the current zoom factor. Its default value is now false. + */ + bool mbMinZoomAutoCalc; + bool mbCenterAllowed; + ::tools::Long mnTicks; + + ViewShell* mpViewShell; + bool mbUseDropScroll; + + virtual void Resize() override; + virtual void PrePaint(vcl::RenderContext& rRenderContext) override; + virtual void Paint(vcl::RenderContext& rRenderContext, + const ::tools::Rectangle& rRect) override; + virtual void MouseMove(const MouseEvent& rMEvt) override; + virtual void MouseButtonUp(const MouseEvent& rMEvt) override; + virtual void MouseButtonDown(const MouseEvent& rMEvt) override; + virtual void Command(const CommandEvent& rCEvt) override; + virtual void RequestHelp(const HelpEvent& rEvt) override; + virtual void LoseFocus() override; + virtual bool EventNotify(NotifyEvent& rNEvt) override; + + /** Create an accessibility object that makes this window accessible. + + @return + The returned reference is empty if an accessible object could + not be created. + */ + virtual css::uno::Reference<css::accessibility::XAccessible> CreateAccessible() override; + + OUString GetSurroundingText() const override; + Selection GetSurroundingTextSelection() const override; + bool DeleteSurroundingText(const Selection& rSelection) override; + + /// @see Window::LogicInvalidate(). + void LogicInvalidate(const ::tools::Rectangle* pRectangle) override; + /// Same as MouseButtonDown(), but coordinates are in logic unit. + virtual void LogicMouseButtonDown(const MouseEvent& rMouseEvent) override; + /// Same as MouseButtonUp(), but coordinates are in logic unit. + virtual void LogicMouseButtonUp(const MouseEvent& rMouseEvent) override; + /// Same as MouseMove(), but coordinates are in logic unit. + virtual void LogicMouseMove(const MouseEvent& rMouseEvent) override; + + FactoryFunction GetUITestFactory() const override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/WindowUpdater.hxx b/sd/source/ui/inc/WindowUpdater.hxx new file mode 100644 index 000000000..2545af79f --- /dev/null +++ b/sd/source/ui/inc/WindowUpdater.hxx @@ -0,0 +1,124 @@ +/* -*- 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 . + */ + +#pragma once + +#include <svl/ctloptions.hxx> +#include <vcl/vclptr.hxx> + +#include <vector> + +namespace vcl +{ +class Window; +} +class OutputDevice; +class SdDrawDocument; + +namespace sd +{ +/** The purpose of the <type>WindowUpdater</type> is to update output + devices to take care of modified global values. These values are + monitored for changes. At the moment this is + the digit language that defines the glyphs to use to render digits. + Other values may be added in the future. + + <p>The methods of this class have not been included into the + <type>ViewShell</type> class in order to not clutter its interface any + further. This class accesses some of <type>ViewShell</type> data + members directly and thus is declared as its friend.</p> + + <p>Windows that are to be kept up-to-date have to be registered via the + <member>RegisterWindow()</member> method. When a document is given then + this document is reformatted when the monitored option changes.</p> +*/ +class WindowUpdater final : public utl::ConfigurationListener +{ +public: + explicit WindowUpdater(); + virtual ~WindowUpdater() noexcept override; + + /** Add the given device to the list of devices which will be updated + when one of the monitored values changes. + @param pWindow + This device is added to the device list if it is not <null/> and + when it is not already a member of that list. + */ + void RegisterWindow(vcl::Window* pWindow); + + /** Remove the given device from the list of devices which will be updated + when one of the monitored values changes. + @param pWindow + This device is removed from the device list when it is a member + of that list. + */ + void UnregisterWindow(vcl::Window* pWindow); + + /** Set the document so that it is reformatted when one of the monitored + values changes. + @param pDocument + When <null/> is given document reformatting will not take + place in the future. + */ + void SetDocument(SdDrawDocument* pDocument); + + /** Update the given output device and update all text objects of the + view shell if not told otherwise. + @param pWindow + The device to update. When the given pointer is NULL then + nothing is done. + */ + void Update(OutputDevice* pDevice) const; + + /** Callback that waits for notifications of a + <type>SvtCTLOptions</type> object. + */ + virtual void ConfigurationChanged(utl::ConfigurationBroadcaster*, + ConfigurationHints nHint) override; + +private: + /// Options to monitor for changes. + SvtCTLOptions maCTLOptions; + + /// The document rendered in the output devices. + SdDrawDocument* mpDocument; + + WindowUpdater(const WindowUpdater& rUpdater) = delete; + + WindowUpdater operator=(const WindowUpdater& rUpdater) = delete; + + /** Type and data member for a list of devices that have to be kept + up-to-date. + */ + typedef ::std::vector<VclPtr<vcl::Window>> tWindowList; + tWindowList maWindowList; + + /** The central method of this class. Update the given output device. + It is the task of the caller to initiate a reformatting of the + document that is rendered on this device to reflect the changes. + @param pWindow + The output device to update. When it is <null/> then the call + is ignored. + */ + void UpdateWindow(OutputDevice* pDevice) const; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/animobjs.hxx b/sd/source/ui/inc/animobjs.hxx new file mode 100644 index 000000000..b44a5fb3d --- /dev/null +++ b/sd/source/ui/inc/animobjs.hxx @@ -0,0 +1,163 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/dockwin.hxx> +#include <tools/fract.hxx> +#include <sfx2/ctrlitem.hxx> +#include <sfx2/progress.hxx> +#include <misc/scopelock.hxx> +#include <vcl/bitmapex.hxx> +#include <vcl/customweld.hxx> +#include <vcl/weld.hxx> +#include <vcl/weldutils.hxx> + +class SdDrawDocument; + +namespace sd { + +class AnimationControllerItem; +class View; + +enum BitmapAdjustment +{ + BA_LEFT_UP, + BA_LEFT, + BA_LEFT_DOWN, + BA_UP, + BA_CENTER, + BA_DOWN, + BA_RIGHT_UP, + BA_RIGHT, + BA_RIGHT_DOWN +}; + +class SdDisplay : public weld::CustomWidgetController +{ +private: + BitmapEx aBitmapEx; + Fraction aScale; + +public: + SdDisplay(); + virtual ~SdDisplay() override; + + virtual void Paint( vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect ) override; + + void SetBitmapEx( BitmapEx const * pBmpEx ); + void SetScale( const Fraction& rFrac ); + + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override; +}; + +class AnimationWindow : public SfxDockingWindow +{ + friend class AnimationChildWindow; + friend class AnimationControllerItem; + +public: + AnimationWindow(SfxBindings* pBindings, SfxChildWindow *pCW, vcl::Window* pParent); + virtual ~AnimationWindow() override; + virtual void dispose() override; + + void AddObj( ::sd::View& rView ); + void CreateAnimObj( ::sd::View& rView ); + + virtual void DataChanged( const DataChangedEvent& rDCEvt ) override; + +protected: + virtual bool Close() override; + virtual void Resize() override; + +private: + std::unique_ptr<SdDisplay> m_xCtlDisplay; + std::unique_ptr<weld::CustomWeld> m_xCtlDisplayWin; + std::unique_ptr<weld::Button> m_xBtnFirst; + std::unique_ptr<weld::Button> m_xBtnReverse; + std::unique_ptr<weld::Button> m_xBtnStop; + std::unique_ptr<weld::Button> m_xBtnPlay; + std::unique_ptr<weld::Button> m_xBtnLast; + std::unique_ptr<weld::SpinButton> m_xNumFldBitmap; + std::unique_ptr<weld::FormattedSpinButton> m_xTimeField; + std::unique_ptr<weld::TimeFormatter> m_xFormatter; + std::unique_ptr<weld::ComboBox> m_xLbLoopCount; + std::unique_ptr<weld::Button> m_xBtnGetOneObject; + std::unique_ptr<weld::Button> m_xBtnGetAllObjects; + std::unique_ptr<weld::Button> m_xBtnRemoveBitmap; + std::unique_ptr<weld::Button> m_xBtnRemoveAll; + std::unique_ptr<weld::Label> m_xFiCount; + + std::unique_ptr<weld::RadioButton> m_xRbtGroup; + std::unique_ptr<weld::RadioButton> m_xRbtBitmap; + std::unique_ptr<weld::Label> m_xFtAdjustment; + std::unique_ptr<weld::ComboBox> m_xLbAdjustment; + std::unique_ptr<weld::Button> m_xBtnCreateGroup; + std::unique_ptr<weld::Button> m_xBtnHelp; + + ::std::vector< ::std::pair<BitmapEx, ::tools::Time> > m_FrameList; + static const size_t EMPTY_FRAMELIST; + size_t m_nCurrentFrame; + std::unique_ptr<SdDrawDocument> pMyDoc; + + bool bMovie; + bool bAllObjects; + + std::unique_ptr<AnimationControllerItem> pControllerItem; + + ScopeLock maPlayLock; + + DECL_LINK( ClickFirstHdl, weld::Button&, void ); + DECL_LINK( ClickStopHdl, weld::Button&, void ); + DECL_LINK( ClickPlayHdl, weld::Button&, void ); + DECL_LINK( ClickLastHdl, weld::Button&, void ); + DECL_LINK( ClickGetObjectHdl, weld::Button&, void ); + DECL_LINK( ClickRemoveBitmapHdl, weld::Button&, void ); + DECL_LINK( ClickRbtHdl, weld::Toggleable&, void ); + DECL_LINK( ClickHelpHdl, weld::Button&, void ); + DECL_LINK( ClickCreateGroupHdl, weld::Button&, void ); + DECL_LINK( ModifyBitmapHdl, weld::SpinButton&, void ); + DECL_LINK( ModifyTimeHdl, weld::FormattedSpinButton&, void ); + + void UpdateControl(bool bDisableCtrls = false); + void ResetAttrs(); + void WaitInEffect( sal_uLong nMilliSeconds, sal_uLong nTime, + SfxProgress* pStbMgr ) const; + Fraction GetScale(); +}; + +/** + * ControllerItem for Animator + */ +class AnimationControllerItem : public SfxControllerItem +{ + +public: + AnimationControllerItem( sal_uInt16, AnimationWindow*, SfxBindings* ); + +protected: + virtual void StateChangedAtToolBoxControl( sal_uInt16 nSId, SfxItemState eState, + const SfxPoolItem* pState ) override; +private: + VclPtr<AnimationWindow> pAnimationWin; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/annotationmanager.hxx b/sd/source/ui/inc/annotationmanager.hxx new file mode 100644 index 000000000..6f47efd1f --- /dev/null +++ b/sd/source/ui/inc/annotationmanager.hxx @@ -0,0 +1,46 @@ +/* -*- 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 . + */ + +#pragma once + +#include <rtl/ref.hxx> + +class SfxRequest; +class SfxItemSet; + +namespace sd +{ +class ViewShellBase; +class AnnotationManagerImpl; + +class AnnotationManager +{ +public: + AnnotationManager(ViewShellBase& rViewShellBase); + ~AnnotationManager(); + + void ExecuteAnnotation(SfxRequest const& rRequest); + void GetAnnotationState(SfxItemSet& rItemSet); + +private: + ::rtl::Reference<AnnotationManagerImpl> mxImpl; +}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/assclass.hxx b/sd/source/ui/inc/assclass.hxx new file mode 100644 index 000000000..2b366e971 --- /dev/null +++ b/sd/source/ui/inc/assclass.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 . + */ + +#pragma once + +#include <memory> +#include <vector> + +#include <sddllapi.h> + +#define MAX_PAGES 10 + +namespace weld { class Widget; } + +class SD_DLLPUBLIC Assistent +{ + /** contains for every page the controls, which have to be + connected? correctly */ + std::vector<weld::Widget*> maPages[MAX_PAGES]; + + /// number of pages + int mnPages; + + int mnCurrentPage; + + std::unique_ptr<bool[]> mpPageStatus; + +public: + + Assistent(int nNoOfPage); + + bool IsEnabled ( int nPage ) const; + void EnablePage( int nPage ); + void DisablePage( int nPage ); + + /// adds a control to the specified page + bool InsertControl(int nDestPage, weld::Widget* pUsedControl); + + void NextPage(); + + void PreviousPage(); + + bool GotoPage(const int nPageToGo); + + bool IsLastPage() const; + + bool IsFirstPage() const; + + int GetCurrentPage() const { return mnCurrentPage;} +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/bulmaper.hxx b/sd/source/ui/inc/bulmaper.hxx new file mode 100644 index 000000000..3de99d262 --- /dev/null +++ b/sd/source/ui/inc/bulmaper.hxx @@ -0,0 +1,37 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sddllapi.h> + +class SfxItemSet; +class SvxNumRule; + +class SD_DLLPUBLIC SdBulletMapper +{ +public: + /* #i35937# + static void PreMapNumBulletForDialog( SfxItemSet& rSet ); + static void PostMapNumBulletForDialog( SfxItemSet& rSet ); +*/ + static void MapFontsInNumRule(SvxNumRule& aNumRule, const SfxItemSet& rSet); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/copydlg.hxx b/sd/source/ui/inc/copydlg.hxx new file mode 100644 index 000000000..7d8274743 --- /dev/null +++ b/sd/source/ui/inc/copydlg.hxx @@ -0,0 +1,67 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/basedlgs.hxx> +#include <tools/fract.hxx> + +class ColorListBox; + +namespace sd +{ +class View; + +/** + * dialog to adjust screen + */ +class CopyDlg : public SfxDialogController +{ +public: + CopyDlg(weld::Window* pWindow, const SfxItemSet& rInAttrs, ::sd::View* pView); + virtual ~CopyDlg() override; + + void GetAttr(SfxItemSet& rOutAttrs); + void Reset(); + +private: + const SfxItemSet& mrOutAttrs; + Fraction maUIScale; + ::sd::View* mpView; + + std::unique_ptr<weld::SpinButton> m_xNumFldCopies; + std::unique_ptr<weld::Button> m_xBtnSetViewData; + std::unique_ptr<weld::MetricSpinButton> m_xMtrFldMoveX; + std::unique_ptr<weld::MetricSpinButton> m_xMtrFldMoveY; + std::unique_ptr<weld::MetricSpinButton> m_xMtrFldAngle; + std::unique_ptr<weld::MetricSpinButton> m_xMtrFldWidth; + std::unique_ptr<weld::MetricSpinButton> m_xMtrFldHeight; + std::unique_ptr<weld::Label> m_xFtEndColor; + std::unique_ptr<weld::Button> m_xBtnSetDefault; + std::unique_ptr<ColorListBox> m_xLbStartColor; + std::unique_ptr<ColorListBox> m_xLbEndColor; + + DECL_LINK(SelectColorHdl, ColorListBox&, void); + DECL_LINK(SetViewData, weld::Button&, void); + DECL_LINK(SetDefault, weld::Button&, void); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/createtableobjectbar.hxx b/sd/source/ui/inc/createtableobjectbar.hxx new file mode 100644 index 000000000..8fc21f19f --- /dev/null +++ b/sd/source/ui/inc/createtableobjectbar.hxx @@ -0,0 +1,37 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +class SfxShell; + +namespace sd +{ +class View; +class ViewShell; +} + +namespace sd::ui::table +{ +SfxShell* CreateTableObjectBar(ViewShell& rShell, ::sd::View* pView); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/custsdlg.hxx b/sd/source/ui/inc/custsdlg.hxx new file mode 100644 index 000000000..52ae87852 --- /dev/null +++ b/sd/source/ui/inc/custsdlg.hxx @@ -0,0 +1,91 @@ +/* -*- 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 . + */ + +#pragma once + +#include <vcl/weld.hxx> +#include <vcl/weldutils.hxx> + +class SdDrawDocument; +class SdCustomShow; +class SdCustomShowList; + +class SdCustomShowDlg : public weld::GenericDialogController +{ +private: + SdDrawDocument& rDoc; + SdCustomShowList* pCustomShowList; + + std::unique_ptr<weld::TreeView> m_xLbCustomShows; + std::unique_ptr<weld::Button> m_xBtnNew; + std::unique_ptr<weld::Button> m_xBtnEdit; + std::unique_ptr<weld::Button> m_xBtnRemove; + std::unique_ptr<weld::Button> m_xBtnCopy; + std::unique_ptr<weld::Button> m_xBtnHelp; + std::unique_ptr<weld::Button> m_xBtnStartShow; + std::unique_ptr<weld::Button> m_xBtnOK; + + void CheckState(); + + DECL_LINK( ClickButtonHdl, weld::Button&, void ); + DECL_LINK( SelectListBoxHdl, weld::TreeView&, void ); + DECL_LINK( StartShowHdl, weld::Button&, void ); + void SelectHdl(void const *); + +public: + SdCustomShowDlg(weld::Window* pWindow, SdDrawDocument& rDrawDoc); + virtual ~SdCustomShowDlg() override; + bool IsCustomShow() const; +}; + +class SdDefineCustomShowDlg : public weld::GenericDialogController +{ +private: + SdDrawDocument& rDoc; + std::unique_ptr<SdCustomShow>& rpCustomShow; + bool bModified; + OUString aOldName; + + std::unique_ptr<weld::Entry> m_xEdtName; + std::unique_ptr<weld::TreeView> m_xLbPages; + std::unique_ptr<weld::Button> m_xBtnAdd; + std::unique_ptr<weld::Button> m_xBtnRemove; + std::unique_ptr<weld::TreeView> m_xLbCustomPages; + std::unique_ptr<weld::ReorderingDropTarget> m_xDropTargetHelper; + std::unique_ptr<weld::Button> m_xBtnOK; + std::unique_ptr<weld::Button> m_xBtnCancel; + std::unique_ptr<weld::Button> m_xBtnHelp; + + void CheckState(); + void CheckCustomShow(); + + DECL_LINK( ClickButtonHdl, weld::Button&, void ); + DECL_LINK( ClickButtonEditHdl, weld::Entry&, void ); + DECL_LINK( ClickButtonHdl3, weld::TreeView&, void ); + DECL_LINK( ClickButtonHdl4, weld::TreeView&, void ); + DECL_LINK( OKHdl, weld::Button&, void ); + void ClickButtonHdl2(void const *); + +public: + + SdDefineCustomShowDlg(weld::Window* pWindow, SdDrawDocument& rDrawDoc, std::unique_ptr<SdCustomShow>& rpCS); + virtual ~SdDefineCustomShowDlg() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/diactrl.hxx b/sd/source/ui/inc/diactrl.hxx new file mode 100644 index 000000000..12e76762e --- /dev/null +++ b/sd/source/ui/inc/diactrl.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 . + */ + +#pragma once + +#include <vcl/InterimItemWindow.hxx> +#include <sfx2/tbxctrl.hxx> + +namespace com::sun::star::frame +{ +class XFrame; +} +class SfxUInt16Item; + +// SdPagesField: + +class SdPagesField final : public InterimItemWindow +{ +private: + std::unique_ptr<weld::SpinButton> m_xWidget; + css::uno::Reference<css::frame::XFrame> m_xFrame; + + DECL_LINK(ModifyHdl, weld::SpinButton&, void); + DECL_STATIC_LINK(SdPagesField, OutputHdl, weld::SpinButton&, void); + DECL_LINK(spin_button_input, int* result, bool); + DECL_LINK(KeyInputHdl, const KeyEvent&, bool); + +public: + SdPagesField(vcl::Window* pParent, const css::uno::Reference<css::frame::XFrame>& rFrame); + virtual void dispose() override; + void set_sensitive(bool bSensitive); + virtual ~SdPagesField() override; + + void UpdatePagesField(const SfxUInt16Item* pItem); +}; + +// SdTbxCtlDiaPages: + +class SdTbxCtlDiaPages : public SfxToolBoxControl +{ +public: + virtual void StateChangedAtToolBoxControl(sal_uInt16 nSID, SfxItemState eState, + const SfxPoolItem* pState) override; + virtual VclPtr<InterimItemWindow> CreateItemWindow(vcl::Window* pParent) override; + + SFX_DECL_TOOLBOX_CONTROL(); + + SdTbxCtlDiaPages(sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx); + virtual ~SdTbxCtlDiaPages() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/dlg_char.hxx b/sd/source/ui/inc/dlg_char.hxx new file mode 100644 index 000000000..36e791a09 --- /dev/null +++ b/sd/source/ui/inc/dlg_char.hxx @@ -0,0 +1,41 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/tabdlg.hxx> + +class SfxItemSet; +class SfxObjectShell; + +/** + * Character-Tab-Dialog + */ +class SdCharDlg : public SfxTabDialogController +{ +private: + const SfxObjectShell& rDocShell; + + virtual void PageCreated(const OString& rId, SfxTabPage& rPage) override; + +public: + SdCharDlg(weld::Window* pParent, const SfxItemSet* pAttr, const SfxObjectShell* pDocShell); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/dlgfield.hxx b/sd/source/ui/inc/dlgfield.hxx new file mode 100644 index 000000000..769dc12c7 --- /dev/null +++ b/sd/source/ui/inc/dlgfield.hxx @@ -0,0 +1,56 @@ +/* -*- 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 . + */ + +#pragma once + +#include <vcl/weld.hxx> +#include <memory> +#include <svl/itemset.hxx> + +class SvxFieldData; +class SvxLanguageBox; + +/** + * dialog to adjust field-commands + */ +class SdModifyFieldDlg : public weld::GenericDialogController +{ +private: + SfxItemSet m_aInputSet; + const SvxFieldData* m_pField; + + std::unique_ptr<weld::RadioButton> m_xRbtFix; + std::unique_ptr<weld::RadioButton> m_xRbtVar; + std::unique_ptr<SvxLanguageBox> m_xLbLanguage; + std::unique_ptr<weld::ComboBox> m_xLbFormat; + + void FillFormatList(); + void FillControls(); + + DECL_LINK(LanguageChangeHdl, weld::ComboBox&, void); + +public: + SdModifyFieldDlg(weld::Window* pWindow, const SvxFieldData* pInField, const SfxItemSet& rSet); + virtual ~SdModifyFieldDlg() override; + + SvxFieldData* GetField(); + SfxItemSet GetItemSet() const; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/dlgpage.hxx b/sd/source/ui/inc/dlgpage.hxx new file mode 100644 index 000000000..718ccf0c6 --- /dev/null +++ b/sd/source/ui/inc/dlgpage.hxx @@ -0,0 +1,48 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/tabdlg.hxx> +#include <svx/xtable.hxx> + +class SfxObjectShell; +enum class ChangeType; + +/** + * Page configuration-tab-dialog + */ +class SdPageDlg : public SfxTabDialogController +{ +private: + bool mbIsImpressDoc; + + XColorListRef mpColorList; + XGradientListRef mpGradientList; + XHatchListRef mpHatchingList; + XBitmapListRef mpBitmapList; + XPatternListRef mpPatternList; +public: + + SdPageDlg(SfxObjectShell const * pDocSh, weld::Window* pParent, const SfxItemSet* pAttr, bool bAreaPage, bool bIsImpressDoc, bool bIsImpressMaster); + + virtual void PageCreated(const OString& rId, SfxTabPage& rPage) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/dlgsnap.hxx b/sd/source/ui/inc/dlgsnap.hxx new file mode 100644 index 000000000..97fe09ccb --- /dev/null +++ b/sd/source/ui/inc/dlgsnap.hxx @@ -0,0 +1,66 @@ +/* -*- 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 . + */ + +#pragma once + +#include <tools/fract.hxx> +#include <vcl/weld.hxx> + +/************************************************************************/ + +class SfxItemSet; +namespace sd { + class View; +} + +/** + * dialog to adjust snap- lines and points + */ +class SdSnapLineDlg : public weld::GenericDialogController +{ +private: + int nXValue; + int nYValue; + Fraction aUIScale; + + std::unique_ptr<weld::Label> m_xFtX; + std::unique_ptr<weld::MetricSpinButton> m_xMtrFldX; + std::unique_ptr<weld::Label> m_xFtY; + std::unique_ptr<weld::MetricSpinButton> m_xMtrFldY; + std::unique_ptr<weld::Widget> m_xRadioGroup; + std::unique_ptr<weld::RadioButton> m_xRbPoint; + std::unique_ptr<weld::RadioButton> m_xRbVert; + std::unique_ptr<weld::RadioButton> m_xRbHorz; + std::unique_ptr<weld::Button> m_xBtnDelete; + + DECL_LINK(ClickHdl, weld::Button&, void); + DECL_LINK(ToggleHdl, weld::Toggleable&, void); + +public: + SdSnapLineDlg(weld::Window* pWindow, const SfxItemSet& rInAttrs, ::sd::View const * pView); + virtual ~SdSnapLineDlg() override; + + void GetAttr(SfxItemSet& rOutAttrs); + + void HideRadioGroup(); + void HideDeleteBtn() { m_xBtnDelete->hide(); } + void SetInputFields(bool bEnableX, bool bEnableY); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/drawview.hxx b/sd/source/ui/inc/drawview.hxx new file mode 100644 index 000000000..daa5cc026 --- /dev/null +++ b/sd/source/ui/inc/drawview.hxx @@ -0,0 +1,72 @@ +/* -*- 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 . + */ + +#pragma once + +#include "View.hxx" + +#include <sddllapi.h> + +namespace sd { + +class DrawDocShell; +class DrawViewShell; + +/** + * Derivative of ::sd::View; contains also a pointer to the document + */ +class SD_DLLPUBLIC DrawView : public ::sd::View +{ +public: + + DrawView ( + DrawDocShell* pDocSh, + OutputDevice* pOutDev, + DrawViewShell* pShell); + virtual ~DrawView() override; + + virtual void MarkListHasChanged() override; + void CompleteRedraw(OutputDevice* pOutDev, const vcl::Region& rReg, sdr::contact::ViewObjectContactRedirector* pRedirector = nullptr) override; + + virtual bool SetAttributes(const SfxItemSet& rSet, bool bReplaceAll = false, bool bSlide = false, bool bMaster = false) override; + void SetMasterAttributes(SdrObject* pObject, const SdPage& rPage, SfxItemSet rSet, SfxStyleSheetBasePool* pStShPool, bool& bOk, bool bMaster, bool bSlide); + + virtual void Notify(SfxBroadcaster& rBC, const SfxHint& rHint) override; + + void BlockPageOrderChangedHint(bool bBlock); + + bool SetStyleSheet(SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr = false) override; + + virtual void MakeVisible(const ::tools::Rectangle& rRect, vcl::Window& rWin) override; + virtual void HideSdrPage() override; // SdrPageView* pPV); + + virtual void DeleteMarked() override; // from SdrView +protected: + virtual void ModelHasChanged() override; + +private: + DrawDocShell* mpDocShell; + DrawViewShell* mpDrawViewShell; + + sal_uInt16 mnPOCHSmph; ///< for blocking PageOrderChangedHint +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/filedlg.hxx b/sd/source/ui/inc/filedlg.hxx new file mode 100644 index 000000000..6a22d6ba5 --- /dev/null +++ b/sd/source/ui/inc/filedlg.hxx @@ -0,0 +1,57 @@ +/* -*- 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 . + */ + +#pragma once + +#include <vcl/errcode.hxx> +#include <vcl/weld.hxx> + +#include <memory> + +#include <sddllapi.h> + +class SdFileDialog_Imp; + +/******************************************************************************/ + +/** + The class SdOpenSoundFileDialog wraps the FileDialogHelper, displaying the + FILEOPEN_PLAY dialog template and performing the 'preview' functionality + (playing the selected sound file). The interface is a downstripped version + of the aforementioned class, with similar semantics. + */ +class SD_DLLPUBLIC SdOpenSoundFileDialog +{ + const std::unique_ptr<SdFileDialog_Imp> mpImpl; + + SdOpenSoundFileDialog(const SdOpenSoundFileDialog&) = delete; + SdOpenSoundFileDialog& operator=(const SdOpenSoundFileDialog&) = delete; + +public: + SdOpenSoundFileDialog(weld::Window* pParent); + ~SdOpenSoundFileDialog(); + + ErrCode Execute(); + OUString GetPath() const; + void SetPath(const OUString& rPath); + // WIP, please don't remove, dear Clang plugins + bool IsInsertAsLinkSelected() const; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/framework/Configuration.hxx b/sd/source/ui/inc/framework/Configuration.hxx new file mode 100644 index 000000000..8f33ef431 --- /dev/null +++ b/sd/source/ui/inc/framework/Configuration.hxx @@ -0,0 +1,181 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/drawing/framework/XConfiguration.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/container/XNamed.hpp> +#include <comphelper/compbase.hxx> + +#include <memory> + +namespace com::sun::star::util { class XCloneable; } +namespace com::sun::star::drawing::framework { class XConfigurationControllerBroadcaster; } + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XConfiguration, + css::container::XNamed, + css::lang::XServiceInfo + > ConfigurationInterfaceBase; + +/** A configuration describes the resources of an application like panes, + views, and tool bars and their relationships that are currently active + or are requested to be activated. Resources are specified by URLs rather + than references so that not only the current configuration but also a + requested configuration can be represented. + + A resource URL describes the type of a resource, not its actual + instance. For resources, like panes, that are unique with respect to an + application frame, that does not mean much of a difference. For other + resources like views, that may have more than one instance per + application frame, this is different. To identify them unambiguously a + second URL, one of a unique resource, is necessary. This second URL is + called the anchor of the first. The two types of resources are called + unique and linked respectively. + + Direct manipulation of a configuration object is not advised with the + exception of the configuration controller and objects that implement the + XConfigurationChangeOperation interface. +*/ +class Configuration final + : public ConfigurationInterfaceBase +{ +public: + /** Create a new configuration with a broadcaster that is used to send + events about requested configuration changes. + @param rxBroadcaster + This broadcaster is typically the same as the one used by the + ConfigurationController. + @param bBroadcastRequestEvents + When this is <TRUE/> then modifications to the configuration + trigger the broadcasting of "ResourceActivationRequestEvent" and + "ResourceDeactivationRequestEvent". When this flag is <FALSE/> + then events with type "ResourceActivationEvent" and + "ResourceDeactivationEvent" are broadcasted. + */ + Configuration (const css::uno::Reference<css::drawing::framework::XConfigurationControllerBroadcaster>& rxBroadcaster, + bool bBroadcastRequestEvents); + virtual ~Configuration() override; + + virtual void disposing(std::unique_lock<std::mutex>&) override; + + // XConfiguration + + virtual void SAL_CALL addResource ( + const css::uno::Reference<css::drawing::framework::XResourceId>& + rxResourceId) override; + + virtual void SAL_CALL removeResource( + const css::uno::Reference<css::drawing::framework::XResourceId>& + rxResourceId) override; + + virtual css::uno::Sequence< css::uno::Reference< + css::drawing::framework::XResourceId> > SAL_CALL getResources ( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxAnchorId, + const OUString& rsResourceURLPrefix, + css::drawing::framework::AnchorBindingMode eMode) override; + + virtual sal_Bool SAL_CALL hasResource ( + const css::uno::Reference<css::drawing::framework::XResourceId>& + rxResourceId) override; + + // XCloneable + + virtual css::uno::Reference<css::util::XCloneable> + SAL_CALL createClone() override; + + // XNamed + + /** Return a human readable string representation. This is used for + debugging purposes. + */ + virtual OUString SAL_CALL getName() override; + + /** This call is ignored because the XNamed interface is (mis)used to + give access to a human readable name for debugging purposes. + */ + virtual void SAL_CALL setName (const OUString& rName) override; + + OUString SAL_CALL getImplementationName() override; + + sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override; + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + +private: + class ResourceContainer; + /** The resource container holds the URLs of unique resource and of + resource linked to unique resources. + */ + std::unique_ptr<ResourceContainer> mpResourceContainer; + + /** The broadcaster used for notifying listeners of requests for + configuration changes. + */ + css::uno::Reference<css::drawing::framework::XConfigurationControllerBroadcaster> + mxBroadcaster; + + bool mbBroadcastRequestEvents; + + /** This private variant of the constructor is used for cloning a + Configuration object. + @param rResourceContainer + The new Configuration is created with a copy of the elements in + this container. + */ + Configuration (const css::uno::Reference<css::drawing::framework::XConfigurationControllerBroadcaster>& rxBroadcaster, + bool bBroadcastRequestEvents, + const ResourceContainer& rResourceContainer); + + /** Send an event to all interested listeners that a resource has been + added or removed. The event is sent to the listeners via the + ConfigurationController. + @param rxResourceId + The resource that is added to or removed from the configuration. + @param bActivation + This specifies whether an activation or deactivation is + broadcasted. The mbBroadcastRequestEvents member is also taken + into account when the actual event type field is determined. + */ + void PostEvent ( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxResourceId, + const bool bActivation); + + /** When the called object has already been disposed this method throws + an exception and does not return. + + @throws css::lang::DisposedException + */ + void ThrowIfDisposed() const; +}; + +/** Return whether the two given configurations contain the same resource + ids. The order of resource ids is ignored. Empty references are + treated like empty configurations. +*/ +bool AreConfigurationsEquivalent ( + const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration1, + const css::uno::Reference<css::drawing::framework::XConfiguration>& rxConfiguration2); + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/framework/ConfigurationController.hxx b/sd/source/ui/inc/framework/ConfigurationController.hxx new file mode 100644 index 000000000..2fe2f48d0 --- /dev/null +++ b/sd/source/ui/inc/framework/ConfigurationController.hxx @@ -0,0 +1,180 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/drawing/framework/XConfigurationController.hpp> +#include <com/sun/star/lang/XInitialization.hpp> + +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/compbase.hxx> + +#include <memory> + + +namespace com::sun::star::drawing::framework { class XConfiguration; } +namespace com::sun::star::drawing::framework { class XConfigurationChangeRequest; } +namespace com::sun::star::drawing::framework { class XResourceId; } +namespace com::sun::star::drawing::framework { struct ConfigurationChangeEvent; } + +namespace sd::framework { + +typedef ::cppu::WeakComponentImplHelper < + css::drawing::framework::XConfigurationController, + css::lang::XInitialization + > ConfigurationControllerInterfaceBase; + +/** The configuration controller is responsible for maintaining the current + configuration. + + @see css::drawing::framework::XConfigurationController + for an extended documentation. +*/ +class ConfigurationController + : private cppu::BaseMutex, + public ConfigurationControllerInterfaceBase +{ +public: + ConfigurationController() noexcept; + virtual ~ConfigurationController() noexcept override; + ConfigurationController(const ConfigurationController&) = delete; + ConfigurationController& operator=(const ConfigurationController&) = delete; + + virtual void SAL_CALL disposing() override; + + void ProcessEvent(); + + /** Normally the requested changes of the configuration are executed + asynchronously. However, there is at least one situation (searching + with the Outliner) where the surrounding code does not cope with + this. So, instead of calling Reschedule until the global event loop + executes the configuration update, this method does (almost) the + same without the reschedules. + + Do not use this method until there is absolutely no other way. + */ + void RequestSynchronousUpdate(); + + // XConfigurationController + + virtual void SAL_CALL lock() override; + + virtual void SAL_CALL unlock() override; + + virtual void SAL_CALL requestResourceActivation ( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxResourceId, + css::drawing::framework::ResourceActivationMode eMode) override; + + virtual void SAL_CALL requestResourceDeactivation ( + const css::uno::Reference<css::drawing::framework::XResourceId>& + rxResourceId) override; + + virtual css::uno::Reference<css::drawing::framework::XResource> + SAL_CALL getResource ( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxResourceId) override; + + virtual void SAL_CALL update() override; + + virtual css::uno::Reference< + css::drawing::framework::XConfiguration> + SAL_CALL getRequestedConfiguration() override; + + virtual css::uno::Reference< + css::drawing::framework::XConfiguration> + SAL_CALL getCurrentConfiguration() override; + + virtual void SAL_CALL restoreConfiguration ( + const css::uno::Reference<css::drawing::framework::XConfiguration>& + rxConfiguration) override; + + // XConfigurationControllerBroadcaster + + virtual void SAL_CALL addConfigurationChangeListener ( + const css::uno::Reference< + css::drawing::framework::XConfigurationChangeListener>& rxListener, + const OUString& rsEventType, + const css::uno::Any& rUserData) override; + + virtual void SAL_CALL removeConfigurationChangeListener ( + const css::uno::Reference< + css::drawing::framework::XConfigurationChangeListener>& rxListener) override; + + virtual void SAL_CALL notifyEvent ( + const css::drawing::framework::ConfigurationChangeEvent& rEvent) override; + + // XConfigurationRequestQueue + + virtual sal_Bool SAL_CALL hasPendingRequests() override; + + virtual void SAL_CALL postChangeRequest ( + const css::uno::Reference< + css::drawing::framework::XConfigurationChangeRequest>& rxRequest) override; + + // XResourceFactoryManager + + virtual void SAL_CALL addResourceFactory( + const OUString& sResourceURL, + const css::uno::Reference<css::drawing::framework::XResourceFactory>& rxResourceFactory) override; + + virtual void SAL_CALL removeResourceFactoryForURL( + const OUString& sResourceURL) override; + + virtual void SAL_CALL removeResourceFactoryForReference( + const css::uno::Reference<css::drawing::framework::XResourceFactory>& rxResourceFactory) override; + + virtual css::uno::Reference<css::drawing::framework::XResourceFactory> + SAL_CALL getResourceFactory ( + const OUString& sResourceURL) override; + + // XInitialization + + virtual void SAL_CALL initialize( + const css::uno::Sequence<css::uno::Any>& rArguments) override; + + /** Use this class instead of calling lock() and unlock() directly in + order to be exception safe. + */ + class Lock + { + public: + Lock (const css::uno::Reference< + css::drawing::framework::XConfigurationController>& rxController); + ~Lock(); + private: + css::uno::Reference< + css::drawing::framework::XConfigurationController> mxController; + }; + +private: + class Implementation; + std::unique_ptr<Implementation> mpImplementation; + bool mbIsDisposed; + + /** When the called object has already been disposed this method throws + an exception and does not return. + + @throws css::lang::DisposedException + @throws css::uno::RuntimeException + */ + void ThrowIfDisposed () const; +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/framework/DrawModule.hxx b/sd/source/ui/inc/framework/DrawModule.hxx new file mode 100644 index 000000000..79a59b4f9 --- /dev/null +++ b/sd/source/ui/inc/framework/DrawModule.hxx @@ -0,0 +1,46 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/types.h> + +namespace com::sun::star::frame +{ +class XController; +} +namespace com::sun::star::uno +{ +template <typename> class Reference; +} + +namespace sd::framework +{ +/** The task of this module is to instantiate all modules that belong to the + Draw application. +*/ +class DrawModule +{ +public: + static void Initialize(css::uno::Reference<css::frame::XController> const& rxController); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/framework/FrameworkHelper.hxx b/sd/source/ui/inc/framework/FrameworkHelper.hxx new file mode 100644 index 000000000..c9bf981bb --- /dev/null +++ b/sd/source/ui/inc/framework/FrameworkHelper.hxx @@ -0,0 +1,340 @@ +/* -*- 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 . + */ + +#pragma once + +#include <ViewShell.hxx> + +#include <tools/SdGlobalResourceContainer.hxx> + +#include <functional> +#include <map> +#include <memory> + +namespace com::sun::star::drawing::framework { class XConfigurationController; } +namespace com::sun::star::drawing::framework { class XResourceId; } +namespace com::sun::star::drawing::framework { class XView; } +namespace com::sun::star::drawing::framework { struct ConfigurationChangeEvent; } + +namespace sd { +class ViewShellBase; +} + + +namespace sd::framework { + +/** The FrameworkHelper is a convenience class that simplifies the + access to the drawing framework. + It has three main tasks: + 1. Provide frequently used strings of resource URLs and event names. + 2. Provide shortcuts for accessing the sd framework. + 3. Ease the migration to the drawing framework. + + Note that a FrameworkHelper disposes itself when one of the resource + controllers called by it throws a DisposedException. +*/ +class FrameworkHelper + : public std::enable_shared_from_this<FrameworkHelper>, + public SdGlobalResource +{ +public: + // URLs of frequently used panes. + static constexpr OUStringLiteral msPaneURLPrefix = u"private:resource/pane/"; + static const OUString msCenterPaneURL; + static const OUString msFullScreenPaneURL; + static const OUString msLeftImpressPaneURL; + static const OUString msLeftDrawPaneURL; + + // URLs of frequently used views. + static constexpr OUStringLiteral msViewURLPrefix = u"private:resource/view/"; + static const OUString msImpressViewURL; + static const OUString msDrawViewURL; + static const OUString msOutlineViewURL; + static const OUString msNotesViewURL; + static const OUString msHandoutViewURL; + static const OUString msSlideSorterURL; + static const OUString msPresentationViewURL; + static const OUString msSidebarViewURL; + + // URLs of frequently used tool bars. + static constexpr OUStringLiteral msToolBarURLPrefix = u"private:resource/toolbar/"; + static const OUString msViewTabBarURL; + + // Names of frequently used events. + static constexpr OUStringLiteral msResourceActivationRequestEvent + = u"ResourceActivationRequested"; + static constexpr OUStringLiteral msResourceDeactivationRequestEvent + = u"ResourceDeactivationRequest"; + static constexpr OUStringLiteral msResourceActivationEvent = u"ResourceActivation"; + static constexpr OUStringLiteral msResourceDeactivationEvent = u"ResourceDeactivation"; + static constexpr OUStringLiteral msResourceDeactivationEndEvent = u"ResourceDeactivationEnd"; + static constexpr OUStringLiteral msConfigurationUpdateStartEvent = u"ConfigurationUpdateStart"; + static constexpr OUStringLiteral msConfigurationUpdateEndEvent = u"ConfigurationUpdateEnd"; + + /** Return the FrameworkHelper object that is associated with the given + ViewShellBase. If such an object does not yet exist, a new one is + created. + */ + static ::std::shared_ptr<FrameworkHelper> Instance (ViewShellBase& rBase); + + /** Mark the FrameworkHelper object for the given ViewShellBase as + disposed. A following ReleaseInstance() call will destroy the + FrameworkHelper object. + + Do not call this method. It is an internally used method that can + not be made private. + */ + static void DisposeInstance (const ViewShellBase& rBase); + + /** Destroy the FrameworkHelper object for the given ViewShellBase. + + Do not call this method. It is an internally used method that can + not be made private. + */ + static void ReleaseInstance (const ViewShellBase& rBase); + + /** Return an identifier for the given view URL. This identifier can be + used in a switch statement. See GetViewURL() for a mapping in the + opposite direction. + */ + static ViewShell::ShellType GetViewId (const OUString& rsViewURL); + + /** Return a view URL for the given identifier. See GetViewId() for a + mapping in the opposite direction. + */ + static OUString GetViewURL (ViewShell::ShellType eType); + + /** Return a ViewShell pointer for the given XView reference. This + assumes that the given reference is implemented by the + ViewShellWrapper class that supports the XTunnel interface. + @return + When the ViewShell pointer can not be inferred from the given + reference then an empty pointer is returned. + */ + static ::std::shared_ptr<ViewShell> GetViewShell ( + const css::uno::Reference<css::drawing::framework::XView>& rxView); + + typedef ::std::function<bool (const css::drawing::framework::ConfigurationChangeEvent&)> + ConfigurationChangeEventFilter; + typedef ::std::function<void (bool bEventSeen)> Callback; + typedef ::std::function< + void ( + const css::uno::Reference< + css::drawing::framework::XResourceId>&) + > ResourceFunctor; + + /** Test whether the called FrameworkHelper object is valid. + @return + When the object has already been disposed then <FALSE/> is returned. + */ + bool IsValid() const; + + /** Return a pointer to the view shell that is displayed in the + specified pane. See GetView() for a variant that returns a + reference to XView instead of a ViewShell pointer. + @return + An empty pointer is returned when for example the specified pane + does not exist or is not visible or does not show a view or one + of the involved objects does not support XUnoTunnel (where + necessary). + */ + ::std::shared_ptr<ViewShell> GetViewShell (const OUString& rsPaneURL); + + /** Return a reference to the view that is displayed in the specified + pane. See GetViewShell () for a variant that returns a ViewShell + pointer instead of a reference to XView. + @param rxPaneOrViewId + When this ResourceId specifies a view then that view is + returned. When it belongs to a pane then one view in that pane + is returned. + @return + An empty reference is returned when for example the specified pane + does not exist or is not visible or does not show a view or one + of the involved objects does not support XTunnel (where + necessary). + */ + css::uno::Reference<css::drawing::framework::XView> GetView ( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxPaneOrViewId); + + /** Request the specified view to be displayed in the specified pane. + When the pane is not visible its creation is also requested. The + update that creates the actual view object is done asynchronously. + @param rsResourceURL + The resource URL of the view to show. + @param rsAnchorURL + The URL of the pane in which to show the view. + @return + The resource id of the requested view is returned. With that + the caller can, for example, call RunOnResourceActivation() to + do some initialization after the requested view becomes active. + */ + css::uno::Reference<css::drawing::framework::XResourceId> RequestView ( + const OUString& rsResourceURL, + const OUString& rsAnchorURL); + + /** Process a slot call that requests a view shell change. + */ + void HandleModeChangeSlot ( + sal_uInt16 nSlotId, + SfxRequest const & rRequest); + + /** Run the given callback when the specified event is notified by the + ConfigurationManager. When there are no pending requests and + therefore no events would be notified (in the foreseeable future) + then the callback is called immediately. + The callback is called with a flag that tells the callback whether + the event it waits for has been sent. + */ + void RunOnConfigurationEvent( + const OUString& rsEventType, + const Callback& rCallback); + + /** Run the given callback when the specified resource has been + activated. When the resource is active already when this method is + called then rCallback is called before this method returns. + @param rxResourceId + Wait for the activation of this resource before calling + rCallback. + @param rCallback + The callback to be called when the resource is activated. + + */ + void RunOnResourceActivation( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxResourceId, + const Callback& rCallback); + + /** Normally the requested changes of the configuration are executed + asynchronously. However, there is at least one situation (searching + with the Outliner) where the surrounding code does not cope with + this. So, instead of calling Reschedule until the global event loop + executes the configuration update, this method does (almost) the + same without the reschedules. + + Do not use this method until there is absolutely no other way. + */ + void RequestSynchronousUpdate(); + + /** Block until the specified event is notified by the configuration + controller. When the configuration controller is not processing any + requests the method returns immediately. + */ + void WaitForEvent (const OUString& rsEventName) const; + + /** This is a short cut for WaitForEvent(msConfigurationUpdateEndEvent). + Call this method to execute the pending requests. + */ + void WaitForUpdate() const; + + /** Explicit request for an update of the current configuration. Call + this method when one of the resources managed by the sd framework + has been activated or deactivated from the outside, i.e. not by the + framework itself. An example for this is a click on the closer + button of one of the side panes. + */ + void UpdateConfiguration(); + + /** Return a string representation of the given XResourceId object. + */ + static OUString ResourceIdToString ( + const css::uno::Reference< + css::drawing::framework::XResourceId>& rxResourceId); + + /** Create a new XResourceId object for the given resource URL. + */ + static css::uno::Reference< + css::drawing::framework::XResourceId> + CreateResourceId ( + const OUString& rsResourceURL); + + /** Create a new XResourceId object for the given resource URL and a + single anchor URL. + */ + static css::uno::Reference< + css::drawing::framework::XResourceId> + CreateResourceId ( + const OUString& rsResourceURL, + const OUString& rsAnchorURL); + + /** Create a new XResourceId object for the given resource URL. + */ + static css::uno::Reference< + css::drawing::framework::XResourceId> + CreateResourceId ( + const OUString& rsResourceURL, + const css::uno::Reference< + css::drawing::framework::XResourceId>& rxAnchor); + + const css::uno::Reference<css::drawing::framework::XConfigurationController>& + GetConfigurationController() const { return mxConfigurationController;} + +private: + typedef ::std::map< + const ViewShellBase*, + ::std::shared_ptr<FrameworkHelper> > InstanceMap; + /** The instance map holds (at least) one FrameworkHelper instance for + every ViewShellBase object. + */ + static InstanceMap maInstanceMap; + class ViewURLMap; + static ViewURLMap maViewURLMap; + + ViewShellBase& mrBase; + css::uno::Reference<css::drawing::framework::XConfigurationController> + mxConfigurationController; + + class DisposeListener; + friend class DisposeListener; + css::uno::Reference<css::lang::XComponent> + mxDisposeListener; + + FrameworkHelper (ViewShellBase& rBase); + FrameworkHelper (const FrameworkHelper& rHelper) = delete; + virtual ~FrameworkHelper() override; + class Deleter; friend class Deleter; + FrameworkHelper& operator= (const FrameworkHelper& rHelper) = delete; + + void Initialize(); + + void Dispose(); + + /** Run the given callback when an event of the specified type is + received from the ConfigurationController or when the + ConfigurationController has no pending change requests. + @param rsEventType + Run rCallback only on this event. + @param rFilter + This filter has to return <TRUE/> in order for rCallback to be + called. + @param rCallback + The callback functor to be called. + */ + void RunOnEvent( + const OUString& rsEventType, + const ConfigurationChangeEventFilter& rFilter, + const Callback& rCallback) const; + + /** This disposing method is forwarded from the inner DisposeListener class. + */ + void disposing (const css::lang::EventObject& rEventObject); +}; + +} // end of namespace sd::framework + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/framework/ImpressModule.hxx b/sd/source/ui/inc/framework/ImpressModule.hxx new file mode 100644 index 000000000..da7ede9d9 --- /dev/null +++ b/sd/source/ui/inc/framework/ImpressModule.hxx @@ -0,0 +1,46 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/types.h> + +namespace com::sun::star::frame +{ +class XController; +} +namespace com::sun::star::uno +{ +template <class interface_type> class Reference; +} + +namespace sd::framework +{ +/** The task of this module is to instantiate all modules that belong to the + Impress application. +*/ +class ImpressModule +{ +public: + static void Initialize(css::uno::Reference<css::frame::XController> const& rxController); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/framework/ModuleController.hxx b/sd/source/ui/inc/framework/ModuleController.hxx new file mode 100644 index 000000000..4efc6cc15 --- /dev/null +++ b/sd/source/ui/inc/framework/ModuleController.hxx @@ -0,0 +1,114 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/drawing/framework/XModuleController.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <comphelper/compbase.hxx> +#include <cppuhelper/weakref.hxx> + +#include <unordered_map> + +namespace com::sun::star::frame { class XController; } +namespace com::sun::star::uno { class XComponentContext; } + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XModuleController, + css::lang::XInitialization + > ModuleControllerInterfaceBase; + +/** The ModuleController has two tasks: + + 1. It reads the + org.openoffice.Office.Impress/MultiPaneGUI/Framework/ResourceFactories + configuration data that maps from resource URLs to service names of + factories that can create resources for the URLs. When the + configuration controller wants to create a resource for which it does + not have a factory, it asks the ModuleController to provide one. The + ModuleController looks up the service name registered for the URL of the + resource and instantiates this service. The service is expected to + register on its creation a factory for the resource in question. + + 2. The ModuleController reads on its creation + org.openoffice.Office.Impress/MultiPaneGUI/Framework/StartupServices + configuration data and instantiates all listed services. These services + can then register as listeners at the ConfigurationController or do + whatever they like. +*/ +class ModuleController final + : public ModuleControllerInterfaceBase +{ +public: + static css::uno::Reference< + css::drawing::framework::XModuleController> + CreateInstance ( + const css::uno::Reference<css::uno::XComponentContext>& + rxContext); + + virtual void disposing(std::unique_lock<std::mutex>&) override; + + // XModuleController + + virtual void SAL_CALL requestResource(const OUString& rsResourceURL) override; + + // XInitialization + + virtual void SAL_CALL initialize( + const css::uno::Sequence<css::uno::Any>& aArguments) override; + +private: + css::uno::Reference< + css::frame::XController> mxController; + + std::unordered_map<OUString, OUString> maResourceToFactoryMap; + std::unordered_map<OUString, css::uno::WeakReference<css::uno::XInterface>> maLoadedFactories; + + /// @throws std::exception + ModuleController ( + const css::uno::Reference<css::uno::XComponentContext>& rxContext); + ModuleController (const ModuleController&) = delete; + virtual ~ModuleController() noexcept override; + + /** Called for every entry in the ResourceFactories configuration entry. + */ + void ProcessFactory (const ::std::vector<css::uno::Any>& rValues); + + /** Instantiate all startup services that are found in the + /org.openoffice.Office.Impress/MultiPaneGUI/Framework/StartupServices + configuration entry. This method is called once when a new + ModuleController object is created. + */ + void InstantiateStartupServices(); + + /** Called for one entry in the StartupServices configuration list this + method instantiates the service described by the entry. It does not + hold references to the new object so that the object will be + destroyed on function exit when it does not register itself + somewhere. It typically will register as + XConfigurationChangeListener at the configuration controller. + */ + void ProcessStartupService (const ::std::vector<css::uno::Any>& rValues); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/framework/Pane.hxx b/sd/source/ui/inc/framework/Pane.hxx new file mode 100644 index 000000000..9e8ee25a1 --- /dev/null +++ b/sd/source/ui/inc/framework/Pane.hxx @@ -0,0 +1,141 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/drawing/framework/XPane.hpp> +#include <com/sun/star/drawing/framework/XPane2.hpp> +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/compbase.hxx> +#include <vcl/vclptr.hxx> +#include <vcl/window.hxx> + +namespace sd::framework { + +typedef ::cppu::WeakComponentImplHelper < + css::drawing::framework::XPane, + css::drawing::framework::XPane2, + css::lang::XUnoTunnel + > PaneInterfaceBase; + +/** A pane is a wrapper for a window and possibly for a tab bar (for view + switching). Panes are unique resources. + + This class has two responsibilities: + 1. It implements the XPane interface. This is the most important + interface of this class for API based views (of which there not that + many yet) because it gives access to the XWindow. + 2. It gives access to the underlying VCL Window by implementing the + XUnoTunnel interface. This is necessary at the moment and in the + foreseeable future because many parts of the Draw and Impress views rely + on direct access on the Window class. +*/ +class Pane + : protected cppu::BaseMutex, + public PaneInterfaceBase +{ +public: + /** Create a new Pane object that wraps the given window. + @param rsPaneURL + The URL that is used by the configuration to identify the pane. + The given URL has to be valid. + @param pWindow + The VCL Window (usually this really is an sd::Window) that is + wrapped by the new Pane object. The given pointer must not be + NULL. + */ + Pane ( + const css::uno::Reference<css::drawing::framework::XResourceId>& rxPaneId, + vcl::Window* pWindow) + noexcept; + virtual ~Pane() override; + + virtual void SAL_CALL disposing() override; + + static const css::uno::Sequence<sal_Int8>& getUnoTunnelId(); + + /** This method is typically used together with the XUnoTunnel to obtain + a Window pointer from an XPane object. + */ + virtual vcl::Window* GetWindow(); + + //----- XPane ------------------------------------------------------------- + + /** For a UNO API based implementation of a view this may the most + important method of this class because the view is only interested + in the window of the pane. + */ + virtual css::uno::Reference<css::awt::XWindow> + SAL_CALL getWindow() override; + + virtual css::uno::Reference<css::rendering::XCanvas> + SAL_CALL getCanvas() override; + + //----- XPane2 ------------------------------------------------------------- + + virtual sal_Bool SAL_CALL isVisible() override; + + virtual void SAL_CALL setVisible (sal_Bool bIsVisible) override; + + virtual css::uno::Reference<css::accessibility::XAccessible> SAL_CALL getAccessible() override; + + virtual void SAL_CALL setAccessible ( + const css::uno::Reference<css::accessibility::XAccessible>& rxAccessible) override; + + //----- XResource --------------------------------------------------------- + + virtual css::uno::Reference<css::drawing::framework::XResourceId> + SAL_CALL getResourceId() override; + + /** For the typical pane it makes no sense to be displayed without a + view. Therefore this default implementation returns always <TRUE/>. + */ + virtual sal_Bool SAL_CALL isAnchorOnly() override; + + //----- XUnoTunnel -------------------------------------------------------- + + virtual sal_Int64 SAL_CALL getSomething (const css::uno::Sequence<sal_Int8>& rId) override; + +protected: + css::uno::Reference<css::drawing::framework::XResourceId> mxPaneId; + VclPtr<vcl::Window> mpWindow; + css::uno::Reference<css::awt::XWindow> mxWindow; + css::uno::Reference<css::rendering::XCanvas> mxCanvas; + + /** Override this method, not getCanvas(), when you want to provide a + different canvas. + + @throws css::uno::RuntimeException + */ + virtual css::uno::Reference<css::rendering::XCanvas> + CreateCanvas(); + + /** Throw DisposedException when the object has already been disposed or + is currently being disposed. Otherwise this method returns + normally. + + @throws css::lang::DisposedException + */ + void ThrowIfDisposed() const; +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/framework/PresentationFactory.hxx b/sd/source/ui/inc/framework/PresentationFactory.hxx new file mode 100644 index 000000000..897825c8a --- /dev/null +++ b/sd/source/ui/inc/framework/PresentationFactory.hxx @@ -0,0 +1,77 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/drawing/framework/XResourceFactory.hpp> +#include <com/sun/star/drawing/framework/XConfigurationChangeListener.hpp> +#include <comphelper/compbase.hxx> + +namespace com::sun::star::frame { class XController; } + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XResourceFactory, + css::drawing::framework::XConfigurationChangeListener + > PresentationFactoryInterfaceBase; + +/** This factory creates a marker view whose existence in a configuration + indicates that a slideshow is running (in another but associated + application window). +*/ +class PresentationFactory final + : public PresentationFactoryInterfaceBase +{ +public: + PresentationFactory ( + const css::uno::Reference<css::frame::XController>& rxController); + virtual ~PresentationFactory() override; + + // XResourceFactory + + virtual css::uno::Reference<css::drawing::framework::XResource> + SAL_CALL createResource ( + const css::uno::Reference< + css::drawing::framework::XResourceId>& rxViewId) override; + + virtual void SAL_CALL releaseResource ( + const css::uno::Reference<css::drawing::framework::XResource>& xView) override; + + // XConfigurationChangeListener + + virtual void SAL_CALL notifyConfigurationChange ( + const css::drawing::framework::ConfigurationChangeEvent& rEvent) override; + + // lang::XEventListener + + using WeakComponentImplHelperBase::disposing; + virtual void SAL_CALL disposing ( + const css::lang::EventObject& rEventObject) override; + +private: + css::uno::Reference<css::frame::XController> mxController; + + /// @throws css::lang::DisposedException + void ThrowIfDisposed() const; +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/framework/PresentationModule.hxx b/sd/source/ui/inc/framework/PresentationModule.hxx new file mode 100644 index 000000000..f6dcfbc69 --- /dev/null +++ b/sd/source/ui/inc/framework/PresentationModule.hxx @@ -0,0 +1,46 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/types.h> + +namespace com::sun::star::frame +{ +class XController; +} +namespace com::sun::star::uno +{ +template <class interface_type> class Reference; +} + +namespace sd::framework +{ +/** The task of this module is to instantiate all modules that belong to the + fullscreen presentation. +*/ +class PresentationModule +{ +public: + static void Initialize(css::uno::Reference<css::frame::XController> const& rxController); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/framework/ResourceId.hxx b/sd/source/ui/inc/framework/ResourceId.hxx new file mode 100644 index 000000000..98b456c76 --- /dev/null +++ b/sd/source/ui/inc/framework/ResourceId.hxx @@ -0,0 +1,213 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <vector> + +#include <com/sun/star/drawing/framework/XResourceId.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <cppuhelper/implbase.hxx> + +#include <memory> + +namespace com::sun::star::util { class XURLTransformer; } +namespace com::sun::star::uno { template <class interface_type> class WeakReference; } + +namespace sd::framework { + +typedef ::cppu::WeakImplHelper < + css::drawing::framework::XResourceId, + css::lang::XInitialization, + css::lang::XServiceInfo + > ResourceIdInterfaceBase; + +/** Implementation of the css::drawing::framework::ResourceId + service and the css::drawing::framework::XResourceId + interface. +*/ +class ResourceId + : public ResourceIdInterfaceBase +{ +public: + /** Create a new, empty resource id. + */ + ResourceId(); + + /** Create a new resource id that is described by the given URLs. + @param rsResourceURLs + The first URL specifies the type of resource. The other URLs + describe its anchor. + The set of URLs may be empty. The result is then the same as + returned by ResourceId() default constructor. + */ + ResourceId (std::vector<OUString>&& rsResourceURLs); + + /** Create a new resource id that has an empty anchor. + @param rsResourceURL + When this resource URL is empty then the resulting ResourceId + object is identical to when the ResourceId() default constructor + had been called. + */ + ResourceId ( + const OUString& rsResourceURL); + + /** Create a new resource id for the given resource type and an anchor + that is specified by a single URL. This constructor can be used for + example for views that are bound to panes. + @param rsResourceURL + The URL of the actual resource. + @param rsAnchorURL + The single URL of the anchor. + */ + ResourceId ( + const OUString& rsResourceURL, + const OUString& rsAnchorURL); + + /** Create a new resource id with an anchor that consists of a sequence + of URLs that is extended by a further URL. + @param rsResourceURL + The URL of the actual resource. + @param rsFirstAnchorURL + This URL extends the anchor given by rAnchorURLs. + @param rAnchorURLs + An anchor as it is returned by XResourceId::getAnchorURLs(). + */ + ResourceId ( + const OUString& rsResourceURL, + const OUString& rsFirstAnchorURL, + const css::uno::Sequence<OUString>& rAnchorURLs); + + virtual ~ResourceId() override; + + //===== XResourceId ======================================================= + + virtual OUString SAL_CALL + getResourceURL() override; + + virtual css::util::URL SAL_CALL + getFullResourceURL() override; + + virtual sal_Bool SAL_CALL + hasAnchor() override; + + virtual css::uno::Reference< + css::drawing::framework::XResourceId> SAL_CALL + getAnchor() override; + + virtual css::uno::Sequence<OUString> SAL_CALL + getAnchorURLs() override; + + virtual OUString SAL_CALL + getResourceTypePrefix() override; + + virtual sal_Int16 SAL_CALL + compareTo (const css::uno::Reference< + css::drawing::framework::XResourceId>& rxResourceId) override; + + virtual sal_Bool SAL_CALL + isBoundTo ( + const css::uno::Reference< + css::drawing::framework::XResourceId>& rxResourceId, + css::drawing::framework::AnchorBindingMode eMode) override; + + virtual sal_Bool SAL_CALL + isBoundToURL ( + const OUString& rsAnchorURL, + css::drawing::framework::AnchorBindingMode eMode) override; + + virtual css::uno::Reference< + css::drawing::framework::XResourceId> SAL_CALL + clone() override; + + //===== XInitialization =================================================== + + void SAL_CALL initialize ( + const css::uno::Sequence<css::uno::Any>& aArguments) override; + + OUString SAL_CALL getImplementationName() override; + + sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override; + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + +private: + /** The set of URLs that consist of the resource URL at index 0 and the + anchor URLs and indices 1 and above. + */ + std::vector<OUString> maResourceURLs; + + std::unique_ptr<css::util::URL> mpURL; + + static css::uno::WeakReference<css::util::XURLTransformer> mxURLTransformerWeak; + + /** Compare the called ResourceId object to the given ResourceId object. + This uses the implementation of both objects to speed up the + comparison. + */ + sal_Int16 CompareToLocalImplementation (const ResourceId& rId) const; + + /** Compare the called ResourceId object to the given XResourceId object + reference. The comparison is done via the UNO interface. Namely, + it uses the getResourceURL() and the getAnchorURLs() methods to get + access to the URLs of the given object. + */ + sal_Int16 CompareToExternalImplementation (const css::uno::Reference< + css::drawing::framework::XResourceId>& rxId) const; + + /** Return whether the called ResourceId object is bound to the anchor + consisting of the URLs given by psFirstAnchorURL and paAnchorURLs. + @param psFirstAnchorURL + Optional first URL of the anchor. This can be missing or present + independently of paAnchorURLs. + @param paAnchorURLs + Optional set of additional anchor URLs. This can be missing or + present independently of psFirstAnchorURL. + @param eMode + This specifies whether the called resource has to be directly + bound to the given anchor in order to return <TRUE/> or whether + it can be bound indirectly, too. + */ + bool IsBoundToAnchor ( + const OUString* psFirstAnchorURL, + const css::uno::Sequence<OUString>* paAnchorURLs, + css::drawing::framework::AnchorBindingMode eMode) const; + + /** Return whether the called ResourceId object is bound to the anchor + consisting of the URLs in rResourceURLs. + @param rResourceURLs + A possibly empty list of anchor URLs. + @param eMode + This specifies whether the called resource has to be directly + bound to the given anchor in order to return <TRUE/> or whether + it can be bound indirectly, too. + */ + bool IsBoundToAnchor ( + const ::std::vector<OUString>& rResourceURLs, + css::drawing::framework::AnchorBindingMode eMode) const; + + void ParseResourceURL(); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/framework/ViewShellWrapper.hxx b/sd/source/ui/inc/framework/ViewShellWrapper.hxx new file mode 100644 index 000000000..43dca4d67 --- /dev/null +++ b/sd/source/ui/inc/framework/ViewShellWrapper.hxx @@ -0,0 +1,131 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/drawing/framework/XView.hpp> +#include <com/sun/star/drawing/framework/XRelocatableResource.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include <com/sun/star/awt/XWindowListener.hpp> +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <comphelper/compbase.hxx> + +#include <memory> + +namespace sd { class ViewShell; } +namespace sd::slidesorter { class SlideSorterViewShell; } +namespace com::sun::star::awt { class XWindow; } + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < css::lang::XUnoTunnel + , css::awt::XWindowListener + , css::view::XSelectionSupplier + , css::drawing::framework::XRelocatableResource + , css::drawing::framework::XView + > ViewShellWrapperInterfaceBase; + +/** This class wraps ViewShell objects and makes them look like an XView. + Most importantly it provides a tunnel to the ViewShell implementation. + Then it forwards size changes of the pane window to the view shell. +*/ +class ViewShellWrapper final : public ViewShellWrapperInterfaceBase +{ +public: + /** Create a new ViewShellWrapper object that wraps the given ViewShell + object. + @param pViewShell + The ViewShell object to wrap. + @param rsViewURL + URL of the view type of the wrapped view shell. + @param rxWindow + This window reference is optional. When a valid reference is + given then size changes of the referenced window are forwarded + to the ViewShell object. + */ + ViewShellWrapper ( + const ::std::shared_ptr<ViewShell>& pViewShell, + const css::uno::Reference<css::drawing::framework::XResourceId>& rxViewId, + const css::uno::Reference<css::awt::XWindow>& rxWindow); + virtual ~ViewShellWrapper() override; + + virtual void disposing(std::unique_lock<std::mutex>&) override; + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + + static const css::uno::Sequence<sal_Int8>& getUnoTunnelId(); + + /** This method is typically used together with the XUnoTunnel interface + to obtain a pointer to the wrapped ViewShell object for a given + XView object. + */ + const ::std::shared_ptr<ViewShell>& GetViewShell() const { return mpViewShell;} + + // XUnoTunnel + + virtual sal_Int64 SAL_CALL getSomething (const css::uno::Sequence<sal_Int8>& rId) override; + + // XResource + + virtual css::uno::Reference<css::drawing::framework::XResourceId> + SAL_CALL getResourceId() override; + + virtual sal_Bool SAL_CALL isAnchorOnly() override; + + // XSelectionSupplier + + virtual sal_Bool SAL_CALL select( const css::uno::Any& aSelection ) override; + virtual css::uno::Any SAL_CALL getSelection() override; + virtual void SAL_CALL addSelectionChangeListener( const css::uno::Reference< css::view::XSelectionChangeListener >& xListener ) override; + virtual void SAL_CALL removeSelectionChangeListener( const css::uno::Reference< css::view::XSelectionChangeListener >& xListener ) override; + + // XRelocatableResource + + virtual sal_Bool SAL_CALL relocateToAnchor ( + const css::uno::Reference< + css::drawing::framework::XResource>& xResource) override; + + // XWindowListener + + virtual void SAL_CALL windowResized( + const css::awt::WindowEvent& rEvent) override; + + virtual void SAL_CALL windowMoved( + const css::awt::WindowEvent& rEvent) override; + + virtual void SAL_CALL windowShown( + const css::lang::EventObject& rEvent) override; + + virtual void SAL_CALL windowHidden( + const css::lang::EventObject& rEvent) override; + + // XEventListener + + virtual void SAL_CALL disposing( + const css::lang::EventObject& rEvent) override; + +private: + ::std::shared_ptr< ViewShell > mpViewShell; + ::std::shared_ptr< ::sd::slidesorter::SlideSorterViewShell > mpSlideSorterViewShell; + const css::uno::Reference< css::drawing::framework::XResourceId > mxViewId; + css::uno::Reference<css::awt::XWindow > mxWindow; +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuarea.hxx b/sd/source/ui/inc/fuarea.hxx new file mode 100644 index 000000000..38f0b48b6 --- /dev/null +++ b/sd/source/ui/inc/fuarea.hxx @@ -0,0 +1,48 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuArea : public FuPoor +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + + virtual void Activate() override; + virtual void Deactivate() override; + +private: + FuArea ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + + virtual void DoExecute( SfxRequest& rReq ) override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fubullet.hxx b/sd/source/ui/inc/fubullet.hxx new file mode 100644 index 000000000..a8b771f06 --- /dev/null +++ b/sd/source/ui/inc/fubullet.hxx @@ -0,0 +1,54 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +class SfxItemSet; +class SfxViewFrame; + +namespace sd { + +class ViewShell; + +class FuBullet : public FuPoor +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + + static void GetSlotState( SfxItemSet& rSet, ViewShell const * pViewShell, SfxViewFrame* pViewFrame ); + +private: + FuBullet ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + + void InsertSpecialCharacter( SfxRequest const & rReq ); + void InsertFormattingMark( sal_Unicode cMark ); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuchar.hxx b/sd/source/ui/inc/fuchar.hxx new file mode 100644 index 000000000..10331b87d --- /dev/null +++ b/sd/source/ui/inc/fuchar.hxx @@ -0,0 +1,49 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuChar + : public FuPoor +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + + virtual void DoExecute( SfxRequest& rReq ) override; + + virtual void Activate() override; + virtual void Deactivate() override; + +private: + FuChar ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fucon3d.hxx b/sd/source/ui/inc/fucon3d.hxx new file mode 100644 index 000000000..545b19327 --- /dev/null +++ b/sd/source/ui/inc/fucon3d.hxx @@ -0,0 +1,61 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fuconstr.hxx" + +class E3dCompoundObject; +class E3dScene; +class SdDrawDocument; +class SfxRequest; + +namespace sd { + +class FuConstruct3dObject + : public FuConstruct +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, bool bPermanent ); + virtual void DoExecute( SfxRequest& rReq ) override; + + // Mouse- & Key-Events + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + + virtual void Activate() override; + + virtual SdrObjectUniquePtr CreateDefaultObject(const sal_uInt16 nID, const ::tools::Rectangle& rRectangle) override; + +private: + FuConstruct3dObject ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + + void ImpPrepareBasic3DShape(E3dCompoundObject const * p3DObj, E3dScene *pScene); + E3dCompoundObject* ImpCreateBasic3DShape(); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuconarc.hxx b/sd/source/ui/inc/fuconarc.hxx new file mode 100644 index 000000000..9a1beae89 --- /dev/null +++ b/sd/source/ui/inc/fuconarc.hxx @@ -0,0 +1,54 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fuconstr.hxx" + +namespace sd { + +class FuConstructArc final + : public FuConstruct +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, bool bPermanent ); + virtual void DoExecute( SfxRequest& rReq ) override; + + // Mouse- & Key-Events + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + + virtual void Activate() override; + + virtual SdrObjectUniquePtr CreateDefaultObject(const sal_uInt16 nID, const ::tools::Rectangle& rRectangle) override; + +private: + FuConstructArc ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuconbez.hxx b/sd/source/ui/inc/fuconbez.hxx new file mode 100644 index 000000000..fe9aceae2 --- /dev/null +++ b/sd/source/ui/inc/fuconbez.hxx @@ -0,0 +1,76 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/uno/Any.hxx> +#include "fuconstr.hxx" + +class SdDrawDocument; + +namespace sd { + +class FuConstructBezierPolygon final + : public FuConstruct +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, bool bPermanent ); + void DoExecute( SfxRequest& rReq ) override; + + // Mouse- & Key-Events + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + + virtual void Activate() override; + virtual void Deactivate() override; + + virtual void SelectionHasChanged() override; + + void SetEditMode(sal_uInt16 nMode); + sal_uInt16 GetEditMode() const { return nEditMode; } + + /** + * set attribute for the object to be created + */ + void SetAttributes(SfxItemSet& rAttr, SdrObject* pObj); + + virtual SdrObjectUniquePtr CreateDefaultObject(const sal_uInt16 nID, const ::tools::Rectangle& rRectangle) override; + +private: + FuConstructBezierPolygon ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + + sal_uInt16 nEditMode; + css::uno::Any maTargets; // used for creating a path for custom animations + + //Extra attributes coming from parameters + sal_uInt16 mnTransparence; // Default: 0 + OUString msColor; // Default: "" + sal_uInt16 mnWidth; // Default: 0 + OUString msShapeName; // Default: "" +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuconcs.hxx b/sd/source/ui/inc/fuconcs.hxx new file mode 100644 index 000000000..f4f4ef30e --- /dev/null +++ b/sd/source/ui/inc/fuconcs.hxx @@ -0,0 +1,64 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fuconstr.hxx" +#include <rtl/ustring.hxx> + +class SdDrawDocument; + +namespace sd { + +class FuConstructCustomShape final + : public FuConstruct +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, bool bPermanent ); + virtual void DoExecute( SfxRequest& rReq ) override; + + // Mouse- & Key-Events + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + + virtual void Activate() override; + + void SetAttributes( SdrObject* pObj ); + const OUString& GetShapeType() const; + + virtual SdrObjectUniquePtr CreateDefaultObject(const sal_uInt16 nID, const ::tools::Rectangle& rRectangle) override; + + // #i33136# + virtual bool doConstructOrthogonal() const override; + +private: + FuConstructCustomShape ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + + OUString aCustomShape; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuconnct.hxx b/sd/source/ui/inc/fuconnct.hxx new file mode 100644 index 000000000..f8000e5bc --- /dev/null +++ b/sd/source/ui/inc/fuconnct.hxx @@ -0,0 +1,46 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuConnectionDlg + : public FuPoor +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuConnectionDlg ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuconrec.hxx b/sd/source/ui/inc/fuconrec.hxx new file mode 100644 index 000000000..caf8ac7eb --- /dev/null +++ b/sd/source/ui/inc/fuconrec.hxx @@ -0,0 +1,71 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fuconstr.hxx" + +class SdDrawDocument; +class SfxItemSet; + +namespace sd { + +/** + * draw rectangle + */ +class FuConstructRectangle final + : public FuConstruct +{ +private: + //Extra attributes coming from parameters + sal_uInt16 mnFillTransparence; // Default: 0 + OUString msFillColor; // Default: "" + sal_uInt16 mnLineStyle; // Default: SAL_MAX_UINT16 + OUString msShapeName; // Default: "" + +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, bool bPermanent ); + virtual void DoExecute( SfxRequest& rReq ) override; + + // Mouse- & Key-Events + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + + virtual void Activate() override; + virtual void Deactivate() override; + + void SetAttributes(SfxItemSet& rAttr, SdrObject* pObj); + void SetLineEnds(SfxItemSet& rAttr, SdrObject const & rObj); + + virtual SdrObjectUniquePtr CreateDefaultObject(const sal_uInt16 nID, const ::tools::Rectangle& rRectangle) override; + +private: + FuConstructRectangle ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuconstr.hxx b/sd/source/ui/inc/fuconstr.hxx new file mode 100644 index 000000000..743c6cd5b --- /dev/null +++ b/sd/source/ui/inc/fuconstr.hxx @@ -0,0 +1,64 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fudraw.hxx" + +class SdrObject; +class SfxItemSet; + +namespace sd { + +class FuConstruct : public FuDraw +{ +public: + + // Mouse Events + virtual bool MouseMove(const MouseEvent& rMEvt) override; + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + + virtual void Activate() override; + virtual void Deactivate() override; + + virtual void SelectionHasChanged() override { bSelectionChanged = true; } + + // SJ: setting stylesheet, the use of a filled or unfilled style + // is determined by the member nSlotId : + void SetStyleSheet(SfxItemSet& rAttr, SdrObject* pObj); + + // SJ: setting stylesheet, the use of a filled or unfilled style + // is determined by the parameters bUseFillStyle and bUseNoFillStyle : + void SetStyleSheet( SfxItemSet& rAttr, SdrObject* pObj, + const bool bUseFillStyle, const bool bUseNoFillStyle ); + +protected: + FuConstruct (ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +private: + bool bSelectionChanged; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuconuno.hxx b/sd/source/ui/inc/fuconuno.hxx new file mode 100644 index 000000000..df8e4d415 --- /dev/null +++ b/sd/source/ui/inc/fuconuno.hxx @@ -0,0 +1,64 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fuconstr.hxx" +#include <rtl/ustring.hxx> + +enum class SdrInventor : sal_uInt32; + +namespace sd { + +/** + * draw control + */ +class FuConstructUnoControl final + : public FuConstruct +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, bool bPermanent ); + virtual void DoExecute( SfxRequest& rReq ) override; + + // Mouse- & Key-Events + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + + virtual void Activate() override; + virtual void Deactivate() override; + + virtual SdrObjectUniquePtr CreateDefaultObject(const sal_uInt16 nID, const ::tools::Rectangle& rRectangle) override; + +private: + FuConstructUnoControl( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + + OUString aOldLayer; + SdrInventor nInventor; + SdrObjKind nIdentifier; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fucopy.hxx b/sd/source/ui/inc/fucopy.hxx new file mode 100644 index 000000000..89b950a8f --- /dev/null +++ b/sd/source/ui/inc/fucopy.hxx @@ -0,0 +1,47 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuCopy + : public FuPoor +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + + FuCopy ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fucushow.hxx b/sd/source/ui/inc/fucushow.hxx new file mode 100644 index 000000000..005aea218 --- /dev/null +++ b/sd/source/ui/inc/fucushow.hxx @@ -0,0 +1,45 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuCustomShowDlg + : public FuPoor +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuCustomShowDlg ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fudraw.hxx b/sd/source/ui/inc/fudraw.hxx new file mode 100644 index 000000000..e1d25a521 --- /dev/null +++ b/sd/source/ui/inc/fudraw.hxx @@ -0,0 +1,85 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +struct SdrViewEvent; +class SdrObject; + +namespace sd { + +/** + * Base class for all Draw specific functions + */ +class FuDraw + : public FuPoor +{ +public: + + virtual bool KeyInput(const KeyEvent& rKEvt) override; + virtual bool MouseMove(const MouseEvent& rMEvt) override; + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + virtual bool RequestHelp(const HelpEvent& rHEvt) override; + + virtual void Activate() override; + + virtual void ForcePointer(const MouseEvent* pMEvt = nullptr); + + virtual void DoubleClick(const MouseEvent& rMEvt); + + bool SetPointer(const SdrObject* pObj, const Point& rPos); + bool SetHelpText(const SdrObject* pObj, const Point& rPos, const SdrViewEvent& rVEvt); + + void SetPermanent(bool bSet) { bPermanent = bSet; } + + /** is called when the current function should be aborted. <p> + This is used when a function gets a KEY_ESCAPE but can also + be called directly. + + @returns true if an active function was aborted + */ + virtual bool cancel() override; + +protected: + FuDraw (ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + + virtual ~FuDraw() override; + + PointerStyle aNewPointer; + PointerStyle aOldPointer; + bool bMBDown; + bool bDragHelpLine; + sal_uInt16 nHelpLine; + bool bPermanent; + +private: + void DoModifiers(const MouseEvent& rMEvt, bool bSnapModPressed); + +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fudspord.hxx b/sd/source/ui/inc/fudspord.hxx new file mode 100644 index 000000000..2c6089442 --- /dev/null +++ b/sd/source/ui/inc/fudspord.hxx @@ -0,0 +1,62 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +class SdrDropMarkerOverlay; +class SdrObject; + +namespace sd { + +class FuDisplayOrder final + : public FuPoor +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + + // Mouse- & Key-Events + virtual bool MouseMove(const MouseEvent& rMEvt) override; + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + + virtual void Activate() override; + virtual void Deactivate() override; + +private: + virtual ~FuDisplayOrder() override; + void implClearOverlay(); + + PointerStyle maPtr; + SdrObject* mpRefObj; + std::unique_ptr<SdrDropMarkerOverlay> mpOverlay; + + FuDisplayOrder ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuediglu.hxx b/sd/source/ui/inc/fuediglu.hxx new file mode 100644 index 000000000..19c32cefd --- /dev/null +++ b/sd/source/ui/inc/fuediglu.hxx @@ -0,0 +1,64 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fudraw.hxx" + +namespace sd { + +class FuEditGluePoints final + : public FuDraw +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, bool bPermanent ); + virtual void DoExecute( SfxRequest& rReq ) override; + + // Mouse- & Key-Events + virtual bool KeyInput(const KeyEvent& rKEvt) override; + virtual bool MouseMove(const MouseEvent& rMEvt) override; + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + virtual bool Command(const CommandEvent& rCEvt) override; + virtual void ReceiveRequest(SfxRequest& rReq) override; + + virtual void Activate() override; + virtual void Deactivate() override; + + //Add Shift+UP/DOWN/LEFT/RIGHT key to move the position of insert point, + //and SHIFT+ENTER key to decide the position and draw the new insert point + virtual void ForcePointer(const MouseEvent* pMEvt = nullptr) override; + +private: + bool bBeginInsertPoint; + Point oldPoint; + + FuEditGluePoints ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + virtual ~FuEditGluePoints() override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuexecuteinteraction.hxx b/sd/source/ui/inc/fuexecuteinteraction.hxx new file mode 100644 index 000000000..1fb733b55 --- /dev/null +++ b/sd/source/ui/inc/fuexecuteinteraction.hxx @@ -0,0 +1,44 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +#include <com/sun/star/media/XPlayer.hpp> + +namespace sd +{ +class FuExecuteInteraction : public FuPoor +{ +public: + static rtl::Reference<FuPoor> Create(ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, + SdDrawDocument* pDoc, SfxRequest& rReq); + virtual void DoExecute(SfxRequest& rReq) override; + +private: + FuExecuteInteraction(ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, + SdDrawDocument* pDoc, SfxRequest& rReq); + + css::uno::Reference<css::media::XPlayer> mxPlayer; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuexpand.hxx b/sd/source/ui/inc/fuexpand.hxx new file mode 100644 index 000000000..ccdffb661 --- /dev/null +++ b/sd/source/ui/inc/fuexpand.hxx @@ -0,0 +1,45 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuExpandPage + : public FuPoor +{ + public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuExpandPage ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuformatpaintbrush.hxx b/sd/source/ui/inc/fuformatpaintbrush.hxx new file mode 100644 index 000000000..0de28d4a8 --- /dev/null +++ b/sd/source/ui/inc/fuformatpaintbrush.hxx @@ -0,0 +1,61 @@ +/* -*- 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 . + */ + +#pragma once + +#include "futext.hxx" + +namespace sd { + +class DrawViewShell; + +class FuFormatPaintBrush : public FuText +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + + virtual bool MouseMove(const MouseEvent& rMEvt) override; + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + virtual bool KeyInput(const KeyEvent& rKEvt) override; + + virtual void Activate() override; + virtual void Deactivate() override; + + static void GetMenuState( DrawViewShell const & rDrawViewShell, SfxItemSet &rSet ); + +private: + FuFormatPaintBrush ( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq); + + void DoExecute( SfxRequest& rReq ) override; + + bool HasContentForThisType( SdrInventor nObjectInventor, SdrObjKind nObjectIdentifier ) const; + void Paste( bool, bool ); + + void implcancel(); + + std::shared_ptr<SfxItemSet> mxItemSet; + bool mbPermanent; + bool mbOldIsQuickTextEditMode; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuhhconv.hxx b/sd/source/ui/inc/fuhhconv.hxx new file mode 100644 index 000000000..a009b3023 --- /dev/null +++ b/sd/source/ui/inc/fuhhconv.hxx @@ -0,0 +1,58 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +class SdOutliner; + +namespace sd { + +class FuHangulHanjaConversion final : public FuPoor +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + + void StartConversion( LanguageType nSourceLanguage, LanguageType nTargetLanguage, + const vcl::Font *pTargetFont, sal_Int32 nOptions, bool bIsInteractive ); + + void StartChineseConversion(); + + void ConvertStyles( LanguageType nTargetLanguage, const vcl::Font *pTargetFont ); + +private: + virtual ~FuHangulHanjaConversion() override; + + SdOutliner* pSdOutliner; + bool bOwnOutliner; + + FuHangulHanjaConversion ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq ); + +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuinsert.hxx b/sd/source/ui/inc/fuinsert.hxx new file mode 100644 index 000000000..a2b335961 --- /dev/null +++ b/sd/source/ui/inc/fuinsert.hxx @@ -0,0 +1,112 @@ +/* -*- 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 . + */ + +#pragma once + +#include <config_features.h> +#include "fupoor.hxx" + +namespace sd { + +class FuInsertGraphic + : public FuPoor +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, + ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, + bool replaceExistingImage); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + + FuInsertGraphic ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq, + bool replaceExistingImage); + + bool mbReplaceExistingImage; +}; + +/************************************************************************/ + +class FuInsertClipboard + : public FuPoor +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuInsertClipboard ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +/************************************************************************/ + +class FuInsertOLE + : public FuPoor +{ + public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuInsertOLE ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +/************************************************************************/ + +class FuInsertAVMedia + : public FuPoor +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuInsertAVMedia ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + +#if HAVE_FEATURE_AVMEDIA + void InsertMediaURL(const OUString& rURL, const Size& rPrefSize, bool bLink); +#endif +}; +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuinsfil.hxx b/sd/source/ui/inc/fuinsfil.hxx new file mode 100644 index 000000000..f29b0764d --- /dev/null +++ b/sd/source/ui/inc/fuinsfil.hxx @@ -0,0 +1,60 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" +#include <vector> +#include <rtl/ustring.hxx> + +class SfxMedium; + +namespace sd { + +class FuInsertFile + : public FuPoor +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + + static void GetSupportedFilterVector( ::std::vector< OUString >& rFilterVector ); + +private: + FuInsertFile ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + + OUString aLayoutName; ///< layout name of the currently inserted page + OUString aFilterName; ///< chosen file filter + OUString aFile; ///< chosen file name + + void InsTextOrRTFinOlMode(SfxMedium* pMedium); + bool InsSDDinOlMode(SfxMedium* pMedium); + void InsTextOrRTFinDrMode(SfxMedium* pMedium); + bool InsSDDinDrMode(SfxMedium* pMedium); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuline.hxx b/sd/source/ui/inc/fuline.hxx new file mode 100644 index 000000000..459ff83ee --- /dev/null +++ b/sd/source/ui/inc/fuline.hxx @@ -0,0 +1,49 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuLine + : public FuPoor +{ +public: + + virtual void Activate() override; + virtual void Deactivate() override; + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + + FuLine ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fulinend.hxx b/sd/source/ui/inc/fulinend.hxx new file mode 100644 index 000000000..ea17a559a --- /dev/null +++ b/sd/source/ui/inc/fulinend.hxx @@ -0,0 +1,49 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuLineEnd + : public FuPoor +{ +public: + + virtual void Activate() override; + virtual void Deactivate() override; + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + + FuLineEnd ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fulink.hxx b/sd/source/ui/inc/fulink.hxx new file mode 100644 index 000000000..38c9d0c07 --- /dev/null +++ b/sd/source/ui/inc/fulink.hxx @@ -0,0 +1,46 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuLink + : public FuPoor +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + + FuLink ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq ); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fumeasur.hxx b/sd/source/ui/inc/fumeasur.hxx new file mode 100644 index 000000000..1900dba17 --- /dev/null +++ b/sd/source/ui/inc/fumeasur.hxx @@ -0,0 +1,46 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuMeasureDlg + : public FuPoor +{ + public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + + FuMeasureDlg ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fumorph.hxx b/sd/source/ui/inc/fumorph.hxx new file mode 100644 index 000000000..2d896b8eb --- /dev/null +++ b/sd/source/ui/inc/fumorph.hxx @@ -0,0 +1,90 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" +#include <basegfx/polygon/b2dpolypolygon.hxx> + +#include <vector> + +namespace basegfx { + class B2DPolygon; + class B2DPoint; +} + +namespace sd { + +class FuMorph + : public FuPoor +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + typedef ::std::vector< ::basegfx::B2DPolyPolygon > B2DPolyPolygonList_impl; + + FuMorph ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + + void ImpInsertPolygons( + B2DPolyPolygonList_impl& rPolyPolyList3D, + bool bAttributeFade, + const SdrObject* pObj1, + const SdrObject* pObj2 + ); + + static ::basegfx::B2DPolyPolygon ImpCreateMorphedPolygon( + const ::basegfx::B2DPolyPolygon& rPolyPolyStart, + const ::basegfx::B2DPolyPolygon& rPolyPolyEnd, + double fMorphingFactor + ); + + static void ImpMorphPolygons( + const ::basegfx::B2DPolyPolygon& rPolyPoly1, + const ::basegfx::B2DPolyPolygon& rPolyPoly2, + const sal_uInt16 nSteps, + B2DPolyPolygonList_impl& rPolyPolyList3D + ); + + static void ImpAddPolys( + ::basegfx::B2DPolyPolygon& rSmaller, + const ::basegfx::B2DPolyPolygon& rBigger + ); + + static void ImpEqualizePolyPointCount( + ::basegfx::B2DPolygon& rSmall, + const ::basegfx::B2DPolygon& rBig + ); + + static sal_uInt32 ImpGetNearestIndex( + const ::basegfx::B2DPolygon& rPoly, + const ::basegfx::B2DPoint& rPos + ); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/funavig.hxx b/sd/source/ui/inc/funavig.hxx new file mode 100644 index 000000000..d717ce202 --- /dev/null +++ b/sd/source/ui/inc/funavig.hxx @@ -0,0 +1,46 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuNavigation + : public FuPoor +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuNavigation ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuoaprms.hxx b/sd/source/ui/inc/fuoaprms.hxx new file mode 100644 index 000000000..09e69c8d1 --- /dev/null +++ b/sd/source/ui/inc/fuoaprms.hxx @@ -0,0 +1,46 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuObjectAnimationParameters + : public FuPoor +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + + FuObjectAnimationParameters ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuolbull.hxx b/sd/source/ui/inc/fuolbull.hxx new file mode 100644 index 000000000..1ed73cf91 --- /dev/null +++ b/sd/source/ui/inc/fuolbull.hxx @@ -0,0 +1,62 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +class SdDrawDocument; +class SfxRequest; +class SfxItemSet; +class SfxPoolItem; +class SvxNumBulletItem; + +namespace sd { + +class View; +class ViewShell; + +/** + * bullet functions in outline mode + */ + +class FuBulletAndPosition + : public FuPoor +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuBulletAndPosition ( + ViewShell* pViewShell, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + + void SetCurrentBulletsNumbering(SfxRequest& rReq); + + const SvxNumBulletItem* GetNumBulletItem(SfxItemSet& aNewAttr, TypedWhichId<SvxNumBulletItem>& nNumItemId); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuoltext.hxx b/sd/source/ui/inc/fuoltext.hxx new file mode 100644 index 000000000..288bcf190 --- /dev/null +++ b/sd/source/ui/inc/fuoltext.hxx @@ -0,0 +1,76 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +class SdDrawDocument; +class SfxRequest; + +namespace sd { + +class View; +class ViewShell; +class OutlineView; +class OutlineViewShell; + +/** + * text functions in outline mode + */ +class FuOutlineText final + : public FuPoor +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + + virtual bool Command(const CommandEvent& rCEvt) override; + + virtual bool KeyInput(const KeyEvent& rKEvt) override; + virtual bool MouseMove(const MouseEvent& rMEvt) override; + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + + virtual void DoCut() override; + virtual void DoCopy() override; + virtual void DoPaste() override; + virtual void DoPasteUnformatted() override; + + /** Call this method when the text in the outliner (may) has changed. + It will invalidate some slots of the view frame and update the + preview in the slide sorter. + */ + void UpdateForKeyPress (const KeyEvent& rEvent); + +private: + FuOutlineText ( + ViewShell* pViewShell, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + + OutlineViewShell* pOutlineViewShell; + OutlineView* pOutlineView; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fupage.hxx b/sd/source/ui/inc/fupage.hxx new file mode 100644 index 000000000..8540d80d7 --- /dev/null +++ b/sd/source/ui/inc/fupage.hxx @@ -0,0 +1,73 @@ +/* -*- 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 . + */ + +#pragma once + +#include <memory> +#include "fupoor.hxx" +#include <vcl/weld.hxx> + +class SfxItemSet; +class SdBackgroundObjUndoAction; +class SdPage; + +namespace sd { +class DrawViewShell; + +class FuPage + : public FuPoor +{ + public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + + virtual void Activate() override; + virtual void Deactivate() override; + + const SfxItemSet* ExecuteDialog(weld::Window* pParent, const SfxRequest& rReq); + +protected: + virtual ~FuPage() override; + +private: + FuPage ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq ); + + void ApplyItemSet( const SfxItemSet* pArgs ); + + SfxRequest& mrReq; + const SfxItemSet* mpArgs; + std::unique_ptr<SdBackgroundObjUndoAction> + mpBackgroundObjUndoAction; + Size maSize; + bool mbPageBckgrdDeleted; + bool mbMasterPage; + bool mbDisplayBackgroundTabPage; + SdPage* mpPage; + DrawViewShell* mpDrawViewShell; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuparagr.hxx b/sd/source/ui/inc/fuparagr.hxx new file mode 100644 index 000000000..559dd0de6 --- /dev/null +++ b/sd/source/ui/inc/fuparagr.hxx @@ -0,0 +1,48 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuParagraph + : public FuPoor +{ +public: + + virtual void Activate() override; + virtual void Deactivate() override; + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuParagraph ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fupoor.hxx b/sd/source/ui/inc/fupoor.hxx new file mode 100644 index 000000000..553f6688d --- /dev/null +++ b/sd/source/ui/inc/fupoor.hxx @@ -0,0 +1,180 @@ +/* -*- 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 . + */ + +#pragma once + +#include <vcl/timer.hxx> +#include <tools/link.hxx> +#include <tools/gen.hxx> +#include <vcl/vclptr.hxx> +#include <svx/svdobj.hxx> + +#include <helper/simplereferencecomponent.hxx> + +class SdDrawDocument; +class SfxRequest; +class CommandEvent; +class HelpEvent; +class KeyEvent; +class MouseEvent; + +namespace sd { + +class DrawDocShell; +class View; +class ViewShell; +class Window; + +/** + * Base class for all functions + */ +class FuPoor : public SimpleReferenceComponent +{ +public: + static const int HITPIX = 2; // hit tolerance in pixel + static const int HITLOG = 53; // hit tolerance in mm100 + static const int DRGPIX = 2; // minimal drag move in pixel + static const int DRGLOG = 53; // minimal drag move in mm100 + + + virtual void DoExecute( SfxRequest& rReq ); + + void SetMouseButtonCode(sal_uInt16 nNew) { if(nNew != mnCode) mnCode = nNew; } + sal_uInt16 GetMouseButtonCode() const { return mnCode; } + + DrawDocShell* GetDocSh() { return mpDocSh; } + + virtual void DoCut(); + virtual void DoCopy(); + virtual void DoPaste(); + virtual void DoPasteUnformatted(); + + // mouse & key events; return value = sal_True: event has been handled + virtual bool KeyInput(const KeyEvent& rKEvt); + virtual bool MouseMove(const MouseEvent& ); + virtual bool MouseButtonUp(const MouseEvent& rMEvt); + + // moved from inline to *.cxx + virtual bool MouseButtonDown(const MouseEvent& rMEvt); + + virtual bool Command(const CommandEvent& rCEvt); + virtual bool RequestHelp(const HelpEvent& rHEvt); + virtual void ReceiveRequest(SfxRequest& rReq); + + virtual void Activate(); ///< activates the function + virtual void Deactivate(); ///< deactivates the function + + void SetWindow(::sd::Window* pWin); + + virtual void SelectionHasChanged(); + + sal_uInt16 GetSlotID() const { return nSlotId; } + + void StartDelayToScrollTimer (); + + virtual SdrObjectUniquePtr CreateDefaultObject(const sal_uInt16 nID, const ::tools::Rectangle& rRectangle); + + /** is called when the current function should be aborted. <p> + This is used when a function gets a KEY_ESCAPE but can also + be called directly. + + @returns true if an active function was aborted + */ + virtual bool cancel(); + + // #i33136# + /** Decide if the object to be created should be created + orthogonal. Default implementation uses nSlotID + to decide. May be overridden to use other criteria + for this decision + + @returns true if the to be created object should be orthogonal. + */ + virtual bool doConstructOrthogonal() const; + +protected: + /** + @param pViewSh + May be NULL. + */ + FuPoor (ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + virtual ~FuPoor() override; + + DECL_LINK( DelayHdl, Timer *, void ); + + static void ImpForceQuadratic(::tools::Rectangle& rRect); + + /** Switch to another layer. The layer to switch to is specified by an + offset relative to the active layer. With respect to the layer bar + control at the lower left of the document window positive values + move to the right and negative values move to the left. + + <p>Switching the layer is independent of the view's layer mode. The + layers are switched even when the layer mode is turned off and the + layer control is not visible.</p> + @param nOffset + If the offset is positive skip that many layers in selecting the + next layer. If it is negative then select a previous one. An + offset or zero does not change the current layer. If the + resulting index lies outside the valid range of indices then it + is set to either the minimal or maximal valid index, whichever + is nearer. + */ + void SwitchLayer (sal_Int32 nOffset); + + ::sd::View* mpView; + ViewShell* mpViewShell; + VclPtr< ::sd::Window> mpWindow; + DrawDocShell* mpDocSh; + SdDrawDocument* mpDoc; + + sal_uInt16 nSlotId; + + Timer aScrollTimer; ///< for auto-scrolling + DECL_LINK( ScrollHdl, Timer *, void ); + void ForceScroll(const Point& aPixPos); + + Timer aDragTimer; ///< for Drag&Drop + DECL_LINK(DragHdl, Timer *, void); + bool bIsInDragMode; + Point aMDPos; ///< position of MouseButtonDown + + /// Flag to prevent auto-scrolling until one drags from outside into the window + bool bNoScrollUntilInside; + + /// timer to delay scrolling (~ 1 sec) when dragging out of the window + Timer aDelayToScrollTimer; + bool bScrollable; + bool bDelayActive; + bool bFirstMouseMove; + + /// member to hold state of the mouse buttons for creation of own MouseEvents (like in ScrollHdl) + +private: + sal_uInt16 mnCode; + +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuprlout.hxx b/sd/source/ui/inc/fuprlout.hxx new file mode 100644 index 000000000..183ea124f --- /dev/null +++ b/sd/source/ui/inc/fuprlout.hxx @@ -0,0 +1,51 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +class SdDrawDocument; +class SfxRequest; + +namespace sd { + +class View; +class ViewShell; + +class FuPresentationLayout + : public FuPoor +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuPresentationLayout ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuprobjs.hxx b/sd/source/ui/inc/fuprobjs.hxx new file mode 100644 index 000000000..732c73526 --- /dev/null +++ b/sd/source/ui/inc/fuprobjs.hxx @@ -0,0 +1,51 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +class SdDrawDocument; +class SfxRequest; + +namespace sd { + +class View; +class ViewShell; + +class FuPresentationObjects + : public FuPoor +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuPresentationObjects ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuscale.hxx b/sd/source/ui/inc/fuscale.hxx new file mode 100644 index 000000000..9b70492b8 --- /dev/null +++ b/sd/source/ui/inc/fuscale.hxx @@ -0,0 +1,45 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuScale + : public FuPoor +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuScale ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fusearch.hxx b/sd/source/ui/inc/fusearch.hxx new file mode 100644 index 000000000..7088dd776 --- /dev/null +++ b/sd/source/ui/inc/fusearch.hxx @@ -0,0 +1,56 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +class SvxSearchItem; +class SdOutliner; + +namespace sd { + +class FuSearch final : public FuPoor +{ +public: + + static FuSearch* createPtr(ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq); + + virtual void DoExecute( SfxRequest& rReq ) override; + + void SearchAndReplace( const SvxSearchItem* pSearchItem ); + +private: + virtual ~FuSearch() override; + + SdOutliner* m_pSdOutliner; + bool m_bOwnOutliner; + + FuSearch ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fusel.hxx b/sd/source/ui/inc/fusel.hxx new file mode 100644 index 000000000..3896a6d42 --- /dev/null +++ b/sd/source/ui/inc/fusel.hxx @@ -0,0 +1,104 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fudraw.hxx" + +namespace com::sun::star::media { class XPlayer; } + +class SdrHdl; +class SdrObject; + +namespace sd { + +class FuSelection final + : public FuDraw +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + + // Mouse- & Key-Events + virtual bool KeyInput(const KeyEvent& rKEvt) override; + virtual bool MouseMove(const MouseEvent& rMEvt) override; + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + + virtual void Activate() override; + + virtual void SelectionHasChanged() override; + + void SetEditMode(sal_uInt16 nMode); + sal_uInt16 GetEditMode() const { return nEditMode; } + + bool HandleImageMapClick(const SdrObject* pObj, const Point& rPos); + + /** is called when the current function should be aborted. <p> + This is used when a function gets a KEY_ESCAPE but can also + be called directly. + + @returns true if an active function was aborted + */ + virtual bool cancel() override; + + //let mouse cursor move + virtual void ForcePointer(const MouseEvent* pMEvt = nullptr) override; + +private: + FuSelection (ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + + virtual ~FuSelection() override; + + bool bTempRotation; + bool bSelectionChanged; + SdrHdl* pHdl; + bool bSuppressChangesOfSelection; + bool bMirrorSide0; + sal_uInt16 nEditMode; + + /** This pointer stores a candidate for assigning a style in the water + can mode between mouse button down and mouse button up. + */ + SdrObject* pWaterCanCandidate; + + /** Find the object under the given test point without selecting it. + @param rTestPoint + The coordinates at which to search for a shape. + @return + The shape at the test point. When there is no shape at this + position then NULL is returned. + */ + SdrObject* pickObject (const Point& rTestPoint); + //Add Shift+UP/DOWN/LEFT/RIGHT key to move the position of insert point, + //and SHIFT+ENTER key to decide the position and draw the new insert point + bool bBeginInsertPoint; + Point oldPoint; + //let mouse cursor move + bool bMovedToCenterPoint; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fusldlg.hxx b/sd/source/ui/inc/fusldlg.hxx new file mode 100644 index 000000000..89f0e69ad --- /dev/null +++ b/sd/source/ui/inc/fusldlg.hxx @@ -0,0 +1,45 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuSlideShowDlg + : public FuPoor +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuSlideShowDlg ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fusnapln.hxx b/sd/source/ui/inc/fusnapln.hxx new file mode 100644 index 000000000..aba91f696 --- /dev/null +++ b/sd/source/ui/inc/fusnapln.hxx @@ -0,0 +1,48 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuSnapLine + : public FuPoor +{ +public: + + virtual void Activate() override; + virtual void Deactivate() override; + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuSnapLine ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fusumry.hxx b/sd/source/ui/inc/fusumry.hxx new file mode 100644 index 000000000..3a20d3813 --- /dev/null +++ b/sd/source/ui/inc/fusumry.hxx @@ -0,0 +1,45 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuSummaryPage + : public FuPoor +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuSummaryPage ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/futempl.hxx b/sd/source/ui/inc/futempl.hxx new file mode 100644 index 000000000..e51447f7b --- /dev/null +++ b/sd/source/ui/inc/futempl.hxx @@ -0,0 +1,48 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuTemplate + : public FuPoor +{ +public: + + virtual void Activate() override; + virtual void Deactivate() override; + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuTemplate ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/futext.hxx b/sd/source/ui/inc/futext.hxx new file mode 100644 index 000000000..f101e07a0 --- /dev/null +++ b/sd/source/ui/inc/futext.hxx @@ -0,0 +1,97 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fuconstr.hxx" +#include <tools/weakbase.h> + +class SdrTextObj; +class FontList; +class OutlinerView; + +namespace sd { + +/** + * Base class for text functions + */ +class FuText + : public FuConstruct +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + + virtual bool KeyInput(const KeyEvent& rKEvt) override; + virtual bool MouseMove(const MouseEvent& rMEvt) override; + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + virtual bool RequestHelp(const HelpEvent& rHEvt) override; + virtual void ReceiveRequest(SfxRequest& rReq) override; + virtual void DoubleClick(const MouseEvent& rMEvt) override; + + virtual void Activate() override; ///< activates the function + virtual void Deactivate() override; ///< deactivates the function + + void SetInEditMode(const MouseEvent& rMEvt, bool bQuickDrag); + void DeleteDefaultText(); + SdrTextObj* GetTextObj() { return mxTextObj.get(); } + + virtual SdrObjectUniquePtr CreateDefaultObject(const sal_uInt16 nID, const ::tools::Rectangle& rRectangle) override; + + /** is called when the current function should be aborted. <p> + This is used when a function gets a KEY_ESCAPE but can also + be called directly. + + @returns true if an active function was aborted + */ + virtual bool cancel() override; + + static void ChangeFontSize( bool, OutlinerView*, const FontList*, ::sd::View* ); + + void InvalidateBindings(); + + +protected: + FuText (ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + +private: + virtual void disposing() override; + + ::tools::WeakReference<SdrTextObj> + mxTextObj; + bool bFirstObjCreated; + bool bJustEndedEdit; + + SfxRequest& rRequest; + + void ImpSetAttributesForNewTextObject(SdrTextObj* pTxtObj); + void ImpSetAttributesFitToSize(SdrTextObj* pTxtObj); + void ImpSetAttributesFitToSizeVertical(SdrTextObj* pTxtObj); + void ImpSetAttributesFitCommon(SdrTextObj* pTxtObj); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/futhes.hxx b/sd/source/ui/inc/futhes.hxx new file mode 100644 index 000000000..3b9533ddd --- /dev/null +++ b/sd/source/ui/inc/futhes.hxx @@ -0,0 +1,45 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuThesaurus + : public FuPoor +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuThesaurus ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/futransf.hxx b/sd/source/ui/inc/futransf.hxx new file mode 100644 index 000000000..dd7ae19a0 --- /dev/null +++ b/sd/source/ui/inc/futransf.hxx @@ -0,0 +1,45 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuTransform + : public FuPoor +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuTransform ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/futxtatt.hxx b/sd/source/ui/inc/futxtatt.hxx new file mode 100644 index 000000000..584e59c0d --- /dev/null +++ b/sd/source/ui/inc/futxtatt.hxx @@ -0,0 +1,45 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuTextAttrDlg + : public FuPoor +{ + public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuTextAttrDlg ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuvect.hxx b/sd/source/ui/inc/fuvect.hxx new file mode 100644 index 000000000..0501fb224 --- /dev/null +++ b/sd/source/ui/inc/fuvect.hxx @@ -0,0 +1,46 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuVectorize + : public FuPoor +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + + FuVectorize ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuzoom.hxx b/sd/source/ui/inc/fuzoom.hxx new file mode 100644 index 000000000..54c352dd3 --- /dev/null +++ b/sd/source/ui/inc/fuzoom.hxx @@ -0,0 +1,64 @@ +/* -*- 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 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +extern const sal_uInt16 SidArrayZoom[]; + +class FuZoom final + : public FuPoor +{ +public: + + static rtl::Reference<FuPoor> Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + + // Mouse- & Key-Events + virtual bool MouseMove(const MouseEvent& rMEvt) override; + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + + virtual void Activate() override; ///< activates the function + virtual void Deactivate() override; ///< deactivates the function + +private: + virtual ~FuZoom() override; + + Point aBeginPosPix; + Point aBeginPos; + Point aEndPos; + ::tools::Rectangle aZoomRect; + bool bVisible; + bool bStartDrag; + PointerStyle aPtr; + + FuZoom ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/gluectrl.hxx b/sd/source/ui/inc/gluectrl.hxx new file mode 100644 index 000000000..58aa13944 --- /dev/null +++ b/sd/source/ui/inc/gluectrl.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 . + */ + +#pragma once + +#include <vcl/InterimItemWindow.hxx> +#include <sfx2/tbxctrl.hxx> + +enum class SdrEscapeDirection; + +/** + * GluePointEscDirLB + */ +class GlueEscDirLB final : public InterimItemWindow +{ +private: + css::uno::Reference<css::frame::XFrame> m_xFrame; + std::unique_ptr<weld::ComboBox> m_xWidget; + + DECL_LINK(SelectHdl, weld::ComboBox&, void); + DECL_LINK(KeyInputHdl, const KeyEvent&, bool); + +public: + GlueEscDirLB(vcl::Window* pParent, const css::uno::Reference<css::frame::XFrame>& rFrame); + virtual void dispose() override; + virtual ~GlueEscDirLB() override; + + void set_active(int nPos) { m_xWidget->set_active(nPos); } + void set_sensitive(bool bSensitive); + + void Fill(); +}; + +/** + * Toolbox controller for glue-point escape direction + */ +class SdTbxCtlGlueEscDir : public SfxToolBoxControl +{ +private: + static sal_uInt16 GetEscDirPos(SdrEscapeDirection nEscDir); + +public: + virtual void StateChangedAtToolBoxControl(sal_uInt16 nSId, SfxItemState eState, + const SfxPoolItem* pState) override; + virtual VclPtr<InterimItemWindow> CreateItemWindow(vcl::Window* pParent) override; + + SFX_DECL_TOOLBOX_CONTROL(); + + SdTbxCtlGlueEscDir(sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/headerfooterdlg.hxx b/sd/source/ui/inc/headerfooterdlg.hxx new file mode 100644 index 000000000..e794712d3 --- /dev/null +++ b/sd/source/ui/inc/headerfooterdlg.hxx @@ -0,0 +1,70 @@ +/* -*- 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 . + */ +#pragma once + +#include <vcl/weld.hxx> + +#include <sdpage.hxx> + +class SdUndoGroup; + +namespace sd +{ +class ViewShell; + +class HeaderFooterTabPage; + +class HeaderFooterDialog : public weld::GenericDialogController +{ +private: + DECL_LINK( ActivatePageHdl, const OString&, void ); + DECL_LINK( ClickApplyToAllHdl, weld::Button&, void ); + DECL_LINK( ClickApplyHdl, weld::Button&, void ); + DECL_LINK( ClickCancelHdl, weld::Button&, void ); + + HeaderFooterSettings maSlideSettings; + HeaderFooterSettings maNotesHandoutSettings; + + SdDrawDocument* mpDoc; + SdPage* mpCurrentPage; + ViewShell* mpViewShell; + + std::unique_ptr<weld::Notebook> mxTabCtrl; + std::unique_ptr<weld::Button> mxPBApplyToAll; + std::unique_ptr<weld::Button> mxPBApply; + std::unique_ptr<weld::Button> mxPBCancel; + std::unique_ptr<HeaderFooterTabPage> mxSlideTabPage; + std::unique_ptr<HeaderFooterTabPage> mxNotesHandoutsTabPage; + + void apply( bool bToAll, bool bForceSlides ); + void change( SdUndoGroup* pUndoGroup, SdPage* pPage, const HeaderFooterSettings& rNewSettings ); + +public: + HeaderFooterDialog(ViewShell* pViewShell, weld::Window* pParent, SdDrawDocument* pDoc, SdPage* pCurrentPage); + virtual ~HeaderFooterDialog() override; + + void ApplyToAll(); + void Apply(); + + virtual short run() override; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/ins_paste.hxx b/sd/source/ui/inc/ins_paste.hxx new file mode 100644 index 000000000..5031d09b0 --- /dev/null +++ b/sd/source/ui/inc/ins_paste.hxx @@ -0,0 +1,37 @@ +/* -*- 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 . + */ + +#pragma once + +#include <vcl/weld.hxx> + +// SdInsertPasteDlg +class SdInsertPasteDlg : public weld::GenericDialogController +{ +private: + std::unique_ptr<weld::RadioButton> m_xRbBefore; + std::unique_ptr<weld::RadioButton> m_xRbAfter; + +public: + SdInsertPasteDlg(weld::Window* pWindow); + virtual ~SdInsertPasteDlg() override; + bool IsInsertBefore() const; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/inspagob.hxx b/sd/source/ui/inc/inspagob.hxx new file mode 100644 index 000000000..d906a10f7 --- /dev/null +++ b/sd/source/ui/inc/inspagob.hxx @@ -0,0 +1,57 @@ +/* -*- 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 . + */ + +#pragma once + +#include <vcl/weld.hxx> + +class SdDrawDocument; +class SdPageObjsTLV; +class SfxMedium; + +class SdInsertPagesObjsDlg : public weld::GenericDialogController +{ +private: + SfxMedium* m_pMedium; + const SdDrawDocument* m_pDoc; + const OUString& m_rName; + + std::unique_ptr<SdPageObjsTLV> m_xLbTree; + std::unique_ptr<weld::CheckButton> m_xCbxLink; + std::unique_ptr<weld::CheckButton> m_xCbxMasters; + + void Reset(); + DECL_LINK(SelectObjectHdl, weld::TreeView&, void); + +public: + SdInsertPagesObjsDlg(weld::Window* pParent, const SdDrawDocument* pDoc, + SfxMedium* pSfxMedium, const OUString& rFileName); + virtual ~SdInsertPagesObjsDlg() override; + + /** returns the list + nType == 0 -> pages + nType == 1 -> objects */ + + std::vector<OUString> GetList ( const sal_uInt16 nType ) ; + + bool IsLink() const; + bool IsRemoveUnnecessaryMasterPages() const; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/layeroptionsdlg.hxx b/sd/source/ui/inc/layeroptionsdlg.hxx new file mode 100644 index 000000000..300994937 --- /dev/null +++ b/sd/source/ui/inc/layeroptionsdlg.hxx @@ -0,0 +1,48 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sddllapi.h> + +#include <vcl/weld.hxx> + +class SfxItemSet; + +class SD_DLLPUBLIC SdInsertLayerDlg : public weld::GenericDialogController +{ +private: + std::unique_ptr<weld::Entry> m_xEdtName; + std::unique_ptr<weld::Entry> m_xEdtTitle; + std::unique_ptr<weld::TextView> m_xEdtDesc; + std::unique_ptr<weld::CheckButton> m_xCbxVisible; + std::unique_ptr<weld::CheckButton> m_xCbxPrintable; + std::unique_ptr<weld::CheckButton> m_xCbxLocked; + std::unique_ptr<weld::Widget> m_xNameFrame; + +public: + + SdInsertLayerDlg(weld::Window* pWindow, const SfxItemSet& rInAttrs, + bool bDeletable, const OUString& rStr); + virtual ~SdInsertLayerDlg() override; + + void GetAttr( SfxItemSet& rOutAttrs ); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/masterlayoutdlg.hxx b/sd/source/ui/inc/masterlayoutdlg.hxx new file mode 100644 index 000000000..0acbb18d7 --- /dev/null +++ b/sd/source/ui/inc/masterlayoutdlg.hxx @@ -0,0 +1,61 @@ +/* -*- 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 . + */ +#pragma once + +#include <vcl/weld.hxx> + +#include <pres.hxx> + +class SdDrawDocument; +class SdPage; + +namespace sd +{ + +class MasterLayoutDialog : public weld::GenericDialogController +{ +private: + SdDrawDocument* mpDoc; + SdPage* mpCurrentPage; + + std::unique_ptr<weld::CheckButton> mxCBDate; + std::unique_ptr<weld::CheckButton> mxCBPageNumber; + std::unique_ptr<weld::CheckButton> mxCBSlideNumber; + std::unique_ptr<weld::CheckButton> mxCBHeader; + std::unique_ptr<weld::CheckButton> mxCBFooter; + + bool mbOldHeader; + bool mbOldFooter; + bool mbOldDate; + bool mbOldPageNumber; + + void applyChanges(); + void remove( PresObjKind eKind ); + void create( PresObjKind eKind ); + +public: + MasterLayoutDialog(weld::Window* pParent, SdDrawDocument* pDoc, SdPage* pCurrentPage); + virtual ~MasterLayoutDialog() override; + + virtual short run() override; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/morphdlg.hxx b/sd/source/ui/inc/morphdlg.hxx new file mode 100644 index 000000000..77b20d718 --- /dev/null +++ b/sd/source/ui/inc/morphdlg.hxx @@ -0,0 +1,49 @@ +/* -*- 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 . + */ + +#pragma once + +#include <vcl/weld.hxx> + +class SdrObject; + +namespace sd { + +class MorphDlg : public weld::GenericDialogController +{ +public: + MorphDlg(weld::Window* pParent, const SdrObject* pObj1, const SdrObject* pObj2); + virtual ~MorphDlg() override; + + void SaveSettings() const; + sal_uInt16 GetFadeSteps() const { return static_cast<sal_uInt16>(m_xMtfSteps->get_value()); } + bool IsAttributeFade() const { return m_xCbxAttributes->get_active(); } + bool IsOrientationFade() const { return m_xCbxOrientation->get_active(); } + +private: + std::unique_ptr<weld::SpinButton> m_xMtfSteps; + std::unique_ptr<weld::CheckButton> m_xCbxAttributes; + std::unique_ptr<weld::CheckButton> m_xCbxOrientation; + + void LoadSettings(); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/navigatr.hxx b/sd/source/ui/inc/navigatr.hxx new file mode 100644 index 000000000..0e500eb0f --- /dev/null +++ b/sd/source/ui/inc/navigatr.hxx @@ -0,0 +1,205 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/ctrlitem.hxx> +#include <sfx2/sidebar/PanelLayout.hxx> +#include <sfx2/navigat.hxx> +#include "sdtreelb.hxx" +#include <pres.hxx> + +// forward +namespace vcl { class Window; } + +namespace sd { +class DrawDocShell; +class SdNavigatorFloat; +} +class Menu; +class SdNavigatorControllerItem; +class SdPageNameControllerItem; + +enum class NavState { + NONE = 0x000000, + TableUpdate = 0x000100, + BtnFirstEnabled = 0x001000, + BtnFirstDisabled = 0x002000, + BtnPrevEnabled = 0x004000, + BtnPrevDisabled = 0x008000, + BtnLastEnabled = 0x010000, + BtnLastDisabled = 0x020000, + BtnNextEnabled = 0x040000, + BtnNextDisabled = 0x080000, +}; +namespace o3tl { + template<> struct typed_flags<NavState> : is_typed_flags<NavState, 0x0ff100> {}; +} + +class NavDocInfo +{ +public: + NavDocInfo() + : bName(false) + , bActive(false) + , mpDocShell(nullptr) + { + } + + bool HasName() const { return bName; } + bool IsActive() const { return bActive; } + + void SetName( bool bOn ) { bName = bOn; } + void SetActive( bool bOn ) { bActive = bOn; } + + ::sd::DrawDocShell* GetDrawDocShell() {return mpDocShell;} + +private: + friend class SdNavigatorWin; + bool bName : 1; + bool bActive : 1; + ::sd::DrawDocShell* mpDocShell; +}; + +namespace sd { + +class SdNavigatorFloat : public SfxNavigator +{ +private: + std::unique_ptr<SdNavigatorWin> m_xNavWin; + bool m_bSetInitialFocusOnActivate; + +public: + SdNavigatorFloat(SfxBindings* _pBindings, SfxChildWindow* pMgr, + vcl::Window* pParent, SfxChildWinInfo* pInfo); + void InitTreeLB(const SdDrawDocument* pDoc); + void FreshTree(const SdDrawDocument* pDoc); + virtual void Activate() override; + virtual void dispose() override; + virtual ~SdNavigatorFloat() override; +}; + +} + +class SD_DLLPUBLIC SdNavigatorWin : public PanelLayout +{ +public: + typedef ::std::function<void ()> UpdateRequestFunctor; + + /** Create a new instance of the navigator. + @param bUseActiveUpdate + When <TRUE/>, the default, then the SdNavigatorWin object + will make a SID_NAVIGATOR_INIT call whenever it thinks an + update is necessary. When <FALSE/> the navigator will + rely on others to trigger updates. + */ + SdNavigatorWin(weld::Widget* pParent, SfxBindings* pBindings, SfxNavigator* pNavigatorDlg); + void SetUpdateRequestFunctor(const UpdateRequestFunctor& rUpdateRequest); + virtual ~SdNavigatorWin() override; + + void InitTreeLB( const SdDrawDocument* pDoc ); + void RefreshDocumentLB( const OUString* pDocName = nullptr ); + void FirstFocus(); + + bool InsertFile(const OUString& rFileName); + + NavigatorDragType GetNavigatorDragType(); + SdPageObjsTLV& GetObjects(); + +private: + friend class SdNavigatorFloat; + friend class SdNavigatorControllerItem; + friend class SdPageNameControllerItem; + + std::unique_ptr<weld::Toolbar> mxToolbox; + std::unique_ptr<SdPageObjsTLV> mxTlbObjects; + std::unique_ptr<weld::ComboBox> mxLbDocs; + std::unique_ptr<weld::Menu> mxDragModeMenu; + std::unique_ptr<weld::Menu> mxShapeMenu; + + VclPtr<SfxNavigator> mxNavigatorDlg; + + bool mbDocImported; + OUString maDropFileName; + NavigatorDragType meDragType; + std::vector<NavDocInfo> maDocList; + SfxBindings* mpBindings; + std::unique_ptr<SdNavigatorControllerItem> mpNavigatorCtrlItem; + std::unique_ptr<SdPageNameControllerItem> mpPageNameCtrlItem; + + /** This flag controls whether all shapes or only the named shapes are + shown. + */ + // bool mbShowAllShapes; + + static OUString GetDragTypeSdBmpId(NavigatorDragType eDT); + NavDocInfo* GetDocInfo(); + + DECL_DLLPRIVATE_LINK( SelectToolboxHdl, const OString&, void ); + DECL_DLLPRIVATE_LINK( DropdownClickToolBoxHdl, const OString&, void ); + DECL_DLLPRIVATE_LINK( ClickObjectHdl, weld::TreeView&, bool ); + DECL_DLLPRIVATE_LINK( SelectDocumentHdl, weld::ComboBox&, void ); + DECL_DLLPRIVATE_LINK( MenuSelectHdl, const OString&, void ); + DECL_DLLPRIVATE_LINK( ShapeFilterCallback, const OString&, void ); + DECL_DLLPRIVATE_LINK( KeyInputHdl, const KeyEvent&, bool ); + + void SetDragImage(); + +public: + //when object is marked , fresh the corresponding entry tree . + void FreshTree ( const SdDrawDocument* pDoc ); + + virtual weld::Window* GetFrameWeld() const override; +}; + +/** + * ControllerItem for Navigator + */ +class SdNavigatorControllerItem : public SfxControllerItem +{ +public: + SdNavigatorControllerItem( sal_uInt16, SdNavigatorWin*, SfxBindings*, + const SdNavigatorWin::UpdateRequestFunctor& rUpdateRequest); + +protected: + virtual void StateChangedAtToolBoxControl( sal_uInt16 nSId, SfxItemState eState, + const SfxPoolItem* pState ) override; + +private: + SdNavigatorWin* pNavigatorWin; + const SdNavigatorWin::UpdateRequestFunctor maUpdateRequest; +}; + +/** + * ControllerItem for Navigator to show the page in the TreeLB + */ +class SdPageNameControllerItem : public SfxControllerItem +{ +public: + SdPageNameControllerItem( sal_uInt16, SdNavigatorWin*, SfxBindings*); + +protected: + virtual void StateChangedAtToolBoxControl( sal_uInt16 nSId, SfxItemState eState, + const SfxPoolItem* pState ) override; + +private: + SdNavigatorWin* pNavigatorWin; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/optsitem.hxx b/sd/source/ui/inc/optsitem.hxx new file mode 100644 index 000000000..979b90b78 --- /dev/null +++ b/sd/source/ui/inc/optsitem.hxx @@ -0,0 +1,580 @@ +/* -*- 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 . + */ + +#pragma once + +#include <unotools/configitem.hxx> +#include <sfx2/module.hxx> +#include <svx/optgrid.hxx> +#include <tools/degree.hxx> +#include <sddllapi.h> +#include <memory> + +class SdOptions; + +namespace sd { +class FrameView; +} + +class SdOptionsGeneric; + +class SD_DLLPUBLIC SdOptionsItem : public ::utl::ConfigItem +{ + +private: + + const SdOptionsGeneric& mrParent; + + virtual void ImplCommit() override; + +public: + + SdOptionsItem( const SdOptionsGeneric& rParent, const OUString& rSubTree ); + virtual ~SdOptionsItem() override; + + SdOptionsItem(SdOptionsItem const &) = default; + SdOptionsItem(SdOptionsItem &&) = default; + SdOptionsItem & operator =(SdOptionsItem const &) = delete; // due to ConfigItem + SdOptionsItem & operator =(SdOptionsItem &&) = delete; // due to ConfigItem + + virtual void Notify( const css::uno::Sequence<OUString>& aPropertyNames) override; + + css::uno::Sequence< css::uno::Any > GetProperties( const css::uno::Sequence< OUString >& rNames ); + bool PutProperties( const css::uno::Sequence< OUString >& rNames, + const css::uno::Sequence< css::uno::Any>& rValues ); + using ConfigItem::SetModified; +}; + +class SD_DLLPUBLIC SdOptionsGeneric +{ +friend class SdOptionsItem; + +private: + + OUString maSubTree; + std::unique_ptr<SdOptionsItem> + mpCfgItem; + bool mbImpress; + bool mbInit : 1; + bool mbEnableModify : 1; + + SAL_DLLPRIVATE void Commit( SdOptionsItem& rCfgItem ) const; + SAL_DLLPRIVATE css::uno::Sequence< OUString > GetPropertyNames() const; + +protected: + + void Init() const; + void OptionsChanged() { if( mpCfgItem && mbEnableModify ) mpCfgItem->SetModified(); } + +protected: + + virtual void GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const = 0; + virtual bool ReadData( const css::uno::Any* pValues ) = 0; + virtual bool WriteData( css::uno::Any* pValues ) const = 0; + +public: + + SdOptionsGeneric(bool bImpress, const OUString& rSubTree); + SdOptionsGeneric(SdOptionsGeneric const &); + virtual ~SdOptionsGeneric(); + + SdOptionsGeneric& operator=( SdOptionsGeneric const & ); + + bool IsImpress() const { return mbImpress; } + + void EnableModify( bool bModify ) { mbEnableModify = bModify; } + + void Store(); + + static bool isMetricSystem(); +}; + +class SD_DLLPUBLIC SdOptionsLayout : public SdOptionsGeneric +{ +private: + + bool bRuler; // Layout/Display/Ruler + bool bMoveOutline; // Layout/Display/Contour + bool bDragStripes; // Layout/Display/Guide + bool bHandlesBezier; // Layout/Display/Bezier + bool bHelplines; // Layout/Display/Helpline + sal_uInt16 nMetric; // Layout/Other/MeasureUnit + sal_uInt16 nDefTab; // Layout/Other/TabStop + +protected: + + virtual void GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const override; + virtual bool ReadData( const css::uno::Any* pValues ) override; + virtual bool WriteData( css::uno::Any* pValues ) const override; + +public: + SdOptionsLayout(bool bImpress, bool bUseConfig); + + bool operator==( const SdOptionsLayout& rOpt ) const; + + bool IsRulerVisible() const { Init(); return bRuler; } + bool IsMoveOutline() const { Init(); return bMoveOutline; } + bool IsDragStripes() const { Init(); return bDragStripes; } + bool IsHandlesBezier() const { Init(); return bHandlesBezier; } + bool IsHelplines() const { Init(); return bHelplines; } + sal_uInt16 GetMetric() const { Init(); return( ( 0xffff == nMetric ) ? static_cast<sal_uInt16>(SfxModule::GetCurrentFieldUnit()) : nMetric ); } + sal_uInt16 GetDefTab() const { Init(); return nDefTab; } + + void SetRulerVisible( bool bOn ) { if( bRuler != bOn ) { OptionsChanged(); bRuler = bOn; } } + void SetMoveOutline( bool bOn ) { if( bMoveOutline != bOn ) { OptionsChanged(); bMoveOutline = bOn; } } + void SetDragStripes( bool bOn ) { if( bDragStripes != bOn ) { OptionsChanged(); bDragStripes = bOn; } } + void SetHandlesBezier( bool bOn ) { if( bHandlesBezier != bOn ) { OptionsChanged(); bHandlesBezier = bOn; } } + void SetHelplines( bool bOn ) { if( bHelplines != bOn ) { OptionsChanged(); bHelplines = bOn; } } + void SetMetric( sal_uInt16 nInMetric ) { if( nMetric != nInMetric ) { OptionsChanged(); nMetric = nInMetric; } } + void SetDefTab( sal_uInt16 nTab ) { if( nDefTab != nTab ) { OptionsChanged(); nDefTab = nTab; } } +}; + +class SD_DLLPUBLIC SdOptionsLayoutItem : public SfxPoolItem +{ +public: + + explicit SdOptionsLayoutItem(); + SdOptionsLayoutItem( SdOptions const * pOpts, ::sd::FrameView const * pView ); + + virtual SdOptionsLayoutItem* Clone( SfxItemPool *pPool = nullptr ) const override; + virtual bool operator==( const SfxPoolItem& ) const override; + + void SetOptions( SdOptions* pOpts ) const; + + SdOptionsLayout& GetOptionsLayout() { return maOptionsLayout; } +private: + SdOptionsLayout maOptionsLayout; +}; + +class SdOptionsContents : public SdOptionsGeneric +{ +private: +protected: + + virtual void GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const override; + virtual bool ReadData( const css::uno::Any* pValues ) override; + virtual bool WriteData( css::uno::Any* pValues ) const override; + +public: + + SdOptionsContents(bool bImpress); + + bool operator==( const SdOptionsContents& rOpt ) const; +}; + +class SD_DLLPUBLIC SdOptionsMisc : public SdOptionsGeneric +{ +private: + + sal_Int32 nDefaultObjectSizeWidth; + sal_Int32 nDefaultObjectSizeHeight; + + bool bStartWithTemplate : 1; // Misc/NewDoc/AutoPilot + bool bMarkedHitMovesAlways : 1; // Misc/ObjectMoveable + bool bMoveOnlyDragging : 1; // Currently, not in use !!! + bool bCrookNoContortion : 1; // Misc/NoDistort + bool bQuickEdit : 1; // Misc/TextObject/QuickEditing + bool bMasterPageCache : 1; // Misc/BackgroundCache + bool bDragWithCopy : 1; // Misc/CopyWhileMoving + bool bPickThrough : 1; // Misc/TextObject/Selectable + bool bDoubleClickTextEdit : 1; // Misc/DclickTextedit + bool bClickChangeRotation : 1; // Misc/RotateClick + bool bEnableSdremote : 1; // Misc/Start/EnableSdremote + bool bEnablePresenterScreen : 1; // Misc/Start/EnablePresenterDisplay + bool bSolidDragging : 1; // Misc/ModifyWithAttributes + bool bSummationOfParagraphs : 1; // misc/SummationOfParagraphs + bool bTabBarVisible : 1; // Misc/TabBarVisible + bool bShowUndoDeleteWarning : 1; // Misc/ShowUndoDeleteWarning + // #i75315# + bool bSlideshowRespectZOrder : 1; // Misc/SlideshowRespectZOrder + bool bShowComments : 1; // Misc/ShowComments + + bool bPreviewNewEffects; + bool bPreviewChangedEffects; + bool bPreviewTransitions; + + + sal_Int32 mnDisplay; + + sal_Int32 mnPenColor; + double mnPenWidth; + + /** This value controls the device to use for formatting documents. + The currently supported values are 0 for the current printer or 1 + for the printer independent virtual device the can be retrieved from + the modules. + */ + sal_uInt16 mnPrinterIndependentLayout; // Misc/Compatibility/PrinterIndependentLayout +// Misc + +protected: + + virtual void GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const override; + virtual bool ReadData( const css::uno::Any* pValues ) override; + virtual bool WriteData( css::uno::Any* pValues ) const override; + +public: + + SdOptionsMisc(bool bImpress, bool bUseConfig); + + bool operator==( const SdOptionsMisc& rOpt ) const; + + bool IsStartWithTemplate() const { Init(); return bStartWithTemplate; } + bool IsMarkedHitMovesAlways() const { Init(); return bMarkedHitMovesAlways; } + bool IsMoveOnlyDragging() const { Init(); return bMoveOnlyDragging; } + bool IsCrookNoContortion() const { Init(); return bCrookNoContortion; } + bool IsQuickEdit() const { Init(); return bQuickEdit; } + bool IsMasterPagePaintCaching() const { Init(); return bMasterPageCache; } + bool IsDragWithCopy() const { Init(); return bDragWithCopy; } + bool IsPickThrough() const { Init(); return bPickThrough; } + bool IsDoubleClickTextEdit() const { Init(); return bDoubleClickTextEdit; } + bool IsClickChangeRotation() const { Init(); return bClickChangeRotation; } + bool IsEnableSdremote() const { Init(); return bEnableSdremote; } + bool IsEnablePresenterScreen() const { Init(); return bEnablePresenterScreen; } + bool IsSolidDragging() const { Init(); return bSolidDragging; } + bool IsSummationOfParagraphs() const { Init(); return bSummationOfParagraphs; }; + bool IsTabBarVisible() const { Init(); return bTabBarVisible; }; + + /** Return the currently selected printer independent layout mode. + @return + Returns 1 for printer independent layout enabled and 0 when it + is disabled. Other values are reserved for future use. + */ + sal_uInt16 GetPrinterIndependentLayout() const { Init(); return mnPrinterIndependentLayout; }; + bool IsShowUndoDeleteWarning() const { Init(); return bShowUndoDeleteWarning; } + bool IsSlideshowRespectZOrder() const { Init(); return bSlideshowRespectZOrder; } + sal_Int32 GetDefaultObjectSizeWidth() const { Init(); return nDefaultObjectSizeWidth; } + sal_Int32 GetDefaultObjectSizeHeight() const { Init(); return nDefaultObjectSizeHeight; } + + bool IsPreviewNewEffects() const { Init(); return bPreviewNewEffects; } + bool IsPreviewChangedEffects() const { Init(); return bPreviewChangedEffects; } + bool IsPreviewTransitions() const { Init(); return bPreviewTransitions; } + + sal_Int32 GetDisplay() const; + void SetDisplay( sal_Int32 nDisplay ); + + sal_Int32 GetPresentationPenColor() const { Init(); return mnPenColor; } + void SetPresentationPenColor( sal_Int32 nPenColor ) { if( mnPenColor != nPenColor ) { OptionsChanged(); mnPenColor = nPenColor; } } + + double GetPresentationPenWidth() const { Init(); return mnPenWidth; } + void SetPresentationPenWidth( double nPenWidth ) { if( mnPenWidth != nPenWidth ) { OptionsChanged(); mnPenWidth = nPenWidth; } } + + void SetStartWithTemplate( bool bOn ) { if( bStartWithTemplate != bOn ) { OptionsChanged(); bStartWithTemplate = bOn; } } + void SetMarkedHitMovesAlways( bool bOn ) { if( bMarkedHitMovesAlways != bOn ) { OptionsChanged(); bMarkedHitMovesAlways = bOn; } } + void SetMoveOnlyDragging( bool bOn ) { if( bMoveOnlyDragging != bOn ) { OptionsChanged(); bMoveOnlyDragging = bOn; } } + void SetCrookNoContortion( bool bOn ) { if( bCrookNoContortion != bOn ) { OptionsChanged(); bCrookNoContortion = bOn; } } + void SetQuickEdit( bool bOn ) { if( bQuickEdit != bOn ) { OptionsChanged(); bQuickEdit = bOn; } } + void SetMasterPagePaintCaching( bool bOn ) { if( bMasterPageCache != bOn ) { OptionsChanged(); bMasterPageCache = bOn; } } + void SetDragWithCopy( bool bOn ) { if( bDragWithCopy != bOn ) { OptionsChanged(); bDragWithCopy = bOn; } } + void SetPickThrough( bool bOn ) { if( bPickThrough != bOn ) { OptionsChanged(); bPickThrough = bOn; } } + void SetDoubleClickTextEdit( bool bOn ) { if( bDoubleClickTextEdit != bOn ) { OptionsChanged(); bDoubleClickTextEdit = bOn; } } + void SetClickChangeRotation( bool bOn ) { if( bClickChangeRotation != bOn ) { OptionsChanged(); bClickChangeRotation = bOn; } } + void SetEnableSdremote( bool bOn ) { if( bEnableSdremote != bOn ) { OptionsChanged(); bEnableSdremote = bOn; } } + void SetEnablePresenterScreen( bool bOn ) { if( bEnablePresenterScreen != bOn ) { OptionsChanged(); bEnablePresenterScreen = bOn; } } + void SetSummationOfParagraphs( bool bOn ){ if ( bOn != bSummationOfParagraphs ) { OptionsChanged(); bSummationOfParagraphs = bOn; } } + void SetTabBarVisible( bool bOn ){ if ( bOn != bTabBarVisible ) { OptionsChanged(); bTabBarVisible = bOn; } } + /** Set the printer independent layout mode. + @param nOn + The default value is to switch printer independent layout on, + hence the parameters name. Use 0 for turning it off. Other + values are reserved for future use. + */ + void SetPrinterIndependentLayout (sal_uInt16 nOn ){ if ( nOn != mnPrinterIndependentLayout ) { OptionsChanged(); mnPrinterIndependentLayout = nOn; } } + void SetSolidDragging( bool bOn ) { if( bSolidDragging != bOn ) { OptionsChanged(); bSolidDragging = bOn; } } + void SetShowUndoDeleteWarning( bool bOn ) { if( bShowUndoDeleteWarning != bOn ) { OptionsChanged(); bShowUndoDeleteWarning = bOn; } } + void SetSlideshowRespectZOrder( bool bOn ) { if( bSlideshowRespectZOrder != bOn ) { OptionsChanged(); bSlideshowRespectZOrder = bOn; } } + void SetDefaultObjectSizeWidth( sal_Int32 nWidth ) { if( nDefaultObjectSizeWidth != nWidth ) { OptionsChanged(); nDefaultObjectSizeWidth = nWidth; } } + void SetDefaultObjectSizeHeight( sal_Int32 nHeight ) { if( nDefaultObjectSizeHeight != nHeight ) { OptionsChanged(); nDefaultObjectSizeHeight = nHeight; } } + + void SetPreviewNewEffects( bool bOn ) { if( bPreviewNewEffects != bOn ) { OptionsChanged(); bPreviewNewEffects = bOn; } } + void SetPreviewChangedEffects( bool bOn ) { if( bPreviewChangedEffects != bOn ) { OptionsChanged(); bPreviewChangedEffects = bOn; } } + void SetPreviewTransitions( bool bOn ) { if( bPreviewTransitions != bOn ) { OptionsChanged(); bPreviewTransitions = bOn; } } + + bool IsShowComments() const { Init(); return bShowComments; } + void SetShowComments( bool bShow ) { if( bShowComments != bShow ) { OptionsChanged(); bShowComments = bShow; } } +}; + +class SD_DLLPUBLIC SdOptionsMiscItem : public SfxPoolItem +{ +public: + + explicit SdOptionsMiscItem(); + SdOptionsMiscItem( SdOptions const * pOpts, ::sd::FrameView const * pView ); + + virtual SdOptionsMiscItem* Clone( SfxItemPool *pPool = nullptr ) const override; + virtual bool operator==( const SfxPoolItem& ) const override; + + void SetOptions( SdOptions* pOpts ) const; + + SdOptionsMisc& GetOptionsMisc() { return maOptionsMisc; } + const SdOptionsMisc& GetOptionsMisc() const { return maOptionsMisc; } +private: + SdOptionsMisc maOptionsMisc; +}; + +class SD_DLLPUBLIC SdOptionsSnap : public SdOptionsGeneric +{ +private: + + bool bSnapHelplines : 1; // Snap/Object/SnapLine + bool bSnapBorder : 1; // Snap/Object/PageMargin + bool bSnapFrame : 1; // Snap/Object/ObjectFrame + bool bSnapPoints : 1; // Snap/Object/ObjectPoint + bool bOrtho : 1; // Snap/Position/CreatingMoving + bool bBigOrtho : 1; // Snap/Position/ExtendEdges + bool bRotate : 1; // Snap/Position/Rotating + sal_Int16 nSnapArea; // Snap/Object/Range + Degree100 nAngle; // Snap/Position/RotatingValue + Degree100 nBezAngle; // Snap/Position/PointReduction + +protected: + + virtual void GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const override; + virtual bool ReadData( const css::uno::Any* pValues ) override; + virtual bool WriteData( css::uno::Any* pValues ) const override; + +public: + + SdOptionsSnap(bool bImpress, bool bUseConfig); + + bool operator==( const SdOptionsSnap& rOpt ) const; + + bool IsSnapHelplines() const { Init(); return bSnapHelplines; } + bool IsSnapBorder() const { Init(); return bSnapBorder; } + bool IsSnapFrame() const { Init(); return bSnapFrame; } + bool IsSnapPoints() const { Init(); return bSnapPoints; } + bool IsOrtho() const { Init(); return bOrtho; } + bool IsBigOrtho() const { Init(); return bBigOrtho; } + bool IsRotate() const { Init(); return bRotate; } + sal_Int16 GetSnapArea() const { Init(); return nSnapArea; } + Degree100 GetAngle() const { Init(); return nAngle; } + Degree100 GetEliminatePolyPointLimitAngle() const { Init(); return nBezAngle; } + + void SetSnapHelplines( bool bOn ) { if( bSnapHelplines != bOn ) { OptionsChanged(); bSnapHelplines = bOn; } } + void SetSnapBorder( bool bOn ) { if( bSnapBorder != bOn ) { OptionsChanged(); bSnapBorder = bOn; } } + void SetSnapFrame( bool bOn ) { if( bSnapFrame != bOn ) { OptionsChanged(); bSnapFrame = bOn; } } + void SetSnapPoints( bool bOn ) { if( bSnapPoints != bOn ) { OptionsChanged(); bSnapPoints = bOn; } } + void SetOrtho( bool bOn ) { if( bOrtho != bOn ) { OptionsChanged(); bOrtho = bOn; } } + void SetBigOrtho( bool bOn ) { if( bBigOrtho != bOn ) { OptionsChanged(); bBigOrtho = bOn; } } + void SetRotate( bool bOn ) { if( bRotate != bOn ) { OptionsChanged(); bRotate = bOn; } } + void SetSnapArea( sal_Int16 nIn ) { if( nSnapArea != nIn ) { OptionsChanged(); nSnapArea = nIn; } } + void SetAngle( Degree100 nIn ) { if( nAngle != nIn ) { OptionsChanged(); nAngle = nIn; } } + void SetEliminatePolyPointLimitAngle( Degree100 nIn ) { if( nBezAngle != nIn ) { OptionsChanged(); nBezAngle = nIn; } } +}; + +class SD_DLLPUBLIC SdOptionsSnapItem : public SfxPoolItem +{ +public: + + explicit SdOptionsSnapItem(); + SdOptionsSnapItem( SdOptions const * pOpts, ::sd::FrameView const * pView ); + + virtual SdOptionsSnapItem* Clone( SfxItemPool *pPool = nullptr ) const override; + virtual bool operator==( const SfxPoolItem& ) const override; + + void SetOptions( SdOptions* pOpts ) const; + + SdOptionsSnap& GetOptionsSnap() { return maOptionsSnap; } +private: + SdOptionsSnap maOptionsSnap; +}; + +class SdOptionsZoom : public SdOptionsGeneric +{ +private: + + sal_Int32 nX; // Zoom/ScaleX + sal_Int32 nY; // Zoom/ScaleY + +protected: + + virtual void GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const override; + virtual bool ReadData( const css::uno::Any* pValues ) override; + virtual bool WriteData( css::uno::Any* pValues ) const override; + +public: + + explicit SdOptionsZoom(bool bImpress); + + void GetScale( sal_Int32& rX, sal_Int32& rY ) const { Init(); rX = nX; rY = nY; } + void SetScale( sal_Int32 nInX, sal_Int32 nInY ) { if( nX != nInX || nY != nInY ) { OptionsChanged(); nX = nInX; nY = nInY; } } +}; + +class SdOptionsGrid : public SdOptionsGeneric, public SvxOptionsGrid +{ +protected: + + virtual void GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const override; + virtual bool ReadData( const css::uno::Any* pValues ) override; + virtual bool WriteData( css::uno::Any* pValues ) const override; + +public: + + explicit SdOptionsGrid(bool bImpress); + virtual ~SdOptionsGrid() override; + + void SetDefaults(); + + sal_uInt32 GetFieldDrawX() const { Init(); return SvxOptionsGrid::GetFieldDrawX(); } + sal_uInt32 GetFieldDivisionX() const { Init(); return SvxOptionsGrid::GetFieldDivisionX(); } + sal_uInt32 GetFieldDrawY() const { Init(); return SvxOptionsGrid::GetFieldDrawY(); } + sal_uInt32 GetFieldDivisionY() const { Init(); return SvxOptionsGrid::GetFieldDivisionY(); } + sal_uInt32 GetFieldSnapX() const { Init(); return SvxOptionsGrid::GetFieldSnapX(); } + sal_uInt32 GetFieldSnapY() const { Init(); return SvxOptionsGrid::GetFieldSnapY(); } + bool IsUseGridSnap() const { Init(); return SvxOptionsGrid::GetUseGridSnap(); } + bool IsSynchronize() const { Init(); return SvxOptionsGrid::GetSynchronize(); } + bool IsGridVisible() const { Init(); return SvxOptionsGrid::GetGridVisible(); } + bool IsEqualGrid() const { Init(); return SvxOptionsGrid::GetEqualGrid(); } + + void SetFieldDrawX( sal_uInt32 nSet ) { if( nSet != SvxOptionsGrid::GetFieldDrawX() ) { OptionsChanged(); SvxOptionsGrid::SetFieldDrawX( nSet ); } } + void SetFieldDivisionX( sal_uInt32 nSet ) { if( nSet != SvxOptionsGrid::GetFieldDivisionX() ) { OptionsChanged(); SvxOptionsGrid::SetFieldDivisionX( nSet ); } } + void SetFieldDrawY( sal_uInt32 nSet ) { if( nSet != SvxOptionsGrid::GetFieldDrawY() ) { OptionsChanged(); SvxOptionsGrid::SetFieldDrawY( nSet ); } } + void SetFieldDivisionY( sal_uInt32 nSet ) { if( nSet != SvxOptionsGrid::GetFieldDivisionY() ) { OptionsChanged(); SvxOptionsGrid::SetFieldDivisionY( nSet ); } } + void SetFieldSnapX( sal_uInt32 nSet ) { if( nSet != SvxOptionsGrid::GetFieldSnapX() ) { OptionsChanged(); SvxOptionsGrid::SetFieldSnapX( nSet ); } } + void SetFieldSnapY( sal_uInt32 nSet ) { if( nSet != SvxOptionsGrid::GetFieldSnapY() ) { OptionsChanged(); SvxOptionsGrid::SetFieldSnapY( nSet ); } } + void SetUseGridSnap( bool bSet ) { if( bSet != SvxOptionsGrid::GetUseGridSnap() ) { OptionsChanged(); SvxOptionsGrid::SetUseGridSnap( bSet ); } } + void SetSynchronize( bool bSet ) { if( bSet != SvxOptionsGrid::GetSynchronize() ) { OptionsChanged(); SvxOptionsGrid::SetSynchronize( bSet ); } } + void SetGridVisible( bool bSet ) { if( bSet != SvxOptionsGrid::GetGridVisible() ) { OptionsChanged(); SvxOptionsGrid::SetGridVisible( bSet ); } } + void SetEqualGrid( bool bSet ) { if( bSet != SvxOptionsGrid::GetEqualGrid() ) { OptionsChanged(); SvxOptionsGrid::SetEqualGrid( bSet ); } } +}; + +class SdOptionsGridItem : public SvxGridItem +{ + +public: + explicit SdOptionsGridItem( SdOptions const * pOpts ); + + void SetOptions( SdOptions* pOpts ) const; +}; + +class SD_DLLPUBLIC SdOptionsPrint : public SdOptionsGeneric +{ +private: + + bool bDraw : 1; // Print/Content/Drawing + bool bNotes : 1; // Print/Content/Note + bool bHandout : 1; // Print/Content/Handout + bool bOutline : 1; // Print/Content/Outline + bool bDate : 1; // Print/Other/Date + bool bTime : 1; // Print/Other/Time + bool bPagename : 1; // Print/Other/PageName + bool bHiddenPages : 1; // Print/Other/HiddenPage + bool bPagesize : 1; // Print/Page/PageSize + bool bPagetile : 1; // Print/Page/PageTile + bool bWarningPrinter : 1; // These flags you get + bool bWarningSize : 1; // from the common options, + bool bWarningOrientation : 1; // currently org.openoffice.Office.Common.xml (class OfaMiscCfg ; sfx2/misccfg.hxx ) + bool bBooklet : 1; // Print/Page/Booklet + bool bFront : 1; // Print/Page/BookletFront + bool bBack : 1; // Print/Page/BookletFront + bool bCutPage : 1; // NOT persistent !!! + bool bPaperbin : 1; // Print/Other/FromPrinterSetup + bool mbHandoutHorizontal : 1; // Order Page previews on Handout Pages horizontal + sal_uInt16 mnHandoutPages; // Number of page previews on handout page (only 1/2/4/6/9 are supported) + sal_uInt16 nQuality; // Print/Other/Quality + +protected: + + virtual void GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const override; + virtual bool ReadData( const css::uno::Any* pValues ) override; + virtual bool WriteData( css::uno::Any* pValues ) const override; + +public: + + SdOptionsPrint(bool bImpress, bool bUseConfig); + + bool operator==( const SdOptionsPrint& rOpt ) const; + + bool IsDraw() const { Init(); return bDraw; } + bool IsNotes() const { Init(); return bNotes; } + bool IsHandout() const { Init(); return bHandout; } + bool IsOutline() const { Init(); return bOutline; } + bool IsDate() const { Init(); return bDate; } + bool IsTime() const { Init(); return bTime; } + bool IsPagename() const { Init(); return bPagename; } + bool IsHiddenPages() const { Init(); return bHiddenPages; } + bool IsPagesize() const { Init(); return bPagesize; } + bool IsPagetile() const { Init(); return bPagetile; } + bool IsWarningPrinter() const { Init(); return bWarningPrinter; } + bool IsWarningSize() const { Init(); return bWarningSize; } + bool IsWarningOrientation() const { Init(); return bWarningOrientation; } + bool IsBooklet() const { Init(); return bBooklet; } + bool IsFrontPage() const { Init(); return bFront; } + bool IsBackPage() const { Init(); return bBack; } + bool IsCutPage() const { Init(); return bCutPage; } + bool IsPaperbin() const { Init(); return bPaperbin; } + sal_uInt16 GetOutputQuality() const { Init(); return nQuality; } + bool IsHandoutHorizontal() const { Init(); return mbHandoutHorizontal; } + sal_uInt16 GetHandoutPages() const { Init(); return mnHandoutPages; } + + void SetDraw( bool bOn ) { if( bDraw != bOn ) { OptionsChanged(); bDraw = bOn; } } + void SetNotes( bool bOn ) { if( bNotes != bOn ) { OptionsChanged(); bNotes = bOn; } } + void SetHandout( bool bOn ) { if( bHandout != bOn ) { OptionsChanged(); bHandout = bOn; } } + void SetOutline( bool bOn ) { if( bOutline != bOn ) { OptionsChanged(); bOutline = bOn; } } + void SetDate( bool bOn ) { if( bDate != bOn ) { OptionsChanged(); bDate = bOn; } } + void SetTime( bool bOn ) { if( bTime != bOn ) { OptionsChanged(); bTime = bOn; } } + void SetPagename( bool bOn ) { if( bPagename != bOn ) { OptionsChanged(); bPagename = bOn; } } + void SetHiddenPages( bool bOn ) { if( bHiddenPages != bOn ) { OptionsChanged(); bHiddenPages = bOn; } } + void SetPagesize( bool bOn ) { if( bPagesize != bOn ) { OptionsChanged(); bPagesize = bOn; } } + void SetPagetile( bool bOn ) { if( bPagetile != bOn ) { OptionsChanged(); bPagetile = bOn; } } + void SetWarningPrinter( bool bOn ) { if( bWarningPrinter != bOn ) { OptionsChanged(); bWarningPrinter = bOn; } } + void SetWarningSize( bool bOn ) { if( bWarningSize != bOn ) { OptionsChanged(); bWarningSize = bOn; } } + void SetWarningOrientation( bool bOn) { if( bWarningOrientation != bOn ) { OptionsChanged(); bWarningOrientation = bOn; } } + void SetBooklet( bool bOn ) { if( bBooklet != bOn ) { OptionsChanged(); bBooklet = bOn; } } + void SetFrontPage( bool bOn ) { if( bFront != bOn ) { OptionsChanged(); bFront = bOn; } } + void SetBackPage( bool bOn ) { if( bBack != bOn ) { OptionsChanged(); bBack = bOn; } } + void SetCutPage( bool bOn ) { if( bCutPage != bOn ) { OptionsChanged(); bCutPage = bOn; } } + void SetPaperbin( bool bOn ) { if( bPaperbin != bOn ) { OptionsChanged(); bPaperbin = bOn; } } + void SetOutputQuality( sal_uInt16 nInQuality ) { if( nQuality != nInQuality ) { OptionsChanged(); nQuality = nInQuality; } } + void SetHandoutHorizontal( bool bHandoutHorizontal ) { if( mbHandoutHorizontal != bHandoutHorizontal ) { OptionsChanged(); mbHandoutHorizontal = bHandoutHorizontal; } } + void SetHandoutPages( sal_uInt16 nHandoutPages ) { if( nHandoutPages != mnHandoutPages ) { OptionsChanged(); mnHandoutPages = nHandoutPages; } } +}; + +class SD_DLLPUBLIC SdOptionsPrintItem : public SfxPoolItem +{ +public: + + explicit SdOptionsPrintItem(); + explicit SdOptionsPrintItem( SdOptions const * pOpts ); + + virtual SdOptionsPrintItem* Clone( SfxItemPool *pPool = nullptr ) const override; + virtual bool operator==( const SfxPoolItem& ) const override; + + void SetOptions( SdOptions* pOpts ) const; + + SdOptionsPrint& GetOptionsPrint() { return maOptionsPrint; } + const SdOptionsPrint& GetOptionsPrint() const { return maOptionsPrint; } +private: + SdOptionsPrint maOptionsPrint; +}; + +class SdOptions : public SdOptionsLayout, public SdOptionsContents, + public SdOptionsMisc, public SdOptionsSnap, + public SdOptionsZoom, public SdOptionsGrid, + public SdOptionsPrint +{ +public: + + explicit SdOptions(bool bImpress); + virtual ~SdOptions() override; + + void StoreConfig(); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/paragr.hxx b/sd/source/ui/inc/paragr.hxx new file mode 100644 index 000000000..30304d800 --- /dev/null +++ b/sd/source/ui/inc/paragr.hxx @@ -0,0 +1,36 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/tabdlg.hxx> + +/** + * Paragraph-Tab-Dialog + */ +class SdParagraphDlg : public SfxTabDialogController +{ +private: + virtual void PageCreated(const OString& rId, SfxTabPage& rPage) override; + +public: + SdParagraphDlg(weld::Window* pParent, const SfxItemSet* pAttr); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/pgjump.hxx b/sd/source/ui/inc/pgjump.hxx new file mode 100644 index 000000000..c4d434caa --- /dev/null +++ b/sd/source/ui/inc/pgjump.hxx @@ -0,0 +1,31 @@ +/* -*- 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 . + */ + +#pragma once + +enum PageJump +{ + PAGE_NONE, + PAGE_FIRST, + PAGE_PREVIOUS, + PAGE_NEXT, + PAGE_LAST +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/present.hxx b/sd/source/ui/inc/present.hxx new file mode 100644 index 000000000..00c78ac79 --- /dev/null +++ b/sd/source/ui/inc/present.hxx @@ -0,0 +1,90 @@ +/* -*- 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 . + */ + +#pragma once + +#include <vcl/weld.hxx> +#include <vcl/weldutils.hxx> + +class SfxItemSet; +class SdCustomShowList; + +/** + * Dialog to define optionsm_xnd to start the presentation + */ +class SdStartPresentationDlg : public weld::GenericDialogController +{ +private: + SdCustomShowList* pCustomShowList; + const SfxItemSet& rOutAttrs; + sal_Int32 mnMonitors; + + std::unique_ptr<weld::RadioButton> m_xRbtAll; + std::unique_ptr<weld::RadioButton> m_xRbtAtDia; + std::unique_ptr<weld::RadioButton> m_xRbtCustomshow; + std::unique_ptr<weld::ComboBox> m_xLbDias; + std::unique_ptr<weld::ComboBox> m_xLbCustomshow; + + std::unique_ptr<weld::RadioButton> m_xRbtStandard; + std::unique_ptr<weld::RadioButton> m_xRbtWindow; + std::unique_ptr<weld::RadioButton> m_xRbtAuto; + std::unique_ptr<weld::FormattedSpinButton> m_xTmfPause; + std::unique_ptr<weld::TimeFormatter> m_xFormatter; + std::unique_ptr<weld::CheckButton> m_xCbxAutoLogo; + + std::unique_ptr<weld::CheckButton> m_xCbxManuel; + std::unique_ptr<weld::CheckButton> m_xCbxMousepointer; + std::unique_ptr<weld::CheckButton> m_xCbxPen; + std::unique_ptr<weld::CheckButton> m_xCbxAnimationAllowed; + std::unique_ptr<weld::CheckButton> m_xCbxChangePage; + std::unique_ptr<weld::CheckButton> m_xCbxAlwaysOnTop; + + std::unique_ptr<weld::Label> m_xFtMonitor; + std::unique_ptr<weld::ComboBox> m_xLBMonitor; + + std::unique_ptr<weld::Label> m_xMonitor; + std::unique_ptr<weld::Label> m_xAllMonitors; + std::unique_ptr<weld::Label> m_xMonitorExternal; + std::unique_ptr<weld::Label> m_xExternal; + + DECL_LINK(ChangeRangeHdl, weld::Toggleable&, void); + DECL_LINK(ClickWindowPresentationHdl, weld::Toggleable&, void); + void ChangePause(); + DECL_LINK(ChangePauseHdl, weld::FormattedSpinButton&, void); + + void InitMonitorSettings(); + enum DisplayType { + EXTERNAL_IS_NUMBER, + MONITOR_NORMAL, + MONITOR_IS_EXTERNAL, + }; + sal_Int32 InsertDisplayEntry(const OUString &aName, + sal_Int32 nDisplay); + OUString GetDisplayName( sal_Int32 nDisplay, + DisplayType eType ); +public: + SdStartPresentationDlg(weld::Window* pWindow, + const SfxItemSet& rInAttrs, + const std::vector<OUString> &rPageNames, + SdCustomShowList* pCSList); + virtual ~SdStartPresentationDlg() override; + void GetAttr( SfxItemSet& rOutAttrs ); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/prltempl.hxx b/sd/source/ui/inc/prltempl.hxx new file mode 100644 index 000000000..efd50e4f7 --- /dev/null +++ b/sd/source/ui/inc/prltempl.hxx @@ -0,0 +1,64 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/tabdlg.hxx> +#include <svx/xtable.hxx> + +#include <prlayout.hxx> + +class SfxObjectShell; +class SfxStyleSheetBase; +class SfxStyleSheetBasePool; + +/** + * Template-Tab-Dialog + */ +class SdPresLayoutTemplateDlg final : public SfxTabDialogController +{ +private: + const SfxObjectShell* mpDocShell; + + XColorListRef pColorTab; + XGradientListRef pGradientList; + XHatchListRef pHatchingList; + XBitmapListRef pBitmapList; + XPatternListRef pPatternList; + XDashListRef pDashList; + XLineEndListRef pLineEndList; + + PresentationObjects ePO; + + virtual void PageCreated(const OString& rId, SfxTabPage &rPage) override; + + // for mapping with the new SvxNumBulletItem + SfxItemSet aInputSet; + std::unique_ptr<SfxItemSet> pOutSet; + + sal_uInt16 GetOutlineLevel() const; + +public: + SdPresLayoutTemplateDlg(SfxObjectShell const * pDocSh, weld::Window* pParent, bool bBackground, SfxStyleSheetBase& rStyleBase, PresentationObjects ePO, SfxStyleSheetBasePool* pSSPool); + virtual ~SdPresLayoutTemplateDlg() override; + + const SfxItemSet* GetOutputItemSet() const; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/prntopts.hxx b/sd/source/ui/inc/prntopts.hxx new file mode 100644 index 000000000..51c3a3603 --- /dev/null +++ b/sd/source/ui/inc/prntopts.hxx @@ -0,0 +1,69 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/tabdlg.hxx> + +class SdPrintOptions final : public SfxTabPage +{ + friend class SdModule; + +private: + std::unique_ptr<weld::Frame> m_xFrmContent; + std::unique_ptr<weld::CheckButton> m_xCbxDraw; + std::unique_ptr<weld::CheckButton> m_xCbxNotes; + std::unique_ptr<weld::CheckButton> m_xCbxHandout; + std::unique_ptr<weld::CheckButton> m_xCbxOutline; + std::unique_ptr<weld::RadioButton> m_xRbtColor; + std::unique_ptr<weld::RadioButton> m_xRbtGrayscale; + std::unique_ptr<weld::RadioButton> m_xRbtBlackWhite; + std::unique_ptr<weld::CheckButton> m_xCbxPagename; + std::unique_ptr<weld::CheckButton> m_xCbxDate; + std::unique_ptr<weld::CheckButton> m_xCbxTime; + std::unique_ptr<weld::CheckButton> m_xCbxHiddenPages; + std::unique_ptr<weld::RadioButton> m_xRbtDefault; + std::unique_ptr<weld::RadioButton> m_xRbtPagesize; + std::unique_ptr<weld::RadioButton> m_xRbtPagetile; + std::unique_ptr<weld::RadioButton> m_xRbtBooklet; + std::unique_ptr<weld::CheckButton> m_xCbxFront; + std::unique_ptr<weld::CheckButton> m_xCbxBack; + std::unique_ptr<weld::CheckButton> m_xCbxPaperbin; + + DECL_LINK(ClickCheckboxHdl, weld::Toggleable&, void); + DECL_LINK(ClickBookletHdl, weld::Toggleable&, void); + + void updateControls(); + +public: + SdPrintOptions(weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet& rInAttrs); + virtual ~SdPrintOptions() override; + + static std::unique_ptr<SfxTabPage> + Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet*); + + virtual bool FillItemSet(SfxItemSet*) override; + virtual void Reset(const SfxItemSet*) override; + + void SetDrawMode(); + virtual void PageCreated(const SfxAllItemSet& aSet) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/pubdlg.hxx b/sd/source/ui/inc/pubdlg.hxx new file mode 100644 index 000000000..1f0b7274c --- /dev/null +++ b/sd/source/ui/inc/pubdlg.hxx @@ -0,0 +1,205 @@ +/* -*- 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 . + */ + +#pragma once + +#include <svtools/valueset.hxx> +#include <vcl/weld.hxx> +#include <vcl/weldutils.hxx> +#include <pres.hxx> +#include "assclass.hxx" + +#include <memory> +#include <vector> + +class ListBox; +class Edit; +class SdHtmlAttrPreview; +class SdPublishingDesign; +class ButtonSet; + +namespace com::sun::star::beans +{ +struct PropertyValue; +} +namespace com::sun::star::uno +{ +template <class E> class Sequence; +} + +// ********************************************************************* +// Html-Export Autopilot +// ********************************************************************* +// should turn this into a wizard +class SdPublishingDlg final : public weld::GenericDialogController +{ +private: + // page 1 controls + std::unique_ptr<weld::Container> m_xPage1; + std::unique_ptr<weld::Label> m_xPage1_Title; + std::unique_ptr<weld::RadioButton> m_xPage1_NewDesign; + std::unique_ptr<weld::RadioButton> m_xPage1_OldDesign; + std::unique_ptr<weld::TreeView> m_xPage1_Designs; + std::unique_ptr<weld::Button> m_xPage1_DelDesign; + std::unique_ptr<weld::Label> m_xPage1_Desc; + + // page 2 controls + std::unique_ptr<weld::Container> m_xPage2; + std::unique_ptr<weld::Container> m_xPage2Frame2; + std::unique_ptr<weld::Container> m_xPage2Frame3; + std::unique_ptr<weld::Container> m_xPage2Frame4; + std::unique_ptr<weld::Label> m_xPage2_Title; + std::unique_ptr<weld::RadioButton> m_xPage2_Standard; + std::unique_ptr<weld::RadioButton> m_xPage2_Frames; + std::unique_ptr<weld::RadioButton> m_xPage2_SingleDocument; + std::unique_ptr<weld::RadioButton> m_xPage2_Kiosk; + std::unique_ptr<weld::RadioButton> m_xPage2_WebCast; + std::unique_ptr<weld::Image> m_xPage2_Standard_FB; + std::unique_ptr<weld::Image> m_xPage2_Frames_FB; + std::unique_ptr<weld::Image> m_xPage2_Kiosk_FB; + std::unique_ptr<weld::Image> m_xPage2_WebCast_FB; + + std::unique_ptr<weld::Label> m_xPage2_Title_Html; + std::unique_ptr<weld::CheckButton> m_xPage2_Content; + std::unique_ptr<weld::CheckButton> m_xPage2_Notes; + + std::unique_ptr<weld::Label> m_xPage2_Title_WebCast; + std::unique_ptr<weld::RadioButton> m_xPage2_ASP; + std::unique_ptr<weld::RadioButton> m_xPage2_PERL; + std::unique_ptr<weld::Label> m_xPage2_URL_txt; + std::unique_ptr<weld::Entry> m_xPage2_URL; + std::unique_ptr<weld::Label> m_xPage2_CGI_txt; + std::unique_ptr<weld::Entry> m_xPage2_CGI; + std::unique_ptr<weld::Label> m_xPage2_Index_txt; + std::unique_ptr<weld::Entry> m_xPage2_Index; + std::unique_ptr<weld::Label> m_xPage2_Title_Kiosk; + std::unique_ptr<weld::RadioButton> m_xPage2_ChgDefault; + std::unique_ptr<weld::RadioButton> m_xPage2_ChgAuto; + std::unique_ptr<weld::Label> m_xPage2_Duration_txt; + std::unique_ptr<weld::FormattedSpinButton> m_xPage2_Duration; + std::unique_ptr<weld::TimeFormatter> m_xFormatter; + std::unique_ptr<weld::CheckButton> m_xPage2_Endless; + + // page 3 controls + std::unique_ptr<weld::Container> m_xPage3; + std::unique_ptr<weld::Label> m_xPage3_Title1; + std::unique_ptr<weld::RadioButton> m_xPage3_Png; + std::unique_ptr<weld::RadioButton> m_xPage3_Gif; + std::unique_ptr<weld::RadioButton> m_xPage3_Jpg; + std::unique_ptr<weld::Label> m_xPage3_Quality_txt; + std::unique_ptr<weld::ComboBox> m_xPage3_Quality; + std::unique_ptr<weld::Label> m_xPage3_Title2; + std::unique_ptr<weld::RadioButton> m_xPage3_Resolution_1; + std::unique_ptr<weld::RadioButton> m_xPage3_Resolution_2; + std::unique_ptr<weld::RadioButton> m_xPage3_Resolution_3; + std::unique_ptr<weld::RadioButton> m_xPage3_Resolution_4; + std::unique_ptr<weld::Label> m_xPage3_Title3; + std::unique_ptr<weld::CheckButton> m_xPage3_SldSound; + std::unique_ptr<weld::CheckButton> m_xPage3_HiddenSlides; + + // page 4 controls + std::unique_ptr<weld::Container> m_xPage4; + std::unique_ptr<weld::Label> m_xPage4_Title1; + std::unique_ptr<weld::Label> m_xPage4_Author_txt; + std::unique_ptr<weld::Entry> m_xPage4_Author; + std::unique_ptr<weld::Label> m_xPage4_Email_txt; + std::unique_ptr<weld::Entry> m_xPage4_Email; + std::unique_ptr<weld::Label> m_xPage4_WWW_txt; + std::unique_ptr<weld::Entry> m_xPage4_WWW; + std::unique_ptr<weld::Label> m_xPage4_Title2; + std::unique_ptr<weld::TextView> m_xPage4_Misc; + std::unique_ptr<weld::CheckButton> m_xPage4_Download; + + // page 5 controls + std::unique_ptr<weld::Container> m_xPage5; + std::unique_ptr<weld::Label> m_xPage5_Title; + std::unique_ptr<weld::CheckButton> m_xPage5_TextOnly; + std::unique_ptr<ValueSet> m_xPage5_Buttons; + std::unique_ptr<weld::CustomWeld> m_xPage5_ButtonsWnd; + + // page 6 controls + std::unique_ptr<weld::Container> m_xPage6; + std::unique_ptr<weld::Label> m_xPage6_Title; + std::unique_ptr<weld::RadioButton> m_xPage6_Default; + std::unique_ptr<weld::RadioButton> m_xPage6_User; + std::unique_ptr<weld::Button> m_xPage6_Back; + std::unique_ptr<weld::Button> m_xPage6_Text; + std::unique_ptr<weld::Button> m_xPage6_Link; + std::unique_ptr<weld::Button> m_xPage6_VLink; + std::unique_ptr<weld::Button> m_xPage6_ALink; + std::unique_ptr<weld::RadioButton> m_xPage6_DocColors; + std::unique_ptr<SdHtmlAttrPreview> m_xPage6_Preview; + std::unique_ptr<weld::CustomWeld> m_xPage6_PreviewWnd; + + std::unique_ptr<ButtonSet> m_xButtonSet; + + // standard controls + std::unique_ptr<weld::Button> m_xLastPageButton; + std::unique_ptr<weld::Button> m_xNextPageButton; + std::unique_ptr<weld::Button> m_xFinishButton; + + Assistent aAssistentFunc; + + bool m_bImpress; + bool m_bButtonsDirty; + + void SetDefaults(); + void CreatePages(); + + Color m_aBackColor, m_aTextColor, m_aLinkColor; + Color m_aVLinkColor, m_aALinkColor; + + void ChangePage(); + void UpdatePage(); + + std::vector<SdPublishingDesign> m_aDesignList; + bool m_bDesignListDirty; + SdPublishingDesign* m_pDesign; + void Load(); + bool Save(); + + void GetDesign(SdPublishingDesign* pDesign); + void SetDesign(SdPublishingDesign const* pDesign); + + void LoadPreviewButtons(); + + DECL_LINK(FinishHdl, weld::Button&, void); + DECL_LINK(NextPageHdl, weld::Button&, void); + DECL_LINK(LastPageHdl, weld::Button&, void); + + DECL_LINK(DesignHdl, weld::Toggleable&, void); + DECL_LINK(DesignSelectHdl, weld::TreeView&, void); + DECL_LINK(DesignDeleteHdl, weld::Button&, void); + DECL_LINK(BaseHdl, weld::Toggleable&, void); + DECL_LINK(ContentHdl, weld::Toggleable&, void); + DECL_LINK(GfxFormatHdl, weld::Toggleable&, void); + DECL_LINK(ResolutionHdl, weld::Toggleable&, void); + DECL_LINK(ButtonsHdl, ValueSet*, void); + DECL_LINK(ColorHdl, weld::Button&, void); + DECL_LINK(WebServerHdl, weld::Toggleable&, void); + DECL_LINK(SlideChgHdl, weld::Toggleable&, void); + +public: + SdPublishingDlg(weld::Window* pWindow, DocumentType eDocType); + virtual ~SdPublishingDlg() override; + + void GetParameterSequence(css::uno::Sequence<css::beans::PropertyValue>& rParams); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/registerinterfaces.hxx b/sd/source/ui/inc/registerinterfaces.hxx new file mode 100644 index 000000000..5a8be7dc0 --- /dev/null +++ b/sd/source/ui/inc/registerinterfaces.hxx @@ -0,0 +1,30 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> +#include <sfx2/module.hxx> + +namespace sd::ui::table +{ +void RegisterInterfaces(const SfxModule* pMod); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/scalectrl.hxx b/sd/source/ui/inc/scalectrl.hxx new file mode 100644 index 000000000..2a0ed5b28 --- /dev/null +++ b/sd/source/ui/inc/scalectrl.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 . + */ + +#pragma once + +#include <sfx2/stbitem.hxx> + +class SdScaleControl final : public SfxStatusBarControl +{ +public: + SdScaleControl(sal_uInt16 nSlotId, sal_uInt16 nId, StatusBar& rStb); + virtual ~SdScaleControl() override; + + virtual void StateChangedAtStatusBarControl(sal_uInt16 nSID, SfxItemState eState, + const SfxPoolItem* pState) override; + + SFX_DECL_STATUSBAR_CONTROL(); + +private: + virtual void Command(const CommandEvent& rCEvt) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/sdpopup.hxx b/sd/source/ui/inc/sdpopup.hxx new file mode 100644 index 000000000..0eccc914d --- /dev/null +++ b/sd/source/ui/inc/sdpopup.hxx @@ -0,0 +1,47 @@ +/* -*- 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 . + */ + +#pragma once + +#include <i18nlangtag/lang.h> +#include <vcl/weld.hxx> + +class SvxFieldData; + +/** + * PopupMenu for editing field-commands + */ +class SdFieldPopup +{ +private: + std::unique_ptr<weld::Builder> m_xBuilder; + std::unique_ptr<weld::Menu> m_xPopup; + const SvxFieldData* m_pField; + + void Fill( LanguageType eLanguage ); + +public: + SdFieldPopup(const SvxFieldData* pInField, LanguageType eLanguage); + void Execute(weld::Window* pParent, const tools::Rectangle& rRect); + ~SdFieldPopup(); + + SvxFieldData* GetField(); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/sdpreslt.hxx b/sd/source/ui/inc/sdpreslt.hxx new file mode 100644 index 000000000..77187a0b3 --- /dev/null +++ b/sd/source/ui/inc/sdpreslt.hxx @@ -0,0 +1,70 @@ +/* -*- 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 . + */ + +#pragma once + +#include <vector> + +#include <vcl/weld.hxx> + +class SfxItemSet; +class ValueSet; +namespace weld { class CustomWeld; } + +namespace sd { +class DrawDocShell; +} + +class SdPresLayoutDlg final + : public weld::GenericDialogController +{ +public: + SdPresLayoutDlg( + ::sd::DrawDocShell* pDocShell, + weld::Window* pWindow, + const SfxItemSet& rInAttrs); + virtual ~SdPresLayoutDlg() override; + + void GetAttr(SfxItemSet& rOutAttrs); + + DECL_LINK(ClickLayoutHdl, ValueSet*, void); + DECL_LINK(ClickLoadHdl, weld::Button&, void); + +private: + ::sd::DrawDocShell* mpDocSh; + + const SfxItemSet& mrOutAttrs; + + std::vector<OUString> maLayoutNames; + + OUString maName; ///< layout name or file name + tools::Long mnLayoutCount; ///< number of master pages in the document + const OUString maStrNone; + + std::unique_ptr<weld::CheckButton> m_xCbxMasterPage; + std::unique_ptr<weld::CheckButton> m_xCbxCheckMasters; + std::unique_ptr<weld::Button> m_xBtnLoad; + std::unique_ptr<ValueSet> m_xVS; + std::unique_ptr<weld::CustomWeld> m_xVSWin; + + void FillValueSet(); + void Reset(); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/sdtreelb.hxx b/sd/source/ui/inc/sdtreelb.hxx new file mode 100644 index 000000000..ea59ed0b5 --- /dev/null +++ b/sd/source/ui/inc/sdtreelb.hxx @@ -0,0 +1,395 @@ +/* -*- 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 . + */ + +#pragma once + +#include <pres.hxx> +#include <sddllapi.h> +#include <vcl/weld.hxx> +#include <svl/urlbmk.hxx> +#include <tools/ref.hxx> +#include "sdxfer.hxx" +#include <memory> +#include <vector> + +class SdDrawDocument; +class SfxMedium; +class SfxViewFrame; +class SdNavigatorWin; +class SdrObject; +class SdrObjList; +class SdPage; +struct ImplSVEvent; + +namespace sd { +class ViewShell; + +class DrawDocShell; +#ifndef SV_DECL_DRAW_DOC_SHELL_DEFINED +#define SV_DECL_DRAW_DOC_SHELL_DEFINED +typedef ::tools::SvRef<DrawDocShell> DrawDocShellRef; +#endif +} +namespace svt { + class AcceleratorExecute; +} + +class SdPageObjsTLVDropTarget final : public DropTargetHelper +{ +private: + weld::TreeView& m_rTreeView; + + virtual sal_Int8 AcceptDrop( const AcceptDropEvent& rEvt ) override; + virtual sal_Int8 ExecuteDrop( const ExecuteDropEvent& rEvt ) override; + +public: + SdPageObjsTLVDropTarget(weld::TreeView& rTreeView); +}; + +class SD_DLLPUBLIC SdPageObjsTLV +{ +private: + static bool SAL_DLLPRIVATE bIsInDrag; ///< static, in the case the navigator is deleted in ExecuteDrag + + std::unique_ptr<weld::TreeView> m_xTreeView; + std::unique_ptr<weld::TreeIter> m_xScratchIter; + std::unique_ptr<SdPageObjsTLVDropTarget> m_xDropTargetHelper; + std::unique_ptr<::svt::AcceleratorExecute> m_xAccel; + SdNavigatorWin* m_pNavigator; + const SdDrawDocument* m_pDoc; + SdDrawDocument* m_pBookmarkDoc; + SfxMedium* m_pMedium; + SfxMedium* m_pOwnMedium; + bool m_bLinkableSelected; + bool m_bShowAllShapes; + + /** This flag controls whether to show all pages. + */ + bool m_bShowAllPages; + + /** + * If changing the selection should also result in navigating to the + * relevant shape. + */ + bool m_bSelectionHandlerNavigates; + + /** + * If navigation should not only select the relevant shape but also change + * focus to it. + */ + bool m_bNavigationGrabsFocus; + + SelectionMode m_eSelectionMode; + + ImplSVEvent* m_nSelectEventId; + ImplSVEvent* m_nRowActivateEventId; + + OUString m_aDocName; + ::sd::DrawDocShellRef m_xBookmarkDocShRef; ///< for the loading of bookmarks + Link<weld::TreeView&, void> m_aChangeHdl; + Link<weld::TreeView&, bool> m_aRowActivatedHdl; + Link<const KeyEvent&, bool> m_aKeyPressHdl; + + /** Return the name of the object. When the object has no user supplied + name and the bCreate flag is <TRUE/> then a name is created + automatically. Additionally the mbShowAllShapes flag is taken into + account when there is no user supplied name. When this flag is + <FALSE/> then no name is created. + @param pObject + When this is NULL then an empty string is returned, regardless + of the value of bCreate. + @param bCreate + This flag controls for objects without user supplied name + whether a name is created. When a name is created then this + name is not stored in the object. + */ + OUString GetObjectName ( + const SdrObject* pObject, + const bool bCreate = true) const; + + void CloseBookmarkDoc(); + + DECL_DLLPRIVATE_LINK(RequestingChildrenHdl, const weld::TreeIter&, bool); + DECL_DLLPRIVATE_LINK(SelectHdl, weld::TreeView&, void); + DECL_DLLPRIVATE_LINK(AsyncSelectHdl, void*, void); + DECL_DLLPRIVATE_LINK(RowActivatedHdl, weld::TreeView&, bool); + DECL_DLLPRIVATE_LINK(AsyncRowActivatedHdl, void*, void); + DECL_DLLPRIVATE_LINK(DragBeginHdl, bool&, bool); + DECL_DLLPRIVATE_LINK(KeyInputHdl, const KeyEvent&, bool); + + /** Determine whether the specified page belongs to the current show + which is either the standard show or a custom show. + @param pPage + Pointer to the page for which to check whether it belongs to the + show. + @return + Returns <FALSE/> if there is no custom show or if the current + show does not contain the specified page at least once. + */ + bool PageBelongsToCurrentShow (const SdPage* pPage) const; + + bool DoDrag(); + static void OnDragFinished(); + + // DragSourceHelper + bool StartDrag(); + +public: + + SdPageObjsTLV(std::unique_ptr<weld::TreeView> xTreeview); + ~SdPageObjsTLV(); + + void set_sensitive(bool bSensitive) + { + m_xTreeView->set_sensitive(bSensitive); + } + + void hide() + { + m_xTreeView->hide(); + } + + void show() + { + m_xTreeView->show(); + } + + void grab_focus() + { + m_xTreeView->grab_focus(); + } + + void set_size_request(int nWidth, int nHeight) + { + m_xTreeView->set_size_request(nWidth, nHeight); + } + + float get_approximate_digit_width() const + { + return m_xTreeView->get_approximate_digit_width(); + } + + DECL_LINK(MousePressHdl, const MouseEvent&, bool); + DECL_LINK(MouseReleaseHdl, const MouseEvent&, bool); + + void Select(); + + int get_height_rows(int nRows) const + { + return m_xTreeView->get_height_rows(nRows); + } + + void set_selection_mode(SelectionMode eMode) + { + m_eSelectionMode = eMode; + m_xTreeView->set_selection_mode(eMode); + } + + SelectionMode get_selection_mode() const + { + return m_eSelectionMode; + } + + void connect_row_activated(const Link<weld::TreeView&, bool>& rLink) + { + m_aRowActivatedHdl = rLink; + } + + void connect_key_press(const Link<const KeyEvent&, bool>& rLink) + { + m_aKeyPressHdl = rLink; + } + + bool HasSelectedChildren(std::u16string_view rName); + bool SelectEntry(std::u16string_view rName); + + OUString get_selected_text() const + { + return m_xTreeView->get_selected_text(); + } + + bool get_selected() const + { + return m_xTreeView->get_selected(nullptr); + } + + int count_selected_rows() const + { + return m_xTreeView->count_selected_rows(); + } + + void connect_changed(const Link<weld::TreeView&, void>& rLink) + { + m_aChangeHdl = rLink; + } + + bool is_selected(const weld::TreeIter& rIter) const + { + return m_xTreeView->is_selected(rIter); + } + + bool get_iter_first(weld::TreeIter& rIter) const + { + return m_xTreeView->get_iter_first(rIter); + } + + std::unique_ptr<weld::TreeIter> make_iterator() + { + return m_xTreeView->make_iterator(); + } + + bool get_visible() const + { + return m_xTreeView->get_visible(); + } + + void unselect_all() + { + m_xTreeView->unselect_all(); + } + + void SetViewFrame(const SfxViewFrame* pViewFrame); + + void Fill(const SdDrawDocument*, bool bAllPages, const OUString& rDocName); + void Fill(const SdDrawDocument*, SfxMedium* pSfxMedium, const OUString& rDocName); + + void SetShowAllShapes (const bool bShowAllShapes, const bool bFill); + bool GetShowAllShapes() const { return m_bShowAllShapes; } + + bool IsNavigationGrabsFocus() const { return m_bNavigationGrabsFocus; } + bool IsEqualToDoc(const SdDrawDocument* pInDoc); + /// Visits rList recursively and tries to advance rEntry accordingly. + bool IsEqualToShapeList(std::unique_ptr<weld::TreeIter>& rEntry, const SdrObjList& rList, + std::u16string_view rListName); + + static bool IsInDrag(); + + /** Return the view shell that is linked to the given doc shell. + Call this method when the there is a chance that the doc shell + has been disconnected from the view shell (but not the other + way round.) + @return + May return <NULL/> when the link between view shell and + doc shell has been severed. + */ + static ::sd::ViewShell* GetViewShellForDocShell (::sd::DrawDocShell &rDocShell); + + /** Add one list box entry for the parent of the given shapes and one child entry for + each of the given shapes. + @param rList + The container of shapes that are to be inserted. + @param pShape + The parent shape or NULL when the parent is a page. + @param rsName + The name to be displayed for the new parent node. + @param bIsExcluded + Some pages can be excluded (from the show?). + @param pParentEntry + The parent entry of the new parent entry. + */ + void AddShapeList ( + const SdrObjList& rList, + const SdrObject* pShape, + const OUString& rsName, + const bool bIsExcluded, + const weld::TreeIter* pParentEntry); + + /** Add the given object to a transferable object so that the object can + be dragged and dropped without having a name. + */ + void AddShapeToTransferable ( + SdTransferable& rTransferable, + const SdrObject& rObject) const; + + /** return selected entries + nDepth == 0 -> pages + nDepth == 1 -> objects */ + + std::vector<OUString> GetSelectEntryList(const int nDepth) const; + + SdDrawDocument* GetBookmarkDoc(SfxMedium* pMedium = nullptr); + + bool IsLinkableSelected() const { return m_bLinkableSelected; } + + void InsertEntry(const OUString &rName, const OUString &rExpander) + { + m_xTreeView->insert(nullptr, -1, &rName, nullptr, nullptr, nullptr, false, m_xScratchIter.get()); + m_xTreeView->set_image(*m_xScratchIter, rExpander); + } + + void InsertEntry(const weld::TreeIter* pParent, const OUString& rId, const OUString &rName, const OUString &rExpander, weld::TreeIter* pEntry = nullptr) + { + m_xTreeView->insert(pParent, -1, &rName, &rId, nullptr, nullptr, false, m_xScratchIter.get()); + m_xTreeView->set_image(*m_xScratchIter, rExpander); + if (pEntry) + m_xTreeView->copy_iterator(*m_xScratchIter, *pEntry); + } + + //Mark Current Entry + void SetSdNavigator(SdNavigatorWin* pNavigator); + + void clear() + { + m_xTreeView->clear(); + } + + // nested class to implement the TransferableHelper + class SAL_DLLPRIVATE SdPageObjsTransferable final : public SdTransferable + { + public: + SdPageObjsTransferable( + const INetBookmark& rBookmark, + ::sd::DrawDocShell& rDocShell, + NavigatorDragType eDragType ); + ::sd::DrawDocShell& GetDocShell() const { return mrDocShell;} + NavigatorDragType GetDragType() const { return meDragType;} + + static const css::uno::Sequence< sal_Int8 >& getUnoTunnelId(); + static SdPageObjsTransferable* getImplementation( const css::uno::Reference< css::uno::XInterface >& rxData ) noexcept; + /** Return a temporary transferable data flavor that is used + internally in the navigator for reordering entries. Its + lifetime ends with the office application. + */ + static SotClipboardFormatId GetListBoxDropFormatId(); + + private: + /** Temporary drop flavor id that is used internally in the + navigator. + */ + static SotClipboardFormatId mnListBoxDropFormatId; + + INetBookmark const maBookmark; + ::sd::DrawDocShell& mrDocShell; + NavigatorDragType const meDragType; + virtual ~SdPageObjsTransferable() override; + + virtual void AddSupportedFormats() override; + virtual bool GetData( const css::datatransfer::DataFlavor& rFlavor, const OUString& rDestDoc ) override; + virtual void DragFinished( sal_Int8 nDropAction ) override; + + virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& rId ) override; + }; + + friend class SdPageObjsTLV::SdPageObjsTransferable; + +private: + rtl::Reference<SdPageObjsTransferable> m_xHelper; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/sdundogr.hxx b/sd/source/ui/inc/sdundogr.hxx new file mode 100644 index 000000000..466182faf --- /dev/null +++ b/sd/source/ui/inc/sdundogr.hxx @@ -0,0 +1,46 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sdundo.hxx> +#include <sddllapi.h> + +#include <memory> +#include <vector> + +class SD_DLLPUBLIC SdUndoGroup final : public SdUndoAction +{ + std::vector<std::unique_ptr<SdUndoAction>> aCtn; +public: + SdUndoGroup(SdDrawDocument* pSdDrawDocument) + : SdUndoAction(pSdDrawDocument) + {} + virtual ~SdUndoGroup() override; + + virtual bool Merge( SfxUndoAction* pNextAction ) override; + + virtual void Undo() override; + virtual void Redo() override; + + void AddAction(SdUndoAction* pAction); + +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/sdxfer.hxx b/sd/source/ui/inc/sdxfer.hxx new file mode 100644 index 000000000..5e25ba682 --- /dev/null +++ b/sd/source/ui/inc/sdxfer.hxx @@ -0,0 +1,148 @@ +/* -*- 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 . + */ + +#pragma once + +#include <vcl/transfer.hxx> +#include <vcl/vclptr.hxx> +#include <sfx2/objsh.hxx> +#include <svl/lstner.hxx> + +// SdTransferable +class SdDrawDocument; +class SdrObject; +class INetBookmark; +class ImageMap; +class VirtualDevice; + +namespace sd { +class DrawDocShell; +class View; +} + +class SAL_DLLPUBLIC_RTTI SdTransferable : public TransferDataContainer, public SfxListener +{ +public: + + SdTransferable( SdDrawDocument* pSrcDoc, ::sd::View* pWorkView, bool bInitOnGetData ); + virtual ~SdTransferable() override; + + void SetDocShell( const SfxObjectShellRef& rRef ) { maDocShellRef = rRef; } + const SfxObjectShellRef& GetDocShell() const { return maDocShellRef; } + + void SetWorkDocument( const SdDrawDocument* pWorkDoc ) { mpSdDrawDocument = mpSdDrawDocumentIntern = const_cast<SdDrawDocument*>(pWorkDoc); } + const SdDrawDocument* GetWorkDocument() const { return mpSdDrawDocument; } + + void SetView(const ::sd::View* pView); + const ::sd::View* GetView() const { return mpSdView; } + + void SetObjectDescriptor( std::unique_ptr<TransferableObjectDescriptor> pObjDesc ); + + void SetStartPos( const Point& rStartPos ) { maStartPos = rStartPos; } + const Point& GetStartPos() const { return maStartPos; } + + void SetInternalMove( bool bSet ) { mbInternalMove = bSet; } + bool IsInternalMove() const { return mbInternalMove; } + + bool HasSourceDoc( const SdDrawDocument* pDoc ) const { return( mpSourceDoc == pDoc ); } + + void SetPageBookmarks( std::vector<OUString>&& rPageBookmarks, bool bPersistent ); + bool IsPageTransferable() const { return mbPageTransferable; } + bool HasPageBookmarks() const { return( mpPageDocShell && ( !maPageBookmarks.empty() ) ); } + const std::vector<OUString>& GetPageBookmarks() const { return maPageBookmarks; } + ::sd::DrawDocShell* GetPageDocShell() const { return mpPageDocShell; } + + bool SetTableRTF( SdDrawDocument* ); + + static const css::uno::Sequence< sal_Int8 >& getUnoTunnelId(); + static SdTransferable* getImplementation( const css::uno::Reference< css::uno::XInterface >& rxData ) noexcept; + + // SfxListener + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override; + + virtual void DragFinished( sal_Int8 nDropAction ) override; + SdDrawDocument* GetSourceDoc() const { return mpSourceDoc;} + + /** User data objects can be used to store information temporarily + at the transferable. The slide sorter uses this to store + previews of the slides that are referenced by the + transferable. + */ + class UserData {public:virtual~UserData(){}}; + + /** Add a user data object. When it was added before (and not + removed) then this call is ignored. + */ + void AddUserData (const std::shared_ptr<UserData>& rpData); + + /** Return the number of user data objects. + */ + sal_Int32 GetUserDataCount() const; + + /** Return the specified user data object. When the index is not + valid, ie not in the range [0,count) then an empty pointer is + returned. + */ + std::shared_ptr<UserData> GetUserData (const sal_Int32 nIndex) const; + + // XUnoTunnel + virtual sal_Int64 SAL_CALL getSomething(const css::uno::Sequence< sal_Int8 >& rId) override; + +protected: + + virtual void AddSupportedFormats() override; + virtual bool GetData( const css::datatransfer::DataFlavor& rFlavor, const OUString& rDestDoc ) override; + virtual bool WriteObject( tools::SvRef<SotTempStream>& rxOStm, void* pUserObject, sal_uInt32 nUserObjectId, const css::datatransfer::DataFlavor& rFlavor ) override; + virtual void ObjectReleased() override final; + +private: + + SfxObjectShellRef maDocShellRef; + ::sd::DrawDocShell* mpPageDocShell; + std::vector<OUString> maPageBookmarks; + std::unique_ptr<TransferableDataHelper> mpOLEDataHelper; + std::unique_ptr<TransferableObjectDescriptor> mpObjDesc; + const ::sd::View* mpSdView; + ::sd::View* mpSdViewIntern; + SdDrawDocument* mpSdDrawDocument; + SdDrawDocument* mpSdDrawDocumentIntern; + SdDrawDocument* mpSourceDoc; + VclPtr<VirtualDevice> mpVDev; + std::unique_ptr<INetBookmark> mpBookmark; + std::unique_ptr<Graphic> mpGraphic; + std::unique_ptr<ImageMap> mpImageMap; + ::tools::Rectangle maVisArea; + Point maStartPos; + bool mbInternalMove : 1; + bool mbOwnDocument : 1; + bool mbOwnView : 1; + bool mbLateInit : 1; + bool mbPageTransferable : 1; + bool mbPageTransferablePersistent : 1; + ::std::vector<std::shared_ptr<UserData> > maUserData; + + SdTransferable( const SdTransferable& ) = delete; + SdTransferable& operator=( const SdTransferable& ) = delete; + + void CreateObjectReplacement( SdrObject* pObj ); + void CreateData(); + +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/slideshow.hxx b/sd/source/ui/inc/slideshow.hxx new file mode 100644 index 000000000..35b93afe8 --- /dev/null +++ b/sd/source/ui/inc/slideshow.hxx @@ -0,0 +1,216 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/presentation/XPresentation2.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> + +#include <rtl/ref.hxx> + +#include <tools/link.hxx> + +#include <comphelper/compbase.hxx> + +#include <editeng/unoipset.hxx> + +#include <memory> +#include <sddllapi.h> + +namespace com::sun::star { + namespace drawing { + class XDrawPage; + } + namespace animations { + class XAnimationNode; + } +} +class SdDrawDocument; +class KeyEvent; +class OutputDevice; +class Size; +namespace vcl { class Window; } +class SfxRequest; +class WorkWindow; +class CommandSwipeData; +class CommandLongPressData; +struct ImplSVEvent; + +// TODO: Remove +#define PAGE_NO_END 65535 + +/* Definition of SlideShow class */ + +namespace sd +{ + +class SlideshowImpl; +class View; +class ViewShell; +class ViewShellBase; +struct PresentationSettingsEx; +class FrameView; + +enum AnimationMode +{ + ANIMATIONMODE_SHOW, + ANIMATIONMODE_PREVIEW +}; + +typedef comphelper::WeakComponentImplHelper< css::presentation::XPresentation2, css::lang::XServiceInfo > SlideshowBase; + +class SlideShow final : public SlideshowBase +{ +public: + /// used by the model to create a slideshow for it + static rtl::Reference< SlideShow > Create( SdDrawDocument* pDoc ); + + // static helper api + static rtl::Reference< SlideShow > GetSlideShow( SdDrawDocument const * pDocument ); + static rtl::Reference< SlideShow > GetSlideShow( SdDrawDocument const & rDocument ); + static rtl::Reference< SlideShow > GetSlideShow( ViewShellBase const & rBase ); + + static css::uno::Reference< css::presentation::XSlideShowController > GetSlideShowController(ViewShellBase const & rBase ); + + static bool StartPreview( ViewShellBase const & rBase, + const css::uno::Reference< css::drawing::XDrawPage >& xDrawPage, + const css::uno::Reference< css::animations::XAnimationNode >& xAnimationNode ); + + static void Stop( ViewShellBase const & rBase ); + + /// returns true if there is a running presentation for the given ViewShellBase + static bool IsRunning( ViewShellBase const & rBase ); + + /// returns true if there is a running presentation inside the given ViewShell + /// returns false even if there is a running presentation but in another ViewShell + static bool IsRunning( const ViewShell& rViewShell ); + + // helper api + + void startPreview( + const css::uno::Reference< css::drawing::XDrawPage >& xDrawPage, + const css::uno::Reference< css::animations::XAnimationNode >& xAnimationNode ); + + // uno api + + virtual void disposing(std::unique_lock<std::mutex>&) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override; + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override; + virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override; + virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override; + virtual void SAL_CALL addVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + + // XPresentation + virtual void SAL_CALL start( ) override; + virtual void SAL_CALL end() override; + virtual void SAL_CALL rehearseTimings( ) override; + + // XPresentation2 + virtual void SAL_CALL startWithArguments(const css::uno::Sequence< css::beans::PropertyValue >& Arguments) override; + virtual sal_Bool SAL_CALL isRunning( ) override; + virtual css::uno::Reference< css::presentation::XSlideShowController > SAL_CALL getController( ) override; + + // legacy api + + // actions + void jumpToPageNumber( sal_Int32 nPage ); // a.k.a. FuSlideShow::JumpToPage() + void jumpToPageIndex( sal_Int32 nIndex ); + void jumpToBookmark( const OUString& sBookmark ); // a.k.a. FuSlideShow::JumpToBookmark() + + /** sets or clears the pause state of the running slideshow. + !!!! This should only be called by the SdShowWindow !!!!*/ + void pause( bool bPause ); + bool swipe(const CommandSwipeData &rSwipeData); + bool longpress(const CommandLongPressData& rLongPressData); + + // settings + bool isFullScreen() const; // a.k.a. FuSlideShow::IsFullScreen() + OutputDevice* getShowWindow(); // a.k.a. FuSlideShow::GetShowWindow() + int getAnimationMode() const; // a.k.a. FuSlideShow::GetAnimationMode() + sal_Int32 getCurrentPageNumber() const; // a.k.a. FuSlideShow::GetCurrentPage() + + // events + void resize( const Size &rSize ); + // return false if the activate failed. callers should call end in response to failure + bool activate(ViewShellBase& rBase); + void deactivate(); + void paint(); + + bool keyInput(const KeyEvent& rKEvt); + + bool dependsOn( ViewShellBase const * pViewShellBase ); + + static sal_Int32 GetDisplay(); + + bool IsExitAfterPresenting() const; + void SetExitAfterPresenting(bool bExit); + +private: + SlideShow( SdDrawDocument* pDoc ); + + DECL_LINK( StartInPlacePresentationConfigurationHdl, void *, void ); + void StartInPlacePresentationConfigurationCallback(); + + void StartInPlacePresentation(); + void StartFullscreenPresentation(); + + /// @throws css::uno::RuntimeException + void ThrowIfDisposed() const; + + void CreateController( ViewShell* pViewSh, ::sd::View* pView, vcl::Window* pParentWindow ); + WorkWindow *GetWorkWindow(); + + SlideShow(const SlideShow&) = delete; + SlideShow& operator=( const SlideShow& ) = delete; + + SvxItemPropertySet maPropSet; + + rtl::Reference< SlideshowImpl > mxController; + /** This flag is used together with mxController.is() to prevent + multiple instances of the slide show for one document. The flag + covers the time before mxController is set. + */ + bool mbIsInStartup; + SdDrawDocument* mpDoc; + + std::shared_ptr< PresentationSettingsEx > mxCurrentSettings; + + ViewShellBase* mpCurrentViewShellBase; + ViewShellBase* mpFullScreenViewShellBase; + FrameView* mpFullScreenFrameView; + ImplSVEvent * mnInPlaceConfigEvent; +}; + +namespace slideshowhelp +{ + SD_DLLPUBLIC void ShowSlideShow(SfxRequest const& rReq, SdDrawDocument& rDoc); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/smarttag.hxx b/sd/source/ui/inc/smarttag.hxx new file mode 100644 index 000000000..6e27979d6 --- /dev/null +++ b/sd/source/ui/inc/smarttag.hxx @@ -0,0 +1,171 @@ +/* -*- 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 . + */ + +#pragma once + +#include <helper/simplereferencecomponent.hxx> +#include <rtl/ref.hxx> +#include <set> +#include <svx/svdhdl.hxx> +#include <svx/svdview.hxx> + +class KeyEvent; +class MouseEvent; + +namespace sd +{ +class View; +class SmartHdl; + +/** a smart tag represents a visual user interface element on the documents edit view + that is not part of the document. It uses derivations from SmartHdl for its visuals. + A SmartTag adds himself to the given view if created. It removes himself if it + is disposed before the view is disposed. + + Derive from this class to implement your own smart tag. +*/ +class SmartTag : public SimpleReferenceComponent +{ + friend class SmartTagSet; + +public: + explicit SmartTag(::sd::View& rView); + virtual ~SmartTag() override; + + /** returns true if the SmartTag consumes this event. */ + virtual bool MouseButtonDown(const MouseEvent&, SmartHdl&); + + /** returns true if the SmartTag consumes this event. */ + virtual bool KeyInput(const KeyEvent& rKEvt); + + /** returns true if the SmartTag consumes this event. */ + virtual bool Command(const CommandEvent& rCEvt); + + /** returns true if this smart tag is currently selected */ + bool isSelected() const { return mbSelected; } + + ::sd::View& getView() const { return mrView; } + +protected: + virtual sal_Int32 GetMarkablePointCount() const; + virtual sal_Int32 GetMarkedPointCount() const; + virtual bool MarkPoint(SdrHdl& rHdl, bool bUnmark); + virtual void CheckPossibilities(); + virtual bool MarkPoints(const ::tools::Rectangle* pRect, bool bUnmark); + + virtual void addCustomHandles(SdrHdlList& rHandlerList); + virtual void select(); + virtual void deselect(); + virtual bool getContext(SdrViewContext& rContext); + + virtual void disposing() override; + + ::sd::View& mrView; + bool mbSelected; + +private: + SmartTag(const SmartTag&) = delete; + SmartTag& operator=(const SmartTag&) = delete; +}; + +typedef rtl::Reference<SmartTag> SmartTagReference; + +/** class to administrate the available smart tags for a single view. */ +class SmartTagSet +{ + friend class SmartTag; + +public: + explicit SmartTagSet(::sd::View& rView); + ~SmartTagSet(); + + /** selects the given smart tag and updates all handles */ + void select(const SmartTagReference& xTag); + + /** deselects the current selected smart tag and updates all handles */ + void deselect(); + + /** returns the currently selected tag or an empty reference. */ + const SmartTagReference& getSelected() const { return mxSelectedTag; } + + /** returns true if a SmartTag consumes this event. */ + bool MouseButtonDown(const MouseEvent&); + + /** returns true if a SmartTag consumes this event. */ + bool KeyInput(const KeyEvent& rKEvt); + + /** returns true if a SmartTag consumes this event. */ + bool Command(const CommandEvent& rCEvt); + + /** disposes all smart tags and clears the set */ + void Dispose(); + + /** adds the handles from all smart tags to the given list */ + void addCustomHandles(SdrHdlList& rHandlerList); + + /** returns true if the currently selected smart tag has + a special context, returned in rContext. */ + bool getContext(SdrViewContext& rContext) const; + + // support point editing + bool HasMarkablePoints() const; + sal_uLong GetMarkablePointCount() const; + bool HasMarkedPoints() const; + sal_uLong GetMarkedPointCount() const; + bool MarkPoint(SdrHdl& rHdl, bool bUnmark); + bool MarkPoints(const ::tools::Rectangle* pRect, bool bUnmark); + + void CheckPossibilities(); + +private: + SmartTagSet(const SmartTagSet&) = delete; + SmartTagSet& operator=(const SmartTagSet&) = delete; + + /** adds a new smart tag to this set */ + void add(const SmartTagReference& xTag); + + /** removes the given smart tag from this set */ + void remove(const SmartTagReference& xTag); + + std::set<SmartTagReference> maSet; + + ::sd::View& mrView; + SmartTagReference mxSelectedTag; + SmartTagReference mxMouseOverTag; +}; + +/** a derivation from this handle is the visual representation for a smart tag. + One smart tag can have more than one handle. +*/ +class SmartHdl : public SdrHdl +{ +public: + SmartHdl(const SmartTagReference& xTag, SdrObject* pObject, const Point& rPnt, + SdrHdlKind eNewKind); + SmartHdl(const SmartTagReference& xTag, const Point& rPnt, SdrHdlKind eNewKind); + + const SmartTagReference& getTag() const { return mxSmartTag; } + +private: + SmartTagReference mxSmartTag; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tablefunction.hxx b/sd/source/ui/inc/tablefunction.hxx new file mode 100644 index 000000000..fe32f16b5 --- /dev/null +++ b/sd/source/ui/inc/tablefunction.hxx @@ -0,0 +1,32 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <tools/stream.hxx> +#include <drawdoc.hxx> + +namespace sd +{ +void CreateTableFromRTF(SvStream& rStream, SdDrawDocument* pModel); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tabtempl.hxx b/sd/source/ui/inc/tabtempl.hxx new file mode 100644 index 000000000..d32388d7b --- /dev/null +++ b/sd/source/ui/inc/tabtempl.hxx @@ -0,0 +1,57 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/styledlg.hxx> +#include <svx/xtable.hxx> + +class SdrModel; +class SfxObjectShell; +class SdrView; + +/** + * Template-Tab-Dialog + */ +class SdTabTemplateDlg final : public SfxStyleDialogController +{ +private: + const SfxObjectShell& rDocShell; + SdrView* pSdrView; + + XColorListRef pColorList; + XGradientListRef pGradientList; + XHatchListRef pHatchingList; + XBitmapListRef pBitmapList; + XPatternListRef pPatternList; + XDashListRef pDashList; + XLineEndListRef pLineEndList; + + virtual void PageCreated(const OString& rId, SfxTabPage &rPage) override; + virtual void RefreshInputSet() override; + +public: + SdTabTemplateDlg(weld::Window* pParent, + const SfxObjectShell* pDocShell, + SfxStyleSheetBase& rStyleBase, + SdrModel const * pModel, + SdrView* pView); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/titledockwin.hxx b/sd/source/ui/inc/titledockwin.hxx new file mode 100644 index 000000000..59e7e04b3 --- /dev/null +++ b/sd/source/ui/inc/titledockwin.hxx @@ -0,0 +1,94 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/dockwin.hxx> +#include <tools/svborder.hxx> +#include <vcl/vclptr.hxx> + +class ToolBox; + +namespace sd +{ + class TitledDockingWindow : public SfxDockingWindow + { + public: + TitledDockingWindow( + SfxBindings* i_pBindings, SfxChildWindow* i_pChildWindow, + vcl::Window* i_pParent + ); + + virtual ~TitledDockingWindow() override; + virtual void dispose() override; + + /** sets a title to be displayed in the docking window + */ + void SetTitle( const OUString& i_rTitle ); + + /** returns the content window, which is to be used as parent window for any content to be displayed + in the docking window. + */ + vcl::Window& GetContentWindow() { return *m_aContentWindow; } + const vcl::Window& GetContentWindow() const { return *m_aContentWindow; } + + /** Return the border that is painted around the inner window as + decoration. + */ + const SvBorder& GetDecorationBorder() const { return m_aBorder; } + + protected: + // Window overridables + virtual void Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& i_rArea) override; + virtual void Resize() override; + virtual void StateChanged( StateChangedType i_nType ) override; + virtual void DataChanged( const DataChangedEvent& i_rDataChangedEvent ) override; + virtual void SetText( const OUString& i_rText ) override; + + virtual void ApplySettings(vcl::RenderContext& rRenderContext) override; + protected: + /** internal version of ResetToolBox + */ + void impl_resetToolBox(); + + private: + DECL_LINK(OnToolboxItemSelected, ToolBox*, void); + + void impl_layout(); + + private: + OUString m_sTitle; + VclPtr<ToolBox> m_aToolbox; + VclPtr<Window> m_aContentWindow; + + /** The border that is painted around the inner window. The bevel + shadow lines are part of the border, so where the border is 0 no + such line is painted. + */ + SvBorder m_aBorder; + + /** Height of the title bar. Calculated in impl_layout(). + */ + int m_nTitleBarHeight; + + }; + +} // namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tmplctrl.hxx b/sd/source/ui/inc/tmplctrl.hxx new file mode 100644 index 000000000..bdf6eed86 --- /dev/null +++ b/sd/source/ui/inc/tmplctrl.hxx @@ -0,0 +1,40 @@ +/* -*- 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 . + */ +#pragma once + +#include <sfx2/stbitem.hxx> + +class SdTemplateControl final : public SfxStatusBarControl +{ +public: + SdTemplateControl( sal_uInt16 nSlotId, sal_uInt16 nId, StatusBar& rStb ); + virtual ~SdTemplateControl() override; + + virtual void StateChangedAtStatusBarControl( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState ) override; + virtual void Paint( const UserDrawEvent& rEvt ) override; + + SFX_DECL_STATUSBAR_CONTROL(); + +private: + virtual void Command( const CommandEvent& rCEvt ) override; + + OUString msTemplate; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tools/AsynchronousCall.hxx b/sd/source/ui/inc/tools/AsynchronousCall.hxx new file mode 100644 index 000000000..fa4c18020 --- /dev/null +++ b/sd/source/ui/inc/tools/AsynchronousCall.hxx @@ -0,0 +1,77 @@ +/* -*- 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 . + */ + +#pragma once + +#include <vcl/timer.hxx> + +#include <memory> +#include <functional> + +namespace sd::tools +{ +/** Store a function object and execute it asynchronous. + + The features of this class are: + a) It provides a wrapper around a VCL Timer so that generic function + objects can be used. + b) When more than one function objects are posted to be executed later + then the pending ones are erased and only the last one will actually be + executed. + + Use this class like this: + aInstanceOfAsynchronousCall.Post( + ::std::bind( + ::std::mem_fun(&DrawViewShell::SwitchPage), + pDrawViewShell, + 11)); +*/ +class AsynchronousCall +{ +public: + /** Create a new asynchronous call. Each object of this class processes + one (semantical) type of call. + */ + AsynchronousCall(); + + ~AsynchronousCall(); + + /** Post a function object that is to be executed asynchronously. When + this method is called while the current function object has not been + executed then the latter is destroyed and only the given function + object will be executed. + @param rFunction + The function object that may be called asynchronously in the + near future. + */ + typedef ::std::function<void()> AsynchronousFunction; + void Post(const AsynchronousFunction& rFunction); + +private: + Timer maTimer; + /** The function object that will be executed when the TimerCallback + function is called the next time. This pointer may be NULL. + */ + ::std::unique_ptr<AsynchronousFunction> mpFunction; + DECL_LINK(TimerCallback, Timer*, void); +}; + +} // end of namespace ::sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tools/AsynchronousTask.hxx b/sd/source/ui/inc/tools/AsynchronousTask.hxx new file mode 100644 index 000000000..696423439 --- /dev/null +++ b/sd/source/ui/inc/tools/AsynchronousTask.hxx @@ -0,0 +1,49 @@ +/* -*- 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 . + */ + +#pragma once + +namespace sd::tools +{ +/** Interface for the asynchronous execution of a task. This interface + allows a controller to run the task either timer based with a fixed + amount of time between the steps or thread based one step right after + the other. +*/ +class AsynchronousTask +{ +public: + /** Run the next step of the task. After HasNextStep() returns false + this method should ignore further calls. + */ + virtual void RunNextStep() = 0; + + /** Return <TRUE/> when there is at least one more step to execute. + When the task has been executed completely then <FALSE/> is + returned. + */ + virtual bool HasNextStep() = 0; + +protected: + ~AsynchronousTask() {} +}; + +} // end of namespace ::sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tools/ConfigurationAccess.hxx b/sd/source/ui/inc/tools/ConfigurationAccess.hxx new file mode 100644 index 000000000..b86a30fff --- /dev/null +++ b/sd/source/ui/inc/tools/ConfigurationAccess.hxx @@ -0,0 +1,144 @@ +/* -*- 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 . + */ + +#pragma once + +#include <rtl/ustring.hxx> +#include <com/sun/star/uno/XInterface.hpp> + +#include <vector> +#include <functional> + +namespace com::sun::star::container { class XHierarchicalNameAccess; } +namespace com::sun::star::container { class XNameAccess; } +namespace com::sun::star::lang { class XMultiServiceFactory; } +namespace com::sun::star::uno { class XComponentContext; } + +namespace sd::tools { + +/** This class gives access to the configuration. Create an object of this + class for one node of the configuration. This will be the root node. + Its children are then accessible through the new ConfigurationAccess + object. +*/ +class ConfigurationAccess +{ +public: + enum WriteMode { READ_WRITE, READ_ONLY }; + + /** Create a new object to access the configuration entries below the + given root. + @param rsRootName + Name of the root. + @param eMode + This flag specifies whether to give read-write or read-only + access. + */ + ConfigurationAccess( + const OUString& rsRootName, + const WriteMode eMode); + + ConfigurationAccess( + const css::uno::Reference<css::uno::XComponentContext>& rxContext, + const OUString& rsRootName, + const WriteMode eMode); + + /** Return a configuration node below the root of the called object. + @param rsPathToNode + The relative path from the root (as given the constructor) to + the node. + @return + The type of the returned node varies with the requested node. + It is empty when the node was not found. + */ + css::uno::Any GetConfigurationNode ( + const OUString& rsPathToNode); + + /** Return a configuration node below the given node. + @param rxNode + The node that acts as root to the given relative path. + @param rsPathToNode + The relative path from the given node to the requested node. + @return + The type of the returned node varies with the requested node. + It is empty when the node was not found. + */ + static css::uno::Any GetConfigurationNode ( + const css::uno::Reference<css::container::XHierarchicalNameAccess>& rxNode, + const OUString& rsPathToNode); + + /** Write any changes that have been made back to the configuration. + This call is ignored when the called ConfigurationAccess object was + not create with read-write mode. + */ + void CommitChanges(); + + /** This functor is typically called for every item in a set. Its two + parameters are the name of key item (often of no further interest) + and the value of the item. + */ + typedef ::std::function<void ( + const OUString&, + const std::vector<css::uno::Any>&) > Functor; + + /** Execute a functor for all elements of the given container. + @param rxContainer + The container is a XNameAccess to a list of the configuration. + This can be a node returned by GetConfigurationNode(). + @param rArguments + The functor is called with arguments that are children of each + element of the container. The set of children is specified in this + list. + @param rFunctor + The functor to be executed for some or all of the elements in + the given container. + */ + static void ForAll ( + const css::uno::Reference<css::container::XNameAccess>& rxContainer, + const ::std::vector<OUString>& rArguments, + const Functor& rFunctor); + + /** Fill a list with the string contents of all sub-elements in the given container. + @param rxContainer + The container is a XNameAccess to a list of the configuration. + This can be a node returned by GetConfigurationNode(). + @param rsArgument + This specifies which string children of the elements in the + container are to be inserted into the list. The specified child + has to be of type string. + @param rList + The list to be filled. + */ + static void FillList( + const css::uno::Reference<css::container::XNameAccess>& rxContainer, + const OUString& rsArgument, + ::std::vector<OUString>& rList); + +private: + css::uno::Reference<css::uno::XInterface> mxRoot; + + void Initialize ( + const css::uno::Reference<css::lang::XMultiServiceFactory>& rxProvider, + const OUString& rsRootName, + const WriteMode eMode); +}; + +} // end of namespace sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tools/GraphicSizeCheck.hxx b/sd/source/ui/inc/tools/GraphicSizeCheck.hxx new file mode 100644 index 000000000..44f78e4eb --- /dev/null +++ b/sd/source/ui/inc/tools/GraphicSizeCheck.hxx @@ -0,0 +1,116 @@ +/* -*- 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/. + * + */ + +#pragma once + +#include <memory> +#include <drawdoc.hxx> +#include <svx/GenericCheckDialog.hxx> +#include <svx/svdograf.hxx> + +namespace sd +{ +/** + * Class responsible to check if a graphic object violates the size + * constraints and store the results. + */ +class GraphicSizeViolation final +{ +private: + SdrGrafObj* m_pGraphicObject; + + sal_Int32 m_nLowDPILimit = 0; + sal_Int32 m_nHighDPILimit = 0; + + sal_Int32 m_nDPIX = 0; + sal_Int32 m_nDPIY = 0; + +public: + GraphicSizeViolation(sal_Int32 nDPI, SdrGrafObj* pGraphicObject); + bool check(); + + const OUString& getGraphicName(); + + SdrGrafObj* getObject() const { return m_pGraphicObject; } + + bool isDPITooLow() { return m_nDPIX < m_nLowDPILimit || m_nDPIY < m_nLowDPILimit; } + + bool isDPITooHigh() { return m_nDPIX > m_nHighDPILimit || m_nDPIY > m_nHighDPILimit; } + + sal_Int32 getDPIX() { return m_nDPIX; } + + sal_Int32 getDPIY() { return m_nDPIY; } +}; + +/** + * Run the graphic size checks for all the graphic objects in the DOM + * and store a list of violations. + */ +class GraphicSizeCheck final +{ +private: + SdDrawDocument* m_pDocument; + std::vector<std::unique_ptr<GraphicSizeViolation>> m_aGraphicSizeViolationList; + +public: + GraphicSizeCheck(SdDrawDocument* pDocument) + : m_pDocument(pDocument) + { + } + + void check(); + + std::vector<std::unique_ptr<GraphicSizeViolation>>& getViolationList() + { + return m_aGraphicSizeViolationList; + } +}; + +/** The UI part of the GraphicSizeViolation used by GenericCheckDialog */ +class GraphicSizeCheckGUIEntry : public svx::CheckData +{ +private: + SdDrawDocument* m_pDocument; + std::unique_ptr<GraphicSizeViolation> m_pViolation; + +public: + GraphicSizeCheckGUIEntry(SdDrawDocument* pDocument, + std::unique_ptr<GraphicSizeViolation>&& pViolation) + : m_pDocument(pDocument) + , m_pViolation(std::move(pViolation)) + { + } + + OUString getText() override; + + bool canMarkObject() override { return true; } + + void markObject() override; + + bool hasProperties() override { return true; } + + void runProperties() override; +}; + +/** + * The UI part presenting the graphic size check results, which is + * used by GenericCheckDialog + */ +class GraphicSizeCheckGUIResult : public svx::CheckDataCollection +{ +public: + GraphicSizeCheckGUIResult(SdDrawDocument* m_pDocument); + + OUString getTitle() override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tools/IconCache.hxx b/sd/source/ui/inc/tools/IconCache.hxx new file mode 100644 index 000000000..fef994764 --- /dev/null +++ b/sd/source/ui/inc/tools/IconCache.hxx @@ -0,0 +1,70 @@ +/* -*- 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 . + */ + +#pragma once + +#include <memory> +#include <tools/SdGlobalResourceContainer.hxx> +#include <vcl/image.hxx> + +namespace sd +{ +/** This simple class stores frequently used icons so that the classes that + use the icons do not have to store them in every one of their + instances. + + Icons are addressed over their resource id and are loaded on demand. + + This cache acts like a singleton with a lifetime equal to that of the sd + module. +*/ +class IconCache final : public SdGlobalResource +{ +public: + /** The lifetime of the returned reference is limited to that of the sd + module. + */ + static IconCache& Instance(); + + /** Return the icon with the given resource id. + @return + The returned Image may be empty when there is no icon for the + given id or an error occurred. Should not happen under normal + circumstances. + */ + Image GetIcon(const OUString& rResourceId); + +private: + class Implementation; + ::std::unique_ptr<Implementation> mpImpl; + + /** The constructor creates the one instance of the cache and registers + it at the SdGlobalResourceContainer to limit is lifetime to that of + the sd module. + */ + IconCache(); + + /** This destructor is called by SdGlobalResourceContainer. + */ + virtual ~IconCache() override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tools/IdleDetection.hxx b/sd/source/ui/inc/tools/IdleDetection.hxx new file mode 100644 index 000000000..decf5ff26 --- /dev/null +++ b/sd/source/ui/inc/tools/IdleDetection.hxx @@ -0,0 +1,89 @@ +/* -*- 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 . + */ + +#pragma once + +#include <o3tl/typed_flags_set.hxx> + +namespace vcl { class Window; } + +namespace sd::tools { + enum class IdleState { + /** When GetIdleState() returns this value, then the system is idle. + */ + Idle = 0x0000, + + /** There are system event pending. + */ + SystemEventPending = 0x0001, + + /** A full screen slide show is running and is active. In contrast + there may be a full screen show be running in an inactive window, + i.e. in the background. + */ + FullScreenShowActive = 0x0002, + + /** A slide show is running in a window. + */ + WindowShowActive = 0x0004, + + /** A window is being painted. + */ + WindowPainting = 0x0008, + }; +} // end of namespace ::sd::tools +namespace o3tl { + template<> struct typed_flags<::sd::tools::IdleState> : is_typed_flags<::sd::tools::IdleState, 0x0f> {}; +} + +namespace sd::tools { + +/** Detect whether the system is idle and some time consuming operation may + be carried out. This class distinguishes between different states of + idle-ness. +*/ +class IdleDetection +{ +public: + /** Determine whether the system is idle. + @param pWindow + When a valid Window pointer is given then it is checked + whether the window is currently being painting. + @return + This method either returns IdleState::Idle or a combination of + IdleStates values or-ed together that describe what the system + is currently doing so that the caller can decide what to do. + */ + static IdleState GetIdleState (const vcl::Window* pWindow); + +private: + /** Check whether there are input events pending. + */ + static IdleState CheckInputPending(); + + /** Check whether a slide show is running full screen or in a window. + */ + static IdleState CheckSlideShowRunning(); + + static IdleState CheckWindowPainting (const vcl::Window& rWindow); +}; + +} // end of namespace ::sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tools/PropertySet.hxx b/sd/source/ui/inc/tools/PropertySet.hxx new file mode 100644 index 000000000..c432783da --- /dev/null +++ b/sd/source/ui/inc/tools/PropertySet.hxx @@ -0,0 +1,114 @@ +/* -*- 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 . + */ + +#pragma once + +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/compbase.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <map> +#include <memory> + +namespace sd::tools { + +typedef ::cppu::WeakComponentImplHelper < + css::beans::XPropertySet +> PropertySetInterfaceBase; + +/** A very simple implementation of the XPropertySet interface. It does not + support constrained properties and thus does not support vetoable + listeners. It does not support the optional property set info. + + In order to use it you have to derive from this class and implement the + GetPropertyValue() and SetPropertyValue() methods. +*/ +class PropertySet + : protected ::cppu::BaseMutex, + public PropertySetInterfaceBase +{ +public: + explicit PropertySet(); + virtual ~PropertySet() override; + + virtual void SAL_CALL disposing() override; + + // XPropertySet + + virtual css::uno::Reference<css::beans::XPropertySetInfo> + SAL_CALL getPropertySetInfo() override; + + virtual void SAL_CALL setPropertyValue ( + const OUString& rsPropertyName, + const css::uno::Any& rsPropertyValue) override; + + virtual css::uno::Any SAL_CALL getPropertyValue (const OUString& rsPropertyName) override; + + virtual void SAL_CALL addPropertyChangeListener ( + const OUString& rsPropertyName, + const css::uno::Reference<css::beans::XPropertyChangeListener>& rxListener) override; + + virtual void SAL_CALL removePropertyChangeListener ( + const OUString& rsPropertyName, + const css::uno::Reference<css::beans::XPropertyChangeListener>& rxListener) override; + + virtual void SAL_CALL addVetoableChangeListener ( + const OUString& rsPropertyName, + const css::uno::Reference<css::beans::XVetoableChangeListener>& rxListener) override; + + virtual void SAL_CALL removeVetoableChangeListener ( + const OUString& rsPropertyName, + const css::uno::Reference<css::beans::XVetoableChangeListener>& rxListener) override; + +protected: + /** Return the requested property value. + @throw css::beans::UnknownPropertyException when the + property is not supported. + */ + virtual css::uno::Any GetPropertyValue (const OUString& rsPropertyName) = 0; + /** Set the given property value. + @return the old value. + @throw css::beans::UnknownPropertyException when the + property is not supported. + */ + virtual css::uno::Any SetPropertyValue ( + const OUString& rsPropertyName, + const css::uno::Any& rValue) = 0; + +private: + typedef ::std::multimap<OUString, + css::uno::Reference<css::beans::XPropertyChangeListener> > ChangeListenerContainer; + std::unique_ptr<ChangeListenerContainer> mpChangeListeners; + + /** Call all listeners that are registered for the given property name. + Call this method with an empty property name to call listeners that + are registered for all properties. + */ + void CallListeners ( + const OUString& rsPropertyName, + const css::beans::PropertyChangeEvent& rEvent); + + /** @throws css::lang::DisposedException when the object has already been + disposed. + */ + void ThrowIfDisposed(); +}; + +} // end of namespace ::sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tools/SdGlobalResourceContainer.hxx b/sd/source/ui/inc/tools/SdGlobalResourceContainer.hxx new file mode 100644 index 000000000..a582ffa72 --- /dev/null +++ b/sd/source/ui/inc/tools/SdGlobalResourceContainer.hxx @@ -0,0 +1,105 @@ +/* -*- 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 . + */ + +#pragma once + +#include <memory> +#include <sal/types.h> +#include <o3tl/deleter.hxx> + +namespace com::sun::star::uno +{ +template <class interface_type> class Reference; +} +namespace com::sun::star::uno +{ +class XInterface; +} + +namespace sd +{ +class SdGlobalResource +{ +public: + virtual ~SdGlobalResource() COVERITY_NOEXCEPT_FALSE{}; +}; + +/** The purpose of this container is to hold references to resources that + are globally available to all interested objects and to destroy them + when the sd module is destroyed. Examples for resources can be + containers of bitmaps or the container of master pages used by the + MasterPagesSelector objects in the task panel. + + It works like a singleton in that there is one instance per sd module. + Resources can be added (by themselves or their owners) to the + container. The main task of the container is the destruction of all + resources that have been added to it. + + As you may note, there is no method to get a resource from the + container. It is the task of the resource to provide other means of + access to it. + + The reason for this design is not to have to change the SdModule + destructor every time when there is a new resource. This is done by + reversing the dependency between module and resource: the resource knows + about the module--this container class to be more precisely--and tells + it to destroy the resource when the sd module is at the end of its + lifetime. +*/ +class SdGlobalResourceContainer final +{ +public: + static SdGlobalResourceContainer& Instance(); + + /** Add a resource to the container. The ownership of the resource is + transferred to the container. The resource is destroyed when the + container is destroyed, i.e. when the sd module is destroyed. + + When in doubt, use the shared_ptr variant of this method. + */ + void AddResource(::std::unique_ptr<SdGlobalResource> pResource); + + /** Add a resource to the container. By using a shared_ptr and + releasing it only when the SgGlobalResourceContainer is destroyed + the given resource is kept alive at least that long. When at the + time of the destruction of SgGlobalResourceContainer no other + references exist the resource is destroyed as well. + */ + void AddResource(const std::shared_ptr<SdGlobalResource>& pResource); + + /** Add a resource that is implemented as UNO object. Destruction + (when the sd modules is unloaded) is done by a) calling dispose() + when the XComponent is supported and by b) releasing the reference. + */ + void AddResource(const css::uno::Reference<css::uno::XInterface>& rxResource); + +private: + friend class SdGlobalResourceContainerInstance; + friend struct o3tl::default_delete<SdGlobalResourceContainer>; + + class Implementation; + ::std::unique_ptr<Implementation> mpImpl; + + SdGlobalResourceContainer(); + ~SdGlobalResourceContainer(); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tools/SlotStateListener.hxx b/sd/source/ui/inc/tools/SlotStateListener.hxx new file mode 100644 index 000000000..54a2e463d --- /dev/null +++ b/sd/source/ui/inc/tools/SlotStateListener.hxx @@ -0,0 +1,138 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/frame/XStatusListener.hpp> +#include <comphelper/compbase.hxx> +#include <tools/link.hxx> +#include <cppuhelper/weakref.hxx> + +namespace com::sun::star::frame { class XDispatch; } +namespace com::sun::star::frame { class XDispatchProvider; } +namespace com::sun::star::frame { class XStatusListener; } +namespace com::sun::star::frame { struct FeatureStateEvent; } + +namespace sd::tools { + +typedef comphelper::WeakComponentImplHelper< + css::frame::XStatusListener + > SlotStateListenerInterfaceBase; + +/** Listen for state changes of slots. This class has been created in order + to be informed when the support for vertical writing changes but it can + be used to relay state changes of other slots as well. +*/ +class SlotStateListener final + : public SlotStateListenerInterfaceBase +{ +public: + /** This convenience version of the constructor takes all parameters + that are necessary to observe a single slot. See descriptions of + the SetCallback(), ConnectToFrame(), and ObserveSlot() methods for + explanations about the parameters. + */ + SlotStateListener ( + Link<const OUString&,void> const & rCallback, + const css::uno::Reference<css::frame::XDispatchProvider>& rxDispatchProvider, + const OUString& rSlotName); + + /** The constructor de-registers all remaining listeners. Usually a prior + dispose() call should have done that already. + */ + virtual ~SlotStateListener() override; + + /** Set the callback to the given value. Whenever one of the observed + slots changes its state this callback is informed about it. + Changing the callback does not release the listeners. + @throws DisposedException + */ + void SetCallback (const Link<const OUString&,void>& rCallback); + + /** Set the frame whose slots shall be observed. When an object of this + class is already observing slots of another frame then these + listeners are released first. + @throws DisposedException + */ + void ConnectToDispatchProvider ( + const css::uno::Reference<css::frame::XDispatchProvider>& rxDispatchProvider); + + /** Observe the slot specified by the given name. Note that + ConnectToFrame() has to have been called earlier. + @param rSlotName + The name of the slot to observe. An example is + ".uno:VerticalTextState". + @throws DisposedException + */ + void ObserveSlot (const OUString& rSlotName); + + //===== frame::XStatusListener ========================================== + + /** Called by slot state change broadcasters. In turn the callback is + informed about the state change. + @throws DisposedException + */ + virtual void SAL_CALL + statusChanged ( + const css::frame::FeatureStateEvent& rState) override; + + //===== lang::XEventListener ============================================ + + virtual void SAL_CALL + disposing(const css::lang::EventObject& rEvent) override; + +private: + /** This method is called by the WeakComponentImplHelper base class in + reaction to a XComponent::dispose() call. It releases all currently + active listeners. + */ + virtual void disposing(std::unique_lock<std::mutex>&) override; + + Link<const OUString&,void> maCallback; + + /** Remember the URLs that describe slots whose state changes we are + listening to. + */ + std::vector<css::util::URL> maRegisteredURLList; + + css::uno::WeakReference<css::frame::XDispatchProvider> mxDispatchProviderWeak; + + /** Deregister all currently active state change listeners. + */ + void ReleaseListeners(); + + /** @throws css::lang::DisposedException when the object has already been + disposed. + */ + void ThrowIfDisposed(); + + /** Transform the given string into a URL object. + */ + static css::util::URL MakeURL (const OUString& rSlotName); + + /** Return an XDispatch object for the given URL. + */ + css::uno::Reference<css::frame::XDispatch> + GetDispatch ( + const css::util::URL& rURL) const; +}; + +} // end of namespace ::sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tools/TimerBasedTaskExecution.hxx b/sd/source/ui/inc/tools/TimerBasedTaskExecution.hxx new file mode 100644 index 000000000..bbff549f1 --- /dev/null +++ b/sd/source/ui/inc/tools/TimerBasedTaskExecution.hxx @@ -0,0 +1,89 @@ +/* -*- 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 . + */ + +#pragma once + +#include <vcl/timer.hxx> + +#include <memory> + +namespace sd::tools { + +class AsynchronousTask; + +/** Execute an AsynchronousTask timer based, i.e. every + nMillisecondsBetweenSteps milliseconds as much steps are executed as fit + into a nMaxTimePerStep millisecond interval. + + When a task is executed completely, i.e. HasNextStep() returns <FALSE/>, + the TimerBasedTaskExecution destroys itself. This, of course, works + only if the creating instance does not hold a shared_ptr to that object. +*/ +class TimerBasedTaskExecution +{ +public: + /** Create a new object of this class. + @param rpTask + The AsynchronousTask that is to be executed. + @param nMillisecondsBetweenSteps + Wait at least this long between the execution of steps. Note + that more than one step may be executed in succession. + @param nMaxTimePerStep + The maximal time for executing steps without yielding control. + */ + static std::shared_ptr<TimerBasedTaskExecution> Create ( + const std::shared_ptr<AsynchronousTask>& rpTask, + sal_uInt32 nMillisecondsBetweenSteps, + sal_uInt32 nMaxTimePerStep); + + /** Stop the execution of the task and release the shared pointer to + itself so that it will eventually be destroyed. + */ + void Release(); + + /** Convenience method that calls Release() on the given task. It + checks the given weak_ptr for being expired and catches bad_weak_ptr + exceptions. + */ + static void ReleaseTask (const std::weak_ptr<TimerBasedTaskExecution>& rpTask); + +private: + std::shared_ptr<AsynchronousTask> mpTask; + Timer maTimer; + /** This shared_ptr to this is used to destroy a TimerBasedTaskExecution + object when its task has been executed completely. + */ + std::shared_ptr<TimerBasedTaskExecution> mpSelf; + sal_uInt32 mnMaxTimePerStep; + + TimerBasedTaskExecution ( + const std::shared_ptr<AsynchronousTask>& rpTask, + sal_uInt32 nMillisecondsBetweenSteps, + sal_uInt32 nMaxTimePerStep); + ~TimerBasedTaskExecution(); + + class Deleter; + friend class Deleter; + + DECL_LINK(TimerCallback, Timer *, void); +}; + +} // end of namespace ::sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tpaction.hxx b/sd/source/ui/inc/tpaction.hxx new file mode 100644 index 000000000..893192d25 --- /dev/null +++ b/sd/source/ui/inc/tpaction.hxx @@ -0,0 +1,104 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/presentation/ClickAction.hpp> +#include <sfx2/tabdlg.hxx> +#include <sfx2/basedlgs.hxx> +#include <svx/xtable.hxx> +#include <unotools/resmgr.hxx> +#include "sdtreelb.hxx" + +#include <vector> + +namespace sd { + class View; +} +class SdDrawDocument; + +/** + * Effect-SingleTab-Dialog + */ +class SdActionDlg final : public SfxSingleTabDialogController +{ +public: + SdActionDlg(weld::Window* pParent, const SfxItemSet* pAttr, ::sd::View const * pView); +}; + +/** + * Interaction-Tab-Page + */ +class SdTPAction final : public SfxTabPage +{ +private: + const ::sd::View* mpView; + SdDrawDocument* mpDoc; + XColorListRef pColList; + + bool bTreeUpdated; + std::vector<css::presentation::ClickAction> maCurrentActions; + OUString aLastFile; + ::std::vector< tools::Long > aVerbVector; + + std::unique_ptr<weld::ComboBox> m_xLbAction; + std::unique_ptr<weld::Label> m_xFtTree; // jump destination controls + std::unique_ptr<SdPageObjsTLV> m_xLbTree; + std::unique_ptr<SdPageObjsTLV> m_xLbTreeDocument; + std::unique_ptr<weld::TreeView> m_xLbOLEAction; + std::unique_ptr<weld::Frame> m_xFrame; + std::unique_ptr<weld::Entry> m_xEdtSound; + std::unique_ptr<weld::Entry> m_xEdtBookmark; + std::unique_ptr<weld::Entry> m_xEdtDocument; + std::unique_ptr<weld::Entry> m_xEdtProgram; + std::unique_ptr<weld::Entry> m_xEdtMacro; + std::unique_ptr<weld::Button> m_xBtnSearch; + std::unique_ptr<weld::Button> m_xBtnSeek; + + DECL_LINK( ClickSearchHdl, weld::Button&, void ); + DECL_LINK( ClickActionHdl, weld::ComboBox&, void ); + DECL_LINK( SelectTreeHdl, weld::TreeView&, void ); + DECL_LINK( CheckFileHdl, weld::Widget&, void ); + + void UpdateTree(); + void OpenFileDialog(); + css::presentation::ClickAction GetActualClickAction(); + void SetActualClickAction( css::presentation::ClickAction eCA ); + void SetEditText( OUString const & rStr ); + OUString GetEditText( bool bURL = false ); +public: + SD_DLLPUBLIC static TranslateId GetClickActionSdResId(css::presentation::ClickAction eCA); + + SdTPAction(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rInAttrs); + virtual ~SdTPAction() override; + + static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& ); + + virtual bool FillItemSet( SfxItemSet* ) override; + virtual void Reset( const SfxItemSet * ) override; + + virtual void ActivatePage( const SfxItemSet& rSet ) override; + virtual DeactivateRC DeactivatePage( SfxItemSet* pSet ) override; + + void Construct(); + + void SetView( const ::sd::View* pSdView ); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tpoption.hxx b/sd/source/ui/inc/tpoption.hxx new file mode 100644 index 000000000..8657db27a --- /dev/null +++ b/sd/source/ui/inc/tpoption.hxx @@ -0,0 +1,144 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/tabdlg.hxx> +#include <svx/optgrid.hxx> + +/** + * Option-Tab-Page: Snap + */ +class SdTpOptionsSnap final : public SvxGridTabPage +{ +public: + SdTpOptionsSnap(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rInAttrs); + static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* ); + virtual ~SdTpOptionsSnap() override; + + virtual bool FillItemSet( SfxItemSet* ) override; + virtual void Reset( const SfxItemSet * ) override; +}; + +/** + * Option-Tab-Page: Contents + */ +class SdTpOptionsContents final : public SfxTabPage +{ +private: + std::unique_ptr<weld::CheckButton> m_xCbxRuler; + std::unique_ptr<weld::CheckButton> m_xCbxDragStripes; + std::unique_ptr<weld::CheckButton> m_xCbxHandlesBezier; + std::unique_ptr<weld::CheckButton> m_xCbxMoveOutline; + +public: + SdTpOptionsContents(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rInAttrs); + static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* ); + virtual ~SdTpOptionsContents() override; + + virtual bool FillItemSet( SfxItemSet* ) override; + virtual void Reset( const SfxItemSet * ) override; +}; + +/** + * Option-Tab-Page: View + */ + +class SdTpOptionsMisc final : public SfxTabPage +{ + friend class SdModule; + +private: + sal_uInt32 nWidth; + sal_uInt32 nHeight; + OUString aInfo1; + OUString aInfo2; + + MapUnit ePoolUnit; + + std::unique_ptr<weld::CheckButton> m_xCbxQuickEdit; + std::unique_ptr<weld::CheckButton> m_xCbxPickThrough; + + std::unique_ptr<weld::Frame> m_xNewDocumentFrame; + std::unique_ptr<weld::CheckButton> m_xCbxStartWithTemplate; + + std::unique_ptr<weld::CheckButton> m_xCbxMasterPageCache; + std::unique_ptr<weld::CheckButton> m_xCbxCopy; + std::unique_ptr<weld::CheckButton> m_xCbxMarkedHitMovesAlways; + std::unique_ptr<weld::Frame> m_xPresentationFrame; + + std::unique_ptr<weld::ComboBox> m_xLbMetric; + std::unique_ptr<weld::MetricSpinButton> m_xMtrFldTabstop; + + std::unique_ptr<weld::CheckButton> m_xCbxEnableSdremote; + std::unique_ptr<weld::CheckButton> m_xCbxEnablePresenterScreen; + std::unique_ptr<weld::CheckButton> m_xCbxUsePrinterMetrics; + std::unique_ptr<weld::CheckButton> m_xCbxCompatibility; + + //Scale + std::unique_ptr<weld::Frame> m_xScaleFrame; + std::unique_ptr<weld::ComboBox> m_xCbScale; + std::unique_ptr<weld::Label> m_xNewDocLb; + std::unique_ptr<weld::Label> m_xFiInfo1; + std::unique_ptr<weld::MetricSpinButton> m_xMtrFldOriginalWidth; + std::unique_ptr<weld::Label> m_xWidthLb; + std::unique_ptr<weld::Label> m_xHeightLb; + std::unique_ptr<weld::Label> m_xFiInfo2; + std::unique_ptr<weld::MetricSpinButton> m_xMtrFldOriginalHeight; + std::unique_ptr<weld::CheckButton> m_xCbxDistort; + std::unique_ptr<weld::MetricSpinButton> m_xMtrFldInfo1; + std::unique_ptr<weld::MetricSpinButton> m_xMtrFldInfo2; + + static OUString GetScale( sal_Int32 nX, sal_Int32 nY ); + static bool SetScale( std::u16string_view aScale, sal_Int32& rX, sal_Int32& rY ); + + DECL_LINK( SelectMetricHdl_Impl, weld::ComboBox&, void ); + + /** Enable or disable the controls in the compatibility section of the + 'general' tab page depending on whether there is at least one + document. + */ + void UpdateCompatibilityControls(); + + virtual void ActivatePage( const SfxItemSet& rSet ) override; + virtual DeactivateRC DeactivatePage( SfxItemSet* pSet ) override; + +public: + SdTpOptionsMisc(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rInAttrs); + static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* ); + virtual ~SdTpOptionsMisc() override; + + virtual bool FillItemSet( SfxItemSet* ) override; + virtual void Reset( const SfxItemSet * ) override; + + /** Hide Impress specific controls, make Draw specific controls visible + and arrange the visible controls. Do not call this method or the + <member>SetImpressMode()</member> method more than once. + */ + void SetDrawMode(); + + /** Hide Draw specific controls, make Impress specific controls visible + and arrange the visible controls. Do not call this method or the + <member>SetDrawMode()</member> method more than once. + */ + void SetImpressMode(); + virtual void PageCreated(const SfxAllItemSet& aSet) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/uiobject.hxx b/sd/source/ui/inc/uiobject.hxx new file mode 100644 index 000000000..06cb6105f --- /dev/null +++ b/sd/source/ui/inc/uiobject.hxx @@ -0,0 +1,35 @@ +/* -*- 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/. + */ + +#include <memory> +#include <vcl/uitest/uiobject.hxx> +#include "Window.hxx" + +class ImpressWindowUIObject final : public WindowUIObject +{ +public: + ImpressWindowUIObject(const VclPtr<sd::Window>& xWindow); + + virtual StringMap get_state() override; + + virtual void execute(const OUString& rAction, const StringMap& rParameters) override; + + virtual std::unique_ptr<UIObject> get_child(const OUString& rID) override; + + virtual std::set<OUString> get_children() const override; + + static std::unique_ptr<UIObject> create(vcl::Window* pWindow); + +private: + virtual OUString get_name() const override; + + VclPtr<sd::Window> mxWindow; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/unchss.hxx b/sd/source/ui/inc/unchss.hxx new file mode 100644 index 000000000..7c3845f8b --- /dev/null +++ b/sd/source/ui/inc/unchss.hxx @@ -0,0 +1,47 @@ +/* -*- 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 . + */ + +#pragma once + +#include <memory> + +#include <svl/itemset.hxx> + +#include <sdundo.hxx> + +class SfxItemSet; +class SfxStyleSheet; +class SdDrawDocument; + +class StyleSheetUndoAction final : public SdUndoAction +{ + SfxStyleSheet* mpStyleSheet; + + std::unique_ptr<SfxItemSet> mpNewSet; + std::unique_ptr<SfxItemSet> mpOldSet; + +public: + StyleSheetUndoAction(SdDrawDocument* pTheDoc, SfxStyleSheet* pTheStyleSheet, + const SfxItemSet* pTheNewItemSet); + + virtual void Undo() override; + virtual void Redo() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/undoback.hxx b/sd/source/ui/inc/undoback.hxx new file mode 100644 index 000000000..afbb13eca --- /dev/null +++ b/sd/source/ui/inc/undoback.hxx @@ -0,0 +1,58 @@ +/* -*- 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 . + */ + +#pragma once + +#include <memory> + +#include <sdundo.hxx> + +#include <svl/itemset.hxx> + +class SdDrawDocument; +class SdPage; +class SfxItemSet; +class SfxPoolItem; + +class SdBackgroundObjUndoAction final : public SdUndoAction +{ +private: + + SdPage& mrPage; + std::unique_ptr<SfxItemSet> mpItemSet; + std::unique_ptr<SfxPoolItem> mpFillBitmapItem; + bool mbHasFillBitmap; + + void ImplRestoreBackgroundObj(); + void saveFillBitmap(SfxItemSet &rItemSet); + void restoreFillBitmap(SfxItemSet &rItemSet); + +public: + SdBackgroundObjUndoAction( + SdDrawDocument& rDoc, + SdPage& rPage, + const SfxItemSet& rItemSet); + + virtual void Undo() override; + virtual void Redo() override; + + virtual SdUndoAction* Clone() const override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/undoheaderfooter.hxx b/sd/source/ui/inc/undoheaderfooter.hxx new file mode 100644 index 000000000..2c8c9c8e9 --- /dev/null +++ b/sd/source/ui/inc/undoheaderfooter.hxx @@ -0,0 +1,45 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sdundo.hxx> +#include <sdpage.hxx> +#include <sddllapi.h> + +class SdDrawDocument; + +/************************************************************************/ + +class SD_DLLPUBLIC SdHeaderFooterUndoAction final : public SdUndoAction +{ + SdPage* mpPage; + + const sd::HeaderFooterSettings maOldSettings; + const sd::HeaderFooterSettings maNewSettings; + +public: + SdHeaderFooterUndoAction( SdDrawDocument* pDoc, SdPage* pPage, const sd::HeaderFooterSettings& rNewSettings ); + virtual ~SdHeaderFooterUndoAction() override; + + virtual void Undo() override; + virtual void Redo() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/undolayer.hxx b/sd/source/ui/inc/undolayer.hxx new file mode 100644 index 000000000..431f60d4f --- /dev/null +++ b/sd/source/ui/inc/undolayer.hxx @@ -0,0 +1,56 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sdundo.hxx> + +class SdDrawDocument; +class SdrLayer; + +/************************************************************************/ + +class SdLayerModifyUndoAction final : public SdUndoAction +{ + +public: + SdLayerModifyUndoAction( SdDrawDocument* _pDoc, SdrLayer* pLayer, + const OUString& rOldLayerName, const OUString& rOldLayerTitle, const OUString& rOldLayerDesc, bool bOldIsVisible, bool bOldIsLocked, bool bOldIsPrintable, + const OUString& rNewLayerName, const OUString& rNewLayerTitle, const OUString& rNewLayerDesc, bool bNewIsVisible, bool bNewIsLocked, bool bNewIsPrintable ); + + virtual void Undo() override; + virtual void Redo() override; + +private: + SdrLayer* mpLayer; + OUString maOldLayerName; + OUString maOldLayerTitle; + OUString maOldLayerDesc; + bool mbOldIsVisible; + bool mbOldIsLocked; + bool mbOldIsPrintable; + OUString maNewLayerName; + OUString maNewLayerTitle; + OUString maNewLayerDesc; + bool mbNewIsVisible; + bool mbNewIsLocked; + bool mbNewIsPrintable; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/undopage.hxx b/sd/source/ui/inc/undopage.hxx new file mode 100644 index 000000000..87d5b5b21 --- /dev/null +++ b/sd/source/ui/inc/undopage.hxx @@ -0,0 +1,161 @@ +/* -*- 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 . + */ + +#pragma once + +#include <tools/gen.hxx> +#include <vcl/prntypes.hxx> + +#include <sdundo.hxx> + +class SdDrawDocument; +class SdPage; + +/************************************************************************/ + +class SdPageFormatUndoAction final : public SdUndoAction +{ + SdPage* mpPage; + + Size maOldSize; + sal_Int32 mnOldLeft; + sal_Int32 mnOldRight; + sal_Int32 mnOldUpper; + sal_Int32 mnOldLower; + Orientation meOldOrientation; + sal_uInt16 mnOldPaperBin; + bool mbOldFullSize; + + Size maNewSize; + sal_Int32 mnNewLeft; + sal_Int32 mnNewRight; + sal_Int32 mnNewUpper; + sal_Int32 mnNewLower; + bool mbNewScale; + Orientation meNewOrientation; + sal_uInt16 mnNewPaperBin; + bool mbNewFullSize; + +public: + SdPageFormatUndoAction( SdDrawDocument* pDoc, + SdPage* pThePage, + const Size& rOldSz, + sal_Int32 nOldLft, + sal_Int32 nOldRgt, + sal_Int32 nOldUpr, + sal_Int32 nOldLwr, + Orientation eOldOrient, + sal_uInt16 nOPaperBin, + bool bOFullSize, + + const Size& rNewSz, + sal_Int32 nNewLft, + sal_Int32 nNewRgt, + sal_Int32 nNewUpr, + sal_Int32 nNewLwr, + bool bNewScl, + Orientation eNewOrient, + sal_uInt16 nNPaperBin, + bool bNFullSize + ) : + SdUndoAction(pDoc), + mpPage (pThePage), + maOldSize (rOldSz), + mnOldLeft (nOldLft), + mnOldRight (nOldRgt), + mnOldUpper (nOldUpr), + mnOldLower (nOldLwr), + meOldOrientation(eOldOrient), + mnOldPaperBin (nOPaperBin), + mbOldFullSize (bOFullSize), + + maNewSize (rNewSz), + mnNewLeft (nNewLft), + mnNewRight (nNewRgt), + mnNewUpper (nNewUpr), + mnNewLower (nNewLwr), + mbNewScale (bNewScl), + meNewOrientation(eNewOrient), + mnNewPaperBin (nNPaperBin), + mbNewFullSize (bNFullSize) + + {} + virtual ~SdPageFormatUndoAction() override; + + virtual void Undo() override; + virtual void Redo() override; +}; + +/************************************************************************/ + +class SdPageLRUndoAction final : public SdUndoAction +{ + SdPage* mpPage; + + sal_Int32 mnOldLeft; + sal_Int32 mnOldRight; + sal_Int32 mnNewLeft; + sal_Int32 mnNewRight; + +public: + SdPageLRUndoAction( SdDrawDocument* pDoc, SdPage* pThePage, + sal_Int32 nOldLft, sal_Int32 nOldRgt, + sal_Int32 nNewLft, sal_Int32 nNewRgt ) : + SdUndoAction(pDoc), + mpPage (pThePage), + mnOldLeft (nOldLft), + mnOldRight (nOldRgt), + mnNewLeft (nNewLft), + mnNewRight (nNewRgt) + {} + virtual ~SdPageLRUndoAction() override; + + virtual void Undo() override; + virtual void Redo() override; +}; + +/************************************************************************/ + +class SdPageULUndoAction final : public SdUndoAction +{ + SdPage* mpPage; + + sal_Int32 mnOldUpper; + sal_Int32 mnOldLower; + sal_Int32 mnNewUpper; + sal_Int32 mnNewLower; + +public: + SdPageULUndoAction( SdDrawDocument* pDoc, SdPage* pThePage, + sal_Int32 nOldUpr, sal_Int32 nOldLwr, + sal_Int32 nNewUpr, sal_Int32 nNewLwr ) : + SdUndoAction(pDoc), + mpPage (pThePage), + mnOldUpper (nOldUpr), + mnOldLower (nOldLwr), + mnNewUpper (nNewUpr), + mnNewLower (nNewLwr) + {} + virtual ~SdPageULUndoAction() override; + + virtual void Undo() override; + virtual void Redo() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/unmodpg.hxx b/sd/source/ui/inc/unmodpg.hxx new file mode 100644 index 000000000..9248642bf --- /dev/null +++ b/sd/source/ui/inc/unmodpg.hxx @@ -0,0 +1,74 @@ +/* -*- 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 . + */ + +#pragma once + +#include <xmloff/autolayout.hxx> + +#include <sdundo.hxx> + +class SdDrawDocument; +class SdPage; + +class ModifyPageUndoAction final : public SdUndoAction +{ + SdPage* mpPage; + OUString maOldName; + OUString maNewName; + AutoLayout meOldAutoLayout; + AutoLayout meNewAutoLayout; + bool mbOldBckgrndVisible; + bool mbNewBckgrndVisible; + bool mbOldBckgrndObjsVisible; + bool mbNewBckgrndObjsVisible; + +public: + ModifyPageUndoAction( + SdDrawDocument* pTheDoc, + SdPage* pThePage, + const OUString& aTheNewName, + AutoLayout eTheNewAutoLayout, + bool bTheNewBckgrndVisible, + bool bTheNewBckgrndObjsVisible); + + virtual ~ModifyPageUndoAction() override; + virtual void Undo() override; + virtual void Redo() override; +}; + +class RenameLayoutTemplateUndoAction final : public SdUndoAction +{ +public: + RenameLayoutTemplateUndoAction( + SdDrawDocument* pDocument, + const OUString& rOldLayoutName, + const OUString& rNewLayoutName); + + virtual void Undo() override; + virtual void Redo() override; + + virtual OUString GetComment() const override; + +private: + OUString maOldName; + OUString maNewName; + const OUString maComment; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/unmovss.hxx b/sd/source/ui/inc/unmovss.hxx new file mode 100644 index 000000000..93e87cd40 --- /dev/null +++ b/sd/source/ui/inc/unmovss.hxx @@ -0,0 +1,44 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sdundo.hxx> +#include <stlsheet.hxx> +#include <vector> + +class SdDrawDocument; + +class SdMoveStyleSheetsUndoAction final : public SdUndoAction +{ + StyleSheetCopyResultVector maStyles; + std::vector< SdStyleSheetVector > maListOfChildLists; + bool mbMySheets; + +public: + SdMoveStyleSheetsUndoAction(SdDrawDocument* pTheDoc, StyleSheetCopyResultVector& rTheStyles, bool bInserted); + + virtual ~SdMoveStyleSheetsUndoAction() override; + virtual void Undo() override; + virtual void Redo() override; + + virtual OUString GetComment() const override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/unoaprms.hxx b/sd/source/ui/inc/unoaprms.hxx new file mode 100644 index 000000000..9ad327668 --- /dev/null +++ b/sd/source/ui/inc/unoaprms.hxx @@ -0,0 +1,148 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/presentation/AnimationEffect.hpp> +#include <com/sun/star/presentation/AnimationSpeed.hpp> +#include <com/sun/star/presentation/ClickAction.hpp> +#include <sdundo.hxx> +#include <tools/color.hxx> + +class SdDrawDocument; +class SdrObject; + +class SdAnimationPrmsUndoAction final : public SdUndoAction +{ + SdrObject* pObject; + bool bOldActive; + bool bNewActive; + bool bOldDimPrevious; + bool bNewDimPrevious; + bool bOldDimHide; + bool bNewDimHide; + bool bOldSoundOn; + bool bNewSoundOn; + bool bOldSecondSoundOn; + bool bNewSecondSoundOn; + bool bOldPlayFull; + bool bNewPlayFull; + bool bOldSecondPlayFull; + bool bNewSecondPlayFull; + css::presentation::AnimationEffect eOldEffect; + css::presentation::AnimationEffect eNewEffect; + css::presentation::AnimationEffect eOldTextEffect; + css::presentation::AnimationEffect eNewTextEffect; + css::presentation::AnimationSpeed eOldSpeed; + css::presentation::AnimationSpeed eNewSpeed; + css::presentation::AnimationEffect eOldSecondEffect; + css::presentation::AnimationEffect eNewSecondEffect; + css::presentation::AnimationSpeed eOldSecondSpeed; + css::presentation::AnimationSpeed eNewSecondSpeed; + Color aOldDimColor; + Color aNewDimColor; + OUString aOldSoundFile; + OUString aNewSoundFile; + css::presentation::ClickAction eOldClickAction; + css::presentation::ClickAction eNewClickAction; + OUString aOldBookmark; + OUString aNewBookmark; + sal_uInt16 nOldVerb; + sal_uInt16 nNewVerb; + + bool bInfoCreated; + +public: + SdAnimationPrmsUndoAction(SdDrawDocument* pTheDoc, SdrObject* pObj, + bool bCreated) + : SdUndoAction(pTheDoc) + , pObject(pObj) + , bOldActive(false) + , bNewActive(false) + , bOldDimPrevious(false) + , bNewDimPrevious(false) + , bOldDimHide(false) + , bNewDimHide(false) + , bOldSoundOn(false) + , bNewSoundOn(false) + , bOldSecondSoundOn(false) + , bNewSecondSoundOn(false) + , bOldPlayFull(false) + , bNewPlayFull(false) + , bOldSecondPlayFull(false) + , bNewSecondPlayFull(false) + , eOldEffect(css::presentation::AnimationEffect_NONE) + , eNewEffect(css::presentation::AnimationEffect_NONE) + , eOldTextEffect(css::presentation::AnimationEffect_NONE) + , eNewTextEffect(css::presentation::AnimationEffect_NONE) + , eOldSpeed(css::presentation::AnimationSpeed_SLOW) + , eNewSpeed(css::presentation::AnimationSpeed_SLOW) + , eOldSecondEffect(css::presentation::AnimationEffect_NONE) + , eNewSecondEffect(css::presentation::AnimationEffect_NONE) + , eOldSecondSpeed(css::presentation::AnimationSpeed_SLOW) + , eNewSecondSpeed(css::presentation::AnimationSpeed_SLOW) + , eOldClickAction(css::presentation::ClickAction_NONE) + , eNewClickAction(css::presentation::ClickAction_NONE) + , nOldVerb(0) + , nNewVerb(0) + , bInfoCreated(bCreated) + { + } + + void SetActive(bool bTheOldActive, bool bTheNewActive) + { bOldActive = bTheOldActive; bNewActive = bTheNewActive; } + void SetEffect(css::presentation::AnimationEffect eTheOldEffect, css::presentation::AnimationEffect eTheNewEffect) + { eOldEffect = eTheOldEffect; eNewEffect = eTheNewEffect; } + void SetTextEffect(css::presentation::AnimationEffect eTheOldEffect, css::presentation::AnimationEffect eTheNewEffect) + { eOldTextEffect = eTheOldEffect; eNewTextEffect = eTheNewEffect; } + void SetSpeed(css::presentation::AnimationSpeed eTheOldSpeed, css::presentation::AnimationSpeed eTheNewSpeed) + { eOldSpeed = eTheOldSpeed; eNewSpeed = eTheNewSpeed; } + void SetDim(bool bTheOldDim, bool bTheNewDim) + { bOldDimPrevious = bTheOldDim; bNewDimPrevious = bTheNewDim; } + void SetDimColor(Color aTheOldDimColor, Color aTheNewDimColor) + { aOldDimColor = aTheOldDimColor; aNewDimColor = aTheNewDimColor; } + void SetDimHide(bool bTheOldDimHide, bool bTheNewDimHide) + { bOldDimHide = bTheOldDimHide; bNewDimHide = bTheNewDimHide; } + void SetSoundOn(bool bTheOldSoundOn, bool bTheNewSoundOn) + { bOldSoundOn = bTheOldSoundOn; bNewSoundOn = bTheNewSoundOn; } + void SetSound(const OUString& aTheOldSound, const OUString& aTheNewSound) + { aOldSoundFile = aTheOldSound; aNewSoundFile = aTheNewSound; } + void SetPlayFull(bool bTheOldPlayFull, bool bTheNewPlayFull) + { bOldPlayFull = bTheOldPlayFull; bNewPlayFull = bTheNewPlayFull; } + void SetClickAction(css::presentation::ClickAction eTheOldAction, css::presentation::ClickAction eTheNewAction) + { eOldClickAction = eTheOldAction; eNewClickAction = eTheNewAction; } + void SetBookmark(const OUString& aTheOldBookmark, const OUString& aTheNewBookmark) + { aOldBookmark = aTheOldBookmark; aNewBookmark = aTheNewBookmark; } + void SetVerb(sal_uInt16 nTheOldVerb, sal_uInt16 nTheNewVerb) + { nOldVerb = nTheOldVerb; nNewVerb = nTheNewVerb; } + void SetSecondEffect(css::presentation::AnimationEffect eTheOldEffect, css::presentation::AnimationEffect eTheNewEffect) + { eOldSecondEffect = eTheOldEffect; eNewSecondEffect = eTheNewEffect; } + void SetSecondSpeed(css::presentation::AnimationSpeed eTheOldSpeed, css::presentation::AnimationSpeed eTheNewSpeed) + { eOldSecondSpeed = eTheOldSpeed; eNewSecondSpeed = eTheNewSpeed; } + void SetSecondSoundOn(bool bTheOldSoundOn, bool bTheNewSoundOn) + { bOldSecondSoundOn = bTheOldSoundOn; bNewSecondSoundOn = bTheNewSoundOn; } + void SetSecondPlayFull(bool bTheOldPlayFull, bool bTheNewPlayFull) + { bOldSecondPlayFull = bTheOldPlayFull; bNewSecondPlayFull = bTheNewPlayFull; } + + virtual ~SdAnimationPrmsUndoAction() override; + virtual void Undo() override; + virtual void Redo() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/unokywds.hxx b/sd/source/ui/inc/unokywds.hxx new file mode 100644 index 000000000..37a03d3d6 --- /dev/null +++ b/sd/source/ui/inc/unokywds.hxx @@ -0,0 +1,119 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> +#include <rtl/ustring.hxx> + +// SdUnoPseudoStyleFamily +inline constexpr OUStringLiteral sUNO_PseudoSheet_Background = u"background"; + +// SdLayer +inline constexpr OUStringLiteral sUNO_LayerName_background = u"background"; +inline constexpr OUStringLiteral sUNO_LayerName_background_objects = u"backgroundobjects"; +inline constexpr OUStringLiteral sUNO_LayerName_layout = u"layout"; +inline constexpr OUStringLiteral sUNO_LayerName_controls = u"controls"; +inline constexpr OUStringLiteral sUNO_LayerName_measurelines = u"measurelines"; + +// services +inline constexpr OUStringLiteral sUNO_Service_FillProperties + = u"com.sun.star.drawing.FillProperties"; +inline constexpr OUStringLiteral sUNO_Service_PageBackground + = u"com.sun.star.drawing.PageBackground"; +inline constexpr OUStringLiteral sUNO_Service_ImageMapRectangleObject + = u"com.sun.star.image.ImageMapRectangleObject"; +inline constexpr OUStringLiteral sUNO_Service_ImageMapCircleObject + = u"com.sun.star.image.ImageMapCircleObject"; +inline constexpr OUStringLiteral sUNO_Service_ImageMapPolygonObject + = u"com.sun.star.image.ImageMapPolygonObject"; + +// properties +inline constexpr OUStringLiteral sUNO_Prop_ForbiddenCharacters = u"ForbiddenCharacters"; +inline constexpr OUStringLiteral sUNO_Prop_MapUnit = u"MapUnit"; +inline constexpr OUStringLiteral sUNO_Prop_VisibleArea = u"VisibleArea"; +inline constexpr OUStringLiteral sUNO_Prop_TabStop = u"TabStop"; +inline constexpr OUStringLiteral sUNO_Prop_CharLocale = u"CharLocale"; +inline constexpr OUStringLiteral sUNO_Prop_AutomContFocus = u"AutomaticControlFocus"; +inline constexpr OUStringLiteral sUNO_Prop_ApplyFrmDsgnMode = u"ApplyFormDesignMode"; +inline constexpr OUStringLiteral sUNO_Prop_IsBackgroundVisible = u"IsBackgroundVisible"; +inline constexpr OUStringLiteral sUNO_Prop_IsBackgroundObjectsVisible + = u"IsBackgroundObjectsVisible"; +inline constexpr OUStringLiteral sUNO_Prop_UserDefinedAttributes = u"UserDefinedAttributes"; +inline constexpr OUStringLiteral sUNO_Prop_BookmarkURL = u"BookmarkURL"; +inline constexpr OUStringLiteral sUNO_Prop_RuntimeUID = u"RuntimeUID"; +inline constexpr OUStringLiteral sUNO_Prop_HasValidSignatures = u"HasValidSignatures"; +inline constexpr OUStringLiteral sUNO_Prop_InteropGrabBag = u"InteropGrabBag"; +inline constexpr OUStringLiteral sUNO_Prop_Theme = u"Theme"; + +// view settings +inline constexpr OUStringLiteral sUNO_View_ViewId = u"ViewId"; +inline constexpr OUStringLiteral sUNO_View_SnapLinesDrawing = u"SnapLinesDrawing"; +inline constexpr OUStringLiteral sUNO_View_SnapLinesNotes = u"SnapLinesNotes"; +inline constexpr OUStringLiteral sUNO_View_SnapLinesHandout = u"SnapLinesHandout"; +inline constexpr OUStringLiteral sUNO_View_RulerIsVisible = u"RulerIsVisible"; +inline constexpr OUStringLiteral sUNO_View_PageKind = u"PageKind"; +inline constexpr OUStringLiteral sUNO_View_SelectedPage = u"SelectedPage"; +inline constexpr OUStringLiteral sUNO_View_IsLayerMode = u"IsLayerMode"; +inline constexpr OUStringLiteral sUNO_View_IsDoubleClickTextEdit = u"IsDoubleClickTextEdit"; +inline constexpr OUStringLiteral sUNO_View_IsClickChangeRotation = u"IsClickChangeRotation"; +inline constexpr OUStringLiteral sUNO_View_SlidesPerRow = u"SlidesPerRow"; +inline constexpr OUStringLiteral sUNO_View_EditMode = u"EditMode"; +inline const char sUNO_View_EditModeStandard[] = "EditModeStandard"; // To be deprecated +// inline const char sUNO_View_EditModeNotes[] = "EditModeNotes"; +// inline const char sUNO_View_EditModeHandout[] = "EditModeHandout"; + +inline constexpr OUStringLiteral sUNO_View_GridIsVisible = u"GridIsVisible"; +inline constexpr OUStringLiteral sUNO_View_GridIsFront = u"GridIsFront"; +inline constexpr OUStringLiteral sUNO_View_IsSnapToGrid = u"IsSnapToGrid"; +inline constexpr OUStringLiteral sUNO_View_IsSnapToPageMargins = u"IsSnapToPageMargins"; +inline constexpr OUStringLiteral sUNO_View_IsSnapToSnapLines = u"IsSnapToSnapLines"; +inline constexpr OUStringLiteral sUNO_View_IsSnapToObjectFrame = u"IsSnapToObjectFrame"; +inline constexpr OUStringLiteral sUNO_View_IsSnapToObjectPoints = u"IsSnapToObjectPoints"; +inline constexpr OUStringLiteral sUNO_View_IsPlusHandlesAlwaysVisible + = u"IsPlusHandlesAlwaysVisible"; +inline constexpr OUStringLiteral sUNO_View_IsFrameDragSingles = u"IsFrameDragSingles"; +inline constexpr OUStringLiteral sUNO_View_EliminatePolyPointLimitAngle + = u"EliminatePolyPointLimitAngle"; +inline constexpr OUStringLiteral sUNO_View_IsEliminatePolyPoints = u"IsEliminatePolyPoints"; +inline const char sUNO_View_ActiveLayer[] = "ActiveLayer"; +inline constexpr OUStringLiteral sUNO_View_NoAttribs = u"NoAttribs"; +inline constexpr OUStringLiteral sUNO_View_NoColors = u"NoColors"; +inline constexpr OUStringLiteral sUNO_View_GridCoarseWidth = u"GridCoarseWidth"; +inline constexpr OUStringLiteral sUNO_View_GridCoarseHeight = u"GridCoarseHeight"; +inline constexpr OUStringLiteral sUNO_View_GridFineWidth = u"GridFineWidth"; +inline constexpr OUStringLiteral sUNO_View_GridFineHeight = u"GridFineHeight"; +inline constexpr OUStringLiteral sUNO_View_IsAngleSnapEnabled = u"IsAngleSnapEnabled"; +inline constexpr OUStringLiteral sUNO_View_SnapAngle = u"SnapAngle"; +inline constexpr OUStringLiteral sUNO_View_GridSnapWidthXNumerator = u"GridSnapWidthXNumerator"; +inline constexpr OUStringLiteral sUNO_View_GridSnapWidthXDenominator = u"GridSnapWidthXDenominator"; +inline constexpr OUStringLiteral sUNO_View_GridSnapWidthYNumerator = u"GridSnapWidthYNumerator"; +inline constexpr OUStringLiteral sUNO_View_GridSnapWidthYDenominator = u"GridSnapWidthYDenominator"; +inline constexpr OUStringLiteral sUNO_View_VisibleLayers = u"VisibleLayers"; +inline constexpr OUStringLiteral sUNO_View_PrintableLayers = u"PrintableLayers"; +inline constexpr OUStringLiteral sUNO_View_LockedLayers = u"LockedLayers"; + +inline constexpr OUStringLiteral sUNO_View_VisibleAreaTop = u"VisibleAreaTop"; +inline constexpr OUStringLiteral sUNO_View_VisibleAreaLeft = u"VisibleAreaLeft"; +inline constexpr OUStringLiteral sUNO_View_VisibleAreaWidth = u"VisibleAreaWidth"; +inline constexpr OUStringLiteral sUNO_View_VisibleAreaHeight = u"VisibleAreaHeight"; + +inline constexpr OUStringLiteral sUNO_View_ZoomOnPage = u"ZoomOnPage"; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/unomodel.hxx b/sd/source/ui/inc/unomodel.hxx new file mode 100644 index 000000000..cf88666f6 --- /dev/null +++ b/sd/source/ui/inc/unomodel.hxx @@ -0,0 +1,406 @@ +/* -*- 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 . + */ +#pragma once + +#include <com/sun/star/style/XStyleFamiliesSupplier.hpp> +#include <com/sun/star/document/XLinkTargetSupplier.hpp> +#include <com/sun/star/drawing/XDrawPagesSupplier.hpp> +#include <com/sun/star/drawing/XDrawPageDuplicator.hpp> +#include <com/sun/star/drawing/XLayerSupplier.hpp> +#include <com/sun/star/drawing/XMasterPagesSupplier.hpp> +#include <com/sun/star/presentation/XPresentationSupplier.hpp> +#include <com/sun/star/presentation/XCustomPresentationSupplier.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/drawing/XDrawPages.hpp> +#include <com/sun/star/ucb/XAnyCompareFactory.hpp> +#include <com/sun/star/presentation/XHandoutMasterSupplier.hpp> +#include <com/sun/star/view/XRenderable.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> + +#include <rtl/ref.hxx> + +#include <sfx2/sfxbasemodel.hxx> +#include <svx/fmdmod.hxx> + +#include <vcl/ITiledRenderable.hxx> + +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/weakref.hxx> +#include <sddllapi.h> + +namespace com::sun::star::container { class XNameContainer; } +namespace com::sun::star::i18n { class XForbiddenCharacters; } +namespace com::sun::star::presentation { class XPresentation; } + +class SdDrawDocument; +class SdPage; +class SvxItemPropertySet; + +namespace sd { +class DrawDocShell; +class DrawViewShell; +} + +extern OUString getPageApiName( SdPage const * pPage ); +extern OUString getPageApiNameFromUiName( const OUString& rUIName ); + +class SD_DLLPUBLIC SdXImpressDocument final : public SfxBaseModel, // implements SfxListener, OWEAKOBJECT & other + public SvxFmMSFactory, + public css::drawing::XDrawPageDuplicator, + public css::drawing::XLayerSupplier, + public css::drawing::XMasterPagesSupplier, + public css::drawing::XDrawPagesSupplier, + public css::presentation::XPresentationSupplier, + public css::presentation::XCustomPresentationSupplier, + public css::document::XLinkTargetSupplier, + public css::beans::XPropertySet, + public css::style::XStyleFamiliesSupplier, + public css::lang::XServiceInfo, + public css::ucb::XAnyCompareFactory, + public css::presentation::XHandoutMasterSupplier, + public css::view::XRenderable, + public vcl::ITiledRenderable +{ + friend class SdDrawPagesAccess; + friend class SdMasterPagesAccess; + friend class SdLayerManager; + +private: + ::sd::DrawDocShell* mpDocShell; + SdDrawDocument* mpDoc; + bool mbDisposed; + + css::uno::Reference<css::uno::XInterface> create( + OUString const & aServiceSpecifier, OUString const & referer); + + /// @throws css::uno::RuntimeException + SdPage* InsertSdPage( sal_uInt16 nPage, bool bDuplicate ); + + const bool mbImpressDoc; + bool mbClipBoard; + + css::uno::WeakReference< css::drawing::XDrawPages > mxDrawPagesAccess; + css::uno::WeakReference< css::drawing::XDrawPages > mxMasterPagesAccess; + css::uno::WeakReference< css::container::XNameAccess > mxLayerManager; + css::uno::WeakReference< css::container::XNameContainer > mxCustomPresentationAccess; + css::uno::WeakReference< css::i18n::XForbiddenCharacters > mxForbiddenCharacters; + css::uno::Reference< css::container::XNameAccess > mxLinks; + + css::uno::Reference< css::uno::XInterface > mxDashTable; + css::uno::Reference< css::uno::XInterface > mxGradientTable; + css::uno::Reference< css::uno::XInterface > mxHatchTable; + css::uno::Reference< css::uno::XInterface > mxBitmapTable; + css::uno::Reference< css::uno::XInterface > mxTransGradientTable; + css::uno::Reference< css::uno::XInterface > mxMarkerTable; + css::uno::Reference< css::uno::XInterface > mxDrawingPool; + + const SvxItemPropertySet* mpPropSet; + + css::uno::Sequence< css::uno::Type > maTypeSequence; + + OUString maBuildId; + + void initializeDocument(); + + sd::DrawViewShell* GetViewShell(); + + /** abstract SdrModel provider */ + virtual SdrModel& getSdrModelFromUnoModel() const override; + +public: + SdXImpressDocument(::sd::DrawDocShell* pShell, bool bClipBoard); + SdXImpressDocument(SdDrawDocument* pDoc, bool bClipBoard); + virtual ~SdXImpressDocument() noexcept override; + + static rtl::Reference< SdXImpressDocument > GetModel( SdDrawDocument const & rDoc ); + + // intern + bool operator==( const SdXImpressDocument& rModel ) const { return mpDoc == rModel.mpDoc; } + bool operator!=( const SdXImpressDocument& rModel ) const { return mpDoc != rModel.mpDoc; } + + ::sd::DrawDocShell* GetDocShell() const { return mpDocShell; } + SdDrawDocument* GetDoc() const { return mpDoc; } + bool IsImpressDocument() const { return mbImpressDoc; } + + void SetModified() noexcept; + + css::uno::Reference< css::i18n::XForbiddenCharacters > getForbiddenCharsTable(); + + // SfxListener + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override; + + UNO3_GETIMPLEMENTATION_DECL(SdXImpressDocument) + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + // XModel + virtual void SAL_CALL lockControllers( ) override; + virtual void SAL_CALL unlockControllers( ) override; + virtual sal_Bool SAL_CALL hasControllersLocked( ) override; + virtual css::uno::Reference < css::container::XIndexAccess > SAL_CALL getViewData() override; + virtual void SAL_CALL setViewData( const css::uno::Reference < css::container::XIndexAccess >& aData ) override; + + // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override; + + // XDrawPageDuplicator + virtual css::uno::Reference< css::drawing::XDrawPage > SAL_CALL duplicate( const css::uno::Reference< css::drawing::XDrawPage >& xPage ) override; + + // XDrawPagesSupplier + virtual css::uno::Reference< css::drawing::XDrawPages > SAL_CALL getDrawPages( ) override; + + // XMasterPagesSupplier + virtual css::uno::Reference< css::drawing::XDrawPages > SAL_CALL getMasterPages( ) override; + + // XLayerManagerSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getLayerManager( ) override; + + // XCustomPresentationSupplier + virtual css::uno::Reference< css::container::XNameContainer > SAL_CALL getCustomPresentations( ) override; + + // XHandoutMasterSupplier + virtual css::uno::Reference< css::drawing::XDrawPage > SAL_CALL getHandoutMasterPage( ) override; + + // XPresentationSupplier + virtual css::uno::Reference< css::presentation::XPresentation > SAL_CALL getPresentation( ) override; + + // XMultiServiceFactory ( SvxFmMSFactory ) + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstance( const OUString& aServiceSpecifier ) override; + virtual css::uno::Reference<css::uno::XInterface> SAL_CALL + createInstanceWithArguments( + OUString const & ServiceSpecifier, + css::uno::Sequence<css::uno::Any> const & Arguments) override; + virtual css::uno::Sequence< OUString > SAL_CALL getAvailableServiceNames( ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override; + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override; + virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override; + virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override; + virtual void SAL_CALL addVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + + // XLinkTargetSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getLinks( ) override; + + // XStyleFamiliesSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getStyleFamilies( ) override; + + // XAnyCompareFactory + virtual css::uno::Reference< css::ucb::XAnyCompare > SAL_CALL createAnyCompareByName( const OUString& PropertyName ) override; + + // XRenderable + virtual sal_Int32 SAL_CALL getRendererCount( const css::uno::Any& aSelection, const css::uno::Sequence< css::beans::PropertyValue >& xOptions ) override; + virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getRenderer( sal_Int32 nRenderer, const css::uno::Any& aSelection, const css::uno::Sequence< css::beans::PropertyValue >& xOptions ) override; + virtual void SAL_CALL render( sal_Int32 nRenderer, const css::uno::Any& aSelection, const css::uno::Sequence< css::beans::PropertyValue >& xOptions ) override; + + // ITiledRenderable + virtual void paintTile( VirtualDevice& rDevice, + int nOutputWidth, + int nOutputHeight, + int nTilePosX, + int nTilePosY, + tools::Long nTileWidth, + tools::Long nTileHeight ) override; + virtual Size getDocumentSize() override; + virtual void setPart( int nPart, bool bAllowChangeFocus = true ) override; + virtual int getPart() override; + virtual int getParts() override; + virtual OUString getPartName( int nPart ) override; + virtual OUString getPartHash( int nPart ) override; + virtual VclPtr<vcl::Window> getDocWindow() override; + bool isMasterViewMode(); + + virtual void setPartMode( int nPartMode ) override; + + /// @see vcl::ITiledRenderable::initializeForTiledRendering(). + virtual void initializeForTiledRendering(const css::uno::Sequence<css::beans::PropertyValue>& rArguments) override; + /// @see vcl::ITiledRenderable::postKeyEvent(). + virtual void postKeyEvent(int nType, int nCharCode, int nKeyCode) override; + /// @see vcl::ITiledRenderable::postMouseEvent(). + virtual void postMouseEvent(int nType, int nX, int nY, int nCount, int nButtons, int nModifier) override; + /// @see vcl::ITiledRenderable::setTextSelection(). + virtual void setTextSelection(int nType, int nX, int nY) override; + /// @see vcl::ITiledRenderable::getSelection(). + virtual css::uno::Reference<css::datatransfer::XTransferable> getSelection() override; + /// @see vcl::ITiledRenderable::setGraphicSelection(). + virtual void setGraphicSelection(int nType, int nX, int nY) override; + /// @see lok::Document::resetSelection(). + virtual void resetSelection() override; + /// @see vcl::ITiledRenderable::setClientVisibleArea(). + virtual void setClientVisibleArea(const tools::Rectangle& rRectangle) override; + /// @see vcl::ITiledRenderable::setClipboard(). + virtual void setClipboard(const css::uno::Reference<css::datatransfer::clipboard::XClipboard>& xClipboard) override; + /// @see vcl::ITiledRenderable::isMimeTypeSupported(). + virtual bool isMimeTypeSupported() override; + /// @see vcl::ITiledRenderable::getPointer(). + virtual PointerStyle getPointer() override; + /// @see vcl::ITiledRenderable::getPostIts(). + virtual void getPostIts(tools::JsonWriter& /*rJsonWriter*/) override; + /// @see vcl::ITiledRenderable::selectPart(). + virtual void selectPart(int nPart, int nSelect) override; + /// @see vcl::ITiledRenderable::moveSelectedParts(). + virtual void moveSelectedParts(int nPosition, bool bDuplicate) override; + /// @see vcl::ITiledRenderable::getPartInfo(). + virtual OUString getPartInfo(int nPart) override; + /// @see vcl::ITiledRenderable::isDisposed(). + virtual bool isDisposed() const override + { + return mbDisposed; + } + + // XComponent + + /** This dispose implementation releases the resources held by the + called object and forwards the call to its base class. + When close() has not yet been called then this is done first. As a + consequence the implementation has to cope with being called twice + and still has to forward the second call to the base class. + See also comments of issue 27847. + */ + virtual void SAL_CALL dispose() override; +}; + +/*********************************************************************** +* * +***********************************************************************/ + +class SdDrawPagesAccess final : public ::cppu::WeakImplHelper< css::drawing::XDrawPages, css::container::XNameAccess, css::lang::XServiceInfo, css::lang::XComponent > +{ +private: + SdXImpressDocument* mpModel; + +public: + SdDrawPagesAccess( SdXImpressDocument& rMyModel ) noexcept; + virtual ~SdDrawPagesAccess() noexcept override; + + // XDrawPages + virtual css::uno::Reference< css::drawing::XDrawPage > SAL_CALL insertNewByIndex( sal_Int32 nIndex ) override; + virtual void SAL_CALL remove( const css::uno::Reference< css::drawing::XDrawPage >& xPage ) override; + + // XNameAccess + virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override; + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + // XIndexAccess + virtual sal_Int32 SAL_CALL getCount() override ; + virtual css::uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XComponent + virtual void SAL_CALL dispose( ) override; + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override; +}; + +/*********************************************************************** +* * +***********************************************************************/ + +class SdMasterPagesAccess final : public ::cppu::WeakImplHelper< css::drawing::XDrawPages, css::lang::XServiceInfo, css::lang::XComponent > +{ +private: + SdXImpressDocument* mpModel; + +public: + SdMasterPagesAccess( SdXImpressDocument& rMyModel ) noexcept; + virtual ~SdMasterPagesAccess() noexcept override; + + // XDrawPages + virtual css::uno::Reference< css::drawing::XDrawPage > SAL_CALL insertNewByIndex( sal_Int32 nIndex ) override; + virtual void SAL_CALL remove( const css::uno::Reference< css::drawing::XDrawPage >& xPage ) override; + + // XIndexAccess + virtual sal_Int32 SAL_CALL getCount() override ; + virtual css::uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XComponent + virtual void SAL_CALL dispose( ) override; + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override; +}; + +/*********************************************************************** +* * +***********************************************************************/ + +class SdDocLinkTargets final : public ::cppu::WeakImplHelper< css::container::XNameAccess, + css::lang::XServiceInfo , css::lang::XComponent > +{ +private: + SdXImpressDocument* mpModel; + +public: + SdDocLinkTargets( SdXImpressDocument& rMyModel ) noexcept; + virtual ~SdDocLinkTargets() noexcept override; + + // XNameAccess + virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override; + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XComponent + virtual void SAL_CALL dispose( ) override; + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override; + + // intern + /// @throws std::exception + SdPage* FindPage( std::u16string_view rName ) const; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/unopage.hxx b/sd/source/ui/inc/unopage.hxx new file mode 100644 index 000000000..af09e5982 --- /dev/null +++ b/sd/source/ui/inc/unopage.hxx @@ -0,0 +1,304 @@ +/* -*- 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 . + */ +#pragma once + +#include <com/sun/star/document/XLinkTargetSupplier.hpp> +#include <com/sun/star/container/XNamed.hpp> +#include <com/sun/star/drawing/XMasterPageTarget.hpp> +#include <com/sun/star/drawing/XShapeCombiner.hpp> +#include <com/sun/star/drawing/XShapeBinder.hpp> +#include <com/sun/star/presentation/XPresentationPage.hpp> +#include <com/sun/star/animations/XAnimationNodeSupplier.hpp> +#include <com/sun/star/beans/XMultiPropertySet.hpp> +#include <com/sun/star/office/XAnnotationAccess.hpp> + +#include <svx/unopage.hxx> +#include <svx/fmdpage.hxx> + +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/implbase.hxx> + +#include "unosrch.hxx" +#include <sdpage.hxx> + +class SdrObject; +class SdXImpressDocument; + +class SdGenericDrawPage : public SvxFmDrawPage, + public SdUnoSearchReplaceShape, + public css::drawing::XShapeCombiner, + public css::drawing::XShapeBinder, + public css::container::XNamed, + public css::beans::XPropertySet, + public css::beans::XMultiPropertySet, + public css::animations::XAnimationNodeSupplier, + public css::office::XAnnotationAccess, + public css::document::XLinkTargetSupplier +{ +private: + SdXImpressDocument* mpDocModel; + SdrModel* mpSdrModel; + bool mbIsImpressDocument; + sal_Int16 mnTempPageNumber; // for printing handouts + + void UpdateModel(); + +protected: + friend class SdXImpressDocument; + + const SvxItemPropertySet* mpPropSet; + + /// @throws css::lang::IllegalArgumentException + virtual void setBackground( const css::uno::Any& rValue ); + /// @throws std::exception + virtual void getBackground( css::uno::Any& rValue ); + + OUString getBookmarkURL() const; + void setBookmarkURL( std::u16string_view rURL ); + + void SetLeftBorder( sal_Int32 nValue ); + void SetRightBorder( sal_Int32 nValue ); + void SetUpperBorder( sal_Int32 nValue ); + void SetLowerBorder( sal_Int32 nValue ); + + void SetWidth( sal_Int32 nWidth ); + void SetHeight( sal_Int32 nHeight ); + + bool IsImpressDocument() const; + + virtual void disposing() noexcept override; + + css::uno::Any getNavigationOrder(); + void setNavigationOrder( const css::uno::Any& rValue ); + + /// @throws css::uno::RuntimeException + void throwIfDisposed() const; + +public: + SdGenericDrawPage(SdXImpressDocument* pModel, SdPage* pInPage, const SvxItemPropertySet* pSet); + virtual ~SdGenericDrawPage() noexcept override; + + // intern + bool isValid() const { return (SvxDrawPage::mpPage != nullptr) && (mpModel != nullptr); } + + SdPage* GetPage() const { return static_cast<SdPage*>(SvxDrawPage::mpPage); } + SdXImpressDocument* GetModel() const; + + static const css::uno::Sequence< sal_Int8 > & getUnoTunnelId() noexcept; + virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) override; + + // this is called whenever a SdrObject must be created for an empty api shape wrapper + virtual SdrObject *CreateSdrObject_( const css::uno::Reference< css::drawing::XShape >& xShape ) override; + + // SvxFmDrawPage + virtual css::uno::Reference<css::drawing::XShape> CreateShape(SdrObject *pObj) const override; + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL release() noexcept override; + + // XShapeCombiner + virtual css::uno::Reference< css::drawing::XShape > SAL_CALL combine( const css::uno::Reference< css::drawing::XShapes >& xShapes ) override; + virtual void SAL_CALL split( const css::uno::Reference< css::drawing::XShape >& xGroup ) override; + + // XShapeBinder + virtual css::uno::Reference< css::drawing::XShape > SAL_CALL bind( const css::uno::Reference< css::drawing::XShapes >& xShapes ) override; + virtual void SAL_CALL unbind( const css::uno::Reference< css::drawing::XShape >& xShape ) override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override; + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override; + virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override; + virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override; + virtual void SAL_CALL addVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + + // XMultiPropertySet + virtual void SAL_CALL setPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Sequence< css::uno::Any >& aValues ) override; + virtual css::uno::Sequence< css::uno::Any > SAL_CALL getPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames ) override; + virtual void SAL_CALL addPropertiesChangeListener( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Reference< css::beans::XPropertiesChangeListener >& xListener ) override; + virtual void SAL_CALL removePropertiesChangeListener( const css::uno::Reference< css::beans::XPropertiesChangeListener >& xListener ) override; + virtual void SAL_CALL firePropertiesChangeEvent( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Reference< css::beans::XPropertiesChangeListener >& xListener ) override; + + // XLinkTargetSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getLinks( ) override; + + // XServiceInfo + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XAnimationNodeSupplier + virtual css::uno::Reference< css::animations::XAnimationNode > SAL_CALL getAnimationNode( ) override; + + // XAnnotationAccess: + virtual css::uno::Reference< css::office::XAnnotation > SAL_CALL createAndInsertAnnotation() override; + virtual void SAL_CALL removeAnnotation(const css::uno::Reference< css::office::XAnnotation > & annotation) override; + virtual css::uno::Reference< css::office::XAnnotationEnumeration > SAL_CALL createAnnotationEnumeration() override; +}; + +/*********************************************************************** +* * +***********************************************************************/ + +class SdDrawPage final : public css::drawing::XMasterPageTarget, + public css::presentation::XPresentationPage, + public SdGenericDrawPage +{ +private: + css::uno::Sequence< css::uno::Type > maTypeSequence; + + virtual void setBackground( const css::uno::Any& rValue ) override; + virtual void getBackground( css::uno::Any& rValue ) override; +public: + SdDrawPage(SdXImpressDocument* pModel, SdPage* pInPage); + virtual ~SdDrawPage() noexcept override; + + UNO3_GETIMPLEMENTATION_DECL( SdDrawPage ) + + static OUString getPageApiName( SdPage const * pPage ); + static OUString getPageApiNameFromUiName( const OUString& rUIName ); + static OUString getUiNameFromPageApiName( const OUString& rApiName ); + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XMasterPageTarget + virtual css::uno::Reference< css::drawing::XDrawPage > SAL_CALL getMasterPage( ) override; + virtual void SAL_CALL setMasterPage( const css::uno::Reference< css::drawing::XDrawPage >& xMasterPage ) override; + + // XPresentationPage + virtual css::uno::Reference< css::drawing::XDrawPage > SAL_CALL getNotesPage( ) override; + + // XNamed + virtual OUString SAL_CALL getName( ) override; + virtual void SAL_CALL setName( const OUString& aName ) override; + + // XIndexAccess + virtual sal_Int32 SAL_CALL getCount() override ; + virtual css::uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + // XShapes + virtual void SAL_CALL add( const css::uno::Reference< css::drawing::XShape >& xShape ) override; + virtual void SAL_CALL remove( const css::uno::Reference< css::drawing::XShape >& xShape ) override; +}; + +/*********************************************************************** +* * +***********************************************************************/ + +class SdMasterPage final : public css::presentation::XPresentationPage, + public SdGenericDrawPage +{ +private: + css::uno::Sequence< css::uno::Type > maTypeSequence; + + virtual void setBackground( const css::uno::Any& rValue ) override; + virtual void getBackground( css::uno::Any& rValue ) override; + +public: + SdMasterPage(SdXImpressDocument* pModel, SdPage* pInPage); + virtual ~SdMasterPage() noexcept override; + + UNO3_GETIMPLEMENTATION_DECL(SdMasterPage) + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XIndexAccess + virtual sal_Int32 SAL_CALL getCount() override ; + virtual css::uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + // XPresentationPage + virtual css::uno::Reference< css::drawing::XDrawPage > SAL_CALL getNotesPage( ) override; + + // XNamed + virtual OUString SAL_CALL getName( ) override; + virtual void SAL_CALL setName( const OUString& aName ) override; + + // XShapes + virtual void SAL_CALL add( const css::uno::Reference< css::drawing::XShape >& xShape ) override; + virtual void SAL_CALL remove( const css::uno::Reference< css::drawing::XShape >& xShape ) override; +}; + +/*********************************************************************** +* * +***********************************************************************/ + +class SdPageLinkTargets final : public ::cppu::WeakImplHelper< css::container::XNameAccess, + css::lang::XServiceInfo > +{ +private: + css::uno::Reference< css::drawing::XDrawPage > mxPage; + SdGenericDrawPage* mpUnoPage; + +public: + SdPageLinkTargets( SdGenericDrawPage* pUnoPage ) noexcept; + virtual ~SdPageLinkTargets() noexcept override; + + // intern + SdrObject* FindObject( std::u16string_view rName ) const noexcept; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XNameAccess + virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override; + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; +}; + +OUString getUiNameFromPageApiNameImpl( const OUString& rApiName ); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/unoprnms.hxx b/sd/source/ui/inc/unoprnms.hxx new file mode 100644 index 000000000..1d3a90552 --- /dev/null +++ b/sd/source/ui/inc/unoprnms.hxx @@ -0,0 +1,73 @@ +/* -*- 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 . + */ +#pragma once + + +#define UNO_NAME_PAGE_BACKGROUND "Background" +#define UNO_NAME_PAGE_LEFT "BorderLeft" +#define UNO_NAME_PAGE_RIGHT "BorderRight" +#define UNO_NAME_PAGE_TOP "BorderTop" +#define UNO_NAME_PAGE_BOTTOM "BorderBottom" +#define UNO_NAME_PAGE_CHANGE "Change" +#define UNO_NAME_PAGE_DURATION "Duration" +#define UNO_NAME_PAGE_EFFECT "Effect" +#define UNO_NAME_PAGE_HEIGHT "Height" +#define UNO_NAME_PAGE_LAYOUT "Layout" +#define UNO_NAME_PAGE_NUMBER "Number" +#define UNO_NAME_PAGE_ORIENTATION "Orientation" +#define UNO_NAME_PAGE_SPEED "Speed" +#define UNO_NAME_PAGE_TRANSITION_DURATION "TransitionDuration" +#define UNO_NAME_PAGE_WIDTH "Width" +#define UNO_NAME_PAGE_PREVIEW "Preview" +#define UNO_NAME_PAGE_PREVIEWBITMAP "PreviewBitmap" +#define UNO_NAME_PAGE_PREVIEWMETAFILE "PreviewMetafile" +#define UNO_NAME_PAGE_VISIBLE "Visible" + +#define UNO_NAME_OBJ_BOOKMARK "Bookmark" +#define UNO_NAME_OBJ_DIMCOLOR "DimColor" +#define UNO_NAME_OBJ_DIMHIDE "DimHide" +#define UNO_NAME_OBJ_DIMPREV "DimPrevious" +#define UNO_NAME_OBJ_EFFECT "Effect" +#define UNO_NAME_OBJ_ISEMPTYPRESOBJ "IsEmptyPresentationObject" +#define UNO_NAME_OBJ_ISPRESOBJ "IsPresentationObject" +#define UNO_NAME_OBJ_CLICKACTION "OnClick" +#define UNO_NAME_OBJ_PLAYFULL "PlayFull" +#define UNO_NAME_OBJ_PRESORDER "PresentationOrder" +#define UNO_NAME_OBJ_SOUNDFILE "Sound" +#define UNO_NAME_OBJ_SOUNDON "SoundOn" +#define UNO_NAME_OBJ_SPEED "Speed" +#define UNO_NAME_OBJ_TEXTEFFECT "TextEffect" +#define UNO_NAME_OBJ_BLUESCREEN "TransparentColor" +#define UNO_NAME_OBJ_VERB "Verb" +#define UNO_NAME_OBJ_STYLE "Style" +#define UNO_NAME_OBJ_MASTERDEPENDENT "IsPlaceholderDependent" +#define UNO_NAME_OBJ_ANIMATIONPATH "AnimationPath" +#define UNO_NAME_OBJ_LEGACYFRAGMENT "LegacyFragment" + +#define UNO_NAME_LAYER_LOCKED "IsLocked" +#define UNO_NAME_LAYER_PRINTABLE "IsPrintable" +#define UNO_NAME_LAYER_VISIBLE "IsVisible" +#define UNO_NAME_LAYER_NAME "Name" + + +#define UNO_NAME_SEARCH_BACKWARDS "SearchBackwards" +#define UNO_NAME_SEARCH_CASE "SearchCaseSensitive" +#define UNO_NAME_SEARCH_WORDS "SearchWords" + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/unosrch.hxx b/sd/source/ui/inc/unosrch.hxx new file mode 100644 index 000000000..6dcf681cb --- /dev/null +++ b/sd/source/ui/inc/unosrch.hxx @@ -0,0 +1,134 @@ +/* -*- 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 . + */ + +#pragma once + +#include <memory> +#include <com/sun/star/util/XReplaceable.hpp> +#include <com/sun/star/util/XReplaceDescriptor.hpp> +#include <com/sun/star/lang/XUnoTunnel.hpp> + +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/implbase.hxx> +#include <editeng/editdata.hxx> + +namespace com::sun::star::drawing { class XDrawPage; } +namespace com::sun::star::drawing { class XShape; } +namespace com::sun::star::text { class XTextRange; } +namespace com::sun::star::util { class XSearchDescriptor; } + +class SvxItemPropertySet; +class SdUnoSearchReplaceDescriptor; + +/** this class implements a search or replace operation on a given page or a given sdrobj */ +class SdUnoSearchReplaceShape : public css::util::XReplaceable +{ +protected: + css::drawing::XDrawPage* mpPage; + + css::uno::Reference< css::text::XTextRange > Search( const css::uno::Reference< css::text::XTextRange >& xText, SdUnoSearchReplaceDescriptor* pDescr ); + bool Search( const OUString& rText, sal_Int32& nStartPos, sal_Int32& nEndPos, SdUnoSearchReplaceDescriptor* pDescr ) noexcept; + static ESelection GetSelection( const css::uno::Reference< css::text::XTextRange >& xTextRange ) noexcept; + static css::uno::Reference< css::drawing::XShape > GetShape( const css::uno::Reference< css::text::XTextRange >& xTextRange ) noexcept; + css::uno::Reference< css::drawing::XShape > GetNextShape( const css::uno::Reference< css::container::XIndexAccess >& xShapes, const css::uno::Reference< css::drawing::XShape >& xCurrentShape ) noexcept; + css::uno::Reference< css::drawing::XShape > GetCurrentShape() const noexcept; + +public: + // danger, this c'tor is only usable if the given shape or page is derived + // from this class!!! + SdUnoSearchReplaceShape( css::drawing::XDrawPage* xPage ) noexcept; + virtual ~SdUnoSearchReplaceShape() noexcept; + + // XReplaceable + virtual css::uno::Reference< css::util::XReplaceDescriptor > SAL_CALL createReplaceDescriptor( ) override; + virtual sal_Int32 SAL_CALL replaceAll( const css::uno::Reference< css::util::XSearchDescriptor >& xDesc ) override; + + // XSearchable + virtual css::uno::Reference< css::util::XSearchDescriptor > SAL_CALL createSearchDescriptor( ) override; + virtual css::uno::Reference< css::container::XIndexAccess > SAL_CALL findAll( const css::uno::Reference< css::util::XSearchDescriptor >& xDesc ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL findFirst( const css::uno::Reference< css::util::XSearchDescriptor >& xDesc ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL findNext( const css::uno::Reference< css::uno::XInterface >& xStartAt, const css::uno::Reference< css::util::XSearchDescriptor >& xDesc ) override; +}; + +/* ================================================================= */ + +/** this class holds the parameters and status of a search or replace operation performed + by class SdUnoSearchReplaceShape */ + +class SdUnoSearchReplaceDescriptor final : public ::cppu::WeakImplHelper< css::lang::XUnoTunnel, css::util::XReplaceDescriptor > // public css::util::XSearchDescriptor, css::beans::XPropertySet +{ + std::unique_ptr<SvxItemPropertySet> mpPropSet; + + bool mbBackwards; + bool mbCaseSensitive; + bool mbWords; + + OUString maSearchStr; + OUString maReplaceStr; + +public: + /// @throws css::uno::RuntimeException + SdUnoSearchReplaceDescriptor(); + virtual ~SdUnoSearchReplaceDescriptor() noexcept override; + + bool IsCaseSensitive() const { return mbCaseSensitive; } + bool IsWords() const { return mbWords; } + + UNO3_GETIMPLEMENTATION_DECL( SdUnoSearchReplaceDescriptor ) + + // XSearchDescriptor + virtual OUString SAL_CALL getSearchString( ) override; + virtual void SAL_CALL setSearchString( const OUString& aString ) override; + + // XReplaceDescriptor + virtual OUString SAL_CALL getReplaceString( ) override; + virtual void SAL_CALL setReplaceString( const OUString& aReplaceString ) override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override; + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override; + virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override; + virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override; + virtual void SAL_CALL addVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; +}; + +/* ================================================================= */ + +/** this class holds a sequence that is a result from a find all and + lets people access it through the XIndexAccess Interface. */ +class SdUnoFindAllAccess final : public ::cppu::WeakImplHelper< css::container::XIndexAccess > // public css::container::XElementAccess +{ + css::uno::Sequence< css::uno::Reference< css::uno::XInterface > > maSequence; + +public: + SdUnoFindAllAccess( css::uno::Sequence< css::uno::Reference< css::uno::XInterface > > const & rSequence ) noexcept; + virtual ~SdUnoFindAllAccess() noexcept override; + + // XIndexAccess + virtual sal_Int32 SAL_CALL getCount() override ; + virtual css::uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/unprlout.hxx b/sd/source/ui/inc/unprlout.hxx new file mode 100644 index 000000000..8d75204f1 --- /dev/null +++ b/sd/source/ui/inc/unprlout.hxx @@ -0,0 +1,55 @@ +/* -*- 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 . + */ + +#pragma once + +#include <xmloff/autolayout.hxx> + +#include <sdundo.hxx> + +class SdDrawDocument; +class SdPage; + +class SdPresentationLayoutUndoAction final : public SdUndoAction +{ + OUString aOldLayoutName; + OUString aNewLayoutName; + AutoLayout eOldAutoLayout; + AutoLayout eNewAutoLayout; + bool bSetAutoLayout; // sal_True: change AutoLayout + SdPage* pPage; + OUString aComment; + +public: + SdPresentationLayoutUndoAction(SdDrawDocument* pTheDoc, + const OUString& aTheOldLayoutName, + const OUString& aTheNewLayoutName, + AutoLayout eTheOldAutoLayout, + AutoLayout eTheNewAutoLayout, + bool bSet, + SdPage* pThePage); + + virtual ~SdPresentationLayoutUndoAction() override; + virtual void Undo() override; + virtual void Redo() override; + + virtual OUString GetComment() const override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/vectdlg.hxx b/sd/source/ui/inc/vectdlg.hxx new file mode 100644 index 000000000..ac7a1bfd4 --- /dev/null +++ b/sd/source/ui/inc/vectdlg.hxx @@ -0,0 +1,81 @@ +/* -*- 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 . + */ + +#pragma once + +#include <vcl/weld.hxx> +#include <svx/graphctl.hxx> + +namespace sd { +class DrawDocShell; +} + +/****************************************************************************** +|* +|* SdVectorizeDlg +|* +\******************************************************************************/ + +class SdVectorizeDlg final : public weld::GenericDialogController +{ + ::sd::DrawDocShell* m_pDocSh; + Bitmap aBmp; + Bitmap aPreviewBmp; + GDIMetaFile aMtf; + + GraphCtrl m_aBmpWin; + GraphCtrl m_aMtfWin; + + std::unique_ptr<weld::SpinButton> m_xNmLayers; + std::unique_ptr<weld::MetricSpinButton> m_xMtReduce; + std::unique_ptr<weld::Label> m_xFtFillHoles; + std::unique_ptr<weld::MetricSpinButton> m_xMtFillHoles; + std::unique_ptr<weld::CheckButton> m_xCbFillHoles; + std::unique_ptr<weld::CustomWeld> m_xBmpWin; + std::unique_ptr<weld::CustomWeld> m_xMtfWin; + std::unique_ptr<weld::ProgressBar> m_xPrgs; + std::unique_ptr<weld::Button> m_xBtnOK; + std::unique_ptr<weld::Button> m_xBtnPreview; + + void LoadSettings(); + void SaveSettings() const; + void InitPreviewBmp(); + + static ::tools::Rectangle GetRect( const Size& rDispSize, const Size& rBmpSize ); + Bitmap GetPreparedBitmap( Bitmap const & rBmp, Fraction& rScale ); + void Calculate( Bitmap const & rBmp, GDIMetaFile& rMtf ); + static void AddTile( BitmapReadAccess const * pRAcc, GDIMetaFile& rMtf, + tools::Long nPosX, tools::Long nPosY, tools::Long nWidth, tools::Long nHeight ); + + DECL_LINK( ProgressHdl, tools::Long, void ); + DECL_LINK( ClickPreviewHdl, weld::Button&, void ); + DECL_LINK( ClickOKHdl, weld::Button&, void ); + DECL_LINK( ToggleHdl, weld::Toggleable&, void ); + DECL_LINK( ModifyHdl, weld::SpinButton&, void ); + DECL_LINK( MetricModifyHdl, weld::MetricSpinButton&, void ); + +public: + + SdVectorizeDlg(weld::Window* pParent, const Bitmap& rBmp, ::sd::DrawDocShell* pDocShell); + virtual ~SdVectorizeDlg() override; + + const GDIMetaFile& GetGDIMetaFile() const { return aMtf; } +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/view/viewoverlaymanager.hxx b/sd/source/ui/inc/view/viewoverlaymanager.hxx new file mode 100644 index 000000000..3a5c98deb --- /dev/null +++ b/sd/source/ui/inc/view/viewoverlaymanager.hxx @@ -0,0 +1,71 @@ +/* -*- 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 . + */ + +#pragma once + +#include <rtl/ref.hxx> +#include <tools/link.hxx> +#include <svl/lstner.hxx> + +#include <vector> + +namespace sd +{ +class SmartTag; +} +namespace sd::tools +{ +class EventMultiplexerEvent; +} +namespace sd +{ +class ViewShellBase; +} +struct ImplSVEvent; + +namespace sd +{ +typedef std::vector<rtl::Reference<SmartTag>> ViewTagVector; + +class ViewOverlayManager final : public SfxListener +{ +public: + ViewOverlayManager(ViewShellBase& rViewShellBase); + virtual ~ViewOverlayManager() override; + + void onZoomChanged(); + void UpdateTags(); + + DECL_LINK(EventMultiplexerListener, tools::EventMultiplexerEvent&, void); + DECL_LINK(UpdateTagsHdl, void*, void); + + bool CreateTags(); + bool DisposeTags(); + + virtual void Notify(SfxBroadcaster& rBC, const SfxHint& rHint) override; + +private: + ViewShellBase& mrBase; + ImplSVEvent* mnUpdateTagsEvent; + + ViewTagVector maTagVector; +}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/zoomlist.hxx b/sd/source/ui/inc/zoomlist.hxx new file mode 100644 index 000000000..b7f7da79d --- /dev/null +++ b/sd/source/ui/inc/zoomlist.hxx @@ -0,0 +1,50 @@ +/* -*- 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 . + */ + +#pragma once + +#include <vector> + +#include <tools/gen.hxx> + +namespace sd +{ +class ViewShell; + +class ZoomList +{ +public: + ZoomList(ViewShell* pViewShell); + + void InsertZoomRect(const ::tools::Rectangle& rRect); + ::tools::Rectangle const& GetNextZoomRect(); + ::tools::Rectangle const& GetPreviousZoomRect(); + bool IsNextPossible() const; + bool IsPreviousPossible() const; + +private: + ViewShell* mpViewShell; + sal_uInt32 mnCurPos; + + std::vector<::tools::Rectangle> maRectangles; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/presenter/CanvasUpdateRequester.cxx b/sd/source/ui/presenter/CanvasUpdateRequester.cxx new file mode 100644 index 000000000..2271ba781 --- /dev/null +++ b/sd/source/ui/presenter/CanvasUpdateRequester.cxx @@ -0,0 +1,131 @@ +/* -*- 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 "CanvasUpdateRequester.hxx" +#include <vcl/svapp.hxx> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/rendering/XSpriteCanvas.hpp> +#include <cppuhelper/weakref.hxx> +#include <vector> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::presenter { + +//===== CanvasUpdateRequester::Deleter ======================================== + +class CanvasUpdateRequester::Deleter +{ +public: + void operator() (CanvasUpdateRequester* pObject) { delete pObject; } +}; + +//===== CanvasUpdateRequester ================================================= + +std::shared_ptr<CanvasUpdateRequester> CanvasUpdateRequester::Instance ( + const Reference<rendering::XSpriteCanvas>& rxSharedCanvas) +{ + // this global must not own anything or we crash on shutdown + static std::vector<std::pair< + uno::WeakReference<rendering::XSpriteCanvas>, + std::weak_ptr<CanvasUpdateRequester>>> s_RequesterMap; + for (auto it = s_RequesterMap.begin(); it != s_RequesterMap.end(); ) + { + uno::Reference<rendering::XSpriteCanvas> const xCanvas(it->first); + if (!xCanvas.is()) + { + it = s_RequesterMap.erase(it); // remove stale entry + } + else if (xCanvas == rxSharedCanvas) + { + std::shared_ptr<CanvasUpdateRequester> pRequester(it->second); + if (pRequester) + { + return pRequester; + } + else + { + std::shared_ptr<CanvasUpdateRequester> const pNew( + new CanvasUpdateRequester(rxSharedCanvas), Deleter()); + it->second = pNew; + return pNew; + } + } + else + { + ++it; + } + } + + // No requester for the given canvas found. Create a new one. + std::shared_ptr<CanvasUpdateRequester> pRequester ( + new CanvasUpdateRequester(rxSharedCanvas), Deleter()); + s_RequesterMap.emplace_back(rxSharedCanvas, pRequester); + return pRequester; +} + + +CanvasUpdateRequester::CanvasUpdateRequester ( + const Reference<rendering::XSpriteCanvas>& rxCanvas) + : mxCanvas(rxCanvas) + , m_pUserEventId(nullptr) + , mbUpdateFlag(false) +{ + Reference<lang::XComponent> xComponent (mxCanvas, UNO_QUERY); + if (xComponent.is()) + { + //xComponent->addEventListener(this); + } +} + +CanvasUpdateRequester::~CanvasUpdateRequester() +{ + assert(m_pUserEventId == nullptr); +} + +void CanvasUpdateRequester::RequestUpdate (const bool bUpdateAll) +{ + if (m_pUserEventId == nullptr) + { + m_pThis = shared_from_this(); // keep instance alive until dispatch + mbUpdateFlag = bUpdateAll; + m_pUserEventId = Application::PostUserEvent(LINK(this, CanvasUpdateRequester, Callback)); + } + else + { + mbUpdateFlag |= bUpdateAll; + } +} + +IMPL_LINK_NOARG(CanvasUpdateRequester, Callback, void*, void) +{ + m_pUserEventId = nullptr; + if (mxCanvas.is()) + { + mxCanvas->updateScreen(mbUpdateFlag); + mbUpdateFlag = false; + } + assert(m_pThis); + m_pThis.reset(); // possibly delete "this" +} + +} // end of namespace ::sd::presenter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/presenter/CanvasUpdateRequester.hxx b/sd/source/ui/presenter/CanvasUpdateRequester.hxx new file mode 100644 index 000000000..ebb582ead --- /dev/null +++ b/sd/source/ui/presenter/CanvasUpdateRequester.hxx @@ -0,0 +1,72 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/uno/Reference.hxx> +#include <tools/link.hxx> +#include <memory> + +namespace com::sun::star::rendering +{ +class XSpriteCanvas; +} + +struct ImplSVEvent; + +namespace sd::presenter +{ +/** Each UpdateRequester handles update requests (calls to + XCanvas::updateScreen()) for one shared canvas (a canvas that has one or + more PresenterCanvas wrappers). Multiple calls are collected and lead + to a single call to updateScreen. +*/ +class CanvasUpdateRequester : public std::enable_shared_from_this<CanvasUpdateRequester> +{ +public: + CanvasUpdateRequester(const CanvasUpdateRequester&) = delete; + CanvasUpdateRequester& operator=(const CanvasUpdateRequester&) = delete; + + /** @return the Canvas UpdateRequester object for the given shared canvas. + A new object is created when it does not already exist. + */ + static std::shared_ptr<CanvasUpdateRequester> + Instance(const css::uno::Reference<css::rendering::XSpriteCanvas>& rxCanvas); + + void RequestUpdate(const bool bUpdateAll); + +private: + explicit CanvasUpdateRequester( + const css::uno::Reference<css::rendering::XSpriteCanvas>& rxCanvas); + ~CanvasUpdateRequester(); + class Deleter; + friend class Deleter; + + /// keep instance alive waiting for event dispatch + std::shared_ptr<CanvasUpdateRequester> m_pThis; + css::uno::Reference<css::rendering::XSpriteCanvas> mxCanvas; + ImplSVEvent* m_pUserEventId; + bool mbUpdateFlag; + + DECL_LINK(Callback, void*, void); +}; + +} // end of namespace ::sd::presenter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/presenter/PresenterCanvas.cxx b/sd/source/ui/presenter/PresenterCanvas.cxx new file mode 100644 index 000000000..f586969bc --- /dev/null +++ b/sd/source/ui/presenter/PresenterCanvas.cxx @@ -0,0 +1,790 @@ +/* -*- 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 "PresenterCanvas.hxx" +#include "CanvasUpdateRequester.hxx" + +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/polygon/b2dpolygonclipper.hxx> +#include <basegfx/range/b2drectangle.hxx> +#include <basegfx/utils/canvastools.hxx> +#include <com/sun/star/awt/XWindow.hpp> +#include <comphelper/compbase.hxx> +#include <rtl/ref.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <vcl/window.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::presenter { + +//===== PresenterCustomSprite ================================================= + +/** Wrapper around a sprite that is displayed on a PresenterCanvas. +*/ +namespace { + typedef comphelper::WeakComponentImplHelper < + css::rendering::XCustomSprite + > PresenterCustomSpriteInterfaceBase; + +class PresenterCustomSprite final + : public PresenterCustomSpriteInterfaceBase +{ +public: + PresenterCustomSprite ( + const rtl::Reference<PresenterCanvas>& rpCanvas, + const Reference<rendering::XCustomSprite>& rxSprite, + const Reference<awt::XWindow>& rxBaseWindow); + PresenterCustomSprite(const PresenterCustomSprite&) = delete; + PresenterCustomSprite& operator=(const PresenterCustomSprite&) = delete; + virtual void disposing(std::unique_lock<std::mutex>&) override; + + // XSprite + + virtual void SAL_CALL setAlpha (double nAlpha) override; + + virtual void SAL_CALL move (const geometry::RealPoint2D& rNewPos, + const rendering::ViewState& rViewState, + const rendering::RenderState& rRenderState) override; + + virtual void SAL_CALL transform (const geometry::AffineMatrix2D& rTransformation) override; + + virtual void SAL_CALL clip (const Reference<rendering::XPolyPolygon2D>& rClip) override; + + virtual void SAL_CALL setPriority (double nPriority) override; + + virtual void SAL_CALL show() override; + + virtual void SAL_CALL hide() override; + + // XCustomSprite + + virtual Reference<rendering::XCanvas> SAL_CALL getContentCanvas() override; + +private: + rtl::Reference<PresenterCanvas> mpCanvas; + Reference<rendering::XCustomSprite> mxSprite; + Reference<awt::XWindow> mxBaseWindow; + geometry::RealPoint2D maPosition; + + /// @throws css::lang::DisposedException + void ThrowIfDisposed(); +}; + +} + +//===== PresenterCanvas ======================================================= + +PresenterCanvas::PresenterCanvas ( + const Reference<rendering::XSpriteCanvas>& rxUpdateCanvas, + const Reference<awt::XWindow>& rxUpdateWindow, + const Reference<rendering::XCanvas>& rxSharedCanvas, + const Reference<awt::XWindow>& rxSharedWindow, + const Reference<awt::XWindow>& rxWindow) + : mxUpdateCanvas(rxUpdateCanvas), + mxUpdateWindow(rxUpdateWindow), + mxSharedCanvas(rxSharedCanvas), + mxSharedWindow(rxSharedWindow), + mxWindow(rxWindow), + mbOffsetUpdatePending(true) +{ + if (mxWindow.is()) + mxWindow->addWindowListener(this); + + if (mxUpdateCanvas.is()) + { + m_pUpdateRequester = CanvasUpdateRequester::Instance(mxUpdateCanvas); + } +} + +PresenterCanvas::~PresenterCanvas() +{ +} + +void PresenterCanvas::disposing(std::unique_lock<std::mutex>&) +{ + if (mxWindow.is()) + { + mxWindow->removeWindowListener(this); + mxWindow.clear(); + } +} + +//----- XCanvas --------------------------------------------------------------- + +void SAL_CALL PresenterCanvas::clear() +{ + ThrowIfDisposed(); + // ToDo: Clear the area covered by the child window. A simple forward + // would clear the whole shared canvas. +} + +void SAL_CALL PresenterCanvas::drawPoint ( + const css::geometry::RealPoint2D& aPoint, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) +{ + ThrowIfDisposed(); + mxSharedCanvas->drawPoint(aPoint,MergeViewState(aViewState),aRenderState); +} + +void SAL_CALL PresenterCanvas::drawLine ( + const css::geometry::RealPoint2D& aStartPoint, + const css::geometry::RealPoint2D& aEndPoint, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) +{ + ThrowIfDisposed(); + mxSharedCanvas->drawLine(aStartPoint,aEndPoint,MergeViewState(aViewState),aRenderState); +} + +void SAL_CALL PresenterCanvas::drawBezier ( + const css::geometry::RealBezierSegment2D& aBezierSegment, + const css::geometry::RealPoint2D& aEndPoint, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) +{ + ThrowIfDisposed(); + mxSharedCanvas->drawBezier(aBezierSegment,aEndPoint,MergeViewState(aViewState),aRenderState); +} + +css::uno::Reference<css::rendering::XCachedPrimitive> SAL_CALL PresenterCanvas::drawPolyPolygon ( + const css::uno::Reference< css::rendering::XPolyPolygon2D >& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) +{ + ThrowIfDisposed(); + return mxSharedCanvas->drawPolyPolygon( + xPolyPolygon, MergeViewState(aViewState), aRenderState); +} + +css::uno::Reference<css::rendering::XCachedPrimitive> SAL_CALL PresenterCanvas::strokePolyPolygon ( + const css::uno::Reference< css::rendering::XPolyPolygon2D >& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState, + const css::rendering::StrokeAttributes& aStrokeAttributes) +{ + ThrowIfDisposed(); + return mxSharedCanvas->strokePolyPolygon( + xPolyPolygon, MergeViewState(aViewState), aRenderState, aStrokeAttributes); +} + +css::uno::Reference<css::rendering::XCachedPrimitive> SAL_CALL + PresenterCanvas::strokeTexturedPolyPolygon ( + const css::uno::Reference< css::rendering::XPolyPolygon2D >& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState, + const css::uno::Sequence< css::rendering::Texture >& aTextures, + const css::rendering::StrokeAttributes& aStrokeAttributes) +{ + ThrowIfDisposed(); + return mxSharedCanvas->strokeTexturedPolyPolygon( + xPolyPolygon, MergeViewState(aViewState), aRenderState, aTextures, aStrokeAttributes); +} + +css::uno::Reference<css::rendering::XCachedPrimitive> SAL_CALL + PresenterCanvas::strokeTextureMappedPolyPolygon( + const css::uno::Reference<css::rendering::XPolyPolygon2D >& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState, + const css::uno::Sequence<css::rendering::Texture>& aTextures, + const css::uno::Reference<css::geometry::XMapping2D>& xMapping, + const css::rendering::StrokeAttributes& aStrokeAttributes) +{ + ThrowIfDisposed(); + return mxSharedCanvas->strokeTextureMappedPolyPolygon( + xPolyPolygon, + MergeViewState(aViewState), + aRenderState, + aTextures, + xMapping, + aStrokeAttributes); +} + +css::uno::Reference<css::rendering::XPolyPolygon2D> SAL_CALL + PresenterCanvas::queryStrokeShapes( + const css::uno::Reference<css::rendering::XPolyPolygon2D>& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState, + const css::rendering::StrokeAttributes& aStrokeAttributes) +{ + ThrowIfDisposed(); + return mxSharedCanvas->queryStrokeShapes( + xPolyPolygon, MergeViewState(aViewState), aRenderState, aStrokeAttributes); +} + +css::uno::Reference<css::rendering::XCachedPrimitive> SAL_CALL + PresenterCanvas::fillPolyPolygon( + const css::uno::Reference<css::rendering::XPolyPolygon2D>& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) +{ + ThrowIfDisposed(); + return mxSharedCanvas->fillPolyPolygon( + xPolyPolygon, MergeViewState(aViewState), aRenderState); +} + +css::uno::Reference<css::rendering::XCachedPrimitive> SAL_CALL + PresenterCanvas::fillTexturedPolyPolygon( + const css::uno::Reference<css::rendering::XPolyPolygon2D>& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState, + const css::uno::Sequence<css::rendering::Texture>& xTextures) +{ + ThrowIfDisposed(); + return mxSharedCanvas->fillTexturedPolyPolygon( + xPolyPolygon, MergeViewState(aViewState), aRenderState, xTextures); +} + +css::uno::Reference<css::rendering::XCachedPrimitive> SAL_CALL + PresenterCanvas::fillTextureMappedPolyPolygon( + const css::uno::Reference< css::rendering::XPolyPolygon2D >& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState, + const css::uno::Sequence< css::rendering::Texture >& xTextures, + const css::uno::Reference< css::geometry::XMapping2D >& xMapping) +{ + ThrowIfDisposed(); + return mxSharedCanvas->fillTextureMappedPolyPolygon( + xPolyPolygon, MergeViewState(aViewState), aRenderState, xTextures, xMapping); +} + +css::uno::Reference<css::rendering::XCanvasFont> SAL_CALL + PresenterCanvas::createFont( + const css::rendering::FontRequest& aFontRequest, + const css::uno::Sequence< css::beans::PropertyValue >& aExtraFontProperties, + const css::geometry::Matrix2D& aFontMatrix) +{ + ThrowIfDisposed(); + return mxSharedCanvas->createFont( + aFontRequest, aExtraFontProperties, aFontMatrix); +} + +css::uno::Sequence<css::rendering::FontInfo> SAL_CALL + PresenterCanvas::queryAvailableFonts( + const css::rendering::FontInfo& aFilter, + const css::uno::Sequence< css::beans::PropertyValue >& aFontProperties) +{ + ThrowIfDisposed(); + return mxSharedCanvas->queryAvailableFonts(aFilter, aFontProperties); +} + +css::uno::Reference<css::rendering::XCachedPrimitive> SAL_CALL + PresenterCanvas::drawText( + const css::rendering::StringContext& aText, + const css::uno::Reference< css::rendering::XCanvasFont >& xFont, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState, + ::sal_Int8 nTextDirection) +{ + ThrowIfDisposed(); + return mxSharedCanvas->drawText( + aText, xFont, MergeViewState(aViewState), aRenderState, nTextDirection); +} + +css::uno::Reference<css::rendering::XCachedPrimitive> SAL_CALL + PresenterCanvas::drawTextLayout( + const css::uno::Reference< css::rendering::XTextLayout >& xLayoutetText, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) +{ + ThrowIfDisposed(); + return mxSharedCanvas->drawTextLayout( + xLayoutetText, MergeViewState(aViewState), aRenderState); +} + +css::uno::Reference<css::rendering::XCachedPrimitive> SAL_CALL + PresenterCanvas::drawBitmap( + const css::uno::Reference< css::rendering::XBitmap >& xBitmap, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) +{ + ThrowIfDisposed(); + return mxSharedCanvas->drawBitmap( + xBitmap, MergeViewState(aViewState), aRenderState); +} + +css::uno::Reference<css::rendering::XCachedPrimitive> SAL_CALL + PresenterCanvas::drawBitmapModulated( + const css::uno::Reference< css::rendering::XBitmap>& xBitmap, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) +{ + ThrowIfDisposed(); + return mxSharedCanvas->drawBitmapModulated( + xBitmap, MergeViewState(aViewState), aRenderState); +} + +css::uno::Reference<css::rendering::XGraphicDevice> SAL_CALL + PresenterCanvas::getDevice() +{ + ThrowIfDisposed(); + return mxSharedCanvas->getDevice(); +} + +//----- XSpriteCanvas --------------------------------------------------------- + +Reference<rendering::XAnimatedSprite> SAL_CALL + PresenterCanvas::createSpriteFromAnimation ( + const css::uno::Reference<css::rendering::XAnimation>& rAnimation) +{ + ThrowIfDisposed(); + + Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxSharedCanvas, UNO_QUERY); + if (xSpriteCanvas.is()) + return xSpriteCanvas->createSpriteFromAnimation(rAnimation); + else + return nullptr; +} + +Reference<rendering::XAnimatedSprite> SAL_CALL + PresenterCanvas::createSpriteFromBitmaps ( + const css::uno::Sequence< + css::uno::Reference< css::rendering::XBitmap > >& rAnimationBitmaps, + ::sal_Int8 nInterpolationMode) +{ + ThrowIfDisposed(); + + Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxSharedCanvas, UNO_QUERY); + if (xSpriteCanvas.is()) + return xSpriteCanvas->createSpriteFromBitmaps(rAnimationBitmaps, nInterpolationMode); + else + return nullptr; +} + +Reference<rendering::XCustomSprite> SAL_CALL + PresenterCanvas::createCustomSprite ( + const css::geometry::RealSize2D& rSpriteSize) +{ + ThrowIfDisposed(); + + Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxSharedCanvas, UNO_QUERY); + if (xSpriteCanvas.is()) + return new PresenterCustomSprite( + this, + xSpriteCanvas->createCustomSprite(rSpriteSize), + mxSharedWindow); + else if (mxUpdateCanvas.is()) + return new PresenterCustomSprite( + this, + mxUpdateCanvas->createCustomSprite(rSpriteSize), + mxUpdateWindow); + else + return nullptr; +} + +Reference<rendering::XSprite> SAL_CALL + PresenterCanvas::createClonedSprite ( + const css::uno::Reference< css::rendering::XSprite >& rxOriginal) +{ + ThrowIfDisposed(); + + Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxSharedCanvas, UNO_QUERY); + if (xSpriteCanvas.is()) + return xSpriteCanvas->createClonedSprite(rxOriginal); + if (mxUpdateCanvas.is()) + return mxUpdateCanvas->createClonedSprite(rxOriginal); + return nullptr; +} + +sal_Bool SAL_CALL PresenterCanvas::updateScreen (sal_Bool bUpdateAll) +{ + ThrowIfDisposed(); + + mbOffsetUpdatePending = true; + if (m_pUpdateRequester != nullptr) + { + m_pUpdateRequester->RequestUpdate(bUpdateAll); + return true; + } + else + { + return false; + } +} + +//----- XEventListener -------------------------------------------------------- + +void SAL_CALL PresenterCanvas::disposing (const css::lang::EventObject& rEvent) +{ + ThrowIfDisposed(); + if (rEvent.Source == mxWindow) + mxWindow = nullptr; +} + +//----- XWindowListener ------------------------------------------------------- + +void SAL_CALL PresenterCanvas::windowResized (const css::awt::WindowEvent&) +{ + ThrowIfDisposed(); + mbOffsetUpdatePending = true; +} + +void SAL_CALL PresenterCanvas::windowMoved (const css::awt::WindowEvent&) +{ + ThrowIfDisposed(); + mbOffsetUpdatePending = true; +} + +void SAL_CALL PresenterCanvas::windowShown (const css::lang::EventObject&) +{ + ThrowIfDisposed(); + mbOffsetUpdatePending = true; +} + +void SAL_CALL PresenterCanvas::windowHidden (const css::lang::EventObject&) +{ + ThrowIfDisposed(); +} + +//----- XBitmap --------------------------------------------------------------- + +geometry::IntegerSize2D SAL_CALL PresenterCanvas::getSize() +{ + ThrowIfDisposed(); + + if (mxWindow.is()) + { + const awt::Rectangle aWindowBox (mxWindow->getPosSize()); + return geometry::IntegerSize2D(aWindowBox.Width, aWindowBox.Height); + } + else + return geometry::IntegerSize2D(0,0); +} + +sal_Bool SAL_CALL PresenterCanvas::hasAlpha() +{ + Reference<rendering::XBitmap> xBitmap (mxSharedCanvas, UNO_QUERY); + if (xBitmap.is()) + return xBitmap->hasAlpha(); + else + return false; +} + +Reference<rendering::XBitmap> SAL_CALL PresenterCanvas::getScaledBitmap( + const css::geometry::RealSize2D&, + sal_Bool) +{ + ThrowIfDisposed(); + + // Not implemented. + + return nullptr; +} + +rendering::ViewState PresenterCanvas::MergeViewState ( + const rendering::ViewState& rViewState) +{ + // Make sure the offset is up-to-date. + if (mbOffsetUpdatePending) + maOffset = GetOffset(mxSharedWindow); + return MergeViewState(rViewState, maOffset); +} + +css::rendering::ViewState PresenterCanvas::MergeViewState ( + const css::rendering::ViewState& rViewState, + const css::awt::Point& rOffset) +{ + // Early rejects. + if ( ! mxSharedCanvas.is()) + return rViewState; + + Reference<rendering::XGraphicDevice> xDevice (mxSharedCanvas->getDevice()); + if ( ! xDevice.is()) + return rViewState; + + // Create a modifiable copy of the given view state. + rendering::ViewState aViewState (rViewState); + + // Prepare the local clip rectangle. + ::basegfx::B2DRectangle aWindowRange (GetClipRectangle(aViewState.AffineTransform, rOffset)); + + // Adapt the offset of the view state. + aViewState.AffineTransform.m02 += rOffset.X; + aViewState.AffineTransform.m12 += rOffset.Y; + + // Adapt the clip polygon. + if ( ! aViewState.Clip.is()) + { + // Cancel out the later multiplication with the view state + // transformation. + aViewState.Clip = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( + xDevice, + ::basegfx::B2DPolyPolygon(::basegfx::utils::createPolygonFromRect(aWindowRange))); + } + else + { + // Have to compute the intersection of the given clipping polygon in + // the view state and the local clip rectangle. + + // Clip the view state clipping polygon against the local clip rectangle. + const ::basegfx::B2DPolyPolygon aClipPolygon ( + ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D( + aViewState.Clip)); + const ::basegfx::B2DPolyPolygon aClippedClipPolygon ( + ::basegfx::utils::clipPolyPolygonOnRange( + aClipPolygon, + aWindowRange, + true, /* bInside */ + false /* bStroke */)); + + aViewState.Clip = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( + xDevice, + aClippedClipPolygon); + } + + return aViewState; +} + +awt::Point PresenterCanvas::GetOffset (const Reference<awt::XWindow>& rxBaseWindow) +{ + mbOffsetUpdatePending = false; + if (mxWindow.is() && rxBaseWindow.is()) + { + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(mxWindow); + VclPtr<vcl::Window> pSharedWindow = VCLUnoHelper::GetWindow(rxBaseWindow); + if (pWindow && pSharedWindow) + { + ::tools::Rectangle aBox = pWindow->GetWindowExtentsRelative(pSharedWindow); + + // Calculate offset of this canvas with respect to the shared + // canvas. + return awt::Point(aBox.Left(), aBox.Top()); + } + } + + return awt::Point(0, 0); +} + +::basegfx::B2DRectangle PresenterCanvas::GetClipRectangle ( + const css::geometry::AffineMatrix2D& rViewTransform, + const awt::Point& rOffset) +{ + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(mxWindow); + if (!pWindow) + return ::basegfx::B2DRectangle(); + + VclPtr<vcl::Window> pSharedWindow = VCLUnoHelper::GetWindow(mxSharedWindow); + if (!pSharedWindow) + return ::basegfx::B2DRectangle(); + + // Get the bounding box of the window and create a range in the + // coordinate system of the child window. + // Use the window extents. + ::tools::Rectangle aLocalClip = pWindow->GetWindowExtentsRelative(pSharedWindow); + + // The local clip rectangle is used to clip the view state clipping + // polygon. + ::basegfx::B2DRectangle aWindowRectangle ( + aLocalClip.Left() - rOffset.X, + aLocalClip.Top() - rOffset.Y, + aLocalClip.Right() - rOffset.X + 1, + aLocalClip.Bottom() - rOffset.Y + 1); + + // Calculate the inverted view state transformation to cancel out a + // later transformation of the local clip polygon with the view state + // transformation. + ::basegfx::B2DHomMatrix aInvertedViewStateTransformation; + ::basegfx::unotools::homMatrixFromAffineMatrix( + aInvertedViewStateTransformation, + rViewTransform); + if (aInvertedViewStateTransformation.invert()) + { + // Cancel out the later multiplication with the view state + // transformation. + aWindowRectangle.transform(aInvertedViewStateTransformation); + } + + return aWindowRectangle; +} + +Reference<rendering::XPolyPolygon2D> PresenterCanvas::UpdateSpriteClip ( + const Reference<rendering::XPolyPolygon2D>& rxOriginalClip, + const geometry::RealPoint2D& rLocation) +{ + // Check used resources and just return the original clip when not + // every one of them is available. + if ( ! mxWindow.is()) + return rxOriginalClip; + + Reference<rendering::XGraphicDevice> xDevice (mxSharedCanvas->getDevice()); + if ( ! xDevice.is()) + return rxOriginalClip; + + // Determine the bounds of the clip rectangle (the window border) in the + // coordinate system of the sprite. + const awt::Rectangle aWindowBox (mxWindow->getPosSize()); + const double nMinX (-rLocation.X); + const double nMinY (-rLocation.Y); + const double nMaxX (aWindowBox.Width-rLocation.X); + const double nMaxY (aWindowBox.Height-rLocation.Y); + + // Create a clip polygon. + Reference<rendering::XPolyPolygon2D> xPolygon; + if (rxOriginalClip.is()) + { + // Combine the original clip with the window clip. + const ::basegfx::B2DPolyPolygon aOriginalClip ( + ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(rxOriginalClip)); + ::basegfx::B2DRectangle aWindowRange (nMinX, nMinY, nMaxX, nMaxY); + const ::basegfx::B2DPolyPolygon aClippedClipPolygon ( + ::basegfx::utils::clipPolyPolygonOnRange( + aOriginalClip, + aWindowRange, + true, /* bInside */ + false /* bStroke */)); + xPolygon = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( + xDevice, + aClippedClipPolygon); + } + else + { + // Create a new clip polygon from the window clip rectangle. + Sequence<Sequence<geometry::RealPoint2D> > aPoints + { + { + { nMinX,nMinY }, + { nMaxX,nMinY }, + { nMaxX,nMaxY }, + { nMinX,nMaxY } + } + }; + Reference<rendering::XLinePolyPolygon2D> xLinePolygon( + xDevice->createCompatibleLinePolyPolygon(aPoints)); + if (xLinePolygon.is()) + xLinePolygon->setClosed(0, true); + xPolygon = xLinePolygon; + } + + return xPolygon; +} + +void PresenterCanvas::ThrowIfDisposed() +{ + if (m_bDisposed || ! mxSharedCanvas.is()) + { + throw lang::DisposedException ("PresenterCanvas object has already been disposed", + static_cast<uno::XWeak*>(this)); + } +} + +//===== PresenterCustomSprite ================================================= + +PresenterCustomSprite::PresenterCustomSprite ( + const rtl::Reference<PresenterCanvas>& rpCanvas, + const Reference<rendering::XCustomSprite>& rxSprite, + const Reference<awt::XWindow>& rxBaseWindow) + : mpCanvas(rpCanvas), + mxSprite(rxSprite), + mxBaseWindow(rxBaseWindow), + maPosition(0,0) +{ +} + +void PresenterCustomSprite::disposing(std::unique_lock<std::mutex>&) +{ + Reference<XComponent> xComponent (mxSprite, UNO_QUERY); + mxSprite = nullptr; + if (xComponent.is()) + xComponent->dispose(); + mpCanvas.clear(); +} + +//----- XSprite --------------------------------------------------------------- + +void SAL_CALL PresenterCustomSprite::setAlpha (const double nAlpha) +{ + ThrowIfDisposed(); + mxSprite->setAlpha(nAlpha); +} + +void SAL_CALL PresenterCustomSprite::move ( + const geometry::RealPoint2D& rNewPos, + const rendering::ViewState& rViewState, + const rendering::RenderState& rRenderState) +{ + ThrowIfDisposed(); + maPosition = rNewPos; + mxSprite->move( + rNewPos, + mpCanvas->MergeViewState(rViewState, mpCanvas->GetOffset(mxBaseWindow)), + rRenderState); + // Clip sprite against window bounds. This call is necessary because + // sprite clipping is done in the coordinate system of the sprite. + // Therefore, after each change of the sprites location the window + // bounds have to be transformed into the sprites coordinate system. + clip(nullptr); +} + +void SAL_CALL PresenterCustomSprite::transform (const geometry::AffineMatrix2D& rTransformation) +{ + ThrowIfDisposed(); + mxSprite->transform(rTransformation); +} + +void SAL_CALL PresenterCustomSprite::clip (const Reference<rendering::XPolyPolygon2D>& rxClip) +{ + ThrowIfDisposed(); + // The clip region is expected in the coordinate system of the sprite. + // UpdateSpriteClip() integrates the window bounds, transformed into the + // sprites coordinate system, with the given clip. + mxSprite->clip(mpCanvas->UpdateSpriteClip(rxClip, maPosition)); +} + +void SAL_CALL PresenterCustomSprite::setPriority (const double nPriority) +{ + ThrowIfDisposed(); + mxSprite->setPriority(nPriority); +} + +void SAL_CALL PresenterCustomSprite::show() +{ + ThrowIfDisposed(); + mxSprite->show(); +} + +void SAL_CALL PresenterCustomSprite::hide() +{ + ThrowIfDisposed(); + mxSprite->hide(); +} + +//----- XCustomSprite --------------------------------------------------------- + +Reference<rendering::XCanvas> PresenterCustomSprite::getContentCanvas() +{ + ThrowIfDisposed(); + return mxSprite->getContentCanvas(); +} + +void PresenterCustomSprite::ThrowIfDisposed() +{ + if (m_bDisposed || ! mxSprite.is()) + { + throw lang::DisposedException ("PresenterCustomSprite object has already been disposed", + static_cast<uno::XWeak*>(this)); + } +} + +} // end of namespace ::sd::presenter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/presenter/PresenterCanvas.hxx b/sd/source/ui/presenter/PresenterCanvas.hxx new file mode 100644 index 000000000..da2f51a79 --- /dev/null +++ b/sd/source/ui/presenter/PresenterCanvas.hxx @@ -0,0 +1,320 @@ +/* -*- 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 . + */ + +#pragma once + +#include <basegfx/range/b2drectangle.hxx> +#include <com/sun/star/awt/Point.hpp> +#include <com/sun/star/awt/XWindowListener.hpp> +#include <com/sun/star/rendering/XSpriteCanvas.hpp> +#include <com/sun/star/rendering/XBitmap.hpp> +#include <comphelper/compbase.hxx> +#include <memory> + +namespace sd::presenter { class CanvasUpdateRequester; } +namespace com::sun::star::awt { class XWindow; } +namespace com::sun::star::geometry { struct AffineMatrix2D; } + +namespace sd::presenter { + +typedef comphelper::WeakComponentImplHelper < + css::rendering::XSpriteCanvas, + css::rendering::XBitmap, + css::awt::XWindowListener +> PresenterCanvasInterfaceBase; + +/** Wrapper around a shared canvas that forwards most of its methods to the + shared canvas. Most notable differences are: + 1. The transformation of the ViewState of forwarded calls is modified by adding + an offset. + 2. The clip polygon of the ViewState of forwarded calls is intersected + with a clip rectangle that can be set via SetClip(). + 3. Calls to updateScreen() are collected. One call to the updateScreen() + method of the shared canvas is made asynchronously. + + The canvas can use different canvases for sharing and for sprite + construction. This allows the shared canvas to be a canvas of sprite itself. +*/ +class PresenterCanvas + : public PresenterCanvasInterfaceBase +{ +public: + /** This constructor is used when a PresenterCanvas object is created + directly, typically by the PresenterCanvasFactory. + @param rxUpdateCanvas + This canvas is used to call updateScreen() at and to create + sprites. In the typical case this canvas is identical to the + rxSharedCanvas argument. + @param rxUpdateWindow + The window that belongs to the canvas given by the + rxUpdateCanvas argument. + @param rxSharedCanvas + The canvas that is wrapped by the new instance of this class. + Typically this is a regular XSpriteCanvas and then is identical + to the one given by the rxUpdateCanvas argument. It may be the + canvas of a sprite which does not support the XSpriteCanvas + interface. In that case the canvas that created the sprite can + be given as rxUpdateCanvas argument to allow to create further + sprites and to have proper calls to updateScreen(). + @param rxSharedWindow + The window that belongs to the canvas given by the + rxSharedCanvas argument. + @param rxWindow + The window that is represented by the new PresenterCanvas + object. It is expected to be a direct descendant of + rxSharedWindow. Its position inside rxSharedWindow defines the + offset of the canvas implemented by the new PresenterCanvas + object and rxSharedCanvas. + */ + PresenterCanvas ( + const css::uno::Reference<css::rendering::XSpriteCanvas>& rxUpdateCanvas, + const css::uno::Reference<css::awt::XWindow>& rxUpdateWindow, + const css::uno::Reference<css::rendering::XCanvas>& rxSharedCanvas, + const css::uno::Reference<css::awt::XWindow>& rxSharedWindow, + const css::uno::Reference<css::awt::XWindow>& rxWindow); + virtual ~PresenterCanvas() override; + PresenterCanvas(const PresenterCanvas&) = delete; + PresenterCanvas& operator=(const PresenterCanvas&) = delete; + + virtual void disposing(std::unique_lock<std::mutex>&) override; + + css::awt::Point GetOffset (const css::uno::Reference<css::awt::XWindow>& rxBaseWindow); + + /** Merge the given view state with the view state that translates the + (virtual) child canvas to the shared canvas. + */ + css::rendering::ViewState MergeViewState ( + const css::rendering::ViewState& rViewState, + const css::awt::Point& raOffset); + + /** Called by custom sprites to update their clip polygon so that they + are clipped at the borders of the canvas. This method has to be + called after each change of the sprite location so that the bounds + of the canvas can be transformed into the coordinate system of the + sprite. + */ + css::uno::Reference<css::rendering::XPolyPolygon2D> UpdateSpriteClip ( + const css::uno::Reference<css::rendering::XPolyPolygon2D>& rxOriginalClip, + const css::geometry::RealPoint2D& rLocation); + + // XCanvas + + virtual void SAL_CALL clear() override; + + virtual void SAL_CALL drawPoint ( + const css::geometry::RealPoint2D& aPoint, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) override; + + virtual void SAL_CALL drawLine ( + const css::geometry::RealPoint2D& aStartPoint, + const css::geometry::RealPoint2D& aEndPoint, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) override; + + virtual void SAL_CALL drawBezier ( + const css::geometry::RealBezierSegment2D& aBezierSegment, + const css::geometry::RealPoint2D& aEndPoint, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) override; + + virtual css::uno::Reference<css::rendering::XCachedPrimitive> SAL_CALL drawPolyPolygon ( + const css::uno::Reference< css::rendering::XPolyPolygon2D >& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) override; + + virtual css::uno::Reference<css::rendering::XCachedPrimitive> SAL_CALL strokePolyPolygon ( + const css::uno::Reference< css::rendering::XPolyPolygon2D >& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState, + const css::rendering::StrokeAttributes& aStrokeAttributes) override; + + virtual css::uno::Reference<css::rendering::XCachedPrimitive> SAL_CALL + strokeTexturedPolyPolygon ( + const css::uno::Reference< css::rendering::XPolyPolygon2D >& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState, + const css::uno::Sequence< css::rendering::Texture >& aTextures, + const css::rendering::StrokeAttributes& aStrokeAttributes) override; + + virtual css::uno::Reference<css::rendering::XCachedPrimitive> SAL_CALL + strokeTextureMappedPolyPolygon( + const css::uno::Reference<css::rendering::XPolyPolygon2D >& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState, + const css::uno::Sequence<css::rendering::Texture>& aTextures, + const css::uno::Reference<css::geometry::XMapping2D>& xMapping, + const css::rendering::StrokeAttributes& aStrokeAttributes) override; + + virtual css::uno::Reference<css::rendering::XPolyPolygon2D> SAL_CALL + queryStrokeShapes( + const css::uno::Reference<css::rendering::XPolyPolygon2D>& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState, + const css::rendering::StrokeAttributes& aStrokeAttributes) override; + + virtual css::uno::Reference<css::rendering::XCachedPrimitive> SAL_CALL + fillPolyPolygon( + const css::uno::Reference<css::rendering::XPolyPolygon2D>& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) override; + + virtual css::uno::Reference<css::rendering::XCachedPrimitive> SAL_CALL + fillTexturedPolyPolygon( + const css::uno::Reference<css::rendering::XPolyPolygon2D>& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState, + const css::uno::Sequence<css::rendering::Texture>& xTextures) override; + + virtual css::uno::Reference<css::rendering::XCachedPrimitive> SAL_CALL + fillTextureMappedPolyPolygon( + const css::uno::Reference< css::rendering::XPolyPolygon2D >& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState, + const css::uno::Sequence< css::rendering::Texture >& xTextures, + const css::uno::Reference< css::geometry::XMapping2D >& xMapping) override; + + virtual css::uno::Reference<css::rendering::XCanvasFont> SAL_CALL + createFont( + const css::rendering::FontRequest& aFontRequest, + const css::uno::Sequence< css::beans::PropertyValue >& aExtraFontProperties, + const css::geometry::Matrix2D& aFontMatrix) override; + + virtual css::uno::Sequence<css::rendering::FontInfo> SAL_CALL + queryAvailableFonts( + const css::rendering::FontInfo& aFilter, + const css::uno::Sequence< css::beans::PropertyValue >& aFontProperties) override; + + virtual css::uno::Reference<css::rendering::XCachedPrimitive> SAL_CALL + drawText( + const css::rendering::StringContext& aText, + const css::uno::Reference< css::rendering::XCanvasFont >& xFont, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState, + ::sal_Int8 nTextDirection) override; + + virtual css::uno::Reference<css::rendering::XCachedPrimitive> SAL_CALL + drawTextLayout( + const css::uno::Reference< css::rendering::XTextLayout >& xLayoutetText, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) override; + + virtual css::uno::Reference<css::rendering::XCachedPrimitive> SAL_CALL + drawBitmap( + const css::uno::Reference< css::rendering::XBitmap >& xBitmap, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) override; + + virtual css::uno::Reference<css::rendering::XCachedPrimitive> SAL_CALL + drawBitmapModulated( + const css::uno::Reference< css::rendering::XBitmap>& xBitmap, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) override; + + virtual css::uno::Reference<css::rendering::XGraphicDevice> SAL_CALL + getDevice() override; + + // XSpriteCanvas + + css::uno::Reference< css::rendering::XAnimatedSprite > SAL_CALL + createSpriteFromAnimation ( + const css::uno::Reference< css::rendering::XAnimation >& animation) override; + + css::uno::Reference< css::rendering::XAnimatedSprite > SAL_CALL + createSpriteFromBitmaps ( + const css::uno::Sequence< + css::uno::Reference< css::rendering::XBitmap > >& animationBitmaps, + ::sal_Int8 interpolationMode) override; + + css::uno::Reference< css::rendering::XCustomSprite > SAL_CALL + createCustomSprite ( + const css::geometry::RealSize2D& spriteSize) override; + + css::uno::Reference< css::rendering::XSprite > SAL_CALL + createClonedSprite ( + const css::uno::Reference< css::rendering::XSprite >& original) override; + + sal_Bool SAL_CALL updateScreen (sal_Bool bUpdateAll) override; + + // XEventListener + + virtual void SAL_CALL disposing (const css::lang::EventObject& rEvent) override; + + // XWindowListener + + virtual void SAL_CALL windowResized (const css::awt::WindowEvent& rEvent) override; + + virtual void SAL_CALL windowMoved (const css::awt::WindowEvent& rEvent) override; + + virtual void SAL_CALL windowShown (const css::lang::EventObject& rEvent) override; + + virtual void SAL_CALL windowHidden (const css::lang::EventObject& rEvent) override; + + // XBitmap + + virtual css::geometry::IntegerSize2D SAL_CALL getSize() override; + + virtual sal_Bool SAL_CALL hasAlpha() override; + + virtual css::uno::Reference<css::rendering::XBitmap> SAL_CALL getScaledBitmap( + const css::geometry::RealSize2D& rNewSize, + sal_Bool bFast) override; + +private: + css::uno::Reference<css::rendering::XSpriteCanvas> mxUpdateCanvas; + css::uno::Reference<css::awt::XWindow> mxUpdateWindow; + css::uno::Reference<css::rendering::XCanvas> mxSharedCanvas; + css::uno::Reference<css::awt::XWindow> mxSharedWindow; + + /** The window for which a canvas is emulated. + */ + css::uno::Reference<css::awt::XWindow> mxWindow; + + /** Offset of the emulated canvas with respect to the shared canvas. + */ + css::awt::Point maOffset; + + /** The UpdateRequester is used by updateScreen() to schedule + updateScreen() calls at the shared canvas. + */ + std::shared_ptr<CanvasUpdateRequester> m_pUpdateRequester; + + /** When this flag is true (it is set to true after every call to + updateScreen()) then the next call to MergeViewState updates the + maOffset member. A possible optimization would set this flag only + to true when one of the windows between mxWindow and mxSharedWindow + changes its position. + */ + bool mbOffsetUpdatePending; + + ::basegfx::B2DRectangle GetClipRectangle ( + const css::geometry::AffineMatrix2D& rViewTransform, + const css::awt::Point& rOffset); + + css::rendering::ViewState MergeViewState (const css::rendering::ViewState& rViewState); + + /** @throws css::lang::DisposedException when the object has already been + disposed. + */ + void ThrowIfDisposed(); +}; + +} // end of namespace ::sd::presenter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/presenter/PresenterHelper.cxx b/sd/source/ui/presenter/PresenterHelper.cxx new file mode 100644 index 000000000..a93113a75 --- /dev/null +++ b/sd/source/ui/presenter/PresenterHelper.cxx @@ -0,0 +1,466 @@ +/* -*- 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 <sal/config.h> + +#include <cstddef> + +#include "PresenterHelper.hxx" +#include "PresenterCanvas.hxx" +#include <cppcanvas/vclfactory.hxx> +#include <com/sun/star/awt/XWindowPeer.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <toolkit/helper/vclunohelper.hxx> +#include <vcl/window.hxx> +#include <vcl/wrkwin.hxx> + + +#include <bitmaps.hlst> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::presenter { + +//===== PresenterHelper ======================================================= + +PresenterHelper::PresenterHelper ( + const Reference<XComponentContext>& rxContext) + : mxComponentContext(rxContext) +{ +} + +PresenterHelper::~PresenterHelper() +{ +} + +//----- XInitialize ----------------------------------------------------------- + +void SAL_CALL PresenterHelper::initialize (const Sequence<Any>&) {} + +//----- XPaneHelper ---------------------------------------------------- + +Reference<awt::XWindow> SAL_CALL PresenterHelper::createWindow ( + const Reference<awt::XWindow>& rxParentWindow, + sal_Bool bCreateSystemChildWindow, + sal_Bool bInitiallyVisible, + sal_Bool bEnableChildTransparentMode, + sal_Bool bEnableParentClip) +{ + VclPtr<vcl::Window> pParentWindow(VCLUnoHelper::GetWindow(rxParentWindow)); + + // Create a new window. + VclPtr<vcl::Window> pWindow; + if (bCreateSystemChildWindow) + { + pWindow = VclPtr<WorkWindow>::Create(pParentWindow, WB_SYSTEMCHILDWINDOW); + } + else + { + pWindow = VclPtr<vcl::Window>::Create(pParentWindow); + } + Reference<awt::XWindow> xWindow (pWindow->GetComponentInterface(), UNO_QUERY); + + if (bEnableChildTransparentMode) + { + // Make the frame window transparent and make the parent able to + // draw behind it. + if (pParentWindow) + pParentWindow->EnableChildTransparentMode(); + } + + pWindow->Show(bInitiallyVisible); + + pWindow->SetMapMode(MapMode(MapUnit::MapPixel)); + pWindow->SetBackground(); + if ( ! bEnableParentClip) + { + pWindow->SetParentClipMode(ParentClipMode::NoClip); + pWindow->SetPaintTransparent(true); + } + else + { + pWindow->SetParentClipMode(ParentClipMode::Clip); + pWindow->SetPaintTransparent(false); + } + + return xWindow; +} + +Reference<rendering::XCanvas> SAL_CALL PresenterHelper::createSharedCanvas ( + const Reference<rendering::XSpriteCanvas>& rxUpdateCanvas, + const Reference<awt::XWindow>& rxUpdateWindow, + const Reference<rendering::XCanvas>& rxSharedCanvas, + const Reference<awt::XWindow>& rxSharedWindow, + const Reference<awt::XWindow>& rxWindow) +{ + if ( ! rxSharedCanvas.is() + || ! rxSharedWindow.is() + || ! rxWindow.is()) + { + throw RuntimeException("illegal argument", static_cast<XWeak*>(this)); + } + + if (rxWindow == rxSharedWindow) + return rxSharedCanvas; + else + return new PresenterCanvas( + rxUpdateCanvas, + rxUpdateWindow, + rxSharedCanvas, + rxSharedWindow, + rxWindow); +} + +Reference<rendering::XCanvas> SAL_CALL PresenterHelper::createCanvas ( + const Reference<awt::XWindow>& rxWindow, + sal_Int16, + const OUString& rsOptionalCanvasServiceName) +{ + // No shared window is given or an explicit canvas service name is + // specified. Create a new canvas. + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(rxWindow); + if (!pWindow) + throw RuntimeException(); + + Sequence<Any> aArg{ // common: first any is VCL pointer to window (for VCL canvas) + Any(reinterpret_cast<sal_Int64>(pWindow.get())), + Any(css::awt::Rectangle()), + Any(false), + Any(rxWindow) + }; + + Reference<lang::XMultiServiceFactory> xFactory ( + mxComponentContext->getServiceManager(), UNO_QUERY_THROW); + return Reference<rendering::XCanvas>( + xFactory->createInstanceWithArguments( + !rsOptionalCanvasServiceName.isEmpty() + ? rsOptionalCanvasServiceName + : OUString("com.sun.star.rendering.Canvas.VCL"), + aArg), + UNO_QUERY); +} + +void SAL_CALL PresenterHelper::toTop ( + const Reference<awt::XWindow>& rxWindow) +{ + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(rxWindow); + if (pWindow) + { + pWindow->ToTop(); + pWindow->SetZOrder(nullptr, ZOrderFlags::Last); + } +} + +namespace { + +struct IdMapEntry { + char const * sid; + rtl::OUStringConstExpr bmpid; +}; + +} + +Reference<rendering::XBitmap> SAL_CALL PresenterHelper::loadBitmap ( + const OUString& id, + const Reference<rendering::XCanvas>& rxCanvas) +{ + if ( ! rxCanvas.is()) + return nullptr; + + static IdMapEntry const map[] = { + { "bitmaps/Background.png", BMP_PRESENTERSCREEN_BACKGROUND }, + { "bitmaps/Animation.png", + BMP_PRESENTERSCREEN_ANIMATION }, + { "bitmaps/Transition.png", + BMP_PRESENTERSCREEN_TRANSITION }, + { "bitmaps/BorderActiveBottom.png", + BMP_PRESENTERSCREEN_BORDER_ACTIVE_BOTTOM }, + { "bitmaps/BorderActiveBottomCallout.png", + BMP_PRESENTERSCREEN_BORDER_ACTIVE_BOTTOM_CALLOUT }, + { "bitmaps/BorderActiveBottomLeft.png", + BMP_PRESENTERSCREEN_BORDER_ACTIVE_BOTTOM_LEFT }, + { "bitmaps/BorderActiveBottomRight.png", + BMP_PRESENTERSCREEN_BORDER_ACTIVE_BOTTOM_RIGHT }, + { "bitmaps/BorderActiveLeft.png", + BMP_PRESENTERSCREEN_BORDER_ACTIVE_LEFT }, + { "bitmaps/BorderActiveRight.png", + BMP_PRESENTERSCREEN_BORDER_ACTIVE_RIGHT }, + { "bitmaps/BorderActiveTop.png", + BMP_PRESENTERSCREEN_BORDER_ACTIVE_TOP }, + { "bitmaps/BorderActiveTopLeft.png", + BMP_PRESENTERSCREEN_BORDER_ACTIVE_TOP_LEFT }, + { "bitmaps/BorderActiveTopRight.png", + BMP_PRESENTERSCREEN_BORDER_ACTIVE_TOP_RIGHT }, + { "bitmaps/BorderBottom.png", BMP_PRESENTERSCREEN_BORDER_BOTTOM }, + { "bitmaps/BorderBottomLeft.png", + BMP_PRESENTERSCREEN_BORDER_BOTTOM_LEFT }, + { "bitmaps/BorderBottomRight.png", + BMP_PRESENTERSCREEN_BORDER_BOTTOM_RIGHT }, + { "bitmaps/BorderCurrentSlideBottom.png", + BMP_PRESENTERSCREEN_BORDER_CURRENT_SLIDE_BOTTOM }, + { "bitmaps/BorderCurrentSlideBottomLeft.png", + BMP_PRESENTERSCREEN_BORDER_CURRENT_SLIDE_BOTTOM_LEFT }, + { "bitmaps/BorderCurrentSlideBottomRight.png", + BMP_PRESENTERSCREEN_BORDER_CURRENT_SLIDE_BOTTOM_RIGHT }, + { "bitmaps/BorderCurrentSlideLeft.png", + BMP_PRESENTERSCREEN_BORDER_CURRENT_SLIDE_LEFT }, + { "bitmaps/BorderCurrentSlideRight.png", + BMP_PRESENTERSCREEN_BORDER_CURRENT_SLIDE_RIGHT }, + { "bitmaps/BorderCurrentSlideTop.png", + BMP_PRESENTERSCREEN_BORDER_CURRENT_SLIDE_TOP }, + { "bitmaps/BorderCurrentSlideTopLeft.png", + BMP_PRESENTERSCREEN_BORDER_CURRENT_SLIDE_TOP_LEFT }, + { "bitmaps/BorderCurrentSlideTopRight.png", + BMP_PRESENTERSCREEN_BORDER_CURRENT_SLIDE_TOP_RIGHT }, + { "bitmaps/BorderLeft.png", BMP_PRESENTERSCREEN_BORDER_LEFT }, + { "bitmaps/BorderRight.png", BMP_PRESENTERSCREEN_BORDER_RIGHT }, + { "bitmaps/BorderToolbarBottom.png", + BMP_PRESENTERSCREEN_BORDER_TOOLBAR_BOTTOM }, + { "bitmaps/BorderToolbarLeft.png", + BMP_PRESENTERSCREEN_BORDER_TOOLBAR_LEFT }, + { "bitmaps/BorderToolbarRight.png", + BMP_PRESENTERSCREEN_BORDER_TOOLBAR_RIGHT }, + { "bitmaps/BorderToolbarTop.png", + BMP_PRESENTERSCREEN_BORDER_TOOLBAR_TOP }, + { "bitmaps/BorderToolbarTopLeft.png", + BMP_PRESENTERSCREEN_BORDER_TOOLBAR_TOP_LEFT }, + { "bitmaps/BorderToolbarTopRight.png", + BMP_PRESENTERSCREEN_BORDER_TOOLBAR_TOP_RIGHT }, + { "bitmaps/BorderTop.png", BMP_PRESENTERSCREEN_BORDER_TOP }, + { "bitmaps/BorderTopLeft.png", BMP_PRESENTERSCREEN_BORDER_TOP_LEFT }, + { "bitmaps/BorderTopRight.png", BMP_PRESENTERSCREEN_BORDER_TOP_RIGHT }, + { "bitmaps/ButtonEffectNextDisabled.png", + BMP_PRESENTERSCREEN_BUTTON_EFFECT_NEXT_DISABLED }, + { "bitmaps/ButtonEffectNextMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_EFFECT_NEXT_MOUSE_OVER }, + { "bitmaps/ButtonEffectNextNormal.png", + BMP_PRESENTERSCREEN_BUTTON_EFFECT_NEXT_NORMAL }, + { "bitmaps/ButtonEffectNextSelected.png", + BMP_PRESENTERSCREEN_BUTTON_EFFECT_NEXT_SELECTED }, + { "bitmaps/ButtonFrameCenterMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_FRAME_CENTER_MOUSE_OVER }, + { "bitmaps/ButtonFrameCenterNormal.png", + BMP_PRESENTERSCREEN_BUTTON_FRAME_CENTER_NORMAL }, + { "bitmaps/ButtonFrameLeftMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_FRAME_LEFT_MOUSE_OVER }, + { "bitmaps/ButtonFrameLeftNormal.png", + BMP_PRESENTERSCREEN_BUTTON_FRAME_LEFT_NORMAL }, + { "bitmaps/ButtonFrameRightMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_FRAME_RIGHT_MOUSE_OVER }, + { "bitmaps/ButtonFrameRightNormal.png", + BMP_PRESENTERSCREEN_BUTTON_FRAME_RIGHT_NORMAL }, + { "bitmaps/ButtonHelpDisabled.png", + BMP_PRESENTERSCREEN_BUTTON_HELP_DISABLED }, + { "bitmaps/ButtonHelpMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_HELP_MOUSE_OVER }, + { "bitmaps/ButtonHelpNormal.png", + BMP_PRESENTERSCREEN_BUTTON_HELP_NORMAL }, + { "bitmaps/ButtonHelpSelected.png", + BMP_PRESENTERSCREEN_BUTTON_HELP_SELECTED }, + { "bitmaps/ButtonExitPresenterMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_EXIT_PRESENTER_MOUSE_OVER }, + { "bitmaps/ButtonExitPresenterNormal.png", + BMP_PRESENTERSCREEN_BUTTON_EXIT_PRESENTER_NORMAL }, + { "bitmaps/ButtonMinusDisabled.png", + BMP_PRESENTERSCREEN_BUTTON_MINUS_DISABLED }, + { "bitmaps/ButtonMinusMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_MINUS_MOUSE_OVER }, + { "bitmaps/ButtonMinusNormal.png", + BMP_PRESENTERSCREEN_BUTTON_MINUS_NORMAL }, + { "bitmaps/ButtonMinusSelected.png", + BMP_PRESENTERSCREEN_BUTTON_MINUS_SELECTED }, + { "bitmaps/ButtonNotesDisabled.png", + BMP_PRESENTERSCREEN_BUTTON_NOTES_DISABLED }, + { "bitmaps/ButtonNotesMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_NOTES_MOUSE_OVER }, + { "bitmaps/ButtonNotesNormal.png", + BMP_PRESENTERSCREEN_BUTTON_NOTES_NORMAL }, + { "bitmaps/ButtonNotesSelected.png", + BMP_PRESENTERSCREEN_BUTTON_NOTES_SELECTED }, + { "bitmaps/ButtonPlusDisabled.png", + BMP_PRESENTERSCREEN_BUTTON_PLUS_DISABLED }, + { "bitmaps/ButtonPlusMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_PLUS_MOUSE_OVER }, + { "bitmaps/ButtonPlusNormal.png", + BMP_PRESENTERSCREEN_BUTTON_PLUS_NORMAL }, + { "bitmaps/ButtonPlusSelected.png", + BMP_PRESENTERSCREEN_BUTTON_PLUS_SELECTED }, + { "bitmaps/ButtonSlideNextDisabled.png", + BMP_PRESENTERSCREEN_BUTTON_SLIDE_NEXT_DISABLED }, + { "bitmaps/ButtonSlideNextMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_SLIDE_NEXT_MOUSE_OVER }, + { "bitmaps/ButtonSlideNextNormal.png", + BMP_PRESENTERSCREEN_BUTTON_SLIDE_NEXT_NORMAL }, + { "bitmaps/ButtonSlidePreviousDisabled.png", + BMP_PRESENTERSCREEN_BUTTON_SLIDE_PREVIOUS_DISABLED }, + { "bitmaps/ButtonSlidePreviousMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_SLIDE_PREVIOUS_MOUSE_OVER }, + { "bitmaps/ButtonSlidePreviousNormal.png", + BMP_PRESENTERSCREEN_BUTTON_SLIDE_PREVIOUS_NORMAL }, + { "bitmaps/ButtonSlidePreviousSelected.png", + BMP_PRESENTERSCREEN_BUTTON_SLIDE_PREVIOUS_SELECTED }, + { "bitmaps/ButtonSlideSorterDisabled.png", + BMP_PRESENTERSCREEN_BUTTON_SLIDE_SORTER_DISABLED }, + { "bitmaps/ButtonSlideSorterMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_SLIDE_SORTER_MOUSE_OVER }, + { "bitmaps/ButtonSlideSorterNormal.png", + BMP_PRESENTERSCREEN_BUTTON_SLIDE_SORTER_NORMAL }, + { "bitmaps/ButtonSlideSorterSelected.png", + BMP_PRESENTERSCREEN_BUTTON_SLIDE_SORTER_SELECTED }, + { "bitmaps/ButtonSwitchMonitorMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_SWITCH_MONITOR_MOUSE_OVER }, + { "bitmaps/ButtonSwitchMonitorNormal.png", + BMP_PRESENTERSCREEN_BUTTON_SWITCH_MONITOR_NORMAL }, + { "bitmaps/ButtonRestartTimerMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_RESTART_TIMER_MOUSE_OVER }, + { "bitmaps/ButtonRestartTimerNormal.png", + BMP_PRESENTERSCREEN_BUTTON_RESTART_TIMER_NORMAL }, + { "bitmaps/ButtonPauseTimerMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_PAUSE_TIMER_MOUSE_OVER }, + { "bitmaps/ButtonPauseTimerNormal.png", + BMP_PRESENTERSCREEN_BUTTON_PAUSE_TIMER_NORMAL }, + { "bitmaps/ButtonResumeTimerMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_RESUME_TIMER_MOUSE_OVER }, + { "bitmaps/ButtonResumeTimerNormal.png", + BMP_PRESENTERSCREEN_BUTTON_RESUME_TIMER_NORMAL }, + { "bitmaps/LabelMouseOverCenter.png", + BMP_PRESENTERSCREEN_LABEL_MOUSE_OVER_CENTER }, + { "bitmaps/LabelMouseOverLeft.png", + BMP_PRESENTERSCREEN_LABEL_MOUSE_OVER_LEFT }, + { "bitmaps/LabelMouseOverRight.png", + BMP_PRESENTERSCREEN_LABEL_MOUSE_OVER_RIGHT }, + { "bitmaps/ScrollbarArrowDownDisabled.png", + BMP_PRESENTERSCREEN_SCROLLBAR_ARROW_DOWN_DISABLED }, + { "bitmaps/ScrollbarArrowDownMouseOver.png", + BMP_PRESENTERSCREEN_SCROLLBAR_ARROW_DOWN_MOUSE_OVER }, + { "bitmaps/ScrollbarArrowDownNormal.png", + BMP_PRESENTERSCREEN_SCROLLBAR_ARROW_DOWN_NORMAL }, + { "bitmaps/ScrollbarArrowDownSelected.png", + BMP_PRESENTERSCREEN_SCROLLBAR_ARROW_DOWN_SELECTED }, + { "bitmaps/ScrollbarArrowUpDisabled.png", + BMP_PRESENTERSCREEN_SCROLLBAR_ARROW_UP_DISABLED }, + { "bitmaps/ScrollbarArrowUpMouseOver.png", + BMP_PRESENTERSCREEN_SCROLLBAR_ARROW_UP_MOUSE_OVER }, + { "bitmaps/ScrollbarArrowUpNormal.png", + BMP_PRESENTERSCREEN_SCROLLBAR_ARROW_UP_NORMAL }, + { "bitmaps/ScrollbarArrowUpSelected.png", + BMP_PRESENTERSCREEN_SCROLLBAR_ARROW_UP_SELECTED }, + { "bitmaps/ScrollbarPagerMiddleMouseOver.png", + BMP_PRESENTERSCREEN_SCROLLBAR_PAGER_MIDDLE_MOUSE_OVER }, + { "bitmaps/ScrollbarPagerMiddleNormal.png", + BMP_PRESENTERSCREEN_SCROLLBAR_PAGER_MIDDLE_NORMAL }, + { "bitmaps/ScrollbarThumbBottomMouseOver.png", + BMP_PRESENTERSCREEN_SCROLLBAR_THUMB_BOTTOM_MOUSE_OVER }, + { "bitmaps/ScrollbarThumbBottomNormal.png", + BMP_PRESENTERSCREEN_SCROLLBAR_THUMB_BOTTOM_NORMAL }, + { "bitmaps/ScrollbarThumbMiddleMouseOver.png", + BMP_PRESENTERSCREEN_SCROLLBAR_THUMB_MIDDLE_MOUSE_OVER }, + { "bitmaps/ScrollbarThumbMiddleNormal.png", + BMP_PRESENTERSCREEN_SCROLLBAR_THUMB_MIDDLE_NORMAL }, + { "bitmaps/ScrollbarThumbTopMouseOver.png", + BMP_PRESENTERSCREEN_SCROLLBAR_THUMB_TOP_MOUSE_OVER }, + { "bitmaps/ScrollbarThumbTopNormal.png", + BMP_PRESENTERSCREEN_SCROLLBAR_THUMB_TOP_NORMAL }, + { "bitmaps/ViewBackground.png", BMP_PRESENTERSCREEN_VIEW_BACKGROUND }, + { "bitmaps/Separator.png", + BMP_PRESENTERSCREEN_SEPARATOR } + }; + OUString bmpid; + for (std::size_t i = 0; i != SAL_N_ELEMENTS(map); ++i) { + if (id.equalsAscii(map[i].sid)) { + bmpid = map[i].bmpid; + break; + } + } + if (bmpid.isEmpty()) { + return nullptr; + } + + ::osl::MutexGuard aGuard (::osl::Mutex::getGlobalMutex()); + + const cppcanvas::CanvasSharedPtr pCanvas ( + cppcanvas::VCLFactory::createCanvas(rxCanvas)); + + if (pCanvas) + { + BitmapEx aBitmapEx(bmpid); + cppcanvas::BitmapSharedPtr xBitmap( + cppcanvas::VCLFactory::createBitmap(pCanvas, + aBitmapEx)); + if (!xBitmap) + return nullptr; + return xBitmap->getUNOBitmap(); + } + + return nullptr; +} + +void SAL_CALL PresenterHelper::captureMouse ( + const Reference<awt::XWindow>& rxWindow) +{ + ::osl::MutexGuard aGuard (::osl::Mutex::getGlobalMutex()); + + // Capture the mouse (if not already done.) + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(rxWindow); + if (pWindow && ! pWindow->IsMouseCaptured()) + { + pWindow->CaptureMouse(); + } +} + +void SAL_CALL PresenterHelper::releaseMouse (const Reference<awt::XWindow>& rxWindow) +{ + ::osl::MutexGuard aGuard (::osl::Mutex::getGlobalMutex()); + + // Release the mouse (if not already done.) + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(rxWindow); + if (pWindow && pWindow->IsMouseCaptured()) + { + pWindow->ReleaseMouse(); + } +} + +awt::Rectangle PresenterHelper::getWindowExtentsRelative ( + const Reference<awt::XWindow>& rxChildWindow, + const Reference<awt::XWindow>& rxParentWindow) +{ + VclPtr<vcl::Window> pChildWindow = VCLUnoHelper::GetWindow(rxChildWindow); + VclPtr<vcl::Window> pParentWindow = VCLUnoHelper::GetWindow(rxParentWindow); + if (pChildWindow && pParentWindow) + { + ::tools::Rectangle aBox (pChildWindow->GetWindowExtentsRelative(pParentWindow)); + return awt::Rectangle(aBox.Left(),aBox.Top(),aBox.GetWidth(),aBox.GetHeight()); + } + else + return awt::Rectangle(); +} + +} // end of namespace ::sd::presenter + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_PresenterHelper_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new sd::presenter::PresenterHelper(context)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/presenter/PresenterHelper.hxx b/sd/source/ui/presenter/PresenterHelper.hxx new file mode 100644 index 000000000..cee7e39fb --- /dev/null +++ b/sd/source/ui/presenter/PresenterHelper.hxx @@ -0,0 +1,93 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/drawing/XPresenterHelper.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <comphelper/compbase.hxx> + +namespace com::sun::star::uno { class XComponentContext; } + +namespace sd::presenter { + +typedef comphelper::WeakComponentImplHelper< + css::lang::XInitialization, + css::drawing::XPresenterHelper +> PresenterHelperInterfaceBase; + +/** Implementation of the XPresenterHelper interface: functionality that can + not be implemented in an extension. +*/ +class PresenterHelper final + : public PresenterHelperInterfaceBase +{ +public: + explicit PresenterHelper (const css::uno::Reference<css::uno::XComponentContext>& rxContext); + virtual ~PresenterHelper() override; + PresenterHelper(const PresenterHelper&) = delete; + PresenterHelper& operator=(const PresenterHelper&) = delete; + + // XInitialize + + virtual void SAL_CALL initialize (const css::uno::Sequence<css::uno::Any>& rArguments) override; + + // XPresenterHelper + + virtual css::uno::Reference<css::awt::XWindow> SAL_CALL createWindow ( + const css::uno::Reference<css::awt::XWindow>& rxParentWindow, + sal_Bool bCreateSystemChildWindow, + sal_Bool bInitiallyVisible, + sal_Bool bEnableChildTransparentMode, + sal_Bool bEnableParentClip) override; + + virtual css::uno::Reference<css::rendering::XCanvas> SAL_CALL createSharedCanvas ( + const css::uno::Reference<css::rendering::XSpriteCanvas>& rxUpdateCanvas, + const css::uno::Reference<css::awt::XWindow>& rxUpdateWindow, + const css::uno::Reference<css::rendering::XCanvas>& rxSharedCanvas, + const css::uno::Reference<css::awt::XWindow>& rxSharedWindow, + const css::uno::Reference<css::awt::XWindow>& rxWindow) override; + + virtual css::uno::Reference<css::rendering::XCanvas> SAL_CALL createCanvas ( + const css::uno::Reference<css::awt::XWindow>& rxWindow, + sal_Int16 nRequestedCanvasFeatures, + const OUString& rsOptionalCanvasServiceName) override; + + virtual void SAL_CALL toTop ( + const css::uno::Reference<css::awt::XWindow>& rxWindow) override; + + virtual css::uno::Reference<css::rendering::XBitmap> SAL_CALL loadBitmap ( + const OUString& rsURL, + const css::uno::Reference<css::rendering::XCanvas>& rxCanvas) override; + + virtual void SAL_CALL captureMouse (const css::uno::Reference<css::awt::XWindow>& rxWindow) override; + + virtual void SAL_CALL releaseMouse (const css::uno::Reference<css::awt::XWindow>& rxWindow) override; + + virtual css::awt::Rectangle SAL_CALL getWindowExtentsRelative ( + const css::uno::Reference<css::awt::XWindow>& rxChildWindow, + const css::uno::Reference<css::awt::XWindow>& rxParentWindow) override; + +private: + css::uno::Reference<css::uno::XComponentContext> mxComponentContext; +}; + +} // end of namespace ::sd::presenter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/presenter/PresenterPreviewCache.cxx b/sd/source/ui/presenter/PresenterPreviewCache.cxx new file mode 100644 index 000000000..fd29cdbfa --- /dev/null +++ b/sd/source/ui/presenter/PresenterPreviewCache.cxx @@ -0,0 +1,360 @@ +/* -*- 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 "PresenterPreviewCache.hxx" + +#include <cache/SlsPageCache.hxx> +#include <cache/SlsCacheContext.hxx> +#include <vcl/bitmapex.hxx> +#include <sdpage.hxx> +#include <cppcanvas/vclfactory.hxx> +#include <com/sun/star/drawing/XDrawPage.hpp> +#include <osl/diagnose.h> + +namespace com::sun::star::uno { class XComponentContext; } + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::sd::slidesorter::cache; + +namespace sd::presenter { + +class PresenterPreviewCache::PresenterCacheContext : public CacheContext +{ +public: + PresenterCacheContext(); + + void SetDocumentSlides ( + const Reference<container::XIndexAccess>& rxSlides, + const Reference<XInterface>& rxDocument); + void SetVisibleSlideRange ( + const sal_Int32 nFirstVisibleSlideIndex, + const sal_Int32 nLastVisibleSlideIndex); + const SdrPage* GetPage (const sal_Int32 nSlideIndex) const; + void AddPreviewCreationNotifyListener (const Reference<drawing::XSlidePreviewCacheListener>& rxListener); + void RemovePreviewCreationNotifyListener (const Reference<drawing::XSlidePreviewCacheListener>& rxListener); + + // CacheContext + virtual void NotifyPreviewCreation (CacheKey aKey) override; + virtual bool IsIdle() override; + virtual bool IsVisible (CacheKey aKey) override; + virtual const SdrPage* GetPage (CacheKey aKey) override; + virtual std::shared_ptr<std::vector<CacheKey> > GetEntryList (bool bVisible) override; + virtual sal_Int32 GetPriority (CacheKey aKey) override; + virtual css::uno::Reference<css::uno::XInterface> GetModel() override; + +private: + Reference<container::XIndexAccess> mxSlides; + Reference<XInterface> mxDocument; + sal_Int32 mnFirstVisibleSlideIndex; + sal_Int32 mnLastVisibleSlideIndex; + typedef ::std::vector<css::uno::Reference<css::drawing::XSlidePreviewCacheListener> > ListenerContainer; + ListenerContainer maListeners; + + void CallListeners (const sal_Int32 nSlideIndex); +}; + +//===== PresenterPreviewCache ================================================= + +PresenterPreviewCache::PresenterPreviewCache () + : maPreviewSize(Size(200,200)), + mpCacheContext(std::make_shared<PresenterCacheContext>()), + mpCache(std::make_shared<PageCache>(maPreviewSize, Bitmap::HasFastScale(), mpCacheContext)) +{ +} + +PresenterPreviewCache::~PresenterPreviewCache() +{ +} + +//----- XInitialize ----------------------------------------------------------- + +void SAL_CALL PresenterPreviewCache::initialize (const Sequence<Any>& rArguments) +{ + if (rArguments.hasElements()) + throw RuntimeException(); +} + +//----- XSlidePreviewCache ---------------------------------------------------- + +void SAL_CALL PresenterPreviewCache::setDocumentSlides ( + const Reference<container::XIndexAccess>& rxSlides, + const Reference<XInterface>& rxDocument) +{ + ThrowIfDisposed(); + OSL_ASSERT(mpCacheContext != nullptr); + + mpCacheContext->SetDocumentSlides(rxSlides, rxDocument); +} + +void SAL_CALL PresenterPreviewCache::setVisibleRange ( + sal_Int32 nFirstVisibleSlideIndex, + sal_Int32 nLastVisibleSlideIndex) +{ + ThrowIfDisposed(); + OSL_ASSERT(mpCacheContext != nullptr); + + mpCacheContext->SetVisibleSlideRange (nFirstVisibleSlideIndex, nLastVisibleSlideIndex); +} + +void SAL_CALL PresenterPreviewCache::setPreviewSize ( + const css::geometry::IntegerSize2D& rSize) +{ + ThrowIfDisposed(); + OSL_ASSERT(mpCache != nullptr); + + maPreviewSize = Size(rSize.Width, rSize.Height); + mpCache->ChangeSize(maPreviewSize, Bitmap::HasFastScale()); +} + +Reference<rendering::XBitmap> SAL_CALL PresenterPreviewCache::getSlidePreview ( + sal_Int32 nSlideIndex, + const Reference<rendering::XCanvas>& rxCanvas) +{ + ThrowIfDisposed(); + OSL_ASSERT(mpCacheContext != nullptr); + + cppcanvas::CanvasSharedPtr pCanvas ( + cppcanvas::VCLFactory::createCanvas(rxCanvas)); + + const SdrPage* pPage = mpCacheContext->GetPage(nSlideIndex); + if (pPage == nullptr) + throw RuntimeException(); + + const BitmapEx aPreview (mpCache->GetPreviewBitmap(pPage,true)); + if (aPreview.IsEmpty()) + return nullptr; + else + return cppcanvas::VCLFactory::createBitmap( + pCanvas, + aPreview)->getUNOBitmap(); +} + +void SAL_CALL PresenterPreviewCache::addPreviewCreationNotifyListener ( + const Reference<drawing::XSlidePreviewCacheListener>& rxListener) +{ + if (m_bDisposed) + return; + if (rxListener.is()) + mpCacheContext->AddPreviewCreationNotifyListener(rxListener); +} + +void SAL_CALL PresenterPreviewCache::removePreviewCreationNotifyListener ( + const css::uno::Reference<css::drawing::XSlidePreviewCacheListener>& rxListener) +{ + ThrowIfDisposed(); + mpCacheContext->RemovePreviewCreationNotifyListener(rxListener); +} + +void SAL_CALL PresenterPreviewCache::pause() +{ + ThrowIfDisposed(); + OSL_ASSERT(mpCache != nullptr); + mpCache->Pause(); +} + +void SAL_CALL PresenterPreviewCache::resume() +{ + ThrowIfDisposed(); + OSL_ASSERT(mpCache != nullptr); + mpCache->Resume(); +} + +void PresenterPreviewCache::ThrowIfDisposed() +{ + if (m_bDisposed) + { + throw lang::DisposedException ("PresenterPreviewCache object has already been disposed", + static_cast<uno::XWeak*>(this)); + } +} + +//===== PresenterPreviewCache::PresenterCacheContext ========================== + +PresenterPreviewCache::PresenterCacheContext::PresenterCacheContext() + : mnFirstVisibleSlideIndex(-1), + mnLastVisibleSlideIndex(-1) +{ +} + +void PresenterPreviewCache::PresenterCacheContext::SetDocumentSlides ( + const Reference<container::XIndexAccess>& rxSlides, + const Reference<XInterface>& rxDocument) +{ + mxSlides = rxSlides; + mxDocument = rxDocument; + mnFirstVisibleSlideIndex = -1; + mnLastVisibleSlideIndex = -1; +} + +void PresenterPreviewCache::PresenterCacheContext::SetVisibleSlideRange ( + const sal_Int32 nFirstVisibleSlideIndex, + const sal_Int32 nLastVisibleSlideIndex) +{ + if (nFirstVisibleSlideIndex > nLastVisibleSlideIndex || nFirstVisibleSlideIndex<0) + { + mnFirstVisibleSlideIndex = -1; + mnLastVisibleSlideIndex = -1; + } + else + { + mnFirstVisibleSlideIndex = nFirstVisibleSlideIndex; + mnLastVisibleSlideIndex = nLastVisibleSlideIndex; + } + if (mxSlides.is() && mnLastVisibleSlideIndex >= mxSlides->getCount()) + mnLastVisibleSlideIndex = mxSlides->getCount() - 1; +} + +void PresenterPreviewCache::PresenterCacheContext::AddPreviewCreationNotifyListener ( + const Reference<drawing::XSlidePreviewCacheListener>& rxListener) +{ + maListeners.push_back(rxListener); +} + +void PresenterPreviewCache::PresenterCacheContext::RemovePreviewCreationNotifyListener ( + const Reference<drawing::XSlidePreviewCacheListener>& rxListener) +{ + auto iListener = std::find(maListeners.begin(), maListeners.end(), rxListener); + if (iListener != maListeners.end()) + maListeners.erase(iListener); +} + +//----- CacheContext ---------------------------------------------------------- + +void PresenterPreviewCache::PresenterCacheContext::NotifyPreviewCreation ( + CacheKey aKey) +{ + if ( ! mxSlides.is()) + return; + const sal_Int32 nCount(mxSlides->getCount()); + for (sal_Int32 nIndex=0; nIndex<nCount; ++nIndex) + if (aKey == GetPage(nIndex)) + CallListeners(nIndex); +} + +bool PresenterPreviewCache::PresenterCacheContext::IsIdle() +{ + return true; +} + +bool PresenterPreviewCache::PresenterCacheContext::IsVisible (CacheKey aKey) +{ + if (mnFirstVisibleSlideIndex < 0) + return false; + for (sal_Int32 nIndex=mnFirstVisibleSlideIndex; nIndex<=mnLastVisibleSlideIndex; ++nIndex) + { + const SdrPage* pPage = GetPage(nIndex); + if (pPage == aKey) + return true; + } + return false; +} + +const SdrPage* PresenterPreviewCache::PresenterCacheContext::GetPage (CacheKey aKey) +{ + return aKey; +} + +std::shared_ptr<std::vector<CacheKey> > + PresenterPreviewCache::PresenterCacheContext::GetEntryList (bool bVisible) +{ + auto pKeys = std::make_shared<std::vector<CacheKey>>(); + + if ( ! mxSlides.is()) + return pKeys; + + const sal_Int32 nFirstIndex (bVisible ? mnFirstVisibleSlideIndex : 0); + const sal_Int32 nLastIndex (bVisible ? mnLastVisibleSlideIndex : mxSlides->getCount()-1); + + if (nFirstIndex < 0) + return pKeys; + + for (sal_Int32 nIndex=nFirstIndex; nIndex<=nLastIndex; ++nIndex) + { + pKeys->push_back(GetPage(nIndex)); + } + + return pKeys; +} + +sal_Int32 PresenterPreviewCache::PresenterCacheContext::GetPriority (CacheKey aKey) +{ + if ( ! mxSlides.is()) + return 0; + + const sal_Int32 nCount (mxSlides->getCount()); + + for (sal_Int32 nIndex=mnFirstVisibleSlideIndex; nIndex<=mnLastVisibleSlideIndex; ++nIndex) + if (aKey == GetPage(nIndex)) + return -nCount-1+nIndex; + + for (sal_Int32 nIndex=0; nIndex<=nCount; ++nIndex) + if (aKey == GetPage(nIndex)) + return nIndex; + + return 0; +} + +Reference<XInterface> PresenterPreviewCache::PresenterCacheContext::GetModel() +{ + return mxDocument; +} + +const SdrPage* PresenterPreviewCache::PresenterCacheContext::GetPage ( + const sal_Int32 nSlideIndex) const +{ + if ( ! mxSlides.is()) + return nullptr; + if (nSlideIndex < 0 || nSlideIndex >= mxSlides->getCount()) + return nullptr; + + Reference<drawing::XDrawPage> xSlide (mxSlides->getByIndex(nSlideIndex), UNO_QUERY); + const SdPage* pPage = SdPage::getImplementation(xSlide); + return pPage; +} + +void PresenterPreviewCache::PresenterCacheContext::CallListeners ( + const sal_Int32 nIndex) +{ + ListenerContainer aListeners (maListeners); + for (const auto& rxListener : aListeners) + { + try + { + rxListener->notifyPreviewCreation(nIndex); + } + catch (lang::DisposedException&) + { + RemovePreviewCreationNotifyListener(rxListener); + } + } +} + +} // end of namespace ::sd::presenter + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_PresenterPreviewCache_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new sd::presenter::PresenterPreviewCache); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/presenter/PresenterPreviewCache.hxx b/sd/source/ui/presenter/PresenterPreviewCache.hxx new file mode 100644 index 000000000..4f8c52280 --- /dev/null +++ b/sd/source/ui/presenter/PresenterPreviewCache.hxx @@ -0,0 +1,97 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/drawing/XSlidePreviewCache.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <tools/gen.hxx> +#include <comphelper/compbase.hxx> +#include <memory> + +namespace sd::slidesorter::cache { class PageCache; } + +namespace sd::presenter { + +typedef comphelper::WeakComponentImplHelper< + css::lang::XInitialization, + css::drawing::XSlidePreviewCache +> PresenterPreviewCacheInterfaceBase; + +/** Uno API wrapper around the slide preview cache. +*/ +class PresenterPreviewCache final + : public PresenterPreviewCacheInterfaceBase +{ +public: + PresenterPreviewCache (); + virtual ~PresenterPreviewCache() override; + PresenterPreviewCache(const PresenterPreviewCache&) = delete; + PresenterPreviewCache& operator=(const PresenterPreviewCache&) = delete; + + // XInitialize + + /** Accepts no arguments. All values that are necessary to set up a + preview cache can be provided via methods. + */ + virtual void SAL_CALL initialize (const css::uno::Sequence<css::uno::Any>& rArguments) override; + + // XSlidePreviewCache + + virtual void SAL_CALL setDocumentSlides ( + const css::uno::Reference<css::container::XIndexAccess>& rxSlides, + const css::uno::Reference<css::uno::XInterface>& rxDocument) override; + + virtual void SAL_CALL setVisibleRange ( + sal_Int32 nFirstVisibleSlideIndex, + sal_Int32 nLastVisibleSlideIndex) override; + + virtual void SAL_CALL setPreviewSize ( + const css::geometry::IntegerSize2D& rSize) override; + + virtual css::uno::Reference<css::rendering::XBitmap> SAL_CALL + getSlidePreview ( + sal_Int32 nSlideIndex, + const css::uno::Reference<css::rendering::XCanvas>& rxCanvas) override; + + virtual void SAL_CALL addPreviewCreationNotifyListener ( + const css::uno::Reference<css::drawing::XSlidePreviewCacheListener>& rxListener) override; + + virtual void SAL_CALL removePreviewCreationNotifyListener ( + const css::uno::Reference<css::drawing::XSlidePreviewCacheListener>& rxListener) override; + + virtual void SAL_CALL pause() override; + + virtual void SAL_CALL resume() override; + +private: + class PresenterCacheContext; + Size maPreviewSize; + std::shared_ptr<PresenterCacheContext> mpCacheContext; + std::shared_ptr<sd::slidesorter::cache::PageCache> mpCache; + + /** @throws css::lang::DisposedException when the object has already been + disposed. + */ + void ThrowIfDisposed(); +}; + +} // end of namespace ::sd::presenter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/presenter/PresenterTextView.cxx b/sd/source/ui/presenter/PresenterTextView.cxx new file mode 100644 index 000000000..affa21b03 --- /dev/null +++ b/sd/source/ui/presenter/PresenterTextView.cxx @@ -0,0 +1,466 @@ +/* -*- 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 "PresenterTextView.hxx" + +#include <i18nlangtag/mslangid.hxx> +#include <cppcanvas/vclfactory.hxx> +#include <svl/itempool.hxx> +#include <unotools/lingucfg.hxx> +#include <editeng/colritem.hxx> +#include <editeng/editeng.hxx> +#include <editeng/editstat.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/fontitem.hxx> +#include <vcl/bitmapex.hxx> +#include <vcl/svapp.hxx> +#include <vcl/virdev.hxx> +#include <com/sun/star/awt/FontDescriptor.hpp> +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/rendering/XCanvas.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/i18n/ScriptType.hpp> +#include <o3tl/string_view.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; + +constexpr OUStringLiteral gsTextPropertyName(u"Text"); +constexpr OUStringLiteral gsBitmapPropertyName(u"Bitmap"); +constexpr OUStringLiteral gsSizePropertyName(u"Size"); +constexpr OUStringLiteral gsBackgroundColorPropertyName(u"BackgroundColor"); +constexpr OUStringLiteral gsTextColorPropertyName(u"TextColor"); +constexpr OUStringLiteral gsFontDescriptorPropertyName(u"FontDescriptor"); +constexpr OUStringLiteral gsTopPropertyName(u"Top"); +constexpr OUStringLiteral gsTopRelativePropertyName(u"RelativeTop"); +constexpr OUStringLiteral gsTotalHeightPropertyName(u"TotalHeight"); + +namespace sd::presenter { + +// PresenterTextView::Implementation +class PresenterTextView::Implementation +{ +public: + Implementation(); + ~Implementation(); + + void SetCanvas (const cppcanvas::CanvasSharedPtr& rCanvas); + void SetSize (const Size aSize); + void SetBackgroundColor (const Color aColor); + void SetTextColor (const Color aColor); + void SetFontDescriptor (const awt::FontDescriptor& rFontDescriptor); + sal_Int32 GetTop() const { return mnTop;} + void SetTop (const sal_Int32 nTop); + void SetText (const OUString& Text); + sal_Int32 ParseDistance (const OUString& rsDistance) const; + Reference<rendering::XBitmap> const & GetBitmap(); + sal_Int32 GetTotalHeight(); + +private: + Reference<rendering::XBitmap> mxBitmap; + cppcanvas::CanvasSharedPtr mpCanvas; + VclPtr<VirtualDevice> mpOutputDevice; + std::unique_ptr<EditEngine> mpEditEngine; + rtl::Reference<SfxItemPool> mpEditEngineItemPool; + Size maSize; + OUString msText; + sal_Int32 mnTop; + sal_Int32 mnTotalHeight; + + void CheckTop(); +}; + +// PresenterTextView +PresenterTextView::PresenterTextView () + : mpImplementation(new Implementation()) +{ +} + +PresenterTextView::~PresenterTextView() +{ +} + +void SAL_CALL PresenterTextView::disposing() +{ + mpImplementation.reset(); +} + +// XInitialization +void SAL_CALL PresenterTextView::initialize (const Sequence<Any>& rArguments) +{ + ThrowIfDisposed(); + + if (rArguments.getLength() != 1) + { + throw RuntimeException("PresenterTextView: invalid number of arguments", + static_cast<XWeak*>(this)); + } + + Reference<rendering::XCanvas> xCanvas (rArguments[0], UNO_QUERY_THROW); + mpImplementation->SetCanvas( + cppcanvas::VCLFactory::createCanvas(xCanvas)); +} + +Any PresenterTextView::GetPropertyValue (const OUString& rsPropertyName) +{ + ThrowIfDisposed(); + + if (rsPropertyName == gsBitmapPropertyName) + { + return Any(mpImplementation->GetBitmap()); + } + else if (rsPropertyName == gsTopPropertyName) + { + return Any(mpImplementation->GetTop()); + } + else if (rsPropertyName == gsTotalHeightPropertyName) + { + return Any(mpImplementation->GetTotalHeight()); + } + + return Any(); +} + +Any PresenterTextView::SetPropertyValue ( + const OUString& rsPropertyName, + const css::uno::Any& rValue) +{ + ThrowIfDisposed(); + + Any aOldValue; + if (rsPropertyName == gsTextPropertyName) + { + OUString sText; + if (rValue >>= sText) + mpImplementation->SetText(sText); + } + else if (rsPropertyName == gsSizePropertyName) + { + awt::Size aSize; + if (rValue >>= aSize) + mpImplementation->SetSize(Size(aSize.Width,aSize.Height)); + } + else if (rsPropertyName == gsBackgroundColorPropertyName) + { + ::Color aColor; + if (rValue >>= aColor) + mpImplementation->SetBackgroundColor(aColor); + } + else if (rsPropertyName == gsTextColorPropertyName) + { + ::Color aColor; + if (rValue >>= aColor) + mpImplementation->SetTextColor(aColor); + } + else if (rsPropertyName == gsFontDescriptorPropertyName) + { + awt::FontDescriptor aFontDescriptor; + if (rValue >>= aFontDescriptor) + mpImplementation->SetFontDescriptor(aFontDescriptor); + } + else if (rsPropertyName == gsTopPropertyName) + { + sal_Int32 nTop = 0; + if (rValue >>= nTop) + mpImplementation->SetTop(nTop); + } + else if (rsPropertyName == gsTopRelativePropertyName) + { + OUString sDistance; + if (rValue >>= sDistance) + mpImplementation->SetTop( + mpImplementation->GetTop() + + mpImplementation->ParseDistance(sDistance)); + } + return aOldValue; +} + +void PresenterTextView::ThrowIfDisposed() +{ + if (PresenterTextViewInterfaceBase::rBHelper.bDisposed + || PresenterTextViewInterfaceBase::rBHelper.bInDispose || mpImplementation == nullptr) + { + throw lang::DisposedException ("PresenterTextView object has already been disposed", + static_cast<uno::XWeak*>(this)); + } +} + +// PresenterTextView::Implementation +PresenterTextView::Implementation::Implementation() + : mpOutputDevice(VclPtr<VirtualDevice>::Create(*Application::GetDefaultDevice(), DeviceFormat::DEFAULT, DeviceFormat::DEFAULT)), + mpEditEngineItemPool(EditEngine::CreatePool()), + maSize(100,100), + mnTop(0), + mnTotalHeight(-1) +{ + mpOutputDevice->SetMapMode(MapMode(MapUnit::MapPixel)); + + // set fonts to be used + SvtLinguOptions aOpt; + SvtLinguConfig().GetOptions( aOpt ); + + struct FontDta { + LanguageType nFallbackLang; + LanguageType nLang; + DefaultFontType nFontType; + sal_uInt16 nFontInfoId; + } aTable[3] = + { + // info to get western font to be used + { LANGUAGE_ENGLISH_US, LANGUAGE_NONE, + DefaultFontType::SERIF, EE_CHAR_FONTINFO }, + // info to get CJK font to be used + { LANGUAGE_JAPANESE, LANGUAGE_NONE, + DefaultFontType::CJK_TEXT, EE_CHAR_FONTINFO_CJK }, + // info to get CTL font to be used + { LANGUAGE_ARABIC_SAUDI_ARABIA, LANGUAGE_NONE, + DefaultFontType::CTL_TEXT, EE_CHAR_FONTINFO_CTL } + }; + aTable[0].nLang = MsLangId::resolveSystemLanguageByScriptType(aOpt.nDefaultLanguage, css::i18n::ScriptType::LATIN); + aTable[1].nLang = MsLangId::resolveSystemLanguageByScriptType(aOpt.nDefaultLanguage_CJK, css::i18n::ScriptType::ASIAN); + aTable[2].nLang = MsLangId::resolveSystemLanguageByScriptType(aOpt.nDefaultLanguage_CTL, css::i18n::ScriptType::COMPLEX); + + for (const FontDta & rFntDta : aTable) + { + LanguageType nLang = (LANGUAGE_NONE == rFntDta.nLang) ? + rFntDta.nFallbackLang : rFntDta.nLang; + vcl::Font aFont = OutputDevice::GetDefaultFont( + rFntDta.nFontType, nLang, GetDefaultFontFlags::OnlyOne); + mpEditEngineItemPool->SetPoolDefaultItem( + SvxFontItem( + aFont.GetFamilyType(), + aFont.GetFamilyName(), + aFont.GetStyleName(), + aFont.GetPitch(), + aFont.GetCharSet(), + rFntDta.nFontInfoId)); + } + + mpEditEngine.reset( new EditEngine (mpEditEngineItemPool.get()) ); + + mpEditEngine->EnableUndo (true); + mpEditEngine->SetDefTab (sal_uInt16( + Application::GetDefaultDevice()->GetTextWidth("XXXX"))); + + mpEditEngine->SetControlWord( + EEControlBits(mpEditEngine->GetControlWord() | EEControlBits::AUTOINDENTING) & + EEControlBits(~EEControlBits::UNDOATTRIBS) & + EEControlBits(~EEControlBits::PASTESPECIAL) ); + + mpEditEngine->SetWordDelimiters (" .=+-*/(){}[];\""); + mpEditEngine->SetRefMapMode(MapMode(MapUnit::MapPixel)); + mpEditEngine->SetPaperSize (Size(800, 0)); + mpEditEngine->EraseVirtualDevice(); + mpEditEngine->ClearModifyFlag(); +} + +PresenterTextView::Implementation::~Implementation() +{ + mpEditEngine.reset(); + mpEditEngineItemPool.clear(); + mpOutputDevice.disposeAndClear(); +} + +void PresenterTextView::Implementation::SetCanvas (const cppcanvas::CanvasSharedPtr& rpCanvas) +{ + mpCanvas = rpCanvas; + mxBitmap = nullptr; +} + +void PresenterTextView::Implementation::SetSize (const Size aSize) +{ + DBG_ASSERT(mpEditEngine!=nullptr, "EditEngine missing"); + + maSize = aSize; + mpEditEngine->SetPaperSize(maSize); + mnTotalHeight = -1; + mxBitmap = nullptr; +} + +void PresenterTextView::Implementation::SetBackgroundColor (const Color aColor) +{ + mxBitmap = nullptr; + + DBG_ASSERT(mpEditEngine!=nullptr, "EditEngine missing"); + DBG_ASSERT(mpEditEngineItemPool!=nullptr, "EditEngineItemPool missing"); + mpEditEngine->SetBackgroundColor(aColor); + mpEditEngine->EnableAutoColor(false); + mpEditEngine->ForceAutoColor(false); +} + +void PresenterTextView::Implementation::SetTextColor (const Color aColor) +{ + mxBitmap = nullptr; + + DBG_ASSERT(mpEditEngineItemPool!=nullptr, "EditEngineItemPool missing"); + mpEditEngineItemPool->SetPoolDefaultItem(SvxColorItem(aColor, EE_CHAR_COLOR)); +} + +void PresenterTextView::Implementation::SetFontDescriptor ( + const awt::FontDescriptor& rFontDescriptor) +{ + mxBitmap = nullptr; + + DBG_ASSERT(mpEditEngineItemPool!=nullptr, "EditEngineItemPool missing"); + + const sal_Int32 nFontHeight = rFontDescriptor.Height; + + SvxFontHeightItem aFontHeight( + Application::GetDefaultDevice()->LogicToPixel( + Size(0, nFontHeight), MapMode (MapUnit::MapPoint)).Height(), + 100, + EE_CHAR_FONTHEIGHT); + mpEditEngineItemPool->SetPoolDefaultItem( aFontHeight); + aFontHeight.SetWhich (EE_CHAR_FONTHEIGHT_CJK); + mpEditEngineItemPool->SetPoolDefaultItem( aFontHeight); + aFontHeight.SetWhich (EE_CHAR_FONTHEIGHT_CTL); + mpEditEngineItemPool->SetPoolDefaultItem( aFontHeight); + + SvxFontItem aSvxFontItem (EE_CHAR_FONTINFO); + aSvxFontItem.SetFamilyName( rFontDescriptor.Name ); + mpEditEngineItemPool->SetPoolDefaultItem(aSvxFontItem); + + mnTotalHeight = -1; + mxBitmap = nullptr; + + CheckTop(); + mnTotalHeight = -1; +} + +void PresenterTextView::Implementation::SetTop (const sal_Int32 nTop) +{ + if (nTop == mnTop) + return; + + mnTop = nTop; + mxBitmap = nullptr; + CheckTop(); +} + +void PresenterTextView::Implementation::SetText (const OUString& rText) +{ + DBG_ASSERT(mpEditEngine!=nullptr, "EditEngine missing"); + msText = rText; + mpEditEngine->SetPaperSize(maSize); + mnTotalHeight = -1; + mxBitmap = nullptr; +} + +sal_Int32 PresenterTextView::Implementation::ParseDistance (const OUString& rsDistance) const +{ + DBG_ASSERT(mpEditEngine!=nullptr, "EditEngine missing"); + sal_Int32 nDistance (0); + if (rsDistance.endsWith("px")) + { + nDistance = o3tl::toInt32(rsDistance.subView(0,rsDistance.getLength()-2)); + } + else if (rsDistance.endsWith("l")) + { + const sal_Int32 nLines (o3tl::toInt32(rsDistance.subView(0,rsDistance.getLength()-1))); + // Take the height of the first line as the height of every line. + const sal_uInt32 nFirstLineHeight (mpEditEngine->GetLineHeight(0)); + nDistance = nFirstLineHeight * nLines; + } + + return nDistance; +} + +Reference<rendering::XBitmap> const & PresenterTextView::Implementation::GetBitmap() +{ + DBG_ASSERT(mpEditEngine!=nullptr, "EditEngine missing"); + + if ( ! mxBitmap.is()) + { + mpOutputDevice.disposeAndClear(); + mpOutputDevice = VclPtr<VirtualDevice>::Create(*Application::GetDefaultDevice(), + DeviceFormat::DEFAULT, DeviceFormat::DEFAULT); + mpOutputDevice->SetMapMode(MapMode(MapUnit::MapPixel)); + mpOutputDevice->SetOutputSizePixel(maSize); + mpOutputDevice->SetLineColor(); + mpOutputDevice->SetFillColor(); + mpOutputDevice->SetBackground(Wallpaper()); + mpOutputDevice->Erase(); + + MapMode aMapMode (mpOutputDevice->GetMapMode()); + aMapMode.SetOrigin(Point(0,0)); + mpOutputDevice->SetMapMode(aMapMode); + const ::tools::Rectangle aWindowBox (Point(0,0), maSize); + mpOutputDevice->DrawRect(aWindowBox); + + mpEditEngine->Clear(); + mpEditEngine->SetText(msText); + mpEditEngine->SetPaperSize(maSize); + + mpEditEngine->Draw(*mpOutputDevice, aWindowBox, Point(0,mnTop)); + + const BitmapEx aBitmap (mpOutputDevice->GetBitmapEx(Point(0,0), maSize)); + mxBitmap = cppcanvas::VCLFactory::createBitmap( + mpCanvas, + aBitmap + )->getUNOBitmap(); + } + return mxBitmap; +} + +sal_Int32 PresenterTextView::Implementation::GetTotalHeight() +{ + DBG_ASSERT(mpEditEngine!=nullptr, "EditEngine missing"); + + if (mnTotalHeight < 0) + { + if ( ! mxBitmap.is()) + GetBitmap(); + mnTotalHeight = mpEditEngine->GetTextHeight(); + } + return mnTotalHeight; +} + +void PresenterTextView::Implementation::CheckTop() +{ + DBG_ASSERT(mpEditEngine!=nullptr, "EditEngine missing"); + + if (mpEditEngine!=nullptr && mnTotalHeight < 0) + mnTotalHeight = mpEditEngine->GetTextHeight(); + if (mpEditEngine!=nullptr && mnTop >= mnTotalHeight) + mnTop = mnTotalHeight - mpEditEngine->GetLineHeight(0); + + if (mnTotalHeight < maSize.Height()) + mnTop = 0; + + if (mnTotalHeight - mnTop < maSize.Height()) + mnTop = mnTotalHeight - maSize.Height(); + + if (mnTop < 0) + mnTop = 0; +} + +} // end of namespace ::sd::presenter + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_PresenterTextView_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new sd::presenter::PresenterTextView); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/presenter/PresenterTextView.hxx b/sd/source/ui/presenter/PresenterTextView.hxx new file mode 100644 index 000000000..28b68aaa5 --- /dev/null +++ b/sd/source/ui/presenter/PresenterTextView.hxx @@ -0,0 +1,71 @@ +/* -*- 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 . + */ + +#pragma once + +#include <tools/PropertySet.hxx> +#include <com/sun/star/lang/XInitialization.hpp> +#include <cppuhelper/implbase.hxx> +#include <memory> + +namespace sd::presenter { + +typedef ::cppu::ImplInheritanceHelper < + tools::PropertySet, + css::lang::XInitialization +> PresenterTextViewInterfaceBase; + +/** Render text into bitmaps. An edit engine is used to render the text. + This service is used by the presenter screen to render the notes view. +*/ +class PresenterTextView + : public PresenterTextViewInterfaceBase +{ +public: + PresenterTextView (); + virtual ~PresenterTextView() override; + PresenterTextView(const PresenterTextView&) = delete; + PresenterTextView& operator=(const PresenterTextView&) = delete; + + // XInitialization + + virtual void SAL_CALL initialize (const css::uno::Sequence<css::uno::Any>& rArguments) override; + +protected: + virtual void SAL_CALL disposing() override; + + virtual css::uno::Any GetPropertyValue ( + const OUString& rsPropertyName) override; + virtual css::uno::Any SetPropertyValue ( + const OUString& rsPropertyName, + const css::uno::Any& rValue) override; + +private: + class Implementation; + std::unique_ptr<Implementation> mpImplementation; + + /** @throws css::lang::DisposedException when the object has already been + disposed. + */ + void ThrowIfDisposed(); +}; + +} // end of namespace ::sd::presenter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/presenter/SlideRenderer.cxx b/sd/source/ui/presenter/SlideRenderer.cxx new file mode 100644 index 000000000..1b57b195a --- /dev/null +++ b/sd/source/ui/presenter/SlideRenderer.cxx @@ -0,0 +1,201 @@ +/* -*- 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 "SlideRenderer.hxx" +#include <sdpage.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <vcl/svapp.hxx> +#include <cppcanvas/vclfactory.hxx> +#include <cppuhelper/supportsservice.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::presenter { + +//===== SlideRenderer ========================================================== + +SlideRenderer::SlideRenderer () +{ +} + +SlideRenderer::~SlideRenderer() +{ +} + +//----- XInitialization ------------------------------------------------------- + +void SAL_CALL SlideRenderer::initialize (const Sequence<Any>& rArguments) +{ + ThrowIfDisposed(); + + if (rArguments.hasElements()) + { + throw RuntimeException("SlideRenderer: invalid number of arguments", + static_cast<XWeak*>(this)); + } +} + +OUString SlideRenderer::getImplementationName() +{ + return "com.sun.star.comp.Draw.SlideRenderer"; +} + +sal_Bool SlideRenderer::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence<OUString> SlideRenderer::getSupportedServiceNames() +{ + return {"com.sun.star.drawing.SlideRenderer"}; +} + +//----- XSlideRenderer -------------------------------------------------------- + +Reference<awt::XBitmap> SlideRenderer::createPreview ( + const Reference<drawing::XDrawPage>& rxSlide, + const awt::Size& rMaximalSize, + sal_Int16 nSuperSampleFactor) +{ + ThrowIfDisposed(); + SolarMutexGuard aGuard; + + return VCLUnoHelper::CreateBitmap( + CreatePreview(rxSlide, rMaximalSize, nSuperSampleFactor)); +} + +Reference<rendering::XBitmap> SlideRenderer::createPreviewForCanvas ( + const Reference<drawing::XDrawPage>& rxSlide, + const awt::Size& rMaximalSize, + sal_Int16 nSuperSampleFactor, + const Reference<rendering::XCanvas>& rxCanvas) +{ + ThrowIfDisposed(); + SolarMutexGuard aGuard; + + cppcanvas::CanvasSharedPtr pCanvas ( + cppcanvas::VCLFactory::createCanvas(rxCanvas)); + if (pCanvas) + return cppcanvas::VCLFactory::createBitmap( + pCanvas, + CreatePreview(rxSlide, rMaximalSize, nSuperSampleFactor))->getUNOBitmap(); + else + return nullptr; +} + +awt::Size SAL_CALL SlideRenderer::calculatePreviewSize ( + double nSlideAspectRatio, + const awt::Size& rMaximalSize) +{ + if (rMaximalSize.Width <= 0 + || rMaximalSize.Height <= 0 + || nSlideAspectRatio <= 0) + { + return awt::Size(0,0); + } + + const double nWindowAspectRatio (double(rMaximalSize.Width) / double(rMaximalSize.Height)); + if (nSlideAspectRatio < nWindowAspectRatio) + return awt::Size( + sal::static_int_cast<sal_Int32>(rMaximalSize.Height * nSlideAspectRatio), + rMaximalSize.Height); + else + return awt::Size( + rMaximalSize.Width, + sal::static_int_cast<sal_Int32>(rMaximalSize.Width / nSlideAspectRatio)); +} + +BitmapEx SlideRenderer::CreatePreview ( + const Reference<drawing::XDrawPage>& rxSlide, + const awt::Size& rMaximalSize, + sal_Int16 nSuperSampleFactor) +{ + const SdPage* pPage = SdPage::getImplementation(rxSlide); + if (pPage == nullptr) + throw lang::IllegalArgumentException("SlideRenderer::createPreview() called with invalid slide", + static_cast<XWeak*>(this), + 0); + + // Determine the size of the current slide and its aspect ratio. + Size aPageSize = pPage->GetSize(); + if (aPageSize.Height() <= 0) + throw lang::IllegalArgumentException("SlideRenderer::createPreview() called with invalid size", + static_cast<XWeak*>(this), + 1); + + // Compare with the aspect ratio of the window (which rMaximalSize + // assumed to be) and calculate the size of the preview so that it + // a) will have the aspect ratio of the page and + // b) will be as large as possible. + awt::Size aPreviewSize (calculatePreviewSize( + double(aPageSize.Width()) / double(aPageSize.Height()), + rMaximalSize)); + if (aPreviewSize.Width <= 0 || aPreviewSize.Height <= 0) + return BitmapEx(); + + // Make sure that the super sample factor has a sane value. + sal_Int16 nFactor (nSuperSampleFactor); + if (nFactor < 1) + nFactor = 1; + else if (nFactor > 10) + nFactor = 10; + + // Create the preview. When the super sample factor n is greater than 1 + // then a preview is created in size (n*width, n*height) and then scaled + // down to (width, height). This is a poor mans antialiasing for the + // time being. When we have true antialiasing support this workaround + // can be removed. + const Image aPreview = maPreviewRenderer.RenderPage ( + pPage, + Size(aPreviewSize.Width*nFactor, aPreviewSize.Height*nFactor), + true); + if (nFactor == 1) + return aPreview.GetBitmapEx(); + else + { + BitmapEx aScaledPreview = aPreview.GetBitmapEx(); + aScaledPreview.Scale( + Size(aPreviewSize.Width,aPreviewSize.Height), + BmpScaleFlag::BestQuality); + return aScaledPreview; + } +} + +void SlideRenderer::ThrowIfDisposed() +{ + if (m_bDisposed) + { + throw lang::DisposedException ("SlideRenderer object has already been disposed", + static_cast<uno::XWeak*>(this)); + } +} + +} // end of namespace ::sd::presenter + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_SlideRenderer_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new sd::presenter::SlideRenderer); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/presenter/SlideRenderer.hxx b/sd/source/ui/presenter/SlideRenderer.hxx new file mode 100644 index 000000000..d39434421 --- /dev/null +++ b/sd/source/ui/presenter/SlideRenderer.hxx @@ -0,0 +1,94 @@ +/* -*- 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 . + */ + +#pragma once + +#include <PreviewRenderer.hxx> +#include <com/sun/star/drawing/XSlideRenderer.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> + +#include <comphelper/compbase.hxx> + +namespace com::sun::star::drawing { class XDrawPage; } + +namespace sd::presenter { + +typedef comphelper::WeakComponentImplHelper < + css::drawing::XSlideRenderer, + css::lang::XInitialization, + css::lang::XServiceInfo +> SlideRendererInterfaceBase; + +/** Render single slides into bitmaps. +*/ +class SlideRenderer final + : public SlideRendererInterfaceBase +{ +public: + SlideRenderer (); + virtual ~SlideRenderer() override; + SlideRenderer(const SlideRenderer&) = delete; + SlideRenderer& operator=(const SlideRenderer&) = delete; + + // XInitialization + + virtual void SAL_CALL initialize (const css::uno::Sequence<css::uno::Any>& rArguments) override; + + OUString SAL_CALL getImplementationName() override; + + sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override; + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + + // XSlideRenderer + + virtual css::uno::Reference<css::awt::XBitmap> SAL_CALL createPreview ( + const css::uno::Reference<css::drawing::XDrawPage>& rxSlide, + const css::awt::Size& rMaximumPreviewPixelSize, + sal_Int16 nSuperSampleFactor) override; + + virtual css::uno::Reference<css::rendering::XBitmap> SAL_CALL createPreviewForCanvas ( + const css::uno::Reference<css::drawing::XDrawPage>& rxSlide, + const css::awt::Size& rMaximumPreviewPixelSize, + sal_Int16 nSuperSampleFactor, + const css::uno::Reference<css::rendering::XCanvas>& rxCanvas) override; + + virtual css::awt::Size SAL_CALL calculatePreviewSize ( + double nSlideAspectRatio, + const css::awt::Size& rMaximumPreviewPixelSize) override; + +private: + PreviewRenderer maPreviewRenderer; + + /// @throws css::uno::RuntimeException + BitmapEx CreatePreview ( + const css::uno::Reference<css::drawing::XDrawPage>& rxSlide, + const css::awt::Size& rMaximumPreviewPixelSize, + sal_Int16 nSuperSampleFactor); + + /** @throws css::lang::DisposedException when the object has already been + disposed. + */ + void ThrowIfDisposed(); +}; + +} // end of namespace ::sd::presenter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/AvahiNetworkService.cxx b/sd/source/ui/remotecontrol/AvahiNetworkService.cxx new file mode 100644 index 000000000..7708e6eb7 --- /dev/null +++ b/sd/source/ui/remotecontrol/AvahiNetworkService.cxx @@ -0,0 +1,209 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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/. + */ + +#include <config_dbus.h> + +#include <iostream> +#include <limits> +#include <new> +#include <assert.h> + +#include <avahi-client/client.h> +#include <avahi-client/publish.h> + +#include <avahi-common/alternative.h> +#include <avahi-common/error.h> +#include <avahi-common/thread-watch.h> +#include <comphelper/random.hxx> + +#if ENABLE_DBUS +#include <dbus/dbus.h> +#endif + +#include <sal/log.hxx> + +#include "AvahiNetworkService.hxx" +#include "ZeroconfService.hxx" + +using namespace sd; + +static AvahiClient *client = nullptr; +static AvahiThreadedPoll *threaded_poll = nullptr; +static AvahiEntryGroup *group = nullptr; +static AvahiNetworkService *avahiService = nullptr; + +static bool create_services(AvahiClient *c); + +static void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) { + assert(g == group || group == nullptr); + group = g; + + /* Called whenever the entry group state changes */ + + switch (state) { + case AVAHI_ENTRY_GROUP_ESTABLISHED : + /* The entry group has been established successfully */ + SAL_INFO( "sdremote.wifi", "Service '" << avahiService->getName() << "' successfully established." ); + break; + + case AVAHI_ENTRY_GROUP_COLLISION : { + char *n; + + /* A service name collision with a remote service + * happened. Let's pick a new name */ + n = avahi_alternative_service_name(avahiService->getName().c_str()); + avahiService->setName(n); + + SAL_INFO( "sdremote.wifi", "Service name collision, renaming service to '" << avahiService->getName() << "'"); + + /* And recreate the services */ + create_services(avahi_entry_group_get_client(g)); + break; + } + + case AVAHI_ENTRY_GROUP_FAILURE : + + SAL_WARN("sdremote.wifi", "Entry group failure: " << avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g)))); + + /* Some kind of failure happened while we were registering our services */ + avahi_threaded_poll_quit(threaded_poll); + break; + + case AVAHI_ENTRY_GROUP_UNCOMMITED: + case AVAHI_ENTRY_GROUP_REGISTERING: + ; + } +} + +static bool create_services(AvahiClient *c) { + assert(c); + + /* If this is the first time we're called, let's create a new + * entry group if necessary */ + if(!client) + return false; + + if (!group) + if (!(group = avahi_entry_group_new(c, entry_group_callback, nullptr))) { + SAL_WARN("sdremote.wifi", "avahi_entry_group_new() failed: " << avahi_strerror(avahi_client_errno(c))); + avahiService->clear(); + return false; + } + + /* If the group is empty (either because it was just created, or + * because it was reset previously, add our entries. */ + + if (avahi_entry_group_is_empty(group)) { + SAL_INFO("sdremote.wifi", "Adding service '" << avahiService->getName() << "'"); + char r[128]; + int nRandom = comphelper::rng::uniform_int_distribution(0, std::numeric_limits<int>::max()); + snprintf(r, sizeof(r), "random=%i", nRandom); + int ret = avahi_entry_group_add_service( + group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, static_cast<AvahiPublishFlags>(0), + avahiService->getName().c_str(), kREG_TYPE, nullptr, nullptr, 1599, "local", r, nullptr + ); + if (ret < 0) { + + if (ret == AVAHI_ERR_COLLISION){ + /* A service name collision with a local service happened. Let's + * pick a new name */ + char *n = avahi_alternative_service_name(avahiService->getName().c_str()); + avahiService->setName(n); + + SAL_WARN("sdremote.wifi", "Service name collision, renaming service to '" << avahiService->getName() << "'"); + + avahi_entry_group_reset(group); + + return create_services(c); + } + + SAL_WARN("sdremote.wifi", "Failed to add _impressremote._tcp service: " << avahi_strerror(ret)); + avahiService->clear(); + return false; + } + + /* Tell the server to register the service */ + if ((ret = avahi_entry_group_commit(group)) < 0) { + SAL_WARN("sdremote.wifi", "Failed to commit entry group: " << avahi_strerror(ret)); + avahiService->clear(); + return false; + } + } + + return true; //Services we're already created +} + +static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) { + assert(c); + + /* Called whenever the client or server state changes */ + + switch (state) { + case AVAHI_CLIENT_S_RUNNING: + create_services(c); + break; + case AVAHI_CLIENT_FAILURE: + SAL_WARN("sdremote.wifi", "Client failure: " << avahi_strerror(avahi_client_errno(c))); + avahiService->clear(); + break; + case AVAHI_CLIENT_S_COLLISION: + case AVAHI_CLIENT_S_REGISTERING: + if (group) + avahi_entry_group_reset(group); + break; + case AVAHI_CLIENT_CONNECTING: + ; + } +} + +void AvahiNetworkService::setup() { +#if ENABLE_DBUS + // Sure, without ENABLE_DBUS it probably makes no sense to try to use this Avahi stuff either, + // but this is just a stop-gap measure to get this to even compile for now with the probably + // pointless combination of configurable options --enable-avahi --enable-dbus --disable-gui. + + // Avahi internally uses D-Bus, which requires the following in order to be + // thread-safe (and we potentially access D-Bus from different threads in + // different places of the code base): + if (!dbus_threads_init_default()) { + throw std::bad_alloc(); + } +#endif + + int error = 0; + avahiService = this; + if (!(threaded_poll = avahi_threaded_poll_new())) { + SAL_WARN("sdremote.wifi", "avahi_threaded_poll_new '" << avahiService->getName() << "' failed"); + return; + } + + if (!(client = avahi_client_new(avahi_threaded_poll_get(threaded_poll), static_cast<AvahiClientFlags>(0), client_callback, nullptr, &error))) { + SAL_WARN("sdremote.wifi", "avahi_client_new failed"); + return; + } + + if(!create_services(client)) + return; + + /* Finally, start the event loop thread */ + if (avahi_threaded_poll_start(threaded_poll) < 0) { + SAL_WARN("sdremote.wifi", "avahi_threaded_poll_start failed"); + return; + } +} + +void AvahiNetworkService::clear() { + /* Call this when the app shuts down */ + if(threaded_poll) + avahi_threaded_poll_stop(threaded_poll); + if(client) + avahi_client_free(client); + if(threaded_poll) + avahi_threaded_poll_free(threaded_poll); +} diff --git a/sd/source/ui/remotecontrol/AvahiNetworkService.hxx b/sd/source/ui/remotecontrol/AvahiNetworkService.hxx new file mode 100644 index 000000000..374a27a3a --- /dev/null +++ b/sd/source/ui/remotecontrol/AvahiNetworkService.hxx @@ -0,0 +1,25 @@ +/* -*- 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/. + */ +#pragma once + +#include <string> +#include "ZeroconfService.hxx" + +namespace sd { + + class AvahiNetworkService : public ZeroconfService + { + public: + AvahiNetworkService(const std::string& aname = "", unsigned int aport = 1599) + : ZeroconfService(aname, aport){} + + void clear() override; + void setup() override; + }; +} diff --git a/sd/source/ui/remotecontrol/BluetoothServer.cxx b/sd/source/ui/remotecontrol/BluetoothServer.cxx new file mode 100644 index 000000000..fc3eeff54 --- /dev/null +++ b/sd/source/ui/remotecontrol/BluetoothServer.cxx @@ -0,0 +1,1521 @@ +/* -*- 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/. + */ + +#include "BluetoothServer.hxx" + +#include <iostream> +#include <memory> +#include <new> +#include <string_view> + +#include <sal/log.hxx> + +#ifdef LINUX_BLUETOOTH + #include <glib.h> + #include <dbus/dbus.h> + #include <errno.h> + #include <fcntl.h> + #include <unistd.h> + #include <sys/socket.h> + #include <bluetooth/bluetooth.h> + #include <bluetooth/rfcomm.h> + #include "BluetoothServiceRecord.hxx" + #include "BufferedStreamSocket.hxx" +#endif + +#ifdef _WIN32 + // LO vs WinAPI conflict + #undef WB_LEFT + #undef WB_RIGHT + #include <winsock2.h> + #include <ws2bth.h> + #include "BufferedStreamSocket.hxx" +#endif + +#ifdef MACOSX + #include <iomanip> + #include <osl/conditn.hxx> + #include <premac.h> + #import <CoreFoundation/CoreFoundation.h> + #import <IOBluetooth/IOBluetoothUtilities.h> + #import <IOBluetooth/objc/IOBluetoothSDPUUID.h> + #import <IOBluetooth/objc/IOBluetoothSDPServiceRecord.h> + #include <postmac.h> + #import "OSXBluetooth.h" + #include "OSXBluetoothWrapper.hxx" +#endif + +#include "Communicator.hxx" + +using namespace sd; + +#ifdef LINUX_BLUETOOTH + +namespace { + +struct DBusObject { + OString maBusName; + OString maPath; + OString maInterface; + + DBusObject() { } + DBusObject( const char *pBusName, const char *pPath, const char *pInterface ) + : maBusName( pBusName ), maPath( pPath ), maInterface( pInterface ) { } + + DBusMessage *getMethodCall( const char *pName ) + { + return dbus_message_new_method_call( maBusName.getStr(), maPath.getStr(), + maInterface.getStr(), pName ); + } + std::unique_ptr<DBusObject> cloneForInterface( const char *pInterface ) + { + std::unique_ptr<DBusObject> pObject(new DBusObject()); + + pObject->maBusName = maBusName; + pObject->maPath = maPath; + pObject->maInterface = pInterface; + + return pObject; + } +}; + +} + +static std::unique_ptr<DBusObject> getBluez5Adapter(DBusConnection *pConnection); + +struct sd::BluetoothServer::Impl { + // the glib mainloop running in the thread + GMainContext *mpContext; + DBusConnection *mpConnection; + std::unique_ptr<DBusObject> mpService; + enum class BluezVersion { BLUEZ4, BLUEZ5, UNKNOWN }; + BluezVersion maBluezVersion; + + Impl() + : mpContext( g_main_context_new() ) + , mpConnection( nullptr ) + , maBluezVersion( BluezVersion::UNKNOWN ) + { } + + std::unique_ptr<DBusObject> getAdapter() + { + if (mpService) + { + return mpService->cloneForInterface( "org.bluez.Adapter" ); + } + else if (spServer->mpImpl->maBluezVersion == BluezVersion::BLUEZ5) + { + return getBluez5Adapter(mpConnection); + } + else + { + return nullptr; + } + } +}; + +static DBusConnection * +dbusConnectToNameOnBus() +{ + DBusError aError; + DBusConnection *pConnection; + + dbus_error_init( &aError ); + + pConnection = dbus_bus_get( DBUS_BUS_SYSTEM, &aError ); + if( !pConnection || dbus_error_is_set( &aError )) + { + SAL_WARN( "sdremote.bluetooth", "failed to get dbus system bus: " << aError.message ); + dbus_error_free( &aError ); + return nullptr; + } + + return pConnection; +} + +static DBusMessage * +sendUnrefAndWaitForReply( DBusConnection *pConnection, DBusMessage *pMsg ) +{ + DBusPendingCall *pPending = nullptr; + + if( !pMsg || !dbus_connection_send_with_reply( pConnection, pMsg, &pPending, + -1 /* default timeout */ ) ) + { + SAL_WARN( "sdremote.bluetooth", "Memory allocation failed on message send" ); + dbus_message_unref( pMsg ); + return nullptr; + } + dbus_connection_flush( pConnection ); + dbus_message_unref( pMsg ); + + dbus_pending_call_block( pPending ); // block for reply + + pMsg = dbus_pending_call_steal_reply( pPending ); + if( !pMsg ) + SAL_WARN( "sdremote.bluetooth", "no valid reply / timeout" ); + + dbus_pending_call_unref( pPending ); + return pMsg; +} + +static bool +isBluez5Available(DBusConnection *pConnection) +{ + DBusMessage *pMsg; + + // Simplest ways to check whether we have Bluez 5+ is to check + // that we can obtain adapters using the new interfaces. + // The first two error checks however don't tell us anything as they should + // succeed as long as dbus is working correctly. + pMsg = DBusObject( "org.bluez", "/", "org.freedesktop.DBus.ObjectManager" ).getMethodCall( "GetManagedObjects" ); + if (!pMsg) + { + SAL_INFO("sdremote.bluetooth", "No GetManagedObjects call created"); + return false; + } + + pMsg = sendUnrefAndWaitForReply( pConnection, pMsg ); + if (!pMsg) + { + SAL_INFO("sdremote.bluetooth", "No reply received"); + return false; + } + + // If dbus is working correctly and we aren't on bluez 5 this is where we + // should actually get the error. + if (dbus_message_get_error_name( pMsg )) + { + SAL_INFO( "sdremote.bluetooth", "GetManagedObjects call failed with \"" + << dbus_message_get_error_name( pMsg ) + << "\" -- we don't seem to have Bluez 5 available"); + return false; + } + SAL_INFO("sdremote.bluetooth", "GetManagedObjects call seems to have succeeded -- we must be on Bluez 5"); + dbus_message_unref(pMsg); + return true; +} + +static std::unique_ptr<DBusObject> +getBluez5Adapter(DBusConnection *pConnection) +{ + DBusMessage *pMsg; + // This returns a list of objects where we need to find the first + // org.bluez.Adapter1 . + pMsg = DBusObject( "org.bluez", "/", "org.freedesktop.DBus.ObjectManager" ).getMethodCall( "GetManagedObjects" ); + if (!pMsg) + return nullptr; + + const gchar* const pInterfaceType = "org.bluez.Adapter1"; + + pMsg = sendUnrefAndWaitForReply( pConnection, pMsg ); + + DBusMessageIter aObjectIterator; + if (pMsg && dbus_message_iter_init(pMsg, &aObjectIterator)) + { + if (DBUS_TYPE_ARRAY == dbus_message_iter_get_arg_type(&aObjectIterator)) + { + DBusMessageIter aObject; + dbus_message_iter_recurse(&aObjectIterator, &aObject); + do + { + if (DBUS_TYPE_DICT_ENTRY == dbus_message_iter_get_arg_type(&aObject)) + { + DBusMessageIter aContainerIter; + dbus_message_iter_recurse(&aObject, &aContainerIter); + char *pPath = nullptr; + do + { + if (DBUS_TYPE_OBJECT_PATH == dbus_message_iter_get_arg_type(&aContainerIter)) + { + dbus_message_iter_get_basic(&aContainerIter, &pPath); + SAL_INFO( "sdremote.bluetooth", "Something retrieved: '" + << pPath << "' '"); + } + else if (DBUS_TYPE_ARRAY == dbus_message_iter_get_arg_type(&aContainerIter)) + { + DBusMessageIter aInnerIter; + dbus_message_iter_recurse(&aContainerIter, &aInnerIter); + do + { + if (DBUS_TYPE_DICT_ENTRY == dbus_message_iter_get_arg_type(&aInnerIter)) + { + DBusMessageIter aInnerInnerIter; + dbus_message_iter_recurse(&aInnerIter, &aInnerInnerIter); + do + { + if (DBUS_TYPE_STRING == dbus_message_iter_get_arg_type(&aInnerInnerIter)) + { + char* pMessage; + + dbus_message_iter_get_basic(&aInnerInnerIter, &pMessage); + if (pMessage == std::string_view("org.bluez.Adapter1")) + { + dbus_message_unref(pMsg); + if (pPath) + { + return std::make_unique<DBusObject>( "org.bluez", pPath, pInterfaceType ); + } + assert(false); // We should already have pPath provided for us. + } + } + } + while (dbus_message_iter_next(&aInnerInnerIter)); + } + } + while (dbus_message_iter_next(&aInnerIter)); + } + } + while (dbus_message_iter_next(&aContainerIter)); + } + } + while (dbus_message_iter_next(&aObject)); + } + dbus_message_unref(pMsg); + } + + return nullptr; +} + +static DBusObject * +bluez4GetDefaultService( DBusConnection *pConnection ) +{ + DBusMessage *pMsg; + DBusMessageIter it; + const gchar* const pInterfaceType = "org.bluez.Service"; + + // org.bluez.manager only exists for bluez 4. + // getMethodCall should return NULL if there is any issue e.g. the + // if org.bluez.manager doesn't exist. + pMsg = DBusObject( "org.bluez", "/", "org.bluez.Manager" ).getMethodCall( "DefaultAdapter" ); + + if (!pMsg) + { + SAL_WARN("sdremote.bluetooth", "Couldn't retrieve DBusObject for DefaultAdapter"); + return nullptr; + } + + SAL_INFO("sdremote.bluetooth", "successfully retrieved org.bluez.Manager.DefaultAdapter, attempting to use."); + pMsg = sendUnrefAndWaitForReply( pConnection, pMsg ); + + if(!pMsg || !dbus_message_iter_init( pMsg, &it ) ) + { + return nullptr; + } + + // This works for Bluez 4 + if( DBUS_TYPE_OBJECT_PATH == dbus_message_iter_get_arg_type( &it ) ) + { + const char *pObjectPath = nullptr; + dbus_message_iter_get_basic( &it, &pObjectPath ); + SAL_INFO( "sdremote.bluetooth", "DefaultAdapter retrieved: '" + << pObjectPath << "' '" << pInterfaceType << "'" ); + dbus_message_unref( pMsg ); + return new DBusObject( "org.bluez", pObjectPath, pInterfaceType ); + } + // Some form of error, e.g. if we have bluez 5 we get a message that + // this method doesn't exist. + else if ( DBUS_TYPE_STRING == dbus_message_iter_get_arg_type( &it ) ) + { + const char *pMessage = nullptr; + dbus_message_iter_get_basic( &it, &pMessage ); + SAL_INFO( "sdremote.bluetooth", "Error message: '" + << pMessage << "' '" << pInterfaceType << "'" ); + } + else + { + SAL_INFO( "sdremote.bluetooth", "invalid type of reply to DefaultAdapter: '" + << static_cast<char>(dbus_message_iter_get_arg_type( &it )) << "'" ); + } + dbus_message_unref(pMsg); + return nullptr; +} + +static bool +bluez4RegisterServiceRecord( DBusConnection *pConnection, DBusObject *pAdapter, + const char *pServiceRecord ) +{ + DBusMessage *pMsg; + DBusMessageIter it; + + pMsg = pAdapter->getMethodCall( "AddRecord" ); + dbus_message_iter_init_append( pMsg, &it ); + dbus_message_iter_append_basic( &it, DBUS_TYPE_STRING, &pServiceRecord ); + + pMsg = sendUnrefAndWaitForReply( pConnection, pMsg ); + + if( !pMsg || !dbus_message_iter_init( pMsg, &it ) || + dbus_message_iter_get_arg_type( &it ) != DBUS_TYPE_UINT32 ) + { + SAL_WARN( "sdremote.bluetooth", "SDP registration failed" ); + return false; + } + + // We ignore the uint de-registration handle we get back: + // bluez will clean us up automatically on exit + + return true; +} + +static void +bluezCreateAttachListeningSocket( GMainContext *pContext, GPollFD *pSocketFD ) +{ + int nSocket; + + pSocketFD->fd = -1; + + if( ( nSocket = socket( AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM ) ) < 0 ) + { + SAL_WARN( "sdremote.bluetooth", "failed to open bluetooth socket with error " << nSocket ); + return; + } + + sockaddr_rc aAddr; + // Initialize whole structure. Mainly to appease valgrind, which + // doesn't know about the padding at the end of sockaddr_rc which + // it will dutifully check for definedness. But also the standard + // definition of BDADDR_ANY is unusable in C++ code, so just use + // memset to set aAddr.rc_bdaddr to 0. + memset( &aAddr, 0, sizeof( aAddr ) ); + aAddr.rc_family = AF_BLUETOOTH; + aAddr.rc_channel = 5; + + int a; + if ( ( a = bind( nSocket, reinterpret_cast<sockaddr*>(&aAddr), sizeof(aAddr) ) ) < 0 ) { + SAL_WARN( "sdremote.bluetooth", "bind failed with error" << a ); + close( nSocket ); + return; + } + + if ( ( a = listen( nSocket, 1 ) ) < 0 ) + { + SAL_WARN( "sdremote.bluetooth", "listen failed with error" << a ); + close( nSocket ); + return; + } + + // set non-blocking behaviour ... + if( fcntl( nSocket, F_SETFL, O_NONBLOCK) < 0 ) + { + close( nSocket ); + return; + } + + pSocketFD->fd = nSocket; + pSocketFD->events = G_IO_IN | G_IO_PRI; + pSocketFD->revents = 0; + + g_main_context_add_poll( pContext, pSocketFD, G_PRIORITY_DEFAULT ); +} + +static void +bluezDetachCloseSocket( GMainContext *pContext, GPollFD *pSocketFD ) +{ + if( pSocketFD->fd >= 0 ) + { + close( pSocketFD->fd ); + g_main_context_remove_poll( pContext, pSocketFD ); + pSocketFD->fd = -1; + } +} + +#endif // LINUX_BLUETOOTH + +#if defined(MACOSX) + +OSXBluetoothWrapper::OSXBluetoothWrapper( IOBluetoothRFCOMMChannel* channel ) : + mpChannel(channel), + mnMTU(0), + mHaveBytes(), + mMutex(), + mBuffer() +{ + // silly enough, can't write more than mnMTU bytes at once + mnMTU = [channel getMTU]; + + SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::OSXBluetoothWrapper(): mnMTU=" << mnMTU ); +} + +sal_Int32 OSXBluetoothWrapper::readLine( OString& aLine ) +{ + SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::readLine()" ); + + while( true ) + { + { + SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::readLine: entering mutex" ); + ::osl::MutexGuard aQueueGuard( mMutex ); + SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::readLine: entered mutex" ); + +#ifdef SAL_LOG_INFO + // We should have in the sal logging some standard way to + // output char buffers with non-printables escaped. + std::ostringstream s; + if (mBuffer.size() > 0) + { + for (unsigned char *p = reinterpret_cast<unsigned char *>(mBuffer.data()); p != reinterpret_cast<unsigned char *>(mBuffer.data()) + mBuffer.size(); p++) + { + if (*p == '\n') + s << "\\n"; + else if (*p < ' ' || *p >= 0x7F) + s << "\\0x" << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(*p) << std::setfill(' ') << std::setw(1) << std::dec; + else + s << *p; + } + } + SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::readLine mBuffer: \"" << s.str() << "\"" ); +#endif + + // got enough bytes to return a line? + std::vector<char>::iterator aIt; + if ( (aIt = find( mBuffer.begin(), mBuffer.end(), '\n' )) + != mBuffer.end() ) + { + sal_uInt64 aLocation = aIt - mBuffer.begin(); + + aLine = OString( &(*mBuffer.begin()), aLocation ); + + mBuffer.erase( mBuffer.begin(), aIt + 1 ); // Also delete the empty line + + // yeps + SAL_INFO( "sdremote.bluetooth", " returning, got \"" << OStringToOUString( aLine, RTL_TEXTENCODING_UTF8 ) << "\"" ); + return aLine.getLength() + 1; + } + + // nope - wait some more (after releasing the mutex) + SAL_INFO( "sdremote.bluetooth", " resetting mHaveBytes" ); + mHaveBytes.reset(); + SAL_INFO( "sdremote.bluetooth", " leaving mutex" ); + } + + SAL_INFO( "sdremote.bluetooth", " waiting for mHaveBytes" ); + mHaveBytes.wait(); + SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::readLine: got mHaveBytes" ); + } +} + +sal_Int32 OSXBluetoothWrapper::write( const void* pBuffer, sal_uInt32 n ) +{ + SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::write(" << pBuffer << ", " << n << ") mpChannel=" << mpChannel ); + + char const * ptr = static_cast<char const *>(pBuffer); + sal_uInt32 nBytesWritten = 0; + + if (mpChannel == nil) + return 0; + + while( nBytesWritten < n ) + { + int toWrite = n - nBytesWritten; + toWrite = toWrite <= mnMTU ? toWrite : mnMTU; + if ( [mpChannel writeSync:const_cast<char *>(ptr) length:toWrite] != kIOReturnSuccess ) + { + SAL_INFO( "sdremote.bluetooth", " [mpChannel writeSync:" << static_cast<void const *>(ptr) << " length:" << toWrite << "] returned error, total written " << nBytesWritten ); + return nBytesWritten; + } + ptr += toWrite; + nBytesWritten += toWrite; + } + SAL_INFO( "sdremote.bluetooth", " total written " << nBytesWritten ); + return nBytesWritten; +} + +void OSXBluetoothWrapper::appendData(void* pBuffer, size_t len) +{ + SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::appendData(" << pBuffer << ", " << len << ")" ); + + if( len ) + { + SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::appendData: entering mutex" ); + ::osl::MutexGuard aQueueGuard( mMutex ); + SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::appendData: entered mutex" ); + mBuffer.insert(mBuffer.begin()+mBuffer.size(), + static_cast<char*>(pBuffer), static_cast<char *>(pBuffer)+len); + SAL_INFO( "sdremote.bluetooth", " setting mHaveBytes" ); + mHaveBytes.set(); + SAL_INFO( "sdremote.bluetooth", " leaving mutex" ); + } +} + +void OSXBluetoothWrapper::channelClosed() +{ + SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::channelClosed()" ); + + mpChannel = nil; +} + +void incomingCallback( void *userRefCon, + IOBluetoothUserNotificationRef, + IOBluetoothObjectRef objectRef ) +{ + SAL_INFO( "sdremote.bluetooth", "incomingCallback()" ); + + BluetoothServer* pServer = static_cast<BluetoothServer*>(userRefCon); + + IOBluetoothRFCOMMChannel* channel = [IOBluetoothRFCOMMChannel withRFCOMMChannelRef:reinterpret_cast<IOBluetoothRFCOMMChannelRef>(objectRef)]; + + OSXBluetoothWrapper* socket = new OSXBluetoothWrapper( channel); + Communicator* pCommunicator = new Communicator( std::unique_ptr<IBluetoothSocket>(socket) ); + pServer->addCommunicator( pCommunicator ); + + ChannelDelegate* delegate = [[ChannelDelegate alloc] initWithCommunicatorAndSocket: pCommunicator socket: socket]; + [channel setDelegate: delegate]; + [delegate retain]; + + pCommunicator->launch(); +} + +void BluetoothServer::addCommunicator( Communicator* pCommunicator ) +{ + mpCommunicators->push_back( pCommunicator ); +} + +#endif // MACOSX + +#ifdef LINUX_BLUETOOTH + +extern "C" { + static gboolean ensureDiscoverable_cb(gpointer) + { + BluetoothServer::doEnsureDiscoverable(); + return FALSE; // remove source + } + static gboolean restoreDiscoverable_cb(gpointer) + { + BluetoothServer::doRestoreDiscoverable(); + return FALSE; // remove source + } +} + +/* + * Bluez 4 uses custom methods for setting properties, whereas Bluez 5+ + * implements properties using the generic "org.freedesktop.DBus.Properties" + * interface -- hence we have a specific Bluez 4 function to deal with the + * old style of reading properties. + */ +static bool +getBluez4BooleanProperty( DBusConnection *pConnection, DBusObject *pAdapter, + const char *pPropertyName, bool *pBoolean ) +{ + *pBoolean = false; + + if( !pAdapter ) + return false; + + DBusMessage *pMsg; + pMsg = sendUnrefAndWaitForReply( pConnection, + pAdapter->getMethodCall( "GetProperties" ) ); + + DBusMessageIter it; + if( !pMsg || !dbus_message_iter_init( pMsg, &it ) ) + { + SAL_WARN( "sdremote.bluetooth", "no valid reply / timeout" ); + return false; + } + + if( DBUS_TYPE_ARRAY != dbus_message_iter_get_arg_type( &it ) ) + { + SAL_WARN( "sdremote.bluetooth", "no valid reply / timeout" ); + return false; + } + + DBusMessageIter arrayIt; + dbus_message_iter_recurse( &it, &arrayIt ); + + while( dbus_message_iter_get_arg_type( &arrayIt ) == DBUS_TYPE_DICT_ENTRY ) + { + DBusMessageIter dictIt; + dbus_message_iter_recurse( &arrayIt, &dictIt ); + + const char *pName = nullptr; + if( dbus_message_iter_get_arg_type( &dictIt ) == DBUS_TYPE_STRING ) + { + dbus_message_iter_get_basic( &dictIt, &pName ); + if( pName != nullptr && !strcmp( pName, pPropertyName ) ) + { + SAL_INFO( "sdremote.bluetooth", "hit " << pPropertyName << " property" ); + dbus_message_iter_next( &dictIt ); + dbus_bool_t bBool = false; + + if( dbus_message_iter_get_arg_type( &dictIt ) == DBUS_TYPE_VARIANT ) + { + DBusMessageIter variantIt; + dbus_message_iter_recurse( &dictIt, &variantIt ); + + if( dbus_message_iter_get_arg_type( &variantIt ) == DBUS_TYPE_BOOLEAN ) + { + dbus_message_iter_get_basic( &variantIt, &bBool ); + SAL_INFO( "sdremote.bluetooth", "" << pPropertyName << " is " << bBool ); + *pBoolean = bBool; + return true; + } + else + SAL_WARN( "sdremote.bluetooth", "" << pPropertyName << " type " << + dbus_message_iter_get_arg_type( &variantIt ) ); + } + else + SAL_WARN( "sdremote.bluetooth", "variant type ? " << + dbus_message_iter_get_arg_type( &dictIt ) ); + } + else + { + const char *pStr = pName ? pName : "<null>"; + SAL_INFO( "sdremote.bluetooth", "property '" << pStr << "'" ); + } + } + else + SAL_WARN( "sdremote.bluetooth", "unexpected property key type " + << dbus_message_iter_get_arg_type( &dictIt ) ); + dbus_message_iter_next( &arrayIt ); + } + dbus_message_unref( pMsg ); + + return false; +} + +/* + * This gets an org.freedesktop.DBus.Properties boolean + * (as opposed to the old Bluez 4 custom properties methods as visible above). + */ +static bool +getDBusBooleanProperty( DBusConnection *pConnection, DBusObject *pAdapter, + const char *pPropertyName, bool *pBoolean ) +{ + assert( pAdapter ); + + *pBoolean = false; + bool bRet = false; + + std::unique_ptr< DBusObject > pProperties ( + pAdapter->cloneForInterface( "org.freedesktop.DBus.Properties" ) ); + + DBusMessage *pMsg = pProperties->getMethodCall( "Get" ); + + DBusMessageIter itIn; + dbus_message_iter_init_append( pMsg, &itIn ); + const char* pInterface = "org.bluez.Adapter1"; + dbus_message_iter_append_basic( &itIn, DBUS_TYPE_STRING, &pInterface ); + dbus_message_iter_append_basic( &itIn, DBUS_TYPE_STRING, &pPropertyName ); + pMsg = sendUnrefAndWaitForReply( pConnection, pMsg ); + + DBusMessageIter it; + if( !pMsg || !dbus_message_iter_init( pMsg, &it ) ) + { + SAL_WARN( "sdremote.bluetooth", "no valid reply / timeout" ); + return false; + } + + if( DBUS_TYPE_VARIANT != dbus_message_iter_get_arg_type( &it ) ) + { + SAL_WARN( "sdremote.bluetooth", "invalid return type" ); + } + else + { + DBusMessageIter variantIt; + dbus_message_iter_recurse( &it, &variantIt ); + + if( dbus_message_iter_get_arg_type( &variantIt ) == DBUS_TYPE_BOOLEAN ) + { + dbus_bool_t bBool = false; + dbus_message_iter_get_basic( &variantIt, &bBool ); + SAL_INFO( "sdremote.bluetooth", "" << pPropertyName << " is " << bBool ); + *pBoolean = bBool; + bRet = true; + } + else + { + SAL_WARN( "sdremote.bluetooth", "" << pPropertyName << " type " << + dbus_message_iter_get_arg_type( &variantIt ) ); + } + + const char* pError = dbus_message_get_error_name( pMsg ); + if ( pError ) + { + SAL_WARN( "sdremote.bluetooth", + "Get failed for " << pPropertyName << " on " << + pAdapter->maPath << " with error: " << pError ); + } + } + dbus_message_unref( pMsg ); + + return bRet; +} + +static void +setDBusBooleanProperty( DBusConnection *pConnection, DBusObject *pAdapter, + const char *pPropertyName, bool bBoolean ) +{ + assert( pAdapter ); + + std::unique_ptr< DBusObject > pProperties( + pAdapter->cloneForInterface( "org.freedesktop.DBus.Properties" ) ); + + DBusMessage *pMsg = pProperties->getMethodCall( "Set" ); + + DBusMessageIter itIn; + dbus_message_iter_init_append( pMsg, &itIn ); + const char* pInterface = "org.bluez.Adapter1"; + dbus_message_iter_append_basic( &itIn, DBUS_TYPE_STRING, &pInterface ); + dbus_message_iter_append_basic( &itIn, DBUS_TYPE_STRING, &pPropertyName ); + + { + DBusMessageIter varIt; + dbus_message_iter_open_container( &itIn, DBUS_TYPE_VARIANT, + DBUS_TYPE_BOOLEAN_AS_STRING, &varIt ); + dbus_bool_t bDBusBoolean = bBoolean; + dbus_message_iter_append_basic( &varIt, DBUS_TYPE_BOOLEAN, &bDBusBoolean ); + dbus_message_iter_close_container( &itIn, &varIt ); + } + + pMsg = sendUnrefAndWaitForReply( pConnection, pMsg ); + + if( !pMsg ) + { + SAL_WARN( "sdremote.bluetooth", "no valid reply / timeout" ); + } + else + { + const char* pError = dbus_message_get_error_name( pMsg ); + if ( pError ) + { + SAL_WARN( "sdremote.bluetooth", + "Set failed for " << pPropertyName << " on " << + pAdapter->maPath << " with error: " << pError ); + } + dbus_message_unref( pMsg ); + } +} + +static bool +getDiscoverable( DBusConnection *pConnection, DBusObject *pAdapter ) +{ + if (pAdapter->maInterface == "org.bluez.Adapter") // Bluez 4 + { + bool bDiscoverable; + if( getBluez4BooleanProperty(pConnection, pAdapter, "Discoverable", &bDiscoverable ) ) + return bDiscoverable; + } + else if (pAdapter->maInterface == "org.bluez.Adapter1") // Bluez 5 + { + bool bDiscoverable; + if ( getDBusBooleanProperty(pConnection, pAdapter, "Discoverable", &bDiscoverable ) ) + return bDiscoverable; + } + return false; +} + +static void +setDiscoverable( DBusConnection *pConnection, DBusObject *pAdapter, bool bDiscoverable ) +{ + SAL_INFO( "sdremote.bluetooth", "setDiscoverable to " << bDiscoverable ); + + if (pAdapter->maInterface == "org.bluez.Adapter") // Bluez 4 + { + bool bPowered = false; + if( !getBluez4BooleanProperty( pConnection, pAdapter, "Powered", &bPowered ) || !bPowered ) + return; // nothing to do + + DBusMessage *pMsg; + DBusMessageIter it, varIt; + + // set timeout to zero + pMsg = pAdapter->getMethodCall( "SetProperty" ); + dbus_message_iter_init_append( pMsg, &it ); + const char *pTimeoutStr = "DiscoverableTimeout"; + dbus_message_iter_append_basic( &it, DBUS_TYPE_STRING, &pTimeoutStr ); + dbus_message_iter_open_container( &it, DBUS_TYPE_VARIANT, + DBUS_TYPE_UINT32_AS_STRING, &varIt ); + dbus_uint32_t nTimeout = 0; + dbus_message_iter_append_basic( &varIt, DBUS_TYPE_UINT32, &nTimeout ); + dbus_message_iter_close_container( &it, &varIt ); + dbus_connection_send( pConnection, pMsg, nullptr ); // async send - why not ? + dbus_message_unref( pMsg ); + + // set discoverable value + pMsg = pAdapter->getMethodCall( "SetProperty" ); + dbus_message_iter_init_append( pMsg, &it ); + const char *pDiscoverableStr = "Discoverable"; + dbus_message_iter_append_basic( &it, DBUS_TYPE_STRING, &pDiscoverableStr ); + dbus_message_iter_open_container( &it, DBUS_TYPE_VARIANT, + DBUS_TYPE_BOOLEAN_AS_STRING, &varIt ); + dbus_bool_t bValue = bDiscoverable; + dbus_message_iter_append_basic( &varIt, DBUS_TYPE_BOOLEAN, &bValue ); + dbus_message_iter_close_container( &it, &varIt ); // async send - why not ? + dbus_connection_send( pConnection, pMsg, nullptr ); + dbus_message_unref( pMsg ); + } + else if (pAdapter->maInterface == "org.bluez.Adapter1") // Bluez 5 + { + setDBusBooleanProperty(pConnection, pAdapter, "Discoverable", bDiscoverable ); + } +} + +static std::unique_ptr<DBusObject> +registerWithDefaultAdapter( DBusConnection *pConnection ) +{ + std::unique_ptr<DBusObject> pService(bluez4GetDefaultService( pConnection )); + if( pService ) + { + if( !bluez4RegisterServiceRecord( pConnection, pService.get(), + bluetooth_service_record ) ) + { + return nullptr; + } + } + + return pService; +} + +static void ProfileUnregisterFunction +(DBusConnection *, void *) +{ + // We specifically don't need to do anything here. +} + +static DBusHandlerResult ProfileMessageFunction +(DBusConnection *pConnection, DBusMessage *pMessage, void *user_data) +{ + SAL_INFO("sdremote.bluetooth", "ProfileMessageFunction||" << dbus_message_get_interface(pMessage) << "||" << dbus_message_get_member(pMessage)); + + if (dbus_message_get_interface(pMessage) == std::string_view("org.bluez.Profile1")) + { + if (dbus_message_get_member(pMessage) == std::string_view("Release")) + { + return DBUS_HANDLER_RESULT_HANDLED; + } + else if (dbus_message_get_member(pMessage) == std::string_view("NewConnection")) + { + if (!dbus_message_has_signature(pMessage, "oha{sv}")) + { + SAL_WARN("sdremote.bluetooth", "wrong signature for NewConnection"); + } + + DBusMessageIter it; + if (!dbus_message_iter_init(pMessage, &it)) + SAL_WARN( "sdremote.bluetooth", "error init dbus" ); + else + { + char* pPath; + dbus_message_iter_get_basic(&it, &pPath); + SAL_INFO("sdremote.bluetooth", "Adapter path:" << pPath); + + if (!dbus_message_iter_next(&it)) + SAL_WARN("sdremote.bluetooth", "not enough parameters passed"); + + // DBUS_TYPE_UNIX_FD == 'h' -- doesn't exist in older versions + // of dbus (< 1.3?) hence defined manually for now + if ('h' == dbus_message_iter_get_arg_type(&it)) + { + + int nDescriptor; + dbus_message_iter_get_basic(&it, &nDescriptor); + std::vector<Communicator*>* pCommunicators = static_cast<std::vector<Communicator*>*>(user_data); + + // Bluez gives us non-blocking sockets, but our code relies + // on blocking behaviour. + (void)fcntl(nDescriptor, F_SETFL, fcntl(nDescriptor, F_GETFL) & ~O_NONBLOCK); + + SAL_INFO( "sdremote.bluetooth", "connection accepted " << nDescriptor); + Communicator* pCommunicator = new Communicator( std::make_unique<BufferedStreamSocket>( nDescriptor ) ); + pCommunicators->push_back( pCommunicator ); + pCommunicator->launch(); + } + + // For some reason an (empty?) reply is expected. + DBusMessage* pRet = dbus_message_new_method_return(pMessage); + dbus_connection_send(pConnection, pRet, nullptr); + dbus_message_unref(pRet); + + // We could read the remote profile version and features here + // (i.e. they are provided as part of the DBusMessage), + // however for us they are irrelevant (as our protocol handles + // equivalent functionality independently of whether we're on + // bluetooth or normal network connection). + return DBUS_HANDLER_RESULT_HANDLED; + } + } + else if (dbus_message_get_member(pMessage) == std::string_view("RequestDisconnection")) + { + return DBUS_HANDLER_RESULT_HANDLED; + } + } + SAL_WARN("sdremote.bluetooth", "Couldn't handle message correctly."); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + +} + +static void +setupBluez5Profile1(DBusConnection* pConnection, std::vector<Communicator*>* pCommunicators) +{ + bool bErr; + + SAL_INFO("sdremote.bluetooth", "Attempting to register our org.bluez.Profile1"); + static DBusObjectPathVTable aVTable; + aVTable.unregister_function = ProfileUnregisterFunction; + aVTable.message_function = ProfileMessageFunction; + + // dbus_connection_try_register_object_path could be used but only exists for + // dbus >= 1.2 -- we really shouldn't be trying this twice in any case. + // (dbus_connection_try_register_object_path also returns an error with more + // information which could be useful for debugging purposes.) + bErr = !dbus_connection_register_object_path(pConnection, "/org/libreoffice/bluez/profile1", &aVTable, pCommunicators); + + if (bErr) + { + SAL_WARN("sdremote.bluetooth", "Failed to register Bluez 5 Profile1 callback, bluetooth won't work."); + } + + dbus_connection_flush( pConnection ); +} + +static void +unregisterBluez5Profile(DBusConnection* pConnection) +{ + DBusMessage* pMsg = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.ProfileManager1", "UnregisterProfile"); + DBusMessageIter it; + dbus_message_iter_init_append(pMsg, &it); + + const char *pPath = "/org/libreoffice/bluez/profile1"; + dbus_message_iter_append_basic(&it, DBUS_TYPE_OBJECT_PATH, &pPath); + + pMsg = sendUnrefAndWaitForReply( pConnection, pMsg ); + + if (pMsg) + dbus_message_unref(pMsg); + + dbus_connection_unregister_object_path( pConnection, "/org/libreoffice/bluez/profile1"); + + dbus_connection_flush(pConnection); +} + +static bool +registerBluez5Profile(DBusConnection* pConnection, std::vector<Communicator*>* pCommunicators) +{ + setupBluez5Profile1(pConnection, pCommunicators); + + DBusMessage *pMsg; + DBusMessageIter it; + + pMsg = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.ProfileManager1", "RegisterProfile"); + dbus_message_iter_init_append(pMsg, &it); + + const char *pPath = "/org/libreoffice/bluez/profile1"; + dbus_message_iter_append_basic(&it, DBUS_TYPE_OBJECT_PATH, &pPath); + const char *pUUID = "spp"; // Bluez translates this to 0x1101 for spp + dbus_message_iter_append_basic(&it, DBUS_TYPE_STRING, &pUUID); + + DBusMessageIter aOptionsIter; + dbus_message_iter_open_container(&it, DBUS_TYPE_ARRAY, "{sv}", &aOptionsIter); + + DBusMessageIter aEntry; + + { + dbus_message_iter_open_container(&aOptionsIter, DBUS_TYPE_DICT_ENTRY, nullptr, &aEntry); + + const char *pString = "Name"; + dbus_message_iter_append_basic(&aEntry, DBUS_TYPE_STRING, &pString); + + const char *pValue = "LibreOffice Impress Remote"; + DBusMessageIter aValue; + dbus_message_iter_open_container(&aEntry, DBUS_TYPE_VARIANT, "s", &aValue); + dbus_message_iter_append_basic(&aValue, DBUS_TYPE_STRING, &pValue); + dbus_message_iter_close_container(&aEntry, &aValue); + dbus_message_iter_close_container(&aOptionsIter, &aEntry); + } + + dbus_message_iter_close_container(&it, &aOptionsIter); + + // Other properties that we could set (but don't, since they appear + // to be useless for us): + // "Service": "0x1101" (not needed, but we used to have it in the manually defined profile). + // "Role": setting this to "server" breaks things, although we think we're a server? + // "Channel": seems to be dealt with automatically (but we used to use 5 in the manual profile). + + bool bSuccess = true; + + pMsg = sendUnrefAndWaitForReply( pConnection, pMsg ); + + DBusError aError; + dbus_error_init(&aError); + if (pMsg && dbus_set_error_from_message( &aError, pMsg )) + { + bSuccess = false; + SAL_WARN("sdremote.bluetooth", + "Failed to register our Profile1 with bluez ProfileManager " + << (aError.message ? aError.message : "<null>")); + } + + dbus_error_free(&aError); + if (pMsg) + dbus_message_unref(pMsg); + + dbus_connection_flush(pConnection); + + return bSuccess; +} + +#endif // LINUX_BLUETOOTH + +BluetoothServer::BluetoothServer( std::vector<Communicator*>* pCommunicators ) + : meWasDiscoverable( UNKNOWN ), + mpCommunicators( pCommunicators ) +{ +#ifdef LINUX_BLUETOOTH + // D-Bus requires the following in order to be thread-safe (and we + // potentially access D-Bus from different threads in different places of + // the code base): + if (!dbus_threads_init_default()) { + throw std::bad_alloc(); + } + + mpImpl.reset(new BluetoothServer::Impl()); +#endif +} + +BluetoothServer::~BluetoothServer() +{ +} + +void BluetoothServer::ensureDiscoverable() +{ +#ifdef LINUX_BLUETOOTH + // Push it all across into our mainloop + if( !spServer ) + return; + GSource *pIdle = g_idle_source_new(); + g_source_set_callback( pIdle, ensureDiscoverable_cb, nullptr, nullptr ); + g_source_set_priority( pIdle, G_PRIORITY_DEFAULT ); + g_source_attach( pIdle, spServer->mpImpl->mpContext ); + g_source_unref( pIdle ); +#endif +} + +void BluetoothServer::restoreDiscoverable() +{ +#ifdef LINUX_BLUETOOTH + // Push it all across into our mainloop + if( !spServer ) + return; + GSource *pIdle = g_idle_source_new(); + g_source_set_callback( pIdle, restoreDiscoverable_cb, nullptr, nullptr ); + g_source_set_priority( pIdle, G_PRIORITY_DEFAULT_IDLE ); + g_source_attach( pIdle, spServer->mpImpl->mpContext ); + g_source_unref( pIdle ); +#endif +} + +void BluetoothServer::doEnsureDiscoverable() +{ +#ifdef LINUX_BLUETOOTH + if (!spServer->mpImpl->mpConnection || + spServer->meWasDiscoverable != UNKNOWN ) + return; + + // Find out if we are discoverable already ... + std::unique_ptr<DBusObject> pAdapter = spServer->mpImpl->getAdapter(); + if( !pAdapter ) + return; + + bool bDiscoverable = getDiscoverable(spServer->mpImpl->mpConnection, pAdapter.get() ); + + spServer->meWasDiscoverable = bDiscoverable ? DISCOVERABLE : NOT_DISCOVERABLE; + if( !bDiscoverable ) + setDiscoverable( spServer->mpImpl->mpConnection, pAdapter.get(), true ); +#endif +} + +void BluetoothServer::doRestoreDiscoverable() +{ + if( spServer->meWasDiscoverable == NOT_DISCOVERABLE ) + { +#ifdef LINUX_BLUETOOTH + std::unique_ptr<DBusObject> pAdapter = spServer->mpImpl->getAdapter(); + if( !pAdapter ) + return; + setDiscoverable( spServer->mpImpl->mpConnection, pAdapter.get(), false ); +#endif + } + spServer->meWasDiscoverable = UNKNOWN; +} + +// We have to have all our clients shut otherwise we can't +// re-bind to the same port number it appears. +void BluetoothServer::cleanupCommunicators() +{ + for (auto& rpCommunicator : *mpCommunicators) + rpCommunicator->forceClose(); + // the hope is that all the threads then terminate cleanly and + // clean themselves up. +} + +void SAL_CALL BluetoothServer::run() +{ + SAL_INFO( "sdremote.bluetooth", "BluetoothServer::run called" ); + osl::Thread::setName("BluetoothServer"); +#ifdef LINUX_BLUETOOTH + DBusConnection *pConnection = dbusConnectToNameOnBus(); + if( !pConnection ) + return; + + // For either implementation we need to poll the dbus fd + int fd = -1; + GPollFD aDBusFD; + if( dbus_connection_get_unix_fd( pConnection, &fd ) && fd >= 0 ) + { + aDBusFD.fd = fd; + aDBusFD.events = G_IO_IN | G_IO_PRI; + g_main_context_add_poll( mpImpl->mpContext, &aDBusFD, G_PRIORITY_DEFAULT ); + } + else + SAL_WARN( "sdremote.bluetooth", "failed to poll for incoming dbus signals" ); + + if (isBluez5Available(pConnection)) + { + SAL_INFO("sdremote.bluetooth", "Using Bluez 5"); + registerBluez5Profile(pConnection, mpCommunicators); + mpImpl->mpConnection = pConnection; + mpImpl->maBluezVersion = Impl::BluezVersion::BLUEZ5; + + // We don't need to listen to adapter changes anymore -- profile + // registration is done globally for the entirety of bluez, so we only + // need adapters when setting discoverability, which can be done + // dynamically without the need to listen for changes. + + // TODO: exit on SD deinit + // Probably best to do that in SdModule::~SdModule? + while (true) + { + aDBusFD.revents = 0; + g_main_context_iteration( mpImpl->mpContext, true ); + if( aDBusFD.revents ) + { + dbus_connection_read_write( pConnection, 0 ); + while (DBUS_DISPATCH_DATA_REMAINS == dbus_connection_get_dispatch_status( pConnection )) + dbus_connection_dispatch( pConnection ); + } + if ((false)) break; + // silence Clang -Wunreachable-code after loop (TODO: proper + // fix?) + } + unregisterBluez5Profile( pConnection ); + g_main_context_unref( mpImpl->mpContext ); + mpImpl->mpConnection = nullptr; + mpImpl->mpContext = nullptr; + return; + } + + // Otherwise we could be on Bluez 4 and continue as usual. + mpImpl->maBluezVersion = Impl::BluezVersion::BLUEZ4; + + // Try to setup the default adapter, otherwise wait for add/remove signal + mpImpl->mpService = registerWithDefaultAdapter( pConnection ); + // listen for connection state and power changes - we need to close + // and re-create our socket code on suspend / resume, enable/disable + DBusError aError; + dbus_error_init( &aError ); + dbus_bus_add_match( pConnection, "type='signal',interface='org.bluez.Manager'", &aError ); + dbus_connection_flush( pConnection ); + + // Try to setup the default adapter, otherwise wait for add/remove signal + mpImpl->mpService = registerWithDefaultAdapter( pConnection ); + + // poll on our bluetooth socket - if we can. + GPollFD aSocketFD; + if( mpImpl->mpService ) + bluezCreateAttachListeningSocket( mpImpl->mpContext, &aSocketFD ); + + mpImpl->mpConnection = pConnection; + + while( true ) + { + aDBusFD.revents = 0; + aSocketFD.revents = 0; + g_main_context_iteration( mpImpl->mpContext, true ); + + SAL_INFO( "sdremote.bluetooth", "main-loop spin " + << aDBusFD.revents << " " << aSocketFD.revents ); + if( aDBusFD.revents ) + { + dbus_connection_read_write( pConnection, 0 ); + DBusMessage *pMsg = dbus_connection_pop_message( pConnection ); + if( pMsg ) + { + if( dbus_message_is_signal( pMsg, "org.bluez.Manager", "AdapterRemoved" ) ) + { + SAL_WARN( "sdremote.bluetooth", "lost adapter - cleaning up sockets" ); + bluezDetachCloseSocket( mpImpl->mpContext, &aSocketFD ); + cleanupCommunicators(); + } + else if( dbus_message_is_signal( pMsg, "org.bluez.Manager", "AdapterAdded" ) || + dbus_message_is_signal( pMsg, "org.bluez.Manager", "DefaultAdapterChanged" ) ) + { + SAL_WARN( "sdremote.bluetooth", "gained adapter - re-generating sockets" ); + bluezDetachCloseSocket( mpImpl->mpContext, &aSocketFD ); + cleanupCommunicators(); + mpImpl->mpService = registerWithDefaultAdapter( pConnection ); + if( mpImpl->mpService ) + bluezCreateAttachListeningSocket( mpImpl->mpContext, &aSocketFD ); + } + else + SAL_INFO( "sdremote.bluetooth", "unknown incoming dbus message, " + " type: " << dbus_message_get_type( pMsg ) + << " path: '" << dbus_message_get_path( pMsg ) + << "' interface: '" << dbus_message_get_interface( pMsg ) + << "' member: '" << dbus_message_get_member( pMsg ) ); + } + dbus_message_unref( pMsg ); + } + + if( aSocketFD.revents ) + { + sockaddr_rc aRemoteAddr; + socklen_t aRemoteAddrLen = sizeof(aRemoteAddr); + + SAL_INFO( "sdremote.bluetooth", "performing accept" ); + int nClient = accept( aSocketFD.fd, reinterpret_cast<sockaddr*>(&aRemoteAddr), &aRemoteAddrLen); + if ( nClient < 0 && errno != EAGAIN ) + { + SAL_WARN( "sdremote.bluetooth", "accept failed with errno " << errno ); + } else { + SAL_INFO( "sdremote.bluetooth", "connection accepted " << nClient ); + Communicator* pCommunicator = new Communicator( std::make_unique<BufferedStreamSocket>( nClient ) ); + mpCommunicators->push_back( pCommunicator ); + pCommunicator->launch(); + } + } + if ((false)) break; + // silence Clang -Wunreachable-code after loop (TODO: proper fix?) + } + + unregisterBluez5Profile( pConnection ); + g_main_context_unref( mpImpl->mpContext ); + mpImpl->mpConnection = nullptr; + mpImpl->mpContext = nullptr; + +#elif defined(_WIN32) + WORD wVersionRequested; + WSADATA wsaData; + + wVersionRequested = MAKEWORD(2, 2); + + if ( WSAStartup(wVersionRequested, &wsaData) ) + { + return; // winsock dll couldn't be loaded + } + + int aSocket = socket( AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM ); + if ( !aSocket ) + { + WSACleanup(); + return; + } + SOCKADDR_BTH aAddr; + aAddr.addressFamily = AF_BTH; + aAddr.btAddr = 0; + aAddr.serviceClassId = GUID_NULL; + aAddr.port = BT_PORT_ANY; // Select any free socket. + if ( bind( aSocket, reinterpret_cast<SOCKADDR*>(&aAddr), sizeof(aAddr) ) == SOCKET_ERROR ) + { + closesocket( aSocket ); + WSACleanup(); + return; + } + + SOCKADDR_BTH aName; + int aNameSize = sizeof(aName); + getsockname( aSocket, reinterpret_cast<SOCKADDR*>(&aName), &aNameSize ); // Retrieve the local address and port + + CSADDR_INFO aAddrInfo = {}; + aAddrInfo.LocalAddr.lpSockaddr = reinterpret_cast<SOCKADDR*>(&aName); + aAddrInfo.LocalAddr.iSockaddrLength = sizeof( SOCKADDR_BTH ); + aAddrInfo.iSocketType = SOCK_STREAM; + aAddrInfo.iProtocol = BTHPROTO_RFCOMM; + + // To be used for setting a custom UUID once available. +// GUID uuid; +// uuid.Data1 = 0x00001101; +// memset( &uuid, 0x1000 + UUID*2^96, sizeof( GUID ) ); +// uuid.Data2 = 0; +// uuid.Data3 = 0x1000; +// ULONGLONG aData4 = 0x800000805F9B34FB; +// memcpy( uuid.Data4, &aData4, sizeof(uuid.Data4) ); + + WSAQUERYSETW aRecord = {}; + aRecord.dwSize = sizeof(aRecord); + aRecord.lpszServiceInstanceName = const_cast<wchar_t *>( + L"LibreOffice Impress Remote Control"); + aRecord.lpszComment = const_cast<wchar_t *>( + L"Remote control of presentations over bluetooth."); + aRecord.lpServiceClassId = const_cast<LPGUID>(&SerialPortServiceClass_UUID); + aRecord.dwNameSpace = NS_BTH; + aRecord.dwNumberOfCsAddrs = 1; + aRecord.lpcsaBuffer = &aAddrInfo; + if (WSASetServiceW( &aRecord, RNRSERVICE_REGISTER, 0 ) == SOCKET_ERROR) + { + closesocket( aSocket ); + WSACleanup(); + return; + } + + if ( listen( aSocket, 1 ) == SOCKET_ERROR ) + { + closesocket( aSocket ); + WSACleanup(); + return; + } + + SOCKADDR_BTH aRemoteAddr; + int aRemoteAddrLen = sizeof(aRemoteAddr); + while ( true ) + { + SOCKET socket; + if ( (socket = accept(aSocket, reinterpret_cast<sockaddr*>(&aRemoteAddr), &aRemoteAddrLen)) == INVALID_SOCKET ) + { + closesocket( aSocket ); + WSACleanup(); + return; + } else { + Communicator* pCommunicator = new Communicator( std::make_unique<BufferedStreamSocket>( socket) ); + mpCommunicators->push_back( pCommunicator ); + pCommunicator->launch(); + } + } + +#elif defined(MACOSX) + // Build up dictionary at run-time instead of bothering with a + // .plist file, using the Objective-C API + + // Compare to BluetoothServiceRecord.hxx + + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + NSDictionary *dict = + [NSDictionary dictionaryWithObjectsAndKeys: + + // Service class ID list + [NSArray arrayWithObject: + [IOBluetoothSDPUUID uuid16: kBluetoothSDPUUID16ServiceClassSerialPort]], + @"0001 - ServiceClassIDList", + + // Protocol descriptor list + [NSArray arrayWithObjects: + [NSArray arrayWithObject: [IOBluetoothSDPUUID uuid16: kBluetoothSDPUUID16L2CAP]], + [NSArray arrayWithObjects: + [IOBluetoothSDPUUID uuid16: kBluetoothL2CAPPSMRFCOMM], + [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInt: 1], + @"DataElementSize", + [NSNumber numberWithInt: 1], + @"DataElementType", + [NSNumber numberWithInt: 5], // RFCOMM port number, will be replaced if necessary automatically + @"DataElementValue", + nil], + nil], + nil], + @"0004 - Protocol descriptor list", + + // Browse group list + [NSArray arrayWithObject: + [IOBluetoothSDPUUID uuid16: kBluetoothSDPUUID16ServiceClassPublicBrowseGroup]], + @"0005 - BrowseGroupList", + + // Language base attribute ID list + [NSArray arrayWithObjects: + [NSData dataWithBytes: "en" length: 2], + [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInt: 2], + @"DataElementSize", + [NSNumber numberWithInt: 1], + @"DataElementType", + [NSNumber numberWithInt: 0x006a], // encoding + @"DataElementValue", + nil], + [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInt: 2], + @"DataElementSize", + [NSNumber numberWithInt: 1], + @"DataElementType", + [NSNumber numberWithInt: 0x0100], // offset + @"DataElementValue", + nil], + nil], + @"0006 - LanguageBaseAttributeIDList", + + // Bluetooth profile descriptor list + [NSArray arrayWithObject: + [NSArray arrayWithObjects: + [IOBluetoothSDPUUID uuid16: kBluetoothSDPUUID16ServiceClassSerialPort], + [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInt: 2], + @"DataElementSize", + [NSNumber numberWithInt: 1], + @"DataElementType", + [NSNumber numberWithInt: 0x0100], // version number ? + @"DataElementValue", + nil], + nil]], + @"0009 - BluetoothProfileDescriptorList", + + // Attributes pointed to by the LanguageBaseAttributeIDList + @"LibreOffice Impress Remote Control", + @"0100 - ServiceName", + @"The Document Foundation", + @"0102 - ProviderName", + nil]; + + // Create service + IOBluetoothSDPServiceRecordRef serviceRecordRef; + SAL_WNODEPRECATED_DECLARATIONS_PUSH //TODO: 10.9 IOBluetoothAddServiceDict + IOReturn rc = IOBluetoothAddServiceDict(reinterpret_cast<CFDictionaryRef>(dict), &serviceRecordRef); + SAL_WNODEPRECATED_DECLARATIONS_POP + + SAL_INFO("sdremote.bluetooth", "IOBluetoothAddServiceDict returned " << rc); + + if (rc == kIOReturnSuccess) + { + IOBluetoothSDPServiceRecord *serviceRecord = + [IOBluetoothSDPServiceRecord withSDPServiceRecordRef: serviceRecordRef]; + + BluetoothRFCOMMChannelID channelID; + [serviceRecord getRFCOMMChannelID: &channelID]; + + BluetoothSDPServiceRecordHandle serviceRecordHandle; + [serviceRecord getServiceRecordHandle: &serviceRecordHandle]; + + // Register callback for incoming connections + IOBluetoothRegisterForFilteredRFCOMMChannelOpenNotifications( + incomingCallback, + this, + channelID, + kIOBluetoothUserNotificationChannelDirectionIncoming); + + [serviceRecord release]; + } + + [pool release]; + + (void) mpCommunicators; +#else + (void) mpCommunicators; // avoid warnings about unused member +#endif +} + +BluetoothServer *sd::BluetoothServer::spServer = nullptr; + +void BluetoothServer::setup( std::vector<Communicator*>* pCommunicators ) +{ + if (spServer) + return; + + spServer = new BluetoothServer( pCommunicators ); + spServer->create(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/BluetoothServer.hxx b/sd/source/ui/remotecontrol/BluetoothServer.hxx new file mode 100644 index 000000000..9e20bfa51 --- /dev/null +++ b/sd/source/ui/remotecontrol/BluetoothServer.hxx @@ -0,0 +1,61 @@ +/* -*- 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/. + */ +#pragma once + +#include <osl/thread.hxx> +#include <memory> +#include <vector> + +#include <config_dbus.h> + +#if (defined(LINUX) && !defined(__FreeBSD_kernel__)) && ENABLE_DBUS && DBUS_HAVE_GLIB +# define LINUX_BLUETOOTH +#endif + +namespace sd +{ + class Communicator; + + class BluetoothServer: + public osl::Thread + { + public: + static void setup( std::vector<Communicator*>* pCommunicators ); + + /// ensure that Bluetooth discoverability is on + static void ensureDiscoverable(); + /// restore the state of discoverability from before ensureDiscoverable + static void restoreDiscoverable(); + + // called by C / idle callbacks + static void doEnsureDiscoverable(); + static void doRestoreDiscoverable(); + +#if defined(MACOSX) + void addCommunicator( Communicator* pCommunicator ); +#endif + private: + explicit BluetoothServer( std::vector<Communicator*>* pCommunicators ); + virtual ~BluetoothServer() override; + + enum { UNKNOWN, DISCOVERABLE, NOT_DISCOVERABLE } meWasDiscoverable; + static BluetoothServer *spServer; + +#ifdef LINUX_BLUETOOTH + struct Impl; + std::unique_ptr<Impl> mpImpl; +#endif + virtual void SAL_CALL run() override; + + void cleanupCommunicators(); + std::vector<Communicator*>* mpCommunicators; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/BluetoothServer.mm b/sd/source/ui/remotecontrol/BluetoothServer.mm new file mode 100644 index 000000000..28288ff6f --- /dev/null +++ b/sd/source/ui/remotecontrol/BluetoothServer.mm @@ -0,0 +1 @@ +#include "BluetoothServer.cxx"
\ No newline at end of file diff --git a/sd/source/ui/remotecontrol/BluetoothServiceRecord.hxx b/sd/source/ui/remotecontrol/BluetoothServiceRecord.hxx new file mode 100644 index 000000000..c1a00fb3b --- /dev/null +++ b/sd/source/ui/remotecontrol/BluetoothServiceRecord.hxx @@ -0,0 +1,75 @@ +/* -*- 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/. + */ + +#pragma once + +// FIXME: look into sharing definitions across OS's (i.e. UUID and port ). +// Look into dynamically determining which ports are available. + +// SDP is a Service Description Protocol cf. +// http://developer.bluetooth.org/TechnologyOverview/Pages/DI.aspx +// This is an XML representation, an alternative would be a +// binary SDP record. + +// for numbers see: +// https://www.bluetooth.org/Technical/AssignedNumbers/service_discovery.htm + +const char * const bluetooth_service_record = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<record>" + "<attribute id=\"0x0001\">" // Service class ID list + "<sequence>" + "<uuid value=\"0x1101\"/>" // an assigned service class meaning: 'serial port' + // we could add our own 'LibreOffice remote' service + // class here too in future ... + "</sequence>" + "</attribute>" + "<attribute id=\"0x0004\">" // Protocol Descriptor list + "<sequence>" + "<sequence>" + "<uuid value=\"0x0100\"/>" // L2CAP Protocol descriptor + "</sequence>" + "<sequence>" + "<uuid value=\"0x0003\"/>" // enumeration value of RFCOMM protocol + "<uint8 value=\"0x05\"/>" // RFCOMM port number + "</sequence>" + "</sequence>" + "</attribute>" + "<attribute id=\"0x0005\">" // Browse Group List + "<sequence>" + "<uuid value=\"0x1002\"/>" // public browse class + "</sequence>" + "</attribute>" + "<attribute id=\"0x0006\">" // Language Base Attribute ID List + "<sequence>" + "<uint16 value=\"0x656e\"/>" // code_ISO639 + "<uint16 value=\"0x006a\"/>" // encoding 0x6a + "<uint16 value=\"0x0100\"/>" // base_offset ie. points to below => + "</sequence>" + "</attribute>" + "<attribute id=\"0x0009\">" // Bluetooth Profile Descriptor List + "<sequence>" + "<sequence>" + "<uuid value=\"0x1101\"/>" // 'serial port' UUID as above + "<uint16 value=\"0x0100\"/>"// version number 1.0 ? + "</sequence>" + "</sequence>" + "</attribute>" + // Attribute identifiers are pointed to by the Language Base Attribute ID List + // id+0 = ServiceName, id+1 = ServiceDescription, id+2=ProviderName + "<attribute id=\"0x0100\">" + "<text value=\"LibreOffice Impress Remote Control\"/>" + "</attribute>" + "<attribute id=\"0x0102\">" + "<text value=\"The Document Foundation\"/>" + "</attribute>" + "</record>" + ; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/BufferedStreamSocket.cxx b/sd/source/ui/remotecontrol/BufferedStreamSocket.cxx new file mode 100644 index 000000000..64ad5eb8d --- /dev/null +++ b/sd/source/ui/remotecontrol/BufferedStreamSocket.cxx @@ -0,0 +1,130 @@ +/* -*- 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/. + */ + +#include "BufferedStreamSocket.hxx" + +#include <osl/socket.hxx> +#include <sal/log.hxx> +#include <algorithm> + +#ifdef _WIN32 + // LO vs WinAPI conflict + #undef WB_LEFT + #undef WB_RIGHT + + #include <winsock2.h> +#else + #include <sys/socket.h> + #include <unistd.h> +#endif +using namespace sd; +using namespace osl; + +BufferedStreamSocket::BufferedStreamSocket( const osl::StreamSocket &aSocket ): + StreamSocket( aSocket ), + aRet( 0 ), + aRead( 0 ), + mSocket( 0 ), + usingCSocket( false ) +{ +} + +BufferedStreamSocket::BufferedStreamSocket( int aSocket ): + aRet( 0 ), + aRead( 0 ), + mSocket( aSocket ), + usingCSocket( true ) +{ +} + +BufferedStreamSocket::~BufferedStreamSocket() { + close(); +} + +void BufferedStreamSocket::getPeerAddr(osl::SocketAddr& rAddr) +{ + assert ( !usingCSocket ); + StreamSocket::getPeerAddr( rAddr ); +} + +sal_Int32 BufferedStreamSocket::write( const void* pBuffer, sal_uInt32 n ) +{ + if ( !usingCSocket ) + return StreamSocket::write( pBuffer, n ); + else + return ::send( + mSocket, +#if defined(_WIN32) + static_cast<char const *>(pBuffer), +#else + pBuffer, +#endif + static_cast<size_t>(n), 0 ); +} + +void BufferedStreamSocket::close() +{ + if( usingCSocket && mSocket != -1 ) + { +#ifdef _WIN32 + ::closesocket( mSocket ); +#else + ::close( mSocket ); +#endif + mSocket = -1; + } + else + ::osl::StreamSocket::close(); +} + +sal_Int32 BufferedStreamSocket::readLine( OString& aLine ) +{ + while ( true ) + { + // Process buffer first in case data already present. + std::vector<char>::iterator aIt; + if ( (aIt = find( aBuffer.begin(), aBuffer.end(), '\n' )) + != aBuffer.end() ) + { + sal_uInt64 aLocation = aIt - aBuffer.begin(); + + aLine = OString( &(*aBuffer.begin()), aLocation ); + + aBuffer.erase( aBuffer.begin(), aIt + 1 ); // Also delete the empty line + aRead -= (aLocation + 1); + + SAL_INFO( "sdremote.bluetooth", "recv line '" << aLine << "'" ); + + return aLine.getLength() + 1; + } + + // Then try and receive if nothing present + aBuffer.resize( aRead + 100 ); + if ( !usingCSocket) + aRet = StreamSocket::recv( &aBuffer[aRead], 100 ); + else + aRet = ::recv( mSocket, &aBuffer[aRead], 100, 0 ); + + SAL_INFO( "sdremote.bluetooth", "recv " << aRet << " aBuffer len " << aBuffer.size() ); + if ( aRet <= 0 ) + { + return 0; + } + // Prevent buffer from growing massively large. + if ( aRead > MAX_LINE_LENGTH ) + { + aBuffer.clear(); + return 0; + } + aRead += aRet; + } + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/BufferedStreamSocket.hxx b/sd/source/ui/remotecontrol/BufferedStreamSocket.hxx new file mode 100644 index 000000000..6abf7ec1b --- /dev/null +++ b/sd/source/ui/remotecontrol/BufferedStreamSocket.hxx @@ -0,0 +1,66 @@ +/* -*- 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/. + */ +#pragma once + +#include "IBluetoothSocket.hxx" +#include <osl/socket_decl.hxx> +#include <vector> + +#define MAX_LINE_LENGTH 20000 + +namespace sd +{ + + /** + * [A wrapper for an osl StreamSocket to allow reading lines.] + * + * Currently wraps either an osl StreamSocket or a standard c socket, + * allowing reading and writing for our purposes. Should eventually be + * returned to being a StreamSocket wrapper if/when Bluetooth is + * integrated into osl Sockets. + */ + class BufferedStreamSocket final : + public IBluetoothSocket, + private ::osl::StreamSocket + { + public: + /** + * Create a BufferedStreamSocket on top of an + * osl::StreamSocket. + */ + explicit BufferedStreamSocket( const osl::StreamSocket &aSocket ); + /** + * Create a BufferedStreamSocket on top of a POSIX or WinSock socket. + */ + explicit BufferedStreamSocket( int aSocket ); + BufferedStreamSocket( const BufferedStreamSocket &aSocket ); + + ~BufferedStreamSocket(); + + /** + * Blocks until a line is read. + * Returns whatever the last call of recv returned, i.e. 0 or less + * if there was a problem in communications. + */ + virtual sal_Int32 readLine( OString& aLine ) override; + + virtual sal_Int32 write( const void* pBuffer, sal_uInt32 n ) override; + + virtual void close() override; + + void getPeerAddr(osl::SocketAddr&); + private: + sal_Int32 aRet, aRead; + std::vector<char> aBuffer; + int mSocket; + bool usingCSocket; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/Communicator.cxx b/sd/source/ui/remotecontrol/Communicator.cxx new file mode 100644 index 000000000..59509ed3c --- /dev/null +++ b/sd/source/ui/remotecontrol/Communicator.cxx @@ -0,0 +1,154 @@ +/* -*- 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/. + */ +#include <algorithm> +#include <vector> + +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/presentation/XPresentation2.hpp> +#include <com/sun/star/presentation/XPresentationSupplier.hpp> +#include <comphelper/processfactory.hxx> +#include <comphelper/documentinfo.hxx> +#include <config_version.h> +#include <rtl/string.hxx> +#include <sal/log.hxx> + +#include "Communicator.hxx" +#include "IBluetoothSocket.hxx" +#include "Listener.hxx" +#include "Receiver.hxx" +#include "Transmitter.hxx" +#include <RemoteServer.hxx> + +using namespace sd; +using namespace com::sun::star; +using namespace osl; + +Communicator::Communicator( std::unique_ptr<IBluetoothSocket> pSocket ): + Thread( "CommunicatorThread" ), + mpSocket( std::move(pSocket) ) +{ +} + +Communicator::~Communicator() +{ +} + +/// Close the underlying socket from another thread to force +/// an early exit / termination +void Communicator::forceClose() +{ + if( mpSocket ) + mpSocket->close(); +} + +// Run as a thread +void Communicator::execute() +{ + pTransmitter.reset( new Transmitter( mpSocket.get() ) ); + pTransmitter->create(); + + pTransmitter->addMessage( "LO_SERVER_SERVER_PAIRED\n\n", + Transmitter::PRIORITY_HIGH ); + + pTransmitter->addMessage( "LO_SERVER_INFO\n" LIBO_VERSION_DOTTED "\n\n", + Transmitter::PRIORITY_HIGH ); + + Receiver aReceiver( pTransmitter.get() ); + try { + uno::Reference< frame::XDesktop2 > xFramesSupplier = frame::Desktop::create( ::comphelper::getProcessComponentContext() ); + uno::Reference< frame::XFrame > xFrame = xFramesSupplier->getActiveFrame(); + + uno::Reference<presentation::XPresentationSupplier> xPS; + if( xFrame.is() ) + xPS.set( xFrame->getController()->getModel(), uno::UNO_QUERY ); + uno::Reference<presentation::XPresentation2> xPresentation; + if( xPS.is() ) + xPresentation.set( xPS->getPresentation(), uno::UNO_QUERY ); + if ( xPresentation.is() && xPresentation->isRunning() ) + { + presentationStarted( xPresentation->getController() ); + OString aBuffer = + "slideshow_info\n" + + OUStringToOString( ::comphelper::DocumentInfo::getDocumentTitle( xFrame->getController()->getModel() ), RTL_TEXTENCODING_UTF8 ) + + "\n\n"; + + pTransmitter->addMessage( aBuffer.getStr(), Transmitter::PRIORITY_LOW ); + } + else + { + pTransmitter->addMessage( "slideshow_finished\n\n", + Transmitter::PRIORITY_HIGH ); + } + } + catch (uno::RuntimeException &) + { + } + + sal_uInt64 aRet; + std::vector<OString> aCommand; + while ( true ) + { + OString aLine; + aRet = mpSocket->readLine( aLine ); + if ( aRet == 0 ) + { + break; // I.e. transmission finished. + } + if ( aLine.getLength() ) + { + aCommand.push_back( aLine ); + } + else + { + aReceiver.pushCommand( aCommand ); + aCommand.clear(); + } + } + + SAL_INFO ("sdremote", "Exiting transmission loop"); + + disposeListener(); + + pTransmitter->notifyFinished(); + pTransmitter->join(); + pTransmitter = nullptr; + + mpSocket->close(); + mpSocket.reset(); + + RemoteServer::removeCommunicator( this ); +} + +void Communicator::informListenerDestroyed() +{ + if ( pTransmitter ) + pTransmitter->addMessage( "slideshow_finished\n\n", + Transmitter::PRIORITY_HIGH ); +} + +void Communicator::presentationStarted( const css::uno::Reference< + css::presentation::XSlideShowController > &rController ) +{ + if ( pTransmitter ) + { + mListener.set( new Listener( this, pTransmitter.get() ) ); + mListener->init( rController ); + } +} + +void Communicator::disposeListener() +{ + if ( mListener.is() ) + { + mListener->dispose(); + mListener = nullptr; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/Communicator.hxx b/sd/source/ui/remotecontrol/Communicator.hxx new file mode 100644 index 000000000..f8f23c58c --- /dev/null +++ b/sd/source/ui/remotecontrol/Communicator.hxx @@ -0,0 +1,52 @@ +/* -*- 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/. + */ +#pragma once + +#include <memory> + +#include <rtl/ref.hxx> +#include <salhelper/thread.hxx> + +namespace com::sun::star::uno { template <typename > class Reference; } +namespace com::sun::star::presentation { class XSlideShowController; } +namespace sd { struct IBluetoothSocket; } + +namespace sd +{ + + class Transmitter; + class Listener; + + /** Class used for communication with one single client, dealing with all + * tasks specific to this client. + * + * Needs to be created, then started using launch(), disposes itself. + */ + class Communicator : public salhelper::Thread + { + public: + explicit Communicator( std::unique_ptr<IBluetoothSocket> pSocket ); + virtual ~Communicator() override; + + void presentationStarted( const css::uno::Reference< + css::presentation::XSlideShowController > &rController ); + void informListenerDestroyed(); + void disposeListener(); + void forceClose(); + + private: + void execute() override; + std::unique_ptr<IBluetoothSocket> mpSocket; + + std::unique_ptr<Transmitter> pTransmitter; + rtl::Reference<Listener> mListener; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/DiscoveryService.cxx b/sd/source/ui/remotecontrol/DiscoveryService.cxx new file mode 100644 index 000000000..bdd0b51c8 --- /dev/null +++ b/sd/source/ui/remotecontrol/DiscoveryService.cxx @@ -0,0 +1,186 @@ +/* -*- 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/. + */ + +#include <errno.h> +#include <string.h> +#include <iostream> + +#include <osl/socket.hxx> +#include <config_features.h> +#include <sal/log.hxx> + +#include "DiscoveryService.hxx" +#include "ZeroconfService.hxx" + +#ifdef _WIN32 + // LO vs WinAPI conflict + #undef WB_LEFT + #undef WB_RIGHT + + #include <winsock2.h> + #include <ws2tcpip.h> + + #include "WINNetworkService.hxx" + typedef int socklen_t; +#else + #include <unistd.h> + #include <sys/socket.h> + #include <netinet/in.h> +#endif + +#ifdef MACOSX + #include <osl/conditn.hxx> + #include <premac.h> + #import <CoreFoundation/CoreFoundation.h> + #include <postmac.h> + #import "OSXNetworkService.hxx" +#endif + +#if HAVE_FEATURE_AVAHI + #include "AvahiNetworkService.hxx" +#endif + +using namespace osl; +using namespace sd; + +DiscoveryService::DiscoveryService() + : mSocket(-1) + , zService(nullptr) +{ +} + +DiscoveryService::~DiscoveryService() +{ + if (mSocket != -1) + { +#ifdef _WIN32 + closesocket( mSocket ); +#else + close( mSocket ); +#endif + } + + if (zService) + zService->clear(); +} + +void DiscoveryService::setupSockets() +{ + +#ifdef MACOSX + // Bonjour for OSX + zService = new OSXNetworkService(); + zService->setup(); +#endif + +#if HAVE_FEATURE_AVAHI + // Avahi for Linux + char hostname[1024]; + hostname[1023] = '\0'; + gethostname(hostname, 1023); + + zService = new AvahiNetworkService(hostname); + zService->setup(); +#endif + +#ifdef _WIN32 + zService = new WINNetworkService(); + zService->setup(); +#endif + + // Old implementation for backward compatibility matter + mSocket = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); + if (mSocket == -1) + { + SAL_WARN("sd", "DiscoveryService: socket failed: " << errno); + return; // would be better to throw, but unsure if caller handles that + } + + sockaddr_in aAddr = {}; + aAddr.sin_family = AF_INET; + aAddr.sin_addr.s_addr = htonl(INADDR_ANY); + aAddr.sin_port = htons( PORT_DISCOVERY ); + + int rc = bind( mSocket, reinterpret_cast<sockaddr*>(&aAddr), sizeof(sockaddr_in) ); + + if (rc) + { + SAL_WARN("sd", "DiscoveryService: bind failed: " << errno); + return; // would be better to throw, but unsure if caller handles that + } + + struct ip_mreq multicastRequest; + + multicastRequest.imr_multiaddr.s_addr = htonl((239U << 24) | 1U); // 239.0.0.1 + multicastRequest.imr_interface.s_addr = htonl(INADDR_ANY); + + rc = setsockopt( mSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, + #ifdef _WIN32 + reinterpret_cast<const char*>(&multicastRequest), + #else + &multicastRequest, + #endif + sizeof(multicastRequest)); + + if (rc) + { + SAL_WARN("sd", "DiscoveryService: setsockopt failed: " << errno); + return; // would be better to throw, but unsure if caller handles that + } +} + +void SAL_CALL DiscoveryService::run() +{ + osl::Thread::setName("DiscoveryService"); + + setupSockets(); + + // Kept for backward compatibility + while ( true ) + { + char aBuffer[BUFFER_SIZE] = {}; + sockaddr_in aAddr; + socklen_t aLen = sizeof( aAddr ); + if(recvfrom( mSocket, aBuffer, BUFFER_SIZE, 0, reinterpret_cast<sockaddr*>(&aAddr), &aLen ) > 0) + { + OString aString( aBuffer, strlen( "LOREMOTE_SEARCH" ) ); + if ( aString == "LOREMOTE_SEARCH" ) + { + OString aStringBuffer = "LOREMOTE_ADVERTISE\n" + + OUStringToOString(osl::SocketAddr::getLocalHostname(), RTL_TEXTENCODING_UTF8 ) + + "\n\n"; + if ( sendto( mSocket, aStringBuffer.getStr(), + aStringBuffer.getLength(), 0, reinterpret_cast<sockaddr*>(&aAddr), + sizeof(aAddr) ) <= 0 ) + { + // Write error or closed socket -- we are done. + return; + } + } + } + else + { + // Read error or closed socket -- we are done. + return; + } + } +} + +DiscoveryService *sd::DiscoveryService::spService = nullptr; + +void DiscoveryService::setup() +{ + if (spService) + return; + + spService = new DiscoveryService(); + spService->create(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/DiscoveryService.hxx b/sd/source/ui/remotecontrol/DiscoveryService.hxx new file mode 100644 index 000000000..4b235fe89 --- /dev/null +++ b/sd/source/ui/remotecontrol/DiscoveryService.hxx @@ -0,0 +1,40 @@ +/* -*- 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/. + */ +#pragma once + +#include <osl/thread.hxx> + +namespace sd { class ZeroconfService; } + +namespace sd +{ + class DiscoveryService : public osl::Thread + { + public: + static void setup(); + + private: + DiscoveryService(); + virtual ~DiscoveryService() override; + + /** + * Networking related setup -- must be run within our own thread + * to prevent the application blocking (fdo#75328). + */ + void setupSockets(); + + static DiscoveryService *spService; + virtual void SAL_CALL run() override; + int mSocket; + + ZeroconfService * zService; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/DiscoveryService.mm b/sd/source/ui/remotecontrol/DiscoveryService.mm new file mode 100644 index 000000000..3cad7cdfb --- /dev/null +++ b/sd/source/ui/remotecontrol/DiscoveryService.mm @@ -0,0 +1 @@ +#include "DiscoveryService.cxx"
\ No newline at end of file diff --git a/sd/source/ui/remotecontrol/IBluetoothSocket.hxx b/sd/source/ui/remotecontrol/IBluetoothSocket.hxx new file mode 100644 index 000000000..4b75a1e82 --- /dev/null +++ b/sd/source/ui/remotecontrol/IBluetoothSocket.hxx @@ -0,0 +1,42 @@ +/* -*- 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/. + */ + +#pragma once + +#include <rtl/string.hxx> + +namespace sd +{ + /** Interface for bluetooth data io + */ + struct IBluetoothSocket + { + IBluetoothSocket() = default; + virtual ~IBluetoothSocket() {} + IBluetoothSocket(const IBluetoothSocket&) = delete; + IBluetoothSocket& operator=(const IBluetoothSocket&) = delete; + + /** Blocks until a line is read. + + @return whatever the last call of recv returned, i.e. 0 or less + if there was a problem in communications. + */ + virtual sal_Int32 readLine(OString& aLine) = 0; + + /** Write a number of bytes + + @return number of bytes actually written + */ + virtual sal_Int32 write( const void* pBuffer, sal_uInt32 n ) = 0; + + virtual void close() {}; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/ImagePreparer.cxx b/sd/source/ui/remotecontrol/ImagePreparer.cxx new file mode 100644 index 000000000..ba8d2c1f3 --- /dev/null +++ b/sd/source/ui/remotecontrol/ImagePreparer.cxx @@ -0,0 +1,255 @@ +/* -*- 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 "ImagePreparer.hxx" +#include "Transmitter.hxx" + +#include <comphelper/base64.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertyvalue.hxx> +#include <osl/file.hxx> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> + +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/drawing/GraphicExportFilter.hpp> +#include <com/sun/star/lang/XServiceName.hpp> +#include <com/sun/star/presentation/XSlideShowController.hpp> +#include <com/sun/star/presentation/XPresentationPage.hpp> +#include <com/sun/star/text/XTextRange.hpp> + +using namespace ::sd; +using namespace ::osl; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +ImagePreparer::ImagePreparer( + const uno::Reference<presentation::XSlideShowController>& rxController, + Transmitter *aTransmitter ) + : Timer("sd ImagePreparer"), + xController( rxController ), + pTransmitter( aTransmitter ) +{ + SAL_INFO( "sdremote", "ImagePreparer - start" ); + SetTimeout( 50 ); + mnSendingSlide = 0; + Start(); +} + +ImagePreparer::~ImagePreparer() +{ + SAL_INFO( "sdremote", "ImagePreparer - stop" ); + Stop(); +} + +void ImagePreparer::Invoke() +{ + sal_uInt32 aSlides = xController->getSlideCount(); + SAL_INFO( "sdremote", "ImagePreparer " << xController->isRunning() << + " sending slide " << mnSendingSlide << " of " << aSlides ); + if ( xController->isRunning() && // not stopped/disposed of. + mnSendingSlide < aSlides ) + { + sendPreview( mnSendingSlide ); + sendNotes( mnSendingSlide ); + mnSendingSlide++; + Start(); + } + else + Stop(); +} + +void ImagePreparer::sendPreview( sal_uInt32 aSlideNumber ) +{ + sal_uInt64 aSize; + uno::Sequence<sal_Int8> aImageData = preparePreview( aSlideNumber, 320, 240, + aSize ); + if ( !xController->isRunning() ) + return; + + OUStringBuffer aStrBuffer; + ::comphelper::Base64::encode( aStrBuffer, aImageData ); + + OString aEncodedShortString = OUStringToOString( + aStrBuffer.makeStringAndClear(), RTL_TEXTENCODING_UTF8 ); + + // Start the writing + OString aBuffer = "slide_preview\n" + + OString::number(aSlideNumber) + + "\n" + aEncodedShortString + "\n\n"; + pTransmitter->addMessage( aBuffer, + Transmitter::PRIORITY_LOW ); + +} + +uno::Sequence<sal_Int8> ImagePreparer::preparePreview( + sal_uInt32 aSlideNumber, sal_uInt32 aWidth, sal_uInt32 aHeight, + sal_uInt64 &rSize ) +{ + OUString aFileURL; + FileBase::createTempFile( nullptr, nullptr, &aFileURL ); + + uno::Reference< drawing::XGraphicExportFilter > xFilter = + drawing::GraphicExportFilter::create( ::comphelper::getProcessComponentContext() ); + + if ( !xController->isRunning() ) + return uno::Sequence<sal_Int8>(); + + uno::Reference< lang::XComponent > xSourceDoc( + xController->getSlideByIndex( aSlideNumber ), + uno::UNO_QUERY_THROW ); + + xFilter->setSourceDocument( xSourceDoc ); + + uno::Sequence< beans::PropertyValue > aFilterData{ + comphelper::makePropertyValue("PixelWidth", aWidth), + comphelper::makePropertyValue("PixelHeight", aHeight), + comphelper::makePropertyValue("ColorMode", sal_Int32(0)) // 0: Color, 1: B&W + }; + + uno::Sequence< beans::PropertyValue > aProps{ + comphelper::makePropertyValue("MediaType", OUString( "image/png" )), + comphelper::makePropertyValue("URL", aFileURL), + comphelper::makePropertyValue("FilterData", aFilterData) + }; + + xFilter->filter( aProps ); + + File aFile(aFileURL); + if (aFile.open(0) != osl::File::E_None) + return uno::Sequence<sal_Int8>(); + + sal_uInt64 aRead; + rSize = 0; + aFile.getSize( rSize ); + uno::Sequence<sal_Int8> aContents( rSize ); + + aFile.read( aContents.getArray(), rSize, aRead ); + if (aRead != rSize) + aContents.realloc(aRead); + + aFile.close(); + File::remove( aFileURL ); + return aContents; + +} + +void ImagePreparer::sendNotes( sal_uInt32 aSlideNumber ) +{ + + OString aNotes = prepareNotes( aSlideNumber ); + + if ( aNotes.isEmpty() ) + return; + + if ( !xController->isRunning() ) + return; + + // Start the writing + OString aBuffer = + "slide_notes\n" + + OString::number( static_cast<sal_Int32>(aSlideNumber) ) + + "\n" + "<html><body>" + + aNotes + + "</body></html>" + "\n\n"; + pTransmitter->addMessage( aBuffer, + Transmitter::PRIORITY_LOW ); +} + +// Code copied from sdremote/source/presenter/PresenterNotesView.cxx +OString ImagePreparer::prepareNotes( sal_uInt32 aSlideNumber ) +{ + OUStringBuffer aRet; + + if ( !xController->isRunning() ) + return ""; + + uno::Reference<css::drawing::XDrawPage> aNotesPage; + uno::Reference< drawing::XDrawPage > xSourceDoc( + xController->getSlideByIndex( aSlideNumber ), + uno::UNO_SET_THROW ); + uno::Reference<presentation::XPresentationPage> xPresentationPage( + xSourceDoc, UNO_QUERY); + if (xPresentationPage.is()) + aNotesPage = xPresentationPage->getNotesPage(); + else + return ""; + + static constexpr OUStringLiteral sNotesShapeName ( + u"com.sun.star.presentation.NotesShape" ); + static constexpr OUStringLiteral sTextShapeName ( + u"com.sun.star.drawing.TextShape" ); + + if (aNotesPage.is()) + { + + // Iterate over all shapes and find the one that holds the text. + sal_Int32 nCount (aNotesPage->getCount()); + for (sal_Int32 nIndex=0; nIndex<nCount; ++nIndex) + { + + uno::Reference<lang::XServiceName> xServiceName ( + aNotesPage->getByIndex(nIndex), UNO_QUERY); + if (xServiceName.is() + && xServiceName->getServiceName() == sNotesShapeName) + { + uno::Reference<text::XTextRange> xText (xServiceName, UNO_QUERY); + if (xText.is()) + { + aRet.append(xText->getString()); + aRet.append("<br/>"); + } + } + else + { + uno::Reference<drawing::XShapeDescriptor> xShapeDescriptor ( + aNotesPage->getByIndex(nIndex), UNO_QUERY); + if (xShapeDescriptor.is()) + { + OUString sType (xShapeDescriptor->getShapeType()); + if (sType == sNotesShapeName || sType == sTextShapeName) + { + uno::Reference<text::XTextRange> xText ( + aNotesPage->getByIndex(nIndex), UNO_QUERY); + if (xText.is()) + { + aRet.append(xText->getString()); + aRet.append("<br/>"); + } + } + } + } + } + } + // Replace all newlines with <br\> tags + for ( sal_Int32 i = 0; i < aRet.getLength(); i++ ) + { + if ( aRet[i] == '\n' ) + { + aRet[i]= '<'; + aRet.insert( i+1, "br/>" ); + } + } + return OUStringToOString( + aRet.makeStringAndClear(), RTL_TEXTENCODING_UTF8 ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/ImagePreparer.hxx b/sd/source/ui/remotecontrol/ImagePreparer.hxx new file mode 100644 index 000000000..146eba073 --- /dev/null +++ b/sd/source/ui/remotecontrol/ImagePreparer.hxx @@ -0,0 +1,45 @@ +/* -*- 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/. + */ +#pragma once + +#include <vcl/timer.hxx> +#include <com/sun/star/uno/Reference.hxx> + +namespace com::sun::star::presentation { class XSlideShowController; } +namespace sd { class Transmitter; } + +namespace sd +{ + +class ImagePreparer : private Timer +{ + sal_uInt32 mnSendingSlide; +public: + ImagePreparer( const + css::uno::Reference<css::presentation::XSlideShowController>& + rxController, sd::Transmitter *aTransmitter ); + virtual ~ImagePreparer() override; + +private: + css::uno::Reference<css::presentation::XSlideShowController> xController; + Transmitter *pTransmitter; + + virtual void Invoke() override; + + void sendPreview( sal_uInt32 aSlideNumber ); + css::uno::Sequence<sal_Int8> preparePreview( sal_uInt32 aSlideNumber, + sal_uInt32 aWidth, sal_uInt32 aHeight, sal_uInt64 &rSize ); + + void sendNotes( sal_uInt32 aSlideNumber ); + OString prepareNotes( sal_uInt32 aSlideNumber ); +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/Listener.cxx b/sd/source/ui/remotecontrol/Listener.cxx new file mode 100644 index 000000000..3753ed9b5 --- /dev/null +++ b/sd/source/ui/remotecontrol/Listener.cxx @@ -0,0 +1,133 @@ +/* -*- 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/. + */ + +#include <sal/log.hxx> +#include <vcl/svapp.hxx> + +#include "Communicator.hxx" +#include "Listener.hxx" +#include "ImagePreparer.hxx" +#include "Transmitter.hxx" + +#include <com/sun/star/presentation/XSlideShowController.hpp> + +using namespace sd; +using namespace ::com::sun::star::presentation; + +Listener::Listener( const ::rtl::Reference<Communicator>& rCommunicator, + sd::Transmitter *aTransmitter ): + mCommunicator( rCommunicator ), + pTransmitter( nullptr ) +{ + pTransmitter = aTransmitter; +} + +Listener::~Listener() +{ +} + +void Listener::init( const css::uno::Reference< css::presentation::XSlideShowController >& aController) +{ + if ( aController.is() ) + { + mController.set( aController ); + aController->addSlideShowListener( this ); + + sal_Int32 aSlides = aController->getSlideCount(); + sal_Int32 aCurrentSlide = aController->getCurrentSlideIndex(); + OString aBuffer = "slideshow_started\n" + + OString::number( aSlides ) + "\n" + + OString::number( aCurrentSlide ) + "\n\n"; + + pTransmitter->addMessage( aBuffer, + Transmitter::PRIORITY_HIGH ); + + { + SolarMutexGuard aGuard; + /* ImagePreparer* pPreparer = */ new ImagePreparer( aController, pTransmitter ); + } + } + else + { + SAL_INFO( "sdremote", "Listener::init but no controller - so no preview push queued" ); + } +} + +//----- XAnimationListener ---------------------------------------------------- + +void SAL_CALL Listener::beginEvent(const css::uno::Reference< + css::animations::XAnimationNode >& ) +{} + +void SAL_CALL Listener::endEvent( const css::uno::Reference< + css::animations::XAnimationNode >& ) +{} + +void SAL_CALL Listener::repeat( const css::uno::Reference< + css::animations::XAnimationNode >&, ::sal_Int32 ) +{} + +//----- XSlideShowListener ---------------------------------------------------- + +void SAL_CALL Listener::paused() +{ +} + +void SAL_CALL Listener::resumed() +{ +} + +void SAL_CALL Listener::slideEnded (sal_Bool) +{ +} + +void SAL_CALL Listener::hyperLinkClicked (const OUString &) +{ +} + +void SAL_CALL Listener::slideTransitionStarted() +{ + sal_Int32 aSlide = mController->getCurrentSlideIndex(); + + OString aBuilder = "slide_updated\n" + + OString::number( aSlide ) + + "\n\n"; + + if ( pTransmitter ) + { + pTransmitter->addMessage( aBuilder, + Transmitter::PRIORITY_HIGH ); + } +} + +void SAL_CALL Listener::slideTransitionEnded() +{ +} + +void SAL_CALL Listener::slideAnimationsEnded() +{ +} + +void Listener::disposing(std::unique_lock<std::mutex>&) +{ + pTransmitter = nullptr; + if ( mController.is() ) + { + mController->removeSlideShowListener( this ); + mController = nullptr; + } + mCommunicator->informListenerDestroyed(); +} + +void SAL_CALL Listener::disposing ( + const css::lang::EventObject&) +{ + dispose(); +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/Listener.hxx b/sd/source/ui/remotecontrol/Listener.hxx new file mode 100644 index 000000000..58d7483f6 --- /dev/null +++ b/sd/source/ui/remotecontrol/Listener.hxx @@ -0,0 +1,62 @@ +/* -*- 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/. + */ +#pragma once + +#include <sal/config.h> +#include <com/sun/star/presentation/XSlideShowListener.hpp> + +#include <rtl/ref.hxx> +#include <comphelper/compbase.hxx> +#include <com/sun/star/uno/Reference.hxx> + +namespace com::sun::star::presentation { class XSlideShowController; } +namespace sd { class Communicator; } +namespace sd { class Transmitter; } + +namespace sd { +/** + * Slide show listener. This class can also be used for anything else that is + * specific to the lifetime of one slideshow while a client is connected. + */ +class Listener + : public comphelper::WeakComponentImplHelper< css::presentation::XSlideShowListener > +{ +public: + Listener( const ::rtl::Reference<Communicator>& rServer, sd::Transmitter *aTransmitter ); + virtual ~Listener() override; + void init( const css::uno::Reference< css::presentation::XSlideShowController >& aController ); + + // XAnimationListener + virtual void SAL_CALL beginEvent(const css::uno::Reference< + css::animations::XAnimationNode >& rNode ) override; + virtual void SAL_CALL endEvent( const css::uno::Reference< + css::animations::XAnimationNode >& rNode ) override; + virtual void SAL_CALL repeat( const css::uno::Reference< + css::animations::XAnimationNode >& rNode, ::sal_Int32 Repeat ) override; + + // XSlideShowListener + virtual void SAL_CALL paused( ) override; + virtual void SAL_CALL resumed( ) override; + virtual void SAL_CALL slideTransitionStarted( ) override; + virtual void SAL_CALL slideTransitionEnded( ) override; + virtual void SAL_CALL slideAnimationsEnded( ) override; + virtual void SAL_CALL slideEnded(sal_Bool bReverse) override; + virtual void SAL_CALL hyperLinkClicked( const OUString& hyperLink ) override; + + // XEventListener + virtual void disposing(std::unique_lock<std::mutex>&) override; + virtual void SAL_CALL disposing (const css::lang::EventObject& rEvent) override; + +private: + rtl::Reference<Communicator> mCommunicator; + sd::Transmitter *pTransmitter; + css::uno::Reference< css::presentation::XSlideShowController > mController; +}; +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/OSXBluetooth.h b/sd/source/ui/remotecontrol/OSXBluetooth.h new file mode 100644 index 000000000..64f095f6c --- /dev/null +++ b/sd/source/ui/remotecontrol/OSXBluetooth.h @@ -0,0 +1,30 @@ +/* -*- Mode: ObjC; 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/. + */ + +#pragma once + +#import <IOBluetooth/objc/IOBluetoothRFCOMMChannel.h> + +#include "IBluetoothSocket.hxx" +#include "Communicator.hxx" +#include "OSXBluetoothWrapper.hxx" + +@interface ChannelDelegate : NSObject<IOBluetoothRFCOMMChannelDelegate> +{ + sd::Communicator* pCommunicator; + sd::OSXBluetoothWrapper* pSocket; +} + +- (id) initWithCommunicatorAndSocket:(sd::Communicator*)communicator socket:(sd::OSXBluetoothWrapper*)socket; +- (void) rfcommChannelData:(IOBluetoothRFCOMMChannel*)rfcommChannel data:(void *)dataPointer length:(size_t)dataLength; +- (void) rfcommChannelClosed:(IOBluetoothRFCOMMChannel*)rfcommChannel; + +@end + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/OSXBluetooth.mm b/sd/source/ui/remotecontrol/OSXBluetooth.mm new file mode 100644 index 000000000..8b705c50b --- /dev/null +++ b/sd/source/ui/remotecontrol/OSXBluetooth.mm @@ -0,0 +1,53 @@ +/* -*- Mode: ObjC; 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/. + */ +#include <osl/conditn.hxx> +#include <sal/log.hxx> + +#include <premac.h> +#import <IOBluetooth/objc/IOBluetoothRFCOMMChannel.h> +#include <postmac.h> + +#include "OSXBluetooth.h" + +@implementation ChannelDelegate + +- (id) initWithCommunicatorAndSocket:(sd::Communicator*)communicator socket:(sd::OSXBluetoothWrapper*)socket +{ + pCommunicator = communicator; + pSocket = socket; + return self; +} + +- (void) rfcommChannelData:(IOBluetoothRFCOMMChannel*)rfcommChannel data:(void *)dataPointer length:(size_t)dataLength +{ + (void) rfcommChannel; + + if ( pSocket ) + { + pSocket->appendData(dataPointer, dataLength); + } +} + +- (void) rfcommChannelClosed:(IOBluetoothRFCOMMChannel*)rfcommChannel +{ + (void) rfcommChannel; + + SAL_INFO( "sdremote.bluetooth", "ChannelDelegate::rfcommChannelClosed()"); + + if ( pSocket ) + { + pSocket->channelClosed(); + } + pCommunicator = nullptr; + pSocket = nullptr; +} + +@end + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/OSXBluetoothWrapper.hxx b/sd/source/ui/remotecontrol/OSXBluetoothWrapper.hxx new file mode 100644 index 000000000..26e1349f0 --- /dev/null +++ b/sd/source/ui/remotecontrol/OSXBluetoothWrapper.hxx @@ -0,0 +1,38 @@ +/* -*- 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/. + */ + +#pragma once + +#include <rtl/string.hxx> +#include <osl/conditn.hxx> +#include <osl/mutex.hxx> +#include <vector> + +#include "IBluetoothSocket.hxx" + +namespace sd +{ + class OSXBluetoothWrapper : public IBluetoothSocket + { + IOBluetoothRFCOMMChannel* mpChannel; + int mnMTU; + osl::Condition mHaveBytes; + osl::Mutex mMutex; + std::vector<char> mBuffer; + + public: + OSXBluetoothWrapper( IOBluetoothRFCOMMChannel* channel ); + virtual sal_Int32 readLine( OString& aLine ) override; + virtual sal_Int32 write( const void* pBuffer, sal_uInt32 len ) override; + void appendData(void* pBuffer, size_t len ); + void channelClosed(); + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/OSXNetworkService.h b/sd/source/ui/remotecontrol/OSXNetworkService.h new file mode 100644 index 000000000..7298d901b --- /dev/null +++ b/sd/source/ui/remotecontrol/OSXNetworkService.h @@ -0,0 +1,30 @@ +/* -*- Mode: ObjC; 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/. + */ + +#pragma once + +#include <sys/socket.h> +#include <netinet/in.h> + +#include <premac.h> +#import <CoreFoundation/CoreFoundation.h> +#import <Foundation/NSNetServices.h> +#import <Foundation/NSRunLoop.h> +#include <postmac.h> + +@interface OSXBonjourService : NSObject <NSNetServiceDelegate> +{ + NSNetService* netService; +} + +- (void)publishImpressRemoteServiceOnLocalNetworkWithName:(NSString*)sName; + +@end + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/OSXNetworkService.hxx b/sd/source/ui/remotecontrol/OSXNetworkService.hxx new file mode 100644 index 000000000..78ab13eff --- /dev/null +++ b/sd/source/ui/remotecontrol/OSXNetworkService.hxx @@ -0,0 +1,43 @@ +/* -*- 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/. + */ +#pragma once + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <algorithm> +#include <vector> +#include <iostream> + +#include <osl/conditn.hxx> +#include <premac.h> +#import <CoreFoundation/CoreFoundation.h> +#include <postmac.h> +#import "OSXNetworkService.h" + +#include "ZeroconfService.hxx" + +namespace sd { + class OSXNetworkService : public ZeroconfService + { + private: + OSXBonjourService *osxservice; + public: + OSXNetworkService(const std::string& aname = "", unsigned int aport = 1599) + : ZeroconfService(aname, aport){} + + void clear() override { + [osxservice dealloc]; + } + void setup() override { + osxservice = [[OSXBonjourService alloc] init]; + [osxservice publishImpressRemoteServiceOnLocalNetworkWithName: @""]; + }; + }; +} diff --git a/sd/source/ui/remotecontrol/OSXNetworkService.mm b/sd/source/ui/remotecontrol/OSXNetworkService.mm new file mode 100644 index 000000000..51cbd8c99 --- /dev/null +++ b/sd/source/ui/remotecontrol/OSXNetworkService.mm @@ -0,0 +1,43 @@ +/* -*- Mode: ObjC; 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/. + */ +#include <osl/conditn.hxx> + +#include <premac.h> + #import <CoreFoundation/CoreFoundation.h> + #import "OSXNetworkService.h" +#include <postmac.h> + +@implementation OSXBonjourService + +- (void) publishImpressRemoteServiceOnLocalNetworkWithName:(NSString *)sName +{ + netService = [[NSNetService alloc] initWithDomain:@"local" type:@"_impressremote._tcp" name:sName port:1599]; + + if (netService != nil) + { + [netService setDelegate:self]; + [netService scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [netService publish]; + } +} + +-(void)netService:(NSNetService *)aNetService + didNotPublish:(NSDictionary *)dict { + NSLog(@"Service %p did not publish: %@", aNetService, dict); +} + +- (void)dealloc { + [netService stop]; + [netService release]; + [super dealloc]; +} + +@end + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/Receiver.cxx b/sd/source/ui/remotecontrol/Receiver.cxx new file mode 100644 index 000000000..dd92e8e99 --- /dev/null +++ b/sd/source/ui/remotecontrol/Receiver.cxx @@ -0,0 +1,207 @@ +/* -*- 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/. + */ +#include "Receiver.hxx" +#include <com/sun/star/presentation/XSlideShowController.hpp> +#include <com/sun/star/presentation/XPresentationSupplier.hpp> +#include <com/sun/star/presentation/XPresentation2.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/uno/RuntimeException.hpp> + +#include <comphelper/processfactory.hxx> +#include <sal/log.hxx> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <vcl/svapp.hxx> +#include <tools/diagnose_ex.h> + +using namespace sd; +using namespace ::osl; +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::presentation; +using namespace ::com::sun::star::beans; + +Receiver::Receiver( Transmitter *aTransmitter ) : Timer("sd Receiver") +{ + pTransmitter = aTransmitter; + SetTimeout( 0 ); +} + +Receiver::~Receiver() +{ +} + +// Bounce the commands to the main thread to avoid threading woes +void Receiver::pushCommand( const std::vector<OString> &rCommand ) +{ + SolarMutexGuard aGuard; + maExecQueue.push_back( rCommand ); + Start(); +} + +void Receiver::Invoke() +{ + if( !maExecQueue.empty() ) + { + std::vector< OString > aCommands( maExecQueue.front() ); + maExecQueue.pop_front(); + if( !aCommands.empty() ) + executeCommand( aCommands ); + Start(); + } + else + Stop(); +} + +void Receiver::executeCommand( const std::vector<OString> &aCommand ) +{ + uno::Reference<presentation::XSlideShowController> xSlideShowController; + uno::Reference<presentation::XPresentation2> xPresentation; + uno::Reference<presentation::XSlideShow> xSlideShow; + try { + uno::Reference< frame::XDesktop2 > xFramesSupplier = frame::Desktop::create( ::comphelper::getProcessComponentContext() ); + uno::Reference< frame::XFrame > xFrame ( xFramesSupplier->getActiveFrame(), uno::UNO_SET_THROW ); + uno::Reference<presentation::XPresentationSupplier> xPS ( xFrame->getController()->getModel(), uno::UNO_QUERY_THROW); + xPresentation.set( xPS->getPresentation(), uno::UNO_QUERY_THROW); + // Throws an exception if no slideshow running + xSlideShowController.set( xPresentation->getController(), uno::UNO_SET_THROW ); + xSlideShow.set( xSlideShowController->getSlideShow(), uno::UNO_SET_THROW ); + } + catch (uno::RuntimeException &) + { + } + + if ( aCommand[0] == "transition_next" ) + { + if ( xSlideShowController.is() ) + xSlideShowController->gotoNextEffect(); + } + else if ( aCommand[0] == "transition_previous" ) + { + if ( xSlideShowController.is() ) + xSlideShowController->gotoPreviousEffect(); + } + else if ( aCommand[0] == "goto_slide" ) + { + // FIXME: if 0 returned, then not a valid number + sal_Int32 aSlide = aCommand[1].toInt32(); + if ( xSlideShowController.is() && + xSlideShowController->getCurrentSlideIndex() != aSlide ) + { + xSlideShowController->gotoSlideIndex( aSlide ); + } + } + else if ( aCommand[0] == "presentation_start" ) + { + if ( xPresentation.is() ) + xPresentation->start(); + } + else if ( aCommand[0] == "presentation_stop" ) + { + if ( xPresentation.is() ) + xPresentation->end(); + } + else if ( aCommand[0] == "presentation_blank_screen" ) + { + if ( aCommand.size() > 1 ) + { +// aColour = FIXME: get the colour in some format from this string +// Determine the formatting first. + } + if ( xSlideShowController.is() ) + { + xSlideShowController->blankScreen( 0 ); // Default is black + } + } + else if (aCommand[0] == "pointer_started" ) + { + // std::cerr << "pointer_started" << std::endl; + float x = aCommand[1].toFloat(); + float y = aCommand[2].toFloat(); + SolarMutexGuard aSolarGuard; + + const css::geometry::RealPoint2D pos(x,y); + // std::cerr << "Pointer at ("<<pos.X<<","<<pos.Y<<")" << std::endl; + + if (xSlideShow.is()) + { + try + { + // std::cerr << "pointer_coordination in the is" << std::endl; + xSlideShow->setProperty(beans::PropertyValue("PointerPosition", -1, Any(pos), + beans::PropertyState_DIRECT_VALUE)); + } + catch (Exception&) + { + TOOLS_WARN_EXCEPTION("sdremote", "sd::SlideShowImpl::setPointerPosition()"); + } + + try + { + xSlideShow->setProperty(beans::PropertyValue("PointerVisible", -1, Any(true), + beans::PropertyState_DIRECT_VALUE)); + } + catch (Exception&) + { + TOOLS_WARN_EXCEPTION("sdremote", "sd::SlideShowImpl::setPointerMode()"); + } + } + + SAL_INFO( "sdremote", "Pointer started, we display the pointer on screen" ); + } + else if (aCommand[0] == "pointer_dismissed" ) + { + SolarMutexGuard aSolarGuard; + if (xSlideShow.is()) try + { + xSlideShow->setProperty( + beans::PropertyValue( "PointerVisible" , + -1, + Any( false ), + beans::PropertyState_DIRECT_VALUE ) ); + } + catch ( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sdremote", "sd::SlideShowImpl::setPointerMode()" ); + } + + SAL_INFO( "sdremote", "Pointer dismissed, we hide the pointer on screen" ); + } + else if (aCommand[0] == "pointer_coordination" ) + { + float x = aCommand[1].toFloat(); + float y = aCommand[2].toFloat(); + + SAL_INFO( "sdremote", "Pointer at ("<<x<<","<<y<<")" ); + const css::geometry::RealPoint2D pos(x,y); + + SolarMutexGuard aSolarGuard; + if (xSlideShow.is()) try + { + xSlideShow->setProperty( + beans::PropertyValue( "PointerPosition" , + -1, + Any( pos ), + beans::PropertyState_DIRECT_VALUE ) ); + } + catch ( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sdremote", "sd::SlideShowImpl::setPointerPosition()" ); + } + } + else if ( aCommand[0] == "presentation_resume" ) + { + if ( xSlideShowController.is() ) + { + xSlideShowController->resume(); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/Receiver.hxx b/sd/source/ui/remotecontrol/Receiver.hxx new file mode 100644 index 000000000..d3fadf0da --- /dev/null +++ b/sd/source/ui/remotecontrol/Receiver.hxx @@ -0,0 +1,37 @@ +/* -*- 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/. + */ +#pragma once + +#include <rtl/string.hxx> +#include <vcl/timer.hxx> + +#include <vector> +#include <deque> + +namespace sd { class Transmitter; } + +namespace sd +{ + +// Timer is protected by the solar mutex => so are we. +class Receiver : private Timer +{ + std::deque< std::vector< OString > > maExecQueue; +public: + explicit Receiver( Transmitter *aTransmitter ); + virtual ~Receiver() override; + virtual void Invoke() override; + void pushCommand( const std::vector<OString> &rCommand ); + static void executeCommand( const std::vector<OString> &aCommand ); + +private: + Transmitter *pTransmitter; +}; + +} diff --git a/sd/source/ui/remotecontrol/Server.cxx b/sd/source/ui/remotecontrol/Server.cxx new file mode 100644 index 000000000..53bf0352c --- /dev/null +++ b/sd/source/ui/remotecontrol/Server.cxx @@ -0,0 +1,373 @@ +/* -*- 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/. + */ + +#include <algorithm> +#include <vector> + +#include <officecfg/Office/Impress.hxx> + +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> + +#include <comphelper/processfactory.hxx> +#include <comphelper/configuration.hxx> +#include <comphelper/sequence.hxx> +#include <sal/log.hxx> +#include <vcl/svapp.hxx> +#include <osl/socket.hxx> + +#include <sddll.hxx> + +#include "DiscoveryService.hxx" +#include "Listener.hxx" +#include <RemoteServer.hxx> +#include "BluetoothServer.hxx" +#include "Communicator.hxx" +#include "BufferedStreamSocket.hxx" + +using namespace sd; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::osl; +using namespace ::comphelper; + +namespace sd { + /** + * Used to keep track of clients that have attempted to connect, but haven't + * yet been approved. + */ + struct ClientInfoInternal: + ClientInfo + { + BufferedStreamSocket *mpStreamSocket; + OUString mPin; + + ClientInfoInternal( const OUString& rName, + BufferedStreamSocket *pSocket, + const OUString& rPin ): + ClientInfo( rName, false ), + mpStreamSocket( pSocket ), + mPin( rPin ) {} + }; +} + +RemoteServer::RemoteServer() : + Thread( "RemoteServerThread" ) +{ + SAL_INFO( "sdremote", "Instantiated RemoteServer" ); +} + +RemoteServer::~RemoteServer() +{ +} + +void RemoteServer::execute() +{ + SAL_INFO( "sdremote", "RemoteServer::execute called" ); + osl::SocketAddr aAddr( "0.0.0.0", PORT ); + if ( !mSocket.bind( aAddr ) ) + { + SAL_WARN( "sdremote", "bind failed" << mSocket.getErrorAsString() ); + spServer = nullptr; + return; + } + + if ( !mSocket.listen(3) ) + { + SAL_WARN( "sdremote", "listen failed" << mSocket.getErrorAsString() ); + spServer = nullptr; + return; + } + while ( true ) + { + StreamSocket aSocket; + SAL_INFO( "sdremote", "waiting on accept" ); + if ( mSocket.acceptConnection( aSocket ) == osl_Socket_Error ) + { + SAL_WARN( "sdremote", "accept failed" << mSocket.getErrorAsString() ); + spServer = nullptr; + return; // Closed, or other issue. + } + BufferedStreamSocket *pSocket = new BufferedStreamSocket( aSocket); + handleAcceptedConnection( pSocket ); + } + SAL_INFO( "sdremote", "shutting down RemoteServer" ); + spServer = nullptr; // Object is destroyed when Thread::execute() ends. +} + +void RemoteServer::handleAcceptedConnection( BufferedStreamSocket *pSocket ) +{ + OString aLine; + if ( ! ( pSocket->readLine( aLine) + && aLine == "LO_SERVER_CLIENT_PAIR" + && pSocket->readLine( aLine ) ) ) + { + SAL_INFO( "sdremote", "client failed to send LO_SERVER_CLIENT_PAIR, ignoring" ); + delete pSocket; + return; + } + + OString aName( aLine ); + + if ( ! pSocket->readLine( aLine ) ) + { + delete pSocket; + return; + } + OString aPin( aLine ); + + SocketAddr aClientAddr; + pSocket->getPeerAddr( aClientAddr ); + + do + { + // Read off any additional non-empty lines + // We know that we at least have the empty termination line to read. + if ( ! pSocket->readLine( aLine ) ) { + delete pSocket; + return; + } + } + while ( aLine.getLength() > 0 ); + + MutexGuard aGuard( sDataMutex ); + std::shared_ptr< ClientInfoInternal > pClient = + std::make_shared<ClientInfoInternal>( + OStringToOUString( aName, RTL_TEXTENCODING_UTF8 ), + pSocket, OStringToOUString( aPin, RTL_TEXTENCODING_UTF8 ) ); + mAvailableClients.push_back( pClient ); + + // Check if we already have this server. + Reference< XNameAccess > const xConfig = officecfg::Office::Impress::Misc::AuthorisedRemotes::get(); + const Sequence< OUString > aNames = xConfig->getElementNames(); + for ( const auto& rName : aNames ) + { + if ( rName == pClient->mName ) + { + Reference<XNameAccess> xSetItem( xConfig->getByName(rName), UNO_QUERY ); + Any axPin(xSetItem->getByName("PIN")); + OUString sPin; + axPin >>= sPin; + + if ( sPin == pClient->mPin ) { + SAL_INFO( "sdremote", "client found on validated list -- connecting" ); + connectClient( pClient, sPin ); + return; + } + } + } + + // Pin not found so inform the client. + SAL_INFO( "sdremote", "client not found on validated list" ); + pSocket->write( "LO_SERVER_VALIDATING_PIN\n\n", + strlen( "LO_SERVER_VALIDATING_PIN\n\n" ) ); +} + +RemoteServer *sd::RemoteServer::spServer = nullptr; +::osl::Mutex sd::RemoteServer::sDataMutex; +::std::vector<Communicator*> sd::RemoteServer::sCommunicators; + +void RemoteServer::setup() +{ + if (spServer) + return; + + spServer = new RemoteServer(); + spServer->launch(); + +#ifdef ENABLE_SDREMOTE_BLUETOOTH + sd::BluetoothServer::setup( &sCommunicators ); +#endif +} + +void RemoteServer::presentationStarted( const css::uno::Reference< + css::presentation::XSlideShowController > &rController ) +{ + if ( !spServer ) + return; + MutexGuard aGuard( sDataMutex ); + for ( const auto& rpCommunicator : sCommunicators ) + { + rpCommunicator->presentationStarted( rController ); + } +} +void RemoteServer::presentationStopped() +{ + if ( !spServer ) + return; + MutexGuard aGuard( sDataMutex ); + for ( const auto& rpCommunicator : sCommunicators ) + { + rpCommunicator->disposeListener(); + } +} + +void RemoteServer::removeCommunicator( Communicator const * mCommunicator ) +{ + if ( !spServer ) + return; + MutexGuard aGuard( sDataMutex ); + auto aIt = std::find(sCommunicators.begin(), sCommunicators.end(), mCommunicator); + if (aIt != sCommunicators.end()) + sCommunicators.erase( aIt ); +} + +std::vector< std::shared_ptr< ClientInfo > > RemoteServer::getClients() +{ + SAL_INFO( "sdremote", "RemoteServer::getClients() called" ); + std::vector< std::shared_ptr< ClientInfo > > aClients; + if ( spServer ) + { + MutexGuard aGuard( sDataMutex ); + aClients.assign( spServer->mAvailableClients.begin(), + spServer->mAvailableClients.end() ); + } + else + { + SAL_INFO( "sdremote", "No remote server instance => no remote clients" ); + } + // We also need to provide authorised clients (no matter whether or not + // they are actually available), so that they can be de-authorised if + // necessary. We specifically want these to be at the end of the list + // since the user is more likely to be trying to connect a new remote + // than removing an existing remote. + // We can also be sure that pre-authorised clients will not be on the + // available clients list, as they get automatically connected if seen. + // TODO: we should probably add some sort of extra labelling to mark + // authorised AND connected client. + Reference< XNameAccess > const xConfig = officecfg::Office::Impress::Misc::AuthorisedRemotes::get(); + const Sequence< OUString > aNames = xConfig->getElementNames(); + std::transform(aNames.begin(), aNames.end(), std::back_inserter(aClients), + [](const OUString& rName) -> std::shared_ptr<ClientInfo> { + return std::make_shared<ClientInfo>(rName, true); }); + + return aClients; +} + +bool RemoteServer::connectClient( const std::shared_ptr< ClientInfo >& pClient, std::u16string_view aPin ) +{ + SAL_INFO( "sdremote", "RemoteServer::connectClient called" ); + if ( !spServer ) + return false; + + ClientInfoInternal* apClient = dynamic_cast< ClientInfoInternal* >( pClient.get() ); + if ( !apClient ) + // could happen if we try to "connect" an already authorised client + { + return false; + } + + if ( apClient->mPin == aPin ) + { + // Save in settings first + std::shared_ptr< ConfigurationChanges > aChanges = ConfigurationChanges::create(); + Reference< XNameContainer > const xConfig = officecfg::Office::Impress::Misc::AuthorisedRemotes::get( aChanges ); + + Reference<XSingleServiceFactory> xChildFactory ( + xConfig, UNO_QUERY); + Reference<XNameReplace> xChild( xChildFactory->createInstance(), UNO_QUERY); + Any aValue; + if (xChild.is()) + { + // Check whether the client is already saved + Sequence< OUString > aNames = xConfig->getElementNames(); + if (comphelper::findValue(aNames, apClient->mName) != -1) + xConfig->replaceByName( apClient->mName, Any( xChild ) ); + else + xConfig->insertByName( apClient->mName, Any( xChild ) ); + aValue <<= apClient->mPin; + xChild->replaceByName("PIN", aValue); + aChanges->commit(); + } + + Communicator* pCommunicator = new Communicator( std::unique_ptr<IBluetoothSocket>(apClient->mpStreamSocket) ); + MutexGuard aGuard( sDataMutex ); + + sCommunicators.push_back( pCommunicator ); + + auto aIt = std::find(spServer->mAvailableClients.begin(), spServer->mAvailableClients.end(), pClient); + if (aIt != spServer->mAvailableClients.end()) + spServer->mAvailableClients.erase( aIt ); + pCommunicator->launch(); + return true; + } + else + { + return false; + } +} + +void RemoteServer::deauthoriseClient( const std::shared_ptr< ClientInfo >& pClient ) +{ + // TODO: we probably want to forcefully disconnect at this point too? + // But possibly via a separate function to allow just disconnecting from + // the UI. + + SAL_INFO( "sdremote", "RemoteServer::deauthoriseClient called" ); + + if ( !pClient->mbIsAlreadyAuthorised ) + // We can't remove unauthorised clients from the authorised list... + { + return; + } + + std::shared_ptr< ConfigurationChanges > aChanges = ConfigurationChanges::create(); + Reference< XNameContainer > const xConfig = + officecfg::Office::Impress::Misc::AuthorisedRemotes::get( aChanges ); + + xConfig->removeByName( pClient->mName ); + aChanges->commit(); +} + +void SdDLL::RegisterRemotes() +{ + SAL_INFO( "sdremote", "SdDLL::RegisterRemotes called" ); + + // The remote server is likely of no use in headless mode. And as only + // one instance of the server can actually own the appropriate ports its + // probably best to not even try to do so from our headless instance + // (i.e. as to avoid blocking expected usage). + // It could perhaps be argued that we would still need the remote + // server for tiled rendering of presentations, but even then this + // implementation would not be of much use, i.e. would be controlling + // the purely imaginary headless presentation -- instead we'd need + // to have some sort of mechanism of plugging in our tiled rendering + // client to be controlled by the remote server, or provide an + // alternative implementation. + if ( Application::IsHeadlessModeEnabled() ) + return; + + if ( !officecfg::Office::Impress::Misc::Start::EnableSdremote::get() ) + return; + + sd::RemoteServer::setup(); + sd::DiscoveryService::setup(); +} + +void RemoteServer::ensureDiscoverable() +{ + // FIXME: we could also enable listening on our WiFi + // socket here to significantly reduce the attack surface. +#ifdef ENABLE_SDREMOTE_BLUETOOTH + BluetoothServer::ensureDiscoverable(); +#endif +} + +void RemoteServer::restoreDiscoverable() +{ +#ifdef ENABLE_SDREMOTE_BLUETOOTH + BluetoothServer::restoreDiscoverable(); +#endif +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/Transmitter.cxx b/sd/source/ui/remotecontrol/Transmitter.cxx new file mode 100644 index 000000000..cca6a3bee --- /dev/null +++ b/sd/source/ui/remotecontrol/Transmitter.cxx @@ -0,0 +1,86 @@ +/* -*- 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/. + */ +#include "Transmitter.hxx" +#include "IBluetoothSocket.hxx" +#include <sal/log.hxx> + +using namespace osl; // Sockets etc. +using namespace sd; + +Transmitter::Transmitter( IBluetoothSocket* aSocket ) + : pStreamSocket( aSocket ), + mFinishRequested( false ) +{ +} + +void SAL_CALL Transmitter::run() +{ + osl_setThreadName("bluetooth Transmitter"); + + while ( true ) + { + mProcessingRequired.wait(); + + ::osl::MutexGuard aGuard( mMutex ); + + if ( mFinishRequested ) { + return; + } + if ( !mHighPriority.empty() ) + { + OString aMessage( mHighPriority.front() ); + mHighPriority.pop(); + SAL_INFO( "sdremote.bluetooth", "write high prio line '" << aMessage << "'" ); + pStreamSocket->write( aMessage.getStr(), aMessage.getLength() ); + } + else if ( !mLowPriority.empty() ) + { + OString aMessage( mLowPriority.front() ); + mLowPriority.pop(); + SAL_INFO( "sdremote.bluetooth", "write normal line '" << aMessage << "'" ); + pStreamSocket->write( aMessage.getStr(), aMessage.getLength() ); + } + + if ( mLowPriority.empty() && mHighPriority.empty()) + { + mProcessingRequired.reset(); + } + } +} + +void Transmitter::notifyFinished() +{ + ::osl::MutexGuard aGuard( mMutex ); + mFinishRequested = true; + mProcessingRequired.set(); +} + +Transmitter::~Transmitter() +{ +} + +void Transmitter::addMessage( const OString& aMessage, const Priority aPriority ) +{ + ::osl::MutexGuard aGuard( mMutex ); + switch ( aPriority ) + { + case PRIORITY_LOW: + mLowPriority.push( aMessage ); + break; + case PRIORITY_HIGH: + mHighPriority.push( aMessage ); + break; + } + if ( !mProcessingRequired.check() ) + { + mProcessingRequired.set(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/Transmitter.hxx b/sd/source/ui/remotecontrol/Transmitter.hxx new file mode 100644 index 000000000..c24f5a5a4 --- /dev/null +++ b/sd/source/ui/remotecontrol/Transmitter.hxx @@ -0,0 +1,55 @@ +/* -*- 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/. + */ + +#pragma once + +#include <osl/conditn.hxx> +#include <osl/mutex.hxx> +#include <osl/thread.hxx> +#include <rtl/string.hxx> + +#include <queue> + +namespace sd { struct IBluetoothSocket; } + +namespace sd +{ + +class Transmitter +: public osl::Thread +{ +public: + enum Priority { PRIORITY_LOW = 1, PRIORITY_HIGH }; + explicit Transmitter( ::sd::IBluetoothSocket* aSocket ); + virtual ~Transmitter() override; + void addMessage( const OString& aMessage, const Priority aPriority ); + void notifyFinished(); + +private: + virtual void SAL_CALL run() override; + + ::sd::IBluetoothSocket* pStreamSocket; + + ::osl::Condition mProcessingRequired; + + ::osl::Mutex mMutex; + /** + * Used to indicate that we're done and the transmitter loop should exit. + * All access must be guarded my `mMutex`. + */ + bool mFinishRequested; + /// Queue for low priority messages. All access must be guarded my `mMutex`. + std::queue<OString> mLowPriority; + /// Queue for high priority messages. All access must be guarded my `mMutex`. + std::queue<OString> mHighPriority; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/WINNetworkService.cxx b/sd/source/ui/remotecontrol/WINNetworkService.cxx new file mode 100644 index 000000000..bd2decf62 --- /dev/null +++ b/sd/source/ui/remotecontrol/WINNetworkService.cxx @@ -0,0 +1,19 @@ +#include <string> +#include <iostream> +#include "WINNetworkService.hxx" +#include <sal/log.hxx> + +void sd::WINNetworkService::setup() +{ + DNSServiceErrorType err = DNSServiceRegister(&client, 0, 0, nullptr, kREG_TYPE, "local", nullptr, 1599, 1, "", nullptr, this ); + + if (kDNSServiceErr_NoError != err) + SAL_WARN("sdremote.wifi", "DNSServiceRegister failed: " << err); + + // Fail silently +} + +void sd::WINNetworkService::clear() +{ + DNSServiceRefDeallocate(client); +} diff --git a/sd/source/ui/remotecontrol/WINNetworkService.hxx b/sd/source/ui/remotecontrol/WINNetworkService.hxx new file mode 100644 index 000000000..3d096dc0f --- /dev/null +++ b/sd/source/ui/remotecontrol/WINNetworkService.hxx @@ -0,0 +1,23 @@ +#pragma once + +#include <string> +#undef WB_LEFT +#undef WB_RIGHT +#include <dns_sd.h> +#include "ZeroconfService.hxx" + +namespace sd{ + class WINNetworkService : public ZeroconfService + { + private: + DNSServiceRef client; + + public: + WINNetworkService(const std::string& aname = "", unsigned int aport = 1599) + : ZeroconfService(aname, aport), client(nullptr) {} + + void clear() override; + void setup() override; + + }; +} diff --git a/sd/source/ui/remotecontrol/ZeroconfService.hxx b/sd/source/ui/remotecontrol/ZeroconfService.hxx new file mode 100644 index 000000000..a595d0b58 --- /dev/null +++ b/sd/source/ui/remotecontrol/ZeroconfService.hxx @@ -0,0 +1,49 @@ +/* -*- 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/. + */ +#ifndef ZEROCONF_SERVICE +#define ZEROCONF_SERVICE + +#include <string> + +/** +* The port used by LO's custom remote server discovery protocol. +*/ +#define PORT_DISCOVERY 1598 +#define BUFFER_SIZE 200 + +#define kREG_TYPE "_impressremote._tcp" + +struct sockaddr_in; + +typedef unsigned int uint; + +namespace sd{ + + class ZeroconfService + { + protected: + std::string name; + uint port; + + public: + explicit ZeroconfService(const std::string& aname, uint aport) + :name(aname), port(aport){} + virtual ~ZeroconfService(){} + + const std::string& getName() const {return name;} + void setName(const char * n) {name = n;} + + // Clean up the service when closing + virtual void clear() = 0; + // Bonjour for OSX, Avahi for Linux + virtual void setup() = 0; + }; + +} +#endif diff --git a/sd/source/ui/sidebar/AllMasterPagesSelector.cxx b/sd/source/ui/sidebar/AllMasterPagesSelector.cxx new file mode 100644 index 000000000..76e056120 --- /dev/null +++ b/sd/source/ui/sidebar/AllMasterPagesSelector.cxx @@ -0,0 +1,180 @@ +/* -*- 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 "AllMasterPagesSelector.hxx" +#include <ViewShellBase.hxx> +#include "MasterPageContainer.hxx" +#include "MasterPageDescriptor.hxx" +#include <helpids.h> + +#include <set> + +namespace { + +using namespace sd::sidebar; + +int GetURLPriority (const SharedMasterPageDescriptor& rpDescriptor) +{ + int nPriority (0); + switch (rpDescriptor->GetURLClassification()) + { + case MasterPageDescriptor::URLCLASS_USER: nPriority = 0; break; + case MasterPageDescriptor::URLCLASS_LAYOUT: nPriority = 1; break; + case MasterPageDescriptor::URLCLASS_PRESENTATION: nPriority = 2; break; + case MasterPageDescriptor::URLCLASS_OTHER: nPriority = 3; break; + case MasterPageDescriptor::URLCLASS_UNKNOWN: nPriority = 4; break; + default: + case MasterPageDescriptor::URLCLASS_UNDETERMINED: nPriority = 5; break; + } + return nPriority; +} + +class MasterPageDescriptorOrder +{ +public: + bool operator() ( + const SharedMasterPageDescriptor& rp1, + const SharedMasterPageDescriptor& rp2) const + { + if (rp1->meOrigin == MasterPageContainer::DEFAULT) + return true; + else if (rp2->meOrigin == MasterPageContainer::DEFAULT) + return false; + else if (rp1->GetURLClassification() == rp2->GetURLClassification()) + return rp1->mnTemplateIndex < rp2->mnTemplateIndex; + else + return GetURLPriority(rp1) < GetURLPriority(rp2); + } +}; + +} // end of anonymous namespace + +namespace sd::sidebar { + +class AllMasterPagesSelector::SortedMasterPageDescriptorList + : public ::std::set<SharedMasterPageDescriptor,MasterPageDescriptorOrder> +{ +public: + SortedMasterPageDescriptorList() {} +}; + +std::unique_ptr<PanelLayout> AllMasterPagesSelector::Create ( + weld::Widget* pParent, + ViewShellBase& rViewShellBase, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar) +{ + SdDrawDocument* pDocument = rViewShellBase.GetDocument(); + if (pDocument == nullptr) + return nullptr; + + auto pContainer = std::make_shared<MasterPageContainer>(); + + auto xSelector(std::make_unique<AllMasterPagesSelector>( + pParent, + *pDocument, + rViewShellBase, + pContainer, + rxSidebar)); + xSelector->LateInit(); + xSelector->SetHelpId(HID_SD_TASK_PANE_PREVIEW_ALL); + + return xSelector; +} + +AllMasterPagesSelector::AllMasterPagesSelector ( + weld::Widget* pParent, + SdDrawDocument& rDocument, + ViewShellBase& rBase, + const std::shared_ptr<MasterPageContainer>& rpContainer, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar) + : MasterPagesSelector(pParent, rDocument, rBase, rpContainer, rxSidebar, "modules/simpress/ui/masterpagepanelall.ui", "allvalueset"), + mpSortedMasterPages(new SortedMasterPageDescriptorList()) +{ + MasterPagesSelector::Fill(); +} + +AllMasterPagesSelector::~AllMasterPagesSelector() +{ +} + +void AllMasterPagesSelector::Fill (ItemList& rItemList) +{ + if (mpSortedMasterPages->empty()) + UpdateMasterPageList(); + UpdatePageSet(rItemList); +} + +void AllMasterPagesSelector::NotifyContainerChangeEvent ( + const MasterPageContainerChangeEvent& rEvent) +{ + switch (rEvent.meEventType) + { + case MasterPageContainerChangeEvent::EventType::CHILD_ADDED: + AddItem(rEvent.maChildToken); + MasterPagesSelector::Fill(); + break; + + case MasterPageContainerChangeEvent::EventType::INDEX_CHANGED: + mpSortedMasterPages->clear(); + MasterPagesSelector::Fill(); + break; + + default: + MasterPagesSelector::NotifyContainerChangeEvent(rEvent); + break; + } +} + +void AllMasterPagesSelector::UpdateMasterPageList() +{ + mpSortedMasterPages->clear(); + int nTokenCount = mpContainer->GetTokenCount(); + for (int i=0; i<nTokenCount; i++) + AddItem(mpContainer->GetTokenForIndex(i)); +} + +void AllMasterPagesSelector::AddItem (MasterPageContainer::Token aToken) +{ + switch (mpContainer->GetOriginForToken(aToken)) + { + case MasterPageContainer::DEFAULT: + case MasterPageContainer::TEMPLATE: + // Templates are added only when coming from the + // MasterPageContainerFiller so that they have an id which + // defines their place in the list. Templates (pre) loaded from + // RecentlyUsedMasterPages are ignored (they will be loaded + // later by the MasterPageContainerFiller.) + if (mpContainer->GetTemplateIndexForToken(aToken) >= 0) + mpSortedMasterPages->insert(mpContainer->GetDescriptorForToken(aToken)); + break; + + default: + break; + } +} + +void AllMasterPagesSelector::UpdatePageSet (ItemList& rItemList) +{ + for (const auto& rxDescriptor : *mpSortedMasterPages) + rItemList.push_back(rxDescriptor->maToken); +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/AllMasterPagesSelector.hxx b/sd/source/ui/sidebar/AllMasterPagesSelector.hxx new file mode 100644 index 000000000..982a2ec52 --- /dev/null +++ b/sd/source/ui/sidebar/AllMasterPagesSelector.hxx @@ -0,0 +1,80 @@ +/* -*- 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 . + */ + +#pragma once + +#include "MasterPagesSelector.hxx" + +#include <memory> + +namespace sd::sidebar { + +/** Show a list of all available master pages so that the user can assign + them to the document. +*/ +class AllMasterPagesSelector + : public MasterPagesSelector +{ + friend class VclPtrInstance<AllMasterPagesSelector>; +public: + static std::unique_ptr<PanelLayout> Create ( + weld::Widget* pParent, + ViewShellBase& rViewShellBase, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar); + + AllMasterPagesSelector ( + weld::Widget* pParent, + SdDrawDocument& rDocument, + ViewShellBase& rBase, + const std::shared_ptr<MasterPageContainer>& rpContainer, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar); + virtual ~AllMasterPagesSelector() override; + + + /** Scan the set of templates for the ones whose first master pages are + shown by this control and store them in the MasterPageContainer. + */ + virtual void Fill (ItemList& rItemList) override; + +protected: + virtual void NotifyContainerChangeEvent (const MasterPageContainerChangeEvent& rEvent) override; + +private: + /** The list of master pages displayed by this class. + */ + class SortedMasterPageDescriptorList; + ::std::unique_ptr<SortedMasterPageDescriptorList> mpSortedMasterPages; + + void AddItem (MasterPageContainer::Token aToken); + + /** Add all items in the internal master page list into the given list. + */ + void UpdatePageSet (ItemList& rItemList); + + /** Update the internal list of master pages that are to show in the + control. + */ + void UpdateMasterPageList(); + + using MasterPagesSelector::Fill; +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/CurrentMasterPagesSelector.cxx b/sd/source/ui/sidebar/CurrentMasterPagesSelector.cxx new file mode 100644 index 000000000..269099edd --- /dev/null +++ b/sd/source/ui/sidebar/CurrentMasterPagesSelector.cxx @@ -0,0 +1,263 @@ +/* -*- 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 "CurrentMasterPagesSelector.hxx" +#include "PreviewValueSet.hxx" +#include <ViewShellBase.hxx> +#include <DrawViewShell.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include "MasterPageContainer.hxx" +#include "MasterPageContainerProviders.hxx" +#include "MasterPageDescriptor.hxx" +#include <EventMultiplexer.hxx> +#include <DrawDocShell.hxx> +#include <osl/diagnose.h> + +#include <helpids.h> + +#include <set> + +using namespace ::com::sun::star; + +namespace sd::sidebar { + +std::unique_ptr<PanelLayout> CurrentMasterPagesSelector::Create ( + weld::Widget* pParent, + ViewShellBase& rViewShellBase, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar) +{ + SdDrawDocument* pDocument = rViewShellBase.GetDocument(); + if (pDocument == nullptr) + return nullptr; + + auto pContainer = std::make_shared<MasterPageContainer>(); + + auto xSelector(std::make_unique<CurrentMasterPagesSelector>( + pParent, + *pDocument, + rViewShellBase, + pContainer, + rxSidebar)); + xSelector->LateInit(); + xSelector->SetHelpId( HID_SD_TASK_PANE_PREVIEW_CURRENT ); + + return xSelector; +} + +CurrentMasterPagesSelector::CurrentMasterPagesSelector ( + weld::Widget* pParent, + SdDrawDocument& rDocument, + ViewShellBase& rBase, + const std::shared_ptr<MasterPageContainer>& rpContainer, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar) + : MasterPagesSelector (pParent, rDocument, rBase, rpContainer, rxSidebar, "modules/simpress/ui/masterpagepanel.ui", "usedvalueset") +{ + Link<sd::tools::EventMultiplexerEvent&,void> aLink (LINK(this,CurrentMasterPagesSelector,EventMultiplexerListener)); + rBase.GetEventMultiplexer()->AddEventListener(aLink); +} + +CurrentMasterPagesSelector::~CurrentMasterPagesSelector() +{ + if (mrDocument.GetDocSh() != nullptr) + { + EndListening(*mrDocument.GetDocSh()); + } + else + { + OSL_ASSERT(mrDocument.GetDocSh() != nullptr); + } + + Link<sd::tools::EventMultiplexerEvent&,void> aLink (LINK(this,CurrentMasterPagesSelector,EventMultiplexerListener)); + mrBase.GetEventMultiplexer()->RemoveEventListener(aLink); +} + +void CurrentMasterPagesSelector::LateInit() +{ + MasterPagesSelector::LateInit(); + MasterPagesSelector::Fill(); + if (mrDocument.GetDocSh() != nullptr) + { + StartListening(*mrDocument.GetDocSh()); + } + else + { + OSL_ASSERT(mrDocument.GetDocSh() != nullptr); + } +} + +void CurrentMasterPagesSelector::Fill (ItemList& rItemList) +{ + sal_uInt16 nPageCount = mrDocument.GetMasterSdPageCount(PageKind::Standard); + // Remember the names of the master pages that have been inserted to + // avoid double insertion. + ::std::set<OUString> aMasterPageNames; + for (sal_uInt16 nIndex=0; nIndex<nPageCount; nIndex++) + { + SdPage* pMasterPage = mrDocument.GetMasterSdPage (nIndex, PageKind::Standard); + if (pMasterPage == nullptr) + continue; + + // Use the name of the master page to avoid duplicate entries. + OUString sName (pMasterPage->GetName()); + if (!aMasterPageNames.insert(sName).second) + continue; + + // Look up the master page in the container and, when it is not yet + // in it, insert it. + MasterPageContainer::Token aToken = mpContainer->GetTokenForPageObject(pMasterPage); + if (aToken == MasterPageContainer::NIL_TOKEN) + { + SharedMasterPageDescriptor pDescriptor = std::make_shared<MasterPageDescriptor>( + MasterPageContainer::MASTERPAGE, + nIndex, + OUString(), + pMasterPage->GetName(), + OUString(), + pMasterPage->IsPrecious(), + std::make_shared<ExistingPageProvider>(pMasterPage), + std::make_shared<PagePreviewProvider>()); + aToken = mpContainer->PutMasterPage(pDescriptor); + } + + rItemList.push_back(aToken); + } +} + +OUString CurrentMasterPagesSelector::GetContextMenuUIFile() const +{ + return "modules/simpress/ui/currentmastermenu.ui"; +} + +void CurrentMasterPagesSelector::UpdateSelection() +{ + // Iterate over all pages and for the selected ones put the name of + // their master page into a set. + sal_uInt16 nPageCount = mrDocument.GetSdPageCount(PageKind::Standard); + ::std::set<OUString> aNames; + sal_uInt16 nIndex; + bool bLoop (true); + for (nIndex=0; nIndex<nPageCount && bLoop; nIndex++) + { + SdPage* pPage = mrDocument.GetSdPage (nIndex, PageKind::Standard); + if (pPage != nullptr && pPage->IsSelected()) + { + if ( ! pPage->TRG_HasMasterPage()) + { + // One of the pages has no master page. This is an + // indicator for that this method is called in the middle of + // a document change and that the model is not in a valid + // state. Therefore we stop update the selection and wait + // for another call to UpdateSelection when the model is + // valid again. + bLoop = false; + } + else + { + SdrPage& rMasterPage (pPage->TRG_GetMasterPage()); + assert(dynamic_cast<SdPage*>(&rMasterPage)); + aNames.insert(static_cast<SdPage&>(rMasterPage).GetName()); + } + } + } + + // Find the items for the master pages in the set. + sal_uInt16 nItemCount (mxPreviewValueSet->GetItemCount()); + for (nIndex=1; nIndex<=nItemCount && bLoop; nIndex++) + { + OUString sName (mxPreviewValueSet->GetItemText (nIndex)); + if (aNames.find(sName) != aNames.end()) + { + mxPreviewValueSet->SelectItem (nIndex); + } + } +} + +void CurrentMasterPagesSelector::ExecuteCommand(const OString &rIdent) +{ + if (rIdent == "delete") + { + // Check once again that the master page can safely be deleted, + // i.e. is not used. + SdPage* pMasterPage = GetSelectedMasterPage(); + if (pMasterPage != nullptr + && mrDocument.GetMasterPageUserCount(pMasterPage) == 0) + { + // Removing the precious flag so that the following call to + // RemoveUnnecessaryMasterPages() will remove this master page. + pMasterPage->SetPrecious(false); + mrDocument.RemoveUnnecessaryMasterPages(pMasterPage); + } + } + else + MasterPagesSelector::ExecuteCommand(rIdent); +} + +void CurrentMasterPagesSelector::ProcessPopupMenu(weld::Menu& rMenu) +{ + // Disable the delete entry when there is only one master page. + if (mrDocument.GetMasterPageUserCount(GetSelectedMasterPage()) > 0) + rMenu.set_sensitive("delete", false); + + std::shared_ptr<DrawViewShell> pDrawViewShell ( + std::dynamic_pointer_cast<DrawViewShell>(mrBase.GetMainViewShell())); + if (pDrawViewShell && pDrawViewShell->GetEditMode() == EditMode::MasterPage) + { + rMenu.set_sensitive("edit", false); + } + + MasterPagesSelector::ProcessPopupMenu(rMenu); +} + +IMPL_LINK(CurrentMasterPagesSelector,EventMultiplexerListener, + sd::tools::EventMultiplexerEvent&, rEvent, void) +{ + switch (rEvent.meEventId) + { + case EventMultiplexerEventId::CurrentPageChanged: + case EventMultiplexerEventId::EditModeNormal: + case EventMultiplexerEventId::EditModeMaster: + case EventMultiplexerEventId::SlideSortedSelection: + UpdateSelection(); + break; + + case EventMultiplexerEventId::PageOrder: + // This is tricky. If a master page is removed, moved, or + // added we have to wait until both the notes master page + // and the standard master page have been removed, moved, + // or added. We do this by looking at the number of master + // pages which has to be odd in the consistent state (the + // handout master page is always present). If the number is + // even we ignore the hint. + if (mrBase.GetDocument()->GetMasterPageCount()%2 == 1) + MasterPagesSelector::Fill(); + break; + + case EventMultiplexerEventId::ShapeChanged: + case EventMultiplexerEventId::ShapeInserted: + case EventMultiplexerEventId::ShapeRemoved: + InvalidatePreview(static_cast<const SdPage*>(rEvent.mpUserData)); + break; + default: break; + } +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/CurrentMasterPagesSelector.hxx b/sd/source/ui/sidebar/CurrentMasterPagesSelector.hxx new file mode 100644 index 000000000..cd7c27734 --- /dev/null +++ b/sd/source/ui/sidebar/CurrentMasterPagesSelector.hxx @@ -0,0 +1,77 @@ +/* -*- 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 . + */ + +#pragma once + +#include <svl/lstner.hxx> + +#include "MasterPagesSelector.hxx" + +namespace sd::tools { class EventMultiplexerEvent; } + +namespace sd::sidebar { + +/** Show the master pages currently used by a SdDrawDocument. +*/ +class CurrentMasterPagesSelector + : public MasterPagesSelector, + public SfxListener +{ + friend class VclPtrInstance<CurrentMasterPagesSelector>; +public: + static std::unique_ptr<PanelLayout> Create ( + weld::Widget* pParent, + ViewShellBase& rViewShellBase, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar); + + CurrentMasterPagesSelector ( + weld::Widget* pParent, + SdDrawDocument& rDocument, + ViewShellBase& rBase, + const std::shared_ptr<MasterPageContainer>& rpContainer, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar); + virtual ~CurrentMasterPagesSelector() override; + + /** Set the selection so that the master page is selected that is + used by the currently selected page of the document in the + center pane. + */ + void UpdateSelection(); + + /** Copy all master pages that are to be shown into the given list. + */ + virtual void Fill (ItemList& rItemList) override; + + using MasterPagesSelector::Fill; + +protected: + virtual OUString GetContextMenuUIFile() const override; + + virtual void ProcessPopupMenu(weld::Menu& rMenu) override; + virtual void ExecuteCommand(const OString &rIdent) override; + +private: + virtual void LateInit() override; + + DECL_LINK(EventMultiplexerListener,sd::tools::EventMultiplexerEvent&, void); +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/DocumentHelper.cxx b/sd/source/ui/sidebar/DocumentHelper.cxx new file mode 100644 index 000000000..00c028868 --- /dev/null +++ b/sd/source/ui/sidebar/DocumentHelper.cxx @@ -0,0 +1,536 @@ +/* -*- 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 "DocumentHelper.hxx" + +#include <drawdoc.hxx> +#include <DrawDocShell.hxx> +#include <sdpage.hxx> +#include <glob.hxx> +#include <unmovss.hxx> +#include <strings.hrc> +#include <sdresid.hxx> +#include <undoback.hxx> +#include <ViewShell.hxx> +#include <ViewShellBase.hxx> +#include <com/sun/star/drawing/XDrawPagesSupplier.hpp> +#include <com/sun/star/drawing/XDrawPages.hpp> +#include <stlpool.hxx> +#include <svx/xfillit0.hxx> +#include <svx/svdundo.hxx> +#include <tools/diagnose_ex.h> +#include <xmloff/autolayout.hxx> +#include <sal/log.hxx> + +using namespace ::com::sun::star; + +namespace sd::sidebar { + +SdPage* DocumentHelper::CopyMasterPageToLocalDocument ( + SdDrawDocument& rTargetDocument, + SdPage* pMasterPage) +{ + SdPage* pNewMasterPage = nullptr; + + do + { + if (pMasterPage == nullptr) + break; + + // Check the presence of the source document. + SdDrawDocument& rSourceDocument(static_cast< SdDrawDocument& >(pMasterPage->getSdrModelFromSdrPage())); + + // When the given master page already belongs to the target document + // then there is nothing more to do. + if (&rSourceDocument == &rTargetDocument) + { + pNewMasterPage = pMasterPage; + break; + } + + // Test if the master pages of both the slide and its notes page are + // present. This is not the case when we are called during the + // creation of the slide master page because then the notes master + // page is not there. + sal_uInt16 nSourceMasterPageCount = rSourceDocument.GetMasterPageCount(); + if (nSourceMasterPageCount%2 == 0) + // There should be 1 handout page + n slide masters + n notes + // masters = 2*n+1. An even value indicates that a new slide + // master but not yet the notes master has been inserted. + break; + sal_uInt16 nIndex = pMasterPage->GetPageNum(); + if (nSourceMasterPageCount <= nIndex+1) + break; + // Get the slide master page. + if (pMasterPage != static_cast<SdPage*>( + rSourceDocument.GetMasterPage(nIndex))) + break; + // Get the notes master page. + SdPage* pNotesMasterPage = static_cast<SdPage*>( + rSourceDocument.GetMasterPage(nIndex+1)); + if (pNotesMasterPage == nullptr) + break; + + // Check if a master page with the same name as that of the given + // master page already exists. + bool bPageExists (false); + sal_uInt16 nMasterPageCount(rTargetDocument.GetMasterSdPageCount(PageKind::Standard)); + for (sal_uInt16 nMaster=0; nMaster<nMasterPageCount; nMaster++) + { + SdPage* pCandidate = rTargetDocument.GetMasterSdPage (nMaster, PageKind::Standard); + if (pCandidate->GetName() == pMasterPage->GetName()) + { + bPageExists = true; + pNewMasterPage = pCandidate; + break; + } + } + if (bPageExists) + break; + + // Create a new slide (and its notes page.) + uno::Reference<drawing::XDrawPagesSupplier> xSlideSupplier ( + rTargetDocument.getUnoModel(), uno::UNO_QUERY); + if ( ! xSlideSupplier.is()) + break; + uno::Reference<drawing::XDrawPages> xSlides = + xSlideSupplier->getDrawPages(); + if ( ! xSlides.is()) + break; + xSlides->insertNewByIndex (xSlides->getCount()); + + // Set a layout. + SdPage* pSlide = rTargetDocument.GetSdPage( + rTargetDocument.GetSdPageCount(PageKind::Standard)-1, + PageKind::Standard); + if (pSlide == nullptr) + break; + pSlide->SetAutoLayout(AUTOLAYOUT_TITLE, true); + + // Create a copy of the master page and the associated notes + // master page and insert them into our document. + pNewMasterPage = AddMasterPage(rTargetDocument, pMasterPage); + if (pNewMasterPage==nullptr) + break; + SdPage* pNewNotesMasterPage + = AddMasterPage(rTargetDocument, pNotesMasterPage); + if (pNewNotesMasterPage==nullptr) + break; + + // Make the connection from the new slide to the master page + // (and do the same for the notes page.) + rTargetDocument.SetMasterPage ( + rTargetDocument.GetSdPageCount(PageKind::Standard)-1, + pNewMasterPage->GetName(), + &rTargetDocument, + false, // Connect the new master page with the new slide but + // do not modify other (master) pages. + true); + } + while (false); + + // We are not interested in any automatisms for our modified internal + // document. + rTargetDocument.SetChanged(false); + + return pNewMasterPage; +} + +SdPage* DocumentHelper::GetSlideForMasterPage (SdPage const * pMasterPage) +{ + SdPage* pCandidate = nullptr; + + SdDrawDocument* pDocument = nullptr; + if (pMasterPage != nullptr) + pDocument = dynamic_cast< SdDrawDocument* >(&pMasterPage->getSdrModelFromSdrPage()); + + // Iterate over all pages and check if it references the given master + // page. + if (pDocument!=nullptr && pDocument->GetSdPageCount(PageKind::Standard) > 0) + { + // In most cases a new slide has just been inserted so start with + // the last page. + sal_uInt16 nPageIndex (pDocument->GetSdPageCount(PageKind::Standard)-1); + bool bFound (false); + while ( ! bFound) + { + pCandidate = pDocument->GetSdPage( + nPageIndex, + PageKind::Standard); + if (pCandidate != nullptr) + { + if (static_cast<SdPage*>(&pCandidate->TRG_GetMasterPage()) + == pMasterPage) + { + bFound = true; + break; + } + } + + if (nPageIndex == 0) + break; + else + nPageIndex --; + } + + // If no page was found, that referenced the given master page, reset + // the pointer that is returned. + if ( ! bFound) + pCandidate = nullptr; + } + + return pCandidate; +} + +SdPage* DocumentHelper::AddMasterPage ( + SdDrawDocument& rTargetDocument, + SdPage const * pMasterPage) +{ + rtl::Reference<SdPage> pClonedMasterPage; + + if (pMasterPage!=nullptr) + { + try + { + // Duplicate the master page. + pClonedMasterPage = static_cast<SdPage*>(pMasterPage->CloneSdrPage(rTargetDocument).get()); + + // Copy the necessary styles. + SdDrawDocument& rSourceDocument(static_cast< SdDrawDocument& >(pMasterPage->getSdrModelFromSdrPage())); + ProvideStyles(rSourceDocument, rTargetDocument, pClonedMasterPage.get()); + + // Copy the precious flag. + pClonedMasterPage->SetPrecious(pMasterPage->IsPrecious()); + + // Now that the styles are available we can insert the cloned + // master page. + rTargetDocument.InsertMasterPage (pClonedMasterPage.get()); + } + catch(const uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + pClonedMasterPage = nullptr; + } + catch(const ::std::exception& e) + { + pClonedMasterPage = nullptr; + SAL_WARN("sd", "caught general exception " << e.what()); + } + catch(...) + { + pClonedMasterPage = nullptr; + SAL_WARN("sd", "caught general exception"); + } + } + + return pClonedMasterPage.get(); +} + +void DocumentHelper::ProvideStyles ( + SdDrawDocument const & rSourceDocument, + SdDrawDocument& rTargetDocument, + SdPage const * pPage) +{ + // Get the layout name of the given page. + OUString sLayoutName (pPage->GetLayoutName()); + sal_Int32 nIndex = sLayoutName.indexOf(SD_LT_SEPARATOR); + if( nIndex != -1 ) + sLayoutName = sLayoutName.copy(0, nIndex); + + // Copy the style sheet from source to target document. + SdStyleSheetPool* pSourceStyleSheetPool = + static_cast<SdStyleSheetPool*>(rSourceDocument.GetStyleSheetPool()); + SdStyleSheetPool* pTargetStyleSheetPool = + static_cast<SdStyleSheetPool*>(rTargetDocument.GetStyleSheetPool()); + StyleSheetCopyResultVector aCreatedStyles; + pTargetStyleSheetPool->CopyLayoutSheets ( + sLayoutName, + *pSourceStyleSheetPool, + aCreatedStyles); + + // Add an undo action for the copied style sheets. + if( !aCreatedStyles.empty() ) + { + SfxUndoManager* pUndoManager = rTargetDocument.GetDocSh()->GetUndoManager(); + if (pUndoManager != nullptr) + { + pUndoManager->AddUndoAction ( + std::make_unique<SdMoveStyleSheetsUndoAction>( + &rTargetDocument, + aCreatedStyles, + true)); + } + } +} + +void DocumentHelper::AssignMasterPageToPageList ( + SdDrawDocument& rTargetDocument, + SdPage* pMasterPage, + const std::shared_ptr<std::vector<SdPage*> >& rpPageList) +{ + if (pMasterPage == nullptr || !pMasterPage->IsMasterPage()) + return; + + // Make the layout name by stripping out the layout postfix from the + // layout name of the given master page. + OUString sFullLayoutName(pMasterPage->GetLayoutName()); + OUString sBaseLayoutName (sFullLayoutName); + sal_Int32 nIndex = sBaseLayoutName.indexOf(SD_LT_SEPARATOR); + if( nIndex != -1 ) + sBaseLayoutName = sBaseLayoutName.copy(0, nIndex); + + if (rpPageList->empty()) + return; + + // Create a second list that contains only the valid pointers to + // pages for which an assignment is necessary. + ::std::vector<SdPage*> aCleanedList; + for (const auto& rpPage : *rpPageList) + { + OSL_ASSERT(rpPage!=nullptr && &rpPage->getSdrModelFromSdrPage() == &rTargetDocument); + if (rpPage != nullptr && rpPage->GetLayoutName() != sFullLayoutName) + { + aCleanedList.push_back(rpPage); + } + } + if (aCleanedList.empty() ) + return; + + ViewShellId nViewShellId(-1); + if (sd::ViewShell* pViewShell = rTargetDocument.GetDocSh()->GetViewShell()) + nViewShellId = pViewShell->GetViewShellBase().GetViewShellId(); + SfxUndoManager* pUndoMgr = rTargetDocument.GetDocSh()->GetUndoManager(); + if( pUndoMgr ) + pUndoMgr->EnterListAction(SdResId(STR_UNDO_SET_PRESLAYOUT), OUString(), 0, nViewShellId); + + SdPage* pMasterPageInDocument = ProvideMasterPage(rTargetDocument,pMasterPage,rpPageList); + if (pMasterPageInDocument == nullptr) + return; + + // Assign the master pages to the given list of pages. + for (const auto& rpPage : aCleanedList) + { + AssignMasterPageToPage ( + pMasterPageInDocument, + sBaseLayoutName, + rpPage); + } + + if( pUndoMgr ) + pUndoMgr->LeaveListAction(); +} + +SdPage* DocumentHelper::AddMasterPage ( + SdDrawDocument& rTargetDocument, + SdPage const * pMasterPage, + sal_uInt16 nInsertionIndex) +{ + rtl::Reference<SdPage> pClonedMasterPage; + + if (pMasterPage!=nullptr) + { + // Duplicate the master page. + pClonedMasterPage = static_cast<SdPage*>(pMasterPage->CloneSdrPage(rTargetDocument).get()); + + // Copy the precious flag. + pClonedMasterPage->SetPrecious(pMasterPage->IsPrecious()); + + // Copy the necessary styles. + SdDrawDocument& rSourceDocument(static_cast< SdDrawDocument& >(pMasterPage->getSdrModelFromSdrPage())); + ProvideStyles(rSourceDocument, rTargetDocument, pClonedMasterPage.get()); + + // Now that the styles are available we can insert the cloned + // master page. + rTargetDocument.InsertMasterPage (pClonedMasterPage.get(), nInsertionIndex); + + // Adapt the size of the new master page to that of the pages in + // the document. + Size aNewSize (rTargetDocument.GetSdPage(0, pMasterPage->GetPageKind())->GetSize()); + ::tools::Rectangle aBorders ( + pClonedMasterPage->GetLeftBorder(), + pClonedMasterPage->GetUpperBorder(), + pClonedMasterPage->GetRightBorder(), + pClonedMasterPage->GetLowerBorder()); + pClonedMasterPage->ScaleObjects(aNewSize, aBorders, true); + pClonedMasterPage->SetSize(aNewSize); + pClonedMasterPage->CreateTitleAndLayout(true); + } + + return pClonedMasterPage.get(); +} + +/** In here we have to handle three cases: + 1. pPage is a normal slide. We can use SetMasterPage to assign the + master pages to it. + 2. pPage is a master page that is used by at least one slide. We can + assign the master page to these slides. + 3. pPage is a master page that is currently not used by any slide. + We can delete that page and add copies of the given master pages + instead. + + For points 2 and 3 where one master page A is assigned to another B we have + to keep in mind that the master page that page A has already been + inserted into the target document. +*/ +void DocumentHelper::AssignMasterPageToPage ( + SdPage const * pMasterPage, + std::u16string_view rsBaseLayoutName, + SdPage* pPage) +{ + // Leave early when the parameters are invalid. + if (pPage == nullptr || pMasterPage == nullptr) + return; + + SdDrawDocument& rDocument(dynamic_cast< SdDrawDocument& >(pPage->getSdrModelFromSdrPage())); + + if ( ! pPage->IsMasterPage()) + { + // 1. Remove the background object (so that, if it exists, does + // not override the new master page) and assign the master page to + // the regular slide. + rDocument.GetDocSh()->GetUndoManager()->AddUndoAction( + std::make_unique<SdBackgroundObjUndoAction>( + rDocument, *pPage, pPage->getSdrPageProperties().GetItemSet()), + true); + pPage->getSdrPageProperties().PutItem(XFillStyleItem(drawing::FillStyle_NONE)); + + rDocument.SetMasterPage ( + (pPage->GetPageNum()-1)/2, + rsBaseLayoutName, + &rDocument, + false, + false); + } + else + { + // Find first slide that uses the master page. + SdPage* pSlide = nullptr; + sal_uInt16 nPageCount = rDocument.GetSdPageCount(PageKind::Standard); + for (sal_uInt16 nPage=0; nPage<nPageCount&&pSlide==nullptr; nPage++) + { + SdrPage* pCandidate = rDocument.GetSdPage(nPage,PageKind::Standard); + if (pCandidate != nullptr + && pCandidate->TRG_HasMasterPage() + && &(pCandidate->TRG_GetMasterPage()) == pPage) + { + pSlide = static_cast<SdPage*>(pCandidate); + } + } + + if (pSlide != nullptr) + { + // 2. Assign the given master pages to the first slide that was + // found above that uses the master page. + rDocument.SetMasterPage ( + (pSlide->GetPageNum()-1)/2, + rsBaseLayoutName, + &rDocument, + false, + false); + } + else + { + // 3. Replace the master page A by a copy of the given master + // page B. + rDocument.RemoveUnnecessaryMasterPages ( + pPage); + } + } +} + +SdPage* DocumentHelper::ProvideMasterPage ( + SdDrawDocument& rTargetDocument, + SdPage* pMasterPage, + const std::shared_ptr<std::vector<SdPage*> >& rpPageList) +{ + // Make sure that both the master page and its notes master exist + // in the source document. If one is missing then return without + // making any changes. + if (pMasterPage == nullptr) + { + // The caller should make sure that the master page is valid. + OSL_ASSERT(pMasterPage != nullptr); + return nullptr; + } + SdDrawDocument& rSourceDocument(static_cast< SdDrawDocument& >(pMasterPage->getSdrModelFromSdrPage())); + SdPage* pNotesMasterPage = static_cast<SdPage*>( + rSourceDocument.GetMasterPage(pMasterPage->GetPageNum()+1)); + if (pNotesMasterPage == nullptr) + { + // The model is not in a valid state. Maybe a new master page + // is being (not finished yet) created? Return without making + // any changes. + return nullptr; + } + + SdPage* pMasterPageInDocument = nullptr; + // Search for a master page with the same name as the given one in + // the target document. + const OUString sMasterPageLayoutName (pMasterPage->GetLayoutName()); + for (sal_uInt16 nIndex=0,nCount=rTargetDocument.GetMasterPageCount(); nIndex<nCount; ++nIndex) + { + SdPage* pCandidate = static_cast<SdPage*>(rTargetDocument.GetMasterPage(nIndex)); + if (pCandidate && sMasterPageLayoutName == pCandidate->GetLayoutName()) + { + // The requested master page does already exist in the + // target document, return it. + return pCandidate; + } + } + + // The given master page does not already belong to the target + // document so we have to create copies and insert them into the + // target document. + + // Determine the position where the new master pages are inserted. + // By default they are inserted at the end. When we assign to a + // master page then insert after the last of the (selected) pages. + sal_uInt16 nInsertionIndex = rTargetDocument.GetMasterPageCount(); + if (rpPageList->front()->IsMasterPage()) + { + nInsertionIndex = rpPageList->back()->GetPageNum(); + } + + // Clone the master page. + if (&pMasterPage->getSdrModelFromSdrPage() != &rTargetDocument) + { + pMasterPageInDocument = AddMasterPage (rTargetDocument, pMasterPage, nInsertionIndex); + if( rTargetDocument.IsUndoEnabled() ) + rTargetDocument.AddUndo( + rTargetDocument.GetSdrUndoFactory().CreateUndoNewPage(*pMasterPageInDocument)); + } + else + pMasterPageInDocument = pMasterPage; + + // Clone the notes master. + if (&pNotesMasterPage->getSdrModelFromSdrPage() != &rTargetDocument) + { + SdPage* pClonedNotesMasterPage + = AddMasterPage (rTargetDocument, pNotesMasterPage, nInsertionIndex+1); + if( rTargetDocument.IsUndoEnabled() ) + rTargetDocument.AddUndo( + rTargetDocument.GetSdrUndoFactory().CreateUndoNewPage(*pClonedNotesMasterPage)); + } + + return pMasterPageInDocument; +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/DocumentHelper.hxx b/sd/source/ui/sidebar/DocumentHelper.hxx new file mode 100644 index 000000000..61ba5f810 --- /dev/null +++ b/sd/source/ui/sidebar/DocumentHelper.hxx @@ -0,0 +1,108 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> +#include <sal/types.h> + +#include <memory> +#include <string_view> +#include <vector> + +class SdDrawDocument; +class SdPage; + +namespace sd::sidebar { + +/** A collection of methods supporting the handling of master pages. +*/ +class DocumentHelper +{ +public: + /** Return a copy of the given master page in the given document. + */ + static SdPage* CopyMasterPageToLocalDocument ( + SdDrawDocument& rTargetDocument, + SdPage* pMasterPage); + + /** Return and, when not yet present, create a slide that uses the given + master page. + */ + static SdPage* GetSlideForMasterPage (SdPage const * pMasterPage); + + /** Copy the styles used by the given page from the source document to + the target document. + */ + static void ProvideStyles ( + SdDrawDocument const & rSourceDocument, + SdDrawDocument& rTargetDocument, + SdPage const * pPage); + + /** Assign the given master page to the list of pages. + @param rTargetDocument + The document that is the owner of the pages in rPageList. + @param pMasterPage + This master page will usually be a member of the list of all + available master pages as provided by the MasterPageContainer. + @param rPageList + The pages to which to assign the master page. These pages may + be slides or master pages themselves. + */ + static void AssignMasterPageToPageList ( + SdDrawDocument& rTargetDocument, + SdPage* pMasterPage, + const std::shared_ptr<std::vector<SdPage*> >& rPageList); + +private: + static SdPage* AddMasterPage ( + SdDrawDocument& rTargetDocument, + SdPage const * pMasterPage); + static SdPage* AddMasterPage ( + SdDrawDocument& rTargetDocument, + SdPage const * pMasterPage, + sal_uInt16 nInsertionIndex); + static SdPage* ProvideMasterPage ( + SdDrawDocument& rTargetDocument, + SdPage* pMasterPage, + const std::shared_ptr<std::vector<SdPage*> >& rpPageList); + + /** Assign the given master page to the given page. + @param pMasterPage + In contrast to AssignMasterPageToPageList() this page is assumed + to be in the target document, i.e. the same document that pPage + is in. The caller will usually call AddMasterPage() to create a + clone of a master page in another document to create it. + @param rsBaseLayoutName + The layout name of the given master page. It is given so that + it has not to be created on every call. It could be generated + from the given master page, though. + @param pPage + The page to which to assign the master page. It can be a slide + or a master page itself. + */ + static void AssignMasterPageToPage ( + SdPage const * pMasterPage, + std::u16string_view rsBaseLayoutName, + SdPage* pPage); +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/IDisposable.hxx b/sd/source/ui/sidebar/IDisposable.hxx new file mode 100644 index 000000000..e2c1afe27 --- /dev/null +++ b/sd/source/ui/sidebar/IDisposable.hxx @@ -0,0 +1,31 @@ +/* -*- 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 . + */ +#pragma once + +namespace sd::sidebar +{ +class IDisposable +{ +public: + virtual ~IDisposable(); +}; + +} // end of namespace ::sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/ISidebarReceiver.hxx b/sd/source/ui/sidebar/ISidebarReceiver.hxx new file mode 100644 index 000000000..bf51cbe12 --- /dev/null +++ b/sd/source/ui/sidebar/ISidebarReceiver.hxx @@ -0,0 +1,31 @@ +/* -*- 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 . + */ +#pragma once + +namespace sd::sidebar +{ +class ISidebarReceiver +{ +public: + virtual ~ISidebarReceiver(); +}; + +} // end of namespace ::sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/LayoutMenu.cxx b/sd/source/ui/sidebar/LayoutMenu.cxx new file mode 100644 index 000000000..23521df0e --- /dev/null +++ b/sd/source/ui/sidebar/LayoutMenu.cxx @@ -0,0 +1,728 @@ +/* -*- 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 "LayoutMenu.hxx" + +#include <app.hrc> +#include <drawdoc.hxx> +#include <framework/FrameworkHelper.hxx> +#include <strings.hrc> +#include <helpids.h> +#include <pres.hxx> +#include <sdmod.hxx> + +#include <sdpage.hxx> +#include <sdresid.hxx> +#include <unokywds.hxx> +#include <bitmaps.hlst> +#include <tools/gen.hxx> +#include <tools/SlotStateListener.hxx> +#include <DrawController.hxx> +#include <DrawDocShell.hxx> +#include <DrawViewShell.hxx> +#include <EventMultiplexer.hxx> +#include <SlideSorterViewShell.hxx> +#include <ViewShellBase.hxx> +#include <sfx2/sidebar/Theme.hxx> +#include <sal/log.hxx> + +#include <comphelper/processfactory.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/request.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/cjkoptions.hxx> +#include <svl/stritem.hxx> +#include <svl/intitem.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/image.hxx> +#include <xmloff/autolayout.hxx> + +#include <com/sun/star/drawing/framework/XControllerManager.hpp> +#include <com/sun/star/drawing/framework/XView.hpp> +#include <com/sun/star/drawing/framework/ResourceId.hpp> + +#include <vector> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; +using namespace ::sd::slidesorter; +using ::sd::framework::FrameworkHelper; + +namespace sd::sidebar { + +namespace { + +struct snew_slide_value_info +{ + rtl::OUStringConstExpr msBmpResId; + TranslateId mpStrResId; + WritingMode meWritingMode; + AutoLayout maAutoLayout; +}; + +} + +constexpr OUStringLiteral EMPTY = u""; + +const snew_slide_value_info notes[] = +{ + {BMP_SLIDEN_01, STR_AUTOLAYOUT_NOTES, WritingMode_LR_TB, + AUTOLAYOUT_NOTES}, + {EMPTY, {}, WritingMode_LR_TB, AUTOLAYOUT_NONE}, +}; + +const snew_slide_value_info handout[] = +{ + {BMP_SLIDEH_01, STR_AUTOLAYOUT_HANDOUT1, WritingMode_LR_TB, + AUTOLAYOUT_HANDOUT1}, + {BMP_SLIDEH_02, STR_AUTOLAYOUT_HANDOUT2, WritingMode_LR_TB, + AUTOLAYOUT_HANDOUT2}, + {BMP_SLIDEH_03, STR_AUTOLAYOUT_HANDOUT3, WritingMode_LR_TB, + AUTOLAYOUT_HANDOUT3}, + {BMP_SLIDEH_04, STR_AUTOLAYOUT_HANDOUT4, WritingMode_LR_TB, + AUTOLAYOUT_HANDOUT4}, + {BMP_SLIDEH_06, STR_AUTOLAYOUT_HANDOUT6, WritingMode_LR_TB, + AUTOLAYOUT_HANDOUT6}, + {BMP_SLIDEH_09, STR_AUTOLAYOUT_HANDOUT9, WritingMode_LR_TB, + AUTOLAYOUT_HANDOUT9}, + {EMPTY, {}, WritingMode_LR_TB, AUTOLAYOUT_NONE}, +}; + +const snew_slide_value_info standard[] = +{ + {BMP_LAYOUT_EMPTY, STR_AUTOLAYOUT_NONE, WritingMode_LR_TB, AUTOLAYOUT_NONE}, + {BMP_LAYOUT_HEAD03, STR_AUTOLAYOUT_TITLE, WritingMode_LR_TB, AUTOLAYOUT_TITLE}, + {BMP_LAYOUT_HEAD02, STR_AUTOLAYOUT_CONTENT, WritingMode_LR_TB, AUTOLAYOUT_TITLE_CONTENT}, + {BMP_LAYOUT_HEAD02A, STR_AUTOLAYOUT_2CONTENT, WritingMode_LR_TB, AUTOLAYOUT_TITLE_2CONTENT}, + {BMP_LAYOUT_HEAD01, STR_AUTOLAYOUT_ONLY_TITLE, WritingMode_LR_TB, AUTOLAYOUT_TITLE_ONLY}, + {BMP_LAYOUT_TEXTONLY, STR_AUTOLAYOUT_ONLY_TEXT, WritingMode_LR_TB, AUTOLAYOUT_ONLY_TEXT}, + {BMP_LAYOUT_HEAD03B, STR_AUTOLAYOUT_2CONTENT_CONTENT, WritingMode_LR_TB, AUTOLAYOUT_TITLE_2CONTENT_CONTENT}, + {BMP_LAYOUT_HEAD03C, STR_AUTOLAYOUT_CONTENT_2CONTENT, WritingMode_LR_TB, AUTOLAYOUT_TITLE_CONTENT_2CONTENT}, + {BMP_LAYOUT_HEAD03A, STR_AUTOLAYOUT_2CONTENT_OVER_CONTENT,WritingMode_LR_TB, AUTOLAYOUT_TITLE_2CONTENT_OVER_CONTENT}, + {BMP_LAYOUT_HEAD02B, STR_AUTOLAYOUT_CONTENT_OVER_CONTENT, WritingMode_LR_TB, AUTOLAYOUT_TITLE_CONTENT_OVER_CONTENT}, + {BMP_LAYOUT_HEAD04, STR_AUTOLAYOUT_4CONTENT, WritingMode_LR_TB, AUTOLAYOUT_TITLE_4CONTENT}, + {BMP_LAYOUT_HEAD06, STR_AUTOLAYOUT_6CONTENT, WritingMode_LR_TB, AUTOLAYOUT_TITLE_6CONTENT}, + + // vertical + {BMP_LAYOUT_VERTICAL02, STR_AL_VERT_TITLE_TEXT_CHART, WritingMode_TB_RL, AUTOLAYOUT_VTITLE_VCONTENT_OVER_VCONTENT}, + {BMP_LAYOUT_VERTICAL01, STR_AL_VERT_TITLE_VERT_OUTLINE, WritingMode_TB_RL, AUTOLAYOUT_VTITLE_VCONTENT}, + {BMP_LAYOUT_HEAD02, STR_AL_TITLE_VERT_OUTLINE, WritingMode_TB_RL, AUTOLAYOUT_TITLE_VCONTENT}, + {BMP_LAYOUT_HEAD02A, STR_AL_TITLE_VERT_OUTLINE_CLIPART, WritingMode_TB_RL, AUTOLAYOUT_TITLE_2VTEXT}, + {EMPTY, {}, WritingMode_LR_TB, AUTOLAYOUT_NONE} +}; + +class LayoutValueSet : public ValueSet +{ +private: + LayoutMenu& mrMenu; + + /** Calculate the number of displayed rows. This depends on the given + item size, the given number of columns, and the size of the + control. Note that this is not the number of rows managed by the + valueset. This number may be larger. In that case a vertical + scroll bar is displayed. + */ + int CalculateRowCount(const Size& rItemSize, int nColumnCount); + +public: + LayoutValueSet(LayoutMenu& rMenu) + : ValueSet(nullptr) + , mrMenu(rMenu) + { + } + + virtual void Resize() override; + + virtual bool Command(const CommandEvent& rEvent) override; +}; + +LayoutMenu::LayoutMenu ( + weld::Widget* pParent, + ViewShellBase& rViewShellBase, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar) + : PanelLayout( pParent, "LayoutPanel", "modules/simpress/ui/layoutpanel.ui" ), + mrBase(rViewShellBase), + mxLayoutValueSet(new LayoutValueSet(*this)), + mxLayoutValueSetWin(new weld::CustomWeld(*m_xBuilder, "layoutvalueset", *mxLayoutValueSet)), + mbIsMainViewChangePending(false), + mxSidebar(rxSidebar), + mbIsDisposed(false) +{ + implConstruct( *mrBase.GetDocument()->GetDocSh() ); + SAL_INFO("sd.ui", "created LayoutMenu at " << this); + + mxLayoutValueSet->SetStyle(mxLayoutValueSet->GetStyle() | WB_ITEMBORDER | WB_FLATVALUESET | WB_TABSTOP); + + mxLayoutValueSet->SetColor(sfx2::sidebar::Theme::GetColor(sfx2::sidebar::Theme::Color_PanelBackground)); +} + +void LayoutMenu::implConstruct( DrawDocShell& rDocumentShell ) +{ + OSL_ENSURE( mrBase.GetDocument()->GetDocSh() == &rDocumentShell, + "LayoutMenu::implConstruct: hmm?" ); + // if this fires, then my assumption that the rDocumentShell parameter to our first ctor is superfluous ... + (void) rDocumentShell; + + mxLayoutValueSet->SetStyle ( + ( mxLayoutValueSet->GetStyle() & ~(WB_ITEMBORDER) ) + | WB_TABSTOP + | WB_MENUSTYLEVALUESET + | WB_NO_DIRECTSELECT + ); + mxLayoutValueSet->SetExtraSpacing(2); + mxLayoutValueSet->SetSelectHdl (LINK(this, LayoutMenu, ClickHandler)); + InvalidateContent(); + + Link<::sd::tools::EventMultiplexerEvent&,void> aEventListenerLink (LINK(this,LayoutMenu,EventMultiplexerListener)); + mrBase.GetEventMultiplexer()->AddEventListener(aEventListenerLink); + + mxLayoutValueSet->SetHelpId(HID_SD_TASK_PANE_PREVIEW_LAYOUTS); + mxLayoutValueSet->SetAccessibleName(SdResId(STR_TASKPANEL_LAYOUT_MENU_TITLE)); + + Link<const OUString&,void> aStateChangeLink (LINK(this,LayoutMenu,StateChangeHandler)); + mxListener = new ::sd::tools::SlotStateListener( + aStateChangeLink, + Reference<frame::XDispatchProvider>(mrBase.GetController()->getFrame(), UNO_QUERY), + ".uno:VerticalTextState"); +} + +LayoutMenu::~LayoutMenu() +{ + SAL_INFO("sd.ui", "destroying LayoutMenu at " << this); + Dispose(); + mxLayoutValueSetWin.reset(); + mxLayoutValueSet.reset(); +} + +void LayoutMenu::Dispose() +{ + if (mbIsDisposed) + return; + + SAL_INFO("sd.ui", "disposing LayoutMenu at " << this); + + mbIsDisposed = true; + + Reference<lang::XComponent> xComponent (mxListener, UNO_QUERY); + if (xComponent.is()) + xComponent->dispose(); + + Clear(); + Link<tools::EventMultiplexerEvent&,void> aLink (LINK(this,LayoutMenu,EventMultiplexerListener)); + mrBase.GetEventMultiplexer()->RemoveEventListener (aLink); +} + +AutoLayout LayoutMenu::GetSelectedAutoLayout() const +{ + AutoLayout aResult = AUTOLAYOUT_NONE; + + if (!mxLayoutValueSet->IsNoSelection() && mxLayoutValueSet->GetSelectedItemId()!=0) + { + AutoLayout* pLayout = static_cast<AutoLayout*>(mxLayoutValueSet->GetItemData(mxLayoutValueSet->GetSelectedItemId())); + if (pLayout != nullptr) + aResult = *pLayout; + } + + return aResult; +} + +ui::LayoutSize LayoutMenu::GetHeightForWidth (const sal_Int32 nWidth) +{ + sal_Int32 nPreferredHeight = 200; + if (mxLayoutValueSet->GetItemCount()>0) + { + Image aImage = mxLayoutValueSet->GetItemImage(mxLayoutValueSet->GetItemId(0)); + Size aItemSize = mxLayoutValueSet->CalcItemSizePixel(aImage.GetSizePixel()); + if (nWidth>0 && aItemSize.Width()>0) + { + aItemSize.AdjustWidth(8 ); + aItemSize.AdjustHeight(8 ); + int nColumnCount = nWidth / aItemSize.Width(); + if (nColumnCount <= 0) + nColumnCount = 1; + else if (nColumnCount > 4) + nColumnCount = 4; + int nRowCount = (mxLayoutValueSet->GetItemCount() + nColumnCount-1) / nColumnCount; + nPreferredHeight = nRowCount * aItemSize.Height(); + } + } + return ui::LayoutSize(nPreferredHeight,nPreferredHeight,nPreferredHeight); +} + +void LayoutValueSet::Resize() +{ + Size aWindowSize = GetOutputSizePixel(); + if (IsVisible() && aWindowSize.Width() > 0) + { + // Calculate the number of rows and columns. + if (GetItemCount() > 0) + { + Image aImage = GetItemImage(GetItemId(0)); + Size aItemSize = CalcItemSizePixel ( + aImage.GetSizePixel()); + aItemSize.AdjustWidth(8 ); + aItemSize.AdjustHeight(8 ); + int nColumnCount = aWindowSize.Width() / aItemSize.Width(); + if (nColumnCount < 1) + nColumnCount = 1; + else if (nColumnCount > 4) + nColumnCount = 4; + + int nRowCount = CalculateRowCount (aItemSize, nColumnCount); + + SetColCount(nColumnCount); + SetLineCount(nRowCount); + } + } + + ValueSet::Resize(); +} + +bool LayoutValueSet::Command(const CommandEvent& rEvent) +{ + if (rEvent.GetCommand() != CommandEventId::ContextMenu) + return false; + + // As a preparation for the context menu the item under the mouse is + // selected. + if (rEvent.IsMouseEvent()) + { + sal_uInt16 nIndex = GetItemId(rEvent.GetMousePosPixel()); + if (nIndex > 0) + SelectItem(nIndex); + } + + mrMenu.ShowContextMenu(rEvent.IsMouseEvent() ? &rEvent.GetMousePosPixel() : nullptr); + return true; +} + +void LayoutMenu::InsertPageWithLayout (AutoLayout aLayout) +{ + ViewShell* pViewShell = mrBase.GetMainViewShell().get(); + if (pViewShell == nullptr) + return; + + SfxViewFrame* pViewFrame = mrBase.GetViewFrame(); + if (pViewFrame == nullptr) + return; + + SfxDispatcher* pDispatcher = pViewFrame->GetDispatcher(); + if (pDispatcher == nullptr) + return; + + // Call SID_INSERTPAGE with the right arguments. This is because + // the popup menu can not call this slot with arguments directly. + SfxRequest aRequest (CreateRequest(SID_INSERTPAGE, aLayout)); + if (aRequest.GetArgs() != nullptr) + { + pDispatcher->Execute( + SID_INSERTPAGE, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + *aRequest.GetArgs()); + } + UpdateSelection(); +} + +void LayoutMenu::InvalidateContent() +{ + // Throw away the current set and fill the menu anew according to the + // current settings (this includes the support for vertical writing.) + Fill(); + + if (mxSidebar.is()) + mxSidebar->requestLayout(); + + // set selection inside the control during Impress start up + UpdateSelection(); +} + +int LayoutValueSet::CalculateRowCount (const Size&, int nColumnCount) +{ + int nRowCount = 0; + + if (GetItemCount() > 0 && nColumnCount > 0) + { + nRowCount = (GetItemCount() + nColumnCount - 1) / nColumnCount; + if (nRowCount < 1) + nRowCount = 1; + } + + return nRowCount; +} + +IMPL_LINK_NOARG(LayoutMenu, ClickHandler, ValueSet*, void) +{ + AssignLayoutToSelectedSlides( GetSelectedAutoLayout() ); +} + +/** The specified layout is assigned to the current page of the view shell + in the center pane. +*/ +void LayoutMenu::AssignLayoutToSelectedSlides (AutoLayout aLayout) +{ + using namespace ::sd::slidesorter; + using namespace ::sd::slidesorter::controller; + + do + { + // The view shell in the center pane has to be present. + ViewShell* pMainViewShell = mrBase.GetMainViewShell().get(); + if (pMainViewShell == nullptr) + break; + + // Determine if the current view is in an invalid master page mode. + // The handout view is always in master page mode and therefore not + // invalid. + bool bMasterPageMode (false); + switch (pMainViewShell->GetShellType()) + { + case ViewShell::ST_NOTES: + case ViewShell::ST_IMPRESS: + { + DrawViewShell* pDrawViewShell = static_cast<DrawViewShell*>(pMainViewShell); + if (pDrawViewShell->GetEditMode() == EditMode::MasterPage) + bMasterPageMode = true; + break; + } + default: + break; + } + if (bMasterPageMode) + break; + + // Get a list of all selected slides and call the SID_MODIFYPAGE + // slot for all of them. + ::sd::slidesorter::SharedPageSelection pPageSelection; + + // Get a list of selected pages. + // First we try to obtain this list from a slide sorter. This is + // possible only some of the view shells in the center pane. When + // no valid slide sorter is available then ask the main view shell + // for its current page. + SlideSorterViewShell* pSlideSorter = nullptr; + switch (pMainViewShell->GetShellType()) + { + case ViewShell::ST_IMPRESS: + case ViewShell::ST_NOTES: + case ViewShell::ST_SLIDE_SORTER: + pSlideSorter = SlideSorterViewShell::GetSlideSorter(mrBase); + break; + default: + break; + } + if (pSlideSorter != nullptr) + { + // There is a slide sorter visible so get the list of selected pages from it. + pPageSelection = pSlideSorter->GetPageSelection(); + } + + if( (pSlideSorter == nullptr) || !pPageSelection || pPageSelection->empty() ) + { + // No valid slide sorter available. Ask the main view shell for + // its current page. + pPageSelection = std::make_shared<::sd::slidesorter::SlideSorterViewShell::PageSelection>(); + pPageSelection->push_back(pMainViewShell->GetActualPage()); + } + + if (pPageSelection->empty()) + break; + + for (const auto& rpPage : *pPageSelection) + { + if (rpPage == nullptr) + continue; + + // Call the SID_ASSIGN_LAYOUT slot with all the necessary parameters. + SfxRequest aRequest (mrBase.GetViewFrame(), SID_ASSIGN_LAYOUT); + aRequest.AppendItem(SfxUInt32Item (ID_VAL_WHATPAGE, (rpPage->GetPageNum()-1)/2)); + aRequest.AppendItem(SfxUInt32Item (ID_VAL_WHATLAYOUT, aLayout)); + pMainViewShell->ExecuteSlot (aRequest, false); + } + } + while(false); +} + +SfxRequest LayoutMenu::CreateRequest ( + sal_uInt16 nSlotId, + AutoLayout aLayout) +{ + SfxRequest aRequest (mrBase.GetViewFrame(), nSlotId); + + do + { + SdrLayerAdmin& rLayerAdmin (mrBase.GetDocument()->GetLayerAdmin()); + SdrLayerID aBackground (rLayerAdmin.GetLayerID(sUNO_LayerName_background)); + SdrLayerID aBackgroundObject (rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects)); + ViewShell* pViewShell = mrBase.GetMainViewShell().get(); + if (pViewShell == nullptr) + break; + SdPage* pPage = pViewShell->GetActualPage(); + if (pPage == nullptr) + break; + + SdrLayerIDSet aVisibleLayers (pPage->TRG_GetMasterPageVisibleLayers()); + + aRequest.AppendItem( + SfxStringItem (ID_VAL_PAGENAME, OUString()));//pPage->GetName())); + aRequest.AppendItem(SfxUInt32Item (ID_VAL_WHATLAYOUT, aLayout)); + aRequest.AppendItem( + SfxBoolItem(ID_VAL_ISPAGEBACK, aVisibleLayers.IsSet(aBackground))); + aRequest.AppendItem( + SfxBoolItem( + ID_VAL_ISPAGEOBJ, + aVisibleLayers.IsSet(aBackgroundObject))); + } + while (false); + + return aRequest; +} + +void LayoutMenu::Fill() +{ + bool bVertical = SvtCJKOptions::IsVerticalTextEnabled(); + SdDrawDocument* pDocument = mrBase.GetDocument(); + bool bRightToLeft = (pDocument!=nullptr + && pDocument->GetDefaultWritingMode() == WritingMode_RL_TB); + + // Get URL of the view in the center pane. + OUString sCenterPaneViewName; + try + { + Reference<XControllerManager> xControllerManager ( + Reference<XWeak>(&mrBase.GetDrawController()), UNO_QUERY_THROW); + Reference<XResourceId> xPaneId (ResourceId::create( + ::comphelper::getProcessComponentContext(), + FrameworkHelper::msCenterPaneURL)); + Reference<XView> xView (FrameworkHelper::Instance(mrBase)->GetView(xPaneId)); + if (xView.is()) + sCenterPaneViewName = xView->getResourceId()->getResourceURL(); + } + catch (RuntimeException&) + {} + + const snew_slide_value_info* pInfo = nullptr; + if (sCenterPaneViewName == framework::FrameworkHelper::msNotesViewURL) + { + pInfo = notes; + } + else if (sCenterPaneViewName == framework::FrameworkHelper::msHandoutViewURL) + { + pInfo = handout; + } + else if (sCenterPaneViewName == framework::FrameworkHelper::msImpressViewURL + || sCenterPaneViewName == framework::FrameworkHelper::msSlideSorterURL) + { + pInfo = standard; + } + else + { + pInfo = nullptr; + } + + Clear(); + for (sal_uInt16 i=1; pInfo!=nullptr && pInfo->mpStrResId; i++, pInfo++) + { + if ((WritingMode_TB_RL != pInfo->meWritingMode) || bVertical) + { + Image aImg("private:graphicrepository/" + static_cast<const OUString &>(pInfo->msBmpResId)); + + if (bRightToLeft && (WritingMode_TB_RL != pInfo->meWritingMode)) + { // FIXME: avoid interpolating RTL layouts. + BitmapEx aRTL = aImg.GetBitmapEx(); + aRTL.Mirror(BmpMirrorFlags::Horizontal); + aImg = Image(aRTL); + } + + mxLayoutValueSet->InsertItem(i, aImg, SdResId(pInfo->mpStrResId)); + mxLayoutValueSet->SetItemData (i, new AutoLayout(pInfo->maAutoLayout)); + } + } +} + +void LayoutMenu::Clear() +{ + for (size_t nId=1; nId<=mxLayoutValueSet->GetItemCount(); nId++) + delete static_cast<AutoLayout*>(mxLayoutValueSet->GetItemData(nId)); + mxLayoutValueSet->Clear(); +} + +void LayoutMenu::ShowContextMenu(const Point* pPos) +{ + if (SD_MOD()->GetWaterCan()) + return; + + // Determine the position where to show the menu. + Point aMenuPosition; + if (pPos) + { + auto nItemId = mxLayoutValueSet->GetItemId(*pPos); + if (nItemId <= 0) + return; + mxLayoutValueSet->SelectItem(nItemId); + aMenuPosition = *pPos; + } + else + { + if (mxLayoutValueSet->GetSelectedItemId() == sal_uInt16(-1)) + return; + ::tools::Rectangle aBBox(mxLayoutValueSet->GetItemRect(mxLayoutValueSet->GetSelectedItemId())); + aMenuPosition = aBBox.Center(); + } + + // Setup the menu. + ::tools::Rectangle aRect(aMenuPosition, Size(1, 1)); + weld::Widget* pPopupParent = mxLayoutValueSet->GetDrawingArea(); + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pPopupParent, "modules/simpress/ui/layoutmenu.ui")); + std::unique_ptr<weld::Menu> xMenu(xBuilder->weld_menu("menu")); + + // Disable the SID_INSERTPAGE_LAYOUT_MENU item when + // the document is read-only. + const SfxPoolItem* pItem = nullptr; + const SfxItemState aState ( + mrBase.GetViewFrame()->GetDispatcher()->QueryState(SID_INSERTPAGE, pItem)); + if (aState == SfxItemState::DISABLED) + xMenu->set_sensitive("insert", false); + + // Show the menu. + OnMenuItemSelected(xMenu->popup_at_rect(pPopupParent, aRect)); +} + +IMPL_LINK_NOARG(LayoutMenu, StateChangeHandler, const OUString&, void) +{ + InvalidateContent(); +} + +void LayoutMenu::OnMenuItemSelected(std::string_view ident) +{ + if (ident.empty()) + return; + + if (ident == "apply") + { + AssignLayoutToSelectedSlides(GetSelectedAutoLayout()); + } + else if (ident == "insert") + { + // Add arguments to this slot and forward it to the main view + // shell. + InsertPageWithLayout(GetSelectedAutoLayout()); + } +} + +// Selects an appropriate layout of the slide inside control. +// +// Method may be called several times with the same item-id to be selected - +// only once the actually state of the control will be changed. +// +void LayoutMenu::UpdateSelection() +{ + bool bItemSelected = false; + + do + { + // Get current page of main view. + ViewShell* pViewShell = mrBase.GetMainViewShell().get(); + if (pViewShell == nullptr) + break; + + SdPage* pCurrentPage = pViewShell->getCurrentPage(); + if (pCurrentPage == nullptr) + break; + + // Get layout of current page. + AutoLayout aLayout (pCurrentPage->GetAutoLayout()); + if (aLayout<AUTOLAYOUT_START || aLayout>AUTOLAYOUT_END) + break; + + // Find the entry of the menu for to the layout. + const sal_uInt16 nItemCount = mxLayoutValueSet->GetItemCount(); + for (sal_uInt16 nId=1; nId<=nItemCount; nId++) + { + if (*static_cast<AutoLayout*>(mxLayoutValueSet->GetItemData(nId)) == aLayout) + { + // do not set selection twice to the same item + if (mxLayoutValueSet->GetSelectedItemId() != nId) + { + mxLayoutValueSet->SetNoSelection(); + mxLayoutValueSet->SelectItem(nId); + } + + bItemSelected = true; // no need to call SetNoSelection() + break; + } + } + } + while (false); + + if (!bItemSelected) + mxLayoutValueSet->SetNoSelection(); +} + +IMPL_LINK(LayoutMenu, EventMultiplexerListener, ::sd::tools::EventMultiplexerEvent&, rEvent, void) +{ + switch (rEvent.meEventId) + { + // tdf#89890 During changes of the Layout of the slide when focus is not set inside main area + // we do not receive notification EventMultiplexerEventId::CurrentPageChanged, but we receive the following 3 notification types. + // => let's make UpdateSelection() also when some shape is changed (during Layout changes) + case EventMultiplexerEventId::ShapeChanged: + case EventMultiplexerEventId::ShapeInserted: + case EventMultiplexerEventId::ShapeRemoved: + UpdateSelection(); + break; + + case EventMultiplexerEventId::CurrentPageChanged: + case EventMultiplexerEventId::SlideSortedSelection: + UpdateSelection(); + break; + + case EventMultiplexerEventId::MainViewAdded: + mbIsMainViewChangePending = true; + break; + + case EventMultiplexerEventId::MainViewRemoved: + mxLayoutValueSet->Invalidate(); // redraw without focus + break; + + case EventMultiplexerEventId::ConfigurationUpdated: + if (mbIsMainViewChangePending) + { + mbIsMainViewChangePending = false; + InvalidateContent(); + } + break; + + default: + break; + } +} + +void LayoutMenu::DataChanged(const DataChangedEvent& rEvent) +{ + PanelLayout::DataChanged(rEvent); + Fill(); + mxLayoutValueSet->StyleUpdated(); + mxLayoutValueSet->SetColor(sfx2::sidebar::Theme::GetColor(sfx2::sidebar::Theme::Color_PanelBackground)); +} + +} // end of namespace ::sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/LayoutMenu.hxx b/sd/source/ui/sidebar/LayoutMenu.hxx new file mode 100644 index 000000000..4cc916858 --- /dev/null +++ b/sd/source/ui/sidebar/LayoutMenu.hxx @@ -0,0 +1,157 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/sidebar/ILayoutableWindow.hxx> +#include <sfx2/sidebar/PanelLayout.hxx> + +#include <svtools/valueset.hxx> +#include <sfx2/request.hxx> +#include <xmloff/autolayout.hxx> + +namespace com::sun::star::frame +{ +class XStatusListener; +} +namespace com::sun::star::ui +{ +class XSidebar; +} + +namespace sd +{ +class DrawDocShell; +class ViewShellBase; +} + +namespace sd::tools +{ +class EventMultiplexerEvent; +} + +namespace sd::sidebar +{ +class LayoutValueSet; + +class LayoutMenu : public PanelLayout, public sfx2::sidebar::ILayoutableWindow +{ +public: + /** Create a new layout menu. Depending on the given flag it + displays its own scroll bar or lets a surrounding window + handle that. + @param i_pParent + the parent node in the control tree + @param i_rPanelViewShell + the view shell of the task pane. + */ + LayoutMenu(weld::Widget* pParent, ViewShellBase& rViewShellBase, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar); + virtual ~LayoutMenu() override; + + void Dispose(); + + /** Return a numerical value representing the currently selected + layout. + */ + AutoLayout GetSelectedAutoLayout() const; + + // From ILayoutableWindow + virtual css::ui::LayoutSize GetHeightForWidth(const sal_Int32 nWidth) override; + + /** Call this method when the set of displayed layouts is not up-to-date + anymore. It will re-assemble this set according to the current + settings. + */ + void InvalidateContent(); + + /** The context menu is requested over this ShowContextMenu() method. + */ + void ShowContextMenu(const Point* pPos); + + /** Call Fill() when switching to or from high contrast mode so that the + correct set of icons is displayed. + */ + virtual void DataChanged(const DataChangedEvent& rEvent) override; + +private: + ViewShellBase& mrBase; + + std::unique_ptr<LayoutValueSet> mxLayoutValueSet; + std::unique_ptr<weld::CustomWeld> mxLayoutValueSetWin; + + /** If we are asked for the preferred window size, then use this + many columns for the calculation. + */ + css::uno::Reference<css::frame::XStatusListener> mxListener; + bool mbIsMainViewChangePending; + css::uno::Reference<css::ui::XSidebar> mxSidebar; + bool mbIsDisposed; + + /** Fill the value set with the layouts that are applicable to the + current main view shell. + */ + void Fill(); + + /** Remove all items from the value set. + */ + void Clear(); + + /** Assign the given layout to all selected slides of a slide sorter. + If no slide sorter is active then this call is ignored. The slide + sorter in the center pane is preferred if the choice exists. + */ + void AssignLayoutToSelectedSlides(AutoLayout aLayout); + + /** Insert a new page with the given layout. The page is inserted via + the main view shell, i.e. its SID_INSERTPAGE slot is called. If it + does not support this slot then inserting a new page does not take + place. The new page is inserted after the currently active one (the + one returned by ViewShell::GetActualPage().) + */ + void InsertPageWithLayout(AutoLayout aLayout); + + /** Create a request structure that can be used with the SID_INSERTPAGE + and SID_MODIFYPAGE slots. The parameters are set so that the given + layout is assigned to the current page of the main view shell. + @param nSlotId + Supported slots are SID_INSERTPAGE and SID_MODIFYPAGE. + @param aLayout + Layout of the page to insert or to assign. + */ + SfxRequest CreateRequest(sal_uInt16 nSlotId, AutoLayout aLayout); + + /** Select the layout that is used by the current page. + */ + void UpdateSelection(); + + // internal ctor + void implConstruct(DrawDocShell& rDocumentShell); + + /** When clicked then set the current page of the view in the center pane. + */ + DECL_LINK(ClickHandler, ValueSet*, void); + DECL_LINK(StateChangeHandler, const OUString&, void); + DECL_LINK(EventMultiplexerListener, ::sd::tools::EventMultiplexerEvent&, void); + void OnMenuItemSelected(std::string_view ident); +}; + +} // end of namespace ::sd::toolpanel + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageContainer.cxx b/sd/source/ui/sidebar/MasterPageContainer.cxx new file mode 100644 index 000000000..20d852807 --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageContainer.cxx @@ -0,0 +1,958 @@ +/* -*- 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 "MasterPageContainer.hxx" + +#include "MasterPageContainerProviders.hxx" +#include "MasterPageDescriptor.hxx" +#include "MasterPageContainerFiller.hxx" +#include "MasterPageContainerQueue.hxx" +#include <PreviewRenderer.hxx> +#include <tools/SdGlobalResourceContainer.hxx> +#include <strings.hrc> +#include <algorithm> +#include <memory> + +#include <unomodel.hxx> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/util/CloseVetoException.hpp> +#include <comphelper/processfactory.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <sdresid.hxx> +#include <tools/TimerBasedTaskExecution.hxx> +#include <o3tl/safeint.hxx> +#include <osl/mutex.hxx> +#include <osl/getglobalmutex.hxx> +#include <xmloff/autolayout.hxx> +#include <tools/debug.hxx> +#include <osl/diagnose.h> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace { + +typedef ::std::vector<sd::sidebar::SharedMasterPageDescriptor> MasterPageContainerType; + +} // end of anonymous namespace + +namespace sd::sidebar { + +/** Inner implementation class of the MasterPageContainer. +*/ +class MasterPageContainer::Implementation + : public SdGlobalResource, + public MasterPageContainerFiller::ContainerAdapter, + public MasterPageContainerQueue::ContainerAdapter +{ +public: + mutable ::osl::Mutex maMutex; + + static std::weak_ptr<Implementation> mpInstance; + MasterPageContainerType maContainer; + + static std::shared_ptr<Implementation> Instance(); + + void LateInit(); + void AddChangeListener (const Link<MasterPageContainerChangeEvent&,void>& rLink); + void RemoveChangeListener (const Link<MasterPageContainerChangeEvent&,void>& rLink); + void UpdatePreviewSizePixel(); + const Size& GetPreviewSizePixel (PreviewSize eSize) const; + + bool HasToken (Token aToken) const; + SharedMasterPageDescriptor GetDescriptor (MasterPageContainer::Token aToken) const; + virtual Token PutMasterPage (const SharedMasterPageDescriptor& rDescriptor) override; + void InvalidatePreview (Token aToken); + Image GetPreviewForToken ( + Token aToken, + PreviewSize ePreviewSize); + PreviewState GetPreviewState (Token aToken) const; + bool RequestPreview (Token aToken); + + Reference<frame::XModel> GetModel(); + SdDrawDocument* GetDocument(); + + void FireContainerChange ( + MasterPageContainerChangeEvent::EventType eType, + Token aToken); + + virtual bool UpdateDescriptor ( + const SharedMasterPageDescriptor& rpDescriptor, + bool bForcePageObject, + bool bForcePreview, + bool bSendEvents) override; + + void ReleaseDescriptor (Token aToken); + + /** Called by the MasterPageContainerFiller to notify that all master + pages from template documents have been added. + */ + virtual void FillingDone() override; + +private: + Implementation(); + virtual ~Implementation() override; + + class Deleter { public: + void operator() (Implementation* pObject) { delete pObject; } + }; + friend class Deleter; + + enum class InitializationState { NotInitialized, Initializing, Initialized }; + InitializationState meInitializationState; + + std::unique_ptr<MasterPageContainerQueue> mpRequestQueue; + css::uno::Reference<css::frame::XModel> mxModel; + SdDrawDocument* mpDocument; + PreviewRenderer maPreviewRenderer; + /** Remember whether the first page object has already been used to + determine the correct size ratio. + */ + bool mbFirstPageObjectSeen; + + // The widths for the previews contain two pixels for the border that is + // painted around the preview. + static const int SMALL_PREVIEW_WIDTH = 72 + 2; + static const int LARGE_PREVIEW_WIDTH = 2*72 + 2; + + /** This substitution of page preview shows "Preparing preview" and is + shown as long as the actual previews are not being present. + */ + Image maLargePreviewBeingCreated; + Image maSmallPreviewBeingCreated; + + /** This substitution of page preview is shown when a preview can not be + created and thus is not available. + */ + Image maLargePreviewNotAvailable; + Image maSmallPreviewNotAvailable; + + ::std::vector<Link<MasterPageContainerChangeEvent&,void>> maChangeListeners; + + // We have to remember the tasks for initialization and filling in case + // a MasterPageContainer object is destroyed before these tasks have + // been completed. + std::weak_ptr<sd::tools::TimerBasedTaskExecution> mpFillerTask; + + Size maSmallPreviewSizePixel; + Size maLargePreviewSizePixel; + + Image GetPreviewSubstitution(TranslateId pId, PreviewSize ePreviewSize); + + void CleanContainer(); +}; + +//===== MasterPageContainer =================================================== + +std::weak_ptr<MasterPageContainer::Implementation> + MasterPageContainer::Implementation::mpInstance; + +std::shared_ptr<MasterPageContainer::Implementation> + MasterPageContainer::Implementation::Instance() +{ + std::shared_ptr<MasterPageContainer::Implementation> pInstance; + + if (Implementation::mpInstance.expired()) + { + ::osl::GetGlobalMutex aMutexFunctor; + ::osl::MutexGuard aGuard (aMutexFunctor()); + if (Implementation::mpInstance.expired()) + { + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + pInstance = std::shared_ptr<MasterPageContainer::Implementation>( + new MasterPageContainer::Implementation(), + MasterPageContainer::Implementation::Deleter()); + SdGlobalResourceContainer::Instance().AddResource(pInstance); + Implementation::mpInstance = pInstance; + } + else + pInstance = std::shared_ptr<MasterPageContainer::Implementation>( + Implementation::mpInstance); + } + else + { + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + pInstance = std::shared_ptr<MasterPageContainer::Implementation>( + Implementation::mpInstance); + } + + DBG_ASSERT(pInstance != nullptr, + "MasterPageContainer::Implementation::Instance(): instance is nullptr"); + return pInstance; +} + +MasterPageContainer::MasterPageContainer() + : mpImpl(Implementation::Instance()), + mePreviewSize(SMALL) +{ + mpImpl->LateInit(); +} + +MasterPageContainer::~MasterPageContainer() +{ +} + +void MasterPageContainer::AddChangeListener (const Link<MasterPageContainerChangeEvent&,void>& rLink) +{ + mpImpl->AddChangeListener(rLink); +} + +void MasterPageContainer::RemoveChangeListener (const Link<MasterPageContainerChangeEvent&,void>& rLink) +{ + mpImpl->RemoveChangeListener(rLink); +} + +void MasterPageContainer::SetPreviewSize (PreviewSize eSize) +{ + mePreviewSize = eSize; + mpImpl->FireContainerChange( + MasterPageContainerChangeEvent::EventType::SIZE_CHANGED, + NIL_TOKEN); +} + +Size const & MasterPageContainer::GetPreviewSizePixel() const +{ + return mpImpl->GetPreviewSizePixel(mePreviewSize); +} + +MasterPageContainer::Token MasterPageContainer::PutMasterPage ( + const std::shared_ptr<MasterPageDescriptor>& rDescriptor) +{ + return mpImpl->PutMasterPage(rDescriptor); +} + +void MasterPageContainer::AcquireToken (Token aToken) +{ + SharedMasterPageDescriptor pDescriptor = mpImpl->GetDescriptor(aToken); + if (pDescriptor) + { + ++pDescriptor->mnUseCount; + } +} + +void MasterPageContainer::ReleaseToken (Token aToken) +{ + SharedMasterPageDescriptor pDescriptor = mpImpl->GetDescriptor(aToken); + if (!pDescriptor) + return; + + OSL_ASSERT(pDescriptor->mnUseCount>0); + --pDescriptor->mnUseCount; + if (pDescriptor->mnUseCount > 0) + return; + + switch (pDescriptor->meOrigin) + { + case DEFAULT: + case TEMPLATE: + default: + break; + + case MASTERPAGE: + mpImpl->ReleaseDescriptor(aToken); + break; + } +} + +int MasterPageContainer::GetTokenCount() const +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + return mpImpl->maContainer.size(); +} + +bool MasterPageContainer::HasToken (Token aToken) const +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + return mpImpl->HasToken(aToken); +} + +MasterPageContainer::Token MasterPageContainer::GetTokenForIndex (int nIndex) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + Token aResult (NIL_TOKEN); + if (HasToken(nIndex)) + aResult = mpImpl->maContainer[nIndex]->maToken; + return aResult; +} + +MasterPageContainer::Token MasterPageContainer::GetTokenForURL ( + const OUString& sURL) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + Token aResult (NIL_TOKEN); + if (!sURL.isEmpty()) + { + MasterPageContainerType::iterator iEntry ( + ::std::find_if ( + mpImpl->maContainer.begin(), + mpImpl->maContainer.end(), + MasterPageDescriptor::URLComparator(sURL))); + if (iEntry != mpImpl->maContainer.end()) + aResult = (*iEntry)->maToken; + } + return aResult; +} + +MasterPageContainer::Token MasterPageContainer::GetTokenForStyleName (const OUString& sStyleName) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + Token aResult (NIL_TOKEN); + if (!sStyleName.isEmpty()) + { + MasterPageContainerType::iterator iEntry ( + ::std::find_if ( + mpImpl->maContainer.begin(), + mpImpl->maContainer.end(), + MasterPageDescriptor::StyleNameComparator(sStyleName))); + if (iEntry != mpImpl->maContainer.end()) + aResult = (*iEntry)->maToken; + } + return aResult; +} + +MasterPageContainer::Token MasterPageContainer::GetTokenForPageObject ( + const SdPage* pPage) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + Token aResult (NIL_TOKEN); + if (pPage != nullptr) + { + MasterPageContainerType::iterator iEntry ( + ::std::find_if ( + mpImpl->maContainer.begin(), + mpImpl->maContainer.end(), + MasterPageDescriptor::PageObjectComparator(pPage))); + if (iEntry != mpImpl->maContainer.end()) + aResult = (*iEntry)->maToken; + } + return aResult; +} + +OUString MasterPageContainer::GetURLForToken ( + MasterPageContainer::Token aToken) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + SharedMasterPageDescriptor pDescriptor = mpImpl->GetDescriptor(aToken); + if (pDescriptor) + return pDescriptor->msURL; + else + return OUString(); +} + +OUString MasterPageContainer::GetPageNameForToken ( + MasterPageContainer::Token aToken) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + SharedMasterPageDescriptor pDescriptor = mpImpl->GetDescriptor(aToken); + if (pDescriptor) + return pDescriptor->msPageName; + else + return OUString(); +} + +OUString MasterPageContainer::GetStyleNameForToken ( + MasterPageContainer::Token aToken) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + SharedMasterPageDescriptor pDescriptor = mpImpl->GetDescriptor(aToken); + if (pDescriptor) + return pDescriptor->msStyleName; + else + return OUString(); +} + +SdPage* MasterPageContainer::GetPageObjectForToken ( + MasterPageContainer::Token aToken, + bool bLoad) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + SdPage* pPageObject = nullptr; + SharedMasterPageDescriptor pDescriptor = mpImpl->GetDescriptor(aToken); + if (pDescriptor) + { + pPageObject = pDescriptor->mpMasterPage; + if (pPageObject == nullptr) + { + // The page object is not (yet) present. Call + // UpdateDescriptor() to trigger the PageObjectProvider() to + // provide it. + if (bLoad) + mpImpl->GetModel(); + if (mpImpl->UpdateDescriptor(pDescriptor,bLoad,false, true)) + pPageObject = pDescriptor->mpMasterPage; + } + } + return pPageObject; +} + +MasterPageContainer::Origin MasterPageContainer::GetOriginForToken (Token aToken) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + SharedMasterPageDescriptor pDescriptor = mpImpl->GetDescriptor(aToken); + if (pDescriptor) + return pDescriptor->meOrigin; + else + return UNKNOWN; +} + +sal_Int32 MasterPageContainer::GetTemplateIndexForToken (Token aToken) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + SharedMasterPageDescriptor pDescriptor = mpImpl->GetDescriptor(aToken); + if (pDescriptor) + return pDescriptor->mnTemplateIndex; + else + return -1; +} + +std::shared_ptr<MasterPageDescriptor> MasterPageContainer::GetDescriptorForToken ( + MasterPageContainer::Token aToken) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + return mpImpl->GetDescriptor(aToken); +} + +void MasterPageContainer::InvalidatePreview (MasterPageContainer::Token aToken) +{ + mpImpl->InvalidatePreview(aToken); +} + +Image MasterPageContainer::GetPreviewForToken (MasterPageContainer::Token aToken) +{ + return mpImpl->GetPreviewForToken(aToken,mePreviewSize); +} + +MasterPageContainer::PreviewState MasterPageContainer::GetPreviewState (Token aToken) +{ + return mpImpl->GetPreviewState(aToken); +} + +bool MasterPageContainer::RequestPreview (Token aToken) +{ + return mpImpl->RequestPreview(aToken); +} + +//==== Implementation ================================================ + +MasterPageContainer::Implementation::Implementation() + : meInitializationState(InitializationState::NotInitialized), + mpDocument(nullptr), + mbFirstPageObjectSeen(false) +{ + UpdatePreviewSizePixel(); +} + +MasterPageContainer::Implementation::~Implementation() +{ + // When the initializer or filler tasks are still running then we have + // to stop them now in order to prevent them from calling us back. + tools::TimerBasedTaskExecution::ReleaseTask(mpFillerTask); + + mpRequestQueue.reset(); + + uno::Reference<util::XCloseable> xCloseable (mxModel, uno::UNO_QUERY); + if (xCloseable.is()) + { + try + { + xCloseable->close(true); + } + catch (const css::util::CloseVetoException&) + { + } + } + mxModel = nullptr; +} + +void MasterPageContainer::Implementation::LateInit() +{ + const ::osl::MutexGuard aGuard (maMutex); + + if (meInitializationState != InitializationState::NotInitialized) + return; + + meInitializationState = InitializationState::Initializing; + + OSL_ASSERT(Instance().get()==this); + mpRequestQueue.reset(MasterPageContainerQueue::Create( + std::shared_ptr<MasterPageContainerQueue::ContainerAdapter>(Instance()))); + + mpFillerTask = ::sd::tools::TimerBasedTaskExecution::Create( + std::make_shared<MasterPageContainerFiller>(*this), + 5, + 50); + + meInitializationState = InitializationState::Initialized; +} + +void MasterPageContainer::Implementation::AddChangeListener (const Link<MasterPageContainerChangeEvent&,void>& rLink) +{ + const ::osl::MutexGuard aGuard (maMutex); + + ::std::vector<Link<MasterPageContainerChangeEvent&,void>>::iterator iListener ( + ::std::find(maChangeListeners.begin(),maChangeListeners.end(),rLink)); + if (iListener == maChangeListeners.end()) + maChangeListeners.push_back(rLink); + +} + +void MasterPageContainer::Implementation::RemoveChangeListener (const Link<MasterPageContainerChangeEvent&,void>& rLink) +{ + const ::osl::MutexGuard aGuard (maMutex); + + ::std::vector<Link<MasterPageContainerChangeEvent&,void>>::iterator iListener ( + ::std::find(maChangeListeners.begin(),maChangeListeners.end(),rLink)); + if (iListener != maChangeListeners.end()) + maChangeListeners.erase(iListener); +} + +void MasterPageContainer::Implementation::UpdatePreviewSizePixel() +{ + const ::osl::MutexGuard aGuard (maMutex); + + // The default aspect ratio is 4:3 + int nWidth (4); + int nHeight (3); + + // Search for the first entry with an existing master page. + auto iDescriptor = std::find_if(maContainer.begin(), maContainer.end(), + [](const SharedMasterPageDescriptor& rxDescriptor) { + return rxDescriptor != nullptr && rxDescriptor->mpMasterPage != nullptr; + }); + if (iDescriptor != maContainer.end()) + { + Size aPageSize ((*iDescriptor)->mpMasterPage->GetSize()); + OSL_ASSERT(!aPageSize.IsEmpty()); + if (aPageSize.Width() > 0) + nWidth = aPageSize.Width(); + if (aPageSize.Height() > 0) + nHeight = aPageSize.Height(); + mbFirstPageObjectSeen = true; + } + + maSmallPreviewSizePixel.setWidth( SMALL_PREVIEW_WIDTH ); + maLargePreviewSizePixel.setWidth( LARGE_PREVIEW_WIDTH ); + + int nNewSmallHeight ((maSmallPreviewSizePixel.Width()-2) * nHeight / nWidth + 2); + int nNewLargeHeight ((maLargePreviewSizePixel.Width()-2) * nHeight / nWidth + 2); + + if (nNewSmallHeight!=maSmallPreviewSizePixel.Height() + || nNewLargeHeight!=maLargePreviewSizePixel.Height()) + { + maSmallPreviewSizePixel.setHeight( nNewSmallHeight ); + maLargePreviewSizePixel.setHeight( nNewLargeHeight ); + FireContainerChange( + MasterPageContainerChangeEvent::EventType::SIZE_CHANGED, + NIL_TOKEN); + } +} + +const Size& MasterPageContainer::Implementation::GetPreviewSizePixel (PreviewSize eSize) const +{ + if (eSize == SMALL) + return maSmallPreviewSizePixel; + else + return maLargePreviewSizePixel; +} + +MasterPageContainer::Token MasterPageContainer::Implementation::PutMasterPage ( + const SharedMasterPageDescriptor& rpDescriptor) +{ + const ::osl::MutexGuard aGuard (maMutex); + + Token aResult (NIL_TOKEN); + + // Get page object and preview when that is inexpensive. + UpdateDescriptor(rpDescriptor,false,false, false); + + // Look up the new MasterPageDescriptor and either insert it or update + // an already existing one. + MasterPageContainerType::iterator aEntry ( + ::std::find_if ( + maContainer.begin(), + maContainer.end(), + MasterPageDescriptor::AllComparator(rpDescriptor))); + if (aEntry == maContainer.end()) + { + // Insert a new MasterPageDescriptor. + bool bIgnore(rpDescriptor->mpPageObjectProvider == nullptr + && rpDescriptor->msURL.isEmpty()); + + if ( ! bIgnore) + { + CleanContainer(); + + aResult = maContainer.size(); + rpDescriptor->SetToken(aResult); + + // Templates are precious, i.e. we lock them so that they will + // not be destroyed when (temporarily) no one references them. + // They will only be deleted when the container is destroyed. + switch (rpDescriptor->meOrigin) + { + case TEMPLATE: + case DEFAULT: + ++rpDescriptor->mnUseCount; + break; + + default: + break; + } + + maContainer.push_back(rpDescriptor); + aEntry = maContainer.end()-1; + + FireContainerChange(MasterPageContainerChangeEvent::EventType::CHILD_ADDED,aResult); + } + } + else + { + // Update an existing MasterPageDescriptor. + aResult = (*aEntry)->maToken; + std::unique_ptr<std::vector<MasterPageContainerChangeEvent::EventType> > pEventTypes( + (*aEntry)->Update(*rpDescriptor)); + if (pEventTypes != nullptr && !pEventTypes->empty()) + { + // One or more aspects of the descriptor have changed. Send + // appropriate events to the listeners. + UpdateDescriptor(*aEntry,false,false, true); + + for (const auto& rEventType : *pEventTypes) + { + FireContainerChange(rEventType, (*aEntry)->maToken); + } + } + } + + return aResult; +} + +bool MasterPageContainer::Implementation::HasToken (Token aToken) const +{ + return aToken>=0 + && o3tl::make_unsigned(aToken)<maContainer.size() + && maContainer[aToken]; +} + +SharedMasterPageDescriptor MasterPageContainer::Implementation::GetDescriptor (Token aToken) const +{ + if (aToken>=0 && o3tl::make_unsigned(aToken)<maContainer.size()) + return maContainer[aToken]; + else + return SharedMasterPageDescriptor(); +} + +void MasterPageContainer::Implementation::InvalidatePreview (Token aToken) +{ + const ::osl::MutexGuard aGuard (maMutex); + + SharedMasterPageDescriptor pDescriptor (GetDescriptor(aToken)); + if (pDescriptor) + { + pDescriptor->maSmallPreview = Image(); + pDescriptor->maLargePreview = Image(); + RequestPreview(aToken); + } +} + +Image MasterPageContainer::Implementation::GetPreviewForToken ( + MasterPageContainer::Token aToken, + PreviewSize ePreviewSize) +{ + const ::osl::MutexGuard aGuard (maMutex); + + Image aPreview; + PreviewState ePreviewState (GetPreviewState(aToken)); + + SharedMasterPageDescriptor pDescriptor = GetDescriptor(aToken); + + // When the preview is missing but inexpensively creatable then do that + // now. + if (pDescriptor) + { + if (ePreviewState == PS_CREATABLE) + if (UpdateDescriptor(pDescriptor, false,false, true)) + if (pDescriptor->maLargePreview.GetSizePixel().Width() != 0) + ePreviewState = PS_AVAILABLE; + + switch (ePreviewState) + { + case PS_AVAILABLE: + aPreview = pDescriptor->GetPreview(ePreviewSize); + break; + + case PS_PREPARING: + aPreview = GetPreviewSubstitution( + STR_TASKPANEL_PREPARING_PREVIEW_SUBSTITUTION, + ePreviewSize); + break; + + case PS_CREATABLE: + aPreview = GetPreviewSubstitution( + STR_TASKPANEL_PREPARING_PREVIEW_SUBSTITUTION, + ePreviewSize); + break; + + case PS_NOT_AVAILABLE: + aPreview = GetPreviewSubstitution( + STR_TASKPANEL_NOT_AVAILABLE_SUBSTITUTION, + ePreviewSize); + if (ePreviewSize == SMALL) + pDescriptor->maSmallPreview = aPreview; + else + pDescriptor->maLargePreview = aPreview; + break; + } + } + + return aPreview; +} + +MasterPageContainer::PreviewState MasterPageContainer::Implementation::GetPreviewState ( + Token aToken) const +{ + const ::osl::MutexGuard aGuard (maMutex); + + PreviewState eState (PS_NOT_AVAILABLE); + + SharedMasterPageDescriptor pDescriptor = GetDescriptor(aToken); + if (pDescriptor) + { + if (pDescriptor->maLargePreview.GetSizePixel().Width() != 0) + eState = PS_AVAILABLE; + else if (pDescriptor->mpPreviewProvider != nullptr) + { + // The preview does not exist but can be created. When that is + // not expensive then do it at once. + if (mpRequestQueue->HasRequest(aToken)) + eState = PS_PREPARING; + else + eState = PS_CREATABLE; + } + else + eState = PS_NOT_AVAILABLE; + } + + return eState; +} + +bool MasterPageContainer::Implementation::RequestPreview (Token aToken) +{ + SharedMasterPageDescriptor pDescriptor = GetDescriptor(aToken); + if (pDescriptor) + return mpRequestQueue->RequestPreview(pDescriptor); + else + return false; +} + +Reference<frame::XModel> MasterPageContainer::Implementation::GetModel() +{ + const ::osl::MutexGuard aGuard (maMutex); + + if ( ! mxModel.is()) + { + // Create a new model. + mxModel.set( + ::comphelper::getProcessServiceFactory()->createInstance( + "com.sun.star.presentation.PresentationDocument"), + uno::UNO_QUERY); + + // Initialize the model. + uno::Reference<frame::XLoadable> xLoadable (mxModel,uno::UNO_QUERY); + if (xLoadable.is()) + xLoadable->initNew(); + + // Use its tunnel to get a pointer to its core implementation. + uno::Reference<lang::XUnoTunnel> xUnoTunnel (mxModel, uno::UNO_QUERY); + if (auto pSdXImpressDocument = comphelper::getFromUnoTunnel<SdXImpressDocument>(xUnoTunnel)) + { + mpDocument = pSdXImpressDocument->GetDoc(); + } + + // Create a default page. + uno::Reference<drawing::XDrawPagesSupplier> xSlideSupplier (mxModel, uno::UNO_QUERY); + if (xSlideSupplier.is()) + { + uno::Reference<drawing::XDrawPages> xSlides = + xSlideSupplier->getDrawPages(); + if (xSlides.is()) + { + uno::Reference<drawing::XDrawPage> xNewPage (xSlides->insertNewByIndex(0)); + uno::Reference<beans::XPropertySet> xProperties(xNewPage, uno::UNO_QUERY); + if (xProperties.is()) + xProperties->setPropertyValue( + "Layout", + Any(sal_Int16(AUTOLAYOUT_TITLE))); + } + } + } + return mxModel; +} + +SdDrawDocument* MasterPageContainer::Implementation::GetDocument() +{ + GetModel(); + return mpDocument; +} + +Image MasterPageContainer::Implementation::GetPreviewSubstitution ( + TranslateId pId, + PreviewSize ePreviewSize) +{ + const ::osl::MutexGuard aGuard (maMutex); + + Image aPreview; + + if (pId == STR_TASKPANEL_PREPARING_PREVIEW_SUBSTITUTION) + { + Image& rPreview (ePreviewSize==SMALL + ? maSmallPreviewBeingCreated + : maLargePreviewBeingCreated); + if (rPreview.GetSizePixel().Width() == 0) + { + rPreview = maPreviewRenderer.RenderSubstitution( + ePreviewSize==SMALL ? maSmallPreviewSizePixel : maLargePreviewSizePixel, + SdResId(STR_TASKPANEL_PREPARING_PREVIEW_SUBSTITUTION)); + } + aPreview = rPreview; + } + else if (pId == STR_TASKPANEL_NOT_AVAILABLE_SUBSTITUTION) + { + Image& rPreview (ePreviewSize==SMALL + ? maSmallPreviewNotAvailable + : maLargePreviewNotAvailable); + if (rPreview.GetSizePixel().Width() == 0) + { + rPreview = maPreviewRenderer.RenderSubstitution( + ePreviewSize==SMALL ? maSmallPreviewSizePixel : maLargePreviewSizePixel, + SdResId(STR_TASKPANEL_NOT_AVAILABLE_SUBSTITUTION)); + } + aPreview = rPreview; + } + + return aPreview; +} + +void MasterPageContainer::Implementation::CleanContainer() +{ + // Remove the empty elements at the end of the container. The empty + // elements in the middle can not be removed because that would + // invalidate the references still held by others. + int nIndex (maContainer.size()-1); + while (nIndex>=0 && !maContainer[nIndex]) + --nIndex; + maContainer.resize(++nIndex); +} + +void MasterPageContainer::Implementation::FireContainerChange ( + MasterPageContainerChangeEvent::EventType eType, + Token aToken) +{ + ::std::vector<Link<MasterPageContainerChangeEvent&,void>> aCopy(maChangeListeners); + MasterPageContainerChangeEvent aEvent; + aEvent.meEventType = eType; + aEvent.maChildToken = aToken; + for (const auto& rListener : aCopy) + rListener.Call(aEvent); +} + +bool MasterPageContainer::Implementation::UpdateDescriptor ( + const SharedMasterPageDescriptor& rpDescriptor, + bool bForcePageObject, + bool bForcePreview, + bool bSendEvents) +{ + const ::osl::MutexGuard aGuard (maMutex); + + // We have to create the page object when the preview provider needs it + // and the caller needs the preview. + bForcePageObject |= (bForcePreview + && rpDescriptor->mpPreviewProvider->NeedsPageObject() + && rpDescriptor->mpMasterPage==nullptr); + + // Define a cost threshold so that an update or page object or preview + // that is at least this cost are made at once. Updates with higher cost + // are scheduled for later. + sal_Int32 nCostThreshold (mpRequestQueue->IsEmpty() ? 5 : 0); + + // Update the page object (which may be used for the preview update). + if (bForcePageObject) + GetDocument(); + int nPageObjectModified (rpDescriptor->UpdatePageObject( + (bForcePageObject ? -1 : nCostThreshold), + mpDocument)); + if (nPageObjectModified == 1 && bSendEvents) + FireContainerChange( + MasterPageContainerChangeEvent::EventType::DATA_CHANGED, + rpDescriptor->maToken); + if (nPageObjectModified == -1 && bSendEvents) + FireContainerChange( + MasterPageContainerChangeEvent::EventType::CHILD_REMOVED, + rpDescriptor->maToken); + if (nPageObjectModified && ! mbFirstPageObjectSeen) + UpdatePreviewSizePixel(); + + // Update the preview. + bool bPreviewModified (rpDescriptor->UpdatePreview( + (bForcePreview ? -1 : nCostThreshold), + maSmallPreviewSizePixel, + maLargePreviewSizePixel, + maPreviewRenderer)); + + if (bPreviewModified && bSendEvents) + FireContainerChange( + MasterPageContainerChangeEvent::EventType::PREVIEW_CHANGED, + rpDescriptor->maToken); + + return nPageObjectModified || bPreviewModified; +} + +void MasterPageContainer::Implementation::ReleaseDescriptor (Token aToken) +{ + if (aToken>=0 && o3tl::make_unsigned(aToken)<maContainer.size()) + { + maContainer[aToken].reset(); + } +} + +void MasterPageContainer::Implementation::FillingDone() +{ + mpRequestQueue->ProcessAllRequests(); +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageContainer.hxx b/sd/source/ui/sidebar/MasterPageContainer.hxx new file mode 100644 index 000000000..9de4eb6bc --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageContainer.hxx @@ -0,0 +1,199 @@ +/* -*- 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 . + */ + +#pragma once + +#include <vcl/image.hxx> + +#include <memory> + +class SdPage; + +namespace sd::sidebar +{ +class MasterPageDescriptor; +class MasterPageContainerChangeEvent; + +/** This container manages the master pages used by the MasterPagesSelector + controls. It uses internally a singleton implementation object. + Therefore, all MasterPageContainer object operator on the same set of + master pages. Each MasterPageContainer, however, has its own + PreviewSize value and thus can independently switch between large and + small previews. + + The container maintains its own document to store master page objects. + + For each master page container stores its URL, preview bitmap, page + name, and, if available, the page object. + + Entries are accessed via a Token, which is mostly a numerical index but + whose values do not necessarily have to be consecutive. +*/ +class MasterPageContainer final +{ +public: + typedef int Token; + static const Token NIL_TOKEN = -1; + + MasterPageContainer(); + ~MasterPageContainer(); + + void AddChangeListener(const Link<MasterPageContainerChangeEvent&, void>& rLink); + void RemoveChangeListener(const Link<MasterPageContainerChangeEvent&, void>& rLink); + + enum PreviewSize + { + SMALL, + LARGE + }; + /** There are two different preview sizes, a small one and a large one. + Which one is used by the called container can be changed with this + method. + When the preview size is changed then all change listeners are + notified of this. + */ + void SetPreviewSize(PreviewSize eSize); + + /** Returns the preview size. + */ + PreviewSize GetPreviewSize() const { return mePreviewSize; } + + /** Return the preview size in pixels. + */ + Size const& GetPreviewSizePixel() const; + + enum PreviewState + { + PS_AVAILABLE, + PS_CREATABLE, + PS_PREPARING, + PS_NOT_AVAILABLE + }; + PreviewState GetPreviewState(Token aToken); + + /** This method is typically called for entries in the container for + which GetPreviewState() returns OS_CREATABLE. The creation of the + preview is then scheduled to be executed asynchronously at a later + point in time. When the preview is available the change listeners + will be notified. + */ + bool RequestPreview(Token aToken); + + /** Each entry of the container is either the first page of a template + document or is a master page of an Impress document. + */ + enum Origin + { + MASTERPAGE, // Master page of a document. + TEMPLATE, // First page of a template file. + DEFAULT, // Empty master page with default style. + UNKNOWN + }; + + /** Put the master page identified and described by the given parameters + into the container. When there already is a master page with the + given URL, page name, or object pointer (when that is not NULL) then + the existing entry is replaced/updated by the given one. Otherwise + a new entry is inserted. + */ + Token PutMasterPage(const std::shared_ptr<MasterPageDescriptor>& rDescriptor); + void AcquireToken(Token aToken); + void ReleaseToken(Token aToken); + + /** This and the GetTokenForIndex() methods can be used to iterate over + all members of the container. + */ + int GetTokenCount() const; + + /** Determine whether the container has a member for the given token. + */ + bool HasToken(Token aToken) const; + + /** Return a token for an index in the range + 0 <= index < GetTokenCount(). + */ + Token GetTokenForIndex(int nIndex); + + Token GetTokenForURL(const OUString& sURL); + Token GetTokenForStyleName(const OUString& sStyleName); + Token GetTokenForPageObject(const SdPage* pPage); + + OUString GetURLForToken(Token aToken); + OUString GetPageNameForToken(Token aToken); + OUString GetStyleNameForToken(Token aToken); + SdPage* GetPageObjectForToken(Token aToken, bool bLoad); + Origin GetOriginForToken(Token aToken); + sal_Int32 GetTemplateIndexForToken(Token aToken); + std::shared_ptr<MasterPageDescriptor> GetDescriptorForToken(Token aToken); + + void InvalidatePreview(Token aToken); + + /** Return a preview for the specified token. When the preview is not + present then the PreviewProvider associated with the token is + executed only when that is not expensive. It is the responsibility + of the caller to call RequestPreview() to do the same + (asynchronously) for expensive PreviewProviders. + Call GetPreviewState() to find out if that is necessary. + @param aToken + This token specifies for which master page to return the preview. + Tokens are returned for example by the GetTokenFor...() methods. + @return + The returned image is the requested preview or a substitution. + */ + Image GetPreviewForToken(Token aToken); + +private: + class Implementation; + std::shared_ptr<Implementation> mpImpl; + PreviewSize mePreviewSize; +}; + +/** For some changes to the set of master pages in a MasterPageContainer or + to the data stored for each master page one or more events are sent to + registered listeners. + Each event has an event type and a token that tells the listener where + the change took place. +*/ +class MasterPageContainerChangeEvent +{ +public: + enum class EventType + { + // A master page was added to the container. + CHILD_ADDED, + // A master page was removed from the container. + CHILD_REMOVED, + // The preview of a master page has changed. + PREVIEW_CHANGED, + // The size of a preview has changed. + SIZE_CHANGED, + // Some of the data stored for a master page has changed. + DATA_CHANGED, + // The TemplateIndex of a master page has changed. + INDEX_CHANGED, + } meEventType; + + // Token of the container entry whose data changed or which was added or + // removed. + MasterPageContainer::Token maChildToken; +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageContainerFiller.cxx b/sd/source/ui/sidebar/MasterPageContainerFiller.cxx new file mode 100644 index 000000000..3568d9c71 --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageContainerFiller.cxx @@ -0,0 +1,168 @@ +/* -*- 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 "MasterPageContainerFiller.hxx" + +#include "MasterPageDescriptor.hxx" +#include "MasterPageContainerProviders.hxx" +#include <TemplateScanner.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::sidebar { + +MasterPageContainerFiller::MasterPageContainerFiller (ContainerAdapter& rpAdapter) + : mrContainerAdapter(rpAdapter), + meState(INITIALIZE_TEMPLATE_SCANNER), + mpLastAddedEntry(nullptr), + mnIndex(1) +{ + // Add one entry for the default master page. We use temporarily the + // DefaultPagePreviewProvider to prevent the rendering (and the + // expensive creation) of the default page. It is replaced later on by + // another. + SharedMasterPageDescriptor pDescriptor = std::make_shared<MasterPageDescriptor>( + MasterPageContainer::DEFAULT, + 0, + OUString(), + OUString(), + OUString(), + false, + std::make_shared<DefaultPageObjectProvider>(), + std::make_shared<PagePreviewProvider>()); + mrContainerAdapter.PutMasterPage(pDescriptor); +} + +MasterPageContainerFiller::~MasterPageContainerFiller() +{ +} + +void MasterPageContainerFiller::RunNextStep() +{ + switch (meState) + { + case INITIALIZE_TEMPLATE_SCANNER: + mpScannerTask.reset(new TemplateScanner()); + meState = SCAN_TEMPLATE; + break; + + case SCAN_TEMPLATE: + meState = ScanTemplate(); + break; + + case ADD_TEMPLATE: + meState = AddTemplate(); + break; + + case DONE: + case ERROR: + default: + break; + } + + // When the state has just been set to DONE or ERROR then tell the + // container that no more templates will be coming and stop the + // scanning. + switch (meState) + { + case DONE: + case ERROR: + if (mpScannerTask != nullptr) + { + mrContainerAdapter.FillingDone(); + mpScannerTask.reset(); + } + break; + default: + break; + } +} + +bool MasterPageContainerFiller::HasNextStep() +{ + switch (meState) + { + case DONE: + case ERROR: + return false; + + default: + return true; + } +} + +MasterPageContainerFiller::State MasterPageContainerFiller::ScanTemplate() +{ + State eState (ERROR); + + if (mpScannerTask != nullptr) + { + if (mpScannerTask->HasNextStep()) + { + mpScannerTask->RunNextStep(); + if (mpScannerTask->GetLastAddedEntry() != mpLastAddedEntry) + { + mpLastAddedEntry = mpScannerTask->GetLastAddedEntry(); + if (mpLastAddedEntry != nullptr) + eState = ADD_TEMPLATE; + else + eState = SCAN_TEMPLATE; + } + else + eState = SCAN_TEMPLATE; + } + else + eState = DONE; + } + + return eState; +} + +MasterPageContainerFiller::State MasterPageContainerFiller::AddTemplate() +{ + if (mpLastAddedEntry != nullptr) + { + SharedMasterPageDescriptor pDescriptor = std::make_shared<MasterPageDescriptor>( + MasterPageContainer::TEMPLATE, + mnIndex, + mpLastAddedEntry->msPath, + mpLastAddedEntry->msTitle, + OUString(), + false, + std::make_shared<TemplatePageObjectProvider>(mpLastAddedEntry->msPath), + std::make_shared<TemplatePreviewProvider>(mpLastAddedEntry->msPath)); + // For user supplied templates we use a different preview provider: + // The preview in the document shows not only shapes on the master + // page but also shapes on the foreground. This is misleading and + // therefore these previews are discarded and created directly from + // the page objects. + if (pDescriptor->GetURLClassification() == MasterPageDescriptor::URLCLASS_USER) + pDescriptor->mpPreviewProvider = std::make_shared<PagePreviewProvider>(); + + mrContainerAdapter.PutMasterPage(pDescriptor); + ++mnIndex; + } + + return SCAN_TEMPLATE; +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageContainerFiller.hxx b/sd/source/ui/sidebar/MasterPageContainerFiller.hxx new file mode 100644 index 000000000..b08452ab6 --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageContainerFiller.hxx @@ -0,0 +1,92 @@ +/* -*- 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 . + */ + +#pragma once + +#include <memory> +#include "MasterPageContainer.hxx" +#include "MasterPageDescriptor.hxx" +#include <tools/AsynchronousTask.hxx> + +namespace sd +{ +class TemplateScanner; +class TemplateEntry; +} + +namespace sd::sidebar +{ +/** Fill a MasterPageContainer with information about the available master + pages. These are provided by one default page and from the existing + Impress templates. This is done asynchronously. +*/ +class MasterPageContainerFiller : public ::sd::tools::AsynchronousTask +{ +public: + class ContainerAdapter + { + public: + virtual MasterPageContainer::Token + PutMasterPage(const SharedMasterPageDescriptor& rpDescriptor) + = 0; + /** This method is called when all Impress templates have been added + to the container via the PutMasterPage() method. + */ + virtual void FillingDone() = 0; + + protected: + ~ContainerAdapter() {} + }; + + explicit MasterPageContainerFiller(ContainerAdapter& rContainerAdapter); + virtual ~MasterPageContainerFiller(); + + /** Run the next step of the task. After HasNextStep() returns false + this method should ignore further calls. + */ + virtual void RunNextStep() override; + + /** Return <TRUE/> when there is at least one more step to execute. + When the task has been executed completely then <FALSE/> is + returned. + */ + virtual bool HasNextStep() override; + +private: + ContainerAdapter& mrContainerAdapter; + // Remember what the next step has to do. + enum State + { + INITIALIZE_TEMPLATE_SCANNER, + SCAN_TEMPLATE, + ADD_TEMPLATE, + ERROR, + DONE + } meState; + ::std::unique_ptr<TemplateScanner> mpScannerTask; + const TemplateEntry* mpLastAddedEntry; + int mnIndex; + + State ScanTemplate(); + State AddTemplate(); +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageContainerProviders.cxx b/sd/source/ui/sidebar/MasterPageContainerProviders.cxx new file mode 100644 index 000000000..785536daa --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageContainerProviders.cxx @@ -0,0 +1,205 @@ +/* -*- 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 "MasterPageContainerProviders.hxx" + +#include <DrawDocShell.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <PreviewRenderer.hxx> +#include <svl/eitem.hxx> +#include <sfx2/app.hxx> +#include <sfx2/sfxsids.hrc> +#include <sfx2/thumbnailview.hxx> +#include <vcl/image.hxx> +#include <tools/diagnose_ex.h> +#include <sal/log.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::sidebar { + +//===== PagePreviewProvider =================================================== + +PagePreviewProvider::PagePreviewProvider() +{ +} + +Image PagePreviewProvider::operator () ( + int nWidth, + SdPage* pPage, + ::sd::PreviewRenderer& rRenderer) +{ + Image aPreview; + + if (pPage != nullptr) + { + // Use the given renderer to create a preview of the given page + // object. + aPreview = rRenderer.RenderPage( + pPage, + nWidth); + } + + return aPreview; +} + +int PagePreviewProvider::GetCostIndex() +{ + return 5; +} + +bool PagePreviewProvider::NeedsPageObject() +{ + return true; +} + +//===== TemplatePreviewProvider =============================================== + +TemplatePreviewProvider::TemplatePreviewProvider (const OUString& rsURL) + : msURL(rsURL) +{ +} + +Image TemplatePreviewProvider::operator() ( + int, + SdPage*, + ::sd::PreviewRenderer&) +{ + return Image(ThumbnailView::readThumbnail(msURL)); +} + +int TemplatePreviewProvider::GetCostIndex() +{ + return 10; +} + +bool TemplatePreviewProvider::NeedsPageObject() +{ + return false; +} + +//===== TemplatePageObjectProvider ============================================= + +TemplatePageObjectProvider::TemplatePageObjectProvider (const OUString& rsURL) + : msURL(rsURL) +{ +} + +SdPage* TemplatePageObjectProvider::operator() (SdDrawDocument*) +{ + SdPage* pPage = nullptr; + + mxDocumentShell = nullptr; + try + { + // Load the template document and return its first page. + ::sd::DrawDocShell* pDocumentShell = LoadDocument (msURL); + if (pDocumentShell != nullptr) + { + SdDrawDocument* pDocument = pDocumentShell->GetDoc(); + if (pDocument != nullptr) + { + pPage = pDocument->GetMasterSdPage(0, PageKind::Standard); + // In order to make the newly loaded master page deletable + // when copied into documents it is marked as no "precious". + // When it is modified then it is marked as "precious". + if (pPage != nullptr) + pPage->SetPrecious(false); + } + } + } + catch (const uno::RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + pPage = nullptr; + } + + return pPage; +} + +::sd::DrawDocShell* TemplatePageObjectProvider::LoadDocument (const OUString& sFileName) +{ + SfxApplication* pSfxApp = SfxGetpApp(); + std::unique_ptr<SfxItemSet> pSet(new SfxAllItemSet (pSfxApp->GetPool())); + pSet->Put (SfxBoolItem (SID_TEMPLATE, true)); + pSet->Put (SfxBoolItem (SID_PREVIEW, true)); + if (pSfxApp->LoadTemplate (mxDocumentShell, sFileName, std::move(pSet))) + { + mxDocumentShell = nullptr; + } + SfxObjectShell* pShell = mxDocumentShell; + return dynamic_cast< ::sd::DrawDocShell *>( pShell ); +} + +int TemplatePageObjectProvider::GetCostIndex() +{ + return 20; +} + +//===== DefaultPageObjectProvider ============================================== + +DefaultPageObjectProvider::DefaultPageObjectProvider() +{ +} + +SdPage* DefaultPageObjectProvider::operator () (SdDrawDocument* pContainerDocument) +{ + SdPage* pLocalMasterPage = nullptr; + if (pContainerDocument != nullptr) + { + SdPage* pLocalSlide = pContainerDocument->GetSdPage(0, PageKind::Standard); + if (pLocalSlide!=nullptr && pLocalSlide->TRG_HasMasterPage()) + pLocalMasterPage = dynamic_cast<SdPage*>(&pLocalSlide->TRG_GetMasterPage()); + } + + if (pLocalMasterPage == nullptr) + { + SAL_WARN( "sd", "can not create master page for slide"); + } + + return pLocalMasterPage; +} + +int DefaultPageObjectProvider::GetCostIndex() +{ + return 15; +} + +//===== ExistingPageProvider ================================================== + +ExistingPageProvider::ExistingPageProvider (SdPage* pPage) + : mpPage(pPage) +{ +} + +SdPage* ExistingPageProvider::operator() (SdDrawDocument*) +{ + return mpPage; +} + +int ExistingPageProvider::GetCostIndex() +{ + return 0; +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageContainerProviders.hxx b/sd/source/ui/sidebar/MasterPageContainerProviders.hxx new file mode 100644 index 000000000..b76076e15 --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageContainerProviders.hxx @@ -0,0 +1,175 @@ +/* -*- 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 . + */ + +#pragma once + +#include <rtl/ustring.hxx> +#include <sfx2/objsh.hxx> + +class Image; +class SdDrawDocument; +class SdPage; +namespace sd +{ +class PreviewRenderer; +} +namespace sd +{ +class DrawDocShell; +} + +namespace sd::sidebar +{ +/** Interface for a provider of page objects. It is used by the + MasterPageDescriptor to create master page objects on demand. +*/ +class PageObjectProvider +{ +public: + /** Return a master page either by returning an already existing one, by + creating a new page, or by loading a document. + @param pDocument + The document of the MasterPageContainer. It may be used to + create new pages. + */ + virtual SdPage* operator()(SdDrawDocument* pDocument) = 0; + + /** An abstract value for the expected cost of providing a master page + object. + @return + A value of 0 represents for the lowest cost, i.e. an almost + immediate return. Positive values stand for higher costs. + Negative values are not supported. + */ + virtual int GetCostIndex() = 0; + +protected: + ~PageObjectProvider() {} +}; + +class PreviewProvider +{ +public: + /** Create a preview image in the specified width. + @param nWidth + Requested width of the preview. The calling method can cope + with other sizes as well but the resulting image quality is + better when the returned image has the requested size. + @param pPage + Page object for which a preview is requested. This may be NULL + when the page object is expensive to get and the PreviewProvider + does not need this object (NeedsPageObject() returns false.) + @param rRenderer + This PreviewRenderer may be used by the PreviewProvider to + create a preview image. + */ + virtual Image operator()(int nWidth, SdPage* pPage, ::sd::PreviewRenderer& rRenderer) = 0; + + /** Return a value that indicates how expensive the creation of a + preview image is. The higher the returned value the more expensive + is the preview creation. Return 0 when the preview is already + present and can be returned immediately. + */ + virtual int GetCostIndex() = 0; + + /** Return whether the page object passed is necessary to create a + preview. + */ + virtual bool NeedsPageObject() = 0; + +protected: + ~PreviewProvider() {} +}; + +/** Provide previews of existing page objects by rendering them. +*/ +class PagePreviewProvider : public PreviewProvider +{ +public: + PagePreviewProvider(); + virtual ~PagePreviewProvider() {} + virtual Image operator()(int nWidth, SdPage* pPage, ::sd::PreviewRenderer& rRenderer) override; + virtual int GetCostIndex() override; + virtual bool NeedsPageObject() override; + +private: +}; + +/** Provide master page objects for template documents for which only the + URL is given. +*/ +class TemplatePageObjectProvider : public PageObjectProvider +{ +public: + explicit TemplatePageObjectProvider(const OUString& rsURL); + virtual ~TemplatePageObjectProvider(){}; + virtual SdPage* operator()(SdDrawDocument* pDocument) override; + virtual int GetCostIndex() override; + +private: + OUString msURL; + SfxObjectShellLock mxDocumentShell; + ::sd::DrawDocShell* LoadDocument(const OUString& sFileName); +}; + +/** Provide previews for template documents by loading the thumbnails from + the documents. +*/ +class TemplatePreviewProvider : public PreviewProvider +{ +public: + explicit TemplatePreviewProvider(const OUString& rsURL); + virtual ~TemplatePreviewProvider(){}; + virtual Image operator()(int nWidth, SdPage* pPage, ::sd::PreviewRenderer& rRenderer) override; + virtual int GetCostIndex() override; + virtual bool NeedsPageObject() override; + +private: + OUString msURL; +}; + +/** Create an empty default master page. +*/ +class DefaultPageObjectProvider : public PageObjectProvider +{ +public: + DefaultPageObjectProvider(); + virtual ~DefaultPageObjectProvider() {} + virtual SdPage* operator()(SdDrawDocument* pDocument) override; + virtual int GetCostIndex() override; +}; + +/** This implementation of the PageObjectProvider simply returns an already + existing master page object. +*/ +class ExistingPageProvider : public PageObjectProvider +{ +public: + explicit ExistingPageProvider(SdPage* pPage); + virtual ~ExistingPageProvider() {} + virtual SdPage* operator()(SdDrawDocument* pDocument) override; + virtual int GetCostIndex() override; + +private: + SdPage* mpPage; +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageContainerQueue.cxx b/sd/source/ui/sidebar/MasterPageContainerQueue.cxx new file mode 100644 index 000000000..229f3d972 --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageContainerQueue.cxx @@ -0,0 +1,263 @@ +/* -*- 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 "MasterPageContainerQueue.hxx" +#include "MasterPageContainerProviders.hxx" + +#include <tools/IdleDetection.hxx> + +#include <set> + +namespace sd::sidebar { + +const sal_Int32 MasterPageContainerQueue::snDelayedCreationTimeout (15); +const sal_Int32 MasterPageContainerQueue::snDelayedCreationTimeoutWhenNotIdle (100); +const sal_Int32 MasterPageContainerQueue::snMasterPagePriorityBoost (5); +const sal_Int32 MasterPageContainerQueue::snWaitForMoreRequestsPriorityThreshold (-10); +sal_uInt32 MasterPageContainerQueue::snWaitForMoreRequestsCount(15); + +//===== MasterPageContainerQueue::PreviewCreationRequest ====================== + +class MasterPageContainerQueue::PreviewCreationRequest +{ +public: + PreviewCreationRequest (const SharedMasterPageDescriptor& rpDescriptor, int nPriority) + : mpDescriptor(rpDescriptor), + mnPriority(nPriority) + {} + SharedMasterPageDescriptor mpDescriptor; + int mnPriority; + class Compare + { + public: + bool operator() (const PreviewCreationRequest& r1,const PreviewCreationRequest& r2) const + { + if (r1.mnPriority != r2.mnPriority) + { + // Prefer requests with higher priority. + return r1.mnPriority > r2.mnPriority; + } + else + { + // Prefer tokens that have been earlier created (those with lower + // value). + return r1.mpDescriptor->maToken < r2.mpDescriptor->maToken; + } + } + }; + class CompareToken + { + public: + MasterPageContainer::Token maToken; + explicit CompareToken(MasterPageContainer::Token aToken) : maToken(aToken) {} + bool operator() (const PreviewCreationRequest& rRequest) const + { return maToken==rRequest.mpDescriptor->maToken; } + }; +}; + +//===== MasterPageContainerQueue::RequestQueue ================================ + +class MasterPageContainerQueue::RequestQueue + : public ::std::set<PreviewCreationRequest,PreviewCreationRequest::Compare> +{ +public: + RequestQueue() {} +}; + +//===== MasterPageContainerQueue ============================================== + +MasterPageContainerQueue* MasterPageContainerQueue::Create ( + const std::weak_ptr<ContainerAdapter>& rpContainer) +{ + MasterPageContainerQueue* pQueue = new MasterPageContainerQueue(rpContainer); + pQueue->LateInit(); + return pQueue; +} + +MasterPageContainerQueue::MasterPageContainerQueue ( + const std::weak_ptr<ContainerAdapter>& rpContainer) + : mpWeakContainer(rpContainer), + mpRequestQueue(new RequestQueue()), + maDelayedPreviewCreationTimer("sd MasterPageContainerQueue maDelayedPreviewCreationTimer"), + mnRequestsServedCount(0) +{ +} + +MasterPageContainerQueue::~MasterPageContainerQueue() +{ + maDelayedPreviewCreationTimer.Stop(); + while ( ! mpRequestQueue->empty()) + mpRequestQueue->erase(mpRequestQueue->begin()); +} + +void MasterPageContainerQueue::LateInit() +{ + // Set up the timer for the delayed creation of preview bitmaps. + maDelayedPreviewCreationTimer.SetTimeout (snDelayedCreationTimeout); + maDelayedPreviewCreationTimer.SetInvokeHandler( + LINK(this,MasterPageContainerQueue,DelayedPreviewCreation) ); +} + +bool MasterPageContainerQueue::RequestPreview (const SharedMasterPageDescriptor& rpDescriptor) +{ + bool bSuccess (false); + if (rpDescriptor + && rpDescriptor->maLargePreview.GetSizePixel().Width() == 0) + { + sal_Int32 nPriority (CalculatePriority(rpDescriptor)); + + // Add a new or replace an existing request. + RequestQueue::iterator iRequest (::std::find_if( + mpRequestQueue->begin(), + mpRequestQueue->end(), + PreviewCreationRequest::CompareToken(rpDescriptor->maToken))); + // When a request for the same token exists then the lowest of the + // two priorities is used. + if (iRequest != mpRequestQueue->end()) + if (iRequest->mnPriority < nPriority) + { + mpRequestQueue->erase(iRequest); + iRequest = mpRequestQueue->end(); + } + + // Add a new request when none exists (or has just been erased). + if (iRequest == mpRequestQueue->end()) + { + mpRequestQueue->insert(PreviewCreationRequest(rpDescriptor,nPriority)); + maDelayedPreviewCreationTimer.Start(); + bSuccess = true; + } + } + return bSuccess; +} + +sal_Int32 MasterPageContainerQueue::CalculatePriority ( + const SharedMasterPageDescriptor& rpDescriptor) +{ + sal_Int32 nPriority; + + // The cost is used as a starting value. + int nCost (0); + if (rpDescriptor->mpPreviewProvider != nullptr) + { + nCost = rpDescriptor->mpPreviewProvider->GetCostIndex(); + if (rpDescriptor->mpPreviewProvider->NeedsPageObject()) + if (rpDescriptor->mpPageObjectProvider != nullptr) + nCost += rpDescriptor->mpPageObjectProvider->GetCostIndex(); + } + + // Its negative value is used so that requests with a low cost are + // preferred over those with high costs. + nPriority = -nCost; + + // Add a term that introduces an order based on the appearance in the + // AllMasterPagesSelector. + nPriority -= rpDescriptor->maToken / 3; + + // Process requests for the CurrentMasterPagesSelector first. + if (rpDescriptor->meOrigin == MasterPageContainer::MASTERPAGE) + nPriority += snMasterPagePriorityBoost; + + return nPriority; +} + +IMPL_LINK(MasterPageContainerQueue, DelayedPreviewCreation, Timer*, pTimer, void) +{ + bool bIsShowingFullScreenShow (false); + bool bWaitForMoreRequests (false); + + do + { + if (mpRequestQueue->empty()) + break; + + // First check whether the system is idle. + tools::IdleState nIdleState (tools::IdleDetection::GetIdleState(nullptr)); + if (nIdleState != tools::IdleState::Idle) + { + if (nIdleState & tools::IdleState::FullScreenShowActive) + bIsShowingFullScreenShow = true; + break; + } + + PreviewCreationRequest aRequest (*mpRequestQueue->begin()); + + // Check if the request should really be processed right now. + // Reasons to not do it are when its cost is high and not many other + // requests have been inserted into the queue that would otherwise + // be processed first. + if (aRequest.mnPriority < snWaitForMoreRequestsPriorityThreshold + && (mnRequestsServedCount+mpRequestQueue->size() < snWaitForMoreRequestsCount)) + { + // Wait for more requests before this one is processed. Note + // that the queue processing is not started anew when this + // method is left. That is done when the next request is + // inserted. + bWaitForMoreRequests = true; + break; + } + + mpRequestQueue->erase(mpRequestQueue->begin()); + + if (aRequest.mpDescriptor) + { + mnRequestsServedCount += 1; + if ( ! mpWeakContainer.expired()) + { + std::shared_ptr<ContainerAdapter> pContainer (mpWeakContainer); + if (pContainer != nullptr) + pContainer->UpdateDescriptor(aRequest.mpDescriptor,false,true,true); + } + } + } + while (false); + + if (!mpRequestQueue->empty() && ! bWaitForMoreRequests) + { + int nTimeout (snDelayedCreationTimeout); + if (bIsShowingFullScreenShow) + nTimeout = snDelayedCreationTimeoutWhenNotIdle; + maDelayedPreviewCreationTimer.SetTimeout(nTimeout); + pTimer->Start(); + } +} + +bool MasterPageContainerQueue::HasRequest (MasterPageContainer::Token aToken) const +{ + return std::any_of( + mpRequestQueue->begin(), + mpRequestQueue->end(), + PreviewCreationRequest::CompareToken(aToken)); +} + +bool MasterPageContainerQueue::IsEmpty() const +{ + return mpRequestQueue->empty(); +} + +void MasterPageContainerQueue::ProcessAllRequests() +{ + snWaitForMoreRequestsCount = 0; + if (!mpRequestQueue->empty()) + maDelayedPreviewCreationTimer.Start(); +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageContainerQueue.hxx b/sd/source/ui/sidebar/MasterPageContainerQueue.hxx new file mode 100644 index 000000000..6b9b0adca --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageContainerQueue.hxx @@ -0,0 +1,131 @@ +/* -*- 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 . + */ + +#pragma once + +#include "MasterPageContainer.hxx" +#include "MasterPageDescriptor.hxx" + +#include <vcl/timer.hxx> + +#include <memory> + +namespace sd::sidebar { + +/** The queue stores and processes all requests from a MasterPageContainer + for the creation of previews. + The order of request processing and its timing is controlled by a + heuristic that uses values given with each request and which is + controlled by various parameters that are described below. +*/ +class MasterPageContainerQueue final +{ +public: + class ContainerAdapter { + public: + virtual bool UpdateDescriptor ( + const SharedMasterPageDescriptor& rpDescriptor, + bool bForcePageObject, + bool bForcePreview, + bool bSendEvents) = 0; + + protected: + ~ContainerAdapter() {} + }; + + static MasterPageContainerQueue* Create ( + const std::weak_ptr<ContainerAdapter>& rpContainer); + ~MasterPageContainerQueue(); + + /** This method is typically called for entries in the container for + which GetPreviewState() returns OS_CREATABLE. The creation of the + preview is then scheduled to be executed asynchronously at a later + point in time. When the preview is available the change listeners + will be notified. + */ + bool RequestPreview (const SharedMasterPageDescriptor& rDescriptor); + + /** Return <TRUE/> when there is a request currently in the queue for + the given token. + */ + bool HasRequest (MasterPageContainer::Token aToken) const; + + /** Return <TRUE/> when there is at least one request in the queue. + */ + bool IsEmpty() const; + + /** After this call the queue does not wait anymore for requests with + higher priority when only a small number of requests with lower + priority are present. This method should be called when all + templates are inserted into the MasterPageContainer. + */ + void ProcessAllRequests(); + +private: + std::weak_ptr<ContainerAdapter> mpWeakContainer; + class PreviewCreationRequest; + class RequestQueue; + std::unique_ptr<RequestQueue> mpRequestQueue; + Timer maDelayedPreviewCreationTimer; + sal_uInt32 mnRequestsServedCount; + + // There are a couple of values that define various aspects of the + // heuristic that defines the order and timing in which requests for + // preview creation are processed. + + /** The time to wait (in milliseconds) between the creation of previews. + */ + static const sal_Int32 snDelayedCreationTimeout; + + /** The time to wait when the system is not idle. + */ + static const sal_Int32 snDelayedCreationTimeoutWhenNotIdle; + + /** Requests for previews of master pages in a document have their + priority increased by this value. + */ + static const sal_Int32 snMasterPagePriorityBoost; + + /** When only requests which a priority lower than this threshold exist + and not many requests have been made yet then wait with processing + them until more requests are present. + */ + static const sal_Int32 snWaitForMoreRequestsPriorityThreshold; + + /** When only requests which a priority lower than a threshold exist + and not more requests than this number have been made or already + processed then wait with processing them until more requests are + present. + */ + static sal_uInt32 snWaitForMoreRequestsCount; + + explicit MasterPageContainerQueue (const std::weak_ptr<ContainerAdapter>& rpContainer); + void LateInit(); + + /** Calculate the priority that defines the order in which requests + are processed. + */ + static sal_Int32 CalculatePriority (const SharedMasterPageDescriptor& rDescriptor); + + DECL_LINK(DelayedPreviewCreation, Timer *, void); +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageDescriptor.cxx b/sd/source/ui/sidebar/MasterPageDescriptor.cxx new file mode 100644 index 000000000..2c0c23eb7 --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageDescriptor.cxx @@ -0,0 +1,341 @@ +/* -*- 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 <memory> +#include "MasterPageDescriptor.hxx" +#include "MasterPageContainerProviders.hxx" + +#include "DocumentHelper.hxx" +#include <PreviewRenderer.hxx> +#include <sdpage.hxx> +#include <tools/urlobj.hxx> +#include <sal/log.hxx> + +namespace sd::sidebar { + +//===== MasterPageDescriptor ================================================== + +MasterPageDescriptor::MasterPageDescriptor ( + MasterPageContainer::Origin eOrigin, + const sal_Int32 nTemplateIndex, + std::u16string_view rsURL, + const OUString& rsPageName, + const OUString& rsStyleName, + const bool bIsPrecious, + const std::shared_ptr<PageObjectProvider>& rpPageObjectProvider, + const std::shared_ptr<PreviewProvider>& rpPreviewProvider) + : maToken(MasterPageContainer::NIL_TOKEN), + meOrigin(eOrigin), + msURL(INetURLObject(rsURL).GetMainURL(INetURLObject::DecodeMechanism::Unambiguous)), + msPageName(rsPageName), + msStyleName(rsStyleName), + mbIsPrecious(bIsPrecious), + mpMasterPage(nullptr), + mpSlide(nullptr), + mpPreviewProvider(rpPreviewProvider), + mpPageObjectProvider(rpPageObjectProvider), + mnTemplateIndex(nTemplateIndex), + meURLClassification(URLCLASS_UNDETERMINED), + mnUseCount(0) +{ +} + +void MasterPageDescriptor::SetToken (MasterPageContainer::Token aToken) +{ + maToken = aToken; +} + +const Image& MasterPageDescriptor::GetPreview (MasterPageContainer::PreviewSize eSize) const +{ + if (eSize == MasterPageContainer::SMALL) + return maSmallPreview; + else + return maLargePreview; +} + +::std::unique_ptr<std::vector<MasterPageContainerChangeEvent::EventType> > + MasterPageDescriptor::Update ( + const MasterPageDescriptor& rDescriptor) +{ + bool bDataChanged (false); + bool bIndexChanged (false); + bool bPreviewChanged (false); + + if (meOrigin==MasterPageContainer::UNKNOWN + && rDescriptor.meOrigin!=MasterPageContainer::UNKNOWN) + { + meOrigin = rDescriptor.meOrigin; + bIndexChanged = true; + } + + if (msURL.isEmpty() && !rDescriptor.msURL.isEmpty()) + { + msURL = rDescriptor.msURL; + bDataChanged = true; + } + + if (msPageName.isEmpty() && !rDescriptor.msPageName.isEmpty()) + { + msPageName = rDescriptor.msPageName; + bDataChanged = true; + } + + if (msStyleName.isEmpty() && !rDescriptor.msStyleName.isEmpty()) + { + msStyleName = rDescriptor.msStyleName; + bDataChanged = true; + } + + if (mpPageObjectProvider == nullptr && rDescriptor.mpPageObjectProvider != nullptr) + { + mpPageObjectProvider = rDescriptor.mpPageObjectProvider; + bDataChanged = true; + } + + if (mpPreviewProvider == nullptr && rDescriptor.mpPreviewProvider != nullptr) + { + mpPreviewProvider = rDescriptor.mpPreviewProvider; + bPreviewChanged = true; + } + + if (mnTemplateIndex<0 && rDescriptor.mnTemplateIndex>=0) + { + mnTemplateIndex = rDescriptor.mnTemplateIndex; + bIndexChanged = true; + } + + // Prepare the list of event types that will be returned. + ::std::unique_ptr<std::vector<MasterPageContainerChangeEvent::EventType> > pResult; + if (bDataChanged || bIndexChanged || bPreviewChanged) + { + pResult.reset(new std::vector<MasterPageContainerChangeEvent::EventType>); + if (bDataChanged) + pResult->push_back(MasterPageContainerChangeEvent::EventType::DATA_CHANGED); + if (bIndexChanged) + pResult->push_back(MasterPageContainerChangeEvent::EventType::INDEX_CHANGED); + if (bPreviewChanged) + pResult->push_back(MasterPageContainerChangeEvent::EventType::PREVIEW_CHANGED); + } + + return pResult; +} + +int MasterPageDescriptor::UpdatePageObject ( + sal_Int32 nCostThreshold, + SdDrawDocument* pDocument) +{ + int nModified = 0; + + // Update the page object when that is not yet known. + if (mpMasterPage == nullptr && mpPageObjectProvider != nullptr + && (nCostThreshold < 0 || mpPageObjectProvider->GetCostIndex() <= nCostThreshold)) + { + // Note that pDocument may be NULL. + + SdPage* pPage = (*mpPageObjectProvider)(pDocument); + if (meOrigin == MasterPageContainer::MASTERPAGE) + { + mpMasterPage = pPage; + if (mpMasterPage != nullptr) + mpMasterPage->SetPrecious(mbIsPrecious); + } + else + { + // Master pages from templates are copied into the local document. + if (pDocument != nullptr) + mpMasterPage = DocumentHelper::CopyMasterPageToLocalDocument(*pDocument,pPage); + mpSlide = DocumentHelper::GetSlideForMasterPage(mpMasterPage); + } + + if (mpMasterPage != nullptr) + { + // Update page name and style name. + if (msPageName.isEmpty()) + msPageName = mpMasterPage->GetName(); + msStyleName = mpMasterPage->GetName(); + + // Delete an existing substitution. The next request for a preview + // will create the real one. + maSmallPreview = Image(); + maLargePreview = Image(); + mpPreviewProvider = std::make_shared<PagePreviewProvider>(); + } + else + { + SAL_WARN( "sd", "UpdatePageObject: master page is NULL"); + return -1; + } + + nModified = 1; + } + + return nModified; +} + +bool MasterPageDescriptor::UpdatePreview ( + sal_Int32 nCostThreshold, + const Size& rSmallSize, + const Size& rLargeSize, + ::sd::PreviewRenderer& rRenderer) +{ + bool bModified (false); + + // Update the preview when that is not yet known. + if (maLargePreview.GetSizePixel().Width() == 0 && mpPreviewProvider != nullptr + && (nCostThreshold < 0 || mpPreviewProvider->GetCostIndex() <= nCostThreshold)) + { + SdPage* pPage = mpSlide; + if (pPage == nullptr) + { + pPage = mpMasterPage; + } + //TODO: Notify LOOL of preview updates. + maLargePreview = (*mpPreviewProvider)( + rLargeSize.Width(), + pPage, + rRenderer); + if (maLargePreview.GetSizePixel().Width() > 0) + { + // Create the small preview by scaling the large one down. + maSmallPreview = rRenderer.ScaleBitmap( + maLargePreview.GetBitmapEx(), + rSmallSize.Width()); + // The large preview may not have the desired width. Scale it + // accordingly. + if (maLargePreview.GetSizePixel().Width() != rLargeSize.Width()) + maLargePreview = rRenderer.ScaleBitmap( + maLargePreview.GetBitmapEx(), + rLargeSize.Width()); + bModified = true; + } + } + + return bModified; +} + +MasterPageDescriptor::URLClassification MasterPageDescriptor::GetURLClassification() +{ + if (meURLClassification == URLCLASS_UNDETERMINED) + { + if (msURL.isEmpty()) + meURLClassification = URLCLASS_UNKNOWN; + else if (msURL.indexOf("presnt")>=0) + { + meURLClassification = URLCLASS_PRESENTATION; + } + else if (msURL.indexOf("layout")>=0) + { + meURLClassification = URLCLASS_LAYOUT; + } + else if (msURL.indexOf("educate")>=0) + { + meURLClassification = URLCLASS_OTHER; + } + else + { + meURLClassification = URLCLASS_USER; + } + } + + return meURLClassification; +} + +//===== URLComparator ========================================================= + +MasterPageDescriptor::URLComparator::URLComparator (const OUString& sURL) + : msURL(sURL) +{ +} + +bool MasterPageDescriptor::URLComparator::operator() ( + const SharedMasterPageDescriptor& rDescriptor) +{ + if (!rDescriptor) + return false; + else + return rDescriptor->msURL == msURL; +} + +// ===== StyleNameComparator ================================================== + +MasterPageDescriptor::StyleNameComparator::StyleNameComparator (const OUString& sStyleName) + : msStyleName(sStyleName) +{ +} + +bool MasterPageDescriptor::StyleNameComparator::operator() ( + const SharedMasterPageDescriptor& rDescriptor) +{ + if (!rDescriptor) + return false; + else + return rDescriptor->msStyleName == msStyleName; +} + +//===== PageObjectComparator ================================================== + +MasterPageDescriptor::PageObjectComparator::PageObjectComparator (const SdPage* pPageObject) + : mpMasterPage(pPageObject) +{ +} + +bool MasterPageDescriptor::PageObjectComparator::operator() ( + const SharedMasterPageDescriptor& rDescriptor) +{ + if (!rDescriptor) + return false; + else + return rDescriptor->mpMasterPage==mpMasterPage; +} + +//===== AllComparator ========================================================= + +MasterPageDescriptor::AllComparator::AllComparator(const SharedMasterPageDescriptor& rDescriptor) + : mpDescriptor(rDescriptor) +{ +} + +bool MasterPageDescriptor::AllComparator::operator() (const SharedMasterPageDescriptor&rDescriptor) +{ + if (!rDescriptor) + return false; + else + { + // Take URL, page name, style name, and page object into account + // when comparing two descriptors. When two descriptors are + // identical in any of these values then there are thought of as + // equivalent. Only the Origin has to be the same in both + // descriptors. + return mpDescriptor->meOrigin == rDescriptor->meOrigin + && ((!mpDescriptor->msURL.isEmpty() && mpDescriptor->msURL == rDescriptor->msURL) + || (!mpDescriptor->msPageName.isEmpty() + && mpDescriptor->msPageName == rDescriptor->msPageName) + || (!mpDescriptor->msStyleName.isEmpty() + && mpDescriptor->msStyleName == rDescriptor->msStyleName) + || (mpDescriptor->mpMasterPage != nullptr + && mpDescriptor->mpMasterPage == rDescriptor->mpMasterPage) + || (mpDescriptor->mpPageObjectProvider != nullptr + && rDescriptor->mpPageObjectProvider != nullptr + && mpDescriptor->mpPageObjectProvider == rDescriptor->mpPageObjectProvider)); + } +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageDescriptor.hxx b/sd/source/ui/sidebar/MasterPageDescriptor.hxx new file mode 100644 index 000000000..62717e528 --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageDescriptor.hxx @@ -0,0 +1,231 @@ +/* -*- 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 . + */ + +#pragma once + +#include "MasterPageContainer.hxx" +#include <memory> + +namespace sd { class PreviewRenderer; } +class SdDrawDocument; + +namespace sd::sidebar { + +class PageObjectProvider; +class PreviewProvider; + +class MasterPageDescriptor; +typedef std::shared_ptr<MasterPageDescriptor> SharedMasterPageDescriptor; + +/** A collection of data that is stored for every master page in the + MasterpageContainer. +*/ +class MasterPageDescriptor +{ +public: + MasterPageDescriptor ( + MasterPageContainer::Origin eOrigin, + const sal_Int32 nTemplateIndex, + std::u16string_view rURL, + const OUString& rPageName, + const OUString& rStyleName, + const bool bIsPrecious, + const std::shared_ptr<PageObjectProvider>& rpPageObjectProvider, + const std::shared_ptr<PreviewProvider>& rpPreviewProvider); + + void SetToken (MasterPageContainer::Token aToken); + + /** Update the called MasterPageDescriptor object with values from the + given one. Only those values are updated that have default values + in the called object and that have non-default values in the given + one. + @return + Returns a list of event types for which event notifications have + to be sent to listeners. The list may be empty or NULL. + */ + ::std::unique_ptr<std::vector<MasterPageContainerChangeEvent::EventType> > + Update ( + const MasterPageDescriptor& rDescriptor); + + /** This convenience method returns either a small or a large preview, + depending on the given size specifier. + Note that the previews are not created when they are not present. + @return + The returned preview may be empty. + */ + const Image& GetPreview (MasterPageContainer::PreviewSize ePreviewSize) const; + + /** Use the PreviewProvider to get access to a preview of the master + page. + + Note that this is only done, when either bForce is <TRUE/> or + the PreviewProvider::GetCostIndex() returns 0. + + The small preview is created by scaling the large one, not by + calling PreviewProvider::operator() a second time. + + It is the responsibility of the caller to call UpdatePageObject() + before calling this method when the PreviewProvider can only work + when the master page object is present, i.e. its NeedsPageObject() + method returns <TRUE/>. + + @param nCostThreshold + When this is zero or positive then the preview is created only + when the preview provider has a cost equal to or smaller than + this threshold. A negative value forces the preview to be + created, regardless of the cost. + @param rSmallSize + Size of the small preview. + @param rLargeSize + Size of the large preview. + @param rRenderer + A PreviewRenderer object that may be used to create a preview. + @return + When the previews are successfully provided then <TRUE/> is + returned. + */ + bool UpdatePreview ( + sal_Int32 nCostThreshold, + const Size& rSmallSize, + const Size& rLargeSize, + ::sd::PreviewRenderer& rRenderer); + + /** Use the PageObjectProvider to get access to the master page object. + + Note that this is only done, when either bForce is <TRUE/> or the + PreviewProvider::GetCostIndex() returns 0. + + @param nCostThreshold + When this is zero or positive then the page object is created + only when the page object provider has a cost equal to or + smaller than this threshold. A negative value forces the + page object be created, regardless of the cost. + @param pDocument + This document of the MasterPageContainer may be used to create + a page object with or store one in. + @return + When the master page object is successfully provided then + 1 is returned, on no change then a 0 is provided, + on a masterpage-error a -1 is provided. + */ + int UpdatePageObject ( + sal_Int32 nCostThreshold, + SdDrawDocument* pDocument); + + enum URLClassification { + URLCLASS_USER, + URLCLASS_LAYOUT, + URLCLASS_PRESENTATION, + URLCLASS_OTHER, + URLCLASS_UNKNOWN, + URLCLASS_UNDETERMINED + }; + + URLClassification GetURLClassification(); + + /** The Token under which the MasterPageContainer gives access to the + object. + */ + MasterPageContainer::Token maToken; + + /** A rough specification of the origin of the master page. + */ + MasterPageContainer::Origin meOrigin; + + /** The URL is not empty for master pages loaded from a template + document. + */ + OUString msURL; + + /** Taken from the title of the template file. + */ + OUString msPageName; + + /** Taken from the master page object. + */ + OUString msStyleName; + + const bool mbIsPrecious; + + /** The actual master page. + */ + SdPage* mpMasterPage; + + /** A slide that uses the master page. + */ + SdPage* mpSlide; + + /** A small (the default size) preview of the master page. May be + empty. When this smaller preview is not empty then the larger one + is not empty, too. + */ + Image maSmallPreview; + + /** A large preview of the master page. May be empty. When this larger + preview is not empty then the smaller one is not empty, too. + */ + Image maLargePreview; + + /** The preview provider. May be empty. May be replaced during the + lifetime of a MasterPageDescriptor object. + */ + std::shared_ptr<PreviewProvider> mpPreviewProvider; + + /** The master page provider. May be empty. May be replaced during + the lifetime of a MasterPageDescriptor object. + */ + std::shared_ptr<PageObjectProvider> mpPageObjectProvider; + + /** This index represents the order in which templates are provided via + the TemplateScanner. It defines the order in which the entries in + the AllMasterPagesSelector are displayed. The default value is -1. + */ + sal_Int32 mnTemplateIndex; + + URLClassification meURLClassification; + + sal_Int32 mnUseCount; + + class URLComparator { public: + OUString msURL; + explicit URLComparator (const OUString& sURL); + bool operator() (const SharedMasterPageDescriptor& rDescriptor); + }; + class StyleNameComparator { public: + OUString msStyleName; + explicit StyleNameComparator (const OUString& sStyleName); + bool operator() (const SharedMasterPageDescriptor& rDescriptor); + }; + class PageObjectComparator { public: + const SdPage* mpMasterPage; + explicit PageObjectComparator (const SdPage* pPageObject); + bool operator() (const SharedMasterPageDescriptor& rDescriptor); + }; + class AllComparator { public: + explicit AllComparator(const SharedMasterPageDescriptor& rDescriptor); + bool operator() (const SharedMasterPageDescriptor& rDescriptor); + private: + SharedMasterPageDescriptor mpDescriptor; + }; + +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageObserver.cxx b/sd/source/ui/sidebar/MasterPageObserver.cxx new file mode 100644 index 000000000..017a0bcdf --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageObserver.cxx @@ -0,0 +1,317 @@ +/* -*- 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 <MasterPageObserver.hxx> + +#include <algorithm> +#include <iterator> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <set> +#include <unordered_map> +#include <memory> +#include <vector> +#include <svl/lstner.hxx> +#include <osl/doublecheckedlocking.h> +#include <osl/getglobalmutex.hxx> +#include <tools/debug.hxx> + +namespace sd { + +class MasterPageObserver::Implementation + : public SfxListener +{ +public: + /** The single instance of this class. It is created on demand when + Instance() is called for the first time. + */ + static MasterPageObserver* mpInstance; + + /** The master page observer will listen to events of this document and + detect changes of the use of master pages. + */ + void RegisterDocument (SdDrawDocument& rDocument); + + /** The master page observer will stop to listen to events of this + document. + */ + void UnregisterDocument (SdDrawDocument& rDocument); + + /** Add a listener that is informed of master pages that are newly + assigned to slides or become unassigned. + @param rEventListener + The event listener to call for future events. Call + RemoveEventListener() before the listener is destroyed. + */ + void AddEventListener (const Link<MasterPageObserverEvent&,void>& rEventListener); + + /** Remove the given listener from the list of listeners. + @param rEventListener + After this method returns the given listener is not called back + from this object. Passing a listener that has not + been registered before is safe and is silently ignored. + */ + void RemoveEventListener (const Link<MasterPageObserverEvent&,void>& rEventListener); + +private: + ::std::vector<Link<MasterPageObserverEvent&,void>> maListeners; + + struct DrawDocHash { + size_t operator()(SdDrawDocument* argument) const + { return reinterpret_cast<sal_uIntPtr>(argument); } + }; + typedef std::unordered_map<SdDrawDocument*, + MasterPageObserver::MasterPageNameSet, + DrawDocHash> + MasterPageContainer; + MasterPageContainer maUsedMasterPages; + + virtual void Notify( + SfxBroadcaster& rBroadcaster, + const SfxHint& rHint) override; + + void AnalyzeUsedMasterPages (SdDrawDocument& rDocument); + + void SendEvent (MasterPageObserverEvent& rEvent); +}; + +MasterPageObserver* MasterPageObserver::Implementation::mpInstance = nullptr; + +//===== MasterPageObserver ==================================================== + +MasterPageObserver& MasterPageObserver::Instance() +{ + if (Implementation::mpInstance == nullptr) + { + ::osl::GetGlobalMutex aMutexFunctor; + ::osl::MutexGuard aGuard (aMutexFunctor()); + if (Implementation::mpInstance == nullptr) + { + MasterPageObserver* pInstance = new MasterPageObserver (); + SdGlobalResourceContainer::Instance().AddResource ( + ::std::unique_ptr<SdGlobalResource>(pInstance)); + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + Implementation::mpInstance = pInstance; + } + } + else + { + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + } + + DBG_ASSERT(Implementation::mpInstance!=nullptr, + "MasterPageObserver::Instance(): instance is NULL"); + return *Implementation::mpInstance; +} + +void MasterPageObserver::RegisterDocument (SdDrawDocument& rDocument) +{ + mpImpl->RegisterDocument (rDocument); +} + +void MasterPageObserver::UnregisterDocument (SdDrawDocument& rDocument) +{ + mpImpl->UnregisterDocument (rDocument); +} + +void MasterPageObserver::AddEventListener (const Link<MasterPageObserverEvent&,void>& rEventListener) +{ + + mpImpl->AddEventListener (rEventListener); +} + +void MasterPageObserver::RemoveEventListener (const Link<MasterPageObserverEvent&,void>& rEventListener) +{ + mpImpl->RemoveEventListener (rEventListener); +} + +MasterPageObserver::MasterPageObserver() + : mpImpl (new Implementation) +{} + +MasterPageObserver::~MasterPageObserver() +{} + +//===== MasterPageObserver::Implementation ==================================== + +void MasterPageObserver::Implementation::RegisterDocument ( + SdDrawDocument& rDocument) +{ + // Gather the names of all the master pages in the given document. + MasterPageContainer::mapped_type aMasterPageSet; + sal_uInt16 nMasterPageCount = rDocument.GetMasterSdPageCount(PageKind::Standard); + for (sal_uInt16 nIndex=0; nIndex<nMasterPageCount; nIndex++) + { + SdPage* pMasterPage = rDocument.GetMasterSdPage (nIndex, PageKind::Standard); + if (pMasterPage != nullptr) + aMasterPageSet.insert (pMasterPage->GetName()); + } + + bool bAlreadyExists = maUsedMasterPages.find(&rDocument) != maUsedMasterPages.end(); + maUsedMasterPages[&rDocument] = aMasterPageSet; + + if (!bAlreadyExists) + StartListening (rDocument); +} + +void MasterPageObserver::Implementation::UnregisterDocument ( + SdDrawDocument& rDocument) +{ + EndListening (rDocument); + + MasterPageContainer::iterator aMasterPageDescriptor(maUsedMasterPages.find(&rDocument)); + if(aMasterPageDescriptor != maUsedMasterPages.end()) + maUsedMasterPages.erase(aMasterPageDescriptor); +} + +void MasterPageObserver::Implementation::AddEventListener ( + const Link<MasterPageObserverEvent&,void>& rEventListener) +{ + if (::std::find ( + maListeners.begin(), + maListeners.end(), + rEventListener) != maListeners.end()) + return; + + maListeners.push_back (rEventListener); + + // Tell the new listener about all the master pages that are + // currently in use. + for (const auto& rDocument : maUsedMasterPages) + { + ::std::set<OUString>::reverse_iterator aNameIterator; + for (aNameIterator=rDocument.second.rbegin(); + aNameIterator!=rDocument.second.rend(); + ++aNameIterator) + { + MasterPageObserverEvent aEvent ( + MasterPageObserverEvent::ET_MASTER_PAGE_EXISTS, + *aNameIterator); + SendEvent (aEvent); + } + } +} + +void MasterPageObserver::Implementation::RemoveEventListener ( + const Link<MasterPageObserverEvent&,void>& rEventListener) +{ + maListeners.erase ( + ::std::find ( + maListeners.begin(), + maListeners.end(), + rEventListener)); +} + +void MasterPageObserver::Implementation::Notify( + SfxBroadcaster& rBroadcaster, + const SfxHint& rHint) +{ + if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint) + return; + const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint); + + switch (pSdrHint->GetKind()) + { + case SdrHintKind::PageOrderChange: + // Process the modified set of pages only when the number of + // standard and notes master pages are equal. This test + // filters out events that are sent in between the insertion + // of a new standard master page and a new notes master + // page. + if (auto pDrawDocument = dynamic_cast<SdDrawDocument *>( &rBroadcaster )) + { + if (pDrawDocument->GetMasterSdPageCount(PageKind::Standard) + == pDrawDocument->GetMasterSdPageCount(PageKind::Notes)) + { + AnalyzeUsedMasterPages (*pDrawDocument); + } + } + break; + + default: + break; + } +} + +void MasterPageObserver::Implementation::AnalyzeUsedMasterPages ( + SdDrawDocument& rDocument) +{ + // Create a set of names of the master pages used by the given document. + sal_uInt16 nMasterPageCount = rDocument.GetMasterSdPageCount(PageKind::Standard); + ::std::set<OUString> aCurrentMasterPages; + for (sal_uInt16 nIndex=0; nIndex<nMasterPageCount; nIndex++) + { + SdPage* pMasterPage = rDocument.GetMasterSdPage (nIndex, PageKind::Standard); + if (pMasterPage != nullptr) + aCurrentMasterPages.insert (pMasterPage->GetName()); + } + + std::vector<OUString> aNewMasterPages; + std::vector<OUString> aRemovedMasterPages; + MasterPageContainer::iterator aOldMasterPagesDescriptor ( + maUsedMasterPages.find(&rDocument)); + if (aOldMasterPagesDescriptor == maUsedMasterPages.end()) + return; + + // Send events about the newly used master pages. + ::std::set_difference ( + aCurrentMasterPages.begin(), + aCurrentMasterPages.end(), + aOldMasterPagesDescriptor->second.begin(), + aOldMasterPagesDescriptor->second.end(), + std::back_inserter(aNewMasterPages)); + for (const auto& aNewMasterPage : aNewMasterPages) + { + MasterPageObserverEvent aEvent ( + MasterPageObserverEvent::ET_MASTER_PAGE_ADDED, + aNewMasterPage); + SendEvent (aEvent); + } + + // Send events about master pages that are not used any longer. + ::std::set_difference ( + aOldMasterPagesDescriptor->second.begin(), + aOldMasterPagesDescriptor->second.end(), + aCurrentMasterPages.begin(), + aCurrentMasterPages.end(), + std::back_inserter(aRemovedMasterPages)); + for (const auto& aRemovedMasterPage : aRemovedMasterPages) + { + MasterPageObserverEvent aEvent ( + MasterPageObserverEvent::ET_MASTER_PAGE_REMOVED, + aRemovedMasterPage); + SendEvent (aEvent); + } + + // Store the new list of master pages. + aOldMasterPagesDescriptor->second = aCurrentMasterPages; +} + +void MasterPageObserver::Implementation::SendEvent ( + MasterPageObserverEvent& rEvent) +{ + for (const auto& aLink : maListeners) + { + aLink.Call(rEvent); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPagesSelector.cxx b/sd/source/ui/sidebar/MasterPagesSelector.cxx new file mode 100644 index 000000000..979726910 --- /dev/null +++ b/sd/source/ui/sidebar/MasterPagesSelector.cxx @@ -0,0 +1,620 @@ +/* -*- 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 <sal/config.h> + +#include <utility> + +#include "MasterPagesSelector.hxx" + +#include "MasterPageContainer.hxx" +#include "DocumentHelper.hxx" +#include <pres.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <app.hrc> + +#include <DrawController.hxx> +#include <SlideSorterViewShell.hxx> +#include "PreviewValueSet.hxx" +#include <ViewShellBase.hxx> +#include <o3tl/safeint.hxx> +#include <vcl/image.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/sidebar/Theme.hxx> +#include <memory> + +using namespace ::com::sun::star::text; + +namespace sd::sidebar { + + /** menu entry that is executed as default action when the left mouse button is + clicked over a master page. + */ +constexpr OStringLiteral gsDefaultClickAction = "applyselect"; + +MasterPagesSelector::MasterPagesSelector ( + weld::Widget* pParent, + SdDrawDocument& rDocument, + ViewShellBase& rBase, + const std::shared_ptr<MasterPageContainer>& rpContainer, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar, + const OUString& rUIFileName, + const OString& rValueSetName) + : PanelLayout( pParent, "MasterPagePanel", rUIFileName ), + mpContainer(rpContainer), + mxPreviewValueSet(new PreviewValueSet), + mxPreviewValueSetWin(new weld::CustomWeld(*m_xBuilder, rValueSetName, *mxPreviewValueSet)), + mrDocument(rDocument), + mrBase(rBase), + mxSidebar(rxSidebar) +{ + mxPreviewValueSet->SetSelectHdl ( + LINK(this, MasterPagesSelector, ClickHandler)); + mxPreviewValueSet->SetContextMenuHandler ( + LINK(this, MasterPagesSelector, ContextMenuHandler)); + mxPreviewValueSet->SetStyle(mxPreviewValueSet->GetStyle() | WB_NO_DIRECTSELECT); + + if (mxPreviewValueSet->GetDrawingArea()->get_ref_device().GetDPIScaleFactor() > 1) + mpContainer->SetPreviewSize(MasterPageContainer::LARGE); + + mxPreviewValueSet->SetPreviewSize(mpContainer->GetPreviewSizePixel()); + mxPreviewValueSet->Show(); + + mxPreviewValueSet->SetColor(sfx2::sidebar::Theme::GetColor(sfx2::sidebar::Theme::Color_PanelBackground)); + + Link<MasterPageContainerChangeEvent&,void> aChangeListener (LINK(this,MasterPagesSelector,ContainerChangeListener)); + mpContainer->AddChangeListener(aChangeListener); +} + +MasterPagesSelector::~MasterPagesSelector() +{ + Clear(); + UpdateLocks(ItemList()); + + Link<MasterPageContainerChangeEvent&,void> aChangeListener (LINK(this,MasterPagesSelector,ContainerChangeListener)); + mpContainer->RemoveChangeListener(aChangeListener); + mpContainer.reset(); + mxPreviewValueSetWin.reset(); + mxPreviewValueSet.reset(); +} + +void MasterPagesSelector::LateInit() +{ +} + +sal_Int32 MasterPagesSelector::GetPreferredHeight (sal_Int32 nWidth) +{ + const ::osl::MutexGuard aGuard (maMutex); + + return mxPreviewValueSet->GetPreferredHeight (nWidth); +} + +void MasterPagesSelector::UpdateLocks (const ItemList& rItemList) +{ + ItemList aNewLockList; + + // In here we first lock the master pages in the given list and then + // release the locks acquired in a previous call to this method. When + // this were done the other way round the lock count of some master + // pages might drop temporarily to 0 and would lead to unnecessary + // deletion and re-creation of MasterPageDescriptor objects. + + // Lock the master pages in the given list. + for (const auto& rItem : rItemList) + { + mpContainer->AcquireToken(rItem); + aNewLockList.push_back(rItem); + } + + // Release the previously locked master pages. + for (const auto& rPage : maLockedMasterPages) + mpContainer->ReleaseToken(rPage); + + maLockedMasterPages.swap(aNewLockList); +} + +void MasterPagesSelector::Fill() +{ + ::std::unique_ptr<ItemList> pItemList (new ItemList); + + Fill(*pItemList); + + UpdateLocks(*pItemList); + UpdateItemList(std::move(pItemList)); +} + +OUString MasterPagesSelector::GetContextMenuUIFile() const +{ + return "modules/simpress/ui/mastermenu.ui"; +} + +IMPL_LINK_NOARG(MasterPagesSelector, ClickHandler, ValueSet*, void) +{ + // We use the framework to assign the clicked-on master page because we + // so use the same mechanism as the context menu does (where we do not + // have the option to call the assignment method directly.) + ExecuteCommand(gsDefaultClickAction); +} + +IMPL_LINK(MasterPagesSelector, ContextMenuHandler, const Point*, pPos, void) +{ + if (pPos) + { + // Here we only prepare the display of the context menu: on right + // click the item under the mouse is selected. + mxPreviewValueSet->GrabFocus(); + mxPreviewValueSet->ReleaseMouse(); + + sal_uInt16 nIndex = mxPreviewValueSet->GetItemId(*pPos); + if (nIndex > 0) + mxPreviewValueSet->SelectItem(nIndex); + } + + // Now do the actual display of the context menu + ShowContextMenu(pPos); +} + +void MasterPagesSelector::ShowContextMenu(const Point* pPos) +{ + // Use the currently selected item and show the popup menu in its + // center. + const sal_uInt16 nIndex = mxPreviewValueSet->GetSelectedItemId(); + if (nIndex <= 0) + return; + + // The position of the upper left corner of the context menu is + // taken either from the mouse position (when the command was sent + // as reaction to a right click) or in the center of the selected + // item (when the command was sent as reaction to Shift+F10.) + Point aPosition; + if (!pPos) + { + ::tools::Rectangle aBBox (mxPreviewValueSet->GetItemRect(nIndex)); + aPosition = aBBox.Center(); + } + else + aPosition = *pPos; + + // Setup the menu. + weld::Widget* pParent = mxPreviewValueSet->GetDrawingArea(); + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pParent, GetContextMenuUIFile())); + std::unique_ptr<weld::Menu> xMenu(xBuilder->weld_menu("menu")); + ProcessPopupMenu(*xMenu); + ::tools::Rectangle aRect(aPosition, Size(1,1)); + // Show the menu. + ExecuteCommand(xMenu->popup_at_rect(pParent, aRect)); +} + +void MasterPagesSelector::ProcessPopupMenu(weld::Menu& rMenu) +{ + // Disable some entries. + if (mpContainer->GetPreviewSize() == MasterPageContainer::SMALL) + rMenu.set_sensitive("small", false); + else + rMenu.set_sensitive("large", false); +} + +void MasterPagesSelector::ExecuteCommand(const OString &rIdent) +{ + if (rIdent == "applyall") + { + mrBase.SetBusyState (true); + AssignMasterPageToAllSlides (GetSelectedMasterPage()); + mrBase.SetBusyState (false); + } + else if (rIdent == "applyselect") + { + mrBase.SetBusyState (true); + AssignMasterPageToSelectedSlides (GetSelectedMasterPage()); + mrBase.SetBusyState (false); + } + else if (rIdent == "large") + { + mrBase.SetBusyState (true); + mpContainer->SetPreviewSize(MasterPageContainer::LARGE); + mrBase.SetBusyState (false); + if (mxSidebar.is()) + mxSidebar->requestLayout(); + } + else if (rIdent == "small") + { + mrBase.SetBusyState (true); + mpContainer->SetPreviewSize(MasterPageContainer::SMALL); + mrBase.SetBusyState (false); + if (mxSidebar.is()) + mxSidebar->requestLayout(); + } + else if (rIdent == "edit") + { + using namespace ::com::sun::star; + uno::Reference<drawing::XDrawPage> xSelectedMaster; + SdPage* pMasterPage = GetSelectedMasterPage(); + assert(pMasterPage); //rhbz#902884 + if (pMasterPage) + xSelectedMaster.set(pMasterPage->getUnoPage(), uno::UNO_QUERY); + SfxViewFrame* pViewFrame = mrBase.GetViewFrame(); + if (pViewFrame != nullptr && xSelectedMaster.is()) + { + SfxDispatcher* pDispatcher = pViewFrame->GetDispatcher(); + if (pDispatcher != nullptr) + { + sal_uInt16 nIndex = mxPreviewValueSet->GetSelectedItemId(); + pDispatcher->Execute(SID_MASTERPAGE, SfxCallMode::SYNCHRON); + mxPreviewValueSet->SelectItem (nIndex); + mrBase.GetDrawController().setCurrentPage(xSelectedMaster); + } + } + } +} + +IMPL_LINK(MasterPagesSelector, ContainerChangeListener, MasterPageContainerChangeEvent&, rEvent, void) +{ + NotifyContainerChangeEvent(rEvent); +} + +SdPage* MasterPagesSelector::GetSelectedMasterPage() +{ + const ::osl::MutexGuard aGuard (maMutex); + + SdPage* pMasterPage = nullptr; + sal_uInt16 nIndex = mxPreviewValueSet->GetSelectedItemId(); + UserData* pData = GetUserData(nIndex); + if (pData != nullptr) + { + pMasterPage = mpContainer->GetPageObjectForToken(pData->second, true); + } + return pMasterPage; +} + +/** Assemble a list of all slides of the document and pass it to + AssignMasterPageToPageList(). +*/ +void MasterPagesSelector::AssignMasterPageToAllSlides (SdPage* pMasterPage) +{ + if (pMasterPage == nullptr) + return; + + sal_uInt16 nPageCount = mrDocument.GetSdPageCount(PageKind::Standard); + if (nPageCount == 0) + return; + + // Get a list of all pages. As a little optimization we only + // include pages that do not already have the given master page + // assigned. + OUString sFullLayoutName(pMasterPage->GetLayoutName()); + ::sd::slidesorter::SharedPageSelection pPageList = + std::make_shared<::sd::slidesorter::SlideSorterViewShell::PageSelection>(); + for (sal_uInt16 nPageIndex=0; nPageIndex<nPageCount; nPageIndex++) + { + SdPage* pPage = mrDocument.GetSdPage (nPageIndex, PageKind::Standard); + if (pPage != nullptr && pPage->GetLayoutName() != sFullLayoutName) + { + pPageList->push_back (pPage); + } + } + + AssignMasterPageToPageList(pMasterPage, pPageList); +} + +/** Assemble a list of the currently selected slides (selected in a visible + slide sorter) and pass it to AssignMasterPageToPageList(). +*/ +void MasterPagesSelector::AssignMasterPageToSelectedSlides ( + SdPage* pMasterPage) +{ + using namespace ::sd::slidesorter; + using namespace ::sd::slidesorter::controller; + + if (pMasterPage == nullptr) + return; + + // Find a visible slide sorter. + SlideSorterViewShell* pSlideSorter = SlideSorterViewShell::GetSlideSorter(mrBase); + if (pSlideSorter == nullptr) + return; + + // Get a list of selected pages. + SharedPageSelection pPageSelection = pSlideSorter->GetPageSelection(); + if (pPageSelection->empty()) + return; + + AssignMasterPageToPageList(pMasterPage, pPageSelection); + + // Restore the previous selection. + pSlideSorter->SetPageSelection(pPageSelection); +} + +void MasterPagesSelector::AssignMasterPageToPageList ( + SdPage* pMasterPage, + const std::shared_ptr<std::vector<SdPage*>>& rPageList) +{ + DocumentHelper::AssignMasterPageToPageList(mrDocument, pMasterPage, rPageList); +} + +void MasterPagesSelector::NotifyContainerChangeEvent (const MasterPageContainerChangeEvent& rEvent) +{ + const ::osl::MutexGuard aGuard (maMutex); + + switch (rEvent.meEventType) + { + case MasterPageContainerChangeEvent::EventType::SIZE_CHANGED: + mxPreviewValueSet->SetPreviewSize(mpContainer->GetPreviewSizePixel()); + UpdateAllPreviews(); + break; + + case MasterPageContainerChangeEvent::EventType::PREVIEW_CHANGED: + { + int nIndex (GetIndexForToken(rEvent.maChildToken)); + if (nIndex >= 0) + { + mxPreviewValueSet->SetItemImage ( + static_cast<sal_uInt16>(nIndex), + mpContainer->GetPreviewForToken(rEvent.maChildToken)); + mxPreviewValueSet->Invalidate(mxPreviewValueSet->GetItemRect(static_cast<sal_uInt16>(nIndex))); + } + } + break; + + case MasterPageContainerChangeEvent::EventType::DATA_CHANGED: + { + InvalidateItem(rEvent.maChildToken); + Fill(); + } + break; + + case MasterPageContainerChangeEvent::EventType::CHILD_REMOVED: + { + int nIndex (GetIndexForToken(rEvent.maChildToken)); + SetItem(nIndex, MasterPageContainer::NIL_TOKEN); + break; + } + + default: + break; + } +} + +MasterPagesSelector::UserData* MasterPagesSelector::GetUserData (int nIndex) const +{ + const ::osl::MutexGuard aGuard (maMutex); + + if (nIndex>0 && o3tl::make_unsigned(nIndex)<=mxPreviewValueSet->GetItemCount()) + return static_cast<UserData*>(mxPreviewValueSet->GetItemData(static_cast<sal_uInt16>(nIndex))); + else + return nullptr; +} + +void MasterPagesSelector::SetUserData (int nIndex, std::unique_ptr<UserData> pData) +{ + const ::osl::MutexGuard aGuard (maMutex); + + delete GetUserData(nIndex); + mxPreviewValueSet->SetItemData(static_cast<sal_uInt16>(nIndex), pData.release()); +} + +void MasterPagesSelector::SetItem ( + sal_uInt16 nIndex, + MasterPageContainer::Token aToken) +{ + const ::osl::MutexGuard aGuard (maMutex); + + RemoveTokenToIndexEntry(nIndex,aToken); + + if (nIndex <= 0) + return; + + if (aToken != MasterPageContainer::NIL_TOKEN) + { + Image aPreview (mpContainer->GetPreviewForToken(aToken)); + MasterPageContainer::PreviewState eState (mpContainer->GetPreviewState(aToken)); + + if (aPreview.GetSizePixel().Width()>0) + { + if (mxPreviewValueSet->GetItemPos(nIndex) != VALUESET_ITEM_NOTFOUND) + { + mxPreviewValueSet->SetItemImage(nIndex,aPreview); + mxPreviewValueSet->SetItemText(nIndex, mpContainer->GetPageNameForToken(aToken)); + } + else + { + mxPreviewValueSet->InsertItem ( + nIndex, + aPreview, + mpContainer->GetPageNameForToken(aToken), + nIndex); + } + SetUserData(nIndex, std::make_unique<UserData>(nIndex,aToken)); + + AddTokenToIndexEntry(nIndex,aToken); + } + + if (eState == MasterPageContainer::PS_CREATABLE) + mpContainer->RequestPreview(aToken); + } + else + { + mxPreviewValueSet->RemoveItem(nIndex); + } + +} + +void MasterPagesSelector::AddTokenToIndexEntry ( + sal_uInt16 nIndex, + MasterPageContainer::Token aToken) +{ + const ::osl::MutexGuard aGuard (maMutex); + + maTokenToValueSetIndex[aToken] = nIndex; +} + +void MasterPagesSelector::RemoveTokenToIndexEntry ( + sal_uInt16 nIndex, + MasterPageContainer::Token aNewToken) +{ + const ::osl::MutexGuard aGuard (maMutex); + + UserData* pData = GetUserData(nIndex); + if (pData != nullptr) + { + // Get the token that the index pointed to previously. + MasterPageContainer::Token aOldToken (pData->second); + + if (aNewToken != aOldToken + && nIndex == GetIndexForToken(aOldToken)) + { + maTokenToValueSetIndex[aOldToken] = 0; + } + } +} + +void MasterPagesSelector::InvalidatePreview (const SdPage* pPage) +{ + const ::osl::MutexGuard aGuard (maMutex); + + for (size_t nIndex=1; nIndex<=mxPreviewValueSet->GetItemCount(); nIndex++) + { + UserData* pData = GetUserData(nIndex); + if (pData != nullptr) + { + MasterPageContainer::Token aToken (pData->second); + if (pPage == mpContainer->GetPageObjectForToken(aToken,false)) + { + mpContainer->InvalidatePreview(aToken); + mpContainer->RequestPreview(aToken); + break; + } + } + } +} + +void MasterPagesSelector::UpdateAllPreviews() +{ + const ::osl::MutexGuard aGuard (maMutex); + + for (size_t nIndex=1; nIndex<=mxPreviewValueSet->GetItemCount(); nIndex++) + { + UserData* pData = GetUserData(nIndex); + if (pData != nullptr) + { + MasterPageContainer::Token aToken (pData->second); + mxPreviewValueSet->SetItemImage( + nIndex, + mpContainer->GetPreviewForToken(aToken)); + if (mpContainer->GetPreviewState(aToken) == MasterPageContainer::PS_CREATABLE) + mpContainer->RequestPreview(aToken); + } + } + mxPreviewValueSet->Rearrange(); +} + +void MasterPagesSelector::ClearPageSet() +{ + const ::osl::MutexGuard aGuard (maMutex); + + for (size_t nIndex=1; nIndex<=mxPreviewValueSet->GetItemCount(); nIndex++) + { + UserData* pData = GetUserData(nIndex); + delete pData; + } + mxPreviewValueSet->Clear(); +} + +void MasterPagesSelector::SetHelpId( const OString& aId ) +{ + const ::osl::MutexGuard aGuard (maMutex); + + mxPreviewValueSet->SetHelpId( aId ); +} + +sal_Int32 MasterPagesSelector::GetIndexForToken (MasterPageContainer::Token aToken) const +{ + const ::osl::MutexGuard aGuard (maMutex); + + TokenToValueSetIndex::const_iterator iIndex (maTokenToValueSetIndex.find(aToken)); + if (iIndex != maTokenToValueSetIndex.end()) + return iIndex->second; + else + return -1; +} + +void MasterPagesSelector::Clear() +{ + const ::osl::MutexGuard aGuard (maMutex); + + ClearPageSet(); +} + +void MasterPagesSelector::InvalidateItem (MasterPageContainer::Token aToken) +{ + const ::osl::MutexGuard aGuard (maMutex); + + auto iItem = std::find(maCurrentItemList.begin(), maCurrentItemList.end(), aToken); + if (iItem != maCurrentItemList.end()) + *iItem = MasterPageContainer::NIL_TOKEN; +} + +void MasterPagesSelector::UpdateItemList (::std::unique_ptr<ItemList> && pNewItemList) +{ + const ::osl::MutexGuard aGuard (maMutex); + + ItemList::const_iterator iNewItem (pNewItemList->begin()); + ItemList::const_iterator iCurrentItem (maCurrentItemList.begin()); + ItemList::const_iterator iNewEnd (pNewItemList->end()); + ItemList::const_iterator iCurrentEnd (maCurrentItemList.end()); + sal_uInt16 nIndex (1); + + // Update existing items. + for ( ; iNewItem!=iNewEnd && iCurrentItem!=iCurrentEnd; ++iNewItem, ++iCurrentItem,++nIndex) + { + if (*iNewItem != *iCurrentItem) + { + SetItem(nIndex,*iNewItem); + } + } + + // Append new items. + for ( ; iNewItem!=iNewEnd; ++iNewItem,++nIndex) + { + SetItem(nIndex,*iNewItem); + } + + // Remove trailing items. + for ( ; iCurrentItem!=iCurrentEnd; ++iCurrentItem,++nIndex) + { + SetItem(nIndex,MasterPageContainer::NIL_TOKEN); + } + + maCurrentItemList.swap(*pNewItemList); + + mxPreviewValueSet->Rearrange(); + if (mxSidebar.is()) + mxSidebar->requestLayout(); +} + +css::ui::LayoutSize MasterPagesSelector::GetHeightForWidth (const sal_Int32 nWidth) +{ + const sal_Int32 nHeight (GetPreferredHeight(nWidth)); + return css::ui::LayoutSize(nHeight,nHeight,nHeight); +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPagesSelector.hxx b/sd/source/ui/sidebar/MasterPagesSelector.hxx new file mode 100644 index 000000000..1b6932789 --- /dev/null +++ b/sd/source/ui/sidebar/MasterPagesSelector.hxx @@ -0,0 +1,180 @@ +/* -*- 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 . + */ + +#pragma once + +#include <map> +#include <memory> +#include "MasterPageContainer.hxx" +#include "PreviewValueSet.hxx" +#include <sfx2/sidebar/ILayoutableWindow.hxx> +#include <sfx2/sidebar/PanelLayout.hxx> + +#include <osl/mutex.hxx> + +namespace com::sun::star::ui { class XSidebar; } +class MouseEvent; +class SdDrawDocument; +class SdPage; + +namespace sd { +class ViewShellBase; +} + +namespace sd::sidebar { + +/** Base class of a menu that lets the user select from a list of + templates or designs that are loaded from files. +*/ +class MasterPagesSelector : public PanelLayout + , public sfx2::sidebar::ILayoutableWindow +{ +public: + MasterPagesSelector ( + weld::Widget* pParent, + SdDrawDocument& rDocument, + ViewShellBase& rBase, + const std::shared_ptr<MasterPageContainer>& rpContainer, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar, + const OUString& rUIFileName, + const OString& rValueSetName); + virtual ~MasterPagesSelector() override; + + virtual void LateInit(); + + sal_Int32 GetPreferredHeight (sal_Int32 nWidth); + + /** Make the selector empty. This method clear the value set from any + entries. Override this method to add functionality, especially to + destroy objects set as data items at the value set. + */ + void ClearPageSet(); + + void SetHelpId( const OString& aId ); + + /** Mark the preview that belongs to the given index as not up-to-date + anymore with respect to page content or preview size. + The implementation of this method will either sunchronously or + asynchronously call UpdatePreview(). + @param nIndex + Index into the value set control that is used for displaying the + previews. + */ + void InvalidatePreview (const SdPage* pPage); + + void UpdateAllPreviews(); + + void ShowContextMenu(const Point* pPos); + + // ILayoutableWindow + virtual css::ui::LayoutSize GetHeightForWidth (const sal_Int32 nWidth) override; + +protected: + mutable ::osl::Mutex maMutex; + std::shared_ptr<MasterPageContainer> mpContainer; + + std::unique_ptr<PreviewValueSet> mxPreviewValueSet; + std::unique_ptr<weld::CustomWeld> mxPreviewValueSetWin; + + SdDrawDocument& mrDocument; + ViewShellBase& mrBase; + + SdPage* GetSelectedMasterPage(); + + /** Assign the given master page to all slides of the document. + @param pMasterPage + The master page to assign to all slides. + */ + void AssignMasterPageToAllSlides (SdPage* pMasterPage); + + /** Assign the given master page to all slides that are selected in a + slide sorter that is displayed in the lef or center pane. When both + panes display a slide sorter then the one in the center pane is + used. + */ + void AssignMasterPageToSelectedSlides (SdPage* pMasterPage); + + virtual void AssignMasterPageToPageList ( + SdPage* pMasterPage, + const std::shared_ptr<std::vector<SdPage*>>& rPageList); + + virtual void NotifyContainerChangeEvent (const MasterPageContainerChangeEvent& rEvent); + + typedef ::std::pair<int, MasterPageContainer::Token> UserData; + UserData* GetUserData (int nIndex) const; + void SetUserData (int nIndex, std::unique_ptr<UserData> pData); + + sal_Int32 GetIndexForToken (MasterPageContainer::Token aToken) const; + typedef ::std::vector<MasterPageContainer::Token> ItemList; + void UpdateItemList (::std::unique_ptr<ItemList> && pList); + void Clear(); + /** Invalidate the specified item so that on the next Fill() this item + is updated. + */ + void InvalidateItem (MasterPageContainer::Token aToken); + + // For every item in the ValueSet we store its associated token. This + // allows a faster access and easier change tracking. + ItemList maCurrentItemList; + typedef ::std::map<MasterPageContainer::Token,sal_Int32> TokenToValueSetIndex; + TokenToValueSetIndex maTokenToValueSetIndex; + + ItemList maLockedMasterPages; + /** Lock master pages in the given list and release locks that were + previously acquired. + */ + void UpdateLocks (const ItemList& rItemList); + + void Fill(); + virtual void Fill (ItemList& rItemList) = 0; + + /** Give derived classes the opportunity to provide their own context + menu. If they do then they probably have to provide their own + Execute() and GetState() methods as well. + */ + virtual OUString GetContextMenuUIFile() const; + + virtual void ProcessPopupMenu(weld::Menu& rMenu); + virtual void ExecuteCommand(const OString& rIdent); + +private: + css::uno::Reference<css::ui::XSidebar> mxSidebar; + + /** The offset between ValueSet index and MasterPageContainer::Token + last seen. This value is used heuristically to speed up the lookup + of an index for a token. + */ + DECL_LINK(ClickHandler, ValueSet*, void); + DECL_LINK(ContextMenuHandler, const Point*, void); + DECL_LINK(ContainerChangeListener, MasterPageContainerChangeEvent&, void); + + void SetItem ( + sal_uInt16 nIndex, + MasterPageContainer::Token aToken); + void AddTokenToIndexEntry ( + sal_uInt16 nIndex, + MasterPageContainer::Token aToken); + void RemoveTokenToIndexEntry ( + sal_uInt16 nIndex, + MasterPageContainer::Token aToken); +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/NavigatorWrapper.cxx b/sd/source/ui/sidebar/NavigatorWrapper.cxx new file mode 100644 index 000000000..95d4a66ae --- /dev/null +++ b/sd/source/ui/sidebar/NavigatorWrapper.cxx @@ -0,0 +1,49 @@ +/* -*- 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 "NavigatorWrapper.hxx" +#include <ViewShellBase.hxx> +#include <navigatr.hxx> + +namespace sd::sidebar { + +NavigatorWrapper::NavigatorWrapper ( + weld::Widget* pParent, + sd::ViewShellBase& rViewShellBase, + SfxBindings* pBindings) + : SdNavigatorWin(pParent, pBindings, nullptr) + , mrViewShellBase(rViewShellBase) +{ + SetUpdateRequestFunctor( + [this] () { return this->UpdateNavigator(); }); +} + +css::ui::LayoutSize NavigatorWrapper::GetHeightForWidth (const sal_Int32) +{ + return css::ui::LayoutSize(-1,-1,-1); +} + +void NavigatorWrapper::UpdateNavigator() +{ + InitTreeLB(mrViewShellBase.GetDocument()); +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/NavigatorWrapper.hxx b/sd/source/ui/sidebar/NavigatorWrapper.hxx new file mode 100644 index 000000000..6632d796f --- /dev/null +++ b/sd/source/ui/sidebar/NavigatorWrapper.hxx @@ -0,0 +1,57 @@ +/* -*- 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 . + */ +#pragma once + +#include <sfx2/sidebar/ILayoutableWindow.hxx> +#include <navigatr.hxx> + +class SfxBindings; +namespace sd { class ViewShellBase; } + +namespace sd::sidebar { + +/** Present the navigator as control that can be displayed inside the + sidebar. + This wrapper has two main responsibilities: + - Watch for document changes and update the navigator when one + happens. + - Forward size changes from sidebar to navigator. +*/ +class NavigatorWrapper + : public SdNavigatorWin, + public sfx2::sidebar::ILayoutableWindow +{ +public: + NavigatorWrapper ( + weld::Widget* pParent, + sd::ViewShellBase& rViewShellBase, + SfxBindings* pBindings); + + // From ILayoutableWindow + virtual css::ui::LayoutSize GetHeightForWidth (const sal_Int32 nWidth) override; + +private: + ViewShellBase& mrViewShellBase; + + void UpdateNavigator(); +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/PageMarginUtils.hxx b/sd/source/ui/sidebar/PageMarginUtils.hxx new file mode 100644 index 000000000..9a1f83493 --- /dev/null +++ b/sd/source/ui/sidebar/PageMarginUtils.hxx @@ -0,0 +1,159 @@ +/* -*- 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 <cmath> +#include <tools/long.hxx> +#define SDPAGE_NO_MARGIN 0 +#define SDPAGE_NARROW_VALUE 635 +#define SDPAGE_MODERATE_LR 955 +#define SDPAGE_NORMAL_VALUE 1000 +#define SDPAGE_WIDE_VALUE1 1270 +#define SDPAGE_WIDE_VALUE2 2540 +#define SDPAGE_WIDE_VALUE3 1590 +#define SDPAGE_UNIT_THRESHOLD 5 + +namespace sd::sidebar{ + +bool IsNone( const ::tools::Long nPageLeftMargin, const ::tools::Long nPageRightMargin, + const ::tools::Long nPageTopMargin, const ::tools::Long nPageBottomMargin ) +{ + return( std::abs(nPageLeftMargin - SDPAGE_NO_MARGIN) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageRightMargin - SDPAGE_NO_MARGIN ) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageTopMargin - SDPAGE_NO_MARGIN) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageBottomMargin - SDPAGE_NO_MARGIN) <= SDPAGE_UNIT_THRESHOLD ); +} + +void SetNone( ::tools::Long& nPageLeftMargin, ::tools::Long& nPageRightMargin, + ::tools::Long& nPageTopMargin, ::tools::Long& nPageBottomMargin ) +{ + nPageLeftMargin = SDPAGE_NO_MARGIN; + nPageRightMargin = SDPAGE_NO_MARGIN; + nPageTopMargin = SDPAGE_NO_MARGIN; + nPageBottomMargin = SDPAGE_NO_MARGIN; +} + +bool IsNarrow( const ::tools::Long nPageLeftMargin, const ::tools::Long nPageRightMargin, + const ::tools::Long nPageTopMargin, const ::tools::Long nPageBottomMargin ) +{ + return( std::abs(nPageLeftMargin - SDPAGE_NARROW_VALUE) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageRightMargin - SDPAGE_NARROW_VALUE) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageTopMargin - SDPAGE_NARROW_VALUE) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageBottomMargin - SDPAGE_NARROW_VALUE) <= SDPAGE_UNIT_THRESHOLD ); +} + +void SetNarrow( ::tools::Long& nPageLeftMargin, ::tools::Long& nPageRightMargin, + ::tools::Long& nPageTopMargin, ::tools::Long& nPageBottomMargin ) +{ + nPageLeftMargin = SDPAGE_NARROW_VALUE; + nPageRightMargin = SDPAGE_NARROW_VALUE; + nPageTopMargin = SDPAGE_NARROW_VALUE; + nPageBottomMargin = SDPAGE_NARROW_VALUE; +} + +bool IsModerate( const ::tools::Long nPageLeftMargin, const ::tools::Long nPageRightMargin, + const ::tools::Long nPageTopMargin, const ::tools::Long nPageBottomMargin ) +{ + return( std::abs(nPageLeftMargin - SDPAGE_MODERATE_LR) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageRightMargin - SDPAGE_MODERATE_LR) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageTopMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageBottomMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD ); +} + +void SetModerate( ::tools::Long& nPageLeftMargin, ::tools::Long& nPageRightMargin, + ::tools::Long& nPageTopMargin, ::tools::Long& nPageBottomMargin ) +{ + nPageLeftMargin = SDPAGE_MODERATE_LR; + nPageRightMargin = SDPAGE_MODERATE_LR; + nPageTopMargin = SDPAGE_WIDE_VALUE1; + nPageBottomMargin = SDPAGE_WIDE_VALUE1; +} + +bool IsNormal075( const ::tools::Long nPageLeftMargin, const ::tools::Long nPageRightMargin, + const ::tools::Long nPageTopMargin, const ::tools::Long nPageBottomMargin ) +{ + return( std::abs(nPageLeftMargin - SDPAGE_NORMAL_VALUE) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageRightMargin - SDPAGE_NORMAL_VALUE) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageTopMargin - SDPAGE_NORMAL_VALUE) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageBottomMargin - SDPAGE_NORMAL_VALUE) <= SDPAGE_UNIT_THRESHOLD ); +} + +void SetNormal075( ::tools::Long& nPageLeftMargin, ::tools::Long& nPageRightMargin, + ::tools::Long& nPageTopMargin, ::tools::Long& nPageBottomMargin ) +{ + nPageLeftMargin = SDPAGE_NORMAL_VALUE; + nPageRightMargin = SDPAGE_NORMAL_VALUE; + nPageTopMargin = SDPAGE_NORMAL_VALUE; + nPageBottomMargin = SDPAGE_NORMAL_VALUE; +} + +bool IsNormal100( const ::tools::Long nPageLeftMargin, const ::tools::Long nPageRightMargin, + const ::tools::Long nPageTopMargin, const ::tools::Long nPageBottomMargin ) +{ + return( std::abs(nPageLeftMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageRightMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageTopMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageBottomMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD ); +} + +void SetNormal100( ::tools::Long& nPageLeftMargin, ::tools::Long& nPageRightMargin, + ::tools::Long& nPageTopMargin, ::tools::Long& nPageBottomMargin ) +{ + nPageLeftMargin = SDPAGE_WIDE_VALUE1; + nPageRightMargin = SDPAGE_WIDE_VALUE1; + nPageTopMargin = SDPAGE_WIDE_VALUE1; + nPageBottomMargin = SDPAGE_WIDE_VALUE1; +} + +bool IsNormal125( const ::tools::Long nPageLeftMargin, const ::tools::Long nPageRightMargin, + const ::tools::Long nPageTopMargin, const ::tools::Long nPageBottomMargin ) +{ + return( std::abs(nPageLeftMargin - SDPAGE_WIDE_VALUE3) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageRightMargin - SDPAGE_WIDE_VALUE3) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageTopMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageBottomMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD ); +} + +void SetNormal125( ::tools::Long& nPageLeftMargin, ::tools::Long& nPageRightMargin, + ::tools::Long& nPageTopMargin, ::tools::Long& nPageBottomMargin ) +{ + nPageLeftMargin = SDPAGE_WIDE_VALUE3; + nPageRightMargin = SDPAGE_WIDE_VALUE3; + nPageTopMargin = SDPAGE_WIDE_VALUE1; + nPageBottomMargin = SDPAGE_WIDE_VALUE1; +} + +bool IsWide( const ::tools::Long nPageLeftMargin, const ::tools::Long nPageRightMargin, + const ::tools::Long nPageTopMargin, const ::tools::Long nPageBottomMargin ) +{ + return( std::abs(nPageLeftMargin - SDPAGE_WIDE_VALUE2) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageRightMargin - SDPAGE_WIDE_VALUE2) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageTopMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageBottomMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD ); +} + +void SetWide( ::tools::Long& nPageLeftMargin, ::tools::Long& nPageRightMargin, + ::tools::Long& nPageTopMargin, ::tools::Long& nPageBottomMargin ) +{ + nPageLeftMargin = SDPAGE_WIDE_VALUE2; + nPageRightMargin = SDPAGE_WIDE_VALUE2; + nPageTopMargin = SDPAGE_WIDE_VALUE1; + nPageBottomMargin = SDPAGE_WIDE_VALUE1; +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/PanelFactory.cxx b/sd/source/ui/sidebar/PanelFactory.cxx new file mode 100644 index 000000000..c7ca8c25c --- /dev/null +++ b/sd/source/ui/sidebar/PanelFactory.cxx @@ -0,0 +1,141 @@ +/* -*- 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 "PanelFactory.hxx" +#include <framework/Pane.hxx> +#include <ViewShellBase.hxx> +#include <DrawController.hxx> +#include "LayoutMenu.hxx" +#include "CurrentMasterPagesSelector.hxx" +#include "RecentMasterPagesSelector.hxx" +#include "AllMasterPagesSelector.hxx" +#include <CustomAnimationPane.hxx> +#include "NavigatorWrapper.hxx" +#include <SlideTransitionPane.hxx> +#include <TableDesignPane.hxx> +#include "SlideBackground.hxx" + +#include <sfx2/sidebar/SidebarPanelBase.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <comphelper/namedvaluecollection.hxx> +#include <comphelper/servicehelper.hxx> +#include <vcl/weldutils.hxx> + +using namespace css; +using namespace css::uno; +using namespace ::sd::framework; + +namespace sd::sidebar { + +//----- PanelFactory -------------------------------------------------------- + +PanelFactory::PanelFactory() +{ +} + +PanelFactory::~PanelFactory() +{ +} + +// XUIElementFactory + +Reference<ui::XUIElement> SAL_CALL PanelFactory::createUIElement ( + const OUString& rsUIElementResourceURL, + const css::uno::Sequence<css::beans::PropertyValue>& rArguments) +{ + // Process arguments. + const ::comphelper::NamedValueCollection aArguments (rArguments); + Reference<frame::XFrame> xFrame (aArguments.getOrDefault("Frame", Reference<frame::XFrame>())); + Reference<awt::XWindow> xParentWindow (aArguments.getOrDefault("ParentWindow", Reference<awt::XWindow>())); + Reference<ui::XSidebar> xSidebar (aArguments.getOrDefault("Sidebar", Reference<ui::XSidebar>())); + + // Throw exceptions when the arguments are not as expected. + weld::Widget* pParent(nullptr); + if (weld::TransportAsXWindow* pTunnel = dynamic_cast<weld::TransportAsXWindow*>(xParentWindow.get())) + pParent = pTunnel->getWidget(); + + if (!pParent) + throw RuntimeException( + "PanelFactory::createUIElement called without ParentWindow"); + if ( ! xFrame.is()) + throw RuntimeException( + "PanelFactory::createUIElement called without XFrame"); + + // Tunnel through the controller to obtain a ViewShellBase. + ViewShellBase* pBase = nullptr; + auto pController = comphelper::getFromUnoTunnel<sd::DrawController>(xFrame->getController()); + if (pController != nullptr) + pBase = pController->GetViewShellBase(); + if (pBase == nullptr) + throw RuntimeException("can not get ViewShellBase for frame"); + + // Get bindings from given arguments. + const sal_uInt64 nBindingsValue (aArguments.getOrDefault("SfxBindings", sal_uInt64(0))); + SfxBindings* pBindings = reinterpret_cast<SfxBindings*>(nBindingsValue); + + // Create a framework view. + std::unique_ptr<PanelLayout> xControl; + css::ui::LayoutSize aLayoutSize (-1,-1,-1); + + /** Note that these names have to be identical to (the tail of) + the entries in officecfg/registry/data/org/openoffice/Office/Impress.xcu + for the TaskPanelFactory. + */ + if (rsUIElementResourceURL.endsWith("/CustomAnimations")) + xControl = std::make_unique<CustomAnimationPane>(pParent, *pBase); + else if (rsUIElementResourceURL.endsWith("/Layouts")) + xControl = std::make_unique<LayoutMenu>(pParent, *pBase, xSidebar); + else if (rsUIElementResourceURL.endsWith("/AllMasterPages")) + xControl = AllMasterPagesSelector::Create(pParent, *pBase, xSidebar); + else if (rsUIElementResourceURL.endsWith("/RecentMasterPages")) + xControl = RecentMasterPagesSelector::Create(pParent, *pBase, xSidebar); + else if (rsUIElementResourceURL.endsWith("/UsedMasterPages")) + xControl = CurrentMasterPagesSelector::Create(pParent, *pBase, xSidebar); + else if (rsUIElementResourceURL.endsWith("/SlideTransitions")) + xControl = std::make_unique<SlideTransitionPane>(pParent, *pBase); + else if (rsUIElementResourceURL.endsWith("/TableDesign")) + xControl = std::make_unique<TableDesignPane>(pParent, *pBase); + else if (rsUIElementResourceURL.endsWith("/NavigatorPanel")) + xControl = std::make_unique<NavigatorWrapper>(pParent, *pBase, pBindings); + else if (rsUIElementResourceURL.endsWith("/SlideBackgroundPanel")) + xControl = std::make_unique<SlideBackground>(pParent, *pBase, xFrame, pBindings); + + if (!xControl) + throw lang::IllegalArgumentException(); + + // Create a wrapper around the control that implements the + // necessary UNO interfaces. + return sfx2::sidebar::SidebarPanelBase::Create( + rsUIElementResourceURL, + xFrame, + std::move(xControl), + aLayoutSize); +} + +} // end of namespace sd::sidebar + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +org_openoffice_comp_Draw_framework_PanelFactory_get_implementation(css::uno::XComponentContext* /*context*/, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new sd::sidebar::PanelFactory); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/PanelFactory.hxx b/sd/source/ui/sidebar/PanelFactory.hxx new file mode 100644 index 000000000..77fc17dbc --- /dev/null +++ b/sd/source/ui/sidebar/PanelFactory.hxx @@ -0,0 +1,49 @@ +/* -*- 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 . + */ +#pragma once + +#include <comphelper/compbase.hxx> + +#include <com/sun/star/ui/XUIElementFactory.hpp> + +namespace sd::sidebar { + +typedef comphelper::WeakComponentImplHelper < + css::ui::XUIElementFactory + > PanelFactoryInterfaceBase; + +class PanelFactory final + : public PanelFactoryInterfaceBase +{ +public: + explicit PanelFactory (); + virtual ~PanelFactory() override; + PanelFactory(const PanelFactory&) = delete; + PanelFactory& operator=(const PanelFactory&) = delete; + + // XUIElementFactory + + css::uno::Reference<css::ui::XUIElement> SAL_CALL createUIElement ( + const OUString& rsResourceURL, + const css::uno::Sequence<css::beans::PropertyValue>& rArguments) override; +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/PreviewValueSet.cxx b/sd/source/ui/sidebar/PreviewValueSet.cxx new file mode 100644 index 000000000..f752d60eb --- /dev/null +++ b/sd/source/ui/sidebar/PreviewValueSet.cxx @@ -0,0 +1,127 @@ +/* -*- 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 "PreviewValueSet.hxx" +#include <vcl/commandevent.hxx> + +namespace sd::sidebar { + +const int gnBorderWidth(3); +const int gnBorderHeight(3); + +PreviewValueSet::PreviewValueSet() + : ValueSet(nullptr) + , maPreviewSize(10,10) +{ + SetStyle ( + GetStyle() + & ~(WB_ITEMBORDER)// | WB_MENUSTYLEVALUESET) + // | WB_FLATVALUESET); + ); +} + +void PreviewValueSet::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + ValueSet::SetDrawingArea(pDrawingArea); + + SetColCount(2); + SetExtraSpacing (2); +} + +PreviewValueSet::~PreviewValueSet() +{ +} + +void PreviewValueSet::SetPreviewSize (const Size& rSize) +{ + maPreviewSize = rSize; +} + +void PreviewValueSet::SetContextMenuHandler(const Link<const Point*, void>& rLink) +{ + maContextMenuHandler = rLink; +} + +bool PreviewValueSet::Command(const CommandEvent& rEvent) +{ + if (rEvent.GetCommand() != CommandEventId::ContextMenu) + return ValueSet::Command(rEvent); + maContextMenuHandler.Call(rEvent.IsMouseEvent() ? &rEvent.GetMousePosPixel() : nullptr); + return true; +} + +void PreviewValueSet::Resize() +{ + ValueSet::Resize(); + + Size aWindowSize (GetOutputSizePixel()); + if (!aWindowSize.IsEmpty()) + { + Rearrange(); + } +} + +void PreviewValueSet::Rearrange() +{ + sal_uInt16 nNewColumnCount (CalculateColumnCount ( + GetOutputSizePixel().Width())); + sal_uInt16 nNewRowCount (CalculateRowCount (nNewColumnCount)); + + SetFormat(); + SetColCount(nNewColumnCount); + SetLineCount(nNewRowCount); +} + +sal_uInt16 PreviewValueSet::CalculateColumnCount (int nWidth) const +{ + int nColumnCount = 0; + if (nWidth > 0) + { + nColumnCount = nWidth / (maPreviewSize.Width() + 2*gnBorderWidth); + if (nColumnCount < 1) + nColumnCount = 1; + } + return static_cast<sal_uInt16>(nColumnCount); +} + +sal_uInt16 PreviewValueSet::CalculateRowCount (sal_uInt16 nColumnCount) const +{ + int nRowCount = 0; + int nItemCount = GetItemCount(); + if (nColumnCount > 0) + { + nRowCount = (nItemCount+nColumnCount-1) / nColumnCount; + if (nRowCount < 1) + nRowCount = 1; + } + + return static_cast<sal_uInt16>(nRowCount); +} + +sal_Int32 PreviewValueSet::GetPreferredHeight (sal_Int32 nWidth) +{ + int nRowCount (CalculateRowCount(CalculateColumnCount(nWidth))); + int nItemHeight (maPreviewSize.Height()); + + return nRowCount * (nItemHeight + 2*gnBorderHeight); +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/PreviewValueSet.hxx b/sd/source/ui/sidebar/PreviewValueSet.hxx new file mode 100644 index 000000000..adab3c78a --- /dev/null +++ b/sd/source/ui/sidebar/PreviewValueSet.hxx @@ -0,0 +1,59 @@ +/* -*- 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 . + */ + +#pragma once + +#include <svtools/valueset.hxx> + +namespace sd::sidebar +{ +/** Adapt the svtools valueset to the needs of the master page controls. +*/ +class PreviewValueSet : public ValueSet +{ +public: + explicit PreviewValueSet(); + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override; + virtual ~PreviewValueSet() override; + + void SetContextMenuHandler(const Link<const Point*, void>& rLink); + + virtual void Resize() override; + virtual bool Command(const CommandEvent& rEvent) override; + + void SetPreviewSize(const Size& rSize); + + sal_Int32 GetPreferredHeight(sal_Int32 nWidth); + + /** Set the number of rows and columns according to the current number + of items. Call this method when new items have been inserted. + */ + void Rearrange(); + +private: + Link<const Point*, void> maContextMenuHandler; + Size maPreviewSize; + + sal_uInt16 CalculateColumnCount(int nWidth) const; + sal_uInt16 CalculateRowCount(sal_uInt16 nColumnCount) const; +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/RecentMasterPagesSelector.cxx b/sd/source/ui/sidebar/RecentMasterPagesSelector.cxx new file mode 100644 index 000000000..6e5a46c73 --- /dev/null +++ b/sd/source/ui/sidebar/RecentMasterPagesSelector.cxx @@ -0,0 +1,138 @@ +/* -*- 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 "RecentMasterPagesSelector.hxx" + +#include <ViewShellBase.hxx> +#include "RecentlyUsedMasterPages.hxx" +#include <MasterPageObserver.hxx> +#include <sdpage.hxx> +#include <drawdoc.hxx> +#include <helpids.h> + +namespace sd::sidebar { + +std::unique_ptr<PanelLayout> RecentMasterPagesSelector::Create ( + weld::Widget* pParent, + ViewShellBase& rViewShellBase, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar) +{ + SdDrawDocument* pDocument = rViewShellBase.GetDocument(); + if (pDocument == nullptr) + return nullptr; + + auto pContainer = std::make_shared<MasterPageContainer>(); + + auto xSelector(std::make_unique<RecentMasterPagesSelector>( + pParent, + *pDocument, + rViewShellBase, + pContainer, + rxSidebar)); + xSelector->LateInit(); + xSelector->SetHelpId(HID_SD_TASK_PANE_PREVIEW_RECENT); + + return xSelector; +} + +RecentMasterPagesSelector::RecentMasterPagesSelector ( + weld::Widget* pParent, + SdDrawDocument& rDocument, + ViewShellBase& rBase, + const std::shared_ptr<MasterPageContainer>& rpContainer, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar) + : MasterPagesSelector (pParent, rDocument, rBase, rpContainer, rxSidebar, "modules/simpress/ui/masterpagepanelrecent.ui", "recentvalueset") +{ +} + +RecentMasterPagesSelector::~RecentMasterPagesSelector() +{ + RecentlyUsedMasterPages::Instance().RemoveEventListener ( + LINK(this,RecentMasterPagesSelector,MasterPageListListener)); +} + +void RecentMasterPagesSelector::LateInit() +{ + MasterPagesSelector::LateInit(); + + MasterPagesSelector::Fill(); + RecentlyUsedMasterPages::Instance().AddEventListener ( + LINK(this,RecentMasterPagesSelector,MasterPageListListener)); +} + +IMPL_LINK_NOARG(RecentMasterPagesSelector, MasterPageListListener, LinkParamNone*, void) +{ + MasterPagesSelector::Fill(); +} + +void RecentMasterPagesSelector::Fill (ItemList& rItemList) +{ + // Create a set of names of the master pages used by the document. + MasterPageObserver::MasterPageNameSet aCurrentNames; + sal_uInt16 nMasterPageCount = mrDocument.GetMasterSdPageCount(PageKind::Standard); + for (sal_uInt16 nIndex=0; nIndex<nMasterPageCount; nIndex++) + { + SdPage* pMasterPage = mrDocument.GetMasterSdPage (nIndex, PageKind::Standard); + if (pMasterPage != nullptr) + aCurrentNames.insert (pMasterPage->GetName()); + } + + // Insert the recently used master pages that are currently not used. + RecentlyUsedMasterPages& rInstance (RecentlyUsedMasterPages::Instance()); + int nPageCount = rInstance.GetMasterPageCount(); + for (int nIndex=0; nIndex<nPageCount; nIndex++) + { + // Add an entry when a) the page is already known to the + // MasterPageContainer, b) the style name is empty, i.e. it has not yet + // been loaded (and thus can not be in use) or otherwise c) the + // style name is not currently in use. + MasterPageContainer::Token aToken (rInstance.GetTokenForIndex(nIndex)); + if (aToken != MasterPageContainer::NIL_TOKEN) + { + OUString sStyleName (mpContainer->GetStyleNameForToken(aToken)); + if (sStyleName.isEmpty() + || aCurrentNames.find(sStyleName) == aCurrentNames.end()) + { + rItemList.push_back(aToken); + } + } + } +} + +void RecentMasterPagesSelector::AssignMasterPageToPageList ( + SdPage* pMasterPage, + const std::shared_ptr<std::vector<SdPage*> >& rpPageList) +{ + sal_uInt16 nSelectedItemId = mxPreviewValueSet->GetSelectedItemId(); + + MasterPagesSelector::AssignMasterPageToPageList(pMasterPage, rpPageList); + + // Restore the selection. + if (mxPreviewValueSet->GetItemCount() > 0) + { + if (mxPreviewValueSet->GetItemCount() >= nSelectedItemId) + mxPreviewValueSet->SelectItem(nSelectedItemId); + else + mxPreviewValueSet->SelectItem(mxPreviewValueSet->GetItemCount()); + } +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/RecentMasterPagesSelector.hxx b/sd/source/ui/sidebar/RecentMasterPagesSelector.hxx new file mode 100644 index 000000000..6dbc3a2aa --- /dev/null +++ b/sd/source/ui/sidebar/RecentMasterPagesSelector.hxx @@ -0,0 +1,71 @@ +/* -*- 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 . + */ + +#pragma once + +#include "MasterPagesSelector.hxx" + +namespace sd::sidebar { + +/** Show the recently used master pages (that are not currently used). +*/ +class RecentMasterPagesSelector final + : public MasterPagesSelector +{ + friend class VclPtrInstance<RecentMasterPagesSelector>; +public: + static std::unique_ptr<PanelLayout> Create ( + weld::Widget* pParent, + ViewShellBase& rViewShellBase, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar); + + RecentMasterPagesSelector ( + weld::Widget* pParent, + SdDrawDocument& rDocument, + ViewShellBase& rBase, + const std::shared_ptr<MasterPageContainer>& rpContainer, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar); + virtual ~RecentMasterPagesSelector() override; + +private: + DECL_LINK(MasterPageListListener, LinkParamNone*, void); + virtual void Fill (ItemList& rItemList) override; + + using sd::sidebar::MasterPagesSelector::Fill; + + /** Forward this call to the base class but save and restore the + currently selected item. + Assign the given master page to the list of pages. + @param pMasterPage + This master page will usually be a member of the list of all + available master pages as provided by the MasterPageContainer. + @param rPageList + The pages to which to assign the master page. These pages may + be slides or master pages themselves. + */ + virtual void AssignMasterPageToPageList ( + SdPage* pMasterPage, + const std::shared_ptr<std::vector<SdPage*> >& rpPageList) override; + + virtual void LateInit() override; +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/RecentlyUsedMasterPages.cxx b/sd/source/ui/sidebar/RecentlyUsedMasterPages.cxx new file mode 100644 index 000000000..268658823 --- /dev/null +++ b/sd/source/ui/sidebar/RecentlyUsedMasterPages.cxx @@ -0,0 +1,366 @@ +/* -*- 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 "RecentlyUsedMasterPages.hxx" +#include "MasterPageContainerProviders.hxx" +#include <MasterPageObserver.hxx> +#include "MasterPageDescriptor.hxx" +#include <tools/ConfigurationAccess.hxx> + +#include <algorithm> +#include <memory> +#include <vector> + +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <osl/doublecheckedlocking.h> +#include <osl/getglobalmutex.hxx> + +using namespace ::std; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace { + +OUString GetPathToImpressConfigurationRoot() +{ + return "/org.openoffice.Office.Impress/"; +} +OUString GetPathToSetNode() +{ + return "MultiPaneGUI/ToolPanel/RecentlyUsedMasterPages"; +} + +} // end of anonymous namespace + +namespace sd::sidebar { + +RecentlyUsedMasterPages* RecentlyUsedMasterPages::mpInstance = nullptr; + +RecentlyUsedMasterPages& RecentlyUsedMasterPages::Instance() +{ + if (mpInstance == nullptr) + { + ::osl::GetGlobalMutex aMutexFunctor; + ::osl::MutexGuard aGuard (aMutexFunctor()); + if (mpInstance == nullptr) + { + RecentlyUsedMasterPages* pInstance = new RecentlyUsedMasterPages(); + pInstance->LateInit(); + SdGlobalResourceContainer::Instance().AddResource ( + ::std::unique_ptr<SdGlobalResource>(pInstance)); + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + mpInstance = pInstance; + } + } + else { + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + } + + return *mpInstance; +} + +constexpr size_t gnMaxListSize(8); + +RecentlyUsedMasterPages::RecentlyUsedMasterPages() + : mpContainer(std::make_shared<MasterPageContainer>()) +{ +} + +RecentlyUsedMasterPages::~RecentlyUsedMasterPages() +{ + Link<MasterPageContainerChangeEvent&,void> aLink (LINK(this,RecentlyUsedMasterPages,MasterPageContainerChangeListener)); + mpContainer->RemoveChangeListener(aLink); + + MasterPageObserver::Instance().RemoveEventListener( + LINK(this,RecentlyUsedMasterPages,MasterPageChangeListener)); +} + +void RecentlyUsedMasterPages::LateInit() +{ + Link<MasterPageContainerChangeEvent&,void> aLink (LINK(this,RecentlyUsedMasterPages,MasterPageContainerChangeListener)); + mpContainer->AddChangeListener(aLink); + + LoadPersistentValues (); + MasterPageObserver::Instance().AddEventListener( + LINK(this,RecentlyUsedMasterPages,MasterPageChangeListener)); +} + +void RecentlyUsedMasterPages::LoadPersistentValues() +{ + try + { + tools::ConfigurationAccess aConfiguration ( + GetPathToImpressConfigurationRoot(), + tools::ConfigurationAccess::READ_ONLY); + Reference<container::XNameAccess> xSet ( + aConfiguration.GetConfigurationNode(GetPathToSetNode()), + UNO_QUERY); + if ( ! xSet.is()) + return; + + static const OUStringLiteral sURLMemberName(u"URL"); + static const OUStringLiteral sNameMemberName(u"Name"); + OUString sURL; + OUString sName; + + // Read the names and URLs of the master pages. + const Sequence<OUString> aKeys (xSet->getElementNames()); + mvMasterPages.clear(); + mvMasterPages.reserve(aKeys.getLength()); + for (const auto& rKey : aKeys) + { + Reference<container::XNameAccess> xSetItem ( + xSet->getByName(rKey), UNO_QUERY); + if (xSetItem.is()) + { + Any aURL (xSetItem->getByName(sURLMemberName)); + Any aName (xSetItem->getByName(sNameMemberName)); + aURL >>= sURL; + aName >>= sName; + SharedMasterPageDescriptor pDescriptor = std::make_shared<MasterPageDescriptor>( + MasterPageContainer::TEMPLATE, + -1, + sURL, + OUString(), + sName, + false, + std::make_shared<TemplatePageObjectProvider>(sURL), + std::make_shared<TemplatePreviewProvider>(sURL)); + // For user supplied templates we use a different + // preview provider: The preview in the document shows + // not only shapes on the master page but also shapes on + // the foreground. This is misleading and therefore + // these previews are discarded and created directly + // from the page objects. + if (pDescriptor->GetURLClassification() == MasterPageDescriptor::URLCLASS_USER) + pDescriptor->mpPreviewProvider = std::make_shared<PagePreviewProvider>(); + MasterPageContainer::Token aToken (mpContainer->PutMasterPage(pDescriptor)); + mvMasterPages.emplace_back(aToken,sURL,sName); + } + } + + ResolveList(); + } + catch (Exception&) + { + // Ignore exception. + } +} + +void RecentlyUsedMasterPages::SavePersistentValues() +{ + try + { + tools::ConfigurationAccess aConfiguration ( + GetPathToImpressConfigurationRoot(), + tools::ConfigurationAccess::READ_WRITE); + Reference<container::XNameContainer> xSet ( + aConfiguration.GetConfigurationNode(GetPathToSetNode()), + UNO_QUERY); + if ( ! xSet.is()) + return; + + // Clear the set. + const Sequence<OUString> aKeys (xSet->getElementNames()); + for (const auto& rKey : aKeys) + xSet->removeByName (rKey); + + // Fill it with the URLs of this object. + static const OUStringLiteral sURLMemberName(u"URL"); + static const OUStringLiteral sNameMemberName(u"Name"); + Any aValue; + Reference<lang::XSingleServiceFactory> xChildFactory ( + xSet, UNO_QUERY); + if ( ! xChildFactory.is()) + return; + sal_Int32 nIndex(0); + for (const auto& rDescriptor : mvMasterPages) + { + // Create new child. + OUString sKey = "index_" + OUString::number(nIndex); + Reference<container::XNameReplace> xChild( + xChildFactory->createInstance(), UNO_QUERY); + if (xChild.is()) + { + xSet->insertByName (sKey, Any(xChild)); + + aValue <<= rDescriptor.msURL; + xChild->replaceByName (sURLMemberName, aValue); + + aValue <<= rDescriptor.msName; + xChild->replaceByName (sNameMemberName, aValue); + } + ++nIndex; + } + + // Write the data back to disk. + aConfiguration.CommitChanges(); + } + catch (Exception&) + { + // Ignore exception. + } +} + +void RecentlyUsedMasterPages::AddEventListener (const Link<LinkParamNone*,void>& rEventListener) +{ + if (::std::find ( + maListeners.begin(), + maListeners.end(), + rEventListener) == maListeners.end()) + { + maListeners.push_back (rEventListener); + } +} + +void RecentlyUsedMasterPages::RemoveEventListener (const Link<LinkParamNone*,void>& rEventListener) +{ + maListeners.erase ( + ::std::find ( + maListeners.begin(), + maListeners.end(), + rEventListener)); +} + +int RecentlyUsedMasterPages::GetMasterPageCount() const +{ + return mvMasterPages.size(); +} + +MasterPageContainer::Token RecentlyUsedMasterPages::GetTokenForIndex (sal_uInt32 nIndex) const +{ + if(nIndex<mvMasterPages.size()) + return mvMasterPages[nIndex].maToken; + else + return MasterPageContainer::NIL_TOKEN; +} + +void RecentlyUsedMasterPages::SendEvent() +{ + for (const auto& aLink : maListeners) + { + aLink.Call(nullptr); + } +} + +IMPL_LINK(RecentlyUsedMasterPages, MasterPageChangeListener, + MasterPageObserverEvent&, rEvent, void) +{ + switch (rEvent.meType) + { + case MasterPageObserverEvent::ET_MASTER_PAGE_ADDED: + case MasterPageObserverEvent::ET_MASTER_PAGE_EXISTS: + AddMasterPage( + mpContainer->GetTokenForStyleName(rEvent.mrMasterPageName)); + break; + + case MasterPageObserverEvent::ET_MASTER_PAGE_REMOVED: + // Do not change the list of recently master pages (the deleted + // page was recently used) but tell the listeners. They may want + // to update their lists. + SendEvent(); + break; + } +} + +IMPL_LINK(RecentlyUsedMasterPages, MasterPageContainerChangeListener, + MasterPageContainerChangeEvent&, rEvent, void) +{ + switch (rEvent.meEventType) + { + case MasterPageContainerChangeEvent::EventType::CHILD_ADDED: + case MasterPageContainerChangeEvent::EventType::CHILD_REMOVED: + case MasterPageContainerChangeEvent::EventType::INDEX_CHANGED: + ResolveList(); + break; + + default: + // Ignored. + break; + } +} + +void RecentlyUsedMasterPages::AddMasterPage ( + MasterPageContainer::Token aToken) +{ + // For the page to be inserted the token has to be valid and the page + // has to have a valid URL. This excludes master pages that do not come + // from template files. + if (aToken == MasterPageContainer::NIL_TOKEN + || mpContainer->GetURLForToken(aToken).isEmpty()) + return; + + MasterPageList::iterator aIterator ( + ::std::find_if(mvMasterPages.begin(),mvMasterPages.end(), + Descriptor::TokenComparator(aToken))); + if (aIterator != mvMasterPages.end()) + { + // When an entry for the given token already exists then remove + // it now and insert it later at the head of the list. + mvMasterPages.erase (aIterator); + } + + mvMasterPages.insert(mvMasterPages.begin(), + Descriptor( + aToken, + mpContainer->GetURLForToken(aToken), + mpContainer->GetStyleNameForToken(aToken))); + + // Shorten list to maximal size. + while (mvMasterPages.size() > gnMaxListSize) + { + mvMasterPages.pop_back (); + } + + SavePersistentValues (); + SendEvent(); +} + +void RecentlyUsedMasterPages::ResolveList() +{ + bool bNotify (false); + + for (auto& rDescriptor : mvMasterPages) + { + if (rDescriptor.maToken == MasterPageContainer::NIL_TOKEN) + { + MasterPageContainer::Token aToken (mpContainer->GetTokenForURL(rDescriptor.msURL)); + rDescriptor.maToken = aToken; + if (aToken != MasterPageContainer::NIL_TOKEN) + bNotify = true; + } + else + { + if ( ! mpContainer->HasToken(rDescriptor.maToken)) + { + rDescriptor.maToken = MasterPageContainer::NIL_TOKEN; + bNotify = true; + } + } + } + + if (bNotify) + SendEvent(); +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/RecentlyUsedMasterPages.hxx b/sd/source/ui/sidebar/RecentlyUsedMasterPages.hxx new file mode 100644 index 000000000..e95e66ccb --- /dev/null +++ b/sd/source/ui/sidebar/RecentlyUsedMasterPages.hxx @@ -0,0 +1,125 @@ +/* -*- 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 . + */ + +#pragma once + +#include <tools/SdGlobalResourceContainer.hxx> +#include <tools/link.hxx> +#include <vector> + +#include "MasterPageContainer.hxx" + +namespace sd { +class MasterPageObserverEvent; +} + +namespace sd::sidebar { + +/** This singleton holds a list of the most recently used master pages. +*/ +class RecentlyUsedMasterPages + : public SdGlobalResource +{ +public: + /** Return the single instance of this class. + */ + static RecentlyUsedMasterPages& Instance(); + + void AddEventListener (const Link<LinkParamNone*,void>& rEventListener); + void RemoveEventListener (const Link<LinkParamNone*,void>& rEventListener); + + int GetMasterPageCount() const; + MasterPageContainer::Token GetTokenForIndex (sal_uInt32 nIndex) const; + +private: + class Descriptor + { + public: + OUString msURL; + OUString msName; + ::sd::sidebar::MasterPageContainer::Token maToken; + Descriptor (::sd::sidebar::MasterPageContainer::Token aToken, + const OUString& rsURL, const OUString& rsName) + : msURL(rsURL), + msName(rsName), + maToken(aToken) + {} + + class TokenComparator + { + public: + explicit TokenComparator(::sd::sidebar::MasterPageContainer::Token aToken) + : maToken(aToken) {} + bool operator () (const Descriptor& rDescriptor) + { return maToken==rDescriptor.maToken; } + + private: + ::sd::sidebar::MasterPageContainer::Token maToken; + }; + }; + + /** The single instance of this class. It is created on demand when + Instance() is called for the first time. + */ + static RecentlyUsedMasterPages* mpInstance; + + ::std::vector<Link<LinkParamNone*,void>> maListeners; + + typedef ::std::vector<Descriptor> MasterPageList; + MasterPageList mvMasterPages; + std::shared_ptr<MasterPageContainer> mpContainer; + + RecentlyUsedMasterPages(); + virtual ~RecentlyUsedMasterPages() override; + + /** Call this method after a new object has been created. + */ + void LateInit(); + + RecentlyUsedMasterPages (const RecentlyUsedMasterPages&) = delete; + + RecentlyUsedMasterPages& operator= (const RecentlyUsedMasterPages&) = delete; + + void SendEvent(); + DECL_LINK(MasterPageChangeListener, MasterPageObserverEvent&, void); + DECL_LINK(MasterPageContainerChangeListener, MasterPageContainerChangeEvent&, void); + + /** Add a descriptor for the specified master page to the end of the + list of most recently used master pages. When the page is already a + member of that list the associated descriptor is moved to the end of + the list to make it the most recently used entry. + */ + void AddMasterPage(MasterPageContainer::Token aToken); + + /** Load the list of recently used master pages from the registry where + it was saved to make it persistent. + */ + void LoadPersistentValues(); + + /** Save the list of recently used master pages to the registry to make + it persistent. + */ + void SavePersistentValues(); + + void ResolveList(); +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/SlideBackground.cxx b/sd/source/ui/sidebar/SlideBackground.cxx new file mode 100644 index 000000000..742275bab --- /dev/null +++ b/sd/source/ui/sidebar/SlideBackground.cxx @@ -0,0 +1,1286 @@ +/* -*- 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 <com/sun/star/ui/XDeck.hpp> +#include <com/sun/star/ui/XPanel.hpp> +#include <com/sun/star/frame/XController2.hpp> +#include <SlideSorter.hxx> +#include <SlideSorterViewShell.hxx> +#include <controller/SlideSorterController.hxx> +#include <controller/SlsPageSelector.hxx> +#include "SlideBackground.hxx" +#include <sdresid.hxx> +#include <ViewShellBase.hxx> +#include <FrameView.hxx> +#include <DrawDocShell.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include "PageMarginUtils.hxx" +#include <strings.hrc> +#include <pageformatpanel.hrc> +#include <DrawViewShell.hxx> +#include <svl/intitem.hxx> +#include <svx/colorbox.hxx> +#include <svx/dlgutil.hxx> +#include <svx/drawitem.hxx> +#include <svx/itemwin.hxx> +#include <svx/pageitem.hxx> +#include <app.hrc> +#include <editeng/paperinf.hxx> +#include <svx/xflgrit.hxx> +#include <svx/rulritem.hxx> +#include <svx/svxids.hrc> +#include <svx/xfillit0.hxx> +#include <svx/xflclit.hxx> +#include <svx/xgrad.hxx> +#include <svx/xbtmpit.hxx> +#include <svx/xflhtit.hxx> +#include <svx/svdpage.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/sidebar/Panel.hxx> +#include <EventMultiplexer.hxx> +#include <unotools/localedatawrapper.hxx> +#include <vcl/EnumContext.hxx> +#include <vcl/svapp.hxx> + +#include <editeng/sizeitem.hxx> +#include <comphelper/lok.hxx> +#include <unomodel.hxx> +#include <sfx2/lokhelper.hxx> + +using namespace ::com::sun::star; + +using ::com::sun::star::uno::Reference; + +namespace sd::sidebar { + +namespace { + +enum eFillStyle +{ + NONE, + SOLID, + GRADIENT, + HATCH, + BITMAP, + PATTERN +}; + +} + +SlideBackground::SlideBackground( + weld::Widget* pParent, + ViewShellBase& rBase, + const css::uno::Reference<css::frame::XFrame>& rxFrame, + SfxBindings* pBindings) : + PanelLayout( pParent, "SlideBackgroundPanel", "modules/simpress/ui/sidebarslidebackground.ui" ), + mrBase( rBase ), + mxPaperSizeBox(new SvxPaperSizeListBox(m_xBuilder->weld_combo_box("paperformat"))), + mxPaperOrientation(m_xBuilder->weld_combo_box("orientation")), + mxMasterSlide(m_xBuilder->weld_combo_box("masterslide")), + mxBackgroundLabel(m_xBuilder->weld_label("label3")), + mxFillStyle(m_xBuilder->weld_combo_box("fillstyle")), + mxFillLB(new ColorListBox(m_xBuilder->weld_menu_button("fillattr"), [this]{ return GetFrameWeld(); })), + mxFillAttr(m_xBuilder->weld_combo_box("fillattr1")), + mxFillGrad1(new ColorListBox(m_xBuilder->weld_menu_button("fillattr2"), [this]{ return GetFrameWeld(); })), + mxFillGrad2(new ColorListBox(m_xBuilder->weld_menu_button("fillattr3"), [this]{ return GetFrameWeld(); })), + mxInsertImage(m_xBuilder->weld_button("button2")), + mxDspMasterBackground(m_xBuilder->weld_check_button("displaymasterbackground")), + mxDspMasterObjects(m_xBuilder->weld_check_button("displaymasterobjects")), + mxCloseMaster(m_xBuilder->weld_button("closemasterslide")), + mxEditMaster(m_xBuilder->weld_button("masterslidebutton")), + mxMasterLabel(m_xBuilder->weld_label("masterlabel")), + mxMarginSelectBox(m_xBuilder->weld_combo_box("marginLB")), + mxCustomEntry(m_xBuilder->weld_label("customlabel")), + mxMarginLabel(m_xBuilder->weld_label("labelmargin")), + maPaperSizeController(SID_ATTR_PAGE_SIZE, *pBindings, *this), + maPaperOrientationController(SID_ATTR_PAGE, *pBindings, *this), + maPaperMarginLRController(SID_ATTR_PAGE_LRSPACE, *pBindings, *this), + maPaperMarginULController(SID_ATTR_PAGE_ULSPACE, *pBindings, *this), + maBckColorController(SID_ATTR_PAGE_COLOR, *pBindings, *this), + maBckGradientController(SID_ATTR_PAGE_GRADIENT, *pBindings, *this), + maBckHatchController(SID_ATTR_PAGE_HATCH, *pBindings, *this), + maBckBitmapController(SID_ATTR_PAGE_BITMAP, *pBindings, *this), + maBckFillStyleController(SID_ATTR_PAGE_FILLSTYLE, *pBindings, *this), + maBckImageController(SID_SELECT_BACKGROUND, *pBindings, *this), + maDspBckController(SID_DISPLAY_MASTER_BACKGROUND, *pBindings, *this), + maDspObjController(SID_DISPLAY_MASTER_OBJECTS, *pBindings, *this), + maMetricController(SID_ATTR_METRIC, *pBindings, *this), + maCloseMasterController(SID_CLOSE_MASTER_VIEW, *pBindings, *this), + mpPageItem( new SvxPageItem(SID_ATTR_PAGE) ), + mbSwitchModeToNormal(false), + mbSwitchModeToMaster(false), + mxFrame(rxFrame), + maDrawOtherContext(vcl::EnumContext::Application::Draw, vcl::EnumContext::Context::DrawPage), + maDrawMasterContext(vcl::EnumContext::Application::Draw, vcl::EnumContext::Context::MasterPage), + maImpressOtherContext(vcl::EnumContext::Application::Impress, vcl::EnumContext::Context::DrawPage), + maImpressMasterContext(vcl::EnumContext::Application::Impress, vcl::EnumContext::Context::MasterPage), + maImpressHandoutContext(vcl::EnumContext::Application::Impress, vcl::EnumContext::Context::HandoutPage), + maImpressNotesContext(vcl::EnumContext::Application::Impress, vcl::EnumContext::Context::NotesPage), + mbTitle(false), + mpPageLRMarginItem( new SvxLongLRSpaceItem( 0, 0, SID_ATTR_PAGE_LRSPACE ) ), + mpPageULMarginItem( new SvxLongULSpaceItem( 0, 0, SID_ATTR_PAGE_ULSPACE ) ), + m_nPageLeftMargin(0), + m_nPageRightMargin(0), + m_nPageTopMargin(0), + m_nPageBottomMargin(0), + meFUnit(GetModuleFieldUnit()), + mpBindings(pBindings) +{ + //let the listbox shrink to any size so the sidebar isn't forced to grow to + //the size of the longest master slide name in the document + mxMasterSlide->set_size_request(42, -1); + + maCustomEntry = mxCustomEntry->get_label(); + + addListener(); + Initialize(); +} + +bool SlideBackground::IsDraw() +{ + return ( maContext == maDrawMasterContext || + maContext == maDrawOtherContext ); +} + +bool SlideBackground::IsImpress() +{ + return ( maContext == maImpressMasterContext || + maContext == maImpressOtherContext || + maContext == maImpressHandoutContext || + maContext == maImpressNotesContext ); +} + +FieldUnit SlideBackground::GetCurrentUnit(SfxItemState eState, const SfxPoolItem* pState) +{ + FieldUnit eUnit; + + if (pState && eState >= SfxItemState::DEFAULT) + eUnit = static_cast<FieldUnit>(static_cast<const SfxUInt16Item*>(pState)->GetValue()); + else + eUnit = GetModuleFieldUnit(); + + return eUnit; +} + +void SlideBackground::SetMarginsFieldUnit() +{ + auto nSelected = mxMarginSelectBox->get_active(); + mxMarginSelectBox->clear(); + + const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper(); + + if (IsInch(meFUnit)) + { + OUString sSuffix = weld::MetricSpinButton::MetricToString(FieldUnit::INCH); + for (size_t i = 0; i < SAL_N_ELEMENTS(RID_PAGEFORMATPANEL_MARGINS_INCH); ++i) + { + OUString sMeasurement = rLocaleData.getNum(RID_PAGEFORMATPANEL_MARGINS_INCH[i].second, 2, true, false) + sSuffix; + mxMarginSelectBox->append_text(SdResId(RID_PAGEFORMATPANEL_MARGINS_INCH[i].first).replaceFirst("%1", sMeasurement)); + } + } + else + { + OUString sSuffix = " " + weld::MetricSpinButton::MetricToString(FieldUnit::CM); + for (size_t i = 0; i < SAL_N_ELEMENTS(RID_PAGEFORMATPANEL_MARGINS_CM); ++i) + { + OUString sMeasurement = rLocaleData.getNum(RID_PAGEFORMATPANEL_MARGINS_CM[i].second, 2, true, false) + sSuffix; + mxMarginSelectBox->append_text(SdResId(RID_PAGEFORMATPANEL_MARGINS_CM[i].first).replaceFirst("%1", sMeasurement)); + } + } + + mxMarginSelectBox->set_active(nSelected); +} + +void SlideBackground::Initialize() +{ + SvxFillTypeBox::Fill(*mxFillStyle); + + SetMarginsFieldUnit(); + + mxPaperSizeBox->FillPaperSizeEntries( PaperSizeApp::Draw ); + mxPaperSizeBox->connect_changed(LINK(this,SlideBackground,PaperSizeModifyHdl)); + mxPaperOrientation->connect_changed(LINK(this,SlideBackground,PaperSizeModifyHdl)); + mxEditMaster->connect_clicked(LINK(this, SlideBackground, EditMasterHdl)); + mxCloseMaster->connect_clicked(LINK(this, SlideBackground, CloseMasterHdl)); + mxInsertImage->connect_clicked(LINK(this, SlideBackground, SelectBgHdl)); + meUnit = maPaperSizeController.GetCoreMetric(); + + mxMasterSlide->connect_changed(LINK(this, SlideBackground, AssignMasterPage)); + + mxFillStyle->connect_changed(LINK(this, SlideBackground, FillStyleModifyHdl)); + mxFillLB->SetSelectHdl(LINK(this, SlideBackground, FillColorHdl)); + mxFillGrad1->SetSelectHdl(LINK(this, SlideBackground, FillColorHdl)); + mxFillGrad2->SetSelectHdl(LINK(this, SlideBackground, FillColorHdl)); + mxFillAttr->connect_changed(LINK(this, SlideBackground, FillBackgroundHdl)); + + ViewShell* pMainViewShell = mrBase.GetMainViewShell().get(); + if (pMainViewShell) + { + FrameView *pFrameView = pMainViewShell->GetFrameView(); + + if ( pFrameView->GetViewShEditMode() == EditMode::Page ) + { + SdPage* mpPage = pMainViewShell->getCurrentPage(); + populateMasterSlideDropdown(); + + OUString aLayoutName( mpPage->GetLayoutName() ); + aLayoutName = aLayoutName.copy(0,aLayoutName.indexOf(SD_LT_SEPARATOR)); + mxMasterSlide->set_active_text(aLayoutName); + } + } + + mxFillStyle->set_active(static_cast< sal_Int32 >(NONE)); + + mxDspMasterBackground->connect_toggled(LINK(this, SlideBackground, DspBackground)); + mxDspMasterObjects->connect_toggled(LINK(this, SlideBackground, DspObjects)); + + //margins + mxMarginSelectBox->connect_changed(LINK(this, SlideBackground, ModifyMarginHdl)); + + Update(); + UpdateMarginBox(); +} + +void SlideBackground::DumpAsPropertyTree(::tools::JsonWriter& rJsonWriter) +{ + if (mxPaperSizeBox->get_active() == -1) + { + mpBindings->Update(SID_ATTR_PAGE_SIZE); + } + + PanelLayout::DumpAsPropertyTree(rJsonWriter); +} + +void SlideBackground::HandleContextChange( + const vcl::EnumContext& rContext) +{ + if (maContext == rContext) + return; + maContext = rContext; + + if ( IsImpress() ) + { + mxMasterLabel->set_label(SdResId(STR_MASTERSLIDE_LABEL)); + + // margin selector is only for Draw + mxMarginSelectBox->hide(); + mxMarginLabel->hide(); + + if ( maContext == maImpressMasterContext ) + { + mxCloseMaster->show(); + mxEditMaster->hide(); + mxMasterSlide->set_sensitive(false); + mxMasterSlide->clear(); + mxDspMasterBackground->set_sensitive(false); + mxDspMasterObjects->set_sensitive(false); + mxFillStyle->hide(); + mxBackgroundLabel->hide(); + mxInsertImage->show(); + + mxFillLB->hide(); + mxFillAttr->hide(); + mxFillGrad1->hide(); + mxFillGrad2->hide(); + } + else if ( maContext == maImpressHandoutContext || maContext == maImpressNotesContext ) + { + mxCloseMaster->hide(); + mxEditMaster->hide(); + mxMasterSlide->set_sensitive(false); + mxMasterSlide->clear(); + mxDspMasterBackground->set_sensitive(false); + mxDspMasterObjects->set_sensitive(false); + mxFillStyle->hide(); + mxBackgroundLabel->hide(); + mxInsertImage->hide(); + } + else if (maContext == maImpressOtherContext) + { + mxCloseMaster->hide(); + mxEditMaster->show(); + mxMasterSlide->set_sensitive(true); + populateMasterSlideDropdown(); + mxDspMasterBackground->set_sensitive(true); + mxDspMasterObjects->set_sensitive(true); + mxFillStyle->show(); + mxBackgroundLabel->show(); + mxInsertImage->show(); + } + + // Need to do a relayouting, otherwise the panel size is not updated after show / hide controls + if (m_pPanel) + m_pPanel->TriggerDeckLayouting(); + + } + else if ( IsDraw() ) + { + mxMasterLabel->set_label(SdResId(STR_MASTERPAGE_LABEL)); + mxDspMasterBackground->hide(); + mxDspMasterObjects->hide(); + + if (maContext == maDrawOtherContext) + { + mxEditMaster->hide(); + mxFillStyle->show(); + mxBackgroundLabel->show(); + } + else if (maContext == maDrawMasterContext) + { + mxFillStyle->hide(); + mxBackgroundLabel->hide(); + } + } + + // The Insert Image button in the sidebar issues .uno:SelectBackground, + // which when invoked without arguments will open the file-open-dialog + // to prompt the user to select a file. This is useless in LOOL. + // Hide for now so the user will only be able to use the menu to insert + // background image, which prompts the user for file selection in the browser. + if (comphelper::LibreOfficeKit::isActive()) + mxInsertImage->hide(); +} + +void SlideBackground::Update() +{ + eFillStyle nPos = static_cast<eFillStyle>(mxFillStyle->get_active()); + + if(maContext != maImpressOtherContext && maContext != maDrawOtherContext) + nPos = NONE; + + SfxObjectShell* pSh = SfxObjectShell::Current(); + if (!pSh) + return; + + switch(nPos) + { + case NONE: + { + mxFillLB->hide(); + mxFillAttr->hide(); + mxFillGrad1->hide(); + mxFillGrad2->hide(); + } + break; + case SOLID: + { + mxFillAttr->hide(); + mxFillGrad1->hide(); + mxFillGrad2->hide(); + mxFillLB->show(); + const Color aColor = GetColorSetOrDefault(); + mxFillLB->SelectEntry(aColor); + } + break; + case GRADIENT: + { + mxFillLB->hide(); + mxFillAttr->hide(); + mxFillGrad1->show(); + mxFillGrad2->show(); + + const XGradient xGradient = GetGradientSetOrDefault(); + const Color aStartColor = xGradient.GetStartColor(); + mxFillGrad1->SelectEntry(aStartColor); + const Color aEndColor = xGradient.GetEndColor(); + mxFillGrad2->SelectEntry(aEndColor); + } + break; + + case HATCH: + { + mxFillLB->hide(); + mxFillAttr->show(); + mxFillAttr->clear(); + SvxFillAttrBox::Fill(*mxFillAttr, pSh->GetItem(SID_HATCH_LIST)->GetHatchList()); + mxFillGrad1->hide(); + mxFillGrad2->hide(); + + const OUString aHatchName = GetHatchingSetOrDefault(); + mxFillAttr->set_active_text( aHatchName ); + } + break; + + case BITMAP: + case PATTERN: + { + mxFillLB->hide(); + mxFillAttr->show(); + mxFillAttr->clear(); + mxFillGrad1->hide(); + mxFillGrad2->hide(); + OUString aName; + if(nPos == BITMAP) + { + SvxFillAttrBox::Fill(*mxFillAttr, pSh->GetItem(SID_BITMAP_LIST)->GetBitmapList()); + aName = GetBitmapSetOrDefault(); + } + else if(nPos == PATTERN) + { + SvxFillAttrBox::Fill(*mxFillAttr, pSh->GetItem(SID_PATTERN_LIST)->GetPatternList()); + aName = GetPatternSetOrDefault(); + } + mxFillAttr->set_active_text( aName ); + } + break; + default: + break; + } + + // Need to do a relayouting, otherwise the panel size is not updated after show / hide controls + if (m_pPanel) + m_pPanel->TriggerDeckLayouting(); +} + +void SlideBackground::UpdateMarginBox() +{ + m_nPageLeftMargin = mpPageLRMarginItem->GetLeft(); + m_nPageRightMargin = mpPageLRMarginItem->GetRight(); + m_nPageTopMargin = mpPageULMarginItem->GetUpper(); + m_nPageBottomMargin = mpPageULMarginItem->GetLower(); + + int nCustomIndex = mxMarginSelectBox->find_text(maCustomEntry); + + if( IsNone(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin) ) + { + mxMarginSelectBox->set_active(0); + if (nCustomIndex != -1) + mxMarginSelectBox->remove(nCustomIndex); + } + else if( IsNarrow(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin) ) + { + mxMarginSelectBox->set_active(1); + if (nCustomIndex != -1) + mxMarginSelectBox->remove(nCustomIndex); + } + else if( IsModerate(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin) ) + { + mxMarginSelectBox->set_active(2); + if (nCustomIndex != -1) + mxMarginSelectBox->remove(nCustomIndex); + } + else if( IsNormal075(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin) ) + { + mxMarginSelectBox->set_active(3); + if (nCustomIndex != -1) + mxMarginSelectBox->remove(nCustomIndex); + } + else if( IsNormal100(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin) ) + { + mxMarginSelectBox->set_active(4); + if (nCustomIndex != -1) + mxMarginSelectBox->remove(nCustomIndex); + } + else if( IsNormal125(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin) ) + { + mxMarginSelectBox->set_active(5); + if (nCustomIndex != -1) + mxMarginSelectBox->remove(nCustomIndex); + } + else if( IsWide(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin) ) + { + mxMarginSelectBox->set_active(6); + if (nCustomIndex != -1) + mxMarginSelectBox->remove(nCustomIndex); + } + else + { + if (nCustomIndex == -1) + mxMarginSelectBox->append_text(maCustomEntry); + mxMarginSelectBox->set_active_text(maCustomEntry); + } +} + +void SlideBackground::SetPanelTitle( const OUString& rTitle ) +{ + Reference<frame::XController2> xController( mxFrame->getController(), uno::UNO_QUERY); + if ( !xController.is() ) + return; + + Reference<ui::XSidebarProvider> xSidebarProvider = xController->getSidebar(); + if ( !xSidebarProvider.is() ) + return; + + Reference<ui::XDecks> xDecks = xSidebarProvider->getDecks(); + if ( !xDecks.is() ) + return; + + Reference<ui::XDeck> xDeck ( xDecks->getByName("PropertyDeck"), uno::UNO_QUERY); + if ( !xDeck.is() ) + return; + + Reference<ui::XPanels> xPanels = xDeck->getPanels(); + if ( !xPanels.is() ) + return; + + if (xPanels->hasByName("SlideBackgroundPanel")) + { + Reference<ui::XPanel> xPanel ( xPanels->getByName("SlideBackgroundPanel"), uno::UNO_QUERY); + if ( !xPanel.is() ) + return; + + xPanel->setTitle( rTitle ); + } +} + +void SlideBackground::addListener() +{ + Link<tools::EventMultiplexerEvent&,void> aLink( LINK(this, SlideBackground, EventMultiplexerListener) ); + mrBase.GetEventMultiplexer()->AddEventListener( aLink ); +} + +void SlideBackground::removeListener() +{ + Link<tools::EventMultiplexerEvent&,void> aLink( LINK(this, SlideBackground, EventMultiplexerListener) ); + mrBase.GetEventMultiplexer()->RemoveEventListener( aLink ); +} + +IMPL_LINK(SlideBackground, EventMultiplexerListener, + tools::EventMultiplexerEvent&, rEvent, void) +{ + switch (rEvent.meEventId) + { + // add more events as per requirement + // Master Page change triggers a shape change event. Solves sync problem. + case EventMultiplexerEventId::ShapeChanged: + populateMasterSlideDropdown(); + break; + case EventMultiplexerEventId::EditModeNormal: + mbSwitchModeToNormal = true; + break; + case EventMultiplexerEventId::EditModeMaster: + mbSwitchModeToMaster = true; + break; + case EventMultiplexerEventId::EditViewSelection: + case EventMultiplexerEventId::EndTextEdit: + { + if ( mbSwitchModeToMaster ) + { + if( IsImpress() ) + SetPanelTitle(SdResId(STR_MASTERSLIDE_NAME)); + else + SetPanelTitle(SdResId(STR_MASTERPAGE_NAME)); + mbSwitchModeToMaster = false; + } + else if ( mbSwitchModeToNormal ) + { + if( IsImpress() ) + SetPanelTitle(SdResId(STR_SLIDE_NAME)); + else + SetPanelTitle(SdResId(STR_PAGE_NAME)); + mbSwitchModeToNormal = false; + } + + } + break; + case EventMultiplexerEventId::CurrentPageChanged: + { + static const sal_uInt16 SidArray[] = { + SID_ATTR_PAGE_COLOR, + SID_ATTR_PAGE_GRADIENT, + SID_ATTR_PAGE_HATCH, + SID_ATTR_PAGE_BITMAP, + SID_ATTR_PAGE_FILLSTYLE, + SID_DISPLAY_MASTER_BACKGROUND, + SID_DISPLAY_MASTER_OBJECTS, + 0 }; + updateMasterSlideSelection(); + GetBindings()->Invalidate( SidArray ); + } + break; + case EventMultiplexerEventId::ViewAdded: + { + if(!mbTitle) + { + if( IsDraw() ) + { + mxCloseMaster->hide(); + mxEditMaster->hide(); + if( maContext == maDrawMasterContext) + SetPanelTitle(SdResId(STR_MASTERPAGE_NAME)); + else + SetPanelTitle(SdResId(STR_PAGE_NAME)); + } + else if ( maContext == maImpressOtherContext || maContext == maImpressMasterContext ) + { + if( maContext == maImpressMasterContext ) + SetPanelTitle(SdResId(STR_MASTERSLIDE_NAME)); + else + SetPanelTitle(SdResId(STR_SLIDE_NAME)); + } + else if ( maContext == maImpressNotesContext ) + { + mxMasterLabel->set_label(SdResId(STR_MASTERSLIDE_LABEL)); + ViewShell* pMainViewShell = mrBase.GetMainViewShell().get(); + + if (pMainViewShell) + { + DrawViewShell* pDrawViewShell = static_cast<DrawViewShell*>(pMainViewShell); + if ( pDrawViewShell->GetEditMode() == EditMode::MasterPage) + SetPanelTitle(SdResId(STR_MASTERSLIDE_NAME)); + else // EditMode::Page + SetPanelTitle(SdResId(STR_SLIDE_NAME)); + } + } + mbTitle = true; + } + } + break; + default: + break; + } +} + +void SlideBackground::populateMasterSlideDropdown() +{ + mxMasterSlide->clear(); + ::sd::DrawDocShell* pDocSh = dynamic_cast<::sd::DrawDocShell*>( SfxObjectShell::Current() ); + SdDrawDocument* pDoc = pDocSh ? pDocSh->GetDoc() : nullptr; + sal_uInt16 nCount = pDoc ? pDoc->GetMasterPageCount() : 0; + for( sal_uInt16 nLayout = 0; nLayout < nCount; nLayout++ ) + { + SdPage* pMaster = static_cast<SdPage*>(pDoc->GetMasterPage(nLayout)); + if( pMaster->GetPageKind() == PageKind::Standard) + { + OUString aLayoutName(pMaster->GetLayoutName()); + aLayoutName = aLayoutName.copy(0,aLayoutName.indexOf(SD_LT_SEPARATOR)); + mxMasterSlide->append_text(aLayoutName); + } + } + updateMasterSlideSelection(); +} + +void SlideBackground::updateMasterSlideSelection() +{ + ViewShell* pMainViewShell = mrBase.GetMainViewShell().get(); + SdPage* pPage = pMainViewShell ? pMainViewShell->getCurrentPage() : nullptr; + if (pPage != nullptr && pPage->TRG_HasMasterPage()) + { + SdrPage& rMasterPage (pPage->TRG_GetMasterPage()); + SdPage* pMasterPage = static_cast<SdPage*>(&rMasterPage); + mxMasterSlide->set_active_text(pMasterPage->GetName()); + } +} + +SlideBackground::~SlideBackground() +{ + removeListener(); + + mxCustomEntry.reset(); + mxMarginLabel.reset(); + mxPaperSizeBox.reset(); + mxPaperOrientation.reset(); + mxMasterSlide.reset(); + mxBackgroundLabel.reset(); + mxFillAttr.reset(); + mxFillGrad1.reset(); + mxFillGrad2.reset(); + mxFillStyle.reset(); + mxFillLB.reset(); + mxInsertImage.reset(); + mxMarginSelectBox.reset(); + mxDspMasterBackground.reset(); + mxDspMasterObjects.reset(); + mxMasterLabel.reset(); + mxEditMaster.reset(); + mxCloseMaster.reset(); + + maPaperSizeController.dispose(); + maPaperOrientationController.dispose(); + maPaperMarginLRController.dispose(); + maPaperMarginULController.dispose(); + maBckColorController.dispose(); + maBckGradientController.dispose(); + maBckHatchController.dispose(); + maBckBitmapController.dispose(); + maBckFillStyleController.dispose(); + maBckImageController.dispose(); + maDspBckController.dispose(); + maDspObjController.dispose(); + maMetricController.dispose(); + maCloseMasterController.dispose(); + + mpPageItem.reset(); + mpColorItem.reset(); + mpHatchItem.reset(); + mpBitmapItem.reset(); + mpPageLRMarginItem.reset(); + mpPageULMarginItem.reset(); +} + +void SlideBackground::ExecuteMarginLRChange(const ::tools::Long mnPageLeftMargin, const ::tools::Long mnPageRightMargin) +{ + mpPageLRMarginItem->SetLeft(mnPageLeftMargin); + mpPageLRMarginItem->SetRight(mnPageRightMargin); + GetBindings()->GetDispatcher()->ExecuteList( SID_ATTR_PAGE_LRSPACE, SfxCallMode::RECORD, { mpPageLRMarginItem.get() } ); +} + +void SlideBackground::ExecuteMarginULChange(const ::tools::Long mnPageTopMargin, const ::tools::Long mnPageBottomMargin) +{ + mpPageULMarginItem->SetUpper(mnPageTopMargin); + mpPageULMarginItem->SetLower(mnPageBottomMargin); + GetBindings()->GetDispatcher()->ExecuteList( SID_ATTR_PAGE_ULSPACE, SfxCallMode::RECORD, { mpPageULMarginItem.get() } ); +} + +Color const & SlideBackground::GetColorSetOrDefault() +{ + // Tango Sky Blue 1, to be consistent w/ area fill panel (b/c COL_AUTO for slides is transparent) + if ( !mpColorItem ) + mpColorItem.reset( new XFillColorItem( OUString(), Color(0x72, 0x9f, 0xcf) ) ); + + return mpColorItem->GetColorValue(); +} + +XGradient const & SlideBackground::GetGradientSetOrDefault() +{ + if( !mpGradientItem ) + { + XGradient aGradient; + OUString aGradientName; + if (SfxObjectShell* pSh = SfxObjectShell::Current()) + { + const SvxGradientListItem * pGradListItem = pSh->GetItem(SID_GRADIENT_LIST); + aGradient = pGradListItem->GetGradientList()->GetGradient(0)->GetGradient(); + aGradientName = pGradListItem->GetGradientList()->GetGradient(0)->GetName(); + } + mpGradientItem.reset( new XFillGradientItem( aGradientName, aGradient ) ); + } + + return mpGradientItem->GetGradientValue(); +} + +OUString const & SlideBackground::GetHatchingSetOrDefault() +{ + if( !mpHatchItem ) + { + XHatch aHatch; + OUString aHatchName; + if (SfxObjectShell* pSh = SfxObjectShell::Current()) + { + const SvxHatchListItem * pHatchListItem = pSh->GetItem(SID_HATCH_LIST); + aHatch = pHatchListItem->GetHatchList()->GetHatch(0)->GetHatch(); + aHatchName = pHatchListItem->GetHatchList()->GetHatch(0)->GetName(); + } + mpHatchItem.reset( new XFillHatchItem( aHatchName, aHatch ) ); + } + + return mpHatchItem->GetName(); +} + +OUString const & SlideBackground::GetBitmapSetOrDefault() +{ + if( !mpBitmapItem || mpBitmapItem->isPattern()) + { + GraphicObject aGraphObj; + OUString aBmpName; + if (SfxObjectShell* pSh = SfxObjectShell::Current()) + { + const SvxBitmapListItem * pBmpListItem = pSh->GetItem(SID_BITMAP_LIST); + aGraphObj = pBmpListItem->GetBitmapList()->GetBitmap(0)->GetGraphicObject(); + aBmpName = pBmpListItem->GetBitmapList()->GetBitmap(0)->GetName(); + } + mpBitmapItem.reset( new XFillBitmapItem( aBmpName, aGraphObj ) ); + } + + return mpBitmapItem->GetName(); +} + +OUString const & SlideBackground::GetPatternSetOrDefault() +{ + if( !mpBitmapItem || !(mpBitmapItem->isPattern())) + { + GraphicObject aGraphObj; + OUString aPtrnName; + if (SfxObjectShell* pSh = SfxObjectShell::Current()) + { + const SvxPatternListItem * pPtrnListItem = pSh->GetItem(SID_PATTERN_LIST); + aGraphObj = pPtrnListItem->GetPatternList()->GetBitmap(0)->GetGraphicObject(); + aPtrnName = pPtrnListItem->GetPatternList()->GetBitmap(0)->GetName(); + } + mpBitmapItem.reset( new XFillBitmapItem( aPtrnName, aGraphObj ) ); + } + + return mpBitmapItem->GetName(); +} + +void SlideBackground::NotifyItemUpdate( + const sal_uInt16 nSID, + const SfxItemState eState, + const SfxPoolItem* pState) +{ + switch(nSID) + { + + case SID_ATTR_PAGE_COLOR: + { + if(eState >= SfxItemState::DEFAULT) + { + mxFillStyle->set_active(static_cast< sal_Int32 >(SOLID)); + mpColorItem.reset(pState ? static_cast< XFillColorItem* >(pState->Clone()) : nullptr); + Update(); + } + } + break; + + case SID_ATTR_PAGE_HATCH: + { + if(eState >= SfxItemState::DEFAULT) + { + mxFillStyle->set_active(static_cast< sal_Int32 >(HATCH)); + mpHatchItem.reset(pState ? static_cast < XFillHatchItem* >(pState->Clone()) : nullptr); + Update(); + } + } + break; + + case SID_ATTR_PAGE_GRADIENT: + { + if(eState >= SfxItemState::DEFAULT) + { + mxFillStyle->set_active(static_cast< sal_Int32>(GRADIENT)); + mpGradientItem.reset(pState ? static_cast< XFillGradientItem* >(pState->Clone()) : nullptr); + Update(); + } + } + break; + case SID_ATTR_PAGE_BITMAP: + { + if(eState >= SfxItemState::DEFAULT) + { + mpBitmapItem.reset(pState ? static_cast< XFillBitmapItem* >(pState->Clone()) : nullptr); + if(mpBitmapItem) + { + if(mpBitmapItem->isPattern()) + mxFillStyle->set_active(static_cast< sal_Int32 >(PATTERN)); + else + mxFillStyle->set_active(static_cast< sal_Int32 >(BITMAP)); + } + else + mxFillStyle->set_active(static_cast< sal_Int32 >(BITMAP)); + Update(); + } + } + break; + + case SID_ATTR_PAGE_FILLSTYLE: + { + const XFillStyleItem* pFillStyleItem = nullptr; + if (eState >= SfxItemState::DEFAULT) + pFillStyleItem = dynamic_cast< const XFillStyleItem* >(pState); + if (pFillStyleItem) + { + css::drawing::FillStyle eXFS = pFillStyleItem->GetValue(); + switch(eXFS) + { + case drawing::FillStyle_NONE: + mxFillStyle->set_active(static_cast< sal_Int32 >(NONE)); + break; + case drawing::FillStyle_SOLID: + mxFillStyle->set_active(static_cast< sal_Int32 >(SOLID)); + break; + case drawing::FillStyle_GRADIENT: + mxFillStyle->set_active(static_cast< sal_Int32 >(GRADIENT)); + break; + case drawing::FillStyle_HATCH: + mxFillStyle->set_active(static_cast< sal_Int32 >(HATCH)); + break; + case drawing::FillStyle_BITMAP: + { + if(mpBitmapItem->isPattern()) + mxFillStyle->set_active(static_cast< sal_Int32 >(PATTERN)); + else + mxFillStyle->set_active(static_cast< sal_Int32 >(BITMAP)); + } + break; + default: + break; + } + Update(); + } + } + break; + + case SID_ATTR_PAGE_SIZE: + { + const SvxSizeItem* pSizeItem = nullptr; + if (eState >= SfxItemState::DEFAULT) + pSizeItem = dynamic_cast<const SvxSizeItem*>(pState); + if (pSizeItem) + { + Size aPaperSize = pSizeItem->GetSize(); + if (mxPaperOrientation->get_active() == 0) + Swap(aPaperSize); + + Paper ePaper = SvxPaperInfo::GetSvxPaper(aPaperSize, meUnit); + mxPaperSizeBox->set_active_id( ePaper ); + } + } + break; + + case SID_ATTR_PAGE: + { + const SvxPageItem* pPageItem = nullptr; + if (eState >= SfxItemState::DEFAULT) + pPageItem = dynamic_cast<const SvxPageItem*>(pState); + if (pPageItem) + { + mpPageItem.reset(pPageItem->Clone()); + bool bIsLandscape = mpPageItem->IsLandscape(); + mxPaperOrientation->set_active( bIsLandscape ? 0 : 1 ); + } + } + break; + + case SID_ATTR_PAGE_LRSPACE: + { + const SvxLongLRSpaceItem* pLRItem = nullptr; + if (eState >= SfxItemState::DEFAULT) + pLRItem = dynamic_cast<const SvxLongLRSpaceItem*>(pState); + if (pLRItem) + { + mpPageLRMarginItem.reset( static_cast<SvxLongLRSpaceItem*>(pState->Clone()) ); + UpdateMarginBox(); + } + } + break; + + case SID_ATTR_PAGE_ULSPACE: + { + const SvxLongULSpaceItem* pULItem = nullptr; + if (eState >= SfxItemState::DEFAULT) + pULItem = dynamic_cast<const SvxLongULSpaceItem*>(pState); + if (pULItem) + { + mpPageULMarginItem.reset( static_cast<SvxLongULSpaceItem*>(pState->Clone()) ); + UpdateMarginBox(); + } + } + break; + + case SID_DISPLAY_MASTER_BACKGROUND: + { + const SfxBoolItem* pBoolItem = nullptr; + if (eState >= SfxItemState::DEFAULT) + pBoolItem = dynamic_cast< const SfxBoolItem* >(pState); + if (pBoolItem) + mxDspMasterBackground->set_active(pBoolItem->GetValue()); + } + break; + case SID_DISPLAY_MASTER_OBJECTS: + { + const SfxBoolItem* pBoolItem = nullptr; + if (eState >= SfxItemState::DEFAULT) + pBoolItem = dynamic_cast< const SfxBoolItem* >(pState); + if (pBoolItem) + mxDspMasterObjects->set_active(pBoolItem->GetValue()); + } + break; + case SID_SELECT_BACKGROUND: + { + if(eState >= SfxItemState::DEFAULT) + { + mxFillStyle->set_active(static_cast< sal_Int32 >(BITMAP)); + Update(); + } + } + break; + case SID_ATTR_METRIC: + { + FieldUnit eFUnit = GetCurrentUnit(eState, pState); + if (meFUnit != eFUnit) + { + meFUnit = eFUnit; + SetMarginsFieldUnit(); + UpdateMarginBox(); + } + } + break; + default: + break; + } +} + +IMPL_LINK_NOARG(SlideBackground, FillStyleModifyHdl, weld::ComboBox&, void) +{ + const eFillStyle nPos = static_cast<eFillStyle>(mxFillStyle->get_active()); + Update(); + + switch (nPos) + { + case NONE: + { + const XFillStyleItem aXFillStyleItem(drawing::FillStyle_NONE); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_FILLSTYLE, SfxCallMode::RECORD, { &aXFillStyleItem }); + } + break; + + case SOLID: + { + if (mpColorItem) + { + const XFillColorItem aItem( OUString(), mpColorItem->GetColorValue() ); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_COLOR, SfxCallMode::RECORD, { &aItem }); + } + } + break; + + case GRADIENT: + { + if (mpGradientItem) + { + const XFillGradientItem aItem( mpGradientItem->GetName(), mpGradientItem->GetGradientValue() ); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_GRADIENT, SfxCallMode::RECORD, { &aItem }); + } + } + break; + + case HATCH: + { + if (mpHatchItem) + { + const XFillHatchItem aItem( mpHatchItem->GetName(), mpHatchItem->GetHatchValue() ); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_HATCH, SfxCallMode::RECORD, { &aItem }); + } + } + break; + + case BITMAP: + case PATTERN: + { + if (mpBitmapItem) + { + const XFillBitmapItem aItem( mpBitmapItem->GetName(), mpBitmapItem->GetGraphicObject() ); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_BITMAP, SfxCallMode::RECORD, { &aItem }); + } + } + break; + + default: + break; + } +//TODO mxFillStyle->Selected(); +} + +IMPL_LINK_NOARG(SlideBackground, PaperSizeModifyHdl, weld::ComboBox&, void) +{ + const Paper ePaper = mxPaperSizeBox->get_active_id(); + Size aSize(SvxPaperInfo::GetPaperSize(ePaper, meUnit)); + + if (mxPaperOrientation->get_active() == 0) + Swap(aSize); + + mpPageItem->SetLandscape(mxPaperOrientation->get_active() == 0); + const SvxSizeItem aSizeItem(SID_ATTR_PAGE_SIZE, aSize); + // Page/slide properties dialog (FuPage::ExecuteDialog and ::ApplyItemSet) misuses + // SID_ATTR_PAGE_EXT1 to distinguish between Impress and Draw, as for whether to fit + // objects to paper size. Until that is handled somehow better, we do the same here + const SfxBoolItem aFitObjs(SID_ATTR_PAGE_EXT1, IsImpress()); + + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_SIZE, SfxCallMode::RECORD, + { &aSizeItem, mpPageItem.get(), &aFitObjs }); + + // Notify LOK clients of the page size change. + if (!comphelper::LibreOfficeKit::isActive()) + return; + + SfxViewShell* pViewShell = SfxViewShell::GetFirst(); + while (pViewShell) + { + if (pViewShell->GetDocId() == mrBase.GetDocId()) + { + SdXImpressDocument* pDoc = comphelper::getFromUnoTunnel<SdXImpressDocument>(pViewShell->GetCurrentDocument()); + SfxLokHelper::notifyDocumentSizeChangedAllViews(pDoc); + } + pViewShell = SfxViewShell::GetNext(*pViewShell); + } +} + +IMPL_LINK_NOARG(SlideBackground, FillColorHdl, ColorListBox&, void) +{ + const drawing::FillStyle eXFS = static_cast<drawing::FillStyle>(mxFillStyle->get_active()); + switch(eXFS) + { + case drawing::FillStyle_SOLID: + { + XFillColorItem aItem(OUString(), mxFillLB->GetSelectEntryColor()); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_COLOR, SfxCallMode::RECORD, { &aItem }); + } + break; + case drawing::FillStyle_GRADIENT: + { + XGradient aGradient; + aGradient.SetStartColor(mxFillGrad1->GetSelectEntryColor()); + aGradient.SetEndColor(mxFillGrad2->GetSelectEntryColor()); + + // the name doesn't really matter, it'll be converted to unique one eventually, + // but it has to be non-empty + XFillGradientItem aItem("gradient", aGradient); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_GRADIENT, SfxCallMode::RECORD, { &aItem }); + } + break; + default: + break; + } +} + +IMPL_LINK_NOARG(SlideBackground, FillBackgroundHdl, weld::ComboBox&, void) +{ + const eFillStyle nFillPos = static_cast<eFillStyle>(mxFillStyle->get_active()); + SfxObjectShell* pSh = SfxObjectShell::Current(); + if (!pSh) + return; + switch(nFillPos) + { + + case HATCH: + { + const SvxHatchListItem * pHatchListItem = pSh->GetItem(SID_HATCH_LIST); + sal_uInt16 nPos = mxFillAttr->get_active(); + XHatch aHatch = pHatchListItem->GetHatchList()->GetHatch(nPos)->GetHatch(); + const OUString aHatchName = pHatchListItem->GetHatchList()->GetHatch(nPos)->GetName(); + + XFillHatchItem aItem(aHatchName, aHatch); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_HATCH, SfxCallMode::RECORD, { &aItem }); + } + break; + + case BITMAP: + case PATTERN: + { + sal_Int16 nPos = mxFillAttr->get_active(); + GraphicObject aBitmap; + OUString aName; + if( nFillPos == BITMAP ) + { + SvxBitmapListItem const * pBitmapListItem = pSh->GetItem(SID_BITMAP_LIST); + aBitmap = pBitmapListItem->GetBitmapList()->GetBitmap(nPos)->GetGraphicObject(); + aName = pBitmapListItem->GetBitmapList()->GetBitmap(nPos)->GetName(); + } + else if( nFillPos == PATTERN ) + { + SvxPatternListItem const * pPatternListItem = pSh->GetItem(SID_PATTERN_LIST); + aBitmap = pPatternListItem->GetPatternList()->GetBitmap(nPos)->GetGraphicObject(); + aName = pPatternListItem->GetPatternList()->GetBitmap(nPos)->GetName(); + } + XFillBitmapItem aItem(aName, aBitmap); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_BITMAP, SfxCallMode::RECORD, { &aItem }); + } + break; + + default: + break; + } +} + +IMPL_LINK_NOARG(SlideBackground, AssignMasterPage, weld::ComboBox&, void) +{ + ::sd::DrawDocShell* pDocSh = dynamic_cast<::sd::DrawDocShell*>( SfxObjectShell::Current() ); + SdDrawDocument* pDoc = pDocSh ? pDocSh->GetDoc() : nullptr; + if (!pDoc) + return; + + auto pSSVS = sd::slidesorter::SlideSorterViewShell::GetSlideSorter(mrBase); + if (pSSVS == nullptr) + return; + + auto& rSSController = pSSVS->GetSlideSorter().GetController(); + auto& rPageSelector = rSSController.GetPageSelector(); + + for( sal_uInt16 nPage = 0; nPage < pDoc->GetSdPageCount(PageKind::Standard); nPage++ ) + { + if (rPageSelector.IsPageSelected(nPage)) + { + OUString aLayoutName(mxMasterSlide->get_active_text()); + pDoc->SetMasterPage(nPage, aLayoutName, pDoc, false, false); + } + } +} + +IMPL_LINK_NOARG(SlideBackground, EditMasterHdl, weld::Button&, void) +{ + GetBindings()->GetDispatcher()->Execute( SID_SLIDE_MASTER_MODE, SfxCallMode::RECORD ); +} + +IMPL_LINK_NOARG(SlideBackground, SelectBgHdl, weld::Button&, void) +{ + GetBindings()->GetDispatcher()->Execute( SID_SELECT_BACKGROUND, SfxCallMode::RECORD ); +} + +IMPL_LINK_NOARG(SlideBackground, CloseMasterHdl, weld::Button&, void) +{ + GetBindings()->GetDispatcher()->Execute( SID_CLOSE_MASTER_VIEW, SfxCallMode::RECORD ); +} + +IMPL_LINK_NOARG(SlideBackground, DspBackground, weld::Toggleable&, void) +{ + bool IsChecked = mxDspMasterBackground->get_active(); + const SfxBoolItem aBoolItem(SID_DISPLAY_MASTER_BACKGROUND, IsChecked); + GetBindings()->GetDispatcher()->ExecuteList(SID_DISPLAY_MASTER_BACKGROUND, SfxCallMode::RECORD, { &aBoolItem }); +} + +IMPL_LINK_NOARG(SlideBackground, DspObjects, weld::Toggleable&, void) +{ + bool IsChecked = mxDspMasterObjects->get_active(); + const SfxBoolItem aBoolItem(SID_DISPLAY_MASTER_OBJECTS,IsChecked); + GetBindings()->GetDispatcher()->ExecuteList(SID_DISPLAY_MASTER_OBJECTS, SfxCallMode::RECORD, { &aBoolItem, &aBoolItem }); +} + +IMPL_LINK_NOARG( SlideBackground, ModifyMarginHdl, weld::ComboBox&, void ) +{ + bool bApplyNewPageMargins = true; + switch ( mxMarginSelectBox->get_active() ) + { + case 0: + SetNone(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin); + break; + case 1: + SetNarrow(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin); + break; + case 2: + SetModerate(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin); + break; + case 3: + SetNormal075(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin); + break; + case 4: + SetNormal100(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin); + break; + case 5: + SetNormal125(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin); + break; + case 6: + SetWide(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin); + break; + default: + bApplyNewPageMargins = false; + break; + } + + if(bApplyNewPageMargins) + { + ExecuteMarginLRChange(m_nPageLeftMargin, m_nPageRightMargin); + ExecuteMarginULChange(m_nPageTopMargin, m_nPageBottomMargin); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/SlideBackground.hxx b/sd/source/ui/sidebar/SlideBackground.hxx new file mode 100644 index 000000000..25af0a4af --- /dev/null +++ b/sd/source/ui/sidebar/SlideBackground.hxx @@ -0,0 +1,180 @@ +/* -*- 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 . + */ + +#pragma once + +#include <memory> +#include <sfx2/sidebar/PanelLayout.hxx> +#include <sfx2/sidebar/ControllerItem.hxx> +#include <svx/papersizelistbox.hxx> +#include <sfx2/sidebar/IContextChangeReceiver.hxx> +#include <vcl/EnumContext.hxx> + +#include <com/sun/star/frame/XFrame.hpp> + +namespace sd { class ViewShellBase; } +namespace sd::tools { class EventMultiplexerEvent; } + +class ColorListBox; +class SvxPageItem; +class SvxLongLRSpaceItem; +class SvxLongULSpaceItem; +class XFillColorItem; +class XGradient; +class XFillGradientItem; +class XFillBitmapItem; +class XFillHatchItem; + +namespace sd::sidebar { + +class SlideBackground : + public PanelLayout, + public ::sfx2::sidebar::IContextChangeReceiver, + public ::sfx2::sidebar::ControllerItem::ItemUpdateReceiverInterface +{ +public: + SlideBackground( + weld::Widget* pParent, + ViewShellBase& rBase, + const css::uno::Reference<css::frame::XFrame>& rxFrame, + SfxBindings* pBindings ); + virtual ~SlideBackground() override; + SfxBindings* GetBindings() { return mpBindings; } + // Window + virtual void NotifyItemUpdate( + const sal_uInt16 nSID, + const SfxItemState eState, + const SfxPoolItem* pState) override; + + virtual void GetControlState( + const sal_uInt16 /*nSId*/, + boost::property_tree::ptree& /*rState*/) override {}; + + virtual void HandleContextChange( + const vcl::EnumContext& rContext) override; + virtual void DumpAsPropertyTree(::tools::JsonWriter&) override; + +private: + + ViewShellBase& mrBase; + + std::unique_ptr<SvxPaperSizeListBox> mxPaperSizeBox; + std::unique_ptr<weld::ComboBox> mxPaperOrientation; + std::unique_ptr<weld::ComboBox> mxMasterSlide; + std::unique_ptr<weld::Label> mxBackgroundLabel; + std::unique_ptr<weld::ComboBox> mxFillStyle; + std::unique_ptr<ColorListBox> mxFillLB; + std::unique_ptr<weld::ComboBox> mxFillAttr; + std::unique_ptr<ColorListBox> mxFillGrad1; + std::unique_ptr<ColorListBox> mxFillGrad2; + std::unique_ptr<weld::Button> mxInsertImage; + std::unique_ptr<weld::CheckButton> mxDspMasterBackground; + std::unique_ptr<weld::CheckButton> mxDspMasterObjects; + std::unique_ptr<weld::Button> mxCloseMaster; + std::unique_ptr<weld::Button> mxEditMaster; + std::unique_ptr<weld::Label> mxMasterLabel; + std::unique_ptr<weld::ComboBox> mxMarginSelectBox; + std::unique_ptr<weld::Label> mxCustomEntry; + std::unique_ptr<weld::Label> mxMarginLabel; + + ::sfx2::sidebar::ControllerItem maPaperSizeController; + ::sfx2::sidebar::ControllerItem maPaperOrientationController; + ::sfx2::sidebar::ControllerItem maPaperMarginLRController; + ::sfx2::sidebar::ControllerItem maPaperMarginULController; + ::sfx2::sidebar::ControllerItem maBckColorController; + ::sfx2::sidebar::ControllerItem maBckGradientController; + ::sfx2::sidebar::ControllerItem maBckHatchController; + ::sfx2::sidebar::ControllerItem maBckBitmapController; + ::sfx2::sidebar::ControllerItem maBckFillStyleController; + ::sfx2::sidebar::ControllerItem maBckImageController; + ::sfx2::sidebar::ControllerItem maDspBckController; + ::sfx2::sidebar::ControllerItem maDspObjController; + ::sfx2::sidebar::ControllerItem maMetricController; + ::sfx2::sidebar::ControllerItem maCloseMasterController; + + std::unique_ptr< SvxPageItem > mpPageItem; + std::unique_ptr< XFillColorItem > mpColorItem; + std::unique_ptr< XFillGradientItem > mpGradientItem; + std::unique_ptr< XFillHatchItem > mpHatchItem; + std::unique_ptr< XFillBitmapItem > mpBitmapItem; + + bool mbSwitchModeToNormal; + bool mbSwitchModeToMaster; + + css::uno::Reference<css::frame::XFrame> mxFrame; + vcl::EnumContext maContext; + vcl::EnumContext maDrawOtherContext; + vcl::EnumContext maDrawMasterContext; + vcl::EnumContext maImpressOtherContext; + vcl::EnumContext maImpressMasterContext; + vcl::EnumContext maImpressHandoutContext; + vcl::EnumContext maImpressNotesContext; + bool mbTitle; + std::unique_ptr<SvxLongLRSpaceItem> mpPageLRMarginItem; + std::unique_ptr<SvxLongULSpaceItem> mpPageULMarginItem; + ::tools::Long m_nPageLeftMargin; + ::tools::Long m_nPageRightMargin; + ::tools::Long m_nPageTopMargin; + ::tools::Long m_nPageBottomMargin; + FieldUnit meFUnit; + OUString maCustomEntry; + + SfxBindings* mpBindings; + + MapUnit meUnit; + + DECL_LINK(FillBackgroundHdl, weld::ComboBox&, void); + DECL_LINK(FillStyleModifyHdl, weld::ComboBox&, void); + DECL_LINK(PaperSizeModifyHdl, weld::ComboBox&, void); + DECL_LINK(FillColorHdl, ColorListBox&, void); + DECL_LINK(AssignMasterPage, weld::ComboBox&, void); + DECL_LINK(DspBackground, weld::Toggleable&, void); + DECL_LINK(DspObjects, weld::Toggleable&, void); + DECL_LINK(CloseMasterHdl, weld::Button&, void); + DECL_LINK(EditMasterHdl, weld::Button&, void); + DECL_LINK(SelectBgHdl, weld::Button&, void); + DECL_LINK(EventMultiplexerListener, tools::EventMultiplexerEvent&, void ); + DECL_LINK( ModifyMarginHdl, weld::ComboBox&, void ); + + void Initialize(); + void Update(); + void UpdateMarginBox(); + void SetPanelTitle(const OUString& rTitle); + void SetMarginsFieldUnit(); + + Color const & GetColorSetOrDefault(); + XGradient const & GetGradientSetOrDefault(); + OUString const & GetHatchingSetOrDefault(); + OUString const & GetBitmapSetOrDefault(); + OUString const & GetPatternSetOrDefault(); + bool IsDraw(); + bool IsImpress(); + void addListener(); + void removeListener(); + void ExecuteMarginLRChange(const ::tools::Long mnPageLeftMargin, const ::tools::Long mnPageRightMargin); + void ExecuteMarginULChange(const ::tools::Long mnPageTopMargin, const ::tools::Long mnPageBottomMargin); + void populateMasterSlideDropdown(); + void updateMasterSlideSelection(); + + static FieldUnit GetCurrentUnit(SfxItemState eState, const SfxPoolItem* pState); +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/PaneHider.cxx b/sd/source/ui/slideshow/PaneHider.cxx new file mode 100644 index 000000000..85858c0b6 --- /dev/null +++ b/sd/source/ui/slideshow/PaneHider.cxx @@ -0,0 +1,99 @@ +/* -*- 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 "PaneHider.hxx" + +#include <ViewShell.hxx> +#include <ViewShellBase.hxx> +#include "slideshowimpl.hxx" +#include <framework/FrameworkHelper.hxx> + +#include <com/sun/star/drawing/framework/XControllerManager.hpp> +#include <com/sun/star/drawing/framework/XConfigurationController.hpp> +#include <com/sun/star/drawing/framework/XConfiguration.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/lang/DisposedException.hpp> + +#include <tools/diagnose_ex.h> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; +using ::sd::framework::FrameworkHelper; +using ::com::sun::star::lang::DisposedException; + +namespace sd +{ +PaneHider::PaneHider(const ViewShell& rViewShell, SlideshowImpl* pSlideShow) +{ + // Hide the left and right pane windows when a slideshow exists and is + // not full screen. + if (pSlideShow == nullptr || pSlideShow->isFullScreen()) + return; + + try + { + Reference<XControllerManager> xControllerManager( + rViewShell.GetViewShellBase().GetController(), UNO_QUERY_THROW); + mxConfigurationController = xControllerManager->getConfigurationController(); + if (mxConfigurationController.is()) + { + // Get and save the current configuration. + mxConfiguration = mxConfigurationController->getRequestedConfiguration(); + if (mxConfiguration.is()) + { + // Iterate over the resources and deactivate the panes. + const Sequence<Reference<XResourceId>> aResources(mxConfiguration->getResources( + nullptr, framework::FrameworkHelper::msPaneURLPrefix, + AnchorBindingMode_DIRECT)); + for (const Reference<XResourceId>& xPaneId : aResources) + { + if (xPaneId->getResourceURL() != FrameworkHelper::msCenterPaneURL) + { + mxConfigurationController->requestResourceDeactivation(xPaneId); + } + } + } + } + FrameworkHelper::Instance(rViewShell.GetViewShellBase())->WaitForUpdate(); + } + catch (RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } +} + +PaneHider::~PaneHider() +{ + if (mxConfiguration.is() && mxConfigurationController.is()) + { + try + { + mxConfigurationController->restoreConfiguration(mxConfiguration); + } + catch (DisposedException&) + { + // When the configuration controller is already disposed then + // there is no point in restoring the configuration. + } + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/PaneHider.hxx b/sd/source/ui/slideshow/PaneHider.hxx new file mode 100644 index 000000000..a2d3cabb0 --- /dev/null +++ b/sd/source/ui/slideshow/PaneHider.hxx @@ -0,0 +1,66 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/uno/Reference.hxx> + +namespace com::sun::star::drawing::framework +{ +class XConfiguration; +} +namespace com::sun::star::drawing::framework +{ +class XConfigurationController; +} + +namespace sd +{ +class ViewShell; +class SlideshowImpl; + +/** Hide the windows of the side panes and restore the original visibility + later. Used by the in-window slide show in order to use the whole frame + window for the show. +*/ +class PaneHider +{ +public: + /** The constructor hides all side panes that belong to the + ViewShellBase of the given view shell. + */ + PaneHider(const ViewShell& rViewShell, SlideshowImpl* pSlideShow); + + /** Restore the original visibility of the side panes. + */ + ~PaneHider(); + +private: + /** Remember whether the visibility states of the windows of the panes + has been modified and have to be restored. + */ + + css::uno::Reference<css::drawing::framework::XConfigurationController> + mxConfigurationController; + css::uno::Reference<css::drawing::framework::XConfiguration> mxConfiguration; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/SlideShowRestarter.cxx b/sd/source/ui/slideshow/SlideShowRestarter.cxx new file mode 100644 index 000000000..b8c61ba48 --- /dev/null +++ b/sd/source/ui/slideshow/SlideShowRestarter.cxx @@ -0,0 +1,156 @@ +/* -*- 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 <DrawController.hxx> +#include <ViewShellBase.hxx> +#include <slideshow.hxx> +#include "SlideShowRestarter.hxx" + +#include <comphelper/propertyvalue.hxx> +#include <framework/ConfigurationController.hxx> +#include <framework/FrameworkHelper.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/svxids.hrc> +#include <vcl/svapp.hxx> + +#include <functional> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using ::sd::framework::FrameworkHelper; + +namespace sd { + +SlideShowRestarter::SlideShowRestarter ( + const ::rtl::Reference<SlideShow>& rpSlideShow, + ViewShellBase* pViewShellBase) + : mnEventId(nullptr), + mpSlideShow(rpSlideShow), + mpViewShellBase(pViewShellBase), + mnDisplayCount(Application::GetScreenCount()), + mpDispatcher(pViewShellBase->GetViewFrame()->GetDispatcher()), + mnCurrentSlideNumber(0) +{ +} + +SlideShowRestarter::~SlideShowRestarter() +{ +} + +void SlideShowRestarter::Restart (bool bForce) +{ + // Prevent multiple and concurrently restarts. + if (mnEventId != nullptr) + return; + + if (bForce) + mnDisplayCount = 0; + + // Remember the current slide in order to restore it after the slide + // show has been restarted. + if (mpSlideShow.is()) + mnCurrentSlideNumber = mpSlideShow->getCurrentPageNumber(); + + // Remember a shared pointer to this object to prevent its destruction + // before the whole restarting process has finished. + mpSelf = shared_from_this(); + + // We do not know in what situation this method was called. So, in + // order to be able to cleanly stop the presentation, we do that + // asynchronously. + mnEventId = Application::PostUserEvent( + LINK(this, SlideShowRestarter, EndPresentation)); +} + +IMPL_LINK_NOARG(SlideShowRestarter, EndPresentation, void*, void) +{ + mnEventId = nullptr; + if (!mpSlideShow.is()) + return; + + if (mnDisplayCount == static_cast<sal_Int32>(Application::GetScreenCount())) + return; + + bool bIsExitAfterPresenting = mpSlideShow->IsExitAfterPresenting(); + mpSlideShow->SetExitAfterPresenting(false); + mpSlideShow->end(); + mpSlideShow->SetExitAfterPresenting(bIsExitAfterPresenting); + + // The following piece of code should not be here because the + // slide show should be aware of the existence of the presenter + // console (which is displayed in the FullScreenPane). But the + // timing has to be right and I did not find a better place for + // it. + + // Wait for the full screen pane, which displays the presenter + // console, to disappear. Only when it is gone, call + // InitiatePresenterStart(), in order to begin the asynchronous + // restart of the slide show. + if (mpViewShellBase == nullptr) + return; + + ::std::shared_ptr<FrameworkHelper> pHelper( + FrameworkHelper::Instance(*mpViewShellBase)); + if (pHelper->GetConfigurationController()->getResource( + FrameworkHelper::CreateResourceId(FrameworkHelper::msFullScreenPaneURL)).is()) + { + ::sd::framework::ConfigurationController::Lock aLock ( + pHelper->GetConfigurationController()); + + pHelper->RunOnConfigurationEvent( + FrameworkHelper::msConfigurationUpdateEndEvent, + ::std::bind(&SlideShowRestarter::StartPresentation, shared_from_this())); + pHelper->UpdateConfiguration(); + } + else + { + StartPresentation(); + } +} + +void SlideShowRestarter::StartPresentation() +{ + //rhbz#1091117 crash because we're exiting the application, and this is + //being called during the configuration update event on exit. At this point + //newly created objects won't get disposed called on them, because the + //disposer is doing its last execution of that now. + if (mpViewShellBase && mpViewShellBase->GetDrawController().IsDisposing()) + return; + + if (mpDispatcher == nullptr && mpViewShellBase!=nullptr) + mpDispatcher = mpViewShellBase->GetViewFrame()->GetDispatcher(); + + // Start the slide show on the saved current slide. + if (mpDispatcher != nullptr) + { + mpDispatcher->Execute(SID_PRESENTATION, SfxCallMode::ASYNCHRON); + if (mpSlideShow.is()) + { + Sequence aProperties{ comphelper::makePropertyValue("FirstPage", + "page" + OUString::number(mnCurrentSlideNumber+1)) }; + mpSlideShow->startWithArguments(aProperties); + } + mpSelf.reset(); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/SlideShowRestarter.hxx b/sd/source/ui/slideshow/SlideShowRestarter.hxx new file mode 100644 index 000000000..e8e97a600 --- /dev/null +++ b/sd/source/ui/slideshow/SlideShowRestarter.hxx @@ -0,0 +1,88 @@ +/* -*- 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 . + */ + +#pragma once + +#include <rtl/ref.hxx> +#include <tools/link.hxx> +#include <memory> + +namespace sd +{ +class SlideShow; +} +namespace sd +{ +class ViewShellBase; +} +class SfxDispatcher; +struct ImplSVEvent; + +namespace sd +{ +/** This class is used when a display is removed or added to restart the + slide show. This is necessary at least with DirectX because + deactivating a display invalidates DirectX resources. Accessing those + leads to a crash. + + During a restart a possibly installed presenter extension is given the + opportunity to show or hide depending on the number of available displays. +*/ +class SlideShowRestarter : public std::enable_shared_from_this<SlideShowRestarter> +{ +public: + /** Create a new SlideShowRestarter object. + @param rpSlideShow + The slide show is used to determine the current slide, which is + restored after the restart, and of course to stop and start the + slide show. + @param pViewShellBase + Used to get access to a slot dispatcher. + */ + SlideShowRestarter(const ::rtl::Reference<SlideShow>& rpSlideShow, + ViewShellBase* pViewShellBase); + virtual ~SlideShowRestarter(); + + /** Restarting the slide show is an asynchronous multi step process + which is started by calling this method. + @param bForce + Used to force a re-start, even if the display count is unchanged. + */ + void Restart(bool bForce); + +private: + ImplSVEvent* mnEventId; + ::rtl::Reference<SlideShow> mpSlideShow; + ViewShellBase* mpViewShellBase; + ::std::shared_ptr<SlideShowRestarter> mpSelf; + sal_Int32 mnDisplayCount; + SfxDispatcher* mpDispatcher; + sal_Int32 mnCurrentSlideNumber; + + DECL_LINK(EndPresentation, void*, void); + + /** Restart the presentation on the slide last shown before the restart + was initiated. + */ + void StartPresentation(); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/showwin.cxx b/sd/source/ui/slideshow/showwin.cxx new file mode 100644 index 000000000..35c0a4027 --- /dev/null +++ b/sd/source/ui/slideshow/showwin.cxx @@ -0,0 +1,629 @@ +/* -*- 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 <com/sun/star/awt/Key.hpp> + +#include "showwindow.hxx" +#include "slideshowimpl.hxx" + +#include <unotools/localedatawrapper.hxx> +#include <unotools/syslocale.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/sfxsids.hrc> + + +#include <slideshow.hxx> +#include <ViewShell.hxx> +#include <sdresid.hxx> +#include <helpids.h> +#include <strings.hrc> + +#include <sal/log.hxx> +#include <vcl/settings.hxx> +#include <vcl/virdev.hxx> + +using namespace ::com::sun::star; + +namespace sd { + +const sal_uInt64 HIDE_MOUSE_TIMEOUT = 10000; +const sal_uInt64 SHOW_MOUSE_TIMEOUT = 1000; + +ShowWindow::ShowWindow( const ::rtl::Reference< SlideshowImpl >& xController, vcl::Window* pParent ) +: ::sd::Window( pParent ) +, maPauseTimer("sd ShowWindow maPauseTimer") +, maMouseTimer("sd ShowWindow maMouseTimer") +, mnPauseTimeout( SLIDE_NO_TIMEOUT ) +, mnRestartPageIndex( PAGE_NO_END ) +, meShowWindowMode(SHOWWINDOWMODE_NORMAL) +, mbShowNavigatorAfterSpecialMode( false ) +, mbMouseAutoHide(true) +, mbMouseCursorHidden(false) +, mnFirstMouseMove(0) +, mxController( xController ) +{ + GetOutDev()->SetOutDevViewType( OutDevViewType::SlideShow ); + + // Do never mirror the preview window. This explicitly includes right + // to left writing environments. + EnableRTL (false); + + MapMode aMap(GetMapMode()); + aMap.SetMapUnit(MapUnit::Map100thMM); + SetMapMode(aMap); + + // set HelpId + SetHelpId( HID_SD_WIN_PRESENTATION ); + + maPauseTimer.SetInvokeHandler( LINK( this, ShowWindow, PauseTimeoutHdl ) ); + maPauseTimer.SetTimeout( 1000 ); + maMouseTimer.SetInvokeHandler( LINK( this, ShowWindow, MouseTimeoutHdl ) ); + maMouseTimer.SetTimeout( HIDE_MOUSE_TIMEOUT ); + + maShowBackground = Wallpaper( COL_BLACK ); + SetBackground(); // avoids that VCL paints any background! + GetParent()->Show(); + AddEventListener( LINK( this, ShowWindow, EventHdl ) ); +} + +ShowWindow::~ShowWindow() +{ + disposeOnce(); +} + +void ShowWindow::dispose() +{ + maPauseTimer.Stop(); + maMouseTimer.Stop(); + ::sd::Window::dispose(); +} + +void ShowWindow::KeyInput(const KeyEvent& rKEvt) +{ + bool bReturn = false; + + if( SHOWWINDOWMODE_PREVIEW == meShowWindowMode ) + { + TerminateShow(); + bReturn = true; + } + else if( SHOWWINDOWMODE_END == meShowWindowMode ) + { + const int nKeyCode = rKEvt.GetKeyCode().GetCode(); + switch( nKeyCode ) + { + case KEY_PAGEUP: + case KEY_LEFT: + case KEY_UP: + case KEY_P: + case KEY_HOME: + case KEY_END: + case awt::Key::CONTEXTMENU: + // these keys will be handled by the slide show even + // while in end mode + break; + default: + TerminateShow(); + bReturn = true; + } + } + else if( SHOWWINDOWMODE_BLANK == meShowWindowMode ) + { + bool bFakeKeyPress = rKEvt.GetKeyCode().GetFullCode() == 0; + // Ignore workaround of https://gitlab.gnome.org/GNOME/gtk/issues/1785 + // See calls to GtkSalFrame::makeFakeKeyPress (Fixed in GTK 2.34) + if (!bFakeKeyPress) + RestartShow(); + bReturn = true; + } + else if( SHOWWINDOWMODE_PAUSE == meShowWindowMode ) + { + const int nKeyCode = rKEvt.GetKeyCode().GetCode(); + switch( nKeyCode ) + { + case KEY_ESCAPE: + TerminateShow(); + bReturn = true; + break; + case KEY_PAGEUP: + case KEY_RIGHT: + case KEY_UP: + case KEY_P: + case KEY_HOME: + case KEY_END: + case awt::Key::CONTEXTMENU: + // these keys will be handled by the slide show even + // while in end mode + break; + default: + RestartShow(); + bReturn = true; + break; + } + } + + if( !bReturn ) + { + if( mxController.is() ) + bReturn = mxController->keyInput(rKEvt); + + if( !bReturn ) + { + if( mpViewShell ) + { + mpViewShell->KeyInput(rKEvt,this); + } + else + { + Window::KeyInput(rKEvt); + } + } + } + + if( mpViewShell ) + mpViewShell->SetActiveWindow( this ); +} + +void ShowWindow::MouseButtonDown(const MouseEvent& /*rMEvt*/) +{ + if( SHOWWINDOWMODE_PREVIEW == meShowWindowMode ) + { + TerminateShow(); + } + else if( mpViewShell ) + { + mpViewShell->SetActiveWindow( this ); + } +} + +void ShowWindow::MouseMove(const MouseEvent& /*rMEvt*/) +{ + if( mbMouseAutoHide ) + { + if( mbMouseCursorHidden ) + { + if( mnFirstMouseMove ) + { + // if this is not the first mouse move while hidden, see if + // enough time has pasted to show mouse pointer again + sal_uInt64 nTime = ::tools::Time::GetSystemTicks(); + if( (nTime - mnFirstMouseMove) >= SHOW_MOUSE_TIMEOUT ) + { + ShowPointer( true ); + mnFirstMouseMove = 0; + mbMouseCursorHidden = false; + maMouseTimer.SetTimeout( HIDE_MOUSE_TIMEOUT ); + maMouseTimer.Start(); + } + } + else + { + // if this is the first mouse move, note current + // time and start idle timer to cancel show mouse pointer + // again if not enough mouse movement is measured + mnFirstMouseMove = ::tools::Time::GetSystemTicks(); + maMouseTimer.SetTimeout( 2*SHOW_MOUSE_TIMEOUT ); + maMouseTimer.Start(); + } + } + else + { + // current mousemove restarts the idle timer to hide the mouse + maMouseTimer.Start(); + } + } + + if( mpViewShell ) + mpViewShell->SetActiveWindow( this ); +} + +void ShowWindow::MouseButtonUp(const MouseEvent& rMEvt) +{ + if( SHOWWINDOWMODE_PREVIEW == meShowWindowMode ) + { + TerminateShow(); + } + else if( (SHOWWINDOWMODE_END == meShowWindowMode) && !rMEvt.IsRight() ) + { + TerminateShow(); + } + else if( (( SHOWWINDOWMODE_BLANK == meShowWindowMode ) || ( SHOWWINDOWMODE_PAUSE == meShowWindowMode )) + && !rMEvt.IsRight() ) + { + RestartShow(); + } + else + { + if( mxController.is() ) + mxController->mouseButtonUp( rMEvt ); + } +} + +/** + * if FuSlideShow is still available, forward it + */ +void ShowWindow::Paint(vcl::RenderContext& /*rRenderContext*/, const ::tools::Rectangle& rRect) +{ + if( (meShowWindowMode == SHOWWINDOWMODE_NORMAL) || (meShowWindowMode == SHOWWINDOWMODE_PREVIEW) ) + { + if( mxController.is() ) + { + mxController->paint(); + } + else if(mpViewShell ) + { + mpViewShell->Paint(rRect, this); + } + } + else + { + GetOutDev()->DrawWallpaper( rRect, maShowBackground ); + + if( SHOWWINDOWMODE_END == meShowWindowMode ) + { + DrawEndScene(); + } + else if( SHOWWINDOWMODE_PAUSE == meShowWindowMode ) + { + DrawPauseScene( false ); + } + else if( SHOWWINDOWMODE_BLANK == meShowWindowMode ) + { + // just blank through background color => nothing to be done here + } + } +} + +void ShowWindow::LoseFocus() +{ + Window::LoseFocus(); + + if( SHOWWINDOWMODE_PREVIEW == meShowWindowMode) + TerminateShow(); +} + +void ShowWindow::SetEndMode() +{ + if( !(( SHOWWINDOWMODE_NORMAL == meShowWindowMode ) && mpViewShell && mpViewShell->GetView()) ) + return; + + DeleteWindowFromPaintView(); + meShowWindowMode = SHOWWINDOWMODE_END; + maShowBackground = Wallpaper( COL_BLACK ); + + // hide navigator if it is visible + if( mpViewShell->GetViewFrame()->GetChildWindow( SID_NAVIGATOR ) ) + { + mpViewShell->GetViewFrame()->ShowChildWindow( SID_NAVIGATOR, false ); + mbShowNavigatorAfterSpecialMode = true; + } + + Invalidate(); +} + +bool ShowWindow::SetPauseMode( sal_Int32 nTimeout, Graphic const * pLogo ) +{ + rtl::Reference< SlideShow > xSlideShow; + + if( mpViewShell ) + xSlideShow = SlideShow::GetSlideShow( mpViewShell->GetViewShellBase() ); + + if( xSlideShow.is() && !nTimeout ) + { + xSlideShow->jumpToPageIndex( 0 ); + } + else if( ( SHOWWINDOWMODE_NORMAL == meShowWindowMode ) && mpViewShell && mpViewShell->GetView() ) + { + DeleteWindowFromPaintView(); + mnPauseTimeout = nTimeout; + mnRestartPageIndex = 0; + meShowWindowMode = SHOWWINDOWMODE_PAUSE; + maShowBackground = Wallpaper( COL_BLACK ); + + // hide navigator if it is visible + if( mpViewShell->GetViewFrame()->GetChildWindow( SID_NAVIGATOR ) ) + { + mpViewShell->GetViewFrame()->ShowChildWindow( SID_NAVIGATOR, false ); + mbShowNavigatorAfterSpecialMode = true; + } + + if( pLogo ) + maLogo = *pLogo; + + Invalidate(); + + if( SLIDE_NO_TIMEOUT != mnPauseTimeout ) + maPauseTimer.Start(); + } + + return( SHOWWINDOWMODE_PAUSE == meShowWindowMode ); +} + +bool ShowWindow::SetBlankMode( sal_Int32 nPageIndexToRestart, const Color& rBlankColor ) +{ + if( ( SHOWWINDOWMODE_NORMAL == meShowWindowMode ) && mpViewShell && mpViewShell->GetView() ) + { + DeleteWindowFromPaintView(); + mnRestartPageIndex = nPageIndexToRestart; + meShowWindowMode = SHOWWINDOWMODE_BLANK; + maShowBackground = Wallpaper( rBlankColor ); + + // hide navigator if it is visible + if( mpViewShell->GetViewFrame()->GetChildWindow( SID_NAVIGATOR ) ) + { + mpViewShell->GetViewFrame()->ShowChildWindow( SID_NAVIGATOR, false ); + mbShowNavigatorAfterSpecialMode = true; + } + + Invalidate(); + } + + return( SHOWWINDOWMODE_BLANK == meShowWindowMode ); +} + +void ShowWindow::SetPreviewMode() +{ + meShowWindowMode = SHOWWINDOWMODE_PREVIEW; +} + +void ShowWindow::TerminateShow() +{ + maLogo.Clear(); + maPauseTimer.Stop(); + maMouseTimer.Stop(); + GetOutDev()->Erase(); + maShowBackground = Wallpaper( COL_BLACK ); + meShowWindowMode = SHOWWINDOWMODE_NORMAL; + mnPauseTimeout = SLIDE_NO_TIMEOUT; + + if( mpViewShell ) + { + // show navigator? + if( mbShowNavigatorAfterSpecialMode ) + { + mpViewShell->GetViewFrame()->ShowChildWindow( SID_NAVIGATOR ); + mbShowNavigatorAfterSpecialMode = false; + } + } + + if( mxController.is() ) + mxController->endPresentation(); + + mnRestartPageIndex = PAGE_NO_END; +} + +void ShowWindow::RestartShow() +{ + RestartShow( mnRestartPageIndex ); +} + +void ShowWindow::RestartShow( sal_Int32 nPageIndexToRestart ) +{ + ShowWindowMode eOldShowWindowMode = meShowWindowMode; + + maLogo.Clear(); + maPauseTimer.Stop(); + GetOutDev()->Erase(); + maShowBackground = Wallpaper( COL_BLACK ); + meShowWindowMode = SHOWWINDOWMODE_NORMAL; + mnPauseTimeout = SLIDE_NO_TIMEOUT; + + if( mpViewShell ) + { + rtl::Reference< SlideShow > xSlideShow( SlideShow::GetSlideShow( mpViewShell->GetViewShellBase() ) ); + + if( xSlideShow.is() ) + { + AddWindowToPaintView(); + + if( SHOWWINDOWMODE_BLANK == eOldShowWindowMode || SHOWWINDOWMODE_END == eOldShowWindowMode ) + { + xSlideShow->pause(false); + Invalidate(); + } + else + { + xSlideShow->jumpToPageIndex( nPageIndexToRestart ); + } + } + } + + mnRestartPageIndex = PAGE_NO_END; + + // show navigator? + if( mbShowNavigatorAfterSpecialMode ) + { + if (mpViewShell) + mpViewShell->GetViewFrame()->ShowChildWindow( SID_NAVIGATOR ); + mbShowNavigatorAfterSpecialMode = false; + } +} + +void ShowWindow::DrawPauseScene( bool bTimeoutOnly ) +{ + const MapMode& rMap = GetMapMode(); + const Point aOutOrg( PixelToLogic( Point() ) ); + const Size aOutSize( GetOutDev()->GetOutputSize() ); + const Size aTextSize(OutputDevice::LogicToLogic(Size(0, 14), MapMode(MapUnit::MapPoint), rMap)); + const Size aOffset(OutputDevice::LogicToLogic(Size(1000, 1000), MapMode(MapUnit::Map100thMM), rMap)); + OUString aText( SdResId( STR_PRES_PAUSE ) ); + bool bDrawn = false; + + vcl::Font aFont( GetSettings().GetStyleSettings().GetMenuFont() ); + const vcl::Font aOldFont( GetFont() ); + + aFont.SetFontSize( aTextSize ); + aFont.SetColor( COL_WHITE ); + aFont.SetCharSet( aOldFont.GetCharSet() ); + aFont.SetLanguage( aOldFont.GetLanguage() ); + + if( !bTimeoutOnly && ( maLogo.GetType() != GraphicType::NONE ) ) + { + Size aGrfSize; + + if (maLogo.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel) + aGrfSize = PixelToLogic( maLogo.GetPrefSize() ); + else + aGrfSize = OutputDevice::LogicToLogic( maLogo.GetPrefSize(), maLogo.GetPrefMapMode(), rMap ); + + const Point aGrfPos( std::max( aOutOrg.X() + aOutSize.Width() - aGrfSize.Width() - aOffset.Width(), aOutOrg.X() ), + std::max( aOutOrg.Y() + aOutSize.Height() - aGrfSize.Height() - aOffset.Height(), aOutOrg.Y() ) ); + + if( maLogo.IsAnimated() ) + maLogo.StartAnimation(*GetOutDev(), aGrfPos, aGrfSize, reinterpret_cast<sal_IntPtr>(this)); + else + maLogo.Draw(*GetOutDev(), aGrfPos, aGrfSize); + } + + if( SLIDE_NO_TIMEOUT != mnPauseTimeout ) + { + MapMode aVMap( rMap ); + ScopedVclPtrInstance< VirtualDevice > pVDev( *GetOutDev() ); + + aVMap.SetOrigin( Point() ); + pVDev->SetMapMode( aVMap ); + pVDev->SetBackground( Wallpaper( COL_BLACK ) ); + + // set font first, to determine real output height + pVDev->SetFont( aFont ); + + const Size aVDevSize( aOutSize.Width(), pVDev->GetTextHeight() ); + + if( pVDev->SetOutputSize( aVDevSize ) ) + { + // Note: if performance gets an issue here, we can use NumberFormatter directly + SvtSysLocale aSysLocale; + const LocaleDataWrapper& aLocaleData = aSysLocale.GetLocaleData(); + + aText += " ( " + aLocaleData.getDuration( ::tools::Time( 0, 0, mnPauseTimeout ) ) + " )"; + pVDev->DrawText( Point( aOffset.Width(), 0 ), aText ); + GetOutDev()->DrawOutDev( Point( aOutOrg.X(), aOffset.Height() ), aVDevSize, Point(), aVDevSize, *pVDev ); + bDrawn = true; + } + } + + if( !bDrawn ) + { + SetFont( aFont ); + GetOutDev()->DrawText( Point( aOutOrg.X() + aOffset.Width(), aOutOrg.Y() + aOffset.Height() ), aText ); + SetFont( aOldFont ); + } +} + +void ShowWindow::DrawEndScene() +{ + const vcl::Font aOldFont( GetFont() ); + vcl::Font aFont( GetSettings().GetStyleSettings().GetMenuFont() ); + + const Point aOutOrg( PixelToLogic( Point() ) ); + const Size aTextSize(OutputDevice::LogicToLogic(Size(0, 14), MapMode(MapUnit::MapPoint), GetMapMode())); + const OUString aText( SdResId( STR_PRES_SOFTEND ) ); + + aFont.SetFontSize( aTextSize ); + aFont.SetColor( COL_WHITE ); + aFont.SetCharSet( aOldFont.GetCharSet() ); + aFont.SetLanguage( aOldFont.GetLanguage() ); + SetFont( aFont ); + GetOutDev()->DrawText( Point( aOutOrg.X() + aTextSize.Height(), aOutOrg.Y() + aTextSize.Height() ), aText ); + SetFont( aOldFont ); +} + +IMPL_LINK( ShowWindow, PauseTimeoutHdl, Timer*, pTimer, void ) +{ + if( !( --mnPauseTimeout ) ) + RestartShow(); + else + { + DrawPauseScene( true ); + pTimer->Start(); + } +} + +IMPL_LINK_NOARG(ShowWindow, MouseTimeoutHdl, Timer *, void) +{ + if( mbMouseCursorHidden ) + { + // not enough mouse movements since first recording so + // cancel show mouse pointer for now + mnFirstMouseMove = 0; + } + else + { + // mouse has been idle too long, hide pointer + ShowPointer( false ); + mbMouseCursorHidden = true; + } +} + +IMPL_LINK( ShowWindow, EventHdl, VclWindowEvent&, rEvent, void ) +{ + if( mbMouseAutoHide ) + { + if (rEvent.GetId() == VclEventId::WindowShow) + { + maMouseTimer.SetTimeout( HIDE_MOUSE_TIMEOUT ); + maMouseTimer.Start(); + } + } +} + +void ShowWindow::DeleteWindowFromPaintView() +{ + if( mpViewShell->GetView() ) + mpViewShell->GetView()->DeleteWindowFromPaintView( GetOutDev() ); + + sal_uInt16 nChild = GetChildCount(); + while( nChild-- ) + GetChild( nChild )->Show( false ); +} + +void ShowWindow::AddWindowToPaintView() +{ + if( mpViewShell->GetView() ) + mpViewShell->GetView()->AddWindowToPaintView( GetOutDev(), nullptr ); + + sal_uInt16 nChild = GetChildCount(); + while( nChild-- ) + GetChild( nChild )->Show(); +} + +// Override the sd::Window's CreateAccessible to create a different accessible object +css::uno::Reference<css::accessibility::XAccessible> + ShowWindow::CreateAccessible() +{ + css::uno::Reference< css::accessibility::XAccessible > xAcc = GetAccessible(false); + if (xAcc) + { + return xAcc; + } + if (mpViewShell != nullptr) + { + xAcc = mpViewShell->CreateAccessibleDocumentView (this); + SetAccessible(xAcc); + return xAcc; + } + else + { + SAL_WARN("sd", "::sd::Window::CreateAccessible: no view shell"); + return vcl::Window::CreateAccessible (); + } +} +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/showwindow.hxx b/sd/source/ui/slideshow/showwindow.hxx new file mode 100644 index 000000000..f0f88228b --- /dev/null +++ b/sd/source/ui/slideshow/showwindow.hxx @@ -0,0 +1,110 @@ +/* -*- 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 . + */ + +#pragma once + +#include <rtl/ref.hxx> +#include <sal/types.h> +#include <vcl/timer.hxx> +#include <vcl/graph.hxx> + +#include <Window.hxx> + +namespace sd { + +class SlideshowImpl; + +#define SLIDE_NO_TIMEOUT SAL_MAX_INT32 + +enum ShowWindowMode +{ + SHOWWINDOWMODE_NORMAL = 0, + SHOWWINDOWMODE_PAUSE = 1, + SHOWWINDOWMODE_END = 2, + SHOWWINDOWMODE_BLANK = 3, + SHOWWINDOWMODE_PREVIEW = 4 +}; + +class ShowWindow + : public ::sd::Window +{ + +public: + ShowWindow ( const ::rtl::Reference< ::sd::SlideshowImpl >& xController, vcl::Window* pParent ); + virtual ~ShowWindow() override; + virtual void dispose() override; + + void SetEndMode(); + bool SetPauseMode( sal_Int32 nTimeoutSec, Graphic const * pLogo = nullptr ); + bool SetBlankMode( sal_Int32 nPageIndexToRestart, const Color& rBlankColor ); + + const Color& GetBlankColor() const { return maShowBackground.GetColor(); } + + void SetPreviewMode(); + + void SetMouseAutoHide( bool bMouseAutoHide ) { mbMouseAutoHide = bMouseAutoHide; } + + ShowWindowMode GetShowWindowMode() const { return meShowWindowMode; } + + void RestartShow( sal_Int32 nPageIndexToRestart ); + + virtual void LoseFocus() override; + + virtual void KeyInput(const KeyEvent& rKEvt) override; + virtual void MouseMove(const MouseEvent& rMEvt) override; + virtual void MouseButtonUp(const MouseEvent& rMEvt) override; + virtual void MouseButtonDown(const MouseEvent& rMEvt) override; + virtual void Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect) override; + /// Override the sd::Window's CreateAccessible to create a different accessible object + virtual css::uno::Reference<css::accessibility::XAccessible> + CreateAccessible() override; + + void TerminateShow(); + void RestartShow(); + +private: + void DrawPauseScene( bool bTimeoutOnly ); + void DrawEndScene(); + + void DeleteWindowFromPaintView(); + void AddWindowToPaintView(); + +private: + Timer maPauseTimer; + Timer maMouseTimer; + Wallpaper maShowBackground; + Graphic maLogo; + sal_uLong mnPauseTimeout; + sal_Int32 mnRestartPageIndex; + ShowWindowMode meShowWindowMode; + bool mbShowNavigatorAfterSpecialMode; + bool mbMouseAutoHide; + bool mbMouseCursorHidden; + sal_uInt64 mnFirstMouseMove; + + DECL_LINK( PauseTimeoutHdl, Timer*, void ); + DECL_LINK(MouseTimeoutHdl, Timer *, void); + DECL_LINK( EventHdl, VclWindowEvent&, void ); + + ::rtl::Reference< SlideshowImpl > mxController; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/slideshow.cxx b/sd/source/ui/slideshow/slideshow.cxx new file mode 100644 index 000000000..fa14c4de6 --- /dev/null +++ b/sd/source/ui/slideshow/slideshow.cxx @@ -0,0 +1,1191 @@ +/* -*- 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 <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/drawing/framework/XControllerManager.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/util/URL.hpp> + +#include <comphelper/propertyvalue.hxx> +#include <cppuhelper/supportsservice.hxx> + +#include <sal/log.hxx> +#include <vcl/svapp.hxx> +#include <vcl/wrkwin.hxx> +#include <svx/svdpool.hxx> +#include <svx/svdlayer.hxx> +#include <svl/itemprop.hxx> + +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/sfxsids.hrc> + +#include <framework/FrameworkHelper.hxx> +#include <comphelper/extract.hxx> + +#include <FrameView.hxx> +#include <createpresentation.hxx> +#include <unomodel.hxx> +#include <slideshow.hxx> +#include "slideshowimpl.hxx" +#include <sdattr.hrc> +#include <sdmod.hxx> +#include <FactoryIds.hxx> +#include <DrawDocShell.hxx> +#include <ViewShell.hxx> +#include <ViewShellBase.hxx> +#include "SlideShowRestarter.hxx" +#include <DrawController.hxx> +#include <PresentationViewShell.hxx> +#include <customshowlist.hxx> +#include <unopage.hxx> +#include <sdpage.hxx> +#include <cusshow.hxx> +#include <optsitem.hxx> +#include <strings.hrc> +#include <sdresid.hxx> + +using ::com::sun::star::presentation::XSlideShowController; +using ::sd::framework::FrameworkHelper; +using ::com::sun::star::awt::XWindow; +using namespace ::sd; +using namespace ::cppu; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::animations; +using namespace ::com::sun::star::drawing::framework; + +namespace { + /** This local version of the work window overrides DataChanged() so that it + can restart the slide show when a display is added or removed. + */ + class FullScreenWorkWindow : public WorkWindow + { + public: + FullScreenWorkWindow ( + const ::rtl::Reference<SlideShow>& rpSlideShow, + ViewShellBase* pViewShellBase) + : WorkWindow(nullptr, WB_HIDE | WB_CLIPCHILDREN), + mpRestarter(std::make_shared<SlideShowRestarter>(rpSlideShow, pViewShellBase)) + {} + + void Restart(bool bForce) + { + mpRestarter->Restart(bForce); + } + + virtual void DataChanged (const DataChangedEvent& rEvent) override + { + if (rEvent.GetType() == DataChangedEventType::DISPLAY) + Restart(false); + } + + private: + ::std::shared_ptr<SlideShowRestarter> mpRestarter; + }; +} + +static const SfxItemPropertyMapEntry* ImplGetPresentationPropertyMap() +{ + // NOTE: First member must be sorted + static const SfxItemPropertyMapEntry aPresentationPropertyMap_Impl[] = + { + { u"AllowAnimations", ATTR_PRESENT_ANIMATION_ALLOWED, cppu::UnoType<bool>::get(), 0, 0 }, + { u"CustomShow", ATTR_PRESENT_CUSTOMSHOW, ::cppu::UnoType<OUString>::get(), 0, 0 }, + { u"Display", ATTR_PRESENT_DISPLAY, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { u"FirstPage", ATTR_PRESENT_DIANAME, ::cppu::UnoType<OUString>::get(), 0, 0 }, + { u"IsAlwaysOnTop", ATTR_PRESENT_ALWAYS_ON_TOP, cppu::UnoType<bool>::get(), 0, 0 }, + { u"IsAutomatic", ATTR_PRESENT_MANUEL, cppu::UnoType<bool>::get(), 0, 0 }, + { u"IsEndless", ATTR_PRESENT_ENDLESS, cppu::UnoType<bool>::get(), 0, 0 }, + { u"IsFullScreen", ATTR_PRESENT_FULLSCREEN, cppu::UnoType<bool>::get(), 0, 0 }, + { u"IsShowAll", ATTR_PRESENT_ALL, cppu::UnoType<bool>::get(), 0, 0 }, + { u"IsMouseVisible", ATTR_PRESENT_MOUSE, cppu::UnoType<bool>::get(), 0, 0 }, + { u"IsShowLogo", ATTR_PRESENT_SHOW_PAUSELOGO, cppu::UnoType<bool>::get(), 0, 0 }, + { u"IsTransitionOnClick", ATTR_PRESENT_CHANGE_PAGE, cppu::UnoType<bool>::get(), 0, 0 }, + { u"Pause", ATTR_PRESENT_PAUSE_TIMEOUT, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { u"StartWithNavigator", ATTR_PRESENT_NAVIGATOR, cppu::UnoType<bool>::get(), 0, 0 }, + { u"UsePen", ATTR_PRESENT_PEN, cppu::UnoType<bool>::get(), 0, 0 }, + { u"", 0, css::uno::Type(), 0, 0 } + }; + + return aPresentationPropertyMap_Impl; +} + + +SlideShow::SlideShow( SdDrawDocument* pDoc ) +: maPropSet(ImplGetPresentationPropertyMap(), SdrObject::GetGlobalDrawObjectItemPool()) +, mbIsInStartup(false) +, mpDoc( pDoc ) +, mpCurrentViewShellBase( nullptr ) +, mpFullScreenViewShellBase( nullptr ) +, mpFullScreenFrameView( nullptr ) +, mnInPlaceConfigEvent( nullptr ) +{ +} + +void SlideShow::ThrowIfDisposed() const +{ + if( mpDoc == nullptr ) + throw DisposedException(); +} + +/// used by the model to create a slideshow for it +rtl::Reference< SlideShow > SlideShow::Create( SdDrawDocument* pDoc ) +{ + return new SlideShow( pDoc ); +} + +rtl::Reference< SlideShow > SlideShow::GetSlideShow( SdDrawDocument const * pDocument ) +{ + rtl::Reference< SlideShow > xRet; + + if( pDocument ) + xRet = GetSlideShow( *pDocument ); + + return xRet; +} + +rtl::Reference< SlideShow > SlideShow::GetSlideShow( SdDrawDocument const & rDocument ) +{ + return rtl::Reference< SlideShow >( + dynamic_cast< SlideShow* >( rDocument.getPresentation().get() ) ); +} + +rtl::Reference< SlideShow > SlideShow::GetSlideShow( ViewShellBase const & rBase ) +{ + return GetSlideShow( rBase.GetDocument() ); +} + +css::uno::Reference< css::presentation::XSlideShowController > SlideShow::GetSlideShowController(ViewShellBase const & rBase ) +{ + rtl::Reference< SlideShow > xSlideShow( GetSlideShow( rBase ) ); + + Reference< XSlideShowController > xRet; + if( xSlideShow.is() ) + xRet = xSlideShow->getController(); + + return xRet; +} + +bool SlideShow::StartPreview( ViewShellBase const & rBase, + const css::uno::Reference< css::drawing::XDrawPage >& xDrawPage, + const css::uno::Reference< css::animations::XAnimationNode >& xAnimationNode ) +{ + rtl::Reference< SlideShow > xSlideShow( GetSlideShow( rBase ) ); + if( !xSlideShow.is() ) + return false; + + xSlideShow->startPreview( xDrawPage, xAnimationNode ); + return true; +} + +void SlideShow::Stop( ViewShellBase const & rBase ) +{ + rtl::Reference< SlideShow > xSlideShow( GetSlideShow( rBase ) ); + if( xSlideShow.is() ) + xSlideShow->end(); +} + +bool SlideShow::IsRunning( ViewShellBase const & rBase ) +{ + rtl::Reference< SlideShow > xSlideShow( GetSlideShow( rBase ) ); + return xSlideShow.is() && xSlideShow->isRunning(); +} + +bool SlideShow::IsRunning( const ViewShell& rViewShell ) +{ + rtl::Reference< SlideShow > xSlideShow( GetSlideShow( rViewShell.GetViewShellBase() ) ); + return xSlideShow.is() && xSlideShow->isRunning() && (xSlideShow->mxController->getViewShell() == &rViewShell); +} + +void SlideShow::CreateController( ViewShell* pViewSh, ::sd::View* pView, vcl::Window* pParentWindow ) +{ + SAL_INFO_IF( !mxController.is(), "sd.slideshow", "sd::SlideShow::CreateController(), clean up old controller first!" ); + + Reference< XPresentation2 > xThis( this ); + + rtl::Reference<SlideshowImpl> xController ( + new SlideshowImpl(xThis, pViewSh, pView, mpDoc, pParentWindow)); + + // Reset mbIsInStartup. From here mxController.is() is used to prevent + // multiple slide show instances for one document. + mxController = xController; + mbIsInStartup = false; + +} + +// XServiceInfo +OUString SAL_CALL SlideShow::getImplementationName( ) +{ + return "com.sun.star.comp.sd.SlideShow"; +} + +sal_Bool SAL_CALL SlideShow::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +Sequence< OUString > SAL_CALL SlideShow::getSupportedServiceNames( ) +{ + return { "com.sun.star.presentation.Presentation" }; +} + +// XPropertySet +Reference< XPropertySetInfo > SAL_CALL SlideShow::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static Reference< XPropertySetInfo > xInfo = maPropSet.getPropertySetInfo(); + return xInfo; + } + +void SAL_CALL SlideShow::setPropertyValue( const OUString& aPropertyName, const Any& aValue ) +{ + SolarMutexGuard aGuard; + ThrowIfDisposed(); + + sd::PresentationSettings& rPresSettings = mpDoc->getPresentationSettings(); + + const SfxItemPropertyMapEntry* pEntry = maPropSet.getPropertyMapEntry(aPropertyName); + + if( pEntry && ((pEntry->nFlags & PropertyAttribute::READONLY) != 0) ) + throw PropertyVetoException(); + + bool bValuesChanged = false; + bool bIllegalArgument = true; + + switch( pEntry ? pEntry->nWID : -1 ) + { + case ATTR_PRESENT_ALL: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + + if( rPresSettings.mbAll != bVal ) + { + rPresSettings.mbAll = bVal; + bValuesChanged = true; + if( bVal ) + rPresSettings.mbCustomShow = false; + } + } + break; + } + case ATTR_PRESENT_CHANGE_PAGE: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + + if( bVal == rPresSettings.mbLockedPages ) + { + bValuesChanged = true; + rPresSettings.mbLockedPages = !bVal; + } + } + break; + } + + case ATTR_PRESENT_ANIMATION_ALLOWED: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + + if(rPresSettings.mbAnimationAllowed != bVal) + { + bValuesChanged = true; + rPresSettings.mbAnimationAllowed = bVal; + } + } + break; + } + case ATTR_PRESENT_CUSTOMSHOW: + { + OUString aShowName; + if( aValue >>= aShowName ) + { + bIllegalArgument = false; + + SdCustomShowList* pCustomShowList = mpDoc->GetCustomShowList(); + if(pCustomShowList) + { + SdCustomShow* pCustomShow; + for( pCustomShow = pCustomShowList->First(); pCustomShow != nullptr; pCustomShow = pCustomShowList->Next() ) + { + if( pCustomShow->GetName() == aShowName ) + break; + } + + rPresSettings.mbCustomShow = true; + bValuesChanged = true; + } + } + break; + } + case ATTR_PRESENT_ENDLESS: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + + if( rPresSettings.mbEndless != bVal) + { + bValuesChanged = true; + rPresSettings.mbEndless = bVal; + } + } + break; + } + case ATTR_PRESENT_FULLSCREEN: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + if( rPresSettings.mbFullScreen != bVal) + { + bValuesChanged = true; + rPresSettings.mbFullScreen = bVal; + } + } + break; + } + case ATTR_PRESENT_DIANAME: + { + OUString aPresPage; + aValue >>= aPresPage; + bIllegalArgument = false; + if( (rPresSettings.maPresPage != aPresPage) || !rPresSettings.mbCustomShow || !rPresSettings.mbAll ) + { + bValuesChanged = true; + rPresSettings.maPresPage = getUiNameFromPageApiNameImpl(aPresPage); + rPresSettings.mbCustomShow = false; + rPresSettings.mbAll = false; + } + break; + } + case ATTR_PRESENT_MANUEL: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + + if( rPresSettings.mbManual != bVal) + { + bValuesChanged = true; + rPresSettings.mbManual = bVal; + } + } + break; + } + case ATTR_PRESENT_MOUSE: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + if( rPresSettings.mbMouseVisible != bVal) + { + bValuesChanged = true; + rPresSettings.mbMouseVisible = bVal; + } + } + break; + } + case ATTR_PRESENT_ALWAYS_ON_TOP: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + + if( rPresSettings.mbAlwaysOnTop != bVal) + { + bValuesChanged = true; + rPresSettings.mbAlwaysOnTop = bVal; + } + } + break; + } + case ATTR_PRESENT_NAVIGATOR: + bIllegalArgument = false; + //ignored, but exists in some older documents + break; + case ATTR_PRESENT_PEN: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + + if(rPresSettings.mbMouseAsPen != bVal) + { + bValuesChanged = true; + rPresSettings.mbMouseAsPen = bVal; + } + } + break; + } + case ATTR_PRESENT_PAUSE_TIMEOUT: + { + sal_Int32 nValue = 0; + if( (aValue >>= nValue) && (nValue >= 0) ) + { + bIllegalArgument = false; + if( rPresSettings.mnPauseTimeout != nValue ) + { + bValuesChanged = true; + rPresSettings.mnPauseTimeout = nValue; + } + } + break; + } + case ATTR_PRESENT_SHOW_PAUSELOGO: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + + if( rPresSettings.mbShowPauseLogo != bVal ) + { + bValuesChanged = true; + rPresSettings.mbShowPauseLogo = bVal; + } + } + break; + } + case ATTR_PRESENT_DISPLAY: + { + sal_Int32 nDisplay = 0; + if( aValue >>= nDisplay ) + { + bIllegalArgument = false; + + SdOptions* pOptions = SD_MOD()->GetSdOptions(DocumentType::Impress); + pOptions->SetDisplay( nDisplay ); + + FullScreenWorkWindow *pWin = dynamic_cast<FullScreenWorkWindow *>(GetWorkWindow()); + if( !pWin ) + return; + pWin->Restart(true); + } + break; + } + + default: + throw UnknownPropertyException( OUString::number(pEntry ? pEntry->nWID : -1), static_cast<cppu::OWeakObject*>(this)); + } + + if( bIllegalArgument ) + throw IllegalArgumentException(); + + if( bValuesChanged ) + mpDoc->SetChanged(); +} + +Any SAL_CALL SlideShow::getPropertyValue( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + ThrowIfDisposed(); + + const sd::PresentationSettings& rPresSettings = mpDoc->getPresentationSettings(); + + const SfxItemPropertyMapEntry* pEntry = maPropSet.getPropertyMapEntry(PropertyName); + + switch( pEntry ? pEntry->nWID : -1 ) + { + case ATTR_PRESENT_ALL: + return Any( !rPresSettings.mbCustomShow && rPresSettings.mbAll ); + case ATTR_PRESENT_CHANGE_PAGE: + return Any( !rPresSettings.mbLockedPages ); + case ATTR_PRESENT_ANIMATION_ALLOWED: + return Any( rPresSettings.mbAnimationAllowed ); + case ATTR_PRESENT_CUSTOMSHOW: + { + SdCustomShowList* pList = mpDoc->GetCustomShowList(); + SdCustomShow* pShow = (pList && rPresSettings.mbCustomShow) ? pList->GetCurObject() : nullptr; + OUString aShowName; + + if(pShow) + aShowName = pShow->GetName(); + + return Any( aShowName ); + } + case ATTR_PRESENT_ENDLESS: + return Any( rPresSettings.mbEndless ); + case ATTR_PRESENT_FULLSCREEN: + return Any( rPresSettings.mbFullScreen ); + case ATTR_PRESENT_DIANAME: + { + OUString aSlideName; + + if( !rPresSettings.mbCustomShow && !rPresSettings.mbAll ) + aSlideName = getPageApiNameFromUiName( rPresSettings.maPresPage ); + + return Any( aSlideName ); + } + case ATTR_PRESENT_MANUEL: + return Any( rPresSettings.mbManual ); + case ATTR_PRESENT_MOUSE: + return Any( rPresSettings.mbMouseVisible ); + case ATTR_PRESENT_ALWAYS_ON_TOP: + return Any( rPresSettings.mbAlwaysOnTop ); + case ATTR_PRESENT_NAVIGATOR: + return Any( false ); + case ATTR_PRESENT_PEN: + return Any( rPresSettings.mbMouseAsPen ); + case ATTR_PRESENT_PAUSE_TIMEOUT: + return Any( rPresSettings.mnPauseTimeout ); + case ATTR_PRESENT_SHOW_PAUSELOGO: + return Any( rPresSettings.mbShowPauseLogo ); + case ATTR_PRESENT_DISPLAY: + { + SdOptions* pOptions = SD_MOD()->GetSdOptions(DocumentType::Impress); + return Any(pOptions->GetDisplay()); + } + + default: + throw UnknownPropertyException( OUString::number(pEntry ? pEntry->nWID : -1), static_cast<cppu::OWeakObject*>(this)); + } +} + +void SAL_CALL SlideShow::addPropertyChangeListener( const OUString& , const Reference< XPropertyChangeListener >& ) +{ +} + +void SAL_CALL SlideShow::removePropertyChangeListener( const OUString& , const Reference< XPropertyChangeListener >& ) +{ +} + +void SAL_CALL SlideShow::addVetoableChangeListener( const OUString& , const Reference< XVetoableChangeListener >& ) +{ +} + +void SAL_CALL SlideShow::removeVetoableChangeListener( const OUString& , const Reference< XVetoableChangeListener >& ) +{ +} + +// XPresentation + +void SAL_CALL SlideShow::start() +{ + const Sequence< PropertyValue > aArguments; + startWithArguments( aArguments ); +} + +WorkWindow *SlideShow::GetWorkWindow() +{ + if( !mpFullScreenViewShellBase ) + return nullptr; + + PresentationViewShell* pShell = dynamic_cast<PresentationViewShell*>(mpFullScreenViewShellBase->GetMainViewShell().get()); + + if( !pShell || !pShell->GetViewFrame() ) + return nullptr; + + return dynamic_cast<WorkWindow*>(pShell->GetViewFrame()->GetFrame().GetWindow().GetParent()); +} + +bool SlideShow::IsExitAfterPresenting() const +{ + SolarMutexGuard aGuard; + ThrowIfDisposed(); + return mpDoc->IsExitAfterPresenting(); +} + +void SlideShow::SetExitAfterPresenting(bool bExit) +{ + SolarMutexGuard aGuard; + ThrowIfDisposed(); + mpDoc->SetExitAfterPresenting(bExit); +} + +void SAL_CALL SlideShow::end() +{ + SolarMutexGuard aGuard; + + // The mbIsInStartup flag should have been reset during the start of the + // slide show. Reset it here just in case that something has horribly + // gone wrong. + assert(!mbIsInStartup); + + rtl::Reference< SlideshowImpl > xController( mxController ); + if( !xController.is() ) + return; + + mxController.clear(); + + if( mpFullScreenFrameView ) + { + delete mpFullScreenFrameView; + mpFullScreenFrameView = nullptr; + } + + ViewShellBase* pFullScreenViewShellBase = mpFullScreenViewShellBase; + mpFullScreenViewShellBase = nullptr; + + // dispose before fullscreen window changes screens + // (potentially). If this needs to be moved behind + // pWorkWindow->StartPresentationMode() again, read issue + // pWorkWindow->i94007 & implement the solution outlined + // there. + xController->dispose(); + + if( pFullScreenViewShellBase ) + { + PresentationViewShell* pShell = dynamic_cast<PresentationViewShell*>(pFullScreenViewShellBase->GetMainViewShell().get()); + + if( pShell && pShell->GetViewFrame() ) + { + WorkWindow* pWorkWindow = dynamic_cast<WorkWindow*>(pShell->GetViewFrame()->GetFrame().GetWindow().GetParent()); + if( pWorkWindow ) + { + pWorkWindow->StartPresentationMode( (mxController.is() && mxController->maPresSettings.mbAlwaysOnTop) + ? PresentationFlags::HideAllApps : PresentationFlags::NONE ); + } + } + } + + if( pFullScreenViewShellBase ) + { + PresentationViewShell* pShell = nullptr; + { + // Get the shell pointer in its own scope to be sure that + // the shared_ptr to the shell is released before DoClose() + // is called. + ::std::shared_ptr<ViewShell> pSharedView (pFullScreenViewShellBase->GetMainViewShell()); + pShell = dynamic_cast<PresentationViewShell*>(pSharedView.get()); + } + if( pShell && pShell->GetViewFrame() ) + pShell->GetViewFrame()->DoClose(); + } + else if( mpCurrentViewShellBase ) + { + ViewShell* pViewShell = mpCurrentViewShellBase->GetMainViewShell().get(); + + if( pViewShell ) + { + FrameView* pFrameView = pViewShell->GetFrameView(); + + if( pFrameView && (pFrameView->GetPresentationViewShellId() != SID_VIEWSHELL0) ) + { + ViewShell::ShellType ePreviousType (pFrameView->GetPreviousViewShellType()); + pFrameView->SetPreviousViewShellType(ViewShell::ST_NONE); + + pFrameView->SetPresentationViewShellId(SID_VIEWSHELL0); + pFrameView->SetPreviousViewShellType(pViewShell->GetShellType()); + + framework::FrameworkHelper::Instance(*mpCurrentViewShellBase)->RequestView( + framework::FrameworkHelper::GetViewURL(ePreviousType), + framework::FrameworkHelper::msCenterPaneURL); + + pViewShell->GetViewFrame()->GetBindings().InvalidateAll( true ); + } + } + } + + if( mpCurrentViewShellBase ) + { + if (ViewShell* const pViewShell = mpCurrentViewShellBase->GetMainViewShell().get()) + { + // invalidate the view shell so the presentation slot will be re-enabled + // and the rehearsing will be updated + pViewShell->Invalidate(); + + if( xController->meAnimationMode ==ANIMATIONMODE_SHOW ) + { + // switch to the previously visible Slide + DrawViewShell* pDrawViewShell = dynamic_cast<DrawViewShell*>( pViewShell ); + if( pDrawViewShell ) + pDrawViewShell->SwitchPage( static_cast<sal_uInt16>(xController->getRestoreSlide()) ); + else + { + Reference<XDrawView> xDrawView ( + Reference<XWeak>(&mpCurrentViewShellBase->GetDrawController()), UNO_QUERY); + if (xDrawView.is()) + xDrawView->setCurrentPage( + Reference<XDrawPage>( + mpDoc->GetSdPage(xController->getRestoreSlide(), PageKind::Standard)->getUnoPage(), + UNO_QUERY)); + } + } + + if( pViewShell->GetDoc()->IsExitAfterPresenting() ) + { + pViewShell->GetDoc()->SetExitAfterPresenting( false ); + + Reference<frame::XDispatchProvider> xProvider(pViewShell->GetViewShellBase().GetController()->getFrame(), + UNO_QUERY); + if( xProvider.is() ) + { + util::URL aURL; + aURL.Complete = ".uno:CloseFrame"; + + uno::Reference< frame::XDispatch > xDispatch( + xProvider->queryDispatch( + aURL, OUString(), 0)); + if( xDispatch.is() ) + { + xDispatch->dispatch(aURL, + uno::Sequence< beans::PropertyValue >()); + } + } + } + + // In case mbMouseAsPen was set, a new layer DrawnInSlideshow might have been generated + // during slideshow, which is not known to FrameView yet. + if (any2bool(getPropertyValue("UsePen")) + && pViewShell->GetDoc()->GetLayerAdmin().GetLayer("DrawnInSlideshow")) + { + SdrLayerIDSet aDocLayerIDSet; + pViewShell->GetDoc()->GetLayerAdmin().getVisibleLayersODF(aDocLayerIDSet); + if (pViewShell->GetFrameView()->GetVisibleLayers() != aDocLayerIDSet) + { + pViewShell->GetFrameView()->SetVisibleLayers(aDocLayerIDSet); + } + pViewShell->GetDoc()->GetLayerAdmin().getPrintableLayersODF(aDocLayerIDSet); + if (pViewShell->GetFrameView()->GetPrintableLayers() != aDocLayerIDSet) + { + pViewShell->GetFrameView()->SetPrintableLayers(aDocLayerIDSet); + } + pViewShell->GetDoc()->GetLayerAdmin().getLockedLayersODF(aDocLayerIDSet); + if (pViewShell->GetFrameView()->GetLockedLayers() != aDocLayerIDSet) + { + pViewShell->GetFrameView()->SetLockedLayers(aDocLayerIDSet); + } + pViewShell->InvalidateWindows(); + } + + // Fire the acc focus event when focus is switched back. The above method + // mpCurrentViewShellBase->GetWindow()->GrabFocus() will set focus to WorkWindow + // instead of the sd::window, so here call Shell's method to fire the focus event + pViewShell->SwitchActiveViewFireFocus(); + } + } + mpCurrentViewShellBase = nullptr; +} + +void SAL_CALL SlideShow::rehearseTimings() +{ + Sequence< PropertyValue > aArguments{ comphelper::makePropertyValue("RehearseTimings", true) }; + startWithArguments( aArguments ); +} + +// XPresentation2 + +void SAL_CALL SlideShow::startWithArguments(const Sequence< PropertyValue >& rArguments) +{ + SolarMutexGuard aGuard; + ThrowIfDisposed(); + + // Stop a running show before starting a new one. + if( mxController.is() ) + { + assert(!mbIsInStartup); + end(); + } + else if (mbIsInStartup) + { + // We are already somewhere in process of starting a slide show but + // have not yet got to the point where mxController is set. There + // is not yet a slide show to end so return silently. + return; + } + + // Prevent multiple instance of the SlideShow class for one document. + mbIsInStartup = true; + + mxCurrentSettings = std::make_shared<PresentationSettingsEx>( mpDoc->getPresentationSettings() ); + mxCurrentSettings->SetArguments( rArguments ); + + // if there is no view shell base set, use the current one or the first using this document + if( mpCurrentViewShellBase == nullptr ) + { + // first check current + ::sd::ViewShellBase* pBase = ::sd::ViewShellBase::GetViewShellBase( SfxViewFrame::Current() ); + if( pBase && pBase->GetDocument() == mpDoc ) + { + mpCurrentViewShellBase = pBase; + } + else + { + // current is not ours, so get first from ours + mpCurrentViewShellBase = ::sd::ViewShellBase::GetViewShellBase( SfxViewFrame::GetFirst( mpDoc->GetDocSh() ) ); + } + } + + // #i118456# make sure TextEdit changes get pushed to model. + // mpDrawView is tested against NULL above already. + if(mpCurrentViewShellBase) + { + ViewShell* pViewShell = mpCurrentViewShellBase->GetMainViewShell().get(); + + if(pViewShell && pViewShell->GetView()) + { + pViewShell->GetView()->SdrEndTextEdit(); + } + } + + // Start either a full-screen or an in-place show. + if(mxCurrentSettings->mbFullScreen && !mxCurrentSettings->mbPreview) + StartFullscreenPresentation(); + else + StartInPlacePresentation(); + +} + +sal_Bool SAL_CALL SlideShow::isRunning( ) +{ + SolarMutexGuard aGuard; + return mxController.is() && mxController->isRunning(); +} + +Reference< XSlideShowController > SAL_CALL SlideShow::getController( ) +{ + ThrowIfDisposed(); + + return mxController; +} + +// XComponent + +void SlideShow::disposing(std::unique_lock<std::mutex>&) +{ + SolarMutexGuard aGuard; + + if( mnInPlaceConfigEvent ) + { + Application::RemoveUserEvent( mnInPlaceConfigEvent ); + mnInPlaceConfigEvent = nullptr; + } + + if( mxController.is() ) + { + mxController->dispose(); + mxController.clear(); + } + + mpCurrentViewShellBase = nullptr; + mpFullScreenViewShellBase = nullptr; + mpDoc = nullptr; +} + +void SlideShow::startPreview( const Reference< XDrawPage >& xDrawPage, const Reference< XAnimationNode >& xAnimationNode ) +{ + Sequence< PropertyValue > aArguments{ + comphelper::makePropertyValue("Preview", true), + comphelper::makePropertyValue("FirstPage", xDrawPage), + comphelper::makePropertyValue("AnimationNode", xAnimationNode), + comphelper::makePropertyValue("ParentWindow", Reference< XWindow >()), + }; + + startWithArguments( aArguments ); +} + +OutputDevice* SlideShow::getShowWindow() +{ + return mxController.is() ? mxController->mpShowWindow->GetOutDev() : nullptr; +} + +int SlideShow::getAnimationMode() const +{ + return mxController.is() ? mxController->meAnimationMode : ANIMATIONMODE_SHOW; +} + +void SlideShow::jumpToPageIndex( sal_Int32 nPageIndex ) +{ + if( mxController.is() ) + mxController->displaySlideIndex( nPageIndex ); +} + +void SlideShow::jumpToPageNumber( sal_Int32 nPageNumber ) +{ + if( mxController.is() ) + mxController->displaySlideNumber( nPageNumber ); +} + +sal_Int32 SlideShow::getCurrentPageNumber() const +{ + return mxController.is() ? mxController->getCurrentSlideNumber() : 0; +} + +void SlideShow::jumpToBookmark( const OUString& sBookmark ) +{ + if( mxController.is() ) + mxController->jumpToBookmark( sBookmark ); +} + +bool SlideShow::isFullScreen() const +{ + return mxController.is() && mxController->maPresSettings.mbFullScreen; +} + +void SlideShow::resize( const Size &rSize ) +{ + if( mxController.is() ) + mxController->resize( rSize ); +} + +bool SlideShow::activate( ViewShellBase& rBase ) +{ + if( (mpFullScreenViewShellBase == &rBase) && !mxController.is() ) + { + ::std::shared_ptr<PresentationViewShell> pShell = std::dynamic_pointer_cast<PresentationViewShell>(rBase.GetMainViewShell()); + if (pShell != nullptr) + { + pShell->FinishInitialization( mpFullScreenFrameView ); + mpFullScreenFrameView = nullptr; + + CreateController( pShell.get(), pShell->GetView(), rBase.GetViewWindow() ); + + if (!mxController->startShow(mxCurrentSettings.get())) + return false; + + pShell->Resize(); + // Defer the sd::ShowWindow's GrabFocus to here. so that the accessible event can be fired correctly. + pShell->GetActiveWindow()->GrabFocus(); + } + } + + if( mxController.is() ) + mxController->activate(); + + return true; +} + +void SlideShow::deactivate() +{ + mxController->deactivate(); +} + +bool SlideShow::keyInput(const KeyEvent& rKEvt) +{ + return mxController.is() && mxController->keyInput(rKEvt); +} + +void SlideShow::paint() +{ + if( mxController.is() ) + mxController->paint(); +} + +void SlideShow::pause( bool bPause ) +{ + if( mxController.is() ) + { + if( bPause ) + mxController->pause(); + else + mxController->resume(); + } +} + +bool SlideShow::swipe(const CommandSwipeData& rSwipeData) +{ + return mxController.is() && mxController->swipe(rSwipeData); +} + +bool SlideShow::longpress(const CommandLongPressData& rLongPressData) +{ + return mxController.is() && mxController->longpress(rLongPressData); +} + +void SlideShow::StartInPlacePresentationConfigurationCallback() +{ + if( mnInPlaceConfigEvent != nullptr ) + Application::RemoveUserEvent( mnInPlaceConfigEvent ); + + mnInPlaceConfigEvent = Application::PostUserEvent( LINK( this, SlideShow, StartInPlacePresentationConfigurationHdl ) ); +} + +IMPL_LINK_NOARG(SlideShow, StartInPlacePresentationConfigurationHdl, void*, void) +{ + mnInPlaceConfigEvent = nullptr; + StartInPlacePresentation(); +} + +void SlideShow::StartInPlacePresentation() +{ + if( mpCurrentViewShellBase ) + { + // Save the current view shell type so that it can be restored after the + // show has ended. If there already is a saved shell type then that is + // not overwritten. + + ViewShell::ShellType eShell = ViewShell::ST_NONE; + + ::std::shared_ptr<FrameworkHelper> pHelper(FrameworkHelper::Instance(*mpCurrentViewShellBase)); + ::std::shared_ptr<ViewShell> pMainViewShell(pHelper->GetViewShell(FrameworkHelper::msCenterPaneURL)); + + if( pMainViewShell ) + eShell = pMainViewShell->GetShellType(); + + if( eShell != ViewShell::ST_IMPRESS ) + { + // Switch temporary to a DrawViewShell which supports the in-place presentation. + + if( pMainViewShell ) + { + FrameView* pFrameView = pMainViewShell->GetFrameView(); + pFrameView->SetPresentationViewShellId(SID_VIEWSHELL1); + pFrameView->SetPreviousViewShellType (pMainViewShell->GetShellType()); + pFrameView->SetPageKind (PageKind::Standard); + } + + pHelper->RequestView( FrameworkHelper::msImpressViewURL, FrameworkHelper::msCenterPaneURL ); + pHelper->RunOnConfigurationEvent( + FrameworkHelper::msConfigurationUpdateEndEvent, + [this] (bool const) { return this->StartInPlacePresentationConfigurationCallback(); } ); + return; + } + else + { + vcl::Window* pParentWindow = mxCurrentSettings->mpParentWindow; + if( pParentWindow == nullptr ) + pParentWindow = mpCurrentViewShellBase->GetViewWindow(); + + CreateController( pMainViewShell.get(), pMainViewShell->GetView(), pParentWindow ); + } + } + else if( mxCurrentSettings->mpParentWindow ) + { + // no current view shell, but parent window + CreateController( nullptr, nullptr, mxCurrentSettings->mpParentWindow ); + } + + if( !mxController.is() ) + return; + + bool bSuccess = false; + if( mxCurrentSettings && mxCurrentSettings->mbPreview ) + { + bSuccess = mxController->startPreview(mxCurrentSettings->mxStartPage, mxCurrentSettings->mxAnimationNode, mxCurrentSettings->mpParentWindow ); + } + else + { + bSuccess = mxController->startShow(mxCurrentSettings.get()); + } + + if( !bSuccess ) + end(); + else + { + if( mpCurrentViewShellBase && ( !mxCurrentSettings || ( mxCurrentSettings && !mxCurrentSettings->mbPreview ) ) ) + mpCurrentViewShellBase->GetWindow()->GrabFocus(); + } +} + +void SlideShow::StartFullscreenPresentation( ) +{ + // Create the top level window in which the PresentationViewShell(Base) + // will be created. This is done here explicitly so that we can make it + // fullscreen. + const sal_Int32 nDisplay (GetDisplay()); + VclPtr<WorkWindow> pWorkWindow = VclPtr<FullScreenWorkWindow>::Create(this, mpCurrentViewShellBase); + pWorkWindow->SetBackground(Wallpaper(COL_BLACK)); + OUString Title(SdResId(STR_FULLSCREEN_SLIDESHOW)); + Title = Title.replaceFirst("%s", + mpCurrentViewShellBase->GetDocShell()->GetTitle(SFX_TITLE_DETECT)); + pWorkWindow->SetText(Title); + pWorkWindow->StartPresentationMode( true, mpDoc->getPresentationSettings().mbAlwaysOnTop ? PresentationFlags::HideAllApps : PresentationFlags::NONE, nDisplay); + // pWorkWindow->ShowFullScreenMode(sal_False, nDisplay); + + if (!pWorkWindow->IsVisible()) + return; + + // Initialize the new presentation view shell with a copy of the + // frame view of the current view shell. This avoids that + // changes made by the presentation have an effect on the other + // view shells. + FrameView* pOriginalFrameView = nullptr; + ::std::shared_ptr<ViewShell> xShell(mpCurrentViewShellBase->GetMainViewShell()); + if (xShell) + pOriginalFrameView = xShell->GetFrameView(); + + delete mpFullScreenFrameView; + mpFullScreenFrameView = new FrameView(mpDoc, pOriginalFrameView); + + // The new frame is created hidden. To make it visible and activate the + // new view shell--a prerequisite to process slot calls and initialize + // its panes--a GrabFocus() has to be called later on. + SfxFrame* pNewFrame = SfxFrame::CreateHidden( *mpDoc->GetDocSh(), *pWorkWindow, PRESENTATION_FACTORY_ID ); + pNewFrame->SetPresentationMode(true); + + mpFullScreenViewShellBase = static_cast<ViewShellBase*>(pNewFrame->GetCurrentViewFrame()->GetViewShell()); + if(mpFullScreenViewShellBase != nullptr) + { + // The following GrabFocus() is responsible for activating the + // new view shell. Without it the screen remains blank (under + // Windows and some Linux variants.) + mpFullScreenViewShellBase->GetWindow()->GrabFocus(); + } +} + +/// convert configuration setting display concept to real screens +sal_Int32 SlideShow::GetDisplay() +{ + sal_Int32 nDisplay = 0; + + SdOptions* pOptions = SD_MOD()->GetSdOptions(DocumentType::Impress); + if( pOptions ) + nDisplay = pOptions->GetDisplay(); + + if( nDisplay < 0 ) + nDisplay = -1; + else if( nDisplay == 0) + nDisplay = static_cast<sal_Int32>(Application::GetDisplayExternalScreen()); + else + nDisplay--; + + SAL_INFO("sd", "Presenting on real screen " << nDisplay); + + return nDisplay; +} + +bool SlideShow::dependsOn( ViewShellBase const * pViewShellBase ) +{ + return mxController.is() && (pViewShellBase == mpCurrentViewShellBase) && mpFullScreenViewShellBase; +} + +Reference< presentation::XPresentation2 > CreatePresentation( const SdDrawDocument& rDocument ) +{ + return Reference< presentation::XPresentation2 >( SlideShow::Create( const_cast< SdDrawDocument* >( &rDocument ) ) ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/slideshowimpl.cxx b/sd/source/ui/slideshow/slideshowimpl.cxx new file mode 100644 index 000000000..89a8ac95f --- /dev/null +++ b/sd/source/ui/slideshow/slideshowimpl.cxx @@ -0,0 +1,3349 @@ +/* -*- 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 <sal/config.h> + +#include <algorithm> + +#include <config_features.h> + +#include <com/sun/star/frame/theAutoRecovery.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/document/XEventsSupplier.hpp> +#include <com/sun/star/drawing/XMasterPageTarget.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/awt/SystemPointer.hpp> +#include <com/sun/star/util/URLTransformer.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> +#include <com/sun/star/frame/XDispatch.hpp> +#include <com/sun/star/frame/XLayoutManager.hpp> +#include <com/sun/star/presentation/SlideShow.hpp> +#include <com/sun/star/media/XPlayer.hpp> +#include <officecfg/Office/Common.hxx> +#include <svl/stritem.hxx> +#include <svl/urihelper.hxx> +#include <basic/sbstar.hxx> + +#include <toolkit/helper/vclunohelper.hxx> +#include <tools/diagnose_ex.h> + +#include <sfx2/infobar.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/app.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/svdoole2.hxx> +#include <svx/f3dchild.hxx> +#include <svx/imapdlg.hxx> +#include <svx/fontwork.hxx> +#include <svx/SvxColorChildWindow.hxx> +#include <svx/bmpmask.hxx> +#include <svx/srchdlg.hxx> +#include <svx/hyperdlg.hxx> +#include <svx/svxids.hrc> +#include <AnimationChildWindow.hxx> +#include <notifydocumentevent.hxx> +#include "slideshowimpl.hxx" +#include "slideshowviewimpl.hxx" +#include "PaneHider.hxx" + +#include <bitmaps.hlst> +#include <strings.hrc> +#include <sdresid.hxx> +#include <vcl/canvastools.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/weldutils.hxx> + +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <vcl/help.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertyvalue.hxx> +#include <rtl/ref.hxx> +#include <o3tl/safeint.hxx> +#include <o3tl/string_view.hxx> +#include <avmedia/mediawindow.hxx> +#include <svtools/colrdlg.hxx> +#include <DrawDocShell.hxx> +#include <ViewShellBase.hxx> +#include <PresentationViewShell.hxx> +#include <RemoteServer.hxx> +#include <customshowlist.hxx> +#include <unopage.hxx> +#include <sdpage.hxx> +#include <sdmod.hxx> +#include <app.hrc> +#include <cusshow.hxx> +#include <optsitem.hxx> + +#define CM_SLIDES 21 + +using ::com::sun::star::animations::XAnimationNode; +using ::com::sun::star::animations::XAnimationListener; +using ::com::sun::star::awt::XWindow; +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::document; +using namespace ::com::sun::star::presentation; +using namespace ::com::sun::star::beans; + +namespace sd +{ +/** Slots, which will be disabled in the slide show and are managed by Sfx. + Have to be sorted in the order of the SIDs */ +sal_uInt16 const pAllowed[] = +{ + SID_OPENDOC , // 5501 ///< that internally jumps work + SID_JUMPTOMARK , // 5598 + SID_OPENHYPERLINK , // 6676 + SID_PRESENTATION_END // 27218 +}; + +class AnimationSlideController +{ +public: + enum Mode { ALL, FROM, CUSTOM, PREVIEW }; + +public: + AnimationSlideController( Reference< XIndexAccess > const & xSlides, Mode eMode ); + + void setStartSlideNumber( sal_Int32 nSlideNumber ) { mnStartSlideNumber = nSlideNumber; } + sal_Int32 getStartSlideIndex() const; + + sal_Int32 getCurrentSlideNumber() const; + sal_Int32 getCurrentSlideIndex() const; + + sal_Int32 getSlideIndexCount() const { return maSlideNumbers.size(); } + sal_Int32 getSlideNumberCount() const { return mnSlideCount; } + + sal_Int32 getSlideNumber( sal_Int32 nSlideIndex ) const; + + void insertSlideNumber( sal_Int32 nSlideNumber, bool bVisible = true ); + void setPreviewNode( const Reference< XAnimationNode >& xPreviewNode ); + + bool jumpToSlideIndex( sal_Int32 nNewSlideIndex ); + bool jumpToSlideNumber( sal_Int32 nNewSlideIndex ); + + bool nextSlide(); + bool previousSlide(); + + void displayCurrentSlide( const Reference< XSlideShow >& xShow, + const Reference< XDrawPagesSupplier>& xDrawPages, + const bool bSkipAllMainSequenceEffects ); + + sal_Int32 getNextSlideIndex() const; + sal_Int32 getPreviousSlideIndex() const; + + bool isVisibleSlideNumber( sal_Int32 nSlideNumber ) const; + + Reference< XDrawPage > getSlideByNumber( sal_Int32 nSlideNumber ) const; + + sal_Int32 getNextSlideNumber() const; + + bool hasSlides() const { return !maSlideNumbers.empty(); } + +private: + bool getSlideAPI( sal_Int32 nSlideNumber, Reference< XDrawPage >& xSlide, Reference< XAnimationNode >& xAnimNode ); + sal_Int32 findSlideIndex( sal_Int32 nSlideNumber ) const; + + bool isValidIndex( sal_Int32 nIndex ) const { return (nIndex >= 0) && (o3tl::make_unsigned(nIndex) < maSlideNumbers.size()); } + bool isValidSlideNumber( sal_Int32 nSlideNumber ) const { return (nSlideNumber >= 0) && (nSlideNumber < mnSlideCount); } + +private: + Mode meMode; + sal_Int32 mnStartSlideNumber; + std::vector< sal_Int32 > maSlideNumbers; + std::vector< bool > maSlideVisible; + std::vector< bool > maSlideVisited; + Reference< XAnimationNode > mxPreviewNode; + sal_Int32 mnSlideCount; + sal_Int32 mnCurrentSlideIndex; + sal_Int32 mnHiddenSlideNumber; + Reference< XIndexAccess > mxSlides; +}; + +Reference< XDrawPage > AnimationSlideController::getSlideByNumber( sal_Int32 nSlideNumber ) const +{ + Reference< XDrawPage > xSlide; + if( mxSlides.is() && (nSlideNumber >= 0) && (nSlideNumber < mxSlides->getCount()) ) + mxSlides->getByIndex( nSlideNumber ) >>= xSlide; + return xSlide; +} + +bool AnimationSlideController::isVisibleSlideNumber( sal_Int32 nSlideNumber ) const +{ + sal_Int32 nIndex = findSlideIndex( nSlideNumber ); + + if( nIndex != -1 ) + return maSlideVisible[ nIndex ]; + else + return false; +} + +void AnimationSlideController::setPreviewNode( const Reference< XAnimationNode >& xPreviewNode ) +{ + mxPreviewNode = xPreviewNode; +} + +AnimationSlideController::AnimationSlideController( Reference< XIndexAccess > const & xSlides, Mode eMode ) +: meMode( eMode ) +, mnStartSlideNumber(-1) +, mnSlideCount( 0 ) +, mnCurrentSlideIndex(0) +, mnHiddenSlideNumber( -1 ) +, mxSlides( xSlides ) +{ + if( mxSlides.is() ) + mnSlideCount = xSlides->getCount(); +} + +sal_Int32 AnimationSlideController::getStartSlideIndex() const +{ + if( mnStartSlideNumber >= 0 ) + { + sal_Int32 nIndex; + const sal_Int32 nCount = maSlideNumbers.size(); + + for( nIndex = 0; nIndex < nCount; nIndex++ ) + { + if( maSlideNumbers[nIndex] == mnStartSlideNumber ) + return nIndex; + } + } + + return 0; +} + +sal_Int32 AnimationSlideController::getCurrentSlideNumber() const +{ + if( mnHiddenSlideNumber != -1 ) + return mnHiddenSlideNumber; + else if( !maSlideNumbers.empty() ) + return maSlideNumbers[mnCurrentSlideIndex]; + else + return 0; +} + +sal_Int32 AnimationSlideController::getCurrentSlideIndex() const +{ + if( mnHiddenSlideNumber != -1 ) + return -1; + else + return mnCurrentSlideIndex; +} + +bool AnimationSlideController::jumpToSlideIndex( sal_Int32 nNewSlideIndex ) +{ + if( isValidIndex( nNewSlideIndex ) ) + { + mnCurrentSlideIndex = nNewSlideIndex; + mnHiddenSlideNumber = -1; + maSlideVisited[mnCurrentSlideIndex] = true; + return true; + } + else + { + return false; + } +} + +bool AnimationSlideController::jumpToSlideNumber( sal_Int32 nNewSlideNumber ) +{ + sal_Int32 nIndex = findSlideIndex( nNewSlideNumber ); + if( isValidIndex( nIndex ) ) + { + return jumpToSlideIndex( nIndex ); + } + else if( (nNewSlideNumber >= 0) && (nNewSlideNumber < mnSlideCount) ) + { + // jump to a hidden slide + mnHiddenSlideNumber = nNewSlideNumber; + return true; + } + else + { + return false; + } +} + +sal_Int32 AnimationSlideController::getSlideNumber( sal_Int32 nSlideIndex ) const +{ + if( isValidIndex( nSlideIndex ) ) + return maSlideNumbers[nSlideIndex]; + else + return -1; +} + +void AnimationSlideController::insertSlideNumber( sal_Int32 nSlideNumber, bool bVisible /* = true */ ) +{ + DBG_ASSERT( isValidSlideNumber( nSlideNumber ), "sd::AnimationSlideController::insertSlideNumber(), illegal index" ); + if( isValidSlideNumber( nSlideNumber ) ) + { + maSlideNumbers.push_back( nSlideNumber ); + maSlideVisible.push_back( bVisible ); + maSlideVisited.push_back( false ); + } +} + +bool AnimationSlideController::getSlideAPI( sal_Int32 nSlideNumber, Reference< XDrawPage >& xSlide, Reference< XAnimationNode >& xAnimNode ) +{ + if( isValidSlideNumber( nSlideNumber ) ) try + { + xSlide.set( mxSlides->getByIndex(nSlideNumber), UNO_QUERY_THROW ); + + if( meMode == PREVIEW ) + { + xAnimNode = mxPreviewNode; + } + else + { + Reference< animations::XAnimationNodeSupplier > xAnimNodeSupplier( xSlide, UNO_QUERY_THROW ); + xAnimNode = xAnimNodeSupplier->getAnimationNode(); + } + + return true; + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::AnimationSlideController::getSlideAPI()" ); + } + + return false; +} + +sal_Int32 AnimationSlideController::findSlideIndex( sal_Int32 nSlideNumber ) const +{ + sal_Int32 nIndex; + const sal_Int32 nCount = maSlideNumbers.size(); + + for( nIndex = 0; nIndex < nCount; nIndex++ ) + { + if( maSlideNumbers[nIndex] == nSlideNumber ) + return nIndex; + } + + return -1; +} + +sal_Int32 AnimationSlideController::getNextSlideIndex() const +{ + switch( meMode ) + { + case ALL: + { + sal_Int32 nNewSlideIndex = mnCurrentSlideIndex + 1; + if( isValidIndex( nNewSlideIndex ) ) + { + // if the current slide is not excluded, make sure the + // next slide is also not excluded. + // if the current slide is excluded, we want to go + // to the next slide, even if this is also excluded. + if( maSlideVisible[mnCurrentSlideIndex] ) + { + while( isValidIndex( nNewSlideIndex ) ) + { + if( maSlideVisible[nNewSlideIndex] ) + break; + + nNewSlideIndex++; + } + } + } + return isValidIndex( nNewSlideIndex ) ? nNewSlideIndex : -1; + } + + case FROM: + case CUSTOM: + return mnHiddenSlideNumber == -1 ? mnCurrentSlideIndex + 1 : mnCurrentSlideIndex; + + default: + case PREVIEW: + return -1; + + } +} + +sal_Int32 AnimationSlideController::getNextSlideNumber() const +{ + sal_Int32 nNextSlideIndex = getNextSlideIndex(); + if( isValidIndex( nNextSlideIndex ) ) + { + return maSlideNumbers[nNextSlideIndex]; + } + else + { + return -1; + } +} + +bool AnimationSlideController::nextSlide() +{ + return jumpToSlideIndex( getNextSlideIndex() ); +} + +sal_Int32 AnimationSlideController::getPreviousSlideIndex() const +{ + sal_Int32 nNewSlideIndex = mnCurrentSlideIndex - 1; + + switch( meMode ) + { + case ALL: + { + // make sure the previous slide is visible + // or was already visited + while( isValidIndex( nNewSlideIndex ) ) + { + if( maSlideVisible[nNewSlideIndex] || maSlideVisited[nNewSlideIndex] ) + break; + + nNewSlideIndex--; + } + + break; + } + + case PREVIEW: + return -1; + + default: + break; + } + + return nNewSlideIndex; +} + +bool AnimationSlideController::previousSlide() +{ + return jumpToSlideIndex( getPreviousSlideIndex() ); +} + +void AnimationSlideController::displayCurrentSlide( const Reference< XSlideShow >& xShow, + const Reference< XDrawPagesSupplier>& xDrawPages, + const bool bSkipAllMainSequenceEffects ) +{ + const sal_Int32 nCurrentSlideNumber = getCurrentSlideNumber(); + + if( !(xShow.is() && (nCurrentSlideNumber != -1 )) ) + return; + + Reference< XDrawPage > xSlide; + Reference< XAnimationNode > xAnimNode; + ::std::vector<PropertyValue> aProperties; + + const sal_Int32 nNextSlideNumber = getNextSlideNumber(); + if( getSlideAPI( nNextSlideNumber, xSlide, xAnimNode ) ) + { + Sequence< Any > aValue{ Any(xSlide), Any(xAnimNode) }; + aProperties.emplace_back( "Prefetch" , + -1, + Any(aValue), + PropertyState_DIRECT_VALUE); + } + if (bSkipAllMainSequenceEffects) + { + // Add one property that prevents the slide transition from being + // shown (to speed up the transition to the previous slide) and + // one to show all main sequence effects so that the user can + // continue to undo effects. + aProperties.emplace_back( "SkipAllMainSequenceEffects", + -1, + Any(true), + PropertyState_DIRECT_VALUE); + aProperties.emplace_back("SkipSlideTransition", + -1, + Any(true), + PropertyState_DIRECT_VALUE); + } + + if( getSlideAPI( nCurrentSlideNumber, xSlide, xAnimNode ) ) + xShow->displaySlide( xSlide, xDrawPages, xAnimNode, comphelper::containerToSequence(aProperties) ); +} + +constexpr OUStringLiteral gsOnClick( u"OnClick" ); +constexpr OUStringLiteral gsBookmark( u"Bookmark" ); +constexpr OUStringLiteral gsVerb( u"Verb" ); + +SlideshowImpl::SlideshowImpl( const Reference< XPresentation2 >& xPresentation, ViewShell* pViewSh, ::sd::View* pView, SdDrawDocument* pDoc, vcl::Window* pParentWindow ) +: mxModel(pDoc->getUnoModel(),UNO_QUERY_THROW) +, maUpdateTimer("SlideShowImpl maUpdateTimer") +, maInputFreezeTimer("SlideShowImpl maInputFreezeTimer") +, maDeactivateTimer("SlideShowImpl maDeactivateTimer") +, mpView(pView) +, mpViewShell(pViewSh) +, mpDocSh(pDoc->GetDocSh()) +, mpDoc(pDoc) +, mpParentWindow(pParentWindow) +, mpShowWindow(nullptr) +, mnRestoreSlide(0) +, maPresSize( -1, -1 ) +, meAnimationMode(ANIMATIONMODE_SHOW) +, mpOldActiveWindow(nullptr) +, mnChildMask( 0 ) +, mbDisposed(false) +, mbAutoSaveWasOn(false) +, mbRehearseTimings(false) +, mbIsPaused(false) +, mbWasPaused(false) +, mbInputFreeze(false) +, mbActive(false) +, maPresSettings( pDoc->getPresentationSettings() ) +, mnUserPaintColor( 0x80ff0000L ) +, mbUsePen(false) +, mdUserPaintStrokeWidth ( 150.0 ) +, mnEndShowEvent(nullptr) +, mnContextMenuEvent(nullptr) +, mxPresentation( xPresentation ) +{ + if( mpViewShell ) + mpOldActiveWindow = mpViewShell->GetActiveWindow(); + + maUpdateTimer.SetInvokeHandler(LINK(this, SlideshowImpl, updateHdl)); + // Priority must not be too high or we'll starve input handling etc. + maUpdateTimer.SetPriority(TaskPriority::REPAINT); + + maDeactivateTimer.SetInvokeHandler(LINK(this, SlideshowImpl, deactivateHdl)); + maDeactivateTimer.SetTimeout( 20 ); + + maInputFreezeTimer.SetInvokeHandler( LINK( this, SlideshowImpl, ReadyForNextInputHdl ) ); + maInputFreezeTimer.SetTimeout( 20 ); + + // no autosave during show + if( officecfg::Office::Common::Save::Document::AutoSave::get() ) + mbAutoSaveWasOn = true; + + Application::AddEventListener( LINK( this, SlideshowImpl, EventListenerHdl ) ); + + mbUsePen = maPresSettings.mbMouseAsPen; + + SdOptions* pOptions = SD_MOD()->GetSdOptions(DocumentType::Impress); + if( pOptions ) + { + mnUserPaintColor = pOptions->GetPresentationPenColor(); + mdUserPaintStrokeWidth = pOptions->GetPresentationPenWidth(); + } +} + +SlideshowImpl::~SlideshowImpl() +{ + SdModule *pModule = SD_MOD(); + //rhbz#806663 SlideshowImpl can outlive SdModule + SdOptions* pOptions = pModule ? + pModule->GetSdOptions(DocumentType::Impress) : nullptr; + if( pOptions ) + { + pOptions->SetPresentationPenColor(mnUserPaintColor); + pOptions->SetPresentationPenWidth(mdUserPaintStrokeWidth); + } + + Application::RemoveEventListener( LINK( this, SlideshowImpl, EventListenerHdl ) ); + + maDeactivateTimer.Stop(); + + if( !mbDisposed ) + { + OSL_FAIL("SlideshowImpl::~SlideshowImpl(), component was not disposed!"); + std::unique_lock g(m_aMutex); + disposing(g); + } +} + +void SlideshowImpl::disposing(std::unique_lock<std::mutex>&) +{ +#ifdef ENABLE_SDREMOTE + RemoteServer::presentationStopped(); +#endif + if( mxShow.is() && mpDoc ) + NotifyDocumentEvent( + *mpDoc, + "OnEndPresentation" ); + + if( mbAutoSaveWasOn ) + setAutoSaveState( true ); + + if( mnEndShowEvent ) + Application::RemoveUserEvent( mnEndShowEvent ); + if( mnContextMenuEvent ) + Application::RemoveUserEvent( mnContextMenuEvent ); + + maInputFreezeTimer.Stop(); + + SolarMutexGuard aSolarGuard; + + if( !mxShow.is() ) + return; + + if( mxPresentation.is() ) + mxPresentation->end(); + + maUpdateTimer.Stop(); + + removeShapeEvents(); + + if( mxListenerProxy.is() ) + mxListenerProxy->removeAsSlideShowListener(); + + try + { + if( mxView.is() ) + mxShow->removeView( mxView ); + + Reference< XComponent > xComponent( mxShow, UNO_QUERY ); + if( xComponent.is() ) + xComponent->dispose(); + + if( mxView.is() ) + mxView->dispose(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::stop()" ); + } + + mxShow.clear(); + mxView.clear(); + mxListenerProxy.clear(); + mpSlideController.reset(); + + // take DrawView from presentation window, but give the old window back + if( mpShowWindow && mpView ) + mpView->DeleteWindowFromPaintView( mpShowWindow->GetOutDev() ); + + if( mpView ) + mpView->SetAnimationPause( false ); + + if( mpViewShell ) + { + mpViewShell->SetActiveWindow(mpOldActiveWindow); + if (mpShowWindow) + mpShowWindow->SetViewShell( nullptr ); + } + + if( mpView ) + mpView->InvalidateAllWin(); + + if( maPresSettings.mbFullScreen ) + { +#if HAVE_FEATURE_SCRIPTING + // restore StarBASICErrorHdl + StarBASIC::SetGlobalErrorHdl(maStarBASICGlobalErrorHdl); + maStarBASICGlobalErrorHdl = Link<StarBASIC*,bool>(); +#endif + } + else + { + if( mpShowWindow ) + mpShowWindow->Hide(); + } + + if( meAnimationMode == ANIMATIONMODE_SHOW ) + { + mpDocSh->SetSlotFilter(); + mpDocSh->ApplySlotFilter(); + + Help::EnableContextHelp(); + Help::EnableExtHelp(); + + showChildWindows(); + mnChildMask = 0; + } + + // show current window again + if( mpViewShell && dynamic_cast< PresentationViewShell *>( mpViewShell ) == nullptr) + { + if( meAnimationMode == ANIMATIONMODE_SHOW ) + { + mpViewShell->GetViewShellBase().ShowUIControls (true); + mpPaneHider.reset(); + } + else if( meAnimationMode == ANIMATIONMODE_PREVIEW ) + { + mpViewShell->ShowUIControls(true); + } + } + + if( mpShowWindow ) + mpShowWindow->Hide(); + mpShowWindow.disposeAndClear(); + + if ( mpViewShell ) + { + if( meAnimationMode == ANIMATIONMODE_SHOW ) + { + ::sd::Window* pActWin = mpViewShell->GetActiveWindow(); + + if (pActWin) + { + Size aVisSizePixel = pActWin->GetOutputSizePixel(); + ::tools::Rectangle aVisAreaWin = pActWin->PixelToLogic( ::tools::Rectangle( Point(0,0), aVisSizePixel) ); + mpViewShell->VisAreaChanged(aVisAreaWin); + if (mpView) + mpView->VisAreaChanged(pActWin->GetOutDev()); + pActWin->GrabFocus(); + } + } + + // restart the custom show dialog if he started us + if( mpViewShell->IsStartShowWithDialog() && getDispatcher() ) + { + mpViewShell->SetStartShowWithDialog( false ); + getDispatcher()->Execute( SID_CUSTOMSHOW_DLG, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD ); + } + + mpViewShell->GetViewShellBase().UpdateBorder(true); + } + + if( mpShowWindow ) + { + mpShowWindow.disposeAndClear(); + } + + setActiveXToolbarsVisible( true ); + + mbDisposed = true; +} + +bool SlideshowImpl::startPreview( + const Reference< XDrawPage >& xDrawPage, + const Reference< XAnimationNode >& xAnimationNode, + vcl::Window * pParent ) +{ + bool bRet = false; + + try + { + const Reference<lang::XServiceInfo> xServiceInfo( xDrawPage, UNO_QUERY ); + if (xServiceInfo.is()) { + const Sequence<OUString> supportedServices( + xServiceInfo->getSupportedServiceNames() ); + if (comphelper::findValue(supportedServices, "com.sun.star.drawing.MasterPage") != -1) { + OSL_FAIL("sd::SlideshowImpl::startPreview() " + "not allowed on master page!"); + return false; + } + } + + mxPreviewDrawPage = xDrawPage; + mxPreviewAnimationNode = xAnimationNode; + meAnimationMode = ANIMATIONMODE_PREVIEW; + + maPresSettings.mbAll = false; + maPresSettings.mbEndless = false; + maPresSettings.mbCustomShow = false; + maPresSettings.mbManual = false; + maPresSettings.mbMouseVisible = false; + maPresSettings.mbMouseAsPen = false; + maPresSettings.mbLockedPages = false; + maPresSettings.mbAlwaysOnTop = false; + maPresSettings.mbFullScreen = false; + maPresSettings.mbAnimationAllowed = true; + maPresSettings.mnPauseTimeout = 0; + maPresSettings.mbShowPauseLogo = false; + + Reference< XDrawPagesSupplier > xDrawPages( mpDoc->getUnoModel(), UNO_QUERY_THROW ); + Reference< XIndexAccess > xSlides( xDrawPages->getDrawPages(), UNO_QUERY_THROW ); + mpSlideController = std::make_shared<AnimationSlideController>( xSlides, AnimationSlideController::PREVIEW ); + + sal_Int32 nSlideNumber = 0; + Reference< XPropertySet > xSet( mxPreviewDrawPage, UNO_QUERY_THROW ); + xSet->getPropertyValue( "Number" ) >>= nSlideNumber; + mpSlideController->insertSlideNumber( nSlideNumber-1 ); + mpSlideController->setPreviewNode( xAnimationNode ); + + mpShowWindow = VclPtr<ShowWindow>::Create( this, ((pParent == nullptr) && mpViewShell) ? mpParentWindow.get() : pParent ); + if( mpViewShell ) + { + mpViewShell->SetActiveWindow( mpShowWindow ); + mpShowWindow->SetViewShell (mpViewShell); + mpViewShell->ShowUIControls (false); + } + + if( mpView ) + { + mpView->AddWindowToPaintView( mpShowWindow->GetOutDev(), nullptr ); + mpView->SetAnimationPause( true ); + } + + // call resize handler + if( pParent ) + { + maPresSize = pParent->GetSizePixel(); + } + else if( mpViewShell ) + { + ::tools::Rectangle aContentRect (mpViewShell->GetViewShellBase().getClientRectangle()); + if (AllSettings::GetLayoutRTL()) + { + aContentRect.SetLeft( aContentRect.Right() ); + aContentRect.AdjustRight(aContentRect.Right() ); + } + maPresSize = aContentRect.GetSize(); + mpShowWindow->SetPosPixel( aContentRect.TopLeft() ); + } + else + { + OSL_FAIL("sd::SlideshowImpl::startPreview(), I need either a parent window or a viewshell!"); + } + resize( maPresSize ); + + sal_Int32 nPropertyCount = 1; + if( mxPreviewAnimationNode.is() ) + nPropertyCount++; + + Sequence< beans::PropertyValue > aProperties(nPropertyCount); + auto pProperties = aProperties.getArray(); + pProperties[0].Name = "AutomaticAdvancement"; + pProperties[0].Value <<= 1.0; // one second timeout + + if( mxPreviewAnimationNode.is() ) + { + pProperties[1].Name = "NoSlideTransitions"; + pProperties[1].Value <<= true; + } + + bRet = startShowImpl( aProperties ); + + if( mpShowWindow != nullptr && meAnimationMode == ANIMATIONMODE_PREVIEW ) + mpShowWindow->SetPreviewMode(); + + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::startPreview()" ); + bRet = false; + } + + return bRet; +} + +bool SlideshowImpl::startShow( PresentationSettingsEx const * pPresSettings ) +{ + const rtl::Reference<SlideshowImpl> xKeepAlive(this); + + DBG_ASSERT( !mxShow.is(), "sd::SlideshowImpl::startShow(), called twice!" ); + if( mxShow.is() ) + return true; + DBG_ASSERT( mpParentWindow!=nullptr, "sd::SlideshowImpl::startShow() called without parent window" ); + if (mpParentWindow == nullptr) + return false; + + // Autoplay (pps/ppsx) + if (mpViewShell->GetDoc()->IsStartWithPresentation()){ + mpViewShell->GetDoc()->SetExitAfterPresenting(true); + } + + bool bRet = false; + + try + { + if( pPresSettings ) + { + maPresSettings = *pPresSettings; + mbRehearseTimings = pPresSettings->mbRehearseTimings; + } + + OUString aPresSlide( maPresSettings.maPresPage ); + SdPage* pStartPage = mpViewShell->GetActualPage(); + bool bStartWithActualSlide = pStartPage; + + // times should be measured? + if( mbRehearseTimings ) + { + maPresSettings.mbEndless = false; + maPresSettings.mbManual = true; + maPresSettings.mbMouseVisible = true; + maPresSettings.mbMouseAsPen = false; + maPresSettings.mnPauseTimeout = 0; + maPresSettings.mbShowPauseLogo = false; + } + + if( pStartPage ) + { + if( pStartPage->GetPageKind() == PageKind::Notes ) + { + // we are in notes page mode, so get + // the corresponding draw page + const sal_uInt16 nPgNum = ( pStartPage->GetPageNum() - 2 ) >> 1; + pStartPage = mpDoc->GetSdPage( nPgNum, PageKind::Standard ); + } + } + + if( bStartWithActualSlide ) + { + if ( aPresSlide.isEmpty()) + { + // no preset slide yet, so pick current on one + aPresSlide = pStartPage->GetName(); + // if the starting slide is hidden, we can't set slide controller to ALL mode + maPresSettings.mbAll = !pStartPage->IsExcluded(); + } + + if( meAnimationMode != ANIMATIONMODE_SHOW ) + { + if( pStartPage->GetPageKind() == PageKind::Standard ) + { + maPresSettings.mbAll = false; + } + } + } + + // build page list + createSlideList( maPresSettings.mbAll, aPresSlide ); + + // remember Slide number from where the show was started + if( pStartPage ) + mnRestoreSlide = ( pStartPage->GetPageNum() - 1 ) / 2; + + if( mpSlideController->hasSlides() ) + { + // hide child windows + hideChildWindows(); + + mpShowWindow = VclPtr<ShowWindow>::Create( this, mpParentWindow ); + mpShowWindow->SetMouseAutoHide( !maPresSettings.mbMouseVisible ); + mpViewShell->SetActiveWindow( mpShowWindow ); + mpShowWindow->SetViewShell (mpViewShell); + mpViewShell->GetViewShellBase().ShowUIControls (false); + // Hide the side panes for in-place presentations. + if ( ! maPresSettings.mbFullScreen) + mpPaneHider.reset(new PaneHider(*mpViewShell,this)); + + // these Slots are forbidden in other views for this document + if( mpDocSh ) + { + mpDocSh->SetSlotFilter( true, pAllowed ); + mpDocSh->ApplySlotFilter(); + } + + Help::DisableContextHelp(); + Help::DisableExtHelp(); + + if( maPresSettings.mbFullScreen ) + { +#if HAVE_FEATURE_SCRIPTING + // disable basic ide error handling + maStarBASICGlobalErrorHdl = StarBASIC::GetGlobalErrorHdl(); + StarBASIC::SetGlobalErrorHdl( Link<StarBASIC*,bool>() ); +#endif + } + + // call resize handler + maPresSize = mpParentWindow->GetSizePixel(); + if (!maPresSettings.mbFullScreen) + { + const ::tools::Rectangle& aClientRect = mpViewShell->GetViewShellBase().getClientRectangle(); + maPresSize = aClientRect.GetSize(); + mpShowWindow->SetPosPixel( aClientRect.TopLeft() ); + resize( maPresSize ); + } + + // #i41824# + // Note: In FullScreen Mode the OS (window manager) sends a resize to + // the WorkWindow once it actually resized it to full size. The + // WorkWindow propagates the resize to the DrawViewShell which calls + // resize() at the SlideShow (this). Calling resize here results in a + // temporary display of a black window in the window's default size + + if( mpView ) + { + mpView->AddWindowToPaintView( mpShowWindow->GetOutDev(), nullptr ); + mpView->SetAnimationPause( true ); + } + + SfxBindings* pBindings = getBindings(); + if( pBindings ) + { + pBindings->Invalidate( SID_PRESENTATION ); + pBindings->Invalidate( SID_REHEARSE_TIMINGS ); + } + + // Defer the sd::ShowWindow's GrabFocus to SlideShow::activate. so that the accessible event can be fired correctly. + //mpShowWindow->GrabFocus(); + + std::vector<beans::PropertyValue> aProperties; + aProperties.reserve( 4 ); + + aProperties.emplace_back( "AdvanceOnClick" , + -1, Any( !maPresSettings.mbLockedPages ), + beans::PropertyState_DIRECT_VALUE ); + + aProperties.emplace_back( "ImageAnimationsAllowed" , + -1, Any( maPresSettings.mbAnimationAllowed ), + beans::PropertyState_DIRECT_VALUE ); + + const bool bZOrderEnabled( + SD_MOD()->GetSdOptions( mpDoc->GetDocumentType() )->IsSlideshowRespectZOrder() ); + aProperties.emplace_back( "DisableAnimationZOrder" , + -1, Any( !bZOrderEnabled ), + beans::PropertyState_DIRECT_VALUE ); + + aProperties.emplace_back( "ForceManualAdvance" , + -1, Any( maPresSettings.mbManual ), + beans::PropertyState_DIRECT_VALUE ); + + if( mbUsePen ) + { + aProperties.emplace_back( "UserPaintColor" , + // User paint color is black by default. + -1, Any( mnUserPaintColor ), + beans::PropertyState_DIRECT_VALUE ); + + aProperties.emplace_back( "UserPaintStrokeWidth" , + // User paint color is black by default. + -1, Any( mdUserPaintStrokeWidth ), + beans::PropertyState_DIRECT_VALUE ); + } + + if (mbRehearseTimings) { + aProperties.emplace_back( "RehearseTimings" , + -1, Any(true), beans::PropertyState_DIRECT_VALUE ); + } + + bRet = startShowImpl( Sequence<beans::PropertyValue>( + aProperties.data(), aProperties.size() ) ); + + } + + setActiveXToolbarsVisible( false ); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::startShow()" ); + bRet = false; + } + + return bRet; +} + +bool SlideshowImpl::startShowImpl( const Sequence< beans::PropertyValue >& aProperties ) +{ + try + { + mxShow.set( createSlideShow(), UNO_SET_THROW ); + + mxView = new SlideShowView( + *mpShowWindow, + mpDoc, + meAnimationMode, + this, + maPresSettings.mbFullScreen); + + // try add wait symbol to properties: + const Reference<rendering::XSpriteCanvas> xSpriteCanvas( + mxView->getCanvas() ); + if (xSpriteCanvas.is()) + { + BitmapEx waitSymbolBitmap(BMP_WAIT_ICON); + const Reference<rendering::XBitmap> xBitmap( + vcl::unotools::xBitmapFromBitmapEx( waitSymbolBitmap ) ); + if (xBitmap.is()) + { + mxShow->setProperty( + beans::PropertyValue( "WaitSymbolBitmap" , + -1, + Any( xBitmap ), + beans::PropertyState_DIRECT_VALUE ) ); + } + + BitmapEx pointerSymbolBitmap(BMP_POINTER_ICON); + const Reference<rendering::XBitmap> xPointerBitmap( + vcl::unotools::xBitmapFromBitmapEx( pointerSymbolBitmap ) ); + if (xPointerBitmap.is()) + { + mxShow->setProperty( + beans::PropertyValue( "PointerSymbolBitmap" , + -1, + Any( xPointerBitmap ), + beans::PropertyState_DIRECT_VALUE ) ); + } + } + + for( const auto& rProp : aProperties ) + mxShow->setProperty( rProp ); + + mxShow->addView( mxView ); + + mxListenerProxy.set( new SlideShowListenerProxy( this, mxShow ) ); + mxListenerProxy->addAsSlideShowListener(); + + NotifyDocumentEvent( + *mpDoc, + "OnStartPresentation"); + displaySlideIndex( mpSlideController->getStartSlideIndex() ); + + return true; + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::startShowImpl()" ); + return false; + } +} + +/** called only by the slideshow view when the first paint event occurs. + This actually starts the slideshow. */ +void SlideshowImpl::onFirstPaint() +{ + if( mpShowWindow ) + { + /* + mpShowWindow->SetBackground( Wallpaper( COL_BLACK ) ); + mpShowWindow->Erase(); + mpShowWindow->SetBackground(); + */ + } + + SolarMutexGuard aSolarGuard; + maUpdateTimer.SetTimeout( sal_uLong(100) ); + maUpdateTimer.Start(); +} + +void SlideshowImpl::paint() +{ + if( mxView.is() ) try + { + awt::PaintEvent aEvt; + // aEvt.UpdateRect = TODO + mxView->paint( aEvt ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::paint()" ); + } +} + +void SAL_CALL SlideshowImpl::addSlideShowListener( const Reference< XSlideShowListener >& xListener ) +{ + if( mxListenerProxy.is() ) + mxListenerProxy->addSlideShowListener( xListener ); +} + +void SAL_CALL SlideshowImpl::removeSlideShowListener( const Reference< XSlideShowListener >& xListener ) +{ + if( mxListenerProxy.is() ) + mxListenerProxy->removeSlideShowListener( xListener ); +} + +void SlideshowImpl::slideEnded(const bool bReverse) +{ + if (bReverse) + gotoPreviousSlide(true); + else + gotoNextSlide(); +} + +bool SlideshowImpl::swipe(const CommandSwipeData &rSwipeData) +{ + if (mbUsePen || mnContextMenuEvent) + return false; + double nVelocityX = rSwipeData.getVelocityX(); + // tdf#108475 make it swipe only if some reasonable movement was involved + if (fabs(nVelocityX) < 50) + return false; + if (nVelocityX > 0) + { + gotoPreviousSlide(); + } + else + { + gotoNextEffect(); + } + //a swipe is followed by a mouse up, tell the view to ignore that mouse up as we've reacted + //to the swipe instead + mxView->ignoreNextMouseReleased(); + return true; +} + +bool SlideshowImpl::longpress(const CommandLongPressData &rLongPressData) +{ + if (mnContextMenuEvent) + return false; + + maPopupMousePos = Point(rLongPressData.getX(), rLongPressData.getY()); + mnContextMenuEvent = Application::PostUserEvent( LINK( this, SlideshowImpl, ContextMenuHdl ) ); + + return true; +} + +void SlideshowImpl::removeShapeEvents() +{ + if( !(mxShow.is() && mxListenerProxy.is()) ) + return; + + try + { + for( const auto& rEntry : maShapeEventMap ) + { + mxListenerProxy->removeShapeEventListener( rEntry.first ); + mxShow->setShapeCursor( rEntry.first, awt::SystemPointer::ARROW ); + } + + maShapeEventMap.clear(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::removeShapeEvents()" ); + } +} + +void SlideshowImpl::registerShapeEvents(sal_Int32 nSlideNumber) +{ + if( nSlideNumber < 0 ) + return; + + try + { + Reference< XDrawPagesSupplier > xDrawPages( mxModel, UNO_QUERY_THROW ); + Reference< XIndexAccess > xPages( xDrawPages->getDrawPages(), UNO_QUERY_THROW ); + + Reference< XShapes > xDrawPage; + xPages->getByIndex(nSlideNumber) >>= xDrawPage; + + if( xDrawPage.is() ) + { + Reference< XMasterPageTarget > xMasterPageTarget( xDrawPage, UNO_QUERY ); + if( xMasterPageTarget.is() ) + { + Reference< XShapes > xMasterPage = xMasterPageTarget->getMasterPage(); + if( xMasterPage.is() ) + registerShapeEvents( xMasterPage ); + } + registerShapeEvents( xDrawPage ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::registerShapeEvents()" ); + } +} + +void SlideshowImpl::registerShapeEvents( Reference< XShapes > const & xShapes ) +{ + try + { + const sal_Int32 nShapeCount = xShapes->getCount(); + sal_Int32 nShape; + for( nShape = 0; nShape < nShapeCount; nShape++ ) + { + Reference< XShape > xShape; + xShapes->getByIndex( nShape ) >>= xShape; + + if( xShape.is() && xShape->getShapeType() == "com.sun.star.drawing.GroupShape" ) + { + Reference< XShapes > xSubShapes( xShape, UNO_QUERY ); + if( xSubShapes.is() ) + registerShapeEvents( xSubShapes ); + } + + Reference< XPropertySet > xSet( xShape, UNO_QUERY ); + if( !xSet.is() ) + continue; + + Reference< XPropertySetInfo > xSetInfo( xSet->getPropertySetInfo() ); + if( !xSetInfo.is() || !xSetInfo->hasPropertyByName( gsOnClick ) ) + continue; + + WrappedShapeEventImplPtr pEvent = std::make_shared<WrappedShapeEventImpl>(); + xSet->getPropertyValue( gsOnClick ) >>= pEvent->meClickAction; + + switch( pEvent->meClickAction ) + { + case ClickAction_PREVPAGE: + case ClickAction_NEXTPAGE: + case ClickAction_FIRSTPAGE: + case ClickAction_LASTPAGE: + case ClickAction_STOPPRESENTATION: + break; + case ClickAction_BOOKMARK: + if( xSetInfo->hasPropertyByName( gsBookmark ) ) + xSet->getPropertyValue( gsBookmark ) >>= pEvent->maStrBookmark; + if( getSlideNumberForBookmark( pEvent->maStrBookmark ) == -1 ) + continue; + break; + case ClickAction_DOCUMENT: + case ClickAction_SOUND: + case ClickAction_PROGRAM: + case ClickAction_MACRO: + if( xSetInfo->hasPropertyByName( gsBookmark ) ) + xSet->getPropertyValue( gsBookmark ) >>= pEvent->maStrBookmark; + break; + case ClickAction_VERB: + if( xSetInfo->hasPropertyByName( gsVerb ) ) + xSet->getPropertyValue( gsVerb ) >>= pEvent->mnVerb; + break; + default: + continue; // skip all others + } + + maShapeEventMap[ xShape ] = pEvent; + + if( mxListenerProxy.is() ) + mxListenerProxy->addShapeEventListener( xShape ); + mxShow->setShapeCursor( xShape, awt::SystemPointer::REFHAND ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::registerShapeEvents()" ); + } +} + +void SlideshowImpl::displayCurrentSlide (const bool bSkipAllMainSequenceEffects) +{ + stopSound(); + removeShapeEvents(); + + if( mpSlideController && mxShow.is() ) + { + Reference< XDrawPagesSupplier > xDrawPages( mpDoc->getUnoModel(), + UNO_QUERY_THROW ); + mpSlideController->displayCurrentSlide( mxShow, xDrawPages, bSkipAllMainSequenceEffects ); + registerShapeEvents(mpSlideController->getCurrentSlideNumber()); + update(); + + } + // send out page change event and notify to update all acc info for current page + if (mpViewShell) + { + sal_Int32 currentPageIndex = getCurrentSlideIndex(); + mpViewShell->fireSwitchCurrentPage(currentPageIndex); + mpViewShell->NotifyAccUpdate(); + } +} + +void SlideshowImpl::endPresentation() +{ + if( maPresSettings.mbMouseAsPen) + { + Reference< XMultiServiceFactory > xDocFactory(mpDoc->getUnoModel(), UNO_QUERY ); + if( xDocFactory.is() ) + mxShow->registerUserPaintPolygons(xDocFactory); + } + + if( !mnEndShowEvent ) + mnEndShowEvent = Application::PostUserEvent( LINK(this, SlideshowImpl, endPresentationHdl) ); +} + +IMPL_LINK_NOARG(SlideshowImpl, endPresentationHdl, void*, void) +{ + mnEndShowEvent = nullptr; + + stopSound(); + + if( mxPresentation.is() ) + mxPresentation->end(); +} + +void SAL_CALL SlideshowImpl::pause() +{ + SolarMutexGuard aSolarGuard; + + if( mbIsPaused ) + return; + + try + { + mbIsPaused = true; + if( mxShow.is() ) + { + mxShow->pause(true); + + if( mxListenerProxy.is() ) + mxListenerProxy->paused(); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::pause()" ); + } +} + +void SAL_CALL SlideshowImpl::resume() +{ + SolarMutexGuard aSolarGuard; + + if( mbIsPaused ) try + { + if( mpShowWindow->GetShowWindowMode() == SHOWWINDOWMODE_BLANK || mpShowWindow->GetShowWindowMode() == SHOWWINDOWMODE_END ) + { + mpShowWindow->RestartShow(); + } + else + { + mbIsPaused = false; + if( mxShow.is() ) + { + mxShow->pause(false); + update(); + + if( mxListenerProxy.is() ) + mxListenerProxy->resumed(); + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::resume()" ); + } +#ifdef ENABLE_SDREMOTE + RemoteServer::presentationStarted( this ); +#endif +} + +sal_Bool SAL_CALL SlideshowImpl::isPaused() +{ + SolarMutexGuard aSolarGuard; + return mbIsPaused; +} + +void SAL_CALL SlideshowImpl::blankScreen( sal_Int32 nColor ) +{ + SolarMutexGuard aSolarGuard; + + if( mpShowWindow && mpSlideController ) + { + if( mpShowWindow->SetBlankMode( mpSlideController->getCurrentSlideIndex(), Color(ColorTransparency, nColor) ) ) + { + pause(); + } + } +} + +// XShapeEventListener + +void SlideshowImpl::click( const Reference< XShape >& xShape ) +{ + SolarMutexGuard aSolarGuard; + + WrappedShapeEventImplPtr pEvent = maShapeEventMap[xShape]; + if( !pEvent ) + return; + + switch( pEvent->meClickAction ) + { + case ClickAction_PREVPAGE: gotoPreviousSlide(); break; + case ClickAction_NEXTPAGE: gotoNextSlide(); break; + case ClickAction_FIRSTPAGE: gotoFirstSlide(); break; + case ClickAction_LASTPAGE: gotoLastSlide(); break; + case ClickAction_STOPPRESENTATION: endPresentation(); break; + case ClickAction_BOOKMARK: + { + gotoBookmark( pEvent->maStrBookmark ); + } + break; + case ClickAction_SOUND: + { +#if HAVE_FEATURE_AVMEDIA + try + { + mxPlayer.set(avmedia::MediaWindow::createPlayer(pEvent->maStrBookmark, ""/*TODO?*/), uno::UNO_SET_THROW ); + mxPlayer->start(); + } + catch( uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::click()" ); + } +#endif + } + break; + + case ClickAction_DOCUMENT: + { + OUString aBookmark( pEvent->maStrBookmark ); + + sal_Int32 nPos = aBookmark.indexOf( '#' ); + if( nPos >= 0 ) + { + OUString aURL( aBookmark.copy( 0, nPos+1 ) ); + OUString aName( aBookmark.copy( nPos+1 ) ); + aURL += getUiNameFromPageApiNameImpl( aName ); + aBookmark = aURL; + } + + mpDocSh->OpenBookmark( aBookmark ); + } + break; + + case ClickAction_PROGRAM: + { + INetURLObject aURL( + ::URIHelper::SmartRel2Abs( + INetURLObject(mpDocSh->GetMedium()->GetBaseURL()), + pEvent->maStrBookmark, ::URIHelper::GetMaybeFileHdl(), true, + false, INetURLObject::EncodeMechanism::WasEncoded, + INetURLObject::DecodeMechanism::Unambiguous ) ); + + if( INetProtocol::File == aURL.GetProtocol() ) + { + SfxStringItem aUrl( SID_FILE_NAME, aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + SfxBoolItem aBrowsing( SID_BROWSE, true ); + + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + if (pViewFrm) + { + SfxUnoFrameItem aDocFrame(SID_FILLFRAME, pViewFrm->GetFrame().GetFrameInterface()); + pViewFrm->GetDispatcher()->ExecuteList( SID_OPENDOC, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aUrl, &aBrowsing }, { &aDocFrame } ); + } + } + } + break; + +#if HAVE_FEATURE_SCRIPTING + case presentation::ClickAction_MACRO: + { + const OUString aMacro( pEvent->maStrBookmark ); + + if ( SfxApplication::IsXScriptURL( aMacro ) ) + { + Any aRet; + Sequence< sal_Int16 > aOutArgsIndex; + Sequence< Any > aOutArgs; + Sequence< Any >* pInArgs = new Sequence< Any >(0); + mpDocSh->CallXScript( aMacro, *pInArgs, aRet, aOutArgsIndex, aOutArgs); + } + else + { + // aMacro has the following syntax: + // "Macroname.Modulname.Libname.Documentname" or + // "Macroname.Modulname.Libname.Applicationname" + sal_Int32 nIdx{ 0 }; + const std::u16string_view aMacroName = o3tl::getToken(aMacro, 0, '.', nIdx); + const std::u16string_view aModulName = o3tl::getToken(aMacro, 0, '.', nIdx); + + // todo: is the limitation still given that only + // Modulname+Macroname can be used here? + OUString aExecMacro = OUString::Concat(aModulName) + "." + aMacroName; + mpDocSh->GetBasic()->Call(aExecMacro); + } + } + break; +#endif + + case ClickAction_VERB: + { + // todo, better do it async? + SdrObject* pObj = SdrObject::getSdrObjectFromXShape(xShape); + SdrOle2Obj* pOleObject = dynamic_cast< SdrOle2Obj* >(pObj); + if (pOleObject && mpViewShell ) + mpViewShell->ActivateObject(pOleObject, pEvent->mnVerb); + } + break; + default: + break; + } +} + +sal_Int32 SlideshowImpl::getSlideNumberForBookmark( const OUString& rStrBookmark ) +{ + bool bIsMasterPage; + OUString aBookmark = getUiNameFromPageApiNameImpl( rStrBookmark ); + sal_uInt16 nPgNum = mpDoc->GetPageByName( aBookmark, bIsMasterPage ); + + if( nPgNum == SDRPAGE_NOTFOUND ) + { + // Is the bookmark an object? + SdrObject* pObj = mpDoc->GetObj( aBookmark ); + + if( pObj ) + { + nPgNum = pObj->getSdrPageFromSdrObject()->GetPageNum(); + bIsMasterPage = pObj->getSdrPageFromSdrObject()->IsMasterPage(); + } + } + + if( (nPgNum == SDRPAGE_NOTFOUND) || bIsMasterPage || static_cast<SdPage*>(mpDoc->GetPage(nPgNum))->GetPageKind() != PageKind::Standard ) + return -1; + + return ( nPgNum - 1) >> 1; +} + +void SlideshowImpl::hyperLinkClicked( OUString const& aHyperLink ) +{ + OUString aBookmark( aHyperLink ); + + sal_Int32 nPos = aBookmark.indexOf( '#' ); + if( nPos >= 0 ) + { + OUString aURL( aBookmark.copy( 0, nPos+1 ) ); + OUString aName( aBookmark.copy( nPos+1 ) ); + aURL += getUiNameFromPageApiNameImpl( aName ); + aBookmark = aURL; + } + + mpDocSh->OpenBookmark( aBookmark ); +} + +void SlideshowImpl::displaySlideNumber( sal_Int32 nSlideNumber ) +{ + if( mpSlideController ) + { + if( mpSlideController->jumpToSlideNumber( nSlideNumber ) ) + { + displayCurrentSlide(); + } + } +} + +/** nSlideIndex == -1 displays current slide again */ +void SlideshowImpl::displaySlideIndex( sal_Int32 nSlideIndex ) +{ + if( mpSlideController ) + { + if( (nSlideIndex == -1) || mpSlideController->jumpToSlideIndex( nSlideIndex ) ) + { + displayCurrentSlide(); + } + } +} + +void SlideshowImpl::jumpToBookmark( const OUString& sBookmark ) +{ + sal_Int32 nSlideNumber = getSlideNumberForBookmark( sBookmark ); + if( nSlideNumber != -1 ) + displaySlideNumber( nSlideNumber ); +} + +sal_Int32 SlideshowImpl::getCurrentSlideNumber() const +{ + return mpSlideController ? mpSlideController->getCurrentSlideNumber() : -1; +} + +sal_Bool SAL_CALL SlideshowImpl::isEndless() +{ + SolarMutexGuard aSolarGuard; + return maPresSettings.mbEndless; +} + +void SlideshowImpl::update() +{ + startUpdateTimer(); +} + +void SlideshowImpl::startUpdateTimer() +{ + SolarMutexGuard aSolarGuard; + maUpdateTimer.SetTimeout( 0 ); + maUpdateTimer.Start(); +} + +/** this timer is called 20ms after a new slide was displayed. + This is used to unfreeze user input that was disabled after + slide change to skip input that was buffered during slide + transition preparation */ +IMPL_LINK_NOARG(SlideshowImpl, ReadyForNextInputHdl, Timer *, void) +{ + mbInputFreeze = false; +} + +/** if I catch someone someday who calls this method by hand + and not by using the timer, I will personally punish this + person seriously, even if this person is me. +*/ +IMPL_LINK_NOARG(SlideshowImpl, updateHdl, Timer *, void) +{ + updateSlideShow(); +} + +void SlideshowImpl::updateSlideShow() +{ + // prevent me from deletion when recursing (App::EnableYieldMode does) + const rtl::Reference<SlideshowImpl> xKeepAlive(this); + + Reference< XSlideShow > xShow( mxShow ); + if ( ! xShow.is()) + return; + + try + { + double fUpdate = 0.0; + if( !xShow->update(fUpdate) ) + fUpdate = -1.0; + + if (mxShow.is() && (fUpdate >= 0.0)) + { + if (::basegfx::fTools::equalZero(fUpdate)) + { + // Make sure idle tasks don't starve when we don't have to wait. + // Don't process any events generated after invoking the function. + Application::Reschedule(/*bHandleAllCurrentEvents=*/true); + } + else + { + // Avoid busy loop when the previous call to update() + // returns a small positive number but not 0 (which is + // handled above). Also, make sure that calls to update() + // have a minimum frequency. + // => Allow up to 60 frames per second. Call at least once + // every 4 seconds. + const static sal_Int32 nMaximumFrameCount (60); + const static double nMinimumTimeout (1.0 / nMaximumFrameCount); + const static double nMaximumTimeout (4.0); + fUpdate = std::clamp(fUpdate, nMinimumTimeout, nMaximumTimeout); + + // Make sure that the maximum frame count has not been set + // too high (only then conversion to milliseconds and long + // integer may lead to zero value.) + OSL_ASSERT(static_cast<sal_uLong>(fUpdate * 1000.0) > 0); + } + + // Use our high resolution timers for the asynchronous callback. + maUpdateTimer.SetTimeout(static_cast<sal_uLong>(fUpdate * 1000.0)); + maUpdateTimer.Start(); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::updateSlideShow()" ); + } +} + +bool SlideshowImpl::keyInput(const KeyEvent& rKEvt) +{ + if( !mxShow.is() || mbInputFreeze ) + return false; + + bool bRet = true; + + try + { + const int nKeyCode = rKEvt.GetKeyCode().GetCode(); + switch( nKeyCode ) + { + case awt::Key::CONTEXTMENU: + if( !mnContextMenuEvent ) + { + if( mpShowWindow ) + maPopupMousePos = mpShowWindow->GetPointerState().maPos; + mnContextMenuEvent = Application::PostUserEvent( LINK( this, SlideshowImpl, ContextMenuHdl ) ); + } + break; + + // cancel show + case KEY_ESCAPE: + case KEY_SUBTRACT: + // in case the user cancels the presentation, switch to current slide + // in edit mode + if( mpSlideController && (ANIMATIONMODE_SHOW == meAnimationMode) ) + { + if( mpSlideController->getCurrentSlideNumber() != -1 ) + mnRestoreSlide = mpSlideController->getCurrentSlideNumber(); + } + endPresentation(); + break; + + // advance show + case KEY_PAGEDOWN: + if(rKEvt.GetKeyCode().IsMod2()) + { + gotoNextSlide(); + break; + } + [[fallthrough]]; + case KEY_SPACE: + case KEY_RIGHT: + case KEY_DOWN: + gotoNextEffect(); + break; + + case KEY_RETURN: + { + if( !maCharBuffer.isEmpty() ) + { + if( mpSlideController ) + { + if( mpSlideController->jumpToSlideNumber( maCharBuffer.toInt32() - 1 ) ) + displayCurrentSlide(); + } + maCharBuffer.clear(); + } + else + { + gotoNextEffect(); + } + } + break; + + // numeric: add to buffer + case KEY_0: + case KEY_1: + case KEY_2: + case KEY_3: + case KEY_4: + case KEY_5: + case KEY_6: + case KEY_7: + case KEY_8: + case KEY_9: + maCharBuffer += OUStringChar( rKEvt.GetCharCode() ); + break; + + case KEY_PAGEUP: + if(rKEvt.GetKeyCode().IsMod2()) + { + gotoPreviousSlide(); + break; + } + [[fallthrough]]; + case KEY_LEFT: + case KEY_UP: + case KEY_BACKSPACE: + gotoPreviousEffect(); + break; + + case KEY_P: + setUsePen( !mbUsePen ); + break; + + // tdf#149351 Ctrl+A disables pointer as pen mode + case KEY_A: + if(rKEvt.GetKeyCode().IsMod1()) + { + setUsePen( false ); + break; + } + break; + + case KEY_E: + setEraseAllInk( true ); + updateSlideShow(); + break; + + case KEY_HOME: + gotoFirstSlide(); + break; + + case KEY_END: + gotoLastSlide(); + break; + + case KEY_B: + case KEY_W: + case KEY_POINT: + case KEY_COMMA: + { + blankScreen( ((nKeyCode == KEY_W ) || (nKeyCode == KEY_COMMA)) ? 0x00ffffff : 0x00000000 ); + } + break; + + default: + bRet = false; + break; + } + } + catch( Exception& ) + { + bRet = false; + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::keyInput()" ); + } + + return bRet; +} + +IMPL_LINK( SlideshowImpl, EventListenerHdl, VclSimpleEvent&, rSimpleEvent, void ) +{ + if( !mxShow.is() || mbInputFreeze ) + return; + + if( !((rSimpleEvent.GetId() == VclEventId::WindowCommand) && static_cast<VclWindowEvent*>(&rSimpleEvent)->GetData()) ) + return; + + const CommandEvent& rEvent = *static_cast<const CommandEvent*>(static_cast<VclWindowEvent*>(&rSimpleEvent)->GetData()); + + if( rEvent.GetCommand() != CommandEventId::Media ) + return; + + CommandMediaData* pMediaData = rEvent.GetMediaData(); + pMediaData->SetPassThroughToOS(false); + switch (pMediaData->GetMediaId()) + { +#if defined( MACOSX ) + case MediaCommand::Menu: + if( !mnContextMenuEvent ) + { + if( mpShowWindow ) + maPopupMousePos = mpShowWindow->GetPointerState().maPos; + mnContextMenuEvent = Application::PostUserEvent( LINK( this, SlideshowImpl, ContextMenuHdl ) ); + } + break; + case MediaCommand::VolumeDown: + gotoPreviousSlide(); + break; + case MediaCommand::VolumeUp: + gotoNextEffect(); + break; +#endif + case MediaCommand::NextTrack: + gotoNextEffect(); + break; + case MediaCommand::Pause: + if( !mbIsPaused ) + blankScreen(0); + break; + case MediaCommand::Play: + if( mbIsPaused ) + resume(); + break; + + case MediaCommand::PlayPause: + if( mbIsPaused ) + resume(); + else + blankScreen(0); + break; + case MediaCommand::PreviousTrack: + gotoPreviousSlide(); + break; + case MediaCommand::NextTrackHold: + gotoLastSlide(); + break; + + case MediaCommand::Rewind: + gotoFirstSlide(); + break; + case MediaCommand::Stop: + // in case the user cancels the presentation, switch to current slide + // in edit mode + if( mpSlideController && (ANIMATIONMODE_SHOW == meAnimationMode) ) + { + if( mpSlideController->getCurrentSlideNumber() != -1 ) + mnRestoreSlide = mpSlideController->getCurrentSlideNumber(); + } + endPresentation(); + break; + default: + pMediaData->SetPassThroughToOS(true); + break; + } +} + +void SlideshowImpl::mouseButtonUp(const MouseEvent& rMEvt) +{ + if( rMEvt.IsRight() && !mnContextMenuEvent ) + { + maPopupMousePos = rMEvt.GetPosPixel(); + mnContextMenuEvent = Application::PostUserEvent( LINK( this, SlideshowImpl, ContextMenuHdl ) ); + } +} + +IMPL_LINK_NOARG(SlideshowImpl, ContextMenuHdl, void*, void) +{ + mnContextMenuEvent = nullptr; + + if (mpSlideController == nullptr) + return; + + mbWasPaused = mbIsPaused; + if( !mbWasPaused ) + pause(); + + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, "modules/simpress/ui/slidecontextmenu.ui")); + std::unique_ptr<weld::Menu> xMenu(xBuilder->weld_menu("menu")); + OUString sNextImage(BMP_MENU_NEXT), sPrevImage(BMP_MENU_PREV); + xMenu->insert(0, "next", SdResId(RID_SVXSTR_MENU_NEXT), &sNextImage, nullptr, nullptr, TRISTATE_INDET); + xMenu->insert(1, "prev", SdResId(RID_SVXSTR_MENU_PREV), &sPrevImage, nullptr, nullptr, TRISTATE_INDET); + + // Adding button to display if in Pen mode + xMenu->set_active("pen", mbUsePen); + + const ShowWindowMode eMode = mpShowWindow->GetShowWindowMode(); + xMenu->set_visible("next", mpSlideController->getNextSlideIndex() != -1); + xMenu->set_visible("prev", (mpSlideController->getPreviousSlideIndex() != -1 ) || (eMode == SHOWWINDOWMODE_END) || (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK)); + xMenu->set_visible("edit", mpViewShell->GetDoc()->IsStartWithPresentation()); + + std::unique_ptr<weld::Menu> xPageMenu(xBuilder->weld_menu("gotomenu")); + OUString sFirstImage(BMP_MENU_FIRST), sLastImage(BMP_MENU_LAST); + xPageMenu->insert(0, "first", SdResId(RID_SVXSTR_MENU_FIRST), &sFirstImage, nullptr, nullptr, TRISTATE_INDET); + xPageMenu->insert(1, "last", SdResId(RID_SVXSTR_MENU_LAST), &sLastImage, nullptr, nullptr, TRISTATE_INDET); + + // populate slide goto list + const sal_Int32 nPageNumberCount = mpSlideController->getSlideNumberCount(); + if( nPageNumberCount <= 1 ) + { + xMenu->set_visible("goto", false); + } + else + { + sal_Int32 nCurrentSlideNumber = mpSlideController->getCurrentSlideNumber(); + if( (eMode == SHOWWINDOWMODE_END) || (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK) ) + nCurrentSlideNumber = -1; + + xPageMenu->set_visible("first", mpSlideController->getSlideNumber(0) != nCurrentSlideNumber); + xPageMenu->set_visible("last", mpSlideController->getSlideNumber(mpSlideController->getSlideIndexCount() - 1) != nCurrentSlideNumber); + + sal_Int32 nPageNumber; + + for( nPageNumber = 0; nPageNumber < nPageNumberCount; nPageNumber++ ) + { + if( mpSlideController->isVisibleSlideNumber( nPageNumber ) ) + { + SdPage* pPage = mpDoc->GetSdPage(static_cast<sal_uInt16>(nPageNumber), PageKind::Standard); + if (pPage) + { + OUString sId(OUString::number(CM_SLIDES + nPageNumber)); + xPageMenu->append_check(sId, pPage->GetName()); + if (nPageNumber == nCurrentSlideNumber) + xPageMenu->set_active(sId.toUtf8(), true); + } + } + } + } + + std::unique_ptr<weld::Menu> xBlankMenu(xBuilder->weld_menu("screenmenu")); + + if (mpShowWindow->GetShowWindowMode() == SHOWWINDOWMODE_BLANK) + { + xBlankMenu->set_active((mpShowWindow->GetBlankColor() == COL_WHITE) ? "white" : "black", true); + } + + std::unique_ptr<weld::Menu> xWidthMenu(xBuilder->weld_menu("widthmenu")); + + // populate color width list + sal_Int32 nIterator; + double nWidth; + + nWidth = 4.0; + for( nIterator = 1; nIterator < 6; nIterator++) + { + switch(nIterator) + { + case 1: + nWidth = 4.0; + break; + case 2: + nWidth = 100.0; + break; + case 3: + nWidth = 150.0; + break; + case 4: + nWidth = 200.0; + break; + case 5: + nWidth = 400.0; + break; + default: + break; + } + + if (nWidth == mdUserPaintStrokeWidth) + xWidthMenu->set_active(OString::number(nWidth), true); + } + + ::tools::Rectangle aRect(maPopupMousePos, Size(1,1)); + weld::Window* pParent = weld::GetPopupParent(*mpShowWindow, aRect); + ContextMenuSelectHdl(xMenu->popup_at_rect(pParent, aRect)); + + if( mxView.is() ) + mxView->ignoreNextMouseReleased(); + + if( !mbWasPaused ) + resume(); +} + +void SlideshowImpl::ContextMenuSelectHdl(std::string_view rMenuId) +{ + if (rMenuId == "prev") + { + gotoPreviousSlide(); + mbWasPaused = false; + } + else if(rMenuId == "next") + { + gotoNextSlide(); + mbWasPaused = false; + } + else if (rMenuId == "first") + { + gotoFirstSlide(); + mbWasPaused = false; + } + else if (rMenuId == "last") + { + gotoLastSlide(); + mbWasPaused = false; + } + else if (rMenuId == "black" || rMenuId == "white") + { + const Color aBlankColor(rMenuId == "white" ? COL_WHITE : COL_BLACK); + if( mbWasPaused ) + { + if( mpShowWindow->GetShowWindowMode() == SHOWWINDOWMODE_BLANK ) + { + if( mpShowWindow->GetBlankColor() == aBlankColor ) + { + mbWasPaused = false; + mpShowWindow->RestartShow(); + return; + } + } + mpShowWindow->RestartShow(); + } + if( mpShowWindow->SetBlankMode( mpSlideController->getCurrentSlideIndex(), aBlankColor ) ) + { + pause(); + mbWasPaused = true; + } + } + else if (rMenuId == "color") + { + //Open a color picker based on SvColorDialog + ::Color aColor( ColorTransparency, mnUserPaintColor ); + SvColorDialog aColorDlg; + aColorDlg.SetColor( aColor ); + + if (aColorDlg.Execute(mpShowWindow->GetFrameWeld())) + { + aColor = aColorDlg.GetColor(); + setPenColor(sal_Int32(aColor)); + } + mbWasPaused = false; + } + else if (rMenuId == "4") + { + setPenWidth(4.0); + mbWasPaused = false; + } + else if (rMenuId == "100") + { + setPenWidth(100.0); + mbWasPaused = false; + } + else if (rMenuId == "150") + { + setPenWidth(150.0); + mbWasPaused = false; + } + else if (rMenuId == "200") + { + setPenWidth(200.0); + mbWasPaused = false; + } + else if (rMenuId == "400") + { + setPenWidth(400.0); + mbWasPaused = false; + } + else if (rMenuId == "erase") + { + setEraseAllInk(true); + mbWasPaused = false; + } + else if (rMenuId == "pen") + { + setUsePen(!mbUsePen); + mbWasPaused = false; + } + else if (rMenuId == "edit") + { + // When in autoplay mode (pps/ppsx), offer editing of the presentation + // Turn autostart off, else Impress will close when exiting the Presentation + mpViewShell->GetDoc()->SetExitAfterPresenting(false); + if( mpSlideController && (ANIMATIONMODE_SHOW == meAnimationMode) ) + { + if( mpSlideController->getCurrentSlideNumber() != -1 ) + { + mnRestoreSlide = mpSlideController->getCurrentSlideNumber(); + } + } + endPresentation(); + } + else if (rMenuId == "end") + { + // in case the user cancels the presentation, switch to current slide + // in edit mode + if( mpSlideController && (ANIMATIONMODE_SHOW == meAnimationMode) ) + { + if( mpSlideController->getCurrentSlideNumber() != -1 ) + { + mnRestoreSlide = mpSlideController->getCurrentSlideNumber(); + } + } + endPresentation(); + } + else if (!rMenuId.empty()) + { + sal_Int32 nPageNumber = o3tl::toInt32(rMenuId) - CM_SLIDES; + const ShowWindowMode eMode = mpShowWindow->GetShowWindowMode(); + if( (eMode == SHOWWINDOWMODE_END) || (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK) ) + { + mpShowWindow->RestartShow( nPageNumber ); + } + else if( nPageNumber != mpSlideController->getCurrentSlideNumber() ) + { + displaySlideNumber( nPageNumber ); + } + mbWasPaused = false; + } +} + +Reference< XSlideShow > SlideshowImpl::createSlideShow() +{ + Reference< XSlideShow > xShow; + + try + { + Reference< uno::XComponentContext > xContext = + ::comphelper::getProcessComponentContext(); + + xShow.set( presentation::SlideShow::create(xContext), UNO_SET_THROW ); + } + catch( uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::createSlideShow()" ); + } + + return xShow; +} + +void SlideshowImpl::createSlideList( bool bAll, std::u16string_view rPresSlide ) +{ + const sal_uInt16 nSlideCount = mpDoc->GetSdPageCount( PageKind::Standard ); + + if( !nSlideCount ) + return; + + SdCustomShow* pCustomShow; + + if( mpDoc->GetCustomShowList() && maPresSettings.mbCustomShow ) + pCustomShow = mpDoc->GetCustomShowList()->GetCurObject(); + else + pCustomShow = nullptr; + + // create animation slide controller + AnimationSlideController::Mode eMode = + ( pCustomShow && !pCustomShow->PagesVector().empty() ) ? AnimationSlideController::CUSTOM : + (bAll ? AnimationSlideController::ALL : AnimationSlideController::FROM); + + Reference< XDrawPagesSupplier > xDrawPages( mpDoc->getUnoModel(), UNO_QUERY_THROW ); + Reference< XIndexAccess > xSlides( xDrawPages->getDrawPages(), UNO_QUERY_THROW ); + mpSlideController = std::make_shared<AnimationSlideController>( xSlides, eMode ); + + if( eMode != AnimationSlideController::CUSTOM ) + { + sal_Int32 nFirstVisibleSlide = 0; + + // normal presentation + if( !rPresSlide.empty() ) + { + sal_Int32 nSlide; + bool bTakeNextAvailable = false; + + for( nSlide = 0, nFirstVisibleSlide = -1; + ( nSlide < nSlideCount ) && ( -1 == nFirstVisibleSlide ); nSlide++ ) + { + SdPage* pTestSlide = mpDoc->GetSdPage( static_cast<sal_uInt16>(nSlide), PageKind::Standard ); + + if( pTestSlide->GetName() == rPresSlide ) + { + if( pTestSlide->IsExcluded() ) + bTakeNextAvailable = true; + else + nFirstVisibleSlide = nSlide; + } + else if( bTakeNextAvailable && !pTestSlide->IsExcluded() ) + nFirstVisibleSlide = nSlide; + } + + if( -1 == nFirstVisibleSlide ) + nFirstVisibleSlide = 0; + } + + for( sal_Int32 i = 0; i < nSlideCount; i++ ) + { + bool bVisible = ! mpDoc->GetSdPage( static_cast<sal_uInt16>(i), PageKind::Standard )->IsExcluded(); + if( bVisible || (eMode == AnimationSlideController::ALL) ) + mpSlideController->insertSlideNumber( i, bVisible ); + } + + mpSlideController->setStartSlideNumber( nFirstVisibleSlide ); + } + else + { + if( meAnimationMode != ANIMATIONMODE_SHOW && !rPresSlide.empty() ) + { + sal_Int32 nSlide; + for( nSlide = 0; nSlide < nSlideCount; nSlide++ ) + if( rPresSlide == mpDoc->GetSdPage( static_cast<sal_uInt16>(nSlide), PageKind::Standard )->GetName() ) + break; + + if( nSlide < nSlideCount ) + mpSlideController->insertSlideNumber( static_cast<sal_uInt16>(nSlide) ); + } + + for( const auto& rpPage : pCustomShow->PagesVector() ) + { + const sal_uInt16 nSdSlide = ( rpPage->GetPageNum() - 1 ) / 2; + + if( ! mpDoc->GetSdPage( nSdSlide, PageKind::Standard )->IsExcluded()) + mpSlideController->insertSlideNumber( nSdSlide ); + } + } +} + +typedef sal_uInt16 (*FncGetChildWindowId)(); + +const FncGetChildWindowId aShowChildren[] = +{ + &AnimationChildWindow::GetChildWindowId, + &Svx3DChildWindow::GetChildWindowId, + &SvxFontWorkChildWindow::GetChildWindowId, + &SvxColorChildWindow::GetChildWindowId, + &SvxSearchDialogWrapper::GetChildWindowId, + &SvxBmpMaskChildWindow::GetChildWindowId, + &SvxIMapDlgChildWindow::GetChildWindowId, + &SvxHlinkDlgWrapper::GetChildWindowId, + &SfxInfoBarContainerChild::GetChildWindowId +}; + +void SlideshowImpl::hideChildWindows() +{ + mnChildMask = 0; + + if( ANIMATIONMODE_SHOW != meAnimationMode ) + return; + + SfxViewFrame* pViewFrame = getViewFrame(); + + if( !pViewFrame ) + return; + + for( sal_uLong i = 0; i < SAL_N_ELEMENTS( aShowChildren ); i++ ) + { + const sal_uInt16 nId = ( *aShowChildren[ i ] )(); + + if( pViewFrame->GetChildWindow( nId ) ) + { + pViewFrame->SetChildWindow( nId, false ); + mnChildMask |= ::tools::ULong(1) << i; + } + } +} + +void SlideshowImpl::showChildWindows() +{ + if( ANIMATIONMODE_SHOW == meAnimationMode ) + { + SfxViewFrame* pViewFrame = getViewFrame(); + if( pViewFrame ) + { + for( sal_uLong i = 0; i < SAL_N_ELEMENTS(aShowChildren); i++ ) + { + if( mnChildMask & ( ::tools::ULong(1) << i ) ) + pViewFrame->SetChildWindow( ( *aShowChildren[ i ] )(), true ); + } + } + } +} + +SfxViewFrame* SlideshowImpl::getViewFrame() const +{ + return mpViewShell ? mpViewShell->GetViewFrame() : nullptr; +} + +SfxDispatcher* SlideshowImpl::getDispatcher() const +{ + return (mpViewShell && mpViewShell->GetViewFrame()) ? mpViewShell->GetViewFrame()->GetDispatcher() : nullptr; +} + +SfxBindings* SlideshowImpl::getBindings() const +{ + return (mpViewShell && mpViewShell->GetViewFrame()) ? &mpViewShell->GetViewFrame()->GetBindings() : nullptr; +} + +void SlideshowImpl::resize( const Size& rSize ) +{ + maPresSize = rSize; + + if(mpShowWindow) + { + mpShowWindow->SetSizePixel( maPresSize ); + mpShowWindow->Show(); + } + + if( mxView.is() ) try + { + awt::WindowEvent aEvt; + mxView->windowResized(aEvt); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::resize()" ); + } +} + +void SlideshowImpl::setActiveXToolbarsVisible( bool bVisible ) +{ + // in case of ActiveX control the toolbars should not be visible if slide show runs in window mode + // actually it runs always in window mode in case of ActiveX control + if ( !(!maPresSettings.mbFullScreen && mpDocSh && mpDocSh->GetMedium()) ) + return; + + const SfxBoolItem* pItem = SfxItemSet::GetItem<SfxBoolItem>(mpDocSh->GetMedium()->GetItemSet(), SID_VIEWONLY, false); + if ( !(pItem && pItem->GetValue()) ) + return; + + // this is a plugin/activex mode, no toolbars should be visible during slide show + // after the end of slide show they should be visible again + SfxViewFrame* pViewFrame = getViewFrame(); + if( !pViewFrame ) + return; + + try + { + Reference< frame::XLayoutManager > xLayoutManager; + Reference< beans::XPropertySet > xFrameProps( pViewFrame->GetFrame().GetFrameInterface(), UNO_QUERY_THROW ); + if ( ( xFrameProps->getPropertyValue( "LayoutManager" ) + >>= xLayoutManager ) + && xLayoutManager.is() ) + { + xLayoutManager->setVisible( bVisible ); + } + } + catch( uno::Exception& ) + {} +} + +void SAL_CALL SlideshowImpl::activate() +{ + SolarMutexGuard aSolarGuard; + + maDeactivateTimer.Stop(); + + if( mbActive || !mxShow.is() ) + return; + + mbActive = true; + + if( ANIMATIONMODE_SHOW == meAnimationMode ) + { + if( mbAutoSaveWasOn ) + setAutoSaveState( false ); + + if( mpShowWindow ) + { + SfxViewFrame* pViewFrame = getViewFrame(); + SfxDispatcher* pDispatcher = pViewFrame ? pViewFrame->GetDispatcher() : nullptr; + + hideChildWindows(); + + if( pDispatcher ) + { + // filter all forbidden slots + pDispatcher->SetSlotFilter( SfxSlotFilterState::ENABLED, pAllowed ); + } + + if( getBindings() ) + getBindings()->InvalidateAll(true); + + mpShowWindow->GrabFocus(); + } + } + + resume(); +} + +void SAL_CALL SlideshowImpl::deactivate() +{ + SolarMutexGuard aSolarGuard; + + if( mbActive && mxShow.is() ) + { + maDeactivateTimer.Start(); + } +} + +IMPL_LINK_NOARG(SlideshowImpl, deactivateHdl, Timer *, void) +{ + if( !(mbActive && mxShow.is()) ) + return; + + mbActive = false; + + pause(); + + if( ANIMATIONMODE_SHOW == meAnimationMode ) + { + if( mbAutoSaveWasOn ) + setAutoSaveState( true ); + + if( mpShowWindow ) + { + showChildWindows(); + } + } +} + +sal_Bool SAL_CALL SlideshowImpl::isActive() +{ + SolarMutexGuard aSolarGuard; + return mbActive; +} + +void SlideshowImpl::setAutoSaveState( bool bOn) +{ + try + { + uno::Reference<uno::XComponentContext> xContext( ::comphelper::getProcessComponentContext() ); + + uno::Reference< util::XURLTransformer > xParser(util::URLTransformer::create(xContext)); + util::URL aURL; + aURL.Complete = "vnd.sun.star.autorecovery:/setAutoSaveState"; + xParser->parseStrict(aURL); + + Sequence< beans::PropertyValue > aArgs{ comphelper::makePropertyValue("AutoSaveState", bOn) }; + + uno::Reference< frame::XDispatch > xAutoSave = frame::theAutoRecovery::get(xContext); + xAutoSave->dispatch(aURL, aArgs); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::setAutoSaveState()"); + } +} + +Reference< XDrawPage > SAL_CALL SlideshowImpl::getCurrentSlide() +{ + SolarMutexGuard aSolarGuard; + + Reference< XDrawPage > xSlide; + if( mxShow.is() && mpSlideController ) + { + sal_Int32 nSlide = getCurrentSlideNumber(); + if( (nSlide >= 0) && (nSlide < mpSlideController->getSlideNumberCount() ) ) + xSlide = mpSlideController->getSlideByNumber( nSlide ); + } + + return xSlide; +} + +sal_Int32 SAL_CALL SlideshowImpl::getNextSlideIndex() +{ + SolarMutexGuard aSolarGuard; + + if( mxShow.is() ) + { + return mpSlideController->getNextSlideIndex(); + } + else + { + return -1; + } +} + +sal_Int32 SAL_CALL SlideshowImpl::getCurrentSlideIndex() +{ + return mpSlideController ? mpSlideController->getCurrentSlideIndex() : -1; +} + +// css::presentation::XSlideShowController: + +::sal_Int32 SAL_CALL SlideshowImpl::getSlideCount() +{ + return mpSlideController ? mpSlideController->getSlideIndexCount() : 0; +} + +Reference< XDrawPage > SAL_CALL SlideshowImpl::getSlideByIndex(::sal_Int32 Index) +{ + if ((mpSlideController == nullptr) || (Index < 0) + || (Index >= mpSlideController->getSlideIndexCount())) + throw IndexOutOfBoundsException(); + + return mpSlideController->getSlideByNumber( mpSlideController->getSlideNumber( Index ) ); +} + +sal_Bool SAL_CALL SlideshowImpl::getAlwaysOnTop() +{ + SolarMutexGuard aSolarGuard; + return maPresSettings.mbAlwaysOnTop; +} + +void SAL_CALL SlideshowImpl::setAlwaysOnTop( sal_Bool bAlways ) +{ + SolarMutexGuard aSolarGuard; + if( maPresSettings.mbAlwaysOnTop != bool(bAlways) ) + { + maPresSettings.mbAlwaysOnTop = bAlways; + // todo, can this be changed while running? + } +} + +sal_Bool SAL_CALL SlideshowImpl::isFullScreen() +{ + SolarMutexGuard aSolarGuard; + return maPresSettings.mbFullScreen; +} + +sal_Bool SAL_CALL SlideshowImpl::getMouseVisible() +{ + SolarMutexGuard aSolarGuard; + return maPresSettings.mbMouseVisible; +} + +void SAL_CALL SlideshowImpl::setMouseVisible( sal_Bool bVisible ) +{ + SolarMutexGuard aSolarGuard; + if( maPresSettings.mbMouseVisible != bool(bVisible) ) + { + maPresSettings.mbMouseVisible = bVisible; + if( mpShowWindow ) + mpShowWindow->SetMouseAutoHide( !maPresSettings.mbMouseVisible ); + } +} + +sal_Bool SAL_CALL SlideshowImpl::getUsePen() +{ + SolarMutexGuard aSolarGuard; + return mbUsePen; +} + +void SAL_CALL SlideshowImpl::setUsePen( sal_Bool bMouseAsPen ) +{ + SolarMutexGuard aSolarGuard; + mbUsePen = bMouseAsPen; + if( !mxShow.is() ) + return; + + try + { + // For Pencolor; + Any aValue; + if( mbUsePen ) + aValue <<= mnUserPaintColor; + beans::PropertyValue aPenProp; + aPenProp.Name = "UserPaintColor"; + aPenProp.Value = aValue; + mxShow->setProperty( aPenProp ); + + //for StrokeWidth : + if( mbUsePen ) + { + beans::PropertyValue aPenPropWidth; + aPenPropWidth.Name = "UserPaintStrokeWidth"; + aPenPropWidth.Value <<= mdUserPaintStrokeWidth; + mxShow->setProperty( aPenPropWidth ); + + // for Pen Mode + beans::PropertyValue aPenPropSwitchPenMode; + aPenPropSwitchPenMode.Name = "SwitchPenMode"; + aPenPropSwitchPenMode.Value <<= true; + mxShow->setProperty( aPenPropSwitchPenMode ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::setUsePen()" ); + } +} + +double SAL_CALL SlideshowImpl::getPenWidth() +{ + SolarMutexGuard aSolarGuard; + return mdUserPaintStrokeWidth; +} + +void SAL_CALL SlideshowImpl::setPenWidth( double dStrokeWidth ) +{ + SolarMutexGuard aSolarGuard; + mdUserPaintStrokeWidth = dStrokeWidth; + setUsePen( true ); // enable pen mode, update color and width +} + +sal_Int32 SAL_CALL SlideshowImpl::getPenColor() +{ + SolarMutexGuard aSolarGuard; + return mnUserPaintColor; +} + +void SAL_CALL SlideshowImpl::setPenColor( sal_Int32 nColor ) +{ + SolarMutexGuard aSolarGuard; + mnUserPaintColor = nColor; + setUsePen( true ); // enable pen mode, update color +} + +void SAL_CALL SlideshowImpl::setEraseAllInk(sal_Bool bEraseAllInk) +{ + if( !bEraseAllInk ) + return; + + SolarMutexGuard aSolarGuard; + if( !mxShow.is() ) + return; + + try + { + beans::PropertyValue aPenPropEraseAllInk; + aPenPropEraseAllInk.Name = "EraseAllInk"; + aPenPropEraseAllInk.Value <<= bEraseAllInk; + mxShow->setProperty( aPenPropEraseAllInk ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd.slideshow", "sd::SlideshowImpl::setEraseAllInk()" ); + } +} + +// XSlideShowController Methods +sal_Bool SAL_CALL SlideshowImpl::isRunning( ) +{ + SolarMutexGuard aSolarGuard; + return mxShow.is(); +} + +void SAL_CALL SlideshowImpl::gotoNextEffect( ) +{ + SolarMutexGuard aSolarGuard; + + if( !(mxShow.is() && mpSlideController && mpShowWindow) ) + return; + + if( mbIsPaused ) + resume(); + + const ShowWindowMode eMode = mpShowWindow->GetShowWindowMode(); + if( eMode == SHOWWINDOWMODE_END ) + { + endPresentation(); + } + else if( (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK) ) + { + mpShowWindow->RestartShow(); + } + else + { + mxShow->nextEffect(); + update(); + } +} + +void SAL_CALL SlideshowImpl::gotoPreviousEffect( ) +{ + SolarMutexGuard aSolarGuard; + + if( !(mxShow.is() && mpSlideController && mpShowWindow) ) + return; + + if( mbIsPaused ) + resume(); + + const ShowWindowMode eMode = mpShowWindow->GetShowWindowMode(); + if( (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK) ) + { + mpShowWindow->RestartShow(); + } + else + { + mxShow->previousEffect(); + update(); + } +} + +void SAL_CALL SlideshowImpl::gotoFirstSlide( ) +{ + SolarMutexGuard aSolarGuard; + + if( !(mpShowWindow && mpSlideController) ) + return; + + if( mbIsPaused ) + resume(); + + if( mpShowWindow->GetShowWindowMode() == SHOWWINDOWMODE_END ) + { + if( mpSlideController->getSlideIndexCount() ) + mpShowWindow->RestartShow( 0); + } + else + { + displaySlideIndex( 0 ); + } +} + +void SAL_CALL SlideshowImpl::gotoNextSlide( ) +{ + SolarMutexGuard aSolarGuard; + + if( mbIsPaused ) + resume(); + + const ShowWindowMode eMode = mpShowWindow->GetShowWindowMode(); + if( (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK) ) + { + mpShowWindow->RestartShow(); + } + else + { + // if this is a show, ignore user inputs and + // start 20ms timer to reenable inputs to filter + // buffered inputs during slide transition + if( meAnimationMode == ANIMATIONMODE_SHOW ) + { + mbInputFreeze = true; + maInputFreezeTimer.Start(); + } + + if( mpSlideController ) + { + if( mpSlideController->nextSlide() ) + { + displayCurrentSlide(); + } + else + { + stopSound(); + + if( meAnimationMode == ANIMATIONMODE_PREVIEW ) + { + endPresentation(); + } + else if( maPresSettings.mbEndless ) + { + if( maPresSettings.mnPauseTimeout ) + { + if( mpShowWindow ) + { + if ( maPresSettings.mbShowPauseLogo ) + { + Graphic aGraphic(SfxApplication::GetApplicationLogo(360)); + mpShowWindow->SetPauseMode( maPresSettings.mnPauseTimeout, &aGraphic ); + } + else + mpShowWindow->SetPauseMode( maPresSettings.mnPauseTimeout ); + } + } + else + { + displaySlideIndex( 0 ); + } + } + else + { + if( mpShowWindow ) + { + mpShowWindow->SetEndMode(); + if( !mpViewShell->GetDoc()->IsStartWithPresentation() ) + pause(); + } + } + } + } + } +} + +void SAL_CALL SlideshowImpl::gotoPreviousSlide( ) +{ + gotoPreviousSlide(false); +} + +void SlideshowImpl::gotoPreviousSlide (const bool bSkipAllMainSequenceEffects) +{ + SolarMutexGuard aSolarGuard; + + if( !(mxShow.is() && mpSlideController) ) + return; + + try + { + if( mbIsPaused ) + resume(); + + const ShowWindowMode eMode = mpShowWindow->GetShowWindowMode(); + if( eMode == SHOWWINDOWMODE_END ) + { + mpShowWindow->RestartShow( mpSlideController->getCurrentSlideIndex() ); + } + else if( (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK) ) + { + mpShowWindow->RestartShow(); + } + else + { + if( mpSlideController->previousSlide()) + displayCurrentSlide(bSkipAllMainSequenceEffects); + else if (bSkipAllMainSequenceEffects) + { + // We could not go to the previous slide (probably because + // the current slide is already the first one). We still + // have to call displayCurrentSlide because the calling + // slideshow can not determine whether there is a previous + // slide or not and has already prepared for a slide change. + // This slide change has to be completed now, even when + // changing to the same slide. + // Note that in this special case we do NOT pass + // bSkipAllMainSequenceEffects because we display the same + // slide as before and do not want to show all its effects. + displayCurrentSlide(); + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::gotoPreviousSlide()" ); + } +} + +void SAL_CALL SlideshowImpl::gotoLastSlide() +{ + SolarMutexGuard aSolarGuard; + + if( !mpSlideController ) + return; + + if( mbIsPaused ) + resume(); + + const sal_Int32 nLastSlideIndex = mpSlideController->getSlideIndexCount() - 1; + if( nLastSlideIndex >= 0 ) + { + if( mpShowWindow->GetShowWindowMode() == SHOWWINDOWMODE_END ) + { + mpShowWindow->RestartShow( nLastSlideIndex ); + } + else + { + displaySlideIndex( nLastSlideIndex ); + } + } +} + +void SAL_CALL SlideshowImpl::gotoBookmark( const OUString& rBookmark ) +{ + SolarMutexGuard aSolarGuard; + + if( mbIsPaused ) + resume(); + + sal_Int32 nSlideNumber = getSlideNumberForBookmark( rBookmark ); + if( nSlideNumber != -1 ) + displaySlideNumber( nSlideNumber ); +} + +void SAL_CALL SlideshowImpl::gotoSlide( const Reference< XDrawPage >& xSlide ) +{ + SolarMutexGuard aSolarGuard; + + if( !(mpSlideController && xSlide.is()) ) + return; + + if( mbIsPaused ) + resume(); + + const sal_Int32 nSlideCount = mpSlideController->getSlideNumberCount(); + for( sal_Int32 nSlide = 0; nSlide < nSlideCount; nSlide++ ) + { + if( mpSlideController->getSlideByNumber( nSlide ) == xSlide ) + { + displaySlideNumber( nSlide ); + } + } +} + +void SAL_CALL SlideshowImpl::gotoSlideIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aSolarGuard; + + if( mbIsPaused ) + resume(); + + displaySlideIndex( nIndex ); +} + +void SAL_CALL SlideshowImpl::stopSound( ) +{ + SolarMutexGuard aSolarGuard; + + try + { + if( mxPlayer.is() ) + { + mxPlayer->stop(); + mxPlayer.clear(); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::stopSound()" ); + } +} + +// XIndexAccess + +::sal_Int32 SAL_CALL SlideshowImpl::getCount( ) +{ + return getSlideCount(); +} + +css::uno::Any SAL_CALL SlideshowImpl::getByIndex( ::sal_Int32 Index ) +{ + return Any( getSlideByIndex( Index ) ); +} + +css::uno::Type SAL_CALL SlideshowImpl::getElementType( ) +{ + return cppu::UnoType<XDrawPage>::get(); +} + +sal_Bool SAL_CALL SlideshowImpl::hasElements( ) +{ + return getSlideCount() != 0; +} + +Reference< XSlideShow > SAL_CALL SlideshowImpl::getSlideShow() +{ + return mxShow; +} + +PresentationSettingsEx::PresentationSettingsEx( const PresentationSettingsEx& r ) +: PresentationSettings( r ) +, mbRehearseTimings(r.mbRehearseTimings) +, mbPreview(r.mbPreview) +, mpParentWindow( nullptr ) +{ +} + +PresentationSettingsEx::PresentationSettingsEx( PresentationSettings const & r ) +: PresentationSettings( r ) +, mbRehearseTimings(false) +, mbPreview(false) +, mpParentWindow(nullptr) +{ +} + +void PresentationSettingsEx::SetArguments( const Sequence< PropertyValue >& rArguments ) +{ + for( const PropertyValue& rValue : rArguments ) + { + SetPropertyValue( rValue.Name, rValue.Value ); + } +} + +void PresentationSettingsEx::SetPropertyValue( std::u16string_view rProperty, const Any& rValue ) +{ + if ( rProperty == u"RehearseTimings" ) + { + if( rValue >>= mbRehearseTimings ) + return; + } + else if ( rProperty == u"Preview" ) + { + if( rValue >>= mbPreview ) + return; + } + else if ( rProperty == u"AnimationNode" ) + { + if( rValue >>= mxAnimationNode ) + return; + } + else if ( rProperty == u"ParentWindow" ) + { + Reference< XWindow > xWindow; + if( rValue >>= xWindow ) + { + mpParentWindow = xWindow.is() ? VCLUnoHelper::GetWindow( xWindow ) + : nullptr; + return; + } + } + else if ( rProperty == u"AllowAnimations" ) + { + if( rValue >>= mbAnimationAllowed ) + return; + } + else if ( rProperty == u"FirstPage" ) + { + OUString aPresPage; + if( rValue >>= aPresPage ) + { + maPresPage = getUiNameFromPageApiNameImpl(aPresPage); + mbCustomShow = false; + mbAll = false; + return; + } + else + { + if( rValue >>= mxStartPage ) + return; + } + } + else if ( rProperty == u"IsAlwaysOnTop" ) + { + if( rValue >>= mbAlwaysOnTop ) + return; + } + else if ( rProperty == u"IsAutomatic" ) + { + if( rValue >>= mbManual ) + return; + } + else if ( rProperty == u"IsEndless" ) + { + if( rValue >>= mbEndless ) + return; + } + else if ( rProperty == u"IsFullScreen" ) + { + if( rValue >>= mbFullScreen ) + return; + } + else if ( rProperty == u"IsMouseVisible" ) + { + if( rValue >>= mbMouseVisible ) + return; + } + else if ( rProperty == u"Pause" ) + { + sal_Int32 nPause = -1; + if( (rValue >>= nPause) && (nPause >= 0) ) + { + mnPauseTimeout = nPause; + return; + } + } + else if ( rProperty == u"UsePen" ) + { + if( rValue >>= mbMouseAsPen ) + return; + } + throw IllegalArgumentException(); +} + +// XAnimationListener + +SlideShowListenerProxy::SlideShowListenerProxy( const rtl::Reference< SlideshowImpl >& xController, const css::uno::Reference< css::presentation::XSlideShow >& xSlideShow ) +: maListeners( m_aMutex ) +, mxController( xController ) +, mxSlideShow( xSlideShow ) +{ +} + +SlideShowListenerProxy::~SlideShowListenerProxy() +{ +} + +void SlideShowListenerProxy::addAsSlideShowListener() +{ + if( mxSlideShow.is() ) + { + Reference< XSlideShowListener > xSlideShowListener( this ); + mxSlideShow->addSlideShowListener( xSlideShowListener ); + } +} + +void SlideShowListenerProxy::removeAsSlideShowListener() +{ + if( mxSlideShow.is() ) + { + Reference< XSlideShowListener > xSlideShowListener( this ); + mxSlideShow->removeSlideShowListener( xSlideShowListener ); + } +} + +void SlideShowListenerProxy::addShapeEventListener( const css::uno::Reference< css::drawing::XShape >& xShape ) +{ + if( mxSlideShow.is() ) + { + Reference< XShapeEventListener > xListener( this ); + mxSlideShow->addShapeEventListener( xListener, xShape ); + } +} + +void SlideShowListenerProxy::removeShapeEventListener( const css::uno::Reference< css::drawing::XShape >& xShape ) +{ + if( mxSlideShow.is() ) + { + Reference< XShapeEventListener > xListener( this ); + mxSlideShow->removeShapeEventListener( xListener, xShape ); + } +} + +void SlideShowListenerProxy::addSlideShowListener( const css::uno::Reference< css::presentation::XSlideShowListener >& xListener ) +{ + maListeners.addInterface(xListener); +} + +void SlideShowListenerProxy::removeSlideShowListener( const css::uno::Reference< css::presentation::XSlideShowListener >& xListener ) +{ + maListeners.removeInterface(xListener); +} + +void SAL_CALL SlideShowListenerProxy::beginEvent( const Reference< XAnimationNode >& xNode ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + if( maListeners.getLength() >= 0 ) + { + maListeners.forEach( + [&] (Reference<XAnimationListener> const& xListener) { + return xListener->beginEvent(xNode); + } ); + } +} + +void SAL_CALL SlideShowListenerProxy::endEvent( const Reference< XAnimationNode >& xNode ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + if( maListeners.getLength() >= 0 ) + { + maListeners.forEach( + [&] (Reference<XAnimationListener> const& xListener) { + return xListener->endEvent(xNode); + } ); + } +} + +void SAL_CALL SlideShowListenerProxy::repeat( const Reference< XAnimationNode >& xNode, ::sal_Int32 nRepeat ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + if( maListeners.getLength() >= 0 ) + { + maListeners.forEach( + [&] (Reference<XAnimationListener> const& xListener) { + return xListener->repeat(xNode, nRepeat); + } ); + } +} + +// css::presentation::XSlideShowListener: + +void SAL_CALL SlideShowListenerProxy::paused( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + maListeners.forEach( + [](uno::Reference<presentation::XSlideShowListener> const& xListener) + { + xListener->paused(); + }); +} + +void SAL_CALL SlideShowListenerProxy::resumed( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + maListeners.forEach( + [](uno::Reference<presentation::XSlideShowListener> const& xListener) + { + xListener->resumed(); + }); +} + +void SAL_CALL SlideShowListenerProxy::slideTransitionStarted( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + maListeners.forEach( + [](uno::Reference<presentation::XSlideShowListener> const& xListener) + { + xListener->slideTransitionStarted(); + }); +} + +void SAL_CALL SlideShowListenerProxy::slideTransitionEnded( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + maListeners.forEach( + [](uno::Reference<presentation::XSlideShowListener> const& xListener) + { + xListener->slideTransitionEnded (); + }); +} + +void SAL_CALL SlideShowListenerProxy::slideAnimationsEnded( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + maListeners.forEach( + [](uno::Reference<presentation::XSlideShowListener> const& xListener) + { + xListener->slideAnimationsEnded (); + }); +} + +void SlideShowListenerProxy::slideEnded(sal_Bool bReverse) +{ + { + ::osl::MutexGuard aGuard( m_aMutex ); + + if( maListeners.getLength() >= 0 ) + { + maListeners.forEach( + [&] (Reference<XSlideShowListener> const& xListener) { + return xListener->slideEnded(bReverse); + } ); + } + } + + { + SolarMutexGuard aSolarGuard; + if( mxController.is() ) + mxController->slideEnded(bReverse); + } +} + +void SlideShowListenerProxy::hyperLinkClicked( OUString const& aHyperLink ) +{ + { + ::osl::MutexGuard aGuard( m_aMutex ); + + if( maListeners.getLength() >= 0 ) + { + maListeners.forEach( + [&] (Reference<XSlideShowListener> const& xListener) { + return xListener->hyperLinkClicked(aHyperLink); + } ); + } + } + + { + SolarMutexGuard aSolarGuard; + if( mxController.is() ) + mxController->hyperLinkClicked(aHyperLink); + } +} + +// XEventListener + +void SAL_CALL SlideShowListenerProxy::disposing( const css::lang::EventObject& aDisposeEvent ) +{ + maListeners.disposeAndClear( aDisposeEvent ); + mxController.clear(); + mxSlideShow.clear(); +} + +// XShapeEventListener + +void SAL_CALL SlideShowListenerProxy::click( const Reference< XShape >& xShape, const css::awt::MouseEvent& /*aOriginalEvent*/ ) +{ + SolarMutexGuard aSolarGuard; + if( mxController.is() ) + mxController->click(xShape ); +} + +} // namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/slideshowimpl.hxx b/sd/source/ui/slideshow/slideshowimpl.hxx new file mode 100644 index 000000000..eeec8a3fd --- /dev/null +++ b/sd/source/ui/slideshow/slideshowimpl.hxx @@ -0,0 +1,342 @@ +/* -*- 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 . + */ + +#pragma once + +#include <memory> +#include <sal/config.h> +#include <comphelper/compbase.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/basemutex.hxx> +#include <comphelper/interfacecontainer3.hxx> +#include <com/sun/star/presentation/ClickAction.hpp> +#include <com/sun/star/presentation/XSlideShowListener.hpp> +#include <com/sun/star/presentation/XSlideShowController.hpp> +#include <com/sun/star/presentation/XShapeEventListener.hpp> + +#include <drawdoc.hxx> + +#include "showwindow.hxx" + +#include <slideshow.hxx> + +namespace com::sun::star::frame { class XModel; } +namespace com::sun::star::media { class XPlayer; } +namespace sd { class DrawDocShell; } +namespace sd { class ViewShell; } + +class SfxBindings; +class SfxDispatcher; +class SfxViewFrame; +class StarBASIC; + +namespace sd +{ +class SlideShowView; +class AnimationSlideController; +class PaneHider; + +struct PresentationSettingsEx : public PresentationSettings +{ + bool mbRehearseTimings; + bool mbPreview; + VclPtr<vcl::Window> mpParentWindow; + css::uno::Reference< css::drawing::XDrawPage > mxStartPage; + css::uno::Reference< css::animations::XAnimationNode > mxAnimationNode; + + PresentationSettingsEx( const PresentationSettingsEx& ); + explicit PresentationSettingsEx( PresentationSettings const & ); + + /// @throws css::lang::IllegalArgumentException + void SetArguments( const css::uno::Sequence< css::beans::PropertyValue >& rArguments ); + + /// @throws css::lang::IllegalArgumentException + void SetPropertyValue( std::u16string_view rProperty, const css::uno::Any& rValue ); +}; + +struct WrappedShapeEventImpl +{ + css::presentation::ClickAction meClickAction; + sal_Int32 mnVerb; + OUString maStrBookmark; + WrappedShapeEventImpl() : meClickAction( css::presentation::ClickAction_NONE ), mnVerb( 0 ) {}; +}; + +typedef std::shared_ptr< WrappedShapeEventImpl > WrappedShapeEventImplPtr; + +class SlideShowListenerProxy : private ::cppu::BaseMutex, + public ::cppu::WeakImplHelper< css::presentation::XSlideShowListener, css::presentation::XShapeEventListener > +{ +public: + SlideShowListenerProxy( const rtl::Reference< SlideshowImpl >& xController, const css::uno::Reference< css::presentation::XSlideShow >& xSlideShow ); + virtual ~SlideShowListenerProxy() override; + + void addAsSlideShowListener(); + void removeAsSlideShowListener(); + + void addSlideShowListener( const css::uno::Reference< css::presentation::XSlideShowListener >& Listener ); + void removeSlideShowListener( const css::uno::Reference< css::presentation::XSlideShowListener >& Listener ); + + void addShapeEventListener( const css::uno::Reference< css::drawing::XShape >& xShape ); + void removeShapeEventListener( const css::uno::Reference< css::drawing::XShape >& xShape ); + + // css::animations::XAnimationListener + virtual void SAL_CALL beginEvent( const css::uno::Reference< css::animations::XAnimationNode >& Node ) override; + virtual void SAL_CALL endEvent( const css::uno::Reference< css::animations::XAnimationNode >& Node ) override; + virtual void SAL_CALL repeat( const css::uno::Reference< css::animations::XAnimationNode >& Node, ::sal_Int32 Repeat ) override; + + // css::presentation::XSlideShowListener: + virtual void SAL_CALL paused() override; + virtual void SAL_CALL resumed() override; + virtual void SAL_CALL slideTransitionStarted() override; + virtual void SAL_CALL slideTransitionEnded() override; + virtual void SAL_CALL slideAnimationsEnded() override; + virtual void SAL_CALL slideEnded(sal_Bool bReverse) override; + virtual void SAL_CALL hyperLinkClicked(const OUString & hyperLink) override; + + // css::lang::XEventListener: + virtual void SAL_CALL disposing(const css::lang::EventObject & Source) override; + + // css::presentation::XShapeEventListener: + virtual void SAL_CALL click(const css::uno::Reference< css::drawing::XShape > & xShape, const css::awt::MouseEvent & aOriginalEvent) override; + +private: + ::comphelper::OInterfaceContainerHelper3<css::presentation::XSlideShowListener> maListeners; + rtl::Reference< SlideshowImpl > mxController; + css::uno::Reference< css::presentation::XSlideShow > mxSlideShow; +}; + +typedef comphelper::WeakComponentImplHelper< css::presentation::XSlideShowController, css::container::XIndexAccess > SlideshowImplBase; + +class SlideshowImpl final : public SlideshowImplBase +{ +friend class SlideShow; +friend class SlideShowView; + +public: + explicit SlideshowImpl( const css::uno::Reference< css::presentation::XPresentation2 >& xPresentation, ViewShell* pViewSh, ::sd::View* pView, SdDrawDocument* pDoc, vcl::Window* pParentWindow); + + // css::presentation::XSlideShowController: + virtual sal_Bool SAL_CALL getAlwaysOnTop() override; + virtual void SAL_CALL setAlwaysOnTop( sal_Bool _alwaysontop ) override; + virtual sal_Bool SAL_CALL getMouseVisible() override; + virtual void SAL_CALL setMouseVisible( sal_Bool _mousevisible ) override; + virtual sal_Bool SAL_CALL getUsePen() override; + virtual void SAL_CALL setUsePen( sal_Bool _usepen ) override; + virtual ::sal_Int32 SAL_CALL getPenColor() override; + virtual void SAL_CALL setPenColor( ::sal_Int32 _pencolor ) override; + virtual double SAL_CALL getPenWidth() override; + virtual void SAL_CALL setPenWidth( double dStrokeWidth ) override; + /// @throws css::uno::RuntimeException + virtual void SAL_CALL setEraseAllInk( sal_Bool bEraseAllInk ) override; + virtual sal_Bool SAL_CALL isRunning( ) override; + virtual ::sal_Int32 SAL_CALL getSlideCount( ) override; + virtual css::uno::Reference< css::drawing::XDrawPage > SAL_CALL getSlideByIndex( ::sal_Int32 Index ) override; + virtual void SAL_CALL addSlideShowListener( const css::uno::Reference< css::presentation::XSlideShowListener >& Listener ) override; + virtual void SAL_CALL removeSlideShowListener( const css::uno::Reference< css::presentation::XSlideShowListener >& Listener ) override; + virtual void SAL_CALL gotoNextEffect( ) override; + virtual void SAL_CALL gotoPreviousEffect( ) override; + virtual void SAL_CALL gotoFirstSlide( ) override; + virtual void SAL_CALL gotoNextSlide( ) override; + virtual void SAL_CALL gotoPreviousSlide( ) override; + virtual void SAL_CALL gotoLastSlide( ) override; + virtual void SAL_CALL gotoBookmark( const OUString& Bookmark ) override; + virtual void SAL_CALL gotoSlide( const css::uno::Reference< css::drawing::XDrawPage >& Page ) override; + virtual void SAL_CALL gotoSlideIndex( ::sal_Int32 Index ) override; + virtual void SAL_CALL stopSound( ) override; + virtual void SAL_CALL pause( ) override; + virtual void SAL_CALL resume( ) override; + virtual sal_Bool SAL_CALL isPaused( ) override; + virtual void SAL_CALL blankScreen( ::sal_Int32 Color ) override; + virtual void SAL_CALL activate( ) override; + virtual void SAL_CALL deactivate( ) override; + virtual sal_Bool SAL_CALL isActive( ) override; + virtual css::uno::Reference< css::drawing::XDrawPage > SAL_CALL getCurrentSlide( ) override; + virtual ::sal_Int32 SAL_CALL getCurrentSlideIndex( ) override; + virtual ::sal_Int32 SAL_CALL getNextSlideIndex( ) override; + virtual sal_Bool SAL_CALL isEndless( ) override; + virtual sal_Bool SAL_CALL isFullScreen( ) override; + virtual css::uno::Reference< css::presentation::XSlideShow > SAL_CALL getSlideShow( ) override; + + // XIndexAccess + virtual ::sal_Int32 SAL_CALL getCount( ) override; + virtual css::uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override; + virtual css::uno::Type SAL_CALL getElementType( ) override; + virtual sal_Bool SAL_CALL hasElements( ) override; + + // will be called from the SlideShowListenerProxy when this event is fired from the XSlideShow + void slideEnded(const bool bReverse); + /// @throws css::uno::RuntimeException + void hyperLinkClicked(const OUString & hyperLink); + void click(const css::uno::Reference< css::drawing::XShape > & xShape); + bool swipe(const CommandSwipeData &rSwipeData); + bool longpress(const CommandLongPressData& rLongPressData); + + /// ends the presentation async + void endPresentation(); + + ViewShell* getViewShell() const { return mpViewShell; } + + void paint(); + bool keyInput(const KeyEvent& rKEvt); + void mouseButtonUp(const MouseEvent& rMEvt); + +private: + SlideshowImpl(SlideshowImpl const &) = delete; + void operator =(SlideshowImpl const &) = delete; + + virtual ~SlideshowImpl() override; + + // override WeakComponentImplHelperBase::disposing() + // This function is called upon disposing the component, + // if your component needs special work when it becomes + // disposed, do it here. + virtual void disposing(std::unique_lock<std::mutex>&) override; + + // internal + bool startShow( PresentationSettingsEx const * pPresSettings ); + bool startPreview( + const css::uno::Reference< css::drawing::XDrawPage >& xDrawPage, + const css::uno::Reference< css::animations::XAnimationNode >& xAnimationNode, + vcl::Window* pParent ); + + /** forces an async call to update in the main thread */ + void startUpdateTimer(); + + void update(); + + void createSlideList( bool bAll, std::u16string_view rPresSlide ); + + void displayCurrentSlide (const bool bSkipAllMainSequenceEffects = false); + + void displaySlideNumber( sal_Int32 nSlide ); + void displaySlideIndex( sal_Int32 nIndex ); + sal_Int32 getCurrentSlideNumber() const; + bool isInputFreezed() const { return mbInputFreeze; } + + void jumpToBookmark( const OUString& sBookmark ); + + void hideChildWindows(); + void showChildWindows(); + + void resize( const Size& rSize ); + + void setActiveXToolbarsVisible( bool bVisible ); + + DECL_LINK( updateHdl, Timer *, void ); + DECL_LINK( ReadyForNextInputHdl, Timer *, void ); + DECL_LINK( endPresentationHdl, void*, void ); + void ContextMenuSelectHdl(std::string_view rIdent); + DECL_LINK( ContextMenuHdl, void*, void ); + DECL_LINK( deactivateHdl, Timer *, void ); + DECL_LINK( EventListenerHdl, VclSimpleEvent&, void ); + + /** called only by the slideshow view when the first paint event occurs. + This actually starts the slideshow. */ + void onFirstPaint(); + + ::tools::Long getRestoreSlide() const { return mnRestoreSlide; } + +private: + bool startShowImpl( + const css::uno::Sequence< css::beans::PropertyValue >& aProperties ); + + SfxViewFrame* getViewFrame() const; + SfxDispatcher* getDispatcher() const; + SfxBindings* getBindings() const; + + sal_Int32 getSlideNumberForBookmark( const OUString& rStrBookmark ); + + void removeShapeEvents(); + void registerShapeEvents( sal_Int32 nSlideNumber ); + /// @throws css::uno::Exception + void registerShapeEvents( css::uno::Reference< css::drawing::XShapes > const & xShapes ); + + static css::uno::Reference< css::presentation::XSlideShow > createSlideShow(); + + static void setAutoSaveState( bool bOn ); + void gotoPreviousSlide (const bool bSkipAllMainSequenceEffects); + + /** Called by our maUpdateTimer's updateHdl handler this method is + responsible to call the slideshow update() method and, depending on + its return value, wait for a certain amount of time before another + call to update() is scheduled. + */ + void updateSlideShow(); + + css::uno::Reference< css::presentation::XSlideShow > mxShow; + rtl::Reference<sd::SlideShowView> mxView; + css::uno::Reference< css::frame::XModel > mxModel; + + Timer maUpdateTimer; + Timer maInputFreezeTimer; + Timer maDeactivateTimer; + + ::sd::View* mpView; + ViewShell* mpViewShell; + DrawDocShell* mpDocSh; + SdDrawDocument* mpDoc; + + VclPtr<vcl::Window> mpParentWindow; + VclPtr<sd::ShowWindow> mpShowWindow; + + std::shared_ptr< AnimationSlideController > mpSlideController; + + ::tools::Long mnRestoreSlide; + Point maPopupMousePos; + Size maPresSize; + AnimationMode meAnimationMode; + OUString maCharBuffer; + VclPtr< ::sd::Window> mpOldActiveWindow; + Link<StarBASIC*,bool> maStarBASICGlobalErrorHdl; + ::tools::ULong mnChildMask; + bool mbDisposed; + bool mbAutoSaveWasOn; + bool mbRehearseTimings; + bool mbIsPaused; + bool mbWasPaused; // used to cache pause state during context menu + bool mbInputFreeze; + bool mbActive; + + PresentationSettings maPresSettings; + sal_Int32 mnUserPaintColor; + + bool mbUsePen; + double mdUserPaintStrokeWidth; + + std::map< css::uno::Reference< css::drawing::XShape >, WrappedShapeEventImplPtr > + maShapeEventMap; + + css::uno::Reference< css::drawing::XDrawPage > mxPreviewDrawPage; + css::uno::Reference< css::animations::XAnimationNode > mxPreviewAnimationNode; + + css::uno::Reference< css::media::XPlayer > mxPlayer; + + ::std::unique_ptr<PaneHider> mpPaneHider; + + ImplSVEvent * mnEndShowEvent; + ImplSVEvent * mnContextMenuEvent; + + css::uno::Reference< css::presentation::XPresentation2 > mxPresentation; + ::rtl::Reference< SlideShowListenerProxy > mxListenerProxy; +}; + +} // namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/slideshowviewimpl.cxx b/sd/source/ui/slideshow/slideshowviewimpl.cxx new file mode 100644 index 000000000..d6addc3f8 --- /dev/null +++ b/sd/source/ui/slideshow/slideshowviewimpl.cxx @@ -0,0 +1,626 @@ +/* -*- 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 "slideshowviewimpl.hxx" +#include "slideshowimpl.hxx" +#include <sdpage.hxx> + +#include <vcl/svapp.hxx> + +#include <com/sun/star/awt/Pointer.hpp> +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/awt/XWindowPeer.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> + +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> + +#include <cppcanvas/vclfactory.hxx> +#include <cppcanvas/basegfxfactory.hxx> +#include <basegfx/utils/canvastools.hxx> + +#include <toolkit/helper/vclunohelper.hxx> +#include <comphelper/processfactory.hxx> + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::WeakReference; +using ::com::sun::star::uno::Exception; + +using namespace ::com::sun::star; + +namespace sd +{ + +void SlideShowViewMouseListeners::notify( std::unique_lock<std::mutex>& rGuard, const WrappedMouseEvent& rEvent ) +{ + forEach(rGuard, + [&rEvent] (const Reference<css::awt::XMouseListener>& rListener) + { + switch( rEvent.meType ) + { + case WrappedMouseEvent::PRESSED: + rListener->mousePressed( rEvent.maEvent ); + break; + + case WrappedMouseEvent::RELEASED: + rListener->mouseReleased( rEvent.maEvent ); + break; + + case WrappedMouseEvent::ENTERED: + rListener->mouseEntered( rEvent.maEvent ); + break; + + case WrappedMouseEvent::EXITED: + rListener->mouseExited( rEvent.maEvent ); + break; + } + }); +} + + +void SlideShowViewMouseMotionListeners::notify( std::unique_lock<std::mutex>& rGuard,const WrappedMouseMotionEvent& rEvent ) +{ + forEach(rGuard, + [&rEvent] (const Reference< awt::XMouseMotionListener >& rListener) + { + switch( rEvent.meType ) + { + case WrappedMouseMotionEvent::DRAGGED: + rListener->mouseDragged( rEvent.maEvent ); + break; + + case WrappedMouseMotionEvent::MOVED: + rListener->mouseMoved( rEvent.maEvent ); + break; + } + }); +} + +// SlideShowView +SlideShowView::SlideShowView( ShowWindow& rOutputWindow, + SdDrawDocument* pDoc, + AnimationMode eAnimationMode, + SlideshowImpl* pSlideShow, + bool bFullScreen ) +: mpCanvas( ::cppcanvas::VCLFactory::createSpriteCanvas( rOutputWindow ) ), + mxWindow( VCLUnoHelper::GetInterface( &rOutputWindow ), uno::UNO_SET_THROW ), + mxWindowPeer( mxWindow, uno::UNO_QUERY_THROW ), + mpSlideShow( pSlideShow ), + mrOutputWindow( rOutputWindow ), + mpDoc( pDoc ), + mbIsMouseMotionListener( false ), + meAnimationMode( eAnimationMode ), + mbFirstPaint( true ), + mbMousePressedEaten( false ) +{ + mxWindow->addWindowListener( this ); + mxWindow->addMouseListener( this ); + + mxPointer = awt::Pointer::create( ::comphelper::getProcessComponentContext() ); + + getTransformation(); + + // #i48939# only switch on kind of hacky scroll optimization, when + // running fullscreen. this minimizes the probability that other + // windows partially cover the show. + if( bFullScreen ) + { + try + { + Reference< beans::XPropertySet > xCanvasProps( getCanvas(), + uno::UNO_QUERY_THROW ); + xCanvasProps->setPropertyValue("UnsafeScrolling", + uno::Any( true ) ); + } + catch( uno::Exception& ) + { + } + } + + mTranslationOffset.Width = 0; + mTranslationOffset.Height = 0; +} + +// Dispose all internal references +void SlideShowView::disposing(std::unique_lock<std::mutex>& rGuard) +{ + mpSlideShow = nullptr; + + // deregister listeners + if( mxWindow.is() ) + { + mxWindow->removeWindowListener( this ); + mxWindow->removeMouseListener( this ); + + if( mbIsMouseMotionListener ) + mxWindow->removeMouseMotionListener( this ); + } + + mpCanvas.reset(); + mxWindow.clear(); + + // clear all listener containers + disposingImpl(rGuard); +} + +// Disposing our broadcaster +void SAL_CALL SlideShowView::disposing( const lang::EventObject& ) +{ + std::unique_lock aGuard( m_aMutex ); + + disposingImpl(aGuard); +} + +// Disposing our broadcaster +void SlideShowView::disposingImpl(std::unique_lock<std::mutex>& rGuard) +{ + // notify all listeners that _we_ are going down (send a disposing()), + // then delete listener containers: + lang::EventObject const evt( static_cast<OWeakObject *>(this) ); + if (!maViewListeners.empty()) + { + auto tmp = std::move(maViewListeners); + rGuard.unlock(); + for( const auto& rxListener : tmp ) + { + Reference< util::XModifyListener > xListener( rxListener ); + if( xListener.is() ) + xListener->disposing( evt ); + } + rGuard.lock(); + } + if (maPaintListeners.getLength(rGuard)) + { + maPaintListeners.disposeAndClear( rGuard, evt ); + rGuard.lock(); + } + if (maMouseListeners.getLength(rGuard)) + { + maMouseListeners.disposeAndClear( rGuard, evt ); + rGuard.lock(); + } + if (maMouseMotionListeners.getLength(rGuard)) + { + maMouseMotionListeners.disposeAndClear( rGuard, evt ); + rGuard.lock(); + } +} + +void SlideShowView::paint( const awt::PaintEvent& e ) +{ + std::unique_lock aGuard( m_aMutex ); + + if( mbFirstPaint ) + { + mbFirstPaint = false; + SlideshowImpl* pSlideShow = mpSlideShow; + aGuard.unlock(); + if( pSlideShow ) + pSlideShow->onFirstPaint(); + } + else + { + // Change event source, to enable listeners to match event + // with view + awt::PaintEvent aEvent( e ); + aEvent.Source = static_cast< ::cppu::OWeakObject* >( this ); + maPaintListeners.notifyEach( aGuard, &css::awt::XPaintListener::windowPaint, aEvent ); + updateimpl( aGuard, mpSlideShow ); // warning: clears guard! + } +} + +// XSlideShowView methods +Reference< rendering::XSpriteCanvas > SAL_CALL SlideShowView::getCanvas( ) +{ + std::unique_lock aGuard( m_aMutex ); + + return mpCanvas ? mpCanvas->getUNOSpriteCanvas() : Reference< rendering::XSpriteCanvas >(); +} + +void SAL_CALL SlideShowView::clear() +{ + // paint background in black + std::unique_lock aGuard( m_aMutex ); + SolarMutexGuard aSolarGuard; + + // fill the bounds rectangle in black + + const Size aWindowSize( mrOutputWindow.GetSizePixel() ); + + ::basegfx::B2DPolygon aPoly( ::basegfx::utils::createPolygonFromRect( + ::basegfx::B2DRectangle(0.0,0.0, + aWindowSize.Width(), + aWindowSize.Height() ) ) ); + ::cppcanvas::PolyPolygonSharedPtr pPolyPoly( + ::cppcanvas::BaseGfxFactory::createPolyPolygon( mpCanvas, aPoly ) ); + + if( pPolyPoly ) + { + pPolyPoly->setRGBAFillColor( 0x000000FFU ); + pPolyPoly->draw(); + } +} + +geometry::IntegerSize2D SAL_CALL SlideShowView::getTranslationOffset( ) +{ + return mTranslationOffset; +} + +geometry::AffineMatrix2D SAL_CALL SlideShowView::getTransformation( ) +{ + std::unique_lock aGuard( m_aMutex ); + SolarMutexGuard aSolarGuard; + + const Size& rTmpSize( mrOutputWindow.GetSizePixel() ); + + if (rTmpSize.IsEmpty()) + { + return geometry::AffineMatrix2D (1,0,0,0,1,0); + } + + const Size aWindowSize( mrOutputWindow.GetSizePixel() ); + Size aOutputSize( aWindowSize ); + + if( meAnimationMode != ANIMATIONMODE_SHOW ) + { + aOutputSize.setWidth( static_cast<::tools::Long>( aOutputSize.Width() / 1.03 ) ); + aOutputSize.setHeight( static_cast<::tools::Long>( aOutputSize.Height() / 1.03 ) ); + } + + SdPage* pP = mpDoc->GetSdPage( 0, PageKind::Standard ); + Size aPageSize( pP->GetSize() ); + + const double page_ratio = static_cast<double>(aPageSize.Width()) / static_cast<double>(aPageSize.Height()); + const double output_ratio = static_cast<double>(aOutputSize.Width()) / static_cast<double>(aOutputSize.Height()); + + if( page_ratio > output_ratio ) + { + aOutputSize.setHeight( ( aOutputSize.Width() * aPageSize.Height() ) / aPageSize.Width() ); + } + else if( page_ratio < output_ratio ) + { + aOutputSize.setWidth( ( aOutputSize.Height() * aPageSize.Width() ) / aPageSize.Height() ); + } + + Point aOutputOffset( ( aWindowSize.Width() - aOutputSize.Width() ) >> 1, + ( aWindowSize.Height() - aOutputSize.Height() ) >> 1 ); + + // Reduce available width by one, as the slides might actually + // render one pixel wider and higher as aPageSize below specifies + // (when shapes of page size have visible border lines) + aOutputSize.AdjustWidth( -1 ); + aOutputSize.AdjustHeight( -1 ); + + // Record mTranslationOffset + mTranslationOffset.Height = aOutputOffset.Y(); + mTranslationOffset.Width = aOutputOffset.X(); + + // scale presentation into available window rect (minus 10%); center in the window + const basegfx::B2DHomMatrix aMatrix(basegfx::utils::createScaleTranslateB2DHomMatrix( + aOutputSize.Width(), aOutputSize.Height(), aOutputOffset.X(), aOutputOffset.Y())); + + geometry::AffineMatrix2D aRes; + + return ::basegfx::unotools::affineMatrixFromHomMatrix( aRes, aMatrix ); +} + +void SAL_CALL SlideShowView::addTransformationChangedListener( const Reference< util::XModifyListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + + if (m_bDisposed) + return; + WeakReference< util::XModifyListener > xWeak( xListener ); + if( std::find( maViewListeners.begin(), maViewListeners.end(), xWeak ) == maViewListeners.end() ) + maViewListeners.push_back( xWeak ); +} + +void SAL_CALL SlideShowView::removeTransformationChangedListener( const Reference< util::XModifyListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + + if (m_bDisposed) + return; + WeakReference< util::XModifyListener > xWeak( xListener ); + auto aIter( std::find( maViewListeners.begin(), maViewListeners.end(), xWeak ) ); + if( aIter != maViewListeners.end() ) + maViewListeners.erase( aIter ); +} + +void SAL_CALL SlideShowView::addPaintListener( const Reference< awt::XPaintListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + + if (!m_bDisposed) + maPaintListeners.addInterface( aGuard, xListener ); +} + +void SAL_CALL SlideShowView::removePaintListener( const Reference< awt::XPaintListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + + if (!m_bDisposed) + maPaintListeners.removeInterface( aGuard, xListener ); +} + +void SAL_CALL SlideShowView::addMouseListener( const Reference< awt::XMouseListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + + if (!m_bDisposed) + maMouseListeners.addInterface( aGuard, xListener ); +} + +void SAL_CALL SlideShowView::removeMouseListener( const Reference< awt::XMouseListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + + if (!m_bDisposed) + maMouseListeners.removeInterface( aGuard, xListener ); +} + +void SAL_CALL SlideShowView::addMouseMotionListener( const Reference< awt::XMouseMotionListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + + if (m_bDisposed) + return; + + if( !mbIsMouseMotionListener && mxWindow.is() ) + { + // delay motion event registration, until we really + // need it + mbIsMouseMotionListener = true; + mxWindow->addMouseMotionListener( this ); + } + + maMouseMotionListeners.addInterface( aGuard, xListener ); +} + +void SAL_CALL SlideShowView::removeMouseMotionListener( const Reference< awt::XMouseMotionListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + + if (!m_bDisposed) + maMouseMotionListeners.removeInterface( aGuard, xListener ); + + // TODO(P1): Might be nice to deregister for mouse motion + // events, when the last listener is gone. +} + +void SAL_CALL SlideShowView::setMouseCursor( sal_Int16 nPointerShape ) +{ + std::unique_lock aGuard( m_aMutex ); + + // forward to window + if( mxPointer.is() ) + mxPointer->setType( nPointerShape ); + + if( mxWindowPeer.is() ) + mxWindowPeer->setPointer( mxPointer ); +} + +awt::Rectangle SAL_CALL SlideShowView::getCanvasArea( ) +{ + awt::Rectangle aRectangle; + + if( mxWindow.is() ) + return mxWindow->getPosSize(); + + aRectangle.X = aRectangle.Y = aRectangle.Width = aRectangle.Height = 0; + + return aRectangle; +} + +void SlideShowView::updateimpl( std::unique_lock<std::mutex>& rGuard, SlideshowImpl* pSlideShow ) +{ + if( !pSlideShow ) + return; + + ::rtl::Reference< SlideshowImpl > xKeepAlive( pSlideShow ); + + if( mbFirstPaint ) + { + mbFirstPaint = false; + SlideshowImpl* pTmpSlideShow = mpSlideShow; + rGuard.unlock(); + if( pTmpSlideShow ) + pTmpSlideShow->onFirstPaint(); + } else + rGuard.unlock(); + + pSlideShow->startUpdateTimer(); +} + +// XWindowListener methods +void SAL_CALL SlideShowView::windowResized( const awt::WindowEvent& e ) +{ + std::unique_lock aGuard( m_aMutex ); + + if (m_bDisposed) + return; + + if (!maViewListeners.empty()) + { + // Change event source, to enable listeners to match event + // with view + awt::WindowEvent aEvent( e ); + aEvent.Source = static_cast< ::cppu::OWeakObject* >( this ); + auto aIter( maViewListeners.begin() ); + while( aIter != maViewListeners.end() ) + { + Reference< util::XModifyListener > xListener( *aIter ); + if( xListener.is() ) + { + aGuard.unlock(); + xListener->modified( aEvent ); + aGuard.lock(); + ++aIter; + } + else + { + aIter = maViewListeners.erase( aIter ); + } + } + } + + updateimpl( aGuard, mpSlideShow ); // warning: clears guard! +} + +void SAL_CALL SlideShowView::windowMoved( const awt::WindowEvent& ) +{ + // ignored +} + +void SAL_CALL SlideShowView::windowShown( const lang::EventObject& ) +{ + // ignored +} + +void SAL_CALL SlideShowView::windowHidden( const lang::EventObject& ) +{ + // ignored +} + +// XMouseListener implementation +void SAL_CALL SlideShowView::mousePressed( const awt::MouseEvent& e ) +{ + std::unique_lock aGuard( m_aMutex ); + if (m_bDisposed) + return; + + if( mpSlideShow && mpSlideShow->isInputFreezed() ) + { + mbMousePressedEaten = true; + } + else + { + mbMousePressedEaten = false; + + // Change event source, to enable listeners to match event + // with view + WrappedMouseEvent aEvent; + aEvent.meType = WrappedMouseEvent::PRESSED; + aEvent.maEvent = e; + aEvent.maEvent.Source = static_cast< ::cppu::OWeakObject* >( this ); + + maMouseListeners.notify( aGuard, aEvent ); + updateimpl( aGuard, mpSlideShow ); // warning: clears guard! + } +} + +void SAL_CALL SlideShowView::mouseReleased( const awt::MouseEvent& e ) +{ + std::unique_lock aGuard( m_aMutex ); + if (m_bDisposed) + return; + + if( mbMousePressedEaten ) + { + // if mouse button down was ignored, also ignore mouse button up + mbMousePressedEaten = false; + } + else if( mpSlideShow && !mpSlideShow->isInputFreezed() ) + { + // Change event source, to enable listeners to match event + // with view + WrappedMouseEvent aEvent; + aEvent.meType = WrappedMouseEvent::RELEASED; + aEvent.maEvent = e; + aEvent.maEvent.Source = static_cast< ::cppu::OWeakObject* >( this ); + + maMouseListeners.notify( aGuard, aEvent ); + updateimpl( aGuard, mpSlideShow ); // warning: clears guard! + } +} + +void SAL_CALL SlideShowView::mouseEntered( const awt::MouseEvent& e ) +{ + std::unique_lock aGuard( m_aMutex ); + if (m_bDisposed) + return; + + // Change event source, to enable listeners to match event + // with view + WrappedMouseEvent aEvent; + aEvent.meType = WrappedMouseEvent::ENTERED; + aEvent.maEvent = e; + aEvent.maEvent.Source = static_cast< ::cppu::OWeakObject* >( this ); + + maMouseListeners.notify( aGuard, aEvent ); + updateimpl( aGuard, mpSlideShow ); // warning: clears guard! +} + +void SAL_CALL SlideShowView::mouseExited( const awt::MouseEvent& e ) +{ + std::unique_lock aGuard( m_aMutex ); + if (m_bDisposed) + return; + + // Change event source, to enable listeners to match event + // with view + WrappedMouseEvent aEvent; + aEvent.meType = WrappedMouseEvent::EXITED; + aEvent.maEvent = e; + aEvent.maEvent.Source = static_cast< ::cppu::OWeakObject* >( this ); + + maMouseListeners.notify( aGuard, aEvent ); + updateimpl( aGuard, mpSlideShow ); // warning: clears guard! +} + +// XMouseMotionListener implementation +void SAL_CALL SlideShowView::mouseDragged( const awt::MouseEvent& e ) +{ + std::unique_lock aGuard( m_aMutex ); + if (m_bDisposed) + return; + + // Change event source, to enable listeners to match event + // with view + WrappedMouseMotionEvent aEvent; + aEvent.meType = WrappedMouseMotionEvent::DRAGGED; + aEvent.maEvent = e; + aEvent.maEvent.Source = static_cast< ::cppu::OWeakObject* >( this ); + + maMouseMotionListeners.notify( aGuard, aEvent ); + updateimpl( aGuard, mpSlideShow ); // warning: clears guard! +} + +void SAL_CALL SlideShowView::mouseMoved( const awt::MouseEvent& e ) +{ + std::unique_lock aGuard( m_aMutex ); + if (m_bDisposed) + return; + + // Change event source, to enable listeners to match event + // with view + WrappedMouseMotionEvent aEvent; + aEvent.meType = WrappedMouseMotionEvent::MOVED; + aEvent.maEvent = e; + aEvent.maEvent.Source = static_cast< ::cppu::OWeakObject* >( this ); + + maMouseMotionListeners.notify( aGuard, aEvent ); + updateimpl( aGuard, mpSlideShow ); // warning: clears guard! +} + +} // namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/slideshowviewimpl.hxx b/sd/source/ui/slideshow/slideshowviewimpl.hxx new file mode 100644 index 000000000..3a5018be4 --- /dev/null +++ b/sd/source/ui/slideshow/slideshowviewimpl.hxx @@ -0,0 +1,182 @@ +/* -*- 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 . + */ + +#pragma once + +#include <comphelper/compbase.hxx> +#include <comphelper/interfacecontainer4.hxx> +#include <com/sun/star/awt/XWindowListener.hpp> +#include <com/sun/star/util/XModifyListener.hpp> +#include <com/sun/star/awt/XPaintListener.hpp> +#include <com/sun/star/presentation/XSlideShowView.hpp> +#include <cppcanvas/spritecanvas.hxx> +#include <cppuhelper/weakref.hxx> + +#include <slideshow.hxx> + +namespace com::sun::star::awt { class XPointer; } +namespace com::sun::star::awt { class XWindow; } +namespace com::sun::star::awt { class XWindowPeer; } +namespace com::sun::star::awt { struct WindowEvent; } +namespace com::sun::star::rendering { class XSpriteCanvas; } +class SdDrawDocument; + +namespace sd +{ + +struct WrappedMouseEvent : public css::lang::EventObject +{ + enum EventType + { + PRESSED, + RELEASED, + ENTERED, + EXITED + }; + + EventType meType; + css::awt::MouseEvent maEvent; +}; + +struct WrappedMouseMotionEvent : public css::lang::EventObject +{ + enum EventType + { + DRAGGED, + MOVED + }; + + EventType meType; + css::awt::MouseEvent maEvent; +}; + +// SlideShowViewPaintListeners +typedef ::comphelper::OInterfaceContainerHelper4< css::awt::XPaintListener > SlideShowViewPaintListeners; + + +// SlideShowViewMouseListeners +typedef ::comphelper::OInterfaceContainerHelper4< css::awt::XMouseListener > SlideShowViewMouseListeners_Base; + +class SlideShowViewMouseListeners : public SlideShowViewMouseListeners_Base +{ +public: + void notify(std::unique_lock<std::mutex>& rGuard, const WrappedMouseEvent& rEvent); +}; + + +// SlideShowViewMouseMotionListeners +typedef ::comphelper::OInterfaceContainerHelper4< css::awt::XMouseMotionListener > SlideShowViewMouseMotionListeners_Base; + +class SlideShowViewMouseMotionListeners : public SlideShowViewMouseMotionListeners_Base +{ +public: + void notify( std::unique_lock<std::mutex>& rGuard, const WrappedMouseMotionEvent& rEvent ); +}; + +// SlideShowView +class ShowWindow; +class SlideshowImpl; + +typedef comphelper::WeakComponentImplHelper< css::presentation::XSlideShowView, + css::awt::XWindowListener, + css::awt::XMouseListener, + css::awt::XMouseMotionListener > SlideShowView_Base; + +class SlideShowView final : public SlideShowView_Base +{ +public: + SlideShowView( ShowWindow& rOutputWindow, + SdDrawDocument* pDoc, + AnimationMode eAnimationMode, + SlideshowImpl* pSlideShow, + bool bFullScreen ); + + void ignoreNextMouseReleased() { mbMousePressedEaten = true; } + + /// Dispose all internal references + virtual void disposing(std::unique_lock<std::mutex>&) override; + + /// Disposing our broadcaster + virtual void SAL_CALL disposing( const css::lang::EventObject& ) override; + + /// @throws css::uno::RuntimeException + void paint( const css::awt::PaintEvent& e ); + + // XSlideShowView methods + virtual css::uno::Reference< css::rendering::XSpriteCanvas > SAL_CALL getCanvas( ) override; + virtual void SAL_CALL clear( ) override; + virtual css::geometry::AffineMatrix2D SAL_CALL getTransformation( ) override; + virtual css::geometry::IntegerSize2D SAL_CALL getTranslationOffset( ) override; + virtual void SAL_CALL addTransformationChangedListener( const css::uno::Reference< css::util::XModifyListener >& xListener ) override; + virtual void SAL_CALL removeTransformationChangedListener( const css::uno::Reference< css::util::XModifyListener >& xListener ) override; + virtual void SAL_CALL addPaintListener( const css::uno::Reference< css::awt::XPaintListener >& xListener ) override; + virtual void SAL_CALL removePaintListener( const css::uno::Reference< css::awt::XPaintListener >& xListener ) override; + virtual void SAL_CALL addMouseListener( const css::uno::Reference< css::awt::XMouseListener >& xListener ) override; + virtual void SAL_CALL removeMouseListener( const css::uno::Reference< css::awt::XMouseListener >& xListener ) override; + virtual void SAL_CALL addMouseMotionListener( const css::uno::Reference< css::awt::XMouseMotionListener >& xListener ) override; + virtual void SAL_CALL removeMouseMotionListener( const css::uno::Reference< css::awt::XMouseMotionListener >& xListener ) override; + virtual void SAL_CALL setMouseCursor( sal_Int16 nPointerShape ) override; + virtual css::awt::Rectangle SAL_CALL getCanvasArea( ) override; + + // XWindowListener methods + virtual void SAL_CALL windowResized( const css::awt::WindowEvent& e ) override; + virtual void SAL_CALL windowMoved( const css::awt::WindowEvent& e ) override; + virtual void SAL_CALL windowShown( const css::lang::EventObject& e ) override; + virtual void SAL_CALL windowHidden( const css::lang::EventObject& e ) override; + + // XMouseListener implementation + virtual void SAL_CALL mousePressed( const css::awt::MouseEvent& e ) override; + virtual void SAL_CALL mouseReleased( const css::awt::MouseEvent& e ) override; + virtual void SAL_CALL mouseEntered( const css::awt::MouseEvent& e ) override; + virtual void SAL_CALL mouseExited( const css::awt::MouseEvent& e ) override; + + // XMouseMotionListener implementation + virtual void SAL_CALL mouseDragged( const css::awt::MouseEvent& e ) override; + virtual void SAL_CALL mouseMoved( const css::awt::MouseEvent& e ) override; + +protected: + virtual ~SlideShowView() override {} + +private: + void updateimpl( std::unique_lock<std::mutex>& rGuard, SlideshowImpl* pSlideShow ); + + void disposingImpl( std::unique_lock<std::mutex>& ); + + ::cppcanvas::SpriteCanvasSharedPtr mpCanvas; + css::uno::Reference< css::awt::XWindow > mxWindow; + css::uno::Reference< css::awt::XWindowPeer > mxWindowPeer; + css::uno::Reference< css::awt::XPointer > mxPointer; + SlideshowImpl* mpSlideShow; + ShowWindow& mrOutputWindow; + std::vector< css::uno::WeakReference< css::util::XModifyListener > > + maViewListeners; + SlideShowViewPaintListeners maPaintListeners; + SlideShowViewMouseListeners maMouseListeners; + SlideShowViewMouseMotionListeners maMouseMotionListeners; + SdDrawDocument* mpDoc; + bool mbIsMouseMotionListener; + AnimationMode meAnimationMode; + bool mbFirstPaint; + bool mbMousePressedEaten; + css::geometry::IntegerSize2D mTranslationOffset; +}; + +} // namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsBitmapCache.cxx b/sd/source/ui/slidesorter/cache/SlsBitmapCache.cxx new file mode 100644 index 000000000..87c727408 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsBitmapCache.cxx @@ -0,0 +1,550 @@ +/* -*- 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 <memory> +#include <unordered_map> +#include "SlsBitmapCache.hxx" +#include "SlsCacheCompactor.hxx" +#include "SlsBitmapCompressor.hxx" +#include "SlsCacheConfiguration.hxx" + +#include <sal/log.hxx> + +// Define the default value for the maximal cache size that is used for +// previews that are currently not visible. The visible previews are all +// held in memory at all times. This default is used only when the +// configuration does not have a value. +const sal_Int32 MAXIMAL_CACHE_SIZE = 4*1024L*1024L; + +using namespace ::com::sun::star::uno; + +namespace sd::slidesorter::cache { + +class BitmapCache::CacheEntry +{ +public: + CacheEntry(const BitmapEx& rBitmap, sal_Int32 nLastAccessTime, bool bIsPrecious); + CacheEntry(sal_Int32 nLastAccessTime, bool bIsPrecious); + inline void Recycle (const CacheEntry& rEntry); + inline sal_Int32 GetMemorySize() const; + void Compress (const std::shared_ptr<BitmapCompressor>& rpCompressor); + inline void Decompress(); + + bool IsUpToDate() const { return mbIsUpToDate; } + void SetUpToDate (bool bIsUpToDate) { mbIsUpToDate = bIsUpToDate; } + sal_Int32 GetAccessTime() const { return mnLastAccessTime; } + void SetAccessTime (sal_Int32 nAccessTime) { mnLastAccessTime = nAccessTime; } + + const BitmapEx& GetPreview() const { return maPreview; } + inline void SetPreview (const BitmapEx& rPreview); + bool HasPreview() const; + + const BitmapEx& GetMarkedPreview() const { return maMarkedPreview; } + inline void SetMarkedPreview (const BitmapEx& rMarkePreview); + + bool HasReplacement() const { return (mpReplacement != nullptr); } + inline bool HasLosslessReplacement() const; + void Invalidate() { mpReplacement.reset(); mpCompressor.reset(); mbIsUpToDate = false; } + bool IsPrecious() const { return mbIsPrecious; } + void SetPrecious (bool bIsPrecious) { mbIsPrecious = bIsPrecious; } + +private: + BitmapEx maPreview; + BitmapEx maMarkedPreview; + std::shared_ptr<BitmapReplacement> mpReplacement; + std::shared_ptr<BitmapCompressor> mpCompressor; + bool mbIsUpToDate; + sal_Int32 mnLastAccessTime; + // When this flag is set then the bitmap is not modified by a cache + // compactor. + bool mbIsPrecious; +}; + +namespace { + +class CacheHash { +public: + size_t operator()(const BitmapCache::CacheKey& p) const + { return reinterpret_cast<size_t>(p); } +}; + +} + +class BitmapCache::CacheBitmapContainer + : public std::unordered_map<CacheKey, CacheEntry, CacheHash> +{ +public: + CacheBitmapContainer() {} +}; + +namespace { + +typedef ::std::vector< + ::std::pair< ::sd::slidesorter::cache::BitmapCache::CacheKey, + ::sd::slidesorter::cache::BitmapCache::CacheEntry> + > SortableBitmapContainer; + + /** Compare elements of the bitmap cache according to their last access + time. + */ + class AccessTimeComparator + { + public: + bool operator () ( + const SortableBitmapContainer::value_type& e1, + const SortableBitmapContainer::value_type& e2) + { + return e1.second.GetAccessTime() < e2.second.GetAccessTime(); + } + }; + +} // end of anonymous namespace + +//===== BitmapCache ========================================================= + +BitmapCache::BitmapCache () + : mpBitmapContainer(new CacheBitmapContainer()), + mnNormalCacheSize(0), + mnPreciousCacheSize(0), + mnCurrentAccessTime(0), + mnMaximalNormalCacheSize(MAXIMAL_CACHE_SIZE), + mbIsFull(false) +{ + Any aCacheSize (CacheConfiguration::Instance()->GetValue("CacheSize")); + if (aCacheSize.has<sal_Int32>()) + aCacheSize >>= mnMaximalNormalCacheSize; + + mpCacheCompactor = CacheCompactor::Create(*this,mnMaximalNormalCacheSize); +} + +BitmapCache::~BitmapCache() +{ + Clear(); +} + +void BitmapCache::Clear() +{ + ::osl::MutexGuard aGuard (maMutex); + + mpBitmapContainer->clear(); + mnNormalCacheSize = 0; + mnPreciousCacheSize = 0; + mnCurrentAccessTime = 0; +} + +bool BitmapCache::HasBitmap (const CacheKey& rKey) +{ + ::osl::MutexGuard aGuard (maMutex); + + CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); + return (iEntry != mpBitmapContainer->end() + && (iEntry->second.HasPreview() || iEntry->second.HasReplacement())); +} + +bool BitmapCache::BitmapIsUpToDate (const CacheKey& rKey) +{ + ::osl::MutexGuard aGuard (maMutex); + + bool bIsUpToDate = false; + CacheBitmapContainer::iterator aIterator (mpBitmapContainer->find(rKey)); + if (aIterator != mpBitmapContainer->end()) + bIsUpToDate = aIterator->second.IsUpToDate(); + + return bIsUpToDate; +} + +BitmapEx BitmapCache::GetBitmap (const CacheKey& rKey) +{ + ::osl::MutexGuard aGuard (maMutex); + + CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); + if (iEntry == mpBitmapContainer->end()) + { + // Create an empty bitmap for the given key that acts as placeholder + // until we are given the real one. Mark it as not being up to date. + SetBitmap(rKey, BitmapEx(), false); + iEntry = mpBitmapContainer->find(rKey); + iEntry->second.SetUpToDate(false); + } + else + { + iEntry->second.SetAccessTime(mnCurrentAccessTime++); + + // Maybe we have to decompress the preview. + if ( ! iEntry->second.HasPreview() && iEntry->second.HasReplacement()) + { + UpdateCacheSize(iEntry->second, REMOVE); + iEntry->second.Decompress(); + UpdateCacheSize(iEntry->second, ADD); + } + } + return iEntry->second.GetPreview(); +} + +BitmapEx BitmapCache::GetMarkedBitmap (const CacheKey& rKey) +{ + ::osl::MutexGuard aGuard (maMutex); + + CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); + if (iEntry != mpBitmapContainer->end()) + { + iEntry->second.SetAccessTime(mnCurrentAccessTime++); + return iEntry->second.GetMarkedPreview(); + } + else + return BitmapEx(); +} + +void BitmapCache::ReleaseBitmap (const CacheKey& rKey) +{ + ::osl::MutexGuard aGuard (maMutex); + + CacheBitmapContainer::iterator aIterator (mpBitmapContainer->find(rKey)); + if (aIterator != mpBitmapContainer->end()) + { + UpdateCacheSize(aIterator->second, REMOVE); + mpBitmapContainer->erase(aIterator); + } +} + +bool BitmapCache::InvalidateBitmap (const CacheKey& rKey) +{ + ::osl::MutexGuard aGuard (maMutex); + + CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); + if (iEntry != mpBitmapContainer->end()) + { + iEntry->second.SetUpToDate(false); + + // When there is a preview then we release the replacement. The + // preview itself is kept until a new one is created. + if (iEntry->second.HasPreview()) + { + UpdateCacheSize(iEntry->second, REMOVE); + iEntry->second.Invalidate(); + UpdateCacheSize(iEntry->second, ADD); + } + return true; + } + else + return false; +} + +void BitmapCache::InvalidateCache() +{ + ::osl::MutexGuard aGuard (maMutex); + + for (auto& rEntry : *mpBitmapContainer) + { + rEntry.second.Invalidate(); + } + ReCalculateTotalCacheSize(); +} + +void BitmapCache::SetBitmap ( + const CacheKey& rKey, + const BitmapEx& rPreview, + bool bIsPrecious) +{ + ::osl::MutexGuard aGuard (maMutex); + + CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); + if (iEntry != mpBitmapContainer->end()) + { + UpdateCacheSize(iEntry->second, REMOVE); + iEntry->second.SetPreview(rPreview); + iEntry->second.SetUpToDate(true); + iEntry->second.SetAccessTime(mnCurrentAccessTime++); + } + else + { + iEntry = mpBitmapContainer->emplace( + rKey, + CacheEntry(rPreview, mnCurrentAccessTime++, bIsPrecious) + ).first; + } + + if (iEntry != mpBitmapContainer->end()) + UpdateCacheSize(iEntry->second, ADD); +} + +void BitmapCache::SetMarkedBitmap ( + const CacheKey& rKey, + const BitmapEx& rPreview) +{ + ::osl::MutexGuard aGuard (maMutex); + + CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); + if (iEntry != mpBitmapContainer->end()) + { + UpdateCacheSize(iEntry->second, REMOVE); + iEntry->second.SetMarkedPreview(rPreview); + iEntry->second.SetAccessTime(mnCurrentAccessTime++); + UpdateCacheSize(iEntry->second, ADD); + } +} + +void BitmapCache::SetPrecious (const CacheKey& rKey, bool bIsPrecious) +{ + ::osl::MutexGuard aGuard (maMutex); + + CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); + if (iEntry != mpBitmapContainer->end()) + { + if (iEntry->second.IsPrecious() != bIsPrecious) + { + UpdateCacheSize(iEntry->second, REMOVE); + iEntry->second.SetPrecious(bIsPrecious); + UpdateCacheSize(iEntry->second, ADD); + } + } + else if (bIsPrecious) + { + iEntry = mpBitmapContainer->emplace( + rKey, + CacheEntry(BitmapEx(), mnCurrentAccessTime++, bIsPrecious) + ).first; + UpdateCacheSize(iEntry->second, ADD); + } +} + +void BitmapCache::ReCalculateTotalCacheSize() +{ + ::osl::MutexGuard aGuard (maMutex); + + mnNormalCacheSize = 0; + mnPreciousCacheSize = 0; + for (const auto& rEntry : *mpBitmapContainer) + { + if (rEntry.second.IsPrecious()) + mnPreciousCacheSize += rEntry.second.GetMemorySize(); + else + mnNormalCacheSize += rEntry.second.GetMemorySize(); + } + mbIsFull = (mnNormalCacheSize >= mnMaximalNormalCacheSize); + + SAL_INFO("sd.sls", __func__ << ": cache size is " << mnNormalCacheSize << "/" << mnPreciousCacheSize); +} + +void BitmapCache::Recycle (const BitmapCache& rCache) +{ + ::osl::MutexGuard aGuard (maMutex); + + for (const auto& rOtherEntry : *rCache.mpBitmapContainer) + { + CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rOtherEntry.first)); + if (iEntry == mpBitmapContainer->end()) + { + iEntry = mpBitmapContainer->emplace( + rOtherEntry.first, + CacheEntry(mnCurrentAccessTime++, true) + ).first; + UpdateCacheSize(iEntry->second, ADD); + } + if (iEntry != mpBitmapContainer->end()) + { + UpdateCacheSize(iEntry->second, REMOVE); + iEntry->second.Recycle(rOtherEntry.second); + UpdateCacheSize(iEntry->second, ADD); + } + } +} + +BitmapCache::CacheIndex BitmapCache::GetCacheIndex() const +{ + ::osl::MutexGuard aGuard (maMutex); + + // Create a copy of the bitmap container. + SortableBitmapContainer aSortedContainer; + aSortedContainer.reserve(mpBitmapContainer->size()); + + // Copy the relevant entries. + for (const auto& rEntry : *mpBitmapContainer) + { + if ( rEntry.second.IsPrecious()) + continue; + + if ( ! rEntry.second.HasPreview()) + continue; + + aSortedContainer.emplace_back(rEntry.first, rEntry.second); + } + + // Sort the remaining entries. + ::std::sort(aSortedContainer.begin(), aSortedContainer.end(), AccessTimeComparator()); + + // Return a list with the keys of the sorted entries. + CacheIndex aIndex; + aIndex.reserve(aSortedContainer.size()); + for (const auto& rIndexEntry : aSortedContainer) + aIndex.push_back(rIndexEntry.first); + return aIndex; +} + +void BitmapCache::Compress ( + const CacheKey& rKey, + const std::shared_ptr<BitmapCompressor>& rpCompressor) +{ + ::osl::MutexGuard aGuard (maMutex); + + CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); + if (iEntry != mpBitmapContainer->end() && iEntry->second.HasPreview()) + { + UpdateCacheSize(iEntry->second, REMOVE); + iEntry->second.Compress(rpCompressor); + UpdateCacheSize(iEntry->second, ADD); + } +} + +void BitmapCache::UpdateCacheSize (const CacheEntry& rEntry, CacheOperation eOperation) +{ + sal_Int32 nEntrySize (rEntry.GetMemorySize()); + sal_Int32& rCacheSize (rEntry.IsPrecious() ? mnPreciousCacheSize : mnNormalCacheSize); + switch (eOperation) + { + case ADD: + rCacheSize += nEntrySize; + if ( ! rEntry.IsPrecious() && mnNormalCacheSize>mnMaximalNormalCacheSize) + { + mbIsFull = true; + SAL_INFO("sd.sls", __func__ << ": cache size is " << mnNormalCacheSize << " > " << mnMaximalNormalCacheSize); + mpCacheCompactor->RequestCompaction(); + } + break; + + case REMOVE: + rCacheSize -= nEntrySize; + if (mnNormalCacheSize < mnMaximalNormalCacheSize) + mbIsFull = false; + break; + + default: + assert(false); + break; + } +} + +//===== CacheEntry ============================================================ + +BitmapCache::CacheEntry::CacheEntry( + sal_Int32 nLastAccessTime, + bool bIsPrecious) + : mbIsUpToDate(true), + mnLastAccessTime(nLastAccessTime), + mbIsPrecious(bIsPrecious) +{ +} + +BitmapCache::CacheEntry::CacheEntry( + const BitmapEx& rPreview, + sal_Int32 nLastAccessTime, + bool bIsPrecious) + : maPreview(rPreview), + mbIsUpToDate(true), + mnLastAccessTime(nLastAccessTime), + mbIsPrecious(bIsPrecious) +{ +} + +inline void BitmapCache::CacheEntry::Recycle (const CacheEntry& rEntry) +{ + if ((rEntry.HasPreview() || rEntry.HasLosslessReplacement()) + && ! (HasPreview() || HasLosslessReplacement())) + { + maPreview = rEntry.maPreview; + maMarkedPreview = rEntry.maMarkedPreview; + mpReplacement = rEntry.mpReplacement; + mpCompressor = rEntry.mpCompressor; + mnLastAccessTime = rEntry.mnLastAccessTime; + mbIsUpToDate = rEntry.mbIsUpToDate; + } +} + +inline sal_Int32 BitmapCache::CacheEntry::GetMemorySize() const +{ + sal_Int32 nSize (0); + nSize += maPreview.GetSizeBytes(); + nSize += maMarkedPreview.GetSizeBytes(); + if (mpReplacement != nullptr) + nSize += mpReplacement->GetMemorySize(); + return nSize; +} + +void BitmapCache::CacheEntry::Compress (const std::shared_ptr<BitmapCompressor>& rpCompressor) +{ + if ( maPreview.IsEmpty()) + return; + + if (mpReplacement == nullptr) + { + mpReplacement = rpCompressor->Compress(maPreview); + +#ifdef DEBUG_SD_SLSBITMAPCACHE + sal_uInt32 nOldSize (maPreview.GetSizeBytes()); + sal_uInt32 nNewSize (mpReplacement.get()!=NULL ? mpReplacement->GetMemorySize() : 0); + if (nOldSize == 0) + nOldSize = 1; + sal_Int32 nRatio (100L * nNewSize / nOldSize); + SAL_INFO("sd.sls", __func__ << ": compressing bitmap for " << %x << " from " << nOldSize << " to " << nNewSize << " bytes (" << nRatio << "%)"); +#endif + + mpCompressor = rpCompressor; + } + + maPreview.SetEmpty(); + maMarkedPreview.SetEmpty(); +} + +inline void BitmapCache::CacheEntry::Decompress() +{ + if (mpReplacement != nullptr && mpCompressor != nullptr && maPreview.IsEmpty()) + { + maPreview = mpCompressor->Decompress(*mpReplacement); + maMarkedPreview.SetEmpty(); + if ( ! mpCompressor->IsLossless()) + mbIsUpToDate = false; + } +} + +inline void BitmapCache::CacheEntry::SetPreview (const BitmapEx& rPreview) +{ + maPreview = rPreview; + maMarkedPreview.SetEmpty(); + mpReplacement.reset(); + mpCompressor.reset(); +} + +bool BitmapCache::CacheEntry::HasPreview() const +{ + return ! maPreview.IsEmpty(); +} + +inline void BitmapCache::CacheEntry::SetMarkedPreview (const BitmapEx& rMarkedPreview) +{ + maMarkedPreview = rMarkedPreview; +} + +inline bool BitmapCache::CacheEntry::HasLosslessReplacement() const +{ + return mpReplacement != nullptr && mpCompressor != nullptr && mpCompressor->IsLossless(); +} + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsBitmapCache.hxx b/sd/source/ui/slidesorter/cache/SlsBitmapCache.hxx new file mode 100644 index 000000000..98b0bcb53 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsBitmapCache.hxx @@ -0,0 +1,208 @@ +/* -*- 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 . + */ + +#pragma once + +#include <vcl/bitmapex.hxx> +#include <osl/mutex.hxx> +#include <memory> + +class SdrPage; + +namespace sd::slidesorter::cache +{ +class CacheCompactor; +class BitmapCompressor; + +/** This low level cache is the actual bitmap container. It supports a + precious flag for every preview bitmap and keeps track of total sizes + for all previews with/without this flag. The precious flag is used by + compaction algorithms to determine which previews may be compressed or + even discarded and which have to remain in their original form. The + precious flag is usually set for the visible previews. + + Additionally to the actual preview there is an optional marked preview. + This is used for slides excluded from the slide show which have a preview + that shows a mark (some sort of bitmap overlay) to that effect. +*/ +class BitmapCache +{ +public: + /** The key for looking up preview bitmaps is a pointer to an SdrPage + object. The prior use of PageObjectViewObjectContact objects (which + ultimately use them) turned out to be less suitable because their + life time is shorter then that of the page objects. Frequent + destruction and re-creation of the preview bitmaps was the result. + */ + typedef const SdrPage* CacheKey; + class CacheEntry; + class CacheBitmapContainer; + typedef ::std::vector<CacheKey> CacheIndex; + + /** Create a new cache for bitmap objects. + The default value from the configuration is used. + When that does not exist then an internal default value is + used. + */ + explicit BitmapCache(); + + /** The destructor clears the cache and releases all bitmaps still in it. + */ + ~BitmapCache(); + + /** Remove all preview bitmaps from the cache. After this call the + cache is empty. + */ + void Clear(); + + /** Return <TRUE/> when the cache is full, i.e. the cache compactor had + to be run. + */ + bool IsFull() const { return mbIsFull; } + + /** Return the memory size that is occupied by all non-precious bitmaps + in the cache. + */ + sal_Int32 GetSize() const { return mnNormalCacheSize; } + + /** Return <TRUE/> when a preview bitmap exists for the given key. + */ + bool HasBitmap(const CacheKey& rKey); + + /** Return <TRUE/> when a preview bitmap exists for the given key and + when it is up-to-date. + */ + bool BitmapIsUpToDate(const CacheKey& rKey); + + /** Return the preview bitmap for the given contact object. + */ + BitmapEx GetBitmap(const CacheKey& rKey); + + /** Return the marked preview bitmap for the given contact object. + */ + BitmapEx GetMarkedBitmap(const CacheKey& rKey); + + /** Release the reference to the preview bitmap that is associated with + the given key. + */ + void ReleaseBitmap(const CacheKey& rKey); + + /** Mark the specified preview bitmap as not being up-to-date + anymore. + @return + When the key references a page in the cache then + return <TRUE/>. When the key is not known then <FALSE/> + is returned. + */ + bool InvalidateBitmap(const CacheKey& rKey); + + /** Mark all preview bitmaps as not being up-to-date anymore. + */ + void InvalidateCache(); + + /** Add or replace a bitmap for the given key. + */ + void SetBitmap(const CacheKey& rKey, const BitmapEx& rPreview, bool bIsPrecious); + + /** Add or replace a marked bitmap for the given key. + */ + void SetMarkedBitmap(const CacheKey& rKey, const BitmapEx& rPreview); + + /** Mark the specified preview bitmap as precious, i.e. that it must not + be compressed or otherwise removed from the cache. + */ + void SetPrecious(const CacheKey& rKey, bool bIsPrecious); + + /** Calculate the cache size. This should rarely be necessary because + the cache size is tracked with each modification of preview + bitmaps. + */ + void ReCalculateTotalCacheSize(); + + /** Use the previews in the given cache to initialize missing previews. + */ + void Recycle(const BitmapCache& rCache); + + /** Return a list of sorted cache keys that represent an index into (a + part of) the cache. The entries of the index are sorted according + to last access times with the least recently access time first. + Entries with the precious flag set are omitted. + Entries with that have no preview bitmaps are omitted. + */ + CacheIndex GetCacheIndex() const; + + /** Compress the specified preview bitmap with the given bitmap + compressor. A reference to the compressor is stored for later + decompression. + */ + void Compress(const CacheKey& rKey, const std::shared_ptr<BitmapCompressor>& rpCompressor); + +private: + mutable ::osl::Mutex maMutex; + + std::unique_ptr<CacheBitmapContainer> mpBitmapContainer; + + /** Total size of bytes that are occupied by bitmaps in the cache for + whom the slides are currently not inside the visible area. + */ + sal_Int32 mnNormalCacheSize; + + /** Total size of bytes that are occupied by bitmaps in the cache for + whom the slides are currently visible. + */ + sal_Int32 mnPreciousCacheSize; + + /** At the moment the access time is not an actual time or date value + but a counter that is increased with every access. It thus defines + the same ordering as a true time. + */ + sal_Int32 mnCurrentAccessTime; + + /** The maximal cache size for the off-screen preview bitmaps. When + mnNormalCacheSize grows larger than this value then the + mpCacheCompactor member is used to reduce the cache size. + */ + sal_Int32 mnMaximalNormalCacheSize; + + /** The cache compactor is used to reduce the number of bytes used by + off-screen preview bitmaps. + */ + ::std::unique_ptr<CacheCompactor> mpCacheCompactor; + + /** This flag stores if the cache is or recently was full, i.e. the + cache compactor has or had to be run in order to reduce the cache + size to the allowed value. + */ + bool mbIsFull; + + /** Update mnNormalCacheSize or mnPreciousCacheSize according to the + precious flag of the specified preview bitmap and the specified + operation. + */ + enum CacheOperation + { + ADD, + REMOVE + }; + void UpdateCacheSize(const CacheEntry& rKey, CacheOperation eOperation); +}; + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsBitmapCompressor.cxx b/sd/source/ui/slidesorter/cache/SlsBitmapCompressor.cxx new file mode 100644 index 000000000..d4da935dd --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsBitmapCompressor.cxx @@ -0,0 +1,197 @@ +/* -*- 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 "SlsBitmapCompressor.hxx" + +#include <tools/stream.hxx> +#include <vcl/bitmapex.hxx> +#include <vcl/filter/PngImageReader.hxx> +#include <vcl/pngwrite.hxx> + +namespace sd::slidesorter::cache { + +//===== NoBitmapCompression =================================================== + +/** This dummy replacement simply stores a shared pointer to the original + preview bitmap. +*/ +class NoBitmapCompression::DummyReplacement + : public BitmapReplacement +{ +public: + BitmapEx maPreview; + + explicit DummyReplacement (const BitmapEx& rPreview) : maPreview(rPreview) { } + virtual ~DummyReplacement() {} + virtual sal_Int32 GetMemorySize() const override { return maPreview.GetSizeBytes(); } +}; + +std::shared_ptr<BitmapReplacement> NoBitmapCompression::Compress (const BitmapEx& rBitmap) const +{ + return std::make_shared<DummyReplacement>(rBitmap); +} + +BitmapEx NoBitmapCompression::Decompress (const BitmapReplacement& rBitmapData) const +{ + return dynamic_cast<const DummyReplacement&>(rBitmapData).maPreview; +} + +bool NoBitmapCompression::IsLossless() const +{ + return true; +} + +//===== CompressionByDeletion ================================================= + +std::shared_ptr<BitmapReplacement> CompressionByDeletion::Compress (const BitmapEx& ) const +{ + return std::shared_ptr<BitmapReplacement>(); +} + +BitmapEx CompressionByDeletion::Decompress (const BitmapReplacement& ) const +{ + // Return a NULL pointer. This will eventually lead to a request for + // the creation of a new one. + return BitmapEx(); +} + +bool CompressionByDeletion::IsLossless() const +{ + return false; +} + +//===== ResolutionReduction =================================================== + +/** Store a scaled down bitmap together with the original size. +*/ +class ResolutionReduction::ResolutionReducedReplacement : public BitmapReplacement +{ +public: + BitmapEx maPreview; + Size maOriginalSize; + + virtual ~ResolutionReducedReplacement(); + virtual sal_Int32 GetMemorySize() const override; +}; + +ResolutionReduction::ResolutionReducedReplacement::~ResolutionReducedReplacement() +{ +} + +sal_Int32 ResolutionReduction::ResolutionReducedReplacement::GetMemorySize() const +{ + return maPreview.GetSizeBytes(); +} + +std::shared_ptr<BitmapReplacement> ResolutionReduction::Compress ( + const BitmapEx& rBitmap) const +{ + auto pResult = std::make_shared<ResolutionReducedReplacement>(); + pResult->maPreview = rBitmap; + Size aSize (rBitmap.GetSizePixel()); + pResult->maOriginalSize = aSize; + if (aSize.Width()>0 && aSize.Width()<mnWidth) + { + int nHeight = aSize.Height() * mnWidth / aSize.Width() ; + pResult->maPreview.Scale(Size(mnWidth,nHeight)); + } + + return pResult; +} + +BitmapEx ResolutionReduction::Decompress (const BitmapReplacement& rBitmapData) const +{ + BitmapEx aResult; + + const ResolutionReducedReplacement* pData ( + dynamic_cast<const ResolutionReducedReplacement*>(&rBitmapData)); + + if ( pData && ! pData->maPreview.IsEmpty()) + { + aResult = pData->maPreview; + if (pData->maOriginalSize.Width() > mnWidth) + aResult.Scale(pData->maOriginalSize); + } + + return aResult; +} + +bool ResolutionReduction::IsLossless() const +{ + return false; +} + +//===== PNGCompression ======================================================== + +class PngCompression::PngReplacement : public BitmapReplacement +{ +public: + void* mpData; + sal_Int32 mnDataSize; + PngReplacement() + : mpData(nullptr), + mnDataSize(0) + {} + virtual ~PngReplacement() + { + delete [] static_cast<char*>(mpData); + } + virtual sal_Int32 GetMemorySize() const override + { + return mnDataSize; + } +}; + +std::shared_ptr<BitmapReplacement> PngCompression::Compress (const BitmapEx& rBitmap) const +{ + vcl::PNGWriter aWriter(rBitmap); + SvMemoryStream aStream (32768, 32768); + aWriter.Write(aStream); + + auto pResult = std::make_shared<PngReplacement>(); + pResult->mnDataSize = aStream.Tell(); + pResult->mpData = new char[pResult->mnDataSize]; + memcpy(pResult->mpData, aStream.GetData(), pResult->mnDataSize); + + return pResult; +} + +BitmapEx PngCompression::Decompress ( + const BitmapReplacement& rBitmapData) const +{ + BitmapEx aResult; + const PngReplacement* pData (dynamic_cast<const PngReplacement*>(&rBitmapData)); + if (pData != nullptr) + { + SvMemoryStream aStream (pData->mpData, pData->mnDataSize, StreamMode::READ); + vcl::PngImageReader aReader (aStream); + aResult = aReader.read().GetBitmap(); + } + + return aResult; +} + +bool PngCompression::IsLossless() const +{ + return true; +} + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsBitmapCompressor.hxx b/sd/source/ui/slidesorter/cache/SlsBitmapCompressor.hxx new file mode 100644 index 000000000..4754bead9 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsBitmapCompressor.hxx @@ -0,0 +1,138 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/types.h> +#include <memory> + +class BitmapEx; + +namespace sd::slidesorter::cache +{ +class BitmapReplacement; + +/** This interface class provides the minimal method set for classes that + implement the compression and decompression of preview bitmaps. +*/ +class BitmapCompressor +{ +public: + /** Compress the given bitmap into a replacement format that is specific + to the compressor class. + */ + virtual std::shared_ptr<BitmapReplacement> Compress(const BitmapEx& rBitmap) const = 0; + + /** Decompress the given replacement data into a preview bitmap. + Depending on the compression technique the returned bitmap may + differ from the original bitmap given to the Compress() method. It + may even of the wrong size or empty or the NULL pointer. It is the + task of the caller to create a new preview bitmap if the returned + one is not as desired. + */ + virtual BitmapEx Decompress(const BitmapReplacement& rBitmapData) const = 0; + + /** Return whether the compression and decompression is lossless. This + value is used by the caller of Decompress() to decide whether to use + the returned bitmap as is or if a new preview has to be created. + */ + virtual bool IsLossless() const = 0; + +protected: + ~BitmapCompressor() {} +}; + +/** Interface for preview bitmap replacements. Each bitmap + compressor/decompressor has to provide an implementation that is + suitable to store the compressed bitmaps. +*/ +class BitmapReplacement +{ +public: + virtual sal_Int32 GetMemorySize() const { return 0; } + +protected: + ~BitmapReplacement() {} +}; + +/** This is one trivial bitmap compressor. It stores bitmaps unmodified + instead of compressing them. + This compressor is lossless. +*/ +class NoBitmapCompression : public BitmapCompressor +{ + class DummyReplacement; + +public: + virtual ~NoBitmapCompression() {} + virtual std::shared_ptr<BitmapReplacement> Compress(const BitmapEx& rpBitmap) const override; + virtual BitmapEx Decompress(const BitmapReplacement& rBitmapData) const override; + virtual bool IsLossless() const override; +}; + +/** This is another trivial bitmap compressor. Instead of compressing a + bitmap, it throws the bitmap away. Its Decompress() method returns a + NULL pointer. The caller has to create a new preview bitmap instead. + This compressor clearly is not lossless. +*/ +class CompressionByDeletion : public BitmapCompressor +{ +public: + virtual ~CompressionByDeletion() {} + virtual std::shared_ptr<BitmapReplacement> Compress(const BitmapEx& rBitmap) const override; + virtual BitmapEx Decompress(const BitmapReplacement& rBitmapData) const override; + virtual bool IsLossless() const override; +}; + +/** Compress a preview bitmap by reducing its resolution. While the aspect + ratio is maintained the horizontal resolution is scaled down to 100 + pixels. + This compressor is not lossless. +*/ +class ResolutionReduction : public BitmapCompressor +{ + class ResolutionReducedReplacement; + static const sal_Int32 mnWidth = 100; + +public: + virtual ~ResolutionReduction() {} + virtual std::shared_ptr<BitmapReplacement> Compress(const BitmapEx& rpBitmap) const override; + /** Scale the replacement bitmap up to the original size. + */ + virtual BitmapEx Decompress(const BitmapReplacement& rBitmapData) const override; + virtual bool IsLossless() const override; +}; + +/** Compress preview bitmaps using the PNG format. + This compressor is lossless. +*/ +class PngCompression : public BitmapCompressor +{ + class PngReplacement; + +public: + virtual ~PngCompression() {} + virtual std::shared_ptr<BitmapReplacement> Compress(const BitmapEx& rBitmap) const override; + virtual BitmapEx Decompress(const BitmapReplacement& rBitmapData) const override; + virtual bool IsLossless() const override; +}; + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsBitmapFactory.cxx b/sd/source/ui/slidesorter/cache/SlsBitmapFactory.cxx new file mode 100644 index 000000000..a9182c2a2 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsBitmapFactory.cxx @@ -0,0 +1,71 @@ +/* -*- 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 "SlsBitmapFactory.hxx" + +#include <PreviewRenderer.hxx> +#include <sdpage.hxx> +#include <vcl/bitmapex.hxx> + +namespace sd::slidesorter::view { +class SlideSorterView; +class PageObjectViewObjectContact; +} + +namespace sd::slidesorter::cache { + +BitmapFactory::BitmapFactory() + : maRenderer(false) +{ +} + +BitmapFactory::~BitmapFactory() +{ +} + +BitmapEx BitmapFactory::CreateBitmap ( + const SdPage& rPage, + const Size& rPixelSize, + const bool bDoSuperSampling) +{ + Size aSize (rPixelSize); + if (bDoSuperSampling) + { + // Supersampling factor + int aSuperSamplingFactor = 2; + aSize.setWidth( aSize.Width() * aSuperSamplingFactor ); + aSize.setHeight( aSize.Height() * aSuperSamplingFactor ); + } + + BitmapEx aPreview (maRenderer.RenderPage ( + &rPage, + aSize, + true, + false).GetBitmapEx()); + if (bDoSuperSampling) + { + aPreview.Scale(rPixelSize, BmpScaleFlag::BestQuality); + } + + return aPreview; +} + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsBitmapFactory.hxx b/sd/source/ui/slidesorter/cache/SlsBitmapFactory.hxx new file mode 100644 index 000000000..c1733b825 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsBitmapFactory.hxx @@ -0,0 +1,46 @@ +/* -*- 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 . + */ + +#pragma once + +#include <PreviewRenderer.hxx> + +class SdPage; +class Size; + +namespace sd::slidesorter::cache +{ +/** This factory class creates preview bitmaps for page objects. It is + merely an adapter for the PreviewRenderer. +*/ +class BitmapFactory +{ +public: + BitmapFactory(); + ~BitmapFactory(); + + BitmapEx CreateBitmap(const SdPage& rPage, const Size& rPixelSize, const bool bDoSuperSampling); + +private: + PreviewRenderer maRenderer; +}; + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsCacheCompactor.cxx b/sd/source/ui/slidesorter/cache/SlsCacheCompactor.cxx new file mode 100644 index 000000000..79ab9fab2 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsCacheCompactor.cxx @@ -0,0 +1,189 @@ +/* -*- 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 <memory> +#include "SlsCacheCompactor.hxx" + +#include "SlsBitmapCompressor.hxx" +#include "SlsBitmapCache.hxx" +#include "SlsCacheConfiguration.hxx" + +#include <rtl/ustring.hxx> +#include <sal/log.hxx> +#include <com/sun/star/uno/Any.hxx> + +using namespace ::com::sun::star::uno; + +namespace { + +/** This is a trivial implementation of the CacheCompactor interface class. + It ignores calls to RequestCompaction() and thus will never decrease the + total size of off-screen preview bitmaps. +*/ +class NoCacheCompaction + : public ::sd::slidesorter::cache::CacheCompactor +{ +public: + NoCacheCompaction ( + ::sd::slidesorter::cache::BitmapCache& rCache, + sal_Int32 nMaximalCacheSize) + : CacheCompactor(rCache, nMaximalCacheSize) + {} + + virtual void RequestCompaction() override { /* Ignored */ }; + +protected: + virtual void Run() override { /* Do nothing */ }; +}; + +/** This implementation of the CacheCompactor interface class uses one of + several bitmap compression algorithms to reduce the number of the bytes + of the off-screen previews in the bitmap cache. See the documentation + of CacheCompactor::Create() for more details on configuration properties + that control the choice of compression algorithm. +*/ +class CacheCompactionByCompression + : public ::sd::slidesorter::cache::CacheCompactor +{ +public: + CacheCompactionByCompression ( + ::sd::slidesorter::cache::BitmapCache& rCache, + sal_Int32 nMaximalCacheSize, + const std::shared_ptr< ::sd::slidesorter::cache::BitmapCompressor>& rpCompressor); + +protected: + virtual void Run() override; + +private: + std::shared_ptr< ::sd::slidesorter::cache::BitmapCompressor> mpCompressor; +}; + +} // end of anonymous namespace + +namespace sd::slidesorter::cache { + +::std::unique_ptr<CacheCompactor> CacheCompactor::Create ( + BitmapCache& rCache, + sal_Int32 nMaximalCacheSize) +{ + static const char sNone[] = "None"; + + std::shared_ptr<BitmapCompressor> pCompressor; + OUString sCompressionPolicy("PNGCompression"); + Any aCompressionPolicy (CacheConfiguration::Instance()->GetValue("CompressionPolicy")); + if (aCompressionPolicy.has<OUString>()) + aCompressionPolicy >>= sCompressionPolicy; + if (sCompressionPolicy == sNone) + pCompressor = std::make_shared<NoBitmapCompression>(); + else if (sCompressionPolicy == "Erase") + pCompressor = std::make_shared<CompressionByDeletion>(); + else if (sCompressionPolicy == "ResolutionReduction") + pCompressor = std::make_shared<ResolutionReduction>(); + else + pCompressor = std::make_shared<PngCompression>(); + + ::std::unique_ptr<CacheCompactor> pCompactor; + OUString sCompactionPolicy("Compress"); + Any aCompactionPolicy (CacheConfiguration::Instance()->GetValue("CompactionPolicy")); + if (aCompactionPolicy.has<OUString>()) + aCompactionPolicy >>= sCompactionPolicy; + if (sCompactionPolicy == sNone) + pCompactor.reset(new NoCacheCompaction(rCache,nMaximalCacheSize)); + else + pCompactor.reset(new CacheCompactionByCompression(rCache,nMaximalCacheSize,pCompressor)); + + return pCompactor; +} + +void CacheCompactor::RequestCompaction() +{ + if ( ! mbIsCompactionRunning && ! maCompactionTimer.IsActive()) + maCompactionTimer.Start(); +} + +CacheCompactor::CacheCompactor( + BitmapCache& rCache, + sal_Int32 nMaximalCacheSize) + : mrCache(rCache), + mnMaximalCacheSize(nMaximalCacheSize), + maCompactionTimer("sd CacheCompactor maCompactionTimer"), + mbIsCompactionRunning(false) +{ + maCompactionTimer.SetTimeout(100); + maCompactionTimer.SetInvokeHandler(LINK(this,CacheCompactor,CompactionCallback)); +} + +IMPL_LINK_NOARG(CacheCompactor, CompactionCallback, Timer *, void) +{ + mbIsCompactionRunning = true; + + try + { + Run(); + } + catch (const css::uno::RuntimeException&) + { + } + catch (const css::uno::Exception&) + { + } + + mbIsCompactionRunning = false; +} + +} // end of namespace ::sd::slidesorter::cache + +namespace { + +//===== CacheCompactionByCompression ========================================== + +CacheCompactionByCompression::CacheCompactionByCompression ( + ::sd::slidesorter::cache::BitmapCache& rCache, + sal_Int32 nMaximalCacheSize, + const std::shared_ptr< ::sd::slidesorter::cache::BitmapCompressor>& rpCompressor) + : CacheCompactor(rCache,nMaximalCacheSize), + mpCompressor(rpCompressor) +{ +} + +void CacheCompactionByCompression::Run() +{ + if (mrCache.GetSize() <= mnMaximalCacheSize) + return; + + SAL_INFO("sd.sls", __func__ << ": bitmap cache uses too much space: " << mrCache.GetSize() << " > " << mnMaximalCacheSize); + + ::sd::slidesorter::cache::BitmapCache::CacheIndex aIndex ( + mrCache.GetCacheIndex()); + for (const auto& rpIndex : aIndex) + { + if (rpIndex == nullptr) + continue; + + mrCache.Compress(rpIndex, mpCompressor); + if (mrCache.GetSize() < mnMaximalCacheSize) + break; + } + mrCache.ReCalculateTotalCacheSize(); + SAL_INFO("sd.sls", __func__ << ": there are now " << mrCache.GetSize() << " bytes occupied"); +} + +} // end of anonymous namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsCacheCompactor.hxx b/sd/source/ui/slidesorter/cache/SlsCacheCompactor.hxx new file mode 100644 index 000000000..d694ae1a1 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsCacheCompactor.hxx @@ -0,0 +1,87 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/types.h> +#include <vcl/timer.hxx> +#include <memory> + +namespace sd::slidesorter::cache { + +class BitmapCache; + +/** This is an interface class whose implementations are created via the + Create() factory method. +*/ +class CacheCompactor +{ +public: + virtual ~CacheCompactor() {}; + + /** Create a new instance of the CacheCompactor interface class. The + type of compaction algorithm used depends on values from the + configuration: the SlideSorter/PreviewCache/CompactionPolicy + property of the Impress.xcs file currently supports the values + "None" and "Compress". With the later the CompressionPolicy + property is evaluated which implementation of the BitmapCompress + interface class to use as bitmap compressor. + @param rCache + The bitmap cache on which to operate. + @param nMaximalCacheSize + The total number of bytes the off-screen bitmaps in the cache + may have. When the Run() method is (indirectly) called the + compactor tries to reduce that summed size of off-screen bitmaps + under this number. However, it is not guaranteed that this + works in all cases. + */ + static ::std::unique_ptr<CacheCompactor> Create ( + BitmapCache& rCache, + sal_Int32 nMaximalCacheSize); + + /** Request a compaction of the off-screen previews in the bitmap + cache. This calls via a timer the Run() method. + */ + virtual void RequestCompaction(); + +protected: + BitmapCache& mrCache; + sal_Int32 mnMaximalCacheSize; + + CacheCompactor( + BitmapCache& rCache, + sal_Int32 nMaximalCacheSize); + + /** This method actually tries to reduce the total number of bytes used + by the off-screen preview bitmaps. + */ + virtual void Run() = 0; + +private: + /** This timer is used to collect calls to RequestCompaction() and + eventually call Run(). + */ + Timer maCompactionTimer; + bool mbIsCompactionRunning; + DECL_LINK(CompactionCallback, Timer *, void); +}; + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsCacheConfiguration.cxx b/sd/source/ui/slidesorter/cache/SlsCacheConfiguration.cxx new file mode 100644 index 000000000..fd08c7627 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsCacheConfiguration.cxx @@ -0,0 +1,144 @@ +/* -*- 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 "SlsCacheConfiguration.hxx" +#include <vcl/svapp.hxx> + +#include <comphelper/processfactory.hxx> +#include <comphelper/propertysequence.hxx> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/configuration/theDefaultProvider.hpp> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::slidesorter::cache { + +namespace +{ + typedef std::shared_ptr<CacheConfiguration> CacheConfigSharedPtr; + CacheConfigSharedPtr& theInstance() + { + static CacheConfigSharedPtr SINGLETON; + return SINGLETON; + } +} + +std::weak_ptr<CacheConfiguration> CacheConfiguration::mpWeakInstance; + +std::shared_ptr<CacheConfiguration> CacheConfiguration::Instance() +{ + SolarMutexGuard aSolarGuard; + CacheConfigSharedPtr &rInstancePtr = theInstance(); + if (!rInstancePtr) + { + // Maybe somebody else kept a previously created instance alive. + if ( ! mpWeakInstance.expired()) + rInstancePtr = std::shared_ptr<CacheConfiguration>(mpWeakInstance); + if (!rInstancePtr) + { + // We have to create a new instance. + rInstancePtr.reset(new CacheConfiguration()); + mpWeakInstance = rInstancePtr; + // Prepare to release this instance in the near future. + rInstancePtr->m_ReleaseTimer.SetInvokeHandler( + LINK(rInstancePtr.get(),CacheConfiguration,TimerCallback)); + rInstancePtr->m_ReleaseTimer.SetTimeout(5000 /* 5s */); + rInstancePtr->m_ReleaseTimer.Start(); + } + } + return rInstancePtr; +} + +CacheConfiguration::CacheConfiguration() + : m_ReleaseTimer("sd::CacheConfiguration maReleaseTimer") +{ + // Get the cache size from configuration. + try + { + // Obtain access to the configuration. + Reference<lang::XMultiServiceFactory> xProvider = + configuration::theDefaultProvider::get( ::comphelper::getProcessComponentContext() ); + + // Obtain access to Impress configuration. + Sequence<Any> aCreationArguments(comphelper::InitAnyPropertySequence( + { + {"nodepath", Any(OUString("/org.openoffice.Office.Impress/"))}, + {"depth", Any(sal_Int32(-1))} + })); + + Reference<XInterface> xRoot (xProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", + aCreationArguments)); + if ( ! xRoot.is()) + return; + Reference<container::XHierarchicalNameAccess> xHierarchy (xRoot, UNO_QUERY); + if ( ! xHierarchy.is()) + return; + + // Get the node for the slide sorter preview cache. + mxCacheNode.set( xHierarchy->getByHierarchicalName("MultiPaneGUI/SlideSorter/PreviewCache"), UNO_QUERY); + } + catch (RuntimeException &) + { + } + catch (Exception &) + { + } +} + +Any CacheConfiguration::GetValue (const OUString& rName) +{ + Any aResult; + + if (mxCacheNode != nullptr) + { + try + { + aResult = mxCacheNode->getByName(rName); + } + catch (Exception &) + { + } + } + + return aResult; +} + +IMPL_STATIC_LINK_NOARG(CacheConfiguration, TimerCallback, Timer *, void) +{ + CacheConfigSharedPtr &rInstancePtr = theInstance(); + // Release our reference to the instance. + rInstancePtr.reset(); + // note: if there are no other references to the instance, m_ReleaseTimer + // will be deleted now +} + +void CacheConfiguration::Shutdown() +{ + CacheConfigSharedPtr &rInstancePtr = theInstance(); + rInstancePtr.reset(); + assert(mpWeakInstance.expired()); // ensure m_ReleaseTimer is destroyed +} + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsCacheConfiguration.hxx b/sd/source/ui/slidesorter/cache/SlsCacheConfiguration.hxx new file mode 100644 index 000000000..d53bcd713 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsCacheConfiguration.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 . + */ + +#pragma once + +#include <com/sun/star/uno/Any.hxx> +#include <vcl/timer.hxx> +#include <memory> + +namespace com::sun::star::container +{ +class XNameAccess; +} + +namespace sd::slidesorter::cache +{ +/** A very simple and easy-to-use access to configuration entries regarding + the slide sorter cache. +*/ +class CacheConfiguration +{ +public: + /** Return an instance to this class. The reference is released after 5 + seconds. Subsequent calls to this function will create a new + instance. + */ + static std::shared_ptr<CacheConfiguration> Instance(); + + static void Shutdown(); + + /** Look up the specified value in + MultiPaneGUI/SlideSorter/PreviewCache. When the specified value + does not exist then an empty Any is returned. + */ + css::uno::Any GetValue(const OUString& rName); + +private: + /** When a caller holds a reference after we have released ours we use + this weak pointer to avoid creating a new instance. + */ + static std::weak_ptr<CacheConfiguration> mpWeakInstance; + Timer m_ReleaseTimer; + css::uno::Reference<css::container::XNameAccess> mxCacheNode; + + CacheConfiguration(); + + DECL_STATIC_LINK(CacheConfiguration, TimerCallback, Timer*, void); +}; + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsGenericPageCache.cxx b/sd/source/ui/slidesorter/cache/SlsGenericPageCache.cxx new file mode 100644 index 000000000..6275754fa --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsGenericPageCache.cxx @@ -0,0 +1,278 @@ +/* -*- 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 "SlsGenericPageCache.hxx" + +#include "SlsQueueProcessor.hxx" +#include "SlsRequestPriorityClass.hxx" +#include "SlsRequestFactory.hxx" +#include "SlsBitmapCache.hxx" +#include <cache/SlsPageCacheManager.hxx> +#include <tools/debug.hxx> + +namespace sd::slidesorter::cache { + +GenericPageCache::GenericPageCache ( + const Size& rPreviewSize, + const bool bDoSuperSampling, + const SharedCacheContext& rpCacheContext) + : maRequestQueue(rpCacheContext), + mpCacheContext(rpCacheContext), + maPreviewSize(rPreviewSize), + mbDoSuperSampling(bDoSuperSampling) +{ + // A large size may indicate an error of the caller. After all we + // are creating previews. + DBG_ASSERT (maPreviewSize.Width()<1000 && maPreviewSize.Height()<1000, + "GenericPageCache<>::GetPreviewBitmap(): bitmap requested with large width. " + "This may indicate an error."); +} + +GenericPageCache::~GenericPageCache() +{ + if (mpQueueProcessor != nullptr) + mpQueueProcessor->Stop(); + maRequestQueue.Clear(); + mpQueueProcessor.reset(); + + if (mpBitmapCache != nullptr) + PageCacheManager::Instance()->ReleaseCache(mpBitmapCache); + mpBitmapCache.reset(); +} + +void GenericPageCache::ProvideCacheAndProcessor() +{ + if (mpBitmapCache == nullptr) + mpBitmapCache = PageCacheManager::Instance()->GetCache( + mpCacheContext->GetModel(), + maPreviewSize); + + if (mpQueueProcessor == nullptr) + mpQueueProcessor.reset(new QueueProcessor( + maRequestQueue, + mpBitmapCache, + maPreviewSize, + mbDoSuperSampling, + mpCacheContext)); +} + +void GenericPageCache::ChangePreviewSize ( + const Size& rPreviewSize, + const bool bDoSuperSampling) +{ + if (rPreviewSize==maPreviewSize && bDoSuperSampling==mbDoSuperSampling) + return; + + // A large size may indicate an error of the caller. After all we + // are creating previews. + DBG_ASSERT (maPreviewSize.Width()<1000 && maPreviewSize.Height()<1000, + "GenericPageCache<>::GetPreviewBitmap(): bitmap requested with large width. " + "This may indicate an error."); + + if (mpBitmapCache != nullptr) + { + mpBitmapCache = PageCacheManager::Instance()->ChangeSize( + mpBitmapCache, maPreviewSize, rPreviewSize); + if (mpQueueProcessor != nullptr) + { + mpQueueProcessor->SetPreviewSize(rPreviewSize, bDoSuperSampling); + mpQueueProcessor->SetBitmapCache(mpBitmapCache); + } + } + maPreviewSize = rPreviewSize; + mbDoSuperSampling = bDoSuperSampling; +} + +BitmapEx GenericPageCache::GetPreviewBitmap ( + const CacheKey aKey, + const bool bResize) +{ + assert(aKey != nullptr); + + BitmapEx aPreview; + bool bMayBeUpToDate = true; + ProvideCacheAndProcessor(); + const SdrPage* pPage = mpCacheContext->GetPage(aKey); + if (mpBitmapCache->HasBitmap(pPage)) + { + aPreview = mpBitmapCache->GetBitmap(pPage); + const Size aBitmapSize (aPreview.GetSizePixel()); + if (aBitmapSize != maPreviewSize) + { + // Scale the bitmap to the desired size when that is possible, + // i.e. the bitmap is not empty. + if (bResize && !aBitmapSize.IsEmpty()) + { + aPreview.Scale(maPreviewSize); + } + bMayBeUpToDate = false; + } + else + bMayBeUpToDate = true; + } + else + bMayBeUpToDate = false; + + // Request the creation of a correctly sized preview bitmap. We do this + // even when the size of the bitmap in the cache is correct because its + // content may be not up-to-date anymore. + RequestPreviewBitmap(aKey, bMayBeUpToDate); + + return aPreview; +} + +BitmapEx GenericPageCache::GetMarkedPreviewBitmap ( + const CacheKey aKey) +{ + assert(aKey != nullptr); + + ProvideCacheAndProcessor(); + const SdrPage* pPage = mpCacheContext->GetPage(aKey); + BitmapEx aMarkedPreview (mpBitmapCache->GetMarkedBitmap(pPage)); + + return aMarkedPreview; +} + +void GenericPageCache::SetMarkedPreviewBitmap ( + const CacheKey aKey, + const BitmapEx& rMarkedBitmap) +{ + assert(aKey != nullptr); + + ProvideCacheAndProcessor(); + const SdrPage* pPage = mpCacheContext->GetPage(aKey); + mpBitmapCache->SetMarkedBitmap(pPage, rMarkedBitmap); +} + +void GenericPageCache::RequestPreviewBitmap ( + const CacheKey aKey, + const bool bMayBeUpToDate) +{ + assert(aKey != nullptr); + + const SdrPage* pPage = mpCacheContext->GetPage(aKey); + + ProvideCacheAndProcessor(); + + // Determine if the available bitmap is up to date. + bool bIsUpToDate = false; + if (bMayBeUpToDate) + bIsUpToDate = mpBitmapCache->BitmapIsUpToDate (pPage); + if (bIsUpToDate) + { + const BitmapEx aPreview (mpBitmapCache->GetBitmap(pPage)); + if (aPreview.IsEmpty() || aPreview.GetSizePixel()!=maPreviewSize) + bIsUpToDate = false; + } + + if ( bIsUpToDate) + return; + + // No, the bitmap is not up-to-date. Request a new one. + RequestPriorityClass ePriorityClass (NOT_VISIBLE); + if (mpCacheContext->IsVisible(aKey)) + { + if (mpBitmapCache->HasBitmap(pPage)) + ePriorityClass = VISIBLE_OUTDATED_PREVIEW; + else + ePriorityClass = VISIBLE_NO_PREVIEW; + } + maRequestQueue.AddRequest(aKey, ePriorityClass); + mpQueueProcessor->Start(ePriorityClass); +} + +bool GenericPageCache::InvalidatePreviewBitmap (const CacheKey aKey) +{ + // Invalidate the page in all caches that reference it, not just this one. + std::shared_ptr<cache::PageCacheManager> pCacheManager ( + cache::PageCacheManager::Instance()); + if (pCacheManager) + return pCacheManager->InvalidatePreviewBitmap( + mpCacheContext->GetModel(), + aKey); + else if (mpBitmapCache != nullptr) + return mpBitmapCache->InvalidateBitmap(mpCacheContext->GetPage(aKey)); + else + return false; +} + +void GenericPageCache::InvalidateCache () +{ + if (!mpBitmapCache) + return; + + // When the cache is being invalidated then it makes no sense to + // continue creating preview bitmaps. However, this may be + // re-started below. + mpQueueProcessor->Stop(); + maRequestQueue.Clear(); + + // Mark the previews in the cache as not being up-to-date anymore. + // Depending on the given bUpdateCache flag we start to create new + // preview bitmaps. + mpBitmapCache->InvalidateCache(); + RequestFactory()(maRequestQueue, mpCacheContext); +} + +void GenericPageCache::SetPreciousFlag ( + const CacheKey aKey, + const bool bIsPrecious) +{ + ProvideCacheAndProcessor(); + + // Change the request priority class according to the new precious flag. + if (bIsPrecious) + { + if (mpBitmapCache->HasBitmap(mpCacheContext->GetPage(aKey))) + maRequestQueue.ChangeClass(aKey,VISIBLE_OUTDATED_PREVIEW); + else + maRequestQueue.ChangeClass(aKey,VISIBLE_NO_PREVIEW); + } + else + { + if (mpBitmapCache->IsFull()) + { + // When the bitmap cache is full then requests for slides that + // are not visible are removed. + maRequestQueue.RemoveRequest(aKey); + } + else + maRequestQueue.ChangeClass(aKey,NOT_VISIBLE); + } + + mpBitmapCache->SetPrecious(mpCacheContext->GetPage(aKey), bIsPrecious); +} + +void GenericPageCache::Pause() +{ + ProvideCacheAndProcessor(); + if (mpQueueProcessor != nullptr) + mpQueueProcessor->Pause(); +} + +void GenericPageCache::Resume() +{ + ProvideCacheAndProcessor(); + if (mpQueueProcessor != nullptr) + mpQueueProcessor->Resume(); +} + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsGenericPageCache.hxx b/sd/source/ui/slidesorter/cache/SlsGenericPageCache.hxx new file mode 100644 index 000000000..900d40268 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsGenericPageCache.hxx @@ -0,0 +1,152 @@ +/* -*- 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 . + */ + +#pragma once + +#include "SlsRequestQueue.hxx" +#include <memory> + +#include <vcl/bitmapex.hxx> + +namespace sd::slidesorter::cache { + +class BitmapCache; +class QueueProcessor; + +/** This basically is the implementation class for the PageCache class. +*/ +class GenericPageCache +{ +public: + /** The page cache is created with a reference to the SlideSorter and + thus has access to both view and model. This allows the cache to + fill itself with requests for all pages or just the visible ones. + @param rPreviewSize + The size of the previews is expected in pixel values. + @param bDoSuperSampling + When <TRUE/> the previews are rendered larger and then scaled + down to the requested size to improve image quality. + */ + GenericPageCache ( + const Size& rPreviewSize, + const bool bDoSuperSampling, + const SharedCacheContext& rpCacheContext); + + ~GenericPageCache(); + + /** Change the size of the preview bitmaps. This may be caused by a + resize of the slide sorter window or a change of the number of + columns. + */ + void ChangePreviewSize ( + const Size& rPreviewSize, + const bool bDoSuperSampling); + + /** Request a preview bitmap for the specified page object in the + specified size. The returned bitmap may be a preview of the preview, + i.e. either a scaled (up or down) version of a previous preview (of + the wrong size) or an empty bitmap. In this case a request for the + generation of a new preview is created and inserted into the request + queue. When the preview is available the page shape will be told to + paint itself again. When it then calls this method again if + receives the correctly sized preview bitmap. + @param rRequestData + This data is used to determine the preview. + @param bResize + When <TRUE/> then when the available bitmap has not the + requested size, it is scaled before it is returned. When + <FALSE/> then the bitmap is returned in the wrong size and it is + the task of the caller to scale it. + @return + Returns a bitmap that is either empty, contains a scaled (up or + down) version or is the requested bitmap. + */ + BitmapEx GetPreviewBitmap ( + const CacheKey aKey, + const bool bResize); + BitmapEx GetMarkedPreviewBitmap ( + const CacheKey aKey); + void SetMarkedPreviewBitmap ( + const CacheKey aKey, + const BitmapEx& rMarkedBitmap); + + /** When the requested preview bitmap does not yet exist or is not + up-to-date then the rendering of one is scheduled. Otherwise this + method does nothing. + @param rRequestData + This data is used to determine the preview. + @param bMayBeUpToDate + This flag helps the method to determine whether an existing + preview that matches the request is up to date. If the caller + knows that it is not then by passing <FALSE/> he tells us that we + do not have to check the up-to-date flag a second time. If + unsure use <TRUE/>. + */ + void RequestPreviewBitmap ( + const CacheKey aKey, + const bool bMayBeUpToDate); + + /** Tell the cache to replace the bitmap associated with the given + request data with a new one that reflects recent changes in the + content of the page object. + @return + When the key is known then return <TRUE/>. + */ + bool InvalidatePreviewBitmap (const CacheKey aKey); + + /** Call this method when all preview bitmaps have to be generated anew. + This is the case when the size of the page objects on the screen has + changed or when the model has changed. + */ + void InvalidateCache (); + + /** With the precious flag you can control whether a bitmap can be + removed from the cache or reduced in size to make room for other + bitmaps or is so precious that it will not be touched. A typical + use is to set the precious flag for the visible pages. + */ + void SetPreciousFlag (const CacheKey aKey, const bool bIsPrecious); + + void Pause(); + void Resume(); + +private: + std::shared_ptr<BitmapCache> mpBitmapCache; + + RequestQueue maRequestQueue; + + std::unique_ptr<QueueProcessor> mpQueueProcessor; + + SharedCacheContext mpCacheContext; + + /** The current size of preview bitmaps. + */ + Size maPreviewSize; + + bool mbDoSuperSampling; + + /** Both bitmap cache and queue processor are created on demand by this + method. + */ + void ProvideCacheAndProcessor(); +}; + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsPageCache.cxx b/sd/source/ui/slidesorter/cache/SlsPageCache.cxx new file mode 100644 index 000000000..82b1b8ae4 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsPageCache.cxx @@ -0,0 +1,109 @@ +/* -*- 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 <tools/gen.hxx> +#include "SlsGenericPageCache.hxx" +#include <cache/SlsPageCache.hxx> + +using namespace ::com::sun::star; + +namespace sd::slidesorter::cache { + +//===== PageCache ============================================================= + +PageCache::PageCache ( + const Size& rPreviewSize, + const bool bDoSuperSampling, + const SharedCacheContext& rpCacheContext) + : mpImplementation( + new GenericPageCache( + rPreviewSize, + bDoSuperSampling, + rpCacheContext)) +{ +} + +PageCache::~PageCache() +{ +} + +void PageCache::ChangeSize ( + const Size& rPreviewSize, + const bool bDoSuperSampling) +{ + mpImplementation->ChangePreviewSize(rPreviewSize, bDoSuperSampling); +} + +BitmapEx PageCache::GetPreviewBitmap ( + const CacheKey aKey, + const bool bResize) +{ + return mpImplementation->GetPreviewBitmap(aKey, bResize); +} + +BitmapEx PageCache::GetMarkedPreviewBitmap ( + const CacheKey aKey) +{ + return mpImplementation->GetMarkedPreviewBitmap(aKey); +} + +void PageCache::SetMarkedPreviewBitmap ( + const CacheKey aKey, + const BitmapEx& rMarkedBitmap) +{ + mpImplementation->SetMarkedPreviewBitmap(aKey, rMarkedBitmap); +} + +void PageCache::RequestPreviewBitmap (const CacheKey aKey) +{ + return mpImplementation->RequestPreviewBitmap(aKey, true); +} + +void PageCache::InvalidatePreviewBitmap ( + const CacheKey aKey) +{ + if (mpImplementation->InvalidatePreviewBitmap(aKey)) + RequestPreviewBitmap(aKey); +} + +void PageCache::InvalidateCache() +{ + mpImplementation->InvalidateCache(); +} + +void PageCache::SetPreciousFlag ( + const CacheKey aKey, + const bool bIsPrecious) +{ + mpImplementation->SetPreciousFlag(aKey, bIsPrecious); +} + +void PageCache::Pause() +{ + mpImplementation->Pause(); +} + +void PageCache::Resume() +{ + mpImplementation->Resume(); +} + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsPageCacheManager.cxx b/sd/source/ui/slidesorter/cache/SlsPageCacheManager.cxx new file mode 100644 index 000000000..45afd93c9 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsPageCacheManager.cxx @@ -0,0 +1,420 @@ +/* -*- 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 <cache/SlsPageCacheManager.hxx> + +#include "SlsBitmapCache.hxx" + +#include <deque> +#include <map> +#include <memory> +#include <unordered_map> + +namespace { + +/** Collection of data that is stored for all active preview caches. +*/ +class CacheDescriptor +{ +public: + ::sd::slidesorter::cache::PageCacheManager::DocumentKey mpDocument; + Size maPreviewSize; + + CacheDescriptor( + ::sd::slidesorter::cache::PageCacheManager::DocumentKey const & pDocument, + const Size& rPreviewSize) + :mpDocument(pDocument),maPreviewSize(rPreviewSize) + {} + /// Test for equality with respect to all members. + class Equal {public: bool operator() ( + const CacheDescriptor& rDescriptor1, const CacheDescriptor& rDescriptor2) const { + return rDescriptor1.mpDocument==rDescriptor2.mpDocument + && rDescriptor1.maPreviewSize==rDescriptor2.maPreviewSize; + } }; + /// Hash function that takes all members into account. + class Hash {public: size_t operator() (const CacheDescriptor& rDescriptor) const { + return reinterpret_cast<size_t>(rDescriptor.mpDocument.get()) + rDescriptor.maPreviewSize.Width(); + } }; +}; + +/** Collection of data that is stored for the inactive, recently used + caches. +*/ +class RecentlyUsedCacheDescriptor +{ +public: + Size maPreviewSize; + std::shared_ptr< ::sd::slidesorter::cache::BitmapCache> mpCache; + + RecentlyUsedCacheDescriptor( + const Size& rPreviewSize, + const std::shared_ptr< ::sd::slidesorter::cache::BitmapCache>& rpCache) + :maPreviewSize(rPreviewSize),mpCache(rpCache) + {} +}; + +/** The list of recently used caches is organized as queue. When elements + are added the list is shortened to the maximally allowed number of + elements by removing the least recently used elements. +*/ +typedef ::std::deque<RecentlyUsedCacheDescriptor> RecentlyUsedQueue; + +/** Compare the caches by preview size. Those that match the given size + come first, then, regardless of the given size, the largest ones before + the smaller ones. +*/ +class BestFittingCacheComparer +{ +public: + explicit BestFittingCacheComparer (const Size& rPreferredSize) + : maPreferredSize(rPreferredSize) + {} + bool operator()(const ::sd::slidesorter::cache::PageCacheManager::BestFittingPageCaches::value_type& rElement1, + const ::sd::slidesorter::cache::PageCacheManager::BestFittingPageCaches::value_type& rElement2) + { + if (rElement2.first == maPreferredSize) + return false; + else if (rElement1.first == maPreferredSize) + return true; + else + return (rElement1.first.Width()*rElement1.first.Height() + > rElement2.first.Width()*rElement2.first.Height()); + } + +private: + Size maPreferredSize; +}; + +} // end of anonymous namespace + +namespace sd::slidesorter::cache { + +/** Container for the active caches. +*/ +class PageCacheManager::PageCacheContainer + : public std::unordered_map<CacheDescriptor, + std::shared_ptr<BitmapCache>, + CacheDescriptor::Hash, + CacheDescriptor::Equal> +{ +public: + PageCacheContainer() {} + + /** Compare entries in the cache container with respect to the cache + address only. + */ + class CompareWithCache { public: + explicit CompareWithCache(const std::shared_ptr<BitmapCache>& rpCache) + : mpCache(rpCache) {} + bool operator () (const PageCacheContainer::value_type& rValue) const + { return rValue.second == mpCache; } + private: + std::shared_ptr<BitmapCache> mpCache; + }; +}; + +/** The recently used caches are stored in one queue for each document. +*/ +class PageCacheManager::RecentlyUsedPageCaches +{ +public: + typedef DocumentKey key_type; + typedef RecentlyUsedQueue mapped_type; + typedef std::map<key_type,mapped_type>::iterator iterator; +private: + std::map<key_type,mapped_type> maMap; +public: + RecentlyUsedPageCaches () {}; + + iterator end() { return maMap.end(); } + void clear() { maMap.clear(); } + iterator find(const key_type& key) { return maMap.find(key); } + template<class... Args> + std::pair<iterator,bool> emplace(Args&&... args) { return maMap.emplace(std::forward<Args>(args)...); } +}; + +class PageCacheManager::Deleter +{ +public: + void operator() (PageCacheManager* pObject) { delete pObject; } +}; + +//===== PageCacheManager ==================================================== + +std::weak_ptr<PageCacheManager> PageCacheManager::mpInstance; + +std::shared_ptr<PageCacheManager> PageCacheManager::Instance() +{ + std::shared_ptr<PageCacheManager> pInstance; + + ::osl::MutexGuard aGuard (::osl::Mutex::getGlobalMutex()); + + pInstance = mpInstance.lock(); + if (pInstance == nullptr) + { + pInstance = std::shared_ptr<PageCacheManager>( + new PageCacheManager(), + PageCacheManager::Deleter()); + mpInstance = pInstance; + } + + return pInstance; +} + +PageCacheManager::PageCacheManager() + : mpPageCaches(new PageCacheContainer()), + mpRecentlyUsedPageCaches(new RecentlyUsedPageCaches()) +{ +} + +PageCacheManager::~PageCacheManager() +{ +} + +std::shared_ptr<BitmapCache> PageCacheManager::GetCache ( + const DocumentKey& pDocument, + const Size& rPreviewSize) +{ + std::shared_ptr<BitmapCache> pResult; + + // Look for the cache in the list of active caches. + CacheDescriptor aKey (pDocument, rPreviewSize); + PageCacheContainer::iterator iCache (mpPageCaches->find(aKey)); + if (iCache != mpPageCaches->end()) + pResult = iCache->second; + + // Look for the cache in the list of recently used caches. + if (pResult == nullptr) + pResult = GetRecentlyUsedCache(pDocument, rPreviewSize); + + // Create the cache when no suitable one does exist. + if (pResult == nullptr) + pResult = std::make_shared<BitmapCache>(); + + // The cache may be newly created and thus empty or is old and may + // contain previews that are not up-to-date. Recycle previews from + // other caches to fill in the holes. + Recycle(pResult, pDocument,rPreviewSize); + + // Put the new (or old) cache into the container. + mpPageCaches->emplace(aKey, pResult); + + return pResult; +} + +void PageCacheManager::Recycle ( + const std::shared_ptr<BitmapCache>& rpCache, + const DocumentKey& pDocument, + const Size& rPreviewSize) +{ + BestFittingPageCaches aCaches; + + // Add bitmap caches from active caches. + for (auto& rActiveCache : *mpPageCaches) + { + if (rActiveCache.first.mpDocument == pDocument) + aCaches.emplace_back( + rActiveCache.first.maPreviewSize, rActiveCache.second); + } + + // Add bitmap caches from recently used caches. + RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument)); + if (iQueue != mpRecentlyUsedPageCaches->end()) + { + for (const auto& rRecentCache : iQueue->second) + aCaches.emplace_back( + rRecentCache.maPreviewSize, rRecentCache.mpCache); + } + + ::std::sort(aCaches.begin(), aCaches.end(), BestFittingCacheComparer(rPreviewSize)); + + for (const auto& rBestCache : aCaches) + { + rpCache->Recycle(*rBestCache.second); + } +} + +void PageCacheManager::ReleaseCache (const std::shared_ptr<BitmapCache>& rpCache) +{ + PageCacheContainer::iterator iCache (::std::find_if( + mpPageCaches->begin(), + mpPageCaches->end(), + PageCacheContainer::CompareWithCache(rpCache))); + + if (iCache != mpPageCaches->end()) + { + assert(iCache->second == rpCache); + + PutRecentlyUsedCache(iCache->first.mpDocument,iCache->first.maPreviewSize,rpCache); + + mpPageCaches->erase(iCache); + } +} + +std::shared_ptr<BitmapCache> PageCacheManager::ChangeSize ( + const std::shared_ptr<BitmapCache>& rpCache, + const Size&, + const Size& rNewPreviewSize) +{ + std::shared_ptr<BitmapCache> pResult; + + if (rpCache != nullptr) + { + // Look up the given cache in the list of active caches. + PageCacheContainer::iterator iCacheToChange (::std::find_if( + mpPageCaches->begin(), + mpPageCaches->end(), + PageCacheContainer::CompareWithCache(rpCache))); + if (iCacheToChange != mpPageCaches->end()) + { + assert(iCacheToChange->second == rpCache); + + // Now, we can change the preview size of the existing one by + // removing the cache from the list and re-insert it with the + // updated size. + const ::sd::slidesorter::cache::PageCacheManager::DocumentKey aKey ( + iCacheToChange->first.mpDocument); + mpPageCaches->erase(iCacheToChange); + mpPageCaches->emplace( + CacheDescriptor(aKey,rNewPreviewSize), + rpCache); + + pResult = rpCache; + } + else + { + assert(iCacheToChange != mpPageCaches->end()); + } + } + + return pResult; +} + +bool PageCacheManager::InvalidatePreviewBitmap ( + const DocumentKey& pDocument, + const SdrPage* pKey) +{ + bool bHasChanged (false); + + if (pDocument!=nullptr) + { + // Iterate over all caches that are currently in use and invalidate + // the previews in those that belong to the document. + for (auto& rCache : *mpPageCaches) + if (rCache.first.mpDocument == pDocument) + bHasChanged |= rCache.second->InvalidateBitmap(pKey); + + // Invalidate the previews in the recently used caches belonging to + // the given document. + RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument)); + if (iQueue != mpRecentlyUsedPageCaches->end()) + { + for (const auto& rCache2 : iQueue->second) + bHasChanged |= rCache2.mpCache->InvalidateBitmap(pKey); + } + } + + return bHasChanged; +} + +void PageCacheManager::InvalidateAllPreviewBitmaps (const DocumentKey& pDocument) +{ + if (pDocument == nullptr) + return; + + // Iterate over all caches that are currently in use and invalidate the + // previews in those that belong to the document. + for (auto& rCache : *mpPageCaches) + if (rCache.first.mpDocument == pDocument) + rCache.second->InvalidateCache(); + + // Invalidate the previews in the recently used caches belonging to the + // given document. + RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument)); + if (iQueue != mpRecentlyUsedPageCaches->end()) + { + for (const auto& rCache2 : iQueue->second) + rCache2.mpCache->InvalidateCache(); + } +} + +void PageCacheManager::InvalidateAllCaches() +{ + // Iterate over all caches that are currently in use and invalidate + // them. + for (auto& rCache : *mpPageCaches) + rCache.second->InvalidateCache(); + + // Remove all recently used caches, there is not much sense in storing + // invalidated and unused caches. + mpRecentlyUsedPageCaches->clear(); +} + +void PageCacheManager::ReleasePreviewBitmap (const SdrPage* pPage) +{ + for (auto& rCache : *mpPageCaches) + rCache.second->ReleaseBitmap(pPage); +} + +std::shared_ptr<BitmapCache> PageCacheManager::GetRecentlyUsedCache ( + const DocumentKey& pDocument, + const Size& rPreviewSize) +{ + std::shared_ptr<BitmapCache> pCache; + + // Look for the cache in the list of recently used caches. + RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument)); + if (iQueue != mpRecentlyUsedPageCaches->end()) + { + RecentlyUsedQueue::iterator iCache = std::find_if(iQueue->second.begin(), iQueue->second.end(), + [&rPreviewSize](const RecentlyUsedCacheDescriptor& rCache) { return rCache.maPreviewSize == rPreviewSize; }); + if (iCache != iQueue->second.end()) + { + pCache = iCache->mpCache; + iQueue->second.erase(iCache); + } + } + + return pCache; +} + +void PageCacheManager::PutRecentlyUsedCache( + DocumentKey const & pDocument, + const Size& rPreviewSize, + const std::shared_ptr<BitmapCache>& rpCache) +{ + // Look up the list of recently used caches for the given document. + RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument)); + if (iQueue == mpRecentlyUsedPageCaches->end()) + iQueue = mpRecentlyUsedPageCaches->emplace( + pDocument, RecentlyUsedQueue() + ).first; + + if (iQueue != mpRecentlyUsedPageCaches->end()) + { + iQueue->second.push_front(RecentlyUsedCacheDescriptor(rPreviewSize,rpCache)); + // Shorten the list of recently used caches to the allowed maximal length. + while (iQueue->second.size() > mnMaximalRecentlyCacheCount) + iQueue->second.pop_back(); + } +} + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsQueueProcessor.cxx b/sd/source/ui/slidesorter/cache/SlsQueueProcessor.cxx new file mode 100644 index 000000000..077c48709 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsQueueProcessor.cxx @@ -0,0 +1,176 @@ +/* -*- 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 "SlsQueueProcessor.hxx" +#include "SlsRequestQueue.hxx" +#include "SlsBitmapCache.hxx" + +#include <sdpage.hxx> +#include <comphelper/profilezone.hxx> +#include <tools/diagnose_ex.h> + +namespace sd::slidesorter::cache { + +//===== QueueProcessor ====================================================== + +QueueProcessor::QueueProcessor ( + RequestQueue& rQueue, + const std::shared_ptr<BitmapCache>& rpCache, + const Size& rPreviewSize, + const bool bDoSuperSampling, + const SharedCacheContext& rpCacheContext) + : maTimer("sd::QueueProcessor maTimer"), + maPreviewSize(rPreviewSize), + mbDoSuperSampling(bDoSuperSampling), + mpCacheContext(rpCacheContext), + mrQueue(rQueue), + mpCache(rpCache), + mbIsPaused(false) +{ + maTimer.SetInvokeHandler (LINK(this,QueueProcessor,ProcessRequestHdl)); + maTimer.SetTimeout (10); +} + +QueueProcessor::~QueueProcessor() +{ +} + +void QueueProcessor::Start (int nPriorityClass) +{ + if (mbIsPaused) + return; + if ( ! maTimer.IsActive()) + { + if (nPriorityClass == 0) + maTimer.SetTimeout (10); + else + maTimer.SetTimeout (100); + maTimer.Start(); + } +} + +void QueueProcessor::Stop() +{ + if (maTimer.IsActive()) + maTimer.Stop(); +} + +void QueueProcessor::Pause() +{ + mbIsPaused = true; +} + +void QueueProcessor::Resume() +{ + mbIsPaused = false; + if ( ! mrQueue.IsEmpty()) + Start(mrQueue.GetFrontPriorityClass()); +} + +void QueueProcessor::SetPreviewSize ( + const Size& rPreviewSize, + const bool bDoSuperSampling) +{ + maPreviewSize = rPreviewSize; + mbDoSuperSampling = bDoSuperSampling; +} + +IMPL_LINK_NOARG(QueueProcessor, ProcessRequestHdl, Timer *, void) +{ + ProcessRequests(); +} + +void QueueProcessor::ProcessRequests() +{ + assert(mpCacheContext); + + // Never process more than one request at a time in order to prevent the + // lock up of the edit view. + if ( ! mrQueue.IsEmpty() + && ! mbIsPaused + && mpCacheContext->IsIdle()) + { + CacheKey aKey = nullptr; + RequestPriorityClass ePriorityClass (NOT_VISIBLE); + { + ::osl::MutexGuard aGuard (mrQueue.GetMutex()); + + if ( ! mrQueue.IsEmpty()) + { + // Get the request with the highest priority from the queue. + ePriorityClass = mrQueue.GetFrontPriorityClass(); + aKey = mrQueue.GetFront(); + mrQueue.PopFront(); + } + } + + if (aKey != nullptr) + ProcessOneRequest(aKey, ePriorityClass); + } + + // Schedule the processing of the next element(s). + { + ::osl::MutexGuard aGuard (mrQueue.GetMutex()); + if ( ! mrQueue.IsEmpty()) + Start(mrQueue.GetFrontPriorityClass()); + else + { + comphelper::ProfileZone aZone("QueueProcessor finished processing all elements"); + } + } +} + +void QueueProcessor::ProcessOneRequest ( + CacheKey aKey, + const RequestPriorityClass ePriorityClass) +{ + try + { + std::scoped_lock aGuard (maMutex); + + // Create a new preview bitmap and store it in the cache. + if (mpCache != nullptr && mpCacheContext) + { + const SdPage* pSdPage = dynamic_cast<const SdPage*>(mpCacheContext->GetPage(aKey)); + if (pSdPage != nullptr) + { + const BitmapEx aPreview ( + maBitmapFactory.CreateBitmap(*pSdPage, maPreviewSize, mbDoSuperSampling)); + mpCache->SetBitmap (pSdPage, aPreview, ePriorityClass!=NOT_VISIBLE); + + // Initiate a repaint of the new preview. + mpCacheContext->NotifyPreviewCreation(aKey); + } + } + } + catch (css::uno::Exception &) + { + TOOLS_WARN_EXCEPTION( "sd", "QueueProcessor"); + } +} + +void QueueProcessor::SetBitmapCache ( + const std::shared_ptr<BitmapCache>& rpCache) +{ + mpCache = rpCache; +} + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsQueueProcessor.hxx b/sd/source/ui/slidesorter/cache/SlsQueueProcessor.hxx new file mode 100644 index 000000000..0035bcbce --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsQueueProcessor.hxx @@ -0,0 +1,98 @@ +/* -*- 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 . + */ + +#pragma once + +#include <cache/SlsCacheContext.hxx> +#include "SlsRequestPriorityClass.hxx" +#include "SlsBitmapFactory.hxx" + +#include <vcl/timer.hxx> +#include <mutex> + +namespace sd::slidesorter::cache { + +class BitmapCache; +class RequestQueue; + +/** This queue processor is timer based, i.e. when an entry is added to the + queue and the processor is started with Start() in the base class a + timer is started that eventually calls ProcessRequest(). This is + repeated until the queue is empty or Stop() is called. +*/ +class QueueProcessor final +{ +public: + QueueProcessor ( + RequestQueue& rQueue, + const std::shared_ptr<BitmapCache>& rpCache, + const Size& rPreviewSize, + const bool bDoSuperSampling, + const SharedCacheContext& rpCacheContext); + ~QueueProcessor(); + + /** Start the processor. This implementation is timer based and waits + a defined amount of time that depends on the given argument before + the next entry in the queue is processed. + @param nPriorityClass + A priority class of 0 tells the processor that a high priority + request is waiting in the queue. The time to wait is thus + shorter then that for a low priority request (denoted by a value + of 1.) When the timer is already running it is not modified. + */ + void Start (int nPriorityClass); + void Stop(); + void Pause(); + void Resume(); + + void SetPreviewSize ( + const Size& rSize, + const bool bDoSuperSampling); + + /** Use this method when the page cache is (maybe) using a different + BitmapCache. This is usually necessary after calling + PageCacheManager::ChangeSize(). + */ + void SetBitmapCache (const std::shared_ptr<BitmapCache>& rpCache); + +private: + /** This mutex is used to guard the queue processor. Be careful not to + mix its use with that of the solar mutex. + */ + std::mutex maMutex; + + Timer maTimer; + DECL_LINK(ProcessRequestHdl, Timer *, void); + Size maPreviewSize; + bool mbDoSuperSampling; + SharedCacheContext mpCacheContext; + RequestQueue& mrQueue; + std::shared_ptr<BitmapCache> mpCache; + BitmapFactory maBitmapFactory; + bool mbIsPaused; + + void ProcessRequests(); + void ProcessOneRequest ( + CacheKey aKey, + const RequestPriorityClass ePriorityClass); +}; + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsRequestFactory.cxx b/sd/source/ui/slidesorter/cache/SlsRequestFactory.cxx new file mode 100644 index 000000000..6fc6cabc9 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsRequestFactory.cxx @@ -0,0 +1,50 @@ +/* -*- 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 "SlsRequestFactory.hxx" +#include "SlsRequestQueue.hxx" + +namespace sd::slidesorter::cache { + +void RequestFactory::operator()( + RequestQueue& rRequestQueue, + const SharedCacheContext& rpCacheContext) +{ + std::shared_ptr<std::vector<CacheKey> > aKeys; + + // Add the requests for the visible pages. + aKeys = rpCacheContext->GetEntryList(true); + if (aKeys != nullptr) + { + for (const auto& rKey : *aKeys) + rRequestQueue.AddRequest(rKey, VISIBLE_NO_PREVIEW); + } + + // Add the requests for the non-visible pages. + aKeys = rpCacheContext->GetEntryList(false); + if (aKeys != nullptr) + { + for (const auto& rKey : *aKeys) + rRequestQueue.AddRequest(rKey, NOT_VISIBLE); + } +} + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsRequestFactory.hxx b/sd/source/ui/slidesorter/cache/SlsRequestFactory.hxx new file mode 100644 index 000000000..3f4207725 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsRequestFactory.hxx @@ -0,0 +1,36 @@ +/* -*- 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 . + */ + +#pragma once + +#include <cache/SlsCacheContext.hxx> + +namespace sd::slidesorter::cache +{ +class RequestQueue; + +class RequestFactory +{ +public: + void operator()(RequestQueue& rRequestQueue, const SharedCacheContext& rpCacheContext); +}; + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsRequestPriorityClass.hxx b/sd/source/ui/slidesorter/cache/SlsRequestPriorityClass.hxx new file mode 100644 index 000000000..2c84ecbcf --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsRequestPriorityClass.hxx @@ -0,0 +1,44 @@ +/* -*- 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 . + */ + +#pragma once + +namespace sd::slidesorter::cache +{ +/** Each request for a preview creation has a priority. This enum defines + the available priorities. The special values MIN_CLASS and MAX_CLASS + are/can be used for validation and have to be kept up-to-date. +*/ +enum RequestPriorityClass +{ + MIN_CLASS = 0, + + // The slide is visible. A preview does not yet exist. + VISIBLE_NO_PREVIEW = MIN_CLASS, + // The slide is visible. A preview exists but is not up-to-date anymore. + VISIBLE_OUTDATED_PREVIEW, + // The slide is not visible. + NOT_VISIBLE, + + MAX_CLASS = NOT_VISIBLE +}; + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsRequestQueue.cxx b/sd/source/ui/slidesorter/cache/SlsRequestQueue.cxx new file mode 100644 index 000000000..931c1a8f6 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsRequestQueue.cxx @@ -0,0 +1,275 @@ +/* -*- 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 "SlsRequestQueue.hxx" + +#include <sal/log.hxx> + +#include <svx/svdpage.hxx> + +#include <set> + +namespace sd::slidesorter::cache { + +namespace { + +/** This class extends the actual request data with additional information + that is used by the priority queues. +*/ +class Request +{ +public: + Request ( + CacheKey aKey, sal_Int32 nPriority, RequestPriorityClass eClass) + : maKey(aKey), mnPriorityInClass(nPriority), meClass(eClass) + {} + /** Sort requests according to priority classes and then to priorities. + */ + class Comparator { public: + bool operator() (const Request& rRequest1, const Request& rRequest2) + const + { + if (rRequest1.meClass == rRequest2.meClass) + { + if (rRequest1.mnPriorityInClass == rRequest2.mnPriorityInClass) + { + return rRequest1.maKey < rRequest2.maKey; + } + return rRequest1.mnPriorityInClass > rRequest2.mnPriorityInClass; + } + return rRequest1.meClass < rRequest2.meClass; + } + }; + /** Request data is compared arbitrarily by their addresses in memory. + This just establishes an order so that the STL containers are happy. + The order is not semantically interpreted. + */ + class DataComparator + { + public: + explicit DataComparator (const CacheKey aKey) + : maKey(aKey) + { + } + bool operator() (const Request& rRequest) const + { + return maKey == rRequest.maKey; + } + private: + const CacheKey maKey; + }; + + CacheKey maKey; + sal_Int32 mnPriorityInClass; + RequestPriorityClass meClass; +}; + +} + +class RequestQueue::Container + : public ::std::set< + Request, + Request::Comparator> +{ +}; + +//===== GenericRequestQueue ================================================= + +RequestQueue::RequestQueue (const SharedCacheContext& rpCacheContext) + : mpRequestQueue(new Container), + mpCacheContext(rpCacheContext), + mnMinimumPriority(0), + mnMaximumPriority(1) +{ +} + +RequestQueue::~RequestQueue() +{ + Clear(); +} + +void RequestQueue::AddRequest ( + CacheKey aKey, + RequestPriorityClass eRequestClass) +{ + ::osl::MutexGuard aGuard (maMutex); + + assert(eRequestClass>=MIN_CLASS && eRequestClass<=MAX_CLASS); + + // If the request is already a member of the queue then remove it so + // that the following insertion will use the new prioritization. +#if OSL_DEBUG_LEVEL >=2 + bool bRemoved = +#endif + RemoveRequest(aKey); + + // The priority of the request inside its priority class is defined by + // the page number. This ensures a strict top-to-bottom, left-to-right + // order. + sal_Int32 nPriority (mpCacheContext->GetPriority(aKey)); + Request aRequest (aKey, nPriority, eRequestClass); + + std::pair<Container::iterator,bool> ret = mpRequestQueue->insert(aRequest); + bool bInserted = ret.second; + + if (bInserted) + { + SdrPage *pPage = const_cast<SdrPage*>(aRequest.maKey); + pPage->AddPageUser(*this); + } + +#if OSL_DEBUG_LEVEL >=2 + SAL_INFO("sd.sls", __func__ << ": " << (bRemoved?"replaced":"added") + << " request for page " << ((aKey->GetPageNum()-1)/2) + << " with priority class " << static_cast<int>(eRequestClass)); +#endif +} + +void RequestQueue::PageInDestruction(const SdrPage& rPage) +{ + //remove any requests pending for this page which is going away now + RemoveRequest(&rPage); +} + +#if OSL_DEBUG_LEVEL >=2 +bool +#else +void +#endif +RequestQueue::RemoveRequest( + CacheKey aKey) +{ + ::osl::MutexGuard aGuard (maMutex); +#if OSL_DEBUG_LEVEL >=2 + bool bIsRemoved = false; +#endif + while(true) + { + Container::const_iterator aRequestIterator = ::std::find_if ( + mpRequestQueue->begin(), + mpRequestQueue->end(), + Request::DataComparator(aKey)); + if (aRequestIterator != mpRequestQueue->end()) + { + if (aRequestIterator->mnPriorityInClass == mnMinimumPriority+1) + mnMinimumPriority++; + else if (aRequestIterator->mnPriorityInClass == mnMaximumPriority-1) + mnMaximumPriority--; + + SdrPage *pPage = const_cast<SdrPage*>(aRequestIterator->maKey); + pPage->RemovePageUser(*this); + mpRequestQueue->erase(aRequestIterator); +#if OSL_DEBUG_LEVEL >=2 + bIsRemoved = true; +#endif + } + else + break; + } +#if OSL_DEBUG_LEVEL >=2 + return bIsRemoved; +#endif + +} + +void RequestQueue::ChangeClass ( + CacheKey aKey, + RequestPriorityClass eNewRequestClass) +{ + ::osl::MutexGuard aGuard (maMutex); + + assert(eNewRequestClass>=MIN_CLASS && eNewRequestClass<=MAX_CLASS); + + Container::const_iterator iRequest ( + ::std::find_if ( + mpRequestQueue->begin(), + mpRequestQueue->end(), + Request::DataComparator(aKey))); + if (iRequest!=mpRequestQueue->end() && iRequest->meClass!=eNewRequestClass) + { + AddRequest(aKey, eNewRequestClass); + } +} + +CacheKey RequestQueue::GetFront() +{ + ::osl::MutexGuard aGuard (maMutex); + + if (mpRequestQueue->empty()) + throw css::uno::RuntimeException("RequestQueue::GetFront(): queue is empty", + nullptr); + + return mpRequestQueue->begin()->maKey; +} + +RequestPriorityClass RequestQueue::GetFrontPriorityClass() +{ + ::osl::MutexGuard aGuard (maMutex); + + if (mpRequestQueue->empty()) + throw css::uno::RuntimeException("RequestQueue::GetFrontPriorityClass(): queue is empty", + nullptr); + + return mpRequestQueue->begin()->meClass; +} + +void RequestQueue::PopFront() +{ + ::osl::MutexGuard aGuard (maMutex); + + if ( mpRequestQueue->empty()) + return; + + Container::const_iterator aIter(mpRequestQueue->begin()); + SdrPage *pPage = const_cast<SdrPage*>(aIter->maKey); + pPage->RemovePageUser(*this); + mpRequestQueue->erase(aIter); + + // Reset the priority counter if possible. + if (mpRequestQueue->empty()) + { + mnMinimumPriority = 0; + mnMaximumPriority = 1; + } +} + +bool RequestQueue::IsEmpty() +{ + ::osl::MutexGuard aGuard (maMutex); + return mpRequestQueue->empty(); +} + +void RequestQueue::Clear() +{ + ::osl::MutexGuard aGuard (maMutex); + + for (const auto& rItem : *mpRequestQueue) + { + SdrPage *pPage = const_cast<SdrPage*>(rItem.maKey); + pPage->RemovePageUser(*this); + } + + mpRequestQueue->clear(); + mnMinimumPriority = 0; + mnMaximumPriority = 1; +} + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsRequestQueue.hxx b/sd/source/ui/slidesorter/cache/SlsRequestQueue.hxx new file mode 100644 index 000000000..618ba5801 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsRequestQueue.hxx @@ -0,0 +1,122 @@ +/* -*- 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 . + */ + +#pragma once + +#include "SlsRequestPriorityClass.hxx" +#include <cache/SlsCacheContext.hxx> +#include <osl/mutex.hxx> +#include <svx/sdrpageuser.hxx> + +#include <memory> + +namespace sd::slidesorter::cache +{ +/** The request queue stores requests that are described by the Request + sorted according to priority class and then priority. +*/ +class RequestQueue : public sdr::PageUser +{ +public: + explicit RequestQueue(const SharedCacheContext& rpCacheContext); + virtual ~RequestQueue(); + + /** Insert a request with highest or lowest priority in its priority + class. When the request is already present then it is first + removed. This effect is then a re-prioritization. + @param aKey + The request. + @param eRequestClass + The priority class in which to insert the request with highest + or lowest priority. + @param bInsertWithHighestPriority + When this flag is <TRUE/> the request is inserted with highest + priority in its class. When <FALSE/> the request is inserted + with lowest priority. + */ + void AddRequest(CacheKey aKey, RequestPriorityClass eRequestClass); + + /** Remove the specified request from the queue. + @param aKey + It is OK when the specified request is not a member of the + queue. + */ +#if OSL_DEBUG_LEVEL >= 2 + bool +#else + void +#endif + RemoveRequest(CacheKey aKey); + + /** Change the priority class of the specified request. + */ + void ChangeClass(CacheKey aKey, RequestPriorityClass eNewRequestClass); + + /** Get the request with the highest priority int the highest priority class. + */ + CacheKey GetFront(); + + // For debugging. + RequestPriorityClass GetFrontPriorityClass(); + + /** Really a synonym for RemoveRequest(GetFront()); + */ + void PopFront(); + + /** Returns <TRUE/> when there is no element in the queue. + */ + bool IsEmpty(); + + /** Remove all requests from the queue. This resets the minimum and + maximum priorities to their default values. + */ + void Clear(); + + /** Return the mutex that guards the access to the priority queue. + */ + ::osl::Mutex& GetMutex() { return maMutex; } + + /** Ensure we don't hand out a page deleted before anyone got a + chance to process it + */ + virtual void PageInDestruction(const SdrPage& rPage) override; + +private: + ::osl::Mutex maMutex; + class Container; + std::unique_ptr<Container> mpRequestQueue; + SharedCacheContext mpCacheContext; + + /** A lower bound of the lowest priority of all elements in the queues. + The start value is 0. It is assigned and then decreased every time + when an element is inserted or marked as the request with lowest + priority. + */ + int mnMinimumPriority; + /** An upper bound of the highest priority of all elements in the queues. + The start value is 1. It is assigned and then increased every time + when an element is inserted or marked as the request with highest + priority. + */ + int mnMaximumPriority; +}; + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlideSorterController.cxx b/sd/source/ui/slidesorter/controller/SlideSorterController.cxx new file mode 100644 index 000000000..5c851f183 --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlideSorterController.cxx @@ -0,0 +1,910 @@ +/* -*- 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 <controller/SlideSorterController.hxx> + +#include <SlideSorter.hxx> +#include <controller/SlsPageSelector.hxx> +#include <controller/SlsSelectionFunction.hxx> +#include <controller/SlsProperties.hxx> +#include <controller/SlsCurrentSlideManager.hxx> +#include "SlsListener.hxx" +#include <controller/SlsFocusManager.hxx> +#include <controller/SlsAnimator.hxx> +#include <controller/SlsClipboard.hxx> +#include <controller/SlsInsertionIndicatorHandler.hxx> +#include <controller/SlsScrollBarManager.hxx> +#include <controller/SlsSelectionManager.hxx> +#include <controller/SlsSlotManager.hxx> +#include <controller/SlsVisibleAreaManager.hxx> +#include <model/SlideSorterModel.hxx> +#include <model/SlsPageEnumerationProvider.hxx> +#include <model/SlsPageDescriptor.hxx> +#include <view/SlideSorterView.hxx> +#include <view/SlsLayouter.hxx> +#include <view/SlsPageObjectLayouter.hxx> +#include <view/SlsTheme.hxx> +#include <view/SlsToolTip.hxx> +#include <cache/SlsPageCache.hxx> +#include <cache/SlsPageCacheManager.hxx> +#include <tools/diagnose_ex.h> + +#include <drawdoc.hxx> +#include <ViewShellBase.hxx> +#include <Window.hxx> +#include <FrameView.hxx> +#include <sdpage.hxx> + +#include <app.hrc> +#include <sdmod.hxx> +#include <ViewShellHint.hxx> +#include <AccessibleSlideSorterView.hxx> +#include <AccessibleSlideSorterObject.hxx> + +#include <vcl/window.hxx> +#include <svx/svxids.hrc> +#include <sfx2/request.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/dispatch.hxx> +#include <tools/debug.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> + +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> + +#include <memory> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::sd::slidesorter::model; +using namespace ::sd::slidesorter::view; +using namespace ::sd::slidesorter::controller; +using namespace ::basegfx; + +namespace sd::slidesorter::controller { + +SlideSorterController::SlideSorterController (SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter), + mrModel(mrSlideSorter.GetModel()), + mrView(mrSlideSorter.GetView()), + mpInsertionIndicatorHandler(std::make_shared<InsertionIndicatorHandler>(rSlideSorter)), + mpAnimator(std::make_shared<Animator>(rSlideSorter)), + mpVisibleAreaManager(new VisibleAreaManager(rSlideSorter)), + mnModelChangeLockCount(0), + mbIsForcedRearrangePending(false), + mbContextMenuOpen(false), + mbPostModelChangePending(false), + mnCurrentPageBeforeSwitch(0), + mpEditModeChangeMasterPage(nullptr), + mnPaintEntranceCount(0) +{ + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + OSL_ASSERT(pWindow); + if (!pWindow) + return; + + // The whole background is painted by the view and controls. + vcl::Window* pParentWindow = pWindow->GetParent(); + OSL_ASSERT(pParentWindow!=nullptr); + pParentWindow->SetBackground (Wallpaper()); + + // Connect the view with the window that has been created by our base + // class. + pWindow->SetBackground(Wallpaper()); + pWindow->SetCenterAllowed(false); + pWindow->SetMapMode(MapMode(MapUnit::MapPixel)); + pWindow->SetViewSize(mrView.GetModelArea().GetSize()); +} + +void SlideSorterController::Init() +{ + mpCurrentSlideManager = std::make_shared<CurrentSlideManager>(mrSlideSorter); + mpPageSelector.reset(new PageSelector(mrSlideSorter)); + mpFocusManager.reset(new FocusManager(mrSlideSorter)); + mpSlotManager = std::make_shared<SlotManager>(mrSlideSorter); + mpScrollBarManager.reset(new ScrollBarManager(mrSlideSorter)); + mpSelectionManager = std::make_shared<SelectionManager>(mrSlideSorter); + mpClipboard.reset(new Clipboard(mrSlideSorter)); + + // Create the selection function. + SfxRequest aRequest ( + SID_OBJECT_SELECT, + SfxCallMode::SLOT, + mrModel.GetDocument()->GetItemPool()); + mrSlideSorter.SetCurrentFunction(CreateSelectionFunction(aRequest)); + + mpListener = new Listener(mrSlideSorter); + + mpPageSelector->GetCoreSelection(); + GetSelectionManager()->SelectionHasChanged(); +} + +SlideSorterController::~SlideSorterController() +{ + try + { + uno::Reference<lang::XComponent> xComponent = mpListener; + if (xComponent.is()) + xComponent->dispose(); + } + catch( uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideSorterController::~SlideSorterController()" ); + } + + // dispose should have been called by now so that nothing is to be done + // to shut down cleanly. +} + +void SlideSorterController::Dispose() +{ + mpInsertionIndicatorHandler->End(Animator::AM_Immediate); + mpClipboard.reset(); + mpSelectionManager.reset(); + mpAnimator->Dispose(); +} + +model::SharedPageDescriptor SlideSorterController::GetPageAt ( + const Point& aWindowPosition) +{ + sal_Int32 nHitPageIndex (mrView.GetPageIndexAtPoint(aWindowPosition)); + model::SharedPageDescriptor pDescriptorAtPoint; + if (nHitPageIndex >= 0) + { + pDescriptorAtPoint = mrModel.GetPageDescriptor(nHitPageIndex); + + // Depending on a property we may have to check that the mouse is no + // just over the page object but over the preview area. + if (pDescriptorAtPoint + && ! pDescriptorAtPoint->HasState(PageDescriptor::ST_Selected)) + { + // Make sure that the mouse is over the preview area. + if ( ! mrView.GetLayouter().GetPageObjectLayouter()->GetBoundingBox( + pDescriptorAtPoint, + view::PageObjectLayouter::Part::Preview, + view::PageObjectLayouter::WindowCoordinateSystem).Contains(aWindowPosition)) + { + pDescriptorAtPoint.reset(); + } + } + } + + return pDescriptorAtPoint; +} + +PageSelector& SlideSorterController::GetPageSelector() +{ + OSL_ASSERT(mpPageSelector != nullptr); + return *mpPageSelector; +} + +FocusManager& SlideSorterController::GetFocusManager() +{ + OSL_ASSERT(mpFocusManager != nullptr); + return *mpFocusManager; +} + +Clipboard& SlideSorterController::GetClipboard() +{ + OSL_ASSERT(mpClipboard != nullptr); + return *mpClipboard; +} + +ScrollBarManager& SlideSorterController::GetScrollBarManager() +{ + OSL_ASSERT(mpScrollBarManager != nullptr); + return *mpScrollBarManager; +} + +std::shared_ptr<CurrentSlideManager> const & SlideSorterController::GetCurrentSlideManager() const +{ + OSL_ASSERT(mpCurrentSlideManager != nullptr); + return mpCurrentSlideManager; +} + +std::shared_ptr<SlotManager> const & SlideSorterController::GetSlotManager() const +{ + OSL_ASSERT(mpSlotManager != nullptr); + return mpSlotManager; +} + +std::shared_ptr<SelectionManager> const & SlideSorterController::GetSelectionManager() const +{ + OSL_ASSERT(mpSelectionManager != nullptr); + return mpSelectionManager; +} + +std::shared_ptr<InsertionIndicatorHandler> const & + SlideSorterController::GetInsertionIndicatorHandler() const +{ + OSL_ASSERT(mpInsertionIndicatorHandler != nullptr); + return mpInsertionIndicatorHandler; +} + +void SlideSorterController::Paint ( + const ::tools::Rectangle& rBBox, + vcl::Window* pWindow) +{ + if (mnPaintEntranceCount != 0) + return; + + ++mnPaintEntranceCount; + + try + { + mrView.CompleteRedraw(pWindow->GetOutDev(), vcl::Region(rBBox)); + } + catch (const Exception&) + { + // Ignore all exceptions. + } + + --mnPaintEntranceCount; +} + +void SlideSorterController::FuTemporary (SfxRequest& rRequest) +{ + mpSlotManager->FuTemporary (rRequest); +} + +void SlideSorterController::FuPermanent (SfxRequest &rRequest) +{ + mpSlotManager->FuPermanent (rRequest); +} + +void SlideSorterController::FuSupport (SfxRequest &rRequest) +{ + mpSlotManager->FuSupport (rRequest); +} + +bool SlideSorterController::Command ( + const CommandEvent& rEvent, + ::sd::Window* pWindow) +{ + bool bEventHasBeenHandled = false; + + if (pWindow == nullptr) + return false; + + ViewShell* pViewShell = mrSlideSorter.GetViewShell(); + if (pViewShell == nullptr) + return false; + + switch (rEvent.GetCommand()) + { + case CommandEventId::ContextMenu: + { + SdPage* pPage = nullptr; + OUString aPopupId; + + model::PageEnumeration aSelectedPages ( + PageEnumerationProvider::CreateSelectedPagesEnumeration(mrModel)); + if (aSelectedPages.HasMoreElements()) + pPage = aSelectedPages.GetNextElement()->GetPage(); + + if (mrModel.GetEditMode() == EditMode::Page) + { + if (pPage != nullptr) + aPopupId = "pagepane"; + else + aPopupId = "pagepanenosel"; + } + else if (pPage != nullptr) + aPopupId = "pagepanemaster"; + else + aPopupId = "pagepanenoselmaster"; + + std::unique_ptr<InsertionIndicatorHandler::ForceShowContext, o3tl::default_delete<InsertionIndicatorHandler::ForceShowContext>> xContext; + if (pPage == nullptr) + { + // When there is no selection, then we show the insertion + // indicator so that the user knows where a page insertion + // would take place. + mpInsertionIndicatorHandler->Start(false); + mpInsertionIndicatorHandler->UpdateIndicatorIcon(SD_MOD()->pTransferClip); + mpInsertionIndicatorHandler->UpdatePosition( + pWindow->PixelToLogic(rEvent.GetMousePosPixel()), + InsertionIndicatorHandler::MoveMode); + xContext.reset(new InsertionIndicatorHandler::ForceShowContext( + mpInsertionIndicatorHandler)); + } + + pWindow->ReleaseMouse(); + + Point aMenuLocation (0,0); + if (!rEvent.IsMouseEvent()) + { + // The event is not a mouse event. Use the center of the + // focused page as top left position of the context menu. + model::SharedPageDescriptor pDescriptor ( + GetFocusManager().GetFocusedPageDescriptor()); + if (pDescriptor) + { + ::tools::Rectangle aBBox ( + mrView.GetLayouter().GetPageObjectLayouter()->GetBoundingBox ( + pDescriptor, + PageObjectLayouter::Part::PageObject, + PageObjectLayouter::ModelCoordinateSystem)); + aMenuLocation = aBBox.Center(); + } + } + + if (SfxDispatcher* pDispatcher = pViewShell->GetDispatcher()) + { + mbContextMenuOpen = true; + if (!rEvent.IsMouseEvent()) + pDispatcher->ExecutePopup(aPopupId, pWindow, &aMenuLocation); + else + pDispatcher->ExecutePopup(aPopupId, pWindow); + mbContextMenuOpen = false; + mrSlideSorter.GetView().UpdatePageUnderMouse(); + ::rtl::Reference<SelectionFunction> pFunction(GetCurrentSelectionFunction()); + if (pFunction.is()) + pFunction->ResetMouseAnchor(); + } + if (pPage == nullptr) + { + // Remember the position of the insertion indicator before + // it is hidden, so that a pending slide insertion slot call + // finds the right place to insert a new slide. + GetSelectionManager()->SetInsertionPosition( + GetInsertionIndicatorHandler()->GetInsertionPageIndex()); + } + xContext.reset(); + bEventHasBeenHandled = true; + } + break; + + case CommandEventId::Wheel: + { + const CommandWheelData* pData = rEvent.GetWheelData(); + if (pData == nullptr) + return false; + if (pData->IsMod1()) + { + // We do not support zooming with control+mouse wheel. + return false; + } + // Determine whether to scroll horizontally or vertically. This + // depends on the orientation of the scroll bar and the + // IsHoriz() flag of the event. + if ((mrSlideSorter.GetView().GetOrientation()==view::Layouter::HORIZONTAL) + == pData->IsHorz()) + { + GetScrollBarManager().Scroll( + ScrollBarManager::Orientation_Vertical, + -pData->GetNotchDelta()); + } + else + { + GetScrollBarManager().Scroll( + ScrollBarManager::Orientation_Horizontal, + -pData->GetNotchDelta()); + } + mrSlideSorter.GetView().UpdatePageUnderMouse(rEvent.GetMousePosPixel()); + + bEventHasBeenHandled = true; + } + break; + + default: break; + } + + return bEventHasBeenHandled; +} + +void SlideSorterController::LockModelChange() +{ + mnModelChangeLockCount += 1; +} + +void SlideSorterController::UnlockModelChange() +{ + mnModelChangeLockCount -= 1; + if (mnModelChangeLockCount==0 && mbPostModelChangePending) + { + PostModelChange(); + } +} + +void SlideSorterController::PreModelChange() +{ + // Prevent PreModelChange to execute more than once per model lock. + if (mbPostModelChangePending) + return; + + if (mrSlideSorter.GetViewShell() != nullptr) + mrSlideSorter.GetViewShell()->Broadcast( + ViewShellHint(ViewShellHint::HINT_COMPLEX_MODEL_CHANGE_START)); + + GetCurrentSlideManager()->PrepareModelChange(); + + if (mrSlideSorter.GetContentWindow()) + mrView.PreModelChange(); + + mbPostModelChangePending = true; +} + +void SlideSorterController::PostModelChange() +{ + mbPostModelChangePending = false; + mrModel.Resync(); + + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if (pWindow) + { + GetCurrentSlideManager()->HandleModelChange(); + + mrView.PostModelChange (); + + pWindow->SetViewOrigin (Point (0,0)); + pWindow->SetViewSize (mrView.GetModelArea().GetSize()); + + // The visibility of the scroll bars may have to be changed. Then + // the size of the view has to change, too. Let Rearrange() handle + // that. + Rearrange(mbIsForcedRearrangePending); + } + + if (mrSlideSorter.GetViewShell() != nullptr) + mrSlideSorter.GetViewShell()->Broadcast( + ViewShellHint(ViewShellHint::HINT_COMPLEX_MODEL_CHANGE_END)); +} + +void SlideSorterController::HandleModelChange() +{ + // Ignore this call when the document is not in a valid state, i.e. has + // not the same number of regular and notes pages. + bool bIsDocumentValid = (mrModel.GetDocument()->GetPageCount() % 2 == 1); + + if (bIsDocumentValid) + { + ModelChangeLock aLock (*this); + PreModelChange(); + } +} + +IMPL_LINK(SlideSorterController, ApplicationEventHandler, VclSimpleEvent&, rEvent, void) +{ + auto windowEvent = dynamic_cast<VclWindowEvent *>(&rEvent); + if (windowEvent != nullptr) { + WindowEventHandler(*windowEvent); + } +} +IMPL_LINK(SlideSorterController, WindowEventHandler, VclWindowEvent&, rEvent, void) +{ + vcl::Window* pWindow = rEvent.GetWindow(); + sd::Window *pActiveWindow (mrSlideSorter.GetContentWindow().get()); + switch (rEvent.GetId()) + { + case VclEventId::WindowActivate: + case VclEventId::WindowShow: + if (pActiveWindow && pWindow == pActiveWindow->GetParent()) + mrView.RequestRepaint(); + break; + + case VclEventId::WindowHide: + if (pActiveWindow && pWindow == pActiveWindow->GetParent()) + mrView.SetPageUnderMouse(SharedPageDescriptor()); + break; + + case VclEventId::WindowGetFocus: + if (pActiveWindow) + if (pWindow == pActiveWindow) + GetFocusManager().ShowFocus(false); + break; + + case VclEventId::WindowLoseFocus: + if (pActiveWindow && pWindow == pActiveWindow) + { + GetFocusManager().HideFocus(); + mrView.GetToolTip().Hide(); + + //don't scroll back to the selected slide when we lose + //focus due to a temporary active context menu + if (!mbContextMenuOpen) + { + // Select the current slide so that it is properly + // visualized when the focus is moved to the edit view. + GetPageSelector().SelectPage(GetCurrentSlideManager()->GetCurrentSlide()); + } + } + break; + + case VclEventId::ApplicationDataChanged: + { + // Invalidate the preview cache. + cache::PageCacheManager::Instance()->InvalidateAllCaches(); + + // Update the draw mode. + DrawModeFlags nDrawMode (Application::GetSettings().GetStyleSettings().GetHighContrastMode() + ? sd::OUTPUT_DRAWMODE_CONTRAST + : sd::OUTPUT_DRAWMODE_COLOR); + if (mrSlideSorter.GetViewShell() != nullptr) + mrSlideSorter.GetViewShell()->GetFrameView()->SetDrawMode(nDrawMode); + if (pActiveWindow != nullptr) + pActiveWindow->GetOutDev()->SetDrawMode(nDrawMode); + mrView.HandleDrawModeChange(); + + // When the system font has changed a layout has to be done. + mrView.Resize(); + + // Update theme colors. + mrSlideSorter.GetProperties()->HandleDataChangeEvent(); + mrSlideSorter.GetTheme()->Update(mrSlideSorter.GetProperties()); + mrView.HandleDataChangeEvent(); + } + break; + + default: + break; + } +} + +void SlideSorterController::GetCtrlState (SfxItemSet& rSet) +{ + if (rSet.GetItemState(SID_RELOAD) != SfxItemState::UNKNOWN) + { + // let SFx en-/disable "last version" + SfxViewFrame* pSlideViewFrame = SfxViewFrame::Current(); + DBG_ASSERT(pSlideViewFrame!=nullptr, + "SlideSorterController::GetCtrlState: ViewFrame not found"); + if (pSlideViewFrame) + { + pSlideViewFrame->GetSlotState (SID_RELOAD, nullptr, &rSet); + } + else // MI says: no MDIFrame --> disable + { + rSet.DisableItem(SID_RELOAD); + } + } + + // Output quality. + if (rSet.GetItemState(SID_OUTPUT_QUALITY_COLOR)==SfxItemState::DEFAULT + ||rSet.GetItemState(SID_OUTPUT_QUALITY_GRAYSCALE)==SfxItemState::DEFAULT + ||rSet.GetItemState(SID_OUTPUT_QUALITY_BLACKWHITE)==SfxItemState::DEFAULT + ||rSet.GetItemState(SID_OUTPUT_QUALITY_CONTRAST)==SfxItemState::DEFAULT) + { + if (mrSlideSorter.GetContentWindow()) + { + DrawModeFlags nMode = mrSlideSorter.GetContentWindow()->GetOutDev()->GetDrawMode(); + sal_uInt16 nQuality = 0; + + if (nMode == sd::OUTPUT_DRAWMODE_COLOR) { + nQuality = 0; + } else if (nMode == sd::OUTPUT_DRAWMODE_GRAYSCALE) { + nQuality = 1; + } else if (nMode == sd::OUTPUT_DRAWMODE_BLACKWHITE) { + nQuality = 2; + } else if (nMode == sd::OUTPUT_DRAWMODE_CONTRAST) { + nQuality = 3; + } + + rSet.Put (SfxBoolItem (SID_OUTPUT_QUALITY_COLOR, nQuality==0)); + rSet.Put (SfxBoolItem (SID_OUTPUT_QUALITY_GRAYSCALE, nQuality==1)); + rSet.Put (SfxBoolItem (SID_OUTPUT_QUALITY_BLACKWHITE, nQuality==2)); + rSet.Put (SfxBoolItem (SID_OUTPUT_QUALITY_CONTRAST, nQuality==3)); + } + } + + if (rSet.GetItemState(SID_MAIL_SCROLLBODY_PAGEDOWN) == SfxItemState::DEFAULT) + { + rSet.Put (SfxBoolItem( SID_MAIL_SCROLLBODY_PAGEDOWN, true)); + } +} + +void SlideSorterController::GetStatusBarState (SfxItemSet& rSet) +{ + mpSlotManager->GetStatusBarState (rSet); +} + +void SlideSorterController::ExecCtrl (SfxRequest& rRequest) +{ + mpSlotManager->ExecCtrl (rRequest); +} + +void SlideSorterController::GetAttrState (SfxItemSet& rSet) +{ + mpSlotManager->GetAttrState (rSet); +} + +void SlideSorterController::UpdateAllPages() +{ + // Do a redraw. + mrSlideSorter.GetContentWindow()->Invalidate(); +} + +void SlideSorterController::Resize (const ::tools::Rectangle& rAvailableSpace) +{ + if (maTotalWindowArea != rAvailableSpace) + { + maTotalWindowArea = rAvailableSpace; + Rearrange(true); + } +} + +void SlideSorterController::Rearrange (bool bForce) +{ + if (maTotalWindowArea.IsEmpty()) + return; + + if (mnModelChangeLockCount>0) + { + mbIsForcedRearrangePending |= bForce; + return; + } + else + mbIsForcedRearrangePending = false; + + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if (!pWindow) + return; + + if (bForce) + mrView.UpdateOrientation(); + + // Place the scroll bars. + ::tools::Rectangle aNewContentArea = GetScrollBarManager().PlaceScrollBars( + maTotalWindowArea, + mrView.GetOrientation() != view::Layouter::VERTICAL, + mrView.GetOrientation() != view::Layouter::HORIZONTAL); + + bool bSizeHasChanged (false); + // Only when bForce is not true we have to test for a size change in + // order to determine whether the window and the view have to be resized. + if ( ! bForce) + { + ::tools::Rectangle aCurrentContentArea (pWindow->GetPosPixel(), pWindow->GetOutputSizePixel()); + bSizeHasChanged = (aNewContentArea != aCurrentContentArea); + } + if (bForce || bSizeHasChanged) + { + // The browser window gets the remaining space. + pWindow->SetPosSizePixel (aNewContentArea.TopLeft(), aNewContentArea.GetSize()); + mrView.Resize(); + } + + // Adapt the scroll bars to the new zoom factor of the browser + // window and the arrangement of the page objects. + GetScrollBarManager().UpdateScrollBars(!bForce); + + // Keep the current slide in the visible area. + GetVisibleAreaManager().RequestCurrentSlideVisible(); + + mrView.RequestRepaint(); +} + +rtl::Reference<FuPoor> SlideSorterController::CreateSelectionFunction (SfxRequest& rRequest) +{ + rtl::Reference<FuPoor> xFunc( SelectionFunction::Create(mrSlideSorter, rRequest) ); + return xFunc; +} + +::rtl::Reference<SelectionFunction> SlideSorterController::GetCurrentSelectionFunction() const +{ + rtl::Reference<FuPoor> pFunction (mrSlideSorter.GetViewShell()->GetCurrentFunction()); + return ::rtl::Reference<SelectionFunction>(dynamic_cast<SelectionFunction*>(pFunction.get())); +} + +void SlideSorterController::PrepareEditModeChange() +{ + // Before we throw away the page descriptors we prepare for selecting + // descriptors in the other mode and for restoring the current + // selection when switching back to the current mode. + if (mrModel.GetEditMode() != EditMode::Page) + return; + + maSelectionBeforeSwitch.clear(); + + // Search for the first selected page and determine the master page + // used by its page object. It will be selected after the switch. + // In the same loop the current selection is stored. + PageEnumeration aSelectedPages ( + PageEnumerationProvider::CreateSelectedPagesEnumeration(mrModel)); + while (aSelectedPages.HasMoreElements()) + { + SharedPageDescriptor pDescriptor (aSelectedPages.GetNextElement()); + SdPage* pPage = pDescriptor->GetPage(); + // Remember the master page of the first selected descriptor. + if (pPage!=nullptr && mpEditModeChangeMasterPage==nullptr) + mpEditModeChangeMasterPage = &static_cast<SdPage&>( + pPage->TRG_GetMasterPage()); + + maSelectionBeforeSwitch.push_back(pPage); + } + + // Remember the current page. + if (mrSlideSorter.GetViewShell() != nullptr) + mnCurrentPageBeforeSwitch = (mrSlideSorter.GetViewShell()->GetViewShellBase() + .GetMainViewShell()->GetActualPage()->GetPageNum()-1)/2; +} + +void SlideSorterController::ChangeEditMode (EditMode eEditMode) +{ + if (mrModel.GetEditMode() != eEditMode) + { + ModelChangeLock aLock (*this); + PreModelChange(); + // Do the actual edit mode switching. + bool bResult = mrModel.SetEditMode(eEditMode); + if (bResult) + HandleModelChange(); + } +} + +void SlideSorterController::FinishEditModeChange() +{ + if (mrModel.GetEditMode() == EditMode::MasterPage) + { + mpPageSelector->DeselectAllPages(); + + // Search for the master page that was determined in + // PrepareEditModeChange() and make it the current page. + PageEnumeration aAllPages (PageEnumerationProvider::CreateAllPagesEnumeration(mrModel)); + while (aAllPages.HasMoreElements()) + { + SharedPageDescriptor pDescriptor (aAllPages.GetNextElement()); + if (pDescriptor->GetPage() == mpEditModeChangeMasterPage) + { + GetCurrentSlideManager()->SwitchCurrentSlide(pDescriptor); + mpPageSelector->SelectPage(pDescriptor); + break; + } + } + } + else + { + PageSelector::BroadcastLock aBroadcastLock (*mpPageSelector); + + SharedPageDescriptor pDescriptor (mrModel.GetPageDescriptor(mnCurrentPageBeforeSwitch)); + GetCurrentSlideManager()->SwitchCurrentSlide(pDescriptor); + + // Restore the selection. + mpPageSelector->DeselectAllPages(); + for (const auto& rpPage : maSelectionBeforeSwitch) + { + mpPageSelector->SelectPage(rpPage); + } + maSelectionBeforeSwitch.clear( ); + } + mpEditModeChangeMasterPage = nullptr; +} + +void SlideSorterController::PageNameHasChanged (int nPageIndex, const OUString& rsOldName) +{ + // Request a repaint for the page object whose name has changed. + model::SharedPageDescriptor pDescriptor (mrModel.GetPageDescriptor(nPageIndex)); + if (pDescriptor) + mrView.RequestRepaint(pDescriptor); + + // Get a pointer to the corresponding accessible object and notify + // that of the name change. + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if ( ! pWindow) + return; + + css::uno::Reference< css::accessibility::XAccessible > + xAccessible (pWindow->GetAccessible(false)); + if ( ! xAccessible.is()) + return; + + // Now comes a small hack. We assume that the accessible object is + // an instantiation of AccessibleSlideSorterView and cast it to that + // class. The cleaner alternative to this cast would be a new member + // in which we would store the last AccessibleSlideSorterView object + // created by SlideSorterViewShell::CreateAccessibleDocumentView(). + // But then there is no guaranty that the accessible object obtained + // from the window really is that instance last created by + // CreateAccessibleDocumentView(). + // However, the dynamic cast together with the check of the result + // being NULL should be safe enough. + ::accessibility::AccessibleSlideSorterView* pAccessibleView + = dynamic_cast< ::accessibility::AccessibleSlideSorterView*>(xAccessible.get()); + if (pAccessibleView == nullptr) + return; + + ::accessibility::AccessibleSlideSorterObject* pChild + = pAccessibleView->GetAccessibleChildImplementation(nPageIndex); + if (pChild == nullptr || pChild->GetPage() == nullptr) + return; + + OUString sNewName (pChild->GetPage()->GetName()); + pChild->FireAccessibleEvent( + css::accessibility::AccessibleEventId::NAME_CHANGED, + Any(rsOldName), + Any(sNewName)); +} + +void SlideSorterController::SetDocumentSlides (const Reference<container::XIndexAccess>& rxSlides) +{ + if (mrModel.GetDocumentSlides() != rxSlides) + { + ModelChangeLock aLock (*this); + PreModelChange(); + + mrModel.SetDocumentSlides(rxSlides); + } +} + +VisibleAreaManager& SlideSorterController::GetVisibleAreaManager() const +{ + OSL_ASSERT(mpVisibleAreaManager); + return *mpVisibleAreaManager; +} + +void SlideSorterController::CheckForMasterPageAssignment() +{ + if (mrModel.GetPageCount()%2==0) + return; + PageEnumeration aAllPages (PageEnumerationProvider::CreateAllPagesEnumeration(mrModel)); + while (aAllPages.HasMoreElements()) + { + SharedPageDescriptor pDescriptor (aAllPages.GetNextElement()); + if (pDescriptor->UpdateMasterPage()) + { + mrView.GetPreviewCache()->InvalidatePreviewBitmap ( + pDescriptor->GetPage()); + } + } +} + +void SlideSorterController::CheckForSlideTransitionAssignment() +{ + if (mrModel.GetPageCount()%2==0) + return; + PageEnumeration aAllPages (PageEnumerationProvider::CreateAllPagesEnumeration(mrModel)); + while (aAllPages.HasMoreElements()) + { + SharedPageDescriptor pDescriptor (aAllPages.GetNextElement()); + if (pDescriptor->UpdateTransitionFlag()) + { + mrView.GetPreviewCache()->InvalidatePreviewBitmap ( + pDescriptor->GetPage()); + } + } +} + +//===== SlideSorterController::ModelChangeLock ================================ + +SlideSorterController::ModelChangeLock::ModelChangeLock ( + SlideSorterController& rController) + : mpController(&rController) +{ + mpController->LockModelChange(); +} + +SlideSorterController::ModelChangeLock::~ModelChangeLock() COVERITY_NOEXCEPT_FALSE +{ + Release(); +} + +void SlideSorterController::ModelChangeLock::Release() +{ + if (mpController != nullptr) + { + mpController->UnlockModelChange(); + mpController = nullptr; + } +} + +} // end of namespace ::sd::slidesorter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsAnimationFunction.cxx b/sd/source/ui/slidesorter/controller/SlsAnimationFunction.cxx new file mode 100644 index 000000000..31978baf7 --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsAnimationFunction.cxx @@ -0,0 +1,129 @@ +/* -*- 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 <sal/config.h> + +#include <o3tl/safeint.hxx> + +#include <controller/SlsAnimationFunction.hxx> + +namespace sd::slidesorter::controller { + +//===== AnimationBezierFunction =============================================== + +AnimationBezierFunction::AnimationBezierFunction ( + const double nX1, + const double nY1) + : mnX1(nX1), + mnY1(nY1), + mnX2(1-nY1), + mnY2(1-nX1) +{ +} + +::basegfx::B2DPoint AnimationBezierFunction::operator() (const double nT) +{ + return ::basegfx::B2DPoint( + EvaluateComponent(nT, mnX1, mnX2), + EvaluateComponent(nT, mnY1, mnY2)); +} + +double AnimationBezierFunction::EvaluateComponent ( + const double nT, + const double nV1, + const double nV2) +{ + const double nS (1-nT); + + // While the control point values 1 and 2 are explicitly given the start + // and end values are implicitly given. + const double nV0 (0); + const double nV3 (1); + + const double nV01 (nS*nV0 + nT*nV1); + const double nV12 (nS*nV1 + nT*nV2); + const double nV23 (nS*nV2 + nT*nV3); + + const double nV012 (nS*nV01 + nT*nV12); + const double nV123 (nS*nV12 + nT*nV23); + + const double nV0123 (nS*nV012 + nT*nV123); + + return nV0123; +} + +//===== AnimationParametricFunction =========================================== + +AnimationParametricFunction::AnimationParametricFunction (const ParametricFunction& rFunction) +{ + const sal_Int32 nSampleCount (64); + + // Sample the given parametric function. + ::std::vector<basegfx::B2DPoint> aPoints; + aPoints.reserve(nSampleCount); + for (sal_Int32 nIndex=0; nIndex<nSampleCount; ++nIndex) + { + const double nT (nIndex/double(nSampleCount-1)); + aPoints.emplace_back(rFunction(nT)); + } + + // Interpolate at evenly spaced points. + maY.clear(); + maY.reserve(nSampleCount); + double nX0 (aPoints[0].getX()); + double nY0 (aPoints[0].getY()); + double nX1 (aPoints[1].getX()); + double nY1 (aPoints[1].getY()); + sal_Int32 nIndex (1); + for (sal_Int32 nIndex2=0; nIndex2<nSampleCount; ++nIndex2) + { + const double nX (nIndex2 / double(nSampleCount-1)); + while (nX > nX1 && nIndex<nSampleCount) + { + nX0 = nX1; + nY0 = nY1; + nX1 = aPoints[nIndex].getX(); + nY1 = aPoints[nIndex].getY(); + ++nIndex; + } + const double nU ((nX-nX1) / (nX0 - nX1)); + const double nY (nY0*nU + nY1*(1-nU)); + maY.push_back(nY); + } +} + +double AnimationParametricFunction::operator() (const double nX) +{ + const sal_Int32 nIndex0 (static_cast<sal_Int32>(nX * maY.size())); + const double nX0 (nIndex0 / double(maY.size()-1)); + const sal_uInt32 nIndex1 (nIndex0 + 1); + const double nX1 (nIndex1 / double(maY.size()-1)); + + if (nIndex0<=0) + return maY[0]; + else if (o3tl::make_unsigned(nIndex0)>=maY.size() || nIndex1>=maY.size()) + return maY[maY.size()-1]; + + const double nU ((nX-nX1) / (nX0 - nX1)); + return maY[nIndex0]*nU + maY[nIndex1]*(1-nU); +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsAnimator.cxx b/sd/source/ui/slidesorter/controller/SlsAnimator.cxx new file mode 100644 index 000000000..b400ec4dc --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsAnimator.cxx @@ -0,0 +1,280 @@ +/* -*- 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 <controller/SlsAnimator.hxx> +#include <view/SlideSorterView.hxx> +#include <osl/diagnose.h> + +namespace sd::slidesorter::controller { + +/** Handle one animation function by using a timer for frequent calls to + the animations operator(). +*/ +class Animator::Animation +{ +public: + Animation ( + const Animator::AnimationFunctor& rAnimation, + const double nStartOffset, + const double nDuration, + const double nGlobalTime, + const Animator::AnimationId nAnimationId, + const Animator::FinishFunctor& rFinishFunctor); + /** Run next animation step. If animation has reached its end it is + expired. + */ + bool Run (const double nGlobalTime); + + /** Typically called when an animation has finished, but also from + Animator::Disposed(). The finish functor is called and the + animation is marked as expired to prevent another run. + */ + void Expire(); + bool IsExpired() const { return mbIsExpired;} + + Animator::AnimationFunctor maAnimation; + Animator::FinishFunctor maFinishFunctor; + const Animator::AnimationId mnAnimationId; + const double mnDuration; + const double mnEnd; + const double mnGlobalTimeAtStart; + bool mbIsExpired; +}; + +Animator::Animator (SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter), + maIdle("sd slidesorter controller Animator"), + mbIsDisposed(false), + mnNextAnimationId(0) +{ + maIdle.SetPriority(TaskPriority::REPAINT); + maIdle.SetInvokeHandler(LINK(this,Animator,TimeoutHandler)); +} + +Animator::~Animator() +{ + if ( ! mbIsDisposed) + { + OSL_ASSERT(mbIsDisposed); + Dispose(); + } +} + +void Animator::Dispose() +{ + mbIsDisposed = true; + + AnimationList aCopy (maAnimations); + for (const auto& rxAnimation : aCopy) + rxAnimation->Expire(); + + maIdle.Stop(); + if (mpDrawLock) + { + mpDrawLock->Dispose(); + mpDrawLock.reset(); + } +} + +Animator::AnimationId Animator::AddAnimation ( + const AnimationFunctor& rAnimation, + const FinishFunctor& rFinishFunctor) +{ + // When the animator is already disposed then ignore this call + // silently (well, we show an assertion, but do not throw an exception.) + OSL_ASSERT( ! mbIsDisposed); + if (mbIsDisposed) + return -1; + + std::shared_ptr<Animation> pAnimation = + std::make_shared<Animation>( + rAnimation, + 0, + 300 / 1000.0, + maElapsedTime.getElapsedTime(), + ++mnNextAnimationId, + rFinishFunctor); + maAnimations.push_back(pAnimation); + + RequestNextFrame(); + + return pAnimation->mnAnimationId; +} + +void Animator::RemoveAnimation (const Animator::AnimationId nId) +{ + OSL_ASSERT( ! mbIsDisposed); + + const AnimationList::iterator iAnimation (::std::find_if( + maAnimations.begin(), + maAnimations.end(), + [nId] (std::shared_ptr<Animation> const& pAnim) + { return nId == pAnim->mnAnimationId; })); + if (iAnimation != maAnimations.end()) + { + OSL_ASSERT((*iAnimation)->mnAnimationId == nId); + (*iAnimation)->Expire(); + maAnimations.erase(iAnimation); + } + + if (maAnimations.empty()) + { + // Reset the animation id when we can. + mnNextAnimationId = 0; + + // No more animations => we do not have to suppress painting + // anymore. + mpDrawLock.reset(); + } +} + +void Animator::RemoveAllAnimations() +{ + for (auto const& it : maAnimations) + { + it->Expire(); + } + maAnimations.clear(); + mnNextAnimationId = 0; + + // No more animations => we do not have to suppress painting + // anymore. + mpDrawLock.reset(); +} + +bool Animator::ProcessAnimations (const double nTime) +{ + bool bExpired (false); + + OSL_ASSERT( ! mbIsDisposed); + if (mbIsDisposed) + return bExpired; + + AnimationList aCopy (maAnimations); + for (const auto& rxAnimation : aCopy) + { + bExpired |= rxAnimation->Run(nTime); + } + + return bExpired; +} + +void Animator::CleanUpAnimationList() +{ + OSL_ASSERT( ! mbIsDisposed); + if (mbIsDisposed) + return; + + AnimationList aActiveAnimations; + + for (const auto& rxAnimation : maAnimations) + { + if ( ! rxAnimation->IsExpired()) + aActiveAnimations.push_back(rxAnimation); + } + + maAnimations.swap(aActiveAnimations); +} + +void Animator::RequestNextFrame () +{ + if ( ! maIdle.IsActive()) + { + // Prevent redraws except for the ones in TimeoutHandler. While the + // Animator is active it will schedule repaints regularly. Repaints + // in between would only lead to visual artifacts. + mpDrawLock.reset(new view::SlideSorterView::DrawLock(mrSlideSorter)); + maIdle.Start(); + } +} + +IMPL_LINK_NOARG(Animator, TimeoutHandler, Timer *, void) +{ + if (mbIsDisposed) + return; + + if (ProcessAnimations(maElapsedTime.getElapsedTime())) + CleanUpAnimationList(); + + // Unlock the draw lock. This should lead to a repaint. + mpDrawLock.reset(); + + if (!maAnimations.empty()) + RequestNextFrame(); +} + +//===== Animator::Animation =================================================== + +Animator::Animation::Animation ( + const Animator::AnimationFunctor& rAnimation, + const double nStartOffset, + const double nDuration, + const double nGlobalTime, + const Animator::AnimationId nId, + const Animator::FinishFunctor& rFinishFunctor) + : maAnimation(rAnimation), + maFinishFunctor(rFinishFunctor), + mnAnimationId(nId), + mnDuration(nDuration), + mnEnd(nGlobalTime + nDuration + nStartOffset), + mnGlobalTimeAtStart(nGlobalTime + nStartOffset), + mbIsExpired(false) +{ + Run(nGlobalTime); +} + +bool Animator::Animation::Run (const double nGlobalTime) +{ + if ( ! mbIsExpired) + { + if (mnDuration > 0) + { + if (nGlobalTime >= mnEnd) + { + maAnimation(1.0); + Expire(); + } + else if (nGlobalTime >= mnGlobalTimeAtStart) + { + maAnimation((nGlobalTime - mnGlobalTimeAtStart) / mnDuration); + } + } + else if (mnDuration < 0) + { + // Animations without end have to be expired by their owner. + maAnimation(nGlobalTime); + } + } + + return mbIsExpired; +} + +void Animator::Animation::Expire() +{ + if ( ! mbIsExpired) + { + mbIsExpired = true; + if (maFinishFunctor) + maFinishFunctor(); + } +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsClipboard.cxx b/sd/source/ui/slidesorter/controller/SlsClipboard.cxx new file mode 100644 index 000000000..160077e64 --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsClipboard.cxx @@ -0,0 +1,918 @@ +/* -*- 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 <sal/config.h> + +#include <cassert> + +#include <controller/SlsClipboard.hxx> + +#include <SlideSorterViewShell.hxx> +#include <SlideSorter.hxx> +#include <model/SlideSorterModel.hxx> +#include <model/SlsPageDescriptor.hxx> +#include <model/SlsPageEnumerationProvider.hxx> +#include <view/SlideSorterView.hxx> +#include <controller/SlideSorterController.hxx> +#include <controller/SlsInsertionIndicatorHandler.hxx> +#include <controller/SlsPageSelector.hxx> +#include <controller/SlsSelectionFunction.hxx> +#include <controller/SlsCurrentSlideManager.hxx> +#include <controller/SlsFocusManager.hxx> +#include <controller/SlsSelectionManager.hxx> +#include <controller/SlsTransferableData.hxx> +#include <controller/SlsSelectionObserver.hxx> +#include <controller/SlsVisibleAreaManager.hxx> +#include <cache/SlsPageCache.hxx> + +#include <ViewShellBase.hxx> +#include <DrawViewShell.hxx> +#include <Window.hxx> +#include <fupoor.hxx> +#include <strings.hrc> +#include <sdresid.hxx> +#include <sdxfer.hxx> +#include <sdmod.hxx> +#include <ins_paste.hxx> +#include <drawdoc.hxx> +#include <DrawDocShell.hxx> +#include <sdpage.hxx> +#include <sdtreelb.hxx> + +#include <com/sun/star/datatransfer/dnd/DNDConstants.hpp> +#include <sfx2/request.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/docfile.hxx> +#include <svx/svxids.hrc> +#include <tools/urlobj.hxx> +#include <rtl/ustring.hxx> +#include <vcl/svapp.hxx> + +namespace sd::slidesorter::controller { + +namespace { +/** Temporarily deactivate slide tracking of the VisibleAreaManager. + This is used as a workaround to avoid unwanted repositioning of + the visible area when the selection of slides is copied to the + clipboard (cloning of slides leads to model change notifications + for the original model.) +*/ +class TemporarySlideTrackingDeactivator +{ +public: + explicit TemporarySlideTrackingDeactivator (SlideSorterController& rController) + : mrController(rController), + mbIsCurrentSlideTrackingActive ( + mrController.GetVisibleAreaManager().IsCurrentSlideTrackingActive()) + { + if (mbIsCurrentSlideTrackingActive) + mrController.GetVisibleAreaManager().DeactivateCurrentSlideTracking(); + } + ~TemporarySlideTrackingDeactivator() + { + if (mbIsCurrentSlideTrackingActive) + mrController.GetVisibleAreaManager().ActivateCurrentSlideTracking(); + } + +private: + SlideSorterController& mrController; + const bool mbIsCurrentSlideTrackingActive; +}; +} // end of anonymous namespace + +class Clipboard::UndoContext +{ +public: + UndoContext ( + SdDrawDocument* pDocument, + const std::shared_ptr<ViewShell>& rpMainViewShell) + : mpDocument(pDocument), + mpMainViewShell(rpMainViewShell) + { + if (mpDocument!=nullptr && mpDocument->IsUndoEnabled()) + { + if (mpMainViewShell && mpMainViewShell->GetShellType() == ViewShell::ST_DRAW) + mpDocument->BegUndo(SdResId(STRING_DRAG_AND_DROP_PAGES)); + else + mpDocument->BegUndo(SdResId(STRING_DRAG_AND_DROP_SLIDES)); + } + } + + ~UndoContext() + { + if (mpDocument!=nullptr && mpDocument->IsUndoEnabled()) + mpDocument->EndUndo(); + if (mpMainViewShell && mpMainViewShell->GetViewFrame()!=nullptr) + { + SfxBindings& rBindings = mpMainViewShell->GetViewFrame()->GetBindings(); + rBindings.Invalidate(SID_UNDO); + rBindings.Invalidate(SID_REDO); + } + } +private: + SdDrawDocument* mpDocument; + std::shared_ptr<ViewShell> mpMainViewShell; +}; + +Clipboard::Clipboard (SlideSorter& rSlideSorter) + : ViewClipboard(rSlideSorter.GetView()), + mrSlideSorter(rSlideSorter), + mrController(mrSlideSorter.GetController()), + mnDragFinishedUserEventId(nullptr) +{ +} + +Clipboard::~Clipboard() +{ + if (mnDragFinishedUserEventId != nullptr) + Application::RemoveUserEvent(mnDragFinishedUserEventId); +} + +/** With the current implementation the forwarded calls to the current + function will come back eventually to call the local Do(Cut|Copy|Paste) + methods. A shortcut is possible but would be an unclean hack. +*/ +void Clipboard::HandleSlotCall (SfxRequest& rRequest) +{ + ViewShell* pViewShell = mrSlideSorter.GetViewShell(); + rtl::Reference<FuPoor> xFunc; + if (pViewShell != nullptr) + xFunc = pViewShell->GetCurrentFunction(); + switch (rRequest.GetSlot()) + { + case SID_CUT: + if (mrSlideSorter.GetModel().GetEditMode() != EditMode::MasterPage) + { + if(xFunc.is()) + xFunc->DoCut(); + else + DoCut(); + } + rRequest.Done(); + break; + + case SID_COPY: + if (mrSlideSorter.GetModel().GetEditMode() != EditMode::MasterPage) + { + if(xFunc.is()) + xFunc->DoCopy(); + else + DoCopy(); + } + rRequest.Done(); + break; + + case SID_PASTE: + // Prevent redraws while inserting pages from the clipboard + // because the intermediate inconsistent state might lead to + // a crash. + if (mrSlideSorter.GetModel().GetEditMode() != EditMode::MasterPage) + { + view::SlideSorterView::DrawLock aLock (mrSlideSorter); + SelectionObserver::Context aContext (mrSlideSorter); + if(xFunc.is()) + xFunc->DoPaste(); + else + DoPaste(); + } + rRequest.Done(); + break; + + case SID_DELETE: + DoDelete(); + rRequest.Done(); + break; + } +} + +void Clipboard::DoCut () +{ + if (mrSlideSorter.GetModel().GetPageCount() > 1) + { + DoCopy(); + DoDelete(); + } +} + +void Clipboard::DoDelete() +{ + if (mrSlideSorter.GetModel().GetPageCount() > 1) + { + mrController.GetSelectionManager()->DeleteSelectedPages(); + } +} + +void Clipboard::DoCopy () +{ + CreateSlideTransferable( nullptr, false ); +} + +void Clipboard::DoPaste () +{ + SdTransferable* pClipTransferable = SD_MOD()->pTransferClip; + + if (pClipTransferable==nullptr || !pClipTransferable->IsPageTransferable()) + return; + + sal_Int32 nInsertPosition = GetInsertionPosition(); + + if (nInsertPosition >= 0) + { + // Paste the pages from the clipboard. + sal_Int32 nInsertPageCount = PasteTransferable(nInsertPosition); + // Select the pasted pages and make the first of them the + // current page. + mrSlideSorter.GetContentWindow()->GrabFocus(); + SelectPageRange(nInsertPosition, nInsertPageCount); + } +} + +sal_Int32 Clipboard::GetInsertionPosition () +{ + sal_Int32 nInsertPosition = -1; + + // Determine the insertion position: + // a) When the insertion indicator is visible, then at that position. + // b) When the focus indicator is visible, then before or after the + // focused page, depending on user input to a dialog. + // c) When there is a selection but no focus, then after the + // selection. + // d) After the last page when there is no selection and no focus. + + std::shared_ptr<controller::InsertionIndicatorHandler> pInsertionIndicatorHandler ( + mrController.GetInsertionIndicatorHandler()); + if (pInsertionIndicatorHandler->IsActive()) + { + // Use the insertion index of an active insertion indicator. + nInsertPosition = pInsertionIndicatorHandler->GetInsertionPageIndex(); + } + else if (mrController.GetSelectionManager()->GetInsertionPosition() >= 0) + { + // Use the insertion index of an insertion indicator that has been + // deactivated a short while ago. + nInsertPosition = mrController.GetSelectionManager()->GetInsertionPosition(); + } + else if (mrController.GetFocusManager().IsFocusShowing()) + { + // Use the focus to determine the insertion position. + vcl::Window* pWin = mrSlideSorter.GetContentWindow(); + SdInsertPasteDlg aDialog(pWin ? pWin->GetFrameWeld() : nullptr); + if (aDialog.run() == RET_OK) + { + nInsertPosition = mrController.GetFocusManager().GetFocusedPageIndex(); + if (!aDialog.IsInsertBefore()) + nInsertPosition ++; + } + } + + return nInsertPosition; +} + +sal_Int32 Clipboard::PasteTransferable (sal_Int32 nInsertPosition) +{ + SdTransferable* pClipTransferable = SD_MOD()->pTransferClip; + model::SlideSorterModel& rModel (mrSlideSorter.GetModel()); + bool bMergeMasterPages = !pClipTransferable->HasSourceDoc (rModel.GetDocument()); + sal_uInt16 nInsertIndex (rModel.GetCoreIndex(nInsertPosition)); + sal_Int32 nInsertPageCount (0); + if (pClipTransferable->HasPageBookmarks()) + { + const std::vector<OUString> &rBookmarkList = pClipTransferable->GetPageBookmarks(); + const SolarMutexGuard aGuard; + + nInsertPageCount = static_cast<sal_uInt16>(rBookmarkList.size()); + rModel.GetDocument()->InsertBookmarkAsPage( + rBookmarkList, + nullptr, + false, + false, + nInsertIndex, + false, + pClipTransferable->GetPageDocShell(), + true, + bMergeMasterPages, + false); + } + else + { + SfxObjectShell* pShell = pClipTransferable->GetDocShell().get(); + DrawDocShell* pDataDocSh = static_cast<DrawDocShell*>(pShell); + SdDrawDocument* pDataDoc = pDataDocSh->GetDoc(); + + if (pDataDoc!=nullptr + && pDataDoc->GetSdPageCount(PageKind::Standard)) + { + const SolarMutexGuard aGuard; + + bMergeMasterPages = (pDataDoc != rModel.GetDocument()); + nInsertPageCount = pDataDoc->GetSdPageCount( PageKind::Standard ); + rModel.GetDocument()->InsertBookmarkAsPage( + std::vector<OUString>(), + nullptr, + false, + false, + nInsertIndex, + false, + pDataDocSh, + true, + bMergeMasterPages, + false); + } + } + mrController.HandleModelChange(); + return nInsertPageCount; +} + +void Clipboard::SelectPageRange (sal_Int32 nFirstIndex, sal_Int32 nPageCount) +{ + // Select the newly inserted pages. That are the nInsertPageCount pages + // after the nInsertIndex position. + PageSelector& rSelector (mrController.GetPageSelector()); + rSelector.DeselectAllPages(); + for (sal_Int32 i=0; i<nPageCount; i++) + { + model::SharedPageDescriptor pDescriptor ( + mrSlideSorter.GetModel().GetPageDescriptor(nFirstIndex + i)); + if (pDescriptor) + { + rSelector.SelectPage(pDescriptor); + // The first page of the new selection is made the current page. + if (i == 0) + { + mrController.GetCurrentSlideManager()->SwitchCurrentSlide(pDescriptor); + } + } + } +} + +void Clipboard::CreateSlideTransferable ( + vcl::Window* pWindow, + bool bDrag) +{ + std::vector<OUString> aBookmarkList; + + // Insert all selected pages into a bookmark list and remember them in + // maPagesToRemove for possible later removal. + model::PageEnumeration aSelectedPages + (model::PageEnumerationProvider::CreateSelectedPagesEnumeration( + mrSlideSorter.GetModel())); + SdDrawDocument* const pDocument = mrSlideSorter.GetModel().GetDocument(); + DrawDocShell* const pDataDocSh = pDocument->GetDocSh(); + + sal_Int32 nUniqueID = 0; + while (aSelectedPages.HasMoreElements()) + { + model::SharedPageDescriptor pDescriptor (aSelectedPages.GetNextElement()); + + //ensure that the slides have unique names + const OUString sOrigName = pDescriptor->GetPage()->GetName(); + if ( pDataDocSh && !pDataDocSh->IsPageNameUnique( sOrigName ) ) + { + OUString sUniqueName; + bool bUnique = false; + while ( !bUnique ) + { + sUniqueName = sOrigName + "_clipboard" + OUString::number(nUniqueID++); + bUnique = pDataDocSh->IsNewPageNameValid( sUniqueName ); + if ( bUnique ) + pDescriptor->GetPage()->SetName(sUniqueName); + } + } + + aBookmarkList.push_back(pDescriptor->GetPage()->GetName()); + maPagesToRemove.push_back (pDescriptor->GetPage()); + } + + // Create a small set of representatives of the selection for which + // previews are included into the transferable so that an insertion + // indicator can be rendered. + aSelectedPages.Rewind(); + ::std::vector<TransferableData::Representative> aRepresentatives; + aRepresentatives.reserve(3); + std::shared_ptr<cache::PageCache> pPreviewCache ( + mrSlideSorter.GetView().GetPreviewCache()); + while (aSelectedPages.HasMoreElements()) + { + model::SharedPageDescriptor pDescriptor (aSelectedPages.GetNextElement()); + if ( ! pDescriptor || pDescriptor->GetPage()==nullptr) + continue; + BitmapEx aPreview (pPreviewCache->GetPreviewBitmap(pDescriptor->GetPage(), false)); + aRepresentatives.emplace_back( + aPreview, + pDescriptor->HasState(model::PageDescriptor::ST_Excluded)); + if (aRepresentatives.size() >= 3) + break; + } + + if (aBookmarkList.empty()) + return; + + mrSlideSorter.GetView().BrkAction(); + rtl::Reference<SdTransferable> pTransferable = TransferableData::CreateTransferable ( + pDocument, + dynamic_cast<SlideSorterViewShell*>(mrSlideSorter.GetViewShell()), + std::move(aRepresentatives)); + + if (bDrag) + SD_MOD()->pTransferDrag = pTransferable.get(); + else + SD_MOD()->pTransferClip = pTransferable.get(); + + pDocument->CreatingDataObj (pTransferable.get()); + pTransferable->SetWorkDocument(pDocument->AllocSdDrawDocument()); + std::unique_ptr<TransferableObjectDescriptor> pObjDesc(new TransferableObjectDescriptor); + pTransferable->GetWorkDocument()->GetDocSh() + ->FillTransferableObjectDescriptor (*pObjDesc); + + if (pDataDocSh != nullptr) + pObjDesc->maDisplayName = pDataDocSh->GetMedium()->GetURLObject().GetURLNoPass(); + + vcl::Window* pActionWindow = pWindow; + if (pActionWindow == nullptr) + { + ViewShell* pViewShell = mrSlideSorter.GetViewShell(); + if (pViewShell != nullptr) + pActionWindow = pViewShell->GetActiveWindow(); + } + + assert(pActionWindow); + + pTransferable->SetStartPos (pActionWindow->PixelToLogic( + pActionWindow->GetPointerPosPixel())); + pTransferable->SetObjectDescriptor (std::move(pObjDesc)); + + { + TemporarySlideTrackingDeactivator aDeactivator (mrController); + pTransferable->SetPageBookmarks (std::move(aBookmarkList), !bDrag); + } + + if (bDrag) + { + pTransferable->SetView (&mrSlideSorter.GetView()); + pTransferable->StartDrag (pActionWindow, DND_ACTION_COPY | DND_ACTION_MOVE); + } + else + pTransferable->CopyToClipboard (pActionWindow); + + pDocument->CreatingDataObj(nullptr); +} + +std::shared_ptr<SdTransferable::UserData> Clipboard::CreateTransferableUserData (SdTransferable* pTransferable) +{ + do + { + SdPageObjsTLV::SdPageObjsTransferable* pTreeListBoxTransferable + = dynamic_cast<SdPageObjsTLV::SdPageObjsTransferable*>(pTransferable); + if (pTreeListBoxTransferable == nullptr) + break; + + // Find view shell for the document of the transferable. + ::sd::ViewShell* pViewShell + = SdPageObjsTLV::GetViewShellForDocShell(pTreeListBoxTransferable->GetDocShell()); + if (pViewShell == nullptr) + break; + + // Find slide sorter for the document of the transferable. + SlideSorterViewShell* pSlideSorterViewShell + = SlideSorterViewShell::GetSlideSorter(pViewShell->GetViewShellBase()); + if (pSlideSorterViewShell == nullptr) + break; + SlideSorter& rSlideSorter (pSlideSorterViewShell->GetSlideSorter()); + + // Get bookmark from transferable. + TransferableDataHelper aDataHelper (pTransferable); + INetBookmark aINetBookmark; + if ( ! aDataHelper.GetINetBookmark(SotClipboardFormatId::NETSCAPE_BOOKMARK, aINetBookmark)) + break; + const OUString sURL (aINetBookmark.GetURL()); + const sal_Int32 nIndex (sURL.indexOf('#')); + if (nIndex == -1) + break; + OUString sBookmark (sURL.copy(nIndex+1)); + + // Make sure that the bookmark points to a page. + SdDrawDocument* pTransferableDocument = rSlideSorter.GetModel().GetDocument(); + if (pTransferableDocument == nullptr) + break; + bool bIsMasterPage = false; + const sal_uInt16 nPageIndex (pTransferableDocument->GetPageByName(sBookmark, bIsMasterPage)); + if (nPageIndex == SDRPAGE_NOTFOUND) + break; + + // Create preview. + ::std::vector<TransferableData::Representative> aRepresentatives; + aRepresentatives.reserve(1); + std::shared_ptr<cache::PageCache> pPreviewCache ( + rSlideSorter.GetView().GetPreviewCache()); + model::SharedPageDescriptor pDescriptor (rSlideSorter.GetModel().GetPageDescriptor((nPageIndex-1)/2)); + if ( ! pDescriptor || pDescriptor->GetPage()==nullptr) + break; + BitmapEx aPreview (pPreviewCache->GetPreviewBitmap(pDescriptor->GetPage(), false)); + aRepresentatives.emplace_back( + aPreview, + pDescriptor->HasState(model::PageDescriptor::ST_Excluded)); + + // Remember the page in maPagesToRemove so that it can be removed + // when drag and drop action is "move". + Clipboard& rOtherClipboard (pSlideSorterViewShell->GetSlideSorter().GetController().GetClipboard()); + rOtherClipboard.maPagesToRemove.clear(); + rOtherClipboard.maPagesToRemove.push_back(pDescriptor->GetPage()); + + // Create the new transferable. + std::shared_ptr<SdTransferable::UserData> pNewTransferable = + std::make_shared<TransferableData>( + pSlideSorterViewShell, + std::move(aRepresentatives)); + pTransferable->SetWorkDocument(pTreeListBoxTransferable->GetSourceDoc()->AllocSdDrawDocument()); + // pTransferable->SetView(&mrSlideSorter.GetView()); + + // Set page bookmark list. + std::vector<OUString> aPageBookmarks { sBookmark }; + pTransferable->SetPageBookmarks(std::move(aPageBookmarks), false); + + // Replace the view referenced by the transferable with the + // corresponding slide sorter view. + pTransferable->SetView(&pSlideSorterViewShell->GetSlideSorter().GetView()); + + return pNewTransferable; + } + while (false); + + return std::shared_ptr<SdTransferable::UserData>(); +} + +void Clipboard::StartDrag ( + const Point& rPosition, + vcl::Window* pWindow) +{ + maPagesToRemove.clear(); + CreateSlideTransferable(pWindow, true); + + mrController.GetInsertionIndicatorHandler()->UpdatePosition( + rPosition, + InsertionIndicatorHandler::UnknownMode); +} + +void Clipboard::DragFinished (sal_Int8 nDropAction) +{ + if (mnDragFinishedUserEventId == nullptr) + { + mnDragFinishedUserEventId = Application::PostUserEvent( + LINK(this, Clipboard, ProcessDragFinished), + reinterpret_cast<void*>(nDropAction)); + } +} + +IMPL_LINK(Clipboard, ProcessDragFinished, void*, pUserData, void) +{ + const sal_Int8 nDropAction (static_cast<sal_Int8>(reinterpret_cast<sal_IntPtr>(pUserData))); + + mnDragFinishedUserEventId = nullptr; + + // Hide the substitution display and insertion indicator. + ::rtl::Reference<SelectionFunction> pFunction (mrController.GetCurrentSelectionFunction()); + if (pFunction.is()) + pFunction->NotifyDragFinished(); + + PageSelector& rSelector (mrController.GetPageSelector()); + if ((nDropAction & DND_ACTION_MOVE) != 0 + && ! maPagesToRemove.empty()) + { + // Remove the pages that have been moved to another place (possibly + // in the same document.) + rSelector.DeselectAllPages(); + for (const auto& rpDraggedPage : maPagesToRemove) + { + rSelector.SelectPage(rpDraggedPage); + } + mrController.GetSelectionManager()->DeleteSelectedPages(); + } + mxUndoContext.reset(); + mxSelectionObserverContext.reset(); +} + +sal_Int8 Clipboard::AcceptDrop ( + const AcceptDropEvent& rEvent, + DropTargetHelper& rTargetHelper, + ::sd::Window* pTargetWindow, + sal_uInt16 nPage, + SdrLayerID nLayer) +{ + sal_Int8 nAction (DND_ACTION_NONE); + + const Clipboard::DropType eDropType (IsDropAccepted()); + + switch (eDropType) + { + case DT_PAGE: + case DT_PAGE_FROM_NAVIGATOR: + { + // Accept a drop. + nAction = rEvent.mnAction; + + // Use the copy action when the drop action is the default, i.e. not + // explicitly set to move or link, and when the source and + // target models are not the same. + SdTransferable* pDragTransferable = SD_MOD()->pTransferDrag; + if (pDragTransferable != nullptr + && pDragTransferable->IsPageTransferable() + && ((rEvent.maDragEvent.DropAction + & css::datatransfer::dnd::DNDConstants::ACTION_DEFAULT) != 0) + && (mrSlideSorter.GetModel().GetDocument()->GetDocSh() + != pDragTransferable->GetPageDocShell())) + { + nAction = DND_ACTION_COPY; + } + else if (IsInsertionTrivial(pDragTransferable, nAction)) + { + nAction = DND_ACTION_NONE; + } + + // Show the insertion marker and the substitution for a drop. + SelectionFunction* pSelectionFunction = dynamic_cast<SelectionFunction*>( + mrSlideSorter.GetViewShell()->GetCurrentFunction().get()); + if (pSelectionFunction != nullptr) + pSelectionFunction->MouseDragged(rEvent, nAction); + + // Scroll the window when the mouse reaches the window border. + // mrController.GetScrollBarManager().AutoScroll (rEvent.maPosPixel); + } + break; + + case DT_SHAPE: + nAction = ExecuteOrAcceptShapeDrop( + DC_ACCEPT, + rEvent.maPosPixel, + &rEvent, + rTargetHelper, + pTargetWindow, + nPage, + nLayer); + break; + + default: + case DT_NONE: + nAction = DND_ACTION_NONE; + break; + } + + return nAction; +} + +sal_Int8 Clipboard::ExecuteDrop ( + const ExecuteDropEvent& rEvent, + DropTargetHelper& rTargetHelper, + ::sd::Window* pTargetWindow, + sal_uInt16 nPage, + SdrLayerID nLayer) +{ + sal_Int8 nResult = DND_ACTION_NONE; + mxUndoContext.reset(); + const Clipboard::DropType eDropType (IsDropAccepted()); + + switch (eDropType) + { + case DT_PAGE: + case DT_PAGE_FROM_NAVIGATOR: + { + SdTransferable* pDragTransferable = SD_MOD()->pTransferDrag; + const Point aEventModelPosition ( + pTargetWindow->PixelToLogic (rEvent.maPosPixel)); + const sal_Int32 nXOffset (std::abs (pDragTransferable->GetStartPos().X() + - aEventModelPosition.X())); + const sal_Int32 nYOffset (std::abs (pDragTransferable->GetStartPos().Y() + - aEventModelPosition.Y())); + bool bContinue = + ( pDragTransferable->GetView() != &mrSlideSorter.GetView() ) + || ( nXOffset >= 2 && nYOffset >= 2 ); + + std::shared_ptr<InsertionIndicatorHandler> pInsertionIndicatorHandler( + mrController.GetInsertionIndicatorHandler()); + // Get insertion position and then turn off the insertion indicator. + pInsertionIndicatorHandler->UpdatePosition(aEventModelPosition, rEvent.mnAction); + // sal_uInt16 nIndex = DetermineInsertPosition(*pDragTransferable); + + // Do not process the insertion when it is trivial, + // i.e. would insert pages at their original place. + if (IsInsertionTrivial(pDragTransferable, rEvent.mnAction)) + bContinue = false; + + // Tell the insertion indicator handler to hide before the model + // is modified. Doing it later may result in page objects whose + // animation state is not properly reset because they are then + // in another run then before the model change. + pInsertionIndicatorHandler->End(Animator::AM_Immediate); + + if (bContinue) + { + SlideSorterController::ModelChangeLock aModelChangeLock (mrController); + + // Handle a general drop operation. + mxUndoContext.reset(new UndoContext ( + mrSlideSorter.GetModel().GetDocument(), + mrSlideSorter.GetViewShell()->GetViewShellBase().GetMainViewShell())); + mxSelectionObserverContext.reset(new SelectionObserver::Context(mrSlideSorter)); + + if (rEvent.mnAction == DND_ACTION_MOVE) + { + SdDrawDocument* pDoc = mrSlideSorter.GetModel().GetDocument(); + const bool bDoesMakePageObjectsNamesUnique = pDoc->DoesMakePageObjectsNamesUnique(); + pDoc->DoMakePageObjectsNamesUnique(false); + HandlePageDrop(*pDragTransferable); + pDoc->DoMakePageObjectsNamesUnique(bDoesMakePageObjectsNamesUnique); + } + else + HandlePageDrop(*pDragTransferable); + + nResult = rEvent.mnAction; + + // We leave the undo context alive for when moving or + // copying inside one view then the actions in + // NotifyDragFinished should be covered as well as + // well as the ones above. + } + + // When the pages originated in another slide sorter then + // only that is notified automatically about the drag + // operation being finished. Because the target slide sorter + // has be notified, too, add a callback for that. + std::shared_ptr<TransferableData> pSlideSorterTransferable ( + TransferableData::GetFromTransferable(pDragTransferable)); + assert(pSlideSorterTransferable); + if (pSlideSorterTransferable + && pSlideSorterTransferable->GetSourceViewShell() != mrSlideSorter.GetViewShell()) + { + DragFinished(nResult); + } + + // Notify the receiving selection function that drag-and-drop is + // finished and the substitution handler can be released. + ::rtl::Reference<SelectionFunction> pFunction ( + mrController.GetCurrentSelectionFunction()); + if (pFunction.is()) + pFunction->NotifyDragFinished(); + } + break; + + case DT_SHAPE: + nResult = ExecuteOrAcceptShapeDrop( + DC_EXECUTE, + rEvent.maPosPixel, + &rEvent, + rTargetHelper, + pTargetWindow, + nPage, + nLayer); + break; + + default: + case DT_NONE: + break; + } + + return nResult; +} + +bool Clipboard::IsInsertionTrivial ( + SdTransferable const * pTransferable, + const sal_Int8 nDndAction) const +{ + std::shared_ptr<TransferableData> pSlideSorterTransferable ( + TransferableData::GetFromTransferable(pTransferable)); + if (pSlideSorterTransferable + && pSlideSorterTransferable->GetSourceViewShell() != mrSlideSorter.GetViewShell()) + return false; + return mrController.GetInsertionIndicatorHandler()->IsInsertionTrivial(nDndAction); +} + +void Clipboard::Abort() +{ + if (mxSelectionObserverContext) + { + mxSelectionObserverContext->Abort(); + mxSelectionObserverContext.reset(); + } +} + +sal_uInt16 Clipboard::DetermineInsertPosition () +{ + // Tell the model to move the dragged pages behind the one with the + // index nInsertionIndex which first has to be transformed into an index + // understandable by the document. + const sal_Int32 nInsertionIndex ( + mrController.GetInsertionIndicatorHandler()->GetInsertionPageIndex()); + + // Convert to insertion index to that of an SdModel. + if (nInsertionIndex >= 0) + return mrSlideSorter.GetModel().GetCoreIndex(nInsertionIndex); + else + return 0; +} + +Clipboard::DropType Clipboard::IsDropAccepted() const +{ + const SdTransferable* pDragTransferable = SD_MOD()->pTransferDrag; + if (pDragTransferable == nullptr) + return DT_NONE; + + if (pDragTransferable->IsPageTransferable()) + { + if (mrSlideSorter.GetModel().GetEditMode() != EditMode::MasterPage) + return DT_PAGE; + else + return DT_NONE; + } + + const SdPageObjsTLV::SdPageObjsTransferable* pPageObjsTransferable + = dynamic_cast<const SdPageObjsTLV::SdPageObjsTransferable*>(pDragTransferable); + if (pPageObjsTransferable != nullptr) + return DT_PAGE_FROM_NAVIGATOR; + + return DT_SHAPE; +} + +sal_Int8 Clipboard::ExecuteOrAcceptShapeDrop ( + DropCommand eCommand, + const Point& rPosition, + const void* pDropEvent, + DropTargetHelper& rTargetHelper, + ::sd::Window* pTargetWindow, + sal_uInt16 nPage, + SdrLayerID nLayer) +{ + sal_Int8 nResult = 0; + + // The dropping of a shape is accepted or executed only when there is + // DrawViewShell available to which we can forward this call. This has + // technical reasons: The actual code to accept or execute a shape drop + // is implemented in the ViewShell class and uses the page view of the + // main edit view. This is not possible without a DrawViewShell. + std::shared_ptr<DrawViewShell> pDrawViewShell; + if (mrSlideSorter.GetViewShell() != nullptr) + pDrawViewShell = std::dynamic_pointer_cast<DrawViewShell>( + mrSlideSorter.GetViewShell()->GetViewShellBase().GetMainViewShell()); + if (pDrawViewShell != nullptr + && (pDrawViewShell->GetShellType() == ViewShell::ST_IMPRESS + || pDrawViewShell->GetShellType() == ViewShell::ST_DRAW)) + { + // The drop is only accepted or executed when it takes place over a + // page object. Therefore we replace a missing page number by the + // number of the page under the mouse. + if (nPage == SDRPAGE_NOTFOUND) + { + model::SharedPageDescriptor pDescriptor ( + mrSlideSorter.GetModel().GetPageDescriptor( + mrSlideSorter.GetView().GetPageIndexAtPoint(rPosition))); + if (pDescriptor) + nPage = pDescriptor->GetPageIndex(); + } + + // Now comes the code that is different for the Execute and Accept: + // We simply forward the call to the AcceptDrop() or ExecuteDrop() + // methods of the DrawViewShell in the center pane. + if (nPage != SDRPAGE_NOTFOUND) + switch (eCommand) + { + case DC_ACCEPT: + nResult = pDrawViewShell->AcceptDrop( + *static_cast<const AcceptDropEvent*>(pDropEvent), + rTargetHelper, + pTargetWindow, + nPage, + nLayer); + break; + + case DC_EXECUTE: + nResult = pDrawViewShell->ExecuteDrop( + *static_cast<const ExecuteDropEvent*>(pDropEvent), + rTargetHelper, + pTargetWindow, + nPage, + nLayer); + break; + } + } + + return nResult; +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsCurrentSlideManager.cxx b/sd/source/ui/slidesorter/controller/SlsCurrentSlideManager.cxx new file mode 100644 index 000000000..9203c06e8 --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsCurrentSlideManager.cxx @@ -0,0 +1,256 @@ +/* -*- 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 <SlideSorter.hxx> +#include <model/SlideSorterModel.hxx> +#include <model/SlsPageDescriptor.hxx> +#include <controller/SlsPageSelector.hxx> +#include <controller/SlideSorterController.hxx> +#include <controller/SlsCurrentSlideManager.hxx> +#include <controller/SlsFocusManager.hxx> +#include <view/SlideSorterView.hxx> +#include <ViewShellBase.hxx> +#include <ViewShell.hxx> +#include <DrawViewShell.hxx> +#include <sdpage.hxx> +#include <FrameView.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <osl/diagnose.h> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +using namespace ::sd::slidesorter::model; + +namespace sd::slidesorter::controller { + +CurrentSlideManager::CurrentSlideManager (SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter), + mnCurrentSlideIndex(-1), + maSwitchPageDelayTimer("sd CurrentSlideManager maSwitchPageDelayTimer") +{ + maSwitchPageDelayTimer.SetTimeout(100); + maSwitchPageDelayTimer.SetInvokeHandler(LINK(this,CurrentSlideManager,SwitchPageCallback)); +} + +CurrentSlideManager::~CurrentSlideManager() +{ +} + +void CurrentSlideManager::NotifyCurrentSlideChange (const SdPage* pPage) +{ + if (pPage != nullptr) + NotifyCurrentSlideChange( + mrSlideSorter.GetModel().GetIndex( + Reference<drawing::XDrawPage>( + const_cast<SdPage*>(pPage)->getUnoPage(), + UNO_QUERY))); + else + NotifyCurrentSlideChange(-1); +} + +void CurrentSlideManager::NotifyCurrentSlideChange (const sal_Int32 nSlideIndex) +{ + if (mnCurrentSlideIndex == nSlideIndex) + return; + + PageSelector::BroadcastLock aBroadcastLock (mrSlideSorter.GetController().GetPageSelector()); + + mrSlideSorter.GetController().GetPageSelector().DeselectAllPages(); + + ReleaseCurrentSlide(); + AcquireCurrentSlide(nSlideIndex); + + // Update the selection. + if (mpCurrentSlide) + { + mrSlideSorter.GetController().GetPageSelector().SelectPage(mpCurrentSlide); + mrSlideSorter.GetController().GetFocusManager().SetFocusedPage(mpCurrentSlide); + } +} + +void CurrentSlideManager::ReleaseCurrentSlide() +{ + if (mpCurrentSlide) + mrSlideSorter.GetView().SetState(mpCurrentSlide, PageDescriptor::ST_Current, false); + + mpCurrentSlide.reset(); + mnCurrentSlideIndex = -1; +} + +void CurrentSlideManager::AcquireCurrentSlide (const sal_Int32 nSlideIndex) +{ + mnCurrentSlideIndex = nSlideIndex; + + // if current slide valid + if (mnCurrentSlideIndex >= 0 && mnCurrentSlideIndex<mrSlideSorter.GetModel().GetPageCount()) + { + // Get a descriptor for the XDrawPage reference. Note that the + // given XDrawPage may or may not be member of the slide sorter + // document. + mpCurrentSlide = mrSlideSorter.GetModel().GetPageDescriptor(mnCurrentSlideIndex); + if (mpCurrentSlide) + mrSlideSorter.GetView().SetState(mpCurrentSlide, PageDescriptor::ST_Current, true); + } +} + +void CurrentSlideManager::SwitchCurrentSlide ( + const sal_Int32 nSlideIndex) +{ + SwitchCurrentSlide(mrSlideSorter.GetModel().GetPageDescriptor(nSlideIndex), true/*bUpdateSelection*/); +} + +void CurrentSlideManager::SwitchCurrentSlide ( + const SharedPageDescriptor& rpDescriptor, + const bool bUpdateSelection) +{ + if (!rpDescriptor || mpCurrentSlide==rpDescriptor) + return; + + ReleaseCurrentSlide(); + AcquireCurrentSlide((rpDescriptor->GetPage()->GetPageNum()-1)/2); + + ViewShell* pViewShell = mrSlideSorter.GetViewShell(); + if (pViewShell != nullptr && pViewShell->IsMainViewShell()) + { + // The slide sorter is the main view. + FrameView* pFrameView = pViewShell->GetFrameView(); + if (pFrameView != nullptr) + pFrameView->SetSelectedPage(sal::static_int_cast<sal_uInt16>(mnCurrentSlideIndex)); + mrSlideSorter.GetController().GetPageSelector().SetCoreSelection(); + } + + // We do not tell the XController/ViewShellBase about the new + // slide right away. This is done asynchronously after a short + // delay to allow for more slide switches in the slide sorter. + // This goes under the assumption that slide switching inside + // the slide sorter is fast (no expensive redraw of the new page + // (unless the preview of the new slide is not yet preset)) and + // that slide switching in the edit view is slow (all shapes of + // the new slide have to be repainted.) + maSwitchPageDelayTimer.Start(); + + // We have to store the (index of the) new current slide at + // the tab control because there are other asynchronous + // notifications of the slide switching that otherwise + // overwrite the correct value. + SetCurrentSlideAtTabControl(mpCurrentSlide); + + if (bUpdateSelection) + { + mrSlideSorter.GetController().GetPageSelector().DeselectAllPages(); + mrSlideSorter.GetController().GetPageSelector().SelectPage(rpDescriptor); + } + mrSlideSorter.GetController().GetFocusManager().SetFocusedPage(rpDescriptor); +} + +void CurrentSlideManager::SetCurrentSlideAtViewShellBase (const SharedPageDescriptor& rpDescriptor) +{ + OSL_ASSERT(rpDescriptor); + + ViewShellBase* pBase = mrSlideSorter.GetViewShellBase(); + if (pBase != nullptr) + { + DrawViewShell* pDrawViewShell = dynamic_cast<DrawViewShell*>( + pBase->GetMainViewShell().get()); + if (pDrawViewShell != nullptr) + { + sal_uInt16 nPageNumber = (rpDescriptor->GetPage()->GetPageNum()-1)/2; + pDrawViewShell->SwitchPage(nPageNumber); + TabControl& rPageTabControl = pDrawViewShell->GetPageTabControl(); + rPageTabControl.SetCurPageId(rPageTabControl.GetPageId(nPageNumber)); + } + } +} + +void CurrentSlideManager::SetCurrentSlideAtTabControl (const SharedPageDescriptor& rpDescriptor) +{ + OSL_ASSERT(rpDescriptor); + + ViewShellBase* pBase = mrSlideSorter.GetViewShellBase(); + if (pBase != nullptr) + { + std::shared_ptr<DrawViewShell> pDrawViewShell ( + std::dynamic_pointer_cast<DrawViewShell>(pBase->GetMainViewShell())); + if (pDrawViewShell) + { + sal_uInt16 nPageNumber = (rpDescriptor->GetPage()->GetPageNum()-1)/2; + TabControl& rPageTabControl = pDrawViewShell->GetPageTabControl(); + rPageTabControl.SetCurPageId(rPageTabControl.GetPageId(nPageNumber)); + } + } +} + +void CurrentSlideManager::SetCurrentSlideAtXController (const SharedPageDescriptor& rpDescriptor) +{ + OSL_ASSERT(rpDescriptor); + + try + { + Reference<beans::XPropertySet> xSet (mrSlideSorter.GetXController(), UNO_QUERY); + if (xSet.is()) + { + Any aPage; + aPage <<= rpDescriptor->GetPage()->getUnoPage(); + xSet->setPropertyValue( "CurrentPage", aPage ); + } + } + catch (const Exception&) + { + // We have not been able to set the current page at the main view. + // This is sad but still leaves us in a valid state. Therefore, + // this exception is silently ignored. + } +} + +void CurrentSlideManager::PrepareModelChange() +{ + mpCurrentSlide.reset(); +} + +void CurrentSlideManager::HandleModelChange() +{ + if (mnCurrentSlideIndex >= 0) + { + mpCurrentSlide = mrSlideSorter.GetModel().GetPageDescriptor(mnCurrentSlideIndex); + if (mpCurrentSlide) + mrSlideSorter.GetView().SetState(mpCurrentSlide, PageDescriptor::ST_Current, true); + } +} + +IMPL_LINK_NOARG(CurrentSlideManager, SwitchPageCallback, Timer *, void) +{ + if (mpCurrentSlide) + { + // Set current page. At the moment we have to do this in two + // different ways. The UNO way is the preferable one but, alas, + // it does not work always correctly (after some kinds of model + // changes). Therefore, we call DrawViewShell::SwitchPage(), + // too. + ViewShell* pViewShell = mrSlideSorter.GetViewShell(); + if (pViewShell==nullptr || ! pViewShell->IsMainViewShell()) + SetCurrentSlideAtViewShellBase(mpCurrentSlide); + SetCurrentSlideAtXController(mpCurrentSlide); + } +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsDragAndDropContext.cxx b/sd/source/ui/slidesorter/controller/SlsDragAndDropContext.cxx new file mode 100644 index 000000000..f447c5656 --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsDragAndDropContext.cxx @@ -0,0 +1,120 @@ +/* -*- 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 "SlsDragAndDropContext.hxx" + +#include <SlideSorter.hxx> +#include <model/SlideSorterModel.hxx> +#include <controller/SlideSorterController.hxx> +#include <controller/SlsInsertionIndicatorHandler.hxx> +#include <controller/SlsScrollBarManager.hxx> +#include <controller/SlsProperties.hxx> +#include <controller/SlsClipboard.hxx> +#include <controller/SlsTransferableData.hxx> +#include <Window.hxx> +#include <sdtreelb.hxx> +#include <sdmod.hxx> + +namespace sd::slidesorter::controller { + +DragAndDropContext::DragAndDropContext (SlideSorter& rSlideSorter) + : mpTargetSlideSorter(&rSlideSorter), + mnInsertionIndex(-1) +{ + // No Drag-and-Drop for master pages. + if (rSlideSorter.GetModel().GetEditMode() != EditMode::Page) + return; + + // For properly handling transferables created by the navigator we + // need additional information. For this a user data object is + // created that contains the necessary information. + SdTransferable* pTransferable = SD_MOD()->pTransferDrag; + SdPageObjsTLV::SdPageObjsTransferable* pTreeListBoxTransferable + = dynamic_cast<SdPageObjsTLV::SdPageObjsTransferable*>(pTransferable); + if (pTreeListBoxTransferable!=nullptr && !TransferableData::GetFromTransferable(pTransferable)) + { + pTransferable->AddUserData( + sd::slidesorter::controller::Clipboard::CreateTransferableUserData(pTransferable)); + } + + rSlideSorter.GetController().GetInsertionIndicatorHandler()->UpdateIndicatorIcon(pTransferable); +} + +DragAndDropContext::~DragAndDropContext() COVERITY_NOEXCEPT_FALSE +{ + SetTargetSlideSorter(); +} + +void DragAndDropContext::Dispose() +{ + mnInsertionIndex = -1; +} + +void DragAndDropContext::UpdatePosition ( + const Point& rMousePosition, + const InsertionIndicatorHandler::Mode eMode, + const bool bAllowAutoScroll) +{ + if (mpTargetSlideSorter == nullptr) + return; + + if (mpTargetSlideSorter->GetProperties()->IsUIReadOnly()) + return; + + // Convert window coordinates into model coordinates (we need the + // window coordinates for auto-scrolling because that remains + // constant while scrolling.) + sd::Window *pWindow = mpTargetSlideSorter->GetContentWindow().get(); + const Point aMouseModelPosition (pWindow->PixelToLogic(rMousePosition)); + std::shared_ptr<InsertionIndicatorHandler> pInsertionIndicatorHandler ( + mpTargetSlideSorter->GetController().GetInsertionIndicatorHandler()); + + bool bDoAutoScroll = bAllowAutoScroll + && mpTargetSlideSorter->GetController().GetScrollBarManager().AutoScroll( + rMousePosition, + [this, eMode, rMousePosition] () { + return this->UpdatePosition(rMousePosition, eMode, false); + }); + + if (!bDoAutoScroll) + { + pInsertionIndicatorHandler->UpdatePosition(aMouseModelPosition, eMode); + + // Remember the new insertion index. + mnInsertionIndex = pInsertionIndicatorHandler->GetInsertionPageIndex(); + if (pInsertionIndicatorHandler->IsInsertionTrivial(mnInsertionIndex, eMode)) + mnInsertionIndex = -1; + } +} + +void DragAndDropContext::SetTargetSlideSorter() +{ + if (mpTargetSlideSorter != nullptr) + { + mpTargetSlideSorter->GetController().GetScrollBarManager().StopAutoScroll(); + mpTargetSlideSorter->GetController().GetInsertionIndicatorHandler()->End( + Animator::AM_Animated); + } + + mpTargetSlideSorter = nullptr; +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsDragAndDropContext.hxx b/sd/source/ui/slidesorter/controller/SlsDragAndDropContext.hxx new file mode 100644 index 000000000..cbeb11f8b --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsDragAndDropContext.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 . + */ + +#pragma once + +#include <controller/SlsInsertionIndicatorHandler.hxx> + +class Point; + +namespace sd::slidesorter +{ +class SlideSorter; +} + +namespace sd::slidesorter::controller +{ +/** A DragAndDropContext object handles an active drag and drop operation. + When the mouse is moved from one slide sorter window to another the + target SlideSorter object is exchanged accordingly. +*/ +class DragAndDropContext +{ +public: + /** Create a substitution display of the currently selected pages or, + when provided, the pages in the transferable. + */ + explicit DragAndDropContext(SlideSorter& rSlideSorter); + ~DragAndDropContext() COVERITY_NOEXCEPT_FALSE; + + /** Call this method (for example as reaction to ESC key press) to avoid + processing (ie moving or inserting) the substitution when the called + DragAndDropContext object is destroyed. + */ + void Dispose(); + + /** Move the substitution display by the distance the mouse has + travelled since the last call to this method or to + CreateSubstitution(). The given point becomes the new anchor. + */ + void UpdatePosition(const Point& rMousePosition, const InsertionIndicatorHandler::Mode eMode, + const bool bAllowAutoScroll); + + void SetTargetSlideSorter(); + +private: + SlideSorter* mpTargetSlideSorter; + sal_Int32 mnInsertionIndex; +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsFocusManager.cxx b/sd/source/ui/slidesorter/controller/SlsFocusManager.cxx new file mode 100644 index 000000000..59027f5a8 --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsFocusManager.cxx @@ -0,0 +1,245 @@ +/* -*- 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 <controller/SlsFocusManager.hxx> + +#include <SlideSorter.hxx> +#include <controller/SlideSorterController.hxx> +#include <controller/SlsCurrentSlideManager.hxx> +#include <controller/SlsVisibleAreaManager.hxx> +#include <model/SlideSorterModel.hxx> +#include <model/SlsPageDescriptor.hxx> +#include <view/SlideSorterView.hxx> +#include <view/SlsLayouter.hxx> +#include <osl/diagnose.h> + +#include <Window.hxx> +#include <sdpage.hxx> + +namespace sd::slidesorter::controller { + +FocusManager::FocusManager (SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter), + mnPageIndex(0), + mbPageIsFocused(false) +{ + if (mrSlideSorter.GetModel().GetPageCount() > 0) + mnPageIndex = 0; +} + +FocusManager::~FocusManager() +{ +} + +void FocusManager::MoveFocus (FocusMoveDirection eDirection) +{ + if (!(mnPageIndex >= 0 && mbPageIsFocused)) + return; + + HideFocusIndicator (GetFocusedPageDescriptor()); + + const sal_Int32 nColumnCount (mrSlideSorter.GetView().GetLayouter().GetColumnCount()); + const sal_Int32 nPageCount (mrSlideSorter.GetModel().GetPageCount()); + switch (eDirection) + { + case FocusMoveDirection::Left: + if (mnPageIndex > 0) + mnPageIndex -= 1; + break; + + case FocusMoveDirection::Right: + if (mnPageIndex < nPageCount-1) + mnPageIndex += 1; + break; + + case FocusMoveDirection::Up: + { + const sal_Int32 nCandidate (mnPageIndex - nColumnCount); + if (nCandidate >= 0) + { + // Move the focus the previous row. + mnPageIndex = nCandidate; + } + } + break; + + case FocusMoveDirection::Down: + { + const sal_Int32 nCandidate (mnPageIndex + nColumnCount); + if (nCandidate < nPageCount) + { + // Move the focus to the next row. + mnPageIndex = nCandidate; + } + } + break; + } + + if (mnPageIndex < 0) + { + OSL_ASSERT(mnPageIndex>=0); + mnPageIndex = 0; + } + else if (mnPageIndex >= nPageCount) + { + OSL_ASSERT(mnPageIndex<nPageCount); + mnPageIndex = nPageCount - 1; + } + + if (mbPageIsFocused) + { + ShowFocusIndicator(GetFocusedPageDescriptor(), true); + } +} + +void FocusManager::ShowFocus (const bool bScrollToFocus) +{ + mbPageIsFocused = true; + ShowFocusIndicator(GetFocusedPageDescriptor(), bScrollToFocus); +} + +void FocusManager::HideFocus() +{ + mbPageIsFocused = false; + HideFocusIndicator(GetFocusedPageDescriptor()); +} + +bool FocusManager::ToggleFocus() +{ + if (mnPageIndex >= 0) + { + if (mbPageIsFocused) + HideFocus (); + else + ShowFocus (); + } + return mbPageIsFocused; +} + +bool FocusManager::HasFocus() const +{ + return mrSlideSorter.GetContentWindow()->HasFocus(); +} + +model::SharedPageDescriptor FocusManager::GetFocusedPageDescriptor() const +{ + return mrSlideSorter.GetModel().GetPageDescriptor(mnPageIndex); +} + +bool FocusManager::SetFocusedPage (const model::SharedPageDescriptor& rpDescriptor) +{ + if (rpDescriptor) + { + FocusHider aFocusHider (*this); + mnPageIndex = (rpDescriptor->GetPage()->GetPageNum()-1)/2; + return true; + } + return false; +} + +void FocusManager::SetFocusedPage (sal_Int32 nPageIndex) +{ + FocusHider aFocusHider (*this); + mnPageIndex = nPageIndex; +} + +bool FocusManager::SetFocusedPageToCurrentPage() +{ + return SetFocusedPage(mrSlideSorter.GetController().GetCurrentSlideManager()->GetCurrentSlide()); +} + +bool FocusManager::IsFocusShowing() const +{ + return HasFocus() && mbPageIsFocused; +} + +void FocusManager::HideFocusIndicator (const model::SharedPageDescriptor& rpDescriptor) +{ + if (rpDescriptor) + { + mrSlideSorter.GetView().SetState(rpDescriptor, model::PageDescriptor::ST_Focused, false); + + // Hide focus should also fire the focus event, Currently, only accessibility add the focus listener + NotifyFocusChangeListeners(); + } +} + +void FocusManager::ShowFocusIndicator ( + const model::SharedPageDescriptor& rpDescriptor, + const bool bScrollToFocus) +{ + if (!rpDescriptor) + return; + + mrSlideSorter.GetView().SetState(rpDescriptor, model::PageDescriptor::ST_Focused, true); + + if (bScrollToFocus) + { + // Scroll the focused page object into the visible area and repaint + // it, so that the focus indicator becomes visible. + mrSlideSorter.GetController().GetVisibleAreaManager().RequestVisible(rpDescriptor,true); + } + mrSlideSorter.GetView().RequestRepaint(rpDescriptor); + + NotifyFocusChangeListeners(); +} + +void FocusManager::AddFocusChangeListener (const Link<LinkParamNone*,void>& rListener) +{ + if (::std::find (maFocusChangeListeners.begin(), maFocusChangeListeners.end(), rListener) + == maFocusChangeListeners.end()) + { + maFocusChangeListeners.push_back (rListener); + } +} + +void FocusManager::RemoveFocusChangeListener (const Link<LinkParamNone*,void>& rListener) +{ + maFocusChangeListeners.erase ( + ::std::find (maFocusChangeListeners.begin(), maFocusChangeListeners.end(), rListener)); +} + +void FocusManager::NotifyFocusChangeListeners() const +{ + // Create a copy of the listener list to be safe when that is modified. + ::std::vector<Link<LinkParamNone*,void>> aListeners (maFocusChangeListeners); + + // Tell the selection change listeners that the selection has changed. + for (const auto& rListener : aListeners) + { + rListener.Call(nullptr); + } +} + +FocusManager::FocusHider::FocusHider (FocusManager& rManager) +: mbFocusVisible(rManager.IsFocusShowing()) +, mrManager(rManager) +{ + mrManager.HideFocus(); +} + +FocusManager::FocusHider::~FocusHider() COVERITY_NOEXCEPT_FALSE +{ + if (mbFocusVisible) + mrManager.ShowFocus(); +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsInsertionIndicatorHandler.cxx b/sd/source/ui/slidesorter/controller/SlsInsertionIndicatorHandler.cxx new file mode 100644 index 000000000..ff1a05ef1 --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsInsertionIndicatorHandler.cxx @@ -0,0 +1,243 @@ +/* -*- 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 <controller/SlsInsertionIndicatorHandler.hxx> +#include <view/SlideSorterView.hxx> +#include <view/SlsLayouter.hxx> +#include <view/SlsInsertAnimator.hxx> +#include <view/SlsInsertionIndicatorOverlay.hxx> +#include <model/SlideSorterModel.hxx> +#include <model/SlsPageEnumerationProvider.hxx> +#include <com/sun/star/datatransfer/dnd/DNDConstants.hpp> +#include <osl/diagnose.h> + +#include <SlideSorter.hxx> + +using namespace ::com::sun::star::datatransfer::dnd::DNDConstants; + +namespace sd::slidesorter::controller { + +InsertionIndicatorHandler::InsertionIndicatorHandler (SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter), + mpInsertionIndicatorOverlay(std::make_shared<view::InsertionIndicatorOverlay>(rSlideSorter)), + meMode(MoveMode), + mbIsInsertionTrivial(false), + mbIsActive(false), + mbIsReadOnly(mrSlideSorter.GetModel().IsReadOnly()), + mbIsOverSourceView(true), + maIconSize(0,0), + mbIsForcedShow(false) +{ +} + +InsertionIndicatorHandler::~InsertionIndicatorHandler() COVERITY_NOEXCEPT_FALSE +{ +} + +void InsertionIndicatorHandler::Start (const bool bIsOverSourceView) +{ + if (mbIsActive) + { + OSL_ASSERT(!mbIsActive); + } + + mbIsReadOnly = mrSlideSorter.GetModel().IsReadOnly(); + if (mbIsReadOnly) + return; + + mbIsActive = true; + mbIsOverSourceView = bIsOverSourceView; +} + +void InsertionIndicatorHandler::End (const controller::Animator::AnimationMode eMode) +{ + if (mbIsForcedShow || ! mbIsActive || mbIsReadOnly) + return; + + GetInsertAnimator()->Reset(eMode); + + mbIsActive = false; + // maInsertPosition = view::InsertPosition(); + meMode = UnknownMode; + + mpInsertionIndicatorOverlay->Hide(); + mpInsertionIndicatorOverlay = std::make_shared<view::InsertionIndicatorOverlay>(mrSlideSorter); +} + +void InsertionIndicatorHandler::ForceShow() +{ + mbIsForcedShow = true; +} + +void InsertionIndicatorHandler::ForceEnd() +{ + mbIsForcedShow = false; + End(Animator::AM_Immediate); +} + +void InsertionIndicatorHandler::UpdateIndicatorIcon (const SdTransferable* pTransferable) +{ + mpInsertionIndicatorOverlay->Create(pTransferable); + maIconSize = mpInsertionIndicatorOverlay->GetSize(); +} + +InsertionIndicatorHandler::Mode InsertionIndicatorHandler::GetModeFromDndAction ( + const sal_Int8 nDndAction) +{ + if ((nDndAction & ACTION_MOVE) != 0) + return MoveMode; + else if ((nDndAction & ACTION_COPY) != 0) + return CopyMode; + else + return UnknownMode; +} + +void InsertionIndicatorHandler::UpdatePosition ( + const Point& rMouseModelPosition, + const Mode eMode) +{ + if ( ! mbIsActive) + return; + + if (mbIsReadOnly) + return; + + SetPosition(rMouseModelPosition, eMode); +} + +void InsertionIndicatorHandler::UpdatePosition ( + const Point& rMouseModelPosition, + const sal_Int8 nDndAction) +{ + UpdatePosition(rMouseModelPosition, GetModeFromDndAction(nDndAction)); +} + +sal_Int32 InsertionIndicatorHandler::GetInsertionPageIndex() const +{ + if (mbIsReadOnly) + return -1; + else + return maInsertPosition.GetIndex(); +} + +void InsertionIndicatorHandler::SetPosition ( + const Point& rPoint, + const Mode eMode) +{ + view::Layouter& rLayouter (mrSlideSorter.GetView().GetLayouter()); + + const view::InsertPosition aInsertPosition (rLayouter.GetInsertPosition( + rPoint, + maIconSize, + mrSlideSorter.GetModel())); + + if (maInsertPosition == aInsertPosition && meMode == eMode) + return; + + maInsertPosition = aInsertPosition; + meMode = eMode; + mbIsInsertionTrivial = IsInsertionTrivial(maInsertPosition.GetIndex(), eMode); + if (maInsertPosition.GetIndex()>=0 && ! mbIsInsertionTrivial) + { + mpInsertionIndicatorOverlay->SetLocation(maInsertPosition.GetLocation()); + + GetInsertAnimator()->SetInsertPosition(maInsertPosition); + mpInsertionIndicatorOverlay->Show(); + } + else + { + GetInsertAnimator()->Reset(Animator::AM_Animated); + mpInsertionIndicatorOverlay->Hide(); + } +} + +std::shared_ptr<view::InsertAnimator> const & InsertionIndicatorHandler::GetInsertAnimator() +{ + if ( ! mpInsertAnimator) + mpInsertAnimator = std::make_shared<view::InsertAnimator>(mrSlideSorter); + return mpInsertAnimator; +} + +bool InsertionIndicatorHandler::IsInsertionTrivial ( + const sal_Int32 nInsertionIndex, + const Mode eMode) const +{ + if (eMode == CopyMode) + return false; + else if (eMode == UnknownMode) + return true; + + if ( ! mbIsOverSourceView) + return false; + + // Iterate over all selected pages and check whether there are + // holes. While we do this we remember the indices of the first and + // last selected page as preparation for the next step. + sal_Int32 nCurrentIndex = -1; + sal_Int32 nFirstIndex = -1; + sal_Int32 nLastIndex = -1; + model::PageEnumeration aSelectedPages ( + model::PageEnumerationProvider::CreateSelectedPagesEnumeration( + mrSlideSorter.GetModel())); + while (aSelectedPages.HasMoreElements()) + { + model::SharedPageDescriptor pDescriptor (aSelectedPages.GetNextElement()); + + // Get the page number and compare it to the last one. + const sal_Int32 nPageNumber (pDescriptor->GetPageIndex()); + if (nCurrentIndex>=0 && nPageNumber>(nCurrentIndex+1)) + return false; + else + nCurrentIndex = nPageNumber; + + // Remember indices of the first and last page of the selection. + if (nFirstIndex == -1) + nFirstIndex = nPageNumber; + nLastIndex = nPageNumber; + } + + // When we come here then the selection has no holes. We still have + // to check that the insertion position is not directly in front or + // directly behind the selection and thus moving the selection there + // would not change the model. + return nInsertionIndex >= nFirstIndex && nInsertionIndex <= (nLastIndex+1); +} + +bool InsertionIndicatorHandler::IsInsertionTrivial (const sal_Int8 nDndAction) +{ + return IsInsertionTrivial(GetInsertionPageIndex(), GetModeFromDndAction(nDndAction)); +} + +//===== InsertionIndicatorHandler::ForceShowContext =========================== + +InsertionIndicatorHandler::ForceShowContext::ForceShowContext ( + const std::shared_ptr<InsertionIndicatorHandler>& rpHandler) + : mpHandler(rpHandler) +{ + mpHandler->ForceShow(); +} + +InsertionIndicatorHandler::ForceShowContext::~ForceShowContext() COVERITY_NOEXCEPT_FALSE +{ + mpHandler->ForceEnd(); +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsListener.cxx b/sd/source/ui/slidesorter/controller/SlsListener.cxx new file mode 100644 index 000000000..000f42da2 --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsListener.cxx @@ -0,0 +1,597 @@ +/* -*- 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 "SlsListener.hxx" + +#include <SlideSorter.hxx> +#include <ViewShell.hxx> +#include <ViewShellHint.hxx> +#include <controller/SlideSorterController.hxx> +#include <controller/SlsPageSelector.hxx> +#include <controller/SlsCurrentSlideManager.hxx> +#include <controller/SlsSelectionManager.hxx> +#include <controller/SlsSelectionObserver.hxx> +#include <model/SlideSorterModel.hxx> +#include <view/SlideSorterView.hxx> +#include <cache/SlsPageCache.hxx> +#include <cache/SlsPageCacheManager.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <DrawDocShell.hxx> +#include <svx/svdpage.hxx> + +#include <ViewShellBase.hxx> +#include <EventMultiplexer.hxx> +#include <com/sun/star/document/XEventBroadcaster.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/FrameActionEvent.hpp> +#include <com/sun/star/frame/FrameAction.hpp> +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> + +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star; + +namespace sd::slidesorter::controller { + +Listener::Listener ( + SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter), + mrController(mrSlideSorter.GetController()), + mpBase(mrSlideSorter.GetViewShellBase()), + mbListeningToDocument (false), + mbListeningToUNODocument (false), + mbListeningToController (false), + mbListeningToFrame (false), + mbIsMainViewChangePending(false) +{ + StartListening(*mrSlideSorter.GetModel().GetDocument()); + StartListening(*mrSlideSorter.GetModel().GetDocument()->GetDocSh()); + mbListeningToDocument = true; + + // Connect to the UNO document. + Reference<document::XEventBroadcaster> xBroadcaster ( + mrSlideSorter.GetModel().GetDocument()->getUnoModel(), uno::UNO_QUERY); + if (xBroadcaster.is()) + { + xBroadcaster->addEventListener (this); + mbListeningToUNODocument = true; + } + + // Listen for disposing events from the document. + Reference<XComponent> xComponent (xBroadcaster, UNO_QUERY); + if (xComponent.is()) + xComponent->addEventListener ( + Reference<lang::XEventListener>( + static_cast<XWeak*>(this), UNO_QUERY)); + + // Connect to the frame to listen for controllers being exchanged. + bool bIsMainViewShell (false); + ViewShell* pViewShell = mrSlideSorter.GetViewShell(); + if (pViewShell != nullptr) + bIsMainViewShell = pViewShell->IsMainViewShell(); + if ( ! bIsMainViewShell) + { + // Listen to changes of certain properties. + Reference<frame::XFrame> xFrame; + Reference<frame::XController> xController (mrSlideSorter.GetXController()); + if (xController.is()) + xFrame = xController->getFrame(); + mxFrameWeak = xFrame; + if (xFrame.is()) + { + xFrame->addFrameActionListener(Reference<frame::XFrameActionListener>(this)); + mbListeningToFrame = true; + } + + // Connect to the current controller. + ConnectToController (); + } + + // Listen for hints of the MainViewShell as well. If that is not yet + // present then the EventMultiplexer will tell us when it is available. + if (mpBase != nullptr) + { + ViewShell* pMainViewShell = mpBase->GetMainViewShell().get(); + if (pMainViewShell != nullptr + && pMainViewShell!=pViewShell) + { + StartListening(*pMainViewShell); + } + + Link<tools::EventMultiplexerEvent&,void> aLink (LINK(this, Listener, EventMultiplexerCallback)); + mpBase->GetEventMultiplexer()->AddEventListener(aLink); + } +} + +Listener::~Listener() +{ + DBG_ASSERT( !mbListeningToDocument && !mbListeningToUNODocument && !mbListeningToFrame, + "sd::Listener::~Listener(), disposing() was not called, ask DBO!" ); +} + +void Listener::ReleaseListeners() +{ + if (mbListeningToDocument) + { + EndListening(*mrSlideSorter.GetModel().GetDocument()->GetDocSh()); + EndListening(*mrSlideSorter.GetModel().GetDocument()); + mbListeningToDocument = false; + } + + if (mbListeningToUNODocument) + { + Reference<document::XEventBroadcaster> xBroadcaster ( + mrSlideSorter.GetModel().GetDocument()->getUnoModel(), UNO_QUERY); + if (xBroadcaster.is()) + xBroadcaster->removeEventListener (this); + + // Remove the dispose listener. + Reference<XComponent> xComponent (xBroadcaster, UNO_QUERY); + if (xComponent.is()) + xComponent->removeEventListener ( + Reference<lang::XEventListener>( + static_cast<XWeak*>(this), UNO_QUERY)); + + mbListeningToUNODocument = false; + } + + if (mbListeningToFrame) + { + // Listen to changes of certain properties. + Reference<frame::XFrame> xFrame (mxFrameWeak); + if (xFrame.is()) + { + xFrame->removeFrameActionListener(Reference<frame::XFrameActionListener>(this)); + mbListeningToFrame = false; + } + } + + DisconnectFromController (); + + if (mpBase != nullptr) + { + Link<sd::tools::EventMultiplexerEvent&,void> aLink (LINK(this, Listener, EventMultiplexerCallback)); + mpBase->GetEventMultiplexer()->RemoveEventListener(aLink); + } +} + +void Listener::ConnectToController() +{ + ViewShell* pShell = mrSlideSorter.GetViewShell(); + + // Register at the controller of the main view shell (if we are that not + // ourself). + if (pShell!=nullptr && pShell->IsMainViewShell()) + return; + + Reference<frame::XController> xController (mrSlideSorter.GetXController()); + + // Listen to changes of certain properties. + Reference<beans::XPropertySet> xSet (xController, UNO_QUERY); + if (xSet.is()) + { + try + { + xSet->addPropertyChangeListener("CurrentPage", this); + } + catch (beans::UnknownPropertyException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } + try + { + xSet->addPropertyChangeListener("IsMasterPageMode", this); + } + catch (beans::UnknownPropertyException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } + } + + // Listen for disposing events. + if (xController.is()) + { + xController->addEventListener ( + Reference<lang::XEventListener>(static_cast<XWeak*>(this), UNO_QUERY)); + + mxControllerWeak = xController; + mbListeningToController = true; + } +} + +void Listener::DisconnectFromController() +{ + if (!mbListeningToController) + return; + + Reference<frame::XController> xController = mxControllerWeak; + Reference<beans::XPropertySet> xSet (xController, UNO_QUERY); + try + { + // Remove the property listener. + if (xSet.is()) + { + xSet->removePropertyChangeListener( "CurrentPage", this ); + xSet->removePropertyChangeListener( "IsMasterPageMode", this); + } + + // Remove the dispose listener. + if (xController.is()) + xController->removeEventListener ( + Reference<lang::XEventListener>( + static_cast<XWeak*>(this), UNO_QUERY)); + } + catch (beans::UnknownPropertyException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } + + mbListeningToController = false; + mxControllerWeak = Reference<frame::XController>(); +} + +void Listener::Notify ( + SfxBroadcaster& rBroadcaster, + const SfxHint& rHint) +{ + if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint) + { + const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint); + switch (pSdrHint->GetKind()) + { + case SdrHintKind::ModelCleared: + if (&rBroadcaster == mrSlideSorter.GetModel().GetDocument()) + { // rhbz#965646 stop listening to dying document + EndListening(rBroadcaster); + return; + } + break; + case SdrHintKind::PageOrderChange: + if (&rBroadcaster == mrSlideSorter.GetModel().GetDocument()) + HandleModelChange(pSdrHint->GetPage()); + break; + + default: + break; + } + } + else if (rHint.GetId() == SfxHintId::DocChanged) + { + mrController.CheckForMasterPageAssignment(); + mrController.CheckForSlideTransitionAssignment(); + } + else if (auto pViewShellHint = dynamic_cast<const ViewShellHint*>(&rHint)) + { + switch (pViewShellHint->GetHintId()) + { + case ViewShellHint::HINT_PAGE_RESIZE_START: + // Initiate a model change but do nothing (well, not much) + // until we are told that all slides have been resized. + mpModelChangeLock.reset(new SlideSorterController::ModelChangeLock(mrController), + o3tl::default_delete<SlideSorterController::ModelChangeLock>()); + mrController.HandleModelChange(); + break; + + case ViewShellHint::HINT_PAGE_RESIZE_END: + // All slides have been resized. The model has to be updated. + mpModelChangeLock.reset(); + break; + + case ViewShellHint::HINT_CHANGE_EDIT_MODE_START: + mrController.PrepareEditModeChange(); + break; + + case ViewShellHint::HINT_CHANGE_EDIT_MODE_END: + mrController.FinishEditModeChange(); + break; + + case ViewShellHint::HINT_COMPLEX_MODEL_CHANGE_START: + mpModelChangeLock.reset(new SlideSorterController::ModelChangeLock(mrController), + o3tl::default_delete<SlideSorterController::ModelChangeLock>()); + break; + + case ViewShellHint::HINT_COMPLEX_MODEL_CHANGE_END: + mpModelChangeLock.reset(); + break; + } + } +} + +IMPL_LINK(Listener, EventMultiplexerCallback, ::sd::tools::EventMultiplexerEvent&, rEvent, void) +{ + switch (rEvent.meEventId) + { + case EventMultiplexerEventId::MainViewRemoved: + { + if (mpBase != nullptr) + { + ViewShell* pMainViewShell = mpBase->GetMainViewShell().get(); + if (pMainViewShell != nullptr) + EndListening(*pMainViewShell); + } + } + break; + + case EventMultiplexerEventId::MainViewAdded: + mbIsMainViewChangePending = true; + break; + + case EventMultiplexerEventId::ConfigurationUpdated: + if (mbIsMainViewChangePending && mpBase != nullptr) + { + mbIsMainViewChangePending = false; + ViewShell* pMainViewShell = mpBase->GetMainViewShell().get(); + if (pMainViewShell != nullptr + && pMainViewShell!=mrSlideSorter.GetViewShell()) + { + StartListening (*pMainViewShell); + } + } + break; + + case EventMultiplexerEventId::ControllerAttached: + { + ConnectToController(); + // mrController.GetPageSelector().GetCoreSelection(); + UpdateEditMode(); + } + break; + + case EventMultiplexerEventId::ControllerDetached: + DisconnectFromController(); + break; + + case EventMultiplexerEventId::ShapeChanged: + case EventMultiplexerEventId::ShapeInserted: + case EventMultiplexerEventId::ShapeRemoved: + HandleShapeModification(static_cast<const SdrPage*>(rEvent.mpUserData)); + break; + + case EventMultiplexerEventId::EndTextEdit: + if (rEvent.mpUserData != nullptr) + { + const SdrObject* pObject = static_cast<const SdrObject*>(rEvent.mpUserData); + HandleShapeModification(pObject->getSdrPageFromSdrObject()); + } + break; + + default: + break; + } +} + +//===== lang::XEventListener ================================================ + +void SAL_CALL Listener::disposing ( + const lang::EventObject& rEventObject) +{ + if ((mbListeningToDocument || mbListeningToUNODocument) + && mrSlideSorter.GetModel().GetDocument()!=nullptr + && rEventObject.Source + == mrSlideSorter.GetModel().GetDocument()->getUnoModel()) + { + mbListeningToDocument = false; + mbListeningToUNODocument = false; + } + else if (mbListeningToController) + { + Reference<frame::XController> xController (mxControllerWeak); + if (rEventObject.Source == xController) + { + mbListeningToController = false; + } + } +} + +//===== document::XEventListener ============================================ + +void SAL_CALL Listener::notifyEvent ( + const document::EventObject& ) +{ +} + +//===== beans::XPropertySetListener ========================================= + +void SAL_CALL Listener::propertyChange ( + const PropertyChangeEvent& rEvent) +{ + if (m_bDisposed) + { + throw lang::DisposedException ("SlideSorterController object has already been disposed", + static_cast<uno::XWeak*>(this)); + } + + if (rEvent.PropertyName == "CurrentPage") + { + Any aCurrentPage = rEvent.NewValue; + Reference<beans::XPropertySet> xPageSet (aCurrentPage, UNO_QUERY); + if (xPageSet.is()) + { + try + { + Any aPageNumber = xPageSet->getPropertyValue ("Number"); + sal_Int32 nCurrentPage = 0; + aPageNumber >>= nCurrentPage; + // The selection is already set but we call SelectPage() + // nevertheless in order to make the new current page the + // last recently selected page of the PageSelector. This is + // used when making the selection visible. + mrController.GetCurrentSlideManager()->NotifyCurrentSlideChange(nCurrentPage-1); + mrController.GetPageSelector().SelectPage(nCurrentPage-1); + } + catch (beans::UnknownPropertyException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } + catch (lang::DisposedException&) + { + // Something is already disposed. There is not much we can + // do, except not to crash. + } + } + } + else if (rEvent.PropertyName == "IsMasterPageMode") + { + bool bIsMasterPageMode = false; + rEvent.NewValue >>= bIsMasterPageMode; + mrController.ChangeEditMode ( + bIsMasterPageMode ? EditMode::MasterPage : EditMode::Page); + } +} + +//===== frame::XFrameActionListener ========================================== + +void SAL_CALL Listener::frameAction (const frame::FrameActionEvent& rEvent) +{ + switch (rEvent.Action) + { + case frame::FrameAction_COMPONENT_DETACHING: + DisconnectFromController(); + break; + + case frame::FrameAction_COMPONENT_REATTACHED: + { + ConnectToController(); + mrController.GetPageSelector().GetCoreSelection(); + UpdateEditMode(); + } + break; + + default: + break; + } +} + +//===== accessibility::XAccessibleEventListener ============================== + +void SAL_CALL Listener::notifyEvent ( + const AccessibleEventObject& ) +{ +} + +void Listener::disposing(std::unique_lock<std::mutex>&) +{ + ReleaseListeners(); +} + +void Listener::UpdateEditMode() +{ + // When there is a new controller then the edit mode may have changed at + // the same time. + Reference<frame::XController> xController (mxControllerWeak); + Reference<beans::XPropertySet> xSet (xController, UNO_QUERY); + bool bIsMasterPageMode = false; + if (xSet != nullptr) + { + try + { + Any aValue (xSet->getPropertyValue( "IsMasterPageMode" )); + aValue >>= bIsMasterPageMode; + } + catch (beans::UnknownPropertyException&) + { + // When the property is not supported then the master page mode + // is not supported, too. + bIsMasterPageMode = false; + } + } + mrController.ChangeEditMode ( + bIsMasterPageMode ? EditMode::MasterPage : EditMode::Page); +} + +void Listener::HandleModelChange (const SdrPage* pPage) +{ + // Notify model and selection observer about the page. The return value + // of the model call acts as filter as to which events to pass to the + // selection observer. + if (mrSlideSorter.GetModel().NotifyPageEvent(pPage)) + { + // The page of the hint belongs (or belonged) to the model. + + // Tell the cache manager that the preview bitmaps for a deleted + // page can be removed from all caches. + if (pPage!=nullptr && ! pPage->IsInserted()) + cache::PageCacheManager::Instance()->ReleasePreviewBitmap(pPage); + + mrController.GetSelectionManager()->GetSelectionObserver()->NotifyPageEvent(pPage); + } + + // Tell the controller about the model change only when the document is + // in a sane state, not just in the middle of a larger change. + SdDrawDocument* pDocument (mrSlideSorter.GetModel().GetDocument()); + if (pDocument != nullptr + && pDocument->GetMasterSdPageCount(PageKind::Standard) == pDocument->GetMasterSdPageCount(PageKind::Notes)) + { + // A model change can make updates of some text fields necessary + // (like page numbers and page count.) Invalidate all previews in + // the cache to cope with this. Doing this on demand would be a + // nice optimization. + cache::PageCacheManager::Instance()->InvalidateAllPreviewBitmaps(pDocument->getUnoModel()); + + mrController.HandleModelChange(); + } +} + +void Listener::HandleShapeModification (const SdrPage* pPage) +{ + if (pPage == nullptr) + return; + + // Invalidate the preview of the page (in all slide sorters that display + // it.) + std::shared_ptr<cache::PageCacheManager> pCacheManager (cache::PageCacheManager::Instance()); + if ( ! pCacheManager) + return; + SdDrawDocument* pDocument = mrSlideSorter.GetModel().GetDocument(); + if (pDocument == nullptr) + { + OSL_ASSERT(pDocument!=nullptr); + return; + } + pCacheManager->InvalidatePreviewBitmap(pDocument->getUnoModel(), pPage); + mrSlideSorter.GetView().GetPreviewCache()->RequestPreviewBitmap(pPage); + + // When the page is a master page then invalidate the previews of all + // pages that are linked to this master page. + if (!pPage->IsMasterPage()) + return; + + for (sal_uInt16 nIndex=0,nCount=pDocument->GetSdPageCount(PageKind::Standard); + nIndex<nCount; + ++nIndex) + { + const SdPage* pCandidate = pDocument->GetSdPage(nIndex, PageKind::Standard); + if (pCandidate!=nullptr && pCandidate->TRG_HasMasterPage()) + { + if (&pCandidate->TRG_GetMasterPage() == pPage) + pCacheManager->InvalidatePreviewBitmap(pDocument->getUnoModel(), pCandidate); + } + else + { + OSL_ASSERT(pCandidate!=nullptr && pCandidate->TRG_HasMasterPage()); + } + } +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsListener.hxx b/sd/source/ui/slidesorter/controller/SlsListener.hxx new file mode 100644 index 000000000..eff02cf19 --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsListener.hxx @@ -0,0 +1,164 @@ +/* -*- 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 . + */ + +#pragma once + +#include <controller/SlideSorterController.hxx> +#include <com/sun/star/document/XEventListener.hpp> +#include <com/sun/star/beans/XPropertyChangeListener.hpp> +#include <com/sun/star/accessibility/XAccessibleEventListener.hpp> +#include <com/sun/star/frame/XFrameActionListener.hpp> +#include <comphelper/compbase.hxx> +#include <cppuhelper/weakref.hxx> + +#include <svl/lstner.hxx> +#include <tools/link.hxx> +#include <memory> + +class SdrPage; + +namespace sd { +class ViewShellBase; +} + +namespace sd::tools { class EventMultiplexerEvent; } +namespace sd::slidesorter { class SlideSorter; } + +namespace sd::slidesorter::controller { + +typedef comphelper::WeakComponentImplHelper< + css::document::XEventListener, + css::beans::XPropertyChangeListener, + css::accessibility::XAccessibleEventListener, + css::frame::XFrameActionListener + > ListenerInterfaceBase; + +/** Listen for events of various types and sources and react to them. This + class is a part of the controller. + + When the view shell in the center pane is replaced by another the + associated controller is replaced as well. Therefore we have to + register at the frame and on certain FrameActionEvents to stop listening + to the old controller and register as listener at the new one. +*/ +class Listener + : public ListenerInterfaceBase, + public SfxListener +{ +public: + explicit Listener (SlideSorter& rSlideSorter); + virtual ~Listener() override; + + /** Connect to the current controller of the view shell as listener. + This method is called once during initialization and every time a + FrameActionEvent signals the current controller being exchanged. + When the connection is successful then the flag + mbListeningToController is set to <TRUE/>. + */ + void ConnectToController(); + + /** Disconnect from the current controller of the view shell as + listener. This method is called once during initialization and + every time a FrameActionEvent signals the current controller being + exchanged. When this method terminates then mbListeningToController + is <FALSE/>. + */ + void DisconnectFromController(); + + virtual void Notify ( + SfxBroadcaster& rBroadcaster, + const SfxHint& rHint) override; + + //===== lang::XEventListener ============================================ + virtual void SAL_CALL + disposing (const css::lang::EventObject& rEventObject) override; + + //===== document::XEventListener ======================================== + virtual void SAL_CALL + notifyEvent ( + const css::document::EventObject& rEventObject) override; + + //===== beans::XPropertySetListener ===================================== + virtual void SAL_CALL + propertyChange ( + const css::beans::PropertyChangeEvent& rEvent) override; + + //===== accessibility::XAccessibleEventListener ========================== + virtual void SAL_CALL + notifyEvent ( + const css::accessibility::AccessibleEventObject& + rEvent) override; + + //===== frame::XFrameActionListener ====================================== + /** For certain actions the listener connects to a new controller of the + frame it is listening to. This usually happens when the view shell + in the center pane is replaced by another view shell. + */ + virtual void SAL_CALL + frameAction (const css::frame::FrameActionEvent& rEvent) override; + + virtual void disposing(std::unique_lock<std::mutex>&) override; + +private: + SlideSorter& mrSlideSorter; + SlideSorterController& mrController; + ViewShellBase* mpBase; + + /// Remember whether we are listening to the document. + bool mbListeningToDocument; + /// Remember whether we are listening to the UNO document. + bool mbListeningToUNODocument; + /// Remember whether we are listening to the UNO controller. + bool mbListeningToController; + /// Remember whether we are listening to the frame. + bool mbListeningToFrame; + bool mbIsMainViewChangePending; + + css::uno::WeakReference< css::frame::XController> mxControllerWeak; + css::uno::WeakReference< css::frame::XFrame> mxFrameWeak; + + /** This object is used to lock the model between some + events. It is references counted in order to cope with events that + are expected but never sent. + */ + std::shared_ptr<SlideSorterController::ModelChangeLock> mpModelChangeLock; + + void ReleaseListeners(); + + /** Called when the edit mode has changed. Update model accordingly. + */ + void UpdateEditMode(); + + /** Handle a change in the order of slides or when the set of slides has + changed, i.e. a slide has been created. + */ + void HandleModelChange (const SdrPage* pPage); + + /** Handle a modification to a shape on the given page. When this is a + regular page then update its preview. When it is a master page then + additionally update the previews of all pages linked to it. + */ + void HandleShapeModification (const SdrPage* pPage); + + DECL_LINK(EventMultiplexerCallback, tools::EventMultiplexerEvent&, void); +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsPageSelector.cxx b/sd/source/ui/slidesorter/controller/SlsPageSelector.cxx new file mode 100644 index 000000000..21affcf2f --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsPageSelector.cxx @@ -0,0 +1,386 @@ +/* -*- 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 <controller/SlsPageSelector.hxx> + +#include <SlideSorter.hxx> +#include <controller/SlideSorterController.hxx> +#include <controller/SlsSelectionManager.hxx> +#include <controller/SlsCurrentSlideManager.hxx> +#include <controller/SlsVisibleAreaManager.hxx> +#include <model/SlsPageDescriptor.hxx> +#include <model/SlsPageEnumerationProvider.hxx> +#include <model/SlideSorterModel.hxx> +#include <view/SlideSorterView.hxx> +#include <osl/diagnose.h> + +#include <sdpage.hxx> +#include <tools/debug.hxx> +#include <memory> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::sd::slidesorter::model; +using namespace ::sd::slidesorter::view; + +namespace sd::slidesorter::controller { + +PageSelector::PageSelector (SlideSorter& rSlideSorter) + : mrModel(rSlideSorter.GetModel()), + mrSlideSorter(rSlideSorter), + mrController(mrSlideSorter.GetController()), + mnSelectedPageCount(0), + mnBroadcastDisableLevel(0), + mbSelectionChangeBroadcastPending(false), + mnUpdateLockCount(0), + mbIsUpdateCurrentPagePending(true) +{ + CountSelectedPages (); +} + +void PageSelector::SelectAllPages() +{ + VisibleAreaManager::TemporaryDisabler aDisabler (mrSlideSorter); + PageSelector::UpdateLock aLock (*this); + + int nPageCount = mrModel.GetPageCount(); + for (int nPageIndex=0; nPageIndex<nPageCount; nPageIndex++) + SelectPage(nPageIndex); +} + +void PageSelector::DeselectAllPages() +{ + VisibleAreaManager::TemporaryDisabler aDisabler (mrSlideSorter); + PageSelector::UpdateLock aLock (*this); + + int nPageCount = mrModel.GetPageCount(); + for (int nPageIndex=0; nPageIndex<nPageCount; nPageIndex++) + DeselectPage(nPageIndex); + + DBG_ASSERT (mnSelectedPageCount==0, + "PageSelector::DeselectAllPages: the selected pages counter is not 0"); + mnSelectedPageCount = 0; + mpSelectionAnchor.reset(); +} + +void PageSelector::GetCoreSelection() +{ + PageSelector::UpdateLock aLock (*this); + + bool bSelectionHasChanged (true); + mnSelectedPageCount = 0; + model::PageEnumeration aAllPages ( + model::PageEnumerationProvider::CreateAllPagesEnumeration(mrModel)); + while (aAllPages.HasMoreElements()) + { + model::SharedPageDescriptor pDescriptor (aAllPages.GetNextElement()); + if (pDescriptor->GetCoreSelection()) + { + mrSlideSorter.GetController().GetVisibleAreaManager().RequestVisible(pDescriptor); + mrSlideSorter.GetView().RequestRepaint(pDescriptor); + bSelectionHasChanged = true; + } + + if (pDescriptor->HasState(PageDescriptor::ST_Selected)) + mnSelectedPageCount++; + } + + if (bSelectionHasChanged) + { + if (mnBroadcastDisableLevel > 0) + mbSelectionChangeBroadcastPending = true; + else + mrController.GetSelectionManager()->SelectionHasChanged(); + } +} + +void PageSelector::SetCoreSelection() +{ + model::PageEnumeration aAllPages ( + model::PageEnumerationProvider::CreateAllPagesEnumeration(mrModel)); + while (aAllPages.HasMoreElements()) + { + model::SharedPageDescriptor pDescriptor (aAllPages.GetNextElement()); + pDescriptor->SetCoreSelection(); + } +} + +void PageSelector::SelectPage (int nPageIndex) +{ + SharedPageDescriptor pDescriptor (mrModel.GetPageDescriptor(nPageIndex)); + if (pDescriptor) + SelectPage(pDescriptor); +} + +void PageSelector::SelectPage (const SdPage* pPage) +{ + const sal_Int32 nPageIndex (mrModel.GetIndex(pPage)); + SharedPageDescriptor pDescriptor (mrModel.GetPageDescriptor(nPageIndex)); + if (pDescriptor && pDescriptor->GetPage()==pPage) + SelectPage(pDescriptor); +} + +void PageSelector::SelectPage (const SharedPageDescriptor& rpDescriptor) +{ + if (!rpDescriptor + || !mrSlideSorter.GetView().SetState(rpDescriptor, PageDescriptor::ST_Selected, true)) + return; + + ++mnSelectedPageCount; + mrSlideSorter.GetController().GetVisibleAreaManager().RequestVisible(rpDescriptor,true); + mrSlideSorter.GetView().RequestRepaint(rpDescriptor); + + mpMostRecentlySelectedPage = rpDescriptor; + if (mpSelectionAnchor == nullptr) + mpSelectionAnchor = rpDescriptor; + + if (mnBroadcastDisableLevel > 0) + mbSelectionChangeBroadcastPending = true; + else + mrController.GetSelectionManager()->SelectionHasChanged(); + UpdateCurrentPage(); + + CheckConsistency(); +} + +void PageSelector::DeselectPage (int nPageIndex) +{ + model::SharedPageDescriptor pDescriptor (mrModel.GetPageDescriptor(nPageIndex)); + if (pDescriptor) + DeselectPage(pDescriptor); +} + +void PageSelector::DeselectPage ( + const SharedPageDescriptor& rpDescriptor, + const bool bUpdateCurrentPage) +{ + if (!rpDescriptor + || !mrSlideSorter.GetView().SetState(rpDescriptor, PageDescriptor::ST_Selected, false)) + return; + + --mnSelectedPageCount; + mrSlideSorter.GetController().GetVisibleAreaManager().RequestVisible(rpDescriptor); + mrSlideSorter.GetView().RequestRepaint(rpDescriptor); + if (mpMostRecentlySelectedPage == rpDescriptor) + mpMostRecentlySelectedPage.reset(); + if (mnBroadcastDisableLevel > 0) + mbSelectionChangeBroadcastPending = true; + else + mrController.GetSelectionManager()->SelectionHasChanged(); + if (bUpdateCurrentPage) + UpdateCurrentPage(); + + CheckConsistency(); +} + +void PageSelector::CheckConsistency() const +{ + int nSelectionCount (0); + for (int nPageIndex=0,nPageCount=mrModel.GetPageCount(); nPageIndex<nPageCount; nPageIndex++) + { + SharedPageDescriptor pDescriptor (mrModel.GetPageDescriptor(nPageIndex)); + assert(pDescriptor); + if (pDescriptor->HasState(PageDescriptor::ST_Selected)) + ++nSelectionCount; + } + if (nSelectionCount!=mnSelectedPageCount) + { + // #i120020# The former call to assert(..) internally calls + // SlideSorterModel::GetPageDescriptor which will crash in this situation + // (only in non-pro code). All what is wanted there is to assert it (the + // error is already detected), so do this directly. + OSL_ENSURE(false, "PageSelector: Consistency error (!)"); + } +} + +bool PageSelector::IsPageSelected(int nPageIndex) +{ + SharedPageDescriptor pDescriptor (mrModel.GetPageDescriptor(nPageIndex)); + if (pDescriptor) + return pDescriptor->HasState(PageDescriptor::ST_Selected); + else + return false; +} + +bool PageSelector::IsPageVisible(int nPageIndex) +{ + SharedPageDescriptor pDescriptor (mrModel.GetPageDescriptor(nPageIndex)); + if (pDescriptor) + return pDescriptor->HasState(PageDescriptor::ST_Visible); + else + return false; +} + +int PageSelector::GetPageCount() const +{ + return mrModel.GetPageCount(); +} + +void PageSelector::CountSelectedPages() +{ + mnSelectedPageCount = 0; + model::PageEnumeration aSelectedPages ( + model::PageEnumerationProvider::CreateSelectedPagesEnumeration(mrModel)); + while (aSelectedPages.HasMoreElements()) + { + mnSelectedPageCount++; + aSelectedPages.GetNextElement(); + } +} + +void PageSelector::EnableBroadcasting() +{ + if (mnBroadcastDisableLevel > 0) + mnBroadcastDisableLevel --; + if (mnBroadcastDisableLevel==0 && mbSelectionChangeBroadcastPending) + { + mrController.GetSelectionManager()->SelectionHasChanged(); + mbSelectionChangeBroadcastPending = false; + } +} + +void PageSelector::DisableBroadcasting() +{ + mnBroadcastDisableLevel ++; +} + +std::shared_ptr<PageSelector::PageSelection> PageSelector::GetPageSelection() const +{ + auto pSelection = std::make_shared<PageSelection>(); + pSelection->reserve(GetSelectedPageCount()); + + int nPageCount = GetPageCount(); + for (int nIndex=0; nIndex<nPageCount; nIndex++) + { + SharedPageDescriptor pDescriptor (mrModel.GetPageDescriptor(nIndex)); + if (pDescriptor && pDescriptor->HasState(PageDescriptor::ST_Selected)) + pSelection->push_back(pDescriptor->GetPage()); + } + + return pSelection; +} + +void PageSelector::SetPageSelection ( + const std::shared_ptr<PageSelection>& rpSelection, + const bool bUpdateCurrentPage) +{ + for (const auto& rpPage : *rpSelection) + SelectPage(rpPage); + if (bUpdateCurrentPage) + UpdateCurrentPage(); +} + +void PageSelector::UpdateCurrentPage (const bool bUpdateOnlyWhenPending) +{ + if (mnUpdateLockCount > 0) + { + mbIsUpdateCurrentPagePending = true; + return; + } + + if ( ! mbIsUpdateCurrentPagePending && bUpdateOnlyWhenPending) + return; + + mbIsUpdateCurrentPagePending = false; + + // Make the first selected page the current page. + SharedPageDescriptor pCurrentPageDescriptor; + const sal_Int32 nPageCount (GetPageCount()); + for (sal_Int32 nIndex=0; nIndex<nPageCount; ++nIndex) + { + SharedPageDescriptor pDescriptor (mrModel.GetPageDescriptor(nIndex)); + if ( ! pDescriptor) + continue; + if (pDescriptor->HasState(PageDescriptor::ST_Selected)) + { + pCurrentPageDescriptor = pDescriptor; + break; + } + } + + if (!pCurrentPageDescriptor) + return; + + // Switching the current slide normally sets also the + // selection to just the new current slide. To prevent that, + // we store (and at the end of this scope restore) the current + // selection. + std::shared_ptr<PageSelection> pSelection (GetPageSelection()); + + mrController.GetCurrentSlideManager()->SwitchCurrentSlide(pCurrentPageDescriptor); + + // Restore the selection and prevent a recursive call to + // UpdateCurrentPage(). + SetPageSelection(pSelection, false); +} + +//===== PageSelector::UpdateLock ============================================== + +PageSelector::UpdateLock::UpdateLock (SlideSorter const & rSlideSorter) + : mpSelector(&rSlideSorter.GetController().GetPageSelector()) +{ + ++mpSelector->mnUpdateLockCount; +} + +PageSelector::UpdateLock::UpdateLock (PageSelector& rSelector) + : mpSelector(&rSelector) +{ + ++mpSelector->mnUpdateLockCount; +} + +PageSelector::UpdateLock::~UpdateLock() +{ + Release(); +} + +void PageSelector::UpdateLock::Release() +{ + if (mpSelector != nullptr) + { + --mpSelector->mnUpdateLockCount; + OSL_ASSERT(mpSelector->mnUpdateLockCount >= 0); + if (mpSelector->mnUpdateLockCount == 0) + mpSelector->UpdateCurrentPage(true); + + mpSelector = nullptr; + } +} + +//===== PageSelector::BroadcastLock ============================================== + +PageSelector::BroadcastLock::BroadcastLock (SlideSorter const & rSlideSorter) + : mrSelector(rSlideSorter.GetController().GetPageSelector()) +{ + mrSelector.DisableBroadcasting(); +} + +PageSelector::BroadcastLock::BroadcastLock (PageSelector& rSelector) + : mrSelector(rSelector) +{ + mrSelector.DisableBroadcasting(); +} + +PageSelector::BroadcastLock::~BroadcastLock() +{ + mrSelector.EnableBroadcasting(); +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsProperties.cxx b/sd/source/ui/slidesorter/controller/SlsProperties.cxx new file mode 100644 index 000000000..f1152a373 --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsProperties.cxx @@ -0,0 +1,106 @@ +/* -*- 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 <controller/SlsProperties.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> + +namespace sd::slidesorter::controller { + +Properties::Properties() + : mbIsHighlightCurrentSlide(false), + mbIsShowSelection(true), + mbIsShowFocus(true), + mbIsCenterSelection(false), + mbIsSmoothSelectionScrolling(true), + mbIsSuspendPreviewUpdatesDuringFullScreenPresentation(true), + maBackgroundColor(Application::GetSettings().GetStyleSettings().GetWindowColor()), + maTextColor(Application::GetSettings().GetStyleSettings().GetActiveTextColor()), + maSelectionColor(Application::GetSettings().GetStyleSettings().GetHighlightColor()), + maHighlightColor(Application::GetSettings().GetStyleSettings().GetMenuHighlightColor()), + mbIsUIReadOnly(false) +{ +} + +void Properties::HandleDataChangeEvent() +{ + maBackgroundColor = Application::GetSettings().GetStyleSettings().GetWindowColor(); + maTextColor = Application::GetSettings().GetStyleSettings().GetActiveTextColor(); + maSelectionColor = Application::GetSettings().GetStyleSettings().GetHighlightColor(); + maHighlightColor = Application::GetSettings().GetStyleSettings().GetMenuHighlightColor(); +} + +void Properties::SetHighlightCurrentSlide (const bool bIsHighlightCurrentSlide) +{ + mbIsHighlightCurrentSlide = bIsHighlightCurrentSlide; +} + +void Properties::SetShowSelection (const bool bIsShowSelection) +{ + mbIsShowSelection = bIsShowSelection; +} + +void Properties::SetShowFocus (const bool bIsShowFocus) +{ + mbIsShowFocus = bIsShowFocus; +} + +void Properties::SetCenterSelection (const bool bIsCenterSelection) +{ + mbIsCenterSelection = bIsCenterSelection; +} + +void Properties::SetSmoothSelectionScrolling (const bool bIsSmoothSelectionScrolling) +{ + mbIsSmoothSelectionScrolling = bIsSmoothSelectionScrolling; +} + +void Properties::SetSuspendPreviewUpdatesDuringFullScreenPresentation (const bool bFlag) +{ + mbIsSuspendPreviewUpdatesDuringFullScreenPresentation = bFlag; +} + +void Properties::SetBackgroundColor (const Color& rColor) +{ + maBackgroundColor = rColor; +} + +void Properties::SetTextColor (const Color& rColor) +{ + maTextColor = rColor; +} + +void Properties::SetSelectionColor (const Color& rColor) +{ + maSelectionColor = rColor; +} + +void Properties::SetHighlightColor (const Color& rColor) +{ + maHighlightColor = rColor; +} + +void Properties::SetUIReadOnly (const bool bIsUIReadOnly) +{ + mbIsUIReadOnly = bIsUIReadOnly; +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsScrollBarManager.cxx b/sd/source/ui/slidesorter/controller/SlsScrollBarManager.cxx new file mode 100644 index 000000000..83192414f --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsScrollBarManager.cxx @@ -0,0 +1,608 @@ +/* -*- 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 <controller/SlsScrollBarManager.hxx> + +#include <SlideSorter.hxx> +#include <ViewShell.hxx> +#include <controller/SlideSorterController.hxx> +#include <controller/SlsVisibleAreaManager.hxx> +#include <model/SlideSorterModel.hxx> +#include <model/SlsPageDescriptor.hxx> +#include <view/SlideSorterView.hxx> +#include <view/SlsLayouter.hxx> +#include <Window.hxx> +#include <sdpage.hxx> +#include <osl/diagnose.h> + +#include <vcl/scrbar.hxx> + +namespace sd::slidesorter::controller { + +constexpr double gnHorizontalScrollFactor(0.15); +constexpr double gnVerticalScrollFactor(0.25); + +ScrollBarManager::ScrollBarManager (SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter), + mpHorizontalScrollBar(mrSlideSorter.GetHorizontalScrollBar()), + mpVerticalScrollBar(mrSlideSorter.GetVerticalScrollBar()), + mnHorizontalPosition (0), + mnVerticalPosition (0), + maScrollBorder (20,20), + mpScrollBarFiller(mrSlideSorter.GetScrollBarFiller()), + maAutoScrollTimer("sd ScrollBarManager maAutoScrollTimer"), + maAutoScrollOffset(0,0), + mbIsAutoScrollActive(false), + mpContentWindow(mrSlideSorter.GetContentWindow()) +{ + // Hide the scroll bars by default to prevent display errors while + // switching between view shells: In the short time between initiating + // such a switch and the final rearrangement of UI controls the scroll + // bars and the filler where displayed in the upper left corner of the + // ViewTabBar. + mpHorizontalScrollBar->Hide(); + mpVerticalScrollBar->Hide(); + mpScrollBarFiller->Hide(); + + maAutoScrollTimer.SetTimeout(25); + maAutoScrollTimer.SetInvokeHandler ( + LINK(this, ScrollBarManager, AutoScrollTimeoutHandler)); +} + +ScrollBarManager::~ScrollBarManager() +{ +} + +void ScrollBarManager::Connect() +{ + if (mpVerticalScrollBar != nullptr) + { + mpVerticalScrollBar->SetScrollHdl ( + LINK(this, ScrollBarManager, VerticalScrollBarHandler)); + } + if (mpHorizontalScrollBar != nullptr) + { + mpHorizontalScrollBar->SetScrollHdl( + LINK(this, ScrollBarManager, HorizontalScrollBarHandler)); + } +} + +void ScrollBarManager::Disconnect() +{ + if (mpVerticalScrollBar != nullptr) + { + mpVerticalScrollBar->SetScrollHdl( Link<ScrollBar*,void>() ); + } + if (mpHorizontalScrollBar != nullptr) + { + mpHorizontalScrollBar->SetScrollHdl( Link<ScrollBar*,void>() ); + } +} + +/** Placing the scroll bars is an iterative process. The visibility of one + scroll bar affects the remaining size and thus may lead to the other + scroll bar becoming visible. + + First we determine the visibility of the horizontal scroll bar. After + that we do the same for the vertical scroll bar. To have an initial + value for the required size we call the layouter before that. When one + of the two scroll bars is made visible then the size of the browser + window changes and a second call to the layouter becomes necessary. + That call is made anyway after this method returns. +*/ +::tools::Rectangle ScrollBarManager::PlaceScrollBars ( + const ::tools::Rectangle& rAvailableArea, + const bool bIsHorizontalScrollBarAllowed, + const bool bIsVerticalScrollBarAllowed) +{ + ::tools::Rectangle aRemainingSpace (DetermineScrollBarVisibilities( + rAvailableArea, + bIsHorizontalScrollBarAllowed, + bIsVerticalScrollBarAllowed)); + + if (mpHorizontalScrollBar!=nullptr && mpHorizontalScrollBar->IsVisible()) + PlaceHorizontalScrollBar (rAvailableArea); + + if (mpVerticalScrollBar!=nullptr && mpVerticalScrollBar->IsVisible()) + PlaceVerticalScrollBar (rAvailableArea); + + if (mpScrollBarFiller!=nullptr && mpScrollBarFiller->IsVisible()) + PlaceFiller (rAvailableArea); + + return aRemainingSpace; +} + +void ScrollBarManager::PlaceHorizontalScrollBar (const ::tools::Rectangle& aAvailableArea) +{ + // Save the current relative position. + mnHorizontalPosition = double(mpHorizontalScrollBar->GetThumbPos()) + / double(mpHorizontalScrollBar->GetRange().Len()); + + // Place the scroll bar. + Size aScrollBarSize (mpHorizontalScrollBar->GetSizePixel()); + mpHorizontalScrollBar->SetPosSizePixel ( + Point(aAvailableArea.Left(), + aAvailableArea.Bottom()-aScrollBarSize.Height()+1), + Size (aAvailableArea.GetWidth() - GetVerticalScrollBarWidth(), + aScrollBarSize.Height())); + + // Restore the relative position. + mpHorizontalScrollBar->SetThumbPos( + static_cast<::tools::Long>(0.5 + mnHorizontalPosition * mpHorizontalScrollBar->GetRange().Len())); +} + +void ScrollBarManager::PlaceVerticalScrollBar (const ::tools::Rectangle& aArea) +{ + const sal_Int32 nThumbPosition (mpVerticalScrollBar->GetThumbPos()); + + // Place the scroll bar. + Size aScrollBarSize (mpVerticalScrollBar->GetSizePixel()); + Point aPosition (aArea.Right()-aScrollBarSize.Width()+1, aArea.Top()); + Size aSize (aScrollBarSize.Width(), aArea.GetHeight() - GetHorizontalScrollBarHeight()); + mpVerticalScrollBar->SetPosSizePixel(aPosition, aSize); + + // Restore the position. + mpVerticalScrollBar->SetThumbPos(static_cast<::tools::Long>(nThumbPosition)); + mnVerticalPosition = nThumbPosition / double(mpVerticalScrollBar->GetRange().Len()); +} + +void ScrollBarManager::PlaceFiller (const ::tools::Rectangle& aArea) +{ + mpScrollBarFiller->SetPosSizePixel( + Point( + aArea.Right()-mpVerticalScrollBar->GetSizePixel().Width()+1, + aArea.Bottom()-mpHorizontalScrollBar->GetSizePixel().Height()+1), + Size ( + mpVerticalScrollBar->GetSizePixel().Width(), + mpHorizontalScrollBar->GetSizePixel().Height())); +} + +void ScrollBarManager::UpdateScrollBars(bool bUseScrolling) +{ + ::tools::Rectangle aModelArea (mrSlideSorter.GetView().GetModelArea()); + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + Size aWindowModelSize (pWindow->PixelToLogic(pWindow->GetSizePixel())); + + // The horizontal scroll bar is only shown when the window is + // horizontally smaller than the view. + if (mpHorizontalScrollBar != nullptr && mpHorizontalScrollBar->IsVisible()) + { + mpHorizontalScrollBar->Show(); + mpHorizontalScrollBar->SetRange ( + Range(aModelArea.Left(), aModelArea.Right())); + mnHorizontalPosition = + double(mpHorizontalScrollBar->GetThumbPos()) + / double(mpHorizontalScrollBar->GetRange().Len()); + + mpHorizontalScrollBar->SetVisibleSize (aWindowModelSize.Width()); + + const ::tools::Long nWidth (mpContentWindow->PixelToLogic( + mpContentWindow->GetSizePixel()).Width()); + // Make the line size about 10% of the visible width. + mpHorizontalScrollBar->SetLineSize (nWidth / 10); + // Make the page size about 90% of the visible width. + mpHorizontalScrollBar->SetPageSize ((nWidth * 9) / 10); + } + else + { + mnHorizontalPosition = 0; + } + + // The vertical scroll bar is always shown. + if (mpVerticalScrollBar != nullptr && mpVerticalScrollBar->IsVisible()) + { + mpVerticalScrollBar->SetRange ( + Range(aModelArea.Top(), aModelArea.Bottom())); + mnVerticalPosition = + double(mpVerticalScrollBar->GetThumbPos()) + / double(mpVerticalScrollBar->GetRange().Len()); + + mpVerticalScrollBar->SetVisibleSize (aWindowModelSize.Height()); + + const ::tools::Long nHeight (mpContentWindow->PixelToLogic( + mpContentWindow->GetSizePixel()).Height()); + // Make the line size about 10% of the visible height. + mpVerticalScrollBar->SetLineSize (nHeight / 10); + // Make the page size about 90% of the visible height. + mpVerticalScrollBar->SetPageSize ((nHeight * 9) / 10); + } + else + { + mnVerticalPosition = 0; + } + + double nEps (::std::numeric_limits<double>::epsilon()); + if (fabs(mnHorizontalPosition-pWindow->GetVisibleX()) > nEps + || fabs(mnVerticalPosition-pWindow->GetVisibleY()) > nEps) + { + mrSlideSorter.GetView().InvalidatePageObjectVisibilities(); + if (bUseScrolling) + pWindow->SetVisibleXY(mnHorizontalPosition, mnVerticalPosition); + else + SetWindowOrigin(mnHorizontalPosition, mnVerticalPosition); + } +} + +IMPL_LINK(ScrollBarManager, VerticalScrollBarHandler, ScrollBar*, pScrollBar, void) +{ + if (pScrollBar!=nullptr + && pScrollBar==mpVerticalScrollBar.get() + && pScrollBar->IsVisible() + && mrSlideSorter.GetContentWindow()) + { + double nRelativePosition = double(pScrollBar->GetThumbPos()) + / double(pScrollBar->GetRange().Len()); + mrSlideSorter.GetView().InvalidatePageObjectVisibilities(); + mrSlideSorter.GetContentWindow()->SetVisibleXY(-1, nRelativePosition); + mrSlideSorter.GetController().GetVisibleAreaManager().DeactivateCurrentSlideTracking(); + } +} + +IMPL_LINK(ScrollBarManager, HorizontalScrollBarHandler, ScrollBar*, pScrollBar, void) +{ + if (pScrollBar!=nullptr + && pScrollBar==mpHorizontalScrollBar.get() + && pScrollBar->IsVisible() + && mrSlideSorter.GetContentWindow()) + { + double nRelativePosition = double(pScrollBar->GetThumbPos()) + / double(pScrollBar->GetRange().Len()); + mrSlideSorter.GetView().InvalidatePageObjectVisibilities(); + mrSlideSorter.GetContentWindow()->SetVisibleXY(nRelativePosition, -1); + mrSlideSorter.GetController().GetVisibleAreaManager().DeactivateCurrentSlideTracking(); + } +} + +void ScrollBarManager::SetWindowOrigin ( + double nHorizontalPosition, + double nVerticalPosition) +{ + mnHorizontalPosition = nHorizontalPosition; + mnVerticalPosition = nVerticalPosition; + + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + Size aViewSize (pWindow->GetViewSize()); + Point aOrigin ( + static_cast<::tools::Long>(mnHorizontalPosition * aViewSize.Width()), + static_cast<::tools::Long>(mnVerticalPosition * aViewSize.Height())); + + pWindow->SetWinViewPos (aOrigin); + pWindow->UpdateMapMode (); + pWindow->Invalidate (); +} + +/** Determining the visibility of the scroll bars is quite complicated. The + visibility of one influences that of the other because showing a scroll + bar makes the available space smaller and may lead to the need of + displaying the other. + To solve this we test all four combinations of showing or hiding each + scroll bar and use the best one. The best one is that combination that + a) shows the least number of scroll bars with preference of showing the + vertical over showing the horizontal and + b) when not showing a scroll bar the area used by the page objects fits + into the available area in the scroll bars orientation. +*/ +::tools::Rectangle ScrollBarManager::DetermineScrollBarVisibilities ( + const ::tools::Rectangle& rAvailableArea, + const bool bIsHorizontalScrollBarAllowed, + const bool bIsVerticalScrollBarAllowed) +{ + // Test which combination of scroll bars is the best. + bool bShowHorizontal = false; + bool bShowVertical = false; + if (mrSlideSorter.GetModel().GetPageCount() == 0) + { + // No pages => no scroll bars. + } + else if (TestScrollBarVisibilities(false, false, rAvailableArea)) + { + // Nothing to be done. + } + else if (bIsHorizontalScrollBarAllowed + && TestScrollBarVisibilities(true, false, rAvailableArea)) + { + bShowHorizontal = true; + } + else if (bIsVerticalScrollBarAllowed + && TestScrollBarVisibilities(false, true, rAvailableArea)) + { + bShowVertical = true; + } + else + { + bShowHorizontal = true; + bShowVertical = true; + } + + // Make the visibility of the scroll bars permanent. + mpVerticalScrollBar->Show(bShowVertical); + mpHorizontalScrollBar->Show(bShowHorizontal); + mpScrollBarFiller->Show(bShowVertical && bShowHorizontal); + + // Adapt the remaining space accordingly. + ::tools::Rectangle aRemainingSpace (rAvailableArea); + if (bShowVertical) + aRemainingSpace.AdjustRight( -(mpVerticalScrollBar->GetSizePixel().Width()) ); + if (bShowHorizontal) + aRemainingSpace.AdjustBottom( -(mpHorizontalScrollBar->GetSizePixel().Height()) ); + + return aRemainingSpace; +} + +bool ScrollBarManager::TestScrollBarVisibilities ( + bool bHorizontalScrollBarVisible, + bool bVerticalScrollBarVisible, + const ::tools::Rectangle& rAvailableArea) +{ + model::SlideSorterModel& rModel (mrSlideSorter.GetModel()); + + // Adapt the available size by subtracting the sizes of the scroll bars + // visible in this combination. + Size aBrowserSize (rAvailableArea.GetSize()); + if (bHorizontalScrollBarVisible) + aBrowserSize.AdjustHeight( -(mpHorizontalScrollBar->GetSizePixel().Height()) ); + if (bVerticalScrollBarVisible) + aBrowserSize.AdjustWidth( -(mpVerticalScrollBar->GetSizePixel().Width()) ); + + // Tell the view to rearrange its page objects and check whether the + // page objects can be shown without clipping. + bool bRearrangeSuccess (mrSlideSorter.GetView().GetLayouter().Rearrange ( + mrSlideSorter.GetView().GetOrientation(), + aBrowserSize, + rModel.GetPageDescriptor(0)->GetPage()->GetSize(), + rModel.GetPageCount())); + + if (bRearrangeSuccess) + { + Size aPageSize = mrSlideSorter.GetView().GetLayouter().GetTotalBoundingBox().GetSize(); + Size aWindowModelSize = mpContentWindow->PixelToLogic(aBrowserSize); + + // The content may be clipped, i.e. not fully visible, in one + // direction only when the scroll bar is visible in that direction. + if (aPageSize.Width() > aWindowModelSize.Width()) + if ( ! bHorizontalScrollBarVisible) + return false; + if (aPageSize.Height() > aWindowModelSize.Height()) + if ( ! bVerticalScrollBarVisible) + return false; + + return true; + } + else + return false; +} + +void ScrollBarManager::SetTopLeft(const Point& rNewTopLeft) +{ + if (( ! mpVerticalScrollBar + || mpVerticalScrollBar->GetThumbPos() == rNewTopLeft.Y()) + && ( ! mpHorizontalScrollBar + || mpHorizontalScrollBar->GetThumbPos() == rNewTopLeft.X())) + return; + + // Flush pending repaints before scrolling to avoid temporary artifacts. + mrSlideSorter.GetContentWindow()->PaintImmediately(); + + if (mpVerticalScrollBar) + { + mpVerticalScrollBar->SetThumbPos(rNewTopLeft.Y()); + mnVerticalPosition = rNewTopLeft.Y() / double(mpVerticalScrollBar->GetRange().Len()); + } + if (mpHorizontalScrollBar) + { + mpHorizontalScrollBar->SetThumbPos(rNewTopLeft.X()); + mnHorizontalPosition = rNewTopLeft.X() / double(mpHorizontalScrollBar->GetRange().Len()); + } + + mrSlideSorter.GetContentWindow()->SetVisibleXY(mnHorizontalPosition, mnVerticalPosition); + mrSlideSorter.GetView().InvalidatePageObjectVisibilities(); +} + +int ScrollBarManager::GetVerticalScrollBarWidth() const +{ + if (mpVerticalScrollBar != nullptr && mpVerticalScrollBar->IsVisible()) + return mpVerticalScrollBar->GetSizePixel().Width(); + else + return 0; +} + +int ScrollBarManager::GetHorizontalScrollBarHeight() const +{ + if (mpHorizontalScrollBar != nullptr && mpHorizontalScrollBar->IsVisible()) + return mpHorizontalScrollBar->GetSizePixel().Height(); + else + return 0; +} + +void ScrollBarManager::CalcAutoScrollOffset (const Point& rMouseWindowPosition) +{ + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + + int nDx = 0; + int nDy = 0; + + Size aWindowSize = pWindow->GetOutputSizePixel(); + ::tools::Rectangle aWindowArea (pWindow->GetPosPixel(), aWindowSize); + ::tools::Rectangle aViewPixelArea ( + pWindow->LogicToPixel(mrSlideSorter.GetView().GetModelArea())); + + if (aWindowSize.Width() > maScrollBorder.Width() * 3 + && mpHorizontalScrollBar != nullptr + && mpHorizontalScrollBar->IsVisible()) + { + if (rMouseWindowPosition.X() < maScrollBorder.Width() + && aWindowArea.Left() > aViewPixelArea.Left()) + { + nDx = -1 + static_cast<int>(gnHorizontalScrollFactor + * (rMouseWindowPosition.X() - maScrollBorder.Width())); + } + + if (rMouseWindowPosition.X() >= (aWindowSize.Width() - maScrollBorder.Width()) + && aWindowArea.Right() < aViewPixelArea.Right()) + { + nDx = 1 + static_cast<int>(gnHorizontalScrollFactor + * (rMouseWindowPosition.X() - aWindowSize.Width() + + maScrollBorder.Width())); + } + } + + if (aWindowSize.Height() > maScrollBorder.Height() * 3 + && aWindowSize.Height() < aViewPixelArea.GetHeight()) + { + if (rMouseWindowPosition.Y() < maScrollBorder.Height() + && aWindowArea.Top() > aViewPixelArea.Top()) + { + nDy = -1 + static_cast<int>(gnVerticalScrollFactor + * (rMouseWindowPosition.Y() - maScrollBorder.Height())); + } + + if (rMouseWindowPosition.Y() >= (aWindowSize.Height() - maScrollBorder.Height()) + && aWindowArea.Bottom() < aViewPixelArea.Bottom()) + { + nDy = 1 + static_cast<int>(gnVerticalScrollFactor + * (rMouseWindowPosition.Y() - aWindowSize.Height() + + maScrollBorder.Height())); + } + } + + maAutoScrollOffset = Size(nDx,nDy); +} + +bool ScrollBarManager::AutoScroll ( + const Point& rMouseWindowPosition, + const ::std::function<void ()>& rAutoScrollFunctor) +{ + maAutoScrollFunctor = rAutoScrollFunctor; + CalcAutoScrollOffset(rMouseWindowPosition); + bool bResult (true); + if ( ! mbIsAutoScrollActive) + bResult = RepeatAutoScroll(); + + return bResult; +} + +void ScrollBarManager::StopAutoScroll() +{ + maAutoScrollTimer.Stop(); + mbIsAutoScrollActive = false; +} + +bool ScrollBarManager::RepeatAutoScroll() +{ + if (maAutoScrollOffset != Size(0,0)) + { + if (mrSlideSorter.GetViewShell() != nullptr) + { + mrSlideSorter.GetViewShell()->Scroll( + maAutoScrollOffset.Width(), + maAutoScrollOffset.Height()); + mrSlideSorter.GetView().InvalidatePageObjectVisibilities(); + + if (maAutoScrollFunctor) + maAutoScrollFunctor(); + + mbIsAutoScrollActive = true; + maAutoScrollTimer.Start(); + + return true; + } + } + + clearAutoScrollFunctor(); + mbIsAutoScrollActive = false; + return false; +} + +void ScrollBarManager::clearAutoScrollFunctor() +{ + maAutoScrollFunctor = ::std::function<void ()>(); +} + +IMPL_LINK_NOARG(ScrollBarManager, AutoScrollTimeoutHandler, Timer *, void) +{ + RepeatAutoScroll(); +} + +void ScrollBarManager::Scroll( + const Orientation eOrientation, + const sal_Int32 nDistance) +{ + bool bIsVertical (false); + switch (eOrientation) + { + case Orientation_Horizontal: bIsVertical = false; break; + case Orientation_Vertical: bIsVertical = true; break; + default: + OSL_ASSERT(eOrientation==Orientation_Horizontal || eOrientation==Orientation_Vertical); + return; + } + + Point aNewTopLeft ( + mpHorizontalScrollBar ? mpHorizontalScrollBar->GetThumbPos() : 0, + mpVerticalScrollBar ? mpVerticalScrollBar->GetThumbPos() : 0); + + view::Layouter& rLayouter (mrSlideSorter.GetView().GetLayouter()); + + // Calculate estimate of new location. + if (bIsVertical) + aNewTopLeft.AdjustY(nDistance * rLayouter.GetPageObjectSize().Height() ); + else + aNewTopLeft.AdjustX(nDistance * rLayouter.GetPageObjectSize().Width() ); + + // Adapt location to show whole slides. + if (bIsVertical) + if (nDistance > 0) + { + const sal_Int32 nIndex (rLayouter.GetIndexAtPoint( + Point(aNewTopLeft.X(), aNewTopLeft.Y()+mpVerticalScrollBar->GetVisibleSize()), + true)); + aNewTopLeft.setY( rLayouter.GetPageObjectBox(nIndex,true).Bottom() + - mpVerticalScrollBar->GetVisibleSize() ); + } + else + { + const sal_Int32 nIndex (rLayouter.GetIndexAtPoint( + Point(aNewTopLeft.X(), aNewTopLeft.Y()), + true)); + aNewTopLeft.setY( rLayouter.GetPageObjectBox(nIndex,true).Top() ); + } + else + if (nDistance > 0) + { + const sal_Int32 nIndex (rLayouter.GetIndexAtPoint( + Point(aNewTopLeft.X()+mpVerticalScrollBar->GetVisibleSize(), aNewTopLeft.Y()), + true)); + aNewTopLeft.setX( rLayouter.GetPageObjectBox(nIndex,true).Right() + - mpVerticalScrollBar->GetVisibleSize() ); + } + else + { + const sal_Int32 nIndex (rLayouter.GetIndexAtPoint( + Point(aNewTopLeft.X(), aNewTopLeft.Y()), + true)); + aNewTopLeft.setX( rLayouter.GetPageObjectBox(nIndex,true).Left() ); + } + + mrSlideSorter.GetController().GetVisibleAreaManager().DeactivateCurrentSlideTracking(); + SetTopLeft(aNewTopLeft); +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsSelectionFunction.cxx b/sd/source/ui/slidesorter/controller/SlsSelectionFunction.cxx new file mode 100644 index 000000000..c710a4c1b --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsSelectionFunction.cxx @@ -0,0 +1,1485 @@ +/* -*- 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 <memory> +#include <sal/config.h> + +#include <controller/SlsSelectionFunction.hxx> + +#include <SlideSorter.hxx> +#include <SlideSorterViewShell.hxx> +#include "SlsDragAndDropContext.hxx" +#include <controller/SlideSorterController.hxx> +#include <controller/SlsPageSelector.hxx> +#include <controller/SlsFocusManager.hxx> +#include <controller/SlsScrollBarManager.hxx> +#include <controller/SlsClipboard.hxx> +#include <controller/SlsCurrentSlideManager.hxx> +#include <controller/SlsInsertionIndicatorHandler.hxx> +#include <controller/SlsSelectionManager.hxx> +#include <controller/SlsProperties.hxx> +#include <controller/SlsVisibleAreaManager.hxx> +#include <model/SlideSorterModel.hxx> +#include <model/SlsPageDescriptor.hxx> +#include <model/SlsPageEnumerationProvider.hxx> +#include <view/SlideSorterView.hxx> +#include <view/SlsLayouter.hxx> +#include <framework/FrameworkHelper.hxx> +#include <osl/diagnose.h> +#include <Window.hxx> +#include <sdpage.hxx> +#include <drawdoc.hxx> +#include <sdxfer.hxx> +#include <ViewShell.hxx> +#include <FrameView.hxx> +#include <app.hrc> +#include <o3tl/deleter.hxx> +#include <sfx2/dispatch.hxx> +#include <vcl/ptrstyle.hxx> +#include <optional> +#include <sdmod.hxx> + +namespace { +const sal_uInt32 SINGLE_CLICK (0x00000001); +const sal_uInt32 DOUBLE_CLICK (0x00000002); +const sal_uInt32 LEFT_BUTTON (0x00000010); +const sal_uInt32 RIGHT_BUTTON (0x00000020); +const sal_uInt32 MIDDLE_BUTTON (0x00000040); +const sal_uInt32 BUTTON_DOWN (0x00000100); +const sal_uInt32 BUTTON_UP (0x00000200); +const sal_uInt32 MOUSE_MOTION (0x00000400); +const sal_uInt32 MOUSE_DRAG (0x00000800); +// The rest leaves the lower 16 bit untouched so that it can be used with +// key codes. +const sal_uInt32 OVER_SELECTED_PAGE (0x00010000); +const sal_uInt32 OVER_UNSELECTED_PAGE (0x00020000); +const sal_uInt32 SHIFT_MODIFIER (0x00200000); +const sal_uInt32 CONTROL_MODIFIER (0x00400000); + +// Some absent events are defined so they can be expressed explicitly. +const sal_uInt32 NO_MODIFIER (0x00000000); +const sal_uInt32 NOT_OVER_PAGE (0x00000000); + +// Masks +const sal_uInt32 MODIFIER_MASK (SHIFT_MODIFIER | CONTROL_MODIFIER); + +} // end of anonymous namespace + +// Define some macros to make the following switch statement more readable. +#define ANY_MODIFIER(code) \ + code|NO_MODIFIER: \ + case code|SHIFT_MODIFIER: \ + case code|CONTROL_MODIFIER + +namespace sd::slidesorter::controller { + +//===== SelectionFunction::EventDescriptor ==================================== + +class SelectionFunction::EventDescriptor +{ +public: + Point maMousePosition; + Point maMouseModelPosition; + model::SharedPageDescriptor mpHitDescriptor; + SdrPage* mpHitPage; + sal_uInt32 mnEventCode; + InsertionIndicatorHandler::Mode meDragMode; + bool mbIsLeaving; + + EventDescriptor ( + sal_uInt32 nEventType, + const MouseEvent& rEvent, + SlideSorter const & rSlideSorter); + EventDescriptor ( + sal_uInt32 nEventType, + const AcceptDropEvent& rEvent, + const sal_Int8 nDragAction, + SlideSorter const & rSlideSorter); + +private: + /** Compute a numerical code that describes a mouse event and that can + be used for fast look up of the appropriate reaction. + */ + sal_uInt32 EncodeMouseEvent (const MouseEvent& rEvent) const; + + /** Compute a numerical code that describes the current state like + whether the selection rectangle is visible or whether the page under + the mouse or the one that has the focus is selected. + */ + sal_uInt32 EncodeState() const; +}; + +//===== SelectionFunction::ModeHandler ======================================== + +class SelectionFunction::ModeHandler +{ +public: + ModeHandler ( + SlideSorter& rSlideSorter, + SelectionFunction& rSelectionFunction, + const bool bIsMouseOverIndicatorAllowed); + virtual ~ModeHandler() COVERITY_NOEXCEPT_FALSE; + + virtual Mode GetMode() const = 0; + virtual void Abort() = 0; + virtual void ProcessEvent (EventDescriptor& rDescriptor); + + /** Set the selection to exactly the specified page and also set it as + the current page. + */ + void SetCurrentPage (const model::SharedPageDescriptor& rpDescriptor); + + /// Deselect all pages. + void DeselectAllPages(); + void SelectOnePage (const model::SharedPageDescriptor& rpDescriptor); + + /** When the view on which this selection function is working is the + main view then the view is switched to the regular editing view. + */ + void SwitchView (const model::SharedPageDescriptor& rpDescriptor); + + void StartDrag ( + const Point& rMousePosition); + + bool IsMouseOverIndicatorAllowed() const { return mbIsMouseOverIndicatorAllowed;} + +protected: + SlideSorter& mrSlideSorter; + SelectionFunction& mrSelectionFunction; + + virtual bool ProcessButtonDownEvent (EventDescriptor& rDescriptor); + virtual bool ProcessButtonUpEvent (EventDescriptor& rDescriptor); + virtual bool ProcessMotionEvent (EventDescriptor& rDescriptor); + virtual bool ProcessDragEvent (EventDescriptor& rDescriptor); + virtual bool HandleUnprocessedEvent (EventDescriptor& rDescriptor); + + void ReprocessEvent (EventDescriptor& rDescriptor); + +private: + const bool mbIsMouseOverIndicatorAllowed; +}; + +namespace { + +/** This is the default handler for processing events. It activates the + multi selection or drag-and-drop when the right conditions are met. +*/ +class NormalModeHandler : public SelectionFunction::ModeHandler +{ +public: + NormalModeHandler ( + SlideSorter& rSlideSorter, + SelectionFunction& rSelectionFunction); + + virtual SelectionFunction::Mode GetMode() const override; + virtual void Abort() override; + + void ResetButtonDownLocation(); + +protected: + virtual bool ProcessButtonDownEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + virtual bool ProcessButtonUpEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + virtual bool ProcessMotionEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + virtual bool ProcessDragEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + +private: + ::std::optional<Point> maButtonDownLocation; + + /** Select all pages between and including the selection anchor and the + specified page. + */ + void RangeSelect (const model::SharedPageDescriptor& rpDescriptor); +}; + +/** Handle events during a multi selection, which typically is started by + pressing the left mouse button when not over a page. +*/ +class MultiSelectionModeHandler : public SelectionFunction::ModeHandler +{ +public: + /** Start a rectangle selection at the given position. + */ + MultiSelectionModeHandler ( + SlideSorter& rSlideSorter, + SelectionFunction& rSelectionFunction, + const Point& rMouseModelPosition, + const sal_uInt32 nEventCode); + + virtual ~MultiSelectionModeHandler() override; + + virtual SelectionFunction::Mode GetMode() const override; + virtual void Abort() override; + virtual void ProcessEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + + enum SelectionMode { SM_Normal, SM_Add, SM_Toggle }; + + void SetSelectionMode (const SelectionMode eSelectionMode); + void SetSelectionModeFromModifier (const sal_uInt32 nEventCode); + +protected: + virtual bool ProcessButtonUpEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + virtual bool ProcessMotionEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + virtual bool HandleUnprocessedEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + +private: + SelectionMode meSelectionMode; + Point maSecondCorner; + PointerStyle maSavedPointer; + bool mbAutoScrollInstalled; + sal_Int32 mnAnchorIndex; + sal_Int32 mnSecondIndex; + + void UpdateModelPosition (const Point& rMouseModelPosition); + void UpdateSelection(); + + /** Update the rectangle selection so that the given position becomes + the new second point of the selection rectangle. + */ + void UpdatePosition ( + const Point& rMousePosition, + const bool bAllowAutoScroll); + + void UpdateSelectionState ( + const model::SharedPageDescriptor& rpDescriptor, + const bool bIsInSelection) const; +}; + +/** Handle events during drag-and-drop. +*/ +class DragAndDropModeHandler : public SelectionFunction::ModeHandler +{ +public: + DragAndDropModeHandler ( + SlideSorter& rSlideSorter, + SelectionFunction& rSelectionFunction, + const Point& rMousePosition, + vcl::Window* pWindow); + virtual ~DragAndDropModeHandler() override; + + virtual SelectionFunction::Mode GetMode() const override; + virtual void Abort() override; + +protected: + virtual bool ProcessButtonUpEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + virtual bool ProcessDragEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + +private: + std::unique_ptr<DragAndDropContext, o3tl::default_delete<DragAndDropContext>> mpDragAndDropContext; +}; + +} + +//===== SelectionFunction ===================================================== + + +SelectionFunction::SelectionFunction ( + SlideSorter& rSlideSorter, + SfxRequest& rRequest) + : FuPoor ( + rSlideSorter.GetViewShell(), + rSlideSorter.GetContentWindow(), + &rSlideSorter.GetView(), + rSlideSorter.GetModel().GetDocument(), + rRequest), + mrSlideSorter(rSlideSorter), + mrController(mrSlideSorter.GetController()), + mnShiftKeySelectionAnchor(-1), + mpModeHandler(std::make_shared<NormalModeHandler>(rSlideSorter, *this)) +{ +} + +SelectionFunction::~SelectionFunction() +{ + mpModeHandler.reset(); +} + +rtl::Reference<FuPoor> SelectionFunction::Create( + SlideSorter& rSlideSorter, + SfxRequest& rRequest) +{ + rtl::Reference<FuPoor> xFunc( new SelectionFunction( rSlideSorter, rRequest ) ); + return xFunc; +} + +bool SelectionFunction::MouseButtonDown (const MouseEvent& rEvent) +{ + // remember button state for creation of own MouseEvents + SetMouseButtonCode (rEvent.GetButtons()); + aMDPos = rEvent.GetPosPixel(); + + // mpWindow->CaptureMouse(); + + ProcessMouseEvent(BUTTON_DOWN, rEvent); + + return true; +} + +bool SelectionFunction::MouseMove (const MouseEvent& rEvent) +{ + ProcessMouseEvent(MOUSE_MOTION, rEvent); + return true; +} + +bool SelectionFunction::MouseButtonUp (const MouseEvent& rEvent) +{ + mrController.GetScrollBarManager().StopAutoScroll (); + + ProcessMouseEvent(BUTTON_UP, rEvent); + + return true; +} + +void SelectionFunction::NotifyDragFinished() +{ + SwitchToNormalMode(); +} + +bool SelectionFunction::KeyInput (const KeyEvent& rEvent) +{ + view::SlideSorterView::DrawLock aDrawLock (mrSlideSorter); + PageSelector::BroadcastLock aBroadcastLock (mrSlideSorter); + PageSelector::UpdateLock aLock (mrSlideSorter); + FocusManager& rFocusManager (mrController.GetFocusManager()); + bool bResult = false; + + const vcl::KeyCode& rCode (rEvent.GetKeyCode()); + switch (rCode.GetCode()) + { + case KEY_RETURN: + { + model::SharedPageDescriptor pDescriptor (rFocusManager.GetFocusedPageDescriptor()); + ViewShell* pViewShell = mrSlideSorter.GetViewShell(); + if (rFocusManager.HasFocus() && pDescriptor && pViewShell!=nullptr) + { + // The Return key triggers different functions depending on + // whether the slide sorter is the main view or displayed in + // the right pane. + if (pViewShell->IsMainViewShell()) + { + mpModeHandler->SetCurrentPage(pDescriptor); + mpModeHandler->SwitchView(pDescriptor); + } + else if (pViewShell->GetDispatcher() != nullptr) + { + pViewShell->GetDispatcher()->Execute( + SID_INSERTPAGE, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + } + bResult = true; + } + break; + } + + case KEY_TAB: + if ( ! rFocusManager.IsFocusShowing()) + { + rFocusManager.ShowFocus(); + bResult = true; + } + break; + + case KEY_ESCAPE: + // When there is an active multiselection or drag-and-drop + // operation then stop that. + mpModeHandler->Abort(); + SwitchToNormalMode(); + bResult = true; + break; + + case KEY_SPACE: + { + // Toggle the selection state. + model::SharedPageDescriptor pDescriptor (rFocusManager.GetFocusedPageDescriptor()); + if (pDescriptor && rCode.IsMod1()) + { + if (pDescriptor->HasState(model::PageDescriptor::ST_Selected)) + mrController.GetPageSelector().DeselectPage(pDescriptor, false); + else + mrController.GetPageSelector().SelectPage(pDescriptor); + } + bResult = true; + } + break; + + // Move the focus indicator left. + case KEY_LEFT: + MoveFocus(FocusManager::FocusMoveDirection::Left, rCode.IsShift(), rCode.IsMod1()); + bResult = true; + break; + + // Move the focus indicator right. + case KEY_RIGHT: + MoveFocus(FocusManager::FocusMoveDirection::Right, rCode.IsShift(), rCode.IsMod1()); + bResult = true; + break; + + // Move the focus indicator up. + case KEY_UP: + MoveFocus(FocusManager::FocusMoveDirection::Up, rCode.IsShift(), rCode.IsMod1()); + bResult = true; + break; + + // Move the focus indicator down. + case KEY_DOWN: + MoveFocus(FocusManager::FocusMoveDirection::Down, rCode.IsShift(), rCode.IsMod1()); + bResult = true; + break; + + // Go to previous page. No wrap around. + case KEY_PAGEUP: + GotoNextPage(-1); + bResult = true; + break; + + // Go to next page. No wrap around... + case KEY_PAGEDOWN: + GotoNextPage(+1); + bResult = true; + break; + + case KEY_HOME: + GotoPage(0); + bResult = true; + break; + + case KEY_END: + GotoPage(mrSlideSorter.GetModel().GetPageCount()-1); + bResult = true; + break; + + case KEY_DELETE: + case KEY_BACKSPACE: + { + if (mrSlideSorter.GetProperties()->IsUIReadOnly()) + break; + + mrController.GetSelectionManager()->DeleteSelectedPages(rCode.GetCode()==KEY_DELETE); + + mnShiftKeySelectionAnchor = -1; + bResult = true; + } + break; + + case KEY_F10: + if (rCode.IsShift()) + { + mpModeHandler->SelectOnePage( + mrSlideSorter.GetController().GetFocusManager().GetFocusedPageDescriptor()); + } + break; + + default: + break; + } + + if ( ! bResult) + bResult = FuPoor::KeyInput(rEvent); + + return bResult; +} + +void SelectionFunction::MoveFocus ( + const FocusManager::FocusMoveDirection eDirection, + const bool bIsShiftDown, + const bool bIsControlDown) +{ + // Remember the anchor of shift key multi selection. + if (bIsShiftDown) + { + if (mnShiftKeySelectionAnchor<0) + { + model::SharedPageDescriptor pFocusedDescriptor ( + mrController.GetFocusManager().GetFocusedPageDescriptor()); + mnShiftKeySelectionAnchor = pFocusedDescriptor->GetPageIndex(); + } + } + else if ( ! bIsControlDown) + ResetShiftKeySelectionAnchor(); + + mrController.GetFocusManager().MoveFocus(eDirection); + + PageSelector& rSelector (mrController.GetPageSelector()); + model::SharedPageDescriptor pFocusedDescriptor ( + mrController.GetFocusManager().GetFocusedPageDescriptor()); + if (bIsShiftDown) + { + // When shift is pressed then select all pages in the range between + // the currently and the previously focused pages, including them. + if (pFocusedDescriptor) + { + sal_Int32 nPageRangeEnd (pFocusedDescriptor->GetPageIndex()); + model::PageEnumeration aPages ( + model::PageEnumerationProvider::CreateAllPagesEnumeration( + mrSlideSorter.GetModel())); + while (aPages.HasMoreElements()) + { + model::SharedPageDescriptor pDescriptor (aPages.GetNextElement()); + if (pDescriptor) + { + const sal_Int32 nPageIndex(pDescriptor->GetPageIndex()); + if ((nPageIndex>=mnShiftKeySelectionAnchor && nPageIndex<=nPageRangeEnd) + || (nPageIndex<=mnShiftKeySelectionAnchor && nPageIndex>=nPageRangeEnd)) + { + rSelector.SelectPage(pDescriptor); + } + else + { + rSelector.DeselectPage(pDescriptor); + } + } + } + } + } + else if (bIsControlDown) + { + // When control is pressed then do not alter the selection or the + // current page, just move the focus. + } + else + { + // Without shift just select the focused page. + mpModeHandler->SelectOnePage(pFocusedDescriptor); + } +} + +void SelectionFunction::DoCut() +{ + if ( ! mrSlideSorter.GetProperties()->IsUIReadOnly()) + { + mrController.GetClipboard().DoCut(); + } +} + +void SelectionFunction::DoCopy() +{ + mrController.GetClipboard().DoCopy(); +} + +void SelectionFunction::DoPaste() +{ + if ( ! mrSlideSorter.GetProperties()->IsUIReadOnly()) + { + mrController.GetClipboard().DoPaste(); + } +} + +bool SelectionFunction::cancel() +{ + mrController.GetFocusManager().ToggleFocus(); + return true; +} + +void SelectionFunction::GotoNextPage (int nOffset) +{ + model::SharedPageDescriptor pDescriptor + = mrController.GetCurrentSlideManager()->GetCurrentSlide(); + if (pDescriptor) + { + SdPage* pPage = pDescriptor->GetPage(); + OSL_ASSERT(pPage!=nullptr); + sal_Int32 nIndex = (pPage->GetPageNum()-1) / 2; + GotoPage(nIndex + nOffset); + } + ResetShiftKeySelectionAnchor(); +} + +void SelectionFunction::GotoPage (int nIndex) +{ + sal_uInt16 nPageCount = static_cast<sal_uInt16>(mrSlideSorter.GetModel().GetPageCount()); + + if (nIndex >= nPageCount) + nIndex = nPageCount - 1; + if (nIndex < 0) + nIndex = 0; + + mrController.GetFocusManager().SetFocusedPage(nIndex); + model::SharedPageDescriptor pNextPageDescriptor ( + mrSlideSorter.GetModel().GetPageDescriptor (nIndex)); + if (pNextPageDescriptor) + mpModeHandler->SetCurrentPage(pNextPageDescriptor); + else + { + OSL_ASSERT(pNextPageDescriptor); + } + ResetShiftKeySelectionAnchor(); +} + +void SelectionFunction::ProcessMouseEvent (sal_uInt32 nEventType, const MouseEvent& rEvent) +{ + // #95491# remember button state for creation of own MouseEvents + SetMouseButtonCode (rEvent.GetButtons()); + + EventDescriptor aEventDescriptor (nEventType, rEvent, mrSlideSorter); + ProcessEvent(aEventDescriptor); +} + +void SelectionFunction::MouseDragged ( + const AcceptDropEvent& rEvent, + const sal_Int8 nDragAction) +{ + EventDescriptor aEventDescriptor (MOUSE_DRAG, rEvent, nDragAction, mrSlideSorter); + ProcessEvent(aEventDescriptor); +} + +void SelectionFunction::ProcessEvent (EventDescriptor& rDescriptor) +{ + // The call to ProcessEvent may switch to another mode handler. + // Prevent the untimely destruction of the called handler by acquiring a + // temporary reference here. + std::shared_ptr<ModeHandler> pModeHandler (mpModeHandler); + pModeHandler->ProcessEvent(rDescriptor); +} + +static bool Match ( + const sal_uInt32 nEventCode, + const sal_uInt32 nPositivePattern) +{ + return (nEventCode & nPositivePattern)==nPositivePattern; +} + +void SelectionFunction::SwitchToNormalMode() +{ + if (mpModeHandler->GetMode() != NormalMode) + SwitchMode(std::make_shared<NormalModeHandler>(mrSlideSorter, *this)); +} + +void SelectionFunction::SwitchToDragAndDropMode (const Point& rMousePosition) +{ + if (mpModeHandler->GetMode() == DragAndDropMode) + return; + + SwitchMode(std::make_shared<DragAndDropModeHandler>(mrSlideSorter, *this, rMousePosition, mpWindow)); +} + +void SelectionFunction::SwitchToMultiSelectionMode ( + const Point& rMousePosition, + const sal_uInt32 nEventCode) +{ + if (mpModeHandler->GetMode() != MultiSelectionMode) + SwitchMode(std::make_shared<MultiSelectionModeHandler>(mrSlideSorter, *this, rMousePosition, nEventCode)); +} + +void SelectionFunction::SwitchMode (const std::shared_ptr<ModeHandler>& rpHandler) +{ + // Not all modes allow mouse over indicator. + if (mpModeHandler->IsMouseOverIndicatorAllowed() != rpHandler->IsMouseOverIndicatorAllowed()) + { + if ( ! rpHandler->IsMouseOverIndicatorAllowed()) + { + mrSlideSorter.GetView().SetPageUnderMouse(model::SharedPageDescriptor()); + } + else + mrSlideSorter.GetView().UpdatePageUnderMouse(); + } + + mpModeHandler = rpHandler; +} + +void SelectionFunction::ResetShiftKeySelectionAnchor() +{ + mnShiftKeySelectionAnchor = -1; +} + +void SelectionFunction::ResetMouseAnchor() +{ + if (mpModeHandler && mpModeHandler->GetMode() == NormalMode) + { + std::shared_ptr<NormalModeHandler> pHandler ( + std::dynamic_pointer_cast<NormalModeHandler>(mpModeHandler)); + if (pHandler) + pHandler->ResetButtonDownLocation(); + } +} + +//===== EventDescriptor ======================================================= + +SelectionFunction::EventDescriptor::EventDescriptor ( + const sal_uInt32 nEventType, + const MouseEvent& rEvent, + SlideSorter const & rSlideSorter) + : maMousePosition(rEvent.GetPosPixel()), + mpHitPage(), + mnEventCode(nEventType), + meDragMode(InsertionIndicatorHandler::MoveMode), + mbIsLeaving(false) +{ + maMouseModelPosition = rSlideSorter.GetContentWindow()->PixelToLogic(maMousePosition); + mpHitDescriptor = rSlideSorter.GetController().GetPageAt(maMousePosition); + if (mpHitDescriptor) + { + mpHitPage = mpHitDescriptor->GetPage(); + } + + mnEventCode |= EncodeMouseEvent(rEvent); + mnEventCode |= EncodeState(); + + // Detect the mouse leaving the window. When not button is pressed then + // we can call IsLeaveWindow at the event. Otherwise we have to make an + // explicit test. + mbIsLeaving = rEvent.IsLeaveWindow() + || ! ::tools::Rectangle(Point(0,0), + rSlideSorter.GetContentWindow()->GetOutputSizePixel()).Contains(maMousePosition); +} + +SelectionFunction::EventDescriptor::EventDescriptor ( + const sal_uInt32 nEventType, + const AcceptDropEvent& rEvent, + const sal_Int8 nDragAction, + SlideSorter const & rSlideSorter) + : maMousePosition(rEvent.maPosPixel), + mpHitPage(), + mnEventCode(nEventType), + meDragMode(InsertionIndicatorHandler::GetModeFromDndAction(nDragAction)), + mbIsLeaving(false) +{ + maMouseModelPosition = rSlideSorter.GetContentWindow()->PixelToLogic(maMousePosition); + mpHitDescriptor = rSlideSorter.GetController().GetPageAt(maMousePosition); + if (mpHitDescriptor) + { + mpHitPage = mpHitDescriptor->GetPage(); + } + + mnEventCode |= EncodeState(); + + // Detect the mouse leaving the window. When not button is pressed then + // we can call IsLeaveWindow at the event. Otherwise we have to make an + // explicit test. + mbIsLeaving = rEvent.mbLeaving + || ! ::tools::Rectangle(Point(0,0), + rSlideSorter.GetContentWindow()->GetOutputSizePixel()).Contains(maMousePosition); +} + +sal_uInt32 SelectionFunction::EventDescriptor::EncodeMouseEvent ( + const MouseEvent& rEvent) const +{ + // Initialize with the type of mouse event. + sal_uInt32 nEventCode (mnEventCode & (BUTTON_DOWN | BUTTON_UP | MOUSE_MOTION)); + + // Detect the affected button. + switch (rEvent.GetButtons()) + { + case MOUSE_LEFT: nEventCode |= LEFT_BUTTON; break; + case MOUSE_RIGHT: nEventCode |= RIGHT_BUTTON; break; + case MOUSE_MIDDLE: nEventCode |= MIDDLE_BUTTON; break; + } + + // Detect the number of clicks. + switch (rEvent.GetClicks()) + { + case 1: nEventCode |= SINGLE_CLICK; break; + case 2: nEventCode |= DOUBLE_CLICK; break; + } + + // Detect pressed modifier keys. + if (rEvent.IsShift()) + nEventCode |= SHIFT_MODIFIER; + if (rEvent.IsMod1()) + nEventCode |= CONTROL_MODIFIER; + + return nEventCode; +} + +sal_uInt32 SelectionFunction::EventDescriptor::EncodeState() const +{ + sal_uInt32 nEventCode (0); + + // Detect whether the event has happened over a page object. + if (mpHitPage!=nullptr && mpHitDescriptor) + { + if (mpHitDescriptor->HasState(model::PageDescriptor::ST_Selected)) + nEventCode |= OVER_SELECTED_PAGE; + else + nEventCode |= OVER_UNSELECTED_PAGE; + } + + return nEventCode; +} + +//===== SelectionFunction::ModeHandler ======================================== + +SelectionFunction::ModeHandler::ModeHandler ( + SlideSorter& rSlideSorter, + SelectionFunction& rSelectionFunction, + const bool bIsMouseOverIndicatorAllowed) + : mrSlideSorter(rSlideSorter), + mrSelectionFunction(rSelectionFunction), + mbIsMouseOverIndicatorAllowed(bIsMouseOverIndicatorAllowed) +{ +} + +SelectionFunction::ModeHandler::~ModeHandler() COVERITY_NOEXCEPT_FALSE +{ +} + +void SelectionFunction::ModeHandler::ReprocessEvent (EventDescriptor& rDescriptor) +{ + mrSelectionFunction.ProcessEvent(rDescriptor); +} + +void SelectionFunction::ModeHandler::ProcessEvent ( + SelectionFunction::EventDescriptor& rDescriptor) +{ + PageSelector::BroadcastLock aBroadcastLock (mrSlideSorter); + PageSelector::UpdateLock aUpdateLock (mrSlideSorter); + + bool bIsProcessed (false); + switch (rDescriptor.mnEventCode & (BUTTON_DOWN | BUTTON_UP | MOUSE_MOTION | MOUSE_DRAG)) + { + case BUTTON_DOWN: + bIsProcessed = ProcessButtonDownEvent(rDescriptor); + break; + + case BUTTON_UP: + bIsProcessed = ProcessButtonUpEvent(rDescriptor); + break; + + case MOUSE_MOTION: + bIsProcessed = ProcessMotionEvent(rDescriptor); + break; + + case MOUSE_DRAG: + bIsProcessed = ProcessDragEvent(rDescriptor); + break; + } + + if ( ! bIsProcessed) + HandleUnprocessedEvent(rDescriptor); +} + +bool SelectionFunction::ModeHandler::ProcessButtonDownEvent (EventDescriptor&) +{ + return false; +} + +bool SelectionFunction::ModeHandler::ProcessButtonUpEvent (EventDescriptor&) +{ + mrSelectionFunction.SwitchToNormalMode(); + return false; +} + +bool SelectionFunction::ModeHandler::ProcessMotionEvent (EventDescriptor& rDescriptor) +{ + if (mbIsMouseOverIndicatorAllowed) + mrSlideSorter.GetView().UpdatePageUnderMouse(rDescriptor.maMousePosition); + + if (rDescriptor.mbIsLeaving) + { + mrSelectionFunction.SwitchToNormalMode(); + mrSlideSorter.GetView().SetPageUnderMouse(model::SharedPageDescriptor()); + + return true; + } + else + return false; +} + +bool SelectionFunction::ModeHandler::ProcessDragEvent (EventDescriptor&) +{ + return false; +} + +bool SelectionFunction::ModeHandler::HandleUnprocessedEvent (EventDescriptor&) +{ + return false; +} + +void SelectionFunction::ModeHandler::SetCurrentPage ( + const model::SharedPageDescriptor& rpDescriptor) +{ + SelectOnePage(rpDescriptor); + mrSlideSorter.GetController().GetCurrentSlideManager()->SwitchCurrentSlide(rpDescriptor); +} + +void SelectionFunction::ModeHandler::DeselectAllPages() +{ + mrSlideSorter.GetController().GetPageSelector().DeselectAllPages(); + mrSelectionFunction.ResetShiftKeySelectionAnchor(); +} + +void SelectionFunction::ModeHandler::SelectOnePage ( + const model::SharedPageDescriptor& rpDescriptor) +{ + DeselectAllPages(); + mrSlideSorter.GetController().GetPageSelector().SelectPage(rpDescriptor); +} + +void SelectionFunction::ModeHandler::SwitchView (const model::SharedPageDescriptor& rpDescriptor) +{ + // Switch to the draw view. This is done only when the current + // view is the main view. + ViewShell* pViewShell = mrSlideSorter.GetViewShell(); + if (pViewShell==nullptr || !pViewShell->IsMainViewShell()) + return; + + if (rpDescriptor && rpDescriptor->GetPage()!=nullptr) + { + mrSlideSorter.GetModel().GetDocument()->SetSelected(rpDescriptor->GetPage(), true); + pViewShell->GetFrameView()->SetSelectedPage( + (rpDescriptor->GetPage()->GetPageNum()-1)/2); + } + if (mrSlideSorter.GetViewShellBase() != nullptr) + framework::FrameworkHelper::Instance(*mrSlideSorter.GetViewShellBase())->RequestView( + framework::FrameworkHelper::msImpressViewURL, + framework::FrameworkHelper::msCenterPaneURL); +} + +void SelectionFunction::ModeHandler::StartDrag ( + const Point& rMousePosition) +{ + // Do not start a drag-and-drop operation when one is already active. + // (when dragging pages from one document into another, pressing a + // modifier key can trigger a MouseMotion event in the originating + // window (focus still in there). Together with the mouse button pressed + // (drag-and-drop is active) this triggers the start of drag-and-drop.) + if (SD_MOD()->pTransferDrag != nullptr) + return; + + if ( ! mrSlideSorter.GetProperties()->IsUIReadOnly()) + { + mrSelectionFunction.SwitchToDragAndDropMode(rMousePosition); + } +} + +//===== NormalModeHandler ===================================================== + +NormalModeHandler::NormalModeHandler ( + SlideSorter& rSlideSorter, + SelectionFunction& rSelectionFunction) + : ModeHandler(rSlideSorter, rSelectionFunction, true) +{ +} + +SelectionFunction::Mode NormalModeHandler::GetMode() const +{ + return SelectionFunction::NormalMode; +} + +void NormalModeHandler::Abort() +{ +} + +bool NormalModeHandler::ProcessButtonDownEvent ( + SelectionFunction::EventDescriptor& rDescriptor) +{ + // Remember the location where the left button is pressed. With + // that we can filter away motion events that are caused by key + // presses. We also can tune the minimal motion distance that + // triggers a drag-and-drop operation. + if ((rDescriptor.mnEventCode & BUTTON_DOWN) != 0) + maButtonDownLocation = rDescriptor.maMousePosition; + + switch (rDescriptor.mnEventCode) + { + case BUTTON_DOWN | LEFT_BUTTON | SINGLE_CLICK | OVER_UNSELECTED_PAGE: + SetCurrentPage(rDescriptor.mpHitDescriptor); + break; + + case BUTTON_DOWN | LEFT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE: + break; + + case BUTTON_DOWN | LEFT_BUTTON | DOUBLE_CLICK | OVER_SELECTED_PAGE: + case BUTTON_DOWN | LEFT_BUTTON | DOUBLE_CLICK | OVER_UNSELECTED_PAGE: + // A double click always shows the selected slide in the center + // pane in an edit view. + SetCurrentPage(rDescriptor.mpHitDescriptor); + SwitchView(rDescriptor.mpHitDescriptor); + break; + + case BUTTON_DOWN | LEFT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE | SHIFT_MODIFIER: + case BUTTON_DOWN | LEFT_BUTTON | SINGLE_CLICK | OVER_UNSELECTED_PAGE | SHIFT_MODIFIER: + // Range selection with the shift modifier. + RangeSelect(rDescriptor.mpHitDescriptor); + break; + + // Right button for context menu. + case BUTTON_DOWN | RIGHT_BUTTON | SINGLE_CLICK | OVER_UNSELECTED_PAGE: + // Single right click and shift+F10 select as preparation to + // show the context menu. Change the selection only when the + // page under the mouse is not selected. In this case the + // selection is set to this single page. Otherwise the + // selection is not modified. + SetCurrentPage(rDescriptor.mpHitDescriptor); + break; + + case BUTTON_DOWN | RIGHT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE: + // Do not change the selection. Just adjust the insertion indicator. + break; + + case BUTTON_DOWN | RIGHT_BUTTON | SINGLE_CLICK | NOT_OVER_PAGE: + // Remember the current selection so that when a multi selection + // is started, we can restore the previous selection. + mrSlideSorter.GetModel().SaveCurrentSelection(); + DeselectAllPages(); + break; + + case ANY_MODIFIER(BUTTON_DOWN | LEFT_BUTTON | SINGLE_CLICK | NOT_OVER_PAGE): + // Remember the current selection so that when a multi selection + // is started, we can restore the previous selection. + mrSlideSorter.GetModel().SaveCurrentSelection(); + DeselectAllPages(); + break; + + case BUTTON_DOWN | LEFT_BUTTON | DOUBLE_CLICK | NOT_OVER_PAGE: + { + // Insert a new slide: + // First of all we need to set the insertion indicator which sets the + // position where the new slide will be inserted. + std::shared_ptr<InsertionIndicatorHandler> pInsertionIndicatorHandler + = mrSlideSorter.GetController().GetInsertionIndicatorHandler(); + + pInsertionIndicatorHandler->Start(false); + pInsertionIndicatorHandler->UpdatePosition( + rDescriptor.maMousePosition, + InsertionIndicatorHandler::MoveMode); + + mrSlideSorter.GetController().GetSelectionManager()->SetInsertionPosition( + pInsertionIndicatorHandler->GetInsertionPageIndex()); + + mrSlideSorter.GetViewShell()->GetDispatcher()->Execute( + SID_INSERTPAGE, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + + pInsertionIndicatorHandler->End(Animator::AM_Immediate); + + break; + } + + default: + return false; + } + return true; +} + +bool NormalModeHandler::ProcessButtonUpEvent ( + SelectionFunction::EventDescriptor& rDescriptor) +{ + bool bIsProcessed (true); + switch (rDescriptor.mnEventCode) + { + case BUTTON_UP | LEFT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE: + SetCurrentPage(rDescriptor.mpHitDescriptor); + break; + + // Multi selection with the control modifier. + case BUTTON_UP | LEFT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE | CONTROL_MODIFIER: + mrSlideSorter.GetController().GetPageSelector().DeselectPage( + rDescriptor.mpHitDescriptor); + break; + + case BUTTON_UP | LEFT_BUTTON | SINGLE_CLICK | OVER_UNSELECTED_PAGE | CONTROL_MODIFIER: + mrSlideSorter.GetController().GetPageSelector().SelectPage( + rDescriptor.mpHitDescriptor); + mrSlideSorter.GetView().SetPageUnderMouse(rDescriptor.mpHitDescriptor); + break; + case BUTTON_UP | LEFT_BUTTON | SINGLE_CLICK | NOT_OVER_PAGE: + break; + + default: + bIsProcessed = false; + break; + } + mrSelectionFunction.SwitchToNormalMode(); + return bIsProcessed; +} + +bool NormalModeHandler::ProcessMotionEvent ( + SelectionFunction::EventDescriptor& rDescriptor) +{ + if (ModeHandler::ProcessMotionEvent(rDescriptor)) + return true; + + bool bIsProcessed (true); + switch (rDescriptor.mnEventCode) + { + // A mouse motion without visible substitution starts that. + case ANY_MODIFIER(MOUSE_MOTION | LEFT_BUTTON | SINGLE_CLICK | OVER_UNSELECTED_PAGE): + case ANY_MODIFIER(MOUSE_MOTION | LEFT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE): + { + if (maButtonDownLocation) + { + const sal_Int32 nDistance(std::max( + std::abs(maButtonDownLocation->X() - rDescriptor.maMousePosition.X()), + std::abs(maButtonDownLocation->Y() - rDescriptor.maMousePosition.Y()))); + if (nDistance > 3) + StartDrag(rDescriptor.maMousePosition); + } + break; + } + + // A mouse motion not over a page starts a rectangle selection. + case ANY_MODIFIER(MOUSE_MOTION | LEFT_BUTTON | SINGLE_CLICK | NOT_OVER_PAGE): + mrSelectionFunction.SwitchToMultiSelectionMode( + rDescriptor.maMouseModelPosition, + rDescriptor.mnEventCode); + break; + + default: + bIsProcessed = false; + break; + } + return bIsProcessed; +} + +bool NormalModeHandler::ProcessDragEvent (SelectionFunction::EventDescriptor& rDescriptor) +{ + mrSelectionFunction.SwitchToDragAndDropMode(rDescriptor.maMousePosition); + ReprocessEvent(rDescriptor); + return true; +} + +void NormalModeHandler::RangeSelect (const model::SharedPageDescriptor& rpDescriptor) +{ + PageSelector::UpdateLock aLock (mrSlideSorter); + PageSelector& rSelector (mrSlideSorter.GetController().GetPageSelector()); + + model::SharedPageDescriptor pAnchor (rSelector.GetSelectionAnchor()); + DeselectAllPages(); + + if (!pAnchor) + return; + + // Select all pages between the anchor and the given one, including + // the two. + const sal_uInt16 nAnchorIndex ((pAnchor->GetPage()->GetPageNum()-1) / 2); + const sal_uInt16 nOtherIndex ((rpDescriptor->GetPage()->GetPageNum()-1) / 2); + + // Iterate over all pages in the range. Start with the anchor + // page. This way the PageSelector will recognize it again as + // anchor (the first selected page after a DeselectAllPages() + // becomes the anchor.) + const sal_uInt16 nStep ((nAnchorIndex < nOtherIndex) ? +1 : -1); + sal_uInt16 nIndex (nAnchorIndex); + while (true) + { + rSelector.SelectPage(nIndex); + if (nIndex == nOtherIndex) + break; + nIndex = nIndex + nStep; + } +} + +void NormalModeHandler::ResetButtonDownLocation() +{ + maButtonDownLocation = ::std::optional<Point>(); +} + +//===== MultiSelectionModeHandler ============================================= + +MultiSelectionModeHandler::MultiSelectionModeHandler ( + SlideSorter& rSlideSorter, + SelectionFunction& rSelectionFunction, + const Point& rMouseModelPosition, + const sal_uInt32 nEventCode) + : ModeHandler(rSlideSorter, rSelectionFunction, false), + meSelectionMode(SM_Normal), + maSecondCorner(rMouseModelPosition), + maSavedPointer(mrSlideSorter.GetContentWindow()->GetPointer()), + mbAutoScrollInstalled(false), + mnAnchorIndex(-1), + mnSecondIndex(-1) +{ + + mrSlideSorter.GetContentWindow()->SetPointer(PointerStyle::Text); + SetSelectionModeFromModifier(nEventCode); +} + +MultiSelectionModeHandler::~MultiSelectionModeHandler() +{ + if (mbAutoScrollInstalled) + { + //a call to this handler's MultiSelectionModeHandler::UpdatePosition + //may be still waiting to be called back + mrSlideSorter.GetController().GetScrollBarManager().clearAutoScrollFunctor(); + } + mrSlideSorter.GetContentWindow()->SetPointer(maSavedPointer); +} + +SelectionFunction::Mode MultiSelectionModeHandler::GetMode() const +{ + return SelectionFunction::MultiSelectionMode; +} + +void MultiSelectionModeHandler::Abort() +{ + mrSlideSorter.GetView().RequestRepaint(mrSlideSorter.GetModel().RestoreSelection()); +} + +void MultiSelectionModeHandler::ProcessEvent ( + SelectionFunction::EventDescriptor& rDescriptor) +{ + // During a multi selection we do not want sudden jumps of the + // visible area caused by moving newly selected pages into view. + // Therefore disable that temporarily. The disabled object is + // released at the end of the event processing, after the focus and + // current slide have been updated. + VisibleAreaManager::TemporaryDisabler aDisabler (mrSlideSorter); + + ModeHandler::ProcessEvent(rDescriptor); +} + +bool MultiSelectionModeHandler::ProcessButtonUpEvent ( + SelectionFunction::EventDescriptor& rDescriptor) +{ + if (mbAutoScrollInstalled) + { + //a call to this handler's MultiSelectionModeHandler::UpdatePosition + //may be still waiting to be called back + mrSlideSorter.GetController().GetScrollBarManager().clearAutoScrollFunctor(); + mbAutoScrollInstalled = false; + } + + if (Match(rDescriptor.mnEventCode, BUTTON_UP | LEFT_BUTTON | SINGLE_CLICK)) + { + mrSelectionFunction.SwitchToNormalMode(); + return true; + } + else + return false; +} + +bool MultiSelectionModeHandler::ProcessMotionEvent ( + SelectionFunction::EventDescriptor& rDescriptor) +{ + // The selection rectangle is visible. Handle events accordingly. + if (Match(rDescriptor.mnEventCode, MOUSE_MOTION | LEFT_BUTTON | SINGLE_CLICK)) + { + SetSelectionModeFromModifier(rDescriptor.mnEventCode); + UpdatePosition(rDescriptor.maMousePosition, true); + return true; + } + else + return false; +} + +bool MultiSelectionModeHandler::HandleUnprocessedEvent ( + SelectionFunction::EventDescriptor& rDescriptor) +{ + if ( ! ModeHandler::HandleUnprocessedEvent(rDescriptor)) + { + // If the event has not been processed then stop multi selection. + mrSelectionFunction.SwitchToNormalMode(); + ReprocessEvent(rDescriptor); + } + return true; +} + +void MultiSelectionModeHandler::UpdatePosition ( + const Point& rMousePosition, + const bool bAllowAutoScroll) +{ + VisibleAreaManager::TemporaryDisabler aDisabler (mrSlideSorter); + + // Convert window coordinates into model coordinates (we need the + // window coordinates for auto-scrolling because that remains + // constant while scrolling.) + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + const Point aMouseModelPosition (pWindow->PixelToLogic(rMousePosition)); + + bool bDoAutoScroll = bAllowAutoScroll && mrSlideSorter.GetController().GetScrollBarManager().AutoScroll( + rMousePosition, + [this, &rMousePosition] () { return this->UpdatePosition(rMousePosition, false); }); + + if (!bDoAutoScroll) + UpdateModelPosition(aMouseModelPosition); + + mbAutoScrollInstalled |= bDoAutoScroll; +} + +void MultiSelectionModeHandler::SetSelectionModeFromModifier ( + const sal_uInt32 nEventCode) +{ + switch (nEventCode & MODIFIER_MASK) + { + case NO_MODIFIER: + SetSelectionMode(SM_Normal); + break; + + case SHIFT_MODIFIER: + SetSelectionMode(SM_Add); + break; + + case CONTROL_MODIFIER: + SetSelectionMode(SM_Toggle); + break; + } +} + +void MultiSelectionModeHandler::SetSelectionMode (const SelectionMode eSelectionMode) +{ + if (meSelectionMode != eSelectionMode) + { + meSelectionMode = eSelectionMode; + UpdateSelection(); + } +} + +void MultiSelectionModeHandler::UpdateSelectionState ( + const model::SharedPageDescriptor& rpDescriptor, + const bool bIsInSelection) const +{ + // Determine whether the page was selected before the rectangle + // selection was started. + const bool bWasSelected (rpDescriptor->HasState(model::PageDescriptor::ST_WasSelected)); + + // Combine the two selection states depending on the selection mode. + bool bSelect (false); + switch(meSelectionMode) + { + case SM_Normal: + bSelect = bIsInSelection; + break; + + case SM_Add: + bSelect = bIsInSelection || bWasSelected; + break; + + case SM_Toggle: + if (bIsInSelection) + bSelect = !bWasSelected; + else + bSelect = bWasSelected; + break; + } + + // Set the new selection state. + if (bSelect) + mrSlideSorter.GetController().GetPageSelector().SelectPage(rpDescriptor); + else + mrSlideSorter.GetController().GetPageSelector().DeselectPage(rpDescriptor); +} + +void MultiSelectionModeHandler::UpdateModelPosition (const Point& rMouseModelPosition) +{ + maSecondCorner = rMouseModelPosition; + UpdateSelection(); +} + +void MultiSelectionModeHandler::UpdateSelection() +{ + view::SlideSorterView::DrawLock aLock (mrSlideSorter); + + model::SlideSorterModel& rModel (mrSlideSorter.GetModel()); + const sal_Int32 nPageCount (rModel.GetPageCount()); + + const sal_Int32 nIndexUnderMouse ( + mrSlideSorter.GetView().GetLayouter().GetIndexAtPoint ( + maSecondCorner, + false, + false)); + if (nIndexUnderMouse < 0 || nIndexUnderMouse >= nPageCount) + return; + + if (mnAnchorIndex < 0) + mnAnchorIndex = nIndexUnderMouse; + mnSecondIndex = nIndexUnderMouse; + + Range aRange (mnAnchorIndex, mnSecondIndex); + aRange.Justify(); + + for (sal_Int32 nIndex=0; nIndex<nPageCount; ++nIndex) + { + UpdateSelectionState(rModel.GetPageDescriptor(nIndex), aRange.Contains(nIndex)); + } +} + +//===== DragAndDropModeHandler ================================================ + +DragAndDropModeHandler::DragAndDropModeHandler ( + SlideSorter& rSlideSorter, + SelectionFunction& rSelectionFunction, + const Point& rMousePosition, + vcl::Window* pWindow) + : ModeHandler(rSlideSorter, rSelectionFunction, false) +{ + SdTransferable* pDragTransferable = SD_MOD()->pTransferDrag; + if (pDragTransferable==nullptr && mrSlideSorter.GetViewShell() != nullptr) + { + SlideSorterViewShell* pSlideSorterViewShell + = dynamic_cast<SlideSorterViewShell*>(mrSlideSorter.GetViewShell()); + if (pSlideSorterViewShell != nullptr) + pSlideSorterViewShell->StartDrag(rMousePosition, pWindow); + pDragTransferable = SD_MOD()->pTransferDrag; + } + + mpDragAndDropContext.reset(new DragAndDropContext(mrSlideSorter)); + mrSlideSorter.GetController().GetInsertionIndicatorHandler()->Start( + pDragTransferable != nullptr + && pDragTransferable->GetView()==&mrSlideSorter.GetView()); +} + +DragAndDropModeHandler::~DragAndDropModeHandler() +{ + if (mpDragAndDropContext) + { + // Disconnect the substitution handler from this selection function. + mpDragAndDropContext->SetTargetSlideSorter(); + mpDragAndDropContext.reset(); + } + mrSlideSorter.GetController().GetInsertionIndicatorHandler()->End(Animator::AM_Animated); +} + +SelectionFunction::Mode DragAndDropModeHandler::GetMode() const +{ + return SelectionFunction::DragAndDropMode; +} + +void DragAndDropModeHandler::Abort() +{ + mrSlideSorter.GetController().GetClipboard().Abort(); + if (mpDragAndDropContext) + mpDragAndDropContext->Dispose(); + // mrSlideSorter.GetView().RequestRepaint(mrSlideSorter.GetModel().RestoreSelection()); +} + +bool DragAndDropModeHandler::ProcessButtonUpEvent ( + SelectionFunction::EventDescriptor& rDescriptor) +{ + if (Match(rDescriptor.mnEventCode, BUTTON_UP | LEFT_BUTTON)) + { + // The following Process() call may lead to the destruction + // of rDescriptor.mpHitDescriptor so release our reference to it. + rDescriptor.mpHitDescriptor.reset(); + mrSelectionFunction.SwitchToNormalMode(); + return true; + } + else + return false; +} + +bool DragAndDropModeHandler::ProcessDragEvent (SelectionFunction::EventDescriptor& rDescriptor) +{ + OSL_ASSERT(mpDragAndDropContext); + + if (rDescriptor.mbIsLeaving) + { + mrSelectionFunction.SwitchToNormalMode(); + } + else if (mpDragAndDropContext) + { + mpDragAndDropContext->UpdatePosition( + rDescriptor.maMousePosition, + rDescriptor.meDragMode, true); + } + + return true; +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsSelectionManager.cxx b/sd/source/ui/slidesorter/controller/SlsSelectionManager.cxx new file mode 100644 index 000000000..e1f75b21c --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsSelectionManager.cxx @@ -0,0 +1,309 @@ +/* -*- 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 <controller/SlsSelectionManager.hxx> + +#include <SlideSorter.hxx> +#include <controller/SlideSorterController.hxx> +#include <controller/SlsCurrentSlideManager.hxx> +#include <controller/SlsFocusManager.hxx> +#include <controller/SlsPageSelector.hxx> +#include <controller/SlsSelectionObserver.hxx> +#include <model/SlideSorterModel.hxx> +#include <model/SlsPageEnumerationProvider.hxx> +#include <model/SlsPageDescriptor.hxx> +#include <view/SlideSorterView.hxx> +#include <tools/diagnose_ex.h> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <drawview.hxx> +#include <DrawViewShell.hxx> +#include <ViewShellBase.hxx> +#include <svx/svxids.hrc> +#include <com/sun/star/drawing/XMasterPagesSupplier.hpp> +#include <com/sun/star/drawing/XDrawPagesSupplier.hpp> + + +#include <sdresid.hxx> +#include <strings.hrc> +#include <app.hrc> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::uno; +using namespace ::sd::slidesorter::model; +using namespace ::sd::slidesorter::view; +using namespace ::sd::slidesorter::controller; + +namespace sd::slidesorter::controller { + +SelectionManager::SelectionManager (SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter), + mrController(rSlideSorter.GetController()), + mnInsertionPosition(-1), + mpSelectionObserver(std::make_shared<SelectionObserver>(rSlideSorter)) +{ +} + +SelectionManager::~SelectionManager() +{ +} + +void SelectionManager::DeleteSelectedPages (const bool bSelectFollowingPage) +{ + // Create some locks to prevent updates of the model, view, selection + // state while modifying any of them. + SlideSorterController::ModelChangeLock aLock (mrController); + SlideSorterView::DrawLock aDrawLock (mrSlideSorter); + PageSelector::UpdateLock aSelectionLock (mrSlideSorter); + + // Hide focus. + bool bIsFocusShowing = mrController.GetFocusManager().IsFocusShowing(); + if (bIsFocusShowing) + mrController.GetFocusManager().ToggleFocus(); + + // Store pointers to all selected page descriptors. This is necessary + // because the pages get deselected when the first one is deleted. + model::PageEnumeration aPageEnumeration ( + PageEnumerationProvider::CreateSelectedPagesEnumeration(mrSlideSorter.GetModel())); + ::std::vector<SdPage*> aSelectedPages; + sal_Int32 nNewCurrentSlide (-1); + while (aPageEnumeration.HasMoreElements()) + { + SharedPageDescriptor pDescriptor (aPageEnumeration.GetNextElement()); + aSelectedPages.push_back(pDescriptor->GetPage()); + if (bSelectFollowingPage || nNewCurrentSlide<0) + nNewCurrentSlide = pDescriptor->GetPageIndex(); + } + if (aSelectedPages.empty()) + return; + + // Determine the slide to select (and thereby make the current slide) + // after the deletion. + if (bSelectFollowingPage) + nNewCurrentSlide -= aSelectedPages.size() - 1; + else + --nNewCurrentSlide; + + const auto pViewShell = mrSlideSorter.GetViewShell(); + const auto pDrawViewShell = pViewShell ? std::dynamic_pointer_cast<sd::DrawViewShell>(pViewShell->GetViewShellBase().GetMainViewShell()) : nullptr; + const auto pDrawView = pDrawViewShell ? pDrawViewShell->GetDrawView() : nullptr; + + if (pDrawView) + pDrawView->BlockPageOrderChangedHint(true); + + // Proper naming for the undo action + OUString sUndoComment(SdResId(STR_UNDO_DELETEPAGES)); + if (mrSlideSorter.GetView().GetDoc().GetDocumentType() == DocumentType::Draw) + sUndoComment = SdResId(STR_UNDO_DELETEPAGES_DRAW); + + // The actual deletion of the selected pages is done in one of two + // helper functions. They are specialized for normal respectively for + // master pages. + mrSlideSorter.GetView().BegUndo (sUndoComment); + if (mrSlideSorter.GetModel().GetEditMode() == EditMode::Page) + DeleteSelectedNormalPages(aSelectedPages); + else + DeleteSelectedMasterPages(aSelectedPages); + mrSlideSorter.GetView().EndUndo (); + + mrController.HandleModelChange(); + aLock.Release(); + if (pDrawView) + { + assert(pDrawViewShell); + pDrawView->BlockPageOrderChangedHint(false); + pDrawViewShell->ResetActualPage(); + } + + // Show focus and move it to next valid location. + if (bIsFocusShowing) + mrController.GetFocusManager().ToggleFocus(); + + // Set the new current slide. + if (nNewCurrentSlide < 0) + nNewCurrentSlide = 0; + else if (nNewCurrentSlide >= mrSlideSorter.GetModel().GetPageCount()) + nNewCurrentSlide = mrSlideSorter.GetModel().GetPageCount()-1; + mrController.GetPageSelector().CountSelectedPages(); + mrController.GetPageSelector().SelectPage(nNewCurrentSlide); + mrController.GetFocusManager().SetFocusedPage(nNewCurrentSlide); +} + +void SelectionManager::DeleteSelectedNormalPages (const ::std::vector<SdPage*>& rSelectedPages) +{ + // Prepare the deletion via the UNO API. + OSL_ASSERT(mrSlideSorter.GetModel().GetEditMode() == EditMode::Page); + + try + { + Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier( mrSlideSorter.GetModel().GetDocument()->getUnoModel(), UNO_QUERY_THROW ); + Reference<drawing::XDrawPages> xPages( xDrawPagesSupplier->getDrawPages(), UNO_SET_THROW ); + + // Iterate over all pages that were selected when this method was called + // and delete the draw page the notes page. The iteration is done in + // reverse order so that when one slide is not deleted (to avoid an + // empty document) the remaining slide is the first one. + ::std::vector<SdPage*>::const_reverse_iterator aI; + for (aI=rSelectedPages.rbegin(); aI!=rSelectedPages.rend(); ++aI) + { + // Do not delete the last slide in the document. + if (xPages->getCount() <= 1) + break; + + const sal_uInt16 nPage (model::FromCoreIndex((*aI)->GetPageNum())); + + Reference< XDrawPage > xPage( xPages->getByIndex( nPage ), UNO_QUERY_THROW ); + xPages->remove(xPage); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "SelectionManager::DeleteSelectedNormalPages()"); + } +} + +void SelectionManager::DeleteSelectedMasterPages (const ::std::vector<SdPage*>& rSelectedPages) +{ + // Prepare the deletion via the UNO API. + OSL_ASSERT(mrSlideSorter.GetModel().GetEditMode() == EditMode::MasterPage); + + try + { + Reference<drawing::XMasterPagesSupplier> xDrawPagesSupplier( mrSlideSorter.GetModel().GetDocument()->getUnoModel(), UNO_QUERY_THROW ); + Reference<drawing::XDrawPages> xPages( xDrawPagesSupplier->getMasterPages(), UNO_SET_THROW ); + + // Iterate over all pages that were selected when this method was called + // and delete the draw page the notes page. The iteration is done in + // reverse order so that when one slide is not deleted (to avoid an + // empty document) the remaining slide is the first one. + ::std::vector<SdPage*>::const_reverse_iterator aI; + for (aI=rSelectedPages.rbegin(); aI!=rSelectedPages.rend(); ++aI) + { + // Do not delete the last slide in the document. + if (xPages->getCount() <= 1) + break; + + const sal_uInt16 nPage (model::FromCoreIndex((*aI)->GetPageNum())); + + Reference< XDrawPage > xPage( xPages->getByIndex( nPage ), UNO_QUERY_THROW ); + xPages->remove(xPage); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "SelectionManager::DeleteSelectedMasterPages()"); + } +} + +void SelectionManager::SelectionHasChanged () +{ + ViewShell* pViewShell = mrSlideSorter.GetViewShell(); + if (pViewShell == nullptr) + return; + + pViewShell->Invalidate (SID_EXPAND_PAGE); + pViewShell->Invalidate (SID_SUMMARY_PAGE); + pViewShell->Invalidate(SID_SHOW_SLIDE); + pViewShell->Invalidate(SID_HIDE_SLIDE); + pViewShell->Invalidate(SID_DELETE_PAGE); + pViewShell->Invalidate(SID_DELETE_MASTER_PAGE); + pViewShell->Invalidate(SID_ASSIGN_LAYOUT); + + // StatusBar + pViewShell->Invalidate (SID_STATUS_PAGE); + pViewShell->Invalidate (SID_STATUS_LAYOUT); + pViewShell->Invalidate (SID_SCALE); + + OSL_ASSERT(mrController.GetCurrentSlideManager()); + SharedPageDescriptor pDescriptor(mrController.GetCurrentSlideManager()->GetCurrentSlide()); + if (pDescriptor) + pViewShell->UpdatePreview(pDescriptor->GetPage()); + + // Tell the selection change listeners that the selection has changed. + for (const auto& rLink : maSelectionChangeListeners) + { + rLink.Call(nullptr); + } + + // Reset the insertion position: until set again it is calculated from + // the current selection. + mnInsertionPosition = -1; +} + +void SelectionManager::AddSelectionChangeListener (const Link<LinkParamNone*,void>& rListener) +{ + if (::std::find ( + maSelectionChangeListeners.begin(), + maSelectionChangeListeners.end(), + rListener) == maSelectionChangeListeners.end()) + { + maSelectionChangeListeners.push_back (rListener); + } +} + +void SelectionManager::RemoveSelectionChangeListener(const Link<LinkParamNone*,void>& rListener) +{ + maSelectionChangeListeners.erase ( + ::std::find ( + maSelectionChangeListeners.begin(), + maSelectionChangeListeners.end(), + rListener)); +} + +sal_Int32 SelectionManager::GetInsertionPosition() const +{ + sal_Int32 nInsertionPosition (mnInsertionPosition); + if (nInsertionPosition < 0) + { + model::PageEnumeration aSelectedPages + (model::PageEnumerationProvider::CreateSelectedPagesEnumeration( + mrSlideSorter.GetModel())); + // Initialize (for the case of an empty selection) with the position + // at the end of the document. + nInsertionPosition = mrSlideSorter.GetModel().GetPageCount(); + while (aSelectedPages.HasMoreElements()) + { + const sal_Int32 nPosition (aSelectedPages.GetNextElement()->GetPage()->GetPageNum()); + // Convert *2+1 index to straight index (n-1)/2 after the page + // (+1). + nInsertionPosition = model::FromCoreIndex(nPosition) + 1; + } + + } + return nInsertionPosition; +} + +void SelectionManager::SetInsertionPosition (const sal_Int32 nInsertionPosition) +{ + if (nInsertionPosition < 0) + mnInsertionPosition = -1; + else if (nInsertionPosition > mrSlideSorter.GetModel().GetPageCount()) + { + // Assert but then ignore invalid values. + OSL_ASSERT(nInsertionPosition<=mrSlideSorter.GetModel().GetPageCount()); + return; + } + else + mnInsertionPosition = nInsertionPosition; +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsSelectionObserver.cxx b/sd/source/ui/slidesorter/controller/SlsSelectionObserver.cxx new file mode 100644 index 000000000..8fb0493a0 --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsSelectionObserver.cxx @@ -0,0 +1,139 @@ +/* -*- 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 <SlideSorter.hxx> +#include <controller/SlideSorterController.hxx> +#include <controller/SlsSelectionManager.hxx> +#include <controller/SlsSelectionObserver.hxx> +#include <controller/SlsPageSelector.hxx> +#include <controller/SlsFocusManager.hxx> +#include <sdpage.hxx> +#include <osl/diagnose.h> + +namespace sd::slidesorter::controller +{ +SelectionObserver::Context::Context(SlideSorter const& rSlideSorter) + : mpSelectionObserver( + rSlideSorter.GetController().GetSelectionManager()->GetSelectionObserver()) +{ + if (mpSelectionObserver) + mpSelectionObserver->StartObservation(); +} + +SelectionObserver::Context::~Context() COVERITY_NOEXCEPT_FALSE +{ + if (mpSelectionObserver) + mpSelectionObserver->EndObservation(); +} + +void SelectionObserver::Context::Abort() +{ + if (mpSelectionObserver) + { + mpSelectionObserver->AbortObservation(); + mpSelectionObserver.reset(); + } +} + +//===== SelectionObserver ===================================================== + +SelectionObserver::SelectionObserver(SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter) + , mbIsObservationActive(false) + , mbPageEventOccurred(false) +{ +} + +SelectionObserver::~SelectionObserver() {} + +void SelectionObserver::NotifyPageEvent(const SdrPage* pSdrPage) +{ + if (!mbIsObservationActive) + return; + + mbPageEventOccurred = true; + + const SdPage* pPage = dynamic_cast<const SdPage*>(pSdrPage); + if (pPage == nullptr) + return; + + //NotifyPageEvent is called for add, remove, *and* change position so for + //the change position case we must ensure we don't end up with the slide + //duplicated in our list + std::vector<const SdPage*>::iterator iPage( + std::find(maInsertedPages.begin(), maInsertedPages.end(), pPage)); + if (iPage != maInsertedPages.end()) + maInsertedPages.erase(iPage); + + if (pPage->IsInserted()) + maInsertedPages.push_back(pPage); +} + +void SelectionObserver::StartObservation() +{ + OSL_ASSERT(!mbIsObservationActive); + maInsertedPages.clear(); + mbIsObservationActive = true; +} + +void SelectionObserver::AbortObservation() +{ + OSL_ASSERT(mbIsObservationActive); + mbIsObservationActive = false; + maInsertedPages.clear(); +} + +void SelectionObserver::EndObservation() +{ + OSL_ASSERT(mbIsObservationActive); + mbIsObservationActive = false; + + if (!mbPageEventOccurred) + return; + + PageSelector& rSelector(mrSlideSorter.GetController().GetPageSelector()); + PageSelector::UpdateLock aUpdateLock(mrSlideSorter); + rSelector.DeselectAllPages(); + if (!maInsertedPages.empty()) + { + // Select the inserted pages. + for (const auto& rpPage : maInsertedPages) + { + rSelector.SelectPage(rpPage); + } + maInsertedPages.clear(); + } + + aUpdateLock.Release(); + FocusManager& rFocusManager = mrSlideSorter.GetController().GetFocusManager(); + bool bSuccess = rFocusManager.SetFocusedPageToCurrentPage(); + // tdf#129346 nothing currently selected, select something, if possible + // but (tdf#129346) only if setting focus to current page failed + if (rSelector.GetPageCount() && rSelector.GetSelectedPageCount() == 0) + { + if (bSuccess) + rSelector.SelectPage(rFocusManager.GetFocusedPageDescriptor()); + else + rSelector.SelectPage(0); + } +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsSlotManager.cxx b/sd/source/ui/slidesorter/controller/SlsSlotManager.cxx new file mode 100644 index 000000000..52e05557e --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsSlotManager.cxx @@ -0,0 +1,1284 @@ +/* -*- 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 <controller/SlsSlotManager.hxx> +#include <SlideSorter.hxx> +#include <SlideSorterViewShell.hxx> +#include <controller/SlideSorterController.hxx> +#include <controller/SlsClipboard.hxx> +#include <controller/SlsCurrentSlideManager.hxx> +#include <controller/SlsInsertionIndicatorHandler.hxx> +#include <controller/SlsPageSelector.hxx> +#include <controller/SlsSelectionFunction.hxx> +#include <controller/SlsSelectionManager.hxx> +#include <model/SlideSorterModel.hxx> +#include <model/SlsPageEnumerationProvider.hxx> +#include <model/SlsPageDescriptor.hxx> +#include <view/SlideSorterView.hxx> +#include <view/SlsLayouter.hxx> +#include <framework/FrameworkHelper.hxx> +#include <Window.hxx> +#include <fupoor.hxx> +#include <fucushow.hxx> +#include <fusldlg.hxx> +#include <fuexpand.hxx> +#include <fusumry.hxx> +#include <slideshow.hxx> +#include <app.hrc> +#include <strings.hrc> +#include <sdresid.hxx> +#include <unokywds.hxx> +#include <drawdoc.hxx> +#include <DrawDocShell.hxx> +#include <ViewShellBase.hxx> +#include <ViewShellImplementation.hxx> +#include <sdpage.hxx> +#include <sdxfer.hxx> +#include <helpids.h> +#include <unmodpg.hxx> +#include <DrawViewShell.hxx> +#include <sdabstdlg.hxx> +#include <sdmod.hxx> + +#include <vcl/uitest/logger.hxx> +#include <vcl/uitest/eventdescription.hxx> + +#include <sfx2/request.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/sidebar/Sidebar.hxx> +#include <svx/svxids.hrc> +#include <svx/svxdlg.hxx> +#include <svl/intitem.hxx> +#include <svl/stritem.hxx> +#include <svl/whiter.hxx> +#include <svl/itempool.hxx> +#include <com/sun/star/drawing/XMasterPagesSupplier.hpp> +#include <com/sun/star/drawing/XDrawPages.hpp> +#include <osl/diagnose.h> + +#include <memory> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; + +namespace sd::slidesorter::controller { + +namespace { + +/** The state of a set of slides with respect to being excluded from the + slide show. +*/ +enum SlideExclusionState {UNDEFINED, EXCLUDED, INCLUDED, MIXED}; + +/** Return for the given set of slides whether they included are + excluded from the slide show. +*/ +SlideExclusionState GetSlideExclusionState (model::PageEnumeration& rPageSet); + +} // end of anonymous namespace + + +namespace { + +void collectUIInformation(std::map<OUString, OUString>&& aParameters, const OUString& rAction) +{ + EventDescription aDescription; + aDescription.aID = "impress_win_or_draw_win"; + aDescription.aParameters = std::move(aParameters); + aDescription.aAction = rAction; + aDescription.aKeyWord = "ImpressWindowUIObject"; + aDescription.aParent = "MainWindow"; + + UITestLogger::getInstance().logEvent(aDescription); +} + +} + +SlotManager::SlotManager (SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter) +{ +} + +void SlotManager::FuTemporary (SfxRequest& rRequest) +{ + SdDrawDocument* pDocument = mrSlideSorter.GetModel().GetDocument(); + + SlideSorterViewShell* pShell + = dynamic_cast<SlideSorterViewShell*>(mrSlideSorter.GetViewShell()); + if (pShell == nullptr) + return; + + switch (rRequest.GetSlot()) + { + case SID_PRESENTATION: + case SID_PRESENTATION_CURRENT_SLIDE: + case SID_REHEARSE_TIMINGS: + slideshowhelp::ShowSlideShow(rRequest, *mrSlideSorter.GetModel().GetDocument()); + pShell->Cancel(); + rRequest.Done(); + break; + + case SID_HIDE_SLIDE: + ChangeSlideExclusionState(model::SharedPageDescriptor(), true); + break; + + case SID_SHOW_SLIDE: + ChangeSlideExclusionState(model::SharedPageDescriptor(), false); + break; + + case SID_PAGES_PER_ROW: + if (rRequest.GetArgs() != nullptr) + { + const SfxUInt16Item* pPagesPerRow = rRequest.GetArg<SfxUInt16Item>(SID_PAGES_PER_ROW); + if (pPagesPerRow != nullptr) + { + sal_Int32 nColumnCount = pPagesPerRow->GetValue(); + // Force the given number of columns by setting + // the minimal and maximal number of columns to + // the same value. + mrSlideSorter.GetView().GetLayouter().SetColumnCount ( + nColumnCount, nColumnCount); + // Force a repaint and re-layout. + pShell->ArrangeGUIElements (); + // Rearrange the UI-elements controlled by the + // controller and force a rearrangement of the + // view. + mrSlideSorter.GetController().Rearrange(true); + } + } + rRequest.Done(); + break; + + case SID_SELECTALL: + mrSlideSorter.GetController().GetPageSelector().SelectAllPages(); + rRequest.Done(); + break; + + case SID_SLIDE_TRANSITIONS_PANEL: + { + // First make sure that the sidebar is visible + pShell->GetViewFrame()->ShowChildWindow(SID_SIDEBAR); + ::sfx2::sidebar::Sidebar::ShowPanel( + u"SdSlideTransitionPanel", + pShell->GetViewFrame()->GetFrame().GetFrameInterface()); + rRequest.Ignore (); + break; + } + + case SID_MASTER_SLIDES_PANEL: + { + // First make sure that the sidebar is visible + pShell->GetViewFrame()->ShowChildWindow(SID_SIDEBAR); + ::sfx2::sidebar::Sidebar::ShowPanel( + u"SdAllMasterPagesPanel", + pShell->GetViewFrame()->GetFrame().GetFrameInterface()); + rRequest.Ignore (); + break; + } + + case SID_PRESENTATION_DLG: + FuSlideShowDlg::Create ( + pShell, + mrSlideSorter.GetContentWindow(), + &mrSlideSorter.GetView(), + pDocument, + rRequest); + break; + + case SID_CUSTOMSHOW_DLG: + FuCustomShowDlg::Create ( + pShell, + mrSlideSorter.GetContentWindow(), + &mrSlideSorter.GetView(), + pDocument, + rRequest); + break; + + case SID_EXPAND_PAGE: + FuExpandPage::Create ( + pShell, + mrSlideSorter.GetContentWindow(), + &mrSlideSorter.GetView(), + pDocument, + rRequest); + break; + + case SID_SUMMARY_PAGE: + FuSummaryPage::Create ( + pShell, + mrSlideSorter.GetContentWindow(), + &mrSlideSorter.GetView(), + pDocument, + rRequest); + break; + + case SID_INSERTPAGE: + case SID_INSERT_MASTER_PAGE: + InsertSlide(rRequest); + rRequest.Done(); + break; + + case SID_DUPLICATE_PAGE: + DuplicateSelectedSlides(rRequest); + rRequest.Done(); + break; + + case SID_DELETE_PAGE: + case SID_DELETE_MASTER_PAGE: + case SID_DELETE: // we need SID_CUT to handle the delete key + // (DEL -> accelerator -> SID_CUT). + if (mrSlideSorter.GetModel().GetPageCount() > 1) + { + mrSlideSorter.GetView().EndTextEditAllViews(); + mrSlideSorter.GetController().GetSelectionManager()->DeleteSelectedPages(); + } + + rRequest.Done(); + break; + + case SID_RENAMEPAGE: + case SID_RENAME_MASTER_PAGE: + RenameSlide (rRequest); + rRequest.Done (); + break; + + case SID_ASSIGN_LAYOUT: + { + pShell->mpImpl->AssignLayout( rRequest, PageKind::Standard ); + rRequest.Done (); + } + break; + + case SID_PHOTOALBUM: + { + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + vcl::Window* pWin = mrSlideSorter.GetContentWindow(); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateSdPhotoAlbumDialog( + pWin ? pWin->GetFrameWeld() : nullptr, + pDocument)); + pDlg->Execute(); + rRequest.Done (); + } + break; + + case SID_REMOTE_DLG: + { +#ifdef ENABLE_SDREMOTE + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + vcl::Window* pWin = mrSlideSorter.GetContentWindow(); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateRemoteDialog(pWin ? pWin->GetFrameWeld() : nullptr)); + pDlg->Execute(); +#endif + } + break; + + default: + break; + } +} + +void SlotManager::FuPermanent (SfxRequest& rRequest) +{ + ViewShell* pShell = mrSlideSorter.GetViewShell(); + if (pShell == nullptr) + return; + + if(pShell->GetCurrentFunction().is()) + { + rtl::Reference<FuPoor> xEmpty; + if (pShell->GetOldFunction() == pShell->GetCurrentFunction()) + pShell->SetOldFunction(xEmpty); + + pShell->GetCurrentFunction()->Deactivate(); + pShell->SetCurrentFunction(xEmpty); + } + + switch(rRequest.GetSlot()) + { + case SID_OBJECT_SELECT: + pShell->SetCurrentFunction( SelectionFunction::Create(mrSlideSorter, rRequest) ); + rRequest.Done(); + break; + + default: + break; + } + + if(pShell->GetOldFunction().is()) + { + pShell->GetOldFunction()->Deactivate(); + rtl::Reference<FuPoor> xEmpty; + pShell->SetOldFunction(xEmpty); + } + + if(pShell->GetCurrentFunction().is()) + { + pShell->GetCurrentFunction()->Activate(); + pShell->SetOldFunction(pShell->GetCurrentFunction()); + } + + //! that's only until ENUM-Slots ?are + // Invalidate( SID_OBJECT_SELECT ); +} + +void SlotManager::FuSupport (SfxRequest& rRequest) +{ + switch (rRequest.GetSlot()) + { + case SID_STYLE_FAMILY: + if (rRequest.GetArgs() != nullptr) + { + SdDrawDocument* pDocument + = mrSlideSorter.GetModel().GetDocument(); + if (pDocument != nullptr) + { + const SfxPoolItem& rItem ( + rRequest.GetArgs()->Get(SID_STYLE_FAMILY)); + pDocument->GetDocSh()->SetStyleFamily( + static_cast<SfxStyleFamily>(static_cast<const SfxUInt16Item&>(rItem).GetValue())); + } + } + break; + + case SID_PASTE: + { + SdTransferable* pTransferClip = SD_MOD()->pTransferClip; + if( pTransferClip ) + { + SfxObjectShell* pTransferDocShell = pTransferClip->GetDocShell().get(); + + DrawDocShell* pDocShell = dynamic_cast<DrawDocShell*>(pTransferDocShell); + if (pDocShell && pDocShell->GetDoc()->GetPageCount() > 1) + { + mrSlideSorter.GetController().GetClipboard().HandleSlotCall(rRequest); + break; + } + } + ViewShellBase* pBase = mrSlideSorter.GetViewShellBase(); + if (pBase != nullptr) + { + std::shared_ptr<DrawViewShell> pDrawViewShell ( + std::dynamic_pointer_cast<DrawViewShell>(pBase->GetMainViewShell())); + if (pDrawViewShell != nullptr) + pDrawViewShell->FuSupport(rRequest); + } + } + break; + + case SID_CUT: + case SID_COPY: + case SID_DELETE: + mrSlideSorter.GetView().EndTextEditAllViews(); + mrSlideSorter.GetController().GetClipboard().HandleSlotCall(rRequest); + break; + + case SID_DRAWINGMODE: + case SID_NOTES_MODE: + case SID_HANDOUT_MASTER_MODE: + case SID_SLIDE_SORTER_MODE: + case SID_OUTLINE_MODE: + { + ViewShellBase* pBase = mrSlideSorter.GetViewShellBase(); + if (pBase != nullptr) + { + framework::FrameworkHelper::Instance(*pBase)->HandleModeChangeSlot( + rRequest.GetSlot(), rRequest); + rRequest.Done(); + } + break; + } + + case SID_UNDO: + { + SlideSorterViewShell* pViewShell + = dynamic_cast<SlideSorterViewShell*>(mrSlideSorter.GetViewShell()); + if (pViewShell != nullptr) + { + pViewShell->ImpSidUndo (rRequest); + } + break; + } + + case SID_REDO: + { + SlideSorterViewShell* pViewShell + = dynamic_cast<SlideSorterViewShell*>(mrSlideSorter.GetViewShell()); + if (pViewShell != nullptr) + { + pViewShell->ImpSidRedo (rRequest); + } + break; + } + + default: + break; + } +} + +void SlotManager::ExecCtrl (SfxRequest& rRequest) +{ + ViewShell* pViewShell = mrSlideSorter.GetViewShell(); + sal_uInt16 nSlot = rRequest.GetSlot(); + switch (nSlot) + { + case SID_RELOAD: + { + // empty Undo-Manager + mrSlideSorter.GetModel().GetDocument()->GetDocSh()->ClearUndoBuffer(); + + // normal forwarding to ViewFrame for execution + if (pViewShell != nullptr) + pViewShell->GetViewFrame()->ExecuteSlot(rRequest); + + // has to be finished right away + return; + } + + case SID_OUTPUT_QUALITY_COLOR: + case SID_OUTPUT_QUALITY_GRAYSCALE: + case SID_OUTPUT_QUALITY_BLACKWHITE: + case SID_OUTPUT_QUALITY_CONTRAST: + { + // flush page cache + if (pViewShell != nullptr) + pViewShell->ExecReq (rRequest); + break; + } + + case SID_MAIL_SCROLLBODY_PAGEDOWN: + { + if (pViewShell != nullptr) + pViewShell->ExecReq (rRequest); + break; + } + + case SID_OPT_LOCALE_CHANGED: + { + mrSlideSorter.GetController().UpdateAllPages(); + if (pViewShell != nullptr) + pViewShell->UpdatePreview (pViewShell->GetActualPage()); + rRequest.Done(); + break; + } + + case SID_SEARCH_DLG: + // We have to handle the SID_SEARCH_DLG slot explicitly because + // in some cases (when the slide sorter is displayed in the + // center pane) we want to disable the search dialog. Therefore + // we have to handle the execution of that slot as well. + // We try to do that by forwarding the request to the view frame + // of the view shell. + if (pViewShell != nullptr) + pViewShell->GetViewFrame()->ExecuteSlot(rRequest); + break; + + default: + break; + } +} + +void SlotManager::GetAttrState (SfxItemSet& rSet) +{ + // Iterate over all items. + SfxWhichIter aIter (rSet); + sal_uInt16 nWhich = aIter.FirstWhich(); + while (nWhich) + { + sal_uInt16 nSlotId (nWhich); + if (SfxItemPool::IsWhich(nWhich) && mrSlideSorter.GetViewShell()!=nullptr) + nSlotId = mrSlideSorter.GetViewShell()->GetPool().GetSlotId(nWhich); + switch (nSlotId) + { + case SID_PAGES_PER_ROW: + rSet.Put ( + SfxUInt16Item ( + nSlotId, + static_cast<sal_uInt16>(mrSlideSorter.GetView().GetLayouter().GetColumnCount()) + ) + ); + break; + } + nWhich = aIter.NextWhich(); + } +} + +void SlotManager::GetMenuState (SfxItemSet& rSet) +{ + EditMode eEditMode = mrSlideSorter.GetModel().GetEditMode(); + ViewShell* pShell = mrSlideSorter.GetViewShell(); + DrawDocShell* pDocShell = mrSlideSorter.GetModel().GetDocument()->GetDocSh(); + + if (pShell!=nullptr && pShell->GetCurrentFunction().is()) + { + sal_uInt16 nSId = pShell->GetCurrentFunction()->GetSlotID(); + + rSet.Put( SfxBoolItem( nSId, true ) ); + } + rSet.Put( SfxBoolItem( SID_DRAWINGMODE, false ) ); + rSet.Put( SfxBoolItem( SID_SLIDE_SORTER_MODE, true ) ); + rSet.Put( SfxBoolItem( SID_OUTLINE_MODE, false ) ); + rSet.Put( SfxBoolItem( SID_NOTES_MODE, false ) ); + rSet.Put( SfxBoolItem( SID_HANDOUT_MASTER_MODE, false ) ); + + if (pShell!=nullptr && pShell->IsMainViewShell()) + { + rSet.DisableItem(SID_SPELL_DIALOG); + rSet.DisableItem(SID_SEARCH_DLG); + } + + if (SfxItemState::DEFAULT == rSet.GetItemState(SID_EXPAND_PAGE)) + { + bool bDisable = true; + if (eEditMode == EditMode::Page) + { + // At least one of the selected pages has to contain an outline + // presentation objects in order to enable the expand page menu + // entry. + model::PageEnumeration aSelectedPages ( + model::PageEnumerationProvider::CreateSelectedPagesEnumeration( + mrSlideSorter.GetModel())); + while (aSelectedPages.HasMoreElements()) + { + SdPage* pPage = aSelectedPages.GetNextElement()->GetPage(); + SdrObject* pObj = pPage->GetPresObj(PresObjKind::Outline); + if (pObj!=nullptr ) + { + if( !pObj->IsEmptyPresObj() ) + { + bDisable = false; + } + else + { + // check if the object is in edit, then if it's temporarily not empty + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( pObj ); + if( pTextObj ) + { + if( pTextObj->CanCreateEditOutlinerParaObject() ) + { + bDisable = false; + } + } + } + } + } + } + + if (bDisable) + rSet.DisableItem (SID_EXPAND_PAGE); + } + + if (SfxItemState::DEFAULT == rSet.GetItemState(SID_SUMMARY_PAGE)) + { + bool bDisable = true; + if (eEditMode == EditMode::Page) + { + // At least one of the selected pages has to contain a title + // presentation objects in order to enable the summary page menu + // entry. + model::PageEnumeration aSelectedPages ( + model::PageEnumerationProvider::CreateSelectedPagesEnumeration( + mrSlideSorter.GetModel())); + while (aSelectedPages.HasMoreElements()) + { + SdPage* pPage = aSelectedPages.GetNextElement()->GetPage(); + SdrObject* pObj = pPage->GetPresObj(PresObjKind::Title); + + if (pObj!=nullptr && !pObj->IsEmptyPresObj()) + bDisable = false; + } + } + if (bDisable) + rSet.DisableItem (SID_SUMMARY_PAGE); + } + + // starting of presentation possible? + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_PRESENTATION ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_REHEARSE_TIMINGS ) ) + { + bool bDisable = true; + model::PageEnumeration aAllPages ( + model::PageEnumerationProvider::CreateAllPagesEnumeration(mrSlideSorter.GetModel())); + while (aAllPages.HasMoreElements()) + { + SdPage* pPage = aAllPages.GetNextElement()->GetPage(); + + if( !pPage->IsExcluded() ) + bDisable = false; + } + if( bDisable || pDocShell->IsPreview()) + { + rSet.DisableItem( SID_PRESENTATION ); + rSet.DisableItem( SID_REHEARSE_TIMINGS ); + } + } + + // Disable the rename slots when there are no or more than one slides/master + // pages selected; disable the duplicate slot when there are no slides + // selected: + if (rSet.GetItemState(SID_RENAMEPAGE) == SfxItemState::DEFAULT + || rSet.GetItemState(SID_RENAME_MASTER_PAGE) == SfxItemState::DEFAULT + || rSet.GetItemState(SID_DUPLICATE_PAGE) == SfxItemState::DEFAULT) + { + int n = mrSlideSorter.GetController().GetPageSelector() + .GetSelectedPageCount(); + if (n != 1) + { + rSet.DisableItem(SID_RENAMEPAGE); + rSet.DisableItem(SID_RENAME_MASTER_PAGE); + } + if (n == 0) + { + rSet.DisableItem(SID_DUPLICATE_PAGE); + } + } + + if (rSet.GetItemState(SID_HIDE_SLIDE) == SfxItemState::DEFAULT + || rSet.GetItemState(SID_SHOW_SLIDE) == SfxItemState::DEFAULT) + { + model::PageEnumeration aSelectedPages ( + model::PageEnumerationProvider::CreateSelectedPagesEnumeration( + mrSlideSorter.GetModel())); + const SlideExclusionState eState (GetSlideExclusionState(aSelectedPages)); + switch (eState) + { + case MIXED: + // Show both entries. + break; + + case EXCLUDED: + rSet.DisableItem(SID_HIDE_SLIDE); + break; + + case INCLUDED: + rSet.DisableItem(SID_SHOW_SLIDE); + break; + + case UNDEFINED: + rSet.DisableItem(SID_HIDE_SLIDE); + rSet.DisableItem(SID_SHOW_SLIDE); + break; + } + } + + if (eEditMode == EditMode::MasterPage) + { + // Disable some slots when in master page mode. + rSet.DisableItem(SID_ASSIGN_LAYOUT); + rSet.DisableItem(SID_INSERTPAGE); + + if (rSet.GetItemState(SID_DUPLICATE_PAGE) == SfxItemState::DEFAULT) + rSet.DisableItem(SID_DUPLICATE_PAGE); + } +} + +void SlotManager::GetClipboardState ( SfxItemSet& rSet) +{ + SdTransferable* pTransferClip = SD_MOD()->pTransferClip; + + if (rSet.GetItemState(SID_PASTE) == SfxItemState::DEFAULT + || rSet.GetItemState(SID_PASTE_SPECIAL) == SfxItemState::DEFAULT) + { + // no own clipboard data? + if ( !pTransferClip || !pTransferClip->GetDocShell().is() ) + { + rSet.DisableItem(SID_PASTE); + rSet.DisableItem(SID_PASTE_SPECIAL); + } + else + { + SfxObjectShell* pTransferDocShell = pTransferClip->GetDocShell().get(); + + if( !pTransferDocShell || static_cast<DrawDocShell*>(pTransferDocShell)->GetDoc()->GetPageCount() <= 1 ) + { + bool bIsPastingSupported (false); + + // No or just one page. Check if there is anything that can be + // pasted via a DrawViewShell. + ViewShellBase* pBase = mrSlideSorter.GetViewShellBase(); + if (pBase != nullptr) + { + std::shared_ptr<DrawViewShell> pDrawViewShell ( + std::dynamic_pointer_cast<DrawViewShell>(pBase->GetMainViewShell())); + if (pDrawViewShell != nullptr) + { + TransferableDataHelper aDataHelper ( + TransferableDataHelper::CreateFromSystemClipboard( + pDrawViewShell->GetActiveWindow())); + if (aDataHelper.GetFormatCount() > 0) + bIsPastingSupported = true; + } + } + + if ( ! bIsPastingSupported) + { + rSet.DisableItem(SID_PASTE); + rSet.DisableItem(SID_PASTE_SPECIAL); + } + } + } + } + + // Cut, copy and paste of master pages is not yet implemented properly + if (rSet.GetItemState(SID_COPY) == SfxItemState::DEFAULT + || rSet.GetItemState(SID_PASTE) == SfxItemState::DEFAULT + || rSet.GetItemState(SID_PASTE_SPECIAL) == SfxItemState::DEFAULT + || rSet.GetItemState(SID_CUT) == SfxItemState::DEFAULT) + { + if (mrSlideSorter.GetModel().GetEditMode() == EditMode::MasterPage) + { + if (rSet.GetItemState(SID_CUT) == SfxItemState::DEFAULT) + rSet.DisableItem(SID_CUT); + if (rSet.GetItemState(SID_COPY) == SfxItemState::DEFAULT) + rSet.DisableItem(SID_COPY); + if (rSet.GetItemState(SID_PASTE) == SfxItemState::DEFAULT) + rSet.DisableItem(SID_PASTE); + if (rSet.GetItemState(SID_PASTE_SPECIAL) == SfxItemState::DEFAULT) + rSet.DisableItem(SID_PASTE_SPECIAL); + } + } + + ViewShellBase* pBase = mrSlideSorter.GetViewShellBase(); + if (pBase && pBase->GetObjectShell()->isContentExtractionLocked()) + { + rSet.DisableItem(SID_COPY); + rSet.DisableItem(SID_CUT); + } + + // Cut, copy, and delete page are disabled when there is no selection. + if (!(rSet.GetItemState(SID_CUT) == SfxItemState::DEFAULT + || rSet.GetItemState(SID_COPY) == SfxItemState::DEFAULT + || rSet.GetItemState(SID_DELETE) == SfxItemState::DEFAULT + || rSet.GetItemState(SID_DELETE_PAGE) == SfxItemState::DEFAULT + || rSet.GetItemState(SID_DELETE_MASTER_PAGE) == SfxItemState::DEFAULT)) + return; + + model::PageEnumeration aSelectedPages ( + model::PageEnumerationProvider::CreateSelectedPagesEnumeration( + mrSlideSorter.GetModel())); + + // For copy to work we have to have at least one selected page. + if ( ! aSelectedPages.HasMoreElements()) + rSet.DisableItem(SID_COPY); + + bool bDisable = false; + // The operations that lead to the deletion of a page are valid if + // a) there is at least one selected page + // b) deleting the selected pages leaves at least one page in the + // document + // c) selected master pages must not be used by slides. + + // Test a). + if ( ! aSelectedPages.HasMoreElements()) + bDisable = true; + // Test b): Count the number of selected pages. It has to be less + // than the number of all pages. + else if (mrSlideSorter.GetController().GetPageSelector().GetSelectedPageCount() + >= mrSlideSorter.GetController().GetPageSelector().GetPageCount()) + bDisable = true; + // Test c): Iterate over the selected pages and look for a master + // page that is used by at least one page. + else while (aSelectedPages.HasMoreElements()) + { + SdPage* pPage = aSelectedPages.GetNextElement()->GetPage(); + int nUseCount (mrSlideSorter.GetModel().GetDocument() + ->GetMasterPageUserCount(pPage)); + if (nUseCount > 0) + { + bDisable = true; + break; + } + } + + if (bDisable) + { + rSet.DisableItem(SID_CUT); + rSet.DisableItem(SID_DELETE_PAGE); + rSet.DisableItem(SID_DELETE_MASTER_PAGE); + } +} + +void SlotManager::GetStatusBarState (SfxItemSet& rSet) +{ + // page view and layout + SdPage* pPage = nullptr; + sal_uInt16 nSelectedPages = mrSlideSorter.GetController().GetPageSelector().GetSelectedPageCount(); + + //Set number of slides + if (nSelectedPages > 0) + { + model::PageEnumeration aSelectedPages ( + model::PageEnumerationProvider::CreateSelectedPagesEnumeration( + mrSlideSorter.GetModel())); + model::SharedPageDescriptor pDescriptor (aSelectedPages.GetNextElement()); + OUString aPageStr; + if (pDescriptor) + { + pPage = pDescriptor->GetPage(); + sal_uInt16 nFirstPage = (pPage->GetPageNum()/2) + 1; + sal_Int32 nPageCount = mrSlideSorter.GetModel().GetPageCount(); + sal_Int32 nActivePageCount = static_cast<sal_Int32>(mrSlideSorter.GetModel().GetDocument()->GetActiveSdPageCount()); + + aPageStr = (nPageCount == nActivePageCount) ? SdResId(STR_SD_PAGE_COUNT) : SdResId(STR_SD_PAGE_COUNT_CUSTOM); + + aPageStr = aPageStr.replaceFirst("%1", OUString::number(nFirstPage)); + aPageStr = aPageStr.replaceFirst("%2", OUString::number(nPageCount)); + if(nPageCount != nActivePageCount) + aPageStr = aPageStr.replaceFirst("%3", OUString::number(nActivePageCount)); + } + rSet.Put( SfxStringItem( SID_STATUS_PAGE, aPageStr ) ); + } + //Set layout + if (nSelectedPages == 1 && pPage != nullptr) + { + SdPage* pFirstPage = pPage; + OUString aLayoutStr = pFirstPage->GetLayoutName(); + sal_Int32 nIndex = aLayoutStr.indexOf( SD_LT_SEPARATOR ); + if( nIndex != -1 ) + aLayoutStr = aLayoutStr.copy(0, nIndex); + rSet.Put( SfxStringItem( SID_STATUS_LAYOUT, aLayoutStr ) ); + } + //Scale value + const Fraction& aUIScale = mrSlideSorter.GetModel().GetDocument()->GetUIScale(); + OUString aString = OUString::number(aUIScale.GetNumerator()) + + ":" + OUString::number(aUIScale.GetDenominator()); + rSet.Put( SfxStringItem( SID_SCALE, aString ) ); +} + +void SlotManager::RenameSlide(const SfxRequest& rRequest) +{ + View* pDrView = &mrSlideSorter.GetView(); + + if ( pDrView->IsTextEdit() ) + { + pDrView->SdrEndTextEdit(); + } + + SdPage* pSelectedPage = nullptr; + model::PageEnumeration aSelectedPages ( + model::PageEnumerationProvider::CreateSelectedPagesEnumeration( + mrSlideSorter.GetModel())); + if (aSelectedPages.HasMoreElements()) + pSelectedPage = aSelectedPages.GetNextElement()->GetPage(); + if (pSelectedPage == nullptr) + return; + + // tdf#107183 Set different dialog titles when renaming + // master slides or normal ones + OUString aTitle; + if( rRequest.GetSlot() == SID_RENAME_MASTER_PAGE ) + aTitle = SdResId( STR_TITLE_RENAMEMASTER ); + else if (pDrView->GetDoc().GetDocumentType() == DocumentType::Draw) + aTitle = SdResId( STR_TITLE_RENAMEPAGE ); + else + aTitle = SdResId( STR_TITLE_RENAMESLIDE ); + + OUString aDescr( SdResId( STR_DESC_RENAMESLIDE ) ); + OUString aPageName = pSelectedPage->GetName(); + + if(rRequest.GetArgs()) + { + OUString aName = rRequest.GetArgs()->GetItem<const SfxStringItem>(SID_RENAMEPAGE)->GetValue(); + + bool bResult = RenameSlideFromDrawViewShell(pSelectedPage->GetPageNum()/2, aName ); + DBG_ASSERT( bResult, "Couldn't rename slide or page" ); + } + else + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + vcl::Window* pWin = mrSlideSorter.GetContentWindow(); + ScopedVclPtr<AbstractSvxNameDialog> aNameDlg(pFact->CreateSvxNameDialog( + pWin ? pWin->GetFrameWeld() : nullptr, + aPageName, aDescr)); + OUString aOldName; + aNameDlg->GetName( aOldName ); + aNameDlg->SetText( aTitle ); + aNameDlg->SetCheckNameHdl( LINK( this, SlotManager, RenameSlideHdl ), true ); + aNameDlg->SetCheckNameTooltipHdl( LINK( this, SlotManager, RenameSlideTooltipHdl ) ); + aNameDlg->SetEditHelpId( HID_SD_NAMEDIALOG_PAGE ); + + if( aNameDlg->Execute() == RET_OK ) + { + OUString aNewName; + aNameDlg->GetName( aNewName ); + if (aNewName != aPageName) + { + bool bResult = + RenameSlideFromDrawViewShell( + pSelectedPage->GetPageNum()/2, aNewName ); + DBG_ASSERT( bResult, "Couldn't rename slide or page" ); + } + } + OUString aNewName; + aNameDlg->GetName( aNewName ); + collectUIInformation({{"OldName", aOldName}, {"NewName", aNewName}}, "RENAME"); + aNameDlg.disposeAndClear(); + } + // Tell the slide sorter about the name change (necessary for + // accessibility.) + mrSlideSorter.GetController().PageNameHasChanged( + (pSelectedPage->GetPageNum()-1)/2, aPageName); +} + +IMPL_LINK(SlotManager, RenameSlideHdl, AbstractSvxNameDialog&, rDialog, bool) +{ + OUString aNewName; + rDialog.GetName( aNewName ); + + model::SharedPageDescriptor pDescriptor ( + mrSlideSorter.GetController().GetCurrentSlideManager()->GetCurrentSlide()); + SdPage* pCurrentPage = nullptr; + if (pDescriptor) + pCurrentPage = pDescriptor->GetPage(); + + return (pCurrentPage!=nullptr && aNewName == pCurrentPage->GetName()) + || (mrSlideSorter.GetViewShell() + && mrSlideSorter.GetViewShell()->GetDocSh()->IsNewPageNameValid( aNewName ) ); +} + +IMPL_STATIC_LINK_NOARG(SlotManager, RenameSlideTooltipHdl, AbstractSvxNameDialog&, OUString) +{ + return SdResId(STR_TOOLTIP_RENAME); +} + +bool SlotManager::RenameSlideFromDrawViewShell( sal_uInt16 nPageId, const OUString & rName ) +{ + bool bOutDummy; + SdDrawDocument* pDocument = mrSlideSorter.GetModel().GetDocument(); + if( pDocument->GetPageByName( rName, bOutDummy ) != SDRPAGE_NOTFOUND ) + return false; + + SdPage* pPageToRename = nullptr; + + SfxUndoManager* pManager = pDocument->GetDocSh()->GetUndoManager(); + + if( mrSlideSorter.GetModel().GetEditMode() == EditMode::Page ) + { + model::SharedPageDescriptor pDescriptor ( + mrSlideSorter.GetController().GetCurrentSlideManager()->GetCurrentSlide()); + if (pDescriptor) + pPageToRename = pDescriptor->GetPage(); + + if (pPageToRename != nullptr) + { + // Undo + SdPage* pUndoPage = pPageToRename; + SdrLayerAdmin & rLayerAdmin = pDocument->GetLayerAdmin(); + SdrLayerID nBackground = rLayerAdmin.GetLayerID(sUNO_LayerName_background); + SdrLayerID nBgObj = rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects); + SdrLayerIDSet aVisibleLayers = pPageToRename->TRG_GetMasterPageVisibleLayers(); + + // (#67720#) + pManager->AddUndoAction( + std::make_unique<ModifyPageUndoAction>( + pDocument, pUndoPage, rName, pUndoPage->GetAutoLayout(), + aVisibleLayers.IsSet( nBackground ), + aVisibleLayers.IsSet( nBgObj ))); + + // rename + pPageToRename->SetName( rName ); + + // also rename notes-page + SdPage* pNotesPage = pDocument->GetSdPage( nPageId, PageKind::Notes ); + if (pNotesPage != nullptr) + pNotesPage->SetName (rName); + } + } + else + { + // rename MasterPage -> rename LayoutTemplate + pPageToRename = pDocument->GetMasterSdPage( nPageId, PageKind::Standard ); + if (pPageToRename != nullptr) + { + const OUString aOldLayoutName( pPageToRename->GetLayoutName() ); + pManager->AddUndoAction( std::make_unique<RenameLayoutTemplateUndoAction>( pDocument, aOldLayoutName, rName ) ); + pDocument->RenameLayoutTemplate( aOldLayoutName, rName ); + } + } + + bool bSuccess = pPageToRename!=nullptr && ( rName == pPageToRename->GetName() ); + + if( bSuccess ) + { + // user edited page names may be changed by the page so update control + // aTabControl.SetPageText( nPageId, rName ); + + // set document to modified state + pDocument->SetChanged(); + + // inform navigator about change + if (mrSlideSorter.GetViewShell() && mrSlideSorter.GetViewShell()->GetViewFrame()) + mrSlideSorter.GetViewShell()->GetViewFrame()->GetBindings().Invalidate( + SID_NAVIGATOR_STATE, true); + } + + return bSuccess; +} + +/** Insert a slide. The insertion position depends on a) the selection and + b) the mouse position when there is no selection. + + When there is a selection then insertion takes place after the last + slide of the selection. For this to work all but the last selected + slide are deselected first. + + Otherwise, when there is no selection but the insertion marker is visible + the slide is inserted at that position. The slide before that marker is + selected first. + + When both the selection and the insertion marker are not visible--can + that happen?--the new slide is inserted after the last slide. +*/ +void SlotManager::InsertSlide (SfxRequest& rRequest) +{ + const sal_Int32 nInsertionIndex (GetInsertionPosition()); + + PageSelector::BroadcastLock aBroadcastLock (mrSlideSorter); + + SdPage* pNewPage = nullptr; + if (mrSlideSorter.GetModel().GetEditMode() == EditMode::Page) + { + SlideSorterViewShell* pShell = dynamic_cast<SlideSorterViewShell*>( + mrSlideSorter.GetViewShell()); + if (pShell != nullptr) + { + pNewPage = pShell->CreateOrDuplicatePage ( + rRequest, + PageKind::Standard, + nInsertionIndex>=0 + ? mrSlideSorter.GetModel().GetPageDescriptor(nInsertionIndex)->GetPage() + : nullptr); + } + } + else + { + // Use the API to create a new page. + SdDrawDocument* pDocument = mrSlideSorter.GetModel().GetDocument(); + Reference<drawing::XMasterPagesSupplier> xMasterPagesSupplier ( + pDocument->getUnoModel(), UNO_QUERY); + if (xMasterPagesSupplier.is()) + { + Reference<drawing::XDrawPages> xMasterPages ( + xMasterPagesSupplier->getMasterPages()); + if (xMasterPages.is()) + { + xMasterPages->insertNewByIndex (nInsertionIndex+1); + + // Create shapes for the default layout. + pNewPage = pDocument->GetMasterSdPage( + static_cast<sal_uInt16>(nInsertionIndex+1), PageKind::Standard); + pNewPage->CreateTitleAndLayout (true,true); + } + } + } + if (pNewPage == nullptr) + return; + + // When a new page has been inserted then select it, make it the + // current page, and focus it. + view::SlideSorterView::DrawLock aDrawLock (mrSlideSorter); + PageSelector::UpdateLock aUpdateLock (mrSlideSorter); + mrSlideSorter.GetController().GetPageSelector().DeselectAllPages(); + mrSlideSorter.GetController().GetPageSelector().SelectPage(pNewPage); + collectUIInformation({{"POS", OUString::number(nInsertionIndex + 2)}}, "Insert_New_Page_or_Slide"); +} + +void SlotManager::DuplicateSelectedSlides (SfxRequest& rRequest) +{ + // Create a list of the pages that are to be duplicated. The process of + // duplication alters the selection. + sal_Int32 nInsertPosition (0); + ::std::vector<SdPage*> aPagesToDuplicate; + model::PageEnumeration aSelectedPages ( + model::PageEnumerationProvider::CreateSelectedPagesEnumeration(mrSlideSorter.GetModel())); + while (aSelectedPages.HasMoreElements()) + { + model::SharedPageDescriptor pDescriptor (aSelectedPages.GetNextElement()); + if (pDescriptor && pDescriptor->GetPage()) + { + aPagesToDuplicate.push_back(pDescriptor->GetPage()); + nInsertPosition = pDescriptor->GetPage()->GetPageNum()+2; + } + } + + // Duplicate the pages in aPagesToDuplicate and collect the newly + // created pages in aPagesToSelect. + const bool bUndo (aPagesToDuplicate.size()>1 && mrSlideSorter.GetView().IsUndoEnabled()); + if (bUndo) + mrSlideSorter.GetView().BegUndo(SdResId(STR_INSERTPAGE)); + + ::std::vector<SdPage*> aPagesToSelect; + for(const auto& rpPage : aPagesToDuplicate) + { + aPagesToSelect.push_back( + mrSlideSorter.GetViewShell()->CreateOrDuplicatePage( + rRequest, PageKind::Standard, rpPage, nInsertPosition)); + nInsertPosition += 2; + } + aPagesToDuplicate.clear(); + + if (bUndo) + mrSlideSorter.GetView().EndUndo(); + + // Set the selection to the pages in aPagesToSelect. + PageSelector& rSelector (mrSlideSorter.GetController().GetPageSelector()); + rSelector.DeselectAllPages(); + for (auto const& it: aPagesToSelect) + { + rSelector.SelectPage(it); + } + + collectUIInformation({{"POS", OUString::number(nInsertPosition + 2)}}, "Duplicate"); +} + +void SlotManager::ChangeSlideExclusionState ( + const model::SharedPageDescriptor& rpDescriptor, + const bool bExcludeSlide) +{ + if (rpDescriptor) + { + mrSlideSorter.GetView().SetState( + rpDescriptor, + model::PageDescriptor::ST_Excluded, + bExcludeSlide); + } + else + { + model::PageEnumeration aSelectedPages ( + model::PageEnumerationProvider::CreateSelectedPagesEnumeration( + mrSlideSorter.GetModel())); + while (aSelectedPages.HasMoreElements()) + { + model::SharedPageDescriptor pDescriptor (aSelectedPages.GetNextElement()); + mrSlideSorter.GetView().SetState( + pDescriptor, + model::PageDescriptor::ST_Excluded, + bExcludeSlide); + } + } + + SfxBindings& rBindings (mrSlideSorter.GetViewShell()->GetViewFrame()->GetBindings()); + rBindings.Invalidate(SID_PRESENTATION); + rBindings.Invalidate(SID_REHEARSE_TIMINGS); + rBindings.Invalidate(SID_HIDE_SLIDE); + rBindings.Invalidate(SID_SHOW_SLIDE); + mrSlideSorter.GetModel().GetDocument()->SetChanged(); +} + +sal_Int32 SlotManager::GetInsertionPosition() const +{ + PageSelector& rSelector (mrSlideSorter.GetController().GetPageSelector()); + + // The insertion indicator is preferred. After all the user explicitly + // used it to define the insertion position. + if (mrSlideSorter.GetController().GetInsertionIndicatorHandler()->IsActive()) + { + // Select the page before the insertion indicator. + return mrSlideSorter.GetController().GetInsertionIndicatorHandler()->GetInsertionPageIndex() + - 1; + } + + // Is there a stored insertion position? + else if (mrSlideSorter.GetController().GetSelectionManager()->GetInsertionPosition() >= 0) + { + return mrSlideSorter.GetController().GetSelectionManager()->GetInsertionPosition() - 1; + } + + // Use the index of the last selected slide. + else if (rSelector.GetSelectedPageCount() > 0) + { + for (int nIndex=rSelector.GetPageCount()-1; nIndex>=0; --nIndex) + if (rSelector.IsPageSelected(nIndex)) + return nIndex; + + // We should never get here. + OSL_ASSERT(false); + return rSelector.GetPageCount() - 1; + } + + // Select the last page when there is at least one page. + else if (rSelector.GetPageCount() > 0) + { + return rSelector.GetPageCount() - 1; + } + + // Hope for the best that CreateOrDuplicatePage() can cope with an empty + // selection. + else + { + // We should never get here because there has to be at least one page. + OSL_ASSERT(false); + return -1; + } +} + +void SlotManager::NotifyEditModeChange() +{ + SfxBindings& rBindings (mrSlideSorter.GetViewShell()->GetViewFrame()->GetBindings()); + rBindings.Invalidate(SID_PRESENTATION); + rBindings.Invalidate(SID_INSERTPAGE); + rBindings.Invalidate(SID_DUPLICATE_PAGE); +} + +namespace { + +SlideExclusionState GetSlideExclusionState (model::PageEnumeration& rPageSet) +{ + SlideExclusionState eState (UNDEFINED); + + // Get toggle state of the selected pages. + while (rPageSet.HasMoreElements() && eState!=MIXED) + { + const bool bState = rPageSet.GetNextElement()->GetPage()->IsExcluded(); + switch (eState) + { + case UNDEFINED: + // Use the first selected page to set the initial value. + eState = bState ? EXCLUDED : INCLUDED; + break; + + case EXCLUDED: + // The pages before where all not part of the show, + // this one is. + if ( ! bState) + eState = MIXED; + break; + + case INCLUDED: + // The pages before where all part of the show, + // this one is not. + if (bState) + eState = MIXED; + break; + + default: + // No need to change anything. + break; + } + } + + return eState; +} + +} // end of anonymous namespace + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsTransferableData.cxx b/sd/source/ui/slidesorter/controller/SlsTransferableData.cxx new file mode 100644 index 000000000..f4b89a5ab --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsTransferableData.cxx @@ -0,0 +1,86 @@ +/* -*- 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 <controller/SlsTransferableData.hxx> + +#include <SlideSorterViewShell.hxx> + +namespace sd::slidesorter::controller { + +rtl::Reference<SdTransferable> TransferableData::CreateTransferable ( + SdDrawDocument* pSrcDoc, + SlideSorterViewShell* pViewShell, + ::std::vector<Representative>&& rRepresentatives) +{ + rtl::Reference<SdTransferable> pTransferable = new SdTransferable (pSrcDoc, nullptr, false/*bInitOnGetData*/); + auto pData = std::make_shared<TransferableData>(pViewShell, std::move(rRepresentatives)); + pTransferable->AddUserData(pData); + return pTransferable; +} + +std::shared_ptr<TransferableData> TransferableData::GetFromTransferable (const SdTransferable* pTransferable) +{ + if (pTransferable) + { + for (sal_Int32 nIndex=0,nCount=pTransferable->GetUserDataCount(); nIndex<nCount; ++nIndex) + { + std::shared_ptr<TransferableData> xData = + std::dynamic_pointer_cast<TransferableData>(pTransferable->GetUserData(nIndex)); + if (xData) + return xData; + } + } + return std::shared_ptr<TransferableData>(); +} + +TransferableData::TransferableData ( + SlideSorterViewShell* pViewShell, + ::std::vector<Representative>&& rRepresentatives) + : mpViewShell(pViewShell), + maRepresentatives(std::move(rRepresentatives)) +{ + if (mpViewShell != nullptr) + StartListening(*mpViewShell); +} + +TransferableData::~TransferableData() +{ + if (mpViewShell != nullptr) + EndListening(*mpViewShell); +} + +void TransferableData::Notify (SfxBroadcaster&, const SfxHint& rHint) +{ + if (mpViewShell) + { + if (rHint.GetId() == SfxHintId::Dying) + { + // This hint may come either from the ViewShell or from the + // document (registered by SdTransferable). We do not know + // which but both are sufficient to disconnect from the + // ViewShell. + EndListening(*mpViewShell); + mpViewShell = nullptr; + } + } +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsVisibleAreaManager.cxx b/sd/source/ui/slidesorter/controller/SlsVisibleAreaManager.cxx new file mode 100644 index 000000000..6f85f362d --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsVisibleAreaManager.cxx @@ -0,0 +1,234 @@ +/* -*- 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 <sal/config.h> + +#include <controller/SlsVisibleAreaManager.hxx> +#include <controller/SlideSorterController.hxx> +#include <controller/SlsAnimationFunction.hxx> +#include <controller/SlsScrollBarManager.hxx> +#include <controller/SlsCurrentSlideManager.hxx> +#include <Window.hxx> +#include <SlideSorter.hxx> +#include <view/SlideSorterView.hxx> + +namespace sd::slidesorter::controller { + +namespace { + class VisibleAreaScroller + { + public: + VisibleAreaScroller ( + SlideSorter& rSlideSorter, + const Point& rStart, + const Point& rEnd); + void operator() (const double nValue); + private: + SlideSorter& mrSlideSorter; + Point maStart; + const Point maEnd; + const ::std::function<double (double)> maAccelerationFunction; + }; + +} // end of anonymous namespace + +VisibleAreaManager::VisibleAreaManager (SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter), + mbIsCurrentSlideTrackingActive(true), + mnDisableCount(0) +{ +} + +VisibleAreaManager::~VisibleAreaManager() +{ +} + +void VisibleAreaManager::ActivateCurrentSlideTracking() +{ + mbIsCurrentSlideTrackingActive = true; +} + +void VisibleAreaManager::DeactivateCurrentSlideTracking() +{ + mbIsCurrentSlideTrackingActive = false; +} + +void VisibleAreaManager::RequestVisible ( + const model::SharedPageDescriptor& rpDescriptor, + const bool bForce) +{ + if (!rpDescriptor) + return; + + if (mnDisableCount == 0) + { + maVisibleRequests.push_back( + mrSlideSorter.GetView().GetLayouter().GetPageObjectBox( + rpDescriptor->GetPageIndex(), + true)); + } + if (bForce && ! mbIsCurrentSlideTrackingActive) + ActivateCurrentSlideTracking(); + MakeVisible(); +} + +void VisibleAreaManager::RequestCurrentSlideVisible() +{ + if (mbIsCurrentSlideTrackingActive && mnDisableCount==0) + RequestVisible( + mrSlideSorter.GetController().GetCurrentSlideManager()->GetCurrentSlide()); +} + +void VisibleAreaManager::MakeVisible() +{ + if (maVisibleRequests.empty()) + return; + + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if ( ! pWindow) + return; + const Point aCurrentTopLeft (pWindow->PixelToLogic(Point(0,0))); + + const ::std::optional<Point> aNewVisibleTopLeft (GetRequestedTopLeft()); + maVisibleRequests.clear(); + if ( ! aNewVisibleTopLeft) + return; + + maRequestedVisibleTopLeft = *aNewVisibleTopLeft; + VisibleAreaScroller aAnimation( + mrSlideSorter, + aCurrentTopLeft, + maRequestedVisibleTopLeft); + // Execute the animation at its final value. + aAnimation(1.0); +} + +::std::optional<Point> VisibleAreaManager::GetRequestedTopLeft() const +{ + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if ( ! pWindow) + return ::std::optional<Point>(); + + // Get the currently visible area and the model area. + const ::tools::Rectangle aVisibleArea (pWindow->PixelToLogic( + ::tools::Rectangle( + Point(0,0), + pWindow->GetOutputSizePixel()))); + const ::tools::Rectangle aModelArea (mrSlideSorter.GetView().GetModelArea()); + + sal_Int32 nVisibleTop (aVisibleArea.Top()); + const sal_Int32 nVisibleWidth (aVisibleArea.GetWidth()); + sal_Int32 nVisibleLeft (aVisibleArea.Left()); + const sal_Int32 nVisibleHeight (aVisibleArea.GetHeight()); + + // Find the longest run of boxes whose union fits into the visible area. + for (const auto& rBox : maVisibleRequests) + { + if (nVisibleTop+nVisibleHeight <= rBox.Bottom()) + nVisibleTop = rBox.Bottom()-nVisibleHeight; + if (nVisibleTop > rBox.Top()) + nVisibleTop = rBox.Top(); + + if (nVisibleLeft+nVisibleWidth <= rBox.Right()) + nVisibleLeft = rBox.Right()-nVisibleWidth; + if (nVisibleLeft > rBox.Left()) + nVisibleLeft = rBox.Left(); + + // Make sure the visible area does not move outside the model area. + if (nVisibleTop + nVisibleHeight > aModelArea.Bottom()) + nVisibleTop = aModelArea.Bottom() - nVisibleHeight; + if (nVisibleTop < aModelArea.Top()) + nVisibleTop = aModelArea.Top(); + + if (nVisibleLeft + nVisibleWidth > aModelArea.Right()) + nVisibleLeft = aModelArea.Right() - nVisibleWidth; + if (nVisibleLeft < aModelArea.Left()) + nVisibleLeft = aModelArea.Left(); + } + + const Point aRequestedTopLeft (nVisibleLeft, nVisibleTop); + if (aRequestedTopLeft == aVisibleArea.TopLeft()) + return ::std::optional<Point>(); + else + return ::std::optional<Point>(aRequestedTopLeft); +} + +//===== VisibleAreaManager::TemporaryDisabler ================================= + +VisibleAreaManager::TemporaryDisabler::TemporaryDisabler (SlideSorter const & rSlideSorter) + : mrVisibleAreaManager(rSlideSorter.GetController().GetVisibleAreaManager()) +{ + ++mrVisibleAreaManager.mnDisableCount; +} + +VisibleAreaManager::TemporaryDisabler::~TemporaryDisabler() +{ + --mrVisibleAreaManager.mnDisableCount; +} + +//===== VerticalVisibleAreaScroller =========================================== + +namespace { + +const sal_Int32 gnMaxScrollDistance = 300; + +VisibleAreaScroller::VisibleAreaScroller ( + SlideSorter& rSlideSorter, + const Point& rStart, + const Point& rEnd) + : mrSlideSorter(rSlideSorter), + maStart(rStart), + maEnd(rEnd), + maAccelerationFunction( + controller::AnimationParametricFunction( + controller::AnimationBezierFunction (0.1,0.6))) +{ + // When the distance to scroll is larger than a threshold then first + // jump to within this distance of the final value and start the + // animation from there. + if (std::abs(rStart.X()-rEnd.X()) > gnMaxScrollDistance) + { + if (rStart.X() < rEnd.X()) + maStart.setX( rEnd.X()-gnMaxScrollDistance ); + else + maStart.setX( rEnd.X()+gnMaxScrollDistance ); + } + if (std::abs(rStart.Y()-rEnd.Y()) > gnMaxScrollDistance) + { + if (rStart.Y() < rEnd.Y()) + maStart.setY( rEnd.Y()-gnMaxScrollDistance ); + else + maStart.setY( rEnd.Y()+gnMaxScrollDistance ); + } +} + +void VisibleAreaScroller::operator() (const double nTime) +{ + const double nLocalTime (maAccelerationFunction(nTime)); + mrSlideSorter.GetController().GetScrollBarManager().SetTopLeft( + Point( + sal_Int32(0.5 + maStart.X() * (1.0 - nLocalTime) + maEnd.X() * nLocalTime), + sal_Int32 (0.5 + maStart.Y() * (1.0 - nLocalTime) + maEnd.Y() * nLocalTime))); +} + +} // end of anonymous namespace + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/cache/SlsCacheContext.hxx b/sd/source/ui/slidesorter/inc/cache/SlsCacheContext.hxx new file mode 100644 index 000000000..12993fdbb --- /dev/null +++ b/sd/source/ui/slidesorter/inc/cache/SlsCacheContext.hxx @@ -0,0 +1,98 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/types.h> +#include <com/sun/star/uno/Reference.hxx> +#include <memory> +#include <vector> + +namespace com::sun::star::uno +{ +class XInterface; +} + +class SdrPage; + +namespace sd::slidesorter::cache +{ +typedef const SdrPage* CacheKey; + +/** This interface allows the individualisation of different instances of + the PreviewCache. +*/ +class CacheContext +{ +public: + virtual ~CacheContext() {} + + /** This method is called when the asynchronous creation of a preview + has been finished. + @param aKey + The key of the page for which the preview has been created. + */ + virtual void NotifyPreviewCreation(CacheKey aKey) = 0; + + /** Called to determine whether the system is idle and a preview can be + created without annoying the user. + */ + virtual bool IsIdle() = 0; + + /** This method is used to determine whether a page is currently visible + or not. It is called when the cache becomes too large and some + previews have to be released or scaled down. + */ + virtual bool IsVisible(CacheKey aKey) = 0; + + /** Return the page associated with the given key. Note that different + keys may map to a single page (this may be the case with custom + slide shows.) + */ + virtual const SdrPage* GetPage(CacheKey aKey) = 0; + + /** This method is used when the request queue is filled. It asks for + the list of visible entries and maybe for the list of not visible + entries and creates preview creation requests for them. + @param bVisible + When this is <FALSE/> then the implementation can decide whether + to allow rendering of previews that are not visible (ahead of + time). When not then return an empty pointer or an empty vector. + */ + virtual std::shared_ptr<std::vector<CacheKey>> GetEntryList(bool bVisible) = 0; + + /** Return the priority that defines the order in which previews are + created for different keys/pages. Typically the visible pages come + first, then top-down, left-to-right. + */ + virtual sal_Int32 GetPriority(CacheKey aKey) = 0; + + /** Return the model to which the pages belong for which the called + cache manages the previews. Different caches that belong to the + same model but have different preview sizes may access previews of + each other in order to create fast previews of the previews. + */ + virtual css::uno::Reference<css::uno::XInterface> GetModel() = 0; +}; + +typedef std::shared_ptr<CacheContext> SharedCacheContext; + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/cache/SlsPageCache.hxx b/sd/source/ui/slidesorter/inc/cache/SlsPageCache.hxx new file mode 100644 index 000000000..4fb596195 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/cache/SlsPageCache.hxx @@ -0,0 +1,141 @@ +/* -*- 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 . + */ + +#pragma once + +#include <cache/SlsCacheContext.hxx> +#include <vcl/bitmapex.hxx> +#include <memory> + +class Size; + +namespace sd::slidesorter::cache +{ +class GenericPageCache; + +/** The page cache is responsible for the creation and storage of preview + bitmaps of pages that are shown by the slide sorter. + + <p>Bitmaps for previews and a cache are used to speed up the display + (painting) of the slide sorter. But, of course, we have to limit this + time-space-tradeoff by limiting the amount of space that can be use to + store bitmaps.</p> + + <p>There are several strategies employed by this class to shorten the + perceived time that is used to paint the slide sorter: + <ul> + <li>Rendering pages ahead of time. Additionally to rendering the + visible slides we try to render part or all of the slides that are not + (yet) visible. This, of course, makes sense only when the computer is + otherwise idle while doing that.</li> + <li>When the size of the slides on the screen changes we mark the + bitmaps as needing an update but use them while the new bitmap in the + correct size is not available.</li> + <li>Give the UI the chance to handle user events between the rendering + of differe slides.</li> + <li>Limit the amount of space that may be used for storing preview + bitmaps and throw.</li> + </p> + + <p>There is another somewhat similar methods for requesting new previews: + GetPreviewBitmap() schedules a re-rendering (when necessary) and + returns the preview what is currently available, either as a preview of + the preview or, when nothing has changed since the last call, as the + final thing. + </p> +*/ +class PageCache +{ +public: + /** The page cache is created with a reference to the slide sorter so + that it has access to both the view and the model and so can fill + itself with requests for all or just the visible pages. + + It is the task of the PageCacheManager to create new objects of this + class. + */ + PageCache(const Size& rPreviewSize, const bool bDoSuperSampling, + const SharedCacheContext& rpCacheContext); + + ~PageCache(); + + void ChangeSize(const Size& rPreviewSize, const bool bDoSuperSampling); + + /** Request a preview bitmap for the specified page object in the + specified size. The returned bitmap may be a preview of the + preview, i.e. either a scaled (up or down) version of a previous + preview (of the wrong size) or an empty bitmap. In this case a + request for the generation of a new preview is created and inserted + into the request queue. When the preview is available in the right + size the page shape will be told to paint itself again. When it + then calls this method again if receives the correctly sized preview + bitmap. + @param rRequestData + This data is used to determine the preview. + @param bResize + When <TRUE/> then when the available bitmap has not the + requested size, it is scaled before it is returned. When + <FALSE/> then the bitmap is returned in the wrong size and it is + the task of the caller to scale it. + @return + Returns a bitmap that is either empty, contains a scaled (up or + down) version or is the requested bitmap. + */ + BitmapEx GetPreviewBitmap(const CacheKey aKey, const bool bResize); + + BitmapEx GetMarkedPreviewBitmap(const CacheKey aKey); + void SetMarkedPreviewBitmap(const CacheKey aKey, const BitmapEx& rBitmap); + + /** When the requested preview bitmap does not yet exist or is not + up-to-date then the rendering of one is scheduled. Otherwise this + method does nothing. + */ + void RequestPreviewBitmap(const CacheKey aKey); + + /** Tell the cache that the bitmap associated with the given request + data is not up-to-date anymore. This will invalidate all previews + in other caches that represent the same page as well. + A new preview is requested and will lead + eventually to a repaint of the associated page object. + */ + void InvalidatePreviewBitmap(const CacheKey aKey); + + /** Call this method when all preview bitmaps have to be generated anew. + This is the case when the size of the page objects on the screen has + changed or when the model has changed. + */ + void InvalidateCache(); + + /** With the precious flag you can control whether a bitmap can be + removed or reduced in size to make room for other bitmaps or is so + precious that it will not touched. A typical use is to set the + precious flag for exactly the visible pages. + */ + void SetPreciousFlag(const CacheKey aKey, const bool bIsPrecious); + + void Pause(); + void Resume(); + +private: + std::unique_ptr<GenericPageCache> mpImplementation; +}; + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/cache/SlsPageCacheManager.hxx b/sd/source/ui/slidesorter/inc/cache/SlsPageCacheManager.hxx new file mode 100644 index 000000000..eaddea5b2 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/cache/SlsPageCacheManager.hxx @@ -0,0 +1,155 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/types.h> +#include <com/sun/star/uno/XInterface.hpp> +#include <memory> +#include <vector> + +class Size; +class SdrPage; + +namespace sd::slidesorter::cache { + +class BitmapCache; + +/** Provide and manage the preview bitmap caches for all slide sorter + instances. There is one cache per active slide sorter plus a small + number of caches that are no longer in use. The later are kept to speed + up the switching between views. +*/ +class PageCacheManager +{ +public: + typedef std::vector< std::pair<Size, std::shared_ptr<BitmapCache> > > BestFittingPageCaches; + typedef css::uno::Reference<css::uno::XInterface> DocumentKey; + + /** Return the one instance of the PageCacheManager class. + */ + static std::shared_ptr<PageCacheManager> Instance(); + + /** Look up the cache for the given model in which the previews have the + specified size. If no such cache exists, then one is created. When + a new BitmapCache is created its Recycle() method is called with a + sorted list of existing caches from which the new one initialize its + previews. + @return + The returned cache lives as long as somebody keeps a shared + pointer and the ReleaseCache() method has not been called. + */ + std::shared_ptr<BitmapCache> GetCache ( + const DocumentKey& pDocument, + const Size& rPreviewSize); + + /** Tell the cache manager to release its own reference to the specified + cache. After that the cache will live as long as the caller (and + maybe others) holds its reference. + */ + void ReleaseCache (const std::shared_ptr<BitmapCache>& rpCache); + + /** This is an information to the cache manager that the size of preview + bitmaps in the specified cache has changed. + + */ + std::shared_ptr<BitmapCache> ChangeSize ( + const std::shared_ptr<BitmapCache>& rpCache, + const Size& rOldPreviewSize, + const Size& rNewPreviewSize); + + /** Invalidate the preview bitmap for one slide that belongs to the + specified document. The bitmaps for this slide in all caches are + marked as out-of-date and will be re-created when they are requested + the next time. + */ + bool InvalidatePreviewBitmap ( + const DocumentKey& pDocument, + const SdrPage* pPage); + + /** Invalidate the preview bitmaps for all slides that belong to the + specified document. This is necessary after model changes that + affect e.g. page number fields. + */ + void InvalidateAllPreviewBitmaps (const DocumentKey& pDocument); + + /** Invalidate all the caches that are currently in use and destroy + those that are not. This is used for example when the high contrast + mode is turned on or off. + */ + void InvalidateAllCaches(); + + /** Call this method when a page has been deleted and its preview + is not needed anymore. + */ + void ReleasePreviewBitmap (const SdrPage* pPage); + +private: + /** Singleton instance of the cache manager. Note that this is a weak + pointer. The (implementation class of) ViewShellBase holds a + shared_ptr so that the cache manager has the same life time as the + ViewShellBase. + */ + static std::weak_ptr<PageCacheManager> mpInstance; + + /// List of active caches. + class PageCacheContainer; + std::unique_ptr<PageCacheContainer> mpPageCaches; + + /// List of inactive, recently used caches. + class RecentlyUsedPageCaches; + std::unique_ptr<RecentlyUsedPageCaches> mpRecentlyUsedPageCaches; + + /** The maximal number of recently used caches that are kept alive after + they have become inactive, i.e. after they are not used anymore by a + slide sorter. + */ + static const sal_uInt32 mnMaximalRecentlyCacheCount = 2; + + PageCacheManager(); + ~PageCacheManager(); + + class Deleter; + friend class Deleter; + + std::shared_ptr<BitmapCache> GetRecentlyUsedCache( + const DocumentKey& pDocument, + const Size& rSize); + + /** Add the given cache to the list of recently used caches for the + document. There is one such list per document. Each least has at + most mnMaximalRecentlyCacheCount members. + */ + void PutRecentlyUsedCache( + DocumentKey const & pDocument, + const Size& rPreviewSize, + const std::shared_ptr<BitmapCache>& rpCache); + + /** This method is used internally to initialize a newly created + BitmapCache with already existing previews. + */ + void Recycle ( + const std::shared_ptr<BitmapCache>& rpCache, + const DocumentKey& pDocument, + const Size& rPreviewSize); +}; + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlideSorterController.hxx b/sd/source/ui/slidesorter/inc/controller/SlideSorterController.hxx new file mode 100644 index 000000000..10c2aa13e --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlideSorterController.hxx @@ -0,0 +1,327 @@ +/* -*- 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 . + */ + +#pragma once + +#include <model/SlsSharedPageDescriptor.hxx> +#include <pres.hxx> + +#include <tools/link.hxx> +#include <tools/gen.hxx> +#include <rtl/ref.hxx> +#include <rtl/ustring.hxx> + +#include <sddllapi.h> + +#include <memory> +#include <vector> + +namespace com::sun::star::container +{ +class XIndexAccess; +} +namespace com::sun::star::uno +{ +template <typename> class Reference; +} +namespace sd +{ +class FuPoor; +} +namespace sd +{ +class Window; +} +namespace vcl +{ +class Window; +} + +namespace sd::slidesorter +{ +class SlideSorter; +} +namespace sd::slidesorter::view +{ +class SlideSorterView; +} +namespace sd::slidesorter::model +{ +class SlideSorterModel; +} + +class CommandEvent; +class SdPage; +class SfxItemSet; +class SfxRequest; +class VclSimpleEvent; +class VclWindowEvent; + +namespace sd::slidesorter::controller +{ +class Animator; +class Clipboard; +class CurrentSlideManager; +class FocusManager; +class InsertionIndicatorHandler; +class Listener; +class PageSelector; +class ScrollBarManager; +class SelectionFunction; +class SelectionManager; +class SlotManager; +class VisibleAreaManager; + +class SlideSorterController final +{ +public: + /** Create a new controller for the slide sorter. + @param pParentWindow + The window that contains the controls of the new + controller. + */ + SlideSorterController(SlideSorter& rSlideSorter); + + /** Late initialization. Call this method once a new object has been + created. + */ + void Init(); + + ~SlideSorterController(); + + void Dispose(); + + /** Place and size the scroll bars and the browser window so that the + given rectangle is filled. + */ + void Resize(const ::tools::Rectangle& rAvailableSpace); + + /** Determine which of the UI elements--the scroll bars, the scroll bar + filler, the actual slide sorter view--are visible and place them in + the area last passed to Resize(). + @param bForce + When <TRUE/> is given (<FALSE/> is the default) then the content + window and with it the SlideSorterView is resized event when its + size does not change (the size does change when the visibility + of scroll bars changes.) + */ + void Rearrange(bool bForce); + + /** Return the descriptor of the page that is rendered under the + given position. This takes the IsOnlyPreviewTriggersMouseOver + property into account. + @return + Returns a pointer to a page descriptor instead of a + reference because when no page is found at the position + then NULL is returned to indicate this. + */ + model::SharedPageDescriptor GetPageAt(const Point& rPixelPosition); + + // Exported for unit test + SD_DLLPUBLIC PageSelector& GetPageSelector(); + FocusManager& GetFocusManager(); + // Exported for unit test + SD_DLLPUBLIC controller::Clipboard& GetClipboard(); + + /** Return the object that manages the scroll bars. + */ + ScrollBarManager& GetScrollBarManager(); + + std::shared_ptr<CurrentSlideManager> const& GetCurrentSlideManager() const; + std::shared_ptr<SlotManager> const& GetSlotManager() const; + std::shared_ptr<SelectionManager> const& GetSelectionManager() const; + std::shared_ptr<InsertionIndicatorHandler> const& GetInsertionIndicatorHandler() const; + + /** This method forwards the call to the SlideSorterView and executes + pending operations like moving selected pages into the visible area. + */ + void Paint(const ::tools::Rectangle& rRect, vcl::Window* pWin); + + void FuTemporary(SfxRequest& rRequest); + void FuPermanent(SfxRequest& rRequest); + void FuSupport(SfxRequest& rRequest); + bool Command(const CommandEvent& rEvent, ::sd::Window* pWindow); + + void GetCtrlState(SfxItemSet& rSet); + void GetStatusBarState(SfxItemSet& rSet); + + void ExecCtrl(SfxRequest& rRequest); + void GetAttrState(SfxItemSet& rSet); + + /** Create an object of this inner class to prevent updates due to model + changes. + */ + class ModelChangeLock + { + public: + ModelChangeLock(SlideSorterController& rController); + ~ModelChangeLock() COVERITY_NOEXCEPT_FALSE; + void Release(); + + private: + SlideSorterController* mpController; + }; + friend class ModelChangeLock; + + /** Handle a change of the model, that is, handle the removal and + insertion of whole pages or a change of the edit mode. + + This method is a convenience function that simply calls + PreModelChange() and then PostModelChange(). + */ + void HandleModelChange(); + + DECL_LINK(WindowEventHandler, VclWindowEvent&, void); + DECL_LINK(ApplicationEventHandler, VclSimpleEvent&, void); + + /** Update the display of all pages. This involves a redraw and + releasing previews and caches. + */ + void UpdateAllPages(); + + /** This factory method creates a selection function. + */ + rtl::Reference<FuPoor> CreateSelectionFunction(SfxRequest& rRequest); + + /** When the current function of the view shell is the slide sorter + selection function then return a reference to it. Otherwise return + an empty reference. + */ + ::rtl::Reference<SelectionFunction> GetCurrentSelectionFunction() const; + + /** Prepare for a change of the edit mode. Depending on the current + edit mode we may save the selection so that it can be restored when + later changing back to the current edit mode. + */ + void PrepareEditModeChange(); + + /** Set a new edit mode and return whether the edit mode really + has been changed. For proper saving and restoring of the selection + this method should be called between calls to + PrepareEditModeChange() and FinishEditModeChange(). + */ + void ChangeEditMode(EditMode eEditMode); + + /** Finish the change of the edit mode. Here we may select a page or + restore a previously saved selection. + */ + void FinishEditModeChange(); + + /** Call this method when the name of one of the pages has changed. + This is then notified to the accessibility object, when that exists. + @param nPageIndex + The index of the page whose name has been changed. + @param rsOldName + The old name of the page. The new name can be taken from the + page object. + */ + void PageNameHasChanged(int nPageIndex, const OUString& rsOldName); + + /** Provide the set of pages to be displayed in the slide sorter. The + GetDocumentSlides() method can be found only in the SlideSorterModel. + */ + void SetDocumentSlides(const css::uno::Reference<css::container::XIndexAccess>& rxSlides); + + /** Return an Animator object. + */ + const std::shared_ptr<Animator>& GetAnimator() const { return mpAnimator; } + + VisibleAreaManager& GetVisibleAreaManager() const; + + void CheckForMasterPageAssignment(); + void CheckForSlideTransitionAssignment(); + +private: + SlideSorter& mrSlideSorter; + model::SlideSorterModel& mrModel; + view::SlideSorterView& mrView; + std::unique_ptr<PageSelector> mpPageSelector; + std::unique_ptr<FocusManager> mpFocusManager; + std::shared_ptr<SlotManager> mpSlotManager; + std::unique_ptr<ScrollBarManager> mpScrollBarManager; + mutable std::shared_ptr<CurrentSlideManager> mpCurrentSlideManager; + std::shared_ptr<SelectionManager> mpSelectionManager; + std::unique_ptr<controller::Clipboard> mpClipboard; + std::shared_ptr<InsertionIndicatorHandler> mpInsertionIndicatorHandler; + std::shared_ptr<Animator> mpAnimator; + std::unique_ptr<VisibleAreaManager> mpVisibleAreaManager; + + // The listener listens to UNO events and thus is a UNO object. + ::rtl::Reference<controller::Listener> mpListener; + + int mnModelChangeLockCount; + bool mbIsForcedRearrangePending; + bool mbContextMenuOpen; + + bool mbPostModelChangePending; + + /** This array stores the indices of the selected page descriptors at + the time when the edit mode is switched to EditMode::MasterPage. With this + we can restore the selection when switching back to EditMode::Page mode. + */ + ::std::vector<SdPage*> maSelectionBeforeSwitch; + /// The current page before the edit mode is switched to EditMode::MasterPage. + int mnCurrentPageBeforeSwitch; + + /** The master page to select after the edit mode is changed. This + member is used to pass the pointer from PrepareEditModeChange() to + FinishEditModeChange(). + */ + SdPage* mpEditModeChangeMasterPage; + + /** This rectangle in the parent window encloses scroll bars and slide + sorter window. It is set when Resize() is called. + */ + ::tools::Rectangle maTotalWindowArea; + + /** This counter is used to avoid processing of reentrant calls to + Paint(). + */ + sal_Int32 mnPaintEntranceCount; + + /** Prepare for several model changes, i.e. prevent time-consuming and + non-critical operations like repaints until UnlockModelChange() is + called. Critical operations like releasing references to pages that + do not exist anymore are executed. + */ + void LockModelChange(); + + /** Further calls to HandleModelChange() will result in a full featured + update of model, view, and controller. When HandleModelChange() has + been called since the last LockModelChange() then this is done right + away to bring the view up-to-date. + */ + void UnlockModelChange(); + + /** Prepare for a model change. This method does all the things that + need to be done _before_ the model changes, e.g. because they need + access to the model data before the change. + */ + void PreModelChange(); + + /** Complete a model change. This includes the recreation of data + structures that depend on the model and the request for a repaint to + show the changes. + */ + void PostModelChange(); +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsAnimationFunction.hxx b/sd/source/ui/slidesorter/inc/controller/SlsAnimationFunction.hxx new file mode 100644 index 000000000..b4847de1a --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsAnimationFunction.hxx @@ -0,0 +1,77 @@ +/* -*- 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 . + */ + +#pragma once + +#include <basegfx/point/b2dpoint.hxx> + +#include <functional> +#include <vector> + + +namespace sd::slidesorter::controller { + +class AnimationBezierFunction +{ +public: + /** Create a cubic bezier curve whose start and end points are given + implicitly as P0=(0,0) and P3=(1,1). The second control point is + implicitly given as P2=(1-nY1,1-nX1). + */ + AnimationBezierFunction ( + const double nX1, + const double nY1); + + ::basegfx::B2DPoint operator() (const double nT); + +private: + const double mnX1; + const double mnY1; + const double mnX2; + const double mnY2; + + static double EvaluateComponent ( + const double nT, + const double nV1, + const double nV2); +}; + +/** Turn a parametric function into one whose y-Values depend on its + x-Values. Note a lot of interpolation takes place. The resulting + accuracy should be good enough for the purpose of acceleration + function for animations. +*/ +class AnimationParametricFunction +{ +public: + typedef ::std::function<basegfx::B2DPoint (double)> ParametricFunction; + AnimationParametricFunction (const ParametricFunction& rFunction); + + double operator() (const double nX); + +private: + /** y-Values of the parametric function given to the constructor + evaluated (and interpolated) for evenly spaced x-Values. + */ + ::std::vector<double> maY; +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsAnimator.hxx b/sd/source/ui/slidesorter/inc/controller/SlsAnimator.hxx new file mode 100644 index 000000000..8c9ec9e81 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsAnimator.hxx @@ -0,0 +1,122 @@ +/* -*- 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 . + */ + +#pragma once + +#include <view/SlideSorterView.hxx> +#include <canvas/elapsedtime.hxx> +#include <vcl/idle.hxx> +#include <sal/types.h> +#include <o3tl/deleter.hxx> + +#include <functional> +#include <memory> +#include <vector> + +namespace sd::slidesorter { class SlideSorter; } + +namespace sd::slidesorter::controller { + +/** Experimental class for simple eye candy animations. +*/ +class Animator +{ +public: + /** In some circumstances we have to avoid animation and jump to the + final animation state immediately. Use this enum instead of a bool + to be more expressive. + */ + enum AnimationMode { AM_Animated, AM_Immediate }; + + explicit Animator (SlideSorter& rSlideSorter); + ~Animator(); + Animator(const Animator&) = delete; + Animator& operator=(const Animator&) = delete; + + /** When disposed the animator will stop its work immediately and not + process any timer events anymore. + */ + void Dispose(); + + /** An animation object is called with values between 0 and 1 as single + argument to its operator() method. + */ + typedef ::std::function<void (double)> AnimationFunctor; + typedef ::std::function<void ()> FinishFunctor; + + typedef sal_Int32 AnimationId; + static const AnimationId NotAnAnimationId = -1; + + /** Schedule a new animation for execution. The () operator of that + animation will be called with increasing values between 0 and 1 for + the specified duration. + @param rAnimation + The animation operation. + */ + AnimationId AddAnimation ( + const AnimationFunctor& rAnimation, + const FinishFunctor& rFinishFunctor); + + /** Abort and remove an animation. In order to reduce the bookkeeping + on the caller side, it is OK to call this method with an animation + function that is not currently being animated. Such a call is + silently ignored. + */ + void RemoveAnimation (const AnimationId nAnimationId); + + /** A typical use case for this method is the temporary shutdown of the + slidesorter when the slide sorter bar is put into a cache due to a + change of the edit mode. + */ + void RemoveAllAnimations(); + +private: + SlideSorter& mrSlideSorter; + Idle maIdle; + bool mbIsDisposed; + class Animation; + typedef ::std::vector<std::shared_ptr<Animation> > AnimationList; + AnimationList maAnimations; + ::canvas::tools::ElapsedTime maElapsedTime; + + std::unique_ptr<view::SlideSorterView::DrawLock, o3tl::default_delete<view::SlideSorterView::DrawLock>> mpDrawLock; + + AnimationId mnNextAnimationId; + + DECL_LINK(TimeoutHandler, Timer *, void); + + /** Execute one step of every active animation. + @param nTime + Time measured in milliseconds with some arbitrary reference point. + @return + When one or more animation has finished then <TRUE/> is + returned. Call CleanUpAnimationList() in this case. + */ + bool ProcessAnimations (const double nTime); + + /** Remove animations that have expired. + */ + void CleanUpAnimationList(); + + void RequestNextFrame(); +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsClipboard.hxx b/sd/source/ui/slidesorter/inc/controller/SlsClipboard.hxx new file mode 100644 index 000000000..6ced17486 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsClipboard.hxx @@ -0,0 +1,208 @@ +/* -*- 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 . + */ + +#pragma once + +#include <memory> +#include <ViewClipboard.hxx> +#include <controller/SlsSelectionObserver.hxx> +#include <sdxfer.hxx> + +#include <sal/types.h> +#include <o3tl/deleter.hxx> +#include <svx/svdtypes.hxx> + +#include <sddllapi.h> + +class SfxRequest; +struct AcceptDropEvent; +class DropTargetHelper; +struct ExecuteDropEvent; +struct ImplSVEvent; +class Point; +class SdPage; +namespace vcl { class Window; } + +namespace sd { +class Window; +} + +namespace sd::slidesorter { class SlideSorter; } + +namespace sd::slidesorter::controller { + +class SlideSorterController; + +class SAL_DLLPUBLIC_RTTI Clipboard final + : public ViewClipboard +{ +public: + Clipboard (SlideSorter& rSlideSorter); + virtual ~Clipboard() override; + + /** Create a slide sorter transferable from the given sd + transferable. The returned transferable is set up with all + information necessary so that it can be dropped on a slide sorter. + */ + static std::shared_ptr<SdTransferable::UserData> CreateTransferableUserData (SdTransferable* pTransferable); + + void HandleSlotCall (SfxRequest& rRequest); + + void DoCut (); + // Exported for unit test + SD_DLLPUBLIC void DoCopy(); + // Exported for unit test + SD_DLLPUBLIC void DoPaste(); + void DoDelete (); + + void StartDrag ( + const Point& rDragPt, + vcl::Window* pWindow ); + + void DragFinished ( + sal_Int8 nDropAction); + + sal_Int8 AcceptDrop ( + const AcceptDropEvent& rEvt, + DropTargetHelper& rTargetHelper, + ::sd::Window* pTargetWindow, + sal_uInt16 nPage, + SdrLayerID nLayer ); + + sal_Int8 ExecuteDrop ( + const ExecuteDropEvent& rEvt, + DropTargetHelper& rTargetHelper, + ::sd::Window* pTargetWindow, + sal_uInt16 nPage, + SdrLayerID nLayer ); + + void Abort(); + +private: + virtual sal_uInt16 DetermineInsertPosition () override; + + SlideSorter& mrSlideSorter; + SlideSorterController& mrController; + + typedef ::std::vector<SdPage*> PageList; + /** Remember the pages that are dragged to another document or to + another place in the same document so that they can be removed after + a move operation. + */ + PageList maPagesToRemove; + + /** Used when a drop is executed to combine all undo actions into one. + Typically created in ExecuteDrop() and released in DragFinish(). + */ + class UndoContext; + std::unique_ptr<UndoContext> mxUndoContext; + + std::unique_ptr<SelectionObserver::Context, o3tl::default_delete<SelectionObserver::Context>> mxSelectionObserverContext; + ImplSVEvent * mnDragFinishedUserEventId; + + void CreateSlideTransferable ( + vcl::Window* pWindow, + bool bDrag); + + /** Determine the position of where to insert the pages in the current + transferable of the sd module. + @return + The index in the range [0,n] (both inclusive) with n the number + of pages is returned. + */ + sal_Int32 GetInsertionPosition (); + + /** Paste the pages of the transferable of the sd module at the given + position. + @param nInsertPosition + The position at which to insert the pages. The valid range is + [0,n] (both inclusive) with n the number of pages in the + document. + @return + The number of inserted pages is returned. + */ + sal_Int32 PasteTransferable (sal_Int32 nInsertPosition); + + /** Select a range of pages of the model. Typically usage is the + selection of newly inserted pages. + @param nFirstIndex + The index of the first page to select. + @param nPageCount + The number of pages to select. + */ + void SelectPageRange (sal_Int32 nFirstIndex, sal_Int32 nPageCount); + + /** Return <TRUE/> when the current transferable in the current state of + the slidesorter is acceptable to be pasted. For this the + transferable has to + a) exist, + b) contain one or more regular draw pages, no master pages. + When master pages are involved, either in the transferable or in the + slide sorter (by it displaying master pages) the drop of the + transferable is not accepted. The reason is the missing + implementation of proper handling master pages copy-and-paste. + */ + enum DropType { DT_PAGE, DT_PAGE_FROM_NAVIGATOR, DT_SHAPE, DT_NONE }; + DropType IsDropAccepted() const; + + /** This method contains the code for AcceptDrop() and ExecuteDrop() shapes. + There are only minor differences for the two cases at this level. + @param eCommand + This parameter specifies whether to do an AcceptDrop() or + ExecuteDrop(). + @param rPosition + Since the event is given as void pointer we can not take the + mouse position from it. The caller has to supply it in this + parameter. + @param pDropEvent + Event though the AcceptDropEvent and ExecuteDropEvent are very + similar they do not have a common base class. Because of that + we have to use a void* to pass these structs. + @param nPage + When the page number is given as 0xffff then it is replaced by + the number of the page at the mouse position. If the mouse is + not over a page then neither AcceptDrop() nor ExecuteDrop() are + executed. + */ + enum DropCommand { DC_ACCEPT, DC_EXECUTE }; + sal_Int8 ExecuteOrAcceptShapeDrop ( + DropCommand eCommand, + const Point& rPosition, + const void* pDropEvent , + DropTargetHelper& rTargetHelper, + ::sd::Window* pTargetWindow, + sal_uInt16 nPage, + SdrLayerID nLayer); + + /** Return whether the insertion defined by the transferable is + trivial, ie would not change either source nor target document. + */ + bool IsInsertionTrivial ( + SdTransferable const * pTransferable, + const sal_Int8 nDndAction) const; + + /** Asynchronous part of DragFinished. The argument is the sal_Int8 + nDropAction, disguised as void*. + */ + DECL_DLLPRIVATE_LINK(ProcessDragFinished, void*, void); +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsCurrentSlideManager.hxx b/sd/source/ui/slidesorter/inc/controller/SlsCurrentSlideManager.hxx new file mode 100644 index 000000000..0c5b3b243 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsCurrentSlideManager.hxx @@ -0,0 +1,112 @@ +/* -*- 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 . + */ + +#pragma once + +#include <model/SlsSharedPageDescriptor.hxx> +#include <vcl/timer.hxx> +#include <tools/link.hxx> + +class SdPage; + +namespace sd::slidesorter +{ +class SlideSorter; +} + +namespace sd::slidesorter::controller +{ +/** Manage the current slide. This includes setting the according flags at + the PageDescriptor objects and setting the current slide at the main + view shell. + + Switching pages is triggered only after a little delay. This allows + fast travelling through a larger set of slides without having to wait + for the edit view to update its content after every slide change. +*/ +class CurrentSlideManager +{ +public: + /** Create a new CurrentSlideManager object that manages the current + slide for the given SlideSorter. + */ + CurrentSlideManager(SlideSorter& rSlideSorter); + + ~CurrentSlideManager(); + + /** Call this when the current page of the main view shell has been + switched. Use SwitchCurrentSlide() to initiate such a switch. + */ + void NotifyCurrentSlideChange(const sal_Int32 nSlideIndex); + void NotifyCurrentSlideChange(const SdPage* pPage); + + /** Call this method to switch the current page of the main view shell + to the given slide. Use CurrentSlideHasChanged() when the current + slide change has been initiated by someone else. + @param nSlideIndex + Zero based index in the range [0,number-of-slides). + The page selection is cleared and only the new + current slide is selected. + */ + void SwitchCurrentSlide(const sal_Int32 nSlideIndex); + void SwitchCurrentSlide(const model::SharedPageDescriptor& rpSlide, + const bool bUpdateSelection = false); + + /** Return the page descriptor for the current slide. Note, that when + there is no current slide then the returned pointer is empty. + */ + const model::SharedPageDescriptor& GetCurrentSlide() const { return mpCurrentSlide; } + + /** Release all references to model data. + */ + void PrepareModelChange(); + + /** Modify inner state in reaction to a change of the SlideSorterModel. + */ + void HandleModelChange(); + +private: + SlideSorter& mrSlideSorter; + sal_Int32 mnCurrentSlideIndex; + model::SharedPageDescriptor mpCurrentSlide; + /** Timer to control the delay after which to ask + XController/ViewShellBase to switch to another slide. + */ + Timer maSwitchPageDelayTimer; + + void SetCurrentSlideAtViewShellBase(const model::SharedPageDescriptor& rpSlide); + void SetCurrentSlideAtTabControl(const model::SharedPageDescriptor& rpSlide); + void SetCurrentSlideAtXController(const model::SharedPageDescriptor& rpSlide); + + /** When switching from one slide to a new current slide then this + method releases all ties to the old slide. + */ + void ReleaseCurrentSlide(); + + /** When switching from one slide to a new current slide then this + method connects to the new current slide. + */ + void AcquireCurrentSlide(const sal_Int32 nSlideIndex); + + DECL_LINK(SwitchPageCallback, Timer*, void); +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsFocusManager.hxx b/sd/source/ui/slidesorter/inc/controller/SlsFocusManager.hxx new file mode 100644 index 000000000..180b7143f --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsFocusManager.hxx @@ -0,0 +1,211 @@ +/* -*- 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 . + */ + +#pragma once + +#include <model/SlsSharedPageDescriptor.hxx> + +#include <sal/types.h> +#include <tools/link.hxx> +#include <vector> + +namespace sd::slidesorter +{ +class SlideSorter; +} + +namespace sd::slidesorter::controller +{ +/** This class manages the focus of the slide sorter. There is the focus + page which is or is not focused. Initialized to point to the first page + it can be set to other pages by using the MoveFocus() method. The + focused state of the focus page can be toggled with the ToggleFocus() + method. +*/ +class FocusManager +{ +public: + /** Create a new focus manager that operates on the pages of the model + associated with the given controller. The focus page is set to the + first page. Focused state is off. + */ + FocusManager(SlideSorter& rSlideSorter); + + ~FocusManager(); + + enum class FocusMoveDirection + { + Left, + Right, + Up, + Down + }; + + /** Move the focus from the currently focused page to one that is + displayed adjacent to it, either vertically or horizontally. + @param eDirection + Direction in which to move the focus. Wrap around is done + differently when moving vertically or horizontally. Vertical + wrap around takes place in the same column, i.e. when you are + in the top row and move up you come out in the bottom row in the + same column. Horizontal wrap around moves to the next + (FocusMoveDirection::Right) or previous (FocusMoveDirection::Left) page. Moving to the right + from the last page goes to the first page and vice versa. + The current page index is set to the nearest valid + page index. + */ + void MoveFocus(FocusMoveDirection eDirection); + + /** Show the focus indicator of the current slide. + @param bScrollToFocus + When <TRUE/> (the default) then the view is scrolled so that the + focus rectangle lies inside its visible area. + */ + void ShowFocus(const bool bScrollToFocus = true); + + /** Hide the focus indicator. + */ + void HideFocus(); + + /** Toggle the focused state of the current slide. + @return + Returns the focused state of the focus page after the call. + */ + bool ToggleFocus(); + + /** Return whether the window managed by the called focus manager has + the input focus of the application. + */ + bool HasFocus() const; + + /** Return the descriptor of the page that currently has the focus. + @return + When there is no page that currently has the focus then NULL is + returned. + */ + model::SharedPageDescriptor GetFocusedPageDescriptor() const; + + /** Return the index of the page that currently has the focus as it is + accepted by the slide sorter model. + @return + When there is no page that currently has the focus then -1 is + returned. + */ + sal_Int32 GetFocusedPageIndex() const { return mnPageIndex; } + + /** Set the focused page to the one described by the given page + descriptor. The visibility of the focus indicator is not modified. + @param rDescriptor + One of the page descriptors that are currently managed by the + SlideSorterModel. + */ + bool SetFocusedPage(const model::SharedPageDescriptor& rDescriptor); + + /** Set the focused page to the one described by the given page + index. The visibility of the focus indicator is not modified. + @param nPageIndex + A valid page index that is understood by the SlideSorterModel. + */ + void SetFocusedPage(sal_Int32 nPageIndex); + + bool SetFocusedPageToCurrentPage(); + + /** Return <TRUE/> when the focus indicator is currently shown. A + prerequisite is that the window managed by this focus manager has + the input focus as indicated by a <TRUE/> return value of + HasFocus(). It is not necessary that the focus indicator is + visible. It may have been scrolled outside the visible area. + */ + bool IsFocusShowing() const; + + /** Add a listener that is called when the focus is shown or hidden or + set to another page object. + @param rListener + When this method is called multiple times for the same listener + the second and all following calls are ignored. Each listener + is added only once. + */ + void AddFocusChangeListener(const Link<LinkParamNone*, void>& rListener); + + /** Remove a focus change listener. + @param rListener + It is safe to pass a listener that was not added are has been + removed previously. Such calls are ignored. + */ + void RemoveFocusChangeListener(const Link<LinkParamNone*, void>& rListener); + + /** Create an instance of this class to temporarily hide the focus + indicator. It is restored to its former visibility state when the + FocusHider is destroyed. + */ + class FocusHider + { + public: + FocusHider(FocusManager&); + ~FocusHider() COVERITY_NOEXCEPT_FALSE; + + private: + bool mbFocusVisible; + FocusManager& mrManager; + }; + +private: + SlideSorter& mrSlideSorter; + + /** Index of the page that may be focused. It is -1 when the model + contains no page. + */ + sal_Int32 mnPageIndex; + + /** This flag indicates whether the page pointed to by mpFocusDescriptor + has the focus. + */ + bool mbPageIsFocused; + + ::std::vector<Link<LinkParamNone*, void>> maFocusChangeListeners; + + /** Reset the focus state of the given descriptor and request a repaint + so that the focus indicator is hidden. + @param pDescriptor + When NULL is given then the call is ignored. + */ + void HideFocusIndicator(const model::SharedPageDescriptor& rpDescriptor); + + /** Set the focus state of the given descriptor, scroll it into the + visible area and request a repaint so that the focus indicator is + made visible. + @param pDescriptor + When NULL is given then the call is ignored. + @param bScrollToFocus + When <TRUE/> (the default) then the view is scrolled so that the + focus rectangle lies inside its visible area. + */ + void ShowFocusIndicator(const model::SharedPageDescriptor& rpDescriptor, + const bool bScrollToFocus); + + /** Call all currently registered listeners that a focus change has + happened. The focus may be hidden or shown or moved from one page + object to another. + */ + void NotifyFocusChangeListeners() const; +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsInsertionIndicatorHandler.hxx b/sd/source/ui/slidesorter/inc/controller/SlsInsertionIndicatorHandler.hxx new file mode 100644 index 000000000..43f2d2f6a --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsInsertionIndicatorHandler.hxx @@ -0,0 +1,138 @@ +/* -*- 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 . + */ + +#pragma once + +#include <controller/SlsAnimator.hxx> + +#include <view/SlsLayouter.hxx> + +namespace sd::slidesorter { class SlideSorter; } +namespace sd::slidesorter::view { +class InsertAnimator; +class InsertionIndicatorOverlay; +} + +class SdTransferable; + +namespace sd::slidesorter::controller { + +/** Manage the visibility and location of the insertion indicator. Its + actual display is controlled by the InsertionIndicatorOverlay. +*/ +class InsertionIndicatorHandler +{ +public: + InsertionIndicatorHandler (SlideSorter& rSlideSorter); + ~InsertionIndicatorHandler() COVERITY_NOEXCEPT_FALSE; + + enum Mode { CopyMode, MoveMode, UnknownMode }; + static Mode GetModeFromDndAction (const sal_Int8 nDndAction); + + /** Activate the insertion marker at the given coordinates. + */ + void Start (const bool bIsOverSourceView); + + /** Deactivate the insertion marker. + */ + void End (const controller::Animator::AnimationMode eMode); + + /** This context make sure that the insertion indicator is shown + (provided that the clipboard is not empty) while the context is + alive. Typically used while a context menu is displayed. + */ + class ForceShowContext + { + public: + ForceShowContext (const std::shared_ptr<InsertionIndicatorHandler>& rpHandler); + ~ForceShowContext() COVERITY_NOEXCEPT_FALSE; + private: + const std::shared_ptr<InsertionIndicatorHandler> mpHandler; + }; + + /** Update the indicator icon from the current transferable (from the + clipboard or an active drag and drop operation.) + */ + void UpdateIndicatorIcon (const SdTransferable* pTransferable); + + /** Set the position of the insertion marker to the given coordinates. + */ + void UpdatePosition ( + const Point& rMouseModelPosition, + const Mode eMode); + void UpdatePosition ( + const Point& rMouseModelPosition, + const sal_Int8 nDndAction); + + /** Return whether the insertion marker is active. + */ + bool IsActive() const { return mbIsActive;} + + /** Return the insertion index that corresponds with the current + graphical location of the insertion indicator. + */ + sal_Int32 GetInsertionPageIndex() const; + + /** Determine whether moving the current selection to the current + position of the insertion marker would alter the document. This + would be the case when the selection is not consecutive or would be + moved to a position outside and not adjacent to the selection. + */ + bool IsInsertionTrivial ( + const sal_Int32 nInsertionIndex, + const Mode eMode) const; + /** This method is like the other variant. It operates implicitly + on the current insertion index as would be returned by + GetInsertionPageIndex(). + */ + bool IsInsertionTrivial (const sal_Int8 nDndAction); + +private: + SlideSorter& mrSlideSorter; + std::shared_ptr<view::InsertAnimator> mpInsertAnimator; + std::shared_ptr<view::InsertionIndicatorOverlay> mpInsertionIndicatorOverlay; + view::InsertPosition maInsertPosition; + Mode meMode; + bool mbIsInsertionTrivial; + bool mbIsActive; + bool mbIsReadOnly; + bool mbIsOverSourceView; + Size maIconSize; + bool mbIsForcedShow; + + void SetPosition ( + const Point& rPoint, + const Mode eMode); + std::shared_ptr<view::InsertAnimator> const & GetInsertAnimator(); + + /** Make the insertion indicator visible (that is the show part) and + keep it visible, even when the mouse leaves the window (that is the + force part). We need this when a context menu is displayed (mouse + over the popup menu triggers a mouse leave event) while the + insertion indicator remains visible in the background. + + In effect all calls to End() are ignored until ForceEnd() is called. + */ + void ForceShow(); + void ForceEnd(); +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsPageSelector.hxx b/sd/source/ui/slidesorter/inc/controller/SlsPageSelector.hxx new file mode 100644 index 000000000..6a4b75004 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsPageSelector.hxx @@ -0,0 +1,219 @@ +/* -*- 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 . + */ + +#pragma once + +#include <model/SlsSharedPageDescriptor.hxx> + +#include <vector> +#include <memory> + +#include <sddllapi.h> + +class SdPage; + +namespace sd::slidesorter +{ +class SlideSorter; +} +namespace sd::slidesorter::model +{ +class SlideSorterModel; +} + +namespace sd::slidesorter::controller +{ +class SlideSorterController; + +/** A sub-controller that handles page selection of the slide browser. + Selecting a page does not make it the current page (of the main view) + automatically as this would not be desired in a multi selection. This + has to be done explicitly by calling the + CurrentSlideManager::SetCurrentSlide() method. + + Indices of pages relate always to the number of all pages in the model + (as returned by GetPageCount()) not just the selected pages. +*/ +class PageSelector +{ +public: + explicit PageSelector(SlideSorter& rSlideSorter); + PageSelector(const PageSelector&) = delete; + PageSelector& operator=(const PageSelector&) = delete; + + // Exported for unit test + SD_DLLPUBLIC void SelectAllPages(); + SD_DLLPUBLIC void DeselectAllPages(); + + /** Update the selection state of all page descriptors to be the same as + that of the corresponding pages of the SdPage objects and issue + redraw requests where necessary. + */ + void GetCoreSelection(); + + /** Update the selection state of the SdPage objects to be the same as + that of the corresponding page descriptors. + */ + void SetCoreSelection(); + + /** Select the specified descriptor. The selection state of the other + descriptors is not affected. + */ + void SelectPage(int nPageIndex); + /** Select the descriptor that is associated with the given page. The + selection state of the other descriptors is not affected. + */ + void SelectPage(const SdPage* pPage); + /** Select the specified descriptor. The selection state of the other + descriptors is not affected. + */ + void SelectPage(const model::SharedPageDescriptor& rpDescriptor); + + /** Return whether the specified page is selected. This convenience + method is a substitute for + SlideSorterModel::GetPageDescriptor(i)->HasState(ST_Selected) is + included here to make this class more self contained. + */ + SD_DLLPUBLIC bool IsPageSelected(int nPageIndex); + + /** Return whether the specified page is visible. This convenience + method is a substitute for + SlideSorterModel::GetPageDescriptor(i)->HasState(ST_Visible) is + included here to make this class more self contained. + */ + bool IsPageVisible(int nPageIndex); + + /** Deselect the descriptor that is associated with the given page. + The current page is updated to the first slide + of the remaining selection. + */ + void DeselectPage(int nPageIndex); + void DeselectPage(const model::SharedPageDescriptor& rpDescriptor, + const bool bUpdateCurrentPage = true); + + /** This convenience method returns the same number of pages that + SlideSorterModel.GetPageCount() returns. It is included here so + that it is self contained for iterating over all pages to select or + deselect them. + */ + int GetPageCount() const; + int GetSelectedPageCount() const { return mnSelectedPageCount; } + + /** Return the anchor for a range selection. This usually is the first + selected page after all pages have been deselected. + @return + The returned anchor may be NULL. + */ + const model::SharedPageDescriptor& GetSelectionAnchor() const { return mpSelectionAnchor; } + + typedef ::std::vector<SdPage*> PageSelection; + + /** Return an object that describes the current selection. The caller + can use that object to later restore the selection. + @return + The object returned describes the selection via indices. So + even if pages are exchanged a later call to SetPageSelection() + is valid. + */ + std::shared_ptr<PageSelection> GetPageSelection() const; + + /** Restore a page selection according to the given selection object. + @param rSelection + Typically obtained by calling GetPageSelection() this object + is used to restore the selection. If pages were exchanged since + the last call to GetPageSelection() it is still valid to call + this method with the selection. When pages have been inserted + or removed the result may be unexpected. + @param bUpdateCurrentPage + When <TRUE/> (the default value) then after setting the + selection update the current page to the first page of the + selection. + When called from within UpdateCurrentPage() then this flag is + used to prevent a recursion loop. + */ + void SetPageSelection(const std::shared_ptr<PageSelection>& rSelection, + const bool bUpdateCurrentPage); + + /** Call this method after the model has changed to set the number + of selected pages. + */ + void CountSelectedPages(); + + /** Use the UpdateLock whenever you do a complex selection, i.e. call + more than one method in a row. An active lock prevents intermediate + changes of the current slide. + */ + class UpdateLock + { + public: + UpdateLock(SlideSorter const& rSlideSorter); + UpdateLock(PageSelector& rPageSelector); + ~UpdateLock(); + void Release(); + + private: + PageSelector* mpSelector; + }; + + class BroadcastLock + { + public: + BroadcastLock(SlideSorter const& rSlideSorter); + BroadcastLock(PageSelector& rPageSelector); + ~BroadcastLock(); + + private: + PageSelector& mrSelector; + }; + +private: + model::SlideSorterModel& mrModel; + SlideSorter& mrSlideSorter; + SlideSorterController& mrController; + int mnSelectedPageCount; + int mnBroadcastDisableLevel; + bool mbSelectionChangeBroadcastPending; + model::SharedPageDescriptor mpMostRecentlySelectedPage; + /// Anchor for a range selection. + model::SharedPageDescriptor mpSelectionAnchor; + sal_Int32 mnUpdateLockCount; + bool mbIsUpdateCurrentPagePending; + + /** Enable the broadcasting of selection change events. This calls the + SlideSorterController::SelectionHasChanged() method to do the actual + work. When EnableBroadcasting has been called as many times as + DisableBroadcasting() was called before and the selection has been + changed in the meantime, this change will be broadcasted. + */ + void EnableBroadcasting(); + + /** Disable the broadcasting of selection change events. Subsequent + changes of the selection will set a flag that triggers the sending + of events when EnableBroadcasting() is called. + */ + void DisableBroadcasting(); + + void UpdateCurrentPage(const bool bUpdateOnlyWhenPending = false); + + void CheckConsistency() const; +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsProperties.hxx b/sd/source/ui/slidesorter/inc/controller/SlsProperties.hxx new file mode 100644 index 000000000..344ac67f3 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsProperties.hxx @@ -0,0 +1,125 @@ +/* -*- 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 . + */ + +#pragma once + +#include <tools/color.hxx> + +namespace sd::slidesorter::controller +{ +/** An extensible set of properties used throughout the slide sorter. +*/ +class Properties +{ +public: + Properties(); + + /** Call this method after receiving a VclEventId::ApplicationDataChanged + event. + */ + void HandleDataChangeEvent(); + + /** When this method returns <TRUE/> then the current slide is + highlighted in the view. The default value is <FALSE/>. + */ + bool IsHighlightCurrentSlide() const { return mbIsHighlightCurrentSlide; } + void SetHighlightCurrentSlide(const bool bIsHighlightCurrentSlide); + + /** When this method returns <TRUE/> then the selection is indicated in + the view (typically by drawing rectangles around the selected + slides.) The default value is <TRUE/>. + */ + bool IsShowSelection() const { return mbIsShowSelection; } + void SetShowSelection(const bool bIsShowSelection); + + /** When this method returns <TRUE/> then the focusdselection is indicated in + the view (typically by drawing dotted rectangles around the selected + slides.) The default value is <TRUE/>. + */ + bool IsShowFocus() const { return mbIsShowFocus; } + void SetShowFocus(const bool bIsShowFocus); + + /** When this method returns <TRUE/> then on a selection change the + visible area is adapted so that the selected slides are shown + centered in the view. This can be used to center the current slide + by selecting only the current slide. The default value is <FALSE/>. + */ + bool IsCenterSelection() const { return mbIsCenterSelection; } + void SetCenterSelection(const bool bIsCenterSelection); + + /** When this method returns <TRUE/> then the view may try to change the + visible area by scrolling it smoothly on the screen. Experimental. + Default value is <FALSE/>. + */ + bool IsSmoothSelectionScrolling() const { return mbIsSmoothSelectionScrolling; } + void SetSmoothSelectionScrolling(const bool bIsSmoothSelectionScrolling); + + /** When this method returns <TRUE/> then during a full screen + presentation the previews in a slide sorter are not updated. + Default value is <TRUE/>. + */ + bool IsSuspendPreviewUpdatesDuringFullScreenPresentation() const + { + return mbIsSuspendPreviewUpdatesDuringFullScreenPresentation; + } + void SetSuspendPreviewUpdatesDuringFullScreenPresentation(const bool bFlag); + + /** Return the background color. + */ + const Color& GetBackgroundColor() const { return maBackgroundColor; } + void SetBackgroundColor(const Color& rColor); + + /** Return the text color. + */ + const Color& GetTextColor() const { return maTextColor; } + void SetTextColor(const Color& rColor); + + /** Return the color in which selections are to be painted. + */ + const Color& GetSelectionColor() const { return maSelectionColor; } + void SetSelectionColor(const Color& rColor); + + /** Return the color used for highlighting e.g. the current slide. + */ + const Color& GetHighlightColor() const { return maHighlightColor; } + void SetHighlightColor(const Color& rColor); + + /** The UI can be set to be read only independently from the model status. + Used for instance in the presenter view. + */ + bool IsUIReadOnly() const { return mbIsUIReadOnly; } + void SetUIReadOnly(const bool bIsUIReadOnly); + +private: + bool mbIsHighlightCurrentSlide; + bool mbIsShowSelection; + bool mbIsShowFocus; + bool mbIsCenterSelection; + bool mbIsSmoothSelectionScrolling; + bool mbIsSuspendPreviewUpdatesDuringFullScreenPresentation; + Color maBackgroundColor; + Color maTextColor; + Color maSelectionColor; + Color maHighlightColor; + bool mbIsUIReadOnly; +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsScrollBarManager.hxx b/sd/source/ui/slidesorter/inc/controller/SlsScrollBarManager.hxx new file mode 100644 index 000000000..853d98f4a --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsScrollBarManager.hxx @@ -0,0 +1,248 @@ +/* -*- 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 . + */ + +#pragma once + +#include <tools/link.hxx> +#include <tools/gen.hxx> +#include <vcl/timer.hxx> +#include <vcl/scrbar.hxx> +#include <vcl/vclptr.hxx> + +#include <functional> + +namespace sd { class Window; } + +namespace sd::slidesorter { class SlideSorter; } + +namespace sd::slidesorter::controller { + +/** Manage the horizontal and vertical scroll bars. Listen for events, set + their sizes, place them in the window, determine their visibilities. + + <p>Handle auto scrolling, i.e. the scrolling of the window when the + mouse comes near the window border while dragging a selection.</p> + + <p>In order to make the slide sorter be used in the task pane with its + own vertical scrollbars the vertical scrollbar of the use of the slide + sorter is optional. When using it the available area in a window is + used and the vertical scrollbar is displayed when that area is not large + enough. When the vertical scrollbar is not used then the available area + is assumed to be modifiable. In that case the PlaceScrollBars() method + may return an area larger than the one given.<p> +*/ +class ScrollBarManager +{ +public: + /** Create a new scroll bar manager that manages three controls: the + horizontal scroll bar, the vertical scroll bar, and the little + window that fills the gap at the bottom right corner that is left + between the two scroll bars. Call LateInitialization() after + constructing a new object. + */ + ScrollBarManager (SlideSorter& rSlideSorter); + + ~ScrollBarManager(); + + /** Register listeners at the scroll bars. This method is called after + startup of a new slide sorter object or after a reactivation of a + slide sorter that for example is taken from a cache. + */ + void Connect(); + + /** Remove listeners from the scroll bars. This method is called when + the slide sorter is destroyed or when it is suspended, e.g. put + into a cache for later reuse. + */ + void Disconnect(); + + /** Set up the scroll bar, i.e. thumb size and position. Call this + method when the content of the browser window changed, i.e. pages + were inserted or deleted, the layout or the zoom factor has + changed. + @param bScrollToCurrentPosition + When <TRUE/> then scroll the window to the new offset that is + defined by the scroll bars. Otherwise the new offset is simply + set and the whole window is repainted. + */ + void UpdateScrollBars ( + bool bScrollToCurrentPosition); + + /** Place the scroll bars inside the given area. When the available + area is not large enough for the content to display the horizontal + and/or vertical scroll bar is enabled. + @param rAvailableArea + The scroll bars will be placed inside this rectangle. It is + expected to be given in pixel relative to its parent. + @param bIsHorizontalScrollBarAllowed + Only when this flag is <TRUE/> the horizontal scroll may be + displayed. + @param bIsVerticalScrollBarAllowed + Only when this flag is <TRUE/> the horizontal scroll may be + displayed. + @return + Returns the space that remains after the scroll bars are + placed. + */ + ::tools::Rectangle PlaceScrollBars ( + const ::tools::Rectangle& rAvailableArea, + const bool bIsHorizontalScrollBarAllowed, + const bool bIsVerticalScrollBarAllowed); + + /** Update the vertical and horizontal scroll bars so that the visible + area has the given top and left values. + */ + void SetTopLeft (const Point& rNewTopLeft); + + /** Return the width of the vertical scroll bar, which--when + shown--should be fixed in contrast to its height. + @return + Returns 0 when the vertical scroll bar is not shown or does not + exist, otherwise its width in pixel is returned. + */ + int GetVerticalScrollBarWidth() const; + + /** Return the height of the horizontal scroll bar, which--when + shown--should be fixed in contrast to its width. + @return + Returns 0 when the vertical scroll bar is not shown or does not + exist, otherwise its height in pixel is returned. + */ + int GetHorizontalScrollBarHeight() const; + + /** Call this method to scroll a window while the mouse is in dragging a + selection. If the mouse is near the window border or is outside the + window then scroll the window accordingly. + @param rMouseWindowPosition + The mouse position for which the scroll amount is calculated. + @param rAutoScrollFunctor + Every time when the window is scrolled then this functor is executed. + @return + When the window is scrolled then this method returns <TRUE/>. + When the window is not changed then <FALSE/> is returned. + */ + bool AutoScroll ( + const Point& rMouseWindowPosition, + const ::std::function<void ()>& rAutoScrollFunctor); + + void StopAutoScroll(); + + void clearAutoScrollFunctor(); + + enum Orientation { Orientation_Horizontal, Orientation_Vertical }; + /** Scroll the slide sorter by setting the thumbs of the scroll bars and + by moving the content of the content window. + @param eOrientation + Defines whether to scroll horizontally or vertically. + @param nDistance + distance in slides. + */ + void Scroll( + const Orientation eOrientation, + const sal_Int32 nDistance); + +private: + SlideSorter& mrSlideSorter; + + /** The horizontal scroll bar. Note that is used but not owned by + objects of this class. It is given to the constructor. + */ + VclPtr<ScrollBar> mpHorizontalScrollBar; + + /** The vertical scroll bar. Note that is used but not owned by + objects of this class. It is given to the constructor. + */ + VclPtr<ScrollBar> mpVerticalScrollBar; + + /// Relative horizontal position of the visible area in the view. + double mnHorizontalPosition; + /// Relative vertical position of the visible area in the view. + double mnVerticalPosition; + /** The width and height of the border at the inside of the window which + when entered while in drag mode leads to a scrolling of the window. + */ + Size maScrollBorder; + /** The only task of this little window is to paint the little square at + the bottom right corner left by the two scroll bars (when both are + visible). + */ + VclPtr<ScrollBarBox> mpScrollBarFiller; + + /** The auto scroll timer is used for keep scrolling the window when the + mouse reaches its border while dragging a selection. When the mouse + is not moved the timer issues events to keep scrolling. + */ + Timer maAutoScrollTimer; + Size maAutoScrollOffset; + bool mbIsAutoScrollActive; + + /** The content window is the one whose view port is controlled by the + scroll bars. + */ + VclPtr<sd::Window> mpContentWindow; + + ::std::function<void ()> maAutoScrollFunctor; + + void SetWindowOrigin ( + double nHorizontalPosition, + double nVerticalPosition); + + /** Determine the visibility of the scroll bars so that the window + content is not clipped in any dimension without showing a scroll + bar. + @param rAvailableArea + The area in which the scroll bars, the scroll bar filler, and + the SlideSorterView will be placed. + @return + The area that is enclosed by the scroll bars is returned. It + will be filled with the SlideSorterView. + */ + ::tools::Rectangle DetermineScrollBarVisibilities( + const ::tools::Rectangle& rAvailableArea, + const bool bIsHorizontalScrollBarAllowed, + const bool bIsVerticalScrollBarAllowed); + + /** Typically called by DetermineScrollBarVisibilities() this method + tests a specific configuration of the two scroll bars being visible + or hidden. + @return + When the window content can be shown with only being clipped in + an orientation where the scroll bar would be shown then <TRUE/> + is returned. + */ + bool TestScrollBarVisibilities ( + bool bHorizontalScrollBarVisible, + bool bVerticalScrollBarVisible, + const ::tools::Rectangle& rAvailableArea); + + void CalcAutoScrollOffset (const Point& rMouseWindowPosition); + bool RepeatAutoScroll(); + + DECL_LINK(HorizontalScrollBarHandler, ScrollBar*, void); + DECL_LINK(VerticalScrollBarHandler, ScrollBar*, void); + DECL_LINK(AutoScrollTimeoutHandler, Timer *, void); + + void PlaceHorizontalScrollBar (const ::tools::Rectangle& aArea); + void PlaceVerticalScrollBar (const ::tools::Rectangle& aArea); + void PlaceFiller (const ::tools::Rectangle& aArea); +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsSelectionFunction.hxx b/sd/source/ui/slidesorter/inc/controller/SlsSelectionFunction.hxx new file mode 100644 index 000000000..7d80fbd26 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsSelectionFunction.hxx @@ -0,0 +1,145 @@ +/* -*- 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 . + */ + +#pragma once + +#include <controller/SlsFocusManager.hxx> +#include <fupoor.hxx> +#include <memory> + +namespace sd::slidesorter +{ +class SlideSorter; +} + +struct AcceptDropEvent; + +namespace sd::slidesorter::controller +{ +class SlideSorterController; + +class SelectionFunction final : public FuPoor +{ +public: + SelectionFunction(const SelectionFunction&) = delete; + SelectionFunction& operator=(const SelectionFunction&) = delete; + + static rtl::Reference<FuPoor> Create(SlideSorter& rSlideSorter, SfxRequest& rRequest); + + // Mouse- & Key-Events + virtual bool KeyInput(const KeyEvent& rKEvt) override; + virtual bool MouseMove(const MouseEvent& rMEvt) override; + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + + /// Forward to the clipboard manager. + virtual void DoCut() override; + + /// Forward to the clipboard manager. + virtual void DoCopy() override; + + /// Forward to the clipboard manager. + virtual void DoPaste() override; + + /** is called when the current function should be aborted. <p> + This is used when a function gets a KEY_ESCAPE but can also + be called directly. + + @returns + true if an active function was aborted + */ + virtual bool cancel() override; + + void MouseDragged(const AcceptDropEvent& rEvent, const sal_Int8 nDragAction); + + /** Turn of substitution display and insertion indicator. + */ + void NotifyDragFinished(); + + class EventDescriptor; + class ModeHandler; + friend class ModeHandler; + enum Mode + { + NormalMode, + MultiSelectionMode, + DragAndDropMode + }; + void SwitchToNormalMode(); + void SwitchToDragAndDropMode(const Point& rMousePosition); + void SwitchToMultiSelectionMode(const Point& rMousePosition, const sal_uInt32 nEventCode); + + void ResetShiftKeySelectionAnchor(); + /** Special case handling for when the context menu is hidden. This + method will reinitialize the current mouse position to prevent the + mouse motion during the time the context menu is displayed from + being interpreted as drag-and-drop start. + */ + void ResetMouseAnchor(); + +private: + SlideSorter& mrSlideSorter; + SlideSorterController& mrController; + + SelectionFunction(SlideSorter& rSlideSorter, SfxRequest& rRequest); + + virtual ~SelectionFunction() override; + + /** Remember the slide where the shift key was pressed and started a + multiselection via keyboard. + */ + sal_Int32 mnShiftKeySelectionAnchor; + + /** The selection function can be in one of several mutually + exclusive modes. + */ + std::shared_ptr<ModeHandler> mpModeHandler; + + /** Make the slide nOffset slides away of the current one the new + current slide. When the new index is outside the range of valid + page numbers it is clipped to that range. + @param nOffset + When nOffset is negative then go back. When nOffset if positive go + forward. When it is zero then ignore the call. + */ + void GotoNextPage(int nOffset); + + /** Make the slide with the given index the new current slide. + @param nIndex + Index of the new current slide. When the new index is outside + the range of valid page numbers it is clipped to that range. + */ + void GotoPage(int nIndex); + + void ProcessMouseEvent(sal_uInt32 nEventType, const MouseEvent& rEvent); + + // What follows are a couple of helper methods that are used by + // ProcessMouseEvent(). + + void ProcessEvent(EventDescriptor& rEvent); + + void MoveFocus(const FocusManager::FocusMoveDirection eDirection, const bool bIsShiftDown, + const bool bIsControlDown); + + void SwitchMode(const std::shared_ptr<ModeHandler>& rpHandler); +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsSelectionManager.hxx b/sd/source/ui/slidesorter/inc/controller/SlsSelectionManager.hxx new file mode 100644 index 000000000..4f52d49e6 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsSelectionManager.hxx @@ -0,0 +1,139 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/types.h> +#include <tools/link.hxx> +#include <vector> +#include <memory> + +class SdPage; + +namespace sd::slidesorter +{ +class SlideSorter; +} + +namespace sd::slidesorter::controller +{ +class SlideSorterController; +class SelectionObserver; + +/** This class is a part of the controller and handles the selection of + slides. + <p>It has methods to modify the selected slides (delete them or + move them to other places in the document), change the visible area so + to make the selected slides visible, tell listeners when the selection + changes.</p> +*/ +class SelectionManager +{ +public: + /** Create a new SelectionManager for the given slide sorter. + */ + SelectionManager(SlideSorter& rSlideSorter); + + ~SelectionManager(); + + /** Delete the currently selected slides. When this method returns the + selection is empty. + @param bSelectFollowingPage + When <TRUE/> then after deleting the selected pages make the + slide after the last selected page the new current page. + When <FALSE/> then make the first slide before the selected + pages the new current slide. + */ + void DeleteSelectedPages(const bool bSelectFollowingPage = true); + + /** Call this method after the selection has changed (possible several + calls to the PageSelector) to invalidate the relevant slots and send + appropriate events. + */ + void SelectionHasChanged(); + + /** Add a listener that is called when the selection of the slide sorter + changes. + @param rListener + When this method is called multiple times for the same listener + the second and all following calls are ignored. Each listener + is added only once. + */ + void AddSelectionChangeListener(const Link<LinkParamNone*, void>& rListener); + + /** Remove a listener that was called when the selection of the slide + sorter changes. + @param rListener + It is safe to pass a listener that was not added are has been + removed previously. Such calls are ignored. + */ + void RemoveSelectionChangeListener(const Link<LinkParamNone*, void>& rListener); + + /** Return the position where to insert pasted slides based on the + current selection. When there is a selection then the insert + position is behind the last slide. When the selection is empty then + most of the time the insert position is at the end of the document. + There is an exception right after the display of a popup-menu. The + position of the associated insertion marker is stored here and reset + the next time the selection changes. + */ + sal_Int32 GetInsertionPosition() const; + + /** Store an insertion position temporarily. It is reset when the + selection changes the next time. + */ + void SetInsertionPosition(const sal_Int32 nInsertionPosition); + + const std::shared_ptr<SelectionObserver>& GetSelectionObserver() const + { + return mpSelectionObserver; + } + +private: + SlideSorter& mrSlideSorter; + SlideSorterController& mrController; + + ::std::vector<Link<LinkParamNone*, void>> maSelectionChangeListeners; + + /** The insertion position is only temporarily valid. Negative values + indicate that the explicit insertion position is not valid. In this + case GetInsertionPosition() calculates it from the current selection. + */ + sal_Int32 mnInsertionPosition; + + std::shared_ptr<SelectionObserver> mpSelectionObserver; + + /** Delete the given list of normal pages. This method is a helper + function for DeleteSelectedPages(). + @param rSelectedNormalPages + A list of normal pages. Supplying master pages is an error. + */ + void DeleteSelectedNormalPages(const ::std::vector<SdPage*>& rSelectedNormalPages); + + /** Delete the given list of master pages. This method is a helper + function for DeleteSelectedPages(). + @param rSelectedMasterPages + A list of master pages. Supplying normal pages is an error. + */ + void DeleteSelectedMasterPages(const ::std::vector<SdPage*>& rSelectedMasterPages); +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsSelectionObserver.hxx b/sd/source/ui/slidesorter/inc/controller/SlsSelectionObserver.hxx new file mode 100644 index 000000000..11742b890 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsSelectionObserver.hxx @@ -0,0 +1,77 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/types.h> +#include <memory> +#include <vector> + +namespace sd::slidesorter +{ +class SlideSorter; +} + +class SdrPage; +class SdPage; + +namespace sd::slidesorter::controller +{ +/** Observe insertions and deletions of pages between calls to + StartObservation() and EndObservation(). When the later is called + the selection is set to just the newly inserted pages. +*/ +class SelectionObserver final +{ +public: + SelectionObserver(SlideSorter& rSlideSorter); + ~SelectionObserver(); + + void NotifyPageEvent(const SdrPage* pPage); + void StartObservation(); + void AbortObservation(); + void EndObservation(); + + /** Use this little class instead of calling StartObservation and + EndObservation directly so that EndObservation is not forgotten or + omitted due to an exception or some break or return in the middle of + code. + */ + class Context + { + public: + Context(SlideSorter const& rSlideSorter); + ~Context() COVERITY_NOEXCEPT_FALSE; + void Abort(); + + private: + std::shared_ptr<SelectionObserver> mpSelectionObserver; + }; + +private: + SlideSorter& mrSlideSorter; + bool mbIsObservationActive; + bool mbPageEventOccurred; + + ::std::vector<const SdPage*> maInsertedPages; +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsSlotManager.hxx b/sd/source/ui/slidesorter/inc/controller/SlsSlotManager.hxx new file mode 100644 index 000000000..57de8422a --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsSlotManager.hxx @@ -0,0 +1,98 @@ +/* -*- 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 . + */ +#pragma once + +#include <model/SlsSharedPageDescriptor.hxx> +#include <tools/link.hxx> +#include <rtl/ustring.hxx> + +class AbstractSvxNameDialog; +class SfxItemSet; +class SfxRequest; + +namespace sd::slidesorter +{ +class SlideSorter; +} + +namespace sd::slidesorter::controller +{ +/** This manager takes over the work of handling slot calls from the + controller of the slide sorter. +*/ +class SlotManager +{ +public: + /** Create a new slot manager that handles slot calls for the controller + of a slide sorter. + @param rController + The controller for which to handle the slot calls. + */ + SlotManager(SlideSorter& rSlideSorter); + + void FuTemporary(SfxRequest& rRequest); + void FuPermanent(SfxRequest& rRequest); + void FuSupport(SfxRequest& rRequest); + void GetMenuState(SfxItemSet& rSet); + void GetClipboardState(SfxItemSet& rSet); + void GetStatusBarState(SfxItemSet& rSet); + void ExecCtrl(SfxRequest& rRequest); + void GetAttrState(SfxItemSet& rSet); + + /** Exclude or include one slide or all selected slides. + @param rpDescriptor + When the pointer is empty then apply the new state to all + selected pages. Otherwise apply the new state to just the + specified state. + */ + void ChangeSlideExclusionState(const model::SharedPageDescriptor& rpDescriptor, + const bool bExcludeSlide); + + /** Call this after a change from normal mode to master mode or back. + The affected slots are invalidated. + */ + void NotifyEditModeChange(); + +private: + /// The controller for which we manage the slot calls. + SlideSorter& mrSlideSorter; + + /** The implementation is a copy of the code for SID_RENAMEPAGE in + drviews2.cxx. + */ + void RenameSlide(const SfxRequest& rRequest); + DECL_LINK(RenameSlideHdl, AbstractSvxNameDialog&, bool); + DECL_STATIC_LINK(SlotManager, RenameSlideTooltipHdl, AbstractSvxNameDialog&, OUString); + bool RenameSlideFromDrawViewShell(sal_uInt16 nPageId, const OUString& rName); + + /** Handle SID_INSERTPAGE slot calls. + */ + void InsertSlide(SfxRequest& rRequest); + + void DuplicateSelectedSlides(SfxRequest& rRequest); + + /** Use one of several ways to determine where to insert a new page. + This can be the current selection or the insertion indicator. + */ + sal_Int32 GetInsertionPosition() const; +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsTransferableData.hxx b/sd/source/ui/slidesorter/inc/controller/SlsTransferableData.hxx new file mode 100644 index 000000000..863c2fe73 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsTransferableData.hxx @@ -0,0 +1,78 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sdxfer.hxx> + +#include <vcl/bitmapex.hxx> + +#include <vector> + +class SdDrawDocument; +namespace sd::slidesorter { class SlideSorterViewShell; } + +namespace sd::slidesorter::controller { + +/** Represent previews and other information so that they can be + attached to an existing transferable. +*/ +class TransferableData final + : public SdTransferable::UserData, + public SfxListener +{ +public: + class Representative + { + public: + Representative (const BitmapEx& rBitmap, const bool bIsExcluded) + : maBitmap(rBitmap), mbIsExcluded(bIsExcluded) {} + + BitmapEx maBitmap; + bool mbIsExcluded; + }; + + static rtl::Reference<SdTransferable> CreateTransferable ( + SdDrawDocument* pSrcDoc, + SlideSorterViewShell* pViewShell, + ::std::vector<TransferableData::Representative>&& rRepresentatives); + + static std::shared_ptr<TransferableData> GetFromTransferable (const SdTransferable* pTransferable); + + TransferableData ( + SlideSorterViewShell* pViewShell, + ::std::vector<TransferableData::Representative>&& rRepresentatives); + virtual ~TransferableData() override; + + const ::std::vector<Representative>& GetRepresentatives() const { return maRepresentatives;} + + /** Return the view shell for which the transferable was created. + */ + SlideSorterViewShell* GetSourceViewShell() const { return mpViewShell;} + +private: + SlideSorterViewShell* mpViewShell; + const ::std::vector<Representative> maRepresentatives; + + virtual void Notify (SfxBroadcaster& rBroadcaster, const SfxHint& rHint) override; +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsVisibleAreaManager.hxx b/sd/source/ui/slidesorter/inc/controller/SlsVisibleAreaManager.hxx new file mode 100644 index 000000000..d9f5845af --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsVisibleAreaManager.hxx @@ -0,0 +1,90 @@ +/* -*- 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 . + */ + +#pragma once + +#include <model/SlsSharedPageDescriptor.hxx> +#include <optional> +#include <tools/gen.hxx> +#include <vector> + +namespace sd::slidesorter +{ +class SlideSorter; +} + +namespace sd::slidesorter::controller +{ +/** Manage requests for scrolling page objects into view. +*/ +class VisibleAreaManager +{ +public: + explicit VisibleAreaManager(SlideSorter& rSlideSorter); + ~VisibleAreaManager(); + VisibleAreaManager(const VisibleAreaManager&) = delete; + VisibleAreaManager& operator=(const VisibleAreaManager&) = delete; + + void ActivateCurrentSlideTracking(); + void DeactivateCurrentSlideTracking(); + bool IsCurrentSlideTrackingActive() const { return mbIsCurrentSlideTrackingActive; } + + /** Request the current slide to be moved into the visible area. + This request is only obeyed when the current slide tracking is + active. + @see ActivateCurrentSlideTracking() and DeactivateCurrentSlideTracking() + */ + void RequestCurrentSlideVisible(); + + /** Request to make the specified page object visible. + */ + void RequestVisible(const model::SharedPageDescriptor& rpDescriptor, const bool bForce = false); + + /** Temporarily disable the update of the visible area. + */ + class TemporaryDisabler + { + public: + explicit TemporaryDisabler(SlideSorter const& rSlideSorter); + ~TemporaryDisabler(); + + private: + VisibleAreaManager& mrVisibleAreaManager; + }; + +private: + SlideSorter& mrSlideSorter; + + /** List of rectangle that someone wants to be moved into the visible + area. + Cleared on every call to ForgetVisibleRequests() and MakeVisible(). + */ + ::std::vector<::tools::Rectangle> maVisibleRequests; + + Point maRequestedVisibleTopLeft; + bool mbIsCurrentSlideTrackingActive; + int mnDisableCount; + + void MakeVisible(); + ::std::optional<Point> GetRequestedTopLeft() const; +}; + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/model/SlideSorterModel.hxx b/sd/source/ui/slidesorter/inc/model/SlideSorterModel.hxx new file mode 100644 index 000000000..90223a1bc --- /dev/null +++ b/sd/source/ui/slidesorter/inc/model/SlideSorterModel.hxx @@ -0,0 +1,227 @@ +/* -*- 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 . + */ + +#pragma once + +#include <model/SlsSharedPageDescriptor.hxx> +#include <pres.hxx> +#include <osl/mutex.hxx> +#include <vcl/region.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <vector> + +class SdDrawDocument; +class SdrPage; +class SdPage; +namespace sd::slidesorter +{ +class SlideSorter; +} +namespace com::sun::star::container +{ +class XIndexAccess; +} +namespace com::sun::star::drawing +{ +class XDrawPage; +} + +namespace sd::slidesorter::model +{ +inline sal_Int32 FromCoreIndex(const sal_uInt16 nCoreIndex) { return (nCoreIndex - 1) / 2; } + +/** The model of the slide sorter gives access to the slides that are to be + displayed in the slide sorter view. Via the SetDocumentSlides() method + this set of slides can be modified (but do not call it directly, use + SlideSorterController::SetDocumentSlides() instead.) +*/ +class SlideSorterModel final +{ +public: + SlideSorterModel(SlideSorter& rSlideSorter); + + ~SlideSorterModel(); + void Dispose(); + + /** This method is present to let the view create a ShowView for + displaying slides. + */ + SdDrawDocument* GetDocument(); + + /** Set a new edit mode and return whether the edit mode really + has been changed. When the edit mode is changed then the + previous page descriptor list is replaced by a new one which + has to be repainted. + @return + A return value of <TRUE/> indicates that the edit mode has + changed and thus the page descriptor list has been set up + to reflect that change. A repaint is necessary. + */ + bool SetEditMode(EditMode eEditMode); + + EditMode GetEditMode() const { return meEditMode; } + + /** Return the number of slides in the document regardless of whether + they are visible or not or whether they are hidden or not. + The number of slides depends on the set of slides available through + the XIndexAccess given to SetDocumentSlides(). + */ + sal_Int32 GetPageCount() const; + + /** Return a page descriptor for the page with the specified index. + Page descriptors are created on demand. The page descriptor is + found (or not found) in constant time. + @param nPageIndex + The index of the requested slide. The valid values + are 0 to GetPageCount()-1. + @param bCreate + When <TRUE/> and the requested page descriptor is missing then + it is created. When <FALSE/> then an empty reference is + returned for missing descriptors. + @return + When the given index is not valid, i.e. lower than zero or + larger than or equal to the number of pages then an empty + reference is returned. Note that the page count may change + between calls to GetPageCount() and GetPageDescriptor(). + */ + SharedPageDescriptor GetPageDescriptor(const sal_Int32 nPageIndex, + const bool bCreate = true) const; + + /** Return a page descriptor for the given XDrawPage. Page descriptors + are created on demand. The page descriptor is found (or not found) + in (at most) linear time. Note that all page descriptors in front of + the one associated with the given XDrawPage are created when not yet + present. When the XDrawPage is not found then all descriptors are + created. + @return + Returns the index to the requested page descriptor or -1 when + there is no such page descriptor. + */ + sal_Int32 GetIndex(const css::uno::Reference<css::drawing::XDrawPage>& rxSlide) const; + + /** Return a page descriptor for the given SdrPage. Page descriptors + are created on demand. The page descriptor is found (or not found) + in (at most) linear time. Note that all page descriptors in front of + the one associated with the given XDrawPage are created when not yet + present. When the SdrPage is not found then all descriptors are + created. + @return + Returns the index to the requested page descriptor or -1 when + there is no such page descriptor. + */ + sal_Int32 GetIndex(const SdrPage* pPage) const; + + /** Return an index for accessing an SdrModel that corresponds to the + given SlideSorterModel index. In many cases we just have to apply + the n*2+1 magic. Only when a special model is set, like a custom + slide show, then the returned value is different. + */ + sal_uInt16 GetCoreIndex(const sal_Int32 nIndex) const; + + /** Call this method after the document has changed its structure. This + will get the model in sync with the SdDrawDocument. This method + tries not to throw away too much information already gathered. This + is especially important for previews of complex pages that take some + time to create. + */ + void Resync(); + + /** Delete all descriptors that currently are in the container. The size + of the container, however, is not altered. Use the AdaptSize + method for that. + */ + void ClearDescriptorList(); + + /** Set the selection of the document to exactly that of the called model. + */ + void SynchronizeDocumentSelection(); + + /** Set the selection of the called model to exactly that of the document. + */ + void SynchronizeModelSelection(); + + /** Return the mutex so that the caller can lock it and then safely + access the model. + */ + ::osl::Mutex& GetMutex() { return maMutex; } + + /** Set the XIndexAccess from which the called SlideSorterModel takes + its pages. + @param rxSlides + The set of slides accessible through this XIndexAccess are not + necessarily the same as the ones of the XModel of the + XController (although it typically is a subset). + */ + void SetDocumentSlides(const css::uno::Reference<css::container::XIndexAccess>& rxSlides); + + /** Return the set of pages that is currently displayed by the slide sorter. + */ + css::uno::Reference<css::container::XIndexAccess> GetDocumentSlides() const; + + /** This method is called when the edit mode has changed. It calls + SetDocumentSlides() with the set of slides or master pages obtained + from the model of the XController. + */ + void UpdatePageList(); + + bool IsReadOnly() const; + + /** The current selection is saved by copying the ST_Selected state into + ST_WasSelected for slides. + */ + void SaveCurrentSelection(); + + /** The current selection is restored from the ST_WasSelected state from + the slides. + @returns + The returned region has to be repainted to reflect the updated + selection states. + */ + vcl::Region RestoreSelection(); + + /** Typically called from controller::Listener this method handles the + insertion and deletion of single pages. + @return + Returns <TRUE/> when the given page is relevant for the current + page kind and edit mode. + */ + bool NotifyPageEvent(const SdrPage* pPage); + +private: + mutable ::osl::Mutex maMutex; + SlideSorter& mrSlideSorter; + css::uno::Reference<css::container::XIndexAccess> mxSlides; + EditMode meEditMode; + mutable ::std::vector<SharedPageDescriptor> maPageDescriptors; + + /** Resize the descriptor container according to current values of + page kind and edit mode. + */ + void AdaptSize(); + + SdPage* GetPage(const sal_Int32 nCoreIndex) const; + void InsertSlide(SdPage* pPage, bool bMarkSelected); + // return if this page was marked as selected before being removed + bool DeleteSlide(const SdPage* pPage); + void UpdateIndices(const sal_Int32 nFirstIndex); +}; + +} // end of namespace ::sd::slidesorter::model + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/model/SlsEnumeration.hxx b/sd/source/ui/slidesorter/inc/model/SlsEnumeration.hxx new file mode 100644 index 000000000..289f85911 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/model/SlsEnumeration.hxx @@ -0,0 +1,44 @@ +/* -*- 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 . + */ + +#pragma once + +#include <memory> + +namespace sd::slidesorter::model +{ +/** Interface to generic enumerations. Designed to operate on shared + pointers. Therefore GetNextElement() returns T and not T&. +*/ +template <class T> class Enumeration +{ +public: + virtual ~Enumeration() {} + + virtual bool HasMoreElements() const = 0; + /** Returns T instead of T& so that it can handle shared pointers. + */ + virtual T GetNextElement() = 0; + virtual void Rewind() = 0; + virtual ::std::unique_ptr<Enumeration<T>> Clone() = 0; +}; + +} // end of namespace ::sd::slidesorter::model + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/model/SlsPageDescriptor.hxx b/sd/source/ui/slidesorter/inc/model/SlsPageDescriptor.hxx new file mode 100644 index 000000000..4f3be3b42 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/model/SlsPageDescriptor.hxx @@ -0,0 +1,144 @@ +/* -*- 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 . + */ + +#pragma once + +#include <model/SlsVisualState.hxx> +#include <tools/gen.hxx> +#include <com/sun/star/uno/Reference.hxx> + +#include <memory> + +namespace com::sun::star::drawing { class XDrawPage; } + +class SdPage; +class SdrPage; + +namespace sd::slidesorter::model { + +/** Each PageDescriptor object represents the preview of one draw page, + slide, or master page of a Draw or Impress document as they are displayed + in the slide sorter. This class gives access to some associated + information like prerendered preview or position on the screen. + + <p>Bounding boxes of page objects come in four varieties: + Model and screen/pixel coordinates and the bounding boxes of the actual + page objects and the larger bounding boxes that include page names and + fade symbol.</p> +*/ +class PageDescriptor + : public ::std::enable_shared_from_this<PageDescriptor> +{ +public: + /** Create a PageDescriptor for the given SdPage object. + @param rxPage + The page that is represented by the new PageDescriptor object. + @param pPage + The page pointer can in some situations not be detected from + rxPage, e.g. after undo of page deletion. Therefore supply it + separately. + @param nIndex + This index is displayed in the view as page number. It is not + necessarily the page index (not even when you add or subtract 1 + or use (x-1)/2 magic). + */ + PageDescriptor ( + const css::uno::Reference<css::drawing::XDrawPage>& rxPage, + SdPage* pPage, + const sal_Int32 nIndex); + + ~PageDescriptor(); + + /** Return the page that is represented by the descriptor as SdPage pointer . + */ + SdPage* GetPage() const { return mpPage;} + + /** Return the page that is represented by the descriptor as XDrawPage reference. + */ + const css::uno::Reference<css::drawing::XDrawPage>& GetXDrawPage() const { return mxPage;} + + /** Returns the index of the page as it is displayed in the view as page + number. The value may differ from the index returned by the + XDrawPage when there are hidden slides and the XIndexAccess used to + access the model filters them out. + */ + sal_Int32 GetPageIndex() const { return mnIndex;} + void SetPageIndex (const sal_Int32 nIndex); + + bool UpdateMasterPage(); + bool UpdateTransitionFlag(); + + enum State { ST_Visible, ST_Selected, ST_WasSelected, + ST_Focused, ST_MouseOver, ST_Current, ST_Excluded }; + + bool HasState (const State eState) const; + + bool SetState (const State eState, const bool bStateValue); + + /** Set the internal mbIsSelected flag to the selection state of the + page. Use this method to synchronize a page descriptor with the + page it describes and determine whether a redraw to update the + selection indicator is necessary. + @return + When the two selection states were different <TRUE/> is + returned. When they were the same this method returns + <FALSE/>. + */ + bool GetCoreSelection(); + + /** Set the selection flags of the SdPage objects to the corresponding + selection states of the page descriptors. + */ + void SetCoreSelection(); + + VisualState& GetVisualState() { return maVisualState;} + + ::tools::Rectangle GetBoundingBox() const; + Point GetLocation (const bool bIgnoreLocation) const; + void SetBoundingBox (const ::tools::Rectangle& rBoundingBox); + +private: + SdPage* mpPage; + css::uno::Reference<css::drawing::XDrawPage> mxPage; + SdrPage const* mpMasterPage; + + /** This index is displayed as page number in the view. It may or may + not be the actual page index. + */ + sal_Int32 mnIndex; + + ::tools::Rectangle maBoundingBox; + VisualState maVisualState; + + bool mbIsSelected : 1; + bool mbWasSelected : 1; + bool mbIsVisible : 1; + bool mbIsFocused : 1; + bool mbIsCurrent : 1; + bool mbIsMouseOver : 1; + bool mbHasTransition : 1; + + PageDescriptor (const PageDescriptor& rDescriptor) = delete; + + PageDescriptor& operator= (const PageDescriptor& rDescriptor) = delete; +}; + +} // end of namespace ::sd::slidesorter::model + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/model/SlsPageEnumeration.hxx b/sd/source/ui/slidesorter/inc/model/SlsPageEnumeration.hxx new file mode 100644 index 000000000..6901a9ff1 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/model/SlsPageEnumeration.hxx @@ -0,0 +1,95 @@ +/* -*- 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 . + */ + +#pragma once + +#include <model/SlsEnumeration.hxx> +#include <model/SlsSharedPageDescriptor.hxx> + +#include <functional> +#include <memory> + +namespace sd::slidesorter::model +{ +class SlideSorterModel; + +/** Public class of page enumerations that delegates its calls to an + implementation object that can filter pages by using a given predicate. + + @see PageEnumerationProvider + The PageEnumerationProvider has methods for creating different types + of page enumerations. +*/ +class PageEnumeration final : public Enumeration<SharedPageDescriptor> +{ +public: + /** Create a new page enumeration that enumerates a subset of the pages + of the given model. + @param rModel + The new page enumeration enumerates the pages of this model. + @param rPredicate + This predicate determines which pages to include in the + enumeration. Pages for which rPredicate returns <FALSE/> are + exclude. + */ + typedef ::std::function<bool(const SharedPageDescriptor&)> PagePredicate; + static PageEnumeration Create(const SlideSorterModel& rModel, const PagePredicate& rPredicate); + + /** This copy constructor creates a copy of the given enumeration. + */ + PageEnumeration(const PageEnumeration& rEnumeration); + + virtual ~PageEnumeration() override; + + /** Create and return an exact copy of the called object. + */ + virtual ::std::unique_ptr<Enumeration<SharedPageDescriptor>> Clone() override; + + PageEnumeration& operator=(const PageEnumeration& rEnumeration); + + /** Return <TRUE/> when the enumeration has more elements, i.e. it is + save to call GetNextElement() at least one more time. + */ + virtual bool HasMoreElements() const override; + + /** Return the next element of the enumeration. Call the + HasMoreElements() before to make sure that there exists at least one + more element. Calling this method with HasMoreElements() returning + <FALSE/> is an error. + */ + virtual SharedPageDescriptor GetNextElement() override; + + /** Rewind the enumeration so that the next call to GetNextElement() + will return its first element. + */ + virtual void Rewind() override; + +private: + /// Implementation object. + ::std::unique_ptr<Enumeration<SharedPageDescriptor>> mpImpl; + + /** This constructor expects an implementation object that holds + the predicate that filters the pages. + */ + PageEnumeration(::std::unique_ptr<Enumeration<SharedPageDescriptor>>&& pImpl); +}; + +} // end of namespace ::sd::slidesorter::model + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/model/SlsPageEnumerationProvider.hxx b/sd/source/ui/slidesorter/inc/model/SlsPageEnumerationProvider.hxx new file mode 100644 index 000000000..b6de98d13 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/model/SlsPageEnumerationProvider.hxx @@ -0,0 +1,51 @@ +/* -*- 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 . + */ + +#pragma once + +#include <model/SlsPageEnumeration.hxx> + +namespace sd::slidesorter::model +{ +class SlideSorterModel; + +/** Collection of methods that create enumeration of slides. +*/ +class PageEnumerationProvider +{ +public: + /** The returned enumeration of slides iterates over all slides of the + given model. + */ + static PageEnumeration CreateAllPagesEnumeration(const SlideSorterModel& rModel); + + /** The returned enumeration of slides iterates over the currently + selected slides of the given model. + */ + static PageEnumeration CreateSelectedPagesEnumeration(const SlideSorterModel& rModel); + + /** The returned enumeration of slides iterates over the slides + (partially) inside the visible area. + */ + static PageEnumeration CreateVisiblePagesEnumeration(const SlideSorterModel& rModel); +}; + +} // end of namespace ::sd::slidesorter::model + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/model/SlsSharedPageDescriptor.hxx b/sd/source/ui/slidesorter/inc/model/SlsSharedPageDescriptor.hxx new file mode 100644 index 000000000..6ee1e2b22 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/model/SlsSharedPageDescriptor.hxx @@ -0,0 +1,32 @@ +/* -*- 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 . + */ + +#pragma once + +#include <memory> + +namespace sd::slidesorter::model +{ +class PageDescriptor; + +typedef std::shared_ptr<PageDescriptor> SharedPageDescriptor; + +} // end of namespace ::sd::slidesorter::model + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/model/SlsVisualState.hxx b/sd/source/ui/slidesorter/inc/model/SlsVisualState.hxx new file mode 100644 index 000000000..89eae16ca --- /dev/null +++ b/sd/source/ui/slidesorter/inc/model/SlsVisualState.hxx @@ -0,0 +1,47 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/types.h> +#include <tools/gen.hxx> + +namespace sd::slidesorter::model +{ +/** This class gives access to values related to the visualization of page + objects. This includes animation state when blending from one state to + another. +*/ +class VisualState +{ +public: + VisualState(const sal_Int32 nPageId); + + const Point& GetLocationOffset() const { return maLocationOffset; } + void SetLocationOffset(const Point& rPoint); + + sal_Int32 mnPageId; // For debugging + +private: + Point maLocationOffset; +}; + +} // end of namespace ::sd::slidesorter::model + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/view/SlideSorterView.hxx b/sd/source/ui/slidesorter/inc/view/SlideSorterView.hxx new file mode 100644 index 000000000..0f3493ab3 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/view/SlideSorterView.hxx @@ -0,0 +1,225 @@ +/* -*- 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 . + */ + +#pragma once + +#include <model/SlsPageDescriptor.hxx> +#include <model/SlsSharedPageDescriptor.hxx> +#include <view/SlsLayouter.hxx> +#include <view/SlsILayerPainter.hxx> +#include <o3tl/deleter.hxx> + +#include <View.hxx> +#include <tools/gen.hxx> +#include <vcl/region.hxx> +#include <memory> + +namespace sd::slidesorter::cache { class PageCache; } +namespace sd::slidesorter::model { class SlideSorterModel; } +namespace sd { class Window; } +namespace sd::slidesorter { class SlideSorter; } +namespace sd::slidesorter::view { + +class LayeredDevice; +class PageObjectPainter; +class ToolTip; + +class SlideSorterView final + : public sd::View +{ +public: + + /** Create a new view for the slide sorter. + @param rViewShell + This reference is simply passed to the base class and not used + by this class. + + */ + explicit SlideSorterView (SlideSorter& rSlideSorter); + void Init(); + + virtual ~SlideSorterView() override; + void Dispose(); + + SlideSorterView(const SlideSorterView&) = delete; + SlideSorterView& operator=(const SlideSorterView&) = delete; + + /** Set the general way of layouting the page objects. Note that this + method does not trigger any repaints or layouts. + */ + bool SetOrientation (const Layouter::Orientation eOrientation); + Layouter::Orientation GetOrientation() const { return meOrientation;} + + void RequestRepaint(); + void RequestRepaint (const model::SharedPageDescriptor& rDescriptor); + void RequestRepaint (const ::tools::Rectangle& rRepaintBox); + void RequestRepaint (const vcl::Region& rRepaintRegion); + + ::tools::Rectangle GetModelArea() const; + + /** Return the index of the page that is rendered at the given position. + @param rPosition + The position is expected to be in pixel coordinates. + @return + The returned index is -1 when there is no page object at the + given position. + */ + sal_Int32 GetPageIndexAtPoint (const Point& rPosition) const; + + view::Layouter& GetLayouter(); + + virtual void ModelHasChanged() override; + + /** This method is typically called before a model change takes place. + All references to model data are released. PostModelChange() has to + be called to complete the handling of the model change. When the + calls to Pre- and PostModelChange() are very close to each other you + may call HandleModelChange() instead. + */ + void PreModelChange(); + + /** This method is typically called after a model change took place. + References to model data are re-allocated. Call this method only + after PreModelChange() has been called. + */ + void PostModelChange(); + + /** This method is a convenience function that simply calls + PreModelChange() and then PostModelChange(). + */ + void HandleModelChange(); + + void HandleDrawModeChange(); + + void Resize(); + virtual void CompleteRedraw ( + OutputDevice* pDevice, + const vcl::Region& rPaintArea, + sdr::contact::ViewObjectContactRedirector* pRedirector = nullptr) override; + void Paint (OutputDevice& rDevice, const ::tools::Rectangle& rRepaintArea); + + virtual void ConfigurationChanged ( + utl::ConfigurationBroadcaster* pBroadcaster, + ConfigurationHints nHint) override; + + void HandleDataChangeEvent(); + + void Layout(); + /** This tells the view that it has to re-determine the visibility of + the page objects before painting them the next time. + */ + void InvalidatePageObjectVisibilities(); + + std::shared_ptr<cache::PageCache> const & GetPreviewCache(); + + /** Return the range of currently visible page objects including the + first and last one in that range. + @return + The returned pair of page object indices is empty when the + second index is lower than the first. + */ + Range const & GetVisiblePageRange(); + + /** Add a shape to the page. Typically used from inside + PostModelChange(). + */ + // void AddSdrObject (SdrObject& rObject); + + /** Add a listener that is called when the set of visible slides. + @param rListener + When this method is called multiple times for the same listener + the second and all following calls are ignored. Each listener + is added only once. + */ + void AddVisibilityChangeListener (const Link<LinkParamNone*,void>& rListener); + + /** Remove a listener that is called when the set of visible slides changes. + @param rListener + It is safe to pass a listener that was not added or has been + removed previously. Such calls are ignored. + */ + void RemoveVisibilityChangeListener (const Link<LinkParamNone*,void>& rListener); + + /** The page under the mouse is not highlighted in some contexts. Call + this method on context changes. + */ + void UpdatePageUnderMouse (); + void UpdatePageUnderMouse (const Point& rMousePosition); + void SetPageUnderMouse (const model::SharedPageDescriptor& rpDescriptor); + + bool SetState ( + const model::SharedPageDescriptor& rpDescriptor, + const model::PageDescriptor::State eState, + const bool bStateValue); + + void UpdateOrientation(); + + std::shared_ptr<PageObjectPainter> const & GetPageObjectPainter(); + const std::shared_ptr<LayeredDevice>& GetLayeredDevice() const { return mpLayeredDevice;} + + class DrawLock + { + public: + DrawLock (SlideSorter const & rSlideSorter); + ~DrawLock(); + /** When the DrawLock is disposed then it will not request a repaint + on destruction. + */ + void Dispose(); + private: + view::SlideSorterView& mrView; + VclPtr<sd::Window> mpWindow; + }; + + ToolTip& GetToolTip() const; + + virtual void DragFinished (sal_Int8 nDropAction) override; + +private: + SlideSorter& mrSlideSorter; + model::SlideSorterModel& mrModel; + bool mbIsDisposed; + ::std::unique_ptr<Layouter> mpLayouter; + bool mbPageObjectVisibilitiesValid; + std::shared_ptr<cache::PageCache> mpPreviewCache; + std::shared_ptr<LayeredDevice> mpLayeredDevice; + Range maVisiblePageRange; + Size maPreviewSize; + bool mbPreciousFlagUpdatePending; + Layouter::Orientation meOrientation; + model::SharedPageDescriptor mpPageUnderMouse; + std::shared_ptr<PageObjectPainter> mpPageObjectPainter; + vcl::Region maRedrawRegion; + SharedILayerPainter mpBackgroundPainter; + std::unique_ptr<ToolTip, o3tl::default_delete<ToolTip>> mpToolTip; + bool mbIsRearrangePending; + ::std::vector<Link<LinkParamNone*,void>> maVisibilityChangeListeners; + + /** Determine the visibility of all page objects. + */ + void DeterminePageObjectVisibilities(); + + void UpdatePreciousFlags(); + void RequestRearrange(); + void Rearrange(); +}; + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/view/SlsILayerPainter.hxx b/sd/source/ui/slidesorter/inc/view/SlsILayerPainter.hxx new file mode 100644 index 000000000..57b90af0a --- /dev/null +++ b/sd/source/ui/slidesorter/inc/view/SlsILayerPainter.hxx @@ -0,0 +1,53 @@ +/* -*- 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 . + */ + +#pragma once + +#include <memory> + +class OutputDevice; +namespace tools { class Rectangle; } + +namespace sd::slidesorter::view { + +class ILayerInvalidator +{ +public: + virtual ~ILayerInvalidator() {} + + virtual void Invalidate (const ::tools::Rectangle& rInvalidationBox) = 0; +}; +typedef std::shared_ptr<ILayerInvalidator> SharedILayerInvalidator; + +class ILayerPainter +{ +public: + virtual ~ILayerPainter() {} + + virtual void SetLayerInvalidator ( + const SharedILayerInvalidator& rpInvalidator) = 0; + virtual void Paint ( + OutputDevice& rDevice, + const ::tools::Rectangle& rRepaintArea) = 0; +}; +typedef std::shared_ptr<ILayerPainter> SharedILayerPainter; + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/view/SlsInsertAnimator.hxx b/sd/source/ui/slidesorter/inc/view/SlsInsertAnimator.hxx new file mode 100644 index 000000000..c74d06cb9 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/view/SlsInsertAnimator.hxx @@ -0,0 +1,59 @@ +/* -*- 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 . + */ + +#pragma once + +#include <controller/SlsAnimator.hxx> +#include <memory> + +namespace sd::slidesorter::view +{ +class InsertPosition; + +/** Animate the positions of page objects to make room at the insert + position while a move or copy operation takes place. +*/ +class InsertAnimator +{ +public: + explicit InsertAnimator(SlideSorter& rSlideSorter); + InsertAnimator(const InsertAnimator&) = delete; + InsertAnimator& operator=(const InsertAnimator&) = delete; + + /** Set the position at which we have to make room for the display of an + icon. + */ + void SetInsertPosition(const InsertPosition& rInsertPosition); + + /** Restore the normal position of all page objects. + @param eMode + This flag controls whether to start an animation that ends in the + normal positions of all slides (AM_Animated) or to restore the + normal positions immediately (AM_Immediate). + */ + void Reset(const controller::Animator::AnimationMode eMode); + +private: + class Implementation; + std::shared_ptr<Implementation> mpImplementation; +}; + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/view/SlsInsertionIndicatorOverlay.hxx b/sd/source/ui/slidesorter/inc/view/SlsInsertionIndicatorOverlay.hxx new file mode 100644 index 000000000..3f4cc2218 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/view/SlsInsertionIndicatorOverlay.hxx @@ -0,0 +1,101 @@ +/* -*- 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 . + */ + +#pragma once + +#include <view/SlsILayerPainter.hxx> +#include <controller/SlsTransferableData.hxx> + +#include <tools/gen.hxx> +#include <vcl/bitmapex.hxx> +#include <memory> +#include <vector> + +class OutputDevice; +class SdTransferable; + +namespace sd::slidesorter { class SlideSorter; } + +namespace sd::slidesorter::view { + +class FramePainter; + +/** The insertion indicator is painted as a vertical or horizontal bar + in the space between slides. +*/ +class InsertionIndicatorOverlay final + : public ILayerPainter, + public std::enable_shared_from_this<InsertionIndicatorOverlay> +{ +public: + InsertionIndicatorOverlay (SlideSorter& rSlideSorter); + virtual ~InsertionIndicatorOverlay() override; + + virtual void SetLayerInvalidator (const SharedILayerInvalidator& rpInvalidator) override; + + void Create (const SdTransferable* pTransferable); + + /** Given a position in model coordinates this method calculates the + insertion marker both as an index in the document and as a location + used for drawing the insertion indicator. + */ + void SetLocation (const Point& rPosition); + + Size GetSize() const; + + virtual void Paint ( + OutputDevice& rDevice, + const ::tools::Rectangle& rRepaintArea) override; + + bool IsVisible() const { return mbIsVisible;} + void Hide(); + void Show(); + + ::tools::Rectangle GetBoundingBox() const; + +private: + SlideSorter& mrSlideSorter; + bool mbIsVisible; + SharedILayerInvalidator mpLayerInvalidator; + // Center of the insertion indicator. + Point maLocation; + BitmapEx maIcon; + std::unique_ptr<FramePainter> mpShadowPainter; + + Point PaintRepresentatives ( + OutputDevice& rContent, + const Size& rPreviewSize, + const sal_Int32 nOffset, + const ::std::vector<controller::TransferableData::Representative>& rPages) const; + void PaintPageCount ( + OutputDevice& rDevice, + const sal_Int32 nSelectionCount, + const Size& rPreviewSize, + const Point& rFirstPageOffset) const; + /** Setup the insertion indicator by creating the icon. It consists of + scaled down previews of some of the selected pages. + */ + void Create ( + const ::std::vector<controller::TransferableData::Representative>& rPages, + const sal_Int32 nSelectionCount); +}; + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/view/SlsLayouter.hxx b/sd/source/ui/slidesorter/inc/view/SlsLayouter.hxx new file mode 100644 index 000000000..b91ae83c5 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/view/SlsLayouter.hxx @@ -0,0 +1,237 @@ +/* -*- 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 . + */ + +#pragma once + +#include <vcl/vclptr.hxx> +#include <tools/gen.hxx> +#include <sal/types.h> +#include <memory> + +namespace sd { class Window; } +namespace sd::slidesorter::model { class SlideSorterModel; } +namespace sd::slidesorter::view { class PageObjectLayouter; } +namespace sd::slidesorter::view { class Theme; } + +namespace sd::slidesorter::view { + +class InsertPosition; + +/** Calculate the size and position of page objects displayed by a slide + sorter. The layouter takes into account various input values: + 1.) Size of the window in which the slide sorter is displayed. + 2.) Desired and minimal and maximal widths of page objects. + 3.) Minimal and maximal number of columns. + 4.) Vertical and horizontal gaps between objects in adjacent columns. + 5.) Borders around every page object. + 6.) Vertical and horizontal borders between enclosing page and outer + page objects. + From these, it calculates various output values: + 1.) The width of page objects. + 2.) The number of columns. + 3.) The size of the enclosing page. + + <p>Sizes and lengths are all in pixel except where explicitly stated + otherwise.</p> + + <p>The GetIndex... methods may return indices that are larger than or + equal to (zero based) the number of pages. This is so because the + number of pages is not known to the class instances. Indices are + calculated with reference to the general grid layout of page + objects.</p> +*/ +class Layouter +{ +public: + enum Orientation { HORIZONTAL, VERTICAL, GRID }; + + Layouter ( + sd::Window *rpWindow, + const std::shared_ptr<Theme>& rpTheme); + ~Layouter(); + + std::shared_ptr<PageObjectLayouter> const & GetPageObjectLayouter() const; + /** Set the interval of valid column counts. When nMinimalColumnCount + <= nMaximalColumnCount is not fulfilled then the call is ignored. + @param nMinimalColumnCount + The default value is 1. The question whether higher values make + any sense is left to the caller. + @param nMaximalColumnCount + The default value is 5. + */ + void SetColumnCount (sal_Int32 nMinimalColumnCount, + sal_Int32 nMaximalColumnCount); + + /** Central method of this class. It takes the input values and + calculates the output values. Both given sizes must not be 0 in any + dimension or the call is ignored. + @param eOrientation + This defines the generally layout and specifies whether there may + be more than one row or more than one column. + @param rWindowSize + The size of the window in pixels that the slide sorter is + displayed in. This can differ from the size of mpWindow during + detection of whether or not the scroll bars should be visible. + @param rPreviewModelSize + Size of each page in model coordinates. + @param rpWindow + The map mode of this window is adapted to the new layout of the + page objects. + @return + The return value indicates whether the Get... methods can be + used to obtain valid values (<TRUE/>). + */ + bool Rearrange ( + const Orientation eOrientation, + const Size& rWindowSize, + const Size& rPreviewModelSize, + const sal_uInt32 nPageCount); + + /** Return the number of columns. + */ + sal_Int32 GetColumnCount() const; + + sal_Int32 GetIndex (const sal_Int32 nRow, const sal_Int32 nColumn) const; + + Size const & GetPageObjectSize() const; + + /** Return the bounding box in window coordinates of the nIndex-th page + object. + */ + ::tools::Rectangle GetPageObjectBox ( + const sal_Int32 nIndex, + const bool bIncludeBorderAndGap) const; + + /** Return the bounding box in model coordinates of the page that + contains the given amount of page objects. + */ + ::tools::Rectangle GetTotalBoundingBox() const; + + /** Return the index of the first fully or partially visible page + object. This takes into account only the vertical dimension. + @return + The second index may be larger than the number of existing + page objects. + */ + Range GetRangeOfVisiblePageObjects (const ::tools::Rectangle& rVisibleArea) const; + + /** Return the index of the page object that is rendered at the given + point. + @param rPosition + The position is expected to be in model coordinates relative to + the page origin. + @param bIncludePageBorders + When <TRUE/> then include the page borders into the calculation, + i.e. when a point lies in the border of a page object but not on + the actual page area the index of that page is returned; + otherwise -1 would be returned to indicate that no page object + has been hit. + @param bClampToValidRange + When <TRUE/> then values outside the valid range [0,mnPageCount) + are mapped to 0 (when smaller than 0) or mnPageCount-1 when + equal to or larger than mnPageCount. + When <FALSE/> then -1 is returned for values outside the valid range. + @return + The returned index may be larger than the number of existing + page objects. + */ + sal_Int32 GetIndexAtPoint ( + const Point& rModelPosition, + const bool bIncludePageBorders, + const bool bClampToValidRange = true) const; + + /** Return an object that describes the logical and visual properties of + where to do an insert operation when the user would release the + mouse button at the given position after a drag operation and of + where and how to display an insertion indicator. + @param rModelPosition + The position in the model coordinate system for which to + determine the insertion page index. The position does not have + to be over a page object to return a valid value. + @param rIndicatorSize + The size of the insertion indicator. This size is used to adapt + the location when at the left or right of a row or at the top or + bottom of a column. + @param rModel + The model is used to get access to the selection states of the + pages. This in turn is used to determine the visual bounding + boxes. + */ + InsertPosition GetInsertPosition ( + const Point& rModelPosition, + const Size& rIndicatorSize, + model::SlideSorterModel const & rModel) const; + + Range GetValidHorizontalSizeRange() const; + Range GetValidVerticalSizeRange() const; + + class Implementation; + +private: + std::unique_ptr<Implementation> mpImplementation; + VclPtr<sd::Window> mpWindow; +}; + +/** Collect all values concerning the logical and visual properties of the + insertion position that is used for drag-and-drop and copy-and-paste. +*/ +class InsertPosition +{ +public: + InsertPosition(); + bool operator== (const InsertPosition& rInsertPosition) const; + bool operator!= (const InsertPosition& rInsertPosition) const; + + void SetLogicalPosition ( + const sal_Int32 nRow, + const sal_Int32 nColumn, + const sal_Int32 nIndex, + const bool bIsAtRunStart, + const bool bIsAtRunEnd, + const bool bIsExtraSpaceNeeded); + void SetGeometricalPosition( + const Point& rLocation, + const Point& rLeadingOffset, + const Point& rTrailingOffset); + + sal_Int32 GetRow() const { return mnRow; } + sal_Int32 GetColumn() const { return mnColumn; } + sal_Int32 GetIndex() const { return mnIndex; } + const Point& GetLocation() const { return maLocation; } + const Point& GetLeadingOffset() const { return maLeadingOffset; } + const Point& GetTrailingOffset() const { return maTrailingOffset; } + bool IsAtRunStart() const { return mbIsAtRunStart; } + bool IsAtRunEnd() const { return mbIsAtRunEnd; } + bool IsExtraSpaceNeeded() const { return mbIsExtraSpaceNeeded; } + +private: + sal_Int32 mnRow; + sal_Int32 mnColumn; + sal_Int32 mnIndex; + bool mbIsAtRunStart : 1; + bool mbIsAtRunEnd : 1; + bool mbIsExtraSpaceNeeded : 1; + Point maLocation; + Point maLeadingOffset; + Point maTrailingOffset; +}; + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/view/SlsPageObjectLayouter.hxx b/sd/source/ui/slidesorter/inc/view/SlsPageObjectLayouter.hxx new file mode 100644 index 000000000..8bb77a988 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/view/SlsPageObjectLayouter.hxx @@ -0,0 +1,144 @@ +/* -*- 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 . + */ + +#pragma once + +#include <model/SlsSharedPageDescriptor.hxx> +#include <tools/gen.hxx> +#include <vcl/image.hxx> + +namespace vcl { class Font; } +namespace sd { class Window; } + +namespace sd::slidesorter::view { + +/** In contrast to the Layouter that places page objects in the view, the + PageObjectLayouter places the parts of individual page objects like page + number area, borders, preview. +*/ +class PageObjectLayouter +{ +public: + /** Create a new PageObjectLayouter object. + @param rPageObjectSize + In general either the width or the height will be 0 in order to + signal that this size component has to be calculated from the other. + This calculation will make the preview as large as possible. + @param nPageCount + The page count is used to determine how wide the page number + area has to be, how many digits to except for the largest page number. + */ + PageObjectLayouter( + const Size& rPageObjectWindowSize, + const Size& rPreviewModelSize, + sd::Window *pWindow, + const sal_Int32 nPageCount); + ~PageObjectLayouter(); + + enum class Part { + // The focus indicator is painted outside the actual page object. + FocusIndicator, + // This is the outer bounding box that includes the preview, page + // number, title. + PageObject, + // Bounding box of the actual preview. + Preview, + // Bounding box of the page number. + PageNumber, + // Indicator whether or not there is a slide transition associated + // with this slide. + TransitionEffectIndicator, + // Indicator whether or not there is a custom animation associated + // with this slide. + CustomAnimationEffectIndicator + }; + /** Two coordinate systems are supported. They differ only in + translation not in scale. Both relate to pixel values in the window. + A position in the model coordinate system does not change when the window content is + scrolled up or down. In the window coordinate system (relative + to the top left point of the window)scrolling leads to different values. + */ + enum CoordinateSystem { + WindowCoordinateSystem, + ModelCoordinateSystem + }; + + /** Return the bounding box of the page object or one of its graphical + parts. + @param rWindow + This device is used to translate between model and window + coordinates. + @param rpPageDescriptor + The page for which to calculate the bounding box. This may be + NULL. When it is NULL then a generic bounding box is calculated + for the location (0,0). + @param ePart + The part of the page object for which to return the bounding + box. + @param eCoordinateSystem + The bounding box can be returned in model and in pixel + (window) coordinates. + @param bIgnoreLocation + Return a position ignoring the slides' location, ie. as if + we were the first slide. + */ + ::tools::Rectangle GetBoundingBox ( + const model::SharedPageDescriptor& rpPageDescriptor, + const Part ePart, + const CoordinateSystem eCoordinateSystem, + bool bIgnoreLocation = false); + + /// the size of the embedded preview: position independent, in window coordinate system + Size GetPreviewSize(); + + /// the maximum size of each tile, also position independent, in window coordinate system + Size GetGridMaxSize(); + + const Image& GetTransitionEffectIcon() const { return maTransitionEffectIcon;} + const Image& GetCustomAnimationEffectIcon() const { return maCustomAnimationEffectIcon;} + +private: + ::tools::Rectangle GetBoundingBox ( + const Point& rPageObjectLocation, + const Part ePart, + const CoordinateSystem eCoordinateSystem); + +private: + VclPtr<sd::Window> mpWindow; + ::tools::Rectangle maFocusIndicatorBoundingBox; + ::tools::Rectangle maPageObjectBoundingBox; + ::tools::Rectangle maPageNumberAreaBoundingBox; + ::tools::Rectangle maPreviewBoundingBox; + ::tools::Rectangle maTransitionEffectBoundingBox; + ::tools::Rectangle maCustomAnimationEffectBoundingBox; + const Image maTransitionEffectIcon; + const Image maCustomAnimationEffectIcon; + const std::shared_ptr<vcl::Font> mpPageNumberFont; + + Size GetPageNumberAreaSize (const int nPageCount); + ::tools::Rectangle CalculatePreviewBoundingBox ( + Size& rPageObjectSize, + const Size& rPreviewModelSize, + const sal_Int32 nPageNumberAreaWidth, + const sal_Int32 nFocusIndicatorWidth); +}; + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/view/SlsPageObjectPainter.hxx b/sd/source/ui/slidesorter/inc/view/SlsPageObjectPainter.hxx new file mode 100644 index 000000000..747c09500 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/view/SlsPageObjectPainter.hxx @@ -0,0 +1,119 @@ +/* -*- 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 . + */ + +#pragma once + +#include <model/SlsSharedPageDescriptor.hxx> +#include <view/SlsTheme.hxx> +#include <memory> + +namespace sd::slidesorter::cache { class PageCache; } +namespace sd::slidesorter { class SlideSorter; } + +namespace sd::slidesorter::view { + +class Layouter; +class PageObjectLayouter; +class FramePainter; + +class PageObjectPainter +{ +public: + PageObjectPainter (const SlideSorter& rSlideSorter); + ~PageObjectPainter(); + + void PaintPageObject ( + OutputDevice& rDevice, + const model::SharedPageDescriptor& rpDescriptor); + + /** Called when the theme changes, either because it is replaced with + another or because the system colors have changed. So, even when + the given theme is the same object as the one already in use by this + painter everything that depends on the theme is updated. + */ + void SetTheme (const std::shared_ptr<view::Theme>& rpTheme); + + /** Return a preview bitmap for the given page descriptor. When the + page is excluded from the show then the preview is marked + accordingly. + @rpDescriptor + Defines the page for which to return the preview. + @pReferenceDevice + When not <NULL/> then this reference device is used to created a + compatible bitmap. + @return + The returned bitmap may have a different size then the preview area. + */ + BitmapEx GetPreviewBitmap ( + const model::SharedPageDescriptor& rpDescriptor, + const OutputDevice* pReferenceDevice) const; + +private: + const Layouter& mrLayouter; + std::shared_ptr<cache::PageCache> mpCache; + std::shared_ptr<view::Theme> mpTheme; + std::shared_ptr<vcl::Font> mpPageNumberFont; + std::unique_ptr<FramePainter> mpShadowPainter; + std::unique_ptr<FramePainter> mpFocusBorderPainter; + + void PaintBackground ( + PageObjectLayouter *pPageObjectLayouter, + OutputDevice& rDevice, + const model::SharedPageDescriptor& rpDescriptor) const; + void PaintPreview ( + PageObjectLayouter *pPageObjectLayouter, + OutputDevice& rDevice, + const model::SharedPageDescriptor& rpDescriptor) const; + void PaintPageNumber ( + PageObjectLayouter *pPageObjectLayouter, + OutputDevice& rDevice, + const model::SharedPageDescriptor& rpDescriptor) const; + static void PaintTransitionEffect ( + PageObjectLayouter *pPageObjectLayouter, + OutputDevice& rDevice, + const model::SharedPageDescriptor& rpDescriptor); + static void PaintCustomAnimationEffect ( + PageObjectLayouter *pPageObjectLayouter, + OutputDevice& rDevice, + const model::SharedPageDescriptor& rpDescriptor); + void PaintBorder ( + OutputDevice& rDevice, + const Theme::GradientColorType eColorType, + const ::tools::Rectangle& rBox) const; + void PaintBackgroundDetail( + PageObjectLayouter *pPageObjectLayouter, + OutputDevice& rDevice, + const model::SharedPageDescriptor& rpDescriptor) const; + + static BitmapEx CreateMarkedPreview( + const Size& rSize, + const BitmapEx& rPreview, + const BitmapEx& rOverlay, + const OutputDevice* pReferenceDevice); + + /** Update the local pointer to the page object layouter to the + one owned by the general layouter. + Return <TRUE/> when after the call we have a valid page object layouter. + */ + bool UpdatePageObjectLayouter(); +}; + +} // end of namespace sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/view/SlsTheme.hxx b/sd/source/ui/slidesorter/inc/view/SlsTheme.hxx new file mode 100644 index 000000000..efb7b2a3e --- /dev/null +++ b/sd/source/ui/slidesorter/inc/view/SlsTheme.hxx @@ -0,0 +1,135 @@ +/* -*- 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 . + */ + +#pragma once + +#include <vcl/bitmapex.hxx> +#include <tools/color.hxx> + +#include <memory> + +namespace vcl { class Font; } + +namespace sd::slidesorter::controller { class Properties; } + +namespace sd::slidesorter::view { + +const int Theme_FocusIndicatorWidth = 3; + +/** Collection of colors and styles that are used to paint the slide sorter + view. +*/ +class Theme +{ +public: + Theme (const std::shared_ptr<controller::Properties>& rpProperties); + + /** Call this method to update some colors as response to a change of + a system color change. + */ + void Update ( + const std::shared_ptr<controller::Properties>& rpProperties); + + // BitmapEx GetInsertIndicatorIcon() const; + + enum FontType { + Font_PageNumber, + Font_PageCount + }; + static std::shared_ptr<vcl::Font> GetFont ( + const FontType eType, + const OutputDevice& rDevice); + + enum ColorType { + Color_Background, + Color_PageNumberDefault, + Color_PageNumberHover, + Color_PageNumberHighContrast, + Color_PageNumberBrightBackground, + Color_PageNumberDarkBackground, + Color_Selection, + Color_PreviewBorder, + Color_PageCountFontColor, + ColorType_Size_ + }; + Color GetColor (const ColorType eType); + + enum GradientColorType { + Gradient_NormalPage, + Gradient_SelectedPage, + Gradient_SelectedAndFocusedPage, + Gradient_MouseOverPage, + Gradient_MouseOverSelected, + Gradient_MouseOverSelectedAndFocusedPage, + Gradient_FocusedPage, + GradientColorType_Size_ + }; + enum class GradientColorClass { + Border1, + Border2, + Fill1, + Fill2 + }; + Color GetGradientColor ( + const GradientColorType eType, + const GradientColorClass eClass); + void SetGradient ( + const GradientColorType eType, + const Color aBaseColor, + const sal_Int32 nSaturationOverride, + const sal_Int32 nBrightnessOverride, + const sal_Int32 nFillStartOffset, + const sal_Int32 nFillEndOffset, + const sal_Int32 nBorderStartOffset, + const sal_Int32 nBorderEndOffset); + + enum IconType + { + Icon_RawShadow, + Icon_RawInsertShadow, + Icon_HideSlideOverlay, + Icon_FocusBorder, + IconType_Size_ + }; + const BitmapEx& GetIcon (const IconType eType); + +private: + class GradientDescriptor + { + public: + Color maFillColor1; + Color maFillColor2; + Color maBorderColor1; + Color maBorderColor2; + }; + Color maBackgroundColor; + ::std::vector<GradientDescriptor> maGradients; + ::std::vector<BitmapEx> maIcons; + ::std::vector<Color> maColor; + + GradientDescriptor& GetGradient (const GradientColorType eType); + /** Guarded initialization of the specified icon in the maIcons + container. + */ + void InitializeIcon(const IconType eType, const OUString& rResourceId); +}; + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/view/SlsToolTip.hxx b/sd/source/ui/slidesorter/inc/view/SlsToolTip.hxx new file mode 100644 index 000000000..6c3557e64 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/view/SlsToolTip.hxx @@ -0,0 +1,75 @@ +/* -*- 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 . + */ + +#pragma once + +#include <model/SlsSharedPageDescriptor.hxx> +#include <rtl/ustring.hxx> +#include <vcl/timer.hxx> + +namespace sd::slidesorter +{ +class SlideSorter; +} + +namespace sd::slidesorter::view +{ +/** Manage the display of tool tips. The tool tip text changes when the + mouse is moved from slide to slide or from button to button. + After the mouse enters a slide the first display of the tool tip is + delayed for a short time in order to not draw attention from the slide + or its button bar. +*/ +class ToolTip +{ +public: + ToolTip(SlideSorter& rSlideSorter); + ~ToolTip(); + + /** Set a new page. This modifies the default help text. After a page + change a timer is started to delay the display of the tool tip for + the new page. + @param rpPage + When this is empty then the tool tip is hidden. + */ + void SetPage(const model::SharedPageDescriptor& rpPage); + + /** Hide the tool tip. + @return + Returns whether the tool tip was visible at the time this method + was called. + */ + bool Hide(); + +private: + SlideSorter& mrSlideSorter; + model::SharedPageDescriptor mpDescriptor; + OUString msCurrentHelpText; + void* mnHelpWindowHandle; + Timer maShowTimer; + Timer maHiddenTimer; + + void DoShow(); + + DECL_LINK(DelayTrigger, Timer*, void); +}; + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/model/SlideSorterModel.cxx b/sd/source/ui/slidesorter/model/SlideSorterModel.cxx new file mode 100644 index 000000000..4d3e79656 --- /dev/null +++ b/sd/source/ui/slidesorter/model/SlideSorterModel.cxx @@ -0,0 +1,676 @@ +/* -*- 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 <model/SlideSorterModel.hxx> + +#include <SlideSorter.hxx> +#include <sal/log.hxx> +#include <model/SlsPageDescriptor.hxx> +#include <model/SlsPageEnumerationProvider.hxx> +#include <controller/SlideSorterController.hxx> +#include <controller/SlsPageSelector.hxx> +#include <controller/SlsCurrentSlideManager.hxx> +#include <controller/SlsSlotManager.hxx> +#include <com/sun/star/drawing/XDrawPagesSupplier.hpp> +#include <com/sun/star/drawing/XMasterPagesSupplier.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/XController.hpp> + +#include <vcl/uitest/logger.hxx> +#include <vcl/uitest/eventdescription.hxx> + +#include <ViewShellBase.hxx> +#include <DrawDocShell.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <FrameView.hxx> + +#include <o3tl/safeint.hxx> +#include <tools/diagnose_ex.h> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::slidesorter::model { + +namespace { + bool PrintModel (const SlideSorterModel& rModel) + { + for (sal_Int32 nIndex=0,nCount=rModel.GetPageCount(); nIndex<nCount; ++nIndex) + { + SharedPageDescriptor pDescriptor (rModel.GetPageDescriptor(nIndex)); + if (pDescriptor) + { + SAL_INFO( + "sd.sls", + nIndex << " " << pDescriptor->GetPageIndex() << " " + << pDescriptor->GetVisualState().mnPageId << " " + << FromCoreIndex(pDescriptor->GetPage()->GetPageNum()) + << " " << pDescriptor->GetPage()); + } + else + { + SAL_INFO("sd.sls", nIndex); + } + } + + return true; + } + bool CheckModel (const SlideSorterModel& rModel) + { + for (sal_Int32 nIndex=0,nCount=rModel.GetPageCount(); nIndex<nCount; ++nIndex) + { + SharedPageDescriptor pDescriptor (rModel.GetPageDescriptor(nIndex)); + if ( ! pDescriptor) + { + PrintModel(rModel); + assert(pDescriptor); + return false; + } + if (nIndex != pDescriptor->GetPageIndex()) + { + PrintModel(rModel); + assert(nIndex == pDescriptor->GetPageIndex()); + return false; + } + if (nIndex != pDescriptor->GetVisualState().mnPageId) + { + PrintModel(rModel); + assert(nIndex == pDescriptor->GetVisualState().mnPageId); + return false; + } + } + + return true; + } +} + +namespace { + +void collectUIInformation(const OUString& num, const OUString& rAction) +{ + EventDescription aDescription; + aDescription.aID = "impress_win_or_draw_win"; + aDescription.aParameters = {{"POS", num}}; + aDescription.aAction = rAction; + aDescription.aKeyWord = "ImpressWindowUIObject"; + aDescription.aParent = "MainWindow"; + + UITestLogger::getInstance().logEvent(aDescription); +} + +} + +SlideSorterModel::SlideSorterModel (SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter), + meEditMode(EditMode::Page), + maPageDescriptors(0) +{ +} + +SlideSorterModel::~SlideSorterModel() +{ + ClearDescriptorList (); +} + +void SlideSorterModel::Dispose() +{ + ClearDescriptorList (); +} + +SdDrawDocument* SlideSorterModel::GetDocument() +{ + if (mrSlideSorter.GetViewShellBase() != nullptr) + return mrSlideSorter.GetViewShellBase()->GetDocument(); + else + return nullptr; +} + +bool SlideSorterModel::SetEditMode (EditMode eEditMode) +{ + bool bEditModeChanged = false; + if (meEditMode != eEditMode) + { + meEditMode = eEditMode; + UpdatePageList(); + bEditModeChanged = true; + } + return bEditModeChanged; +} + +sal_Int32 SlideSorterModel::GetPageCount() const +{ + return maPageDescriptors.size(); +} + +SharedPageDescriptor SlideSorterModel::GetPageDescriptor ( + const sal_Int32 nPageIndex, + const bool bCreate) const +{ + ::osl::MutexGuard aGuard (maMutex); + + SharedPageDescriptor pDescriptor; + + if (nPageIndex>=0 && nPageIndex<GetPageCount()) + { + pDescriptor = maPageDescriptors[nPageIndex]; + if (pDescriptor == nullptr && bCreate && mxSlides.is()) + { + SdPage* pPage = GetPage(nPageIndex); + pDescriptor = std::make_shared<PageDescriptor>( + Reference<drawing::XDrawPage>(mxSlides->getByIndex(nPageIndex),UNO_QUERY), + pPage, + nPageIndex); + maPageDescriptors[nPageIndex] = pDescriptor; + } + } + + return pDescriptor; +} + +sal_Int32 SlideSorterModel::GetIndex (const Reference<drawing::XDrawPage>& rxSlide) const +{ + ::osl::MutexGuard aGuard (maMutex); + + // First try to guess the right index. + Reference<beans::XPropertySet> xSet (rxSlide, UNO_QUERY); + if (xSet.is()) + { + try + { + const Any aNumber (xSet->getPropertyValue("Number")); + sal_Int16 nNumber (-1); + aNumber >>= nNumber; + nNumber -= 1; + SharedPageDescriptor pDescriptor (GetPageDescriptor(nNumber, false)); + if (pDescriptor + && pDescriptor->GetXDrawPage() == rxSlide) + { + return nNumber; + } + } + catch (uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } + } + + // Guess was wrong, iterate over all slides and search for the right + // one. + const sal_Int32 nCount (maPageDescriptors.size()); + for (sal_Int32 nIndex=0; nIndex<nCount; ++nIndex) + { + SharedPageDescriptor pDescriptor (maPageDescriptors[nIndex]); + + // Make sure that the descriptor exists. Without it the given slide + // can not be found. + if (!pDescriptor) + { + // Call GetPageDescriptor() to create the missing descriptor. + pDescriptor = GetPageDescriptor(nIndex); + } + + if (pDescriptor->GetXDrawPage() == rxSlide) + return nIndex; + } + + return -1; +} + +sal_Int32 SlideSorterModel::GetIndex (const SdrPage* pPage) const +{ + if (pPage == nullptr) + return -1; + + ::osl::MutexGuard aGuard (maMutex); + + // First try to guess the right index. + sal_Int16 nNumber ((pPage->GetPageNum()-1)/2); + SharedPageDescriptor pDescriptor (GetPageDescriptor(nNumber, false)); + if (pDescriptor + && pDescriptor->GetPage() == pPage) + { + return nNumber; + } + + // Guess was wrong, iterate over all slides and search for the right + // one. + const sal_Int32 nCount (maPageDescriptors.size()); + for (sal_Int32 nIndex=0; nIndex<nCount; ++nIndex) + { + pDescriptor = maPageDescriptors[nIndex]; + + // Make sure that the descriptor exists. Without it the given slide + // can not be found. + if (!pDescriptor) + { + // Call GetPageDescriptor() to create the missing descriptor. + pDescriptor = GetPageDescriptor(nIndex); + } + + if (pDescriptor->GetPage() == pPage) + return nIndex; + } + + return -1; +} + +sal_uInt16 SlideSorterModel::GetCoreIndex (const sal_Int32 nIndex) const +{ + SharedPageDescriptor pDescriptor (GetPageDescriptor(nIndex)); + if (pDescriptor) + return pDescriptor->GetPage()->GetPageNum(); + else + return mxSlides->getCount()*2+1; +} + +/** For now this method uses a trivial algorithm: throw away all descriptors + and create them anew (on demand). The main problem that we are facing + when designing a better algorithm is that we can not compare pointers to + pages stored in the PageDescriptor objects and those obtained from the + document: pages may have been deleted and others may have been created + at the exact same memory locations. +*/ +void SlideSorterModel::Resync() +{ + ::osl::MutexGuard aGuard (maMutex); + + // Check if document and this model really differ. + bool bIsUpToDate (true); + SdDrawDocument* pDocument = GetDocument(); + if (pDocument!=nullptr && maPageDescriptors.size()==pDocument->GetSdPageCount(PageKind::Standard)) + { + for (sal_Int32 nIndex=0,nCount=maPageDescriptors.size(); nIndex<nCount; ++nIndex) + { + if (maPageDescriptors[nIndex] + && maPageDescriptors[nIndex]->GetPage() + != GetPage(nIndex)) + { + SAL_INFO("sd.sls", "page " << nIndex << " differs"); + bIsUpToDate = false; + break; + } + } + } + else + { + bIsUpToDate = false; + } + + if ( ! bIsUpToDate) + { + SynchronizeDocumentSelection(); // Try to make the current selection persistent. + ClearDescriptorList (); + AdaptSize(); + SynchronizeModelSelection(); + mrSlideSorter.GetController().GetPageSelector().CountSelectedPages(); + } + CheckModel(*this); +} + +void SlideSorterModel::ClearDescriptorList() +{ + ::std::vector<SharedPageDescriptor> aDescriptors; + + { + ::osl::MutexGuard aGuard (maMutex); + aDescriptors.swap(maPageDescriptors); + } + + for (auto& rxDescriptor : aDescriptors) + { + if (rxDescriptor != nullptr) + { + if (rxDescriptor.use_count() > 1) + { + SAL_INFO( + "sd.sls", + "trying to delete page descriptor that is still used with" + " count " << rxDescriptor.use_count()); + // No assertion here because that can hang the office when + // opening a dialog from here. + } + rxDescriptor.reset(); + } + } +} + +void SlideSorterModel::SynchronizeDocumentSelection() +{ + ::osl::MutexGuard aGuard (maMutex); + + PageEnumeration aAllPages (PageEnumerationProvider::CreateAllPagesEnumeration(*this)); + while (aAllPages.HasMoreElements()) + { + SharedPageDescriptor pDescriptor (aAllPages.GetNextElement()); + const bool bIsSelected (pDescriptor->HasState(PageDescriptor::ST_Selected)); + pDescriptor->GetPage()->SetSelected(bIsSelected); + } +} + +void SlideSorterModel::SynchronizeModelSelection() +{ + ::osl::MutexGuard aGuard (maMutex); + + PageEnumeration aAllPages (PageEnumerationProvider::CreateAllPagesEnumeration(*this)); + while (aAllPages.HasMoreElements()) + { + SharedPageDescriptor pDescriptor (aAllPages.GetNextElement()); + const bool bIsSelected (pDescriptor->GetPage()->IsSelected()); + pDescriptor->SetState(PageDescriptor::ST_Selected, bIsSelected); + } +} + +void SlideSorterModel::SetDocumentSlides ( + const Reference<container::XIndexAccess>& rxSlides) +{ + ::osl::MutexGuard aGuard (maMutex); + + // Make the current selection persistent and then release the + // current set of pages. + SynchronizeDocumentSelection(); + mxSlides = nullptr; + ClearDescriptorList (); + + // Reset the current page to cause everybody to release references to it. + mrSlideSorter.GetController().GetCurrentSlideManager()->NotifyCurrentSlideChange(-1); + + // Set the new set of pages. + mxSlides = rxSlides; + AdaptSize(); + SynchronizeModelSelection(); + mrSlideSorter.GetController().GetPageSelector().CountSelectedPages(); + + model::PageEnumeration aSelectedPages ( + model::PageEnumerationProvider::CreateSelectedPagesEnumeration(*this)); + if (aSelectedPages.HasMoreElements()) + { + SharedPageDescriptor pDescriptor (aSelectedPages.GetNextElement()); + mrSlideSorter.GetController().GetCurrentSlideManager()->NotifyCurrentSlideChange( + pDescriptor->GetPage()); + } + + ViewShell* pViewShell = mrSlideSorter.GetViewShell(); + if (pViewShell != nullptr) + { + SdPage* pPage = pViewShell->getCurrentPage(); + if (pPage != nullptr) + mrSlideSorter.GetController().GetCurrentSlideManager()->NotifyCurrentSlideChange( + pPage); + else + { + // No current page. This can only be when the slide sorter is + // the main view shell. Get current slide form frame view. + const FrameView* pFrameView = pViewShell->GetFrameView(); + if (pFrameView != nullptr) + mrSlideSorter.GetController().GetCurrentSlideManager()->NotifyCurrentSlideChange( + pFrameView->GetSelectedPage()); + else + { + // No frame view. As a last resort use the first slide as + // current slide. + mrSlideSorter.GetController().GetCurrentSlideManager()->NotifyCurrentSlideChange( + sal_Int32(0)); + } + } + } + + mrSlideSorter.GetController().GetSlotManager()->NotifyEditModeChange(); +} + +Reference<container::XIndexAccess> SlideSorterModel::GetDocumentSlides() const +{ + ::osl::MutexGuard aGuard (maMutex); + return mxSlides; +} + +void SlideSorterModel::UpdatePageList() +{ + ::osl::MutexGuard aGuard (maMutex); + + Reference<container::XIndexAccess> xPages; + + // Get the list of pages according to the edit mode. + Reference<frame::XController> xController (mrSlideSorter.GetXController()); + if (xController.is()) + { + switch (meEditMode) + { + case EditMode::MasterPage: + { + Reference<drawing::XMasterPagesSupplier> xSupplier ( + xController->getModel(), UNO_QUERY); + if (xSupplier.is()) + { + xPages = xSupplier->getMasterPages(); + } + } + break; + + case EditMode::Page: + { + Reference<drawing::XDrawPagesSupplier> xSupplier ( + xController->getModel(), UNO_QUERY); + if (xSupplier.is()) + { + xPages = xSupplier->getDrawPages(); + } + } + break; + + default: + // We should never get here. + assert(false); + break; + } + } + + mrSlideSorter.GetController().SetDocumentSlides(xPages); +} + +void SlideSorterModel::AdaptSize() +{ + if (mxSlides.is()) + maPageDescriptors.resize(mxSlides->getCount()); + else + maPageDescriptors.resize(0); +} + +bool SlideSorterModel::IsReadOnly() const +{ + if (mrSlideSorter.GetViewShellBase() != nullptr + && mrSlideSorter.GetViewShellBase()->GetDocShell()) + return mrSlideSorter.GetViewShellBase()->GetDocShell()->IsReadOnly(); + else + return true; +} + +void SlideSorterModel::SaveCurrentSelection() +{ + PageEnumeration aPages (PageEnumerationProvider::CreateAllPagesEnumeration(*this)); + while (aPages.HasMoreElements()) + { + SharedPageDescriptor pDescriptor (aPages.GetNextElement()); + pDescriptor->SetState( + PageDescriptor::ST_WasSelected, + pDescriptor->HasState(PageDescriptor::ST_Selected)); + } +} + +vcl::Region SlideSorterModel::RestoreSelection() +{ + vcl::Region aRepaintRegion; + PageEnumeration aPages (PageEnumerationProvider::CreateAllPagesEnumeration(*this)); + while (aPages.HasMoreElements()) + { + SharedPageDescriptor pDescriptor (aPages.GetNextElement()); + if (pDescriptor->SetState( + PageDescriptor::ST_Selected, + pDescriptor->HasState(PageDescriptor::ST_WasSelected))) + { + aRepaintRegion.Union(pDescriptor->GetBoundingBox()); + } + } + return aRepaintRegion; +} + +bool SlideSorterModel::NotifyPageEvent (const SdrPage* pSdrPage) +{ + ::osl::MutexGuard aGuard (maMutex); + + SdPage* pPage = const_cast<SdPage*>(dynamic_cast<const SdPage*>(pSdrPage)); + if (pPage == nullptr) + return false; + + // We are only interested in pages that are currently served by this + // model. + if (pPage->GetPageKind() != PageKind::Standard) + return false; + if (pPage->IsMasterPage() != (meEditMode==EditMode::MasterPage)) + return false; + + //NotifyPageEvent is called for add, remove, *and* change position so for + //the change position case we must ensure we don't end up with the slide + //duplicated in our list + bool bSelected = DeleteSlide(pPage); + if (pPage->IsInserted()) + { + InsertSlide(pPage, bSelected); + } + CheckModel(*this); + + return true; +} + +void SlideSorterModel::InsertSlide(SdPage* pPage, bool bMarkSelected) +{ + // Find the index at which to insert the given page. + sal_uInt16 nCoreIndex (pPage->GetPageNum()); + sal_Int32 nIndex (FromCoreIndex(nCoreIndex)); + if (pPage != GetPage(nIndex)) + return; + + // Check that the pages in the document before and after the given page + // are present in this model. + if (nIndex>0) + if (GetPage(nIndex-1) != GetPageDescriptor(nIndex-1)->GetPage()) + return; + if (nIndex < static_cast<sal_Int32>(maPageDescriptors.size()) -1) + if (GetPage(nIndex+1) != GetPageDescriptor(nIndex)->GetPage()) + return; + + auto iter = maPageDescriptors.begin() + nIndex; + + // Insert the given page at index nIndex + iter = maPageDescriptors.insert( + iter, + std::make_shared<PageDescriptor>( + Reference<drawing::XDrawPage>(mxSlides->getByIndex(nIndex),UNO_QUERY), + pPage, + nIndex)); + + if (bMarkSelected) + (*iter)->SetState(PageDescriptor::ST_Selected, true); + + // Update page indices. + UpdateIndices(nIndex+1); +} + +bool SlideSorterModel::DeleteSlide (const SdPage* pPage) +{ + sal_Int32 nIndex(0); + + // Caution, GetIndex() may be negative since it uses GetPageNumber()-1 + // for calculation, so do this only when page is inserted, else the + // GetPageNumber() will be zero and thus GetIndex() == -1 + if(pPage->IsInserted()) + { + nIndex = GetIndex(pPage); + } + else + { + // if not inserted, search for page + for(; nIndex < static_cast<sal_Int32>(maPageDescriptors.size()); nIndex++) + { + if(maPageDescriptors[nIndex]->GetPage() == pPage) + { + break; + } + } + } + + bool bMarkedSelected(false); + + if(nIndex >= 0 && o3tl::make_unsigned(nIndex) < maPageDescriptors.size()) + { + if (maPageDescriptors[nIndex]) + if (maPageDescriptors[nIndex]->GetPage() != pPage) + return false; + + auto iter = maPageDescriptors.begin() + nIndex; + bMarkedSelected = (*iter)->HasState(PageDescriptor::ST_Selected); + maPageDescriptors.erase(iter); + UpdateIndices(nIndex); + + collectUIInformation(OUString::number(nIndex + 1), "Delete_Slide_or_Page"); + } + return bMarkedSelected; +} + +void SlideSorterModel::UpdateIndices (const sal_Int32 nFirstIndex) +{ + for (sal_Int32 nDescriptorIndex=0,nCount=maPageDescriptors.size(); + nDescriptorIndex<nCount; + ++nDescriptorIndex) + { + SharedPageDescriptor& rpDescriptor (maPageDescriptors[nDescriptorIndex]); + if (rpDescriptor) + { + if (nDescriptorIndex < nFirstIndex) + { + if (rpDescriptor->GetPageIndex()!=nDescriptorIndex) + { + assert(rpDescriptor->GetPageIndex()==nDescriptorIndex); + } + } + else + { + rpDescriptor->SetPageIndex(nDescriptorIndex); + } + } + } +} + +SdPage* SlideSorterModel::GetPage (const sal_Int32 nSdIndex) const +{ + SdDrawDocument* pModel = const_cast<SlideSorterModel*>(this)->GetDocument(); + if (pModel != nullptr) + { + if (meEditMode == EditMode::Page) + return pModel->GetSdPage (static_cast<sal_uInt16>(nSdIndex), PageKind::Standard); + else + return pModel->GetMasterSdPage (static_cast<sal_uInt16>(nSdIndex), PageKind::Standard); + } + else + return nullptr; +} + +} // end of namespace ::sd::slidesorter::model + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/model/SlsPageDescriptor.cxx b/sd/source/ui/slidesorter/model/SlsPageDescriptor.cxx new file mode 100644 index 000000000..5118cf58e --- /dev/null +++ b/sd/source/ui/slidesorter/model/SlsPageDescriptor.cxx @@ -0,0 +1,226 @@ +/* -*- 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 <model/SlsPageDescriptor.hxx> + +#include <sdpage.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star; + +namespace sd::slidesorter::model { + +PageDescriptor::PageDescriptor ( + const Reference<drawing::XDrawPage>& rxPage, + SdPage* pPage, + const sal_Int32 nIndex) + : mpPage(pPage), + mxPage(rxPage), + mpMasterPage(nullptr), + mnIndex(nIndex), + maVisualState(nIndex), + mbIsSelected(false), + mbWasSelected(false), + mbIsVisible(false), + mbIsFocused(false), + mbIsCurrent(false), + mbIsMouseOver(false), + mbHasTransition(false) +{ + assert(mpPage); + assert(mpPage == SdPage::getImplementation(rxPage)); + if (mpPage != nullptr) + { + if (mpPage->TRG_HasMasterPage()) + mpMasterPage = &mpPage->TRG_GetMasterPage(); + if (mpPage->getTransitionType() > 0) + mbHasTransition = true; + } +} + +PageDescriptor::~PageDescriptor() +{ +} + +void PageDescriptor::SetPageIndex (const sal_Int32 nNewIndex) +{ + mnIndex = nNewIndex; + maVisualState.mnPageId = nNewIndex; +} + +bool PageDescriptor::UpdateMasterPage() +{ + const SdrPage* pMaster = nullptr; + if (mpPage!=nullptr && mpPage->TRG_HasMasterPage()) + pMaster = &mpPage->TRG_GetMasterPage(); + if (mpMasterPage != pMaster) + { + mpMasterPage = pMaster; + return true; + } + else + return false; +} + +bool PageDescriptor::UpdateTransitionFlag() +{ + bool bHasSlideTransition (false); + if (mpPage != nullptr) + bHasSlideTransition = mpPage->getTransitionType() > 0; + if (bHasSlideTransition != mbHasTransition) + { + mbHasTransition = bHasSlideTransition; + return true; + } + else + return false; +} + +bool PageDescriptor::HasState (const State eState) const +{ + switch (eState) + { + case ST_Visible: + return mbIsVisible; + + case ST_Selected: + return mbIsSelected; + + case ST_WasSelected: + return mbWasSelected; + + case ST_Focused: + return mbIsFocused; + + case ST_MouseOver: + return mbIsMouseOver; + + case ST_Current: + return mbIsCurrent; + + case ST_Excluded: + return mpPage!=nullptr && mpPage->IsExcluded(); + + default: + assert(false); + return false; + } +} + +bool PageDescriptor::SetState (const State eState, const bool bNewStateValue) +{ + bool bModified (false); + switch (eState) + { + case ST_Visible: + bModified = (bNewStateValue!=mbIsVisible); + if (bModified) + mbIsVisible = bNewStateValue; + break; + + case ST_Selected: + bModified = (bNewStateValue!=mbIsSelected); + if (bModified) + mbIsSelected = bNewStateValue; + break; + + case ST_WasSelected: + bModified = (bNewStateValue!=mbWasSelected); + if (bModified) + mbWasSelected = bNewStateValue; + break; + + case ST_Focused: + bModified = (bNewStateValue!=mbIsFocused); + if (bModified) + mbIsFocused = bNewStateValue; + break; + + case ST_MouseOver: + bModified = (bNewStateValue!=mbIsMouseOver); + if (bModified) + mbIsMouseOver = bNewStateValue; + break; + + case ST_Current: + bModified = (bNewStateValue!=mbIsCurrent); + if (bModified) + mbIsCurrent = bNewStateValue; + break; + + case ST_Excluded: + // This is a state of the page and has to be handled differently + // from the view-only states. + if (mpPage != nullptr) + if (bNewStateValue != mpPage->IsExcluded()) + { + mpPage->SetExcluded(bNewStateValue); + bModified = true; + } + break; + } + + return bModified; +} + +bool PageDescriptor::GetCoreSelection() +{ + if (mpPage!=nullptr && mpPage->IsSelected() != mbIsSelected) + return SetState(ST_Selected, !mbIsSelected); + else + return false; +} + +void PageDescriptor::SetCoreSelection() +{ + if (mpPage != nullptr) + if (HasState(ST_Selected)) + mpPage->SetSelected(true); + else + mpPage->SetSelected(false); + else + { + assert(mpPage!=nullptr); + } +} + +::tools::Rectangle PageDescriptor::GetBoundingBox() const +{ + ::tools::Rectangle aBox (maBoundingBox); + const Point aOffset (maVisualState.GetLocationOffset()); + aBox.Move(aOffset.X(), aOffset.Y()); + return aBox; +} + +Point PageDescriptor::GetLocation (const bool bIgnoreOffset) const +{ + if (bIgnoreOffset) + return maBoundingBox.TopLeft(); + else + return maBoundingBox.TopLeft() + maVisualState.GetLocationOffset(); +} + +void PageDescriptor::SetBoundingBox (const ::tools::Rectangle& rBoundingBox) +{ + maBoundingBox = rBoundingBox; +} + +} // end of namespace ::sd::slidesorter::model + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/model/SlsPageEnumeration.cxx b/sd/source/ui/slidesorter/model/SlsPageEnumeration.cxx new file mode 100644 index 000000000..a67f057e7 --- /dev/null +++ b/sd/source/ui/slidesorter/model/SlsPageEnumeration.cxx @@ -0,0 +1,202 @@ +/* -*- 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 <memory> +#include <sal/config.h> + +#include <utility> + +#include <model/SlsPageEnumeration.hxx> +#include <model/SlideSorterModel.hxx> + +using namespace ::sd::slidesorter; +using namespace ::sd::slidesorter::model; + +namespace { + +class PageEnumerationImpl + : public Enumeration<SharedPageDescriptor> +{ +public: + PageEnumerationImpl ( + const SlideSorterModel& rModel, + const PageEnumeration::PagePredicate& rPredicate); + PageEnumerationImpl(const PageEnumerationImpl&) = delete; + PageEnumerationImpl& operator=(const PageEnumerationImpl&) = delete; + /** Create a copy of the called enumeration object. + */ + virtual ::std::unique_ptr<Enumeration<SharedPageDescriptor> > Clone() override; + + virtual bool HasMoreElements() const override; + virtual SharedPageDescriptor GetNextElement() override; + virtual void Rewind() override; + +private: + const SlideSorterModel& mrModel; + const PageEnumeration::PagePredicate maPredicate; + int mnIndex; + + /** This constructor sets the internal page index to the given value. + It does not call AdvanceToNextValidElement() to skip elements that + do not fulfill Predicate. + */ + PageEnumerationImpl ( + const SlideSorterModel& rModel, + const PageEnumeration::PagePredicate& rPredicate, + int nIndex); + + /** Skip all elements that do not fulfill Predicate starting with the + one pointed to by mnIndex. + */ + void AdvanceToNextValidElement(); +}; + +} // end of anonymous namespace + +namespace sd::slidesorter::model { + +PageEnumeration PageEnumeration::Create ( + const SlideSorterModel& rModel, + const PagePredicate& rPredicate) +{ + return PageEnumeration(::std::unique_ptr<Enumeration<SharedPageDescriptor> >( + new PageEnumerationImpl(rModel, rPredicate))); +} + +PageEnumeration::PageEnumeration ( + ::std::unique_ptr<Enumeration<SharedPageDescriptor> > && pImpl) + : mpImpl(std::move(pImpl)) +{ +} + +PageEnumeration::PageEnumeration (const PageEnumeration& rEnumeration ) +: sd::slidesorter::model::Enumeration<sd::slidesorter::model::SharedPageDescriptor>() +{ + mpImpl = rEnumeration.mpImpl->Clone(); +} + +PageEnumeration::~PageEnumeration() +{ +} + +PageEnumeration& PageEnumeration::operator= ( + const PageEnumeration& rEnumeration) +{ + mpImpl = rEnumeration.mpImpl->Clone(); + return *this; +} + +::std::unique_ptr<Enumeration<SharedPageDescriptor> > PageEnumeration::Clone() +{ + return ::std::unique_ptr<Enumeration<SharedPageDescriptor> >( + new PageEnumeration (*this)); +} + +bool PageEnumeration::HasMoreElements() const +{ + return mpImpl->HasMoreElements(); +} + +SharedPageDescriptor PageEnumeration::GetNextElement() +{ + return mpImpl->GetNextElement(); +} + +void PageEnumeration::Rewind() +{ + return mpImpl->Rewind(); +} + +} // end of namespace ::sd::slidesorter::model + +namespace { + +PageEnumerationImpl::PageEnumerationImpl ( + const SlideSorterModel& rModel, + const PageEnumeration::PagePredicate& rPredicate) + : mrModel(rModel), + maPredicate(rPredicate), + mnIndex(0) +{ + Rewind(); +} + +PageEnumerationImpl::PageEnumerationImpl ( + const SlideSorterModel& rModel, + const PageEnumeration::PagePredicate& rPredicate, + int nIndex) + : mrModel(rModel), + maPredicate(rPredicate), + mnIndex(nIndex) +{ +} + +::std::unique_ptr<Enumeration<SharedPageDescriptor> > + PageEnumerationImpl::Clone() +{ + return ::std::unique_ptr<Enumeration<SharedPageDescriptor> >( + new PageEnumerationImpl(mrModel,maPredicate,mnIndex)); +} + +bool PageEnumerationImpl::HasMoreElements() const +{ + return (mnIndex < mrModel.GetPageCount()); +} + +SharedPageDescriptor PageEnumerationImpl::GetNextElement() +{ + SharedPageDescriptor pDescriptor (mrModel.GetPageDescriptor(mnIndex)); + + // Go to the following valid element. + mnIndex += 1; + AdvanceToNextValidElement(); + + return pDescriptor; +} + +void PageEnumerationImpl::Rewind() +{ + // Go to first valid element. + mnIndex = 0; + AdvanceToNextValidElement(); +} + +void PageEnumerationImpl::AdvanceToNextValidElement() +{ + while (mnIndex < mrModel.GetPageCount()) + { + SharedPageDescriptor pDescriptor (mrModel.GetPageDescriptor(mnIndex)); + + // Test for the predicate being fulfilled. + if (pDescriptor && maPredicate(pDescriptor)) + { + // This predicate is valid. + break; + } + else + { + // Advance to next predicate. + mnIndex += 1; + } + } +} + +} // end of anonymous namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/model/SlsPageEnumerationProvider.cxx b/sd/source/ui/slidesorter/model/SlsPageEnumerationProvider.cxx new file mode 100644 index 000000000..800fa12db --- /dev/null +++ b/sd/source/ui/slidesorter/model/SlsPageEnumerationProvider.cxx @@ -0,0 +1,81 @@ +/* -*- 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 <model/SlsPageEnumerationProvider.hxx> +#include <model/SlsPageEnumeration.hxx> +#include <model/SlsPageDescriptor.hxx> + +namespace sd::slidesorter::model { + +namespace { + +class AllPagesPredicate +{ +public: + bool operator() (const SharedPageDescriptor&) const + { + return true; + } +}; + +class SelectedPagesPredicate +{ +public: + bool operator() (const SharedPageDescriptor& rpDescriptor) + { + return rpDescriptor->HasState(PageDescriptor::ST_Selected); + } +}; + +class VisiblePagesPredicate +{ +public: + bool operator() (const SharedPageDescriptor& rpDescriptor) + { + return rpDescriptor->HasState(PageDescriptor::ST_Visible); + } +}; + +} + +PageEnumeration PageEnumerationProvider::CreateAllPagesEnumeration ( + const SlideSorterModel& rModel) +{ + return PageEnumeration::Create(rModel, AllPagesPredicate()); +} + +PageEnumeration PageEnumerationProvider::CreateSelectedPagesEnumeration ( + const SlideSorterModel& rModel) +{ + return PageEnumeration::Create( + rModel, + SelectedPagesPredicate()); +} + +PageEnumeration PageEnumerationProvider::CreateVisiblePagesEnumeration ( + const SlideSorterModel& rModel) +{ + return PageEnumeration::Create( + rModel, + VisiblePagesPredicate()); +} + +} // end of namespace ::sd::slidesorter::model + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/model/SlsVisualState.cxx b/sd/source/ui/slidesorter/model/SlsVisualState.cxx new file mode 100644 index 000000000..3e16823ff --- /dev/null +++ b/sd/source/ui/slidesorter/model/SlsVisualState.cxx @@ -0,0 +1,40 @@ +/* -*- 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 <model/SlsVisualState.hxx> + +namespace sd::slidesorter::model +{ +VisualState::VisualState(const sal_Int32 nPageId) + : mnPageId(nPageId) + , maLocationOffset(0, 0) +{ +} + +void VisualState::SetLocationOffset(const Point& rOffset) +{ + if (maLocationOffset != rOffset) + { + maLocationOffset = rOffset; + } +} + +} // end of namespace ::sd::slidesorter::model + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/shell/SlideSorter.cxx b/sd/source/ui/slidesorter/shell/SlideSorter.cxx new file mode 100644 index 000000000..550a40469 --- /dev/null +++ b/sd/source/ui/slidesorter/shell/SlideSorter.cxx @@ -0,0 +1,456 @@ +/* -*- 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 <SlideSorter.hxx> + +#include <com/sun/star/frame/XController.hpp> + +#include <controller/SlideSorterController.hxx> +#include <controller/SlsScrollBarManager.hxx> +#include <controller/SlsProperties.hxx> +#include <controller/SlsAnimator.hxx> +#include <o3tl/deleter.hxx> +#include <view/SlideSorterView.hxx> +#include <view/SlsTheme.hxx> +#include <model/SlideSorterModel.hxx> + +#include <ViewShell.hxx> +#include <ViewShellBase.hxx> +#include <Window.hxx> + +#include <tools/debug.hxx> +#include <vcl/scrbar.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star; + +namespace sd::slidesorter { + +namespace { +class ContentWindow : public ::sd::Window +{ +public: + ContentWindow(vcl::Window& rParent, SlideSorter& rSlideSorter); + + void SetCurrentFunction (const rtl::Reference<FuPoor>& rpFunction); + virtual void Paint(vcl::RenderContext& /*rRenderContext*/, const ::tools::Rectangle& rRect) override; + virtual void KeyInput (const KeyEvent& rEvent) override; + virtual void MouseMove (const MouseEvent& rEvent) override; + virtual void MouseButtonUp (const MouseEvent& rEvent) override; + virtual void MouseButtonDown (const MouseEvent& rEvent) override; + virtual void Command (const CommandEvent& rEvent) override; + virtual bool EventNotify (NotifyEvent& rEvent) override; + +private: + SlideSorter& mrSlideSorter; + rtl::Reference<FuPoor> mpCurrentFunction; +}; +} + +//===== SlideSorter =========================================================== + +std::shared_ptr<SlideSorter> SlideSorter::CreateSlideSorter( + ViewShell& rViewShell, + sd::Window* pContentWindow, + ScrollBar* pHorizontalScrollBar, + ScrollBar* pVerticalScrollBar, + ScrollBarBox* pScrollBarBox) +{ + std::shared_ptr<SlideSorter> pSlideSorter( + new SlideSorter( + rViewShell, + pContentWindow, + pHorizontalScrollBar, + pVerticalScrollBar, + pScrollBarBox), + o3tl::default_delete<SlideSorter>()); + pSlideSorter->Init(); + return pSlideSorter; +} + +std::shared_ptr<SlideSorter> SlideSorter::CreateSlideSorter ( + ViewShellBase& rBase, + vcl::Window& rParentWindow) +{ + std::shared_ptr<SlideSorter> pSlideSorter( + new SlideSorter( + rBase, + rParentWindow), + o3tl::default_delete<SlideSorter>()); + pSlideSorter->Init(); + return pSlideSorter; +} + +SlideSorter::SlideSorter ( + ViewShell& rViewShell, + sd::Window* pContentWindow, + ScrollBar* pHorizontalScrollBar, + ScrollBar* pVerticalScrollBar, + ScrollBarBox* pScrollBarBox) + : mbIsValid(false), + mpViewShell(&rViewShell), + mpViewShellBase(&rViewShell.GetViewShellBase()), + mpContentWindow(pContentWindow), + mpHorizontalScrollBar(pHorizontalScrollBar), + mpVerticalScrollBar(pVerticalScrollBar), + mpScrollBarBox(pScrollBarBox), + mpProperties(std::make_shared<controller::Properties>()), + mpTheme(std::make_shared<view::Theme>(mpProperties)) +{ +} + +SlideSorter::SlideSorter ( + ViewShellBase& rBase, + vcl::Window& rParentWindow) + : mbIsValid(false), + mpViewShell(nullptr), + mpViewShellBase(&rBase), + mpContentWindow(VclPtr<ContentWindow>::Create(rParentWindow,*this )), + mpHorizontalScrollBar(VclPtr<ScrollBar>::Create(&rParentWindow,WinBits(WB_HSCROLL | WB_DRAG))), + mpVerticalScrollBar(VclPtr<ScrollBar>::Create(&rParentWindow,WinBits(WB_VSCROLL | WB_DRAG))), + mpScrollBarBox(VclPtr<ScrollBarBox>::Create(&rParentWindow)), + mpProperties(std::make_shared<controller::Properties>()), + mpTheme(std::make_shared<view::Theme>(mpProperties)) +{ +} + +void SlideSorter::Init() +{ + if (mpViewShellBase != nullptr) + mxControllerWeak = mpViewShellBase->GetController(); + + // Reinitialize colors in Properties with window specific values. + if (mpContentWindow) + { + mpProperties->SetBackgroundColor( + mpContentWindow->GetSettings().GetStyleSettings().GetWindowColor()); + mpProperties->SetTextColor( + mpContentWindow->GetSettings().GetStyleSettings().GetWindowTextColor()); + mpProperties->SetSelectionColor( + mpContentWindow->GetSettings().GetStyleSettings().GetMenuHighlightColor()); + mpProperties->SetHighlightColor( + mpContentWindow->GetSettings().GetStyleSettings().GetMenuHighlightColor()); + } + + CreateModelViewController (); + + SetupListeners (); + + // Initialize the window. + sd::Window *pContentWindow = GetContentWindow().get(); + if (!pContentWindow) + return; + + vcl::Window* pParentWindow = pContentWindow->GetParent(); + if (pParentWindow != nullptr) + pParentWindow->SetBackground(Wallpaper()); + pContentWindow->SetBackground(Wallpaper()); + pContentWindow->SetViewOrigin (Point(0,0)); + // We do our own scrolling while dragging a page selection. + pContentWindow->SetUseDropScroll (false); + // Change the winbits so that the active window accepts the focus. + pContentWindow->SetStyle ((pContentWindow->GetStyle() & ~WB_DIALOGCONTROL) | WB_TABSTOP); + pContentWindow->Hide(); + + // Set view pointer of base class. + SetupControls(); + + mbIsValid = true; +} + +SlideSorter::~SlideSorter() +{ + mbIsValid = false; + + ReleaseListeners(); + + // Dispose model, view and controller to avoid calls between them when + // they are being destructed and one or two of them are already gone. + mpSlideSorterController->Dispose(); + mpSlideSorterView->Dispose(); + mpSlideSorterModel->Dispose(); + + // Reset the auto pointers explicitly to control the order of destruction. + mpSlideSorterController.reset(); + mpSlideSorterView.reset(); + mpSlideSorterModel.reset(); + + mpHorizontalScrollBar.reset(); + mpVerticalScrollBar.reset(); + mpScrollBarBox.reset(); +} + +model::SlideSorterModel& SlideSorter::GetModel() const +{ + assert(mpSlideSorterModel); + return *mpSlideSorterModel; +} + +view::SlideSorterView& SlideSorter::GetView() const +{ + assert(mpSlideSorterView); + return *mpSlideSorterView; +} + +controller::SlideSorterController& SlideSorter::GetController() const +{ + assert(mpSlideSorterController); + return *mpSlideSorterController; +} + +Reference<frame::XController> SlideSorter::GetXController() const +{ + Reference<frame::XController> xController(mxControllerWeak); + return xController; +} + +void SlideSorter::Paint (const ::tools::Rectangle& rRepaintArea) +{ + GetController().Paint( + rRepaintArea, + GetContentWindow()); +} + +void SlideSorter::SetupControls() +{ + GetVerticalScrollBar()->Show(); +} + +void SlideSorter::SetupListeners() +{ + sd::Window *pWindow = GetContentWindow().get(); + if (pWindow) + { + vcl::Window* pParentWindow = pWindow->GetParent(); + if (pParentWindow != nullptr) + pParentWindow->AddEventListener( + LINK( + mpSlideSorterController.get(), + controller::SlideSorterController, + WindowEventHandler)); + pWindow->AddEventListener( + LINK( + mpSlideSorterController.get(), + controller::SlideSorterController, + WindowEventHandler)); + } + Application::AddEventListener( + LINK( + mpSlideSorterController.get(), + controller::SlideSorterController, + ApplicationEventHandler)); + + mpSlideSorterController->GetScrollBarManager().Connect(); +} + +void SlideSorter::ReleaseListeners() +{ + mpSlideSorterController->GetScrollBarManager().Disconnect(); + + sd::Window *pWindow (GetContentWindow().get()); + if (pWindow) + { + pWindow->RemoveEventListener( + LINK(mpSlideSorterController.get(), + controller::SlideSorterController, + WindowEventHandler)); + + vcl::Window* pParentWindow = pWindow->GetParent(); + if (pParentWindow != nullptr) + pParentWindow->RemoveEventListener( + LINK(mpSlideSorterController.get(), + controller::SlideSorterController, + WindowEventHandler)); + } + Application::RemoveEventListener( + LINK(mpSlideSorterController.get(), + controller::SlideSorterController, + ApplicationEventHandler)); +} + +void SlideSorter::CreateModelViewController() +{ + mpSlideSorterModel.reset(CreateModel()); + DBG_ASSERT(mpSlideSorterModel != nullptr, "Can not create model for slide browser"); + + mpSlideSorterView.reset(new view::SlideSorterView (*this)); + mpSlideSorterController.reset(new controller::SlideSorterController(*this)); + + // Now that model, view, and controller are constructed, do the + // initialization that relies on all three being in place. + mpSlideSorterController->Init(); + mpSlideSorterView->Init(); +} + +model::SlideSorterModel* SlideSorter::CreateModel() +{ + // Get pointers to the document. + ViewShellBase* pViewShellBase = GetViewShellBase(); + if (pViewShellBase != nullptr) + { + assert (pViewShellBase->GetDocument() != nullptr); + + return new model::SlideSorterModel(*this); + } + else + return nullptr; +} + +void SlideSorter::ArrangeGUIElements ( + const Point& rOffset, + const Size& rSize) +{ + Point aOrigin (rOffset); + + if (rSize.Width()>0 + && rSize.Height()>0 + && GetContentWindow() + && GetContentWindow()->IsVisible()) + { + // Prevent untimely redraws while the view is not yet correctly + // resized. + view::SlideSorterView::DrawLock aLock (*this); + GetContentWindow()->EnablePaint (false); + + mpSlideSorterController->Resize (::tools::Rectangle(aOrigin, rSize)); + + GetContentWindow()->EnablePaint (true); + } +} + +void SlideSorter::RelocateToWindow (vcl::Window* pParentWindow) +{ + // Stop all animations for they have been started for the old window. + mpSlideSorterController->GetAnimator()->RemoveAllAnimations(); + + ReleaseListeners(); + + if (mpViewShell) + { + mpViewShell->ViewShell::RelocateToParentWindow(pParentWindow); + } + + SetupControls(); + SetupListeners(); + + // For accessibility we have to shortly hide the content window. This + // triggers the construction of a new accessibility object for the new + // view shell. (One is created earlier while the constructor of the base + // class is executed. But because at that time the correct + // accessibility object can not be constructed we do that now.) + if (mpContentWindow) + { + mpContentWindow->Hide(); + mpContentWindow->Show(); + } +} + +void SlideSorter::SetCurrentFunction (const rtl::Reference<FuPoor>& rpFunction) +{ + if (GetViewShell() != nullptr) + { + GetViewShell()->SetCurrentFunction(rpFunction); + GetViewShell()->SetOldFunction(rpFunction); + } + else + { + ContentWindow* pWindow = dynamic_cast<ContentWindow*>(GetContentWindow().get()); + if (pWindow != nullptr) + pWindow->SetCurrentFunction(rpFunction); + } +} + +std::shared_ptr<controller::Properties> const & SlideSorter::GetProperties() const +{ + assert(mpProperties); + return mpProperties; +} + +std::shared_ptr<view::Theme> const & SlideSorter::GetTheme() const +{ + assert(mpTheme); + return mpTheme; +} + +//===== ContentWindow ========================================================= + +namespace { + +ContentWindow::ContentWindow( + vcl::Window& rParent, + SlideSorter& rSlideSorter) + : ::sd::Window(&rParent), + mrSlideSorter(rSlideSorter) +{ + SetDialogControlFlags(GetDialogControlFlags() & ~DialogControlFlags::WantFocus); + SetStyle(GetStyle() | WB_NOPOINTERFOCUS); +} + +void ContentWindow::SetCurrentFunction (const rtl::Reference<FuPoor>& rpFunction) +{ + mpCurrentFunction = rpFunction; +} + +void ContentWindow::Paint (vcl::RenderContext& /*rRenderContext*/, const ::tools::Rectangle& rRect) +{ + mrSlideSorter.Paint(rRect); +} + +void ContentWindow::KeyInput (const KeyEvent& rEvent) +{ + if (mpCurrentFunction.is()) + mpCurrentFunction->KeyInput(rEvent); +} + +void ContentWindow::MouseMove (const MouseEvent& rEvent) +{ + if (mpCurrentFunction.is()) + mpCurrentFunction->MouseMove(rEvent); +} + +void ContentWindow::MouseButtonUp(const MouseEvent& rEvent) +{ + if (mpCurrentFunction.is()) + mpCurrentFunction->MouseButtonUp(rEvent); +} + +void ContentWindow::MouseButtonDown(const MouseEvent& rEvent) +{ + if (mpCurrentFunction.is()) + mpCurrentFunction->MouseButtonDown(rEvent); +} + +void ContentWindow::Command(const CommandEvent& rEvent) +{ + if (mpCurrentFunction.is()) + mpCurrentFunction->Command(rEvent); +} + +bool ContentWindow::EventNotify(NotifyEvent&) +{ + return false; +} + +} // end of anonymous namespace + +} // end of namespace ::sd::slidesorter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/shell/SlideSorterService.cxx b/sd/source/ui/slidesorter/shell/SlideSorterService.cxx new file mode 100644 index 000000000..a086f3b9e --- /dev/null +++ b/sd/source/ui/slidesorter/shell/SlideSorterService.cxx @@ -0,0 +1,412 @@ +/* -*- 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 "SlideSorterService.hxx" +#include <SlideSorter.hxx> +#include <controller/SlideSorterController.hxx> +#include <controller/SlsProperties.hxx> +#include <controller/SlsCurrentSlideManager.hxx> +#include <model/SlideSorterModel.hxx> +#include <model/SlsPageDescriptor.hxx> +#include <view/SlideSorterView.hxx> +#include <view/SlsLayouter.hxx> +#include <DrawController.hxx> + +#include <comphelper/servicehelper.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <com/sun/star/lang/XUnoTunnel.hpp> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; +using ::sd::slidesorter::view::Layouter; + +namespace sd::slidesorter { + +//===== SlideSorterService ========================================================== + +SlideSorterService::SlideSorterService() +{ +} + +SlideSorterService::~SlideSorterService() +{ +} + +void SlideSorterService::disposing(std::unique_lock<std::mutex>&) +{ + mpSlideSorter.reset(); + + if (mxParentWindow.is()) + { + mxParentWindow->removeWindowListener(this); + } +} + +//----- XInitialization ------------------------------------------------------- + +void SAL_CALL SlideSorterService::initialize (const Sequence<Any>& rArguments) +{ + ThrowIfDisposed(); + + if (rArguments.getLength() != 3) + { + throw RuntimeException("SlideSorterService: invalid number of arguments", + static_cast<drawing::XDrawView*>(this)); + } + + mxViewId.set(rArguments[0], UNO_QUERY_THROW); + + // Get the XController. + Reference<frame::XController> xController (rArguments[1], UNO_QUERY_THROW); + + // Tunnel through the controller to obtain a ViewShellBase. + ViewShellBase* pBase = nullptr; + Reference<lang::XUnoTunnel> xTunnel (xController, UNO_QUERY_THROW); + ::sd::DrawController* pController = comphelper::getFromUnoTunnel<sd::DrawController>(xTunnel); + if (pController != nullptr) + pBase = pController->GetViewShellBase(); + + // Get the parent window. + mxParentWindow.set(rArguments[2], UNO_QUERY_THROW); + VclPtr<vcl::Window> pParentWindow = VCLUnoHelper::GetWindow(mxParentWindow); + + mxParentWindow->addWindowListener(this); + + if (pBase != nullptr && pParentWindow) + mpSlideSorter = SlideSorter::CreateSlideSorter( + *pBase, + *pParentWindow); + + Resize(); +} + +//----- XView ----------------------------------------------------------------- + +Reference<XResourceId> SAL_CALL SlideSorterService::getResourceId() +{ + return mxViewId; +} + +sal_Bool SAL_CALL SlideSorterService::isAnchorOnly() +{ + return false; +} + +//----- XWindowListener ------------------------------------------------------- + +void SAL_CALL SlideSorterService::windowResized (const awt::WindowEvent&) +{ + ThrowIfDisposed(); + + Resize(); +} + +void SAL_CALL SlideSorterService::windowMoved (const awt::WindowEvent&) {} + +void SAL_CALL SlideSorterService::windowShown (const lang::EventObject&) +{ + ThrowIfDisposed(); + Resize(); +} + +void SAL_CALL SlideSorterService::windowHidden (const lang::EventObject&) +{ + ThrowIfDisposed(); +} + +//----- lang::XEventListener -------------------------------------------------- + +void SAL_CALL SlideSorterService::disposing (const lang::EventObject& rEvent) +{ + if (rEvent.Source == mxParentWindow) + mxParentWindow = nullptr; +} + +//----- XDrawView ------------------------------------------------------------- + +void SAL_CALL SlideSorterService::setCurrentPage(const Reference<drawing::XDrawPage>& rxSlide) +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr) + mpSlideSorter->GetController().GetCurrentSlideManager()->NotifyCurrentSlideChange( + mpSlideSorter->GetModel().GetIndex(rxSlide)); +} + +Reference<drawing::XDrawPage> SAL_CALL SlideSorterService::getCurrentPage() +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr) + return mpSlideSorter->GetController().GetCurrentSlideManager()->GetCurrentSlide()->GetXDrawPage(); + else + return nullptr; +} + +//----- attributes ------------------------------------------------------------ + +Reference<container::XIndexAccess> SAL_CALL SlideSorterService::getDocumentSlides() +{ + return mpSlideSorter->GetModel().GetDocumentSlides(); +} + +void SAL_CALL SlideSorterService::setDocumentSlides ( + const Reference<container::XIndexAccess >& rxSlides) +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr && mpSlideSorter->IsValid()) + mpSlideSorter->GetController().SetDocumentSlides(rxSlides); +} + +sal_Bool SAL_CALL SlideSorterService::getIsHighlightCurrentSlide() +{ + ThrowIfDisposed(); + if (mpSlideSorter == nullptr || !mpSlideSorter->IsValid()) + return false; + else + return mpSlideSorter->GetProperties()->IsHighlightCurrentSlide(); +} + +void SAL_CALL SlideSorterService::setIsHighlightCurrentSlide (sal_Bool bValue) +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr && mpSlideSorter->IsValid()) + { + mpSlideSorter->GetProperties()->SetHighlightCurrentSlide(bValue); + controller::SlideSorterController::ModelChangeLock aLock (mpSlideSorter->GetController()); + mpSlideSorter->GetController().HandleModelChange(); + } +} + +sal_Bool SAL_CALL SlideSorterService::getIsShowSelection() +{ + ThrowIfDisposed(); + if (mpSlideSorter == nullptr || !mpSlideSorter->IsValid()) + return false; + else + return mpSlideSorter->GetProperties()->IsShowSelection(); +} + +void SAL_CALL SlideSorterService::setIsShowSelection (sal_Bool bValue) +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr && mpSlideSorter->IsValid()) + mpSlideSorter->GetProperties()->SetShowSelection(bValue); +} + +sal_Bool SAL_CALL SlideSorterService::getIsShowFocus() +{ + ThrowIfDisposed(); + if (mpSlideSorter == nullptr || !mpSlideSorter->IsValid()) + return false; + else + return mpSlideSorter->GetProperties()->IsShowFocus(); +} + +void SAL_CALL SlideSorterService::setIsShowFocus (sal_Bool bValue) +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr && mpSlideSorter->IsValid()) + mpSlideSorter->GetProperties()->SetShowFocus(bValue); +} + +sal_Bool SAL_CALL SlideSorterService::getIsCenterSelection() +{ + ThrowIfDisposed(); + if (mpSlideSorter == nullptr || !mpSlideSorter->IsValid()) + return false; + else + return mpSlideSorter->GetProperties()->IsCenterSelection(); +} + +void SAL_CALL SlideSorterService::setIsCenterSelection (sal_Bool bValue) +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr && mpSlideSorter->IsValid()) + mpSlideSorter->GetProperties()->SetCenterSelection(bValue); +} + +sal_Bool SAL_CALL SlideSorterService::getIsSuspendPreviewUpdatesDuringFullScreenPresentation() +{ + ThrowIfDisposed(); + if (mpSlideSorter == nullptr || !mpSlideSorter->IsValid()) + return true; + else + return mpSlideSorter->GetProperties() + ->IsSuspendPreviewUpdatesDuringFullScreenPresentation(); +} + +void SAL_CALL SlideSorterService::setIsSuspendPreviewUpdatesDuringFullScreenPresentation ( + sal_Bool bValue) +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr && mpSlideSorter->IsValid()) + mpSlideSorter->GetProperties() + ->SetSuspendPreviewUpdatesDuringFullScreenPresentation(bValue); +} + +sal_Bool SAL_CALL SlideSorterService::getIsOrientationVertical() +{ + ThrowIfDisposed(); + if (mpSlideSorter == nullptr || !mpSlideSorter->IsValid()) + return true; + else + return mpSlideSorter->GetView().GetOrientation() != Layouter::HORIZONTAL; +} + +void SAL_CALL SlideSorterService::setIsOrientationVertical (sal_Bool bValue) +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr && mpSlideSorter->IsValid()) + mpSlideSorter->GetView().SetOrientation(bValue + ? Layouter::GRID + : Layouter::HORIZONTAL); +} + +sal_Bool SAL_CALL SlideSorterService::getIsSmoothScrolling() +{ + ThrowIfDisposed(); + if (mpSlideSorter == nullptr || !mpSlideSorter->IsValid()) + return false; + else + return mpSlideSorter->GetProperties()->IsSmoothSelectionScrolling(); +} + +void SAL_CALL SlideSorterService::setIsSmoothScrolling (sal_Bool bValue) +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr && mpSlideSorter->IsValid()) + mpSlideSorter->GetProperties()->SetSmoothSelectionScrolling(bValue); +} + +sal_Int32 SAL_CALL SlideSorterService::getBackgroundColor() +{ + ThrowIfDisposed(); + if (mpSlideSorter == nullptr || !mpSlideSorter->IsValid()) + return util::Color(); + else + return util::Color( + mpSlideSorter->GetProperties()->GetBackgroundColor()); +} + +void SAL_CALL SlideSorterService::setBackgroundColor (sal_Int32 aBackgroundColor) +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr && mpSlideSorter->IsValid()) + mpSlideSorter->GetProperties()->SetBackgroundColor(Color(ColorTransparency, aBackgroundColor)); +} + +sal_Int32 SAL_CALL SlideSorterService::getTextColor() +{ + ThrowIfDisposed(); + if (mpSlideSorter == nullptr || !mpSlideSorter->IsValid()) + return util::Color(); + else + return util::Color( + mpSlideSorter->GetProperties()->GetTextColor()); +} + +void SAL_CALL SlideSorterService::setTextColor (sal_Int32 aTextColor) +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr && mpSlideSorter->IsValid()) + mpSlideSorter->GetProperties()->SetTextColor(Color(ColorTransparency, aTextColor)); +} + +sal_Int32 SAL_CALL SlideSorterService::getSelectionColor() +{ + ThrowIfDisposed(); + if (mpSlideSorter == nullptr || !mpSlideSorter->IsValid()) + return util::Color(); + else + return util::Color( + mpSlideSorter->GetProperties()->GetSelectionColor()); +} + +void SAL_CALL SlideSorterService::setSelectionColor (sal_Int32 aSelectionColor) +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr && mpSlideSorter->IsValid()) + mpSlideSorter->GetProperties()->SetSelectionColor(Color(ColorTransparency, aSelectionColor)); +} + +sal_Int32 SAL_CALL SlideSorterService::getHighlightColor() +{ + ThrowIfDisposed(); + if (mpSlideSorter == nullptr || !mpSlideSorter->IsValid()) + return util::Color(); + else + return util::Color( + mpSlideSorter->GetProperties()->GetHighlightColor()); +} + +void SAL_CALL SlideSorterService::setHighlightColor (sal_Int32 aHighlightColor) +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr && mpSlideSorter->IsValid()) + mpSlideSorter->GetProperties()->SetHighlightColor(Color(ColorTransparency, aHighlightColor)); +} + +sal_Bool SAL_CALL SlideSorterService::getIsUIReadOnly() +{ + ThrowIfDisposed(); + if (mpSlideSorter == nullptr || !mpSlideSorter->IsValid()) + return true; + else + return mpSlideSorter->GetProperties()->IsUIReadOnly(); +} + +void SAL_CALL SlideSorterService::setIsUIReadOnly (sal_Bool bIsUIReadOnly) +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr && mpSlideSorter->IsValid()) + mpSlideSorter->GetProperties()->SetUIReadOnly(bIsUIReadOnly); +} + +void SlideSorterService::Resize() +{ + if (mxParentWindow.is()) + { + awt::Rectangle aWindowBox = mxParentWindow->getPosSize(); + mpSlideSorter->ArrangeGUIElements( + Point(0,0), + Size(aWindowBox.Width, aWindowBox.Height)); + } +} + +void SlideSorterService::ThrowIfDisposed() +{ + if (SlideSorterServiceInterfaceBase::m_bDisposed) + { + throw lang::DisposedException ("SlideSorterService object has already been disposed", + static_cast<drawing::XDrawView*>(this)); + } +} + +} // end of namespace ::sd::slidesorter + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_SlideSorter_get_implementation(css::uno::XComponentContext* /*context*/, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new sd::slidesorter::SlideSorterService); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/shell/SlideSorterService.hxx b/sd/source/ui/slidesorter/shell/SlideSorterService.hxx new file mode 100644 index 000000000..579a5bae5 --- /dev/null +++ b/sd/source/ui/slidesorter/shell/SlideSorterService.hxx @@ -0,0 +1,153 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/awt/XWindowListener.hpp> +#include <com/sun/star/drawing/XSlideSorterBase.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <comphelper/compbase.hxx> +#include <memory> + +namespace com::sun::star::awt { class XWindow; } + +namespace sd::slidesorter { + +typedef comphelper::WeakComponentImplHelper < + css::drawing::XSlideSorterBase, + css::lang::XInitialization, + css::awt::XWindowListener +> SlideSorterServiceInterfaceBase; + +class SlideSorter; + +/** Implementation of the com.sun.star.drawing.SlideSorter service. +*/ +class SlideSorterService + : public SlideSorterServiceInterfaceBase +{ +public: + explicit SlideSorterService(); + virtual ~SlideSorterService() override; + SlideSorterService(const SlideSorterService&) = delete; + SlideSorterService& operator=(const SlideSorterService&) = delete; + virtual void disposing(std::unique_lock<std::mutex>&) override; + + // XInitialization + + virtual void SAL_CALL initialize (const css::uno::Sequence<css::uno::Any>& rArguments) override; + + // XResourceId + + css::uno::Reference<css::drawing::framework::XResourceId> SAL_CALL getResourceId() override; + + sal_Bool SAL_CALL isAnchorOnly() override; + + // XWindowListener + + virtual void SAL_CALL windowResized (const css::awt::WindowEvent& rEvent) override; + + virtual void SAL_CALL windowMoved (const css::awt::WindowEvent& rEvent) override; + + virtual void SAL_CALL windowShown (const css::lang::EventObject& rEvent) override; + + virtual void SAL_CALL windowHidden (const css::lang::EventObject& rEvent) override; + + // lang::XEventListener + virtual void SAL_CALL disposing (const css::lang::EventObject& rEvent) override; + + // XDrawView + + virtual void SAL_CALL setCurrentPage( + const css::uno::Reference<css::drawing::XDrawPage>& rxSlide) override; + + virtual css::uno::Reference<css::drawing::XDrawPage> SAL_CALL getCurrentPage() override; + + // Attributes + + virtual css::uno::Reference<css::container::XIndexAccess> SAL_CALL getDocumentSlides() override; + + virtual void SAL_CALL setDocumentSlides ( + const css::uno::Reference<css::container::XIndexAccess >& rxSlides) override; + + virtual sal_Bool SAL_CALL getIsHighlightCurrentSlide() override; + + virtual void SAL_CALL setIsHighlightCurrentSlide (sal_Bool bIsHighlightCurrentSlide) override; + + virtual sal_Bool SAL_CALL getIsShowSelection() override; + + virtual void SAL_CALL setIsShowSelection (sal_Bool bIsShowSelection) override; + + virtual sal_Bool SAL_CALL getIsCenterSelection() override; + + virtual void SAL_CALL setIsCenterSelection (sal_Bool bIsCenterSelection) override; + + virtual sal_Bool SAL_CALL getIsSuspendPreviewUpdatesDuringFullScreenPresentation() override; + + virtual void SAL_CALL setIsSuspendPreviewUpdatesDuringFullScreenPresentation ( + sal_Bool bIsSuspendPreviewUpdatesDuringFullScreenPresentation) override; + + virtual sal_Bool SAL_CALL getIsOrientationVertical() override; + + virtual void SAL_CALL setIsOrientationVertical (sal_Bool bIsOrientationVertical) override; + + virtual sal_Bool SAL_CALL getIsSmoothScrolling() override; + + virtual void SAL_CALL setIsSmoothScrolling (sal_Bool bIsOrientationVertical) override; + + virtual sal_Int32 SAL_CALL getBackgroundColor() override; + + virtual void SAL_CALL setBackgroundColor (sal_Int32 aBackgroundColor) override; + + virtual sal_Int32 SAL_CALL getTextColor() override; + + virtual void SAL_CALL setTextColor (sal_Int32 aTextColor) override; + + virtual sal_Int32 SAL_CALL getSelectionColor() override; + + virtual void SAL_CALL setSelectionColor (sal_Int32 aSelectionColor) override; + + virtual sal_Int32 SAL_CALL getHighlightColor() override; + + virtual void SAL_CALL setHighlightColor (sal_Int32 aHighlightColor) override; + + virtual sal_Bool SAL_CALL getIsUIReadOnly() override; + + virtual void SAL_CALL setIsUIReadOnly (sal_Bool bIsUIReadOnly) override; + + virtual sal_Bool SAL_CALL getIsShowFocus() override; + + virtual void SAL_CALL setIsShowFocus (sal_Bool bIsShowFocus) override; + +private: + std::shared_ptr<SlideSorter> mpSlideSorter; + css::uno::Reference<css::drawing::framework::XResourceId> mxViewId; + css::uno::Reference<css::awt::XWindow> mxParentWindow; + + void Resize(); + + /** @throws css::lang::DisposedException when the object has already been + disposed. + */ + void ThrowIfDisposed(); +}; + +} // end of namespace ::sd::slidesorter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/shell/SlideSorterViewShell.cxx b/sd/source/ui/slidesorter/shell/SlideSorterViewShell.cxx new file mode 100644 index 000000000..af5bd5791 --- /dev/null +++ b/sd/source/ui/slidesorter/shell/SlideSorterViewShell.cxx @@ -0,0 +1,924 @@ +#/* -*- 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 <SlideSorterViewShell.hxx> +#include <ViewShellImplementation.hxx> + +#include <SlideSorter.hxx> +#include <controller/SlideSorterController.hxx> +#include <controller/SlsClipboard.hxx> +#include <controller/SlsScrollBarManager.hxx> +#include <controller/SlsPageSelector.hxx> +#include <controller/SlsSlotManager.hxx> +#include <controller/SlsCurrentSlideManager.hxx> +#include <controller/SlsSelectionManager.hxx> +#include <view/SlideSorterView.hxx> +#include <view/SlsLayouter.hxx> +#include <model/SlideSorterModel.hxx> +#include <model/SlsPageDescriptor.hxx> +#include <framework/FrameworkHelper.hxx> +#include <ViewShellBase.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <app.hrc> +#include <AccessibleSlideSorterView.hxx> +#include <DrawDocShell.hxx> +#include <DrawViewShell.hxx> +#include <FrameView.hxx> +#include <SdUnoSlideView.hxx> +#include <ViewShellManager.hxx> +#include <Window.hxx> +#include <drawview.hxx> +#include <sfx2/msg.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/request.hxx> +#include <sfx2/sidebar/SidebarChildWindow.hxx> +#include <sfx2/devtools/DevelopmentToolChildWindow.hxx> +#include <svx/svxids.hrc> +#include <vcl/EnumContext.hxx> +#include <svx/sidebar/ContextChangeEventMultiplexer.hxx> +#include <tools/diagnose_ex.h> +#include <sfx2/sidebar/SidebarController.hxx> + +using namespace ::sd::slidesorter; +#define ShellClass_SlideSorterViewShell +#include <sdslots.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +using ::sd::framework::FrameworkHelper; +using ::vcl::EnumContext; +using namespace sfx2::sidebar; + +namespace sd::slidesorter { + +namespace { + +bool inChartContext(const sd::View* pView) +{ + if (!pView) + return false; + + SfxViewShell* pViewShell = pView->GetSfxViewShell(); + SidebarController* pSidebar = SidebarController::GetSidebarControllerForView(pViewShell); + if (pSidebar) + return pSidebar->hasChartContextCurrently(); + + return false; +} + +} // anonymous namespace + + +SFX_IMPL_INTERFACE(SlideSorterViewShell, SfxShell) + +void SlideSorterViewShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterChildWindow(::sfx2::sidebar::SidebarChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(DevelopmentToolChildWindow::GetChildWindowId()); +} + + +std::shared_ptr<SlideSorterViewShell> SlideSorterViewShell::Create ( + SfxViewFrame* pFrame, + ViewShellBase& rViewShellBase, + vcl::Window* pParentWindow, + FrameView* pFrameViewArgument) +{ + std::shared_ptr<SlideSorterViewShell> pViewShell; + try + { + pViewShell.reset( + new SlideSorterViewShell(pFrame,rViewShellBase,pParentWindow,pFrameViewArgument)); + pViewShell->Initialize(); + if (pViewShell->mpSlideSorter == nullptr) + pViewShell.reset(); + } + catch(Exception&) + { + pViewShell.reset(); + } + return pViewShell; +} + +SlideSorterViewShell::SlideSorterViewShell ( + SfxViewFrame* /*pFrame*/, + ViewShellBase& rViewShellBase, + vcl::Window* pParentWindow, + FrameView* pFrameViewArgument) + : ViewShell (pParentWindow, rViewShellBase), + mbIsArrangeGUIElementsPending(true) +{ + GetContentWindow()->set_id("slidesorter"); + meShellType = ST_SLIDE_SORTER; + + if (pFrameViewArgument != nullptr) + mpFrameView = pFrameViewArgument; + else + mpFrameView = new FrameView(GetDoc()); + GetFrameView()->Connect(); + + SetName ("SlideSorterViewShell"); + + pParentWindow->SetStyle(pParentWindow->GetStyle() | WB_DIALOGCONTROL); +} + +SlideSorterViewShell::~SlideSorterViewShell() +{ + DisposeFunctions(); + + try + { + ::sd::Window* pWindow = GetActiveWindow(); + if (pWindow!=nullptr) + { + css::uno::Reference<css::lang::XComponent> xComponent ( + pWindow->GetAccessible(false), + css::uno::UNO_QUERY); + if (xComponent.is()) + xComponent->dispose(); + } + } + catch( css::uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideSorterViewShell::~SlideSorterViewShell()" ); + } + GetFrameView()->Disconnect(); +} + +void SlideSorterViewShell::Initialize() +{ + mpSlideSorter = SlideSorter::CreateSlideSorter( + *this, + mpContentWindow, + mpHorizontalScrollBar, + mpVerticalScrollBar, + mpScrollBarBox); + mpView = &mpSlideSorter->GetView(); + + doShow(); + + SetPool( &GetDoc()->GetPool() ); + SetUndoManager( GetDoc()->GetDocSh()->GetUndoManager() ); + + // For accessibility we have to shortly hide the content window. + // This triggers the construction of a new accessibility object for + // the new view shell. (One is created earlier while the constructor + // of the base class is executed. At that time the correct + // accessibility object can not be constructed.) + sd::Window *pWindow (mpSlideSorter->GetContentWindow().get()); + if (pWindow) + { + pWindow->Hide(); + pWindow->Show(); + } +} + +void SlideSorterViewShell::Init (bool bIsMainViewShell) +{ + ViewShell::Init(bIsMainViewShell); + + // since the updatePageList will show focus, the window.show() must be called ahead. This show is deferred from Init() + ::sd::Window* pActiveWindow = GetActiveWindow(); + if (pActiveWindow) + pActiveWindow->Show(); + mpSlideSorter->GetModel().UpdatePageList(); + + if (mpContentWindow) + mpContentWindow->SetViewShell(this); +} + +SlideSorterViewShell* SlideSorterViewShell::GetSlideSorter (ViewShellBase& rBase) +{ + SlideSorterViewShell* pViewShell = nullptr; + + // Test the center and left pane for showing a slide sorter. + OUString aPaneURLs[] = { + FrameworkHelper::msCenterPaneURL, + FrameworkHelper::msFullScreenPaneURL, + FrameworkHelper::msLeftImpressPaneURL, + FrameworkHelper::msLeftDrawPaneURL, + OUString()}; + + try + { + std::shared_ptr<FrameworkHelper> pFrameworkHelper (FrameworkHelper::Instance(rBase)); + if (pFrameworkHelper->IsValid()) + for (int i=0; pViewShell==nullptr && !aPaneURLs[i].isEmpty(); ++i) + { + pViewShell = dynamic_cast<SlideSorterViewShell*>( + pFrameworkHelper->GetViewShell(aPaneURLs[i]).get()); + } + } + catch (RuntimeException&) + {} + + return pViewShell; +} + +Reference<drawing::XDrawSubController> SlideSorterViewShell::CreateSubController() +{ + Reference<drawing::XDrawSubController> xSubController; + + if (IsMainViewShell()) + { + // Create uno controller for the main view shell. + xSubController.set( new SdUnoSlideView( *mpSlideSorter)); + } + + return xSubController; +} + +/** If there is a valid controller then create a new instance of + <type>AccessibleSlideSorterView</type>. Otherwise delegate this call + to the base class to return a default object (probably an empty + reference). +*/ +css::uno::Reference<css::accessibility::XAccessible> + SlideSorterViewShell::CreateAccessibleDocumentView (::sd::Window* pWindow) +{ + // When the view is not set then the initialization is not yet complete + // and we can not yet provide an accessibility object. + if (mpView == nullptr || mpSlideSorter == nullptr) + return nullptr; + + assert(mpSlideSorter); + + rtl::Reference<::accessibility::AccessibleSlideSorterView> pAccessibleView = + new ::accessibility::AccessibleSlideSorterView( + *mpSlideSorter, + pWindow); + + pAccessibleView->Init(); + + return pAccessibleView; +} + +void SlideSorterViewShell::SwitchViewFireFocus(const css::uno::Reference< css::accessibility::XAccessible >& xAcc ) +{ + if (xAcc) + { + ::accessibility::AccessibleSlideSorterView* pBase = static_cast< ::accessibility::AccessibleSlideSorterView* >(xAcc.get()); + if (pBase) + { + pBase->SwitchViewActivated(); + } + } +} + +SlideSorter& SlideSorterViewShell::GetSlideSorter() const +{ + assert(mpSlideSorter); + return *mpSlideSorter; +} + +bool SlideSorterViewShell::RelocateToParentWindow (vcl::Window* pParentWindow) +{ + assert(mpSlideSorter); + if ( ! mpSlideSorter) + return false; + + mpSlideSorter->RelocateToWindow(pParentWindow); + ReadFrameViewData(mpFrameView); + + return true; +} + +SfxUndoManager* SlideSorterViewShell::ImpGetUndoManager() const +{ + SfxShell* pObjectBar = GetViewShellBase().GetViewShellManager()->GetTopShell(); + if (pObjectBar != nullptr) + { + // When it exists then return the undo manager of the currently + // active object bar. The object bar is missing when the + // SlideSorterViewShell is not the main view shell. + return pObjectBar->GetUndoManager(); + } + else + { + // Return the undo manager of this shell when there is no object or + // tool bar. + return const_cast<SlideSorterViewShell*>(this)->GetUndoManager(); + } +} + +SdPage* SlideSorterViewShell::getCurrentPage() const +{ + // since SlideSorterViewShell::GetActualPage() currently also + // returns master pages, which is a wrong behaviour for GetActualPage(), + // we can just use that for now + return const_cast<SlideSorterViewShell*>(this)->GetActualPage(); +} + +SdPage* SlideSorterViewShell::GetActualPage() +{ + SdPage* pCurrentPage = nullptr; + + // 1. Try to get the current page from the view shell in the center pane + // (if we are that not ourself). + if ( ! IsMainViewShell()) + { + std::shared_ptr<ViewShell> pMainViewShell = GetViewShellBase().GetMainViewShell(); + if (pMainViewShell != nullptr) + pCurrentPage = pMainViewShell->GetActualPage(); + } + + if (pCurrentPage == nullptr) + { + model::SharedPageDescriptor pDescriptor ( + mpSlideSorter->GetController().GetCurrentSlideManager()->GetCurrentSlide()); + if (pDescriptor) + pCurrentPage = pDescriptor->GetPage(); + } + + return pCurrentPage; +} + +void SlideSorterViewShell::GetMenuState ( SfxItemSet& rSet) +{ + ViewShell::GetMenuState(rSet); + assert(mpSlideSorter); + mpSlideSorter->GetController().GetSlotManager()->GetMenuState(rSet); +} + +void SlideSorterViewShell::GetClipboardState ( SfxItemSet& rSet) +{ + ViewShell::GetMenuState(rSet); + assert(mpSlideSorter); + mpSlideSorter->GetController().GetSlotManager()->GetClipboardState(rSet); +} + +void SlideSorterViewShell::ExecCtrl (SfxRequest& rRequest) +{ + assert(mpSlideSorter); + mpSlideSorter->GetController().ExecCtrl(rRequest); +} + +void SlideSorterViewShell::GetCtrlState (SfxItemSet& rSet) +{ + assert(mpSlideSorter); + mpSlideSorter->GetController().GetCtrlState(rSet); +} + +void SlideSorterViewShell::FuSupport (SfxRequest& rRequest) +{ + assert(mpSlideSorter); + mpSlideSorter->GetController().FuSupport(rRequest); +} + +/** We have to handle those slot calls here that need to have access to + private or protected members and methods of this class. +*/ +void SlideSorterViewShell::FuTemporary (SfxRequest& rRequest) +{ + assert(mpSlideSorter); + switch (rRequest.GetSlot()) + { + case SID_MODIFYPAGE: + { + SdPage* pCurrentPage = GetActualPage(); + if (pCurrentPage != nullptr) + mpImpl->ProcessModifyPageSlot ( + rRequest, + pCurrentPage, + PageKind::Standard); + Cancel(); + rRequest.Done (); + } + break; + + default: + mpSlideSorter->GetController().FuTemporary(rRequest); + break; + } +} + +void SlideSorterViewShell::GetStatusBarState (SfxItemSet& rSet) +{ + assert(mpSlideSorter); + mpSlideSorter->GetController().GetStatusBarState(rSet); +} + +void SlideSorterViewShell::FuPermanent (SfxRequest& rRequest) +{ + assert(mpSlideSorter); + mpSlideSorter->GetController().FuPermanent(rRequest); +} + +void SlideSorterViewShell::GetAttrState (SfxItemSet& rSet) +{ + assert(mpSlideSorter); + mpSlideSorter->GetController().GetAttrState(rSet); +} + +void SlideSorterViewShell::ExecStatusBar (SfxRequest& ) +{ +} + +void SlideSorterViewShell::Paint ( + const ::tools::Rectangle& rBBox, + ::sd::Window* pWindow) +{ + SetActiveWindow (pWindow); + assert(mpSlideSorter); + if (mpSlideSorter) + mpSlideSorter->GetController().Paint(rBBox,pWindow); +} + +void SlideSorterViewShell::ArrangeGUIElements() +{ + if (IsActive()) + { + assert(mpSlideSorter); + mpSlideSorter->ArrangeGUIElements(maViewPos, maViewSize); + mbIsArrangeGUIElementsPending = false; + } + else + mbIsArrangeGUIElementsPending = true; +} + +void SlideSorterViewShell::Activate (bool bIsMDIActivate) +{ + if(inChartContext(GetView())) + { + // Avoid context changes for chart during activation / deactivation. + const bool bIsContextBroadcasterEnabled (SfxShell::SetContextBroadcasterEnabled(false)); + + ViewShell::Activate(bIsMDIActivate); + + SfxShell::SetContextBroadcasterEnabled(bIsContextBroadcasterEnabled); + return; + } + + ViewShell::Activate(bIsMDIActivate); + if (mbIsArrangeGUIElementsPending) + ArrangeGUIElements(); + + // Determine and broadcast the context that belongs to the main view shell. + EnumContext::Context eContext = EnumContext::Context::Unknown; + std::shared_ptr<ViewShell> pMainViewShell (GetViewShellBase().GetMainViewShell()); + ViewShell::ShellType eMainViewShellType ( + pMainViewShell + ? pMainViewShell->GetShellType() + : ViewShell::ST_NONE); + switch (eMainViewShellType) + { + case ViewShell::ST_IMPRESS: + case ViewShell::ST_SLIDE_SORTER: + case ViewShell::ST_NOTES: + case ViewShell::ST_DRAW: + eContext = EnumContext::Context::DrawPage; + if( nullptr != dynamic_cast< const DrawViewShell *>( pMainViewShell.get() )) + { + DrawViewShell* pDrawViewShell = dynamic_cast<DrawViewShell*>(pMainViewShell.get()); + if (pDrawViewShell != nullptr) + eContext = EnumContext::GetContextEnum(pDrawViewShell->GetSidebarContextName()); + } + break; + + default: + break; + } + ContextChangeEventMultiplexer::NotifyContextChange( + &GetViewShellBase(), + eContext); +} + +void SlideSorterViewShell::Deactivate (bool /*bIsMDIActivate*/) +{ + // Save Settings - Specifically SlidesPerRow to retrieve it later + WriteFrameViewData(); +} + +void SlideSorterViewShell::Command ( + const CommandEvent& rEvent, + ::sd::Window* pWindow) +{ + assert(mpSlideSorter); + if ( ! mpSlideSorter->GetController().Command (rEvent, pWindow)) + ViewShell::Command (rEvent, pWindow); +} + +void SlideSorterViewShell::ReadFrameViewData (FrameView* pFrameView) +{ + assert(mpSlideSorter); + if (pFrameView != nullptr) + { + view::SlideSorterView& rView (mpSlideSorter->GetView()); + + sal_uInt16 nSlidesPerRow (pFrameView->GetSlidesPerRow()); + if (nSlidesPerRow > 0 + && rView.GetOrientation() == view::Layouter::GRID + && IsMainViewShell()) + { + rView.GetLayouter().SetColumnCount(nSlidesPerRow,nSlidesPerRow); + } + if (IsMainViewShell()) + mpSlideSorter->GetController().GetCurrentSlideManager()->NotifyCurrentSlideChange( + mpFrameView->GetSelectedPage()); + mpSlideSorter->GetController().Rearrange(true); + + // DrawMode for 'main' window + if (GetActiveWindow()->GetOutDev()->GetDrawMode() != pFrameView->GetDrawMode() ) + GetActiveWindow()->GetOutDev()->SetDrawMode( pFrameView->GetDrawMode() ); + } + + // When this slide sorter is not displayed in the main window then we do + // not share the same frame view and have to find other ways to acquire + // certain values. + if ( ! IsMainViewShell()) + { + std::shared_ptr<ViewShell> pMainViewShell = GetViewShellBase().GetMainViewShell(); + if (pMainViewShell != nullptr) + mpSlideSorter->GetController().GetCurrentSlideManager()->NotifyCurrentSlideChange( + pMainViewShell->getCurrentPage()); + } +} + +void SlideSorterViewShell::WriteFrameViewData() +{ + assert(mpSlideSorter); + if (mpFrameView == nullptr) + return; + + view::SlideSorterView& rView (mpSlideSorter->GetView()); + mpFrameView->SetSlidesPerRow(static_cast<sal_uInt16>(rView.GetLayouter().GetColumnCount())); + + // DrawMode for 'main' window + if( mpFrameView->GetDrawMode() != GetActiveWindow()->GetOutDev()->GetDrawMode() ) + mpFrameView->SetDrawMode( GetActiveWindow()->GetOutDev()->GetDrawMode() ); + + SdPage* pActualPage = GetActualPage(); + if (pActualPage != nullptr) + { + if (IsMainViewShell()) + mpFrameView->SetSelectedPage((pActualPage->GetPageNum()- 1) / 2); + // else + // The slide sorter is not expected to switch the current page + // other than by double clicks. That is handled separately. + } + else + { + // We have no current page to set but at least we can make sure + // that the index of the frame view has a legal value. + if (mpFrameView->GetSelectedPage() >= mpSlideSorter->GetModel().GetPageCount()) + mpFrameView->SetSelectedPage(static_cast<sal_uInt16>(mpSlideSorter->GetModel().GetPageCount())-1); + } +} + +void SlideSorterViewShell::SetZoom (::tools::Long ) +{ + // Ignored. + // The zoom scale is adapted internally to fit a number of columns in + // the window. +} + +void SlideSorterViewShell::SetZoomRect (const ::tools::Rectangle& rZoomRect) +{ + assert(mpSlideSorter); + Size aPageSize (mpSlideSorter->GetView().GetLayouter().GetPageObjectSize()); + + ::tools::Rectangle aRect(rZoomRect); + + if (aRect.GetWidth() < aPageSize.Width()) + { + ::tools::Long nWidthDiff = (aPageSize.Width() - aRect.GetWidth()) / 2; + + aRect.AdjustLeft( -nWidthDiff ); + aRect.AdjustRight(nWidthDiff ); + + if (aRect.Left() < 0) + { + aRect.SetPos(Point(0, aRect.Top())); + } + } + + if (aRect.GetHeight() < aPageSize.Height()) + { + ::tools::Long nHeightDiff = (aPageSize.Height() - aRect.GetHeight()) / 2; + + aRect.AdjustTop( -nHeightDiff ); + aRect.AdjustBottom(nHeightDiff ); + + if (aRect.Top() < 0) + { + aRect.SetPos(Point(aRect.Left(), 0)); + } + } + + ViewShell::SetZoomRect(aRect); + + GetViewFrame()->GetBindings().Invalidate( SID_ATTR_ZOOM ); + GetViewFrame()->GetBindings().Invalidate( SID_ATTR_ZOOMSLIDER ); +} + +void SlideSorterViewShell::UpdateScrollBars() +{ + // Do not call the overwritten method of the base class: We do all the + // scroll bar setup by ourselves. + mpSlideSorter->GetController().GetScrollBarManager().UpdateScrollBars(true); +} + +void SlideSorterViewShell::StartDrag ( + const Point& rDragPt, + vcl::Window* pWindow ) +{ + assert(mpSlideSorter); + mpSlideSorter->GetController().GetClipboard().StartDrag ( + rDragPt, + pWindow); +} + +sal_Int8 SlideSorterViewShell::AcceptDrop ( + const AcceptDropEvent& rEvt, + DropTargetHelper& rTargetHelper, + ::sd::Window* pTargetWindow, + sal_uInt16 nPage, + SdrLayerID nLayer) +{ + assert(mpSlideSorter); + return mpSlideSorter->GetController().GetClipboard().AcceptDrop ( + rEvt, + rTargetHelper, + pTargetWindow, + nPage, + nLayer); +} + +sal_Int8 SlideSorterViewShell::ExecuteDrop ( + const ExecuteDropEvent& rEvt, + DropTargetHelper& rTargetHelper, + ::sd::Window* pTargetWindow, + sal_uInt16 nPage, + SdrLayerID nLayer) +{ + assert(mpSlideSorter); + return mpSlideSorter->GetController().GetClipboard().ExecuteDrop ( + rEvt, + rTargetHelper, + pTargetWindow, + nPage, + nLayer); +} + +std::shared_ptr<SlideSorterViewShell::PageSelection> + SlideSorterViewShell::GetPageSelection() const +{ + assert(mpSlideSorter); + return mpSlideSorter->GetController().GetPageSelector().GetPageSelection(); +} + +void SlideSorterViewShell::SetPageSelection ( + const std::shared_ptr<PageSelection>& rSelection) +{ + assert(mpSlideSorter); + mpSlideSorter->GetController().GetPageSelector().SetPageSelection(rSelection, true); +} + +void SlideSorterViewShell::AddSelectionChangeListener ( + const Link<LinkParamNone*,void>& rCallback) +{ + assert(mpSlideSorter); + mpSlideSorter->GetController().GetSelectionManager()->AddSelectionChangeListener(rCallback); +} + +void SlideSorterViewShell::RemoveSelectionChangeListener ( + const Link<LinkParamNone*,void>& rCallback) +{ + assert(mpSlideSorter); + mpSlideSorter->GetController().GetSelectionManager()->RemoveSelectionChangeListener(rCallback); +} + +void SlideSorterViewShell::MainViewEndEditAndUnmarkAll() +{ + std::shared_ptr<ViewShell> pMainViewShell = GetViewShellBase().GetMainViewShell(); + DrawViewShell* pDrawViewShell = dynamic_cast<DrawViewShell*>(pMainViewShell.get()); + SdrView* pView = pDrawViewShell ? pDrawViewShell->GetDrawView() : nullptr; + if (pView) + { + pView->SdrEndTextEdit(); + pView->UnmarkAll(); + } +} + +std::pair<sal_uInt16, sal_uInt16> SlideSorterViewShell::SyncPageSelectionToDocument(const std::shared_ptr<SlideSorterViewShell::PageSelection> &rpSelection) +{ + sal_uInt16 firstSelectedPageNo = SAL_MAX_UINT16; + sal_uInt16 lastSelectedPageNo = 0; + + GetDoc()->UnselectAllPages(); + for (auto& rpPage : *rpSelection) + { + // Check page number + sal_uInt16 pageNo = rpPage->GetPageNum(); + if (pageNo > lastSelectedPageNo) + lastSelectedPageNo = pageNo; + if (pageNo < firstSelectedPageNo) + firstSelectedPageNo = pageNo; + GetDoc()->SetSelected(rpPage, true); + } + + return std::make_pair(firstSelectedPageNo, lastSelectedPageNo); +} + +void SlideSorterViewShell::ExecMovePageFirst (SfxRequest& /*rReq*/) +{ + MainViewEndEditAndUnmarkAll(); + + std::shared_ptr<SlideSorterViewShell::PageSelection> xSelection(GetPageSelection()); + + // SdDrawDocument MovePages is based on SdPage IsSelected, so + // transfer the SlideSorter selection to SdPages + SyncPageSelectionToDocument(xSelection); + + // Moves selected pages after page -1 + GetDoc()->MovePages( sal_uInt16(-1) ); + + PostMoveSlidesActions(xSelection); +} + +void SlideSorterViewShell::GetStateMovePageFirst (SfxItemSet& rSet) +{ + if ( ! IsMainViewShell()) + { + std::shared_ptr<ViewShell> pMainViewShell = GetViewShellBase().GetMainViewShell(); + DrawViewShell* pDrawViewShell = dynamic_cast<DrawViewShell*>(pMainViewShell.get()); + if (pDrawViewShell != nullptr && pDrawViewShell->GetPageKind() == PageKind::Handout) + { + rSet.DisableItem( SID_MOVE_PAGE_FIRST ); + rSet.DisableItem( SID_MOVE_PAGE_UP ); + return; + } + } + + std::shared_ptr<SlideSorterViewShell::PageSelection> xSelection(GetPageSelection()); + + // SdDrawDocument MovePages is based on SdPage IsSelected, so + // transfer the SlideSorter selection to SdPages + sal_uInt16 firstSelectedPageNo = SyncPageSelectionToDocument(xSelection).first; + // Now compute human page number from internal page number + firstSelectedPageNo = (firstSelectedPageNo - 1) / 2; + + if (firstSelectedPageNo == 0) + { + rSet.DisableItem( SID_MOVE_PAGE_FIRST ); + rSet.DisableItem( SID_MOVE_PAGE_UP ); + } +} + +void SlideSorterViewShell::ExecMovePageUp (SfxRequest& /*rReq*/) +{ + MainViewEndEditAndUnmarkAll(); + + std::shared_ptr<SlideSorterViewShell::PageSelection> xSelection(GetPageSelection()); + + // SdDrawDocument MovePages is based on SdPage IsSelected, so + // transfer the SlideSorter selection to SdPages + sal_uInt16 firstSelectedPageNo = SyncPageSelectionToDocument(xSelection).first; + + // In case no slide is selected + if (firstSelectedPageNo == SAL_MAX_UINT16) + return; + + // Now compute human page number from internal page number + firstSelectedPageNo = (firstSelectedPageNo - 1) / 2; + + if (firstSelectedPageNo == 0) + return; + + // Move pages before firstSelectedPageNo - 1 (so after firstSelectedPageNo - 2), + // remembering that -1 means at first, which is good. + GetDoc()->MovePages( firstSelectedPageNo - 2 ); + + PostMoveSlidesActions(xSelection); +} + +void SlideSorterViewShell::GetStateMovePageUp (SfxItemSet& rSet) +{ + GetStateMovePageFirst(rSet); +} + +void SlideSorterViewShell::ExecMovePageDown (SfxRequest& /*rReq*/) +{ + MainViewEndEditAndUnmarkAll(); + + std::shared_ptr<SlideSorterViewShell::PageSelection> xSelection(GetPageSelection()); + + // SdDrawDocument MovePages is based on SdPage IsSelected, so + // transfer the SlideSorter selection to SdPages + sal_uInt16 lastSelectedPageNo = SyncPageSelectionToDocument(xSelection).second; + + // Get page number of the last page + sal_uInt16 nNoOfPages = GetDoc()->GetSdPageCount(PageKind::Standard); + + // Now compute human page number from internal page number + lastSelectedPageNo = (lastSelectedPageNo - 1) / 2; + if (lastSelectedPageNo == nNoOfPages - 1) + return; + + // Move to position after lastSelectedPageNo + GetDoc()->MovePages( lastSelectedPageNo + 1 ); + + PostMoveSlidesActions(xSelection); +} + +void SlideSorterViewShell::GetStateMovePageDown (SfxItemSet& rSet) +{ + GetStateMovePageLast( rSet ); +} + +void SlideSorterViewShell::ExecMovePageLast (SfxRequest& /*rReq*/) +{ + MainViewEndEditAndUnmarkAll(); + + std::shared_ptr<SlideSorterViewShell::PageSelection> xSelection(GetPageSelection()); + + // SdDrawDocument MovePages is based on SdPage IsSelected, so + // transfer the SlideSorter selection to SdPages + SyncPageSelectionToDocument(xSelection); + + // Get page number of the last page + sal_uInt16 nNoOfPages = GetDoc()->GetSdPageCount(PageKind::Standard); + + // Move to position after last page No (=Number of pages - 1) + GetDoc()->MovePages( nNoOfPages - 1 ); + + PostMoveSlidesActions(xSelection); +} + +void SlideSorterViewShell::GetStateMovePageLast (SfxItemSet& rSet) +{ + std::shared_ptr<ViewShell> pMainViewShell = GetViewShellBase().GetMainViewShell(); + DrawViewShell* pDrawViewShell = dynamic_cast<DrawViewShell*>(pMainViewShell.get()); + if (pDrawViewShell != nullptr && pDrawViewShell->GetPageKind() == PageKind::Handout) + { + rSet.DisableItem( SID_MOVE_PAGE_LAST ); + rSet.DisableItem( SID_MOVE_PAGE_DOWN ); + return; + } + + std::shared_ptr<SlideSorterViewShell::PageSelection> xSelection(GetPageSelection()); + + // SdDrawDocument MovePages is based on SdPage IsSelected, so + // transfer the SlideSorter selection to SdPages + sal_uInt16 lastSelectedPageNo = SyncPageSelectionToDocument(xSelection).second; + + // Get page number of the last page + sal_uInt16 nNoOfPages = GetDoc()->GetSdPageCount(PageKind::Standard); + + // Now compute human page number from internal page number + lastSelectedPageNo = (lastSelectedPageNo - 1) / 2; + if (lastSelectedPageNo == nNoOfPages - 1) + { + rSet.DisableItem( SID_MOVE_PAGE_LAST ); + rSet.DisableItem( SID_MOVE_PAGE_DOWN ); + } +} + +void SlideSorterViewShell::PostMoveSlidesActions(const std::shared_ptr<SlideSorterViewShell::PageSelection> &rpSelection) +{ + sal_uInt16 nNoOfPages = GetDoc()->GetSdPageCount(PageKind::Standard); + for (sal_uInt16 nPage = 0; nPage < nNoOfPages; nPage++) + { + SdPage* pPage = GetDoc()->GetSdPage(nPage, PageKind::Standard); + GetDoc()->SetSelected(pPage, false); + } + + mpSlideSorter->GetController().GetPageSelector().DeselectAllPages(); + for (const auto& rpPage : *rpSelection) + { + mpSlideSorter->GetController().GetPageSelector().SelectPage(rpPage); + } + + // Refresh toolbar icons + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate(SID_MOVE_PAGE_FIRST); + rBindings.Invalidate(SID_MOVE_PAGE_UP); + rBindings.Invalidate(SID_MOVE_PAGE_DOWN); + rBindings.Invalidate(SID_MOVE_PAGE_LAST); + +} + +} // end of namespace ::sd::slidesorter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/view/SlideSorterView.cxx b/sd/source/ui/slidesorter/view/SlideSorterView.cxx new file mode 100644 index 000000000..390541e37 --- /dev/null +++ b/sd/source/ui/slidesorter/view/SlideSorterView.cxx @@ -0,0 +1,856 @@ +/* -*- 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 <view/SlideSorterView.hxx> + +#include <SlideSorter.hxx> +#include <ViewShell.hxx> +#include "SlsViewCacheContext.hxx" +#include "SlsLayeredDevice.hxx" +#include <view/SlsLayouter.hxx> +#include <view/SlsPageObjectLayouter.hxx> +#include <view/SlsPageObjectPainter.hxx> +#include <view/SlsILayerPainter.hxx> +#include <view/SlsToolTip.hxx> +#include <controller/SlideSorterController.hxx> +#include <controller/SlsClipboard.hxx> +#include <model/SlideSorterModel.hxx> +#include <model/SlsPageEnumerationProvider.hxx> +#include <model/SlsPageDescriptor.hxx> +#include <cache/SlsPageCache.hxx> +#include <cache/SlsPageCacheManager.hxx> +#include <PaneDockingWindow.hxx> + +#include <sdpage.hxx> +#include <Window.hxx> + +#include <comphelper/lok.hxx> +#include <osl/diagnose.h> +#include <vcl/svapp.hxx> +#include <vcl/scrbar.hxx> +#include <vcl/settings.hxx> +#include <vcl/graphicfilter.hxx> + +#include <algorithm> + +//#define DEBUG_TIMING +#ifdef DEBUG_TIMING +#include <memory> +#include <vector> +#endif + +using namespace ::sd::slidesorter::model; +using namespace ::drawinglayer::primitive2d; + +namespace sd::slidesorter::view { + +namespace { + /** Wrapper around the SlideSorterView that supports the IPainter + interface and that allows the LayeredDevice to hold the + SlideSorterView (held as unique_ptr by the SlideSorter) as + shared_ptr. + */ + class Painter : public ILayerPainter + { + public: + explicit Painter (SlideSorterView& rView) : mrView(rView) {} + + virtual void Paint (OutputDevice& rDevice, const ::tools::Rectangle& rRepaintArea) override + { + mrView.Paint(rDevice,rRepaintArea); + } + + virtual void SetLayerInvalidator (const SharedILayerInvalidator&) override {} + + private: + SlideSorterView& mrView; + }; +} + +namespace { + +class BackgroundPainter + : public ILayerPainter +{ +public: + explicit BackgroundPainter (const Color& rBackgroundColor) : maBackgroundColor(rBackgroundColor) {} + BackgroundPainter(const BackgroundPainter&) = delete; + BackgroundPainter& operator=(const BackgroundPainter&) = delete; + + virtual void Paint (OutputDevice& rDevice, const ::tools::Rectangle& rRepaintArea) override + { + rDevice.SetFillColor(maBackgroundColor); + rDevice.SetLineColor(); + rDevice.DrawRect(rRepaintArea); + } + + virtual void SetLayerInvalidator (const SharedILayerInvalidator&) override {} + + void SetColor (const Color& rColor) { maBackgroundColor = rColor; } + +private: + Color maBackgroundColor; +}; + +} + +SlideSorterView::SlideSorterView (SlideSorter& rSlideSorter) + : ::sd::View ( + *rSlideSorter.GetModel().GetDocument(), + rSlideSorter.GetContentWindow()->GetOutDev(), + rSlideSorter.GetViewShell()), + mrSlideSorter(rSlideSorter), + mrModel(rSlideSorter.GetModel()), + mbIsDisposed(false), + mpLayouter (new Layouter(rSlideSorter.GetContentWindow(), rSlideSorter.GetTheme())), + mbPageObjectVisibilitiesValid (false), + mpLayeredDevice(std::make_shared<LayeredDevice>(rSlideSorter.GetContentWindow())), + maVisiblePageRange(-1,-1), + maPreviewSize(0,0), + mbPreciousFlagUpdatePending(true), + meOrientation(Layouter::GRID), + mpBackgroundPainter( + std::make_shared<BackgroundPainter>(mrSlideSorter.GetTheme()->GetColor(Theme::Color_Background))), + mpToolTip(new ToolTip(mrSlideSorter)), + mbIsRearrangePending(true) +{ + // Hide the page that contains the page objects. + SetPageVisible (false); + + // Register the background painter on level 1 to avoid the creation of a + // background buffer. + mpLayeredDevice->RegisterPainter(mpBackgroundPainter, 1); + + // Wrap a shared_ptr-held-wrapper around this view and register it as + // painter at the layered device. There is no explicit destruction: in + // the SlideSorterView destructor the layered device is destroyed and + // with it the only reference to the wrapper which therefore is also + // destroyed. + SharedILayerPainter pPainter = std::make_shared<Painter>(*this); + + // The painter is placed on level 1 to avoid buffering. This should be + // a little faster during animations because the previews are painted + // directly into the window, not via the buffer. + mpLayeredDevice->RegisterPainter(pPainter, 1); +} + +SlideSorterView::~SlideSorterView() +{ + if ( ! mbIsDisposed) + { + OSL_ASSERT(mbIsDisposed); + Dispose(); + } +} + +void SlideSorterView::Init() +{ + HandleModelChange(); +} + +void SlideSorterView::Dispose() +{ + mpLayeredDevice->Dispose(); + mpPreviewCache.reset(); + + SetPageUnderMouse(SharedPageDescriptor()); + + // Hide the page to avoid problems in the view when deleting + // visualized objects + HideSdrPage(); + + // Deletion of the objects and the page will be done in SdrModel + // destructor (as long as objects and pages are added) + + OSL_ASSERT(mpLayeredDevice.use_count() == 1); + mpLayeredDevice.reset(); + + mbIsDisposed = true; +} + +sal_Int32 SlideSorterView::GetPageIndexAtPoint (const Point& rWindowPosition) const +{ + sal_Int32 nIndex (-1); + + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if (pWindow) + { + nIndex = mpLayouter->GetIndexAtPoint(pWindow->PixelToLogic(rWindowPosition), false, false); + + // Clip the page index against the page count. + if (nIndex >= mrModel.GetPageCount()) + nIndex = -1; + } + + return nIndex; +} + +Layouter& SlideSorterView::GetLayouter() { return *mpLayouter; } + +void SlideSorterView::ModelHasChanged() +{ + // Ignore this call. Rely on hints sent by the model to get informed of + // model changes. +} + +void SlideSorterView::PreModelChange() +{ + // Reset the slide under the mouse. It will be re-set in PostModelChange(). + SetPageUnderMouse(SharedPageDescriptor()); +} + +void SlideSorterView::PostModelChange() +{ + // In PreModelChange() the page objects have been released. Here we + // create new ones. + ::osl::MutexGuard aGuard (mrModel.GetMutex()); + + model::PageEnumerationProvider::CreateAllPagesEnumeration(mrModel); + + // The new page objects have to be scaled and positioned. + RequestRearrange(); + RequestRepaint(); +} + +/** At the moment for every model change all page objects are destroyed and + re-created again. This can be optimized by accepting hints that + describe the type of change so that existing page objects can be + reused. +*/ +void SlideSorterView::HandleModelChange() +{ + PreModelChange (); + PostModelChange(); +} + +void SlideSorterView::HandleDrawModeChange() +{ + // Replace the preview cache with a new and empty one. The + // PreviewRenderer that is used by the cache is replaced by this as + // well. + mpPreviewCache.reset(); + GetPreviewCache()->InvalidateCache(); + + RequestRepaint(); +} + +void SlideSorterView::HandleDataChangeEvent() +{ + GetPageObjectPainter()->SetTheme(mrSlideSorter.GetTheme()); + + // Update the color used by the background painter. + std::shared_ptr<BackgroundPainter> pPainter ( + std::dynamic_pointer_cast<BackgroundPainter>(mpBackgroundPainter)); + if (pPainter) + pPainter->SetColor(mrSlideSorter.GetTheme()->GetColor(Theme::Color_Background)); + + RequestRepaint(); +} + +void SlideSorterView::Resize() +{ + UpdateOrientation(); + + mpLayeredDevice->Resize(); + RequestRearrange(); +} + +void SlideSorterView::RequestRearrange() +{ + mbIsRearrangePending = true; + Rearrange(); +} + +void SlideSorterView::Rearrange() +{ + if ( ! mbIsRearrangePending) + return; + if (mrModel.GetPageCount() <= 0) + return; + + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if ( ! pWindow) + return; + const Size aWindowSize (pWindow->GetSizePixel()); + if (aWindowSize.IsEmpty()) + return; + + const bool bRearrangeSuccess ( + mpLayouter->Rearrange ( + meOrientation, + aWindowSize, + mrModel.GetPageDescriptor(0)->GetPage()->GetSize(), + mrModel.GetPageCount())); + if (bRearrangeSuccess) + { + mbIsRearrangePending = false; + Layout(); + UpdatePageUnderMouse(); + // RequestRepaint(); + } +} + +void SlideSorterView::UpdateOrientation() +{ + // The layout of slides depends on whether the slide sorter is + // displayed in the center or the side pane. + if (mrSlideSorter.GetViewShell()->IsMainViewShell()) + SetOrientation(Layouter::GRID); + else + { + // Get access to the docking window. + vcl::Window* pWindow = mrSlideSorter.GetContentWindow(); + PaneDockingWindow* pDockingWindow = nullptr; + while (pWindow!=nullptr && pDockingWindow==nullptr) + { + pDockingWindow = dynamic_cast<PaneDockingWindow*>(pWindow); + pWindow = pWindow->GetParent(); + } + + if (pDockingWindow != nullptr) + { + const ::tools::Long nScrollBarSize ( + Application::GetSettings().GetStyleSettings().GetScrollBarSize()); + switch (pDockingWindow->GetOrientation()) + { + case PaneDockingWindow::HorizontalOrientation: + if (SetOrientation(Layouter::HORIZONTAL)) + { + const Range aRange (mpLayouter->GetValidVerticalSizeRange()); + pDockingWindow->SetValidSizeRange(Range( + aRange.Min() + nScrollBarSize, + aRange.Max() + nScrollBarSize)); + } + break; + + case PaneDockingWindow::VerticalOrientation: + if (SetOrientation(Layouter::VERTICAL)) + { + const Range aRange (mpLayouter->GetValidHorizontalSizeRange()); + pDockingWindow->SetValidSizeRange(Range( + aRange.Min() + nScrollBarSize, + aRange.Max() + nScrollBarSize)); + } + break; + + case PaneDockingWindow::UnknownOrientation: + if (SetOrientation(Layouter::GRID)) + { + const sal_Int32 nAdditionalSize (10); + pDockingWindow->SetMinOutputSizePixel(Size( + mpLayouter->GetValidHorizontalSizeRange().Min() + + nScrollBarSize + + nAdditionalSize, + mpLayouter->GetValidVerticalSizeRange().Min() + + nScrollBarSize + + nAdditionalSize)); + } + return; + } + } + else + { + // We are not placed in a docking window. One possible reason + // is that the slide sorter is temporarily into a cache and was + // reparented to a non-docking window. + SetOrientation(Layouter::GRID); + } + } +} + +void SlideSorterView::Layout () +{ + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if (pWindow) + { + // Set the model area, i.e. the smallest rectangle that includes all + // page objects. + const ::tools::Rectangle aViewBox (mpLayouter->GetTotalBoundingBox()); + pWindow->SetViewOrigin (aViewBox.TopLeft()); + pWindow->SetViewSize (aViewBox.GetSize()); + + std::shared_ptr<PageObjectLayouter> pPageObjectLayouter( + mpLayouter->GetPageObjectLayouter()); + if (pPageObjectLayouter) + { + const Size aNewPreviewSize (mpLayouter->GetPageObjectLayouter()->GetPreviewSize()); + if (maPreviewSize != aNewPreviewSize && GetPreviewCache()) + { + mpPreviewCache->ChangeSize(aNewPreviewSize, Bitmap::HasFastScale()); + maPreviewSize = aNewPreviewSize; + } + } + + // Iterate over all page objects and place them relative to the + // containing page. + model::PageEnumeration aPageEnumeration ( + model::PageEnumerationProvider::CreateAllPagesEnumeration(mrModel)); + while (aPageEnumeration.HasMoreElements()) + { + model::SharedPageDescriptor pDescriptor (aPageEnumeration.GetNextElement()); + pDescriptor->SetBoundingBox(mpLayouter->GetPageObjectBox(pDescriptor->GetPageIndex(), false)); + } + } + + InvalidatePageObjectVisibilities (); +} + +void SlideSorterView::InvalidatePageObjectVisibilities() +{ + mbPageObjectVisibilitiesValid = false; +} + +void SlideSorterView::DeterminePageObjectVisibilities() +{ + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if (!pWindow) + return; + + // Set this flag to true here so that an invalidate during the + // visibility calculation can correctly invalidate it again. + mbPageObjectVisibilitiesValid = true; + + ::tools::Rectangle aViewArea (pWindow->PixelToLogic(::tools::Rectangle(Point(0,0),pWindow->GetSizePixel()))); + const Range aRange (mpLayouter->GetRangeOfVisiblePageObjects(aViewArea)); + const Range aUnion( + ::std::min(maVisiblePageRange.Min(), aRange.Min()), + ::std::max(maVisiblePageRange.Max(), aRange.Max())); + + // For page objects that just dropped off the visible area we + // decrease the priority of pending requests for preview bitmaps. + if (maVisiblePageRange != aRange) + mbPreciousFlagUpdatePending |= true; + + model::SharedPageDescriptor pDescriptor; + for (::tools::Long nIndex=aUnion.Min(); nIndex<=aUnion.Max(); nIndex++) + { + pDescriptor = mrModel.GetPageDescriptor(nIndex); + if (pDescriptor) + SetState( + pDescriptor, + PageDescriptor::ST_Visible, + aRange.Contains(nIndex)); + } + + // Broadcast a change of the set of visible page objects. + if (maVisiblePageRange != aRange) + { + maVisiblePageRange = aRange; + + // Tell the listeners that the visibility of some objects has + // changed. + ::std::vector<Link<LinkParamNone*,void>>& aChangeListeners (maVisibilityChangeListeners); + for (const auto& rLink : aChangeListeners) + { + rLink.Call(nullptr); + } + } + + // Restore the mouse over state. + UpdatePageUnderMouse(); +} + +void SlideSorterView::UpdatePreciousFlags() +{ + if (!mbPreciousFlagUpdatePending) + return; + + mbPreciousFlagUpdatePending = false; + + model::SharedPageDescriptor pDescriptor; + std::shared_ptr<cache::PageCache> pCache = GetPreviewCache(); + sal_Int32 nPageCount (mrModel.GetPageCount()); + + for (int nIndex=0; nIndex<=nPageCount; ++nIndex) + { + pDescriptor = mrModel.GetPageDescriptor(nIndex); + if (pDescriptor) + { + pCache->SetPreciousFlag( + pDescriptor->GetPage(), + maVisiblePageRange.Contains(nIndex)); + } + else + { + // At least one cache entry can not be updated. Remember to + // repeat the whole updating later and leave the loop now. + mbPreciousFlagUpdatePending = true; + break; + } + } +} + +bool SlideSorterView::SetOrientation (const Layouter::Orientation eOrientation) +{ + if (meOrientation != eOrientation) + { + meOrientation = eOrientation; + return true; + } + else + return false; +} + +void SlideSorterView::RequestRepaint() +{ + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if (pWindow) + { + mpLayeredDevice->InvalidateAllLayers( + ::tools::Rectangle( + pWindow->PixelToLogic(Point(0,0)), + pWindow->PixelToLogic(pWindow->GetSizePixel()))); + pWindow->Invalidate(); + } +} + +void SlideSorterView::RequestRepaint (const model::SharedPageDescriptor& rpDescriptor) +{ + if (rpDescriptor) + RequestRepaint(rpDescriptor->GetBoundingBox()); +} + +void SlideSorterView::RequestRepaint (const ::tools::Rectangle& rRepaintBox) +{ + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if (pWindow) + { + mpLayeredDevice->InvalidateAllLayers(rRepaintBox); + pWindow->Invalidate(rRepaintBox); + } +} + +void SlideSorterView::RequestRepaint (const vcl::Region& rRepaintRegion) +{ + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if (pWindow) + { + mpLayeredDevice->InvalidateAllLayers(rRepaintRegion); + pWindow->Invalidate(rRepaintRegion); + } +} + +::tools::Rectangle SlideSorterView::GetModelArea() const +{ + return mpLayouter->GetTotalBoundingBox(); +} + +#ifdef DEBUG_TIMING +static ::canvas::tools::ElapsedTime gaTimer; +static const size_t gFrameTimeCount (10); +static size_t gFrameTimeIndex (0); +static ::std::vector<double> gFrameTimes (gFrameTimeCount, 0); +static double gFrameTimeSum (0); +static const ::tools::Rectangle gFrameTimeBox (10,10,150,20); +static double gnLastFrameStart = 0; +#endif + +void SlideSorterView::CompleteRedraw ( + OutputDevice* pDevice, + const vcl::Region& rPaintArea, + sdr::contact::ViewObjectContactRedirector* pRedirector) +{ + (void)pRedirector; + + if (comphelper::LibreOfficeKit::isActive()) + return; + + if (pDevice == nullptr || pDevice!=mrSlideSorter.GetContentWindow()->GetOutDev()) + return; + +#ifdef DEBUG_TIMING + const double nStartTime (gaTimer.getElapsedTime()); + SAL_INFO("sd.timing", "SlideSorterView::CompleteRedraw start" << (mnLockRedrawSmph ? " locked" : "")); +#endif + + // The parent implementation of CompleteRedraw is called only when + // painting is locked. We do all the painting ourself. When painting + // is locked the parent implementation keeps track of the repaint + // requests and later, when painting is unlocked, calls CompleteRedraw + // for all missed repaints. + + if (mnLockRedrawSmph == 0) + { + if (mpLayeredDevice->HandleMapModeChange()) + DeterminePageObjectVisibilities(); + mpLayeredDevice->Repaint(rPaintArea); + } + else + { + maRedrawRegion.Union(rPaintArea); + } + +#ifdef DEBUG_TIMING + const double nEndTime (gaTimer.getElapsedTime()); + SAL_INFO("sd.timing", "SlideSorterView::CompleteRedraw end after " << (nEndTime-nStartTime)*1000 << " ms"); + gFrameTimeSum -= gFrameTimes[gFrameTimeIndex]; + gFrameTimes[gFrameTimeIndex] = nStartTime - gnLastFrameStart; + gnLastFrameStart = nStartTime; + gFrameTimeSum += gFrameTimes[gFrameTimeIndex]; + gFrameTimeIndex = (gFrameTimeIndex+1) % gFrameTimeCount; + + mrSlideSorter.GetContentWindow()->SetFillColor(COL_BLUE); + mrSlideSorter.GetContentWindow()->DrawRect(gFrameTimeBox); + mrSlideSorter.GetContentWindow()->SetTextColor(COL_WHITE); + mrSlideSorter.GetContentWindow()->DrawText( + gFrameTimeBox, + OUString::number(1 / (gFrameTimeSum / gFrameTimeCount)), + DrawTextFlags::Right | DrawTextFlags::VCenter); + // mrSlideSorter.GetContentWindow()->Invalidate(gFrameTimeBox); +#endif +} + +void SlideSorterView::Paint ( + OutputDevice& rDevice, + const ::tools::Rectangle& rRepaintArea) +{ + if (rRepaintArea.IsEmpty()) + return; + + if ( ! mpPageObjectPainter) + if ( ! GetPageObjectPainter()) + return; + + // Update the page visibilities when they have been invalidated. + if ( ! mbPageObjectVisibilitiesValid) + DeterminePageObjectVisibilities(); + + if (mbPreciousFlagUpdatePending) + UpdatePreciousFlags(); + + if (mbIsRearrangePending) + Rearrange(); + + // Paint all page objects that are fully or partially inside the + // repaint region. + const Range aRange (mpLayouter->GetRangeOfVisiblePageObjects(rRepaintArea)); + // Try to prefetch all graphics from the pages to paint. This will be done + // in threads to be more efficient than loading them on-demand one by one. + std::vector<Graphic*> graphics; + for (::tools::Long nIndex=aRange.Min(); nIndex<=aRange.Max(); ++nIndex) + { + model::SharedPageDescriptor pDescriptor (mrModel.GetPageDescriptor(nIndex)); + if (!pDescriptor || ! pDescriptor->HasState(PageDescriptor::ST_Visible)) + continue; + pDescriptor->GetPage()->getGraphicsForPrefetch(graphics); + } + // Handle also one page before and after to have those in advance on scrolling. + for (::tools::Long nIndex : { aRange.Min() - 1, aRange.Max() + 1 }) + { + model::SharedPageDescriptor pDescriptor (mrModel.GetPageDescriptor(nIndex)); + if (!pDescriptor) + continue; + pDescriptor->GetPage()->getGraphicsForPrefetch(graphics); + } + if(graphics.size() > 1) // threading does not help with loading just one + GraphicFilter::GetGraphicFilter().MakeGraphicsAvailableThreaded(graphics); + + for (::tools::Long nIndex=aRange.Min(); nIndex<=aRange.Max(); ++nIndex) + { + model::SharedPageDescriptor pDescriptor (mrModel.GetPageDescriptor(nIndex)); + if (!pDescriptor || ! pDescriptor->HasState(PageDescriptor::ST_Visible)) + continue; + + mpPageObjectPainter->PaintPageObject(rDevice, pDescriptor); + } +} + +void SlideSorterView::ConfigurationChanged ( + utl::ConfigurationBroadcaster* pBroadcaster, + ConfigurationHints nHint) +{ + // Some changes of the configuration (some of the colors for example) + // may affect the previews. Throw away the old ones and create new ones. + cache::PageCacheManager::Instance()->InvalidateAllCaches(); + + ::sd::View::ConfigurationChanged(pBroadcaster, nHint); + RequestRepaint(); + +} + +std::shared_ptr<cache::PageCache> const & SlideSorterView::GetPreviewCache() +{ + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if (pWindow && mpPreviewCache == nullptr) + { + mpPreviewCache = + std::make_shared<cache::PageCache>( + mpLayouter->GetPageObjectSize(), + Bitmap::HasFastScale(), + std::make_shared<ViewCacheContext>(mrSlideSorter)); + } + + return mpPreviewCache; +} + +Range const & SlideSorterView::GetVisiblePageRange() +{ + if ( ! mbPageObjectVisibilitiesValid) + DeterminePageObjectVisibilities(); + return maVisiblePageRange; +} + +void SlideSorterView::AddVisibilityChangeListener (const Link<LinkParamNone*,void>& rListener) +{ + if (::std::find ( + maVisibilityChangeListeners.begin(), + maVisibilityChangeListeners.end(), + rListener) == maVisibilityChangeListeners.end()) + { + maVisibilityChangeListeners.push_back(rListener); + } +} + +void SlideSorterView::RemoveVisibilityChangeListener(const Link<LinkParamNone*,void>&rListener) +{ + maVisibilityChangeListeners.erase ( + ::std::find ( + maVisibilityChangeListeners.begin(), + maVisibilityChangeListeners.end(), + rListener)); +} + +ToolTip& SlideSorterView::GetToolTip() const +{ + OSL_ASSERT(mpToolTip); + return *mpToolTip; +} + +void SlideSorterView::DragFinished (sal_Int8 nDropAction) +{ + mrSlideSorter.GetController().GetClipboard().DragFinished(nDropAction); + + View::DragFinished(nDropAction); +} + +void SlideSorterView::UpdatePageUnderMouse () +{ + VclPtr<ScrollBar> pVScrollBar (mrSlideSorter.GetVerticalScrollBar()); + VclPtr<ScrollBar> pHScrollBar (mrSlideSorter.GetHorizontalScrollBar()); + if ((pVScrollBar && pVScrollBar->IsVisible() && pVScrollBar->IsTracking()) + || (pHScrollBar && pHScrollBar->IsVisible() && pHScrollBar->IsTracking())) + { + // One of the scroll bars is tracking mouse movement. Do not + // highlight the slide under the mouse in this case. + SetPageUnderMouse(SharedPageDescriptor()); + return; + } + + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if (pWindow && pWindow->IsVisible() && ! pWindow->IsMouseCaptured()) + { + const Window::PointerState aPointerState (pWindow->GetPointerState()); + const ::tools::Rectangle aWindowBox (pWindow->GetPosPixel(), pWindow->GetSizePixel()); + if (aWindowBox.Contains(aPointerState.maPos)) + { + UpdatePageUnderMouse(aPointerState.maPos); + return; + } + } + + SetPageUnderMouse(SharedPageDescriptor()); +} + +void SlideSorterView::UpdatePageUnderMouse ( + const Point& rMousePosition) +{ + SetPageUnderMouse(mrSlideSorter.GetController().GetPageAt(rMousePosition)); +} + +void SlideSorterView::SetPageUnderMouse ( + const model::SharedPageDescriptor& rpDescriptor) +{ + if (mpPageUnderMouse == rpDescriptor) + return; + + if (mpPageUnderMouse) + SetState(mpPageUnderMouse, PageDescriptor::ST_MouseOver, false); + + mpPageUnderMouse = rpDescriptor; + + if (mpPageUnderMouse) + SetState(mpPageUnderMouse, PageDescriptor::ST_MouseOver, true); + + // Change the quick help text to display the name of the page under + // the mouse. + mpToolTip->SetPage(rpDescriptor); +} + +bool SlideSorterView::SetState ( + const model::SharedPageDescriptor& rpDescriptor, + const PageDescriptor::State eState, + const bool bStateValue) +{ + if ( ! rpDescriptor) + return false; + + const bool bModified (rpDescriptor->SetState(eState, bStateValue)); + if ( ! bModified) + return false; + + // When the page object is not visible (i.e. not on the screen then + // nothing has to be painted. + if (rpDescriptor->HasState(PageDescriptor::ST_Visible)) + { + // For most states a change of that state leads to visible + // difference and we have to request a repaint. + if (eState != PageDescriptor::ST_WasSelected) + RequestRepaint(rpDescriptor); + } + + return bModified; +} + +std::shared_ptr<PageObjectPainter> const & SlideSorterView::GetPageObjectPainter() +{ + if ( ! mpPageObjectPainter) + mpPageObjectPainter = std::make_shared<PageObjectPainter>(mrSlideSorter); + return mpPageObjectPainter; +} + +//===== SlideSorterView::DrawLock ============================================= + +SlideSorterView::DrawLock::DrawLock (SlideSorter const & rSlideSorter) + : mrView(rSlideSorter.GetView()), + mpWindow(rSlideSorter.GetContentWindow()) +{ + if (mrView.mnLockRedrawSmph == 0) + mrView.maRedrawRegion.SetEmpty(); + ++mrView.mnLockRedrawSmph; +} + +SlideSorterView::DrawLock::~DrawLock() +{ + OSL_ASSERT(mrView.mnLockRedrawSmph>0); + --mrView.mnLockRedrawSmph; + if (mrView.mnLockRedrawSmph == 0) + if (mpWindow) + { + mpWindow->Invalidate(mrView.maRedrawRegion); + } +} + +void SlideSorterView::DrawLock::Dispose() +{ + mpWindow.reset(); +} + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/view/SlsFramePainter.cxx b/sd/source/ui/slidesorter/view/SlsFramePainter.cxx new file mode 100644 index 000000000..31c301868 --- /dev/null +++ b/sd/source/ui/slidesorter/view/SlsFramePainter.cxx @@ -0,0 +1,225 @@ +/* -*- 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 "SlsFramePainter.hxx" +#include <vcl/outdev.hxx> +#include <osl/diagnose.h> + +namespace sd::slidesorter::view { + +FramePainter::FramePainter (const BitmapEx& rShadowBitmap) + : maTopLeft(rShadowBitmap,-1,-1), + maTop(rShadowBitmap,0,-1), + maTopRight(rShadowBitmap,+1,-1), + maLeft(rShadowBitmap,-1,0), + maRight(rShadowBitmap,+1,0), + maBottomLeft(rShadowBitmap,-1,+1), + maBottom(rShadowBitmap,0,+1), + maBottomRight(rShadowBitmap,+1,+1), + maCenter(rShadowBitmap,0,0), + mbIsValid(false) +{ + if (rShadowBitmap.GetSizePixel().Width() == rShadowBitmap.GetSizePixel().Height() + && (rShadowBitmap.GetSizePixel().Width()-1)%2 == 0 + && ((rShadowBitmap.GetSizePixel().Width()-1)/2)%2 == 1) + { + mbIsValid = true; + } + else + { + OSL_ASSERT(rShadowBitmap.GetSizePixel().Width() == rShadowBitmap.GetSizePixel().Height()); + OSL_ASSERT((rShadowBitmap.GetSizePixel().Width()-1)%2 == 0); + OSL_ASSERT(((rShadowBitmap.GetSizePixel().Width()-1)/2)%2 == 1); + } +} + +FramePainter::~FramePainter() +{ +} + +void FramePainter::PaintFrame ( + OutputDevice& rDevice, + const ::tools::Rectangle& rBox) const +{ + if ( ! mbIsValid) + return; + + // Paint the shadow. + maTopLeft.PaintCorner(rDevice, rBox.TopLeft()); + maTopRight.PaintCorner(rDevice, rBox.TopRight()); + maBottomLeft.PaintCorner(rDevice, rBox.BottomLeft()); + maBottomRight.PaintCorner(rDevice, rBox.BottomRight()); + maLeft.PaintSide(rDevice, rBox.TopLeft(), rBox.BottomLeft(), maTopLeft, maBottomLeft); + maRight.PaintSide(rDevice, rBox.TopRight(), rBox.BottomRight(), maTopRight, maBottomRight); + maTop.PaintSide(rDevice, rBox.TopLeft(), rBox.TopRight(), maTopLeft, maTopRight); + maBottom.PaintSide(rDevice, rBox.BottomLeft(), rBox.BottomRight(), maBottomLeft, maBottomRight); + maCenter.PaintCenter(rDevice,rBox); +} + +void FramePainter::AdaptColor ( + const Color aNewColor) +{ + // Get the source color. + if (maCenter.maBitmap.IsEmpty()) + return; + const Color aSourceColor = maCenter.maBitmap.GetPixelColor(0,0); + + // Erase the center bitmap. + maCenter.maBitmap.SetEmpty(); + + // Replace the color in all bitmaps. + maTopLeft.maBitmap.Replace(aSourceColor, aNewColor); + maTop.maBitmap.Replace(aSourceColor, aNewColor); + maTopRight.maBitmap.Replace(aSourceColor, aNewColor); + maLeft.maBitmap.Replace(aSourceColor, aNewColor); + maCenter.maBitmap.Replace(aSourceColor, aNewColor); + maRight.maBitmap.Replace(aSourceColor, aNewColor); + maBottomLeft.maBitmap.Replace(aSourceColor, aNewColor); + maBottom.maBitmap.Replace(aSourceColor, aNewColor); + maBottomRight.maBitmap.Replace(aSourceColor, aNewColor); +} + +//===== FramePainter::OffsetBitmap ============================================ + +FramePainter::OffsetBitmap::OffsetBitmap ( + const BitmapEx& rBitmap, + const sal_Int32 nHorizontalPosition, + const sal_Int32 nVerticalPosition) +{ + OSL_ASSERT(nHorizontalPosition>=-1 && nHorizontalPosition<=+1); + OSL_ASSERT(nVerticalPosition>=-1 && nVerticalPosition<=+1); + + const sal_Int32 nS (1); + const sal_Int32 nC (::std::max<sal_Int32>(0,(rBitmap.GetSizePixel().Width()-nS)/2)); + const sal_Int32 nO (nC/2); + + const Point aOrigin( + nHorizontalPosition<0 ? 0 : (nHorizontalPosition == 0 ? nC : nC+nS), + nVerticalPosition<0 ? 0 : (nVerticalPosition == 0 ? nC : nC+nS)); + const Size aSize( + nHorizontalPosition==0 ? nS : nC, + nVerticalPosition==0 ? nS : nC); + maBitmap = BitmapEx(rBitmap, aOrigin, aSize); + if (maBitmap.IsEmpty()) + return; + maOffset = Point( + nHorizontalPosition<0 ? -nO : nHorizontalPosition>0 ? -nO : 0, + nVerticalPosition<0 ? -nO : nVerticalPosition>0 ? -nO : 0); + + // Enlarge the side bitmaps so that painting the frame requires less + // paint calls. + const sal_Int32 nSideBitmapSize (64); + if (nHorizontalPosition == 0 && nVerticalPosition == 0) + { + maBitmap.Scale(Size(nSideBitmapSize,nSideBitmapSize)); + } + else if (nHorizontalPosition == 0) + { + maBitmap.Scale(Size(nSideBitmapSize,aSize.Height())); + } + else if (nVerticalPosition == 0) + { + maBitmap.Scale(Size(maBitmap.GetSizePixel().Width(), nSideBitmapSize)); + } +} + +void FramePainter::OffsetBitmap::PaintCorner ( + OutputDevice& rDevice, + const Point& rAnchor) const +{ + if ( ! maBitmap.IsEmpty()) + rDevice.DrawBitmapEx(rAnchor+maOffset, maBitmap); +} + +void FramePainter::OffsetBitmap::PaintSide ( + OutputDevice& rDevice, + const Point& rAnchor1, + const Point& rAnchor2, + const OffsetBitmap& rCornerBitmap1, + const OffsetBitmap& rCornerBitmap2) const +{ + if (maBitmap.IsEmpty()) + return; + + const Size aBitmapSize (maBitmap.GetSizePixel()); + if (rAnchor1.Y() == rAnchor2.Y()) + { + // Side is horizontal. + const sal_Int32 nY (rAnchor1.Y() + maOffset.Y()); + const sal_Int32 nLeft ( + rAnchor1.X() + + rCornerBitmap1.maBitmap.GetSizePixel().Width() + + rCornerBitmap1.maOffset.X()); + const sal_Int32 nRight ( + rAnchor2.X() + + rCornerBitmap2.maOffset.X() + - 1); + for (sal_Int32 nX=nLeft; nX<=nRight; nX+=aBitmapSize.Width()) + { + rDevice.DrawBitmapEx( + Point(nX,nY), + Size(std::min(aBitmapSize.Width(),static_cast<::tools::Long>(nRight-nX+1)),aBitmapSize.Height()), + maBitmap); + } + } + else if (rAnchor1.X() == rAnchor2.X()) + { + // Side is vertical. + const sal_Int32 nX (rAnchor1.X() + maOffset.X()); + const sal_Int32 nTop ( + rAnchor1.Y() + + rCornerBitmap1.maBitmap.GetSizePixel().Height() + + rCornerBitmap1.maOffset.Y()); + const sal_Int32 nBottom ( + rAnchor2.Y() + + rCornerBitmap2.maOffset.Y() + - 1); + for (sal_Int32 nY=nTop; nY<=nBottom; nY+=aBitmapSize.Height()) + { + rDevice.DrawBitmapEx( + Point(nX,nY), + Size(aBitmapSize.Width(), std::min(aBitmapSize.Height(), static_cast<::tools::Long>(nBottom-nY+1))), + maBitmap); + } + } + else + { + // Diagonal sides indicates an error. + OSL_ASSERT(false); + } +} + +void FramePainter::OffsetBitmap::PaintCenter ( + OutputDevice& rDevice, + const ::tools::Rectangle& rBox) const +{ + const Size aBitmapSize (maBitmap.GetSizePixel()); + for (::tools::Long nY=rBox.Top(); nY<=rBox.Bottom(); nY+=aBitmapSize.Height()) + for (::tools::Long nX=rBox.Left(); nX<=rBox.Right(); nX+=aBitmapSize.Width()) + rDevice.DrawBitmapEx( + Point(nX,nY), + Size( + ::std::min(aBitmapSize.Width(), rBox.Right()-nX+1), + std::min(aBitmapSize.Height(), rBox.Bottom()-nY+1)), + maBitmap); +} + +} // end of namespace sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/view/SlsFramePainter.hxx b/sd/source/ui/slidesorter/view/SlsFramePainter.hxx new file mode 100644 index 000000000..9398cb94e --- /dev/null +++ b/sd/source/ui/slidesorter/view/SlsFramePainter.hxx @@ -0,0 +1,109 @@ +/* -*- 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 . + */ + +#pragma once + +#include <vcl/bitmapex.hxx> + +namespace sd::slidesorter::view { + +class FramePainter +{ +public: + explicit FramePainter (const BitmapEx& rBitmap); + ~FramePainter(); + + /** Paint a border around the given box by using a set of bitmaps for + the corners and sides. + */ + void PaintFrame (OutputDevice&rDevice, const ::tools::Rectangle& rBox) const; + + /** Special functionality that takes the color from the center + bitmap and replaces that color in all bitmaps by the given new + color. Alpha values are not modified. + The center bitmap is erased. + */ + void AdaptColor (const Color aNewColor); + +private: + /** Bitmap with offset that is used when the bitmap is painted. The bitmap + */ + class OffsetBitmap { + public: + BitmapEx maBitmap; + Point maOffset; + + /** Create one of the eight shadow bitmaps from one that combines + them all. This larger bitmap is expected to have dimension NxN + with N=1+2*M. Of this larger bitmap there are created four + corner bitmaps of size 2*M x 2*M and four side bitmaps of sizes + 1xM (top and bottom) and Mx1 (left and right). The corner + bitmaps have each one quadrant of size MxM that is painted under + the interior of the frame. + @param rBitmap + The larger bitmap of which the eight shadow bitmaps are cut + out from. + @param nHorizontalPosition + Valid values are -1 (left), 0 (center), and +1 (right). + @param nVerticalPosition + Valid values are -1 (top), 0 (center), and +1 (bottom). + */ + OffsetBitmap ( + const BitmapEx& rBitmap, + const sal_Int32 nHorizontalPosition, + const sal_Int32 nVerticalPosition); + + /** Use the given device to paint the bitmap at the location that is + the sum of the given anchor and the internal offset. + */ + void PaintCorner (OutputDevice& rDevice, const Point& rAnchor) const; + + /** Use the given device to paint the bitmap stretched between the + two given locations. Offsets of the adjacent corner bitmaps and + the offset of the side bitmap are used to determine the area + that is to be filled with the side bitmap. + */ + void PaintSide ( + OutputDevice& rDevice, + const Point& rAnchor1, + const Point& rAnchor2, + const OffsetBitmap& rCornerBitmap1, + const OffsetBitmap& rCornerBitmap2) const; + + /** Fill the given rectangle with the bitmap. + */ + void PaintCenter ( + OutputDevice& rDevice, + const ::tools::Rectangle& rBox) const; + }; + OffsetBitmap maTopLeft; + OffsetBitmap maTop; + OffsetBitmap maTopRight; + OffsetBitmap maLeft; + OffsetBitmap maRight; + OffsetBitmap maBottomLeft; + OffsetBitmap maBottom; + OffsetBitmap maBottomRight; + OffsetBitmap maCenter; + bool mbIsValid; +}; + +} // end of namespace sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/view/SlsInsertAnimator.cxx b/sd/source/ui/slidesorter/view/SlsInsertAnimator.cxx new file mode 100644 index 000000000..361c55f05 --- /dev/null +++ b/sd/source/ui/slidesorter/view/SlsInsertAnimator.cxx @@ -0,0 +1,428 @@ +/* -*- 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 <view/SlsInsertAnimator.hxx> +#include <controller/SlideSorterController.hxx> +#include <controller/SlsAnimationFunction.hxx> +#include <view/SlideSorterView.hxx> +#include <view/SlsLayouter.hxx> +#include <model/SlideSorterModel.hxx> +#include <SlideSorter.hxx> +#include <Window.hxx> +#include <osl/diagnose.h> + +#include <memory> +#include <set> + +namespace sd::slidesorter::view { + +namespace { + +class PageObjectRun; + +class AnimatorAccess +{ +public: + virtual void AddRun (const std::shared_ptr<PageObjectRun>& rRun) = 0; + virtual void RemoveRun (const std::shared_ptr<PageObjectRun>& rRun) = 0; + virtual model::SlideSorterModel& GetModel () const = 0; + virtual view::SlideSorterView& GetView () const = 0; + virtual std::shared_ptr<controller::Animator> GetAnimator () = 0; + virtual VclPtr<sd::Window> GetContentWindow () = 0; + +protected: + ~AnimatorAccess() COVERITY_NOEXCEPT_FALSE {} +}; + +/** Controller of the position offsets of all page objects in one row or one + column. +*/ +class PageObjectRun : public std::enable_shared_from_this<PageObjectRun> +{ +public: + PageObjectRun ( + AnimatorAccess& rAnimatorAccess, + const sal_Int32 nRunIndex, + const sal_Int32 nStartIndex, + const sal_Int32 nEndIndex); + + void operator () (const double nTime); + + void UpdateOffsets( + const InsertPosition& rInsertPosition, + const view::Layouter& GetLayouter); + void ResetOffsets (const controller::Animator::AnimationMode eMode); + + /// Index of the row or column that this run represents. + sal_Int32 mnRunIndex; + /// The index at which to make place for the insertion indicator (-1 for + /// no indicator). + sal_Int32 mnLocalInsertIndex; + /// Index of the first page in the run. + sal_Int32 mnStartIndex; + /// Index of the last page in the run. + sal_Int32 mnEndIndex; + /// Offset of each item in the run at the start of the current animation. + ::std::vector<Point> maStartOffset; + /// Target offset of each item in the run at the end of the current animation. + ::std::vector<Point> maEndOffset; + /// Time at which the current animation started. + double mnStartTime; + + class Comparator + { + public: bool operator() ( + const std::shared_ptr<PageObjectRun>& rpRunA, + const std::shared_ptr<PageObjectRun>& rpRunB) const + { + return rpRunA->mnRunIndex < rpRunB->mnRunIndex; + } + }; +private: + controller::Animator::AnimationId mnAnimationId; + AnimatorAccess& mrAnimatorAccess; + ::std::function<double (double)> maAccelerationFunction; + + void RestartAnimation(); +}; +typedef std::shared_ptr<PageObjectRun> SharedPageObjectRun; + +Point Blend (const Point& rPointA, const Point& rPointB, const double nT) +{ + return Point( + sal_Int32(rPointA.X() * (1-nT) + rPointB.X() * nT), + sal_Int32(rPointA.Y() * (1-nT) + rPointB.Y() * nT)); +} + +} // end of anonymous namespace + +class InsertAnimator::Implementation : public AnimatorAccess +{ +public: + explicit Implementation (SlideSorter& rSlideSorter); + virtual ~Implementation(); + + void SetInsertPosition ( + const InsertPosition& rInsertPosition, + const controller::Animator::AnimationMode eAnimationMode); + + virtual void AddRun (const std::shared_ptr<PageObjectRun>& rRun) override; + virtual void RemoveRun (const std::shared_ptr<PageObjectRun>& rRun) override; + + virtual model::SlideSorterModel& GetModel() const override { return mrModel; } + virtual view::SlideSorterView& GetView() const override { return mrView; } + virtual std::shared_ptr<controller::Animator> GetAnimator() override { return mpAnimator; } + virtual VclPtr<sd::Window> GetContentWindow() override { return mrSlideSorter.GetContentWindow(); } + +private: + model::SlideSorterModel& mrModel; + view::SlideSorterView& mrView; + SlideSorter& mrSlideSorter; + std::shared_ptr<controller::Animator> mpAnimator; + typedef ::std::set<SharedPageObjectRun, PageObjectRun::Comparator> RunContainer; + RunContainer maRuns; + InsertPosition maInsertPosition; + + SharedPageObjectRun GetRun ( + view::Layouter const & rLayouter, + const InsertPosition& rInsertPosition); + RunContainer::const_iterator FindRun (const sal_Int32 nRunIndex) const; +}; + +//===== InsertAnimator ======================================================== + +InsertAnimator::InsertAnimator (SlideSorter& rSlideSorter) + : mpImplementation(std::make_shared<Implementation>(rSlideSorter)) +{ +} + +void InsertAnimator::SetInsertPosition (const InsertPosition& rInsertPosition) +{ + mpImplementation->SetInsertPosition(rInsertPosition, controller::Animator::AM_Animated); +} + +void InsertAnimator::Reset (const controller::Animator::AnimationMode eMode) +{ + mpImplementation->SetInsertPosition(InsertPosition(), eMode); +} + +//===== InsertAnimator::Implementation ======================================== + +InsertAnimator::Implementation::Implementation (SlideSorter& rSlideSorter) + : mrModel(rSlideSorter.GetModel()), + mrView(rSlideSorter.GetView()), + mrSlideSorter(rSlideSorter), + mpAnimator(rSlideSorter.GetController().GetAnimator()) +{ +} + +InsertAnimator::Implementation::~Implementation() +{ + SetInsertPosition(InsertPosition(), controller::Animator::AM_Immediate); +} + +void InsertAnimator::Implementation::SetInsertPosition ( + const InsertPosition& rInsertPosition, + const controller::Animator::AnimationMode eMode) +{ + if (maInsertPosition == rInsertPosition) + return; + + SharedPageObjectRun pOldRun (GetRun(mrView.GetLayouter(), maInsertPosition)); + SharedPageObjectRun pCurrentRun (GetRun(mrView.GetLayouter(), rInsertPosition)); + maInsertPosition = rInsertPosition; + + // When the new insert position is in a different run then move the page + // objects in the old run to their default positions. + if (pOldRun != pCurrentRun && pOldRun) + pOldRun->ResetOffsets(eMode); + + if (pCurrentRun) + { + pCurrentRun->UpdateOffsets(rInsertPosition, mrView.GetLayouter()); + } +} + +SharedPageObjectRun InsertAnimator::Implementation::GetRun ( + view::Layouter const & rLayouter, + const InsertPosition& rInsertPosition) +{ + const sal_Int32 nRow (rInsertPosition.GetRow()); + if (nRow < 0) + return SharedPageObjectRun(); + + RunContainer::const_iterator iRun (maRuns.end()); + if (rLayouter.GetColumnCount() == 1) + { + // There is only one run that contains all slides. + if (maRuns.empty()) + maRuns.insert(std::make_shared<PageObjectRun>( + *this, + 0, + 0, + mrModel.GetPageCount()-1)); + iRun = maRuns.begin(); + } + else + { + iRun = FindRun(nRow); + if (iRun == maRuns.end()) + { + // Create a new run. + const sal_Int32 nStartIndex (rLayouter.GetIndex(nRow, 0)); + const sal_Int32 nEndIndex (rLayouter.GetIndex(nRow, rLayouter.GetColumnCount()-1)); + if (nStartIndex <= nEndIndex) + { + iRun = maRuns.insert(std::make_shared<PageObjectRun>( + *this, + nRow, + nStartIndex, + nEndIndex)).first; + OSL_ASSERT(iRun != maRuns.end()); + } + } + } + + if (iRun != maRuns.end()) + return *iRun; + else + return SharedPageObjectRun(); +} + +InsertAnimator::Implementation::RunContainer::const_iterator + InsertAnimator::Implementation::FindRun (const sal_Int32 nRunIndex) const +{ + return std::find_if( + maRuns.begin(), + maRuns.end(), + [nRunIndex] (std::shared_ptr<PageObjectRun> const& rRun) + { return rRun->mnRunIndex == nRunIndex; }); +} + +void InsertAnimator::Implementation::AddRun (const std::shared_ptr<PageObjectRun>& rRun) +{ + if (rRun) + { + maRuns.insert(rRun); + } + else + { + OSL_ASSERT(rRun); + } +} + +void InsertAnimator::Implementation::RemoveRun (const std::shared_ptr<PageObjectRun>& rRun) +{ + if (rRun) + { + // Do not remove runs that show the space for the insertion indicator. + if (rRun->mnLocalInsertIndex == -1) + { + InsertAnimator::Implementation::RunContainer::const_iterator iRun (FindRun(rRun->mnRunIndex)); + if (iRun != maRuns.end()) + { + OSL_ASSERT(*iRun == rRun); + maRuns.erase(iRun); + } + } + } + else + { + OSL_ASSERT(rRun); + } +} + +//===== PageObjectRun ========================================================= + +PageObjectRun::PageObjectRun ( + AnimatorAccess& rAnimatorAccess, + const sal_Int32 nRunIndex, + const sal_Int32 nStartIndex, + const sal_Int32 nEndIndex) + : mnRunIndex(nRunIndex), + mnLocalInsertIndex(-1), + mnStartIndex(nStartIndex), + mnEndIndex(nEndIndex), + mnStartTime(-1), + mnAnimationId(controller::Animator::NotAnAnimationId), + mrAnimatorAccess(rAnimatorAccess), + maAccelerationFunction( + controller::AnimationParametricFunction( + controller::AnimationBezierFunction (0.1,0.7))) +{ + maStartOffset.resize(nEndIndex - nStartIndex + 1); + maEndOffset.resize(nEndIndex - nStartIndex + 1); +} + +void PageObjectRun::UpdateOffsets( + const InsertPosition& rInsertPosition, + const view::Layouter& rLayouter) +{ + const bool bIsVertical (rLayouter.GetColumnCount()==1); + const sal_Int32 nLocalInsertIndex(bIsVertical + ? rInsertPosition.GetRow() + : rInsertPosition.GetColumn()); + if (nLocalInsertIndex == mnLocalInsertIndex) + return; + + mnLocalInsertIndex = nLocalInsertIndex; + + model::SlideSorterModel& rModel (mrAnimatorAccess.GetModel()); + const sal_Int32 nRunLength (mnEndIndex - mnStartIndex + 1); + for (sal_Int32 nIndex=0; nIndex<nRunLength; ++nIndex) + { + model::SharedPageDescriptor pDescriptor(rModel.GetPageDescriptor(nIndex+mnStartIndex)); + if (pDescriptor) + maStartOffset[nIndex] = pDescriptor->GetVisualState().GetLocationOffset(); + maEndOffset[nIndex] = nIndex < mnLocalInsertIndex + ? rInsertPosition.GetLeadingOffset() + : rInsertPosition.GetTrailingOffset(); + if (bIsVertical) + maEndOffset[nIndex].setX( 0 ); + else + maEndOffset[nIndex].setY( 0 ); + } + RestartAnimation(); +} + +void PageObjectRun::ResetOffsets (const controller::Animator::AnimationMode eMode) +{ + mnLocalInsertIndex = -1; + const sal_Int32 nRunLength (mnEndIndex - mnStartIndex + 1); + model::SlideSorterModel& rModel (mrAnimatorAccess.GetModel()); + view::SlideSorterView& rView (mrAnimatorAccess.GetView()); + for (sal_Int32 nIndex=0; nIndex<nRunLength; ++nIndex) + { + model::SharedPageDescriptor pDescriptor(rModel.GetPageDescriptor(nIndex+mnStartIndex)); + if (pDescriptor) + { + if (eMode == controller::Animator::AM_Animated) + maStartOffset[nIndex] = pDescriptor->GetVisualState().GetLocationOffset(); + else + { + const ::tools::Rectangle aOldBoundingBox (pDescriptor->GetBoundingBox()); + pDescriptor->GetVisualState().SetLocationOffset(Point(0,0)); + rView.RequestRepaint(aOldBoundingBox); + rView.RequestRepaint(pDescriptor); + } + } + maEndOffset[nIndex] = Point(0,0); + } + if (eMode == controller::Animator::AM_Animated) + RestartAnimation(); + else + mrAnimatorAccess.RemoveRun(shared_from_this()); +} + +void PageObjectRun::RestartAnimation() +{ + // Stop the current animation. + if (mnAnimationId != controller::Animator::NotAnAnimationId) + { + mrAnimatorAccess.GetAnimator()->RemoveAnimation(mnAnimationId); + } + + // Restart the animation. + mrAnimatorAccess.AddRun(shared_from_this()); + auto sharedThis(shared_from_this()); + mnAnimationId = mrAnimatorAccess.GetAnimator()->AddAnimation( + [this] (double const val) { (*this)(val); }, + [sharedThis] () { sharedThis->mrAnimatorAccess.RemoveRun(sharedThis); } + ); +} + +void PageObjectRun::operator () (const double nGlobalTime) +{ + if (mnStartTime < 0) + mnStartTime = nGlobalTime; + + double nLocalTime (nGlobalTime - mnStartTime); + if (nLocalTime > 1.0) + nLocalTime = 1.0; + nLocalTime = maAccelerationFunction(nLocalTime); + + model::SlideSorterModel& rModel (mrAnimatorAccess.GetModel()); + view::SlideSorterView& rView (mrAnimatorAccess.GetView()); + for (sal_Int32 nIndex=mnStartIndex; nIndex<=mnEndIndex; ++nIndex) + { + model::SharedPageDescriptor pDescriptor (rModel.GetPageDescriptor(nIndex)); + if ( ! pDescriptor) + continue; + const ::tools::Rectangle aOldBoundingBox (pDescriptor->GetBoundingBox()); + pDescriptor->GetVisualState().SetLocationOffset( + Blend( + maStartOffset[nIndex-mnStartIndex], + maEndOffset[nIndex-mnStartIndex], + nLocalTime)); + + // Request a repaint of the old and new bounding box (which largely overlap.) + rView.RequestRepaint(aOldBoundingBox); + rView.RequestRepaint(pDescriptor); + } + + // Call Flush to make + // a) animations a bit more smooth and + // b) on Mac without the Flush a Reset of the page locations is not properly + // visualized when the mouse leaves the window during drag-and-drop. + mrAnimatorAccess.GetContentWindow()->GetOutDev()->Flush(); +} + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/view/SlsInsertionIndicatorOverlay.cxx b/sd/source/ui/slidesorter/view/SlsInsertionIndicatorOverlay.cxx new file mode 100644 index 000000000..c1eb0ea90 --- /dev/null +++ b/sd/source/ui/slidesorter/view/SlsInsertionIndicatorOverlay.cxx @@ -0,0 +1,360 @@ +/* -*- 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 <view/SlsInsertionIndicatorOverlay.hxx> + +#include <SlideSorter.hxx> +#include <view/SlideSorterView.hxx> +#include <view/SlsLayouter.hxx> +#include <view/SlsPageObjectLayouter.hxx> +#include <view/SlsTheme.hxx> +#include "SlsFramePainter.hxx" +#include "SlsLayeredDevice.hxx" +#include <DrawDocShell.hxx> +#include <drawdoc.hxx> +#include <Window.hxx> + +#include <o3tl/safeint.hxx> +#include <rtl/math.hxx> +#include <vcl/virdev.hxx> +#include <basegfx/range/b2drectangle.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> + +namespace { + +const double gnPreviewOffsetScale = 1.0 / 8.0; + +::tools::Rectangle GrowRectangle (const ::tools::Rectangle& rBox, const sal_Int32 nOffset) +{ + return ::tools::Rectangle ( + rBox.Left() - nOffset, + rBox.Top() - nOffset, + rBox.Right() + nOffset, + rBox.Bottom() + nOffset); +} + +sal_Int32 RoundToInt (const double nValue) { return sal_Int32(::rtl::math::round(nValue)); } + +} // end of anonymous namespace + +namespace sd::slidesorter::view { + +//===== InsertionIndicatorOverlay =========================================== + +const sal_Int32 gnShadowBorder = 3; +const sal_Int32 gnLayerIndex = 2; + +InsertionIndicatorOverlay::InsertionIndicatorOverlay (SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter), + mbIsVisible(false), + mpShadowPainter( + new FramePainter(mrSlideSorter.GetTheme()->GetIcon(Theme::Icon_RawInsertShadow))) +{ +} + +InsertionIndicatorOverlay::~InsertionIndicatorOverlay() +{ + // cid#1491947 silence Uncaught exception + suppress_fun_call_w_exception(Hide()); +} + +void InsertionIndicatorOverlay::Create (const SdTransferable* pTransferable) +{ + if (pTransferable == nullptr) + return; + + std::shared_ptr<controller::TransferableData> pData ( + controller::TransferableData::GetFromTransferable(pTransferable)); + if ( ! pData) + return; + sal_Int32 nSelectionCount (0); + if (pTransferable->HasPageBookmarks()) + nSelectionCount = pTransferable->GetPageBookmarks().size(); + else + { + DrawDocShell* pDataDocShell = dynamic_cast<DrawDocShell*>(pTransferable->GetDocShell().get()); + if (pDataDocShell != nullptr) + { + SdDrawDocument* pDataDocument = pDataDocShell->GetDoc(); + if (pDataDocument != nullptr) + nSelectionCount = pDataDocument->GetSdPageCount(PageKind::Standard); + } + } + Create(pData->GetRepresentatives(), nSelectionCount); +} + +void InsertionIndicatorOverlay::Create ( + const ::std::vector<controller::TransferableData::Representative>& rRepresentatives, + const sal_Int32 nSelectionCount) +{ + view::Layouter& rLayouter (mrSlideSorter.GetView().GetLayouter()); + const std::shared_ptr<view::PageObjectLayouter>& pPageObjectLayouter ( + rLayouter.GetPageObjectLayouter()); + std::shared_ptr<view::Theme> pTheme (mrSlideSorter.GetTheme()); + const Size aOriginalPreviewSize (pPageObjectLayouter->GetPreviewSize()); + + const double nPreviewScale (0.5); + const Size aPreviewSize ( + RoundToInt(aOriginalPreviewSize.Width()*nPreviewScale), + RoundToInt(aOriginalPreviewSize.Height()*nPreviewScale)); + const sal_Int32 nOffset ( + RoundToInt(std::min(aPreviewSize.Width(),aPreviewSize.Height()) * gnPreviewOffsetScale)); + + // Determine size and offset depending on the number of previews. + sal_Int32 nCount (rRepresentatives.size()); + if (nCount > 0) + --nCount; + Size aIconSize( + aPreviewSize.Width() + 2 * gnShadowBorder + nCount*nOffset, + aPreviewSize.Height() + 2 * gnShadowBorder + nCount*nOffset); + + // Create virtual devices for bitmap and mask whose bitmaps later be + // combined to form the BitmapEx of the icon. + ScopedVclPtrInstance<VirtualDevice> pContent( + *mrSlideSorter.GetContentWindow()->GetOutDev(), DeviceFormat::DEFAULT, DeviceFormat::DEFAULT); + pContent->SetOutputSizePixel(aIconSize); + + pContent->SetFillColor(); + pContent->SetLineColor(pTheme->GetColor(Theme::Color_PreviewBorder)); + const Point aOffset = PaintRepresentatives(*pContent, aPreviewSize, nOffset, rRepresentatives); + + PaintPageCount(*pContent, nSelectionCount, aPreviewSize, aOffset); + + maIcon = pContent->GetBitmapEx(Point(0,0), aIconSize); + maIcon.Scale(aIconSize); +} + +Point InsertionIndicatorOverlay::PaintRepresentatives ( + OutputDevice& rContent, + const Size& rPreviewSize, + const sal_Int32 nOffset, + const ::std::vector<controller::TransferableData::Representative>& rRepresentatives) const +{ + const Point aOffset (0,rRepresentatives.size()==1 ? -nOffset : 0); + + // Paint the pages. + Point aPageOffset (0,0); + double nTransparency (0); + const BitmapEx aExclusionOverlay (mrSlideSorter.GetTheme()->GetIcon(Theme::Icon_HideSlideOverlay)); + for (sal_Int32 nIndex=2; nIndex>=0; --nIndex) + { + if (rRepresentatives.size() <= o3tl::make_unsigned(nIndex)) + continue; + switch(nIndex) + { + case 0 : + aPageOffset = Point(0, nOffset); + nTransparency = 0.85; + break; + case 1: + aPageOffset = Point(nOffset, 0); + nTransparency = 0.75; + break; + case 2: + aPageOffset = Point(2*nOffset, 2*nOffset); + nTransparency = 0.65; + break; + } + aPageOffset += aOffset; + aPageOffset.AdjustX(gnShadowBorder ); + aPageOffset.AdjustY(gnShadowBorder ); + + // Paint the preview. + BitmapEx aPreview (rRepresentatives[nIndex].maBitmap); + aPreview.Scale(rPreviewSize, BmpScaleFlag::BestQuality); + rContent.DrawBitmapEx(aPageOffset, aPreview); + + // When the page is marked as excluded from the slide show then + // paint an overlay that visualizes this. + if (rRepresentatives[nIndex].mbIsExcluded) + { + const vcl::Region aSavedClipRegion (rContent.GetClipRegion()); + rContent.IntersectClipRegion(::tools::Rectangle(aPageOffset, rPreviewSize)); + // Paint bitmap tiled over the preview to mark it as excluded. + const sal_Int32 nIconWidth (aExclusionOverlay.GetSizePixel().Width()); + const sal_Int32 nIconHeight (aExclusionOverlay.GetSizePixel().Height()); + if (nIconWidth>0 && nIconHeight>0) + { + for (::tools::Long nX=0; nX<rPreviewSize.Width(); nX+=nIconWidth) + for (::tools::Long nY=0; nY<rPreviewSize.Height(); nY+=nIconHeight) + rContent.DrawBitmapEx(Point(nX,nY)+aPageOffset, aExclusionOverlay); + } + rContent.SetClipRegion(aSavedClipRegion); + } + + // Tone down the bitmap. The further back the darker it becomes. + ::tools::Rectangle aBox ( + aPageOffset.X(), + aPageOffset.Y(), + aPageOffset.X()+rPreviewSize.Width()-1, + aPageOffset.Y()+rPreviewSize.Height()-1); + rContent.SetFillColor(COL_BLACK); + rContent.SetLineColor(); + rContent.DrawTransparent( + basegfx::B2DHomMatrix(), + ::basegfx::B2DPolyPolygon(::basegfx::utils::createPolygonFromRect( + ::basegfx::B2DRectangle(aBox.Left(), aBox.Top(), aBox.Right()+1, aBox.Bottom()+1), + 0, + 0)), + nTransparency); + + // Draw border around preview. + ::tools::Rectangle aBorderBox (GrowRectangle(aBox, 1)); + rContent.SetLineColor(COL_GRAY); + rContent.SetFillColor(); + rContent.DrawRect(aBorderBox); + + // Draw shadow around preview. + mpShadowPainter->PaintFrame(rContent, aBorderBox); + } + + return aPageOffset; +} + +void InsertionIndicatorOverlay::PaintPageCount ( + OutputDevice& rDevice, + const sal_Int32 nSelectionCount, + const Size& rPreviewSize, + const Point& rFirstPageOffset) const +{ + // Paint the number of slides. + std::shared_ptr<view::Theme> pTheme (mrSlideSorter.GetTheme()); + std::shared_ptr<vcl::Font> pFont(Theme::GetFont(Theme::Font_PageCount, rDevice)); + if (!pFont) + return; + + OUString sNumber (OUString::number(nSelectionCount)); + + // Determine the size of the (painted) text and create a bounding + // box that centers the text on the first preview. + rDevice.SetFont(*pFont); + ::tools::Rectangle aTextBox; + rDevice.GetTextBoundRect(aTextBox, sNumber); + Point aTextOffset (aTextBox.TopLeft()); + Size aTextSize (aTextBox.GetSize()); + // Place text inside the first page preview. + Point aTextLocation(rFirstPageOffset); + // Center the text. + aTextLocation += Point( + (rPreviewSize.Width()-aTextBox.GetWidth())/2, + (rPreviewSize.Height()-aTextBox.GetHeight())/2); + aTextBox = ::tools::Rectangle(aTextLocation, aTextSize); + + // Paint background, border and text. + static const sal_Int32 nBorder = 5; + rDevice.SetFillColor(pTheme->GetColor(Theme::Color_Selection)); + rDevice.SetLineColor(pTheme->GetColor(Theme::Color_Selection)); + rDevice.DrawRect(GrowRectangle(aTextBox, nBorder)); + + rDevice.SetFillColor(); + rDevice.SetLineColor(pTheme->GetColor(Theme::Color_PageCountFontColor)); + rDevice.DrawRect(GrowRectangle(aTextBox, nBorder-1)); + + rDevice.SetTextColor(pTheme->GetColor(Theme::Color_PageCountFontColor)); + rDevice.DrawText(aTextBox.TopLeft()-aTextOffset, sNumber); +} + +void InsertionIndicatorOverlay::SetLocation (const Point& rLocation) +{ + const Point aTopLeft ( + rLocation - Point( + maIcon.GetSizePixel().Width()/2, + maIcon.GetSizePixel().Height()/2)); + if (maLocation != aTopLeft) + { + const ::tools::Rectangle aOldBoundingBox (GetBoundingBox()); + + maLocation = aTopLeft; + + if (mpLayerInvalidator && IsVisible()) + { + mpLayerInvalidator->Invalidate(aOldBoundingBox); + mpLayerInvalidator->Invalidate(GetBoundingBox()); + } + } +} + +void InsertionIndicatorOverlay::Paint ( + OutputDevice& rDevice, + const ::tools::Rectangle&) +{ + if ( ! IsVisible()) + return; + + rDevice.DrawImage(maLocation, Image(maIcon)); +} + +void InsertionIndicatorOverlay::SetLayerInvalidator (const SharedILayerInvalidator& rpInvalidator) +{ + mpLayerInvalidator = rpInvalidator; + + if (mbIsVisible && mpLayerInvalidator) + mpLayerInvalidator->Invalidate(GetBoundingBox()); +} + +void InsertionIndicatorOverlay::Show() +{ + if ( mbIsVisible) + return; + + mbIsVisible = true; + + std::shared_ptr<LayeredDevice> pLayeredDevice ( + mrSlideSorter.GetView().GetLayeredDevice()); + if (pLayeredDevice) + { + pLayeredDevice->RegisterPainter(shared_from_this(), gnLayerIndex); + if (mpLayerInvalidator) + mpLayerInvalidator->Invalidate(GetBoundingBox()); + } +} + +void InsertionIndicatorOverlay::Hide() +{ + if (!mbIsVisible) + return; + + mbIsVisible = false; + + std::shared_ptr<LayeredDevice> pLayeredDevice ( + mrSlideSorter.GetView().GetLayeredDevice()); + if (pLayeredDevice) + { + if (mpLayerInvalidator) + mpLayerInvalidator->Invalidate(GetBoundingBox()); + pLayeredDevice->RemovePainter(shared_from_this(), gnLayerIndex); + } +} + +::tools::Rectangle InsertionIndicatorOverlay::GetBoundingBox() const +{ + return ::tools::Rectangle(maLocation, maIcon.GetSizePixel()); +} + +Size InsertionIndicatorOverlay::GetSize() const +{ + return Size( + maIcon.GetSizePixel().Width() + 10, + maIcon.GetSizePixel().Height() + 10); +} + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/view/SlsLayeredDevice.cxx b/sd/source/ui/slidesorter/view/SlsLayeredDevice.cxx new file mode 100644 index 000000000..b41bbe307 --- /dev/null +++ b/sd/source/ui/slidesorter/view/SlsLayeredDevice.cxx @@ -0,0 +1,491 @@ +/* -*- 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 "SlsLayeredDevice.hxx" +#include <Window.hxx> + +#include <vcl/virdev.hxx> +#include <sal/log.hxx> +#include <o3tl/safeint.hxx> +#include <osl/diagnose.h> + +#include <tools/gen.hxx> +#include <tools/fract.hxx> + +#include <functional> + +namespace sd::slidesorter::view { + +namespace { +const sal_Int32 gnMaximumLayerCount = 8; + +class LayerInvalidator : public ILayerInvalidator +{ +public: + LayerInvalidator ( + const std::shared_ptr<LayeredDevice>& rpLayeredDevice, + sd::Window *pTargetWindow, + const int nLayer) + : mpLayeredDevice(rpLayeredDevice), + mpTargetWindow(pTargetWindow), + mnLayer(nLayer) + { + } + + virtual void Invalidate (const ::tools::Rectangle& rInvalidationBox) override + { + mpLayeredDevice->Invalidate(rInvalidationBox, mnLayer); + mpTargetWindow->Invalidate(rInvalidationBox); + } + +private: + const std::shared_ptr<LayeredDevice> mpLayeredDevice; + VclPtr<sd::Window> mpTargetWindow; + const int mnLayer; +}; + +void DeviceCopy ( + vcl::RenderContext& rTargetDevice, + vcl::RenderContext const & rSourceDevice, + const ::tools::Rectangle& rBox) +{ + rTargetDevice.DrawOutDev( + rBox.TopLeft(), + rBox.GetSize(), + rBox.TopLeft(), + rBox.GetSize(), + rSourceDevice); +} + +void ForAllRectangles (const vcl::Region& rRegion, const std::function<void (const ::tools::Rectangle&)>& aFunction) +{ + OSL_ASSERT(aFunction); + RectangleVector aRectangles; + rRegion.GetRegionRectangles(aRectangles); + + if(aRectangles.empty()) + { + aFunction(::tools::Rectangle()); + } + else + { + for(const auto& rRect : aRectangles) + { + aFunction(rRect); + } + + //Region aMutableRegionCopy (rRegion); + //RegionHandle aHandle(aMutableRegionCopy.BeginEnumRects()); + //Rectangle aBox; + //while (aMutableRegionCopy.GetEnumRects(aHandle, aBox)) + // aFunction(aBox); + //aMutableRegionCopy.EndEnumRects(aHandle); + } +} + +class Layer +{ +public: + Layer(); + Layer(const Layer&) = delete; + Layer& operator=(const Layer&) = delete; + + void Initialize (sd::Window *pTargetWindow); + void InvalidateRectangle (const ::tools::Rectangle& rInvalidationBox); + void InvalidateRegion (const vcl::Region& rInvalidationRegion); + void Validate (const MapMode& rMapMode); + void Repaint ( + OutputDevice& rTargetDevice, + const ::tools::Rectangle& rRepaintRectangle); + void Resize (const Size& rSize); + void AddPainter (const SharedILayerPainter& rpPainter); + void RemovePainter (const SharedILayerPainter& rpPainter); + bool HasPainter() const; + void Dispose(); + +private: + ScopedVclPtr<VirtualDevice> mpLayerDevice; + ::std::vector<SharedILayerPainter> maPainters; + vcl::Region maInvalidationRegion; + + void ValidateRectangle (const ::tools::Rectangle& rBox); +}; +typedef std::shared_ptr<Layer> SharedLayer; + +} // end of anonymous namespace + +class LayeredDevice::LayerContainer +{ +public: + LayerContainer() {} + + bool empty() const { return mvLayers.empty(); } + + size_t size() const { return mvLayers.size(); } + + const SharedLayer& back() const { return mvLayers.back(); } + + ::std::vector<SharedLayer>::const_iterator begin() const { return mvLayers.begin(); } + ::std::vector<SharedLayer>::const_iterator end() const { return mvLayers.end(); } + + void clear() { mvLayers.clear(); } + + void pop_back() { mvLayers.pop_back(); } + + void resize(size_t n) { mvLayers.resize(n); } + + SharedLayer& operator[](size_t i) { return mvLayers[i]; } + +private: + ::std::vector<SharedLayer> mvLayers; +}; + +//===== LayeredDevice ========================================================= + +LayeredDevice::LayeredDevice (const VclPtr<sd::Window>& pTargetWindow) + : mpTargetWindow(pTargetWindow), + mpLayers(new LayerContainer()), + mpBackBuffer(VclPtr<VirtualDevice>::Create(*mpTargetWindow->GetOutDev())), + maSavedMapMode(pTargetWindow->GetMapMode()) +{ + mpBackBuffer->SetOutputSizePixel(mpTargetWindow->GetSizePixel()); +} + +LayeredDevice::~LayeredDevice() +{ +} + +void LayeredDevice::Invalidate ( + const ::tools::Rectangle& rInvalidationArea, + const sal_Int32 nLayer) +{ + if (nLayer<0 || o3tl::make_unsigned(nLayer)>=mpLayers->size()) + { + OSL_ASSERT(nLayer>=0 && o3tl::make_unsigned(nLayer)<mpLayers->size()); + return; + } + + (*mpLayers)[nLayer]->InvalidateRectangle(rInvalidationArea); +} + +void LayeredDevice::InvalidateAllLayers (const ::tools::Rectangle& rInvalidationArea) +{ + for (size_t nLayer=0; nLayer<mpLayers->size(); ++nLayer) + (*mpLayers)[nLayer]->InvalidateRectangle(rInvalidationArea); +} + +void LayeredDevice::InvalidateAllLayers (const vcl::Region& rInvalidationRegion) +{ + for (size_t nLayer=0; nLayer<mpLayers->size(); ++nLayer) + (*mpLayers)[nLayer]->InvalidateRegion(rInvalidationRegion); +} + +void LayeredDevice::RegisterPainter ( + const SharedILayerPainter& rpPainter, + const sal_Int32 nLayer) +{ + OSL_ASSERT(mpLayers); + if ( ! rpPainter) + { + OSL_ASSERT(rpPainter); + return; + } + if (nLayer<0 || nLayer>=gnMaximumLayerCount) + { + OSL_ASSERT(nLayer>=0 && nLayer<gnMaximumLayerCount); + return; + } + + // Provide the layers. + if (o3tl::make_unsigned(nLayer) >= mpLayers->size()) + { + const sal_Int32 nOldLayerCount (mpLayers->size()); + mpLayers->resize(nLayer+1); + + for (size_t nIndex=nOldLayerCount; nIndex<mpLayers->size(); ++nIndex) + (*mpLayers)[nIndex] = std::make_shared<Layer>(); + } + + (*mpLayers)[nLayer]->AddPainter(rpPainter); + if (nLayer == 0) + (*mpLayers)[nLayer]->Initialize(mpTargetWindow); + + rpPainter->SetLayerInvalidator( + std::make_shared<LayerInvalidator>(shared_from_this(),mpTargetWindow,nLayer)); +} + +void LayeredDevice::RemovePainter ( + const SharedILayerPainter& rpPainter, + const sal_Int32 nLayer) +{ + if ( ! rpPainter) + { + OSL_ASSERT(rpPainter); + return; + } + if (nLayer<0 || o3tl::make_unsigned(nLayer)>=mpLayers->size()) + { + OSL_ASSERT(nLayer>=0 && o3tl::make_unsigned(nLayer)<mpLayers->size()); + return; + } + + rpPainter->SetLayerInvalidator(SharedILayerInvalidator()); + + (*mpLayers)[nLayer]->RemovePainter(rpPainter); + + // Remove top most layers that do not contain any painters. + while ( ! mpLayers->empty() && ! mpLayers->back()->HasPainter()) + mpLayers->pop_back(); +} + +void LayeredDevice::Repaint (const vcl::Region& rRepaintRegion) +{ + // Validate the contents of all layers (that have their own devices.) + for (auto const& it : *mpLayers) + { + it->Validate(mpTargetWindow->GetMapMode()); + } + + ForAllRectangles(rRepaintRegion, + [this] (::tools::Rectangle const& r) { this->RepaintRectangle(r); }); +} + +void LayeredDevice::RepaintRectangle (const ::tools::Rectangle& rRepaintRectangle) +{ + if (mpLayers->empty()) + return; + else if (mpLayers->size() == 1) + { + // Just copy the main layer into the target device. + (*mpLayers)[0]->Repaint(*mpTargetWindow->GetOutDev(), rRepaintRectangle); + } + else + { + // Paint all layers first into the back buffer (to avoid flickering + // due to synchronous paints) and then copy that into the target + // device. + mpBackBuffer->SetMapMode(mpTargetWindow->GetMapMode()); + for (auto const& it : *mpLayers) + { + it->Repaint(*mpBackBuffer, rRepaintRectangle); + } + DeviceCopy(*mpTargetWindow->GetOutDev(), *mpBackBuffer, rRepaintRectangle); + } +} + +void LayeredDevice::Resize() +{ + const Size aSize (mpTargetWindow->GetSizePixel()); + mpBackBuffer->SetOutputSizePixel(aSize); + for (auto const& it : *mpLayers) + { + it->Resize(aSize); + } +} + +void LayeredDevice::Dispose() +{ + for (auto const& it : *mpLayers) + { + it->Dispose(); + } + mpLayers->clear(); +} + +bool LayeredDevice::HandleMapModeChange() +{ + const MapMode& rMapMode (mpTargetWindow->GetMapMode()); + if (maSavedMapMode == rMapMode) + return false; + + const ::tools::Rectangle aLogicWindowBox ( + mpTargetWindow->PixelToLogic(::tools::Rectangle(Point(0,0), mpTargetWindow->GetSizePixel()))); + if (maSavedMapMode.GetScaleX() != rMapMode.GetScaleX() + || maSavedMapMode.GetScaleY() != rMapMode.GetScaleY() + || maSavedMapMode.GetMapUnit() != rMapMode.GetMapUnit()) + { + // When the scale has changed then we have to paint everything. + InvalidateAllLayers(aLogicWindowBox); + } + else if (maSavedMapMode.GetOrigin() != rMapMode.GetOrigin()) + { + // Window has been scrolled. Adapt contents of backbuffers and + // layer devices. + const Point aDelta (rMapMode.GetOrigin() - maSavedMapMode.GetOrigin()); + mpBackBuffer->CopyArea( + aLogicWindowBox.TopLeft(), + mpTargetWindow->PixelToLogic(Point(0,0), maSavedMapMode), + aLogicWindowBox.GetSize()); + + // Invalidate the area(s) that have been exposed. + const ::tools::Rectangle aWindowBox (Point(0,0), mpTargetWindow->GetSizePixel()); + if (aDelta.Y() < 0) + InvalidateAllLayers(mpTargetWindow->PixelToLogic(::tools::Rectangle( + aWindowBox.Left(), + aWindowBox.Bottom()+aDelta.Y(), + aWindowBox.Right(), + aWindowBox.Bottom()))); + else if (aDelta.Y() > 0) + InvalidateAllLayers(mpTargetWindow->PixelToLogic(::tools::Rectangle( + aWindowBox.Left(), + aWindowBox.Top(), + aWindowBox.Right(), + aWindowBox.Top()+aDelta.Y()))); + if (aDelta.X() < 0) + InvalidateAllLayers(mpTargetWindow->PixelToLogic(::tools::Rectangle( + aWindowBox.Right()+aDelta.X(), + aWindowBox.Top(), + aWindowBox.Right(), + aWindowBox.Bottom()))); + else if (aDelta.X() > 0) + InvalidateAllLayers(mpTargetWindow->PixelToLogic(::tools::Rectangle( + aWindowBox.Left(), + aWindowBox.Top(), + aWindowBox.Left()+aDelta.X(), + aWindowBox.Bottom()))); + } + else + { + // Can this happen? Lets trigger a warning when it does. + OSL_ASSERT(false); + } + + maSavedMapMode = rMapMode; + + return true; +} + +//===== Layer ================================================================= + +Layer::Layer() +{ +} + +void Layer::Initialize (sd::Window *pTargetWindow) +{ +#if 0 + (void)pTargetWindow; +#else + if ( ! mpLayerDevice) + { + mpLayerDevice.disposeAndReset(VclPtr<VirtualDevice>::Create(*pTargetWindow->GetOutDev())); + mpLayerDevice->SetOutputSizePixel(pTargetWindow->GetSizePixel()); + } +#endif +} + +void Layer::InvalidateRectangle (const ::tools::Rectangle& rInvalidationBox) +{ + maInvalidationRegion.Union(rInvalidationBox); +} + +void Layer::InvalidateRegion (const vcl::Region& rInvalidationRegion) +{ + maInvalidationRegion.Union(rInvalidationRegion); +} + +void Layer::Validate (const MapMode& rMapMode) +{ + if (mpLayerDevice && ! maInvalidationRegion.IsEmpty()) + { + vcl::Region aRegion (maInvalidationRegion); + maInvalidationRegion.SetEmpty(); + + mpLayerDevice->SetMapMode(rMapMode); + ForAllRectangles( + aRegion, + [this] (::tools::Rectangle const& r) { return this->ValidateRectangle(r); }); + } +} + +void Layer::ValidateRectangle (const ::tools::Rectangle& rBox) +{ + if ( ! mpLayerDevice) + return; + const vcl::Region aSavedClipRegion (mpLayerDevice->GetClipRegion()); + mpLayerDevice->IntersectClipRegion(rBox); + + for (const auto& rxPainter : maPainters) + { + rxPainter->Paint(*mpLayerDevice, rBox); + } + + mpLayerDevice->SetClipRegion(aSavedClipRegion); +} + +void Layer::Repaint ( + OutputDevice& rTargetDevice, + const ::tools::Rectangle& rRepaintRectangle) +{ + if (mpLayerDevice) + { + DeviceCopy(rTargetDevice, *mpLayerDevice, rRepaintRectangle); + } + else + { + for (auto const& it : maPainters) + { + it->Paint(rTargetDevice, rRepaintRectangle); + } + } +} + +void Layer::Resize (const Size& rSize) +{ + if (mpLayerDevice) + { + mpLayerDevice->SetOutputSizePixel(rSize); + maInvalidationRegion = ::tools::Rectangle(Point(0,0), rSize); + } +} + +void Layer::AddPainter (const SharedILayerPainter& rpPainter) +{ + OSL_ASSERT(::std::find(maPainters.begin(), maPainters.end(), rpPainter) == maPainters.end()); + + maPainters.push_back(rpPainter); +} + +void Layer::RemovePainter (const SharedILayerPainter& rpPainter) +{ + const ::std::vector<SharedILayerPainter>::iterator iPainter ( + ::std::find(maPainters.begin(), maPainters.end(), rpPainter)); + if (iPainter != maPainters.end()) + { + maPainters.erase(iPainter); + } + else + { + SAL_WARN("sd", "LayeredDevice::RemovePainter called for painter that is not registered"); + } +} + +bool Layer::HasPainter() const +{ + return !maPainters.empty(); +} + +void Layer::Dispose() +{ + maPainters.clear(); +} + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/view/SlsLayeredDevice.hxx b/sd/source/ui/slidesorter/view/SlsLayeredDevice.hxx new file mode 100644 index 000000000..5ec0d0e9f --- /dev/null +++ b/sd/source/ui/slidesorter/view/SlsLayeredDevice.hxx @@ -0,0 +1,84 @@ +/* -*- 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 . + */ + +#pragma once + +#include <view/SlsILayerPainter.hxx> + +#include <vcl/vclptr.hxx> +#include <vcl/mapmod.hxx> + +#include <memory> + +namespace sd { class Window; } +namespace tools { class Rectangle; } +namespace vcl { class Region; } + +class VirtualDevice; + +namespace sd::slidesorter::view { + +/** A simple wrapper around an OutputDevice that provides support for + independent layers and buffering. + Each layer may contain any number of painters. +*/ +class LayeredDevice + : public std::enable_shared_from_this<LayeredDevice> + +{ +public: + explicit LayeredDevice (const VclPtr<sd::Window>& pTargetWindow); + ~LayeredDevice (); + + void Invalidate ( + const ::tools::Rectangle& rInvalidationBox, + const sal_Int32 nLayer); + void InvalidateAllLayers ( + const ::tools::Rectangle& rInvalidationBox); + void InvalidateAllLayers ( + const vcl::Region& rInvalidationRegion); + + void RegisterPainter ( + const SharedILayerPainter& rPainter, + const sal_Int32 nLayer); + + void RemovePainter ( + const SharedILayerPainter& rPainter, + const sal_Int32 nLayer); + + bool HandleMapModeChange(); + void Repaint (const vcl::Region& rRepaintRegion); + + void Resize(); + + void Dispose(); + +private: + VclPtr<sd::Window> mpTargetWindow; + class LayerContainer; + std::unique_ptr<LayerContainer> mpLayers; + ScopedVclPtr<VirtualDevice> mpBackBuffer; + MapMode maSavedMapMode; + + void RepaintRectangle (const ::tools::Rectangle& rRepaintRectangle); +}; + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/view/SlsLayouter.cxx b/sd/source/ui/slidesorter/view/SlsLayouter.cxx new file mode 100644 index 000000000..21f0be13c --- /dev/null +++ b/sd/source/ui/slidesorter/view/SlsLayouter.cxx @@ -0,0 +1,1225 @@ +/* -*- 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 <view/SlsPageObjectLayouter.hxx> +#include <view/SlsTheme.hxx> +#include <view/SlsLayouter.hxx> +#include <model/SlideSorterModel.hxx> +#include <model/SlsPageDescriptor.hxx> +#include <Window.hxx> +#include <osl/diagnose.h> + +namespace sd::slidesorter::view { + +class Layouter::Implementation +{ +public: + VclPtr<sd::Window> mpWindow; + static const sal_Int32 mnRequestedLeftBorder = 5; + static const sal_Int32 mnRequestedRightBorder = 5; + static const sal_Int32 mnRequestedTopBorder = 5; + static const sal_Int32 mnRequestedBottomBorder = 5; + sal_Int32 mnLeftBorder; + sal_Int32 mnRightBorder; + sal_Int32 mnTopBorder; + sal_Int32 mnBottomBorder; + static const sal_Int32 gnVerticalGap = 10 - 2*Theme_FocusIndicatorWidth; + static const sal_Int32 gnHorizontalGap = 10 - 2*Theme_FocusIndicatorWidth; + Size maMinimalSize; + Size maPreferredSize; + Size maMaximalSize; + sal_Int32 mnMinimalColumnCount; + sal_Int32 mnMaximalColumnCount; + sal_Int32 mnPageCount; + sal_Int32 mnColumnCount; + sal_Int32 mnRowCount; + /// The maximum number of columns. Can only be larger than the current + /// number of columns when there are not enough pages to fill all + /// available columns. + sal_Int32 mnMaxColumnCount; + /// The maximum number of rows. Can only be larger than the current + /// number of rows when there are not enough pages to fill all available + /// rows. + sal_Int32 mnMaxRowCount; + Size maPageObjectSize; + std::shared_ptr<PageObjectLayouter> mpPageObjectLayouter; + std::shared_ptr<view::Theme> mpTheme; + + /** Specify how the gap between two page objects is associated with the + page objects. + */ + enum GapMembership { + GM_NONE, // Gap is not associated with any page object. + GM_PREVIOUS, // The whole gap is associated with the previous page + // object (left or above the gap.) + GM_BOTH, // Half of the gap is associated with previous, half + // with the next page object. + GM_NEXT, // The whole gap is associated with the next page + // object (right or below the gap.) + GM_PAGE_BORDER + }; + + static Implementation* Create ( + const Implementation& rImplementation, + const Layouter::Orientation eOrientation); + + virtual Layouter::Orientation GetOrientation() const = 0; + + bool Rearrange ( + const Size& rWindowSize, + const Size& rPreviewModelSize, + const sal_uInt32 nPageCount); + + /** Calculate the row that the point with the given vertical coordinate + is over. The horizontal component is ignored. + @param nYPosition + Vertical position in model coordinates. + @param bIncludeBordersAndGaps + When this flag is <TRUE/> then the area of borders and gaps are + interpreted as belonging to one of the rows. + @param eGapMembership + Specifies to what row the gap areas belong. Here GM_NONE + corresponds to bIncludeBordersAndGaps being <FALSE/>. When + GM_BOTH is given then the upper half is associated to the row + above and the lower half to the row below. Values of + GM_PREVIOUS and GM_NEXT associate the whole gap area with the + row above or below respectively. + */ + sal_Int32 GetRowAtPosition ( + sal_Int32 nYPosition, + bool bIncludeBordersAndGaps, + GapMembership eGapMembership) const; + + /** Calculate the column that the point with the given horizontal + coordinate is over. The vertical component is ignored. + @param nXPosition + Horizontal position in model coordinates. + @param bIncludeBordersAndGaps + When this flag is <TRUE/> then the area of borders and gaps are + interpreted as belonging to one of the columns. + @param eGapMembership + Specifies to what column the gap areas belong. + */ + sal_Int32 GetColumnAtPosition ( + sal_Int32 nXPosition, + bool bIncludeBordersAndGaps, + GapMembership eGapMembership) const; + + /** This method is typically called from GetRowAtPosition() and + GetColumnAtPosition() to handle a position that lies inside the gap + between two adjacent rows or columns. + @param nDistanceIntoGap + Vertical distance from the bottom of the upper row down into the + gap or horizontal distance from the right edge right into the + gap. + @param eGapMemberhship + This value decides what areas in the gap belong to which (or no) + row or column. + @param nIndex + The row index of the upper row or the column index of the left + column. + @param nGap + Width or height of the gap in model coordinates between the + page borders. + @return + Returns either the index of the upper row (as given as nRow), the + index of the lower row (nRow+1) or -1 to indicate that the + position belongs to no row. + */ + static sal_Int32 ResolvePositionInGap ( + sal_Int32 nDistanceIntoGap, + GapMembership eGapMembership, + sal_Int32 nIndex, + sal_Int32 nGap); + + /** Calculate the logical part of the insert position, i.e. the page + after which to insert. + */ + virtual void CalculateLogicalInsertPosition ( + const Point& rModelPosition, + InsertPosition& rPosition) const = 0; + + /** Calculate the geometrical part of the insert position, i.e. the + location of where to display the insertion indicator and the + distances about which the leading and trailing pages have to be + moved to make room for the indicator. + */ + void CalculateGeometricPosition ( + InsertPosition& rPosition, + const Size& rIndicatorSize, + const bool bIsVertical, + model::SlideSorterModel const & rModel) const; + + /** Return the bounding box of the preview or, when selected, of the page + object. Thus, it returns something like a visual bounding box. + */ + ::tools::Rectangle GetInnerBoundingBox ( + model::SlideSorterModel const & rModel, + const sal_Int32 nIndex) const; + + Range GetValidHorizontalSizeRange() const; + Range GetValidVerticalSizeRange() const; + + Range GetRangeOfVisiblePageObjects (const ::tools::Rectangle& aVisibleArea) const; + sal_Int32 GetIndex ( + const sal_Int32 nRow, + const sal_Int32 nColumn, + const bool bClampToValidRange) const; + + ::tools::Rectangle GetPageObjectBox ( + const sal_Int32 nIndex, + const bool bIncludeBorderAndGap = false) const; + + ::tools::Rectangle GetPageObjectBox ( + const sal_Int32 nRow, + const sal_Int32 nColumn) const; + + ::tools::Rectangle AddBorderAndGap ( + const ::tools::Rectangle& rBoundingBox, + const sal_Int32 nRow, + const sal_Int32 nColumn) const; + + ::tools::Rectangle GetTotalBoundingBox() const; + + virtual ~Implementation(); + +protected: + Implementation ( + sd::Window *pWindow, + const std::shared_ptr<view::Theme>& rpTheme); + explicit Implementation (const Implementation& rImplementation); + + virtual void CalculateRowAndColumnCount (const Size& rWindowSize) = 0; + virtual void CalculateMaxRowAndColumnCount (const Size& rWindowSize) = 0; + virtual Size CalculateTargetSize ( + const Size& rWindowSize) const = 0; + Size GetTargetSize ( + const Size& rWindowSize, + const bool bCalculateWidth, + const bool bCalculateHeight) const; + void CalculateVerticalLogicalInsertPosition ( + const Point& rModelPosition, + InsertPosition& rPosition) const; +}; + +namespace { + +/** The vertical layouter has one column and as many rows as there are + pages. +*/ +class VerticalImplementation : public Layouter::Implementation +{ +public: + explicit VerticalImplementation (const Implementation& rImplementation); + + virtual Layouter::Orientation GetOrientation() const override; + + void CalculateLogicalInsertPosition ( + const Point& rModelPosition, + InsertPosition& rPosition) const override; + +protected: + virtual void CalculateRowAndColumnCount (const Size& rWindowSize) override; + virtual void CalculateMaxRowAndColumnCount (const Size& rWindowSize) override; + virtual Size CalculateTargetSize ( + const Size& rWindowSize) const override; +}; + +/** The horizontal layouter has one row and as many columns as there are + pages. +*/ +class HorizontalImplementation : public Layouter::Implementation +{ +public: + explicit HorizontalImplementation(const Implementation& rImplementation); + + virtual Layouter::Orientation GetOrientation() const override; + + void CalculateLogicalInsertPosition ( + const Point& rModelPosition, + InsertPosition& rPosition) const override; + +protected: + virtual void CalculateRowAndColumnCount (const Size& rWindowSize) override; + virtual void CalculateMaxRowAndColumnCount (const Size& rWindowSize) override; + virtual Size CalculateTargetSize ( + const Size& rWindowSize) const override; +}; + +/** The number of columns of the grid layouter is defined via a control in + the slide sorter tool bar. The number of rows is calculated from the + number of columns and the number of pages. +*/ +class GridImplementation : public Layouter::Implementation +{ +public: + GridImplementation ( + sd::Window *pWindow, + const std::shared_ptr<view::Theme>& rpTheme); + explicit GridImplementation(const Implementation& rImplementation); + + virtual Layouter::Orientation GetOrientation() const override; + + void CalculateLogicalInsertPosition ( + const Point& rModelPosition, + InsertPosition& rPosition) const override; + +protected: + virtual void CalculateRowAndColumnCount (const Size& rWindowSize) override; + virtual void CalculateMaxRowAndColumnCount (const Size& rWindowSize) override; + virtual Size CalculateTargetSize ( + const Size& rWindowSize) const override; +}; + +} + +//===== Layouter ============================================================== + +Layouter::Layouter ( + sd::Window *pWindow, + const std::shared_ptr<Theme>& rpTheme) + : mpImplementation(new GridImplementation(pWindow, rpTheme)), + mpWindow(pWindow) +{ +} + +Layouter::~Layouter() +{ +} + +std::shared_ptr<PageObjectLayouter> const & Layouter::GetPageObjectLayouter() const +{ + return mpImplementation->mpPageObjectLayouter; +} + +void Layouter::SetColumnCount ( + sal_Int32 nMinimalColumnCount, + sal_Int32 nMaximalColumnCount) +{ + if (nMinimalColumnCount <= nMaximalColumnCount) + { + mpImplementation->mnMinimalColumnCount = nMinimalColumnCount; + mpImplementation->mnMaximalColumnCount = nMaximalColumnCount; + } +} + +bool Layouter::Rearrange ( + const Orientation eOrientation, + const Size& rWindowSize, + const Size& rPageSize, + const sal_uInt32 nPageCount) +{ + OSL_ASSERT(mpWindow); + + if (eOrientation != mpImplementation->GetOrientation()) + mpImplementation.reset(Implementation::Create(*mpImplementation, eOrientation)); + + return mpImplementation->Rearrange(rWindowSize, rPageSize, nPageCount); +} + +sal_Int32 Layouter::GetColumnCount() const +{ + return mpImplementation->mnColumnCount; +} + +sal_Int32 Layouter::GetIndex (const sal_Int32 nRow, const sal_Int32 nColumn) const +{ + return mpImplementation->GetIndex(nRow,nColumn,true); +} + +Size const & Layouter::GetPageObjectSize() const +{ + return mpImplementation->maPageObjectSize; +} + +::tools::Rectangle Layouter::GetPageObjectBox ( + const sal_Int32 nIndex, + const bool bIncludeBorderAndGap) const +{ + return mpImplementation->GetPageObjectBox(nIndex, bIncludeBorderAndGap); +} + +::tools::Rectangle Layouter::GetTotalBoundingBox() const +{ + return mpImplementation->GetTotalBoundingBox(); +} + +InsertPosition Layouter::GetInsertPosition ( + const Point& rModelPosition, + const Size& rIndicatorSize, + model::SlideSorterModel const & rModel) const +{ + InsertPosition aPosition; + mpImplementation->CalculateLogicalInsertPosition( + rModelPosition, + aPosition); + mpImplementation->CalculateGeometricPosition( + aPosition, + rIndicatorSize, + GetColumnCount()==1, + rModel); + return aPosition; +} + +Range Layouter::GetValidHorizontalSizeRange() const +{ + return mpImplementation->GetValidHorizontalSizeRange(); +} + +Range Layouter::GetValidVerticalSizeRange() const +{ + return mpImplementation->GetValidVerticalSizeRange(); +} + +Range Layouter::GetRangeOfVisiblePageObjects (const ::tools::Rectangle& aVisibleArea) const +{ + return mpImplementation->GetRangeOfVisiblePageObjects(aVisibleArea); +} + +sal_Int32 Layouter::GetIndexAtPoint ( + const Point& rPosition, + const bool bIncludePageBorders, + const bool bClampToValidRange) const +{ + const sal_Int32 nRow ( + mpImplementation->GetRowAtPosition ( + rPosition.Y(), + bIncludePageBorders, + bIncludePageBorders ? Implementation::GM_PAGE_BORDER : Implementation::GM_NONE)); + const sal_Int32 nColumn ( + mpImplementation->GetColumnAtPosition ( + rPosition.X(), + bIncludePageBorders, + bIncludePageBorders ? Implementation::GM_PAGE_BORDER : Implementation::GM_NONE)); + + return mpImplementation->GetIndex(nRow,nColumn,bClampToValidRange); +} + +//===== Layouter::Implementation ============================================== + +Layouter::Implementation* Layouter::Implementation::Create ( + const Implementation& rImplementation, + const Layouter::Orientation eOrientation) +{ + switch (eOrientation) + { + case HORIZONTAL: return new HorizontalImplementation(rImplementation); + case VERTICAL: return new VerticalImplementation(rImplementation); + case GRID: + default: return new GridImplementation(rImplementation); + } +} + +Layouter::Implementation::Implementation ( + sd::Window *pWindow, + const std::shared_ptr<view::Theme>& rpTheme) + : mpWindow(pWindow), + mnLeftBorder(5), + mnRightBorder(5), + mnTopBorder(5), + mnBottomBorder(5), + maMinimalSize(132,98), + maPreferredSize(200,150), + maMaximalSize(600,400), + mnMinimalColumnCount(1), + mnMaximalColumnCount(15), + mnPageCount(0), + mnColumnCount(1), + mnRowCount(0), + mnMaxColumnCount(0), + mnMaxRowCount(0), + maPageObjectSize(1,1), + mpTheme(rpTheme) +{ +} + +Layouter::Implementation::Implementation (const Implementation& rImplementation) + : mpWindow(rImplementation.mpWindow), + mnLeftBorder(rImplementation.mnLeftBorder), + mnRightBorder(rImplementation.mnRightBorder), + mnTopBorder(rImplementation.mnTopBorder), + mnBottomBorder(rImplementation.mnBottomBorder), + maMinimalSize(rImplementation.maMinimalSize), + maPreferredSize(rImplementation.maPreferredSize), + maMaximalSize(rImplementation.maMaximalSize), + mnMinimalColumnCount(rImplementation.mnMinimalColumnCount), + mnMaximalColumnCount(rImplementation.mnMaximalColumnCount), + mnPageCount(rImplementation.mnPageCount), + mnColumnCount(rImplementation.mnColumnCount), + mnRowCount(rImplementation.mnRowCount), + mnMaxColumnCount(rImplementation.mnMaxColumnCount), + mnMaxRowCount(rImplementation.mnMaxRowCount), + maPageObjectSize(rImplementation.maPageObjectSize), + mpTheme(rImplementation.mpTheme) +{ +} + +Layouter::Implementation::~Implementation() +{ +} + +bool Layouter::Implementation::Rearrange ( + const Size& rWindowSize, + const Size& rPreviewModelSize, + const sal_uInt32 nPageCount) +{ + mnPageCount = nPageCount; + + // Return early when the window or the model have not yet been initialized. + if (rWindowSize.IsEmpty()) + return false; + if (rPreviewModelSize.IsEmpty()) + return false; + + CalculateRowAndColumnCount(rWindowSize); + + // Update the border values. + mnLeftBorder = mnRequestedLeftBorder; + mnTopBorder = mnRequestedTopBorder; + mnRightBorder = mnRequestedRightBorder; + mnBottomBorder = mnRequestedBottomBorder; + if (mnColumnCount > 1) + { + int nMinimumBorderWidth = gnHorizontalGap/2; + if (mnLeftBorder < nMinimumBorderWidth) + mnLeftBorder = nMinimumBorderWidth; + if (mnRightBorder < nMinimumBorderWidth) + mnRightBorder = nMinimumBorderWidth; + } + else + { + int nMinimumBorderHeight = gnVerticalGap/2; + if (mnTopBorder < nMinimumBorderHeight) + mnTopBorder = nMinimumBorderHeight; + if (mnBottomBorder < nMinimumBorderHeight) + mnBottomBorder = nMinimumBorderHeight; + } + + mpPageObjectLayouter = + std::make_shared<PageObjectLayouter>( + CalculateTargetSize(rWindowSize), + rPreviewModelSize, + mpWindow, + mnPageCount); + + maPageObjectSize = mpPageObjectLayouter->GetGridMaxSize(); + + CalculateMaxRowAndColumnCount(rWindowSize); + + return true; +} + +sal_Int32 Layouter::Implementation::GetRowAtPosition ( + sal_Int32 nYPosition, + bool bIncludeBordersAndGaps, + GapMembership eGapMembership) const +{ + sal_Int32 nRow = -1; + + const sal_Int32 nY = nYPosition - mnTopBorder; + if (nY >= 0) + { + // Vertical distance from one row to the next. + const sal_Int32 nRowOffset (maPageObjectSize.Height() + gnVerticalGap); + + // Calculate row consisting of page objects and gap below. + nRow = nY / nRowOffset; + + const sal_Int32 nDistanceIntoGap ((nY - nRow*nRowOffset) - maPageObjectSize.Height()); + // When inside the gap below then nYPosition is not over a page + // object. + if (nDistanceIntoGap > 0) + { + sal_Int32 nResolvedRow = ResolvePositionInGap( + nDistanceIntoGap, + eGapMembership, + nRow, + gnVerticalGap); + if (!bIncludeBordersAndGaps || nResolvedRow != -1) + nRow = nResolvedRow; + } + } + else if (bIncludeBordersAndGaps) + { + // We are in the top border area. Set nRow to the first row when + // the top border shall be considered to belong to the first row. + nRow = 0; + } + + return nRow; +} + +sal_Int32 Layouter::Implementation::GetColumnAtPosition ( + sal_Int32 nXPosition, + bool bIncludeBordersAndGaps, + GapMembership eGapMembership) const +{ + sal_Int32 nColumn = -1; + + sal_Int32 nX = nXPosition - mnLeftBorder; + if (nX >= 0) + { + // Horizontal distance from one column to the next. + const sal_Int32 nColumnOffset (maPageObjectSize.Width() + gnHorizontalGap); + + // Calculate row consisting of page objects and gap below. + nColumn = nX / nColumnOffset; + if (nColumn < 0) + nColumn = 0; + else if (nColumn >= mnColumnCount) + nColumn = mnColumnCount-1; + + const sal_Int32 nDistanceIntoGap ((nX - nColumn*nColumnOffset) - maPageObjectSize.Width()); + // When inside the gap at the right then nXPosition is not over a + // page object. + if (nDistanceIntoGap > 0) + { + sal_Int32 nResolvedColumn = ResolvePositionInGap( + nDistanceIntoGap, + eGapMembership, + nColumn, + gnHorizontalGap); + if (!bIncludeBordersAndGaps || nResolvedColumn != -1) + nColumn = nResolvedColumn; + } + } + else if (bIncludeBordersAndGaps) + { + // We are in the left border area. Set nColumn to the first column + // when the left border shall be considered to belong to the first + // column. + nColumn = 0; + } + return nColumn; +} + +sal_Int32 Layouter::Implementation::ResolvePositionInGap ( + sal_Int32 nDistanceIntoGap, + GapMembership eGapMembership, + sal_Int32 nIndex, + sal_Int32 nGap) +{ + switch (eGapMembership) + { + case GM_NONE: + // The gap is no man's land. + nIndex = -1; + break; + + case GM_BOTH: + { + // The lower half of the gap belongs to the next row or column. + sal_Int32 nFirstHalfGapWidth = nGap / 2; + if (nDistanceIntoGap > nFirstHalfGapWidth) + nIndex ++; + break; + } + + case GM_PREVIOUS: + // Row or column already at correct value. + break; + + case GM_NEXT: + // The complete gap belongs to the next row or column. + nIndex ++; + break; + + case GM_PAGE_BORDER: + if (nDistanceIntoGap > 0) + { + if (nDistanceIntoGap > nGap) + { + // Inside the border of the next row or column. + nIndex ++; + } + else + { + // Inside the gap between the page borders. + nIndex = -1; + } + } + break; + + default: + nIndex = -1; + } + + return nIndex; +} + +void Layouter::Implementation::CalculateGeometricPosition ( + InsertPosition& rPosition, + const Size& rIndicatorSize, + const bool bIsVertical, + model::SlideSorterModel const & rModel) const +{ + // 1. Determine right/bottom of the leading page and the left/top of the + // trailing page object and how to distribute the missing space. + sal_Int32 nLeadingLocation (0); + sal_Int32 nTrailingLocation (0); + bool bIsLeadingFixed (false); + bool bIsTrailingFixed (false); + sal_Int32 nSecondaryLocation (0); + const sal_Int32 nIndex (rPosition.GetIndex()); + + if (rPosition.IsAtRunStart()) + { + // Place indicator at the top of the column. + const ::tools::Rectangle aOuterBox (GetPageObjectBox(nIndex)); + const ::tools::Rectangle aInnerBox (GetInnerBoundingBox(rModel, nIndex)); + if (bIsVertical) + { + nLeadingLocation = aOuterBox.Top(); + nTrailingLocation = aInnerBox.Top(); + nSecondaryLocation = aInnerBox.Center().X(); + } + else + { + nLeadingLocation = aOuterBox.Left(); + nTrailingLocation = aInnerBox.Left(); + nSecondaryLocation = aInnerBox.Center().Y(); + } + bIsLeadingFixed = true; + } + else if (rPosition.IsAtRunEnd()) + { + // Place indicator at the bottom/right of the column/row. + + const ::tools::Rectangle aOuterBox (GetPageObjectBox(nIndex-1)); + const ::tools::Rectangle aInnerBox (GetInnerBoundingBox(rModel, nIndex-1)); + if (bIsVertical) + { + nLeadingLocation = aInnerBox.Bottom(); + nTrailingLocation = aOuterBox.Bottom(); + nSecondaryLocation = aInnerBox.Center().X(); + } + else + { + nLeadingLocation = aInnerBox.Right(); + nTrailingLocation = aOuterBox.Right(); + nSecondaryLocation = aInnerBox.Center().Y(); + } + bIsTrailingFixed = true; + if ( ! rPosition.IsExtraSpaceNeeded()) + bIsLeadingFixed = true; + } + else + { + // Place indicator between two rows/columns. + const ::tools::Rectangle aBox1 (GetInnerBoundingBox(rModel, nIndex-1)); + const ::tools::Rectangle aBox2 (GetInnerBoundingBox(rModel, nIndex)); + if (bIsVertical) + { + nLeadingLocation = aBox1.Bottom(); + nTrailingLocation = aBox2.Top(); + nSecondaryLocation = (aBox1.Center().X() + aBox2.Center().X()) / 2; + } + else + { + nLeadingLocation = aBox1.Right(); + nTrailingLocation = aBox2.Left(); + nSecondaryLocation = (aBox1.Center().Y() + aBox2.Center().Y()) / 2; + } + } + + // 2. Calculate the location of the insert indicator and the offsets of + // leading and trailing pages. + const sal_Int32 nAvailableSpace (nTrailingLocation - nLeadingLocation); + const sal_Int32 nRequiredSpace (bIsVertical ? rIndicatorSize.Height():rIndicatorSize.Width()); + const sal_Int32 nMissingSpace (::std::max(sal_Int32(0), nRequiredSpace - nAvailableSpace)); + sal_Int32 nPrimaryLocation (0); + sal_Int32 nLeadingOffset (0); + sal_Int32 nTrailingOffset (0); + if (bIsLeadingFixed) + { + nPrimaryLocation = nLeadingLocation + nRequiredSpace/2; + if ( ! bIsTrailingFixed) + nTrailingOffset = nMissingSpace; + } + else if (bIsTrailingFixed) + { + nPrimaryLocation = nTrailingLocation - nRequiredSpace/2; + nLeadingOffset = -nMissingSpace; + } + else + { + nPrimaryLocation = (nLeadingLocation + nTrailingLocation) /2; + nLeadingOffset = -nMissingSpace/2; + nTrailingOffset = nMissingSpace + nLeadingOffset; + } + + if (bIsVertical) + { + rPosition.SetGeometricalPosition( + Point(nSecondaryLocation, nPrimaryLocation), + Point(0, nLeadingOffset), + Point(0, nTrailingOffset)); + } + else + { + rPosition.SetGeometricalPosition( + Point(nPrimaryLocation, nSecondaryLocation), + Point(nLeadingOffset, 0), + Point(nTrailingOffset, 0)); + } +} + +::tools::Rectangle Layouter::Implementation::GetInnerBoundingBox ( + model::SlideSorterModel const & rModel, + const sal_Int32 nIndex) const +{ + model::SharedPageDescriptor pDescriptor (rModel.GetPageDescriptor(nIndex)); + if ( ! pDescriptor) + return ::tools::Rectangle(); + + PageObjectLayouter::Part ePart = PageObjectLayouter::Part::Preview; + + if (pDescriptor->HasState(model::PageDescriptor::ST_Selected)) + ePart = PageObjectLayouter::Part::PageObject; + + return mpPageObjectLayouter->GetBoundingBox( + pDescriptor, ePart, + PageObjectLayouter::ModelCoordinateSystem, true); +} + +Range Layouter::Implementation::GetValidHorizontalSizeRange() const +{ + return Range( + mnLeftBorder + maMinimalSize.Width() + mnRightBorder, + mnLeftBorder + maMaximalSize.Width() + mnRightBorder); +} + +Range Layouter::Implementation::GetValidVerticalSizeRange() const +{ + return Range( + mnTopBorder + maMinimalSize.Height() + mnBottomBorder, + mnTopBorder + maMaximalSize.Height() + mnBottomBorder); +} + +Range Layouter::Implementation::GetRangeOfVisiblePageObjects (const ::tools::Rectangle& aVisibleArea) const +{ + // technically that's not empty, but it's the default, so... + if (aVisibleArea.IsEmpty()) + return Range(-1, -1); + + const sal_Int32 nRow0 (GetRowAtPosition(aVisibleArea.Top(), true, GM_NEXT)); + const sal_Int32 nCol0 (GetColumnAtPosition(aVisibleArea.Left(),true, GM_NEXT)); + const sal_Int32 nRow1 (GetRowAtPosition(aVisibleArea.Bottom(), true, GM_PREVIOUS)); + const sal_Int32 nCol1 (GetColumnAtPosition(aVisibleArea.Right(), true, GM_PREVIOUS)); + + // When start and end lie in different rows then the range may include + // slides outside (left or right of) the given area. + return Range(GetIndex(nRow0,nCol0,true), GetIndex(nRow1,nCol1,true)); +} + +Size Layouter::Implementation::GetTargetSize ( + const Size& rWindowSize, + const bool bCalculateWidth, + const bool bCalculateHeight) const +{ + if (mnColumnCount<=0 || mnRowCount<=0) + return maPreferredSize; + if ( ! (bCalculateWidth || bCalculateHeight)) + { + OSL_ASSERT(bCalculateWidth || bCalculateHeight); + return maPreferredSize; + } + + // Calculate the width of each page object. + Size aTargetSize (0,0); + if (bCalculateWidth) + aTargetSize.setWidth( + (rWindowSize.Width() - mnLeftBorder - mnRightBorder + - (mnColumnCount-1) * gnHorizontalGap) + / mnColumnCount); + else if (bCalculateHeight) + aTargetSize.setHeight( + (rWindowSize.Height() - mnTopBorder - mnBottomBorder + - (mnRowCount-1) * gnVerticalGap) + / mnRowCount); + + if (bCalculateWidth) + { + if (aTargetSize.Width() < maMinimalSize.Width()) + aTargetSize.setWidth(maMinimalSize.Width()); + else if (aTargetSize.Width() > maMaximalSize.Width()) + aTargetSize.setWidth(maMaximalSize.Width()); + } + else if (bCalculateHeight) + { + if (aTargetSize.Height() < maMinimalSize.Height()) + aTargetSize.setHeight(maMinimalSize.Height()); + else if (aTargetSize.Height() > maMaximalSize.Height()) + aTargetSize.setHeight(maMaximalSize.Height()); + } + + return aTargetSize; +} + +sal_Int32 Layouter::Implementation::GetIndex ( + const sal_Int32 nRow, + const sal_Int32 nColumn, + const bool bClampToValidRange) const +{ + if (nRow >= 0 && nColumn >= 0) + { + const sal_Int32 nIndex (nRow * mnColumnCount + nColumn); + if (nIndex >= mnPageCount) + if (bClampToValidRange) + return mnPageCount-1; + else + return -1; + else + return nIndex; + } + else if (bClampToValidRange) + return 0; + else + return -1; +} + +::tools::Rectangle Layouter::Implementation::GetPageObjectBox ( + const sal_Int32 nIndex, + const bool bIncludeBorderAndGap) const +{ + const sal_Int32 nRow (nIndex / mnColumnCount); + const sal_Int32 nColumn (nIndex % mnColumnCount); + + const ::tools::Rectangle aBoundingBox (GetPageObjectBox(nRow,nColumn)); + if (bIncludeBorderAndGap) + return AddBorderAndGap(aBoundingBox, nRow, nColumn); + else + return aBoundingBox; +} + +::tools::Rectangle Layouter::Implementation::GetPageObjectBox ( + const sal_Int32 nRow, + const sal_Int32 nColumn) const +{ + return ::tools::Rectangle( + Point (mnLeftBorder + + nColumn * maPageObjectSize.Width() + + std::max<sal_Int32>(nColumn,0) * gnHorizontalGap, + mnTopBorder + + nRow * maPageObjectSize.Height() + + std::max<sal_Int32>(nRow,0) * gnVerticalGap), + maPageObjectSize); +} + +::tools::Rectangle Layouter::Implementation::AddBorderAndGap ( + const ::tools::Rectangle& rBoundingBox, + const sal_Int32 nRow, + const sal_Int32 nColumn) const +{ + ::tools::Rectangle aBoundingBox (rBoundingBox); + + if (nColumn == 0) + aBoundingBox.SetLeft( 0 ); + else + aBoundingBox.AdjustLeft( -(gnHorizontalGap/2) ); + if (nColumn == mnColumnCount-1) + aBoundingBox.AdjustRight(mnRightBorder ); + else + aBoundingBox.AdjustRight(gnHorizontalGap/2 ); + if (nRow == 0) + aBoundingBox.SetTop( 0 ); + else + aBoundingBox.AdjustTop( -(gnVerticalGap/2) ); + if (nRow == mnRowCount-1) + aBoundingBox.AdjustBottom(mnBottomBorder ); + else + aBoundingBox.AdjustBottom(gnVerticalGap/2 ); + return aBoundingBox; +} + +::tools::Rectangle Layouter::Implementation::GetTotalBoundingBox() const +{ + sal_Int32 nHorizontalSize = 0; + sal_Int32 nVerticalSize = 0; + if (mnColumnCount > 0) + { + sal_Int32 nRowCount = (mnPageCount+mnColumnCount-1) / mnColumnCount; + nHorizontalSize = + mnLeftBorder + + mnRightBorder + + mnColumnCount * maPageObjectSize.Width(); + if (mnColumnCount > 1) + nHorizontalSize += (mnColumnCount-1) * gnHorizontalGap; + nVerticalSize = + mnTopBorder + + mnBottomBorder + + nRowCount * maPageObjectSize.Height(); + if (nRowCount > 1) + nVerticalSize += (nRowCount-1) * gnVerticalGap; + } + + return ::tools::Rectangle ( + Point(0,0), + Size (nHorizontalSize, nVerticalSize) + ); +} + +void Layouter::Implementation::CalculateVerticalLogicalInsertPosition ( + const Point& rModelPosition, + InsertPosition& rPosition) const +{ + const sal_Int32 nY = rModelPosition.Y() - mnTopBorder + maPageObjectSize.Height()/2; + const sal_Int32 nRowHeight (maPageObjectSize.Height() + gnVerticalGap); + const sal_Int32 nRow (::std::min(mnPageCount, nY / nRowHeight)); + rPosition.SetLogicalPosition ( + nRow, + 0, + nRow, + (nRow == 0), + (nRow == mnRowCount), + (nRow >= mnMaxRowCount)); +} + +//===== HorizontalImplementation ================================================ + +HorizontalImplementation::HorizontalImplementation (const Implementation& rImplementation) + : Implementation(rImplementation) +{ +} + +Layouter::Orientation HorizontalImplementation::GetOrientation() const +{ + return Layouter::HORIZONTAL; +} + +void HorizontalImplementation::CalculateRowAndColumnCount (const Size&) +{ + // Row and column count are fixed (for a given page count.) + mnColumnCount = mnPageCount; + mnRowCount = 1; +} + +void HorizontalImplementation::CalculateMaxRowAndColumnCount (const Size& rWindowSize) +{ + mnMaxColumnCount = (rWindowSize.Width() - mnLeftBorder - mnRightBorder) + / (maPageObjectSize.Width() + gnHorizontalGap); + mnMaxRowCount = 1; +} + +Size HorizontalImplementation::CalculateTargetSize ( + const Size& rWindowSize) const +{ + return Implementation::GetTargetSize(rWindowSize, false, true); +} + +void HorizontalImplementation::CalculateLogicalInsertPosition ( + const Point& rModelPosition, + InsertPosition& rPosition) const +{ + const sal_Int32 nX = rModelPosition.X() - mnLeftBorder + maPageObjectSize.Width()/2; + const sal_Int32 nColumnWidth (maPageObjectSize.Width() + gnHorizontalGap); + const sal_Int32 nColumn (::std::min(mnPageCount, nX / nColumnWidth)); + rPosition.SetLogicalPosition ( + 0, + nColumn, + nColumn, + (nColumn == 0), + (nColumn == mnColumnCount), + (nColumn >= mnMaxColumnCount)); +} + +//===== VerticalImplementation ================================================ + +VerticalImplementation::VerticalImplementation (const Implementation& rImplementation) + : Implementation(rImplementation) +{ +} + +Layouter::Orientation VerticalImplementation::GetOrientation() const +{ + return Layouter::VERTICAL; +} + +void VerticalImplementation::CalculateRowAndColumnCount (const Size&) +{ + // Row and column count are fixed (for a given page count.) + mnRowCount = mnPageCount; + mnColumnCount = 1; + +} + +void VerticalImplementation::CalculateMaxRowAndColumnCount (const Size& rWindowSize) +{ + mnMaxRowCount = (rWindowSize.Height() - mnTopBorder - mnBottomBorder) + / (maPageObjectSize.Height() + gnVerticalGap); + mnMaxColumnCount = 1; +} + +Size VerticalImplementation::CalculateTargetSize ( + const Size& rWindowSize) const +{ + return Implementation::GetTargetSize(rWindowSize, true, false); +} + +void VerticalImplementation::CalculateLogicalInsertPosition ( + const Point& rModelPosition, + InsertPosition& rPosition) const +{ + return CalculateVerticalLogicalInsertPosition(rModelPosition, rPosition); +} + +//===== GridImplementation ================================================ + +GridImplementation::GridImplementation ( + sd::Window *pWindow, + const std::shared_ptr<view::Theme>& rpTheme) + : Implementation(pWindow, rpTheme) +{ +} + +GridImplementation::GridImplementation (const Implementation& rImplementation) + : Implementation(rImplementation) +{ +} + +Layouter::Orientation GridImplementation::GetOrientation() const +{ + return Layouter::GRID; +} + +void GridImplementation::CalculateRowAndColumnCount (const Size& rWindowSize) +{ + // Calculate the column count. + mnColumnCount + = (rWindowSize.Width() - mnRequestedLeftBorder - mnRequestedRightBorder) + / (maPreferredSize.Width() + gnHorizontalGap); + if (mnColumnCount < mnMinimalColumnCount) + mnColumnCount = mnMinimalColumnCount; + if (mnColumnCount > mnMaximalColumnCount) + mnColumnCount = mnMaximalColumnCount; + mnRowCount = (mnPageCount + mnColumnCount-1)/mnColumnCount; +} + +void GridImplementation::CalculateMaxRowAndColumnCount (const Size& rWindowSize) +{ + mnMaxColumnCount = (rWindowSize.Width() - mnLeftBorder - mnRightBorder) + / (maPageObjectSize.Width() + gnHorizontalGap); + mnMaxRowCount = (rWindowSize.Height() - mnTopBorder - mnBottomBorder) + / (maPageObjectSize.Height() + gnVerticalGap); +} + +Size GridImplementation::CalculateTargetSize ( + const Size& rWindowSize) const +{ + return Implementation::GetTargetSize(rWindowSize, true, true); +} + +void GridImplementation::CalculateLogicalInsertPosition ( + const Point& rModelPosition, + InsertPosition& rPosition) const +{ + if (mnColumnCount == 1) + { + CalculateVerticalLogicalInsertPosition(rModelPosition, rPosition); + } + else + { + // Handle the general case of more than one column. + sal_Int32 nRow (::std::min( + mnRowCount-1, + GetRowAtPosition (rModelPosition.Y(), true, GM_BOTH))); + const sal_Int32 nX = rModelPosition.X() - mnLeftBorder + maPageObjectSize.Width()/2; + const sal_Int32 nColumnWidth (maPageObjectSize.Width() + gnHorizontalGap); + sal_Int32 nColumn (::std::min(mnColumnCount, nX / nColumnWidth)); + sal_Int32 nIndex (nRow * mnColumnCount + nColumn); + bool bIsAtRunEnd (nColumn == mnColumnCount); + + if (nIndex >= mnPageCount) + { + nIndex = mnPageCount; + nRow = mnRowCount-1; + nColumn = ::std::min(::std::min(mnPageCount, mnColumnCount), nColumn); + bIsAtRunEnd = true; + } + + rPosition.SetLogicalPosition ( + nRow, + nColumn, + nIndex, + (nColumn == 0), + bIsAtRunEnd, + (nColumn >= mnMaxColumnCount)); + } +} + +//===== InsertPosition ======================================================== + +InsertPosition::InsertPosition() + : mnRow(-1), + mnColumn(-1), + mnIndex(-1), + mbIsAtRunStart(false), + mbIsAtRunEnd(false), + mbIsExtraSpaceNeeded(false), + maLocation(0,0), + maLeadingOffset(0,0), + maTrailingOffset(0,0) +{ +} + +bool InsertPosition::operator== (const InsertPosition& rInsertPosition) const +{ + // Do not compare the geometrical information (maLocation). + return mnRow==rInsertPosition.mnRow + && mnColumn==rInsertPosition.mnColumn + && mnIndex==rInsertPosition.mnIndex + && mbIsAtRunStart==rInsertPosition.mbIsAtRunStart + && mbIsAtRunEnd==rInsertPosition.mbIsAtRunEnd + && mbIsExtraSpaceNeeded==rInsertPosition.mbIsExtraSpaceNeeded; +} + +bool InsertPosition::operator!= (const InsertPosition& rInsertPosition) const +{ + return !operator==(rInsertPosition); +} + +void InsertPosition::SetLogicalPosition ( + const sal_Int32 nRow, + const sal_Int32 nColumn, + const sal_Int32 nIndex, + const bool bIsAtRunStart, + const bool bIsAtRunEnd, + const bool bIsExtraSpaceNeeded) +{ + mnRow = nRow; + mnColumn = nColumn; + mnIndex = nIndex; + mbIsAtRunStart = bIsAtRunStart; + mbIsAtRunEnd = bIsAtRunEnd; + mbIsExtraSpaceNeeded = bIsExtraSpaceNeeded; +} + +void InsertPosition::SetGeometricalPosition( + const Point& rLocation, + const Point& rLeadingOffset, + const Point& rTrailingOffset) +{ + maLocation = rLocation; + maLeadingOffset = rLeadingOffset; + maTrailingOffset = rTrailingOffset; +} + +} // end of namespace ::sd::slidesorter::namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/view/SlsPageObjectLayouter.cxx b/sd/source/ui/slidesorter/view/SlsPageObjectLayouter.cxx new file mode 100644 index 000000000..b26eb0746 --- /dev/null +++ b/sd/source/ui/slidesorter/view/SlsPageObjectLayouter.cxx @@ -0,0 +1,259 @@ +/* -*- 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 <view/SlsPageObjectLayouter.hxx> + +#include <model/SlsPageDescriptor.hxx> +#include <view/SlsTheme.hxx> +#include <tools/IconCache.hxx> +#include <Window.hxx> + +#include <bitmaps.hlst> +#include <osl/diagnose.h> + +namespace sd::slidesorter::view { + +namespace { +const sal_Int32 gnLeftPageNumberOffset = 2; +const sal_Int32 gnRightPageNumberOffset = 5; +const sal_Int32 gnOuterBorderWidth = 5; +const sal_Int32 gnInfoAreaMinWidth = 26; +} + +PageObjectLayouter::PageObjectLayouter ( + const Size& rPageObjectWindowSize, + const Size& rPageSize, + sd::Window *pWindow, + const sal_Int32 nPageCount) + : mpWindow(pWindow), + maTransitionEffectIcon(IconCache::Instance().GetIcon(BMP_FADE_EFFECT_INDICATOR)), + maCustomAnimationEffectIcon(IconCache::Instance().GetIcon(BMP_CUSTOM_ANIMATION_INDICATOR)), + mpPageNumberFont(Theme::GetFont(Theme::Font_PageNumber, *pWindow->GetOutDev())) +{ + const Size aPageNumberAreaSize (GetPageNumberAreaSize(nPageCount)); + + const int nMaximumBorderWidth (gnOuterBorderWidth); + const int nFocusIndicatorWidth (Theme_FocusIndicatorWidth); + + Size aPageObjectSize(rPageObjectWindowSize.Width(), rPageObjectWindowSize.Height()); + maPreviewBoundingBox = CalculatePreviewBoundingBox( + aPageObjectSize, + Size(rPageSize.Width(), rPageSize.Height()), + aPageNumberAreaSize.Width(), + nFocusIndicatorWidth); + maFocusIndicatorBoundingBox = ::tools::Rectangle(Point(0,0), aPageObjectSize); + maPageObjectBoundingBox = ::tools::Rectangle( + Point( + nFocusIndicatorWidth, + nFocusIndicatorWidth), + Size( + aPageObjectSize.Width()-2*nFocusIndicatorWidth, + aPageObjectSize.Height()-2*nFocusIndicatorWidth)); + + maPageNumberAreaBoundingBox = ::tools::Rectangle( + Point( + std::max(gnLeftPageNumberOffset, + sal_Int32(maPreviewBoundingBox.Left() + - gnRightPageNumberOffset + - aPageNumberAreaSize.Width())), + nMaximumBorderWidth), + aPageNumberAreaSize); + + const Size aIconSize (maTransitionEffectIcon.GetSizePixel()); + maTransitionEffectBoundingBox = ::tools::Rectangle( + Point( + (maPreviewBoundingBox.Left() - 2*aIconSize.Width()) / 2, + maPreviewBoundingBox.Bottom() - aIconSize.Height()), + aIconSize); + maCustomAnimationEffectBoundingBox = ::tools::Rectangle( + Point( + (maPreviewBoundingBox.Left() - 2*aIconSize.Width()) / 2, + maPreviewBoundingBox.Bottom() - 2*aIconSize.Height()), + aIconSize); +} + +PageObjectLayouter::~PageObjectLayouter() +{ +} + +::tools::Rectangle PageObjectLayouter::CalculatePreviewBoundingBox ( + Size& rPageObjectSize, + const Size& rPageSize, + const sal_Int32 nPageNumberAreaWidth, + const sal_Int32 nFocusIndicatorWidth) +{ + const sal_Int32 nIconWidth (maTransitionEffectIcon.GetSizePixel().Width()); + const sal_Int32 nLeftAreaWidth ( + ::std::max( + gnInfoAreaMinWidth, + gnRightPageNumberOffset + + ::std::max( + nPageNumberAreaWidth, + nIconWidth))); + sal_Int32 nPreviewWidth; + sal_Int32 nPreviewHeight; + const double nPageAspectRatio (double(rPageSize.Width()) / double(rPageSize.Height())); + if (rPageObjectSize.Height() == 0) + { + // Calculate height so that the preview fills the available + // horizontal space completely while observing the aspect ratio of + // the preview. + nPreviewWidth = rPageObjectSize.Width() + - nLeftAreaWidth - gnOuterBorderWidth - 2*nFocusIndicatorWidth - 1; + nPreviewHeight = ::basegfx::fround(nPreviewWidth / nPageAspectRatio); + rPageObjectSize.setHeight(nPreviewHeight + 2*gnOuterBorderWidth + 2*nFocusIndicatorWidth + 1); + } + else if (rPageObjectSize.Width() == 0) + { + // Calculate the width of the page object so that the preview fills + // the available vertical space completely while observing the + // aspect ratio of the preview. + nPreviewHeight = rPageObjectSize.Height() - 2*gnOuterBorderWidth - 2*nFocusIndicatorWidth - 1; + nPreviewWidth = ::basegfx::fround(nPreviewHeight * nPageAspectRatio); + rPageObjectSize.setWidth(nPreviewWidth + + nLeftAreaWidth + gnOuterBorderWidth + 2*nFocusIndicatorWidth + 1); + + } + else + { + // The size of the page object is given. Calculate the size of the + // preview. + nPreviewWidth = rPageObjectSize.Width() + - nLeftAreaWidth - gnOuterBorderWidth - 2*nFocusIndicatorWidth - 1; + nPreviewHeight = rPageObjectSize.Height() + - gnOuterBorderWidth - 2*nFocusIndicatorWidth - 1; + if (double(nPreviewWidth)/double(nPreviewHeight) > nPageAspectRatio) + nPreviewWidth = ::basegfx::fround(nPreviewHeight * nPageAspectRatio); + else + nPreviewHeight = ::basegfx::fround(nPreviewWidth / nPageAspectRatio); + } + // When the preview does not fill the available space completely then + // place it flush right and vertically centered. + const int nLeft (rPageObjectSize.Width() + - gnOuterBorderWidth - nPreviewWidth - nFocusIndicatorWidth - 1); + const int nTop ((rPageObjectSize.Height() - nPreviewHeight)/2); + return ::tools::Rectangle( + nLeft, + nTop, + nLeft + nPreviewWidth, + nTop + nPreviewHeight); +} + +::tools::Rectangle PageObjectLayouter::GetBoundingBox ( + const model::SharedPageDescriptor& rpPageDescriptor, + const Part ePart, + const CoordinateSystem eCoordinateSystem, + bool bIgnoreLocation) +{ + OSL_ASSERT(rpPageDescriptor); + Point aLocation(0,0); + if (rpPageDescriptor) + aLocation = rpPageDescriptor->GetLocation( bIgnoreLocation ); + return GetBoundingBox(aLocation, ePart, eCoordinateSystem); +} + +::tools::Rectangle PageObjectLayouter::GetBoundingBox ( + const Point& rPageObjectLocation, + const Part ePart, + const CoordinateSystem eCoordinateSystem) +{ + ::tools::Rectangle aBoundingBox; + switch (ePart) + { + case Part::FocusIndicator: + aBoundingBox = maFocusIndicatorBoundingBox; + break; + + case Part::PageObject: + aBoundingBox = maPageObjectBoundingBox; + break; + + case Part::Preview: + aBoundingBox = maPreviewBoundingBox; + break; + + case Part::PageNumber: + aBoundingBox = maPageNumberAreaBoundingBox; + break; + + case Part::TransitionEffectIndicator: + aBoundingBox = maTransitionEffectBoundingBox; + break; + case Part::CustomAnimationEffectIndicator: + aBoundingBox = maCustomAnimationEffectBoundingBox; + break; + } + + // Adapt coordinates to the requested coordinate system. + Point aLocation (rPageObjectLocation); + if (eCoordinateSystem == WindowCoordinateSystem) + aLocation += mpWindow->GetMapMode().GetOrigin(); + + return ::tools::Rectangle( + aBoundingBox.TopLeft() + aLocation, + aBoundingBox.BottomRight() + aLocation); +} + +Size PageObjectLayouter::GetPreviewSize () +{ + return GetBoundingBox(Point(0,0), PageObjectLayouter::Part::Preview, + WindowCoordinateSystem).GetSize(); +} + +Size PageObjectLayouter::GetGridMaxSize() +{ + return GetBoundingBox(Point(0,0), PageObjectLayouter::Part::FocusIndicator, + WindowCoordinateSystem).GetSize(); +} + +Size PageObjectLayouter::GetPageNumberAreaSize (const int nPageCount) +{ + OSL_ASSERT(mpWindow); + + // Set the correct font. + vcl::Font aOriginalFont (mpWindow->GetFont()); + if (mpPageNumberFont) + mpWindow->SetFont(*mpPageNumberFont); + + OUString sPageNumberTemplate; + if (nPageCount < 10) + sPageNumberTemplate = "9"; + else if (nPageCount < 100) + sPageNumberTemplate = "99"; + else if (nPageCount < 200) + // Just for the case that 1 is narrower than 9. + sPageNumberTemplate = "199"; + else if (nPageCount < 1000) + sPageNumberTemplate = "999"; + else + sPageNumberTemplate = "9999"; + // More than 9999 pages are not handled. + + const Size aSize ( + mpWindow->GetTextWidth(sPageNumberTemplate), + mpWindow->GetTextHeight()); + + mpWindow->SetFont(aOriginalFont); + + return aSize; +} + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/view/SlsPageObjectPainter.cxx b/sd/source/ui/slidesorter/view/SlsPageObjectPainter.cxx new file mode 100644 index 000000000..feaf5a5fa --- /dev/null +++ b/sd/source/ui/slidesorter/view/SlsPageObjectPainter.cxx @@ -0,0 +1,442 @@ +/* -*- 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 <view/SlsPageObjectPainter.hxx> + +#include <model/SlsPageDescriptor.hxx> +#include <view/SlideSorterView.hxx> +#include <view/SlsPageObjectLayouter.hxx> +#include <view/SlsLayouter.hxx> +#include <view/SlsTheme.hxx> +#include <SlideSorter.hxx> +#include "SlsFramePainter.hxx" +#include <cache/SlsPageCache.hxx> +#include <Window.hxx> +#include <sdpage.hxx> +#include <vcl/virdev.hxx> +#include <CustomAnimationEffect.hxx> +#include <osl/diagnose.h> +#include <memory> + +using namespace ::drawinglayer::primitive2d; + +namespace sd::slidesorter::view { + +//===== PageObjectPainter ===================================================== + +PageObjectPainter::PageObjectPainter ( + const SlideSorter& rSlideSorter) + : mrLayouter(rSlideSorter.GetView().GetLayouter()), + mpCache(rSlideSorter.GetView().GetPreviewCache()), + mpTheme(rSlideSorter.GetTheme()), + mpPageNumberFont(Theme::GetFont(Theme::Font_PageNumber, *rSlideSorter.GetContentWindow()->GetOutDev())), + mpShadowPainter(new FramePainter(mpTheme->GetIcon(Theme::Icon_RawShadow))), + mpFocusBorderPainter(new FramePainter(mpTheme->GetIcon(Theme::Icon_FocusBorder))) +{ + // Replace the color (not the alpha values) in the focus border with a + // color derived from the current selection color. + Color aColor (mpTheme->GetColor(Theme::Color_Selection)); + sal_uInt16 nHue, nSat, nBri; + aColor.RGBtoHSB(nHue, nSat, nBri); + aColor = Color::HSBtoRGB(nHue, 28, 65); + mpFocusBorderPainter->AdaptColor(aColor); +} + +PageObjectPainter::~PageObjectPainter() +{ +} + +void PageObjectPainter::PaintPageObject ( + OutputDevice& rDevice, + const model::SharedPageDescriptor& rpDescriptor) +{ + if (!UpdatePageObjectLayouter()) + return; + + PageObjectLayouter *pPageObjectLayouter = mrLayouter.GetPageObjectLayouter().get(); + // Turn off antialiasing to avoid the bitmaps from being + // shifted by fractions of a pixel and thus show blurry edges. + const AntialiasingFlags nSavedAntialiasingMode (rDevice.GetAntialiasing()); + rDevice.SetAntialiasing(nSavedAntialiasingMode & ~AntialiasingFlags::Enable); + + PaintBackground(pPageObjectLayouter, rDevice, rpDescriptor); + PaintPreview(pPageObjectLayouter, rDevice, rpDescriptor); + PaintPageNumber(pPageObjectLayouter, rDevice, rpDescriptor); + PaintTransitionEffect(pPageObjectLayouter, rDevice, rpDescriptor); + if (rpDescriptor->GetPage()->hasAnimationNode()) + PaintCustomAnimationEffect(pPageObjectLayouter, rDevice, rpDescriptor); + rDevice.SetAntialiasing(nSavedAntialiasingMode); +} + +bool PageObjectPainter::UpdatePageObjectLayouter() +{ + // The page object layouter is quite volatile. It may have been replaced + // since the last call. Update it now. + PageObjectLayouter *pPageObjectLayouter = mrLayouter.GetPageObjectLayouter().get(); + if ( ! pPageObjectLayouter) + { + OSL_FAIL("no page object layouter"); + return false; + } + + return true; +} + +void PageObjectPainter::SetTheme (const std::shared_ptr<view::Theme>& rpTheme) +{ + mpTheme = rpTheme; +} + +void PageObjectPainter::PaintBackground ( + PageObjectLayouter *pPageObjectLayouter, + OutputDevice& rDevice, + const model::SharedPageDescriptor& rpDescriptor) const +{ + PaintBackgroundDetail(pPageObjectLayouter, rDevice, rpDescriptor); + + // Fill the interior of the preview area with the default background + // color of the page. + SdPage* pPage = rpDescriptor->GetPage(); + if (pPage != nullptr) + { + rDevice.SetFillColor(pPage->GetPageBackgroundColor(nullptr)); + rDevice.SetLineColor(pPage->GetPageBackgroundColor(nullptr)); + const ::tools::Rectangle aPreviewBox (pPageObjectLayouter->GetBoundingBox( + rpDescriptor, + PageObjectLayouter::Part::Preview, + PageObjectLayouter::ModelCoordinateSystem)); + rDevice.DrawRect(aPreviewBox); + } +} + +void PageObjectPainter::PaintPreview ( + PageObjectLayouter *pPageObjectLayouter, + OutputDevice& rDevice, + const model::SharedPageDescriptor& rpDescriptor) const +{ + const ::tools::Rectangle aBox (pPageObjectLayouter->GetBoundingBox( + rpDescriptor, + PageObjectLayouter::Part::Preview, + PageObjectLayouter::ModelCoordinateSystem)); + + if (mpCache == nullptr) + return; + + const SdrPage* pPage = rpDescriptor->GetPage(); + mpCache->SetPreciousFlag(pPage, true); + + const BitmapEx aPreview (GetPreviewBitmap(rpDescriptor, &rDevice)); + if ( ! aPreview.IsEmpty()) + { + if (aPreview.GetSizePixel() != aBox.GetSize()) + rDevice.DrawBitmapEx(aBox.TopLeft(), aBox.GetSize(), aPreview); + else + rDevice.DrawBitmapEx(aBox.TopLeft(), aPreview); + } +} + +BitmapEx PageObjectPainter::CreateMarkedPreview ( + const Size& rSize, + const BitmapEx& rPreview, + const BitmapEx& rOverlay, + const OutputDevice* pReferenceDevice) +{ + ScopedVclPtr<VirtualDevice> pDevice; + if (pReferenceDevice != nullptr) + pDevice.disposeAndReset(VclPtr<VirtualDevice>::Create(*pReferenceDevice)); + else + pDevice.disposeAndReset(VclPtr<VirtualDevice>::Create()); + pDevice->SetOutputSizePixel(rSize); + + pDevice->DrawBitmapEx(Point(0,0), rSize, rPreview); + + // Paint bitmap tiled over the preview to mark it as excluded. + const sal_Int32 nIconWidth (rOverlay.GetSizePixel().Width()); + const sal_Int32 nIconHeight (rOverlay.GetSizePixel().Height()); + if (nIconWidth>0 && nIconHeight>0) + { + for (::tools::Long nX=0; nX<rSize.Width(); nX+=nIconWidth) + for (::tools::Long nY=0; nY<rSize.Height(); nY+=nIconHeight) + pDevice->DrawBitmapEx(Point(nX,nY), rOverlay); + } + return pDevice->GetBitmapEx(Point(0,0), rSize); +} + +BitmapEx PageObjectPainter::GetPreviewBitmap ( + const model::SharedPageDescriptor& rpDescriptor, + const OutputDevice* pReferenceDevice) const +{ + const SdrPage* pPage = rpDescriptor->GetPage(); + const bool bIsExcluded (rpDescriptor->HasState(model::PageDescriptor::ST_Excluded)); + + if (bIsExcluded) + { + PageObjectLayouter *pPageObjectLayouter = mrLayouter.GetPageObjectLayouter().get(); + + BitmapEx aMarkedPreview (mpCache->GetMarkedPreviewBitmap(pPage)); + const ::tools::Rectangle aPreviewBox (pPageObjectLayouter->GetBoundingBox( + rpDescriptor, + PageObjectLayouter::Part::Preview, + PageObjectLayouter::ModelCoordinateSystem)); + if (aMarkedPreview.IsEmpty() || aMarkedPreview.GetSizePixel()!=aPreviewBox.GetSize()) + { + aMarkedPreview = CreateMarkedPreview( + aPreviewBox.GetSize(), + mpCache->GetPreviewBitmap(pPage,true), + mpTheme->GetIcon(Theme::Icon_HideSlideOverlay), + pReferenceDevice); + mpCache->SetMarkedPreviewBitmap(pPage, aMarkedPreview); + } + return aMarkedPreview; + } + else + { + return mpCache->GetPreviewBitmap(pPage,false); + } +} + +void PageObjectPainter::PaintPageNumber ( + PageObjectLayouter *pPageObjectLayouter, + OutputDevice& rDevice, + const model::SharedPageDescriptor& rpDescriptor) const +{ + const ::tools::Rectangle aBox (pPageObjectLayouter->GetBoundingBox( + rpDescriptor, + PageObjectLayouter::Part::PageNumber, + PageObjectLayouter::ModelCoordinateSystem)); + + // Determine the color of the page number. + Color aPageNumberColor (mpTheme->GetColor(Theme::Color_PageNumberDefault)); + if (rpDescriptor->HasState(model::PageDescriptor::ST_MouseOver) || + rpDescriptor->HasState(model::PageDescriptor::ST_Selected)) + { + // Page number is painted on background for hover or selection or + // both. Each of these background colors has a predefined luminance + // which is compatible with the PageNumberHover color. + aPageNumberColor = mpTheme->GetColor(Theme::Color_PageNumberHover); + } + else + { + const Color aBackgroundColor (mpTheme->GetColor(Theme::Color_Background)); + const sal_Int32 nBackgroundLuminance (aBackgroundColor.GetLuminance()); + // When the background color is black then this is interpreted as + // high contrast mode and the font color is set to white. + if (nBackgroundLuminance == 0) + aPageNumberColor = mpTheme->GetColor(Theme::Color_PageNumberHighContrast); + else + { + // Compare luminance of default page number color and background + // color. When the two are similar then use a darker + // (preferred) or brighter font color. + const sal_Int32 nFontLuminance (aPageNumberColor.GetLuminance()); + if (abs(nBackgroundLuminance - nFontLuminance) < 60) + { + if (nBackgroundLuminance > nFontLuminance-30) + aPageNumberColor = mpTheme->GetColor(Theme::Color_PageNumberBrightBackground); + else + aPageNumberColor = mpTheme->GetColor(Theme::Color_PageNumberDarkBackground); + } + } + } + + // Paint the page number. + OSL_ASSERT(rpDescriptor->GetPage()!=nullptr); + const sal_Int32 nPageNumber ((rpDescriptor->GetPage()->GetPageNum() - 1) / 2 + 1); + const OUString sPageNumber(OUString::number(nPageNumber)); + rDevice.SetFont(*mpPageNumberFont); + rDevice.SetTextColor(aPageNumberColor); + rDevice.DrawText(aBox, sPageNumber, DrawTextFlags::Right | DrawTextFlags::VCenter); +} + +void PageObjectPainter::PaintTransitionEffect ( + PageObjectLayouter *pPageObjectLayouter, + OutputDevice& rDevice, + const model::SharedPageDescriptor& rpDescriptor) +{ + const SdPage* pPage = rpDescriptor->GetPage(); + if (pPage!=nullptr && pPage->getTransitionType() > 0) + { + const ::tools::Rectangle aBox (pPageObjectLayouter->GetBoundingBox( + rpDescriptor, + PageObjectLayouter::Part::TransitionEffectIndicator, + PageObjectLayouter::ModelCoordinateSystem)); + + rDevice.DrawBitmapEx( + aBox.TopCenter(), + pPageObjectLayouter->GetTransitionEffectIcon().GetBitmapEx()); + } +} + +void PageObjectPainter::PaintCustomAnimationEffect ( + PageObjectLayouter *pPageObjectLayouter, + OutputDevice& rDevice, + const model::SharedPageDescriptor& rpDescriptor) +{ + SdPage* pPage = rpDescriptor->GetPage(); + std::shared_ptr< MainSequence > aMainSequence = pPage->getMainSequence(); + EffectSequence::iterator aIter = aMainSequence->getBegin(); + EffectSequence::iterator aEnd = aMainSequence->getEnd(); + if ( aIter != aEnd ) + { + const ::tools::Rectangle aBox (pPageObjectLayouter->GetBoundingBox( + rpDescriptor, + PageObjectLayouter::Part::CustomAnimationEffectIndicator, + PageObjectLayouter::ModelCoordinateSystem)); + rDevice.DrawBitmapEx( + aBox.TopCenter(), + pPageObjectLayouter->GetCustomAnimationEffectIcon().GetBitmapEx()); + } +} + +void PageObjectPainter::PaintBackgroundDetail ( + PageObjectLayouter *pPageObjectLayouter, + OutputDevice& rDevice, + const model::SharedPageDescriptor& rpDescriptor) const +{ + enum State { None = 0x00, Selected = 0x01, MouseOver = 0x02, Focused = 0x04 }; + const int eState = + (rpDescriptor->HasState(model::PageDescriptor::ST_Selected) ? Selected : None) + | (rpDescriptor->HasState(model::PageDescriptor::ST_MouseOver) ? MouseOver : None) + | (rpDescriptor->HasState(model::PageDescriptor::ST_Focused) ? Focused : None); + + bool bHasFocusBorder; + Theme::GradientColorType eColorType; + + switch (eState) + { + case MouseOver | Selected | Focused: + eColorType = Theme::Gradient_MouseOverSelectedAndFocusedPage; + bHasFocusBorder = true; + break; + + case MouseOver | Selected: + eColorType = Theme::Gradient_MouseOverSelected; + bHasFocusBorder = false; + break; + + case MouseOver: + eColorType = Theme::Gradient_MouseOverPage; + bHasFocusBorder = false; + break; + + case MouseOver | Focused: + eColorType = Theme::Gradient_MouseOverPage; + bHasFocusBorder = true; + break; + + case Selected | Focused: + eColorType = Theme::Gradient_SelectedAndFocusedPage; + bHasFocusBorder = true; + break; + + case Selected: + eColorType = Theme::Gradient_SelectedPage; + bHasFocusBorder = false; + break; + + case Focused: + eColorType = Theme::Gradient_FocusedPage; + bHasFocusBorder = true; + break; + + case None: + default: + eColorType = Theme::Gradient_NormalPage; + bHasFocusBorder = false; + break; + } + + const ::tools::Rectangle aFocusSize (pPageObjectLayouter->GetBoundingBox( + rpDescriptor, + PageObjectLayouter::Part::FocusIndicator, + PageObjectLayouter::ModelCoordinateSystem)); + + const ::tools::Rectangle aPageObjectBox (pPageObjectLayouter->GetBoundingBox( + rpDescriptor, + PageObjectLayouter::Part::PageObject, + PageObjectLayouter::ModelCoordinateSystem)); + + // Fill the background with the background color of the slide sorter. + const Color aBackgroundColor (mpTheme->GetColor(Theme::Color_Background)); + rDevice.SetFillColor(aBackgroundColor); + rDevice.SetLineColor(aBackgroundColor); + rDevice.DrawRect(aFocusSize); + + // Paint the slide area with a linear gradient that starts some pixels + // below the top and ends some pixels above the bottom. + const Color aTopColor(mpTheme->GetGradientColor(eColorType, Theme::GradientColorClass::Fill1)); + const Color aBottomColor(mpTheme->GetGradientColor(eColorType, Theme::GradientColorClass::Fill2)); + if (aTopColor != aBottomColor) + { + Gradient gradient(GradientStyle::Linear, aTopColor, aBottomColor); + rDevice.DrawGradient(aPageObjectBox, gradient); + } + else + { + rDevice.SetFillColor(aTopColor); + rDevice.DrawRect(aPageObjectBox); + } + + // Paint the simple border and, for some backgrounds, the focus border. + if (bHasFocusBorder) + mpFocusBorderPainter->PaintFrame(rDevice, aPageObjectBox); + else + PaintBorder(rDevice, eColorType, aPageObjectBox); + + // Get bounding box of the preview around which a shadow is painted. + // Compensate for the border around the preview. + const ::tools::Rectangle aBox (pPageObjectLayouter->GetBoundingBox( + rpDescriptor, + PageObjectLayouter::Part::Preview, + PageObjectLayouter::ModelCoordinateSystem)); + ::tools::Rectangle aFrameBox (aBox.Left()-1,aBox.Top()-1,aBox.Right()+1,aBox.Bottom()+1); + mpShadowPainter->PaintFrame(rDevice, aFrameBox); +} + +void PageObjectPainter::PaintBorder ( + OutputDevice& rDevice, + const Theme::GradientColorType eColorType, + const ::tools::Rectangle& rBox) const +{ + rDevice.SetFillColor(); + const sal_Int32 nBorderWidth (1); + for (int nIndex=0; nIndex<nBorderWidth; ++nIndex) + { + const int nDelta (nIndex); + rDevice.SetLineColor(mpTheme->GetGradientColor(eColorType, Theme::GradientColorClass::Border2)); + rDevice.DrawLine( + Point(rBox.Left()-nDelta, rBox.Top()-nDelta), + Point(rBox.Left()-nDelta, rBox.Bottom()+nDelta)); + rDevice.DrawLine( + Point(rBox.Left()-nDelta, rBox.Bottom()+nDelta), + Point(rBox.Right()+nDelta, rBox.Bottom()+nDelta)); + rDevice.DrawLine( + Point(rBox.Right()+nDelta, rBox.Bottom()+nDelta), + Point(rBox.Right()+nDelta, rBox.Top()-nDelta)); + + rDevice.SetLineColor(mpTheme->GetGradientColor(eColorType, Theme::GradientColorClass::Border1)); + rDevice.DrawLine( + Point(rBox.Left()-nDelta, rBox.Top()-nDelta), + Point(rBox.Right()+nDelta, rBox.Top()-nDelta)); + } +} + +} // end of namespace sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/view/SlsTheme.cxx b/sd/source/ui/slidesorter/view/SlsTheme.cxx new file mode 100644 index 000000000..5172e6241 --- /dev/null +++ b/sd/source/ui/slidesorter/view/SlsTheme.cxx @@ -0,0 +1,239 @@ +/* -*- 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 <bitmaps.hlst> +#include <view/SlsTheme.hxx> +#include <controller/SlsProperties.hxx> +#include <tools/color.hxx> +#include <vcl/outdev.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> + +#include <osl/diagnose.h> + +namespace sd::slidesorter::view { + +const Color Black(0x000000); +const Color White(0xffffff); + +static Color ChangeLuminance (Color aColor, const int nValue) +{ + if (nValue > 0) + aColor.IncreaseLuminance(nValue); + else + aColor.DecreaseLuminance(-nValue); + return aColor; +} + +static Color HGBAdapt ( + const Color aColor, + const sal_Int32 nNewSaturation, + const sal_Int32 nNewBrightness) +{ + sal_uInt16 nHue (0); + sal_uInt16 nSaturation (0); + sal_uInt16 nBrightness (0); + aColor.RGBtoHSB(nHue, nSaturation, nBrightness); + return Color::HSBtoRGB( + nHue, + nNewSaturation>=0 ? nNewSaturation : nSaturation, + nNewBrightness>=0 ? nNewBrightness : nBrightness); +} + +Theme::Theme (const std::shared_ptr<controller::Properties>& rpProperties) + : maBackgroundColor(rpProperties->GetBackgroundColor()) +{ + maColor.resize(ColorType_Size_); + maColor[Color_Background] = maBackgroundColor; + maColor[Color_PageNumberDefault] = Color(0x0808080); + maColor[Color_PageNumberHover] = Color(0x4c4c4c); + maColor[Color_PageNumberHighContrast] = White; + maColor[Color_PageNumberBrightBackground] = Color(0x333333); + maColor[Color_PageNumberDarkBackground] = Color(0xcccccc); + maColor[Color_PreviewBorder] = Color(0x949599); + + Update(rpProperties); +} + +void Theme::Update (const std::shared_ptr<controller::Properties>& rpProperties) +{ + // Set up colors. + maBackgroundColor = rpProperties->GetBackgroundColor(); + + maColor[Color_Background] = maBackgroundColor; + + maGradients.resize(GradientColorType_Size_); + + maColor[Color_Background] = maBackgroundColor; + const Color aSelectionColor (rpProperties->GetSelectionColor()); + maColor[Color_Selection] = aSelectionColor; + if (aSelectionColor.IsBright()) + maColor[Color_PageCountFontColor] = Black; + else + maColor[Color_PageCountFontColor] = White; + + // Set up gradients. + SetGradient(Gradient_MouseOverPage, aSelectionColor, 0, 60, +80,+100, +50,+25); + SetGradient(Gradient_SelectedPage, aSelectionColor, 50, 50, +80,+100, +50,+25); + SetGradient(Gradient_FocusedPage, aSelectionColor, -1,-1, 0,0, -50,-75); + SetGradient(Gradient_MouseOverSelected, aSelectionColor, 55, 60, +80,+100, +50,+25); + SetGradient(Gradient_SelectedAndFocusedPage, aSelectionColor, 50, 50, +80,+100, -50,-75); + SetGradient(Gradient_MouseOverSelectedAndFocusedPage, aSelectionColor, 55, 60, +80,+100, -50,-75); + + SetGradient(Gradient_NormalPage, maBackgroundColor, -1,-1, 0,0, 0,0); + + // The focused gradient needs special handling because its fill color is + // like that of the NormalPage gradient. + GetGradient(Gradient_FocusedPage).maFillColor1 = GetGradient(Gradient_NormalPage).maFillColor1; + GetGradient(Gradient_FocusedPage).maFillColor2 = GetGradient(Gradient_NormalPage).maFillColor2; + + // Set up icons. + if (maIcons.empty()) + { + maIcons.resize(IconType_Size_); + + InitializeIcon(Icon_RawShadow, IMAGE_SHADOW); + InitializeIcon(Icon_RawInsertShadow, IMAGE_INSERT_SHADOW); + InitializeIcon(Icon_HideSlideOverlay, IMAGE_HIDE_SLIDE_OVERLAY); + InitializeIcon(Icon_FocusBorder, IMAGE_FOCUS_BORDER); + } +} + +std::shared_ptr<vcl::Font> Theme::GetFont ( + const FontType eType, + const OutputDevice& rDevice) +{ + std::shared_ptr<vcl::Font> pFont; + + switch (eType) + { + case Font_PageNumber: + pFont = std::make_shared<vcl::Font>(Application::GetSettings().GetStyleSettings().GetAppFont()); + pFont->SetTransparent(true); + pFont->SetWeight(WEIGHT_BOLD); + break; + + case Font_PageCount: + pFont = std::make_shared<vcl::Font>(Application::GetSettings().GetStyleSettings().GetAppFont()); + pFont->SetTransparent(true); + pFont->SetWeight(WEIGHT_NORMAL); + { + const Size aSize (pFont->GetFontSize()); + pFont->SetFontSize(Size(aSize.Width()*5/3, aSize.Height()*5/3)); + } + break; + } + + if (pFont) + { + // Transform the point size to pixel size. + const MapMode aFontMapMode (MapUnit::MapPoint); + const Size aFontSize (rDevice.LogicToPixel(pFont->GetFontSize(), aFontMapMode)); + + // Transform the font size to the logical coordinates of the device. + pFont->SetFontSize(rDevice.PixelToLogic(aFontSize)); + } + + return pFont; +} + +Color Theme::GetColor (const ColorType eType) +{ + if (sal_uInt32(eType)<maColor.size()) + return maColor[eType]; + else + return Color(0); +} + +Color Theme::GetGradientColor ( + const GradientColorType eType, + const GradientColorClass eClass) +{ + GradientDescriptor& rDescriptor (GetGradient(eType)); + + switch (eClass) + { + case GradientColorClass::Border1: return rDescriptor.maBorderColor1; + case GradientColorClass::Border2: return rDescriptor.maBorderColor2; + case GradientColorClass::Fill1: return rDescriptor.maFillColor1; + case GradientColorClass::Fill2: return rDescriptor.maFillColor2; + } + return Color(0); +} + +void Theme::SetGradient ( + const GradientColorType eType, + const Color aBaseColor, + const sal_Int32 nSaturationOverride, + const sal_Int32 nBrightnessOverride, + const sal_Int32 nFillStartOffset, + const sal_Int32 nFillEndOffset, + const sal_Int32 nBorderStartOffset, + const sal_Int32 nBorderEndOffset) +{ + GradientDescriptor& rGradient (GetGradient(eType)); + + const Color aColor (nSaturationOverride>=0 || nBrightnessOverride>=0 + ? HGBAdapt(aBaseColor, nSaturationOverride, nBrightnessOverride) + : aBaseColor); + + rGradient.maFillColor1 = ChangeLuminance(aColor, nFillStartOffset); + rGradient.maFillColor2 = ChangeLuminance(aColor, nFillEndOffset); + rGradient.maBorderColor1 = ChangeLuminance(aColor, nBorderStartOffset); + rGradient.maBorderColor2 = ChangeLuminance(aColor, nBorderEndOffset); +} + +const BitmapEx& Theme::GetIcon (const IconType eType) +{ + if (size_t(eType)<maIcons.size()) + return maIcons[eType]; + else + { + OSL_ASSERT(eType>=0 && size_t(eType)<maIcons.size()); + return maIcons[0]; + } +} + +Theme::GradientDescriptor& Theme::GetGradient (const GradientColorType eType) +{ + if (size_t(eType)<maGradients.size()) + return maGradients[eType]; + else + { + OSL_ASSERT(eType>=0 && size_t(eType)<maGradients.size()); + return maGradients[0]; + } +} + +void Theme::InitializeIcon(const IconType eType, const OUString& rResourceId) +{ + if (size_t(eType)<maIcons.size()) + { + const BitmapEx aIcon(rResourceId); + maIcons[eType] = aIcon; + } + else + { + OSL_ASSERT(eType>=0 && size_t(eType)<maIcons.size()); + } +} + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/view/SlsToolTip.cxx b/sd/source/ui/slidesorter/view/SlsToolTip.cxx new file mode 100644 index 000000000..c266bbe3d --- /dev/null +++ b/sd/source/ui/slidesorter/view/SlsToolTip.cxx @@ -0,0 +1,160 @@ +/* -*- 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 <view/SlsPageObjectLayouter.hxx> +#include <view/SlsToolTip.hxx> +#include <view/SlideSorterView.hxx> +#include <view/SlsLayouter.hxx> +#include <SlideSorter.hxx> +#include <Window.hxx> +#include <sdpage.hxx> +#include <sdresid.hxx> +#include <strings.hrc> + +#include <osl/diagnose.h> +#include <vcl/settings.hxx> +#include <vcl/help.hxx> + +namespace sd::slidesorter::view { + +ToolTip::ToolTip (SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter), + mnHelpWindowHandle(nullptr), + maShowTimer("sd::slidesorter::view::ToolTip maShowTimer"), + maHiddenTimer("sd::slidesorter::view::ToolTip maHiddenTimer") +{ + maShowTimer.SetTimeout(HelpSettings::GetTipDelay()); + maShowTimer.SetInvokeHandler(LINK(this, ToolTip, DelayTrigger)); + maHiddenTimer.SetTimeout(HelpSettings::GetTipDelay()); +} + +ToolTip::~ToolTip() +{ + maShowTimer.Stop(); + maHiddenTimer.Stop(); + Hide(); +} + +void ToolTip::SetPage (const model::SharedPageDescriptor& rpDescriptor) +{ + if (mpDescriptor == rpDescriptor) + return; + + maShowTimer.Stop(); + bool bWasVisible = Hide(); + + if (bWasVisible) + { + maHiddenTimer.Start(); + } + + mpDescriptor = rpDescriptor; + + if (mpDescriptor) + { + SdPage* pPage = mpDescriptor->GetPage(); + OUString sHelpText; + if (pPage != nullptr) + sHelpText = pPage->GetName(); + else + { + OSL_ASSERT(mpDescriptor->GetPage() != nullptr); + } + if (sHelpText.isEmpty()) + { + sHelpText = SdResId(STR_PAGE) + + OUString::number(mpDescriptor->GetPageIndex()+1); + } + + msCurrentHelpText = sHelpText; + // show new tooltip immediately, if last one was recently hidden + if(maHiddenTimer.IsActive()) + DoShow(); + else + maShowTimer.Start(); + } + else + { + msCurrentHelpText.clear(); + } +} + +void ToolTip::DoShow() +{ + if (maShowTimer.IsActive()) + { + // The delay timer is active. Wait for it to trigger the showing of + // the tool tip. + return; + } + + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if (msCurrentHelpText.isEmpty() || !pWindow) + return; + + ::tools::Rectangle aBox ( + mrSlideSorter.GetView().GetLayouter().GetPageObjectLayouter()->GetBoundingBox( + mpDescriptor, + PageObjectLayouter::Part::Preview, + PageObjectLayouter::WindowCoordinateSystem)); + + // Do not show the help text when the (lower edge of the ) preview + // is not visible. The tool tip itself may still be outside the + // window. + if (aBox.Bottom() >= pWindow->GetSizePixel().Height()) + return; + + vcl::Window* pParent (pWindow); + while (pParent!=nullptr && pParent->GetParent()!=nullptr) + pParent = pParent->GetParent(); + const Point aOffset (pWindow->GetWindowExtentsRelative(pParent).TopLeft()); + + // We do not know how high the tool tip will be but want its top + // edge not its bottom to be at a specific position (a little below + // the preview). Therefore we use a little trick and place the tool + // tip at the top of a rectangle that is placed below the preview. + aBox.Move(aOffset.X(), aOffset.Y() + aBox.GetHeight() + 3); + mnHelpWindowHandle = Help::ShowPopover( + pWindow, + aBox, + msCurrentHelpText, + QuickHelpFlags::Center | QuickHelpFlags::Top); +} + +bool ToolTip::Hide() +{ + if (mnHelpWindowHandle) + { + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + Help::HidePopover(pWindow, mnHelpWindowHandle); + mnHelpWindowHandle = nullptr; + return true; + } + else + return false; +} + +IMPL_LINK_NOARG(ToolTip, DelayTrigger, Timer *, void) +{ + DoShow(); +} + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/view/SlsViewCacheContext.cxx b/sd/source/ui/slidesorter/view/SlsViewCacheContext.cxx new file mode 100644 index 000000000..ce27cec28 --- /dev/null +++ b/sd/source/ui/slidesorter/view/SlsViewCacheContext.cxx @@ -0,0 +1,117 @@ +/* -*- 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 "SlsViewCacheContext.hxx" + +#include <SlideSorter.hxx> +#include <model/SlideSorterModel.hxx> +#include <model/SlsPageDescriptor.hxx> +#include <model/SlsPageEnumerationProvider.hxx> +#include <view/SlideSorterView.hxx> +#include <sdpage.hxx> +#include <Window.hxx> +#include <drawdoc.hxx> +#include <tools/IdleDetection.hxx> +#include <svx/svdpage.hxx> + +namespace sd::slidesorter::view { + +ViewCacheContext::ViewCacheContext (SlideSorter& rSlideSorter) + : mrModel(rSlideSorter.GetModel()), + mrSlideSorter(rSlideSorter) +{ +} + +ViewCacheContext::~ViewCacheContext() +{ +} + +void ViewCacheContext::NotifyPreviewCreation(cache::CacheKey aKey) +{ + const model::SharedPageDescriptor pDescriptor (GetDescriptor(aKey)); + if (pDescriptor) + { + // Force a repaint that will trigger their re-creation. + mrSlideSorter.GetView().RequestRepaint(pDescriptor); + } + else + { + // It is OK when a preview was created for a page that is not + // currently displayed because both normal and master pages are + // kept in the same cache. + } +} + +bool ViewCacheContext::IsIdle() +{ + tools::IdleState nIdleState (tools::IdleDetection::GetIdleState(mrSlideSorter.GetContentWindow())); + return nIdleState == tools::IdleState::Idle; +} + +bool ViewCacheContext::IsVisible (cache::CacheKey aKey) +{ + const model::SharedPageDescriptor pDescriptor (GetDescriptor(aKey)); + return pDescriptor && pDescriptor->HasState(model::PageDescriptor::ST_Visible); +} + +const SdrPage* ViewCacheContext::GetPage (cache::CacheKey aKey) +{ + return aKey; +} + +std::shared_ptr<std::vector<cache::CacheKey> > ViewCacheContext::GetEntryList (bool bVisible) +{ + auto pKeys = std::make_shared<std::vector<cache::CacheKey>>(); + + model::PageEnumeration aPageEnumeration ( + bVisible + ? model::PageEnumerationProvider::CreateVisiblePagesEnumeration(mrModel) + : model::PageEnumerationProvider::CreateAllPagesEnumeration(mrModel)); + + while (aPageEnumeration.HasMoreElements()) + { + model::SharedPageDescriptor pDescriptor (aPageEnumeration.GetNextElement()); + pKeys->push_back(pDescriptor->GetPage()); + } + + return pKeys; +} + +sal_Int32 ViewCacheContext::GetPriority (cache::CacheKey aKey) +{ + return - (aKey->GetPageNum()-1) / 2; +} + +model::SharedPageDescriptor ViewCacheContext::GetDescriptor (cache::CacheKey aKey) +{ + sal_uInt16 nPageIndex ((aKey->GetPageNum() - 1) / 2); + return mrModel.GetPageDescriptor(nPageIndex); +} + +css::uno::Reference<css::uno::XInterface> ViewCacheContext::GetModel() +{ + if (mrModel.GetDocument() == nullptr) + return nullptr; + else + return mrModel.GetDocument()->getUnoModel(); +} + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/view/SlsViewCacheContext.hxx b/sd/source/ui/slidesorter/view/SlsViewCacheContext.hxx new file mode 100644 index 000000000..501517cb8 --- /dev/null +++ b/sd/source/ui/slidesorter/view/SlsViewCacheContext.hxx @@ -0,0 +1,61 @@ +/* -*- 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 . + */ + +#pragma once + +#include <cache/SlsCacheContext.hxx> +#include <model/SlsSharedPageDescriptor.hxx> + +namespace sd::slidesorter::model +{ +class SlideSorterModel; +} +namespace sd::slidesorter +{ +class SlideSorter; +} + +namespace sd::slidesorter::view +{ +/** The cache context for the SlideSorter as used by Draw and Impress. See + the base class for documentation of the individual methods. +*/ +class ViewCacheContext : public cache::CacheContext +{ +public: + explicit ViewCacheContext(SlideSorter& rSlideSorter); + virtual ~ViewCacheContext() override; + virtual void NotifyPreviewCreation(cache::CacheKey aKey) override; + virtual bool IsIdle() override; + virtual bool IsVisible(cache::CacheKey aKey) override; + virtual const SdrPage* GetPage(cache::CacheKey aKey) override; + virtual std::shared_ptr<std::vector<cache::CacheKey>> GetEntryList(bool bVisible) override; + virtual sal_Int32 GetPriority(cache::CacheKey aKey) override; + virtual css::uno::Reference<css::uno::XInterface> GetModel() override; + +private: + model::SlideSorterModel& mrModel; + SlideSorter& mrSlideSorter; + + model::SharedPageDescriptor GetDescriptor(cache::CacheKey aKey); +}; + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/table/TableDesignPane.cxx b/sd/source/ui/table/TableDesignPane.cxx new file mode 100644 index 000000000..82d4485fb --- /dev/null +++ b/sd/source/ui/table/TableDesignPane.cxx @@ -0,0 +1,763 @@ +/* -*- 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 <sal/config.h> + +#include <string_view> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/drawing/XDrawView.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include <com/sun/star/style/XStyle.hpp> +#include <com/sun/star/style/XStyleFamiliesSupplier.hpp> + +#include <comphelper/sequence.hxx> +#include <sfx2/viewfrm.hxx> +#include <vcl/image.hxx> +#include <vcl/settings.hxx> +#include <vcl/virdev.hxx> + +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> +#include <svl/style.hxx> +#include <svl/stritem.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/app.hxx> +#include <sfx2/request.hxx> +#include <sfx2/dispatch.hxx> +#include <svx/svxids.hrc> +#include <svx/svdetc.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/borderline.hxx> +#include <editeng/colritem.hxx> +#include <editeng/eeitem.hxx> +#include <svx/sdr/table/tabledesign.hxx> +#include <o3tl/enumrange.hxx> + +#include <TableDesignPane.hxx> + +#include <ViewShell.hxx> +#include <ViewShellBase.hxx> +#include <EventMultiplexer.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::view; +using namespace ::com::sun::star::style; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::ui; + +namespace sd { + +const sal_Int32 nPreviewColumns = 5; +const sal_Int32 nPreviewRows = 5; +const sal_Int32 nCellWidth = 12; // one pixel is shared with the next cell! +const sal_Int32 nCellHeight = 7; // one pixel is shared with the next cell! +const sal_Int32 nBitmapWidth = (nCellWidth * nPreviewColumns) - (nPreviewColumns - 1); +const sal_Int32 nBitmapHeight = (nCellHeight * nPreviewRows) - (nPreviewRows - 1); + +const std::string_view gPropNames[CB_COUNT] = +{ + "UseFirstRowStyle", + "UseLastRowStyle", + "UseBandingRowStyle", + "UseFirstColumnStyle", + "UseLastColumnStyle", + "UseBandingColumnStyle" +}; + +TableDesignWidget::TableDesignWidget(weld::Builder& rBuilder, ViewShellBase& rBase) + : mrBase(rBase) + , m_xValueSet(new TableValueSet(rBuilder.weld_scrolled_window("previewswin", true))) + , m_xValueSetWin(new weld::CustomWeld(rBuilder, "previews", *m_xValueSet)) +{ + m_xValueSet->SetStyle(m_xValueSet->GetStyle() | WB_NO_DIRECTSELECT | WB_FLATVALUESET | WB_ITEMBORDER); + m_xValueSet->SetExtraSpacing(8); + m_xValueSet->setModal(false); + m_xValueSet->SetColor(); + m_xValueSet->SetSelectHdl(LINK(this, TableDesignWidget, implValueSetHdl)); + + for (sal_uInt16 i = CB_HEADER_ROW; i <= CB_BANDED_COLUMNS; ++i) + { + m_aCheckBoxes[i] = rBuilder.weld_check_button(OString(gPropNames[i].data(), gPropNames[i].size())); + m_aCheckBoxes[i]->connect_toggled(LINK(this, TableDesignWidget, implCheckBoxHdl)); + } + + // get current controller and initialize listeners + try + { + mxView.set(mrBase.GetController(), UNO_QUERY); + addListener(); + + Reference< XController > xController( mrBase.GetController(), UNO_SET_THROW ); + Reference< XStyleFamiliesSupplier > xFamiliesSupp( xController->getModel(), UNO_QUERY_THROW ); + Reference< XNameAccess > xFamilies( xFamiliesSupp->getStyleFamilies() ); + mxTableFamily.set( xFamilies->getByName( "table" ), UNO_QUERY_THROW ); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationPane::CustomAnimationPane()" ); + } + + onSelectionChanged(); + updateControls(); +} + +TableDesignWidget::~TableDesignWidget() +{ + removeListener(); +} + +static SfxBindings* getBindings( ViewShellBase const & rBase ) +{ + if( rBase.GetMainViewShell() && rBase.GetMainViewShell()->GetViewFrame() ) + return &rBase.GetMainViewShell()->GetViewFrame()->GetBindings(); + else + return nullptr; +} + +static SfxDispatcher* getDispatcher( ViewShellBase const & rBase ) +{ + if( rBase.GetMainViewShell() && rBase.GetMainViewShell()->GetViewFrame() ) + return rBase.GetMainViewShell()->GetViewFrame()->GetDispatcher(); + else + return nullptr; +} + +IMPL_LINK_NOARG(TableDesignWidget, implValueSetHdl, ValueSet*, void) +{ + ApplyStyle(); +} + +void TableDesignWidget::ApplyStyle() +{ + try + { + OUString sStyleName; + sal_Int32 nIndex = static_cast< sal_Int32 >( m_xValueSet->GetSelectedItemId() ) - 1; + + if( (nIndex >= 0) && (nIndex < mxTableFamily->getCount()) ) + { + Reference< XNameAccess > xNames( mxTableFamily, UNO_QUERY_THROW ); + sStyleName = xNames->getElementNames()[nIndex]; + } + + if( sStyleName.isEmpty() ) + return; + + SdrView* pView = mrBase.GetDrawView(); + if( mxSelectedTable.is() ) + { + if( pView ) + { + SfxRequest aReq( SID_TABLE_STYLE, SfxCallMode::SYNCHRON, SfxGetpApp()->GetPool() ); + aReq.AppendItem( SfxStringItem( SID_TABLE_STYLE, sStyleName ) ); + + const rtl::Reference< sdr::SelectionController >& xController( pView->getSelectionController() ); + if( xController.is() ) + xController->Execute( aReq ); + + SfxBindings* pBindings = getBindings( mrBase ); + if( pBindings ) + { + pBindings->Invalidate( SID_UNDO ); + pBindings->Invalidate( SID_REDO ); + } + } + } + else + { + SfxDispatcher* pDispatcher = getDispatcher( mrBase ); + SfxStringItem aArg( SID_TABLE_STYLE, sStyleName ); + pDispatcher->ExecuteList(SID_INSERT_TABLE, SfxCallMode::ASYNCHRON, + { &aArg }); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "TableDesignWidget::implValueSetHdl()"); + } +} + +IMPL_LINK_NOARG(TableDesignWidget, implCheckBoxHdl, weld::Toggleable&, void) +{ + ApplyOptions(); + FillDesignPreviewControl(); +} + +void TableDesignWidget::ApplyOptions() +{ + static const sal_uInt16 gParamIds[CB_COUNT] = + { + ID_VAL_USEFIRSTROWSTYLE, ID_VAL_USELASTROWSTYLE, ID_VAL_USEBANDINGROWSTYLE, + ID_VAL_USEFIRSTCOLUMNSTYLE, ID_VAL_USELASTCOLUMNSTYLE, ID_VAL_USEBANDINGCOLUMNSTYLE + }; + + if( !mxSelectedTable.is() ) + return; + + SfxRequest aReq( SID_TABLE_STYLE_SETTINGS, SfxCallMode::SYNCHRON, SfxGetpApp()->GetPool() ); + + for( sal_uInt16 i = CB_HEADER_ROW; i <= CB_BANDED_COLUMNS; ++i ) + { + aReq.AppendItem( SfxBoolItem( gParamIds[i], m_aCheckBoxes[i]->get_active() ) ); + } + + SdrView* pView = mrBase.GetDrawView(); + if( !pView ) + return; + + const rtl::Reference< sdr::SelectionController >& xController( pView->getSelectionController() ); + if( xController.is() ) + { + xController->Execute( aReq ); + + SfxBindings* pBindings = getBindings( mrBase ); + if( pBindings ) + { + pBindings->Invalidate( SID_UNDO ); + pBindings->Invalidate( SID_REDO ); + } + } +} + +void TableDesignWidget::onSelectionChanged() +{ + Reference< XPropertySet > xNewSelection; + + if( mxView.is() ) try + { + Reference< XSelectionSupplier > xSel( mxView, UNO_QUERY_THROW ); + Any aSel( xSel->getSelection() ); + Sequence< XShape > xShapeSeq; + if( aSel >>= xShapeSeq ) + { + if( xShapeSeq.getLength() == 1 ) + aSel <<= xShapeSeq[0]; + } + else + { + Reference< XShapes > xShapes( aSel, UNO_QUERY ); + if( xShapes.is() && (xShapes->getCount() == 1) ) + aSel = xShapes->getByIndex(0); + } + + Reference< XShapeDescriptor > xDesc( aSel, UNO_QUERY ); + if( xDesc.is() && ( xDesc->getShapeType() == "com.sun.star.drawing.TableShape" || xDesc->getShapeType() == "com.sun.star.presentation.TableShape" ) ) + { + xNewSelection.set( xDesc, UNO_QUERY ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::TableDesignWidget::onSelectionChanged()" ); + } + + if( mxSelectedTable != xNewSelection ) + { + mxSelectedTable = xNewSelection; + updateControls(); + } +} + +void TableValueSet::Resize() +{ + ValueSet::Resize(); + // Calculate the number of rows and columns. + if( GetItemCount() <= 0 ) + return; + + Size aValueSetSize = GetOutputSizePixel(); + + Image aImage = GetItemImage(GetItemId(0)); + Size aItemSize = aImage.GetSizePixel(); + + aItemSize.AdjustHeight(10 ); + int nColumnCount = (aValueSetSize.Width() - GetScrollWidth()) / aItemSize.Width(); + if (nColumnCount < 1) + nColumnCount = 1; + + int nRowCount = (GetItemCount() + nColumnCount - 1) / nColumnCount; + if (nRowCount < 1) + nRowCount = 1; + + int nVisibleRowCount = (aValueSetSize.Height()+2) / aItemSize.Height(); + + SetColCount (static_cast<sal_uInt16>(nColumnCount)); + SetLineCount (static_cast<sal_uInt16>(nRowCount)); + + if( !m_bModal ) + { + WinBits nStyle = GetStyle() & ~WB_VSCROLL; + if( nRowCount > nVisibleRowCount ) + { + nStyle |= WB_VSCROLL; + } + SetStyle( nStyle ); + } +} + +TableValueSet::TableValueSet(std::unique_ptr<weld::ScrolledWindow> pScrolledWindow) + : ValueSet(std::move(pScrolledWindow)) + , m_bModal(false) +{ +} + +void TableValueSet::StyleUpdated() +{ + updateSettings(); +} + +void TableValueSet::updateSettings() +{ + if( !m_bModal ) + { + Color aColor = Application::GetSettings().GetStyleSettings().GetWindowColor(); + SetColor(aColor); + SetExtraSpacing(8); + } +} + +void TableDesignWidget::updateControls() +{ + static const bool gDefaults[CB_COUNT] = { true, false, true, false, false, false }; + + const bool bHasTable = mxSelectedTable.is(); + + for (sal_uInt16 i = CB_HEADER_ROW; i <= CB_BANDED_COLUMNS; ++i) + { + bool bUse = gDefaults[i]; + if( bHasTable ) try + { + mxSelectedTable->getPropertyValue( OUString::createFromAscii(gPropNames[i]) ) >>= bUse; + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::TableDesignWidget::updateControls()"); + } + m_aCheckBoxes[i]->set_active(bUse); + m_aCheckBoxes[i]->set_sensitive(bHasTable); + } + + FillDesignPreviewControl(); + m_xValueSet->updateSettings(); + m_xValueSet->Resize(); + + sal_uInt16 nSelection = 0; + if( mxSelectedTable.is() ) + { + Reference< XNamed > xNamed( mxSelectedTable->getPropertyValue( "TableTemplate" ), UNO_QUERY ); + if( xNamed.is() ) + { + const OUString sStyleName( xNamed->getName() ); + + Reference< XNameAccess > xNames( mxTableFamily, UNO_QUERY ); + if( xNames.is() ) + { + Sequence< OUString > aNames( xNames->getElementNames() ); + sal_Int32 nIndex = comphelper::findValue(aNames, sStyleName); + if (nIndex != -1) + nSelection = static_cast<sal_uInt16>(nIndex) + 1; + } + } + } + m_xValueSet->SelectItem( nSelection ); +} + +void TableDesignWidget::addListener() +{ + Link<tools::EventMultiplexerEvent&,void> aLink( LINK(this,TableDesignWidget,EventMultiplexerListener) ); + mrBase.GetEventMultiplexer()->AddEventListener( aLink ); +} + +void TableDesignWidget::removeListener() +{ + Link<tools::EventMultiplexerEvent&,void> aLink( LINK(this,TableDesignWidget,EventMultiplexerListener) ); + mrBase.GetEventMultiplexer()->RemoveEventListener( aLink ); +} + +IMPL_LINK(TableDesignWidget,EventMultiplexerListener, + tools::EventMultiplexerEvent&, rEvent, void) +{ + switch (rEvent.meEventId) + { + case EventMultiplexerEventId::CurrentPageChanged: + case EventMultiplexerEventId::EditViewSelection: + onSelectionChanged(); + break; + + case EventMultiplexerEventId::MainViewRemoved: + mxView.clear(); + onSelectionChanged(); + break; + + case EventMultiplexerEventId::MainViewAdded: + mxView.set( mrBase.GetController(), UNO_QUERY ); + onSelectionChanged(); + break; + + default: break; + } +} + +namespace { + +struct CellInfo +{ + Color maCellColor; + Color maTextColor; + std::shared_ptr<SvxBoxItem> maBorder; + + explicit CellInfo( const Reference< XStyle >& xStyle ); +}; + +} + +CellInfo::CellInfo( const Reference< XStyle >& xStyle ) +: maBorder(std::make_shared<SvxBoxItem>(SDRATTR_TABLE_BORDER)) +{ + SfxStyleSheet* pStyleSheet = SfxUnoStyleSheet::getUnoStyleSheet( xStyle ); + if( !pStyleSheet ) + return; + + SfxItemSet& rSet = pStyleSheet->GetItemSet(); + + // get style fill color + if( !GetDraftFillColor(rSet, maCellColor) ) + maCellColor = COL_TRANSPARENT; + + // get style text color + const SvxColorItem* pTextColor = rSet.GetItem(EE_CHAR_COLOR); + if( pTextColor ) + maTextColor = pTextColor->GetValue(); + else + maTextColor = COL_TRANSPARENT; + + // get border + const SvxBoxItem* pBoxItem = rSet.GetItem( SDRATTR_TABLE_BORDER ); + if( pBoxItem ) + maBorder.reset(pBoxItem->Clone()); +} + +typedef std::vector< std::shared_ptr< CellInfo > > CellInfoVector; +typedef std::shared_ptr< CellInfo > CellInfoMatrix[nPreviewColumns * nPreviewRows]; + +namespace { + +struct TableStyleSettings +{ + bool mbUseFirstRow; + bool mbUseLastRow; + bool mbUseFirstColumn; + bool mbUseLastColumn; + bool mbUseRowBanding; + bool mbUseColumnBanding; + + TableStyleSettings() + : mbUseFirstRow(true) + , mbUseLastRow(false) + , mbUseFirstColumn(false) + , mbUseLastColumn(false) + , mbUseRowBanding(true) + , mbUseColumnBanding(false) {} +}; + +} + +static void FillCellInfoVector( const Reference< XIndexAccess >& xTableStyle, CellInfoVector& rVector ) +{ + DBG_ASSERT( xTableStyle.is() && (xTableStyle->getCount() == sdr::table::style_count ), "sd::FillCellInfoVector(), invalid table style!" ); + if( !xTableStyle.is() ) + return; + + try + { + rVector.resize( sdr::table::style_count ); + + for( sal_Int32 nStyle = 0; nStyle < sdr::table::style_count; ++nStyle ) + { + Reference< XStyle > xStyle( xTableStyle->getByIndex( nStyle ), UNO_QUERY ); + if( xStyle.is() ) + rVector[nStyle] = std::make_shared<CellInfo>( xStyle ); + } + } + catch(Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::FillCellInfoVector()"); + } +} + +static void FillCellInfoMatrix( const CellInfoVector& rStyle, const TableStyleSettings& rSettings, CellInfoMatrix& rMatrix ) +{ + for( sal_Int32 nRow = 0; nRow < nPreviewColumns; ++nRow ) + { + const bool bFirstRow = rSettings.mbUseFirstRow && (nRow == 0); + const bool bLastRow = rSettings.mbUseLastRow && (nRow == nPreviewColumns - 1); + + for( sal_Int32 nCol = 0; nCol < nPreviewColumns; ++nCol ) + { + std::shared_ptr< CellInfo > xCellInfo; + + // first and last row win first, if used and available + if( bFirstRow ) + { + xCellInfo = rStyle[sdr::table::first_row_style]; + } + else if( bLastRow ) + { + xCellInfo = rStyle[sdr::table::last_row_style]; + } + + if( !xCellInfo ) + { + // next come first and last column, if used and available + if( rSettings.mbUseFirstColumn && (nCol == 0) ) + { + xCellInfo = rStyle[sdr::table::first_column_style]; + } + else if( rSettings.mbUseLastColumn && (nCol == nPreviewColumns-1) ) + { + xCellInfo = rStyle[sdr::table::last_column_style]; + } + } + + if( !xCellInfo ) + { + if( rSettings.mbUseRowBanding ) + { + if( (nRow & 1) == 0 ) + { + xCellInfo = rStyle[sdr::table::even_rows_style]; + } + else + { + xCellInfo = rStyle[sdr::table::odd_rows_style]; + } + } + } + + if( !xCellInfo ) + { + if( rSettings.mbUseColumnBanding ) + { + if( (nCol & 1) == 0 ) + { + xCellInfo = rStyle[sdr::table::even_columns_style]; + } + else + { + xCellInfo = rStyle[sdr::table::odd_columns_style]; + } + } + } + + if( !xCellInfo ) + { + // use default cell style if non found yet + xCellInfo = rStyle[sdr::table::body_style]; + } + + rMatrix[(nCol * nPreviewColumns) + nRow] = xCellInfo; + } + } +} + +static BitmapEx CreateDesignPreview( const Reference< XIndexAccess >& xTableStyle, const TableStyleSettings& rSettings, bool bIsPageDark ) +{ + CellInfoVector aCellInfoVector(sdr::table::style_count); + FillCellInfoVector( xTableStyle, aCellInfoVector ); + + CellInfoMatrix aMatrix; + FillCellInfoMatrix( aCellInfoVector, rSettings, aMatrix ); + + // bbbbbbbbbbbb w = 12 pixel + // bccccccccccb h = 7 pixel + // bccccccccccb b = border color + // bcttttttttcb c = cell color + // bccccccccccb t = text color + // bccccccccccb + // bbbbbbbbbbbb + + ScopedVclPtr<VirtualDevice> pVirDev(VclPtr<VirtualDevice>::Create()); + Size aBmpSize(nBitmapWidth, nBitmapHeight); + pVirDev->SetOutputSizePixel(aBmpSize); + + pVirDev->SetBackground( bIsPageDark ? COL_BLACK : COL_WHITE ); + pVirDev->Erase(); + + // first draw cell background and text line previews + sal_Int32 nY = 0; + sal_Int32 nRow; + for( nRow = 0; nRow < nPreviewRows; ++nRow, nY += nCellHeight-1 ) + { + sal_Int32 nX = 0; + for( sal_Int32 nCol = 0; nCol < nPreviewColumns; ++nCol, nX += nCellWidth-1 ) + { + std::shared_ptr< CellInfo > xCellInfo(aMatrix[(nCol * nPreviewColumns) + nRow]); + + Color aTextColor( COL_AUTO ); + if( xCellInfo ) + { + // fill cell background + const ::tools::Rectangle aRect( nX, nY, nX + nCellWidth - 1, nY + nCellHeight - 1 ); + + if( xCellInfo->maCellColor != COL_TRANSPARENT ) + { + pVirDev->SetFillColor( xCellInfo->maCellColor ); + pVirDev->DrawRect( aRect ); + } + + aTextColor = xCellInfo->maTextColor; + } + + // draw text preview line + if( aTextColor == COL_AUTO ) + aTextColor = bIsPageDark ? COL_WHITE : COL_BLACK; + pVirDev->SetLineColor( aTextColor ); + const Point aPnt1( nX + 2, nY + ((nCellHeight - 1 ) >> 1) ); + const Point aPnt2( nX + nCellWidth - 3, aPnt1.Y() ); + pVirDev->DrawLine( aPnt1, aPnt2 ); + } + } + + // second draw border lines + nY = 0; + for( nRow = 0; nRow < nPreviewRows; ++nRow, nY += nCellHeight-1 ) + { + sal_Int32 nX = 0; + for( sal_Int32 nCol = 0; nCol < nPreviewColumns; ++nCol, nX += nCellWidth-1 ) + { + std::shared_ptr< CellInfo > xCellInfo(aMatrix[(nCol * nPreviewColumns) + nRow]); + + if( xCellInfo ) + { + const Point aPntTL( nX, nY ); + const Point aPntTR( nX + nCellWidth - 1, nY ); + const Point aPntBL( nX, nY + nCellHeight - 1 ); + const Point aPntBR( nX + nCellWidth - 1, nY + nCellHeight - 1 ); + + sal_Int32 border_diffs[8] = { 0,-1, 0,1, -1,0, 1,0 }; + sal_Int32* pDiff = &border_diffs[0]; + + // draw top border + for( SvxBoxItemLine nLine : o3tl::enumrange<SvxBoxItemLine>() ) + { + const ::editeng::SvxBorderLine* pBorderLine = xCellInfo->maBorder->GetLine(nLine); + if( !pBorderLine || ((pBorderLine->GetOutWidth() == 0) && (pBorderLine->GetInWidth()==0)) ) + continue; + + sal_Int32 nBorderCol = nCol + *pDiff++; + sal_Int32 nBorderRow = nRow + *pDiff++; + if( (nBorderCol >= 0) && (nBorderCol < nPreviewColumns) && (nBorderRow >= 0) && (nBorderRow < nPreviewRows) ) + { + // check border + std::shared_ptr< CellInfo > xBorderInfo(aMatrix[(nBorderCol * nPreviewColumns) + nBorderRow]); + if( xBorderInfo ) + { + const ::editeng::SvxBorderLine* pBorderLine2 = xBorderInfo->maBorder->GetLine(static_cast<SvxBoxItemLine>(static_cast<int>(nLine)^1)); + if( pBorderLine2 && pBorderLine2->HasPriority(*pBorderLine) ) + continue; // other border line wins + } + } + + pVirDev->SetLineColor( pBorderLine->GetColor() ); + switch( nLine ) + { + case SvxBoxItemLine::TOP: pVirDev->DrawLine( aPntTL, aPntTR ); break; + case SvxBoxItemLine::BOTTOM: pVirDev->DrawLine( aPntBL, aPntBR ); break; + case SvxBoxItemLine::LEFT: pVirDev->DrawLine( aPntTL, aPntBL ); break; + case SvxBoxItemLine::RIGHT: pVirDev->DrawLine( aPntTR, aPntBR ); break; + } + } + } + } + } + + return pVirDev->GetBitmapEx(Point(0,0), aBmpSize); +} + +void TableDesignWidget::FillDesignPreviewControl() +{ + sal_uInt16 nSelectedItem = m_xValueSet->GetSelectedItemId(); + m_xValueSet->Clear(); + try + { + TableStyleSettings aSettings; + if( mxSelectedTable.is() ) + { + aSettings.mbUseFirstRow = m_aCheckBoxes[CB_HEADER_ROW]->get_active(); + aSettings.mbUseLastRow = m_aCheckBoxes[CB_TOTAL_ROW]->get_active(); + aSettings.mbUseRowBanding = m_aCheckBoxes[CB_BANDED_ROWS]->get_active(); + aSettings.mbUseFirstColumn = m_aCheckBoxes[CB_FIRST_COLUMN]->get_active(); + aSettings.mbUseLastColumn = m_aCheckBoxes[CB_LAST_COLUMN]->get_active(); + aSettings.mbUseColumnBanding = m_aCheckBoxes[CB_BANDED_COLUMNS]->get_active(); + } + + bool bIsPageDark = false; + if( mxView.is() ) + { + Reference< XPropertySet > xPageSet( mxView->getCurrentPage(), UNO_QUERY ); + if( xPageSet.is() ) + { + xPageSet->getPropertyValue("IsBackgroundDark") >>= bIsPageDark; + } + } + + sal_Int32 nCount = mxTableFamily->getCount(); + for( sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex ) try + { + Reference< XIndexAccess > xTableStyle( mxTableFamily->getByIndex( nIndex ), UNO_QUERY ); + if( xTableStyle.is() ) + m_xValueSet->InsertItem( sal::static_int_cast<sal_uInt16>( nIndex + 1 ), Image( CreateDesignPreview( xTableStyle, aSettings, bIsPageDark ) ) ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::TableDesignWidget::FillDesignPreviewControl()"); + } + sal_Int32 nCols = 3; + sal_Int32 nRows = (nCount+2)/3; + m_xValueSet->SetColCount(nCols); + m_xValueSet->SetLineCount(nRows); + WinBits nStyle = m_xValueSet->GetStyle() & ~WB_VSCROLL; + m_xValueSet->SetStyle(nStyle); + + m_xValueSet->SetOptimalSize(); + weld::DrawingArea* pDrawingArea = m_xValueSet->GetDrawingArea(); + Size aSize = pDrawingArea->get_preferred_size(); + aSize.AdjustWidth(10 * nCols); + aSize.AdjustHeight(10 * nRows); + pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); + + m_xValueSet->Resize(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::TableDesignWidget::FillDesignPreviewControl()"); + } + m_xValueSet->SelectItem(nSelectedItem); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/table/tablefunction.cxx b/sd/source/ui/table/tablefunction.cxx new file mode 100644 index 000000000..c19445429 --- /dev/null +++ b/sd/source/ui/table/tablefunction.cxx @@ -0,0 +1,292 @@ +/* -*- 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 <sal/config.h> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/drawing/XSelectionFunction.hpp> + +#include <comphelper/lok.hxx> + +#include <svx/svdotable.hxx> +#include <svx/svxids.hrc> +#include <svx/svdpagv.hxx> +#include <svx/svxdlg.hxx> + +#include <svl/intitem.hxx> +#include <svl/stritem.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/request.hxx> +#include <sfx2/sidebar/Sidebar.hxx> +#include <svl/style.hxx> +#include <tools/diagnose_ex.h> + +#include <tablefunction.hxx> +#include <DrawViewShell.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <Window.hxx> +#include <drawview.hxx> +#include <sdmod.hxx> + +#include <memory> + +using namespace ::sd; +using namespace sdr::table; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::linguistic2; + +namespace sd +{ + +static void apply_table_style( SdrTableObj* pObj, SdrModel const * pModel, const OUString& sTableStyle ) +{ + if( !(pModel && pObj) ) + return; + + Reference< XNameAccess > xPool( dynamic_cast< XNameAccess* >( pModel->GetStyleSheetPool() ) ); + if( !xPool.is() ) + return; + + try + { + Reference< XNameContainer > xTableFamily( xPool->getByName( "table" ), UNO_QUERY_THROW ); + OUString aStdName( "default" ); + if( !sTableStyle.isEmpty() ) + aStdName = sTableStyle; + Reference< XIndexAccess > xStyle( xTableFamily->getByName( aStdName ), UNO_QUERY_THROW ); + pObj->setTableStyle( xStyle ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::apply_default_table_style()"); + } +} + +static void InsertTableImpl(const DrawViewShell* pShell, + ::sd::View* pView, + sal_Int32 nColumns, + sal_Int32 nRows, + const OUString& sTableStyle) +{ + ::tools::Rectangle aRect; + + SdrObject* pPickObj = pView->GetEmptyPresentationObject( PresObjKind::Table ); + if( pPickObj ) + { + aRect = pPickObj->GetLogicRect(); + aRect.setHeight( 200 ); + } + else + { + Size aSize( 14100, 2000 ); + + Point aPos; + ::tools::Rectangle aWinRect(aPos, pShell->GetActiveWindow()->GetOutputSizePixel()); + aWinRect = pShell->GetActiveWindow()->PixelToLogic(aWinRect); + + // make sure that the default size of the table fits on the paper and is inside the viewing area. + // if zoomed in close, don't make the table bigger than the viewing window. + Size aMaxSize = pShell->getCurrentPage()->GetSize(); + + if (comphelper::LibreOfficeKit::isActive()) + { + // aWinRect is nonsensical in the LOK case + aWinRect = ::tools::Rectangle(aPos, aMaxSize); + } + else + { + if( aMaxSize.Height() > aWinRect.getHeight() ) + aMaxSize.setHeight( aWinRect.getHeight() ); + if( aMaxSize.Width() > aWinRect.getWidth() ) + aMaxSize.setWidth( aWinRect.getWidth() ); + } + + if( aSize.Width() > aMaxSize.getWidth() ) + aSize.setWidth( aMaxSize.getWidth() ); + + // adjust height based on # of rows. + if( nRows > 0 ) + { + aSize.setHeight( aSize.Height() * nRows ); + if( aSize.Height() > aMaxSize.getHeight() ) + aSize.setHeight( aMaxSize.getHeight() ); + } + + aPos = aWinRect.Center(); + aPos.AdjustX( -(aSize.Width() / 2) ); + aPos.AdjustY( -(aSize.Height() / 2) ); + aRect = ::tools::Rectangle(aPos, aSize); + } + + sdr::table::SdrTableObj* pObj = new sdr::table::SdrTableObj( + *pShell->GetDoc(), // TTTT should be reference + aRect, + nColumns, + nRows); + pObj->NbcSetStyleSheet( pShell->GetDoc()->GetDefaultStyleSheet(), true ); + apply_table_style( pObj, pShell->GetDoc(), sTableStyle ); + SdrPageView* pPV = pView->GetSdrPageView(); + + // #i123359# if an object is to be replaced/manipulated it may be that it is in text edit mode, + // so to be on the safe side call SdrEndTextEdit here + SdrTextObj* pCheckForTextEdit = dynamic_cast< SdrTextObj* >(pPickObj); + + if(pCheckForTextEdit && pCheckForTextEdit->IsInEditMode()) + { + pView->SdrEndTextEdit(); + } + + // if we have a pick obj we need to make this new ole a pres obj replacing the current pick obj + if( pPickObj ) + { + SdPage* pPage = static_cast< SdPage* >(pPickObj->getSdrPageFromSdrObject()); + if(pPage && pPage->IsPresObj(pPickObj)) + { + pObj->SetUserCall( pPickObj->GetUserCall() ); + pPage->InsertPresObj( pObj, PresObjKind::Table ); + } + } + + pShell->GetParentWindow()->GrabFocus(); + if( pPickObj ) + pView->ReplaceObjectAtView(pPickObj, *pPV, pObj ); + else + pView->InsertObjectAtView(pObj, *pPV, SdrInsertFlags::SETDEFLAYER); +} + +void DrawViewShell::FuTable(SfxRequest& rReq) +{ + switch( rReq.GetSlot() ) + { + case SID_INSERT_TABLE: + { + sal_Int32 nColumns = 0; + sal_Int32 nRows = 0; + OUString sTableStyle; + DrawViewShell* pShell = this; + ::sd::View* pView = mpView; + + const SfxUInt16Item* pCols = rReq.GetArg<SfxUInt16Item>(SID_ATTR_TABLE_COLUMN); + const SfxUInt16Item* pRows = rReq.GetArg<SfxUInt16Item>(SID_ATTR_TABLE_ROW); + const SfxStringItem* pStyle = rReq.GetArg<SfxStringItem>(SID_TABLE_STYLE); + + if( pCols ) + nColumns = pCols->GetValue(); + + if( pRows ) + nRows = pRows->GetValue(); + + if( pStyle ) + sTableStyle = pStyle->GetValue(); + + if( (nColumns == 0) || (nRows == 0) ) + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + std::shared_ptr<SvxAbstractNewTableDialog> pDlg( pFact->CreateSvxNewTableDialog(rReq.GetFrameWeld()) ); + + weld::DialogController::runAsync(pDlg->getDialogController(), + [pDlg, pShell, pView, sTableStyle] (sal_Int32 nResult) { + if (nResult == RET_OK) + { + sal_Int32 nColumnsIn = pDlg->getColumns(); + sal_Int32 nRowsIn = pDlg->getRows(); + + InsertTableImpl(pShell, pView, nColumnsIn, nRowsIn, sTableStyle); + } + }); + } + else + { + InsertTableImpl(pShell, pView, nColumns, nRows, sTableStyle); + } + + rReq.Ignore(); + SfxViewShell* pViewShell = GetViewShell(); + OSL_ASSERT (pViewShell!=nullptr); + SfxBindings& rBindings = pViewShell->GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_INSERT_TABLE, true ); + break; + } + case SID_TABLEDESIGN: + { + // First make sure that the sidebar is visible + GetViewFrame()->ShowChildWindow(SID_SIDEBAR); + ::sfx2::sidebar::Sidebar::ShowPanel( + u"SdTableDesignPanel", + GetViewFrame()->GetFrame().GetFrameInterface()); + + Cancel(); + rReq.Done (); + break; + } + default: + break; + } +} + +void DrawViewShell::GetTableMenuState( SfxItemSet &rSet ) +{ + OUString aActiveLayer = mpDrawView->GetActiveLayer(); + SdrPageView* pPV = mpDrawView->GetSdrPageView(); + + if( + ( !aActiveLayer.isEmpty() && pPV && ( pPV->IsLayerLocked(aActiveLayer) || + !pPV->IsLayerVisible(aActiveLayer) ) ) || + SD_MOD()->GetWaterCan() ) + { + rSet.DisableItem( SID_INSERT_TABLE ); + } +} + +void CreateTableFromRTF( SvStream& rStream, SdDrawDocument* pModel ) +{ + rStream.Seek( 0 ); + + if( !pModel ) + return; + + SdrPage* pPage = pModel->GetPage(0); + if( !pPage ) + return; + + Size aSize( 200, 200 ); + ::tools::Rectangle aRect (Point(), aSize); + sdr::table::SdrTableObj* pObj = new sdr::table::SdrTableObj( + *pModel, + aRect, + 1, + 1); + pObj->NbcSetStyleSheet( pModel->GetDefaultStyleSheet(), true ); + apply_table_style( pObj, pModel, OUString() ); + + pPage->NbcInsertObject( pObj ); + + sdr::table::ImportAsRTF( rStream, *pObj ); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/table/tableobjectbar.cxx b/sd/source/ui/table/tableobjectbar.cxx new file mode 100644 index 000000000..62d81d980 --- /dev/null +++ b/sd/source/ui/table/tableobjectbar.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 <sfx2/bindings.hxx> +#include <sfx2/msg.hxx> +#include <sfx2/request.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/msgpool.hxx> +#include <vcl/EnumContext.hxx> +#include <svl/intitem.hxx> +#include <svx/svxdlg.hxx> +#include <svx/svxids.hrc> + +#include <createtableobjectbar.hxx> +#include <registerinterfaces.hxx> + +#include <strings.hrc> +#include <DrawDocShell.hxx> +#include <ViewShell.hxx> +#include <sdmod.hxx> +#include <sdresid.hxx> +#include <DrawViewShell.hxx> + +#include "tableobjectbar.hxx" + +using namespace sd; +using namespace sd::ui::table; + +#define ShellClass_TableObjectBar +#include <sdslots.hxx> + +namespace sd::ui::table { + +/** creates a table object bar for the given ViewShell */ +SfxShell* CreateTableObjectBar( ViewShell& rShell, ::sd::View* pView ) +{ + return new TableObjectBar( &rShell, pView ); +} + +/** registers the interfaces from the table ui */ +void RegisterInterfaces(const SfxModule* pMod) +{ + TableObjectBar::RegisterInterface(pMod); +} + + +SFX_IMPL_INTERFACE(TableObjectBar, SfxShell) + +void TableObjectBar::InitInterface_Impl() +{ +} + +TableObjectBar::TableObjectBar( ViewShell* pSdViewShell, ::sd::View* pSdView ) +: SfxShell( pSdViewShell->GetViewShell() ) +, mpView( pSdView ) +, mpViewSh( pSdViewShell ) +{ + DrawDocShell* pDocShell = mpViewSh->GetDocSh(); + if( pDocShell ) + { + SetPool( &pDocShell->GetPool() ); + SetUndoManager( pDocShell->GetUndoManager() ); + } + SetRepeatTarget( mpView ); + SetName( SdResId( RID_DRAW_TABLE_TOOLBOX ) ); + SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Table)); +} + +TableObjectBar::~TableObjectBar() +{ + SetRepeatTarget( nullptr ); +} + +void TableObjectBar::GetState( SfxItemSet& rSet ) +{ + if( mpView ) + { + rtl::Reference< sdr::SelectionController > xController( mpView->getSelectionController() ); + if( xController.is() ) + { + xController->GetState( rSet ); + } + } +} + +void TableObjectBar::GetAttrState( SfxItemSet& rSet ) +{ + DrawViewShell* pDrawViewShell = dynamic_cast< DrawViewShell* >( mpViewSh ); + if( pDrawViewShell ) + pDrawViewShell->GetAttrState( rSet ); +} + +void TableObjectBar::Execute( SfxRequest& rReq ) +{ + if( !mpView ) + return; + + SdrView* pView = mpView; + SfxBindings* pBindings = &mpViewSh->GetViewFrame()->GetBindings(); + + rtl::Reference< sdr::SelectionController > xController( mpView->getSelectionController() ); + sal_uLong nSlotId = rReq.GetSlot(); + if( xController.is() ) + { + switch( nSlotId ) + { + case SID_TABLE_INSERT_ROW_DLG: + case SID_TABLE_INSERT_ROW_BEFORE: + case SID_TABLE_INSERT_ROW_AFTER: + case SID_TABLE_INSERT_COL_DLG: + case SID_TABLE_INSERT_COL_BEFORE: + case SID_TABLE_INSERT_COL_AFTER: + { + ScopedVclPtr<SvxAbstractInsRowColDlg> pDlg; + if (nSlotId == SID_TABLE_INSERT_ROW_DLG || nSlotId == SID_TABLE_INSERT_COL_DLG) + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + vcl::Window* pWin = mpView->GetViewShell()->GetParentWindow(); + pDlg.disposeAndReset( pFact->CreateSvxInsRowColDlg(pWin ? pWin->GetFrameWeld() : nullptr, + nSlotId == SID_TABLE_INSERT_COL_DLG, + SD_MOD()->GetSlotPool()->GetSlot(nSlotId)->GetCommand()) ); + + if (pDlg->Execute() != 1) + break; + } + + sal_uInt16 nCount = 1; + bool bInsertAfter = (nSlotId == SID_TABLE_INSERT_ROW_AFTER) || (nSlotId == SID_TABLE_INSERT_COL_AFTER); + + if (nSlotId == SID_TABLE_INSERT_ROW_DLG) + { + nCount = pDlg->getInsertCount(); + bInsertAfter = !pDlg->isInsertBefore(); + } + else if (nSlotId == SID_TABLE_INSERT_COL_DLG) + { + nCount = pDlg->getInsertCount(); + bInsertAfter = !pDlg->isInsertBefore(); + } + + if (nSlotId == SID_TABLE_INSERT_ROW_DLG || nSlotId == SID_TABLE_INSERT_ROW_BEFORE || nSlotId == SID_TABLE_INSERT_ROW_AFTER) + nSlotId = SID_TABLE_INSERT_ROW; + else + nSlotId = SID_TABLE_INSERT_COL; + + rReq.AppendItem(SfxInt16Item(static_cast<sal_uInt16>(nSlotId), nCount)); + rReq.AppendItem(SfxBoolItem(SID_TABLE_PARAM_INSERT_AFTER, bInsertAfter)); + + rReq.SetSlot( static_cast<sal_uInt16>(nSlotId) ); + } + } + + xController->Execute( rReq ); + } + + // note: we may be deleted at this point, no more member access possible + + switch( rReq.GetSlot() ) + { + case SID_ATTR_BORDER: + case SID_TABLE_MERGE_CELLS: + case SID_TABLE_SPLIT_CELLS: + case SID_OPTIMIZE_TABLE: + case SID_TABLE_DELETE_ROW: + case SID_TABLE_DELETE_COL: + case SID_TABLE_DELETE_TABLE: + case SID_FORMAT_TABLE_DLG: + case SID_TABLE_INSERT_ROW: + case SID_TABLE_INSERT_COL: + { + pView->AdjustMarkHdl(); + pBindings->Invalidate( SID_TABLE_DELETE_ROW ); + pBindings->Invalidate( SID_TABLE_DELETE_COL ); + pBindings->Invalidate( SID_TABLE_DELETE_TABLE ); + pBindings->Invalidate( SID_FRAME_LINESTYLE ); + pBindings->Invalidate( SID_FRAME_LINECOLOR ); + pBindings->Invalidate( SID_ATTR_BORDER ); + pBindings->Invalidate( SID_ATTR_FILL_STYLE ); + pBindings->Invalidate( SID_ATTR_FILL_USE_SLIDE_BACKGROUND ); + pBindings->Invalidate( SID_ATTR_FILL_TRANSPARENCE ); + pBindings->Invalidate( SID_ATTR_FILL_FLOATTRANSPARENCE ); + pBindings->Invalidate( SID_TABLE_MERGE_CELLS ); + pBindings->Invalidate( SID_TABLE_SPLIT_CELLS ); + pBindings->Invalidate( SID_OPTIMIZE_TABLE ); + pBindings->Invalidate( SID_TABLE_VERT_BOTTOM ); + pBindings->Invalidate( SID_TABLE_VERT_CENTER ); + pBindings->Invalidate( SID_TABLE_VERT_NONE ); + break; + } + case SID_TABLE_VERT_BOTTOM: + case SID_TABLE_VERT_CENTER: + case SID_TABLE_VERT_NONE: + { + pBindings->Invalidate( SID_TABLE_VERT_BOTTOM ); + pBindings->Invalidate( SID_TABLE_VERT_CENTER ); + pBindings->Invalidate( SID_TABLE_VERT_NONE ); + break; + } + } + + pBindings->Invalidate( SID_UNDO ); + pBindings->Invalidate( SID_REDO ); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/table/tableobjectbar.hxx b/sd/source/ui/table/tableobjectbar.hxx new file mode 100644 index 000000000..c5d513bff --- /dev/null +++ b/sd/source/ui/table/tableobjectbar.hxx @@ -0,0 +1,56 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/shell.hxx> +#include <glob.hxx> + +namespace sd { + +class View; +class ViewShell; + +} + +namespace sd::ui::table { + +class TableObjectBar final : public SfxShell +{ +public: + SFX_DECL_INTERFACE( SD_IF_SDDRAWTABLEOBJECTBAR ) + + TableObjectBar( ::sd::ViewShell* pSdViewShell, ::sd::View* pSdView); + virtual ~TableObjectBar() override; + + void GetState( SfxItemSet& rSet ); + void GetAttrState( SfxItemSet& rSet ); + void Execute( SfxRequest& rReq ); + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + + ::sd::View* mpView; + ::sd::ViewShell* mpViewSh; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/tools/AsynchronousCall.cxx b/sd/source/ui/tools/AsynchronousCall.cxx new file mode 100644 index 000000000..b7b70f63a --- /dev/null +++ b/sd/source/ui/tools/AsynchronousCall.cxx @@ -0,0 +1,56 @@ +/* -*- 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 <memory> +#include <tools/AsynchronousCall.hxx> + +namespace sd::tools +{ +AsynchronousCall::AsynchronousCall() + : maTimer("sd AsynchronousCall") +{ + maTimer.SetInvokeHandler(LINK(this, AsynchronousCall, TimerCallback)); +} + +AsynchronousCall::~AsynchronousCall() +{ + mpFunction.reset(); + maTimer.Stop(); +} + +void AsynchronousCall::Post(const AsynchronousFunction& rFunction) +{ + mpFunction.reset(new AsynchronousFunction(rFunction)); + maTimer.SetTimeout(10); + maTimer.Start(); +} + +IMPL_LINK(AsynchronousCall, TimerCallback, Timer*, pTimer, void) +{ + if (pTimer == &maTimer) + { + ::std::unique_ptr<AsynchronousFunction> pFunction; + pFunction.swap(mpFunction); + (*pFunction)(); + } +} + +} // end of namespace ::sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/tools/ConfigurationAccess.cxx b/sd/source/ui/tools/ConfigurationAccess.cxx new file mode 100644 index 000000000..d359aeb18 --- /dev/null +++ b/sd/source/ui/tools/ConfigurationAccess.cxx @@ -0,0 +1,173 @@ +/* -*- 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 <tools/ConfigurationAccess.hxx> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> +#include <com/sun/star/configuration/theDefaultProvider.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/util/XChangesBatch.hpp> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertysequence.hxx> +#include <tools/diagnose_ex.h> +#include <sal/log.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::tools { + +ConfigurationAccess::ConfigurationAccess ( + const Reference<XComponentContext>& rxContext, + const OUString& rsRootName, + const WriteMode eMode) +{ + Reference<lang::XMultiServiceFactory> xProvider = + configuration::theDefaultProvider::get( rxContext ); + Initialize(xProvider, rsRootName, eMode); +} + +ConfigurationAccess::ConfigurationAccess ( + const OUString& rsRootName, + const WriteMode eMode) +{ + Reference<lang::XMultiServiceFactory> xProvider = + configuration::theDefaultProvider::get( ::comphelper::getProcessComponentContext() ); + Initialize(xProvider, rsRootName, eMode); +} + +void ConfigurationAccess::Initialize ( + const Reference<lang::XMultiServiceFactory>& rxProvider, + const OUString& rsRootName, + const WriteMode eMode) +{ + try + { + Sequence<Any> aCreationArguments(comphelper::InitAnyPropertySequence( + { + {"nodepath", Any(rsRootName)}, + {"depth", Any(sal_Int32(-1))} + })); + + OUString sAccessService; + if (eMode == READ_ONLY) + sAccessService = "com.sun.star.configuration.ConfigurationAccess"; + else + sAccessService = "com.sun.star.configuration.ConfigurationUpdateAccess"; + + mxRoot = rxProvider->createInstanceWithArguments( + sAccessService, + aCreationArguments); + } + catch (Exception&) + { + DBG_UNHANDLED_EXCEPTION("sd.tools"); + } +} + +Any ConfigurationAccess::GetConfigurationNode ( + const OUString& sPathToNode) +{ + return GetConfigurationNode( + Reference<container::XHierarchicalNameAccess>(mxRoot, UNO_QUERY), + sPathToNode); +} + +Any ConfigurationAccess::GetConfigurationNode ( + const css::uno::Reference<css::container::XHierarchicalNameAccess>& rxNode, + const OUString& sPathToNode) +{ + if (sPathToNode.isEmpty()) + return Any(rxNode); + + try + { + if (rxNode.is()) + { + return rxNode->getByHierarchicalName(sPathToNode); + } + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("sd", "caught exception while getting configuration node" << sPathToNode); + } + + return Any(); +} + +void ConfigurationAccess::CommitChanges() +{ + Reference<util::XChangesBatch> xConfiguration (mxRoot, UNO_QUERY); + if (xConfiguration.is()) + xConfiguration->commitChanges(); +} + +void ConfigurationAccess::ForAll ( + const Reference<container::XNameAccess>& rxContainer, + const ::std::vector<OUString>& rArguments, + const Functor& rFunctor) +{ + if (!rxContainer.is()) + return; + + ::std::vector<Any> aValues(rArguments.size()); + const Sequence<OUString> aKeys (rxContainer->getElementNames()); + for (const OUString& rsKey : aKeys) + { + Reference<container::XNameAccess> xSetItem (rxContainer->getByName(rsKey), UNO_QUERY); + if (xSetItem.is()) + { + // Get from the current item of the container the children + // that match the names in the rArguments list. + for (size_t nValueIndex=0; nValueIndex<aValues.size(); ++nValueIndex) + aValues[nValueIndex] = xSetItem->getByName(rArguments[nValueIndex]); + } + rFunctor(rsKey, aValues); + } +} + +void ConfigurationAccess::FillList( + const Reference<container::XNameAccess>& rxContainer, + const OUString& rsArgument, + ::std::vector<OUString>& rList) +{ + try + { + if (rxContainer.is()) + { + Sequence<OUString> aKeys (rxContainer->getElementNames()); + rList.resize(aKeys.getLength()); + for (sal_Int32 nItemIndex=0; nItemIndex<aKeys.getLength(); ++nItemIndex) + { + Reference<container::XNameAccess> xSetItem ( + rxContainer->getByName(aKeys[nItemIndex]), UNO_QUERY); + if (xSetItem.is()) + { + xSetItem->getByName(rsArgument) >>= rList[nItemIndex]; + } + } + } + } + catch (RuntimeException&) + {} +} + +} // end of namespace sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/tools/EventMultiplexer.cxx b/sd/source/ui/tools/EventMultiplexer.cxx new file mode 100644 index 000000000..a61413ac6 --- /dev/null +++ b/sd/source/ui/tools/EventMultiplexer.cxx @@ -0,0 +1,661 @@ +/* -*- 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 <EventMultiplexer.hxx> + +#include <ViewShellBase.hxx> +#include <drawdoc.hxx> +#include <DrawController.hxx> +#include <SlideSorterViewShell.hxx> +#include <framework/FrameworkHelper.hxx> +#include <sal/log.hxx> +#include <tools/debug.hxx> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/drawing/framework/XConfigurationChangeListener.hpp> +#include <com/sun/star/drawing/framework/XView.hpp> +#include <comphelper/compbase.hxx> +#include <sfx2/viewfrm.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +using ::sd::framework::FrameworkHelper; + +class SdDrawDocument; + +namespace { +const sal_Int32 ResourceActivationEvent = 0; +const sal_Int32 ResourceDeactivationEvent = 1; +const sal_Int32 ConfigurationUpdateEvent = 2; +} + +namespace sd::tools { + +typedef comphelper::WeakComponentImplHelper< + css::beans::XPropertyChangeListener, + css::frame::XFrameActionListener, + css::view::XSelectionChangeListener, + css::drawing::framework::XConfigurationChangeListener + > EventMultiplexerImplementationInterfaceBase; + +class EventMultiplexer::Implementation + : public EventMultiplexerImplementationInterfaceBase, + public SfxListener +{ +public: + explicit Implementation (ViewShellBase& rBase); + virtual ~Implementation() override; + + void AddEventListener ( + const Link<EventMultiplexerEvent&,void>& rCallback); + + void RemoveEventListener ( + const Link<EventMultiplexerEvent&,void>& rCallback); + + void CallListeners (EventMultiplexerEvent& rEvent); + + //===== lang::XEventListener ============================================== + virtual void SAL_CALL + disposing (const css::lang::EventObject& rEventObject) override; + + //===== beans::XPropertySetListener ======================================= + virtual void SAL_CALL + propertyChange ( + const css::beans::PropertyChangeEvent& rEvent) override; + + //===== view::XSelectionChangeListener ==================================== + virtual void SAL_CALL + selectionChanged ( + const css::lang::EventObject& rEvent) override; + + //===== frame::XFrameActionListener ====================================== + /** For certain actions the listener connects to a new controller of the + frame it is listening to. This usually happens when the view shell + in the center pane is replaced by another view shell. + */ + virtual void SAL_CALL + frameAction (const css::frame::FrameActionEvent& rEvent) override; + + //===== drawing::framework::XConfigurationChangeListener ================== + virtual void SAL_CALL + notifyConfigurationChange ( + const css::drawing::framework::ConfigurationChangeEvent& rEvent) override; + + virtual void disposing(std::unique_lock<std::mutex>&) override; + +protected: + virtual void Notify ( + SfxBroadcaster& rBroadcaster, + const SfxHint& rHint) override; + +private: + ViewShellBase& mrBase; + typedef ::std::vector<Link<EventMultiplexerEvent&,void>> ListenerList; + ListenerList maListeners; + + /// Remember whether we are listening to the UNO controller. + bool mbListeningToController; + /// Remember whether we are listening to the frame. + bool mbListeningToFrame; + + css::uno::WeakReference<css::frame::XController> mxControllerWeak; + css::uno::WeakReference<css::frame::XFrame> mxFrameWeak; + SdDrawDocument* mpDocument; + css::uno::WeakReference<css::drawing::framework::XConfigurationController> + mxConfigurationControllerWeak; + + void ReleaseListeners(); + + void ConnectToController(); + void DisconnectFromController(); + + void CallListeners ( + EventMultiplexerEventId eId, + void const * pUserData = nullptr); + + DECL_LINK(SlideSorterSelectionChangeListener, LinkParamNone*, void); +}; + +constexpr OUStringLiteral aCurrentPagePropertyName = u"CurrentPage"; +constexpr OUStringLiteral aEditModePropertyName = u"IsMasterPageMode"; + +//===== EventMultiplexer ====================================================== + +EventMultiplexer::EventMultiplexer (ViewShellBase& rBase) + : mpImpl (new EventMultiplexer::Implementation(rBase)) +{ +} + +EventMultiplexer::~EventMultiplexer() +{ + try + { + mpImpl->dispose(); + } + catch (const RuntimeException&) + { + } + catch (const Exception&) + { + } +} + +void EventMultiplexer::AddEventListener ( + const Link<EventMultiplexerEvent&,void>& rCallback) +{ + mpImpl->AddEventListener(rCallback); +} + +void EventMultiplexer::RemoveEventListener ( + const Link<EventMultiplexerEvent&,void>& rCallback) +{ + mpImpl->RemoveEventListener(rCallback); +} + +void EventMultiplexer::MultiplexEvent( + EventMultiplexerEventId eEventId, + void const * pUserData ) +{ + EventMultiplexerEvent aEvent(eEventId, pUserData); + mpImpl->CallListeners(aEvent); +} + +//===== EventMultiplexer::Implementation ====================================== + +EventMultiplexer::Implementation::Implementation (ViewShellBase& rBase) + : mrBase (rBase), + mbListeningToController (false), + mbListeningToFrame (false), + mxControllerWeak(nullptr), + mxFrameWeak(nullptr), + mpDocument(nullptr) +{ + // Connect to the frame to listen for controllers being exchanged. + // Listen to changes of certain properties. + Reference<frame::XFrame> xFrame = + mrBase.GetFrame()->GetFrame().GetFrameInterface(); + mxFrameWeak = xFrame; + if (xFrame.is()) + { + xFrame->addFrameActionListener ( Reference<frame::XFrameActionListener>(this) ); + mbListeningToFrame = true; + } + + // Connect to the current controller. + ConnectToController (); + + // Listen for document changes. + mpDocument = mrBase.GetDocument(); + if (mpDocument != nullptr) + StartListening (*mpDocument); + + // Listen for configuration changes. + Reference<XControllerManager> xControllerManager ( + Reference<XWeak>(&mrBase.GetDrawController()), UNO_QUERY); + if (!xControllerManager.is()) + return; + + Reference<XConfigurationController> xConfigurationController ( + xControllerManager->getConfigurationController()); + mxConfigurationControllerWeak = xConfigurationController; + if (!xConfigurationController.is()) + return; + + Reference<XComponent> xComponent (xConfigurationController, UNO_QUERY); + if (xComponent.is()) + xComponent->addEventListener(static_cast<beans::XPropertyChangeListener*>(this)); + + xConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceActivationEvent, + Any(ResourceActivationEvent)); + xConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceDeactivationEvent, + Any(ResourceDeactivationEvent)); + xConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msConfigurationUpdateEndEvent, + Any(ConfigurationUpdateEvent)); +} + +EventMultiplexer::Implementation::~Implementation() +{ + DBG_ASSERT( !mbListeningToFrame, + "sd::EventMultiplexer::Implementation::~Implementation(), disposing was not called!" ); +} + +void EventMultiplexer::Implementation::ReleaseListeners() +{ + if (mbListeningToFrame) + { + mbListeningToFrame = false; + + // Stop listening for changes of certain properties. + Reference<frame::XFrame> xFrame (mxFrameWeak); + if (xFrame.is()) + { + xFrame->removeFrameActionListener ( + Reference<frame::XFrameActionListener>(this) ); + } + } + + DisconnectFromController (); + + if (mpDocument != nullptr) + { + EndListening (*mpDocument); + mpDocument = nullptr; + } + + // Stop listening for configuration changes. + Reference<XConfigurationController> xConfigurationController (mxConfigurationControllerWeak); + if (xConfigurationController.is()) + { + Reference<XComponent> xComponent (xConfigurationController, UNO_QUERY); + if (xComponent.is()) + xComponent->removeEventListener(static_cast<beans::XPropertyChangeListener*>(this)); + + xConfigurationController->removeConfigurationChangeListener(this); + } +} + +void EventMultiplexer::Implementation::AddEventListener ( + const Link<EventMultiplexerEvent&,void>& rCallback) +{ + for (auto const & i : maListeners) + if (i == rCallback) + return; + maListeners.push_back(rCallback); +} + +void EventMultiplexer::Implementation::RemoveEventListener ( + const Link<EventMultiplexerEvent&,void>& rCallback) +{ + auto iListener = std::find(maListeners.begin(), maListeners.end(), rCallback); + if (iListener != maListeners.end()) + maListeners.erase(iListener); +} + +void EventMultiplexer::Implementation::ConnectToController() +{ + // Just in case that we missed some event we now disconnect from the old + // controller. + DisconnectFromController (); + + // Register at the controller of the main view shell. + + // We have to store a (weak) reference to the controller so that we can + // unregister without having to ask the mrBase member (which at that + // time may be destroyed.) + Reference<frame::XController> xController = mrBase.GetController(); + mxControllerWeak = mrBase.GetController(); + + try + { + // Listen for disposing events. + if (xController.is()) + { + xController->addEventListener ( + Reference<lang::XEventListener>( + static_cast<XWeak*>(this), UNO_QUERY)); + mbListeningToController = true; + } + + // Listen to changes of certain properties. + Reference<beans::XPropertySet> xSet (xController, UNO_QUERY); + if (xSet.is()) + { + try + { + xSet->addPropertyChangeListener(aCurrentPagePropertyName, this); + } + catch (const beans::UnknownPropertyException&) + { + SAL_WARN("sd", "EventMultiplexer::ConnectToController: CurrentPage unknown"); + } + + try + { + xSet->addPropertyChangeListener(aEditModePropertyName, this); + } + catch (const beans::UnknownPropertyException&) + { + SAL_WARN("sd", "EventMultiplexer::ConnectToController: IsMasterPageMode unknown"); + } + } + + // Listen for selection change events. + Reference<view::XSelectionSupplier> xSelection (xController, UNO_QUERY); + if (xSelection.is()) + { + xSelection->addSelectionChangeListener(this); + } + } + catch (const lang::DisposedException&) + { + mbListeningToController = false; + } +} + +void EventMultiplexer::Implementation::DisconnectFromController() +{ + if (!mbListeningToController) + return; + + mbListeningToController = false; + + Reference<frame::XController> xController = mxControllerWeak; + + Reference<beans::XPropertySet> xSet (xController, UNO_QUERY); + // Remove the property listener. + if (xSet.is()) + { + try + { + xSet->removePropertyChangeListener(aCurrentPagePropertyName, this); + } + catch (const beans::UnknownPropertyException&) + { + SAL_WARN("sd", "DisconnectFromController: CurrentPage unknown"); + } + + try + { + xSet->removePropertyChangeListener(aEditModePropertyName, this); + } + catch (const beans::UnknownPropertyException&) + { + SAL_WARN("sd", "DisconnectFromController: IsMasterPageMode unknown"); + } + } + + // Remove selection change listener. + Reference<view::XSelectionSupplier> xSelection (xController, UNO_QUERY); + if (xSelection.is()) + { + xSelection->removeSelectionChangeListener(this); + } + + // Remove listener for disposing events. + if (xController.is()) + { + xController->removeEventListener ( + Reference<lang::XEventListener>(static_cast<XWeak*>(this), UNO_QUERY)); + } +} + +//===== lang::XEventListener ================================================ + +void SAL_CALL EventMultiplexer::Implementation::disposing ( + const lang::EventObject& rEventObject) +{ + if (mbListeningToController) + { + Reference<frame::XController> xController (mxControllerWeak); + if (rEventObject.Source == xController) + { + mbListeningToController = false; + } + } + + Reference<XConfigurationController> xConfigurationController ( + mxConfigurationControllerWeak); + if (xConfigurationController.is() + && rEventObject.Source == xConfigurationController) + { + mxConfigurationControllerWeak.clear(); + } +} + +//===== beans::XPropertySetListener ========================================= + +void SAL_CALL EventMultiplexer::Implementation::propertyChange ( + const beans::PropertyChangeEvent& rEvent) +{ + if (m_bDisposed) + { + throw lang::DisposedException ( + "SlideSorterController object has already been disposed", + static_cast<uno::XWeak*>(this)); + } + + if ( rEvent.PropertyName == aCurrentPagePropertyName ) + { + CallListeners(EventMultiplexerEventId::CurrentPageChanged); + } + else if ( rEvent.PropertyName == aEditModePropertyName ) + { + bool bIsMasterPageMode (false); + rEvent.NewValue >>= bIsMasterPageMode; + if (bIsMasterPageMode) + CallListeners(EventMultiplexerEventId::EditModeMaster); + else + CallListeners(EventMultiplexerEventId::EditModeNormal); + } +} + +//===== frame::XFrameActionListener ========================================== + +void SAL_CALL EventMultiplexer::Implementation::frameAction ( + const frame::FrameActionEvent& rEvent) +{ + Reference<frame::XFrame> xFrame (mxFrameWeak); + if (rEvent.Frame != xFrame) + return; + + switch (rEvent.Action) + { + case frame::FrameAction_COMPONENT_DETACHING: + DisconnectFromController(); + CallListeners (EventMultiplexerEventId::ControllerDetached); + break; + + case frame::FrameAction_COMPONENT_REATTACHED: + CallListeners (EventMultiplexerEventId::ControllerDetached); + DisconnectFromController(); + ConnectToController(); + CallListeners (EventMultiplexerEventId::ControllerAttached); + break; + + case frame::FrameAction_COMPONENT_ATTACHED: + ConnectToController(); + CallListeners (EventMultiplexerEventId::ControllerAttached); + break; + + default: + break; + } +} + +//===== view::XSelectionChangeListener ======================================== + +void SAL_CALL EventMultiplexer::Implementation::selectionChanged ( + const lang::EventObject& ) +{ + CallListeners (EventMultiplexerEventId::EditViewSelection); +} + +//===== drawing::framework::XConfigurationChangeListener ================== + +void SAL_CALL EventMultiplexer::Implementation::notifyConfigurationChange ( + const ConfigurationChangeEvent& rEvent) +{ + sal_Int32 nEventType = 0; + rEvent.UserData >>= nEventType; + switch (nEventType) + { + case ResourceActivationEvent: + if (rEvent.ResourceId->getResourceURL().match(FrameworkHelper::msViewURLPrefix)) + { + CallListeners (EventMultiplexerEventId::ViewAdded); + + if (rEvent.ResourceId->isBoundToURL( + FrameworkHelper::msCenterPaneURL, AnchorBindingMode_DIRECT)) + { + CallListeners (EventMultiplexerEventId::MainViewAdded); + } + + // Add selection change listener at slide sorter. + if (rEvent.ResourceId->getResourceURL() == FrameworkHelper::msSlideSorterURL) + { + slidesorter::SlideSorterViewShell* pViewShell + = dynamic_cast<slidesorter::SlideSorterViewShell*>( + FrameworkHelper::GetViewShell( + Reference<XView>(rEvent.ResourceObject,UNO_QUERY)).get()); + if (pViewShell != nullptr) + pViewShell->AddSelectionChangeListener ( + LINK(this, + EventMultiplexer::Implementation, + SlideSorterSelectionChangeListener)); + } + } + break; + + case ResourceDeactivationEvent: + if (rEvent.ResourceId->getResourceURL().match(FrameworkHelper::msViewURLPrefix)) + { + if (rEvent.ResourceId->isBoundToURL( + FrameworkHelper::msCenterPaneURL, AnchorBindingMode_DIRECT)) + { + CallListeners (EventMultiplexerEventId::MainViewRemoved); + } + + // Remove selection change listener from slide sorter. Add + // selection change listener at slide sorter. + if (rEvent.ResourceId->getResourceURL() == FrameworkHelper::msSlideSorterURL) + { + slidesorter::SlideSorterViewShell* pViewShell + = dynamic_cast<slidesorter::SlideSorterViewShell*>( + FrameworkHelper::GetViewShell( + Reference<XView>(rEvent.ResourceObject, UNO_QUERY)).get()); + if (pViewShell != nullptr) + pViewShell->RemoveSelectionChangeListener ( + LINK(this, + EventMultiplexer::Implementation, + SlideSorterSelectionChangeListener)); + } + } + break; + + case ConfigurationUpdateEvent: + CallListeners (EventMultiplexerEventId::ConfigurationUpdated); + break; + } + +} + +void EventMultiplexer::Implementation::disposing(std::unique_lock<std::mutex>& rGuard) +{ + ListenerList aCopyListeners( maListeners ); + + rGuard.unlock(); + + EventMultiplexerEvent rEvent(EventMultiplexerEventId::Disposing, nullptr); + for (const auto& rListener : aCopyListeners) + rListener.Call(rEvent); + + rGuard.lock(); + + ReleaseListeners(); +} + +void EventMultiplexer::Implementation::Notify ( + SfxBroadcaster&, + const SfxHint& rHint) +{ + if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint) + { + const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint); + switch (pSdrHint->GetKind()) + { + case SdrHintKind::ModelCleared: + case SdrHintKind::PageOrderChange: + CallListeners (EventMultiplexerEventId::PageOrder); + break; + + case SdrHintKind::SwitchToPage: + CallListeners (EventMultiplexerEventId::CurrentPageChanged); + break; + + case SdrHintKind::ObjectChange: + CallListeners(EventMultiplexerEventId::ShapeChanged, + static_cast<const void*>(pSdrHint->GetPage())); + break; + + case SdrHintKind::ObjectInserted: + CallListeners(EventMultiplexerEventId::ShapeInserted, + static_cast<const void*>(pSdrHint->GetPage())); + break; + + case SdrHintKind::ObjectRemoved: + CallListeners(EventMultiplexerEventId::ShapeRemoved, + static_cast<const void*>(pSdrHint->GetPage())); + break; + default: + break; + } + } + else + { + if (rHint.GetId() == SfxHintId::Dying) + mpDocument = nullptr; + } +} + +void EventMultiplexer::Implementation::CallListeners ( + EventMultiplexerEventId eId, + void const * pUserData) +{ + EventMultiplexerEvent aEvent(eId, pUserData); + CallListeners(aEvent); +} + +void EventMultiplexer::Implementation::CallListeners (EventMultiplexerEvent& rEvent) +{ + ListenerList aCopyListeners( maListeners ); + for (const auto& rListener : aCopyListeners) + { + rListener.Call(rEvent); + } +} + +IMPL_LINK_NOARG(EventMultiplexer::Implementation, SlideSorterSelectionChangeListener, LinkParamNone*, void) +{ + CallListeners(EventMultiplexerEventId::SlideSortedSelection); +} + +//===== EventMultiplexerEvent ================================================= + +EventMultiplexerEvent::EventMultiplexerEvent ( + EventMultiplexerEventId eEventId, + const void* pUserData) + : meEventId(eEventId), + mpUserData(pUserData) +{ +} + +} // end of namespace ::sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/tools/GraphicSizeCheck.cxx b/sd/source/ui/tools/GraphicSizeCheck.cxx new file mode 100644 index 000000000..492a2d70b --- /dev/null +++ b/sd/source/ui/tools/GraphicSizeCheck.cxx @@ -0,0 +1,221 @@ +/* -*- 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/. + * + */ + +#include <memory> +#include <tools/GraphicSizeCheck.hxx> +#include <svx/strings.hrc> +#include <svx/svdobj.hxx> +#include <svx/svdpage.hxx> +#include <svx/svxids.hrc> +#include <sfx2/dispatch.hxx> + +#include <sdresid.hxx> +#include <DrawDocShell.hxx> +#include <ViewShell.hxx> + +namespace sd +{ +namespace +{ +/** + * Interface for the visitor class, which handles each visited SdrObject + * in the DOM. + */ +class ModelTraverseHandler +{ +public: + virtual ~ModelTraverseHandler() {} + + virtual void handleSdrObject(SdrObject* pObject) = 0; +}; + +/** + * Traverses the DOM and calls a handler for each object (SdrObject) it + * encounters. + */ +class ModelTraverser +{ +private: + std::vector<std::shared_ptr<ModelTraverseHandler>> m_pNodeHandler; + SdDrawDocument* m_pDocument; + +public: + ModelTraverser(SdDrawDocument* pDocument) + : m_pDocument(pDocument) + { + } + + void traverse() + { + if (!m_pDocument) + return; + + for (sal_uInt16 nPage = 0; nPage < m_pDocument->GetPageCount(); ++nPage) + { + SdrPage* pPage = m_pDocument->GetPage(nPage); + if (pPage) + { + for (size_t nObject = 0; nObject < pPage->GetObjCount(); ++nObject) + { + SdrObject* pObject = pPage->GetObj(nObject); + if (pObject) + { + for (auto& pNodeHandler : m_pNodeHandler) + { + pNodeHandler->handleSdrObject(pObject); + } + } + } + } + } + } + + void addNodeHandler(std::shared_ptr<ModelTraverseHandler> pHandler) + { + m_pNodeHandler.push_back(pHandler); + } +}; +} + +GraphicSizeViolation::GraphicSizeViolation(sal_Int32 nDPI, SdrGrafObj* pGraphicObject) + : m_pGraphicObject(pGraphicObject) +{ + constexpr double fLowPercentage = 110; + constexpr double fHighPercentage = 50; + + m_nLowDPILimit = sal_Int32(100.0 / fLowPercentage * nDPI); + m_nHighDPILimit = sal_Int32(100.0 / fHighPercentage * nDPI); +} + +bool GraphicSizeViolation::check() +{ + Graphic aGraphic = m_pGraphicObject->GetGraphic(); + Size aSizePixel = aGraphic.GetSizePixel(); + Size aGraphicSize = m_pGraphicObject->GetLogicRect().GetSize(); + + double nSizeXInch + = o3tl::convert(double(aGraphicSize.Width()), o3tl::Length::mm100, o3tl::Length::in); + double nSizeYInch + = o3tl::convert(double(aGraphicSize.Height()), o3tl::Length::mm100, o3tl::Length::in); + + m_nDPIX = sal_Int32(aSizePixel.Width() / nSizeXInch); + m_nDPIY = sal_Int32(aSizePixel.Height() / nSizeYInch); + + return isDPITooLow() || isDPITooHigh(); +} + +const OUString& GraphicSizeViolation::getGraphicName() { return m_pGraphicObject->GetName(); } + +namespace +{ +class GraphicSizeCheckHandler : public ModelTraverseHandler +{ + sal_Int32 m_nDPI; + std::vector<std::unique_ptr<GraphicSizeViolation>>& m_rGraphicSizeViolationList; + +public: + GraphicSizeCheckHandler( + sal_Int32 nDPI, + std::vector<std::unique_ptr<GraphicSizeViolation>>& rGraphicSizeViolationList) + : m_nDPI(nDPI) + , m_rGraphicSizeViolationList(rGraphicSizeViolationList) + { + } + + void handleSdrObject(SdrObject* pObject) override + { + auto* pGraphicObject = dynamic_cast<SdrGrafObj*>(pObject); + if (!pGraphicObject) + return; + + auto pEntry = std::make_unique<GraphicSizeViolation>(m_nDPI, pGraphicObject); + if (pEntry->check()) + { + m_rGraphicSizeViolationList.push_back(std::move(pEntry)); + } + } +}; + +} // end anonymous namespace + +void GraphicSizeCheck::check() +{ + if (!m_pDocument) + return; + + sal_Int32 nDPI = m_pDocument->getImagePreferredDPI(); + if (nDPI == 0) + return; + + auto pHandler = std::make_shared<GraphicSizeCheckHandler>(nDPI, m_aGraphicSizeViolationList); + + ModelTraverser aModelTraverser(m_pDocument); + aModelTraverser.addNodeHandler(pHandler); + aModelTraverser.traverse(); +} + +OUString GraphicSizeCheckGUIEntry::getText() +{ + OUString sText; + + if (m_pViolation->isDPITooLow()) + { + sText = SdResId(STR_WARNING_GRAPHIC_PIXEL_COUNT_LOW); + } + else if (m_pViolation->isDPITooHigh()) + { + sText = SdResId(STR_WARNING_GRAPHIC_PIXEL_COUNT_HIGH); + } + + sText = sText.replaceAll("%NAME%", m_pViolation->getGraphicName()); + sText = sText.replaceAll("%DPIX%", OUString::number(m_pViolation->getDPIX())); + sText = sText.replaceAll("%DPIY%", OUString::number(m_pViolation->getDPIY())); + + return sText; +} + +void GraphicSizeCheckGUIEntry::markObject() +{ + sd::ViewShell* pViewShell = m_pDocument->GetDocSh()->GetViewShell(); + SdrView* pView = pViewShell->GetView(); + pView->ShowSdrPage(m_pViolation->getObject()->getSdrPageFromSdrObject()); + pView->UnmarkAll(); + pView->MarkObj(m_pViolation->getObject(), pView->GetSdrPageView()); +} + +void GraphicSizeCheckGUIEntry::runProperties() +{ + markObject(); + sd::ViewShell* pViewShell = m_pDocument->GetDocSh()->GetViewShell(); + pViewShell->GetDispatcher()->Execute(SID_ATTR_GRAF_CROP, SfxCallMode::SYNCHRON); +} + +GraphicSizeCheckGUIResult::GraphicSizeCheckGUIResult(SdDrawDocument* pDocument) +{ + GraphicSizeCheck aCheck(pDocument); + aCheck.check(); + + auto& rCollection = getCollection(); + for (auto& rpViolation : aCheck.getViolationList()) + { + auto rGUIEntry + = std::make_unique<GraphicSizeCheckGUIEntry>(pDocument, std::move(rpViolation)); + rCollection.push_back(std::move(rGUIEntry)); + } +} + +OUString GraphicSizeCheckGUIResult::getTitle() +{ + return SdResId(STR_GRAPHIC_SIZE_CHECK_DIALOG_TITLE); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/tools/IconCache.cxx b/sd/source/ui/tools/IconCache.cxx new file mode 100644 index 000000000..0ce80922b --- /dev/null +++ b/sd/source/ui/tools/IconCache.cxx @@ -0,0 +1,106 @@ +/* -*- 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 <memory> +#include <tools/IconCache.hxx> + +#include <tools/debug.hxx> +#include <osl/doublecheckedlocking.h> +#include <osl/getglobalmutex.hxx> +#include <unordered_map> + +namespace sd +{ +//===== IconCache::Implementation ============================================= + +class IconCache::Implementation +{ +private: + friend class IconCache; + + /** This pointer holds a valid reference from first time that + IconCache::Instance() is called to the end of the sd module when the + cache is destroyed from SdGlobalResourceContainer. + */ + static IconCache* s_pIconCache; + + typedef std::unordered_map<OUString, Image> ImageContainer; + ImageContainer maContainer; + + Image GetIcon(const OUString& rResourceId); +}; + +IconCache* IconCache::Implementation::s_pIconCache = nullptr; + +Image IconCache::Implementation::GetIcon(const OUString& rResourceId) +{ + Image aResult; + ImageContainer::iterator iImage = maContainer.find(rResourceId); + if (iImage == maContainer.end()) + { + aResult = Image(StockImage::Yes, rResourceId); + maContainer[rResourceId] = aResult; + } + else + aResult = iImage->second; + return aResult; +} + +//===== IconCache ============================================================= + +//static +IconCache& IconCache::Instance() +{ + if (Implementation::s_pIconCache == nullptr) + { + ::osl::GetGlobalMutex aMutexFunctor; + ::osl::MutexGuard aGuard(aMutexFunctor()); + if (Implementation::s_pIconCache == nullptr) + { + IconCache* pCache = new IconCache(); + SdGlobalResourceContainer::Instance().AddResource( + ::std::unique_ptr<SdGlobalResource>(pCache)); + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + Implementation::s_pIconCache = pCache; + } + } + else + { + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + } + + DBG_ASSERT(Implementation::s_pIconCache != nullptr, "IconCache::Instance(): instance is NULL"); + return *Implementation::s_pIconCache; +} + +Image IconCache::GetIcon(const OUString& rResourceId) { return mpImpl->GetIcon(rResourceId); } + +IconCache::IconCache() + : mpImpl(new Implementation) +{ +} + +IconCache::~IconCache() +{ + // empty +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/tools/IdleDetection.cxx b/sd/source/ui/tools/IdleDetection.cxx new file mode 100644 index 000000000..988bd849b --- /dev/null +++ b/sd/source/ui/tools/IdleDetection.cxx @@ -0,0 +1,103 @@ +/* -*- 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 <tools/IdleDetection.hxx> + +#include <slideshow.hxx> +#include <ViewShellBase.hxx> + +#include <vcl/window.hxx> +#include <sfx2/viewfrm.hxx> + +#include <com/sun/star/frame/XFrame.hpp> +#include <vcl/svapp.hxx> + +using namespace ::com::sun::star; + +namespace sd::tools { + +IdleState IdleDetection::GetIdleState (const vcl::Window* pWindow) +{ + IdleState nResult (CheckInputPending() | CheckSlideShowRunning()); + if (pWindow != nullptr) + nResult |= CheckWindowPainting(*pWindow); + return nResult; +} + +IdleState IdleDetection::CheckInputPending() +{ + if (Application::AnyInput(VclInputFlags::MOUSE | VclInputFlags::KEYBOARD | VclInputFlags::PAINT)) + return IdleState::SystemEventPending; + else + return IdleState::Idle; +} + +IdleState IdleDetection::CheckSlideShowRunning() +{ + IdleState eResult (IdleState::Idle); + + // Iterate over all view frames. + for (SfxViewFrame* pViewFrame = SfxViewFrame::GetFirst(); + pViewFrame!=nullptr; + pViewFrame = SfxViewFrame::GetNext(*pViewFrame)) + { + // Ignore the current frame when it does not exist, is not valid, or + // is not active. + bool bIgnoreFrame (true); + uno::Reference<frame::XFrame> xFrame (pViewFrame->GetFrame().GetFrameInterface()); + try + { + if (xFrame.is() && xFrame->isActive()) + bIgnoreFrame = false; + } + catch (const uno::RuntimeException&) + { + } + if (bIgnoreFrame) + continue; + + // Get sd::ViewShell from active frame. + ViewShellBase* pBase = ViewShellBase::GetViewShellBase(pViewFrame); + if (pBase != nullptr) + { + rtl::Reference< SlideShow > xSlideShow( SlideShow::GetSlideShow( *pBase ) ); + if( xSlideShow.is() && xSlideShow->isRunning() ) + { + if (xSlideShow->isFullScreen()) + eResult |= IdleState::FullScreenShowActive; + else + eResult |= IdleState::WindowShowActive; + } + } + } + + return eResult; +} + +IdleState IdleDetection::CheckWindowPainting (const vcl::Window& rWindow) +{ + if (rWindow.IsInPaint()) + return IdleState::WindowPainting; + else + return IdleState::Idle; +} + +} // end of namespace ::sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/tools/PreviewRenderer.cxx b/sd/source/ui/tools/PreviewRenderer.cxx new file mode 100644 index 000000000..be8d8ca20 --- /dev/null +++ b/sd/source/ui/tools/PreviewRenderer.cxx @@ -0,0 +1,532 @@ +/* -*- 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 <PreviewRenderer.hxx> + +#include <DrawDocShell.hxx> +#include <drawdoc.hxx> +#include <drawview.hxx> +#include <sdpage.hxx> +#include <ViewShell.hxx> +#include <vcl/virdev.hxx> +#include <vcl/settings.hxx> + +#include <svx/svdpagv.hxx> +#include <svx/svdoutl.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/editstat.hxx> +#include <vcl/svapp.hxx> +#include <tools/diagnose_ex.h> +#include <svx/sdr/contact/viewobjectcontact.hxx> +#include <svx/sdr/contact/viewcontact.hxx> + +#include <memory> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd { + +const int PreviewRenderer::snSubstitutionTextSize = 11; +const int PreviewRenderer::snFrameWidth = 1; + +namespace { + /** This incarnation of the ViewObjectContactRedirector filters away all + PageObj objects, unconditionally. + */ + class ViewRedirector : public sdr::contact::ViewObjectContactRedirector + { + public: + ViewRedirector(); + + virtual void createRedirectedPrimitive2DSequence( + const sdr::contact::ViewObjectContact& rOriginal, + const sdr::contact::DisplayInfo& rDisplayInfo, + drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) override; + }; +} + +//===== PreviewRenderer ======================================================= + +PreviewRenderer::PreviewRenderer ( + const bool bHasFrame) + : mpPreviewDevice (VclPtr<VirtualDevice>::Create()), + mpDocShellOfView(nullptr), + maFrameColor (svtools::ColorConfig().GetColorValue(svtools::DOCBOUNDARIES).nColor), + mbHasFrame(bHasFrame) +{ + mpPreviewDevice->SetBackground(Wallpaper( + Application::GetSettings().GetStyleSettings().GetWindowColor())); +} + +PreviewRenderer::~PreviewRenderer() +{ + if (mpDocShellOfView != nullptr) + EndListening (*mpDocShellOfView); +} + +Image PreviewRenderer::RenderPage ( + const SdPage* pPage, + const sal_Int32 nWidth) +{ + if (pPage != nullptr) + { + const Size aPageModelSize (pPage->GetSize()); + const double nAspectRatio ( + double(aPageModelSize.Width()) / double(aPageModelSize.Height())); + const sal_Int32 nFrameWidth (mbHasFrame ? snFrameWidth : 0); + const sal_Int32 nHeight (sal::static_int_cast<sal_Int32>( + (nWidth - 2*nFrameWidth) / nAspectRatio + 2*nFrameWidth + 0.5)); + return RenderPage ( + pPage, + Size(nWidth,nHeight), + false/*bObeyHighContrastMode*/); + } + else + return Image(); +} + +Image PreviewRenderer::RenderPage ( + const SdPage* pPage, + Size aPixelSize, + const bool bObeyHighContrastMode, + const bool bDisplayPresentationObjects) +{ + Image aPreview; + + if (pPage != nullptr) + { + try + { + if (Initialize(pPage, aPixelSize, bObeyHighContrastMode)) + { + PaintPage(pPage, bDisplayPresentationObjects); + PaintSubstitutionText(""); + PaintFrame(); + + Size aSize (mpPreviewDevice->GetOutputSizePixel()); + aPreview = Image(mpPreviewDevice->GetBitmapEx( + mpPreviewDevice->PixelToLogic(Point(0,0)), + mpPreviewDevice->PixelToLogic(aSize))); + + mpView->HideSdrPage(); + } + } + catch (const css::uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("sd.tools"); + } + } + + return aPreview; +} + +Image PreviewRenderer::RenderSubstitution ( + const Size& rPreviewPixelSize, + const OUString& rSubstitutionText) +{ + Image aPreview; + + try + { + // Set size. + mpPreviewDevice->SetOutputSizePixel(rPreviewPixelSize); + + // Adjust contrast mode. + const bool bUseContrast ( + Application::GetSettings().GetStyleSettings().GetHighContrastMode()); + mpPreviewDevice->SetDrawMode (bUseContrast + ? sd::OUTPUT_DRAWMODE_CONTRAST + : sd::OUTPUT_DRAWMODE_COLOR); + + // Set a map mode that makes a typical substitution text completely + // visible. + MapMode aMapMode (mpPreviewDevice->GetMapMode()); + aMapMode.SetMapUnit(MapUnit::Map100thMM); + Fraction aFinalScale(25 * rPreviewPixelSize.Width(), 28000); + aMapMode.SetScaleX(aFinalScale); + aMapMode.SetScaleY(aFinalScale); + const sal_Int32 nFrameWidth (mbHasFrame ? snFrameWidth : 0); + aMapMode.SetOrigin(mpPreviewDevice->PixelToLogic( + Point(nFrameWidth,nFrameWidth),aMapMode)); + mpPreviewDevice->SetMapMode (aMapMode); + + // Clear the background. + const ::tools::Rectangle aPaintRectangle ( + Point(0,0), + mpPreviewDevice->GetOutputSizePixel()); + mpPreviewDevice->EnableMapMode(false); + mpPreviewDevice->SetLineColor(); + svtools::ColorConfig aColorConfig; + mpPreviewDevice->SetFillColor(aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor); + mpPreviewDevice->DrawRect (aPaintRectangle); + mpPreviewDevice->EnableMapMode(); + + // Paint substitution text and a frame around it. + PaintSubstitutionText (rSubstitutionText); + PaintFrame(); + + const Size aSize (mpPreviewDevice->GetOutputSizePixel()); + aPreview = Image(mpPreviewDevice->GetBitmapEx( + mpPreviewDevice->PixelToLogic(Point(0,0)), + mpPreviewDevice->PixelToLogic(aSize))); + } + catch (const css::uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("sd.tools"); + } + + return aPreview; +} + +bool PreviewRenderer::Initialize ( + const SdPage* pPage, + const Size& rPixelSize, + const bool bObeyHighContrastMode) +{ + if (!pPage) + return false; + + SetupOutputSize(*pPage, rPixelSize); + SdDrawDocument& rDocument(static_cast< SdDrawDocument& >(pPage->getSdrModelFromSdrPage())); + DrawDocShell* pDocShell = rDocument.GetDocSh(); + + if (!pDocShell) + return false; + + // Create view + ProvideView (pDocShell); + if (mpView == nullptr) + return false; + + // Adjust contrast mode. + bool bUseContrast (bObeyHighContrastMode + && Application::GetSettings().GetStyleSettings().GetHighContrastMode()); + mpPreviewDevice->SetDrawMode (bUseContrast + ? sd::OUTPUT_DRAWMODE_CONTRAST + : sd::OUTPUT_DRAWMODE_COLOR); + mpPreviewDevice->SetSettings(Application::GetSettings()); + + // Tell the view to show the given page. + SdPage* pNonConstPage = const_cast<SdPage*>(pPage); + if (pPage->IsMasterPage()) + { + mpView->ShowSdrPage(mpView->GetModel()->GetMasterPage(pPage->GetPageNum())); + } + else + { + mpView->ShowSdrPage(pNonConstPage); + } + + // Make sure that a page view exists. + SdrPageView* pPageView = mpView->GetSdrPageView(); + + if (pPageView == nullptr) + return false; + + // #i121224# No need to set SetApplicationBackgroundColor (which is the color + // of the area 'behind' the page (formerly called 'Wiese') since the page previews + // produced exactly cover the page's area, so it would never be visible. What + // needs to be set is the ApplicationDocumentColor which is derived from + // svtools::DOCCOLOR normally + Color aApplicationDocumentColor; + + if (pPageView->GetApplicationDocumentColor() == COL_AUTO) + { + svtools::ColorConfig aColorConfig; + aApplicationDocumentColor = aColorConfig.GetColorValue( svtools::DOCCOLOR ).nColor; + } + else + { + aApplicationDocumentColor = pPageView->GetApplicationDocumentColor(); + } + + pPageView->SetApplicationDocumentColor(aApplicationDocumentColor); + SdrOutliner& rOutliner(rDocument.GetDrawOutliner()); + rOutliner.SetBackgroundColor(aApplicationDocumentColor); + rOutliner.SetDefaultLanguage(rDocument.GetLanguage(EE_CHAR_LANGUAGE)); + mpPreviewDevice->SetBackground(Wallpaper(aApplicationDocumentColor)); + mpPreviewDevice->Erase(); + + return true; +} + +void PreviewRenderer::PaintPage ( + const SdPage* pPage, + const bool bDisplayPresentationObjects) +{ + // Paint the page. + ::tools::Rectangle aPaintRectangle (Point(0,0), pPage->GetSize()); + vcl::Region aRegion (aPaintRectangle); + + // Turn off online spelling and redlining. + SdrOutliner* pOutliner = nullptr; + EEControlBits nSavedControlWord = EEControlBits::NONE; + if (mpDocShellOfView!=nullptr && mpDocShellOfView->GetDoc()!=nullptr) + { + pOutliner = &mpDocShellOfView->GetDoc()->GetDrawOutliner(); + nSavedControlWord = pOutliner->GetControlWord(); + pOutliner->SetControlWord(nSavedControlWord & ~EEControlBits::ONLINESPELLING); + } + + // Use a special redirector to prevent PresObj shapes from being painted. + std::unique_ptr<ViewRedirector> pRedirector; + if ( ! bDisplayPresentationObjects) + pRedirector.reset(new ViewRedirector()); + + try + { + mpView->CompleteRedraw(mpPreviewDevice.get(), aRegion, pRedirector.get()); + } + catch (const css::uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("sd.tools"); + } + + // Restore the previous online spelling and redlining states. + if (pOutliner != nullptr) + pOutliner->SetControlWord(nSavedControlWord); +} + +void PreviewRenderer::PaintSubstitutionText (const OUString& rSubstitutionText) +{ + if (rSubstitutionText.isEmpty()) + return; + + // Set the font size. + const vcl::Font& rOriginalFont (mpPreviewDevice->GetFont()); + vcl::Font aFont (mpPreviewDevice->GetSettings().GetStyleSettings().GetAppFont()); + sal_Int32 nHeight (mpPreviewDevice->PixelToLogic(Size(0,snSubstitutionTextSize)).Height()); + aFont.SetFontHeight(nHeight); + mpPreviewDevice->SetFont (aFont); + + // Paint the substitution text. + ::tools::Rectangle aTextBox ( + Point(0,0), + mpPreviewDevice->PixelToLogic( + mpPreviewDevice->GetOutputSizePixel())); + DrawTextFlags const nTextStyle = + DrawTextFlags::Center + | DrawTextFlags::VCenter + | DrawTextFlags::MultiLine + | DrawTextFlags::WordBreak; + mpPreviewDevice->DrawText (aTextBox, rSubstitutionText, nTextStyle); + + // Restore the font. + mpPreviewDevice->SetFont (rOriginalFont); +} + +void PreviewRenderer::PaintFrame() +{ + if (mbHasFrame) + { + // Paint a frame around the preview. + ::tools::Rectangle aPaintRectangle ( + Point(0,0), + mpPreviewDevice->GetOutputSizePixel()); + mpPreviewDevice->EnableMapMode(false); + mpPreviewDevice->SetLineColor(maFrameColor); + mpPreviewDevice->SetFillColor(); + mpPreviewDevice->DrawRect(aPaintRectangle); + mpPreviewDevice->EnableMapMode(); + } +} + +void PreviewRenderer::SetupOutputSize ( + const SdPage& rPage, + const Size& rFramePixelSize) +{ + // First set the map mode to some arbitrary scale that is numerically + // stable. + MapMode aMapMode (mpPreviewDevice->GetMapMode()); + aMapMode.SetMapUnit(MapUnit::MapPixel); + + // Adapt it to the desired width. + const Size aPageModelSize (rPage.GetSize()); + if (!aPageModelSize.IsEmpty()) + { + const sal_Int32 nFrameWidth (mbHasFrame ? snFrameWidth : 0); + aMapMode.SetScaleX( + Fraction(rFramePixelSize.Width()-2*nFrameWidth-1, aPageModelSize.Width())); + aMapMode.SetScaleY( + Fraction(rFramePixelSize.Height()-2*nFrameWidth-1, aPageModelSize.Height())); + aMapMode.SetOrigin(mpPreviewDevice->PixelToLogic(Point(nFrameWidth,nFrameWidth),aMapMode)); + } + else + { + // We should never get here. + OSL_ASSERT(false); + aMapMode.SetScaleX(Fraction(1.0)); + aMapMode.SetScaleY(Fraction(1.0)); + } + mpPreviewDevice->SetMapMode (aMapMode); + mpPreviewDevice->SetOutputSizePixel(rFramePixelSize); +} + +void PreviewRenderer::ProvideView (DrawDocShell* pDocShell) +{ + if (pDocShell != mpDocShellOfView) + { + // Destroy the view that is connected to the current doc shell. + mpView.reset(); + + // Switch our attention, i.e. listening for DYING events, to + // the new doc shell. + if (mpDocShellOfView != nullptr) + EndListening (*mpDocShellOfView); + mpDocShellOfView = pDocShell; + if (mpDocShellOfView != nullptr) + StartListening (*mpDocShellOfView); + } + if (mpView == nullptr) + { + mpView.reset (new DrawView (pDocShell, mpPreviewDevice.get(), nullptr)); + } + mpView->SetPreviewRenderer(true); +#if 1 + mpView->SetPageVisible(false); + mpView->SetPageBorderVisible(); + mpView->SetBordVisible(false); + mpView->SetGridVisible(false); + mpView->SetHlplVisible(false); + mpView->SetGlueVisible(false); + +#else + // This works in the slide sorter but prevents the master page + // background being painted in the list of current master pages in the + // task manager. + mpView->SetPagePaintingAllowed(false); +#endif +} + +Image PreviewRenderer::ScaleBitmap ( + const BitmapEx& rBitmapEx, + int nWidth) +{ + Image aPreview; + + do + { + // Adjust contrast mode. + bool bUseContrast = Application::GetSettings().GetStyleSettings(). + GetHighContrastMode(); + mpPreviewDevice->SetDrawMode (bUseContrast + ? sd::OUTPUT_DRAWMODE_CONTRAST + : sd::OUTPUT_DRAWMODE_COLOR); + + // Set output size. + Size aSize (rBitmapEx.GetSizePixel()); + if (aSize.Width() <= 0) + break; + Size aFrameSize ( + nWidth, + static_cast<::tools::Long>((nWidth*1.0 * aSize.Height()) / aSize.Width() + 0.5)); + Size aPreviewSize (aFrameSize.Width()-2,aFrameSize.Height()-2); + MapMode aMapMode (mpPreviewDevice->GetMapMode()); + aMapMode.SetMapUnit(MapUnit::MapPixel); + aMapMode.SetOrigin (Point()); + aMapMode.SetScaleX (Fraction(1.0)); + aMapMode.SetScaleY (Fraction(1.0)); + mpPreviewDevice->SetMapMode (aMapMode); + mpPreviewDevice->SetOutputSize (aFrameSize); + + // Paint a frame around the preview. + mpPreviewDevice->SetLineColor (maFrameColor); + mpPreviewDevice->SetFillColor (); + mpPreviewDevice->DrawRect (::tools::Rectangle(Point(0,0), aFrameSize)); + + // Paint the bitmap scaled to the desired width. + BitmapEx aScaledBitmap(rBitmapEx); + aScaledBitmap.Scale (aPreviewSize, BmpScaleFlag::BestQuality); + mpPreviewDevice->DrawBitmapEx ( + Point(1,1), + aPreviewSize, + aScaledBitmap); + + // Get the resulting bitmap. + aPreview = Image(mpPreviewDevice->GetBitmapEx(Point(0,0), aFrameSize)); + } + while (false); + + return aPreview; +} + +void PreviewRenderer::Notify(SfxBroadcaster&, const SfxHint& rHint) +{ + if (!mpDocShellOfView) + return; + + if (rHint.GetId() == SfxHintId::Dying) + { + // The doc shell is dying. Our view uses its item pool and + // has to be destroyed as well. The next call to + // ProvideView will create a new one (for another + // doc shell, of course.) + mpView.reset(); + mpDocShellOfView = nullptr; + } +} + +//===== ViewRedirector ======================================================== + +namespace { + +ViewRedirector::ViewRedirector() +{ +} + +void ViewRedirector::createRedirectedPrimitive2DSequence( + const sdr::contact::ViewObjectContact& rOriginal, + const sdr::contact::DisplayInfo& rDisplayInfo, + drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) +{ + SdrObject* pObject = rOriginal.GetViewContact().TryToGetSdrObject(); + + if (pObject==nullptr || pObject->getSdrPageFromSdrObject() == nullptr) + { + // not a SdrObject visualisation (maybe e.g. page) or no page + sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence( + rOriginal, + rDisplayInfo, + rVisitor); + return; + } + + const bool bDoCreateGeometry (pObject->getSdrPageFromSdrObject()->checkVisibility( rOriginal, rDisplayInfo, true)); + + if ( ! bDoCreateGeometry + && (pObject->GetObjInventor() != SdrInventor::Default || pObject->GetObjIdentifier() != SdrObjKind::Page)) + { + return; + } + + if (pObject->IsEmptyPresObj()) + return; + + sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence( + rOriginal, + rDisplayInfo, + rVisitor); +} + +} // end of anonymous namespace + +} // end of namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/tools/PropertySet.cxx b/sd/source/ui/tools/PropertySet.cxx new file mode 100644 index 000000000..057b7dd96 --- /dev/null +++ b/sd/source/ui/tools/PropertySet.cxx @@ -0,0 +1,158 @@ +/* -*- 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 <tools/PropertySet.hxx> +#include <algorithm> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::tools { + +PropertySet::PropertySet() + : PropertySetInterfaceBase(m_aMutex), + mpChangeListeners(new ChangeListenerContainer) +{ +} + +PropertySet::~PropertySet() +{ +} + +void SAL_CALL PropertySet::disposing() +{ +} + +//----- XPropertySet ---------------------------------------------------------- + +Reference<beans::XPropertySetInfo> SAL_CALL PropertySet::getPropertySetInfo() +{ + return nullptr; +} + +void SAL_CALL PropertySet::setPropertyValue ( + const OUString& rsPropertyName, + const css::uno::Any& rsPropertyValue) +{ + ThrowIfDisposed(); + + Any aOldValue (SetPropertyValue(rsPropertyName,rsPropertyValue)); + if (aOldValue == rsPropertyValue) + return; + + // Inform listeners that are registered specifically for the + // property and those registered for any property. + beans::PropertyChangeEvent aEvent( + static_cast<XWeak*>(this), + rsPropertyName, + false, + -1, + aOldValue, + rsPropertyValue); + CallListeners(rsPropertyName, aEvent); + CallListeners(OUString(), aEvent); +} + +Any SAL_CALL PropertySet::getPropertyValue (const OUString& rsPropertyName) +{ + ThrowIfDisposed(); + + return GetPropertyValue(rsPropertyName); +} + +void SAL_CALL PropertySet::addPropertyChangeListener ( + const OUString& rsPropertyName, + const css::uno::Reference<css::beans::XPropertyChangeListener>& rxListener) +{ + if ( ! rxListener.is()) + throw lang::IllegalArgumentException(); + + if (rBHelper.bDisposed || rBHelper.bInDispose) + return; + + mpChangeListeners->emplace(rsPropertyName, rxListener); +} + +void SAL_CALL PropertySet::removePropertyChangeListener ( + const OUString& rsPropertyName, + const css::uno::Reference<css::beans::XPropertyChangeListener>& rxListener) +{ + ::std::pair<ChangeListenerContainer::iterator,ChangeListenerContainer::iterator> + aRange (mpChangeListeners->equal_range(rsPropertyName)); + + ChangeListenerContainer::iterator iListener ( + ::std::find_if( + aRange.first, + aRange.second, + [&rxListener] (const ChangeListenerContainer::value_type& listener) { + return listener.second == rxListener; + })); + + if (iListener == mpChangeListeners->end()) + { + throw lang::IllegalArgumentException(); + } + + mpChangeListeners->erase(iListener); + +} + +void SAL_CALL PropertySet::addVetoableChangeListener ( + const OUString&, + const css::uno::Reference<css::beans::XVetoableChangeListener>&) +{ + // Constraint properties are not supported and thus no vetoable + // listeners. +} + +void SAL_CALL PropertySet::removeVetoableChangeListener ( + const OUString&, + const css::uno::Reference<css::beans::XVetoableChangeListener>&) +{ + // Constraint properties are not supported and thus no vetoable + // listeners. +} + +void PropertySet::CallListeners ( + const OUString& rsPropertyName, + const beans::PropertyChangeEvent& rEvent) +{ + ::std::pair<ChangeListenerContainer::iterator,ChangeListenerContainer::iterator> + aRange (mpChangeListeners->equal_range(rsPropertyName)); + ChangeListenerContainer::const_iterator iListener; + for (iListener=aRange.first; iListener!=aRange.second; ++iListener) + { + if (iListener->second.is()) + iListener->second->propertyChange(rEvent); + } +} + +void PropertySet::ThrowIfDisposed() +{ + if (rBHelper.bDisposed || rBHelper.bInDispose) + { + throw lang::DisposedException ( + "PropertySet object has already been disposed", + static_cast<uno::XWeak*>(this)); + } +} + +} // end of namespace ::sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/tools/SdGlobalResourceContainer.cxx b/sd/source/ui/tools/SdGlobalResourceContainer.cxx new file mode 100644 index 000000000..a7dbc5f25 --- /dev/null +++ b/sd/source/ui/tools/SdGlobalResourceContainer.cxx @@ -0,0 +1,197 @@ +/* -*- 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 <tools/SdGlobalResourceContainer.hxx> + +#include <../cache/SlsCacheConfiguration.hxx> + +#include <comphelper/processfactory.hxx> +#include <comphelper/unique_disposing_ptr.hxx> + +#include <com/sun/star/frame/Desktop.hpp> + +#include <sal/log.hxx> +#include <tools/debug.hxx> + +#include <algorithm> +#include <memory> +#include <vector> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd { + +class SdGlobalResourceContainerInstance + : public comphelper::unique_disposing_solar_mutex_reset_ptr<SdGlobalResourceContainer> +{ +public: + SdGlobalResourceContainerInstance() + : comphelper::unique_disposing_solar_mutex_reset_ptr<SdGlobalResourceContainer>( + uno::Reference<lang::XComponent>(frame::Desktop::create(comphelper::getProcessComponentContext()), uno::UNO_QUERY_THROW), + new SdGlobalResourceContainer, true) + { + } +}; + +namespace { + +SdGlobalResourceContainerInstance& theSdGlobalResourceContainerInstance() +{ + static SdGlobalResourceContainerInstance SINGLETON; + return SINGLETON; +} + +} // namespace + +//===== SdGlobalResourceContainer::Implementation ============================= + +class SdGlobalResourceContainer::Implementation +{ +private: + friend class SdGlobalResourceContainer; + + ::osl::Mutex maMutex; + + /** All instances of SdGlobalResource in this vector are owned by the + container and will be destroyed when the container is destroyed. + */ + std::vector<std::unique_ptr<SdGlobalResource>> maResources; + + typedef ::std::vector<std::shared_ptr<SdGlobalResource> > SharedResourceList; + SharedResourceList maSharedResources; + + typedef ::std::vector<Reference<XInterface> > XInterfaceResourceList; + XInterfaceResourceList maXInterfaceResources; +}; + +// static +SdGlobalResourceContainer& SdGlobalResourceContainer::Instance() +{ + SdGlobalResourceContainer *const pRet(theSdGlobalResourceContainerInstance().get()); + assert(pRet); // error if it has been deleted and is null + return *pRet; +} + +//===== SdGlobalResourceContainer ============================================= + +void SdGlobalResourceContainer::AddResource ( + ::std::unique_ptr<SdGlobalResource> pResource) +{ + ::osl::MutexGuard aGuard (mpImpl->maMutex); + + assert( std::none_of( + mpImpl->maResources.begin(), + mpImpl->maResources.end(), + [&](const std::unique_ptr<SdGlobalResource>& p) { return p == pResource; }) + && "duplicate resource?"); + + mpImpl->maResources.push_back(std::move(pResource)); +} + +void SdGlobalResourceContainer::AddResource ( + const std::shared_ptr<SdGlobalResource>& pResource) +{ + ::osl::MutexGuard aGuard (mpImpl->maMutex); + + Implementation::SharedResourceList::iterator iResource = ::std::find ( + mpImpl->maSharedResources.begin(), + mpImpl->maSharedResources.end(), + pResource); + if (iResource == mpImpl->maSharedResources.end()) + mpImpl->maSharedResources.push_back(pResource); + else + { + SAL_WARN ("sd.tools", + "SdGlobalResourceContainer:AddResource(): Resource added twice."); + } +} + +void SdGlobalResourceContainer::AddResource (const Reference<XInterface>& rxResource) +{ + ::osl::MutexGuard aGuard (mpImpl->maMutex); + + Implementation::XInterfaceResourceList::iterator iResource = ::std::find ( + mpImpl->maXInterfaceResources.begin(), + mpImpl->maXInterfaceResources.end(), + rxResource); + if (iResource == mpImpl->maXInterfaceResources.end()) + mpImpl->maXInterfaceResources.push_back(rxResource); + else + { + SAL_WARN ("sd.tools", + "SdGlobalResourceContainer:AddResource(): Resource added twice."); + } +} + +SdGlobalResourceContainer::SdGlobalResourceContainer() + : mpImpl (new SdGlobalResourceContainer::Implementation) +{ +} + +SdGlobalResourceContainer::~SdGlobalResourceContainer() +{ + ::osl::MutexGuard aGuard (mpImpl->maMutex); + + // Release the resources in reversed order of their addition to the + // container. This is because a resource A added before resource B + // may have been created due to a request of B. Thus B depends on A and + // should be destroyed first. + for (auto iResource = mpImpl->maResources.rbegin(); + iResource != mpImpl->maResources.rend(); + ++iResource) + { + iResource->reset(); + } + + + // The SharedResourceList has not to be released manually. We just + // assert resources that are still held by someone other than us. + Implementation::SharedResourceList::reverse_iterator iSharedResource; + for (iSharedResource = mpImpl->maSharedResources.rbegin(); + iSharedResource != mpImpl->maSharedResources.rend(); + ++iSharedResource) + { + if (iSharedResource->use_count() > 1) + { + SdGlobalResource* pResource = iSharedResource->get(); + SAL_INFO( + "sd.tools", pResource << " " << iSharedResource->use_count()); + DBG_ASSERT(iSharedResource->use_count() == 1, + "SdGlobalResource still held in ~SdGlobalResourceContainer"); + } + } + + Implementation::XInterfaceResourceList::reverse_iterator iXInterfaceResource; + for (iXInterfaceResource = mpImpl->maXInterfaceResources.rbegin(); + iXInterfaceResource != mpImpl->maXInterfaceResources.rend(); + ++iXInterfaceResource) + { + Reference<lang::XComponent> xComponent (*iXInterfaceResource, UNO_QUERY); + *iXInterfaceResource = nullptr; + if (xComponent.is()) + xComponent->dispose(); + } + + sd::slidesorter::cache::CacheConfiguration::Shutdown(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/tools/SlotStateListener.cxx b/sd/source/ui/tools/SlotStateListener.cxx new file mode 100644 index 000000000..9b75b322e --- /dev/null +++ b/sd/source/ui/tools/SlotStateListener.cxx @@ -0,0 +1,153 @@ +/* -*- 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 <tools/SlotStateListener.hxx> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/frame/XDispatch.hpp> +#include <com/sun/star/util/URLTransformer.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> + +#include <comphelper/processfactory.hxx> + +using namespace ::com::sun::star; + +namespace sd::tools { + +SlotStateListener::SlotStateListener ( + Link<const OUString&,void> const & rCallback, + const uno::Reference<frame::XDispatchProvider>& rxDispatchProvider, + const OUString& rSlotName) + : mxDispatchProviderWeak(nullptr) +{ + SetCallback(rCallback); + ConnectToDispatchProvider(rxDispatchProvider); + ObserveSlot(rSlotName); +} + +SlotStateListener::~SlotStateListener() +{ + ReleaseListeners(); +} + +void SlotStateListener::SetCallback (const Link<const OUString&,void>& rCallback) +{ + ThrowIfDisposed(); + + maCallback = rCallback; +} + +void SlotStateListener::ConnectToDispatchProvider ( + const uno::Reference<frame::XDispatchProvider>& rxDispatchProvider) +{ + ThrowIfDisposed(); + + // When we are listening to state changes of slots of another frame then + // release these listeners first. + if ( ! maRegisteredURLList.empty()) + ReleaseListeners(); + + mxDispatchProviderWeak = rxDispatchProvider; +} + +void SlotStateListener::ObserveSlot (const OUString& rSlotName) +{ + ThrowIfDisposed(); + + if (maCallback.IsSet()) + { + // Connect the state change listener. + util::URL aURL (MakeURL(rSlotName)); + uno::Reference<frame::XDispatch> xDispatch (GetDispatch(aURL)); + if (xDispatch.is()) + { + maRegisteredURLList.push_back(aURL); + xDispatch->addStatusListener(this,aURL); + } + } +} + +void SlotStateListener::disposing(std::unique_lock<std::mutex>&) +{ + ReleaseListeners(); + mxDispatchProviderWeak.clear(); + maCallback = Link<const OUString&,void>(); +} + +util::URL SlotStateListener::MakeURL (const OUString& rSlotName) +{ + util::URL aURL; + aURL.Complete = rSlotName; + + uno::Reference<util::XURLTransformer> xTransformer(util::URLTransformer::create(::comphelper::getProcessComponentContext())); + xTransformer->parseStrict(aURL); + + return aURL; +} + +uno::Reference<frame::XDispatch> + SlotStateListener::GetDispatch (const util::URL& rURL) const +{ + uno::Reference<frame::XDispatch> xDispatch; + + uno::Reference<frame::XDispatchProvider> xDispatchProvider (mxDispatchProviderWeak); + if (xDispatchProvider.is()) + xDispatch = xDispatchProvider->queryDispatch(rURL, OUString(), 0); + + return xDispatch; +} + +void SlotStateListener::statusChanged ( + const frame::FeatureStateEvent& rState) +{ + ThrowIfDisposed(); + OUString sSlotName (rState.FeatureURL.Complete); + maCallback.Call(sSlotName); +} + +void SlotStateListener::ReleaseListeners() +{ + for (const auto& rURL : maRegisteredURLList) + { + uno::Reference<frame::XDispatch> xDispatch (GetDispatch(rURL)); + if (xDispatch.is()) + { + xDispatch->removeStatusListener(this,rURL); + } + } +} + +//===== lang::XEventListener ================================================ + +void SAL_CALL SlotStateListener::disposing ( + const lang::EventObject& ) +{ +} + +void SlotStateListener::ThrowIfDisposed() +{ + if (m_bDisposed) + { + throw lang::DisposedException ("SlideSorterController object has already been disposed", + static_cast<uno::XWeak*>(this)); + } +} + +} // end of namespace ::sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/tools/TimerBasedTaskExecution.cxx b/sd/source/ui/tools/TimerBasedTaskExecution.cxx new file mode 100644 index 000000000..ae1f2233f --- /dev/null +++ b/sd/source/ui/tools/TimerBasedTaskExecution.cxx @@ -0,0 +1,130 @@ +/* -*- 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 <tools/TimerBasedTaskExecution.hxx> +#include <tools/AsynchronousTask.hxx> +#include <tools/time.hxx> +#include <sal/log.hxx> +#include <memory> + +namespace sd::tools { + +/** Used by the shared_ptr instead of the private destructor. +*/ +class TimerBasedTaskExecution::Deleter +{ +public: + void operator() (TimerBasedTaskExecution* pObject) + { + delete pObject; + } +}; + +std::shared_ptr<TimerBasedTaskExecution> TimerBasedTaskExecution::Create ( + const std::shared_ptr<AsynchronousTask>& rpTask, + sal_uInt32 nMillisecondsBetweenSteps, + sal_uInt32 nMaxTimePerStep) +{ + std::shared_ptr<TimerBasedTaskExecution> pExecution( + new TimerBasedTaskExecution(rpTask,nMillisecondsBetweenSteps,nMaxTimePerStep), + Deleter()); + // Let the new object have a shared_ptr to itself, so that it can + // release itself when the AsynchronousTask has been executed + // completely. + if (pExecution->mpTask != nullptr) + pExecution->mpSelf = pExecution; + return pExecution; +} + +void TimerBasedTaskExecution::Release() +{ + maTimer.Stop(); + mpSelf.reset(); +} + +//static +void TimerBasedTaskExecution::ReleaseTask ( + const std::weak_ptr<TimerBasedTaskExecution>& rpExecution) +{ + if ( rpExecution.expired()) + return; + + try + { + std::shared_ptr<tools::TimerBasedTaskExecution> pExecution (rpExecution); + pExecution->Release(); + } + catch (const std::bad_weak_ptr&) + { + // When a bad_weak_ptr has been thrown then the object pointed + // to by rpTask has been released right after we checked that it + // still existed. Too bad, but that means, that we have nothing + // more do. + } +} + +TimerBasedTaskExecution::TimerBasedTaskExecution ( + const std::shared_ptr<AsynchronousTask>& rpTask, + sal_uInt32 nMillisecondsBetweenSteps, + sal_uInt32 nMaxTimePerStep) + : mpTask(rpTask), + maTimer("sd TimerBasedTaskExecution maTimer"), + mnMaxTimePerStep(nMaxTimePerStep) +{ + maTimer.SetInvokeHandler( LINK(this,TimerBasedTaskExecution,TimerCallback) ); + maTimer.SetTimeout(nMillisecondsBetweenSteps); + maTimer.Start(); +} + +TimerBasedTaskExecution::~TimerBasedTaskExecution() +{ + maTimer.Stop(); +} + +IMPL_LINK_NOARG(TimerBasedTaskExecution, TimerCallback, Timer *, void) +{ + if (mpTask == nullptr) + return; + + if (mpTask->HasNextStep()) + { + // Execute as many steps as fit into the time span of length + // mnMaxTimePerStep. Note that the last step may take longer + // than allowed. + sal_uInt32 nStartTime (::tools::Time( ::tools::Time::SYSTEM ).GetMSFromTime()); + SAL_INFO("sd.tools", __func__ << ": starting TimerBasedTaskExecution at " << nStartTime); + do + { + mpTask->RunNextStep(); + sal_uInt32 nDuration (::tools::Time( ::tools::Time::SYSTEM ).GetMSFromTime()-nStartTime); + SAL_INFO("sd.tools", __func__ << ": executed step in " << nDuration); + if (nDuration > mnMaxTimePerStep) + break; + } + while (mpTask->HasNextStep()); + SAL_INFO("sd.tools", __func__ << ": TimerBasedTaskExecution sleeping"); + maTimer.Start(); + } + else + mpSelf.reset(); +} + +} // end of namespace ::sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/uitest/uiobject.cxx b/sd/source/ui/uitest/uiobject.cxx new file mode 100644 index 000000000..afd130ee2 --- /dev/null +++ b/sd/source/ui/uitest/uiobject.cxx @@ -0,0 +1,183 @@ +/* -*- 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/. + */ + +#include <memory> +#include <uiobject.hxx> + +#include <Window.hxx> +#include <DrawViewShell.hxx> +#include <sdpage.hxx> + +#include <sfx2/sidebar/Sidebar.hxx> +#include <sfx2/sfxsids.hrc> +#include <svx/uiobject.hxx> +#include <tools/debug.hxx> + +namespace +{ +class ImpressSdrObject : public SdrUIObject +{ +public: + ImpressSdrObject(const VclPtr<sd::Window>& xImpressWin, const OUString& rName); + + SdrObject* get_object() override; + +private: + VclPtr<sd::Window> mxWindow; + + OUString maName; +}; + +sd::DrawViewShell* getViewShell(const VclPtr<sd::Window>& xWindow) +{ + sd::DrawViewShell* pViewShell = dynamic_cast<sd::DrawViewShell*>(xWindow->GetViewShell()); + assert(pViewShell); + + return pViewShell; +} + +OUString getObjectName(SdrObject const* pObject) +{ + if (pObject->GetName().isEmpty()) + return "Unnamed Drawinglayer object " + OUString::number(pObject->GetOrdNum()); + else + return pObject->GetName(); +} + +SdrObject* getObject(const VclPtr<sd::Window>& xWindow, std::u16string_view rName) +{ + SdrPage* pPage = getViewShell(xWindow)->getCurrentPage(); + + if (!pPage) + return nullptr; + + size_t nObjs = pPage->GetObjCount(); + for (size_t i = 0; i < nObjs; ++i) + { + SdrObject* pObj = pPage->GetObj(i); + if (rName == getObjectName(pObj)) + return pObj; + } + + return nullptr; +} +} + +ImpressSdrObject::ImpressSdrObject(const VclPtr<sd::Window>& xImpressWin, const OUString& rName) + : mxWindow(xImpressWin) + , maName(rName) +{ +} + +SdrObject* ImpressSdrObject::get_object() { return getObject(mxWindow, maName); } + +ImpressWindowUIObject::ImpressWindowUIObject(const VclPtr<sd::Window>& xWindow) + : WindowUIObject(xWindow) + , mxWindow(xWindow) +{ +} + +StringMap ImpressWindowUIObject::get_state() +{ + StringMap aMap = WindowUIObject::get_state(); + + aMap["SelectedText"] = getViewShell(mxWindow)->GetSelectionText(false); + aMap["CurrentSlide"] = OUString::number(getViewShell(mxWindow)->GetCurPagePos() + 1); + aMap["Zoom"] = OUString::number(getViewShell(mxWindow)->GetZoom()); + + return aMap; +} + +void ImpressWindowUIObject::execute(const OUString& rAction, const StringMap& rParameters) +{ + if (rAction == "SET") + { + if (rParameters.find("ZOOM") != rParameters.end()) + { + auto itr = rParameters.find("ZOOM"); + OUString aVal = itr->second; + sal_Int32 nVal = aVal.toInt32(); + getViewShell(mxWindow)->SetZoom(nVal); + } + } + else if (rAction == "GOTO") + { + if (rParameters.find("PAGE") != rParameters.end()) + { + auto itr = rParameters.find("PAGE"); + OUString aVal = itr->second; + sal_Int32 nVal = aVal.toInt32(); + getViewShell(mxWindow)->SwitchPage(nVal - 1); + } + } + else if (rAction == "SELECT") + { + if (rParameters.find("OBJECT") != rParameters.end()) + { + auto itr = rParameters.find("OBJECT"); + OUString aName = itr->second; + SdrObject* pObj = getObject(mxWindow, aName); + SdrPageView* pPageView = getViewShell(mxWindow)->GetView()->GetSdrPageView(); + getViewShell(mxWindow)->GetView()->MarkObj(pObj, pPageView); + } + } + else if (rAction == "SIDEBAR") + { + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + DBG_ASSERT(pViewFrm, "ImpressWindowUIObject::execute: no viewframe"); + pViewFrm->ShowChildWindow(SID_SIDEBAR); + + auto itr = rParameters.find("PANEL"); + if (itr != rParameters.end()) + { + OUString aVal = itr->second; + ::sfx2::sidebar::Sidebar::ShowPanel(aVal, pViewFrm->GetFrame().GetFrameInterface()); + } + } + else if (rAction == "DESELECT") + { + getViewShell(mxWindow)->GetView()->UnMarkAll(); + } + else + WindowUIObject::execute(rAction, rParameters); +} + +std::unique_ptr<UIObject> ImpressWindowUIObject::get_child(const OUString& rID) +{ + return std::unique_ptr<UIObject>(new ImpressSdrObject(mxWindow, rID)); +} + +std::set<OUString> ImpressWindowUIObject::get_children() const +{ + SdrPage* pPage = getViewShell(mxWindow)->getCurrentPage(); + + std::set<OUString> aRet; + if (!pPage) + return aRet; + + size_t nObjs = pPage->GetObjCount(); + for (size_t i = 0; i < nObjs; ++i) + { + SdrObject* pObject = pPage->GetObj(i); + aRet.insert(getObjectName(pObject)); + } + + return aRet; +} + +OUString ImpressWindowUIObject::get_name() const { return "ImpressWindowUIObject"; } + +std::unique_ptr<UIObject> ImpressWindowUIObject::create(vcl::Window* pWindow) +{ + sd::Window* pWin = dynamic_cast<sd::Window*>(pWindow); + assert(pWin); + return std::unique_ptr<UIObject>(new ImpressWindowUIObject(pWin)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/DrawController.cxx b/sd/source/ui/unoidl/DrawController.cxx new file mode 100644 index 000000000..e4afc842a --- /dev/null +++ b/sd/source/ui/unoidl/DrawController.cxx @@ -0,0 +1,815 @@ +/* -*- 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 <DrawController.hxx> + +#include <sdpage.hxx> +#include <ViewShell.hxx> +#include <ViewShellBase.hxx> +#include <ViewShellManager.hxx> +#include <FormShellManager.hxx> +#include <Window.hxx> + +#include <comphelper/processfactory.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/typeprovider.hxx> + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/drawing/framework/ConfigurationController.hpp> +#include <com/sun/star/drawing/framework/ModuleController.hpp> +#include <com/sun/star/drawing/XDrawSubController.hpp> +#include <com/sun/star/drawing/XLayer.hpp> + +#include <slideshow.hxx> + +#include <sal/log.hxx> +#include <svx/fmshell.hxx> +#include <vcl/svapp.hxx> +#include <vcl/EnumContext.hxx> +#include <svx/sidebar/ContextChangeEventMultiplexer.hxx> +#include <tools/diagnose_ex.h> + +#include <memory> + +using namespace ::std; +using namespace ::cppu; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; +using vcl::EnumContext; + +namespace sd { + +DrawController::DrawController (ViewShellBase& rBase) noexcept + : DrawControllerInterfaceBase(&rBase), + BroadcastHelperOwner(SfxBaseController::m_aMutex), + OPropertySetHelper(BroadcastHelperOwner::maBroadcastHelper), + mpCurrentLayer(nullptr), + m_aSelectionTypeIdentifier( + cppu::UnoType<view::XSelectionChangeListener>::get()), + mpBase(&rBase), + mpCurrentPage(nullptr), + mbMasterPageMode(false), + mbLayerMode(false), + mbDisposing(false) +{ + ProvideFrameworkControllers(); +} + +DrawController::~DrawController() noexcept +{ +} + +void DrawController::SetSubController ( + const Reference<drawing::XDrawSubController>& rxSubController) +{ + // Update the internal state. + mxSubController = rxSubController; + mpPropertyArrayHelper.reset(); + maLastVisArea = ::tools::Rectangle(); + + // Inform listeners about the changed state. + FireSelectionChangeListener(); +} + +// XInterface + +IMPLEMENT_FORWARD_XINTERFACE2( + DrawController, + DrawControllerInterfaceBase, + OPropertySetHelper); + +// XTypeProvider + +Sequence<Type> SAL_CALL DrawController::getTypes() +{ + ThrowIfDisposed(); + // OPropertySetHelper does not provide getTypes, so we have to + // implement this method manually and list its three interfaces. + OTypeCollection aTypeCollection ( + cppu::UnoType<beans::XMultiPropertySet>::get(), + cppu::UnoType<beans::XFastPropertySet>::get(), + cppu::UnoType<beans::XPropertySet>::get()); + + return ::comphelper::concatSequences( + SfxBaseController::getTypes(), + aTypeCollection.getTypes(), + DrawControllerInterfaceBase::getTypes()); +} + +IMPLEMENT_GET_IMPLEMENTATION_ID(DrawController); + +// XComponent + +void SAL_CALL DrawController::dispose() +{ + if( mbDisposing ) + return; + + SolarMutexGuard aGuard; + + if( mbDisposing ) + return; + + mbDisposing = true; + + std::shared_ptr<ViewShell> pViewShell; + if (mpBase) + pViewShell = mpBase->GetMainViewShell(); + if ( pViewShell ) + { + pViewShell->DeactivateCurrentFunction(); + auto* pView = pViewShell->GetView(); + if (pView) + pView->getSearchContext().resetSearchFunction(); + } + pViewShell.reset(); + + // When the controller has not been detached from its view + // shell, i.e. mpViewShell is not NULL, then tell PaneManager + // and ViewShellManager to clear the shell stack. + if (mxSubController.is() && mpBase!=nullptr) + { + mpBase->DisconnectAllClients(); + mpBase->GetViewShellManager()->Shutdown(); + } + + OPropertySetHelper::disposing(); + + DisposeFrameworkControllers(); + + SfxBaseController::dispose(); +} + +void SAL_CALL DrawController::addEventListener( + const Reference<lang::XEventListener >& xListener) +{ + ThrowIfDisposed(); + SfxBaseController::addEventListener( xListener ); +} + +void SAL_CALL DrawController::removeEventListener ( + const Reference<lang::XEventListener >& aListener) +{ + if(!rBHelper.bDisposed && !rBHelper.bInDispose && !mbDisposing) + SfxBaseController::removeEventListener( aListener ); +} + +// XController +sal_Bool SAL_CALL DrawController::suspend( sal_Bool Suspend ) +{ + if( Suspend ) + { + ViewShellBase* pViewShellBase = GetViewShellBase(); + if( pViewShellBase ) + { + // do not allow suspend if a slideshow needs this controller! + rtl::Reference< SlideShow > xSlideShow( SlideShow::GetSlideShow( *pViewShellBase ) ); + if( xSlideShow.is() && xSlideShow->dependsOn(pViewShellBase) ) + return false; + } + } + + return SfxBaseController::suspend( Suspend ); +} + +// XServiceInfo +OUString SAL_CALL DrawController::getImplementationName( ) +{ + // Do not throw an exception at the moment. This leads to a crash + // under Solaris on reload. See issue i70929 for details. + // ThrowIfDisposed(); + return "DrawController" ; +} + +constexpr OUStringLiteral ssServiceName = u"com.sun.star.drawing.DrawingDocumentDrawView"; + +sal_Bool SAL_CALL DrawController::supportsService (const OUString& rsServiceName) +{ + return cppu::supportsService(this, rsServiceName); +} + +Sequence<OUString> SAL_CALL DrawController::getSupportedServiceNames() +{ + ThrowIfDisposed(); + Sequence<OUString> aSupportedServices { ssServiceName }; + return aSupportedServices; +} + +//------ XSelectionSupplier -------------------------------------------- +sal_Bool SAL_CALL DrawController::select (const Any& aSelection) +{ + ThrowIfDisposed(); + SolarMutexGuard aGuard; + + if (mxSubController.is()) + return mxSubController->select(aSelection); + else + return false; +} + +Any SAL_CALL DrawController::getSelection() +{ + ThrowIfDisposed(); + SolarMutexGuard aGuard; + + if (mxSubController.is()) + return mxSubController->getSelection(); + else + return Any(); +} + +void SAL_CALL DrawController::addSelectionChangeListener( + const Reference< view::XSelectionChangeListener >& xListener) +{ + if( mbDisposing ) + throw lang::DisposedException(); + + BroadcastHelperOwner::maBroadcastHelper.addListener (m_aSelectionTypeIdentifier, xListener); +} + +void SAL_CALL DrawController::removeSelectionChangeListener( + const Reference< view::XSelectionChangeListener >& xListener ) +{ + if (rBHelper.bDisposed) + throw lang::DisposedException(); + + BroadcastHelperOwner::maBroadcastHelper.removeListener (m_aSelectionTypeIdentifier, xListener); +} + +//===== lang::XEventListener ================================================ + +void SAL_CALL + DrawController::disposing (const lang::EventObject& ) +{ +} + +//===== view::XSelectionChangeListener ====================================== + +void SAL_CALL + DrawController::selectionChanged (const lang::EventObject& rEvent) +{ + ThrowIfDisposed(); + // Have to forward the event to our selection change listeners. + OInterfaceContainerHelper* pListeners = BroadcastHelperOwner::maBroadcastHelper.getContainer( + cppu::UnoType<view::XSelectionChangeListener>::get()); + if (!pListeners) + return; + + // Re-send the event to all of our listeners. + OInterfaceIteratorHelper aIterator (*pListeners); + while (aIterator.hasMoreElements()) + { + try + { + view::XSelectionChangeListener* pListener = + static_cast<view::XSelectionChangeListener*>( + aIterator.next()); + if (pListener != nullptr) + pListener->selectionChanged (rEvent); + } + catch (const RuntimeException&) + { + } + } +} + +// XDrawView + +void SAL_CALL DrawController::setCurrentPage( const Reference< drawing::XDrawPage >& xPage ) +{ + ThrowIfDisposed(); + SolarMutexGuard aGuard; + + if (mxSubController.is()) + mxSubController->setCurrentPage(xPage); +} + +Reference< drawing::XDrawPage > SAL_CALL DrawController::getCurrentPage() +{ + ThrowIfDisposed(); + SolarMutexGuard aGuard; + Reference<drawing::XDrawPage> xPage; + + // Get current page from sub controller. + if (mxSubController.is()) + xPage = mxSubController->getCurrentPage(); + + // When there is not yet a sub controller (during initialization) then fall back + // to the current page in mpCurrentPage. + if ( ! xPage.is() ) + if (rtl::Reference<SdPage> pPage = mpCurrentPage.get()) + xPage.set(pPage->getUnoPage(), UNO_QUERY); + + return xPage; +} + +void DrawController::FireVisAreaChanged (const ::tools::Rectangle& rVisArea) noexcept +{ + if( maLastVisArea == rVisArea ) + return; + + Any aNewValue; + aNewValue <<= awt::Rectangle( + rVisArea.Left(), + rVisArea.Top(), + rVisArea.GetWidth(), + rVisArea.GetHeight() ); + + Any aOldValue; + aOldValue <<= awt::Rectangle( + maLastVisArea.Left(), + maLastVisArea.Top(), + maLastVisArea.GetWidth(), + maLastVisArea.GetHeight() ); + + FirePropertyChange (PROPERTY_WORKAREA, aNewValue, aOldValue); + + maLastVisArea = rVisArea; +} + +void DrawController::FireSelectionChangeListener() noexcept +{ + OInterfaceContainerHelper * pLC = BroadcastHelperOwner::maBroadcastHelper.getContainer( + m_aSelectionTypeIdentifier); + if( !pLC ) + return; + + Reference< XInterface > xSource( static_cast<XWeak*>(this) ); + const lang::EventObject aEvent( xSource ); + + // iterate over all listeners and send events + OInterfaceIteratorHelper aIt( *pLC); + while( aIt.hasMoreElements() ) + { + try + { + view::XSelectionChangeListener * pL = + static_cast<view::XSelectionChangeListener*>(aIt.next()); + if (pL != nullptr) + pL->selectionChanged( aEvent ); + } + catch (const RuntimeException&) + { + } + } +} + +void DrawController::FireChangeEditMode (bool bMasterPageMode) noexcept +{ + if (bMasterPageMode != mbMasterPageMode ) + { + FirePropertyChange( + PROPERTY_MASTERPAGEMODE, + Any(bMasterPageMode), + Any(mbMasterPageMode)); + + mbMasterPageMode = bMasterPageMode; + } +} + +void DrawController::FireChangeLayerMode (bool bLayerMode) noexcept +{ + if (bLayerMode != mbLayerMode) + { + FirePropertyChange( + PROPERTY_LAYERMODE, + Any(bLayerMode), + Any(mbLayerMode)); + + mbLayerMode = bLayerMode; + } +} + +void DrawController::FireSwitchCurrentPage (SdPage* pNewCurrentPage) noexcept +{ + rtl::Reference<SdrPage> pCurrentPage = mpCurrentPage.get(); + if (pNewCurrentPage == pCurrentPage.get()) + return; + + try + { + Any aNewValue ( + Any(Reference<drawing::XDrawPage>(pNewCurrentPage->getUnoPage(), UNO_QUERY))); + + Any aOldValue; + if (pCurrentPage != nullptr) + { + Reference<drawing::XDrawPage> xOldPage (pCurrentPage->getUnoPage(), UNO_QUERY); + aOldValue <<= xOldPage; + } + + FirePropertyChange(PROPERTY_CURRENTPAGE, aNewValue, aOldValue); + + mpCurrentPage = pNewCurrentPage; + } + catch (const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("sd", "sd::SdUnoDrawView::FireSwitchCurrentPage()"); + } +} + +void DrawController::NotifyAccUpdate() +{ + sal_Int32 nHandle = PROPERTY_UPDATEACC; + Any aNewValue, aOldValue; + fire (&nHandle, &aNewValue, &aOldValue, 1, false); +} + +void DrawController::fireChangeLayer( css::uno::Reference< css::drawing::XLayer>* pCurrentLayer ) noexcept +{ + if( pCurrentLayer != mpCurrentLayer ) + { + sal_Int32 nHandle = PROPERTY_ACTIVE_LAYER; + + Any aNewValue ( *pCurrentLayer); + + Any aOldValue ; + + fire (&nHandle, &aNewValue, &aOldValue, 1, false); + + mpCurrentLayer = pCurrentLayer; + } +} + +// This method is only called in slide show and outline view +//void DrawController::fireSwitchCurrentPage(String pageName ) throw() +void DrawController::fireSwitchCurrentPage(sal_Int32 pageIndex ) noexcept +{ + Any aNewValue; + Any aOldValue; + //OUString aPageName( pageName ); + //aNewValue <<= aPageName ; + aNewValue <<= pageIndex; + + // Use new property to handle page change event + sal_Int32 nHandles = PROPERTY_PAGE_CHANGE; + fire( &nHandles, &aNewValue, &aOldValue, 1, false ); +} + +void DrawController::FirePropertyChange ( + sal_Int32 nHandle, + const Any& rNewValue, + const Any& rOldValue) +{ + try + { + fire (&nHandle, &rNewValue, &rOldValue, 1, false); + } + catch (const RuntimeException&) + { + // Ignore this exception. Exceptions should be handled in the + // fire() function so that all listeners are called. This is + // not the case at the moment, so we simply ignore the + // exception. + } + +} + +void DrawController::BroadcastContextChange() const +{ + std::shared_ptr<ViewShell> pViewShell (mpBase->GetMainViewShell()); + if ( ! pViewShell) + return; + + EnumContext::Context eContext (EnumContext::Context::Unknown); + switch (pViewShell->GetShellType()) + { + case ViewShell::ST_IMPRESS: + case ViewShell::ST_DRAW: + if (mbMasterPageMode) + eContext = EnumContext::Context::MasterPage; + else + eContext = EnumContext::Context::DrawPage; + break; + + case ViewShell::ST_NOTES: + eContext = EnumContext::Context::NotesPage; + break; + + case ViewShell::ST_HANDOUT: + eContext = EnumContext::Context::HandoutPage; + break; + + case ViewShell::ST_OUTLINE: + eContext = EnumContext::Context::OutlineText; + break; + + case ViewShell::ST_SLIDE_SORTER: + eContext = EnumContext::Context::SlidesorterPage; + break; + + case ViewShell::ST_PRESENTATION: + case ViewShell::ST_NONE: + default: + eContext = EnumContext::Context::Empty; + break; + } + + ContextChangeEventMultiplexer::NotifyContextChange(mpBase, eContext); +} + +void DrawController::ReleaseViewShellBase() +{ + DisposeFrameworkControllers(); + mpBase = nullptr; +} + +//===== XControllerManager ============================================================== + +Reference<XConfigurationController> SAL_CALL + DrawController::getConfigurationController() +{ + ThrowIfDisposed(); + + return mxConfigurationController; +} + +Reference<XModuleController> SAL_CALL + DrawController::getModuleController() +{ + ThrowIfDisposed(); + + return mxModuleController; +} + +//===== XUnoTunnel ============================================================ + +const Sequence<sal_Int8>& DrawController::getUnoTunnelId() +{ + static const comphelper::UnoIdInit theDrawControllerUnoTunnelId; + return theDrawControllerUnoTunnelId.getSeq(); +} + +sal_Int64 SAL_CALL DrawController::getSomething (const Sequence<sal_Int8>& rId) +{ + return comphelper::getSomethingImpl(rId, this); +} + +//===== Properties ============================================================ + +void DrawController::FillPropertyTable ( + ::std::vector<beans::Property>& rProperties) +{ + rProperties.emplace_back("VisibleArea", + PROPERTY_WORKAREA, + ::cppu::UnoType< css::awt::Rectangle>::get(), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY); + rProperties.emplace_back( + "SubController", + PROPERTY_SUB_CONTROLLER, + cppu::UnoType<drawing::XDrawSubController>::get(), + beans::PropertyAttribute::BOUND); + rProperties.emplace_back( + "CurrentPage", + PROPERTY_CURRENTPAGE, + cppu::UnoType<drawing::XDrawPage>::get(), + beans::PropertyAttribute::BOUND ); + rProperties.emplace_back("IsLayerMode", + PROPERTY_LAYERMODE, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::BOUND ); + rProperties.emplace_back("IsMasterPageMode", + PROPERTY_MASTERPAGEMODE, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::BOUND ); + rProperties.emplace_back("ActiveLayer", + PROPERTY_ACTIVE_LAYER, + cppu::UnoType<drawing::XLayer>::get(), + beans::PropertyAttribute::BOUND ); + rProperties.emplace_back("ZoomValue", + PROPERTY_ZOOMVALUE, + ::cppu::UnoType<sal_Int16>::get(), + beans::PropertyAttribute::BOUND ); + rProperties.emplace_back("ZoomType", + PROPERTY_ZOOMTYPE, + ::cppu::UnoType<sal_Int16>::get(), + beans::PropertyAttribute::BOUND ); + rProperties.emplace_back("ViewOffset", + PROPERTY_VIEWOFFSET, + ::cppu::UnoType< css::awt::Point>::get(), + beans::PropertyAttribute::BOUND ); + rProperties.emplace_back("DrawViewMode", + PROPERTY_DRAWVIEWMODE, + ::cppu::UnoType< css::awt::Point>::get(), + beans::PropertyAttribute::BOUND|beans::PropertyAttribute::READONLY|beans::PropertyAttribute::MAYBEVOID ); + // add new property to update current page's acc information + rProperties.emplace_back( "UpdateAcc", + PROPERTY_UPDATEACC, + ::cppu::UnoType<sal_Int16>::get(), + beans::PropertyAttribute::BOUND ); + rProperties.emplace_back( "PageChange", + PROPERTY_PAGE_CHANGE, + ::cppu::UnoType<sal_Int16>::get(), + beans::PropertyAttribute::BOUND ); +} + +IPropertyArrayHelper & DrawController::getInfoHelper() +{ + SolarMutexGuard aGuard; + + if (mpPropertyArrayHelper == nullptr) + { + ::std::vector<beans::Property> aProperties; + FillPropertyTable(aProperties); + mpPropertyArrayHelper.reset(new OPropertyArrayHelper(comphelper::containerToSequence(aProperties), false)); + } + + return *mpPropertyArrayHelper; +} + +Reference < beans::XPropertySetInfo > DrawController::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + + static Reference < beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +uno::Reference< form::runtime::XFormController > SAL_CALL DrawController::getFormController( const uno::Reference< form::XForm >& Form ) +{ + SolarMutexGuard aGuard; + + FmFormShell* pFormShell = mpBase->GetFormShellManager()->GetFormShell(); + SdrView* pSdrView = mpBase->GetDrawView(); + std::shared_ptr<ViewShell> pViewShell = mpBase->GetMainViewShell(); + ::sd::Window* pWindow = pViewShell ? pViewShell->GetActiveWindow() : nullptr; + + uno::Reference< form::runtime::XFormController > xController; + if ( pFormShell && pSdrView && pWindow ) + xController = FmFormShell::GetFormController( Form, *pSdrView, *pWindow->GetOutDev() ); + return xController; +} + +sal_Bool SAL_CALL DrawController::isFormDesignMode( ) +{ + SolarMutexGuard aGuard; + + bool bIsDesignMode = true; + + FmFormShell* pFormShell = mpBase->GetFormShellManager()->GetFormShell(); + if ( pFormShell ) + bIsDesignMode = pFormShell->IsDesignMode(); + + return bIsDesignMode; +} + +void SAL_CALL DrawController::setFormDesignMode( sal_Bool DesignMode ) +{ + SolarMutexGuard aGuard; + + FmFormShell* pFormShell = mpBase->GetFormShellManager()->GetFormShell(); + if ( pFormShell ) + pFormShell->SetDesignMode( DesignMode ); +} + +uno::Reference< awt::XControl > SAL_CALL DrawController::getControl( const uno::Reference< awt::XControlModel >& xModel ) +{ + SolarMutexGuard aGuard; + + FmFormShell* pFormShell = mpBase->GetFormShellManager()->GetFormShell(); + SdrView* pSdrView = mpBase->GetDrawView(); + std::shared_ptr<ViewShell> pViewShell = mpBase->GetMainViewShell(); + ::sd::Window* pWindow = pViewShell ? pViewShell->GetActiveWindow() : nullptr; + + uno::Reference< awt::XControl > xControl; + if ( pFormShell && pSdrView && pWindow ) + pFormShell->GetFormControl( xModel, *pSdrView, *pWindow->GetOutDev(), xControl ); + return xControl; +} + +sal_Bool DrawController::convertFastPropertyValue ( + Any & rConvertedValue, + Any & rOldValue, + sal_Int32 nHandle, + const Any& rValue) +{ + bool bResult = false; + + if (nHandle == PROPERTY_SUB_CONTROLLER) + { + rOldValue <<= mxSubController; + rConvertedValue <<= Reference<drawing::XDrawSubController>(rValue, UNO_QUERY); + bResult = (rOldValue != rConvertedValue); + } + else if (mxSubController.is()) + { + rConvertedValue = rValue; + try + { + rOldValue = mxSubController->getFastPropertyValue(nHandle); + bResult = (rOldValue != rConvertedValue); + } + catch (const beans::UnknownPropertyException&) + { + // The property is unknown and thus an illegal argument to this method. + throw css::lang::IllegalArgumentException(); + } + } + + return bResult; +} + +void DrawController::setFastPropertyValue_NoBroadcast ( + sal_Int32 nHandle, + const Any& rValue) +{ + SolarMutexGuard aGuard; + if (nHandle == PROPERTY_SUB_CONTROLLER) + SetSubController(Reference<drawing::XDrawSubController>(rValue, UNO_QUERY)); + else if (mxSubController.is()) + mxSubController->setFastPropertyValue(nHandle, rValue); +} + +void DrawController::getFastPropertyValue ( + Any & rRet, + sal_Int32 nHandle ) const +{ + SolarMutexGuard aGuard; + + switch( nHandle ) + { + case PROPERTY_WORKAREA: + rRet <<= awt::Rectangle( + maLastVisArea.Left(), + maLastVisArea.Top(), + maLastVisArea.GetWidth(), + maLastVisArea.GetHeight()); + break; + + case PROPERTY_SUB_CONTROLLER: + rRet <<= mxSubController; + break; + + default: + if (mxSubController.is()) + rRet = mxSubController->getFastPropertyValue(nHandle); + break; + } +} + +void DrawController::ProvideFrameworkControllers() +{ + SolarMutexGuard aGuard; + try + { + Reference<XController> xController (this); + const Reference<XComponentContext> xContext ( + ::comphelper::getProcessComponentContext() ); + mxConfigurationController = ConfigurationController::create( + xContext, + xController); + mxModuleController = ModuleController::create( + xContext, + xController); + } + catch (const RuntimeException&) + { + mxConfigurationController = nullptr; + mxModuleController = nullptr; + } +} + +void DrawController::DisposeFrameworkControllers() +{ + Reference<XComponent> xComponent (mxModuleController, UNO_QUERY); + if (xComponent.is()) + xComponent->dispose(); + + xComponent.set(mxConfigurationController, UNO_QUERY); + if (xComponent.is()) + xComponent->dispose(); +} + +void DrawController::ThrowIfDisposed() const +{ + if (rBHelper.bDisposed || rBHelper.bInDispose || mbDisposing) + { + SAL_WARN("sd", "Calling disposed DrawController object. Throwing exception:"); + throw lang::DisposedException ( + "DrawController object has already been disposed", + const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this))); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/SdUnoDrawView.cxx b/sd/source/ui/unoidl/SdUnoDrawView.cxx new file mode 100644 index 000000000..379a2956f --- /dev/null +++ b/sd/source/ui/unoidl/SdUnoDrawView.cxx @@ -0,0 +1,548 @@ +/* -*- 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 <SdUnoDrawView.hxx> + +#include <DrawController.hxx> +#include <DrawDocShell.hxx> +#include <DrawViewShell.hxx> +#include <drawdoc.hxx> +#include "unolayer.hxx" +#include <unomodel.hxx> +#include <Window.hxx> +#include <pres.hxx> + +#include <comphelper/processfactory.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/svdpagv.hxx> +#include <svx/unopage.hxx> +#include <sfx2/zoomitem.hxx> +#include <com/sun/star/drawing/DrawViewMode.hpp> +#include <com/sun/star/drawing/ShapeCollection.hpp> +#include <com/sun/star/drawing/XLayerManager.hpp> +#include <com/sun/star/view/DocumentZoomType.hpp> + +#include <vector> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing; + +namespace sd { + +SdUnoDrawView::SdUnoDrawView( + DrawViewShell& rViewShell, + View& rView) noexcept + : DrawSubControllerInterfaceBase(m_aMutex), + mrDrawViewShell(rViewShell), + mrView(rView) +{ +} + +SdUnoDrawView::~SdUnoDrawView() noexcept +{ +} + +bool SdUnoDrawView::getMasterPageMode() const noexcept +{ + return (mrDrawViewShell.GetEditMode() == EditMode::MasterPage); +} + +void SdUnoDrawView::setMasterPageMode (bool bMasterPageMode) noexcept +{ + if ((mrDrawViewShell.GetEditMode() == EditMode::MasterPage) != bMasterPageMode) + { + mrDrawViewShell.ChangeEditMode ( + bMasterPageMode ? EditMode::MasterPage : EditMode::Page, + mrDrawViewShell.IsLayerModeActive()); + } +} + +bool SdUnoDrawView::getLayerMode() const noexcept +{ + return mrDrawViewShell.IsLayerModeActive(); +} + +void SdUnoDrawView::setLayerMode (bool bLayerMode) noexcept +{ + if (mrDrawViewShell.IsLayerModeActive() != bLayerMode) + { + mrDrawViewShell.ChangeEditMode ( + mrDrawViewShell.GetEditMode(), + bLayerMode); + } +} + +Reference<drawing::XLayer> SdUnoDrawView::getActiveLayer() const +{ + Reference<drawing::XLayer> xCurrentLayer; + + do + { + // Retrieve the layer manager from the model. + SdXImpressDocument* pModel = GetModel(); + if (pModel == nullptr) + break; + + SdDrawDocument* pSdModel = pModel->GetDoc(); + if (pSdModel == nullptr) + break; + + // From the model get the current SdrLayer object via the layer admin. + SdrLayerAdmin& rLayerAdmin = pSdModel->GetLayerAdmin (); + SdrLayer* pLayer = rLayerAdmin.GetLayer (mrView.GetActiveLayer()); + if (pLayer == nullptr) + break; + + // Get the corresponding XLayer object from the implementation + // object of the layer manager. + Reference<drawing::XLayerManager> xManager (pModel->getLayerManager(), uno::UNO_QUERY); + SdLayerManager* pManager = comphelper::getFromUnoTunnel<SdLayerManager> (xManager); + if (pManager != nullptr) + xCurrentLayer = pManager->GetLayer (pLayer); + } + while (false); + + return xCurrentLayer; +} + +void SdUnoDrawView::setActiveLayer (const Reference<drawing::XLayer>& rxLayer) +{ + // Get the SdrLayer object corresponding to the given reference. + if ( ! rxLayer.is()) + return; + + SdLayer* pLayer = comphelper::getFromUnoTunnel<SdLayer> (rxLayer); + if (pLayer == nullptr) + return; + + SdrLayer* pSdrLayer = pLayer->GetSdrLayer(); + if (pSdrLayer == nullptr) + return; + + // Set the new active layer and make the change visible. + mrView.SetActiveLayer (pSdrLayer->GetName()); + mrDrawViewShell.ResetActualLayer (); +} + +// XSelectionSupplier + +sal_Bool SAL_CALL SdUnoDrawView::select( const Any& aSelection ) +{ + bool bOk = true; + + ::std::vector<SdrObject*> aObjects; + + SdrPage* pSdrPage = nullptr; + + Reference< drawing::XShape > xShape; + aSelection >>= xShape; + + if(xShape.is()) + { + SdrObject* pObj = SdrObject::getSdrObjectFromXShape( xShape ); + if( pObj ) + { + pSdrPage = pObj->getSdrPageFromSdrObject(); + aObjects.push_back( pObj ); + } + else + { + bOk = false; + } + } + else + { + Reference< drawing::XShapes > xShapes; + aSelection >>= xShapes; + if( xShapes.is() ) + { + const sal_uInt32 nCount = xShapes->getCount(); + for( sal_uInt32 i = 0; i < nCount; i++ ) + { + xShapes->getByIndex(i) >>= xShape; + if( xShape.is() ) + { + SdrObject* pObj = SdrObject::getSdrObjectFromXShape(xShape); + if( !pObj ) + { + bOk = false; + break; + } + + if( pSdrPage == nullptr ) + { + pSdrPage = pObj->getSdrPageFromSdrObject(); + } + else if( pSdrPage != pObj->getSdrPageFromSdrObject() ) + { + bOk = false; + break; + } + + aObjects.push_back( pObj ); + } + } + } + } + + if( bOk ) + { + if( pSdrPage ) + { + setMasterPageMode( pSdrPage->IsMasterPage() ); + mrDrawViewShell.SwitchPage( (pSdrPage->GetPageNum() - 1) >> 1 ); + mrDrawViewShell.WriteFrameViewData(); + } + + SdrPageView *pPV = mrView.GetSdrPageView(); + + if(pPV) + { + // first deselect all + mrView.UnmarkAllObj( pPV ); + + for( SdrObject* pObj : aObjects ) + { + mrView.MarkObj( pObj, pPV ); + } + } + else + { + bOk = false; + } + } + + return bOk; +} + +Any SAL_CALL SdUnoDrawView::getSelection() +{ + Any aAny; + + if( mrView.IsTextEdit() ) + mrView.getTextSelection( aAny ); + + if( !aAny.hasValue() ) + { + const SdrMarkList& rMarkList = mrView.GetMarkedObjectList(); + const size_t nCount = rMarkList.GetMarkCount(); + if( nCount ) + { + Reference< drawing::XShapes > xShapes = drawing::ShapeCollection::create( + comphelper::getProcessComponentContext()); + for( size_t nNum = 0; nNum < nCount; ++nNum) + { + SdrMark *pMark = rMarkList.GetMark(nNum); + if(pMark==nullptr) + continue; + + SdrObject *pObj = pMark->GetMarkedSdrObj(); + if(pObj==nullptr || pObj->getSdrPageFromSdrObject() == nullptr) + continue; + + Reference< drawing::XDrawPage > xPage( pObj->getSdrPageFromSdrObject()->getUnoPage(), UNO_QUERY); + + if(!xPage.is()) + continue; + + SvxDrawPage* pDrawPage = comphelper::getFromUnoTunnel<SvxDrawPage>( xPage ); + + if(pDrawPage==nullptr) + continue; + + Reference< drawing::XShape > xShape( pObj->getUnoShape(), UNO_QUERY ); + + if(xShape.is()) + xShapes->add(xShape); + } + aAny <<= xShapes; + } + } + + return aAny; +} + +void SAL_CALL SdUnoDrawView::addSelectionChangeListener ( + const css::uno::Reference<css::view::XSelectionChangeListener>&) +{} + +void SAL_CALL SdUnoDrawView::removeSelectionChangeListener ( + const css::uno::Reference<css::view::XSelectionChangeListener>&) +{} + +void SdUnoDrawView::setFastPropertyValue ( + sal_Int32 nHandle, + const Any& rValue) +{ + switch( nHandle ) + { + case DrawController::PROPERTY_CURRENTPAGE: + { + Reference< drawing::XDrawPage > xPage; + rValue >>= xPage; + setCurrentPage( xPage ); + } + break; + + case DrawController::PROPERTY_MASTERPAGEMODE: + { + bool bValue = false; + rValue >>= bValue; + setMasterPageMode( bValue ); + } + break; + + case DrawController::PROPERTY_LAYERMODE: + { + bool bValue = false; + rValue >>= bValue; + setLayerMode( bValue ); + } + break; + case DrawController::PROPERTY_ACTIVE_LAYER: + { + Reference<drawing::XLayer> xLayer; + rValue >>= xLayer; + setActiveLayer (xLayer); + } + break; + case DrawController::PROPERTY_ZOOMVALUE: + { + sal_Int16 nZoom = 0; + rValue >>= nZoom; + SetZoom( nZoom ); + } + break; + case DrawController::PROPERTY_ZOOMTYPE: + { + sal_Int16 nType = 0; + rValue >>= nType; + SetZoomType( nType ); + } + break; + case DrawController::PROPERTY_VIEWOFFSET: + { + awt::Point aOffset; + rValue >>= aOffset; + SetViewOffset( aOffset ); + } + break; + default: + throw beans::UnknownPropertyException( OUString::number(nHandle), static_cast<cppu::OWeakObject*>(this)); + } +} + +Any SAL_CALL SdUnoDrawView::getFastPropertyValue ( + sal_Int32 nHandle) +{ + Any aValue; + switch( nHandle ) + { + case DrawController::PROPERTY_CURRENTPAGE: + aValue <<= getCurrentPage(); + break; + + case DrawController::PROPERTY_MASTERPAGEMODE: + aValue <<= getMasterPageMode(); + break; + + case DrawController::PROPERTY_LAYERMODE: + aValue <<= getLayerMode(); + break; + + case DrawController::PROPERTY_ACTIVE_LAYER: + aValue <<= getActiveLayer(); + break; + + case DrawController::PROPERTY_ZOOMVALUE: + aValue <<= GetZoom(); + break; + case DrawController::PROPERTY_ZOOMTYPE: + aValue <<= sal_Int16(css::view::DocumentZoomType::BY_VALUE); + break; + case DrawController::PROPERTY_VIEWOFFSET: + aValue <<= GetViewOffset(); + break; + + case DrawController::PROPERTY_DRAWVIEWMODE: + aValue = getDrawViewMode(); + break; + + default: + throw beans::UnknownPropertyException( OUString::number(nHandle), static_cast<cppu::OWeakObject*>(this)); + } + + return aValue; +} + +// XDrawView + +void SAL_CALL SdUnoDrawView::setCurrentPage ( + const Reference< drawing::XDrawPage >& xPage ) +{ + SvxDrawPage* pDrawPage = comphelper::getFromUnoTunnel<SvxDrawPage>( xPage ); + SdrPage *pSdrPage = pDrawPage ? pDrawPage->GetSdrPage() : nullptr; + + if(pSdrPage) + { + // End editing of text. Otherwise the edited text object would + // still be visible on the new page. + mrDrawViewShell.GetView()->SdrEndTextEdit(); + + setMasterPageMode( pSdrPage->IsMasterPage() ); + mrDrawViewShell.SwitchPage( (pSdrPage->GetPageNum() - 1) >> 1 ); + mrDrawViewShell.WriteFrameViewData(); + } +} + +Reference< drawing::XDrawPage > SAL_CALL SdUnoDrawView::getCurrentPage() +{ + Reference< drawing::XDrawPage > xPage; + + SdrPageView *pPV = mrView.GetSdrPageView(); + SdrPage* pPage = pPV ? pPV->GetPage() : nullptr; + + if(pPage) + xPage.set( pPage->getUnoPage(), UNO_QUERY ); + + return xPage; +} + +sal_Int16 SdUnoDrawView::GetZoom() const +{ + if (mrDrawViewShell.GetActiveWindow() ) + { + return static_cast<sal_Int16>(mrDrawViewShell.GetActiveWindow()->GetZoom()); + } + else + { + return 0; + } +} + +void SdUnoDrawView::SetZoom( sal_Int16 nZoom ) +{ + SvxZoomItem aZoomItem( SvxZoomType::PERCENT, nZoom ); + + SfxViewFrame* pViewFrame = mrDrawViewShell.GetViewFrame(); + if( pViewFrame ) + { + SfxDispatcher* pDispatcher = pViewFrame->GetDispatcher(); + if( pDispatcher ) + { + pDispatcher->ExecuteList(SID_ATTR_ZOOM, SfxCallMode::SYNCHRON, + { &aZoomItem }); + } + } +} + +void SdUnoDrawView::SetViewOffset(const awt::Point& rWinPos ) +{ + Point aWinPos( rWinPos.X, rWinPos.Y ); + aWinPos += mrDrawViewShell.GetViewOrigin(); + mrDrawViewShell.SetWinViewPos( aWinPos ); +} + +awt::Point SdUnoDrawView::GetViewOffset() const +{ + Point aRet = mrDrawViewShell.GetWinViewPos(); + aRet -= mrDrawViewShell.GetViewOrigin(); + + return awt::Point( aRet.X(), aRet.Y() ); +} + +void SdUnoDrawView::SetZoomType ( sal_Int16 nType ) +{ + SfxViewFrame* pViewFrame = mrDrawViewShell.GetViewFrame(); + if( !pViewFrame ) + return; + + SfxDispatcher* pDispatcher = pViewFrame->GetDispatcher(); + if( !pDispatcher ) + return; + + SvxZoomType eZoomType; + switch( nType ) + { + case css::view::DocumentZoomType::OPTIMAL: + eZoomType = SvxZoomType::OPTIMAL; + break; + + case css::view::DocumentZoomType::PAGE_WIDTH: + case css::view::DocumentZoomType::PAGE_WIDTH_EXACT: + eZoomType = SvxZoomType::PAGEWIDTH; + break; + + case css::view::DocumentZoomType::ENTIRE_PAGE: + eZoomType = SvxZoomType::WHOLEPAGE; + break; + + default: + return; + } + SvxZoomItem aZoomItem( eZoomType ); + pDispatcher->ExecuteList(SID_ATTR_ZOOM, SfxCallMode::SYNCHRON, + { &aZoomItem }); +} + +SdXImpressDocument* SdUnoDrawView::GetModel() const noexcept +{ + if (mrView.GetDocSh()!=nullptr) + { + Reference<frame::XModel> xModel (mrView.GetDocSh()->GetModel()); + return comphelper::getFromUnoTunnel<SdXImpressDocument>(xModel); + } + else + return nullptr; +} + +Any SdUnoDrawView::getDrawViewMode() const +{ + Any aRet; + switch( mrDrawViewShell.GetPageKind() ) + { + case PageKind::Notes: aRet <<= DrawViewMode_NOTES; break; + case PageKind::Handout: aRet <<= DrawViewMode_HANDOUT; break; + case PageKind::Standard: aRet <<= DrawViewMode_DRAW; break; + } + return aRet; +} + +// XServiceInfo +OUString SAL_CALL SdUnoDrawView::getImplementationName( ) +{ + return "com.sun.star.comp.sd.SdUnoDrawView" ; +} + +sal_Bool SAL_CALL SdUnoDrawView::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +Sequence< OUString > SAL_CALL SdUnoDrawView::getSupportedServiceNames( ) +{ + return { "com.sun.star.drawing.DrawingDocumentDrawView" }; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/SdUnoOutlineView.cxx b/sd/source/ui/unoidl/SdUnoOutlineView.cxx new file mode 100644 index 000000000..6b98f2140 --- /dev/null +++ b/sd/source/ui/unoidl/SdUnoOutlineView.cxx @@ -0,0 +1,156 @@ +/* -*- 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 <SdUnoOutlineView.hxx> + +#include <DrawController.hxx> +#include <OutlineViewShell.hxx> +#include <sdpage.hxx> + +#include <cppuhelper/supportsservice.hxx> +#include <svx/unopage.hxx> + +using namespace ::cppu; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd { + +SdUnoOutlineView::SdUnoOutlineView( + OutlineViewShell& rViewShell) noexcept + : DrawSubControllerInterfaceBase(m_aMutex), + mrOutlineViewShell(rViewShell) +{ +} + +SdUnoOutlineView::~SdUnoOutlineView() noexcept +{ +} + +void SAL_CALL SdUnoOutlineView::disposing() +{ +} + +//----- XSelectionSupplier ---------------------------------------------------- + +sal_Bool SAL_CALL SdUnoOutlineView::select( const Any& ) +{ + // todo: add selections for text ranges + return false; +} + +Any SAL_CALL SdUnoOutlineView::getSelection() +{ + Any aAny; + return aAny; +} + +void SAL_CALL SdUnoOutlineView::addSelectionChangeListener ( + const css::uno::Reference<css::view::XSelectionChangeListener>&) +{} + +void SAL_CALL SdUnoOutlineView::removeSelectionChangeListener ( + const css::uno::Reference<css::view::XSelectionChangeListener>&) +{} + +//----- XDrawView ------------------------------------------------------------- +void SAL_CALL SdUnoOutlineView::setCurrentPage ( + const Reference< drawing::XDrawPage >& xPage) +{ + SvxDrawPage* pDrawPage = comphelper::getFromUnoTunnel<SvxDrawPage>( xPage ); + SdrPage *pSdrPage = pDrawPage ? pDrawPage->GetSdrPage() : nullptr; + SdPage *pSdPage = dynamic_cast<SdPage*>(pSdrPage); + + if (pSdPage != nullptr) + mrOutlineViewShell.SetCurrentPage(pSdPage); +} + +Reference< drawing::XDrawPage > SAL_CALL SdUnoOutlineView::getCurrentPage() +{ + Reference<drawing::XDrawPage> xPage; + + SdPage* pPage = mrOutlineViewShell.getCurrentPage(); + if (pPage != nullptr) + xPage.set(pPage->getUnoPage(), UNO_QUERY); + + return xPage; +} + +void SdUnoOutlineView::setFastPropertyValue ( + sal_Int32 nHandle, + const Any& rValue) +{ + switch( nHandle ) + { + case DrawController::PROPERTY_CURRENTPAGE: + { + Reference< drawing::XDrawPage > xPage; + rValue >>= xPage; + setCurrentPage( xPage ); + } + break; + + default: + throw beans::UnknownPropertyException( OUString::number(nHandle), static_cast<cppu::OWeakObject*>(this)); + } +} + +Any SAL_CALL SdUnoOutlineView::getFastPropertyValue ( + sal_Int32 nHandle) +{ + Any aValue; + + switch( nHandle ) + { + case DrawController::PROPERTY_CURRENTPAGE: + { + SdPage* pPage = mrOutlineViewShell.GetActualPage(); + if (pPage != nullptr) + aValue <<= pPage->getUnoPage(); + } + break; + case DrawController::PROPERTY_VIEWOFFSET: + break; + + default: + throw beans::UnknownPropertyException( OUString::number(nHandle), static_cast<cppu::OWeakObject*>(this)); + } + + return aValue; +} + +// XServiceInfo +OUString SAL_CALL SdUnoOutlineView::getImplementationName( ) +{ + return "com.sun.star.comp.sd.SdUnoOutlineView"; +} + +sal_Bool SAL_CALL SdUnoOutlineView::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +Sequence< OUString > SAL_CALL SdUnoOutlineView::getSupportedServiceNames( ) +{ + return { "com.sun.star.presentation.OutlineView" }; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/SdUnoSlideView.cxx b/sd/source/ui/unoidl/SdUnoSlideView.cxx new file mode 100644 index 000000000..c30962ed7 --- /dev/null +++ b/sd/source/ui/unoidl/SdUnoSlideView.cxx @@ -0,0 +1,172 @@ +/* -*- 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 <cppuhelper/supportsservice.hxx> + +#include <DrawController.hxx> +#include <SdUnoSlideView.hxx> + +#include <SlideSorter.hxx> +#include <controller/SlideSorterController.hxx> +#include <controller/SlsPageSelector.hxx> +#include <controller/SlsCurrentSlideManager.hxx> +#include <model/SlsPageEnumerationProvider.hxx> +#include <model/SlsPageDescriptor.hxx> +#include <sdpage.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd { + +SdUnoSlideView::SdUnoSlideView ( + slidesorter::SlideSorter& rSlideSorter) noexcept + : DrawSubControllerInterfaceBase(m_aMutex), + mrSlideSorter(rSlideSorter) +{ +} + +SdUnoSlideView::~SdUnoSlideView() noexcept +{ +} + +//----- XSelectionSupplier ---------------------------------------------------- + +sal_Bool SAL_CALL SdUnoSlideView::select (const Any& aSelection) +{ + slidesorter::controller::SlideSorterController& rSlideSorterController + = mrSlideSorter.GetController(); + slidesorter::controller::PageSelector& rSelector (rSlideSorterController.GetPageSelector()); + rSelector.DeselectAllPages(); + Sequence<Reference<drawing::XDrawPage> > xPages; + aSelection >>= xPages; + for (const auto& rPage : std::as_const(xPages)) + { + Reference<beans::XPropertySet> xSet (rPage, UNO_QUERY); + if (xSet.is()) + { + try + { + Any aNumber = xSet->getPropertyValue("Number"); + sal_Int32 nPageNumber = 0; + aNumber >>= nPageNumber; + nPageNumber -=1; // Transform 1-based page numbers to 0-based ones. + rSelector.SelectPage(nPageNumber); + } + catch (const RuntimeException&) + { + } + } + } + + return true; +} + +Any SAL_CALL SdUnoSlideView::getSelection() +{ + Any aResult; + + slidesorter::model::PageEnumeration aSelectedPages ( + slidesorter::model::PageEnumerationProvider::CreateSelectedPagesEnumeration( + mrSlideSorter.GetModel())); + int nSelectedPageCount ( + mrSlideSorter.GetController().GetPageSelector().GetSelectedPageCount()); + + Sequence<Reference<XInterface> > aPages(nSelectedPageCount); + auto aPagesRange = asNonConstRange(aPages); + int nIndex = 0; + while (aSelectedPages.HasMoreElements() && nIndex<nSelectedPageCount) + { + slidesorter::model::SharedPageDescriptor pDescriptor (aSelectedPages.GetNextElement()); + aPagesRange[nIndex++] = pDescriptor->GetPage()->getUnoPage(); + } + aResult <<= aPages; + + return aResult; +} + +void SAL_CALL SdUnoSlideView::addSelectionChangeListener ( + const css::uno::Reference<css::view::XSelectionChangeListener>&) +{} + +void SAL_CALL SdUnoSlideView::removeSelectionChangeListener ( + const css::uno::Reference<css::view::XSelectionChangeListener>&) +{} + +//----- XDrawView ------------------------------------------------------------- + +void SAL_CALL SdUnoSlideView::setCurrentPage ( + const css::uno::Reference<css::drawing::XDrawPage>& rxDrawPage) +{ + Reference<beans::XPropertySet> xProperties (rxDrawPage, UNO_QUERY); + if (xProperties.is()) + { + sal_uInt16 nPageNumber(0); + if (xProperties->getPropertyValue("Number") >>= nPageNumber) + { + mrSlideSorter.GetController().GetCurrentSlideManager()->SwitchCurrentSlide( + nPageNumber-1); + } + } +} + +css::uno::Reference<css::drawing::XDrawPage > SAL_CALL + SdUnoSlideView::getCurrentPage() +{ + return mrSlideSorter.GetController().GetCurrentSlideManager()->GetCurrentSlide()->GetXDrawPage(); +} + +//----- XFastPropertySet ------------------------------------------------------ + +void SdUnoSlideView::setFastPropertyValue ( + sal_Int32 nHandle, + const Any&) +{ + throw beans::UnknownPropertyException( OUString::number(nHandle), static_cast<cppu::OWeakObject*>(this)); +} + +Any SAL_CALL SdUnoSlideView::getFastPropertyValue ( + sal_Int32 nHandle) +{ + if( nHandle != DrawController::PROPERTY_VIEWOFFSET ) + throw beans::UnknownPropertyException( OUString::number(nHandle), static_cast<cppu::OWeakObject*>(this)); + + return Any(); +} + +// XServiceInfo +OUString SAL_CALL SdUnoSlideView::getImplementationName( ) +{ + return "com.sun.star.comp.sd.SdUnoSlideView"; +} + +sal_Bool SAL_CALL SdUnoSlideView::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +Sequence< OUString > SAL_CALL SdUnoSlideView::getSupportedServiceNames( ) +{ + return { "com.sun.star.presentation.SlidesView" }; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/UnoDocumentSettings.cxx b/sd/source/ui/unoidl/UnoDocumentSettings.cxx new file mode 100644 index 000000000..331f90b53 --- /dev/null +++ b/sd/source/ui/unoidl/UnoDocumentSettings.cxx @@ -0,0 +1,1431 @@ +/* -*- 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 <sal/config.h> + +#include <memory> +#include <utility> +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/XTransactedObject.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XMultiPropertySet.hpp> +#include <com/sun/star/i18n/XForbiddenCharacters.hpp> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/propertysethelper.hxx> +#include <comphelper/propertysetinfo.hxx> +#include <o3tl/string_view.hxx> +#include <tools/debug.hxx> +#include <tools/urlobj.hxx> +#include <svx/xtable.hxx> +#include <vcl/svapp.hxx> + +#include <drawdoc.hxx> +#include <DrawDocShell.hxx> +#include "UnoDocumentSettings.hxx" +#include <unomodel.hxx> + +#include <optsitem.hxx> +#include <sfx2/printer.hxx> +#include <sfx2/sfxsids.hrc> +#include <sdattr.hrc> +#include <sdmod.hxx> +#include <Outliner.hxx> +#include <xmloff/settingsstore.hxx> +#include <editeng/editstat.hxx> +#include <svx/unoapi.hxx> + +using namespace ::comphelper; +using namespace ::cppu; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::document; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::i18n; + +namespace sd +{ + namespace { + + class DocumentSettings : public WeakImplHelper< XPropertySet, XMultiPropertySet, XServiceInfo >, + public comphelper::PropertySetHelper, + public DocumentSettingsSerializer + { + public: + explicit DocumentSettings( SdXImpressDocument* pModel ); + + // XInterface + virtual Any SAL_CALL queryInterface( const Type& aType ) override; + virtual void SAL_CALL acquire( ) noexcept override; + virtual void SAL_CALL release( ) noexcept override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override; + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override; + virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override; + virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override; + virtual void SAL_CALL addVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + + // XMultiPropertySet + virtual void SAL_CALL setPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Sequence< css::uno::Any >& aValues ) override; + virtual css::uno::Sequence< css::uno::Any > SAL_CALL getPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames ) override; + virtual void SAL_CALL addPropertiesChangeListener( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Reference< css::beans::XPropertiesChangeListener >& xListener ) override; + virtual void SAL_CALL removePropertiesChangeListener( const css::uno::Reference< css::beans::XPropertiesChangeListener >& xListener ) override; + virtual void SAL_CALL firePropertiesChangeEvent( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Reference< css::beans::XPropertiesChangeListener >& xListener ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // DocumentSettingsSerializer cf. xmloff + virtual uno::Sequence<beans::PropertyValue> + filterStreamsFromStorage(OUString const & referer, + const uno::Reference< embed::XStorage > &xStorage, + const uno::Sequence<beans::PropertyValue>& aConfigProps ) override; + virtual uno::Sequence<beans::PropertyValue> + filterStreamsToStorage(const uno::Reference< embed::XStorage > &xStorage, + const uno::Sequence<beans::PropertyValue>& aConfigProps ) override; + + protected: + virtual void _setPropertyValues( const comphelper::PropertyMapEntry** ppEntries, const css::uno::Any* pValues ) override; + virtual void _getPropertyValues( const comphelper::PropertyMapEntry** ppEntries, css::uno::Any* pValue ) override; + + private: + bool LoadList( XPropertyListType t, const OUString &rPath, + const OUString &rReferer, + const uno::Reference< embed::XStorage > &xStorage ); + void AssignURL( XPropertyListType t, const Any* pValue, bool *pOk, bool *pChanged ); + void ExtractURL( XPropertyListType t, Any* pValue ); + rtl::Reference<SdXImpressDocument> mxModel; + }; + + } + + Reference< XInterface > DocumentSettings_createInstance( SdXImpressDocument* pModel ) + noexcept + { + DBG_ASSERT( pModel, "I need a model for the DocumentSettings!" ); + return static_cast<XWeak*>(new DocumentSettings( pModel )); + } + +namespace { + +enum SdDocumentSettingsPropertyHandles +{ + HANDLE_PRINTDRAWING, HANDLE_PRINTNOTES, HANDLE_PRINTHANDOUT, HANDLE_PRINTOUTLINE, HANDLE_MEASUREUNIT, HANDLE_SCALE_NUM, + HANDLE_SCALE_DOM, HANDLE_TABSTOP, HANDLE_PRINTPAGENAME, HANDLE_PRINTDATE, HANDLE_PRINTTIME, + HANDLE_PRINTHIDDENPAGES, HANDLE_PRINTFITPAGE, HANDLE_PRINTTILEPAGE, HANDLE_PRINTBOOKLET, HANDLE_PRINTBOOKLETFRONT, + HANDLE_PRINTBOOKLETBACK, HANDLE_PRINTQUALITY, HANDLE_COLORTABLEURL, HANDLE_DASHTABLEURL, HANDLE_LINEENDTABLEURL, HANDLE_HATCHTABLEURL, + HANDLE_GRADIENTTABLEURL, HANDLE_BITMAPTABLEURL, HANDLE_FORBIDDENCHARS, HANDLE_APPLYUSERDATA, HANDLE_SAVETHUMBNAIL, HANDLE_PAGENUMFMT, + HANDLE_PRINTERNAME, HANDLE_PRINTERJOB, HANDLE_PRINTERPAPERSIZE, HANDLE_PARAGRAPHSUMMATION, HANDLE_CHARCOMPRESS, HANDLE_ASIANPUNCT, + HANDLE_UPDATEFROMTEMPLATE, HANDLE_PRINTER_INDEPENDENT_LAYOUT + // #i33095# + ,HANDLE_LOAD_READONLY, HANDLE_MODIFY_PASSWD, HANDLE_SAVE_VERSION + ,HANDLE_SLIDESPERHANDOUT, HANDLE_HANDOUTHORIZONTAL, + HANDLE_EMBED_FONTS, HANDLE_EMBED_USED_FONTS, + HANDLE_EMBED_LATIN_SCRIPT_FONTS, HANDLE_EMBED_ASIAN_SCRIPT_FONTS, HANDLE_EMBED_COMPLEX_SCRIPT_FONTS, + HANDLE_IMAGE_PREFERRED_DPI +}; + +} + +#define MID_PRINTER 1 + + static rtl::Reference<PropertySetInfo> createSettingsInfoImpl( bool bIsDraw ) + { + static PropertyMapEntry const aImpressSettingsInfoMap[] = + { + { OUString("IsPrintDrawing"), HANDLE_PRINTDRAWING, cppu::UnoType<bool>::get(), 0, MID_PRINTER }, + { OUString("IsPrintNotes"), HANDLE_PRINTNOTES, cppu::UnoType<bool>::get(), 0, MID_PRINTER }, + { OUString("IsPrintHandout"), HANDLE_PRINTHANDOUT, cppu::UnoType<bool>::get(), 0, MID_PRINTER }, + { OUString("IsPrintOutline"), HANDLE_PRINTOUTLINE, cppu::UnoType<bool>::get(), 0, MID_PRINTER }, + { OUString("SlidesPerHandout"), HANDLE_SLIDESPERHANDOUT, ::cppu::UnoType<sal_Int16>::get(), 0, MID_PRINTER }, + { OUString("HandoutsHorizontal"), HANDLE_HANDOUTHORIZONTAL, cppu::UnoType<bool>::get(), 0, MID_PRINTER }, + }; + + static PropertyMapEntry const aDrawSettingsInfoMap[] = + { + { OUString("MeasureUnit"), HANDLE_MEASUREUNIT, ::cppu::UnoType<sal_Int16>::get(), 0, 0 }, + { OUString("ScaleNumerator"), HANDLE_SCALE_NUM, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { OUString("ScaleDenominator"), HANDLE_SCALE_DOM, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + }; + + static PropertyMapEntry const aCommonSettingsInfoMap[] = + { + { OUString("DefaultTabStop"), HANDLE_TABSTOP, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { OUString("PrinterName"), HANDLE_PRINTERNAME, ::cppu::UnoType<OUString>::get(), 0, 0 }, + { OUString("PrinterSetup"), HANDLE_PRINTERJOB, cppu::UnoType<uno::Sequence < sal_Int8 >>::get(), 0, MID_PRINTER }, + { OUString("PrinterPaperFromSetup"), HANDLE_PRINTERPAPERSIZE, cppu::UnoType<bool>::get(), 0, MID_PRINTER }, + + { OUString("IsPrintPageName"), HANDLE_PRINTPAGENAME, cppu::UnoType<bool>::get(), 0, MID_PRINTER }, + { OUString("IsPrintDate"), HANDLE_PRINTDATE, cppu::UnoType<bool>::get(), 0, MID_PRINTER }, + { OUString("IsPrintTime"), HANDLE_PRINTTIME, cppu::UnoType<bool>::get(), 0, MID_PRINTER }, + { OUString("IsPrintHiddenPages"), HANDLE_PRINTHIDDENPAGES, cppu::UnoType<bool>::get(), 0, MID_PRINTER }, + { OUString("IsPrintFitPage"), HANDLE_PRINTFITPAGE, cppu::UnoType<bool>::get(), 0, MID_PRINTER }, + { OUString("IsPrintTilePage"), HANDLE_PRINTTILEPAGE, cppu::UnoType<bool>::get(), 0, MID_PRINTER }, + { OUString("IsPrintBooklet"), HANDLE_PRINTBOOKLET, cppu::UnoType<bool>::get(), 0, MID_PRINTER }, + { OUString("IsPrintBookletFront"), HANDLE_PRINTBOOKLETFRONT, cppu::UnoType<bool>::get(), 0, MID_PRINTER }, + { OUString("IsPrintBookletBack"), HANDLE_PRINTBOOKLETBACK, cppu::UnoType<bool>::get(), 0, MID_PRINTER }, + { OUString("PrintQuality"), HANDLE_PRINTQUALITY, ::cppu::UnoType<sal_Int32>::get(), 0, MID_PRINTER }, + { OUString("ColorTableURL"), HANDLE_COLORTABLEURL, ::cppu::UnoType<OUString>::get(), 0, 0 }, + { OUString("DashTableURL"), HANDLE_DASHTABLEURL, ::cppu::UnoType<OUString>::get(), 0, 0 }, + { OUString("LineEndTableURL"), HANDLE_LINEENDTABLEURL, ::cppu::UnoType<OUString>::get(), 0, 0 }, + { OUString("HatchTableURL"), HANDLE_HATCHTABLEURL, ::cppu::UnoType<OUString>::get(), 0, 0 }, + { OUString("GradientTableURL"), HANDLE_GRADIENTTABLEURL, ::cppu::UnoType<OUString>::get(), 0, 0 }, + { OUString("BitmapTableURL"), HANDLE_BITMAPTABLEURL, ::cppu::UnoType<OUString>::get(), 0, 0 }, + + { OUString("ForbiddenCharacters"), HANDLE_FORBIDDENCHARS, cppu::UnoType<XForbiddenCharacters>::get(), 0, 0 }, + { OUString("ApplyUserData"), HANDLE_APPLYUSERDATA, cppu::UnoType<bool>::get(), 0, 0 }, + { OUString("SaveThumbnail"), HANDLE_SAVETHUMBNAIL, cppu::UnoType<bool>::get(), 0, 0 }, + + { OUString("PageNumberFormat"), HANDLE_PAGENUMFMT, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { OUString("ParagraphSummation"), HANDLE_PARAGRAPHSUMMATION, cppu::UnoType<bool>::get(), 0, 0 }, + { OUString("CharacterCompressionType"),HANDLE_CHARCOMPRESS, ::cppu::UnoType<sal_Int16>::get(), 0, 0 }, + { OUString("IsKernAsianPunctuation"),HANDLE_ASIANPUNCT, cppu::UnoType<bool>::get(), 0, 0 }, + { OUString("UpdateFromTemplate"), HANDLE_UPDATEFROMTEMPLATE, cppu::UnoType<bool>::get(), 0, 0 }, + { OUString("PrinterIndependentLayout"),HANDLE_PRINTER_INDEPENDENT_LAYOUT,::cppu::UnoType<sal_Int16>::get(), 0, 0 }, + // --> #i33095# + { OUString("LoadReadonly"), HANDLE_LOAD_READONLY, cppu::UnoType<bool>::get(), 0, 0 }, + { OUString("ModifyPasswordInfo"), HANDLE_MODIFY_PASSWD, cppu::UnoType<uno::Sequence < beans::PropertyValue >>::get(), 0, 0 }, + { OUString("SaveVersionOnClose"), HANDLE_SAVE_VERSION, cppu::UnoType<bool>::get(), 0, 0 }, + { OUString("EmbedFonts"), HANDLE_EMBED_FONTS, cppu::UnoType<bool>::get(), 0, 0 }, + { OUString("EmbedOnlyUsedFonts"), HANDLE_EMBED_USED_FONTS, cppu::UnoType<bool>::get(), 0, 0 }, + { OUString("EmbedLatinScriptFonts"), HANDLE_EMBED_LATIN_SCRIPT_FONTS, cppu::UnoType<bool>::get(), 0, 0 }, + { OUString("EmbedAsianScriptFonts"), HANDLE_EMBED_ASIAN_SCRIPT_FONTS, cppu::UnoType<bool>::get(), 0, 0 }, + { OUString("EmbedComplexScriptFonts"), HANDLE_EMBED_COMPLEX_SCRIPT_FONTS, cppu::UnoType<bool>::get(), 0, 0 }, + { OUString("ImagePreferredDPI"), HANDLE_IMAGE_PREFERRED_DPI, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + }; + + rtl::Reference<PropertySetInfo> xInfo = new PropertySetInfo( aCommonSettingsInfoMap ); + if (bIsDraw) + xInfo->add( aDrawSettingsInfoMap ); + else + xInfo->add( aImpressSettingsInfoMap ); + + return xInfo; + } +} + +using namespace ::sd; + +DocumentSettings::DocumentSettings( SdXImpressDocument* pModel ) +: PropertySetHelper( createSettingsInfoImpl( !pModel->IsImpressDocument() ) ), + mxModel( pModel ) +{ +} + +bool DocumentSettings::LoadList( XPropertyListType t, const OUString &rInPath, + const OUString &rReferer, + const uno::Reference< embed::XStorage > &xStorage ) +{ + SdDrawDocument* pDoc = mxModel->GetDoc(); + + sal_Int32 nSlash = rInPath.lastIndexOf('/'); + OUString aPath, aName; + if (nSlash < 0) + aName = rInPath; + else { + aName = rInPath.copy( nSlash + 1 ); + aPath = rInPath.copy( 0, nSlash ); + } + + XPropertyListRef pList = XPropertyList::CreatePropertyList( + t, aPath, rReferer ); + pList->SetName( aName ); + + if( pList->LoadFrom( xStorage, rInPath, rReferer ) ) + { + pDoc->SetPropertyList( pList ); + return true; + } + + return false; +} + +void DocumentSettings::AssignURL( XPropertyListType t, const Any* pValue, + bool *pOk, bool *pChanged ) +{ + OUString aURL; + if( !( *pValue >>= aURL ) ) + return; + + if( LoadList( t, aURL, ""/*TODO?*/, uno::Reference< embed::XStorage >() ) ) + *pOk = *pChanged = true; +} + +struct { + const char *pName; + XPropertyListType t; +} const aURLPropertyNames[] = { + { "ColorTableURL", XPropertyListType::Color }, + { "DashTableURL", XPropertyListType::Dash }, + { "LineEndTableURL", XPropertyListType::LineEnd }, + { "HatchTableURL", XPropertyListType::Hatch }, + { "GradientTableURL", XPropertyListType::Gradient }, + { "BitmapTableURL", XPropertyListType::Bitmap } +}; + +static XPropertyListType getTypeOfName( std::u16string_view aName ) +{ + for(const auto & rURLPropertyName : aURLPropertyNames) { + if( o3tl::equalsAscii( aName, rURLPropertyName.pName ) ) + return rURLPropertyName.t; + } + return XPropertyListType::Unknown; +} + +static OUString getNameOfType( XPropertyListType t ) +{ + for(const auto & rURLPropertyName : aURLPropertyNames) { + if( t == rURLPropertyName.t ) + return OUString( rURLPropertyName.pName, + strlen( rURLPropertyName.pName ) - 3, + RTL_TEXTENCODING_ASCII_US ); + } + return OUString(); +} + +uno::Sequence<beans::PropertyValue> + DocumentSettings::filterStreamsFromStorage( + OUString const & referer, + const uno::Reference< embed::XStorage > &xStorage, + const uno::Sequence<beans::PropertyValue>& aConfigProps ) +{ + uno::Sequence<beans::PropertyValue> aRet( aConfigProps.getLength() ); + auto aRetRange = asNonConstRange(aRet); + int nRet = 0; + for( const auto& rConfigProp : aConfigProps ) + { + XPropertyListType t = getTypeOfName( rConfigProp.Name ); + if (t == XPropertyListType::Unknown) + aRetRange[nRet++] = rConfigProp; + else + { + OUString aURL; + rConfigProp.Value >>= aURL; + LoadList( t, aURL, referer, xStorage ); + } + } + aRet.realloc( nRet ); + return aRet; +} + +uno::Sequence<beans::PropertyValue> + DocumentSettings::filterStreamsToStorage( + const uno::Reference< embed::XStorage > &xStorage, + const uno::Sequence<beans::PropertyValue>& aConfigProps ) +{ + uno::Sequence<beans::PropertyValue> aRet( aConfigProps.getLength() ); + + bool bHasEmbed = false; + SdDrawDocument* pDoc = mxModel->GetDoc(); + for( size_t i = 0; i < SAL_N_ELEMENTS( aURLPropertyNames ); i++ ) + { + const XPropertyListRef& pList = pDoc->GetPropertyList( static_cast<XPropertyListType>(i) ); + bHasEmbed = pList.is() && pList->IsEmbedInDocument(); + if( bHasEmbed ) + break; + } + if( !bHasEmbed ) + return aConfigProps; + + try { + // create Settings/ sub storage. + uno::Reference< embed::XStorage > xSubStorage = xStorage->openStorageElement( "Settings" , + embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE ); + if( !xSubStorage.is() ) + return aRet; + + auto aRetRange = asNonConstRange(aRet); + // now populate it + for( sal_Int32 i = 0; i < aConfigProps.getLength(); i++ ) + { + XPropertyListType t = getTypeOfName( aConfigProps[i].Name ); + aRetRange[i] = aConfigProps[i]; + if (t != XPropertyListType::Unknown) { + const XPropertyListRef& pList = pDoc->GetPropertyList( t ); + if( !pList.is() || !pList->IsEmbedInDocument() ) + continue; // no change ... + else + { + // Such specific path construction is grim. + + OUString aName( getNameOfType( t ) ); + OUString aResult; + if( pList->SaveTo( xSubStorage, aName, &aResult ) ) + { + OUString aRealPath = "Settings/" + aResult; + aRetRange[i].Value <<= aRealPath; + } + } + } + } + + // surprisingly difficult to make it really exist + uno::Reference< embed::XTransactedObject > xTrans( xSubStorage, UNO_QUERY ); + if( xTrans.is() ) + xTrans->commit(); + if( xSubStorage.is() ) + xSubStorage->dispose(); + } catch (const uno::Exception &) { +// fprintf (stderr, "saving etc. exception '%s'\n", +// OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8).getStr()); + } + + return aRet; +} + +// Most of the code reading/writing UNO document settings is the same in +// sd, sc and sw and it is mostly copy-pasted back and forth. +// TODO: Move _setPropertyValues and _getPropertyValues to some shared +// place, at least for the settings that are common to sd, sc and sw +void +DocumentSettings::_setPropertyValues(const PropertyMapEntry** ppEntries, + const Any* pValues) +{ + ::SolarMutexGuard aGuard; + + SdDrawDocument* pDoc = mxModel->GetDoc(); + ::sd::DrawDocShell* pDocSh = mxModel->GetDocShell(); + if( nullptr == pDoc || nullptr == pDocSh ) + { + throw RuntimeException("Document or Shell missing", + static_cast<OWeakObject *>(this)); + } + + bool bValue = false; + bool bOk, bChanged = false, bOptionsChanged = false; + + SdOptionsPrintItem aOptionsPrintItem; + + VclPtr<SfxPrinter> pPrinter = pDocSh->GetPrinter( false ); + if( pPrinter ) + { + SdOptionsPrintItem const * pPrinterOptions = pPrinter->GetOptions().GetItemIfSet( ATTR_OPTIONS_PRINT, false ); + if(pPrinterOptions) + aOptionsPrintItem.GetOptionsPrint() = pPrinterOptions->GetOptionsPrint(); + } + else + { + aOptionsPrintItem.SetOptions( SD_MOD()->GetSdOptions(pDoc->GetDocumentType()) ); + } + SdOptionsPrint& aPrintOpts = aOptionsPrintItem.GetOptionsPrint(); + + for( ; *ppEntries; ppEntries++, pValues++ ) + { + bOk = false; + + switch( (*ppEntries)->mnHandle ) + { + case HANDLE_COLORTABLEURL: + AssignURL( XPropertyListType::Color, pValues, &bOk, &bChanged ); + break; + + case HANDLE_DASHTABLEURL: + AssignURL( XPropertyListType::Dash, pValues, &bOk, &bChanged ); + break; + + case HANDLE_LINEENDTABLEURL: + AssignURL( XPropertyListType::LineEnd, pValues, &bOk, &bChanged ); + break; + + case HANDLE_HATCHTABLEURL: + AssignURL( XPropertyListType::Hatch, pValues, &bOk, &bChanged ); + break; + + case HANDLE_GRADIENTTABLEURL: + AssignURL( XPropertyListType::Gradient, pValues, &bOk, &bChanged ); + break; + + case HANDLE_BITMAPTABLEURL: + AssignURL( XPropertyListType::Bitmap, pValues, &bOk, &bChanged ); + break; + + case HANDLE_FORBIDDENCHARS: + bOk = true; + break; + + case HANDLE_APPLYUSERDATA: + { + bool bApplyUserData = false; + if( *pValues >>= bApplyUserData ) + { + bChanged = ( bApplyUserData != pDocSh->IsUseUserData() ); + pDocSh->SetUseUserData( bApplyUserData ); + bOk = true; + } + } + break; + case HANDLE_SAVETHUMBNAIL: + { + bool bSaveThumbnail = false; + if (*pValues >>= bSaveThumbnail) + { + bChanged = (bSaveThumbnail != pDocSh->IsUseThumbnailSave()); + pDocSh->SetUseThumbnailSave(bSaveThumbnail); + bOk = true; + } + } + break; + + case HANDLE_PRINTDRAWING: + if( *pValues >>= bValue ) + { + if( aPrintOpts.IsDraw() != bValue ) + { + aPrintOpts.SetDraw( bValue ); + bOptionsChanged = true; + } + + bOk = true; + } + break; + case HANDLE_PRINTNOTES: + if( *pValues >>= bValue ) + { + if( aPrintOpts.IsNotes() != bValue ) + { + aPrintOpts.SetNotes( bValue ); + bOptionsChanged = true; + } + + bOk = true; + } + break; + case HANDLE_PRINTHANDOUT: + if( *pValues >>= bValue ) + { + if( aPrintOpts.IsHandout() != bValue) + { + aPrintOpts.SetHandout( bValue ); + bOptionsChanged = true; + } + + bOk = true; + } + break; + case HANDLE_PRINTOUTLINE: + if( *pValues >>= bValue ) + { + if( aPrintOpts.IsOutline() != bValue) + { + aPrintOpts.SetOutline( bValue ); + bOptionsChanged = true; + } + bOk = true; + } + break; + case HANDLE_SLIDESPERHANDOUT: + { + sal_Int16 nValue = 0; + if( (*pValues >>= nValue) && (nValue >= 1) && (nValue <= 9) ) + { + if( static_cast<sal_Int16>( aPrintOpts.GetHandoutPages() ) != nValue ) + { + aPrintOpts.SetHandoutPages( static_cast< sal_uInt16 >( nValue ) ); + bOptionsChanged = true; + } + bOk = true; + } + } + break; + case HANDLE_HANDOUTHORIZONTAL: + if( *pValues >>= bValue ) + { + if( aPrintOpts.IsHandoutHorizontal() != bValue ) + { + aPrintOpts.SetHandoutHorizontal( bValue ); + bOptionsChanged = true; + } + bOk = true; + } + break; + + case HANDLE_PRINTPAGENAME: + if( *pValues >>= bValue ) + { + if( aPrintOpts.IsPagename() != bValue) + { + aPrintOpts.SetPagename( bValue ); + bOptionsChanged = true; + } + bOk = true; + } + break; + case HANDLE_PRINTDATE: + if( *pValues >>= bValue ) + { + if( aPrintOpts.IsDate() != bValue) + { + aPrintOpts.SetDate( bValue ); + bOptionsChanged = true; + } + bOk = true; + } + break; + case HANDLE_PRINTTIME: + if( *pValues >>= bValue ) + { + if( aPrintOpts.IsDate() != bValue) + { + aPrintOpts.SetTime( bValue ); + bOptionsChanged = true; + } + bOk = true; + } + break; + case HANDLE_PRINTHIDDENPAGES: + if( *pValues >>= bValue ) + { + if( aPrintOpts.IsHiddenPages() != bValue) + { + aPrintOpts.SetHiddenPages( bValue ); + bOptionsChanged = true; + } + bOk = true; + } + break; + case HANDLE_PRINTFITPAGE: + if( *pValues >>= bValue ) + { + if( aPrintOpts.IsPagesize() != bValue) + { + aPrintOpts.SetPagesize( bValue ); + bOptionsChanged = true; + } + bOk = true; + } + break; + case HANDLE_PRINTTILEPAGE: + if( *pValues >>= bValue ) + { + if( aPrintOpts.IsPagetile() != bValue) + { + aPrintOpts.SetPagetile( bValue ); + bOptionsChanged = true; + } + bOk = true; + } + break; + case HANDLE_PRINTBOOKLET: + if( *pValues >>= bValue ) + { + if( aPrintOpts.IsBooklet() != bValue) + { + aPrintOpts.SetBooklet( bValue ); + bOptionsChanged = true; + } + bOk = true; + } + break; + case HANDLE_PRINTBOOKLETFRONT: + if( *pValues >>= bValue ) + { + if( aPrintOpts.IsFrontPage() != bValue) + { + aPrintOpts.SetFrontPage( bValue ); + bOptionsChanged = true; + } + bOk = true; + } + break; + case HANDLE_PRINTBOOKLETBACK: + if( *pValues >>= bValue ) + { + if( aPrintOpts.IsBackPage() != bValue) + { + aPrintOpts.SetBackPage( bValue ); + bOptionsChanged = true; + } + bOk = true; + } + break; + case HANDLE_PRINTQUALITY: + { + sal_Int32 nValue = 0; + if( *pValues >>= nValue ) + { + if( aPrintOpts.GetOutputQuality() != nValue) + { + aPrintOpts.SetOutputQuality( static_cast<sal_uInt16>(nValue) ); + bOptionsChanged = true; + } + bOk = true; + } + } + break; + case HANDLE_MEASUREUNIT: + { + sal_Int16 nValue = 0; + if( *pValues >>= nValue ) + { + FieldUnit nFieldUnit; + if( SvxMeasureUnitToFieldUnit( nValue, nFieldUnit ) ) + { + pDoc->SetUIUnit( nFieldUnit ); + bOk = true; + } + } + } + break; + case HANDLE_SCALE_NUM: + { + sal_Int32 nValue = 0; + if( *pValues >>= nValue ) + { + Fraction aFract( nValue, pDoc->GetUIScale().GetDenominator() ); + pDoc->SetUIScale( aFract ); + bOk = true; + bChanged = true; + } + } + break; + case HANDLE_SCALE_DOM: + { + sal_Int32 nValue = 0; + if( *pValues >>= nValue ) + { + auto nNumerator = pDoc->GetUIScale().GetNumerator(); + assert(nNumerator != 0); + Fraction aFract(nNumerator, nValue); + pDoc->SetUIScale( aFract ); + bOk = true; + bChanged = true; + } + } + break; + + case HANDLE_TABSTOP: + { + sal_Int32 nValue = 0; + if( (*pValues >>= nValue) && (nValue >= 0) ) + { + pDoc->SetDefaultTabulator(static_cast<sal_uInt16>(nValue)); + bOk = true; + bChanged = true; + } + } + break; + case HANDLE_PAGENUMFMT: + { + sal_Int32 nValue = 0; + if( (*pValues >>= nValue ) && (nValue >= css::style::NumberingType::CHARS_UPPER_LETTER ) && (nValue <= css::style::NumberingType::PAGE_DESCRIPTOR) ) + { + pDoc->SetPageNumType(static_cast<SvxNumType>(nValue)); + bOk = true; + bChanged = true; + } + } + break; + case HANDLE_PRINTERNAME: + { + OUString aPrinterName; + if( *pValues >>= aPrinterName ) + { + bOk = true; + if( !aPrinterName.isEmpty() && pDocSh->GetCreateMode() != SfxObjectCreateMode::EMBEDDED ) + { + SfxPrinter *pTempPrinter = pDocSh->GetPrinter( true ); + if (pTempPrinter) + { + VclPtr<SfxPrinter> pNewPrinter = VclPtr<SfxPrinter>::Create( pTempPrinter->GetOptions().Clone(), aPrinterName ); + pDocSh->SetPrinter( pNewPrinter ); + } + } + } + } + break; + case HANDLE_PRINTERJOB: + { + Sequence < sal_Int8 > aSequence; + if ( *pValues >>= aSequence ) + { + bOk = true; + sal_uInt32 nSize = aSequence.getLength(); + if( nSize ) + { + SvMemoryStream aStream (aSequence.getArray(), nSize, StreamMode::READ ); + aStream.Seek ( STREAM_SEEK_TO_BEGIN ); + std::unique_ptr<SfxItemSet> pItemSet; + + bool bPreferPrinterPapersize = false; + if( pPrinter ) + { + pItemSet = pPrinter->GetOptions().Clone(); + bPreferPrinterPapersize = pPrinter->GetPrinterSettingsPreferred(); + } + else + { + pItemSet = std::make_unique<SfxItemSetFixed + <SID_PRINTER_NOTFOUND_WARN, SID_PRINTER_NOTFOUND_WARN, + SID_PRINTER_CHANGESTODOC, SID_PRINTER_CHANGESTODOC, + ATTR_OPTIONS_PRINT, ATTR_OPTIONS_PRINT>>(pDoc->GetPool()); + } + + pPrinter = SfxPrinter::Create ( aStream, std::move(pItemSet) ); + pPrinter->SetPrinterSettingsPreferred( bPreferPrinterPapersize ); + + MapMode aMM (pPrinter->GetMapMode()); + aMM.SetMapUnit(MapUnit::Map100thMM); + pPrinter->SetMapMode(aMM); + + pDocSh->SetPrinter( pPrinter ); + + pPrinter = nullptr; + } + } + } + break; + + case HANDLE_PRINTERPAPERSIZE: + { + bool bPreferPrinterPapersize; + if( *pValues >>= bPreferPrinterPapersize ) + { + bOk = true; + if( pDocSh->GetCreateMode() != SfxObjectCreateMode::EMBEDDED ) + { + SfxPrinter *pTempPrinter = pDocSh->GetPrinter( true ); + if (pTempPrinter) + pTempPrinter->SetPrinterSettingsPreferred( bPreferPrinterPapersize ); + } + } + } + break; + + case HANDLE_PARAGRAPHSUMMATION : + { + bool bIsSummationOfParagraphs = false; + if ( *pValues >>= bIsSummationOfParagraphs ) + { + bOk = true; + bChanged = true; + if ( pDoc->GetDocumentType() == DocumentType::Impress ) + { + EEControlBits nSum = bIsSummationOfParagraphs ? EEControlBits::ULSPACESUMMATION : EEControlBits::NONE; + EEControlBits nCntrl; + + pDoc->SetSummationOfParagraphs( bIsSummationOfParagraphs ); + SdDrawDocument* pDocument = pDocSh->GetDoc(); + SdrOutliner& rOutl = pDocument->GetDrawOutliner(); + nCntrl = rOutl.GetControlWord() &~ EEControlBits::ULSPACESUMMATION; + rOutl.SetControlWord( nCntrl | nSum ); + SdOutliner* pOutl = pDocument->GetOutliner( false ); + if( pOutl ) + { + nCntrl = pOutl->GetControlWord() &~ EEControlBits::ULSPACESUMMATION; + pOutl->SetControlWord( nCntrl | nSum ); + } + pOutl = pDocument->GetInternalOutliner( false ); + if( pOutl ) + { + nCntrl = pOutl->GetControlWord() &~ EEControlBits::ULSPACESUMMATION; + pOutl->SetControlWord( nCntrl | nSum ); + } + } + } + } + break; + + case HANDLE_CHARCOMPRESS: + { + sal_Int16 nCharCompressType = 0; + if( *pValues >>= nCharCompressType ) + { + bOk = true; + + pDoc->SetCharCompressType( static_cast<CharCompressType>(nCharCompressType) ); + SdDrawDocument* pDocument = pDocSh->GetDoc(); + SdrOutliner& rOutl = pDocument->GetDrawOutliner(); + rOutl.SetAsianCompressionMode( static_cast<CharCompressType>(nCharCompressType) ); + SdOutliner* pOutl = pDocument->GetOutliner( false ); + if( pOutl ) + { + pOutl->SetAsianCompressionMode( static_cast<CharCompressType>(nCharCompressType) ); + } + pOutl = pDocument->GetInternalOutliner( false ); + if( pOutl ) + { + pOutl->SetAsianCompressionMode( static_cast<CharCompressType>(nCharCompressType) ); + } + } + break; + + } + case HANDLE_ASIANPUNCT: + { + bool bAsianPunct = false; + if( *pValues >>= bAsianPunct ) + { + bOk = true; + + pDoc->SetKernAsianPunctuation( bAsianPunct ); + SdDrawDocument* pDocument = pDocSh->GetDoc(); + SdrOutliner& rOutl = pDocument->GetDrawOutliner(); + rOutl.SetKernAsianPunctuation( bAsianPunct ); + SdOutliner* pOutl = pDocument->GetOutliner( false ); + if( pOutl ) + { + pOutl->SetKernAsianPunctuation( bAsianPunct ); + } + pOutl = pDocument->GetInternalOutliner( false ); + if( pOutl ) + { + pOutl->SetKernAsianPunctuation( bAsianPunct ); + } + } + break; + + } + case HANDLE_UPDATEFROMTEMPLATE: + { + bool value = false; + if( *pValues >>= value ) + { + bChanged = ( value != pDocSh->IsQueryLoadTemplate() ); + pDocSh->SetQueryLoadTemplate( value ); + bOk = true; + } + } + break; + + case HANDLE_PRINTER_INDEPENDENT_LAYOUT: + { + // Just propagate the new printer independent layout mode to + // the document and determine it really differs from the old + // one. + sal_Int16 nOldValue = + static_cast<sal_Int16>(pDoc->GetPrinterIndependentLayout ()); + sal_Int16 nValue = 0; + if (*pValues >>= nValue) + { + pDoc->SetPrinterIndependentLayout (nValue); + bChanged = (nValue != nOldValue); + bOk = true; + } + } + break; + + // --> #i33095# + case HANDLE_LOAD_READONLY: + { + bool bNewValue = false; + if ( *pValues >>= bNewValue ) + { + bChanged = ( pDocSh->IsLoadReadonly() != bNewValue ); + pDocSh->SetLoadReadonly( bNewValue ); + bOk = true; + } + } + break; + + case HANDLE_MODIFY_PASSWD: + { + uno::Sequence< beans::PropertyValue > aInfo; + if ( !( *pValues >>= aInfo ) ) + throw lang::IllegalArgumentException( + "Value of type Sequence<PropertyValue> expected!", + uno::Reference< uno::XInterface >(), + 2 ); + + if ( !pDocSh->SetModifyPasswordInfo( aInfo ) ) + throw beans::PropertyVetoException( + "The hash is not allowed to be changed now!" ); + + bOk = true +; + + } + break; + + case HANDLE_SAVE_VERSION: + { + bool bNewValue = false; + if ( *pValues >>= bNewValue ) + { + bChanged = ( pDocSh->IsSaveVersionOnClose() != bNewValue ); + pDocSh->SetSaveVersionOnClose( bNewValue ); + bOk = true; + } + } + break; + + case HANDLE_EMBED_FONTS: + { + if (pValues->has<bool>()) + { + bool bNewValue = pValues->get<bool>(); + bChanged = (pDoc->IsEmbedFonts() != bNewValue); + pDoc->SetEmbedFonts(bNewValue); + bOk = true; + } + } + break; + + case HANDLE_EMBED_USED_FONTS: + { + if (pValues->has<bool>()) + { + bool bNewValue = pValues->get<bool>(); + bChanged = (pDoc->IsEmbedUsedFontsOnly() != bNewValue); + pDoc->SetEmbedUsedFontsOnly(bNewValue); + bOk = true; + } + } + break; + + case HANDLE_EMBED_LATIN_SCRIPT_FONTS: + { + if (pValues->has<bool>()) + { + bool bNewValue = pValues->get<bool>(); + bChanged = (pDoc->IsEmbedFontScriptLatin() != bNewValue); + pDoc->SetEmbedFontScriptLatin(bNewValue); + bOk = true; + } + } + break; + + case HANDLE_EMBED_ASIAN_SCRIPT_FONTS: + { + if (pValues->has<bool>()) + { + bool bNewValue = pValues->get<bool>(); + bChanged = (pDoc->IsEmbedFontScriptAsian() != bNewValue); + pDoc->SetEmbedFontScriptAsian(bNewValue); + bOk = true; + } + } + break; + + case HANDLE_EMBED_COMPLEX_SCRIPT_FONTS: + { + if (pValues->has<bool>()) + { + bool bNewValue = pValues->get<bool>(); + bChanged = (pDoc->IsEmbedFontScriptComplex() != bNewValue); + pDoc->SetEmbedFontScriptComplex(bNewValue); + bOk = true; + } + } + break; + + case HANDLE_IMAGE_PREFERRED_DPI: + { + if (pValues->has<sal_Int32>()) + { + auto nNewValue = pValues->get<sal_Int32>(); + bChanged = (pDoc->getImagePreferredDPI() != nNewValue); + pDoc->setImagePreferredDPI(nNewValue); + bOk = true; + } + } + break; + + default: + throw UnknownPropertyException( OUString::number((*ppEntries)->mnHandle), static_cast<cppu::OWeakObject*>(this)); + } + + if( !bOk ) + throw IllegalArgumentException(); + } + + if( bOptionsChanged ) + { + if( !pPrinter ) + pPrinter = pDocSh->GetPrinter( true ); + SfxItemSet aNewOptions( pPrinter->GetOptions() ); + aNewOptions.Put( aOptionsPrintItem ); + pPrinter->SetOptions( aNewOptions ); + } + + if( bChanged || bOptionsChanged ) + mxModel->SetModified(); +} + +void DocumentSettings::ExtractURL( XPropertyListType t, Any* pValue ) +{ + XPropertyListRef pList = mxModel->GetDoc()->GetPropertyList( t ); + if( !pList.is() ) + return; + + INetURLObject aPathURL( pList->GetPath() ); + aPathURL.insertName( pList->GetName() ); + aPathURL.setExtension( pList->GetDefaultExt() ); + OUString aPath( aPathURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + *pValue <<= aPath; +} + +void +DocumentSettings::_getPropertyValues( + const PropertyMapEntry** ppEntries, Any* pValue) +{ + ::SolarMutexGuard aGuard; + + SdDrawDocument* pDoc = mxModel->GetDoc(); + ::sd::DrawDocShell* pDocSh = mxModel->GetDocShell(); + if( nullptr == pDoc || nullptr == pDocSh ) + { + throw RuntimeException("Document or Shell missing", + static_cast<OWeakObject *>(this)); + } + + SdOptionsPrintItem aOptionsPrintItem; + + SfxPrinter* pPrinter = pDocSh->GetPrinter( false ); + if( pPrinter ) + { + SdOptionsPrintItem const * pPrinterOptions = pPrinter->GetOptions().GetItemIfSet( ATTR_OPTIONS_PRINT, false ); + if (pPrinterOptions) + aOptionsPrintItem.GetOptionsPrint() = pPrinterOptions->GetOptionsPrint(); + } + else + { + aOptionsPrintItem.SetOptions( SD_MOD()->GetSdOptions(pDoc->GetDocumentType()) ); + } + SdOptionsPrint& aPrintOpts = aOptionsPrintItem.GetOptionsPrint(); + + for( ; *ppEntries; ppEntries++, pValue++ ) + { + switch( (*ppEntries)->mnHandle ) + { + case HANDLE_COLORTABLEURL: + ExtractURL( XPropertyListType::Color, pValue ); + break; + case HANDLE_DASHTABLEURL: + ExtractURL( XPropertyListType::Dash, pValue ); + break; + case HANDLE_LINEENDTABLEURL: + ExtractURL( XPropertyListType::LineEnd, pValue ); + break; + case HANDLE_HATCHTABLEURL: + ExtractURL( XPropertyListType::Hatch, pValue ); + break; + case HANDLE_GRADIENTTABLEURL: + ExtractURL( XPropertyListType::Gradient, pValue ); + break; + case HANDLE_BITMAPTABLEURL: + ExtractURL( XPropertyListType::Bitmap, pValue ); + break; + case HANDLE_FORBIDDENCHARS: + *pValue <<= mxModel->getForbiddenCharsTable(); + break; + case HANDLE_APPLYUSERDATA: + *pValue <<= pDocSh->IsUseUserData(); + break; + case HANDLE_SAVETHUMBNAIL: + *pValue <<= pDocSh->IsUseThumbnailSave(); + break; + case HANDLE_PRINTDRAWING: + *pValue <<= aPrintOpts.IsDraw(); + break; + case HANDLE_PRINTNOTES: + *pValue <<= aPrintOpts.IsNotes(); + break; + case HANDLE_PRINTHANDOUT: + *pValue <<= aPrintOpts.IsHandout(); + break; + case HANDLE_PRINTOUTLINE: + *pValue <<= aPrintOpts.IsOutline(); + break; + case HANDLE_SLIDESPERHANDOUT: + *pValue <<= static_cast<sal_Int16>(aPrintOpts.GetHandoutPages()); + break; + case HANDLE_HANDOUTHORIZONTAL: + *pValue <<= aPrintOpts.IsHandoutHorizontal(); + break; + case HANDLE_PRINTPAGENAME: + *pValue <<= aPrintOpts.IsPagename(); + break; + case HANDLE_PRINTDATE: + *pValue <<= aPrintOpts.IsDate(); + break; + case HANDLE_PRINTTIME: + *pValue <<= aPrintOpts.IsTime(); + break; + case HANDLE_PRINTHIDDENPAGES: + *pValue <<= aPrintOpts.IsHiddenPages(); + break; + case HANDLE_PRINTFITPAGE: + *pValue <<= aPrintOpts.IsPagesize(); + break; + case HANDLE_PRINTTILEPAGE: + *pValue <<= aPrintOpts.IsPagetile(); + break; + case HANDLE_PRINTBOOKLET: + *pValue <<= aPrintOpts.IsBooklet(); + break; + case HANDLE_PRINTBOOKLETFRONT: + *pValue <<= aPrintOpts.IsFrontPage(); + break; + case HANDLE_PRINTBOOKLETBACK: + *pValue <<= aPrintOpts.IsBackPage(); + break; + case HANDLE_PRINTQUALITY: + *pValue <<= static_cast<sal_Int32>(aPrintOpts.GetOutputQuality()); + break; + case HANDLE_MEASUREUNIT: + { + short nMeasure; + SvxFieldUnitToMeasureUnit( pDoc->GetUIUnit(), nMeasure ); + *pValue <<= static_cast<sal_Int16>(nMeasure); + } + break; + case HANDLE_SCALE_NUM: + *pValue <<= pDoc->GetUIScale().GetNumerator(); + break; + case HANDLE_SCALE_DOM: + *pValue <<= pDoc->GetUIScale().GetDenominator(); + break; + case HANDLE_TABSTOP: + *pValue <<= static_cast<sal_Int32>(pDoc->GetDefaultTabulator()); + break; + case HANDLE_PAGENUMFMT: + *pValue <<= static_cast<sal_Int32>(pDoc->GetPageNumType()); + break; + case HANDLE_PRINTERNAME: + { + SfxPrinter *pTempPrinter = pDocSh->GetPrinter( false ); + *pValue <<= pTempPrinter ? pTempPrinter->GetName() : OUString(); + } + break; + case HANDLE_PRINTERJOB: + { + SfxPrinter *pTempPrinter = pDocSh->GetPrinter( false ); + if (pTempPrinter) + { + SvMemoryStream aStream; + pTempPrinter->Store( aStream ); + *pValue <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aStream.GetData() ), + aStream.TellEnd() ); + } + else + { + Sequence < sal_Int8 > aSequence; + *pValue <<= aSequence; + } + } + break; + + case HANDLE_PRINTERPAPERSIZE: + { + SfxPrinter *pTempPrinter = pDocSh->GetPrinter( false ); + *pValue <<= pTempPrinter && pTempPrinter->GetPrinterSettingsPreferred(); + } + break; + + case HANDLE_PARAGRAPHSUMMATION : + { + bool bIsSummationOfParagraphs = pDoc->IsSummationOfParagraphs(); + *pValue <<= bIsSummationOfParagraphs; + } + break; + + case HANDLE_CHARCOMPRESS: + { + *pValue <<= static_cast<sal_Int16>(pDoc->GetCharCompressType()); + break; + } + + case HANDLE_ASIANPUNCT: + { + *pValue <<= pDoc->IsKernAsianPunctuation(); + break; + } + + case HANDLE_UPDATEFROMTEMPLATE: + { + *pValue <<= pDocSh->IsQueryLoadTemplate(); + } + break; + + case HANDLE_PRINTER_INDEPENDENT_LAYOUT: + { + sal_Int16 nPrinterIndependentLayout = + static_cast<sal_Int16>(pDoc->GetPrinterIndependentLayout()); + *pValue <<= nPrinterIndependentLayout; + } + break; + + // --> #i33095# + case HANDLE_LOAD_READONLY: + { + *pValue <<= pDocSh->IsLoadReadonly(); + } + break; + + case HANDLE_MODIFY_PASSWD: + { + *pValue <<= pDocSh->GetModifyPasswordInfo(); + } + break; + + case HANDLE_SAVE_VERSION: + { + *pValue <<= pDocSh->IsSaveVersionOnClose(); + } + break; + + case HANDLE_EMBED_FONTS: + { + *pValue <<= pDoc->IsEmbedFonts(); + } + break; + + case HANDLE_EMBED_USED_FONTS: + { + *pValue <<= pDoc->IsEmbedUsedFontsOnly(); + } + break; + + case HANDLE_EMBED_LATIN_SCRIPT_FONTS: + { + *pValue <<= pDoc->IsEmbedFontScriptLatin(); + } + break; + + case HANDLE_EMBED_ASIAN_SCRIPT_FONTS: + { + *pValue <<= pDoc->IsEmbedFontScriptAsian(); + } + break; + + case HANDLE_EMBED_COMPLEX_SCRIPT_FONTS: + { + *pValue <<= pDoc->IsEmbedFontScriptComplex(); + } + break; + + case HANDLE_IMAGE_PREFERRED_DPI: + { + *pValue <<= pDoc->getImagePreferredDPI(); + } + break; + + default: + throw UnknownPropertyException( OUString::number((*ppEntries)->mnHandle), static_cast<cppu::OWeakObject*>(this)); + } + } +} + +// XInterface +Any SAL_CALL DocumentSettings::queryInterface( const Type& aType ) +{ + return WeakImplHelper< XPropertySet, XMultiPropertySet, XServiceInfo >::queryInterface( aType ); +} + +void SAL_CALL DocumentSettings::acquire( ) noexcept +{ + WeakImplHelper< XPropertySet, XMultiPropertySet, XServiceInfo >::acquire(); +} + +void SAL_CALL DocumentSettings::release( ) noexcept +{ + WeakImplHelper< XPropertySet, XMultiPropertySet, XServiceInfo >::release(); +} + +// XPropertySet +Reference< XPropertySetInfo > SAL_CALL DocumentSettings::getPropertySetInfo( ) +{ + return PropertySetHelper::getPropertySetInfo(); +} + +void SAL_CALL DocumentSettings::setPropertyValue( const OUString& aPropertyName, const Any& aValue ) +{ + PropertySetHelper::setPropertyValue( aPropertyName, aValue ); +} + +Any SAL_CALL DocumentSettings::getPropertyValue( const OUString& PropertyName ) +{ + return PropertySetHelper::getPropertyValue( PropertyName ); +} + +void SAL_CALL DocumentSettings::addPropertyChangeListener( const OUString& aPropertyName, const Reference< XPropertyChangeListener >& xListener ) +{ + PropertySetHelper::addPropertyChangeListener( aPropertyName, xListener ); +} + +void SAL_CALL DocumentSettings::removePropertyChangeListener( const OUString& aPropertyName, const Reference< XPropertyChangeListener >& aListener ) +{ + PropertySetHelper::removePropertyChangeListener( aPropertyName, aListener ); +} + +void SAL_CALL DocumentSettings::addVetoableChangeListener( const OUString& PropertyName, const Reference< XVetoableChangeListener >& aListener ) +{ + PropertySetHelper::addVetoableChangeListener( PropertyName, aListener ); +} + +void SAL_CALL DocumentSettings::removeVetoableChangeListener( const OUString& PropertyName, const Reference< XVetoableChangeListener >& aListener ) +{ + PropertySetHelper::removeVetoableChangeListener( PropertyName, aListener ); +} + +// XMultiPropertySet +void SAL_CALL DocumentSettings::setPropertyValues( const Sequence< OUString >& aPropertyNames, const Sequence< Any >& aValues ) +{ + PropertySetHelper::setPropertyValues( aPropertyNames, aValues ); +} + +Sequence< Any > SAL_CALL DocumentSettings::getPropertyValues( const Sequence< OUString >& aPropertyNames ) +{ + return PropertySetHelper::getPropertyValues( aPropertyNames ); +} + +void SAL_CALL DocumentSettings::addPropertiesChangeListener( const Sequence< OUString >& aPropertyNames, const Reference< XPropertiesChangeListener >& xListener ) +{ + PropertySetHelper::addPropertiesChangeListener( aPropertyNames, xListener ); +} + +void SAL_CALL DocumentSettings::removePropertiesChangeListener( const Reference< XPropertiesChangeListener >& xListener ) +{ + PropertySetHelper::removePropertiesChangeListener( xListener ); +} + +void SAL_CALL DocumentSettings::firePropertiesChangeEvent( const Sequence< OUString >& aPropertyNames, const Reference< XPropertiesChangeListener >& xListener ) +{ + PropertySetHelper::firePropertiesChangeEvent( aPropertyNames, xListener ); +} + +// XServiceInfo +OUString SAL_CALL DocumentSettings::getImplementationName( ) +{ + return "com.sun.star.comp.Draw.DocumentSettings"; +} + +sal_Bool SAL_CALL DocumentSettings::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence< OUString > SAL_CALL DocumentSettings::getSupportedServiceNames( ) +{ + return { "com.sun.star.document.Settings" , + mxModel->IsImpressDocument()?OUString("com.sun.star.presentation.DocumentSettings"):OUString("com.sun.star.drawing.DocumentSettings") }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/UnoDocumentSettings.hxx b/sd/source/ui/unoidl/UnoDocumentSettings.hxx new file mode 100644 index 000000000..bba3c66b1 --- /dev/null +++ b/sd/source/ui/unoidl/UnoDocumentSettings.hxx @@ -0,0 +1,37 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/uno/Reference.hxx> + +namespace com::sun::star::uno +{ +class XInterface; +} + +class SdXImpressDocument; + +namespace sd +{ +css::uno::Reference<css::uno::XInterface> +DocumentSettings_createInstance(SdXImpressDocument* pDoc) noexcept; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/randomnode.cxx b/sd/source/ui/unoidl/randomnode.cxx new file mode 100644 index 000000000..a87ae1783 --- /dev/null +++ b/sd/source/ui/unoidl/randomnode.cxx @@ -0,0 +1,573 @@ +/* -*- 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 <com/sun/star/animations/XTimeContainer.hpp> +#include <com/sun/star/presentation/ParagraphTarget.hpp> +#include <com/sun/star/animations/AnimationFill.hpp> +#include <com/sun/star/animations/AnimationNodeType.hpp> +#include <com/sun/star/animations/XAnimate.hpp> +#include <com/sun/star/animations/AnimationRestart.hpp> +#include <com/sun/star/animations/ParallelTimeContainer.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <com/sun/star/util/XCloneable.hpp> +#include <comphelper/processfactory.hxx> + +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weakref.hxx> +#include <osl/mutex.hxx> +#include <CustomAnimationPreset.hxx> +#include <randomnode.hxx> + +using ::osl::Mutex; +using ::osl::Guard; + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::XInterface; +using ::com::sun::star::uno::WeakReference; +using ::com::sun::star::beans::NamedValue; +using ::com::sun::star::lang::IllegalArgumentException; +using ::com::sun::star::container::XEnumeration; +using ::com::sun::star::container::XEnumerationAccess; +using ::com::sun::star::util::XCloneable; +using ::com::sun::star::lang::XServiceInfo; +using ::com::sun::star::lang::XInitialization; +using ::com::sun::star::uno::Type; +using ::com::sun::star::uno::XWeak; +using ::com::sun::star::presentation::ParagraphTarget; +using ::com::sun::star::drawing::XShape; + +using namespace ::com::sun::star::animations; + +namespace sd +{ + +typedef ::cppu::WeakImplHelper< XTimeContainer, XEnumerationAccess, XCloneable, XServiceInfo, XInitialization > RandomAnimationNodeBase; + +namespace { + +class RandomAnimationNode : public RandomAnimationNodeBase +{ +public: + RandomAnimationNode( const RandomAnimationNode& rNode ); + explicit RandomAnimationNode( sal_Int16 nPresetClass ); + RandomAnimationNode(); + + void init( sal_Int16 nPresetClass ); + + // XInitialization + void SAL_CALL initialize( const Sequence< Any >& aArguments ) override; + + // XChild + Reference< XInterface > SAL_CALL getParent( ) override; + void SAL_CALL setParent( const Reference< XInterface >& Parent ) override; + + // XCloneable + virtual Reference< XCloneable > SAL_CALL createClone() override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + + // XAnimationNode + ::sal_Int16 SAL_CALL getType() override; + Any SAL_CALL getBegin() override; + void SAL_CALL setBegin( const Any& _begin ) override; + Any SAL_CALL getDuration() override; + void SAL_CALL setDuration( const Any& _duration ) override; + Any SAL_CALL getEnd() override; + void SAL_CALL setEnd( const Any& _end ) override; + Any SAL_CALL getEndSync() override; + void SAL_CALL setEndSync( const Any& _endsync ) override; + Any SAL_CALL getRepeatCount() override; + void SAL_CALL setRepeatCount( const Any& _repeatcount ) override; + Any SAL_CALL getRepeatDuration() override; + void SAL_CALL setRepeatDuration( const Any& _repeatduration ) override; + ::sal_Int16 SAL_CALL getFill() override; + void SAL_CALL setFill( ::sal_Int16 _fill ) override; + ::sal_Int16 SAL_CALL getFillDefault() override; + void SAL_CALL setFillDefault( ::sal_Int16 _filldefault ) override; + ::sal_Int16 SAL_CALL getRestart() override; + void SAL_CALL setRestart( ::sal_Int16 _restart ) override; + ::sal_Int16 SAL_CALL getRestartDefault() override; + void SAL_CALL setRestartDefault( ::sal_Int16 _restartdefault ) override; + double SAL_CALL getAcceleration() override; + void SAL_CALL setAcceleration( double _acceleration ) override; + double SAL_CALL getDecelerate() override; + void SAL_CALL setDecelerate( double _decelerate ) override; + sal_Bool SAL_CALL getAutoReverse() override; + void SAL_CALL setAutoReverse( sal_Bool _autoreverse ) override; + Sequence< NamedValue > SAL_CALL getUserData() override; + void SAL_CALL setUserData( const Sequence< NamedValue >& _userdata ) override; + + // XElementAccess + virtual Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + // XEnumerationAccess + virtual Reference< XEnumeration > SAL_CALL createEnumeration() override; + + // XTimeContainer + Reference< XAnimationNode > SAL_CALL insertBefore( const Reference< XAnimationNode >& newChild, const Reference< XAnimationNode >& refChild ) override; + Reference< XAnimationNode > SAL_CALL insertAfter( const Reference< XAnimationNode >& newChild, const Reference< XAnimationNode >& refChild ) override; + Reference< XAnimationNode > SAL_CALL replaceChild( const Reference< XAnimationNode >& newChild, const Reference< XAnimationNode >& oldChild ) override; + Reference< XAnimationNode > SAL_CALL removeChild( const Reference< XAnimationNode >& oldChild ) override; + Reference< XAnimationNode > SAL_CALL appendChild( const Reference< XAnimationNode >& newChild ) override; + +private: + // our first, last and only protection from multi-threads! + Mutex maMutex; + + sal_Int16 mnPresetClass; + WeakReference<XInterface> mxParent; + + Any maBegin, maDuration, maEnd, maEndSync, maRepeatCount, maRepeatDuration, maTarget; + sal_Int16 mnFill, mnFillDefault, mnRestart, mnRestartDefault; + double mfAcceleration, mfDecelerate; + bool mbAutoReverse; + Sequence< NamedValue > maUserData; + + Reference< XAnimate > mxFirstNode; +}; + +} + +Reference< XInterface > RandomAnimationNode_createInstance( sal_Int16 nPresetClass ) +{ + Reference< XInterface > xInt( static_cast<XWeak*>( new RandomAnimationNode( nPresetClass ) ) ); + return xInt; +} + +RandomAnimationNode::RandomAnimationNode( const RandomAnimationNode& rNode ) +: RandomAnimationNodeBase(rNode), + mnPresetClass( rNode.mnPresetClass ), + maBegin( rNode.maBegin ), + maDuration( rNode.maDuration ), + maEnd( rNode.maEnd ), + maEndSync( rNode.maEndSync ), + maRepeatCount( rNode.maRepeatCount ), + maRepeatDuration( rNode.maRepeatDuration ), + maTarget( rNode.maTarget ), + mnFill( rNode.mnFill ), + mnFillDefault( rNode.mnFillDefault ), + mnRestart( rNode.mnRestart ), + mnRestartDefault( rNode.mnRestartDefault ), + mfAcceleration( rNode.mfAcceleration ), + mfDecelerate( rNode.mfDecelerate ), + mbAutoReverse( rNode.mbAutoReverse ), + maUserData( rNode.maUserData ) +{ +} + +RandomAnimationNode::RandomAnimationNode( sal_Int16 nPresetClass ) +{ + init( nPresetClass ); +} + +RandomAnimationNode::RandomAnimationNode() +{ + init( 1 ); +} + +void RandomAnimationNode::init( sal_Int16 nPresetClass ) +{ + mnPresetClass = nPresetClass; + mnFill = AnimationFill::DEFAULT; + mnFillDefault = AnimationFill::INHERIT; + mnRestart = AnimationRestart::DEFAULT; + mnRestartDefault = AnimationRestart::INHERIT; + mfAcceleration = 0.0; + mfDecelerate = 0.0; + mbAutoReverse = false; +} + +// XInitialization +void SAL_CALL RandomAnimationNode::initialize( const Sequence< Any >& aArguments ) +{ + if( aArguments.getLength() != 1 ) + throw IllegalArgumentException(); + + if( aArguments[0].getValueType() == ::cppu::UnoType<sal_Int16>::get() ) + { + aArguments[0] >>= mnPresetClass; + } + else if( aArguments[0].getValueType() != ::cppu::UnoType<ParagraphTarget>::get() ) + { + Reference< XShape > xShape; + aArguments[0] >>= xShape; + if( !xShape.is() ) + throw IllegalArgumentException(); + } + maTarget = aArguments[0]; +} + +// XAnimationNode +sal_Int16 SAL_CALL RandomAnimationNode::getType() +{ + Guard< Mutex > aGuard( maMutex ); + return css::animations::AnimationNodeType::PAR; +} + +// XAnimationNode +Any SAL_CALL RandomAnimationNode::getBegin() +{ + Guard< Mutex > aGuard( maMutex ); + return maBegin; +} + +// XAnimationNode +void SAL_CALL RandomAnimationNode::setBegin( const Any& _begin ) +{ + Guard< Mutex > aGuard( maMutex ); + maBegin = _begin; +} + +// XAnimationNode +Any SAL_CALL RandomAnimationNode::getDuration() +{ + Guard< Mutex > aGuard( maMutex ); + return maDuration; +} + +// XAnimationNode +void SAL_CALL RandomAnimationNode::setDuration( const Any& _duration ) +{ + Guard< Mutex > aGuard( maMutex ); + maDuration = _duration; +} + +// XAnimationNode +Any SAL_CALL RandomAnimationNode::getEnd() +{ + Guard< Mutex > aGuard( maMutex ); + return maEnd; +} + +// XAnimationNode +void SAL_CALL RandomAnimationNode::setEnd( const Any& _end ) +{ + Guard< Mutex > aGuard( maMutex ); + maEnd = _end; +} + +// XAnimationNode +Any SAL_CALL RandomAnimationNode::getEndSync() +{ + Guard< Mutex > aGuard( maMutex ); + return maEndSync; +} + +// XAnimationNode +void SAL_CALL RandomAnimationNode::setEndSync( const Any& _endsync ) +{ + Guard< Mutex > aGuard( maMutex ); + maEndSync = _endsync; +} + +// XAnimationNode +Any SAL_CALL RandomAnimationNode::getRepeatCount() +{ + Guard< Mutex > aGuard( maMutex ); + return maRepeatCount; +} + +// XAnimationNode +void SAL_CALL RandomAnimationNode::setRepeatCount( const Any& _repeatcount ) +{ + Guard< Mutex > aGuard( maMutex ); + maRepeatCount = _repeatcount; +} + +// XAnimationNode +Any SAL_CALL RandomAnimationNode::getRepeatDuration() +{ + Guard< Mutex > aGuard( maMutex ); + return maRepeatDuration; +} + +// XAnimationNode +void SAL_CALL RandomAnimationNode::setRepeatDuration( const Any& _repeatduration ) +{ + Guard< Mutex > aGuard( maMutex ); + maRepeatDuration = _repeatduration; +} + +// XAnimationNode +sal_Int16 SAL_CALL RandomAnimationNode::getFill() +{ + Guard< Mutex > aGuard( maMutex ); + return mnFill; +} + +// XAnimationNode +void SAL_CALL RandomAnimationNode::setFill( sal_Int16 _fill ) +{ + Guard< Mutex > aGuard( maMutex ); + mnFill = _fill; +} + +// XAnimationNode +sal_Int16 SAL_CALL RandomAnimationNode::getFillDefault() +{ + Guard< Mutex > aGuard( maMutex ); + return mnFillDefault; +} + +// XAnimationNode +void SAL_CALL RandomAnimationNode::setFillDefault( sal_Int16 _filldefault ) +{ + Guard< Mutex > aGuard( maMutex ); + mnFillDefault = _filldefault; +} + +// XAnimationNode +sal_Int16 SAL_CALL RandomAnimationNode::getRestart() +{ + Guard< Mutex > aGuard( maMutex ); + return mnRestart; +} + +// XAnimationNode +void SAL_CALL RandomAnimationNode::setRestart( sal_Int16 _restart ) +{ + Guard< Mutex > aGuard( maMutex ); + mnRestart = _restart; +} + +// XAnimationNode +sal_Int16 SAL_CALL RandomAnimationNode::getRestartDefault() +{ + Guard< Mutex > aGuard( maMutex ); + return mnRestartDefault; +} + +// XAnimationNode +void SAL_CALL RandomAnimationNode::setRestartDefault( sal_Int16 _restartdefault ) +{ + Guard< Mutex > aGuard( maMutex ); + mnRestartDefault = _restartdefault; +} + +// XAnimationNode +double SAL_CALL RandomAnimationNode::getAcceleration() +{ + Guard< Mutex > aGuard( maMutex ); + return mfAcceleration; +} + +// XAnimationNode +void SAL_CALL RandomAnimationNode::setAcceleration( double _acceleration ) +{ + Guard< Mutex > aGuard( maMutex ); + mfAcceleration = _acceleration; +} + +// XAnimationNode +double SAL_CALL RandomAnimationNode::getDecelerate() +{ + Guard< Mutex > aGuard( maMutex ); + return mfDecelerate; +} + +// XAnimationNode +void SAL_CALL RandomAnimationNode::setDecelerate( double _decelerate ) +{ + Guard< Mutex > aGuard( maMutex ); + mfDecelerate = _decelerate; +} + +// XAnimationNode +sal_Bool SAL_CALL RandomAnimationNode::getAutoReverse() +{ + Guard< Mutex > aGuard( maMutex ); + return mbAutoReverse; +} + +// XAnimationNode +void SAL_CALL RandomAnimationNode::setAutoReverse( sal_Bool _autoreverse ) +{ + Guard< Mutex > aGuard( maMutex ); + mbAutoReverse = _autoreverse; +} + +Sequence< NamedValue > SAL_CALL RandomAnimationNode::getUserData() +{ + Guard< Mutex > aGuard( maMutex ); + return maUserData; +} + +void SAL_CALL RandomAnimationNode::setUserData( const Sequence< NamedValue >& _userdata ) +{ + Guard< Mutex > aGuard( maMutex ); + maUserData = _userdata; +} + +// XChild +Reference< XInterface > SAL_CALL RandomAnimationNode::getParent() +{ + Guard< Mutex > aGuard( maMutex ); + return mxParent.get(); +} + +// XChild +void SAL_CALL RandomAnimationNode::setParent( const Reference< XInterface >& Parent ) +{ + Guard< Mutex > aGuard( maMutex ); + mxParent = Parent; +} + +// XCloneable +Reference< XCloneable > SAL_CALL RandomAnimationNode::createClone() +{ + Reference< XCloneable > xNewNode( new RandomAnimationNode( *this ) ); + return xNewNode; +} + +// XElementAccess +Type SAL_CALL RandomAnimationNode::getElementType() +{ + return cppu::UnoType<XAnimationNode>::get(); +} + +// XElementAccess +sal_Bool SAL_CALL RandomAnimationNode::hasElements() +{ + return true; +} + +// XEnumerationAccess +Reference< XEnumeration > SAL_CALL RandomAnimationNode::createEnumeration() +{ + Guard< Mutex > aGuard( maMutex ); + + if( !maTarget.hasValue() && mxFirstNode.is() ) + { + Any aTarget( mxFirstNode->getTarget() ); + if( aTarget.hasValue() ) + { + maTarget = aTarget; + mxFirstNode.clear(); + } + } + + Reference< XEnumeration > xEnum; + + Reference< XEnumerationAccess > aEnumAccess( CustomAnimationPresets::getCustomAnimationPresets().getRandomPreset( mnPresetClass ), UNO_QUERY ); + + if( aEnumAccess.is() ) + { + Reference< XEnumeration > xEnumeration = aEnumAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY ); + if( xAnimate.is() ) + xAnimate->setTarget( maTarget ); + } + } + xEnum = aEnumAccess->createEnumeration(); + } + else + { + // no presets? give empty node! + Reference< XParallelTimeContainer > xTimeContainer = ParallelTimeContainer::create( comphelper::getProcessComponentContext() ); + xEnum = xTimeContainer->createEnumeration(); + } + + return xEnum; +} + +// XTimeContainer +Reference< XAnimationNode > SAL_CALL RandomAnimationNode::insertBefore( const Reference< XAnimationNode >& newChild, const Reference< XAnimationNode >& ) +{ + return appendChild( newChild ); +} + +// XTimeContainer +Reference< XAnimationNode > SAL_CALL RandomAnimationNode::insertAfter( const Reference< XAnimationNode >& newChild, const Reference< XAnimationNode >& ) +{ + return appendChild( newChild ); +} + +// XTimeContainer +Reference< XAnimationNode > SAL_CALL RandomAnimationNode::replaceChild( const Reference< XAnimationNode >& newChild, const Reference< XAnimationNode >& ) +{ + return appendChild( newChild ); +} + +// XTimeContainer +Reference< XAnimationNode > SAL_CALL RandomAnimationNode::removeChild( const Reference< XAnimationNode >& oldChild ) +{ + return oldChild; +} + +// XTimeContainer +Reference< XAnimationNode > SAL_CALL RandomAnimationNode::appendChild( const Reference< XAnimationNode >& newChild ) +{ + Reference< XAnimate > xAnimate( newChild, UNO_QUERY ); + if( xAnimate.is() ) + { + Any aTarget( xAnimate->getTarget() ); + if( aTarget.hasValue() ) + maTarget = aTarget; + } + + if( !maTarget.hasValue() && !mxFirstNode.is() ) + mxFirstNode = xAnimate; + + return newChild; +} + +// XServiceInfo +OUString RandomAnimationNode::getImplementationName() +{ + return "sd::RandomAnimationNode" ; +} + +// XServiceInfo +sal_Bool RandomAnimationNode::supportsService(const OUString& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +// XServiceInfo +Sequence< OUString > RandomAnimationNode::getSupportedServiceNames() +{ + return { "com.sun.star.animations.ParallelTimeContainer", "com.sun.star.comp.sd.RandomAnimationNode" }; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +RandomAnimationNode_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new sd::RandomAnimationNode()); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/sddetect.cxx b/sd/source/ui/unoidl/sddetect.cxx new file mode 100644 index 000000000..cfa50d141 --- /dev/null +++ b/sd/source/ui/unoidl/sddetect.cxx @@ -0,0 +1,160 @@ +/* -*- 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 "sddetect.hxx" + +#include <com/sun/star/beans/PropertyValue.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/ucb/ContentCreationException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <vcl/graphicfilter.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/fcontnr.hxx> +#include <vcl/FilterConfigItem.hxx> +#include <sot/storage.hxx> +#include <unotools/mediadescriptor.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::task; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using utl::MediaDescriptor; + +SdFilterDetect::SdFilterDetect() +{ +} + +SdFilterDetect::~SdFilterDetect() +{ +} + +OUString SAL_CALL SdFilterDetect::detect( Sequence< beans::PropertyValue >& lDescriptor ) +{ + MediaDescriptor aMediaDesc( lDescriptor ); + OUString aTypeName = aMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_TYPENAME, OUString() ); + uno::Reference< io::XInputStream > xInStream ( aMediaDesc[MediaDescriptor::PROP_INPUTSTREAM], uno::UNO_QUERY ); + if ( !xInStream.is() ) + return OUString(); + + SfxMedium aMedium; + aMedium.UseInteractionHandler( false ); + aMedium.setStreamToLoadFrom( xInStream, true ); + + SvStream *pInStrm = aMedium.GetInStream(); + if ( !pInStrm || pInStrm->GetError() ) + return OUString(); + + if ( aTypeName.startsWith( "impress_MS_PowerPoint_97" ) ) + { + // Do not attempt to create an SotStorage on a + // 0-length stream as that would create the compound + // document header on the stream and effectively write to + // disk! + pInStrm->Seek( STREAM_SEEK_TO_BEGIN ); + if ( pInStrm->remainingSize() == 0 ) + return OUString(); + + try + { + tools::SvRef<SotStorage> aStorage = new SotStorage( pInStrm, false ); + if ( !aStorage->GetError() && aStorage->IsStream( "PowerPoint Document" ) ) + return aTypeName; + } + catch (const css::ucb::ContentCreationException&) + { + } + } + else + { + pInStrm->Seek( STREAM_SEEK_TO_BEGIN ); + + const OUString aFileName( aMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_URL, OUString() ) ); + GraphicDescriptor aDesc( *pInStrm, &aFileName ); + if( !aDesc.Detect() ) + { + INetURLObject aCheckURL( aFileName ); + if( aCheckURL.getExtension().equalsIgnoreAsciiCase("cgm") ) + { + sal_uInt8 n8; + pInStrm->Seek( STREAM_SEEK_TO_BEGIN ); + pInStrm->ReadUChar( n8 ); + if ( ( n8 & 0xf0 ) == 0 ) + // we are supporting binary cgm format only, so + // this is a small test to exclude cgm text + return "impress_CGM_Computer_Graphics_Metafile"; + } + } + else + { + OUString aShortName( GraphicDescriptor::GetImportFormatShortName( aDesc.GetFileFormat() ) ); + GraphicFilter &rGrfFilter = GraphicFilter::GetGraphicFilter(); + const OUString aName( rGrfFilter.GetImportFormatTypeName( rGrfFilter.GetImportFormatNumberForShortName( aShortName ) ) ); + + if ( aShortName.equalsIgnoreAsciiCase( "PCD" ) ) // there is a multiple pcd selection possible + { + sal_Int32 nBase = 2; // default Base0 + if ( aTypeName == "pcd_Photo_CD_Base4" ) + nBase = 1; + else if ( aTypeName == "pcd_Photo_CD_Base16" ) + nBase = 0; + FilterConfigItem aFilterConfigItem( u"Office.Common/Filter/Graphic/Import/PCD" ); + aFilterConfigItem.WriteInt32( "Resolution" , nBase ); + } + + SfxFilterMatcher aMatch("sdraw"); + std::shared_ptr<const SfxFilter> pFilter = aMatch.GetFilter4FilterName( aName ); + if ( pFilter ) + return pFilter->GetRealTypeName(); + } + } + + return OUString(); +} + +// XServiceInfo +OUString SAL_CALL SdFilterDetect::getImplementationName() +{ + return "com.sun.star.comp.draw.FormatDetector"; +} + +// XServiceInfo +sal_Bool SAL_CALL SdFilterDetect::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +// XServiceInfo +Sequence< OUString > SAL_CALL SdFilterDetect::getSupportedServiceNames() +{ + return { "com.sun.star.frame.ExtendedTypeDetection" }; +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_draw_FormatDetector_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SdFilterDetect()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/sddetect.hxx b/sd/source/ui/unoidl/sddetect.hxx new file mode 100644 index 000000000..3d22cc12c --- /dev/null +++ b/sd/source/ui/unoidl/sddetect.hxx @@ -0,0 +1,48 @@ +/* -*- 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 . + */ + +#pragma once + +#include <rtl/ustring.hxx> +#include <com/sun/star/document/XExtendedFilterDetection.hpp> +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/lang/XServiceInfo.hpp> + + +namespace com::sun::star::beans { struct PropertyValue; } + +class SdFilterDetect : public ::cppu::WeakImplHelper< css::document::XExtendedFilterDetection, css::lang::XServiceInfo > +{ +public: + SdFilterDetect(); + virtual ~SdFilterDetect() override; + + // XServiceInfo + + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& sServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XExtendedFilterDetect + + virtual OUString SAL_CALL detect( css::uno::Sequence< css::beans::PropertyValue >& lDescriptor ) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unocpres.cxx b/sd/source/ui/unoidl/unocpres.cxx new file mode 100644 index 000000000..cc57b1e79 --- /dev/null +++ b/sd/source/ui/unoidl/unocpres.cxx @@ -0,0 +1,450 @@ +/* -*- 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 <algorithm> + +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <o3tl/safeint.hxx> +#include <vcl/svapp.hxx> +#include <svx/svdpage.hxx> +#include <cppuhelper/supportsservice.hxx> + +#include <createunocustomshow.hxx> +#include <unomodel.hxx> +#include <drawdoc.hxx> +#include "unocpres.hxx" +#include <cusshow.hxx> +#include <unopage.hxx> +#include <customshowlist.hxx> + +using namespace ::com::sun::star; + +uno::Reference< uno::XInterface > createUnoCustomShow( SdCustomShow* pShow ) +{ + return static_cast<cppu::OWeakObject*>(new SdXCustomPresentation( pShow )); +} + +SdXCustomPresentation::SdXCustomPresentation() noexcept +: mpSdCustomShow(nullptr), mpModel(nullptr), + aDisposeListeners( aDisposeContainerMutex ), + bDisposing( false ) +{ +} + +SdXCustomPresentation::SdXCustomPresentation( SdCustomShow* pShow) noexcept +: mpSdCustomShow(pShow), mpModel(nullptr), + aDisposeListeners( aDisposeContainerMutex ), + bDisposing( false ) +{ +} + +SdXCustomPresentation::~SdXCustomPresentation() noexcept +{ +} + +UNO3_GETIMPLEMENTATION_IMPL( SdXCustomPresentation ); + +// XServiceInfo +OUString SAL_CALL SdXCustomPresentation::getImplementationName() +{ + return "SdXCustomPresentation" ; +} + +sal_Bool SAL_CALL SdXCustomPresentation::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +uno::Sequence< OUString > SAL_CALL SdXCustomPresentation::getSupportedServiceNames() +{ + return { "com.sun.star.presentation.CustomPresentation" }; +} + +// XIndexContainer +void SAL_CALL SdXCustomPresentation::insertByIndex( sal_Int32 Index, const uno::Any& Element ) +{ + SolarMutexGuard aGuard; + + if( bDisposing ) + throw lang::DisposedException(); + + if( Index < 0 || o3tl::make_unsigned(Index) > ( mpSdCustomShow ? mpSdCustomShow->PagesVector().size() : 0 ) ) + throw lang::IndexOutOfBoundsException(); + + uno::Reference< drawing::XDrawPage > xPage; + Element >>= xPage; + + if(!xPage.is()) + throw lang::IllegalArgumentException(); + + SdDrawPage* pPage = comphelper::getFromUnoTunnel<SdDrawPage>( xPage ); + + if(pPage) + { + if( nullptr == mpModel ) + mpModel = pPage->GetModel(); + + if( nullptr != mpModel && nullptr == mpSdCustomShow && mpModel->GetDoc() ) + mpSdCustomShow = new SdCustomShow; + + mpSdCustomShow->PagesVector().insert(mpSdCustomShow->PagesVector().begin() + Index, + static_cast<SdPage*>(pPage->GetSdrPage())); + } + + if( mpModel ) + mpModel->SetModified(); +} + +void SAL_CALL SdXCustomPresentation::removeByIndex( sal_Int32 Index ) +{ + SolarMutexGuard aGuard; + + if( bDisposing ) + throw lang::DisposedException(); + + if(mpSdCustomShow) + { + uno::Reference< drawing::XDrawPage > xPage; + getByIndex( Index ) >>= xPage; + + if( xPage.is() ) + { + SvxDrawPage* pPage = comphelper::getFromUnoTunnel<SvxDrawPage>( xPage ); + if(pPage) + { + SdCustomShow::PageVec::iterator it = std::find( + mpSdCustomShow->PagesVector().begin(), + mpSdCustomShow->PagesVector().end(), + pPage->GetSdrPage()); + if (it != mpSdCustomShow->PagesVector().end()) + mpSdCustomShow->PagesVector().erase(it); + } + } + } + + if( mpModel ) + mpModel->SetModified(); +} + +// XIndexReplace +void SAL_CALL SdXCustomPresentation::replaceByIndex( sal_Int32 Index, const uno::Any& Element ) +{ + removeByIndex( Index ); + insertByIndex( Index, Element ); +} + +// XElementAccess +uno::Type SAL_CALL SdXCustomPresentation::getElementType() +{ + return cppu::UnoType<drawing::XDrawPage>::get(); +} + +sal_Bool SAL_CALL SdXCustomPresentation::hasElements() +{ + SolarMutexGuard aGuard; + + if( bDisposing ) + throw lang::DisposedException(); + + return getCount() > 0; +} + +// XIndexAccess +sal_Int32 SAL_CALL SdXCustomPresentation::getCount() +{ + SolarMutexGuard aGuard; + if( bDisposing ) + throw lang::DisposedException(); + + return mpSdCustomShow ? mpSdCustomShow->PagesVector().size() : 0; +} + +uno::Any SAL_CALL SdXCustomPresentation::getByIndex( sal_Int32 Index ) +{ + SolarMutexGuard aGuard; + + if( bDisposing ) + throw lang::DisposedException(); + + if (Index < 0 || !mpSdCustomShow || o3tl::make_unsigned(Index) >= mpSdCustomShow->PagesVector().size()) + throw lang::IndexOutOfBoundsException(); + + uno::Any aAny; + SdrPage * pPage = const_cast<SdPage *>(mpSdCustomShow->PagesVector()[Index]); + + if( pPage ) + { + uno::Reference< drawing::XDrawPage > xRef( pPage->getUnoPage(), uno::UNO_QUERY ); + aAny <<= xRef; + } + + return aAny; +} + +// XNamed +OUString SAL_CALL SdXCustomPresentation::getName() +{ + SolarMutexGuard aGuard; + + if( bDisposing ) + throw lang::DisposedException(); + + if(mpSdCustomShow) + return mpSdCustomShow->GetName(); + + return OUString(); +} + +void SAL_CALL SdXCustomPresentation::setName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + + if( bDisposing ) + throw lang::DisposedException(); + + if(mpSdCustomShow) + mpSdCustomShow->SetName( aName ); +} + +// XComponent +void SAL_CALL SdXCustomPresentation::dispose() +{ + SolarMutexGuard aGuard; + + if( bDisposing ) + return; // caught a recursion + + bDisposing = true; + + uno::Reference< uno::XInterface > xSource( static_cast<cppu::OWeakObject*>(this) ); + + lang::EventObject aEvt; + aEvt.Source = xSource; + aDisposeListeners.disposeAndClear(aEvt); + + mpSdCustomShow = nullptr; +} + +void SAL_CALL SdXCustomPresentation::addEventListener( const uno::Reference< lang::XEventListener >& xListener ) +{ + if( bDisposing ) + throw lang::DisposedException(); + + aDisposeListeners.addInterface(xListener); +} + +void SAL_CALL SdXCustomPresentation::removeEventListener( const uno::Reference< lang::XEventListener >& aListener ) +{ + if( !bDisposing ) + aDisposeListeners.removeInterface(aListener); +} + +/*===========================================================================* + * class SdXCustomPresentationAccess : public XCustomPresentationAccess, * + * public UsrObject * + *===========================================================================*/ + +SdXCustomPresentationAccess::SdXCustomPresentationAccess(SdXImpressDocument& rMyModel) noexcept +: mrModel(rMyModel) +{ +} + +SdXCustomPresentationAccess::~SdXCustomPresentationAccess() noexcept +{ +} + +// XServiceInfo +OUString SAL_CALL SdXCustomPresentationAccess::getImplementationName() +{ + return "SdXCustomPresentationAccess"; +} + +sal_Bool SAL_CALL SdXCustomPresentationAccess::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +uno::Sequence< OUString > SAL_CALL SdXCustomPresentationAccess::getSupportedServiceNames() +{ + return { "com.sun.star.presentation.CustomPresentationAccess" }; +} + +// XSingleServiceFactory +uno::Reference< uno::XInterface > SAL_CALL SdXCustomPresentationAccess::createInstance() +{ + uno::Reference< uno::XInterface > xRef( static_cast<cppu::OWeakObject*>(new SdXCustomPresentation()) ); + return xRef; +} + +uno::Reference< uno::XInterface > SAL_CALL SdXCustomPresentationAccess::createInstanceWithArguments( const uno::Sequence< uno::Any >& ) +{ + return createInstance(); +} + +// XNameContainer +void SAL_CALL SdXCustomPresentationAccess::insertByName( const OUString& aName, const uno::Any& aElement ) +{ + SolarMutexGuard aGuard; + + // get the documents custom show list + SdCustomShowList* pList = nullptr; + if(mrModel.GetDoc()) + pList = mrModel.GetDoc()->GetCustomShowList(true); + + // no list, no cookies + if( nullptr == pList) + throw uno::RuntimeException(); + + // do we have a container::XIndexContainer? + SdXCustomPresentation* pXShow = nullptr; + + uno::Reference< container::XIndexContainer > xContainer; + if( (aElement >>= xContainer) && xContainer.is() ) + pXShow = comphelper::getFromUnoTunnel<SdXCustomPresentation>(xContainer); + + if( nullptr == pXShow ) + throw lang::IllegalArgumentException(); + + // get the internal custom show from the api wrapper + SdCustomShow* pShow = pXShow->GetSdCustomShow(); + if( nullptr == pShow ) + { + pShow = new SdCustomShow( xContainer ); + pXShow->SetSdCustomShow( pShow ); + } + else + { + if( nullptr == pXShow->GetModel() || *pXShow->GetModel() != mrModel ) + throw lang::IllegalArgumentException(); + } + + // give it a name + pShow->SetName( aName); + + // check if this or another customshow with the same name already exists + for( SdCustomShow* pCompare = pList->First(); + pCompare; + pCompare = pList->Next() ) + { + if( pCompare == pShow || pCompare->GetName() == pShow->GetName() ) + throw container::ElementExistException(); + } + + pList->push_back(std::unique_ptr<SdCustomShow>(pShow)); + + mrModel.SetModified(); +} + +void SAL_CALL SdXCustomPresentationAccess::removeByName( const OUString& Name ) +{ + SolarMutexGuard aGuard; + + SdCustomShow* pShow = getSdCustomShow(Name); + + SdCustomShowList* pList = GetCustomShowList(); + if(!pList || !pShow) + throw container::NoSuchElementException(); + + pList->erase( pShow ); + + mrModel.SetModified(); +} + +// XNameReplace +void SAL_CALL SdXCustomPresentationAccess::replaceByName( const OUString& aName, const uno::Any& aElement ) +{ + removeByName( aName ); + insertByName( aName, aElement ); +} + +// XNameAccess +uno::Any SAL_CALL SdXCustomPresentationAccess::getByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + + SdCustomShow* pShow = getSdCustomShow(aName); + if(!pShow) + { + throw container::NoSuchElementException(); + } + + uno::Reference< container::XIndexContainer > xRef( pShow->getUnoCustomShow(), uno::UNO_QUERY ); + return uno::Any(xRef); +} + +uno::Sequence< OUString > SAL_CALL SdXCustomPresentationAccess::getElementNames() +{ + SolarMutexGuard aGuard; + + SdCustomShowList* pList = GetCustomShowList(); + const sal_uInt32 nCount = pList ? pList->size() : 0; + + uno::Sequence< OUString > aSequence( nCount ); + OUString* pStringList = aSequence.getArray(); + + sal_uInt32 nIdx = 0; + while( nIdx < nCount ) + { + const SdCustomShow* pShow = (*pList)[nIdx].get(); + pStringList[nIdx] = pShow->GetName(); + nIdx++; + } + + return aSequence; +} + +sal_Bool SAL_CALL SdXCustomPresentationAccess::hasByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + return getSdCustomShow(aName) != nullptr; +} + +// XElementAccess +uno::Type SAL_CALL SdXCustomPresentationAccess::getElementType() +{ + return cppu::UnoType<container::XIndexContainer>::get(); +} + +sal_Bool SAL_CALL SdXCustomPresentationAccess::hasElements() +{ + SolarMutexGuard aGuard; + + SdCustomShowList* pList = GetCustomShowList(); + return pList && !pList->empty(); +} + +SdCustomShow * SdXCustomPresentationAccess::getSdCustomShow( std::u16string_view rName ) const noexcept +{ + sal_uInt32 nIdx = 0; + + SdCustomShowList* pList = GetCustomShowList(); + const sal_uInt32 nCount = pList ? pList->size() : 0; + + while( nIdx < nCount ) + { + SdCustomShow* pShow = (*pList)[nIdx].get(); + if( pShow->GetName() == rName ) + return pShow; + nIdx++; + } + return nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unocpres.hxx b/sd/source/ui/unoidl/unocpres.hxx new file mode 100644 index 000000000..e32dce956 --- /dev/null +++ b/sd/source/ui/unoidl/unocpres.hxx @@ -0,0 +1,147 @@ +/* -*- 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 . + */ +#pragma once + +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/container/XIndexContainer.hpp> +#include <com/sun/star/container/XNamed.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <comphelper/interfacecontainer3.hxx> +#include <osl/mutex.hxx> + +#include <cppuhelper/implbase.hxx> +#include <comphelper/servicehelper.hxx> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> + +#include <unomodel.hxx> +#include <drawdoc.hxx> + + +class SdCustomShow; + +class SdXCustomPresentation : public ::cppu::WeakImplHelper< css::container::XIndexContainer, + css::container::XNamed, + css::lang::XUnoTunnel, + css::lang::XComponent, + css::lang::XServiceInfo > +{ +private: + SdCustomShow* mpSdCustomShow; + SdXImpressDocument* mpModel; + + // for xComponent + ::osl::Mutex aDisposeContainerMutex; + ::comphelper::OInterfaceContainerHelper3<css::lang::XEventListener> aDisposeListeners; + bool bDisposing; + +public: + SdXCustomPresentation() noexcept; + explicit SdXCustomPresentation( SdCustomShow* mpSdCustomShow ) noexcept; + virtual ~SdXCustomPresentation() noexcept override; + + // internal + SdCustomShow* GetSdCustomShow() const noexcept { return mpSdCustomShow; } + void SetSdCustomShow( SdCustomShow* pShow ) noexcept { mpSdCustomShow = pShow; } + SdXImpressDocument* GetModel() const noexcept { return mpModel; } + + // uno helper + UNO3_GETIMPLEMENTATION_DECL(SdXCustomPresentation) + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XIndexContainer + virtual void SAL_CALL insertByIndex( sal_Int32 Index, const css::uno::Any& Element ) override; + virtual void SAL_CALL removeByIndex( sal_Int32 Index ) override; + + // XIndexReplace + virtual void SAL_CALL replaceByIndex( sal_Int32 Index, const css::uno::Any& Element ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + // XIndexAccess + virtual sal_Int32 SAL_CALL getCount() override ; + virtual css::uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override; + + // XNamed + virtual OUString SAL_CALL getName( ) override; + virtual void SAL_CALL setName( const OUString& aName ) override; + + // XComponent + virtual void SAL_CALL dispose( ) override; + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override; +}; + +class SdXCustomPresentationAccess : public ::cppu::WeakImplHelper< css::container::XNameContainer, + css::lang::XSingleServiceFactory, + css::lang::XServiceInfo > +{ +private: + SdXImpressDocument& mrModel; + + // intern + inline SdCustomShowList* GetCustomShowList() const noexcept; + SdCustomShow * getSdCustomShow( std::u16string_view Name ) const noexcept; + +public: + explicit SdXCustomPresentationAccess(SdXImpressDocument& rMyModel) noexcept; + virtual ~SdXCustomPresentationAccess() noexcept override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XSingleServiceFactory + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstance( ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithArguments( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XNameContainer + virtual void SAL_CALL insertByName( const OUString& aName, const css::uno::Any& aElement ) override; + virtual void SAL_CALL removeByName( const OUString& Name ) override; + + // XNameReplace + virtual void SAL_CALL replaceByName( const OUString& aName, const css::uno::Any& aElement ) override; + + // XNameAccess + virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override; + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; +}; + +inline SdCustomShowList* SdXCustomPresentationAccess::GetCustomShowList() const noexcept +{ + if(mrModel.GetDoc()) + return mrModel.GetDoc()->GetCustomShowList(); + else + return nullptr; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unodoc.cxx b/sd/source/ui/unoidl/unodoc.cxx new file mode 100644 index 000000000..cfb97c186 --- /dev/null +++ b/sd/source/ui/unoidl/unodoc.cxx @@ -0,0 +1,73 @@ +/* -*- 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/sfxmodelfactory.hxx> +#include <com/sun/star/frame/XModel.hpp> + +#include <sddll.hxx> +#include <DrawDocShell.hxx> +#include <GraphicDocShell.hxx> +#include <vcl/svapp.hxx> + +using namespace ::com::sun::star; + +// com.sun.star.comp.Draw.DrawingDocument + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +sd_DrawingDocument_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const& args) +{ + SolarMutexGuard aGuard; + + SdDLL::Init(); + + css::uno::Reference<css::uno::XInterface> xInterface = sfx2::createSfxModelInstance(args, + [](SfxModelFlags _nCreationFlags) + { + SfxObjectShell* pShell = new ::sd::GraphicDocShell( _nCreationFlags ); + return uno::Reference< uno::XInterface >( pShell->GetModel() ); + }); + xInterface->acquire(); + return xInterface.get(); +} + + +// com.sun.star.comp.Draw.PresentationDocument + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +sd_PresentationDocument_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const& args) +{ + SolarMutexGuard aGuard; + + SdDLL::Init(); + + css::uno::Reference<css::uno::XInterface> xInterface = sfx2::createSfxModelInstance(args, + [](SfxModelFlags _nCreationFlags) + { + SfxObjectShell* pShell = + new ::sd::DrawDocShell( + _nCreationFlags, false, DocumentType::Impress ); + return pShell->GetModel(); + }); + xInterface->acquire(); + return xInterface.get(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unolayer.cxx b/sd/source/ui/unoidl/unolayer.cxx new file mode 100644 index 000000000..c6bc3cdf3 --- /dev/null +++ b/sd/source/ui/unoidl/unolayer.cxx @@ -0,0 +1,707 @@ +/* -*- 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 <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> + +#include "unolayer.hxx" + +#include <comphelper/extract.hxx> +#include <editeng/unoipset.hxx> +#include <osl/diagnose.h> +#include <svl/itemprop.hxx> +#include <svx/svdpagv.hxx> +#include <svx/svdobj.hxx> +#include <cppuhelper/supportsservice.hxx> + +// following ones for InsertSdPage() +#include <svx/svdlayer.hxx> + +#include <DrawDocShell.hxx> +#include <drawdoc.hxx> +#include <unomodel.hxx> +#include <unoprnms.hxx> +#include <com/sun/star/lang/NoSupportException.hpp> +#include <svx/svdpool.hxx> +#include <FrameView.hxx> +#include <DrawViewShell.hxx> +#include <View.hxx> +#include <ViewShell.hxx> +#include <strings.hrc> +#include <sdresid.hxx> + +#include "unowcntr.hxx" +#include <vcl/svapp.hxx> + +using namespace ::com::sun::star; + +// class SdLayer +#define WID_LAYER_LOCKED 1 +#define WID_LAYER_PRINTABLE 2 +#define WID_LAYER_VISIBLE 3 +#define WID_LAYER_NAME 4 +#define WID_LAYER_TITLE 5 +#define WID_LAYER_DESC 6 + +static const SvxItemPropertySet* ImplGetSdLayerPropertySet() +{ + static const SfxItemPropertyMapEntry aSdLayerPropertyMap_Impl[] = + { + { u"" UNO_NAME_LAYER_LOCKED, WID_LAYER_LOCKED, cppu::UnoType<bool>::get(), 0, 0 }, + { u"" UNO_NAME_LAYER_PRINTABLE, WID_LAYER_PRINTABLE,cppu::UnoType<bool>::get(), 0, 0 }, + { u"" UNO_NAME_LAYER_VISIBLE, WID_LAYER_VISIBLE, cppu::UnoType<bool>::get(), 0, 0 }, + { u"" UNO_NAME_LAYER_NAME, WID_LAYER_NAME, ::cppu::UnoType<OUString>::get(), 0, 0 }, + { u"Title", WID_LAYER_TITLE, ::cppu::UnoType<OUString>::get(), 0, 0 }, + { u"Description", WID_LAYER_DESC, ::cppu::UnoType<OUString>::get(), 0, 0 }, + { u"", 0, css::uno::Type(), 0, 0 } + }; + static SvxItemPropertySet aSDLayerPropertySet_Impl( aSdLayerPropertyMap_Impl, SdrObject::GetGlobalDrawObjectItemPool() ); + return &aSDLayerPropertySet_Impl; +} + +SdLayer::SdLayer(SdLayerManager* pLayerManager_, SdrLayer* pSdrLayer_) +: mxLayerManager(pLayerManager_) +, pLayer(pSdrLayer_) +, pPropSet(ImplGetSdLayerPropertySet()) +{ + // no defaults possible yet, a "set" would overwrite existing information + // in view, which is currently needed for saving, because pLayer is not updated + // from view. +} + +SdLayer::~SdLayer() noexcept +{ +} + +// uno helper +UNO3_GETIMPLEMENTATION_IMPL( SdLayer ); + +// XServiceInfo +OUString SAL_CALL SdLayer::getImplementationName() +{ + return "SdUnoLayer"; +} + +sal_Bool SAL_CALL SdLayer::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +uno::Sequence< OUString > SAL_CALL SdLayer::getSupportedServiceNames() +{ + return { "com.sun.star.drawing.Layer" }; +} + +// beans::XPropertySet +uno::Reference< beans::XPropertySetInfo > SAL_CALL SdLayer::getPropertySetInfo( ) +{ + SolarMutexGuard aGuard; + return pPropSet->getPropertySetInfo(); +} + +void SAL_CALL SdLayer::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + + if(pLayer == nullptr || mxLayerManager == nullptr) + throw lang::DisposedException(); + + const SfxItemPropertyMapEntry* pEntry = pPropSet->getPropertyMapEntry(aPropertyName); + + switch( pEntry ? pEntry->nWID : -1 ) + { + case WID_LAYER_LOCKED: + { + pLayer->SetLockedODF( cppu::any2bool(aValue) ); + set(LOCKED, cppu::any2bool(aValue)); // changes the View, if any exists + break; + } + case WID_LAYER_PRINTABLE: + { + pLayer->SetPrintableODF( cppu::any2bool(aValue) ); + set(PRINTABLE, cppu::any2bool(aValue)); // changes the View, if any exists + break; + } + case WID_LAYER_VISIBLE: + { + pLayer->SetVisibleODF( cppu::any2bool(aValue) ); + set(VISIBLE, cppu::any2bool(aValue)); // changes the View, if any exists + break; + } + case WID_LAYER_NAME: + { + OUString aName; + if(!(aValue >>= aName)) + throw lang::IllegalArgumentException(); + + pLayer->SetName(aName); + mxLayerManager->UpdateLayerView(); + break; + } + + case WID_LAYER_TITLE: + { + OUString sTitle; + if(!(aValue >>= sTitle)) + throw lang::IllegalArgumentException(); + + pLayer->SetTitle(sTitle); + break; + } + + case WID_LAYER_DESC: + { + OUString sDescription; + if(!(aValue >>= sDescription)) + throw lang::IllegalArgumentException(); + + pLayer->SetDescription(sDescription); + break; + } + + default: + throw beans::UnknownPropertyException( aPropertyName, static_cast<cppu::OWeakObject*>(this)); + } + + if( mxLayerManager->GetDocShell() ) + mxLayerManager->GetDocShell()->SetModified(); +} + +uno::Any SAL_CALL SdLayer::getPropertyValue( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + if(pLayer == nullptr || mxLayerManager == nullptr) + throw lang::DisposedException(); + + const SfxItemPropertyMapEntry* pEntry = pPropSet->getPropertyMapEntry(PropertyName); + + uno::Any aValue; + + switch( pEntry ? pEntry->nWID : -1 ) + { + case WID_LAYER_LOCKED: + aValue <<= get( LOCKED ); + break; + case WID_LAYER_PRINTABLE: + aValue <<= get( PRINTABLE ); + break; + case WID_LAYER_VISIBLE: + aValue <<= get( VISIBLE ); + break; + case WID_LAYER_NAME: + { + OUString aRet(pLayer->GetName()); + aValue <<= aRet; + break; + } + case WID_LAYER_TITLE: + aValue <<= pLayer->GetTitle(); + break; + case WID_LAYER_DESC: + aValue <<= pLayer->GetDescription(); + break; + default: + throw beans::UnknownPropertyException( PropertyName, static_cast<cppu::OWeakObject*>(this)); + } + + return aValue; +} + +void SAL_CALL SdLayer::addPropertyChangeListener( const OUString& , const uno::Reference< beans::XPropertyChangeListener >& ) {} +void SAL_CALL SdLayer::removePropertyChangeListener( const OUString& , const uno::Reference< beans::XPropertyChangeListener >& ) {} +void SAL_CALL SdLayer::addVetoableChangeListener( const OUString& , const uno::Reference< beans::XVetoableChangeListener >& ) {} +void SAL_CALL SdLayer::removeVetoableChangeListener( const OUString& , const uno::Reference< beans::XVetoableChangeListener >& ) {} + +bool SdLayer::get( LayerAttribute what ) noexcept +{ + if(pLayer && mxLayerManager.is()) + { + // Try 1. is an arbitrary page open? + ::sd::View *pView = mxLayerManager->GetView(); + SdrPageView* pSdrPageView = nullptr; + if(pView) + pSdrPageView = pView->GetSdrPageView(); + + if(pSdrPageView) + { + OUString aLayerName = pLayer->GetName(); + switch(what) + { + case VISIBLE: return pSdrPageView->IsLayerVisible(aLayerName); + case PRINTABLE: return pSdrPageView->IsLayerPrintable(aLayerName); + case LOCKED: return pSdrPageView->IsLayerLocked(aLayerName); + } + } + + // Try 2. get info from FrameView + if(mxLayerManager->GetDocShell()) + { + ::sd::FrameView *pFrameView = mxLayerManager->GetDocShell()->GetFrameView(); + if(pFrameView) + switch(what) + { + case VISIBLE: return pFrameView->GetVisibleLayers().IsSet(pLayer->GetID()); + case PRINTABLE: return pFrameView->GetPrintableLayers().IsSet(pLayer->GetID()); + case LOCKED: return pFrameView->GetLockedLayers().IsSet(pLayer->GetID()); + } + } + + // no view at all, e.g. Draw embedded as OLE in text document, ODF default values + switch(what) + { + case VISIBLE: return true; + case PRINTABLE: return true; + case LOCKED: return false; + } + + } + return false; //TODO: uno::Exception? +} + +void SdLayer::set( LayerAttribute what, bool flag ) noexcept +{ + if(!(pLayer && mxLayerManager.is())) + return; + + // Try 1. is an arbitrary page open? + ::sd::View *pView = mxLayerManager->GetView(); + SdrPageView* pSdrPageView = nullptr; + if(pView) + pSdrPageView = pView->GetSdrPageView(); + + if(pSdrPageView) + { + OUString aLayerName(pLayer->GetName()); + switch(what) + { + case VISIBLE: pSdrPageView->SetLayerVisible(aLayerName,flag); + break; + case PRINTABLE: pSdrPageView->SetLayerPrintable(aLayerName,flag); + break; + case LOCKED: pSdrPageView->SetLayerLocked(aLayerName,flag); + break; + } + } + + // Try 2. get info from FrameView + if(!mxLayerManager->GetDocShell()) + return; + + ::sd::FrameView *pFrameView = mxLayerManager->GetDocShell()->GetFrameView(); + + if(!pFrameView) + return; + + SdrLayerIDSet aNewLayers; + switch(what) + { + case VISIBLE: aNewLayers = pFrameView->GetVisibleLayers(); + break; + case PRINTABLE: aNewLayers = pFrameView->GetPrintableLayers(); + break; + case LOCKED: aNewLayers = pFrameView->GetLockedLayers(); + break; + } + + aNewLayers.Set(pLayer->GetID(),flag); + + switch(what) + { + case VISIBLE: pFrameView->SetVisibleLayers(aNewLayers); + break; + case PRINTABLE: pFrameView->SetPrintableLayers(aNewLayers); + break; + case LOCKED: pFrameView->SetLockedLayers(aNewLayers); + break; + } + return; + //TODO: uno::Exception? +} + +// css::container::XChild +uno::Reference<uno::XInterface> SAL_CALL SdLayer::getParent() +{ + SolarMutexGuard aGuard; + + if( !mxLayerManager.is() ) + throw lang::DisposedException(); + + return uno::Reference<uno::XInterface> (static_cast<cppu::OWeakObject*>(mxLayerManager.get()), uno::UNO_QUERY); +} + +void SAL_CALL SdLayer::setParent (const uno::Reference<uno::XInterface >& ) +{ + throw lang::NoSupportException (); +} + +// XComponent +void SAL_CALL SdLayer::dispose( ) +{ + mxLayerManager.clear(); + pLayer = nullptr; +} + +void SAL_CALL SdLayer::addEventListener( const uno::Reference< lang::XEventListener >& ) +{ + OSL_FAIL("not implemented!"); +} + +void SAL_CALL SdLayer::removeEventListener( const uno::Reference< lang::XEventListener >& ) +{ + OSL_FAIL("not implemented!"); +} + +// class SdLayerManager +SdLayerManager::SdLayerManager( SdXImpressDocument& rMyModel ) noexcept +:mpModel( &rMyModel) +{ + mpLayers.reset(new SvUnoWeakContainer); +} + +SdLayerManager::~SdLayerManager() noexcept +{ + dispose(); +} + +// uno helper +UNO3_GETIMPLEMENTATION_IMPL( SdLayerManager ); + +// XComponent +void SAL_CALL SdLayerManager::dispose( ) +{ + mpModel = nullptr; + if( mpLayers ) + { + mpLayers->dispose(); + mpLayers.reset(); + } +} + +void SAL_CALL SdLayerManager::addEventListener( const uno::Reference< lang::XEventListener >& ) +{ + OSL_FAIL("not implemented!"); +} + +void SAL_CALL SdLayerManager::removeEventListener( const uno::Reference< lang::XEventListener >& ) +{ + OSL_FAIL("not implemented!"); +} + +// XServiceInfo +OUString SAL_CALL SdLayerManager::getImplementationName() +{ + return "SdUnoLayerManager"; +} + +sal_Bool SAL_CALL SdLayerManager::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +uno::Sequence< OUString > SAL_CALL SdLayerManager::getSupportedServiceNames() +{ + return {"com.sun.star.drawing.LayerManager"}; +} + +// XLayerManager +uno::Reference< drawing::XLayer > SAL_CALL SdLayerManager::insertNewByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + + if( mpModel == nullptr ) + throw lang::DisposedException(); + + uno::Reference< drawing::XLayer > xLayer; + + if( mpModel->mpDoc ) + { + SdrLayerAdmin& rLayerAdmin = mpModel->mpDoc->GetLayerAdmin(); + sal_uInt16 nLayerCnt = rLayerAdmin.GetLayerCount(); + sal_Int32 nLayer = nLayerCnt - 2 + 1; + OUString aLayerName; + + // Test for existing names + while( aLayerName.isEmpty() || rLayerAdmin.GetLayer( aLayerName ) ) + { + aLayerName = SdResId(STR_LAYER) + OUString::number(nLayer); + ++nLayer; + } + + SdrLayerAdmin& rLA=mpModel->mpDoc->GetLayerAdmin(); + const sal_Int32 nMax=rLA.GetLayerCount(); + if (nIndex>nMax) nIndex=nMax; + xLayer = GetLayer (rLA.NewLayer(aLayerName,static_cast<sal_uInt16>(nIndex))); + mpModel->SetModified(); + } + return xLayer; +} + +void SAL_CALL SdLayerManager::remove( const uno::Reference< drawing::XLayer >& xLayer ) +{ + SolarMutexGuard aGuard; + + if( mpModel == nullptr ) + throw lang::DisposedException(); + + SdLayer* pSdLayer = comphelper::getFromUnoTunnel<SdLayer>(xLayer); + + if(pSdLayer && GetView()) + { + const SdrLayer* pSdrLayer = pSdLayer->GetSdrLayer(); + GetView()->DeleteLayer( pSdrLayer->GetName() ); + + UpdateLayerView(); + } + + mpModel->SetModified(); +} + +void SAL_CALL SdLayerManager::attachShapeToLayer( const uno::Reference< drawing::XShape >& xShape, const uno::Reference< drawing::XLayer >& xLayer ) +{ + SolarMutexGuard aGuard; + + if( mpModel == nullptr ) + throw lang::DisposedException(); + + SdLayer* pSdLayer = comphelper::getFromUnoTunnel<SdLayer>(xLayer); + SdrLayer* pSdrLayer = pSdLayer?pSdLayer->GetSdrLayer():nullptr; + if(pSdrLayer==nullptr) + return; + + SdrObject* pSdrObject = SdrObject::getSdrObjectFromXShape( xShape ); + + if(pSdrObject) + pSdrObject->SetLayer(pSdrLayer->GetID()); + + mpModel->SetModified(); +} + +uno::Reference< drawing::XLayer > SAL_CALL SdLayerManager::getLayerForShape( const uno::Reference< drawing::XShape >& xShape ) +{ + SolarMutexGuard aGuard; + + if( mpModel == nullptr ) + throw lang::DisposedException(); + + uno::Reference< drawing::XLayer > xLayer; + + if(mpModel->mpDoc) + { + SdrObject* pObj = SdrObject::getSdrObjectFromXShape( xShape ); + if(pObj) + { + SdrLayerID aId = pObj->GetLayer(); + SdrLayerAdmin& rLayerAdmin = mpModel->mpDoc->GetLayerAdmin(); + xLayer = GetLayer (rLayerAdmin.GetLayerPerID(aId)); + } + } + return xLayer; +} + +// XIndexAccess +sal_Int32 SAL_CALL SdLayerManager::getCount() +{ + SolarMutexGuard aGuard; + + if( mpModel == nullptr ) + throw lang::DisposedException(); + + if( mpModel->mpDoc ) + { + SdrLayerAdmin& rLayerAdmin = mpModel->mpDoc->GetLayerAdmin(); + return rLayerAdmin.GetLayerCount(); + } + + return 0; +} + +uno::Any SAL_CALL SdLayerManager::getByIndex( sal_Int32 nLayer ) +{ + SolarMutexGuard aGuard; + + if( mpModel == nullptr ) + throw lang::DisposedException(); + + if( nLayer >= getCount() || nLayer < 0 ) + throw lang::IndexOutOfBoundsException(); + + uno::Any aAny; + + if( mpModel->mpDoc ) + { + SdrLayerAdmin& rLayerAdmin = mpModel->mpDoc->GetLayerAdmin(); + uno::Reference<drawing::XLayer> xLayer (GetLayer (rLayerAdmin.GetLayer(static_cast<sal_uInt16>(nLayer)))); + aAny <<= xLayer; + } + return aAny; +} + +// XNameAccess +uno::Any SAL_CALL SdLayerManager::getByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + + if( (mpModel == nullptr) || (mpModel->mpDoc == nullptr ) ) + throw lang::DisposedException(); + + SdrLayerAdmin& rLayerAdmin = mpModel->mpDoc->GetLayerAdmin(); + SdrLayer* pLayer = rLayerAdmin.GetLayer(aName); + if( pLayer == nullptr ) + throw container::NoSuchElementException(); + + return uno::Any( GetLayer (pLayer) ); +} + +uno::Sequence< OUString > SAL_CALL SdLayerManager::getElementNames() +{ + SolarMutexGuard aGuard; + + if( mpModel == nullptr ) + throw lang::DisposedException(); + + SdrLayerAdmin& rLayerAdmin = mpModel->mpDoc->GetLayerAdmin(); + const sal_uInt16 nLayerCount = rLayerAdmin.GetLayerCount(); + + uno::Sequence< OUString > aSeq( nLayerCount ); + + OUString* pStrings = aSeq.getArray(); + + for( sal_uInt16 nLayer = 0; nLayer < nLayerCount; nLayer++ ) + { + SdrLayer* pLayer = rLayerAdmin.GetLayer( nLayer ); + if( pLayer ) + *pStrings++ = pLayer->GetName(); + } + + return aSeq; +} + +sal_Bool SAL_CALL SdLayerManager::hasByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + + if( mpModel == nullptr ) + throw lang::DisposedException(); + + SdrLayerAdmin& rLayerAdmin = mpModel->mpDoc->GetLayerAdmin(); + + return nullptr != rLayerAdmin.GetLayer(aName); +} + +// XElementAccess +uno::Type SAL_CALL SdLayerManager::getElementType() +{ + return cppu::UnoType<drawing::XLayer>::get(); +} + +sal_Bool SAL_CALL SdLayerManager::hasElements() +{ + return getCount() > 0; +} + +/** + * If something was changed at the layers, this methods takes care that the + * changes are made visible in sdbcx::View. + */ +void SdLayerManager::UpdateLayerView() const noexcept +{ + if(!mpModel->mpDocShell) + return; + + ::sd::DrawViewShell* pDrViewSh = dynamic_cast< ::sd::DrawViewShell* >( mpModel->mpDocShell->GetViewShell()); + + if(pDrViewSh) + { + bool bLayerMode = pDrViewSh->IsLayerModeActive(); + pDrViewSh->ChangeEditMode(pDrViewSh->GetEditMode(), !bLayerMode); + pDrViewSh->ChangeEditMode(pDrViewSh->GetEditMode(), bLayerMode); + } + + mpModel->mpDoc->SetChanged(); +} + +/** */ +::sd::View* SdLayerManager::GetView() const noexcept +{ + if( mpModel->mpDocShell ) + { + ::sd::ViewShell* pViewSh = mpModel->mpDocShell->GetViewShell(); + if(pViewSh) + return pViewSh->GetView(); + } + return nullptr; +} + +namespace +{ +/** Compare two pointers to <type>SdrLayer</type> objects. + @param xRef + The implementing SdLayer class provides the first pointer by the + <member>SdLayer::GetSdrLayer</member> method. + @param pSearchData + This void pointer is the second pointer to an <type>SdrLayer</type> + object. + @return + Return </True> if both pointers point to the same object. +*/ +bool compare_layers (const uno::WeakReference<uno::XInterface>& xRef, void const * pSearchData) +{ + uno::Reference<uno::XInterface> xLayer (xRef); + if (xLayer.is()) + { + SdLayer* pSdLayer = comphelper::getFromUnoTunnel<SdLayer> (xRef); + if (pSdLayer != nullptr) + { + SdrLayer* pSdrLayer = pSdLayer->GetSdrLayer (); + if (pSdrLayer == static_cast<SdrLayer const *>(pSearchData)) + return true; + } + } + return false; +} +} + +/** Use the <member>mpLayers</member> container of weak references to either + retrieve and return a previously created <type>XLayer</type> object for + the given <type>SdrLayer</type> object or create and remember a new one. +*/ +uno::Reference<drawing::XLayer> SdLayerManager::GetLayer (SdrLayer* pLayer) +{ + uno::WeakReference<uno::XInterface> xRef; + uno::Reference<drawing::XLayer> xLayer; + + // Search existing xLayer for the given pLayer. + if (mpLayers->findRef (xRef, static_cast<void*>(pLayer), compare_layers)) + xLayer.set(xRef, uno::UNO_QUERY); + + // Create the xLayer if necessary. + if ( ! xLayer.is()) + { + xLayer = new SdLayer (this, pLayer); + + // Remember the new xLayer for future calls. + uno::WeakReference<uno::XInterface> wRef(xLayer); + mpLayers->insert(wRef); + } + + return xLayer; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unolayer.hxx b/sd/source/ui/unoidl/unolayer.hxx new file mode 100644 index 000000000..aa7d4891b --- /dev/null +++ b/sd/source/ui/unoidl/unolayer.hxx @@ -0,0 +1,169 @@ +/* -*- 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 . + */ +#pragma once + +#include <com/sun/star/drawing/XLayer.hpp> +#include <com/sun/star/drawing/XLayerManager.hpp> + +#include <cppuhelper/implbase.hxx> +#include <comphelper/servicehelper.hxx> +#include <rtl/ref.hxx> + +#include <unomodel.hxx> + +class SdrLayer; +class SdLayerManager; +class SvUnoWeakContainer; + +namespace sd { +class View; +} +enum LayerAttribute { VISIBLE, PRINTABLE, LOCKED }; + +/*********************************************************************** +* * +***********************************************************************/ +class SdLayer : public ::cppu::WeakImplHelper< css::drawing::XLayer, + css::lang::XServiceInfo, + css::container::XChild, + css::lang::XUnoTunnel, + css::lang::XComponent > +{ +public: + SdLayer(SdLayerManager* pLayerManager_, SdrLayer* pSdrLayer_); + virtual ~SdLayer() noexcept override; + + // intern + SdrLayer* GetSdrLayer() const noexcept { return pLayer; } + + // uno helper + UNO3_GETIMPLEMENTATION_DECL( SdLayer ) + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // css::beans::XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override; + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override; + virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override; + virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override; + virtual void SAL_CALL addVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + + // css::container::XChild + + /** Returns the layer manager that manages this layer. + */ + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getParent( ) override; + + // XComponent + virtual void SAL_CALL dispose( ) override; + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override; + + /** Not implemented. Always throws an exception. + @throws NoSupportException. + */ + virtual void SAL_CALL setParent( const css::uno::Reference< css::uno::XInterface >& Parent ) override; + +private: + rtl::Reference<SdLayerManager> mxLayerManager; + SdrLayer* pLayer; + const SvxItemPropertySet* pPropSet; + + bool get( LayerAttribute what ) noexcept; + void set( LayerAttribute what, bool flag ) noexcept; + +}; + +/*********************************************************************** +* * +***********************************************************************/ + +class SdLayerManager : public ::cppu::WeakImplHelper< css::drawing::XLayerManager, + css::container::XNameAccess, + css::lang::XServiceInfo, + css::lang::XUnoTunnel, + css::lang::XComponent > +{ + friend class SdLayer; + +public: + explicit SdLayerManager( SdXImpressDocument& rMyModel ) noexcept; + virtual ~SdLayerManager() noexcept override; + + // uno helper + UNO3_GETIMPLEMENTATION_DECL( SdLayerManager ) + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XLayerManager + virtual css::uno::Reference< css::drawing::XLayer > SAL_CALL insertNewByIndex( sal_Int32 nIndex ) override; + virtual void SAL_CALL remove( const css::uno::Reference< css::drawing::XLayer >& xLayer ) override; + virtual void SAL_CALL attachShapeToLayer( const css::uno::Reference< css::drawing::XShape >& xShape, const css::uno::Reference< css::drawing::XLayer >& xLayer ) override; + virtual css::uno::Reference< css::drawing::XLayer > SAL_CALL getLayerForShape( const css::uno::Reference< css::drawing::XShape >& xShape ) override; + + // XIndexAccess + virtual sal_Int32 SAL_CALL getCount() override ; + virtual css::uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override; + + // XNameAccess + virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override; + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + /** Return the <type>XLayer</type> object that is associated with the + given <type>SdrLayer</type> object. If the requested object does + not yet exist it is created. All calls with the same argument + return the same object. + @param pLayer + The <type>SdrLayer</type> object for which to return the + associated <type>XLayer</type> object. + @return + The returned value is the unique <type>XLayer</type> object + associated with the specified argument. If no layer can be + created for the argument than an empty reference is returned. + */ + css::uno::Reference< css::drawing::XLayer> GetLayer (SdrLayer* pLayer); + + // XComponent + virtual void SAL_CALL dispose( ) override; + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override; + +private: + SdXImpressDocument* mpModel; + std::unique_ptr<SvUnoWeakContainer> mpLayers; + + ::sd::View* GetView() const noexcept; + ::sd::DrawDocShell* GetDocShell() const noexcept { return mpModel->mpDocShell; } + void UpdateLayerView() const noexcept; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unomodel.cxx b/sd/source/ui/unoidl/unomodel.cxx new file mode 100644 index 000000000..758ce1380 --- /dev/null +++ b/sd/source/ui/unoidl/unomodel.cxx @@ -0,0 +1,3491 @@ +/* -*- 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 <memory> + +#include <com/sun/star/presentation/XPresentation2.hpp> + +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/ServiceNotRegisteredException.hpp> +#include <com/sun/star/lang/Locale.hpp> +#include <com/sun/star/awt/XDevice.hpp> +#include <com/sun/star/document/IndexedPropertyValues.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> + +#include <com/sun/star/embed/Aspects.hpp> + +#include <officecfg/Office/Common.hxx> +#include <comphelper/indexedpropertyvalues.hxx> +#include <comphelper/lok.hxx> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/profilezone.hxx> + +#include <sal/log.hxx> +#include <editeng/unofield.hxx> +#include <notifydocumentevent.hxx> +#include <tpaction.hxx> +#include <unomodel.hxx> +#include "unopool.hxx" +#include <sfx2/lokhelper.hxx> +#include <sfx2/dispatch.hxx> +#include <vcl/svapp.hxx> +#include <LibreOfficeKit/LibreOfficeKitEnums.h> + +#include <editeng/UnoForbiddenCharsTable.hxx> +#include <svx/svdoutl.hxx> +#include <o3tl/safeint.hxx> +#include <o3tl/string_view.hxx> +#include <o3tl/unit_conversion.hxx> +#include <svx/UnoNamespaceMap.hxx> +#include <svx/svdlayer.hxx> +#include <svx/svdsob.hxx> +#include <svx/svdundo.hxx> +#include <svx/unoapi.hxx> +#include <svx/unofill.hxx> +#include <svx/sdrpagewindow.hxx> +#include <svx/sdrpaintwindow.hxx> +#include <editeng/fontitem.hxx> +#include <toolkit/awt/vclxdevice.hxx> +#include <svx/svdpool.hxx> +#include <svx/svdpagv.hxx> +#include <svtools/unoimap.hxx> +#include <svtools/slidesorterbaropt.hxx> +#include <svx/unoshape.hxx> +#include <editeng/unonrule.hxx> +#include <editeng/eeitem.hxx> +#include <unotools/datetime.hxx> +#include <xmloff/autolayout.hxx> + +// Support creation of GraphicStorageHandler and EmbeddedObjectResolver +#include <svx/xmleohlp.hxx> +#include <svx/xmlgrhlp.hxx> +#include <DrawDocShell.hxx> +#include <ViewShellBase.hxx> +#include "UnoDocumentSettings.hxx" + +#include <Annotation.hxx> +#include <drawdoc.hxx> +#include <sdmod.hxx> +#include <sdresid.hxx> +#include <sdpage.hxx> + +#include <strings.hrc> +#include <strings.hxx> +#include "unolayer.hxx" +#include <unopage.hxx> +#include "unocpres.hxx" +#include "unoobj.hxx" +#include <stlpool.hxx> +#include "unopback.hxx" +#include <unokywds.hxx> + +#include <FrameView.hxx> +#include <ClientView.hxx> +#include <DrawViewShell.hxx> +#include <ViewShell.hxx> +#include <Window.hxx> +#include <optsitem.hxx> + +#include <vcl/pdfextoutdevdata.hxx> +#include <com/sun/star/presentation/AnimationSpeed.hpp> +#include <com/sun/star/presentation/ClickAction.hpp> +#include <svx/sdr/contact/viewobjectcontact.hxx> +#include <svx/sdr/contact/viewcontact.hxx> +#include <svx/sdr/contact/displayinfo.hxx> + +#include <com/sun/star/office/XAnnotation.hpp> +#include <com/sun/star/office/XAnnotationAccess.hpp> +#include <com/sun/star/office/XAnnotationEnumeration.hpp> +#include <com/sun/star/geometry/RealPoint2D.hpp> +#include <com/sun/star/util/DateTime.hpp> + +#include <drawinglayer/primitive2d/structuretagprimitive2d.hxx> + +#include <sfx2/lokcomponenthelpers.hxx> +#include <tools/gen.hxx> +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> +#include <tools/json_writer.hxx> +#include <tools/UnitConversion.hxx> +#include <svx/ColorSets.hxx> + +#include <app.hrc> + +using namespace ::cppu; +using namespace ::com::sun::star; +using namespace ::sd; + +TranslateId SdTPAction::GetClickActionSdResId( presentation::ClickAction eCA ) +{ + switch( eCA ) + { + case presentation::ClickAction_NONE: return STR_CLICK_ACTION_NONE; + case presentation::ClickAction_PREVPAGE: return STR_CLICK_ACTION_PREVPAGE; + case presentation::ClickAction_NEXTPAGE: return STR_CLICK_ACTION_NEXTPAGE; + case presentation::ClickAction_FIRSTPAGE: return STR_CLICK_ACTION_FIRSTPAGE; + case presentation::ClickAction_LASTPAGE: return STR_CLICK_ACTION_LASTPAGE; + case presentation::ClickAction_BOOKMARK: return STR_CLICK_ACTION_BOOKMARK; + case presentation::ClickAction_DOCUMENT: return STR_CLICK_ACTION_DOCUMENT; + case presentation::ClickAction_PROGRAM: return STR_CLICK_ACTION_PROGRAM; + case presentation::ClickAction_MACRO: return STR_CLICK_ACTION_MACRO; + case presentation::ClickAction_SOUND: return STR_CLICK_ACTION_SOUND; + case presentation::ClickAction_VERB: return STR_CLICK_ACTION_VERB; + case presentation::ClickAction_STOPPRESENTATION: return STR_CLICK_ACTION_STOPPRESENTATION; + default: OSL_FAIL( "No StringResource for ClickAction available!" ); + } + return {}; +} + +namespace { + +class SdUnoForbiddenCharsTable : public SvxUnoForbiddenCharsTable, + public SfxListener +{ +public: + explicit SdUnoForbiddenCharsTable(SdrModel* pModel); + virtual ~SdUnoForbiddenCharsTable() override; + + // SfxListener + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) noexcept override; +protected: + virtual void onChange() override; + +private: + SdrModel* mpModel; +}; + +} + +SdUnoForbiddenCharsTable::SdUnoForbiddenCharsTable( SdrModel* pModel ) +: SvxUnoForbiddenCharsTable( pModel->GetForbiddenCharsTable() ), mpModel( pModel ) +{ + StartListening( *pModel ); +} + +void SdUnoForbiddenCharsTable::onChange() +{ + if( mpModel ) + { + mpModel->ReformatAllTextObjects(); + } +} + +SdUnoForbiddenCharsTable::~SdUnoForbiddenCharsTable() +{ + SolarMutexGuard g; + + if( mpModel ) + EndListening( *mpModel ); +} + +void SdUnoForbiddenCharsTable::Notify( SfxBroadcaster&, const SfxHint& rHint ) noexcept +{ + if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint) + return; + const SdrHint* pSdrHint = static_cast<const SdrHint*>( &rHint ); + if( SdrHintKind::ModelCleared == pSdrHint->GetKind() ) + { + mpModel = nullptr; + } +} + +const sal_uInt16 WID_MODEL_LANGUAGE = 1; +const sal_uInt16 WID_MODEL_TABSTOP = 2; +const sal_uInt16 WID_MODEL_VISAREA = 3; +const sal_uInt16 WID_MODEL_MAPUNIT = 4; +const sal_uInt16 WID_MODEL_FORBCHARS = 5; +const sal_uInt16 WID_MODEL_CONTFOCUS = 6; +const sal_uInt16 WID_MODEL_DSGNMODE = 7; +const sal_uInt16 WID_MODEL_BASICLIBS = 8; +const sal_uInt16 WID_MODEL_RUNTIMEUID = 9; +const sal_uInt16 WID_MODEL_BUILDID = 10; +const sal_uInt16 WID_MODEL_HASVALIDSIGNATURES = 11; +const sal_uInt16 WID_MODEL_DIALOGLIBS = 12; +const sal_uInt16 WID_MODEL_FONTS = 13; +const sal_uInt16 WID_MODEL_INTEROPGRABBAG = 14; +const sal_uInt16 WID_MODEL_THEME = 15; + +static const SvxItemPropertySet* ImplGetDrawModelPropertySet() +{ + // Attention: the first parameter HAS TO BE sorted!!! + const static SfxItemPropertyMapEntry aDrawModelPropertyMap_Impl[] = + { + { u"BuildId", WID_MODEL_BUILDID, ::cppu::UnoType<OUString>::get(), 0, 0}, + { sUNO_Prop_CharLocale, WID_MODEL_LANGUAGE, ::cppu::UnoType<lang::Locale>::get(), 0, 0}, + { sUNO_Prop_TabStop, WID_MODEL_TABSTOP, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, + { sUNO_Prop_VisibleArea, WID_MODEL_VISAREA, ::cppu::UnoType<awt::Rectangle>::get(), 0, 0}, + { sUNO_Prop_MapUnit, WID_MODEL_MAPUNIT, ::cppu::UnoType<sal_Int16>::get(), beans::PropertyAttribute::READONLY, 0}, + { sUNO_Prop_ForbiddenCharacters, WID_MODEL_FORBCHARS, cppu::UnoType<i18n::XForbiddenCharacters>::get(), beans::PropertyAttribute::READONLY, 0}, + { sUNO_Prop_AutomContFocus, WID_MODEL_CONTFOCUS, cppu::UnoType<bool>::get(), 0, 0}, + { sUNO_Prop_ApplyFrmDsgnMode, WID_MODEL_DSGNMODE, cppu::UnoType<bool>::get(), 0, 0}, + { u"BasicLibraries", WID_MODEL_BASICLIBS, cppu::UnoType<script::XLibraryContainer>::get(), beans::PropertyAttribute::READONLY, 0}, + { u"DialogLibraries", WID_MODEL_DIALOGLIBS, cppu::UnoType<script::XLibraryContainer>::get(), beans::PropertyAttribute::READONLY, 0}, + { sUNO_Prop_RuntimeUID, WID_MODEL_RUNTIMEUID, ::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0}, + { sUNO_Prop_HasValidSignatures, WID_MODEL_HASVALIDSIGNATURES, ::cppu::UnoType<sal_Bool>::get(), beans::PropertyAttribute::READONLY, 0}, + { u"Fonts", WID_MODEL_FONTS, cppu::UnoType<uno::Sequence<uno::Any>>::get(), beans::PropertyAttribute::READONLY, 0}, + { sUNO_Prop_InteropGrabBag, WID_MODEL_INTEROPGRABBAG, cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get(), 0, 0}, + { sUNO_Prop_Theme, WID_MODEL_THEME, cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get(), 0, 0}, + { u"", 0, css::uno::Type(), 0, 0 } + }; + static SvxItemPropertySet aDrawModelPropertySet_Impl( aDrawModelPropertyMap_Impl, SdrObject::GetGlobalDrawObjectItemPool() ); + return &aDrawModelPropertySet_Impl; +} + +// this ctor is used from the DocShell +SdXImpressDocument::SdXImpressDocument(::sd::DrawDocShell* pShell, bool bClipBoard) +: SfxBaseModel( pShell ), + mpDocShell( pShell ), + mpDoc( pShell ? pShell->GetDoc() : nullptr ), + mbDisposed(false), + mbImpressDoc( pShell && pShell->GetDoc() && pShell->GetDoc()->GetDocumentType() == DocumentType::Impress ), + mbClipBoard( bClipBoard ), + mpPropSet( ImplGetDrawModelPropertySet() ) +{ + if( mpDoc ) + { + StartListening( *mpDoc ); + } + else + { + OSL_FAIL("DocShell is invalid"); + } +} + +SdXImpressDocument::SdXImpressDocument(SdDrawDocument* pDoc, bool bClipBoard) +: SfxBaseModel( nullptr ), + mpDocShell( nullptr ), + mpDoc( pDoc ), + mbDisposed(false), + mbImpressDoc( pDoc && pDoc->GetDocumentType() == DocumentType::Impress ), + mbClipBoard( bClipBoard ), + mpPropSet( ImplGetDrawModelPropertySet() ) +{ + if( mpDoc ) + { + StartListening( *mpDoc ); + } + else + { + OSL_FAIL("SdDrawDocument is invalid"); + } +} + +/*********************************************************************** +* * +***********************************************************************/ +SdXImpressDocument::~SdXImpressDocument() noexcept +{ +} + +// XInterface +uno::Any SAL_CALL SdXImpressDocument::queryInterface( const uno::Type & rType ) +{ + uno::Any aAny; + + if (rType == cppu::UnoType<lang::XServiceInfo>::get()) + aAny <<= uno::Reference<lang::XServiceInfo>(this); + else if (rType == cppu::UnoType<beans::XPropertySet>::get()) + aAny <<= uno::Reference<beans::XPropertySet>(this); + else if (rType == cppu::UnoType<lang::XMultiServiceFactory>::get()) + aAny <<= uno::Reference<lang::XMultiServiceFactory>(this); + else if (rType == cppu::UnoType<drawing::XDrawPageDuplicator>::get()) + aAny <<= uno::Reference<drawing::XDrawPageDuplicator>(this); + else if (rType == cppu::UnoType<drawing::XLayerSupplier>::get()) + aAny <<= uno::Reference<drawing::XLayerSupplier>(this); + else if (rType == cppu::UnoType<drawing::XMasterPagesSupplier>::get()) + aAny <<= uno::Reference<drawing::XMasterPagesSupplier>(this); + else if (rType == cppu::UnoType<drawing::XDrawPagesSupplier>::get()) + aAny <<= uno::Reference<drawing::XDrawPagesSupplier>(this); + else if (rType == cppu::UnoType<presentation::XHandoutMasterSupplier>::get()) + aAny <<= uno::Reference<presentation::XHandoutMasterSupplier>(this); + else if (rType == cppu::UnoType<document::XLinkTargetSupplier>::get()) + aAny <<= uno::Reference<document::XLinkTargetSupplier>(this); + else if (rType == cppu::UnoType<style::XStyleFamiliesSupplier>::get()) + aAny <<= uno::Reference<style::XStyleFamiliesSupplier>(this); + else if (rType == cppu::UnoType<css::ucb::XAnyCompareFactory>::get()) + aAny <<= uno::Reference<css::ucb::XAnyCompareFactory>(this); + else if (rType == cppu::UnoType<view::XRenderable>::get()) + aAny <<= uno::Reference<view::XRenderable>(this); + else if (mbImpressDoc && rType == cppu::UnoType<presentation::XPresentationSupplier>::get()) + aAny <<= uno::Reference< presentation::XPresentationSupplier >(this); + else if (mbImpressDoc && rType == cppu::UnoType<presentation::XCustomPresentationSupplier>::get()) + aAny <<= uno::Reference< presentation::XCustomPresentationSupplier >(this); + else + return SfxBaseModel::queryInterface(rType); + + return aAny; +} + +void SAL_CALL SdXImpressDocument::acquire() noexcept +{ + SfxBaseModel::acquire(); +} + +void SAL_CALL SdXImpressDocument::release() noexcept +{ + if (osl_atomic_decrement( &m_refCount ) != 0) + return; + + // restore reference count: + osl_atomic_increment( &m_refCount ); + if(!mbDisposed) + { + try + { + dispose(); + } + catch (const uno::RuntimeException&) + { + // don't break throw () + TOOLS_WARN_EXCEPTION( "sd", "" ); + } + } + SfxBaseModel::release(); +} + +// XUnoTunnel +const css::uno::Sequence< sal_Int8 > & SdXImpressDocument::getUnoTunnelId() noexcept +{ + static const comphelper::UnoIdInit theSdXImpressDocumentUnoTunnelId; + return theSdXImpressDocumentUnoTunnelId.getSeq(); +} + +sal_Int64 SAL_CALL SdXImpressDocument::getSomething( const css::uno::Sequence< sal_Int8 >& rIdentifier ) +{ + if (comphelper::isUnoTunnelId<SdrModel>(rIdentifier)) + return comphelper::getSomething_cast(mpDoc); + + return comphelper::getSomethingImpl(rIdentifier, this, + comphelper::FallbackToGetSomethingOf<SfxBaseModel>{}); +} + +// XTypeProvider +uno::Sequence< uno::Type > SAL_CALL SdXImpressDocument::getTypes( ) +{ + ::SolarMutexGuard aGuard; + + if( !maTypeSequence.hasElements() ) + { + uno::Sequence< uno::Type > aTypes( SfxBaseModel::getTypes() ); + aTypes = comphelper::concatSequences(aTypes, + uno::Sequence { + cppu::UnoType<beans::XPropertySet>::get(), + cppu::UnoType<lang::XServiceInfo>::get(), + cppu::UnoType<lang::XMultiServiceFactory>::get(), + cppu::UnoType<drawing::XDrawPageDuplicator>::get(), + cppu::UnoType<drawing::XLayerSupplier>::get(), + cppu::UnoType<drawing::XMasterPagesSupplier>::get(), + cppu::UnoType<drawing::XDrawPagesSupplier>::get(), + cppu::UnoType<document::XLinkTargetSupplier>::get(), + cppu::UnoType<style::XStyleFamiliesSupplier>::get(), + cppu::UnoType<css::ucb::XAnyCompareFactory>::get(), + cppu::UnoType<view::XRenderable>::get() }); + if( mbImpressDoc ) + { + aTypes = comphelper::concatSequences(aTypes, + uno::Sequence { + cppu::UnoType<presentation::XPresentationSupplier>::get(), + cppu::UnoType<presentation::XCustomPresentationSupplier>::get(), + cppu::UnoType<presentation::XHandoutMasterSupplier>::get() }); + } + maTypeSequence = aTypes; + } + + return maTypeSequence; +} + +uno::Sequence< sal_Int8 > SAL_CALL SdXImpressDocument::getImplementationId( ) +{ + return css::uno::Sequence<sal_Int8>(); +} + +/*********************************************************************** +* * +***********************************************************************/ +void SdXImpressDocument::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) +{ + if( mpDoc ) + { + if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint) + { + const SdrHint* pSdrHint = static_cast<const SdrHint*>( &rHint ); + if( hasEventListeners() ) + { + document::EventObject aEvent; + if( SvxUnoDrawMSFactory::createEvent( mpDoc, pSdrHint, aEvent ) ) + notifyEvent( aEvent ); + } + + if( pSdrHint->GetKind() == SdrHintKind::ModelCleared ) + { + if( mpDoc ) + EndListening( *mpDoc ); + mpDoc = nullptr; + mpDocShell = nullptr; + } + } + else + { + // did our SdDrawDocument just died? + if(rHint.GetId() == SfxHintId::Dying) + { + // yes, so we ask for a new one + if( mpDocShell ) + { + SdDrawDocument *pNewDoc = mpDocShell->GetDoc(); + + // is there a new one? + if( pNewDoc != mpDoc ) + { + mpDoc = pNewDoc; + if(mpDoc) + StartListening( *mpDoc ); + } + } + } + } + } + SfxBaseModel::Notify( rBC, rHint ); +} + +/****************************************************************************** +* * +******************************************************************************/ +SdPage* SdXImpressDocument::InsertSdPage( sal_uInt16 nPage, bool bDuplicate ) +{ + sal_uInt16 nPageCount = mpDoc->GetSdPageCount( PageKind::Standard ); + SdrLayerAdmin& rLayerAdmin = mpDoc->GetLayerAdmin(); + SdrLayerID aBckgrnd = rLayerAdmin.GetLayerID(sUNO_LayerName_background); + SdrLayerID aBckgrndObj = rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects); + + rtl::Reference<SdPage> pStandardPage; + + if( 0 == nPageCount ) + { + // this is only used for clipboard where we only have one page + pStandardPage = mpDoc->AllocSdPage(false); + + Size aDefSize(21000, 29700); // A4 portrait orientation + pStandardPage->SetSize( aDefSize ); + mpDoc->InsertPage(pStandardPage.get(), 0); + } + else + { + // here we determine the page after which we should insert + SdPage* pPreviousStandardPage = mpDoc->GetSdPage( std::min( static_cast<sal_uInt16>(nPageCount - 1), nPage ), PageKind::Standard ); + SdrLayerIDSet aVisibleLayers = pPreviousStandardPage->TRG_GetMasterPageVisibleLayers(); + bool bIsPageBack = aVisibleLayers.IsSet( aBckgrnd ); + bool bIsPageObj = aVisibleLayers.IsSet( aBckgrndObj ); + + // AutoLayouts must be ready + mpDoc->StopWorkStartupDelay(); + + /* First we create a standard page and then a notes page. It is + guaranteed, that after a standard page the corresponding notes page + follows. */ + + sal_uInt16 nStandardPageNum = pPreviousStandardPage->GetPageNum() + 2; + SdPage* pPreviousNotesPage = static_cast<SdPage*>( mpDoc->GetPage( nStandardPageNum - 1 ) ); + sal_uInt16 nNotesPageNum = nStandardPageNum + 1; + + /************************************************************** + * standard page + **************************************************************/ + if( bDuplicate ) + pStandardPage = static_cast<SdPage*>( pPreviousStandardPage->CloneSdrPage(*mpDoc).get() ); + else + pStandardPage = mpDoc->AllocSdPage(false); + + pStandardPage->SetSize( pPreviousStandardPage->GetSize() ); + pStandardPage->SetBorder( pPreviousStandardPage->GetLeftBorder(), + pPreviousStandardPage->GetUpperBorder(), + pPreviousStandardPage->GetRightBorder(), + pPreviousStandardPage->GetLowerBorder() ); + pStandardPage->SetOrientation( pPreviousStandardPage->GetOrientation() ); + pStandardPage->SetName(OUString()); + + // insert page after current page + mpDoc->InsertPage(pStandardPage.get(), nStandardPageNum); + + if( !bDuplicate ) + { + // use MasterPage of the current page + pStandardPage->TRG_SetMasterPage(pPreviousStandardPage->TRG_GetMasterPage()); + pStandardPage->SetLayoutName( pPreviousStandardPage->GetLayoutName() ); + pStandardPage->SetAutoLayout(AUTOLAYOUT_NONE, true ); + } + + aBckgrnd = rLayerAdmin.GetLayerID(sUNO_LayerName_background); + aBckgrndObj = rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects); + aVisibleLayers.Set(aBckgrnd, bIsPageBack); + aVisibleLayers.Set(aBckgrndObj, bIsPageObj); + pStandardPage->TRG_SetMasterPageVisibleLayers(aVisibleLayers); + + /************************************************************** + * notes page + **************************************************************/ + rtl::Reference<SdPage> pNotesPage; + + if( bDuplicate ) + pNotesPage = static_cast<SdPage*>( pPreviousNotesPage->CloneSdrPage(*mpDoc).get() ); + else + pNotesPage = mpDoc->AllocSdPage(false); + + pNotesPage->SetSize( pPreviousNotesPage->GetSize() ); + pNotesPage->SetBorder( pPreviousNotesPage->GetLeftBorder(), + pPreviousNotesPage->GetUpperBorder(), + pPreviousNotesPage->GetRightBorder(), + pPreviousNotesPage->GetLowerBorder() ); + pNotesPage->SetOrientation( pPreviousNotesPage->GetOrientation() ); + pNotesPage->SetName(OUString()); + pNotesPage->SetPageKind(PageKind::Notes); + + // insert page after current page + mpDoc->InsertPage(pNotesPage.get(), nNotesPageNum); + + if( !bDuplicate ) + { + // use MasterPage of the current page + pNotesPage->TRG_SetMasterPage(pPreviousNotesPage->TRG_GetMasterPage()); + pNotesPage->SetLayoutName( pPreviousNotesPage->GetLayoutName() ); + pNotesPage->SetAutoLayout(AUTOLAYOUT_NOTES, true ); + } + } + + SetModified(); + + return pStandardPage.get(); +} + +void SdXImpressDocument::SetModified() noexcept +{ + if( mpDoc ) + mpDoc->SetChanged(); +} + +// XModel +void SAL_CALL SdXImpressDocument::lockControllers( ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + mpDoc->setLock(true); +} + +void SAL_CALL SdXImpressDocument::unlockControllers( ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + if( mpDoc->isLocked() ) + { + mpDoc->setLock(false); + } +} + +sal_Bool SAL_CALL SdXImpressDocument::hasControllersLocked( ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + return mpDoc->isLocked(); +} + +uno::Reference < container::XIndexAccess > SAL_CALL SdXImpressDocument::getViewData() +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + uno::Reference < container::XIndexAccess > xRet( SfxBaseModel::getViewData() ); + + if( !xRet.is() ) + { + const std::vector<std::unique_ptr<sd::FrameView>> &rList = mpDoc->GetFrameViewList(); + + if( !rList.empty() ) + { + xRet = new comphelper::IndexedPropertyValuesContainer(); + + uno::Reference < container::XIndexContainer > xCont( xRet, uno::UNO_QUERY ); + DBG_ASSERT( xCont.is(), "SdXImpressDocument::getViewData() failed for OLE object" ); + if( xCont.is() ) + { + for( sal_uInt32 i = 0, n = rList.size(); i < n; i++ ) + { + ::sd::FrameView* pFrameView = rList[ i ].get(); + + uno::Sequence< beans::PropertyValue > aSeq; + pFrameView->WriteUserDataSequence( aSeq ); + xCont->insertByIndex( i, uno::Any( aSeq ) ); + } + } + } + } + + return xRet; +} + +void SAL_CALL SdXImpressDocument::setViewData( const uno::Reference < container::XIndexAccess >& xData ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + SfxBaseModel::setViewData( xData ); + if( !(mpDocShell && (mpDocShell->GetCreateMode() == SfxObjectCreateMode::EMBEDDED) && xData.is()) ) + return; + + const sal_Int32 nCount = xData->getCount(); + + std::vector<std::unique_ptr<sd::FrameView>> &rViews = mpDoc->GetFrameViewList(); + + rViews.clear(); + + uno::Sequence< beans::PropertyValue > aSeq; + for( sal_Int32 nIndex = 0; nIndex < nCount; nIndex++ ) + { + if( xData->getByIndex( nIndex ) >>= aSeq ) + { + std::unique_ptr<::sd::FrameView> pFrameView(new ::sd::FrameView( mpDoc )); + pFrameView->ReadUserDataSequence( aSeq ); + rViews.push_back( std::move(pFrameView) ); + } + } +} + +// XDrawPageDuplicator +uno::Reference< drawing::XDrawPage > SAL_CALL SdXImpressDocument::duplicate( const uno::Reference< drawing::XDrawPage >& xPage ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + // get pPage from xPage and determine the Id (nPos ) afterwards + SvxDrawPage* pSvxPage = comphelper::getFromUnoTunnel<SvxDrawPage>( xPage ); + if( pSvxPage ) + { + SdPage* pPage = static_cast<SdPage*>( pSvxPage->GetSdrPage() ); + sal_uInt16 nPos = pPage->GetPageNum(); + nPos = ( nPos - 1 ) / 2; + pPage = InsertSdPage( nPos, true ); + if( pPage ) + { + uno::Reference< drawing::XDrawPage > xDrawPage( pPage->getUnoPage(), uno::UNO_QUERY ); + return xDrawPage; + } + } + + uno::Reference< drawing::XDrawPage > xDrawPage; + return xDrawPage; +} + +// XDrawPagesSupplier +uno::Reference< drawing::XDrawPages > SAL_CALL SdXImpressDocument::getDrawPages() +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + uno::Reference< drawing::XDrawPages > xDrawPages( mxDrawPagesAccess ); + + if( !xDrawPages.is() ) + { + initializeDocument(); + mxDrawPagesAccess = xDrawPages = new SdDrawPagesAccess(*this); + } + + return xDrawPages; +} + +// XMasterPagesSupplier +uno::Reference< drawing::XDrawPages > SAL_CALL SdXImpressDocument::getMasterPages() +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + uno::Reference< drawing::XDrawPages > xMasterPages( mxMasterPagesAccess ); + + if( !xMasterPages.is() ) + { + if ( !hasControllersLocked() ) + initializeDocument(); + mxMasterPagesAccess = xMasterPages = new SdMasterPagesAccess(*this); + } + + return xMasterPages; +} + +// XLayerManagerSupplier +uno::Reference< container::XNameAccess > SAL_CALL SdXImpressDocument::getLayerManager( ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + uno::Reference< container::XNameAccess > xLayerManager( mxLayerManager ); + + if( !xLayerManager.is() ) + mxLayerManager = xLayerManager = new SdLayerManager(*this); + + return xLayerManager; +} + +// XCustomPresentationSupplier +uno::Reference< container::XNameContainer > SAL_CALL SdXImpressDocument::getCustomPresentations() +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + uno::Reference< container::XNameContainer > xCustomPres( mxCustomPresentationAccess ); + + if( !xCustomPres.is() ) + mxCustomPresentationAccess = xCustomPres = new SdXCustomPresentationAccess(*this); + + return xCustomPres; +} + +// XPresentationSupplier +uno::Reference< presentation::XPresentation > SAL_CALL SdXImpressDocument::getPresentation() +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + return mpDoc->getPresentation(); +} + +// XHandoutMasterSupplier +uno::Reference< drawing::XDrawPage > SAL_CALL SdXImpressDocument::getHandoutMasterPage() +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + uno::Reference< drawing::XDrawPage > xPage; + + initializeDocument(); + SdPage* pPage = mpDoc->GetMasterSdPage(0, PageKind::Handout); + if (pPage) + xPage.set(pPage->getUnoPage(), uno::UNO_QUERY); + return xPage; +} + +// XMultiServiceFactory ( SvxFmMSFactory ) + +css::uno::Reference<css::uno::XInterface> SdXImpressDocument::create( + OUString const & aServiceSpecifier, OUString const & referer) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + if( aServiceSpecifier == "com.sun.star.drawing.DashTable" ) + { + if( !mxDashTable.is() ) + mxDashTable = SvxUnoDashTable_createInstance( mpDoc ); + + return mxDashTable; + } + if( aServiceSpecifier == "com.sun.star.drawing.GradientTable" ) + { + if( !mxGradientTable.is() ) + mxGradientTable = SvxUnoGradientTable_createInstance( mpDoc ); + + return mxGradientTable; + } + if( aServiceSpecifier == "com.sun.star.drawing.HatchTable" ) + { + if( !mxHatchTable.is() ) + mxHatchTable = SvxUnoHatchTable_createInstance( mpDoc ); + + return mxHatchTable; + } + if( aServiceSpecifier == "com.sun.star.drawing.BitmapTable" ) + { + if( !mxBitmapTable.is() ) + mxBitmapTable = SvxUnoBitmapTable_createInstance( mpDoc ); + + return mxBitmapTable; + } + if( aServiceSpecifier == "com.sun.star.drawing.TransparencyGradientTable" ) + { + if( !mxTransGradientTable.is() ) + mxTransGradientTable = SvxUnoTransGradientTable_createInstance( mpDoc ); + + return mxTransGradientTable; + } + if( aServiceSpecifier == "com.sun.star.drawing.MarkerTable" ) + { + if( !mxMarkerTable.is() ) + mxMarkerTable = SvxUnoMarkerTable_createInstance( mpDoc ); + + return mxMarkerTable; + } + if( aServiceSpecifier == "com.sun.star.text.NumberingRules" ) + { + return uno::Reference< uno::XInterface >( SvxCreateNumRule( mpDoc ), uno::UNO_QUERY ); + } + if( aServiceSpecifier == "com.sun.star.drawing.Background" ) + { + return uno::Reference< uno::XInterface >( + static_cast<uno::XWeak*>(new SdUnoPageBackground( mpDoc ))); + } + + if( aServiceSpecifier == "com.sun.star.drawing.Defaults" ) + { + if( !mxDrawingPool.is() ) + mxDrawingPool = SdUnoCreatePool( mpDoc ); + + return mxDrawingPool; + + } + + if ( aServiceSpecifier == sUNO_Service_ImageMapRectangleObject ) + { + return SvUnoImageMapRectangleObject_createInstance( ImplGetSupportedMacroItems() ); + } + + if ( aServiceSpecifier == sUNO_Service_ImageMapCircleObject ) + { + return SvUnoImageMapCircleObject_createInstance( ImplGetSupportedMacroItems() ); + } + + if ( aServiceSpecifier == sUNO_Service_ImageMapPolygonObject ) + { + return SvUnoImageMapPolygonObject_createInstance( ImplGetSupportedMacroItems() ); + } + + if( aServiceSpecifier == "com.sun.star.document.Settings" || + ( !mbImpressDoc && ( aServiceSpecifier == "com.sun.star.drawing.DocumentSettings" ) ) || + ( mbImpressDoc && ( aServiceSpecifier == "com.sun.star.presentation.DocumentSettings" ) ) ) + { + return sd::DocumentSettings_createInstance( this ); + } + + if( aServiceSpecifier == "com.sun.star.text.TextField.DateTime" || + aServiceSpecifier == "com.sun.star.text.textfield.DateTime" ) + { + return static_cast<cppu::OWeakObject *>(new SvxUnoTextField( text::textfield::Type::DATE )); + } + + if( aServiceSpecifier == "com.sun.star.presentation.TextField.Header" || + aServiceSpecifier == "com.sun.star.presentation.textfield.Header" ) + { + return static_cast<cppu::OWeakObject *>(new SvxUnoTextField( text::textfield::Type::PRESENTATION_HEADER )); + } + + if( aServiceSpecifier == "com.sun.star.presentation.TextField.Footer" || + aServiceSpecifier == "com.sun.star.presentation.textfield.Footer" ) + { + return static_cast<cppu::OWeakObject *>(new SvxUnoTextField( text::textfield::Type::PRESENTATION_FOOTER )); + } + + if( aServiceSpecifier == "com.sun.star.presentation.TextField.DateTime" || + aServiceSpecifier == "com.sun.star.presentation.textfield.DateTime" ) + { + return static_cast<cppu::OWeakObject *>(new SvxUnoTextField( text::textfield::Type::PRESENTATION_DATE_TIME )); + } + + if( aServiceSpecifier == "com.sun.star.text.TextField.PageName" || + aServiceSpecifier == "com.sun.star.text.textfield.PageName" ) + { + return static_cast<cppu::OWeakObject *>(new SvxUnoTextField( text::textfield::Type::PAGE_NAME )); + } + + if (aServiceSpecifier == "com.sun.star.text.TextField.DocInfo.Custom" || + aServiceSpecifier == "com.sun.star.text.textfield.DocInfo.Custom") + { + return static_cast<cppu::OWeakObject *>(new SvxUnoTextField(text::textfield::Type::DOCINFO_CUSTOM)); + } + + if( aServiceSpecifier == "com.sun.star.xml.NamespaceMap" ) + { + static sal_uInt16 aWhichIds[] = { SDRATTR_XMLATTRIBUTES, EE_CHAR_XMLATTRIBS, EE_PARA_XMLATTRIBS, 0 }; + + return svx::NamespaceMap_createInstance( aWhichIds, &mpDoc->GetItemPool() ); + } + + // Support creation of GraphicStorageHandler and EmbeddedObjectResolver + if (aServiceSpecifier == "com.sun.star.document.ExportGraphicStorageHandler") + { + return static_cast<cppu::OWeakObject *>(new SvXMLGraphicHelper( SvXMLGraphicHelperMode::Write )); + } + + if (aServiceSpecifier == "com.sun.star.document.ImportGraphicStorageHandler") + { + return static_cast<cppu::OWeakObject *>(new SvXMLGraphicHelper( SvXMLGraphicHelperMode::Read )); + } + + if( aServiceSpecifier == "com.sun.star.document.ExportEmbeddedObjectResolver" ) + { + comphelper::IEmbeddedHelper* pPersist = mpDoc->GetPersist(); + if( nullptr == pPersist ) + throw lang::DisposedException(); + + return static_cast<cppu::OWeakObject *>(new SvXMLEmbeddedObjectHelper( *pPersist, SvXMLEmbeddedObjectHelperMode::Write )); + } + + if( aServiceSpecifier == "com.sun.star.document.ImportEmbeddedObjectResolver" ) + { + comphelper::IEmbeddedHelper* pPersist = mpDoc->GetPersist(); + if( nullptr == pPersist ) + throw lang::DisposedException(); + + return static_cast<cppu::OWeakObject *>(new SvXMLEmbeddedObjectHelper( *pPersist, SvXMLEmbeddedObjectHelperMode::Read )); + } + + uno::Reference< uno::XInterface > xRet; + + if( aServiceSpecifier.startsWith( "com.sun.star.presentation.") ) + { + const std::u16string_view aType( aServiceSpecifier.subView(26) ); + rtl::Reference<SvxShape> pShape; + + SdrObjKind nType = SdrObjKind::Text; + // create a shape wrapper + if( o3tl::starts_with(aType, u"TitleTextShape" ) ) + { + nType = SdrObjKind::Text; + } + else if( o3tl::starts_with(aType, u"OutlinerShape" ) ) + { + nType = SdrObjKind::Text; + } + else if( o3tl::starts_with(aType, u"SubtitleShape" ) ) + { + nType = SdrObjKind::Text; + } + else if( o3tl::starts_with(aType, u"GraphicObjectShape" ) ) + { + nType = SdrObjKind::Graphic; + } + else if( o3tl::starts_with(aType, u"PageShape" ) ) + { + nType = SdrObjKind::Page; + } + else if( o3tl::starts_with(aType, u"OLE2Shape" ) ) + { + nType = SdrObjKind::OLE2; + } + else if( o3tl::starts_with(aType, u"ChartShape" ) ) + { + nType = SdrObjKind::OLE2; + } + else if( o3tl::starts_with(aType, u"CalcShape" ) ) + { + nType = SdrObjKind::OLE2; + } + else if( o3tl::starts_with(aType, u"TableShape" ) ) + { + nType = SdrObjKind::Table; + } + else if( o3tl::starts_with(aType, u"OrgChartShape" ) ) + { + nType = SdrObjKind::OLE2; + } + else if( o3tl::starts_with(aType, u"NotesShape" ) ) + { + nType = SdrObjKind::Text; + } + else if( o3tl::starts_with(aType, u"HandoutShape" ) ) + { + nType = SdrObjKind::Page; + } + else if( o3tl::starts_with(aType, u"FooterShape" ) ) + { + nType = SdrObjKind::Text; + } + else if( o3tl::starts_with(aType, u"HeaderShape" ) ) + { + nType = SdrObjKind::Text; + } + else if( o3tl::starts_with(aType, u"SlideNumberShape" ) ) + { + nType = SdrObjKind::Text; + } + else if( o3tl::starts_with(aType, u"DateTimeShape" ) ) + { + nType = SdrObjKind::Text; + } + else if( o3tl::starts_with(aType, u"MediaShape" ) ) + { + nType = SdrObjKind::Media; + } + else + { + throw lang::ServiceNotRegisteredException(); + } + + // create the API wrapper + pShape = CreateSvxShapeByTypeAndInventor( nType, SdrInventor::Default, referer ); + + // set shape type + if( pShape && !mbClipBoard ) + pShape->SetShapeType(aServiceSpecifier); + + xRet = static_cast<uno::XWeak*>(pShape.get()); + } + else if ( aServiceSpecifier == "com.sun.star.drawing.TableShape" ) + { + rtl::Reference<SvxShape> pShape = CreateSvxShapeByTypeAndInventor( SdrObjKind::Table, SdrInventor::Default, referer ); + if( pShape && !mbClipBoard ) + pShape->SetShapeType(aServiceSpecifier); + + xRet = static_cast<uno::XWeak*>(pShape.get()); + } + else + { + xRet = SvxFmMSFactory::createInstance( aServiceSpecifier ); + } + + uno::Reference< drawing::XShape > xShape( xRet, uno::UNO_QUERY ); + SvxShape* pShape = xShape.is() ? comphelper::getFromUnoTunnel<SvxShape>(xShape) : nullptr; + if (pShape) + { + xRet.clear(); + new SdXShape( pShape, this ); + xRet = xShape; + xShape.clear(); + } + + return xRet; +} + +uno::Reference< uno::XInterface > SAL_CALL SdXImpressDocument::createInstance( const OUString& aServiceSpecifier ) +{ + return create(aServiceSpecifier, ""); +} + +css::uno::Reference<css::uno::XInterface> +SdXImpressDocument::createInstanceWithArguments( + OUString const & ServiceSpecifier, + css::uno::Sequence<css::uno::Any> const & Arguments) +{ + OUString arg; + if ((ServiceSpecifier == "com.sun.star.drawing.GraphicObjectShape" + || ServiceSpecifier == "com.sun.star.drawing.MediaShape" + || ServiceSpecifier == "com.sun.star.presentation.MediaShape") + && Arguments.getLength() == 1 && (Arguments[0] >>= arg)) + { + return create(ServiceSpecifier, arg); + } + return SvxFmMSFactory::createInstanceWithArguments( + ServiceSpecifier, Arguments); +} + +uno::Sequence< OUString > SAL_CALL SdXImpressDocument::getAvailableServiceNames() +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + const uno::Sequence< OUString > aSNS_ORG( SvxFmMSFactory::getAvailableServiceNames() ); + + uno::Sequence< OUString > aSNS_Common{ "com.sun.star.drawing.DashTable", + "com.sun.star.drawing.GradientTable", + "com.sun.star.drawing.HatchTable", + "com.sun.star.drawing.BitmapTable", + "com.sun.star.drawing.TransparencyGradientTable", + "com.sun.star.drawing.MarkerTable", + "com.sun.star.text.NumberingRules", + "com.sun.star.drawing.Background", + "com.sun.star.document.Settings", + sUNO_Service_ImageMapRectangleObject, + sUNO_Service_ImageMapCircleObject, + sUNO_Service_ImageMapPolygonObject, + "com.sun.star.xml.NamespaceMap", + + // Support creation of GraphicStorageHandler and EmbeddedObjectResolver + "com.sun.star.document.ExportGraphicStorageHandler", + "com.sun.star.document.ImportGraphicStorageHandler", + "com.sun.star.document.ExportEmbeddedObjectResolver", + "com.sun.star.document.ImportEmbeddedObjectResolver", + "com.sun.star.drawing.TableShape" }; + + uno::Sequence< OUString > aSNS_Specific; + + if(mbImpressDoc) + aSNS_Specific = { "com.sun.star.presentation.TitleTextShape", + "com.sun.star.presentation.OutlinerShape", + "com.sun.star.presentation.SubtitleShape", + "com.sun.star.presentation.GraphicObjectShape", + "com.sun.star.presentation.ChartShape", + "com.sun.star.presentation.PageShape", + "com.sun.star.presentation.OLE2Shape", + "com.sun.star.presentation.TableShape", + "com.sun.star.presentation.OrgChartShape", + "com.sun.star.presentation.NotesShape", + "com.sun.star.presentation.HandoutShape", + "com.sun.star.presentation.DocumentSettings", + "com.sun.star.presentation.FooterShape", + "com.sun.star.presentation.HeaderShape", + "com.sun.star.presentation.SlideNumberShape", + "com.sun.star.presentation.DateTimeShape", + "com.sun.star.presentation.CalcShape", + "com.sun.star.presentation.MediaShape" }; + else + aSNS_Specific = { "com.sun.star.drawing.DocumentSettings" }; + + return comphelper::concatSequences( aSNS_ORG, aSNS_Common, aSNS_Specific ); +} + +// lang::XServiceInfo +OUString SAL_CALL SdXImpressDocument::getImplementationName() +{ + return "SdXImpressDocument"; + /* // Matching the .component information: + return mbImpressDoc + ? OUString("com.sun.star.comp.Draw.PresentationDocument") + : OUString("com.sun.star.comp.Draw.DrawingDocument"); + */ +} + +sal_Bool SAL_CALL SdXImpressDocument::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence< OUString > SAL_CALL SdXImpressDocument::getSupportedServiceNames() +{ + ::SolarMutexGuard aGuard; + + return { "com.sun.star.document.OfficeDocument", + "com.sun.star.drawing.GenericDrawingDocument", + "com.sun.star.drawing.DrawingDocumentFactory", + mbImpressDoc?OUString("com.sun.star.presentation.PresentationDocument"):OUString("com.sun.star.drawing.DrawingDocument") }; +} + +// XPropertySet +uno::Reference< beans::XPropertySetInfo > SAL_CALL SdXImpressDocument::getPropertySetInfo( ) +{ + ::SolarMutexGuard aGuard; + return mpPropSet->getPropertySetInfo(); +} + +void SAL_CALL SdXImpressDocument::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + const SfxItemPropertyMapEntry* pEntry = mpPropSet->getPropertyMapEntry(aPropertyName); + + switch( pEntry ? pEntry->nWID : -1 ) + { + case WID_MODEL_LANGUAGE: + { + lang::Locale aLocale; + if(!(aValue >>= aLocale)) + throw lang::IllegalArgumentException(); + + mpDoc->SetLanguage( LanguageTag::convertToLanguageType(aLocale), EE_CHAR_LANGUAGE ); + break; + } + case WID_MODEL_TABSTOP: + { + sal_Int32 nValue = 0; + if(!(aValue >>= nValue) || nValue < 0 ) + throw lang::IllegalArgumentException(); + + mpDoc->SetDefaultTabulator(static_cast<sal_uInt16>(nValue)); + break; + } + case WID_MODEL_VISAREA: + { + SfxObjectShell* pEmbeddedObj = mpDoc->GetDocSh(); + if( !pEmbeddedObj ) + break; + + awt::Rectangle aVisArea; + if( !(aValue >>= aVisArea) || (aVisArea.Width < 0) || (aVisArea.Height < 0) ) + throw lang::IllegalArgumentException(); + + sal_Int32 nRight, nTop; + if (o3tl::checked_add(aVisArea.X, aVisArea.Width, nRight) || o3tl::checked_add(aVisArea.Y, aVisArea.Height, nTop)) + throw lang::IllegalArgumentException(); + + pEmbeddedObj->SetVisArea(::tools::Rectangle(aVisArea.X, aVisArea.Y, nRight, nTop)); + } + break; + case WID_MODEL_CONTFOCUS: + { + bool bFocus = false; + if( !(aValue >>= bFocus ) ) + throw lang::IllegalArgumentException(); + mpDoc->SetAutoControlFocus( bFocus ); + } + break; + case WID_MODEL_DSGNMODE: + { + bool bMode = false; + if( !(aValue >>= bMode ) ) + throw lang::IllegalArgumentException(); + mpDoc->SetOpenInDesignMode( bMode ); + } + break; + case WID_MODEL_BUILDID: + aValue >>= maBuildId; + return; + case WID_MODEL_MAPUNIT: + case WID_MODEL_BASICLIBS: + case WID_MODEL_RUNTIMEUID: // is read-only + case WID_MODEL_DIALOGLIBS: + case WID_MODEL_FONTS: + throw beans::PropertyVetoException(); + case WID_MODEL_INTEROPGRABBAG: + setGrabBagItem(aValue); + break; + case WID_MODEL_THEME: + { + SdrModel& rModel = getSdrModelFromUnoModel(); + std::unique_ptr<svx::Theme> pTheme = svx::Theme::FromAny(aValue); + rModel.SetTheme(std::move(pTheme)); + } + break; + default: + throw beans::UnknownPropertyException( aPropertyName, static_cast<cppu::OWeakObject*>(this)); + } + + SetModified(); +} + +uno::Any SAL_CALL SdXImpressDocument::getPropertyValue( const OUString& PropertyName ) +{ + ::SolarMutexGuard aGuard; + + uno::Any aAny; + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + const SfxItemPropertyMapEntry* pEntry = mpPropSet->getPropertyMapEntry(PropertyName); + + switch( pEntry ? pEntry->nWID : -1 ) + { + case WID_MODEL_LANGUAGE: + { + LanguageType eLang = mpDoc->GetLanguage( EE_CHAR_LANGUAGE ); + aAny <<= LanguageTag::convertToLocale( eLang); + break; + } + case WID_MODEL_TABSTOP: + aAny <<= static_cast<sal_Int32>(mpDoc->GetDefaultTabulator()); + break; + case WID_MODEL_VISAREA: + { + SfxObjectShell* pEmbeddedObj = mpDoc->GetDocSh(); + if( !pEmbeddedObj ) + break; + + const ::tools::Rectangle& aRect = pEmbeddedObj->GetVisArea(); + awt::Rectangle aVisArea( aRect.Left(), aRect.Top(), aRect.getWidth(), aRect.getHeight() ); + aAny <<= aVisArea; + } + break; + case WID_MODEL_MAPUNIT: + { + SfxObjectShell* pEmbeddedObj = mpDoc->GetDocSh(); + if( !pEmbeddedObj ) + break; + + sal_Int16 nMeasureUnit = 0; + SvxMapUnitToMeasureUnit( pEmbeddedObj->GetMapUnit(), nMeasureUnit ); + aAny <<= nMeasureUnit; + } + break; + case WID_MODEL_FORBCHARS: + { + aAny <<= getForbiddenCharsTable(); + } + break; + case WID_MODEL_CONTFOCUS: + aAny <<= mpDoc->GetAutoControlFocus(); + break; + case WID_MODEL_DSGNMODE: + aAny <<= mpDoc->GetOpenInDesignMode(); + break; + case WID_MODEL_BASICLIBS: + aAny <<= mpDocShell->GetBasicContainer(); + break; + case WID_MODEL_DIALOGLIBS: + aAny <<= mpDocShell->GetDialogContainer(); + break; + case WID_MODEL_RUNTIMEUID: + aAny <<= getRuntimeUID(); + break; + case WID_MODEL_BUILDID: + return uno::Any( maBuildId ); + case WID_MODEL_HASVALIDSIGNATURES: + aAny <<= hasValidSignatures(); + break; + case WID_MODEL_FONTS: + { + uno::Sequence<uno::Any> aSeq; + int nSeqIndex = 0; + + sal_uInt16 const aWhichIds[] { EE_CHAR_FONTINFO, EE_CHAR_FONTINFO_CJK, + EE_CHAR_FONTINFO_CTL }; + + const SfxItemPool& rPool = mpDoc->GetPool(); + + for(sal_uInt16 nWhichId : aWhichIds) + { + sal_uInt32 nItems = rPool.GetItemCount2( nWhichId ); + + aSeq.realloc( aSeq.getLength() + nItems*5 + 5 ); + auto pSeq = aSeq.getArray(); + + for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(nWhichId)) + { + const SvxFontItem *pFont = static_cast<const SvxFontItem *>(pItem); + + pSeq[nSeqIndex++] <<= pFont->GetFamilyName(); + pSeq[nSeqIndex++] <<= pFont->GetStyleName(); + pSeq[nSeqIndex++] <<= sal_Int16(pFont->GetFamily()); + pSeq[nSeqIndex++] <<= sal_Int16(pFont->GetPitch()); + pSeq[nSeqIndex++] <<= sal_Int16(pFont->GetCharSet()); + } + + const SvxFontItem& rFont = static_cast<const SvxFontItem&>(rPool.GetDefaultItem( nWhichId )); + + pSeq[nSeqIndex++] <<= rFont.GetFamilyName(); + pSeq[nSeqIndex++] <<= rFont.GetStyleName(); + pSeq[nSeqIndex++] <<= sal_Int16(rFont.GetFamily()); + pSeq[nSeqIndex++] <<= sal_Int16(rFont.GetPitch()); + pSeq[nSeqIndex++] <<= sal_Int16(rFont.GetCharSet()); + + } + + aSeq.realloc( nSeqIndex ); + aAny <<= aSeq; + break; + } + case WID_MODEL_INTEROPGRABBAG: + getGrabBagItem(aAny); + break; + case WID_MODEL_THEME: + { + SdrModel& rModel = getSdrModelFromUnoModel(); + svx::Theme* pTheme = rModel.GetTheme(); + if (pTheme) + { + pTheme->ToAny(aAny); + } + else + { + beans::PropertyValues aValues; + aAny <<= aValues; + } + break; + } + default: + throw beans::UnknownPropertyException( PropertyName, static_cast<cppu::OWeakObject*>(this)); + } + + return aAny; +} + +void SAL_CALL SdXImpressDocument::addPropertyChangeListener( const OUString& , const uno::Reference< beans::XPropertyChangeListener >& ) {} +void SAL_CALL SdXImpressDocument::removePropertyChangeListener( const OUString& , const uno::Reference< beans::XPropertyChangeListener >& ) {} +void SAL_CALL SdXImpressDocument::addVetoableChangeListener( const OUString& , const uno::Reference< beans::XVetoableChangeListener >& ) {} +void SAL_CALL SdXImpressDocument::removeVetoableChangeListener( const OUString& , const uno::Reference< beans::XVetoableChangeListener >& ) {} + +// XLinkTargetSupplier +uno::Reference< container::XNameAccess > SAL_CALL SdXImpressDocument::getLinks() +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + uno::Reference< container::XNameAccess > xLinks( mxLinks ); + if( !xLinks.is() ) + mxLinks = xLinks = new SdDocLinkTargets( *this ); + return xLinks; +} + +// XStyleFamiliesSupplier +uno::Reference< container::XNameAccess > SAL_CALL SdXImpressDocument::getStyleFamilies( ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + uno::Reference< container::XNameAccess > xStyles( dynamic_cast< container::XNameAccess* >( mpDoc->GetStyleSheetPool()) ); + return xStyles; +} + +// XAnyCompareFactory +uno::Reference< css::ucb::XAnyCompare > SAL_CALL SdXImpressDocument::createAnyCompareByName( const OUString& ) +{ + return SvxCreateNumRuleCompare(); +} + +// XRenderable +sal_Int32 SAL_CALL SdXImpressDocument::getRendererCount( const uno::Any& rSelection, + const uno::Sequence< beans::PropertyValue >& ) +{ + ::SolarMutexGuard aGuard; + sal_Int32 nRet = 0; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + if (mpDocShell) + { + uno::Reference< frame::XModel > xModel; + + rSelection >>= xModel; + + if( xModel == mpDocShell->GetModel() ) + nRet = mpDoc->GetSdPageCount( PageKind::Standard ); + else + { + uno::Reference< drawing::XShapes > xShapes; + + rSelection >>= xShapes; + + if( xShapes.is() && xShapes->getCount() ) + nRet = 1; + } + } + return nRet; +} + +uno::Sequence< beans::PropertyValue > SAL_CALL SdXImpressDocument::getRenderer( sal_Int32 , const uno::Any& , + const uno::Sequence< beans::PropertyValue >& rxOptions ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + bool bExportNotesPages = false; + for( const auto& rOption : rxOptions ) + { + if ( rOption.Name == "ExportNotesPages" ) + rOption.Value >>= bExportNotesPages; + } + uno::Sequence< beans::PropertyValue > aRenderer; + if (mpDocShell) + { + awt::Size aPageSize; + if ( bExportNotesPages ) + { + Size aNotesPageSize = mpDoc->GetSdPage( 0, PageKind::Notes )->GetSize(); + aPageSize = awt::Size( aNotesPageSize.Width(), aNotesPageSize.Height() ); + } + else + { + const ::tools::Rectangle aVisArea( mpDocShell->GetVisArea( embed::Aspects::MSOLE_DOCPRINT ) ); + aPageSize = awt::Size( aVisArea.GetWidth(), aVisArea.GetHeight() ); + } + aRenderer = { comphelper::makePropertyValue("PageSize", aPageSize) }; + } + return aRenderer; +} + +namespace { + +class ImplRenderPaintProc : public sdr::contact::ViewObjectContactRedirector +{ + const SdrLayerAdmin& rLayerAdmin; + SdrPageView* pSdrPageView; + +public: + bool IsVisible ( const SdrObject* pObj ) const; + bool IsPrintable( const SdrObject* pObj ) const; + + ImplRenderPaintProc(const SdrLayerAdmin& rLA, SdrPageView* pView); + + // all default implementations just call the same methods at the original. To do something + // different, override the method and at least do what the method does. + virtual void createRedirectedPrimitive2DSequence( + const sdr::contact::ViewObjectContact& rOriginal, + const sdr::contact::DisplayInfo& rDisplayInfo, + drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) override; +}; + +} + +ImplRenderPaintProc::ImplRenderPaintProc(const SdrLayerAdmin& rLA, SdrPageView *const pView) + : rLayerAdmin(rLA) + , pSdrPageView(pView) +{ +} + +static sal_Int32 ImplPDFGetBookmarkPage( const OUString& rBookmark, SdDrawDocument const & rDoc ) +{ + sal_Int32 nPage = -1; + + OUString aBookmark( rBookmark ); + + if( rBookmark.startsWith("#") ) + aBookmark = rBookmark.copy( 1 ); + + // is the bookmark a page ? + bool bIsMasterPage; + sal_uInt16 nPgNum = rDoc.GetPageByName( aBookmark, bIsMasterPage ); + + if ( nPgNum == SDRPAGE_NOTFOUND ) + { + // is the bookmark an object ? + SdrObject* pObj = rDoc.GetObj( aBookmark ); + if (pObj) + nPgNum = pObj->getSdrPageFromSdrObject()->GetPageNum(); + } + if ( nPgNum != SDRPAGE_NOTFOUND ) + nPage = ( nPgNum - 1 ) / 2; + return nPage; +} + +static void ImplPDFExportComments( const uno::Reference< drawing::XDrawPage >& xPage, vcl::PDFExtOutDevData& rPDFExtOutDevData ) +{ + try + { + uno::Reference< office::XAnnotationAccess > xAnnotationAccess( xPage, uno::UNO_QUERY_THROW ); + uno::Reference< office::XAnnotationEnumeration > xAnnotationEnumeration( xAnnotationAccess->createAnnotationEnumeration() ); + + while( xAnnotationEnumeration->hasMoreElements() ) + { + uno::Reference< office::XAnnotation > xAnnotation( xAnnotationEnumeration->nextElement() ); + + geometry::RealPoint2D aRealPoint2D( xAnnotation->getPosition() ); + uno::Reference< text::XText > xText( xAnnotation->getTextRange() ); + + vcl::PDFNote aNote; + aNote.Title = xAnnotation->getAuthor(); + aNote.Contents = xText->getString(); + aNote.maModificationDate = xAnnotation->getDateTime(); + + rPDFExtOutDevData.CreateNote( ::tools::Rectangle( Point( static_cast< ::tools::Long >( aRealPoint2D.X * 100 ), + static_cast< ::tools::Long >( aRealPoint2D.Y * 100 ) ), Size( 1000, 1000 ) ), aNote ); + } + } + catch (const uno::Exception&) + { + } +} + +static void ImplPDFExportShapeInteraction( const uno::Reference< drawing::XShape >& xShape, SdDrawDocument& rDoc, vcl::PDFExtOutDevData& rPDFExtOutDevData ) +{ + if ( xShape->getShapeType() == "com.sun.star.drawing.GroupShape" ) + { + uno::Reference< container::XIndexAccess > xIndexAccess( xShape, uno::UNO_QUERY ); + if ( xIndexAccess.is() ) + { + sal_Int32 i, nCount = xIndexAccess->getCount(); + for ( i = 0; i < nCount; i++ ) + { + uno::Reference< drawing::XShape > xSubShape( xIndexAccess->getByIndex( i ), uno::UNO_QUERY ); + if ( xSubShape.is() ) + ImplPDFExportShapeInteraction( xSubShape, rDoc, rPDFExtOutDevData ); + } + } + } + else + { + uno::Reference< beans::XPropertySet > xShapePropSet( xShape, uno::UNO_QUERY ); + if( xShapePropSet.is() ) + { + Size aPageSize( rDoc.GetSdPage( 0, PageKind::Standard )->GetSize() ); + Point aPoint( 0, 0 ); + ::tools::Rectangle aPageRect( aPoint, aPageSize ); + + awt::Point aShapePos( xShape->getPosition() ); + awt::Size aShapeSize( xShape->getSize() ); + ::tools::Rectangle aLinkRect( Point( aShapePos.X, aShapePos.Y ), Size( aShapeSize.Width, aShapeSize.Height ) ); + + // Handle linked videos. + if (xShape->getShapeType() == "com.sun.star.drawing.MediaShape" || xShape->getShapeType() == "com.sun.star.presentation.MediaShape") + { + OUString aMediaURL; + xShapePropSet->getPropertyValue("MediaURL") >>= aMediaURL; + if (!aMediaURL.isEmpty()) + { + sal_Int32 nScreenId = rPDFExtOutDevData.CreateScreen(aLinkRect, rPDFExtOutDevData.GetCurrentPageNumber()); + if (aMediaURL.startsWith("vnd.sun.star.Package:")) + { + OUString aTempFileURL; + xShapePropSet->getPropertyValue("PrivateTempFileURL") >>= aTempFileURL; + rPDFExtOutDevData.SetScreenStream(nScreenId, aTempFileURL); + } + else + rPDFExtOutDevData.SetScreenURL(nScreenId, aMediaURL); + } + } + + presentation::ClickAction eCa; + uno::Any aAny( xShapePropSet->getPropertyValue( "OnClick" ) ); + if ( aAny >>= eCa ) + { + OUString const actionName(SdResId(SdTPAction::GetClickActionSdResId(eCa))); + switch ( eCa ) + { + case presentation::ClickAction_LASTPAGE : + { + sal_Int32 nCount = rDoc.GetSdPageCount( PageKind::Standard ); + sal_Int32 nDestId = rPDFExtOutDevData.CreateDest( aPageRect, nCount - 1, vcl::PDFWriter::DestAreaType::FitRectangle ); + sal_Int32 nLinkId = rPDFExtOutDevData.CreateLink(aLinkRect, actionName); + rPDFExtOutDevData.SetLinkDest( nLinkId, nDestId ); + } + break; + case presentation::ClickAction_FIRSTPAGE : + { + sal_Int32 nDestId = rPDFExtOutDevData.CreateDest( aPageRect, 0, vcl::PDFWriter::DestAreaType::FitRectangle ); + sal_Int32 nLinkId = rPDFExtOutDevData.CreateLink(aLinkRect, actionName); + rPDFExtOutDevData.SetLinkDest( nLinkId, nDestId ); + } + break; + case presentation::ClickAction_PREVPAGE : + { + sal_Int32 nDestPage = rPDFExtOutDevData.GetCurrentPageNumber(); + if ( nDestPage ) + nDestPage--; + sal_Int32 nDestId = rPDFExtOutDevData.CreateDest( aPageRect, nDestPage, vcl::PDFWriter::DestAreaType::FitRectangle ); + sal_Int32 nLinkId = rPDFExtOutDevData.CreateLink(aLinkRect, actionName); + rPDFExtOutDevData.SetLinkDest( nLinkId, nDestId ); + } + break; + case presentation::ClickAction_NEXTPAGE : + { + sal_Int32 nDestPage = rPDFExtOutDevData.GetCurrentPageNumber() + 1; + sal_Int32 nLastPage = rDoc.GetSdPageCount( PageKind::Standard ) - 1; + if ( nDestPage > nLastPage ) + nDestPage = nLastPage; + sal_Int32 nDestId = rPDFExtOutDevData.CreateDest( aPageRect, nDestPage, vcl::PDFWriter::DestAreaType::FitRectangle ); + sal_Int32 nLinkId = rPDFExtOutDevData.CreateLink(aLinkRect, actionName); + rPDFExtOutDevData.SetLinkDest( nLinkId, nDestId ); + } + break; + + case presentation::ClickAction_PROGRAM : + case presentation::ClickAction_BOOKMARK : + case presentation::ClickAction_DOCUMENT : + { + OUString aBookmark; + xShapePropSet->getPropertyValue( "Bookmark" ) >>= aBookmark; + if( !aBookmark.isEmpty() ) + { + switch( eCa ) + { + case presentation::ClickAction_DOCUMENT : + case presentation::ClickAction_PROGRAM : + { + sal_Int32 nLinkId = rPDFExtOutDevData.CreateLink(aLinkRect, actionName); + rPDFExtOutDevData.SetLinkURL( nLinkId, aBookmark ); + } + break; + case presentation::ClickAction_BOOKMARK : + { + sal_Int32 nPage = ImplPDFGetBookmarkPage( aBookmark, rDoc ); + if ( nPage != -1 ) + { + sal_Int32 nDestId = rPDFExtOutDevData.CreateDest( aPageRect, nPage, vcl::PDFWriter::DestAreaType::FitRectangle ); + sal_Int32 nLinkId = rPDFExtOutDevData.CreateLink(aLinkRect, actionName); + rPDFExtOutDevData.SetLinkDest( nLinkId, nDestId ); + } + } + break; + default: + break; + } + } + } + break; + + case presentation::ClickAction_STOPPRESENTATION : + case presentation::ClickAction_SOUND : + case presentation::ClickAction_INVISIBLE : + case presentation::ClickAction_VERB : + case presentation::ClickAction_VANISH : + case presentation::ClickAction_MACRO : + default : + break; + } + } + } + } +} + +void ImplRenderPaintProc::createRedirectedPrimitive2DSequence( + const sdr::contact::ViewObjectContact& rOriginal, + const sdr::contact::DisplayInfo& rDisplayInfo, + drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) +{ + SdrObject* pObject = rOriginal.GetViewContact().TryToGetSdrObject(); + if(!pObject) + { + // not an object, maybe a page + sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(rOriginal, rDisplayInfo, rVisitor); + return; + } + SdrPage* pSdrPage(pObject->getSdrPageFromSdrObject()); + if(!pSdrPage) + return; + if(!pSdrPage->checkVisibility(rOriginal, rDisplayInfo, false)) + return; + if(!IsVisible(pObject) || !IsPrintable(pObject)) + return; + + sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(rOriginal, rDisplayInfo, rVisitor); +} + +bool ImplRenderPaintProc::IsVisible( const SdrObject* pObj ) const +{ + bool bVisible = true; + SdrLayerID nLayerId = pObj->GetLayer(); + if( pSdrPageView ) + { + const SdrLayer* pSdrLayer = rLayerAdmin.GetLayerPerID( nLayerId ); + if ( pSdrLayer ) + { + const OUString& aLayerName = pSdrLayer->GetName(); + bVisible = pSdrPageView->IsLayerVisible( aLayerName ); + } + } + return bVisible; +} +bool ImplRenderPaintProc::IsPrintable( const SdrObject* pObj ) const +{ + bool bPrintable = true; + SdrLayerID nLayerId = pObj->GetLayer(); + if( pSdrPageView ) + { + const SdrLayer* pSdrLayer = rLayerAdmin.GetLayerPerID( nLayerId ); + if ( pSdrLayer ) + { + const OUString& aLayerName = pSdrLayer->GetName(); + bPrintable = pSdrPageView->IsLayerPrintable( aLayerName ); + } + } + return bPrintable; + +} + +namespace +{ + sal_Int16 CalcOutputPageNum(vcl::PDFExtOutDevData const * pPDFExtOutDevData, SdDrawDocument const *pDoc, sal_Int16 nPageNumber) + { + //export all pages, simple one to one case + if (pPDFExtOutDevData && pPDFExtOutDevData->GetIsExportHiddenSlides()) + return nPageNumber-1; + //check all preceding pages, and only count non-hidden ones + sal_Int16 nRet = 0; + for (sal_Int16 i = 0; i < nPageNumber-1; ++i) + { + if (!pDoc->GetSdPage(i, PageKind::Standard)->IsExcluded()) + ++nRet; + } + return nRet; + } +} + +void SAL_CALL SdXImpressDocument::render( sal_Int32 nRenderer, const uno::Any& rSelection, + const uno::Sequence< beans::PropertyValue >& rxOptions ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + if (!mpDocShell) + return; + + uno::Reference< awt::XDevice > xRenderDevice; + const sal_Int32 nPageNumber = nRenderer + 1; + PageKind ePageKind = PageKind::Standard; + bool bExportNotesPages = false; + + for( const auto& rOption : rxOptions ) + { + if ( rOption.Name == "RenderDevice" ) + rOption.Value >>= xRenderDevice; + else if ( rOption.Name == "ExportNotesPages" ) + { + rOption.Value >>= bExportNotesPages; + if ( bExportNotesPages ) + ePageKind = PageKind::Notes; + } + } + + if( !(xRenderDevice.is() && nPageNumber && ( nPageNumber <= mpDoc->GetSdPageCount( ePageKind ) )) ) + return; + + VCLXDevice* pDevice = comphelper::getFromUnoTunnel<VCLXDevice>( xRenderDevice ); + VclPtr< OutputDevice> pOut = pDevice ? pDevice->GetOutputDevice() : VclPtr< OutputDevice >(); + + if( !pOut ) + return; + + vcl::PDFExtOutDevData* pPDFExtOutDevData = dynamic_cast<vcl::PDFExtOutDevData* >( pOut->GetExtOutDevData() ); + + if ( mpDoc->GetSdPage(static_cast<sal_Int16>(nPageNumber)-1, PageKind::Standard)->IsExcluded() && + !(pPDFExtOutDevData && pPDFExtOutDevData->GetIsExportHiddenSlides()) ) + return; + + if (pPDFExtOutDevData) + { + css::lang::Locale const docLocale(Application::GetSettings().GetLanguageTag().getLocale()); + pPDFExtOutDevData->SetDocumentLocale(docLocale); + } + + ::sd::ClientView aView( mpDocShell, pOut ); + ::tools::Rectangle aVisArea( Point(), mpDoc->GetSdPage( static_cast<sal_uInt16>(nPageNumber) - 1, ePageKind )->GetSize() ); + vcl::Region aRegion( aVisArea ); + + ::sd::ViewShell* pOldViewSh = mpDocShell->GetViewShell(); + ::sd::View* pOldSdView = pOldViewSh ? pOldViewSh->GetView() : nullptr; + + if ( pOldSdView ) + pOldSdView->SdrEndTextEdit(); + + aView.SetHlplVisible( false ); + aView.SetGridVisible( false ); + aView.SetBordVisible( false ); + aView.SetPageVisible( false ); + aView.SetGlueVisible( false ); + + pOut->SetMapMode(MapMode(MapUnit::Map100thMM)); + pOut->IntersectClipRegion( aVisArea ); + + uno::Reference< frame::XModel > xModel; + rSelection >>= xModel; + + if( xModel == mpDocShell->GetModel() ) + { + aView.ShowSdrPage( mpDoc->GetSdPage( static_cast<sal_uInt16>(nPageNumber) - 1, ePageKind )); + SdrPageView* pPV = aView.GetSdrPageView(); + + if( pOldSdView ) + { + SdrPageView* pOldPV = pOldSdView->GetSdrPageView(); + if( pPV && pOldPV ) + { + pPV->SetVisibleLayers( pOldPV->GetVisibleLayers() ); + pPV->SetPrintableLayers( pOldPV->GetPrintableLayers() ); + } + } + + ImplRenderPaintProc aImplRenderPaintProc( mpDoc->GetLayerAdmin(), + pPV); + + // background color for outliner :o + SdPage* pPage = pPV ? static_cast<SdPage*>(pPV->GetPage()) : nullptr; + if( pPage ) + { + SdrOutliner& rOutl = mpDoc->GetDrawOutliner(); + bool bScreenDisplay(true); + + // #i75566# printing; suppress AutoColor BackgroundColor generation + // for visibility reasons by giving GetPageBackgroundColor() + // the needed hint + // #i75566# PDF export; suppress AutoColor BackgroundColor generation (see printing) + if (pOut && ((OUTDEV_PRINTER == pOut->GetOutDevType()) + || (OUTDEV_PDF == pOut->GetOutDevType()))) + bScreenDisplay = false; + + // #i75566# Name change GetBackgroundColor -> GetPageBackgroundColor and + // hint value if screen display. Only then the AutoColor mechanisms shall be applied + rOutl.SetBackgroundColor( pPage->GetPageBackgroundColor( pPV, bScreenDisplay ) ); + } + aView.SdrPaintView::CompleteRedraw( pOut, aRegion, &aImplRenderPaintProc ); + + if ( pPDFExtOutDevData && pPage ) + { + try + { + uno::Any aAny; + uno::Reference< drawing::XDrawPage > xPage( uno::Reference< drawing::XDrawPage >::query( pPage->getUnoPage() ) ); + if ( xPage.is() ) + { + if ( pPDFExtOutDevData->GetIsExportNotes() ) + ImplPDFExportComments( xPage, *pPDFExtOutDevData ); + uno::Reference< beans::XPropertySet > xPagePropSet( xPage, uno::UNO_QUERY ); + if( xPagePropSet.is() ) + { + // exporting object interactions to pdf + + // if necessary, the master page interactions will be exported first + bool bIsBackgroundObjectsVisible = false; // #i39428# IsBackgroundObjectsVisible not available for Draw + if ( mbImpressDoc && xPagePropSet->getPropertySetInfo()->hasPropertyByName( "IsBackgroundObjectsVisible" ) ) + xPagePropSet->getPropertyValue( "IsBackgroundObjectsVisible" ) >>= bIsBackgroundObjectsVisible; + if ( bIsBackgroundObjectsVisible && !pPDFExtOutDevData->GetIsExportNotesPages() ) + { + uno::Reference< drawing::XMasterPageTarget > xMasterPageTarget( xPage, uno::UNO_QUERY ); + if ( xMasterPageTarget.is() ) + { + uno::Reference< drawing::XDrawPage > xMasterPage = xMasterPageTarget->getMasterPage(); + if ( xMasterPage.is() ) + { + sal_Int32 i, nCount = xMasterPage->getCount(); + for ( i = 0; i < nCount; i++ ) + { + aAny = xMasterPage->getByIndex( i ); + uno::Reference< drawing::XShape > xShape; + if ( aAny >>= xShape ) + ImplPDFExportShapeInteraction( xShape, *mpDoc, *pPDFExtOutDevData ); + } + } + } + } + + // exporting slide page object interactions + sal_Int32 i, nCount = xPage->getCount(); + for ( i = 0; i < nCount; i++ ) + { + aAny = xPage->getByIndex( i ); + uno::Reference< drawing::XShape > xShape; + if ( aAny >>= xShape ) + ImplPDFExportShapeInteraction( xShape, *mpDoc, *pPDFExtOutDevData ); + } + + // exporting transition effects to pdf + if ( mbImpressDoc && !pPDFExtOutDevData->GetIsExportNotesPages() && pPDFExtOutDevData->GetIsExportTransitionEffects() ) + { + static const OUStringLiteral sEffect( u"Effect" ); + static const OUStringLiteral sSpeed ( u"Speed" ); + sal_Int32 nTime = 800; + presentation::AnimationSpeed aAs; + if ( xPagePropSet->getPropertySetInfo( )->hasPropertyByName( sSpeed ) ) + { + aAny = xPagePropSet->getPropertyValue( sSpeed ); + if ( aAny >>= aAs ) + { + switch( aAs ) + { + case presentation::AnimationSpeed_SLOW : nTime = 1500; break; + case presentation::AnimationSpeed_FAST : nTime = 300; break; + default: + case presentation::AnimationSpeed_MEDIUM : nTime = 800; + } + } + } + presentation::FadeEffect eFe; + vcl::PDFWriter::PageTransition eType = vcl::PDFWriter::PageTransition::Regular; + if ( xPagePropSet->getPropertySetInfo( )->hasPropertyByName( sEffect ) ) + { + aAny = xPagePropSet->getPropertyValue( sEffect ); + if ( aAny >>= eFe ) + { + switch( eFe ) + { + case presentation::FadeEffect_HORIZONTAL_LINES : + case presentation::FadeEffect_HORIZONTAL_CHECKERBOARD : + case presentation::FadeEffect_HORIZONTAL_STRIPES : eType = vcl::PDFWriter::PageTransition::BlindsHorizontal; break; + + case presentation::FadeEffect_VERTICAL_LINES : + case presentation::FadeEffect_VERTICAL_CHECKERBOARD : + case presentation::FadeEffect_VERTICAL_STRIPES : eType = vcl::PDFWriter::PageTransition::BlindsVertical; break; + + case presentation::FadeEffect_UNCOVER_TO_RIGHT : + case presentation::FadeEffect_UNCOVER_TO_UPPERRIGHT : + case presentation::FadeEffect_ROLL_FROM_LEFT : + case presentation::FadeEffect_FADE_FROM_UPPERLEFT : + case presentation::FadeEffect_MOVE_FROM_UPPERLEFT : + case presentation::FadeEffect_FADE_FROM_LEFT : + case presentation::FadeEffect_MOVE_FROM_LEFT : eType = vcl::PDFWriter::PageTransition::WipeLeftToRight; break; + + case presentation::FadeEffect_UNCOVER_TO_BOTTOM : + case presentation::FadeEffect_UNCOVER_TO_LOWERRIGHT : + case presentation::FadeEffect_ROLL_FROM_TOP : + case presentation::FadeEffect_FADE_FROM_UPPERRIGHT : + case presentation::FadeEffect_MOVE_FROM_UPPERRIGHT : + case presentation::FadeEffect_FADE_FROM_TOP : + case presentation::FadeEffect_MOVE_FROM_TOP : eType = vcl::PDFWriter::PageTransition::WipeTopToBottom; break; + + case presentation::FadeEffect_UNCOVER_TO_LEFT : + case presentation::FadeEffect_UNCOVER_TO_LOWERLEFT : + case presentation::FadeEffect_ROLL_FROM_RIGHT : + + case presentation::FadeEffect_FADE_FROM_LOWERRIGHT : + case presentation::FadeEffect_MOVE_FROM_LOWERRIGHT : + case presentation::FadeEffect_FADE_FROM_RIGHT : + case presentation::FadeEffect_MOVE_FROM_RIGHT : eType = vcl::PDFWriter::PageTransition::WipeRightToLeft; break; + + case presentation::FadeEffect_UNCOVER_TO_TOP : + case presentation::FadeEffect_UNCOVER_TO_UPPERLEFT : + case presentation::FadeEffect_ROLL_FROM_BOTTOM : + case presentation::FadeEffect_FADE_FROM_LOWERLEFT : + case presentation::FadeEffect_MOVE_FROM_LOWERLEFT : + case presentation::FadeEffect_FADE_FROM_BOTTOM : + case presentation::FadeEffect_MOVE_FROM_BOTTOM : eType = vcl::PDFWriter::PageTransition::WipeBottomToTop; break; + + case presentation::FadeEffect_OPEN_VERTICAL : eType = vcl::PDFWriter::PageTransition::SplitHorizontalInward; break; + case presentation::FadeEffect_CLOSE_HORIZONTAL : eType = vcl::PDFWriter::PageTransition::SplitHorizontalOutward; break; + + case presentation::FadeEffect_OPEN_HORIZONTAL : eType = vcl::PDFWriter::PageTransition::SplitVerticalInward; break; + case presentation::FadeEffect_CLOSE_VERTICAL : eType = vcl::PDFWriter::PageTransition::SplitVerticalOutward; break; + + case presentation::FadeEffect_FADE_TO_CENTER : eType = vcl::PDFWriter::PageTransition::BoxInward; break; + case presentation::FadeEffect_FADE_FROM_CENTER : eType = vcl::PDFWriter::PageTransition::BoxOutward; break; + + case presentation::FadeEffect_NONE : eType = vcl::PDFWriter::PageTransition::Regular; break; + + case presentation::FadeEffect_RANDOM : + case presentation::FadeEffect_DISSOLVE : + default: eType = vcl::PDFWriter::PageTransition::Dissolve; break; + } + } + } + + if ( xPagePropSet->getPropertySetInfo( )->hasPropertyByName( sEffect ) || + xPagePropSet->getPropertySetInfo( )->hasPropertyByName( sSpeed ) ) + { + pPDFExtOutDevData->SetPageTransition( eType, nTime ); + } + } + } + } + + Size aPageSize( mpDoc->GetSdPage( 0, PageKind::Standard )->GetSize() ); + Point aPoint( 0, 0 ); + ::tools::Rectangle aPageRect( aPoint, aPageSize ); + + // resolving links found in this page by the method ImpEditEngine::Paint + std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = pPDFExtOutDevData->GetBookmarks(); + for ( const auto& rBookmark : rBookmarks ) + { + sal_Int32 nPage = ImplPDFGetBookmarkPage( rBookmark.aBookmark, *mpDoc ); + if ( nPage != -1 ) + { + if ( rBookmark.nLinkId != -1 ) + pPDFExtOutDevData->SetLinkDest( rBookmark.nLinkId, pPDFExtOutDevData->CreateDest( aPageRect, nPage, vcl::PDFWriter::DestAreaType::FitRectangle ) ); + else + pPDFExtOutDevData->DescribeRegisteredDest( rBookmark.nDestId, aPageRect, nPage, vcl::PDFWriter::DestAreaType::FitRectangle ); + } + else + pPDFExtOutDevData->SetLinkURL( rBookmark.nLinkId, rBookmark.aBookmark ); + } + rBookmarks.clear(); + //---> #i56629, #i40318 + //get the page name, will be used as outline element in PDF bookmark pane + OUString aPageName = mpDoc->GetSdPage( static_cast<sal_uInt16>(nPageNumber) - 1 , PageKind::Standard )->GetName(); + if( !aPageName.isEmpty() ) + { + // Destination PageNum + const sal_Int32 nDestPageNum = CalcOutputPageNum(pPDFExtOutDevData, mpDoc, nPageNumber); + + // insert the bookmark to this page into the NamedDestinations + if( pPDFExtOutDevData->GetIsExportNamedDestinations() ) + pPDFExtOutDevData->CreateNamedDest(aPageName, aPageRect, nDestPageNum); + + // add the name to the outline, (almost) same code as in sc/source/ui/unoobj/docuno.cxx + // issue #i40318. + + if( pPDFExtOutDevData->GetIsExportBookmarks() ) + { + // Destination Export + const sal_Int32 nDestId = + pPDFExtOutDevData->CreateDest(aPageRect , nDestPageNum); + + // Create a new outline item: + pPDFExtOutDevData->CreateOutlineItem( -1 , aPageName, nDestId ); + } + } + //<--- #i56629, #i40318 + } + catch (const uno::Exception&) + { + } + + } + } + else + { + uno::Reference< drawing::XShapes > xShapes; + rSelection >>= xShapes; + + if( xShapes.is() && xShapes->getCount() ) + { + SdrPageView* pPV = nullptr; + + ImplRenderPaintProc aImplRenderPaintProc( mpDoc->GetLayerAdmin(), + pOldSdView ? pOldSdView->GetSdrPageView() : nullptr); + + for( sal_uInt32 i = 0, nCount = xShapes->getCount(); i < nCount; i++ ) + { + uno::Reference< drawing::XShape > xShape; + xShapes->getByIndex( i ) >>= xShape; + + if( xShape.is() ) + { + SdrObject* pObj = SdrObject::getSdrObjectFromXShape( xShape ); + if( pObj && pObj->getSdrPageFromSdrObject() + && aImplRenderPaintProc.IsVisible( pObj ) + && aImplRenderPaintProc.IsPrintable( pObj ) ) + { + if( !pPV ) + pPV = aView.ShowSdrPage( pObj->getSdrPageFromSdrObject() ); + + if( pPV ) + aView.MarkObj( pObj, pPV ); + } + } + } + aView.DrawMarkedObj(*pOut); + } + } +} + +DrawViewShell* SdXImpressDocument::GetViewShell() +{ + DrawViewShell* pViewSh = dynamic_cast<DrawViewShell*>(mpDocShell->GetViewShell()); + if (!pViewSh) + { + SAL_WARN("sd", "DrawViewShell not available!"); + return nullptr; + } + return pViewSh; +} + +void SdXImpressDocument::paintTile( VirtualDevice& rDevice, + int nOutputWidth, int nOutputHeight, + int nTilePosX, int nTilePosY, + ::tools::Long nTileWidth, ::tools::Long nTileHeight ) +{ + DrawViewShell* pViewSh = GetViewShell(); + if (!pViewSh) + return; + + // Setup drawing layer to work properly. Since we use a custom VirtualDevice + // for the drawing, SdrPaintView::BeginCompleteRedraw() will call FindPaintWindow() + // unsuccessfully and use a temporary window that doesn't keep state. So patch + // the existing SdrPageWindow to use a temporary, and this way the state will be kept. + // Well, at least that's how I understand it based on Writer's RenderContextGuard, + // as the drawing layer classes lack documentation. + SdrPageWindow* patchedPageWindow = nullptr; + SdrPaintWindow* previousPaintWindow = nullptr; + std::unique_ptr<SdrPaintWindow> temporaryPaintWindow; + if(SdrView* pDrawView = pViewSh->GetDrawView()) + { + if(SdrPageView* pSdrPageView = pDrawView->GetSdrPageView()) + { + patchedPageWindow = pSdrPageView->FindPageWindow(*getDocWindow()->GetOutDev()); + temporaryPaintWindow.reset(new SdrPaintWindow(*pDrawView, rDevice)); + if (patchedPageWindow) + previousPaintWindow = patchedPageWindow->patchPaintWindow(*temporaryPaintWindow); + } + } + + // Scaling. Must convert from pixels to twips. We know + // that VirtualDevices use a DPI of 96. + // We specifically calculate these scales first as we're still + // in TWIPs, and might as well minimize the number of conversions. + const Fraction scale = conversionFract(o3tl::Length::px, o3tl::Length::twip); + Fraction scaleX = Fraction(nOutputWidth, nTileWidth) * scale; + Fraction scaleY = Fraction(nOutputHeight, nTileHeight) * scale; + + // svx seems to be the only component that works natively in + // 100th mm rather than TWIP. It makes most sense just to + // convert here and in getDocumentSize, and leave the tiled + // rendering API working in TWIPs. + ::tools::Long nTileWidthHMM = convertTwipToMm100( nTileWidth ); + ::tools::Long nTileHeightHMM = convertTwipToMm100( nTileHeight ); + int nTilePosXHMM = convertTwipToMm100( nTilePosX ); + int nTilePosYHMM = convertTwipToMm100( nTilePosY ); + + MapMode aMapMode = rDevice.GetMapMode(); + aMapMode.SetMapUnit( MapUnit::Map100thMM ); + aMapMode.SetOrigin( Point( -nTilePosXHMM, + -nTilePosYHMM) ); + aMapMode.SetScaleX( scaleX ); + aMapMode.SetScaleY( scaleY ); + + rDevice.SetMapMode( aMapMode ); + + rDevice.SetOutputSizePixel( Size(nOutputWidth, nOutputHeight) ); + + Point aPoint(nTilePosXHMM, nTilePosYHMM); + Size aSize(nTileWidthHMM, nTileHeightHMM); + ::tools::Rectangle aRect(aPoint, aSize); + + pViewSh->GetView()->CompleteRedraw(&rDevice, vcl::Region(aRect)); + + LokChartHelper::PaintAllChartsOnTile(rDevice, nOutputWidth, nOutputHeight, + nTilePosX, nTilePosY, nTileWidth, nTileHeight); + + if(patchedPageWindow != nullptr) + patchedPageWindow->unpatchPaintWindow(previousPaintWindow); +} + +void SdXImpressDocument::selectPart(int nPart, int nSelect) +{ + DrawViewShell* pViewSh = GetViewShell(); + if (!pViewSh) + return; + + pViewSh->SelectPage(nPart, nSelect); +} + +void SdXImpressDocument::moveSelectedParts(int nPosition, bool bDuplicate) +{ + // Duplicating is currently unsupported. + if (!bDuplicate) + mpDoc->MovePages(nPosition); +} + +OUString SdXImpressDocument::getPartInfo(int nPart) +{ + DrawViewShell* pViewSh = GetViewShell(); + if (!pViewSh) + return OUString(); + + const bool bIsVisible = pViewSh->IsVisible(nPart); + const bool bIsSelected = pViewSh->IsSelected(nPart); + const sal_Int16 nMasterPageCount= pViewSh->GetDoc()->GetMasterSdPageCount(pViewSh->GetPageKind()); + + OUString aPartInfo = "{ \"visible\": \"" + + OUString::number(static_cast<unsigned int>(bIsVisible)) + + "\", \"selected\": \"" + + OUString::number(static_cast<unsigned int>(bIsSelected)) + + "\", \"masterPageCount\": \"" + + OUString::number(nMasterPageCount) + + "\" }"; + return aPartInfo; +} + +void SdXImpressDocument::setPart( int nPart, bool bAllowChangeFocus ) +{ + DrawViewShell* pViewSh = GetViewShell(); + if (!pViewSh) + return; + + pViewSh->SwitchPage( nPart, bAllowChangeFocus ); +} + +int SdXImpressDocument::getParts() +{ + if (!mpDoc) + return 0; + + if (isMasterViewMode()) + return mpDoc->GetMasterSdPageCount(PageKind::Standard); + + return mpDoc->GetSdPageCount(PageKind::Standard); +} + +int SdXImpressDocument::getPart() +{ + DrawViewShell* pViewSh = GetViewShell(); + if (!pViewSh) + return 0; + + return pViewSh->GetViewShellBase().getPart(); +} + +OUString SdXImpressDocument::getPartName(int nPart) +{ + SdPage* pPage; + if (isMasterViewMode()) + pPage = mpDoc->GetMasterSdPage(nPart, PageKind::Standard); + else + pPage = mpDoc->GetSdPage(nPart, PageKind::Standard); + + if (!pPage) + { + SAL_WARN("sd", "DrawViewShell not available!"); + return OUString(); + } + + return pPage->GetName(); +} + +OUString SdXImpressDocument::getPartHash(int nPart) +{ + SdPage* pPage; + if (isMasterViewMode()) + pPage = mpDoc->GetMasterSdPage(nPart, PageKind::Standard); + else + pPage = mpDoc->GetSdPage(nPart, PageKind::Standard); + + if (!pPage) + { + SAL_WARN("sd", "DrawViewShell not available!"); + return OUString(); + } + + return OUString::number(pPage->GetHashCode()); +} + +bool SdXImpressDocument::isMasterViewMode() +{ + DrawViewShell* pViewSh = GetViewShell(); + if (!pViewSh) + return false; + + if (pViewSh->GetDispatcher()) + { + const SfxBoolItem* isMasterViewMode = nullptr; + pViewSh->GetDispatcher()->QueryState(SID_SLIDE_MASTER_MODE, isMasterViewMode); + if (isMasterViewMode && isMasterViewMode->GetValue()) + return true; + } + return false; +} + +VclPtr<vcl::Window> SdXImpressDocument::getDocWindow() +{ + SolarMutexGuard aGuard; + DrawViewShell* pViewShell = GetViewShell(); + VclPtr<vcl::Window> pWindow; + if (pViewShell) + pWindow = pViewShell->GetActiveWindow(); + + LokChartHelper aChartHelper(pViewShell->GetViewShell()); + VclPtr<vcl::Window> pChartWindow = aChartHelper.GetWindow(); + if (pChartWindow) + pWindow = pChartWindow; + + return pWindow; +} + +void SdXImpressDocument::setPartMode( int nPartMode ) +{ + DrawViewShell* pViewSh = GetViewShell(); + if (!pViewSh) + return; + + PageKind aPageKind( PageKind::Standard ); + switch ( nPartMode ) + { + case LOK_PARTMODE_SLIDES: + break; + case LOK_PARTMODE_NOTES: + aPageKind = PageKind::Notes; + break; + } + pViewSh->SetPageKind( aPageKind ); +} + +Size SdXImpressDocument::getDocumentSize() +{ + DrawViewShell* pViewSh = GetViewShell(); + if (!pViewSh) + return Size(); + + SdrView *pSdrView = pViewSh->GetView(); + if (!pSdrView) + return Size(); + + SdrPageView* pCurPageView = pSdrView->GetSdrPageView(); + if (!pCurPageView) + return Size(); + + Size aSize = pCurPageView->GetPageRect().GetSize(); + // Convert the size in 100th mm to TWIP + // See paintTile above for further info. + return o3tl::convert(aSize, o3tl::Length::mm100, o3tl::Length::twip); +} + +void SdXImpressDocument::getPostIts(::tools::JsonWriter& rJsonWriter) +{ + auto commentsNode = rJsonWriter.startNode("comments"); + // Return annotations on master pages too ? + const sal_uInt16 nMaxPages = mpDoc->GetPageCount(); + SdPage* pPage; + for (sal_uInt16 nPage = 0; nPage < nMaxPages; ++nPage) + { + pPage = static_cast<SdPage*>(mpDoc->GetPage(nPage)); + const sd::AnnotationVector& aPageAnnotations = pPage->getAnnotations(); + + for (const uno::Reference<office::XAnnotation>& xAnnotation : aPageAnnotations) + { + sal_uInt32 nID = sd::getAnnotationId(xAnnotation); + OString nodeName = "comment" + OString::number(nID); + auto commentNode = rJsonWriter.startNode(nodeName.getStr()); + rJsonWriter.put("id", nID); + rJsonWriter.put("author", xAnnotation->getAuthor()); + rJsonWriter.put("dateTime", utl::toISO8601(xAnnotation->getDateTime())); + uno::Reference<text::XText> xText(xAnnotation->getTextRange()); + rJsonWriter.put("text", xText->getString()); + rJsonWriter.put("parthash", pPage->GetHashCode()); + geometry::RealPoint2D const & rPoint = xAnnotation->getPosition(); + geometry::RealSize2D const & rSize = xAnnotation->getSize(); + ::tools::Rectangle aRectangle(Point(rPoint.X * 100.0, rPoint.Y * 100.0), Size(rSize.Width * 100.0, rSize.Height * 100.0)); + aRectangle = o3tl::toTwips(aRectangle, o3tl::Length::mm100); + OString sRectangle = aRectangle.toString(); + rJsonWriter.put("rectangle", sRectangle.getStr()); + } + } +} + +void SdXImpressDocument::initializeForTiledRendering(const css::uno::Sequence<css::beans::PropertyValue>& rArguments) +{ + SolarMutexGuard aGuard; + + if (DrawViewShell* pViewShell = GetViewShell()) + { + DrawView* pDrawView = pViewShell->GetDrawView(); + for (const beans::PropertyValue& rValue : rArguments) + { + if (rValue.Name == ".uno:ShowBorderShadow" && rValue.Value.has<bool>()) + pDrawView->SetPageShadowVisible(rValue.Value.get<bool>()); + else if (rValue.Name == ".uno:Author" && rValue.Value.has<OUString>()) + pDrawView->SetAuthor(rValue.Value.get<OUString>()); + else if (rValue.Name == ".uno:SpellOnline" && rValue.Value.has<bool>()) + mpDoc->SetOnlineSpell(rValue.Value.get<bool>()); + } + + // Disable comments if requested + SdOptions* pOptions = SD_MOD()->GetSdOptions(mpDoc->GetDocumentType()); + pOptions->SetShowComments(comphelper::LibreOfficeKit::isTiledAnnotations()); + + pViewShell->SetRuler(false); + pViewShell->SetScrollBarsVisible(false); + + if (sd::Window* pWindow = pViewShell->GetActiveWindow()) + { + // get the full page size in pixels + pWindow->EnableMapMode(); + Size aSize(pWindow->LogicToPixel(pDrawView->GetSdrPageView()->GetPage()->GetSize())); + // Disable map mode, so that it's possible to send mouse event + // coordinates in logic units + pWindow->EnableMapMode(false); + + // arrange UI elements again with new view size + pViewShell->GetParentWindow()->SetSizePixel(aSize); + pViewShell->Resize(); + } + + // Forces all images to be swapped in synchronously, this + // ensures that images are available when paintTile is called + // (whereas with async loading images start being loaded after + // we have painted the tile, resulting in an invalidate, followed + // by the tile being rerendered - which is wasteful and ugly). + pDrawView->SetSwapAsynchron(false); + } + + // when the "This document may contain formatting or content that cannot + // be saved..." dialog appears, it is auto-cancelled with tiled rendering, + // causing 'Save' being disabled; so let's always save to the original + // format + auto xChanges = comphelper::ConfigurationChanges::create(); + officecfg::Office::Common::Save::Document::WarnAlienFormat::set(false, xChanges); + xChanges->commit(); + + if (!getenv("LO_TESTNAME")) + SvtSlideSorterBarOptions().SetVisibleImpressView(true); +} + +void SdXImpressDocument::postKeyEvent(int nType, int nCharCode, int nKeyCode) +{ + SolarMutexGuard aGuard; + SfxLokHelper::postKeyEventAsync(getDocWindow(), nType, nCharCode, nKeyCode); +} + +void SdXImpressDocument::postMouseEvent(int nType, int nX, int nY, int nCount, int nButtons, int nModifier) +{ + SolarMutexGuard aGuard; + + DrawViewShell* pViewShell = GetViewShell(); + if (!pViewShell) + return; + + constexpr double fScale = o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::px); + + // check if user hit a chart which is being edited by him + LokChartHelper aChartHelper(pViewShell->GetViewShell()); + if (aChartHelper.postMouseEvent(nType, nX, nY, + nCount, nButtons, nModifier, + fScale, fScale)) + return; + + // check if the user hit a chart which is being edited by someone else + // and, if so, skip current mouse event + if (nType != LOK_MOUSEEVENT_MOUSEMOVE) + { + if (LokChartHelper::HitAny(Point(nX, nY))) + return; + } + + const Point aPos(Point(convertTwipToMm100(nX), convertTwipToMm100(nY))); + LokMouseEventData aMouseEventData(nType, aPos, nCount, MouseEventModifiers::SIMPLECLICK, + nButtons, nModifier); + SfxLokHelper::postMouseEventAsync(pViewShell->GetActiveWindow(), aMouseEventData); +} + +void SdXImpressDocument::setTextSelection(int nType, int nX, int nY) +{ + SolarMutexGuard aGuard; + + DrawViewShell* pViewShell = GetViewShell(); + if (!pViewShell) + return; + + LokChartHelper aChartHelper(pViewShell->GetViewShell()); + if (aChartHelper.setTextSelection(nType, nX, nY)) + return; + + Point aPoint(convertTwipToMm100(nX), convertTwipToMm100(nY)); + switch (nType) + { + case LOK_SETTEXTSELECTION_START: + pViewShell->SetCursorMm100Position(aPoint, /*bPoint=*/false, /*bClearMark=*/false); + break; + case LOK_SETTEXTSELECTION_END: + pViewShell->SetCursorMm100Position(aPoint, /*bPoint=*/true, /*bClearMark=*/false); + break; + case LOK_SETTEXTSELECTION_RESET: + pViewShell->SetCursorMm100Position(aPoint, /*bPoint=*/true, /*bClearMark=*/true); + break; + default: + assert(false); + break; + } +} + +uno::Reference<datatransfer::XTransferable> SdXImpressDocument::getSelection() +{ + SolarMutexGuard aGuard; + + DrawViewShell* pViewShell = GetViewShell(); + if (!pViewShell) + return uno::Reference<datatransfer::XTransferable>(); + + return pViewShell->GetSelectionTransferrable(); +} + +void SdXImpressDocument::setGraphicSelection(int nType, int nX, int nY) +{ + SolarMutexGuard aGuard; + + DrawViewShell* pViewShell = GetViewShell(); + if (!pViewShell) + return; + + constexpr double fScale = o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::px); + + LokChartHelper aChartHelper(pViewShell->GetViewShell()); + if (aChartHelper.setGraphicSelection(nType, nX, nY, fScale, fScale)) + return; + + Point aPoint(convertTwipToMm100(nX), convertTwipToMm100(nY)); + switch (nType) + { + case LOK_SETGRAPHICSELECTION_START: + pViewShell->SetGraphicMm100Position(/*bStart=*/true, aPoint); + break; + case LOK_SETGRAPHICSELECTION_END: + pViewShell->SetGraphicMm100Position(/*bStart=*/false, aPoint); + break; + default: + assert(false); + break; + } +} + +void SdXImpressDocument::resetSelection() +{ + SolarMutexGuard aGuard; + + DrawViewShell* pViewShell = GetViewShell(); + if (!pViewShell) + return; + + SdrView* pSdrView = pViewShell->GetView(); + if (!pSdrView) + return; + + if (pSdrView->IsTextEdit()) + { + // Reset the editeng selection. + pSdrView->UnmarkAll(); + // Finish editing. + pSdrView->SdrEndTextEdit(); + } + // Reset graphic selection. + pSdrView->UnmarkAll(); +} + +void SdXImpressDocument::setClientVisibleArea(const ::tools::Rectangle& rRectangle) +{ + SolarMutexGuard aGuard; + + DrawViewShell* pViewShell = GetViewShell(); + if (!pViewShell) + return; + + pViewShell->GetViewShellBase().setLOKVisibleArea(rRectangle); +} + +void SdXImpressDocument::setClipboard(const uno::Reference<datatransfer::clipboard::XClipboard>& xClipboard) +{ + SolarMutexGuard aGuard; + + DrawViewShell* pViewShell = GetViewShell(); + if (!pViewShell) + return; + + pViewShell->GetActiveWindow()->SetClipboard(xClipboard); +} + +bool SdXImpressDocument::isMimeTypeSupported() +{ + SolarMutexGuard aGuard; + DrawViewShell* pViewShell = GetViewShell(); + if (!pViewShell) + return false; + + TransferableDataHelper aDataHelper(TransferableDataHelper::CreateFromSystemClipboard(pViewShell->GetActiveWindow())); + return EditEngine::HasValidData(aDataHelper.GetTransferable()); +} + +PointerStyle SdXImpressDocument::getPointer() +{ + SolarMutexGuard aGuard; + DrawViewShell* pViewShell = GetViewShell(); + if (!pViewShell) + return PointerStyle::Arrow; + + Window* pWindow = pViewShell->GetActiveWindow(); + if (!pWindow) + return PointerStyle::Arrow; + + return pWindow->GetPointer(); +} + +uno::Reference< i18n::XForbiddenCharacters > SdXImpressDocument::getForbiddenCharsTable() +{ + uno::Reference< i18n::XForbiddenCharacters > xForb(mxForbiddenCharacters); + + if( !xForb.is() ) + mxForbiddenCharacters = xForb = new SdUnoForbiddenCharsTable( mpDoc ); + + return xForb; +} + +void SdXImpressDocument::initializeDocument() +{ + if( mbClipBoard ) + return; + + switch( mpDoc->GetPageCount() ) + { + case 1: + { + // nasty hack to detect clipboard document + mbClipBoard = true; + break; + } + case 0: + { + mpDoc->CreateFirstPages(); + mpDoc->StopWorkStartupDelay(); + break; + } + } +} + +SdrModel& SdXImpressDocument::getSdrModelFromUnoModel() const +{ + OSL_ENSURE(GetDoc(), "No SdrModel in draw/Impress, should not happen"); + return *GetDoc(); // TTTT should be reference +} + +void SAL_CALL SdXImpressDocument::dispose() +{ + if( mbDisposed ) + return; + + ::SolarMutexGuard aGuard; + + if( mpDoc ) + { + EndListening( *mpDoc ); + mpDoc = nullptr; + } + + // Call the base class dispose() before setting the mbDisposed flag + // to true. The reason for this is that if close() has not yet been + // called this is done in SfxBaseModel::dispose(). At the end of + // that dispose() is called again. It is important to forward this + // second dispose() to the base class, too. + // As a consequence the following code has to be able to be run twice. + SfxBaseModel::dispose(); + mbDisposed = true; + + uno::Reference< container::XNameAccess > xLinks( mxLinks ); + if( xLinks.is() ) + { + uno::Reference< lang::XComponent > xComp( xLinks, uno::UNO_QUERY ); + if( xComp.is() ) + xComp->dispose(); + + xLinks = nullptr; + } + + uno::Reference< drawing::XDrawPages > xDrawPagesAccess( mxDrawPagesAccess ); + if( xDrawPagesAccess.is() ) + { + uno::Reference< lang::XComponent > xComp( xDrawPagesAccess, uno::UNO_QUERY ); + if( xComp.is() ) + xComp->dispose(); + + xDrawPagesAccess = nullptr; + } + + uno::Reference< drawing::XDrawPages > xMasterPagesAccess( mxMasterPagesAccess ); + if( xDrawPagesAccess.is() ) + { + uno::Reference< lang::XComponent > xComp( xMasterPagesAccess, uno::UNO_QUERY ); + if( xComp.is() ) + xComp->dispose(); + + xDrawPagesAccess = nullptr; + } + + uno::Reference< container::XNameAccess > xLayerManager( mxLayerManager ); + if( xLayerManager.is() ) + { + uno::Reference< lang::XComponent > xComp( xLayerManager, uno::UNO_QUERY ); + if( xComp.is() ) + xComp->dispose(); + + xLayerManager = nullptr; + } + + uno::Reference< container::XNameContainer > xCustomPresentationAccess( mxCustomPresentationAccess ); + if( xCustomPresentationAccess.is() ) + { + uno::Reference< lang::XComponent > xComp( xCustomPresentationAccess, uno::UNO_QUERY ); + if( xComp.is() ) + xComp->dispose(); + + xCustomPresentationAccess = nullptr; + } + + mxDashTable = nullptr; + mxGradientTable = nullptr; + mxHatchTable = nullptr; + mxBitmapTable = nullptr; + mxTransGradientTable = nullptr; + mxMarkerTable = nullptr; + mxDrawingPool = nullptr; +} + + +SdDrawPagesAccess::SdDrawPagesAccess( SdXImpressDocument& rMyModel ) noexcept +: mpModel( &rMyModel) +{ +} + +SdDrawPagesAccess::~SdDrawPagesAccess() noexcept +{ +} + +// XIndexAccess +sal_Int32 SAL_CALL SdDrawPagesAccess::getCount() +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpModel ) + throw lang::DisposedException(); + + return mpModel->mpDoc->GetSdPageCount( PageKind::Standard ); +} + +uno::Any SAL_CALL SdDrawPagesAccess::getByIndex( sal_Int32 Index ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpModel ) + throw lang::DisposedException(); + + uno::Any aAny; + + if( (Index < 0) || (Index >= mpModel->mpDoc->GetSdPageCount( PageKind::Standard ) ) ) + throw lang::IndexOutOfBoundsException(); + + SdPage* pPage = mpModel->mpDoc->GetSdPage( static_cast<sal_uInt16>(Index), PageKind::Standard ); + if( pPage ) + { + uno::Reference< drawing::XDrawPage > xDrawPage( pPage->getUnoPage(), uno::UNO_QUERY ); + aAny <<= xDrawPage; + } + + return aAny; +} + +// XNameAccess +uno::Any SAL_CALL SdDrawPagesAccess::getByName( const OUString& aName ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpModel ) + throw lang::DisposedException(); + + if( !aName.isEmpty() ) + { + const sal_uInt16 nCount = mpModel->mpDoc->GetSdPageCount( PageKind::Standard ); + sal_uInt16 nPage; + for( nPage = 0; nPage < nCount; nPage++ ) + { + SdPage* pPage = mpModel->mpDoc->GetSdPage( nPage, PageKind::Standard ); + if(nullptr == pPage) + continue; + + if( aName == SdDrawPage::getPageApiName( pPage ) ) + { + uno::Any aAny; + uno::Reference< drawing::XDrawPage > xDrawPage( pPage->getUnoPage(), uno::UNO_QUERY ); + aAny <<= xDrawPage; + return aAny; + } + } + } + + throw container::NoSuchElementException(); +} + +uno::Sequence< OUString > SAL_CALL SdDrawPagesAccess::getElementNames() +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpModel ) + throw lang::DisposedException(); + + const sal_uInt16 nCount = mpModel->mpDoc->GetSdPageCount( PageKind::Standard ); + uno::Sequence< OUString > aNames( nCount ); + OUString* pNames = aNames.getArray(); + + sal_uInt16 nPage; + for( nPage = 0; nPage < nCount; nPage++ ) + { + SdPage* pPage = mpModel->mpDoc->GetSdPage( nPage, PageKind::Standard ); + *pNames++ = SdDrawPage::getPageApiName( pPage ); + } + + return aNames; +} + +sal_Bool SAL_CALL SdDrawPagesAccess::hasByName( const OUString& aName ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpModel ) + throw lang::DisposedException(); + + const sal_uInt16 nCount = mpModel->mpDoc->GetSdPageCount( PageKind::Standard ); + sal_uInt16 nPage; + for( nPage = 0; nPage < nCount; nPage++ ) + { + SdPage* pPage = mpModel->mpDoc->GetSdPage( nPage, PageKind::Standard ); + if(nullptr == pPage) + continue; + + if( aName == SdDrawPage::getPageApiName( pPage ) ) + return true; + } + + return false; +} + +// XElementAccess +uno::Type SAL_CALL SdDrawPagesAccess::getElementType() +{ + return cppu::UnoType<drawing::XDrawPage>::get(); +} + +sal_Bool SAL_CALL SdDrawPagesAccess::hasElements() +{ + return getCount() > 0; +} + +// XDrawPages + +/** + * Creates a new page with model at the specified position. + * @returns corresponding SdDrawPage + */ +uno::Reference< drawing::XDrawPage > SAL_CALL SdDrawPagesAccess::insertNewByIndex( sal_Int32 nIndex ) +{ + ::SolarMutexGuard aGuard; + comphelper::ProfileZone aZone("insertNewByIndex"); + + if( nullptr == mpModel ) + throw lang::DisposedException(); + + if( mpModel->mpDoc ) + { + SdPage* pPage = mpModel->InsertSdPage( static_cast<sal_uInt16>(nIndex), false ); + if( pPage ) + { + uno::Reference< drawing::XDrawPage > xDrawPage( pPage->getUnoPage(), uno::UNO_QUERY ); + return xDrawPage; + } + } + uno::Reference< drawing::XDrawPage > xDrawPage; + return xDrawPage; +} + +/** + * Removes the specified SdDrawPage from the model and the internal list. It + * only works, if there is at least one *normal* page in the model after + * removing this page. + */ +void SAL_CALL SdDrawPagesAccess::remove( const uno::Reference< drawing::XDrawPage >& xPage ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpModel || mpModel->mpDoc == nullptr ) + throw lang::DisposedException(); + + SdDrawDocument& rDoc = *mpModel->mpDoc; + + sal_uInt16 nPageCount = rDoc.GetSdPageCount( PageKind::Standard ); + if( nPageCount > 1 ) + { + // get pPage from xPage and determine the Id (nPos ) afterwards + SdDrawPage* pSvxPage = comphelper::getFromUnoTunnel<SdDrawPage>( xPage ); + if( pSvxPage ) + { + SdPage* pPage = static_cast<SdPage*>(pSvxPage->GetSdrPage()); + if(pPage && ( pPage->GetPageKind() == PageKind::Standard ) ) + { + sal_uInt16 nPage = pPage->GetPageNum(); + + SdPage* pNotesPage = static_cast< SdPage* >( rDoc.GetPage( nPage+1 ) ); + + bool bUndo = rDoc.IsUndoEnabled(); + if( bUndo ) + { + // Add undo actions and delete the pages. The order of adding + // the undo actions is important. + rDoc.BegUndo( SdResId( STR_UNDO_DELETEPAGES ) ); + rDoc.AddUndo(rDoc.GetSdrUndoFactory().CreateUndoDeletePage(*pNotesPage)); + rDoc.AddUndo(rDoc.GetSdrUndoFactory().CreateUndoDeletePage(*pPage)); + } + + rDoc.RemovePage( nPage ); // the page + rDoc.RemovePage( nPage ); // the notes page + + if( bUndo ) + { + rDoc.EndUndo(); + } + } + } + } + + mpModel->SetModified(); +} + +// XServiceInfo + +OUString SAL_CALL SdDrawPagesAccess::getImplementationName( ) +{ + return "SdDrawPagesAccess"; +} + +sal_Bool SAL_CALL SdDrawPagesAccess::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence< OUString > SAL_CALL SdDrawPagesAccess::getSupportedServiceNames( ) +{ + return { "com.sun.star.drawing.DrawPages" }; +} + +// XComponent +void SAL_CALL SdDrawPagesAccess::dispose( ) +{ + mpModel = nullptr; +} + +void SAL_CALL SdDrawPagesAccess::addEventListener( const uno::Reference< lang::XEventListener >& ) +{ + OSL_FAIL( "not implemented!" ); +} + +void SAL_CALL SdDrawPagesAccess::removeEventListener( const uno::Reference< lang::XEventListener >& ) +{ + OSL_FAIL( "not implemented!" ); +} + + +SdMasterPagesAccess::SdMasterPagesAccess( SdXImpressDocument& rMyModel ) noexcept +: mpModel(&rMyModel) +{ +} + +SdMasterPagesAccess::~SdMasterPagesAccess() noexcept +{ +} + +// XComponent +void SAL_CALL SdMasterPagesAccess::dispose( ) +{ + mpModel = nullptr; +} + +void SAL_CALL SdMasterPagesAccess::addEventListener( const uno::Reference< lang::XEventListener >& ) +{ + OSL_FAIL( "not implemented!" ); +} + +void SAL_CALL SdMasterPagesAccess::removeEventListener( const uno::Reference< lang::XEventListener >& ) +{ + OSL_FAIL( "not implemented!" ); +} + +// XIndexAccess +sal_Int32 SAL_CALL SdMasterPagesAccess::getCount() +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpModel->mpDoc ) + throw lang::DisposedException(); + + return mpModel->mpDoc->GetMasterSdPageCount(PageKind::Standard); +} + +/** + * Provides a drawing::XDrawPage interface for accessing the Masterpage at the + * specified position in the model. + */ +uno::Any SAL_CALL SdMasterPagesAccess::getByIndex( sal_Int32 Index ) +{ + ::SolarMutexGuard aGuard; + comphelper::ProfileZone aZone("SdMasterPagesAccess::getByIndex"); + + if( nullptr == mpModel ) + throw lang::DisposedException(); + + uno::Any aAny; + + if( (Index < 0) || (Index >= mpModel->mpDoc->GetMasterSdPageCount( PageKind::Standard ) ) ) + throw lang::IndexOutOfBoundsException(); + + SdPage* pPage = mpModel->mpDoc->GetMasterSdPage( static_cast<sal_uInt16>(Index), PageKind::Standard ); + if( pPage ) + { + uno::Reference< drawing::XDrawPage > xDrawPage( pPage->getUnoPage(), uno::UNO_QUERY ); + aAny <<= xDrawPage; + } + + return aAny; +} + +// XElementAccess +uno::Type SAL_CALL SdMasterPagesAccess::getElementType() +{ + return cppu::UnoType<drawing::XDrawPage>::get(); +} + +sal_Bool SAL_CALL SdMasterPagesAccess::hasElements() +{ + return getCount() > 0; +} + +// XDrawPages +uno::Reference< drawing::XDrawPage > SAL_CALL SdMasterPagesAccess::insertNewByIndex( sal_Int32 nInsertPos ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpModel ) + throw lang::DisposedException(); + + uno::Reference< drawing::XDrawPage > xDrawPage; + + SdDrawDocument* pDoc = mpModel->mpDoc; + if( pDoc ) + { + // calculate internal index and check for range errors + const sal_Int32 nMPageCount = pDoc->GetMasterPageCount(); + nInsertPos = nInsertPos * 2 + 1; + if( nInsertPos < 0 || nInsertPos > nMPageCount ) + nInsertPos = nMPageCount; + + // now generate a unique name for the new masterpage + const OUString aStdPrefix( SdResId(STR_LAYOUT_DEFAULT_NAME) ); + OUString aPrefix( aStdPrefix ); + + bool bUnique = true; + + std::vector<OUString> aPageNames; + for (sal_Int32 nMaster = 1; nMaster < nMPageCount; ++nMaster) + { + const SdPage* pPage = static_cast<const SdPage*>(pDoc->GetMasterPage(static_cast<sal_uInt16>(nMaster))); + if (!pPage) + continue; + aPageNames.push_back(pPage->GetName()); + if (aPageNames.back() == aPrefix) + bUnique = false; + } + + sal_Int32 i = 0; + while (!bUnique) + { + aPrefix = aStdPrefix + " " + OUString::number(++i); + bUnique = std::find(aPageNames.begin(), aPageNames.end(), aPrefix) == aPageNames.end(); + } + + OUString aLayoutName = aPrefix + SD_LT_SEPARATOR + STR_LAYOUT_OUTLINE; + + // create styles + static_cast<SdStyleSheetPool*>(pDoc->GetStyleSheetPool())->CreateLayoutStyleSheets( aPrefix ); + + // get the first page for initial size and border settings + SdPage* pPage = mpModel->mpDoc->GetSdPage( sal_uInt16(0), PageKind::Standard ); + SdPage* pRefNotesPage = mpModel->mpDoc->GetSdPage( sal_uInt16(0), PageKind::Notes); + + // create and insert new draw masterpage + rtl::Reference<SdPage> pMPage = mpModel->mpDoc->AllocSdPage(true); + pMPage->SetSize( pPage->GetSize() ); + pMPage->SetBorder( pPage->GetLeftBorder(), + pPage->GetUpperBorder(), + pPage->GetRightBorder(), + pPage->GetLowerBorder() ); + pMPage->SetLayoutName( aLayoutName ); + pDoc->InsertMasterPage(pMPage.get(), static_cast<sal_uInt16>(nInsertPos)); + + { + // ensure default MasterPage fill + pMPage->EnsureMasterPageDefaultBackground(); + } + + xDrawPage.set( pMPage->getUnoPage(), uno::UNO_QUERY ); + + // create and insert new notes masterpage + rtl::Reference<SdPage> pMNotesPage = mpModel->mpDoc->AllocSdPage(true); + pMNotesPage->SetSize( pRefNotesPage->GetSize() ); + pMNotesPage->SetPageKind(PageKind::Notes); + pMNotesPage->SetBorder( pRefNotesPage->GetLeftBorder(), + pRefNotesPage->GetUpperBorder(), + pRefNotesPage->GetRightBorder(), + pRefNotesPage->GetLowerBorder() ); + pMNotesPage->SetLayoutName( aLayoutName ); + pDoc->InsertMasterPage(pMNotesPage.get(), static_cast<sal_uInt16>(nInsertPos) + 1); + pMNotesPage->SetAutoLayout(AUTOLAYOUT_NOTES, true, true); + mpModel->SetModified(); + } + + return xDrawPage; +} + +/** + * Removes the specified SdMasterPage from the model and the internal list. It + * only works, if there is no *normal* page using this page as MasterPage in + * the model. + */ +void SAL_CALL SdMasterPagesAccess::remove( const uno::Reference< drawing::XDrawPage >& xPage ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpModel || mpModel->mpDoc == nullptr ) + throw lang::DisposedException(); + + SdMasterPage* pSdPage = comphelper::getFromUnoTunnel<SdMasterPage>( xPage ); + if(pSdPage == nullptr) + return; + + SdPage* pPage = dynamic_cast< SdPage* > (pSdPage->GetSdrPage()); + + DBG_ASSERT( pPage && pPage->IsMasterPage(), "SdMasterPage is not masterpage?"); + + if( !pPage || !pPage->IsMasterPage() || (mpModel->mpDoc->GetMasterPageUserCount(pPage) > 0)) + return; //Todo: this should be excepted + + // only standard pages can be removed directly + if( pPage->GetPageKind() != PageKind::Standard ) + return; + + sal_uInt16 nPage = pPage->GetPageNum(); + + SdDrawDocument& rDoc = *mpModel->mpDoc; + + SdPage* pNotesPage = static_cast< SdPage* >( rDoc.GetMasterPage( nPage+1 ) ); + + bool bUndo = rDoc.IsUndoEnabled(); + if( bUndo ) + { + // Add undo actions and delete the pages. The order of adding + // the undo actions is important. + rDoc.BegUndo( SdResId( STR_UNDO_DELETEPAGES ) ); + rDoc.AddUndo(rDoc.GetSdrUndoFactory().CreateUndoDeletePage(*pNotesPage)); + rDoc.AddUndo(rDoc.GetSdrUndoFactory().CreateUndoDeletePage(*pPage)); + } + + // remove both pages + rDoc.RemoveMasterPage( nPage ); + rDoc.RemoveMasterPage( nPage ); + + if( bUndo ) + { + rDoc.EndUndo(); + } +} + +// XServiceInfo + +OUString SAL_CALL SdMasterPagesAccess::getImplementationName( ) +{ + return "SdMasterPagesAccess"; +} + +sal_Bool SAL_CALL SdMasterPagesAccess::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence< OUString > SAL_CALL SdMasterPagesAccess::getSupportedServiceNames( ) +{ + return { "com.sun.star.drawing.MasterPages" }; +} + + +SdDocLinkTargets::SdDocLinkTargets( SdXImpressDocument& rMyModel ) noexcept +: mpModel( &rMyModel ) +{ +} + +SdDocLinkTargets::~SdDocLinkTargets() noexcept +{ +} + +// XComponent +void SAL_CALL SdDocLinkTargets::dispose( ) +{ + mpModel = nullptr; +} + +void SAL_CALL SdDocLinkTargets::addEventListener( const uno::Reference< lang::XEventListener >& ) +{ + OSL_FAIL( "not implemented!" ); +} + +void SAL_CALL SdDocLinkTargets::removeEventListener( const uno::Reference< lang::XEventListener >& ) +{ + OSL_FAIL( "not implemented!" ); +} + +// XNameAccess +uno::Any SAL_CALL SdDocLinkTargets::getByName( const OUString& aName ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpModel ) + throw lang::DisposedException(); + + SdPage* pPage = FindPage( aName ); + + if( pPage == nullptr ) + throw container::NoSuchElementException(); + + uno::Any aAny; + + uno::Reference< beans::XPropertySet > xProps( pPage->getUnoPage(), uno::UNO_QUERY ); + if( xProps.is() ) + aAny <<= xProps; + + return aAny; +} + +uno::Sequence< OUString > SAL_CALL SdDocLinkTargets::getElementNames() +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpModel ) + throw lang::DisposedException(); + + SdDrawDocument* pDoc = mpModel->GetDoc(); + if( pDoc == nullptr ) + { + return { }; + } + + if( pDoc->GetDocumentType() == DocumentType::Draw ) + { + const sal_uInt16 nMaxPages = pDoc->GetSdPageCount( PageKind::Standard ); + const sal_uInt16 nMaxMasterPages = pDoc->GetMasterSdPageCount( PageKind::Standard ); + + uno::Sequence< OUString > aSeq( nMaxPages + nMaxMasterPages ); + OUString* pStr = aSeq.getArray(); + + sal_uInt16 nPage; + // standard pages + for( nPage = 0; nPage < nMaxPages; nPage++ ) + *pStr++ = pDoc->GetSdPage( nPage, PageKind::Standard )->GetName(); + + // master pages + for( nPage = 0; nPage < nMaxMasterPages; nPage++ ) + *pStr++ = pDoc->GetMasterSdPage( nPage, PageKind::Standard )->GetName(); + return aSeq; + } + else + { + const sal_uInt16 nMaxPages = pDoc->GetPageCount(); + const sal_uInt16 nMaxMasterPages = pDoc->GetMasterPageCount(); + + uno::Sequence< OUString > aSeq( nMaxPages + nMaxMasterPages ); + OUString* pStr = aSeq.getArray(); + + sal_uInt16 nPage; + // standard pages + for( nPage = 0; nPage < nMaxPages; nPage++ ) + *pStr++ = static_cast<SdPage*>(pDoc->GetPage( nPage ))->GetName(); + + // master pages + for( nPage = 0; nPage < nMaxMasterPages; nPage++ ) + *pStr++ = static_cast<SdPage*>(pDoc->GetMasterPage( nPage ))->GetName(); + return aSeq; + } +} + +sal_Bool SAL_CALL SdDocLinkTargets::hasByName( const OUString& aName ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpModel ) + throw lang::DisposedException(); + + return FindPage( aName ) != nullptr; +} + +// container::XElementAccess +uno::Type SAL_CALL SdDocLinkTargets::getElementType() +{ + return cppu::UnoType<beans::XPropertySet>::get(); +} + +sal_Bool SAL_CALL SdDocLinkTargets::hasElements() +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpModel ) + throw lang::DisposedException(); + + return mpModel->GetDoc() != nullptr; +} + +SdPage* SdDocLinkTargets::FindPage( std::u16string_view rName ) const +{ + SdDrawDocument* pDoc = mpModel->GetDoc(); + if( pDoc == nullptr ) + return nullptr; + + const sal_uInt16 nMaxPages = pDoc->GetPageCount(); + const sal_uInt16 nMaxMasterPages = pDoc->GetMasterPageCount(); + + sal_uInt16 nPage; + SdPage* pPage; + + const bool bDraw = pDoc->GetDocumentType() == DocumentType::Draw; + + // standard pages + for( nPage = 0; nPage < nMaxPages; nPage++ ) + { + pPage = static_cast<SdPage*>(pDoc->GetPage( nPage )); + if( (pPage->GetName() == rName) && (!bDraw || (pPage->GetPageKind() == PageKind::Standard)) ) + return pPage; + } + + // master pages + for( nPage = 0; nPage < nMaxMasterPages; nPage++ ) + { + pPage = static_cast<SdPage*>(pDoc->GetMasterPage( nPage )); + if( (pPage->GetName() == rName) && (!bDraw || (pPage->GetPageKind() == PageKind::Standard)) ) + return pPage; + } + + return nullptr; +} + +// XServiceInfo +OUString SAL_CALL SdDocLinkTargets::getImplementationName() +{ + return "SdDocLinkTargets"; +} + +sal_Bool SAL_CALL SdDocLinkTargets::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +uno::Sequence< OUString > SAL_CALL SdDocLinkTargets::getSupportedServiceNames() +{ + return { "com.sun.star.document.LinkTargets" }; +} + +rtl::Reference< SdXImpressDocument > SdXImpressDocument::GetModel( SdDrawDocument const & rDocument ) +{ + rtl::Reference< SdXImpressDocument > xRet; + ::sd::DrawDocShell* pDocShell(rDocument.GetDocSh()); + if( pDocShell ) + { + uno::Reference<frame::XModel> xModel(pDocShell->GetModel()); + + xRet.set( dynamic_cast< SdXImpressDocument* >( xModel.get() ) ); + } + + return xRet; +} + +void NotifyDocumentEvent( SdDrawDocument const & rDocument, const OUString& rEventName ) +{ + rtl::Reference< SdXImpressDocument > xModel( SdXImpressDocument::GetModel( rDocument ) ); + + if( xModel.is() ) + { + uno::Reference< uno::XInterface > xSource( static_cast<uno::XWeak*>( xModel.get() ) ); + css::document::EventObject aEvent( xSource, rEventName ); + xModel->notifyEvent(aEvent ); + } +} + +void NotifyDocumentEvent( SdDrawDocument const & rDocument, const OUString& rEventName, const uno::Reference< uno::XInterface >& xSource ) +{ + rtl::Reference< SdXImpressDocument > xModel( SdXImpressDocument::GetModel( rDocument ) ); + + if( xModel.is() ) + { + css::document::EventObject aEvent( xSource, rEventName ); + xModel->notifyEvent(aEvent ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unomodule.cxx b/sd/source/ui/unoidl/unomodule.cxx new file mode 100644 index 000000000..d862f7c9d --- /dev/null +++ b/sd/source/ui/unoidl/unomodule.cxx @@ -0,0 +1,132 @@ +/* -*- 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 <com/sun/star/frame/DispatchResultState.hpp> +#include <cppuhelper/supportsservice.hxx> + +#include <sddll.hxx> +#include <sdmod.hxx> +#include "unomodule.hxx" +#include <sfx2/objface.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/request.hxx> +#include <vcl/svapp.hxx> + +using namespace ::com::sun::star; + + // XNotifyingDispatch +void SAL_CALL SdUnoModule::dispatchWithNotification( const util::URL& aURL, const uno::Sequence< beans::PropertyValue >& aArgs, const uno::Reference< frame::XDispatchResultListener >& xListener ) +{ + // there is no guarantee, that we are holded alive during this method! + // May the outside dispatch container will be updated by a CONTEXT_CHANGED + // asynchronous ... + uno::Reference< uno::XInterface > xThis(static_cast< frame::XNotifyingDispatch* >(this)); + + SolarMutexGuard aGuard; + SdDLL::Init(); + const SfxSlot* pSlot = SD_MOD()->GetInterface()->GetSlot( aURL.Complete ); + + sal_Int16 aState = frame::DispatchResultState::DONTKNOW; + if ( !pSlot ) + aState = frame::DispatchResultState::FAILURE; + else + { + SfxRequest aReq( pSlot, aArgs, SfxCallMode::SYNCHRON, SD_MOD()->GetPool() ); + const SfxPoolItem* pResult = SD_MOD()->ExecuteSlot( aReq ); + if ( pResult ) + aState = frame::DispatchResultState::SUCCESS; + else + aState = frame::DispatchResultState::FAILURE; + } + + if ( xListener.is() ) + { + xListener->dispatchFinished( + frame::DispatchResultEvent( + xThis, aState, uno::Any())); + } +} + // XDispatch +void SAL_CALL SdUnoModule::dispatch( const util::URL& aURL, const uno::Sequence< beans::PropertyValue >& aArgs ) +{ + dispatchWithNotification(aURL, aArgs, uno::Reference< frame::XDispatchResultListener >()); +} + +void SAL_CALL SdUnoModule::addStatusListener(const uno::Reference< frame::XStatusListener > &, const util::URL&) +{ +} + +void SAL_CALL SdUnoModule::removeStatusListener(const uno::Reference< frame::XStatusListener > &, const util::URL&) +{ +} + +uno::Sequence< uno::Reference< frame::XDispatch > > SAL_CALL SdUnoModule::queryDispatches( const uno::Sequence< frame::DispatchDescriptor >& seqDescripts ) +{ + sal_Int32 nCount = seqDescripts.getLength(); + uno::Sequence< uno::Reference< frame::XDispatch > > lDispatcher( nCount ); + + std::transform(seqDescripts.begin(), seqDescripts.end(), lDispatcher.getArray(), + [this](const frame::DispatchDescriptor& rDescr) -> uno::Reference<frame::XDispatch> { + return queryDispatch(rDescr.FeatureURL, rDescr.FrameName, rDescr.SearchFlags); }); + + return lDispatcher; +} + +// XDispatchProvider +uno::Reference< frame::XDispatch > SAL_CALL SdUnoModule::queryDispatch( const util::URL& aURL, const OUString&, sal_Int32 ) +{ + SolarMutexGuard aGuard; + SdDLL::Init(); + const SfxSlot* pSlot = SD_MOD()->GetInterface()->GetSlot( aURL.Complete ); + + uno::Reference< frame::XDispatch > xSlot; + if ( pSlot ) + xSlot = this; + + return xSlot; +} + +// XServiceInfo +OUString SAL_CALL SdUnoModule::getImplementationName( ) +{ + return "com.sun.star.comp.Draw.DrawingModule"; +} + +sal_Bool SAL_CALL SdUnoModule::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +uno::Sequence< OUString > SAL_CALL SdUnoModule::getSupportedServiceNames( ) +{ + return { "com.sun.star.drawing.ModuleDispatcher" }; +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_DrawingModule_get_implementation(css::uno::XComponentContext* , + css::uno::Sequence<css::uno::Any> const &) +{ + SolarMutexGuard aGuard; + + return cppu::acquire(new SdUnoModule); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unomodule.hxx b/sd/source/ui/unoidl/unomodule.hxx new file mode 100644 index 000000000..bc78c6b19 --- /dev/null +++ b/sd/source/ui/unoidl/unomodule.hxx @@ -0,0 +1,57 @@ +/* -*- 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 . + */ + +#pragma once + +#include <rtl/ustring.hxx> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/frame/XNotifyingDispatch.hpp> +#include <com/sun/star/uno/Reference.h> +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/lang/XServiceInfo.hpp> + +namespace com::sun::star::beans { struct PropertyValue; } +namespace com::sun::star::frame { struct DispatchDescriptor; } + +class SdUnoModule : public ::cppu::WeakImplHelper< css::frame::XDispatchProvider, css::frame::XNotifyingDispatch, css::lang::XServiceInfo > +{ +public: + SdUnoModule() {} + + // XnotifyingDispatch + virtual void SAL_CALL dispatchWithNotification( const css::util::URL& URL, const css::uno::Sequence< css::beans::PropertyValue >& Arguments, const css::uno::Reference< css::frame::XDispatchResultListener >& Listener ) override; + + // XDispatch + virtual void SAL_CALL dispatch( const css::util::URL& aURL, const css::uno::Sequence< css::beans::PropertyValue >& aArgs ) override; + virtual void SAL_CALL addStatusListener(const css::uno::Reference< css::frame::XStatusListener > & xControl, const css::util::URL& aURL) override; + virtual void SAL_CALL removeStatusListener(const css::uno::Reference< css::frame::XStatusListener > & xControl, const css::util::URL& aURL) override; + + // XDispatchProvider + virtual css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& seqDescriptor ) override ; + virtual css::uno::Reference< css::frame::XDispatch > SAL_CALL queryDispatch( const css::util::URL & aURL , + const OUString & sTargetFrameName, + sal_Int32 eSearchFlags ) override; + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unoobj.cxx b/sd/source/ui/unoidl/unoobj.cxx new file mode 100644 index 000000000..6ed6729f9 --- /dev/null +++ b/sd/source/ui/unoidl/unoobj.cxx @@ -0,0 +1,1627 @@ +/* -*- 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 <sal/config.h> + +#include <memory> +#include <string_view> +#include <utility> + +#include <com/sun/star/style/XStyle.hpp> +#include <com/sun/star/presentation/ClickAction.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/PropertyState.hpp> +#include <rtl/ustrbuf.hxx> +#include <svl/itemprop.hxx> +#include <svl/style.hxx> +#include <svx/svdpool.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/app.hxx> +#include <svtools/unoimap.hxx> +#include <svtools/unoevent.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/event.hxx> +#include <sfx2/sfxsids.hrc> +#include <comphelper/extract.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <svx/unoshape.hxx> +#include <svx/svdotext.hxx> +#include <svx/svdopath.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdograf.hxx> +#include <svx/ImageMapInfo.hxx> +#include <filter/msfilter/msdffimp.hxx> +#include <svl/instrm.hxx> +#include <editeng/outlobj.hxx> +#include <Outliner.hxx> +#include <comphelper/sequence.hxx> +#include <svx/svdogrp.hxx> +#include <o3tl/typed_flags_set.hxx> +#include <vcl/svapp.hxx> +#include <tools/debug.hxx> + +#include <anminfo.hxx> +#include "unoobj.hxx" +#include <unoprnms.hxx> +#include <unomodel.hxx> +#include <drawdoc.hxx> +#include <sdmod.hxx> +#include <sdpage.hxx> +#include <ViewShell.hxx> +#include <unopage.hxx> +#include <DrawDocShell.hxx> +#include <EffectMigration.hxx> + +using namespace ::sd; +using namespace ::com::sun::star; +using namespace ::com::sun::star::presentation; +using namespace ::com::sun::star::animations; + +using ::com::sun::star::uno::Any; +using ::com::sun::star::drawing::XShape; + +#define WID_EFFECT 1 +#define WID_SPEED 2 +#define WID_TEXTEFFECT 3 +#define WID_BOOKMARK 4 +#define WID_CLICKACTION 5 +#define WID_PLAYFULL 6 +#define WID_SOUNDFILE 7 +#define WID_SOUNDON 8 +#define WID_BLUESCREEN 9 +#define WID_VERB 10 +#define WID_DIMCOLOR 11 +#define WID_DIMHIDE 12 +#define WID_DIMPREV 13 +#define WID_PRESORDER 14 +#define WID_STYLE 15 +#define WID_ANIMPATH 16 +#define WID_IMAGEMAP 17 +#define WID_ISANIMATION 18 +#define WID_THAT_NEED_ANIMINFO 19 + +#define WID_ISEMPTYPRESOBJ 20 +#define WID_ISPRESOBJ 21 +#define WID_MASTERDEPEND 22 + +#define WID_NAVORDER 23 +#define WID_PLACEHOLDERTEXT 24 +#define WID_LEGACYFRAGMENT 25 + +#define IMPRESS_MAP_ENTRIES \ + { u"" UNO_NAME_OBJ_LEGACYFRAGMENT,WID_LEGACYFRAGMENT, cppu::UnoType<drawing::XShape>::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_ANIMATIONPATH, WID_ANIMPATH, cppu::UnoType<drawing::XShape>::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_BOOKMARK, WID_BOOKMARK, cppu::UnoType<OUString>::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_DIMCOLOR, WID_DIMCOLOR, cppu::UnoType<sal_Int32>::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_DIMHIDE, WID_DIMHIDE, cppu::UnoType<bool>::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_DIMPREV, WID_DIMPREV, cppu::UnoType<bool>::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_EFFECT, WID_EFFECT, cppu::UnoType<presentation::AnimationEffect>::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_ISEMPTYPRESOBJ,WID_ISEMPTYPRESOBJ, cppu::UnoType<bool>::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_ISPRESOBJ, WID_ISPRESOBJ, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY, 0},\ + { u"" UNO_NAME_OBJ_MASTERDEPENDENT,WID_MASTERDEPEND, cppu::UnoType<bool>::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_CLICKACTION, WID_CLICKACTION, cppu::UnoType<presentation::ClickAction>::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_PLAYFULL, WID_PLAYFULL, cppu::UnoType<bool>::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_PRESORDER, WID_PRESORDER, cppu::UnoType<sal_Int32>::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_STYLE, WID_STYLE, cppu::UnoType<style::XStyle>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0},\ + { u"" UNO_NAME_OBJ_SOUNDFILE, WID_SOUNDFILE, cppu::UnoType<OUString>::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_SOUNDON, WID_SOUNDON, cppu::UnoType<bool>::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_SPEED, WID_SPEED, cppu::UnoType<presentation::AnimationSpeed>::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_TEXTEFFECT, WID_TEXTEFFECT, cppu::UnoType<presentation::AnimationEffect>::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_BLUESCREEN, WID_BLUESCREEN, cppu::UnoType<sal_Int32>::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_VERB, WID_VERB, cppu::UnoType<sal_Int32>::get(), 0, 0},\ + { u"IsAnimation", WID_ISANIMATION, cppu::UnoType<bool>::get(), 0, 0},\ + { u"NavigationOrder", WID_NAVORDER, cppu::UnoType<sal_Int32>::get(), 0, 0},\ + { u"PlaceholderText", WID_PLACEHOLDERTEXT, cppu::UnoType<OUString>::get(), 0, 0},\ + { u"", 0, css::uno::Type(), 0, 0 } + + static const SfxItemPropertyMapEntry* lcl_GetImpress_SdXShapePropertyGraphicMap_Impl() + { + + static const SfxItemPropertyMapEntry aImpress_SdXShapePropertyGraphicMap_Impl[] = + { + { u"ImageMap", WID_IMAGEMAP, cppu::UnoType<container::XIndexContainer>::get(), 0, 0 }, + IMPRESS_MAP_ENTRIES + }; + return aImpress_SdXShapePropertyGraphicMap_Impl; + } + + static const SfxItemPropertyMapEntry* lcl_GetImpress_SdXShapePropertySimpleMap_Impl() + { + + static const SfxItemPropertyMapEntry aImpress_SdXShapePropertySimpleMap_Impl[] = + { + IMPRESS_MAP_ENTRIES + }; + return aImpress_SdXShapePropertySimpleMap_Impl; + } + + #define DRAW_MAP_ENTRIES\ + { u"" UNO_NAME_OBJ_BOOKMARK, WID_BOOKMARK, cppu::UnoType<OUString>::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_CLICKACTION, WID_CLICKACTION, cppu::UnoType<presentation::ClickAction>::get(),0, 0},\ + { u"" UNO_NAME_OBJ_STYLE, WID_STYLE, cppu::UnoType<style::XStyle>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0},\ + { u"NavigationOrder", WID_NAVORDER, cppu::UnoType<sal_Int32>::get(), 0, 0},\ + { u"", 0, css::uno::Type(), 0, 0 } + + static const SfxItemPropertyMapEntry* lcl_GetDraw_SdXShapePropertySimpleMap_Impl() + { + static const SfxItemPropertyMapEntry aDraw_SdXShapePropertyMap_Impl[] = + { + DRAW_MAP_ENTRIES + }; + return aDraw_SdXShapePropertyMap_Impl; + } + static const SfxItemPropertyMapEntry* lcl_GetDraw_SdXShapePropertyGraphicMap_Impl() + { + static const SfxItemPropertyMapEntry aDraw_SdXShapePropertyGraphicMap_Impl[] = + { + { u"ImageMap", WID_IMAGEMAP, cppu::UnoType<container::XIndexContainer>::get(), 0, 0 }, + DRAW_MAP_ENTRIES + }; + return aDraw_SdXShapePropertyGraphicMap_Impl; + } + static const SfxItemPropertyMapEntry* lcl_ImplGetShapePropertyMap( bool bImpress, bool bGraphicObj ) + { + const SfxItemPropertyMapEntry* pRet = nullptr; + if( bImpress ) + { + if( bGraphicObj ) + pRet = lcl_GetImpress_SdXShapePropertyGraphicMap_Impl(); + else + pRet = lcl_GetImpress_SdXShapePropertySimpleMap_Impl(); + } + else + { + if( bGraphicObj ) + pRet = lcl_GetDraw_SdXShapePropertyGraphicMap_Impl(); + else + pRet = lcl_GetDraw_SdXShapePropertySimpleMap_Impl(); + } + return pRet; + + } + static const SvxItemPropertySet* lcl_ImplGetShapePropertySet( bool bImpress, bool bGraphicObj ) + { + const SvxItemPropertySet* pRet = nullptr; + if( bImpress ) + { + if( bGraphicObj ) + { + static SvxItemPropertySet aImpress_SdXShapePropertyGraphicSet_Impl( lcl_GetImpress_SdXShapePropertyGraphicMap_Impl(), SdrObject::GetGlobalDrawObjectItemPool()); + pRet = &aImpress_SdXShapePropertyGraphicSet_Impl; + } + else + { + static SvxItemPropertySet aImpress_SdXShapePropertySet_Impl(lcl_GetImpress_SdXShapePropertySimpleMap_Impl(), SdrObject::GetGlobalDrawObjectItemPool()); + pRet = &aImpress_SdXShapePropertySet_Impl; + } + } + else + { + if( bGraphicObj ) + { + static SvxItemPropertySet aDraw_SdXShapePropertyGraphicSet_Impl(lcl_GetDraw_SdXShapePropertyGraphicMap_Impl(), SdrObject::GetGlobalDrawObjectItemPool()); + pRet = &aDraw_SdXShapePropertyGraphicSet_Impl; + } + else + { + static SvxItemPropertySet aDraw_SdXShapePropertySet_Impl( lcl_GetDraw_SdXShapePropertySimpleMap_Impl(), SdrObject::GetGlobalDrawObjectItemPool()); + pRet = &aDraw_SdXShapePropertySet_Impl; + } + } + return pRet; + } + static const SfxItemPropertyMapEntry* lcl_GetEmpty_SdXShapePropertyMap_Impl() + { + static const SfxItemPropertyMapEntry aEmpty_SdXShapePropertyMap_Impl[] = + { + { u"", 0, css::uno::Type(), 0, 0 } + }; + return aEmpty_SdXShapePropertyMap_Impl; + } + + static const SvxItemPropertySet* lcl_GetEmpty_SdXShapePropertySet_Impl() + { + static SvxItemPropertySet aEmptyPropSet( lcl_GetEmpty_SdXShapePropertyMap_Impl(), SdrObject::GetGlobalDrawObjectItemPool() ); + return &aEmptyPropSet; + } +const SvEventDescription* ImplGetSupportedMacroItems() +{ + static const SvEventDescription aMacroDescriptionsImpl[] = + { + { SvMacroItemId::OnMouseOver, "OnMouseOver" }, + { SvMacroItemId::OnMouseOut, "OnMouseOut" }, + { SvMacroItemId::NONE, nullptr } + }; + + return aMacroDescriptionsImpl; +} + +SdXShape::SdXShape(SvxShape* pShape, SdXImpressDocument* pModel) +: mpShape( pShape ), + mpPropSet( pModel? + lcl_ImplGetShapePropertySet(pModel->IsImpressDocument(), pShape->getShapeKind() == SdrObjKind::Graphic ) + : lcl_GetEmpty_SdXShapePropertySet_Impl() ), + mpMap( pModel? + lcl_ImplGetShapePropertyMap(pModel->IsImpressDocument(), pShape->getShapeKind() == SdrObjKind::Graphic ) + : lcl_GetEmpty_SdXShapePropertyMap_Impl() ), + mpModel(pModel) +{ + + pShape->setMaster( this ); +} + +SdXShape::~SdXShape() noexcept +{ +} + +void SdXShape::dispose() +{ + mpShape->setMaster( nullptr ); + delete this; +} + +uno::Any SAL_CALL SdXShape::queryInterface( const uno::Type & rType ) +{ + return mpShape->queryInterface( rType ); +} + +void SAL_CALL SdXShape::acquire() noexcept +{ + mpShape->acquire(); +} + +void SAL_CALL SdXShape::release() noexcept +{ + mpShape->release(); +} + +bool SdXShape::queryAggregation( const css::uno::Type & rType, css::uno::Any& aAny ) +{ + if( mpModel && mpModel ->IsImpressDocument() ) + { + if( rType == cppu::UnoType<document::XEventsSupplier>::get()) + { + aAny <<= uno::Reference< document::XEventsSupplier >(this); + return true; + } + } + + return false; +} + +uno::Sequence< uno::Type > SAL_CALL SdXShape::getTypes() +{ + if( mpModel && !mpModel->IsImpressDocument() ) + { + return mpShape->_getTypes(); + } + else + { + SdrObjKind nObjId = mpShape->getShapeKind(); + uno::Sequence< uno::Type > aTypes; + SdTypesCache& gImplTypesCache = SD_MOD()->gImplTypesCache; + SdTypesCache::iterator aIter( gImplTypesCache.find( nObjId ) ); + if( aIter == gImplTypesCache.end() ) + { + aTypes = mpShape->_getTypes(); + sal_uInt32 nCount = aTypes.getLength(); + aTypes.realloc( nCount+1 ); + aTypes.getArray()[nCount] = cppu::UnoType<lang::XTypeProvider>::get(); + + gImplTypesCache.insert(std::make_pair(nObjId, aTypes)); + } + else + { + // use the already computed implementation id + aTypes = (*aIter).second; + } + return aTypes; + } +} + +// XPropertyState +beans::PropertyState SAL_CALL SdXShape::getPropertyState( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + if( mpPropSet->getPropertyMapEntry(PropertyName) ) + { + return beans::PropertyState_DIRECT_VALUE; + } + else + { + SdrObject* pObj = mpShape->GetSdrObject(); + if( pObj == nullptr || ( pObj->getSdrPageFromSdrObject()->IsMasterPage() && pObj->IsEmptyPresObj() ) ) + return beans::PropertyState_DEFAULT_VALUE; + + return mpShape->_getPropertyState( PropertyName ); + } +} + +void SAL_CALL SdXShape::setPropertyToDefault( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + if( mpPropSet->getPropertyMapEntry(PropertyName) ) + { + return; + } + else + { + mpShape->_setPropertyToDefault(PropertyName); + } +} + +uno::Any SAL_CALL SdXShape::getPropertyDefault( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + + if( mpPropSet->getPropertyMapEntry(aPropertyName) ) + { + return getPropertyValue( aPropertyName ); + } + else + { + uno::Any aRet( mpShape->_getPropertyDefault(aPropertyName) ); + return aRet; + } +} + +//XPropertySet +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL SdXShape::getPropertySetInfo() +{ + SfxItemPropertyMapEntry const * nObjId = mpShape->getPropertyMapEntries(); + css::uno::Reference<css::beans::XPropertySetInfo> pInfo; + + SdExtPropertySetInfoCache& rCache = (mpModel && mpModel->IsImpressDocument()) ? + SD_MOD()->gImplImpressPropertySetInfoCache : SD_MOD()->gImplDrawPropertySetInfoCache; + + SdExtPropertySetInfoCache::iterator aIter( rCache.find( nObjId ) ); + if( aIter == rCache.end() ) + { + uno::Reference< beans::XPropertySetInfo > xInfo( mpShape->_getPropertySetInfo() ); + pInfo = new SfxExtItemPropertySetInfo( mpMap, xInfo->getProperties() ); + + rCache.insert(std::make_pair(nObjId, pInfo)); + } + else + { + // use the already computed implementation id + pInfo = (*aIter).second; + } + + return pInfo; +} + +void SAL_CALL SdXShape::setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + + const SfxItemPropertyMapEntry* pEntry = mpPropSet->getPropertyMapEntry(aPropertyName); + + if( pEntry ) + { + SdrObject* pObj = mpShape->GetSdrObject(); + if( pObj ) + { + SdAnimationInfo* pInfo = GetAnimationInfo(pEntry->nWID <= WID_THAT_NEED_ANIMINFO); + + switch(pEntry->nWID) + { + case WID_NAVORDER: + { + sal_Int32 nNavOrder = 0; + if(!(aValue >>= nNavOrder)) + throw lang::IllegalArgumentException(); + + SdrObjList* pObjList = pObj->getParentSdrObjListFromSdrObject(); + if( pObjList ) + pObjList->SetObjectNavigationPosition( *pObj, (nNavOrder < 0) ? SAL_MAX_UINT32 : static_cast< sal_uInt32 >( nNavOrder ) ); + break; + } + + case WID_EFFECT: + { + AnimationEffect eEffect; + if(!(aValue >>= eEffect)) + throw lang::IllegalArgumentException(); + + EffectMigration::SetAnimationEffect( mpShape, eEffect ); + break; + } + case WID_TEXTEFFECT: + { + AnimationEffect eEffect; + if(!(aValue >>= eEffect)) + throw lang::IllegalArgumentException(); + + EffectMigration::SetTextAnimationEffect( mpShape, eEffect ); + break; + } + case WID_SPEED: + { + AnimationSpeed eSpeed; + if(!(aValue>>=eSpeed)) + throw lang::IllegalArgumentException(); + + EffectMigration::SetAnimationSpeed( mpShape, eSpeed ); + break; + } + case WID_ISANIMATION: + { + bool bIsAnimation(false); + + if(!(aValue >>= bIsAnimation)) + { + throw lang::IllegalArgumentException(); + } + + if(bIsAnimation) + { + SdrObjGroup* pGroup = dynamic_cast< SdrObjGroup* >(pObj); + SdPage* pPage = pGroup ? dynamic_cast< SdPage* >(pGroup->getSdrPageFromSdrObject()) : nullptr; + + if (pPage) + { + // #i42894# Animated Group object, migrate that effect + EffectMigration::CreateAnimatedGroup(*pGroup, *pPage); + + // #i42894# unfortunately when doing this all group members have to + // be moved to the page as direct members, else the currently + // available forms of animation do not work. If it succeeds, + // the group is empty and can be removed and deleted + if(!pGroup->GetSubList()->GetObjCount()) + { + pPage->NbcRemoveObject(pGroup->GetOrdNum()); + + // always use SdrObject::Free(...) for SdrObjects (!) + SdrObject* pTemp(pGroup); + SdrObject::Free(pTemp); + } + } + } + //pInfo->mbIsMovie = bIsAnimation; + break; + } + case WID_BOOKMARK: + { + OUString aString; + if(!(aValue >>= aString)) + throw lang::IllegalArgumentException(); + + pInfo->SetBookmark( SdDrawPage::getUiNameFromPageApiName( aString ) ); + break; + } + case WID_CLICKACTION: + ::cppu::any2enum< presentation::ClickAction >( pInfo->meClickAction, aValue); + break; + +// TODO: WID_PLAYFULL: + case WID_SOUNDFILE: + { + OUString aString; + if(!(aValue >>= aString)) + throw lang::IllegalArgumentException(); + pInfo->maSoundFile = aString; + EffectMigration::UpdateSoundEffect( mpShape, pInfo ); + break; + } + + case WID_SOUNDON: + { + if( !(aValue >>= pInfo->mbSoundOn) ) + throw lang::IllegalArgumentException(); + EffectMigration::UpdateSoundEffect( mpShape, pInfo ); + break; + } + case WID_VERB: + { + sal_Int32 nVerb = 0; + if(!(aValue >>= nVerb)) + throw lang::IllegalArgumentException(); + + pInfo->mnVerb = static_cast<sal_uInt16>(nVerb); + break; + } + case WID_DIMCOLOR: + { + sal_Int32 nColor = 0; + + if( !(aValue >>= nColor) ) + throw lang::IllegalArgumentException(); + + EffectMigration::SetDimColor( mpShape, nColor ); + break; + } + case WID_DIMHIDE: + { + bool bDimHide = false; + if( !(aValue >>= bDimHide) ) + throw lang::IllegalArgumentException(); + + EffectMigration::SetDimHide( mpShape, bDimHide ); + break; + } + case WID_DIMPREV: + { + bool bDimPrevious = false; + if( !(aValue >>= bDimPrevious) ) + throw lang::IllegalArgumentException(); + + EffectMigration::SetDimPrevious( mpShape, bDimPrevious ); + break; + } + case WID_PRESORDER: + { + sal_Int32 nNewPos = 0; + if( !(aValue >>= nNewPos) ) + throw lang::IllegalArgumentException(); + + EffectMigration::SetPresentationOrder( mpShape, nNewPos ); + break; + } + case WID_STYLE: + SetStyleSheet( aValue ); + break; + case WID_ISEMPTYPRESOBJ: + SetEmptyPresObj( ::cppu::any2bool(aValue) ); + break; + case WID_MASTERDEPEND: + SetMasterDepend( ::cppu::any2bool(aValue) ); + break; + + case WID_LEGACYFRAGMENT: + { + uno::Reference< io::XInputStream > xInputStream; + aValue >>= xInputStream; + if( xInputStream.is() ) + { + SvInputStream aStream( xInputStream ); + SdrObject* pObject = mpShape->GetSdrObject(); + SvxMSDffManager::ReadObjText( aStream, pObject ); + } + } + break; + + case WID_ANIMPATH: + { + uno::Reference< drawing::XShape > xShape( aValue, uno::UNO_QUERY ); + SdrPathObj* pObj2 = xShape.is() ? dynamic_cast<SdrPathObj*>(SdrObject::getSdrObjectFromXShape(xShape)) : nullptr; + + if( pObj2 == nullptr ) + throw lang::IllegalArgumentException(); + + EffectMigration::SetAnimationPath( mpShape, pObj2 ); + break; + } + case WID_IMAGEMAP: + { + SdDrawDocument* pDoc = mpModel?mpModel->GetDoc():nullptr; + if( pDoc ) + { + ImageMap aImageMap; + uno::Reference< uno::XInterface > xImageMap; + aValue >>= xImageMap; + + if( !xImageMap.is() || !SvUnoImageMap_fillImageMap( xImageMap, aImageMap ) ) + throw lang::IllegalArgumentException(); + + SvxIMapInfo* pIMapInfo = SvxIMapInfo::GetIMapInfo(pObj); + if( pIMapInfo ) + { + // replace existing image map + pIMapInfo->SetImageMap( aImageMap ); + } + else + { + // insert new user data with image map + pObj->AppendUserData(std::unique_ptr<SdrObjUserData>(new SvxIMapInfo(aImageMap) )); + } + } + } + break; + } + } + } + else + { + mpShape->_setPropertyValue(aPropertyName, aValue); + } + + if( mpModel ) + mpModel->SetModified(); +} + +css::uno::Any SAL_CALL SdXShape::getPropertyValue( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + uno::Any aRet; + + const SfxItemPropertyMapEntry* pEntry = mpPropSet->getPropertyMapEntry(PropertyName); + + if( pEntry && mpShape->GetSdrObject() ) + { + SdAnimationInfo* pInfo = GetAnimationInfo(); + + switch(pEntry->nWID) + { + case WID_NAVORDER: + { + const sal_uInt32 nNavOrder = mpShape->GetSdrObject()->GetNavigationPosition(); + aRet <<= nNavOrder == SAL_MAX_UINT32 ? static_cast<sal_Int32>(-1) : static_cast< sal_Int32 >(nNavOrder); + } + break; + case WID_EFFECT: + aRet <<= EffectMigration::GetAnimationEffect( mpShape ); + break; + case WID_TEXTEFFECT: + aRet <<= EffectMigration::GetTextAnimationEffect( mpShape ); + break; + case WID_ISPRESOBJ: + aRet <<= IsPresObj(); + break; + case WID_ISEMPTYPRESOBJ: + aRet <<= IsEmptyPresObj(); + break; + case WID_MASTERDEPEND: + aRet <<= IsMasterDepend(); + break; + case WID_SPEED: + aRet <<= EffectMigration::GetAnimationSpeed( mpShape ); + break; + case WID_ISANIMATION: + aRet <<= (pInfo && pInfo->mbIsMovie); + break; + case WID_PLACEHOLDERTEXT: + aRet <<= GetPlaceholderText(); + break; + case WID_BOOKMARK: + { + OUString aString; + SdDrawDocument* pDoc = mpModel ? mpModel->GetDoc() : nullptr; + if (pInfo && pDoc) + { + // is the bookmark a page? + bool bIsMasterPage; + if(pDoc->GetPageByName( pInfo->GetBookmark(), bIsMasterPage ) != SDRPAGE_NOTFOUND) + { + aString = SdDrawPage::getPageApiNameFromUiName( pInfo->GetBookmark() ); + } + else + { + aString = pInfo->GetBookmark() ; + sal_Int32 nPos = aString.lastIndexOf( '#' ); + if( nPos >= 0 ) + { + OUString aURL( aString.copy( 0, nPos+1 ) ); + OUString aName( aString.copy( nPos+1 ) ); + if(pDoc->GetPageByName( aName, bIsMasterPage ) != SDRPAGE_NOTFOUND) + { + aURL += SdDrawPage::getPageApiNameFromUiName( aName ); + aString = aURL; + } + } + } + } + + aRet <<= aString; + break; + } + case WID_CLICKACTION: + aRet <<= ( pInfo?pInfo->meClickAction:presentation::ClickAction_NONE ); + break; + case WID_PLAYFULL: + aRet <<= ( pInfo && pInfo->mbPlayFull ); + break; + case WID_SOUNDFILE: + aRet <<= EffectMigration::GetSoundFile( mpShape ); + break; + case WID_SOUNDON: + aRet <<= EffectMigration::GetSoundOn( mpShape ); + break; + case WID_BLUESCREEN: + aRet <<= pInfo ? pInfo->maBlueScreen : Color(0x00ffffff); + break; + case WID_VERB: + aRet <<= static_cast<sal_Int32>( pInfo?pInfo->mnVerb:0 ); + break; + case WID_DIMCOLOR: + aRet <<= EffectMigration::GetDimColor( mpShape ); + break; + case WID_DIMHIDE: + aRet <<= EffectMigration::GetDimHide( mpShape ); + break; + case WID_DIMPREV: + aRet <<= EffectMigration::GetDimPrevious( mpShape ); + break; + case WID_PRESORDER: + aRet <<= EffectMigration::GetPresentationOrder( mpShape ); + break; + case WID_STYLE: + aRet = GetStyleSheet(); + break; + case WID_IMAGEMAP: + { + uno::Reference< uno::XInterface > xImageMap; + + SdDrawDocument* pDoc = mpModel?mpModel->GetDoc():nullptr; + if( pDoc ) + { + + SvxIMapInfo* pIMapInfo = SvxIMapInfo::GetIMapInfo(mpShape->GetSdrObject()); + if( pIMapInfo ) + { + const ImageMap& rIMap = pIMapInfo->GetImageMap(); + xImageMap = SvUnoImageMap_createInstance( rIMap, ImplGetSupportedMacroItems() ); + } + else + { + xImageMap = SvUnoImageMap_createInstance(); + } + } + + aRet <<= uno::Reference< container::XIndexContainer >::query( xImageMap ); + break; + } + } + } + else + { + aRet = mpShape->_getPropertyValue(PropertyName); + } + + return aRet; +} + +/** */ +SdAnimationInfo* SdXShape::GetAnimationInfo( bool bCreate ) const +{ + SdAnimationInfo* pInfo = nullptr; + + SdrObject* pObj = mpShape->GetSdrObject(); + if(pObj) + pInfo = SdDrawDocument::GetShapeUserData(*pObj, bCreate); + + return pInfo; +} + +uno::Sequence< OUString > SAL_CALL SdXShape::getSupportedServiceNames() +{ + std::vector<std::u16string_view> aAdd{ u"com.sun.star.presentation.Shape", + u"com.sun.star.document.LinkTarget" }; + + SdrObject* pObj = mpShape->GetSdrObject(); + if(pObj && pObj->GetObjInventor() == SdrInventor::Default ) + { + SdrObjKind nInventor = pObj->GetObjIdentifier(); + switch( nInventor ) + { + case SdrObjKind::TitleText: + aAdd.emplace_back(u"com.sun.star.presentation.TitleTextShape"); + break; + case SdrObjKind::OutlineText: + aAdd.emplace_back(u"com.sun.star.presentation.OutlinerShape"); + break; + default: ; + } + } + return comphelper::concatSequences(mpShape->_getSupportedServiceNames(), aAdd); +} + +/** checks if this is a presentation object + */ +bool SdXShape::IsPresObj() const +{ + SdrObject* pObj = mpShape->GetSdrObject(); + if(pObj) + { + SdPage* pPage = dynamic_cast<SdPage* >(pObj->getSdrPageFromSdrObject()); + if(pPage) + return pPage->GetPresObjKind(pObj) != PresObjKind::NONE; + } + return false; +} + +/** checks if this presentation object is empty + */ +bool SdXShape::IsEmptyPresObj() const +{ + SdrObject* pObj = mpShape->GetSdrObject(); + if( (pObj != nullptr) && pObj->IsEmptyPresObj() ) + { + // check if the object is in edit, then if it's temporarily not empty + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( pObj ); + if( pTextObj == nullptr ) + return true; + + return !pTextObj->CanCreateEditOutlinerParaObject(); + } + + return false; +} + +OUString SdXShape::GetPlaceholderText() const +{ + // only possible if this actually *is* a presentation object + if( !IsPresObj() ) + return OUString(); + + SdrObject* pObj = mpShape->GetSdrObject(); + if( pObj == nullptr ) + return OUString(); + + SdPage* pPage = dynamic_cast< SdPage* >(pObj->getSdrPageFromSdrObject()); + DBG_ASSERT( pPage, "no page?" ); + if( pPage == nullptr ) + return OUString(); + + return pPage->GetPresObjText( pPage->GetPresObjKind(pObj) ); +} + +/** sets/reset the empty status of a presentation object +*/ +void SdXShape::SetEmptyPresObj(bool bEmpty) +{ + // only possible if this actually *is* a presentation object + if( !IsPresObj() ) + return; + + SdrObject* pObj = mpShape->GetSdrObject(); + if( pObj == nullptr ) + return; + + if( pObj->IsEmptyPresObj() == bEmpty ) + return; + + if(!bEmpty) + { + OutlinerParaObject* pOutlinerParaObject = pObj->GetOutlinerParaObject(); + const bool bVertical = pOutlinerParaObject && pOutlinerParaObject->IsEffectivelyVertical(); + + // really delete SdrOutlinerObj at pObj + pObj->NbcSetOutlinerParaObject(std::nullopt); + if( bVertical ) + if (auto pTextObj = dynamic_cast<SdrTextObj*>( pObj ) ) + pTextObj->SetVerticalWriting( true ); + + SdrGrafObj* pGraphicObj = dynamic_cast<SdrGrafObj*>( pObj ); + if( pGraphicObj ) + { + Graphic aEmpty; + pGraphicObj->SetGraphic(aEmpty); + } + else + { + SdrOle2Obj* pOleObj = dynamic_cast< SdrOle2Obj* >( pObj ); + if( pOleObj ) + { + pOleObj->ClearGraphic(); + } + } + } + else + { + // now set an empty OutlinerParaObject at pObj without + // any content but with the style of the old OutlinerParaObjects + // first paragraph + do + { + SdDrawDocument* pDoc = mpModel?mpModel->GetDoc():nullptr; + DBG_ASSERT( pDoc, "no document?" ); + if( pDoc == nullptr) + break; + + SdOutliner* pOutliner = pDoc->GetInternalOutliner(); + DBG_ASSERT( pOutliner, "no outliner?" ); + if( pOutliner == nullptr ) + break; + + SdPage* pPage = dynamic_cast< SdPage* >(pObj->getSdrPageFromSdrObject()); + DBG_ASSERT( pPage, "no page?" ); + if( pPage == nullptr ) + break; + + OutlinerParaObject* pOutlinerParaObject = pObj->GetOutlinerParaObject(); + pOutliner->SetText( *pOutlinerParaObject ); + const bool bVertical = pOutliner->IsVertical(); + + pOutliner->Clear(); + pOutliner->SetVertical( bVertical ); + pOutliner->SetStyleSheetPool( static_cast<SfxStyleSheetPool*>(pDoc->GetStyleSheetPool()) ); + pOutliner->SetStyleSheet( 0, pPage->GetTextStyleSheetForObject( pObj ) ); + pOutliner->Insert( pPage->GetPresObjText( pPage->GetPresObjKind(pObj) ) ); + pObj->SetOutlinerParaObject( pOutliner->CreateParaObject() ); + pOutliner->Clear(); + } + while(false); + } + + pObj->SetEmptyPresObj(bEmpty); +} + +bool SdXShape::IsMasterDepend() const noexcept +{ + SdrObject* pObj = mpShape->GetSdrObject(); + return pObj && pObj->GetUserCall() != nullptr; +} + +void SdXShape::SetMasterDepend( bool bDepend ) noexcept +{ + if( IsMasterDepend() == bDepend ) + return; + + SdrObject* pObj = mpShape->GetSdrObject(); + if( pObj ) + { + if( bDepend ) + { + SdPage* pPage = dynamic_cast< SdPage* >(pObj->getSdrPageFromSdrObject()); + pObj->SetUserCall( pPage ); + } + else + { + pObj->SetUserCall( nullptr ); + } + } +} + +void SdXShape::SetStyleSheet( const uno::Any& rAny ) +{ + SdrObject* pObj = mpShape->GetSdrObject(); + if( pObj == nullptr ) + throw beans::UnknownPropertyException(); + + uno::Reference< style::XStyle > xStyle( rAny, uno::UNO_QUERY ); + SfxStyleSheet* pStyleSheet = SfxUnoStyleSheet::getUnoStyleSheet( xStyle ); + + const SfxStyleSheet* pOldStyleSheet = pObj->GetStyleSheet(); + if( pOldStyleSheet == pStyleSheet ) + return; + + if( pStyleSheet == nullptr || (pStyleSheet->GetFamily() != SfxStyleFamily::Para && pStyleSheet->GetFamily() != SfxStyleFamily::Page) ) + throw lang::IllegalArgumentException(); + + pObj->SetStyleSheet( pStyleSheet, false ); + + SdDrawDocument* pDoc = mpModel? mpModel->GetDoc() : nullptr; + if( pDoc ) + { + ::sd::DrawDocShell* pDocSh = pDoc->GetDocSh(); + ::sd::ViewShell* pViewSh = pDocSh ? pDocSh->GetViewShell() : nullptr; + + if( pViewSh ) + pViewSh->GetViewFrame()->GetBindings().Invalidate( SID_STYLE_FAMILY2 ); + } +} + +uno::Any SdXShape::GetStyleSheet() const +{ + SdrObject* pObj = mpShape->GetSdrObject(); + if( pObj == nullptr ) + throw beans::UnknownPropertyException(); + + SfxStyleSheet* pStyleSheet = pObj->GetStyleSheet(); + // it is possible for shapes inside a draw to have a presentation style + // but we don't want this for the api + if( (pStyleSheet == nullptr) || ((pStyleSheet->GetFamily() != SfxStyleFamily::Para) && !mpModel->IsImpressDocument()) ) + return Any(); + + return Any( uno::Reference< style::XStyle >( dynamic_cast< SfxUnoStyleSheet* >( pStyleSheet ) ) ); +} + +class SdUnoEventsAccess : public cppu::WeakImplHelper< css::container::XNameReplace, css::lang::XServiceInfo > +{ +private: + SdXShape* mpShape; + +public: + explicit SdUnoEventsAccess(SdXShape* pShape) noexcept; + + // XNameReplace + virtual void SAL_CALL replaceByName( const OUString& aName, const css::uno::Any& aElement ) override; + + // XNameAccess + virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames( ) override; + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType( ) override; + virtual sal_Bool SAL_CALL hasElements( ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; +}; + +// XEventsSupplier +uno::Reference< container::XNameReplace > SAL_CALL SdXShape::getEvents( ) +{ + return new SdUnoEventsAccess( this ); +} + +constexpr OUStringLiteral gaStrOnClick( u"OnClick" ); +constexpr OUStringLiteral gaStrServiceName( u"com.sun.star.documents.Events" ); +constexpr OUStringLiteral gaStrEventType( u"EventType" ); +constexpr OUStringLiteral gaStrPresentation( u"Presentation" ); +constexpr OUStringLiteral gaStrLibrary(u"Library"); +constexpr OUStringLiteral gaStrMacroName(u"MacroName"); +constexpr OUStringLiteral gaStrClickAction( u"ClickAction" ); +constexpr OUStringLiteral gaStrBookmark( u"Bookmark" ); +constexpr OUStringLiteral gaStrEffect( u"Effect" ); +constexpr OUStringLiteral gaStrPlayFull( u"PlayFull" ); +constexpr OUStringLiteral gaStrVerb( u"Verb" ); +constexpr OUStringLiteral gaStrSoundURL( u"SoundURL" ); +constexpr OUStringLiteral gaStrSpeed( u"Speed" ); +constexpr OUStringLiteral gaStrStarBasic( u"StarBasic" ); +constexpr OUStringLiteral gaStrScript( u"Script" ); + +SdUnoEventsAccess::SdUnoEventsAccess( SdXShape* pShape ) noexcept + : mpShape( pShape ) +{ +} + +namespace { + +enum class FoundFlags { + NONE = 0x0000, + ClickAction = 0x0001, + Bookmark = 0x0002, + Effect = 0x0004, + PlayFull = 0x0008, + Verb = 0x0010, + SoundUrl = 0x0020, + Speed = 0x0040, + EventType = 0x0080, + Macro = 0x0100, + Library = 0x0200, +}; + +} + +namespace o3tl { + template<> struct typed_flags<FoundFlags> : is_typed_flags<FoundFlags, 0x03ff> {}; +} + +static void clearEventsInAnimationInfo( SdAnimationInfo* pInfo ) +{ + pInfo->SetBookmark( "" ); + pInfo->mbSecondSoundOn = false; + pInfo->mbSecondPlayFull = false; + pInfo->meClickAction = presentation::ClickAction_NONE; + pInfo->meSecondEffect = presentation::AnimationEffect_NONE; + pInfo->meSecondSpeed = presentation::AnimationSpeed_MEDIUM; + pInfo->mnVerb = 0; +} + +// XNameReplace +void SAL_CALL SdUnoEventsAccess::replaceByName( const OUString& aName, const uno::Any& aElement ) +{ + if( mpShape == nullptr || aName != gaStrOnClick ) + throw container::NoSuchElementException(); + + uno::Sequence< beans::PropertyValue > aProperties; + if( !aElement.hasValue() || aElement.getValueType() != getElementType() || !(aElement >>= aProperties) ) + throw lang::IllegalArgumentException(); + + FoundFlags nFound = FoundFlags::NONE; + + OUString aStrEventType; + presentation::ClickAction eClickAction = presentation::ClickAction_NONE; + presentation::AnimationEffect eEffect = presentation::AnimationEffect_NONE; + presentation::AnimationSpeed eSpeed = presentation::AnimationSpeed_MEDIUM; + OUString aStrSoundURL; + bool bPlayFull = false; + sal_Int32 nVerb = 0; + OUString aStrMacro; + OUString aStrLibrary; + OUString aStrBookmark; + + for( const beans::PropertyValue& rProperty : std::as_const(aProperties) ) + { + if( !( nFound & FoundFlags::EventType ) && rProperty.Name == gaStrEventType ) + { + if( rProperty.Value >>= aStrEventType ) + { + nFound |= FoundFlags::EventType; + continue; + } + } + else if( !( nFound & FoundFlags::ClickAction ) && rProperty.Name == gaStrClickAction ) + { + if( rProperty.Value >>= eClickAction ) + { + nFound |= FoundFlags::ClickAction; + continue; + } + } + else if( !( nFound & FoundFlags::Macro ) && ( rProperty.Name == gaStrMacroName || rProperty.Name == gaStrScript ) ) + { + if( rProperty.Value >>= aStrMacro ) + { + nFound |= FoundFlags::Macro; + continue; + } + } + else if( !( nFound & FoundFlags::Library ) && rProperty.Name == gaStrLibrary ) + { + if( rProperty.Value >>= aStrLibrary ) + { + nFound |= FoundFlags::Library; + continue; + } + } + else if( !( nFound & FoundFlags::Effect ) && rProperty.Name == gaStrEffect ) + { + if( rProperty.Value >>= eEffect ) + { + nFound |= FoundFlags::Effect; + continue; + } + } + else if( !( nFound & FoundFlags::Bookmark ) && rProperty.Name == gaStrBookmark ) + { + if( rProperty.Value >>= aStrBookmark ) + { + nFound |= FoundFlags::Bookmark; + continue; + } + } + else if( !( nFound & FoundFlags::Speed ) && rProperty.Name == gaStrSpeed ) + { + if( rProperty.Value >>= eSpeed ) + { + nFound |= FoundFlags::Speed; + continue; + } + } + else if( !( nFound & FoundFlags::SoundUrl ) && rProperty.Name == gaStrSoundURL ) + { + if( rProperty.Value >>= aStrSoundURL ) + { + nFound |= FoundFlags::SoundUrl; + continue; + } + } + else if( !( nFound & FoundFlags::PlayFull ) && rProperty.Name == gaStrPlayFull ) + { + if( rProperty.Value >>= bPlayFull ) + { + nFound |= FoundFlags::PlayFull; + continue; + } + } + else if( !( nFound & FoundFlags::Verb ) && rProperty.Name == gaStrVerb ) + { + if( rProperty.Value >>= nVerb ) + { + nFound |= FoundFlags::Verb; + continue; + } + } + + throw lang::IllegalArgumentException(); + } + + bool bOk = false; + do + { + if( !( nFound & FoundFlags::EventType ) ) + break; + + if( aStrEventType == gaStrPresentation ) + { + if( !( nFound & FoundFlags::ClickAction ) ) + break; + + SdAnimationInfo* pInfo = mpShape->GetAnimationInfo(); + if( presentation::ClickAction_NONE == eClickAction && nullptr == pInfo ) + { + bOk = true; + break; + } + + if( nullptr == pInfo ) + pInfo = mpShape->GetAnimationInfo( true ); + + DBG_ASSERT( pInfo, "shape animation info could not be created!" ); + if( nullptr == pInfo ) + break; + + clearEventsInAnimationInfo( pInfo ); + pInfo->meClickAction = eClickAction; + + switch( eClickAction ) + { + case presentation::ClickAction_NONE: + case presentation::ClickAction_PREVPAGE: + case presentation::ClickAction_NEXTPAGE: + case presentation::ClickAction_FIRSTPAGE: + case presentation::ClickAction_LASTPAGE: + case presentation::ClickAction_INVISIBLE: + case presentation::ClickAction_STOPPRESENTATION: + { + bOk = true; + } + break; + + case presentation::ClickAction_PROGRAM: + case presentation::ClickAction_BOOKMARK: + case presentation::ClickAction_DOCUMENT: + if( nFound & FoundFlags::Bookmark ) + { + if( eClickAction == presentation::ClickAction_BOOKMARK ) + { + aStrBookmark = getUiNameFromPageApiNameImpl( aStrBookmark ); + } + else if( eClickAction == presentation::ClickAction_DOCUMENT ) + { + sal_Int32 nPos = aStrBookmark.lastIndexOf( '#' ); + if( nPos >= 0 ) + { + OUString aURL = aStrBookmark.subView( 0, nPos+1 ) + + getUiNameFromPageApiNameImpl( aStrBookmark.copy( nPos+1 ) ); + aStrBookmark = aURL; + } + } + + pInfo->SetBookmark( aStrBookmark ); + bOk = true; + } + break; + + case presentation::ClickAction_MACRO: + if( nFound & FoundFlags::Macro ) + { + pInfo->SetBookmark( aStrMacro ); + bOk = true; + } + break; + + case presentation::ClickAction_VERB: + if( nFound & FoundFlags::Verb ) + { + pInfo->mnVerb = static_cast<sal_uInt16>(nVerb); + bOk = true; + } + break; + + case presentation::ClickAction_VANISH: + if( !( nFound & FoundFlags::Effect ) ) + break; + + pInfo->meSecondEffect = eEffect; + pInfo->meSecondSpeed = nFound & FoundFlags::Speed ? eSpeed : presentation::AnimationSpeed_MEDIUM; + + bOk = true; + + [[fallthrough]]; + + case presentation::ClickAction_SOUND: + if( nFound & FoundFlags::SoundUrl ) + { + pInfo->SetBookmark( aStrSoundURL ); + if( eClickAction != presentation::ClickAction_SOUND ) + pInfo->mbSecondSoundOn = !aStrSoundURL.isEmpty(); + pInfo->mbSecondPlayFull = (nFound & FoundFlags::PlayFull) && bPlayFull; + + bOk = true; + } + break; + default: + break; + } + } + else + { + SdAnimationInfo* pInfo = mpShape->GetAnimationInfo( true ); + + DBG_ASSERT( pInfo, "shape animation info could not be created!" ); + if( nullptr == pInfo ) + break; + + clearEventsInAnimationInfo( pInfo ); + pInfo->meClickAction = presentation::ClickAction_MACRO; + + if ( SfxApplication::IsXScriptURL( aStrMacro ) ) + { + pInfo->SetBookmark( aStrMacro ); + } + else + { + sal_Int32 nIdx{ 0 }; + const std::u16string_view aLibName = o3tl::getToken(aStrMacro, 0, '.', nIdx); + const std::u16string_view aModulName = o3tl::getToken(aStrMacro, 0, '.', nIdx); + const std::u16string_view aMacroName = o3tl::getToken(aStrMacro, 0, '.', nIdx); + + OUStringBuffer sBuffer( + OUString::Concat(aMacroName) + OUStringChar('.') + aModulName + OUStringChar('.') + aLibName + OUStringChar('.') ); + + if ( aStrLibrary == "StarOffice" ) + { + sBuffer.append( "BASIC" ); + } + else + { + sBuffer.append( aStrLibrary ); + } + + pInfo->SetBookmark( sBuffer.makeStringAndClear() ); + } + bOk = true; + } + } + while(false); + + if( !bOk ) + throw lang::IllegalArgumentException(); +} + +// XNameAccess +uno::Any SAL_CALL SdUnoEventsAccess::getByName( const OUString& aName ) +{ + if( mpShape == nullptr || aName != gaStrOnClick ) + throw container::NoSuchElementException(); + + SdAnimationInfo* pInfo = mpShape->GetAnimationInfo(); + + presentation::ClickAction eClickAction = presentation::ClickAction_NONE; + if( pInfo ) + eClickAction = pInfo->meClickAction; + + sal_Int32 nPropertyCount = 2; + switch( eClickAction ) + { + case presentation::ClickAction_NONE: + case presentation::ClickAction_PREVPAGE: + case presentation::ClickAction_NEXTPAGE: + case presentation::ClickAction_FIRSTPAGE: + case presentation::ClickAction_LASTPAGE: + case presentation::ClickAction_INVISIBLE: + case presentation::ClickAction_STOPPRESENTATION: + break; + case presentation::ClickAction_PROGRAM: + case presentation::ClickAction_VERB: + case presentation::ClickAction_BOOKMARK: + case presentation::ClickAction_DOCUMENT: + case presentation::ClickAction_MACRO: + if ( !SfxApplication::IsXScriptURL( pInfo->GetBookmark() ) ) + nPropertyCount += 1; + break; + + case presentation::ClickAction_SOUND: + nPropertyCount += 2; + break; + + case presentation::ClickAction_VANISH: + nPropertyCount += 4; + break; + default: + break; + } + + uno::Sequence< beans::PropertyValue > aProperties( nPropertyCount ); + beans::PropertyValue* pProperties = aProperties.getArray(); + + uno::Any aAny; + + if( eClickAction == presentation::ClickAction_MACRO ) + { + if ( SfxApplication::IsXScriptURL( pInfo->GetBookmark() ) ) + { + // Scripting Framework URL + aAny <<= OUString(gaStrScript); + pProperties->Name = gaStrEventType; + pProperties->Handle = -1; + pProperties->Value = aAny; + pProperties->State = beans::PropertyState_DIRECT_VALUE; + pProperties++; + + aAny <<= pInfo->GetBookmark(); + pProperties->Name = gaStrScript; + pProperties->Handle = -1; + pProperties->Value = aAny; + pProperties->State = beans::PropertyState_DIRECT_VALUE; + pProperties++; + } + else + { + // Old Basic macro URL + aAny <<= OUString(gaStrStarBasic); + pProperties->Name = gaStrEventType; + pProperties->Handle = -1; + pProperties->Value = aAny; + pProperties->State = beans::PropertyState_DIRECT_VALUE; + pProperties++; + + OUString aMacro = pInfo->GetBookmark(); + + // aMacro has got following format: + // "Macroname.Modulname.Libname.Documentname" or + // "Macroname.Modulname.Libname.Applicationname" + sal_Int32 nIdx{ 0 }; + const std::u16string_view aMacroName = o3tl::getToken(aMacro, 0, '.', nIdx); + const std::u16string_view aModulName = o3tl::getToken(aMacro, 0, '.', nIdx); + const std::u16string_view aLibName = o3tl::getToken(aMacro, 0, '.', nIdx); + + OUString sBuffer = OUString::Concat(aLibName) + + "." + + aModulName + + "." + + aMacroName; + + aAny <<= sBuffer; + pProperties->Name = gaStrMacroName; + pProperties->Handle = -1; + pProperties->Value = aAny; + pProperties->State = beans::PropertyState_DIRECT_VALUE; + pProperties++; + + aAny <<= OUString( "StarOffice" ); + pProperties->Name = gaStrLibrary; + pProperties->Handle = -1; + pProperties->Value = aAny; + pProperties->State = beans::PropertyState_DIRECT_VALUE; + } + } + else + { + aAny <<= OUString(gaStrPresentation); + pProperties->Name = gaStrEventType; + pProperties->Handle = -1; + pProperties->Value = aAny; + pProperties->State = beans::PropertyState_DIRECT_VALUE; + pProperties++; + + aAny <<= eClickAction; + pProperties->Name = gaStrClickAction; + pProperties->Handle = -1; + pProperties->Value = aAny; + pProperties->State = beans::PropertyState_DIRECT_VALUE; + pProperties++; + + switch( eClickAction ) + { + case presentation::ClickAction_NONE: + case presentation::ClickAction_PREVPAGE: + case presentation::ClickAction_NEXTPAGE: + case presentation::ClickAction_FIRSTPAGE: + case presentation::ClickAction_LASTPAGE: + case presentation::ClickAction_INVISIBLE: + case presentation::ClickAction_STOPPRESENTATION: + break; + case presentation::ClickAction_BOOKMARK: + { + const OUString aStrBookmark( getPageApiNameFromUiName( pInfo->GetBookmark()) ); + pProperties->Name = gaStrBookmark; + pProperties->Handle = -1; + pProperties->Value <<= aStrBookmark; + pProperties->State = beans::PropertyState_DIRECT_VALUE; + } + break; + + case presentation::ClickAction_DOCUMENT: + case presentation::ClickAction_PROGRAM: + { + OUString aString( pInfo->GetBookmark()); + sal_Int32 nPos = aString.lastIndexOf( '#' ); + if( nPos >= 0 ) + { + OUString aURL = aString.subView( 0, nPos+1 ) + + getPageApiNameFromUiName( aString.copy( nPos+1 ) ); + aString = aURL; + } + pProperties->Name = gaStrBookmark; + pProperties->Handle = -1; + pProperties->Value <<= aString; + pProperties->State = beans::PropertyState_DIRECT_VALUE; + } + break; + + case presentation::ClickAction_VANISH: + aAny <<= pInfo->meSecondEffect; + pProperties->Name = gaStrEffect; + pProperties->Handle = -1; + pProperties->Value = aAny; + pProperties->State = beans::PropertyState_DIRECT_VALUE; + pProperties++; + + aAny <<= pInfo->meSecondSpeed; + pProperties->Name = gaStrSpeed; + pProperties->Handle = -1; + pProperties->Value = aAny; + pProperties->State = beans::PropertyState_DIRECT_VALUE; + pProperties++; + + [[fallthrough]]; + + case presentation::ClickAction_SOUND: + if( eClickAction == presentation::ClickAction_SOUND || pInfo->mbSecondSoundOn ) + { + aAny <<= pInfo->GetBookmark(); + pProperties->Name = gaStrSoundURL; + pProperties->Handle = -1; + pProperties->Value = aAny; + pProperties->State = beans::PropertyState_DIRECT_VALUE; + pProperties++; + + pProperties->Name = gaStrPlayFull; + pProperties->Handle = -1; + pProperties->Value <<= pInfo->mbSecondPlayFull; + pProperties->State = beans::PropertyState_DIRECT_VALUE; + } + break; + + case presentation::ClickAction_VERB: + aAny <<= static_cast<sal_Int32>(pInfo->mnVerb); + pProperties->Name = gaStrVerb; + pProperties->Handle = -1; + pProperties->Value = aAny; + pProperties->State = beans::PropertyState_DIRECT_VALUE; + break; + default: + break; + } + } + + aAny <<= aProperties; + return aAny; +} + +uno::Sequence< OUString > SAL_CALL SdUnoEventsAccess::getElementNames( ) +{ + return { gaStrOnClick }; +} + +sal_Bool SAL_CALL SdUnoEventsAccess::hasByName( const OUString& aName ) +{ + return aName == gaStrOnClick; +} + +// XElementAccess +uno::Type SAL_CALL SdUnoEventsAccess::getElementType( ) +{ + return cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get(); +} + +sal_Bool SAL_CALL SdUnoEventsAccess::hasElements( ) +{ + return true; +} + +// XServiceInfo +OUString SAL_CALL SdUnoEventsAccess::getImplementationName( ) +{ + return "SdUnoEventsAccess"; +} + +sal_Bool SAL_CALL SdUnoEventsAccess::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence< OUString > SAL_CALL SdUnoEventsAccess::getSupportedServiceNames( ) +{ + return { gaStrServiceName }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unoobj.hxx b/sd/source/ui/unoidl/unoobj.hxx new file mode 100644 index 000000000..3f01cbed1 --- /dev/null +++ b/sd/source/ui/unoidl/unoobj.hxx @@ -0,0 +1,100 @@ +/* -*- 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 . + */ +#pragma once + +#include <com/sun/star/beans/PropertyState.hpp> +#include <com/sun/star/document/XEventsSupplier.hpp> +#include <svx/unomaster.hxx> + +namespace com::sun::star::beans { class XPropertySetInfo; } + +class SdXImpressDocument; +class SdAnimationInfo; +class SvxItemPropertySet; +class SvxShape; +struct SfxItemPropertyMapEntry; + +class SdXShape : public SvxShapeMaster, + public css::document::XEventsSupplier +{ + friend class SdUnoEventsAccess; + +private: + SvxShape* mpShape; + const SvxItemPropertySet* mpPropSet; + const SfxItemPropertyMapEntry* mpMap; + SdXImpressDocument* mpModel; + + /// @throws css::lang::IllegalArgumentException + /// @throws css::beans::UnknownPropertyException + /// @throws css::uno::RuntimeException + void SetStyleSheet( const css::uno::Any& rAny ); + /// @throws css::beans::UnknownPropertyException + css::uno::Any GetStyleSheet() const; + + // Intern + /// @throws std::exception + SdAnimationInfo* GetAnimationInfo( bool bCreate = false ) const; + /// @throws std::exception + bool IsPresObj() const; + + bool IsEmptyPresObj() const; + void SetEmptyPresObj(bool bEmpty); + + bool IsMasterDepend() const noexcept; + void SetMasterDepend( bool bDepend ) noexcept; + + OUString GetPlaceholderText() const; + +public: + SdXShape(SvxShape* pShape, SdXImpressDocument* pModel); + virtual ~SdXShape() noexcept; + + virtual bool queryAggregation( const css::uno::Type & rType, css::uno::Any& aAny ) override; + virtual void dispose() override; + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + // XServiceInfo + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + //XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override; + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override; + + //XPropertyState + virtual css::beans::PropertyState SAL_CALL getPropertyState( const OUString& PropertyName ) override; + virtual void SAL_CALL setPropertyToDefault( const OUString& PropertyName ) override; + virtual css::uno::Any SAL_CALL getPropertyDefault( const OUString& aPropertyName ) override; + + // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + + // XEventsSupplier + virtual css::uno::Reference< css::container::XNameReplace > SAL_CALL getEvents( ) override; +}; + +struct SvEventDescription; +const SvEventDescription* ImplGetSupportedMacroItems(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unopage.cxx b/sd/source/ui/unoidl/unopage.cxx new file mode 100644 index 000000000..8a9549c51 --- /dev/null +++ b/sd/source/ui/unoidl/unopage.cxx @@ -0,0 +1,3056 @@ +/* -*- 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 <sal/config.h> + +#include <initializer_list> +#include <string_view> + +#include <com/sun/star/awt/XBitmap.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/presentation/FadeEffect.hpp> +#include <com/sun/star/presentation/AnimationSpeed.hpp> +#include <com/sun/star/view/PaperOrientation.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <cppuhelper/implbase.hxx> +#include <comphelper/profilezone.hxx> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <rtl/ustrbuf.hxx> +#include <vcl/bitmapex.hxx> +#include <vcl/filter/SvmWriter.hxx> +#include <vcl/metaact.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <tools/diagnose_ex.h> +#include <vcl/svapp.hxx> +#include <AnnotationEnumeration.hxx> +#include <createunopageimpl.hxx> +#include <unomodel.hxx> +#include <unopage.hxx> +#include <svl/itemset.hxx> +#include <svx/svdmodel.hxx> +#include <sdresid.hxx> +#include <strings.hrc> +#include <sdpage.hxx> +#include <unoprnms.hxx> +#include <drawdoc.hxx> +#include <svx/unoshape.hxx> +#include <svl/style.hxx> +#include <comphelper/extract.hxx> +#include <comphelper/sequence.hxx> +#include <svx/svditer.hxx> +#include <vcl/wmf.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdpool.hxx> +#include <svx/svdview.hxx> +#include <svx/xfillit0.hxx> +#include <DrawDocShell.hxx> +#include <ViewShell.hxx> +#include <DrawViewShell.hxx> +#include <editeng/unoprnms.hxx> +#include "unoobj.hxx" + +#include <strings.hxx> +#include <bitmaps.hlst> +#include <unokywds.hxx> +#include "unopback.hxx" +#include <vcl/dibtools.hxx> +#include <tools/debug.hxx> +#include <tools/stream.hxx> +#include <o3tl/string_view.hxx> + +using ::com::sun::star::animations::XAnimationNode; +using ::com::sun::star::animations::XAnimationNodeSupplier; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::office; + +namespace { + +// this are the ids for page properties +enum WID_PAGE +{ + WID_PAGE_LEFT, WID_PAGE_RIGHT, WID_PAGE_TOP, WID_PAGE_BOTTOM, WID_PAGE_WIDTH, + WID_PAGE_HEIGHT, WID_PAGE_EFFECT, WID_PAGE_CHANGE, WID_PAGE_SPEED, WID_PAGE_NUMBER, + WID_PAGE_ORIENT, WID_PAGE_LAYOUT, WID_PAGE_DURATION, WID_PAGE_HIGHRESDURATION, WID_PAGE_LDNAME, WID_PAGE_LDBITMAP, + WID_PAGE_BACK, WID_PAGE_PREVIEW, WID_PAGE_PREVIEWBITMAP, WID_PAGE_VISIBLE, WID_PAGE_SOUNDFILE, WID_PAGE_BACKFULL, + WID_PAGE_BACKVIS, WID_PAGE_BACKOBJVIS, WID_PAGE_USERATTRIBS, WID_PAGE_BOOKMARK, WID_PAGE_ISDARK, + WID_PAGE_HEADERVISIBLE, WID_PAGE_HEADERTEXT, WID_PAGE_FOOTERVISIBLE, WID_PAGE_FOOTERTEXT, + WID_PAGE_PAGENUMBERVISIBLE, WID_PAGE_DATETIMEVISIBLE, WID_PAGE_DATETIMEFIXED, + WID_PAGE_DATETIMETEXT, WID_PAGE_DATETIMEFORMAT, WID_TRANSITION_TYPE, WID_TRANSITION_SUBTYPE, + WID_TRANSITION_DIRECTION, WID_TRANSITION_FADE_COLOR, WID_TRANSITION_DURATION, WID_LOOP_SOUND, + WID_NAVORDER, WID_PAGE_PREVIEWMETAFILE, WID_PAGE_THEME +}; + +} + +constexpr OUStringLiteral sEmptyPageName = u"page"; + +// this function stores the property maps for draw pages in impress and draw +static const SvxItemPropertySet* ImplGetDrawPagePropertySet( bool bImpress, PageKind ePageKind ) +{ + static const SfxItemPropertyMapEntry aDrawPagePropertyMap_Impl[] = + { + { u"" UNO_NAME_PAGE_BACKGROUND, WID_PAGE_BACK, cppu::UnoType<beans::XPropertySet>::get(), beans::PropertyAttribute::MAYBEVOID,0}, + { u"" UNO_NAME_PAGE_BOTTOM, WID_PAGE_BOTTOM, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, + { u"" UNO_NAME_PAGE_LEFT, WID_PAGE_LEFT, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, + { u"" UNO_NAME_PAGE_RIGHT, WID_PAGE_RIGHT, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, + { u"" UNO_NAME_PAGE_TOP, WID_PAGE_TOP, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, + { u"" UNO_NAME_PAGE_CHANGE, WID_PAGE_CHANGE, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, + { u"" UNO_NAME_PAGE_DURATION, WID_PAGE_DURATION, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, + { u"" UNO_NAME_PAGE_EFFECT, WID_PAGE_EFFECT, ::cppu::UnoType<presentation::FadeEffect>::get(), 0, 0}, + { u"" UNO_NAME_PAGE_HEIGHT, WID_PAGE_HEIGHT, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, + { u"" UNO_NAME_PAGE_LAYOUT, WID_PAGE_LAYOUT, ::cppu::UnoType<sal_Int16>::get(), 0, 0}, + { UNO_NAME_LINKDISPLAYBITMAP, WID_PAGE_LDBITMAP, cppu::UnoType<awt::XBitmap>::get(), beans::PropertyAttribute::READONLY, 0}, + { UNO_NAME_LINKDISPLAYNAME, WID_PAGE_LDNAME, ::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0}, + { u"" UNO_NAME_PAGE_NUMBER, WID_PAGE_NUMBER, ::cppu::UnoType<sal_Int16>::get(), beans::PropertyAttribute::READONLY, 0}, + { u"" UNO_NAME_PAGE_ORIENTATION, WID_PAGE_ORIENT, ::cppu::UnoType<view::PaperOrientation>::get(),0, 0}, + { u"" UNO_NAME_PAGE_SPEED, WID_PAGE_SPEED, ::cppu::UnoType<presentation::AnimationSpeed>::get(), 0, 0}, + { u"" UNO_NAME_PAGE_WIDTH, WID_PAGE_WIDTH, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, + { u"" UNO_NAME_PAGE_PREVIEW, WID_PAGE_PREVIEW, cppu::UnoType<css::uno::Sequence<sal_Int8>>::get(), css::beans::PropertyAttribute::READONLY, 0}, + { u"" UNO_NAME_PAGE_PREVIEWBITMAP, WID_PAGE_PREVIEWBITMAP, cppu::UnoType<css::uno::Sequence<sal_Int8>>::get(), css::beans::PropertyAttribute::READONLY, 0}, + { u"" UNO_NAME_PAGE_PREVIEWMETAFILE, WID_PAGE_PREVIEWMETAFILE, cppu::UnoType<css::uno::Sequence<sal_Int8>>::get(), css::beans::PropertyAttribute::READONLY, 0}, + { u"" UNO_NAME_PAGE_VISIBLE, WID_PAGE_VISIBLE, cppu::UnoType<bool>::get(), 0, 0}, + { u"" UNO_NAME_OBJ_SOUNDFILE, WID_PAGE_SOUNDFILE, cppu::UnoType<Any>::get(), 0, 0}, + { sUNO_Prop_IsBackgroundVisible, WID_PAGE_BACKVIS, cppu::UnoType<bool>::get(), 0, 0}, + { sUNO_Prop_IsBackgroundObjectsVisible, WID_PAGE_BACKOBJVIS, cppu::UnoType<bool>::get(), 0, 0}, + { sUNO_Prop_UserDefinedAttributes,WID_PAGE_USERATTRIBS, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0}, + { sUNO_Prop_BookmarkURL, WID_PAGE_BOOKMARK, ::cppu::UnoType<OUString>::get(), 0, 0}, + { u"HighResDuration", WID_PAGE_HIGHRESDURATION, ::cppu::UnoType<double>::get(), 0, 0}, + { u"IsBackgroundDark" , WID_PAGE_ISDARK, cppu::UnoType<bool>::get(), beans::PropertyAttribute::READONLY, 0}, + { u"IsFooterVisible", WID_PAGE_FOOTERVISIBLE, cppu::UnoType<bool>::get(), 0, 0}, + { u"FooterText", WID_PAGE_FOOTERTEXT, ::cppu::UnoType<OUString>::get(), 0, 0}, + { u"IsPageNumberVisible", WID_PAGE_PAGENUMBERVISIBLE, cppu::UnoType<bool>::get(), 0, 0}, + { u"IsDateTimeVisible", WID_PAGE_DATETIMEVISIBLE, cppu::UnoType<bool>::get(), 0, 0}, + { u"IsDateTimeFixed", WID_PAGE_DATETIMEFIXED, cppu::UnoType<bool>::get(), 0, 0}, + { u"DateTimeText", WID_PAGE_DATETIMETEXT, ::cppu::UnoType<OUString>::get(), 0, 0}, + { u"DateTimeFormat", WID_PAGE_DATETIMEFORMAT, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, + { u"TransitionType", WID_TRANSITION_TYPE, ::cppu::UnoType<sal_Int16>::get(), 0, 0}, + { u"TransitionSubtype", WID_TRANSITION_SUBTYPE, ::cppu::UnoType<sal_Int16>::get(), 0, 0}, + { u"TransitionDirection", WID_TRANSITION_DIRECTION, ::cppu::UnoType<sal_Bool>::get(), 0, 0}, + { u"TransitionFadeColor", WID_TRANSITION_FADE_COLOR, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, + { u"" UNO_NAME_PAGE_TRANSITION_DURATION, WID_TRANSITION_DURATION, ::cppu::UnoType<double>::get(), 0, 0}, + { u"LoopSound", WID_LOOP_SOUND, cppu::UnoType<bool>::get(), 0, 0}, + { u"NavigationOrder", WID_NAVORDER, cppu::UnoType<css::container::XIndexAccess>::get(),0, 0}, + { u"", 0, css::uno::Type(), 0, 0 } + }; + +#define DRAW_PAGE_NOTES_PROPERTIES \ + { u"" UNO_NAME_PAGE_BOTTOM, WID_PAGE_BOTTOM, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, \ + { u"" UNO_NAME_PAGE_LEFT, WID_PAGE_LEFT, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, \ + { u"" UNO_NAME_PAGE_RIGHT, WID_PAGE_RIGHT, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, \ + { u"" UNO_NAME_PAGE_TOP, WID_PAGE_TOP, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, \ + { u"" UNO_NAME_PAGE_HEIGHT, WID_PAGE_HEIGHT, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, \ + { u"" UNO_NAME_PAGE_LAYOUT, WID_PAGE_LAYOUT, ::cppu::UnoType<sal_Int16>::get(), 0, 0}, \ + { UNO_NAME_LINKDISPLAYBITMAP, WID_PAGE_LDBITMAP, cppu::UnoType<awt::XBitmap>::get(), beans::PropertyAttribute::READONLY, 0}, \ + { UNO_NAME_LINKDISPLAYNAME, WID_PAGE_LDNAME, ::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0}, \ + { u"" UNO_NAME_PAGE_NUMBER, WID_PAGE_NUMBER, ::cppu::UnoType<sal_Int16>::get(), beans::PropertyAttribute::READONLY, 0}, \ + { u"" UNO_NAME_PAGE_ORIENTATION, WID_PAGE_ORIENT, ::cppu::UnoType<view::PaperOrientation>::get(),0, 0}, \ + { u"" UNO_NAME_PAGE_WIDTH, WID_PAGE_WIDTH, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, \ + { sUNO_Prop_UserDefinedAttributes,WID_PAGE_USERATTRIBS, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},\ + { u"IsHeaderVisible", WID_PAGE_HEADERVISIBLE, cppu::UnoType<bool>::get(), 0, 0}, \ + { u"HeaderText", WID_PAGE_HEADERTEXT, ::cppu::UnoType<OUString>::get(), 0, 0}, \ + { u"IsBackgroundDark", WID_PAGE_ISDARK, cppu::UnoType<bool>::get(), beans::PropertyAttribute::READONLY, 0}, \ + { u"IsFooterVisible", WID_PAGE_FOOTERVISIBLE, cppu::UnoType<bool>::get(), 0, 0}, \ + { u"FooterText", WID_PAGE_FOOTERTEXT, ::cppu::UnoType<OUString>::get(), 0, 0}, \ + { u"IsPageNumberVisible", WID_PAGE_PAGENUMBERVISIBLE, cppu::UnoType<bool>::get(), 0, 0}, \ + { u"IsDateTimeVisible", WID_PAGE_DATETIMEVISIBLE, cppu::UnoType<bool>::get(), 0, 0}, \ + { u"IsDateTimeFixed", WID_PAGE_DATETIMEFIXED, cppu::UnoType<bool>::get(), 0, 0}, \ + { u"DateTimeText", WID_PAGE_DATETIMETEXT, ::cppu::UnoType<OUString>::get(), 0, 0}, \ + { u"DateTimeFormat", WID_PAGE_DATETIMEFORMAT, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, \ + { u"NavigationOrder", WID_NAVORDER, cppu::UnoType<css::container::XIndexAccess>::get(),0, 0}, \ + { u"", 0, css::uno::Type(), 0, 0 } + + static const SfxItemPropertyMapEntry aDrawPageNotesHandoutPropertyMap_Impl[] = + { + // this must be the first two entries so they can be excluded for PageKind::Standard + { u"" UNO_NAME_PAGE_BACKGROUND, WID_PAGE_BACK, cppu::UnoType<beans::XPropertySet>::get(), beans::PropertyAttribute::MAYBEVOID,0}, + DRAW_PAGE_NOTES_PROPERTIES + }; + static const SfxItemPropertyMapEntry aDrawPageNotesHandoutPropertyNoBackMap_Impl[] = + { + DRAW_PAGE_NOTES_PROPERTIES + }; + +#define GRAPHIC_PAGE_PROPERTIES \ + { u"" UNO_NAME_PAGE_BOTTOM, WID_PAGE_BOTTOM, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, \ + { u"" UNO_NAME_PAGE_LEFT, WID_PAGE_LEFT, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, \ + { u"" UNO_NAME_PAGE_RIGHT, WID_PAGE_RIGHT, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, \ + { u"" UNO_NAME_PAGE_TOP, WID_PAGE_TOP, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, \ + { u"" UNO_NAME_PAGE_HEIGHT, WID_PAGE_HEIGHT, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, \ + { UNO_NAME_LINKDISPLAYBITMAP, WID_PAGE_LDBITMAP, cppu::UnoType<awt::XBitmap>::get(), beans::PropertyAttribute::READONLY, 0}, \ + { UNO_NAME_LINKDISPLAYNAME, WID_PAGE_LDNAME, ::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0}, \ + { u"" UNO_NAME_PAGE_NUMBER, WID_PAGE_NUMBER, ::cppu::UnoType<sal_Int16>::get(), beans::PropertyAttribute::READONLY, 0}, \ + { u"" UNO_NAME_PAGE_ORIENTATION, WID_PAGE_ORIENT, ::cppu::UnoType<view::PaperOrientation>::get(),0, 0}, \ + { u"" UNO_NAME_PAGE_WIDTH, WID_PAGE_WIDTH, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, \ + { u"" UNO_NAME_PAGE_PREVIEW, WID_PAGE_PREVIEW, cppu::UnoType<css::uno::Sequence<sal_Int8>>::get(), css::beans::PropertyAttribute::READONLY, 0}, \ + { u"" UNO_NAME_PAGE_PREVIEWBITMAP, WID_PAGE_PREVIEWBITMAP, cppu::UnoType<css::uno::Sequence<sal_Int8>>::get(), css::beans::PropertyAttribute::READONLY, 0},\ + { u"" UNO_NAME_PAGE_PREVIEWMETAFILE, WID_PAGE_PREVIEWMETAFILE, cppu::UnoType<css::uno::Sequence<sal_Int8>>::get(), css::beans::PropertyAttribute::READONLY, 0},\ + { sUNO_Prop_UserDefinedAttributes,WID_PAGE_USERATTRIBS, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0}, \ + { sUNO_Prop_BookmarkURL, WID_PAGE_BOOKMARK, ::cppu::UnoType<OUString>::get(), 0, 0}, \ + { u"IsBackgroundDark", WID_PAGE_ISDARK, cppu::UnoType<bool>::get(), beans::PropertyAttribute::READONLY, 0}, \ + { u"NavigationOrder", WID_NAVORDER, cppu::UnoType<css::container::XIndexAccess>::get(),0, 0}, \ + { u"", 0, css::uno::Type(), 0, 0 } + + static const SfxItemPropertyMapEntry aGraphicPagePropertyMap_Impl[] = + { + { u"" UNO_NAME_PAGE_BACKGROUND, WID_PAGE_BACK, cppu::UnoType<beans::XPropertySet>::get(), beans::PropertyAttribute::MAYBEVOID,0}, + GRAPHIC_PAGE_PROPERTIES + }; + static const SfxItemPropertyMapEntry aGraphicPagePropertyNoBackMap_Impl[] = + { + GRAPHIC_PAGE_PROPERTIES + }; + + bool bWithoutBackground = ePageKind != PageKind::Standard && ePageKind != PageKind::Handout; + const SvxItemPropertySet* pRet = nullptr; + if( bImpress ) + { + if( ePageKind == PageKind::Standard ) + { + //PageKind::Standard always has a background property + static SvxItemPropertySet aDrawPagePropertySet_Impl( aDrawPagePropertyMap_Impl, SdrObject::GetGlobalDrawObjectItemPool() ); + pRet = &aDrawPagePropertySet_Impl; + } + else + { + if(bWithoutBackground) + { + static SvxItemPropertySet aDrawPageNotesHandoutPropertyNoBackSet_Impl( aDrawPageNotesHandoutPropertyNoBackMap_Impl, SdrObject::GetGlobalDrawObjectItemPool() ); + pRet = &aDrawPageNotesHandoutPropertyNoBackSet_Impl; + } + else + { + static SvxItemPropertySet aDrawPageNotesHandoutPropertySet_Impl( aDrawPageNotesHandoutPropertyMap_Impl, SdrObject::GetGlobalDrawObjectItemPool() ); + pRet = &aDrawPageNotesHandoutPropertySet_Impl; + } + } + } + else + { + if(bWithoutBackground) + { + static SvxItemPropertySet aGraphicPagePropertyNoBackSet_Impl( aGraphicPagePropertyNoBackMap_Impl, SdrObject::GetGlobalDrawObjectItemPool() ); + pRet = &aGraphicPagePropertyNoBackSet_Impl; + } + else + { + static SvxItemPropertySet aGraphicPagePropertySet_Impl( aGraphicPagePropertyMap_Impl, SdrObject::GetGlobalDrawObjectItemPool() ); + pRet = &aGraphicPagePropertySet_Impl; + } + } + return pRet; +} + +/** this function stores the property map for master pages in impress and draw */ +static const SvxItemPropertySet* ImplGetMasterPagePropertySet( PageKind ePageKind ) +{ + static const SfxItemPropertyMapEntry aMasterPagePropertyMap_Impl[] = + { + { u"" UNO_NAME_PAGE_BACKGROUND, WID_PAGE_BACK, cppu::UnoType<beans::XPropertySet>::get(), 0, 0}, + { u"" UNO_NAME_PAGE_BOTTOM, WID_PAGE_BOTTOM, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, + { u"" UNO_NAME_PAGE_LEFT, WID_PAGE_LEFT, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, + { u"" UNO_NAME_PAGE_RIGHT, WID_PAGE_RIGHT, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, + { u"" UNO_NAME_PAGE_TOP, WID_PAGE_TOP, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, + { u"" UNO_NAME_PAGE_HEIGHT, WID_PAGE_HEIGHT, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, + { UNO_NAME_LINKDISPLAYBITMAP, WID_PAGE_LDBITMAP, cppu::UnoType<awt::XBitmap>::get(), beans::PropertyAttribute::READONLY, 0}, + { UNO_NAME_LINKDISPLAYNAME, WID_PAGE_LDNAME, ::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0}, + { u"" UNO_NAME_PAGE_NUMBER, WID_PAGE_NUMBER, ::cppu::UnoType<sal_Int16>::get(), beans::PropertyAttribute::READONLY, 0}, + { u"" UNO_NAME_PAGE_ORIENTATION, WID_PAGE_ORIENT, ::cppu::UnoType<view::PaperOrientation>::get(),0, 0}, + { u"" UNO_NAME_PAGE_WIDTH, WID_PAGE_WIDTH, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, + { u"BackgroundFullSize", WID_PAGE_BACKFULL, cppu::UnoType<bool>::get(), 0, 0}, + { sUNO_Prop_UserDefinedAttributes,WID_PAGE_USERATTRIBS, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0}, + { u"IsBackgroundDark", WID_PAGE_ISDARK, cppu::UnoType<bool>::get(), beans::PropertyAttribute::READONLY, 0}, + { u"Theme", WID_PAGE_THEME, cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get(), 0, 0}, + { u"", 0, css::uno::Type(), 0, 0 } + }; + + static const SfxItemPropertyMapEntry aHandoutMasterPagePropertyMap_Impl[] = + { + { u"" UNO_NAME_PAGE_BOTTOM, WID_PAGE_BOTTOM, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, + { u"" UNO_NAME_PAGE_LEFT, WID_PAGE_LEFT, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, + { u"" UNO_NAME_PAGE_RIGHT, WID_PAGE_RIGHT, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, + { u"" UNO_NAME_PAGE_TOP, WID_PAGE_TOP, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, + { u"" UNO_NAME_PAGE_HEIGHT, WID_PAGE_HEIGHT, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, + { u"" UNO_NAME_PAGE_ORIENTATION, WID_PAGE_ORIENT, ::cppu::UnoType<view::PaperOrientation>::get(),0, 0}, + { u"" UNO_NAME_PAGE_NUMBER, WID_PAGE_NUMBER, ::cppu::UnoType<sal_Int16>::get(), beans::PropertyAttribute::READONLY, 0}, + { u"" UNO_NAME_PAGE_WIDTH, WID_PAGE_WIDTH, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, + { u"" UNO_NAME_PAGE_LAYOUT, WID_PAGE_LAYOUT, ::cppu::UnoType<sal_Int16>::get(), 0, 0}, + { sUNO_Prop_UserDefinedAttributes,WID_PAGE_USERATTRIBS, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0}, + { u"IsBackgroundDark", WID_PAGE_ISDARK, cppu::UnoType<bool>::get(), beans::PropertyAttribute::READONLY, 0}, + { u"IsHeaderVisible", WID_PAGE_HEADERVISIBLE, cppu::UnoType<bool>::get(), 0, 0}, + { u"HeaderText", WID_PAGE_HEADERTEXT, ::cppu::UnoType<OUString>::get(), 0, 0}, + { u"IsFooterVisible", WID_PAGE_FOOTERVISIBLE, cppu::UnoType<bool>::get(), 0, 0}, + { u"FooterText", WID_PAGE_FOOTERTEXT, ::cppu::UnoType<OUString>::get(), 0, 0}, + { u"IsPageNumberVisible", WID_PAGE_PAGENUMBERVISIBLE, cppu::UnoType<bool>::get(), 0, 0}, + { u"IsDateTimeVisible", WID_PAGE_DATETIMEVISIBLE, cppu::UnoType<bool>::get(), 0, 0}, + { u"IsDateTimeFixed", WID_PAGE_DATETIMEFIXED, cppu::UnoType<bool>::get(), 0, 0}, + { u"DateTimeText", WID_PAGE_DATETIMETEXT, ::cppu::UnoType<OUString>::get(), 0, 0}, + { u"DateTimeFormat", WID_PAGE_DATETIMEFORMAT, ::cppu::UnoType<sal_Int32>::get(), 0, 0}, + { u"", 0, css::uno::Type(), 0, 0 } + }; + + const SvxItemPropertySet* pRet = nullptr; + if( ePageKind == PageKind::Handout ) + { + static SvxItemPropertySet aHandoutMasterPagePropertySet_Impl( aHandoutMasterPagePropertyMap_Impl, SdrObject::GetGlobalDrawObjectItemPool() ); + pRet = &aHandoutMasterPagePropertySet_Impl; + } + else + { + static SvxItemPropertySet aMasterPagePropertySet_Impl( aMasterPagePropertyMap_Impl, SdrObject::GetGlobalDrawObjectItemPool() ); + pRet = &aMasterPagePropertySet_Impl; + } + return pRet; +} + +const css::uno::Sequence< sal_Int8 > & SdGenericDrawPage::getUnoTunnelId() noexcept +{ + static const comphelper::UnoIdInit theSdGenericDrawPageUnoTunnelId; + return theSdGenericDrawPageUnoTunnelId.getSeq(); +} + +sal_Int64 SAL_CALL SdGenericDrawPage::getSomething( const css::uno::Sequence< sal_Int8 >& rId ) +{ + return comphelper::getSomethingImpl(rId, this, + comphelper::FallbackToGetSomethingOf<SvxFmDrawPage>{}); +} + +SdGenericDrawPage::SdGenericDrawPage(SdXImpressDocument* _pModel, SdPage* pInPage, const SvxItemPropertySet* _pSet) +: SvxFmDrawPage( static_cast<SdrPage*>(pInPage) ), + SdUnoSearchReplaceShape(this), + mpDocModel( _pModel ), + mpSdrModel(nullptr), + mbIsImpressDocument(false), + mnTempPageNumber(0), + mpPropSet ( _pSet ) +{ + mpSdrModel = SvxFmDrawPage::mpModel; + if( mpDocModel ) + mbIsImpressDocument = mpDocModel->IsImpressDocument(); + +} + +SdGenericDrawPage::~SdGenericDrawPage() noexcept +{ +} + +void SdGenericDrawPage::throwIfDisposed() const +{ + if( (SvxFmDrawPage::mpModel == nullptr) || (mpDocModel == nullptr) || (SvxFmDrawPage::mpPage == nullptr) ) + throw lang::DisposedException(); +} + +SdXImpressDocument* SdGenericDrawPage::GetModel() const +{ + if( mpSdrModel != SvxFmDrawPage::mpModel ) + const_cast<SdGenericDrawPage*>(this)->UpdateModel(); + return mpDocModel; +} + +bool SdGenericDrawPage::IsImpressDocument() const +{ + if( mpSdrModel != SvxFmDrawPage::mpModel ) + const_cast<SdGenericDrawPage*>(this)->UpdateModel(); + return mbIsImpressDocument; +} + + +void SdGenericDrawPage::UpdateModel() +{ + mpSdrModel = SvxFmDrawPage::mpModel; + if( mpSdrModel ) + { + uno::Reference< uno::XInterface > xModel( SvxFmDrawPage::mpModel->getUnoModel() ); + mpDocModel = comphelper::getFromUnoTunnel<SdXImpressDocument>( xModel ); + } + else + { + mpDocModel = nullptr; + } + mbIsImpressDocument = mpDocModel && mpDocModel->IsImpressDocument(); +} + +// this is called whenever a SdrObject must be created for an empty api shape wrapper +SdrObject * SdGenericDrawPage::CreateSdrObject_( const Reference< drawing::XShape >& xShape ) +{ + if( nullptr == SvxFmDrawPage::mpPage || !xShape.is() ) + return nullptr; + + OUString aType( xShape->getShapeType() ); + static const OUStringLiteral aPrefix( u"com.sun.star.presentation." ); + if( !aType.startsWith( aPrefix ) ) + { + SdrObject* pObj = SvxFmDrawPage::CreateSdrObject_( xShape ); + return pObj; + } + + aType = aType.copy( aPrefix.getLength() ); + + PresObjKind eObjKind = PresObjKind::NONE; + + if( aType == "TitleTextShape" ) + { + eObjKind = PresObjKind::Title; + } + else if( aType == "OutlinerShape" ) + { + eObjKind = PresObjKind::Outline; + } + else if( aType == "SubtitleShape" ) + { + eObjKind = PresObjKind::Text; + } + else if( aType == "OLE2Shape" ) + { + eObjKind = PresObjKind::Object; + } + else if( aType == "ChartShape" ) + { + eObjKind = PresObjKind::Chart; + } + else if( aType == "CalcShape" ) + { + eObjKind = PresObjKind::Calc; + } + else if( aType == "TableShape" ) + { + eObjKind = PresObjKind::Table; + } + else if( aType == "GraphicObjectShape" ) + { + eObjKind = PresObjKind::Graphic; + } + else if( aType == "OrgChartShape" ) + { + eObjKind = PresObjKind::OrgChart; + } + else if( aType == "PageShape" ) + { + if( GetPage()->GetPageKind() == PageKind::Notes && GetPage()->IsMasterPage() ) + eObjKind = PresObjKind::Title; + else + eObjKind = PresObjKind::Page; + } + else if( aType == "NotesShape" ) + { + eObjKind = PresObjKind::Notes; + } + else if( aType == "HandoutShape" ) + { + eObjKind = PresObjKind::Handout; + } + else if( aType == "FooterShape" ) + { + eObjKind = PresObjKind::Footer; + } + else if( aType == "HeaderShape" ) + { + eObjKind = PresObjKind::Header; + } + else if( aType == "SlideNumberShape" ) + { + eObjKind = PresObjKind::SlideNumber; + } + else if( aType == "DateTimeShape" ) + { + eObjKind = PresObjKind::DateTime; + } + else if( aType == "MediaShape" ) + { + eObjKind = PresObjKind::Media; + } + + ::tools::Rectangle aRect( eObjKind == PresObjKind::Title ? GetPage()->GetTitleRect() : GetPage()->GetLayoutRect() ); + + const awt::Point aPos( aRect.Left(), aRect.Top() ); + xShape->setPosition( aPos ); + + const awt::Size aSize( aRect.GetWidth(), aRect.GetHeight() ); + xShape->setSize( aSize ); + + SdrObject *pPresObj = nullptr; + if( (eObjKind == PresObjKind::Table) || (eObjKind == PresObjKind::Media) ) + { + pPresObj = SvxFmDrawPage::CreateSdrObject_( xShape ); + if( pPresObj ) + { + SdDrawDocument& rDoc(static_cast< SdDrawDocument& >(GetPage()->getSdrModelFromSdrPage())); + pPresObj->NbcSetStyleSheet(rDoc.GetDefaultStyleSheet(), true); + GetPage()->InsertPresObj( pPresObj, eObjKind ); + } + } + else + { + pPresObj = GetPage()->CreatePresObj( eObjKind, false, aRect ); + } + + if( pPresObj ) + pPresObj->SetUserCall( GetPage() ); + + return pPresObj; +} + +// XInterface +Any SAL_CALL SdGenericDrawPage::queryInterface( const uno::Type & rType ) +{ + Any aAny; + + if (rType == cppu::UnoType<beans::XPropertySet>::get()) + { + aAny <<= Reference<beans::XPropertySet>(this); + } + else if (rType == cppu::UnoType<container::XNamed>::get()) + { + aAny <<= Reference<container::XNamed>(this); + } + else if (rType == cppu::UnoType<util::XReplaceable>::get()) + { + aAny <<= Reference<util::XReplaceable>(this); + } + else if (rType == cppu::UnoType<util::XSearchable>::get()) + { + aAny <<= Reference<util::XSearchable>(this); + } + else if (rType == cppu::UnoType<document::XLinkTargetSupplier>::get()) + { + aAny <<= Reference<document::XLinkTargetSupplier>(this); + } + else if (rType == cppu::UnoType<drawing::XShapeCombiner>::get()) + { + aAny <<= Reference<drawing::XShapeCombiner>(this); + } + else if (rType == cppu::UnoType<drawing::XShapeBinder>::get()) + { + aAny <<= Reference<drawing::XShapeBinder>(this); + } + else if (rType == cppu::UnoType<beans::XMultiPropertySet>::get()) + { + aAny <<= Reference<beans::XMultiPropertySet>(this); + } + else if (rType == cppu::UnoType<office::XAnnotationAccess>::get()) + { + aAny <<= Reference<office::XAnnotationAccess>(this); + } + else if (IsImpressDocument() && rType == cppu::UnoType<XAnimationNodeSupplier>::get()) + { + const PageKind ePageKind = GetPage() ? GetPage()->GetPageKind() : PageKind::Standard; + + if( ePageKind == PageKind::Standard ) + return Any( Reference< XAnimationNodeSupplier >( this ) ); + } + else + return SvxDrawPage::queryInterface( rType ); + + return aAny; +} + +// XPropertySet +Reference< beans::XPropertySetInfo > SAL_CALL SdGenericDrawPage::getPropertySetInfo() +{ + ::SolarMutexGuard aGuard; + throwIfDisposed(); + return mpPropSet->getPropertySetInfo(); +} + +void SAL_CALL SdGenericDrawPage::setPropertyValue( const OUString& aPropertyName, const Any& aValue ) +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + const SfxItemPropertyMapEntry* pEntry = mpPropSet->getPropertyMapEntry(aPropertyName); + + switch( pEntry ? pEntry->nWID : -1 ) + { + case WID_NAVORDER: + setNavigationOrder( aValue ); + break; + case WID_PAGE_LEFT: + case WID_PAGE_RIGHT: + case WID_PAGE_TOP: + case WID_PAGE_BOTTOM: + case WID_PAGE_LAYOUT: + case WID_PAGE_DURATION: + case WID_PAGE_CHANGE: + { + sal_Int32 nValue = 0; + if(!(aValue >>= nValue)) + throw lang::IllegalArgumentException(); + + switch( pEntry->nWID ) + { + case WID_PAGE_LEFT: + SetLeftBorder(nValue); + break; + case WID_PAGE_RIGHT: + SetRightBorder( nValue ); + break; + case WID_PAGE_TOP: + SetUpperBorder( nValue ); + break; + case WID_PAGE_BOTTOM: + SetLowerBorder( nValue ); + break; + case WID_PAGE_CHANGE: + GetPage()->SetPresChange( static_cast<PresChange>(nValue) ); + break; + case WID_PAGE_LAYOUT: + GetPage()->SetAutoLayout( static_cast<AutoLayout>(nValue), true ); + break; + case WID_PAGE_DURATION: + GetPage()->SetTime(nValue); + break; + } + break; + } + case WID_PAGE_HIGHRESDURATION: + { + double fValue = 0; + if(!(aValue >>= fValue)) + throw lang::IllegalArgumentException(); + + GetPage()->SetTime(fValue); + break; + } + case WID_PAGE_WIDTH: + { + sal_Int32 nWidth = 0; + if(!(aValue >>= nWidth)) + throw lang::IllegalArgumentException(); + + SetWidth( nWidth ); + break; + } + case WID_PAGE_HEIGHT: + { + sal_Int32 nHeight = 0; + if(!(aValue >>= nHeight)) + throw lang::IllegalArgumentException(); + + SetHeight( nHeight ); + break; + } + case WID_PAGE_ORIENT: + { + sal_Int32 nEnum = 0; + if(!::cppu::enum2int( nEnum, aValue )) + throw lang::IllegalArgumentException(); + + Orientation eOri = (static_cast<view::PaperOrientation>(nEnum) == view::PaperOrientation_PORTRAIT)?Orientation::Portrait:Orientation::Landscape; + + if( eOri != GetPage()->GetOrientation() ) + { + SdDrawDocument& rDoc(static_cast< SdDrawDocument& >(GetPage()->getSdrModelFromSdrPage())); + const PageKind ePageKind = GetPage()->GetPageKind(); + + sal_uInt16 i, nPageCnt = rDoc.GetMasterSdPageCount(ePageKind); + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = rDoc.GetMasterSdPage(i, ePageKind); + pPage->SetOrientation( eOri ); + } + + nPageCnt = rDoc.GetSdPageCount(ePageKind); + + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = rDoc.GetSdPage(i, ePageKind); + pPage->SetOrientation( eOri ); + } + } + break; + } + case WID_PAGE_EFFECT: + { + sal_Int32 nEnum = 0; + if(!::cppu::enum2int( nEnum, aValue )) + throw lang::IllegalArgumentException(); + + GetPage()->SetFadeEffect( static_cast<presentation::FadeEffect>(nEnum) ); + break; + } + case WID_PAGE_BACK: + setBackground( aValue ); + break; + case WID_PAGE_SPEED: + { + sal_Int32 nEnum = 0; + if(!::cppu::enum2int( nEnum, aValue )) + throw lang::IllegalArgumentException(); + + GetPage()->setTransitionDuration( nEnum == 0 ? 3.0 : (nEnum == 1 ? 2.0 : 1.0 ) ); + break; + } + case WID_PAGE_VISIBLE : + { + bool bVisible = false; + if( ! ( aValue >>= bVisible ) ) + throw lang::IllegalArgumentException(); + GetPage()->SetExcluded( !bVisible ); + break; + } + case WID_PAGE_SOUNDFILE : + { + OUString aURL; + if( aValue >>= aURL ) + { + GetPage()->SetSoundFile( aURL ); + GetPage()->SetSound( !aURL.isEmpty() ); + break; + } + else + { + bool bStopSound = false; + if( aValue >>= bStopSound ) + { + GetPage()->SetStopSound( bStopSound ); + break; + } + } + + throw lang::IllegalArgumentException(); + } + case WID_LOOP_SOUND: + { + bool bLoop = false; + if( ! (aValue >>= bLoop) ) + throw lang::IllegalArgumentException(); + + GetPage()->SetLoopSound( bLoop ); + break; + } + case WID_PAGE_BACKFULL: + { + bool bFullSize = false; + if( ! ( aValue >>= bFullSize ) ) + throw lang::IllegalArgumentException(); + GetPage()->SetBackgroundFullSize( bFullSize ); + break; + } + case WID_PAGE_BACKVIS: + { + bool bVisible = false; + if( ! ( aValue >>= bVisible ) ) + throw lang::IllegalArgumentException(); + + SdrPage* pPage = GetPage(); + if( pPage ) + { + SdDrawDocument& rDoc(static_cast< SdDrawDocument& >(pPage->getSdrModelFromSdrPage())); + if( rDoc.GetMasterPageCount() ) + { + SdrLayerAdmin& rLayerAdmin = rDoc.GetLayerAdmin(); + SdrLayerIDSet aVisibleLayers = pPage->TRG_GetMasterPageVisibleLayers(); + aVisibleLayers.Set(rLayerAdmin.GetLayerID(sUNO_LayerName_background), bVisible); + pPage->TRG_SetMasterPageVisibleLayers(aVisibleLayers); + } + } + break; + } + case WID_PAGE_BACKOBJVIS: + { + bool bVisible = false; + if( ! ( aValue >>= bVisible ) ) + throw lang::IllegalArgumentException(); + + SdrPage* pPage = GetPage(); + if( pPage ) + { + SdDrawDocument& rDoc(static_cast< SdDrawDocument& >(pPage->getSdrModelFromSdrPage())); + if( rDoc.GetMasterPageCount() ) + { + SdrLayerAdmin& rLayerAdmin = rDoc.GetLayerAdmin(); + SdrLayerIDSet aVisibleLayers = pPage->TRG_GetMasterPageVisibleLayers(); + aVisibleLayers.Set(rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects), bVisible); + pPage->TRG_SetMasterPageVisibleLayers(aVisibleLayers); + } + } + + break; + } + case WID_PAGE_USERATTRIBS: + { + if( !GetPage()->setAlienAttributes( aValue ) ) + throw lang::IllegalArgumentException(); + break; + } + case WID_PAGE_BOOKMARK: + { + OUString aBookmarkURL; + if( ! ( aValue >>= aBookmarkURL ) ) + throw lang::IllegalArgumentException(); + + setBookmarkURL( aBookmarkURL ); + break; + } + + case WID_PAGE_HEADERVISIBLE: + case WID_PAGE_HEADERTEXT: + case WID_PAGE_FOOTERVISIBLE: + case WID_PAGE_FOOTERTEXT: + case WID_PAGE_PAGENUMBERVISIBLE: + case WID_PAGE_DATETIMEVISIBLE: + case WID_PAGE_DATETIMEFIXED: + case WID_PAGE_DATETIMETEXT: + case WID_PAGE_DATETIMEFORMAT: + { + sd::HeaderFooterSettings aHeaderFooterSettings( GetPage()->getHeaderFooterSettings() ); + + switch( pEntry->nWID ) + { + case WID_PAGE_HEADERVISIBLE: + { + bool bVisible = false; + if( ! ( aValue >>= bVisible ) ) + throw lang::IllegalArgumentException(); + + aHeaderFooterSettings.mbHeaderVisible = bVisible; + break; + } + case WID_PAGE_HEADERTEXT: + { + OUString aText; + if( ! ( aValue >>= aText ) ) + throw lang::IllegalArgumentException(); + + aHeaderFooterSettings.maHeaderText = aText; + break; + } + case WID_PAGE_FOOTERVISIBLE: + { + bool bVisible = false; + if( ! ( aValue >>= bVisible ) ) + throw lang::IllegalArgumentException(); + + aHeaderFooterSettings.mbFooterVisible = bVisible; + break; + } + case WID_PAGE_FOOTERTEXT: + { + OUString aText; + if( ! ( aValue >>= aText ) ) + throw lang::IllegalArgumentException(); + + aHeaderFooterSettings.maFooterText = aText; + break; + } + case WID_PAGE_PAGENUMBERVISIBLE: + { + bool bVisible = false; + if( ! ( aValue >>= bVisible ) ) + throw lang::IllegalArgumentException(); + + aHeaderFooterSettings.mbSlideNumberVisible = bVisible; + break; + } + case WID_PAGE_DATETIMEVISIBLE: + { + bool bVisible = false; + if( ! ( aValue >>= bVisible ) ) + throw lang::IllegalArgumentException(); + + aHeaderFooterSettings.mbDateTimeVisible = bVisible; + break; + } + case WID_PAGE_DATETIMEFIXED: + { + bool bVisible = false; + if( ! ( aValue >>= bVisible ) ) + throw lang::IllegalArgumentException(); + + aHeaderFooterSettings.mbDateTimeIsFixed = bVisible; + break; + } + case WID_PAGE_DATETIMETEXT: + { + OUString aText; + if( ! ( aValue >>= aText ) ) + throw lang::IllegalArgumentException(); + + aHeaderFooterSettings.maDateTimeText = aText; + break; + } + case WID_PAGE_DATETIMEFORMAT: + { + sal_Int32 nValue = 0; + if( ! ( aValue >>= nValue ) ) + throw lang::IllegalArgumentException(); + + aHeaderFooterSettings.meDateFormat = static_cast<SvxDateFormat>(nValue & 0x0f); + aHeaderFooterSettings.meTimeFormat = static_cast<SvxTimeFormat>((nValue >> 4) & 0x0f); + break; + } + } + + if( !(aHeaderFooterSettings == GetPage()->getHeaderFooterSettings()) ) + GetPage()->setHeaderFooterSettings( aHeaderFooterSettings ); + + break; + } + + case WID_PAGE_NUMBER: + if( (GetPage()->GetPageKind() == PageKind::Handout) && !GetPage()->IsMasterPage() ) + { + if( !(aValue >>= mnTempPageNumber) ) + throw lang::IllegalArgumentException(); + + break; + } + throw beans::PropertyVetoException(); + + case WID_PAGE_LDBITMAP: + case WID_PAGE_LDNAME: + case WID_PAGE_ISDARK: + throw beans::PropertyVetoException(); + + case WID_TRANSITION_TYPE: + { + sal_Int16 nValue = 0; + if( ! ( aValue >>= nValue ) ) + throw lang::IllegalArgumentException(); + + GetPage()->setTransitionType( nValue ); + break; + } + + case WID_TRANSITION_SUBTYPE: + { + sal_Int16 nValue = 0; + if( ! ( aValue >>= nValue ) ) + throw lang::IllegalArgumentException(); + + GetPage()->setTransitionSubtype( nValue ); + break; + } + + case WID_TRANSITION_DIRECTION: + { + bool bValue = false; + if( ! ( aValue >>= bValue ) ) + throw lang::IllegalArgumentException(); + + GetPage()->setTransitionDirection( bValue ); + break; + } + + case WID_TRANSITION_FADE_COLOR: + { + sal_Int32 nValue = 0; + if( ! ( aValue >>= nValue ) ) + throw lang::IllegalArgumentException(); + + GetPage()->setTransitionFadeColor( nValue ); + break; + } + + case WID_TRANSITION_DURATION: + { + double fValue = 0.0; + if( ! ( aValue >>= fValue ) ) + throw lang::IllegalArgumentException(); + + GetPage()->setTransitionDuration( fValue ); + break; + } + + case WID_PAGE_THEME: + { + SdrPage* pPage = GetPage(); + std::unique_ptr<svx::Theme> pTheme = svx::Theme::FromAny(aValue); + pPage->getSdrPageProperties().SetTheme(std::move(pTheme)); + break; + } + + default: + throw beans::UnknownPropertyException( aPropertyName, static_cast<cppu::OWeakObject*>(this)); + } + + GetModel()->SetModified(); +} + +Any SAL_CALL SdGenericDrawPage::getPropertyValue( const OUString& PropertyName ) +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + uno::Any aAny; + + const SfxItemPropertyMapEntry* pEntry = mpPropSet->getPropertyMapEntry(PropertyName); + + sal_Int16 nEntry = pEntry ? pEntry->nWID : -1; + switch (nEntry) + { + case WID_NAVORDER: + aAny = getNavigationOrder(); + break; + case WID_PAGE_LEFT: + aAny <<= GetPage()->GetLeftBorder(); + break; + case WID_PAGE_RIGHT: + aAny <<= GetPage()->GetRightBorder(); + break; + case WID_PAGE_TOP: + aAny <<= GetPage()->GetUpperBorder(); + break; + case WID_PAGE_BOTTOM: + aAny <<= GetPage()->GetLowerBorder(); + break; + case WID_PAGE_WIDTH: + aAny <<= static_cast<sal_Int32>( GetPage()->GetSize().getWidth() ); + break; + case WID_PAGE_HEIGHT: + aAny <<= static_cast<sal_Int32>( GetPage()->GetSize().getHeight() ); + break; + case WID_PAGE_ORIENT: + aAny <<= + GetPage()->GetOrientation() == Orientation::Portrait + ? view::PaperOrientation_PORTRAIT + : view::PaperOrientation_LANDSCAPE; + break; + case WID_PAGE_EFFECT: + aAny <<= GetPage()->GetFadeEffect(); + break; + case WID_PAGE_CHANGE: + aAny <<= static_cast<sal_Int32>( GetPage()->GetPresChange() ); + break; + case WID_PAGE_SPEED: + { + const double fDuration = GetPage()->getTransitionDuration(); + aAny <<= presentation::AnimationSpeed( + fDuration < 2.0 ? 2 : fDuration > 2.0 ? 0 : 1); + } + break; + case WID_PAGE_LAYOUT: + aAny <<= static_cast<sal_Int16>( GetPage()->GetAutoLayout() ); + break; + case WID_PAGE_NUMBER: + { + const sal_uInt16 nPageNumber(GetPage()->GetPageNum()); + + if(nPageNumber > 0) + { + // for all other pages calculate the number + aAny <<= static_cast<sal_Int16>(static_cast<sal_uInt16>((nPageNumber-1)>>1) + 1); + } + else + { + aAny <<= mnTempPageNumber; + } + } + break; + case WID_PAGE_DURATION: + aAny <<= static_cast<sal_Int32>( GetPage()->GetTime() + .5 ); + break; + case WID_PAGE_HIGHRESDURATION: + aAny <<= GetPage()->GetTime(); + break; + case WID_PAGE_LDNAME: + { + const OUString aName( GetPage()->GetName() ); + aAny <<= aName; + break; + } + case WID_PAGE_LDBITMAP: + { + Reference< awt::XBitmap > xBitmap(VCLUnoHelper::CreateBitmap(BitmapEx(BMP_PAGE))); + aAny <<= xBitmap; + } + break; + case WID_PAGE_BACK: + getBackground( aAny ); + break; + case WID_PAGE_PREVIEW : + case WID_PAGE_PREVIEWMETAFILE : + { + SdDrawDocument& rDoc(static_cast< SdDrawDocument& >(GetPage()->getSdrModelFromSdrPage())); + ::sd::DrawDocShell* pDocShell = rDoc.GetDocSh(); + if ( pDocShell ) + { + sal_uInt16 nPgNum = 0; + sal_uInt16 nPageCount = rDoc.GetSdPageCount( PageKind::Standard ); + sal_uInt16 nPageNumber = static_cast<sal_uInt16>( ( GetPage()->GetPageNum() - 1 ) >> 1 ); + while( nPgNum < nPageCount ) + { + rDoc.SetSelected( rDoc.GetSdPage( nPgNum, PageKind::Standard ), nPgNum == nPageNumber ); + nPgNum++; + } + std::shared_ptr<GDIMetaFile> xMetaFile = pDocShell->GetPreviewMetaFile(); + if (xMetaFile) + { + Size aSize( GetPage()->GetSize() ); + xMetaFile->AddAction( new MetaFillColorAction( COL_WHITE, true ), 0 ); + xMetaFile->AddAction( new MetaRectAction( ::tools::Rectangle( Point(), aSize ) ), 1 ); + xMetaFile->SetPrefMapMode(MapMode(MapUnit::Map100thMM)); + xMetaFile->SetPrefSize( aSize ); + + SvMemoryStream aDestStrm( 65535, 65535 ); + if (nEntry == WID_PAGE_PREVIEW) + // Preview: WMF format. + ConvertGDIMetaFileToWMF(*xMetaFile, aDestStrm, nullptr, false); + else + { + // PreviewMetafile: SVM format. + SvmWriter aWriter(aDestStrm); + aWriter.Write(*xMetaFile); + } + Sequence<sal_Int8> aSeq( static_cast<sal_Int8 const *>(aDestStrm.GetData()), aDestStrm.Tell() ); + aAny <<= aSeq; + } + } + } + break; + + case WID_PAGE_PREVIEWBITMAP : + { + SdDrawDocument& rDoc(static_cast< SdDrawDocument& >(GetPage()->getSdrModelFromSdrPage())); + ::sd::DrawDocShell* pDocShell = rDoc.GetDocSh(); + if ( pDocShell ) + { + sal_uInt16 nPgNum = 0; + sal_uInt16 nPageCount = rDoc.GetSdPageCount( PageKind::Standard ); + sal_uInt16 nPageNumber = static_cast<sal_uInt16>( ( GetPage()->GetPageNum() - 1 ) >> 1 ); + while( nPgNum < nPageCount ) + { + rDoc.SetSelected( rDoc.GetSdPage( nPgNum, PageKind::Standard ), nPgNum == nPageNumber ); + nPgNum++; + } + std::shared_ptr<GDIMetaFile> xMetaFile = pDocShell->GetPreviewMetaFile(); + BitmapEx aBitmap; + if (xMetaFile && xMetaFile->CreateThumbnail(aBitmap)) + { + SvMemoryStream aMemStream; + WriteDIB(aBitmap.GetBitmap(), aMemStream, false, false); + uno::Sequence<sal_Int8> aSeq( static_cast<sal_Int8 const *>(aMemStream.GetData()), aMemStream.Tell() ); + aAny <<= aSeq; + } + } + } + break; + + case WID_PAGE_VISIBLE : + { + bool bVisible = !GetPage()->IsExcluded(); + aAny <<= bVisible; + break; + } + + case WID_PAGE_SOUNDFILE : + { + if( GetPage()->IsStopSound() ) + { + aAny <<= true; + } + else + { + OUString aURL; + if( GetPage()->IsSoundOn() ) + aURL = GetPage()->GetSoundFile(); + aAny <<= aURL; + } + break; + } + case WID_LOOP_SOUND: + { + aAny <<= GetPage()->IsLoopSound(); + break; + } + case WID_PAGE_BACKFULL: + { + bool bFullSize = GetPage()->IsBackgroundFullSize(); + aAny <<= bFullSize; + break; + } + case WID_PAGE_BACKVIS: + { + SdrPage* pPage = GetPage(); + if( pPage ) + { + SdDrawDocument& rDoc(static_cast< SdDrawDocument& >(pPage->getSdrModelFromSdrPage())); + if( rDoc.GetMasterPageCount() ) + { + SdrLayerAdmin& rLayerAdmin = rDoc.GetLayerAdmin(); + SdrLayerIDSet aVisibleLayers = pPage->TRG_GetMasterPageVisibleLayers(); + aAny <<= aVisibleLayers.IsSet(rLayerAdmin.GetLayerID(sUNO_LayerName_background)); + } + else + { + aAny <<= false; + } + } + break; + } + case WID_PAGE_BACKOBJVIS: + { + SdrPage* pPage = GetPage(); + if( pPage ) + { + SdDrawDocument& rDoc(static_cast< SdDrawDocument& >(pPage->getSdrModelFromSdrPage())); + if( rDoc.GetMasterPageCount() ) + { + SdrLayerAdmin& rLayerAdmin = rDoc.GetLayerAdmin(); + SdrLayerIDSet aVisibleLayers = pPage->TRG_GetMasterPageVisibleLayers(); + aAny <<= aVisibleLayers.IsSet(rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects)); + } + else + { + aAny <<= false; + } + } + break; + } + case WID_PAGE_USERATTRIBS: + { + GetPage()->getAlienAttributes( aAny ); + break; + } + case WID_PAGE_BOOKMARK: + { + aAny <<= getBookmarkURL(); + break; + } + case WID_PAGE_ISDARK: + { + aAny <<= GetPage()->GetPageBackgroundColor().IsDark(); + break; + } + case WID_PAGE_HEADERVISIBLE: + aAny <<= GetPage()->getHeaderFooterSettings().mbHeaderVisible; + break; + case WID_PAGE_HEADERTEXT: + { + const OUString aText( GetPage()->getHeaderFooterSettings().maHeaderText ); + aAny <<= aText; + } + break; + case WID_PAGE_FOOTERVISIBLE: + aAny <<= GetPage()->getHeaderFooterSettings().mbFooterVisible; + break; + case WID_PAGE_FOOTERTEXT: + { + const OUString aText( GetPage()->getHeaderFooterSettings().maFooterText ); + aAny <<= aText; + } + break; + case WID_PAGE_PAGENUMBERVISIBLE: + aAny <<= GetPage()->getHeaderFooterSettings().mbSlideNumberVisible; + break; + case WID_PAGE_DATETIMEVISIBLE: + aAny <<= GetPage()->getHeaderFooterSettings().mbDateTimeVisible; + break; + case WID_PAGE_DATETIMEFIXED: + aAny <<= GetPage()->getHeaderFooterSettings().mbDateTimeIsFixed; + break; + case WID_PAGE_DATETIMETEXT: + { + const OUString aText( GetPage()->getHeaderFooterSettings().maDateTimeText ); + aAny <<= aText; + } + break; + case WID_PAGE_DATETIMEFORMAT: + { + auto const & rSettings = GetPage()->getHeaderFooterSettings(); + sal_Int32 x = static_cast<sal_Int32>(rSettings.meDateFormat) | (static_cast<sal_Int32>(rSettings.meTimeFormat) << 4); + aAny <<= x; + } + break; + + case WID_TRANSITION_TYPE: + aAny <<= GetPage()->getTransitionType(); + break; + + case WID_TRANSITION_SUBTYPE: + aAny <<= GetPage()->getTransitionSubtype(); + break; + + case WID_TRANSITION_DIRECTION: + aAny <<= GetPage()->getTransitionDirection(); + break; + + case WID_TRANSITION_FADE_COLOR: + aAny <<= GetPage()->getTransitionFadeColor(); + break; + + case WID_TRANSITION_DURATION: + aAny <<= GetPage()->getTransitionDuration(); + break; + + case WID_PAGE_THEME: + { + SdrPage* pPage = GetPage(); + svx::Theme* pTheme = pPage->getSdrPageProperties().GetTheme(); + if (pTheme) + { + pTheme->ToAny(aAny); + } + else + { + beans::PropertyValues aValues; + aAny <<= aValues; + } + break; + } + + default: + throw beans::UnknownPropertyException( PropertyName, static_cast<cppu::OWeakObject*>(this)); + } + return aAny; +} + +void SAL_CALL SdGenericDrawPage::addPropertyChangeListener( const OUString& , const Reference< beans::XPropertyChangeListener >& ) {} +void SAL_CALL SdGenericDrawPage::removePropertyChangeListener( const OUString& , const Reference< beans::XPropertyChangeListener >& ) {} +void SAL_CALL SdGenericDrawPage::addVetoableChangeListener( const OUString& , const Reference< beans::XVetoableChangeListener >& ) {} +void SAL_CALL SdGenericDrawPage::removeVetoableChangeListener( const OUString& , const Reference< beans::XVetoableChangeListener >& ) {} + +// XMultiPropertySet +void SAL_CALL SdGenericDrawPage::setPropertyValues( const Sequence< OUString >& aPropertyNames, const Sequence< Any >& aValues ) +{ + if( aPropertyNames.getLength() != aValues.getLength() ) + throw lang::IllegalArgumentException(); + + const OUString* pNames = aPropertyNames.getConstArray(); + const Any* pValues = aValues.getConstArray(); + sal_uInt32 nCount = aValues.getLength(); + while( nCount-- ) + { + try + { + setPropertyValue( *pNames++, *pValues++ ); + } + catch( beans::UnknownPropertyException& ) + { + // ignore for multi property set + // todo: optimize this! + } + } +} + +Sequence< Any > SAL_CALL SdGenericDrawPage::getPropertyValues( const Sequence< OUString >& aPropertyNames ) +{ + sal_Int32 nCount = aPropertyNames.getLength(); + Sequence< Any > aValues( nCount ); + std::transform(aPropertyNames.begin(), aPropertyNames.end(), aValues.getArray(), + [this](const OUString& rName) -> Any { + Any aValue; + try + { + aValue = getPropertyValue(rName); + } + catch( beans::UnknownPropertyException& ) + { + // ignore for multi property set + // todo: optimize this! + } + return aValue; + }); + return aValues; +} + +void SAL_CALL SdGenericDrawPage::addPropertiesChangeListener( const Sequence< OUString >& , const Reference< beans::XPropertiesChangeListener >& ) +{ +} + +void SAL_CALL SdGenericDrawPage::removePropertiesChangeListener( const Reference< beans::XPropertiesChangeListener >& ) +{ +} + +void SAL_CALL SdGenericDrawPage::firePropertiesChangeEvent( const Sequence< OUString >& , const Reference< beans::XPropertiesChangeListener >& ) +{ +} + +Reference< drawing::XShape > SdGenericDrawPage::CreateShape(SdrObject *pObj) const +{ + DBG_ASSERT( GetPage(), "SdGenericDrawPage::CreateShape(), can't create shape for disposed page!" ); + DBG_ASSERT( pObj, "SdGenericDrawPage::CreateShape(), invalid call with pObj == 0!" ); + + if (!pObj) + return Reference< drawing::XShape >(); + + if (GetPage()) + { + PresObjKind eKind = GetPage()->GetPresObjKind(pObj); + + rtl::Reference<SvxShape> pShape; + + if(pObj->GetObjInventor() == SdrInventor::Default) + { + SdrObjKind nInventor = pObj->GetObjIdentifier(); + switch( nInventor ) + { + case SdrObjKind::TitleText: + pShape = new SvxShapeText( pObj ); + if( GetPage()->GetPageKind() == PageKind::Notes && GetPage()->IsMasterPage() ) + { + // fake an empty PageShape if it's a title shape on the master page + pShape->SetShapeType("com.sun.star.presentation.PageShape"); + } + else + { + pShape->SetShapeType("com.sun.star.presentation.TitleTextShape"); + } + eKind = PresObjKind::NONE; + break; + case SdrObjKind::OutlineText: + pShape = new SvxShapeText( pObj ); + pShape->SetShapeType("com.sun.star.presentation.OutlinerShape"); + eKind = PresObjKind::NONE; + break; + default: ; + } + } + + Reference< drawing::XShape > xShape( pShape ); + + if(!xShape.is()) + xShape = SvxFmDrawPage::CreateShape( pObj ); + + if( eKind != PresObjKind::NONE ) + { + OUString aShapeType("com.sun.star.presentation."); + + switch( eKind ) + { + case PresObjKind::Title: + aShapeType += "TitleTextShape"; + break; + case PresObjKind::Outline: + aShapeType += "OutlinerShape"; + break; + case PresObjKind::Text: + aShapeType += "SubtitleShape"; + break; + case PresObjKind::Graphic: + aShapeType += "GraphicObjectShape"; + break; + case PresObjKind::Object: + aShapeType += "OLE2Shape"; + break; + case PresObjKind::Chart: + aShapeType += "ChartShape"; + break; + case PresObjKind::OrgChart: + aShapeType += "OrgChartShape"; + break; + case PresObjKind::Calc: + aShapeType += "CalcShape"; + break; + case PresObjKind::Table: + aShapeType += "TableShape"; + break; + case PresObjKind::Media: + aShapeType += "MediaShape"; + break; + case PresObjKind::Page: + aShapeType += "PageShape"; + break; + case PresObjKind::Handout: + aShapeType += "HandoutShape"; + break; + case PresObjKind::Notes: + aShapeType += "NotesShape"; + break; + case PresObjKind::Footer: + aShapeType += "FooterShape"; + break; + case PresObjKind::Header: + aShapeType += "HeaderShape"; + break; + case PresObjKind::SlideNumber: + aShapeType += "SlideNumberShape"; + break; + case PresObjKind::DateTime: + aShapeType += "DateTimeShape"; + break; + // coverity[dead_error_begin] - following conditions exist to avoid compiler warning + case PresObjKind::NONE: + break; + } + + if( !pShape ) + pShape = comphelper::getFromUnoTunnel<SvxShape>( xShape ); + + if( pShape ) + pShape->SetShapeType( aShapeType ); + } + + SvxShape *pSdShape = comphelper::getFromUnoTunnel<SvxShape>(xShape); + if (pSdShape) + { + // SdXShape aggregates SvxShape + new SdXShape(pSdShape, GetModel()); + } + return xShape; + } + else + { + return SvxFmDrawPage::CreateShape( pObj ); + } + +} + +// XServiceInfo +Sequence< OUString > SAL_CALL SdGenericDrawPage::getSupportedServiceNames() +{ + return comphelper::concatSequences( + SvxFmDrawPage::getSupportedServiceNames(), + std::initializer_list<std::u16string_view>{ u"com.sun.star.drawing.GenericDrawPage", + u"com.sun.star.document.LinkTarget", + u"com.sun.star.document.LinkTargetSupplier" }); +} + +// XLinkTargetSupplier +Reference< container::XNameAccess > SAL_CALL SdGenericDrawPage::getLinks( ) +{ + return new SdPageLinkTargets( this ); +} + +void SdGenericDrawPage::setBackground( const Any& ) +{ + OSL_FAIL( "Don't call me, I'm useless!" ); +} + +void SdGenericDrawPage::getBackground( Any& ) +{ + OSL_FAIL( "Don't call me, I'm useless!" ); +} + +OUString SdGenericDrawPage::getBookmarkURL() const +{ + OUStringBuffer aRet; + if( SvxFmDrawPage::mpPage ) + { + OUString aFileName( static_cast<SdPage*>(SvxFmDrawPage::mpPage)->GetFileName() ); + if( !aFileName.isEmpty() ) + { + const OUString aBookmarkName( SdDrawPage::getPageApiNameFromUiName( static_cast<SdPage*>(SvxFmDrawPage::mpPage)->GetBookmarkName() ) ); + aRet.append( aFileName ); + aRet.append( '#' ); + aRet.append( aBookmarkName ); + } + } + + return aRet.makeStringAndClear(); +} + +void SdGenericDrawPage::setBookmarkURL( std::u16string_view rURL ) +{ + if( !SvxFmDrawPage::mpPage ) + return; + + size_t nIndex = rURL.find( '#' ); + if( nIndex == std::u16string_view::npos ) + return; + + const OUString aFileName( rURL.substr( 0, nIndex ) ); + const OUString aBookmarkName( SdDrawPage::getUiNameFromPageApiName( OUString(rURL.substr( nIndex+1 )) ) ); + + if( !aFileName.isEmpty() && !aBookmarkName.isEmpty() ) + { + static_cast<SdPage*>(SvxFmDrawPage::mpPage)->DisconnectLink(); + static_cast<SdPage*>(SvxFmDrawPage::mpPage)->SetFileName( aFileName ); + static_cast<SdPage*>(SvxFmDrawPage::mpPage)->SetBookmarkName( aBookmarkName ); + static_cast<SdPage*>(SvxFmDrawPage::mpPage)->ConnectLink(); + } +} + +Reference< drawing::XShape > SAL_CALL SdGenericDrawPage::combine( const Reference< drawing::XShapes >& xShapes ) +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + DBG_ASSERT(SvxFmDrawPage::mpPage,"SdrPage is NULL! [CL]"); + DBG_ASSERT(mpView, "SdrView is NULL! [CL]"); + + Reference< drawing::XShape > xShape; + if(mpView==nullptr||!xShapes.is()||GetPage()==nullptr) + return xShape; + + SdrPageView* pPageView = mpView->ShowSdrPage( GetPage() ); + + SelectObjectsInView( xShapes, pPageView ); + + mpView->CombineMarkedObjects( false ); + + mpView->AdjustMarkHdl(); + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + if( rMarkList.GetMarkCount() == 1 ) + { + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + if( pObj ) + xShape.set( pObj->getUnoShape(), UNO_QUERY ); + } + + mpView->HideSdrPage(); + + GetModel()->SetModified(); + + return xShape; +} + +void SAL_CALL SdGenericDrawPage::split( const Reference< drawing::XShape >& xGroup ) +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + if(mpView==nullptr||!xGroup.is()||GetPage()==nullptr) + return; + + SdrPageView* pPageView = mpView->ShowSdrPage( GetPage() ); + SelectObjectInView( xGroup, pPageView ); + mpView->DismantleMarkedObjects(); + mpView->HideSdrPage(); + + GetModel()->SetModified(); +} + +Reference< drawing::XShape > SAL_CALL SdGenericDrawPage::bind( const Reference< drawing::XShapes >& xShapes ) +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + uno::Reference< drawing::XShape > xShape; + if(mpView==nullptr||!xShapes.is()||GetPage()==nullptr) + return xShape; + + SdrPageView* pPageView = mpView->ShowSdrPage( GetPage() ); + + SelectObjectsInView( xShapes, pPageView ); + + mpView->CombineMarkedObjects(); + + mpView->AdjustMarkHdl(); + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + if( rMarkList.GetMarkCount() == 1 ) + { + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + if( pObj ) + xShape.set( pObj->getUnoShape(), UNO_QUERY ); + } + + mpView->HideSdrPage(); + + GetModel()->SetModified(); + + return xShape; +} + +void SAL_CALL SdGenericDrawPage::unbind( const Reference< drawing::XShape >& xShape ) +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + if(mpView==nullptr||!xShape.is()||GetPage()==nullptr) + return; + + SdrPageView* pPageView = mpView->ShowSdrPage( GetPage() ); + SelectObjectInView( xShape, pPageView ); + mpView->DismantleMarkedObjects( true ); + mpView->HideSdrPage(); + + GetModel()->SetModified(); +} + +void SdGenericDrawPage::SetLeftBorder( sal_Int32 nValue ) +{ + if( nValue == GetPage()->GetLeftBorder() ) + return; + + SdDrawDocument& rDoc(static_cast< SdDrawDocument& >(GetPage()->getSdrModelFromSdrPage())); + const PageKind ePageKind = GetPage()->GetPageKind(); + + sal_uInt16 i, nPageCnt = rDoc.GetMasterSdPageCount(ePageKind); + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = rDoc.GetMasterSdPage(i, ePageKind); + pPage->SetLeftBorder( nValue ); + } + + nPageCnt = rDoc.GetSdPageCount(ePageKind); + + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = rDoc.GetSdPage(i, ePageKind); + pPage->SetLeftBorder( nValue ); + } +} + +void SdGenericDrawPage::SetRightBorder( sal_Int32 nValue ) +{ + if( nValue == GetPage()->GetRightBorder() ) + return; + + SdDrawDocument& rDoc(static_cast< SdDrawDocument& >(GetPage()->getSdrModelFromSdrPage())); + const PageKind ePageKind = GetPage()->GetPageKind(); + + sal_uInt16 i, nPageCnt = rDoc.GetMasterSdPageCount(ePageKind); + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = rDoc.GetMasterSdPage(i, ePageKind); + pPage->SetRightBorder( nValue ); + } + + nPageCnt = rDoc.GetSdPageCount(ePageKind); + + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = rDoc.GetSdPage(i, ePageKind); + pPage->SetRightBorder( nValue ); + } +} + +void SdGenericDrawPage::SetUpperBorder( sal_Int32 nValue ) +{ + if( nValue == GetPage()->GetUpperBorder() ) + return; + + SdDrawDocument& rDoc(static_cast< SdDrawDocument& >(GetPage()->getSdrModelFromSdrPage())); + const PageKind ePageKind = GetPage()->GetPageKind(); + + sal_uInt16 i, nPageCnt = rDoc.GetMasterSdPageCount(ePageKind); + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = rDoc.GetMasterSdPage(i, ePageKind); + pPage->SetUpperBorder( nValue ); + } + + nPageCnt = rDoc.GetSdPageCount(ePageKind); + + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = rDoc.GetSdPage(i, ePageKind); + pPage->SetUpperBorder( nValue ); + } +} + +void SdGenericDrawPage::SetLowerBorder( sal_Int32 nValue ) +{ + if( nValue == GetPage()->GetLowerBorder() ) + return; + + SdDrawDocument& rDoc(static_cast< SdDrawDocument& >(GetPage()->getSdrModelFromSdrPage())); + const PageKind ePageKind = GetPage()->GetPageKind(); + + sal_uInt16 i, nPageCnt = rDoc.GetMasterSdPageCount(ePageKind); + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = rDoc.GetMasterSdPage(i, ePageKind); + pPage->SetLowerBorder( nValue ); + } + + nPageCnt = rDoc.GetSdPageCount(ePageKind); + + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = rDoc.GetSdPage(i, ePageKind); + pPage->SetLowerBorder( nValue ); + } +} + +static void refreshpage( SdDrawDocument* pDoc, const PageKind ePageKind ) +{ + ::sd::DrawDocShell* pDocShell = pDoc->GetDocSh(); + if ( !pDocShell ) + return; + + ::sd::ViewShell* pViewSh = pDocShell->GetViewShell(); + + if( !pViewSh ) + return; + + if( auto pDrawViewShell = dynamic_cast<::sd::DrawViewShell* >(pViewSh) ) + pDrawViewShell->ResetActualPage(); + + Size aPageSize = pDoc->GetSdPage(0, ePageKind)->GetSize(); + const tools::Long nWidth = aPageSize.Width(); + const tools::Long nHeight = aPageSize.Height(); + + Point aPageOrg(nWidth, nHeight / 2); + Size aViewSize(nWidth * 3, nHeight * 2); + + pDoc->SetMaxObjSize(aViewSize); + + pViewSh->InitWindows(aPageOrg, aViewSize, Point(-1, -1), true); + + pViewSh->UpdateScrollBars(); +} + +void SdGenericDrawPage::SetWidth( sal_Int32 nWidth ) +{ + Size aSize( GetPage()->GetSize() ); + if( aSize.getWidth() == nWidth ) + return; + + aSize.setWidth( nWidth ); + + SdDrawDocument& rDoc(static_cast< SdDrawDocument& >(GetPage()->getSdrModelFromSdrPage())); + const PageKind ePageKind = GetPage()->GetPageKind(); + + sal_uInt16 i, nPageCnt = rDoc.GetMasterSdPageCount(ePageKind); + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = rDoc.GetMasterSdPage(i, ePageKind); + pPage->SetSize(aSize); + } + + nPageCnt = rDoc.GetSdPageCount(ePageKind); + + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = rDoc.GetSdPage(i, ePageKind); + pPage->SetSize(aSize); + } + + refreshpage( &rDoc, ePageKind ); +} + +void SdGenericDrawPage::SetHeight( sal_Int32 nHeight ) +{ + Size aSize( GetPage()->GetSize() ); + if( aSize.getHeight() == nHeight ) + return; + + aSize.setHeight( nHeight ); + + SdDrawDocument& rDoc(static_cast< SdDrawDocument& >(GetPage()->getSdrModelFromSdrPage())); + const PageKind ePageKind = GetPage()->GetPageKind(); + + sal_uInt16 i, nPageCnt = rDoc.GetMasterSdPageCount(ePageKind); + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = rDoc.GetMasterSdPage(i, ePageKind); + pPage->SetSize(aSize); + } + + nPageCnt = rDoc.GetSdPageCount(ePageKind); + + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = rDoc.GetSdPage(i, ePageKind); + pPage->SetSize(aSize); + } + + refreshpage( &rDoc, ePageKind ); +} + +// XInterface +void SdGenericDrawPage::release() noexcept +{ + + OWeakAggObject::release(); +} + +// XComponent +void SdGenericDrawPage::disposing() noexcept +{ + mpDocModel = nullptr; + SvxFmDrawPage::disposing(); +} + +// XAnimationNodeSupplier +Reference< XAnimationNode > SAL_CALL SdGenericDrawPage::getAnimationNode() +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + SdPage *pSdPage = static_cast<SdPage*>(SvxFmDrawPage::mpPage); + + return pSdPage->getAnimationNode(); +} + +// SdPageLinkTargets +SdPageLinkTargets::SdPageLinkTargets( SdGenericDrawPage* pUnoPage ) noexcept +{ + mxPage = pUnoPage; + mpUnoPage = pUnoPage; +} + +SdPageLinkTargets::~SdPageLinkTargets() noexcept +{ +} + +// XElementAccess +uno::Type SAL_CALL SdPageLinkTargets::getElementType() +{ + return cppu::UnoType<beans::XPropertySet>::get(); +} + +sal_Bool SAL_CALL SdPageLinkTargets::hasElements() +{ + ::SolarMutexGuard aGuard; + + SdPage* pPage = mpUnoPage->GetPage(); + if( pPage != nullptr ) + { + SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups ); + + while( aIter.IsMore() ) + { + SdrObject* pObj = aIter.Next(); + OUString aStr( pObj->GetName() ); + if( aStr.isEmpty() ) + if (auto pOleObj = dynamic_cast< const SdrOle2Obj *>( pObj )) + aStr = pOleObj->GetPersistName(); + if( !aStr.isEmpty() ) + return true; + } + } + + return false; +} + +// container::XNameAccess + +// XNameAccess +Any SAL_CALL SdPageLinkTargets::getByName( const OUString& aName ) +{ + ::SolarMutexGuard aGuard; + + SdPage* pPage = mpUnoPage->GetPage(); + if( pPage != nullptr ) + { + SdrObject* pObj = FindObject( aName ); + if( pObj ) + { + Reference< beans::XPropertySet > aRef( pObj->getUnoShape(), uno::UNO_QUERY ); + return Any( aRef ); + } + } + + throw container::NoSuchElementException(); +} + +Sequence< OUString > SAL_CALL SdPageLinkTargets::getElementNames() +{ + ::SolarMutexGuard aGuard; + + sal_uInt32 nObjCount = 0; + + SdPage* pPage = mpUnoPage->GetPage(); + if( pPage != nullptr ) + { + SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups ); + while( aIter.IsMore() ) + { + SdrObject* pObj = aIter.Next(); + OUString aStr( pObj->GetName() ); + if( aStr.isEmpty() ) + if (auto pOleObj = dynamic_cast< const SdrOle2Obj *>( pObj )) + aStr = pOleObj->GetPersistName(); + if( !aStr.isEmpty() ) + nObjCount++; + } + } + + Sequence< OUString > aSeq( nObjCount ); + if( nObjCount > 0 ) + { + OUString* pStr = aSeq.getArray(); + + SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups ); + while( aIter.IsMore() ) + { + SdrObject* pObj = aIter.Next(); + OUString aStr( pObj->GetName() ); + if( aStr.isEmpty() ) + if (auto pOleObj = dynamic_cast< const SdrOle2Obj *>( pObj )) + aStr = pOleObj->GetPersistName(); + if( !aStr.isEmpty() ) + *pStr++ = aStr; + } + } + + return aSeq; +} + +sal_Bool SAL_CALL SdPageLinkTargets::hasByName( const OUString& aName ) +{ + ::SolarMutexGuard aGuard; + + return FindObject( aName ) != nullptr; +} + +SdrObject* SdPageLinkTargets::FindObject( std::u16string_view rName ) const noexcept +{ + SdPage* pPage = mpUnoPage->GetPage(); + if( pPage == nullptr ) + return nullptr; + + SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups ); + + while( aIter.IsMore() ) + { + SdrObject* pObj = aIter.Next(); + OUString aStr( pObj->GetName() ); + if( aStr.isEmpty() ) + if (auto pOleObj = dynamic_cast< const SdrOle2Obj *>( pObj )) + aStr = pOleObj->GetPersistName(); + if( !aStr.isEmpty() && (aStr == rName) ) + return pObj; + } + + return nullptr; +} + +// XServiceInfo +OUString SAL_CALL SdPageLinkTargets::getImplementationName() +{ + return "SdPageLinkTargets"; +} + +sal_Bool SAL_CALL SdPageLinkTargets::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +Sequence< OUString > SAL_CALL SdPageLinkTargets::getSupportedServiceNames() +{ + return { "com.sun.star.document.LinkTargets" }; +} + +// SdDrawPage +SdDrawPage::SdDrawPage(SdXImpressDocument* pModel, SdPage* pPage) + : SdGenericDrawPage( pModel, pPage, ImplGetDrawPagePropertySet( pModel->IsImpressDocument(), pPage->GetPageKind() ) ) +{ +} + +SdDrawPage::~SdDrawPage() noexcept +{ +} + +// XInterface +Any SAL_CALL SdDrawPage::queryInterface( const uno::Type & rType ) +{ + if( rType == cppu::UnoType<drawing::XMasterPageTarget>::get() ) + { + return Any( Reference< drawing::XMasterPageTarget >( this ) ); + } + else if( IsImpressDocument() + && rType == cppu::UnoType<presentation::XPresentationPage>::get() ) + { + SdPage * p = dynamic_cast<SdPage *>(SvxDrawPage::mpPage); + if( p == nullptr || p->GetPageKind() != PageKind::Handout ) + { + return Any( Reference< presentation::XPresentationPage >( this ) ); + } + } + + return SdGenericDrawPage::queryInterface( rType ); +} + +void SAL_CALL SdDrawPage::acquire() noexcept +{ + SvxDrawPage::acquire(); +} + +void SAL_CALL SdDrawPage::release() noexcept +{ + SvxDrawPage::release(); +} + +UNO3_GETIMPLEMENTATION2_IMPL( SdDrawPage, SdGenericDrawPage ); + +// XTypeProvider +Sequence< uno::Type > SAL_CALL SdDrawPage::getTypes() +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + if( !maTypeSequence.hasElements() ) + { + const PageKind ePageKind = GetPage() ? GetPage()->GetPageKind() : PageKind::Standard; + bool bPresPage = IsImpressDocument() && ePageKind != PageKind::Handout; + + // Collect the types of this class. + ::std::vector<uno::Type> aTypes; + aTypes.reserve(13); + aTypes.push_back(cppu::UnoType<drawing::XDrawPage>::get()); + aTypes.push_back(cppu::UnoType<beans::XPropertySet>::get()); + aTypes.push_back(cppu::UnoType<container::XNamed>::get()); + aTypes.push_back(cppu::UnoType<drawing::XMasterPageTarget>::get()); + aTypes.push_back(cppu::UnoType<lang::XServiceInfo>::get()); + aTypes.push_back(cppu::UnoType<util::XReplaceable>::get()); + aTypes.push_back(cppu::UnoType<document::XLinkTargetSupplier>::get()); + aTypes.push_back(cppu::UnoType<drawing::XShapeCombiner>::get()); + aTypes.push_back(cppu::UnoType<drawing::XShapeBinder>::get()); + aTypes.push_back(cppu::UnoType<office::XAnnotationAccess>::get()); + aTypes.push_back(cppu::UnoType<beans::XMultiPropertySet>::get()); + if( bPresPage ) + aTypes.push_back(cppu::UnoType<presentation::XPresentationPage>::get()); + if( bPresPage && ePageKind == PageKind::Standard ) + aTypes.push_back(cppu::UnoType<XAnimationNodeSupplier>::get()); + + // Get types of base class. + // Join those types in a sequence. + return comphelper::concatSequences( + comphelper::containerToSequence(aTypes), + SdGenericDrawPage::getTypes() ); + } + + return maTypeSequence; +} + +Sequence< sal_Int8 > SAL_CALL SdDrawPage::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +OUString SdDrawPage::getPageApiName( SdPage const * pPage ) +{ + return ::getPageApiName( pPage ); +} + +OUString getPageApiName( SdPage const * pPage ) +{ + OUString aPageName; + + if(pPage) + { + aPageName = pPage->GetRealName(); + + if( aPageName.isEmpty() ) + { + const sal_Int32 nPageNum = ( ( pPage->GetPageNum() - 1 ) >> 1 ) + 1; + aPageName = sEmptyPageName + OUString::number( nPageNum ); + } + } + + return aPageName; +} + +OUString getPageApiNameFromUiName( const OUString& rUIName ) +{ + OUString aApiName; + + OUString aDefPageName(SdResId(STR_PAGE) + " "); + + if( rUIName.startsWith( aDefPageName ) ) + { + aApiName = OUString::Concat(sEmptyPageName) + rUIName.subView( aDefPageName.getLength() ); + } + else + { + aApiName = rUIName; + } + + return aApiName; +} + +OUString SdDrawPage::getPageApiNameFromUiName( const OUString& rUIName ) +{ + return ::getPageApiNameFromUiName( rUIName ); +} + +OUString getUiNameFromPageApiNameImpl( const OUString& rApiName ) +{ + const OUString aDefPageName( sEmptyPageName ); + if( rApiName.startsWith( aDefPageName ) ) + { + std::u16string_view aNumber( rApiName.subView( aDefPageName.getLength() ) ); + + // create the page number + sal_Int32 nPageNumber = o3tl::toInt32(aNumber); + + // check if there are non number characters in the number part + const size_t nChars = aNumber.size(); + const sal_Unicode* pString = aNumber.data(); + for( size_t nChar = 0; nChar < nChars; nChar++, pString++ ) + { + if((*pString < '0') || (*pString > '9')) + { + // found a non number character, so this is not the default + // name for this page + nPageNumber = -1; + break; + } + } + + if( nPageNumber != -1) + { + return SdResId(STR_PAGE) + " " + aNumber; + } + } + + return rApiName; +} + +OUString SdDrawPage::getUiNameFromPageApiName( const OUString& rApiName ) +{ + return getUiNameFromPageApiNameImpl( rApiName ); +} + +// XServiceInfo +OUString SAL_CALL SdDrawPage::getImplementationName() +{ + return "SdDrawPage"; +} + +Sequence< OUString > SAL_CALL SdDrawPage::getSupportedServiceNames() +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + std::vector<std::u16string_view> aAdd{ u"com.sun.star.drawing.DrawPage" }; + + if( IsImpressDocument() ) + aAdd.emplace_back(u"com.sun.star.presentation.DrawPage"); + + return comphelper::concatSequences(SdGenericDrawPage::getSupportedServiceNames(), aAdd); +} + +sal_Bool SAL_CALL SdDrawPage::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +// XNamed +void SAL_CALL SdDrawPage::setName( const OUString& rName ) +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + DBG_ASSERT( GetPage() && !GetPage()->IsMasterPage(), "Don't call base implementation for masterpages!" ); + + OUString aName( rName ); + + if(!(GetPage() && GetPage()->GetPageKind() != PageKind::Notes)) + return; + + // check if this is the default 'page1234' name + OUString aNumber; + if(aName.startsWith( sEmptyPageName, &aNumber )) + { + // ok, it maybe is, aNumber is the number part after 'page' + + // create the page number + sal_Int32 nPageNumber = aNumber.toInt32(); + + // check if there are non number characters in the number part + const sal_Int32 nChars = aNumber.getLength(); + const sal_Unicode* pString = aNumber.getStr(); + sal_Int32 nChar; + for( nChar = 0; nChar < nChars; nChar++, pString++ ) + { + if((*pString < '0') || (*pString > '9')) + { + // found a non number character, so this is not the default + // name for this page + nPageNumber = -1; + break; + } + } + + if( nPageNumber == ( ( GetPage()->GetPageNum() - 1 ) >> 1 ) + 1 ) + aName.clear(); + } + else + { + OUString aDefaultPageName( SdResId(STR_PAGE) + " " ); + if( aName.startsWith( aDefaultPageName ) ) + aName.clear(); + } + + GetPage()->SetName( aName ); + + sal_uInt16 nNotesPageNum = (GetPage()->GetPageNum()-1)>>1; + if( GetModel()->GetDoc()->GetSdPageCount( PageKind::Notes ) > nNotesPageNum ) + { + SdPage* pNotesPage = GetModel()->GetDoc()->GetSdPage( nNotesPageNum, PageKind::Notes ); + if( pNotesPage ) + pNotesPage->SetName(aName); + } + + // fake a mode change to repaint the page tab bar + ::sd::DrawDocShell* pDocSh = GetModel()->GetDocShell(); + ::sd::ViewShell* pViewSh = pDocSh ? pDocSh->GetViewShell() : nullptr; + if( auto pDrawViewSh = dynamic_cast<::sd::DrawViewShell* >(pViewSh) ) + { + EditMode eMode = pDrawViewSh->GetEditMode(); + if( eMode == EditMode::Page ) + { + bool bLayer = pDrawViewSh->IsLayerModeActive(); + + pDrawViewSh->ChangeEditMode( eMode, !bLayer ); + pDrawViewSh->ChangeEditMode( eMode, bLayer ); + } + } + + GetModel()->SetModified(); +} + +OUString SAL_CALL SdDrawPage::getName() +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + return getPageApiName( GetPage() ); +} + +// XMasterPageTarget +Reference< drawing::XDrawPage > SAL_CALL SdDrawPage::getMasterPage( ) +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + if(GetPage()) + { + Reference< drawing::XDrawPage > xPage; + + if(SvxFmDrawPage::mpPage->TRG_HasMasterPage()) + { + SdrPage& rMasterPage = SvxFmDrawPage::mpPage->TRG_GetMasterPage(); + xPage.set( rMasterPage.getUnoPage(), uno::UNO_QUERY ); + } + + return xPage; + } + return nullptr; +} + +void SAL_CALL SdDrawPage::setMasterPage( const Reference< drawing::XDrawPage >& xMasterPage ) +{ + ::SolarMutexGuard aGuard; + comphelper::ProfileZone aZone("setMasterPage"); + + throwIfDisposed(); + + if(!SvxFmDrawPage::mpPage) + return; + + SdMasterPage* pMasterPage = comphelper::getFromUnoTunnel<SdMasterPage>( xMasterPage ); + if( !(pMasterPage && pMasterPage->isValid()) ) + return; + + SvxFmDrawPage::mpPage->TRG_ClearMasterPage(); + + SdPage* pSdPage = static_cast<SdPage*>(pMasterPage->GetSdrPage()); + SvxFmDrawPage::mpPage->TRG_SetMasterPage(*pSdPage); + + SvxFmDrawPage::mpPage->SetBorder(pSdPage->GetLeftBorder(),pSdPage->GetUpperBorder(), + pSdPage->GetRightBorder(),pSdPage->GetLowerBorder() ); + + SvxFmDrawPage::mpPage->SetSize( pSdPage->GetSize() ); + SvxFmDrawPage::mpPage->SetOrientation( pSdPage->GetOrientation() ); + static_cast<SdPage*>(SvxFmDrawPage::mpPage)->SetLayoutName( pSdPage->GetLayoutName() ); + + // set notes master also + SdPage* pNotesPage = GetModel()->GetDoc()->GetSdPage( (SvxFmDrawPage::mpPage->GetPageNum()-1)>>1, PageKind::Notes ); + + pNotesPage->TRG_ClearMasterPage(); + sal_uInt16 nNum = SvxFmDrawPage::mpPage->TRG_GetMasterPage().GetPageNum() + 1; + pNotesPage->TRG_SetMasterPage(*SvxFmDrawPage::mpPage->getSdrModelFromSdrPage().GetMasterPage(nNum)); + pNotesPage->SetLayoutName( pSdPage->GetLayoutName() ); + + GetModel()->SetModified(); +} + +// XPresentationPage +Reference< drawing::XDrawPage > SAL_CALL SdDrawPage::getNotesPage() +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + if(SvxFmDrawPage::mpPage && GetModel()->GetDoc() && SvxFmDrawPage::mpPage->GetPageNum() ) + { + SdPage* pNotesPage = GetModel()->GetDoc()->GetSdPage( (SvxFmDrawPage::mpPage->GetPageNum()-1)>>1, PageKind::Notes ); + if( pNotesPage ) + { + Reference< drawing::XDrawPage > xPage( pNotesPage->getUnoPage(), uno::UNO_QUERY ); + return xPage; + } + } + return nullptr; +} + +// XIndexAccess +sal_Int32 SAL_CALL SdDrawPage::getCount() +{ + return SdGenericDrawPage::getCount(); +} + +Any SAL_CALL SdDrawPage::getByIndex( sal_Int32 Index ) +{ + return SdGenericDrawPage::getByIndex( Index ); +} + +// XElementAccess +uno::Type SAL_CALL SdDrawPage::getElementType() +{ + return SdGenericDrawPage::getElementType(); +} + +sal_Bool SAL_CALL SdDrawPage::hasElements() +{ + return SdGenericDrawPage::hasElements(); +} + +// XShapes +void SAL_CALL SdDrawPage::add( const Reference< drawing::XShape >& xShape ) +{ + SdGenericDrawPage::add( xShape ); +} + +void SAL_CALL SdDrawPage::remove( const Reference< drawing::XShape >& xShape ) +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + SdrObject* pObj = SdrObject::getSdrObjectFromXShape( xShape ); + if( pObj ) + { + GetPage()->RemovePresObj(pObj); + pObj->SetUserCall(nullptr); + } + + SdGenericDrawPage::remove( xShape ); +} + +void SdDrawPage::setBackground( const Any& rValue ) +{ + Reference< beans::XPropertySet > xSet; + + if( !(rValue >>= xSet) && !rValue.hasValue() ) + throw lang::IllegalArgumentException(); + + if( !xSet.is() ) + { + // the easy case, no background set. Set drawing::FillStyle_NONE to represent this + GetPage()->getSdrPageProperties().PutItem(XFillStyleItem(drawing::FillStyle_NONE)); + return; + } + + // is it our own implementation? + SdUnoPageBackground* pBack = comphelper::getFromUnoTunnel<SdUnoPageBackground>( xSet ); + + SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST> aSet( GetModel()->GetDoc()->GetPool() ); + + if( pBack ) + { + pBack->fillItemSet( static_cast<SdDrawDocument*>(&GetPage()->getSdrModelFromSdrPage()), aSet ); + } + else + { + rtl::Reference<SdUnoPageBackground> pBackground = new SdUnoPageBackground(); + + Reference< beans::XPropertySetInfo > xSetInfo( xSet->getPropertySetInfo() ); + Reference< beans::XPropertySetInfo > xDestSetInfo( pBackground->getPropertySetInfo() ); + + const Sequence< beans::Property > aProperties( xDestSetInfo->getProperties() ); + + for( const beans::Property& rProp : aProperties ) + { + const OUString aPropName( rProp.Name ); + if( xSetInfo->hasPropertyByName( aPropName ) ) + pBackground->setPropertyValue( aPropName, + xSet->getPropertyValue( aPropName ) ); + } + + pBackground->fillItemSet( static_cast<SdDrawDocument*>(&GetPage()->getSdrModelFromSdrPage()), aSet ); + } + + if( aSet.Count() == 0 ) + { + // no background fill, represent by setting drawing::FillStyle_NONE + GetPage()->getSdrPageProperties().PutItem(XFillStyleItem(drawing::FillStyle_NONE)); + } + else + { + // background fill, set at page (not sure if ClearItem is needed) + GetPage()->getSdrPageProperties().ClearItem(); + GetPage()->getSdrPageProperties().PutItemSet(aSet); + } + + // repaint only + SvxFmDrawPage::mpPage->ActionChanged(); +} + +// XAnnotationAccess: +Reference< XAnnotation > SAL_CALL SdGenericDrawPage::createAndInsertAnnotation() +{ + if( !GetPage() ) + throw DisposedException(); + + Reference< XAnnotation > xRet; + GetPage()->createAnnotation(xRet); + return xRet; +} + +void SAL_CALL SdGenericDrawPage::removeAnnotation(const Reference< XAnnotation > & annotation) +{ + GetPage()->removeAnnotation(annotation); +} + +Reference< XAnnotationEnumeration > SAL_CALL SdGenericDrawPage::createAnnotationEnumeration() +{ + return ::sd::createAnnotationEnumeration( std::vector(GetPage()->getAnnotations()) ); +} + +void SdDrawPage::getBackground(Any& rValue) +{ + const SfxItemSet& rFillAttributes = GetPage()->getSdrPageProperties().GetItemSet(); + + if(drawing::FillStyle_NONE == rFillAttributes.Get(XATTR_FILLSTYLE).GetValue()) + { + // no fill set (switched off by drawing::FillStyle_NONE), clear rValue to represent this + rValue.clear(); + } + else + { + // there is a fill set, export to rValue + Reference< beans::XPropertySet > xSet(new SdUnoPageBackground( + GetModel()->GetDoc(), + &GetPage()->getSdrPageProperties().GetItemSet())); + rValue <<= xSet; + } +} + +void SdGenericDrawPage::setNavigationOrder( const Any& rValue ) +{ + Reference< XIndexAccess > xIA( rValue, UNO_QUERY ); + if( xIA.is() ) + { + if( dynamic_cast< SdDrawPage* >( xIA.get() ) == this ) + { + if( GetPage()->HasObjectNavigationOrder() ) + GetPage()->ClearObjectNavigationOrder(); + + return; + } + else if( static_cast<size_t>(xIA->getCount()) == GetPage()->GetObjCount() ) + { + GetPage()->SetNavigationOrder(xIA); + return; + } + } + throw IllegalArgumentException(); +} + +namespace { + +class SdNavigationOrderAccess : public ::cppu::WeakImplHelper< XIndexAccess > +{ +public: + explicit SdNavigationOrderAccess(SdrPage const * pPage); + + // XIndexAccess + virtual sal_Int32 SAL_CALL getCount( ) override; + virtual Any SAL_CALL getByIndex( sal_Int32 Index ) override; + + // XElementAccess + virtual Type SAL_CALL getElementType( ) override; + virtual sal_Bool SAL_CALL hasElements( ) override; + +private: + std::vector< Reference< XShape > > maShapes; +}; + +} + +SdNavigationOrderAccess::SdNavigationOrderAccess( SdrPage const * pPage ) +: maShapes( pPage ? pPage->GetObjCount() : 0 ) +{ + if( pPage ) + { + const size_t nCount = pPage->GetObjCount(); + for( size_t nIndex = 0; nIndex < nCount; ++nIndex ) + { + SdrObject* pObj = pPage->GetObj( nIndex ); + sal_uInt32 nNavPos = pObj->GetNavigationPosition(); + DBG_ASSERT( !maShapes[nNavPos].is(), "sd::SdNavigationOrderAccess::SdNavigationOrderAccess(), duplicate navigation positions from core!" ); + maShapes[nNavPos].set( pObj->getUnoShape(), UNO_QUERY ); + } + } +} + +// XIndexAccess +sal_Int32 SAL_CALL SdNavigationOrderAccess::getCount( ) +{ + return static_cast< sal_Int32 >( maShapes.size() ); +} + +Any SAL_CALL SdNavigationOrderAccess::getByIndex( sal_Int32 Index ) +{ + if( (Index < 0) || (Index > getCount()) ) + throw IndexOutOfBoundsException(); + + return Any( maShapes[Index] ); +} + +// XElementAccess +Type SAL_CALL SdNavigationOrderAccess::getElementType( ) +{ + return cppu::UnoType<XShape>::get(); +} + +sal_Bool SAL_CALL SdNavigationOrderAccess::hasElements( ) +{ + return !maShapes.empty(); +} + +Any SdGenericDrawPage::getNavigationOrder() +{ + if( GetPage()->HasObjectNavigationOrder() ) + { + return Any( Reference< XIndexAccess >( new SdNavigationOrderAccess( GetPage() ) ) ); + } + else + { + return Any( Reference< XIndexAccess >( this ) ); + } +} + +SdMasterPage::SdMasterPage(SdXImpressDocument* pModel, SdPage* pPage) + : SdGenericDrawPage(pModel, pPage, ImplGetMasterPagePropertySet(pPage->GetPageKind())) +{ +} + +SdMasterPage::~SdMasterPage() noexcept +{ +} + +// XInterface +Any SAL_CALL SdMasterPage::queryInterface( const uno::Type & rType ) +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + uno::Any aAny; + + if( rType == cppu::UnoType<container::XIndexAccess>::get() ) + aAny <<= Reference< container::XIndexAccess >(static_cast<presentation::XPresentationPage*>(this)); + else if( rType == cppu::UnoType<container::XElementAccess>::get() ) + aAny <<= Reference< container::XElementAccess >(static_cast<presentation::XPresentationPage*>(this)); + else if( rType == cppu::UnoType<container::XNamed>::get() ) + aAny <<= Reference< container::XNamed >(this); + else if( rType == cppu::UnoType<presentation::XPresentationPage>::get() && + ( IsImpressDocument() && + GetPage() && GetPage()->GetPageKind() != PageKind::Handout) ) + aAny <<= Reference< presentation::XPresentationPage >( this ); + else + return SdGenericDrawPage::queryInterface( rType ); + + return aAny; +} + +void SAL_CALL SdMasterPage::acquire() noexcept +{ + SvxDrawPage::acquire(); +} + +void SAL_CALL SdMasterPage::release() noexcept +{ + SvxDrawPage::release(); +} + +UNO3_GETIMPLEMENTATION2_IMPL( SdMasterPage, SdGenericDrawPage ); + +// XTypeProvider +Sequence< uno::Type > SAL_CALL SdMasterPage::getTypes() +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + if( !maTypeSequence.hasElements() ) + { + const PageKind ePageKind = GetPage() ? GetPage()->GetPageKind() : PageKind::Standard; + bool bPresPage = IsImpressDocument() && SvxFmDrawPage::mpPage && ePageKind != PageKind::Handout; + + // Collect the types of this class. + ::std::vector<uno::Type> aTypes; + aTypes.reserve(12); + aTypes.push_back(cppu::UnoType<drawing::XDrawPage>::get()); + aTypes.push_back(cppu::UnoType<beans::XPropertySet>::get()); + aTypes.push_back(cppu::UnoType<container::XNamed>::get()); + aTypes.push_back(cppu::UnoType<lang::XServiceInfo>::get()); + aTypes.push_back(cppu::UnoType<util::XReplaceable>::get()); + aTypes.push_back(cppu::UnoType<document::XLinkTargetSupplier>::get()); + aTypes.push_back(cppu::UnoType<drawing::XShapeCombiner>::get()); + aTypes.push_back(cppu::UnoType<drawing::XShapeBinder>::get()); + aTypes.push_back(cppu::UnoType<office::XAnnotationAccess>::get()); + aTypes.push_back(cppu::UnoType<beans::XMultiPropertySet>::get()); + if( bPresPage ) + aTypes.push_back(cppu::UnoType<presentation::XPresentationPage>::get()); + if( bPresPage && ePageKind == PageKind::Standard ) + aTypes.push_back(cppu::UnoType<XAnimationNodeSupplier>::get()); + + // Get types of base class. + // Join those types in a sequence. + return comphelper::concatSequences( + comphelper::containerToSequence(aTypes), + SdGenericDrawPage::getTypes() ); + } + + return maTypeSequence; +} + +Sequence< sal_Int8 > SAL_CALL SdMasterPage::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// XServiceInfo +OUString SAL_CALL SdMasterPage::getImplementationName() +{ + return "SdMasterPage"; +} + +Sequence< OUString > SAL_CALL SdMasterPage::getSupportedServiceNames() +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + std::vector<std::u16string_view> aAdd{ u"com.sun.star.drawing.MasterPage" }; + + if( SvxFmDrawPage::mpPage && static_cast<SdPage*>(SvxFmDrawPage::mpPage)->GetPageKind() == PageKind::Handout ) + aAdd.emplace_back(u"com.sun.star.presentation.HandoutMasterPage"); + + return comphelper::concatSequences(SdGenericDrawPage::getSupportedServiceNames(), aAdd); +} + +sal_Bool SAL_CALL SdMasterPage::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +// XElementAccess +sal_Bool SAL_CALL SdMasterPage::hasElements() +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + if( SvxFmDrawPage::mpPage == nullptr ) + return false; + + return SvxFmDrawPage::mpPage->GetObjCount() > 0; +} + +uno::Type SAL_CALL SdMasterPage::getElementType() +{ + return SdGenericDrawPage::getElementType(); +} + +// XIndexAccess +sal_Int32 SAL_CALL SdMasterPage::getCount() +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + return SdGenericDrawPage::getCount(); +} + +Any SAL_CALL SdMasterPage::getByIndex( sal_Int32 Index ) +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + return SdGenericDrawPage::getByIndex(Index); +} + +// intern +void SdMasterPage::setBackground( const Any& rValue ) +{ + // we need at least a beans::XPropertySet + Reference< beans::XPropertySet > xInputSet( rValue, UNO_QUERY ); + if( !xInputSet.is() ) + throw lang::IllegalArgumentException(); + + try + { + if( GetModel() && IsImpressDocument() ) + { + Reference< container::XNameAccess > xFamilies( GetModel()->getStyleFamilies(), UNO_SET_THROW ); + Reference< container::XNameAccess > xFamily( xFamilies->getByName( getName() ), UNO_QUERY_THROW ) ; + + Reference< beans::XPropertySet > xStyleSet( xFamily->getByName( sUNO_PseudoSheet_Background ), UNO_QUERY_THROW ); + + Reference< beans::XPropertySetInfo > xSetInfo( xInputSet->getPropertySetInfo(), UNO_SET_THROW ); + Reference< beans::XPropertyState > xSetStates( xInputSet, UNO_QUERY ); + + for( const auto pProp : ImplGetPageBackgroundPropertySet()->getPropertyMap().getPropertyEntries() ) + { + const OUString& rPropName = pProp->aName; + if( xSetInfo->hasPropertyByName( rPropName ) ) + { + if( !xSetStates.is() || xSetStates->getPropertyState( rPropName ) == beans::PropertyState_DIRECT_VALUE ) + xStyleSet->setPropertyValue( rPropName, xInputSet->getPropertyValue( rPropName ) ); + else + xSetStates->setPropertyToDefault( rPropName ); + } + } + } + else + { + // first fill an item set + // is it our own implementation? + SdUnoPageBackground* pBack = comphelper::getFromUnoTunnel<SdUnoPageBackground>( xInputSet ); + + SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST> aSet( GetModel()->GetDoc()->GetPool() ); + + if( pBack ) + { + pBack->fillItemSet( static_cast<SdDrawDocument*>(&GetPage()->getSdrModelFromSdrPage()), aSet ); + } + else + { + rtl::Reference<SdUnoPageBackground> pBackground = new SdUnoPageBackground(); + + Reference< beans::XPropertySetInfo > xInputSetInfo( xInputSet->getPropertySetInfo(), UNO_SET_THROW ); + Reference< beans::XPropertySetInfo > xDestSetInfo( pBackground->getPropertySetInfo(), UNO_SET_THROW ); + + const uno::Sequence< beans::Property> aProperties( xDestSetInfo->getProperties() ); + + for( const beans::Property& rProp : aProperties ) + { + const OUString aPropName( rProp.Name ); + if( xInputSetInfo->hasPropertyByName( aPropName ) ) + pBackground->setPropertyValue( aPropName, xInputSet->getPropertyValue( aPropName ) ); + } + + pBackground->fillItemSet( static_cast<SdDrawDocument*>(&SvxFmDrawPage::mpPage->getSdrModelFromSdrPage()), aSet ); + } + + // if we find the background style, copy the set to the background + SdDrawDocument* pDoc = static_cast<SdDrawDocument*>(&SvxFmDrawPage::mpPage->getSdrModelFromSdrPage()); + SfxStyleSheetBasePool* pSSPool = pDoc->GetStyleSheetPool(); + if(pSSPool) + { + OUString aLayoutName( static_cast< SdPage* >( SvxFmDrawPage::mpPage )->GetLayoutName() ); + aLayoutName = OUString::Concat(aLayoutName.subView(0, aLayoutName.indexOf(SD_LT_SEPARATOR)+4)) + + STR_LAYOUT_BACKGROUND; + SfxStyleSheetBase* pStyleSheet = pSSPool->Find( aLayoutName, SfxStyleFamily::Page ); + + if( pStyleSheet ) + { + pStyleSheet->GetItemSet().Put( aSet ); + + // repaint only + SvxFmDrawPage::mpPage->ActionChanged(); + return; + } + } + + // if no background style is available, set at page directly. This + // is an error and should NOT happen (and will be asserted from the SdrPage) + GetPage()->getSdrPageProperties().PutItemSet(aSet); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SdMasterPage::setBackground()"); + } +} + +void SdMasterPage::getBackground( Any& rValue ) +{ + if( !GetModel() ) + return; + + try + { + if( IsImpressDocument() ) + { + Reference< container::XNameAccess > xFamilies( GetModel()->getStyleFamilies(), UNO_SET_THROW ); + Reference< container::XNameAccess > xFamily( xFamilies->getByName( getName() ), UNO_QUERY_THROW ); + + rValue <<= Reference< beans::XPropertySet >( xFamily->getByName( sUNO_PseudoSheet_Background ), UNO_QUERY_THROW ); + } + else + { + SdDrawDocument* pDoc = static_cast<SdDrawDocument*>(&SvxFmDrawPage::mpPage->getSdrModelFromSdrPage()); + SfxStyleSheetBasePool* pSSPool = pDoc->GetStyleSheetPool(); + if(pSSPool) + { + OUString aLayoutName( static_cast< SdPage* >(SvxFmDrawPage::mpPage)->GetLayoutName() ); + aLayoutName = OUString::Concat(aLayoutName.subView(0, aLayoutName.indexOf(SD_LT_SEPARATOR)+4)) + + STR_LAYOUT_BACKGROUND; + SfxStyleSheetBase* pStyleSheet = pSSPool->Find( aLayoutName, SfxStyleFamily::Page ); + + if( pStyleSheet ) + { + SfxItemSet aStyleSet( pStyleSheet->GetItemSet()); + if( aStyleSet.Count() ) + { + rValue <<= Reference< beans::XPropertySet >( new SdUnoPageBackground( pDoc, &aStyleSet ) ); + return; + } + } + } + + // No style found, use fill attributes from page background. This + // should NOT happen and is an error + const SfxItemSet& rFallbackItemSet(SvxFmDrawPage::mpPage->getSdrPageProperties().GetItemSet()); + + if(drawing::FillStyle_NONE == rFallbackItemSet.Get(XATTR_FILLSTYLE).GetValue()) + { + rValue <<= Reference< beans::XPropertySet >( + new SdUnoPageBackground(GetModel()->GetDoc(), &rFallbackItemSet)); + } + else + { + rValue.clear(); + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SdMasterPage::getBackground()"); + rValue.clear(); + } +} + +// XNamed +void SAL_CALL SdMasterPage::setName( const OUString& rName ) +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + if(!(SvxFmDrawPage::mpPage && GetPage()->GetPageKind() != PageKind::Notes)) + return; + + SdDrawDocument* pDoc = GetModel()->GetDoc(); + bool bOutDummy; + + // Slide Name has to be unique + if( pDoc && pDoc->GetPageByName( rName, bOutDummy ) != SDRPAGE_NOTFOUND ) + return; // throw Exception ? + + GetPage()->SetName( rName ); + + if( pDoc ) + pDoc->RenameLayoutTemplate( GetPage()->GetLayoutName(), rName ); + + // fake a mode change to repaint the page tab bar + ::sd::DrawDocShell* pDocSh = GetModel()->GetDocShell(); + ::sd::ViewShell* pViewSh = pDocSh ? pDocSh->GetViewShell() : nullptr; + if( auto pDrawViewSh = dynamic_cast< ::sd::DrawViewShell* >(pViewSh) ) + { + EditMode eMode = pDrawViewSh->GetEditMode(); + if( eMode == EditMode::MasterPage ) + { + bool bLayer = pDrawViewSh->IsLayerModeActive(); + + pDrawViewSh->ChangeEditMode( eMode, !bLayer ); + pDrawViewSh->ChangeEditMode( eMode, bLayer ); + } + } + + GetModel()->SetModified(); +} + +OUString SAL_CALL SdMasterPage::getName( ) +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + if(SvxFmDrawPage::mpPage) + { + OUString aLayoutName( GetPage()->GetLayoutName() ); + return aLayoutName.copy(0, aLayoutName.indexOf(SD_LT_SEPARATOR)); + } + + return OUString(); +} + +// XPresentationPage +Reference< drawing::XDrawPage > SAL_CALL SdMasterPage::getNotesPage() +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + if(SvxFmDrawPage::mpPage && GetModel()->GetDoc() ) + { + SdPage* pNotesPage = GetModel()->GetDoc()->GetMasterSdPage( (SvxFmDrawPage::mpPage->GetPageNum()-1)>>1, PageKind::Notes ); + if( pNotesPage ) + { + Reference< drawing::XDrawPage > xPage( pNotesPage->getUnoPage(), uno::UNO_QUERY ); + return xPage; + } + } + return nullptr; +} + +// XShapes +void SAL_CALL SdMasterPage::add( const Reference< drawing::XShape >& xShape ) +{ + SdGenericDrawPage::add( xShape ); +} + +void SAL_CALL SdMasterPage::remove( const Reference< drawing::XShape >& xShape ) +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + SdrObject* pObj = SdrObject::getSdrObjectFromXShape( xShape ); + if( pObj && GetPage()->IsPresObj( pObj ) ) + GetPage()->RemovePresObj(pObj); + + SdGenericDrawPage::remove( xShape ); +} + +Reference< uno::XInterface > createUnoPageImpl( SdPage* pPage ) +{ + Reference< uno::XInterface > xPage; + + if( pPage ) + { + SdXImpressDocument* pModel = comphelper::getFromUnoTunnel<SdXImpressDocument>( pPage->getSdrModelFromSdrPage().getUnoModel() ); + if( pModel ) + { + if( pPage->IsMasterPage() ) + { + xPage = static_cast<cppu::OWeakObject*>(new SdMasterPage( pModel, pPage )); + } + else + { + xPage = static_cast<cppu::OWeakObject*>(new SdDrawPage( pModel, pPage )); + } + } + } + + return xPage; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unopback.cxx b/sd/source/ui/unoidl/unopback.cxx new file mode 100644 index 000000000..508b1f866 --- /dev/null +++ b/sd/source/ui/unoidl/unopback.cxx @@ -0,0 +1,410 @@ +/* -*- 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 <com/sun/star/drawing/BitmapMode.hpp> +#include <vcl/svapp.hxx> +#include <svl/itemset.hxx> +#include <svx/svdpool.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <svx/xflbstit.hxx> +#include <svx/xflbmtit.hxx> +#include <svx/svdobj.hxx> +#include <svx/unoshape.hxx> +#include <svx/unoshprp.hxx> + +#include "unopback.hxx" +#include <drawdoc.hxx> +#include <unokywds.hxx> + +using namespace ::com::sun::star; + +const SvxItemPropertySet* ImplGetPageBackgroundPropertySet() +{ + static const SfxItemPropertyMapEntry aPageBackgroundPropertyMap_Impl[] = + { + FILL_PROPERTIES + { u"", 0, css::uno::Type(), 0, 0 } + }; + + static SvxItemPropertySet aPageBackgroundPropertySet_Impl( aPageBackgroundPropertyMap_Impl, SdrObject::GetGlobalDrawObjectItemPool() ); + return &aPageBackgroundPropertySet_Impl; +} + +UNO3_GETIMPLEMENTATION_IMPL( SdUnoPageBackground ); + +SdUnoPageBackground::SdUnoPageBackground( + SdDrawDocument* pDoc /* = NULL */, + const SfxItemSet* pSet /* = NULL */) +: mpPropSet(ImplGetPageBackgroundPropertySet()), + mpDoc(pDoc) +{ + if( pDoc ) + { + StartListening( *pDoc ); + mpSet = std::make_unique<SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST>>( pDoc->GetPool() ); + + if( pSet ) + mpSet->Put(*pSet); + } +} + +SdUnoPageBackground::~SdUnoPageBackground() noexcept +{ + SolarMutexGuard g; + + if( mpDoc ) + EndListening( *mpDoc ); +} + +void SdUnoPageBackground::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint) + return; + const SdrHint* pSdrHint = static_cast<const SdrHint*>( &rHint ); + + // delete item set if document is dying because then the pool + // will also die + if( pSdrHint->GetKind() == SdrHintKind::ModelCleared ) + { + mpSet.reset(); + mpDoc = nullptr; + } +} + +void SdUnoPageBackground::fillItemSet( SdDrawDocument* pDoc, SfxItemSet& rSet ) +{ + rSet.ClearItem(); + + if( mpSet == nullptr ) + { + StartListening( *pDoc ); + mpDoc = pDoc; + + mpSet = std::make_unique<SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST>>( *rSet.GetPool() ); + + if( maUsrAnys.AreThereOwnUsrAnys() ) + { + for( const auto pProp : mpPropSet->getPropertyMap().getPropertyEntries() ) + { + uno::Any* pAny = maUsrAnys.GetUsrAnyForID( *pProp ); + if( pAny ) + { + const OUString & aPropertyName = pProp->aName; + switch( pProp->nWID ) + { + case XATTR_FILLFLOATTRANSPARENCE : + case XATTR_FILLGRADIENT : + { + if ( ( pAny->getValueType() == ::cppu::UnoType< css::awt::Gradient>::get() ) + && ( pProp->nMemberId == MID_FILLGRADIENT ) ) + { + setPropertyValue( aPropertyName, *pAny ); + } + else if ( ( pAny->getValueType() == ::cppu::UnoType<OUString>::get() ) && + ( pProp->nMemberId == MID_NAME ) ) + { + setPropertyValue( aPropertyName, *pAny ); + } + } + break; + case XATTR_FILLHATCH : + { + if ( ( pAny->getValueType() == ::cppu::UnoType< css::drawing::Hatch>::get() ) + && ( pProp->nMemberId == MID_FILLHATCH ) ) + { + setPropertyValue( aPropertyName, *pAny ); + } + else if ( ( pAny->getValueType() == ::cppu::UnoType<OUString>::get() ) && + ( pProp->nMemberId == MID_NAME ) ) + { + setPropertyValue( aPropertyName, *pAny ); + } + } + break; + case XATTR_FILLBITMAP : + { + if (pProp->nMemberId == MID_BITMAP && + (pAny->getValueType() == cppu::UnoType<css::awt::XBitmap>::get() || + pAny->getValueType() == cppu::UnoType<css::graphic::XGraphic>::get())) + { + setPropertyValue( aPropertyName, *pAny ); + } + else if (pAny->getValueType() == ::cppu::UnoType<OUString>::get() && pProp->nMemberId == MID_NAME) + { + setPropertyValue( aPropertyName, *pAny ); + } + } + break; + + default: + setPropertyValue( aPropertyName, *pAny ); + } + } + } + } + } + + rSet.Put( *mpSet ); +} + +// XServiceInfo +OUString SAL_CALL SdUnoPageBackground::getImplementationName() +{ + return "SdUnoPageBackground"; +} + +sal_Bool SAL_CALL SdUnoPageBackground::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +uno::Sequence< OUString > SAL_CALL SdUnoPageBackground::getSupportedServiceNames() +{ + return { sUNO_Service_PageBackground, sUNO_Service_FillProperties }; +} + +// XPropertySet +uno::Reference< beans::XPropertySetInfo > SAL_CALL SdUnoPageBackground::getPropertySetInfo() +{ + return mpPropSet->getPropertySetInfo(); +} + +void SAL_CALL SdUnoPageBackground::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + + const SfxItemPropertyMapEntry* pEntry = getPropertyMapEntry( aPropertyName ); + + if( pEntry == nullptr ) + { + throw beans::UnknownPropertyException( aPropertyName, static_cast<cppu::OWeakObject*>(this)); + } + + if( mpSet ) + { + if( pEntry->nWID == OWN_ATTR_FILLBMP_MODE ) + { + drawing::BitmapMode eMode; + if( aValue >>= eMode ) + { + mpSet->Put( XFillBmpStretchItem( eMode == drawing::BitmapMode_STRETCH ) ); + mpSet->Put( XFillBmpTileItem( eMode == drawing::BitmapMode_REPEAT ) ); + return; + } + throw lang::IllegalArgumentException(); + } + + SfxItemPool& rPool = *mpSet->GetPool(); + SfxItemSet aSet( rPool, pEntry->nWID, pEntry->nWID); + aSet.Put( *mpSet ); + + if( !aSet.Count() ) + aSet.Put( rPool.GetDefaultItem( pEntry->nWID ) ); + + if( pEntry->nMemberId == MID_NAME && ( pEntry->nWID == XATTR_FILLBITMAP || pEntry->nWID == XATTR_FILLGRADIENT || pEntry->nWID == XATTR_FILLHATCH || pEntry->nWID == XATTR_FILLFLOATTRANSPARENCE ) ) + { + OUString aName; + if(!(aValue >>= aName )) + throw lang::IllegalArgumentException(); + + SvxShape::SetFillAttribute( pEntry->nWID, aName, aSet ); + } + else + { + SvxItemPropertySet_setPropertyValue( pEntry, aValue, aSet ); + } + + mpSet->Put( aSet ); + } + else + { + if(pEntry->nWID) + SvxItemPropertySet::setPropertyValue( pEntry, aValue, maUsrAnys ); + } +} + +uno::Any SAL_CALL SdUnoPageBackground::getPropertyValue( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + uno::Any aAny; + const SfxItemPropertyMapEntry* pEntry = getPropertyMapEntry(PropertyName); + + if( pEntry == nullptr ) + { + throw beans::UnknownPropertyException( PropertyName, static_cast<cppu::OWeakObject*>(this)); + } + + if( mpSet ) + { + if( pEntry->nWID == OWN_ATTR_FILLBMP_MODE ) + { + const XFillBmpStretchItem* pStretchItem = mpSet->GetItem<XFillBmpStretchItem>(XATTR_FILLBMP_STRETCH); + const XFillBmpTileItem* pTileItem = mpSet->GetItem<XFillBmpTileItem>(XATTR_FILLBMP_TILE); + + if( pStretchItem && pTileItem ) + { + if( pTileItem->GetValue() ) + aAny <<= drawing::BitmapMode_REPEAT; + else if( pStretchItem->GetValue() ) + aAny <<= drawing::BitmapMode_STRETCH; + else + aAny <<= drawing::BitmapMode_NO_REPEAT; + } + } + else + { + SfxItemPool& rPool = *mpSet->GetPool(); + SfxItemSet aSet( rPool, pEntry->nWID, pEntry->nWID); + aSet.Put( *mpSet ); + + if( !aSet.Count() ) + aSet.Put( rPool.GetDefaultItem( pEntry->nWID ) ); + + // get value from ItemSet + aAny = SvxItemPropertySet_getPropertyValue( pEntry, aSet ); + } + } + else + { + if(pEntry->nWID) + aAny = mpPropSet->getPropertyValue( pEntry, maUsrAnys ); + } + return aAny; +} + +void SAL_CALL SdUnoPageBackground::addPropertyChangeListener( const OUString& , const uno::Reference< beans::XPropertyChangeListener >& ) {} +void SAL_CALL SdUnoPageBackground::removePropertyChangeListener( const OUString& , const uno::Reference< beans::XPropertyChangeListener >& ) {} +void SAL_CALL SdUnoPageBackground::addVetoableChangeListener( const OUString& , const uno::Reference< beans::XVetoableChangeListener >& ) {} +void SAL_CALL SdUnoPageBackground::removeVetoableChangeListener( const OUString& , const uno::Reference< beans::XVetoableChangeListener >& ) {} + +// XPropertyState +beans::PropertyState SAL_CALL SdUnoPageBackground::getPropertyState( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + const SfxItemPropertyMapEntry* pEntry = getPropertyMapEntry(PropertyName); + + if( pEntry == nullptr ) + throw beans::UnknownPropertyException( PropertyName, static_cast<cppu::OWeakObject*>(this)); + + if( mpSet ) + { + if( pEntry->nWID == OWN_ATTR_FILLBMP_MODE ) + { + if( mpSet->GetItemState( XATTR_FILLBMP_STRETCH, false ) == SfxItemState::SET || + mpSet->GetItemState( XATTR_FILLBMP_TILE, false ) == SfxItemState::SET ) + { + return beans::PropertyState_DIRECT_VALUE; + } + else + { + return beans::PropertyState_AMBIGUOUS_VALUE; + } + } + + switch( mpSet->GetItemState( pEntry->nWID, false ) ) + { + case SfxItemState::SET: + return beans::PropertyState_DIRECT_VALUE; + case SfxItemState::DEFAULT: + return beans::PropertyState_DEFAULT_VALUE; + default: +// case SfxItemState::DONTCARE: +// case SfxItemState::DISABLED: + return beans::PropertyState_AMBIGUOUS_VALUE; + } + } + else + { + if( nullptr == maUsrAnys.GetUsrAnyForID(*pEntry) ) + return beans::PropertyState_DEFAULT_VALUE; + else + return beans::PropertyState_DIRECT_VALUE; + } +} + +uno::Sequence< beans::PropertyState > SAL_CALL SdUnoPageBackground::getPropertyStates( const uno::Sequence< OUString >& aPropertyName ) +{ + SolarMutexGuard aGuard; + + sal_Int32 nCount = aPropertyName.getLength(); + + uno::Sequence< beans::PropertyState > aPropertyStateSequence( nCount ); + + std::transform(aPropertyName.begin(), aPropertyName.end(), aPropertyStateSequence.getArray(), + [this](const OUString& rName) -> beans::PropertyState { return getPropertyState(rName); }); + + return aPropertyStateSequence; +} + +void SAL_CALL SdUnoPageBackground::setPropertyToDefault( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + const SfxItemPropertyMapEntry* pEntry = getPropertyMapEntry(PropertyName); + + if( pEntry == nullptr ) + throw beans::UnknownPropertyException( PropertyName, static_cast<cppu::OWeakObject*>(this)); + + if( mpSet ) + { + if( pEntry->nWID == OWN_ATTR_FILLBMP_MODE ) + { + mpSet->ClearItem( XATTR_FILLBMP_STRETCH ); + mpSet->ClearItem( XATTR_FILLBMP_TILE ); + } + else + { + mpSet->ClearItem( pEntry->nWID ); + } + } +} + +uno::Any SAL_CALL SdUnoPageBackground::getPropertyDefault( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + + const SfxItemPropertyMapEntry* pEntry = getPropertyMapEntry(aPropertyName); + if( pEntry == nullptr || mpSet == nullptr ) + throw beans::UnknownPropertyException( aPropertyName, static_cast<cppu::OWeakObject*>(this)); + + uno::Any aAny; + if (pEntry->nWID == OWN_ATTR_FILLBMP_MODE) + { + aAny <<= drawing::BitmapMode_REPEAT; + } + else + { + SfxItemPool& rPool = *mpSet->GetPool(); + SfxItemSet aSet(rPool, pEntry->nWID, pEntry->nWID); + aSet.Put(rPool.GetDefaultItem(pEntry->nWID)); + + aAny = SvxItemPropertySet_getPropertyValue(pEntry, aSet); + } + return aAny; +} + +/** this is used because our property map is not sorted yet */ +const SfxItemPropertyMapEntry* SdUnoPageBackground::getPropertyMapEntry( std::u16string_view rPropertyName ) const noexcept +{ + return mpPropSet->getPropertyMap().getByName(rPropertyName); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unopback.hxx b/sd/source/ui/unoidl/unopback.hxx new file mode 100644 index 000000000..c70cc2fea --- /dev/null +++ b/sd/source/ui/unoidl/unopback.hxx @@ -0,0 +1,89 @@ +/* -*- 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 . + */ +#pragma once + +#include <sal/config.h> + +#include <memory> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XPropertyState.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XUnoTunnel.hpp> + +#include <svl/lstner.hxx> +#include <comphelper/servicehelper.hxx> + +#include <cppuhelper/implbase.hxx> +#include <editeng/unoipset.hxx> + +class SdDrawDocument; +class SdrModel; +class SfxItemSet; +class SvxItemPropertySet; +struct SfxItemPropertyMapEntry; + +const SvxItemPropertySet* ImplGetPageBackgroundPropertySet(); + +class SdUnoPageBackground final : public ::cppu::WeakImplHelper< + css::beans::XPropertySet, + css::lang::XServiceInfo, + css::beans::XPropertyState, + css::lang::XUnoTunnel>, + public SfxListener +{ + const SvxItemPropertySet* mpPropSet; + SvxItemPropertySetUsrAnys maUsrAnys; + std::unique_ptr<SfxItemSet> mpSet; + SdrModel* mpDoc; + + const SfxItemPropertyMapEntry* getPropertyMapEntry( std::u16string_view rPropertyName ) const noexcept; +public: + SdUnoPageBackground( SdDrawDocument* pDoc = nullptr, const SfxItemSet* pSet = nullptr); + virtual ~SdUnoPageBackground() noexcept override; + + // internal + void fillItemSet( SdDrawDocument* pDoc, SfxItemSet& rSet ); + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override; + + // uno helper + UNO3_GETIMPLEMENTATION_DECL( SdUnoPageBackground ) + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override; + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override; + virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override; + virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override; + virtual void SAL_CALL addVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + + // XPropertyState + virtual css::beans::PropertyState SAL_CALL getPropertyState( const OUString& PropertyName ) override; + virtual css::uno::Sequence< css::beans::PropertyState > SAL_CALL getPropertyStates( const css::uno::Sequence< OUString >& aPropertyName ) override; + virtual void SAL_CALL setPropertyToDefault( const OUString& PropertyName ) override; + virtual css::uno::Any SAL_CALL getPropertyDefault( const OUString& aPropertyName ) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unopool.cxx b/sd/source/ui/unoidl/unopool.cxx new file mode 100644 index 000000000..7345dc45d --- /dev/null +++ b/sd/source/ui/unoidl/unopool.cxx @@ -0,0 +1,89 @@ +/* -*- 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 <i18nlangtag/languagetag.hxx> +#include <comphelper/propertysetinfo.hxx> +#include <editeng/eeitem.hxx> +#include <svx/unopool.hxx> + +#include <drawdoc.hxx> +#include "unopool.hxx" + +using namespace ::com::sun::star; +using namespace ::cppu; +using namespace ::comphelper; + +static LanguageType SdUnoGetLanguage( const lang::Locale& rLocale ) +{ + // empty language -> LANGUAGE_SYSTEM + if ( rLocale.Language.getLength() == 0 ) + return LANGUAGE_SYSTEM; + + LanguageType eRet = LanguageTag::convertToLanguageType( rLocale, false); + if ( eRet == LANGUAGE_NONE ) + eRet = LANGUAGE_SYSTEM; //! or throw an exception? + + return eRet; +} + +namespace { + +class SdUnoDrawPool : public SvxUnoDrawPool +{ +public: + explicit SdUnoDrawPool(SdDrawDocument* pModel); + +protected: + virtual void putAny( SfxItemPool* pPool, const PropertyMapEntry* pEntry, const uno::Any& rValue ) override; + +private: + SdDrawDocument* mpDrawModel; +}; + +} + +SdUnoDrawPool::SdUnoDrawPool(SdDrawDocument* pModel) +: SvxUnoDrawPool( pModel ), mpDrawModel( pModel ) +{ +} + +void SdUnoDrawPool::putAny( SfxItemPool* pPool, const comphelper::PropertyMapEntry* pEntry, const uno::Any& rValue ) +{ + switch( pEntry->mnHandle ) + { + case EE_CHAR_LANGUAGE: + case EE_CHAR_LANGUAGE_CJK: + case EE_CHAR_LANGUAGE_CTL: + { + lang::Locale aLocale; + if( rValue >>= aLocale ) + mpDrawModel->SetLanguage( + SdUnoGetLanguage( aLocale ), + static_cast<sal_uInt16>(pEntry->mnHandle) ); + } + } + SvxUnoDrawPool::putAny( pPool, pEntry, rValue ); +} + +uno::Reference< uno::XInterface > SdUnoCreatePool( SdDrawDocument* pDrawModel ) +{ + return static_cast<uno::XAggregation*>(new SdUnoDrawPool( pDrawModel )); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unopool.hxx b/sd/source/ui/unoidl/unopool.hxx new file mode 100644 index 000000000..4188e80a9 --- /dev/null +++ b/sd/source/ui/unoidl/unopool.hxx @@ -0,0 +1,29 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <com/sun/star/uno/Reference.hxx> +#include <drawdoc.hxx> + +css::uno::Reference<css::uno::XInterface> SdUnoCreatePool(SdDrawDocument* pDrawModel); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unosrch.cxx b/sd/source/ui/unoidl/unosrch.cxx new file mode 100644 index 000000000..f1005819d --- /dev/null +++ b/sd/source/ui/unoidl/unosrch.cxx @@ -0,0 +1,778 @@ +/* -*- 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 <memory> +#include <sal/config.h> + +#include <com/sun/star/drawing/XShapes.hpp> +#include <com/sun/star/drawing/XDrawPage.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <vcl/svapp.hxx> + +#include <svx/svdobj.hxx> +#include <svx/svdpool.hxx> +#include <editeng/unoipset.hxx> +#include <editeng/unotext.hxx> +#include <tools/debug.hxx> + +#include <unoprnms.hxx> +#include <unosrch.hxx> + +using namespace ::com::sun::star; + +#define WID_SEARCH_BACKWARDS 0 +#define WID_SEARCH_CASE 1 +#define WID_SEARCH_WORDS 2 + +static const SfxItemPropertyMapEntry* ImplGetSearchPropertyMap() +{ + static const SfxItemPropertyMapEntry aSearchPropertyMap_Impl[] = + { + { u"" UNO_NAME_SEARCH_BACKWARDS, WID_SEARCH_BACKWARDS, cppu::UnoType<bool>::get(), 0, 0 }, + { u"" UNO_NAME_SEARCH_CASE, WID_SEARCH_CASE, cppu::UnoType<bool>::get(), 0, 0 }, + { u"" UNO_NAME_SEARCH_WORDS, WID_SEARCH_WORDS, cppu::UnoType<bool>::get(), 0, 0 }, + { u"", 0, css::uno::Type(), 0, 0 } + }; + + return aSearchPropertyMap_Impl; +} + +namespace { + +class SearchContext_impl +{ + uno::Reference< drawing::XShapes > mxShapes; + sal_Int32 mnIndex; + +public: + SearchContext_impl(uno::Reference<drawing::XShapes> const& xShapes) + : mxShapes( xShapes ), mnIndex( -1 ) {} + + uno::Reference< drawing::XShape > firstShape() + { + mnIndex = -1; + return nextShape(); + } + + uno::Reference< drawing::XShape > nextShape() + { + uno::Reference< drawing::XShape > xShape; + mnIndex++; + if( mxShapes.is() && mxShapes->getCount() > mnIndex ) + { + mxShapes->getByIndex( mnIndex ) >>= xShape; + } + return xShape; + } +}; + +} + +/* ================================================================= */ +/** this class implements a search or replace operation on a given + page or a given sdrobj + */ + +SdUnoSearchReplaceShape::SdUnoSearchReplaceShape( drawing::XDrawPage* pPage ) noexcept + : mpPage(pPage) +{ +} + +SdUnoSearchReplaceShape::~SdUnoSearchReplaceShape() noexcept +{ +} + +// util::XReplaceable +uno::Reference< util::XReplaceDescriptor > SAL_CALL SdUnoSearchReplaceShape::createReplaceDescriptor() +{ + return new SdUnoSearchReplaceDescriptor; +} + +sal_Int32 SAL_CALL SdUnoSearchReplaceShape::replaceAll( const uno::Reference< util::XSearchDescriptor >& xDesc ) +{ + SdUnoSearchReplaceDescriptor* pDescr = comphelper::getFromUnoTunnel<SdUnoSearchReplaceDescriptor>( xDesc ); + if( pDescr == nullptr ) + return 0; + + sal_Int32 nFound = 0; + + uno::Reference< drawing::XShapes > xShapes; + uno::Reference< drawing::XShape > xShape; + + std::vector<SearchContext_impl> aContexts; + if(mpPage) + { + xShapes = mpPage; + + if( xShapes->getCount() ) + { + aContexts.push_back(SearchContext_impl(xShapes)); + xShape = aContexts.back().firstShape(); + } + else + { + xShapes = nullptr; + } + } + + while( xShape.is() ) + { + // replace in xShape + uno::Reference< text::XText > xText(xShape, uno::UNO_QUERY); + uno::Reference< text::XTextRange > xRange = xText; + uno::Reference< text::XTextRange > xFound; + + while( xRange.is() ) + { + xFound = Search( xRange, pDescr ); + if( !xFound.is() ) + break; + + xFound->setString( pDescr->getReplaceString() ); + xRange = xFound->getEnd(); + nFound++; + } + // done with xShape -> get next shape + + // test if it's a group + uno::Reference< drawing::XShapes > xGroupShape( xShape, uno::UNO_QUERY ); + if( xGroupShape.is() && ( xGroupShape->getCount() > 0 ) ) + { + aContexts.push_back(SearchContext_impl(xGroupShape)); + xShape = aContexts.back().firstShape(); + } + else + { + if (!aContexts.empty()) + xShape = aContexts.back().nextShape(); + else + xShape = nullptr; + } + + // test parent contexts for next shape if none + // is found in the current context + while (!aContexts.empty() && !xShape.is()) + { + aContexts.pop_back(); + if (!aContexts.empty()) + xShape = aContexts.back().nextShape(); + } + } + + return nFound; +} + +// XSearchable +uno::Reference< css::util::XSearchDescriptor > SAL_CALL SdUnoSearchReplaceShape::createSearchDescriptor( ) +{ + return new SdUnoSearchReplaceDescriptor; +} + +uno::Reference< css::container::XIndexAccess > SAL_CALL SdUnoSearchReplaceShape::findAll( const css::uno::Reference< css::util::XSearchDescriptor >& xDesc ) +{ + SdUnoSearchReplaceDescriptor* pDescr = comphelper::getFromUnoTunnel<SdUnoSearchReplaceDescriptor>( xDesc ); + if( pDescr == nullptr ) + return uno::Reference< container::XIndexAccess > (); + + sal_Int32 nSequence = 32; + sal_Int32 nFound = 0; + + uno::Sequence < uno::Reference< uno::XInterface > > aSeq( nSequence ); + + uno::Reference< uno::XInterface > * pArray = aSeq.getArray(); + + uno::Reference< drawing::XShapes > xShapes; + uno::Reference< drawing::XShape > xShape; + + std::vector<SearchContext_impl> aContexts; + if(mpPage) + { + xShapes = mpPage; + + if( xShapes->getCount() > 0 ) + { + aContexts.push_back(SearchContext_impl(xShapes)); + xShape = aContexts.back().firstShape(); + } + else + { + xShapes = nullptr; + } + } + + while( xShape.is() ) + { + // find in xShape + uno::Reference< text::XText > xText(xShape, uno::UNO_QUERY); + uno::Reference< text::XTextRange > xRange = xText; + uno::Reference< text::XTextRange > xFound; + + while( xRange.is() ) + { + xFound = Search( xRange, pDescr ); + if( !xFound.is() ) + break; + + if( nFound >= nSequence ) + { + nSequence += 32; + aSeq.realloc( nSequence ); + pArray = aSeq.getArray(); + } + + pArray[nFound++] = xFound; + + xRange = xFound->getEnd(); + } + // done with shape -> get next shape + + // test if it's a group + uno::Reference< drawing::XShapes > xGroupShape; + xGroupShape.set( xShape, uno::UNO_QUERY ); + + if( xGroupShape.is() && xGroupShape->getCount() > 0 ) + { + aContexts.push_back(SearchContext_impl(xGroupShape)); + xShape = aContexts.back().firstShape(); + } + else + { + if (!aContexts.empty()) + xShape = aContexts.back().nextShape(); + else + xShape = nullptr; + } + + // test parent contexts for next shape if none + // is found in the current context + while (!aContexts.empty() && !xShape.is()) + { + aContexts.pop_back(); + if (!aContexts.empty()) + xShape = aContexts.back().nextShape(); + } + } + + if( nFound != nSequence ) + aSeq.realloc( nFound ); + + uno::Reference<css::container::XIndexAccess> xRet(new SdUnoFindAllAccess(aSeq)); + return xRet; +} + +uno::Reference< css::uno::XInterface > SAL_CALL SdUnoSearchReplaceShape::findFirst( const css::uno::Reference< css::util::XSearchDescriptor >& xDesc ) +{ + uno::Reference< text::XTextRange > xRange( GetCurrentShape(), uno::UNO_QUERY ); + if( xRange.is() ) + return findNext( xRange, xDesc ); + + return uno::Reference< uno::XInterface > (); +} + +uno::Reference< drawing::XShape > SdUnoSearchReplaceShape::GetCurrentShape() const noexcept +{ + uno::Reference< drawing::XShape > xShape; + + if( mpPage && mpPage->getCount() > 0) + mpPage->getByIndex(0) >>= xShape; + + return xShape; + +} + +uno::Reference< css::uno::XInterface > SAL_CALL SdUnoSearchReplaceShape::findNext( const css::uno::Reference< css::uno::XInterface >& xStartAt, const css::uno::Reference< css::util::XSearchDescriptor >& xDesc ) +{ + SdUnoSearchReplaceDescriptor* pDescr = comphelper::getFromUnoTunnel<SdUnoSearchReplaceDescriptor>( xDesc ); + + uno::Reference< uno::XInterface > xFound; + + uno::Reference< text::XTextRange > xRange( xStartAt, uno::UNO_QUERY ); + if(pDescr && xRange.is() ) + { + + uno::Reference< text::XTextRange > xCurrentRange( xStartAt, uno::UNO_QUERY ); + + uno::Reference< drawing::XShape > xCurrentShape( GetShape( xCurrentRange ) ); + + while(!xFound.is() && xRange.is()) + { + xFound = Search( xRange, pDescr ); + if(!xFound.is()) + { + // we need a new starting range now + xRange = nullptr; + + if(mpPage) + { + // we do a page wide search, so skip to the next shape here + // get next shape on our page + uno::Reference< drawing::XShape > xFound2( GetNextShape( mpPage, xCurrentShape ) ); + if( xFound2.is() && (xFound2.get() != xCurrentShape.get()) ) + xCurrentShape = xFound2; + else + xCurrentShape = nullptr; + + xRange.set( xCurrentShape, uno::UNO_QUERY ); + if(!(xCurrentShape.is() && (xRange.is()))) + xRange = nullptr; + } + else + { + // we search only in this shape, so end search if we have + // not found anything + } + } + } + } + return xFound; +} + +/** this method returns the shape that follows xCurrentShape in the shape collection xShapes. + It steps recursive into groupshapes and returns the xCurrentShape if it is the last + shape in this collection */ +uno::Reference< drawing::XShape > SdUnoSearchReplaceShape::GetNextShape( const uno::Reference< container::XIndexAccess >& xShapes, const uno::Reference< drawing::XShape >& xCurrentShape ) noexcept +{ + uno::Reference< drawing::XShape > xFound; + + if(xShapes.is() && xCurrentShape.is()) + { + const sal_Int32 nCount = xShapes->getCount(); + for( sal_Int32 i = 0; i < nCount; i++ ) + { + uno::Reference< drawing::XShape > xSearchShape; + xShapes->getByIndex(i) >>= xSearchShape; + + if( xSearchShape.is() ) + { + uno::Reference< container::XIndexAccess > xGroup( xSearchShape, uno::UNO_QUERY ); + + if( xCurrentShape.get() == xSearchShape.get() ) + { + if( xGroup.is() && xGroup->getCount() > 0 ) + { + xGroup->getByIndex( 0 ) >>= xFound; + } + else + { + i++; + if( i < nCount ) + xShapes->getByIndex( i ) >>= xFound; + else + xFound = xCurrentShape; + } + + break; + } + else if( xGroup.is() ) + { + xFound = GetNextShape( xGroup, xCurrentShape ); + if( xFound.is() ) + { + if( xFound.get() == xCurrentShape.get() ) + { + // the current shape was found at the end of the group + i++; + if( i < nCount ) + { + xShapes->getByIndex(i) >>= xFound; + } + } + break; + } + } + } + } + } + + return xFound; +} + +uno::Reference< text::XTextRange > SdUnoSearchReplaceShape::Search( const uno::Reference< text::XTextRange >& xText, SdUnoSearchReplaceDescriptor* pDescr ) +{ + if(!xText.is()) + return uno::Reference< text::XTextRange > (); + + uno::Reference< text::XText > xParent( xText->getText() ); + + if( !xParent.is() ) + { + xParent.set( xText, uno::UNO_QUERY ); + } + + const OUString aText( xParent->getString() ); + + const sal_Int32 nTextLen = aText.getLength(); + + std::unique_ptr<sal_Int32[]> pConvertPos( new sal_Int32[nTextLen+2] ); + std::unique_ptr<sal_Int32[]> pConvertPara( new sal_Int32[nTextLen+2] ); + + sal_Int32* pPos = pConvertPos.get(); + sal_Int32* pPara = pConvertPara.get(); + + sal_Int32 nLastPos = 0, nLastPara = 0; + + uno::Reference< container::XEnumerationAccess > xEnumAccess( xParent, uno::UNO_QUERY ); + + // first we fill the arrays with the position and paragraph for every character + // inside the text + if( xEnumAccess.is() ) + { + uno::Reference< container::XEnumeration > xParaEnum( xEnumAccess->createEnumeration() ); + + while(xParaEnum->hasMoreElements()) + { + int ndbg = 0; + uno::Reference< text::XTextContent > xParagraph( xParaEnum->nextElement(), uno::UNO_QUERY ); + if( xParagraph.is() ) + xEnumAccess.set(xParagraph, css::uno::UNO_QUERY); + else + xEnumAccess.clear(); + + if( xEnumAccess.is() ) + { + uno::Reference< container::XEnumeration > xPortionEnum( xEnumAccess->createEnumeration() ); + if( xPortionEnum.is() ) + { + while(xPortionEnum->hasMoreElements()) + { + uno::Reference< text::XTextRange > xPortion( xPortionEnum->nextElement(), uno::UNO_QUERY ); + if( xPortion.is() ) + { + const OUString aPortion( xPortion->getString() ); + const sal_Int32 nLen = aPortion.getLength(); + + ESelection aStartSel( GetSelection( xPortion->getStart() ) ); + ESelection aEndSel( GetSelection( xPortion->getEnd() ) ); + + // special case for empty portions with content or length one portions with content (fields) + if( (aStartSel.nStartPos == aEndSel.nStartPos) || ( (aStartSel.nStartPos == (aEndSel.nStartPos - 1)) && (nLen > 1) ) ) + { + for( sal_Int32 i = 0; i < nLen; i++ ) + { + if( ndbg < (nTextLen+2) ) + { + *pPos++ = aStartSel.nStartPos; + *pPara++ = aStartSel.nStartPara; + + ndbg += 1; + } + else + { + OSL_FAIL( "array overflow while searching" ); + } + } + + nLastPos = aStartSel.nStartPos; + } + // normal case + else + { + for( sal_Int32 i = 0; i < nLen; i++ ) + { + if( ndbg < (nTextLen+2) ) + { + *pPos++ = aStartSel.nStartPos++; + *pPara++ = aStartSel.nStartPara; + + ndbg += 1; + } + else + { + OSL_FAIL( "array overflow while searching" ); + } + } + + nLastPos = aStartSel.nStartPos - 1; + DBG_ASSERT( aEndSel.nStartPos == aStartSel.nStartPos, "Search is not working" ); + } + nLastPara = aStartSel.nStartPara; + } + } + } + } + + if( ndbg < (nTextLen+2) ) + { + *pPos++ = nLastPos + 1; + *pPara++ = nLastPara; + } + else + { + OSL_FAIL( "array overflow while searching" ); + } + } + } + + uno::Reference< text::XTextRange > xFound; + ESelection aSel; + + if( xText.is() ) + aSel = GetSelection( xText ); + + sal_Int32 nStartPos; + sal_Int32 nEndPos = 0; + for( nStartPos = 0; nStartPos < nTextLen; nStartPos++ ) + { + if( pConvertPara[nStartPos] == aSel.nStartPara && pConvertPos[nStartPos] == aSel.nStartPos ) + break; + } + + if( Search( aText, nStartPos, nEndPos, pDescr ) ) + { + if( nStartPos <= nTextLen && nEndPos <= nTextLen ) + { + ESelection aSelection( pConvertPara[nStartPos], pConvertPos[nStartPos], + pConvertPara[nEndPos], pConvertPos[nEndPos] ); + + SvxUnoTextBase* pParent = comphelper::getFromUnoTunnel<SvxUnoTextBase>( xParent ); + + if(pParent) + { + rtl::Reference<SvxUnoTextRange> pRange = new SvxUnoTextRange( *pParent ); + xFound = pRange; + pRange->SetSelection(aSelection); + } + } + else + { + OSL_FAIL("Array overflow while searching!"); + } + } + + return xFound; +} + +bool SdUnoSearchReplaceShape::Search( const OUString& rText, sal_Int32& nStartPos, sal_Int32& nEndPos, SdUnoSearchReplaceDescriptor* pDescr ) noexcept +{ + OUString aSearchStr( pDescr->getSearchString() ); + OUString aText( rText ); + + if( !pDescr->IsCaseSensitive() ) + { + aText = aText.toAsciiLowerCase(); + aSearchStr = aSearchStr.toAsciiLowerCase(); + } + + sal_Int32 nFound = aText.indexOf( aSearchStr, nStartPos ); + if( nFound != -1 ) + { + nStartPos = nFound; + nEndPos = nFound + aSearchStr.getLength(); + + if(pDescr->IsWords()) + { + if( (nStartPos > 0 && aText[nStartPos-1] > ' ') || + (nEndPos < aText.getLength() && aText[nEndPos] > ' ') ) + { + nStartPos++; + return Search( aText, nStartPos, nEndPos, pDescr ); + } + } + + return true; + } + else + return false; +} + +ESelection SdUnoSearchReplaceShape::GetSelection( const uno::Reference< text::XTextRange >& xTextRange ) noexcept +{ + ESelection aSel; + SvxUnoTextRangeBase* pRange = comphelper::getFromUnoTunnel<SvxUnoTextRangeBase>( xTextRange ); + + if(pRange) + aSel = pRange->GetSelection(); + + return aSel; +} + +uno::Reference< drawing::XShape > SdUnoSearchReplaceShape::GetShape( const uno::Reference< text::XTextRange >& xTextRange ) noexcept +{ + uno::Reference< drawing::XShape > xShape; + + if(xTextRange.is()) + { + uno::Reference< text::XText > xText( xTextRange->getText() ); + + if(xText.is()) + { + do + { + xShape.set( xText, uno::UNO_QUERY ); + if(!xShape.is()) + { + uno::Reference< text::XText > xParent( xText->getText() ); + if(!xParent.is() || xText.get() == xParent.get()) + return xShape; + + xText = xParent; + } + } while( !xShape.is() ); + } + } + + return xShape; +} + +/* ================================================================= */ +/** this class holds the parameters and status of a search or replace + operation performed by class SdUnoSearchReplaceShape + */ + +UNO3_GETIMPLEMENTATION_IMPL( SdUnoSearchReplaceDescriptor ); + +SdUnoSearchReplaceDescriptor::SdUnoSearchReplaceDescriptor() +{ + mpPropSet.reset( new SvxItemPropertySet(ImplGetSearchPropertyMap(), SdrObject::GetGlobalDrawObjectItemPool()) ); + + mbBackwards = false; + mbCaseSensitive = false; + mbWords = false; +} + +SdUnoSearchReplaceDescriptor::~SdUnoSearchReplaceDescriptor() noexcept +{ +} + +// XSearchDescriptor +OUString SAL_CALL SdUnoSearchReplaceDescriptor::getSearchString() +{ + return maSearchStr; +} + +void SAL_CALL SdUnoSearchReplaceDescriptor::setSearchString( const OUString& aString ) +{ + maSearchStr = aString; +} + +// XReplaceDescriptor +OUString SAL_CALL SdUnoSearchReplaceDescriptor::getReplaceString() +{ + return maReplaceStr; +} + +void SAL_CALL SdUnoSearchReplaceDescriptor::setReplaceString( const OUString& aReplaceString ) +{ + maReplaceStr = aReplaceString; +} + +// XPropertySet +uno::Reference< css::beans::XPropertySetInfo > SAL_CALL SdUnoSearchReplaceDescriptor::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + return mpPropSet->getPropertySetInfo(); +} + +void SAL_CALL SdUnoSearchReplaceDescriptor::setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + + const SfxItemPropertyMapEntry* pEntry = mpPropSet->getPropertyMapEntry(aPropertyName); + + bool bOk = false; + + switch( pEntry ? pEntry->nWID : -1 ) + { + case WID_SEARCH_BACKWARDS: + bOk = (aValue >>= mbBackwards); + break; + case WID_SEARCH_CASE: + bOk = (aValue >>= mbCaseSensitive); + break; + case WID_SEARCH_WORDS: + bOk = (aValue >>= mbWords); + break; + default: + throw beans::UnknownPropertyException( aPropertyName, static_cast<cppu::OWeakObject*>(this)); + } + + if( !bOk ) + throw lang::IllegalArgumentException(); +} + +uno::Any SAL_CALL SdUnoSearchReplaceDescriptor::getPropertyValue( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + uno::Any aAny; + + const SfxItemPropertyMapEntry* pEntry = mpPropSet->getPropertyMapEntry(PropertyName); + + switch( pEntry ? pEntry->nWID : -1 ) + { + case WID_SEARCH_BACKWARDS: + aAny <<= mbBackwards; + break; + case WID_SEARCH_CASE: + aAny <<= mbCaseSensitive; + break; + case WID_SEARCH_WORDS: + aAny <<= mbWords; + break; + default: + throw beans::UnknownPropertyException( PropertyName, static_cast<cppu::OWeakObject*>(this)); + } + + return aAny; +} + +void SAL_CALL SdUnoSearchReplaceDescriptor::addPropertyChangeListener( const OUString& , const css::uno::Reference< css::beans::XPropertyChangeListener >& ) {} +void SAL_CALL SdUnoSearchReplaceDescriptor::removePropertyChangeListener( const OUString& , const css::uno::Reference< css::beans::XPropertyChangeListener >& ) {} +void SAL_CALL SdUnoSearchReplaceDescriptor::addVetoableChangeListener( const OUString& , const css::uno::Reference< css::beans::XVetoableChangeListener >& ) {} +void SAL_CALL SdUnoSearchReplaceDescriptor::removeVetoableChangeListener( const OUString& , const css::uno::Reference< css::beans::XVetoableChangeListener >& ) {} + +/* ================================================================= */ + +SdUnoFindAllAccess::SdUnoFindAllAccess( uno::Sequence< uno::Reference< uno::XInterface > > const & rSequence ) noexcept +:maSequence( rSequence ) +{ +} + +SdUnoFindAllAccess::~SdUnoFindAllAccess() noexcept +{ +} + +// XElementAccess +uno::Type SAL_CALL SdUnoFindAllAccess::getElementType() +{ + return cppu::UnoType<text::XTextRange>::get(); +} + +sal_Bool SAL_CALL SdUnoFindAllAccess::hasElements() +{ + return maSequence.hasElements(); +} + +// XIndexAccess +sal_Int32 SAL_CALL SdUnoFindAllAccess::getCount() +{ + return maSequence.getLength(); +} + +uno::Any SAL_CALL SdUnoFindAllAccess::getByIndex( sal_Int32 Index ) +{ + if( Index < 0 || Index >= getCount() ) + throw lang::IndexOutOfBoundsException(); + + uno::Any aAny; + aAny <<= maSequence[Index]; + return aAny; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unowcntr.cxx b/sd/source/ui/unoidl/unowcntr.cxx new file mode 100644 index 000000000..1079477ef --- /dev/null +++ b/sd/source/ui/unoidl/unowcntr.cxx @@ -0,0 +1,99 @@ +/* -*- 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 <com/sun/star/lang/XComponent.hpp> + +#include "unowcntr.hxx" + +using namespace ::com::sun::star; + +SvUnoWeakContainer::SvUnoWeakContainer() noexcept +{ +} + +SvUnoWeakContainer::~SvUnoWeakContainer() noexcept +{ +} + +/** inserts the given ref into this container */ +void SvUnoWeakContainer::insert( const uno::WeakReference< uno::XInterface >& xRef ) noexcept +{ + for ( auto it = maVector.begin(); it != maVector.end(); ) + { + uno::WeakReference< uno::XInterface > & rWeakRef = *it; + uno::Reference< uno::XInterface > xTestRef( rWeakRef ); + if ( !xTestRef.is() ) + { + it = maVector.erase( it ); + } + else + { + if ( rWeakRef == xRef ) + return; + ++it; + } + } + maVector.emplace_back( xRef ); +} + +/** searches the container for a ref that returns true on the given + search function +*/ +bool SvUnoWeakContainer::findRef( + uno::WeakReference< uno::XInterface >& rRef, + void const * pSearchData, + weakref_searchfunc pSearchFunc +) +{ + for ( auto it = maVector.begin(); it != maVector.end(); ) + { + uno::WeakReference< uno::XInterface > & itRef = *it; + uno::Reference< uno::XInterface > xTestRef( itRef ); + if ( !xTestRef.is() ) + { + it = maVector.erase( it ); + } + else + { + if( (*pSearchFunc)( itRef, pSearchData ) ) + { + rRef = itRef; + return true; + } + ++it; + } + } + return false; +} + +void SvUnoWeakContainer::dispose() +{ + for (auto const& elem : maVector) + { + uno::Reference< uno::XInterface > xTestRef( elem ); + if ( xTestRef.is() ) + { + uno::Reference< lang::XComponent > xComp( xTestRef, uno::UNO_QUERY ); + if( xComp.is() ) + xComp->dispose(); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unowcntr.hxx b/sd/source/ui/unoidl/unowcntr.hxx new file mode 100644 index 000000000..a863f0929 --- /dev/null +++ b/sd/source/ui/unoidl/unowcntr.hxx @@ -0,0 +1,47 @@ +/* -*- 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 . + */ + +#pragma once + +#include <cppuhelper/weakref.hxx> +#include <vector> + +typedef bool (*weakref_searchfunc)( const css::uno::WeakReference< css::uno::XInterface >& xRef, void const * pSearchData ); + +class SvUnoWeakContainer +{ +private: + std::vector< css::uno::WeakReference< css::uno::XInterface > > maVector; + +public: + SvUnoWeakContainer() noexcept; + ~SvUnoWeakContainer() noexcept; + + /** inserts the given ref into this container */ + void insert( const css::uno::WeakReference< css::uno::XInterface >& xRef ) noexcept; + + /** searches the container for a ref that returns true on the given + search function + */ + bool findRef( css::uno::WeakReference< css::uno::XInterface >& rRef, void const * pSearchData, weakref_searchfunc pSearchFunc ); + + void dispose(); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/DocumentRenderer.cxx b/sd/source/ui/view/DocumentRenderer.cxx new file mode 100644 index 000000000..eee1a759e --- /dev/null +++ b/sd/source/ui/view/DocumentRenderer.cxx @@ -0,0 +1,2256 @@ +/* -*- 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 <com/sun/star/beans/XPropertySet.hpp> + +#include <DocumentRenderer.hxx> +#include <DocumentRenderer.hrc> +#include <ViewShellBase.hxx> + +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <sdresid.hxx> +#include <strings.hrc> +#include <drawview.hxx> +#include <DrawViewShell.hxx> +#include <FrameView.hxx> +#include <Outliner.hxx> +#include <OutlineViewShell.hxx> +#include <SlideSorterViewShell.hxx> +#include <DrawDocShell.hxx> + +#include <tools/multisel.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/sequence.hxx> +#include <rtl/ustrbuf.hxx> +#include <editeng/editstat.hxx> +#include <editeng/outlobj.hxx> +#include <svx/svdetc.hxx> +#include <svx/svditer.hxx> +#include <svx/svdopage.hxx> +#include <svx/svdopath.hxx> +#include <svx/svdpagv.hxx> +#include <svx/xlineit0.hxx> +#include <svx/xlnclit.hxx> +#include <toolkit/awt/vclxdevice.hxx> +#include <unotools/localedatawrapper.hxx> +#include <vcl/print.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <unotools/moduleoptions.hxx> +#include <xmloff/autolayout.hxx> +#include <sfx2/objsh.hxx> + +#include <officecfg/Office/Draw.hxx> +#include <officecfg/Office/Impress.hxx> + +#include <algorithm> +#include <memory> +#include <vector> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd { + +namespace { + + /** Convenience class to extract values from the sequence of properties + given to one of the XRenderable methods. + */ + class PrintOptions + { + public: + PrintOptions ( + const vcl::PrinterOptionsHelper& rHelper, + std::vector<sal_Int32>&& rSlidesPerPage) + : mrProperties(rHelper), + maSlidesPerPage(std::move(rSlidesPerPage)) + { + } + + bool IsWarningOrientation() const + { + return GetBoolValue(nullptr, true); + } + + bool IsPrintPageName() const + { + return GetBoolValue("IsPrintName", false); + } + + bool IsDate() const + { + return GetBoolValue("IsPrintDateTime", false); + } + + bool IsTime() const + { + return GetBoolValue("IsPrintDateTime", false); + } + + bool IsHiddenPages() const + { + return GetBoolValue("IsPrintHidden", false); + } + + bool IsHandoutHorizontal() const + { + return GetBoolValue("SlidesPerPageOrder", sal_Int32(0)); + } + + sal_Int32 GetHandoutPageCount() const + { + sal_uInt32 nIndex = static_cast<sal_Int32>(mrProperties.getIntValue("SlidesPerPage", sal_Int32(0))); + if (nIndex<maSlidesPerPage.size()) + return maSlidesPerPage[nIndex]; + else if ( ! maSlidesPerPage.empty()) + return maSlidesPerPage[0]; + else + return 0; + } + + bool IsDraw() const + { + return GetBoolValue("PageContentType", sal_Int32(0)); + } + + bool IsHandout() const + { + return GetBoolValue("PageContentType", sal_Int32(1)); + } + + bool IsNotes() const + { + return GetBoolValue("PageContentType", sal_Int32(2)); + } + + bool IsOutline() const + { + return GetBoolValue("PageContentType", sal_Int32(3)); + } + + sal_uLong GetOutputQuality() const + { + sal_Int32 nQuality = static_cast<sal_Int32>(mrProperties.getIntValue( "Quality", sal_Int32(0) )); + return nQuality; + } + + bool IsPageSize() const + { + return GetBoolValue("PageOptions", sal_Int32(1)); + } + + bool IsTilePage() const + { + return GetBoolValue("PageOptions", sal_Int32(2)) || GetBoolValue("PageOptions", sal_Int32(3)); + } + + bool IsCutPage() const + { + return GetBoolValue("PageOptions", sal_Int32(0)); + } + + bool IsBooklet() const + { + return GetBoolValue("PrintProspect", false); + } + + bool IsPrinterPreferred(DocumentType eDocType) const + { + bool bIsDraw = eDocType == DocumentType::Draw; + return IsTilePage() || IsPageSize() || IsBooklet() || (!bIsDraw && !IsNotes()); + } + + bool IsPrintExcluded() const + { + return (IsNotes() || IsDraw() || IsHandout()) && IsHiddenPages(); + } + + bool IsPrintFrontPage() const + { + sal_Int32 nInclude = static_cast<sal_Int32>(mrProperties.getIntValue( "EvenOdd", 0 )); + return nInclude != 2; + } + + bool IsPrintBackPage() const + { + sal_Int32 nInclude = static_cast<sal_Int32>(mrProperties.getIntValue( "EvenOdd", 0 )); + return nInclude != 1; + } + + bool IsPaperBin() const + { + return GetBoolValue("PrintPaperFromSetup", false); + } + + bool IsPrintMarkedOnly() const + { + return GetBoolValue("PrintContent", sal_Int32(4)); + } + + OUString GetPrinterSelection (sal_Int32 nPageCount, sal_Int32 nCurrentPageIndex) const + { + sal_Int32 nContent = static_cast<sal_Int32>(mrProperties.getIntValue( "PrintContent", 0 )); + OUString sFullRange = "1-" + OUString::number(nPageCount); + + if (nContent == 0) // all pages/slides + { + return sFullRange; + } + + if (nContent == 1) // range + { + OUString sValue = mrProperties.getStringValue("PageRange"); + return sValue.isEmpty() ? sFullRange : sValue; + } + + if (nContent == 2 && // selection + nCurrentPageIndex >= 0) + { + return OUString::number(nCurrentPageIndex + 1); + } + + return OUString(); + } + + private: + const vcl::PrinterOptionsHelper& mrProperties; + const std::vector<sal_Int32> maSlidesPerPage; + + /** When the value of the property with name pName is a boolean then + return its value. When the property is unknown then + bDefaultValue is returned. Otherwise <FALSE/> is returned. + */ + bool GetBoolValue ( + const char* pName, + const bool bDefaultValue) const + { + bool bValue = mrProperties.getBoolValue( pName, bDefaultValue ); + return bValue; + } + + /** Return <TRUE/> when the value of the property with name pName is + an integer and its value is nTriggerValue. Otherwise <FALSE/> is + returned. + */ + bool GetBoolValue ( + const char* pName, + const sal_Int32 nTriggerValue) const + { + sal_Int32 nValue = static_cast<sal_Int32>(mrProperties.getIntValue( pName, 0 )); + return nValue == nTriggerValue; + } + }; + + /** A collection of values that helps to reduce the number of arguments + given to some functions. Note that not all values are set at the + same time. + */ + class PrintInfo + { + public: + PrintInfo ( + Printer* pPrinter, + const bool bPrintMarkedOnly) + : mpPrinter(pPrinter), + mnDrawMode(DrawModeFlags::Default), + maPrintSize(0,0), + maPageSize(0,0), + meOrientation(Orientation::Portrait), + mbPrintMarkedOnly(bPrintMarkedOnly) + {} + + const VclPtr<Printer> mpPrinter; + DrawModeFlags mnDrawMode; + OUString msTimeDate; + OUString msPageString; + Size maPrintSize; + Size maPageSize; + Orientation meOrientation; + MapMode maMap; + const bool mbPrintMarkedOnly; + }; + + /** Output one page of the document to the given printer. Note that + more than one document page may be output to one printer page. + */ + void PrintPage ( + Printer& rPrinter, + ::sd::View& rPrintView, + SdPage& rPage, + View const * pView, + const bool bPrintMarkedOnly, + const SdrLayerIDSet& rVisibleLayers, + const SdrLayerIDSet& rPrintableLayers) + { + rPrintView.ShowSdrPage(&rPage); + + const MapMode aOriginalMapMode (rPrinter.GetMapMode()); + + // Set the visible layers + SdrPageView* pPageView = rPrintView.GetSdrPageView(); + OSL_ASSERT(pPageView!=nullptr); + pPageView->SetVisibleLayers(rVisibleLayers); + pPageView->SetPrintableLayers(rPrintableLayers); + + if (pView!=nullptr && bPrintMarkedOnly) + pView->DrawMarkedObj(rPrinter); + else + rPrintView.CompleteRedraw(&rPrinter, + vcl::Region(::tools::Rectangle(Point(0,0), rPage.GetSize()))); + + rPrinter.SetMapMode(aOriginalMapMode); + + rPrintView.HideSdrPage(); + } + + /** Output a string (that typically is not part of a document page) to + the given printer. + */ + void PrintMessage ( + Printer& rPrinter, + const OUString& rsPageString, + const Point& rPageStringOffset) + { + const vcl::Font aOriginalFont (rPrinter.OutputDevice::GetFont()); + rPrinter.SetFont(vcl::Font(FAMILY_SWISS, Size(0, 423))); + rPrinter.DrawText(rPageStringOffset, rsPageString); + rPrinter.SetFont(aOriginalFont); + } + + /** Read the resources and process then into a sequence of properties + that can be passed to the printing dialog. + */ + class DialogCreator + { + public: + DialogCreator (ViewShellBase &rBase, bool bImpress, sal_Int32 nCurPage) + : mrBase(rBase) + , mbImpress(bImpress) + , mnCurPage(nCurPage) + { + ProcessResource(); + } + + const std::vector< beans::PropertyValue >& GetDialogControls() const + { + return maProperties; + } + + const std::vector<sal_Int32>& GetSlidesPerPage() const + { + return maSlidesPerPage; + } + + private: + ViewShellBase &mrBase; + std::vector<beans::PropertyValue> maProperties; + std::vector<sal_Int32> maSlidesPerPage; + bool mbImpress; + sal_Int32 mnCurPage; + + void ProcessResource() + { + // load the writer PrinterOptions into the custom tab + beans::PropertyValue aOptionsUIFile; + aOptionsUIFile.Name = "OptionsUIFile"; + if( mbImpress ) + aOptionsUIFile.Value <<= OUString("modules/simpress/ui/impressprinteroptions.ui"); + else + aOptionsUIFile.Value <<= OUString("modules/sdraw/ui/drawprinteroptions.ui"); + maProperties.push_back(aOptionsUIFile); + + SvtModuleOptions aOpt; + OUString aAppGroupname(SdResId(STR_IMPRESS_PRINT_UI_GROUP_NAME)); + aAppGroupname = aAppGroupname.replaceFirst("%s", aOpt.GetModuleName( + mbImpress ? SvtModuleOptions::EModule::IMPRESS : SvtModuleOptions::EModule::DRAW)); + AddDialogControl(vcl::PrinterOptionsHelper::setGroupControlOpt("tabcontrol-page2", aAppGroupname, ".HelpID:vcl:PrintDialog:TabPage:AppPage")); + + uno::Sequence< OUString > aHelpIds, aWidgetIds; + if( mbImpress ) + { + aHelpIds = { ".HelpID:vcl:PrintDialog:PageContentType:ListBox" }; + AddDialogControl( vcl::PrinterOptionsHelper::setChoiceListControlOpt( + "impressdocument", + SdResId(STR_IMPRESS_PRINT_UI_CONTENT), + aHelpIds, + "PageContentType" , + CreateChoice(STR_IMPRESS_PRINT_UI_CONTENT_CHOICES, SAL_N_ELEMENTS(STR_IMPRESS_PRINT_UI_CONTENT_CHOICES)), + 0) + ); + + aHelpIds = { ".HelpID:vcl:PrintDialog:SlidesPerPage:ListBox" }; + vcl::PrinterOptionsHelper::UIControlOptions aContentOpt( "PageContentType" , 1 ); + AddDialogControl( vcl::PrinterOptionsHelper::setChoiceListControlOpt( + "slidesperpage", + SdResId(STR_IMPRESS_PRINT_UI_SLIDESPERPAGE), + aHelpIds, + "SlidesPerPage" , + GetSlidesPerPageSequence(), + 0, + Sequence< sal_Bool >(), + aContentOpt + ) + ); + + aHelpIds = { ".HelpID:vcl:PrintDialog:SlidesPerPageOrder:ListBox" }; + vcl::PrinterOptionsHelper::UIControlOptions aSlidesPerPageOpt( "SlidesPerPage" , -1, true ); + AddDialogControl( vcl::PrinterOptionsHelper::setChoiceListControlOpt( + "slidesperpageorder", + SdResId(STR_IMPRESS_PRINT_UI_ORDER), + aHelpIds, + "SlidesPerPageOrder" , + CreateChoice(STR_IMPRESS_PRINT_UI_ORDER_CHOICES, SAL_N_ELEMENTS(STR_IMPRESS_PRINT_UI_ORDER_CHOICES)), + 0, + Sequence< sal_Bool >(), + aSlidesPerPageOpt ) + ); + } + + AddDialogControl( vcl::PrinterOptionsHelper::setSubgroupControlOpt("contents", + SdResId(STR_IMPRESS_PRINT_UI_INCLUDE_CONTENT), "" ) ); + + if( mbImpress ) + { + AddDialogControl( vcl::PrinterOptionsHelper::setBoolControlOpt("printname", + SdResId(STR_IMPRESS_PRINT_UI_IS_PRINT_NAME), + ".HelpID:vcl:PrintDialog:IsPrintName:CheckBox" , + "IsPrintName" , + officecfg::Office::Impress::Print::Other::PageName::get() + ) + ); + } + else + { + AddDialogControl( vcl::PrinterOptionsHelper::setBoolControlOpt("printname", + SdResId(STR_DRAW_PRINT_UI_IS_PRINT_NAME), + ".HelpID:vcl:PrintDialog:IsPrintName:CheckBox" , + "IsPrintName" , + officecfg::Office::Draw::Print::Other::PageName::get() + ) + ); + } + + AddDialogControl( vcl::PrinterOptionsHelper::setBoolControlOpt("printdatetime", + SdResId(STR_IMPRESS_PRINT_UI_IS_PRINT_DATE), + ".HelpID:vcl:PrintDialog:IsPrintDateTime:CheckBox" , + "IsPrintDateTime" , + // Separate settings for time and date in Impress/Draw -> Print page, check that both are set + mbImpress ? + officecfg::Office::Impress::Print::Other::Date::get() && + officecfg::Office::Impress::Print::Other::Time::get() : + officecfg::Office::Draw::Print::Other::Date::get() && + officecfg::Office::Draw::Print::Other::Time::get() + ) + ); + + if( mbImpress ) + { + AddDialogControl( vcl::PrinterOptionsHelper::setBoolControlOpt("printhidden", + SdResId(STR_IMPRESS_PRINT_UI_IS_PRINT_HIDDEN), + ".HelpID:vcl:PrintDialog:IsPrintHidden:CheckBox" , + "IsPrintHidden" , + officecfg::Office::Impress::Print::Other::HiddenPage::get() + ) + ); + } + + AddDialogControl( vcl::PrinterOptionsHelper::setSubgroupControlOpt("color", + SdResId(STR_IMPRESS_PRINT_UI_QUALITY), "" ) ); + + aHelpIds = { ".HelpID:vcl:PrintDialog:Quality:RadioButton:0", + ".HelpID:vcl:PrintDialog:Quality:RadioButton:1", + ".HelpID:vcl:PrintDialog:Quality:RadioButton:2" }; + aWidgetIds = { "originalcolors", "grayscale", "blackandwhite" }; + AddDialogControl( vcl::PrinterOptionsHelper::setChoiceRadiosControlOpt( + aWidgetIds, + "", + aHelpIds, + "Quality" , + CreateChoice(STR_IMPRESS_PRINT_UI_QUALITY_CHOICES, SAL_N_ELEMENTS(STR_IMPRESS_PRINT_UI_QUALITY_CHOICES)), + mbImpress ? officecfg::Office::Impress::Print::Other::Quality::get() : + officecfg::Office::Draw::Print::Other::Quality::get() ) + + ); + + AddDialogControl( vcl::PrinterOptionsHelper::setSubgroupControlOpt("pagesizes", + SdResId(STR_IMPRESS_PRINT_UI_PAGE_OPTIONS), "" ) ); + + aHelpIds = { ".HelpID:vcl:PrintDialog:PageOptions:RadioButton:0", + ".HelpID:vcl:PrintDialog:PageOptions:RadioButton:1", + ".HelpID:vcl:PrintDialog:PageOptions:RadioButton:2", + ".HelpID:vcl:PrintDialog:PageOptions:RadioButton:3" }; + aWidgetIds = { "originalsize", "fittoprintable", "distributeonmultiple", "tilesheet" }; + + // Mutually exclusive page options settings are stored in separate config keys... + // TODO: There is no config key to set the distributeonmultiple option as default + sal_Int32 nDefaultChoice = 0; + if ( mbImpress ? officecfg::Office::Impress::Print::Page::PageSize::get() : + officecfg::Office::Draw::Print::Page::PageSize::get() ) + { + nDefaultChoice = 1; + } + else if ( mbImpress ? officecfg::Office::Impress::Print::Page::PageTile::get() : + officecfg::Office::Draw::Print::Page::PageTile::get() ) + { + nDefaultChoice = 3; + } + vcl::PrinterOptionsHelper::UIControlOptions aPageOptionsOpt("PrintProspect", 0); + AddDialogControl( vcl::PrinterOptionsHelper::setChoiceRadiosControlOpt( + aWidgetIds, + "", + aHelpIds, + "PageOptions" , + mbImpress ? CreateChoice(STR_IMPRESS_PRINT_UI_PAGE_OPTIONS_CHOICES, SAL_N_ELEMENTS(STR_IMPRESS_PRINT_UI_PAGE_OPTIONS_CHOICES)) : + CreateChoice(STR_IMPRESS_PRINT_UI_PAGE_OPTIONS_CHOICES_DRAW, SAL_N_ELEMENTS(STR_IMPRESS_PRINT_UI_PAGE_OPTIONS_CHOICES_DRAW)), + nDefaultChoice, + Sequence< sal_Bool >(), + aPageOptionsOpt + ) + ); + + vcl::PrinterOptionsHelper::UIControlOptions aBrochureOpt; + aBrochureOpt.maGroupHint = "LayoutPage" ; + AddDialogControl( vcl::PrinterOptionsHelper::setSubgroupControlOpt("pagesides", + SdResId(STR_IMPRESS_PRINT_UI_PAGE_SIDES), "", + aBrochureOpt ) ); + + // brochure printing + AddDialogControl( vcl::PrinterOptionsHelper::setBoolControlOpt("brochure", + SdResId(STR_IMPRESS_PRINT_UI_BROCHURE), + ".HelpID:vcl:PrintDialog:PrintProspect:CheckBox" , + "PrintProspect" , + mbImpress ? officecfg::Office::Impress::Print::Page::Booklet::get() : + officecfg::Office::Draw::Print::Page::Booklet::get(), + aBrochureOpt + ) + ); + + vcl::PrinterOptionsHelper::UIControlOptions + aIncludeOpt( "PrintProspect" , -1, false ); + aIncludeOpt.maGroupHint = "LayoutPage" ; + aHelpIds = { ".HelpID:vcl:PrintDialog:PrintProspectInclude:ListBox" }; + AddDialogControl( vcl::PrinterOptionsHelper::setChoiceListControlOpt( + "brochureinclude", + SdResId(STR_IMPRESS_PRINT_UI_BROCHURE_INCLUDE), + aHelpIds, + "PrintProspectInclude" , + CreateChoice(STR_IMPRESS_PRINT_UI_BROCHURE_INCLUDE_LIST, SAL_N_ELEMENTS(STR_IMPRESS_PRINT_UI_BROCHURE_INCLUDE_LIST)), + 0, + Sequence< sal_Bool >(), + aIncludeOpt + ) + ); + + // paper tray (on options page) + vcl::PrinterOptionsHelper::UIControlOptions aPaperTrayOpt; + aPaperTrayOpt.maGroupHint = "OptionsPageOptGroup" ; + AddDialogControl( vcl::PrinterOptionsHelper::setBoolControlOpt("printpaperfromsetup", + SdResId(STR_IMPRESS_PRINT_UI_PAPER_TRAY), + ".HelpID:vcl:PrintDialog:PrintPaperFromSetup:CheckBox" , + "PrintPaperFromSetup" , + false, + aPaperTrayOpt + ) + ); + // print range selection + vcl::PrinterOptionsHelper::UIControlOptions aPrintRangeOpt; + aPrintRangeOpt.mbInternalOnly = true; + aPrintRangeOpt.maGroupHint = "PrintRange" ; + AddDialogControl( vcl::PrinterOptionsHelper::setSubgroupControlOpt("printrange", + mbImpress ? SdResId(STR_IMPRESS_PRINT_UI_SLIDE_RANGE) : SdResId(STR_IMPRESS_PRINT_UI_PAGE_RANGE), + "", + aPrintRangeOpt ) + ); + + // check if there is a selection of slides + OUString aPageRange(OUString::number(mnCurPage + 1)); + int nPrintRange(0); + using sd::slidesorter::SlideSorterViewShell; + SlideSorterViewShell* const pSSViewSh(SlideSorterViewShell::GetSlideSorter(mrBase)); + if (pSSViewSh) + { + const std::shared_ptr<SlideSorterViewShell::PageSelection> pPageSelection(pSSViewSh->GetPageSelection()); + if (bool(pPageSelection) && pPageSelection->size() > 1) + { + OUStringBuffer aBuf; + // TODO: this could be improved by writing ranges instead of consecutive page + // numbers if appropriate. Do we have a helper function for that somewhere? + bool bFirst(true); + for (auto pPage: *pPageSelection) + { + if (bFirst) + bFirst = false; + else + aBuf.append(','); + aBuf.append(static_cast<sal_Int32>(pPage->GetPageNum() / 2 + 1)); + } + aPageRange = aBuf.makeStringAndClear(); + nPrintRange = 1; + } + } +/* + OUString aPrintRangeName( "PrintContent" ); + aHelpIds.realloc( 1 ); + aHelpIds[0] = ".HelpID:vcl:PrintDialog:PageContentType:ListBox"; + AddDialogControl( vcl::PrinterOptionsHelper::setChoiceListControlOpt( "printpagesbox", OUString(), + aHelpIds, aPrintRangeName, + mbImpress ? CreateChoice( STR_IMPRESS_PRINT_UI_PAGE_RANGE_CHOICE, SAL_N_ELEMENTS(STR_IMPRESS_PRINT_UI_PAGE_RANGE_CHOICE ) ) : + CreateChoice( STR_DRAW_PRINT_UI_PAGE_RANGE_CHOICE, SAL_N_ELEMENTS(STR_DRAW_PRINT_UI_PAGE_RANGE_CHOICE ) ), + nPrintRange ) ); +*/ + OUString aPrintRangeName( "PrintContent" ); + aHelpIds = { ".HelpID:vcl:PrintDialog:PrintContent:RadioButton:0", + ".HelpID:vcl:PrintDialog:PrintContent:RadioButton:1", + ".HelpID:vcl:PrintDialog:PrintContent:RadioButton:2" }; + aWidgetIds = { "rbAllPages", "rbRangePages", "rbRangeSelection" }; + + AddDialogControl( vcl::PrinterOptionsHelper::setChoiceRadiosControlOpt(aWidgetIds, OUString(), + aHelpIds, aPrintRangeName, + mbImpress ? CreateChoice(STR_IMPRESS_PRINT_UI_PAGE_RANGE_CHOICE, SAL_N_ELEMENTS(STR_IMPRESS_PRINT_UI_PAGE_RANGE_CHOICE)) : + CreateChoice(STR_DRAW_PRINT_UI_PAGE_RANGE_CHOICE, SAL_N_ELEMENTS(STR_DRAW_PRINT_UI_PAGE_RANGE_CHOICE)), + nPrintRange ) + ); + // create an Edit dependent on "Pages" selected + vcl::PrinterOptionsHelper::UIControlOptions aPageRangeOpt( aPrintRangeName, 1, true ); + AddDialogControl(vcl::PrinterOptionsHelper::setEditControlOpt("pagerange", "", + ".HelpID:vcl:PrintDialog:PageRange:Edit", "PageRange", + aPageRange, aPageRangeOpt)); + vcl::PrinterOptionsHelper::UIControlOptions aEvenOddOpt(aPrintRangeName, -1, true); + AddDialogControl(vcl::PrinterOptionsHelper::setChoiceListControlOpt("evenoddbox", "", + uno::Sequence<OUString>(), "EvenOdd", uno::Sequence<OUString>(), + 0, uno::Sequence<sal_Bool>(), aEvenOddOpt)); + } + + void AddDialogControl( const Any& i_rCtrl ) + { + beans::PropertyValue aVal; + aVal.Value = i_rCtrl; + maProperties.push_back( aVal ); + } + + static Sequence<OUString> CreateChoice(const TranslateId* pResourceId, size_t nCount) + { + Sequence<OUString> aChoices (nCount); + std::transform(pResourceId, pResourceId + nCount, aChoices.getArray(), + [](const auto& id) { return SdResId(id); }); + return aChoices; + } + + Sequence<OUString> GetSlidesPerPageSequence() + { + const Sequence<OUString> aChoice ( + CreateChoice(STR_IMPRESS_PRINT_UI_SLIDESPERPAGE_CHOICES, SAL_N_ELEMENTS(STR_IMPRESS_PRINT_UI_SLIDESPERPAGE_CHOICES))); + maSlidesPerPage.clear(); + maSlidesPerPage.push_back(0); // first is using the default + std::transform(std::next(aChoice.begin()), aChoice.end(), std::back_inserter(maSlidesPerPage), + [](const OUString& rChoice) -> sal_Int32 { return rChoice.toInt32(); }); + return aChoice; + } + }; + + /** The Prepare... methods of the DocumentRenderer::Implementation class + create a set of PrinterPage objects that contain all necessary + information to do the actual printing. There is one PrinterPage + object per printed page. Derived classes implement the actual, mode + specific printing. + + This and all derived classes support the asynchronous printing + process by not storing pointers to any data with lifetime shorter + than the PrinterPage objects, i.e. slides, shapes, (one of) the + outliner (of the document). + */ + class PrinterPage + { + public: + PrinterPage ( + const PageKind ePageKind, + const MapMode& rMapMode, + const bool bPrintMarkedOnly, + const OUString& rsPageString, + const Point& rPageStringOffset, + const DrawModeFlags nDrawMode, + const Orientation eOrientation, + const sal_uInt16 nPaperTray) + : mePageKind(ePageKind), + maMap(rMapMode), + mbPrintMarkedOnly(bPrintMarkedOnly), + msPageString(rsPageString), + maPageStringOffset(rPageStringOffset), + mnDrawMode(nDrawMode), + meOrientation(eOrientation), + mnPaperTray(nPaperTray) + { + } + + virtual ~PrinterPage() {} + + virtual void Print ( + Printer& rPrinter, + SdDrawDocument& rDocument, + ViewShell& rViewShell, + View* pView, + DrawView& rPrintView, + const SdrLayerIDSet& rVisibleLayers, + const SdrLayerIDSet& rPrintableLayers) const = 0; + + DrawModeFlags GetDrawMode() const { return mnDrawMode; } + Orientation GetOrientation() const { return meOrientation; } + sal_uInt16 GetPaperTray() const { return mnPaperTray; } + + protected: + const PageKind mePageKind; + const MapMode maMap; + const bool mbPrintMarkedOnly; + const OUString msPageString; + const Point maPageStringOffset; + const DrawModeFlags mnDrawMode; + const Orientation meOrientation; + const sal_uInt16 mnPaperTray; + }; + + /** The RegularPrinterPage is used for printing one regular slide (no + notes, handout, or outline) to one printer page. + */ + class RegularPrinterPage : public PrinterPage + { + public: + RegularPrinterPage ( + const sal_uInt16 nPageIndex, + const PageKind ePageKind, + const MapMode& rMapMode, + const bool bPrintMarkedOnly, + const OUString& rsPageString, + const Point& rPageStringOffset, + const DrawModeFlags nDrawMode, + const Orientation eOrientation, + const sal_uInt16 nPaperTray) + : PrinterPage(ePageKind, rMapMode, bPrintMarkedOnly, rsPageString, + rPageStringOffset, nDrawMode, eOrientation, nPaperTray), + mnPageIndex(nPageIndex) + { + } + + virtual void Print ( + Printer& rPrinter, + SdDrawDocument& rDocument, + ViewShell&, + View* pView, + DrawView& rPrintView, + const SdrLayerIDSet& rVisibleLayers, + const SdrLayerIDSet& rPrintableLayers) const override + { + SdPage* pPageToPrint = rDocument.GetSdPage(mnPageIndex, mePageKind); + rPrinter.SetMapMode(maMap); + PrintPage( + rPrinter, + rPrintView, + *pPageToPrint, + pView, + mbPrintMarkedOnly, + rVisibleLayers, + rPrintableLayers); + PrintMessage( + rPrinter, + msPageString, + maPageStringOffset); + } + + private: + const sal_uInt16 mnPageIndex; + }; + + /** Print one slide multiple times on a printer page so that the whole + printer page is covered. + */ + class TiledPrinterPage : public PrinterPage + { + public: + TiledPrinterPage ( + const sal_uInt16 nPageIndex, + const PageKind ePageKind, + const bool bPrintMarkedOnly, + const OUString& rsPageString, + const Point& rPageStringOffset, + const DrawModeFlags nDrawMode, + const Orientation eOrientation, + const sal_uInt16 nPaperTray) + : PrinterPage(ePageKind, MapMode(), bPrintMarkedOnly, rsPageString, + rPageStringOffset, nDrawMode, eOrientation, nPaperTray), + mnPageIndex(nPageIndex) + { + } + + virtual void Print ( + Printer& rPrinter, + SdDrawDocument& rDocument, + ViewShell&, + View* pView, + DrawView& rPrintView, + const SdrLayerIDSet& rVisibleLayers, + const SdrLayerIDSet& rPrintableLayers) const override + { + SdPage* pPageToPrint = rDocument.GetSdPage(mnPageIndex, mePageKind); + if (pPageToPrint==nullptr) + return; + MapMode aMap (rPrinter.GetMapMode()); + + const Size aPageSize (pPageToPrint->GetSize()); + const Size aPrintSize (rPrinter.GetOutputSize()); + + const sal_Int32 nPageWidth (aPageSize.Width() + mnGap + - pPageToPrint->GetLeftBorder() - pPageToPrint->GetRightBorder()); + const sal_Int32 nPageHeight (aPageSize.Height() + mnGap + - pPageToPrint->GetUpperBorder() - pPageToPrint->GetLowerBorder()); + if (nPageWidth<=0 || nPageHeight<=0) + return; + + // Print at least two rows and columns. More if the document + // page fits completely onto the printer page. + const sal_Int32 nColumnCount (std::max(sal_Int32(2), + sal_Int32(aPrintSize.Width() / nPageWidth))); + const sal_Int32 nRowCount (std::max(sal_Int32(2), + sal_Int32(aPrintSize.Height() / nPageHeight))); + for (sal_Int32 nRow=0; nRow<nRowCount; ++nRow) + for (sal_Int32 nColumn=0; nColumn<nColumnCount; ++nColumn) + { + aMap.SetOrigin(Point(nColumn*nPageWidth,nRow*nPageHeight)); + rPrinter.SetMapMode(aMap); + PrintPage( + rPrinter, + rPrintView, + *pPageToPrint, + pView, + mbPrintMarkedOnly, + rVisibleLayers, + rPrintableLayers); + } + + PrintMessage( + rPrinter, + msPageString, + maPageStringOffset); + } + + private: + const sal_uInt16 mnPageIndex; + static const sal_Int32 mnGap = 500; + }; + + /** Print two slides to one printer page so that the resulting pages + form a booklet. + */ + class BookletPrinterPage : public PrinterPage + { + public: + BookletPrinterPage ( + const sal_uInt16 nFirstPageIndex, + const sal_uInt16 nSecondPageIndex, + const Point& rFirstOffset, + const Point& rSecondOffset, + const PageKind ePageKind, + const MapMode& rMapMode, + const bool bPrintMarkedOnly, + const DrawModeFlags nDrawMode, + const Orientation eOrientation, + const sal_uInt16 nPaperTray) + : PrinterPage(ePageKind, rMapMode, bPrintMarkedOnly, "", + Point(), nDrawMode, eOrientation, nPaperTray), + mnFirstPageIndex(nFirstPageIndex), + mnSecondPageIndex(nSecondPageIndex), + maFirstOffset(rFirstOffset), + maSecondOffset(rSecondOffset) + { + } + + virtual void Print ( + Printer& rPrinter, + SdDrawDocument& rDocument, + ViewShell&, + View* pView, + DrawView& rPrintView, + const SdrLayerIDSet& rVisibleLayers, + const SdrLayerIDSet& rPrintableLayers) const override + { + MapMode aMap (maMap); + SdPage* pPageToPrint = rDocument.GetSdPage(mnFirstPageIndex, mePageKind); + if (pPageToPrint) + { + aMap.SetOrigin(maFirstOffset); + rPrinter.SetMapMode(aMap); + PrintPage( + rPrinter, + rPrintView, + *pPageToPrint, + pView, + mbPrintMarkedOnly, + rVisibleLayers, + rPrintableLayers); + } + + pPageToPrint = rDocument.GetSdPage(mnSecondPageIndex, mePageKind); + if( !pPageToPrint ) + return; + + aMap.SetOrigin(maSecondOffset); + rPrinter.SetMapMode(aMap); + PrintPage( + rPrinter, + rPrintView, + *pPageToPrint, + pView, + mbPrintMarkedOnly, + rVisibleLayers, + rPrintableLayers); + } + + private: + const sal_uInt16 mnFirstPageIndex; + const sal_uInt16 mnSecondPageIndex; + const Point maFirstOffset; + const Point maSecondOffset; + }; + + /** One handout page displays one to nine slides. + */ + class HandoutPrinterPage : public PrinterPage + { + public: + HandoutPrinterPage ( + const sal_uInt16 nHandoutPageIndex, + std::vector<sal_uInt16>&& rPageIndices, + const MapMode& rMapMode, + const OUString& rsPageString, + const Point& rPageStringOffset, + const DrawModeFlags nDrawMode, + const Orientation eOrientation, + const sal_uInt16 nPaperTray) + : PrinterPage(PageKind::Handout, rMapMode, false, rsPageString, + rPageStringOffset, nDrawMode, eOrientation, nPaperTray), + mnHandoutPageIndex(nHandoutPageIndex), + maPageIndices(std::move(rPageIndices)) + { + } + + virtual void Print ( + Printer& rPrinter, + SdDrawDocument& rDocument, + ViewShell& rViewShell, + View* pView, + DrawView& rPrintView, + const SdrLayerIDSet& rVisibleLayers, + const SdrLayerIDSet& rPrintableLayers) const override + { + SdPage& rHandoutPage (*rDocument.GetSdPage(0, PageKind::Handout)); + + Reference< css::beans::XPropertySet > xHandoutPage( rHandoutPage.getUnoPage(), UNO_QUERY ); + static const OUStringLiteral sPageNumber( u"Number" ); + + // Collect the page objects of the handout master. + std::vector<SdrPageObj*> aHandoutPageObjects; + SdrObjListIter aShapeIter (&rHandoutPage); + while (aShapeIter.IsMore()) + { + SdrPageObj* pPageObj = dynamic_cast<SdrPageObj*>(aShapeIter.Next()); + if (pPageObj) + aHandoutPageObjects.push_back(pPageObj); + } + if (aHandoutPageObjects.empty()) + return; + + // Connect page objects with pages. + std::vector<SdrPageObj*>::iterator aPageObjIter (aHandoutPageObjects.begin()); + for (std::vector<sal_uInt16>::const_iterator + iPageIndex(maPageIndices.begin()), + iEnd(maPageIndices.end()); + iPageIndex!=iEnd && aPageObjIter!=aHandoutPageObjects.end(); + ++iPageIndex) + { + // Check if the page still exists. + if (*iPageIndex >= rDocument.GetSdPageCount(PageKind::Standard)) + continue; + + SdrPageObj* pPageObj = *aPageObjIter++; + pPageObj->SetReferencedPage(rDocument.GetSdPage(*iPageIndex, PageKind::Standard)); + } + + // if there are more page objects than pages left, set the rest to invisible + int nHangoverCount = 0; + while (aPageObjIter != aHandoutPageObjects.end()) + { + (*aPageObjIter++)->SetReferencedPage(nullptr); + nHangoverCount++; + } + + // Hide outlines for objects that have pages attached. + if (nHangoverCount > 0) + { + int nSkip = aHandoutPageObjects.size() - nHangoverCount; + aShapeIter.Reset(); + while (aShapeIter.IsMore()) + { + SdrPathObj* pPathObj = dynamic_cast<SdrPathObj*>(aShapeIter.Next()); + if (pPathObj) + { + if (nSkip > 0) + --nSkip; + else + pPathObj->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE)); + } + } + } + + if( xHandoutPage.is() ) try + { + xHandoutPage->setPropertyValue( sPageNumber, Any( static_cast<sal_Int16>(mnHandoutPageIndex) ) ); + } + catch( Exception& ) + { + } + rViewShell.SetPrintedHandoutPageNum( mnHandoutPageIndex + 1 ); + + rPrinter.SetMapMode(maMap); + + PrintPage( + rPrinter, + rPrintView, + rHandoutPage, + pView, + false, + rVisibleLayers, + rPrintableLayers); + PrintMessage( + rPrinter, + msPageString, + maPageStringOffset); + + if( xHandoutPage.is() ) try + { + xHandoutPage->setPropertyValue( sPageNumber, Any( static_cast<sal_Int16>(0) ) ); + } + catch( Exception& ) + { + } + rViewShell.SetPrintedHandoutPageNum(1); + + // Restore outlines. + if (nHangoverCount > 0) + { + aShapeIter.Reset(); + while (aShapeIter.IsMore()) + { + SdrPathObj* pPathObj = dynamic_cast<SdrPathObj*>(aShapeIter.Next()); + if (pPathObj != nullptr) + pPathObj->SetMergedItem(XLineStyleItem(drawing::LineStyle_SOLID)); + } + } + + } + + private: + const sal_uInt16 mnHandoutPageIndex; + const std::vector<sal_uInt16> maPageIndices; + }; + + /** The outline information (title, subtitle, outline objects) of the + document. There is no fixed mapping of slides to printer pages. + */ + class OutlinerPrinterPage : public PrinterPage + { + public: + OutlinerPrinterPage ( + std::optional<OutlinerParaObject> pParaObject, + const MapMode& rMapMode, + const OUString& rsPageString, + const Point& rPageStringOffset, + const DrawModeFlags nDrawMode, + const Orientation eOrientation, + const sal_uInt16 nPaperTray) + : PrinterPage(PageKind::Handout, rMapMode, false, rsPageString, + rPageStringOffset, nDrawMode, eOrientation, nPaperTray), + mpParaObject(std::move(pParaObject)) + { + } + + virtual void Print ( + Printer& rPrinter, + SdDrawDocument& rDocument, + ViewShell&, + View*, + DrawView&, + const SdrLayerIDSet&, + const SdrLayerIDSet&) const override + { + // Set up the printer. + rPrinter.SetMapMode(maMap); + + // Get and set up the outliner. + const ::tools::Rectangle aOutRect (rPrinter.GetPageOffset(), rPrinter.GetOutputSize()); + Outliner* pOutliner = rDocument.GetInternalOutliner(); + const OutlinerMode nSavedOutlMode (pOutliner->GetOutlinerMode()); + const bool bSavedUpdateMode (pOutliner->IsUpdateLayout()); + const Size aSavedPaperSize (pOutliner->GetPaperSize()); + + pOutliner->Init(OutlinerMode::OutlineView); + pOutliner->SetPaperSize(aOutRect.GetSize()); + pOutliner->SetUpdateLayout(true); + pOutliner->Clear(); + pOutliner->SetText(*mpParaObject); + + pOutliner->Draw(rPrinter, aOutRect); + + PrintMessage( + rPrinter, + msPageString, + maPageStringOffset); + + // Restore outliner and printer. + pOutliner->Clear(); + pOutliner->SetUpdateLayout(bSavedUpdateMode); + pOutliner->SetPaperSize(aSavedPaperSize); + pOutliner->Init(nSavedOutlMode); + } + + private: + std::optional<OutlinerParaObject> mpParaObject; + }; +} + +//===== DocumentRenderer::Implementation ====================================== + +class DocumentRenderer::Implementation + : public SfxListener, + public vcl::PrinterOptionsHelper +{ +public: + explicit Implementation (ViewShellBase& rBase) + : mxObjectShell(rBase.GetDocShell()) + , mrBase(rBase) + , mbIsDisposed(false) + , mpPrinter(nullptr) + , mbHasOrientationWarningBeenShown(false) + { + DialogCreator aCreator( mrBase, mrBase.GetDocShell()->GetDocumentType() == DocumentType::Impress, GetCurrentPageIndex() ); + m_aUIProperties = aCreator.GetDialogControls(); + maSlidesPerPage = aCreator.GetSlidesPerPage(); + + StartListening(mrBase); + } + + virtual ~Implementation() override + { + EndListening(mrBase); + } + + virtual void Notify (SfxBroadcaster& rBroadcaster, const SfxHint& rHint) override + { + if (&rBroadcaster != &static_cast<SfxBroadcaster&>(mrBase)) + return; + + if (rHint.GetId() == SfxHintId::Dying) + { + mbIsDisposed = true; + } + } + + /** Process the sequence of properties given to one of the XRenderable + methods. + */ + void ProcessProperties (const css::uno::Sequence<css::beans::PropertyValue >& rOptions) + { + OSL_ASSERT(!mbIsDisposed); + if (mbIsDisposed) + return; + + bool bIsValueChanged = processProperties( rOptions ); + bool bIsPaperChanged = false; + + // The RenderDevice property is handled specially: its value is + // stored in mpPrinter instead of being retrieved on demand. + Any aDev( getValue( "RenderDevice" ) ); + Reference<awt::XDevice> xRenderDevice; + + if (aDev >>= xRenderDevice) + { + VCLXDevice* pDevice = comphelper::getFromUnoTunnel<VCLXDevice>(xRenderDevice); + VclPtr< OutputDevice > pOut = pDevice ? pDevice->GetOutputDevice() + : VclPtr< OutputDevice >(); + mpPrinter = dynamic_cast<Printer*>(pOut.get()); + Size aPageSizePixel = mpPrinter ? mpPrinter->GetPaperSizePixel() : Size(); + if( aPageSizePixel != maPrinterPageSizePixel ) + { + bIsPaperChanged = true; + maPrinterPageSizePixel = aPageSizePixel; + } + } + + if (bIsValueChanged && ! mpOptions ) + mpOptions.reset(new PrintOptions(*this, std::vector(maSlidesPerPage))); + if( bIsValueChanged || bIsPaperChanged ) + PreparePages(); + } + + /** Return the number of pages that are to be printed. + */ + sal_Int32 GetPrintPageCount() const + { + OSL_ASSERT(!mbIsDisposed); + if (mbIsDisposed) + return 0; + else + return maPrinterPages.size(); + } + + /** Return a sequence of properties that can be returned by the + XRenderable::getRenderer() method. + */ + css::uno::Sequence<css::beans::PropertyValue> GetProperties () const + { + css::uno::Sequence<css::beans::PropertyValue> aProperties{ + comphelper::makePropertyValue("ExtraPrintUIOptions", + comphelper::containerToSequence(m_aUIProperties)), + comphelper::makePropertyValue("PageSize", maPrintSize), + // FIXME: is this always true ? + comphelper::makePropertyValue("PageIncludesNonprintableArea", true) + }; + + return aProperties; + } + + /** Print one of the prepared pages. + */ + void PrintPage (const sal_Int32 nIndex) + { + OSL_ASSERT(!mbIsDisposed); + if (mbIsDisposed) + return; + + Printer& rPrinter (*mpPrinter); + + std::shared_ptr<ViewShell> pViewShell (mrBase.GetMainViewShell()); + if ( ! pViewShell) + return; + + SdDrawDocument* pDocument = pViewShell->GetDoc(); + OSL_ASSERT(pDocument!=nullptr); + + std::shared_ptr<DrawViewShell> pDrawViewShell( + std::dynamic_pointer_cast<DrawViewShell>(mrBase.GetMainViewShell())); + + if (!mpPrintView) + mpPrintView.reset(new DrawView(mrBase.GetDocShell(), &rPrinter, nullptr)); + + if (nIndex<0 || sal::static_int_cast<sal_uInt32>(nIndex)>=maPrinterPages.size()) + return; + + const std::shared_ptr<PrinterPage> pPage (maPrinterPages[nIndex]); + OSL_ASSERT(pPage); + if ( ! pPage) + return; + + const Orientation eSavedOrientation (rPrinter.GetOrientation()); + const DrawModeFlags nSavedDrawMode (rPrinter.GetDrawMode()); + const MapMode aSavedMapMode (rPrinter.GetMapMode()); + const sal_uInt16 nSavedPaperBin (rPrinter.GetPaperBin()); + + // Set page orientation. + if ( ! rPrinter.SetOrientation(pPage->GetOrientation())) + { + if ( ! mbHasOrientationWarningBeenShown + && mpOptions->IsWarningOrientation()) + { + mbHasOrientationWarningBeenShown = true; + // Show warning that the orientation could not be set. + std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog( + pViewShell->GetFrameWeld(), VclMessageType::Warning, VclButtonsType::OkCancel, + SdResId(STR_WARN_PRINTFORMAT_FAILURE))); + xWarn->set_default_response(RET_CANCEL); + if (xWarn->run() != RET_OK) + return; + } + } + + // Set the draw mode. + rPrinter.SetDrawMode(pPage->GetDrawMode()); + + // Set paper tray. + rPrinter.SetPaperBin(pPage->GetPaperTray()); + + // Print the actual page. + pPage->Print( + rPrinter, + *pDocument, + *pViewShell, + pDrawViewShell ? pDrawViewShell->GetView() : nullptr, + *mpPrintView, + pViewShell->GetFrameView()->GetVisibleLayers(), + pViewShell->GetFrameView()->GetPrintableLayers()); + + rPrinter.SetOrientation(eSavedOrientation); + rPrinter.SetDrawMode(nSavedDrawMode); + rPrinter.SetMapMode(aSavedMapMode); + rPrinter.SetPaperBin(nSavedPaperBin); + } + +private: + // rhbz#657394: keep the document alive: prevents crash when + SfxObjectShellRef mxObjectShell; // destroying mpPrintView + ViewShellBase& mrBase; + bool mbIsDisposed; + VclPtr<Printer> mpPrinter; + Size maPrinterPageSizePixel; + std::unique_ptr<PrintOptions> mpOptions; + std::vector< std::shared_ptr< ::sd::PrinterPage> > maPrinterPages; + std::unique_ptr<DrawView> mpPrintView; + bool mbHasOrientationWarningBeenShown; + std::vector<sal_Int32> maSlidesPerPage; + awt::Size maPrintSize; + + sal_Int32 GetCurrentPageIndex() const + { + const ViewShell *pShell = mrBase.GetMainViewShell().get(); + const SdPage *pCurrentPage = pShell ? pShell->getCurrentPage() : nullptr; + return pCurrentPage ? (pCurrentPage->GetPageNum()-1)/2 : -1; + } + + /** Determine and set the paper orientation. + */ + void SetupPaperOrientation ( + const PageKind ePageKind, + PrintInfo& rInfo) + { + SdDrawDocument* pDocument = mrBase.GetMainViewShell()->GetDoc(); + rInfo.meOrientation = Orientation::Portrait; + + if( ! mpOptions->IsBooklet()) + { + rInfo.meOrientation = pDocument->GetSdPage(0, ePageKind)->GetOrientation(); + } + else if (rInfo.maPageSize.Width() < rInfo.maPageSize.Height()) + rInfo.meOrientation = Orientation::Landscape; + + // Draw and Notes should usually abide by their specified paper size + Size aPaperSize; + if (!mpOptions->IsPrinterPreferred(pDocument->GetDocumentType())) + { + aPaperSize.setWidth(rInfo.maPageSize.Width()); + aPaperSize.setHeight(rInfo.maPageSize.Height()); + } + else + { + aPaperSize.setWidth(rInfo.mpPrinter->GetPaperSize().Width()); + aPaperSize.setHeight(rInfo.mpPrinter->GetPaperSize().Height()); + } + + maPrintSize = awt::Size(aPaperSize.Width(), aPaperSize.Height()); + + if (mpOptions->IsPrinterPreferred(pDocument->GetDocumentType())) + { + if( (rInfo.meOrientation == Orientation::Landscape && + (aPaperSize.Width() < aPaperSize.Height())) + || + (rInfo.meOrientation == Orientation::Portrait && + (aPaperSize.Width() > aPaperSize.Height())) + ) + { + maPrintSize = awt::Size(aPaperSize.Height(), aPaperSize.Width()); + } + } + } + + /** Top most method for preparing printer pages. In this and the other + Prepare... methods the various special cases are detected and + handled. + For every page that is to be printed (that may contain several + slides) one PrinterPage object is created and inserted into + maPrinterPages. + */ + void PreparePages() + { + mpPrintView.reset(); + maPrinterPages.clear(); + mbHasOrientationWarningBeenShown = false; + + ViewShell* pShell = mrBase.GetMainViewShell().get(); + + PrintInfo aInfo (mpPrinter, mpOptions->IsPrintMarkedOnly()); + + if (aInfo.mpPrinter==nullptr || pShell==nullptr) + return; + + MapMode aMap (aInfo.mpPrinter->GetMapMode()); + aMap.SetMapUnit(MapUnit::Map100thMM); + aInfo.maMap = aMap; + mpPrinter->SetMapMode(aMap); + + ::Outliner& rOutliner = mrBase.GetDocument()->GetDrawOutliner(); + const EEControlBits nSavedControlWord (rOutliner.GetControlWord()); + EEControlBits nCntrl = nSavedControlWord; + nCntrl &= ~EEControlBits::MARKFIELDS; + nCntrl &= ~EEControlBits::ONLINESPELLING; + rOutliner.SetControlWord( nCntrl ); + + // When in outline view then apply all pending changes to the model. + if( auto pOutlineViewShell = dynamic_cast< OutlineViewShell *>( pShell ) ) + pOutlineViewShell->PrepareClose (false); + + // Collect some frequently used data. + if (mpOptions->IsDate()) + { + aInfo.msTimeDate += GetSdrGlobalData().GetLocaleData()->getDate( Date( Date::SYSTEM ) ); + aInfo.msTimeDate += " "; + } + + if (mpOptions->IsTime()) + aInfo.msTimeDate += GetSdrGlobalData().GetLocaleData()->getTime( ::tools::Time( ::tools::Time::SYSTEM ), false ); + + // Draw and Notes should usually use specified paper size when printing + if (!mpOptions->IsPrinterPreferred(mrBase.GetDocShell()->GetDocumentType())) + { + aInfo.maPrintSize = mrBase.GetDocument()->GetSdPage(0, PageKind::Standard)->GetSize(); + maPrintSize = awt::Size(aInfo.maPrintSize.Width(), + aInfo.maPrintSize.Height()); + } + else + { + aInfo.maPrintSize = aInfo.mpPrinter->GetOutputSize(); + maPrintSize = awt::Size( + aInfo.mpPrinter->GetPaperSize().Width(), + aInfo.mpPrinter->GetPaperSize().Height()); + } + + switch (mpOptions->GetOutputQuality()) + { + case 1: // Grayscale + aInfo.mnDrawMode = DrawModeFlags::GrayLine | DrawModeFlags::GrayFill + | DrawModeFlags::GrayText | DrawModeFlags::GrayBitmap + | DrawModeFlags::GrayGradient; + break; + + case 2: // Black & White + aInfo.mnDrawMode = DrawModeFlags::BlackLine | DrawModeFlags::WhiteFill + | DrawModeFlags::BlackText | DrawModeFlags::GrayBitmap + | DrawModeFlags::WhiteGradient; + break; + + default: + aInfo.mnDrawMode = DrawModeFlags::Default; + } + + if (mpOptions->IsDraw()) + PrepareStdOrNotes(PageKind::Standard, aInfo); + if (mpOptions->IsNotes()) + PrepareStdOrNotes(PageKind::Notes, aInfo); + if (mpOptions->IsHandout()) + { + InitHandoutTemplate(); + PrepareHandout(aInfo); + } + if (mpOptions->IsOutline()) + PrepareOutline(aInfo); + + rOutliner.SetControlWord(nSavedControlWord); + } + + /** Create the page objects of the handout template. When the actual + printing takes place then the page objects are assigned different + sets of slides for each printed page (see HandoutPrinterPage::Print). + */ + void InitHandoutTemplate() + { + const sal_Int32 nSlidesPerHandout (mpOptions->GetHandoutPageCount()); + const bool bHandoutHorizontal (mpOptions->IsHandoutHorizontal()); + + AutoLayout eLayout = AUTOLAYOUT_HANDOUT6; + switch (nSlidesPerHandout) + { + case 0: eLayout = AUTOLAYOUT_NONE; break; // AUTOLAYOUT_HANDOUT1; break; + case 1: eLayout = AUTOLAYOUT_HANDOUT1; break; + case 2: eLayout = AUTOLAYOUT_HANDOUT2; break; + case 3: eLayout = AUTOLAYOUT_HANDOUT3; break; + case 4: eLayout = AUTOLAYOUT_HANDOUT4; break; + default: + case 6: eLayout = AUTOLAYOUT_HANDOUT6; break; + case 9: eLayout = AUTOLAYOUT_HANDOUT9; break; + } + + if( !mrBase.GetDocument() ) + return; + + SdDrawDocument& rModel = *mrBase.GetDocument(); + + // first, prepare handout page (not handout master) + + SdPage* pHandout = rModel.GetSdPage(0, PageKind::Handout); + if( !pHandout ) + return; + + // delete all previous shapes from handout page + while( pHandout->GetObjCount() ) + { + SdrObject* pObj = pHandout->NbcRemoveObject(0); + if( pObj ) + SdrObject::Free( pObj ); + } + + const bool bDrawLines (eLayout == AUTOLAYOUT_HANDOUT3); + + std::vector< ::tools::Rectangle > aAreas; + SdPage::CalculateHandoutAreas( rModel, eLayout, bHandoutHorizontal, aAreas ); + + std::vector< ::tools::Rectangle >::iterator iter( aAreas.begin() ); + while( iter != aAreas.end() ) + { + pHandout->NbcInsertObject( + new SdrPageObj( + rModel, + (*iter++))); + + if( bDrawLines && (iter != aAreas.end()) ) + { + ::tools::Rectangle aRect( *iter++ ); + + basegfx::B2DPolygon aPoly; + aPoly.insert(0, basegfx::B2DPoint( aRect.Left(), aRect.Top() ) ); + aPoly.insert(1, basegfx::B2DPoint( aRect.Right(), aRect.Top() ) ); + + basegfx::B2DHomMatrix aMatrix; + aMatrix.translate( 0.0, static_cast< double >( aRect.GetHeight() / 7 ) ); + + basegfx::B2DPolyPolygon aPathPoly; + for( sal_uInt16 nLine = 0; nLine < 7; nLine++ ) + { + aPoly.transform( aMatrix ); + aPathPoly.append( aPoly ); + } + + SdrPathObj* pPathObj = new SdrPathObj( + rModel, + SdrObjKind::PathLine, + aPathPoly); + pPathObj->SetMergedItem(XLineStyleItem(drawing::LineStyle_SOLID)); + pPathObj->SetMergedItem(XLineColorItem(OUString(), COL_BLACK)); + + pHandout->NbcInsertObject( pPathObj ); + } + } + } + + /** Detect whether the specified slide is to be printed. + @return + When the slide is not to be printed then <NULL/> is returned. + Otherwise a pointer to the slide is returned. + */ + SdPage* GetFilteredPage ( + const sal_Int32 nPageIndex, + const PageKind ePageKind) const + { + OSL_ASSERT(mrBase.GetDocument() != nullptr); + OSL_ASSERT(nPageIndex>=0); + SdPage* pPage = mrBase.GetDocument()->GetSdPage( + sal::static_int_cast<sal_uInt16>(nPageIndex), + ePageKind); + if (pPage == nullptr) + return nullptr; + if ( ! pPage->IsExcluded() || mpOptions->IsPrintExcluded()) + return pPage; + else + return nullptr; + } + + /** Prepare the outline of the document for printing. There is no fixed + number of slides whose outline data is put onto one printer page. + If the current printer page has enough room for the outline of the + current slide then that is added. Otherwise a new printer page is + started. + */ + void PrepareOutline (PrintInfo const & rInfo) + { + MapMode aMap (rInfo.maMap); + Point aPageOfs (rInfo.mpPrinter->GetPageOffset() ); + aMap.SetScaleX(Fraction(1,2)); + aMap.SetScaleY(Fraction(1,2)); + mpPrinter->SetMapMode(aMap); + + ::tools::Rectangle aOutRect(aPageOfs, rInfo.mpPrinter->GetOutputSize()); + if( aOutRect.GetWidth() > aOutRect.GetHeight() ) + { + Size aPaperSize( rInfo.mpPrinter->PixelToLogic( rInfo.mpPrinter->GetPaperSizePixel(), MapMode( MapUnit::Map100thMM ) ) ); + maPrintSize.Width = aPaperSize.Height(); + maPrintSize.Height = aPaperSize.Width(); + const auto nRotatedWidth = aOutRect.GetHeight(); + const auto nRotatedHeight = aOutRect.GetWidth(); + const auto nRotatedX = aPageOfs.Y(); + const auto nRotatedY = aPageOfs.X(); + aOutRect = ::tools::Rectangle(Point( nRotatedX, nRotatedY), + Size(nRotatedWidth, nRotatedHeight)); + } + + Outliner* pOutliner = mrBase.GetDocument()->GetInternalOutliner(); + pOutliner->Init(OutlinerMode::OutlineView); + const OutlinerMode nSavedOutlMode (pOutliner->GetOutlinerMode()); + const bool bSavedUpdateMode (pOutliner->IsUpdateLayout()); + const Size aSavedPaperSize (pOutliner->GetPaperSize()); + const MapMode aSavedMapMode (pOutliner->GetRefMapMode()); + pOutliner->SetPaperSize(aOutRect.GetSize()); + pOutliner->SetUpdateLayout(true); + + ::tools::Long nPageH = aOutRect.GetHeight(); + + std::vector< sal_Int32 > aPages; + sal_Int32 nPageCount = mrBase.GetDocument()->GetSdPageCount(PageKind::Standard); + StringRangeEnumerator::getRangesFromString( + mpOptions->GetPrinterSelection(nPageCount, GetCurrentPageIndex()), + aPages, 0, nPageCount-1); + + for (size_t nIndex = 0, nCount = aPages.size(); nIndex < nCount;) + { + pOutliner->Clear(); + + Paragraph* pPara = nullptr; + ::tools::Long nH (0); + while (nH < nPageH && nIndex<nCount) + { + SdPage* pPage = GetFilteredPage(aPages[nIndex], PageKind::Standard); + ++nIndex; + if (pPage == nullptr) + continue; + + SdrTextObj* pTextObj = nullptr; + size_t nObj (0); + + while (pTextObj==nullptr && nObj < pPage->GetObjCount()) + { + SdrObject* pObj = pPage->GetObj(nObj++); + if (pObj->GetObjInventor() == SdrInventor::Default + && pObj->GetObjIdentifier() == SdrObjKind::TitleText) + { + pTextObj = dynamic_cast<SdrTextObj*>(pObj); + } + } + + pPara = pOutliner->GetParagraph(pOutliner->GetParagraphCount() - 1); + + if (pTextObj!=nullptr + && !pTextObj->IsEmptyPresObj() + && pTextObj->GetOutlinerParaObject()) + { + pOutliner->AddText(*(pTextObj->GetOutlinerParaObject())); + } + else + pOutliner->Insert(OUString()); + + pTextObj = nullptr; + nObj = 0; + + while (pTextObj==nullptr && nObj<pPage->GetObjCount()) + { + SdrObject* pObj = pPage->GetObj(nObj++); + if (pObj->GetObjInventor() == SdrInventor::Default + && pObj->GetObjIdentifier() == SdrObjKind::OutlineText) + { + pTextObj = dynamic_cast<SdrTextObj*>(pObj); + } + } + + bool bSubTitle (false); + if (!pTextObj) + { + bSubTitle = true; + pTextObj = dynamic_cast<SdrTextObj*>(pPage->GetPresObj(PresObjKind::Text)); // is there a subtitle? + } + + sal_Int32 nParaCount1 = pOutliner->GetParagraphCount(); + + if (pTextObj!=nullptr + && !pTextObj->IsEmptyPresObj() + && pTextObj->GetOutlinerParaObject()) + { + pOutliner->AddText(*(pTextObj->GetOutlinerParaObject())); + } + + if (bSubTitle ) + { + const sal_Int32 nParaCount2 (pOutliner->GetParagraphCount()); + for (sal_Int32 nPara=nParaCount1; nPara<nParaCount2; ++nPara) + { + Paragraph* pP = pOutliner->GetParagraph(nPara); + if (pP!=nullptr && pOutliner->GetDepth(nPara) > 0) + pOutliner->SetDepth(pP, 0); + } + } + + nH = pOutliner->GetTextHeight(); + } + + // Remove the last paragraph when that does not fit completely on + // the current page. + if (nH > nPageH && pPara!=nullptr) + { + sal_Int32 nCnt = pOutliner->GetAbsPos( + pOutliner->GetParagraph( pOutliner->GetParagraphCount() - 1 ) ); + sal_Int32 nParaPos = pOutliner->GetAbsPos( pPara ); + nCnt -= nParaPos; + pPara = pOutliner->GetParagraph( ++nParaPos ); + if ( nCnt && pPara ) + { + pOutliner->Remove(pPara, nCnt); + --nIndex; + } + } + + if ( CheckForFrontBackPages( nIndex ) ) + { + maPrinterPages.push_back( + std::make_shared<OutlinerPrinterPage>( + pOutliner->CreateParaObject(), + aMap, + rInfo.msTimeDate, + aPageOfs, + rInfo.mnDrawMode, + rInfo.meOrientation, + rInfo.mpPrinter->GetPaperBin())); + } + } + + pOutliner->SetRefMapMode(aSavedMapMode); + pOutliner->SetUpdateLayout(bSavedUpdateMode); + pOutliner->SetPaperSize(aSavedPaperSize); + pOutliner->Init(nSavedOutlMode); + } + + /** Prepare handout pages for slides that are to be printed. + */ + void PrepareHandout (PrintInfo& rInfo) + { + SdDrawDocument* pDocument = mrBase.GetDocument(); + OSL_ASSERT(pDocument != nullptr); + SdPage& rHandoutPage (*pDocument->GetSdPage(0, PageKind::Handout)); + + const bool bScalePage (mpOptions->IsPageSize()); + + sal_uInt16 nPaperBin; + if ( ! mpOptions->IsPaperBin()) + nPaperBin = rHandoutPage.GetPaperBin(); + else + nPaperBin = rInfo.mpPrinter->GetPaperBin(); + + // Change orientation? + SdPage& rMaster (dynamic_cast<SdPage&>(rHandoutPage.TRG_GetMasterPage())); + rInfo.meOrientation = rMaster.GetOrientation(); + + const Size aPaperSize (rInfo.mpPrinter->GetPaperSize()); + if( (rInfo.meOrientation == Orientation::Landscape && + (aPaperSize.Width() < aPaperSize.Height())) + || + (rInfo.meOrientation == Orientation::Portrait && + (aPaperSize.Width() > aPaperSize.Height())) + ) + { + maPrintSize = awt::Size(aPaperSize.Height(), aPaperSize.Width()); + } + else + { + maPrintSize = awt::Size(aPaperSize.Width(), aPaperSize.Height()); + } + + MapMode aMap (rInfo.maMap); + const Point aPageOfs (rInfo.mpPrinter->GetPageOffset()); + + if ( bScalePage ) + { + const Size aPageSize (rHandoutPage.GetSize()); + const Size aPrintSize (rInfo.mpPrinter->GetOutputSize()); + + const double fHorz = static_cast<double>(aPrintSize.Width()) / aPageSize.Width(); + const double fVert = static_cast<double>(aPrintSize.Height()) / aPageSize.Height(); + + Fraction aFract; + if ( fHorz < fVert ) + aFract = Fraction(aPrintSize.Width(), aPageSize.Width()); + else + aFract = Fraction(aPrintSize.Height(), aPageSize.Height()); + + aMap.SetScaleX(aFract); + aMap.SetScaleY(aFract); + aMap.SetOrigin(Point()); + } + + std::shared_ptr<ViewShell> pViewShell (mrBase.GetMainViewShell()); + pViewShell->WriteFrameViewData(); + + // Count page shapes. + sal_uInt32 nShapeCount (0); + SdrObjListIter aShapeIter (&rHandoutPage); + while (aShapeIter.IsMore()) + { + SdrPageObj* pPageObj = dynamic_cast<SdrPageObj*>(aShapeIter.Next()); + if (pPageObj) + ++nShapeCount; + } + + const sal_uInt16 nPageCount = mrBase.GetDocument()->GetSdPageCount(PageKind::Standard); + const sal_uInt16 nHandoutPageCount = nShapeCount ? (nPageCount + nShapeCount - 1) / nShapeCount : 0; + pViewShell->SetPrintedHandoutPageCount( nHandoutPageCount ); + mrBase.GetDocument()->setHandoutPageCount( nHandoutPageCount ); + + // Distribute pages to handout pages. + StringRangeEnumerator aRangeEnum( + mpOptions->GetPrinterSelection(nPageCount, GetCurrentPageIndex()), + 0, nPageCount-1); + std::vector<sal_uInt16> aPageIndices; + sal_uInt16 nPrinterPageIndex = 0; + StringRangeEnumerator::Iterator it = aRangeEnum.begin(), itEnd = aRangeEnum.end(); + bool bLastLoop = (it == itEnd); + while (!bLastLoop) + { + sal_Int32 nPageIndex = *it; + ++it; + bLastLoop = (it == itEnd); + + if (GetFilteredPage(nPageIndex, PageKind::Standard)) + aPageIndices.push_back(nPageIndex); + else if (!bLastLoop) + continue; + + // Create a printer page when we have found one page for each + // placeholder or when this is the last (and special) loop. + if ( !aPageIndices.empty() && CheckForFrontBackPages( nPageIndex ) + && (aPageIndices.size() == nShapeCount || bLastLoop) ) + { + maPrinterPages.push_back( + std::make_shared<HandoutPrinterPage>( + nPrinterPageIndex++, + std::move(aPageIndices), + aMap, + rInfo.msTimeDate, + aPageOfs, + rInfo.mnDrawMode, + rInfo.meOrientation, + nPaperBin)); + aPageIndices.clear(); + } + } + } + + /** Prepare the notes pages or regular slides. + */ + void PrepareStdOrNotes ( + const PageKind ePageKind, + PrintInfo& rInfo) + { + OSL_ASSERT(rInfo.mpPrinter != nullptr); + + // Fill in page kind specific data. + SdDrawDocument* pDocument = mrBase.GetMainViewShell()->GetDoc(); + if (pDocument->GetSdPageCount(ePageKind) == 0) + return; + SdPage* pRefPage = pDocument->GetSdPage(0, ePageKind); + rInfo.maPageSize = pRefPage->GetSize(); + + SetupPaperOrientation(ePageKind, rInfo); + + MapMode aMap (rInfo.maMap); + rInfo.maMap = aMap; + + if (mpOptions->IsBooklet()) + PrepareBooklet(ePageKind, rInfo); + else + PrepareRegularPages(ePageKind, rInfo); + } + + /** Prepare slides in a non-booklet way: one slide per one to many + printer pages. + */ + void PrepareRegularPages ( + const PageKind ePageKind, + PrintInfo& rInfo) + { + std::shared_ptr<ViewShell> pViewShell (mrBase.GetMainViewShell()); + pViewShell->WriteFrameViewData(); + + sal_Int32 nPageCount = mrBase.GetDocument()->GetSdPageCount(PageKind::Standard); + StringRangeEnumerator aRangeEnum( + mpOptions->GetPrinterSelection(nPageCount, GetCurrentPageIndex()), + 0, nPageCount-1); + for (StringRangeEnumerator::Iterator + it = aRangeEnum.begin(), + itEnd = aRangeEnum.end(); + it != itEnd; + ++it) + { + SdPage* pPage = GetFilteredPage(*it, ePageKind); + if (pPage == nullptr) + continue; + + MapMode aMap (rInfo.maMap); + // is it possible that the page size changed? + const Size aPageSize = pPage->GetSize(); + + if (mpOptions->IsPageSize()) + { + const double fHorz (static_cast<double>(rInfo.maPrintSize.Width()) / aPageSize.Width()); + const double fVert (static_cast<double>(rInfo.maPrintSize.Height()) / aPageSize.Height()); + + Fraction aFract; + if (fHorz < fVert) + aFract = Fraction(rInfo.maPrintSize.Width(), aPageSize.Width()); + else + aFract = Fraction(rInfo.maPrintSize.Height(), aPageSize.Height()); + + aMap.SetScaleX(aFract); + aMap.SetScaleY(aFract); + aMap.SetOrigin(Point()); + } + + if (mpOptions->IsPrintPageName()) + { + rInfo.msPageString = pPage->GetName() + " "; + } + else + rInfo.msPageString.clear(); + rInfo.msPageString += rInfo.msTimeDate; + + ::tools::Long aPageWidth = aPageSize.Width() - pPage->GetLeftBorder() - pPage->GetRightBorder(); + ::tools::Long aPageHeight = aPageSize.Height() - pPage->GetUpperBorder() - pPage->GetLowerBorder(); + // Bugfix for 44530: + // if it was implicitly changed (Landscape/Portrait), + // this is considered for tiling, respectively for the splitting up + // (Poster) + if( ( rInfo.maPrintSize.Width() > rInfo.maPrintSize.Height() + && aPageWidth < aPageHeight ) + || ( rInfo.maPrintSize.Width() < rInfo.maPrintSize.Height() + && aPageWidth > aPageHeight ) ) + { + const sal_Int32 nTmp (rInfo.maPrintSize.Width()); + rInfo.maPrintSize.setWidth( rInfo.maPrintSize.Height() ); + rInfo.maPrintSize.setHeight( nTmp ); + } + + if (mpOptions->IsTilePage() + && aPageWidth < rInfo.maPrintSize.Width() + && aPageHeight < rInfo.maPrintSize.Height()) + { + // Put multiple slides on one printer page. + PrepareTiledPage(*it, *pPage, ePageKind, rInfo); + } + else + { + rInfo.maMap = aMap; + PrepareScaledPage(*it, *pPage, ePageKind, rInfo); + } + } + } + + /** Put two slides on one printer page. + */ + void PrepareBooklet ( + const PageKind ePageKind, + const PrintInfo& rInfo) + { + MapMode aStdMap (rInfo.maMap); + Point aOffset; + Size aPrintSize_2 (rInfo.maPrintSize); + Size aPageSize_2 (rInfo.maPageSize); + + if (rInfo.meOrientation == Orientation::Landscape) + aPrintSize_2.setWidth( aPrintSize_2.Width() >> 1 ); + else + aPrintSize_2.setHeight( aPrintSize_2.Height() >> 1 ); + + const double fPageWH = static_cast<double>(aPageSize_2.Width()) / aPageSize_2.Height(); + const double fPrintWH = static_cast<double>(aPrintSize_2.Width()) / aPrintSize_2.Height(); + + if( fPageWH < fPrintWH ) + { + aPageSize_2.setWidth( static_cast<::tools::Long>( aPrintSize_2.Height() * fPageWH ) ); + aPageSize_2.setHeight( aPrintSize_2.Height() ); + } + else + { + aPageSize_2.setWidth( aPrintSize_2.Width() ); + aPageSize_2.setHeight( static_cast<::tools::Long>( aPrintSize_2.Width() / fPageWH ) ); + } + + MapMode aMap (rInfo.maMap); + aMap.SetScaleX( Fraction( aPageSize_2.Width(), rInfo.maPageSize.Width() ) ); + aMap.SetScaleY( Fraction( aPageSize_2.Height(), rInfo.maPageSize.Height() ) ); + + // calculate adjusted print size + const Size aAdjustedPrintSize (OutputDevice::LogicToLogic( + rInfo.maPrintSize, + aStdMap, + aMap)); + + if (rInfo.meOrientation == Orientation::Landscape) + { + aOffset.setX( ( ( aAdjustedPrintSize.Width() >> 1 ) - rInfo.maPageSize.Width() ) >> 1 ); + aOffset.setY( ( aAdjustedPrintSize.Height() - rInfo.maPageSize.Height() ) >> 1 ); + } + else + { + aOffset.setX( ( aAdjustedPrintSize.Width() - rInfo.maPageSize.Width() ) >> 1 ); + aOffset.setY( ( ( aAdjustedPrintSize.Height() >> 1 ) - rInfo.maPageSize.Height() ) >> 1 ); + } + + // create vector of pages to print + sal_Int32 nPageCount = mrBase.GetDocument()->GetSdPageCount(ePageKind); + StringRangeEnumerator aRangeEnum( + mpOptions->GetPrinterSelection(nPageCount, GetCurrentPageIndex()), + 0, nPageCount-1); + std::vector< sal_uInt16 > aPageVector; + for (StringRangeEnumerator::Iterator + it = aRangeEnum.begin(), + itEnd = aRangeEnum.end(); + it != itEnd; + ++it) + { + SdPage* pPage = GetFilteredPage(*it, ePageKind); + if (pPage != nullptr) + aPageVector.push_back(*it); + } + + // create pairs of pages to print on each page + std::vector< std::pair< sal_uInt16, sal_uInt16 > > aPairVector; + if ( ! aPageVector.empty()) + { + sal_uInt32 nFirstIndex = 0, nLastIndex = aPageVector.size() - 1; + + if( aPageVector.size() & 1 ) + aPairVector.emplace_back( sal_uInt16(65535), aPageVector[ nFirstIndex++ ] ); + else + aPairVector.emplace_back( aPageVector[ nLastIndex-- ], aPageVector[ nFirstIndex++ ] ); + + while( nFirstIndex < nLastIndex ) + { + if( nFirstIndex & 1 ) + aPairVector.emplace_back( aPageVector[ nFirstIndex++ ], aPageVector[ nLastIndex-- ] ); + else + aPairVector.emplace_back( aPageVector[ nLastIndex-- ], aPageVector[ nFirstIndex++ ] ); + } + } + + for (sal_uInt32 + nIndex=0, + nCount=aPairVector.size(); + nIndex < nCount; + ++nIndex) + { + if ( CheckForFrontBackPages( nIndex ) ) + { + const std::pair<sal_uInt16, sal_uInt16> aPair (aPairVector[nIndex]); + Point aSecondOffset (aOffset); + if (rInfo.meOrientation == Orientation::Landscape) + aSecondOffset.AdjustX( aAdjustedPrintSize.Width() / 2 ); + else + aSecondOffset.AdjustY( aAdjustedPrintSize.Height() / 2 ); + maPrinterPages.push_back( + std::make_shared<BookletPrinterPage>( + aPair.first, + aPair.second, + aOffset, + aSecondOffset, + ePageKind, + aMap, + rInfo.mbPrintMarkedOnly, + rInfo.mnDrawMode, + rInfo.meOrientation, + rInfo.mpPrinter->GetPaperBin())); + + } + } + } + + /** Print one slide multiple times on one printer page so that the whole + printer page is covered. + */ + void PrepareTiledPage ( + const sal_Int32 nPageIndex, + const SdPage& rPage, + const PageKind ePageKind, + const PrintInfo& rInfo) + { + sal_uInt16 nPaperBin; + if ( ! mpOptions->IsPaperBin()) + nPaperBin = rPage.GetPaperBin(); + else + nPaperBin = rInfo.mpPrinter->GetPaperBin(); + + if ( !CheckForFrontBackPages( nPageIndex ) ) + return; + + maPrinterPages.push_back( + std::make_shared<TiledPrinterPage>( + sal::static_int_cast<sal_uInt16>(nPageIndex), + ePageKind, + rInfo.mbPrintMarkedOnly, + rInfo.msPageString, + rInfo.mpPrinter->GetPageOffset(), + rInfo.mnDrawMode, + rInfo.meOrientation, + nPaperBin)); + } + + /** Print one standard slide or notes page on one to many printer + pages. More than on printer page is used when the slide is larger + than the printable area. + */ + void PrepareScaledPage ( + const sal_Int32 nPageIndex, + const SdPage& rPage, + const PageKind ePageKind, + const PrintInfo& rInfo) + { + const Point aPageOffset (rInfo.mpPrinter->GetPageOffset()); + + sal_uInt16 nPaperBin; + if ( ! mpOptions->IsPaperBin()) + nPaperBin = rPage.GetPaperBin(); + else + nPaperBin = rInfo.mpPrinter->GetPaperBin(); + + // For pages larger then the printable area there + // are three options: + // 1. Scale down to the page to the printable area. + // 2. Print only the upper left part of the page + // (without the unprintable borders). + // 3. Split the page into parts of the size of the + // printable area. + const bool bScalePage (mpOptions->IsPageSize()); + const bool bCutPage (mpOptions->IsCutPage()); + MapMode aMap (rInfo.maMap); + if ( (bScalePage || bCutPage) && CheckForFrontBackPages( nPageIndex ) ) + { + // Handle 1 and 2. + + // if CutPage is set then do not move it, otherwise move the + // scaled page to printable area + maPrinterPages.push_back( + std::make_shared<RegularPrinterPage>( + sal::static_int_cast<sal_uInt16>(nPageIndex), + ePageKind, + aMap, + rInfo.mbPrintMarkedOnly, + rInfo.msPageString, + aPageOffset, + rInfo.mnDrawMode, + rInfo.meOrientation, + nPaperBin)); + } + else + { + // Handle 3. Print parts of the page in the size of the + // printable area until the whole page is covered. + + // keep the page content at its position if it fits, otherwise + // move it to the printable area + const ::tools::Long nPageWidth ( + rInfo.maPageSize.Width() - rPage.GetLeftBorder() - rPage.GetRightBorder()); + const ::tools::Long nPageHeight ( + rInfo.maPageSize.Height() - rPage.GetUpperBorder() - rPage.GetLowerBorder()); + + Point aOrigin ( 0, 0 ); + + for (Point aPageOrigin = aOrigin; + -aPageOrigin.Y()<nPageHeight; + aPageOrigin.AdjustY( -rInfo.maPrintSize.Height() )) + { + for (aPageOrigin.setX(aOrigin.X()); + -aPageOrigin.X()<nPageWidth; + aPageOrigin.AdjustX(-rInfo.maPrintSize.Width())) + { + if ( CheckForFrontBackPages( nPageIndex ) ) + { + aMap.SetOrigin(aPageOrigin); + maPrinterPages.push_back( + std::make_shared<RegularPrinterPage>( + sal::static_int_cast<sal_uInt16>(nPageIndex), + ePageKind, + aMap, + rInfo.mbPrintMarkedOnly, + rInfo.msPageString, + aPageOffset, + rInfo.mnDrawMode, + rInfo.meOrientation, + nPaperBin)); + } + } + } + } + } + +bool CheckForFrontBackPages( sal_Int32 nPage ) +{ + const bool bIsIndexOdd(nPage & 1); + if ((!bIsIndexOdd && mpOptions->IsPrintFrontPage()) + || (bIsIndexOdd && mpOptions->IsPrintBackPage())) + { + return true; + } + else + return false; +} +}; + +//===== DocumentRenderer ====================================================== + +DocumentRenderer::DocumentRenderer (ViewShellBase& rBase) + : mpImpl(new Implementation(rBase)) +{ +} + +DocumentRenderer::~DocumentRenderer() +{ +} + +//----- XRenderable ----------------------------------------------------------- + +sal_Int32 SAL_CALL DocumentRenderer::getRendererCount ( + const css::uno::Any&, + const css::uno::Sequence<css::beans::PropertyValue >& rOptions) +{ + mpImpl->ProcessProperties(rOptions); + return mpImpl->GetPrintPageCount(); +} + +Sequence<beans::PropertyValue> SAL_CALL DocumentRenderer::getRenderer ( + sal_Int32, + const css::uno::Any&, + const css::uno::Sequence<css::beans::PropertyValue>& rOptions) +{ + mpImpl->ProcessProperties(rOptions); + return mpImpl->GetProperties(); +} + +void SAL_CALL DocumentRenderer::render ( + sal_Int32 nRenderer, + const css::uno::Any&, + const css::uno::Sequence<css::beans::PropertyValue>& rOptions) +{ + mpImpl->ProcessProperties(rOptions); + mpImpl->PrintPage(nRenderer); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/FormShellManager.cxx b/sd/source/ui/view/FormShellManager.cxx new file mode 100644 index 000000000..3efa9bed7 --- /dev/null +++ b/sd/source/ui/view/FormShellManager.cxx @@ -0,0 +1,319 @@ +/* -*- 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 <FormShellManager.hxx> + +#include <EventMultiplexer.hxx> +#include <ViewShell.hxx> +#include <ViewShellBase.hxx> +#include <ViewShellManager.hxx> +#include <Window.hxx> +#include <vcl/vclevent.hxx> +#include <svx/fmshell.hxx> +#include <osl/diagnose.h> + +namespace sd { + +namespace { + +/** This factory is responsible for creating and deleting the FmFormShell. +*/ +class FormShellManagerFactory + : public ::sd::ShellFactory<SfxShell> +{ +public: + FormShellManagerFactory (ViewShell& rViewShell, FormShellManager& rManager); + virtual FmFormShell* CreateShell (ShellId nId) override; + virtual void ReleaseShell (SfxShell* pShell) override; + +private: + ::sd::ViewShell& mrViewShell; + FormShellManager& mrFormShellManager; +}; + +} // end of anonymous namespace + +FormShellManager::FormShellManager (ViewShellBase& rBase) + : mrBase(rBase), + mpFormShell(nullptr), + mbFormShellAboveViewShell(false), + mbIsMainViewChangePending(false), + mpMainViewShellWindow(nullptr) +{ + // Register at the EventMultiplexer to be informed about changes in the + // center pane. + Link<sd::tools::EventMultiplexerEvent&,void> aLink (LINK(this, FormShellManager, ConfigurationUpdateHandler)); + mrBase.GetEventMultiplexer()->AddEventListener(aLink); + + RegisterAtCenterPane(); +} + +FormShellManager::~FormShellManager() +{ + SetFormShell(nullptr); + UnregisterAtCenterPane(); + + // Unregister from the EventMultiplexer. + Link<sd::tools::EventMultiplexerEvent&,void> aLink (LINK(this, FormShellManager, ConfigurationUpdateHandler)); + mrBase.GetEventMultiplexer()->RemoveEventListener(aLink); + + if (mpSubShellFactory) + { + ViewShell* pShell = mrBase.GetMainViewShell().get(); + if (pShell != nullptr) + mrBase.GetViewShellManager()->RemoveSubShellFactory(pShell,mpSubShellFactory); + } +} + +void FormShellManager::SetFormShell (FmFormShell* pFormShell) +{ + if (mpFormShell == pFormShell) + return; + + // Disconnect from the old form shell. + if (mpFormShell != nullptr) + { + mpFormShell->SetControlActivationHandler(Link<LinkParamNone*,void>()); + EndListening(*mpFormShell); + mpFormShell->SetView(nullptr); + } + + mpFormShell = pFormShell; + + // Connect to the new form shell. + if (mpFormShell != nullptr) + { + mpFormShell->SetControlActivationHandler( + LINK( + this, + FormShellManager, + FormControlActivated)); + StartListening(*mpFormShell); + + ViewShell* pMainViewShell = mrBase.GetMainViewShell().get(); + if (pMainViewShell != nullptr) + { + // Prevent setting the view twice at the FmFormShell. + FmFormView* pFormView = pMainViewShell->GetView(); + if (mpFormShell->GetFormView() != pFormView) + mpFormShell->SetView(pFormView); + } + } + + // Tell the ViewShellManager where on the stack to place the form shell. + mrBase.GetViewShellManager()->SetFormShell( + mrBase.GetMainViewShell().get(), + mpFormShell, + mbFormShellAboveViewShell); +} + +void FormShellManager::RegisterAtCenterPane() +{ + ViewShell* pShell = mrBase.GetMainViewShell().get(); + if (pShell == nullptr) + return; + + // No form shell for the slide sorter. Besides that it is not + // necessary, using both together results in crashes. + if (pShell->GetShellType() == ViewShell::ST_SLIDE_SORTER) + return; + + mpMainViewShellWindow = pShell->GetActiveWindow(); + if (mpMainViewShellWindow == nullptr) + return; + + // Register at the window to get informed when to move the form + // shell to the bottom of the shell stack. + mpMainViewShellWindow->AddEventListener( + LINK( + this, + FormShellManager, + WindowEventHandler)); + + // Create a shell factory and with it activate the form shell. + OSL_ASSERT(!mpSubShellFactory); + mpSubShellFactory = std::make_shared<FormShellManagerFactory>(*pShell, *this); + mrBase.GetViewShellManager()->AddSubShellFactory(pShell,mpSubShellFactory); + mrBase.GetViewShellManager()->ActivateSubShell(*pShell, ToolbarId::FormLayer_Toolbox); +} + +void FormShellManager::UnregisterAtCenterPane() +{ + if (mpMainViewShellWindow != nullptr) + { + // Unregister from the window. + mpMainViewShellWindow->RemoveEventListener( + LINK( + this, + FormShellManager, + WindowEventHandler)); + mpMainViewShellWindow = nullptr; + } + + // Unregister form at the form shell. + SetFormShell(nullptr); + + // Deactivate the form shell and destroy the shell factory. + ViewShell* pShell = mrBase.GetMainViewShell().get(); + if (pShell != nullptr) + { + mrBase.GetViewShellManager()->DeactivateSubShell(*pShell, ToolbarId::FormLayer_Toolbox); + mrBase.GetViewShellManager()->RemoveSubShellFactory(pShell, mpSubShellFactory); + } + + mpSubShellFactory.reset(); +} + +IMPL_LINK_NOARG(FormShellManager, FormControlActivated, LinkParamNone*, void) +{ + // The form shell has been activated. To give it priority in reacting to + // slot calls the form shell is moved to the top of the object bar shell + // stack. + ViewShell* pShell = mrBase.GetMainViewShell().get(); + if (pShell!=nullptr && !mbFormShellAboveViewShell) + { + mbFormShellAboveViewShell = true; + + ViewShellManager::UpdateLock aLock (mrBase.GetViewShellManager()); + mrBase.GetViewShellManager()->SetFormShell(pShell,mpFormShell,mbFormShellAboveViewShell); + } +} + +IMPL_LINK(FormShellManager, ConfigurationUpdateHandler, sd::tools::EventMultiplexerEvent&, rEvent, void) +{ + switch (rEvent.meEventId) + { + case EventMultiplexerEventId::MainViewRemoved: + UnregisterAtCenterPane(); + break; + + case EventMultiplexerEventId::MainViewAdded: + mbIsMainViewChangePending = true; + break; + + case EventMultiplexerEventId::ConfigurationUpdated: + if (mbIsMainViewChangePending) + { + mbIsMainViewChangePending = false; + RegisterAtCenterPane(); + } + break; + + default: + break; + } +} + +IMPL_LINK(FormShellManager, WindowEventHandler, VclWindowEvent&, rEvent, void) +{ + switch (rEvent.GetId()) + { + case VclEventId::WindowGetFocus: + { + // The window of the center pane got the focus. Therefore + // the form shell is moved to the bottom of the object bar + // stack. + ViewShell* pShell = mrBase.GetMainViewShell().get(); + if (pShell!=nullptr && mbFormShellAboveViewShell) + { + mbFormShellAboveViewShell = false; + ViewShellManager::UpdateLock aLock (mrBase.GetViewShellManager()); + mrBase.GetViewShellManager()->SetFormShell( + pShell, + mpFormShell, + mbFormShellAboveViewShell); + } + } + break; + + case VclEventId::WindowLoseFocus: + // We follow the sloppy focus policy. Losing the focus is + // ignored. We wait for the focus to be placed either in + // the window or the form shell. The later, however, is + // notified over the FormControlActivated handler, not this + // one. + break; + + case VclEventId::ObjectDying: + mpMainViewShellWindow = nullptr; + break; + + default: break; + } +} + +void FormShellManager::Notify(SfxBroadcaster&, const SfxHint& rHint) +{ + if (rHint.GetId()!=SfxHintId::Dying) + return; + + // If all goes well this listener is called after the + // FormShellManager was notified about the dying form shell by the + // FormShellManagerFactory. + OSL_ASSERT(mpFormShell==nullptr); + if (mpFormShell != nullptr) + { + mpFormShell = nullptr; + mrBase.GetViewShellManager()->SetFormShell( + mrBase.GetMainViewShell().get(), + nullptr, + false); + } +} + +//===== FormShellManagerFactory =============================================== + +namespace { + +FormShellManagerFactory::FormShellManagerFactory ( + ::sd::ViewShell& rViewShell, + FormShellManager& rManager) + : mrViewShell(rViewShell), + mrFormShellManager(rManager) +{ +} + +FmFormShell* FormShellManagerFactory::CreateShell( ::sd::ShellId nId ) +{ + FmFormShell* pShell = nullptr; + + ::sd::View* pView = mrViewShell.GetView(); + if (nId == ToolbarId::FormLayer_Toolbox) + { + pShell = new FmFormShell(&mrViewShell.GetViewShellBase(), pView); + mrFormShellManager.SetFormShell(pShell); + } + + return pShell; +} + +void FormShellManagerFactory::ReleaseShell (SfxShell* pShell) +{ + if (pShell != nullptr) + { + mrFormShellManager.SetFormShell(nullptr); + delete pShell; + } +} + +} // end of anonymous namespace + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/GraphicObjectBar.cxx b/sd/source/ui/view/GraphicObjectBar.cxx new file mode 100644 index 000000000..60cab73f7 --- /dev/null +++ b/sd/source/ui/view/GraphicObjectBar.cxx @@ -0,0 +1,141 @@ +/* -*- 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 <GraphicObjectBar.hxx> + +#include <sfx2/shell.hxx> +#include <svx/svxids.hrc> +#include <sfx2/request.hxx> +#include <svx/svdograf.hxx> +#include <svx/grfflt.hxx> +#include <svx/grafctrl.hxx> + +#include <sfx2/objface.hxx> + +#include <strings.hrc> +#include <DrawDocShell.hxx> +#include <ViewShell.hxx> +#include <sdresid.hxx> + +using namespace sd; +#define ShellClass_GraphicObjectBar +#include <sdslots.hxx> + +namespace sd { + + +SFX_IMPL_INTERFACE(GraphicObjectBar, SfxShell) + +void GraphicObjectBar::InitInterface_Impl() +{ +} + + +GraphicObjectBar::GraphicObjectBar ( + const ViewShell* pSdViewShell, + ::sd::View* pSdView ) + : SfxShell (pSdViewShell->GetViewShell()), + mpView ( pSdView ) +{ + DrawDocShell* pDocShell = pSdViewShell->GetDocSh(); + + SetPool( &pDocShell->GetPool() ); + SetUndoManager( pDocShell->GetUndoManager() ); + SetRepeatTarget( mpView ); + SetName( "Graphic objectbar"); +} + +GraphicObjectBar::~GraphicObjectBar() +{ + SetRepeatTarget( nullptr ); +} + +void GraphicObjectBar::GetAttrState( SfxItemSet& rSet ) +{ + if( mpView ) + SvxGrafAttrHelper::GetGrafAttrState( rSet, *mpView ); +} + +void GraphicObjectBar::Execute( SfxRequest& rReq ) +{ + if( mpView ) + { + SvxGrafAttrHelper::ExecuteGrafAttr( rReq, *mpView ); + Invalidate(); + } +} + +void GraphicObjectBar::GetFilterState( SfxItemSet& rSet ) +{ + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + bool bEnable = false; + + if( rMarkList.GetMarkCount() == 1 ) + { + SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj(); + + if( auto pGrafObj = dynamic_cast< SdrGrafObj *>( pObj ) ) + if( pGrafObj->GetGraphicType() == GraphicType::Bitmap ) + bEnable = true; + } + + if( !bEnable ) + SvxGraphicFilter::DisableGraphicFilterSlots( rSet ); +} + +void GraphicObjectBar::ExecuteFilter( SfxRequest const & rReq ) +{ + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + if( rMarkList.GetMarkCount() == 1 ) + { + SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj(); + + if( auto pGrafObj = dynamic_cast< SdrGrafObj *>( pObj ) ) + if( pGrafObj->GetGraphicType() == GraphicType::Bitmap ) + { + GraphicObject aFilterObj( pGrafObj->GetGraphicObject() ); + + if( SvxGraphicFilterResult::NONE == + SvxGraphicFilter::ExecuteGrfFilterSlot( rReq, aFilterObj ) ) + { + SdrPageView* pPageView = mpView->GetSdrPageView(); + + if( pPageView ) + { + SdrGrafObj* pFilteredObj = static_cast<SdrGrafObj*>( pObj->CloneSdrObject(pObj->getSdrModelFromSdrObject()) ); + OUString aStr = mpView->GetDescriptionOfMarkedObjects() + + " " + SdResId(STR_UNDO_GRAFFILTER); + mpView->BegUndo( aStr ); + pFilteredObj->SetGraphicObject( aFilterObj ); + ::sd::View* const pView = mpView; + pView->ReplaceObjectAtView( pObj, *pPageView, pFilteredObj ); + pView->EndUndo(); + return; + } + } + } + } + + Invalidate(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/GraphicViewShellBase.cxx b/sd/source/ui/view/GraphicViewShellBase.cxx new file mode 100644 index 000000000..d58c8a0d2 --- /dev/null +++ b/sd/source/ui/view/GraphicViewShellBase.cxx @@ -0,0 +1,93 @@ +/* -*- 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 <GraphicViewShellBase.hxx> + +#include <GraphicDocShell.hxx> +#include <app.hrc> +#include <framework/DrawModule.hxx> +#include <framework/FrameworkHelper.hxx> +#include <sfx2/request.hxx> +#include <sfx2/viewfac.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/viewsh.hxx> + +namespace sd +{ +// We have to expand the SFX_IMPL_VIEWFACTORY macro to call LateInit() after a +// new GraphicViewShellBase object has been constructed. + +SfxViewFactory* GraphicViewShellBase::s_pFactory; +SfxViewShell* GraphicViewShellBase::CreateInstance(SfxViewFrame* pFrame, SfxViewShell* pOldView) +{ + GraphicViewShellBase* pBase = new GraphicViewShellBase(pFrame, pOldView); + pBase->LateInit(framework::FrameworkHelper::msDrawViewURL); + return pBase; +} +void GraphicViewShellBase::RegisterFactory(SfxInterfaceId nPrio) +{ + s_pFactory = new SfxViewFactory(&CreateInstance, nPrio, "Default"); + InitFactory(); +} +void GraphicViewShellBase::InitFactory() { SFX_VIEW_REGISTRATION(GraphicDocShell); } + +GraphicViewShellBase::GraphicViewShellBase(SfxViewFrame* _pFrame, SfxViewShell* pOldShell) + : ViewShellBase(_pFrame, pOldShell) +{ +} + +GraphicViewShellBase::~GraphicViewShellBase() {} + +void GraphicViewShellBase::Execute(SfxRequest& rRequest) +{ + sal_uInt16 nSlotId = rRequest.GetSlot(); + + switch (nSlotId) + { + case SID_NOTES_WINDOW: + case SID_SLIDE_SORTER_MULTI_PANE_GUI: + case SID_SLIDE_SORTER_MODE: + case SID_SLIDE_MASTER_MODE: + case SID_OUTLINE_MODE: + case SID_NOTES_MODE: + case SID_NOTES_MASTER_MODE: + case SID_HANDOUT_MASTER_MODE: + // Prevent some Impress-only slots from being executed. + rRequest.Cancel(); + break; + + case SID_SWITCH_SHELL: + case SID_LEFT_PANE_DRAW: + case SID_LEFT_PANE_IMPRESS: + default: + // The remaining requests are forwarded to our base class. + ViewShellBase::Execute(rRequest); + break; + } +} + +void GraphicViewShellBase::InitializeFramework() +{ + css::uno::Reference<css::frame::XController> xController(GetController()); + sd::framework::DrawModule::Initialize(xController); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/ImpressViewShellBase.cxx b/sd/source/ui/view/ImpressViewShellBase.cxx new file mode 100644 index 000000000..96b0b5aa9 --- /dev/null +++ b/sd/source/ui/view/ImpressViewShellBase.cxx @@ -0,0 +1,97 @@ +/* -*- 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 <ImpressViewShellBase.hxx> + +#include <DrawDocShell.hxx> +#include <app.hrc> +#include <framework/FrameworkHelper.hxx> +#include <framework/ImpressModule.hxx> +#include <MasterPageObserver.hxx> +#include <sfx2/request.hxx> +#include <sfx2/viewfac.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/viewsh.hxx> +#include <comphelper/lok.hxx> + +namespace sd { + + +// We have to expand the SFX_IMPL_VIEWFACTORY macro to call LateInit() after a +// new ImpressViewShellBase object has been constructed. + +SfxViewFactory* ImpressViewShellBase::s_pFactory; +SfxViewShell* ImpressViewShellBase::CreateInstance ( + SfxViewFrame *pFrame, SfxViewShell *pOldView) +{ + ImpressViewShellBase* pBase = new ImpressViewShellBase(pFrame, pOldView); + pBase->LateInit(comphelper::LibreOfficeKit::isActive() ? framework::FrameworkHelper::msImpressViewURL : ""); + return pBase; +} +void ImpressViewShellBase::RegisterFactory( SfxInterfaceId nPrio ) +{ + s_pFactory = new SfxViewFactory(&CreateInstance,nPrio,"Default"); + InitFactory(); +} +void ImpressViewShellBase::InitFactory() +{ + SFX_VIEW_REGISTRATION(DrawDocShell); +} + +ImpressViewShellBase::ImpressViewShellBase ( + SfxViewFrame* _pFrame, + SfxViewShell* pOldShell) + : ViewShellBase (_pFrame, pOldShell) +{ + MasterPageObserver::Instance().RegisterDocument (*GetDocShell()->GetDoc()); +} + +ImpressViewShellBase::~ImpressViewShellBase() +{ + MasterPageObserver::Instance().UnregisterDocument (*GetDocShell()->GetDoc()); +} + +void ImpressViewShellBase::Execute (SfxRequest& rRequest) +{ + sal_uInt16 nSlotId = rRequest.GetSlot(); + + switch (nSlotId) + { + case SID_LEFT_PANE_DRAW: + // Prevent a Draw-only slots from being executed. + rRequest.Cancel(); + break; + + default: + // The remaining requests are forwarded to our base class. + ViewShellBase::Execute(rRequest); + break; + } +} + +void ImpressViewShellBase::InitializeFramework() +{ + css::uno::Reference<css::frame::XController> + xController (GetController()); + sd::framework::ImpressModule::Initialize(xController); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/MediaObjectBar.cxx b/sd/source/ui/view/MediaObjectBar.cxx new file mode 100644 index 000000000..232535240 --- /dev/null +++ b/sd/source/ui/view/MediaObjectBar.cxx @@ -0,0 +1,77 @@ +/* -*- 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 <MediaObjectBar.hxx> +#include <avmedia/mediaitem.hxx> +#include <sfx2/sfxsids.hrc> +#include <sfx2/objface.hxx> +#include <svx/MediaShellHelpers.hxx> + +#include <strings.hrc> +#include <DrawDocShell.hxx> +#include <ViewShell.hxx> +#include <sdresid.hxx> +#include <drawdoc.hxx> + +using namespace sd; +using namespace svx; + +#define ShellClass_MediaObjectBar +#include <sdslots.hxx> + +namespace sd +{ +SFX_IMPL_INTERFACE(MediaObjectBar, SfxShell) + +void MediaObjectBar::InitInterface_Impl() {} + +MediaObjectBar::MediaObjectBar(const ViewShell* pSdViewShell, ::sd::View* pSdView) + : SfxShell(pSdViewShell->GetViewShell()) + , mpView(pSdView) +{ + DrawDocShell* pDocShell = pSdViewShell->GetDocSh(); + + SetPool(&pDocShell->GetPool()); + SetUndoManager(pDocShell->GetUndoManager()); + SetRepeatTarget(mpView); + SetName(SdResId(RID_DRAW_MEDIA_TOOLBOX)); +} + +MediaObjectBar::~MediaObjectBar() { SetRepeatTarget(nullptr); } + +void MediaObjectBar::GetState(SfxItemSet& rSet) { MediaShellHelpers::GetState(mpView, rSet); } + +void MediaObjectBar::Execute(SfxRequest const& rReq) +{ + const ::avmedia::MediaItem* pMediaItem = MediaShellHelpers::Execute(mpView, rReq); + if (!pMediaItem) + return; + + //if only changing state then don't set modified flag (e.g. playing a video) + if (!(pMediaItem->getMaskSet() & AVMediaSetMask::STATE)) + { + //fdo #32598: after changing playback opts, set document's modified flag + SdDrawDocument& rDoc = mpView->GetDoc(); + rDoc.SetChanged(); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/OutlineViewShellBase.cxx b/sd/source/ui/view/OutlineViewShellBase.cxx new file mode 100644 index 000000000..8da1bcbca --- /dev/null +++ b/sd/source/ui/view/OutlineViewShellBase.cxx @@ -0,0 +1,66 @@ +/* -*- 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 <OutlineViewShellBase.hxx> +#include <DrawDocShell.hxx> +#include <framework/FrameworkHelper.hxx> +#include <sfx2/viewfac.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/viewsh.hxx> + +namespace sd { + +class DrawDocShell; + + +// We have to expand the SFX_IMPL_VIEWFACTORY macro to call LateInit() after a +// new OutlineViewShellBase object has been constructed. + +SfxViewFactory* OutlineViewShellBase::s_pFactory; +SfxViewShell* OutlineViewShellBase::CreateInstance ( + SfxViewFrame *pFrame, SfxViewShell *pOldView) +{ + OutlineViewShellBase* pBase = new OutlineViewShellBase(pFrame, pOldView); + pBase->LateInit(framework::FrameworkHelper::msOutlineViewURL); + return pBase; +} +void OutlineViewShellBase::RegisterFactory( SfxInterfaceId nPrio ) +{ + s_pFactory = new SfxViewFactory(&CreateInstance,nPrio,"Outline"); + InitFactory(); +} +void OutlineViewShellBase::InitFactory() +{ + SFX_VIEW_REGISTRATION(DrawDocShell); +} + +OutlineViewShellBase::OutlineViewShellBase ( + SfxViewFrame* _pFrame, + SfxViewShell* pOldShell) + : ImpressViewShellBase (_pFrame, pOldShell) +{ +} + +OutlineViewShellBase::~OutlineViewShellBase() +{ +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/Outliner.cxx b/sd/source/ui/view/Outliner.cxx new file mode 100644 index 000000000..a63337692 --- /dev/null +++ b/sd/source/ui/view/Outliner.cxx @@ -0,0 +1,2066 @@ +/* -*- 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 <Outliner.hxx> +#include <boost/property_tree/json_parser.hpp> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> + +#include <svl/srchitem.hxx> +#include <svl/intitem.hxx> +#include <editeng/editstat.hxx> +#include <vcl/canvastools.hxx> +#include <vcl/outdev.hxx> +#include <vcl/weld.hxx> +#include <sfx2/dispatch.hxx> +#include <svx/svdotext.hxx> +#include <svx/svdograf.hxx> +#include <editeng/unolingu.hxx> +#include <com/sun/star/linguistic2/XSpellChecker1.hpp> +#include <svx/srchdlg.hxx> +#include <unotools/linguprops.hxx> +#include <unotools/lingucfg.hxx> +#include <editeng/editeng.hxx> +#include <sfx2/viewfrm.hxx> +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> + +#include <strings.hrc> +#include <editeng/outliner.hxx> +#include <sdmod.hxx> +#include <Window.hxx> +#include <sdresid.hxx> +#include <DrawViewShell.hxx> +#include <OutlineView.hxx> +#include <OutlineViewShell.hxx> +#include <drawdoc.hxx> +#include <DrawDocShell.hxx> +#include <drawview.hxx> +#include <ViewShellBase.hxx> +#include <SpellDialogChildWindow.hxx> +#include <framework/FrameworkHelper.hxx> +#include <svx/svxids.hrc> +#include <LibreOfficeKit/LibreOfficeKitEnums.h> +#include <comphelper/string.hxx> +#include <comphelper/lok.hxx> +#include <comphelper/scopeguard.hxx> +#include <VectorGraphicSearchContext.hxx> +#include <fusearch.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::linguistic2; + +class SfxStyleSheetPool; + +class SdOutliner::Implementation +{ +public: + /** The original edit mode directly after switching to a different view + mode. Used for restoring the edit mode when leaving that view mode + again. + */ + EditMode meOriginalEditMode; + + Implementation(); + ~Implementation(); + + /** Return the OutlinerView that was provided by the last call to + ProvideOutlinerView() (or NULL when there was no such call.) + */ + OutlinerView* GetOutlinerView() { return mpOutlineView;} + + /** Provide in the member mpOutlineView an instance of OutlinerView that + is either taken from the ViewShell, when it is an OutlineViewShell, + or is created. When an OutlinerView already exists it is initialized. + */ + void ProvideOutlinerView ( + Outliner& rOutliner, + const std::shared_ptr<sd::ViewShell>& rpViewShell, + vcl::Window* pWindow); + + /** This method is called when the OutlinerView is no longer used. + */ + void ReleaseOutlinerView(); + + sd::VectorGraphicSearchContext& getVectorGraphicSearchContext() { return maVectorGraphicSearchContext; } + +private: + /** Flag that specifies whether we own the outline view pointed to by + <member>mpOutlineView</member> and thus have to + delete it in <member>EndSpelling()</member>. + */ + bool mbOwnOutlineView; + + /** The outline view used for searching and spelling. If searching or + spell checking an outline view this data member points to that view. + For all other views an instance is created. The + <member>mbOwnOutlineView</member> distinguishes between both cases. + */ + OutlinerView* mpOutlineView; + + sd::VectorGraphicSearchContext maVectorGraphicSearchContext; +}; + +namespace +{ + +sd::ViewShellBase* getViewShellBase() +{ + return dynamic_cast<sd::ViewShellBase*>(SfxViewShell::Current()); +} + +} // end anonymous namespace + +SdOutliner::SdOutliner( SdDrawDocument* pDoc, OutlinerMode nMode ) + : SdrOutliner( &pDoc->GetItemPool(), nMode ), + mpImpl(new Implementation()), + meMode(SEARCH), + mpView(nullptr), + mpWindow(nullptr), + mpDrawDocument(pDoc), + mnConversionLanguage(LANGUAGE_NONE), + mnIgnoreCurrentPageChangesLevel(0), + mbStringFound(false), + mbMatchMayExist(false), + mnPageCount(0), + mbEndOfSearch(false), + mbFoundObject(false), + mbDirectionIsForward(true), + mbRestrictSearchToSelection(false), + mpObj(nullptr), + mpFirstObj(nullptr), + mpSearchSpellTextObj(nullptr), + mnText(0), + mpParaObj(nullptr), + meStartViewMode(PageKind::Standard), + meStartEditMode(EditMode::Page), + mnStartPageIndex(sal_uInt16(-1)), + mpStartEditedObject(nullptr), + mbPrepareSpellingPending(true) +{ + SetStyleSheetPool(static_cast<SfxStyleSheetPool*>( mpDrawDocument->GetStyleSheetPool() )); + SetEditTextObjectPool( &pDoc->GetItemPool() ); + SetCalcFieldValueHdl(LINK(SD_MOD(), SdModule, CalcFieldValueHdl)); + SetForbiddenCharsTable( pDoc->GetForbiddenCharsTable() ); + + EEControlBits nCntrl = GetControlWord(); + nCntrl |= EEControlBits::ALLOWBIGOBJS; + nCntrl |= EEControlBits::MARKFIELDS; + nCntrl |= EEControlBits::AUTOCORRECT; + + bool bOnlineSpell = false; + + sd::DrawDocShell* pDocSh = mpDrawDocument->GetDocSh(); + + if (pDocSh) + { + bOnlineSpell = mpDrawDocument->GetOnlineSpell(); + } + else + { + bOnlineSpell = false; + + try + { + const SvtLinguConfig aLinguConfig; + Any aAny = aLinguConfig.GetProperty( UPN_IS_SPELL_AUTO ); + aAny >>= bOnlineSpell; + } + catch( ... ) + { + OSL_FAIL( "Ill. type in linguistic property" ); + } + } + + if (bOnlineSpell) + nCntrl |= EEControlBits::ONLINESPELLING; + else + nCntrl &= ~EEControlBits::ONLINESPELLING; + + SetControlWord(nCntrl); + + Reference< XSpellChecker1 > xSpellChecker( LinguMgr::GetSpellChecker() ); + if ( xSpellChecker.is() ) + SetSpeller( xSpellChecker ); + + Reference< XHyphenator > xHyphenator( LinguMgr::GetHyphenator() ); + if( xHyphenator.is() ) + SetHyphenator( xHyphenator ); + + SetDefaultLanguage( Application::GetSettings().GetLanguageTag().getLanguageType() ); +} + +/// Nothing spectacular in the destructor. +SdOutliner::~SdOutliner() +{ +} + +OutlinerView* SdOutliner::getOutlinerView() +{ + return mpImpl->GetOutlinerView(); +} + +/** Prepare find&replace or spellchecking. This distinguishes between three + cases: + <ol> + <li>The current shell is a <type>DrawViewShell</type>: Create a + <type>OutlinerView</type> object and search all objects of (i) the + current mark list, (ii) of the current view, or (iii) of all the view + combinations: + <ol> + <li>Draw view, slide view</li> + <li>Draw view, background view</li> + <li>Notes view, slide view</li> + <li>Notes view, background view</li> + <li>Handout view, slide view</li> + <li>Handout view, background view</li> + </ol> + + <li>When the current shell is a <type>SdOutlineViewShell</type> then + directly operate on it. No switching into other views takes place.</li> + </ol> +*/ +void SdOutliner::PrepareSpelling() +{ + mbPrepareSpellingPending = false; + + sd::ViewShellBase* pBase = getViewShellBase(); + if (pBase != nullptr) + SetViewShell (pBase->GetMainViewShell()); + SetRefDevice( SD_MOD()->GetVirtualRefDevice() ); + + std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock()); + if (pViewShell) + { + mbStringFound = false; + + // Supposed that we are not located at the very beginning/end of + // the document then there may be a match in the document + // prior/after the current position. + mbMatchMayExist = true; + + maObjectIterator = sd::outliner::Iterator(); + maSearchStartPosition = sd::outliner::Iterator(); + RememberStartPosition(); + + mpImpl->ProvideOutlinerView(*this, pViewShell, mpWindow); + + HandleChangedSelection (); + } + ClearModifyFlag(); +} + +void SdOutliner::StartSpelling() +{ + meMode = SPELL; + mbDirectionIsForward = true; + mpSearchItem.reset(); +} + +/** Free all resources acquired during the search/spell check. After a + spell check the start position is restored here. +*/ +void SdOutliner::EndSpelling() +{ + // Keep old view shell alive until we release the outliner view. + std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock()); + std::shared_ptr<sd::ViewShell> pOldViewShell (pViewShell); + + sd::ViewShellBase* pBase = getViewShellBase(); + if (pBase != nullptr) + pViewShell = pBase->GetMainViewShell(); + else + pViewShell.reset(); + mpWeakViewShell = pViewShell; + + // When in <member>PrepareSpelling()</member> a new outline view has + // been created then delete it here. + bool bViewIsDrawViewShell(dynamic_cast< const sd::DrawViewShell *>( pViewShell.get() )); + if (bViewIsDrawViewShell) + { + SetStatusEventHdl(Link<EditStatus&,void>()); + mpView = pViewShell->GetView(); + mpView->UnmarkAllObj (mpView->GetSdrPageView()); + mpView->SdrEndTextEdit(); + // Make FuSelection the current function. + pViewShell->GetDispatcher()->Execute( + SID_OBJECT_SELECT, + SfxCallMode::SYNCHRON | SfxCallMode::RECORD); + + // Remove and, if previously created by us, delete the outline + // view. + OutlinerView* pOutlinerView = getOutlinerView(); + if (pOutlinerView != nullptr) + { + RemoveView(pOutlinerView); + mpImpl->ReleaseOutlinerView(); + } + + SetUpdateLayout(true); + } + + // Before clearing the modify flag use it as a hint that + // changes were done at SpellCheck + if(IsModified()) + { + if(auto pOutlineView = dynamic_cast<sd::OutlineView *>( mpView )) + pOutlineView->PrepareClose(); + if(mpDrawDocument && !mpDrawDocument->IsChanged()) + mpDrawDocument->SetChanged(); + } + + // Now clear the modify flag to have a specified state of + // Outliner + ClearModifyFlag(); + + // When spell checking then restore the start position. + if (meMode==SPELL || meMode==TEXT_CONVERSION) + RestoreStartPosition (); + + mpWeakViewShell.reset(); + mpView = nullptr; + mpWindow = nullptr; + mnStartPageIndex = sal_uInt16(-1); +} + +bool SdOutliner::SpellNextDocument() +{ + std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock()); + if( nullptr != dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() )) + { + // When doing a spell check in the outline view then there is + // only one document. + mbEndOfSearch = true; + EndOfSearch (); + } + else + { + if( auto pOutlineView = dynamic_cast<sd::OutlineView *>( mpView )) + pOutlineView->PrepareClose(); + mpDrawDocument->GetDocSh()->SetWaitCursor( true ); + + Initialize (true); + + mpWindow = pViewShell->GetActiveWindow(); + OutlinerView* pOutlinerView = getOutlinerView(); + if (pOutlinerView != nullptr) + pOutlinerView->SetWindow(mpWindow); + ProvideNextTextObject (); + + mpDrawDocument->GetDocSh()->SetWaitCursor( false ); + ClearModifyFlag(); + } + + return !mbEndOfSearch; +} + +/** + * check next text object + */ +svx::SpellPortions SdOutliner::GetNextSpellSentence() +{ + svx::SpellPortions aResult; + + DetectChange(); + // Iterate over sentences and text shapes until a sentence with a + // spelling error has been found. If no such sentence can be + // found the loop is left through a break. + // It is the responsibility of the sd outliner object to correctly + // iterate over all text shapes, i.e. switch between views, wrap + // around at the end of the document, stop when all text shapes + // have been examined exactly once. + bool bFoundNextSentence = false; + while ( ! bFoundNextSentence) + { + OutlinerView* pOutlinerView = GetView(0); + if (pOutlinerView != nullptr) + { + ESelection aCurrentSelection (pOutlinerView->GetSelection()); + if ( ! mbMatchMayExist + && maStartSelection < aCurrentSelection) + EndOfSearch(); + + // Advance to the next sentence. + bFoundNextSentence = SpellSentence( pOutlinerView->GetEditView(), aResult); + } + + // When no sentence with spelling errors has been found in the + // currently selected text shape or there is no selected text + // shape then advance to the next text shape. + if ( ! bFoundNextSentence) + if ( ! SpellNextDocument()) + // All text objects have been processed so exit the + // loop and return an empty portions list. + break; + } + + return aResult; +} + +/** Go to next match. +*/ +bool SdOutliner::StartSearchAndReplace (const SvxSearchItem* pSearchItem) +{ + bool bEndOfSearch = true; + + // clear the search toolbar entry + SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::Empty); + + mpDrawDocument->GetDocSh()->SetWaitCursor( true ); + + // Since REPLACE is really a replaceAndSearchNext instead of a searchAndReplace, + // make sure that the search portion has not changed since the last FIND. + if (!mbPrepareSpellingPending && mpSearchItem + && pSearchItem->GetCommand() == SvxSearchCmd::REPLACE + && !mpSearchItem->equalsIgnoring(*pSearchItem, /*bIgnoreReplace=*/true, + /*bIgnoreCommand=*/true)) + { + EndSpelling(); + mbPrepareSpellingPending = true; + } + + if (mbPrepareSpellingPending) + PrepareSpelling(); + sd::ViewShellBase* pBase = getViewShellBase(); + // Determine whether we have to abort the search. This is necessary + // when the main view shell does not support searching. + bool bAbort = false; + if (pBase != nullptr) + { + std::shared_ptr<sd::ViewShell> pShell (pBase->GetMainViewShell()); + SetViewShell(pShell); + if (pShell == nullptr) + bAbort = true; + else + switch (pShell->GetShellType()) + { + case sd::ViewShell::ST_DRAW: + case sd::ViewShell::ST_IMPRESS: + case sd::ViewShell::ST_NOTES: + case sd::ViewShell::ST_HANDOUT: + case sd::ViewShell::ST_OUTLINE: + bAbort = false; + break; + default: + bAbort = true; + break; + } + } + + std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock()); + if ( ! pViewShell) + { + OSL_ASSERT(pViewShell); + return true; + } + + if ( ! bAbort) + { + meMode = SEARCH; + mpSearchItem.reset(pSearchItem->Clone()); + + mbFoundObject = false; + + Initialize ( ! mpSearchItem->GetBackward()); + + const SvxSearchCmd nCommand (mpSearchItem->GetCommand()); + if (nCommand == SvxSearchCmd::FIND_ALL || nCommand == SvxSearchCmd::REPLACE_ALL) + { + bEndOfSearch = SearchAndReplaceAll (); + } + else + { + RememberStartPosition (); + bEndOfSearch = SearchAndReplaceOnce (); + // restore start position if nothing was found + if(!mbStringFound) + { + RestoreStartPosition (); + // Nothing was changed, no need to restart the spellchecker. + if (nCommand == SvxSearchCmd::FIND) + bEndOfSearch = false; + } + mnStartPageIndex = sal_uInt16(-1); + } + } + + mpDrawDocument->GetDocSh()->SetWaitCursor( false ); + + return bEndOfSearch; +} + +void SdOutliner::Initialize (bool bDirectionIsForward) +{ + const bool bIsAtEnd (maObjectIterator == sd::outliner::OutlinerContainer(this).end()); + const bool bOldDirectionIsForward = mbDirectionIsForward; + mbDirectionIsForward = bDirectionIsForward; + + if (maObjectIterator == sd::outliner::Iterator()) + { + // Initialize a new search. + maObjectIterator = sd::outliner::OutlinerContainer(this).current(); + maCurrentPosition = *maObjectIterator; + + std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock()); + if ( ! pViewShell) + { + OSL_ASSERT(pViewShell); + return; + } + + // In case we are searching in an outline view then first remove the + // current selection and place cursor at its start or end. + if( nullptr != dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() )) + { + ESelection aSelection = getOutlinerView()->GetSelection (); + if (mbDirectionIsForward) + { + aSelection.nEndPara = aSelection.nStartPara; + aSelection.nEndPos = aSelection.nStartPos; + } + else + { + aSelection.nStartPara = aSelection.nEndPara; + aSelection.nStartPos = aSelection.nEndPos; + } + getOutlinerView()->SetSelection (aSelection); + } + + // When not beginning the search at the beginning of the search area + // then there may be matches before the current position. + mbMatchMayExist = (maObjectIterator!=sd::outliner::OutlinerContainer(this).begin()); + } + else if (bOldDirectionIsForward != mbDirectionIsForward) + { + // Requested iteration direction has changed. Turn around the iterator. + maObjectIterator.Reverse(); + if (bIsAtEnd) + { + // The iterator has pointed to end(), which after the search + // direction is reversed, becomes begin(). + maObjectIterator = sd::outliner::OutlinerContainer(this).begin(); + } + else + { + // The iterator has pointed to the object one ahead/before the current + // one. Now move it to the one before/ahead the current one. + ++maObjectIterator; + if (maObjectIterator != sd::outliner::OutlinerContainer(this).end()) + { + ++maObjectIterator; + } + } + + mbMatchMayExist = true; + } + + // Initialize the last valid position with where the search starts so + // that it always points to a valid position. + maLastValidPosition = *sd::outliner::OutlinerContainer(this).current(); +} + +bool SdOutliner::SearchAndReplaceAll() +{ + bool bRet = true; + + // Save the current position to be restored after having replaced all + // matches. + RememberStartPosition (); + + std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock()); + if ( ! pViewShell) + { + OSL_ASSERT(pViewShell); + return true; + } + + std::vector<sd::SearchSelection> aSelections; + if( nullptr != dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() )) + { + // Put the cursor to the beginning/end of the outliner. + getOutlinerView()->SetSelection (GetSearchStartPosition ()); + + // The outliner does all the work for us when we are in this mode. + SearchAndReplaceOnce(); + } + else if( nullptr != dynamic_cast< const sd::DrawViewShell *>( pViewShell.get() )) + { + // Disable selection change notifications during search all. + SfxViewShell& rSfxViewShell = pViewShell->GetViewShellBase(); + rSfxViewShell.setTiledSearching(true); + comphelper::ScopeGuard aGuard([&rSfxViewShell]() + { + rSfxViewShell.setTiledSearching(false); + }); + + // Go to beginning/end of document. + maObjectIterator = sd::outliner::OutlinerContainer(this).begin(); + // Switch to the first object which contains the search string. + ProvideNextTextObject(); + if( !mbStringFound ) + { + RestoreStartPosition (); + mnStartPageIndex = sal_uInt16(-1); + return true; + } + // Reset the iterator back to the beginning + maObjectIterator = sd::outliner::OutlinerContainer(this).begin(); + + // Search/replace until the end of the document is reached. + bool bFoundMatch; + do + { + bFoundMatch = ! SearchAndReplaceOnce(&aSelections); + if (mpSearchItem->GetCommand() == SvxSearchCmd::FIND_ALL && comphelper::LibreOfficeKit::isActive() && bFoundMatch && aSelections.size() == 1) + { + // Without this, RememberStartPosition() will think it already has a remembered position. + mnStartPageIndex = sal_uInt16(-1); + + RememberStartPosition(); + + // So when RestoreStartPosition() restores the first match, then spellchecker doesn't kill the selection. + bRet = false; + } + } + while (bFoundMatch); + + if (mpSearchItem->GetCommand() == SvxSearchCmd::FIND_ALL && comphelper::LibreOfficeKit::isActive() && !aSelections.empty()) + { + boost::property_tree::ptree aTree; + aTree.put("searchString", mpSearchItem->GetSearchString().toUtf8().getStr()); + aTree.put("highlightAll", true); + + boost::property_tree::ptree aChildren; + for (const sd::SearchSelection& rSelection : aSelections) + { + boost::property_tree::ptree aChild; + aChild.put("part", OString::number(rSelection.m_nPage).getStr()); + aChild.put("rectangles", rSelection.m_aRectangles.getStr()); + aChildren.push_back(std::make_pair("", aChild)); + } + aTree.add_child("searchResultSelection", aChildren); + + std::stringstream aStream; + boost::property_tree::write_json(aStream, aTree); + OString aPayload = aStream.str().c_str(); + rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_RESULT_SELECTION, aPayload.getStr()); + } + } + + RestoreStartPosition (); + + if (mpSearchItem->GetCommand() == SvxSearchCmd::FIND_ALL && comphelper::LibreOfficeKit::isActive() && !bRet) + { + // Find-all, tiled rendering and we have at least one match. + OString aPayload = OString::number(mnStartPageIndex); + SfxViewShell& rSfxViewShell = pViewShell->GetViewShellBase(); + rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_SET_PART, aPayload.getStr()); + + // Emit a selection callback here: + // 1) The original one is no longer valid, as we there was a SET_PART in between + // 2) The underlying editeng will only talk about the first match till + // it doesn't support multi-selection. + std::vector<OString> aRectangles; + for (const sd::SearchSelection& rSelection : aSelections) + { + if (rSelection.m_nPage == mnStartPageIndex) + aRectangles.push_back(rSelection.m_aRectangles); + } + OString sRectangles = comphelper::string::join("; ", aRectangles); + rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, sRectangles.getStr()); + } + + mnStartPageIndex = sal_uInt16(-1); + + return bRet; +} + +namespace +{ + +basegfx::B2DRectangle getPDFSelection(const std::unique_ptr<VectorGraphicSearch> & rVectorGraphicSearch, + const SdrObject* pObject) +{ + basegfx::B2DRectangle aSelection; + + auto const & rTextRectangles = rVectorGraphicSearch->getTextRectangles(); + if (rTextRectangles.empty()) + return aSelection; + + basegfx::B2DSize aPdfPageSizeHMM = rVectorGraphicSearch->pageSize(); + + basegfx::B2DRectangle aObjectB2DRectHMM(vcl::unotools::b2DRectangleFromRectangle(pObject->GetLogicRect())); + + // Setup coordinate conversion matrix to convert the inner PDF + // coordinates to the page relative coordinates + basegfx::B2DHomMatrix aB2DMatrix; + + aB2DMatrix.scale(aObjectB2DRectHMM.getWidth() / aPdfPageSizeHMM.getX(), + aObjectB2DRectHMM.getHeight() / aPdfPageSizeHMM.getY()); + + aB2DMatrix.translate(aObjectB2DRectHMM.getMinX(), aObjectB2DRectHMM.getMinY()); + + + for (auto const & rRectangle : rVectorGraphicSearch->getTextRectangles()) + { + basegfx::B2DRectangle aRectangle(rRectangle); + aRectangle *= aB2DMatrix; + + if (aSelection.isEmpty()) + aSelection = aRectangle; + else + aSelection.expand(aRectangle); + } + + return aSelection; +} + +} // end namespace + +void SdOutliner::sendLOKSearchResultCallback(const std::shared_ptr<sd::ViewShell> & pViewShell, + const OutlinerView* pOutlinerView, + std::vector<sd::SearchSelection>* pSelections) +{ + std::vector<::tools::Rectangle> aLogicRects; + auto& rVectorGraphicSearchContext = mpImpl->getVectorGraphicSearchContext(); + if (rVectorGraphicSearchContext.mbCurrentIsVectorGraphic) + { + basegfx::B2DRectangle aSelectionHMM = getPDFSelection(rVectorGraphicSearchContext.mpVectorGraphicSearch, mpObj); + + tools::Rectangle aSelection(Point(aSelectionHMM.getMinX(), aSelectionHMM.getMinY()), + Size(aSelectionHMM.getWidth(), aSelectionHMM.getHeight())); + aSelection = o3tl::convert(aSelection, o3tl::Length::mm100, o3tl::Length::twip); + aLogicRects.push_back(aSelection); + } + else + { + pOutlinerView->GetSelectionRectangles(aLogicRects); + + // convert to twips if in 100thmm (seems as if LibreOfficeKit is based on twips?). Do this + // here where we have the only place needing this, *not* in ImpEditView::GetSelectionRectangles + // which makes that method unusable for others + if (pOutlinerView->GetWindow() && MapUnit::Map100thMM == pOutlinerView->GetWindow()->GetMapMode().GetMapUnit()) + { + for (tools::Rectangle& rRectangle : aLogicRects) + { + rRectangle = o3tl::convert(rRectangle, o3tl::Length::mm100, o3tl::Length::twip); + } + } + } + + std::vector<OString> aLogicRectStrings; + std::transform(aLogicRects.begin(), aLogicRects.end(), std::back_inserter(aLogicRectStrings), + [](const ::tools::Rectangle& rRectangle) + { + return rRectangle.toString(); + }); + + OString sRectangles = comphelper::string::join("; ", aLogicRectStrings); + + if (!pSelections) + { + // notify LibreOfficeKit about changed page + OString aPayload = OString::number(maCurrentPosition.mnPageIndex); + SfxViewShell& rSfxViewShell = pViewShell->GetViewShellBase(); + rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_SET_PART, aPayload.getStr()); + + // also about search result selections + boost::property_tree::ptree aTree; + aTree.put("searchString", mpSearchItem->GetSearchString().toUtf8().getStr()); + aTree.put("highlightAll", false); + + boost::property_tree::ptree aChildren; + boost::property_tree::ptree aChild; + aChild.put("part", OString::number(maCurrentPosition.mnPageIndex).getStr()); + aChild.put("rectangles", sRectangles.getStr()); + aChildren.push_back(std::make_pair("", aChild)); + aTree.add_child("searchResultSelection", aChildren); + + std::stringstream aStream; + boost::property_tree::write_json(aStream, aTree); + aPayload = aStream.str().c_str(); + rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_RESULT_SELECTION, aPayload.getStr()); + + if (rVectorGraphicSearchContext.mbCurrentIsVectorGraphic) + { + rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, sRectangles.getStr()); + } + } + else + { + sd::SearchSelection aSelection(maCurrentPosition.mnPageIndex, sRectangles); + bool bDuplicate = !pSelections->empty() && pSelections->back() == aSelection; + if (!bDuplicate) + pSelections->push_back(aSelection); + } +} + +bool SdOutliner::SearchAndReplaceOnce(std::vector<sd::SearchSelection>* pSelections) +{ + DetectChange (); + + std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock()); + + if (!getOutlinerView() || !GetEditEngine().HasView(&getOutlinerView()->GetEditView())) + { + std::shared_ptr<sd::DrawViewShell> pDrawViewShell ( + std::dynamic_pointer_cast<sd::DrawViewShell>(pViewShell)); + + // Perhaps the user switched to a different page/slide between searches. + // If so, reset the starting search position to the current slide like DetectChange does + if (pDrawViewShell && pDrawViewShell->GetCurPagePos() != maCurrentPosition.mnPageIndex) + maObjectIterator = sd::outliner::OutlinerContainer(this).current(); + + mpImpl->ProvideOutlinerView(*this, pViewShell, mpWindow); + } + + if (pViewShell) + { + mpView = pViewShell->GetView(); + mpWindow = pViewShell->GetActiveWindow(); + getOutlinerView()->SetWindow(mpWindow); + auto& rVectorGraphicSearchContext = mpImpl->getVectorGraphicSearchContext(); + if (nullptr != dynamic_cast<const sd::DrawViewShell*>(pViewShell.get())) + { + sal_uLong nMatchCount = 0; + + if (rVectorGraphicSearchContext.mbCurrentIsVectorGraphic) + { + OUString const & rString = mpSearchItem->GetSearchString(); + bool bBackwards = mpSearchItem->GetBackward(); + + VectorGraphicSearchOptions aOptions; + aOptions.meStartPosition = bBackwards ? SearchStartPosition::End : SearchStartPosition::Begin; + aOptions.mbMatchCase = mpSearchItem->GetExact(); + aOptions.mbMatchWholeWord = mpSearchItem->GetWordOnly(); + + bool bResult = rVectorGraphicSearchContext.mpVectorGraphicSearch->search(rString, aOptions); + + if (bResult) + { + if (bBackwards) + bResult = rVectorGraphicSearchContext.mpVectorGraphicSearch->previous(); + else + bResult = rVectorGraphicSearchContext.mpVectorGraphicSearch->next(); + } + + if (bResult) + { + nMatchCount = 1; + + SdrPageView* pPageView = mpView->GetSdrPageView(); + mpView->UnmarkAllObj(pPageView); + + std::vector<basegfx::B2DRectangle> aSubSelections; + basegfx::B2DRectangle aSubSelection = getPDFSelection(rVectorGraphicSearchContext.mpVectorGraphicSearch, mpObj); + if (!aSubSelection.isEmpty()) + aSubSelections.push_back(aSubSelection); + mpView->MarkObj(mpObj, pPageView, false, false, std::move(aSubSelections)); + } + else + { + rVectorGraphicSearchContext.reset(); + } + } + else + { + // When replacing we first check if there is a selection + // indicating a match. If there is then replace it. The + // following call to StartSearchAndReplace will then search for + // the next match. + if (meMode == SEARCH && mpSearchItem->GetCommand() == SvxSearchCmd::REPLACE) + { + if (getOutlinerView()->GetSelection().HasRange()) + getOutlinerView()->StartSearchAndReplace(*mpSearchItem); + } + + // Search for the next match. + if (mpSearchItem->GetCommand() != SvxSearchCmd::REPLACE_ALL) + { + nMatchCount = getOutlinerView()->StartSearchAndReplace(*mpSearchItem); + } + } + + // Go to the next text object when there have been no matches in + // the current object or the whole object has already been + // processed. + if (nMatchCount==0 || mpSearchItem->GetCommand()==SvxSearchCmd::REPLACE_ALL) + { + ProvideNextTextObject (); + + if (!mbEndOfSearch && !rVectorGraphicSearchContext.mbCurrentIsVectorGraphic) + { + // Remember the current position as the last one with a + // text object. + maLastValidPosition = maCurrentPosition; + + // Now that the mbEndOfSearch flag guards this block the + // following assertion and return should not be + // necessary anymore. + DBG_ASSERT(GetEditEngine().HasView(&getOutlinerView()->GetEditView() ), + "SearchAndReplace without valid view!" ); + if ( ! GetEditEngine().HasView( &getOutlinerView()->GetEditView() ) ) + { + mpDrawDocument->GetDocSh()->SetWaitCursor( false ); + return true; + } + + if (meMode == SEARCH) + getOutlinerView()->StartSearchAndReplace(*mpSearchItem); + } + } + } + else if (nullptr != dynamic_cast<const sd::OutlineViewShell*>(pViewShell.get())) + { + mpDrawDocument->GetDocSh()->SetWaitCursor(false); + // The following loop is executed more than once only when a + // wrap around search is done. + while (true) + { + int nResult = getOutlinerView()->StartSearchAndReplace(*mpSearchItem); + if (nResult == 0) + { + if (HandleFailedSearch ()) + { + getOutlinerView()->SetSelection (GetSearchStartPosition ()); + continue; + } + } + else + mbStringFound = true; + break; + } + } + } + + mpDrawDocument->GetDocSh()->SetWaitCursor( false ); + + if (pViewShell && comphelper::LibreOfficeKit::isActive() && mbStringFound) + { + sendLOKSearchResultCallback(pViewShell, getOutlinerView(), pSelections); + } + + return mbEndOfSearch; +} + +/** Try to detect whether the document or the view (shell) has changed since + the last time <member>StartSearchAndReplace()</member> has been called. +*/ +void SdOutliner::DetectChange() +{ + sd::outliner::IteratorPosition aPosition (maCurrentPosition); + + std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock()); + std::shared_ptr<sd::DrawViewShell> pDrawViewShell ( + std::dynamic_pointer_cast<sd::DrawViewShell>(pViewShell)); + + // Detect whether the view has been switched from the outside. + if (pDrawViewShell != nullptr + && (aPosition.meEditMode != pDrawViewShell->GetEditMode() + || aPosition.mePageKind != pDrawViewShell->GetPageKind())) + { + // Either the edit mode or the page kind has changed. + SetStatusEventHdl(Link<EditStatus&,void>()); + + SdrPageView* pPageView = mpView->GetSdrPageView(); + if (pPageView != nullptr) + mpView->UnmarkAllObj (pPageView); + mpView->SdrEndTextEdit(); + SetUpdateLayout(false); + OutlinerView* pOutlinerView = getOutlinerView(); + if (pOutlinerView != nullptr) + pOutlinerView->SetOutputArea( ::tools::Rectangle( Point(), Size(1, 1) ) ); + if (meMode == SPELL) + SetPaperSize( Size(1, 1) ); + SetText(OUString(), GetParagraph(0)); + + RememberStartPosition (); + + mnPageCount = mpDrawDocument->GetSdPageCount(pDrawViewShell->GetPageKind()); + maObjectIterator = sd::outliner::OutlinerContainer(this).current(); + } + + // Detect change of the set of selected objects. If their number has + // changed start again with the first selected object. + else if (DetectSelectionChange()) + { + HandleChangedSelection (); + maObjectIterator = sd::outliner::OutlinerContainer(this).current(); + } + + // Detect change of page count. Restart search at first/last page in + // that case. + else if (aPosition.meEditMode == EditMode::Page + && mpDrawDocument->GetSdPageCount(aPosition.mePageKind) != mnPageCount) + { + // The number of pages has changed. + mnPageCount = mpDrawDocument->GetSdPageCount(aPosition.mePageKind); + maObjectIterator = sd::outliner::OutlinerContainer(this).current(); + } + else if (aPosition.meEditMode == EditMode::MasterPage + && mpDrawDocument->GetSdPageCount(aPosition.mePageKind) != mnPageCount) + { + // The number of master pages has changed. + mnPageCount = mpDrawDocument->GetSdPageCount(aPosition.mePageKind); + maObjectIterator = sd::outliner::OutlinerContainer(this).current(); + } +} + +bool SdOutliner::DetectSelectionChange() +{ + bool bSelectionHasChanged = false; + + // If mpObj is NULL then we have not yet found our first match. + // Detecting a change makes no sense. + if (mpObj != nullptr) + { + const size_t nMarkCount = mpView ? mpView->GetMarkedObjectList().GetMarkCount() : 0; + switch (nMarkCount) + { + case 0: + // The selection has changed when previously there have been + // selected objects. + bSelectionHasChanged = mbRestrictSearchToSelection; + break; + case 1: + // Check if the only selected object is not the one that we + // had selected. + if (mpView != nullptr) + { + SdrMark* pMark = mpView->GetMarkedObjectList().GetMark(0); + if (pMark != nullptr) + bSelectionHasChanged = (mpObj != pMark->GetMarkedSdrObj ()); + } + break; + default: + // We had selected exactly one object. + bSelectionHasChanged = true; + break; + } + } + + return bSelectionHasChanged; +} + +void SdOutliner::RememberStartPosition() +{ + std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock()); + if ( ! pViewShell) + { + OSL_ASSERT(pViewShell); + return; + } + + if ( mnStartPageIndex != sal_uInt16(-1) ) + return; + + if( nullptr != dynamic_cast< const sd::DrawViewShell *>( pViewShell.get() )) + { + std::shared_ptr<sd::DrawViewShell> pDrawViewShell ( + std::dynamic_pointer_cast<sd::DrawViewShell>(pViewShell)); + if (pDrawViewShell != nullptr) + { + meStartViewMode = pDrawViewShell->GetPageKind(); + meStartEditMode = pDrawViewShell->GetEditMode(); + mnStartPageIndex = pDrawViewShell->GetCurPagePos(); + } + + if (mpView != nullptr) + { + mpStartEditedObject = mpView->GetTextEditObject(); + if (mpStartEditedObject != nullptr) + { + // Try to retrieve current caret position only when there is an + // edited object. + ::Outliner* pOutliner = + static_cast<sd::DrawView*>(mpView)->GetTextEditOutliner(); + if (pOutliner!=nullptr && pOutliner->GetViewCount()>0) + { + OutlinerView* pOutlinerView = pOutliner->GetView(0); + maStartSelection = pOutlinerView->GetSelection(); + } + } + } + } + else if( nullptr != dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() )) + { + // Remember the current cursor position. + OutlinerView* pView = GetView(0); + if (pView != nullptr) + pView->GetSelection(); + } + else + { + mnStartPageIndex = sal_uInt16(-1); + } +} + +void SdOutliner::RestoreStartPosition() +{ + bool bRestore = true; + // Take a negative start page index as indicator that restoring the + // start position is not requested. + if (mnStartPageIndex == sal_uInt16(-1) ) + bRestore = false; + // Don't restore when the view shell is not valid. + std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock()); + if (pViewShell == nullptr) + bRestore = false; + + if (!bRestore) + return; + + if( nullptr != dynamic_cast< const sd::DrawViewShell *>( pViewShell.get() )) + { + std::shared_ptr<sd::DrawViewShell> pDrawViewShell ( + std::dynamic_pointer_cast<sd::DrawViewShell>(pViewShell)); + SetViewMode (meStartViewMode); + if (pDrawViewShell != nullptr) + { + SetPage (meStartEditMode, mnStartPageIndex); + mpObj = mpStartEditedObject; + if (mpObj) + { + PutTextIntoOutliner(); + EnterEditMode(false); + if (getOutlinerView()) + getOutlinerView()->SetSelection(maStartSelection); + } + } + } + else if( nullptr != dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() )) + { + // Set cursor to its old position. + OutlinerView* pView = GetView(0); + if (pView != nullptr) + pView->SetSelection (maStartSelection); + } +} + +namespace +{ + +bool lclIsValidTextObject(const sd::outliner::IteratorPosition& rPosition) +{ + auto* pObject = dynamic_cast< SdrTextObj* >( rPosition.mxObject.get() ); + return (pObject != nullptr) && pObject->HasText() && ! pObject->IsEmptyPresObj(); +} + +bool isValidVectorGraphicObject(const sd::outliner::IteratorPosition& rPosition) +{ + auto* pGraphicObject = dynamic_cast<SdrGrafObj*>(rPosition.mxObject.get()); + if (pGraphicObject) + { + auto const& pVectorGraphicData = pGraphicObject->GetGraphic().getVectorGraphicData(); + if (pVectorGraphicData && VectorGraphicDataType::Pdf == pVectorGraphicData->getType()) + { + return true; + } + } + return false; +} + +} // end anonymous namespace + + +/** The main purpose of this method is to iterate over all shape objects of + the search area (current selection, current view, or whole document) + until a text object has been found that contains at least one match or + until no such object can be found anymore. These two conditions are + expressed by setting one of the flags <member>mbFoundObject</member> or + <member>mbEndOfSearch</member> to <TRUE/>. +*/ +void SdOutliner::ProvideNextTextObject() +{ + mbEndOfSearch = false; + mbFoundObject = false; + + // reset the vector search + auto& rVectorGraphicSearchContext = mpImpl->getVectorGraphicSearchContext(); + rVectorGraphicSearchContext.reset(); + + mpView->UnmarkAllObj (mpView->GetSdrPageView()); + try + { + mpView->SdrEndTextEdit(); + } + catch (const css::uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("sd.view"); + } + SetUpdateLayout(false); + OutlinerView* pOutlinerView = getOutlinerView(); + if (pOutlinerView != nullptr) + pOutlinerView->SetOutputArea( ::tools::Rectangle( Point(), Size(1, 1) ) ); + if (meMode == SPELL) + SetPaperSize( Size(1, 1) ); + SetText(OUString(), GetParagraph(0)); + + mpSearchSpellTextObj = nullptr; + + // Iterate until a valid text object has been found or the search ends. + do + { + mpObj = nullptr; + mpParaObj = nullptr; + + if (maObjectIterator != sd::outliner::OutlinerContainer(this).end()) + { + maCurrentPosition = *maObjectIterator; + + // LOK: do not descent to notes or master pages when searching + bool bForbiddenPage = comphelper::LibreOfficeKit::isActive() && (maCurrentPosition.mePageKind != PageKind::Standard || maCurrentPosition.meEditMode != EditMode::Page); + + rVectorGraphicSearchContext.reset(); + + if (!bForbiddenPage) + { + // Switch to the current object only if it is a valid text object. + if (lclIsValidTextObject(maCurrentPosition)) + { + // Don't set yet in case of searching: the text object may not match. + if (meMode != SEARCH) + mpObj = SetObject(maCurrentPosition); + else + mpObj = maCurrentPosition.mxObject.get(); + } + // Or if the object is a valid graphic object which contains vector graphic + else if (meMode == SEARCH && isValidVectorGraphicObject(maCurrentPosition)) + { + mpObj = maCurrentPosition.mxObject.get(); + rVectorGraphicSearchContext.mbCurrentIsVectorGraphic = true; + } + } + + // Advance to the next object + ++maObjectIterator; + + if (mpObj) + { + if (rVectorGraphicSearchContext.mbCurrentIsVectorGraphic) + { + // We know here the object is a SdrGrafObj and that it + // contains a vector graphic + auto* pGraphicObject = static_cast<SdrGrafObj*>(mpObj); + OUString const & rString = mpSearchItem->GetSearchString(); + bool bBackwards = mpSearchItem->GetBackward(); + + VectorGraphicSearchOptions aOptions; + aOptions.meStartPosition = bBackwards ? SearchStartPosition::End : SearchStartPosition::Begin; + aOptions.mbMatchCase = mpSearchItem->GetExact(); + aOptions.mbMatchWholeWord = mpSearchItem->GetWordOnly(); + + rVectorGraphicSearchContext.mpVectorGraphicSearch = std::make_unique<VectorGraphicSearch>(pGraphicObject->GetGraphic()); + + bool bResult = rVectorGraphicSearchContext.mpVectorGraphicSearch->search(rString, aOptions); + if (bResult) + { + if (bBackwards) + bResult = rVectorGraphicSearchContext.mpVectorGraphicSearch->previous(); + else + bResult = rVectorGraphicSearchContext.mpVectorGraphicSearch->next(); + } + + if (bResult) + { + mpObj = SetObject(maCurrentPosition); + + mbStringFound = true; + mbMatchMayExist = true; + mbFoundObject = true; + + SdrPageView* pPageView = mpView->GetSdrPageView(); + mpView->UnmarkAllObj(pPageView); + + std::vector<basegfx::B2DRectangle> aSubSelections; + basegfx::B2DRectangle aSubSelection = getPDFSelection(rVectorGraphicSearchContext.mpVectorGraphicSearch, mpObj); + if (!aSubSelection.isEmpty()) + aSubSelections.push_back(aSubSelection); + + mpView->MarkObj(mpObj, pPageView, false, false, std::move(aSubSelections)); + + mpDrawDocument->GetDocSh()->SetWaitCursor( false ); + } + else + { + rVectorGraphicSearchContext.reset(); + } + } + else + { + PutTextIntoOutliner(); + + std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock()); + if (pViewShell != nullptr) + { + switch (meMode) + { + case SEARCH: + PrepareSearchAndReplace (); + break; + case SPELL: + PrepareSpellCheck (); + break; + case TEXT_CONVERSION: + PrepareConversion(); + break; + } + } + } + } + } + else + { + rVectorGraphicSearchContext.reset(); + + if (meMode == SEARCH) + // Instead of doing a full-blown SetObject(), which would do the same -- but would also possibly switch pages. + mbStringFound = false; + + mbEndOfSearch = true; + EndOfSearch (); + } + } + while ( ! (mbFoundObject || mbEndOfSearch)); +} + +void SdOutliner::EndOfSearch() +{ + std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock()); + if ( ! pViewShell) + { + OSL_ASSERT(pViewShell); + return; + } + + // Before we display a dialog we first jump to where the last valid text + // object was found. All page and view mode switching since then was + // temporary and should not be visible to the user. + if( nullptr == dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() )) + SetObject (maLastValidPosition); + + if (mbRestrictSearchToSelection) + ShowEndOfSearchDialog (); + else + { + // When no match has been found so far then terminate the search. + if ( ! mbMatchMayExist) + { + ShowEndOfSearchDialog (); + mbEndOfSearch = true; + } + // Ask the user whether to wrap around and continue the search or + // to terminate. + else if (meMode==TEXT_CONVERSION || ShowWrapAroundDialog ()) + { + mbMatchMayExist = false; + // Everything back to beginning (or end?) of the document. + maObjectIterator = sd::outliner::OutlinerContainer(this).begin(); + if( nullptr != dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() )) + { + // Set cursor to first character of the document. + OutlinerView* pOutlinerView = getOutlinerView(); + if (pOutlinerView != nullptr) + pOutlinerView->SetSelection (GetSearchStartPosition ()); + } + + mbEndOfSearch = false; + } + else + { + // No wrap around. + mbEndOfSearch = true; + } + } +} + +void SdOutliner::ShowEndOfSearchDialog() +{ + if (meMode == SEARCH) + { + if (!mbStringFound) + { + SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::NotFound); + std::shared_ptr<sd::ViewShell> pViewShell(mpWeakViewShell.lock()); + if (pViewShell) + { + SfxViewShell& rSfxViewShell = pViewShell->GetViewShellBase(); + rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_NOT_FOUND, mpSearchItem->GetSearchString().toUtf8().getStr()); + } + } + + // don't do anything else for search + return; + } + + OUString aString; + if (mpView->AreObjectsMarked()) + aString = SdResId(STR_END_SPELLING_OBJ); + else + aString = SdResId(STR_END_SPELLING); + + // Show the message in an info box that is modal with respect to the whole application. + weld::Window* pParent = GetMessageBoxParent(); + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pParent, + VclMessageType::Info, VclButtonsType::Ok, aString)); + xInfoBox->run(); +} + +bool SdOutliner::ShowWrapAroundDialog() +{ + // Determine whether to show the dialog. + if (mpSearchItem) + { + // When searching display the dialog only for single find&replace. + const SvxSearchCmd nCommand(mpSearchItem->GetCommand()); + if (nCommand == SvxSearchCmd::REPLACE || nCommand == SvxSearchCmd::FIND) + { + if (mbDirectionIsForward) + SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::End); + else + SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::Start); + + return true; + } + else + return false; + } + + // show dialog only for spelling + if (meMode != SPELL) + return false; + + // The question text depends on the search direction. + bool bImpress = mpDrawDocument && mpDrawDocument->GetDocumentType() == DocumentType::Impress; + + TranslateId pStringId; + if (mbDirectionIsForward) + pStringId = bImpress ? STR_SAR_WRAP_FORWARD : STR_SAR_WRAP_FORWARD_DRAW; + else + pStringId = bImpress ? STR_SAR_WRAP_BACKWARD : STR_SAR_WRAP_BACKWARD_DRAW; + + // Pop up question box that asks the user whether to wrap around. + // The dialog is made modal with respect to the whole application. + weld::Window* pParent = GetMessageBoxParent(); + std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(pParent, + VclMessageType::Question, VclButtonsType::YesNo, SdResId(pStringId))); + sal_uInt16 nBoxResult = xQueryBox->run(); + + return (nBoxResult == RET_YES); +} + +void SdOutliner::PutTextIntoOutliner() +{ + mpSearchSpellTextObj = dynamic_cast<SdrTextObj*>( mpObj ); + if ( mpSearchSpellTextObj && mpSearchSpellTextObj->HasText() && !mpSearchSpellTextObj->IsEmptyPresObj() ) + { + SdrText* pText = mpSearchSpellTextObj->getText( maCurrentPosition.mnText ); + mpParaObj = pText ? pText->GetOutlinerParaObject() : nullptr; + + if (mpParaObj != nullptr) + { + SetText(*mpParaObj); + + ClearModifyFlag(); + } + } + else + { + mpSearchSpellTextObj = nullptr; + } +} + +void SdOutliner::PrepareSpellCheck() +{ + EESpellState eState = HasSpellErrors(); + DBG_ASSERT(eState != EESpellState::NoSpeller, "No SpellChecker"); + + if (eState == EESpellState::Ok) + return; + + // When spell checking we have to test whether we have processed the + // whole document and have reached the start page again. + if (meMode == SPELL) + { + if (maSearchStartPosition == sd::outliner::Iterator()) + // Remember the position of the first text object so that we + // know when we have processed the whole document. + maSearchStartPosition = maObjectIterator; + else if (maSearchStartPosition == maObjectIterator) + { + mbEndOfSearch = true; + } + } + + EnterEditMode( false ); +} + +void SdOutliner::PrepareSearchAndReplace() +{ + if (!HasText( *mpSearchItem )) + return; + + // Set the object now that we know it matches. + mpObj = SetObject(maCurrentPosition); + + mbStringFound = true; + mbMatchMayExist = true; + + EnterEditMode(false); + + mpDrawDocument->GetDocSh()->SetWaitCursor( false ); + // Start search at the right end of the current object's text + // depending on the search direction. + OutlinerView* pOutlinerView = getOutlinerView(); + if (pOutlinerView != nullptr) + pOutlinerView->SetSelection (GetSearchStartPosition ()); +} + +void SdOutliner::SetViewMode (PageKind ePageKind) +{ + std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock()); + std::shared_ptr<sd::DrawViewShell> pDrawViewShell( + std::dynamic_pointer_cast<sd::DrawViewShell>(pViewShell)); + if (pDrawViewShell == nullptr || ePageKind == pDrawViewShell->GetPageKind()) + return; + + // Restore old edit mode. + pDrawViewShell->ChangeEditMode(mpImpl->meOriginalEditMode, false); + + SetStatusEventHdl(Link<EditStatus&,void>()); + OUString sViewURL; + switch (ePageKind) + { + case PageKind::Standard: + default: + sViewURL = sd::framework::FrameworkHelper::msImpressViewURL; + break; + case PageKind::Notes: + sViewURL = sd::framework::FrameworkHelper::msNotesViewURL; + break; + case PageKind::Handout: + sViewURL = sd::framework::FrameworkHelper::msHandoutViewURL; + break; + } + // The text object iterator is destroyed when the shells are + // switched but we need it so save it and restore it afterwards. + sd::outliner::Iterator aIterator (maObjectIterator); + bool bMatchMayExist = mbMatchMayExist; + + sd::ViewShellBase& rBase = pViewShell->GetViewShellBase(); + + rtl::Reference<sd::FuSearch> xFuSearch; + if (pViewShell->GetView()) + xFuSearch = pViewShell->GetView()->getSearchContext().getFunctionSearch(); + + SetViewShell(std::shared_ptr<sd::ViewShell>()); + sd::framework::FrameworkHelper::Instance(rBase)->RequestView( + sViewURL, + sd::framework::FrameworkHelper::msCenterPaneURL); + + // Force (well, request) a synchronous update of the configuration. + // In a better world we would handle the asynchronous view update + // instead. But that would involve major restructuring of the + // Outliner code. + sd::framework::FrameworkHelper::Instance(rBase)->RequestSynchronousUpdate(); + + auto pNewViewShell = rBase.GetMainViewShell(); + SetViewShell(pNewViewShell); + if (xFuSearch.is() && pNewViewShell->GetView()) + pNewViewShell->GetView()->getSearchContext().setSearchFunction(xFuSearch); + + // Switching to another view shell has intermediatly called + // EndSpelling(). A PrepareSpelling() is pending, so call that now. + PrepareSpelling(); + + // Update the number of pages so that + // <member>DetectChange()</member> has the correct value to compare + // to. + mnPageCount = mpDrawDocument->GetSdPageCount(ePageKind); + + maObjectIterator = aIterator; + mbMatchMayExist = bMatchMayExist; + + // Save edit mode so that it can be restored when switching the view + // shell again. + pDrawViewShell = std::dynamic_pointer_cast<sd::DrawViewShell>(pViewShell); + OSL_ASSERT(pDrawViewShell != nullptr); + if (pDrawViewShell != nullptr) + mpImpl->meOriginalEditMode = pDrawViewShell->GetEditMode(); +} + +void SdOutliner::SetPage (EditMode eEditMode, sal_uInt16 nPageIndex) +{ + if ( ! mbRestrictSearchToSelection) + { + std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock()); + std::shared_ptr<sd::DrawViewShell> pDrawViewShell( + std::dynamic_pointer_cast<sd::DrawViewShell>(pViewShell)); + OSL_ASSERT(pDrawViewShell != nullptr); + if (pDrawViewShell != nullptr) + { + pDrawViewShell->ChangeEditMode(eEditMode, false); + pDrawViewShell->SwitchPage(nPageIndex); + } + } +} + +void SdOutliner::EnterEditMode (bool bGrabFocus) +{ + OutlinerView* pOutlinerView = getOutlinerView(); + if (!(pOutlinerView && mpSearchSpellTextObj)) + return; + + pOutlinerView->SetOutputArea( ::tools::Rectangle( Point(), Size(1, 1))); + SetPaperSize( mpSearchSpellTextObj->GetLogicRect().GetSize() ); + SdrPageView* pPV = mpView->GetSdrPageView(); + + // Make FuText the current function. + SfxUInt16Item aItem (SID_TEXTEDIT, 1); + std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock()); + if (!(pViewShell && pViewShell->GetDispatcher())) + return; + + pViewShell->GetDispatcher()->ExecuteList( + SID_TEXTEDIT, SfxCallMode::SYNCHRON | SfxCallMode::RECORD, {&aItem}); + + if (mpView->IsTextEdit()) + { + // end text edition before starting it again + mpView->SdrEndTextEdit(); + } + + // To be consistent with the usual behaviour in the Office the text + // object that is put into edit mode would have also to be selected. + // Starting the text edit mode is not enough so we do it here by + // hand. + mpView->UnmarkAllObj(pPV); + mpView->MarkObj(mpSearchSpellTextObj, pPV); + + mpSearchSpellTextObj->setActiveText(mnText); + + // Turn on the edit mode for the text object. + SetUpdateLayout(true); + mpView->SdrBeginTextEdit(mpSearchSpellTextObj, pPV, mpWindow, true, this, + pOutlinerView, true, true, bGrabFocus); + + mbFoundObject = true; +} + +ESelection SdOutliner::GetSearchStartPosition() const +{ + ESelection aPosition; + if (mbDirectionIsForward) + { + // The default constructor uses the beginning of the text as default. + aPosition = ESelection (); + } + else + { + // Retrieve the position after the last character in the last + // paragraph. + sal_Int32 nParagraphCount = GetParagraphCount(); + if (nParagraphCount == 0) + aPosition = ESelection(); + else + { + sal_Int32 nLastParagraphLength = GetEditEngine().GetTextLen ( + nParagraphCount-1); + aPosition = ESelection (nParagraphCount-1, nLastParagraphLength); + } + } + + return aPosition; +} + +bool SdOutliner::HasNoPreviousMatch() +{ + OutlinerView* pOutlinerView = getOutlinerView(); + + DBG_ASSERT (pOutlinerView!=nullptr, "outline view in SdOutliner::HasNoPreviousMatch is NULL"); + + // Detect whether the cursor stands at the beginning + // resp. at the end of the text. + return pOutlinerView->GetSelection() == GetSearchStartPosition(); +} + +bool SdOutliner::HandleFailedSearch() +{ + bool bContinueSearch = false; + + OutlinerView* pOutlinerView = getOutlinerView(); + if (pOutlinerView && mpSearchItem) + { + // Detect whether there is/may be a prior match. If there is then + // ask the user whether to wrap around. Otherwise tell the user + // that there is no match. + if (HasNoPreviousMatch ()) + { + // No match found in the whole presentation. + SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::NotFound); + } + + else + { + // No further matches found. Ask the user whether to wrap + // around and start again. + bContinueSearch = ShowWrapAroundDialog(); + } + } + + return bContinueSearch; +} + +SdrObject* SdOutliner::SetObject ( + const sd::outliner::IteratorPosition& rPosition) +{ + SetViewMode (rPosition.mePageKind); + SetPage (rPosition.meEditMode, static_cast<sal_uInt16>(rPosition.mnPageIndex)); + mnText = rPosition.mnText; + return rPosition.mxObject.get(); +} + +void SdOutliner::SetViewShell (const std::shared_ptr<sd::ViewShell>& rpViewShell) +{ + std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock()); + if (pViewShell == rpViewShell) + return; + + // Set the new view shell. + mpWeakViewShell = rpViewShell; + // When the outline view is not owned by us then we have to clear + // that pointer so that the current one for the new view shell will + // be used (in ProvideOutlinerView). + if (rpViewShell) + { + mpView = rpViewShell->GetView(); + + mpWindow = rpViewShell->GetActiveWindow(); + + mpImpl->ProvideOutlinerView(*this, rpViewShell, mpWindow); + OutlinerView* pOutlinerView = getOutlinerView(); + if (pOutlinerView != nullptr) + pOutlinerView->SetWindow(mpWindow); + } + else + { + mpView = nullptr; + mpWindow = nullptr; + } +} + +void SdOutliner::HandleChangedSelection() +{ + maMarkListCopy.clear(); + mbRestrictSearchToSelection = mpView->AreObjectsMarked(); + if (!mbRestrictSearchToSelection) + return; + + // Make a copy of the current mark list. + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + const size_t nCount = rMarkList.GetMarkCount(); + if (nCount > 0) + { + maMarkListCopy.clear(); + maMarkListCopy.reserve (nCount); + for (size_t i=0; i<nCount; ++i) + maMarkListCopy.emplace_back(rMarkList.GetMark(i)->GetMarkedSdrObj ()); + } + else + // No marked object. Is this case possible? + mbRestrictSearchToSelection = false; +} + +void SdOutliner::StartConversion( LanguageType nSourceLanguage, LanguageType nTargetLanguage, + const vcl::Font *pTargetFont, sal_Int32 nOptions, bool bIsInteractive ) +{ + std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock()); + bool bMultiDoc = nullptr != dynamic_cast< const sd::DrawViewShell *>( pViewShell.get() ); + + meMode = TEXT_CONVERSION; + mbDirectionIsForward = true; + mpSearchItem.reset(); + mnConversionLanguage = nSourceLanguage; + + BeginConversion(); + + OutlinerView* pOutlinerView = getOutlinerView(); + if (pOutlinerView != nullptr) + { + pOutlinerView->StartTextConversion( + GetMessageBoxParent(), + nSourceLanguage, + nTargetLanguage, + pTargetFont, + nOptions, + bIsInteractive, + bMultiDoc); + } + + EndConversion(); +} + +/** Prepare to do a text conversion on the current text object. This + includes putting it into edit mode. +*/ +void SdOutliner::PrepareConversion() +{ + SetUpdateLayout(true); + if( HasConvertibleTextPortion( mnConversionLanguage ) ) + { + SetUpdateLayout(false); + mbStringFound = true; + mbMatchMayExist = true; + + EnterEditMode(true); + + mpDrawDocument->GetDocSh()->SetWaitCursor( false ); + // Start search at the right end of the current object's text + // depending on the search direction. + } + else + { + SetUpdateLayout(false); + } +} + +void SdOutliner::BeginConversion() +{ + SetRefDevice( SD_MOD()->GetVirtualRefDevice() ); + + sd::ViewShellBase* pBase = getViewShellBase(); + if (pBase != nullptr) + SetViewShell (pBase->GetMainViewShell()); + + std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock()); + if (pViewShell) + { + mbStringFound = false; + + // Supposed that we are not located at the very beginning/end of the + // document then there may be a match in the document prior/after + // the current position. + mbMatchMayExist = true; + + maObjectIterator = sd::outliner::Iterator(); + maSearchStartPosition = sd::outliner::Iterator(); + RememberStartPosition(); + + mpImpl->ProvideOutlinerView(*this, pViewShell, mpWindow); + + HandleChangedSelection (); + } + ClearModifyFlag(); +} + +void SdOutliner::EndConversion() +{ + EndSpelling(); +} + +bool SdOutliner::ConvertNextDocument() +{ + std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock()); + if (dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() ) ) + return false; + + mpDrawDocument->GetDocSh()->SetWaitCursor( true ); + + Initialize ( true ); + + OutlinerView* pOutlinerView = getOutlinerView(); + if (pOutlinerView != nullptr) + { + mpWindow = pViewShell->GetActiveWindow(); + pOutlinerView->SetWindow(mpWindow); + } + ProvideNextTextObject (); + + mpDrawDocument->GetDocSh()->SetWaitCursor( false ); + ClearModifyFlag(); + + // for text conversion we automatically wrap around one + // time and stop at the start shape + if( mpFirstObj ) + { + if( (mnText == 0) && (mpFirstObj == mpObj) ) + return false; + } + else + { + mpFirstObj = mpObj; + } + + return !mbEndOfSearch; +} + +weld::Window* SdOutliner::GetMessageBoxParent() +{ + // We assume that the parent of the given message box is NULL, i.e. it is + // modal with respect to the top application window. However, this + // does not affect the search dialog. Therefore we have to lock it here + // while the message box is being shown. We also have to take into + // account that we are called during a spell check and the search dialog + // is not available. + weld::Window* pSearchDialog = nullptr; + SfxChildWindow* pChildWindow = nullptr; + switch (meMode) + { + case SEARCH: + if (SfxViewFrame* pViewFrm = SfxViewFrame::Current()) + pChildWindow = pViewFrm->GetChildWindow( + SvxSearchDialogWrapper::GetChildWindowId()); + break; + + case SPELL: + if (SfxViewFrame* pViewFrm = SfxViewFrame::Current()) + pChildWindow = pViewFrm->GetChildWindow( + sd::SpellDialogChildWindow::GetChildWindowId()); + break; + + case TEXT_CONVERSION: + // There should no messages boxes be displayed while doing the + // hangul hanja conversion. + break; + } + + if (pChildWindow != nullptr) + { + auto xController = pChildWindow->GetController(); + pSearchDialog = xController ? xController->getDialog() : nullptr; + } + + if (pSearchDialog) + return pSearchDialog; + + std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock()); + auto pWin = pViewShell->GetActiveWindow(); + return pWin ? pWin->GetFrameWeld() : nullptr; +} + +//===== SdOutliner::Implementation ============================================== + +SdOutliner::Implementation::Implementation() + : meOriginalEditMode(EditMode::Page), + mbOwnOutlineView(false), + mpOutlineView(nullptr) +{ +} + +SdOutliner::Implementation::~Implementation() +{ + if (mbOwnOutlineView && mpOutlineView!=nullptr) + { + mpOutlineView->SetWindow(nullptr); + delete mpOutlineView; + mpOutlineView = nullptr; + } +} + +/** We try to create a new OutlinerView only when there is none available, + either from an OutlinerViewShell or a previous call to + ProvideOutlinerView(). This is necessary to support the spell checker + which can not cope with exchanging the OutlinerView. +*/ +void SdOutliner::Implementation::ProvideOutlinerView ( + Outliner& rOutliner, + const std::shared_ptr<sd::ViewShell>& rpViewShell, + vcl::Window* pWindow) +{ + if (rpViewShell == nullptr) + return; + + switch (rpViewShell->GetShellType()) + { + case sd::ViewShell::ST_DRAW: + case sd::ViewShell::ST_IMPRESS: + case sd::ViewShell::ST_NOTES: + case sd::ViewShell::ST_HANDOUT: + { + // Create a new outline view to do the search on. + bool bInsert = false; + if (mpOutlineView != nullptr && !mbOwnOutlineView) + mpOutlineView = nullptr; + + if (mpOutlineView == nullptr || !rOutliner.GetEditEngine().HasView(&mpOutlineView->GetEditView())) + { + delete mpOutlineView; + mpOutlineView = new OutlinerView(&rOutliner, pWindow); + mbOwnOutlineView = true; + bInsert = true; + } + else + mpOutlineView->SetWindow(pWindow); + + EVControlBits nStat = mpOutlineView->GetControlWord(); + nStat &= ~EVControlBits::AUTOSCROLL; + mpOutlineView->SetControlWord(nStat); + + if (bInsert) + rOutliner.InsertView( mpOutlineView ); + + rOutliner.SetUpdateLayout(false); + mpOutlineView->SetOutputArea (::tools::Rectangle (Point(), Size(1, 1))); + rOutliner.SetPaperSize( Size(1, 1) ); + rOutliner.SetText(OUString(), rOutliner.GetParagraph(0)); + + meOriginalEditMode = + std::static_pointer_cast<sd::DrawViewShell>(rpViewShell)->GetEditMode(); + } + break; + + case sd::ViewShell::ST_OUTLINE: + { + if (mpOutlineView!=nullptr && mbOwnOutlineView) + delete mpOutlineView; + mpOutlineView = rOutliner.GetView(0); + mbOwnOutlineView = false; + } + break; + + default: + case sd::ViewShell::ST_NONE: + case sd::ViewShell::ST_PRESENTATION: + // Ignored + break; + } +} + +void SdOutliner::Implementation::ReleaseOutlinerView() +{ + if (mbOwnOutlineView) + { + OutlinerView* pView = mpOutlineView; + mpOutlineView = nullptr; + mbOwnOutlineView = false; + if (pView != nullptr) + { + pView->SetWindow(nullptr); + delete pView; + } + } + else + { + mpOutlineView = nullptr; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/OutlinerIterator.cxx b/sd/source/ui/view/OutlinerIterator.cxx new file mode 100644 index 000000000..8cdb29330 --- /dev/null +++ b/sd/source/ui/view/OutlinerIterator.cxx @@ -0,0 +1,798 @@ +/* -*- 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 <OutlinerIterator.hxx> +#include <OutlinerIteratorImpl.hxx> +#include <svx/svditer.hxx> +#include <tools/debug.hxx> +#include <osl/diagnose.h> +#include <Outliner.hxx> + +#include <drawdoc.hxx> +#include <DrawViewShell.hxx> +#include <sdpage.hxx> + +namespace sd::outliner { + +//===== IteratorPosition ====================================================== + +IteratorPosition::IteratorPosition() +: mnText(0) +, mnPageIndex(-1) +, mePageKind(PageKind::Standard) +, meEditMode(EditMode::Page) +{ +} + +bool IteratorPosition::operator== (const IteratorPosition& aPosition) const +{ + return mxObject.get() == aPosition.mxObject.get() + && mnText == aPosition.mnText + && mnPageIndex == aPosition.mnPageIndex + && mePageKind == aPosition.mePageKind + && meEditMode == aPosition.meEditMode; +} + +//===== Iterator ============================================================== + +Iterator::Iterator() +{ +} + +Iterator::Iterator (const Iterator& rIterator) + : mxIterator(rIterator.mxIterator ? rIterator.mxIterator->Clone() : nullptr) +{ +} + +Iterator::Iterator(Iterator&& rIterator) noexcept + : mxIterator(std::move(rIterator.mxIterator)) +{ +} + +Iterator::Iterator (std::unique_ptr<IteratorImplBase> pObject) + : mxIterator(std::move(pObject)) +{ +} + +Iterator::~Iterator() +{ +} + +Iterator& Iterator::operator= (const Iterator& rIterator) +{ + if (this != &rIterator) + { + if (rIterator.mxIterator) + mxIterator.reset(rIterator.mxIterator->Clone()); + else + mxIterator.reset(); + } + return *this; +} + +Iterator& Iterator::operator=(Iterator&& rIterator) noexcept +{ + mxIterator = std::move(rIterator.mxIterator); + return *this; +} + +const IteratorPosition& Iterator::operator* () const +{ + DBG_ASSERT (mxIterator, "::sd::outliner::Iterator::operator* : missing implementation object"); + return mxIterator->GetPosition(); +} + +Iterator& Iterator::operator++ () +{ + if (mxIterator) + mxIterator->GotoNextText(); + return *this; +} + +bool Iterator::operator== (const Iterator& rIterator) const +{ + if (!mxIterator || !rIterator.mxIterator) + return mxIterator.get() == rIterator.mxIterator.get(); + else + return *mxIterator == *rIterator.mxIterator; +} + +bool Iterator::operator!= (const Iterator& rIterator) const +{ + return ! operator==(rIterator); +} + +void Iterator::Reverse() +{ + if (mxIterator) + mxIterator->Reverse(); +} + +//===== IteratorFactory ======================================================= + +OutlinerContainer::OutlinerContainer (SdOutliner* pOutliner) +: mpOutliner(pOutliner) +{ +} + +Iterator OutlinerContainer::begin() +{ + return CreateIterator (BEGIN); +} + +Iterator OutlinerContainer::end() +{ + return CreateIterator (END); +} + +Iterator OutlinerContainer::current() +{ + return CreateIterator (CURRENT); +} + +Iterator OutlinerContainer::CreateIterator (IteratorLocation aLocation) +{ + // Decide on certain features of the outliner which kind of iterator to + // use. + if (mpOutliner->mbRestrictSearchToSelection) + // There is a selection. Search only in this. + return CreateSelectionIterator ( + mpOutliner->maMarkListCopy, + mpOutliner->mpDrawDocument, + mpOutliner->mpWeakViewShell.lock(), + mpOutliner->mbDirectionIsForward, + aLocation); + else + // Search in the whole document. + return CreateDocumentIterator ( + mpOutliner->mpDrawDocument, + mpOutliner->mpWeakViewShell.lock(), + mpOutliner->mbDirectionIsForward, + aLocation); +} + +Iterator OutlinerContainer::CreateSelectionIterator ( + const ::std::vector<::tools::WeakReference<SdrObject>>& rObjectList, + SdDrawDocument* pDocument, + const std::shared_ptr<ViewShell>& rpViewShell, + bool bDirectionIsForward, + IteratorLocation aLocation) +{ + OSL_ASSERT(rpViewShell); + + sal_Int32 nObjectIndex; + + if (bDirectionIsForward) + switch (aLocation) + { + case CURRENT: + case BEGIN: + default: + nObjectIndex = 0; + break; + case END: + nObjectIndex = rObjectList.size(); + break; + } + else + switch (aLocation) + { + case CURRENT: + case BEGIN: + default: + nObjectIndex = rObjectList.size()-1; + break; + case END: + nObjectIndex = -1; + break; + } + + return Iterator (std::make_unique<SelectionIteratorImpl> ( + rObjectList, nObjectIndex, pDocument, rpViewShell, bDirectionIsForward)); +} + +Iterator OutlinerContainer::CreateDocumentIterator ( + SdDrawDocument* pDocument, + const std::shared_ptr<ViewShell>& rpViewShell, + bool bDirectionIsForward, + IteratorLocation aLocation) +{ + OSL_ASSERT(rpViewShell); + + PageKind ePageKind; + EditMode eEditMode; + + switch (aLocation) + { + case BEGIN: + default: + if (bDirectionIsForward) + { + ePageKind = PageKind::Standard; + eEditMode = EditMode::Page; + } + else + { + ePageKind = PageKind::Handout; + eEditMode = EditMode::MasterPage; + } + break; + + case END: + if (bDirectionIsForward) + { + ePageKind = PageKind::Handout; + eEditMode = EditMode::MasterPage; + } + else + { + ePageKind = PageKind::Standard; + eEditMode = EditMode::Page; + } + break; + + case CURRENT: + const std::shared_ptr<DrawViewShell> pDrawViewShell( + std::dynamic_pointer_cast<DrawViewShell>(rpViewShell)); + if (pDrawViewShell) + { + ePageKind = pDrawViewShell->GetPageKind(); + eEditMode = pDrawViewShell->GetEditMode(); + } + else + { + ePageKind = PageKind::Standard; + eEditMode = EditMode::Page; + } + break; + } + + sal_Int32 nPageIndex = GetPageIndex (pDocument, rpViewShell, + ePageKind, eEditMode, bDirectionIsForward, aLocation); + + return Iterator ( + std::make_unique<DocumentIteratorImpl> (nPageIndex, ePageKind, eEditMode, + pDocument, rpViewShell, bDirectionIsForward)); +} + +sal_Int32 OutlinerContainer::GetPageIndex ( + SdDrawDocument const * pDocument, + const std::shared_ptr<ViewShell>& rpViewShell, + PageKind ePageKind, + EditMode eEditMode, + bool bDirectionIsForward, + IteratorLocation aLocation) +{ + OSL_ASSERT(rpViewShell); + + sal_Int32 nPageIndex; + sal_Int32 nPageCount; + + const std::shared_ptr<DrawViewShell> pDrawViewShell( + std::dynamic_pointer_cast<DrawViewShell>(rpViewShell)); + + switch (eEditMode) + { + case EditMode::Page: + nPageCount = pDocument->GetSdPageCount (ePageKind); + break; + case EditMode::MasterPage: + nPageCount = pDocument->GetMasterSdPageCount(ePageKind); + break; + default: + nPageCount = 0; + } + + switch (aLocation) + { + case CURRENT: + if (pDrawViewShell) + nPageIndex = pDrawViewShell->GetCurPagePos(); + else + { + const SdPage* pPage = rpViewShell->GetActualPage(); + if (pPage != nullptr) + nPageIndex = (pPage->GetPageNum()-1)/2; + else + nPageIndex = 0; + } + break; + + case BEGIN: + default: + if (bDirectionIsForward) + nPageIndex = 0; + else + nPageIndex = nPageCount-1; + break; + + case END: + if (bDirectionIsForward) + nPageIndex = nPageCount; + else + nPageIndex = -1; + break; + } + + return nPageIndex; +} + +//===== IteratorImplBase ==================================================== + +IteratorImplBase::IteratorImplBase(SdDrawDocument* pDocument, + const std::weak_ptr<ViewShell>& rpViewShellWeak, + bool bDirectionIsForward) +: mpDocument (pDocument) +, mpViewShellWeak (rpViewShellWeak) +, mbDirectionIsForward (bDirectionIsForward) +{ + std::shared_ptr<DrawViewShell> pDrawViewShell; + if ( ! mpViewShellWeak.expired()) + pDrawViewShell = std::dynamic_pointer_cast<DrawViewShell>(rpViewShellWeak.lock()); + + if (pDrawViewShell) + { + maPosition.mePageKind = pDrawViewShell->GetPageKind(); + maPosition.meEditMode = pDrawViewShell->GetEditMode(); + } + else + { + maPosition.mePageKind = PageKind::Standard; + maPosition.meEditMode = EditMode::Page; + } +} + +IteratorImplBase::IteratorImplBase( SdDrawDocument* pDocument, + const std::weak_ptr<ViewShell>& rpViewShellWeak, + bool bDirectionIsForward, PageKind ePageKind, EditMode eEditMode) +: mpDocument (pDocument) +, mpViewShellWeak (rpViewShellWeak) +, mbDirectionIsForward (bDirectionIsForward) +{ + maPosition.mePageKind = ePageKind; + maPosition.meEditMode = eEditMode; +} + +IteratorImplBase::~IteratorImplBase() +{} + +bool IteratorImplBase::operator== (const IteratorImplBase& rIterator) const +{ + return maPosition == rIterator.maPosition; +} + +bool IteratorImplBase::IsEqualSelection(const IteratorImplBase& rIterator) const +{ + // When this method is executed instead of the ones from derived classes + // then the argument is of another type then the object itself. In this + // just compare the position objects. + return maPosition == rIterator.maPosition; +} + +const IteratorPosition& IteratorImplBase::GetPosition() +{ + return maPosition; +} + +IteratorImplBase* IteratorImplBase::Clone (IteratorImplBase* pObject) const +{ + if (pObject != nullptr) + { + pObject->maPosition = maPosition; + pObject->mpDocument = mpDocument; + pObject->mpViewShellWeak = mpViewShellWeak; + pObject->mbDirectionIsForward = mbDirectionIsForward; + } + return pObject; +} + +void IteratorImplBase::Reverse() +{ + mbDirectionIsForward = ! mbDirectionIsForward; +} + +//===== SelectionIteratorImpl =========================================== + +SelectionIteratorImpl::SelectionIteratorImpl ( + const ::std::vector<::tools::WeakReference<SdrObject>>& rObjectList, + sal_Int32 nObjectIndex, + SdDrawDocument* pDocument, + const std::weak_ptr<ViewShell>& rpViewShellWeak, + bool bDirectionIsForward) + : IteratorImplBase (pDocument, rpViewShellWeak, bDirectionIsForward), + mrObjectList(rObjectList), + mnObjectIndex(nObjectIndex) +{ +} + +SelectionIteratorImpl::~SelectionIteratorImpl() +{} + +IteratorImplBase* SelectionIteratorImpl::Clone (IteratorImplBase* pObject) const +{ + SelectionIteratorImpl* pIterator = static_cast<SelectionIteratorImpl*>(pObject); + if (pIterator == nullptr) + pIterator = new SelectionIteratorImpl ( + mrObjectList, mnObjectIndex, mpDocument, mpViewShellWeak, mbDirectionIsForward); + return pIterator; +} + +void SelectionIteratorImpl::GotoNextText() +{ + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( mrObjectList.at(mnObjectIndex).get() ); + if (mbDirectionIsForward) + { + if( pTextObj ) + { + ++maPosition.mnText; + if( maPosition.mnText >= pTextObj->getTextCount() ) + { + maPosition.mnText = 0; + ++mnObjectIndex; + } + } + else + { + ++mnObjectIndex; + } + } + else + { + if( pTextObj ) + { + --maPosition.mnText; + if( maPosition.mnText < 0 ) + { + maPosition.mnText = -1; + --mnObjectIndex; + } + } + else + { + --mnObjectIndex; + maPosition.mnText = -1; + } + + if( (maPosition.mnText == -1) && (mnObjectIndex >= 0) ) + { + pTextObj = dynamic_cast< SdrTextObj* >( mrObjectList.at(mnObjectIndex).get() ); + if( pTextObj ) + maPosition.mnText = pTextObj->getTextCount() - 1; + } + + if( maPosition.mnText == -1 ) + maPosition.mnText = 0; + } +} + +const IteratorPosition& SelectionIteratorImpl::GetPosition() +{ + maPosition.mxObject = mrObjectList.at(mnObjectIndex); + + return maPosition; +} + +bool SelectionIteratorImpl::operator== (const IteratorImplBase& rIterator) const +{ + return rIterator.IsEqualSelection(*this); +} + +bool SelectionIteratorImpl::IsEqualSelection(const IteratorImplBase& rIterator) const +{ + const SelectionIteratorImpl* pSelectionIterator = + static_cast<const SelectionIteratorImpl*>(&rIterator); + return mpDocument == pSelectionIterator->mpDocument + && mnObjectIndex == pSelectionIterator->mnObjectIndex; +} + +//===== ViewIteratorImpl ================================================ + +ViewIteratorImpl::ViewIteratorImpl ( + sal_Int32 nPageIndex, + SdDrawDocument* pDocument, + const std::weak_ptr<ViewShell>& rpViewShellWeak, + bool bDirectionIsForward) + : IteratorImplBase (pDocument, rpViewShellWeak, bDirectionIsForward), + mbPageChangeOccurred(false), + mpPage(nullptr) +{ + SetPage (nPageIndex); +} + +ViewIteratorImpl::ViewIteratorImpl ( + sal_Int32 nPageIndex, + SdDrawDocument* pDocument, + const std::weak_ptr<ViewShell>& rpViewShellWeak, + bool bDirectionIsForward, + PageKind ePageKind, + EditMode eEditMode) + : IteratorImplBase (pDocument, rpViewShellWeak, bDirectionIsForward, ePageKind, eEditMode), + mbPageChangeOccurred(false), + mpPage(nullptr) +{ + SetPage (nPageIndex); +} + +ViewIteratorImpl::~ViewIteratorImpl() +{ +} + +IteratorImplBase* ViewIteratorImpl::Clone (IteratorImplBase* pObject) const +{ + + ViewIteratorImpl* pIterator = static_cast<ViewIteratorImpl*>(pObject); + if (pIterator == nullptr) + pIterator = new ViewIteratorImpl ( + maPosition.mnPageIndex, mpDocument, mpViewShellWeak, mbDirectionIsForward); + + IteratorImplBase::Clone (pObject); + + if (mpObjectIterator != nullptr) + { + pIterator->mpObjectIterator.reset( new SdrObjListIter(mpPage, SdrIterMode::DeepNoGroups, !mbDirectionIsForward) ); + + // No direct way to set the object iterator to the current object. + pIterator->maPosition.mxObject.reset(nullptr); + while (pIterator->mpObjectIterator->IsMore() && pIterator->maPosition.mxObject!=maPosition.mxObject) + pIterator->maPosition.mxObject.reset(pIterator->mpObjectIterator->Next()); + } + else + pIterator->mpObjectIterator.reset(); + + return pIterator; +} + +void ViewIteratorImpl::GotoNextText() +{ + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( maPosition.mxObject.get() ); + if( pTextObj ) + { + if (mbDirectionIsForward) + { + ++maPosition.mnText; + if( maPosition.mnText < pTextObj->getTextCount() ) + return; + } + else + { + --maPosition.mnText; + if( maPosition.mnText >= 0 ) + return; + } + } + + if (mpObjectIterator != nullptr && mpObjectIterator->IsMore()) + maPosition.mxObject.reset(mpObjectIterator->Next()); + else + maPosition.mxObject.reset(nullptr); + + if (!maPosition.mxObject.is() ) + { + if (mbDirectionIsForward) + SetPage (maPosition.mnPageIndex+1); + else + SetPage (maPosition.mnPageIndex-1); + + if (mpPage != nullptr) + mpObjectIterator.reset( new SdrObjListIter(mpPage, SdrIterMode::DeepNoGroups, !mbDirectionIsForward) ); + if (mpObjectIterator!=nullptr && mpObjectIterator->IsMore()) + maPosition.mxObject.reset(mpObjectIterator->Next()); + else + maPosition.mxObject.reset(nullptr); + } + + maPosition.mnText = 0; + if( !mbDirectionIsForward && maPosition.mxObject.is() ) + { + pTextObj = dynamic_cast< SdrTextObj* >( maPosition.mxObject.get() ); + if( pTextObj ) + maPosition.mnText = pTextObj->getTextCount() - 1; + } +} + +void ViewIteratorImpl::SetPage (sal_Int32 nPageIndex) +{ + mbPageChangeOccurred = (maPosition.mnPageIndex!=nPageIndex); + if (mbPageChangeOccurred) + { + maPosition.mnPageIndex = nPageIndex; + + sal_Int32 nPageCount; + if (maPosition.meEditMode == EditMode::Page) + nPageCount = mpDocument->GetSdPageCount(maPosition.mePageKind); + else + nPageCount = mpDocument->GetMasterSdPageCount( + maPosition.mePageKind); + + // Get page pointer. Here we have three cases: regular pages, + // master pages and invalid page indices. The later ones are not + // errors but the effect of the iterator advancing to the next page + // and going past the last one. This dropping of the rim at the far + // side is detected here and has to be reacted to by the caller. + if (nPageIndex>=0 && nPageIndex < nPageCount) + { + if (maPosition.meEditMode == EditMode::Page) + mpPage = mpDocument->GetSdPage ( + static_cast<sal_uInt16>(nPageIndex), + maPosition.mePageKind); + else + mpPage = mpDocument->GetMasterSdPage ( + static_cast<sal_uInt16>(nPageIndex), + maPosition.mePageKind); + } + else + mpPage = nullptr; + } + + // Set up object list iterator. + if (mpPage != nullptr) + mpObjectIterator.reset( new SdrObjListIter(mpPage, SdrIterMode::DeepNoGroups, ! mbDirectionIsForward) ); + else + mpObjectIterator.reset(); + + // Get object pointer. + if (mpObjectIterator!=nullptr && mpObjectIterator->IsMore()) + maPosition.mxObject.reset( mpObjectIterator->Next() ); + else + maPosition.mxObject.reset(nullptr); + + maPosition.mnText = 0; + if( !mbDirectionIsForward && maPosition.mxObject.is() ) + { + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( maPosition.mxObject.get() ); + if( pTextObj ) + maPosition.mnText = pTextObj->getTextCount() - 1; + } + +} + +void ViewIteratorImpl::Reverse() +{ + IteratorImplBase::Reverse (); + + // Create reversed object list iterator. + if (mpPage != nullptr) + mpObjectIterator.reset( new SdrObjListIter(mpPage, SdrIterMode::DeepNoGroups, ! mbDirectionIsForward) ); + else + mpObjectIterator.reset(); + + // Move iterator to the current object. + ::tools::WeakReference<SdrObject> xObject = std::move(maPosition.mxObject); + + if (!mpObjectIterator) + return; + + while (mpObjectIterator->IsMore() && maPosition.mxObject != xObject) + maPosition.mxObject.reset(mpObjectIterator->Next()); +} + +//===== DocumentIteratorImpl ============================================ + +DocumentIteratorImpl::DocumentIteratorImpl ( + sal_Int32 nPageIndex, + PageKind ePageKind, EditMode eEditMode, + SdDrawDocument* pDocument, + const std::weak_ptr<ViewShell>& rpViewShellWeak, + bool bDirectionIsForward) + : ViewIteratorImpl (nPageIndex, pDocument, rpViewShellWeak, bDirectionIsForward, + ePageKind, eEditMode) +{ + if (eEditMode == EditMode::Page) + mnPageCount = pDocument->GetSdPageCount (ePageKind); + else + mnPageCount = pDocument->GetMasterSdPageCount(ePageKind); +} + +DocumentIteratorImpl::~DocumentIteratorImpl() +{} + +IteratorImplBase* DocumentIteratorImpl::Clone (IteratorImplBase* pObject) const +{ + DocumentIteratorImpl* pIterator = static_cast<DocumentIteratorImpl*>(pObject); + if (pIterator == nullptr) + pIterator = new DocumentIteratorImpl ( + maPosition.mnPageIndex, maPosition.mePageKind, maPosition.meEditMode, + mpDocument, mpViewShellWeak, mbDirectionIsForward); + // Finish the cloning. + return ViewIteratorImpl::Clone (pIterator); +} + +void DocumentIteratorImpl::GotoNextText() +{ + bool bSetToOnePastLastPage = false; + bool bViewChanged = false; + + ViewIteratorImpl::GotoNextText(); + + if (mbDirectionIsForward) + { + if (maPosition.mnPageIndex >= mnPageCount) + { + // Switch to master page. + if (maPosition.meEditMode == EditMode::Page) + { + maPosition.meEditMode = EditMode::MasterPage; + SetPage (0); + } + + // Switch to next view mode. + else + { + if (maPosition.mePageKind == PageKind::Handout) + // Not really necessary but makes things more clear. + bSetToOnePastLastPage = true; + else + { + maPosition.meEditMode = EditMode::Page; + if (maPosition.mePageKind == PageKind::Standard) + maPosition.mePageKind = PageKind::Notes; + else if (maPosition.mePageKind == PageKind::Notes) + maPosition.mePageKind = PageKind::Handout; + SetPage (0); + } + } + bViewChanged = true; + } + } + else + if (maPosition.mnPageIndex < 0) + { + // Switch from master pages to draw pages. + if (maPosition.meEditMode == EditMode::MasterPage) + { + maPosition.meEditMode = EditMode::Page; + bSetToOnePastLastPage = true; + } + + // Switch to previous view mode. + else + { + if (maPosition.mePageKind == PageKind::Standard) + SetPage (-1); + else + { + maPosition.meEditMode = EditMode::MasterPage; + if (maPosition.mePageKind == PageKind::Handout) + maPosition.mePageKind = PageKind::Notes; + else if (maPosition.mePageKind == PageKind::Notes) + maPosition.mePageKind = PageKind::Standard; + bSetToOnePastLastPage = true; + } + } + bViewChanged = true; + } + + if (!bViewChanged) + return; + + // Get new page count; + sal_Int32 nPageCount; + if (maPosition.meEditMode == EditMode::Page) + nPageCount = mpDocument->GetSdPageCount (maPosition.mePageKind); + else + nPageCount = mpDocument->GetMasterSdPageCount(maPosition.mePageKind); + + // Now that we know the number of pages we can set the current page index. + if (bSetToOnePastLastPage) + SetPage (nPageCount); +} + +} // end of namespace ::sd::outliner + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/PresentationViewShellBase.cxx b/sd/source/ui/view/PresentationViewShellBase.cxx new file mode 100644 index 000000000..789afbbdd --- /dev/null +++ b/sd/source/ui/view/PresentationViewShellBase.cxx @@ -0,0 +1,94 @@ +/* -*- 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 <PresentationViewShellBase.hxx> +#include <DrawDocShell.hxx> +#include <framework/FrameworkHelper.hxx> +#include <framework/PresentationModule.hxx> + +#include <sfx2/viewfac.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/viewsh.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/XFrame.hpp> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd { + +class DrawDocShell; + + +// We have to expand the SFX_IMPL_VIEWFACTORY macro to call LateInit() after a +// new PresentationViewShellBase object has been constructed. + +SfxViewFactory* PresentationViewShellBase::s_pFactory; +SfxViewShell* PresentationViewShellBase::CreateInstance ( + SfxViewFrame *_pFrame, SfxViewShell *pOldView) +{ + PresentationViewShellBase* pBase = + new PresentationViewShellBase(_pFrame, pOldView); + pBase->LateInit(framework::FrameworkHelper::msPresentationViewURL); + return pBase; +} +void PresentationViewShellBase::RegisterFactory( SfxInterfaceId nPrio ) +{ + s_pFactory = new SfxViewFactory( + &CreateInstance,nPrio,"FullScreenPresentation"); + InitFactory(); +} +void PresentationViewShellBase::InitFactory() +{ + SFX_VIEW_REGISTRATION(DrawDocShell); +} + +PresentationViewShellBase::PresentationViewShellBase ( + SfxViewFrame* _pFrame, + SfxViewShell* pOldShell) + : ViewShellBase (_pFrame, pOldShell) +{ + // Hide the automatic (non-context sensitive) tool bars. + Reference<beans::XPropertySet> xFrameSet ( + _pFrame->GetFrame().GetFrameInterface(), + UNO_QUERY); + if (xFrameSet.is()) + { + Reference<beans::XPropertySet> xLayouterSet(xFrameSet->getPropertyValue("LayoutManager"), UNO_QUERY); + if (xLayouterSet.is()) + { + xLayouterSet->setPropertyValue("AutomaticToolbars", Any(false)); + } + } +} + +PresentationViewShellBase::~PresentationViewShellBase() +{ +} + +void PresentationViewShellBase::InitializeFramework() +{ + css::uno::Reference<css::frame::XController> + xController (GetController()); + sd::framework::PresentationModule::Initialize(xController); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/SlideSorterViewShellBase.cxx b/sd/source/ui/view/SlideSorterViewShellBase.cxx new file mode 100644 index 000000000..ecf679c98 --- /dev/null +++ b/sd/source/ui/view/SlideSorterViewShellBase.cxx @@ -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 . + */ + +#include <SlideSorterViewShellBase.hxx> +#include <DrawDocShell.hxx> +#include <framework/FrameworkHelper.hxx> +#include <sfx2/viewfac.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/viewsh.hxx> + +namespace sd { + +class DrawDocShell; + + +// We have to expand the SFX_IMPL_VIEWFACTORY macro to call LateInit() after a +// new SlideSorterViewShellBase object has been constructed. + +SfxViewFactory* SlideSorterViewShellBase::s_pFactory; +SfxViewShell* SlideSorterViewShellBase::CreateInstance ( + SfxViewFrame *pFrame, SfxViewShell *pOldView) +{ + SlideSorterViewShellBase* pBase = new SlideSorterViewShellBase(pFrame, pOldView); + pBase->LateInit(framework::FrameworkHelper::msSlideSorterURL); + return pBase; +} + +void SlideSorterViewShellBase::RegisterFactory( SfxInterfaceId nPrio ) +{ + s_pFactory = new SfxViewFactory(&CreateInstance,nPrio,"SlideSorter"); + InitFactory(); +} + +void SlideSorterViewShellBase::InitFactory() +{ + SFX_VIEW_REGISTRATION(DrawDocShell); +} + +SlideSorterViewShellBase::SlideSorterViewShellBase ( + SfxViewFrame* _pFrame, + SfxViewShell* pOldShell) + : ImpressViewShellBase (_pFrame, pOldShell) +{ +} + +SlideSorterViewShellBase::~SlideSorterViewShellBase() +{ +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/ToolBarManager.cxx b/sd/source/ui/view/ToolBarManager.cxx new file mode 100644 index 000000000..0bb0f9528 --- /dev/null +++ b/sd/source/ui/view/ToolBarManager.cxx @@ -0,0 +1,1375 @@ +/* -*- 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 <ToolBarManager.hxx> + +#include <DrawViewShell.hxx> +#include <EventMultiplexer.hxx> +#include <ViewShellBase.hxx> +#include <ViewShellManager.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/XLayoutManager.hpp> + +#include <sal/log.hxx> +#include <osl/mutex.hxx> +#include <o3tl/deleter.hxx> +#include <o3tl/enumrange.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/toolbarids.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/eitem.hxx> +#include <svx/svxids.hrc> +#include <svx/extrusionbar.hxx> +#include <svx/fontworkbar.hxx> +#include <tools/debug.hxx> +#include <tools/link.hxx> +#include <vcl/svapp.hxx> +#include <osl/diagnose.h> + +#include <map> +#include <utility> +#include <memory> +#include <string_view> +#include <vector> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace { + +using namespace sd; + +class ToolBarRules; + +/** Lock of the frame::XLayoutManager. +*/ +class LayouterLock +{ + Reference<frame::XLayoutManager> mxLayouter; +public: + explicit LayouterLock (const Reference<frame::XLayoutManager>& rxLayouter); + ~LayouterLock(); + bool is() const { return mxLayouter.is(); } +}; + +/** Store a list of tool bars for each of the tool bar groups. From + this the list of requested tool bars is built. +*/ +class ToolBarList +{ +public: + ToolBarList(); + + void ClearGroup (sd::ToolBarManager::ToolBarGroup eGroup); + void AddToolBar (sd::ToolBarManager::ToolBarGroup eGroup, const OUString& rsName); + bool RemoveToolBar (sd::ToolBarManager::ToolBarGroup eGroup, const OUString& rsName); + + void GetToolBarsToActivate (std::vector<OUString>& rToolBars) const; + void GetToolBarsToDeactivate (std::vector<OUString>& rToolBars) const; + + void MarkToolBarAsActive (const OUString& rsName); + void MarkToolBarAsNotActive (const OUString& rsName); + void MarkAllToolBarsAsNotActive(); + +private: + typedef ::std::map<sd::ToolBarManager::ToolBarGroup, std::vector<OUString> > Groups; + Groups maGroups; + std::vector<OUString> maActiveToolBars; + + void MakeRequestedToolBarList (std::vector<OUString>& rToolBars) const; +}; + +/** Manage tool bars that are implemented as sub shells of a view shell. + The typical procedure of updating the sub shells of a view shell is to + rebuild a list of sub shells that the caller would like to have active. + The methods ClearGroup() and AddShellId() allow the caller to do that. A + final call to UpdateShells() activates the requested shells that are not + active and deactivates the active shells that are not requested . + + This is done by maintaining two lists. One (the current list) + reflects the current state. The other (the requested list) contains the + currently requested shells. UpdateShells() makes the requested + list the current list and clears the current list. + + Each shell belongs to one group. Different groups can be modified + separately. +*/ +class ToolBarShellList +{ +public: + /** Create a new object with an empty current list and an empty + requested list. + */ + ToolBarShellList(); + + /** Remove all shells from a group. Calling this method should normally + not be necessary because after the construction or after a call to + UpdateShells() the requested list is empty. + @param eGroup + The group to clear. Shells in other groups are not modified. + */ + void ClearGroup (sd::ToolBarManager::ToolBarGroup eGroup); + + /** Add a shell. When the specified shell has already been requested + for another group then it is moved to this group. + @param eGroup + The group to which to add the shell. + @param nId + The id of the shell to add. + */ + void AddShellId (sd::ToolBarManager::ToolBarGroup eGroup, sd::ShellId nId); + + /** Releasing all shells means that the given ToolBarRules object is + informed that every shell managed by the called ToolBarShellList is + about to be removed and that the associated framework tool bars can + be removed as well. The caller still has to call UpdateShells(). + */ + void ReleaseAllShells (ToolBarRules& rRules); + + /** The requested list is made the current list by activating all + shells in the requested list and by deactivating the shells in the + current list that are not in the requested list. + @param pMainViewShell + The shells that are activated or deactivated are sub shells of + this view shell. + @param rManager + This ViewShellManager is used to activate or deactivate shells. + */ + void UpdateShells ( + const std::shared_ptr<ViewShell>& rpMainViewShell, + const std::shared_ptr<ViewShellManager>& rpManager); + +private: + class ShellDescriptor + {public: + ShellDescriptor (ShellId nId,sd::ToolBarManager::ToolBarGroup eGroup); + ShellId mnId; + sd::ToolBarManager::ToolBarGroup meGroup; + friend bool operator<(const ShellDescriptor& r1, const ShellDescriptor& r2) + { return r1.mnId < r2.mnId; } + }; + + /** The requested list of tool bar shells that will be active after the + next call to UpdateShells(). + */ + typedef ::std::set<ShellDescriptor> GroupedShellList; + GroupedShellList maNewList; + + /** The list of tool bar shells that are currently on the shell stack. + Using a GroupedShellList is not strictly necessary but it makes + things easier and does not waste too much memory. + */ + GroupedShellList maCurrentList; +}; + +/** This class concentrates the knowledge about when to show what tool bars + in one place. +*/ +class ToolBarRules +{ +public: + ToolBarRules ( + const std::shared_ptr<ToolBarManager>& rpToolBarManager, + const std::shared_ptr<ViewShellManager>& rpViewShellManager); + + /** This method calls MainViewShellChanged() and SelectionHasChanged() + for the current main view shell and its view. + */ + void Update (ViewShellBase const & rBase); + + /** Reset all tool bars in all groups and add tool bars and tool bar + shells to the ToolBarGroup::Permanent group for the specified ViewShell type. + */ + void MainViewShellChanged (ViewShell::ShellType nShellType); + + /** Reset all tool bars in all groups and add tool bars and tool bar + shells to the ToolBarGroup::Permanent group for the specified ViewShell. + */ + void MainViewShellChanged (const ViewShell& rMainViewShell); + + /** Reset all tool bars in the ToolBarGroup::Function group and add tool bars and tool bar + shells to this group for the current selection. + */ + void SelectionHasChanged ( + const ::sd::ViewShell& rViewShell, + const SdrView& rView); + + /** Add a tool bar for the specified tool bar shell. + */ + void SubShellAdded ( + ::sd::ToolBarManager::ToolBarGroup eGroup, + sd::ShellId nShellId); + + /** Remove a tool bar for the specified tool bar shell. + */ + void SubShellRemoved ( + ::sd::ToolBarManager::ToolBarGroup eGroup, + sd::ShellId nShellId); + +private: + std::shared_ptr<ToolBarManager> mpToolBarManager; + std::shared_ptr<ViewShellManager> mpViewShellManager; +}; + +} // end of anonymous namespace + +namespace sd { + +//===== ToolBarManager::Implementation ======================================== + +class ToolBarManager::Implementation +{ +public: + /** This constructor takes three arguments even though the + ToolBarManager could be taken from the ViewShellBase. This is so to + state explicitly which information has to be present when this + constructor is called. The ViewShellBase may not have been fully + initialized at this point and must not be asked for this values. + */ + Implementation ( + ViewShellBase& rBase, + const std::shared_ptr<sd::tools::EventMultiplexer>& rpMultiplexer, + const std::shared_ptr<ViewShellManager>& rpViewShellManager, + const std::shared_ptr<ToolBarManager>& rpToolBarManager); + ~Implementation(); + + void SetValid (bool bValid); + + void ResetToolBars (ToolBarGroup eGroup); + void ResetAllToolBars(); + void AddToolBar (ToolBarGroup eGroup, const OUString& rsToolBarName); + void AddToolBarShell (ToolBarGroup eGroup, ShellId nToolBarId); + void RemoveToolBar (ToolBarGroup eGroup, const OUString& rsToolBarName); + + /** Release all tool bar shells and the associated framework tool bars. + Typically called when the main view shell is being replaced by + another, all tool bar shells are released. In that process the + shells are destroyed anyway and without calling this method they + would still be referenced. + */ + void ReleaseAllToolBarShells(); + + void ToolBarsDestroyed(); + + void RequestUpdate(); + + void PreUpdate(); + void PostUpdate(); + /** Tell the XLayoutManager about the tool bars that we would like to be + shown. + @param rpLayouterLock + This typically is the mpSynchronousLayouterLock that is used in + this method and that is either released at its end or assigned + to mpAsynchronousLock in order to be unlocked later. + */ + void Update (::std::unique_ptr<LayouterLock> pLayouterLock); + + class UpdateLockImplementation + { + public: + explicit UpdateLockImplementation (Implementation& rImplementation) + : mrImplementation(rImplementation) { mrImplementation.LockUpdate(); } + ~UpdateLockImplementation() { mrImplementation.UnlockUpdate(); } + private: + Implementation& mrImplementation; + }; + + void LockViewShellManager(); + void LockUpdate(); + void UnlockUpdate(); + + ToolBarRules& GetToolBarRules() { return maToolBarRules;} + +private: + mutable ::osl::Mutex maMutex; + ViewShellBase& mrBase; + std::shared_ptr<sd::tools::EventMultiplexer> mpEventMultiplexer; + bool mbIsValid; + ToolBarList maToolBarList; + ToolBarShellList maToolBarShellList; + Reference<frame::XLayoutManager> mxLayouter; + sal_Int32 mnLockCount; + bool mbPreUpdatePending; + bool mbPostUpdatePending; + /** The layouter locks manage the locking of the XLayoutManager. The + lock() and unlock() functions are not called directly because the + (final) unlocking is usually done asynchronously *after* the + list of requested toolbars is updated. + */ + ::std::unique_ptr<LayouterLock> mpSynchronousLayouterLock; + ::std::unique_ptr<LayouterLock> mpAsynchronousLayouterLock; + ::std::unique_ptr<ViewShellManager::UpdateLock, o3tl::default_delete<ViewShellManager::UpdateLock>> mpViewShellManagerLock; + ImplSVEvent * mnPendingUpdateCall; + ImplSVEvent * mnPendingSetValidCall; + ToolBarRules maToolBarRules; + + static OUString GetToolBarResourceName (std::u16string_view rsBaseName); + bool CheckPlugInMode (std::u16string_view rsName) const; + + DECL_LINK(UpdateCallback, void *, void); + DECL_LINK(EventMultiplexerCallback, sd::tools::EventMultiplexerEvent&, void); + DECL_LINK(SetValidCallback, void*, void); +}; + +//===== ToolBarManager ======================================================== + +std::shared_ptr<ToolBarManager> ToolBarManager::Create ( + ViewShellBase& rBase, + const std::shared_ptr<sd::tools::EventMultiplexer>& rpMultiplexer, + const std::shared_ptr<ViewShellManager>& rpViewShellManager) +{ + std::shared_ptr<ToolBarManager> pManager (new ToolBarManager()); + pManager->mpImpl.reset( + new Implementation(rBase,rpMultiplexer,rpViewShellManager,pManager)); + return pManager; +} + +ToolBarManager::ToolBarManager() +{ +} + +ToolBarManager::~ToolBarManager() +{ +} + +void ToolBarManager::Shutdown() +{ + if (mpImpl != nullptr) + mpImpl.reset(); +} + +void ToolBarManager::ResetToolBars (ToolBarGroup eGroup) +{ + if (mpImpl != nullptr) + { + UpdateLock aLock (shared_from_this()); + mpImpl->ResetToolBars(eGroup); + } +} + +void ToolBarManager::ResetAllToolBars() +{ + if (mpImpl != nullptr) + { + UpdateLock aLock (shared_from_this()); + mpImpl->ResetAllToolBars(); + } +} + +void ToolBarManager::AddToolBar ( + ToolBarGroup eGroup, + const OUString& rsToolBarName) +{ + if (mpImpl != nullptr) + { + UpdateLock aLock (shared_from_this()); + mpImpl->AddToolBar(eGroup,rsToolBarName); + } +} + +void ToolBarManager::AddToolBarShell ( + ToolBarGroup eGroup, + ShellId nToolBarId) +{ + if (mpImpl != nullptr) + { + UpdateLock aLock (shared_from_this()); + mpImpl->AddToolBarShell(eGroup,nToolBarId); + } +} + +void ToolBarManager::RemoveToolBar ( + ToolBarGroup eGroup, + const OUString& rsToolBarName) +{ + if (mpImpl != nullptr) + { + UpdateLock aLock (shared_from_this()); + mpImpl->RemoveToolBar(eGroup,rsToolBarName); + } +} + +void ToolBarManager::SetToolBar ( + ToolBarGroup eGroup, + const OUString& rsToolBarName) +{ + if (mpImpl != nullptr) + { + UpdateLock aLock (shared_from_this()); + mpImpl->ResetToolBars(eGroup); + mpImpl->AddToolBar(eGroup,rsToolBarName); + } +} + +void ToolBarManager::SetToolBarShell ( + ToolBarGroup eGroup, + ShellId nToolBarId) +{ + if (mpImpl != nullptr) + { + UpdateLock aLock (shared_from_this()); + mpImpl->ResetToolBars(eGroup); + mpImpl->AddToolBarShell(eGroup,nToolBarId); + } +} + +void ToolBarManager::PreUpdate() +{ + if (mpImpl != nullptr) + mpImpl->PreUpdate(); +} + +void ToolBarManager::RequestUpdate() +{ + if (mpImpl != nullptr) + mpImpl->RequestUpdate(); +} + +void ToolBarManager::LockViewShellManager() +{ + if (mpImpl != nullptr) + mpImpl->LockViewShellManager(); +} + +void ToolBarManager::LockUpdate() +{ + if (mpImpl != nullptr) + mpImpl->LockUpdate(); +} + +void ToolBarManager::UnlockUpdate() +{ + if (mpImpl != nullptr) + mpImpl->UnlockUpdate(); +} + +void ToolBarManager::MainViewShellChanged () +{ + if (mpImpl != nullptr) + { + mpImpl->ReleaseAllToolBarShells(); + mpImpl->GetToolBarRules().MainViewShellChanged(ViewShell::ST_NONE); + } +} + +void ToolBarManager::MainViewShellChanged (const ViewShell& rMainViewShell) +{ + if (mpImpl != nullptr) + { + mpImpl->ReleaseAllToolBarShells(); + mpImpl->GetToolBarRules().MainViewShellChanged(rMainViewShell); + } +} + +void ToolBarManager::SelectionHasChanged ( + const ViewShell& rViewShell, + const SdrView& rView) +{ + if (mpImpl != nullptr) + mpImpl->GetToolBarRules().SelectionHasChanged(rViewShell,rView); +} + +void ToolBarManager::ToolBarsDestroyed() +{ + if (mpImpl != nullptr) + mpImpl->ToolBarsDestroyed(); +} + +//===== ToolBarManager::Implementation ======================================= + +ToolBarManager::Implementation::Implementation ( + ViewShellBase& rBase, + const std::shared_ptr<sd::tools::EventMultiplexer>& rpMultiplexer, + const std::shared_ptr<ViewShellManager>& rpViewShellManager, + const std::shared_ptr<ToolBarManager>& rpToolBarManager) + : mrBase(rBase), + mpEventMultiplexer(rpMultiplexer), + mbIsValid(false), + mnLockCount(0), + mbPreUpdatePending(false), + mbPostUpdatePending(false), + mnPendingUpdateCall(nullptr), + mnPendingSetValidCall(nullptr), + maToolBarRules(rpToolBarManager,rpViewShellManager) +{ + Link<tools::EventMultiplexerEvent&,void> aLink (LINK(this,ToolBarManager::Implementation,EventMultiplexerCallback)); + mpEventMultiplexer->AddEventListener( aLink ); +} + +/** The order of statements is important. + First unregister listeners, which may post user events. + Then remove pending user events. +*/ +ToolBarManager::Implementation::~Implementation() +{ + // Unregister at broadcasters. + Link<tools::EventMultiplexerEvent&,void> aLink (LINK(this,ToolBarManager::Implementation,EventMultiplexerCallback)); + mpEventMultiplexer->RemoveEventListener(aLink); + + // Abort pending user calls. + if (mnPendingUpdateCall != nullptr) + Application::RemoveUserEvent(mnPendingUpdateCall); + if (mnPendingSetValidCall != nullptr) + Application::RemoveUserEvent(mnPendingSetValidCall); +} + +void ToolBarManager::Implementation::ToolBarsDestroyed() +{ + maToolBarList.MarkAllToolBarsAsNotActive(); +} + +void ToolBarManager::Implementation::SetValid (bool bValid) +{ + ::osl::MutexGuard aGuard(maMutex); + + if (mbIsValid == bValid) + return; + + UpdateLockImplementation aUpdateLock (*this); + + mbIsValid = bValid; + if (mbIsValid) + { + Reference<frame::XFrame> xFrame; + if (mrBase.GetViewFrame() != nullptr) + xFrame = mrBase.GetViewFrame()->GetFrame().GetFrameInterface(); + try + { + Reference<beans::XPropertySet> xFrameProperties (xFrame, UNO_QUERY_THROW); + Any aValue (xFrameProperties->getPropertyValue("LayoutManager")); + aValue >>= mxLayouter; + // tdf#119997 if mpSynchronousLayouterLock was created before mxLayouter was + // set then update it now that its available + if (mpSynchronousLayouterLock && !mpSynchronousLayouterLock->is()) + mpSynchronousLayouterLock.reset(new LayouterLock(mxLayouter)); + } + catch (const RuntimeException&) + { + } + + GetToolBarRules().Update(mrBase); + } + else + { + ResetAllToolBars(); + mxLayouter = nullptr; + } +} + +void ToolBarManager::Implementation::ResetToolBars (ToolBarGroup eGroup) +{ + ::osl::MutexGuard aGuard(maMutex); + + maToolBarList.ClearGroup(eGroup); + maToolBarShellList.ClearGroup(eGroup); + + mbPreUpdatePending = true; +} + +void ToolBarManager::Implementation::ResetAllToolBars() +{ + SAL_INFO("sd.view", __func__ << ": resetting all tool bars"); + for (auto i : o3tl::enumrange<ToolBarGroup>()) + ResetToolBars(i); +} + +void ToolBarManager::Implementation::AddToolBar ( + ToolBarGroup eGroup, + const OUString& rsToolBarName) +{ + ::osl::MutexGuard aGuard(maMutex); + + if (CheckPlugInMode(rsToolBarName)) + { + maToolBarList.AddToolBar(eGroup,rsToolBarName); + + mbPostUpdatePending = true; + if (mnLockCount == 0) + PostUpdate(); + } +} + +void ToolBarManager::Implementation::RemoveToolBar ( + ToolBarGroup eGroup, + const OUString& rsToolBarName) +{ + ::osl::MutexGuard aGuard(maMutex); + + if (maToolBarList.RemoveToolBar(eGroup,rsToolBarName)) + { + mbPreUpdatePending = true; + if (mnLockCount == 0) + PreUpdate(); + } +} + +void ToolBarManager::Implementation::AddToolBarShell ( + ToolBarGroup eGroup, + ShellId nToolBarId) +{ + ViewShell* pMainViewShell = mrBase.GetMainViewShell().get(); + if (pMainViewShell != nullptr) + { + maToolBarShellList.AddShellId(eGroup,nToolBarId); + GetToolBarRules().SubShellAdded(eGroup, nToolBarId); + } +} + +void ToolBarManager::Implementation::ReleaseAllToolBarShells() +{ + maToolBarShellList.ReleaseAllShells(GetToolBarRules()); + maToolBarShellList.UpdateShells(mrBase.GetMainViewShell(), mrBase.GetViewShellManager()); +} + +void ToolBarManager::Implementation::RequestUpdate() +{ + if (mnPendingUpdateCall == nullptr) + { + mnPendingUpdateCall = Application::PostUserEvent( + LINK(this,ToolBarManager::Implementation,UpdateCallback)); + } +} + +void ToolBarManager::Implementation::PreUpdate() +{ + ::osl::MutexGuard aGuard(maMutex); + + if (!(mbIsValid + && mbPreUpdatePending + && mxLayouter.is())) + return; + + mbPreUpdatePending = false; + + SAL_INFO("sd.view", __func__ << ": ToolBarManager::PreUpdate ["); + + // Get the list of tool bars that are not used anymore and are to be + // deactivated. + std::vector<OUString> aToolBars; + maToolBarList.GetToolBarsToDeactivate(aToolBars); + + // Turn off the tool bars. + for (const auto& aToolBar : aToolBars) + { + OUString sFullName (GetToolBarResourceName(aToolBar)); + SAL_INFO("sd.view", __func__ << ": turning off tool bar " << sFullName); + mxLayouter->destroyElement(sFullName); + maToolBarList.MarkToolBarAsNotActive(aToolBar); + } + + SAL_INFO("sd.view", __func__ << ": ToolBarManager::PreUpdate ]"); +} + +void ToolBarManager::Implementation::PostUpdate() +{ + ::osl::MutexGuard aGuard(maMutex); + + if (!(mbIsValid + && mbPostUpdatePending + && mxLayouter.is())) + return; + + mbPostUpdatePending = false; + + // Create the list of requested tool bars. + std::vector<OUString> aToolBars; + maToolBarList.GetToolBarsToActivate(aToolBars); + + SAL_INFO("sd.view", __func__ << ": ToolBarManager::PostUpdate ["); + + // Turn on the tool bars that are visible in the new context. + for (const auto& aToolBar : aToolBars) + { + OUString sFullName (GetToolBarResourceName(aToolBar)); + SAL_INFO("sd.view", __func__ << ": turning on tool bar " << sFullName); + mxLayouter->requestElement(sFullName); + maToolBarList.MarkToolBarAsActive(aToolBar); + } + + SAL_INFO("sd.view", __func__ << ": ToolBarManager::PostUpdate ]"); +} + +void ToolBarManager::Implementation::LockViewShellManager() +{ + if (mpViewShellManagerLock == nullptr) + mpViewShellManagerLock.reset( + new ViewShellManager::UpdateLock(mrBase.GetViewShellManager())); +} + +void ToolBarManager::Implementation::LockUpdate() +{ + SAL_INFO("sd.view", __func__ << ": LockUpdate " << mnLockCount); + ::osl::MutexGuard aGuard(maMutex); + + DBG_ASSERT(mnLockCount<100, "ToolBarManager lock count unusually high"); + if (mnLockCount == 0) + { + OSL_ASSERT(mpSynchronousLayouterLock == nullptr); + + mpSynchronousLayouterLock.reset(new LayouterLock(mxLayouter)); + } + ++mnLockCount; +} + +void ToolBarManager::Implementation::UnlockUpdate() +{ + SAL_INFO("sd.view", __func__ << ": UnlockUpdate " << mnLockCount); + ::osl::MutexGuard aGuard(maMutex); + + OSL_ASSERT(mnLockCount>0); + --mnLockCount; + if (mnLockCount == 0) + { + Update(std::move(mpSynchronousLayouterLock)); + } +} + +void ToolBarManager::Implementation::Update ( + ::std::unique_ptr<LayouterLock> pLocalLayouterLock) +{ + // When the lock is released and there are pending changes to the set of + // tool bars then update this set now. + if (mnLockCount != 0) + return; + + // During creation of ViewShellBase we may have the situation that + // the controller has already been created and attached to the frame + // but that the ToolBarManager has not yet completed its + // initialization (by initializing the mxLayouter member.) We do + // this here so that we do not have to wait for the next Update() + // call to show the tool bars. + if (mnPendingSetValidCall != nullptr) + { + Application::RemoveUserEvent(mnPendingSetValidCall); + mnPendingSetValidCall = nullptr; + SetValid(true); + } + + if (mbIsValid && mxLayouter.is() && (mbPreUpdatePending || mbPostUpdatePending)) + { + // 1) Release UNO tool bars that are no longer used. Do this + // now so that they are not updated when the SFX shell stack is + // modified. + if (mbPreUpdatePending) + PreUpdate(); + + // 2) Update the requested shells that represent tool bar + // functionality. Those that are not used anymore are + // deactivated now. Those that are missing are activated in the + // next step together with the view shells. + if (mpViewShellManagerLock == nullptr) + mpViewShellManagerLock.reset( + new ViewShellManager::UpdateLock(mrBase.GetViewShellManager())); + maToolBarShellList.UpdateShells( + mrBase.GetMainViewShell(), + mrBase.GetViewShellManager()); + + // 3) Unlock the ViewShellManager::UpdateLock. This updates the + // shell stack. + mpViewShellManagerLock.reset(); + + // 4) Make the UNO tool bars visible. The outstanding call to + // PostUpdate() is done via PostUserEvent() so that it is + // guaranteed to be executed when the SFX shell stack has been + // updated (under the assumption that our lock to the + // ViewShellManager was the only one open. If that is not the + // case then all should still be well but not as fast.) + + // Note that the lock count may have been increased since + // entering this method. In that case one of the next + // UnlockUpdate() calls will post the UpdateCallback. + if (mnLockCount==0) + { + mpAsynchronousLayouterLock = std::move(pLocalLayouterLock); + if (mnPendingUpdateCall==nullptr) + { + mnPendingUpdateCall = Application::PostUserEvent( + LINK(this,ToolBarManager::Implementation,UpdateCallback)); + } + } + } + else + { + mpViewShellManagerLock.reset(); + pLocalLayouterLock.reset(); + } +} + +IMPL_LINK_NOARG(ToolBarManager::Implementation, UpdateCallback, void*, void) +{ + mnPendingUpdateCall = nullptr; + if (mnLockCount == 0) + { + if (mbPreUpdatePending) + PreUpdate(); + if (mbPostUpdatePending) + PostUpdate(); + if (mbIsValid && mxLayouter.is()) + mpAsynchronousLayouterLock.reset(); + } +} + +IMPL_LINK(ToolBarManager::Implementation,EventMultiplexerCallback, + sd::tools::EventMultiplexerEvent&, rEvent, void) +{ + SolarMutexGuard g; + switch (rEvent.meEventId) + { + case EventMultiplexerEventId::ControllerAttached: + if (mnPendingSetValidCall == nullptr) + mnPendingSetValidCall + = Application::PostUserEvent(LINK(this,Implementation,SetValidCallback)); + break; + + case EventMultiplexerEventId::ControllerDetached: + SetValid(false); + break; + + default: break; + } +} + +IMPL_LINK_NOARG(ToolBarManager::Implementation, SetValidCallback, void*, void) +{ + mnPendingSetValidCall = nullptr; + SetValid(true); +} + +OUString ToolBarManager::Implementation::GetToolBarResourceName ( + std::u16string_view rsBaseName) +{ + return OUString::Concat("private:resource/toolbar/") + rsBaseName; +} + +bool ToolBarManager::Implementation::CheckPlugInMode (std::u16string_view rsName) const +{ + bool bValid (false); + + // Determine the plug in mode. + bool bIsPlugInMode (false); + do + { + SfxObjectShell* pObjectShell = mrBase.GetObjectShell(); + if (pObjectShell == nullptr) + break; + + SfxMedium* pMedium = pObjectShell->GetMedium(); + if (pMedium == nullptr) + break; + + const SfxBoolItem* pViewOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_VIEWONLY, false); + if (pViewOnlyItem == nullptr) + break; + + bIsPlugInMode = pViewOnlyItem->GetValue(); + } + while (false); + + if (rsName == msViewerToolBar) + bValid = bIsPlugInMode; + else + bValid = ! bIsPlugInMode; + + return bValid; +} + +} // end of namespace sd + +namespace { + +using namespace ::sd; + +//===== LayouterLock ========================================================== + +LayouterLock::LayouterLock (const Reference<frame::XLayoutManager>& rxLayouter) + : mxLayouter(rxLayouter) +{ + SAL_INFO("sd.view", __func__ << ": LayouterLock " << (mxLayouter.is() ? 1 :0)); + if (mxLayouter.is()) + mxLayouter->lock(); +} + +LayouterLock::~LayouterLock() +{ + SAL_INFO("sd.view", __func__ << ": ~LayouterLock " << (mxLayouter.is() ? 1 :0)); + if (mxLayouter.is()) + mxLayouter->unlock(); +} + +//===== ToolBarRules ========================================================== + +ToolBarRules::ToolBarRules ( + const std::shared_ptr<sd::ToolBarManager>& rpToolBarManager, + const std::shared_ptr<sd::ViewShellManager>& rpViewShellManager) + : mpToolBarManager(rpToolBarManager), + mpViewShellManager(rpViewShellManager) +{ +} + +void ToolBarRules::Update (ViewShellBase const & rBase) +{ + ViewShell* pMainViewShell = rBase.GetMainViewShell().get(); + if (pMainViewShell != nullptr) + { + MainViewShellChanged(pMainViewShell->GetShellType()); + if (pMainViewShell->GetView()) + SelectionHasChanged (*pMainViewShell, *pMainViewShell->GetView()); + } + else + MainViewShellChanged(ViewShell::ST_NONE); +} + +void ToolBarRules::MainViewShellChanged (ViewShell::ShellType nShellType) +{ + ::sd::ToolBarManager::UpdateLock aToolBarManagerLock (mpToolBarManager); + ::sd::ViewShellManager::UpdateLock aViewShellManagerLock (mpViewShellManager); + + mpToolBarManager->ResetAllToolBars(); + + switch(nShellType) + { + case ::sd::ViewShell::ST_IMPRESS: + case ::sd::ViewShell::ST_NOTES: + case ::sd::ViewShell::ST_HANDOUT: + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msToolBar); + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msOptionsToolBar); + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msViewerToolBar); + break; + + case ::sd::ViewShell::ST_DRAW: + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msToolBar); + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msOptionsToolBar); + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msViewerToolBar); + break; + + case ViewShell::ST_OUTLINE: + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msOutlineToolBar); + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msViewerToolBar); + mpToolBarManager->AddToolBarShell( + ToolBarManager::ToolBarGroup::Permanent, ToolbarId::Draw_Text_Toolbox_Sd); + break; + + case ViewShell::ST_SLIDE_SORTER: + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msViewerToolBar); + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msSlideSorterToolBar); + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msSlideSorterObjectBar); + break; + + case ViewShell::ST_NONE: + case ViewShell::ST_PRESENTATION: + case ViewShell::ST_SIDEBAR: + default: + break; + } +} + +void ToolBarRules::MainViewShellChanged (const ViewShell& rMainViewShell) +{ + ::sd::ToolBarManager::UpdateLock aToolBarManagerLock (mpToolBarManager); + ::sd::ViewShellManager::UpdateLock aViewShellManagerLock (mpViewShellManager); + + MainViewShellChanged(rMainViewShell.GetShellType()); + switch(rMainViewShell.GetShellType()) + { + case ::sd::ViewShell::ST_IMPRESS: + case ::sd::ViewShell::ST_DRAW: + case ::sd::ViewShell::ST_NOTES: + { + const DrawViewShell* pDrawViewShell + = dynamic_cast<const DrawViewShell*>(&rMainViewShell); + if (pDrawViewShell != nullptr) + { + if (pDrawViewShell->GetEditMode() == EditMode::MasterPage) + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::MasterMode, + ToolBarManager::msMasterViewToolBar); + else if ( rMainViewShell.GetShellType() != ::sd::ViewShell::ST_DRAW ) + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::CommonTask, + ToolBarManager::msCommonTaskToolBar); + } + break; + } + + default: + break; + } +} + +void ToolBarRules::SelectionHasChanged ( + const ::sd::ViewShell& rViewShell, + const SdrView& rView) +{ + ::sd::ToolBarManager::UpdateLock aLock (mpToolBarManager); + mpToolBarManager->LockViewShellManager(); + bool bTextEdit = rView.IsTextEdit(); + + mpToolBarManager->ResetToolBars(ToolBarManager::ToolBarGroup::Function); + + switch (rView.GetContext()) + { + case SdrViewContext::Graphic: + if( !bTextEdit ) + mpToolBarManager->SetToolBarShell(ToolBarManager::ToolBarGroup::Function, ToolbarId::Draw_Graf_Toolbox); + break; + + case SdrViewContext::Media: + if( !bTextEdit ) + mpToolBarManager->SetToolBarShell(ToolBarManager::ToolBarGroup::Function, ToolbarId::Draw_Media_Toolbox); + break; + + case SdrViewContext::Table: + mpToolBarManager->SetToolBarShell(ToolBarManager::ToolBarGroup::Function, ToolbarId::Draw_Table_Toolbox); + bTextEdit = true; + break; + + case SdrViewContext::Standard: + default: + if( !bTextEdit ) + { + switch(rViewShell.GetShellType()) + { + case ::sd::ViewShell::ST_IMPRESS: + case ::sd::ViewShell::ST_DRAW: + case ::sd::ViewShell::ST_NOTES: + case ::sd::ViewShell::ST_HANDOUT: + mpToolBarManager->SetToolBar( + ToolBarManager::ToolBarGroup::Function, + ToolBarManager::msDrawingObjectToolBar); + break; + default: + break; + } + break; + } + } + + if( bTextEdit ) + mpToolBarManager->AddToolBarShell(ToolBarManager::ToolBarGroup::Function, ToolbarId::Draw_Text_Toolbox_Sd); + + SdrView* pView = &const_cast<SdrView&>(rView); + // Check if the extrusion tool bar and the fontwork tool bar have to + // be activated. + if (svx::checkForSelectedCustomShapes(pView, true /* bOnlyExtruded */ )) + mpToolBarManager->AddToolBarShell(ToolBarManager::ToolBarGroup::Function, ToolbarId::Svx_Extrusion_Bar); + + if (svx::checkForSelectedFontWork(pView)) + mpToolBarManager->AddToolBarShell(ToolBarManager::ToolBarGroup::Function, ToolbarId::Svx_Fontwork_Bar); + + // Switch on additional context-sensitive tool bars. + if (rView.GetContext() == SdrViewContext::PointEdit) + mpToolBarManager->AddToolBarShell(ToolBarManager::ToolBarGroup::Function, ToolbarId::Bezier_Toolbox_Sd); +} + +void ToolBarRules::SubShellAdded ( + ::sd::ToolBarManager::ToolBarGroup eGroup, + sd::ShellId nShellId) +{ + // For some tool bar shells (those defined in sd) we have to add the + // actual tool bar here. + switch (nShellId) + { + case ToolbarId::Draw_Graf_Toolbox: + mpToolBarManager->AddToolBar(eGroup, ToolBarManager::msGraphicObjectBar); + break; + + case ToolbarId::Draw_Media_Toolbox: + mpToolBarManager->AddToolBar(eGroup, ToolBarManager::msMediaObjectBar); + break; + + case ToolbarId::Draw_Text_Toolbox_Sd: + mpToolBarManager->AddToolBar(eGroup, ToolBarManager::msTextObjectBar); + break; + + case ToolbarId::Bezier_Toolbox_Sd: + mpToolBarManager->AddToolBar(eGroup, ToolBarManager::msBezierObjectBar); + break; + + case ToolbarId::Draw_Table_Toolbox: + mpToolBarManager->AddToolBar(eGroup, ToolBarManager::msTableObjectBar); + break; + + default: + break; + } +} + +void ToolBarRules::SubShellRemoved ( + ::sd::ToolBarManager::ToolBarGroup eGroup, + sd::ShellId nShellId) +{ + // For some tool bar shells (those defined in sd) we have to add the + // actual tool bar here. + switch (nShellId) + { + case ToolbarId::Draw_Graf_Toolbox: + mpToolBarManager->RemoveToolBar(eGroup, ToolBarManager::msGraphicObjectBar); + break; + + case ToolbarId::Draw_Media_Toolbox: + mpToolBarManager->RemoveToolBar(eGroup, ToolBarManager::msMediaObjectBar); + break; + + case ToolbarId::Draw_Text_Toolbox_Sd: + mpToolBarManager->RemoveToolBar(eGroup, ToolBarManager::msTextObjectBar); + break; + + case ToolbarId::Bezier_Toolbox_Sd: + mpToolBarManager->RemoveToolBar(eGroup, ToolBarManager::msBezierObjectBar); + break; + + case ToolbarId::Draw_Table_Toolbox: + mpToolBarManager->RemoveToolBar(eGroup, ToolBarManager::msTableObjectBar); + break; + + default: + break; + } +} + +//===== ToolBarList =========================================================== + +ToolBarList::ToolBarList() +{ +} + +void ToolBarList::ClearGroup (sd::ToolBarManager::ToolBarGroup eGroup) +{ + Groups::iterator iGroup (maGroups.find(eGroup)); + if (iGroup != maGroups.end()) + { + iGroup->second.clear(); + } +} + +void ToolBarList::AddToolBar ( + sd::ToolBarManager::ToolBarGroup eGroup, + const OUString& rsName) +{ + Groups::iterator iGroup (maGroups.find(eGroup)); + if (iGroup == maGroups.end()) + iGroup = maGroups.emplace(eGroup,std::vector<OUString>()).first; + + if (iGroup != maGroups.end()) + { + auto iBar (std::find(iGroup->second.cbegin(),iGroup->second.cend(),rsName)); + if (iBar == iGroup->second.cend()) + { + iGroup->second.push_back(rsName); + } + } +} + +bool ToolBarList::RemoveToolBar ( + sd::ToolBarManager::ToolBarGroup eGroup, + const OUString& rsName) +{ + Groups::iterator iGroup (maGroups.find(eGroup)); + if (iGroup != maGroups.end()) + { + auto iBar (std::find(iGroup->second.begin(),iGroup->second.end(),rsName)); + if (iBar != iGroup->second.end()) + { + iGroup->second.erase(iBar); + return true; + } + } + return false; +} + +void ToolBarList::MakeRequestedToolBarList (std::vector<OUString>& rRequestedToolBars) const +{ + for (auto eGroup : o3tl::enumrange<sd::ToolBarManager::ToolBarGroup>()) + { + Groups::const_iterator iGroup (maGroups.find(eGroup)); + if (iGroup != maGroups.end()) + rRequestedToolBars.insert( rRequestedToolBars.end(), + iGroup->second.begin(), + iGroup->second.end() ); + } +} + +void ToolBarList::GetToolBarsToActivate (std::vector<OUString>& rToolBars) const +{ + std::vector<OUString> aRequestedToolBars; + MakeRequestedToolBarList(aRequestedToolBars); + + for (const auto& aToolBar : aRequestedToolBars) + { + if (::std::find(maActiveToolBars.begin(),maActiveToolBars.end(),aToolBar) + == maActiveToolBars.end()) + { + rToolBars.push_back(aToolBar); + } + } +} + +void ToolBarList::GetToolBarsToDeactivate (std::vector<OUString>& rToolBars) const +{ + std::vector<OUString> aRequestedToolBars; + MakeRequestedToolBarList(aRequestedToolBars); + + for (auto& aToolBar : maActiveToolBars) + { + if (::std::find(aRequestedToolBars.begin(),aRequestedToolBars.end(),aToolBar) + == aRequestedToolBars.end()) + { + rToolBars.push_back(aToolBar); + } + } +} + +void ToolBarList::MarkToolBarAsActive (const OUString& rsName) +{ + maActiveToolBars.push_back(rsName); +} + +void ToolBarList::MarkToolBarAsNotActive (const OUString& rsName) +{ + maActiveToolBars.erase( + ::std::find(maActiveToolBars.begin(),maActiveToolBars.end(), rsName)); +} + +void ToolBarList::MarkAllToolBarsAsNotActive() +{ + maActiveToolBars.clear(); +} + +//===== ToolBarShellList ====================================================== + +ToolBarShellList::ShellDescriptor::ShellDescriptor ( + ShellId nId, + sd::ToolBarManager::ToolBarGroup eGroup) + : mnId(nId), + meGroup(eGroup) +{ +} + +ToolBarShellList::ToolBarShellList() +{ +} + +void ToolBarShellList::ClearGroup (sd::ToolBarManager::ToolBarGroup eGroup) +{ + for (GroupedShellList::iterator iDescriptor = maNewList.begin(); iDescriptor != maNewList.end(); ) + if (iDescriptor->meGroup == eGroup) + iDescriptor = maNewList.erase(iDescriptor); + else + ++iDescriptor; +} + +void ToolBarShellList::AddShellId (sd::ToolBarManager::ToolBarGroup eGroup, sd::ShellId nId) +{ + // Make sure that the shell is not added twice (and possibly in + // different groups.) + ShellDescriptor aDescriptor (nId,eGroup); + GroupedShellList::iterator iDescriptor (maNewList.find(aDescriptor)); + if (iDescriptor != maNewList.end()) + { + // The shell is already requested. + if (iDescriptor->meGroup != eGroup) + { + // It is now being requested for another group. + // (Is this an error?) + // Move it to that group. + maNewList.erase(iDescriptor); + maNewList.insert(aDescriptor); + } + // else nothing to do. + } + else + maNewList.insert(aDescriptor); +} + +void ToolBarShellList::ReleaseAllShells (ToolBarRules& rRules) +{ + // Release the currently active tool bars. + GroupedShellList aList (maCurrentList); + for (const auto& rDescriptor : aList) + { + rRules.SubShellRemoved(rDescriptor.meGroup, rDescriptor.mnId); + } + + // Clear the list of requested tool bars. + maNewList.clear(); +} + +void ToolBarShellList::UpdateShells ( + const std::shared_ptr<ViewShell>& rpMainViewShell, + const std::shared_ptr<ViewShellManager>& rpManager) +{ + if (rpMainViewShell == nullptr) + return; + + GroupedShellList aList; + + // Deactivate shells that are in maCurrentList, but not in + // maNewList. + ::std::set_difference(maCurrentList.begin(), maCurrentList.end(), + maNewList.begin(), maNewList.end(), + std::insert_iterator<GroupedShellList>(aList,aList.begin())); + for (const auto& rShell : aList) + { + SAL_INFO("sd.view", __func__ << ": deactivating tool bar shell " << static_cast<sal_uInt32>(rShell.mnId)); + rpManager->DeactivateSubShell(*rpMainViewShell, rShell.mnId); + } + + // Activate shells that are in maNewList, but not in + // maCurrentList. + aList.clear(); + ::std::set_difference(maNewList.begin(), maNewList.end(), + maCurrentList.begin(), maCurrentList.end(), + std::insert_iterator<GroupedShellList>(aList,aList.begin())); + for (const auto& rShell : aList) + { + SAL_INFO("sd.view", __func__ << ": activating tool bar shell " << static_cast<sal_uInt32>(rShell.mnId)); + rpManager->ActivateSubShell(*rpMainViewShell, rShell.mnId); + } + + // The maNewList now reflects the current state and thus is made + // maCurrentList. + maCurrentList = maNewList; +} + +} // end of anonymous namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/ViewClipboard.cxx b/sd/source/ui/view/ViewClipboard.cxx new file mode 100644 index 000000000..c17bf7de1 --- /dev/null +++ b/sd/source/ui/view/ViewClipboard.cxx @@ -0,0 +1,240 @@ +/* -*- 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 <ViewClipboard.hxx> + +#include <DrawDocShell.hxx> +#include <DrawViewShell.hxx> +#include <View.hxx> +#include <ViewShell.hxx> +#include <Window.hxx> + +#include <drawdoc.hxx> +#include <sdmod.hxx> +#include <sdpage.hxx> +#include <sdxfer.hxx> +#include <strings.hxx> + +#include <svx/svdpagv.hxx> +#include <vcl/svapp.hxx> + +namespace sd { + +ViewClipboard::ViewClipboard (::sd::View& rView) + : mrView(rView) +{ +} + +ViewClipboard::~ViewClipboard() +{ +} + +void ViewClipboard::HandlePageDrop (const SdTransferable& rTransferable) +{ + // Determine whether to insert the given set of slides or to assign a + // given master page. + // tdf#113405 only assign master pages to normal pages, don't attempt to assign a master + // page to a master page + sd::DrawViewShell* pDrawViewShell = dynamic_cast<::sd::DrawViewShell*>(mrView.GetViewShell()); + SdPage* pMasterPage = (pDrawViewShell && pDrawViewShell->GetEditMode() == EditMode::Page) ? GetFirstMasterPage(rTransferable) : nullptr; + if (pMasterPage) + AssignMasterPage (rTransferable, pMasterPage); + else + InsertSlides (rTransferable, DetermineInsertPosition ()); +} + +SdPage* ViewClipboard::GetFirstMasterPage (const SdTransferable& rTransferable) +{ + SdPage* pFirstMasterPage = nullptr; + + if (rTransferable.HasPageBookmarks()) + { + do + { + const std::vector<OUString> &rBookmarks = rTransferable.GetPageBookmarks(); + + if (rBookmarks.empty()) + break; + + DrawDocShell* pDocShell = rTransferable.GetPageDocShell(); + if (pDocShell == nullptr) + break; + + SdDrawDocument* pDocument = pDocShell->GetDoc(); + if (pDocument == nullptr) + break; + + for (const OUString& sName : rBookmarks) + { + bool bIsMasterPage; + + // SdPage* GetMasterSdPage(sal_uInt16 nPgNum, PageKind ePgKind); + // sal_uInt16 GetMasterSdPageCount(PageKind ePgKind) const; + + sal_uInt16 nBMPage = pDocument->GetPageByName ( + sName, bIsMasterPage); + if ( ! bIsMasterPage) + { + // At least one regular slide: return NULL to indicate + // that not all bookmarks point to master pages. + pFirstMasterPage = nullptr; + break; + } + else if (pFirstMasterPage == nullptr) + { + // Remember the first master page for later. + if (nBMPage != SDRPAGE_NOTFOUND) + pFirstMasterPage = static_cast<SdPage*>( + pDocument->GetMasterPage(nBMPage)); + } + } + } + while (false); + } + + return pFirstMasterPage; +} + +void ViewClipboard::AssignMasterPage ( + const SdTransferable& rTransferable, + SdPage const * pMasterPage) +{ + if (pMasterPage == nullptr) + return; + + // Get the target page to which the master page is assigned. + SdrPageView* pPageView = mrView.GetSdrPageView(); + if (pPageView == nullptr) + return; + + SdPage* pPage = static_cast<SdPage*>(pPageView->GetPage()); + if (pPage == nullptr) + return; + + SdDrawDocument& rDocument = mrView.GetDoc(); + + if ( ! rTransferable.HasPageBookmarks()) + return; + + DrawDocShell* pDataDocShell = rTransferable.GetPageDocShell(); + if (pDataDocShell == nullptr) + return; + + SdDrawDocument* pSourceDocument = pDataDocShell->GetDoc(); + if (pSourceDocument == nullptr) + return; + + // We have to remove the layout suffix from the layout name which is + // appended again by SetMasterPage() to the given name. Don't ask. + OUString sLayoutSuffix = SD_LT_SEPARATOR + STR_LAYOUT_OUTLINE; + sal_Int32 nLength = sLayoutSuffix.getLength(); + OUString sLayoutName = pMasterPage->GetLayoutName(); + if (sLayoutName.endsWith(sLayoutSuffix)) + sLayoutName = sLayoutName.copy(0, sLayoutName.getLength() - nLength); + + rDocument.SetMasterPage ( + pPage->GetPageNum() / 2, + sLayoutName, + pSourceDocument, + false, // Exchange the master page of only the target page. + false // Keep unused master pages. + ); +} + +sal_uInt16 ViewClipboard::DetermineInsertPosition () +{ + SdDrawDocument& rDoc = mrView.GetDoc(); + sal_uInt16 nPgCnt = rDoc.GetSdPageCount( PageKind::Standard ); + + // Insert position is the behind the last selected page or behind the + // last page when the selection is empty. + sal_uInt16 nInsertPos = rDoc.GetSdPageCount( PageKind::Standard ) * 2 + 1; + for( sal_uInt16 nPage = 0; nPage < nPgCnt; nPage++ ) + { + SdPage* pPage = rDoc.GetSdPage( nPage, PageKind::Standard ); + + if( pPage->IsSelected() ) + nInsertPos = nPage * 2 + 3; + } + + return nInsertPos; +} + +sal_uInt16 ViewClipboard::InsertSlides ( + const SdTransferable& rTransferable, + sal_uInt16 nInsertPosition) +{ + SdDrawDocument& rDoc = mrView.GetDoc(); + + sal_uInt16 nInsertPgCnt = 0; + bool bMergeMasterPages = !rTransferable.HasSourceDoc( &rDoc ); + + // Prepare the insertion. + const std::vector<OUString> *pBookmarkList = nullptr; + DrawDocShell* pDataDocSh; + if (rTransferable.HasPageBookmarks()) + { + // When the transferable contains page bookmarks then the referenced + // pages are inserted. + pBookmarkList = &rTransferable.GetPageBookmarks(); + pDataDocSh = rTransferable.GetPageDocShell(); + nInsertPgCnt = static_cast<sal_uInt16>(pBookmarkList->size()); + } + else + { + // Otherwise all pages of the document of the transferable are + // inserted. + SfxObjectShell* pShell = rTransferable.GetDocShell().get(); + pDataDocSh = static_cast<DrawDocShell*>(pShell); + SdDrawDocument* pDataDoc = pDataDocSh->GetDoc(); + + if (pDataDoc!=nullptr && pDataDoc->GetSdPageCount(PageKind::Standard)) + nInsertPgCnt = pDataDoc->GetSdPageCount(PageKind::Standard); + } + if (nInsertPgCnt > 0) + { + const SolarMutexGuard aGuard; + ::sd::Window* pWin = mrView.GetViewShell()->GetActiveWindow(); + const bool bWait = pWin && pWin->IsWait(); + + if( bWait ) + pWin->LeaveWait(); + + rDoc.InsertBookmarkAsPage( + pBookmarkList ? *pBookmarkList : std::vector<OUString>(), + nullptr, + false, + false, + nInsertPosition, + (&rTransferable == SD_MOD()->pTransferDrag), + pDataDocSh, + true, + bMergeMasterPages, + false); + + if( bWait ) + pWin->EnterWait(); + } + + return nInsertPgCnt; +} + +} // end of namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/ViewShellBase.cxx b/sd/source/ui/view/ViewShellBase.cxx new file mode 100644 index 000000000..26245971f --- /dev/null +++ b/sd/source/ui/view/ViewShellBase.cxx @@ -0,0 +1,1456 @@ +/* -*- 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 <comphelper/processfactory.hxx> + +#include <ViewShellBase.hxx> +#include <algorithm> +#include <EventMultiplexer.hxx> +#include <cache/SlsPageCacheManager.hxx> +#include <app.hrc> +#include <slideshow.hxx> +#include <unokywds.hxx> +#include <svx/svxids.hrc> +#include <DrawDocShell.hxx> +#include <ViewShellManager.hxx> +#include <DrawController.hxx> +#include <FrameView.hxx> +#include <ViewTabBar.hxx> +#include <sfx2/event.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/request.hxx> +#include <sfx2/printer.hxx> +#include <DrawViewShell.hxx> +#include <FormShellManager.hxx> +#include <ToolBarManager.hxx> +#include <Window.hxx> +#include <framework/ConfigurationController.hxx> +#include <DocumentRenderer.hxx> +#include <optsitem.hxx> +#include <sdmod.hxx> + +#include <com/sun/star/document/XViewDataSupplier.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/drawing/XDrawPagesSupplier.hpp> +#include <com/sun/star/drawing/XMasterPagesSupplier.hpp> +#include <com/sun/star/drawing/framework/XControllerManager.hpp> +#include <com/sun/star/drawing/framework/XConfigurationController.hpp> +#include <com/sun/star/drawing/framework/ResourceId.hpp> +#include <framework/FrameworkHelper.hxx> + +#include <sal/log.hxx> +#include <rtl/ref.hxx> +#include <sfx2/msg.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/whiter.hxx> +#include <vcl/commandinfoprovider.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> + +#include <sfx2/notebookbar/SfxNotebookBar.hxx> + +#include <tools/diagnose_ex.h> +#include <sfx2/lokhelper.hxx> +#include <LibreOfficeKit/LibreOfficeKitEnums.h> +#include <editeng/editview.hxx> +#include <tools/svborder.hxx> + +#include <fubullet.hxx> +#include <drawview.hxx> + +using namespace sd; +#define ShellClass_ViewShellBase +#include <sdslots.hxx> + +using ::sd::framework::FrameworkHelper; + +using namespace com::sun::star; +using namespace com::sun::star::beans; +using namespace com::sun::star::container; +using namespace com::sun::star::drawing::framework; +using namespace com::sun::star::lang; +using namespace com::sun::star::uno; + +namespace { + +class CurrentPageSetter +{ +public: + explicit CurrentPageSetter (ViewShellBase& rBase); + void operator () (bool); +private: + ViewShellBase& mrBase; +}; + +} // end of anonymous namespace + +namespace sd { + +class ViewShellBase::Implementation +{ +public: + /** Main controller of the view shell. During the switching from one + stacked shell to another this pointer may be NULL. + */ + ::rtl::Reference<DrawController> mpController; + + /** The view tab bar is the control for switching between different + views in one pane. + */ + ::rtl::Reference<ViewTabBar> mpViewTabBar; + + // contains the complete area of the current view relative to the frame window + ::tools::Rectangle maClientArea; + + // This is set to true when PrepareClose() is called. + bool mbIsClosing; + + /** The view window is the parent of all UI elements that belong to the + view or ViewShell. This comprises the rulers, the scroll bars, and + the content window. + It does not include the ViewTabBar. + */ + VclPtr<vcl::Window> mpViewWindow; + std::shared_ptr<ToolBarManager> mpToolBarManager; + std::shared_ptr<ViewShellManager> mpViewShellManager; + std::shared_ptr<tools::EventMultiplexer> mpEventMultiplexer; + std::shared_ptr<FormShellManager> mpFormShellManager; + + explicit Implementation (ViewShellBase& rBase); + ~Implementation(); + + void LateInit(); + + /** Show or hide the ViewTabBar. + @param bShow + When <TRUE/> then the ViewTabBar is shown, otherwise it is hidden. + */ + void ShowViewTabBar (bool bShow); + + void SetUserWantsTabBar(bool inValue); + bool GetUserWantsTabBar() const { return mbUserWantsTabBar; } + + /** Common code of ViewShellBase::OuterResizePixel() and + ViewShellBase::InnerResizePixel(). + */ + void ResizePixel ( + const Point& rOrigin, + const Size& rSize, + bool bOuterResize); + + /** Show or hide the specified pane. The visibility state is taken + from the given request. + @param rRequest + The request determines the new visibility state. The state can + either be toggled or be set to a given value. + @param rsPaneURL + This URL specifies the pane whose visibility state to set. + @param rsViewURL + When the pane becomes visible then this view URL specifies which + type of view to show in it. + */ + void SetPaneVisibility ( + const SfxRequest& rRequest, + const OUString& rsPaneURL, + const OUString& rsViewURL); + + void GetSlotState (SfxItemSet& rSet); + + void ProcessRestoreEditingViewSlot(); + +private: + ViewShellBase& mrBase; + bool mbUserWantsTabBar; + bool mbTabBarShouldBeVisible; + /** Hold a reference to the page cache manager of the slide sorter in + order to ensure that it stays alive while the ViewShellBase is + alive. + */ + std::shared_ptr<slidesorter::cache::PageCacheManager> mpPageCacheManager; +}; + +namespace { +/** The only task of this window is to forward key presses to the content + window of the main view shell. With the key press it forwards the focus + so that it is not called very often. +*/ +class FocusForwardingWindow : public vcl::Window +{ +public: + FocusForwardingWindow (vcl::Window& rParentWindow, ViewShellBase& rBase); + virtual ~FocusForwardingWindow() override; + virtual void dispose() override; + virtual void KeyInput (const KeyEvent& rEvent) override; + virtual void Command (const CommandEvent& rEvent) override; + +private: + ViewShellBase& mrBase; +}; +} // end of anonymous namespace + +//===== ViewShellBase ========================================================= + + +// We have to expand the SFX_IMPL_VIEWFACTORY macro to call LateInit() after a +// new ViewShellBase object has been constructed. + +SFX_IMPL_SUPERCLASS_INTERFACE(ViewShellBase, SfxViewShell) + +void ViewShellBase::InitInterface_Impl() +{ +} + +ViewShellBase::ViewShellBase ( + SfxViewFrame* _pFrame, + SfxViewShell*) + : SfxViewShell (_pFrame, SfxViewShellFlags::HAS_PRINTOPTIONS), + mpDocShell (nullptr), + mpDocument (nullptr) +{ + mpImpl.reset(new Implementation(*this)); + mpImpl->mpViewWindow = VclPtr<FocusForwardingWindow>::Create(_pFrame->GetWindow(),*this); + mpImpl->mpViewWindow->SetBackground(Wallpaper()); + + _pFrame->GetWindow().SetBackground(Application::GetSettings().GetStyleSettings().GetLightColor()); + + // Set up the members in the correct order. + if (auto pDrawDocShell = dynamic_cast< DrawDocShell *>( GetViewFrame()->GetObjectShell() )) + mpDocShell = pDrawDocShell; + if (mpDocShell != nullptr) + mpDocument = mpDocShell->GetDoc(); + mpImpl->mpViewShellManager = std::make_shared<ViewShellManager>(*this); + + SetWindow(mpImpl->mpViewWindow.get()); + + // Hide the window to avoid complaints from Sfx...SwitchViewShell... + _pFrame->GetWindow().Hide(); +} + +/** In this destructor the order in which some of the members are destroyed + (and/or being prepared to being destroyed) is important. Change it only + when you know what you are doing. +*/ +ViewShellBase::~ViewShellBase() +{ + // Notify other LOK views that we are going away. + SfxLokHelper::notifyOtherViews(this, LOK_CALLBACK_VIEW_CURSOR_VISIBLE, "visible", "false"); + SfxLokHelper::notifyOtherViews(this, LOK_CALLBACK_TEXT_VIEW_SELECTION, "selection", ""); + SfxLokHelper::notifyOtherViews(this, LOK_CALLBACK_GRAPHIC_VIEW_SELECTION, "selection", "EMPTY"); + + sfx2::SfxNotebookBar::CloseMethod(GetFrame()->GetBindings()); + + rtl::Reference<SlideShow> xSlideShow(SlideShow::GetSlideShow(*this)); + if (xSlideShow.is() && xSlideShow->dependsOn(this)) + SlideShow::Stop(*this); + xSlideShow.clear(); + + // Tell the controller that the ViewShellBase is not available anymore. + if (mpImpl->mpController) + mpImpl->mpController->ReleaseViewShellBase(); + + // We have to hide the main window to prevent SFX complaining after a + // reload about it being already visible. + ViewShell* pShell = GetMainViewShell().get(); + if (pShell!=nullptr + && pShell->GetActiveWindow()!=nullptr + && pShell->GetActiveWindow()->GetParent()!=nullptr) + { + pShell->GetActiveWindow()->GetParent()->Hide(); + } + + mpImpl->mpToolBarManager->Shutdown(); + mpImpl->mpViewShellManager->Shutdown(); + + EndListening(*GetViewFrame()); + EndListening(*GetDocShell()); + + SetWindow(nullptr); + + mpImpl->mpFormShellManager.reset(); +} + +void ViewShellBase::LateInit (const OUString& rsDefaultView) +{ + StartListening(*GetViewFrame(), DuplicateHandling::Prevent); + StartListening(*GetDocShell(), DuplicateHandling::Prevent); + mpImpl->LateInit(); + InitializeFramework(); + + mpImpl->mpEventMultiplexer = std::make_shared<tools::EventMultiplexer>(*this); + + mpImpl->mpFormShellManager = std::make_shared<FormShellManager>(*this); + + mpImpl->mpToolBarManager = ToolBarManager::Create( + *this, + mpImpl->mpEventMultiplexer, + mpImpl->mpViewShellManager); + + try + { + Reference<XControllerManager> xControllerManager (GetDrawController(), UNO_QUERY_THROW); + Reference<XConfigurationController> xConfigurationController ( + xControllerManager->getConfigurationController()); + if (xConfigurationController.is()) + { + OUString sView (rsDefaultView); + if (sView.isEmpty()) + sView = GetInitialViewShellType(); + + FrameworkHelper::Instance(*this); + + // Create the resource ids for the center pane and view. + const Reference<drawing::framework::XResourceId> xCenterPaneId ( + FrameworkHelper::CreateResourceId(FrameworkHelper::msCenterPaneURL)); + const Reference<drawing::framework::XResourceId> xCenterViewId ( + FrameworkHelper::CreateResourceId(sView, xCenterPaneId)); + + // Request center pane and view. + xConfigurationController->requestResourceActivation(xCenterPaneId, ResourceActivationMode_ADD); + xConfigurationController->requestResourceActivation(xCenterViewId, ResourceActivationMode_REPLACE); + + // Process configuration events synchronously until the center view + // has been created. + sd::framework::ConfigurationController* pConfigurationController + = dynamic_cast<sd::framework::ConfigurationController*>(xConfigurationController.get()); + if (pConfigurationController != nullptr) + { + while ( + ! pConfigurationController->getResource(xCenterViewId).is() + && pConfigurationController->hasPendingRequests()) + { + pConfigurationController->ProcessEvent(); + } + } + } + } + catch (const RuntimeException&) + { + } + + // AutoLayouts have to be ready. + GetDocument()->StopWorkStartupDelay(); + + UpdateBorder(); + + // Remember the type of the current main view shell in the frame view. + ViewShell* pViewShell = GetMainViewShell().get(); + if (pViewShell != nullptr) + { + FrameView* pFrameView = pViewShell->GetFrameView(); + if (pFrameView != nullptr) + pFrameView->SetViewShellTypeOnLoad(pViewShell->GetShellType()); + } + // Show/Hide the TabBar + SdOptions* pOptions = SD_MOD()->GetSdOptions(GetDocument()->GetDocumentType()); + bool bIsTabBarVisible = pOptions->IsTabBarVisible(); + mpImpl->SetUserWantsTabBar( bIsTabBarVisible ); +} + +std::shared_ptr<ViewShellManager> const & ViewShellBase::GetViewShellManager() const +{ + return mpImpl->mpViewShellManager; +} + +std::shared_ptr<ViewShell> ViewShellBase::GetMainViewShell() const +{ + std::shared_ptr<ViewShell> pMainViewShell ( + framework::FrameworkHelper::Instance(*const_cast<ViewShellBase*>(this)) + ->GetViewShell(framework::FrameworkHelper::msCenterPaneURL)); + if (pMainViewShell == nullptr) + pMainViewShell = framework::FrameworkHelper::Instance(*const_cast<ViewShellBase*>(this)) + ->GetViewShell(framework::FrameworkHelper::msFullScreenPaneURL); + return pMainViewShell; +} + +ViewShellBase* ViewShellBase::GetViewShellBase (SfxViewFrame const * pViewFrame) +{ + ViewShellBase* pBase = nullptr; + + if (pViewFrame != nullptr) + { + // Get the view shell for the frame and cast it to + // sd::ViewShellBase. + SfxViewShell* pSfxViewShell = pViewFrame->GetViewShell(); + pBase = dynamic_cast< ::sd::ViewShellBase *>( pSfxViewShell ); + } + + return pBase; +} + +void ViewShellBase::Notify(SfxBroadcaster& rBC, const SfxHint& rHint) +{ + SfxViewShell::Notify(rBC, rHint); + + const SfxEventHint* pEventHint = dynamic_cast<const SfxEventHint*>(&rHint); + if (pEventHint) + { + switch (pEventHint->GetEventId()) + { + case SfxEventHintId::OpenDoc: + if( GetDocument() && GetDocument()->IsStartWithPresentation() ) + { + if( GetViewFrame() ) + { + GetViewFrame()->GetDispatcher()->Execute( + SID_PRESENTATION, SfxCallMode::ASYNCHRON ); + } + } + break; + + default: + break; + } + } + else + { + const SfxHintId nSlot = rHint.GetId(); + switch ( nSlot ) + { + case SfxHintId::LanguageChanged: + { + GetViewFrame()->GetBindings().Invalidate(SID_LANGUAGE_STATUS); + } + break; + + default: + break; + } + } +} + +void ViewShellBase::InitializeFramework() +{ +} + +OUString ViewShellBase::GetSelectionText(bool bCompleteWords, bool /*bOnlyASample*/) +{ + std::shared_ptr<ViewShell> const pMainShell(GetMainViewShell()); + DrawViewShell *const pDrawViewShell( + dynamic_cast<DrawViewShell*>(pMainShell.get())); + return pDrawViewShell + ? pDrawViewShell->GetSelectionText(bCompleteWords) + : SfxViewShell::GetSelectionText(bCompleteWords); +} + +bool ViewShellBase::HasSelection(bool bText) const +{ + std::shared_ptr<ViewShell> const pMainShell(GetMainViewShell()); + DrawViewShell *const pDrawViewShell( + dynamic_cast<DrawViewShell*>(pMainShell.get())); + return pDrawViewShell + ? pDrawViewShell->HasSelection(bText) + : SfxViewShell::HasSelection(bText); +} + +void ViewShellBase::InnerResizePixel (const Point& rOrigin, const Size &rSize, bool) +{ + Size aObjSize = GetObjectShell()->GetVisArea().GetSize(); + if ( !aObjSize.IsEmpty() ) + { + SvBorder aBorder( GetBorderPixel() ); + Size aSize( rSize ); + aSize.AdjustWidth( -(aBorder.Left() + aBorder.Right()) ); + aSize.AdjustHeight( -(aBorder.Top() + aBorder.Bottom()) ); + Size aObjSizePixel = mpImpl->mpViewWindow->LogicToPixel(aObjSize, MapMode(MapUnit::Map100thMM)); + SfxViewShell::SetZoomFactor( + Fraction( aSize.Width(), std::max( aObjSizePixel.Width(), static_cast<::tools::Long>(1) ) ), + Fraction( aSize.Height(), std::max( aObjSizePixel.Height(), static_cast<::tools::Long>(1)) ) ); + } + + mpImpl->ResizePixel(rOrigin, rSize, false); +} + +void ViewShellBase::OuterResizePixel (const Point& rOrigin, const Size &rSize) +{ + mpImpl->ResizePixel (rOrigin, rSize, true); +} + +void ViewShellBase::Rearrange() +{ + OSL_ASSERT(GetViewFrame()!=nullptr); + + // There is a bug in the communication between embedded objects and the + // framework::LayoutManager that leads to missing resize updates. The + // following workaround enforces such an update by cycling the border to + // zero and back to the current value. + if (GetWindow() != nullptr) + { + SetBorderPixel(SvBorder()); + UpdateBorder(true); + } + else + { + SAL_WARN("sd.view", "Rearrange: window missing"); + } + + GetViewFrame()->Resize(true); +} + +ErrCode ViewShellBase::DoVerb(sal_Int32 nVerb) +{ + ErrCode aResult = ERRCODE_NONE; + + ::sd::ViewShell* pShell = GetMainViewShell().get(); + if (pShell != nullptr) + aResult = pShell->DoVerb(nVerb); + + return aResult; +} + +Reference<view::XRenderable> ViewShellBase::GetRenderable() +{ + // Create a new DocumentRenderer on every call. It observes the life + // time of this ViewShellBase object. + return Reference<view::XRenderable>(new DocumentRenderer(*this)); +} + +SfxPrinter* ViewShellBase::GetPrinter (bool bCreate) +{ + OSL_ASSERT(mpImpl != nullptr); + + return GetDocShell()->GetPrinter (bCreate); +} + +sal_uInt16 ViewShellBase::SetPrinter ( + SfxPrinter* pNewPrinter, + SfxPrinterChangeFlags nDiffFlags) +{ + OSL_ASSERT(mpImpl != nullptr); + + GetDocShell()->SetPrinter(pNewPrinter); + + if ( (nDiffFlags & SfxPrinterChangeFlags::CHG_ORIENTATION || + nDiffFlags & SfxPrinterChangeFlags::CHG_SIZE) && pNewPrinter ) + { + MapMode aMap = pNewPrinter->GetMapMode(); + aMap.SetMapUnit(MapUnit::Map100thMM); + MapMode aOldMap = pNewPrinter->GetMapMode(); + pNewPrinter->SetMapMode(aMap); + Size aNewSize = pNewPrinter->GetOutputSize(); + + std::shared_ptr<DrawViewShell> pDrawViewShell ( + std::dynamic_pointer_cast<DrawViewShell>(GetMainViewShell())); + if (pDrawViewShell) + { + SdPage* pPage = GetDocument()->GetSdPage( + 0, PageKind::Standard ); + pDrawViewShell->SetPageSizeAndBorder ( + pDrawViewShell->GetPageKind(), + aNewSize, + -1,-1,-1,-1, + false/*bScaleAll*/, + pNewPrinter->GetOrientation(), + pPage->GetPaperBin(), + pPage->IsBackgroundFullSize()); + } + + pNewPrinter->SetMapMode(aOldMap); + } + + return 0; +} + +void ViewShellBase::UIActivating( SfxInPlaceClient* pClient ) +{ + mpImpl->ShowViewTabBar(false); + + ViewShell* pViewShell = GetMainViewShell().get(); + if ( pViewShell ) + pViewShell->UIActivating( pClient ); + + SfxViewShell::UIActivating( pClient ); +} + +void ViewShellBase::UIDeactivated( SfxInPlaceClient* pClient ) +{ + SfxViewShell::UIDeactivated( pClient ); + + mpImpl->ShowViewTabBar(true); + + ViewShell* pViewShell = GetMainViewShell().get(); + if ( pViewShell ) + pViewShell->UIDeactivated( pClient ); +} + +SvBorder ViewShellBase::GetBorder (bool ) +{ + int nTop = 0; + if (mpImpl->mpViewTabBar.is() && mpImpl->mpViewTabBar->GetTabControl()->IsVisible()) + nTop = mpImpl->mpViewTabBar->GetHeight(); + return SvBorder(0,nTop,0,0); +} + +void ViewShellBase::Execute (SfxRequest& rRequest) +{ + sal_uInt16 nSlotId = rRequest.GetSlot(); + + switch (nSlotId) + { + case SID_SWITCH_SHELL: + { + Reference<XControllerManager> xControllerManager (GetController(), UNO_QUERY); + if (xControllerManager.is()) + { + Reference<XConfigurationController> xConfigurationController ( + xControllerManager->getConfigurationController()); + if (xConfigurationController.is()) + xConfigurationController->update(); + } + } + break; + + case SID_LEFT_PANE_DRAW: + mpImpl->SetPaneVisibility( + rRequest, + framework::FrameworkHelper::msLeftDrawPaneURL, + framework::FrameworkHelper::msSlideSorterURL); + break; + + case SID_LEFT_PANE_IMPRESS: + mpImpl->SetPaneVisibility( + rRequest, + framework::FrameworkHelper::msLeftImpressPaneURL, + framework::FrameworkHelper::msSlideSorterURL); + break; + + case SID_TOGGLE_TABBAR_VISIBILITY: + { + SdOptions* pOptions = SD_MOD()->GetSdOptions(GetDocument()->GetDocumentType()); + bool bIsTabBarVisible = pOptions->IsTabBarVisible(); + pOptions->SetTabBarVisible( !bIsTabBarVisible ); + mpImpl->SetUserWantsTabBar( !bIsTabBarVisible ); + rRequest.Done(); + } + break; + + // draw + case SID_DRAWINGMODE: + // impress normal + case SID_NORMAL_MULTI_PANE_GUI: + case SID_NOTES_MODE: + case SID_OUTLINE_MODE: + case SID_SLIDE_SORTER_MULTI_PANE_GUI: + case SID_SLIDE_SORTER_MODE: + // impress master + case SID_SLIDE_MASTER_MODE: + case SID_NOTES_MASTER_MODE: + case SID_HANDOUT_MASTER_MODE: + framework::FrameworkHelper::Instance(*this)->HandleModeChangeSlot(nSlotId, rRequest); + break; + + case SID_WIN_FULLSCREEN: + // The full screen mode is not supported. Ignore the request. + break; + + case SID_RESTORE_EDITING_VIEW: + mpImpl->ProcessRestoreEditingViewSlot(); + break; + + default: + // Ignore any other slot. + rRequest.Ignore (); + break; + } +} + +void ViewShellBase::GetState (SfxItemSet& rSet) +{ + mpImpl->GetSlotState(rSet); + + FuBullet::GetSlotState( rSet, nullptr, GetViewFrame() ); +} + +void ViewShellBase::WriteUserDataSequence ( + css::uno::Sequence< css::beans::PropertyValue >& rSequence) +{ + // Forward call to main sub shell. + ViewShell* pShell = GetMainViewShell().get(); + if (pShell != nullptr) + pShell->WriteUserDataSequence (rSequence); +} + +void ViewShellBase::ReadUserDataSequence ( + const css::uno::Sequence< css::beans::PropertyValue >& rSequence) +{ + // Forward call to main sub shell. + ViewShell* pShell = GetMainViewShell().get(); + if (pShell == nullptr) + return; + + pShell->ReadUserDataSequence (rSequence); + + // For certain shell types ReadUserDataSequence may have changed the + // type to another one. Make sure that the center pane shows the + // right view shell. + switch (pShell->GetShellType()) + { + case ViewShell::ST_IMPRESS: + case ViewShell::ST_NOTES: + case ViewShell::ST_HANDOUT: + { + OUString sViewURL; + switch (dynamic_cast<DrawViewShell&>(*pShell).GetPageKind()) + { + default: + case PageKind::Standard: + sViewURL = framework::FrameworkHelper::msImpressViewURL; + break; + case PageKind::Notes: + sViewURL = framework::FrameworkHelper::msNotesViewURL; + break; + case PageKind::Handout: + sViewURL = framework::FrameworkHelper::msHandoutViewURL; + break; + } + if (!sViewURL.isEmpty()) + framework::FrameworkHelper::Instance(*this)->RequestView( + sViewURL, + framework::FrameworkHelper::msCenterPaneURL); + } + break; + + default: + break; + } +} + +void ViewShellBase::Activate (bool bIsMDIActivate) +{ + SfxViewShell::Activate(bIsMDIActivate); + + Reference<XControllerManager> xControllerManager (GetController(), UNO_QUERY); + if (xControllerManager.is()) + { + Reference<XConfigurationController> xConfigurationController ( + xControllerManager->getConfigurationController()); + if (xConfigurationController.is()) + xConfigurationController->update(); + } + GetToolBarManager()->RequestUpdate(); +} + +void ViewShellBase::SetZoomFactor ( + const Fraction &rZoomX, + const Fraction &rZoomY) +{ + SfxViewShell::SetZoomFactor (rZoomX, rZoomY); + // Forward call to main sub shell. + ViewShell* pShell = GetMainViewShell().get(); + if (pShell != nullptr) + pShell->SetZoomFactor (rZoomX, rZoomY); +} + +bool ViewShellBase::PrepareClose (bool bUI) +{ + bool bResult = SfxViewShell::PrepareClose (bUI); + + if (bResult) + { + mpImpl->mbIsClosing = true; + + // Forward call to main sub shell. + ViewShell* pShell = GetMainViewShell().get(); + if (pShell != nullptr) + bResult = pShell->PrepareClose (bUI); + } + + return bResult; +} + +void ViewShellBase::WriteUserData (OUString& rString, bool bBrowse) +{ + SfxViewShell::WriteUserData (rString, bBrowse); + + // Forward call to main sub shell. + ViewShell* pShell = GetMainViewShell().get(); + if (pShell != nullptr) + pShell->WriteUserData(); +} + +void ViewShellBase::ReadUserData (const OUString& rString, bool bBrowse) +{ + SfxViewShell::ReadUserData (rString, bBrowse); + + // Forward call to main sub shell. + ViewShell* pShell = GetMainViewShell().get(); + if (pShell != nullptr) + pShell->ReadUserData(); +} + +SdrView* ViewShellBase::GetDrawView() const +{ + // Forward call to main sub shell. + ViewShell* pShell = GetMainViewShell().get(); + if (pShell != nullptr) + return pShell->GetDrawView (); + else + return SfxViewShell::GetDrawView(); +} + +void ViewShellBase::SetBusyState (bool bBusy) +{ + if (GetDocShell() != nullptr) + GetDocShell()->SetWaitCursor (bBusy); +} + +void ViewShellBase::UpdateBorder ( bool bForce /* = false */ ) +{ + // The following calls to SetBorderPixel() and InvalidateBorder() are + // made only for the main view shell. This not only avoids unnecessary + // calls for the views in side panes but prevents calling an already + // dying SfxViewShell base class. + // We have to check the existence of the window, too. + // The SfxViewFrame accesses the window without checking it. + ViewShell* pMainViewShell = GetMainViewShell().get(); + if (pMainViewShell == nullptr || GetWindow()==nullptr) + return; + + SvBorder aCurrentBorder (GetBorderPixel()); + bool bOuterResize ( ! GetDocShell()->IsInPlaceActive()); + SvBorder aBorder (GetBorder(bOuterResize)); + aBorder += pMainViewShell->GetBorder(); + + if (bForce || (aBorder != aCurrentBorder)) + { + SetBorderPixel (aBorder); + InvalidateBorder(); + } +} + +void ViewShellBase::ShowUIControls (bool bVisible) +{ + mpImpl->ShowViewTabBar(bVisible); + + ViewShell* pMainViewShell = GetMainViewShell().get(); + if (pMainViewShell != nullptr) + pMainViewShell->ShowUIControls (bVisible); + + UpdateBorder(); + if (bVisible) + Rearrange(); +} + +OUString ViewShellBase::GetInitialViewShellType() const +{ + OUString sRequestedView (FrameworkHelper::msImpressViewURL); + + do + { + Reference<document::XViewDataSupplier> xViewDataSupplier ( + GetDocShell()->GetModel(), UNO_QUERY); + if ( ! xViewDataSupplier.is()) + break; + + Reference<container::XIndexAccess> xViewData (xViewDataSupplier->getViewData()); + if ( ! xViewData.is()) + break; + if (xViewData->getCount() == 0) + break; + + css::uno::Any aAny = xViewData->getByIndex(0); + Sequence<beans::PropertyValue> aProperties; + if ( ! (aAny >>= aProperties)) + break; + + // Search the properties for the one that tells us what page kind to + // use. + auto pProperty = std::find_if(std::cbegin(aProperties), std::cend(aProperties), + [](const beans::PropertyValue& rProperty) { return rProperty.Name == sUNO_View_PageKind; }); + if (pProperty != std::cend(aProperties)) + { + sal_Int16 nPageKind = 0; + pProperty->Value >>= nPageKind; + switch (static_cast<PageKind>(nPageKind)) + { + case PageKind::Standard: + sRequestedView = FrameworkHelper::msImpressViewURL; + break; + + case PageKind::Handout: + sRequestedView = FrameworkHelper::msHandoutViewURL; + break; + + case PageKind::Notes: + sRequestedView = FrameworkHelper::msNotesViewURL; + break; + + default: + // The page kind is invalid. This is probably an + // error by the caller. We use the standard type to + // keep things going. + SAL_WARN( "sd.view", "ViewShellBase::GetInitialViewShellType: invalid page kind"); + sRequestedView = FrameworkHelper::msImpressViewURL; + break; + } + } + } + while (false); + + return sRequestedView; +} + +std::shared_ptr<tools::EventMultiplexer> const & ViewShellBase::GetEventMultiplexer() const +{ + OSL_ASSERT(mpImpl != nullptr); + OSL_ASSERT(mpImpl->mpEventMultiplexer != nullptr); + + return mpImpl->mpEventMultiplexer; +} + +const ::tools::Rectangle& ViewShellBase::getClientRectangle() const +{ + return mpImpl->maClientArea; +} + +std::shared_ptr<ToolBarManager> const & ViewShellBase::GetToolBarManager() const +{ + OSL_ASSERT(mpImpl != nullptr); + OSL_ASSERT(mpImpl->mpToolBarManager != nullptr); + + return mpImpl->mpToolBarManager; +} + +std::shared_ptr<FormShellManager> const & ViewShellBase::GetFormShellManager() const +{ + OSL_ASSERT(mpImpl != nullptr); + OSL_ASSERT(mpImpl->mpFormShellManager != nullptr); + + return mpImpl->mpFormShellManager; +} + +DrawController& ViewShellBase::GetDrawController() const +{ + OSL_ASSERT(mpImpl != nullptr); + + return *mpImpl->mpController; +} + +void ViewShellBase::SetViewTabBar (const ::rtl::Reference<ViewTabBar>& rViewTabBar) +{ + OSL_ASSERT(mpImpl != nullptr); + + mpImpl->mpViewTabBar = rViewTabBar; +} + +vcl::Window* ViewShellBase::GetViewWindow() +{ + OSL_ASSERT(mpImpl != nullptr); + + return mpImpl->mpViewWindow.get(); +} + +OUString ViewShellBase::RetrieveLabelFromCommand( const OUString& aCmdURL ) const +{ + OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(GetMainViewShell()->GetViewFrame()->GetFrame().GetFrameInterface())); + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCmdURL, aModuleName); + return vcl::CommandInfoProvider::GetLabelForCommand(aProperties); +} + +int ViewShellBase::getPart() const +{ + ViewShell* pViewShell = framework::FrameworkHelper::Instance(*const_cast<ViewShellBase*>(this))->GetViewShell(FrameworkHelper::msCenterPaneURL).get(); + + if (DrawViewShell* pDrawViewShell = dynamic_cast<DrawViewShell*>(pViewShell)) + { + return pDrawViewShell->GetCurPagePos(); + } + + return 0; +} + +void ViewShellBase::NotifyCursor(SfxViewShell* pOtherShell) const +{ + ViewShell* pThisShell = framework::FrameworkHelper::Instance(*const_cast<ViewShellBase*>(this))->GetViewShell(FrameworkHelper::msCenterPaneURL).get(); + + DrawViewShell* pDrawViewShell = dynamic_cast<DrawViewShell*>(pThisShell); + if (!pDrawViewShell) + return; + + if (this == pOtherShell) + return; + + DrawView* pDrawView = pDrawViewShell->GetDrawView(); + if (!pDrawView) + return; + + if (pDrawView->GetTextEditObject()) + { + // Blinking cursor. + EditView& rEditView = pDrawView->GetTextEditOutlinerView()->GetEditView(); + rEditView.RegisterOtherShell(pOtherShell); + rEditView.ShowCursor(); + rEditView.RegisterOtherShell(nullptr); + // Text selection, if any. + rEditView.DrawSelectionXOR(pOtherShell); + + // Shape text lock. + if (OutlinerView* pOutlinerView = pDrawView->GetTextEditOutlinerView()) + { + ::tools::Rectangle aRectangle = pOutlinerView->GetOutputArea(); + vcl::Window* pWin = pThisShell->GetActiveWindow(); + if (pWin && pWin->GetMapMode().GetMapUnit() == MapUnit::Map100thMM) + aRectangle = o3tl::toTwips(aRectangle, o3tl::Length::mm100); + OString sRectangle = aRectangle.toString(); + SfxLokHelper::notifyOtherView(&pDrawViewShell->GetViewShellBase(), pOtherShell, LOK_CALLBACK_VIEW_LOCK, "rectangle", sRectangle); + } + } + else + { + // Graphic selection. + pDrawView->AdjustMarkHdl(pOtherShell); + } +} + +//===== ViewShellBase::Implementation ========================================= + +ViewShellBase::Implementation::Implementation (ViewShellBase& rBase) + : mbIsClosing(false), + mrBase(rBase), + mbUserWantsTabBar(false), + mbTabBarShouldBeVisible(true), + mpPageCacheManager(slidesorter::cache::PageCacheManager::Instance()) +{ +} + +ViewShellBase::Implementation::~Implementation() +{ + mpController = nullptr; + mpViewTabBar = nullptr; + mpViewWindow.disposeAndClear(); + mpToolBarManager.reset(); +} + +void ViewShellBase::Implementation::LateInit() +{ + mpController = new DrawController(mrBase); +} + +void ViewShellBase::Implementation::ProcessRestoreEditingViewSlot() +{ + ViewShell* pViewShell = mrBase.GetMainViewShell().get(); + if (pViewShell == nullptr) + return; + + FrameView* pFrameView = pViewShell->GetFrameView(); + if (pFrameView == nullptr) + return; + + // Set view shell, edit mode, and page kind. + // pFrameView->SetViewShEditMode( + // pFrameView->GetViewShEditModeOnLoad(), + // pFrameView->GetPageKindOnLoad()); + pFrameView->SetViewShEditMode( + pFrameView->GetViewShEditModeOnLoad() ); + pFrameView->SetPageKind( + pFrameView->GetPageKindOnLoad()); + std::shared_ptr<FrameworkHelper> pHelper (FrameworkHelper::Instance(mrBase)); + pHelper->RequestView( + FrameworkHelper::GetViewURL(pFrameView->GetViewShellTypeOnLoad()), + FrameworkHelper::msCenterPaneURL); + pHelper->RunOnConfigurationEvent("ConfigurationUpdateEnd", CurrentPageSetter(mrBase)); +} + +void ViewShellBase::Implementation::SetUserWantsTabBar(bool inValue) +{ + mbUserWantsTabBar = inValue; + // Call ShowViewTabBar to refresh the TabBar visibility + ShowViewTabBar(mbTabBarShouldBeVisible); +} + +void ViewShellBase::Implementation::ShowViewTabBar (bool bShow) +{ + mbTabBarShouldBeVisible = bShow; + bShow = bShow && mbUserWantsTabBar; + + if (mpViewTabBar.is() + && mpViewTabBar->GetTabControl()->IsVisible() != bShow) + { + mpViewTabBar->GetTabControl()->Show(bShow); + mrBase.Rearrange(); + } +} + +void ViewShellBase::Implementation::ResizePixel ( + const Point& rOrigin, + const Size &rSize, + bool bOuterResize) +{ + if (mbIsClosing) + return; + + // Forward the call to both the base class and the main stacked sub + // shell only when main sub shell exists. + ViewShell* pMainViewShell = mrBase.GetMainViewShell().get(); + + // Set the ViewTabBar temporarily to full size so that, when asked + // later, it can return its true height. + mrBase.SetWindow (mpViewWindow.get()); + if (mpViewTabBar.is() && mpViewTabBar->GetTabControl()->IsVisible()) + mpViewTabBar->GetTabControl()->SetPosSizePixel (rOrigin, rSize); + + // Calculate and set the border before the controls are placed. + SvBorder aBorder; + if (pMainViewShell != nullptr) + aBorder = pMainViewShell->GetBorder(); + aBorder += mrBase.GetBorder(bOuterResize); + if (mrBase.GetBorderPixel() != aBorder) + mrBase.SetBorderPixel(aBorder); + + // Place the ViewTabBar at the top. It is part of the border. + SvBorder aBaseBorder; + if (mpViewTabBar.is() && mpViewTabBar->GetTabControl()->IsVisible()) + { + aBaseBorder.Top() = mpViewTabBar->GetHeight(); + mpViewTabBar->GetTabControl()->SetPosSizePixel( + rOrigin, Size(rSize.Width(),aBaseBorder.Top())); + } + + // The view window gets the remaining space. + Point aViewWindowPosition ( + rOrigin.X()+aBaseBorder.Left(), + rOrigin.Y()+aBaseBorder.Top()); + + Size aViewWindowSize ( + rSize.Width() - aBaseBorder.Left() - aBaseBorder.Right(), + rSize.Height() - aBaseBorder.Top() - aBaseBorder.Bottom()); + mpViewWindow->SetPosSizePixel(aViewWindowPosition, aViewWindowSize); + + maClientArea = ::tools::Rectangle(Point(0,0), aViewWindowSize); +} + +void ViewShellBase::Implementation::SetPaneVisibility ( + const SfxRequest& rRequest, + const OUString& rsPaneURL, + const OUString& rsViewURL) +{ + try + { + Reference<XControllerManager> xControllerManager (mrBase.GetController(), UNO_QUERY_THROW); + + const Reference< XComponentContext > xContext( + ::comphelper::getProcessComponentContext() ); + Reference<XResourceId> xPaneId (ResourceId::create( + xContext, rsPaneURL)); + Reference<XResourceId> xViewId (ResourceId::createWithAnchorURL( + xContext, rsViewURL, rsPaneURL)); + + // Determine the new visibility state. + const SfxItemSet* pArguments = rRequest.GetArgs(); + bool bShowChildWindow; + sal_uInt16 nSlotId = rRequest.GetSlot(); + if (pArguments != nullptr) + bShowChildWindow = static_cast<const SfxBoolItem&>( + pArguments->Get(nSlotId)).GetValue(); + else + { + Reference<XConfigurationController> xConfigurationController ( + xControllerManager->getConfigurationController()); + if ( ! xConfigurationController.is()) + throw RuntimeException(); + Reference<XConfiguration> xConfiguration ( + xConfigurationController->getRequestedConfiguration()); + if ( ! xConfiguration.is()) + throw RuntimeException(); + + bShowChildWindow = ! xConfiguration->hasResource(xPaneId); + } + + // Set the desired visibility state at the current configuration + // and update it accordingly. + Reference<XConfigurationController> xConfigurationController ( + xControllerManager->getConfigurationController()); + if ( ! xConfigurationController.is()) + throw RuntimeException(); + if (bShowChildWindow) + { + xConfigurationController->requestResourceActivation( + xPaneId, + ResourceActivationMode_ADD); + xConfigurationController->requestResourceActivation( + xViewId, + ResourceActivationMode_REPLACE); + } + else + xConfigurationController->requestResourceDeactivation( + xPaneId); + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("sd.view"); + } +} + +void ViewShellBase::Implementation::GetSlotState (SfxItemSet& rSet) +{ + try + { + // Get some frequently used values. + Reference<XControllerManager> xControllerManager (mrBase.GetController(), UNO_QUERY_THROW); + Reference<XConfigurationController> xConfigurationController ( + xControllerManager->getConfigurationController()); + if ( ! xConfigurationController.is()) + throw RuntimeException(); + Reference<XConfiguration> xConfiguration ( + xConfigurationController->getRequestedConfiguration()); + if ( ! xConfiguration.is()) + throw RuntimeException(); + + const Reference< XComponentContext > xContext( + ::comphelper::getProcessComponentContext() ); + SfxWhichIter aSetIterator (rSet); + sal_uInt16 nItemId (aSetIterator.FirstWhich()); + + while (nItemId > 0) + { + bool bState (false); + Reference<XResourceId> xResourceId; + try + { + // Check if the right view is active + switch (nItemId) + { + case SID_LEFT_PANE_IMPRESS: + xResourceId = ResourceId::create( + xContext, FrameworkHelper::msLeftImpressPaneURL); + bState = xConfiguration->hasResource(xResourceId); + break; + + case SID_LEFT_PANE_DRAW: + xResourceId = ResourceId::create( + xContext, FrameworkHelper::msLeftDrawPaneURL); + bState = xConfiguration->hasResource(xResourceId); + break; + + case SID_DRAWINGMODE: + case SID_NORMAL_MULTI_PANE_GUI: + case SID_SLIDE_MASTER_MODE: + xResourceId = ResourceId::createWithAnchorURL( + xContext, FrameworkHelper::msImpressViewURL, + FrameworkHelper::msCenterPaneURL); + bState = xConfiguration->hasResource(xResourceId); + break; + + case SID_SLIDE_SORTER_MULTI_PANE_GUI: + case SID_SLIDE_SORTER_MODE: + xResourceId = ResourceId::createWithAnchorURL( + xContext, + FrameworkHelper::msSlideSorterURL, + FrameworkHelper::msCenterPaneURL); + bState = xConfiguration->hasResource(xResourceId); + break; + + case SID_OUTLINE_MODE: + xResourceId = ResourceId::createWithAnchorURL( + xContext, + FrameworkHelper::msOutlineViewURL, + FrameworkHelper::msCenterPaneURL); + bState = xConfiguration->hasResource(xResourceId); + break; + + case SID_HANDOUT_MASTER_MODE: + xResourceId = ResourceId::createWithAnchorURL( + xContext, FrameworkHelper::msHandoutViewURL, + FrameworkHelper::msCenterPaneURL); + bState = xConfiguration->hasResource(xResourceId); + break; + + case SID_NOTES_MODE: + case SID_NOTES_MASTER_MODE: + xResourceId = ResourceId::createWithAnchorURL( + xContext, FrameworkHelper::msNotesViewURL, + FrameworkHelper::msCenterPaneURL); + bState = xConfiguration->hasResource(xResourceId); + break; + + case SID_TOGGLE_TABBAR_VISIBILITY: + bState = GetUserWantsTabBar(); + break; + + default: + // Ignore all other items. They are not meant to be + // handled by us. + break; + } + } + catch (const DeploymentException&) + { + } + + // Check if edit mode fits too + if (bState) + { + ViewShell* const pCenterViewShell = FrameworkHelper::Instance(mrBase)->GetViewShell( + FrameworkHelper::msCenterPaneURL).get(); + DrawViewShell* const pShell = dynamic_cast<DrawViewShell*>(pCenterViewShell); + if (pShell) + { + switch (nItemId) + { + case SID_DRAWINGMODE: + case SID_NORMAL_MULTI_PANE_GUI: + case SID_NOTES_MODE: + bState = pShell->GetEditMode() == EditMode::Page; + break; + case SID_SLIDE_MASTER_MODE: + case SID_NOTES_MASTER_MODE: + bState = pShell->GetEditMode() == EditMode::MasterPage; + break; + } + } + } + + // And finally set the state. + rSet.Put(SfxBoolItem(nItemId, bState)); + + nItemId = aSetIterator.NextWhich(); + } + } + catch (const RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd.view"); + } + +} + +} // end of namespace sd + +//===== CurrentPageSetter =========================================== + +namespace { + +CurrentPageSetter::CurrentPageSetter (ViewShellBase& rBase) + : mrBase(rBase) +{ +} + +void CurrentPageSetter::operator() (bool) +{ + FrameView* pFrameView = nullptr; + + if (mrBase.GetMainViewShell() != nullptr) + { + pFrameView = mrBase.GetMainViewShell()->GetFrameView(); + } + + if (pFrameView==nullptr) + return; + + try + { + // Get the current page either from the DrawPagesSupplier or the + // MasterPagesSupplier. + Any aPage; + if (pFrameView->GetViewShEditModeOnLoad() == EditMode::Page) + { + Reference<drawing::XDrawPagesSupplier> xPagesSupplier ( + mrBase.GetController()->getModel(), UNO_QUERY_THROW); + Reference<container::XIndexAccess> xPages ( + xPagesSupplier->getDrawPages(), UNO_QUERY_THROW); + aPage = xPages->getByIndex(pFrameView->GetSelectedPageOnLoad()); + } + else + { + Reference<drawing::XMasterPagesSupplier> xPagesSupplier ( + mrBase.GetController()->getModel(), UNO_QUERY_THROW); + Reference<container::XIndexAccess> xPages ( + xPagesSupplier->getMasterPages(), UNO_QUERY_THROW); + aPage = xPages->getByIndex(pFrameView->GetSelectedPageOnLoad()); + } + // Switch to the page last edited by setting the CurrentPage + // property. + Reference<beans::XPropertySet> xSet (mrBase.GetController(), UNO_QUERY_THROW); + xSet->setPropertyValue ("CurrentPage", aPage); + } + catch (const RuntimeException&) + { + // We have not been able to set the current page at the main view. + // This is sad but still leaves us in a valid state. Therefore, + // this exception is silently ignored. + } + catch (const beans::UnknownPropertyException&) + { + SAL_WARN("sd.view", "CurrentPage property unknown"); + } +} + +} // end of anonymous namespace + +//===== FocusForwardingWindow ================================================= + +namespace sd { +namespace { + +FocusForwardingWindow::FocusForwardingWindow ( + vcl::Window& rParentWindow, + ViewShellBase& rBase) + : vcl::Window(&rParentWindow, WinBits(WB_CLIPCHILDREN | WB_DIALOGCONTROL)), + mrBase(rBase) +{ + SAL_INFO("sd.view", "created FocusForwardingWindow at " << this); +} + +FocusForwardingWindow::~FocusForwardingWindow() +{ + disposeOnce(); +} + +void FocusForwardingWindow::dispose() +{ + SAL_INFO("sd.view", "destroyed FocusForwardingWindow at " << this); + vcl::Window::dispose(); +} + +void FocusForwardingWindow::KeyInput (const KeyEvent& rKEvt) +{ + std::shared_ptr<ViewShell> pViewShell = mrBase.GetMainViewShell(); + if (pViewShell != nullptr) + { + vcl::Window* pWindow = pViewShell->GetActiveWindow(); + if (pWindow != nullptr) + { + // Forward the focus so that the window is called directly the + // next time. + pWindow->GrabFocus(); + // Forward the key press as well. + pWindow->KeyInput(rKEvt); + } + } +} + +void FocusForwardingWindow::Command (const CommandEvent& rEvent) +{ + std::shared_ptr<ViewShell> pViewShell = mrBase.GetMainViewShell(); + if (pViewShell != nullptr) + { + vcl::Window* pWindow = pViewShell->GetActiveWindow(); + if (pWindow != nullptr) + { + pWindow->Command(rEvent); + } + } +} + +} // end of anonymous namespace + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/ViewShellHint.cxx b/sd/source/ui/view/ViewShellHint.cxx new file mode 100644 index 000000000..b86cbaa32 --- /dev/null +++ b/sd/source/ui/view/ViewShellHint.cxx @@ -0,0 +1,31 @@ +/* -*- 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 <ViewShellHint.hxx> + +namespace sd +{ +ViewShellHint::ViewShellHint(HintId eHintId) + : meHintId(eHintId) +{ +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/ViewShellImplementation.cxx b/sd/source/ui/view/ViewShellImplementation.cxx new file mode 100644 index 000000000..a0c025ce5 --- /dev/null +++ b/sd/source/ui/view/ViewShellImplementation.cxx @@ -0,0 +1,379 @@ +/* -*- 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 <config_features.h> + +#include <ViewShellImplementation.hxx> + +#include <sdpage.hxx> +#include <drawdoc.hxx> +#include <sdresid.hxx> +#include <unokywds.hxx> +#include <strings.hrc> +#include <app.hrc> +#include <unmodpg.hxx> +#include <DrawDocShell.hxx> +#include <FactoryIds.hxx> +#include <ViewShellBase.hxx> + +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/request.hxx> +#include <sfx2/sfxsids.hrc> +#include <sfx2/viewfrm.hxx> +#include <sfx2/sidebar/Sidebar.hxx> +#include <svl/intitem.hxx> +#include <svl/stritem.hxx> +#include <svx/imapdlg.hxx> +#include <basic/sbstar.hxx> +#include <basic/sberrors.hxx> +#include <xmloff/autolayout.hxx> +#include <vcl/svapp.hxx> + +#include <undo/undoobjects.hxx> + +#include <com/sun/star/drawing/framework/XControllerManager.hpp> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace sd { + +ViewShell::Implementation::Implementation (ViewShell& rViewShell) + : mbIsMainViewShell(false), + mbIsInitialized(false), + mbArrangeActive(false), + mrViewShell(rViewShell) +{ +} + +ViewShell::Implementation::~Implementation() COVERITY_NOEXCEPT_FALSE +{ + if ( ! mpUpdateLockForMouse.expired()) + { + std::shared_ptr<ToolBarManagerLock> pLock(mpUpdateLockForMouse); + if (pLock != nullptr) + { + // Force the ToolBarManagerLock to be released even when the + // IsUICaptured() returns <TRUE/>. + pLock->Release(true); + } + } +} + +void ViewShell::Implementation::ProcessModifyPageSlot ( + SfxRequest& rRequest, + SdPage* pCurrentPage, + PageKind ePageKind) +{ + SdDrawDocument* pDocument = mrViewShell.GetDoc(); + SdrLayerAdmin& rLayerAdmin = pDocument->GetLayerAdmin(); + SdrLayerIDSet aVisibleLayers; + bool bHandoutMode = false; + SdPage* pHandoutMPage = nullptr; + OUString aNewName; + + AutoLayout aNewAutoLayout; + + bool bBVisible; + bool bBObjsVisible; + const SfxItemSet* pArgs = rRequest.GetArgs(); + + if (pCurrentPage != nullptr && pCurrentPage->TRG_HasMasterPage()) + aVisibleLayers = pCurrentPage->TRG_GetMasterPageVisibleLayers(); + else + aVisibleLayers.SetAll(); + + do + { + if (pCurrentPage == nullptr) + break; + + if (!pArgs || pArgs->Count() == 1 || pArgs->Count() == 2 ) + { + // First make sure that the sidebar is visible + mrViewShell.GetDrawView()->SdrEndTextEdit(); + mrViewShell.GetDrawView()->UnmarkAll(); + mrViewShell.GetViewFrame()->ShowChildWindow(SID_SIDEBAR); + sfx2::sidebar::Sidebar::TogglePanel( + u"SdLayoutsPanel", + mrViewShell.GetViewFrame()->GetFrame().GetFrameInterface()); + break; + } + else if (pArgs->Count() == 4) + { + const SfxStringItem* pNewName = rRequest.GetArg<SfxStringItem>(ID_VAL_PAGENAME); + const SfxUInt32Item* pNewAutoLayout = rRequest.GetArg<SfxUInt32Item>(ID_VAL_WHATLAYOUT); + const SfxBoolItem* pBVisible = rRequest.GetArg<SfxBoolItem>(ID_VAL_ISPAGEBACK); + const SfxBoolItem* pBObjsVisible = rRequest.GetArg<SfxBoolItem>(ID_VAL_ISPAGEOBJ); + AutoLayout aLayout (static_cast<AutoLayout>(pNewAutoLayout->GetValue ())); + if (aLayout >= AUTOLAYOUT_START + && aLayout < AUTOLAYOUT_END) + { + aNewName = pNewName->GetValue (); + aNewAutoLayout = static_cast<AutoLayout>(pNewAutoLayout->GetValue ()); + bBVisible = pBVisible->GetValue (); + bBObjsVisible = pBObjsVisible->GetValue (); + } + else + { +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_BAD_PROP_VALUE); +#endif + rRequest.Ignore (); + break; + } + if (ePageKind == PageKind::Handout) + { + bHandoutMode = true; + pHandoutMPage = pDocument->GetMasterSdPage(0, PageKind::Handout); + } + } + else + { +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + rRequest.Ignore (); + break; + } + + SdPage* pUndoPage = + bHandoutMode ? pHandoutMPage : pCurrentPage; + + SfxUndoManager* pUndoManager = mrViewShell.GetDocSh()->GetUndoManager(); + DBG_ASSERT(pUndoManager, "No UNDO MANAGER ?!?"); + + if( pUndoManager ) + { + OUString aComment( SdResId(STR_UNDO_MODIFY_PAGE) ); + pUndoManager->EnterListAction(aComment, aComment, 0, mrViewShell.GetViewShellBase().GetViewShellId()); + pUndoManager->AddUndoAction( + std::make_unique<ModifyPageUndoAction>( + pDocument, pUndoPage, aNewName, aNewAutoLayout, bBVisible, bBObjsVisible)); + + // Clear the selection because the selected object may be removed as + // a result of the assignment of the layout. + mrViewShell.GetDrawView()->UnmarkAll(); + + if (!bHandoutMode) + { + if (pCurrentPage->GetName() != aNewName) + { + pCurrentPage->SetName(aNewName); + + if (ePageKind == PageKind::Standard) + { + sal_uInt16 nPage = (pCurrentPage->GetPageNum()-1) / 2; + SdPage* pNotesPage = pDocument->GetSdPage(nPage, PageKind::Notes); + if (pNotesPage != nullptr) + pNotesPage->SetName(aNewName); + } + } + + pCurrentPage->SetAutoLayout(aNewAutoLayout, true); + + SdrLayerID aBckgrnd = rLayerAdmin.GetLayerID(sUNO_LayerName_background); + SdrLayerID aBckgrndObj = rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects); + aVisibleLayers.Set(aBckgrnd, bBVisible); + aVisibleLayers.Set(aBckgrndObj, bBObjsVisible); + pCurrentPage->TRG_SetMasterPageVisibleLayers(aVisibleLayers); + } + else + { + pHandoutMPage->SetAutoLayout(aNewAutoLayout, true); + } + + mrViewShell.GetViewFrame()->GetDispatcher()->Execute(SID_SWITCHPAGE, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + + bool bSetModified = true; + + if (pArgs->Count() == 1) + { + bSetModified = static_cast<const SfxBoolItem&>(pArgs->Get(SID_MODIFYPAGE)).GetValue(); + } + + pUndoManager->AddUndoAction( std::make_unique<UndoAutoLayoutPosAndSize>( *pUndoPage ) ); + pUndoManager->LeaveListAction(); + + pDocument->SetChanged(bSetModified); + } + } + while (false); + + mrViewShell.Cancel(); + rRequest.Done (); +} + +void ViewShell::Implementation::AssignLayout ( SfxRequest const & rRequest, PageKind ePageKind ) +{ + const SfxUInt32Item* pWhatPage = rRequest.GetArg<SfxUInt32Item>(ID_VAL_WHATPAGE); + const SfxUInt32Item* pWhatLayout = rRequest.GetArg<SfxUInt32Item>(ID_VAL_WHATLAYOUT); + + SdDrawDocument* pDocument = mrViewShell.GetDoc(); + if( !pDocument ) + return; + + SdPage* pPage = nullptr; + if( pWhatPage ) + { + pPage = pDocument->GetSdPage(static_cast<sal_uInt16>(pWhatPage->GetValue()), ePageKind); + } + + if( pPage == nullptr ) + pPage = mrViewShell.getCurrentPage(); + + if( !pPage ) + return; + + AutoLayout eLayout = pPage->GetAutoLayout(); + + if( pWhatLayout ) + eLayout = static_cast< AutoLayout >( pWhatLayout->GetValue() ); + + // Transform the given request into the four argument form that is + // understood by ProcessModifyPageSlot(). + SdrLayerAdmin& rLayerAdmin (mrViewShell.GetViewShellBase().GetDocument()->GetLayerAdmin()); + SdrLayerID aBackground (rLayerAdmin.GetLayerID(sUNO_LayerName_background)); + SdrLayerID aBackgroundObject (rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects)); + + SdrLayerIDSet aVisibleLayers; + + if( pPage->GetPageKind() == PageKind::Handout ) + aVisibleLayers.SetAll(); + else + aVisibleLayers = pPage->TRG_GetMasterPageVisibleLayers(); + + SfxRequest aRequest (mrViewShell.GetViewShellBase().GetViewFrame(), SID_MODIFYPAGE); + aRequest.AppendItem(SfxStringItem (ID_VAL_PAGENAME, pPage->GetName())); + aRequest.AppendItem(SfxUInt32Item (ID_VAL_WHATLAYOUT, eLayout)); + aRequest.AppendItem(SfxBoolItem(ID_VAL_ISPAGEBACK, aVisibleLayers.IsSet(aBackground))); + aRequest.AppendItem(SfxBoolItem(ID_VAL_ISPAGEOBJ, aVisibleLayers.IsSet(aBackgroundObject))); + + // Forward the call with the new arguments. + ProcessModifyPageSlot( aRequest, pPage, pPage->GetPageKind()); +} + +SfxInterfaceId ViewShell::Implementation::GetViewId() const +{ + switch (mrViewShell.GetShellType()) + { + case ViewShell::ST_IMPRESS: + case ViewShell::ST_NOTES: + case ViewShell::ST_HANDOUT: + return IMPRESS_FACTORY_ID; + + case ViewShell::ST_DRAW: + return DRAW_FACTORY_ID; + + case ViewShell::ST_OUTLINE: + return OUTLINE_FACTORY_ID; + + case ViewShell::ST_SLIDE_SORTER: + return SLIDE_SORTER_FACTORY_ID; + + case ViewShell::ST_PRESENTATION: + return PRESENTATION_FACTORY_ID; + + // Since we have to return a view id for every possible shell type + // and there is not (yet) a proper ViewShellBase sub class for the + // remaining types we chose the Impress factory as a fall back. + case ViewShell::ST_SIDEBAR: + case ViewShell::ST_NONE: + default: + return IMPRESS_FACTORY_ID; + } +} + +SvxIMapDlg* ViewShell::Implementation::GetImageMapDialog() +{ + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + if (!pViewFrm) + return nullptr; + + SfxChildWindow* pChildWindow = pViewFrm->GetChildWindow( + SvxIMapDlgChildWindow::GetChildWindowId()); + if (pChildWindow == nullptr) + return nullptr; + + return dynamic_cast<SvxIMapDlg*>(pChildWindow->GetController().get()); +} + +//===== ToolBarManagerLock ==================================================== + +class ViewShell::Implementation::ToolBarManagerLock::Deleter { public: + void operator() (ToolBarManagerLock* pObject) { delete pObject; } +}; + +std::shared_ptr<ViewShell::Implementation::ToolBarManagerLock> + ViewShell::Implementation::ToolBarManagerLock::Create ( + const std::shared_ptr<ToolBarManager>& rpManager) +{ + std::shared_ptr<ToolBarManagerLock> pLock ( + new ViewShell::Implementation::ToolBarManagerLock(rpManager), + ViewShell::Implementation::ToolBarManagerLock::Deleter()); + pLock->mpSelf = pLock; + return pLock; +} + +ViewShell::Implementation::ToolBarManagerLock::ToolBarManagerLock ( + const std::shared_ptr<ToolBarManager>& rpManager) + : mpLock(new ToolBarManager::UpdateLock(rpManager)), + maTimer("sd ToolBarManagerLock maTimer") +{ + // Start a timer that will unlock the ToolBarManager update lock when + // that is not done explicitly by calling Release(). + maTimer.SetInvokeHandler(LINK(this,ToolBarManagerLock,TimeoutCallback)); + maTimer.SetTimeout(100); + maTimer.Start(); +} + +IMPL_LINK_NOARG(ViewShell::Implementation::ToolBarManagerLock, TimeoutCallback, Timer *, void) +{ + // If possible then release the lock now. Otherwise start the timer + // and try again later. + if (Application::IsUICaptured()) + { + maTimer.Start(); + } + else + { + mpSelf.reset(); + } +} + +void ViewShell::Implementation::ToolBarManagerLock::Release (bool bForce) +{ + // If possible then release the lock now. Otherwise try again when the + // timer expires. + if (bForce || ! Application::IsUICaptured()) + { + mpSelf.reset(); + } +} + +ViewShell::Implementation::ToolBarManagerLock::~ToolBarManagerLock() +{ + mpLock.reset(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/ViewShellManager.cxx b/sd/source/ui/view/ViewShellManager.cxx new file mode 100644 index 000000000..db2ee5f8f --- /dev/null +++ b/sd/source/ui/view/ViewShellManager.cxx @@ -0,0 +1,1168 @@ +/* -*- 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 <ViewShellManager.hxx> +#include <ViewShell.hxx> +#include <ViewShellBase.hxx> +#include <Window.hxx> +#include <DrawDocShell.hxx> + +#include <sal/log.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/svxids.hrc> +#include <svx/fmshell.hxx> +#include <vcl/vclevent.hxx> +#include <osl/diagnose.h> + +#include <iterator> +#include <list> +#include <unordered_map> + +namespace sd { + +namespace { + +/** The ShellDescriptor class is used to shells together with their ids and + the factory that was used to create the shell. + + The shell pointer may be NULL. In that case the shell is created on + demand by a factory. + + The factory pointer may be NULL. In that case the shell pointer is + given to the ViewShellManager. + + Shell pointer and factory pointer can but should not be NULL at the same + time. +*/ +class ShellDescriptor { +public: + SfxShell* mpShell; + ShellId mnId; + ViewShellManager::SharedShellFactory mpFactory; + bool mbIsListenerAddedToWindow; + + ShellDescriptor (); + explicit ShellDescriptor (ShellId nId); + vcl::Window* GetWindow() const; +}; + +/** This functor can be used to search for a shell in an STL container when the + shell pointer is given. +*/ +class IsShell +{ +public: + explicit IsShell (const SfxShell* pShell) : mpShell(pShell) {} + bool operator() (const ShellDescriptor& rDescriptor) + { return rDescriptor.mpShell == mpShell; } +private: + const SfxShell* mpShell; +}; + +/** This functor can be used to search for a shell in an STL container when the + id of the shell is given. +*/ +class IsId +{ +public: + explicit IsId (ShellId nId) : mnId(nId) {} + bool operator() (const ShellDescriptor& rDescriptor) + { return rDescriptor.mnId == mnId; } +private: + ShellId mnId; +}; + +} // end of anonymous namespace + +class ViewShellManager::Implementation +{ +public: + Implementation ( + ViewShellBase& rBase); + ~Implementation() COVERITY_NOEXCEPT_FALSE; + + void AddShellFactory ( + const SfxShell* pViewShell, + const SharedShellFactory& rpFactory); + void RemoveShellFactory ( + const SfxShell* pViewShell, + const SharedShellFactory& rpFactory); + void ActivateViewShell ( + ViewShell* pViewShell); + void DeactivateViewShell (const ViewShell& rShell); + void ActivateShell (SfxShell& rShell); + void DeactivateShell (const SfxShell& rShell); + void ActivateShell (const ShellDescriptor& rDescriptor); + void SetFormShell (const ViewShell* pViewShell, FmFormShell* pFormShell, bool bAbove); + void ActivateSubShell (const SfxShell& rParentShell, ShellId nId); + void DeactivateSubShell (const SfxShell& rParentShell, ShellId nId); + void MoveToTop (const SfxShell& rParentShell); + SfxShell* GetShell (ShellId nId) const; + SfxShell* GetTopShell() const; + SfxShell* GetTopViewShell() const; + void Shutdown(); + void InvalidateAllSubShells (const SfxShell* pParentShell); + + /** Remove all shells from the SFX stack above and including the given + shell. + */ + void TakeShellsFromStack (const SfxShell* pShell); + + class UpdateLock + { + public: + explicit UpdateLock (Implementation& rImpl) : mrImpl(rImpl) {mrImpl.LockUpdate();} + ~UpdateLock() COVERITY_NOEXCEPT_FALSE {mrImpl.UnlockUpdate();} + private: + Implementation& mrImpl; + }; + + /** Prevent updates of the shell stack. While the sub shell manager is + locked it will update its internal data structures but not alter the + shell stack. Use this method when there are several modifications + to the shell stack to prevent multiple rebuilds of the shell stack + and resulting broadcasts. + */ + void LockUpdate(); + + /** Allow updates of the shell stack. This method has to be called the + same number of times as LockUpdate() to really allow a rebuild of + the shell stack. + */ + void UnlockUpdate(); + +private: + ViewShellBase& mrBase; + mutable ::osl::Mutex maMutex; + + class ShellHash { public: size_t operator()(const SfxShell* p) const { return reinterpret_cast<size_t>(p);} }; + typedef std::unordered_multimap<const SfxShell*,SharedShellFactory,ShellHash> + FactoryList; + FactoryList maShellFactories; + + /** List of the active view shells. In order to create gather all shells + to put on the shell stack each view shell in this list is asked for + its sub-shells (typically toolbars). + */ + typedef std::list<ShellDescriptor> ActiveShellList; + ActiveShellList maActiveViewShells; + + typedef std::list<ShellDescriptor> SubShellSubList; + typedef std::unordered_map<const SfxShell*,SubShellSubList,ShellHash> SubShellList; + SubShellList maActiveSubShells; + + /** In this member we remember what shells we have pushed on the shell + stack. + */ + typedef ::std::vector<SfxShell*> ShellStack; + + int mnUpdateLockCount; + + /** The UpdateShellStack() method can be called recursively. This flag + is used to communicate between different levels of invocation: if + the stack has been updated in an inner call the outer call can (has + to) stop and return immediately. + */ + bool mbShellStackIsUpToDate; + + SfxShell* mpFormShell; + const ViewShell* mpFormShellParent; + bool mbFormShellAboveParent; + + SfxShell* mpTopShell; + SfxShell* mpTopViewShell; + + + void UpdateShellStack(); + + void CreateShells(); + + /** This method rebuilds the stack of shells that are stacked upon the + view shell base. + */ + void CreateTargetStack (ShellStack& rStack) const; + + DECL_LINK(WindowEventHandler, VclWindowEvent&, void); + +#if OSL_DEBUG_LEVEL >= 2 + void DumpShellStack (const ShellStack& rStack); + void DumpSfxShellStack(); +#endif + + /** To be called before a shell is taken from the SFX shell stack. This + method deactivates an active text editing to avoid problems with + undo managers. + Afterwards the Deactivate() of the shell is called. + */ + static void Deactivate (SfxShell* pShell); + + ShellDescriptor CreateSubShell ( + SfxShell const * pShell, + ShellId nShellId); + void DestroyViewShell (ShellDescriptor& rDescriptor); + static void DestroySubShell (const ShellDescriptor& rDescriptor); +}; + +//===== ViewShellManager ====================================================== + +ViewShellManager::ViewShellManager (ViewShellBase& rBase) + : mpImpl(new Implementation(rBase)), + mbValid(true) +{ +} + +ViewShellManager::~ViewShellManager() +{ +} + +void ViewShellManager::AddSubShellFactory ( + ViewShell const * pViewShell, + const SharedShellFactory& rpFactory) +{ + if (mbValid) + mpImpl->AddShellFactory(pViewShell, rpFactory); +} + +void ViewShellManager::RemoveSubShellFactory ( + ViewShell const * pViewShell, + const SharedShellFactory& rpFactory) +{ + if (mbValid) + mpImpl->RemoveShellFactory(pViewShell, rpFactory); +} + +void ViewShellManager::ActivateViewShell (ViewShell* pViewShell) +{ + if (mbValid) + return mpImpl->ActivateViewShell(pViewShell); +} + +void ViewShellManager::DeactivateViewShell (const ViewShell* pShell) +{ + if (mbValid && pShell!=nullptr) + mpImpl->DeactivateViewShell(*pShell); +} + +void ViewShellManager::SetFormShell ( + const ViewShell* pParentShell, + FmFormShell* pFormShell, + bool bAbove) +{ + if (mbValid) + mpImpl->SetFormShell(pParentShell,pFormShell,bAbove); +} + +void ViewShellManager::ActivateSubShell (const ViewShell& rViewShell, ShellId nId) +{ + if (mbValid) + mpImpl->ActivateSubShell(rViewShell,nId); +} + +void ViewShellManager::DeactivateSubShell (const ViewShell& rViewShell, ShellId nId) +{ + if (mbValid) + mpImpl->DeactivateSubShell(rViewShell,nId); +} + +void ViewShellManager::InvalidateAllSubShells (ViewShell const * pViewShell) +{ + if (mbValid) + mpImpl->InvalidateAllSubShells(pViewShell); +} + +void ViewShellManager::ActivateShell (SfxShell* pShell) +{ + if (mbValid && pShell!=nullptr) + mpImpl->ActivateShell(*pShell); +} + +void ViewShellManager::DeactivateShell (const SfxShell* pShell) +{ + if (mbValid && pShell!=nullptr) + mpImpl->DeactivateShell(*pShell); +} + +void ViewShellManager::MoveToTop (const ViewShell& rParentShell) +{ + if (mbValid) + mpImpl->MoveToTop(rParentShell); +} + +SfxShell* ViewShellManager::GetShell (ShellId nId) const +{ + if (mbValid) + return mpImpl->GetShell(nId); + else + return nullptr; +} + +SfxShell* ViewShellManager::GetTopShell() const +{ + if (mbValid) + return mpImpl->GetTopShell(); + else + return nullptr; +} + +SfxShell* ViewShellManager::GetTopViewShell() const +{ + if (mbValid) + return mpImpl->GetTopViewShell(); + else + return nullptr; +} + +void ViewShellManager::Shutdown() +{ + if (mbValid) + { + mpImpl->Shutdown(); + mbValid = false; + } +} + +void ViewShellManager::LockUpdate() +{ + mpImpl->LockUpdate(); +} + +void ViewShellManager::UnlockUpdate() +{ + mpImpl->UnlockUpdate(); +} + +//===== ViewShellManager::Implementation ====================================== + +ViewShellManager::Implementation::Implementation ( + ViewShellBase& rBase) + : mrBase(rBase), + mnUpdateLockCount(0), + mbShellStackIsUpToDate(true), + mpFormShell(nullptr), + mpFormShellParent(nullptr), + mbFormShellAboveParent(true), + mpTopShell(nullptr), + mpTopViewShell(nullptr) +{} + +ViewShellManager::Implementation::~Implementation() COVERITY_NOEXCEPT_FALSE +{ + Shutdown(); +} + +void ViewShellManager::Implementation::AddShellFactory ( + const SfxShell* pViewShell, + const SharedShellFactory& rpFactory) +{ + bool bAlreadyAdded (false); + + // Check that the given factory has not already been added. + ::std::pair<FactoryList::iterator,FactoryList::iterator> aRange( + maShellFactories.equal_range(pViewShell)); + for (FactoryList::const_iterator iFactory=aRange.first; iFactory!=aRange.second; ++iFactory) + if (iFactory->second == rpFactory) + { + bAlreadyAdded = true; + break; + } + + // Add the factory if it is not already present. + if ( ! bAlreadyAdded) + maShellFactories.emplace(pViewShell, rpFactory); +} + +void ViewShellManager::Implementation::RemoveShellFactory ( + const SfxShell* pViewShell, + const SharedShellFactory& rpFactory) +{ + ::std::pair<FactoryList::iterator,FactoryList::iterator> aRange( + maShellFactories.equal_range(pViewShell)); + for (FactoryList::iterator iFactory=aRange.first; iFactory!=aRange.second; ++iFactory) + if (iFactory->second == rpFactory) + { + maShellFactories.erase(iFactory); + break; + } +} + +void ViewShellManager::Implementation::ActivateViewShell (ViewShell* pViewShell) +{ + ::osl::MutexGuard aGuard (maMutex); + + ShellDescriptor aResult; + aResult.mpShell = pViewShell; + + // Register as window listener so that the shells of the current + // window can be moved to the top of the shell stack. + if (aResult.mpShell != nullptr) + { + vcl::Window* pWindow = aResult.GetWindow(); + if (pWindow != nullptr) + { + pWindow->AddEventListener( + LINK(this, ViewShellManager::Implementation, WindowEventHandler)); + aResult.mbIsListenerAddedToWindow = true; + } + else + { + SAL_WARN("sd.view", + "ViewShellManager::ActivateViewShell: " + "new view shell has no active window"); + } + } + + ActivateShell(aResult); +} + +void ViewShellManager::Implementation::DeactivateViewShell (const ViewShell& rShell) +{ + ::osl::MutexGuard aGuard (maMutex); + + ActiveShellList::iterator iShell (::std::find_if ( + maActiveViewShells.begin(), + maActiveViewShells.end(), + IsShell(&rShell))); + if (iShell == maActiveViewShells.end()) + return; + + UpdateLock aLocker (*this); + + ShellDescriptor aDescriptor(*iShell); + mrBase.GetDocShell()->Disconnect(dynamic_cast<ViewShell*>(aDescriptor.mpShell)); + maActiveViewShells.erase(iShell); + TakeShellsFromStack(aDescriptor.mpShell); + + // Deactivate sub shells. + SubShellList::iterator iList (maActiveSubShells.find(&rShell)); + if (iList != maActiveSubShells.end()) + { + SubShellSubList& rList (iList->second); + while ( ! rList.empty()) + DeactivateSubShell(rShell, rList.front().mnId); + } + + DestroyViewShell(aDescriptor); +} + +void ViewShellManager::Implementation::ActivateShell (SfxShell& rShell) +{ + ::osl::MutexGuard aGuard (maMutex); + + // Create a new shell or recycle on in the cache. + ShellDescriptor aDescriptor; + aDescriptor.mpShell = &rShell; + + ActivateShell(aDescriptor); +} + +void ViewShellManager::Implementation::ActivateShell (const ShellDescriptor& rDescriptor) +{ + // Put shell on top of the active view shells. + if (rDescriptor.mpShell != nullptr) + { + maActiveViewShells.insert( maActiveViewShells.begin(), rDescriptor); + } +} + +void ViewShellManager::Implementation::DeactivateShell (const SfxShell& rShell) +{ + ::osl::MutexGuard aGuard (maMutex); + + ActiveShellList::iterator iShell (::std::find_if ( + maActiveViewShells.begin(), + maActiveViewShells.end(), + IsShell(&rShell))); + if (iShell == maActiveViewShells.end()) + return; + + UpdateLock aLocker (*this); + + ShellDescriptor aDescriptor(*iShell); + mrBase.GetDocShell()->Disconnect(dynamic_cast<ViewShell*>(aDescriptor.mpShell)); + maActiveViewShells.erase(iShell); + TakeShellsFromStack(aDescriptor.mpShell); + + // Deactivate sub shells. + SubShellList::iterator iList (maActiveSubShells.find(&rShell)); + if (iList != maActiveSubShells.end()) + { + SubShellSubList& rList (iList->second); + while ( ! rList.empty()) + DeactivateSubShell(rShell, rList.front().mnId); + } + + DestroyViewShell(aDescriptor); +} + +void ViewShellManager::Implementation::ActivateSubShell ( + const SfxShell& rParentShell, + ShellId nId) +{ + ::osl::MutexGuard aGuard (maMutex); + + // Check that the given view shell is active. + if (std::none_of (maActiveViewShells.begin(), maActiveViewShells.end(), IsShell(&rParentShell))) + return; + + // Create the sub shell list if it does not yet exist. + SubShellList::iterator iList (maActiveSubShells.find(&rParentShell)); + if (iList == maActiveSubShells.end()) + iList = maActiveSubShells.emplace(&rParentShell,SubShellSubList()).first; + + // Do not activate an object bar that is already active. Requesting + // this is not exactly an error but may be an indication of one. + SubShellSubList& rList (iList->second); + if (std::any_of(rList.begin(),rList.end(), IsId(nId))) + return; + + // Add just the id of the sub shell. The actual shell is created + // later in CreateShells(). + UpdateLock aLock (*this); + rList.emplace_back(nId); +} + +void ViewShellManager::Implementation::DeactivateSubShell ( + const SfxShell& rParentShell, + ShellId nId) +{ + ::osl::MutexGuard aGuard (maMutex); + + // Check that the given view shell is active. + SubShellList::iterator iList (maActiveSubShells.find(&rParentShell)); + if (iList == maActiveSubShells.end()) + return; + + // Look up the sub shell. + SubShellSubList& rList (iList->second); + SubShellSubList::iterator iShell ( + ::std::find_if(rList.begin(),rList.end(), IsId(nId))); + if (iShell == rList.end()) + return; + SfxShell* pShell = iShell->mpShell; + if (pShell == nullptr) + return; + + UpdateLock aLock (*this); + + ShellDescriptor aDescriptor(*iShell); + // Remove the sub shell from both the internal structure as well as the + // SFX shell stack above and including the sub shell. + rList.erase(iShell); + TakeShellsFromStack(pShell); + + DestroySubShell(aDescriptor); +} + +void ViewShellManager::Implementation::MoveToTop (const SfxShell& rShell) +{ + ::osl::MutexGuard aGuard (maMutex); + + // Check that we have access to a dispatcher. If not, then we are + // (probably) called while the view shell is still being created or + // initialized. Without dispatcher we can not rebuild the shell stack + // to move the requested shell to the top. So return right away instead + // of making a mess without being able to clean up afterwards. + if (mrBase.GetDispatcher() == nullptr) + return; + + ActiveShellList::iterator iShell (::std::find_if ( + maActiveViewShells.begin(), + maActiveViewShells.end(), + IsShell(&rShell))); + bool bMove = true; + if (iShell != maActiveViewShells.end()) + { + // Is the shell already at the top of the stack? We have to keep + // the case in mind that mbKeepMainViewShellOnTop is true. Shells + // that are not the main view shell are placed on the second-to-top + // position in this case. + if (iShell == maActiveViewShells.begin()) + { + // The shell is at the top position and is either a) the main + // view shell or b) another shell but the main view shell is not + // kept at the top position. We do not have to move the shell. + bMove = false; + } + } + else + { + // The shell is not on the stack. Therefore it can not be moved. + // We could insert it but we don't. Use ActivateViewShell() for + // that. + bMove = false; + } + + // When the shell is not at the right position it is removed from the + // internal list of shells and inserted at the correct position. + if (bMove) + { + UpdateLock aLock (*this); + + ShellDescriptor aDescriptor(*iShell); + + TakeShellsFromStack(&rShell); + maActiveViewShells.erase(iShell); + + maActiveViewShells.insert(maActiveViewShells.begin(), aDescriptor); + } +} + +SfxShell* ViewShellManager::Implementation::GetShell (ShellId nId) const +{ + ::osl::MutexGuard aGuard (maMutex); + + SfxShell* pShell = nullptr; + + // First search the active view shells. + ActiveShellList::const_iterator iShell ( + ::std::find_if ( + maActiveViewShells.begin(), + maActiveViewShells.end(), + IsId(nId))); + if (iShell != maActiveViewShells.end()) + pShell = iShell->mpShell; + else + { + // Now search the active sub shells of every active view shell. + for (auto const& activeSubShell : maActiveSubShells) + { + const SubShellSubList& rList (activeSubShell.second); + SubShellSubList::const_iterator iSubShell( + ::std::find_if(rList.begin(),rList.end(), IsId(nId))); + if (iSubShell != rList.end()) + { + pShell = iSubShell->mpShell; + break; + } + } + } + + return pShell; +} + +SfxShell* ViewShellManager::Implementation::GetTopShell() const +{ + OSL_ASSERT(mpTopShell == mrBase.GetSubShell(0)); + return mpTopShell; +} + +SfxShell* ViewShellManager::Implementation::GetTopViewShell() const +{ + return mpTopViewShell; +} + +void ViewShellManager::Implementation::LockUpdate() +{ + mnUpdateLockCount++; +} + +void ViewShellManager::Implementation::UnlockUpdate() +{ + ::osl::MutexGuard aGuard (maMutex); + + mnUpdateLockCount--; + if (mnUpdateLockCount < 0) + { + // This should not happen. + OSL_ASSERT (mnUpdateLockCount>=0); + mnUpdateLockCount = 0; + } + if (mnUpdateLockCount == 0) + UpdateShellStack(); +} + +/** Update the SFX shell stack (the portion that is visible to us) so that + it matches the internal shell stack. This is done in six steps: + 1. Create the missing view shells and sub shells. + 2. Set up the internal shell stack. + 3. Get the SFX shell stack. + 4. Find the lowest shell in which the two stacks differ. + 5. Remove all shells above and including that shell from the SFX stack. + 6. Push all shells of the internal stack on the SFX shell stack that are + not already present on the later. +*/ +void ViewShellManager::Implementation::UpdateShellStack() +{ + ::osl::MutexGuard aGuard (maMutex); + + // Remember the undo manager from the top-most shell on the stack. + SfxShell* pTopMostShell = mrBase.GetSubShell(0); + SfxUndoManager* pUndoManager = (pTopMostShell!=nullptr) + ? pTopMostShell->GetUndoManager() + : nullptr; + + // 1. Create the missing shells. + CreateShells(); + + // Update the pointer to the top-most active view shell. + mpTopViewShell = (maActiveViewShells.empty()) + ? nullptr : maActiveViewShells.begin()->mpShell; + + + // 2. Create the internal target stack. + ShellStack aTargetStack; + CreateTargetStack(aTargetStack); + + // 3. Get SFX shell stack. + ShellStack aSfxShellStack; + sal_uInt16 nIndex (0); + while (mrBase.GetSubShell(nIndex)!=nullptr) + ++nIndex; + aSfxShellStack.reserve(nIndex); + while (nIndex-- > 0) + aSfxShellStack.push_back(mrBase.GetSubShell(nIndex)); + +#if OSL_DEBUG_LEVEL >= 2 + SAL_INFO("sd.view", __func__ << ": Current SFX Stack"); + DumpShellStack(aSfxShellStack); + SAL_INFO("sd.view", __func__ << ": Target Stack"); + DumpShellStack(aTargetStack); +#endif + + // 4. Find the lowest shell in which the two stacks differ. + auto mismatchIters = std::mismatch(aSfxShellStack.begin(), aSfxShellStack.end(), + aTargetStack.begin(), aTargetStack.end()); + ShellStack::iterator iSfxShell (mismatchIters.first); + ShellStack::iterator iTargetShell (mismatchIters.second); + + // 5. Remove all shells above and including the differing shell from the + // SFX stack starting with the shell on top of the stack. + for (std::reverse_iterator<ShellStack::const_iterator> i(aSfxShellStack.end()), iLast(iSfxShell); + i != iLast; ++i) + { + SfxShell* const pShell = *i; + SAL_INFO("sd.view", __func__ << ": removing shell " << pShell << " from stack"); + mrBase.RemoveSubShell(pShell); + } + aSfxShellStack.erase(iSfxShell, aSfxShellStack.end()); + + // 6. Push shells from the given stack onto the SFX stack. + mbShellStackIsUpToDate = false; + while (iTargetShell != aTargetStack.end()) + { + SAL_INFO("sd.view", __func__ << ": pushing shell " << *iTargetShell << " on stack"); + mrBase.AddSubShell(**iTargetShell); + ++iTargetShell; + + // The pushing of the shell on to the shell stack may have lead to + // another invocation of this method. In this case we have to abort + // pushing shells on the stack and return immediately. + if (mbShellStackIsUpToDate) + break; + } + if (mrBase.GetDispatcher() != nullptr) + mrBase.GetDispatcher()->Flush(); + + // Update the pointer to the top-most shell and set its undo manager + // to the one of the previous top-most shell. + mpTopShell = mrBase.GetSubShell(0); + if (mpTopShell!=nullptr && pUndoManager!=nullptr && mpTopShell->GetUndoManager()==nullptr) + mpTopShell->SetUndoManager(pUndoManager); + + // Finally tell an invocation of this method on a higher level that it can (has + // to) abort and return immediately. + mbShellStackIsUpToDate = true; + +#if OSL_DEBUG_LEVEL >= 2 + SAL_INFO("sd.view", __func__ << ": New current stack"); + DumpSfxShellStack(); +#endif +} + +void ViewShellManager::Implementation::TakeShellsFromStack (const SfxShell* pShell) +{ + ::osl::MutexGuard aGuard (maMutex); + + // Remember the undo manager from the top-most shell on the stack. + SfxShell* pTopMostShell = mrBase.GetSubShell(0); + SfxUndoManager* pUndoManager = (pTopMostShell!=nullptr) + ? pTopMostShell->GetUndoManager() + : nullptr; + +#if OSL_DEBUG_LEVEL >= 2 + SAL_INFO("sd.view", __func__ << "TakeShellsFromStack( " << pShell << ")"); + DumpSfxShellStack(); +#endif + + // 0.Make sure that the given shell is on the stack. This is a + // preparation for the following assertion. + for (sal_uInt16 nIndex=0; true; nIndex++) + { + SfxShell* pShellOnStack = mrBase.GetSubShell(nIndex); + if (pShellOnStack == nullptr) + { + // Set pShell to NULL to indicate the following code that the + // shell is not on the stack. + pShell = nullptr; + break; + } + else if (pShellOnStack == pShell) + break; + } + + if (pShell == nullptr) + return; + + // 1. Deactivate our shells on the stack before they are removed so + // that during the Deactivation() calls the stack is still intact. + for (sal_uInt16 nIndex=0; true; nIndex++) + { + SfxShell* pShellOnStack = mrBase.GetSubShell(nIndex); + Deactivate(pShellOnStack); + if (pShellOnStack == pShell) + break; + } + + // 2. Remove the shells from the stack. + while (true) + { + SfxShell* pShellOnStack = mrBase.GetSubShell(0); + SAL_INFO("sd.view", __func__ << "removing shell " << pShellOnStack << " from stack"); + mrBase.RemoveSubShell(pShellOnStack); + if (pShellOnStack == pShell) + break; + } + + // 3. Update the stack. + if (mrBase.GetDispatcher() != nullptr) + mrBase.GetDispatcher()->Flush(); + + // Update the pointer to the top-most shell and set its undo manager + // to the one of the previous top-most shell. + mpTopShell = mrBase.GetSubShell(0); + if (mpTopShell!=nullptr && pUndoManager!=nullptr && mpTopShell->GetUndoManager()==nullptr) + mpTopShell->SetUndoManager(pUndoManager); + +#if OSL_DEBUG_LEVEL >= 2 + SAL_INFO("sd.view", __func__ << "Sfx shell stack is:"); + DumpSfxShellStack(); +#endif +} + +void ViewShellManager::Implementation::CreateShells() +{ + ::osl::MutexGuard aGuard (maMutex); + + // Iterate over all view shells. + ActiveShellList::reverse_iterator iShell; + for (iShell=maActiveViewShells.rbegin(); iShell!=maActiveViewShells.rend(); ++iShell) + { + // Get the list of associated sub shells. + SubShellList::iterator iList (maActiveSubShells.find(iShell->mpShell)); + if (iList != maActiveSubShells.end()) + { + SubShellSubList& rList (iList->second); + + // Iterate over all sub shells of the current view shell. + for (auto & subShell : rList) + { + if (subShell.mpShell == nullptr) + { + subShell = CreateSubShell(iShell->mpShell,subShell.mnId); + } + } + } + } +} + +void ViewShellManager::Implementation::CreateTargetStack (ShellStack& rStack) const +{ + // Create a local stack of the shells that are to push on the shell + // stack. We can thus safely create the required shells while still + // having a valid shell stack. + for (ActiveShellList::const_reverse_iterator iViewShell (maActiveViewShells.rbegin()); + iViewShell != maActiveViewShells.rend(); + ++iViewShell) + { + // Possibly place the form shell below the current view shell. + if ( ! mbFormShellAboveParent + && mpFormShell!=nullptr + && iViewShell->mpShell==mpFormShellParent) + { + rStack.push_back(mpFormShell); + } + + // Put the view shell itself on the local stack. + rStack.push_back (iViewShell->mpShell); + + // Possibly place the form shell above the current view shell. + if (mbFormShellAboveParent + && mpFormShell!=nullptr + && iViewShell->mpShell==mpFormShellParent) + { + rStack.push_back(mpFormShell); + } + + // Add all other sub shells. + SubShellList::const_iterator iList (maActiveSubShells.find(iViewShell->mpShell)); + if (iList != maActiveSubShells.end()) + { + const SubShellSubList& rList (iList->second); + SubShellSubList::const_reverse_iterator iSubShell; + for (iSubShell=rList.rbegin(); iSubShell!=rList.rend(); ++iSubShell) + if (iSubShell->mpShell != mpFormShell) + rStack.push_back(iSubShell->mpShell); + } + } +} + +IMPL_LINK(ViewShellManager::Implementation, WindowEventHandler, VclWindowEvent&, rEvent, void) +{ + vcl::Window* pEventWindow = rEvent.GetWindow(); + + switch (rEvent.GetId()) + { + case VclEventId::WindowGetFocus: + { + for (auto const& activeShell : maActiveViewShells) + { + if (pEventWindow == activeShell.GetWindow()) + { + MoveToTop(*activeShell.mpShell); + break; + } + } + } + break; + + case VclEventId::WindowLoseFocus: + break; + + case VclEventId::ObjectDying: + // Remember that we do not have to remove the window + // listener for this window. + for (auto & activeViewShell : maActiveViewShells) + { + if (activeViewShell.GetWindow() == pEventWindow) + { + activeViewShell.mbIsListenerAddedToWindow = false; + break; + } + } + break; + + default: break; + } +} + +ShellDescriptor ViewShellManager::Implementation::CreateSubShell ( + SfxShell const * pParentShell, + ShellId nShellId) +{ + ::osl::MutexGuard aGuard (maMutex); + ShellDescriptor aResult; + + // Look up the factories for the parent shell. + ::std::pair<FactoryList::iterator,FactoryList::iterator> aRange( + maShellFactories.equal_range(pParentShell)); + + // Try all factories to create the shell. + for (FactoryList::const_iterator iFactory=aRange.first; iFactory!=aRange.second; ++iFactory) + { + SharedShellFactory pFactory = iFactory->second; + if (pFactory != nullptr) + aResult.mpShell = pFactory->CreateShell(nShellId); + + // Exit the loop when the shell has been successfully created. + if (aResult.mpShell != nullptr) + { + aResult.mpFactory = pFactory; + aResult.mnId = nShellId; + break; + } + } + + return aResult; +} + +void ViewShellManager::Implementation::DestroyViewShell ( + ShellDescriptor& rDescriptor) +{ + OSL_ASSERT(rDescriptor.mpShell != nullptr); + + if (rDescriptor.mbIsListenerAddedToWindow) + { + rDescriptor.mbIsListenerAddedToWindow = false; + vcl::Window* pWindow = rDescriptor.GetWindow(); + if (pWindow != nullptr) + { + pWindow->RemoveEventListener( + LINK(this, ViewShellManager::Implementation, WindowEventHandler)); + } + } + + // Destroy the sub shell factories. + ::std::pair<FactoryList::iterator,FactoryList::iterator> aRange( + maShellFactories.equal_range(rDescriptor.mpShell)); + if (aRange.first != maShellFactories.end()) + maShellFactories.erase(aRange.first, aRange.second); + + // Release the shell. + if (rDescriptor.mpFactory) + rDescriptor.mpFactory->ReleaseShell(rDescriptor.mpShell); +} + +void ViewShellManager::Implementation::DestroySubShell ( + const ShellDescriptor& rDescriptor) +{ + OSL_ASSERT(rDescriptor.mpFactory); + rDescriptor.mpFactory->ReleaseShell(rDescriptor.mpShell); +} + +void ViewShellManager::Implementation::InvalidateAllSubShells (const SfxShell* pParentShell) +{ + ::osl::MutexGuard aGuard (maMutex); + + SubShellList::iterator iList (maActiveSubShells.find(pParentShell)); + if (iList != maActiveSubShells.end()) + { + SubShellSubList& rList (iList->second); + for (auto const& shell : rList) + if (shell.mpShell != nullptr) + shell.mpShell->Invalidate(); + } +} + +void ViewShellManager::Implementation::Shutdown() +{ + ::osl::MutexGuard aGuard (maMutex); + + // Take stacked shells from stack. + if ( ! maActiveViewShells.empty()) + { + UpdateLock aLock (*this); + + while ( ! maActiveViewShells.empty()) + { + SfxShell* pShell = maActiveViewShells.front().mpShell; + if (pShell != nullptr) + { + ViewShell* pViewShell = dynamic_cast<ViewShell*>(pShell); + if (pViewShell != nullptr) + DeactivateViewShell(*pViewShell); + else + DeactivateShell(*pShell); + } + else + { + SAL_WARN("sd.view", + "ViewShellManager::Implementation::Shutdown(): empty active shell descriptor"); + maActiveViewShells.pop_front(); + } + } + } + mrBase.RemoveSubShell (); + + maShellFactories.clear(); +} + +#if OSL_DEBUG_LEVEL >= 2 +void ViewShellManager::Implementation::DumpShellStack (const ShellStack& rStack) +{ + ShellStack::const_reverse_iterator iEntry; + for (iEntry=rStack.rbegin(); iEntry!=rStack.rend(); ++iEntry) + if (*iEntry != NULL) + SAL_INFO("sd.view", __func__ << ": " << + *iEntry << " : " << + (*iEntry)->GetName()); + else + SAL_INFO("sd.view", __func__ << " null"); +} + +void ViewShellManager::Implementation::DumpSfxShellStack() +{ + ShellStack aSfxShellStack; + sal_uInt16 nIndex (0); + while (mrBase.GetSubShell(nIndex)!=NULL) + ++nIndex; + aSfxShellStack.reserve(nIndex); + while (nIndex-- > 0) + aSfxShellStack.push_back(mrBase.GetSubShell(nIndex)); + DumpShellStack(aSfxShellStack); +} +#endif + +void ViewShellManager::Implementation::Deactivate (SfxShell* pShell) +{ + OSL_ASSERT(pShell!=nullptr); + + // We have to end a text edit for view shells that are to be taken from + // the shell stack. + ViewShell* pViewShell = dynamic_cast<ViewShell*>(pShell); + if (pViewShell != nullptr) + { + sd::View* pView = pViewShell->GetView(); + if (pView!=nullptr && pView->IsTextEdit()) + { + pView->SdrEndTextEdit(); + pView->UnmarkAll(); + pViewShell->GetViewFrame()->GetDispatcher()->Execute( + SID_OBJECT_SELECT, + SfxCallMode::ASYNCHRON); + } + } + + // Now we can deactivate the shell. + pShell->Deactivate(true); +} + +void ViewShellManager::Implementation::SetFormShell ( + const ViewShell* pFormShellParent, + FmFormShell* pFormShell, + bool bFormShellAboveParent) +{ + ::osl::MutexGuard aGuard (maMutex); + + mpFormShellParent = pFormShellParent; + mpFormShell = pFormShell; + mbFormShellAboveParent = bFormShellAboveParent; +} + +namespace { + +ShellDescriptor::ShellDescriptor() + : mpShell(nullptr), + mnId(ToolbarId::None), + mbIsListenerAddedToWindow(false) +{ +} + +ShellDescriptor::ShellDescriptor ( + ShellId nId) + : mpShell(nullptr), + mnId(nId), + mbIsListenerAddedToWindow(false) +{ +} + +vcl::Window* ShellDescriptor::GetWindow() const +{ + ViewShell* pViewShell = dynamic_cast<ViewShell*>(mpShell); + if (pViewShell != nullptr) + return pViewShell->GetActiveWindow(); + else + return nullptr; +} + +} // end of anonymous namespace + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/ViewTabBar.cxx b/sd/source/ui/view/ViewTabBar.cxx new file mode 100644 index 000000000..18a408e83 --- /dev/null +++ b/sd/source/ui/view/ViewTabBar.cxx @@ -0,0 +1,561 @@ +/* -*- 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 <ViewTabBar.hxx> + +#include <ViewShellBase.hxx> +#include <framework/FrameworkHelper.hxx> +#include <framework/Pane.hxx> +#include <DrawController.hxx> + +#include <Client.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> + +#include <sfx2/viewfrm.hxx> +#include <com/sun/star/drawing/framework/ResourceId.hpp> +#include <com/sun/star/drawing/framework/XControllerManager.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/drawing/framework/XView.hpp> +#include <comphelper/processfactory.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/servicehelper.hxx> +#include <tools/diagnose_ex.h> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; +using ::sd::framework::FrameworkHelper; + +namespace sd { + +namespace { +bool IsEqual (const TabBarButton& rButton1, const TabBarButton& rButton2) +{ + return ((rButton1.ResourceId.is() + && rButton2.ResourceId.is() + && rButton1.ResourceId->compareTo(rButton2.ResourceId) == 0) + || rButton1.ButtonLabel == rButton2.ButtonLabel); +} + +} // end of anonymous namespace + +ViewTabBar::ViewTabBar ( + const Reference<XResourceId>& rxViewTabBarId, + const Reference<frame::XController>& rxController) + : mpTabControl(VclPtr<TabBarControl>::Create(GetAnchorWindow(rxViewTabBarId,rxController), this)), + mxController(rxController), + mxViewTabBarId(rxViewTabBarId), + mpViewShellBase(nullptr), + mnNoteBookWidthPadding(0) +{ + // Tunnel through the controller and use the ViewShellBase to obtain the + // view frame. + try + { + Reference<lang::XUnoTunnel> xTunnel (mxController, UNO_QUERY_THROW); + if (auto pController = comphelper::getFromUnoTunnel<DrawController>(xTunnel)) + mpViewShellBase = pController->GetViewShellBase(); + } + catch (const RuntimeException&) + { + } + + // Register as listener at XConfigurationController. + Reference<XControllerManager> xControllerManager (mxController, UNO_QUERY); + if (xControllerManager.is()) + { + mxConfigurationController = xControllerManager->getConfigurationController(); + if (mxConfigurationController.is()) + { + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceActivationEvent, + Any()); + } + } + + mpTabControl->Show(); + + if (mpViewShellBase != nullptr + && rxViewTabBarId->isBoundToURL( + FrameworkHelper::msCenterPaneURL, AnchorBindingMode_DIRECT)) + { + mpViewShellBase->SetViewTabBar(this); + } +} + +ViewTabBar::~ViewTabBar() +{ +} + +void ViewTabBar::disposing(std::unique_lock<std::mutex>&) +{ + if (mpViewShellBase != nullptr + && mxViewTabBarId->isBoundToURL( + FrameworkHelper::msCenterPaneURL, AnchorBindingMode_DIRECT)) + { + mpViewShellBase->SetViewTabBar(nullptr); + } + + if (mxConfigurationController.is()) + { + // Unregister listener from XConfigurationController. + try + { + mxConfigurationController->removeConfigurationChangeListener(this); + } + catch (const lang::DisposedException&) + { + // Receiving a disposed exception is the normal case. Is there + // a way to avoid it? + } + mxConfigurationController = nullptr; + } + + { + const SolarMutexGuard aSolarGuard; + mpTabControl.disposeAndClear(); + } + + mxController = nullptr; +} + +vcl::Window* ViewTabBar::GetAnchorWindow( + const Reference<XResourceId>& rxViewTabBarId, + const Reference<frame::XController>& rxController) +{ + vcl::Window* pWindow = nullptr; + ViewShellBase* pBase = nullptr; + + // Tunnel through the controller and use the ViewShellBase to obtain the + // view frame. + try + { + Reference<lang::XUnoTunnel> xTunnel (rxController, UNO_QUERY_THROW); + if (auto pController = comphelper::getFromUnoTunnel<DrawController>(xTunnel)) + pBase = pController->GetViewShellBase(); + } + catch (const RuntimeException&) + { + } + + // The ViewTabBar supports at the moment only the center pane. + if (rxViewTabBarId.is() + && rxViewTabBarId->isBoundToURL( + FrameworkHelper::msCenterPaneURL, AnchorBindingMode_DIRECT)) + { + if (pBase != nullptr && pBase->GetViewFrame() != nullptr) + pWindow = &pBase->GetViewFrame()->GetWindow(); + } + + // The rest is (at the moment) just for the emergency case. + if (pWindow == nullptr) + { + Reference<XPane> xPane; + try + { + Reference<XControllerManager> xControllerManager (rxController, UNO_QUERY_THROW); + Reference<XConfigurationController> xCC ( + xControllerManager->getConfigurationController()); + if (xCC.is()) + xPane.set(xCC->getResource(rxViewTabBarId->getAnchor()), UNO_QUERY); + } + catch (const RuntimeException&) + { + } + + // Tunnel through the XWindow to the VCL side. + try + { + Reference<lang::XUnoTunnel> xTunnel (xPane, UNO_QUERY_THROW); + if (auto pPane = comphelper::getFromUnoTunnel<framework::Pane>(xTunnel)) + pWindow = pPane->GetWindow()->GetParent(); + } + catch (const RuntimeException&) + { + } + } + + return pWindow; +} + +//----- XConfigurationChangeListener ------------------------------------------ + +void SAL_CALL ViewTabBar::notifyConfigurationChange ( + const ConfigurationChangeEvent& rEvent) +{ + if (rEvent.Type == FrameworkHelper::msResourceActivationEvent + && rEvent.ResourceId->getResourceURL().match(FrameworkHelper::msViewURLPrefix) + && rEvent.ResourceId->isBoundTo(mxViewTabBarId->getAnchor(), AnchorBindingMode_DIRECT)) + { + UpdateActiveButton(); + } +} + +//----- XEventListener -------------------------------------------------------- + +void SAL_CALL ViewTabBar::disposing( + const lang::EventObject& rEvent) +{ + if (rEvent.Source == mxConfigurationController) + { + mxConfigurationController = nullptr; + mxController = nullptr; + } +} + +//----- XTabBar --------------------------------------------------------------- + +void SAL_CALL ViewTabBar::addTabBarButtonAfter ( + const TabBarButton& rButton, + const TabBarButton& rAnchor) +{ + const SolarMutexGuard aSolarGuard; + AddTabBarButton(rButton, rAnchor); +} + +void SAL_CALL ViewTabBar::appendTabBarButton (const TabBarButton& rButton) +{ + const SolarMutexGuard aSolarGuard; + AddTabBarButton(rButton); +} + +void SAL_CALL ViewTabBar::removeTabBarButton (const TabBarButton& rButton) +{ + const SolarMutexGuard aSolarGuard; + RemoveTabBarButton(rButton); +} + +sal_Bool SAL_CALL ViewTabBar::hasTabBarButton (const TabBarButton& rButton) +{ + const SolarMutexGuard aSolarGuard; + return HasTabBarButton(rButton); +} + +Sequence<TabBarButton> SAL_CALL ViewTabBar::getTabBarButtons() +{ + const SolarMutexGuard aSolarGuard; + return GetTabBarButtons(); +} + +//----- XResource ------------------------------------------------------------- + +Reference<XResourceId> SAL_CALL ViewTabBar::getResourceId() +{ + return mxViewTabBarId; +} + +sal_Bool SAL_CALL ViewTabBar::isAnchorOnly() +{ + return false; +} + +//----- XUnoTunnel ------------------------------------------------------------ + +const Sequence<sal_Int8>& ViewTabBar::getUnoTunnelId() +{ + static const comphelper::UnoIdInit theViewTabBarUnoTunnelId; + return theViewTabBarUnoTunnelId.getSeq(); +} + +sal_Int64 SAL_CALL ViewTabBar::getSomething (const Sequence<sal_Int8>& rId) +{ + return comphelper::getSomethingImpl(rId, this); +} + +bool ViewTabBar::ActivatePage(size_t nIndex) +{ + try + { + Reference<XControllerManager> xControllerManager (mxController,UNO_QUERY_THROW); + Reference<XConfigurationController> xConfigurationController ( + xControllerManager->getConfigurationController()); + if ( ! xConfigurationController.is()) + throw RuntimeException(); + Reference<XView> xView; + try + { + xView.set(xConfigurationController->getResource( + ResourceId::create( + ::comphelper::getProcessComponentContext(), + FrameworkHelper::msCenterPaneURL)), + UNO_QUERY); + } + catch (const DeploymentException&) + { + } + + Client* pIPClient = nullptr; + if (mpViewShellBase != nullptr) + pIPClient = dynamic_cast<Client*>(mpViewShellBase->GetIPClient()); + if (pIPClient==nullptr || ! pIPClient->IsObjectInPlaceActive()) + { + if (nIndex < maTabBarButtons.size()) + { + xConfigurationController->requestResourceActivation( + maTabBarButtons[nIndex].ResourceId, + ResourceActivationMode_REPLACE); + } + + return true; + } + } + catch (const RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd.view"); + } + + return false; +} + +int ViewTabBar::GetHeight() const +{ + int nHeight (0); + + if (!maTabBarButtons.empty()) + { + if (mpTabControl->IsReallyVisible()) + { + weld::Notebook& rNotebook = mpTabControl->GetNotebook(); + int nAllocatedWidth = mpTabControl->GetAllocatedWidth(); + int nPageWidth = nAllocatedWidth - mnNoteBookWidthPadding; + + // set each page width-request to the size it takes to fit the notebook allocation + for (int nIndex = 1, nPageCount = rNotebook.get_n_pages(); nIndex <= nPageCount; ++nIndex) + { + OString sIdent(OString::number(nIndex)); + weld::Container* pContainer = rNotebook.get_page(sIdent); + pContainer->set_size_request(nPageWidth, -1); + } + + // get the height-for-width for this allocation + nHeight = mpTabControl->get_preferred_size().Height(); + } + + if (nHeight <= 0) + { + // Using a default when the real height can not be determined. + // To get correct height this method should be called when the + // control is visible. + nHeight = 21; + } + } + + return nHeight; +} + +void ViewTabBar::AddTabBarButton ( + const css::drawing::framework::TabBarButton& rButton, + const css::drawing::framework::TabBarButton& rAnchor) +{ + TabBarButtonList::size_type nIndex; + + if ( ! rAnchor.ResourceId.is() + || (rAnchor.ResourceId->getResourceURL().isEmpty() + && rAnchor.ButtonLabel.isEmpty())) + { + nIndex = 0; + } + else + { + for (nIndex=0; nIndex<maTabBarButtons.size(); ++nIndex) + { + if (IsEqual(maTabBarButtons[nIndex], rAnchor)) + { + ++nIndex; + break; + } + } + } + + AddTabBarButton(rButton,nIndex); +} + +void ViewTabBar::AddTabBarButton ( + const css::drawing::framework::TabBarButton& rButton) +{ + AddTabBarButton(rButton, maTabBarButtons.size()); +} + +void ViewTabBar::AddTabBarButton ( + const css::drawing::framework::TabBarButton& rButton, + sal_Int32 nPosition) +{ + if (nPosition >= 0 && + nPosition <= mpTabControl->GetNotebook().get_n_pages()) + { + // Insert the button into our local array. + maTabBarButtons.insert(maTabBarButtons.begin() + nPosition, rButton); + UpdateTabBarButtons(); + UpdateActiveButton(); + } +} + +void ViewTabBar::RemoveTabBarButton ( + const css::drawing::framework::TabBarButton& rButton) +{ + for (TabBarButtonList::size_type nIndex=0; nIndex<maTabBarButtons.size(); ++nIndex) + { + if (IsEqual(maTabBarButtons[nIndex], rButton)) + { + maTabBarButtons.erase(maTabBarButtons.begin()+nIndex); + UpdateTabBarButtons(); + UpdateActiveButton(); + break; + } + } +} + +bool ViewTabBar::HasTabBarButton ( + const css::drawing::framework::TabBarButton& rButton) +{ + bool bResult (false); + + for (const css::drawing::framework::TabBarButton & r : maTabBarButtons) + { + if (IsEqual(r, rButton)) + { + bResult = true; + break; + } + } + + return bResult; +} + +css::uno::Sequence<css::drawing::framework::TabBarButton> + ViewTabBar::GetTabBarButtons() +{ + return comphelper::containerToSequence(maTabBarButtons); +} + +void ViewTabBar::UpdateActiveButton() +{ + Reference<XView> xView; + if (mpViewShellBase != nullptr) + xView = FrameworkHelper::Instance(*mpViewShellBase)->GetView( + mxViewTabBarId->getAnchor()); + if (!xView.is()) + return; + + Reference<XResourceId> xViewId (xView->getResourceId()); + for (size_t nIndex=0; nIndex<maTabBarButtons.size(); ++nIndex) + { + if (maTabBarButtons[nIndex].ResourceId->compareTo(xViewId) == 0) + { + mpTabControl->GetNotebook().set_current_page(nIndex); + break; + } + } +} + +void ViewTabBar::UpdateTabBarButtons() +{ + int nMaxPageWidthReq(0); + + weld::Notebook& rNotebook = mpTabControl->GetNotebook(); + int nPageCount(rNotebook.get_n_pages()); + int nIndex = 1; + for (const auto& rTab : maTabBarButtons) + { + OString sIdent(OString::number(nIndex)); + // Create a new tab when there are not enough. + if (nPageCount < nIndex) + rNotebook.append_page(sIdent, rTab.ButtonLabel); + else + { + // Update the tab. + rNotebook.set_tab_label_text(sIdent, rTab.ButtonLabel); + } + + // Set a fairly arbitrary initial width request for the pages so we can + // measure what extra width the notebook itself uses + weld::Container* pContainer = rNotebook.get_page(sIdent); + int nTextWidth = pContainer->get_pixel_size(rTab.ButtonLabel).Width(); + pContainer->set_size_request(nTextWidth, -1); + nMaxPageWidthReq = std::max(nMaxPageWidthReq, nTextWidth); + + ++nIndex; + } + + // Delete tabs that are no longer used. + for (; nIndex<=nPageCount; ++nIndex) + rNotebook.remove_page(OString::number(nIndex)); + + int nWidthReq = rNotebook.get_preferred_size().Width(); + // The excess width over the page request that the notebook uses we will + // use this later to help measure the best height-for-width given the + // eventual allocated width of the notebook + mnNoteBookWidthPadding = nWidthReq - nMaxPageWidthReq; +} + +//===== TabBarControl ========================================================= + +TabBarControl::TabBarControl ( + vcl::Window* pParentWindow, + const ::rtl::Reference<ViewTabBar>& rpViewTabBar) + : InterimItemWindow(pParentWindow, "modules/simpress/ui/tabviewbar.ui", "TabViewBar") + , mxTabControl(m_xBuilder->weld_notebook("tabcontrol")) + , mpViewTabBar(rpViewTabBar) + , mnAllocatedWidth(0) +{ + // Because the actual window background is transparent--to avoid + // flickering due to multiple background paintings by this and by child + // windows--we have to paint the background for this control explicitly: + // the actual control is not painted over its whole bounding box. + SetPaintTransparent(false); + SetBackground(Application::GetSettings().GetStyleSettings().GetDialogColor()); + + InitControlBase(mxTabControl.get()); + + mxTabControl->connect_enter_page(LINK(this, TabBarControl, ActivatePageHdl)); + mxTabControl->connect_size_allocate(LINK(this, TabBarControl, NotebookSizeAllocHdl)); +} + +void TabBarControl::dispose() +{ + mxTabControl.reset(); + InterimItemWindow::dispose(); +} + +TabBarControl::~TabBarControl() +{ + disposeOnce(); +} + +IMPL_LINK(TabBarControl, NotebookSizeAllocHdl, const Size&, rSize, void) +{ + mnAllocatedWidth = rSize.Width(); +} + +IMPL_LINK(TabBarControl, ActivatePageHdl, const OString&, rPage, void) +{ + if (!mpViewTabBar->ActivatePage(mxTabControl->get_page_index(rPage))) + { + // When we run into this else branch then we have an active OLE + // object. We ignore the request to switch views. Additionally + // we put the active tab back to the one for the current view. + mpViewTabBar->UpdateActiveButton(); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/WindowUpdater.cxx b/sd/source/ui/view/WindowUpdater.cxx new file mode 100644 index 000000000..c3f1bb53e --- /dev/null +++ b/sd/source/ui/view/WindowUpdater.cxx @@ -0,0 +1,131 @@ +/* -*- 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 <WindowUpdater.hxx> +#include <drawdoc.hxx> + +#include <vcl/outdev.hxx> +#include <vcl/vclptr.hxx> +#include <vcl/window.hxx> + +#include <algorithm> + +namespace sd { + +WindowUpdater::WindowUpdater() + : mpDocument (nullptr) +{ + maCTLOptions.AddListener(this); +} + +WindowUpdater::~WindowUpdater() noexcept +{ + maCTLOptions.RemoveListener(this); +} + +void WindowUpdater::RegisterWindow (vcl::Window* pWindow) +{ + if (pWindow != nullptr) + { + tWindowList::iterator aWindowIterator ( + ::std::find ( + maWindowList.begin(), maWindowList.end(), pWindow)); + if (aWindowIterator == maWindowList.end()) + { + // Update the device once right now and add it to the list. + Update (pWindow->GetOutDev()); + maWindowList.emplace_back(pWindow); + } + } +} + +void WindowUpdater::UnregisterWindow (vcl::Window* pWindow) +{ + tWindowList::iterator aWindowIterator ( + ::std::find ( + maWindowList.begin(), maWindowList.end(), pWindow)); + if (aWindowIterator != maWindowList.end()) + { + maWindowList.erase (aWindowIterator); + } +} + +void WindowUpdater::SetDocument (SdDrawDocument* pDocument) +{ + mpDocument = pDocument; +} + +void WindowUpdater::Update ( + OutputDevice* pDevice) const +{ + if (pDevice != nullptr) + { + UpdateWindow (pDevice); + } +} + +void WindowUpdater::UpdateWindow (OutputDevice* pDevice) const +{ + if (pDevice == nullptr) + return; + + SvtCTLOptions::TextNumerals aNumeralMode (maCTLOptions.GetCTLTextNumerals()); + + LanguageType aLanguage; + // Now this is a bit confusing. The numerals in arabic languages + // are Hindi numerals and what the western world generally uses are + // arabic numerals. The digits used in the Hindi language are not + // used at all. + switch (aNumeralMode) + { + case SvtCTLOptions::NUMERALS_HINDI: + aLanguage = LANGUAGE_ARABIC_SAUDI_ARABIA; + break; + + case SvtCTLOptions::NUMERALS_SYSTEM: + aLanguage = LANGUAGE_SYSTEM; + break; + + case SvtCTLOptions::NUMERALS_ARABIC: + default: + aLanguage = LANGUAGE_ENGLISH; + break; + } + + pDevice->SetDigitLanguage (aLanguage); +} + +void WindowUpdater::ConfigurationChanged( utl::ConfigurationBroadcaster*, ConfigurationHints ) +{ + // Set the current state at all registered output devices. + for (auto& rxWindow : maWindowList) + Update (rxWindow->GetOutDev()); + + // Reformat the document for the modified state to take effect. + if (mpDocument != nullptr) + mpDocument->ReformatAllTextObjects(); + + // Invalidate the windows to make the modified state visible. + for (auto& rxWindow : maWindowList) + rxWindow->Invalidate(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/clview.cxx b/sd/source/ui/view/clview.cxx new file mode 100644 index 000000000..9f11839da --- /dev/null +++ b/sd/source/ui/view/clview.cxx @@ -0,0 +1,62 @@ +/* -*- 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 <ClientView.hxx> +#include <drawview.hxx> + +namespace sd +{ +class DrawDocShell; +class DrawViewShell; + +/** + * ClientView is used for DrawDocShell::Draw() + */ + +ClientView::ClientView(DrawDocShell* pDocSh, OutputDevice* pOutDev) + : DrawView(pDocSh, pOutDev, nullptr) +{ +} + +ClientView::~ClientView() {} + +/** + * If View should not Invalidate() the windows, this method has + * to be overridden and properly handled. + */ + +void ClientView::InvalidateOneWin(OutputDevice& rWin) +{ + vcl::Region aRegion; + CompleteRedraw(&rWin, aRegion); +} + +/** + * If View should not Invalidate() the windows, this method has + * to be overridden and properly handled. + */ + +void ClientView::InvalidateOneWin(OutputDevice& rWin, const ::tools::Rectangle& rRect) +{ + CompleteRedraw(&rWin, vcl::Region(rRect)); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drawview.cxx b/sd/source/ui/view/drawview.cxx new file mode 100644 index 000000000..e4cdf0107 --- /dev/null +++ b/sd/source/ui/view/drawview.cxx @@ -0,0 +1,634 @@ +/* -*- 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 <vcl/weld.hxx> +#include <svl/style.hxx> +#include <editeng/outliner.hxx> +#include <svx/svdotext.hxx> +#include <svl/poolitem.hxx> +#include <editeng/eeitem.hxx> +#include <svl/whiter.hxx> +#include <sal/log.hxx> +#include <tools/debug.hxx> + +#include <svx/svdundo.hxx> +#include <svx/strings.hrc> +#include <svx/dialmgr.hxx> + +#include <strings.hrc> +#include <View.hxx> +#include <drawview.hxx> +#include <drawdoc.hxx> +#include <DrawDocShell.hxx> +#include <sdpage.hxx> +#include <ViewShellBase.hxx> +#include <DrawViewShell.hxx> +#include <pres.hxx> +#include <sdresid.hxx> +#include <unchss.hxx> +#include <slideshow.hxx> + +#include <undo/undomanager.hxx> + +using namespace ::com::sun::star; + +namespace sd { + + +/** + * Shows the first page of document at position 0,0. In the case + * that there is no page a page is created. + */ + +DrawView::DrawView( + DrawDocShell* pDocSh, + OutputDevice* pOutDev, + DrawViewShell* pShell) +: ::sd::View(*pDocSh->GetDoc(), pOutDev, pShell) + ,mpDocShell(pDocSh) + ,mpDrawViewShell(pShell) + ,mnPOCHSmph(0) +{ + SetCurrentObj(SdrObjKind::Rectangle); +} + +DrawView::~DrawView() +{ +} + +/** + * Virtual method from SdrView, called at selection change. + */ + +void DrawView::MarkListHasChanged() +{ + ::sd::View::MarkListHasChanged(); + + if (mpDrawViewShell) + mpDrawViewShell->SelectionHasChanged(); +} + +/** + * Virtual method from SdrView, called at model change. + */ + +void DrawView::ModelHasChanged() +{ + ::sd::View::ModelHasChanged(); + + // force framer to rerender + SfxStyleSheetBasePool* pSSPool = mrDoc.GetStyleSheetPool(); + pSSPool->Broadcast(SfxStyleSheetPoolHint()); + + if( mpDrawViewShell ) + mpDrawViewShell->ModelHasChanged(); + +} + +/** + * Redirect attributes onto title and outline text and background + * rectangle of a masterpage into templates, otherwise pass on baseclass. + */ + +bool DrawView::SetAttributes(const SfxItemSet& rSet, + bool bReplaceAll, bool bSlide, bool bMaster) +{ + bool bOk = false; + + if (mpDrawViewShell && bMaster) + { + SfxStyleSheetBasePool* pStShPool = mrDoc.GetStyleSheetPool(); + SdPage& rPage = *mpDrawViewShell->getCurrentPage(); + SdrPage& rMasterPage = rPage.TRG_GetMasterPage(); + size_t nObjCount = rMasterPage.GetObjCount(); + for (size_t nObj = 0; nObj < nObjCount; ++nObj) + { + SdrObject* pObject = rMasterPage.GetObj(nObj); + SetMasterAttributes(pObject, rPage, rSet, pStShPool, bOk, bMaster, bSlide); + } + return bOk; + } + if (mpDrawViewShell && bSlide) + { + SfxStyleSheetBasePool* pStShPool = mrDoc.GetStyleSheetPool(); + SdPage& rPage = *mpDrawViewShell->getCurrentPage(); + size_t nObjCount = rPage.GetObjCount(); + for (size_t nObj = 0; nObj < nObjCount; ++nObj) + { + SdrObject* pObject = rPage.GetObj(nObj); + SetMasterAttributes(pObject, rPage, rSet, pStShPool, bOk, bMaster, bSlide); + } + return bOk; + } + + // is there a masterpage edit? + if ( mpDrawViewShell && (mpDrawViewShell->GetEditMode() == EditMode::MasterPage) ) + { + SfxStyleSheetBasePool* pStShPool = mrDoc.GetStyleSheetPool(); + SdPage& rPage = *mpDrawViewShell->getCurrentPage(); + SdrTextObj* pEditObject = GetTextEditObject(); + + if (pEditObject) + { + // Textedit + + SdrInventor nInv = pEditObject->GetObjInventor(); + + if (nInv == SdrInventor::Default) + { + SdrObjKind eObjKind = pEditObject->GetObjIdentifier(); + PresObjKind ePresObjKind = rPage.GetPresObjKind(pEditObject); + + if ( ePresObjKind == PresObjKind::Title || + ePresObjKind == PresObjKind::Notes ) + { + // Presentation object (except outline) + SfxStyleSheet* pSheet = rPage.GetStyleSheetForPresObj( ePresObjKind ); + DBG_ASSERT(pSheet, "StyleSheet not found"); + + SfxItemSet aTempSet( pSheet->GetItemSet() ); + aTempSet.Put( rSet ); + aTempSet.ClearInvalidItems(); + + // Undo-Action + mpDocSh->GetUndoManager()->AddUndoAction( + std::make_unique<StyleSheetUndoAction>(&mrDoc, pSheet, &aTempSet)); + + pSheet->GetItemSet().Put(aTempSet); + pSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); + bOk = true; + } + else if (eObjKind == SdrObjKind::OutlineText) + { + // Presentation object outline + OutlinerView* pOV = GetTextEditOutlinerView(); + ::Outliner* pOutliner = pOV->GetOutliner(); + + pOutliner->SetUpdateLayout(false); + mpDocSh->SetWaitCursor( true ); + + // replace placeholder by template name + OUString aComment(SdResId(STR_UNDO_CHANGE_PRES_OBJECT)); + aComment = aComment.replaceFirst("$", SdResId(STR_PSEUDOSHEET_OUTLINE)); + mpDocSh->GetUndoManager()->EnterListAction( aComment, OUString(), 0, mpDrawViewShell->GetViewShellBase().GetViewShellId() ); + + std::vector<Paragraph*> aSelList; + pOV->CreateSelectionList(aSelList); + + std::vector<Paragraph*>::reverse_iterator iter = aSelList.rbegin(); + Paragraph* pPara = iter != aSelList.rend() ? *iter : nullptr; + + while (pPara) + { + sal_Int32 nParaPos = pOutliner->GetAbsPos( pPara ); + sal_Int16 nDepth = pOutliner->GetDepth( nParaPos ); + OUString aName = rPage.GetLayoutName() + " " + + OUString::number((nDepth <= 0) ? 1 : nDepth + 1); + SfxStyleSheet* pSheet = static_cast<SfxStyleSheet*>(pStShPool->Find(aName, SfxStyleFamily::Page)); + //We have no stylesheet if we access outline level 10 + //in the master preview, there is no true style backing + //that entry + SAL_WARN_IF(!pSheet, "sd", "StyleSheet " << aName << " not found"); + if (pSheet) + { + SfxItemSet aTempSet( pSheet->GetItemSet() ); + aTempSet.Put( rSet ); + aTempSet.ClearInvalidItems(); + + if( nDepth > 0 && aTempSet.GetItemState( EE_PARA_NUMBULLET ) == SfxItemState::SET ) + { + // no SvxNumBulletItem in outline level 1 to 8! + aTempSet.ClearItem( EE_PARA_NUMBULLET ); + } + + // Undo-Action + mpDocSh->GetUndoManager()->AddUndoAction( + std::make_unique<StyleSheetUndoAction>(&mrDoc, pSheet, &aTempSet)); + + pSheet->GetItemSet().Put(aTempSet); + pSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); + + // now also broadcast any child sheets + sal_Int16 nChild; + for( nChild = nDepth + 1; nChild < 9; nChild++ ) + { + OUString aSheetName = rPage.GetLayoutName() + " " + + OUString::number((nChild <= 0) ? 1 : nChild + 1); + SfxStyleSheet* pOutlSheet = static_cast< SfxStyleSheet* >(pStShPool->Find(aSheetName, SfxStyleFamily::Page)); + + if( pOutlSheet ) + pOutlSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); + } + } + + ++iter; + pPara = iter != aSelList.rend() ? *iter : nullptr; + + bool bJumpToLevel1 = false; + if( !pPara && nDepth > 0 && rSet.GetItemState( EE_PARA_NUMBULLET ) == SfxItemState::SET ) + bJumpToLevel1 = true; + + if (bJumpToLevel1) + { + iter = aSelList.rend(); + --iter; + + if (pOutliner->GetDepth(pOutliner->GetAbsPos(*iter)) > 0) + pPara = pOutliner->GetParagraph( 0 ); // Put NumBulletItem in outline level 1 + } + } + + mpDocSh->SetWaitCursor( false ); + pOV->GetOutliner()->SetUpdateLayout(true); + + mpDocSh->GetUndoManager()->LeaveListAction(); + + bOk = true; + } + else + { + bOk = ::sd::View::SetAttributes(rSet, bReplaceAll); + } + } + } + else + { + // Selection + const SdrMarkList& rList = GetMarkedObjectList(); + const size_t nMarkCount = rList.GetMarkCount(); + for (size_t nMark = 0; nMark < nMarkCount; ++nMark) + { + SdrObject* pObject = rList.GetMark(nMark)->GetMarkedSdrObj(); + SetMasterAttributes(pObject, rPage, rSet, pStShPool, bOk, bMaster, bSlide); + } + + if(!bOk) + bOk = ::sd::View::SetAttributes(rSet, bReplaceAll); + } + } + else // not at masterpage + { + bOk = ::sd::View::SetAttributes(rSet, bReplaceAll); + } + + return bOk; +} + +void DrawView::SetMasterAttributes( SdrObject* pObject, const SdPage& rPage, SfxItemSet rSet, SfxStyleSheetBasePool* pStShPool, bool& bOk, bool bMaster, bool bSlide ) +{ + SdrInventor nInv = pObject->GetObjInventor(); + + if (nInv != SdrInventor::Default) + return; + + SdrObjKind eObjKind = pObject->GetObjIdentifier(); + PresObjKind ePresObjKind = rPage.GetPresObjKind(pObject); + if (bSlide && eObjKind == SdrObjKind::Text) + { + // Presentation object (except outline) + SfxStyleSheet* pSheet = rPage.GetTextStyleSheetForObject(pObject); + DBG_ASSERT(pSheet, "StyleSheet not found"); + + SfxItemSet aTempSet( pSheet->GetItemSet() ); + aTempSet.Put( rSet ); + aTempSet.ClearInvalidItems(); + + // Undo-Action + mpDocSh->GetUndoManager()->AddUndoAction( + std::make_unique<StyleSheetUndoAction>(&mrDoc, pSheet, &aTempSet)); + + pSheet->GetItemSet().Put(aTempSet,false); + pSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); + bOk = true; + } + + if (!bSlide && + (ePresObjKind == PresObjKind::Title || + ePresObjKind == PresObjKind::Notes)) + { + // Presentation object (except outline) + SfxStyleSheet* pSheet = rPage.GetStyleSheetForPresObj( ePresObjKind ); + DBG_ASSERT(pSheet, "StyleSheet not found"); + + SfxItemSet aTempSet( pSheet->GetItemSet() ); + aTempSet.Put( rSet ); + aTempSet.ClearInvalidItems(); + + // Undo-Action + mpDocSh->GetUndoManager()->AddUndoAction( + std::make_unique<StyleSheetUndoAction>(&mrDoc, pSheet, &aTempSet)); + + pSheet->GetItemSet().Put(aTempSet,false); + pSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); + bOk = true; + } + else if (eObjKind == SdrObjKind::OutlineText) + { + // tdf#127900: do not forget to apply master style to placeholders + if (!rSet.HasItem(EE_PARA_NUMBULLET) || bMaster) + { + // Presentation object outline + for (sal_uInt16 nLevel = 9; nLevel > 0; nLevel--) + { + OUString aName = rPage.GetLayoutName() + " " + + OUString::number(nLevel); + SfxStyleSheet* pSheet = static_cast<SfxStyleSheet*>(pStShPool-> + Find(aName, SfxStyleFamily::Page)); + DBG_ASSERT(pSheet, "StyleSheet not found"); + + SfxItemSet aTempSet( pSheet->GetItemSet() ); + + if( nLevel > 1 ) + { + // for all levels over 1, clear all items that will be + // hard set to level 1 + SfxWhichIter aWhichIter(rSet); + sal_uInt16 nWhich(aWhichIter.FirstWhich()); + while( nWhich ) + { + if( SfxItemState::SET == aWhichIter.GetItemState() ) + aTempSet.ClearItem( nWhich ); + nWhich = aWhichIter.NextWhich(); + } + + } + else + { + // put the items hard into level one + aTempSet.Put( rSet ); + } + + aTempSet.ClearInvalidItems(); + + // Undo-Action + mpDocSh->GetUndoManager()->AddUndoAction( + std::make_unique<StyleSheetUndoAction>(&mrDoc, pSheet, &aTempSet)); + + pSheet->GetItemSet().Set(aTempSet,false); + pSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); + } + + // remove all hard set items from shape that are now set in style + SfxWhichIter aWhichIter(rSet); + sal_uInt16 nWhich(aWhichIter.FirstWhich()); + while( nWhich ) + { + if( SfxItemState::SET == aWhichIter.GetItemState() ) + pObject->ClearMergedItem( nWhich ); + nWhich = aWhichIter.NextWhich(); + } + } + else + pObject->SetMergedItemSet(rSet); + + bOk = true; + } +} + +/** + * Notify for change of site arrangement + */ + +void DrawView::Notify(SfxBroadcaster& rBC, const SfxHint& rHint) +{ + if ( mpDrawViewShell && rHint.GetId() == SfxHintId::ThisIsAnSdrHint ) + { + SdrHintKind eHintKind = static_cast<const SdrHint&>(rHint).GetKind(); + + if ( mnPOCHSmph == 0 && eHintKind == SdrHintKind::PageOrderChange ) + { + mpDrawViewShell->ResetActualPage(); + } + else if ( eHintKind == SdrHintKind::LayerChange || eHintKind == SdrHintKind::LayerOrderChange ) + { + mpDrawViewShell->ResetActualLayer(); + } + + // switch to that page when it's not a master page + if(SdrHintKind::SwitchToPage == eHintKind) + { + // We switch page only in the current view, which triggered this event + // and keep other views untouched. + SfxViewShell* pViewShell = SfxViewShell::Current(); + if(pViewShell && pViewShell != &mpDrawViewShell->GetViewShellBase()) + return; + + const SdrPage* pPage = static_cast<const SdrHint&>(rHint).GetPage(); + if(pPage && !pPage->IsMasterPage()) + { + if(mpDrawViewShell->GetActualPage() != pPage) + { + sal_uInt16 nPageNum = (pPage->GetPageNum() - 1) / 2; // Sdr --> Sd + mpDrawViewShell->SwitchPage(nPageNum); + } + } + } + } + + ::sd::View::Notify(rBC, rHint); +} + +/** + * Lock/Unlock PageOrderChangedHint + */ + +void DrawView::BlockPageOrderChangedHint(bool bBlock) +{ + if (bBlock) + mnPOCHSmph++; + else + { + DBG_ASSERT(mnPOCHSmph, "counter overflow"); + mnPOCHSmph--; + } +} + +/** + * If presentation objects are selected, intercept stylesheet-positioning at + * masterpage. + */ + +bool DrawView::SetStyleSheet(SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr) +{ + bool bResult = true; + + // is there a masterpage edit? + if (mpDrawViewShell && mpDrawViewShell->GetEditMode() == EditMode::MasterPage) + { + if (IsPresObjSelected(false)) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(mpDrawViewShell->GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + bResult = false; + } + else + { + bResult = ::sd::View::SetStyleSheet(pStyleSheet, bDontRemoveHardAttr); + } + } + else + { + bResult = ::sd::View::SetStyleSheet(pStyleSheet, bDontRemoveHardAttr); + } + return bResult; +} + +/** + * Paint-method: Redirect event to the view + */ + +void DrawView::CompleteRedraw(OutputDevice* pOutDev, const vcl::Region& rReg, sdr::contact::ViewObjectContactRedirector* pRedirector /*=0*/) +{ + bool bStandardPaint = true; + + SdDrawDocument* pDoc = mpDocShell->GetDoc(); + if( pDoc && pDoc->GetDocumentType() == DocumentType::Impress) + { + rtl::Reference< sd::SlideShow > xSlideshow( SlideShow::GetSlideShow( pDoc ) ); + if(xSlideshow.is() && xSlideshow->isRunning()) + { + OutputDevice* pShowWindow = xSlideshow->getShowWindow(); + if( (pShowWindow == pOutDev) || (xSlideshow->getAnimationMode() == ANIMATIONMODE_PREVIEW) ) + { + if( pShowWindow == pOutDev && mpViewSh ) + xSlideshow->paint(); + bStandardPaint = false; + } + } + } + + if(bStandardPaint) + { + ::sd::View::CompleteRedraw(pOutDev, rReg, pRedirector); + } +} + +/** + * Make passed region visible (scrolling if necessary) + */ + +void DrawView::MakeVisible(const ::tools::Rectangle& rRect, vcl::Window& rWin) +{ + if (!rRect.IsEmpty() && mpDrawViewShell) + { + mpDrawViewShell->MakeVisible(rRect, rWin); + } +} + +/** + * Hide page. + */ + +void DrawView::HideSdrPage() +{ + if (mpDrawViewShell) + { + mpDrawViewShell->HidePage(); + } + + ::sd::View::HideSdrPage(); +} + +void DrawView::DeleteMarked() +{ + sd::UndoManager* pUndoManager = mrDoc.GetUndoManager(); + DBG_ASSERT( pUndoManager, "sd::DrawView::DeleteMarked(), ui action without undo manager!?" ); + + if( pUndoManager ) + { + OUString aUndo(SvxResId(STR_EditDelete)); + aUndo = aUndo.replaceFirst("%1", GetDescriptionOfMarkedObjects()); + ViewShellId nViewShellId = mpDrawViewShell ? mpDrawViewShell->GetViewShellBase().GetViewShellId() : ViewShellId(-1); + pUndoManager->EnterListAction(aUndo, aUndo, 0, nViewShellId); + } + + SdPage* pPage = nullptr; + bool bResetLayout = false; + + const size_t nMarkCount = GetMarkedObjectList().GetMarkCount(); + if( nMarkCount ) + { + SdrMarkList aList( GetMarkedObjectList() ); + for (size_t nMark = 0; nMark < nMarkCount; ++nMark) + { + SdrObject* pObj = aList.GetMark(nMark)->GetMarkedSdrObj(); + if( pObj && !pObj->IsEmptyPresObj() && pObj->GetUserCall() ) + { + pPage = static_cast< SdPage* >( pObj->getSdrPageFromSdrObject() ); + if (pPage) + { + PresObjKind ePresObjKind(pPage->GetPresObjKind(pObj)); + switch( ePresObjKind ) + { + case PresObjKind::NONE: + continue; // ignore it + case PresObjKind::Graphic: + case PresObjKind::Object: + case PresObjKind::Chart: + case PresObjKind::OrgChart: + case PresObjKind::Table: + case PresObjKind::Calc: + case PresObjKind::Media: + ePresObjKind = PresObjKind::Outline; + break; + default: + break; + } + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( pObj ); + bool bVertical = pTextObj && pTextObj->IsVerticalWriting(); + ::tools::Rectangle aRect( pObj->GetLogicRect() ); + SdrObject* pNewObj = pPage->InsertAutoLayoutShape( nullptr, ePresObjKind, bVertical, aRect, true ); + + // pUndoManager should not be NULL (see assert above) + // but since we have defensive code + // for it earlier and later in the function + // we might as well be consistent + if(pUndoManager) + { + // Move the new PresObj to the position before the + // object it will replace. + pUndoManager->AddUndoAction( + mrDoc.GetSdrUndoFactory().CreateUndoObjectOrdNum( + *pNewObj, + pNewObj->GetOrdNum(), + pObj->GetOrdNum())); + } + pPage->SetObjectOrdNum( pNewObj->GetOrdNum(), pObj->GetOrdNum() ); + + bResetLayout = true; + } + } + } + } + + ::sd::View::DeleteMarked(); + + if( pPage && bResetLayout ) + pPage->SetAutoLayout( pPage->GetAutoLayout() ); + + if( pUndoManager ) + pUndoManager->LeaveListAction(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drbezob.cxx b/sd/source/ui/view/drbezob.cxx new file mode 100644 index 000000000..c84489042 --- /dev/null +++ b/sd/source/ui/view/drbezob.cxx @@ -0,0 +1,320 @@ +/* -*- 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 <BezierObjectBar.hxx> +#include <sfx2/msg.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/objface.hxx> + +#include <svx/svxids.hrc> +#include <svl/eitem.hxx> +#include <sfx2/request.hxx> +#include <svx/svdopath.hxx> +#include <svx/svdundo.hxx> +#include <sfx2/dispatch.hxx> + +#include <sdresid.hxx> + + +#include <strings.hrc> + +#include <DrawDocShell.hxx> +#include <ViewShell.hxx> +#include <drawdoc.hxx> +#include <fusel.hxx> +#include <fuconbez.hxx> + +using namespace sd; +#define ShellClass_BezierObjectBar +#include <sdslots.hxx> + +namespace sd { + +/** + * Declare default interface (Slotmap must not be empty) + */ +SFX_IMPL_INTERFACE(BezierObjectBar, ::SfxShell) + +void BezierObjectBar::InitInterface_Impl() +{ +} + + +BezierObjectBar::BezierObjectBar( + ViewShell* pSdViewShell, + ::sd::View* pSdView) + : SfxShell(pSdViewShell->GetViewShell()), + mpView(pSdView), + mpViewSh(pSdViewShell) +{ + DrawDocShell* pDocShell = mpViewSh->GetDocSh(); + SetPool(&pDocShell->GetPool()); + SetUndoManager(pDocShell->GetUndoManager()); + SetRepeatTarget(mpView); +} + +BezierObjectBar::~BezierObjectBar() +{ + SetRepeatTarget(nullptr); +} + +/** + * Status of attribute items. + */ + +void BezierObjectBar::GetAttrState(SfxItemSet& rSet) +{ + SfxItemSet aAttrSet( mpView->GetDoc().GetPool() ); + mpView->GetAttributes( aAttrSet ); + rSet.Put(aAttrSet, false); // <- sal_False, so DontCare-Status gets acquired + + rtl::Reference<FuPoor> xFunc( mpViewSh->GetCurrentFunction() ); + + if(xFunc.is()) + { + if( auto pFuSelection = dynamic_cast< const FuSelection *>( xFunc.get() )) + { + sal_uInt16 nEditMode = pFuSelection->GetEditMode(); + rSet.Put(SfxBoolItem(nEditMode, true)); + } + else if( auto pFuPolygon = dynamic_cast< const FuConstructBezierPolygon *>( xFunc.get() )) + { + sal_uInt16 nEditMode = pFuPolygon->GetEditMode(); + rSet.Put(SfxBoolItem(nEditMode, true)); + } + } + + if(!mpView->IsMoveAllowed() || !mpView->IsResizeAllowed()) + { + // #i77187# if object is move and/or size protected, do not allow point editing at all + rSet.DisableItem(SID_BEZIER_MOVE); + rSet.DisableItem(SID_BEZIER_INSERT); + + rSet.DisableItem(SID_BEZIER_DELETE); + rSet.DisableItem(SID_BEZIER_CUTLINE); + rSet.DisableItem(SID_BEZIER_CONVERT); + + rSet.DisableItem(SID_BEZIER_EDGE); + rSet.DisableItem(SID_BEZIER_SMOOTH); + rSet.DisableItem(SID_BEZIER_SYMMTR); + + rSet.DisableItem(SID_BEZIER_CLOSE); + + rSet.DisableItem(SID_BEZIER_ELIMINATE_POINTS); + } + else + { + IPolyPolygonEditorController* pIPPEC = nullptr; + if( mpView->GetMarkedObjectList().GetMarkCount() ) + pIPPEC = mpView; + else + pIPPEC = dynamic_cast< IPolyPolygonEditorController* >( mpView->getSmartTags().getSelected().get() ); + + if ( !pIPPEC || !pIPPEC->IsRipUpAtMarkedPointsPossible()) + { + rSet.DisableItem(SID_BEZIER_CUTLINE); + } + if (!pIPPEC || !pIPPEC->IsDeleteMarkedPointsPossible()) + { + rSet.DisableItem(SID_BEZIER_DELETE); + } + if (!pIPPEC || !pIPPEC->IsSetMarkedSegmentsKindPossible()) + { + rSet.DisableItem(SID_BEZIER_CONVERT); + } + else + { + SdrPathSegmentKind eSegm = pIPPEC->GetMarkedSegmentsKind(); + switch (eSegm) + { + case SdrPathSegmentKind::DontCare: rSet.InvalidateItem(SID_BEZIER_CONVERT); break; + case SdrPathSegmentKind::Line : rSet.Put(SfxBoolItem(SID_BEZIER_CONVERT,false)); break; // Button down = curve + case SdrPathSegmentKind::Curve : rSet.Put(SfxBoolItem(SID_BEZIER_CONVERT,true)); break; + default: break; + } + } + if (!pIPPEC || !pIPPEC->IsSetMarkedPointsSmoothPossible()) + { + rSet.DisableItem(SID_BEZIER_EDGE); + rSet.DisableItem(SID_BEZIER_SMOOTH); + rSet.DisableItem(SID_BEZIER_SYMMTR); + } + else + { + SdrPathSmoothKind eSmooth = pIPPEC->GetMarkedPointsSmooth(); + switch (eSmooth) + { + case SdrPathSmoothKind::DontCare : break; + case SdrPathSmoothKind::Angular : rSet.Put(SfxBoolItem(SID_BEZIER_EDGE, true)); break; + case SdrPathSmoothKind::Asymmetric: rSet.Put(SfxBoolItem(SID_BEZIER_SMOOTH,true)); break; + case SdrPathSmoothKind::Symmetric : rSet.Put(SfxBoolItem(SID_BEZIER_SYMMTR,true)); break; + } + } + if (!pIPPEC || !pIPPEC->IsOpenCloseMarkedObjectsPossible()) + { + rSet.DisableItem(SID_BEZIER_CLOSE); + } + else + { + SdrObjClosedKind eClose = pIPPEC->GetMarkedObjectsClosedState(); + switch (eClose) + { + case SdrObjClosedKind::DontCare: rSet.InvalidateItem(SID_BEZIER_CLOSE); break; + case SdrObjClosedKind::Open : rSet.Put(SfxBoolItem(SID_BEZIER_CLOSE,false)); break; + case SdrObjClosedKind::Closed : rSet.Put(SfxBoolItem(SID_BEZIER_CLOSE,true)); break; + default: break; + } + } + + if(pIPPEC == mpView) + rSet.Put(SfxBoolItem(SID_BEZIER_ELIMINATE_POINTS, mpView->IsEliminatePolyPoints())); + else + rSet.DisableItem( SID_BEZIER_ELIMINATE_POINTS ); // only works for views + } +} + +/** + * Process SfxRequests + */ + +void BezierObjectBar::Execute(SfxRequest& rReq) +{ + sal_uInt16 nSId = rReq.GetSlot(); + + switch (nSId) + { + case SID_BEZIER_CUTLINE: + case SID_BEZIER_CONVERT: + case SID_BEZIER_DELETE: + case SID_BEZIER_EDGE: + case SID_BEZIER_SMOOTH: + case SID_BEZIER_SYMMTR: + case SID_BEZIER_CLOSE: + { + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + IPolyPolygonEditorController* pIPPEC = nullptr; + if( rMarkList.GetMarkCount() ) + pIPPEC = mpView; + else + pIPPEC = dynamic_cast< IPolyPolygonEditorController* >( mpView->getSmartTags().getSelected().get() ); + + if( pIPPEC && !mpView->IsAction()) + { + switch (nSId) + { + case SID_BEZIER_DELETE: + pIPPEC->DeleteMarkedPoints(); + break; + + case SID_BEZIER_CUTLINE: + pIPPEC->RipUpAtMarkedPoints(); + break; + + case SID_BEZIER_CONVERT: + { + pIPPEC->SetMarkedSegmentsKind(SdrPathSegmentKind::Toggle); + break; + } + + case SID_BEZIER_EDGE: + case SID_BEZIER_SMOOTH: + case SID_BEZIER_SYMMTR: + { + SdrPathSmoothKind eKind; + + switch (nSId) + { + default: + case SID_BEZIER_EDGE: eKind = SdrPathSmoothKind::Angular; break; + case SID_BEZIER_SMOOTH: eKind = SdrPathSmoothKind::Asymmetric; break; + case SID_BEZIER_SYMMTR: eKind = SdrPathSmoothKind::Symmetric; break; + } + + pIPPEC->SetMarkedPointsSmooth(eKind); + break; + } + + case SID_BEZIER_CLOSE: + { + SdrPathObj* pPathObj = static_cast<SdrPathObj*>( rMarkList.GetMark(0)->GetMarkedSdrObj() ); + const bool bUndo = mpView->IsUndoEnabled(); + if( bUndo ) + mpView->BegUndo(SdResId(STR_UNDO_BEZCLOSE)); + + mpView->UnmarkAllPoints(); + + if( bUndo ) + mpView->AddUndo(mpView->GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pPathObj)); + + pPathObj->ToggleClosed(); + + if( bUndo ) + mpView->EndUndo(); + break; + } + } + } + + if( (pIPPEC == mpView) && !mpView->AreObjectsMarked() ) + mpViewSh->GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + + rReq.Ignore(); + } + break; + + case SID_BEZIER_ELIMINATE_POINTS: + { + mpView->SetEliminatePolyPoints(!mpView->IsEliminatePolyPoints()); + Invalidate(SID_BEZIER_ELIMINATE_POINTS); + rReq.Done(); + } + break; + + case SID_BEZIER_MOVE: + case SID_BEZIER_INSERT: + { + rtl::Reference<FuPoor> xFunc( mpViewSh->GetCurrentFunction() ); + + if(xFunc.is()) + { + if( auto pFuSelection = dynamic_cast<FuSelection *>( xFunc.get() )) + { + pFuSelection->SetEditMode(rReq.GetSlot()); + } + else if( auto pFuPolygon = dynamic_cast<FuConstructBezierPolygon *>( xFunc.get() )) + { + pFuPolygon->SetEditMode(rReq.GetSlot()); + } + } + + rReq.Ignore (); + } + break; + + default: + break; + } + + Invalidate(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drtxtob.cxx b/sd/source/ui/view/drtxtob.cxx new file mode 100644 index 000000000..b10af0828 --- /dev/null +++ b/sd/source/ui/view/drtxtob.cxx @@ -0,0 +1,625 @@ +/* -*- 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 <TextObjectBar.hxx> + +#include <svx/svxids.hrc> + +#include <com/sun/star/linguistic2/XThesaurus.hpp> + +#include <editeng/eeitem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/lspcitem.hxx> +#include <editeng/adjustitem.hxx> +#include <editeng/editview.hxx> +#include <editeng/outliner.hxx> +#include <editeng/unolingu.hxx> +#include <editeng/kernitem.hxx> +#include <svl/whiter.hxx> +#include <svl/itempool.hxx> +#include <svl/stritem.hxx> +#include <svl/style.hxx> +#include <svl/languageoptions.hxx> +#include <svl/cjkoptions.hxx> +#include <svl/ctloptions.hxx> +#include <sfx2/tplpitem.hxx> +#include <editeng/escapementitem.hxx> +#include <svx/svdoutl.hxx> +#include <editeng/scripttypeitem.hxx> +#include <editeng/writingmodeitem.hxx> +#include <editeng/frmdiritem.hxx> +#include <editeng/fhgtitem.hxx> + +#include <sfx2/objface.hxx> + +#include <drawdoc.hxx> +#include <DrawDocShell.hxx> +#include <DrawViewShell.hxx> +#include <OutlineViewShell.hxx> +#include <Window.hxx> +#include <OutlineView.hxx> + +using namespace sd; +using namespace ::com::sun::star; + +#define ShellClass_TextObjectBar +#include <sdslots.hxx> + +namespace sd { + +/** + * Declare default interface (Slotmap must not be empty, therefore enter + * something that (hopefully) never occurs. + */ +SFX_IMPL_INTERFACE(TextObjectBar, SfxShell) + +void TextObjectBar::InitInterface_Impl() +{ +} + + +TextObjectBar::TextObjectBar ( + ViewShell* pSdViewSh, + SfxItemPool& rItemPool, + ::sd::View* pSdView ) + : SfxShell(pSdViewSh->GetViewShell()), + mpViewShell( pSdViewSh ), + mpView( pSdView ) +{ + SetPool(&rItemPool); + + if( mpView ) + { + OutlineView* pOutlinerView = dynamic_cast< OutlineView* >( mpView ); + if( pOutlinerView ) + { + SetUndoManager(&pOutlinerView->GetOutliner().GetUndoManager()); + } + else + { + DrawDocShell* pDocShell = mpView->GetDoc().GetDocSh(); + if( pDocShell ) + { + SetUndoManager(pDocShell->GetUndoManager()); + DrawViewShell* pDrawViewShell = dynamic_cast< DrawViewShell* >( pSdViewSh ); + if ( pDrawViewShell ) + SetRepeatTarget(pSdView); + } + } + } + + SetName( "TextObjectBar"); + + // SetHelpId( SD_IF_SDDRAWTEXTOBJECTBAR ); +} + +TextObjectBar::~TextObjectBar() +{ + SetRepeatTarget(nullptr); +} + +void TextObjectBar::GetCharState( SfxItemSet& rSet ) +{ + SfxItemSet aCharAttrSet( mpView->GetDoc().GetPool() ); + mpView->GetAttributes( aCharAttrSet ); + + SfxItemSetFixed<EE_ITEMS_START,EE_ITEMS_END> aNewAttr( mpViewShell->GetPool() ); + + aNewAttr.Put(aCharAttrSet, false); + rSet.Put(aNewAttr, false); + + SvxKerningItem aKern = aCharAttrSet.Get( EE_CHAR_KERNING ); + //aKern.SetWhich(SID_ATTR_CHAR_KERNING); + rSet.Put(aKern); + + SfxItemState eState = aCharAttrSet.GetItemState( EE_CHAR_KERNING ); + if ( eState == SfxItemState::DONTCARE ) + { + rSet.InvalidateItem(EE_CHAR_KERNING); + } +} + +/** + * Status of attribute items. + */ +void TextObjectBar::GetAttrState( SfxItemSet& rSet ) +{ + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + SfxItemSet aAttrSet( mpView->GetDoc().GetPool() ); + bool bDisableParagraphTextDirection = !SvtCTLOptions().IsCTLFontEnabled(); + bool bDisableVerticalText = !SvtCJKOptions::IsVerticalTextEnabled(); + + mpView->GetAttributes( aAttrSet ); + + while ( nWhich ) + { + sal_uInt16 nSlotId = SfxItemPool::IsWhich(nWhich) + ? GetPool().GetSlotId(nWhich) + : nWhich; + + switch ( nSlotId ) + { + case SID_ATTR_CHAR_FONT: + case SID_ATTR_CHAR_FONTHEIGHT: + case SID_ATTR_CHAR_WEIGHT: + case SID_ATTR_CHAR_POSTURE: + case SID_ATTR_CHAR_SHADOWED: + case SID_ATTR_CHAR_STRIKEOUT: + case SID_ATTR_CHAR_CASEMAP: + { + sal_uInt16 stretchX = 100; + SvxScriptSetItem aSetItem( nSlotId, GetPool() ); + aSetItem.GetItemSet().Put( aAttrSet, false ); + + SvtScriptType nScriptType = mpView->GetScriptType(); + + if( (nSlotId == SID_ATTR_CHAR_FONT) || (nSlotId == SID_ATTR_CHAR_FONTHEIGHT) ) + { + // input language should be preferred over + // current cursor position to detect script type + OutlinerView* pOLV = mpView->GetTextEditOutlinerView(); + SdrOutliner *pOutliner = mpView->GetTextEditOutliner(); + + assert(mpViewShell); + + if (OutlineView* pOView = dynamic_cast<OutlineView*>(mpView)) + pOLV = pOView->GetViewByWindow(mpViewShell->GetActiveWindow()); + + sal_uInt16 stretchY = 100; + if( pOutliner ) + pOutliner->GetGlobalCharStretching( stretchX, stretchY ); + + if(pOLV && !pOLV->GetSelection().HasRange()) + { + if (mpViewShell->GetViewShell() && mpViewShell->GetViewShell()->GetWindow()) + { + LanguageType nInputLang = mpViewShell->GetViewShell()->GetWindow()->GetInputLanguage(); + if(nInputLang != LANGUAGE_DONTKNOW && nInputLang != LANGUAGE_SYSTEM) + nScriptType = SvtLanguageOptions::GetScriptTypeOfLanguage( nInputLang ); + } + } + } + + const SfxPoolItem* pI = aSetItem.GetItemOfScript( nScriptType ); + if( pI ) + { + if( nSlotId == SID_ATTR_CHAR_FONTHEIGHT ) + { + SvxFontHeightItem aFontItem = dynamic_cast<const SvxFontHeightItem&>(*pI); + aFontItem.SetHeight(aFontItem.GetHeight(), stretchX, aFontItem.GetPropUnit()); + aFontItem.SetWhich(nWhich); + aAttrSet.Put( aFontItem ); + } + else + { + aAttrSet.Put( pI->CloneSetWhich(nWhich) ); + } + } + else + { + aAttrSet.InvalidateItem( nWhich ); + } + } + break; + + case SID_STYLE_APPLY: + case SID_STYLE_FAMILY2: + { + SfxStyleSheet* pStyleSheet = mpView->GetStyleSheetFromMarked(); + if( pStyleSheet ) + rSet.Put( SfxTemplateItem( nWhich, pStyleSheet->GetName() ) ); + else + { + rSet.Put( SfxTemplateItem( nWhich, OUString() ) ); + } + } + break; + + case SID_OUTLINE_LEFT: + case SID_OUTLINE_RIGHT: + case SID_OUTLINE_UP: + case SID_OUTLINE_DOWN: + { + bool bDisableLeft = true; + bool bDisableRight = true; + bool bDisableUp = true; + bool bDisableDown = true; + + //fdo#78151 it doesn't make sense to promote or demote outline levels in master view. + const DrawViewShell* pDrawViewShell = dynamic_cast< DrawViewShell* >(mpViewShell); + const bool bInMasterView = pDrawViewShell && pDrawViewShell->GetEditMode() == EditMode::MasterPage; + + if (!bInMasterView) + { + OutlinerView* pOLV = mpView->GetTextEditOutlinerView(); + + if (OutlineView* pOView = dynamic_cast<OutlineView*>(mpView)) + pOLV = pOView->GetViewByWindow(mpViewShell->GetActiveWindow()); + + bool bOutlineViewSh = dynamic_cast< const OutlineViewShell *>( mpViewShell ) != nullptr; + + if (pOLV) + { + // Outliner at outline-mode + ::Outliner* pOutl = pOLV->GetOutliner(); + + std::vector<Paragraph*> aSelList; + pOLV->CreateSelectionList(aSelList); + Paragraph* pPara = aSelList.empty() ? nullptr : *(aSelList.begin()); + + // find out if we are an OutlineView + bool bIsOutlineView(OutlinerMode::OutlineView == pOLV->GetOutliner()->GetOutlinerMode()); + + // This is ONLY for OutlineViews + if(bIsOutlineView) + { + // allow move up if position is 2 or greater OR it + // is a title object (and thus depth==1) + if(pOutl->GetAbsPos(pPara) > 1 || ( ::Outliner::HasParaFlag(pPara,ParaFlag::ISPAGE) && pOutl->GetAbsPos(pPara) > 0 ) ) + { + // not at top + bDisableUp = false; + } + } + else + { + // old behaviour for OutlinerMode::OutlineObject + if(pOutl->GetAbsPos(pPara) > 0) + { + // not at top + bDisableUp = false; + } + } + + for (const auto& rpItem : aSelList) + { + pPara = rpItem; + + sal_Int16 nDepth = pOutl->GetDepth( pOutl->GetAbsPos( pPara ) ); + + if (nDepth > 0 || (bOutlineViewSh && (nDepth <= 0) && !::Outliner::HasParaFlag( pPara, ParaFlag::ISPAGE )) ) + { + // not minimum depth + bDisableLeft = false; + } + + if( (nDepth < pOLV->GetOutliner()->GetMaxDepth() && ( !bOutlineViewSh || pOutl->GetAbsPos(pPara) != 0 )) || + (bOutlineViewSh && (nDepth <= 0) && ::Outliner::HasParaFlag( pPara, ParaFlag::ISPAGE ) && pOutl->GetAbsPos(pPara) != 0) ) + { + // not maximum depth and not at top + bDisableRight = false; + } + } + + if ( ( pOutl->GetAbsPos(pPara) < pOutl->GetParagraphCount() - 1 ) && + ( pOutl->GetParagraphCount() > 1 || !bOutlineViewSh) ) + { + // not last paragraph + bDisableDown = false; + } + + // disable when first para and 2nd is not a title + pPara = aSelList.empty() ? nullptr : *(aSelList.begin()); + + if(!bDisableDown && bIsOutlineView + && pPara + && 0 == pOutl->GetAbsPos(pPara) + && pOutl->GetParagraphCount() > 1 + && !::Outliner::HasParaFlag( pOutl->GetParagraph(1), ParaFlag::ISPAGE ) ) + { + // Needs to be disabled + bDisableDown = true; + } + } + } + + if (bDisableLeft) + rSet.DisableItem(SID_OUTLINE_LEFT); + if (bDisableRight) + rSet.DisableItem(SID_OUTLINE_RIGHT); + if (bDisableUp) + rSet.DisableItem(SID_OUTLINE_UP); + if (bDisableDown) + rSet.DisableItem(SID_OUTLINE_DOWN); + } + break; + + case SID_TEXTDIRECTION_LEFT_TO_RIGHT: + case SID_TEXTDIRECTION_TOP_TO_BOTTOM: + { + if ( bDisableVerticalText ) + { + rSet.DisableItem( SID_TEXTDIRECTION_LEFT_TO_RIGHT ); + rSet.DisableItem( SID_TEXTDIRECTION_TOP_TO_BOTTOM ); + } + else + { + bool bLeftToRight = true; + + SdrOutliner* pOutl = mpView->GetTextEditOutliner(); + if( pOutl ) + { + if( pOutl->IsVertical() ) + bLeftToRight = false; + } + else + bLeftToRight = aAttrSet.Get( SDRATTR_TEXTDIRECTION ).GetValue() == css::text::WritingMode_LR_TB; + + rSet.Put( SfxBoolItem( SID_TEXTDIRECTION_LEFT_TO_RIGHT, bLeftToRight ) ); + rSet.Put( SfxBoolItem( SID_TEXTDIRECTION_TOP_TO_BOTTOM, !bLeftToRight ) ); + + if( !bLeftToRight ) + bDisableParagraphTextDirection = true; + } + } + break; + + case SID_ULINE_VAL_NONE: + case SID_ULINE_VAL_SINGLE: + case SID_ULINE_VAL_DOUBLE: + case SID_ULINE_VAL_DOTTED: + { + if( aAttrSet.GetItemState( EE_CHAR_UNDERLINE ) >= SfxItemState::DEFAULT ) + { + FontLineStyle eLineStyle = aAttrSet.Get(EE_CHAR_UNDERLINE).GetLineStyle(); + + switch (nSlotId) + { + case SID_ULINE_VAL_NONE: + rSet.Put(SfxBoolItem(nSlotId, eLineStyle == LINESTYLE_NONE)); + break; + case SID_ULINE_VAL_SINGLE: + rSet.Put(SfxBoolItem(nSlotId, eLineStyle == LINESTYLE_SINGLE)); + break; + case SID_ULINE_VAL_DOUBLE: + rSet.Put(SfxBoolItem(nSlotId, eLineStyle == LINESTYLE_DOUBLE)); + break; + case SID_ULINE_VAL_DOTTED: + rSet.Put(SfxBoolItem(nSlotId, eLineStyle == LINESTYLE_DOTTED)); + break; + } + } + } + break; + + case SID_GROW_FONT_SIZE: + case SID_SHRINK_FONT_SIZE: + { + // todo + } + break; + + case SID_THES: + { + if (mpView->GetTextEditOutlinerView()) + { + EditView & rEditView = mpView->GetTextEditOutlinerView()->GetEditView(); + OUString aStatusVal; + LanguageType nLang = LANGUAGE_NONE; + bool bIsLookUpWord = GetStatusValueForThesaurusFromContext( aStatusVal, nLang, rEditView ); + rSet.Put( SfxStringItem( SID_THES, aStatusVal ) ); + + // disable "Thesaurus" context menu entry if there is nothing to look up + uno::Reference< linguistic2::XThesaurus > xThes( LinguMgr::GetThesaurus() ); + if (!bIsLookUpWord || + !xThes.is() || nLang == LANGUAGE_NONE || !xThes->hasLocale( LanguageTag( nLang). getLocale() )) + rSet.DisableItem( SID_THES ); + } + else + { + rSet.DisableItem( SID_THES ); + } + } + break; + + default: + break; + } + + nWhich = aIter.NextWhich(); + } + + rSet.Put( aAttrSet, false ); // <- sal_False, so DontCare-Status gets acquired + + // these are disabled in outline-mode + if (!mpViewShell || dynamic_cast< const DrawViewShell *>( mpViewShell ) == nullptr) + { + rSet.DisableItem( SID_ATTR_PARA_ADJUST_LEFT ); + rSet.DisableItem( SID_ATTR_PARA_ADJUST_RIGHT ); + rSet.DisableItem( SID_ATTR_PARA_ADJUST_CENTER ); + rSet.DisableItem( SID_ATTR_PARA_ADJUST_BLOCK ); + rSet.DisableItem( SID_ATTR_PARA_LINESPACE_10 ); + rSet.DisableItem( SID_ATTR_PARA_LINESPACE_15 ); + rSet.DisableItem( SID_ATTR_PARA_LINESPACE_20 ); + rSet.DisableItem( SID_DEC_INDENT ); + rSet.DisableItem( SID_INC_INDENT ); + rSet.DisableItem( SID_PARASPACE_INCREASE ); + rSet.DisableItem( SID_PARASPACE_DECREASE ); + rSet.DisableItem( SID_TEXTDIRECTION_TOP_TO_BOTTOM ); + rSet.DisableItem( SID_TEXTDIRECTION_LEFT_TO_RIGHT ); + rSet.DisableItem( SID_ATTR_PARA_LEFT_TO_RIGHT ); + rSet.DisableItem( SID_ATTR_PARA_RIGHT_TO_LEFT ); + } + else + { + // paragraph spacing + OutlinerView* pOLV = mpView->GetTextEditOutlinerView(); + if( pOLV ) + { + ESelection aSel = pOLV->GetSelection(); + aSel.Adjust(); + sal_Int32 nStartPara = aSel.nStartPara; + sal_Int32 nEndPara = aSel.nEndPara; + if( !aSel.HasRange() ) + { + nStartPara = 0; + nEndPara = pOLV->GetOutliner()->GetParagraphCount() - 1; + } + ::tools::Long nUpper = 0; + + for( sal_Int32 nPara = nStartPara; nPara <= nEndPara; nPara++ ) + { + const SfxItemSet& rItems = pOLV->GetOutliner()->GetParaAttribs( nPara ); + const SvxULSpaceItem& rItem = rItems.Get( EE_PARA_ULSPACE ); + nUpper = std::max( nUpper, static_cast<::tools::Long>(rItem.GetUpper()) ); + } + if( nUpper == 0 ) + rSet.DisableItem( SID_PARASPACE_DECREASE ); + } + else + { + // never disabled at the moment! + //rSet.DisableItem( SID_PARASPACE_INCREASE ); + //rSet.DisableItem( SID_PARASPACE_DECREASE ); + } + + // paragraph justification + const SvxLRSpaceItem& aLR = aAttrSet.Get( EE_PARA_LRSPACE ); + rSet.Put(aLR); + SvxAdjust eAdj = aAttrSet.Get( EE_PARA_JUST ).GetAdjust(); + switch( eAdj ) + { + case SvxAdjust::Left: + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_LEFT, true ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_CENTER, false ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_RIGHT, false ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_BLOCK, false ) ); + break; + case SvxAdjust::Center: + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_CENTER, true ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_LEFT, false ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_RIGHT, false ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_BLOCK, false ) ); + break; + case SvxAdjust::Right: + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_RIGHT, true ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_CENTER, false ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_LEFT, false ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_BLOCK, false ) ); + break; + case SvxAdjust::Block: + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_BLOCK, true ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_CENTER, false ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_RIGHT, false ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_LEFT, false ) ); + break; + default: + break; + } + + Invalidate(SID_ATTR_PARA_ADJUST_LEFT); + Invalidate(SID_ATTR_PARA_ADJUST_CENTER); + Invalidate(SID_ATTR_PARA_ADJUST_RIGHT); + Invalidate(SID_ATTR_PARA_ADJUST_BLOCK); + Invalidate(SID_ATTR_PARA_LINESPACE); + Invalidate(SID_ATTR_PARA_ULSPACE); + + // paragraph text direction + if( bDisableParagraphTextDirection ) + { + rSet.DisableItem( SID_ATTR_PARA_LEFT_TO_RIGHT ); + rSet.DisableItem( SID_ATTR_PARA_RIGHT_TO_LEFT ); + } + else + { + switch( aAttrSet.Get( EE_PARA_WRITINGDIR ).GetValue() ) + { + case SvxFrameDirection::Vertical_LR_TB: + case SvxFrameDirection::Vertical_RL_TB: + { + rSet.DisableItem( SID_ATTR_PARA_LEFT_TO_RIGHT ); + rSet.DisableItem( SID_ATTR_PARA_RIGHT_TO_LEFT ); + } + break; + + case SvxFrameDirection::Horizontal_LR_TB: + rSet.Put( SfxBoolItem( SID_ATTR_PARA_LEFT_TO_RIGHT, true ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_RIGHT_TO_LEFT, false ) ); + break; + + case SvxFrameDirection::Horizontal_RL_TB: + rSet.Put( SfxBoolItem( SID_ATTR_PARA_LEFT_TO_RIGHT, false ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_RIGHT_TO_LEFT, true ) ); + break; + + // The case for the superordinate object is missing. + case SvxFrameDirection::Environment: + { + SdDrawDocument& rDoc = mpView->GetDoc(); + css::text::WritingMode eMode = rDoc.GetDefaultWritingMode(); + bool bIsLeftToRight(false); + + if(css::text::WritingMode_LR_TB == eMode + || css::text::WritingMode_TB_RL == eMode) + { + bIsLeftToRight = true; + } + + rSet.Put( SfxBoolItem( SID_ATTR_PARA_LEFT_TO_RIGHT, bIsLeftToRight ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_RIGHT_TO_LEFT, !bIsLeftToRight ) ); + } + break; + default: break; + } + } + + SvxLRSpaceItem aLRSpace = aAttrSet.Get( EE_PARA_LRSPACE ); + aLRSpace.SetWhich(SID_ATTR_PARA_LRSPACE); + rSet.Put(aLRSpace); + Invalidate(SID_ATTR_PARA_LRSPACE); + + //Added by xuxu + SfxItemState eState = aAttrSet.GetItemState( EE_PARA_LRSPACE ); + if ( eState == SfxItemState::DONTCARE ) + { + rSet.InvalidateItem(EE_PARA_LRSPACE); + rSet.InvalidateItem(SID_ATTR_PARA_LRSPACE); + } + sal_uInt16 nLineSpace = aAttrSet.Get( EE_PARA_SBL ).GetPropLineSpace(); + switch( nLineSpace ) + { + case 100: + rSet.Put( SfxBoolItem( SID_ATTR_PARA_LINESPACE_10, true ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_LINESPACE_15, false ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_LINESPACE_20, false ) ); + break; + case 150: + rSet.Put( SfxBoolItem( SID_ATTR_PARA_LINESPACE_15, true ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_LINESPACE_10, false ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_LINESPACE_20, false ) ); + break; + case 200: + rSet.Put( SfxBoolItem( SID_ATTR_PARA_LINESPACE_20, true ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_LINESPACE_10, false ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_LINESPACE_15, false ) ); + break; + } + } + + // justification (superscript, subscript) is also needed in outline-mode + SvxEscapement eEsc = static_cast<SvxEscapement>(aAttrSet.Get( EE_CHAR_ESCAPEMENT ).GetEnumValue()); + rSet.Put(SfxBoolItem(SID_SET_SUPER_SCRIPT, eEsc == SvxEscapement::Superscript)); + rSet.Put(SfxBoolItem(SID_SET_SUB_SCRIPT, eEsc == SvxEscapement::Subscript)); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drtxtob1.cxx b/sd/source/ui/view/drtxtob1.cxx new file mode 100644 index 000000000..86b7a698a --- /dev/null +++ b/sd/source/ui/view/drtxtob1.cxx @@ -0,0 +1,865 @@ +/* -*- 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 <TextObjectBar.hxx> + +#include <svx/svxids.hrc> + +#include <editeng/eeitem.hxx> +#include <editeng/editview.hxx> +#include <editeng/outliner.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/lspcitem.hxx> +#include <editeng/adjustitem.hxx> +#include <editeng/numitem.hxx> +#include <svl/itempool.hxx> +#include <svl/stritem.hxx> +#include <svl/style.hxx> +#include <sfx2/request.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/dispatch.hxx> +#include <editeng/escapementitem.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/postitem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/crossedoutitem.hxx> +#include <editeng/contouritem.hxx> +#include <editeng/shdditem.hxx> +#include <svx/svdpagv.hxx> +#include <editeng/flstitem.hxx> +#include <editeng/scripttypeitem.hxx> +#include <editeng/writingmodeitem.hxx> +#include <editeng/frmdiritem.hxx> +#include <editeng/cmapitem.hxx> + +#include <app.hrc> +#include <strings.hrc> +#include <sdresid.hxx> +#include <prlayout.hxx> +#include <ViewShell.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <stlpool.hxx> +#include <stlsheet.hxx> +#include <OutlineView.hxx> +#include <Window.hxx> +#include <futempl.hxx> +#include <DrawDocShell.hxx> +#include <futext.hxx> +#include <editeng/colritem.hxx> + +#include <memory> + +namespace +{ + void lcl_convertStringArguments(sal_uInt16 nSlot, const std::unique_ptr<SfxItemSet>& pArgs) + { + Color aColor; + OUString sColor; + const SfxPoolItem* pColorStringItem = nullptr; + + if (SfxItemState::SET != pArgs->GetItemState(SID_ATTR_COLOR_STR, false, &pColorStringItem)) + return; + + sColor = static_cast<const SfxStringItem*>(pColorStringItem)->GetValue(); + + if (sColor == "transparent") + aColor = COL_TRANSPARENT; + else + aColor = Color(ColorTransparency, sColor.toInt32(16)); + + switch (nSlot) + { + case SID_ATTR_CHAR_COLOR: + { + SvxColorItem aColorItem(aColor, EE_CHAR_COLOR); + pArgs->Put(aColorItem); + break; + } + + case SID_ATTR_CHAR_BACK_COLOR: + { + SvxColorItem pBackgroundItem(aColor, EE_CHAR_BKGCOLOR); + pArgs->Put(pBackgroundItem); + break; + } + } + } +} + +namespace sd { + +/** + * Process SfxRequests + */ + +void TextObjectBar::Execute( SfxRequest &rReq ) +{ + const SfxItemSet* pArgs = rReq.GetArgs(); + sal_uInt16 nSlot = rReq.GetSlot(); + OutlinerView* pOLV = mpView->GetTextEditOutlinerView(); + + std::unique_ptr<OutlineViewModelChangeGuard, o3tl::default_delete<OutlineViewModelChangeGuard>> aGuard; + + assert(mpViewShell); + + if (OutlineView* pOView = dynamic_cast<OutlineView*>(mpView)) + { + pOLV = pOView->GetViewByWindow(mpViewShell->GetActiveWindow()); + aGuard.reset( new OutlineViewModelChangeGuard( static_cast<OutlineView&>(*mpView) ) ); + } + + switch (nSlot) + { + case SID_STYLE_APPLY: + { + if( pArgs ) + { + SdDrawDocument& rDoc = mpView->GetDoc(); + assert(mpViewShell->GetViewShell()); + rtl::Reference<FuPoor> xFunc( FuTemplate::Create( mpViewShell, static_cast< ::sd::Window*>( mpViewShell->GetViewShell()->GetWindow()), mpView, &rDoc, rReq ) ); + + if(xFunc.is()) + { + xFunc->Activate(); + xFunc->Deactivate(); + + if( rReq.GetSlot() == SID_STYLE_APPLY ) + { + if (mpViewShell->GetViewFrame()) + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_STYLE_APPLY ); + } + } + } + else + { + if (mpViewShell->GetViewFrame()) + mpViewShell->GetViewFrame()->GetDispatcher()->Execute( SID_STYLE_DESIGNER, SfxCallMode::ASYNCHRON ); + } + + rReq.Done(); + } + break; + + case SID_INC_INDENT: + case SID_DEC_INDENT: + { + if( pOLV ) + { + ESelection aSel = pOLV->GetSelection(); + aSel.Adjust(); + sal_Int32 nStartPara = aSel.nStartPara; + sal_Int32 nEndPara = aSel.nEndPara; + if( !aSel.HasRange() ) + { + nStartPara = 0; + nEndPara = pOLV->GetOutliner()->GetParagraphCount() - 1; + } + + pOLV->GetOutliner()->UndoActionStart( OLUNDO_ATTR ); + for( sal_Int32 nPara = nStartPara; nPara <= nEndPara; nPara++ ) + { + SfxStyleSheet* pStyleSheet = nullptr; + if (pOLV->GetOutliner() != nullptr) + pStyleSheet = pOLV->GetOutliner()->GetStyleSheet(nPara); + if (pStyleSheet != nullptr) + { + SfxItemSet aAttr( pStyleSheet->GetItemSet() ); + SfxItemSet aTmpSet( pOLV->GetOutliner()->GetParaAttribs( nPara ) ); + aAttr.Put( aTmpSet, false ); + const SvxLRSpaceItem& rItem = aAttr.Get( EE_PARA_LRSPACE ); + std::unique_ptr<SvxLRSpaceItem> pNewItem(rItem.Clone()); + + ::tools::Long nLeft = pNewItem->GetLeft(); + if( nSlot == SID_INC_INDENT ) + nLeft += 1000; + else + { + nLeft -= 1000; + nLeft = std::max<::tools::Long>( nLeft, 0 ); + } + pNewItem->SetLeftValue( static_cast<sal_uInt16>(nLeft) ); + + SfxItemSet aNewAttrs( aAttr ); + aNewAttrs.Put( std::move(pNewItem) ); + pOLV->GetOutliner()->SetParaAttribs( nPara, aNewAttrs ); + } + } + pOLV->GetOutliner()->UndoActionEnd(); + mpViewShell->Invalidate( SID_UNDO ); + } + rReq.Done(); + + Invalidate(); + // to refresh preview (in outline mode), slot has to be invalidated: + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_PREVIEW_STATE, true ); + + } + break; + + case SID_PARASPACE_INCREASE: + case SID_PARASPACE_DECREASE: + { + if( pOLV ) + { + ESelection aSel = pOLV->GetSelection(); + aSel.Adjust(); + sal_Int32 nStartPara = aSel.nStartPara; + sal_Int32 nEndPara = aSel.nEndPara; + if( !aSel.HasRange() ) + { + nStartPara = 0; + nEndPara = pOLV->GetOutliner()->GetParagraphCount() - 1; + } + + pOLV->GetOutliner()->UndoActionStart( OLUNDO_ATTR ); + for( sal_Int32 nPara = nStartPara; nPara <= nEndPara; nPara++ ) + { + SfxStyleSheet* pStyleSheet = nullptr; + if (pOLV->GetOutliner() != nullptr) + pStyleSheet = pOLV->GetOutliner()->GetStyleSheet(nPara); + if (pStyleSheet != nullptr) + { + SfxItemSet aAttr( pStyleSheet->GetItemSet() ); + SfxItemSet aTmpSet( pOLV->GetOutliner()->GetParaAttribs( nPara ) ); + aAttr.Put( aTmpSet, false ); // sal_False= InvalidItems is not default, handle it as "holes" + const SvxULSpaceItem& rItem = aAttr.Get( EE_PARA_ULSPACE ); + std::unique_ptr<SvxULSpaceItem> pNewItem(rItem.Clone()); + + ::tools::Long nUpper = pNewItem->GetUpper(); + if( nSlot == SID_PARASPACE_INCREASE ) + nUpper += 100; + else + { + nUpper -= 100; + nUpper = std::max<::tools::Long>( nUpper, 0 ); + } + pNewItem->SetUpper( static_cast<sal_uInt16>(nUpper) ); + + ::tools::Long nLower = pNewItem->GetLower(); + if( nSlot == SID_PARASPACE_INCREASE ) + nLower += 100; + else + { + nLower -= 100; + nLower = std::max<::tools::Long>( nLower, 0 ); + } + pNewItem->SetLower( static_cast<sal_uInt16>(nLower) ); + + SfxItemSet aNewAttrs( aAttr ); + aNewAttrs.Put( std::move(pNewItem) ); + pOLV->GetOutliner()->SetParaAttribs( nPara, aNewAttrs ); + } + } + pOLV->GetOutliner()->UndoActionEnd(); + mpViewShell->Invalidate( SID_UNDO ); + } + else + { + // the following code could be enabled, if I get a correct + // DontCare status from JOE. + + // gets enabled, through it doesn't really work (see above) + SfxItemSet aEditAttr( mpView->GetDoc().GetPool() ); + mpView->GetAttributes( aEditAttr ); + if( aEditAttr.GetItemState( EE_PARA_ULSPACE ) >= SfxItemState::DEFAULT ) + { + SfxItemSet aNewAttrs(*(aEditAttr.GetPool()), aEditAttr.GetRanges()); + const SvxULSpaceItem& rItem = aEditAttr.Get( EE_PARA_ULSPACE ); + std::unique_ptr<SvxULSpaceItem> pNewItem(rItem.Clone()); + ::tools::Long nUpper = pNewItem->GetUpper(); + + if( nSlot == SID_PARASPACE_INCREASE ) + nUpper += 100; + else + { + nUpper -= 100; + nUpper = std::max<::tools::Long>( nUpper, 0 ); + } + pNewItem->SetUpper( static_cast<sal_uInt16>(nUpper) ); + + ::tools::Long nLower = pNewItem->GetLower(); + if( nSlot == SID_PARASPACE_INCREASE ) + nLower += 100; + else + { + nLower -= 100; + nLower = std::max<::tools::Long>( nLower, 0 ); + } + pNewItem->SetLower( static_cast<sal_uInt16>(nLower) ); + + aNewAttrs.Put( std::move(pNewItem) ); + + mpView->SetAttributes( aNewAttrs ); + } + } + rReq.Done(); + + Invalidate(); + // to refresh preview (in outline mode), slot has to be invalidated: + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_PREVIEW_STATE, true ); + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_ATTR_PARA_ULSPACE, true ); + } + break; + + case SID_OUTLINE_LEFT: + { + if (pOLV) + { + pOLV->AdjustDepth( -1 ); + + // Ensure bold/italic etc. icon state updates + Invalidate(); + // trigger preview refresh + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_PREVIEW_STATE, true ); + } + rReq.Done(); + } + break; + + case SID_OUTLINE_RIGHT: + { + if (pOLV) + { + pOLV->AdjustDepth( 1 ); + + // Ensure bold/italic etc. icon state updates + Invalidate(); + // trigger preview refresh + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_PREVIEW_STATE, true ); + } + rReq.Done(); + } + break; + + case SID_ATTR_PARA_LRSPACE: + { + SvxLRSpaceItem aLRSpace = static_cast<const SvxLRSpaceItem&>(pArgs->Get( + SID_ATTR_PARA_LRSPACE)); + + SfxItemSetFixed<EE_PARA_LRSPACE, EE_PARA_LRSPACE> aEditAttr( GetPool() ); + aLRSpace.SetWhich( EE_PARA_LRSPACE ); + + aEditAttr.Put( aLRSpace ); + mpView->SetAttributes( aEditAttr ); + + Invalidate(SID_ATTR_PARA_LRSPACE); + } + break; + + case SID_HANGING_INDENT: + { + SfxItemSetFixed<EE_PARA_LRSPACE, EE_PARA_LRSPACE> aLRSpaceSet( GetPool() ); + mpView->GetAttributes( aLRSpaceSet ); + SvxLRSpaceItem aParaMargin( aLRSpaceSet.Get( EE_PARA_LRSPACE ) ); + + SvxLRSpaceItem aNewMargin( EE_PARA_LRSPACE ); + aNewMargin.SetTextLeft( aParaMargin.GetTextLeft() + aParaMargin.GetTextFirstLineOffset() ); + aNewMargin.SetRight( aParaMargin.GetRight() ); + aNewMargin.SetTextFirstLineOffset( ( aParaMargin.GetTextFirstLineOffset() ) * -1 ); + aLRSpaceSet.Put( aNewMargin ); + mpView->SetAttributes( aLRSpaceSet ); + + Invalidate(SID_ATTR_PARA_LRSPACE); + } + break; + + case SID_OUTLINE_UP: + { + if (pOLV) + { + pOLV->AdjustHeight( -1 ); + + // trigger preview refresh + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_PREVIEW_STATE, true ); + } + rReq.Done(); + } + break; + + case SID_OUTLINE_DOWN: + { + if (pOLV) + { + pOLV->AdjustHeight( 1 ); + + // trigger preview refresh + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_PREVIEW_STATE, true ); + } + rReq.Done(); + } + break; + + case SID_TEXTDIRECTION_LEFT_TO_RIGHT: + case SID_TEXTDIRECTION_TOP_TO_BOTTOM: + { + mpView->SdrEndTextEdit(); + // tdf#131571: SdrEndTextEdit invalidates pTextEditOutlinerView, the pointer retrieved for pOLV + // so reinitialize pOLV + pOLV=mpView->GetTextEditOutlinerView(); + SfxItemSetFixed<SDRATTR_TEXTDIRECTION, SDRATTR_TEXTDIRECTION> aAttr( mpView->GetDoc().GetPool() ); + aAttr.Put( SvxWritingModeItem( + nSlot == SID_TEXTDIRECTION_LEFT_TO_RIGHT ? + css::text::WritingMode_LR_TB : css::text::WritingMode_TB_RL, + SDRATTR_TEXTDIRECTION ) ); + rReq.Done( aAttr ); + mpView->SetAttributes( aAttr ); + Invalidate(); + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_PREVIEW_STATE, true ); + } + break; + + case FN_NUM_BULLET_ON: + { + if (pOLV) + { + bool bMasterPage = false; + SdrPageView* pPageView = mpView->GetSdrPageView(); + if (pPageView) + { + SdPage* pPage = static_cast<SdPage*>(pPageView->GetPage()); + bMasterPage = pPage && (pPage->GetPageKind() == PageKind::Standard) && pPage->IsMasterPage(); + } + + if (!bMasterPage) + pOLV->ToggleBullets(); + else + { + //Resolves: fdo#78151 in master pages if we toggle bullets on + //and off then just disable/enable the bulleting, but do not + //change the *level* of the paragraph, because the paragraph is + //effectively a preview of the equivalent style level, and + //changing the level disconnects it from the style + + ::Outliner* pOL = pOLV->GetOutliner(); + if (pOL) + { + const SvxNumBulletItem *pItem = nullptr; + SfxStyleSheetBasePool* pSSPool = mpView->GetDocSh()->GetStyleSheetPool(); + OUString sStyleName(SdResId(STR_PSEUDOSHEET_OUTLINE) + " 1"); + SfxStyleSheetBase* pFirstStyleSheet = pSSPool->Find(sStyleName, SfxStyleFamily::Pseudo); + if( pFirstStyleSheet ) + pItem = pFirstStyleSheet->GetItemSet().GetItemIfSet(EE_PARA_NUMBULLET, false); + + if (pItem ) + { + SvxNumRule aNewRule(pItem->GetNumRule()); + ESelection aSel = pOLV->GetSelection(); + aSel.Adjust(); + sal_Int32 nStartPara = aSel.nStartPara; + sal_Int32 nEndPara = aSel.nEndPara; + for (sal_Int32 nPara = nStartPara; nPara <= nEndPara; ++nPara) + { + sal_uInt16 nLevel = pOL->GetDepth(nPara); + SvxNumberFormat aFmt(aNewRule.GetLevel(nLevel)); + + if (aFmt.GetNumberingType() == SVX_NUM_NUMBER_NONE) + { + aFmt.SetNumberingType(SVX_NUM_CHAR_SPECIAL); + SdStyleSheetPool::setDefaultOutlineNumberFormatBulletAndIndent(nLevel, aFmt); + } + else + { + aFmt.SetNumberingType(SVX_NUM_NUMBER_NONE); + aFmt.SetAbsLSpace(0); + aFmt.SetFirstLineOffset(0); + } + + aNewRule.SetLevel(nLevel, aFmt); + } + + pFirstStyleSheet->GetItemSet().Put(SvxNumBulletItem(std::move(aNewRule), EE_PARA_NUMBULLET)); + + SdStyleSheet::BroadcastSdStyleSheetChange(pFirstStyleSheet, PresentationObjects::Outline_1, pSSPool); + } + } + } + } + break; + } + case SID_GROW_FONT_SIZE: + case SID_SHRINK_FONT_SIZE: + { + const SvxFontListItem* pFonts = static_cast<const SvxFontListItem*>(mpViewShell->GetDocSh()->GetItem( SID_ATTR_CHAR_FONTLIST )); + const FontList* pFontList = pFonts ? pFonts->GetFontList(): nullptr; + if( pFontList ) + { + FuText::ChangeFontSize( nSlot == SID_GROW_FONT_SIZE, pOLV, pFontList, mpView ); + if( pOLV ) + pOLV->SetAttribs( pOLV->GetEditView().GetEmptyItemSet() ); + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_ATTR_CHAR_FONTHEIGHT ); + } + rReq.Done(); + } + break; + + case SID_THES: + { + OUString aReplaceText; + const SfxStringItem* pItem2 = rReq.GetArg(FN_PARAM_THES_WORD_REPLACE); + if (pItem2) + aReplaceText = pItem2->GetValue(); + if (!aReplaceText.isEmpty()) + ReplaceTextWithSynonym( pOLV->GetEditView(), aReplaceText ); + } + break; + + default: + { + SfxItemSet aEditAttr( mpView->GetDoc().GetPool() ); + mpView->GetAttributes( aEditAttr ); + SfxItemSet aNewAttr(*(aEditAttr.GetPool()), aEditAttr.GetRanges()); + + if( !pArgs ) + { + //aNewAttr.InvalidateAllItems(); <- produces problems (#35465#) + + switch ( nSlot ) + { + case SID_ATTR_CHAR_WEIGHT: + { + FontWeight eFW = aEditAttr.Get( EE_CHAR_WEIGHT ).GetWeight(); + aNewAttr.Put( SvxWeightItem( eFW == WEIGHT_NORMAL ? + WEIGHT_BOLD : WEIGHT_NORMAL, + EE_CHAR_WEIGHT ) ); + } + break; + case SID_ATTR_CHAR_POSTURE: + { + FontItalic eFI = aEditAttr.Get( EE_CHAR_ITALIC ).GetPosture(); + aNewAttr.Put( SvxPostureItem( eFI == ITALIC_NORMAL ? + ITALIC_NONE : ITALIC_NORMAL, + EE_CHAR_ITALIC ) ); + } + break; + case SID_ATTR_CHAR_UNDERLINE: + { + FontLineStyle eFU = aEditAttr.Get( EE_CHAR_UNDERLINE ).GetLineStyle(); + aNewAttr.Put( SvxUnderlineItem( eFU == LINESTYLE_SINGLE ? + LINESTYLE_NONE : LINESTYLE_SINGLE, + EE_CHAR_UNDERLINE ) ); + } + break; + + case SID_ULINE_VAL_NONE: + { + aNewAttr.Put(SvxUnderlineItem(LINESTYLE_NONE, EE_CHAR_UNDERLINE)); + break; + } + + case SID_ULINE_VAL_SINGLE: + case SID_ULINE_VAL_DOUBLE: + case SID_ULINE_VAL_DOTTED: + { + FontLineStyle eOld = aEditAttr.Get(EE_CHAR_UNDERLINE).GetLineStyle(); + FontLineStyle eNew = eOld; + + switch (nSlot) + { + case SID_ULINE_VAL_SINGLE: + eNew = ( eOld == LINESTYLE_SINGLE ) ? LINESTYLE_NONE : LINESTYLE_SINGLE; + break; + case SID_ULINE_VAL_DOUBLE: + eNew = ( eOld == LINESTYLE_DOUBLE ) ? LINESTYLE_NONE : LINESTYLE_DOUBLE; + break; + case SID_ULINE_VAL_DOTTED: + eNew = ( eOld == LINESTYLE_DOTTED ) ? LINESTYLE_NONE : LINESTYLE_DOTTED; + break; + } + + SvxUnderlineItem aUnderline(eNew, EE_CHAR_UNDERLINE); + aNewAttr.Put(aUnderline); + } + break; + + case SID_ATTR_CHAR_OVERLINE: + { + FontLineStyle eFO = aEditAttr.Get( EE_CHAR_OVERLINE ).GetLineStyle(); + aNewAttr.Put( SvxOverlineItem( eFO == LINESTYLE_SINGLE ? + LINESTYLE_NONE : LINESTYLE_SINGLE, + EE_CHAR_OVERLINE ) ); + } + break; + case SID_ATTR_CHAR_CONTOUR: + { + aNewAttr.Put( SvxContourItem( !aEditAttr.Get( EE_CHAR_OUTLINE ).GetValue(), EE_CHAR_OUTLINE ) ); + } + break; + case SID_ATTR_CHAR_SHADOWED: + { + aNewAttr.Put( SvxShadowedItem( !aEditAttr.Get( EE_CHAR_SHADOW ).GetValue(), EE_CHAR_SHADOW ) ); + } + break; + case SID_ATTR_CHAR_CASEMAP: + { + aNewAttr.Put( aEditAttr.Get( EE_CHAR_CASEMAP ) ); + } + break; + case SID_ATTR_CHAR_STRIKEOUT: + { + FontStrikeout eFSO = aEditAttr.Get( EE_CHAR_STRIKEOUT ).GetStrikeout(); + aNewAttr.Put( SvxCrossedOutItem( eFSO == STRIKEOUT_SINGLE ? + STRIKEOUT_NONE : STRIKEOUT_SINGLE, EE_CHAR_STRIKEOUT ) ); + } + break; + + case SID_ATTR_PARA_ADJUST_LEFT: + { + aNewAttr.Put( SvxAdjustItem( SvxAdjust::Left, EE_PARA_JUST ) ); + } + break; + case SID_ATTR_PARA_ADJUST_CENTER: + { + aNewAttr.Put( SvxAdjustItem( SvxAdjust::Center, EE_PARA_JUST ) ); + } + break; + case SID_ATTR_PARA_ADJUST_RIGHT: + { + aNewAttr.Put( SvxAdjustItem( SvxAdjust::Right, EE_PARA_JUST ) ); + } + break; + case SID_ATTR_PARA_ADJUST_BLOCK: + { + aNewAttr.Put( SvxAdjustItem( SvxAdjust::Block, EE_PARA_JUST ) ); + } + break; + case SID_ATTR_PARA_LINESPACE_10: + { + SvxLineSpacingItem aItem( LINE_SPACE_DEFAULT_HEIGHT, EE_PARA_SBL ); + aItem.SetPropLineSpace( 100 ); + aNewAttr.Put( aItem ); + } + break; + case SID_ATTR_PARA_LINESPACE_15: + { + SvxLineSpacingItem aItem( LINE_SPACE_DEFAULT_HEIGHT, EE_PARA_SBL ); + aItem.SetPropLineSpace( 150 ); + aNewAttr.Put( aItem ); + } + break; + case SID_ATTR_PARA_LINESPACE_20: + { + SvxLineSpacingItem aItem( LINE_SPACE_DEFAULT_HEIGHT, EE_PARA_SBL ); + aItem.SetPropLineSpace( 200 ); + aNewAttr.Put( aItem ); + } + break; + case SID_SET_SUPER_SCRIPT: + { + SvxEscapementItem aItem( EE_CHAR_ESCAPEMENT ); + SvxEscapement eEsc = static_cast<SvxEscapement>(aEditAttr.Get( EE_CHAR_ESCAPEMENT ).GetEnumValue()); + + if( eEsc == SvxEscapement::Superscript ) + aItem.SetEscapement( SvxEscapement::Off ); + else + aItem.SetEscapement( SvxEscapement::Superscript ); + aNewAttr.Put( aItem ); + } + break; + case SID_SET_SUB_SCRIPT: + { + SvxEscapementItem aItem( EE_CHAR_ESCAPEMENT ); + SvxEscapement eEsc = static_cast<SvxEscapement>(aEditAttr.Get( EE_CHAR_ESCAPEMENT ).GetEnumValue()); + + if( eEsc == SvxEscapement::Subscript ) + aItem.SetEscapement( SvxEscapement::Off ); + else + aItem.SetEscapement( SvxEscapement::Subscript ); + aNewAttr.Put( aItem ); + } + break; + + // attributes for TextObjectBar + case SID_ATTR_CHAR_FONT: + mpViewShell->GetViewFrame()->GetDispatcher()-> + Execute( SID_CHAR_DLG, SfxCallMode::ASYNCHRON ); + break; + case SID_ATTR_CHAR_FONTHEIGHT: + mpViewShell->GetViewFrame()->GetDispatcher()-> + Execute( SID_CHAR_DLG, SfxCallMode::ASYNCHRON ); + break; + case SID_ATTR_CHAR_COLOR: + break; +// #i35937# removed need for FN_NUM_BULLET_ON handling + } + + rReq.Done( aNewAttr ); + pArgs = rReq.GetArgs(); + } + else if ( nSlot == SID_ATTR_PARA_LEFT_TO_RIGHT || + nSlot == SID_ATTR_PARA_RIGHT_TO_LEFT ) + { + bool bLeftToRight = nSlot == SID_ATTR_PARA_LEFT_TO_RIGHT; + + SvxAdjust nAdjust = SvxAdjust::Left; + if( const SvxAdjustItem* pAdjustItem = aEditAttr.GetItemIfSet(EE_PARA_JUST) ) + nAdjust = pAdjustItem->GetAdjust(); + + if( bLeftToRight ) + { + aNewAttr.Put( SvxFrameDirectionItem( SvxFrameDirection::Horizontal_LR_TB, EE_PARA_WRITINGDIR ) ); + if( nAdjust == SvxAdjust::Right ) + aNewAttr.Put( SvxAdjustItem( SvxAdjust::Left, EE_PARA_JUST ) ); + } + else + { + aNewAttr.Put( SvxFrameDirectionItem( SvxFrameDirection::Horizontal_RL_TB, EE_PARA_WRITINGDIR ) ); + if( nAdjust == SvxAdjust::Left ) + aNewAttr.Put( SvxAdjustItem( SvxAdjust::Right, EE_PARA_JUST ) ); + } + + rReq.Done( aNewAttr ); + pArgs = rReq.GetArgs(); + + Invalidate( SID_RULER_TEXT_RIGHT_TO_LEFT ); + } + else if ( nSlot == SID_ATTR_CHAR_FONT || + nSlot == SID_ATTR_CHAR_FONTHEIGHT || + nSlot == SID_ATTR_CHAR_POSTURE || + nSlot == SID_ATTR_CHAR_WEIGHT ) + { + // #i78017 establish the same behaviour as in Writer + SvtScriptType nScriptType = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX; + if (nSlot == SID_ATTR_CHAR_FONT) + nScriptType = mpView->GetScriptType(); + + SfxItemPool& rPool = mpView->GetDoc().GetPool(); + SvxScriptSetItem aSvxScriptSetItem( nSlot, rPool ); + aSvxScriptSetItem.PutItemForScriptType( nScriptType, pArgs->Get( rPool.GetWhich( nSlot ) ) ); + aNewAttr.Put( aSvxScriptSetItem.GetItemSet() ); + rReq.Done( aNewAttr ); + pArgs = rReq.GetArgs(); + } + else if (nSlot == SID_ATTR_PARA_ADJUST_LEFT || + nSlot == SID_ATTR_PARA_ADJUST_CENTER || + nSlot == SID_ATTR_PARA_ADJUST_RIGHT || + nSlot == SID_ATTR_PARA_ADJUST_BLOCK) + { + switch( nSlot ) + { + case SID_ATTR_PARA_ADJUST_LEFT: + { + aNewAttr.Put( SvxAdjustItem( SvxAdjust::Left, EE_PARA_JUST ) ); + } + break; + case SID_ATTR_PARA_ADJUST_CENTER: + { + aNewAttr.Put( SvxAdjustItem( SvxAdjust::Center, EE_PARA_JUST ) ); + } + break; + case SID_ATTR_PARA_ADJUST_RIGHT: + { + aNewAttr.Put( SvxAdjustItem( SvxAdjust::Right, EE_PARA_JUST ) ); + } + break; + case SID_ATTR_PARA_ADJUST_BLOCK: + { + aNewAttr.Put( SvxAdjustItem( SvxAdjust::Block, EE_PARA_JUST ) ); + } + break; + } + rReq.Done( aNewAttr ); + pArgs = rReq.GetArgs(); + } + else if(nSlot == SID_ATTR_CHAR_KERNING) + { + aNewAttr.Put(pArgs->Get(pArgs->GetPool()->GetWhich(nSlot))); + rReq.Done( aNewAttr ); + pArgs = rReq.GetArgs(); + } + else if(nSlot == SID_SET_SUPER_SCRIPT ) + { + SvxEscapementItem aItem(EE_CHAR_ESCAPEMENT); + SvxEscapement eEsc = static_cast<SvxEscapement>(aEditAttr.Get( EE_CHAR_ESCAPEMENT ).GetEnumValue()); + + if( eEsc == SvxEscapement::Superscript ) + aItem.SetEscapement( SvxEscapement::Off ); + else + aItem.SetEscapement( SvxEscapement::Superscript ); + aNewAttr.Put( aItem ); + rReq.Done( aNewAttr ); + pArgs = rReq.GetArgs(); + } + else if( nSlot == SID_SET_SUB_SCRIPT ) + { + SvxEscapementItem aItem(EE_CHAR_ESCAPEMENT); + SvxEscapement eEsc = static_cast<SvxEscapement>(aEditAttr.Get( EE_CHAR_ESCAPEMENT ).GetEnumValue()); + + if( eEsc == SvxEscapement::Subscript ) + aItem.SetEscapement( SvxEscapement::Off ); + else + aItem.SetEscapement( SvxEscapement::Subscript ); + aNewAttr.Put( aItem ); + rReq.Done( aNewAttr ); + pArgs = rReq.GetArgs(); + } + + std::unique_ptr<SfxItemSet> pNewArgs = pArgs->Clone(); + lcl_convertStringArguments(nSlot, pNewArgs); + + // Merge the color parameters to the color itself. + std::unique_ptr<SvxColorItem> pColorItem; + if (nSlot == SID_ATTR_CHAR_COLOR) + { + pColorItem = std::make_unique<SvxColorItem>(pNewArgs->Get(EE_CHAR_COLOR)); + } + if (const SfxInt16Item* pIntItem = pArgs->GetItemIfSet(SID_ATTR_COLOR_THEME_INDEX, false)) + { + pColorItem->GetThemeColor().SetThemeIndex(pIntItem->GetValue()); + } + if (const SfxInt16Item* pIntItem = pArgs->GetItemIfSet(SID_ATTR_COLOR_LUM_MOD, false)) + { + pColorItem->GetThemeColor().SetLumMod(pIntItem->GetValue()); + } + if (const SfxInt16Item* pIntItem = pArgs->GetItemIfSet(SID_ATTR_COLOR_LUM_OFF, false)) + { + pColorItem->GetThemeColor().SetLumOff(pIntItem->GetValue()); + } + if (pColorItem) + { + pNewArgs->Put(std::move(pColorItem)); + } + + mpView->SetAttributes(*pNewArgs); + + // invalidate entire shell because of performance and + // extension reasons + Invalidate(); + + // to refresh preview (in outline mode), slot has to be invalidated: + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_PREVIEW_STATE, true ); + } + break; + } + + if ( nSlot != SID_STYLE_APPLY && pOLV ) + { + pOLV->ShowCursor(); + pOLV->GetWindow()->GrabFocus(); + } + + Invalidate( SID_OUTLINE_LEFT ); + Invalidate( SID_OUTLINE_RIGHT ); + Invalidate( SID_OUTLINE_UP ); + Invalidate( SID_OUTLINE_DOWN ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviews1.cxx b/sd/source/ui/view/drviews1.cxx new file mode 100644 index 000000000..085bc93f2 --- /dev/null +++ b/sd/source/ui/view/drviews1.cxx @@ -0,0 +1,1360 @@ +/* -*- 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 <DrawViewShell.hxx> +#include <ViewShellImplementation.hxx> + +#include <DrawController.hxx> +#include <com/sun/star/embed/XEmbeddedObject.hpp> + +#include <comphelper/scopeguard.hxx> +#include <rtl/ref.hxx> + +#include <svx/svxids.hrc> +#include <svx/svdpagv.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/bindings.hxx> +#include <svx/svdoole2.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/module.hxx> +#include <vcl/scrbar.hxx> +#include <svx/svdopage.hxx> +#include <svx/fmshell.hxx> +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> +#include <vcl/graphicfilter.hxx> + +#include <view/viewoverlaymanager.hxx> + +#include <app.hrc> + +#include <fupoor.hxx> +#include <unokywds.hxx> +#include <sdpage.hxx> +#include <FrameView.hxx> +#include <Window.hxx> +#include <drawview.hxx> +#include <drawdoc.hxx> +#include <DrawDocShell.hxx> +#include <Ruler.hxx> +#include <Client.hxx> +#include <slideshow.hxx> +#include <AnimationChildWindow.hxx> +#include <ToolBarManager.hxx> +#include <FormShellManager.hxx> +#include <ViewShellBase.hxx> +#include <LayerTabBar.hxx> +#include <ViewShellManager.hxx> +#include <ViewShellHint.hxx> +#include <SlideSorter.hxx> +#include <SlideSorterViewShell.hxx> +#include <controller/SlideSorterController.hxx> +#include <controller/SlsPageSelector.hxx> + +#include <comphelper/lok.hxx> +#include <LibreOfficeKit/LibreOfficeKitEnums.h> +#include <vcl/uitest/logger.hxx> +#include <vcl/uitest/eventdescription.hxx> + +using namespace com::sun::star; + +namespace sd { + +void DrawViewShell::Activate(bool bIsMDIActivate) +{ + ViewShell::Activate(bIsMDIActivate); + + // When the mode is switched to normal the main view shell grabs focus. + // This is done for getting cut/copy/paste commands on slides in the left + // pane (slide sorter view shell) to work properly. + SfxShell* pTopViewShell = this->GetViewShellBase().GetViewShellManager()->GetTopViewShell(); + if (pTopViewShell && pTopViewShell == this) + { + this->GetActiveWindow()->GrabFocus(); + } +} + +void DrawViewShell::UIActivating( SfxInPlaceClient* pCli ) +{ + ViewShell::UIActivating(pCli); + + // Disable own controls + maTabControl->Disable(); + if (GetLayerTabControl() != nullptr) + GetLayerTabControl()->Disable(); +} + +void DrawViewShell::UIDeactivated( SfxInPlaceClient* pCli ) +{ + // Enable own controls + maTabControl->Enable(); + if (GetLayerTabControl() != nullptr) + GetLayerTabControl()->Enable(); + + ViewShell::UIDeactivated(pCli); +} + +void DrawViewShell::Deactivate(bool bIsMDIActivate) +{ + // Temporarily disable context broadcasting while the Deactivate() + // call is forwarded to our base class. + const bool bIsContextBroadcasterEnabled (SfxShell::SetContextBroadcasterEnabled(false)); + + ViewShell::Deactivate(bIsMDIActivate); + + SfxShell::SetContextBroadcasterEnabled(bIsContextBroadcasterEnabled); +} + +namespace +{ + class LockUI + { + private: + void Lock(bool bLock); + SfxViewFrame *mpFrame; + public: + explicit LockUI(SfxViewFrame *pFrame) : mpFrame(pFrame) { Lock(true); } + ~LockUI() { Lock(false); } + + }; + + void LockUI::Lock(bool bLock) + { + if (!mpFrame) + return; + mpFrame->Enable( !bLock ); + } +} + +/** + * Called, if state of selection of view is changed + */ + +void DrawViewShell::SelectionHasChanged() +{ + Invalidate(); + + //Update3DWindow(); // 3D-Controller + SfxBoolItem aItem( SID_3D_STATE, true ); + GetViewFrame()->GetDispatcher()->ExecuteList( + SID_3D_STATE, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, { &aItem }); + + SdrOle2Obj* pOleObj = nullptr; + + if ( mpDrawView->AreObjectsMarked() ) + { + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + + if (rMarkList.GetMarkCount() == 1) + { + SdrMark* pMark = rMarkList.GetMark(0); + SdrObject* pObj = pMark->GetMarkedSdrObj(); + + SdrInventor nInv = pObj->GetObjInventor(); + SdrObjKind nSdrObjKind = pObj->GetObjIdentifier(); + + if (nInv == SdrInventor::Default && nSdrObjKind == SdrObjKind::OLE2) + { + pOleObj = static_cast<SdrOle2Obj*>(pObj); + UpdateIMapDlg( pObj ); + } + else if (nSdrObjKind == SdrObjKind::Graphic) + UpdateIMapDlg( pObj ); + } + } + + ViewShellBase& rBase = GetViewShellBase(); + rBase.SetVerbs( uno::Sequence< embed::VerbDescriptor >() ); + + try + { + Client* pIPClient = static_cast<Client*>(rBase.GetIPClient()); + if ( pIPClient && pIPClient->IsObjectInPlaceActive() ) + { + // as appropriate take ole-objects into account and deactivate + + // this means we recently deselected an inplace active ole object so + // we need to deselect it now + if (!pOleObj) + { + //#i47279# disable frame until after object has completed unload + LockUI aUILock(GetViewFrame()); + pIPClient->DeactivateObject(); + //HMHmpDrView->ShowMarkHdl(); + } + else + { + const uno::Reference < embed::XEmbeddedObject >& xObj = pOleObj->GetObjRef(); + if ( xObj.is() ) + { + rBase.SetVerbs( xObj->getSupportedVerbs() ); + } + else + { + rBase.SetVerbs( uno::Sequence < embed::VerbDescriptor >() ); + } + } + } + else + { + if ( pOleObj ) + { + const uno::Reference < embed::XEmbeddedObject >& xObj = pOleObj->GetObjRef(); + if ( xObj.is() ) + { + rBase.SetVerbs( xObj->getSupportedVerbs() ); + } + else + { + rBase.SetVerbs( uno::Sequence < embed::VerbDescriptor >() ); + } + } + else + { + rBase.SetVerbs( uno::Sequence < embed::VerbDescriptor >() ); + } + } + } + catch( css::uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::DrawViewShell::SelectionHasChanged()" ); + } + + if( HasCurrentFunction() ) + { + GetCurrentFunction()->SelectionHasChanged(); + } + else + { + GetViewShellBase().GetToolBarManager()->SelectionHasChanged(*this,*mpDrawView); + } + + // Invalidate for every subshell + GetViewShellBase().GetViewShellManager()->InvalidateAllSubShells(this); + + mpDrawView->UpdateSelectionClipboard(); + + GetViewShellBase().GetDrawController().FireSelectionChangeListener(); +} + +namespace { + +void collectUIInformation(const OUString& aZoom) +{ + EventDescription aDescription; + aDescription.aID = "impress_win"; + aDescription.aParameters = {{"ZOOM", aZoom}}; + aDescription.aAction = "SET"; + aDescription.aKeyWord = "ImpressWindowUIObject"; + aDescription.aParent = "MainWindow"; + + UITestLogger::getInstance().logEvent(aDescription); +} + +} + +/** + * set zoom factor + */ +void DrawViewShell::SetZoom( ::tools::Long nZoom ) +{ + // Make sure that the zoom factor will not be recalculated on + // following window resizings. + mbZoomOnPage = false; + ViewShell::SetZoom( nZoom ); + GetViewFrame()->GetBindings().Invalidate( SID_ATTR_ZOOM ); + GetViewFrame()->GetBindings().Invalidate( SID_ATTR_ZOOMSLIDER ); + mpViewOverlayManager->onZoomChanged(); + collectUIInformation(OUString::number(nZoom)); +} + +/** + * Set zoom rectangle for active window + */ + +void DrawViewShell::SetZoomRect( const ::tools::Rectangle& rZoomRect ) +{ + ViewShell::SetZoomRect( rZoomRect ); + GetViewFrame()->GetBindings().Invalidate( SID_ATTR_ZOOM ); + GetViewFrame()->GetBindings().Invalidate( SID_ATTR_ZOOMSLIDER ); + mpViewOverlayManager->onZoomChanged(); +} + +/** + * PrepareClose, as appropriate end text input, so other viewshells + * discover a refreshed text object. + */ + +bool DrawViewShell::PrepareClose( bool bUI ) +{ + if ( !ViewShell::PrepareClose(bUI) ) + return false; + + if( HasCurrentFunction() ) + { + sal_uInt16 nID = GetCurrentFunction()->GetSlotID(); + if (nID == SID_TEXTEDIT || nID == SID_ATTR_CHAR) + { + mpDrawView->SdrEndTextEdit(); + } + } + + return true; +} + + +/** + * Set status (enabled/disabled) of menu SfxSlots + */ + +void DrawViewShell::ChangeEditMode(EditMode eEMode, bool bIsLayerModeActive) +{ + if (meEditMode == eEMode && mbIsLayerModeActive == bIsLayerModeActive) + return; + + ViewShellManager::UpdateLock aLock (GetViewShellBase().GetViewShellManager()); + + sal_uInt16 nActualPageId = maTabControl->GetPageId(0); + + if (mePageKind == PageKind::Handout) + { + // at handouts only allow MasterPage + eEMode = EditMode::MasterPage; + } + + GetViewShellBase().GetDrawController().FireChangeEditMode (eEMode == EditMode::MasterPage); + GetViewShellBase().GetDrawController().FireChangeLayerMode (bIsLayerModeActive); + + if ( mpDrawView->IsTextEdit() ) + { + // This exits the text edit mode when going in and out of window focus, which is not needed + // Let's keep this call as comment for now as it probably just needs a better conditional. + // mpDrawView->SdrEndTextEdit(); + } + + LayerTabBar* pLayerBar = GetLayerTabControl(); + if (pLayerBar != nullptr) + pLayerBar->EndEditMode(); + maTabControl->EndEditMode(); + + GetViewShellBase().GetDrawController().BroadcastContextChange(); + + meEditMode = eEMode; + + if(pLayerBar) + { + // #i87182# only switch activation mode of LayerTabBar when there is one, + // else it will not get initialized with the current set of Layers as needed + mbIsLayerModeActive = bIsLayerModeActive; + } + + // Determine whether to show the master view toolbar. The master + // page mode has to be active and the shell must not be a handout + // view. + bool bShowMasterViewToolbar (meEditMode == EditMode::MasterPage + && GetShellType() != ViewShell::ST_HANDOUT); + bool bShowPresentationToolbar (meEditMode != EditMode::MasterPage + && GetShellType() != ViewShell::ST_HANDOUT + && GetShellType() != ViewShell::ST_DRAW); + + // If the master view toolbar is not shown we hide it before + // switching the edit mode. + if (::sd::ViewShell::mpImpl->mbIsInitialized + && IsMainViewShell()) + { + if ( !bShowMasterViewToolbar ) + GetViewShellBase().GetToolBarManager()->ResetToolBars(ToolBarManager::ToolBarGroup::MasterMode); + if ( !bShowPresentationToolbar ) + GetViewShellBase().GetToolBarManager()->ResetToolBars(ToolBarManager::ToolBarGroup::CommonTask); + } + + ConfigureAppBackgroundColor(); + + if (meEditMode == EditMode::Page) + { + /****************************************************************** + * PAGEMODE + ******************************************************************/ + + maTabControl->Clear(); + + SdPage* pPage; + sal_uInt16 nPageCnt = GetDoc()->GetSdPageCount(mePageKind); + + for (sal_uInt16 i = 0; i < nPageCnt; i++) + { + pPage = GetDoc()->GetSdPage(i, mePageKind); + OUString aPageName = pPage->GetName(); + maTabControl->InsertPage(pPage->getPageId(), aPageName); + + if ( pPage->IsSelected() ) + { + nActualPageId = pPage->getPageId(); + } + } + + maTabControl->SetCurPageId(nActualPageId); + + SwitchPage(maTabControl->GetPagePos(nActualPageId)); + + //tdf#102343 re-enable common undo on switch back from master mode + mpDrawView->GetModel()->SetDisableTextEditUsesCommonUndoManager(false); + } + else + { + /****************************************************************** + * MASTERPAGE + ******************************************************************/ + GetViewFrame()->SetChildWindow( + AnimationChildWindow::GetChildWindowId(), false ); + + if (comphelper::LibreOfficeKit::isActive()) + GetViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED, + ".uno:SlideMasterPage=true"); + if (!mpActualPage) + { + // as long as there is no mpActualPage, take first + mpActualPage = GetDoc()->GetSdPage(0, mePageKind); + } + + maTabControl->Clear(); + sal_uInt16 nActualMasterPageId = maTabControl->GetPageId(0); + sal_uInt16 nMasterPageCnt = GetDoc()->GetMasterSdPageCount(mePageKind); + + for (sal_uInt16 i = 0; i < nMasterPageCnt; i++) + { + SdPage* pMaster = GetDoc()->GetMasterSdPage(i, mePageKind); + OUString aLayoutName = pMaster->GetLayoutName(); + sal_Int32 nPos = aLayoutName.indexOf(SD_LT_SEPARATOR); + if (nPos != -1) + aLayoutName = aLayoutName.copy(0, nPos); + + maTabControl->InsertPage(pMaster->getPageId(), aLayoutName); + + if (&(mpActualPage->TRG_GetMasterPage()) == pMaster) + { + nActualMasterPageId = pMaster->getPageId(); + } + } + + maTabControl->SetCurPageId(nActualMasterPageId); + SwitchPage(maTabControl->GetPagePos(nActualMasterPageId)); + + //tdf#102343 changing attributes of textboxes in master typically + //changes the stylesheet they are linked to, so if the common + //undo manager is in use, those stylesheet changes are thrown + //away at present + mpDrawView->GetModel()->SetDisableTextEditUsesCommonUndoManager(true); + } + + // If the master view toolbar is to be shown we turn it on after the + // edit mode has been changed. + if (::sd::ViewShell::mpImpl->mbIsInitialized + && IsMainViewShell()) + { + if (bShowMasterViewToolbar) + GetViewShellBase().GetToolBarManager()->SetToolBar( + ToolBarManager::ToolBarGroup::MasterMode, + ToolBarManager::msMasterViewToolBar); + if (bShowPresentationToolbar) + GetViewShellBase().GetToolBarManager()->SetToolBar( + ToolBarManager::ToolBarGroup::CommonTask, + ToolBarManager::msCommonTaskToolBar); + } + + if ( ! mbIsLayerModeActive) + { + maTabControl->Show(); + // Set the tab control only for draw pages. For master page + // this has been done already above. + if (meEditMode == EditMode::Page) + maTabControl->SetCurPageId (nActualPageId); + } + + ResetActualLayer(); + + Invalidate( SID_PAGEMODE ); + Invalidate( SID_LAYERMODE ); + Invalidate( SID_MASTERPAGE ); + Invalidate( SID_DELETE_MASTER_PAGE ); + Invalidate( SID_DELETE_PAGE ); + Invalidate( SID_SLIDE_MASTER_MODE ); + Invalidate( SID_NOTES_MASTER_MODE ); + Invalidate( SID_HANDOUT_MASTER_MODE ); + InvalidateWindows(); + + SetContextName(GetSidebarContextName()); + +} + +/** + * Generate horizontal ruler + */ + +VclPtr<SvxRuler> DrawViewShell::CreateHRuler (::sd::Window* pWin) +{ + VclPtr<Ruler> pRuler; + WinBits aWBits; + SvxRulerSupportFlags nFlags = SvxRulerSupportFlags::OBJECT; + + aWBits = WB_HSCROLL | WB_3DLOOK | WB_BORDER | WB_EXTRAFIELD; + nFlags |= SvxRulerSupportFlags::SET_NULLOFFSET | + SvxRulerSupportFlags::TABS | + SvxRulerSupportFlags::PARAGRAPH_MARGINS; // new + + pRuler = VclPtr<Ruler>::Create(*this, GetParentWindow(), pWin, nFlags, + GetViewFrame()->GetBindings(), aWBits); + + // Metric ... + sal_uInt16 nMetric = static_cast<sal_uInt16>(GetDoc()->GetUIUnit()); + + if( nMetric == 0xffff ) + nMetric = static_cast<sal_uInt16>(GetViewShellBase().GetViewFrame()->GetDispatcher()->GetModule()->GetFieldUnit()); + + pRuler->SetUnit( FieldUnit( nMetric ) ); + + // ... and also set DefTab at the ruler + pRuler->SetDefTabDist( GetDoc()->GetDefaultTabulator() ); // new + + Fraction aUIScale(pWin->GetMapMode().GetScaleX()); + aUIScale *= GetDoc()->GetUIScale(); + pRuler->SetZoom(aUIScale); + + return pRuler; +} + +/** + * Generate vertical ruler + */ + +VclPtr<SvxRuler> DrawViewShell::CreateVRuler(::sd::Window* pWin) +{ + VclPtr<SvxRuler> pRuler; + WinBits aWBits = WB_VSCROLL | WB_3DLOOK | WB_BORDER; + SvxRulerSupportFlags nFlags = SvxRulerSupportFlags::OBJECT; + + pRuler = VclPtr<Ruler>::Create(*this, GetParentWindow(), pWin, nFlags, + GetViewFrame()->GetBindings(), aWBits); + + // Metric same as HRuler, use document setting + sal_uInt16 nMetric = static_cast<sal_uInt16>(GetDoc()->GetUIUnit()); + + if( nMetric == 0xffff ) + nMetric = static_cast<sal_uInt16>(GetViewShellBase().GetViewFrame()->GetDispatcher()->GetModule()->GetFieldUnit()); + + pRuler->SetUnit( FieldUnit( nMetric ) ); + + Fraction aUIScale(pWin->GetMapMode().GetScaleY()); + aUIScale *= GetDoc()->GetUIScale(); + pRuler->SetZoom(aUIScale); + + return pRuler; +} + +/** + * Refresh horizontal ruler + */ + +void DrawViewShell::UpdateHRuler() +{ + Invalidate( SID_ATTR_LONG_LRSPACE ); + Invalidate( SID_RULER_PAGE_POS ); + Invalidate( SID_RULER_OBJECT ); + Invalidate( SID_RULER_TEXT_RIGHT_TO_LEFT ); + + if (mpHorizontalRuler) + mpHorizontalRuler->ForceUpdate(); +} + +/** + * Refresh vertical ruler + */ + +void DrawViewShell::UpdateVRuler() +{ + Invalidate( SID_ATTR_LONG_LRSPACE ); + Invalidate( SID_RULER_PAGE_POS ); + Invalidate( SID_RULER_OBJECT ); + + if (mpVerticalRuler) + mpVerticalRuler->ForceUpdate(); +} + +/** + * Refresh TabControl on splitter change + */ + +IMPL_LINK( DrawViewShell, TabSplitHdl, TabBar *, pTab, void ) +{ + const ::tools::Long nMax = maViewSize.Width() - maScrBarWH.Width() + - maTabControl->GetPosPixel().X() ; + + Size aTabSize = maTabControl->GetSizePixel(); + aTabSize.setWidth( std::min(pTab->GetSplitSize(), static_cast<::tools::Long>(nMax-1)) ); + + maTabControl->SetSizePixel(aTabSize); + + if(GetLayerTabControl()) // #i87182# + { + GetLayerTabControl()->SetSizePixel(aTabSize); + } + + Point aPos = maTabControl->GetPosPixel(); + aPos.AdjustX(aTabSize.Width() ); + + Size aScrSize(nMax - aTabSize.Width(), maScrBarWH.Height()); + mpHorizontalScrollBar->SetPosSizePixel(aPos, aScrSize); +} + +/// inherited from sd::ViewShell +SdPage* DrawViewShell::getCurrentPage() const +{ + const sal_uInt16 nPageCount = (meEditMode == EditMode::Page)? + GetDoc()->GetSdPageCount(mePageKind): + GetDoc()->GetMasterSdPageCount(mePageKind); + + sal_uInt16 nCurrentPage = maTabControl->GetCurPagePos(); + DBG_ASSERT((nCurrentPage<nPageCount), "sd::DrawViewShell::getCurrentPage(), illegal page index!"); + if (nCurrentPage >= nPageCount) + nCurrentPage = 0; // play safe here + + if (meEditMode == EditMode::Page) + { + return GetDoc()->GetSdPage(nCurrentPage, mePageKind); + } + else // EditMode::MasterPage + { + return GetDoc()->GetMasterSdPage(nCurrentPage, mePageKind); + } +} + +/** + * Select new refreshed page, in case of a page order change (eg. by undo) + */ + +void DrawViewShell::ResetActualPage() +{ + if (!GetDoc()) + return; + + sal_uInt16 nCurrentPageId = maTabControl->GetCurPageId(); + sal_uInt16 nNewPageId = nCurrentPageId; + sal_uInt16 nCurrentPageNum = maTabControl->GetPagePos(nCurrentPageId); + sal_uInt16 nPageCount = (meEditMode == EditMode::Page)?GetDoc()->GetSdPageCount(mePageKind):GetDoc()->GetMasterSdPageCount(mePageKind); + + if (meEditMode == EditMode::Page) + { + + // Update for TabControl + maTabControl->Clear(); + + SdPage* pPage = nullptr; + + for (sal_uInt16 i = 0; i < nPageCount; i++) + { + pPage = GetDoc()->GetSdPage(i, mePageKind); + OUString aPageName = pPage->GetName(); + maTabControl->InsertPage(pPage->getPageId(), aPageName); + + if (nCurrentPageId == pPage->getPageId()) + { + nCurrentPageNum = i; + GetDoc()->SetSelected(pPage, true); + } + else + GetDoc()->SetSelected(pPage, false); + } + + nNewPageId = maTabControl->GetPageId(nCurrentPageNum); + maTabControl->SetCurPageId(nNewPageId); + } + else // EditMode::MasterPage + { + maTabControl->Clear(); + + sal_uInt16 nMasterPageCnt = GetDoc()->GetMasterSdPageCount(mePageKind); + for (sal_uInt16 i = 0; i < nMasterPageCnt; i++) + { + SdPage* pMaster = GetDoc()->GetMasterSdPage(i, mePageKind); + OUString aLayoutName = pMaster->GetLayoutName(); + sal_Int32 nPos = aLayoutName.indexOf(SD_LT_SEPARATOR); + if (nPos != -1) + aLayoutName = aLayoutName.copy(0, nPos); + maTabControl->InsertPage(pMaster->getPageId(), aLayoutName); + + if (pMaster->getPageId() == nCurrentPageId) + nCurrentPageNum = i; + } + + nNewPageId = maTabControl->GetPageId(nCurrentPageNum); + maTabControl->SetCurPageId(nNewPageId); + SwitchPage(nCurrentPageNum); + } + + bool bAllowChangeFocus = nNewPageId != nCurrentPageId; + SfxBoolItem aI(SID_SWITCHPAGE, bAllowChangeFocus); + GetViewFrame()->GetDispatcher()->ExecuteList(SID_SWITCHPAGE, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aI }); +} + +/** + * Apply "Verb" on OLE-object. + */ +ErrCode DrawViewShell::DoVerb(sal_Int32 nVerb) +{ + if ( mpDrawView->AreObjectsMarked() ) + { + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + + if (rMarkList.GetMarkCount() == 1) + { + SdrMark* pMark = rMarkList.GetMark(0); + SdrObject* pObj = pMark->GetMarkedSdrObj(); + + SdrInventor nInv = pObj->GetObjInventor(); + SdrObjKind nSdrObjKind = pObj->GetObjIdentifier(); + + if (nInv == SdrInventor::Default && nSdrObjKind == SdrObjKind::OLE2) + { + ActivateObject( static_cast<SdrOle2Obj*>(pObj), nVerb); + } + } + } + + return ERRCODE_NONE; +} + +/** + * Activate OLE-object + */ +bool DrawViewShell::ActivateObject(SdrOle2Obj* pObj, sal_Int32 nVerb) +{ + bool bActivated = false; + + if ( !GetDocSh()->IsUIActive() ) + { + ToolBarManager::UpdateLock aLock (GetViewShellBase().GetToolBarManager()); + + bActivated = ViewShell::ActivateObject(pObj, nVerb); + } + + return bActivated; +} + +/** + * Mark the desired page as selected (1), deselected (0), toggle (2). + * nPage refers to the page in question. + */ +bool DrawViewShell::SelectPage(sal_uInt16 nPage, sal_uInt16 nSelect) +{ + SdPage* pPage = GetDoc()->GetSdPage(nPage, PageKind::Standard); + + //page selector marks pages to selected in view + auto &pageSelector = sd::slidesorter::SlideSorterViewShell::GetSlideSorter(GetViewShellBase())->GetSlideSorter().GetController().GetPageSelector(); + + if (pPage) + { + if (nSelect == 0) + { + GetDoc()->SetSelected(pPage, false); // Deselect. + pageSelector.DeselectPage(nPage); + } + else if (nSelect == 1) + { + GetDoc()->SetSelected(pPage, true); // Select. + pageSelector.SelectPage(nPage); + } + else + { + // Toggle. + if (pPage->IsSelected()) + { + GetDoc()->SetSelected(pPage, false); + pageSelector.DeselectPage(nPage); + } + else + { + GetDoc()->SetSelected(pPage, true); + pageSelector.SelectPage(nPage); + } + } + return true; + } + + return false; +} + +bool DrawViewShell::IsSelected(sal_uInt16 nPage) +{ + slidesorter::SlideSorterViewShell* pVShell + = slidesorter::SlideSorterViewShell::GetSlideSorter(GetViewShellBase()); + if (pVShell != nullptr) + return pVShell->GetSlideSorter().GetController().GetPageSelector().IsPageSelected(nPage); + + return false; +} + +bool DrawViewShell::IsVisible(sal_uInt16 nPage) +{ + slidesorter::SlideSorterViewShell* pVShell + = slidesorter::SlideSorterViewShell::GetSlideSorter(GetViewShellBase()); + if (pVShell != nullptr) + return pVShell->GetSlideSorter().GetController().GetPageSelector().IsPageVisible(nPage); + + return false; +} + +/** + * Switch to desired page. + * nSelectPage refers to the current EditMode + * bAllowChangeFocus set to false when slide is inserted before current page + * and we need to only update the current page number, + * do not disturb editing in that case + */ +bool DrawViewShell::SwitchPage(sal_uInt16 nSelectedPage, bool bAllowChangeFocus) +{ + /** Under some circumstances there are nested calls to SwitchPage() and + may crash the application (activation of form controls when the + shell of the edit view is not on top of the shell stack, see issue + 83888 for details.) Therefore the nested calls are ignored (they + would jump to the wrong page anyway.) + */ + + if (mbIsInSwitchPage) + return false; + mbIsInSwitchPage = true; + comphelper::ScopeGuard aGuard( + [this] () { this->mbIsInSwitchPage = false; } ); + + if (GetActiveWindow()->IsInPaint()) + { + // Switching the current page while a Paint is being executed is + // dangerous. So, post it for later execution and return. + maAsynchronousSwitchPageCall.Post( + [this, nSelectedPage] () { this->SwitchPage(nSelectedPage); } ); + return false; + } + + bool bOK = false; + + // With the current implementation of FuSlideShow there is a problem + // when it displays the show in a window: when the show is stopped it + // returns at one point in time SDRPAGE_NOTFOUND as current page index. + // Because FuSlideShow is currently being rewritten this bug is fixed + // here. + // This is not as bad a hack as it may look because making SwitchPage() + // more robust with respect to invalid page numbers is a good thing + // anyway. + if (nSelectedPage == SDRPAGE_NOTFOUND) + { + nSelectedPage = 0; + } + else + { + // Make sure that the given page index points to an existing page. Move + // the index into the valid range if necessary. + sal_uInt16 nPageCount = (meEditMode == EditMode::Page) + ? GetDoc()->GetSdPageCount(mePageKind) + : GetDoc()->GetMasterSdPageCount(mePageKind); + if (nSelectedPage >= nPageCount) + nSelectedPage = nPageCount-1; + } + + if (IsSwitchPageAllowed()) + { + ModifyGuard aGuard2( GetDoc() ); + + bOK = true; + + if (mpActualPage) + { + SdPage* pNewPage = nullptr; + + if (meEditMode == EditMode::MasterPage) + { + if( GetDoc()->GetMasterSdPageCount(mePageKind) > nSelectedPage ) + pNewPage = GetDoc()->GetMasterSdPage(nSelectedPage, mePageKind); + + if( pNewPage ) + { + SdrPageView* pPV = mpDrawView->GetSdrPageView(); + OUString sPageText(pNewPage->GetLayoutName()); + sal_Int32 nPos = sPageText.indexOf(SD_LT_SEPARATOR); + if (nPos != -1) + sPageText = sPageText.copy(0, nPos); + if (pPV + && pNewPage == dynamic_cast< SdPage* >( pPV->GetPage() ) + && sPageText == maTabControl->GetPageText(maTabControl->GetPageId(nSelectedPage))) + { + // this slide is already visible + return true; + } + } + } + else + { + OSL_ASSERT(mpFrameView!=nullptr); + mpFrameView->SetSelectedPage(nSelectedPage); + + if (GetDoc()->GetSdPageCount(mePageKind) > nSelectedPage) + pNewPage = GetDoc()->GetSdPage(nSelectedPage, mePageKind); + + if (mpActualPage == pNewPage) + { + SdrPageView* pPV = mpDrawView->GetSdrPageView(); + + SdPage* pCurrentPage = pPV ? dynamic_cast<SdPage*>(pPV->GetPage()) : nullptr; + if (pCurrentPage + && pNewPage == pCurrentPage + && maTabControl->GetPageText(maTabControl->GetPageId(nSelectedPage)) == pNewPage->GetName()) + { + // this slide is already visible + return true; + } + } + } + } + + if (bAllowChangeFocus) + mpDrawView->SdrEndTextEdit(); + + mpActualPage = nullptr; + + if (meEditMode == EditMode::Page) + { + mpActualPage = GetDoc()->GetSdPage(nSelectedPage, mePageKind); + } + else + { + SdPage* pMaster = GetDoc()->GetMasterSdPage(nSelectedPage, mePageKind); + + // does the selected page fit to the masterpage? + sal_uInt16 nPageCount = GetDoc()->GetSdPageCount(mePageKind); + for (sal_uInt16 i = 0; i < nPageCount; i++) + { + SdPage* pPage = GetDoc()->GetSdPage(i, mePageKind); + if(pPage && pPage->IsSelected() && pMaster == &(pPage->TRG_GetMasterPage())) + { + mpActualPage = pPage; + break; + } + } + + if (!mpActualPage) + { + // take the first page, that fits to the masterpage + for (sal_uInt16 i = 0; i < nPageCount; i++) + { + SdPage* pPage = GetDoc()->GetSdPage(i, mePageKind); + if(pPage && pMaster == &(pPage->TRG_GetMasterPage())) + { + mpActualPage = pPage; + break; + } + } + } + } + + for (sal_uInt16 i = 0; i < GetDoc()->GetSdPageCount(mePageKind); i++) + { + // deselect all pages + GetDoc()->SetSelected( GetDoc()->GetSdPage(i, mePageKind), false); + } + + if (!mpActualPage) + { + // as far as there is no mpActualPage, take the first + mpActualPage = GetDoc()->GetSdPage(0, mePageKind); + } + + // also select this page (mpActualPage always points at a drawing page, + // never at a masterpage) + GetDoc()->SetSelected(mpActualPage, true); + + if (comphelper::LibreOfficeKit::isActive()) + { + // notify LibreOfficeKit about changed page + OString aPayload = OString::number(nSelectedPage); + if (SfxViewShell* pViewShell = GetViewShell()) + pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_SET_PART, aPayload.getStr()); + } + + rtl::Reference< sd::SlideShow > xSlideshow( SlideShow::GetSlideShow( GetDoc() ) ); + if( !xSlideshow.is() || !xSlideshow->isRunning() || ( xSlideshow->getAnimationMode() != ANIMATIONMODE_SHOW ) ) + { + // tighten VisArea, to possibly deactivate objects + // !!! only if we are not in presentation mode (#96279) !!! + OSL_ASSERT (GetViewShell()!=nullptr); + GetViewShell()->DisconnectAllClients(); + VisAreaChanged(::tools::Rectangle(Point(), Size(1, 1))); + } + + // Try to prefetch all graphics for the active page. This will be done + // in threads to be more efficient than loading them on-demand one by one. + std::vector<Graphic*> graphics; + mpActualPage->getGraphicsForPrefetch(graphics); + if(graphics.size() > 1) // threading does not help with loading just one + GraphicFilter::GetGraphicFilter().MakeGraphicsAvailableThreaded(graphics); + + if (meEditMode == EditMode::Page) + { + /********************************************************************** + * PAGEMODE + **********************************************************************/ + GetDoc()->SetSelected(mpActualPage, true); + + SdrPageView* pPageView = mpDrawView->GetSdrPageView(); + + if (pPageView) + { + mpFrameView->SetVisibleLayers( pPageView->GetVisibleLayers() ); + mpFrameView->SetPrintableLayers( pPageView->GetPrintableLayers() ); + mpFrameView->SetLockedLayers( pPageView->GetLockedLayers() ); + + if (mePageKind == PageKind::Notes) + { + mpFrameView->SetNotesHelpLines( pPageView->GetHelpLines() ); + } + else if (mePageKind == PageKind::Handout) + { + mpFrameView->SetHandoutHelpLines( pPageView->GetHelpLines() ); + } + else + { + mpFrameView->SetStandardHelpLines( pPageView->GetHelpLines() ); + } + } + + mpDrawView->HideSdrPage(); + maTabControl->SetCurPageId(maTabControl->GetPageId(nSelectedPage)); + mpDrawView->ShowSdrPage(mpActualPage); + GetViewShellBase().GetDrawController().FireSwitchCurrentPage(mpActualPage); + + SdrPageView* pNewPageView = mpDrawView->GetSdrPageView(); + + if (pNewPageView) + { + pNewPageView->SetVisibleLayers( mpFrameView->GetVisibleLayers() ); + pNewPageView->SetPrintableLayers( mpFrameView->GetPrintableLayers() ); + pNewPageView->SetLockedLayers( mpFrameView->GetLockedLayers() ); + + if (mePageKind == PageKind::Notes) + { + pNewPageView->SetHelpLines( mpFrameView->GetNotesHelpLines() ); + } + else if (mePageKind == PageKind::Handout) + { + pNewPageView->SetHelpLines( mpFrameView->GetHandoutHelpLines() ); + } + else + { + pNewPageView->SetHelpLines( mpFrameView->GetStandardHelpLines() ); + } + } + + OUString aPageName = mpActualPage->GetName(); + + if (maTabControl->GetPageText(maTabControl->GetPageId(nSelectedPage)) != aPageName) + { + maTabControl->SetPageText(maTabControl->GetPageId(nSelectedPage), aPageName); + } + } + else + { + /********************************************************************** + * MASTERPAGE + **********************************************************************/ + SdrPageView* pPageView = mpDrawView->GetSdrPageView(); + + if (pPageView) + { + mpFrameView->SetVisibleLayers( pPageView->GetVisibleLayers() ); + mpFrameView->SetPrintableLayers( pPageView->GetPrintableLayers() ); + mpFrameView->SetLockedLayers( pPageView->GetLockedLayers() ); + + if (mePageKind == PageKind::Notes) + { + mpFrameView->SetNotesHelpLines( pPageView->GetHelpLines() ); + } + else if (mePageKind == PageKind::Handout) + { + mpFrameView->SetHandoutHelpLines( pPageView->GetHelpLines() ); + } + else + { + mpFrameView->SetStandardHelpLines( pPageView->GetHelpLines() ); + } + } + + mpDrawView->HideSdrPage(); + maTabControl->SetCurPageId(maTabControl->GetPageId(nSelectedPage)); + + SdPage* pMaster = GetDoc()->GetMasterSdPage(nSelectedPage, mePageKind); + + if( !pMaster ) // if this page should not exist + pMaster = GetDoc()->GetMasterSdPage(0, mePageKind); + + sal_uInt16 nNum = pMaster->GetPageNum(); + mpDrawView->ShowSdrPage(mpDrawView->GetModel()->GetMasterPage(nNum)); + + GetViewShellBase().GetDrawController().FireSwitchCurrentPage(pMaster); + + SdrPageView* pNewPageView = mpDrawView->GetSdrPageView(); + + if (pNewPageView) + { + pNewPageView->SetVisibleLayers( mpFrameView->GetVisibleLayers() ); + pNewPageView->SetPrintableLayers( mpFrameView->GetPrintableLayers() ); + pNewPageView->SetLockedLayers( mpFrameView->GetLockedLayers() ); + + if (mePageKind == PageKind::Notes) + { + pNewPageView->SetHelpLines( mpFrameView->GetNotesHelpLines() ); + } + else if (mePageKind == PageKind::Handout) + { + pNewPageView->SetHelpLines( mpFrameView->GetHandoutHelpLines() ); + } + else + { + pNewPageView->SetHelpLines( mpFrameView->GetStandardHelpLines() ); + } + } + + OUString aLayoutName(pMaster->GetLayoutName()); + sal_Int32 nPos = aLayoutName.indexOf(SD_LT_SEPARATOR); + if (nPos != -1) + aLayoutName = aLayoutName.copy(0, nPos); + + if (maTabControl->GetPageText(maTabControl->GetPageId(nSelectedPage)) != aLayoutName) + { + maTabControl->SetPageText(maTabControl->GetPageId(nSelectedPage), aLayoutName); + } + + if( mePageKind == PageKind::Handout ) + { + // set pages for all available handout presentation objects + sd::ShapeList& rShapeList = pMaster->GetPresentationShapeList(); + SdrObject* pObj = nullptr; + rShapeList.seekShape(0); + + while( (pObj = rShapeList.getNextShape()) ) + { + if( pMaster->GetPresObjKind(pObj) == PresObjKind::Handout ) + { + // #i105146# We want no content to be displayed for PageKind::Handout, + // so just never set a page as content + static_cast<SdrPageObj*>(pObj)->SetReferencedPage(nullptr); + } + } + } + } + + Size aVisSizePixel = GetActiveWindow()->GetOutputSizePixel(); + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), aVisSizePixel) ); + VisAreaChanged(aVisAreaWin); + mpDrawView->VisAreaChanged(GetActiveWindow()->GetOutDev()); + + // so navigator (and effect window) notice that + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate(SID_NAVIGATOR_STATE, true); + rBindings.Invalidate(SID_NAVIGATOR_PAGENAME, true); + rBindings.Invalidate(SID_STATUS_PAGE, true); + rBindings.Invalidate(SID_DELETE_MASTER_PAGE, true); + rBindings.Invalidate(SID_DELETE_PAGE, true); + rBindings.Invalidate(SID_ASSIGN_LAYOUT, true); + rBindings.Invalidate(SID_INSERTPAGE, true); + UpdatePreview( mpActualPage ); + + mpDrawView->AdjustMarkHdl(); + } + + return bOK; +} + +/** + * Check if page change is allowed + */ + +bool DrawViewShell::IsSwitchPageAllowed() const +{ + bool bOK = true; + + FmFormShell* pFormShell = GetViewShellBase().GetFormShellManager()->GetFormShell(); + if (pFormShell != nullptr && !pFormShell->PrepareClose(false)) + bOK = false; + + return bOK; +} + +/** + * Select new refreshed page, in case of a page order change (eg. by undo) + */ + +void DrawViewShell::ResetActualLayer() +{ + LayerTabBar* pLayerBar = GetLayerTabControl(); + if (pLayerBar == nullptr) + return; + + // remember old tab count and current tab id + // this is needed when one layer is renamed to + // restore current tab + sal_uInt16 nOldLayerCnt = pLayerBar->GetPageCount(); // actually it is tab count + sal_uInt16 nOldLayerPos = pLayerBar->GetCurPageId(); // actually it is a tab nId + + /** + * Update for LayerTab + */ + pLayerBar->Clear(); + + OUString aName; // a real layer name + OUString aActiveLayer = mpDrawView->GetActiveLayer(); + sal_uInt16 nActiveLayerPos = SDRLAYERPOS_NOTFOUND; + SdrLayerAdmin& rLayerAdmin = GetDoc()->GetLayerAdmin(); + sal_uInt16 nLayerCnt = rLayerAdmin.GetLayerCount(); + + for ( sal_uInt16 nLayerPos = 0; nLayerPos < nLayerCnt; nLayerPos++ ) + { + aName = rLayerAdmin.GetLayer(nLayerPos)->GetName(); + + if ( aName == aActiveLayer ) + { + nActiveLayerPos = nLayerPos; + } + + if ( aName != sUNO_LayerName_background ) // layer "background" has never a tab + { + if (meEditMode == EditMode::MasterPage) + { + // don't show page layer onto the masterpage + if (aName != sUNO_LayerName_layout && + aName != sUNO_LayerName_controls && + aName != sUNO_LayerName_measurelines) + { + TabBarPageBits nBits = TabBarPageBits::NONE; + SdrPageView* pPV = mpDrawView->GetSdrPageView(); + if (pPV) + { + if (!pPV->IsLayerVisible(aName)) + { + nBits |= TabBarPageBits::Blue; + } + if (pPV->IsLayerLocked(aName)) + { + nBits |= TabBarPageBits::Italic; + } + if (!pPV->IsLayerPrintable(aName)) + { + nBits |= TabBarPageBits::Underline; + } + } + + pLayerBar->InsertPage(nLayerPos+1, aName, nBits); // why +1? It is a nId, not a position. Position is APPEND. + } + } + else + { + // don't show masterpage layer onto the page + if (aName != sUNO_LayerName_background_objects) + { + TabBarPageBits nBits = TabBarPageBits::NONE; + if (!mpDrawView->GetSdrPageView()->IsLayerVisible(aName)) + { + nBits = TabBarPageBits::Blue; + } + if (mpDrawView->GetSdrPageView()->IsLayerLocked(aName)) + { + nBits |= TabBarPageBits::Italic; + } + if (!mpDrawView->GetSdrPageView()->IsLayerPrintable(aName)) + { + nBits |= TabBarPageBits::Underline; + } + + pLayerBar->InsertPage(nLayerPos+1, aName, nBits);// why +1? + } + } + } + } + + if ( nActiveLayerPos == SDRLAYERPOS_NOTFOUND ) + { + if( nOldLayerCnt == pLayerBar->GetPageCount() ) + { + nActiveLayerPos = nOldLayerPos - 1; + } + else + { + nActiveLayerPos = ( meEditMode == EditMode::MasterPage ) ? 2 : 0; + } + + mpDrawView->SetActiveLayer( pLayerBar->GetLayerName(nActiveLayerPos + 1) );// why +1? + } + + pLayerBar->SetCurPageId(nActiveLayerPos + 1); + GetViewFrame()->GetBindings().Invalidate( SID_MODIFYLAYER ); + GetViewFrame()->GetBindings().Invalidate( SID_DELETE_LAYER ); +} + +/** + * AcceptDrop + */ + +sal_Int8 DrawViewShell::AcceptDrop ( + const AcceptDropEvent& rEvt, + DropTargetHelper& rTargetHelper, + ::sd::Window* /*pTargetWindow*/, + sal_uInt16 /*nPage*/, + SdrLayerID nLayer ) +{ + if( SlideShow::IsRunning( GetViewShellBase() ) ) + return DND_ACTION_NONE; + + return mpDrawView->AcceptDrop( rEvt, rTargetHelper, nLayer ); +} + +/** + * ExecuteDrop + */ + +sal_Int8 DrawViewShell::ExecuteDrop ( + const ExecuteDropEvent& rEvt, + DropTargetHelper& /*rTargetHelper*/, + ::sd::Window* pTargetWindow, + sal_uInt16 nPage, + SdrLayerID nLayer) +{ + if( nPage != SDRPAGE_NOTFOUND ) + nPage = GetDoc()->GetSdPage( nPage, mePageKind )->GetPageNum(); + + if( SlideShow::IsRunning( GetViewShellBase() ) ) + return DND_ACTION_NONE; + + Broadcast(ViewShellHint(ViewShellHint::HINT_COMPLEX_MODEL_CHANGE_START)); + sal_Int8 nResult (mpDrawView->ExecuteDrop( rEvt, pTargetWindow, nPage, nLayer )); + Broadcast(ViewShellHint(ViewShellHint::HINT_COMPLEX_MODEL_CHANGE_END)); + + return nResult; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviews2.cxx b/sd/source/ui/view/drviews2.cxx new file mode 100644 index 000000000..8be942743 --- /dev/null +++ b/sd/source/ui/view/drviews2.cxx @@ -0,0 +1,4004 @@ +/* -*- 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 <config_features.h> + +#include <avmedia/mediaplayer.hxx> + +#include <basic/sberrors.hxx> +#include <basic/sbstar.hxx> + +#include <com/sun/star/drawing/XMasterPagesSupplier.hpp> +#include <com/sun/star/drawing/XDrawPages.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp> +#include <com/sun/star/ui/dialogs/XSLTFilterDialog.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/util/URLTransformer.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> +#include <com/sun/star/scanner/XScannerManager2.hpp> +#include <com/sun/star/document/XDocumentProperties.hpp> + +#include <comphelper/processfactory.hxx> +#include <comphelper/propertysequence.hxx> +#include <comphelper/scopeguard.hxx> +#include <comphelper/lok.hxx> + +#include <editeng/contouritem.hxx> +#include <editeng/editdata.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/flditem.hxx> +#include <editeng/section.hxx> +#include <editeng/editobj.hxx> +#include <editeng/CustomPropertyField.hxx> +#include <editeng/urlfieldhelper.hxx> + +#include <sal/log.hxx> + +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/msgpool.hxx> +#include <sfx2/msg.hxx> +#include <sfx2/request.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/zoomitem.hxx> + +#include <svx/compressgraphicdialog.hxx> +#include <svx/ClassificationDialog.hxx> +#include <svx/ClassificationCommon.hxx> +#include <svx/bmpmask.hxx> +#include <svx/extedit.hxx> +#include <svx/extrusionbar.hxx> +#include <svx/f3dchild.hxx> +#include <svx/fontwork.hxx> +#include <svx/fontworkbar.hxx> +#include <svx/graphichelper.hxx> +#include <svx/hlnkitem.hxx> +#include <svx/imapdlg.hxx> +#include <svx/sdtagitm.hxx> +#include <svx/svdograf.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdpagv.hxx> +#include <svx/svdundo.hxx> +#include <svx/svxdlg.hxx> +#include <svx/svxids.hrc> +#include <svx/sdtfsitm.hxx> +#include <svx/sdmetitm.hxx> +#include <svx/zoomslideritem.hxx> +#include <svx/xflclit.hxx> +#include <svx/xlnwtit.hxx> +#include <svx/chrtitem.hxx> +#include <svx/xlnclit.hxx> +#include <svx/xflgrit.hxx> + +#include <tools/diagnose_ex.h> +#include <tools/UnitConversion.hxx> + +#include <unotools/useroptions.hxx> + +#include <vcl/abstdlg.hxx> +#include <vcl/graph.hxx> +#include <vcl/svapp.hxx> +#include <vcl/unohelp2.hxx> +#include <vcl/weld.hxx> + +#include <editeng/cmapitem.hxx> +#include <editeng/escapementitem.hxx> +#include <editeng/kernitem.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/postitem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/crossedoutitem.hxx> +#include <editeng/shdditem.hxx> +#include <editeng/numitem.hxx> +#include <svx/svdobj.hxx> +#include <svx/SvxColorChildWindow.hxx> +#include <editeng/outlobj.hxx> +#include <editeng/flstitem.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/colritem.hxx> + +#include <svl/poolitem.hxx> +#include <svl/style.hxx> +#include <svl/whiter.hxx> + +#include <app.hrc> +#include <strings.hrc> + +#include <AnimationChildWindow.hxx> +#include <DrawDocShell.hxx> +#include <DrawViewShell.hxx> +#include <LayerTabBar.hxx> +#include <Outliner.hxx> +#include <ViewShellHint.hxx> +#include <ViewShellImplementation.hxx> +#include <Window.hxx> +#include <drawdoc.hxx> +#include <drawview.hxx> +#include <fuarea.hxx> +#include <fubullet.hxx> +#include <fuchar.hxx> +#include <fucushow.hxx> +#include <fuconnct.hxx> +#include <fucopy.hxx> +#include <fudspord.hxx> +#include <fuexecuteinteraction.hxx> +#include <fuexpand.hxx> +#include <fuinsert.hxx> +#include <fuinsfil.hxx> +#include <fuline.hxx> +#include <fulinend.hxx> +#include <fulink.hxx> +#include <fumeasur.hxx> +#include <fumorph.hxx> +#include <fuoaprms.hxx> +#include <fuolbull.hxx> +#include <fupage.hxx> +#include <fuparagr.hxx> +#include <fuprlout.hxx> +#include <fuscale.hxx> +#include <fusel.hxx> +#include <fusldlg.hxx> +#include <fusnapln.hxx> +#include <fusumry.hxx> +#include <futempl.hxx> +#include <futhes.hxx> +#include <futransf.hxx> +#include <futxtatt.hxx> +#include <fuvect.hxx> +#include <futext.hxx> +#include <helpids.h> +#include <sdabstdlg.hxx> +#include <sdattr.hxx> +#include <sdpage.hxx> +#include <sdresid.hxx> +#include <unokywds.hxx> +#include <slideshow.hxx> +#include <stlsheet.hxx> +#include <undolayer.hxx> +#include <sfx2/sidebar/Sidebar.hxx> +#include <sfx2/classificationhelper.hxx> +#include <sdmod.hxx> +#include <model/SlsPageDescriptor.hxx> +#include <model/SlsPageEnumerationProvider.hxx> +#include <SlideSorter.hxx> +#include <view/SlideSorterView.hxx> +#include <SlideSorterViewShell.hxx> +#include <controller/SlideSorterController.hxx> +#include <controller/SlsPageSelector.hxx> +#include <tools/GraphicSizeCheck.hxx> + +#include <ViewShellBase.hxx> +#include <memory> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +#define MIN_ACTIONS_FOR_DIALOG 5000 ///< if there are more meta objects, we show a dialog during the break up + +namespace sd { + +namespace { + +const SvxFieldItem* findField(editeng::Section const & rSection) +{ + for (SfxPoolItem const * pPool: rSection.maAttributes) + { + if (pPool->Which() == EE_FEATURE_FIELD) + return static_cast<const SvxFieldItem*>(pPool); + } + return nullptr; +} + +bool hasCustomPropertyField(std::vector<editeng::Section> const & aSections, std::u16string_view rName) +{ + for (editeng::Section const & rSection : aSections) + { + const SvxFieldItem* pFieldItem = findField(rSection); + if (pFieldItem) + { + const editeng::CustomPropertyField* pCustomPropertyField = dynamic_cast<const editeng::CustomPropertyField*>(pFieldItem->GetField()); + if (pCustomPropertyField && pCustomPropertyField->GetName() == rName) + return true; + } + } + return false; +} + +OUString getWeightString(SfxItemSet const & rItemSet) +{ + OUString sWeightString = "NORMAL"; + + if (const SfxPoolItem* pItem = rItemSet.GetItem(EE_CHAR_WEIGHT, false)) + { + const SvxWeightItem* pWeightItem = dynamic_cast<const SvxWeightItem*>(pItem); + if (pWeightItem && pWeightItem->GetWeight() == WEIGHT_BOLD) + sWeightString = "BOLD"; + } + return sWeightString; +} + +class ClassificationCommon +{ +protected: + sd::DrawViewShell& m_rDrawViewShell; + uno::Reference<document::XDocumentProperties> m_xDocumentProperties; + uno::Reference<beans::XPropertyContainer> m_xPropertyContainer; + sfx::ClassificationKeyCreator m_aKeyCreator; +public: + ClassificationCommon(sd::DrawViewShell& rDrawViewShell, const css::uno::Reference<css::document::XDocumentProperties>& rDocProps) + : m_rDrawViewShell(rDrawViewShell) + , m_xDocumentProperties(rDocProps) + , m_xPropertyContainer(m_xDocumentProperties->getUserDefinedProperties()) + , m_aKeyCreator(SfxClassificationHelper::getPolicyType()) + {} +}; + +class ClassificationCollector : public ClassificationCommon +{ +private: + std::vector<svx::ClassificationResult> m_aResults; + + void iterateSectionsAndCollect(std::vector<editeng::Section> const & rSections, EditTextObject const & rEditText) + { + sal_Int32 nCurrentParagraph = -1; + OUString sBlank; + + for (editeng::Section const & rSection : rSections) + { + // Insert new paragraph if needed + while (nCurrentParagraph < rSection.mnParagraph) + { + nCurrentParagraph++; + // Get Weight of current paragraph + OUString sWeightProperty = getWeightString(rEditText.GetParaAttribs(nCurrentParagraph)); + // Insert new paragraph into collection + m_aResults.push_back({ svx::ClassificationType::PARAGRAPH, sWeightProperty, sBlank, sBlank }); + } + + const SvxFieldItem* pFieldItem = findField(rSection); + const editeng::CustomPropertyField* pCustomPropertyField = pFieldItem ? + dynamic_cast<const editeng::CustomPropertyField*>(pFieldItem->GetField()) : + nullptr; + if (pCustomPropertyField) + { + const OUString& aKey = pCustomPropertyField->GetName(); + if (m_aKeyCreator.isMarkingTextKey(aKey)) + { + OUString aValue = svx::classification::getProperty(m_xPropertyContainer, aKey); + m_aResults.push_back({ svx::ClassificationType::TEXT, aValue, sBlank, sBlank }); + } + else if (m_aKeyCreator.isCategoryNameKey(aKey) || m_aKeyCreator.isCategoryIdentifierKey(aKey)) + { + OUString aValue = svx::classification::getProperty(m_xPropertyContainer, aKey); + m_aResults.push_back({ svx::ClassificationType::CATEGORY, aValue, sBlank, sBlank }); + } + else if (m_aKeyCreator.isMarkingKey(aKey)) + { + OUString aValue = svx::classification::getProperty(m_xPropertyContainer, aKey); + m_aResults.push_back({ svx::ClassificationType::MARKING, aValue, sBlank, sBlank }); + } + else if (m_aKeyCreator.isIntellectualPropertyPartKey(aKey)) + { + OUString aValue = svx::classification::getProperty(m_xPropertyContainer, aKey); + m_aResults.push_back({ svx::ClassificationType::INTELLECTUAL_PROPERTY_PART, aValue, sBlank, sBlank }); + } + } + } + } + +public: + ClassificationCollector(sd::DrawViewShell & rDrawViewShell, const css::uno::Reference<css::document::XDocumentProperties>& rDocProps) + : ClassificationCommon(rDrawViewShell, rDocProps) + {} + + std::vector<svx::ClassificationResult> const & getResults() const + { + return m_aResults; + } + + void collect() + { + // Set to MASTER mode + EditMode eOldMode = m_rDrawViewShell.GetEditMode(); + if (eOldMode != EditMode::MasterPage) + m_rDrawViewShell.ChangeEditMode(EditMode::MasterPage, false); + + // Scoped guard to revert to the previous mode + comphelper::ScopeGuard const aGuard([this, eOldMode] () { + m_rDrawViewShell.ChangeEditMode(eOldMode, false); + }); + + const sal_uInt16 nCount = m_rDrawViewShell.GetDoc()->GetMasterSdPageCount(PageKind::Standard); + + for (sal_uInt16 nPageIndex = 0; nPageIndex < nCount; ++nPageIndex) + { + SdPage* pMasterPage = m_rDrawViewShell.GetDoc()->GetMasterSdPage(nPageIndex, PageKind::Standard); + for (size_t nObject = 0; nObject < pMasterPage->GetObjCount(); ++nObject) + { + SdrObject* pObject = pMasterPage->GetObj(nObject); + SdrRectObj* pRectObject = dynamic_cast<SdrRectObj*>(pObject); + if (pRectObject && pRectObject->GetTextKind() == SdrObjKind::Text) + { + OutlinerParaObject* pOutlinerParagraphObject = pRectObject->GetOutlinerParaObject(); + if (pOutlinerParagraphObject) + { + const EditTextObject& rEditText = pOutlinerParagraphObject->GetTextObject(); + std::vector<editeng::Section> aSections; + rEditText.GetAllSections(aSections); + + // Search for a custom property field that has the classification category identifier key + if (hasCustomPropertyField(aSections, m_aKeyCreator.makeCategoryNameKey())) + { + iterateSectionsAndCollect(aSections, rEditText); + return; + } + } + } + } + } + } +}; + +class ClassificationInserter : public ClassificationCommon +{ +private: + /// Delete the previous existing classification object(s) - if they exist + void deleteExistingObjects() + { + OUString sKey = m_aKeyCreator.makeCategoryNameKey(); + + const sal_uInt16 nCount = m_rDrawViewShell.GetDoc()->GetMasterSdPageCount(PageKind::Standard); + + for (sal_uInt16 nPageIndex = 0; nPageIndex < nCount; ++nPageIndex) + { + SdPage* pMasterPage = m_rDrawViewShell.GetDoc()->GetMasterSdPage(nPageIndex, PageKind::Standard); + for (size_t nObject = 0; nObject < pMasterPage->GetObjCount(); ++nObject) + { + SdrObject* pObject = pMasterPage->GetObj(nObject); + SdrRectObj* pRectObject = dynamic_cast<SdrRectObj*>(pObject); + if (pRectObject && pRectObject->GetTextKind() == SdrObjKind::Text) + { + OutlinerParaObject* pOutlinerParagraphObject = pRectObject->GetOutlinerParaObject(); + if (pOutlinerParagraphObject) + { + const EditTextObject& rEditText = pOutlinerParagraphObject->GetTextObject(); + std::vector<editeng::Section> aSections; + rEditText.GetAllSections(aSections); + + if (hasCustomPropertyField(aSections, sKey)) + { + pMasterPage->RemoveObject(pRectObject->GetOrdNum()); + } + } + } + } + } + } + + void fillTheOutliner(Outliner* pOutliner, std::vector<svx::ClassificationResult> const & rResults) + { + sal_Int32 nParagraph = -1; + for (svx::ClassificationResult const & rResult : rResults) + { + + ESelection aPosition(nParagraph, EE_TEXTPOS_MAX_COUNT, nParagraph, EE_TEXTPOS_MAX_COUNT); + + switch (rResult.meType) + { + case svx::ClassificationType::TEXT: + { + OUString sKey = m_aKeyCreator.makeNumberedTextKey(); + svx::classification::addOrInsertDocumentProperty(m_xPropertyContainer, sKey, rResult.msName); + pOutliner->QuickInsertField(SvxFieldItem(editeng::CustomPropertyField(sKey, rResult.msName), EE_FEATURE_FIELD), aPosition); + } + break; + + case svx::ClassificationType::CATEGORY: + { + OUString sKey = m_aKeyCreator.makeCategoryNameKey(); + pOutliner->QuickInsertField(SvxFieldItem(editeng::CustomPropertyField(sKey, rResult.msName), EE_FEATURE_FIELD), aPosition); + } + break; + + case svx::ClassificationType::MARKING: + { + OUString sKey = m_aKeyCreator.makeNumberedMarkingKey(); + svx::classification::addOrInsertDocumentProperty(m_xPropertyContainer, sKey, rResult.msName); + pOutliner->QuickInsertField(SvxFieldItem(editeng::CustomPropertyField(sKey, rResult.msName), EE_FEATURE_FIELD), aPosition); + } + break; + + case svx::ClassificationType::INTELLECTUAL_PROPERTY_PART: + { + OUString sKey = m_aKeyCreator.makeNumberedIntellectualPropertyPartKey(); + svx::classification::addOrInsertDocumentProperty(m_xPropertyContainer, sKey, rResult.msName); + pOutliner->QuickInsertField(SvxFieldItem(editeng::CustomPropertyField(sKey, rResult.msName), EE_FEATURE_FIELD), aPosition); + } + break; + + case svx::ClassificationType::PARAGRAPH: + { + nParagraph++; + pOutliner->Insert(""); + + SfxItemSetFixed<EE_ITEMS_START, EE_ITEMS_END> aItemSet(m_rDrawViewShell.GetDoc()->GetPool()); + + if (rResult.msName == "BOLD") + aItemSet.Put(SvxWeightItem(WEIGHT_BOLD, EE_CHAR_WEIGHT)); + else + aItemSet.Put(SvxWeightItem(WEIGHT_NORMAL, EE_CHAR_WEIGHT)); + + SvxNumRule aDefaultNumRule(SvxNumRuleFlags::NONE, 0, false); + aItemSet.Put(SvxNumBulletItem(std::move(aDefaultNumRule), EE_PARA_NUMBULLET)); + + pOutliner->SetParaAttribs(nParagraph, aItemSet); + } + break; + + default: + break; + } + } + } + +public: + ClassificationInserter(sd::DrawViewShell & rDrawViewShell, const css::uno::Reference<css::document::XDocumentProperties>& rDocProps) + : ClassificationCommon(rDrawViewShell, rDocProps) + { + } + + void insert(std::vector<svx::ClassificationResult> const & rResults) + { + // Set to MASTER mode + EditMode eOldMode = m_rDrawViewShell.GetEditMode(); + if (eOldMode != EditMode::MasterPage) + m_rDrawViewShell.ChangeEditMode(EditMode::MasterPage, false); + + // Scoped guard to revert the mode + comphelper::ScopeGuard const aGuard([this, eOldMode] () { + m_rDrawViewShell.ChangeEditMode(eOldMode, false); + }); + + // Delete the previous existing object - if exists + deleteExistingObjects(); + + // Clear properties + svx::classification::removeAllProperties(m_xPropertyContainer); + + SfxClassificationHelper aHelper(m_xDocumentProperties); + + // Apply properties from the BA policy + for (svx::ClassificationResult const & rResult : rResults) + { + if (rResult.meType == svx::ClassificationType::CATEGORY) + aHelper.SetBACName(rResult.msName, SfxClassificationHelper::getPolicyType()); + } + + // Insert full text as document property + svx::classification::insertFullTextualRepresentationAsDocumentProperty(m_xPropertyContainer, m_aKeyCreator, rResults); + + // Create the outliner from the + Outliner* pOutliner = m_rDrawViewShell.GetDoc()->GetInternalOutliner(); + OutlinerMode eOutlinerMode = pOutliner->GetOutlinerMode(); + + comphelper::ScopeGuard const aOutlinerGuard([pOutliner, eOutlinerMode] () { + pOutliner->Init(eOutlinerMode); + }); + + pOutliner->Init(OutlinerMode::TextObject); + + // Fill the outliner with the text from classification result + fillTheOutliner(pOutliner, rResults); + + // Calculate to outliner text size + pOutliner->UpdateFields(); + pOutliner->SetUpdateLayout(true); + Size aTextSize(pOutliner->CalcTextSize()); + pOutliner->SetUpdateLayout(false); + + // Create objects, apply the outliner and add them (objects) to all master pages + const sal_uInt16 nCount = m_rDrawViewShell.GetDoc()->GetMasterSdPageCount(PageKind::Standard); + + for (sal_uInt16 nPageIndex = 0; nPageIndex < nCount; ++nPageIndex) + { + SdPage* pMasterPage = m_rDrawViewShell.GetDoc()->GetMasterSdPage(nPageIndex, PageKind::Standard); + if (!pMasterPage) + continue; + + SdrRectObj* pObject = new SdrRectObj( + *m_rDrawViewShell.GetDoc(), // TTTT should be reference + SdrObjKind::Text); + pObject->SetMergedItem(makeSdrTextAutoGrowWidthItem(true)); + pObject->SetOutlinerParaObject(pOutliner->CreateParaObject()); + pMasterPage->InsertObject(pObject); + + // Calculate position + ::tools::Rectangle aRectangle(Point(), pMasterPage->GetSize()); + Point aPosition(aRectangle.Center().X(), aRectangle.Bottom()); + + aPosition.AdjustX( -(aTextSize.Width() / 2) ); + aPosition.AdjustY( -(aTextSize.Height()) ); + + pObject->SetLogicRect(::tools::Rectangle(aPosition, aTextSize)); + } + } +}; + + void lcl_convertStringArguments(sal_uInt16 nSlot, const std::unique_ptr<SfxItemSet>& pArgs) + { + Color aColor; + const SfxPoolItem* pItem = nullptr; + + if (SfxItemState::SET == pArgs->GetItemState(SID_ATTR_LINE_WIDTH_ARG, false, &pItem)) + { + double fValue = static_cast<const SvxDoubleItem*>(pItem)->GetValue(); + // FIXME: different units... + int nPow = 100; + int nValue = fValue * nPow; + + XLineWidthItem aItem(nValue); + pArgs->Put(aItem); + } + if (SfxItemState::SET == pArgs->GetItemState(SID_ATTR_COLOR_STR, false, &pItem)) + { + OUString sColor = static_cast<const SfxStringItem*>(pItem)->GetValue(); + + if (sColor == "transparent") + aColor = COL_TRANSPARENT; + else + aColor = Color(ColorTransparency, sColor.toInt32(16)); + + switch (nSlot) + { + case SID_ATTR_LINE_COLOR: + { + XLineColorItem aLineColorItem(OUString(), aColor); + pArgs->Put(aLineColorItem); + break; + } + + case SID_ATTR_FILL_COLOR: + { + XFillColorItem aFillColorItem(OUString(), aColor); + pArgs->Put(aFillColorItem); + break; + } + } + } + if (SfxItemState::SET == pArgs->GetItemState(SID_FILL_GRADIENT_JSON, false, &pItem)) + { + const SfxStringItem* pJSON = static_cast<const SfxStringItem*>(pItem); + if (pJSON) + { + XGradient aGradient = XGradient::fromJSON(pJSON->GetValue()); + XFillGradientItem aItem(aGradient); + pArgs->Put(aItem); + } + } + + if (nSlot == SID_ATTR_FILL_COLOR) + { + // Merge the color parameters to the color itself. + const XFillColorItem* pColorItem = static_cast<const XFillColorItem*>(pArgs->GetItem(SID_ATTR_FILL_COLOR)); + if (pColorItem) + { + XFillColorItem aColorItem(*pColorItem); + if (pArgs->GetItemState(SID_ATTR_COLOR_THEME_INDEX, false, &pItem) == SfxItemState::SET) + { + auto pIntItem = static_cast<const SfxInt16Item*>(pItem); + aColorItem.GetThemeColor().SetThemeIndex(pIntItem->GetValue()); + } + if (pArgs->GetItemState(SID_ATTR_COLOR_LUM_MOD, false, &pItem) == SfxItemState::SET) + { + auto pIntItem = static_cast<const SfxInt16Item*>(pItem); + aColorItem.GetThemeColor().SetLumMod(pIntItem->GetValue()); + } + if (pArgs->GetItemState(SID_ATTR_COLOR_LUM_OFF, false, &pItem) == SfxItemState::SET) + { + auto pIntItem = static_cast<const SfxInt16Item*>(pItem); + aColorItem.GetThemeColor().SetLumOff(pIntItem->GetValue()); + } + pArgs->Put(aColorItem); + } + } + } +} + +/** + * SfxRequests for temporary actions + */ + +void DrawViewShell::FuTemporary(SfxRequest& rReq) +{ + // during a native slide show nothing gets executed! + if(SlideShow::IsRunning( GetViewShellBase() ) && (rReq.GetSlot() != SID_NAVIGATOR)) + return; + + DBG_ASSERT( mpDrawView, "sd::DrawViewShell::FuTemporary(), no draw view!" ); + if( !mpDrawView ) + return; + + CheckLineTo (rReq); + + DeactivateCurrentFunction(); + + sal_uInt16 nSId = rReq.GetSlot(); + + switch ( nSId ) + { + case SID_OUTLINE_TEXT_AUTOFIT: + { + SfxUndoManager* pUndoManager = GetDocSh()->GetUndoManager(); + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + if( rMarkList.GetMarkCount() == 1 ) + { + pUndoManager->EnterListAction("", "", 0, GetViewShellBase().GetViewShellId()); + mpDrawView->BegUndo(); + + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + bool bSet = pObj->GetMergedItemSet().GetItem<SdrTextFitToSizeTypeItem>(SDRATTR_TEXT_FITTOSIZE)->GetValue() != drawing::TextFitToSizeType_NONE; + + mpDrawView->AddUndo(GetDoc()->GetSdrUndoFactory().CreateUndoAttrObject(*pObj)); + + if (!bSet) + { + //If we are turning on AutoFit we have to turn these off if already on + if (pObj->GetMergedItemSet().GetItem<SdrOnOffItem>(SDRATTR_TEXT_AUTOGROWHEIGHT)->GetValue()) + pObj->SetMergedItem(makeSdrTextAutoGrowHeightItem(false)); + if (pObj->GetMergedItemSet().GetItem<SdrOnOffItem>(SDRATTR_TEXT_AUTOGROWWIDTH)->GetValue()) + pObj->SetMergedItem(makeSdrTextAutoGrowWidthItem(false)); + } + + pObj->SetMergedItem(SdrTextFitToSizeTypeItem(bSet ? drawing::TextFitToSizeType_NONE : drawing::TextFitToSizeType_AUTOFIT)); + + mpDrawView->EndUndo(); + pUndoManager->LeaveListAction(); + } + Cancel(); + rReq.Done(); + } + break; + + // area and line attributes: shall have + // an own Execute method (like StateMethode) + case SID_ATTR_FILL_STYLE: + case SID_ATTR_FILL_COLOR: + case SID_ATTR_FILL_GRADIENT: + case SID_ATTR_FILL_HATCH: + case SID_ATTR_FILL_BITMAP: + case SID_ATTR_FILL_SHADOW: + case SID_ATTR_SHADOW_COLOR: + case SID_ATTR_SHADOW_TRANSPARENCE: + case SID_ATTR_SHADOW_BLUR: + case SID_ATTR_SHADOW_XDISTANCE: + case SID_ATTR_SHADOW_YDISTANCE: + case SID_ATTR_FILL_USE_SLIDE_BACKGROUND: + case SID_ATTR_FILL_TRANSPARENCE: + case SID_ATTR_FILL_FLOATTRANSPARENCE: + + case SID_ATTR_LINE_STYLE: + case SID_ATTR_LINE_DASH: + case SID_ATTR_LINE_WIDTH: + case SID_ATTR_LINE_COLOR: + case SID_ATTR_LINEEND_STYLE: + case SID_ATTR_LINE_START: + case SID_ATTR_LINE_END: + case SID_ATTR_LINE_TRANSPARENCE: + case SID_ATTR_LINE_JOINT: + case SID_ATTR_LINE_CAP: + + case SID_ATTR_TEXT_FITTOSIZE: + { + if( rReq.GetArgs() ) + { + std::unique_ptr<SfxItemSet> pNewArgs = rReq.GetArgs()->Clone(); + lcl_convertStringArguments(rReq.GetSlot(), pNewArgs); + mpDrawView->SetAttributes(*pNewArgs); + rReq.Done(); + } + else + { + switch( rReq.GetSlot() ) + { + case SID_ATTR_FILL_SHADOW: + case SID_ATTR_SHADOW_COLOR: + case SID_ATTR_SHADOW_TRANSPARENCE: + case SID_ATTR_SHADOW_BLUR: + case SID_ATTR_SHADOW_XDISTANCE: + case SID_ATTR_SHADOW_YDISTANCE: + case SID_ATTR_FILL_STYLE: + case SID_ATTR_FILL_COLOR: + case SID_ATTR_FILL_GRADIENT: + case SID_ATTR_FILL_HATCH: + case SID_ATTR_FILL_BITMAP: + case SID_ATTR_FILL_USE_SLIDE_BACKGROUND: + case SID_ATTR_FILL_TRANSPARENCE: + case SID_ATTR_FILL_FLOATTRANSPARENCE: + GetViewFrame()->GetDispatcher()->Execute( SID_ATTRIBUTES_AREA, SfxCallMode::ASYNCHRON ); + break; + case SID_ATTR_LINE_STYLE: + case SID_ATTR_LINE_DASH: + case SID_ATTR_LINE_WIDTH: + case SID_ATTR_LINE_COLOR: + case SID_ATTR_LINE_TRANSPARENCE: + case SID_ATTR_LINE_JOINT: + case SID_ATTR_LINE_CAP: + GetViewFrame()->GetDispatcher()->Execute( SID_ATTRIBUTES_LINE, SfxCallMode::ASYNCHRON ); + break; + case SID_ATTR_TEXT_FITTOSIZE: + GetViewFrame()->GetDispatcher()->Execute( SID_TEXTATTR_DLG, SfxCallMode::ASYNCHRON ); + break; + } + } + Cancel(); + } + break; + + case SID_HYPHENATION: + { + const SfxBoolItem* pItem = rReq.GetArg<SfxBoolItem>(SID_HYPHENATION); + + if( pItem ) + { + SfxItemSetFixed<EE_PARA_HYPHENATE, EE_PARA_HYPHENATE> aSet( GetPool() ); + bool bValue = pItem->GetValue(); + aSet.Put( SfxBoolItem( EE_PARA_HYPHENATE, bValue ) ); + mpDrawView->SetAttributes( aSet ); + } + else // only for testing purpose + { + OSL_FAIL(" no value for hyphenation!"); + SfxItemSetFixed<EE_PARA_HYPHENATE, EE_PARA_HYPHENATE> aSet( GetPool() ); + aSet.Put( SfxBoolItem( EE_PARA_HYPHENATE, true ) ); + mpDrawView->SetAttributes( aSet ); + } + rReq.Done(); + Cancel(); + } + break; + + case SID_INSERTPAGE: + case SID_INSERTPAGE_QUICK: + { + SdPage* pNewPage = CreateOrDuplicatePage (rReq, mePageKind, GetActualPage()); + Cancel(); + if(HasCurrentFunction(SID_BEZIER_EDIT) ) + GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + if (pNewPage != nullptr) + SwitchPage((pNewPage->GetPageNum()-1)/2); + rReq.Done (); + } + break; + + case SID_DUPLICATE_PAGE: + { + auto slideSorter = sd::slidesorter::SlideSorterViewShell::GetSlideSorter(GetViewShellBase()); + SdPage* pNewPage = nullptr; + if(slideSorter) + DuplicateSelectedSlides(rReq); + else + pNewPage = CreateOrDuplicatePage (rReq, mePageKind, GetActualPage()); + Cancel(); + if(HasCurrentFunction(SID_BEZIER_EDIT) ) + GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + if(!slideSorter && pNewPage != nullptr) + SwitchPage((pNewPage->GetPageNum()-1)/2); + rReq.Done(); + } + break; + + case SID_INSERT_MASTER_PAGE: + { + // Use the API to create a new page. + Reference<drawing::XMasterPagesSupplier> xMasterPagesSupplier ( + GetDoc()->getUnoModel(), UNO_QUERY); + if (xMasterPagesSupplier.is()) + { + Reference<drawing::XDrawPages> xMasterPages ( + xMasterPagesSupplier->getMasterPages()); + if (xMasterPages.is()) + { + sal_uInt16 nIndex = GetCurPagePos() + 1; + xMasterPages->insertNewByIndex (nIndex); + + // Create shapes for the default layout. + SdPage* pMasterPage = GetDoc()->GetMasterSdPage( + nIndex, PageKind::Standard); + pMasterPage->CreateTitleAndLayout (true,true); + } + } + + Cancel(); + if(HasCurrentFunction(SID_BEZIER_EDIT)) + GetViewFrame()->GetDispatcher()->Execute( + SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + rReq.Done (); + } + break; + + case SID_MODIFYPAGE: + { + if (mePageKind==PageKind::Standard || mePageKind==PageKind::Notes || + (mePageKind==PageKind::Handout && meEditMode==EditMode::MasterPage) ) + { + if ( mpDrawView->IsTextEdit() ) + { + mpDrawView->SdrEndTextEdit(); + } + sal_uInt16 nPage = maTabControl->GetCurPagePos(); + mpActualPage = GetDoc()->GetSdPage(nPage, mePageKind); + ::sd::ViewShell::mpImpl->ProcessModifyPageSlot ( + rReq, + mpActualPage, + mePageKind); + } + + Cancel(); + rReq.Done (); + } + break; + + case SID_ASSIGN_LAYOUT: + { + if (mePageKind==PageKind::Standard || mePageKind==PageKind::Notes || (mePageKind==PageKind::Handout && meEditMode==EditMode::MasterPage)) + { + if ( mpDrawView->IsTextEdit() ) + mpDrawView->SdrEndTextEdit(); + + ::sd::ViewShell::mpImpl->AssignLayout(rReq, mePageKind); + } + Cancel(); + rReq.Done (); + } + break; + + case SID_RENAMEPAGE: + case SID_RENAME_MASTER_PAGE: + { + if (mePageKind==PageKind::Standard || mePageKind==PageKind::Notes ) + { + if ( mpDrawView->IsTextEdit() ) + { + mpDrawView->SdrEndTextEdit(); + } + + sal_uInt16 nPage = maTabControl->GetCurPagePos(); + SdPage* pCurrentPage = ( GetEditMode() == EditMode::Page ) + ? GetDoc()->GetSdPage( nPage, GetPageKind() ) + : GetDoc()->GetMasterSdPage( nPage, GetPageKind() ); + + OUString aTitle = SdResId(STR_TITLE_RENAMESLIDE); + OUString aDescr = SdResId(STR_DESC_RENAMESLIDE); + const OUString& aPageName = pCurrentPage->GetName(); + + if(rReq.GetArgs()) + { + OUString aName = rReq.GetArgs()->GetItem<const SfxStringItem>(SID_RENAMEPAGE)->GetValue(); + + bool bResult = RenameSlide( maTabControl->GetPageId(nPage), aName ); + DBG_ASSERT( bResult, "Couldn't rename slide" ); + } + else + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSvxNameDialog> aNameDlg(pFact->CreateSvxNameDialog(GetFrameWeld(), aPageName, aDescr)); + aNameDlg->SetText( aTitle ); + aNameDlg->SetCheckNameHdl( LINK( this, DrawViewShell, RenameSlideHdl ), true ); + aNameDlg->SetEditHelpId( HID_SD_NAMEDIALOG_PAGE ); + + if( aNameDlg->Execute() == RET_OK ) + { + OUString aNewName; + aNameDlg->GetName( aNewName ); + if (aNewName != aPageName) + { + bool bResult = RenameSlide( maTabControl->GetPageId(nPage), aNewName ); + DBG_ASSERT( bResult, "Couldn't rename slide" ); + } + } + } + } + Cancel(); + rReq.Ignore(); + } + break; + + case SID_RENAMEPAGE_QUICK: + { + if (mePageKind==PageKind::Standard || mePageKind==PageKind::Notes ) + { + if ( mpDrawView->IsTextEdit() ) + { + mpDrawView->SdrEndTextEdit(); + } + + maTabControl->StartEditMode( maTabControl->GetCurPageId() ); + } + + Cancel(); + rReq.Ignore (); + } + break; + + case SID_PAGESIZE : // either this (no menu entries or something else!) + { + const SfxItemSet *pArgs = rReq.GetArgs (); + + if (pArgs && pArgs->Count () == 3) + { + const SfxUInt32Item* pWidth = rReq.GetArg<SfxUInt32Item>(ID_VAL_PAGEWIDTH); + const SfxUInt32Item* pHeight = rReq.GetArg<SfxUInt32Item>(ID_VAL_PAGEHEIGHT); + const SfxBoolItem* pScaleAll = rReq.GetArg<SfxBoolItem>(ID_VAL_SCALEOBJECTS); + + Size aSize (pWidth->GetValue (), pHeight->GetValue ()); + + SetupPage (aSize, 0, 0, 0, 0, true, false, pScaleAll->GetValue ()); + rReq.Ignore (); + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + rReq.Ignore (); + break; + } + + case SID_PAGEMARGIN : // or this (no menu entries or something else!) + { + const SfxItemSet *pArgs = rReq.GetArgs (); + + if (pArgs && pArgs->Count () == 5) + { + const SfxUInt32Item* pLeft = rReq.GetArg<SfxUInt32Item>(ID_VAL_PAGELEFT); + const SfxUInt32Item* pRight = rReq.GetArg<SfxUInt32Item>(ID_VAL_PAGERIGHT); + const SfxUInt32Item* pUpper = rReq.GetArg<SfxUInt32Item>(ID_VAL_PAGETOP); + const SfxUInt32Item* pLower = rReq.GetArg<SfxUInt32Item>(ID_VAL_PAGEBOTTOM); + const SfxBoolItem* pScaleAll = rReq.GetArg<SfxBoolItem>(ID_VAL_SCALEOBJECTS); + + Size aEmptySize (0, 0); + + SetupPage (aEmptySize, pLeft->GetValue (), pRight->GetValue (), + pUpper->GetValue (), pLower->GetValue (), + false, true, pScaleAll->GetValue ()); + rReq.Ignore (); + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + rReq.Ignore (); + break; + } + + case SID_ATTR_ZOOMSLIDER: + { + const SfxItemSet* pArgs = rReq.GetArgs(); + + const SfxUInt16Item* pScale = (pArgs && pArgs->Count () == 1) ? + rReq.GetArg(SID_ATTR_ZOOMSLIDER) : nullptr; + if (pScale && CHECK_RANGE (5, pScale->GetValue (), 3000)) + { + SetZoom (pScale->GetValue ()); + + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_ATTR_ZOOM ); + rBindings.Invalidate( SID_ZOOM_IN ); + rBindings.Invalidate( SID_ZOOM_OUT ); + rBindings.Invalidate( SID_ATTR_ZOOMSLIDER ); + + } + + Cancel(); + rReq.Done (); + break; + } + + case SID_ATTR_ZOOM: + { + const SfxItemSet* pArgs = rReq.GetArgs(); + mbZoomOnPage = false; + + if ( pArgs ) + { + SvxZoomType eZT = pArgs->Get( SID_ATTR_ZOOM ).GetType(); + switch( eZT ) + { + case SvxZoomType::PERCENT: + SetZoom( static_cast<::tools::Long>( pArgs->Get( SID_ATTR_ZOOM ).GetValue()) ); + break; + + case SvxZoomType::OPTIMAL: + GetViewFrame()->GetDispatcher()->Execute( SID_SIZE_ALL, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD ); + break; + + case SvxZoomType::PAGEWIDTH: + GetViewFrame()->GetDispatcher()->Execute( SID_SIZE_PAGE_WIDTH, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD ); + break; + + case SvxZoomType::WHOLEPAGE: + GetViewFrame()->GetDispatcher()->Execute( SID_SIZE_PAGE, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD ); + break; + case SvxZoomType::PAGEWIDTH_NOBORDER: + OSL_FAIL("sd::DrawViewShell::FuTemporary(), SvxZoomType::PAGEWIDTH_NOBORDER not handled!" ); + break; + } + rReq.Ignore (); + } + else + { + // open zoom dialog + SetCurrentFunction( FuScale::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + } + Cancel(); + } + break; + + case SID_CHANGEBEZIER: + case SID_CHANGEPOLYGON: + if ( mpDrawView->IsTextEdit() ) + { + mpDrawView->SdrEndTextEdit(); + GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + } + + if ( mpDrawView->IsPresObjSelected() ) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + if( rReq.GetSlot() == SID_CHANGEBEZIER ) + { + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->ConvertMarkedToPathObj(false); + } + else + { + if( mpDrawView->IsVectorizeAllowed() ) + { + SetCurrentFunction( FuVectorize::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + } + else + { + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->ConvertMarkedToPolyObj(); + } + } + + Invalidate(SID_CHANGEBEZIER); + Invalidate(SID_CHANGEPOLYGON); + } + Cancel(); + + if( HasCurrentFunction(SID_BEZIER_EDIT) ) + { // where applicable, activate right edit action + GetViewFrame()->GetDispatcher()->Execute(SID_SWITCH_POINTEDIT, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + } + rReq.Ignore (); + break; + + case SID_CONVERT_TO_CONTOUR: + if ( mpDrawView->IsTextEdit() ) + { + mpDrawView->SdrEndTextEdit(); + GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + } + + if ( mpDrawView->IsPresObjSelected() ) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->ConvertMarkedToPathObj(true); + + Invalidate(SID_CONVERT_TO_CONTOUR); + } + Cancel(); + + rReq.Ignore (); + break; + + case SID_CONVERT_TO_METAFILE: + case SID_CONVERT_TO_BITMAP: + { + // End text edit mode when it is active because the metafile or + // bitmap that will be created does not support it. + if ( mpDrawView->IsTextEdit() ) + { + mpDrawView->SdrEndTextEdit(); + GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + } + + if ( mpDrawView->IsPresObjSelected(true,true,true) ) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + weld::WaitObject aWait(GetFrameWeld()); + + // create SdrGrafObj from metafile/bitmap + Graphic aGraphic; + switch (nSId) + { + case SID_CONVERT_TO_METAFILE: + { + // switch on undo for the next operations + mpDrawView->BegUndo(SdResId(STR_UNDO_CONVERT_TO_METAFILE)); + GDIMetaFile aMetaFile(mpDrawView->GetMarkedObjMetaFile()); + aGraphic = Graphic(aMetaFile); + } + break; + case SID_CONVERT_TO_BITMAP: + { + // Disable spelling during conversion + bool bOnlineSpell = GetDoc()->GetOnlineSpell(); + GetDoc()->SetOnlineSpell(false); + + // switch on undo for the next operations + mpDrawView->BegUndo(SdResId(STR_UNDO_CONVERT_TO_BITMAP)); + bool bDone(false); + + // I have to get the image here directly since GetMarkedObjBitmapEx works + // based on Bitmaps, but not on BitmapEx, thus throwing away the alpha + // channel. Argh! GetMarkedObjBitmapEx itself is too widely used to safely + // change that, e.g. in the exchange formats. For now I can only add this + // exception to get good results for Svgs. This is how the code gets more + // and more crowded, at last I made a remark for myself to change this + // as one of the next tasks. + if(1 == mpDrawView->GetMarkedObjectCount()) + { + const SdrGrafObj* pSdrGrafObj = dynamic_cast< const SdrGrafObj* >(mpDrawView->GetMarkedObjectByIndex(0)); + + if(pSdrGrafObj && pSdrGrafObj->isEmbeddedVectorGraphicData()) + { + aGraphic = Graphic(pSdrGrafObj->GetGraphic().getVectorGraphicData()->getReplacement()); + bDone = true; + } + } + + if(!bDone) + { + aGraphic = Graphic(mpDrawView->GetMarkedObjBitmapEx()); + } + // Restore online spelling + GetDoc()->SetOnlineSpell(bOnlineSpell); + } + break; + } + + // create new object + SdrGrafObj* pGraphicObj = new SdrGrafObj( + *GetDoc(), + aGraphic); + + // get some necessary info and ensure it + const SdrMarkList& rMarkList(mpDrawView->GetMarkedObjectList()); + const size_t nMarkCount(rMarkList.GetMarkCount()); + SdrPageView* pPageView = mpDrawView->GetSdrPageView(); + OSL_ENSURE(nMarkCount, "DrawViewShell::FuTemporary: SID_CONVERT_TO_BITMAP with empty selection (!)"); + OSL_ENSURE(pPageView, "DrawViewShell::FuTemporary: SID_CONVERT_TO_BITMAP without SdrPageView (!)"); + + // fit rectangle of new graphic object to selection's mark rect + ::tools::Rectangle aAllMarkedRect; + rMarkList.TakeBoundRect(pPageView, aAllMarkedRect); + pGraphicObj->SetLogicRect(aAllMarkedRect); + + // #i71540# to keep the order, it is necessary to replace the lowest object + // of the selection with the new object. This also means that with multi + // selection, all other objects need to be deleted first + SdrMark* pFirstMark = rMarkList.GetMark(0); + SdrObject* pReplacementCandidate = pFirstMark->GetMarkedSdrObj(); + + if(nMarkCount > 1) + { + // take first object out of selection + mpDrawView->MarkObj(pReplacementCandidate, pPageView, true, true); + + // clear remaining selection + mpDrawView->DeleteMarkedObj(); + } + + // #i124816# copy layer from lowest object which gets replaced + pGraphicObj->SetLayer(pReplacementCandidate->GetLayer()); + + // now replace lowest object with new one + mpDrawView->ReplaceObjectAtView(pReplacementCandidate, *pPageView, pGraphicObj); + + // switch off undo + mpDrawView->EndUndo(); + } + } + + Cancel(); + + rReq.Done (); + break; + + case SID_REMOVE_HYPERLINK: + { + if (mpDrawView->IsTextEdit()) + { + // First make sure the field is selected + OutlinerView* pOutView = mpDrawView->GetTextEditOutlinerView(); + if (pOutView) + { + pOutView->SelectFieldAtCursor(); + URLFieldHelper::RemoveURLField(pOutView->GetEditView()); + } + } + } + Cancel(); + rReq.Done (); + break; + + case SID_SET_DEFAULT: + { + std::optional<SfxItemSet> pSet; + + if (mpDrawView->IsTextEdit()) + { + pSet.emplace( GetPool(), svl::Items<EE_ITEMS_START, EE_ITEMS_END> ); + mpDrawView->SetAttributes( *pSet, true ); + } + else + { + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + const size_t nCount = rMarkList.GetMarkCount(); + + // For every presentation object a SfxItemSet of hard attributes + // and the UserCall is stored in this list. This is because + // at the following mpDrawView->SetAttributes( *pSet, sal_True ) + // they get lost and have to be restored. + std::vector<std::pair<std::unique_ptr<SfxItemSet>,SdrObjUserCall*> > aAttrList; + SdPage* pPresPage = static_cast<SdPage*>( mpDrawView->GetSdrPageView()->GetPage() ); + + for ( size_t i = 0; i < nCount; ++i ) + { + SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj(); + + if( pPresPage->IsPresObj( pObj ) ) + { + auto pNewSet = std::make_unique<SfxItemSetFixed<SDRATTR_TEXT_MINFRAMEHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT>>( GetDoc()->GetPool() ); + pNewSet->Put(pObj->GetMergedItemSet()); + aAttrList.emplace_back(std::move(pNewSet), pObj->GetUserCall()); + } + } + + pSet.emplace( GetPool() ); + mpDrawView->SetAttributes( *pSet, true ); + + sal_uLong j = 0; + + for ( size_t i = 0; i < nCount; ++i ) + { + SfxStyleSheet* pSheet = nullptr; + SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj(); + + if (pObj->GetObjIdentifier() == SdrObjKind::TitleText) + { + pSheet = mpActualPage->GetStyleSheetForPresObj(PresObjKind::Title); + if (pSheet) + pObj->SetStyleSheet(pSheet, false); + } + else if(pObj->GetObjIdentifier() == SdrObjKind::OutlineText) + { + for (sal_uInt16 nLevel = 1; nLevel < 10; nLevel++) + { + pSheet = mpActualPage->GetStyleSheetForPresObj( PresObjKind::Outline ); + DBG_ASSERT(pSheet, "Template for outline object not found"); + if (pSheet) + { + pObj->StartListening(*pSheet); + + if( nLevel == 1 ) + // text frame listens on StyleSheet of level1 + pObj->NbcSetStyleSheet(pSheet, false); + } + } + } + + if( pPresPage->IsPresObj( pObj ) ) + { + std::pair<std::unique_ptr<SfxItemSet>,SdrObjUserCall*> &rAttr = aAttrList[j++]; + + std::unique_ptr<SfxItemSet> & pNewSet(rAttr.first); + SdrObjUserCall* pUserCall = rAttr.second; + + if ( pNewSet && pNewSet->GetItemState( SDRATTR_TEXT_MINFRAMEHEIGHT ) == SfxItemState::SET ) + { + pObj->SetMergedItem(pNewSet->Get(SDRATTR_TEXT_MINFRAMEHEIGHT)); + } + + if ( pNewSet && pNewSet->GetItemState( SDRATTR_TEXT_AUTOGROWHEIGHT ) == SfxItemState::SET ) + { + pObj->SetMergedItem(pNewSet->Get(SDRATTR_TEXT_AUTOGROWHEIGHT)); + } + + if( pUserCall ) + pObj->SetUserCall( pUserCall ); + } + } + } + + pSet.reset(); + Cancel(); + } + break; + + case SID_DELETE_SNAPITEM: + { + SdrPageView* pPV; + Point aMPos = GetActiveWindow()->PixelToLogic( maMousePos ); + sal_uInt16 nHitLog = static_cast<sal_uInt16>(GetActiveWindow()->PixelToLogic( Size( + FuPoor::HITPIX, 0 ) ).Width()); + sal_uInt16 nHelpLine; + + if( mpDrawView->PickHelpLine( aMPos, nHitLog, *GetActiveWindow()->GetOutDev(), nHelpLine, pPV) ) + { + pPV->DeleteHelpLine( nHelpLine ); + } + Cancel(); + rReq.Ignore (); + } + break; + + case SID_DELETE_PAGE: + case SID_DELETE_MASTER_PAGE: + DeleteActualPage(); + Cancel(); + rReq.Ignore (); + break; + + case SID_DELETE_LAYER: + DeleteActualLayer(); + Cancel(); + rReq.Ignore (); + break; + + case SID_ORIGINAL_SIZE: + mpDrawView->SetMarkedOriginalSize(); + Cancel(); + rReq.Done(); + break; + + case SID_DRAW_FONTWORK: + case SID_DRAW_FONTWORK_VERTICAL: + { + svx::FontworkBar::execute(*mpView, rReq, GetViewFrame()->GetBindings()); // SJ: can be removed (I think) + Cancel(); + rReq.Done(); + } + break; + + case SID_SAVE_GRAPHIC: + { + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + if( rMarkList.GetMarkCount() == 1 ) + { + const SdrGrafObj* pObj = dynamic_cast<const SdrGrafObj*>(rMarkList.GetMark(0)->GetMarkedSdrObj()); + if (pObj && pObj->GetGraphicType() == GraphicType::Bitmap) + { + weld::Window* pFrame = GetFrameWeld(); + GraphicAttr aGraphicAttr = pObj->GetGraphicAttr(); + short nState = RET_CANCEL; + if (aGraphicAttr != GraphicAttr()) // the image has been modified + { + if (pFrame) + { + nState = GraphicHelper::HasToSaveTransformedImage(pFrame); + } + } + else + { + nState = RET_NO; + } + + if (nState == RET_YES) + { + GraphicHelper::ExportGraphic(pFrame, pObj->GetTransformedGraphic(), ""); + } + else if (nState == RET_NO) + { + const GraphicObject& aGraphicObject(pObj->GetGraphicObject()); + GraphicHelper::ExportGraphic(pFrame, aGraphicObject.GetGraphic(), ""); + } + } + } + Cancel(); + rReq.Ignore(); + } + break; + + case SID_EXTERNAL_EDIT: + { + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + if( rMarkList.GetMarkCount() == 1 ) + { + SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj(); + if( auto pGraphicObj = dynamic_cast<SdrGrafObj*>( pObj ) ) + if( pGraphicObj->GetGraphicType() == GraphicType::Bitmap ) + { + GraphicObject aGraphicObject( pGraphicObj->GetGraphicObject() ); + m_ExternalEdits.push_back( + std::make_unique<SdrExternalToolEdit>( + mpDrawView.get(), pObj)); + m_ExternalEdits.back()->Edit( &aGraphicObject ); + } + } + Cancel(); + rReq.Ignore(); + } + break; + + case SID_COMPRESS_GRAPHIC: + { + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + if( rMarkList.GetMarkCount() == 1 ) + { + SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj(); + + if( auto pGraphicObj = dynamic_cast<SdrGrafObj*>( pObj ) ) + if( pGraphicObj->GetGraphicType() == GraphicType::Bitmap ) + { + CompressGraphicsDialog dialog(GetFrameWeld(), pGraphicObj, GetViewFrame()->GetBindings() ); + if (dialog.run() == RET_OK) + { + SdrGrafObj* pNewObject = dialog.GetCompressedSdrGrafObj(); + SdrPageView* pPageView = mpDrawView->GetSdrPageView(); + OUString aUndoString = mpDrawView->GetDescriptionOfMarkedObjects() + " Compress"; + mpDrawView->BegUndo( aUndoString ); + mpDrawView->ReplaceObjectAtView( pObj, *pPageView, pNewObject ); + mpDrawView->EndUndo(); + } + } + } + Cancel(); + rReq.Ignore(); + } + break; + + case SID_GRAPHIC_SIZE_CHECK: + { + sd::GraphicSizeCheckGUIResult aResult(GetDoc()); + svx::GenericCheckDialog aDialog(GetFrameWeld(), aResult); + aDialog.run(); + + Cancel(); + rReq.Ignore(); + } + break; + + case SID_ATTRIBUTES_LINE: // BASIC + { + SetCurrentFunction( FuLine::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + if (rReq.GetArgs()) + Cancel(); + } + break; + + case SID_ATTRIBUTES_AREA: // BASIC + { + SetCurrentFunction( FuArea::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + if (rReq.GetArgs()) + Cancel(); + } + break; + + case SID_ATTR_TRANSFORM: + { + SetCurrentFunction( FuTransform::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + // tdf#138963 conditions tested for here must be the same as those + // of the early returns from FuTransform::DoExecute + if (rReq.GetArgs() || !mpDrawView->AreObjectsMarked()) + { + Invalidate(SID_RULER_OBJECT); + Cancel(); + } + } + break; + case SID_MOVE_SHAPE_HANDLE: + { + const SfxItemSet *pArgs = rReq.GetArgs (); + if (pArgs && pArgs->Count () >= 3) + { + const SfxUInt32Item* handleNumItem = rReq.GetArg<SfxUInt32Item>(FN_PARAM_1); + const SfxUInt32Item* newPosXTwips = rReq.GetArg<SfxUInt32Item>(FN_PARAM_2); + const SfxUInt32Item* newPosYTwips = rReq.GetArg<SfxUInt32Item>(FN_PARAM_3); + const SfxInt32Item* OrdNum = rReq.GetArg<SfxInt32Item>(FN_PARAM_4); + + const sal_uLong handleNum = handleNumItem->GetValue(); + const sal_uLong newPosX = convertTwipToMm100(newPosXTwips->GetValue()); + const sal_uLong newPosY = convertTwipToMm100(newPosYTwips->GetValue()); + + mpDrawView->MoveShapeHandle(handleNum, Point(newPosX, newPosY), OrdNum ? OrdNum->GetValue() : -1); + Cancel(); + } + break; + } + case SID_CHAR_DLG_EFFECT: + case SID_CHAR_DLG: // BASIC + { + SetCurrentFunction( FuChar::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + + case SID_PARA_DLG: + { + SetCurrentFunction( FuParagraph::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + + case FN_NUM_BULLET_ON: + { + // The value (sal_uInt16)0xFFFF means set bullet on/off. + SfxUInt16Item aItem(FN_SVX_SET_BULLET, sal_uInt16(0xFFFF)); + GetViewFrame()->GetDispatcher()->ExecuteList(FN_SVX_SET_BULLET, + SfxCallMode::RECORD, { &aItem }); + } + break; + + case FN_NUM_NUMBERING_ON: + { + // The value (sal_uInt16)0xFFFF means set bullet on/off. + SfxUInt16Item aItem(FN_SVX_SET_NUMBER, sal_uInt16(0xFFFF)); + GetViewFrame()->GetDispatcher()->ExecuteList(FN_SVX_SET_NUMBER, + SfxCallMode::RECORD, { &aItem }); + } + break; + + case SID_OUTLINE_BULLET: + case FN_SVX_SET_BULLET: + case FN_SVX_SET_NUMBER: + { + SetCurrentFunction( FuBulletAndPosition::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + + case FN_INSERT_SOFT_HYPHEN: + case FN_INSERT_HARDHYPHEN: + case FN_INSERT_HARD_SPACE: + case FN_INSERT_NNBSP: + case SID_INSERT_RLM : + case SID_INSERT_LRM : + case SID_INSERT_WJ : + case SID_INSERT_ZWSP: + case SID_CHARMAP: + { + SetCurrentFunction( FuBullet::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + + case SID_PRESENTATION_LAYOUT: + { + SetCurrentFunction( FuPresentationLayout::Create(this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq) ); + Cancel(); + } + break; + + case SID_PASTE_SPECIAL: + { + SetCurrentFunction( FuInsertClipboard::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_CHANGE_PICTURE: + case SID_INSERT_GRAPHIC: + { + SetCurrentFunction( FuInsertGraphic::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq, + nSId == SID_CHANGE_PICTURE ) ); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_INSERT_AVMEDIA: + { + SetCurrentFunction( FuInsertAVMedia::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + + Cancel(); + rReq.Ignore (); + } + break; + + case SID_INSERT_OBJECT: + case SID_INSERT_FLOATINGFRAME: + case SID_INSERT_MATH: + case SID_INSERT_DIAGRAM: + case SID_ATTR_TABLE: + { + SetCurrentFunction( FuInsertOLE::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + // Set the selection tool as the old one. This in particular important for the + // zoom function, in which clicking without dragging zooms as well, and that + // makes exiting the object editing mode impossible. + if (dynamic_cast<FuSelection*>( GetOldFunction().get() ) == nullptr) + SetOldFunction( FuSelection::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + rReq.Ignore (); + } + break; + case SID_CLASSIFICATION_APPLY: + { + const SfxItemSet* pArgs = rReq.GetArgs(); + const SfxPoolItem* pItem = nullptr; + if (pArgs && pArgs->GetItemState(nSId, false, &pItem) == SfxItemState::SET) + { + const OUString& rName = static_cast<const SfxStringItem*>(pItem)->GetValue(); + auto eType = SfxClassificationPolicyType::IntellectualProperty; + if (pArgs->GetItemState(SID_TYPE_NAME, false, &pItem) == SfxItemState::SET) + { + const OUString& rType = static_cast<const SfxStringItem*>(pItem)->GetValue(); + eType = SfxClassificationHelper::stringToPolicyType(rType); + } + if (SfxViewFrame* pViewFrame = GetViewFrame()) + { + if (SfxObjectShell* pObjectShell = pViewFrame->GetObjectShell()) + { + SfxClassificationHelper aHelper(pObjectShell->getDocProperties()); + aHelper.SetBACName(rName, eType); + } + } + } + else + SAL_WARN("sd.ui", "missing parameter for SID_CLASSIFICATION_APPLY"); + + Cancel(); + rReq.Ignore(); + } + break; + + case SID_CLASSIFICATION_DIALOG: + { + if (SfxObjectShell* pObjShell = SfxObjectShell::Current()) + { + css::uno::Reference<css::document::XDocumentProperties> xDocProps(pObjShell->getDocProperties()); + auto xDialog = std::make_shared<svx::ClassificationDialog>(GetFrameWeld(), xDocProps, false, [](){} ); + ClassificationCollector aCollector(*this, xDocProps); + aCollector.collect(); + + xDialog->setupValues(std::vector(aCollector.getResults())); + + if (RET_OK == xDialog->run()) + { + ClassificationInserter aInserter(*this, xDocProps); + aInserter.insert(xDialog->getResult()); + } + xDialog.reset(); + } + + Cancel(); + rReq.Ignore(); + } + break; + + case SID_COPYOBJECTS: + { + if ( mpDrawView->IsPresObjSelected(false) ) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + if ( mpDrawView->IsTextEdit() ) + { + mpDrawView->SdrEndTextEdit(); + } + + SetCurrentFunction( FuCopy::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + } + Cancel(); + rReq.Ignore (); + } + break; + + case SID_INSERTFILE: // BASIC + { + Broadcast (ViewShellHint(ViewShellHint::HINT_COMPLEX_MODEL_CHANGE_START)); + SetCurrentFunction( FuInsertFile::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Broadcast (ViewShellHint(ViewShellHint::HINT_COMPLEX_MODEL_CHANGE_END)); + Cancel(); + rReq.Done (); + } + break; + + case SID_SELECT_BACKGROUND: + case SID_SAVE_BACKGROUND: + case SID_ATTR_PAGE_SIZE: + case SID_ATTR_PAGE: + case SID_PAGESETUP: // BASIC ?? + { + SetCurrentFunction( FuPage::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + rReq.Ignore (); // we generate independent macros !! + } + break; + + case SID_BEFORE_OBJ: + case SID_BEHIND_OBJ: + { + SetCurrentFunction( FuDisplayOrder::Create(this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq) ); + rReq.Done(); + // finishes itself, no Cancel() needed! + } + break; + + case SID_REVERSE_ORDER: // BASIC + { + mpDrawView->ReverseOrderOfMarked(); + Cancel(); + rReq.Done (); + } + break; + + case SID_ANIMATION_EFFECTS: + { + SetCurrentFunction( FuObjectAnimationParameters::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq) ); + Cancel(); + } + break; + + case SID_EXECUTE_ANIMATION_EFFECT: + { + SetCurrentFunction(FuExecuteInteraction::Create(this, GetActiveWindow(), + mpDrawView.get(), GetDoc(), rReq)); + Cancel(); + } + break; + + case SID_LINEEND_POLYGON: + { + SetCurrentFunction( FuLineEnd::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + + case SID_CAPTUREPOINT: + // negative value to signal call from menu + maMousePos = Point(-1,-1); + [[fallthrough]]; + case SID_SET_SNAPITEM: + { + SetCurrentFunction( FuSnapLine::Create(this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq) ); + Cancel(); + } + break; + + case SID_MANAGE_LINKS: + { + SetCurrentFunction( FuLink::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_THESAURUS: + { + SetCurrentFunction( FuThesaurus::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_TEXTATTR_DLG: + { + if (mpDrawView->IsTextEdit()) + mpDrawView->SdrEndTextEdit(); + SetCurrentFunction( FuTextAttrDlg::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_MEASURE_DLG: + { + SetCurrentFunction( FuMeasureDlg::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_CONNECTION_DLG: + { + SetCurrentFunction( FuConnectionDlg::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + rReq.Done(); + } + break; + + case SID_CONNECTION_NEW_ROUTING: + { + SfxItemSetFixed<SDRATTR_EDGELINE1DELTA, SDRATTR_EDGELINE3DELTA> aDefAttr( GetPool() ); + GetView()->SetAttributes( aDefAttr, true ); // (ReplaceAll) + + Cancel(); + rReq.Done(); + } + break; + + case SID_TWAIN_SELECT: + { + if( mxScannerManager.is() ) + { + try + { + const css::uno::Sequence< css::scanner::ScannerContext > + aContexts( mxScannerManager->getAvailableScanners() ); + + if( aContexts.hasElements() ) + { + css::scanner::ScannerContext aContext( aContexts.getConstArray()[ 0 ] ); + + Reference<lang::XInitialization> xInit(mxScannerManager, UNO_QUERY); + if (xInit.is()) + { + // initialize dialog + weld::Window* pWindow = rReq.GetFrameWeld(); + uno::Sequence<uno::Any> aSeq(comphelper::InitAnyPropertySequence( + { + {"ParentWindow", pWindow ? uno::Any(pWindow->GetXWindow()) : uno::Any(Reference<awt::XWindow>())} + })); + xInit->initialize( aSeq ); + } + + mxScannerManager->configureScannerAndScan( aContext, mxScannerListener ); + } + } + catch(...) + { + } + } + + Cancel(); + rReq.Done(); + } + break; + + case SID_TWAIN_TRANSFER: + { + bool bDone = false; + + if( mxScannerManager.is() ) + { + try + { + const css::uno::Sequence< css::scanner::ScannerContext > aContexts( mxScannerManager->getAvailableScanners() ); + + if( aContexts.hasElements() ) + { + mxScannerManager->startScan( aContexts.getConstArray()[ 0 ], mxScannerListener ); + bDone = true; + } + } + catch( ... ) + { + } + } + + if( !bDone ) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, +#ifndef UNX + SdResId(STR_TWAIN_NO_SOURCE) +#else + SdResId(STR_TWAIN_NO_SOURCE_UNX) +#endif + )); + xInfoBox->run(); + + } + else + { + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_TWAIN_SELECT ); + rBindings.Invalidate( SID_TWAIN_TRANSFER ); + } + + Cancel(); + rReq.Done(); + } + break; + + case SID_POLYGON_MORPHING: + { + SetCurrentFunction( FuMorph::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + + case SID_INSERTLAYER: + { + if ( mpDrawView->IsTextEdit() ) + { + mpDrawView->SdrEndTextEdit(); + } + + SdrLayerAdmin& rLayerAdmin = GetDoc()->GetLayerAdmin(); + sal_uInt16 nLayerCnt = rLayerAdmin.GetLayerCount(); + sal_uInt16 nLayer = nLayerCnt - 2 + 1; + OUString aLayerName = SdResId(STR_LAYER) + OUString::number(nLayer); + OUString aLayerTitle, aLayerDesc; + bool bIsVisible = false; + bool bIsLocked = false; + bool bIsPrintable = false; + + const SfxItemSet* pArgs = rReq.GetArgs(); + + if (! pArgs) + { + SfxItemSetFixed<ATTR_LAYER_START, ATTR_LAYER_END> aNewAttr( GetDoc()->GetPool() ); + + aNewAttr.Put( makeSdAttrLayerName( aLayerName ) ); + aNewAttr.Put( makeSdAttrLayerTitle() ); + aNewAttr.Put( makeSdAttrLayerDesc() ); + aNewAttr.Put( makeSdAttrLayerVisible() ); + aNewAttr.Put( makeSdAttrLayerPrintable() ); + aNewAttr.Put( makeSdAttrLayerLocked() ); + aNewAttr.Put( makeSdAttrLayerThisPage() ); + + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + vcl::Window* pWin = GetActiveWindow(); + ScopedVclPtr<AbstractSdInsertLayerDlg> pDlg( pFact->CreateSdInsertLayerDlg(pWin ? pWin->GetFrameWeld() : nullptr, aNewAttr, true, SdResId(STR_INSERTLAYER)) ); + pDlg->SetHelpId( SD_MOD()->GetSlotPool()->GetSlot( SID_INSERTLAYER )->GetCommand() ); + + // test for already existing names + bool bLoop = true; + while( bLoop && pDlg->Execute() == RET_OK ) + { + pDlg->GetAttr( aNewAttr ); + aLayerName = static_cast<const SfxStringItem &>( aNewAttr.Get (ATTR_LAYER_NAME)).GetValue (); + + if( rLayerAdmin.GetLayer( aLayerName ) + || aLayerName.isEmpty() + || LayerTabBar::IsLocalizedNameOfStandardLayer( aLayerName) ) + { + // name already exists + std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Warning, VclButtonsType::Ok, + SdResId(STR_WARN_NAME_DUPLICATE))); + xWarn->run(); + } + else + bLoop = false; + } + if( bLoop ) // was canceled + { + pDlg.disposeAndClear(); + Cancel(); + rReq.Ignore (); + break; + } + else + { + aLayerTitle = static_cast<const SfxStringItem &>( aNewAttr.Get (ATTR_LAYER_TITLE)).GetValue (); + aLayerDesc = static_cast<const SfxStringItem &>( aNewAttr.Get (ATTR_LAYER_DESC)).GetValue (); + bIsVisible = static_cast<const SfxBoolItem &>( aNewAttr.Get (ATTR_LAYER_VISIBLE)).GetValue (); + bIsLocked = static_cast<const SfxBoolItem &>( aNewAttr.Get (ATTR_LAYER_LOCKED)).GetValue () ; + bIsPrintable = static_cast<const SfxBoolItem &>( aNewAttr.Get (ATTR_LAYER_PRINTABLE)).GetValue () ; + } + } + else if (pArgs->Count () != 4) + { +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + Cancel(); + rReq.Ignore (); + break; + } + else + { + const SfxStringItem* pLayerName = rReq.GetArg<SfxStringItem>(ID_VAL_LAYERNAME); + const SfxBoolItem* pIsVisible = rReq.GetArg<SfxBoolItem>(ID_VAL_ISVISIBLE); + const SfxBoolItem* pIsLocked = rReq.GetArg<SfxBoolItem>(ID_VAL_ISLOCKED); + const SfxBoolItem* pIsPrintable = rReq.GetArg<SfxBoolItem>(ID_VAL_ISPRINTABLE); + + aLayerName = pLayerName->GetValue (); + bIsVisible = pIsVisible->GetValue (); + bIsLocked = pIsLocked->GetValue (); + bIsPrintable = pIsPrintable->GetValue (); + } + + OUString aPrevLayer = mpDrawView->GetActiveLayer(); + SdrLayer* pLayer; + sal_uInt16 nPrevLayer = 0; + nLayerCnt = rLayerAdmin.GetLayerCount(); + + for ( nLayer = 0; nLayer < nLayerCnt; nLayer++ ) + { + pLayer = rLayerAdmin.GetLayer(nLayer); + OUString aName = pLayer->GetName(); + + if ( aPrevLayer == aName ) + { + nPrevLayer = std::max(nLayer, sal_uInt16(4)); + } + } + + mpDrawView->InsertNewLayer(aLayerName, nPrevLayer + 1); + pLayer = rLayerAdmin.GetLayer(aLayerName); + if( pLayer ) + { + pLayer->SetTitle( aLayerTitle ); + pLayer->SetDescription( aLayerDesc ); + } + + mpDrawView->SetLayerVisible( aLayerName, bIsVisible ); + mpDrawView->SetLayerLocked( aLayerName, bIsLocked); + mpDrawView->SetLayerPrintable(aLayerName, bIsPrintable); + + mpDrawView->SetActiveLayer(aLayerName); + + ResetActualLayer(); + + GetDoc()->SetChanged(); + + GetViewFrame()->GetDispatcher()->Execute(SID_SWITCHLAYER, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + + Cancel(); + rReq.Done (); + } + break; + + case SID_MODIFYLAYER: + { + if(!GetLayerTabControl()) // #i87182# + { + OSL_ENSURE(false, "No LayerTabBar (!)"); + Cancel(); + rReq.Ignore(); + break; + } + + if ( mpDrawView->IsTextEdit() ) + { + mpDrawView->SdrEndTextEdit(); + } + + SdrLayerAdmin& rLayerAdmin = GetDoc()->GetLayerAdmin(); + sal_uInt16 nCurPage = GetLayerTabControl()->GetCurPageId(); + OUString aLayerName = GetLayerTabControl()->GetLayerName(nCurPage); + SdrLayer* pLayer = rLayerAdmin.GetLayer(aLayerName); + + OUString aLayerTitle = pLayer->GetTitle(); + OUString aLayerDesc = pLayer->GetDescription(); + + OUString aOldLayerName(aLayerName); + OUString aOldLayerTitle(aLayerTitle); + OUString aOldLayerDesc(aLayerDesc); + + bool bIsVisible, bIsLocked, bIsPrintable; + bool bOldIsVisible = bIsVisible = mpDrawView->IsLayerVisible(aLayerName); + bool bOldIsLocked = bIsLocked = mpDrawView->IsLayerLocked(aLayerName); + bool bOldIsPrintable = bIsPrintable = mpDrawView->IsLayerPrintable(aLayerName); + + const SfxItemSet* pArgs = rReq.GetArgs(); + // is it allowed to delete the layer? + bool bDelete = !( LayerTabBar::IsRealNameOfStandardLayer(aLayerName) ); + + if (! pArgs) + { + SfxItemSetFixed<ATTR_LAYER_START, ATTR_LAYER_END> aNewAttr( GetDoc()->GetPool() ); + + aNewAttr.Put( makeSdAttrLayerName( aLayerName ) ); + aNewAttr.Put( makeSdAttrLayerTitle( aLayerTitle ) ); + aNewAttr.Put( makeSdAttrLayerDesc( aLayerDesc ) ); + aNewAttr.Put( makeSdAttrLayerVisible( bIsVisible ) ); + aNewAttr.Put( makeSdAttrLayerLocked( bIsLocked ) ); + aNewAttr.Put( makeSdAttrLayerPrintable( bIsPrintable ) ); + aNewAttr.Put( makeSdAttrLayerThisPage() ); + + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + vcl::Window* pWin = GetActiveWindow(); + ScopedVclPtr<AbstractSdInsertLayerDlg> pDlg( pFact->CreateSdInsertLayerDlg(pWin ? pWin->GetFrameWeld() : nullptr, aNewAttr, bDelete, SdResId(STR_MODIFYLAYER)) ); + pDlg->SetHelpId( SD_MOD()->GetSlotPool()->GetSlot( SID_MODIFYLAYER )->GetCommand() ); + + // test for already existing names + bool bLoop = true; + sal_uInt16 nRet = 0; + while( bLoop ) + { + nRet = pDlg->Execute(); + if (nRet != RET_OK) + break; + pDlg->GetAttr( aNewAttr ); + aLayerName = static_cast<const SfxStringItem &>( aNewAttr.Get (ATTR_LAYER_NAME)).GetValue (); + if (bDelete) + { + if( (rLayerAdmin.GetLayer( aLayerName ) && aLayerName != aOldLayerName) + || LayerTabBar::IsRealNameOfStandardLayer(aLayerName) + || LayerTabBar::IsLocalizedNameOfStandardLayer(aLayerName) + || aLayerName.isEmpty() ) + { + // name already exists + std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Warning, VclButtonsType::Ok, + SdResId(STR_WARN_NAME_DUPLICATE))); + xWarn->run(); + } + else + bLoop = false; + } + else + bLoop = false; // altering name is already disabled in the dialog itself + } + switch (nRet) + { + case RET_OK : + aLayerTitle = static_cast<const SfxStringItem &>( aNewAttr.Get (ATTR_LAYER_TITLE)).GetValue (); + aLayerDesc = static_cast<const SfxStringItem &>( aNewAttr.Get (ATTR_LAYER_DESC)).GetValue (); + bIsVisible = static_cast<const SfxBoolItem &>( aNewAttr.Get (ATTR_LAYER_VISIBLE)).GetValue (); + bIsLocked = static_cast<const SfxBoolItem &>( aNewAttr.Get (ATTR_LAYER_LOCKED)).GetValue (); + bIsPrintable = static_cast<const SfxBoolItem &>( aNewAttr.Get (ATTR_LAYER_PRINTABLE)).GetValue (); + break; + + default : + pDlg.disposeAndClear(); + rReq.Ignore (); + Cancel (); + return; + } + } + else if (pArgs->Count () == 4) + { + const SfxStringItem* pLayerName = rReq.GetArg<SfxStringItem>(ID_VAL_LAYERNAME); + const SfxBoolItem* pIsVisible = rReq.GetArg<SfxBoolItem>(ID_VAL_ISVISIBLE); + const SfxBoolItem* pIsLocked = rReq.GetArg<SfxBoolItem>(ID_VAL_ISLOCKED); + const SfxBoolItem* pIsPrintable = rReq.GetArg<SfxBoolItem>(ID_VAL_ISPRINTABLE); + + aLayerName = pLayerName->GetValue (); + bIsVisible = pIsVisible->GetValue (); + bIsLocked = pIsLocked->GetValue (); + bIsPrintable = pIsPrintable->GetValue (); + } + else + { +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + Cancel (); + rReq.Ignore (); + break; + } + + SfxUndoManager* pManager = GetDoc()->GetDocSh()->GetUndoManager(); + std::unique_ptr<SdLayerModifyUndoAction> pAction( new SdLayerModifyUndoAction( + GetDoc(), + pLayer, + // old values + aOldLayerName, + aOldLayerTitle, + aOldLayerDesc, + bOldIsVisible, + bOldIsLocked, + bOldIsPrintable, + // new values + aLayerName, + aLayerTitle, + aLayerDesc, + bIsVisible, + bIsLocked, + bIsPrintable + ) ); + pManager->AddUndoAction( std::move(pAction) ); + + ModifyLayer( pLayer, aLayerName, aLayerTitle, aLayerDesc, bIsVisible, bIsLocked, bIsPrintable ); + + Cancel(); + rReq.Done (); + } + break; + + case SID_RENAMELAYER: + { + if ( mpDrawView->IsTextEdit() ) + { + mpDrawView->SdrEndTextEdit(); + } + + if(GetLayerTabControl()) // #i87182# + { + GetLayerTabControl()->StartEditMode(GetLayerTabControl()->GetCurPageId()); + } + else + { + OSL_ENSURE(false, "No LayerTabBar (!)"); + } + + Cancel(); + rReq.Ignore (); + } + break; + + case SID_EDIT_HYPERLINK : + { + // Ensure the field is selected first + OutlinerView* pOutView = mpDrawView->GetTextEditOutlinerView(); + if (pOutView) + pOutView->SelectFieldAtCursor(); + + GetViewFrame()->GetDispatcher()->Execute( SID_HYPERLINK_DIALOG ); + + Cancel(); + rReq.Done (); + } + break; + + case SID_OPEN_HYPERLINK: + { + OutlinerView* pOutView = mpDrawView->GetTextEditOutlinerView(); + if ( pOutView ) + { + const SvxFieldData* pField = pOutView->GetFieldAtCursor(); + if( auto pURLField = dynamic_cast< const SvxURLField *>( pField ) ) + { + SfxStringItem aUrl( SID_FILE_NAME, pURLField->GetURL() ); + SfxStringItem aTarget( SID_TARGETNAME, pURLField->GetTargetFrame() ); + + OUString aReferName; + SfxViewFrame* pFrame = GetViewFrame(); + SfxMedium* pMed = pFrame->GetObjectShell()->GetMedium(); + if (pMed) + aReferName = pMed->GetName(); + + SfxFrameItem aFrm( SID_DOCFRAME, pFrame ); + SfxStringItem aReferer( SID_REFERER, aReferName ); + + SfxBoolItem aNewView( SID_OPEN_NEW_VIEW, false ); + SfxBoolItem aBrowsing( SID_BROWSE, true ); + + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + if (pViewFrm) + { + pViewFrm->GetDispatcher()->ExecuteList(SID_OPENDOC, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aUrl, &aTarget, &aFrm, &aReferer, + &aNewView, &aBrowsing }); + } + } + } + Cancel(); + rReq.Done (); + } + break; + + case SID_COPY_HYPERLINK_LOCATION: + { + OutlinerView* pOutView = mpDrawView->GetTextEditOutlinerView(); + if ( pOutView ) + { + const SvxFieldData* pField = pOutView->GetFieldAtCursor(); + if (const SvxURLField* pURLField = dynamic_cast<const SvxURLField*>(pField)) + { + uno::Reference<datatransfer::clipboard::XClipboard> xClipboard + = pOutView->GetWindow()->GetClipboard(); + + vcl::unohelper::TextDataObject::CopyStringTo(pURLField->GetURL(), xClipboard, SfxViewShell::Current()); + } + } + + Cancel(); + rReq.Done (); + } + break; + + case SID_HYPERLINK_SETLINK: + { + const SfxItemSet* pReqArgs = rReq.GetArgs(); + + if (pReqArgs) + { + const SvxHyperlinkItem* pHLItem = + &pReqArgs->Get(SID_HYPERLINK_SETLINK); + + if (pHLItem->GetInsertMode() == HLINK_FIELD) + { + InsertURLField(pHLItem->GetURL(), pHLItem->GetName(), + pHLItem->GetTargetFrame()); + } + else if (pHLItem->GetInsertMode() == HLINK_BUTTON) + { + InsertURLButton(pHLItem->GetURL(), pHLItem->GetName(), + pHLItem->GetTargetFrame(), nullptr); + } + else if (pHLItem->GetInsertMode() == HLINK_DEFAULT) + { + OutlinerView* pOlView = mpDrawView->GetTextEditOutlinerView(); + + if (pOlView || comphelper::LibreOfficeKit::isActive()) + { + InsertURLField(pHLItem->GetURL(), pHLItem->GetName(), + pHLItem->GetTargetFrame()); + } + else + { + InsertURLButton(pHLItem->GetURL(), pHLItem->GetName(), + pHLItem->GetTargetFrame(), nullptr); + } + } + } + + Cancel(); + rReq.Ignore (); + } + break; + + case SID_HIDE_LAST_LEVEL: + { + ESelection aSel; + // fdo#78151 editing a PresObjKind::Outline in a master page ? + ::Outliner* pOL = GetOutlinerForMasterPageOutlineTextObj(aSel); + if (pOL) + { + //we are on the last paragraph + aSel.Adjust(); + if (aSel.nEndPara == pOL->GetParagraphCount() - 1) + { + sal_uInt16 nDepth = pOL->GetDepth(aSel.nEndPara); + //there exists a previous numbering level + if (nDepth != sal_uInt16(-1) && nDepth > 0) + { + Paragraph* pPara = pOL->GetParagraph(aSel.nEndPara); + pOL->Remove(pPara, 1); + } + } + } + Cancel(); + rReq.Done (); + } + break; + + case SID_SHOW_NEXT_LEVEL: + { + const TranslateId STR_PRESOBJ_MPOUTLINE_ARY[] + { + STR_PRESOBJ_MPOUTLINE, + STR_PRESOBJ_MPOUTLLAYER2, + STR_PRESOBJ_MPOUTLLAYER3, + STR_PRESOBJ_MPOUTLLAYER4, + STR_PRESOBJ_MPOUTLLAYER5, + STR_PRESOBJ_MPOUTLLAYER6, + STR_PRESOBJ_MPOUTLLAYER7, + STR_PRESOBJ_MPNOTESTITLE, + STR_PRESOBJ_MPNOTESTEXT, + STR_PRESOBJ_NOTESTEXT + }; + + ESelection aSel; + // fdo#78151 editing a PresObjKind::Outline in a master page ? + ::Outliner* pOL = GetOutlinerForMasterPageOutlineTextObj(aSel); + if (pOL) + { + //we are on the last paragraph + aSel.Adjust(); + if (aSel.nEndPara == pOL->GetParagraphCount() - 1) + { + sal_uInt16 nDepth = pOL->GetDepth(aSel.nEndPara); + //there exists a previous numbering level + if (nDepth < 8) + { + sal_uInt16 nNewDepth = nDepth+1; + pOL->Insert(SdResId(STR_PRESOBJ_MPOUTLINE_ARY[nNewDepth]), EE_PARA_APPEND, nNewDepth); + } + } + } + Cancel(); + rReq.Done (); + } + break; + + case SID_INSERT_FLD_DATE_FIX: + case SID_INSERT_FLD_DATE_VAR: + case SID_INSERT_FLD_TIME_FIX: + case SID_INSERT_FLD_TIME_VAR: + case SID_INSERT_FLD_AUTHOR: + case SID_INSERT_FLD_PAGE: + case SID_INSERT_FLD_PAGE_TITLE: + case SID_INSERT_FLD_PAGES: + case SID_INSERT_FLD_FILE: + { + sal_uInt16 nMul = 1; + std::unique_ptr<SvxFieldItem> pFieldItem; + + switch( nSId ) + { + case SID_INSERT_FLD_DATE_FIX: + pFieldItem.reset(new SvxFieldItem( + SvxDateField( Date( Date::SYSTEM ), SvxDateType::Fix ), EE_FEATURE_FIELD )); + break; + + case SID_INSERT_FLD_DATE_VAR: + pFieldItem.reset(new SvxFieldItem( SvxDateField(), EE_FEATURE_FIELD )); + break; + + case SID_INSERT_FLD_TIME_FIX: + pFieldItem.reset(new SvxFieldItem( + SvxExtTimeField( ::tools::Time( ::tools::Time::SYSTEM ), SvxTimeType::Fix ), EE_FEATURE_FIELD )); + break; + + case SID_INSERT_FLD_TIME_VAR: + pFieldItem.reset(new SvxFieldItem( SvxExtTimeField(), EE_FEATURE_FIELD )); + break; + + case SID_INSERT_FLD_AUTHOR: + { + SvtUserOptions aUserOptions; + pFieldItem.reset(new SvxFieldItem( + SvxAuthorField( + aUserOptions.GetFirstName(), aUserOptions.GetLastName(), aUserOptions.GetID() ), EE_FEATURE_FIELD )); + } + break; + + case SID_INSERT_FLD_PAGE: + { + pFieldItem.reset(new SvxFieldItem( SvxPageField(), EE_FEATURE_FIELD )); + nMul = 3; + } + break; + + case SID_INSERT_FLD_PAGE_TITLE: + { + pFieldItem.reset(new SvxFieldItem( SvxPageTitleField(), EE_FEATURE_FIELD)); + nMul = 3; + } + break; + + case SID_INSERT_FLD_PAGES: + { + pFieldItem.reset(new SvxFieldItem( SvxPagesField(), EE_FEATURE_FIELD )); + nMul = 3; + } + break; + + case SID_INSERT_FLD_FILE: + { + OUString aName; + if( GetDocSh()->HasName() ) + aName = GetDocSh()->GetMedium()->GetName(); + pFieldItem.reset(new SvxFieldItem( SvxExtFileField( aName ), EE_FEATURE_FIELD )); + } + break; + } + + OutlinerView* pOLV = mpDrawView->GetTextEditOutlinerView(); + + if( pOLV ) + { + const SvxFieldItem* pOldFldItem = pOLV->GetFieldAtSelection(); + + if( pOldFldItem && ( nullptr != dynamic_cast< const SvxURLField *>( pOldFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxDateField *>( pOldFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxTimeField *>( pOldFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxExtTimeField *>( pOldFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxExtFileField *>( pOldFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxAuthorField *>( pOldFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxPageField *>( pOldFldItem->GetField() ) ) ) + { + // select field, then it will be deleted when inserting + ESelection aSel = pOLV->GetSelection(); + if( aSel.nStartPos == aSel.nEndPos ) + aSel.nEndPos++; + pOLV->SetSelection( aSel ); + } + + if( pFieldItem ) + pOLV->InsertField( *pFieldItem ); + } + else + { + Outliner* pOutl = GetDoc()->GetInternalOutliner(); + pOutl->Init( OutlinerMode::TextObject ); + OutlinerMode nOutlMode = pOutl->GetOutlinerMode(); + pOutl->SetStyleSheet( 0, nullptr ); + pOutl->QuickInsertField( *pFieldItem, ESelection() ); + std::optional<OutlinerParaObject> pOutlParaObject = pOutl->CreateParaObject(); + + SdrRectObj* pRectObj = new SdrRectObj( + *GetDoc(), + SdrObjKind::Text); + pRectObj->SetMergedItem(makeSdrTextAutoGrowWidthItem(true)); + + pOutl->UpdateFields(); + pOutl->SetUpdateLayout( true ); + Size aSize( pOutl->CalcTextSize() ); + aSize.setWidth( aSize.Width() * nMul ); + pOutl->SetUpdateLayout( false ); + + Point aPos; + ::tools::Rectangle aRect( aPos, GetActiveWindow()->GetOutputSizePixel() ); + aPos = aRect.Center(); + aPos = GetActiveWindow()->PixelToLogic(aPos); + aPos.AdjustX( -(aSize.Width() / 2) ); + aPos.AdjustY( -(aSize.Height() / 2) ); + + ::tools::Rectangle aLogicRect(aPos, aSize); + pRectObj->SetLogicRect(aLogicRect); + pRectObj->SetOutlinerParaObject( std::move(pOutlParaObject) ); + mpDrawView->InsertObjectAtView(pRectObj, *mpDrawView->GetSdrPageView()); + pOutl->Init( nOutlMode ); + } + + pFieldItem.reset(); + + Cancel(); + rReq.Ignore (); + } + break; + + case SID_MODIFY_FIELD: + { + OutlinerView* pOLV = mpDrawView->GetTextEditOutlinerView(); + + if( pOLV ) + { + const SvxFieldItem* pFldItem = pOLV->GetFieldAtSelection(); + + if( pFldItem && (nullptr != dynamic_cast< const SvxDateField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxAuthorField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxExtFileField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxExtTimeField *>( pFldItem->GetField() ) ) ) + { + // Dialog... + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + vcl::Window* pWin = GetActiveWindow(); + ScopedVclPtr<AbstractSdModifyFieldDlg> pDlg( pFact->CreateSdModifyFieldDlg(pWin ? pWin->GetFrameWeld() : nullptr, pFldItem->GetField(), pOLV->GetAttribs() ) ); + if( pDlg->Execute() == RET_OK ) + { + // To make a correct SetAttribs() call at the utlinerView + // it is necessary to split the actions here + std::unique_ptr<SvxFieldData> pField(pDlg->GetField()); + ESelection aSel = pOLV->GetSelection(); + bool bSelectionWasModified(false); + + if( pField ) + { + SvxFieldItem aFieldItem( *pField, EE_FEATURE_FIELD ); + + if( aSel.nStartPos == aSel.nEndPos ) + { + bSelectionWasModified = true; + aSel.nEndPos++; + pOLV->SetSelection( aSel ); + } + + pOLV->InsertField( aFieldItem ); + + // select again for eventual SetAttribs call + pOLV->SetSelection( aSel ); + } + + SfxItemSet aSet( pDlg->GetItemSet() ); + + if( aSet.Count() ) + { + pOLV->SetAttribs( aSet ); + + ::Outliner* pOutliner = pOLV->GetOutliner(); + if( pOutliner ) + pOutliner->UpdateFields(); + } + + if(pField) + { + // restore selection to original + if(bSelectionWasModified) + { + aSel.nEndPos--; + pOLV->SetSelection( aSel ); + } + } + } + } + } + + Cancel(); + rReq.Ignore (); + } + break; + + case SID_OPEN_XML_FILTERSETTINGS: + { + try + { + css::uno::Reference < css::ui::dialogs::XExecutableDialog > xDialog = css::ui::dialogs::XSLTFilterDialog::create( ::comphelper::getProcessComponentContext() ); + xDialog->execute(); + } + catch( css::uno::RuntimeException& ) + { + DBG_UNHANDLED_EXCEPTION("sd.view"); + } + + Cancel(); + rReq.Ignore (); + } + break; + + case SID_GROUP: // BASIC + { + if ( mpDrawView->IsPresObjSelected( true, true, true ) ) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + mpDrawView->GroupMarked(); + } + Cancel(); + rReq.Done (); + } + break; + + case SID_UNGROUP: // BASIC + { + mpDrawView->UnGroupMarked(); + Cancel(); + rReq.Done (); + } + break; + + case SID_NAME_GROUP: + { + // only allow for single object selection since the name of an object needs + // to be unique + if(1 == mpDrawView->GetMarkedObjectCount()) + { + // #i68101# + SdrObject* pSelected = mpDrawView->GetMarkedObjectByIndex(0); + OSL_ENSURE(pSelected, "DrawViewShell::FuTemp03: nMarkCount, but no object (!)"); + OUString aName(pSelected->GetName()); + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSvxObjectNameDialog> pDlg(pFact->CreateSvxObjectNameDialog(GetFrameWeld(), aName)); + + pDlg->SetCheckNameHdl(LINK(this, DrawViewShell, NameObjectHdl)); + + if(RET_OK == pDlg->Execute()) + { + pDlg->GetName(aName); + pSelected->SetName(aName); + + SdPage* pPage = GetActualPage(); + if (pPage) + pPage->notifyObjectRenamed(pSelected); + } + } + + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_NAVIGATOR_STATE, true ); + rBindings.Invalidate( SID_CONTEXT ); + + Cancel(); + rReq.Ignore(); + break; + } + + // #i68101# + case SID_OBJECT_TITLE_DESCRIPTION: + { + if(1 == mpDrawView->GetMarkedObjectCount()) + { + SdrObject* pSelected = mpDrawView->GetMarkedObjectByIndex(0); + OSL_ENSURE(pSelected, "DrawViewShell::FuTemp03: nMarkCount, but no object (!)"); + OUString aTitle(pSelected->GetTitle()); + OUString aDescription(pSelected->GetDescription()); + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSvxObjectTitleDescDialog> pDlg(pFact->CreateSvxObjectTitleDescDialog( + GetFrameWeld(), aTitle, aDescription)); + + if(RET_OK == pDlg->Execute()) + { + pDlg->GetTitle(aTitle); + pDlg->GetDescription(aDescription); + pSelected->SetTitle(aTitle); + pSelected->SetDescription(aDescription); + } + } + + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_NAVIGATOR_STATE, true ); + rBindings.Invalidate( SID_CONTEXT ); + + Cancel(); + rReq.Ignore(); + break; + } + + case SID_ENTER_GROUP: // BASIC + { + mpDrawView->EnterMarkedGroup(); + Cancel(); + rReq.Done (); + } + break; + + case SID_LEAVE_GROUP: // BASIC + { + mpDrawView->LeaveOneGroup(); + Cancel(); + rReq.Done (); + } + break; + + case SID_LEAVE_ALL_GROUPS: // BASIC + { + mpDrawView->LeaveAllGroup(); + Cancel(); + rReq.Done (); + } + break; + + case SID_TEXT_COMBINE: // BASIC + { + // End text edit to avoid conflicts + if(mpDrawView->IsTextEdit()) + mpDrawView->SdrEndTextEdit(); + + if ( mpDrawView->IsPresObjSelected() ) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->CombineMarkedTextObjects(); + } + Cancel(); + rReq.Done (); + } + break; + + case SID_COMBINE: // BASIC + { + // End text edit to avoid conflicts + if(mpDrawView->IsTextEdit()) + mpDrawView->SdrEndTextEdit(); + + if ( mpDrawView->IsPresObjSelected() ) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->CombineMarkedObjects(false); + } + Cancel(); + rReq.Done (); + } + break; + + case SID_DISTRIBUTE_HLEFT: + case SID_DISTRIBUTE_HCENTER: + case SID_DISTRIBUTE_HDISTANCE: + case SID_DISTRIBUTE_HRIGHT: + case SID_DISTRIBUTE_VTOP: + case SID_DISTRIBUTE_VCENTER: + case SID_DISTRIBUTE_VDISTANCE: + case SID_DISTRIBUTE_VBOTTOM: + { + if ( mpDrawView->IsPresObjSelected() ) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + mpDrawView->DistributeMarkedObjects(nSId); + } + Cancel(); + rReq.Done (); + } + break; + case SID_POLY_MERGE: + { + // End text edit to avoid conflicts + if(mpDrawView->IsTextEdit()) + mpDrawView->SdrEndTextEdit(); + + if ( mpDrawView->IsPresObjSelected() ) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->MergeMarkedObjects(SdrMergeMode::Merge); + } + Cancel(); + rReq.Done (); + } + break; + + case SID_POLY_SUBSTRACT: + { + // End text edit to avoid conflicts + if(mpDrawView->IsTextEdit()) + mpDrawView->SdrEndTextEdit(); + + if ( mpDrawView->IsPresObjSelected() ) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->MergeMarkedObjects(SdrMergeMode::Subtract); + } + Cancel(); + rReq.Done (); + } + break; + + case SID_POLY_INTERSECT: + { + // End text edit to avoid conflicts + if(mpDrawView->IsTextEdit()) + mpDrawView->SdrEndTextEdit(); + + if ( mpDrawView->IsPresObjSelected() ) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->MergeMarkedObjects(SdrMergeMode::Intersect); + } + Cancel(); + rReq.Done (); + } + break; + + case SID_EQUALIZEWIDTH: + case SID_EQUALIZEHEIGHT: + { + // End text edit to avoid conflicts + if(mpDrawView->IsTextEdit()) + mpDrawView->SdrEndTextEdit(); + + mpDrawView->EqualizeMarkedObjects(nSId == SID_EQUALIZEWIDTH); + Cancel(); + rReq.Done (); + } + break; + + case SID_DISMANTLE: // BASIC + { + if ( mpDrawView->IsDismantlePossible() ) + { + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->DismantleMarkedObjects(); + } + Cancel(); + rReq.Done (); + } + break; + + case SID_CONNECT: // BASIC + { + if ( mpDrawView->IsPresObjSelected() ) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->CombineMarkedObjects(); + } + Cancel(); + rReq.Done (); + } + break; + + case SID_BREAK: // BASIC + { + if ( mpDrawView->IsTextEdit() ) + { + mpDrawView->SdrEndTextEdit(); + } + + if ( mpDrawView->IsBreak3DObjPossible() ) + { + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->Break3DObj(); + } + else if ( mpDrawView->IsDismantlePossible(true) ) + { + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->DismantleMarkedObjects(true); + } + else if ( mpDrawView->IsImportMtfPossible() ) + { + weld::WaitObject aWait(GetFrameWeld()); + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + const size_t nCnt=rMarkList.GetMarkCount(); + + // determine the sum of meta objects of all selected meta files + sal_uLong nCount = 0; + for(size_t nm=0; nm<nCnt; ++nm) + { + SdrMark* pM=rMarkList.GetMark(nm); + SdrObject* pObj=pM->GetMarkedSdrObj(); + SdrGrafObj* pGraf= dynamic_cast< SdrGrafObj *>( pObj ); + SdrOle2Obj* pOle2= dynamic_cast< SdrOle2Obj *>( pObj ); + + if (pGraf != nullptr) + { + if (pGraf->HasGDIMetaFile()) + { + nCount += pGraf->GetGraphic().GetGDIMetaFile().GetActionSize(); + } + else if (pGraf->isEmbeddedVectorGraphicData()) + { + nCount += pGraf->getMetafileFromEmbeddedVectorGraphicData().GetActionSize(); + } + } + + if(pOle2 && pOle2->GetGraphic()) + { + nCount += pOle2->GetGraphic()->GetGDIMetaFile().GetActionSize(); + } + } + + // decide with the sum of all meta objects if we should show a dialog + if(nCount < MIN_ACTIONS_FOR_DIALOG) + { + // nope, no dialog + mpDrawView->DoImportMarkedMtf(); + } + else + { + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateBreakDlg(GetFrameWeld(), mpDrawView.get(), GetDocSh(), nCount, static_cast<sal_uLong>(nCnt) )); + pDlg->Execute(); + } + } + + Cancel(); + rReq.Done (); + } + break; + + case SID_CONVERT_TO_3D: + { + if ( mpDrawView->IsPresObjSelected() ) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + if (mpDrawView->IsConvertTo3DObjPossible()) + { + if (mpDrawView->IsTextEdit()) + { + mpDrawView->SdrEndTextEdit(); + } + + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->ConvertMarkedObjTo3D(); + } + } + + Cancel(); + rReq.Done(); + } + break; + + case SID_FRAME_TO_TOP: // BASIC + { + mpDrawView->PutMarkedToTop(); + Cancel(); + rReq.Done (); + } + break; + + case SID_MOREFRONT: // BASIC + case SID_FRAME_UP: // BASIC + { + mpDrawView->MovMarkedToTop(); + Cancel(); + rReq.Done (); + } + break; + + case SID_MOREBACK: // BASIC + case SID_FRAME_DOWN: // BASIC + { + mpDrawView->MovMarkedToBtm(); + Cancel(); + rReq.Done (); + } + break; + + case SID_FRAME_TO_BOTTOM: // BASIC + { + mpDrawView->PutMarkedToBtm(); + Cancel(); + rReq.Done (); + } + break; + + case SID_HORIZONTAL: // BASIC + case SID_FLIP_HORIZONTAL: + { + mpDrawView->MirrorAllMarkedHorizontal(); + Cancel(); + rReq.Done (); + } + break; + + case SID_VERTICAL: // BASIC + case SID_FLIP_VERTICAL: + { + mpDrawView->MirrorAllMarkedVertical(); + Cancel(); + rReq.Done (); + } + break; + + case SID_OBJECT_ALIGN_LEFT: // BASIC + { + mpDrawView->AlignMarkedObjects(SdrHorAlign::Left, SdrVertAlign::NONE); + Cancel(); + rReq.Done (); + } + break; + + case SID_OBJECT_ALIGN_CENTER: // BASIC + { + mpDrawView->AlignMarkedObjects(SdrHorAlign::Center, SdrVertAlign::NONE); + Cancel(); + rReq.Done (); + } + break; + + case SID_OBJECT_ALIGN_RIGHT: // BASIC + { + mpDrawView->AlignMarkedObjects(SdrHorAlign::Right, SdrVertAlign::NONE); + Cancel(); + rReq.Done (); + } + break; + + case SID_OBJECT_ALIGN_UP: // BASIC + { + mpDrawView->AlignMarkedObjects(SdrHorAlign::NONE, SdrVertAlign::Top); + Cancel(); + rReq.Done (); + } + break; + + case SID_OBJECT_ALIGN_MIDDLE: // BASIC + { + mpDrawView->AlignMarkedObjects(SdrHorAlign::NONE, SdrVertAlign::Center); + Cancel(); + rReq.Done (); + } + break; + + case SID_OBJECT_ALIGN_DOWN: // BASIC + { + mpDrawView->AlignMarkedObjects(SdrHorAlign::NONE, SdrVertAlign::Bottom); + Cancel(); + rReq.Done (); + } + break; + + case SID_SELECTALL: // BASIC + { + if( (dynamic_cast<FuSelection*>( GetOldFunction().get() ) != nullptr) && + !GetView()->IsFrameDragSingles() && GetView()->HasMarkablePoints()) + { + if ( !mpDrawView->IsAction() ) + mpDrawView->MarkAllPoints(); + } + else + mpDrawView->SelectAll(); + + Cancel(); + rReq.Done (); + } + break; + + case SID_STYLE_NEW: // BASIC ??? + case SID_STYLE_APPLY: + case SID_STYLE_EDIT: + case SID_STYLE_DELETE: + case SID_STYLE_HIDE: + case SID_STYLE_SHOW: + case SID_STYLE_FAMILY: + case SID_STYLE_WATERCAN: + case SID_STYLE_UPDATE_BY_EXAMPLE: + case SID_STYLE_NEW_BY_EXAMPLE: + { + if( rReq.GetSlot() == SID_STYLE_EDIT && !rReq.GetArgs() ) + { + SfxStyleSheet* pStyleSheet = mpDrawView->GetStyleSheet(); + if( pStyleSheet && pStyleSheet->GetFamily() == SfxStyleFamily::Page) + pStyleSheet = static_cast<SdStyleSheet*>(pStyleSheet)->GetPseudoStyleSheet(); + + if( (pStyleSheet == nullptr) && GetView()->IsTextEdit() ) + { + GetView()->SdrEndTextEdit(); + + pStyleSheet = mpDrawView->GetStyleSheet(); + if(pStyleSheet && pStyleSheet->GetFamily() == SfxStyleFamily::Page) + pStyleSheet = static_cast<SdStyleSheet*>(pStyleSheet)->GetPseudoStyleSheet(); + } + + if( pStyleSheet == nullptr ) + { + rReq.Ignore(); + break; + } + + SfxAllItemSet aSet(GetDoc()->GetPool()); + + SfxStringItem aStyleNameItem( SID_STYLE_EDIT, pStyleSheet->GetName() ); + aSet.Put(aStyleNameItem); + + SfxUInt16Item aStyleFamilyItem( SID_STYLE_FAMILY, static_cast<sal_uInt16>(pStyleSheet->GetFamily()) ); + aSet.Put(aStyleFamilyItem); + + rReq.SetArgs(aSet); + } + + if( rReq.GetArgs() ) + { + SetCurrentFunction( FuTemplate::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + if( rReq.GetSlot() == SID_STYLE_APPLY ) + GetViewFrame()->GetBindings().Invalidate( SID_STYLE_APPLY ); + Cancel(); + } + else if( rReq.GetSlot() == SID_STYLE_APPLY ) + GetViewFrame()->GetDispatcher()->Execute( SID_STYLE_DESIGNER, SfxCallMode::ASYNCHRON ); + rReq.Ignore (); + } + break; + + case SID_IMAP: + { + sal_uInt16 nId = SvxIMapDlgChildWindow::GetChildWindowId(); + + GetViewFrame()->ToggleChildWindow( nId ); + GetViewFrame()->GetBindings().Invalidate( SID_IMAP ); + + if ( GetViewFrame()->HasChildWindow( nId ) + && ( ( ViewShell::Implementation::GetImageMapDialog() ) != nullptr ) ) + { + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + + if ( rMarkList.GetMarkCount() == 1 ) + UpdateIMapDlg( rMarkList.GetMark( 0 )->GetMarkedSdrObj() ); + } + + Cancel(); + rReq.Ignore (); + } + break; + + case SID_GRID_FRONT: + { + mpDrawView->SetGridFront( !mpDrawView->IsGridFront() ); + Cancel(); + rReq.Done (); + } + break; + + case SID_HELPLINES_FRONT: + { + mpDrawView->SetHlplFront( !mpDrawView->IsHlplFront() ); + Cancel(); + rReq.Done (); + } + break; + + case SID_FONTWORK: + { + if ( rReq.GetArgs() ) + { + GetViewFrame()->SetChildWindow(SvxFontWorkChildWindow::GetChildWindowId(), + static_cast<const SfxBoolItem&>(rReq.GetArgs()-> + Get(SID_FONTWORK)).GetValue()); + } + else + { + GetViewFrame()->ToggleChildWindow( SvxFontWorkChildWindow::GetChildWindowId() ); + } + + GetViewFrame()->GetBindings().Invalidate(SID_FONTWORK); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_COLOR_CONTROL: + { + if ( rReq.GetArgs() ) + GetViewFrame()->SetChildWindow(SvxColorChildWindow::GetChildWindowId(), + static_cast<const SfxBoolItem&>(rReq.GetArgs()-> + Get(SID_COLOR_CONTROL)).GetValue()); + else + GetViewFrame()->ToggleChildWindow(SvxColorChildWindow::GetChildWindowId() ); + + GetViewFrame()->GetBindings().Invalidate(SID_COLOR_CONTROL); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_EXTRUSION_TOGGLE: + case SID_EXTRUSION_TILT_DOWN: + case SID_EXTRUSION_TILT_UP: + case SID_EXTRUSION_TILT_LEFT: + case SID_EXTRUSION_TILT_RIGHT: + case SID_EXTRUSION_3D_COLOR: + case SID_EXTRUSION_DEPTH: + case SID_EXTRUSION_DIRECTION: + case SID_EXTRUSION_PROJECTION: + case SID_EXTRUSION_LIGHTING_DIRECTION: + case SID_EXTRUSION_LIGHTING_INTENSITY: + case SID_EXTRUSION_SURFACE: + case SID_EXTRUSION_DEPTH_FLOATER: + case SID_EXTRUSION_DIRECTION_FLOATER: + case SID_EXTRUSION_LIGHTING_FLOATER: + case SID_EXTRUSION_SURFACE_FLOATER: + case SID_EXTRUSION_DEPTH_DIALOG: + svx::ExtrusionBar::execute( mpDrawView.get(), rReq, GetViewFrame()->GetBindings() ); + Cancel(); + rReq.Ignore (); + break; + + case SID_FONTWORK_SHAPE: + case SID_FONTWORK_SHAPE_TYPE: + case SID_FONTWORK_ALIGNMENT: + case SID_FONTWORK_SAME_LETTER_HEIGHTS: + case SID_FONTWORK_CHARACTER_SPACING: + case SID_FONTWORK_KERN_CHARACTER_PAIRS: + case SID_FONTWORK_GALLERY_FLOATER: + case SID_FONTWORK_CHARACTER_SPACING_FLOATER: + case SID_FONTWORK_ALIGNMENT_FLOATER: + case SID_FONTWORK_CHARACTER_SPACING_DIALOG: + svx::FontworkBar::execute(*mpDrawView, rReq, GetViewFrame()->GetBindings()); + Cancel(); + rReq.Ignore (); + break; + + case SID_BMPMASK: + { + GetViewFrame()->ToggleChildWindow( SvxBmpMaskChildWindow::GetChildWindowId() ); + GetViewFrame()->GetBindings().Invalidate( SID_BMPMASK ); + + Cancel(); + rReq.Ignore (); + } + break; + + case SID_NAVIGATOR: + { + if ( rReq.GetArgs() ) + GetViewFrame()->SetChildWindow(SID_NAVIGATOR, + static_cast<const SfxBoolItem&>(rReq.GetArgs()-> + Get(SID_NAVIGATOR)).GetValue()); + else + GetViewFrame()->ToggleChildWindow( SID_NAVIGATOR ); + + GetViewFrame()->GetBindings().Invalidate(SID_NAVIGATOR); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_SLIDE_TRANSITIONS_PANEL: + case SID_MASTER_SLIDES_PANEL: + case SID_CUSTOM_ANIMATION_PANEL: + case SID_GALLERY: + { + // First make sure that the sidebar is visible + GetViewFrame()->ShowChildWindow(SID_SIDEBAR); + + OUString panelId; + if (nSId == SID_CUSTOM_ANIMATION_PANEL) + panelId = "SdCustomAnimationPanel"; + else if (nSId == SID_GALLERY) + panelId = "GalleryPanel"; + else if (nSId == SID_SLIDE_TRANSITIONS_PANEL) + panelId = "SdSlideTransitionPanel"; + else if (nSId == SID_MASTER_SLIDES_PANEL) + panelId = "SdAllMasterPagesPanel"; + + ::sfx2::sidebar::Sidebar::TogglePanel( + panelId, + GetViewFrame()->GetFrame().GetFrameInterface()); + + Cancel(); + rReq.Done(); + } + break; + + case SID_ANIMATION_OBJECTS: + { + if ( rReq.GetArgs() ) + GetViewFrame()->SetChildWindow( + AnimationChildWindow::GetChildWindowId(), + static_cast<const SfxBoolItem&>(rReq.GetArgs()-> + Get(SID_ANIMATION_OBJECTS)).GetValue()); + else + GetViewFrame()->ToggleChildWindow( + AnimationChildWindow::GetChildWindowId() ); + + GetViewFrame()->GetBindings().Invalidate(SID_ANIMATION_OBJECTS); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_3D_WIN: + { + if ( rReq.GetArgs() ) + GetViewFrame()->SetChildWindow( Svx3DChildWindow::GetChildWindowId(), + static_cast<const SfxBoolItem&>(rReq.GetArgs()-> + Get( SID_3D_WIN )).GetValue()); + else + GetViewFrame()->ToggleChildWindow( Svx3DChildWindow::GetChildWindowId() ); + + GetViewFrame()->GetBindings().Invalidate( SID_3D_WIN ); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_CONVERT_TO_3D_LATHE_FAST: + { + /* The call is enough. The initialization via Start3DCreation and + CreateMirrorPolygons is no longer needed if the parameter + sal_True is provided. Then a tilted rotary body with an axis left + besides the bounding rectangle of the selected objects is drawn + immediately and without user interaction. */ + mpDrawView->SdrEndTextEdit(); + if(GetActiveWindow()) + GetActiveWindow()->EnterWait(); + mpDrawView->End3DCreation(true); + Cancel(); + rReq.Ignore(); + if(GetActiveWindow()) + GetActiveWindow()->LeaveWait(); + } + break; + + case SID_PRESENTATION_DLG: + { + SetCurrentFunction( FuSlideShowDlg::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + + case SID_REMOTE_DLG: + { +#ifdef ENABLE_SDREMOTE + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateRemoteDialog(GetFrameWeld())); + pDlg->Execute(); +#endif + } + break; + + case SID_CUSTOMSHOW_DLG: + { + SetCurrentFunction( FuCustomShowDlg::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + + case SID_EXPAND_PAGE: + { + SetCurrentFunction( FuExpandPage::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + + case SID_SUMMARY_PAGE: + { + mpDrawView->SdrEndTextEdit(); + SetCurrentFunction( FuSummaryPage::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + +#if HAVE_FEATURE_AVMEDIA + case SID_AVMEDIA_PLAYER: + { + GetViewFrame()->ToggleChildWindow( ::avmedia::MediaPlayer::GetChildWindowId() ); + GetViewFrame()->GetBindings().Invalidate( SID_AVMEDIA_PLAYER ); + Cancel(); + rReq.Ignore (); + } + break; +#endif + + case SID_PRESENTATION_MINIMIZER: + { + Reference<XComponentContext> xContext(::comphelper::getProcessComponentContext()); + Reference<util::XURLTransformer> xParser(util::URLTransformer::create(xContext)); + Reference<frame::XDispatchProvider> xProvider(GetViewShellBase().GetController()->getFrame(), UNO_QUERY); + if (xProvider.is()) + { + util::URL aURL; + aURL.Complete = "vnd.com.sun.star.comp.PresentationMinimizer:execute"; + xParser->parseStrict(aURL); + uno::Reference<frame::XDispatch> xDispatch(xProvider->queryDispatch(aURL, OUString(), 0)); + if (xDispatch.is()) + { + xDispatch->dispatch(aURL, uno::Sequence< beans::PropertyValue >()); + } + } + Cancel(); + rReq.Ignore(); + } + break; + + case SID_DISPLAY_MASTER_BACKGROUND: + case SID_DISPLAY_MASTER_OBJECTS: + { + // Determine current page and toggle visibility of layers + // associated with master page background or master page shapes. + // FIXME: This solution is wrong, because shapes of master pages need + // not be on layer "background" or "backgroundobjects". + // See tdf#118613 + SdPage* pPage = GetActualPage(); + if (pPage != nullptr + && GetDoc() != nullptr) + { + SdrLayerIDSet aVisibleLayers = pPage->TRG_GetMasterPageVisibleLayers(); + SdrLayerAdmin& rLayerAdmin = GetDoc()->GetLayerAdmin(); + SdrLayerID aLayerId; + if (nSId == SID_DISPLAY_MASTER_BACKGROUND) + aLayerId = rLayerAdmin.GetLayerID(sUNO_LayerName_background); + else + aLayerId = rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects); + aVisibleLayers.Set(aLayerId, !aVisibleLayers.IsSet(aLayerId)); + pPage->TRG_SetMasterPageVisibleLayers(aVisibleLayers); + } + Cancel(); + rReq.Done(); // Mark task as done to auto-update the state of each buttons tdf#132816 + } + break; + + case SID_PHOTOALBUM: + { + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + vcl::Window* pWin = GetActiveWindow(); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateSdPhotoAlbumDialog( + pWin ? pWin->GetFrameWeld() : nullptr, + GetDoc())); + + pDlg->Execute(); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_INSERT_QRCODE: + case SID_EDIT_QRCODE: + { + VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create(); + const uno::Reference<frame::XModel> xModel = GetViewShellBase().GetController()->getModel(); + ScopedVclPtr<AbstractQrCodeGenDialog> pDlg(pFact->CreateQrCodeGenDialog( + GetFrameWeld(), xModel, rReq.GetSlot() == SID_EDIT_QRCODE)); + pDlg->Execute(); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_ADDITIONS_DIALOG: + { + OUString sAdditionsTag = ""; + + const SfxStringItem* pStringArg = rReq.GetArg<SfxStringItem>(FN_PARAM_ADDITIONS_TAG); + if (pStringArg) + sAdditionsTag = pStringArg->GetValue(); + + VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractAdditionsDialog> pDlg( + pFact->CreateAdditionsDialog(GetFrameWeld(), sAdditionsTag)); + pDlg->Execute(); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_ATTR_GLOW_COLOR: + case SID_ATTR_GLOW_RADIUS: + case SID_ATTR_GLOW_TRANSPARENCY: + case SID_ATTR_SOFTEDGE_RADIUS: + case SID_ATTR_TEXTCOLUMNS_NUMBER: + case SID_ATTR_TEXTCOLUMNS_SPACING: + if (const SfxItemSet* pNewArgs = rReq.GetArgs()) + mpDrawView->SetAttributes(*pNewArgs); + rReq.Done(); + Cancel(); + break; + + default: + { + SAL_WARN( "sd.ui", "Slot without function" ); + Cancel(); + rReq.Ignore (); + } + break; + } + + if(HasCurrentFunction()) + { + GetCurrentFunction()->Activate(); + } +} + +void DrawViewShell::ExecChar( SfxRequest &rReq ) +{ + SdDrawDocument* pDoc = GetDoc(); + if (!pDoc || !mpDrawView) + return; + + SfxItemSet aEditAttr( pDoc->GetPool() ); + mpDrawView->GetAttributes( aEditAttr ); + + //modified by wj for sym2_1580, if put old itemset into new set, + //when mpDrawView->SetAttributes(aNewAttr) it will invalidate all the item + // and use old attr to update all the attributes +// SfxItemSet aNewAttr( GetPool(), +// EE_ITEMS_START, EE_ITEMS_END ); +// aNewAttr.Put( aEditAttr, sal_False ); + SfxItemSet aNewAttr( pDoc->GetPool() ); + //modified end + + sal_uInt16 nSId = rReq.GetSlot(); + + switch ( nSId ) + { + case SID_ATTR_CHAR_FONT: + if( rReq.GetArgs() ) + { + const SvxFontItem* pItem = rReq.GetArg<SvxFontItem>(SID_ATTR_CHAR_FONT); + if (pItem) + { + aNewAttr.Put(*pItem); + } + } + break; + case SID_ATTR_CHAR_FONTHEIGHT: + if( rReq.GetArgs() ) + { + const SvxFontHeightItem* pItem = rReq.GetArg<SvxFontHeightItem>(SID_ATTR_CHAR_FONTHEIGHT); + if (pItem) + { + aNewAttr.Put(*pItem); + } + } + break; + case SID_ATTR_CHAR_WEIGHT: + if( rReq.GetArgs() ) + { + const SvxWeightItem* pItem = rReq.GetArg<SvxWeightItem>(SID_ATTR_CHAR_WEIGHT); + if (pItem) + { + aNewAttr.Put(*pItem); + } + } + break; + case SID_ATTR_CHAR_POSTURE: + if( rReq.GetArgs() ) + { + const SvxPostureItem* pItem = rReq.GetArg<SvxPostureItem>(SID_ATTR_CHAR_POSTURE); + if (pItem) + { + aNewAttr.Put(*pItem); + } + } + break; + case SID_ATTR_CHAR_UNDERLINE: + if( rReq.GetArgs() ) + { + const SvxUnderlineItem* pItem = rReq.GetArg<SvxUnderlineItem>(SID_ATTR_CHAR_UNDERLINE); + if (pItem) + { + aNewAttr.Put(*pItem); + } + else + { + FontLineStyle eFU = aEditAttr.Get( EE_CHAR_UNDERLINE ).GetLineStyle(); + aNewAttr.Put( SvxUnderlineItem( eFU != LINESTYLE_NONE ?LINESTYLE_NONE : LINESTYLE_SINGLE, EE_CHAR_UNDERLINE ) ); + } + } + break; + case SID_ATTR_CHAR_OVERLINE: + if( rReq.GetArgs() ) + { + const SvxOverlineItem* pItem = rReq.GetArg<SvxOverlineItem>(SID_ATTR_CHAR_OVERLINE); + if (pItem) + { + aNewAttr.Put(*pItem); + } + else + { + FontLineStyle eFU = aEditAttr.Get( EE_CHAR_OVERLINE ).GetLineStyle(); + aNewAttr.Put( SvxOverlineItem( eFU != LINESTYLE_NONE ?LINESTYLE_NONE : LINESTYLE_SINGLE, EE_CHAR_OVERLINE ) ); + } + } + break; + + case SID_ULINE_VAL_NONE: + { + aNewAttr.Put(SvxUnderlineItem(LINESTYLE_NONE, EE_CHAR_UNDERLINE)); + break; + } + + case SID_ULINE_VAL_SINGLE: + case SID_ULINE_VAL_DOUBLE: + case SID_ULINE_VAL_DOTTED: + { + FontLineStyle eOld = aEditAttr.Get(EE_CHAR_UNDERLINE).GetLineStyle(); + FontLineStyle eNew = eOld; + + switch (nSId) + { + case SID_ULINE_VAL_SINGLE: + eNew = ( eOld == LINESTYLE_SINGLE ) ? LINESTYLE_NONE : LINESTYLE_SINGLE; + break; + case SID_ULINE_VAL_DOUBLE: + eNew = ( eOld == LINESTYLE_DOUBLE ) ? LINESTYLE_NONE : LINESTYLE_DOUBLE; + break; + case SID_ULINE_VAL_DOTTED: + eNew = ( eOld == LINESTYLE_DOTTED ) ? LINESTYLE_NONE : LINESTYLE_DOTTED; + break; + } + + SvxUnderlineItem aUnderline(eNew, EE_CHAR_UNDERLINE); + aNewAttr.Put(aUnderline); + } + break; + + case SID_ATTR_CHAR_SHADOWED: + if( rReq.GetArgs() ) + { + const SvxShadowedItem* pItem = rReq.GetArg<SvxShadowedItem>(SID_ATTR_CHAR_SHADOWED); + if (pItem) + { + aNewAttr.Put(*pItem); + } + } + break; + case SID_ATTR_CHAR_CONTOUR: + if( rReq.GetArgs() ) + { + const SvxContourItem* pItem = rReq.GetArg<SvxContourItem>(SID_ATTR_CHAR_CONTOUR); + if (pItem) + { + aNewAttr.Put(*pItem); + } + } + break; + + case SID_ATTR_CHAR_STRIKEOUT: + if( rReq.GetArgs() ) + { + const SvxCrossedOutItem* pItem = rReq.GetArg<SvxCrossedOutItem>(SID_ATTR_CHAR_STRIKEOUT); + if (pItem) + { + aNewAttr.Put(*pItem); + } + } + break; + case SID_ATTR_CHAR_COLOR: + if( rReq.GetArgs() ) + { + const SvxColorItem* pItem = rReq.GetArg<SvxColorItem>(SID_ATTR_CHAR_COLOR); + if (pItem) + { + aNewAttr.Put(*pItem); + } + } + break; + case SID_ATTR_CHAR_KERNING: + if( rReq.GetArgs() ) + { + const SvxKerningItem* pItem = rReq.GetArg<SvxKerningItem>(SID_ATTR_CHAR_KERNING); + if (pItem) + { + aNewAttr.Put(*pItem); + } + } + break; + case SID_ATTR_CHAR_CASEMAP: + if( rReq.GetArgs() ) + { + const SvxCaseMapItem* pItem = rReq.GetArg<SvxCaseMapItem>(SID_ATTR_CHAR_CASEMAP); + if (pItem) + { + aNewAttr.Put(*pItem); + } + } + break; + case SID_SET_SUB_SCRIPT: + { + SvxEscapementItem aItem( EE_CHAR_ESCAPEMENT ); + SvxEscapement eEsc = static_cast<SvxEscapement>(aEditAttr.Get( EE_CHAR_ESCAPEMENT ).GetEnumValue()); + if( eEsc == SvxEscapement::Subscript ) + aItem.SetEscapement( SvxEscapement::Off ); + else + aItem.SetEscapement( SvxEscapement::Subscript ); + aNewAttr.Put( aItem ); + } + break; + case SID_SET_SUPER_SCRIPT: + { + SvxEscapementItem aItem( EE_CHAR_ESCAPEMENT ); + SvxEscapement eEsc = static_cast<SvxEscapement>(aEditAttr.Get( EE_CHAR_ESCAPEMENT ).GetEnumValue()); + if( eEsc == SvxEscapement::Superscript ) + aItem.SetEscapement( SvxEscapement::Off ); + else + aItem.SetEscapement( SvxEscapement::Superscript ); + aNewAttr.Put( aItem ); + } + break; + case SID_SHRINK_FONT_SIZE: + case SID_GROW_FONT_SIZE: + { + const SvxFontListItem* pFonts = dynamic_cast<const SvxFontListItem*>(GetDocSh()->GetItem( SID_ATTR_CHAR_FONTLIST ) ); + const FontList* pFontList = pFonts ? pFonts->GetFontList() : nullptr; + if( pFontList ) + { + FuText::ChangeFontSize( nSId == SID_GROW_FONT_SIZE, nullptr, pFontList, mpView ); + GetViewFrame()->GetBindings().Invalidate( SID_ATTR_CHAR_FONTHEIGHT ); + } + break; + } + case SID_ATTR_CHAR_BACK_COLOR: + if( rReq.GetArgs() ) + { + const SvxColorItem* pItem = rReq.GetArg<SvxColorItem>(SID_ATTR_CHAR_BACK_COLOR); + if (pItem) + { + aNewAttr.Put(*pItem); + } + } + break; + default: + break; + } + + mpDrawView->SetAttributes(aNewAttr); + rReq.Done(); + Cancel(); +} + +/** This method consists basically of three parts: + 1. Process the arguments of the SFX request. + 2. Use the model to create a new page or duplicate an existing one. + 3. Update the tab control and switch to the new page. +*/ +SdPage* DrawViewShell::CreateOrDuplicatePage ( + SfxRequest& rRequest, + PageKind ePageKind, + SdPage* pPage, + const sal_Int32 nInsertPosition) +{ + SdPage* pNewPage = nullptr; + if (ePageKind == PageKind::Standard && meEditMode != EditMode::MasterPage) + { + if ( mpDrawView->IsTextEdit() ) + { + mpDrawView->SdrEndTextEdit(); + } + pNewPage = ViewShell::CreateOrDuplicatePage (rRequest, ePageKind, pPage, nInsertPosition); + } + return pNewPage; +} + +void DrawViewShell::DuplicateSelectedSlides (SfxRequest& rRequest) +{ + // Create a list of the pages that are to be duplicated. The process of + // duplication alters the selection. + sal_Int32 nInsertPosition (0); + ::std::vector<SdPage*> aPagesToDuplicate; + sd::slidesorter::SlideSorter &mrSlideSorter = sd::slidesorter::SlideSorterViewShell::GetSlideSorter(GetViewShellBase())->GetSlideSorter(); + sd::slidesorter::model::PageEnumeration aSelectedPages ( + sd::slidesorter::model::PageEnumerationProvider::CreateSelectedPagesEnumeration(mrSlideSorter.GetModel())); + while (aSelectedPages.HasMoreElements()) + { + sd::slidesorter::model::SharedPageDescriptor pDescriptor (aSelectedPages.GetNextElement()); + if (pDescriptor && pDescriptor->GetPage()) + { + aPagesToDuplicate.push_back(pDescriptor->GetPage()); + nInsertPosition = pDescriptor->GetPage()->GetPageNum()+2; + } + } + + // Duplicate the pages in aPagesToDuplicate and collect the newly + // created pages in aPagesToSelect. + const bool bUndo (aPagesToDuplicate.size()>1 && mrSlideSorter.GetView().IsUndoEnabled()); + if (bUndo) + mrSlideSorter.GetView().BegUndo(SdResId(STR_INSERTPAGE)); + + ::std::vector<SdPage*> aPagesToSelect; + for(::std::vector<SdPage*>::const_iterator + iPage(aPagesToDuplicate.begin()), + iEnd(aPagesToDuplicate.end()); + iPage!=iEnd; + ++iPage, nInsertPosition+=2) + { + aPagesToSelect.push_back( + mrSlideSorter.GetViewShell()->CreateOrDuplicatePage( + rRequest, PageKind::Standard, *iPage, nInsertPosition)); + } + aPagesToDuplicate.clear(); + + if (bUndo) + mrSlideSorter.GetView().EndUndo(); + + // Set the selection to the pages in aPagesToSelect. + sd::slidesorter::controller::PageSelector& rSelector (mrSlideSorter.GetController().GetPageSelector()); + rSelector.DeselectAllPages(); + for (auto const& it: aPagesToSelect) + { + rSelector.SelectPage(it); + } +} + +void DrawViewShell::ExecutePropPanelAttr (SfxRequest const & rReq) +{ + if(SlideShow::IsRunning( GetViewShellBase() )) + return; + + SdDrawDocument* pDoc = GetDoc(); + if (!pDoc || !mpDrawView) + return; + + sal_uInt16 nSId = rReq.GetSlot(); + SfxItemSet aAttrs( pDoc->GetPool() ); + + switch ( nSId ) + { + case SID_TABLE_VERT_NONE: + case SID_TABLE_VERT_CENTER: + case SID_TABLE_VERT_BOTTOM: + SdrTextVertAdjust eTVA = SDRTEXTVERTADJUST_TOP; + if (nSId == SID_TABLE_VERT_CENTER) + eTVA = SDRTEXTVERTADJUST_CENTER; + else if (nSId == SID_TABLE_VERT_BOTTOM) + eTVA = SDRTEXTVERTADJUST_BOTTOM; + + aAttrs.Put( SdrTextVertAdjustItem(eTVA) ); + mpDrawView->SetAttributes(aAttrs); + + break; + } +} + +void DrawViewShell::GetStatePropPanelAttr(SfxItemSet& rSet) +{ + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + + SdDrawDocument* pDoc = GetDoc(); + if (!pDoc || !mpDrawView) + return; + + SfxItemSet aAttrs( pDoc->GetPool() ); + mpDrawView->GetAttributes( aAttrs ); + + while ( nWhich ) + { + sal_uInt16 nSlotId = SfxItemPool::IsWhich(nWhich) + ? GetPool().GetSlotId(nWhich) + : nWhich; + switch ( nSlotId ) + { + case SID_TABLE_VERT_NONE: + case SID_TABLE_VERT_CENTER: + case SID_TABLE_VERT_BOTTOM: + bool bContour = false; + SfxItemState eConState = aAttrs.GetItemState( SDRATTR_TEXT_CONTOURFRAME ); + if( eConState != SfxItemState::DONTCARE ) + { + bContour = aAttrs.Get( SDRATTR_TEXT_CONTOURFRAME ).GetValue(); + } + if (bContour) break; + + SfxItemState eVState = aAttrs.GetItemState( SDRATTR_TEXT_VERTADJUST ); + //SfxItemState eHState = aAttrs.GetItemState( SDRATTR_TEXT_HORZADJUST ); + + //if(SfxItemState::DONTCARE != eVState && SfxItemState::DONTCARE != eHState) + if(SfxItemState::DONTCARE != eVState) + { + SdrTextVertAdjust eTVA = aAttrs.Get(SDRATTR_TEXT_VERTADJUST).GetValue(); + bool bSet = (nSlotId == SID_TABLE_VERT_NONE && eTVA == SDRTEXTVERTADJUST_TOP) || + (nSlotId == SID_TABLE_VERT_CENTER && eTVA == SDRTEXTVERTADJUST_CENTER) || + (nSlotId == SID_TABLE_VERT_BOTTOM && eTVA == SDRTEXTVERTADJUST_BOTTOM); + rSet.Put(SfxBoolItem(nSlotId, bSet)); + } + else + { + rSet.Put(SfxBoolItem(nSlotId, false)); + } + break; + } + nWhich = aIter.NextWhich(); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviews3.cxx b/sd/source/ui/view/drviews3.cxx new file mode 100644 index 000000000..3f16136ff --- /dev/null +++ b/sd/source/ui/view/drviews3.cxx @@ -0,0 +1,1106 @@ +/* -*- 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 <config_features.h> + +#include <DrawViewShell.hxx> + +#include <sfx2/viewfrm.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/tstpitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/protitem.hxx> +#include <editeng/frmdiritem.hxx> +#include <editeng/adjustitem.hxx> +#include <svx/svdotable.hxx> +#include <editeng/numitem.hxx> +#include <svx/rulritem.hxx> +#include <svx/svxids.hrc> +#include <svx/svdpagv.hxx> +#include <sfx2/request.hxx> +#include <sfx2/dispatch.hxx> +#include <tools/urlobj.hxx> +#include <svl/eitem.hxx> +#include <svl/rectitem.hxx> +#include <svl/stritem.hxx> +#include <svx/svdoole2.hxx> +#include <svl/itempool.hxx> +#include <svl/ptitem.hxx> +#include <basic/sbstar.hxx> +#include <basic/sberrors.hxx> +#include <svx/fmshell.hxx> +#include <svx/f3dchild.hxx> +#include <svx/float3d.hxx> +#include <svx/sdmetitm.hxx> +#include <svx/svdogrp.hxx> +#include <svx/diagram/IDiagramHelper.hxx> + +#include <app.hrc> +#include <strings.hrc> + +#include <sdundogr.hxx> +#include <undopage.hxx> +#include <fupoor.hxx> +#include <slideshow.hxx> +#include <sdpage.hxx> +#include <Window.hxx> +#include <sdresid.hxx> +#include <unokywds.hxx> +#include <drawview.hxx> +#include <drawdoc.hxx> +#include <DrawDocShell.hxx> +#include <sdabstdlg.hxx> +#include <sfx2/ipclient.hxx> +#include <tools/diagnose_ex.h> +#include <ViewShellBase.hxx> +#include <FormShellManager.hxx> +#include <LayerTabBar.hxx> +#include <com/sun/star/drawing/framework/XControllerManager.hpp> +#include <com/sun/star/drawing/framework/XConfigurationController.hpp> +#include <com/sun/star/drawing/framework/XConfiguration.hpp> +#include <com/sun/star/drawing/XShape.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <editeng/lspcitem.hxx> +#include <editeng/ulspitem.hxx> +#include <memory> +#include <comphelper/processfactory.hxx> +#include <oox/drawingml/diagram/diagram.hxx> +#include <oox/export/drawingml.hxx> +#include <oox/shape/ShapeFilterBase.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; +using ::com::sun::star::frame::XFrame; +using ::com::sun::star::frame::XController; + +namespace sd { + +/** + * handle SfxRequests for controller + */ +void DrawViewShell::ExecCtrl(SfxRequest& rReq) +{ + // except a page switch and jumps to bookmarks, nothing is executed during + // a slide show + if( HasCurrentFunction(SID_PRESENTATION) && + rReq.GetSlot() != SID_SWITCHPAGE && + rReq.GetSlot() != SID_JUMPTOMARK) + return; + + CheckLineTo (rReq); + + // End text edit mode for some requests. + sal_uInt16 nSlot = rReq.GetSlot(); + bool bAllowFocusChange = true; + switch (nSlot) + { + case SID_OUTPUT_QUALITY_COLOR: + case SID_OUTPUT_QUALITY_GRAYSCALE: + case SID_OUTPUT_QUALITY_BLACKWHITE: + case SID_OUTPUT_QUALITY_CONTRAST: + // Do nothing. + break; + case SID_SWITCHPAGE: + if (rReq.GetArgs() && rReq.GetArgs()->Count () == 1) + { + const SfxBoolItem* pAllowFocusChange = rReq.GetArg<SfxBoolItem>(SID_SWITCHPAGE); + bAllowFocusChange = pAllowFocusChange->GetValue(); + if (!bAllowFocusChange) + break; + } + [[fallthrough]]; + default: + if ( mpDrawView->IsTextEdit() ) + { + mpDrawView->SdrEndTextEdit(); + } + } + + // sal_uInt16 nSlot = rReq.GetSlot(); + switch (nSlot) + { + case SID_SWITCHPAGE: // BASIC + { + // switch page in running slide show + if(SlideShow::IsRunning(GetViewShellBase()) && rReq.GetArgs()) + { + if (const SfxUInt32Item* pWhatPage = rReq.GetArg<SfxUInt32Item>(ID_VAL_WHATPAGE)) + SlideShow::GetSlideShow(GetViewShellBase())->jumpToPageNumber(static_cast<sal_Int32>((pWhatPage->GetValue()-1)>>1)); + } + else + { + const SfxItemSet *pArgs = rReq.GetArgs (); + sal_uInt16 nSelectedPage = 0; + + if (! pArgs || pArgs->Count () == 1) + { + nSelectedPage = maTabControl->GetCurPagePos(); + } + else if (pArgs->Count () == 2) + { + const SfxUInt32Item* pWhatPage = rReq.GetArg<SfxUInt32Item>(ID_VAL_WHATPAGE); + const SfxUInt32Item* pWhatKind = rReq.GetArg<SfxUInt32Item>(ID_VAL_WHATKIND); + + sal_Int32 nWhatPage = static_cast<sal_Int32>(pWhatPage->GetValue ()); + PageKind nWhatKind = static_cast<PageKind>(pWhatKind->GetValue ()); + if (nWhatKind < PageKind::Standard || nWhatKind > PageKind::Handout) + { +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_BAD_PROP_VALUE); +#endif + rReq.Ignore (); + break; + } + else if (meEditMode != EditMode::MasterPage) + { + if (! CHECK_RANGE (0, nWhatPage, GetDoc()->GetSdPageCount(nWhatKind))) + { +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_BAD_PROP_VALUE); +#endif + rReq.Ignore (); + break; + } + + nSelectedPage = static_cast<short>(nWhatPage); + mePageKind = nWhatKind; + } + } + else + { +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + rReq.Ignore (); + break; + } + + if( GetDocSh() && (GetDocSh()->GetCreateMode() == SfxObjectCreateMode::EMBEDDED)) + GetDocSh()->SetModified(); + + SwitchPage(nSelectedPage, bAllowFocusChange); + + if(HasCurrentFunction(SID_BEZIER_EDIT)) + GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + + Invalidate(); + InvalidateWindows(); + rReq.Done (); + } + break; + } + + case SID_SWITCHLAYER: // BASIC + { + const SfxItemSet *pArgs = rReq.GetArgs (); + + // #i87182# + bool bCurPageValid(false); + sal_uInt16 nCurPage(0); + + if(GetLayerTabControl()) + { + nCurPage = GetLayerTabControl()->GetCurPageId(); + bCurPageValid = true; + } + + if(pArgs && 1 == pArgs->Count()) + { + const SfxUInt32Item* pWhatLayer = rReq.GetArg<SfxUInt32Item>(ID_VAL_WHATLAYER); + + if(pWhatLayer) + { + nCurPage = static_cast<short>(pWhatLayer->GetValue()); + bCurPageValid = true; + } + } + + if(bCurPageValid) + { + OUString aLayerName( GetLayerTabControl()->GetLayerName(nCurPage)); + if (!aLayerName.isEmpty()) + { + mpDrawView->SetActiveLayer(aLayerName); + } + Invalidate(); + } + + rReq.Done (); + + break; + } + + case SID_PAGEMODE: // BASIC + { + + const SfxItemSet *pArgs = rReq.GetArgs(); + + if (pArgs && pArgs->Count () == 2) + { + const SfxBoolItem* pIsActive = rReq.GetArg<SfxBoolItem>(ID_VAL_ISACTIVE); + const SfxUInt32Item* pWhatKind = rReq.GetArg<SfxUInt32Item>(ID_VAL_WHATKIND); + + PageKind nWhatKind = static_cast<PageKind>(pWhatKind->GetValue()); + if ( nWhatKind >= PageKind::Standard && nWhatKind <= PageKind::Handout) + { + mbIsLayerModeActive = pIsActive->GetValue(); + mePageKind = nWhatKind; + } + } + + // turn on default layer of page + mpDrawView->SetActiveLayer(sUNO_LayerName_layout); + + ChangeEditMode(EditMode::Page, mbIsLayerModeActive); + + Invalidate(); + rReq.Done (); + + break; + } + + case SID_LAYERMODE: // BASIC + { + const SfxItemSet *pArgs = rReq.GetArgs(); + + if (pArgs && pArgs->Count() == 2) + { + const SfxUInt32Item* pWhatLayer = rReq.GetArg<SfxUInt32Item>(ID_VAL_WHATLAYER); + EditMode nWhatLayer = static_cast<EditMode>(pWhatLayer->GetValue()); + if (nWhatLayer == EditMode::Page || nWhatLayer == EditMode::MasterPage) + { + mbIsLayerModeActive = rReq.GetArg<SfxBoolItem>(ID_VAL_ISACTIVE)->GetValue(); + meEditMode = nWhatLayer; + } + } + + ChangeEditMode(meEditMode, !mbIsLayerModeActive); + + Invalidate(); + rReq.Done(); + + break; + } + + case SID_HEADER_AND_FOOTER: + case SID_INSERT_PAGE_NUMBER: + case SID_INSERT_DATE_TIME: + { + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + vcl::Window* pWin = GetActiveWindow(); + VclPtr<AbstractHeaderFooterDialog> pDlg(pFact->CreateHeaderFooterDialog(this, pWin ? pWin->GetFrameWeld() : nullptr, GetDoc(), mpActualPage)); + auto xRequest = std::make_shared<SfxRequest>(rReq); + rReq.Ignore(); // the 'old' request is not relevant any more + pDlg->StartExecuteAsync([this, pDlg, xRequest](sal_Int32 /*nResult*/){ + GetActiveWindow()->Invalidate(); + UpdatePreview( mpActualPage ); + + Invalidate(); + xRequest->Done(); + + pDlg->disposeOnce(); + }); + break; + } + + case SID_MASTER_LAYOUTS: + { + SdPage* pPage = GetActualPage(); + if (meEditMode == EditMode::MasterPage) + // Use the master page of the current page. + pPage = static_cast<SdPage*>(&pPage->TRG_GetMasterPage()); + + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + vcl::Window* pWin = GetActiveWindow(); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateMasterLayoutDialog(pWin ? pWin->GetFrameWeld() : nullptr, GetDoc(), pPage)); + pDlg->Execute(); + Invalidate(); + rReq.Done (); + break; + } + case SID_OBJECTRESIZE: + { + // The server likes to change the client size + OSL_ASSERT (GetViewShell()!=nullptr); + SfxInPlaceClient* pIPClient = GetViewShell()->GetIPClient(); + + if ( pIPClient && pIPClient->IsObjectInPlaceActive() ) + { + const SfxRectangleItem& rRect = + rReq.GetArgs()->Get(SID_OBJECTRESIZE); + ::tools::Rectangle aRect( GetActiveWindow()->PixelToLogic( rRect.GetValue() ) ); + + if ( mpDrawView->AreObjectsMarked() ) + { + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + + if (rMarkList.GetMarkCount() == 1) + { + SdrMark* pMark = rMarkList.GetMark(0); + SdrObject* pObj = pMark->GetMarkedSdrObj(); + + SdrOle2Obj* pOle2Obj = dynamic_cast< SdrOle2Obj* >( pObj ); + if(pOle2Obj) + { + if( pOle2Obj->GetObjRef().is() ) + { + pOle2Obj->SetLogicRect(aRect); + } + } + } + } + } + rReq.Ignore (); + break; + } + + case SID_RELOAD: + { + sal_uInt16 nId = Svx3DChildWindow::GetChildWindowId(); + SfxViewFrame* pFrame = GetViewFrame(); + + try + { + Reference< XFrame > xFrame( pFrame->GetFrame().GetFrameInterface(), UNO_SET_THROW ); + + // Save the current configuration of panes and views. + Reference<XControllerManager> xControllerManager ( + GetViewShellBase().GetController(), UNO_QUERY_THROW); + Reference<XConfigurationController> xConfigurationController ( + xControllerManager->getConfigurationController(), UNO_SET_THROW ); + Reference<XConfiguration> xConfiguration ( + xConfigurationController->getRequestedConfiguration(), UNO_SET_THROW ); + + SfxChildWindow* pWindow = pFrame->GetChildWindow(nId); + if(pWindow) + { + Svx3DWin* p3DWin = static_cast<Svx3DWin*>(pWindow->GetWindow()); + if(p3DWin) + p3DWin->DocumentReload(); + } + + // normal forwarding to ViewFrame for execution + GetViewFrame()->ExecuteSlot(rReq); + + // From here on we must cope with this object and the frame already being + // deleted. Do not call any methods or use data members. + Reference<XController> xController( xFrame->getController(), UNO_SET_THROW ); + + // Restore the configuration. + xControllerManager.set( xController, UNO_QUERY_THROW ); + xConfigurationController.set( xControllerManager->getConfigurationController() ); + if ( ! xConfigurationController.is()) + throw RuntimeException(); + xConfigurationController->restoreConfiguration(xConfiguration); + } + catch (RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd.view"); + } + + // We have to return immediately to avoid accessing this object. + return; + } + + case SID_JUMPTOMARK: + { + if( rReq.GetArgs() ) + { + const SfxStringItem* pBookmark = rReq.GetArg<SfxStringItem>(SID_JUMPTOMARK); + + if (pBookmark) + { + OUString sBookmark(INetURLObject::decode(pBookmark->GetValue(), INetURLObject::DecodeMechanism::WithCharset)); + + rtl::Reference< sd::SlideShow > xSlideshow( SlideShow::GetSlideShow( GetViewShellBase() ) ); + if(xSlideshow.is() && xSlideshow->isRunning()) + { + xSlideshow->jumpToBookmark(sBookmark); + } + else + { + GotoBookmark(sBookmark); + } + } + } + rReq.Done(); + break; + } + + case SID_OUTPUT_QUALITY_COLOR: + case SID_OUTPUT_QUALITY_GRAYSCALE: + case SID_OUTPUT_QUALITY_BLACKWHITE: + case SID_OUTPUT_QUALITY_CONTRAST: + { + ExecReq( rReq ); + break; + } + + case SID_MAIL_SCROLLBODY_PAGEDOWN: + { + ExecReq( rReq ); + break; + } + + case SID_ATTR_YEAR2000: + { + FmFormShell* pFormShell = GetViewShellBase().GetFormShellManager()->GetFormShell(); + if (pFormShell != nullptr) + { + const SfxPoolItem* pItem; + if (rReq.GetArgs()->GetItemState( + SID_ATTR_YEAR2000, true, &pItem) == SfxItemState::SET) + pFormShell->SetY2KState ( + static_cast<const SfxUInt16Item*>(pItem)->GetValue()); + } + + rReq.Done(); + } + break; + + case SID_OPT_LOCALE_CHANGED: + { + GetActiveWindow()->Invalidate(); + UpdatePreview( mpActualPage ); + rReq.Done(); + } + break; + + case SID_REGENERATE_DIAGRAM: + case SID_EDIT_DIAGRAM: + { + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + + if (1 == rMarkList.GetMarkCount()) + { + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + + // Support advanced DiagramHelper + if(nullptr != pObj && pObj->isDiagram()) + { + if(SID_REGENERATE_DIAGRAM == nSlot) + { + mpDrawView->UnmarkAll(); + pObj->getDiagramHelper()->reLayout(*static_cast<SdrObjGroup*>(pObj)); + mpDrawView->MarkObj(pObj, mpDrawView->GetSdrPageView()); + } + else // SID_EDIT_DIAGRAM + { + VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create(); + ScopedVclPtr<VclAbstractDialog> pDlg = pFact->CreateDiagramDialog( + GetFrameWeld(), + *static_cast<SdrObjGroup*>(pObj)); + pDlg->Execute(); + } + } + } + + rReq.Done(); + } + break; + + default: + break; + } +} + +void DrawViewShell::ExecRuler(SfxRequest& rReq) +{ + // nothing is executed during a slide show! + if(HasCurrentFunction(SID_PRESENTATION)) + return; + + CheckLineTo (rReq); + + const SfxItemSet* pArgs = rReq.GetArgs(); + const Point aPagePos( GetActiveWindow()->GetViewOrigin() ); + Size aPageSize = mpActualPage->GetSize(); + Size aViewSize = GetActiveWindow()->GetViewSize(); + + switch ( rReq.GetSlot() ) + { + case SID_ATTR_LONG_LRSPACE: + if (pArgs) + { + std::unique_ptr<SdUndoGroup> pUndoGroup(new SdUndoGroup(GetDoc())); + pUndoGroup->SetComment(SdResId(STR_UNDO_CHANGE_PAGEBORDER)); + + const SvxLongLRSpaceItem& rLRSpace = + pArgs->Get(SID_ATTR_LONG_LRSPACE); + + if( mpDrawView->IsTextEdit() ) + { + ::tools::Rectangle aRect = maMarkRect; + aRect.SetPos(aRect.TopLeft() + aPagePos); + aRect.SetLeft( rLRSpace.GetLeft() ); + aRect.SetRight( aViewSize.Width() - rLRSpace.GetRight() ); + aRect.SetPos(aRect.TopLeft() - aPagePos); + if ( aRect != maMarkRect) + { + mpDrawView->SetAllMarkedRect(aRect); + maMarkRect = mpDrawView->GetAllMarkedRect(); + Invalidate( SID_RULER_OBJECT ); + } + } + else + { + ::tools::Long nLeft = std::max(::tools::Long(0), rLRSpace.GetLeft() - aPagePos.X()); + ::tools::Long nRight = std::max(::tools::Long(0), rLRSpace.GetRight() + aPagePos.X() + + aPageSize.Width() - aViewSize.Width()); + + sal_uInt16 nPageCnt = GetDoc()->GetSdPageCount(mePageKind); + sal_uInt16 i; + for ( i = 0; i < nPageCnt; i++) + { + SdPage* pPage = GetDoc()->GetSdPage(i, mePageKind); + SdUndoAction* pUndo = new SdPageLRUndoAction(GetDoc(), + pPage, + pPage->GetLeftBorder(), + pPage->GetRightBorder(), + nLeft, nRight); + pUndoGroup->AddAction(pUndo); + pPage->SetLeftBorder(nLeft); + pPage->SetRightBorder(nRight); + } + nPageCnt = GetDoc()->GetMasterSdPageCount(mePageKind); + + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = GetDoc()->GetMasterSdPage(i, mePageKind); + SdUndoAction* pUndo = new SdPageLRUndoAction(GetDoc(), + pPage, + pPage->GetLeftBorder(), + pPage->GetRightBorder(), + nLeft, nRight); + pUndoGroup->AddAction(pUndo); + pPage->SetLeftBorder(nLeft); + pPage->SetRightBorder(nRight); + } + InvalidateWindows(); + } + + // give the undo group to the undo manager + GetViewFrame()->GetObjectShell()->GetUndoManager()-> + AddUndoAction(std::move(pUndoGroup)); + } + break; + case SID_ATTR_LONG_ULSPACE: + if (pArgs) + { + std::unique_ptr<SdUndoGroup> pUndoGroup(new SdUndoGroup(GetDoc())); + pUndoGroup->SetComment(SdResId(STR_UNDO_CHANGE_PAGEBORDER)); + + const SvxLongULSpaceItem& rULSpace = + pArgs->Get(SID_ATTR_LONG_ULSPACE); + + if( mpDrawView->IsTextEdit() ) + { + ::tools::Rectangle aRect = maMarkRect; + aRect.SetPos(aRect.TopLeft() + aPagePos); + aRect.SetTop( rULSpace.GetUpper() ); + aRect.SetBottom( aViewSize.Height() - rULSpace.GetLower() ); + aRect.SetPos(aRect.TopLeft() - aPagePos); + + if ( aRect != maMarkRect) + { + mpDrawView->SetAllMarkedRect(aRect); + maMarkRect = mpDrawView->GetAllMarkedRect(); + Invalidate( SID_RULER_OBJECT ); + } + } + else + { + ::tools::Long nUpper = std::max(::tools::Long(0), rULSpace.GetUpper() - aPagePos.Y()); + ::tools::Long nLower = std::max(::tools::Long(0), rULSpace.GetLower() + aPagePos.Y() + + aPageSize.Height() - aViewSize.Height()); + + sal_uInt16 nPageCnt = GetDoc()->GetSdPageCount(mePageKind); + sal_uInt16 i; + for ( i = 0; i < nPageCnt; i++) + { + SdPage* pPage = GetDoc()->GetSdPage(i, mePageKind); + SdUndoAction* pUndo = new SdPageULUndoAction(GetDoc(), + pPage, + pPage->GetUpperBorder(), + pPage->GetLowerBorder(), + nUpper, nLower); + pUndoGroup->AddAction(pUndo); + pPage->SetUpperBorder(nUpper); + pPage->SetLowerBorder(nLower); + } + nPageCnt = GetDoc()->GetMasterSdPageCount(mePageKind); + + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = GetDoc()->GetMasterSdPage(i, mePageKind); + SdUndoAction* pUndo = new SdPageULUndoAction(GetDoc(), + pPage, + pPage->GetUpperBorder(), + pPage->GetLowerBorder(), + nUpper, nLower); + pUndoGroup->AddAction(pUndo); + pPage->SetUpperBorder(nUpper); + pPage->SetLowerBorder(nLower); + } + InvalidateWindows(); + } + + // give the undo group to the undo manager + GetViewFrame()->GetObjectShell()->GetUndoManager()-> + AddUndoAction(std::move(pUndoGroup)); + } + break; + case SID_RULER_OBJECT: + if (pArgs) + { + ::tools::Rectangle aRect = maMarkRect; + aRect.SetPos(aRect.TopLeft() + aPagePos); + + const SvxObjectItem& rOI = pArgs->Get(SID_RULER_OBJECT); + + if ( rOI.GetStartX() != rOI.GetEndX() ) + { + aRect.SetLeft( rOI.GetStartX() ); + aRect.SetRight( rOI.GetEndX() ); + } + if ( rOI.GetStartY() != rOI.GetEndY() ) + { + aRect.SetTop( rOI.GetStartY() ); + aRect.SetBottom( rOI.GetEndY() ); + } + aRect.SetPos(aRect.TopLeft() - aPagePos); + if ( aRect != maMarkRect) + { + mpDrawView->SetAllMarkedRect(aRect); + maMarkRect = mpDrawView->GetAllMarkedRect(); + Invalidate( SID_RULER_OBJECT ); + } + } + break; + case SID_ATTR_TABSTOP: + if (pArgs && mpDrawView->IsTextEdit()) + { + const SvxTabStopItem& rItem = pArgs->Get( EE_PARA_TABS ); + + SfxItemSetFixed<EE_PARA_TABS, EE_PARA_TABS> aEditAttr( GetPool() ); + + aEditAttr.Put( rItem ); + mpDrawView->SetAttributes( aEditAttr ); + + Invalidate(SID_ATTR_TABSTOP); + } + break; + case SID_ATTR_PARA_LINESPACE: + if (pArgs) + { + SvxLineSpacingItem aParaLineSP = pArgs->Get( + GetPool().GetWhich(SID_ATTR_PARA_LINESPACE)); + + SfxItemSetFixed<EE_PARA_SBL, EE_PARA_SBL> aEditAttr( GetPool() ); + aParaLineSP.SetWhich( EE_PARA_SBL ); + + aEditAttr.Put( aParaLineSP ); + mpDrawView->SetAttributes( aEditAttr ); + + Invalidate(SID_ATTR_PARA_LINESPACE); + } + break; + case SID_ATTR_PARA_ADJUST_LEFT: + { + SvxAdjustItem aItem( SvxAdjust::Left, EE_PARA_JUST ); + SfxItemSetFixed<EE_PARA_JUST, EE_PARA_JUST> aEditAttr( GetPool() ); + + aEditAttr.Put( aItem ); + mpDrawView->SetAttributes( aEditAttr ); + + Invalidate(SID_ATTR_PARA_ADJUST_LEFT); + break; + } + case SID_ATTR_PARA_ADJUST_CENTER: + { + SvxAdjustItem aItem( SvxAdjust::Center, EE_PARA_JUST ); + SfxItemSetFixed<EE_PARA_JUST, EE_PARA_JUST> aEditAttr( GetPool() ); + + aEditAttr.Put( aItem ); + mpDrawView->SetAttributes( aEditAttr ); + + Invalidate(SID_ATTR_PARA_ADJUST_CENTER); + break; + } + case SID_ATTR_PARA_ADJUST_RIGHT: + { + SvxAdjustItem aItem( SvxAdjust::Right, EE_PARA_JUST ); + SfxItemSetFixed<EE_PARA_JUST, EE_PARA_JUST> aEditAttr( GetPool() ); + + aEditAttr.Put( aItem ); + mpDrawView->SetAttributes( aEditAttr ); + + Invalidate(SID_ATTR_PARA_ADJUST_RIGHT); + break; + } + case SID_ATTR_PARA_ADJUST_BLOCK: + { + SvxAdjustItem aItem( SvxAdjust::Block, EE_PARA_JUST ); + SfxItemSetFixed<EE_PARA_JUST, EE_PARA_JUST> aEditAttr( GetPool() ); + + aEditAttr.Put( aItem ); + mpDrawView->SetAttributes( aEditAttr ); + + Invalidate(SID_ATTR_PARA_ADJUST_BLOCK); + break; + } + case SID_ATTR_PARA_ULSPACE: + if (pArgs) + { + SvxULSpaceItem aULSP = static_cast<const SvxULSpaceItem&>(pArgs->Get( + SID_ATTR_PARA_ULSPACE)); + SfxItemSetFixed<EE_PARA_ULSPACE, EE_PARA_ULSPACE> aEditAttr( GetPool() ); + aULSP.SetWhich( EE_PARA_ULSPACE ); + + aEditAttr.Put( aULSP ); + mpDrawView->SetAttributes( aEditAttr ); + + Invalidate(SID_ATTR_PARA_ULSPACE); + } + break; + case SID_ATTR_PARA_LRSPACE: + if (pArgs) + { + SvxLRSpaceItem aLRSpace = static_cast<const SvxLRSpaceItem&>(pArgs->Get( + SID_ATTR_PARA_LRSPACE)); + + SfxItemSetFixed<EE_PARA_LRSPACE, EE_PARA_LRSPACE> aEditAttr( GetPool() ); + aLRSpace.SetWhich( EE_PARA_LRSPACE ); + + aEditAttr.Put( aLRSpace ); + mpDrawView->SetAttributes( aEditAttr ); + + Invalidate(SID_ATTR_PARA_LRSPACE); + } + break; + case SID_ATTR_LRSPACE: + if (pArgs && mpDrawView->IsTextEdit()) + { + sal_uInt16 nId = SID_ATTR_PARA_LRSPACE; + const SvxLRSpaceItem& rItem = static_cast<const SvxLRSpaceItem&>( + pArgs->Get( nId )); + + SfxItemSetFixed< + EE_PARA_NUMBULLET, EE_PARA_NUMBULLET, + EE_PARA_OUTLLEVEL, EE_PARA_OUTLLEVEL, + EE_PARA_LRSPACE, EE_PARA_LRSPACE> aEditAttr( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( aEditAttr ); + + nId = EE_PARA_LRSPACE; + SvxLRSpaceItem aLRSpaceItem( rItem.GetLeft(), + rItem.GetRight(), rItem.GetTextLeft(), + rItem.GetTextFirstLineOffset(), nId ); + + const sal_Int16 nOutlineLevel = aEditAttr.Get( EE_PARA_OUTLLEVEL ).GetValue(); + const SvxLRSpaceItem& rOrigLRSpaceItem = aEditAttr.Get( EE_PARA_LRSPACE ); + const SvxNumBulletItem& rNumBulletItem = aEditAttr.Get( EE_PARA_NUMBULLET ); + if( nOutlineLevel != -1 && + rNumBulletItem.GetNumRule().GetLevelCount() > nOutlineLevel ) + { + const SvxNumberFormat& rFormat = rNumBulletItem.GetNumRule().GetLevel(nOutlineLevel); + SvxNumberFormat aFormat(rFormat); + + // left margin gets distributed onto LRSpace item + // and number format AbsLSpace - this fixes + // n#707779 (previously, LRSpace left indent could + // become negative - EditEngine really does not + // like that. + const auto nAbsLSpace=aFormat.GetAbsLSpace(); + const ::tools::Long nTxtLeft=rItem.GetTextLeft(); + const ::tools::Long nLeftIndent=std::max(::tools::Long(0),nTxtLeft - nAbsLSpace); + aLRSpaceItem.SetTextLeft(nLeftIndent); + // control for clipped left indent - remainder + // reduces number format first line indent + aFormat.SetAbsLSpace(nTxtLeft - nLeftIndent); + + // negative first line indent goes to the number + // format, positive to the lrSpace item + if( rItem.GetTextFirstLineOffset() < 0 ) + { + aFormat.SetFirstLineOffset( + rItem.GetTextFirstLineOffset() + - rOrigLRSpaceItem.GetTextFirstLineOffset() + + aFormat.GetCharTextDistance()); + aLRSpaceItem.SetTextFirstLineOffset(0); + } + else + { + aFormat.SetFirstLineOffset(0); + aLRSpaceItem.SetTextFirstLineOffset( + rItem.GetTextFirstLineOffset() + - aFormat.GetFirstLineOffset() //TODO: overflow + + aFormat.GetCharTextDistance()); + } + + if( rFormat != aFormat ) + { + // put all items + const_cast<SvxNumRule&>(rNumBulletItem.GetNumRule()).SetLevel(nOutlineLevel,aFormat); + aEditAttr.Put( rNumBulletItem ); + aEditAttr.Put( aLRSpaceItem ); + mpDrawView->SetAttributes( aEditAttr ); + + Invalidate(SID_ATTR_PARA_LRSPACE); + break; + } + } + + // only put lrSpace item + SfxItemSetFixed<EE_PARA_LRSPACE, EE_PARA_LRSPACE> aEditAttrReduced( GetDoc()->GetPool() ); + aEditAttrReduced.Put( aLRSpaceItem ); + mpDrawView->SetAttributes( aEditAttrReduced ); + + Invalidate(SID_ATTR_PARA_LRSPACE); + } + break; + } +} + +void DrawViewShell::GetRulerState(SfxItemSet& rSet) +{ + Point aOrigin; + + if (mpDrawView->GetSdrPageView()) + { + aOrigin = mpDrawView->GetSdrPageView()->GetPageOrigin(); + } + + Size aViewSize = GetActiveWindow()->GetViewSize(); + + const Point aPagePos( GetActiveWindow()->GetViewOrigin() ); + Size aPageSize = mpActualPage->GetSize(); + + ::tools::Rectangle aRect(aPagePos, Point( aViewSize.Width() - (aPagePos.X() + aPageSize.Width()), + aViewSize.Height() - (aPagePos.Y() + aPageSize.Height()))); + + if( mpDrawView->IsTextEdit() ) + { + Point aPnt1 = GetActiveWindow()->GetWinViewPos(); + ::tools::Rectangle aMinMaxRect( aPnt1, Size(-1, -1) ); + rSet.Put( SfxRectangleItem(SID_RULER_LR_MIN_MAX, aMinMaxRect) ); + } + else + { + rSet.Put( SfxRectangleItem(SID_RULER_LR_MIN_MAX, aRect) ); + } + + SvxLongLRSpaceItem aLRSpace(aPagePos.X() + mpActualPage->GetLeftBorder(), + aRect.Right() + mpActualPage->GetRightBorder(), + SID_ATTR_LONG_LRSPACE); + SvxLongULSpaceItem aULSpace(aPagePos.Y() + mpActualPage->GetUpperBorder(), + aRect.Bottom() + mpActualPage->GetLowerBorder(), + SID_ATTR_LONG_ULSPACE); + rSet.Put(SvxPagePosSizeItem(Point(0,0) - aPagePos, aViewSize.Width(), + aViewSize.Height())); + SfxPointItem aPointItem( SID_RULER_NULL_OFFSET, aPagePos + aOrigin ); + + SvxProtectItem aProtect( SID_RULER_PROTECT ); + + maMarkRect = mpDrawView->GetAllMarkedRect(); + + const bool bRTL = GetDoc() && GetDoc()->GetDefaultWritingMode() == css::text::WritingMode_RL_TB; + rSet.Put(SfxBoolItem(SID_RULER_TEXT_RIGHT_TO_LEFT, bRTL)); + + if( mpDrawView->AreObjectsMarked() ) + { + if( mpDrawView->IsTextEdit() ) + { + SdrObject* pObj = mpDrawView->GetMarkedObjectList().GetMark( 0 )->GetMarkedSdrObj(); + if( pObj->GetObjInventor() == SdrInventor::Default) + { + SfxItemSet aEditAttr( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( aEditAttr ); + if( aEditAttr.GetItemState( EE_PARA_TABS ) >= SfxItemState::DEFAULT ) + { + const SvxTabStopItem& rItem = aEditAttr.Get( EE_PARA_TABS ); + rSet.Put( rItem ); + + const SvxLRSpaceItem& rLRSpaceItem = aEditAttr.Get( EE_PARA_LRSPACE ); + SvxLRSpaceItem aLRSpaceItem( rLRSpaceItem.GetLeft(), + rLRSpaceItem.GetRight(), rLRSpaceItem.GetTextLeft(), + rLRSpaceItem.GetTextFirstLineOffset(), SID_ATTR_PARA_LRSPACE ); + + const sal_Int16 nOutlineLevel = aEditAttr.Get( EE_PARA_OUTLLEVEL ).GetValue(); + const SvxNumBulletItem& rNumBulletItem = aEditAttr.Get( EE_PARA_NUMBULLET ); + if( nOutlineLevel != -1 && + rNumBulletItem.GetNumRule().GetLevelCount() > nOutlineLevel ) + { + const SvxNumberFormat& rFormat = rNumBulletItem.GetNumRule().GetLevel(nOutlineLevel); + aLRSpaceItem.SetTextLeft(rFormat.GetAbsLSpace() + rLRSpaceItem.GetTextLeft()); + aLRSpaceItem.SetTextFirstLineOffset( + rLRSpaceItem.GetTextFirstLineOffset() + rFormat.GetFirstLineOffset() + //TODO: overflow + - rFormat.GetCharTextDistance()); + } + + rSet.Put( aLRSpaceItem ); + + Point aPos( aPagePos + maMarkRect.TopLeft() ); + + if ( aEditAttr.GetItemState( SDRATTR_TEXT_LEFTDIST ) == SfxItemState::SET ) + { + const SdrMetricItem& rTLDItem = aEditAttr.Get( SDRATTR_TEXT_LEFTDIST ); + ::tools::Long nLD = rTLDItem.GetValue(); + aPos.AdjustX(nLD ); + } + + aPointItem.SetValue( aPos ); + + ::tools::Rectangle aParaRect(maMarkRect); + if (pObj->GetObjIdentifier() == SdrObjKind::Table) + { + sdr::table::SdrTableObj* pTable = static_cast<sdr::table::SdrTableObj*>(pObj); + sdr::table::CellPos cellpos; + pTable->getActiveCellPos(cellpos); + pTable->getCellBounds(cellpos, aParaRect); + } + + aLRSpace.SetLeft(aPagePos.X() + aParaRect.Left()); + + if ( aEditAttr.GetItemState( SDRATTR_TEXT_LEFTDIST ) == SfxItemState::SET ) + { + const SdrMetricItem& rTLDItem = aEditAttr.Get( SDRATTR_TEXT_LEFTDIST ); + ::tools::Long nLD = rTLDItem.GetValue(); + aLRSpace.SetLeft( aLRSpace.GetLeft() + nLD ); + } + + aLRSpace.SetRight(aRect.Right() + aPageSize.Width() - aParaRect.Right()); + + if ( aEditAttr.GetItemState( SDRATTR_TEXT_RIGHTDIST ) == SfxItemState::SET ) + { + const SdrMetricItem& rTRDItem = aEditAttr.Get( SDRATTR_TEXT_RIGHTDIST ); + ::tools::Long nRD = rTRDItem.GetValue(); + aLRSpace.SetRight( aLRSpace.GetRight() + nRD ); + } + + aULSpace.SetUpper( aPagePos.Y() + maMarkRect.Top() ); + aULSpace.SetLower( aRect.Bottom() + aPageSize.Height() - maMarkRect.Bottom() ); + + rSet.DisableItem( SID_RULER_OBJECT ); + + // lock page margins + aProtect.SetSizeProtect( true ); + aProtect.SetPosProtect( true ); + } + + if( aEditAttr.GetItemState( EE_PARA_WRITINGDIR ) >= SfxItemState::DEFAULT ) + { + const SvxFrameDirectionItem& rItem = aEditAttr.Get( EE_PARA_WRITINGDIR ); + rSet.Put(SfxBoolItem(SID_RULER_TEXT_RIGHT_TO_LEFT, rItem.GetValue() == SvxFrameDirection::Horizontal_RL_TB)); + } + } + } + else + { + rSet.DisableItem( EE_PARA_TABS ); + rSet.DisableItem( SID_RULER_TEXT_RIGHT_TO_LEFT ); + + if( mpDrawView->IsResizeAllowed(true) ) + { + ::tools::Rectangle aResizeRect( maMarkRect ); + + aResizeRect.SetPos(aResizeRect.TopLeft() + aPagePos); + SvxObjectItem aObjItem(aResizeRect.Left(), aResizeRect.Right(), + aResizeRect.Top(), aResizeRect.Bottom()); + rSet.Put(aObjItem); + rSet.DisableItem( EE_PARA_TABS ); + } + else + { + rSet.DisableItem( SID_RULER_OBJECT ); + } + } + } + else + { + rSet.DisableItem( SID_RULER_OBJECT ); + rSet.DisableItem( EE_PARA_TABS ); + } + + rSet.Put( aLRSpace ); + rSet.Put( aULSpace ); + + rSet.Put( aPointItem ); + rSet.Put( aProtect ); +} + +void DrawViewShell::ExecStatusBar(SfxRequest& rReq) +{ + // nothing is executed during a slide show! + if(HasCurrentFunction(SID_PRESENTATION)) + return; + + CheckLineTo (rReq); + + switch ( rReq.GetSlot() ) + { + case SID_ATTR_SIZE: + { + GetViewFrame()->GetDispatcher()->Execute( SID_ATTR_TRANSFORM, SfxCallMode::ASYNCHRON ); + } + break; + + case SID_STATUS_LAYOUT: + { + GetViewFrame()->GetDispatcher()->Execute( SID_PRESENTATION_LAYOUT, SfxCallMode::ASYNCHRON ); + } + break; + } +} + +/** + * set state of snap object entries in popup + */ +void DrawViewShell::GetSnapItemState( SfxItemSet &rSet ) +{ + SdrPageView* pPV; + Point aMPos = GetActiveWindow()->PixelToLogic(maMousePos); + sal_uInt16 nHitLog = static_cast<sal_uInt16>(GetActiveWindow()->PixelToLogic( + Size(FuPoor::HITPIX,0)).Width()); + sal_uInt16 nHelpLine; + + if ( !mpDrawView->PickHelpLine(aMPos, nHitLog, *GetActiveWindow()->GetOutDev(), nHelpLine, pPV) ) + return; + + const SdrHelpLine& rHelpLine = (pPV->GetHelpLines())[nHelpLine]; + + if ( rHelpLine.GetKind() == SdrHelpLineKind::Point ) + { + rSet.Put( SfxStringItem( SID_SET_SNAPITEM, + SdResId( STR_POPUP_EDIT_SNAPPOINT)) ); + rSet.Put( SfxStringItem( SID_DELETE_SNAPITEM, + SdResId( STR_POPUP_DELETE_SNAPPOINT)) ); + } + else + { + rSet.Put( SfxStringItem( SID_SET_SNAPITEM, + SdResId( STR_POPUP_EDIT_SNAPLINE)) ); + rSet.Put( SfxStringItem( SID_DELETE_SNAPITEM, + SdResId( STR_POPUP_DELETE_SNAPLINE)) ); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviews4.cxx b/sd/source/ui/view/drviews4.cxx new file mode 100644 index 000000000..df251880d --- /dev/null +++ b/sd/source/ui/view/drviews4.cxx @@ -0,0 +1,982 @@ +/* -*- 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 <com/sun/star/drawing/XDrawPagesSupplier.hpp> + +#include <DrawViewShell.hxx> +#include <svl/intitem.hxx> +#include <svl/stritem.hxx> +#include <svl/urlbmk.hxx> +#include <svx/svdpagv.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/flditem.hxx> +#include <svx/svxids.hrc> +#include <svx/ruler.hxx> +#include <svx/svdobjkind.hxx> +#include <editeng/outliner.hxx> +#include <sfx2/ipclient.hxx> +#include <sfx2/dispatch.hxx> +#include <svx/svdopath.hxx> +#include <sfx2/viewfrm.hxx> +#include <editeng/editview.hxx> +#include <tools/diagnose_ex.h> +#include <vcl/cursor.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/dialoghelper.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weldutils.hxx> + +#include <app.hrc> +#include <strings.hrc> + +#include <DrawDocShell.hxx> +#include <drawdoc.hxx> +#include <Window.hxx> +#include <fupoor.hxx> +#include <sdmod.hxx> +#include <Ruler.hxx> +#include <sdresid.hxx> +#include <sdpage.hxx> +#include <slideshow.hxx> +#include <sdpopup.hxx> +#include <drawview.hxx> +#include <svx/bmpmask.hxx> +#include <LayerTabBar.hxx> +#include <ViewShellBase.hxx> + +#include <SlideSorterViewShell.hxx> +#include <svx/svditer.hxx> + +#include <navigatr.hxx> +#include <memory> + +namespace { + void EndTextEditOnPage(sal_uInt16 nPageId) + { + SfxViewShell* pShell = SfxViewShell::GetFirst(); + while (pShell) + { + ::sd::ViewShellBase* pBase = dynamic_cast<::sd::ViewShellBase*>(pShell); + if (pBase) + { + ::sd::ViewShell* pViewSh = pBase->GetMainViewShell().get(); + ::sd::DrawViewShell* pDrawSh = dynamic_cast<::sd::DrawViewShell*>(pViewSh); + if (pDrawSh && pDrawSh->GetDrawView() && pDrawSh->getCurrentPage()->getPageId() == nPageId) + pDrawSh->GetDrawView()->SdrEndTextEdit(); + } + + pShell = SfxViewShell::GetNext(*pShell); + } + } +} + +namespace sd { + +#define PIPETTE_RANGE 0 + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing; + +void DrawViewShell::DeleteActualPage() +{ + mpDrawView->SdrEndTextEdit(); + + try + { + Reference<XDrawPagesSupplier> xDrawPagesSupplier( GetDoc()->getUnoModel(), UNO_QUERY_THROW ); + Reference<XDrawPages> xPages( xDrawPagesSupplier->getDrawPages(), UNO_SET_THROW ); + sal_uInt16 nPageCount = GetDoc()->GetSdPageCount(mePageKind); + SdPage* pPage = nullptr; + std::vector<Reference<XDrawPage>> pagesToDelete; + + GetView()->BegUndo(SdResId(STR_UNDO_DELETEPAGES)); + + for (sal_uInt16 i = 0; i < nPageCount; i++) + { + pPage = GetDoc()->GetSdPage(i, mePageKind); + sal_uInt16 nPageIndex = maTabControl->GetPagePos(pPage->getPageId()); + + slidesorter::SlideSorterViewShell* pVShell + = slidesorter::SlideSorterViewShell::GetSlideSorter(GetViewShellBase()); + bool bUseSlideSorter = pVShell != nullptr; + + if((bUseSlideSorter && IsSelected(nPageIndex)) || (!bUseSlideSorter && pPage->IsSelected())) + { + EndTextEditOnPage(pPage->getPageId()); + Reference< XDrawPage > xPage( xPages->getByIndex( nPageIndex ), UNO_QUERY_THROW ); + pagesToDelete.push_back(xPage); + } + } + for (const auto &xPage: pagesToDelete) + { + xPages->remove(xPage); + } + + GetView()->EndUndo(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "SelectionManager::DeleteSelectedMasterPages()"); + } +} + +void DrawViewShell::DeleteActualLayer() +{ + if(!GetLayerTabControl()) // #i87182# + { + OSL_ENSURE(false, "No LayerTabBar (!)"); + return; + } + + SdrLayerAdmin& rAdmin = GetDoc()->GetLayerAdmin(); + sal_uInt16 nId = GetLayerTabControl()->GetCurPageId(); + const OUString& rName = GetLayerTabControl()->GetLayerName(nId); + if(LayerTabBar::IsRealNameOfStandardLayer(rName)) + { + assert(false && "Standard layer may not be deleted."); + return; + } + const OUString& rDisplayName(GetLayerTabControl()->GetPageText(nId)); + OUString aString(SdResId(STR_ASK_DELETE_LAYER)); + + // replace placeholder + aString = aString.replaceFirst("$", rDisplayName); + + std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Question, VclButtonsType::YesNo, + aString)); + if (xQueryBox->run() == RET_YES) + { + const SdrLayer* pLayer = rAdmin.GetLayer(rName); + mpDrawView->DeleteLayer( pLayer->GetName() ); + + /* in order to redraw TabBar and Window; should be initiated later on by + a hint from Joe (as by a change if the layer order). */ + // ( View::Notify() --> ViewShell::ResetActualLayer() ) + + mbIsLayerModeActive = false; // so that ChangeEditMode() does something + ChangeEditMode(GetEditMode(), true); + } +} + +bool DrawViewShell::KeyInput (const KeyEvent& rKEvt, ::sd::Window* pWin) +{ + bool bRet = false; + + if (!IsInputLocked() || (rKEvt.GetKeyCode().GetCode() == KEY_ESCAPE)) + { + if(KEY_RETURN == rKEvt.GetKeyCode().GetCode() + && rKEvt.GetKeyCode().IsMod1() + && GetView()->IsTextEdit()) + { + // this should be used for cursor travelling. + SdPage* pActualPage = GetActualPage(); + const SdrMarkList& rMarkList = GetView()->GetMarkedObjectList(); + SdrTextObj* pCandidate = nullptr; + + if(pActualPage && 1 == rMarkList.GetMarkCount()) + { + SdrMark* pMark = rMarkList.GetMark(0); + + // remember which object was the text in edit mode + SdrObject* pOldObj = pMark->GetMarkedSdrObj(); + + // end text edit now + GetView()->SdrEndTextEdit(); + + // look for a new candidate, a successor of pOldObj + SdrObjListIter aIter(pActualPage, SdrIterMode::DeepNoGroups); + bool bDidVisitOldObject(false); + + while(aIter.IsMore() && !pCandidate) + { + SdrObject* pObj = aIter.Next(); + + if(auto pSdrTextObj = dynamic_cast<SdrTextObj *>( pObj )) + { + SdrInventor nInv(pObj->GetObjInventor()); + SdrObjKind nKnd(pObj->GetObjIdentifier()); + + if(SdrInventor::Default == nInv && + (SdrObjKind::TitleText == nKnd || SdrObjKind::OutlineText == nKnd || SdrObjKind::Text == nKnd) + && bDidVisitOldObject) + { + pCandidate = pSdrTextObj; + } + + if(pObj == pOldObj) + { + bDidVisitOldObject = true; + } + } + } + } + + if(pCandidate) + { + // set the new candidate to text edit mode + GetView()->UnMarkAll(); + GetView()->MarkObj(pCandidate, GetView()->GetSdrPageView()); + + GetViewFrame()->GetDispatcher()->Execute( + SID_ATTR_CHAR, SfxCallMode::ASYNCHRON); + } + else + { + // insert a new page with the same page layout + GetViewFrame()->GetDispatcher()->Execute( + SID_INSERTPAGE_QUICK, SfxCallMode::ASYNCHRON); + } + } + else + { + bRet = ViewShell::KeyInput(rKEvt, pWin); + //If object is marked , the corresponding entry is set true , else + //the corresponding entry is set false . + if(KEY_TAB == rKEvt.GetKeyCode().GetCode()) + { + FreshNavigatrTree(); + } + } + if (!bRet && !mbReadOnly) // tdf#139804 + { + bRet = GetView()->KeyInput(rKEvt, pWin); + } + } + + return bRet; +} + +/** + * Start with Drag from ruler (helper lines, origin) + */ +void DrawViewShell::StartRulerDrag ( + const Ruler& rRuler, + const MouseEvent& rMEvt) +{ + GetActiveWindow()->CaptureMouse(); + + Point aWPos = GetActiveWindow()->PixelToLogic(GetActiveWindow()->GetPointerPosPixel()); + + if ( rRuler.GetExtraRect().Contains(rMEvt.GetPosPixel()) ) + { + mpDrawView->BegSetPageOrg(aWPos); + mbIsRulerDrag = true; + } + else + { + // #i34536# if no guide-lines are visible yet, that show them + if( ! mpDrawView->IsHlplVisible()) + mpDrawView->SetHlplVisible(); + + SdrHelpLineKind eKind; + + if ( rMEvt.IsMod1() ) + eKind = SdrHelpLineKind::Point; + else if ( rRuler.IsHorizontal() ) + eKind = SdrHelpLineKind::Horizontal; + else + eKind = SdrHelpLineKind::Vertical; + + mpDrawView->BegDragHelpLine(aWPos, eKind); + mbIsRulerDrag = true; + } +} + +void DrawViewShell::FreshNavigatrTree() +{ + SfxChildWindow* pWindow = GetViewFrame()->GetChildWindow( SID_NAVIGATOR ); + if( pWindow ) + { + SdNavigatorFloat* pNavWin = static_cast<SdNavigatorFloat*>( pWindow->GetWindow() ); + if( pNavWin ) + pNavWin->FreshTree( GetDoc() ); + } +} + +void DrawViewShell::MouseButtonDown(const MouseEvent& rMEvt, + ::sd::Window* pWin) +{ + mbMouseButtonDown = true; + mbMouseSelecting = false; + + // We have to check if a context menu is shown and we have an UI + // active inplace client. In that case we have to ignore the mouse + // button down event. Otherwise we would crash (context menu has been + // opened by inplace client and we would deactivate the inplace client, + // the context menu is closed by VCL asynchronously which in the end + // would work on deleted objects or the context menu has no parent anymore) + SfxInPlaceClient* pIPClient = GetViewShell()->GetIPClient(); + bool bIsOleActive = ( pIPClient && pIPClient->IsObjectInPlaceActive() ); + + if (bIsOleActive && vcl::IsInPopupMenuExecute()) + return; + + if ( IsInputLocked() ) + return; + + ViewShell::MouseButtonDown(rMEvt, pWin); + + //If object is marked , the corresponding entry is set true , + //else the corresponding entry is set false . + FreshNavigatrTree(); + if (mbPipette) + { + SfxChildWindow* pWnd = GetViewFrame()->GetChildWindow(SvxBmpMaskChildWindow::GetChildWindowId()); + SvxBmpMask* pBmpMask = pWnd ? static_cast<SvxBmpMask*>(pWnd->GetWindow()) : nullptr; + if (pBmpMask) + pBmpMask->PipetteClicked(); + } +} + +void DrawViewShell::MouseMove(const MouseEvent& rMEvt, ::sd::Window* pWin) +{ + if ( IsMouseButtonDown() ) + mbMouseSelecting = true; + + if ( IsInputLocked() ) + return; + + if ( mpDrawView->IsAction() ) + { + ::tools::Rectangle aOutputArea(Point(0,0), GetActiveWindow()->GetOutputSizePixel()); + + if ( !aOutputArea.Contains(rMEvt.GetPosPixel()) ) + { + bool bInsideOtherWindow = false; + + if (mpContentWindow) + { + aOutputArea = ::tools::Rectangle(Point(0,0), + mpContentWindow->GetOutputSizePixel()); + + Point aPos = mpContentWindow->GetPointerPosPixel(); + if ( aOutputArea.Contains(aPos) ) + bInsideOtherWindow = true; + } + + if (! GetActiveWindow()->HasFocus ()) + { + GetActiveWindow()->ReleaseMouse (); + mpDrawView->BrkAction (); + return; + } + else if ( bInsideOtherWindow ) + { + GetActiveWindow()->ReleaseMouse(); + pWin->CaptureMouse (); + } + } + else if ( pWin != GetActiveWindow() ) + pWin->CaptureMouse(); + } + + // Since the next MouseMove may execute a IsSolidDraggingNow() in + // SdrCreateView::MovCreateObj and there the ApplicationBackgroundColor + // is needed it is necessary to set it here. + if (GetDoc()) + { + ConfigureAppBackgroundColor(); + mpDrawView->SetApplicationBackgroundColor( mnAppBackgroundColor ); + } + + ViewShell::MouseMove(rMEvt, pWin); + + maMousePos = rMEvt.GetPosPixel(); + + ::tools::Rectangle aRect; + + if ( mbIsRulerDrag ) + { + Point aLogPos = GetActiveWindow()->PixelToLogic(maMousePos); + mpDrawView->MovAction(aLogPos); + } + + if ( mpDrawView->IsAction() ) + { + mpDrawView->TakeActionRect(aRect); + aRect = GetActiveWindow()->LogicToPixel(aRect); + } + else + { + aRect = ::tools::Rectangle(maMousePos, maMousePos); + } + + ShowMousePosInfo(aRect, pWin); + + SvxBmpMask* pBmpMask = nullptr; + if (mbPipette && GetViewFrame()->HasChildWindow(SvxBmpMaskChildWindow::GetChildWindowId())) + { + SfxChildWindow* pWnd = GetViewFrame()->GetChildWindow(SvxBmpMaskChildWindow::GetChildWindowId()); + pBmpMask = pWnd ? static_cast<SvxBmpMask*>(pWnd->GetWindow()) : nullptr; + } + + if (!pBmpMask) + return; + + const ::tools::Long nStartX = maMousePos.X() - PIPETTE_RANGE; + const ::tools::Long nEndX = maMousePos.X() + PIPETTE_RANGE; + const ::tools::Long nStartY = maMousePos.Y() - PIPETTE_RANGE; + const ::tools::Long nEndY = maMousePos.Y() + PIPETTE_RANGE; + ::tools::Long nRed = 0; + ::tools::Long nGreen = 0; + ::tools::Long nBlue = 0; + const double fDiv = ( ( PIPETTE_RANGE << 1 ) + 1 ) * ( ( PIPETTE_RANGE << 1 ) + 1 ); + + for ( ::tools::Long nY = nStartY; nY <= nEndY; nY++ ) + { + for( ::tools::Long nX = nStartX; nX <= nEndX; nX++ ) + { + const Color aCol( pWin->GetOutDev()->GetPixel( pWin->PixelToLogic( Point( nX, nY ) ) ) ); + + nRed += aCol.GetRed(); + nGreen += aCol.GetGreen(); + nBlue += aCol.GetBlue(); + } + } + + pBmpMask->SetColor( Color( static_cast<sal_uInt8>( nRed / fDiv + .5 ), + static_cast<sal_uInt8>( nGreen / fDiv + .5 ), + static_cast<sal_uInt8>( nBlue / fDiv + .5 ) ) ); +} + +void DrawViewShell::MouseButtonUp(const MouseEvent& rMEvt, ::sd::Window* pWin) +{ + mbMouseButtonDown = false; + + if ( !IsInputLocked() ) + { + bool bIsSetPageOrg = mpDrawView->IsSetPageOrg(); + + if (mbIsRulerDrag) + { + ::tools::Rectangle aOutputArea(Point(0,0), GetActiveWindow()->GetOutputSizePixel()); + + if (aOutputArea.Contains(rMEvt.GetPosPixel())) + { + mpDrawView->EndAction(); + + if (bIsSetPageOrg) + GetViewFrame()->GetBindings().Invalidate(SID_RULER_NULL_OFFSET); + } + else if (rMEvt.IsLeft() && bIsSetPageOrg) + { + mpDrawView->BrkAction(); + SdPage* pPage = static_cast<SdPage*>( mpDrawView->GetSdrPageView()->GetPage() ); + Point aOrg(pPage->GetLeftBorder(), pPage->GetUpperBorder()); + mpDrawView->GetSdrPageView()->SetPageOrigin(aOrg); + GetViewFrame()->GetBindings().Invalidate(SID_RULER_NULL_OFFSET); + } + else + { + mpDrawView->BrkAction(); + } + + GetActiveWindow()->ReleaseMouse(); + mbIsRulerDrag = false; + } + else + ViewShell::MouseButtonUp(rMEvt, pWin); + //If object is marked , the corresponding entry is set true , + //else the corresponding entry is set false . + FreshNavigatrTree(); + } + mbMouseSelecting = false; +} + +void DrawViewShell::Command(const CommandEvent& rCEvt, ::sd::Window* pWin) +{ + // The command event is send to the window after a possible context + // menu from an inplace client is closed. Now we have the chance to + // deactivate the inplace client without any problem regarding parent + // windows and code on the stack. + SfxInPlaceClient* pIPClient = GetViewShell()->GetIPClient(); + bool bIsOleActive = ( pIPClient && pIPClient->IsObjectInPlaceActive() ); + if ( bIsOleActive && ( rCEvt.GetCommand() == CommandEventId::ContextMenu )) + { + // Deactivate OLE object + mpDrawView->UnmarkAll(); + SelectionHasChanged(); + return; + } + + if ( IsInputLocked() ) + return; + + if( GetView() &&GetView()->getSmartTags().Command(rCEvt) ) + return; + + const bool bNativeShow (SlideShow::IsRunning(GetViewShellBase())); + + if( rCEvt.GetCommand() == CommandEventId::PasteSelection && !bNativeShow ) + { + TransferableDataHelper aDataHelper(TransferableDataHelper::CreateFromPrimarySelection()); + + if( aDataHelper.GetTransferable().is() ) + { + Point aPos; + sal_Int8 nDnDAction = DND_ACTION_COPY; + + if( GetActiveWindow() ) + aPos = GetActiveWindow()->PixelToLogic( rCEvt.GetMousePosPixel() ); + + if( !mpDrawView->InsertData( aDataHelper, aPos, nDnDAction, false ) ) + { + INetBookmark aINetBookmark( "", "" ); + + if( ( aDataHelper.HasFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ) && + aDataHelper.GetINetBookmark( SotClipboardFormatId::NETSCAPE_BOOKMARK, aINetBookmark ) ) || + ( aDataHelper.HasFormat( SotClipboardFormatId::FILEGRPDESCRIPTOR ) && + aDataHelper.GetINetBookmark( SotClipboardFormatId::FILEGRPDESCRIPTOR, aINetBookmark ) ) || + ( aDataHelper.HasFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) && + aDataHelper.GetINetBookmark( SotClipboardFormatId::UNIFORMRESOURCELOCATOR, aINetBookmark ) ) ) + { + InsertURLField( aINetBookmark.GetURL(), aINetBookmark.GetDescription(), "" ); + } + } + } + } + else if( rCEvt.GetCommand() == CommandEventId::ContextMenu && !bNativeShow && + pWin != nullptr && !mpDrawView->IsAction() && !SD_MOD()->GetWaterCan() ) + { + OUString aPopupId; // Resource name for popup menu + + // is there a snap object under the cursor? + SdrPageView* pPV; + Point aMPos = pWin->PixelToLogic( maMousePos ); + sal_uInt16 nHitLog = static_cast<sal_uInt16>(GetActiveWindow()->PixelToLogic( + Size(FuPoor::HITPIX, 0 ) ).Width()); + sal_uInt16 nHelpLine; + // for gluepoints + SdrObject* pObj = nullptr; + sal_uInt16 nPickId = 0; + // for field command + OutlinerView* pOLV = mpDrawView->GetTextEditOutlinerView(); + const SvxFieldItem* pFldItem = nullptr; + if( pOLV ) + pFldItem = pOLV->GetFieldAtSelection(); + + // helper line + if ( mpDrawView->PickHelpLine( aMPos, nHitLog, *GetActiveWindow()->GetOutDev(), nHelpLine, pPV) ) + { + ::tools::Rectangle aRect(rCEvt.GetMousePosPixel(), Size(10, 10)); + weld::Window* pParent = weld::GetPopupParent(*pWin, aRect); + ShowSnapLineContextMenu(pParent, aRect, *pPV, nHelpLine); + return; + } + // is gluepoint under cursor marked? + else if( mpDrawView->PickGluePoint( aMPos, pObj, nPickId, pPV ) && + mpDrawView->IsGluePointMarked( pObj, nPickId ) ) + { + aPopupId = "gluepoint"; + } + // field command? + else if( pFldItem && (nullptr != dynamic_cast< const SvxDateField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxExtTimeField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxExtFileField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxAuthorField *>( pFldItem->GetField() ) ) ) + { + LanguageType eLanguage( LANGUAGE_SYSTEM ); + + // Format popup with outliner language, if possible + if( pOLV->GetOutliner() ) + { + ESelection aSelection( pOLV->GetSelection() ); + eLanguage = pOLV->GetOutliner()->GetLanguage( aSelection.nStartPara, aSelection.nStartPos ); + } + + //fdo#44998 if the outliner has captured the mouse events release the lock + //so the SdFieldPopup can get them + pOLV->ReleaseMouse(); + SdFieldPopup aFieldPopup(pFldItem->GetField(), eLanguage); + + if ( rCEvt.IsMouseEvent() ) + aMPos = rCEvt.GetMousePosPixel(); + else + aMPos = Point( 20, 20 ); + ::tools::Rectangle aRect(aMPos, Size(1, 1)); + weld::Window* pParent = weld::GetPopupParent(*pWin, aRect); + + aFieldPopup.Execute(pParent, aRect); + + std::unique_ptr<SvxFieldData> pField(aFieldPopup.GetField()); + if (pField) + { + SvxFieldItem aFieldItem( *pField, EE_FEATURE_FIELD ); + // select field, so that it will be deleted on insert + ESelection aSel = pOLV->GetSelection(); + bool bSel = true; + if( aSel.nStartPos == aSel.nEndPos ) + { + bSel = false; + aSel.nEndPos++; + } + pOLV->SetSelection( aSel ); + + pOLV->InsertField( aFieldItem ); + + // reset selection back to original state + if( !bSel ) + aSel.nEndPos--; + pOLV->SetSelection( aSel ); + } + } + else + { + // is something selected? + if (mpDrawView->AreObjectsMarked() && + mpDrawView->GetMarkedObjectList().GetMarkCount() == 1 ) + { + pObj = mpDrawView->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj(); + if( HasCurrentFunction(SID_BEZIER_EDIT) && (dynamic_cast< SdrPathObj * >( pObj ) != nullptr ) ) + { + aPopupId = "bezier"; + } + else + { + if( mpDrawView->GetTextEditObject() ) + { + OutlinerView* pOutlinerView = mpDrawView->GetTextEditOutlinerView(); + Point aPos(rCEvt.GetMousePosPixel()); + + if ( pOutlinerView ) + { + if( ( rCEvt.IsMouseEvent() && pOutlinerView->IsWrongSpelledWordAtPos(aPos) ) || + ( !rCEvt.IsMouseEvent() && pOutlinerView->IsCursorAtWrongSpelledWord() ) ) + { + // Popup for Online-Spelling now handled by DrawDocShell + Link<SpellCallbackInfo&,void> aLink = LINK(GetDocSh(), DrawDocShell, OnlineSpellCallback); + + if( !rCEvt.IsMouseEvent() ) + { + aPos = GetActiveWindow()->LogicToPixel( pOutlinerView->GetEditView().GetCursor()->GetPos() ); + } + // While showing the spell context menu + // we lock the input so that another + // context menu can not be opened during + // that time (crash #i43235#). In order + // to not lock the UI completely we + // first release the mouse. + GetActiveWindow()->ReleaseMouse(); + LockInput(); + pOutlinerView->ExecuteSpellPopup(aPos, aLink); + pOutlinerView->GetEditView().Invalidate(); + UnlockInput(); + } + else + { + if( (pObj->GetObjInventor() == SdrInventor::Default) && (pObj->GetObjIdentifier() == SdrObjKind::Table) ) + { + aPopupId = "table"; + } + else + { + aPopupId = "drawtext"; + } + } + } + } + else + { + SdrInventor nInv = pObj->GetObjInventor(); + SdrObjKind nId = pObj->GetObjIdentifier(); + + if (nInv == SdrInventor::Default) + { + switch ( nId ) + { + case SdrObjKind::OutlineText: + case SdrObjKind::Caption: + case SdrObjKind::TitleText: + case SdrObjKind::Text: + aPopupId = "textbox"; + break; + + case SdrObjKind::PathLine: + case SdrObjKind::PolyLine: + aPopupId = "curve"; + break; + + case SdrObjKind::FreehandLine: + case SdrObjKind::Edge: + aPopupId = "connector"; + break; + + case SdrObjKind::Line: + aPopupId = "line"; + break; + + case SdrObjKind::Measure: + aPopupId = "measure"; + break; + + case SdrObjKind::Rectangle: + case SdrObjKind::CircleOrEllipse: + case SdrObjKind::FreehandFill: + case SdrObjKind::PathFill: + case SdrObjKind::Polygon: + case SdrObjKind::CircleSection: + case SdrObjKind::CircleArc: + case SdrObjKind::CircleCut: + case SdrObjKind::CustomShape: + aPopupId = "draw"; + break; + + case SdrObjKind::Group: + aPopupId = "group"; + break; + + case SdrObjKind::Graphic: + aPopupId = "graphic"; + break; + + case SdrObjKind::OLE2: + aPopupId = "oleobject"; + break; + case SdrObjKind::Media: + aPopupId = "media"; + break; + case SdrObjKind::Table: + aPopupId = "table"; + break; + default: ; + } + } + else if( nInv == SdrInventor::E3d ) + { + if( nId == SdrObjKind::E3D_Scene ) + { + if( !mpDrawView->IsGroupEntered() ) + aPopupId = "3dscene"; + else + aPopupId = "3dscene2"; + } + else + aPopupId = "3dobject"; + } + else if( nInv == SdrInventor::FmForm ) + { + aPopupId = "form"; + } + } + } + } + + // multiple selection + else if (mpDrawView->AreObjectsMarked() && + mpDrawView->GetMarkedObjectList().GetMarkCount() > 1 ) + { + aPopupId = "multiselect"; + } + + // nothing selected + else + { + aPopupId = "page"; + } + } + // show Popup-Menu + if (!aPopupId.isEmpty()) + { + GetActiveWindow()->ReleaseMouse(); + + // tdf#137445 at this context menu popup time get what the + // DisableEditHyperlink would be for this position + bool bShouldDisableEditHyperlink = ShouldDisableEditHyperlink(); + + if(rCEvt.IsMouseEvent()) + GetViewFrame()->GetDispatcher()->ExecutePopup( aPopupId ); + else + { + //don't open contextmenu at mouse position if not opened via mouse + + //middle of the window if nothing is marked + Point aMenuPos(GetActiveWindow()->GetSizePixel().Width()/2 + ,GetActiveWindow()->GetSizePixel().Height()/2); + + //middle of the bounding rect if something is marked + if( mpDrawView->AreObjectsMarked() && mpDrawView->GetMarkedObjectList().GetMarkCount() >= 1 ) + { + ::tools::Rectangle aMarkRect; + mpDrawView->GetMarkedObjectList().TakeBoundRect(nullptr,aMarkRect); + aMenuPos = GetActiveWindow()->LogicToPixel( aMarkRect.Center() ); + + //move the point into the visible window area + if( aMenuPos.X() < 0 ) + aMenuPos.setX( 0 ); + if( aMenuPos.Y() < 0 ) + aMenuPos.setY( 0 ); + if( aMenuPos.X() > GetActiveWindow()->GetSizePixel().Width() ) + aMenuPos.setX( GetActiveWindow()->GetSizePixel().Width() ); + if( aMenuPos.Y() > GetActiveWindow()->GetSizePixel().Height() ) + aMenuPos.setY( GetActiveWindow()->GetSizePixel().Height() ); + } + + //open context menu at that point + GetViewFrame()->GetDispatcher()->ExecutePopup( aPopupId, GetActiveWindow(), &aMenuPos ); + } + + if (!bShouldDisableEditHyperlink) + { + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + // tdf#137445 set what the menu popup state for this was + EnableEditHyperlink(); + // ensure moAtContextMenu_DisableEditHyperlink will be cleared + // in the case that EditHyperlink is not dispatched by the menu + rBindings.Invalidate(SID_EDIT_HYPERLINK); + } + } + } + else + { + ViewShell::Command(rCEvt, pWin); + } +} + +void DrawViewShell::EnableEditHyperlink() +{ + moAtContextMenu_DisableEditHyperlink = false; +} + +void DrawViewShell::ShowMousePosInfo(const ::tools::Rectangle& rRect, + ::sd::Window const * pWin) +{ + if (mbHasRulers && pWin ) + { + RulerLine pHLines[2]; + RulerLine pVLines[2]; + ::tools::Long nHOffs = 0; + ::tools::Long nVOffs = 0; + sal_uInt16 nCnt; + + if (mpHorizontalRuler) + mpHorizontalRuler->SetLines(); + + if (mpVerticalRuler) + mpVerticalRuler->SetLines(); + + if (mpHorizontalRuler) + { + nHOffs = mpHorizontalRuler->GetNullOffset() + + mpHorizontalRuler->GetPageOffset(); + } + + if (mpVerticalRuler) + { + nVOffs = mpVerticalRuler->GetNullOffset() + + mpVerticalRuler->GetPageOffset(); + } + + nCnt = 1; + pHLines[0].nPos = rRect.Left() - nHOffs; + pVLines[0].nPos = rRect.Top() - nVOffs; + + if ( rRect.Right() != rRect.Left() || rRect.Bottom() != rRect.Top() ) + { + pHLines[1].nPos = rRect.Right() - nHOffs; + pVLines[1].nPos = rRect.Bottom() - nVOffs; + nCnt++; + } + + if (mpHorizontalRuler) + mpHorizontalRuler->SetLines(nCnt, pHLines); + if (mpVerticalRuler) + mpVerticalRuler->SetLines(nCnt, pVLines); + } + + // display with coordinates in StatusBar + OSL_ASSERT (GetViewShell()!=nullptr); + if ( GetViewShell()->GetUIActiveClient() ) + return; + + SfxItemSetFixed< + SID_CONTEXT, SID_CONTEXT, + SID_ATTR_POSITION, SID_ATTR_SIZE> aSet(GetPool()); + + GetStatusBarState(aSet); + + aSet.Put( SfxStringItem( SID_CONTEXT, mpDrawView->GetStatusText() ) ); + + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.SetState(aSet); + rBindings.Invalidate(SID_CONTEXT); + rBindings.Invalidate(SID_ATTR_POSITION); + rBindings.Invalidate(SID_ATTR_SIZE); +} + +void DrawViewShell::LockInput() +{ + mnLockCount++; +} + +void DrawViewShell::UnlockInput() +{ + DBG_ASSERT( mnLockCount, "Input for this shell is not locked!" ); + if ( mnLockCount ) + mnLockCount--; +} + +void DrawViewShell::ShowSnapLineContextMenu(weld::Window* pParent, const ::tools::Rectangle& rRect, + SdrPageView& rPageView, const sal_uInt16 nSnapLineIndex) +{ + const SdrHelpLine& rHelpLine (rPageView.GetHelpLines()[nSnapLineIndex]); + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, "modules/simpress/ui/snapmenu.ui")); + std::unique_ptr<weld::Menu> xMenu(xBuilder->weld_menu("menu")); + + if (rHelpLine.GetKind() == SdrHelpLineKind::Point) + { + xMenu->append(OUString::number(SID_SET_SNAPITEM), SdResId(STR_POPUP_EDIT_SNAPPOINT)); + xMenu->append_separator("separator"); + xMenu->append(OUString::number(SID_DELETE_SNAPITEM), SdResId(STR_POPUP_DELETE_SNAPPOINT)); + } + else + { + xMenu->append(OUString::number(SID_SET_SNAPITEM), SdResId(STR_POPUP_EDIT_SNAPLINE)); + xMenu->append_separator("separator"); + xMenu->append(OUString::number(SID_DELETE_SNAPITEM), SdResId(STR_POPUP_DELETE_SNAPLINE)); + } + + const int nResult = xMenu->popup_at_rect(pParent, rRect).toInt32(); + switch (nResult) + { + case SID_SET_SNAPITEM: + { + SfxUInt32Item aHelpLineItem (ID_VAL_INDEX, nSnapLineIndex); + const SfxPoolItem* aArguments[] = {&aHelpLineItem, nullptr}; + GetViewFrame()->GetDispatcher()->Execute( + SID_SET_SNAPITEM, + SfxCallMode::SLOT, + aArguments); + } + break; + + case SID_DELETE_SNAPITEM: + { + rPageView.DeleteHelpLine(nSnapLineIndex); + } + break; + + default: + break; + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviews5.cxx b/sd/source/ui/view/drviews5.cxx new file mode 100644 index 000000000..3eb9f39c3 --- /dev/null +++ b/sd/source/ui/view/drviews5.cxx @@ -0,0 +1,650 @@ +/* -*- 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 <DrawViewShell.hxx> +#include <editeng/outliner.hxx> +#include <svx/svxids.hrc> +#include <sfx2/request.hxx> +#include <sfx2/dispatch.hxx> +#include <svx/svdpagv.hxx> +#include <svx/svdoutl.hxx> + +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <sdcommands.h> +#include <sal/log.hxx> + +#include <svx/fmshell.hxx> +#include <editeng/eeitem.hxx> +#include <AccessibleDrawDocumentView.hxx> + +#include <sfx2/viewfrm.hxx> +#include <LayerTabBar.hxx> + +#include <app.hrc> +#include <helpids.h> +#include <optsitem.hxx> +#include <sdmod.hxx> +#include <FrameView.hxx> +#include <Window.hxx> +#include <drawview.hxx> +#include <drawdoc.hxx> +#include <DrawDocShell.hxx> +#include <Client.hxx> +#include <slideshow.hxx> +#include <unokywds.hxx> +#include <sdpage.hxx> +#include <SdUnoDrawView.hxx> +#include <ViewShellBase.hxx> +#include <FormShellManager.hxx> +#include <DrawController.hxx> +#include <memory> +#include <comphelper/lok.hxx> + +namespace sd { + +void DrawViewShell::ModelHasChanged() +{ + Invalidate(); + // that the navigator also gets an up to date state + GetViewFrame()->GetBindings().Invalidate( SID_NAVIGATOR_STATE, true ); + + SfxBoolItem aItem( SID_3D_STATE, true ); + GetViewFrame()->GetDispatcher()->ExecuteList( + SID_3D_STATE, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, { &aItem }); + + // now initialize the TextEditOutliner which was newly created by the draw engine + ::Outliner* pOutliner = mpDrawView->GetTextEditOutliner(); + if (pOutliner) + { + SfxStyleSheetPool* pSPool = static_cast<SfxStyleSheetPool*>( GetDocSh()->GetStyleSheetPool() ); + pOutliner->SetStyleSheetPool(pSPool); + } +} + +void DrawViewShell::Resize() +{ + ViewShell::Resize(); + + // tdf#151621 Do not set if the embedded object is opening in a new window. + if (GetDocSh()->GetCreateMode() == SfxObjectCreateMode::EMBEDDED + && GetDocSh()->IsInPlaceActive()) + { + SetZoomRect(GetDocSh()->GetVisArea(ASPECT_CONTENT)); + } + + rtl::Reference< sd::SlideShow > xSlideshow( SlideShow::GetSlideShow( GetViewShellBase() ) ); + if( xSlideshow.is() && xSlideshow->isRunning() && !xSlideshow->isFullScreen() ) + { + xSlideshow->resize(maViewSize); + } +} + +void DrawViewShell::ArrangeGUIElements() +{ + // Retrieve the current size (thickness) of the scroll bars. That is + // the width of the vertical and the height of the horizontal scroll + // bar. + int nScrollBarSize = GetParentWindow()->GetSettings().GetStyleSettings().GetScrollBarSize(); + maScrBarWH = Size (nScrollBarSize, nScrollBarSize); + + ViewShell::ArrangeGUIElements (); + + maTabControl->Hide(); + + OSL_ASSERT (GetViewShell()!=nullptr); + Client* pIPClient = static_cast<Client*>(GetViewShell()->GetIPClient()); + bool bClientActive = false; + if ( pIPClient && pIPClient->IsObjectInPlaceActive() ) + bClientActive = true; + + bool bInPlaceActive = GetViewFrame()->GetFrame().IsInPlace(); + + if ( mbZoomOnPage && !bInPlaceActive && !bClientActive ) + { + // with split, always resize first window + //af pWindow = mpContentWindow.get(); + SfxRequest aReq(SID_SIZE_PAGE, SfxCallMode::SLOT, GetDoc()->GetItemPool()); + ExecuteSlot( aReq ); + } +} + +/** + * Apply data of the FrameView on the current view + */ +void DrawViewShell::ReadFrameViewData(FrameView* pView) +{ + ModifyGuard aGuard( GetDoc() ); + + // this option has to be adjust at the model + GetDoc()->SetPickThroughTransparentTextFrames( + SD_MOD()->GetSdOptions(GetDoc()->GetDocumentType())->IsPickThrough()); + + // initialization of the Character-(Screen-) attribute + if (HasRuler() != pView->HasRuler()) + SetRuler( pView->HasRuler() ); + + if (mpDrawView->GetGridCoarse() != pView->GetGridCoarse()) + mpDrawView->SetGridCoarse( pView->GetGridCoarse() ); + + if (mpDrawView->GetGridFine() != pView->GetGridFine()) + mpDrawView->SetGridFine( pView->GetGridFine() ); + + if (mpDrawView->GetSnapGridWidthX() != pView->GetSnapGridWidthX() || mpDrawView->GetSnapGridWidthY() != pView->GetSnapGridWidthY()) + mpDrawView->SetSnapGridWidth(pView->GetSnapGridWidthX(), pView->GetSnapGridWidthY()); + + if (mpDrawView->IsGridVisible() != pView->IsGridVisible()) + mpDrawView->SetGridVisible( pView->IsGridVisible() ); + + if (mpDrawView->IsGridFront() != pView->IsGridFront()) + mpDrawView->SetGridFront( pView->IsGridFront() ); + + if (mpDrawView->GetSnapAngle() != pView->GetSnapAngle()) + mpDrawView->SetSnapAngle( pView->GetSnapAngle() ); + + if (mpDrawView->IsGridSnap() != pView->IsGridSnap() ) + mpDrawView->SetGridSnap( pView->IsGridSnap() ); + + if (mpDrawView->IsBordSnap() != pView->IsBordSnap() ) + mpDrawView->SetBordSnap( pView->IsBordSnap() ); + + if (mpDrawView->IsHlplSnap() != pView->IsHlplSnap() ) + mpDrawView->SetHlplSnap( pView->IsHlplSnap() ); + + if (mpDrawView->IsOFrmSnap() != pView->IsOFrmSnap() ) + mpDrawView->SetOFrmSnap( pView->IsOFrmSnap() ); + + if (mpDrawView->IsOPntSnap() != pView->IsOPntSnap() ) + mpDrawView->SetOPntSnap( pView->IsOPntSnap() ); + + if (mpDrawView->IsOConSnap() != pView->IsOConSnap() ) + mpDrawView->SetOConSnap( pView->IsOConSnap() ); + + if (mpDrawView->IsHlplVisible() != pView->IsHlplVisible() ) + mpDrawView->SetHlplVisible( pView->IsHlplVisible() ); + + if (mpDrawView->IsDragStripes() != pView->IsDragStripes() ) + mpDrawView->SetDragStripes( pView->IsDragStripes() ); + + if (mpDrawView->IsPlusHandlesAlwaysVisible() != pView->IsPlusHandlesAlwaysVisible() ) + mpDrawView->SetPlusHandlesAlwaysVisible( pView->IsPlusHandlesAlwaysVisible() ); + + if (mpDrawView->GetSnapMagneticPixel() != pView->GetSnapMagneticPixel() ) + mpDrawView->SetSnapMagneticPixel( pView->GetSnapMagneticPixel() ); + + if (mpDrawView->IsMarkedHitMovesAlways() != pView->IsMarkedHitMovesAlways() ) + mpDrawView->SetMarkedHitMovesAlways( pView->IsMarkedHitMovesAlways() ); + + if (mpDrawView->IsMoveOnlyDragging() != pView->IsMoveOnlyDragging() ) + mpDrawView->SetMoveOnlyDragging( pView->IsMoveOnlyDragging() ); + + if (mpDrawView->IsNoDragXorPolys() != pView->IsNoDragXorPolys() ) + mpDrawView->SetNoDragXorPolys( pView->IsNoDragXorPolys() ); + + if (mpDrawView->IsCrookNoContortion() != pView->IsCrookNoContortion() ) + mpDrawView->SetCrookNoContortion( pView->IsCrookNoContortion() ); + + if (mpDrawView->IsAngleSnapEnabled() != pView->IsAngleSnapEnabled() ) + mpDrawView->SetAngleSnapEnabled( pView->IsAngleSnapEnabled() ); + + if (mpDrawView->IsBigOrtho() != pView->IsBigOrtho() ) + mpDrawView->SetBigOrtho( pView->IsBigOrtho() ); + + if (mpDrawView->IsOrtho() != pView->IsOrtho() ) + mpDrawView->SetOrtho( pView->IsOrtho() ); + + if (mpDrawView->GetEliminatePolyPointLimitAngle() != pView->GetEliminatePolyPointLimitAngle() ) + mpDrawView->SetEliminatePolyPointLimitAngle( pView->GetEliminatePolyPointLimitAngle() ); + + if (mpDrawView->IsEliminatePolyPoints() != pView->IsEliminatePolyPoints() ) + mpDrawView->SetEliminatePolyPoints( pView->IsEliminatePolyPoints() ); + + if (mpDrawView->IsSolidDragging() != pView->IsSolidDragging() ) + mpDrawView->SetSolidDragging( pView->IsSolidDragging() ); + + if (mpDrawView->IsQuickTextEditMode() != pView->IsQuickEdit()) + mpDrawView->SetQuickTextEditMode( pView->IsQuickEdit() ); + + // #i26631# + if (mpDrawView->IsMasterPagePaintCaching() != pView->IsMasterPagePaintCaching()) + mpDrawView->SetMasterPagePaintCaching( pView->IsMasterPagePaintCaching() ); + + // handle size: 9 pixels + sal_uInt16 nTmp = mpDrawView->GetMarkHdlSizePixel(); + if( nTmp != 9 ) + mpDrawView->SetMarkHdlSizePixel( 9 ); + + SdrPageView* pPageView = mpDrawView->GetSdrPageView(); + if (pPageView) + { + if ( pPageView->GetVisibleLayers() != pView->GetVisibleLayers() ) + pPageView->SetVisibleLayers( pView->GetVisibleLayers() ); + + if ( pPageView->GetPrintableLayers() != pView->GetPrintableLayers() ) + pPageView->SetPrintableLayers( pView->GetPrintableLayers() ); + + if ( pPageView->GetLockedLayers() != pView->GetLockedLayers() ) + pPageView->SetLockedLayers( pView->GetLockedLayers() ); + + if (mePageKind == PageKind::Notes) + { + if (pPageView->GetHelpLines() != pView->GetNotesHelpLines()) + pPageView->SetHelpLines( pView->GetNotesHelpLines() ); + } + else if (mePageKind == PageKind::Handout) + { + if (pPageView->GetHelpLines() != pView->GetHandoutHelpLines()) + pPageView->SetHelpLines( pView->GetHandoutHelpLines() ); + } + else + { + if (pPageView->GetHelpLines() != pView->GetStandardHelpLines()) + pPageView->SetHelpLines( pView->GetStandardHelpLines() ); + } + } + + if ( mpDrawView->GetActiveLayer() != pView->GetActiveLayer() ) + mpDrawView->SetActiveLayer( pView->GetActiveLayer() ); + + sal_uInt16 nSelectedPage = 0; + + if (mePageKind != PageKind::Handout) + { + nSelectedPage = pView->GetSelectedPage(); + } + + EditMode eNewEditMode = pView->GetViewShEditMode(/*mePageKind*/); + bool bNewLayerMode = pView->IsLayerMode(); + + if(IsLayerModeActive() && bNewLayerMode) + { + // #i57936# Force mbIsLayerModeActive to false so that ChangeEditMode + // below does something regarding LayerTabBar content refresh. That refresh + // is only done when IsLayerModeActive changes. It needs to be done + // since e.g. Layer visibility was changed above and this may need + // a refresh to show the correct graphical representation + mbIsLayerModeActive = false; + } + + ChangeEditMode(eNewEditMode, bNewLayerMode); + SwitchPage(nSelectedPage); + + // restore DrawMode for 'normal' window + if(GetActiveWindow()->GetOutDev()->GetDrawMode() != pView->GetDrawMode()) + GetActiveWindow()->GetOutDev()->SetDrawMode(pView->GetDrawMode()); + + if ( mpDrawView->IsDesignMode() != pView->IsDesignMode() ) + { + SfxBoolItem aDesignModeItem( SID_FM_DESIGN_MODE, pView->IsDesignMode() ); + GetViewFrame()->GetDispatcher()->ExecuteList(SID_FM_DESIGN_MODE, + SfxCallMode::SYNCHRON | SfxCallMode::RECORD, + { &aDesignModeItem }); + } + + // has to be called in the end, because it executes a WriteFrameViewData() + if (mpDrawView->IsFrameDragSingles() != pView->IsFrameDragSingles() ) + mpDrawView->SetFrameDragSingles( pView->IsFrameDragSingles() ); +} + +/** + * Apply data of the current view on the FrameView + */ +void DrawViewShell::WriteFrameViewData() +{ + // store character-(screen-) attribute of FrameView + mpFrameView->SetRuler( HasRuler() ); + mpFrameView->SetGridCoarse( mpDrawView->GetGridCoarse() ); + mpFrameView->SetGridFine( mpDrawView->GetGridFine() ); + mpFrameView->SetSnapGridWidth(mpDrawView->GetSnapGridWidthX(), mpDrawView->GetSnapGridWidthY()); + mpFrameView->SetGridVisible( mpDrawView->IsGridVisible() ); + mpFrameView->SetGridFront( mpDrawView->IsGridFront() ); + mpFrameView->SetSnapAngle( mpDrawView->GetSnapAngle() ); + mpFrameView->SetGridSnap( mpDrawView->IsGridSnap() ); + mpFrameView->SetBordSnap( mpDrawView->IsBordSnap() ); + mpFrameView->SetHlplSnap( mpDrawView->IsHlplSnap() ); + mpFrameView->SetOFrmSnap( mpDrawView->IsOFrmSnap() ); + mpFrameView->SetOPntSnap( mpDrawView->IsOPntSnap() ); + mpFrameView->SetOConSnap( mpDrawView->IsOConSnap() ); + mpFrameView->SetHlplVisible( mpDrawView->IsHlplVisible() ); + mpFrameView->SetDragStripes( mpDrawView->IsDragStripes() ); + mpFrameView->SetPlusHandlesAlwaysVisible( mpDrawView->IsPlusHandlesAlwaysVisible() ); + mpFrameView->SetFrameDragSingles( mpDrawView->IsFrameDragSingles() ); + mpFrameView->SetMarkedHitMovesAlways( mpDrawView->IsMarkedHitMovesAlways() ); + mpFrameView->SetMoveOnlyDragging( mpDrawView->IsMoveOnlyDragging() ); + mpFrameView->SetNoDragXorPolys( mpDrawView->IsNoDragXorPolys() ); + mpFrameView->SetCrookNoContortion( mpDrawView->IsCrookNoContortion() ); + mpFrameView->SetBigOrtho( mpDrawView->IsBigOrtho() ); + mpFrameView->SetEliminatePolyPointLimitAngle( mpDrawView->GetEliminatePolyPointLimitAngle() ); + mpFrameView->SetEliminatePolyPoints( mpDrawView->IsEliminatePolyPoints() ); + + mpFrameView->SetSolidDragging( mpDrawView->IsSolidDragging() ); + mpFrameView->SetQuickEdit( mpDrawView->IsQuickTextEditMode() ); + + mpFrameView->SetDesignMode( mpDrawView->IsDesignMode() ); + + Size aVisSizePixel = GetActiveWindow()->GetOutputSizePixel(); + ::tools::Rectangle aVisArea = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), aVisSizePixel) ); + if (comphelper::LibreOfficeKit::isActive()) + { + // aVisArea is nonsensical in the LOK case, use the slide size + aVisArea = ::tools::Rectangle(Point(), getCurrentPage()->GetSize()); + } + + mpFrameView->SetVisArea(aVisArea); + + if( mePageKind == PageKind::Handout ) + mpFrameView->SetSelectedPage(0); + else + { + mpFrameView->SetSelectedPage( maTabControl->GetCurPagePos() ); + } + + mpFrameView->SetViewShEditMode(meEditMode); + mpFrameView->SetLayerMode(IsLayerModeActive()); + + SdrPageView* pPageView = mpDrawView->GetSdrPageView(); + + if (pPageView) + { + if ( mpFrameView->GetVisibleLayers() != pPageView->GetVisibleLayers() ) + mpFrameView->SetVisibleLayers( pPageView->GetVisibleLayers() ); + + if ( mpFrameView->GetPrintableLayers() != pPageView->GetPrintableLayers() ) + mpFrameView->SetPrintableLayers( pPageView->GetPrintableLayers() ); + + if ( mpFrameView->GetLockedLayers() != pPageView->GetLockedLayers() ) + mpFrameView->SetLockedLayers( pPageView->GetLockedLayers() ); + + if (mePageKind == PageKind::Notes) + { + mpFrameView->SetNotesHelpLines( pPageView->GetHelpLines() ); + } + else if (mePageKind == PageKind::Handout) + { + mpFrameView->SetHandoutHelpLines( pPageView->GetHelpLines() ); + } + else + { + mpFrameView->SetStandardHelpLines( pPageView->GetHelpLines() ); + } + } + + if ( mpFrameView->GetActiveLayer() != mpDrawView->GetActiveLayer() ) + mpFrameView->SetActiveLayer( mpDrawView->GetActiveLayer() ); + + // store DrawMode for 'normal' window + if(mpFrameView->GetDrawMode() != GetActiveWindow()->GetOutDev()->GetDrawMode()) + mpFrameView->SetDrawMode(GetActiveWindow()->GetOutDev()->GetDrawMode()); +} + +void DrawViewShell::PrePaint() +{ + mpDrawView->PrePaint(); +} + +/** + * The event is forwarded to the Viewshell and the current function by the + * window pWin. + * + * Remark: pWin==NULL, if Paint() is called from ShowWindow! + */ +void DrawViewShell::Paint(const ::tools::Rectangle& rRect, ::sd::Window* pWin) +{ + /* This is done before each text edit, so why not do it before every paint. + The default language is only used if the outliner only contains one + character in a symbol font */ + GetDoc()->GetDrawOutliner().SetDefaultLanguage( GetDoc()->GetLanguage( EE_CHAR_LANGUAGE ) ); + + // Set Application Background color for usage in SdrPaintView(s) + mpDrawView->SetApplicationBackgroundColor( mnAppBackgroundColor ); + + /* This is done before each text edit, so why not do it before every paint. + The default language is only used if the outliner only contains one + character in a symbol font */ + GetDoc()->GetDrawOutliner().SetDefaultLanguage( Application::GetSettings().GetLanguageTag().getLanguageType() ); + + mpDrawView->CompleteRedraw( pWin->GetOutDev(), vcl::Region( rRect ) ); +} + +/** + * adjust zoom factor for InPlace + */ +void DrawViewShell::SetZoomFactor(const Fraction& rZoomX, const Fraction& rZoomY) +{ + ViewShell::SetZoomFactor(rZoomX, rZoomY); + mbZoomOnPage = false; + Point aOrigin = GetActiveWindow()->GetViewOrigin(); + GetActiveWindow()->SetWinViewPos(aOrigin); +} + +void DrawViewShell::HidePage() +{ + FmFormShell* pFormShell = GetViewShellBase().GetFormShellManager()->GetFormShell(); + if (pFormShell != nullptr) + pFormShell->PrepareClose(false); +} + +void DrawViewShell::WriteUserDataSequence ( css::uno::Sequence < css::beans::PropertyValue >& rSequence ) +{ + WriteFrameViewData(); + + ViewShell::WriteUserDataSequence( rSequence ); + + const sal_Int32 nIndex = rSequence.getLength(); + rSequence.realloc( nIndex + 1 ); + auto pSequence = rSequence.getArray(); + pSequence[nIndex].Name = sUNO_View_ZoomOnPage ; + pSequence[nIndex].Value <<= mbZoomOnPage; + + // Common SdrModel processing + GetDocSh()->GetDoc()->WriteUserDataSequence(rSequence); +} + +void DrawViewShell::ReadUserDataSequence ( const css::uno::Sequence < css::beans::PropertyValue >& rSequence ) +{ + WriteFrameViewData(); + + ViewShell::ReadUserDataSequence( rSequence ); + + for (const css::beans::PropertyValue& rValue : rSequence) + { + if ( rValue.Name == sUNO_View_ZoomOnPage ) + { + bool bZoomPage = false; + if( rValue.Value >>= bZoomPage ) + { + mbZoomOnPage = bZoomPage; + } + } + // Fallback to common SdrModel processing + else GetDocSh()->GetDoc()->ReadUserDataSequenceValue(&rValue); + } + + // The parameter rSequence contains the config-items from + // <config:config-item-set config:name="ooo:view-settings">. Determine, whether + // they contain "VisibleLayers", "PrintableLayers" and "LockedLayers". If not, it + // is a foreign document or a new document after transition period and the corresponding + // information were read from <draw:layer-set>. In that case we need to bring + // the information from model to view. + bool bHasVisiPrnLockSettings(false); + for ( auto & rPropertyValue : rSequence ) + { + if ( rPropertyValue.Name == sUNO_View_VisibleLayers + || rPropertyValue.Name == sUNO_View_PrintableLayers + || rPropertyValue.Name == sUNO_View_LockedLayers ) + { + bHasVisiPrnLockSettings = true; + break; + } + } + if ( !bHasVisiPrnLockSettings ) + { + const SdrLayerAdmin& rLayerAdmin = GetDocSh()->GetDoc()->GetLayerAdmin(); + SdrLayerIDSet aSdrLayerIDSet; + rLayerAdmin.getVisibleLayersODF( aSdrLayerIDSet ); + mpFrameView -> SetVisibleLayers( aSdrLayerIDSet ); + rLayerAdmin.getPrintableLayersODF( aSdrLayerIDSet ); + mpFrameView -> SetPrintableLayers( aSdrLayerIDSet ); + rLayerAdmin.getLockedLayersODF( aSdrLayerIDSet ); + mpFrameView -> SetLockedLayers( aSdrLayerIDSet ); + } + else + { + // tdf#129898 repair layer "DrawnInSlideshow", which was wrongly written + // in LO 6.2 to 6.4. The ODF defaults were corrected when reading draw:layer-set, but + // not in reading config settings, because there the name is not known. + const SdrLayerAdmin& rLayerAdmin = GetDocSh()->GetDoc()->GetLayerAdmin(); + if (rLayerAdmin.GetLayer("DrawnInSlideshow")) + { + SdrLayerIDSet aSdrLayerIDSet; + rLayerAdmin.getVisibleLayersODF( aSdrLayerIDSet ); + mpFrameView -> SetVisibleLayers( aSdrLayerIDSet ); + rLayerAdmin.getPrintableLayersODF( aSdrLayerIDSet ); + mpFrameView -> SetPrintableLayers( aSdrLayerIDSet ); + rLayerAdmin.getLockedLayersODF( aSdrLayerIDSet ); + mpFrameView -> SetLockedLayers( aSdrLayerIDSet ); + } + } + + + if( mpFrameView->GetPageKind() != mePageKind ) + { + mePageKind = mpFrameView->GetPageKind(); + + if (mePageKind == PageKind::Notes) + { + GetActiveWindow()->SetHelpId( CMD_SID_NOTES_MODE ); + } + else if (mePageKind == PageKind::Handout) + { + GetActiveWindow()->SetHelpId( CMD_SID_HANDOUT_MASTER_MODE ); + } + else + { + GetActiveWindow()->SetHelpId( HID_SDDRAWVIEWSHELL ); + } + } + + ReadFrameViewData( mpFrameView ); + + if( !mbZoomOnPage ) + { + const ::tools::Rectangle aVisArea( mpFrameView->GetVisArea() ); + + // tdf#151621 Do not set if the embedded object is opening in a new window. + if (GetDocSh()->GetCreateMode() == SfxObjectCreateMode::EMBEDDED + && GetDocSh()->IsInPlaceActive()) + { + GetDocSh()->SetVisArea(aVisArea); + } + + VisAreaChanged(aVisArea); + + ::sd::View* pView = GetView(); + + if (pView) + { + pView->VisAreaChanged(GetActiveWindow()->GetOutDev()); + } + + SetZoomRect(aVisArea); + } + ChangeEditMode (meEditMode, ! IsLayerModeActive()); + ResetActualLayer(); +} + +void DrawViewShell::VisAreaChanged(const ::tools::Rectangle& rRect) +{ + ViewShell::VisAreaChanged( rRect ); + + DrawController& rController = GetViewShellBase().GetDrawController(); + rController.FireVisAreaChanged (rRect); +} + +/** If there is a valid controller then create a new instance of + <type>AccessibleDrawDocumentView</type>. Otherwise return an empty + reference. +*/ +css::uno::Reference<css::accessibility::XAccessible> + DrawViewShell::CreateAccessibleDocumentView (::sd::Window* pWindow) +{ + if (GetViewShellBase().GetController() != nullptr) + { + rtl::Reference<accessibility::AccessibleDrawDocumentView> pDocumentView = + new accessibility::AccessibleDrawDocumentView ( + pWindow, + this, + GetViewShellBase().GetController(), + pWindow->GetAccessibleParentWindow()->GetAccessible()); + pDocumentView->Init(); + return pDocumentView; + } + + SAL_WARN("sd", "DrawViewShell::CreateAccessibleDocumentView: no controller"); + return css::uno::Reference< css::accessibility::XAccessible>(); +} + +int DrawViewShell::GetActiveTabLayerIndex() const +{ + const LayerTabBar* pBar + = const_cast<DrawViewShell*>(this)->GetLayerTabControl (); + if (pBar != nullptr) + return pBar->GetPagePos (pBar->GetCurPageId()); + else + return -1; +} + +void DrawViewShell::SetActiveTabLayerIndex (int nIndex) +{ + LayerTabBar* pBar = GetLayerTabControl (); + if (pBar == nullptr) + return; + + // Ignore invalid indices silently. + if (nIndex>=0 && nIndex<pBar->GetPageCount()) + { + // Tell the draw view and the tab control of the new active layer. + mpDrawView->SetActiveLayer (pBar->GetLayerName (pBar->GetPageId (static_cast<sal_uInt16>(nIndex)))); + pBar->SetCurPageId (pBar->GetPageId (static_cast<sal_uInt16>(nIndex))); + rtl::Reference<SdUnoDrawView> pUnoDrawView(new SdUnoDrawView ( + *this, + *GetView())); + css::uno::Reference< css::drawing::XLayer> rLayer = pUnoDrawView->getActiveLayer(); + GetViewShellBase().GetDrawController().fireChangeLayer( &rLayer ); + } +} + +LayerTabBar* DrawViewShell::GetLayerTabControl() +{ + return mpLayerTabBar.get(); +} + +int DrawViewShell::GetTabLayerCount() const +{ + const LayerTabBar* pBar + = const_cast<DrawViewShell*>(this)->GetLayerTabControl (); + if (pBar != nullptr) + return pBar->GetPageCount(); + else + return 0; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviews6.cxx b/sd/source/ui/view/drviews6.cxx new file mode 100644 index 000000000..7d85151f7 --- /dev/null +++ b/sd/source/ui/view/drviews6.cxx @@ -0,0 +1,339 @@ +/* -*- 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 <config_features.h> + +#include <DrawViewShell.hxx> +#include <sfx2/request.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/svdograf.hxx> +#include <svx/svxids.hrc> +#include <svx/fontwork.hxx> +#include <svx/bmpmask.hxx> +#include <svx/imapdlg.hxx> +#include <svx/SvxColorChildWindow.hxx> +#include <svx/f3dchild.hxx> +#include <avmedia/mediaplayer.hxx> +#include <svl/intitem.hxx> + +#include <app.hrc> +#include <strings.hrc> + +#include <animobjs.hxx> +#include <AnimationChildWindow.hxx> +#include <sdresid.hxx> +#include <drawdoc.hxx> +#include <drawview.hxx> +#include <svx/svdoashp.hxx> + +namespace sd { + +/** + * handle SfxRequests for FontWork + */ +void DrawViewShell::ExecFormText(SfxRequest& rReq) +{ + // nothing is executed during a slide show! + if(HasCurrentFunction(SID_PRESENTATION)) + return; + + CheckLineTo (rReq); + + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + + if ( rMarkList.GetMarkCount() == 1 && rReq.GetArgs() && + !mpDrawView->IsPresObjSelected() ) + { + const SfxItemSet& rSet = *rReq.GetArgs(); + + if ( mpDrawView->IsTextEdit() ) + mpDrawView->SdrEndTextEdit(); + + mpDrawView->SetAttributes(rSet); + } +} + +/** + * Return state values for FontWork + */ +void DrawViewShell::GetFormTextState(SfxItemSet& rSet) +{ + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + const SdrObject* pObj = nullptr; + + if ( rMarkList.GetMarkCount() == 1 ) + pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + + const SdrTextObj* pTextObj = dynamic_cast< const SdrTextObj* >(pObj); + const bool bDeactivate( + !pObj || + !pTextObj || + !pTextObj->HasText() || + dynamic_cast< const SdrObjCustomShape* >(pObj)); // #121538# no FontWork for CustomShapes + + if(bDeactivate) + { +// automatic open/close the FontWork-Dialog; first deactivate it + + rSet.DisableItem(XATTR_FORMTXTSTYLE); + rSet.DisableItem(XATTR_FORMTXTADJUST); + rSet.DisableItem(XATTR_FORMTXTDISTANCE); + rSet.DisableItem(XATTR_FORMTXTSTART); + rSet.DisableItem(XATTR_FORMTXTMIRROR); + rSet.DisableItem(XATTR_FORMTXTHIDEFORM); + rSet.DisableItem(XATTR_FORMTXTOUTLINE); + rSet.DisableItem(XATTR_FORMTXTSHADOW); + rSet.DisableItem(XATTR_FORMTXTSHDWCOLOR); + rSet.DisableItem(XATTR_FORMTXTSHDWXVAL); + rSet.DisableItem(XATTR_FORMTXTSHDWYVAL); + } + else + { + SfxItemSet aSet( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( aSet ); + rSet.Set( aSet ); + } +} + +void DrawViewShell::ExecAnimationWin( SfxRequest& rReq ) +{ + // nothing is executed during a slide show! + if (HasCurrentFunction(SID_PRESENTATION)) + return; + + CheckLineTo (rReq); + + sal_uInt16 nSId = rReq.GetSlot(); + + switch( nSId ) + { + case SID_ANIMATOR_INIT: + case SID_ANIMATOR_ADD: + case SID_ANIMATOR_CREATE: + { + AnimationWindow* pAnimWin; + sal_uInt16 nId = AnimationChildWindow::GetChildWindowId(); + + SfxChildWindow* pWnd = GetViewFrame()->GetChildWindow(nId); + + pAnimWin = pWnd ? static_cast<AnimationWindow*>(pWnd->GetWindow()) : nullptr; + + if ( pAnimWin ) + { + if( nSId == SID_ANIMATOR_ADD ) + pAnimWin->AddObj( *mpDrawView ); + else if( nSId == SID_ANIMATOR_CREATE ) + pAnimWin->CreateAnimObj( *mpDrawView ); + } + } + break; + + default: + break; + } +} + +/** + * Return status values for animator + * + * nValue == 0 -> No button + * nValue == 1 -> Button 'accept' + * nValue == 2 -> Button 'accept individually' + * nValue == 3 -> Buttons 'accept' and 'accept individually' + */ +void DrawViewShell::GetAnimationWinState( SfxItemSet& rSet ) +{ + // here we could disable buttons etc. + sal_uInt16 nValue; + + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + const size_t nMarkCount = rMarkList.GetMarkCount(); + + if( nMarkCount == 0 ) + nValue = 0; + else if( nMarkCount > 1 ) + nValue = 3; + else // 1 Object + { + const SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj(); + SdrInventor nInv = pObj->GetObjInventor(); + SdrObjKind nId = pObj->GetObjIdentifier(); + // 1 selected group object + if( nInv == SdrInventor::Default && nId == SdrObjKind::Group ) + nValue = 3; + else if( nInv == SdrInventor::Default && nId == SdrObjKind::Graphic ) // Animated GIF ? + { + sal_uInt16 nCount = 0; + + if( static_cast<const SdrGrafObj*>(pObj)->IsAnimated() ) + nCount = static_cast<const SdrGrafObj*>(pObj)->GetGraphic().GetAnimation().Count(); + if( nCount > 0 ) + nValue = 2; + else + nValue = 1; + } + else + nValue = 1; + } + rSet.Put( SfxUInt16Item( SID_ANIMATOR_STATE, nValue ) ); +} + +void DrawViewShell::SetChildWindowState( SfxItemSet& rSet ) +{ + // State of SfxChild-Windows (Animator, Fontwork etc.) + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_FONTWORK ) ) + { + sal_uInt16 nId = SvxFontWorkChildWindow::GetChildWindowId(); + rSet.Put(SfxBoolItem(SID_FONTWORK, GetViewFrame()->HasChildWindow(nId))); + } + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_COLOR_CONTROL ) ) + { + sal_uInt16 nId = SvxColorChildWindow::GetChildWindowId(); + rSet.Put(SfxBoolItem(SID_COLOR_CONTROL, GetViewFrame()->HasChildWindow(nId))); + } + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_ANIMATION_OBJECTS ) ) + { + sal_uInt16 nId = AnimationChildWindow::GetChildWindowId(); + rSet.Put( SfxBoolItem( SID_ANIMATION_OBJECTS, GetViewFrame()->HasChildWindow( nId ) ) ); + } + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_NAVIGATOR ) ) + { + rSet.Put( SfxBoolItem( SID_NAVIGATOR, GetViewFrame()->HasChildWindow( SID_NAVIGATOR ) ) ); + } + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_BMPMASK ) ) + { + sal_uInt16 nId = SvxBmpMaskChildWindow::GetChildWindowId(); + rSet.Put( SfxBoolItem( SID_BMPMASK, GetViewFrame()->HasChildWindow( nId ) ) ); + } + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_IMAP ) ) + { + sal_uInt16 nId = SvxIMapDlgChildWindow::GetChildWindowId(); + rSet.Put( SfxBoolItem( SID_IMAP, GetViewFrame()->HasChildWindow( nId ) ) ); + } + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_3D_WIN ) ) + { + sal_uInt16 nId = Svx3DChildWindow::GetChildWindowId(); + rSet.Put( SfxBoolItem( SID_3D_WIN, GetViewFrame()->HasChildWindow( nId ) ) ); + } +#if HAVE_FEATURE_AVMEDIA + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_AVMEDIA_PLAYER ) ) + { + sal_uInt16 nId = ::avmedia::MediaPlayer::GetChildWindowId(); + rSet.Put( SfxBoolItem( SID_AVMEDIA_PLAYER, GetViewFrame()->HasChildWindow( nId ) ) ); + } +#endif +} + +/** + * Handle SfxRequests for pipette + */ +void DrawViewShell::ExecBmpMask( SfxRequest const & rReq ) +{ + // nothing is executed during a slide show! + if (HasCurrentFunction(SID_PRESENTATION)) + return; + + switch ( rReq.GetSlot() ) + { + case SID_BMPMASK_PIPETTE : + { + mbPipette = static_cast<const SfxBoolItem&>( rReq.GetArgs()-> + Get( SID_BMPMASK_PIPETTE ) ).GetValue(); + } + break; + + case SID_BMPMASK_EXEC : + { + SdrGrafObj* pObj = nullptr; + if( mpDrawView && mpDrawView->GetMarkedObjectList().GetMarkCount() ) + pObj = dynamic_cast< SdrGrafObj* >( mpDrawView->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj() ); + + if ( pObj && !mpDrawView->IsTextEdit() ) + { + typedef std::unique_ptr< SdrGrafObj, SdrObjectFreeOp > SdrGrafObjPtr; + SdrGrafObjPtr xNewObj(pObj->CloneSdrObject(pObj->getSdrModelFromSdrObject())); + bool bCont = true; + + if (xNewObj->IsLinkedGraphic()) + { + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(GetFrameWeld(), "modules/sdraw/ui/queryunlinkimagedialog.ui")); + std::unique_ptr<weld::MessageDialog> xQueryBox(xBuilder->weld_message_dialog("QueryUnlinkImageDialog")); + + if (RET_YES == xQueryBox->run()) + xNewObj->ReleaseGraphicLink(); + else + bCont = false; + } + + SfxChildWindow* pWnd = GetViewFrame()->GetChildWindow( + SvxBmpMaskChildWindow::GetChildWindowId()); + SvxBmpMask* pBmpMask = pWnd ? static_cast<SvxBmpMask*>(pWnd->GetWindow()) : nullptr; + assert(pBmpMask); + if (bCont && pBmpMask) + { + const Graphic& rOldGraphic = xNewObj->GetGraphic(); + const Graphic aNewGraphic(pBmpMask->Mask(rOldGraphic)); + + if( aNewGraphic != rOldGraphic ) + { + SdrPageView* pPV = mpDrawView->GetSdrPageView(); + + xNewObj->SetEmptyPresObj(false); + xNewObj->SetGraphic(pBmpMask->Mask(xNewObj->GetGraphic())); + + OUString aStr = mpDrawView->GetDescriptionOfMarkedObjects() + + " " + SdResId(STR_EYEDROPPER); + + mpDrawView->BegUndo( aStr ); + mpDrawView->ReplaceObjectAtView(pObj, *pPV, xNewObj.release()); + mpDrawView->EndUndo(); + } + } + } + } + break; + + default: + break; + } +} + +void DrawViewShell::GetBmpMaskState( SfxItemSet& rSet ) +{ + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + const SdrObject* pObj = nullptr; + bool bEnable = false; + + if ( rMarkList.GetMarkCount() == 1 ) + pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + + // valid graphic object? + if( auto pGrafObj = dynamic_cast< const SdrGrafObj *>( pObj ) ) + if (!pGrafObj->IsEPS() && !mpDrawView->IsTextEdit() ) + bEnable = true; + + // put value + rSet.Put( SfxBoolItem( SID_BMPMASK_EXEC, bEnable ) ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviews7.cxx b/sd/source/ui/view/drviews7.cxx new file mode 100644 index 000000000..4f375dc6a --- /dev/null +++ b/sd/source/ui/view/drviews7.cxx @@ -0,0 +1,1991 @@ +/* -*- 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 <memory> + +#include <sal/config.h> +#include <sal/log.hxx> + +#include <utility> + +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/linguistic2/XThesaurus.hpp> +#include <svx/pageitem.hxx> +#include <svx/rulritem.hxx> +#include <svx/svdouno.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/sizeitem.hxx> +#include <editeng/urlfieldhelper.hxx> +#include <officecfg/Office/Impress.hxx> +#include <svx/svxids.hrc> +#include <svx/svdpagv.hxx> +#include <svx/clipfmtitem.hxx> +#include <svl/eitem.hxx> +#include <svl/intitem.hxx> +#include <svl/itemset.hxx> +#include <svl/stritem.hxx> +#include <svl/visitem.hxx> +#include <svl/whiter.hxx> +#include <svx/svdograf.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xflclit.hxx> +#include <svx/xflgrit.hxx> +#include <svx/xflhtit.hxx> +#include <svx/xbtmpit.hxx> +#include <editeng/unolingu.hxx> +#include <svx/extrusionbar.hxx> +#include <svx/fontworkbar.hxx> + +// #UndoRedo# +#include <svtools/insdlg.hxx> +#include <unotools/moduleoptions.hxx> +#include <svl/cjkoptions.hxx> +#include <comphelper/processfactory.hxx> +#include <sfx2/request.hxx> + +#include <svtools/cliplistener.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> + +#include <app.hrc> + +#include <PresentationViewShell.hxx> + +#include <drawdoc.hxx> +#include <DrawViewShell.hxx> +#include <sdmod.hxx> +#include <unokywds.hxx> +#include <sdpage.hxx> +#include <DrawDocShell.hxx> +#include <zoomlist.hxx> +#include <slideshow.hxx> +#include <drawview.hxx> +#include <ViewShellBase.hxx> +#include <ViewShellManager.hxx> +#include <LayerTabBar.hxx> +#include <fupoor.hxx> +#include <Window.hxx> +#include <fuediglu.hxx> +#include <fubullet.hxx> +#include <fuconcs.hxx> +#include <fuformatpaintbrush.hxx> +#include <stlsheet.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::linguistic2; + +/** Create a list of clipboard formats that are supported both from the + current clipboard content and the DrawViewShell. + The list is stored in a new instance of SvxClipboardFormatItem. +*/ +static ::std::unique_ptr<SvxClipboardFormatItem> GetSupportedClipboardFormats ( + TransferableDataHelper& rDataHelper) +{ + ::std::unique_ptr<SvxClipboardFormatItem> pResult ( + new SvxClipboardFormatItem(SID_CLIPBOARD_FORMAT_ITEMS)); + + sal_uInt32 nFormatCount (rDataHelper.GetFormatCount()); + for (sal_uInt32 i=0; i<nFormatCount; i++) + { + const SotClipboardFormatId nTestFormat = rDataHelper.GetFormat(i); + + // Check if the current format is the same as one that has already + // been handled. + bool bDuplicate (false); + for (sal_uInt32 j=0; j<i; j++) + { + if (nTestFormat == rDataHelper.GetFormat(j)) + { + bDuplicate = true; + break; + } + } + + // Look up the format among those that are supported by the + // DrawViewShell. + if ( ! bDuplicate) + { + switch (nTestFormat) + { + case SotClipboardFormatId::EMBED_SOURCE: + { + OUString sName; + + TransferableObjectDescriptor aDescriptor; + if (rDataHelper.GetTransferableObjectDescriptor( + SotClipboardFormatId::OBJECTDESCRIPTOR, aDescriptor)) + { + sName = aDescriptor.maTypeName; + } + if (!sName.isEmpty()) + pResult->AddClipbrdFormat(nTestFormat, sName); + else + pResult->AddClipbrdFormat(nTestFormat); + + break; + } + + + case SotClipboardFormatId::LINK_SOURCE: + case SotClipboardFormatId::DRAWING: + case SotClipboardFormatId::SVXB: + case SotClipboardFormatId::GDIMETAFILE: + case SotClipboardFormatId::BITMAP: + case SotClipboardFormatId::NETSCAPE_BOOKMARK: + case SotClipboardFormatId::STRING: + case SotClipboardFormatId::HTML: + case SotClipboardFormatId::RTF: + case SotClipboardFormatId::RICHTEXT: + case SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT: + pResult->AddClipbrdFormat(nTestFormat); + break; + default: break; + } + } + } + + // Check some OLE formats whose names are handled differently. + SotClipboardFormatId nFormat (SotClipboardFormatId::EMBED_SOURCE_OLE); + bool bHasFormat (rDataHelper.HasFormat(nFormat)); + if ( ! bHasFormat) + { + bHasFormat = rDataHelper.HasFormat(nFormat); + } + if (bHasFormat) + { + OUString sName; + OUString sSource; + if (SvPasteObjectHelper::GetEmbeddedName (rDataHelper, sName, sSource, nFormat)) + pResult->AddClipbrdFormat (nFormat, sName); + } + + return pResult; +} + +namespace sd { + +IMPL_LINK( DrawViewShell, ClipboardChanged, TransferableDataHelper*, pDataHelper, void ) +{ + mbPastePossible = ( pDataHelper->GetFormatCount() != 0 ); + + // Update the list of supported clipboard formats according to the + // new clipboard content. + // There are some stack traces that indicate the possibility of the + // DrawViewShell destructor called during the call to + // GetSupportedClipboardFormats(). If that really has happened then + // exit immediately. + TransferableDataHelper aDataHelper ( + TransferableDataHelper::CreateFromSystemClipboard(GetActiveWindow())); + ::std::unique_ptr<SvxClipboardFormatItem> pFormats (GetSupportedClipboardFormats(aDataHelper)); + if (mpDrawView == nullptr) + return; + mpCurrentClipboardFormats = std::move(pFormats); + + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_PASTE ); + rBindings.Invalidate( SID_PASTE_SPECIAL ); + rBindings.Invalidate( SID_PASTE_UNFORMATTED ); + rBindings.Invalidate( SID_CLIPBOARD_FORMAT_ITEMS ); +} + +void DrawViewShell::GetDrawAttrState(SfxItemSet& rSet) +{ + SfxItemSet aSet( mpDrawView->GetGeoAttrFromMarked() ); + rSet.Put(aSet,false); +} + +::Outliner* DrawViewShell::GetOutlinerForMasterPageOutlineTextObj(ESelection &rSel) +{ + if( !mpDrawView ) + return nullptr; + + //when there is one object selected + if (!mpDrawView->AreObjectsMarked() || (mpDrawView->GetMarkedObjectList().GetMarkCount() != 1)) + return nullptr; + + //and we are editing the outline object + if (!mpDrawView->IsTextEdit()) + return nullptr; + + SdrPageView* pPageView = mpDrawView->GetSdrPageView(); + if (!pPageView) + return nullptr; + + SdPage* pPage = static_cast<SdPage*>(pPageView->GetPage()); + //only show these in a normal master page + if (!pPage || (pPage->GetPageKind() != PageKind::Standard) || !pPage->IsMasterPage()) + return nullptr; + + OutlinerView* pOLV = mpDrawView->GetTextEditOutlinerView(); + ::Outliner* pOL = pOLV ? pOLV->GetOutliner() : nullptr; + if (!pOL) + return nullptr; + rSel = pOLV->GetSelection(); + + return pOL; +} + +void DrawViewShell::GetMarginProperties( SfxItemSet &rSet ) +{ + SdPage *pPage = getCurrentPage(); + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + while ( nWhich ) + { + switch ( nWhich ) + { + case SID_ATTR_PAGE_LRSPACE: + { + // const SvxLRSpaceItem aTmpPageLRSpace ( rDesc.GetMaster().GetLRSpace() ); + const SvxLongLRSpaceItem aLongLR( + static_cast<::tools::Long>(pPage->GetLeftBorder()), + static_cast<::tools::Long>(pPage->GetRightBorder()), + SID_ATTR_PAGE_LRSPACE ); + rSet.Put( aLongLR ); + } + break; + + case SID_ATTR_PAGE_ULSPACE: + { + // const SvxULSpaceItem aUL( rDesc.GetMaster().GetULSpace() ); + SvxLongULSpaceItem aLongUL( + static_cast<::tools::Long>(pPage->GetUpperBorder()), + static_cast<::tools::Long>(pPage->GetLowerBorder()), + SID_ATTR_PAGE_ULSPACE ); + rSet.Put( aLongUL ); + } + break; + + default: + break; + } + nWhich = aIter.NextWhich(); + } +} + +bool DrawViewShell::ShouldDisableEditHyperlink() const +{ + if (!mpDrawView) + return true; + if (!mpDrawView->AreObjectsMarked()) + return true; + if (mpDrawView->GetMarkedObjectList().GetMarkCount() != 1) + return true; + + bool bDisableEditHyperlink = true; + if( mpDrawView->IsTextEdit() ) + { + if (URLFieldHelper::IsCursorAtURLField(mpDrawView->GetTextEditOutlinerView())) + bDisableEditHyperlink = false; + } + else + { + SdrUnoObj* pUnoCtrl = dynamic_cast<SdrUnoObj*>( mpDrawView->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj() ); + + if ( pUnoCtrl && SdrInventor::FmForm == pUnoCtrl->GetObjInventor() ) + { + const uno::Reference< awt::XControlModel >& xControlModel( pUnoCtrl->GetUnoControlModel() ); + if( xControlModel.is() ) + { + uno::Reference< beans::XPropertySet > xPropSet( xControlModel, uno::UNO_QUERY ); + if( xPropSet.is() ) + { + uno::Reference< beans::XPropertySetInfo > xPropInfo( xPropSet->getPropertySetInfo() ); + if( xPropInfo.is() && xPropInfo->hasPropertyByName( "TargetURL") ) + { + bDisableEditHyperlink = false; + } + } + } + } + } + return bDisableEditHyperlink; +} + +void DrawViewShell::GetMenuState( SfxItemSet &rSet ) +{ + if (mpDrawView == nullptr) + { + // This assertion and return are here to prevent crashes. + DBG_ASSERT(mpDrawView!=nullptr, "Please report this assertion to the Impress team."); + return; + } + + ViewShell::GetMenuState(rSet); + bool bDisableVerticalText = !SvtCJKOptions::IsVerticalTextEnabled(); + + if ( bDisableVerticalText ) + { + rSet.DisableItem( SID_DRAW_FONTWORK_VERTICAL ); + rSet.DisableItem( SID_DRAW_CAPTION_VERTICAL ); + rSet.DisableItem( SID_TEXT_FITTOSIZE_VERTICAL ); + rSet.DisableItem( SID_DRAW_TEXT_VERTICAL ); + } + + bool bConvertToPathPossible = mpDrawView->IsConvertToPathObjPossible(); + + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + const size_t nMarkCount = rMarkList.GetMarkCount(); + + if( nMarkCount == 1 ) + { + bool bDisable = true; + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + if( auto pGrafObj = dynamic_cast<const SdrGrafObj*>( pObj) ) + if( pGrafObj->getQrCode() ) + bDisable = false; + if(bDisable) + { + rSet.DisableItem(SID_EDIT_QRCODE); + } + } + + //format paintbrush + FuFormatPaintBrush::GetMenuState( *this, rSet ); + + // State of SfxChild-Windows (Animator, Fontwork etc.) + SetChildWindowState( rSet ); + + if(HasCurrentFunction()) + { + sal_uInt16 nSId = GetCurrentFunction()->GetSlotID(); + rSet.Put( SfxBoolItem( nSId, true ) ); + } + + SdrPageView* pPageView = mpDrawView->GetSdrPageView(); + + GetMenuStateSel(rSet); + + if (SfxItemState::DEFAULT == rSet.GetItemState(SID_ASSIGN_LAYOUT)) + { + bool bDisable = true; + if( pPageView ) + { + SdPage* pPage = dynamic_cast< SdPage* >( pPageView->GetPage() ); + + if( pPage && !pPage->IsMasterPage() ) + { + rSet.Put( SfxUInt32Item( SID_ASSIGN_LAYOUT, static_cast< sal_uInt32 >(pPage->GetAutoLayout()) ) ); + bDisable = false; + } + } + + if(bDisable) + { + rSet.DisableItem(SID_ASSIGN_LAYOUT); + } + } + + if (SfxItemState::DEFAULT == rSet.GetItemState(SID_EXPAND_PAGE)) + { + bool bDisable = true; + if( pPageView ) + { + SdPage* pPage = dynamic_cast< SdPage* >( pPageView->GetPage() ); + + if( pPage && (pPage->GetPageKind() == PageKind::Standard) && !pPage->IsMasterPage() ) + { + SdrObject* pObj = pPage->GetPresObj(PresObjKind::Outline); + + if (pObj!=nullptr ) + { + if( !pObj->IsEmptyPresObj() ) + { + bDisable = false; + } + else + { + // check if the object is in edit, then if it's temporarily not empty + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( pObj ); + if( pTextObj ) + { + if( pTextObj->CanCreateEditOutlinerParaObject() ) + { + bDisable = false; + } + } + } + } + } + } + + if(bDisable) + { + rSet.DisableItem(SID_EXPAND_PAGE); + } + } + + if (SfxItemState::DEFAULT == rSet.GetItemState(SID_SUMMARY_PAGE)) + { + bool bDisable = true; + if( pPageView ) + { + SdPage* pPage = dynamic_cast< SdPage* >( pPageView->GetPage() ); + + if( pPage && (pPage->GetPageKind() == PageKind::Standard) && !pPage->IsMasterPage() ) + { + SdrObject* pObj = pPage->GetPresObj(PresObjKind::Title); + + if(pObj && !pObj->IsEmptyPresObj()) + { + bDisable = false; + } + } + } + + if(bDisable) + { + rSet.DisableItem(SID_SUMMARY_PAGE); + } + } + + if (SfxItemState::DEFAULT == rSet.GetItemState(SID_ASSIGN_LAYOUT)) + { + bool bDisable = true; + if( pPageView ) + { + SdPage* pPage = dynamic_cast< SdPage* >( pPageView->GetPage() ); + + if( pPage && !pPage->IsMasterPage() ) + { + rSet.Put( SfxUInt32Item(SID_ASSIGN_LAYOUT, pPage->GetAutoLayout()) ); + bDisable = false; + } + } + + if(bDisable) + { + rSet.DisableItem(SID_ASSIGN_LAYOUT); + } + } + + // is it possible to start the presentation? + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_PRESENTATION ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_REHEARSE_TIMINGS ) ) + { + bool bDisable = true; + sal_uInt16 nCount = GetDoc()->GetSdPageCount( PageKind::Standard ); + + for( sal_uInt16 i = 0; i < nCount && bDisable; i++ ) + { + SdPage* pPage = GetDoc()->GetSdPage(i, PageKind::Standard); + + if( !pPage->IsExcluded() ) + bDisable = false; + } + + if( bDisable || GetDocSh()->IsPreview()) + { + rSet.DisableItem( SID_PRESENTATION ); + rSet.DisableItem( SID_REHEARSE_TIMINGS ); + } + } + + // gluepoints + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_GLUE_EDITMODE ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_GLUE_INSERT_POINT ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_GLUE_PERCENT ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_GLUE_ESCDIR ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_GLUE_ESCDIR_LEFT ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_GLUE_ESCDIR_RIGHT ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_GLUE_ESCDIR_TOP ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_GLUE_ESCDIR_BOTTOM ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_GLUE_HORZALIGN_CENTER ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_GLUE_HORZALIGN_LEFT ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_GLUE_HORZALIGN_RIGHT ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_GLUE_VERTALIGN_CENTER ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_GLUE_VERTALIGN_TOP ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_GLUE_VERTALIGN_BOTTOM ) ) + { + // percent + TriState eState = mpDrawView->IsMarkedGluePointsPercent(); + if( eState == TRISTATE_INDET ) + rSet.InvalidateItem( SID_GLUE_PERCENT ); + else + rSet.Put( SfxBoolItem( SID_GLUE_PERCENT, eState == TRISTATE_TRUE ) ); + + // alignment has no effect by percent + if( eState == TRISTATE_TRUE ) + { + rSet.DisableItem( SID_GLUE_HORZALIGN_CENTER ); + rSet.DisableItem( SID_GLUE_HORZALIGN_LEFT ); + rSet.DisableItem( SID_GLUE_HORZALIGN_RIGHT ); + rSet.DisableItem( SID_GLUE_VERTALIGN_CENTER ); + rSet.DisableItem( SID_GLUE_VERTALIGN_TOP ); + rSet.DisableItem( SID_GLUE_VERTALIGN_BOTTOM ); + } + else + { + // horizontal alignment + SdrAlign nHorz = mpDrawView->GetMarkedGluePointsAlign( false ); + rSet.Put( SfxBoolItem( SID_GLUE_HORZALIGN_CENTER, nHorz == SdrAlign::HORZ_CENTER ) ); + rSet.Put( SfxBoolItem( SID_GLUE_HORZALIGN_LEFT, nHorz == SdrAlign::HORZ_LEFT ) ); + rSet.Put( SfxBoolItem( SID_GLUE_HORZALIGN_RIGHT, nHorz == SdrAlign::HORZ_RIGHT ) ); + // vertical alignment + SdrAlign nVert = mpDrawView->GetMarkedGluePointsAlign( true ); + rSet.Put( SfxBoolItem( SID_GLUE_VERTALIGN_CENTER, nVert == SdrAlign::VERT_CENTER ) ); + rSet.Put( SfxBoolItem( SID_GLUE_VERTALIGN_TOP, nVert == SdrAlign::VERT_TOP ) ); + rSet.Put( SfxBoolItem( SID_GLUE_VERTALIGN_BOTTOM, nVert == SdrAlign::VERT_BOTTOM ) ); + } + + // insert point + rSet.Put( SfxBoolItem( SID_GLUE_INSERT_POINT, mpDrawView->IsInsGluePointMode() ) ); + + // Escape direction + // left + eState = mpDrawView->IsMarkedGluePointsEscDir( SdrEscapeDirection::LEFT ); + if( eState == TRISTATE_INDET ) + rSet.InvalidateItem( SID_GLUE_ESCDIR_LEFT ); + else + rSet.Put( SfxBoolItem( SID_GLUE_ESCDIR_LEFT, eState == TRISTATE_TRUE ) ); + // right + eState = mpDrawView->IsMarkedGluePointsEscDir( SdrEscapeDirection::RIGHT ); + if( eState == TRISTATE_INDET ) + rSet.InvalidateItem( SID_GLUE_ESCDIR_RIGHT ); + else + rSet.Put( SfxBoolItem( SID_GLUE_ESCDIR_RIGHT, eState == TRISTATE_TRUE ) ); + // top + eState = mpDrawView->IsMarkedGluePointsEscDir( SdrEscapeDirection::TOP ); + if( eState == TRISTATE_INDET ) + rSet.InvalidateItem( SID_GLUE_ESCDIR_TOP ); + else + rSet.Put( SfxBoolItem( SID_GLUE_ESCDIR_TOP, eState == TRISTATE_TRUE ) ); + // bottom + eState = mpDrawView->IsMarkedGluePointsEscDir( SdrEscapeDirection::BOTTOM ); + if( eState == TRISTATE_INDET ) + rSet.InvalidateItem( SID_GLUE_ESCDIR_BOTTOM ); + else + rSet.Put( SfxBoolItem( SID_GLUE_ESCDIR_BOTTOM, eState == TRISTATE_TRUE ) ); + } + + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_GRID_FRONT ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_HELPLINES_FRONT ) ) + { + rSet.Put( SfxBoolItem( SID_GRID_FRONT, mpDrawView->IsGridFront() ) ); + rSet.Put( SfxBoolItem( SID_HELPLINES_FRONT, mpDrawView->IsHlplFront() ) ); + } + + if (!mpDrawView->IsFrameDragSingles()) + rSet.Put(SfxBoolItem(SID_BEZIER_EDIT, true)); + else + rSet.Put(SfxBoolItem(SID_BEZIER_EDIT, false)); + + if(dynamic_cast<FuEditGluePoints*>( GetCurrentFunction().get())) + rSet.Put(SfxBoolItem(SID_GLUE_EDITMODE, true)); + else + rSet.Put(SfxBoolItem(SID_GLUE_EDITMODE, false)); + + if( !mpDrawView->IsMirrorAllowed( true, true ) ) + { + rSet.DisableItem( SID_HORIZONTAL ); + rSet.DisableItem( SID_VERTICAL ); + rSet.DisableItem( SID_FLIP_HORIZONTAL ); + rSet.DisableItem( SID_FLIP_VERTICAL ); + } + + if( !mpDrawView->IsMirrorAllowed() ) + { + rSet.DisableItem( SID_OBJECT_MIRROR ); +// rSet.DisableItem( SID_CONVERT_TO_3D_LATHE ); +// rSet.DisableItem( SID_CONVERT_TO_3D_LATHE_FAST ); + } + + // interactive transparence control + if(!mpDrawView->IsTransparenceAllowed()) + { + rSet.DisableItem( SID_OBJECT_TRANSPARENCE ); + } + + // interactive gradient control + if(!mpDrawView->IsGradientAllowed()) + { + rSet.DisableItem( SID_OBJECT_GRADIENT ); + } + + // disable morphing if necessary + if ( !mpDrawView->IsMorphingAllowed() ) + rSet.DisableItem( SID_POLYGON_MORPHING ); + + if( !mpDrawView->IsReverseOrderPossible() ) + { + rSet.DisableItem( SID_REVERSE_ORDER ); + } + + if ( !bConvertToPathPossible && + !mpDrawView->IsCrookAllowed( mpDrawView->IsCrookNoContortion() ) ) + { + // implicit transformation into curve not possible + rSet.DisableItem(SID_OBJECT_CROOK_ROTATE); + rSet.DisableItem(SID_OBJECT_CROOK_SLANT); + rSet.DisableItem(SID_OBJECT_CROOK_STRETCH); + } + + if ( !mpDrawView->IsGroupEntered() ) + { + rSet.DisableItem( SID_LEAVE_GROUP ); + rSet.Put( SfxBoolItem( SID_LEAVE_ALL_GROUPS, false ) ); + rSet.ClearItem( SID_LEAVE_ALL_GROUPS ); + rSet.DisableItem( SID_LEAVE_ALL_GROUPS ); + } + else + rSet.Put( SfxBoolItem( SID_LEAVE_ALL_GROUPS, true ) ); + + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_THESAURUS ) ) + { + if ( !mpDrawView->IsTextEdit() ) + { + rSet.DisableItem( SID_THESAURUS ); + } + else + { + LanguageType eLang = GetDoc()->GetLanguage( EE_CHAR_LANGUAGE ); + Reference< XThesaurus > xThesaurus( LinguMgr::GetThesaurus() ); + + if (!xThesaurus.is() || eLang == LANGUAGE_NONE || !xThesaurus->hasLocale( LanguageTag::convertToLocale( eLang)) ) + rSet.DisableItem( SID_THESAURUS ); + } + } + + if ( !mpDrawView->IsTextEdit() ) + { + rSet.DisableItem( SID_THESAURUS ); + } + + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_SELECTALL ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_SIZE_ALL ) ) + { + if( pPageView && pPageView->GetObjList()->GetObjCount() == 0 ) + { + // should be disabled if there is no object on the draw area: + rSet.DisableItem( SID_SELECTALL ); + rSet.DisableItem( SID_SIZE_ALL ); + } + } + + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_CONTEXT ) ) + rSet.Put( SfxStringItem( SID_CONTEXT, mpDrawView->GetStatusText() ) ); + + // clipboard (paste) + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_PASTE ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_PASTE_SPECIAL ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_PASTE_UNFORMATTED ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_CLIPBOARD_FORMAT_ITEMS ) ) + { + if ( !mxClipEvtLstnr.is() ) + { + // avoid clipboard initialization for + // read-only presentation views (workaround for NT4.0 + // clipboard prob...) + if( dynamic_cast< const PresentationViewShell *>( this ) == nullptr ) + { + // create listener + mxClipEvtLstnr = new TransferableClipboardListener( LINK( this, DrawViewShell, ClipboardChanged ) ); + mxClipEvtLstnr->AddListener( GetActiveWindow() ); + + // get initial state + TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( GetActiveWindow() ) ); + mbPastePossible = ( aDataHelper.GetFormatCount() != 0 ); + mpCurrentClipboardFormats = GetSupportedClipboardFormats( aDataHelper ); + } + else + mbPastePossible = false; + } + + if( !mbPastePossible ) + { + rSet.DisableItem( SID_PASTE ); + rSet.DisableItem( SID_PASTE_SPECIAL ); + rSet.DisableItem( SID_PASTE_UNFORMATTED ); + rSet.DisableItem( SID_CLIPBOARD_FORMAT_ITEMS ); + } + else if( SfxItemState::DEFAULT == rSet.GetItemState( SID_CLIPBOARD_FORMAT_ITEMS ) ) + { + if (mpCurrentClipboardFormats != nullptr) + rSet.Put(*mpCurrentClipboardFormats); + } + } + + if ( !bConvertToPathPossible ) + { + rSet.DisableItem(SID_CHANGEBEZIER); + } + + if (mpDrawView == nullptr) + { + // The mpDrawView was not NULL but is now. + // The reason for this may be that the DrawViewShell has been + // destroyed in the meantime. + // We can only return immediately and hope that the deleted + // DrawViewShell is not called again. + DBG_ASSERT(mpDrawView!=nullptr, "Please report this assertion to the Impress team."); + return; + } + + if( !( mpDrawView->IsConvertToPolyObjPossible() || mpDrawView->IsVectorizeAllowed() ) ) + rSet.DisableItem(SID_CHANGEPOLYGON); + + if( !( mpDrawView->IsConvertToPolyObjPossible() || mpDrawView->IsConvertToContourPossible() ) ) + rSet.DisableItem(SID_CONVERT_TO_CONTOUR); + + if ( !mpDrawView->IsConvertTo3DObjPossible() ) + { + rSet.DisableItem(SID_CONVERT_TO_3D); + rSet.DisableItem(SID_CONVERT_TO_3D_LATHE); + rSet.DisableItem(SID_CONVERT_TO_3D_LATHE_FAST); + } + + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_MANAGE_LINKS ) ) + { + if ( GetDoc()->GetLinkCount() == 0 ) + { + rSet.DisableItem(SID_MANAGE_LINKS); + } + } + + if (mePageKind == PageKind::Handout) + { + rSet.DisableItem(SID_PRESENTATION_LAYOUT); + rSet.DisableItem(SID_SELECT_BACKGROUND); + rSet.DisableItem(SID_SAVE_BACKGROUND); + } + + if (mePageKind == PageKind::Notes) + { + rSet.DisableItem(SID_INSERTPAGE); + rSet.DisableItem(SID_RENAMEPAGE); + rSet.DisableItem(SID_RENAMEPAGE_QUICK); + rSet.DisableItem(SID_DUPLICATE_PAGE); + rSet.ClearItem(SID_ANIMATION_OBJECTS); + rSet.DisableItem(SID_ANIMATION_OBJECTS); + rSet.DisableItem(SID_ANIMATION_EFFECTS); + rSet.DisableItem(SID_EXECUTE_ANIMATION_EFFECT); + + if (meEditMode == EditMode::MasterPage) + rSet.DisableItem(SID_MODIFYPAGE); + + rSet.DisableItem(SID_SELECT_BACKGROUND); + rSet.DisableItem(SID_SAVE_BACKGROUND); + rSet.DisableItem(SID_INSERTLAYER); + rSet.DisableItem(SID_LAYERMODE); + rSet.DisableItem(SID_INSERTFILE); + } + else if (mePageKind == PageKind::Handout) + { + rSet.DisableItem(SID_INSERTPAGE); + rSet.DisableItem(SID_DUPLICATE_PAGE); + rSet.ClearItem(SID_ANIMATION_OBJECTS); + rSet.DisableItem(SID_ANIMATION_OBJECTS); + rSet.DisableItem(SID_ANIMATION_EFFECTS); + rSet.DisableItem(SID_EXECUTE_ANIMATION_EFFECT); + rSet.DisableItem(SID_RENAMEPAGE); + rSet.DisableItem(SID_RENAMEPAGE_QUICK); + rSet.DisableItem(SID_INSERTLAYER); + rSet.DisableItem(SID_MODIFYLAYER); + rSet.DisableItem(SID_RENAMELAYER); + rSet.DisableItem(SID_LAYERMODE); + rSet.DisableItem(SID_INSERTFILE); + rSet.DisableItem(SID_PAGEMODE); + rSet.DisableItem(SID_SELECT_BACKGROUND); + rSet.DisableItem(SID_SAVE_BACKGROUND); + } + else + { + if (meEditMode == EditMode::MasterPage) + { + rSet.DisableItem(SID_INSERTPAGE); + rSet.DisableItem(SID_DUPLICATE_PAGE); + rSet.DisableItem(SID_MODIFYPAGE); + rSet.ClearItem(SID_ANIMATION_OBJECTS); + rSet.DisableItem(SID_ANIMATION_OBJECTS); + } + + rSet.Put (SfxBoolItem (SID_LAYERMODE, IsLayerModeActive())); + } + + if ( ! IsLayerModeActive()) + { + rSet.DisableItem( SID_INSERTLAYER ); + rSet.DisableItem( SID_MODIFYLAYER ); + rSet.DisableItem( SID_DELETE_LAYER ); + rSet.DisableItem( SID_RENAMELAYER ); + } + + if (meEditMode == EditMode::Page) + { + /********************************************************************** + * page mode + **********************************************************************/ + rSet.Put(SfxBoolItem(SID_PAGEMODE, true)); + rSet.Put(SfxBoolItem(SID_MASTERPAGE, false)); + rSet.Put(SfxBoolItem(SID_SLIDE_MASTER_MODE, false)); + rSet.Put(SfxBoolItem(SID_NOTES_MASTER_MODE, false)); + rSet.Put(SfxBoolItem(SID_HANDOUT_MASTER_MODE, false)); + + rSet.DisableItem (SID_INSERT_MASTER_PAGE); + rSet.DisableItem (SID_DELETE_MASTER_PAGE); + rSet.DisableItem (SID_RENAME_MASTER_PAGE); + rSet.DisableItem (SID_CLOSE_MASTER_VIEW); + } + else + { + rSet.Put(SfxBoolItem(SID_PAGEMODE, false)); + rSet.Put(SfxBoolItem(SID_MASTERPAGE, true)); + + /********************************************************************** + * Background page mode + **********************************************************************/ + if (mePageKind == PageKind::Standard) + { + rSet.Put(SfxBoolItem(SID_SLIDE_MASTER_MODE, true)); + rSet.Put(SfxBoolItem(SID_NOTES_MASTER_MODE, false)); + rSet.Put(SfxBoolItem(SID_HANDOUT_MASTER_MODE, false)); + + } + else if (mePageKind == PageKind::Notes) + { + rSet.Put(SfxBoolItem(SID_SLIDE_MASTER_MODE, false)); + rSet.Put(SfxBoolItem(SID_NOTES_MASTER_MODE, true)); + rSet.Put(SfxBoolItem(SID_HANDOUT_MASTER_MODE, false)); + } + else if (mePageKind == PageKind::Handout) + { + rSet.Put(SfxBoolItem(SID_SLIDE_MASTER_MODE, false)); + rSet.Put(SfxBoolItem(SID_NOTES_MASTER_MODE, false)); + rSet.Put(SfxBoolItem(SID_HANDOUT_MASTER_MODE, true)); + } + } + + // set state of the ruler + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_RULER ) ) + rSet.Put( SfxBoolItem( SID_RULER, HasRuler() ) ); + + // do not delete the last page or a master page + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_DELETE_PAGE ) + || SfxItemState::DEFAULT == rSet.GetItemState( SID_DELETE_MASTER_PAGE ) ) + { + if (maTabControl->GetPageCount() == 1 || + meEditMode == EditMode::MasterPage || + mePageKind == PageKind::Notes || + mePageKind == PageKind::Handout || + (GetShellType()!=ST_DRAW&&IsLayerModeActive())) + { + if (rSet.GetItemState(SID_DELETE_PAGE) == SfxItemState::DEFAULT) + rSet.DisableItem(SID_DELETE_PAGE); + if (rSet.GetItemState(SID_DELETE_MASTER_PAGE)==SfxItemState::DEFAULT) + rSet.DisableItem(SID_DELETE_MASTER_PAGE); + } + } + + // is it allowed to delete the current layer? + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_DELETE_LAYER ) + || SfxItemState::DEFAULT == rSet.GetItemState( SID_RENAMELAYER ) ) + { + if(GetLayerTabControl()) // #i87182# + { + sal_uInt16 nCurrentLayer = GetLayerTabControl()->GetCurPageId(); + const OUString& rName = GetLayerTabControl()->GetLayerName(nCurrentLayer); + + if (!IsLayerModeActive() || LayerTabBar::IsRealNameOfStandardLayer(rName)) + { + rSet.DisableItem(SID_DELETE_LAYER); + rSet.DisableItem(SID_RENAMELAYER); + } + } + else + { + OSL_ENSURE(false, "No LayerTabBar (!)"); + } + } + + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_CUT ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_COPY ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_OUTLINE_BULLET )) + { + OutlinerView* pOlView = mpDrawView->GetTextEditOutlinerView(); + + // special treatment of for SID_OUTLINE_BULLET if objects with different + // kinds of NumBullets are marked + bool bHasOutliner = false; + bool bHasOther = false; + for(size_t nNum = 0; nNum < nMarkCount; ++nNum) + { + SdrObject* pObj = rMarkList.GetMark(nNum)->GetMarkedSdrObj(); + if( pObj->GetObjInventor() == SdrInventor::Default ) + { + if( pObj->GetObjIdentifier() == SdrObjKind::OutlineText ) + { + bHasOutliner = true; + if(bHasOther) + break; + } + else + { + bHasOther = true; + if(bHasOutliner) + break; + } + } + } + + if( bHasOther && bHasOutliner ) + rSet.DisableItem( SID_OUTLINE_BULLET ); + + if (pOlView) + { + if (pOlView->GetSelected().isEmpty() || GetObjectShell()->isContentExtractionLocked()) + { + rSet.DisableItem( SID_CUT ); + rSet.DisableItem( SID_COPY ); + } + } + + } + + FuBullet::GetSlotState( rSet, this, GetViewFrame() ); + + if ( GetDocSh()->IsUIActive() ) + { + rSet.DisableItem( SID_INSERT_OBJECT ); + rSet.DisableItem( SID_INSERT_FLOATINGFRAME ); + rSet.DisableItem( SID_INSERT_MATH ); + rSet.DisableItem( SID_INSERT_DIAGRAM ); + rSet.DisableItem( SID_ATTR_TABLE ); + rSet.DisableItem( SID_SIZE_REAL ); + rSet.DisableItem( SID_SIZE_OPTIMAL ); + rSet.DisableItem( SID_SIZE_ALL ); + rSet.DisableItem( SID_SIZE_PAGE_WIDTH ); + rSet.DisableItem( SID_SIZE_PAGE ); + rSet.DisableItem( SID_DUPLICATE_PAGE ); + rSet.DisableItem( SID_ZOOM_TOOLBOX ); + } + + // Zoom-State + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_ZOOM_IN ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_ZOOM_OUT )|| + SfxItemState::DEFAULT == rSet.GetItemState( SID_ZOOM_PANNING ) ) + { + if( GetActiveWindow()->GetZoom() <= GetActiveWindow()->GetMinZoom() || GetDocSh()->IsUIActive() ) + { + rSet.DisableItem( SID_ZOOM_OUT ); + rSet.DisableItem( SID_ZOOM_PANNING ); + } + if( GetActiveWindow()->GetZoom() >= GetActiveWindow()->GetMaxZoom() || GetDocSh()->IsUIActive() ) + rSet.DisableItem( SID_ZOOM_IN ); + } + + if (!mpZoomList->IsNextPossible()) + { + rSet.DisableItem(SID_ZOOM_NEXT); + } + if (!mpZoomList->IsPreviousPossible()) + { + rSet.DisableItem(SID_ZOOM_PREV); + } + + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_REMOTE_DLG ) ) + { + + bool bDisableSdremoteForGood = false; +#ifndef ENABLE_SDREMOTE + bDisableSdremoteForGood = true; +#endif + bDisableSdremoteForGood |= ! ( /*officecfg::Office::Common::Misc::ExperimentalMode::get() &&*/ + officecfg::Office::Impress::Misc::Start::EnableSdremote::get() ); + + // This dialog is only useful for TCP/IP remote control + // which is unusual, under-tested and a security issue. + if ( bDisableSdremoteForGood ) + { + rSet.Put(SfxVisibilityItem(SID_REMOTE_DLG, false)); + } + } + + // EditText active + if (GetViewShellBase().GetViewShellManager()->GetShell(ToolbarId::Draw_Text_Toolbox_Sd) != nullptr) + { + sal_uInt16 nCurrentSId = SID_ATTR_CHAR; + + if(HasCurrentFunction()) + { + nCurrentSId = GetCurrentFunction()->GetSlotID(); + } + if( nCurrentSId != SID_TEXT_FITTOSIZE && + nCurrentSId != SID_TEXT_FITTOSIZE_VERTICAL && + nCurrentSId != SID_ATTR_CHAR_VERTICAL ) + nCurrentSId = SID_ATTR_CHAR; + + rSet.Put( SfxBoolItem( nCurrentSId, true ) ); + } + + if ( GetDocSh()->IsReadOnly() ) + { + rSet.DisableItem( SID_AUTOSPELL_CHECK ); + } + else + { + if (GetDoc()->GetOnlineSpell()) + { + rSet.Put(SfxBoolItem(SID_AUTOSPELL_CHECK, true)); + } + else + { + rSet.Put(SfxBoolItem(SID_AUTOSPELL_CHECK, false)); + } + } + + SdrPageView* pPV = mpDrawView->GetSdrPageView(); + OUString aActiveLayer = mpDrawView->GetActiveLayer(); + + if ( ( !aActiveLayer.isEmpty() && pPV && ( pPV->IsLayerLocked(aActiveLayer) || + !pPV->IsLayerVisible(aActiveLayer) ) ) || + SD_MOD()->GetWaterCan() ) + { + rSet.DisableItem( SID_PASTE ); + rSet.DisableItem( SID_PASTE_SPECIAL ); + rSet.DisableItem( SID_PASTE_UNFORMATTED ); + rSet.DisableItem( SID_CLIPBOARD_FORMAT_ITEMS ); + + rSet.DisableItem( SID_INSERT_FLD_DATE_FIX ); + rSet.DisableItem( SID_INSERT_FLD_DATE_VAR ); + rSet.DisableItem( SID_INSERT_FLD_TIME_FIX ); + rSet.DisableItem( SID_INSERT_FLD_TIME_VAR ); + rSet.DisableItem( SID_INSERT_FLD_AUTHOR ); + rSet.DisableItem( SID_INSERT_FLD_PAGE ); + rSet.DisableItem( SID_INSERT_FLD_PAGE_TITLE ); + rSet.DisableItem( SID_INSERT_FLD_PAGES ); + rSet.DisableItem( SID_INSERT_FLD_FILE ); + + rSet.DisableItem( SID_INSERT_GRAPHIC ); + rSet.DisableItem( SID_INSERT_AVMEDIA ); + rSet.DisableItem( SID_INSERT_DIAGRAM ); + rSet.DisableItem( SID_INSERT_OBJECT ); + rSet.DisableItem( SID_INSERT_FLOATINGFRAME ); + + rSet.DisableItem( SID_INSERT_MATH ); + rSet.DisableItem( SID_INSERT_FRAME ); + rSet.DisableItem( SID_INSERTFILE ); + rSet.DisableItem( SID_ATTR_TABLE ); + rSet.DisableItem( SID_COPYOBJECTS ); + + rSet.DisableItem( SID_SCAN ); + rSet.DisableItem( SID_TWAIN_SELECT ); + rSet.DisableItem( SID_TWAIN_TRANSFER ); + +// rSet.DisableItem( SID_BEZIER_EDIT ); + rSet.DisableItem( SID_GLUE_EDITMODE ); + rSet.DisableItem( SID_OBJECT_ROTATE ); + rSet.DisableItem( SID_OBJECT_SHEAR ); + rSet.DisableItem( SID_OBJECT_MIRROR ); + rSet.DisableItem( SID_OBJECT_CROP ); + rSet.DisableItem( SID_ATTR_GRAF_CROP ); + rSet.DisableItem( SID_OBJECT_TRANSPARENCE ); + rSet.DisableItem( SID_OBJECT_GRADIENT ); + rSet.DisableItem( SID_OBJECT_CROOK_ROTATE ); + rSet.DisableItem( SID_OBJECT_CROOK_SLANT ); + rSet.DisableItem( SID_OBJECT_CROOK_STRETCH ); + + // Disable all object-creating tools + rSet.ClearItem( SID_ATTR_CHAR ); + rSet.DisableItem( SID_ATTR_CHAR ); + rSet.ClearItem( SID_ATTR_CHAR_VERTICAL ); + rSet.DisableItem( SID_ATTR_CHAR_VERTICAL ); + rSet.ClearItem(SID_DRAW_LINE); + rSet.DisableItem(SID_DRAW_LINE); + rSet.ClearItem(SID_DRAW_MEASURELINE); + rSet.DisableItem(SID_DRAW_MEASURELINE); + rSet.ClearItem(SID_DRAW_XLINE); + rSet.DisableItem(SID_DRAW_XLINE); + rSet.ClearItem( SID_LINE_ARROW_START ); + rSet.DisableItem( SID_LINE_ARROW_START ); + rSet.ClearItem( SID_LINE_ARROW_END ); + rSet.DisableItem( SID_LINE_ARROW_END ); + rSet.ClearItem( SID_LINE_ARROWS ); + rSet.DisableItem( SID_LINE_ARROWS ); + rSet.ClearItem( SID_LINE_ARROW_CIRCLE ); + rSet.DisableItem( SID_LINE_ARROW_CIRCLE ); + rSet.ClearItem( SID_LINE_CIRCLE_ARROW ); + rSet.DisableItem( SID_LINE_CIRCLE_ARROW ); + rSet.ClearItem( SID_LINE_ARROW_SQUARE ); + rSet.DisableItem( SID_LINE_ARROW_SQUARE ); + rSet.ClearItem( SID_LINE_SQUARE_ARROW ); + rSet.DisableItem( SID_LINE_SQUARE_ARROW ); + + rSet.ClearItem(SID_DRAW_RECT); + rSet.DisableItem(SID_DRAW_RECT); + rSet.ClearItem(SID_DRAW_RECT_NOFILL); + rSet.DisableItem(SID_DRAW_RECT_NOFILL); + rSet.ClearItem(SID_DRAW_RECT_ROUND); + rSet.DisableItem(SID_DRAW_RECT_ROUND); + rSet.ClearItem(SID_DRAW_RECT_ROUND_NOFILL); + rSet.DisableItem(SID_DRAW_RECT_ROUND_NOFILL); + rSet.ClearItem(SID_DRAW_SQUARE); + rSet.DisableItem(SID_DRAW_SQUARE); + rSet.ClearItem(SID_DRAW_SQUARE_NOFILL); + rSet.DisableItem(SID_DRAW_SQUARE_NOFILL); + rSet.ClearItem(SID_DRAW_SQUARE_ROUND); + rSet.DisableItem(SID_DRAW_SQUARE_ROUND); + rSet.ClearItem(SID_DRAW_SQUARE_ROUND_NOFILL); + rSet.DisableItem(SID_DRAW_SQUARE_ROUND_NOFILL); + rSet.ClearItem(SID_DRAW_ELLIPSE); + rSet.DisableItem(SID_DRAW_ELLIPSE); + rSet.ClearItem(SID_DRAW_ELLIPSE_NOFILL); + rSet.DisableItem(SID_DRAW_ELLIPSE_NOFILL); + rSet.ClearItem(SID_DRAW_CIRCLE); + rSet.DisableItem(SID_DRAW_CIRCLE); + rSet.ClearItem(SID_DRAW_CIRCLE_NOFILL); + rSet.DisableItem(SID_DRAW_CIRCLE_NOFILL); + rSet.ClearItem(SID_DRAW_CAPTION); + rSet.DisableItem(SID_DRAW_CAPTION); + rSet.ClearItem(SID_DRAW_FONTWORK); + rSet.DisableItem(SID_DRAW_FONTWORK); + rSet.ClearItem(SID_DRAW_FONTWORK_VERTICAL); + rSet.DisableItem(SID_DRAW_FONTWORK_VERTICAL); + rSet.ClearItem(SID_DRAW_CAPTION_VERTICAL); + rSet.DisableItem(SID_DRAW_CAPTION_VERTICAL); + rSet.ClearItem(SID_TEXT_FITTOSIZE); + rSet.DisableItem(SID_TEXT_FITTOSIZE); + rSet.ClearItem(SID_TEXT_FITTOSIZE_VERTICAL); + rSet.DisableItem(SID_TEXT_FITTOSIZE_VERTICAL); + rSet.ClearItem(SID_TOOL_CONNECTOR); + rSet.DisableItem(SID_TOOL_CONNECTOR); + rSet.ClearItem(SID_CONNECTOR_ARROW_START); + rSet.DisableItem(SID_CONNECTOR_ARROW_START); + rSet.ClearItem(SID_CONNECTOR_ARROW_END); + rSet.DisableItem(SID_CONNECTOR_ARROW_END); + rSet.ClearItem(SID_CONNECTOR_ARROWS); + rSet.DisableItem(SID_CONNECTOR_ARROWS); + rSet.ClearItem(SID_CONNECTOR_CIRCLE_START); + rSet.DisableItem(SID_CONNECTOR_CIRCLE_START); + rSet.ClearItem(SID_CONNECTOR_CIRCLE_END); + rSet.DisableItem(SID_CONNECTOR_CIRCLE_END); + rSet.ClearItem(SID_CONNECTOR_CIRCLES); + rSet.DisableItem(SID_CONNECTOR_CIRCLES); + rSet.ClearItem(SID_CONNECTOR_LINE); + rSet.DisableItem(SID_CONNECTOR_LINE); + rSet.ClearItem(SID_CONNECTOR_LINE_ARROW_START); + rSet.DisableItem(SID_CONNECTOR_LINE_ARROW_START); + rSet.ClearItem(SID_CONNECTOR_LINE_ARROW_END); + rSet.DisableItem(SID_CONNECTOR_LINE_ARROW_END); + rSet.ClearItem(SID_CONNECTOR_LINE_ARROWS); + rSet.DisableItem(SID_CONNECTOR_LINE_ARROWS); + rSet.ClearItem(SID_CONNECTOR_LINE_CIRCLE_START); + rSet.DisableItem(SID_CONNECTOR_LINE_CIRCLE_START); + rSet.ClearItem(SID_CONNECTOR_LINE_CIRCLE_END); + rSet.DisableItem(SID_CONNECTOR_LINE_CIRCLE_END); + rSet.ClearItem(SID_CONNECTOR_LINE_CIRCLES); + rSet.DisableItem(SID_CONNECTOR_LINE_CIRCLES); + rSet.ClearItem(SID_CONNECTOR_CURVE); + rSet.DisableItem(SID_CONNECTOR_CURVE); + rSet.ClearItem(SID_CONNECTOR_CURVE_ARROW_START); + rSet.DisableItem(SID_CONNECTOR_CURVE_ARROW_START); + rSet.ClearItem(SID_CONNECTOR_CURVE_ARROW_END); + rSet.DisableItem(SID_CONNECTOR_CURVE_ARROW_END); + rSet.ClearItem(SID_CONNECTOR_CURVE_ARROWS); + rSet.DisableItem(SID_CONNECTOR_CURVE_ARROWS); + rSet.ClearItem(SID_CONNECTOR_CURVE_CIRCLE_START); + rSet.DisableItem(SID_CONNECTOR_CURVE_CIRCLE_START); + rSet.ClearItem(SID_CONNECTOR_CURVE_CIRCLE_END); + rSet.DisableItem(SID_CONNECTOR_CURVE_CIRCLE_END); + rSet.ClearItem(SID_CONNECTOR_CURVE_CIRCLES); + rSet.DisableItem(SID_CONNECTOR_CURVE_CIRCLES); + rSet.ClearItem(SID_CONNECTOR_LINES); + rSet.DisableItem(SID_CONNECTOR_LINES); + rSet.ClearItem(SID_CONNECTOR_LINES_ARROW_START); + rSet.DisableItem(SID_CONNECTOR_LINES_ARROW_START); + rSet.ClearItem(SID_CONNECTOR_LINES_ARROW_END); + rSet.DisableItem(SID_CONNECTOR_LINES_ARROW_END); + rSet.ClearItem(SID_CONNECTOR_LINES_ARROWS); + rSet.DisableItem(SID_CONNECTOR_LINES_ARROWS); + rSet.ClearItem(SID_CONNECTOR_LINES_CIRCLE_START); + rSet.DisableItem(SID_CONNECTOR_LINES_CIRCLE_START); + rSet.ClearItem(SID_CONNECTOR_LINES_CIRCLE_END); + rSet.DisableItem(SID_CONNECTOR_LINES_CIRCLE_END); + rSet.ClearItem(SID_CONNECTOR_LINES_CIRCLES); + rSet.DisableItem(SID_CONNECTOR_LINES_CIRCLES); + rSet.ClearItem(SID_DRAW_ARC); + rSet.DisableItem(SID_DRAW_ARC); + rSet.ClearItem(SID_DRAW_CIRCLEARC); + rSet.DisableItem(SID_DRAW_CIRCLEARC); + rSet.ClearItem(SID_DRAW_PIE); + rSet.DisableItem(SID_DRAW_PIE); + rSet.ClearItem(SID_DRAW_PIE_NOFILL); + rSet.DisableItem(SID_DRAW_PIE_NOFILL); + rSet.ClearItem(SID_DRAW_CIRCLEPIE); + rSet.DisableItem(SID_DRAW_CIRCLEPIE); + rSet.ClearItem(SID_DRAW_CIRCLEPIE_NOFILL); + rSet.DisableItem(SID_DRAW_CIRCLEPIE_NOFILL); + rSet.ClearItem(SID_DRAW_ELLIPSECUT); + rSet.DisableItem(SID_DRAW_ELLIPSECUT); + rSet.ClearItem(SID_DRAW_ELLIPSECUT_NOFILL); + rSet.DisableItem(SID_DRAW_ELLIPSECUT_NOFILL); + rSet.ClearItem(SID_DRAW_CIRCLECUT); + rSet.DisableItem(SID_DRAW_CIRCLECUT); + rSet.ClearItem(SID_DRAW_CIRCLECUT_NOFILL); + rSet.DisableItem(SID_DRAW_CIRCLECUT_NOFILL); + rSet.ClearItem(SID_DRAW_POLYGON); + rSet.DisableItem(SID_DRAW_POLYGON); + rSet.ClearItem(SID_DRAW_POLYGON_NOFILL); + rSet.DisableItem(SID_DRAW_POLYGON_NOFILL); + rSet.ClearItem(SID_DRAW_FREELINE); + rSet.DisableItem(SID_DRAW_FREELINE); + rSet.ClearItem(SID_DRAW_FREELINE_NOFILL); + rSet.DisableItem(SID_DRAW_FREELINE_NOFILL); + rSet.ClearItem(SID_DRAW_XPOLYGON); + rSet.DisableItem(SID_DRAW_XPOLYGON); + rSet.ClearItem(SID_DRAW_XPOLYGON_NOFILL); + rSet.DisableItem(SID_DRAW_XPOLYGON_NOFILL); + rSet.ClearItem(SID_DRAW_BEZIER_FILL); + rSet.DisableItem(SID_DRAW_BEZIER_FILL); + rSet.ClearItem(SID_DRAW_BEZIER_NOFILL); + rSet.DisableItem(SID_DRAW_BEZIER_NOFILL); + rSet.ClearItem(SID_3D_CUBE); + rSet.DisableItem(SID_3D_CUBE); + rSet.ClearItem(SID_3D_SHELL); + rSet.DisableItem(SID_3D_SHELL); + rSet.ClearItem(SID_3D_SPHERE); + rSet.DisableItem(SID_3D_SPHERE); + rSet.ClearItem(SID_3D_HALF_SPHERE); + rSet.DisableItem(SID_3D_HALF_SPHERE); + rSet.ClearItem(SID_3D_CYLINDER); + rSet.DisableItem(SID_3D_CYLINDER); + rSet.ClearItem(SID_3D_CONE); + rSet.DisableItem(SID_3D_CONE); + rSet.ClearItem(SID_3D_TORUS); + rSet.DisableItem(SID_3D_TORUS); + rSet.ClearItem(SID_3D_PYRAMID); + rSet.DisableItem(SID_3D_PYRAMID); + } + + // are the modules available? + + if (!SvtModuleOptions().IsCalc()) + { + // remove menu entry if module is not available + rSet.Put( SfxVisibilityItem( SID_ATTR_TABLE, false ) ); + } + if (!SvtModuleOptions().IsChart()) + { + rSet.DisableItem( SID_INSERT_DIAGRAM ); + } + if (!SvtModuleOptions().IsMath()) + { + rSet.DisableItem( SID_INSERT_MATH ); + } + + rtl::Reference< sd::SlideShow > xSlideshow( SlideShow::GetSlideShow( GetViewShellBase() ) ); + if( (xSlideshow.is() && xSlideshow->isRunning() && (xSlideshow->getAnimationMode() != ANIMATIONMODE_PREVIEW) ) || GetDocSh()->IsPreview() ) + { + // Own Slots + rSet.DisableItem( SID_PRESENTATION ); + rSet.DisableItem( SID_ZOOM_IN ); + rSet.DisableItem( SID_ZOOM_OUT ); + rSet.DisableItem( SID_ZOOM_PANNING ); + rSet.DisableItem( SID_ZOOM_MODE ); + rSet.DisableItem( SID_ZOOM_NEXT ); + rSet.DisableItem( SID_ZOOM_PREV ); + rSet.DisableItem( SID_SIZE_REAL ); + rSet.DisableItem( SID_SIZE_OPTIMAL ); + rSet.DisableItem( SID_SIZE_ALL ); + rSet.DisableItem( SID_SIZE_PAGE_WIDTH ); + rSet.DisableItem( SID_SIZE_PAGE ); + rSet.DisableItem( SID_INSERTPAGE ); + rSet.DisableItem( SID_DUPLICATE_PAGE ); + rSet.DisableItem( SID_MODIFYPAGE ); + rSet.DisableItem( SID_RENAMEPAGE ); + rSet.DisableItem( SID_RENAMEPAGE_QUICK ); + rSet.DisableItem( SID_DELETE_PAGE ); + rSet.DisableItem( SID_PAGESETUP ); + + if( xSlideshow.is() && xSlideshow->isRunning() ) + { + rSet.ClearItem(SID_INSERTFILE); + rSet.ClearItem(SID_OBJECT_ROTATE); + rSet.ClearItem(SID_FM_CONFIG); + rSet.ClearItem(SID_ANIMATION_EFFECTS); + rSet.ClearItem(SID_EXECUTE_ANIMATION_EFFECT); + rSet.ClearItem(SID_ANIMATION_OBJECTS); + rSet.ClearItem(SID_3D_WIN); + + rSet.DisableItem(SID_OBJECT_ALIGN); + rSet.DisableItem(SID_ZOOM_TOOLBOX); + rSet.DisableItem(SID_OBJECT_CHOOSE_MODE); + rSet.DisableItem(SID_DRAWTBX_TEXT); + rSet.DisableItem(SID_DRAWTBX_RECTANGLES); + rSet.DisableItem(SID_DRAWTBX_ELLIPSES); + rSet.DisableItem(SID_DRAWTBX_LINES); + rSet.DisableItem(SID_DRAWTBX_ARROWS); + rSet.DisableItem(SID_DRAWTBX_3D_OBJECTS); + rSet.DisableItem(SID_DRAWTBX_CONNECTORS); + rSet.DisableItem(SID_OBJECT_CHOOSE_MODE ); + rSet.DisableItem(SID_DRAWTBX_INSERT); + rSet.DisableItem(SID_INSERTFILE); + rSet.DisableItem(SID_OBJECT_ROTATE); + rSet.DisableItem(SID_POSITION); + rSet.DisableItem(SID_FM_CONFIG); + rSet.DisableItem(SID_ANIMATION_EFFECTS); + rSet.DisableItem(SID_EXECUTE_ANIMATION_EFFECT); + rSet.DisableItem(SID_ANIMATION_OBJECTS); + rSet.DisableItem(SID_3D_WIN); + } + } + + // Menuoption: Change->Convert->To Bitmap, Change->Convert->To Metafile + // disable, if there only Bitmap or Metafiles marked + // Menuoption: Format->Area, Format->Line + // disabled, if the marked objects not able to handle + // these attributes + + bool bSingleGraphicSelected = false; + + if (!mpDrawView->AreObjectsMarked()) + { + rSet.DisableItem (SID_CONVERT_TO_METAFILE); + rSet.DisableItem (SID_CONVERT_TO_BITMAP); + } + else + { + // get marklist + SdrMarkList aMarkList = mpDrawView->GetMarkedObjectList(); + + bool bFoundBitmap = false; + bool bFoundMetafile = false; + bool bFoundObjNoArea = false; + bool bFoundNoGraphicObj = false; + bool bFoundAny = false; + bool bFoundTable = false; + +// const size_t nMarkCount = aMarkList.GetMarkCount(); + for (size_t i=0; i < nMarkCount && !bFoundAny; ++i) + { + SdrObject* pObj = aMarkList.GetMark(i)->GetMarkedSdrObj(); + SdrObjKind nId = pObj->GetObjIdentifier(); + SdrInventor nInv = pObj->GetObjInventor(); + + if(nInv == SdrInventor::Default) + { + // 2D objects + switch( nId ) + { + case SdrObjKind::PathLine : + case SdrObjKind::PolyLine : + case SdrObjKind::Line: + case SdrObjKind::FreehandLine : + case SdrObjKind::Edge: + case SdrObjKind::CircleArc : + bFoundObjNoArea = true; + bFoundNoGraphicObj = true; + break; + case SdrObjKind::OLE2 : + // #i118485# #i118525# Allow Line, Area and Graphic (Metafile, Bitmap) + bSingleGraphicSelected = nMarkCount == 1; + bFoundBitmap = true; + bFoundMetafile = true; + break; + case SdrObjKind::Graphic : + { + bSingleGraphicSelected = nMarkCount == 1; + const SdrGrafObj* pSdrGrafObj = static_cast< const SdrGrafObj* >(pObj); + + // Current size of the OBJ_GRAF + const ::tools::Rectangle aRect = pObj->GetLogicRect(); + const Size aCurrentSizeofObj = aRect.GetSize(); + + // Original size of the OBJ_GRAF + const Size aOriginalSizeofObj = pSdrGrafObj->getOriginalSize(); + + if(aCurrentSizeofObj == aOriginalSizeofObj ) + rSet.DisableItem(SID_ORIGINAL_SIZE); + + switch(pSdrGrafObj->GetGraphicType()) + { + case GraphicType::Bitmap : + bFoundBitmap = true; + if(pSdrGrafObj->isEmbeddedVectorGraphicData()) + { + bFoundMetafile = true; + } + break; + case GraphicType::GdiMetafile : + bFoundMetafile = true; + break; + default: + break; + } + break; + } + case SdrObjKind::Table: + bFoundTable = true; + break; + default : + bFoundAny = true; + } + } + else if(nInv == SdrInventor::E3d) + { + // 3D objects + bFoundAny = true; + } + } + + if( bFoundTable ) + rSet.DisableItem( SID_ATTRIBUTES_LINE ); + + if (!bFoundAny) + { + // Disable menuitem for area-dialog + if( bFoundObjNoArea ) // #i25616# + rSet.DisableItem( SID_ATTRIBUTES_AREA ); + + if( bFoundBitmap && !bFoundMetafile && !bFoundNoGraphicObj ) // only Bitmaps marked + rSet.DisableItem( SID_CONVERT_TO_BITMAP ); + else if( !bFoundBitmap && bFoundMetafile && !bFoundNoGraphicObj ) // only Metafiles marked + rSet.DisableItem( SID_CONVERT_TO_METAFILE ); + else if( !bFoundBitmap && !bFoundMetafile && !bFoundNoGraphicObj ) // nothing to do + { + rSet.DisableItem( SID_CONVERT_TO_BITMAP ); + rSet.DisableItem( SID_CONVERT_TO_METAFILE ); + } + } + } + + if( !bSingleGraphicSelected ) + { + rSet.DisableItem (SID_OBJECT_CROP); + rSet.DisableItem (SID_ATTR_GRAF_CROP); + } + + // Menuoption: Edit->Hyperlink + // Disable, if there is no hyperlink + bool bDisableEditHyperlink; + if (!moAtContextMenu_DisableEditHyperlink) + bDisableEditHyperlink = ShouldDisableEditHyperlink(); + else + { + // tdf#137445 if a popup menu was active, use the state as of when the popup was launched and then drop + // moAtContextMenu_DisableEditHyperlink + bDisableEditHyperlink = *moAtContextMenu_DisableEditHyperlink; + moAtContextMenu_DisableEditHyperlink.reset(); + } + + //highlight selected custom shape + { + if(HasCurrentFunction()) + { + rtl::Reference< FuPoor > xFunc( GetCurrentFunction() ); + FuConstructCustomShape* pShapeFunc = dynamic_cast< FuConstructCustomShape* >( xFunc.get() ); + + static const sal_uInt16 nCSTbArray[] = { SID_DRAWTBX_CS_BASIC, SID_DRAWTBX_CS_SYMBOL, + SID_DRAWTBX_CS_ARROW, SID_DRAWTBX_CS_FLOWCHART, + SID_DRAWTBX_CS_CALLOUT, SID_DRAWTBX_CS_STAR }; + + const sal_uInt16 nCurrentSId = GetCurrentFunction()->GetSlotID(); + for (sal_uInt16 i : nCSTbArray) + { + rSet.ClearItem( i ); // Why is this necessary? + rSet.Put( SfxStringItem( i, nCurrentSId == i && pShapeFunc + ? pShapeFunc->GetShapeType() : OUString() ) ); + } + } + } + + if ( bDisableEditHyperlink || GetDocSh()->IsReadOnly() ) + rSet.DisableItem( SID_EDIT_HYPERLINK ); + + if ( bDisableEditHyperlink ) + { + rSet.DisableItem( SID_OPEN_HYPERLINK ); + rSet.DisableItem( SID_COPY_HYPERLINK_LOCATION ); + } + + //fdo#78151 enable show next level/hide last level if editing a master page + //PresObjKind::Outline object and the current selection allow that to happen + { + bool bDisableShowNextLevel = true; + bool bDisableHideLastLevel = true; + + ESelection aSel; + ::Outliner* pOL = GetOutlinerForMasterPageOutlineTextObj(aSel); + if (pOL) + { + //and are on the last paragraph + aSel.Adjust(); + if (aSel.nEndPara == pOL->GetParagraphCount() - 1) + { + sal_uInt16 nDepth = pOL->GetDepth(aSel.nEndPara); + if (nDepth != sal_uInt16(-1)) + { + //there exists another numbering level that + //is currently hidden + if (nDepth < 8) + bDisableShowNextLevel = false; + //there exists a previous numbering level + if (nDepth > 0) + bDisableHideLastLevel = false; + } + } + } + + if (bDisableShowNextLevel) + rSet.DisableItem(SID_SHOW_NEXT_LEVEL); + + if (bDisableHideLastLevel) + rSet.DisableItem(SID_HIDE_LAST_LEVEL); + } + +#if defined(_WIN32) || defined UNX + if( !mxScannerManager.is() ) + { + rSet.DisableItem( SID_TWAIN_SELECT ); + rSet.DisableItem( SID_TWAIN_TRANSFER ); + } +#endif + + // Set the state of two entries in the 'Slide' context sub-menu + // concerning the visibility of master page background and master page + // shapes. + if (rSet.GetItemState(SID_DISPLAY_MASTER_BACKGROUND) == SfxItemState::DEFAULT + || rSet.GetItemState(SID_DISPLAY_MASTER_OBJECTS) == SfxItemState::DEFAULT) + { + SdPage* pPage = GetActualPage(); + if (pPage != nullptr && GetDoc() != nullptr) + { + SdrLayerIDSet aVisibleLayers = pPage->TRG_GetMasterPageVisibleLayers(); + SdrLayerAdmin& rLayerAdmin = GetDoc()->GetLayerAdmin(); + SdrLayerID aBackgroundId = rLayerAdmin.GetLayerID(sUNO_LayerName_background); + SdrLayerID aObjectId = rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects); + rSet.Put(SfxBoolItem(SID_DISPLAY_MASTER_BACKGROUND, + aVisibleLayers.IsSet(aBackgroundId))); + rSet.Put(SfxBoolItem(SID_DISPLAY_MASTER_OBJECTS, + aVisibleLayers.IsSet(aObjectId))); + } + } + + if (rSet.GetItemState(SID_SAVE_BACKGROUND) == SfxItemState::DEFAULT) + { + bool bDisableSaveBackground = true; + SdPage* pPage = GetActualPage(); + if (pPage != nullptr && GetDoc() != nullptr) + { + SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST> aMergedAttr(GetDoc()->GetPool()); + SdStyleSheet* pStyleSheet = pPage->getPresentationStyle(HID_PSEUDOSHEET_BACKGROUND); + MergePageBackgroundFilling(pPage, pStyleSheet, meEditMode == EditMode::MasterPage, aMergedAttr); + if (drawing::FillStyle_BITMAP == aMergedAttr.Get(XATTR_FILLSTYLE).GetValue()) + { + bDisableSaveBackground = false; + } + } + if (bDisableSaveBackground) + rSet.DisableItem(SID_SAVE_BACKGROUND); + } + + if (GetObjectShell()->isExportLocked()) + rSet.DisableItem(SID_PRESENTATION_MINIMIZER); + + if (rSet.GetItemState(SID_INSERT_SIGNATURELINE) == SfxItemState::DEFAULT) + { + if (!GetObjectShell()->IsSignPDF()) + { + // Currently SID_INSERT_SIGNATURELINE assumes a PDF that was opened for signing, disable + // it otherwise. + rSet.DisableItem(SID_INSERT_SIGNATURELINE); + } + } + + GetModeSwitchingMenuState (rSet); +} + +void DrawViewShell::GetModeSwitchingMenuState (SfxItemSet &rSet) +{ + //DrawView + rSet.Put(SfxBoolItem(SID_SLIDE_SORTER_MODE, false)); + rSet.Put(SfxBoolItem(SID_OUTLINE_MODE, false)); + rSet.Put(SfxBoolItem(SID_SLIDE_MASTER_MODE, false)); + rSet.Put(SfxBoolItem(SID_NOTES_MASTER_MODE, false)); + if (mePageKind == PageKind::Notes) + { + rSet.Put(SfxBoolItem(SID_DRAWINGMODE, false)); + rSet.Put(SfxBoolItem(SID_NOTES_MODE, true)); + rSet.Put(SfxBoolItem(SID_HANDOUT_MASTER_MODE, false)); + } + else if (mePageKind == PageKind::Handout) + { + rSet.Put(SfxBoolItem(SID_DRAWINGMODE, false)); + rSet.Put(SfxBoolItem(SID_NOTES_MODE, false)); + rSet.Put(SfxBoolItem(SID_HANDOUT_MASTER_MODE, true)); + } + else + { + rSet.Put(SfxBoolItem(SID_DRAWINGMODE, true)); + rSet.Put(SfxBoolItem(SID_NOTES_MODE, false)); + rSet.Put(SfxBoolItem(SID_HANDOUT_MASTER_MODE, false)); + } + + // Removed [GetDocSh()->GetCurrentFunction() ||] from the following + // clause because the current function of the docshell can only be + // search and replace or spell checking and in that case switching the + // view mode is allowed. + const bool bIsRunning = SlideShow::IsRunning(GetViewShellBase()); + + if (GetViewFrame()->GetFrame().IsInPlace() || bIsRunning) + { + if ( !GetViewFrame()->GetFrame().IsInPlace() ) + { + rSet.ClearItem( SID_DRAWINGMODE ); + rSet.DisableItem( SID_DRAWINGMODE ); + } + + rSet.ClearItem( SID_NOTES_MODE ); + rSet.DisableItem( SID_NOTES_MODE ); + + rSet.ClearItem( SID_HANDOUT_MASTER_MODE ); + rSet.DisableItem( SID_HANDOUT_MASTER_MODE ); + + rSet.ClearItem( SID_OUTLINE_MODE ); + rSet.DisableItem( SID_OUTLINE_MODE ); + + rSet.ClearItem( SID_SLIDE_MASTER_MODE ); + rSet.DisableItem( SID_SLIDE_MASTER_MODE ); + + rSet.ClearItem( SID_NOTES_MASTER_MODE ); + rSet.DisableItem( SID_NOTES_MASTER_MODE ); + + rSet.ClearItem( SID_SLIDE_SORTER_MODE ); + rSet.DisableItem( SID_SLIDE_SORTER_MODE ); + } + + if (GetDocSh()->GetCreateMode() == SfxObjectCreateMode::EMBEDDED) + { + // Outplace-Edit: do not allow switch + rSet.ClearItem( SID_OUTLINE_MODE ); + rSet.DisableItem( SID_OUTLINE_MODE ); + + rSet.ClearItem( SID_SLIDE_SORTER_MODE ); + rSet.DisableItem( SID_SLIDE_SORTER_MODE ); + + rSet.ClearItem( SID_NOTES_MODE ); + rSet.DisableItem( SID_NOTES_MODE ); + + rSet.ClearItem( SID_HANDOUT_MASTER_MODE ); + rSet.DisableItem( SID_HANDOUT_MASTER_MODE ); + + rSet.ClearItem( SID_SLIDE_MASTER_MODE ); + rSet.DisableItem( SID_SLIDE_MASTER_MODE ); + + rSet.ClearItem( SID_NOTES_MASTER_MODE ); + rSet.DisableItem( SID_NOTES_MASTER_MODE ); + } + + svx::ExtrusionBar::getState( mpDrawView.get(), rSet ); + svx::FontworkBar::getState( mpDrawView.get(), rSet ); +} + +void DrawViewShell::GetPageProperties( SfxItemSet &rSet ) +{ + SdPage *pPage = getCurrentPage(); + + if (pPage == nullptr || GetDoc() == nullptr) + return; + + SvxPageItem aPageItem(SID_ATTR_PAGE); + aPageItem.SetLandscape( pPage->GetOrientation() == Orientation::Landscape ); + + rSet.Put(SvxSizeItem( SID_ATTR_PAGE_SIZE, pPage->GetSize() )); + rSet.Put(aPageItem); + + const SfxItemSet &rPageAttr = pPage->getSdrPageProperties().GetItemSet(); + const XFillStyleItem* pFillStyle = rPageAttr.GetItem(XATTR_FILLSTYLE); + if (!pFillStyle) + return; + + drawing::FillStyle eXFS = pFillStyle->GetValue(); + XFillStyleItem aFillStyleItem( eXFS ); + aFillStyleItem.SetWhich( SID_ATTR_PAGE_FILLSTYLE ); + rSet.Put(aFillStyleItem); + + switch (eXFS) + { + case drawing::FillStyle_SOLID: + if (const XFillColorItem* pColorItem = rPageAttr.GetItem(XATTR_FILLCOLOR)) + { + Color aColor = pColorItem->GetColorValue(); + XFillColorItem aFillColorItem( OUString(), aColor ); + aFillColorItem.SetWhich( SID_ATTR_PAGE_COLOR ); + rSet.Put( aFillColorItem ); + } + break; + + case drawing::FillStyle_GRADIENT: + { + const XFillGradientItem *pGradient = rPageAttr.GetItem( XATTR_FILLGRADIENT ); + XFillGradientItem aFillGradientItem( pGradient->GetName(), pGradient->GetGradientValue(), SID_ATTR_PAGE_GRADIENT ); + rSet.Put( aFillGradientItem ); + } + break; + + case drawing::FillStyle_HATCH: + { + const XFillHatchItem *pFillHatchItem( rPageAttr.GetItem( XATTR_FILLHATCH ) ); + XFillHatchItem aFillHatchItem( pFillHatchItem->GetName(), pFillHatchItem->GetHatchValue()); + aFillHatchItem.SetWhich( SID_ATTR_PAGE_HATCH ); + rSet.Put( aFillHatchItem ); + } + break; + + case drawing::FillStyle_BITMAP: + { + const XFillBitmapItem *pFillBitmapItem = rPageAttr.GetItem( XATTR_FILLBITMAP ); + XFillBitmapItem aFillBitmapItem( pFillBitmapItem->GetName(), pFillBitmapItem->GetGraphicObject() ); + aFillBitmapItem.SetWhich( SID_ATTR_PAGE_BITMAP ); + rSet.Put( aFillBitmapItem ); + } + break; + + default: + break; + } +} + +void DrawViewShell::SetPageProperties (SfxRequest& rReq) +{ + SdPage *pPage = getCurrentPage(); + if (!pPage) + return; + sal_uInt16 nSlotId = rReq.GetSlot(); + const SfxItemSet *pArgs = rReq.GetArgs(); + if (!pArgs) + return; + + if ( ( nSlotId >= SID_ATTR_PAGE_COLOR ) && ( nSlotId <= SID_ATTR_PAGE_FILLSTYLE ) ) + { + SdrPageProperties& rPageProperties = pPage->getSdrPageProperties(); + const SfxItemSet &aPageItemSet = rPageProperties.GetItemSet(); + SfxItemSet aTempSet = aPageItemSet.CloneAsValue(false, &mpDrawView->GetModel()->GetItemPool()); + const SfxPoolItem* pItem = nullptr; + + rPageProperties.ClearItem(XATTR_FILLSTYLE); + rPageProperties.ClearItem(XATTR_FILLGRADIENT); + rPageProperties.ClearItem(XATTR_FILLHATCH); + rPageProperties.ClearItem(XATTR_FILLBITMAP); + + switch (nSlotId) + { + case SID_ATTR_PAGE_FILLSTYLE: + { + XFillStyleItem aFSItem( pArgs->Get( XATTR_FILLSTYLE ) ); + drawing::FillStyle eXFS = aFSItem.GetValue(); + + if ( eXFS == drawing::FillStyle_NONE ) + rPageProperties.PutItem( XFillStyleItem( eXFS ) ); + } + break; + + case SID_ATTR_PAGE_COLOR: + { + if (SfxItemState::SET == pArgs->GetItemState(SID_ATTR_COLOR_STR, false, &pItem)) + { + Color aColor; + OUString sColor; + + sColor = static_cast<const SfxStringItem*>(pItem)->GetValue(); + + if (sColor == "transparent") + aColor = COL_TRANSPARENT; + else + aColor = Color(ColorTransparency, sColor.toInt32(16)); + + XFillColorItem aColorItem(OUString(), aColor); + rPageProperties.PutItem( XFillStyleItem( drawing::FillStyle_SOLID ) ); + rPageProperties.PutItem( aColorItem ); + } + else + { + XFillColorItem aColorItem( pArgs->Get( XATTR_FILLCOLOR ) ); + rPageProperties.PutItem( XFillStyleItem( drawing::FillStyle_SOLID ) ); + rPageProperties.PutItem( aColorItem ); + } + } + break; + + case SID_ATTR_PAGE_GRADIENT: + { + if (SfxItemState::SET == pArgs->GetItemState(SID_FILL_GRADIENT_JSON, false, &pItem)) + { + const SfxStringItem* pJSON = static_cast<const SfxStringItem*>(pItem); + XFillGradientItem aGradientItem( XGradient::fromJSON(pJSON->GetValue()) ); + + // MigrateItemSet guarantees unique gradient names + SfxItemSetFixed<XATTR_FILLGRADIENT, XATTR_FILLGRADIENT> aMigrateSet( mpDrawView->GetModel()->GetItemPool() ); + aMigrateSet.Put( aGradientItem ); + SdrModel::MigrateItemSet( &aMigrateSet, &aTempSet, mpDrawView->GetModel() ); + + rPageProperties.PutItem( XFillStyleItem( drawing::FillStyle_GRADIENT ) ); + rPageProperties.PutItemSet( aTempSet ); + } + else + { + XFillGradientItem aGradientItem( pArgs->Get( XATTR_FILLGRADIENT ) ); + + // MigrateItemSet guarantees unique gradient names + SfxItemSetFixed<XATTR_FILLGRADIENT, XATTR_FILLGRADIENT> aMigrateSet( mpDrawView->GetModel()->GetItemPool() ); + aMigrateSet.Put( aGradientItem ); + SdrModel::MigrateItemSet( &aMigrateSet, &aTempSet, mpDrawView->GetModel() ); + + rPageProperties.PutItem( XFillStyleItem( drawing::FillStyle_GRADIENT ) ); + rPageProperties.PutItemSet( aTempSet ); + } + } + break; + + case SID_ATTR_PAGE_HATCH: + { + XFillHatchItem aHatchItem( pArgs->Get( XATTR_FILLHATCH ) ); + rPageProperties.PutItem( XFillStyleItem( drawing::FillStyle_HATCH ) ); + rPageProperties.PutItem( aHatchItem ); + } + break; + + case SID_ATTR_PAGE_BITMAP: + { + XFillBitmapItem aBitmapItem( pArgs->Get( XATTR_FILLBITMAP ) ); + rPageProperties.PutItem( XFillStyleItem( drawing::FillStyle_BITMAP ) ); + rPageProperties.PutItem( aBitmapItem ); + } + break; + + default: + break; + } + + rReq.Done(); + } + else + { + PageKind ePageKind = GetPageKind(); + const SfxPoolItem* pPoolItem = nullptr; + Size aNewSize(pPage->GetSize()); + sal_Int32 nLeft = -1, nRight = -1, nUpper = -1, nLower = -1; + bool bScaleAll = true; + Orientation eOrientation = pPage->GetOrientation(); + SdPage* pMasterPage = pPage->IsMasterPage() ? pPage : &static_cast<SdPage&>(pPage->TRG_GetMasterPage()); + bool bFullSize = pMasterPage->IsBackgroundFullSize(); + sal_uInt16 nPaperBin = pPage->GetPaperBin(); + + switch (nSlotId) + { + case SID_ATTR_PAGE_LRSPACE: + if( pArgs->GetItemState(SID_ATTR_PAGE_LRSPACE, + true,&pPoolItem) == SfxItemState::SET ) + { + nLeft = static_cast<const SvxLongLRSpaceItem*>(pPoolItem)->GetLeft(); + nRight = static_cast<const SvxLongLRSpaceItem*>(pPoolItem)->GetRight(); + if (nLeft != -1) + { + nUpper = pPage->GetUpperBorder(); + nLower = pPage->GetLowerBorder(); + } + SetPageSizeAndBorder(ePageKind, aNewSize, nLeft, nRight, nUpper, nLower, bScaleAll, eOrientation, nPaperBin, bFullSize ); + } + break; + + case SID_ATTR_PAGE_ULSPACE: + if( pArgs->GetItemState(SID_ATTR_PAGE_ULSPACE, + true,&pPoolItem) == SfxItemState::SET ) + { + nUpper = static_cast<const SvxLongULSpaceItem*>(pPoolItem)->GetUpper(); + nLower = static_cast<const SvxLongULSpaceItem*>(pPoolItem)->GetLower(); + if (nUpper != -1) + { + nLeft = pPage->GetLeftBorder(); + nRight = pPage->GetRightBorder(); + } + SetPageSizeAndBorder(ePageKind, aNewSize, nLeft, nRight, nUpper, nLower, bScaleAll, eOrientation, nPaperBin, bFullSize ); + } + break; + + default: + break; + } + } +} + +void DrawViewShell::GetState (SfxItemSet& rSet) +{ + // Iterate over all requested items in the set. + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + while (nWhich) + { + switch (nWhich) + { + case SID_SEARCH_ITEM: + case SID_SEARCH_OPTIONS: + // Forward this request to the common (old) code of the + // document shell. + GetDocSh()->GetState (rSet); + break; + default: + SAL_WARN("sd", "DrawViewShell::GetState(): can not handle which id " << nWhich); + break; + } + nWhich = aIter.NextWhich(); + } +} + +void DrawViewShell::Execute (SfxRequest& rReq) +{ + if(SlideShow::IsRunning(GetViewShellBase())) + { + // Do not execute anything during a native slide show. + return; + } + + switch (rReq.GetSlot()) + { + case SID_SEARCH_ITEM: + // Forward this request to the common (old) code of the + // document shell. + GetDocSh()->Execute (rReq); + break; + + case SID_SPELL_DIALOG: + { + SfxViewFrame* pViewFrame = GetViewFrame(); + if (rReq.GetArgs() != nullptr) + pViewFrame->SetChildWindow (SID_SPELL_DIALOG, + static_cast<const SfxBoolItem&>(rReq.GetArgs()-> + Get(SID_SPELL_DIALOG)).GetValue()); + else + pViewFrame->ToggleChildWindow(SID_SPELL_DIALOG); + + pViewFrame->GetBindings().Invalidate(SID_SPELL_DIALOG); + rReq.Ignore (); + } + break; + + default: + SAL_WARN("sd", "DrawViewShell::Execute(): can not handle slot " << rReq.GetSlot()); + break; + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviews8.cxx b/sd/source/ui/view/drviews8.cxx new file mode 100644 index 000000000..e5ae5cd97 --- /dev/null +++ b/sd/source/ui/view/drviews8.cxx @@ -0,0 +1,135 @@ +/* -*- 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 <DrawViewShell.hxx> + +#include <com/sun/star/scanner/XScannerManager2.hpp> +#include <toolkit/helper/vclunohelper.hxx> +#include <svx/svxids.hrc> +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/svdograf.hxx> +#include <svx/svdpagv.hxx> + +#include <Window.hxx> +#include <drawview.hxx> +#include <tools/helpers.hxx> +#include <vcl/svapp.hxx> + +namespace sd { + +void DrawViewShell::ScannerEvent() +{ + if( mxScannerManager.is() ) + { + const css::scanner::ScannerContext aContext( mxScannerManager->getAvailableScanners().getConstArray()[ 0 ] ); + const css::scanner::ScanError eError = mxScannerManager->getError( aContext ); + + if( css::scanner::ScanError_ScanErrorNone == eError ) + { + const css::uno::Reference< css::awt::XBitmap > xBitmap( mxScannerManager->getBitmap( aContext ) ); + + if( xBitmap.is() ) + { + const BitmapEx aScanBmp( VCLUnoHelper::GetBitmap( xBitmap ) ); + + if( !aScanBmp.IsEmpty() ) + { + const SolarMutexGuard aGuard; + SdrPage* pPage = mpDrawView->GetSdrPageView()->GetPage(); + Size aBmpSize( aScanBmp.GetPrefSize() ), aPageSize( pPage->GetSize() ); + const MapMode aMap100( MapUnit::Map100thMM ); + + if( !aBmpSize.Width() || !aBmpSize.Height() ) + aBmpSize = aScanBmp.GetSizePixel(); + + if( aScanBmp.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel ) + aBmpSize = GetActiveWindow()->PixelToLogic( aBmpSize, aMap100 ); + else + aBmpSize = OutputDevice::LogicToLogic( aBmpSize, aScanBmp.GetPrefMapMode(), aMap100 ); + + aPageSize.AdjustWidth( -(pPage->GetLeftBorder() + pPage->GetRightBorder()) ); + aPageSize.AdjustHeight( -(pPage->GetUpperBorder() + pPage->GetLowerBorder()) ); + + if( ( ( aBmpSize.Height() > aPageSize.Height() ) || ( aBmpSize.Width() > aPageSize.Width() ) ) && aBmpSize.Height() && aPageSize.Height() ) + { + double fGrfWH = static_cast<double>(aBmpSize.Width()) / aBmpSize.Height(); + double fWinWH = static_cast<double>(aPageSize.Width()) / aPageSize.Height(); + + if( fGrfWH < fWinWH ) + { + aBmpSize.setWidth( FRound( aPageSize.Height() * fGrfWH ) ); + aBmpSize.setHeight( aPageSize.Height() ); + } + else if( fGrfWH > 0.F ) + { + aBmpSize.setWidth( aPageSize.Width() ); + aBmpSize.setHeight( FRound( aPageSize.Width() / fGrfWH ) ); + } + } + + Point aPnt ( ( aPageSize.Width() - aBmpSize.Width() ) >> 1, ( aPageSize.Height() - aBmpSize.Height() ) >> 1 ); + aPnt += Point( pPage->GetLeftBorder(), pPage->GetUpperBorder() ); + ::tools::Rectangle aRect( aPnt, aBmpSize ); + bool bInsertNewObject = true; + + if( GetView()->AreObjectsMarked() ) + { + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + + if( rMarkList.GetMarkCount() == 1 ) + { + SdrMark* pMark = rMarkList.GetMark(0); + SdrObject* pObj = pMark->GetMarkedSdrObj(); + + if( auto pGrafObj = dynamic_cast< SdrGrafObj *>( pObj ) ) + { + if( pGrafObj->IsEmptyPresObj() ) + { + bInsertNewObject = false; + pGrafObj->SetEmptyPresObj(false); + pGrafObj->SetOutlinerParaObject(std::nullopt); + pGrafObj->SetGraphic( Graphic( aScanBmp ) ); + } + } + } + } + + if( bInsertNewObject ) + { + auto pGrafObj = new SdrGrafObj( + GetView()->getSdrModelFromSdrView(), + Graphic(aScanBmp), + aRect); + SdrPageView* pPV = GetView()->GetSdrPageView(); + GetView()->InsertObjectAtView( pGrafObj, *pPV, SdrInsertFlags::SETDEFLAYER ); + } + } + } + } + } + + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_TWAIN_SELECT ); + rBindings.Invalidate( SID_TWAIN_TRANSFER ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviews9.cxx b/sd/source/ui/view/drviews9.cxx new file mode 100644 index 000000000..f80419587 --- /dev/null +++ b/sd/source/ui/view/drviews9.cxx @@ -0,0 +1,886 @@ +/* -*- 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 <config_features.h> + +#include <DrawViewShell.hxx> +#include <svx/xgrad.hxx> +#include <svx/svdpagv.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xlineit0.hxx> +#include <svx/xlnwtit.hxx> +#include <svx/xlndsit.hxx> +#include <svx/xflhtit.hxx> +#include <svx/xflgrit.hxx> +#include <svx/xlnclit.hxx> +#include <svx/xflclit.hxx> +#include <sfx2/bindings.hxx> + +#include <sfx2/dispatch.hxx> +#include <svl/intitem.hxx> +#include <sfx2/request.hxx> +#include <svl/stritem.hxx> +#include <svx/svxids.hrc> +#include <svx/xtable.hxx> +#include <vcl/graph.hxx> +#include <svx/svdograf.hxx> +#include <svl/whiter.hxx> +#include <basic/sbstar.hxx> +#include <basic/sberrors.hxx> + +#include <sfx2/viewfrm.hxx> + +#include <app.hrc> +#include <strings.hrc> +#include <Window.hxx> +#include <drawdoc.hxx> +#include <drawview.hxx> +#include <DrawDocShell.hxx> +#include <sdresid.hxx> + +#include <svx/galleryitem.hxx> +#include <com/sun/star/gallery/GalleryItemType.hpp> +#include <com/sun/star/drawing/LineStyle.hpp> +#include <memory> + +using namespace com::sun::star; + +namespace sd { + +void DrawViewShell::ExecGallery(SfxRequest const & rReq) +{ + // nothing is executed during a slide show! + if(HasCurrentFunction(SID_PRESENTATION)) + return; + + const SfxItemSet* pArgs = rReq.GetArgs(); + + const SvxGalleryItem* pGalleryItem = SfxItemSet::GetItem<SvxGalleryItem>(pArgs, SID_GALLERY_FORMATS, false); + if ( !pGalleryItem ) + return; + + GetDocSh()->SetWaitCursor( true ); + + sal_Int8 nType( pGalleryItem->GetType() ); + // insert graphic + if (nType == css::gallery::GalleryItemType::GRAPHIC) + { + Graphic aGraphic( pGalleryItem->GetGraphic() ); + + // reduce size if necessary + ScopedVclPtrInstance< Window > aWindow(GetActiveWindow()); + aWindow->SetMapMode(aGraphic.GetPrefMapMode()); + Size aSizePix = aWindow->LogicToPixel(aGraphic.GetPrefSize()); + aWindow->SetMapMode( MapMode(MapUnit::Map100thMM) ); + Size aSize = aWindow->PixelToLogic(aSizePix); + + // constrain size to page size if necessary + SdrPage* pPage = mpDrawView->GetSdrPageView()->GetPage(); + Size aPageSize = pPage->GetSize(); + aPageSize.AdjustWidth( -(pPage->GetLeftBorder() + pPage->GetRightBorder()) ); + aPageSize.AdjustHeight( -(pPage->GetUpperBorder() + pPage->GetLowerBorder()) ); + + // If the image is too large we make it fit into the page + if ( ( ( aSize.Height() > aPageSize.Height() ) || ( aSize.Width() > aPageSize.Width() ) ) && + aSize.Height() && aPageSize.Height() ) + { + float fGrfWH = static_cast<float>(aSize.Width()) / + static_cast<float>(aSize.Height()); + float fWinWH = static_cast<float>(aPageSize.Width()) / + static_cast<float>(aPageSize.Height()); + + // constrain size to page size if necessary + if ((fGrfWH != 0.F) && (fGrfWH < fWinWH)) + { + aSize.setWidth( static_cast<::tools::Long>(aPageSize.Height() * fGrfWH) ); + aSize.setHeight( aPageSize.Height() ); + } + else + { + aSize.setWidth( aPageSize.Width() ); + aSize.setHeight( static_cast<::tools::Long>(aPageSize.Width() / fGrfWH) ); + } + } + + // set output rectangle for graphic + Point aPnt ((aPageSize.Width() - aSize.Width()) / 2, + (aPageSize.Height() - aSize.Height()) / 2); + aPnt += Point(pPage->GetLeftBorder(), pPage->GetUpperBorder()); + ::tools::Rectangle aRect (aPnt, aSize); + + SdrGrafObj* pGrafObj = nullptr; + + bool bInsertNewObject = true; + + if ( mpDrawView->AreObjectsMarked() ) + { + // is there an empty graphic object? + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + + if (rMarkList.GetMarkCount() == 1) + { + SdrMark* pMark = rMarkList.GetMark(0); + SdrObject* pObj = pMark->GetMarkedSdrObj(); + + if (pObj->GetObjInventor() == SdrInventor::Default && pObj->GetObjIdentifier() == SdrObjKind::Graphic) + { + pGrafObj = static_cast<SdrGrafObj*>(pObj); + + if( pGrafObj->IsEmptyPresObj() ) + { + // the empty graphic object gets a new graphic + bInsertNewObject = false; + + SdrGrafObj* pNewGrafObj(pGrafObj->CloneSdrObject(pGrafObj->getSdrModelFromSdrObject())); + pNewGrafObj->SetEmptyPresObj(false); + pNewGrafObj->SetOutlinerParaObject(std::nullopt); + pNewGrafObj->SetGraphic(aGraphic); + + OUString aStr = mpDrawView->GetDescriptionOfMarkedObjects() + + " " + SdResId(STR_UNDO_REPLACE); + mpDrawView->BegUndo(aStr); + SdrPageView* pPV = mpDrawView->GetSdrPageView(); + mpDrawView->ReplaceObjectAtView(pGrafObj, *pPV, pNewGrafObj); + mpDrawView->EndUndo(); + } + } + } + } + + if( bInsertNewObject ) + { + pGrafObj = new SdrGrafObj( + GetView()->getSdrModelFromSdrView(), + aGraphic, + aRect); + SdrPageView* pPV = mpDrawView->GetSdrPageView(); + mpDrawView->InsertObjectAtView(pGrafObj, *pPV, SdrInsertFlags::SETDEFLAYER); + } + } + // insert sound + else if( nType == css::gallery::GalleryItemType::MEDIA ) + { + const SfxStringItem aMediaURLItem( SID_INSERT_AVMEDIA, pGalleryItem->GetURL() ); + GetViewFrame()->GetDispatcher()->ExecuteList(SID_INSERT_AVMEDIA, + SfxCallMode::SYNCHRON, { &aMediaURLItem }); + } + + GetDocSh()->SetWaitCursor( false ); +} + +/** + * Edit macros for attribute configuration + */ + +/* the work flow to adjust the attributes is nearly everywhere the same + 1. read existing attributes + 2. read parameter from the basic-set + 3. delete selected item from the attribute-set + 4. create new attribute-item + 5. insert item into set */ +void DrawViewShell::AttrExec (SfxRequest &rReq) +{ + // nothing is executed during a slide show! + if(HasCurrentFunction(SID_PRESENTATION)) + return; + + CheckLineTo (rReq); + + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + SfxItemSet aAttr( GetDoc()->GetPool() ); + + GetView()->GetAttributes( aAttr ); + const SfxItemSet* pArgs = rReq.GetArgs(); + + switch (rReq.GetSlot ()) + { + // set new fill-style + case SID_SETFILLSTYLE : + if (pArgs && pArgs->Count () == 1) + { + const SfxUInt32Item* pFillStyle = rReq.GetArg<SfxUInt32Item>(ID_VAL_STYLE); + if (CHECK_RANGE (drawing::FillStyle_NONE, static_cast<drawing::FillStyle>(pFillStyle->GetValue ()), drawing::FillStyle_BITMAP)) + { + aAttr.ClearItem (XATTR_FILLSTYLE); + XFillStyleItem aStyleItem(static_cast<drawing::FillStyle>(pFillStyle->GetValue ())); + aStyleItem.SetWhich(XATTR_FILLSTYLE); + aAttr.Put (aStyleItem); + rBindings.Invalidate (SID_ATTR_FILL_STYLE); + rBindings.Invalidate (SID_ATTR_PAGE_FILLSTYLE); + } +#if HAVE_FEATURE_SCRIPTING + else StarBASIC::FatalError (ERRCODE_BASIC_BAD_PROP_VALUE); +#endif + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + break; + + // determine new line style + case SID_SETLINESTYLE : + if (pArgs && pArgs->Count () == 1) + { + const SfxUInt32Item* pLineStyle = rReq.GetArg<SfxUInt32Item>(ID_VAL_STYLE); + if (CHECK_RANGE (sal_Int32(drawing::LineStyle_NONE), static_cast<sal_Int32>(pLineStyle->GetValue()), sal_Int32(drawing::LineStyle_DASH))) + { + aAttr.ClearItem (XATTR_LINESTYLE); + XLineStyleItem aStyleItem(static_cast<drawing::LineStyle>(pLineStyle->GetValue())); + aStyleItem.SetWhich(XATTR_LINESTYLE); + aAttr.Put(aStyleItem); + rBindings.Invalidate (SID_ATTR_LINE_STYLE); + } +#if HAVE_FEATURE_SCRIPTING + else StarBASIC::FatalError (ERRCODE_BASIC_BAD_PROP_VALUE); +#endif + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + break; + + // set line width + case SID_SETLINEWIDTH : + if (pArgs && pArgs->Count () == 1) + { + const SfxUInt32Item* pLineWidth = rReq.GetArg<SfxUInt32Item>(ID_VAL_WIDTH); + aAttr.ClearItem (XATTR_LINEWIDTH); + XLineWidthItem aWidthItem(pLineWidth->GetValue()); + aWidthItem.SetWhich(XATTR_LINEWIDTH); + aAttr.Put(aWidthItem); + rBindings.Invalidate (SID_ATTR_LINE_WIDTH); + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + break; + + case SID_SETFILLCOLOR : + if (pArgs && pArgs->Count () == 3) + { + const SfxUInt32Item* pRed = rReq.GetArg<SfxUInt32Item>(ID_VAL_RED); + const SfxUInt32Item* pGreen = rReq.GetArg<SfxUInt32Item>(ID_VAL_GREEN); + const SfxUInt32Item* pBlue = rReq.GetArg<SfxUInt32Item>(ID_VAL_BLUE); + + aAttr.ClearItem (XATTR_FILLCOLOR); + aAttr.ClearItem (XATTR_FILLSTYLE); + XFillColorItem aColorItem(-1, Color (static_cast<sal_uInt8>(pRed->GetValue ()), + static_cast<sal_uInt8>(pGreen->GetValue ()), + static_cast<sal_uInt8>(pBlue->GetValue ()))); + aColorItem.SetWhich(XATTR_FILLCOLOR); + aAttr.Put(aColorItem); + rBindings.Invalidate (SID_ATTR_FILL_COLOR); + rBindings.Invalidate (SID_ATTR_PAGE_COLOR); + rBindings.Invalidate (SID_ATTR_FILL_STYLE); + rBindings.Invalidate (SID_ATTR_PAGE_FILLSTYLE); + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + break; + + case SID_SETLINECOLOR : + if (pArgs && pArgs->Count () == 3) + { + const SfxUInt32Item* pRed = rReq.GetArg<SfxUInt32Item>(ID_VAL_RED); + const SfxUInt32Item* pGreen = rReq.GetArg<SfxUInt32Item>(ID_VAL_GREEN); + const SfxUInt32Item* pBlue = rReq.GetArg<SfxUInt32Item>(ID_VAL_BLUE); + + aAttr.ClearItem (XATTR_LINECOLOR); + XLineColorItem aColorItem(-1, Color(static_cast<sal_uInt8>(pRed->GetValue()), + static_cast<sal_uInt8>(pGreen->GetValue()), + static_cast<sal_uInt8>(pBlue->GetValue()))); + aColorItem.SetWhich(XATTR_LINECOLOR); + aAttr.Put(aColorItem); + rBindings.Invalidate (SID_ATTR_LINE_COLOR); + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + break; + + case SID_SETGRADSTARTCOLOR : + case SID_SETGRADENDCOLOR : + if (pArgs && pArgs->Count () == 4) + { + const SfxStringItem* pName = rReq.GetArg<SfxStringItem>(ID_VAL_INDEX); + const SfxUInt32Item* pRed = rReq.GetArg<SfxUInt32Item>(ID_VAL_RED); + const SfxUInt32Item* pGreen = rReq.GetArg<SfxUInt32Item>(ID_VAL_GREEN); + const SfxUInt32Item* pBlue = rReq.GetArg<SfxUInt32Item>(ID_VAL_BLUE); + + XGradientListRef pGradientList = GetDoc()->GetGradientList (); + ::tools::Long nCounts = pGradientList->Count (); + Color aColor (static_cast<sal_uInt8>(pRed->GetValue ()), + static_cast<sal_uInt8>(pGreen->GetValue ()), + static_cast<sal_uInt8>(pBlue->GetValue ())); + ::tools::Long i; + + aAttr.ClearItem (XATTR_FILLGRADIENT); + aAttr.ClearItem (XATTR_FILLSTYLE); + + for ( i = 0; i < nCounts; i ++) + { + const XGradientEntry* pEntry = pGradientList->GetGradient(i); + + if (pEntry->GetName () == pName->GetValue ()) + { + XGradient aGradient(pEntry->GetGradient()); + + if (rReq.GetSlot () == SID_SETGRADSTARTCOLOR) aGradient.SetStartColor (aColor); + else aGradient.SetEndColor (aColor); + + XFillStyleItem aStyleItem(drawing::FillStyle_GRADIENT); + aStyleItem.SetWhich(XATTR_FILLSTYLE); + aAttr.Put(aStyleItem); + XFillGradientItem aGradientItem(pName->GetValue (), aGradient); + aGradientItem.SetWhich(XATTR_FILLGRADIENT); + aAttr.Put(aGradientItem); + break; + } + } + + if (i >= nCounts) + { + Color aBlack (0, 0, 0); + XGradient aGradient ((rReq.GetSlot () == SID_SETGRADSTARTCOLOR) + ? aColor + : aBlack, + (rReq.GetSlot () == SID_SETGRADENDCOLOR) + ? aColor + : aBlack); + + GetDoc()->GetGradientList()->Insert(std::make_unique<XGradientEntry>(aGradient, pName->GetValue())); + + XFillStyleItem aStyleItem(drawing::FillStyle_GRADIENT); + aStyleItem.SetWhich(XATTR_FILLSTYLE); + aAttr.Put(aStyleItem); + XFillGradientItem aGradientItem(pName->GetValue(), aGradient); + aGradientItem.SetWhich(XATTR_FILLGRADIENT); + aAttr.Put(aGradientItem); + } + + rBindings.Invalidate (SID_ATTR_FILL_STYLE); + rBindings.Invalidate (SID_ATTR_PAGE_FILLSTYLE); + rBindings.Invalidate (SID_ATTR_FILL_GRADIENT); + rBindings.Invalidate (SID_ATTR_PAGE_GRADIENT); + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + break; + + case SID_SETHATCHCOLOR : + if (pArgs && pArgs->Count () == 4) + { + const SfxStringItem* pName = rReq.GetArg<SfxStringItem>(ID_VAL_INDEX); + const SfxUInt32Item* pRed = rReq.GetArg<SfxUInt32Item>(ID_VAL_RED); + const SfxUInt32Item* pGreen = rReq.GetArg<SfxUInt32Item>(ID_VAL_GREEN); + const SfxUInt32Item* pBlue = rReq.GetArg<SfxUInt32Item>(ID_VAL_BLUE); + + XHatchListRef pHatchList = GetDoc()->GetHatchList (); + ::tools::Long nCounts = pHatchList->Count (); + Color aColor (static_cast<sal_uInt8>(pRed->GetValue ()), + static_cast<sal_uInt8>(pGreen->GetValue ()), + static_cast<sal_uInt8>(pBlue->GetValue ())); + ::tools::Long i; + + aAttr.ClearItem (XATTR_FILLHATCH); + aAttr.ClearItem (XATTR_FILLSTYLE); + + for ( i = 0; i < nCounts; i ++) + { + const XHatchEntry* pEntry = pHatchList->GetHatch(i); + + if (pEntry->GetName () == pName->GetValue ()) + { + XHatch aHatch(pEntry->GetHatch()); + + aHatch.SetColor (aColor); + + XFillStyleItem aStyleItem(drawing::FillStyle_HATCH); + aStyleItem.SetWhich(XATTR_FILLSTYLE); + aAttr.Put(aStyleItem); + XFillHatchItem aHatchItem(pName->GetValue(), aHatch); + aHatchItem.SetWhich(XATTR_FILLHATCH); + aAttr.Put(aHatchItem); + break; + } + } + + if (i >= nCounts) + { + XHatch aHatch (aColor); + + GetDoc()->GetHatchList()->Insert(std::make_unique<XHatchEntry>(aHatch, pName->GetValue())); + + XFillStyleItem aStyleItem(drawing::FillStyle_HATCH); + aStyleItem.SetWhich(XATTR_FILLSTYLE); + aAttr.Put(aStyleItem); + XFillHatchItem aHatchItem(pName->GetValue (), aHatch); + aHatchItem.SetWhich(XATTR_FILLHATCH); + aAttr.Put(aHatchItem); + } + + rBindings.Invalidate (SID_ATTR_FILL_HATCH); + rBindings.Invalidate (SID_ATTR_PAGE_HATCH); + rBindings.Invalidate (SID_ATTR_FILL_STYLE); + rBindings.Invalidate (SID_ATTR_PAGE_FILLSTYLE); + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + break; + + // configuration for line-dash + case SID_DASH : + if (pArgs && pArgs->Count () == 7) + { + const SfxStringItem* pName = rReq.GetArg<SfxStringItem>(ID_VAL_INDEX); + const SfxUInt32Item* pStyle = rReq.GetArg<SfxUInt32Item>(ID_VAL_STYLE); + const SfxUInt32Item* pDots = rReq.GetArg<SfxUInt32Item>(ID_VAL_DOTS); + const SfxUInt32Item* pDotLen = rReq.GetArg<SfxUInt32Item>(ID_VAL_DOTLEN); + const SfxUInt32Item* pDashes = rReq.GetArg<SfxUInt32Item>(ID_VAL_DASHES); + const SfxUInt32Item* pDashLen = rReq.GetArg<SfxUInt32Item>(ID_VAL_DASHLEN); + const SfxUInt32Item* pDistance = rReq.GetArg<SfxUInt32Item>(ID_VAL_DISTANCE); + + if (CHECK_RANGE (sal_Int32(css::drawing::DashStyle_RECT), static_cast<sal_Int32>(pStyle->GetValue()), sal_Int32(css::drawing::DashStyle_ROUNDRELATIVE))) + { + XDash aNewDash (static_cast<css::drawing::DashStyle>(pStyle->GetValue ()), static_cast<short>(pDots->GetValue ()), pDotLen->GetValue (), + static_cast<short>(pDashes->GetValue ()), pDashLen->GetValue (), pDistance->GetValue ()); + + aAttr.ClearItem (XATTR_LINEDASH); + aAttr.ClearItem (XATTR_LINESTYLE); + + XDashListRef pDashList = GetDoc()->GetDashList(); + ::tools::Long nCounts = pDashList->Count (); + std::unique_ptr<XDashEntry> pEntry = std::make_unique<XDashEntry>(aNewDash, pName->GetValue()); + ::tools::Long i; + + for ( i = 0; i < nCounts; i++ ) + if (pDashList->GetDash (i)->GetName () == pName->GetValue ()) + break; + + if (i < nCounts) + pDashList->Replace(std::move(pEntry), i); + else + pDashList->Insert(std::move(pEntry)); + + XLineDashItem aDashItem(pName->GetValue(), aNewDash); + aDashItem.SetWhich(XATTR_LINEDASH); + aAttr.Put(aDashItem); + XLineStyleItem aStyleItem(drawing::LineStyle_DASH); + aStyleItem.SetWhich(XATTR_LINESTYLE); + aAttr.Put(aStyleItem); + rBindings.Invalidate (SID_ATTR_LINE_DASH); + rBindings.Invalidate (SID_ATTR_FILL_STYLE); + } +#if HAVE_FEATURE_SCRIPTING + else StarBASIC::FatalError (ERRCODE_BASIC_BAD_PROP_VALUE); +#endif + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + break; + + // configuration for gradients + case SID_GRADIENT : + if (pArgs && pArgs->Count () == 8) + { + const SfxStringItem* pName = rReq.GetArg<SfxStringItem>(ID_VAL_INDEX); + const SfxUInt32Item* pStyle = rReq.GetArg<SfxUInt32Item>(ID_VAL_STYLE); + const SfxUInt32Item* pAngle = rReq.GetArg<SfxUInt32Item>(ID_VAL_ANGLE); + const SfxUInt32Item* pBorder = rReq.GetArg<SfxUInt32Item>(ID_VAL_BORDER); + const SfxUInt32Item* pCenterX = rReq.GetArg<SfxUInt32Item>(ID_VAL_CENTER_X); + const SfxUInt32Item* pCenterY = rReq.GetArg<SfxUInt32Item>(ID_VAL_CENTER_Y); + const SfxUInt32Item* pStart = rReq.GetArg<SfxUInt32Item>(ID_VAL_STARTINTENS); + const SfxUInt32Item* pEnd = rReq.GetArg<SfxUInt32Item>(ID_VAL_ENDINTENS); + + if (CHECK_RANGE (sal_Int32(css::awt::GradientStyle_LINEAR), static_cast<sal_Int32>(pStyle->GetValue()), sal_Int32(css::awt::GradientStyle_RECT)) && + CHECK_RANGE (0, static_cast<sal_Int32>(pAngle->GetValue ()), 360) && + CHECK_RANGE (0, static_cast<sal_Int32>(pBorder->GetValue ()), 100) && + CHECK_RANGE (0, static_cast<sal_Int32>(pCenterX->GetValue ()), 100) && + CHECK_RANGE (0, static_cast<sal_Int32>(pCenterY->GetValue ()), 100) && + CHECK_RANGE (0, static_cast<sal_Int32>(pStart->GetValue ()), 100) && + CHECK_RANGE (0, static_cast<sal_Int32>(pEnd->GetValue ()), 100)) + { + aAttr.ClearItem (XATTR_FILLGRADIENT); + aAttr.ClearItem (XATTR_FILLSTYLE); + + XGradientListRef pGradientList = GetDoc()->GetGradientList (); + ::tools::Long nCounts = pGradientList->Count (); + ::tools::Long i; + + for ( i = 0; i < nCounts; i++ ) + { + const XGradientEntry* pEntry = pGradientList->GetGradient(i); + + if (pEntry->GetName () == pName->GetValue ()) + { + XGradient aGradient(pEntry->GetGradient()); + + aGradient.SetGradientStyle (static_cast<css::awt::GradientStyle>(pStyle->GetValue ())); + aGradient.SetAngle (Degree10(pAngle->GetValue () * 10)); + aGradient.SetBorder (static_cast<short>(pBorder->GetValue ())); + aGradient.SetXOffset (static_cast<short>(pCenterX->GetValue ())); + aGradient.SetYOffset (static_cast<short>(pCenterY->GetValue ())); + aGradient.SetStartIntens (static_cast<short>(pStart->GetValue ())); + aGradient.SetEndIntens (static_cast<short>(pEnd->GetValue ())); + + XFillStyleItem aStyleItem(drawing::FillStyle_GRADIENT); + aStyleItem.SetWhich(XATTR_FILLSTYLE); + aAttr.Put(aStyleItem); + XFillGradientItem aGradientItem(pName->GetValue (), aGradient); + aGradientItem.SetWhich(XATTR_FILLGRADIENT); + aAttr.Put(aGradientItem); + break; + } + } + + if (i >= nCounts) + { + Color aBlack (0, 0, 0); + XGradient aGradient (aBlack, aBlack, static_cast<css::awt::GradientStyle>(pStyle->GetValue ()), + Degree10(pAngle->GetValue () * 10), static_cast<short>(pCenterX->GetValue ()), + static_cast<short>(pCenterY->GetValue ()), static_cast<short>(pBorder->GetValue ()), + static_cast<short>(pStart->GetValue ()), static_cast<short>(pEnd->GetValue ())); + + pGradientList->Insert(std::make_unique<XGradientEntry>(aGradient, pName->GetValue())); + XFillStyleItem aStyleItem(drawing::FillStyle_GRADIENT); + aStyleItem.SetWhich(XATTR_FILLSTYLE); + aAttr.Put(aStyleItem); + XFillGradientItem aGradientItem(pName->GetValue (), aGradient); + aGradientItem.SetWhich(XATTR_FILLGRADIENT); + aAttr.Put(aGradientItem); + } + + rBindings.Invalidate (SID_ATTR_FILL_GRADIENT); + rBindings.Invalidate (SID_ATTR_PAGE_GRADIENT); + rBindings.Invalidate (SID_ATTR_FILL_STYLE); + rBindings.Invalidate (SID_ATTR_PAGE_FILLSTYLE); + } +#if HAVE_FEATURE_SCRIPTING + else StarBASIC::FatalError (ERRCODE_BASIC_BAD_PROP_VALUE); +#endif + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + break; + + // configuration for hatch + case SID_HATCH : + if (pArgs && pArgs->Count () == 4) + { + const SfxStringItem* pName = rReq.GetArg<SfxStringItem>(ID_VAL_INDEX); + const SfxUInt32Item* pStyle = rReq.GetArg<SfxUInt32Item>(ID_VAL_STYLE); + const SfxUInt32Item* pDistance = rReq.GetArg<SfxUInt32Item>(ID_VAL_DISTANCE); + const SfxUInt32Item* pAngle = rReq.GetArg<SfxUInt32Item>(ID_VAL_ANGLE); + + if (CHECK_RANGE (sal_Int32(css::drawing::HatchStyle_SINGLE), static_cast<sal_Int32>(pStyle->GetValue()), sal_Int32(css::drawing::HatchStyle_TRIPLE)) && + CHECK_RANGE (0, static_cast<sal_Int32>(pAngle->GetValue ()), 360)) + { + aAttr.ClearItem (XATTR_FILLHATCH); + aAttr.ClearItem (XATTR_FILLSTYLE); + + XHatchListRef pHatchList = GetDoc()->GetHatchList (); + ::tools::Long nCounts = pHatchList->Count (); + ::tools::Long i; + + for ( i = 0; i < nCounts; i++ ) + { + const XHatchEntry* pEntry = pHatchList->GetHatch(i); + + if (pEntry->GetName () == pName->GetValue ()) + { + XHatch aHatch(pEntry->GetHatch()); + + aHatch.SetHatchStyle (static_cast<css::drawing::HatchStyle>(pStyle->GetValue ())); + aHatch.SetDistance (pDistance->GetValue ()); + aHatch.SetAngle (Degree10(pAngle->GetValue () * 10)); + + XFillStyleItem aStyleItem(drawing::FillStyle_HATCH); + aStyleItem.SetWhich(XATTR_FILLSTYLE); + aAttr.Put(aStyleItem); + XFillHatchItem aHatchItem(pName->GetValue (), aHatch); + aHatchItem.SetWhich(XATTR_FILLHATCH); + aAttr.Put(aHatchItem); + break; + } + } + + if (i >= nCounts) + { + XHatch aHatch (Color(0), static_cast<css::drawing::HatchStyle>(pStyle->GetValue ()), pDistance->GetValue (), + Degree10(pAngle->GetValue () * 10)); + + pHatchList->Insert(std::make_unique<XHatchEntry>(aHatch, pName->GetValue())); + XFillStyleItem aStyleItem(drawing::FillStyle_HATCH); + aStyleItem.SetWhich(XATTR_FILLSTYLE); + aAttr.Put(aStyleItem); + XFillHatchItem aHatchItem(pName->GetValue (), aHatch); + aHatchItem.SetWhich(XATTR_FILLHATCH); + aAttr.Put(aHatchItem); + } + + rBindings.Invalidate (SID_ATTR_FILL_HATCH); + rBindings.Invalidate (SID_ATTR_FILL_STYLE); + } +#if HAVE_FEATURE_SCRIPTING + else StarBASIC::FatalError (ERRCODE_BASIC_BAD_PROP_VALUE); +#endif + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + break; + + case SID_SELECTGRADIENT : + if (pArgs && (pArgs->Count () == 1)) + { + const SfxStringItem* pName = rReq.GetArg<SfxStringItem>(ID_VAL_INDEX); + + XGradientListRef pGradientList = GetDoc()->GetGradientList (); + ::tools::Long nCounts = pGradientList->Count (); + + for (::tools::Long i = 0; i < nCounts; i ++) + { + const XGradientEntry* pEntry = pGradientList->GetGradient(i); + + if (pEntry->GetName () == pName->GetValue ()) + { + aAttr.ClearItem (XATTR_FILLGRADIENT); + aAttr.ClearItem (XATTR_FILLSTYLE); + XFillStyleItem aStyleItem(drawing::FillStyle_GRADIENT); + aStyleItem.SetWhich(XATTR_FILLSTYLE); + aAttr.Put(aStyleItem); + XFillGradientItem aGradientItem(pName->GetValue (), pEntry->GetGradient ()); + aGradientItem.SetWhich(XATTR_FILLGRADIENT); + aAttr.Put(aGradientItem); + rBindings.Invalidate (SID_ATTR_FILL_GRADIENT); + rBindings.Invalidate (SID_ATTR_PAGE_GRADIENT); + rBindings.Invalidate (SID_ATTR_FILL_STYLE); + rBindings.Invalidate (SID_ATTR_PAGE_FILLSTYLE); + break; + } + } + + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + break; + + case SID_SELECTHATCH : + if (pArgs && pArgs->Count () == 1) + { + const SfxStringItem* pName = rReq.GetArg<SfxStringItem>(ID_VAL_INDEX); + + XHatchListRef pHatchList = GetDoc()->GetHatchList (); + ::tools::Long nCounts = pHatchList->Count (); + + for (::tools::Long i = 0; i < nCounts; i ++) + { + const XHatchEntry* pEntry = pHatchList->GetHatch(i); + + if (pEntry->GetName () == pName->GetValue ()) + { + aAttr.ClearItem (XATTR_FILLHATCH); + aAttr.ClearItem (XATTR_FILLSTYLE); + XFillStyleItem aStyleItem(drawing::FillStyle_HATCH); + aStyleItem.SetWhich(XATTR_FILLSTYLE); + aAttr.Put(aStyleItem); + XFillHatchItem aHatchItem(pName->GetValue (), pEntry->GetHatch ()); + aHatchItem.SetWhich(XATTR_FILLHATCH); + aAttr.Put(aHatchItem); + + rBindings.Invalidate (SID_ATTR_FILL_HATCH); + rBindings.Invalidate (SID_ATTR_PAGE_HATCH); + rBindings.Invalidate (SID_ATTR_FILL_STYLE); + rBindings.Invalidate (SID_ATTR_PAGE_FILLSTYLE); + break; + } + } + + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + break; + + case SID_UNSELECT : + mpDrawView->UnmarkAll (); + break; + + case SID_GETRED : + if (pArgs && pArgs->Count () == 1) + { + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + break; + +/* case SID_SETFONTFAMILYNAME : + case SID_SETFONTSTYLENAME : + case SID_SETFONTFAMILY : + case SID_SETFONTPITCH : + case SID_SETFONTCHARSET : + case SID_SETFONTPOSTURE : + case SID_SETFONTWEIGHT : + case SID_SETFONTUNDERLINE : + case SID_SETFONTCROSSEDOUT : + case SID_SETFONTSHADOWED : + case SID_SETFONTCONTOUR : + case SID_SETFONTCOLOR : + case SID_SETFONTLANGUAGE : + case SID_SETFONTWORDLINE : + case SID_SETFONTCASEMAP : + case SID_SETFONTESCAPE : + case SID_SETFONTKERNING : + break;*/ + + default : + ; + } + + mpDrawView->SetAttributes (const_cast<const SfxItemSet &>(aAttr)); + rReq.Ignore (); +} + +/** + * Edit macros for attribute configuration + */ +void DrawViewShell::AttrState (SfxItemSet& rSet) +{ + SfxWhichIter aIter (rSet); + sal_uInt16 nWhich = aIter.FirstWhich (); + SfxItemSet aAttr( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( aAttr ); + + while (nWhich) + { + switch (nWhich) + { + case SID_GETFILLSTYLE : + { + const XFillStyleItem &rFillStyleItem = aAttr.Get (XATTR_FILLSTYLE); + + rSet.Put (SfxUInt32Item (nWhich, static_cast<::tools::Long>(rFillStyleItem.GetValue ()))); + break; + } + + case SID_GETLINESTYLE : + { + const XLineStyleItem &rLineStyleItem = aAttr.Get (XATTR_LINESTYLE); + + rSet.Put (SfxUInt32Item (nWhich, static_cast<::tools::Long>(rLineStyleItem.GetValue ()))); + break; + } + + case SID_GETLINEWIDTH : + { + const XLineWidthItem &rLineWidthItem = aAttr.Get (XATTR_LINEWIDTH); + + rSet.Put (SfxUInt32Item (nWhich, static_cast<::tools::Long>(rLineWidthItem.GetValue ()))); + break; + } + + case SID_GETGREEN : + case SID_GETRED : + case SID_GETBLUE : + { + const SfxUInt32Item &rWhatKind = static_cast<const SfxUInt32Item &>( rSet.Get (ID_VAL_WHATKIND) ); + Color aColor; + + switch (rWhatKind.GetValue ()) + { + case 1 : + { + const XLineColorItem &rLineColorItem = aAttr.Get (XATTR_LINECOLOR); + + aColor = rLineColorItem.GetColorValue (); + break; + } + + case 2 : + { + const XFillColorItem &rFillColorItem = aAttr.Get (XATTR_FILLCOLOR); + + aColor = rFillColorItem.GetColorValue (); + break; + } + + case 3 : + case 4 : + { + const XFillGradientItem &rFillGradientItem = aAttr.Get (XATTR_FILLGRADIENT); + const XGradient &rGradient = rFillGradientItem.GetGradientValue (); + + aColor = (rWhatKind.GetValue () == 3) + ? rGradient.GetStartColor () + : rGradient.GetEndColor (); + break; + } + + case 5: + { + const XFillHatchItem &rFillHatchItem = aAttr.Get (XATTR_FILLHATCH); + const XHatch &rHatch = rFillHatchItem.GetHatchValue (); + + aColor = rHatch.GetColor (); + break; + } + + default : + ; + } + + rSet.Put (SfxUInt32Item (nWhich, static_cast<::tools::Long>((nWhich == SID_GETRED) + ? aColor.GetRed () + : (nWhich == SID_GETGREEN) + ? aColor.GetGreen () + : aColor.GetBlue ()))); + break; + } + + default : + ; + } + + nWhich = aIter.NextWhich (); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviewsa.cxx b/sd/source/ui/view/drviewsa.cxx new file mode 100644 index 000000000..a61d64599 --- /dev/null +++ b/sd/source/ui/view/drviewsa.cxx @@ -0,0 +1,848 @@ +/* -*- 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 <DrawViewShell.hxx> +#include <com/sun/star/scanner/ScannerManager.hpp> +#include <cppuhelper/implbase.hxx> +#include <comphelper/processfactory.hxx> +#include <editeng/sizeitem.hxx> +#include <svx/svdlayer.hxx> +#include <sfx2/zoomitem.hxx> +#include <svx/svdpagv.hxx> +#include <svl/ptitem.hxx> +#include <svl/stritem.hxx> +#include <sfx2/request.hxx> +#include <sfx2/dispatch.hxx> +#include <svx/zoomslideritem.hxx> +#include <svl/eitem.hxx> + +#include <sdcommands.h> +#include <svx/f3dchild.hxx> +#include <svx/clipfmtitem.hxx> + +#include <sfx2/viewfrm.hxx> +#include <svtools/cliplistener.hxx> +#include <svx/float3d.hxx> +#include <svx/extedit.hxx> +#include <svx/sidebar/SelectionAnalyzer.hxx> +#include <svx/sidebar/SelectionChangeHandler.hxx> +#include <helpids.h> + +#include <view/viewoverlaymanager.hxx> +#include <app.hrc> +#include <strings.hrc> +#include <sdmod.hxx> +#include <sdpage.hxx> +#include <FrameView.hxx> +#include <drawdoc.hxx> +#include <sdresid.hxx> +#include <DrawDocShell.hxx> +#include <Window.hxx> +#include <fupoor.hxx> +#include <fusel.hxx> +#include <funavig.hxx> +#include <drawview.hxx> +#include <SdUnoDrawView.hxx> +#include <ViewShellBase.hxx> +#include <slideshow.hxx> +#include <annotationmanager.hxx> +#include <DrawController.hxx> +#include <tools/diagnose_ex.h> +#include <LayerTabBar.hxx> + +#include <memory> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using vcl::EnumContext; + +namespace sd { + +bool DrawViewShell::mbPipette = false; + +namespace { + +class ScannerEventListener : public ::cppu::WeakImplHelper< lang::XEventListener > +{ +private: + + DrawViewShell* mpParent; + +public: + + explicit ScannerEventListener( DrawViewShell* pParent ) : mpParent( pParent ) {} + + // XEventListener + virtual void SAL_CALL disposing( const lang::EventObject& rEventObject ) override; + + void ParentDestroyed() { mpParent = nullptr; } +}; + +} + +void SAL_CALL ScannerEventListener::disposing( const lang::EventObject& /*rEventObject*/ ) +{ + if( mpParent ) + mpParent->ScannerEvent(); +} + +DrawViewShell::DrawViewShell( ViewShellBase& rViewShellBase, vcl::Window* pParentWindow, PageKind ePageKind, FrameView* pFrameViewArgument ) + : ViewShell (pParentWindow, rViewShellBase) + , maTabControl(VclPtr<sd::TabControl>::Create(this, pParentWindow)) + , mbIsLayerModeActive(false) + , mbIsInSwitchPage(false) + , mpSelectionChangeHandler(new svx::sidebar::SelectionChangeHandler( + [this] () { return this->GetSidebarContextName(); }, + uno::Reference<frame::XController>(&rViewShellBase.GetDrawController()), + vcl::EnumContext::Context::Default)) + , mbMouseButtonDown(false) + , mbMouseSelecting(false) +{ + if (pFrameViewArgument != nullptr) + mpFrameView = pFrameViewArgument; + else + mpFrameView = new FrameView(GetDoc()); + Construct(GetDocSh(), ePageKind); + + mpSelectionChangeHandler->Connect(); + + SetContextName(GetSidebarContextName()); + + doShow(); + + ConfigureAppBackgroundColor(); + SD_MOD()->GetColorConfig().AddListener(this); +} + +DrawViewShell::~DrawViewShell() +{ + SD_MOD()->GetColorConfig().RemoveListener(this); + + mpSelectionChangeHandler->Disconnect(); + + mpAnnotationManager.reset(); + mpViewOverlayManager.reset(); + + OSL_ASSERT (GetViewShell()!=nullptr); + + if( mxScannerListener.is() ) + static_cast< ScannerEventListener* >( mxScannerListener.get() )->ParentDestroyed(); + + // Remove references to items within Svx3DWin + // (maybe do a listening sometime in Svx3DWin) + sal_uInt16 nId = Svx3DChildWindow::GetChildWindowId(); + SfxChildWindow* pWindow = GetViewFrame() ? GetViewFrame()->GetChildWindow(nId) : nullptr; + if(pWindow) + { + Svx3DWin* p3DWin = static_cast< Svx3DWin* > (pWindow->GetWindow()); + if(p3DWin) + p3DWin->DocumentReload(); + } + + EndListening (*GetDoc()); + EndListening (*GetDocSh()); + + if( SlideShow::IsRunning(*this) ) + StopSlideShow(); + + DisposeFunctions(); + + sal_uInt16 aPageCnt = GetDoc()->GetSdPageCount(mePageKind); + + for (sal_uInt16 i = 0; i < aPageCnt; i++) + { + SdPage* pPage = GetDoc()->GetSdPage(i, mePageKind); + + if (pPage == mpActualPage) + { + GetDoc()->SetSelected(pPage, true); + } + else + { + GetDoc()->SetSelected(pPage, false); + } + } + + if ( mxClipEvtLstnr.is() ) + { + mxClipEvtLstnr->RemoveListener( GetActiveWindow() ); + mxClipEvtLstnr->ClearCallbackLink(); // prevent callback if another thread is waiting + mxClipEvtLstnr.clear(); + } + + mpDrawView.reset(); + // Set mpView to NULL so that the destructor of the ViewShell base class + // does not access it. + mpView = nullptr; + + mpFrameView->Disconnect(); + maTabControl.disposeAndClear(); +} + +/** + * common part of both constructors + */ +void DrawViewShell::Construct(DrawDocShell* pDocSh, PageKind eInitialPageKind) +{ + mpActualPage = nullptr; + mbReadOnly = GetDocSh()->IsReadOnly(); + mxClipEvtLstnr.clear(); + mbPastePossible = false; + mbIsLayerModeActive = false; + + mpFrameView->Connect(); + + OSL_ASSERT (GetViewShell()!=nullptr); + + SetPool( &GetDoc()->GetPool() ); + + GetDoc()->CreateFirstPages(); + + mpDrawView.reset( new DrawView(pDocSh, GetActiveWindow()->GetOutDev(), this) ); + mpView = mpDrawView.get(); // Pointer of base class ViewShell + mpDrawView->SetSwapAsynchron(); // Asynchronous load of graphics + + // We do not read the page kind from the frame view anymore so we have + // to set it in order to resync frame view and this view. + mpFrameView->SetPageKind(eInitialPageKind); + mePageKind = eInitialPageKind; + meEditMode = EditMode::Page; + DocumentType eDocType = GetDoc()->GetDocumentType(); // RTTI does not work here + switch (mePageKind) + { + case PageKind::Standard: + meShellType = ST_IMPRESS; + break; + + case PageKind::Notes: + meShellType = ST_NOTES; + break; + + case PageKind::Handout: + meShellType = ST_HANDOUT; + break; + } + + Size aPageSize( GetDoc()->GetSdPage(0, mePageKind)->GetSize() ); + Point aPageOrg( aPageSize.Width(), aPageSize.Height() / 2); + Size aSize(aPageSize.Width() * 3, aPageSize.Height() * 2); + InitWindows(aPageOrg, aSize, Point(-1, -1)); + + Point aVisAreaPos; + + if ( pDocSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED ) + { + aVisAreaPos = pDocSh->GetVisArea(ASPECT_CONTENT).TopLeft(); + } + + mpDrawView->SetWorkArea(::tools::Rectangle(Point() - aVisAreaPos - aPageOrg, aSize)); + + // objects can not grow bigger than ViewSize + GetDoc()->SetMaxObjSize(aSize); + + // Split-Handler for TabControls + maTabControl->SetSplitHdl( LINK( this, DrawViewShell, TabSplitHdl ) ); + + /* In order to set the correct EditMode of the FrameView, we select another + one (small trick). */ + if (mpFrameView->GetViewShEditMode(/*mePageKind*/) == EditMode::Page) + { + meEditMode = EditMode::MasterPage; + } + else + { + meEditMode = EditMode::Page; + } + + // Use configuration of FrameView + ReadFrameViewData(mpFrameView); + + if( eDocType == DocumentType::Draw ) + { + GetActiveWindow()->SetHelpId( HID_SDGRAPHICVIEWSHELL ); + } + else + { + if (mePageKind == PageKind::Notes) + { + GetActiveWindow()->SetHelpId( CMD_SID_NOTES_MODE ); + + // AutoLayouts have to be created + GetDoc()->StopWorkStartupDelay(); + } + else if (mePageKind == PageKind::Handout) + { + GetActiveWindow()->SetHelpId( CMD_SID_HANDOUT_MASTER_MODE ); + + // AutoLayouts have to be created + GetDoc()->StopWorkStartupDelay(); + } + else + { + GetActiveWindow()->SetHelpId( HID_SDDRAWVIEWSHELL ); + } + } + + // start selection function + SfxRequest aReq(SID_OBJECT_SELECT, SfxCallMode::SLOT, GetDoc()->GetItemPool()); + FuPermanent(aReq); + mpDrawView->SetFrameDragSingles(); + + if (pDocSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED) + { + mbZoomOnPage = false; + } + else + { + mbZoomOnPage = true; + } + + mbIsRulerDrag = false; + + SetName ("DrawViewShell"); + + mnLockCount = 0; + + uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + + try + { + mxScannerManager = scanner::ScannerManager::create( xContext ); + + mxScannerListener = new ScannerEventListener( this ); + } + catch (Exception const &) + { + // Eat the exception and log it + // We can still continue if scanner manager is not available. + DBG_UNHANDLED_EXCEPTION("sd"); + } + + mpAnnotationManager.reset( new AnnotationManager( GetViewShellBase() ) ); + mpViewOverlayManager.reset( new ViewOverlayManager( GetViewShellBase() ) ); +} + +void DrawViewShell::Init (bool bIsMainViewShell) +{ + ViewShell::Init(bIsMainViewShell); + + if (!IsListening(*GetDocSh())) + StartListening (*GetDocSh()); +} + +void DrawViewShell::Shutdown() +{ + ViewShell::Shutdown(); + + if(SlideShow::IsRunning( GetViewShellBase() ) ) + { + // Turn off effects. + GetDrawView()->SetAnimationMode(SdrAnimationMode::Disable); + } +} + +css::uno::Reference<css::drawing::XDrawSubController> DrawViewShell::CreateSubController() +{ + css::uno::Reference<css::drawing::XDrawSubController> xSubController; + + if (IsMainViewShell()) + { + // Create uno sub controller for the main view shell. + xSubController.set( new SdUnoDrawView( *this, *GetView())); + } + + return xSubController; +} + +bool DrawViewShell::RelocateToParentWindow (vcl::Window* pParentWindow) +{ + // DrawViewShells can not be relocated to a new parent window at the + // moment, so return <FALSE/> except when the given parent window is the + // parent window that is already in use. + return pParentWindow==GetParentWindow(); +} + +/** + * check if we have to draw a polyline + */ + +/* + Polylines are represented by macros as a sequence of: + MoveTo (x, y) + LineTo (x, y) [or BezierTo (x, y)] + LineTo (x, y) + : + There is no end command for polylines. Therefore, we have to test all + commands in the requests for LineTo (BezierTo) and we have to gather + the point-parameter. The first not-LineTo leads to the creation of the + polyline from the gathered points. +*/ + +void DrawViewShell::CheckLineTo(SfxRequest& rReq) +{ +#ifdef DBG_UTIL + if(rReq.IsAPI()) + { + if(SID_LINETO == rReq.GetSlot() || SID_BEZIERTO == rReq.GetSlot() || SID_MOVETO == rReq.GetSlot() ) + { + OSL_FAIL("DrawViewShell::CheckLineTo: slots SID_LINETO, SID_BEZIERTO, SID_MOVETO no longer supported."); + } + } +#endif + + rReq.Ignore (); +} + +/** + * Change page parameter if SID_PAGESIZE or SID_PAGEMARGIN + */ +void DrawViewShell::SetupPage (Size const &rSize, + ::tools::Long nLeft, + ::tools::Long nRight, + ::tools::Long nUpper, + ::tools::Long nLower, + bool bSize, + bool bMargin, + bool bScaleAll) +{ + sal_uInt16 nPageCnt = GetDoc()->GetMasterSdPageCount(mePageKind); + sal_uInt16 i; + + for (i = 0; i < nPageCnt; i++) + { + // first, handle all master pages + SdPage *pPage = GetDoc()->GetMasterSdPage(i, mePageKind); + + if( pPage ) + { + if( bSize ) + { + ::tools::Rectangle aBorderRect(nLeft, nUpper, nRight, nLower); + pPage->ScaleObjects(rSize, aBorderRect, bScaleAll); + pPage->SetSize(rSize); + + } + if( bMargin ) + { + pPage->SetLeftBorder(nLeft); + pPage->SetRightBorder(nRight); + pPage->SetUpperBorder(nUpper); + pPage->SetLowerBorder(nLower); + } + + if ( mePageKind == PageKind::Standard ) + { + GetDoc()->GetMasterSdPage(i, PageKind::Notes)->CreateTitleAndLayout(); + } + + pPage->CreateTitleAndLayout(); + } + } + + nPageCnt = GetDoc()->GetSdPageCount(mePageKind); + + for (i = 0; i < nPageCnt; i++) + { + // then, handle all pages + SdPage *pPage = GetDoc()->GetSdPage(i, mePageKind); + + if( pPage ) + { + if( bSize ) + { + ::tools::Rectangle aBorderRect(nLeft, nUpper, nRight, nLower); + pPage->ScaleObjects(rSize, aBorderRect, bScaleAll); + pPage->SetSize(rSize); + } + if( bMargin ) + { + pPage->SetLeftBorder(nLeft); + pPage->SetRightBorder(nRight); + pPage->SetUpperBorder(nUpper); + pPage->SetLowerBorder(nLower); + } + + if ( mePageKind == PageKind::Standard ) + { + SdPage* pNotesPage = GetDoc()->GetSdPage(i, PageKind::Notes); + pNotesPage->SetAutoLayout( pNotesPage->GetAutoLayout() ); + } + + pPage->SetAutoLayout( pPage->GetAutoLayout() ); + } + } + + if ( mePageKind == PageKind::Standard ) + { + SdPage* pHandoutPage = GetDoc()->GetSdPage(0, PageKind::Handout); + pHandoutPage->CreateTitleAndLayout(true); + } + + ::tools::Long nWidth = mpActualPage->GetSize().Width(); + ::tools::Long nHeight = mpActualPage->GetSize().Height(); + + Point aPageOrg(nWidth, nHeight / 2); + Size aSize( nWidth * 3, nHeight * 2); + + InitWindows(aPageOrg, aSize, Point(-1, -1), true); + + Point aVisAreaPos; + + if ( GetDocSh()->GetCreateMode() == SfxObjectCreateMode::EMBEDDED ) + { + aVisAreaPos = GetDocSh()->GetVisArea(ASPECT_CONTENT).TopLeft(); + } + + GetView()->SetWorkArea(::tools::Rectangle(Point() - aVisAreaPos - aPageOrg, aSize)); + + UpdateScrollBars(); + + Point aNewOrigin(mpActualPage->GetLeftBorder(), mpActualPage->GetUpperBorder()); + GetView()->GetSdrPageView()->SetPageOrigin(aNewOrigin); + + GetViewFrame()->GetBindings().Invalidate(SID_RULER_NULL_OFFSET); + + // zoom onto (new) page size + GetViewFrame()->GetDispatcher()->Execute(SID_SIZE_PAGE, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); +} + +void DrawViewShell::GetStatusBarState(SfxItemSet& rSet) +{ + /* Zoom-Item + Here we should propagate the corresponding value (Optimal ?, page width + or page) with the help of the ZoomItems !!! */ + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_ATTR_ZOOM ) ) + { + if (GetDocSh()->IsUIActive() || SlideShow::IsRunning(GetViewShellBase()) + || !GetActiveWindow()) + { + rSet.DisableItem( SID_ATTR_ZOOM ); + } + else + { + std::unique_ptr<SvxZoomItem> pZoomItem; + sal_uInt16 nZoom = static_cast<sal_uInt16>(GetActiveWindow()->GetZoom()); + + if( mbZoomOnPage ) + pZoomItem.reset(new SvxZoomItem( SvxZoomType::WHOLEPAGE, nZoom )); + else + pZoomItem.reset(new SvxZoomItem( SvxZoomType::PERCENT, nZoom )); + + // constrain area + SvxZoomEnableFlags nZoomValues = SvxZoomEnableFlags::ALL; + SdrPageView* pPageView = mpDrawView->GetSdrPageView(); + + if( pPageView && pPageView->GetObjList()->GetObjCount() == 0 ) + { + nZoomValues &= ~SvxZoomEnableFlags::OPTIMAL; + } + + pZoomItem->SetValueSet( nZoomValues ); + rSet.Put( std::move(pZoomItem) ); + } + } + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_ATTR_ZOOMSLIDER ) ) + { + rtl::Reference< sd::SlideShow > xSlideshow( SlideShow::GetSlideShow( GetDoc() ) ); + if (GetDocSh()->IsUIActive() || (xSlideshow.is() && xSlideshow->isRunning()) || !GetActiveWindow() ) + { + rSet.DisableItem( SID_ATTR_ZOOMSLIDER ); + } + else + { + sd::Window * pActiveWindow = GetActiveWindow(); + SvxZoomSliderItem aZoomItem( static_cast<sal_uInt16>(pActiveWindow->GetZoom()), static_cast<sal_uInt16>(pActiveWindow->GetMinZoom()), static_cast<sal_uInt16>(pActiveWindow->GetMaxZoom()) ) ; + + SdrPageView* pPageView = mpDrawView->GetSdrPageView(); + if( pPageView ) + { + Point aPagePos(0, 0); + Size aPageSize = pPageView->GetPage()->GetSize(); + + aPagePos.AdjustX(aPageSize.Width() / 2 ); + aPageSize.setWidth( static_cast<::tools::Long>(aPageSize.Width() * 1.03) ); + + aPagePos.AdjustY(aPageSize.Height() / 2 ); + aPageSize.setHeight( static_cast<::tools::Long>(aPageSize.Height() * 1.03) ); + aPagePos.AdjustY( -(aPageSize.Height() / 2) ); + + aPagePos.AdjustX( -(aPageSize.Width() / 2) ); + + ::tools::Rectangle aFullPageZoomRect( aPagePos, aPageSize ); + aZoomItem.AddSnappingPoint( pActiveWindow->GetZoomForRect( aFullPageZoomRect ) ); + } + aZoomItem.AddSnappingPoint(100); + rSet.Put( aZoomItem ); + } + } + + SdrPageView* pPageView = mpDrawView->GetSdrPageView(); + if (pPageView) + { + Point aPos = GetActiveWindow()->PixelToLogic(maMousePos); + pPageView->LogicToPagePos(aPos); + Fraction aUIScale(GetDoc()->GetUIScale()); + aPos.setX( ::tools::Long(aPos.X() / aUIScale) ); + aPos.setY( ::tools::Long(aPos.Y() / aUIScale) ); + + // position- and size items + if ( mpDrawView->IsAction() ) + { + ::tools::Rectangle aRect; + mpDrawView->TakeActionRect( aRect ); + + if ( aRect.IsEmpty() ) + rSet.Put( SfxPointItem(SID_ATTR_POSITION, aPos) ); + else + { + pPageView->LogicToPagePos(aRect); + aPos = aRect.TopLeft(); + aPos.setX( ::tools::Long(aPos.X() / aUIScale) ); + aPos.setY( ::tools::Long(aPos.Y() / aUIScale) ); + rSet.Put( SfxPointItem( SID_ATTR_POSITION, aPos) ); + Size aSize( aRect.Right() - aRect.Left(), aRect.Bottom() - aRect.Top() ); + aSize.setHeight( ::tools::Long(aSize.Height() / aUIScale) ); + aSize.setWidth( ::tools::Long(aSize.Width() / aUIScale) ); + rSet.Put( SvxSizeItem( SID_ATTR_SIZE, aSize) ); + } + } + else + { + if ( mpDrawView->AreObjectsMarked() ) + { + ::tools::Rectangle aRect = mpDrawView->GetAllMarkedRect(); + pPageView->LogicToPagePos(aRect); + + // Show the position of the selected shape(s) + Point aShapePosition (aRect.TopLeft()); + aShapePosition.setX( ::tools::Long(aShapePosition.X() / aUIScale) ); + aShapePosition.setY( ::tools::Long(aShapePosition.Y() / aUIScale) ); + rSet.Put (SfxPointItem(SID_ATTR_POSITION, aShapePosition)); + + Size aSize( aRect.Right() - aRect.Left(), aRect.Bottom() - aRect.Top() ); + aSize.setHeight( ::tools::Long(aSize.Height() / aUIScale) ); + aSize.setWidth( ::tools::Long(aSize.Width() / aUIScale) ); + rSet.Put( SvxSizeItem( SID_ATTR_SIZE, aSize) ); + } + else + { + rSet.Put( SfxPointItem(SID_ATTR_POSITION, aPos) ); + rSet.Put( SvxSizeItem( SID_ATTR_SIZE, Size( 0, 0 ) ) ); + } + } + } + + // Display of current page and layer. + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_STATUS_PAGE ) ) + { + sal_Int32 nPageCount = sal_Int32(GetDoc()->GetSdPageCount(mePageKind)); + sal_Int32 nActivePageCount = sal_Int32(GetDoc()->GetActiveSdPageCount()); + // Always show the slide/page number. + OUString aOUString = (nPageCount == nActivePageCount) ? SdResId(STR_SD_PAGE_COUNT) : SdResId(STR_SD_PAGE_COUNT_CUSTOM); + + aOUString = aOUString.replaceFirst("%1", OUString::number(maTabControl->GetCurPagePos() + 1)); + aOUString = aOUString.replaceFirst("%2", OUString::number(nPageCount)); + if(nPageCount != nActivePageCount) + aOUString = aOUString.replaceFirst("%3", OUString::number(nActivePageCount)); + + // If in layer mode additionally show the layer that contains all + // selected shapes of the page. If the shapes are distributed on + // more than one layer, no layer name is shown. + if (IsLayerModeActive()) + { + SdrLayerAdmin& rLayerAdmin = GetDoc()->GetLayerAdmin(); + SdrLayerID nLayer(0), nOldLayer(0); + SdrObject* pObj = nullptr; + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + const size_t nMarkCount = rMarkList.GetMarkCount(); + bool bOneLayer = true; + + // Use the first ten selected shapes as a (hopefully + // representative) sample of all shapes of the current page. + // Detect whether they belong to the same layer. + for( size_t j = 0; j < nMarkCount && bOneLayer && j < 10; ++j ) + { + pObj = rMarkList.GetMark( j )->GetMarkedSdrObj(); + if( pObj ) + { + nLayer = pObj->GetLayer(); + + if( j != 0 && nLayer != nOldLayer ) + bOneLayer = false; + + nOldLayer = nLayer; + } + } + + // Append the layer name to the current page number. + if( bOneLayer && nMarkCount ) + { + SdrLayer* pLayer = rLayerAdmin.GetLayerPerID( nLayer ); + if( pLayer ) + { + aOUString += " (" + LayerTabBar::convertToLocalizedName(pLayer->GetName()) + ")"; + } + } + } + + rSet.Put (SfxStringItem (SID_STATUS_PAGE, aOUString)); + } + // Layout + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_STATUS_LAYOUT ) ) + { + OUString aString = mpActualPage->GetLayoutName(); + sal_Int32 nPos = aString.indexOf(SD_LT_SEPARATOR); + if (nPos != -1) + aString = aString.copy(0, nPos); + rSet.Put( SfxStringItem( SID_STATUS_LAYOUT, aString ) ); + } + // Scale + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_SCALE ) ) + { + const Fraction& aUIScale = GetDoc()->GetUIScale(); + OUString aString = OUString::number(aUIScale.GetNumerator()) + + ":" + OUString::number(aUIScale.GetDenominator()); + rSet.Put( SfxStringItem( SID_SCALE, aString ) ); + } +} + +void DrawViewShell::Notify (SfxBroadcaster&, const SfxHint& rHint) +{ + if (rHint.GetId()!=SfxHintId::ModeChanged) + return; + + // Change to selection when turning on read-only mode. + if(GetDocSh()->IsReadOnly() && dynamic_cast< FuSelection* >( GetCurrentFunction().get() ) ) + { + SfxRequest aReq(SID_OBJECT_SELECT, SfxCallMode::SLOT, GetDoc()->GetItemPool()); + FuPermanent(aReq); + } + + // Turn on design mode when document is not read-only. + if (GetDocSh()->IsReadOnly() != mbReadOnly ) + { + mbReadOnly = GetDocSh()->IsReadOnly(); + + SfxBoolItem aItem( SID_FM_DESIGN_MODE, !mbReadOnly ); + GetViewFrame()->GetDispatcher()->ExecuteList(SID_FM_DESIGN_MODE, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, { &aItem }); + } + +} + +void DrawViewShell::ExecuteAnnotation (SfxRequest const & rRequest) +{ + if (mpAnnotationManager) + mpAnnotationManager->ExecuteAnnotation( rRequest ); +} + +void DrawViewShell::GetAnnotationState (SfxItemSet& rItemSet ) +{ + if (mpAnnotationManager) + mpAnnotationManager->GetAnnotationState( rItemSet ); +} + +OUString const & DrawViewShell::GetSidebarContextName() const +{ + svx::sidebar::SelectionAnalyzer::ViewType eViewType (svx::sidebar::SelectionAnalyzer::ViewType::Standard); + switch (mePageKind) + { + case PageKind::Handout: + eViewType = svx::sidebar::SelectionAnalyzer::ViewType::Handout; + break; + case PageKind::Notes: + eViewType = svx::sidebar::SelectionAnalyzer::ViewType::Notes; + break; + case PageKind::Standard: + if (meEditMode == EditMode::MasterPage) + eViewType = svx::sidebar::SelectionAnalyzer::ViewType::Master; + else + eViewType = svx::sidebar::SelectionAnalyzer::ViewType::Standard; + break; + } + return EnumContext::GetContextName( + svx::sidebar::SelectionAnalyzer::GetContextForSelection_SD( + mpDrawView->GetMarkedObjectList(), + eViewType)); +} + +void DrawViewShell::ExecGoToNextPage (SfxRequest& rReq) +{ + SetCurrentFunction( FuNavigation::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq) ); + Cancel(); +} + +void DrawViewShell::GetStateGoToNextPage (SfxItemSet& rSet) +{ + SdPage* pPage = GetActualPage(); + sal_uInt16 nSdPage = (pPage->GetPageNum() - 1) / 2; + sal_uInt16 totalPages = GetDoc()->GetSdPageCount(pPage->GetPageKind()); + if (nSdPage + 1 >= totalPages) + rSet.DisableItem( SID_GO_TO_NEXT_PAGE ); +} + +void DrawViewShell::ExecGoToPreviousPage (SfxRequest& rReq) +{ + SetCurrentFunction( FuNavigation::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq) ); + Cancel(); +} + +void DrawViewShell::GetStateGoToPreviousPage (SfxItemSet& rSet) +{ + SdPage* pPage = GetActualPage(); + sal_uInt16 nSdPage = (pPage->GetPageNum() - 1) / 2; + if (nSdPage == 0) + rSet.DisableItem( SID_GO_TO_PREVIOUS_PAGE ); +} + + +void DrawViewShell::ExecGoToFirstPage (SfxRequest& rReq) +{ + SetCurrentFunction( FuNavigation::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq) ); + Cancel(); +} + +void DrawViewShell::GetStateGoToFirstPage (SfxItemSet& rSet) +{ + SdPage* pPage = GetActualPage(); + sal_uInt16 nSdPage = (pPage->GetPageNum() - 1) / 2; + if (nSdPage == 0) + rSet.DisableItem( SID_GO_TO_FIRST_PAGE ); +} + +void DrawViewShell::ExecGoToLastPage (SfxRequest& rReq) +{ + SetCurrentFunction( FuNavigation::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq) ); + Cancel(); +} + +void DrawViewShell::GetStateGoToLastPage (SfxItemSet& rSet) +{ + SdPage* pPage = GetActualPage(); + sal_uInt16 nSdPage = (pPage->GetPageNum() - 1) / 2; + sal_uInt16 totalPages = GetDoc()->GetSdPageCount(pPage->GetPageKind()); + if (nSdPage + 1 >= totalPages) + rSet.DisableItem( SID_GO_TO_LAST_PAGE ); +} + + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviewsb.cxx b/sd/source/ui/view/drviewsb.cxx new file mode 100644 index 000000000..6f6bba855 --- /dev/null +++ b/sd/source/ui/view/drviewsb.cxx @@ -0,0 +1,205 @@ +/* -*- 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 <svx/svdlayer.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/dispatch.hxx> +#include <svx/fmshell.hxx> +#include <svx/svxdlg.hxx> +#include <osl/diagnose.h> + +#include <app.hrc> + +#include <drawdoc.hxx> +#include <DrawDocShell.hxx> +#include <unokywds.hxx> +#include <sdpage.hxx> +#include <DrawViewShell.hxx> +#include <drawview.hxx> +#include <unmodpg.hxx> +#include <ViewShellBase.hxx> +#include <FormShellManager.hxx> +#include <LayerTabBar.hxx> +#include <SlideSorterViewShell.hxx> +#include <SlideSorter.hxx> +#include <controller/SlideSorterController.hxx> + +namespace sd { + +bool DrawViewShell::RenameSlide( sal_uInt16 nPageId, const OUString & rName ) +{ + bool bOutDummy; + if( GetDoc()->GetPageByName( rName, bOutDummy ) != SDRPAGE_NOTFOUND ) + return false; + + SdPage* pPageToRename = nullptr; + PageKind ePageKind = GetPageKind(); + + if( GetEditMode() == EditMode::Page ) + { + pPageToRename = GetDoc()->GetSdPage( maTabControl->GetPagePos(nPageId), ePageKind ); + + // Undo + SdPage* pUndoPage = pPageToRename; + SdrLayerAdmin & rLayerAdmin = GetDoc()->GetLayerAdmin(); + SdrLayerID nBackground = rLayerAdmin.GetLayerID(sUNO_LayerName_background); + SdrLayerID nBgObj = rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects); + SdrLayerIDSet aVisibleLayers = mpActualPage->TRG_GetMasterPageVisibleLayers(); + + SfxUndoManager* pManager = GetDoc()->GetDocSh()->GetUndoManager(); + pManager->AddUndoAction( + std::make_unique<ModifyPageUndoAction>( + GetDoc(), pUndoPage, rName, pUndoPage->GetAutoLayout(), + aVisibleLayers.IsSet( nBackground ), + aVisibleLayers.IsSet( nBgObj ))); + + // rename + pPageToRename->SetName( rName ); + + if( ePageKind == PageKind::Standard ) + { + // also rename notes-page + SdPage* pNotesPage = GetDoc()->GetSdPage( maTabControl->GetPagePos(nPageId), PageKind::Notes ); + pNotesPage->SetName( rName ); + } + } + else + { + // rename MasterPage -> rename LayoutTemplate + pPageToRename = GetDoc()->GetMasterSdPage( maTabControl->GetPagePos(nPageId), ePageKind ); + GetDoc()->RenameLayoutTemplate( pPageToRename->GetLayoutName(), rName ); + } + + bool bSuccess = (rName == pPageToRename->GetName()); + + if( bSuccess ) + { + // user edited page names may be changed by the page so update control + maTabControl->SetPageText( nPageId, rName ); + + // set document to modified state + GetDoc()->SetChanged(); + + // inform navigator about change + if (GetViewFrame()) + { + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate(SID_NAVIGATOR_STATE, true); + } + + // Tell the slide sorter about the name change (necessary for + // accessibility.) + slidesorter::SlideSorterViewShell* pSlideSorterViewShell + = slidesorter::SlideSorterViewShell::GetSlideSorter(GetViewShellBase()); + if (pSlideSorterViewShell != nullptr) + { + pSlideSorterViewShell->GetSlideSorter().GetController().PageNameHasChanged( + maTabControl->GetPagePos(nPageId), rName); + } + } + + return bSuccess; +} + +IMPL_LINK( DrawViewShell, RenameSlideHdl, AbstractSvxNameDialog&, rDialog, bool ) +{ + OUString aNewName; + rDialog.GetName( aNewName ); + + SdPage* pCurrentPage = GetDoc()->GetSdPage( maTabControl->GetCurPagePos(), GetPageKind() ); + + return pCurrentPage && ( aNewName == pCurrentPage->GetName() || GetDocSh()->IsNewPageNameValid( aNewName ) ); +} + +void DrawViewShell::ModifyLayer ( + SdrLayer* pLayer, + const OUString& rLayerName, + const OUString& rLayerTitle, + const OUString& rLayerDesc, + bool bIsVisible, + bool bIsLocked, + bool bIsPrintable) +{ + if(!GetLayerTabControl()) // #i87182# + { + OSL_ENSURE(false, "No LayerTabBar (!)"); + return; + } + + if( !pLayer ) + return; + + const sal_uInt16 nPageCount = GetLayerTabControl()->GetPageCount(); + sal_uInt16 nCurPage = 0; + sal_uInt16 nPos; + for( nPos = 0; nPos < nPageCount; nPos++ ) + { + sal_uInt16 nId = GetLayerTabControl()->GetPageId( nPos ); + if (GetLayerTabControl()->GetLayerName(nId) == pLayer->GetName()) + { + nCurPage = nId; + break; + } + } + + pLayer->SetName( rLayerName ); + pLayer->SetTitle( rLayerTitle ); + pLayer->SetDescription( rLayerDesc ); + mpDrawView->SetLayerVisible( rLayerName, bIsVisible ); + mpDrawView->SetLayerLocked( rLayerName, bIsLocked); + mpDrawView->SetLayerPrintable(rLayerName, bIsPrintable); + + GetDoc()->SetChanged(); + + GetLayerTabControl()->SetPageText(nCurPage, rLayerName); + + // Set page bits for modified tab name display + + TabBarPageBits nBits = TabBarPageBits::NONE; + + if (!bIsVisible) + { + nBits = TabBarPageBits::Blue; + } + if (bIsLocked) + { + nBits |= TabBarPageBits::Italic; + } + if (!bIsPrintable) + { + nBits |= TabBarPageBits::Underline; + } + + // Save the bits + + GetLayerTabControl()->SetPageBits(nCurPage, nBits); + + GetViewFrame()->GetDispatcher()->Execute( + SID_SWITCHLAYER, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + + // Call Invalidate at the form shell. + FmFormShell* pFormShell = GetViewShellBase().GetFormShellManager()->GetFormShell(); + if (pFormShell != nullptr) + pFormShell->Invalidate(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviewsc.cxx b/sd/source/ui/view/drviewsc.cxx new file mode 100644 index 000000000..6be86e63c --- /dev/null +++ b/sd/source/ui/view/drviewsc.cxx @@ -0,0 +1,72 @@ +/* -*- 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 <DrawViewShell.hxx> + +#include <svx/imapdlg.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdograf.hxx> +#include <svx/svxdlg.hxx> +#include <svx/ImageMapInfo.hxx> + +#include <sfx2/viewfrm.hxx> + +#include <drawdoc.hxx> +#include <drawview.hxx> +#include <memory> + +namespace sd { + +void DrawViewShell::UpdateIMapDlg( SdrObject* pObj ) +{ + if( ( dynamic_cast< SdrGrafObj *>( pObj ) == nullptr && dynamic_cast< SdrOle2Obj *>( pObj ) == nullptr ) + || mpDrawView->IsTextEdit() + || !GetViewFrame()->HasChildWindow( SvxIMapDlgChildWindow::GetChildWindowId() ) ) + return; + + Graphic aGraphic; + ImageMap* pIMap = nullptr; + std::unique_ptr<TargetList> pTargetList; + SvxIMapInfo* pIMapInfo = SvxIMapInfo::GetIMapInfo( pObj ); + + // get graphic from shape + SdrGrafObj* pGrafObj = dynamic_cast< SdrGrafObj* >( pObj ); + if( pGrafObj ) + aGraphic = pGrafObj->GetGraphic(); + + if ( pIMapInfo ) + { + pIMap = const_cast<ImageMap*>(&pIMapInfo->GetImageMap()); + pTargetList.reset(new TargetList); + SfxViewFrame::GetTargetList( *pTargetList ); + } + + SvxIMapDlgChildWindow::UpdateIMapDlg( aGraphic, pIMap, pTargetList.get(), pObj ); +} + +IMPL_LINK( DrawViewShell, NameObjectHdl, AbstractSvxObjectNameDialog&, rDialog, bool ) +{ + OUString aName; + rDialog.GetName( aName ); + return aName.isEmpty() || ( GetDoc() && !GetDoc()->GetObj( aName ) ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviewsd.cxx b/sd/source/ui/view/drviewsd.cxx new file mode 100644 index 000000000..31fe06dde --- /dev/null +++ b/sd/source/ui/view/drviewsd.cxx @@ -0,0 +1,193 @@ +/* -*- 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 <DrawViewShell.hxx> + +#include <svx/svxids.hrc> +#include <svl/stritem.hxx> +#include <sfx2/childwin.hxx> +#include <sfx2/docfile.hxx> +#include <svl/intitem.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/request.hxx> + +#include <sfx2/viewfrm.hxx> + +#include <app.hrc> +#include <sdpage.hxx> +#include <drawdoc.hxx> +#include <DrawDocShell.hxx> +#include <pgjump.hxx> +#include <navigatr.hxx> +#include <drawview.hxx> + +namespace sd { + +/** + * handle SfxRequests for navigator + */ +void DrawViewShell::ExecNavigatorWin( SfxRequest& rReq ) +{ + CheckLineTo (rReq); + + sal_uInt16 nSId = rReq.GetSlot(); + + switch( nSId ) + { + case SID_NAVIGATOR_INIT: + { + SfxChildWindow* pWindow = GetViewFrame()->GetChildWindow( SID_NAVIGATOR ); + if( pWindow ) + { + SdNavigatorFloat* pNavWin = static_cast<SdNavigatorFloat*>(pWindow->GetWindow()); + if( pNavWin ) + pNavWin->InitTreeLB( GetDoc() ); + } + } + break; + + case SID_NAVIGATOR_PAGE: + case SID_NAVIGATOR_OBJECT: + { + if (nSId == SID_NAVIGATOR_PAGE) + { + if ( mpDrawView->IsTextEdit() ) + mpDrawView->SdrEndTextEdit(); + + const SfxItemSet* pArgs = rReq.GetArgs(); + PageJump eJump = static_cast<PageJump>(static_cast<const SfxUInt16Item&>( pArgs-> + Get(SID_NAVIGATOR_PAGE)).GetValue()); + + switch (eJump) + { + case PAGE_FIRST: + { + // jump to first page + SwitchPage(0); + } + break; + + case PAGE_LAST: + { + // jump to last page + SwitchPage(GetDoc()->GetSdPageCount(mpActualPage->GetPageKind()) - 1); + } + break; + + case PAGE_NEXT: + { + // jump to next page + sal_uInt16 nSdPage = (mpActualPage->GetPageNum() - 1) / 2; + + if (nSdPage < GetDoc()->GetSdPageCount(mpActualPage->GetPageKind()) - 1) + { + SwitchPage(nSdPage + 1); + } + } + break; + + case PAGE_PREVIOUS: + { + // jump to previous page + sal_uInt16 nSdPage = (mpActualPage->GetPageNum() - 1) / 2; + + if (nSdPage > 0) + { + SwitchPage(nSdPage - 1); + } + } + break; + + case PAGE_NONE: + break; + } + } + else if (nSId == SID_NAVIGATOR_OBJECT) + { + OUString aBookmarkStr("#"); + const SfxItemSet* pArgs = rReq.GetArgs(); + OUString aTarget = static_cast<const SfxStringItem&>( pArgs-> + Get(SID_NAVIGATOR_OBJECT)).GetValue(); + aBookmarkStr += aTarget; + SfxStringItem aStrItem(SID_FILE_NAME, aBookmarkStr); + SfxStringItem aReferer(SID_REFERER, GetDocSh()->GetMedium()->GetName()); + SfxViewFrame* pFrame = GetViewFrame(); + SfxFrameItem aFrameItem(SID_DOCFRAME, pFrame); + SfxBoolItem aBrowseItem(SID_BROWSE, true); + pFrame->GetDispatcher()-> + ExecuteList(SID_OPENDOC, SfxCallMode::SYNCHRON | SfxCallMode::RECORD, + { &aStrItem, &aFrameItem, &aBrowseItem, &aReferer }); + } + + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_NAVIGATOR_STATE ); + rBindings.Invalidate( SID_NAVIGATOR_PAGENAME ); + } + break; + + default: + break; + } +} + +void DrawViewShell::GetNavigatorWinState( SfxItemSet& rSet ) +{ + NavState nState = NavState::NONE; + sal_uInt16 nCurrentPage = 0; + sal_uInt16 nLastPage; + OUString aPageName; + + nState |= NavState::TableUpdate; + + if (mpActualPage != nullptr) + { + nCurrentPage = ( mpActualPage->GetPageNum() - 1 ) / 2; + aPageName = mpActualPage->GetName(); + } + nLastPage = GetDoc()->GetSdPageCount( mePageKind ) - 1; + + + // first page / previous page + if( nCurrentPage == 0 ) + { + nState |= NavState::BtnFirstDisabled | NavState::BtnPrevDisabled; + } + else + { + nState |= NavState::BtnFirstEnabled | NavState::BtnPrevEnabled; + } + + // last page / next page + if( nCurrentPage == nLastPage ) + { + nState |= NavState::BtnLastDisabled | NavState::BtnNextDisabled; + } + else + { + nState |= NavState::BtnLastEnabled | NavState::BtnNextEnabled; + } + + rSet.Put( SfxUInt32Item( SID_NAVIGATOR_STATE, static_cast<sal_uInt32>(nState) ) ); + rSet.Put( SfxStringItem( SID_NAVIGATOR_PAGENAME, aPageName ) ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviewse.cxx b/sd/source/ui/view/drviewse.cxx new file mode 100644 index 000000000..309eb2b85 --- /dev/null +++ b/sd/source/ui/view/drviewse.cxx @@ -0,0 +1,1701 @@ +/* -*- 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 <config_features.h> + +#include <com/sun/star/presentation/XPresentation2.hpp> +#include <com/sun/star/form/FormButtonType.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <i18nutil/unicode.hxx> +#include <i18nutil/transliteration.hxx> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/uno/Any.hxx> + +#include <LibreOfficeKit/LibreOfficeKitEnums.h> +#include <comphelper/lok.hxx> +#include <comphelper/propertyvalue.hxx> +#include <editeng/editstat.hxx> +#include <editeng/outlobj.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <svl/urlbmk.hxx> +#include <svx/clipfmtitem.hxx> +#include <svx/svdpagv.hxx> +#include <svx/svdopath.hxx> +#include <svx/svdundo.hxx> +#include <svx/svdorect.hxx> +#include <svl/eitem.hxx> +#include <svl/intitem.hxx> +#include <svl/poolitem.hxx> +#include <svl/stritem.hxx> +#include <editeng/eeitem.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/request.hxx> +#include <svx/svxids.hrc> +#include <editeng/flditem.hxx> +#include <svx/obj3d.hxx> +#include <svx/svdobjkind.hxx> +#include <svx/svdouno.hxx> +#include <svx/dataaccessdescriptor.hxx> +#include <tools/urlobj.hxx> +#include <sfx2/ipclient.hxx> +#include <avmedia/mediawindow.hxx> +#include <svl/urihelper.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/notebookbar/SfxNotebookBar.hxx> +#include <osl/diagnose.h> + +#include <DrawViewShell.hxx> +#include <slideshow.hxx> +#include <ViewShellHint.hxx> +#include <framework/FrameworkHelper.hxx> +#include <app.hrc> +#include <strings.hrc> + +#include <drawdoc.hxx> +#include <fusel.hxx> +#include <futext.hxx> +#include <fuconrec.hxx> +#include <fuconcs.hxx> +#include <fuconuno.hxx> +#include <fuconbez.hxx> +#include <fuediglu.hxx> +#include <fuconarc.hxx> +#include <fucon3d.hxx> +#include <sdresid.hxx> +#include <unokywds.hxx> +#include <Outliner.hxx> +#include <sdpage.hxx> +#include <FrameView.hxx> +#include <zoomlist.hxx> +#include <drawview.hxx> +#include <DrawDocShell.hxx> +#include <ViewShellBase.hxx> +#include <ToolBarManager.hxx> +#include <anminfo.hxx> +#include <optsitem.hxx> +#include <Window.hxx> +#include <fuformatpaintbrush.hxx> +#include <fuzoom.hxx> +#include <sdmod.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::presentation; +using namespace ::com::sun::star::beans; + +namespace sd { + +// Permanent Functions + +static void ImpAddPrintableCharactersToTextEdit(SfxRequest const & rReq, ::sd::View* pView) +{ + // evtl. feed characters to activated textedit + const SfxItemSet* pSet = rReq.GetArgs(); + + if(!pSet) + return; + + OUString aInputString; + + if(SfxItemState::SET == pSet->GetItemState(SID_ATTR_CHAR)) + aInputString = static_cast<const SfxStringItem&>(pSet->Get(SID_ATTR_CHAR)).GetValue(); + + if(aInputString.isEmpty()) + return; + + OutlinerView* pOLV = pView->GetTextEditOutlinerView(); + + if(pOLV) + { + for(sal_Int32 a(0); a < aInputString.getLength(); a++) + { + vcl::KeyCode aKeyCode; + // tdf#38669 - create the key event using a Unicode character + KeyEvent aKeyEvent(aInputString[a], aKeyCode); + + // add actual character + pOLV->PostKeyEvent(aKeyEvent); + } + } +} + +void DrawViewShell::FuPermanent(SfxRequest& rReq) +{ + // We do not execute a thing during a native slide show + + if (SlideShow::IsRunning(GetViewShellBase())) + return; + + sal_uInt16 nSId = rReq.GetSlot(); + + if( HasCurrentFunction() && + ( nSId == SID_TEXTEDIT || nSId == SID_ATTR_CHAR || nSId == SID_TEXT_FITTOSIZE || + nSId == SID_ATTR_CHAR_VERTICAL || nSId == SID_TEXT_FITTOSIZE_VERTICAL ) ) + { + rtl::Reference<FuPoor> xFunc( GetCurrentFunction() ); + + FuText* pFuText = dynamic_cast< FuText* >( xFunc.get() ); + + if( pFuText ) + { + pFuText->SetPermanent(true); + xFunc->ReceiveRequest( rReq ); + + Invalidate(); + + // evtl. feed characters to activated textedit + if(SID_ATTR_CHAR == nSId && GetView() && GetView()->IsTextEdit()) + ImpAddPrintableCharactersToTextEdit(rReq, GetView()); + + rReq.Done(); + return; + } + } + + CheckLineTo (rReq); + sal_uInt16 nOldSId = 0; + bool bPermanent = false; + + if( !mpDrawView ) + return; + + if(HasCurrentFunction()) + { + if( (nSId == SID_FORMATPAINTBRUSH) && (GetCurrentFunction()->GetSlotID() == SID_TEXTEDIT) ) + { + // save text edit mode for format paintbrush! + SetOldFunction( GetCurrentFunction() ); + } + else + { + if(GetOldFunction() == GetCurrentFunction()) + { + SetOldFunction(nullptr); + } + } + + if ( nSId != SID_TEXTEDIT && nSId != SID_ATTR_CHAR && nSId != SID_TEXT_FITTOSIZE && + nSId != SID_ATTR_CHAR_VERTICAL && nSId != SID_TEXT_FITTOSIZE_VERTICAL && + nSId != SID_FORMATPAINTBRUSH && + mpDrawView->IsTextEdit() ) + { + mpDrawView->SdrEndTextEdit(); + } + + if( HasCurrentFunction() ) + { + nOldSId = GetCurrentFunction()->GetSlotID(); + + if (nOldSId == nSId || + ((nOldSId == SID_TEXTEDIT || nOldSId == SID_ATTR_CHAR || nOldSId == SID_TEXT_FITTOSIZE || + nOldSId == SID_ATTR_CHAR_VERTICAL || nOldSId == SID_TEXT_FITTOSIZE_VERTICAL) && + (nSId == SID_TEXTEDIT || nSId == SID_ATTR_CHAR || nSId == SID_TEXT_FITTOSIZE || + nSId == SID_ATTR_CHAR_VERTICAL || nSId == SID_TEXT_FITTOSIZE_VERTICAL ))) + { + bPermanent = true; + } + + GetCurrentFunction()->Deactivate(); + } + + SetCurrentFunction(nullptr); + + SfxBindings& rBind = GetViewFrame()->GetBindings(); + rBind.Invalidate(nOldSId); + rBind.Update(nOldSId); + } + + // for LibreOfficeKit - choosing a shape should construct it directly + bool bCreateDirectly = false; + + switch ( nSId ) + { + case SID_TEXTEDIT: // BASIC ??? + case SID_ATTR_CHAR: + case SID_ATTR_CHAR_VERTICAL: + case SID_TEXT_FITTOSIZE: + case SID_TEXT_FITTOSIZE_VERTICAL: + { + SetCurrentFunction( FuText::Create(this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq) ); + GetCurrentFunction()->DoExecute(rReq); + + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_ATTR_CHAR ); + rBindings.Invalidate( SID_ATTR_CHAR_VERTICAL ); + rBindings.Invalidate( SID_TEXT_FITTOSIZE ); + rBindings.Invalidate( SID_TEXT_FITTOSIZE_VERTICAL ); + + // evtl. feed characters to activated textedit + if(SID_ATTR_CHAR == nSId && GetView() && GetView()->IsTextEdit()) + ImpAddPrintableCharactersToTextEdit(rReq, GetView()); + + rReq.Done(); + + const SfxItemSet* pArgs = rReq.GetArgs(); + if (pArgs && pArgs->HasItem(FN_PARAM_1)) + bCreateDirectly = static_cast<const SfxBoolItem&>(pArgs->Get(FN_PARAM_1)).GetValue(); + } + break; + + case SID_FM_CREATE_CONTROL: + { + SetCurrentFunction( FuConstructUnoControl::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq, bPermanent ) ); + rReq.Done(); + } + break; + + case SID_FM_CREATE_FIELDCONTROL: + { + const SfxUnoAnyItem* pDescriptorItem = rReq.GetArg<SfxUnoAnyItem>(SID_FM_DATACCESS_DESCRIPTOR); + DBG_ASSERT( pDescriptorItem, "DrawViewShell::FuPermanent(SID_FM_CREATE_FIELDCONTROL): invalid request args!" ); + + if(pDescriptorItem) + { + // get the form view + FmFormView* pFormView = mpDrawView.get(); + SdrPageView* pPageView = pFormView ? pFormView->GetSdrPageView() : nullptr; + + if(pPageView) + { + svx::ODataAccessDescriptor aDescriptor(pDescriptorItem->GetValue()); + SdrObjectUniquePtr pNewDBField = pFormView->CreateFieldControl(aDescriptor); + + if(pNewDBField) + { + ::tools::Rectangle aVisArea = GetActiveWindow()->PixelToLogic(::tools::Rectangle(Point(0,0), GetActiveWindow()->GetOutputSizePixel())); + Point aObjPos(aVisArea.Center()); + Size aObjSize(pNewDBField->GetLogicRect().GetSize()); + aObjPos.AdjustX( -(aObjSize.Width() / 2) ); + aObjPos.AdjustY( -(aObjSize.Height() / 2) ); + ::tools::Rectangle aNewObjectRectangle(aObjPos, aObjSize); + + pNewDBField->SetLogicRect(aNewObjectRectangle); + + GetView()->InsertObjectAtView(pNewDBField.release(), *pPageView); + } + } + } + rReq.Done(); + } + break; + + case SID_OBJECT_SELECT: + case SID_OBJECT_ROTATE: + case SID_OBJECT_MIRROR: + case SID_OBJECT_CROP: + case SID_OBJECT_TRANSPARENCE: + case SID_OBJECT_GRADIENT: + case SID_OBJECT_SHEAR: + case SID_OBJECT_CROOK_ROTATE: + case SID_OBJECT_CROOK_SLANT: + case SID_OBJECT_CROOK_STRETCH: + case SID_CONVERT_TO_3D_LATHE: + { + sal_uInt16 nSlotId = rReq.GetSlot(); + + // toggle function + if( nOldSId == nSlotId ) + { + nSlotId = SID_OBJECT_SELECT; + rReq.SetSlot( nSlotId ); + } + + if (nSlotId == SID_OBJECT_CROOK_ROTATE || + nSlotId == SID_OBJECT_CROOK_SLANT || + nSlotId == SID_OBJECT_CROOK_STRETCH) + { + if ( mpDrawView->GetMarkedObjectList().GetMarkCount() > 0 && + !mpDrawView->IsCrookAllowed( mpDrawView->IsCrookNoContortion() ) ) + { + if ( mpDrawView->IsPresObjSelected() ) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Question, VclButtonsType::YesNo, + SdResId(STR_ASK_FOR_CONVERT_TO_BEZIER))); + if (xQueryBox->run() == RET_YES ) + { + // implicit transformation into bezier + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->ConvertMarkedToPathObj(false); + } + } + } + } + else if (nSlotId == SID_OBJECT_SHEAR) + { + size_t i = 0; + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + const size_t nMarkCnt = rMarkList.GetMarkCount(); + bool b3DObjMarked = false; + + while (i < nMarkCnt && !b3DObjMarked) + { + if (nullptr != dynamic_cast< E3dObject *>( rMarkList.GetMark(i)->GetMarkedSdrObj() )) + { + b3DObjMarked = true; + } + else + { + i++; + } + } + + if ( nMarkCnt > 0 && !b3DObjMarked && + (!mpDrawView->IsShearAllowed() || !mpDrawView->IsDistortAllowed()) ) + { + if ( mpDrawView->IsPresObjSelected() ) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Question, VclButtonsType::YesNo, + SdResId(STR_ASK_FOR_CONVERT_TO_BEZIER))); + if (xQueryBox->run() == RET_YES) + { + // implicit transformation into bezier + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->ConvertMarkedToPathObj(false); + } + } + } + } + + SetCurrentFunction( FuSelection::Create(this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq) ); + rReq.Done(); + Invalidate( SID_OBJECT_SELECT ); + } + break; + + case SID_DRAW_LINE: + case SID_DRAW_XLINE: + case SID_DRAW_MEASURELINE: + case SID_LINE_ARROW_START: + case SID_LINE_ARROW_END: + case SID_LINE_ARROWS: + case SID_LINE_ARROW_CIRCLE: + case SID_LINE_CIRCLE_ARROW: + case SID_LINE_ARROW_SQUARE: + case SID_LINE_SQUARE_ARROW: + + case SID_DRAW_RECT: + case SID_DRAW_RECT_NOFILL: + case SID_DRAW_RECT_ROUND: + case SID_DRAW_RECT_ROUND_NOFILL: + case SID_DRAW_SQUARE: + case SID_DRAW_SQUARE_NOFILL: + case SID_DRAW_SQUARE_ROUND: + case SID_DRAW_SQUARE_ROUND_NOFILL: + case SID_DRAW_ELLIPSE: + case SID_DRAW_ELLIPSE_NOFILL: + case SID_DRAW_CIRCLE: + case SID_DRAW_CIRCLE_NOFILL: + case SID_DRAW_CAPTION: + case SID_DRAW_CAPTION_VERTICAL: + case SID_TOOL_CONNECTOR: + case SID_CONNECTOR_ARROW_START: + case SID_CONNECTOR_ARROW_END: + case SID_CONNECTOR_ARROWS: + case SID_CONNECTOR_CIRCLE_START: + case SID_CONNECTOR_CIRCLE_END: + case SID_CONNECTOR_CIRCLES: + case SID_CONNECTOR_LINE: + case SID_CONNECTOR_LINE_ARROW_START: + case SID_CONNECTOR_LINE_ARROW_END: + case SID_CONNECTOR_LINE_ARROWS: + case SID_CONNECTOR_LINE_CIRCLE_START: + case SID_CONNECTOR_LINE_CIRCLE_END: + case SID_CONNECTOR_LINE_CIRCLES: + case SID_CONNECTOR_CURVE: + case SID_CONNECTOR_CURVE_ARROW_START: + case SID_CONNECTOR_CURVE_ARROW_END: + case SID_CONNECTOR_CURVE_ARROWS: + case SID_CONNECTOR_CURVE_CIRCLE_START: + case SID_CONNECTOR_CURVE_CIRCLE_END: + case SID_CONNECTOR_CURVE_CIRCLES: + case SID_CONNECTOR_LINES: + case SID_CONNECTOR_LINES_ARROW_START: + case SID_CONNECTOR_LINES_ARROW_END: + case SID_CONNECTOR_LINES_ARROWS: + case SID_CONNECTOR_LINES_CIRCLE_START: + case SID_CONNECTOR_LINES_CIRCLE_END: + case SID_CONNECTOR_LINES_CIRCLES: + case SID_INSERT_SIGNATURELINE: + { + bCreateDirectly = comphelper::LibreOfficeKit::isActive(); + SetCurrentFunction( FuConstructRectangle::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq, bPermanent ) ); + rReq.Done(); + } + break; + case SID_DRAW_POLYGON: + case SID_DRAW_POLYGON_NOFILL: + case SID_DRAW_XPOLYGON: + case SID_DRAW_XPOLYGON_NOFILL: + case SID_DRAW_FREELINE: + case SID_DRAW_FREELINE_NOFILL: + case SID_DRAW_BEZIER_FILL: // BASIC + case SID_DRAW_BEZIER_NOFILL: // BASIC + { + SetCurrentFunction( FuConstructBezierPolygon::Create(this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq, bPermanent) ); + rReq.Done(); + } + break; + + case SID_GLUE_EDITMODE: + { + if (nOldSId != SID_GLUE_EDITMODE) + { + SetCurrentFunction( FuEditGluePoints::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq, bPermanent ) ); + } + else + { + GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + } + + rReq.Done(); + } + break; + + case SID_DRAW_ARC: + case SID_DRAW_CIRCLEARC: + case SID_DRAW_PIE: + case SID_DRAW_PIE_NOFILL: + case SID_DRAW_CIRCLEPIE: + case SID_DRAW_CIRCLEPIE_NOFILL: + case SID_DRAW_ELLIPSECUT: + case SID_DRAW_ELLIPSECUT_NOFILL: + case SID_DRAW_CIRCLECUT: + case SID_DRAW_CIRCLECUT_NOFILL: + { + SetCurrentFunction( FuConstructArc::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq, bPermanent) ); + rReq.Done(); + } + break; + + case SID_3D_CUBE: + case SID_3D_SHELL: + case SID_3D_SPHERE: + case SID_3D_TORUS: + case SID_3D_HALF_SPHERE: + case SID_3D_CYLINDER: + case SID_3D_CONE: + case SID_3D_PYRAMID: + { + SetCurrentFunction( FuConstruct3dObject::Create(this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq, bPermanent ) ); + rReq.Done(); + } + break; + + case SID_DRAWTBX_CS_BASIC : + case SID_DRAWTBX_CS_SYMBOL : + case SID_DRAWTBX_CS_ARROW : + case SID_DRAWTBX_CS_FLOWCHART : + case SID_DRAWTBX_CS_CALLOUT : + case SID_DRAWTBX_CS_STAR : + case SID_DRAW_CS_ID : + { + SetCurrentFunction( FuConstructCustomShape::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq, bPermanent ) ); + rReq.Done(); + + bCreateDirectly = comphelper::LibreOfficeKit::isActive(); + const SfxItemSet* pArgs = rReq.GetArgs(); + if (pArgs && pArgs->HasItem(FN_PARAM_1)) + { + bCreateDirectly = static_cast<const SfxBoolItem&>(pArgs->Get(FN_PARAM_1)).GetValue(); + } + + if ( nSId != SID_DRAW_CS_ID ) + { + SfxBindings& rBind = GetViewFrame()->GetBindings(); + rBind.Invalidate( nSId ); + rBind.Update( nSId ); + } + } + break; + + case SID_FORMATPAINTBRUSH: + { + SetCurrentFunction( FuFormatPaintBrush::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + rReq.Done(); + SfxBindings& rBind = GetViewFrame()->GetBindings(); + rBind.Invalidate( nSId ); + rBind.Update( nSId ); + break; + } + + case SID_ZOOM_MODE: + case SID_ZOOM_PANNING: + { + if (nOldSId != nSId) + { + mbZoomOnPage = false; + SetCurrentFunction( FuZoom::Create(this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + } + else + { + GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + } + rReq.Done(); + } + break; + + default: + break; + } + + if(HasOldFunction()) + { + sal_uInt16 nSlotId = GetOldFunction()->GetSlotID(); + + GetOldFunction()->Deactivate(); + SetOldFunction(nullptr); + + SfxBindings& rBind = GetViewFrame()->GetBindings(); + rBind.Invalidate( nSlotId ); + rBind.Update( nSlotId ); + } + + if(HasCurrentFunction()) + { + GetCurrentFunction()->Activate(); + SetOldFunction( GetCurrentFunction() ); + } + + // invalidate shell, is faster than every individually (says MI) + // now explicit the last slot incl. Update() + Invalidate(); + + // CTRL-SID_OBJECT_SELECT -> select first draw object if none is selected yet + if(SID_OBJECT_SELECT == nSId && HasCurrentFunction() && (rReq.GetModifier() & KEY_MOD1)) + { + if(!GetView()->AreObjectsMarked()) + { + // select first object + GetView()->UnmarkAllObj(); + GetView()->MarkNextObj(true); + + // ...and make it visible + if(GetView()->AreObjectsMarked()) + GetView()->MakeVisible(GetView()->GetAllMarkedRect(), *GetActiveWindow()); + } + } + + // with qualifier construct directly + if(!(HasCurrentFunction() && ((rReq.GetModifier() & KEY_MOD1) || bCreateDirectly))) + return; + + // disable interactive drawing for LOK + if (bCreateDirectly) + GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + + // get SdOptions + SdOptions* pOptions = SD_MOD()->GetSdOptions(GetDoc()->GetDocumentType()); + sal_uInt32 nDefaultObjectSizeWidth(pOptions->GetDefaultObjectSizeWidth()); + sal_uInt32 nDefaultObjectSizeHeight(pOptions->GetDefaultObjectSizeHeight()); + + // calc position and size + ::tools::Rectangle aVisArea = GetActiveWindow()->PixelToLogic(::tools::Rectangle(Point(0,0), GetActiveWindow()->GetOutputSizePixel())); + if (comphelper::LibreOfficeKit::isActive()) + { + // aVisArea is nonsensical in the LOK case, use the slide size + aVisArea = ::tools::Rectangle(Point(), getCurrentPage()->GetSize()); + } + + Point aPagePos = aVisArea.Center(); + aPagePos.AdjustX( -sal_Int32(nDefaultObjectSizeWidth / 2) ); + aPagePos.AdjustY( -sal_Int32(nDefaultObjectSizeHeight / 2) ); + ::tools::Rectangle aNewObjectRectangle(aPagePos, Size(nDefaultObjectSizeWidth, nDefaultObjectSizeHeight)); + SdrPageView* pPageView = mpDrawView->GetSdrPageView(); + + if(!pPageView) + return; + + // create the default object + SdrObjectUniquePtr pObj = GetCurrentFunction()->CreateDefaultObject(nSId, aNewObjectRectangle); + + if(!pObj) + return; + + auto pObjTmp = pObj.get(); + // insert into page + GetView()->InsertObjectAtView(pObj.release(), *pPageView); + + // Now that pFuActual has done what it was created for we + // can switch on the edit mode for callout objects. + switch (nSId) + { + case SID_DRAW_CAPTION: + case SID_DRAW_CAPTION_VERTICAL: + { + // Make FuText the current function. + SfxUInt16Item aItem (SID_TEXTEDIT, 1); + GetViewFrame()->GetDispatcher()-> + ExecuteList(SID_TEXTEDIT, SfxCallMode::SYNCHRON | + SfxCallMode::RECORD, { &aItem }); + // Put text object into edit mode. + GetView()->SdrBeginTextEdit(static_cast<SdrTextObj*>(pObjTmp), pPageView); + break; + } + } +} + +void DrawViewShell::FuDeleteSelectedObjects() +{ + if( !mpDrawView ) + return; + + bool bConsumed = false; + + //if any placeholders are selected + if (mpDrawView->IsPresObjSelected(false)) + { + //If there are placeholders in the list which can be toggled + //off in edit->master->master elements then do that here, + std::vector<SdrObject*> aPresMarksToRemove; + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + for (size_t i=0; i < rMarkList.GetMarkCount(); ++i) + { + SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj(); + SdPage* pPage = static_cast<SdPage*>(pObj->getSdrPageFromSdrObject()); + PresObjKind eKind = pPage->GetPresObjKind(pObj); + if (eKind == PresObjKind::Footer || eKind == PresObjKind::Header || + eKind == PresObjKind::DateTime || eKind == PresObjKind::SlideNumber) + { + aPresMarksToRemove.push_back(pObj); + } + } + + for (SdrObject* pObj : aPresMarksToRemove) + { + //Unmark object + mpDrawView->MarkObj(pObj, mpDrawView->GetSdrPageView(), true); + SdPage* pPage = static_cast<SdPage*>(pObj->getSdrPageFromSdrObject()); + //remove placeholder from master page + pPage->DestroyDefaultPresObj(pPage->GetPresObjKind(pObj)); + } + + bConsumed = true; + } + + // placeholders which cannot be deleted selected + if (mpDrawView->IsPresObjSelected(false, true, false, true)) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + bConsumed = true; + } + + if (bConsumed) + return; + + vcl::KeyCode aKCode(KEY_DELETE); + KeyEvent aKEvt( 0, aKCode); + + bConsumed = mpDrawView->getSmartTags().KeyInput( aKEvt ); + + if (!bConsumed && HasCurrentFunction()) + bConsumed = GetCurrentFunction()->KeyInput(aKEvt); + + if (!bConsumed) + mpDrawView->DeleteMarked(); +} + +void DrawViewShell::FuSupport(SfxRequest& rReq) +{ + if( rReq.GetSlot() == SID_STYLE_FAMILY && rReq.GetArgs()) + GetDocSh()->SetStyleFamily(static_cast<SfxStyleFamily>(rReq.GetArgs()->Get( SID_STYLE_FAMILY ).GetValue())); + + // We do not execute a thing during a native slide show + if(SlideShow::IsRunning(GetViewShellBase()) && + (rReq.GetSlot() != SID_PRESENTATION_END && + rReq.GetSlot() != SID_SIZE_PAGE)) + return; + + CheckLineTo (rReq); + + if( !mpDrawView ) + return; + + sal_uInt16 nSId = rReq.GetSlot(); + + switch ( nSId ) + { + case SID_CLEAR_UNDO_STACK: + { + GetDocSh()->ClearUndoBuffer(); + rReq.Ignore (); + } + break; + + case SID_PRESENTATION: + case SID_PRESENTATION_CURRENT_SLIDE: + case SID_REHEARSE_TIMINGS: + { + slideshowhelp::ShowSlideShow(rReq, *GetDoc()); + rReq.Ignore (); + } + break; + + case SID_PRESENTATION_END: + { + StopSlideShow(); + + rReq.Ignore (); + } + break; + + case SID_BEZIER_EDIT: + { + mpDrawView->SetFrameDragSingles(!mpDrawView->IsFrameDragSingles()); + + /****************************************************************** + * turn ObjectBar on + ******************************************************************/ + if( dynamic_cast< FuSelection* >( GetCurrentFunction().get() ) || dynamic_cast< FuConstructBezierPolygon* >( GetCurrentFunction().get() ) ) + { + // Tell the tool bar manager about the context change. + GetViewShellBase().GetToolBarManager()->SelectionHasChanged(*this,*mpDrawView); + } + + Invalidate(SID_BEZIER_EDIT); + rReq.Ignore(); + } + break; + + case SID_OBJECT_CLOSE: + { + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + if ( rMarkList.GetMark(0) && !mpDrawView->IsAction() ) + { + SdrPathObj* pPathObj = static_cast<SdrPathObj*>( rMarkList.GetMark(0)->GetMarkedSdrObj()); + const bool bUndo = mpDrawView->IsUndoEnabled(); + if( bUndo ) + mpDrawView->BegUndo(SdResId(STR_UNDO_BEZCLOSE)); + + mpDrawView->UnmarkAllPoints(); + + if( bUndo ) + mpDrawView->AddUndo(std::make_unique<SdrUndoGeoObj>(*pPathObj)); + + pPathObj->ToggleClosed(); + + if( bUndo ) + mpDrawView->EndUndo(); + } + rReq.Done(); + } + break; + + case SID_CUT: + { + if ( mpDrawView->IsPresObjSelected(false, true, false, true) ) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + //tdf#126197: EndTextEdit in all views if current one is not in TextEdit + if ( !mpDrawView->IsTextEdit() ) + mpDrawView->EndTextEditAllViews(); + + if(HasCurrentFunction()) + { + GetCurrentFunction()->DoCut(); + } + else if(mpDrawView) + { + mpDrawView->DoCut(); + } + } + rReq.Ignore (); + } + break; + + case SID_COPY: + { + if ( mpDrawView->IsPresObjSelected(false, true, false, true) ) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + if(HasCurrentFunction()) + { + GetCurrentFunction()->DoCopy(); + } + else if( mpDrawView ) + { + mpDrawView->DoCopy(); + } + } + rReq.Ignore (); + } + break; + + case SID_PASTE: + { + weld::WaitObject aWait(GetFrameWeld()); + + if(HasCurrentFunction()) + { + GetCurrentFunction()->DoPaste(); + } + else if(mpDrawView) + { + mpDrawView->DoPaste(); + } + + rReq.Ignore (); + } + break; + + case SID_UNICODE_NOTATION_TOGGLE: + { + if( mpDrawView->IsTextEdit() ) + { + OutlinerView* pOLV = mpDrawView->GetTextEditOutlinerView(); + if( pOLV ) + { + OUString sInput = pOLV->GetSurroundingText(); + ESelection aSel( pOLV->GetSelection() ); + if( aSel.nStartPos > aSel.nEndPos ) + aSel.nEndPos = aSel.nStartPos; + + //calculate a valid end-position by reading logical characters + sal_Int32 nUtf16Pos=0; + while( (nUtf16Pos < sInput.getLength()) && (nUtf16Pos < aSel.nEndPos) ) + { + sInput.iterateCodePoints(&nUtf16Pos); + //The mouse can set the cursor in the middle of a multi-unit character, + // so reset to the proper end of the logical characters + if( nUtf16Pos > aSel.nEndPos ) + aSel.nEndPos = nUtf16Pos; + } + + ToggleUnicodeCodepoint aToggle; + while( nUtf16Pos && aToggle.AllowMoreInput( sInput[nUtf16Pos-1]) ) + --nUtf16Pos; + OUString sReplacement = aToggle.ReplacementString(); + if( !sReplacement.isEmpty() ) + { + OUString sStringToReplace = aToggle.StringToReplace(); + mpDrawView->BegUndo(sStringToReplace +"->"+ sReplacement); + aSel.nStartPos = aSel.nEndPos - sStringToReplace.getLength(); + pOLV->SetSelection( aSel ); + pOLV->InsertText(sReplacement, true); + mpDrawView->EndUndo(); + } + } + } + } + break; + + case SID_PASTE_UNFORMATTED: + { + weld::WaitObject aWait(GetFrameWeld()); + + if(HasCurrentFunction()) + { + GetCurrentFunction()->DoPasteUnformatted(); + } + else if(mpDrawView) + { + TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( GetActiveWindow() ) ); + if (aDataHelper.GetTransferable().is()) + { + sal_Int8 nAction = DND_ACTION_COPY; + mpDrawView->InsertData( aDataHelper, + GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(), GetActiveWindow()->GetOutputSizePixel() ).Center() ), + nAction, false, SotClipboardFormatId::STRING); + } + } + + rReq.Ignore (); + } + break; + + case SID_CLIPBOARD_FORMAT_ITEMS: + { + weld::WaitObject aWait(GetFrameWeld()); + TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( GetActiveWindow() ) ); + const SfxItemSet* pReqArgs = rReq.GetArgs(); + SotClipboardFormatId nFormat = SotClipboardFormatId::NONE; + + if( pReqArgs ) + { + const SfxUInt32Item* pIsActive = rReq.GetArg<SfxUInt32Item>(SID_CLIPBOARD_FORMAT_ITEMS); + nFormat = static_cast<SotClipboardFormatId>(pIsActive->GetValue()); + } + + if( nFormat != SotClipboardFormatId::NONE && aDataHelper.GetTransferable().is() ) + { + sal_Int8 nAction = DND_ACTION_COPY; + + if( !mpDrawView->InsertData( aDataHelper, + GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(), GetActiveWindow()->GetOutputSizePixel() ).Center() ), + nAction, false, nFormat ) ) + { + INetBookmark aINetBookmark( "", "" ); + + if( ( aDataHelper.HasFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ) && + aDataHelper.GetINetBookmark( SotClipboardFormatId::NETSCAPE_BOOKMARK, aINetBookmark ) ) || + ( aDataHelper.HasFormat( SotClipboardFormatId::FILEGRPDESCRIPTOR ) && + aDataHelper.GetINetBookmark( SotClipboardFormatId::FILEGRPDESCRIPTOR, aINetBookmark ) ) || + ( aDataHelper.HasFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) && + aDataHelper.GetINetBookmark( SotClipboardFormatId::UNIFORMRESOURCELOCATOR, aINetBookmark ) ) ) + { + InsertURLField( aINetBookmark.GetURL(), aINetBookmark.GetDescription(), "" ); + } + } + } + } + break; + + case SID_DELETE: + { + if ( mpDrawView->IsTextEdit() ) + { + OutlinerView* pOLV = mpDrawView->GetTextEditOutlinerView(); + + if (pOLV) + { + vcl::KeyCode aKCode(KEY_DELETE); + KeyEvent aKEvt( 0, aKCode); + // We use SdrObjEditView to handle DEL for underflow handling + (void)mpDrawView->KeyInput(aKEvt, nullptr); + } + } + else + { + mpDrawView->EndTextEditAllViews(); + FuDeleteSelectedObjects(); + } + rReq.Ignore (); + } + break; + + case SID_NOTES_MODE: + case SID_SLIDE_MASTER_MODE: + case SID_NOTES_MASTER_MODE: + case SID_HANDOUT_MASTER_MODE: + + // AutoLayouts have to be ready. + GetDoc()->StopWorkStartupDelay(); + [[fallthrough]]; + + case SID_DRAWINGMODE: + case SID_SLIDE_SORTER_MODE: + case SID_OUTLINE_MODE: + // Let the sub-shell manager handle the slot handling. + framework::FrameworkHelper::Instance(GetViewShellBase())->HandleModeChangeSlot( + nSId, + rReq); + rReq.Ignore (); + break; + + case SID_MASTERPAGE: // BASIC + { + if (comphelper::LibreOfficeKit::isActive()) + GetViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED, + ".uno:SlideMasterPage=true"); + + // AutoLayouts needs to be finished + GetDoc()->StopWorkStartupDelay(); + + const SfxItemSet* pReqArgs = rReq.GetArgs(); + + if ( pReqArgs ) + { + const SfxBoolItem* pIsActive = rReq.GetArg<SfxBoolItem>(SID_MASTERPAGE); + mbIsLayerModeActive = pIsActive->GetValue (); + } + + Broadcast ( + ViewShellHint(ViewShellHint::HINT_CHANGE_EDIT_MODE_START)); + + // turn on default layer of MasterPage + mpDrawView->SetActiveLayer(sUNO_LayerName_background_objects); + + ChangeEditMode(EditMode::MasterPage, mbIsLayerModeActive); + + if(HasCurrentFunction(SID_BEZIER_EDIT)) + GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + + Broadcast ( + ViewShellHint(ViewShellHint::HINT_CHANGE_EDIT_MODE_END)); + + InvalidateWindows(); + Invalidate(); + + rReq.Done(); + } + break; + + case SID_CLOSE_MASTER_VIEW: + { + // Notify of disabling master view, which is enabled in DrawViewShell::ChangeEditMode. + if (comphelper::LibreOfficeKit::isActive()) + GetViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED, + ".uno:SlideMasterPage=false"); + + Broadcast ( + ViewShellHint(ViewShellHint::HINT_CHANGE_EDIT_MODE_START)); + + // Switch page back to the first one. Not doing so leads to a + // crash. This seems to be some bug in the edit mode switching + // and page switching methods. + SwitchPage (0); + ChangeEditMode(EditMode::Page, IsLayerModeActive()); + Broadcast ( + ViewShellHint(ViewShellHint::HINT_CHANGE_EDIT_MODE_END)); + + if(HasCurrentFunction(SID_BEZIER_EDIT)) + { + GetViewFrame()->GetDispatcher()->Execute( + SID_OBJECT_SELECT, + SfxCallMode::ASYNCHRON); + } + + rReq.Done(); + } + break; + + case SID_RULER: + { + const SfxItemSet* pReqArgs = rReq.GetArgs(); + + // Remember old ruler state + bool bOldHasRuler(HasRuler()); + + if ( pReqArgs ) + { + const SfxBoolItem* pIsActive = rReq.GetArg<SfxBoolItem>(SID_RULER); + SetRuler (pIsActive->GetValue ()); + } + else SetRuler (!HasRuler()); + + // Did ruler state change? Tell that to SdOptions, too. + bool bHasRuler(HasRuler()); + + if(bOldHasRuler != bHasRuler) + { + SdOptions* pOptions = SD_MOD()->GetSdOptions(GetDoc()->GetDocumentType()); + + if(pOptions && pOptions->IsRulerVisible() != bHasRuler) + { + pOptions->SetRulerVisible(bHasRuler); + } + } + + Invalidate (SID_RULER); + Resize(); + rReq.Done (); + } + break; + + case SID_SIZE_PAGE: + case SID_SIZE_PAGE_WIDTH: // BASIC + { + mbZoomOnPage = ( rReq.GetSlot() == SID_SIZE_PAGE ); + + SdrPageView* pPageView = mpDrawView->GetSdrPageView(); + + if ( pPageView ) + { + Point aPagePos(0, 0); // = pPageView->GetOffset(); + Size aPageSize = pPageView->GetPage()->GetSize(); + + aPagePos.AdjustX(aPageSize.Width() / 2 ); + aPageSize.setWidth( static_cast<::tools::Long>(aPageSize.Width() * 1.03) ); + + if( rReq.GetSlot() == SID_SIZE_PAGE ) + { + aPagePos.AdjustY(aPageSize.Height() / 2 ); + aPageSize.setHeight( static_cast<::tools::Long>(aPageSize.Height() * 1.03) ); + aPagePos.AdjustY( -(aPageSize.Height() / 2) ); + } + else + { + Point aPt = GetActiveWindow()->PixelToLogic( Point( 0, GetActiveWindow()->GetSizePixel().Height() / 2 ) ); + aPagePos.AdjustY(aPt.Y() ); + aPageSize.setHeight( 2 ); + } + + aPagePos.AdjustX( -(aPageSize.Width() / 2) ); + + SetZoomRect( ::tools::Rectangle( aPagePos, aPageSize ) ); + + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), + GetActiveWindow()->GetOutputSizePixel()) ); + mpZoomList->InsertZoomRect(aVisAreaWin); + } + Invalidate( SID_ZOOM_IN ); + Invalidate( SID_ZOOM_OUT ); + Invalidate( SID_ZOOM_PANNING ); + rReq.Done (); + } + break; + + case SID_SIZE_REAL: // BASIC + { + mbZoomOnPage = false; + SetZoom( 100 ); + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), + GetActiveWindow()->GetOutputSizePixel()) ); + mpZoomList->InsertZoomRect(aVisAreaWin); + Invalidate( SID_ZOOM_IN ); + Invalidate( SID_ZOOM_OUT ); + Invalidate( SID_ZOOM_PANNING ); + rReq.Done (); + } + break; + + case SID_ZOOM_OUT: // BASIC + { + mbZoomOnPage = false; + SetZoom( std::max<::tools::Long>( GetActiveWindow()->GetZoom() / 2, GetActiveWindow()->GetMinZoom() ) ); + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), + GetActiveWindow()->GetOutputSizePixel()) ); + mpZoomList->InsertZoomRect(aVisAreaWin); + Invalidate( SID_ZOOM_IN ); + Invalidate( SID_ZOOM_OUT ); + Invalidate( SID_ZOOM_PANNING ); + rReq.Done (); + } + break; + + case SID_ZOOM_IN: + { + mbZoomOnPage = false; + SetZoom( std::min<::tools::Long>( GetActiveWindow()->GetZoom() * 2, GetActiveWindow()->GetMaxZoom() ) ); + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), + GetActiveWindow()->GetOutputSizePixel()) ); + mpZoomList->InsertZoomRect(aVisAreaWin); + Invalidate( SID_ZOOM_IN ); + Invalidate(SID_ZOOM_OUT); + Invalidate( SID_ZOOM_PANNING ); + rReq.Done (); + } + break; + + case SID_SIZE_VISAREA: + { + ::tools::Rectangle aVisArea = mpFrameView->GetVisArea(); + Size aVisAreaSize = aVisArea.GetSize(); + + if (!aVisAreaSize.IsEmpty()) + { + mbZoomOnPage = false; + SetZoomRect(aVisArea); + Invalidate( SID_ZOOM_IN ); + Invalidate( SID_ZOOM_OUT ); + Invalidate( SID_ZOOM_PANNING ); + } + rReq.Done (); + } + break; + + // name confusion: SID_SIZE_OPTIMAL -> Zoom onto selected objects + // --> Is offered as object zoom in program + case SID_SIZE_OPTIMAL: // BASIC + { + mbZoomOnPage = false; + if ( mpDrawView->AreObjectsMarked() ) + { + maMarkRect = mpDrawView->GetAllMarkedRect(); + ::tools::Long nW = static_cast<::tools::Long>(maMarkRect.GetWidth() * 1.03); + ::tools::Long nH = static_cast<::tools::Long>(maMarkRect.GetHeight() * 1.03); + Point aPos = maMarkRect.Center(); + aPos.AdjustX( -(nW / 2) ); + aPos.AdjustY( -(nH / 2) ); + if ( nW && nH ) + { + SetZoomRect(::tools::Rectangle(aPos, Size(nW, nH))); + + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), + GetActiveWindow()->GetOutputSizePixel()) ); + mpZoomList->InsertZoomRect(aVisAreaWin); + } + } + Invalidate( SID_ZOOM_IN ); + Invalidate( SID_ZOOM_OUT ); + Invalidate( SID_ZOOM_PANNING ); + rReq.Done (); + } + break; + + // name confusion: SID_SIZE_ALL -> Zoom onto all objects + // --> Is offered as optimal in program + case SID_SIZE_ALL: // BASIC + { + mbZoomOnPage = false; + SdrPageView* pPageView = mpDrawView->GetSdrPageView(); + + if( pPageView ) + { + ::tools::Rectangle aBoundRect( pPageView->GetObjList()->GetAllObjBoundRect() ); + + ::tools::Long nW = static_cast<::tools::Long>(aBoundRect.GetWidth() * 1.03); + ::tools::Long nH = static_cast<::tools::Long>(aBoundRect.GetHeight() * 1.03); + Point aPos = aBoundRect.Center(); + aPos.AdjustX( -(nW / 2) ); + aPos.AdjustY( -(nH / 2) ); + if ( nW && nH ) + { + SetZoomRect( ::tools::Rectangle( aPos, Size( nW, nH ) ) ); + + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), + GetActiveWindow()->GetOutputSizePixel()) ); + mpZoomList->InsertZoomRect(aVisAreaWin); + } + + Invalidate( SID_ZOOM_IN ); + Invalidate( SID_ZOOM_OUT ); + Invalidate( SID_ZOOM_PANNING ); + } + rReq.Done (); + } + break; + + case SID_ZOOM_PREV: + { + if (mpDrawView->IsTextEdit()) + { + mpDrawView->SdrEndTextEdit(); + } + + if (mpZoomList->IsPreviousPossible()) + { + // set previous ZoomRect + SetZoomRect(mpZoomList->GetPreviousZoomRect()); + } + rReq.Done (); + } + break; + + case SID_ZOOM_NEXT: + { + if (mpDrawView->IsTextEdit()) + { + mpDrawView->SdrEndTextEdit(); + } + + if (mpZoomList->IsNextPossible()) + { + // set next ZoomRect + SetZoomRect(mpZoomList->GetNextZoomRect()); + } + rReq.Done (); + } + break; + + case SID_GLUE_INSERT_POINT: + case SID_GLUE_PERCENT: + case SID_GLUE_ESCDIR: + case SID_GLUE_ESCDIR_LEFT: + case SID_GLUE_ESCDIR_RIGHT: + case SID_GLUE_ESCDIR_TOP: + case SID_GLUE_ESCDIR_BOTTOM: + case SID_GLUE_HORZALIGN_CENTER: + case SID_GLUE_HORZALIGN_LEFT: + case SID_GLUE_HORZALIGN_RIGHT: + case SID_GLUE_VERTALIGN_CENTER: + case SID_GLUE_VERTALIGN_TOP: + case SID_GLUE_VERTALIGN_BOTTOM: + { + rtl::Reference<FuPoor> xFunc( GetCurrentFunction() ); + FuEditGluePoints* pFunc = dynamic_cast< FuEditGluePoints* >( xFunc.get() ); + + if(pFunc) + pFunc->ReceiveRequest(rReq); + + rReq.Done(); + } + break; + + case SID_AUTOSPELL_CHECK: + { + bool bOnlineSpell; + const SfxPoolItem* pItem; + + if (rReq.GetArgs()->HasItem(FN_PARAM_1, &pItem)) + bOnlineSpell = static_cast<const SfxBoolItem*>(pItem)->GetValue(); + else // Toggle + bOnlineSpell = !GetDoc()->GetOnlineSpell(); + + GetDoc()->SetOnlineSpell(bOnlineSpell); + + ::Outliner* pOL = mpDrawView->GetTextEditOutliner(); + + if (pOL) + { + EEControlBits nCntrl = pOL->GetControlWord(); + + if (bOnlineSpell) + nCntrl |= EEControlBits::ONLINESPELLING; + else + nCntrl &= ~EEControlBits::ONLINESPELLING; + + pOL->SetControlWord(nCntrl); + } + + GetActiveWindow()->Invalidate(); + rReq.Done (); + } + break; + + case SID_TRANSLITERATE_SENTENCE_CASE: + case SID_TRANSLITERATE_TITLE_CASE: + case SID_TRANSLITERATE_TOGGLE_CASE: + case SID_TRANSLITERATE_UPPER: + case SID_TRANSLITERATE_LOWER: + case SID_TRANSLITERATE_HALFWIDTH: + case SID_TRANSLITERATE_FULLWIDTH: + case SID_TRANSLITERATE_HIRAGANA: + case SID_TRANSLITERATE_KATAKANA: + { + OutlinerView* pOLV = GetView()->GetTextEditOutlinerView(); + if( pOLV ) + { + TransliterationFlags nType = TransliterationFlags::NONE; + + switch( nSId ) + { + case SID_TRANSLITERATE_SENTENCE_CASE: + nType = TransliterationFlags::SENTENCE_CASE; + break; + case SID_TRANSLITERATE_TITLE_CASE: + nType = TransliterationFlags::TITLE_CASE; + break; + case SID_TRANSLITERATE_TOGGLE_CASE: + nType = TransliterationFlags::TOGGLE_CASE; + break; + case SID_TRANSLITERATE_UPPER: + nType = TransliterationFlags::LOWERCASE_UPPERCASE; + break; + case SID_TRANSLITERATE_LOWER: + nType = TransliterationFlags::UPPERCASE_LOWERCASE; + break; + case SID_TRANSLITERATE_HALFWIDTH: + nType = TransliterationFlags::FULLWIDTH_HALFWIDTH; + break; + case SID_TRANSLITERATE_FULLWIDTH: + nType = TransliterationFlags::HALFWIDTH_FULLWIDTH; + break; + case SID_TRANSLITERATE_HIRAGANA: + nType = TransliterationFlags::KATAKANA_HIRAGANA; + break; + case SID_TRANSLITERATE_KATAKANA: + nType = TransliterationFlags::HIRAGANA_KATAKANA; + break; + } + + pOLV->TransliterateText( nType ); + } + + rReq.Done(); + } + break; + + // #UndoRedo# + case SID_UNDO : + { + // moved implementation to BaseClass + ImpSidUndo(rReq); + } + break; + case SID_REDO : + { + // moved implementation to BaseClass + ImpSidRedo(rReq); + } + break; + + default: + break; + } +} + +void DrawViewShell::FuSupportRotate(SfxRequest const &rReq) +{ + if( rReq.GetSlot() != SID_TRANSLITERATE_ROTATE_CASE ) + return; + + ::sd::View* pView = GetView(); + + if (!pView) + return; + + OutlinerView* pOLV = pView->GetTextEditOutlinerView(); + + if (!pOLV) + return; + + pOLV->TransliterateText( m_aRotateCase.getNextMode() ); +} + +void DrawViewShell::InsertURLField(const OUString& rURL, const OUString& rText, + const OUString& rTarget) +{ + OutlinerView* pOLV = mpDrawView->GetTextEditOutlinerView(); + + if (pOLV) + { + ESelection aSel( pOLV->GetSelection() ); + SvxFieldItem aURLItem( SvxURLField( rURL, rText, SvxURLFormat::Repr ), EE_FEATURE_FIELD ); + pOLV->InsertField( aURLItem ); + if ( aSel.nStartPos <= aSel.nEndPos ) + aSel.nEndPos = aSel.nStartPos + 1; + else + aSel.nStartPos = aSel.nEndPos + 1; + pOLV->SetSelection( aSel ); + } + else + { + Outliner* pOutl = GetDoc()->GetInternalOutliner(); + pOutl->Init( OutlinerMode::TextObject ); + OutlinerMode nOutlMode = pOutl->GetOutlinerMode(); + + SvxURLField aURLField(rURL, rText, SvxURLFormat::Repr); + aURLField.SetTargetFrame(rTarget); + SvxFieldItem aURLItem(aURLField, EE_FEATURE_FIELD); + pOutl->QuickInsertField( aURLItem, ESelection() ); + std::optional<OutlinerParaObject> pOutlParaObject = pOutl->CreateParaObject(); + + SdrRectObj* pRectObj = new SdrRectObj( + GetView()->getSdrModelFromSdrView(), + SdrObjKind::Text); + + pOutl->UpdateFields(); + pOutl->SetUpdateLayout( true ); + Size aSize(pOutl->CalcTextSize()); + pOutl->SetUpdateLayout( false ); + + Point aPos; + ::tools::Rectangle aRect(aPos, GetActiveWindow()->GetOutputSizePixel() ); + aPos = aRect.Center(); + aPos = GetActiveWindow()->PixelToLogic(aPos); + + if (aPos.getX() - (aSize.Width() / 2) >= 0) + aPos.AdjustX( -(aSize.Width() / 2) ); + if (aPos.getY() - (aSize.Height() / 2) >= 0) + aPos.AdjustY( -(aSize.Height() / 2) ); + + ::tools::Rectangle aLogicRect(aPos, aSize); + pRectObj->SetLogicRect(aLogicRect); + pRectObj->SetOutlinerParaObject( std::move(pOutlParaObject) ); + mpDrawView->InsertObjectAtView(pRectObj, *mpDrawView->GetSdrPageView()); + pOutl->Init( nOutlMode ); + } +} + +void DrawViewShell::InsertURLButton(const OUString& rURL, const OUString& rText, + const OUString& rTarget, const Point* pPos) +{ + bool bNewObj = true; + + const OUString sTargetURL( ::URIHelper::SmartRel2Abs( INetURLObject( GetDocSh()->GetMedium()->GetBaseURL() ), rURL, URIHelper::GetMaybeFileHdl(), true, false, + INetURLObject::EncodeMechanism::WasEncoded, + INetURLObject::DecodeMechanism::Unambiguous ) ); + if (mpDrawView->GetMarkedObjectList().GetMarkCount() > 0) + { + SdrObject* pMarkedObj = mpDrawView->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj(); + if( pMarkedObj ) try + { + // change first marked object + if( SdrInventor::FmForm == pMarkedObj->GetObjInventor() && pMarkedObj->GetObjIdentifier() == SdrObjKind::FormButton ) + { + bNewObj = false; + + SdrUnoObj* pUnoCtrl = static_cast< SdrUnoObj* >( pMarkedObj ); + + Reference< awt::XControlModel > xControlModel( pUnoCtrl->GetUnoControlModel(), UNO_SET_THROW ); + Reference< beans::XPropertySet > xPropSet( xControlModel, UNO_QUERY_THROW ); + + xPropSet->setPropertyValue("Label" , Any( rText ) ); + xPropSet->setPropertyValue("TargetURL" , Any( sTargetURL ) ); + + if( !rTarget.isEmpty() ) + xPropSet->setPropertyValue("TargetFrame" , Any( rTarget ) ); + + xPropSet->setPropertyValue( "ButtonType" , Any( form::FormButtonType_URL ) ); +#if HAVE_FEATURE_AVMEDIA + if ( ::avmedia::MediaWindow::isMediaURL( rURL, ""/*TODO?*/ ) ) + { + xPropSet->setPropertyValue( "DispatchURLInternal" , Any( true ) ); + } +#endif + } + else + { + // add url as interaction for first selected shape + bNewObj = false; + + SdAnimationInfo* pInfo = SdDrawDocument::GetShapeUserData(*pMarkedObj, true); + pInfo->meClickAction = presentation::ClickAction_DOCUMENT; + pInfo->SetBookmark( sTargetURL ); + } + } + catch( uno::Exception& ) + { + } + } + + if (!bNewObj) + return; + + try + { + SdrUnoObj* pUnoCtrl = static_cast< SdrUnoObj* >( + SdrObjFactory::MakeNewObject( + GetView()->getSdrModelFromSdrView(), + SdrInventor::FmForm, + SdrObjKind::FormButton)); //, + //mpDrawView->GetSdrPageView()->GetPage())); + + Reference< awt::XControlModel > xControlModel( pUnoCtrl->GetUnoControlModel(), uno::UNO_SET_THROW ); + Reference< beans::XPropertySet > xPropSet( xControlModel, uno::UNO_QUERY_THROW ); + + xPropSet->setPropertyValue( "Label" , Any( rText ) ); + xPropSet->setPropertyValue( "TargetURL" , Any( sTargetURL ) ); + + if( !rTarget.isEmpty() ) + xPropSet->setPropertyValue( "TargetFrame" , Any( rTarget ) ); + + xPropSet->setPropertyValue( "ButtonType" , Any( form::FormButtonType_URL ) ); +#if HAVE_FEATURE_AVMEDIA + if ( ::avmedia::MediaWindow::isMediaURL( rURL, ""/*TODO?*/ ) ) + xPropSet->setPropertyValue( "DispatchURLInternal" , Any( true ) ); +#endif + + Point aPos; + + if (pPos) + { + aPos = *pPos; + } + else + { + aPos = ::tools::Rectangle(aPos, GetActiveWindow()->GetOutputSizePixel()).Center(); + aPos = GetActiveWindow()->PixelToLogic(aPos); + } + + Size aSize(4000, 1000); + aPos.AdjustX( -(aSize.Width() / 2) ); + aPos.AdjustY( -(aSize.Height() / 2) ); + pUnoCtrl->SetLogicRect(::tools::Rectangle(aPos, aSize)); + + SdrInsertFlags nOptions = SdrInsertFlags::SETDEFLAYER; + + OSL_ASSERT (GetViewShell()!=nullptr); + SfxInPlaceClient* pIpClient = GetViewShell()->GetIPClient(); + if (pIpClient!=nullptr && pIpClient->IsObjectInPlaceActive()) + { + nOptions |= SdrInsertFlags::DONTMARK; + } + + mpDrawView->InsertObjectAtView(pUnoCtrl, *mpDrawView->GetSdrPageView(), nOptions); + } + catch( Exception& ) + { + } +} + +void DrawViewShell::ShowUIControls (bool bVisible) +{ + ViewShell::ShowUIControls (bVisible); + maTabControl->Show (bVisible); +} + +namespace slideshowhelp +{ + void ShowSlideShow(SfxRequest const & rReq, SdDrawDocument &rDoc) + { + Reference< XPresentation2 > xPresentation( rDoc.getPresentation() ); + if( !xPresentation.is() ) + return; + + sfx2::SfxNotebookBar::LockNotebookBar(); + if (SID_REHEARSE_TIMINGS == rReq.GetSlot()) + xPresentation->rehearseTimings(); + else if (rDoc.getPresentationSettings().mbCustomShow) + { + //fdo#69975 if a custom show has been set, then + //use it whether or not we've been asked to + //start from the current or first slide + xPresentation->start(); + + // if the custom show not set by default, only show it. + if (rDoc.getPresentationSettings().mbStartCustomShow) + rDoc.getPresentationSettings().mbCustomShow = false; + } + else if (SID_PRESENTATION_CURRENT_SLIDE == rReq.GetSlot()) + { + //If there is no custom show set, start will automatically + //start at the current page + xPresentation->start(); + } + else + { + //Start at page 0, this would blow away any custom + //show settings if any were set + Sequence< PropertyValue > aArguments{ comphelper::makePropertyValue("FirstPage", + OUString("0")) }; + xPresentation->startWithArguments( aArguments ); + } + sfx2::SfxNotebookBar::UnlockNotebookBar(); + } +} + +void DrawViewShell::StopSlideShow() +{ + Reference< XPresentation2 > xPresentation( GetDoc()->getPresentation() ); + if(xPresentation.is() && xPresentation->isRunning()) + { + if( mpDrawView->IsTextEdit() ) + mpDrawView->SdrEndTextEdit(); + + xPresentation->end(); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviewsf.cxx b/sd/source/ui/view/drviewsf.cxx new file mode 100644 index 000000000..8aab2c576 --- /dev/null +++ b/sd/source/ui/view/drviewsf.cxx @@ -0,0 +1,826 @@ +/* -*- 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 <DrawViewShell.hxx> +#include <com/sun/star/form/FormButtonType.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <comphelper/string.hxx> +#include <svx/svxids.hrc> +#include <svx/sdmetitm.hxx> +#include <svx/hlnkitem.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/flditem.hxx> +#include <editeng/udlnitem.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/whiter.hxx> +#include <svl/eitem.hxx> +#include <svl/intitem.hxx> +#include <svl/itempool.hxx> +#include <sfx2/tplpitem.hxx> +#include <sfx2/bindings.hxx> +#include <svx/xdef.hxx> +#include <svx/svdoutl.hxx> +#include <svx/svdouno.hxx> +#include <svx/fmshell.hxx> +#include <svl/cjkoptions.hxx> + +#include <app.hrc> + +#include <sdmod.hxx> +#include <stlsheet.hxx> +#include <drawview.hxx> +#include <drawdoc.hxx> +#include <Window.hxx> +#include <ViewShellBase.hxx> +#include <FormShellManager.hxx> +#include <anminfo.hxx> + +#include <editeng/lspcitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/escapementitem.hxx> +#include <editeng/numitem.hxx> +#include <editeng/adjustitem.hxx> +#include <editeng/urlfieldhelper.hxx> +#include <svx/nbdtmgfact.hxx> +#include <svx/nbdtmg.hxx> +#include <memory> + +using namespace com::sun::star::drawing; +using namespace svx::sidebar; +using namespace ::com::sun::star; + +namespace sd { + +/** + * Set state of controller SfxSlots + */ +void DrawViewShell::GetCtrlState(SfxItemSet &rSet) +{ + if (rSet.GetItemState(SID_RELOAD) != SfxItemState::UNKNOWN) + { + // let "last version" of SFx en/disable + GetViewFrame()->GetSlotState (SID_RELOAD, nullptr, &rSet); + } + + if (SfxItemState::DEFAULT == rSet.GetItemState(SID_HYPERLINK_GETLINK)) + { + SvxHyperlinkItem aHLinkItem; + + OutlinerView* pOLV = mpDrawView->GetTextEditOutlinerView(); + + if (pOLV) + { + const SvxFieldData* pField = pOLV->GetFieldAtCursor(); + if( auto pUrlField = dynamic_cast< const SvxURLField *>( pField ) ) + { + aHLinkItem.SetName(pUrlField->GetRepresentation()); + aHLinkItem.SetURL(pUrlField->GetURL()); + aHLinkItem.SetTargetFrame(pUrlField->GetTargetFrame()); + } + else + { + // use selected text as name for urls + OUString sReturn = pOLV->GetSelected(); + if (sReturn.getLength() > 255) + sReturn = sReturn.copy(0, 255); + aHLinkItem.SetName(comphelper::string::stripEnd(sReturn, ' ')); + } + } + else + { + if (mpDrawView->GetMarkedObjectList().GetMarkCount() > 0) + { + bool bFound = false; + + SdrObject* pMarkedObj = mpDrawView->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj(); + if( pMarkedObj && (SdrInventor::FmForm == pMarkedObj->GetObjInventor()) ) + { + SdrUnoObj* pUnoCtrl = dynamic_cast< SdrUnoObj* >( pMarkedObj ); + + if(pUnoCtrl) try + { + uno::Reference< awt::XControlModel > xControlModel( pUnoCtrl->GetUnoControlModel(), uno::UNO_SET_THROW ); + uno::Reference< beans::XPropertySet > xPropSet( xControlModel, uno::UNO_QUERY_THROW ); + uno::Reference< beans::XPropertySetInfo > xPropInfo( xPropSet->getPropertySetInfo(), uno::UNO_SET_THROW ); + + form::FormButtonType eButtonType = form::FormButtonType_URL; + static const OUStringLiteral sButtonType( u"ButtonType" ); + if(xPropInfo->hasPropertyByName( sButtonType ) && (xPropSet->getPropertyValue( sButtonType ) >>= eButtonType ) ) + { + OUString aString; + + // Label + static const OUStringLiteral sLabel( u"Label" ); + if(xPropInfo->hasPropertyByName(sLabel)) + { + if( xPropSet->getPropertyValue(sLabel) >>= aString ) + aHLinkItem.SetName(aString); + } + + // URL + static const OUStringLiteral sTargetURL( u"TargetURL" ); + if(xPropInfo->hasPropertyByName(sTargetURL)) + { + if( xPropSet->getPropertyValue(sTargetURL) >>= aString ) + aHLinkItem.SetURL(aString); + } + + // Target + static const OUStringLiteral sTargetFrame( u"TargetFrame" ); + if(xPropInfo->hasPropertyByName(sTargetFrame) ) + { + if( xPropSet->getPropertyValue(sTargetFrame) >>= aString ) + aHLinkItem.SetTargetFrame(aString); + } + + aHLinkItem.SetInsertMode(HLINK_BUTTON); + bFound = true; + } + } + catch( uno::Exception& ) + { + } + } + + // try interaction link + if( !bFound && pMarkedObj ) + { + SdAnimationInfo* pInfo = SdDrawDocument::GetShapeUserData(*pMarkedObj); + if( pInfo && (pInfo->meClickAction == presentation::ClickAction_DOCUMENT) ) + aHLinkItem.SetURL( pInfo->GetBookmark()); + aHLinkItem.SetInsertMode(HLINK_BUTTON); + } + } + } + + rSet.Put(aHLinkItem); + } + rSet.Put( SfxBoolItem( SID_READONLY_MODE, mbReadOnly ) ); + + // output quality + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_OUTPUT_QUALITY_COLOR ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_OUTPUT_QUALITY_GRAYSCALE ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_OUTPUT_QUALITY_BLACKWHITE ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_OUTPUT_QUALITY_CONTRAST ) ) + { + const sal_uLong nMode = static_cast<sal_Int32>(GetActiveWindow()->GetOutDev()->GetDrawMode()); + rSet.Put( SfxBoolItem( SID_OUTPUT_QUALITY_COLOR, sal_uLong(OUTPUT_DRAWMODE_COLOR) == nMode ) ); + rSet.Put( SfxBoolItem( SID_OUTPUT_QUALITY_GRAYSCALE, static_cast<sal_uLong>(OUTPUT_DRAWMODE_GRAYSCALE) == nMode ) ); + rSet.Put( SfxBoolItem( SID_OUTPUT_QUALITY_BLACKWHITE, static_cast<sal_uLong>(OUTPUT_DRAWMODE_BLACKWHITE) == nMode ) ); + rSet.Put( SfxBoolItem( SID_OUTPUT_QUALITY_CONTRAST, static_cast<sal_uLong>(OUTPUT_DRAWMODE_CONTRAST) == nMode ) ); + } + + if ( SfxItemState::DEFAULT == rSet.GetItemState(SID_MAIL_SCROLLBODY_PAGEDOWN) ) + { + rSet.Put( SfxBoolItem( SID_MAIL_SCROLLBODY_PAGEDOWN, true ) ); + } + + if ( SfxItemState::DEFAULT == rSet.GetItemState(SID_ATTR_YEAR2000) ) + { + FmFormShell* pFormShell = GetViewShellBase().GetFormShellManager()->GetFormShell(); + if (pFormShell != nullptr) + { + sal_uInt16 nState = 0; + if (pFormShell->GetY2KState(nState)) + rSet.Put( SfxUInt16Item( SID_ATTR_YEAR2000, nState ) ); + else + rSet.DisableItem( SID_ATTR_YEAR2000 ); + } + } + + if ( !GetView()->GetTextEditOutliner() ) + { + if( !SvtCJKOptions::IsChangeCaseMapEnabled() ) + { + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_HALFWIDTH, false ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_FULLWIDTH, false ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_HIRAGANA, false ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_KATAKANA, false ); + } + else + { + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_HALFWIDTH, true ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_FULLWIDTH, true ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_HIRAGANA, true ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_KATAKANA, true ); + } + + rSet.DisableItem( SID_TRANSLITERATE_SENTENCE_CASE ); + rSet.DisableItem( SID_TRANSLITERATE_TITLE_CASE ); + rSet.DisableItem( SID_TRANSLITERATE_TOGGLE_CASE ); + rSet.DisableItem( SID_TRANSLITERATE_UPPER ); + rSet.DisableItem( SID_TRANSLITERATE_LOWER ); + rSet.DisableItem( SID_TRANSLITERATE_HALFWIDTH ); + rSet.DisableItem( SID_TRANSLITERATE_FULLWIDTH ); + rSet.DisableItem( SID_TRANSLITERATE_HIRAGANA ); + rSet.DisableItem( SID_TRANSLITERATE_KATAKANA ); + } + else + { + if( !SvtCJKOptions::IsChangeCaseMapEnabled() ) + { + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_HALFWIDTH, false ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_FULLWIDTH, false ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_HIRAGANA, false ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_KATAKANA, false ); + rSet.DisableItem( SID_TRANSLITERATE_HALFWIDTH ); + rSet.DisableItem( SID_TRANSLITERATE_FULLWIDTH ); + rSet.DisableItem( SID_TRANSLITERATE_HIRAGANA ); + rSet.DisableItem( SID_TRANSLITERATE_KATAKANA ); + } + else + { + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_HALFWIDTH, true ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_FULLWIDTH, true ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_HIRAGANA, true ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_KATAKANA, true ); + } + } +} + +void DrawViewShell::GetAttrState( SfxItemSet& rSet ) +{ + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + + bool bAttr = false; + SfxAllItemSet aAllSet( *rSet.GetPool() ); + + while ( nWhich ) + { + sal_uInt16 nSlotId = SfxItemPool::IsWhich(nWhich) + ? GetPool().GetSlotId(nWhich) + : nWhich; + switch ( nSlotId ) + { + case SID_ATTR_PARA_ADJUST_LEFT: + { + SfxItemSet aAttrs( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( aAttrs ); + + SvxAdjust eAdj = aAttrs.Get( EE_PARA_JUST ).GetAdjust(); + if ( eAdj == SvxAdjust::Left) + { + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_LEFT, true ) ); + } + + bAttr = true; + + Invalidate(nSlotId); + } + break; + case SID_ATTR_PARA_ADJUST_CENTER: + { + SfxItemSet aAttrs( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( aAttrs ); + + SvxAdjust eAdj = aAttrs.Get( EE_PARA_JUST ).GetAdjust(); + if ( eAdj == SvxAdjust::Center) + { + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_CENTER, true ) ); + } + + bAttr = true; + + Invalidate(nSlotId); + } + break; + case SID_ATTR_PARA_ADJUST_RIGHT: + { + SfxItemSet aAttrs( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( aAttrs ); + + SvxAdjust eAdj = aAttrs.Get( EE_PARA_JUST ).GetAdjust(); + if ( eAdj == SvxAdjust::Right) + { + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_RIGHT, true ) ); + } + + bAttr = true; + + Invalidate(nSlotId); + } + break; + case SID_ATTR_PARA_ADJUST_BLOCK: + { + SfxItemSet aAttrs( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( aAttrs ); + + SvxAdjust eAdj = aAttrs.Get( EE_PARA_JUST ).GetAdjust(); + if ( eAdj == SvxAdjust::Block) + { + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_BLOCK, true ) ); + } + + bAttr = true; + + Invalidate(nSlotId); + } + break; + case SID_ATTR_PARA_LRSPACE: + { + SfxItemSet aAttrs( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( aAttrs ); + SvxLRSpaceItem aLRSpace = aAttrs.Get( EE_PARA_LRSPACE ); + aLRSpace.SetWhich(SID_ATTR_PARA_LRSPACE); + rSet.Put(aLRSpace); + bAttr = true; + Invalidate(SID_ATTR_PARA_LRSPACE); + } + break; + case SID_ATTR_PARA_LINESPACE: + { + SfxItemSet aAttrs( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( aAttrs ); + SvxLineSpacingItem aLineLR = aAttrs.Get( EE_PARA_SBL ); + rSet.Put(aLineLR); + bAttr = true; + Invalidate(SID_ATTR_PARA_LINESPACE); + } + break; + case SID_ATTR_PARA_ULSPACE: + { + SfxItemSet aAttrs( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( aAttrs ); + SvxULSpaceItem aULSP = aAttrs.Get( EE_PARA_ULSPACE ); + aULSP.SetWhich(SID_ATTR_PARA_ULSPACE); + rSet.Put(aULSP); + bAttr = true; + Invalidate(SID_ATTR_PARA_ULSPACE); + } + break; + case SID_ULINE_VAL_NONE: + case SID_ULINE_VAL_SINGLE: + case SID_ULINE_VAL_DOUBLE: + case SID_ULINE_VAL_DOTTED: + { + SfxItemSet aAttrs( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( aAttrs ); + if( aAttrs.GetItemState( EE_CHAR_UNDERLINE ) >= SfxItemState::DEFAULT ) + { + FontLineStyle eLineStyle = aAttrs.Get(EE_CHAR_UNDERLINE).GetLineStyle(); + + switch (nSlotId) + { + case SID_ULINE_VAL_NONE: + rSet.Put(SfxBoolItem(nSlotId, eLineStyle == LINESTYLE_NONE)); + break; + case SID_ULINE_VAL_SINGLE: + rSet.Put(SfxBoolItem(nSlotId, eLineStyle == LINESTYLE_SINGLE)); + break; + case SID_ULINE_VAL_DOUBLE: + rSet.Put(SfxBoolItem(nSlotId, eLineStyle == LINESTYLE_DOUBLE)); + break; + case SID_ULINE_VAL_DOTTED: + rSet.Put(SfxBoolItem(nSlotId, eLineStyle == LINESTYLE_DOTTED)); + break; + } + } + + bAttr = true; + + Invalidate(nSlotId); + } + break; + case SID_ATTR_FILL_STYLE: + case SID_ATTR_FILL_COLOR: + case SID_ATTR_FILL_GRADIENT: + case SID_ATTR_FILL_HATCH: + case SID_ATTR_FILL_BITMAP: + case SID_ATTR_FILL_SHADOW: + case SID_ATTR_SHADOW_COLOR: + case SID_ATTR_SHADOW_TRANSPARENCE: + case SID_ATTR_SHADOW_BLUR: + case SID_ATTR_SHADOW_XDISTANCE: + case SID_ATTR_SHADOW_YDISTANCE: + case SID_ATTR_FILL_USE_SLIDE_BACKGROUND: + case SID_ATTR_FILL_TRANSPARENCE: + case SID_ATTR_FILL_FLOATTRANSPARENCE: + case SID_ATTR_LINE_STYLE: + case SID_ATTR_LINE_DASH: + case SID_ATTR_LINE_WIDTH: + case SID_ATTR_LINE_COLOR: + case SID_ATTR_LINE_TRANSPARENCE: + case SID_ATTR_LINE_JOINT: + case SID_ATTR_LINE_CAP: + case SID_ATTR_TEXT_FITTOSIZE: + case SID_ATTR_CHAR_FONT: + case SID_ATTR_CHAR_FONTHEIGHT: + case SID_ATTR_CHAR_SHADOWED: + case SID_ATTR_CHAR_POSTURE: + case SID_ATTR_CHAR_OVERLINE: + case SID_ATTR_CHAR_UNDERLINE: + case SID_ATTR_CHAR_STRIKEOUT: + case SID_ATTR_CHAR_CONTOUR: + case SID_ATTR_CHAR_WEIGHT: + case SID_ATTR_CHAR_COLOR: + case SID_ATTR_CHAR_KERNING: + case SID_ATTR_CHAR_CASEMAP: + case SID_ATTR_GLOW_COLOR: + case SID_ATTR_GLOW_RADIUS: + case SID_ATTR_GLOW_TRANSPARENCY: + case SID_ATTR_SOFTEDGE_RADIUS: + case SID_SET_SUB_SCRIPT: + case SID_SET_SUPER_SCRIPT: + { + bAttr = true; + } + break; + + case SID_ATTR_TEXTCOLUMNS_NUMBER: + case SID_ATTR_TEXTCOLUMNS_SPACING: + { + SfxItemSet aAttrs(GetDoc()->GetPool()); + mpDrawView->GetAttributes(aAttrs); + const sal_uInt16 nActWhich = nSlotId == SID_ATTR_TEXTCOLUMNS_NUMBER + ? SDRATTR_TEXTCOLUMNS_NUMBER + : SDRATTR_TEXTCOLUMNS_SPACING; + rSet.Put(aAttrs.Get(nActWhich).CloneSetWhich(nSlotId)); + } + break; + + case SID_HYPHENATION: + { + SfxItemSet aAttrs( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( aAttrs ); + if( aAttrs.GetItemState( EE_PARA_HYPHENATE ) >= SfxItemState::DEFAULT ) + { + bool bValue = aAttrs.Get( EE_PARA_HYPHENATE ).GetValue(); + rSet.Put( SfxBoolItem( SID_HYPHENATION, bValue ) ); + } + } + break; + + case SID_STYLE_FAMILY2: + case SID_STYLE_FAMILY3: + case SID_STYLE_FAMILY5: + case SID_STYLE_APPLY: // StyleControl + { + SfxStyleSheet* pStyleSheet = mpDrawView->GetStyleSheet(); + if( pStyleSheet ) + { + if( nSlotId != SID_STYLE_APPLY && !mpDrawView->AreObjectsMarked() ) + { + SfxTemplateItem aTmpItem( nWhich, OUString() ); + aAllSet.Put( aTmpItem, aTmpItem.Which() ); + } + else + { + if (pStyleSheet->GetFamily() == SfxStyleFamily::Page) + pStyleSheet = static_cast<SdStyleSheet*>(pStyleSheet)->GetPseudoStyleSheet(); + + if( pStyleSheet ) + { + SfxStyleFamily eFamily = pStyleSheet->GetFamily(); + + if ((eFamily == SfxStyleFamily::Para && nSlotId == SID_STYLE_FAMILY2) || + (eFamily == SfxStyleFamily::Frame && nSlotId == SID_STYLE_FAMILY3) || + (eFamily == SfxStyleFamily::Pseudo && nSlotId == SID_STYLE_FAMILY5)) + { + SfxTemplateItem aTmpItem ( nWhich, pStyleSheet->GetName() ); + aAllSet.Put( aTmpItem, aTmpItem.Which() ); + } + else + { + SfxTemplateItem aTmpItem(nWhich, OUString()); + aAllSet.Put(aTmpItem,aTmpItem.Which() ); + } + } + } + } + else + { SfxTemplateItem aItem( nWhich, OUString() ); + aAllSet.Put( aItem, aItem.Which() ); + } + } + break; + + case SID_SET_DEFAULT: + { + if( !mpDrawView->GetMarkedObjectList().GetMarkCount() || + ( !mpDrawView->IsTextEdit() && !mpDrawView->GetStyleSheet() ) + ) + rSet.DisableItem( nWhich ); + } + break; + + case SID_REMOVE_HYPERLINK: + { + if (!URLFieldHelper::IsCursorAtURLField(mpDrawView->GetTextEditOutlinerView())) + rSet.DisableItem(nWhich); + } + break; + + case SID_STYLE_WATERCAN: + { + std::unique_ptr<SfxUInt16Item> pFamilyItem; + GetViewFrame()->GetBindings().QueryState(SID_STYLE_FAMILY, pFamilyItem); + if (pFamilyItem && static_cast<SfxStyleFamily>(pFamilyItem->GetValue()) == SfxStyleFamily::Pseudo) + rSet.Put(SfxBoolItem(nWhich,false)); + else + { + SfxBoolItem aItem(nWhich, SD_MOD()->GetWaterCan()); + aAllSet.Put( aItem, aItem.Which()); + } + } + break; + + case SID_STYLE_NEW: + { + std::unique_ptr<SfxUInt16Item> pFamilyItem; + GetViewFrame()->GetBindings().QueryState(SID_STYLE_FAMILY, pFamilyItem); + if (pFamilyItem && static_cast<SfxStyleFamily>(pFamilyItem->GetValue()) == SfxStyleFamily::Pseudo) + { + rSet.DisableItem(nWhich); + } + } + break; + + case SID_STYLE_DRAGHIERARCHIE: + { + std::unique_ptr<SfxUInt16Item> pFamilyItem; + GetViewFrame()->GetBindings().QueryState(SID_STYLE_FAMILY, pFamilyItem); + if (pFamilyItem && static_cast<SfxStyleFamily>(pFamilyItem->GetValue()) == SfxStyleFamily::Pseudo) + rSet.DisableItem(nWhich); + } + break; + + case SID_STYLE_NEW_BY_EXAMPLE: + { + // It is not possible to create PseudoStyleSheets 'by Example'; + // normal style sheets need a selected object for that + + std::unique_ptr<SfxUInt16Item> pFamilyItem; + GetViewFrame()->GetBindings().QueryState(SID_STYLE_FAMILY, pFamilyItem); + if (pFamilyItem) + { + if (static_cast<SfxStyleFamily>(pFamilyItem->GetValue()) == SfxStyleFamily::Pseudo) + { + rSet.DisableItem(nWhich); + } + else if (static_cast<SfxStyleFamily>(pFamilyItem->GetValue()) == SfxStyleFamily::Para) + { + if (!mpDrawView->AreObjectsMarked()) + { + rSet.DisableItem(nWhich); + } + } + } + // if there is no (yet) a style designer, we have to go back into the + // view state; an actual set family can not be considered + else + { + if (!mpDrawView->AreObjectsMarked()) + { + rSet.DisableItem(nWhich); + } + } + } + break; + + case SID_STYLE_UPDATE_BY_EXAMPLE: + { + if (!mpDrawView->AreObjectsMarked()) + { + rSet.DisableItem(nWhich); + } + } + break; + case FN_BUL_NUM_RULE_INDEX: + case FN_NUM_NUM_RULE_INDEX: + { + SfxItemSet aEditAttr( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( aEditAttr ); + + SfxItemSetFixed<EE_ITEMS_START, EE_ITEMS_END> aNewAttr( GetPool() ); + aNewAttr.Put( aEditAttr, false ); + + std::unique_ptr<SvxNumRule> pNumRule; + const SfxPoolItem* pTmpItem=nullptr; + TypedWhichId<SvxNumBulletItem> nNumItemId = SID_ATTR_NUMBERING_RULE; + sal_uInt16 nActNumLvl = mpDrawView->GetSelectionLevel(); + pTmpItem=GetNumBulletItem(aNewAttr, nNumItemId); + + if (pTmpItem) + pNumRule.reset(new SvxNumRule(static_cast<const SvxNumBulletItem*>(pTmpItem)->GetNumRule())); + + if ( pNumRule ) + { + sal_uInt16 nMask = 1; + sal_uInt16 nCount = 0; + sal_uInt16 nCurLevel = sal_uInt16(0xFFFF); + for(sal_uInt16 i = 0; i < pNumRule->GetLevelCount(); i++) + { + if(nActNumLvl & nMask) + { + nCount++; + nCurLevel = i; + } + nMask <<= 1; + } + if ( nCount == 1 ) + { + const SvxNumberFormat* pNumFmt = pNumRule->Get(nCurLevel); + if ( pNumFmt ) + { + bool bBullets = false; + switch(pNumFmt->GetNumberingType()) + { + case SVX_NUM_CHAR_SPECIAL: + case SVX_NUM_BITMAP: + bBullets = true; + break; + + default: + bBullets = false; + } + + rSet.Put(SfxUInt16Item(FN_BUL_NUM_RULE_INDEX,sal_uInt16(0xFFFF))); + rSet.Put(SfxUInt16Item(FN_NUM_NUM_RULE_INDEX,sal_uInt16(0xFFFF))); + if ( bBullets ) + { + NBOTypeMgrBase* pBullets = NBOutlineTypeMgrFact::CreateInstance(NBOType::Bullets); + if ( pBullets ) + { + sal_uInt16 nBulIndex = pBullets->GetNBOIndexForNumRule(*pNumRule,nActNumLvl); + rSet.Put(SfxUInt16Item(FN_BUL_NUM_RULE_INDEX,nBulIndex)); + } + }else + { + NBOTypeMgrBase* pNumbering = NBOutlineTypeMgrFact::CreateInstance(NBOType::Numbering); + if ( pNumbering ) + { + sal_uInt16 nBulIndex = pNumbering->GetNBOIndexForNumRule(*pNumRule,nActNumLvl); + rSet.Put(SfxUInt16Item(FN_NUM_NUM_RULE_INDEX,nBulIndex)); + } + } + } + } + } + } + break; + case FN_NUM_BULLET_ON: + case FN_NUM_NUMBERING_ON: + { + bool bEnable = false; + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + const size_t nMarkCount = rMarkList.GetMarkCount(); + for (size_t nIndex = 0; nIndex < nMarkCount; ++nIndex) + { + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >(rMarkList.GetMark(nIndex)->GetMarkedSdrObj()); + if (pTextObj && pTextObj->GetObjInventor() == SdrInventor::Default) + { + if (pTextObj->GetObjIdentifier() != SdrObjKind::OLE2) + { + bEnable = true; + break; + } + } + } + if (bEnable) + { + rSet.Put(SfxBoolItem(FN_NUM_BULLET_ON, false)); + rSet.Put(SfxBoolItem(FN_NUM_NUMBERING_ON, false)); + } + else + { + rSet.DisableItem(FN_NUM_BULLET_ON); + rSet.DisableItem(FN_NUM_NUMBERING_ON); + } + } + break; + } + nWhich = aIter.NextWhich(); + } + + std::optional<SfxItemSet> pSet; + + if( bAttr ) + { + pSet.emplace( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( *pSet ); + rSet.Put( *pSet, false ); + } + + rSet.Put( aAllSet, false ); + + // there were changes at area and/or line attributes + if( !(bAttr && pSet) ) + return; + + // if the view owns selected objects, corresponding items have to be + // changed from SfxItemState::DEFAULT (_ON) to SfxItemState::DISABLED + if( mpDrawView->AreObjectsMarked() ) + { + SfxWhichIter aNewIter( *pSet ); + nWhich = aNewIter.FirstWhich(); + while( nWhich ) + { + if (nWhich >= XATTR_LINE_FIRST && nWhich <= XATTR_LINE_LAST + && SfxItemState::DEFAULT == aNewIter.GetItemState() ) + { + rSet.ClearItem( nWhich ); + rSet.DisableItem( nWhich ); + } + nWhich = aNewIter.NextWhich(); + } + } + + SfxItemState eState = pSet->GetItemState( EE_PARA_LRSPACE ); + if ( eState == SfxItemState::DONTCARE ) + { + rSet.InvalidateItem(EE_PARA_LRSPACE); + rSet.InvalidateItem(SID_ATTR_PARA_LRSPACE); + } + eState = pSet->GetItemState( EE_PARA_SBL ); + if ( eState == SfxItemState::DONTCARE ) + { + rSet.InvalidateItem(EE_PARA_SBL); + rSet.InvalidateItem(SID_ATTR_PARA_LINESPACE); + } + eState = pSet->GetItemState( EE_PARA_ULSPACE ); + if ( eState == SfxItemState::DONTCARE ) + { + rSet.InvalidateItem(EE_PARA_ULSPACE); + rSet.InvalidateItem(SID_ATTR_PARA_ULSPACE); + } + + SvxEscapement eEsc = static_cast<SvxEscapement>(pSet->Get( EE_CHAR_ESCAPEMENT ).GetEnumValue()); + rSet.Put(SfxBoolItem(SID_SET_SUPER_SCRIPT, eEsc == SvxEscapement::Superscript)); + rSet.Put(SfxBoolItem(SID_SET_SUB_SCRIPT, eEsc == SvxEscapement::Subscript)); + + eState = pSet->GetItemState( EE_CHAR_KERNING ); + if ( eState == SfxItemState::DONTCARE ) + { + rSet.InvalidateItem(EE_CHAR_KERNING); + rSet.InvalidateItem(SID_ATTR_CHAR_KERNING); + } +} + +OUString DrawViewShell::GetSelectionText(bool bCompleteWords) +{ + OUString aStrSelection; + ::Outliner* pOl = mpDrawView->GetTextEditOutliner(); + OutlinerView* pOlView = mpDrawView->GetTextEditOutlinerView(); + + if (pOl && pOlView) + { + if (bCompleteWords) + { + ESelection aSel = pOlView->GetSelection(); + OUString aStrCurrentDelimiters = pOl->GetWordDelimiters(); + + pOl->SetWordDelimiters(" .,;\"'"); + aStrSelection = pOl->GetWord( aSel.nEndPara, aSel.nEndPos ); + pOl->SetWordDelimiters( aStrCurrentDelimiters ); + } + else + { + aStrSelection = pOlView->GetSelected(); + } + } + + return aStrSelection; +} + +bool DrawViewShell::HasSelection(bool bText) const +{ + bool bReturn = false; + + if (bText) + { + OutlinerView* pOlView = mpDrawView->GetTextEditOutlinerView(); + + if (pOlView && !pOlView->GetSelected().isEmpty()) + { + bReturn = true; + } + } + else if (mpDrawView->GetMarkedObjectList().GetMarkCount() != 0) + { + bReturn = true; + } + + return bReturn; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviewsg.cxx b/sd/source/ui/view/drviewsg.cxx new file mode 100644 index 000000000..e3930fa7e --- /dev/null +++ b/sd/source/ui/view/drviewsg.cxx @@ -0,0 +1,232 @@ +/* -*- 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 <DrawViewShell.hxx> +#include <ViewShellImplementation.hxx> + +#include <svx/svxids.hrc> +#include <svx/imapdlg.hxx> +#include <sfx2/request.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/svdograf.hxx> +#include <svx/ImageMapInfo.hxx> + +#include <app.hrc> + +#include <drawdoc.hxx> +#include <sdmod.hxx> +#include <optsitem.hxx> +#include <FrameView.hxx> +#include <drawview.hxx> + +namespace sd { + +void DrawViewShell::ExecIMap( SfxRequest const & rReq ) +{ + // during a slide show, nothing is executed! + if(HasCurrentFunction(SID_PRESENTATION) ) + return; + + if ( rReq.GetSlot() != SID_IMAP_EXEC ) + return; + + SdrMark* pMark = mpDrawView->GetMarkedObjectList().GetMark(0); + + if ( !pMark ) + return; + + SdrObject* pSdrObj = pMark->GetMarkedSdrObj(); + SvxIMapDlg* pDlg = ViewShell::Implementation::GetImageMapDialog(); + + if ( pDlg->GetEditingObject() == static_cast<void*>(pSdrObj) ) + { + const ImageMap& rImageMap = pDlg->GetImageMap(); + SvxIMapInfo* pIMapInfo = SvxIMapInfo::GetIMapInfo( pSdrObj ); + + if ( !pIMapInfo ) + pSdrObj->AppendUserData( std::unique_ptr<SdrObjUserData>(new SvxIMapInfo( rImageMap )) ); + else + pIMapInfo->SetImageMap( rImageMap ); + + GetDoc()->SetChanged(); + } +} + +void DrawViewShell::GetIMapState( SfxItemSet& rSet ) +{ + bool bDisable = true; + + if( GetViewFrame()->HasChildWindow( SvxIMapDlgChildWindow::GetChildWindowId() ) ) + { + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + + if ( rMarkList.GetMarkCount() == 1 ) + { + const SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj(); + + SvxIMapDlg* pImageMapDialog = ViewShell::Implementation::GetImageMapDialog(); + if ( ( dynamic_cast< const SdrGrafObj *>( pObj ) != nullptr /*|| pObj->ISA( SdrOle2Obj )*/ ) + && pImageMapDialog!=nullptr + && ( pImageMapDialog->GetEditingObject() == static_cast<void const *>(pObj) ) ) + { + bDisable = false; + } + } + } + + rSet.Put( SfxBoolItem( SID_IMAP_EXEC, bDisable ) ); +} + +void DrawViewShell::ExecOptionsBar( SfxRequest& rReq ) +{ + // during a slide show, nothing is executed! + if(HasCurrentFunction(SID_PRESENTATION)) + return; + + bool bDefault = false; + sal_uInt16 nSlot = rReq.GetSlot(); + + SdOptions* pOptions = SD_MOD()->GetSdOptions(GetDoc()->GetDocumentType()); + + switch( nSlot ) + { + case SID_SOLID_CREATE: + pOptions->SetSolidDragging( !mpDrawView->IsSolidDragging() ); + break; + + // Grid- / Help lines option + case SID_GRID_VISIBLE: // not here yet! + { + pOptions->SetGridVisible( !mpDrawView->IsGridVisible() ); + } + break; + + case SID_GRID_USE: + { + pOptions->SetUseGridSnap( !mpDrawView->IsGridSnap() ); + } + break; + + case SID_HELPLINES_VISIBLE: // not here yet! + { + pOptions->SetHelplines( !mpDrawView->IsHlplVisible() ); + } + break; + + case SID_HELPLINES_USE: + { + pOptions->SetSnapHelplines( !mpDrawView->IsHlplSnap() ); + } + break; + + case SID_HELPLINES_MOVE: + { + pOptions->SetDragStripes( !mpDrawView->IsDragStripes() ); + } + break; + + case SID_SNAP_BORDER: + { + pOptions->SetSnapBorder( !mpDrawView->IsBordSnap() ); + } + break; + + case SID_SNAP_FRAME: + { + pOptions->SetSnapFrame( !mpDrawView->IsOFrmSnap() ); + } + break; + + case SID_SNAP_POINTS: + { + pOptions->SetSnapPoints( !mpDrawView->IsOPntSnap() ); + } + break; + + case SID_QUICKEDIT: + { + pOptions->SetQuickEdit( !mpDrawView->IsQuickTextEditMode() ); + } + break; + + case SID_PICK_THROUGH: + { + pOptions->SetPickThrough( + !mpDrawView->GetModel()->IsPickThroughTransparentTextFrames() ); + } + break; + + case SID_DOUBLECLICK_TEXTEDIT: + { + pOptions->SetDoubleClickTextEdit( !mpFrameView->IsDoubleClickTextEdit() ); + } + break; + + case SID_CLICK_CHANGE_ROTATION: + { + pOptions->SetClickChangeRotation( !mpFrameView->IsClickChangeRotation() ); + } + break; + + default: + bDefault = true; + break; + } + + if( bDefault ) + return; + + pOptions->StoreConfig(); + + // Saves the configuration IMMEDIATELY + // SfxGetpApp()->SaveConfiguration(); + WriteFrameViewData(); + + mpFrameView->Update( pOptions ); + ReadFrameViewData( mpFrameView ); + + Invalidate( nSlot ); + rReq.Done(); + +} + +void DrawViewShell::GetOptionsBarState( SfxItemSet& rSet ) +{ + rSet.Put( SfxBoolItem( SID_SOLID_CREATE, mpDrawView->IsSolidDragging() ) ); + rSet.Put( SfxBoolItem( SID_GRID_VISIBLE, mpDrawView->IsGridVisible() ) ); + rSet.Put( SfxBoolItem( SID_GRID_USE, mpDrawView->IsGridSnap() ) ); + rSet.Put( SfxBoolItem( SID_HELPLINES_VISIBLE, mpDrawView->IsHlplVisible() ) ); + rSet.Put( SfxBoolItem( SID_HELPLINES_USE, mpDrawView->IsHlplSnap() ) ); + rSet.Put( SfxBoolItem( SID_HELPLINES_MOVE, mpDrawView->IsDragStripes() ) ); + + rSet.Put( SfxBoolItem( SID_SNAP_BORDER, mpDrawView->IsBordSnap() ) ); + rSet.Put( SfxBoolItem( SID_SNAP_FRAME, mpDrawView->IsOFrmSnap() ) ); + rSet.Put( SfxBoolItem( SID_SNAP_POINTS, mpDrawView->IsOPntSnap() ) ); + + rSet.Put( SfxBoolItem( SID_QUICKEDIT, mpDrawView->IsQuickTextEditMode() ) ); + rSet.Put( SfxBoolItem( SID_PICK_THROUGH, + mpDrawView->GetModel()->IsPickThroughTransparentTextFrames() ) ); + + rSet.Put( SfxBoolItem( SID_DOUBLECLICK_TEXTEDIT, mpFrameView->IsDoubleClickTextEdit() ) ); + rSet.Put( SfxBoolItem( SID_CLICK_CHANGE_ROTATION, mpFrameView->IsClickChangeRotation() ) ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviewsh.cxx b/sd/source/ui/view/drviewsh.cxx new file mode 100644 index 000000000..c0e09a478 --- /dev/null +++ b/sd/source/ui/view/drviewsh.cxx @@ -0,0 +1,203 @@ +/* -*- 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 <DrawViewShell.hxx> + +#include <sal/log.hxx> +#include <rtl/math.hxx> +#include <comphelper/lok.hxx> + +#include <DrawDocShell.hxx> + +#include <slideshow.hxx> + +namespace sd { + +void DrawViewShell::GotoBookmark(std::u16string_view rBookmark) +{ + ::sd::DrawDocShell* pDocSh = GetDocSh(); + if( pDocSh ) + { + if( !pDocSh->GetViewShell() ) //#i26016# this case occurs if the jump-target-document was opened already with file open dialog before triggering the jump via hyperlink + pDocSh->Connect(this); + pDocSh->GotoBookmark(rBookmark); + } +} + +/** + * Make area visible (scroll part of picture) +|* +\************************************************************************/ + +void DrawViewShell::MakeVisible(const ::tools::Rectangle& rRect, vcl::Window& rWin) +{ + if ( (IsMouseButtonDown() && !IsMouseSelecting()) || SlideShow::IsRunning( GetViewShellBase() ) ) + return; + + // tdf#98646 check if Rectangle which contains the bounds of the region to + // be shown eventually contains values that cause overflows when processing + // e.g. when calling GetWidth() + const bool bOverflowInX(!rtl::math::approxEqual(static_cast<double>(rRect.getWidth()), static_cast<double>(rRect.Right()) - static_cast<double>(rRect.Left()))); + const bool bOverflowInY(!rtl::math::approxEqual(static_cast<double>(rRect.getHeight()), static_cast<double>(rRect.Bottom()) - static_cast<double>(rRect.Top()))); + + if(bOverflowInX || bOverflowInY) + { + SAL_WARN("sd", "The given Rectangle contains values that lead to numerical overflows (!)"); + return; + } + + // In older versions, if in X or Y the size of the object was + // smaller than the visible area, the user-defined zoom was + // changed. This was decided to be a bug for + // StarOffice 6.x (Apr 2002), thus I developed a + // version which instead handles X/Y bigger/smaller and visibility + // questions separately + const Size aLogicSize(rRect.GetSize()); + + // visible area + Size aVisSizePixel(rWin.GetOutputSizePixel()); + bool bTiledRendering = comphelper::LibreOfficeKit::isActive() && !rWin.IsMapModeEnabled(); + if (bTiledRendering) + { + rWin.GetOutDev()->Push(vcl::PushFlags::MAPMODE); + rWin.EnableMapMode(); + } + ::tools::Rectangle aVisArea(rWin.PixelToLogic(::tools::Rectangle(Point(0,0), aVisSizePixel))); + if (bTiledRendering) + rWin.GetOutDev()->Pop(); + Size aVisAreaSize(aVisArea.GetSize()); + + if ( aVisArea.Contains(rRect) ) + return; + + // object is not entirely in visible area + sal_Int32 nFreeSpaceX(aVisAreaSize.Width() - aLogicSize.Width()); + sal_Int32 nFreeSpaceY(aVisAreaSize.Height() - aLogicSize.Height()); + + // allow a mode for move-only visibility without zooming. + const sal_Int32 nPercentBorder(30); + const ::tools::Rectangle aInnerRectangle( + aVisArea.Left() + ((aVisAreaSize.Width() * nPercentBorder) / 200), + aVisArea.Top() + ((aVisAreaSize.Height() * nPercentBorder) / 200), + aVisArea.Right() - ((aVisAreaSize.Width() * nPercentBorder) / 200), + aVisArea.Bottom() - ((aVisAreaSize.Height() * nPercentBorder) / 200) + ); + Point aNewPos(aVisArea.TopLeft()); + + if(nFreeSpaceX < 0) + { + if(aInnerRectangle.Left() > rRect.Right()) + { + // object moves out to the left + aNewPos.AdjustX( -(aVisAreaSize.Width() / 2) ); + } + + if(aInnerRectangle.Right() < rRect.Left()) + { + // object moves out to the right + aNewPos.AdjustX(aVisAreaSize.Width() / 2 ); + } + } + else + { + if(nFreeSpaceX > rRect.GetWidth()) + { + nFreeSpaceX = rRect.GetWidth(); + } + + if(nFreeSpaceX <= 0) + { + SAL_WARN("sd", "The given Rectangle contains values that lead to numerical overflows (!)"); + } + else + { + const ::tools::Long distRight(rRect.Right() - aNewPos.X() - aVisAreaSize.Width()); + + if(distRight > 0) + { + ::tools::Long mult = (distRight / nFreeSpaceX) + 1; + aNewPos.AdjustX(mult * nFreeSpaceX ); + } + + const ::tools::Long distLeft(aNewPos.X() - rRect.Left()); + + if(distLeft > 0) + { + ::tools::Long mult = (distLeft / nFreeSpaceX) + 1; + aNewPos.AdjustX( -(mult * nFreeSpaceX) ); + } + } + } + + if(nFreeSpaceY < 0) + { + if(aInnerRectangle.Top() > rRect.Bottom()) + { + // object moves out to the top + aNewPos.AdjustY( -(aVisAreaSize.Height() / 2) ); + } + + if(aInnerRectangle.Bottom() < rRect.Top()) + { + // object moves out to the right + aNewPos.AdjustY(aVisAreaSize.Height() / 2 ); + } + } + else + { + if(nFreeSpaceY > rRect.GetHeight()) + { + nFreeSpaceY = rRect.GetHeight(); + } + + if(nFreeSpaceY <= 0) + { + SAL_WARN("sd", "The given Rectangle contains values that lead to numerical overflows (!)"); + } + else + { + const ::tools::Long distBottom(rRect.Bottom() - aNewPos.Y() - aVisAreaSize.Height()); + + if(distBottom > 0) + { + ::tools::Long mult = (distBottom / nFreeSpaceY) + 1; + aNewPos.AdjustY(mult * nFreeSpaceY ); + } + + const ::tools::Long distTop(aNewPos.Y() - rRect.Top()); + + if(distTop > 0) + { + ::tools::Long mult = (distTop / nFreeSpaceY) + 1; + aNewPos.AdjustY( -(mult * nFreeSpaceY) ); + } + } + } + + // did position change? Does it need to be set? + if(aNewPos != aVisArea.TopLeft()) + { + aVisArea.SetPos(aNewPos); + SetZoomRect(aVisArea); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviewsi.cxx b/sd/source/ui/view/drviewsi.cxx new file mode 100644 index 000000000..039840824 --- /dev/null +++ b/sd/source/ui/view/drviewsi.cxx @@ -0,0 +1,165 @@ +/* -*- 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 <DrawViewShell.hxx> +#include <svx/xfillit0.hxx> +#include <editeng/eeitem.hxx> +#include <sfx2/request.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/svxids.hrc> +#include <sfx2/dispatch.hxx> +#include <svx/float3d.hxx> +#include <svx/f3dchild.hxx> +#include <vcl/weld.hxx> + +#include <strings.hrc> + +#include <drawdoc.hxx> +#include <Window.hxx> +#include <sdresid.hxx> + +using namespace ::com::sun::star; + +namespace sd { + +/** + * Handle SfxRequests for EffekteWindow + */ +void DrawViewShell::ExecEffectWin( SfxRequest& rReq ) +{ + CheckLineTo (rReq); + + sal_uInt16 nSId = rReq.GetSlot(); + + switch( nSId ) + { + case SID_3D_INIT: + { + sal_uInt16 nId = Svx3DChildWindow::GetChildWindowId(); + SfxChildWindow* pWindow = GetViewFrame()->GetChildWindow( nId ); + if( pWindow ) + { + Svx3DWin* p3DWin = static_cast<Svx3DWin*>( pWindow->GetWindow() ); + if( p3DWin ) + p3DWin->InitColorLB(); + } + } + break; + + case SID_3D_STATE: + { + Update3DWindow(); + } + break; + + case SID_3D_ASSIGN: + { + AssignFrom3DWindow(); + } + break; + + } +} + +void DrawViewShell::Update3DWindow() +{ + sal_uInt16 nId = Svx3DChildWindow::GetChildWindowId(); + SfxChildWindow* pWindow = GetViewFrame()->GetChildWindow( nId ); + if( pWindow ) + { + Svx3DWin* p3DWin = static_cast<Svx3DWin*>( pWindow->GetWindow() ); + if( p3DWin && p3DWin->IsUpdateMode() ) + { + SfxItemSet aTmpItemSet = GetView()->Get3DAttributes(); + p3DWin->Update( aTmpItemSet ); + } + } +} + +/*----------------------------------------------------------------------------*/ + +void DrawViewShell::AssignFrom3DWindow() +{ + sal_uInt16 nId = Svx3DChildWindow::GetChildWindowId(); + SfxChildWindow* pWin = GetViewFrame()->GetChildWindow( nId ); + if( !pWin ) + return; + + Svx3DWin* p3DWin = static_cast<Svx3DWin*>( pWin->GetWindow() ); + if( !(p3DWin && GetView()) ) + return; + + if(!GetView()->IsPresObjSelected()) + { + SfxItemSetFixed<SDRATTR_START, SDRATTR_END> aSet( GetDoc()->GetPool() ); + p3DWin->GetAttr( aSet ); + + // own UNDO-compounding also around transformation in 3D + GetView()->BegUndo(SdResId(STR_UNDO_APPLY_3D_FAVOURITE)); + + if(GetView()->IsConvertTo3DObjPossible()) + { + // assign only text-attribute + SfxItemSetFixed<EE_ITEMS_START, EE_ITEMS_END> aTextSet( GetDoc()->GetPool() ); + aTextSet.Put( aSet, false ); + GetView()->SetAttributes( aTextSet ); + + // transform text into 3D + sal_uInt16 nSId = SID_CONVERT_TO_3D; + SfxBoolItem aItem( nSId, true ); + GetViewFrame()->GetDispatcher()->ExecuteList( + nSId, SfxCallMode::SYNCHRON | SfxCallMode::RECORD, + { &aItem }); + + // Determine if a FILL attribute is set. + // If not, hard set a fill attribute + drawing::FillStyle eFillStyle = aSet.Get(XATTR_FILLSTYLE).GetValue(); + if(eFillStyle == drawing::FillStyle_NONE) + aSet.Put(XFillStyleItem (drawing::FillStyle_SOLID)); + + // remove some 3DSCENE attributes since these were + // created by convert to 3D and may not be changed + // to the defaults again. + aSet.ClearItem(SDRATTR_3DSCENE_DISTANCE); + aSet.ClearItem(SDRATTR_3DSCENE_FOCAL_LENGTH); + aSet.ClearItem(SDRATTR_3DOBJ_DEPTH); + } + + // assign attribute + GetView()->Set3DAttributes( aSet ); + + // end UNDO + GetView()->EndUndo(); + } + else + { + vcl::Window* pWindow = GetActiveWindow(); + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pWindow ? pWindow->GetFrameWeld() : nullptr, + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + + // get focus back + GetActiveWindow()->GrabFocus(); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviewsj.cxx b/sd/source/ui/view/drviewsj.cxx new file mode 100644 index 000000000..a1a7d899f --- /dev/null +++ b/sd/source/ui/view/drviewsj.cxx @@ -0,0 +1,567 @@ +/* -*- 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 <DrawViewShell.hxx> +#include <com/sun/star/embed/EmbedMisc.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/presentation/ClickAction.hpp> +#include <sfx2/objsh.hxx> +#include <svx/svxids.hrc> +#include <svx/sdmetitm.hxx> +#include <editeng/flditem.hxx> +#include <svx/svdogrp.hxx> +#include <svx/svdograf.hxx> +#include <svx/svdoole2.hxx> +#include <svx/sdtfsitm.hxx> +#include <svx/svdopath.hxx> +#include <svx/obj3d.hxx> +#include <svx/scene3d.hxx> + +#include <app.hrc> + +#include <anminfo.hxx> +#include <drawdoc.hxx> +#include <drawview.hxx> + +using namespace com::sun::star; + +namespace sd { + +/** + * Set state (Enabled/Disabled) of Menu-SfxSlots + */ +void DrawViewShell::GetMenuStateSel( SfxItemSet &rSet ) +{ + // Status of menu entries (Buttons,...) + + // Single selection + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + const size_t nMarkCount = rMarkList.GetMarkCount(); + + if ( nMarkCount == 1 ) + { + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_BEZIER_EDIT ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_UNGROUP ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_ENTER_GROUP ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_NAME_GROUP ) || + + // #i68101# + SfxItemState::DEFAULT == rSet.GetItemState( SID_OBJECT_TITLE_DESCRIPTION ) || + + SfxItemState::DEFAULT == rSet.GetItemState( SID_ATTR_FILL_STYLE ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_ATTR_FILL_USE_SLIDE_BACKGROUND ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_ATTR_FILL_TRANSPARENCE ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_ATTR_FILL_FLOATTRANSPARENCE ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_CHANGEBEZIER ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_CHANGEPOLYGON ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_LINEEND_POLYGON ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_MEASURE_DLG ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_CONNECTION_DLG ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_CONNECTION_NEW_ROUTING ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_OBJECT_SHEAR ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_OBJECT_ALIGN_LEFT ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_OBJECT_ALIGN_CENTER ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_OBJECT_ALIGN_RIGHT ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_OBJECT_ALIGN_UP ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_OBJECT_ALIGN_MIDDLE ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_OBJECT_ALIGN_DOWN ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_FRAME_TO_TOP ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_MOREFRONT ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_FRAME_UP ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_MOREBACK ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_FRAME_DOWN ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_FRAME_TO_BOTTOM ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_BEFORE_OBJ ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_BEHIND_OBJ ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_REVERSE_ORDER ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_ORIGINAL_SIZE ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_SAVE_GRAPHIC ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_COMPRESS_GRAPHIC ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_TEXTATTR_DLG ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_EXECUTE_ANIMATION_EFFECT )) + { + const SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + const SdrGrafObj* pSdrGrafObj = dynamic_cast< const SdrGrafObj* >(pObj); + const SdrOle2Obj* pSdrOle2Obj = dynamic_cast< const SdrOle2Obj* >(pObj); + const SdAnimationInfo* pAnimationInfo + = SdDrawDocument::GetAnimationInfo(rMarkList.GetMark(0)->GetMarkedSdrObj()); + SdrInventor nInv = pObj->GetObjInventor(); + SdrObjKind nId = pObj->GetObjIdentifier(); + SdrObjTransformInfoRec aInfoRec; + pObj->TakeObjInfo( aInfoRec ); + + // don't show original size entry if not possible + if(pSdrOle2Obj) + { + if (pSdrOle2Obj->GetObjRef().is() && + (pSdrOle2Obj->GetObjRef()->getStatus( pSdrOle2Obj->GetAspect() ) & embed::EmbedMisc::MS_EMBED_RECOMPOSEONRESIZE) ) + rSet.DisableItem(SID_ORIGINAL_SIZE); + } + + if(!pSdrGrafObj) + { + rSet.DisableItem(SID_SAVE_GRAPHIC); + rSet.DisableItem(SID_COMPRESS_GRAPHIC); + } + + if (!pAnimationInfo + || pAnimationInfo->meClickAction == presentation::ClickAction::ClickAction_NONE + // Sound does not work in edit mode + || pAnimationInfo->meClickAction == presentation::ClickAction::ClickAction_SOUND + // No point in exiting the presentation in edit mode + || pAnimationInfo->meClickAction + == presentation::ClickAction::ClickAction_STOPPRESENTATION) + { + rSet.DisableItem(SID_EXECUTE_ANIMATION_EFFECT); + } + + /* If it is not a group object or 3D object, we disable "enter + group". */ + const auto* pSdrObjGroup = dynamic_cast<const SdrObjGroup*>(pObj); + + if( !( ( pSdrObjGroup != nullptr && nInv == SdrInventor::Default ) || + ( dynamic_cast< const E3dScene* >(pObj) != nullptr ) ) ) + { + rSet.DisableItem( SID_ENTER_GROUP ); + } + + // Don't allow enter Diagrams + if(nullptr != pSdrObjGroup && pSdrObjGroup->isDiagram()) + { + rSet.DisableItem( SID_ENTER_GROUP ); + } + + // If it is not a group object, we disable "ungroup" + if(pSdrObjGroup == nullptr || nInv != SdrInventor::Default) + { + rSet.DisableItem(SID_UNGROUP); + } + + // Support advanced DiagramHelper + if(!pSdrObjGroup || !pSdrObjGroup->isDiagram()) + { + rSet.DisableItem( SID_REGENERATE_DIAGRAM ); + rSet.DisableItem( SID_EDIT_DIAGRAM ); + } + + if( nInv == SdrInventor::Default && + (nId == SdrObjKind::Line || + nId == SdrObjKind::PolyLine || + nId == SdrObjKind::PathLine || + nId == SdrObjKind::FreehandLine )) + { + //rSet.DisableItem( SID_ATTRIBUTES_AREA ); // remove again! + rSet.DisableItem( SID_ATTR_FILL_STYLE ); + rSet.DisableItem( SID_ATTR_FILL_USE_SLIDE_BACKGROUND ); + rSet.DisableItem( SID_ATTR_FILL_TRANSPARENCE ); + rSet.DisableItem( SID_ATTR_FILL_FLOATTRANSPARENCE ); + } + if( (dynamic_cast< const SdrPathObj *>( pObj ) == nullptr&& !aInfoRec.bCanConvToPath) || dynamic_cast< const SdrObjGroup *>( pObj ) != nullptr ) // As long as JOE handles it incorrectly! + { // JOE: a group object may can be converted into a PathObj + rSet.DisableItem( SID_LINEEND_POLYGON ); + } + if(nInv == SdrInventor::Default && + (nId == SdrObjKind::PathFill || nId == SdrObjKind::PathLine || !aInfoRec.bCanConvToPath)) + rSet.DisableItem( SID_CHANGEBEZIER ); + + if( nInv == SdrInventor::Default && + ( nId == SdrObjKind::Polygon || nId == SdrObjKind::PolyLine || !aInfoRec.bCanConvToPoly ) && + !GetView()->IsVectorizeAllowed() ) + { + rSet.DisableItem( SID_CHANGEPOLYGON ); + } + + if(nInv == SdrInventor::Default && nId == SdrObjKind::Table ) + { + rSet.DisableItem( SID_TEXTATTR_DLG ); + } + + if( nInv != SdrInventor::Default || nId != SdrObjKind::Measure ) + rSet.DisableItem( SID_MEASURE_DLG ); + + if( nInv != SdrInventor::Default || nId != SdrObjKind::Edge ) + rSet.DisableItem( SID_CONNECTION_DLG ); + else + { + bool bDisable = true; + SfxItemSet aAttrSet( GetDoc()->GetPool() ); + GetView()->GetAttributes( aAttrSet ); + + if( aAttrSet.GetItemState( SDRATTR_EDGELINE1DELTA ) >= SfxItemState::DEFAULT && + aAttrSet.GetItemState( SDRATTR_EDGELINE2DELTA ) >= SfxItemState::DEFAULT && + aAttrSet.GetItemState( SDRATTR_EDGELINE3DELTA ) >= SfxItemState::DEFAULT ) + { + ::tools::Long nVal1 = aAttrSet.Get( SDRATTR_EDGELINE1DELTA ).GetValue(); + ::tools::Long nVal2 = aAttrSet.Get( SDRATTR_EDGELINE2DELTA ).GetValue(); + ::tools::Long nVal3 = aAttrSet.Get( SDRATTR_EDGELINE3DELTA ).GetValue(); + { + if( nVal1 != 0 || nVal2 != 0 || nVal3 != 0 ) + bDisable = false; + } + } + if( bDisable ) + rSet.DisableItem( SID_CONNECTION_NEW_ROUTING ); + } + + if ( nInv == SdrInventor::E3d || + (!mpDrawView->IsConvertToPathObjPossible() && + !mpDrawView->IsShearAllowed() && + !mpDrawView->IsDistortAllowed()) ) + { + rSet.DisableItem( SID_OBJECT_SHEAR ); + } + + if(dynamic_cast< const E3dCompoundObject *>( pObj ) != nullptr) + { + rSet.DisableItem( SID_OBJECT_ALIGN ); + rSet.DisableItem( SID_OBJECT_ALIGN_LEFT ); + rSet.DisableItem( SID_OBJECT_ALIGN_CENTER ); + rSet.DisableItem( SID_OBJECT_ALIGN_RIGHT ); + rSet.DisableItem( SID_OBJECT_ALIGN_UP ); + rSet.DisableItem( SID_OBJECT_ALIGN_MIDDLE ); + rSet.DisableItem( SID_OBJECT_ALIGN_DOWN ); + rSet.DisableItem( SID_FRAME_TO_TOP ); + rSet.DisableItem( SID_MOREFRONT ); + rSet.DisableItem( SID_FRAME_UP ); + rSet.DisableItem( SID_MOREBACK ); + rSet.DisableItem( SID_FRAME_DOWN ); + rSet.DisableItem( SID_FRAME_TO_BOTTOM ); + rSet.DisableItem( SID_BEFORE_OBJ ); + rSet.DisableItem( SID_BEHIND_OBJ ); + rSet.DisableItem( SID_REVERSE_ORDER ); + rSet.DisableItem( SID_POSITION ); + } + } + + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_DISMANTLE ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_BREAK ) ) + { + if ( !mpDrawView->IsDismantlePossible() ) + { + rSet.DisableItem( SID_DISMANTLE ); + } + + if ( !mpDrawView->IsDismantlePossible(true) && + !mpDrawView->IsImportMtfPossible() && + !mpDrawView->IsBreak3DObjPossible() ) + { + rSet.DisableItem( SID_BREAK ); + } + } + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_MODIFY_FIELD ) ) + { + OutlinerView* pOLV = mpDrawView->GetTextEditOutlinerView(); + + if( pOLV ) + { + const SvxFieldItem* pFldItem = pOLV->GetFieldAtSelection(); + + if( !( pFldItem && (nullptr != dynamic_cast< const SvxDateField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxAuthorField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxExtFileField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxExtTimeField *>( pFldItem->GetField() ) ) ) ) + { + rSet.DisableItem( SID_MODIFY_FIELD ); + } + } + else + rSet.DisableItem( SID_MODIFY_FIELD ); + } + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_OUTLINE_TEXT_AUTOFIT ) ) + { + const SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + const SdrTextFitToSizeTypeItem* pItem = pObj->GetMergedItemSet().GetItem<SdrTextFitToSizeTypeItem>(SDRATTR_TEXT_FITTOSIZE); + const bool bSet = pItem && pItem->GetValue() != drawing::TextFitToSizeType_NONE; + rSet.Put(SfxBoolItem(SID_OUTLINE_TEXT_AUTOFIT, bSet)); + } + + rSet.DisableItem(SID_GROUP); + rSet.DisableItem(SID_TEXT_COMBINE); + rSet.DisableItem(SID_COMBINE); + rSet.DisableItem(SID_DISTRIBUTE_HLEFT); + rSet.DisableItem(SID_DISTRIBUTE_HCENTER); + rSet.DisableItem(SID_DISTRIBUTE_HDISTANCE); + rSet.DisableItem(SID_DISTRIBUTE_HRIGHT); + rSet.DisableItem(SID_DISTRIBUTE_VTOP); + rSet.DisableItem(SID_DISTRIBUTE_VCENTER); + rSet.DisableItem(SID_DISTRIBUTE_VDISTANCE); + rSet.DisableItem(SID_DISTRIBUTE_VBOTTOM); + rSet.DisableItem(SID_POLY_MERGE); + rSet.DisableItem(SID_POLY_SUBSTRACT); + rSet.DisableItem(SID_POLY_INTERSECT); + rSet.DisableItem(SID_EQUALIZEWIDTH); + rSet.DisableItem(SID_EQUALIZEHEIGHT); + rSet.DisableItem(SID_CONNECT); + } + // multi-selection + else if( nMarkCount > 1 ) + { + // distribute dialog for 3+n objects + if(nMarkCount <= 2) + { + rSet.DisableItem(SID_DISTRIBUTE_HLEFT); + rSet.DisableItem(SID_DISTRIBUTE_HCENTER); + rSet.DisableItem(SID_DISTRIBUTE_HDISTANCE); + rSet.DisableItem(SID_DISTRIBUTE_HRIGHT); + rSet.DisableItem(SID_DISTRIBUTE_VTOP); + rSet.DisableItem(SID_DISTRIBUTE_VCENTER); + rSet.DisableItem(SID_DISTRIBUTE_VDISTANCE); + rSet.DisableItem(SID_DISTRIBUTE_VBOTTOM); + } + + rSet.DisableItem( SID_LINEEND_POLYGON ); + rSet.DisableItem( SID_ENTER_GROUP ); + // Now names for objects have to be unique + rSet.DisableItem( SID_NAME_GROUP ); + // #i68101# + rSet.DisableItem( SID_OBJECT_TITLE_DESCRIPTION ); + rSet.DisableItem( SID_MODIFY_FIELD ); + + { + bool bText = false; + bool bLine = false; + bool bGroup = false; + bool bDrawObj = false; + bool b3dObj = false; + bool bTable = false; + bool bMeasureObj = false; + bool bEdgeObj = false; // Connector + bool bE3dCompoundObject = false; + + for( size_t i = 0; i < nMarkCount && !bText && i < 50; ++i ) + { + SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj(); + SdrInventor nInv = pObj->GetObjInventor(); + SdrObjKind nId = pObj->GetObjIdentifier(); + + if (nInv == SdrInventor::Default) + { + switch (nId) + { + case SdrObjKind::Text: bText = true; break; + + case SdrObjKind::Line: bLine = true; break; + + case SdrObjKind::Edge: bEdgeObj = true; break; + + case SdrObjKind::Measure: bMeasureObj = true; break; + + case SdrObjKind::Rectangle: + case SdrObjKind::CircleOrEllipse: + case SdrObjKind::FreehandLine: + case SdrObjKind::FreehandFill: + case SdrObjKind::PathFill: + case SdrObjKind::PathLine: + case SdrObjKind::CircleSection: + case SdrObjKind::CircleArc: + case SdrObjKind::CircleCut: bDrawObj = true; break; + + case SdrObjKind::Group: bGroup = true; break; + + case SdrObjKind::Graphic: break; + + case SdrObjKind::Table: bTable = true; break; + default: ; + } + } + else if (nInv == SdrInventor::E3d) + { + if(dynamic_cast< const E3dScene *>( pObj ) != nullptr) + b3dObj = true; + else if(dynamic_cast< const E3dCompoundObject* >(pObj) != nullptr) + bE3dCompoundObject = true; + } + } + if( bLine && !bText && !bDrawObj &&!b3dObj) + { + rSet.DisableItem( SID_ATTR_FILL_STYLE ); + rSet.DisableItem( SID_ATTR_FILL_USE_SLIDE_BACKGROUND ); + rSet.DisableItem( SID_ATTR_FILL_TRANSPARENCE ); + rSet.DisableItem( SID_ATTR_FILL_FLOATTRANSPARENCE ); + } + if( !bEdgeObj ) + rSet.DisableItem( SID_CONNECTION_DLG ); + + if (b3dObj) + { + rSet.DisableItem( SID_COMBINE ); + rSet.DisableItem(SID_POLY_MERGE); + rSet.DisableItem(SID_POLY_SUBSTRACT); + rSet.DisableItem(SID_POLY_INTERSECT); + rSet.DisableItem(SID_EQUALIZEWIDTH); + rSet.DisableItem(SID_EQUALIZEHEIGHT); + } + + if (b3dObj || + (!mpDrawView->IsConvertToPathObjPossible() && + !mpDrawView->IsShearAllowed() && + !mpDrawView->IsDistortAllowed()) ) + { + rSet.DisableItem( SID_OBJECT_SHEAR ); + } + + if( !bGroup ) + { + rSet.DisableItem( SID_UNGROUP ); + } + if( bTable ) + rSet.DisableItem( SID_TEXTATTR_DLG ); + + if( !bMeasureObj ) + rSet.DisableItem( SID_MEASURE_DLG ); + + if(bE3dCompoundObject) + { + rSet.DisableItem( SID_OBJECT_ALIGN ); + rSet.DisableItem( SID_OBJECT_ALIGN_LEFT ); + rSet.DisableItem( SID_OBJECT_ALIGN_CENTER ); + rSet.DisableItem( SID_OBJECT_ALIGN_RIGHT ); + rSet.DisableItem( SID_OBJECT_ALIGN_UP ); + rSet.DisableItem( SID_OBJECT_ALIGN_MIDDLE ); + rSet.DisableItem( SID_OBJECT_ALIGN_DOWN ); + rSet.DisableItem( SID_FRAME_TO_TOP ); + rSet.DisableItem( SID_MOREFRONT ); + rSet.DisableItem( SID_FRAME_UP ); + rSet.DisableItem( SID_MOREBACK ); + rSet.DisableItem( SID_FRAME_DOWN ); + rSet.DisableItem( SID_FRAME_TO_BOTTOM ); + rSet.DisableItem( SID_BEFORE_OBJ ); + rSet.DisableItem( SID_BEHIND_OBJ ); + rSet.DisableItem( SID_REVERSE_ORDER ); + rSet.DisableItem( SID_POSITION ); + } + } + + if ( !mpDrawView->IsDismantlePossible() ) + { + rSet.DisableItem( SID_DISMANTLE ); + } + if ( !mpDrawView->IsDismantlePossible(true) && + !mpDrawView->IsImportMtfPossible() && + !mpDrawView->IsBreak3DObjPossible() ) + { + rSet.DisableItem( SID_BREAK ); + } + if ( !mpDrawView->IsCombinePossible() ) + { + rSet.DisableItem(SID_COMBINE); + rSet.DisableItem(SID_POLY_MERGE); + rSet.DisableItem(SID_POLY_SUBSTRACT); + rSet.DisableItem(SID_POLY_INTERSECT); + rSet.DisableItem(SID_EQUALIZEWIDTH); + rSet.DisableItem(SID_EQUALIZEHEIGHT); + } + if ( !mpDrawView->IsCombinePossible(true) ) + { + rSet.DisableItem( SID_CONNECT ); + } + if ( !mpDrawView->IsGroupPossible() ) + { + rSet.DisableItem( SID_GROUP ); + } + if ( !mpDrawView->IsUnGroupPossible() ) + { + rSet.DisableItem( SID_UNGROUP ); + } + } + // select no object + else + { + rSet.DisableItem( SID_ENTER_GROUP ); + rSet.DisableItem( SID_CUT ); + rSet.DisableItem( SID_COPY ); + rSet.DisableItem( SID_DELETE ); + rSet.DisableItem( SID_ATTR_TRANSFORM ); + + rSet.DisableItem( SID_OBJECT_ALIGN ); + rSet.DisableItem( SID_OBJECT_ALIGN_LEFT ); + rSet.DisableItem( SID_OBJECT_ALIGN_CENTER ); + rSet.DisableItem( SID_OBJECT_ALIGN_RIGHT ); + rSet.DisableItem( SID_OBJECT_ALIGN_UP ); + rSet.DisableItem( SID_OBJECT_ALIGN_MIDDLE ); + rSet.DisableItem( SID_OBJECT_ALIGN_DOWN ); + + rSet.DisableItem( SID_FRAME_TO_TOP ); + rSet.DisableItem( SID_MOREFRONT ); + rSet.DisableItem( SID_FRAME_UP ); + rSet.DisableItem( SID_MOREBACK ); + rSet.DisableItem( SID_FRAME_DOWN ); + rSet.DisableItem( SID_FRAME_TO_BOTTOM ); + rSet.DisableItem( SID_BEFORE_OBJ ); + rSet.DisableItem( SID_BEHIND_OBJ ); + rSet.DisableItem( SID_POSITION ); + + rSet.DisableItem( SID_SIZE_OPTIMAL ); + rSet.DisableItem( SID_LINEEND_POLYGON ); + rSet.DisableItem( SID_COPYOBJECTS ); + rSet.DisableItem( SID_HORIZONTAL ); + rSet.DisableItem( SID_VERTICAL ); + rSet.DisableItem( SID_FLIP_HORIZONTAL ); + rSet.DisableItem( SID_FLIP_VERTICAL ); + rSet.DisableItem( SID_GROUP ); + rSet.DisableItem( SID_UNGROUP ); + rSet.DisableItem( SID_NAME_GROUP ); + + // #i68101# + rSet.DisableItem( SID_OBJECT_TITLE_DESCRIPTION ); + + rSet.DisableItem( SID_DISMANTLE ); + rSet.DisableItem( SID_BREAK ); + rSet.DisableItem( SID_TEXT_COMBINE ); + rSet.DisableItem( SID_COMBINE ); + rSet.DisableItem(SID_DISTRIBUTE_DLG); + rSet.DisableItem(SID_DISTRIBUTE_HLEFT); + rSet.DisableItem(SID_DISTRIBUTE_HCENTER); + rSet.DisableItem(SID_DISTRIBUTE_HDISTANCE); + rSet.DisableItem(SID_DISTRIBUTE_HRIGHT); + rSet.DisableItem(SID_DISTRIBUTE_VTOP); + rSet.DisableItem(SID_DISTRIBUTE_VCENTER); + rSet.DisableItem(SID_DISTRIBUTE_VDISTANCE); + rSet.DisableItem(SID_DISTRIBUTE_VBOTTOM); + rSet.DisableItem(SID_POLY_MERGE); + rSet.DisableItem(SID_POLY_SUBSTRACT); + rSet.DisableItem(SID_POLY_INTERSECT); + rSet.DisableItem(SID_EQUALIZEWIDTH); + rSet.DisableItem(SID_EQUALIZEHEIGHT); + rSet.DisableItem( SID_CONNECT ); + rSet.DisableItem( SID_ANIMATION_EFFECTS ); + rSet.DisableItem( SID_EXECUTE_ANIMATION_EFFECT ); + rSet.DisableItem( SID_MODIFY_FIELD ); + rSet.DisableItem (SID_OBJECT_SHEAR); + } + + if (GetObjectShell()->isContentExtractionLocked()) + { + rSet.DisableItem(SID_COPY); + rSet.DisableItem(SID_CUT); + } + if(GetObjectShell()->isExportLocked()) + { + rSet.DisableItem(SID_SAVE_GRAPHIC); + rSet.DisableItem(SID_EXTERNAL_EDIT); + } + if (GetDoc()->getImagePreferredDPI() <= 0) + { + rSet.DisableItem(SID_GRAPHIC_SIZE_CHECK); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviewsk.cxx b/sd/source/ui/view/drviewsk.cxx new file mode 100644 index 000000000..9daeecc02 --- /dev/null +++ b/sd/source/ui/view/drviewsk.cxx @@ -0,0 +1,37 @@ +/* -*- 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/. + */ + +#include <DrawViewShell.hxx> +#include <sdmod.hxx> + +#include <comphelper/lok.hxx> + +namespace sd { + +void DrawViewShell::ConfigurationChanged( utl::ConfigurationBroadcaster* pCb, ConfigurationHints ) +{ + ConfigureAppBackgroundColor( dynamic_cast<svtools::ColorConfig*>(pCb) ); +} + +void DrawViewShell::ConfigureAppBackgroundColor( svtools::ColorConfig *pColorConfig ) +{ + if (!pColorConfig) + pColorConfig = &SD_MOD()->GetColorConfig(); + Color aFillColor( pColorConfig->GetColorValue( svtools::APPBACKGROUND ).nColor ); + if (comphelper::LibreOfficeKit::isActive()) + aFillColor = COL_TRANSPARENT; + // tdf#87905 Use darker background color for master view + if (meEditMode == EditMode::MasterPage) + aFillColor.DecreaseLuminance( 64 ); + mnAppBackgroundColor = aFillColor; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sd/source/ui/view/drvwshrg.cxx b/sd/source/ui/view/drvwshrg.cxx new file mode 100644 index 000000000..792d5b833 --- /dev/null +++ b/sd/source/ui/view/drvwshrg.cxx @@ -0,0 +1,110 @@ +/* -*- 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 <config_features.h> + +#include <DrawViewShell.hxx> +#include <sfx2/infobar.hxx> + +#include <svx/fontwork.hxx> +#include <svx/bmpmask.hxx> +#include <svx/imapdlg.hxx> +#include <svx/SvxColorChildWindow.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/sidebar/SidebarChildWindow.hxx> +#include <sfx2/devtools/DevelopmentToolChildWindow.hxx> +#include <svx/f3dchild.hxx> + +#include <svx/svxids.hrc> +#include <svx/hyperdlg.hxx> +#include <avmedia/mediaplayer.hxx> + +#include <app.hrc> + +#include <SpellDialogChildWindow.hxx> +#include <GraphicViewShell.hxx> +#include <AnimationChildWindow.hxx> + +using namespace sd; +#define ShellClass_DrawViewShell +#include <sdslots.hxx> +#define ShellClass_GraphicViewShell +#include <sdgslots.hxx> + +namespace sd +{ +/** + * Declare SFX-Slotmap and Standardinterface + */ + +SFX_IMPL_INTERFACE(DrawViewShell, SfxShell) + +void DrawViewShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterPopupMenu("drawtext"); + + GetStaticInterface()->RegisterChildWindow(SID_NAVIGATOR, true); + + GetStaticInterface()->RegisterChildWindow(SfxInfoBarContainerChild::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SvxFontWorkChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SvxColorChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(AnimationChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(Svx3DChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SvxBmpMaskChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SvxIMapDlgChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SvxHlinkDlgWrapper::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(::sd::SpellDialogChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SID_SEARCH_DLG); +#if HAVE_FEATURE_AVMEDIA + GetStaticInterface()->RegisterChildWindow(::avmedia::MediaPlayer::GetChildWindowId()); +#endif + GetStaticInterface()->RegisterChildWindow( + sfx2::sidebar::SidebarChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(DevelopmentToolChildWindow::GetChildWindowId()); +} + +// SdGraphicViewShell +SFX_IMPL_INTERFACE(GraphicViewShell, SfxShell) + +void GraphicViewShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterPopupMenu("drawtext"); + + GetStaticInterface()->RegisterChildWindow(SID_NAVIGATOR, true); + + GetStaticInterface()->RegisterChildWindow(SfxInfoBarContainerChild::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SvxFontWorkChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SvxColorChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(Svx3DChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SvxBmpMaskChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SvxIMapDlgChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SvxHlinkDlgWrapper::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(::sd::SpellDialogChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SID_SEARCH_DLG); +#if HAVE_FEATURE_AVMEDIA + GetStaticInterface()->RegisterChildWindow(::avmedia::MediaPlayer::GetChildWindowId()); +#endif + GetStaticInterface()->RegisterChildWindow( + sfx2::sidebar::SidebarChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(DevelopmentToolChildWindow::GetChildWindowId()); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/frmview.cxx b/sd/source/ui/view/frmview.cxx new file mode 100644 index 000000000..fad0dc9ad --- /dev/null +++ b/sd/source/ui/view/frmview.cxx @@ -0,0 +1,916 @@ +/* -*- 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 <FrameView.hxx> + +#include <svx/svxids.hrc> +#include <com/sun/star/drawing/framework/ResourceId.hpp> +#include <com/sun/star/drawing/framework/XView.hpp> +#include <rtl/ustrbuf.hxx> +#include <unokywds.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <osl/diagnose.h> + +#include <vector> +#include <ViewShell.hxx> +#include <drawdoc.hxx> +#include <DrawDocShell.hxx> +#include <optsitem.hxx> +#include <ViewShellBase.hxx> +#include <sdmod.hxx> +#include <pres.hxx> +#include <framework/FrameworkHelper.hxx> +#include <comphelper/processfactory.hxx> +#include <sfx2/viewfrm.hxx> +#include <officecfg/Office/Common.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::std; + +namespace sd { + +FrameView::FrameView(SdDrawDocument* pDrawDoc, FrameView* pFrameView /* = NULL */) +: SdrView(*pDrawDoc, nullptr), // TTTT SdDrawDocument* -> should be reference + mnRefCount(0), + mnPresViewShellId(SID_VIEWSHELL0), + mbIsNavigatorShowingAllShapes(false) +{ + EndListening(*pDrawDoc); + + EnableExtendedKeyInputDispatcher(false); + EnableExtendedMouseEventDispatcher(false); + + SetGridFront( false ); + SetHlplFront( false ); + SetOConSnap( false ); + SetFrameDragSingles(); + SetSlidesPerRow(4); + + if( nullptr == pFrameView ) + { + DrawDocShell* pDocShell = pDrawDoc->GetDocSh(); + + if ( pDocShell ) + { + // document is loaded, is there a FrameView? + sal_uLong nSdViewShellCount = 0; + SfxViewFrame* pSfxViewFrame = SfxViewFrame::GetFirst(pDocShell); + + while (pSfxViewFrame) + { + // Count the FrameViews and remember the type of the main + // view shell. + SfxViewShell* pSfxViewSh = pSfxViewFrame->GetViewShell(); + ViewShellBase* pBase = dynamic_cast<ViewShellBase*>( pSfxViewSh ); + + if (pBase != nullptr) + { + nSdViewShellCount++; + + OUString sViewURL; + Reference<drawing::framework::XView> xView ( + framework::FrameworkHelper::Instance(*pBase)->GetView( + drawing::framework::ResourceId::create( + ::comphelper::getProcessComponentContext(), + framework::FrameworkHelper::msCenterPaneURL))); + if (xView.is()) + sViewURL = xView->getResourceId()->getResourceURL(); + + switch (framework::FrameworkHelper::GetViewId(sViewURL)) + { + default: +// case ViewShell::ST_IMPRESS: +// case ViewShell::ST_NOTES: +// case ViewShell::ST_HANDOUT: + mnPresViewShellId = SID_VIEWSHELL0; + break; + + case ViewShell::ST_SLIDE_SORTER: + mnPresViewShellId = SID_VIEWSHELL1; + break; + + case ViewShell::ST_OUTLINE: + mnPresViewShellId = SID_VIEWSHELL2; + break; + } + } + + pSfxViewFrame = SfxViewFrame::GetNext(*pSfxViewFrame, pDocShell); + } + + SdDrawDocument* pDoc = pDocShell->GetDoc(); + pFrameView = pDoc->GetFrameView(nSdViewShellCount); + } + } + + if (pFrameView) + { + // initialize FrameView with the FrameView of the DocShell + SetRuler( pFrameView->HasRuler() ); + SetGridCoarse( pFrameView->GetGridCoarse() ); + SetGridFine( pFrameView->GetGridFine() ); + SetSnapGridWidth(pFrameView->GetSnapGridWidthX(), pFrameView->GetSnapGridWidthY()); + SetGridVisible( pFrameView->IsGridVisible() ); + SetGridFront( pFrameView->IsGridFront() ); + SetSnapAngle( pFrameView->GetSnapAngle() ); + SetGridSnap( pFrameView->IsGridSnap() ); + SetBordSnap( pFrameView->IsBordSnap() ); + SetHlplSnap( pFrameView->IsHlplSnap() ); + SetOFrmSnap( pFrameView->IsOFrmSnap() ); + SetOPntSnap( pFrameView->IsOPntSnap() ); + SetOConSnap( pFrameView->IsOConSnap() ); + SetHlplVisible( pFrameView->IsHlplVisible() ); + SetDragStripes( pFrameView->IsDragStripes() ); + SetPlusHandlesAlwaysVisible( pFrameView->IsPlusHandlesAlwaysVisible() ); + SetFrameDragSingles( pFrameView->IsFrameDragSingles() ); + SetSnapMagneticPixel( pFrameView->GetSnapMagneticPixel() ); + SetMarkedHitMovesAlways( pFrameView->IsMarkedHitMovesAlways() ); + SetMoveOnlyDragging( pFrameView->IsMoveOnlyDragging() ); + SetCrookNoContortion( pFrameView->IsCrookNoContortion() ); + SetSlantButShear( pFrameView->IsSlantButShear() ); + SetNoDragXorPolys( pFrameView->IsNoDragXorPolys() ); + SetAngleSnapEnabled( pFrameView->IsAngleSnapEnabled() ); + SetBigOrtho( pFrameView->IsBigOrtho() ); + SetOrtho( pFrameView->IsOrtho() ); + SetEliminatePolyPointLimitAngle( pFrameView->GetEliminatePolyPointLimitAngle() ); + SetEliminatePolyPoints( pFrameView->IsEliminatePolyPoints() ); + SetDesignMode( pFrameView->IsDesignMode() ); + + SetSolidDragging( pFrameView->IsSolidDragging() ); + + maVisibleLayers = pFrameView->GetVisibleLayers(); + maPrintableLayers = pFrameView->GetPrintableLayers(); + maLockedLayers = pFrameView->GetLockedLayers(); + maStandardHelpLines = pFrameView->GetStandardHelpLines(); + maNotesHelpLines = pFrameView->GetNotesHelpLines(); + maHandoutHelpLines = pFrameView->GetHandoutHelpLines(); + SetActiveLayer( pFrameView->GetActiveLayer() ); + mbNoColors = pFrameView->IsNoColors(); + mbNoAttribs = pFrameView->IsNoAttribs() ; + maVisArea = pFrameView->GetVisArea(); + mePageKind = pFrameView->GetPageKind(); + mePageKindOnLoad = pFrameView->GetPageKindOnLoad(); + mnSelectedPage = pFrameView->GetSelectedPage(); + mnSelectedPageOnLoad = pFrameView->GetSelectedPageOnLoad(); + mePageEditMode = pFrameView->GetViewShEditMode(); + // meStandardEditMode = pFrameView->GetViewShEditMode(PageKind::Standard); + // meNotesEditMode = pFrameView->GetViewShEditMode(PageKind::Notes); + // meHandoutEditMode = pFrameView->GetViewShEditMode(PageKind::Handout); + SetViewShEditModeOnLoad(pFrameView->GetViewShEditModeOnLoad()); + mbLayerMode = pFrameView->IsLayerMode(); + mbQuickEdit = pFrameView->IsQuickEdit(); + + // #i26631# + SetMasterPagePaintCaching( pFrameView->IsMasterPagePaintCaching() ); + + SetDragWithCopy( pFrameView->IsDragWithCopy() ); + mbDoubleClickTextEdit = pFrameView->IsDoubleClickTextEdit(); + mbClickChangeRotation = pFrameView->IsClickChangeRotation(); + mnSlidesPerRow = pFrameView->GetSlidesPerRow(); + mnDrawMode = pFrameView->GetDrawMode(); + mbIsNavigatorShowingAllShapes = pFrameView->IsNavigatorShowingAllShapes(); + SetPreviousViewShellType (pFrameView->GetPreviousViewShellType()); + SetViewShellTypeOnLoad (pFrameView->GetViewShellTypeOnLoad()); + } + else + { + // initialize FrameView with the application data + + // Layers need to be set, otherwise they are not visible and not printable in + // Impress documents. The document contains already the actual layers and their + // settings for visible, printable and locked. In case not read from <draw:layer-set>, + // ODF defaults are used. + SdrLayerAdmin rLayerAdmin = pDrawDoc -> GetLayerAdmin(); + rLayerAdmin.getVisibleLayersODF(maVisibleLayers); + rLayerAdmin.getPrintableLayersODF(maPrintableLayers); + rLayerAdmin.getLockedLayersODF(maLockedLayers); + SetGridCoarse( Size( 1000, 1000 ) ); + SetSnapGridWidth(Fraction(1000, 1), Fraction(1000, 1)); + SetActiveLayer(sUNO_LayerName_layout); + mbNoColors = true; + mbNoAttribs = false; + maVisArea = ::tools::Rectangle( Point(), Size(0, 0) ); + mePageKind = PageKind::Standard; + mePageKindOnLoad = PageKind::Standard; + mnSelectedPage = 0; + mnSelectedPageOnLoad = 0; + mePageEditMode = EditMode::Page; + // meStandardEditMode = EditMode::Page; + // meNotesEditMode = EditMode::Page; + // meHandoutEditMode = EditMode::MasterPage; + SetViewShEditModeOnLoad(EditMode::Page); + mbLayerMode = false; + SetEliminatePolyPoints(false); + mbDoubleClickTextEdit = false; + mbClickChangeRotation = false; + mnSlidesPerRow = 4; + + { + bool bUseContrast = Application::GetSettings().GetStyleSettings().GetHighContrastMode(); + mnDrawMode = bUseContrast ? OUTPUT_DRAWMODE_CONTRAST : OUTPUT_DRAWMODE_COLOR; + } + mbIsNavigatorShowingAllShapes = true; + SetPreviousViewShellType (ViewShell::ST_NONE); + SetViewShellTypeOnLoad (ViewShell::ST_IMPRESS); + + // get default for design mode + bool bInitDesignMode = pDrawDoc->GetOpenInDesignMode(); + if( pDrawDoc->OpenInDesignModeIsDefaulted() ) + { + bInitDesignMode = true; + } + + SfxObjectShell* pObjShell = pDrawDoc->GetObjectShell(); + if( pObjShell && pObjShell->IsReadOnly() ) + bInitDesignMode = false; + SetDesignMode( bInitDesignMode ); + + Update( SD_MOD()->GetSdOptions(pDrawDoc->GetDocumentType()) ); + } + +} + +FrameView::~FrameView() +{ +} + +void FrameView::Connect() +{ + mnRefCount++; +} + +void FrameView::Disconnect() +{ + if (mnRefCount > 0) + { + mnRefCount--; + } + + if (mnRefCount == 0) + { + delete this; + } +} + +/** + * Update with data from the specified SdOptions + */ +void FrameView::Update(SdOptions const * pOptions) +{ + if (!pOptions) + return; + + mbRuler = pOptions->IsRulerVisible(); + SetGridVisible( pOptions->IsGridVisible() ); + SetSnapAngle( pOptions->GetAngle() ); + SetGridSnap( pOptions->IsUseGridSnap() ); + SetBordSnap( pOptions->IsSnapBorder() ); + SetHlplSnap( pOptions->IsSnapHelplines() ); + SetOFrmSnap( pOptions->IsSnapFrame() ); + SetOPntSnap( pOptions->IsSnapPoints() ); + SetHlplVisible( pOptions->IsHelplines() ); + SetDragStripes( pOptions->IsDragStripes() ); + SetPlusHandlesAlwaysVisible( pOptions->IsHandlesBezier() ); + SetSnapMagneticPixel( pOptions->GetSnapArea() ); + SetMarkedHitMovesAlways( pOptions->IsMarkedHitMovesAlways() ); + SetMoveOnlyDragging( pOptions->IsMoveOnlyDragging() ); + SetSlantButShear( pOptions->IsMoveOnlyDragging() ); + SetNoDragXorPolys ( !pOptions->IsMoveOutline() ); + SetCrookNoContortion( pOptions->IsCrookNoContortion() ); + SetAngleSnapEnabled( pOptions->IsRotate() ); + SetBigOrtho( pOptions->IsBigOrtho() ); + SetOrtho( pOptions->IsOrtho() ); + SetEliminatePolyPointLimitAngle( pOptions->GetEliminatePolyPointLimitAngle() ); + GetModel()->SetPickThroughTransparentTextFrames( pOptions->IsPickThrough() ); + + SetSolidDragging( pOptions->IsSolidDragging() ); + + SetGridCoarse( Size( pOptions->GetFieldDrawX(), pOptions->GetFieldDrawY() ) ); + SetGridFine( Size( pOptions->GetFieldDivisionX(), pOptions->GetFieldDivisionY() ) ); + Fraction aFractX(pOptions->GetFieldDrawX(), pOptions->GetFieldDrawX() / ( pOptions->GetFieldDivisionX() ? pOptions->GetFieldDivisionX() : 1 )); + Fraction aFractY(pOptions->GetFieldDrawY(), pOptions->GetFieldDrawY() / ( pOptions->GetFieldDivisionY() ? pOptions->GetFieldDivisionY() : 1 )); + SetSnapGridWidth(aFractX, aFractY); + SetQuickEdit(pOptions->IsQuickEdit()); + + // #i26631# + SetMasterPagePaintCaching( pOptions->IsMasterPagePaintCaching() ); + + SetDragWithCopy(pOptions->IsDragWithCopy()); + SetDoubleClickTextEdit( pOptions->IsDoubleClickTextEdit() ); + SetClickChangeRotation( pOptions->IsClickChangeRotation() ); +} + +/** + * Set EditMode (Page or MasterPage) of working mode + */ +void FrameView::SetViewShEditMode(EditMode eMode) +{ + mePageEditMode = eMode; +} + +/** + * Return EditMode (Page or MasterPage) of working mode + */ +EditMode FrameView::GetViewShEditMode() const +{ + return mePageEditMode; +} + +void FrameView::SetViewShEditModeOnLoad (EditMode eMode) +{ + meEditModeOnLoad = eMode; +} + +static OUString createHelpLinesString( const SdrHelpLineList& rHelpLines ) +{ + OUStringBuffer aLines; + + const sal_uInt16 nCount = rHelpLines.GetCount(); + for( sal_uInt16 nHlpLine = 0; nHlpLine < nCount; nHlpLine++ ) + { + const SdrHelpLine& rHelpLine = rHelpLines[nHlpLine]; + const Point& rPos = rHelpLine.GetPos(); + + switch( rHelpLine.GetKind() ) + { + case SdrHelpLineKind::Point: + aLines.append( 'P' ); + aLines.append( static_cast<sal_Int32>(rPos.X()) ); + aLines.append( ',' ); + aLines.append( static_cast<sal_Int32>(rPos.Y()) ); + break; + case SdrHelpLineKind::Vertical: + aLines.append( 'V' ); + aLines.append( static_cast<sal_Int32>(rPos.X()) ); + break; + case SdrHelpLineKind::Horizontal: + aLines.append( 'H' ); + aLines.append( static_cast<sal_Int32>(rPos.Y()) ); + break; + default: + OSL_FAIL( "Unsupported helpline Kind!" ); + } + } + + return aLines.makeStringAndClear(); +} + +void FrameView::WriteUserDataSequence ( css::uno::Sequence < css::beans::PropertyValue >& rValues ) +{ + std::vector< std::pair< OUString, Any > > aUserData; + aUserData.reserve(41); // worst case + + aUserData.emplace_back( sUNO_View_GridIsVisible, Any( IsGridVisible() ) ); + aUserData.emplace_back( sUNO_View_GridIsFront, Any( IsGridFront() ) ); + aUserData.emplace_back( sUNO_View_IsSnapToGrid, Any( IsGridSnap() ) ); + aUserData.emplace_back( sUNO_View_IsSnapToPageMargins, Any( IsBordSnap() ) ); + aUserData.emplace_back( sUNO_View_IsSnapToSnapLines, Any( IsHlplSnap() ) ); + aUserData.emplace_back( sUNO_View_IsSnapToObjectFrame, Any( IsOFrmSnap() ) ); + aUserData.emplace_back( sUNO_View_IsSnapToObjectPoints, Any( IsOPntSnap() ) ); + + aUserData.emplace_back( sUNO_View_IsPlusHandlesAlwaysVisible, Any( IsPlusHandlesAlwaysVisible() ) ); + aUserData.emplace_back( sUNO_View_IsFrameDragSingles, Any( IsFrameDragSingles() ) ); + + aUserData.emplace_back( sUNO_View_EliminatePolyPointLimitAngle, Any( static_cast<sal_Int32>(GetEliminatePolyPointLimitAngle()) ) ); + aUserData.emplace_back( sUNO_View_IsEliminatePolyPoints, Any( IsEliminatePolyPoints() ) ); + + if ( officecfg::Office::Common::Misc::WriteLayerStateAsConfigItem::get() ) + { + SdrLayerAdmin& rLayerAdmin = getSdrModelFromSdrView().GetLayerAdmin(); + Any aAny; + rLayerAdmin.QueryValue(GetVisibleLayers(), aAny); + aUserData.emplace_back( sUNO_View_VisibleLayers, aAny ); + + rLayerAdmin.QueryValue(GetPrintableLayers(), aAny); + aUserData.emplace_back( sUNO_View_PrintableLayers, aAny ); + + rLayerAdmin.QueryValue(GetLockedLayers(), aAny); + aUserData.emplace_back( sUNO_View_LockedLayers, aAny ); + } + + aUserData.emplace_back( sUNO_View_NoAttribs, Any( IsNoAttribs() ) ); + aUserData.emplace_back( sUNO_View_NoColors, Any( IsNoColors() ) ); + + if( GetStandardHelpLines().GetCount() ) + aUserData.emplace_back( sUNO_View_SnapLinesDrawing, Any( createHelpLinesString( GetStandardHelpLines() ) ) ); + + if( GetNotesHelpLines().GetCount() ) + aUserData.emplace_back( sUNO_View_SnapLinesNotes, Any( createHelpLinesString( GetNotesHelpLines() ) ) ); + + if( GetHandoutHelpLines().GetCount() ) + aUserData.emplace_back( sUNO_View_SnapLinesHandout, Any( createHelpLinesString( GetHandoutHelpLines() ) ) ); + + aUserData.emplace_back( sUNO_View_RulerIsVisible, Any( HasRuler() ) ); + aUserData.emplace_back( sUNO_View_PageKind, Any( static_cast<sal_Int16>(GetPageKind()) ) ); + aUserData.emplace_back( sUNO_View_SelectedPage, Any( static_cast<sal_Int16>(GetSelectedPage()) ) ); + aUserData.emplace_back( sUNO_View_IsLayerMode, Any( IsLayerMode() ) ); + + aUserData.emplace_back( sUNO_View_IsDoubleClickTextEdit, Any( IsDoubleClickTextEdit() ) ); + aUserData.emplace_back( sUNO_View_IsClickChangeRotation, Any( IsClickChangeRotation() ) ); + + aUserData.emplace_back( sUNO_View_SlidesPerRow, Any( static_cast<sal_Int16>(GetSlidesPerRow()) ) ); + aUserData.emplace_back( sUNO_View_EditMode, Any( static_cast<sal_Int32>(GetViewShEditMode()) ) ); + // aUserData.emplace_back( sUNO_View_EditModeStandard, makeAny( (sal_Int32)GetViewShEditMode( PageKind::Standard ) ) ); + // aUserData.emplace_back( sUNO_View_EditModeNotes, makeAny( (sal_Int32)GetViewShEditMode( PageKind::Notes ) ) ); + // aUserData.emplace_back( sUNO_View_EditModeHandout, makeAny( (sal_Int32)GetViewShEditMode( PageKind::Handout ) ) ); + + { + const ::tools::Rectangle aVisArea = GetVisArea(); + + aUserData.emplace_back( sUNO_View_VisibleAreaTop, Any( static_cast<sal_Int32>(aVisArea.Top()) ) ); + aUserData.emplace_back( sUNO_View_VisibleAreaLeft, Any( static_cast<sal_Int32>(aVisArea.Left()) ) ); + aUserData.emplace_back( sUNO_View_VisibleAreaWidth, Any( static_cast<sal_Int32>(aVisArea.GetWidth()) ) ); + aUserData.emplace_back( sUNO_View_VisibleAreaHeight, Any( static_cast<sal_Int32>(aVisArea.GetHeight()) ) ); + } + + aUserData.emplace_back( sUNO_View_GridCoarseWidth, Any( static_cast<sal_Int32>(GetGridCoarse().Width()) ) ); + aUserData.emplace_back( sUNO_View_GridCoarseHeight, Any( static_cast<sal_Int32>(GetGridCoarse().Height()) ) ); + aUserData.emplace_back( sUNO_View_GridFineWidth, Any( static_cast<sal_Int32>(GetGridFine().Width()) ) ); + aUserData.emplace_back( sUNO_View_GridFineHeight, Any( static_cast<sal_Int32>(GetGridFine().Height()) ) ); + aUserData.emplace_back( sUNO_View_GridSnapWidthXNumerator, Any( GetSnapGridWidthX().GetNumerator() ) ); + aUserData.emplace_back( sUNO_View_GridSnapWidthXDenominator, Any( GetSnapGridWidthX().GetDenominator() ) ); + aUserData.emplace_back( sUNO_View_GridSnapWidthYNumerator, Any( GetSnapGridWidthY().GetNumerator() ) ); + aUserData.emplace_back( sUNO_View_GridSnapWidthYDenominator, Any( GetSnapGridWidthY().GetDenominator() ) ); + aUserData.emplace_back( sUNO_View_IsAngleSnapEnabled, Any( IsAngleSnapEnabled() ) ); + aUserData.emplace_back( sUNO_View_SnapAngle, Any( static_cast<sal_Int32>(GetSnapAngle()) ) ); + + const sal_Int32 nOldLength = rValues.getLength(); + rValues.realloc( nOldLength + aUserData.size() ); + + PropertyValue* pValue = &(rValues.getArray()[nOldLength]); + + for( const auto& rItem : aUserData ) + { + pValue->Name = rItem.first; + pValue->Value = rItem.second; + ++pValue; + } +} + +static void createHelpLinesFromString( const OUString& rLines, SdrHelpLineList& rHelpLines ) +{ + const sal_Unicode * pStr = rLines.getStr(); + SdrHelpLine aNewHelpLine; + OUStringBuffer sBuffer; + + while( *pStr ) + { + Point aPoint; + + switch( *pStr ) + { + case 'P': + aNewHelpLine.SetKind( SdrHelpLineKind::Point ); + break; + case 'V': + aNewHelpLine.SetKind( SdrHelpLineKind::Vertical ); + break; + case 'H': + aNewHelpLine.SetKind( SdrHelpLineKind::Horizontal ); + break; + default: + OSL_FAIL( "syntax error in snap lines settings string" ); + return; + } + + pStr++; + + while( (*pStr >= '0' && *pStr <= '9') || (*pStr == '+') || (*pStr == '-') ) + { + sBuffer.append( *pStr++ ); + } + + sal_Int32 nValue = sBuffer.makeStringAndClear().toInt32(); + + if( aNewHelpLine.GetKind() == SdrHelpLineKind::Horizontal ) + { + aPoint.setY( nValue ); + } + else + { + aPoint.setX( nValue ); + + if( aNewHelpLine.GetKind() == SdrHelpLineKind::Point ) + { + if( *pStr++ != ',' ) + return; + + while( (*pStr >= '0' && *pStr <= '9') || (*pStr == '+') || (*pStr == '-') ) + { + sBuffer.append( *pStr++ ); + } + + aPoint.setY( sBuffer.makeStringAndClear().toInt32() ); + + } + } + + aNewHelpLine.SetPos( aPoint ); + rHelpLines.Insert( aNewHelpLine ); + } +} + +void FrameView::ReadUserDataSequence ( const css::uno::Sequence < css::beans::PropertyValue >& rSequence ) +{ + const sal_Int32 nLength = rSequence.getLength(); + if (!nLength) + return; + + SdDrawDocument* pDrawDocument = dynamic_cast<SdDrawDocument*>(GetModel()); + const bool bImpress = pDrawDocument && pDrawDocument->GetDocumentType() == DocumentType::Impress; + + bool bBool = false; + sal_Int32 nInt32 = 0; + sal_Int16 nInt16 = 0; + OUString aString; + + sal_Int32 aSnapGridWidthXNum = GetSnapGridWidthX().GetNumerator(); + sal_Int32 aSnapGridWidthXDom = GetSnapGridWidthX().GetDenominator(); + + sal_Int32 aSnapGridWidthYNum = GetSnapGridWidthY().GetNumerator(); + sal_Int32 aSnapGridWidthYDom = GetSnapGridWidthY().GetDenominator(); + + for (const css::beans::PropertyValue& rValue : rSequence) + { + if ( rValue.Name == sUNO_View_ViewId ) + { + } + else if ( rValue.Name == sUNO_View_SnapLinesDrawing ) + { + if( rValue.Value >>= aString ) + { + SdrHelpLineList aHelpLines; + createHelpLinesFromString( aString, aHelpLines ); + SetStandardHelpLines( aHelpLines ); + } + } + else if ( rValue.Name == sUNO_View_SnapLinesNotes ) + { + if( rValue.Value >>= aString ) + { + SdrHelpLineList aHelpLines; + createHelpLinesFromString( aString, aHelpLines ); + SetNotesHelpLines( aHelpLines ); + } + } + else if ( rValue.Name == sUNO_View_SnapLinesHandout ) + { + if( rValue.Value >>= aString ) + { + SdrHelpLineList aHelpLines; + createHelpLinesFromString( aString, aHelpLines ); + SetHandoutHelpLines( aHelpLines ); + } + } + else if ( rValue.Name == sUNO_View_RulerIsVisible ) + { + if( rValue.Value >>= bBool ) + { + SetRuler( bBool ); + } + } + else if ( rValue.Name == sUNO_View_PageKind ) + { + if( rValue.Value >>= nInt16 ) + { + SdDrawDocument* pDoc = dynamic_cast< SdDrawDocument* >( GetModel() ); + if( pDoc && pDoc->GetDocSh() && ( SfxObjectCreateMode::EMBEDDED == pDoc->GetDocSh()->GetCreateMode() ) ) + SetPageKind( static_cast<PageKind>(nInt16) ); + + SetPageKindOnLoad( static_cast<PageKind>(nInt16) ); + } + } + else if ( rValue.Name == sUNO_View_SelectedPage ) + { + if( rValue.Value >>= nInt16 ) + { + SdDrawDocument* pDoc = dynamic_cast< SdDrawDocument* >( GetModel() ); + if( pDoc && pDoc->GetDocSh() && ( SfxObjectCreateMode::EMBEDDED == pDoc->GetDocSh()->GetCreateMode() ) ) + SetSelectedPage( static_cast<sal_uInt16>(nInt16) ); + + SetSelectedPageOnLoad( static_cast<sal_uInt16>(nInt16) ); + } + } + else if ( rValue.Name == sUNO_View_IsLayerMode ) + { + if( rValue.Value >>= bBool ) + { + SetLayerMode( bBool ); + } + } + else if ( rValue.Name == sUNO_View_IsDoubleClickTextEdit ) + { + if( rValue.Value >>= bBool ) + { + SetDoubleClickTextEdit( bBool ); + } + } + else if ( rValue.Name == sUNO_View_IsClickChangeRotation ) + { + if( rValue.Value >>= bBool ) + { + SetClickChangeRotation( bBool ); + } + } + else if ( rValue.Name == sUNO_View_SlidesPerRow ) + { + if( rValue.Value >>= nInt16 ) + { + SetSlidesPerRow( static_cast<sal_uInt16>(nInt16) ); + } + } + else if ( rValue.Name == sUNO_View_EditMode ) + { + if( rValue.Value >>= nInt32 ) + { + SdDrawDocument* pDoc = dynamic_cast< SdDrawDocument* >( GetModel() ); + if( pDoc && pDoc->GetDocSh() && ( SfxObjectCreateMode::EMBEDDED == pDoc->GetDocSh()->GetCreateMode() ) ) + SetViewShEditMode( static_cast<EditMode>(nInt32) ); + } + } + // This one is kept for compatibility. Old value read from sUNO_View_EditModeStandard + // is used. New value will be written into sUNO_View_EditMode. + // Values from sUNO_View_EditModeNotes and sUNO_View_EditModeHangout will be ignored. + else if ( rValue.Name == sUNO_View_EditModeStandard ) + { + if( rValue.Value >>= nInt32 ) + { + SdDrawDocument* pDoc = dynamic_cast< SdDrawDocument* >( GetModel() ); + if( pDoc && pDoc->GetDocSh() && ( SfxObjectCreateMode::EMBEDDED == pDoc->GetDocSh()->GetCreateMode() ) ) + SetViewShEditMode( static_cast<EditMode>(nInt32) ); + } + } + else if ( rValue.Name == sUNO_View_VisibleAreaTop ) + { + sal_Int32 nTop = 0; + if( rValue.Value >>= nTop ) + { + ::tools::Rectangle aVisArea( GetVisArea() ); + aVisArea.AdjustBottom(nTop - aVisArea.Top() ); + aVisArea.SetTop( nTop ); + SetVisArea( aVisArea ); + } + } + else if ( rValue.Name == sUNO_View_VisibleAreaLeft ) + { + sal_Int32 nLeft = 0; + if( rValue.Value >>= nLeft ) + { + ::tools::Rectangle aVisArea( GetVisArea() ); + aVisArea.AdjustRight(nLeft - aVisArea.Left() ); + aVisArea.SetLeft( nLeft ); + SetVisArea( aVisArea ); + } + } + else if ( rValue.Name == sUNO_View_VisibleAreaWidth ) + { + sal_Int32 nWidth = 0; + if( rValue.Value >>= nWidth ) + { + ::tools::Rectangle aVisArea( GetVisArea() ); + aVisArea.SetRight( aVisArea.Left() + nWidth - 1 ); + SetVisArea( aVisArea ); + } + } + else if ( rValue.Name == sUNO_View_VisibleAreaHeight ) + { + sal_Int32 nHeight = 0; + if( rValue.Value >>= nHeight ) + { + ::tools::Rectangle aVisArea( GetVisArea() ); + aVisArea.SetBottom( nHeight + aVisArea.Top() - 1 ); + SetVisArea( aVisArea ); + } + } + + else if ( rValue.Name == sUNO_View_GridIsVisible ) + { + if( rValue.Value >>= bBool ) + { + SetGridVisible( bBool ); + } + } + + else if ( rValue.Name == sUNO_View_IsSnapToGrid ) + { + if( rValue.Value >>= bBool ) + { + SetGridSnap( bBool ); + } + } + else if ( rValue.Name == sUNO_View_GridIsFront ) + { + if( rValue.Value >>= bBool ) + { + SetGridFront( bBool ); + } + } + else if ( rValue.Name == sUNO_View_IsSnapToPageMargins ) + { + if( rValue.Value >>= bBool ) + { + SetBordSnap( bBool ); + } + } + else if ( rValue.Name == sUNO_View_IsSnapToSnapLines ) + { + if( rValue.Value >>= bBool ) + { + SetHlplSnap( bBool ); + } + } + else if ( rValue.Name == sUNO_View_IsSnapToObjectFrame ) + { + if( rValue.Value >>= bBool ) + { + SetOFrmSnap( bBool ); + } + } + else if ( rValue.Name == sUNO_View_IsSnapToObjectPoints ) + { + if( rValue.Value >>= bBool ) + { + SetOPntSnap( bBool ); + } + } + else if ( rValue.Name == sUNO_View_IsPlusHandlesAlwaysVisible ) + { + if( rValue.Value >>= bBool ) + { + SetPlusHandlesAlwaysVisible( bBool ); + } + } + else if ( rValue.Name == sUNO_View_IsFrameDragSingles ) + { + if( rValue.Value >>= bBool ) + { + SetFrameDragSingles( bBool ); + } + } + else if ( rValue.Name == sUNO_View_EliminatePolyPointLimitAngle ) + { + if( rValue.Value >>= nInt32 ) + { + SetEliminatePolyPointLimitAngle( Degree100(nInt32) ); + } + } + else if ( rValue.Name == sUNO_View_IsEliminatePolyPoints ) + { + if( rValue.Value >>= bBool ) + { + SetEliminatePolyPoints( bBool ); + } + } + else if ( rValue.Name == sUNO_View_ActiveLayer ) + { + if( rValue.Value >>= aString ) + { + SetActiveLayer( aString ); + } + } + else if ( rValue.Name == sUNO_View_NoAttribs ) + { + if( rValue.Value >>= bBool ) + { + SetNoAttribs( bBool ); + } + } + else if ( rValue.Name == sUNO_View_NoColors ) + { + if( rValue.Value >>= bBool ) + { + SetNoColors( bBool ); + } + } + else if ( rValue.Name == sUNO_View_GridCoarseWidth ) + { + if( rValue.Value >>= nInt32 ) + { + const Size aCoarse( nInt32, GetGridCoarse().Height() ); + SetGridCoarse( aCoarse ); + } + } + else if ( rValue.Name == sUNO_View_GridCoarseHeight ) + { + if( rValue.Value >>= nInt32 ) + { + const Size aCoarse( GetGridCoarse().Width(), nInt32 ); + SetGridCoarse( aCoarse ); + } + } + else if ( rValue.Name == sUNO_View_GridFineWidth ) + { + if( rValue.Value >>= nInt32 ) + { + const Size aCoarse( nInt32, GetGridFine().Height() ); + SetGridFine( aCoarse ); + } + } + else if ( rValue.Name == sUNO_View_GridFineHeight ) + { + if( rValue.Value >>= nInt32 ) + { + const Size aCoarse( GetGridFine().Width(), nInt32 ); + SetGridFine( aCoarse ); + } + } + else if ( rValue.Name == sUNO_View_IsAngleSnapEnabled ) + { + if( rValue.Value >>= bBool ) + { + SetAngleSnapEnabled( bBool ); + } + } + else if ( rValue.Name == sUNO_View_SnapAngle ) + { + if( rValue.Value >>= nInt32 ) + { + SetSnapAngle( Degree100(nInt32) ); + } + } + else if ( rValue.Name == sUNO_View_GridSnapWidthXNumerator ) + { + rValue.Value >>= aSnapGridWidthXNum; + } + else if ( rValue.Name == sUNO_View_GridSnapWidthXDenominator ) + { + rValue.Value >>= aSnapGridWidthXDom; + } + else if ( rValue.Name == sUNO_View_GridSnapWidthYNumerator ) + { + rValue.Value >>= aSnapGridWidthYNum; + } + else if ( rValue.Name == sUNO_View_GridSnapWidthYDenominator ) + { + rValue.Value >>= aSnapGridWidthYDom; + } + else if (!bImpress && rValue.Name == sUNO_View_VisibleLayers ) + { + SdrLayerIDSet aSdrLayerIDSets; + aSdrLayerIDSets.PutValue( rValue.Value ); + SetVisibleLayers( aSdrLayerIDSets ); + } + else if (!bImpress && rValue.Name == sUNO_View_PrintableLayers ) + { + SdrLayerIDSet aSdrLayerIDSets; + aSdrLayerIDSets.PutValue( rValue.Value ); + SetPrintableLayers( aSdrLayerIDSets ); + } + else if (!bImpress && rValue.Name == sUNO_View_LockedLayers ) + { + SdrLayerIDSet aSdrLayerIDSets; + aSdrLayerIDSets.PutValue( rValue.Value ); + SetLockedLayers( aSdrLayerIDSets ); + } + } + + SetViewShEditModeOnLoad(EditMode::Page); + + const Fraction aSnapGridWidthX( aSnapGridWidthXNum, aSnapGridWidthXDom ); + const Fraction aSnapGridWidthY( aSnapGridWidthYNum, aSnapGridWidthYDom ); + + SetSnapGridWidth( aSnapGridWidthX, aSnapGridWidthY ); +} + +void FrameView::SetPreviousViewShellType (ViewShell::ShellType eType) +{ + mePreviousViewShellType = eType; +} + +void FrameView::SetViewShellTypeOnLoad (ViewShell::ShellType eType) +{ + meViewShellTypeOnLoad = eType; +} + +void FrameView::SetSelectedPage(sal_uInt16 nPage) +{ + mnSelectedPage = nPage; +} + +void FrameView::SetIsNavigatorShowingAllShapes (const bool bIsNavigatorShowingAllShapes) +{ + mbIsNavigatorShowingAllShapes = bIsNavigatorShowingAllShapes; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/grviewsh.cxx b/sd/source/ui/view/grviewsh.cxx new file mode 100644 index 000000000..b914b2da8 --- /dev/null +++ b/sd/source/ui/view/grviewsh.cxx @@ -0,0 +1,88 @@ +/* -*- 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 <GraphicViewShell.hxx> +#include <LayerTabBar.hxx> +#include <FrameView.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/viewfrm.hxx> + +constexpr sal_Int32 TAB_HEIGHT_MARGIN = 10; + +namespace sd { + +GraphicViewShell::GraphicViewShell ( + ViewShellBase& rViewShellBase, + vcl::Window* pParentWindow, + FrameView* pFrameView) + : DrawViewShell ( + rViewShellBase, + pParentWindow, + PageKind::Standard, + pFrameView) +{ + ConstructGraphicViewShell(); +} + +GraphicViewShell::~GraphicViewShell() +{ +} + +void GraphicViewShell::ConstructGraphicViewShell() +{ + meShellType = ST_DRAW; + + mpLayerTabBar.reset (VclPtr<LayerTabBar>::Create(this, GetParentWindow())); + + // #i67363# no layer tabbar in preview mode + if ( !GetObjectShell()->IsPreview() ) + mpLayerTabBar->Show(); +} + +void GraphicViewShell::ChangeEditMode ( + EditMode eMode, + bool ) +{ + // There is no page tab that could be shown instead of the layer tab. + // Therefore we have it always visible regardless of what the caller + // said. (We have to change the callers behaviour, of course.) + DrawViewShell::ChangeEditMode (eMode, true); +} + +void GraphicViewShell::ArrangeGUIElements() +{ + if (mpLayerTabBar && mpLayerTabBar->IsVisible()) + { + Size aSize = mpLayerTabBar->GetSizePixel(); + const Size aFrameSize (GetViewFrame()->GetWindow().GetOutputSizePixel()); + + aSize.setHeight(GetParentWindow()->GetFont().GetFontHeight() + TAB_HEIGHT_MARGIN); + aSize.setWidth( aFrameSize.Width() ); + + Point aPos (0, maViewSize.Height() - aSize.Height()); + + mpLayerTabBar->SetPosSizePixel (aPos, aSize); + } + + DrawViewShell::ArrangeGUIElements(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/outlnvs2.cxx b/sd/source/ui/view/outlnvs2.cxx new file mode 100644 index 000000000..2a890cec1 --- /dev/null +++ b/sd/source/ui/view/outlnvs2.cxx @@ -0,0 +1,636 @@ +/* -*- 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 <OutlineViewShell.hxx> + +#include <app.hrc> +#include <svx/hlnkitem.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/request.hxx> +#include <sfx2/zoomitem.hxx> +#include <svx/svxids.hrc> +#include <svx/svdoutl.hxx> +#include <svx/zoomslideritem.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/flditem.hxx> +#include <editeng/editstat.hxx> +#include <unotools/useroptions.hxx> + +#include <sfx2/viewfrm.hxx> +#include <Window.hxx> +#include <fubullet.hxx> +#include <fuolbull.hxx> +#include <fuscale.hxx> +#include <fuchar.hxx> +#include <fuinsfil.hxx> +#include <fuprobjs.hxx> +#include <futhes.hxx> +#include <futempl.hxx> +#include <fusldlg.hxx> +#include <zoomlist.hxx> +#include <fuexpand.hxx> +#include <fusumry.hxx> +#include <fucushow.hxx> +#include <sdabstdlg.hxx> +#include <DrawDocShell.hxx> +#include <DrawViewShell.hxx> +#include <OutlineView.hxx> +#include <slideshow.hxx> +#include <memory> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; + +namespace sd { + +/************************************************************************/ + +/** + * SfxRequests for temporary functions + */ + +void OutlineViewShell::FuTemporary(SfxRequest &rReq) +{ + DeactivateCurrentFunction(); + + OutlinerView* pOutlinerView = pOlView->GetViewByWindow( GetActiveWindow() ); + sal_uInt16 nSId = rReq.GetSlot(); + + switch( nSId ) + { + case SID_ATTR_ZOOM: + { + const SfxItemSet* pArgs = rReq.GetArgs(); + + if ( pArgs ) + { + SvxZoomType eZT = pArgs->Get( SID_ATTR_ZOOM ).GetType(); + switch( eZT ) + { + case SvxZoomType::PERCENT: + SetZoom( static_cast<::tools::Long>( pArgs->Get( SID_ATTR_ZOOM ).GetValue()) ); + Invalidate( SID_ATTR_ZOOM ); + Invalidate( SID_ATTR_ZOOMSLIDER ); + break; + default: + break; + } + rReq.Done(); + } + else + { + // open the zoom dialog here + SetCurrentFunction( FuScale::Create( this, GetActiveWindow(), pOlView.get(), GetDoc(), rReq ) ); + } + Cancel(); + } + break; + + case SID_ATTR_ZOOMSLIDER: + { + const SfxItemSet* pArgs = rReq.GetArgs(); + + const SfxUInt16Item* pScale = (pArgs && pArgs->Count () == 1) ? + rReq.GetArg(SID_ATTR_ZOOMSLIDER) : nullptr; + if (pScale && CHECK_RANGE (5, pScale->GetValue (), 3000)) + { + SetZoom (pScale->GetValue ()); + + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_ATTR_ZOOM ); + rBindings.Invalidate( SID_ZOOM_IN ); + rBindings.Invalidate( SID_ZOOM_OUT ); + rBindings.Invalidate( SID_ATTR_ZOOMSLIDER ); + + } + + Cancel(); + rReq.Done (); + break; + } + + case SID_ZOOM_IN: + { + SetZoom( std::min<::tools::Long>( GetActiveWindow()->GetZoom() * 2, GetActiveWindow()->GetMaxZoom() ) ); + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), + GetActiveWindow()->GetOutputSizePixel()) ); + mpZoomList->InsertZoomRect(aVisAreaWin); + Invalidate( SID_ATTR_ZOOM ); + Invalidate( SID_ZOOM_IN ); + Invalidate(SID_ZOOM_OUT); + Invalidate( SID_ATTR_ZOOMSLIDER ); + Cancel(); + rReq.Done(); + } + break; + + case SID_SIZE_REAL: + { + SetZoom( 100 ); + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), + GetActiveWindow()->GetOutputSizePixel()) ); + mpZoomList->InsertZoomRect(aVisAreaWin); + Invalidate( SID_ATTR_ZOOM ); + Invalidate( SID_ATTR_ZOOMSLIDER ); + Cancel(); + rReq.Done(); + } + break; + + case SID_ZOOM_OUT: + { + SetZoom( std::max<::tools::Long>( GetActiveWindow()->GetZoom() / 2, GetActiveWindow()->GetMinZoom() ) ); + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), + GetActiveWindow()->GetOutputSizePixel()) ); + mpZoomList->InsertZoomRect(aVisAreaWin); + Invalidate( SID_ATTR_ZOOM ); + Invalidate( SID_ZOOM_OUT); + Invalidate( SID_ZOOM_IN ); + Invalidate( SID_ATTR_ZOOMSLIDER ); + Cancel(); + rReq.Done(); + } + break; + + case SID_OUTLINE_COLLAPSE_ALL: + { + pOutlinerView->CollapseAll(); + Cancel(); + rReq.Done(); + } + break; + + case SID_OUTLINE_COLLAPSE: + { + pOutlinerView->Collapse(); + Cancel(); + rReq.Done(); + } + break; + + case SID_OUTLINE_EXPAND_ALL: + { + pOutlinerView->ExpandAll(); + Cancel(); + rReq.Done(); + } + break; + + case SID_OUTLINE_EXPAND: + { + pOutlinerView->Expand(); + Cancel(); + rReq.Done(); + } + break; + + case SID_OUTLINE_FORMAT: + { + ::Outliner* pOutl = pOutlinerView->GetOutliner(); + pOutl->SetFlatMode( !pOutl->IsFlatMode() ); + Invalidate( SID_COLORVIEW ); + Cancel(); + rReq.Done(); + } + break; + + case SID_SELECTALL: + { + ::Outliner& rOutl = pOlView->GetOutliner(); + sal_Int32 nParaCount = rOutl.GetParagraphCount(); + if (nParaCount > 0) + { + pOutlinerView->SelectRange( 0, nParaCount ); + } + Cancel(); + } + break; + + case SID_PRESENTATION: + case SID_PRESENTATION_CURRENT_SLIDE: + case SID_REHEARSE_TIMINGS: + { + pOlView->PrepareClose(); + slideshowhelp::ShowSlideShow(rReq, *GetDoc()); + Cancel(); + rReq.Done(); + } + break; + + case SID_COLORVIEW: + { + ::Outliner* pOutl = pOutlinerView->GetOutliner(); + EEControlBits nCntrl = pOutl->GetControlWord(); + + if ( !(nCntrl & EEControlBits::NOCOLORS) ) + { + // color view is enabled: disable + pOutl->SetControlWord(nCntrl | EEControlBits::NOCOLORS); + } + else + { + // color view is disabled: enable + pOutl->SetControlWord(nCntrl & ~EEControlBits::NOCOLORS); + } + + InvalidateWindows(); + Invalidate( SID_COLORVIEW ); + Cancel(); + rReq.Done(); + } + break; + + case SID_STYLE_EDIT: + case SID_STYLE_UPDATE_BY_EXAMPLE: + { + if( rReq.GetArgs() ) + { + SetCurrentFunction( FuTemplate::Create( this, GetActiveWindow(), pOlView.get(), GetDoc(), rReq ) ); + Cancel(); + } + + rReq.Ignore (); + } + break; + + case SID_PRESENTATION_DLG: + { + SetCurrentFunction( FuSlideShowDlg::Create( this, GetActiveWindow(), pOlView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + + case SID_REMOTE_DLG: + { +#ifdef ENABLE_SDREMOTE + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateRemoteDialog(GetFrameWeld())); + pDlg->Execute(); +#endif + } + break; + + case SID_CUSTOMSHOW_DLG: + { + SetCurrentFunction( FuCustomShowDlg::Create( this, GetActiveWindow(), pOlView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + + case SID_PHOTOALBUM: + { + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + vcl::Window* pWin = GetActiveWindow(); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateSdPhotoAlbumDialog( + pWin ? pWin->GetFrameWeld() : nullptr, + GetDoc())); + + pDlg->Execute(); + + Cancel(); + rReq.Ignore (); + } + break; + } + + if(HasCurrentFunction()) + GetCurrentFunction()->Activate(); + + Invalidate( SID_OUTLINE_COLLAPSE_ALL ); + Invalidate( SID_OUTLINE_COLLAPSE ); + Invalidate( SID_OUTLINE_EXPAND_ALL ); + Invalidate( SID_OUTLINE_EXPAND ); + + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_OUTLINE_LEFT ); + rBindings.Invalidate( SID_OUTLINE_RIGHT ); + rBindings.Invalidate( SID_OUTLINE_UP ); + rBindings.Invalidate( SID_OUTLINE_DOWN ); + + Invalidate( SID_OUTLINE_FORMAT ); + Invalidate( SID_COLORVIEW ); + Invalidate(SID_CUT); + Invalidate(SID_COPY); + Invalidate(SID_PASTE); + Invalidate(SID_PASTE_UNFORMATTED); +} + +void OutlineViewShell::FuTemporaryModify(SfxRequest &rReq) +{ + sal_uInt16 nSId = rReq.GetSlot(); + std::unique_ptr<OutlineViewModelChangeGuard, o3tl::default_delete<OutlineViewModelChangeGuard>> aGuard; + if (nSId != SID_OUTLINE_BULLET && nSId != FN_SVX_SET_BULLET && nSId != FN_SVX_SET_NUMBER) + { + aGuard.reset( new OutlineViewModelChangeGuard(*pOlView) ); + } + DeactivateCurrentFunction(); + + OutlinerView* pOutlinerView = pOlView->GetViewByWindow( GetActiveWindow() ); + //sal_uInt16 nSId = rReq.GetSlot(); + + switch( nSId ) + { + case SID_HYPERLINK_SETLINK: + { + const SfxItemSet* pReqArgs = rReq.GetArgs(); + + if (pReqArgs) + { + const SvxHyperlinkItem* pHLItem = + &pReqArgs->Get(SID_HYPERLINK_SETLINK); + + SvxFieldItem aURLItem(SvxURLField(pHLItem->GetURL(), + pHLItem->GetName(), + SvxURLFormat::Repr), EE_FEATURE_FIELD); + ESelection aSel( pOutlinerView->GetSelection() ); + pOutlinerView->InsertField(aURLItem); + if ( aSel.nStartPos <= aSel.nEndPos ) + aSel.nEndPos = aSel.nStartPos + 1; + else + aSel.nStartPos = aSel.nEndPos + 1; + pOutlinerView->SetSelection( aSel ); + } + + Cancel(); + rReq.Ignore (); + } + break; + + case FN_INSERT_SOFT_HYPHEN: + case FN_INSERT_HARDHYPHEN: + case FN_INSERT_HARD_SPACE: + case FN_INSERT_NNBSP: + case SID_INSERT_RLM : + case SID_INSERT_LRM : + case SID_INSERT_WJ : + case SID_INSERT_ZWSP: + case SID_CHARMAP: + { + SetCurrentFunction( FuBullet::Create( this, GetActiveWindow(), pOlView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + + case SID_OUTLINE_BULLET: + case FN_SVX_SET_BULLET: + case FN_SVX_SET_NUMBER: + { + SetCurrentFunction( FuBulletAndPosition::Create( this, GetActiveWindow(), pOlView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + + case SID_THESAURUS: + { + SetCurrentFunction( FuThesaurus::Create( this, GetActiveWindow(), pOlView.get(), GetDoc(), rReq ) ); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_CHAR_DLG_EFFECT: + case SID_CHAR_DLG: + { + SetCurrentFunction( FuChar::Create( this, GetActiveWindow(), pOlView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + + case SID_INSERTFILE: + { + SetCurrentFunction( FuInsertFile::Create(this, GetActiveWindow(), pOlView.get(), GetDoc(), rReq) ); + Cancel(); + } + break; + + case SID_PRESENTATIONOBJECT: + { + SetCurrentFunction( FuPresentationObjects::Create(this, GetActiveWindow(), pOlView.get(), GetDoc(), rReq) ); + Cancel(); + } + break; + + case SID_SET_DEFAULT: + { + pOutlinerView->RemoveAttribs(true); // sal_True = also paragraph attributes + Cancel(); + rReq.Done(); + } + break; + + case SID_SUMMARY_PAGE: + { + pOlView->SetSelectedPages(); + SetCurrentFunction( FuSummaryPage::Create( this, GetActiveWindow(), pOlView.get(), GetDoc(), rReq ) ); + pOlView->GetOutliner().Clear(); + pOlView->FillOutliner(); + pOlView->GetActualPage(); + Cancel(); + } + break; + + case SID_EXPAND_PAGE: + { + pOlView->SetSelectedPages(); + SetCurrentFunction( FuExpandPage::Create( this, GetActiveWindow(), pOlView.get(), GetDoc(), rReq ) ); + pOlView->GetOutliner().Clear(); + pOlView->FillOutliner(); + pOlView->GetActualPage(); + Cancel(); + } + break; + + case SID_INSERT_FLD_DATE_FIX: + case SID_INSERT_FLD_DATE_VAR: + case SID_INSERT_FLD_TIME_FIX: + case SID_INSERT_FLD_TIME_VAR: + case SID_INSERT_FLD_AUTHOR: + case SID_INSERT_FLD_PAGE: + case SID_INSERT_FLD_PAGE_TITLE: + case SID_INSERT_FLD_PAGES: + case SID_INSERT_FLD_FILE: + { + std::unique_ptr<SvxFieldItem> pFieldItem; + + switch( nSId ) + { + case SID_INSERT_FLD_DATE_FIX: + pFieldItem.reset(new SvxFieldItem( + SvxDateField( Date( Date::SYSTEM ), SvxDateType::Fix ), EE_FEATURE_FIELD )); + break; + + case SID_INSERT_FLD_DATE_VAR: + pFieldItem.reset(new SvxFieldItem( SvxDateField(), EE_FEATURE_FIELD )); + break; + + case SID_INSERT_FLD_TIME_FIX: + pFieldItem.reset(new SvxFieldItem( + SvxExtTimeField( ::tools::Time( ::tools::Time::SYSTEM ), SvxTimeType::Fix ), EE_FEATURE_FIELD )); + break; + + case SID_INSERT_FLD_TIME_VAR: + pFieldItem.reset(new SvxFieldItem( SvxExtTimeField(), EE_FEATURE_FIELD )); + break; + + case SID_INSERT_FLD_AUTHOR: + { + SvtUserOptions aUserOptions; + pFieldItem.reset(new SvxFieldItem( + SvxAuthorField( + aUserOptions.GetFirstName(), aUserOptions.GetLastName(), aUserOptions.GetID() ) + , EE_FEATURE_FIELD )); + } + break; + + case SID_INSERT_FLD_PAGE: + pFieldItem.reset(new SvxFieldItem( SvxPageField(), EE_FEATURE_FIELD )); + break; + + case SID_INSERT_FLD_PAGE_TITLE: + pFieldItem.reset(new SvxFieldItem( SvxPageTitleField(), EE_FEATURE_FIELD)); + break; + + case SID_INSERT_FLD_PAGES: + pFieldItem.reset(new SvxFieldItem( SvxPagesField(), EE_FEATURE_FIELD )); + break; + + case SID_INSERT_FLD_FILE: + { + OUString aName; + if( GetDocSh()->HasName() ) + aName = GetDocSh()->GetMedium()->GetName(); + //else + // aName = GetDocSh()->GetName(); + pFieldItem.reset(new SvxFieldItem( SvxExtFileField( aName ), EE_FEATURE_FIELD )); + } + break; + } + + const SvxFieldItem* pOldFldItem = pOutlinerView->GetFieldAtSelection(); + + if( pOldFldItem && ( nullptr != dynamic_cast< const SvxURLField *>( pOldFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxDateField *>( pOldFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxTimeField *>( pOldFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxExtTimeField *>( pOldFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxExtFileField *>( pOldFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxAuthorField *>( pOldFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxPageField *>( pOldFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxPagesField *>( pOldFldItem->GetField() )) ) + { + // select field, so it gets deleted on Insert + ESelection aSel = pOutlinerView->GetSelection(); + if( aSel.nStartPos == aSel.nEndPos ) + aSel.nEndPos++; + pOutlinerView->SetSelection( aSel ); + } + + if( pFieldItem ) + pOutlinerView->InsertField( *pFieldItem ); + + pFieldItem.reset(); + + Cancel(); + rReq.Ignore (); + } + break; + + case SID_MODIFY_FIELD: + { + const SvxFieldItem* pFldItem = pOutlinerView->GetFieldAtSelection(); + + if( pFldItem && (nullptr != dynamic_cast< const SvxDateField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxAuthorField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxExtFileField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxExtTimeField *>( pFldItem->GetField() ) ) ) + { + // Dialog... + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + vcl::Window* pWin = GetActiveWindow(); + ScopedVclPtr<AbstractSdModifyFieldDlg> pDlg(pFact->CreateSdModifyFieldDlg(pWin ? pWin->GetFrameWeld() : nullptr, pFldItem->GetField(), pOutlinerView->GetAttribs() )); + if( pDlg->Execute() == RET_OK ) + { + std::unique_ptr<SvxFieldData> pField(pDlg->GetField()); + if( pField ) + { + SvxFieldItem aFieldItem( *pField, EE_FEATURE_FIELD ); + //pOLV->DeleteSelected(); <-- unfortunately missing! + // select field, so it gets deleted on Insert + ESelection aSel = pOutlinerView->GetSelection(); + bool bSel = true; + if( aSel.nStartPos == aSel.nEndPos ) + { + bSel = false; + aSel.nEndPos++; + } + pOutlinerView->SetSelection( aSel ); + + pOutlinerView->InsertField( aFieldItem ); + + // reset selection to original state + if( !bSel ) + aSel.nEndPos--; + pOutlinerView->SetSelection( aSel ); + + pField.reset(); + } + + SfxItemSet aSet( pDlg->GetItemSet() ); + if( aSet.Count() ) + { + pOutlinerView->SetAttribs( aSet ); + + ::Outliner* pOutliner = pOutlinerView->GetOutliner(); + if( pOutliner ) + pOutliner->UpdateFields(); + } + } + } + + Cancel(); + rReq.Ignore (); + } + break; + } + + if(HasCurrentFunction()) + GetCurrentFunction()->Activate(); + + Invalidate( SID_OUTLINE_COLLAPSE_ALL ); + Invalidate( SID_OUTLINE_COLLAPSE ); + Invalidate( SID_OUTLINE_EXPAND_ALL ); + Invalidate( SID_OUTLINE_EXPAND ); + + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_OUTLINE_LEFT ); + rBindings.Invalidate( SID_OUTLINE_RIGHT ); + rBindings.Invalidate( SID_OUTLINE_UP ); + rBindings.Invalidate( SID_OUTLINE_DOWN ); + + Invalidate( SID_OUTLINE_FORMAT ); + Invalidate( SID_COLORVIEW ); + Invalidate(SID_CUT); + Invalidate(SID_COPY); + Invalidate(SID_PASTE); + Invalidate(SID_PASTE_UNFORMATTED); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/outlnvsh.cxx b/sd/source/ui/view/outlnvsh.cxx new file mode 100644 index 000000000..0eb351f91 --- /dev/null +++ b/sd/source/ui/view/outlnvsh.cxx @@ -0,0 +1,1883 @@ +/* -*- 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 <OutlineViewShell.hxx> + +#include <helpids.h> +#include <app.hrc> +#include <svx/hyperdlg.hxx> +#include <svx/zoomslideritem.hxx> +#include <svx/svdundo.hxx> + +#include <sfx2/infobar.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/zoomitem.hxx> +#include <editeng/editview.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/flditem.hxx> +#include <sfx2/shell.hxx> +#include <sfx2/request.hxx> +#include <svx/hlnkitem.hxx> +#include <svx/svdotext.hxx> +#include <svx/svdoutl.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/scrbar.hxx> +#include <vcl/settings.hxx> + +#include <sal/log.hxx> +#include <svl/stritem.hxx> +#include <svl/whiter.hxx> +#include <editeng/editstat.hxx> +#include <svl/itempool.hxx> +#include <sfx2/tplpitem.hxx> +#include <sfx2/sidebar/SidebarChildWindow.hxx> +#include <vcl/EnumContext.hxx> +#include <sot/formats.hxx> +#include <com/sun/star/linguistic2/XThesaurus.hpp> +#include <editeng/unolingu.hxx> +#include <editeng/outlobj.hxx> +#include <svl/cjkoptions.hxx> +#include <svtools/cliplistener.hxx> +#include <svl/srchitem.hxx> +#include <editeng/editobj.hxx> +#include <fubullet.hxx> + +#include <strings.hrc> + +#include <Window.hxx> +#include <drawdoc.hxx> +#include <sdresid.hxx> +#include <sdpage.hxx> +#include <fuoltext.hxx> +#include <FrameView.hxx> +#include <zoomlist.hxx> +#include <stlsheet.hxx> +#include <SdUnoOutlineView.hxx> +#include <SpellDialogChildWindow.hxx> + +#include <AccessibleOutlineView.hxx> +#include <ViewShellBase.hxx> +#include <DrawController.hxx> +#include <DrawDocShell.hxx> +#include <OutlineView.hxx> +#include <framework/FrameworkHelper.hxx> +#include <sfx2/devtools/DevelopmentToolChildWindow.hxx> + +#include <memory> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::linguistic2; + +using namespace sd; + +#define ShellClass_OutlineViewShell +#include <sdslots.hxx> + +namespace sd { + +#define MIN_ZOOM 10 // minimum zoom factor +#define MAX_ZOOM 1000 // maximum zoom factor + +/** + * Declare SFX-Slotmap and standard interface + */ +SFX_IMPL_INTERFACE(OutlineViewShell, SfxShell) + +void OutlineViewShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterPopupMenu("outline"); + + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_TOOLS, SfxVisibilityFlags::Standard | SfxVisibilityFlags::FullScreen | SfxVisibilityFlags::Server, + ToolbarId::Outline_Toolbox); + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_APPLICATION, SfxVisibilityFlags::Standard | SfxVisibilityFlags::Client | SfxVisibilityFlags::Viewer | SfxVisibilityFlags::ReadonlyDoc, + ToolbarId::Draw_Viewer_Toolbox); + + GetStaticInterface()->RegisterChildWindow(SfxInfoBarContainerChild::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SvxHlinkDlgWrapper::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(::sd::SpellDialogChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SID_SEARCH_DLG); + GetStaticInterface()->RegisterChildWindow(sfx2::sidebar::SidebarChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(DevelopmentToolChildWindow::GetChildWindowId()); +} + + +/** + * common initialization part of both constructors + */ +void OutlineViewShell::Construct() +{ + bool bModified = GetDoc()->IsChanged(); + + meShellType = ST_OUTLINE; + Size aSize(29700, 21000); + Point aWinPos (0, 0); + Point aViewOrigin(0, 0); + GetActiveWindow()->SetMinZoomAutoCalc(false); + GetActiveWindow()->SetMinZoom( MIN_ZOOM ); + GetActiveWindow()->SetMaxZoom( MAX_ZOOM ); + InitWindows(aViewOrigin, aSize, aWinPos); + pOlView.reset( new OutlineView(*GetDocSh(), GetActiveWindow(), *this) ); + mpView = pOlView.get(); // Pointer of base class ViewShell + + SetPool( &GetDoc()->GetPool() ); + + SetZoom(69); + + // Apply settings of FrameView + ReadFrameViewData(mpFrameView); + + ::Outliner& rOutl = pOlView->GetOutliner(); + rOutl.SetUpdateLayout(true); + + if (!bModified) + { + rOutl.ClearModifyFlag(); + } + + pLastPage = GetActualPage(); + + SetName( "OutlineViewShell" ); + + GetActiveWindow()->SetHelpId( HID_SDOUTLINEVIEWSHELL ); +} + +Reference<drawing::XDrawSubController> OutlineViewShell::CreateSubController() +{ + Reference<drawing::XDrawSubController> xSubController; + + if (IsMainViewShell()) + { + // Create uno sub controller for the main view shell. + xSubController.set( new SdUnoOutlineView(*this) ); + } + + return xSubController; +} + +/** + * Default constructor, windows must not center themselves automatically + */ +OutlineViewShell::OutlineViewShell ( + SfxViewFrame* /*pFrame*/, + ViewShellBase& rViewShellBase, + vcl::Window* pParentWindow, + FrameView* pFrameViewArgument) + : ViewShell(pParentWindow, rViewShellBase), + pLastPage( nullptr ), + bPastePossible(false), + mbInitialized(false) + +{ + if (pFrameViewArgument != nullptr) + mpFrameView = pFrameViewArgument; + else + mpFrameView = new FrameView(GetDoc()); + + mpFrameView->Connect(); + + Construct(); + + SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::OutlineText)); + + m_StrOldPageName.clear(); + + doShow(); +} + +OutlineViewShell::~OutlineViewShell() +{ + DisposeFunctions(); + + pOlView.reset(); + + mpFrameView->Disconnect(); + + if ( mxClipEvtLstnr.is() ) + { + mxClipEvtLstnr->RemoveListener( GetActiveWindow() ); + mxClipEvtLstnr->ClearCallbackLink(); // prevent callback if another thread is waiting + } +} + +void OutlineViewShell::Shutdown() +{ + ViewShell::Shutdown(); + + PrepareClose(); +} + +/** + * Paint method: the event gets forwarded from pWindow to the Viewshell + * and the current function + */ +void OutlineViewShell::Paint(const ::tools::Rectangle& rRect, ::sd::Window* pWin) +{ + if (pOlView) + { + pOlView->Paint(rRect, pWin); + } +} + +void OutlineViewShell::ArrangeGUIElements () +{ + // Retrieve the current size (thickness) of the scroll bars. That is + // the width of the vertical and the height of the horizontal scroll + // bar. + int nScrollBarSize = + GetParentWindow()->GetSettings().GetStyleSettings().GetScrollBarSize(); + maScrBarWH = Size (nScrollBarSize, nScrollBarSize); + + ViewShell::ArrangeGUIElements (); + + ::sd::Window* pWindow = mpContentWindow.get(); + if (pWindow == nullptr) + return; + + pWindow->SetMinZoomAutoCalc(false); + + // change OutputArea of the OutlinerView + OutlinerView* pOutlinerView = pOlView->GetViewByWindow(pWindow); + + ::tools::Rectangle aWin(Point(0,0), pWindow->GetOutputSizePixel()); + + aWin = pWindow->PixelToLogic(aWin); + pOutlinerView->SetOutputArea(aWin); + + ::tools::Rectangle aVis = pOutlinerView->GetVisArea(); + + ::tools::Rectangle aText(Point(0,0), + Size(pOlView->GetPaperWidth(), + pOlView->GetOutliner().GetTextHeight())); + if (aWin.GetHeight() > aText.Bottom()) + aText.SetBottom( aWin.GetHeight() ); + + if (!aWin.IsEmpty()) // not when opening + { + InitWindows(Point(0,0), aText.GetSize(), aVis.TopLeft()); + UpdateScrollBars(); + } +} + +/** + * Handle SfxRequest for the Controller + */ +void OutlineViewShell::ExecCtrl(SfxRequest &rReq) +{ + sal_uInt16 nSlot = rReq.GetSlot(); + switch ( nSlot ) + { + case SID_MAIL_SCROLLBODY_PAGEDOWN: + { + ExecReq( rReq ); + break; + } + + case SID_OPT_LOCALE_CHANGED: + { + pOlView->GetOutliner().UpdateFields(); + UpdatePreview( GetActualPage() ); + rReq.Done(); + break; + } + + default: + break; + } +} + +/** + * Activate(): during the first invocation the fields get updated + */ +void OutlineViewShell::Activate( bool bIsMDIActivate ) +{ + if ( ! mbInitialized) + { + mbInitialized = true; + SfxRequest aRequest (SID_EDIT_OUTLINER, SfxCallMode::SLOT, GetDoc()->GetItemPool()); + FuPermanent (aRequest); + } + + ViewShell::Activate( bIsMDIActivate ); + SfxShell::BroadcastContextForActivation(true); + + pOlView->SetLinks(); + pOlView->ConnectToApplication(); + + if( bIsMDIActivate ) + { + OutlinerView* pOutlinerView = pOlView->GetViewByWindow( GetActiveWindow() ); + ::Outliner* pOutl = pOutlinerView->GetOutliner(); + pOutl->UpdateFields(); + } +} + +void OutlineViewShell::Deactivate( bool bIsMDIActivate ) +{ + pOlView->DisconnectFromApplication(); + + // Links must be kept also on deactivated viewshell, to allow drag'n'drop + // to function properly + ViewShell::Deactivate( bIsMDIActivate ); +} + +/** + * Set status of Controller-SfxSlots + */ +void OutlineViewShell::GetCtrlState(SfxItemSet &rSet) +{ + if (SfxItemState::DEFAULT == rSet.GetItemState(SID_HYPERLINK_GETLINK)) + { + SvxHyperlinkItem aHLinkItem; + + OutlinerView* pOLV = pOlView->GetViewByWindow(GetActiveWindow()); + if (pOLV) + { + const SvxFieldItem* pFieldItem = pOLV->GetFieldAtSelection(); + if (pFieldItem) + { + ESelection aSel = pOLV->GetSelection(); + if ( abs( aSel.nEndPos - aSel.nStartPos ) == 1 ) + { + const SvxFieldData* pField = pFieldItem->GetField(); + if ( auto pUrlField = dynamic_cast< const SvxURLField *>( pField ) ) + { + aHLinkItem.SetName(pUrlField->GetRepresentation()); + aHLinkItem.SetURL(pUrlField->GetURL()); + aHLinkItem.SetTargetFrame(pUrlField->GetTargetFrame()); + } + } + } + } + rSet.Put(aHLinkItem); + } + rSet.Put( SfxBoolItem( SID_READONLY_MODE, GetDocSh()->IsReadOnly() ) ); + + if ( SfxItemState::DEFAULT == rSet.GetItemState(SID_MAIL_SCROLLBODY_PAGEDOWN) ) + rSet.Put( SfxBoolItem( SID_MAIL_SCROLLBODY_PAGEDOWN, true ) ); + + if ( !(SfxItemState::DEFAULT == rSet.GetItemState(SID_TRANSLITERATE_HALFWIDTH) || + SfxItemState::DEFAULT == rSet.GetItemState(SID_TRANSLITERATE_FULLWIDTH) || + SfxItemState::DEFAULT == rSet.GetItemState(SID_TRANSLITERATE_HIRAGANA) || + SfxItemState::DEFAULT == rSet.GetItemState(SID_TRANSLITERATE_KATAKANA)) ) + return; + + if( !SvtCJKOptions::IsChangeCaseMapEnabled() ) + { + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_HALFWIDTH, false ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_FULLWIDTH, false ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_HIRAGANA, false ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_KATAKANA, false ); + rSet.DisableItem( SID_TRANSLITERATE_HALFWIDTH ); + rSet.DisableItem( SID_TRANSLITERATE_FULLWIDTH ); + rSet.DisableItem( SID_TRANSLITERATE_HIRAGANA ); + rSet.DisableItem( SID_TRANSLITERATE_KATAKANA ); + } + else + { + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_HALFWIDTH, true ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_FULLWIDTH, true ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_HIRAGANA, true ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_KATAKANA, true ); + } +} + +/** + * SfxRequests for support functions + */ +void OutlineViewShell::FuSupport(SfxRequest &rReq) +{ + if( rReq.GetSlot() == SID_STYLE_FAMILY && rReq.GetArgs()) + GetDocSh()->SetStyleFamily(static_cast<SfxStyleFamily>(rReq.GetArgs()->Get( SID_STYLE_FAMILY ).GetValue())); + + bool bPreviewState = false; + sal_uLong nSlot = rReq.GetSlot(); + + std::unique_ptr<OutlineViewModelChangeGuard, o3tl::default_delete<OutlineViewModelChangeGuard>> aGuard; + if( pOlView && ( + (nSlot == SID_TRANSLITERATE_SENTENCE_CASE) || + (nSlot == SID_TRANSLITERATE_TITLE_CASE) || + (nSlot == SID_TRANSLITERATE_TOGGLE_CASE) || + (nSlot == SID_TRANSLITERATE_UPPER) || + (nSlot == SID_TRANSLITERATE_LOWER) || + (nSlot == SID_TRANSLITERATE_HALFWIDTH) || + (nSlot == SID_TRANSLITERATE_FULLWIDTH) || + (nSlot == SID_TRANSLITERATE_HIRAGANA) || + (nSlot == SID_TRANSLITERATE_KATAKANA) || + (nSlot == SID_CUT) || + (nSlot == SID_PASTE) || + (nSlot == SID_PASTE_UNFORMATTED) || + (nSlot == SID_DELETE))) + { + aGuard.reset( new OutlineViewModelChangeGuard( *pOlView ) ); + } + + switch ( nSlot ) + { + case SID_CUT: + { + if(HasCurrentFunction()) + { + GetCurrentFunction()->DoCut(); + } + else if (pOlView) + { + pOlView->DoCut(); + } + rReq.Done(); + bPreviewState = true; + } + break; + + case SID_COPY: + { + if(HasCurrentFunction()) + { + GetCurrentFunction()->DoCopy(); + } + else if (pOlView) + { + pOlView->DoCopy(); + } + rReq.Done(); + bPreviewState = true; + } + break; + + case SID_PASTE: + { + OutlineViewPageChangesGuard aGuard2(pOlView.get()); + + if(HasCurrentFunction()) + { + GetCurrentFunction()->DoPaste(); + } + else if (pOlView) + { + pOlView->DoPaste(); + } + rReq.Done(); + bPreviewState = true; + } + break; + + case SID_PASTE_UNFORMATTED: + { + OutlineViewPageChangesGuard aGuard2(pOlView.get()); + + if(HasCurrentFunction()) + { + GetCurrentFunction()->DoPasteUnformatted(); + } + else if(pOlView) + { + TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( GetActiveWindow() ) ); + if (aDataHelper.GetTransferable().is()) + { + sal_Int8 nAction = DND_ACTION_COPY; + pOlView->InsertData( aDataHelper, + GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(), GetActiveWindow()->GetOutputSizePixel() ).Center() ), + nAction, false, SotClipboardFormatId::STRING); + } + } + + rReq.Ignore (); + } + break; + case SID_DELETE: + { + if( pOlView ) + { + OutlinerView* pOutlView = pOlView->GetViewByWindow(GetActiveWindow()); + if (pOutlView) + { + OutlineViewPageChangesGuard aGuard2(pOlView.get()); + + vcl::KeyCode aKCode(KEY_DELETE); + KeyEvent aKEvt( 0, aKCode ); + pOutlView->PostKeyEvent(aKEvt); + + rtl::Reference<FuPoor> xFunc( GetCurrentFunction() ); + FuOutlineText* pFuOutlineText = dynamic_cast< FuOutlineText* >( xFunc.get() ); + if( pFuOutlineText ) + pFuOutlineText->UpdateForKeyPress (aKEvt); + } + } + rReq.Done(); + bPreviewState = true; + } + break; + + case SID_DRAWINGMODE: + case SID_SLIDE_MASTER_MODE: + case SID_NOTES_MODE: + case SID_NOTES_MASTER_MODE: + case SID_HANDOUT_MASTER_MODE: + case SID_SLIDE_SORTER_MODE: + case SID_OUTLINE_MODE: + framework::FrameworkHelper::Instance(GetViewShellBase())->HandleModeChangeSlot( + nSlot, + rReq); + rReq.Done(); + break; + + case SID_RULER: + SetRuler( !HasRuler() ); + Invalidate( SID_RULER ); + rReq.Done(); + break; + + case SID_ZOOM_PREV: + { + if (mpZoomList->IsPreviousPossible()) + { + SetZoomRect(mpZoomList->GetPreviousZoomRect()); + } + rReq.Done (); + } + break; + + case SID_ZOOM_NEXT: + { + if (mpZoomList->IsNextPossible()) + { + SetZoomRect(mpZoomList->GetNextZoomRect()); + } + rReq.Done (); + } + break; + + case SID_AUTOSPELL_CHECK: + { + GetDoc()->SetOnlineSpell(!GetDoc()->GetOnlineSpell()); + rReq.Done (); + } + break; + + case SID_TRANSLITERATE_SENTENCE_CASE: + case SID_TRANSLITERATE_TITLE_CASE: + case SID_TRANSLITERATE_TOGGLE_CASE: + case SID_TRANSLITERATE_UPPER: + case SID_TRANSLITERATE_LOWER: + case SID_TRANSLITERATE_HALFWIDTH: + case SID_TRANSLITERATE_FULLWIDTH: + case SID_TRANSLITERATE_HIRAGANA: + case SID_TRANSLITERATE_KATAKANA: + { + OutlinerView* pOLV = pOlView ? pOlView->GetViewByWindow( GetActiveWindow() ) : nullptr; + if( pOLV ) + { + TransliterationFlags nType = TransliterationFlags::NONE; + + switch( nSlot ) + { + case SID_TRANSLITERATE_SENTENCE_CASE: + nType = TransliterationFlags::SENTENCE_CASE; + break; + case SID_TRANSLITERATE_TITLE_CASE: + nType = TransliterationFlags::TITLE_CASE; + break; + case SID_TRANSLITERATE_TOGGLE_CASE: + nType = TransliterationFlags::TOGGLE_CASE; + break; + case SID_TRANSLITERATE_UPPER: + nType = TransliterationFlags::LOWERCASE_UPPERCASE; + break; + case SID_TRANSLITERATE_LOWER: + nType = TransliterationFlags::UPPERCASE_LOWERCASE; + break; + case SID_TRANSLITERATE_HALFWIDTH: + nType = TransliterationFlags::FULLWIDTH_HALFWIDTH; + break; + case SID_TRANSLITERATE_FULLWIDTH: + nType = TransliterationFlags::HALFWIDTH_FULLWIDTH; + break; + case SID_TRANSLITERATE_HIRAGANA: + nType = TransliterationFlags::KATAKANA_HIRAGANA; + break; + case SID_TRANSLITERATE_KATAKANA: + nType = TransliterationFlags::HIRAGANA_KATAKANA; + break; + } + + pOLV->TransliterateText( nType ); + } + + rReq.Done(); + bPreviewState = true; + } + break; + + // added Undo/Redo handling + case SID_UNDO : + { + OutlineViewPageChangesGuard aGuard2(pOlView.get()); + ImpSidUndo(rReq); + } + break; + case SID_REDO : + { + OutlineViewPageChangesGuard aGuard2(pOlView.get()); + ImpSidRedo(rReq); + } + break; + + default: + break; + } + + if( bPreviewState ) + Invalidate( SID_PREVIEW_STATE ); + + Invalidate(SID_CUT); + Invalidate(SID_COPY); + Invalidate(SID_PASTE); +} + +/** + * SfxRequests for permanent functions + */ +void OutlineViewShell::FuPermanent(SfxRequest &rReq) +{ + if(HasCurrentFunction()) + { + DeactivateCurrentFunction(true); + } + + switch ( rReq.GetSlot() ) + { + case SID_EDIT_OUTLINER: + { + ::Outliner& rOutl = pOlView->GetOutliner(); + rOutl.GetUndoManager().Clear(); + rOutl.UpdateFields(); + + SetCurrentFunction( FuOutlineText::Create(this,GetActiveWindow(),pOlView.get(),GetDoc(),rReq) ); + + rReq.Done(); + } + break; + + default: + break; + } + + if(HasOldFunction()) + { + GetOldFunction()->Deactivate(); + SetOldFunction(nullptr); + } + + if(HasCurrentFunction()) + { + GetCurrentFunction()->Activate(); + SetOldFunction(GetCurrentFunction()); + } +} + +IMPL_LINK( OutlineViewShell, ClipboardChanged, TransferableDataHelper*, pDataHelper, void ) +{ + bPastePossible = pDataHelper->GetFormatCount() != 0 && + ( pDataHelper->HasFormat( SotClipboardFormatId::STRING ) || + pDataHelper->HasFormat( SotClipboardFormatId::RTF ) || + pDataHelper->HasFormat( SotClipboardFormatId::RICHTEXT ) || + pDataHelper->HasFormat( SotClipboardFormatId::HTML ) ); + + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_PASTE ); + rBindings.Invalidate( SID_PASTE_SPECIAL ); + rBindings.Invalidate( SID_PASTE_UNFORMATTED ); + rBindings.Invalidate( SID_CLIPBOARD_FORMAT_ITEMS ); +} + +/** + * Set Status (Enabled/Disabled) of Menu-SfxSlots + */ +void OutlineViewShell::GetMenuState( SfxItemSet &rSet ) +{ + ViewShell::GetMenuState(rSet); + + rSet.Put(SfxBoolItem(SID_SLIDE_SORTER_MODE, false)); + rSet.Put(SfxBoolItem(SID_DRAWINGMODE, false)); + rSet.Put(SfxBoolItem(SID_SLIDE_MASTER_MODE, false)); + rSet.Put(SfxBoolItem(SID_OUTLINE_MODE, true)); + rSet.Put(SfxBoolItem(SID_NOTES_MODE, false)); + rSet.Put(SfxBoolItem(SID_NOTES_MASTER_MODE, false)); + rSet.Put(SfxBoolItem(SID_HANDOUT_MASTER_MODE, false)); + + if (!mpZoomList->IsNextPossible()) + { + rSet.DisableItem(SID_ZOOM_NEXT); + } + if (!mpZoomList->IsPreviousPossible()) + { + rSet.DisableItem(SID_ZOOM_PREV); + } + + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_ZOOM_IN ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_ZOOM_OUT ) ) + { + if( GetActiveWindow()->GetZoom() <= GetActiveWindow()->GetMinZoom() || GetDocSh()->IsUIActive() ) + rSet.DisableItem( SID_ZOOM_OUT ); + if( GetActiveWindow()->GetZoom() >= GetActiveWindow()->GetMaxZoom() || GetDocSh()->IsUIActive() ) + rSet.DisableItem( SID_ZOOM_IN ); + } + + ::Outliner& rOutl = pOlView->GetOutliner(); + + // allow 'Select All'? + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_SELECTALL ) ) + { + sal_Int32 nParaCount = rOutl.GetParagraphCount(); + bool bDisable = nParaCount == 0; + if (!bDisable && nParaCount == 1) + { + OUString aTest = rOutl.GetText(rOutl.GetParagraph(0)); + if (aTest.isEmpty()) + { + bDisable = true; + } + } + if (bDisable) + rSet.DisableItem(SID_SELECTALL); + } + + // set status of Ruler + rSet.Put( SfxBoolItem( SID_RULER, HasRuler() ) ); + + // Enable formatting? + rSet.Put( SfxBoolItem( SID_OUTLINE_FORMAT, !rOutl.IsFlatMode() ) ); + + if( rOutl.IsFlatMode() ) + rSet.DisableItem( SID_COLORVIEW ); + else + { + // Enable color view? + EEControlBits nCntrl = rOutl.GetControlWord(); + bool bNoColor = false; + if (nCntrl & EEControlBits::NOCOLORS) + bNoColor = true; + + rSet.Put( SfxBoolItem( SID_COLORVIEW, bNoColor ) ); + } + + // Buttons of toolbar + // first the selection dependent ones: COLLAPSE, EXPAND + bool bDisableCollapse = true; + bool bDisableExpand = true; + bool bUnique = true; + OutlinerView* pOutlinerView = pOlView->GetViewByWindow(GetActiveWindow()); + + std::vector<Paragraph*> aSelList; + pOutlinerView->CreateSelectionList(aSelList); + + if (!aSelList.empty()) + { + sal_Int16 nTmpDepth = rOutl.GetDepth( rOutl.GetAbsPos( aSelList.front() ) ); + bool bPage = ::Outliner::HasParaFlag( aSelList.front(), ParaFlag::ISPAGE ); + + for (const Paragraph* pPara : aSelList) + { + sal_Int16 nDepth = rOutl.GetDepth( rOutl.GetAbsPos( pPara ) ); + + if( nDepth != nTmpDepth || bPage != ::Outliner::HasParaFlag( pPara, ParaFlag::ISPAGE )) + bUnique = false; + + if (rOutl.HasChildren(pPara)) + { + if (!rOutl.IsExpanded(pPara)) + bDisableExpand = false; + else + bDisableCollapse = false; + } + } + } + + if (bDisableExpand) + rSet.DisableItem(SID_OUTLINE_EXPAND); + if (bDisableCollapse) + rSet.DisableItem(SID_OUTLINE_COLLAPSE); + + // does the selection provide a unique presentation layout? + // if not, the templates must not be edited + SfxItemSetFixed<SID_STATUS_LAYOUT, SID_STATUS_LAYOUT> aSet(*rSet.GetPool()); + GetStatusBarState(aSet); + OUString aTest = static_cast<const SfxStringItem&>(aSet.Get(SID_STATUS_LAYOUT)).GetValue(); + if (aTest.isEmpty()) + { + bUnique = false; + } + + if (!bUnique) + rSet.DisableItem( SID_PRESENTATIONOBJECT ); + + // now the selection independent ones: COLLAPSE_ALL, EXPAND_ALL + bool bDisableCollapseAll = true; + bool bDisableExpandAll = true; + + // does the selection contain something collapsible/expandable? + if (!bDisableCollapse) + bDisableCollapseAll = false; + if (!bDisableExpand) + bDisableExpandAll = false; + + // otherwise look through all paragraphs + if (bDisableCollapseAll || bDisableExpandAll) + { + sal_Int32 nParaPos = 0; + Paragraph* pPara = rOutl.GetParagraph( nParaPos ); + while (pPara && (bDisableCollapseAll || bDisableExpandAll)) + { + if (!rOutl.IsExpanded(pPara) && rOutl.HasChildren(pPara)) + bDisableExpandAll = false; + + if (rOutl.IsExpanded(pPara) && rOutl.HasChildren(pPara)) + bDisableCollapseAll = false; + + pPara = rOutl.GetParagraph( ++nParaPos ); + } + } + + if (bDisableExpandAll) + rSet.DisableItem(SID_OUTLINE_EXPAND_ALL); + if (bDisableCollapseAll) + rSet.DisableItem(SID_OUTLINE_COLLAPSE_ALL); + + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_PASTE ) ) + { + if ( !mxClipEvtLstnr.is() ) + { + // create listener + mxClipEvtLstnr = new TransferableClipboardListener( LINK( this, OutlineViewShell, ClipboardChanged ) ); + mxClipEvtLstnr->AddListener( GetActiveWindow() ); + + // get initial state + TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( GetActiveWindow() ) ); + bPastePossible = ( aDataHelper.GetFormatCount() != 0 && + ( aDataHelper.HasFormat( SotClipboardFormatId::STRING ) || + aDataHelper.HasFormat( SotClipboardFormatId::RTF ) || + aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) || + aDataHelper.HasFormat( SotClipboardFormatId::HTML ) ) ); + } + + if( !bPastePossible ) + { + rSet.DisableItem( SID_PASTE ); + } + } + + if (!pOlView->GetViewByWindow(GetActiveWindow())->HasSelection() + || GetObjectShell()->isContentExtractionLocked()) + { + rSet.DisableItem(SID_CUT); + rSet.DisableItem(SID_COPY); + } + + if (pOlView->GetOutliner().IsModified()) + { + GetDoc()->SetChanged(); + } + + // the status has to be set here because of overriding + if( !GetDocSh()->IsModified() ) + { + rSet.DisableItem( SID_SAVEDOC ); + } + + if ( GetDocSh()->IsReadOnly() ) + { + rSet.DisableItem( SID_AUTOSPELL_CHECK ); + } + else + { + if (GetDoc()->GetOnlineSpell()) + { + rSet.Put(SfxBoolItem(SID_AUTOSPELL_CHECK, true)); + } + else + { + rSet.Put(SfxBoolItem(SID_AUTOSPELL_CHECK, false)); + } + } + + // field commands + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_MODIFY_FIELD ) ) + { + const SvxFieldItem* pFldItem = pOutlinerView->GetFieldAtSelection(); + + if( !( pFldItem && (nullptr != dynamic_cast< const SvxDateField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxAuthorField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxExtFileField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxExtTimeField *>( pFldItem->GetField() ) ) ) ) + { + rSet.DisableItem( SID_MODIFY_FIELD ); + } + } + + if (SfxItemState::DEFAULT == rSet.GetItemState(SID_EXPAND_PAGE)) + { + bool bDisable = true; + sal_uInt16 i = 0; + sal_uInt16 nCount = GetDoc()->GetSdPageCount(PageKind::Standard); + pOlView->SetSelectedPages(); + + while (i < nCount && bDisable) + { + SdPage* pPage = GetDoc()->GetSdPage(i, PageKind::Standard); + + if (pPage->IsSelected()) + { + SdrObject* pObj = pPage->GetPresObj(PresObjKind::Outline); + + if (pObj!=nullptr ) + { + if( !pObj->IsEmptyPresObj() ) + { + bDisable = false; + } + else + { + // check if the object is in edit, then if it's temporarily not empty + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( pObj ); + if( pTextObj ) + { + if( pTextObj->CanCreateEditOutlinerParaObject() ) + { + bDisable = false; + } + } + } + } + } + + i++; + } + + if (bDisable) + { + rSet.DisableItem(SID_EXPAND_PAGE); + } + } + + if (SfxItemState::DEFAULT == rSet.GetItemState(SID_SUMMARY_PAGE)) + { + bool bDisable = true; + sal_uInt16 i = 0; + sal_uInt16 nCount = GetDoc()->GetSdPageCount(PageKind::Standard); + pOlView->SetSelectedPages(); + + while (i < nCount && bDisable) + { + SdPage* pPage = GetDoc()->GetSdPage(i, PageKind::Standard); + + if (pPage->IsSelected()) + { + SdrObject* pObj = pPage->GetPresObj(PresObjKind::Title); + + if (pObj && !pObj->IsEmptyPresObj()) + { + bDisable = false; + } + } + + i++; + } + + if (bDisable) + { + rSet.DisableItem(SID_SUMMARY_PAGE); + } + } + + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_THESAURUS ) ) + { + if ( !pOlView->IsTextEdit() ) + { + rSet.DisableItem( SID_THESAURUS ); + } + else + { + LanguageType eLang = GetDoc()->GetLanguage( EE_CHAR_LANGUAGE ); + Reference< XThesaurus > xThesaurus( LinguMgr::GetThesaurus() ); + + if (!xThesaurus.is() || eLang == LANGUAGE_NONE || !xThesaurus->hasLocale( LanguageTag::convertToLocale( eLang))) + rSet.DisableItem( SID_THESAURUS ); + } + } + + // is starting the presentation possible? + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_PRESENTATION ) ) + { + bool bDisable = true; + sal_uInt16 nCount = GetDoc()->GetSdPageCount( PageKind::Standard ); + + for( sal_uInt16 i = 0; i < nCount && bDisable; i++ ) + { + SdPage* pPage = GetDoc()->GetSdPage(i, PageKind::Standard); + + if( !pPage->IsExcluded() ) + bDisable = false; + } + if( bDisable || GetDocSh()->IsPreview()) + { + rSet.DisableItem( SID_PRESENTATION ); + } + } + + FuBullet::GetSlotState( rSet, this, GetViewFrame() ); + +} + +/** + * gets invoked when ScrollBar is used + */ +void OutlineViewShell::VirtHScrollHdl(ScrollBar* pHScroll) +{ + ::tools::Long nThumb = pHScroll->GetThumbPos(); + ::tools::Long nRange = pHScroll->GetRange().Len(); + double fX = static_cast<double>(nThumb) / nRange; + + Window* pWin = mpContentWindow.get(); + OutlinerView* pOutlinerView = pOlView->GetViewByWindow(pWin); + ::tools::Long nViewWidth = pWin->PixelToLogic( + pWin->GetSizePixel()).Width(); + ::tools::Long nTextWidth = pOlView->GetPaperWidth(); + nViewWidth = std::max(nViewWidth, nTextWidth); + ::tools::Long nCurrentPos = pOutlinerView->GetVisArea().Left(); + ::tools::Long nTargetPos = static_cast<::tools::Long>(fX * nViewWidth); + ::tools::Long nDelta = nTargetPos - nCurrentPos; + + pOutlinerView->HideCursor(); + pOutlinerView->Scroll(-nDelta, 0); + pOutlinerView->ShowCursor(false); +} + +void OutlineViewShell::VirtVScrollHdl(ScrollBar* pVScroll) +{ + ::tools::Long nThumb = pVScroll->GetThumbPos(); + ::tools::Long nRange = pVScroll->GetRange().Len(); + double fY = static_cast<double>(nThumb) / nRange; + + Window* pWin = mpContentWindow.get(); + OutlinerView* pOutlinerView = pOlView->GetViewByWindow(pWin); + ::tools::Long nViewHeight = pWin->PixelToLogic( + pWin->GetSizePixel()).Height(); + ::tools::Long nTextHeight = pOlView->GetOutliner().GetTextHeight(); + nViewHeight += nTextHeight; + ::tools::Long nCurrentPos = pOutlinerView->GetVisArea().Top(); + ::tools::Long nTargetPos = static_cast<::tools::Long>(fY * nViewHeight); + ::tools::Long nDelta = nTargetPos - nCurrentPos; + + pOutlinerView->HideCursor(); + pOutlinerView->Scroll(0, -nDelta); + pOutlinerView->ShowCursor(false); +} + +/** + * PrepareClose, gets called when the Shell shall be destroyed. + * Forwards the invocation to the View + */ +bool OutlineViewShell::PrepareClose( bool bUI ) +{ + if( !ViewShell::PrepareClose(bUI) ) + return false; + + if (pOlView) + pOlView->PrepareClose(); + return true; +} + +/** + * Zoom with zoom factor. Inform OutlinerView + */ +void OutlineViewShell::SetZoom(::tools::Long nZoom) +{ + ViewShell::SetZoom(nZoom); + + ::sd::Window* pWindow = mpContentWindow.get(); + if (pWindow) + { + // change OutputArea of OutlinerView + OutlinerView* pOutlinerView = pOlView->GetViewByWindow(pWindow); + ::tools::Rectangle aWin(Point(0,0), pWindow->GetOutputSizePixel()); + aWin = pWindow->PixelToLogic(aWin); + pOutlinerView->SetOutputArea(aWin); + } + + GetViewFrame()->GetBindings().Invalidate( SID_ATTR_ZOOM ); + GetViewFrame()->GetBindings().Invalidate( SID_ATTR_ZOOMSLIDER ); +} + +/** + * Zoom with zoom rectangle. Inform OutlinerView + */ +void OutlineViewShell::SetZoomRect(const ::tools::Rectangle& rZoomRect) +{ + ViewShell::SetZoomRect(rZoomRect); + + ::sd::Window* pWindow = mpContentWindow.get(); + if (pWindow) + { + // change OutputArea of OutlinerView + OutlinerView* pOutlinerView = pOlView->GetViewByWindow(pWindow); + ::tools::Rectangle aWin(Point(0,0), pWindow->GetOutputSizePixel()); + aWin = pWindow->PixelToLogic(aWin); + pOutlinerView->SetOutputArea(aWin); + } + + GetViewFrame()->GetBindings().Invalidate( SID_ATTR_ZOOM ); + GetViewFrame()->GetBindings().Invalidate( SID_ATTR_ZOOMSLIDER ); +} + +/** + * Before saving: Update Model of the Drawing Engine, then forward the + * invocation to the ObjectShell. + */ +void OutlineViewShell::Execute(SfxRequest& rReq) +{ + bool bForwardCall = true; + + switch(rReq.GetSlot()) + { + case SID_SAVEDOC: + case SID_SAVEASDOC: + PrepareClose(); + break; + + case SID_SEARCH_ITEM: + // Forward this request to the common (old) code of the + // document shell. + GetDocSh()->Execute (rReq); + bForwardCall = false; + break; + + case SID_SPELL_DIALOG: + { + SfxViewFrame* pViewFrame = GetViewFrame(); + if (rReq.GetArgs() != nullptr) + pViewFrame->SetChildWindow (SID_SPELL_DIALOG, + static_cast<const SfxBoolItem&>(rReq.GetArgs()-> + Get(SID_SPELL_DIALOG)).GetValue()); + else + pViewFrame->ToggleChildWindow(SID_SPELL_DIALOG); + + pViewFrame->GetBindings().Invalidate(SID_SPELL_DIALOG); + rReq.Done (); + + bForwardCall = false; + } + break; + + default: + SAL_WARN("sd", "OutlineViewShell::Execute(): can not handle slot " << rReq.GetSlot()); + break; + + } + + if (bForwardCall) + static_cast<DrawDocShell*>(GetViewFrame()->GetObjectShell())->ExecuteSlot( rReq ); +} + +/** + * Read FrameViews data and set actual views data + */ +void OutlineViewShell::ReadFrameViewData(FrameView* pView) +{ + ::Outliner& rOutl = pOlView->GetOutliner(); + + rOutl.SetFlatMode( pView->IsNoAttribs() ); + + EEControlBits nCntrl = rOutl.GetControlWord(); + + if ( pView->IsNoColors() ) + rOutl.SetControlWord(nCntrl | EEControlBits::NOCOLORS); + else + rOutl.SetControlWord(nCntrl & ~EEControlBits::NOCOLORS); + + sal_uInt16 nPage = mpFrameView->GetSelectedPage(); + pLastPage = GetDoc()->GetSdPage( nPage, PageKind::Standard ); + pOlView->SetActualPage(pLastPage); +} + +/** + * Write actual views data to FrameView + */ +void OutlineViewShell::WriteFrameViewData() +{ + ::Outliner& rOutl = pOlView->GetOutliner(); + + EEControlBits nCntrl = rOutl.GetControlWord(); + bool bNoColor = false; + if (nCntrl & EEControlBits::NOCOLORS) + bNoColor = true; + mpFrameView->SetNoColors(bNoColor); + mpFrameView->SetNoAttribs( rOutl.IsFlatMode() ); + SdPage* pActualPage = pOlView->GetActualPage(); + DBG_ASSERT(pActualPage, "No current page"); + if( pActualPage ) + mpFrameView->SetSelectedPage((pActualPage->GetPageNum() - 1) / 2); +} + +/** + * Handle SfxRequests for the StatusBar + */ +void OutlineViewShell::ExecStatusBar(SfxRequest&) +{ +} + +void OutlineViewShell::GetStatusBarState(SfxItemSet& rSet) +{ + // Zoom-Item + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_ATTR_ZOOM ) ) + { + sal_uInt16 nZoom = static_cast<sal_uInt16>(GetActiveWindow()->GetZoom()); + + std::unique_ptr<SvxZoomItem> pZoomItem(new SvxZoomItem( SvxZoomType::PERCENT, nZoom )); + + // limit area + SvxZoomEnableFlags nZoomValues = SvxZoomEnableFlags::ALL; + nZoomValues &= ~SvxZoomEnableFlags::OPTIMAL; + nZoomValues &= ~SvxZoomEnableFlags::WHOLEPAGE; + nZoomValues &= ~SvxZoomEnableFlags::PAGEWIDTH; + + pZoomItem->SetValueSet( nZoomValues ); + rSet.Put( std::move(pZoomItem) ); + } + + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_ATTR_ZOOMSLIDER ) ) + { + if (GetDocSh()->IsUIActive() || !GetActiveWindow() ) + { + rSet.DisableItem( SID_ATTR_ZOOMSLIDER ); + } + else + { + sd::Window * pActiveWindow = GetActiveWindow(); + SvxZoomSliderItem aZoomItem( static_cast<sal_uInt16>(pActiveWindow->GetZoom()), static_cast<sal_uInt16>(pActiveWindow->GetMinZoom()), static_cast<sal_uInt16>(pActiveWindow->GetMaxZoom()) ) ; + aZoomItem.AddSnappingPoint(100); + rSet.Put( aZoomItem ); + } + } + + // page view and layout + + sal_uInt16 nPageCount = GetDoc()->GetSdPageCount( PageKind::Standard ); + OUString aPageStr, aLayoutStr; + + ::sd::Window* pWin = GetActiveWindow(); + OutlinerView* pActiveView = pOlView->GetViewByWindow( pWin ); + + std::vector<Paragraph*> aSelList; + pActiveView->CreateSelectionList(aSelList); + + Paragraph *pFirstPara = nullptr; + Paragraph *pLastPara = nullptr; + + if (!aSelList.empty()) + { + pFirstPara = *(aSelList.begin()); + pLastPara = *(aSelList.rbegin()); + } + + if( !::Outliner::HasParaFlag(pFirstPara,ParaFlag::ISPAGE) ) + pFirstPara = pOlView->GetPrevTitle( pFirstPara ); + + if( !::Outliner::HasParaFlag(pLastPara, ParaFlag::ISPAGE) ) + pLastPara = pOlView->GetPrevTitle( pLastPara ); + + // only one page selected? + if( pFirstPara == pLastPara ) + { + // how many pages are we before the selected page? + sal_uLong nPos = 0; + while( pFirstPara ) + { + pFirstPara = pOlView->GetPrevTitle( pFirstPara ); + if( pFirstPara ) + nPos++; + } + + if( nPos >= GetDoc()->GetSdPageCount( PageKind::Standard ) ) + nPos = 0; + + SdrPage* pPage = GetDoc()->GetSdPage( static_cast<sal_uInt16>(nPos), PageKind::Standard ); + + aPageStr = SdResId(STR_SD_PAGE_COUNT); + + aPageStr = aPageStr.replaceFirst("%1", OUString::number(static_cast<sal_Int32>(nPos + 1))); + aPageStr = aPageStr.replaceFirst("%2", OUString::number(nPageCount)); + + aLayoutStr = pPage->GetLayoutName(); + sal_Int32 nIndex = aLayoutStr.indexOf(SD_LT_SEPARATOR); + if (nIndex != -1) + aLayoutStr = aLayoutStr.copy(0, nIndex); + //Now, CurrentPage property change is already sent for DrawView and OutlineView, so it is not necessary to send again here + if(m_StrOldPageName!=aPageStr) + { + GetViewShellBase().GetDrawController().fireSwitchCurrentPage(nPos); + m_StrOldPageName = aPageStr; + } + } + rSet.Put( SfxStringItem( SID_STATUS_PAGE, aPageStr ) ); + rSet.Put( SfxStringItem( SID_STATUS_LAYOUT, aLayoutStr ) ); +} + +void OutlineViewShell::Command( const CommandEvent& rCEvt, ::sd::Window* pWin ) +{ + if ( rCEvt.GetCommand() == CommandEventId::ContextMenu ) + { + GetActiveWindow()->ReleaseMouse(); + + OutlinerView* pOLV = pOlView->GetViewByWindow(GetActiveWindow()); + Point aPos(rCEvt.GetMousePosPixel()); + + if (pOLV && pOLV->IsWrongSpelledWordAtPos(aPos)) + { + // Popup for Online-Spelling now handled by DrawDocShell + Link<SpellCallbackInfo&,void> aLink = LINK(GetDocSh(), DrawDocShell, OnlineSpellCallback); + + pOLV->ExecuteSpellPopup(aPos, aLink); + pOLV->GetEditView().Invalidate(); + } + else + { + GetViewFrame()->GetDispatcher()->ExecutePopup("outline"); + } + } + else + { + ViewShell::Command( rCEvt, pWin ); + + // if necessary communicate the new context to the Preview + Invalidate( SID_PREVIEW_STATE ); + + } +} + +bool OutlineViewShell::KeyInput(const KeyEvent& rKEvt, ::sd::Window* pWin) +{ + bool bReturn = false; + OutlineViewPageChangesGuard aGuard(pOlView.get()); + + if (pWin == nullptr && HasCurrentFunction()) + { + bReturn = GetCurrentFunction()->KeyInput(rKEvt); + } + + // no, forward to base class + else + { + bReturn = ViewShell::KeyInput(rKEvt, pWin); + } + + Invalidate(SID_STYLE_EDIT); + Invalidate(SID_STYLE_NEW); + Invalidate(SID_STYLE_DELETE); + Invalidate(SID_STYLE_HIDE); + Invalidate(SID_STYLE_SHOW); + Invalidate(SID_STYLE_UPDATE_BY_EXAMPLE); + Invalidate(SID_STYLE_NEW_BY_EXAMPLE); + Invalidate(SID_STYLE_WATERCAN); + Invalidate(SID_STYLE_FAMILY5); + + // check and distinguish cursor movements- or input-keys + vcl::KeyCode aKeyGroup( rKEvt.GetKeyCode().GetGroup() ); + if( (aKeyGroup != KEYGROUP_CURSOR && aKeyGroup != KEYGROUP_FKEYS) || + (GetActualPage() != pLastPage) ) + { + Invalidate( SID_PREVIEW_STATE ); + } + + return bReturn; +} + +/** + * Status of Attribute-Items + */ +void OutlineViewShell::GetAttrState( SfxItemSet& rSet ) +{ + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + SfxAllItemSet aAllSet( *rSet.GetPool() ); + + while ( nWhich ) + { + sal_uInt16 nSlotId = SfxItemPool::IsWhich(nWhich) + ? GetPool().GetSlotId(nWhich) + : nWhich; + + switch ( nSlotId ) + { + case SID_STYLE_FAMILY2: + case SID_STYLE_FAMILY3: + { + rSet.DisableItem( nWhich ); + } + break; + + case SID_STYLE_FAMILY5: + { + SfxStyleSheet* pStyleSheet = pOlView->GetViewByWindow(GetActiveWindow())->GetStyleSheet(); + + if( pStyleSheet ) + { + pStyleSheet = static_cast<SdStyleSheet*>(pStyleSheet)->GetPseudoStyleSheet(); + + if (pStyleSheet) + { + SfxTemplateItem aItem( nWhich, pStyleSheet->GetName() ); + aAllSet.Put( aItem, aItem.Which() ); + } + } + + if( !pStyleSheet ) + { + SfxTemplateItem aItem( nWhich, OUString() ); + aAllSet.Put( aItem, aItem.Which() ); + // rSet.DisableItem( nWhich ); + } + } + break; + + case SID_STYLE_EDIT: + { + std::unique_ptr<SfxUInt16Item> pFamilyItem; + GetViewFrame()->GetBindings().QueryState(SID_STYLE_FAMILY, pFamilyItem); + if (pFamilyItem && static_cast<SfxStyleFamily>(pFamilyItem->GetValue()) == SfxStyleFamily::Pseudo) + { + SfxItemSetFixed<SID_STATUS_LAYOUT, SID_STATUS_LAYOUT> aSet(*rSet.GetPool()); + GetStatusBarState(aSet); + OUString aRealStyle = static_cast<const SfxStringItem&>(aSet.Get(SID_STATUS_LAYOUT)).GetValue(); + if (aRealStyle.isEmpty()) + { + // no unique layout name found + rSet.DisableItem(nWhich); + } + } + } + break; + + case SID_STYLE_UPDATE_BY_EXAMPLE: + { + ::sd::Window* pActWin = GetActiveWindow(); + OutlinerView* pOV = pOlView->GetViewByWindow(pActWin); + ESelection aESel(pOV->GetSelection()); + + if (aESel.nStartPara != aESel.nEndPara || + aESel.nStartPos != aESel.nEndPos) + // spanned selection, i.e. StyleSheet and/or + // attribution not necessarily unique + rSet.DisableItem(nWhich); + } + break; + + case SID_STYLE_NEW: + case SID_STYLE_DELETE: + case SID_STYLE_HIDE: + case SID_STYLE_SHOW: + case SID_STYLE_NEW_BY_EXAMPLE: + case SID_STYLE_WATERCAN: + { + rSet.DisableItem(nWhich); + } + break; + } + + nWhich = aIter.NextWhich(); + } + + rSet.Put( aAllSet, false ); +} + +void OutlineViewShell::MouseButtonUp(const MouseEvent& rMEvt, ::sd::Window* pWin) +{ + // first the base classes + ViewShell::MouseButtonUp(rMEvt, pWin); + + Invalidate(SID_STYLE_EDIT); + Invalidate(SID_STYLE_NEW); + Invalidate(SID_STYLE_DELETE); + Invalidate(SID_STYLE_HIDE); + Invalidate(SID_STYLE_SHOW); + Invalidate(SID_STYLE_UPDATE_BY_EXAMPLE); + Invalidate(SID_STYLE_NEW_BY_EXAMPLE); + Invalidate(SID_STYLE_WATERCAN); + Invalidate(SID_STYLE_FAMILY5); + + // if necessary communicate the new context to the Preview + if( GetActualPage() != pLastPage ) + Invalidate( SID_PREVIEW_STATE ); +} + +SdPage* OutlineViewShell::getCurrentPage() const +{ + // since there are no master pages in outline view, we can + // for now use the GetActualPage method + return const_cast<OutlineViewShell*>(this)->GetActualPage(); +} + +/** + * Returns the first selected page. + * If nothing is selected, the first page is returned. + */ +SdPage* OutlineViewShell::GetActualPage() +{ + return pOlView->GetActualPage(); +} + +void OutlineViewShell::UpdatePreview( SdPage* pPage ) +{ + const bool bNewPage = pPage != pLastPage; + pLastPage = pPage; + if (bNewPage) + { + OutlineViewPageChangesGuard aGuard(pOlView.get()); + SetCurrentPage(pPage); + } +} + +void OutlineViewShell::UpdateTitleObject( SdPage* pPage, Paragraph const * pPara ) +{ + DBG_ASSERT( pPage, "sd::OutlineViewShell::UpdateTitleObject(), pPage == 0?" ); + DBG_ASSERT( pPara, "sd::OutlineViewShell::UpdateTitleObject(), pPara == 0?" ); + + if( !pPage || !pPara ) + return; + + ::Outliner& rOutliner = pOlView->GetOutliner(); + SdrTextObj* pTO = OutlineView::GetTitleTextObject( pPage ); + + OUString aTest = rOutliner.GetText(pPara); + bool bText = !aTest.isEmpty(); + + if( bText ) + { + bool bNewObject = false; + // create a title object if we don't have one but have text + if( !pTO ) + { + DBG_ASSERT( pOlView->isRecordingUndo(), "sd::OutlineViewShell::UpdateTitleObject(), no undo for model change!?" ); + pTO = OutlineView::CreateTitleTextObject(pPage); + bNewObject = true; + } + + // if we have a title object and a text, set the text + std::optional<OutlinerParaObject> pOPO; + if (pTO) + pOPO = rOutliner.CreateParaObject(rOutliner.GetAbsPos(pPara), 1); + if (pOPO) + { + pOPO->SetOutlinerMode( OutlinerMode::TitleObject ); + assert(pTO); + pOPO->SetVertical( pTO->IsVerticalWriting() ); + if( pTO->GetOutlinerParaObject() && (pOPO->GetTextObject() == pTO->GetOutlinerParaObject()->GetTextObject()) ) + { + // do nothing, same text already set + } + else + { + DBG_ASSERT( pOlView->isRecordingUndo(), "sd::OutlineViewShell::UpdateTitleObject(), no undo for model change!?" ); + if( !bNewObject && pOlView->isRecordingUndo() ) + pOlView->AddUndo(GetDoc()->GetSdrUndoFactory().CreateUndoObjectSetText(*pTO,0)); + + pTO->SetOutlinerParaObject( std::move(pOPO) ); + pTO->SetEmptyPresObj( false ); + pTO->ActionChanged(); + } + } + } + else if( pTO ) + { + // no text but object available? + // outline object available, but we have no text + if(pPage->IsPresObj(pTO)) + { + // if it is not already empty + if( !pTO->IsEmptyPresObj() ) + { + DBG_ASSERT( pOlView->isRecordingUndo(), "sd::OutlineViewShell::UpdateTitleObject(), no undo for model change!?" ); + + // make it empty + if( pOlView->isRecordingUndo() ) + pOlView->AddUndo(GetDoc()->GetSdrUndoFactory().CreateUndoObjectSetText(*pTO,0)); + pPage->RestoreDefaultText( pTO ); + pTO->SetEmptyPresObj(true); + pTO->ActionChanged(); + } + } + else + { + DBG_ASSERT( pOlView->isRecordingUndo(), "sd::OutlineViewShell::UpdateTitleObject(), no undo for model change!?" ); + // outline object is not part of the layout, delete it + if( pOlView->isRecordingUndo() ) + pOlView->AddUndo(GetDoc()->GetSdrUndoFactory().CreateUndoRemoveObject(*pTO)); + pPage->RemoveObject(pTO->GetOrdNum()); + } + } +} + +void OutlineViewShell::UpdateOutlineObject( SdPage* pPage, Paragraph* pPara ) +{ + DBG_ASSERT( pPage, "sd::OutlineViewShell::UpdateOutlineObject(), pPage == 0?" ); + DBG_ASSERT( pPara, "sd::OutlineViewShell::UpdateOutlineObject(), pPara == 0?" ); + + if( !pPage || !pPara ) + return; + + ::Outliner& rOutliner = pOlView->GetOutliner(); + std::optional<OutlinerParaObject> pOPO; + SdrTextObj* pTO = nullptr; + + OutlinerMode eOutlinerMode = OutlinerMode::TitleObject; + pTO = static_cast<SdrTextObj*>(pPage->GetPresObj( PresObjKind::Text )); + if( !pTO ) + { + eOutlinerMode = OutlinerMode::OutlineObject; + pTO = OutlineView::GetOutlineTextObject( pPage ); + } + + // how many paragraphs in the outline? + sal_Int32 nTitlePara = rOutliner.GetAbsPos( pPara ); + sal_Int32 nPara = nTitlePara + 1; + sal_Int32 nParasInLayout = 0; + pPara = rOutliner.GetParagraph( nPara ); + while( pPara && !::Outliner::HasParaFlag(pPara, ParaFlag::ISPAGE) ) + { + nParasInLayout++; + pPara = rOutliner.GetParagraph( ++nPara ); + } + if( nParasInLayout ) + { + // create an OutlinerParaObject + pOPO = rOutliner.CreateParaObject( nTitlePara + 1, nParasInLayout ); + } + + if( pOPO ) + { + DBG_ASSERT( pOlView->isRecordingUndo(), "sd::OutlineViewShell::UpdateOutlineObject(), no undo for model change!?" ); + bool bNewObject = false; + + // do we need an outline text object? + if( !pTO ) + { + pTO = OutlineView::CreateOutlineTextObject( pPage ); + bNewObject = true; + } + + // page object, outline text in Outliner: + // apply text + if( pTO ) + { + pOPO->SetVertical( pTO->IsVerticalWriting() ); + pOPO->SetOutlinerMode( eOutlinerMode ); + if( pTO->GetOutlinerParaObject() && (pOPO->GetTextObject() == pTO->GetOutlinerParaObject()->GetTextObject()) ) + { + // do nothing, same text already set + } + else + { + if( !bNewObject && pOlView->isRecordingUndo() ) + pOlView->AddUndo(GetDoc()->GetSdrUndoFactory().CreateUndoObjectSetText(*pTO,0)); + + pTO->SetOutlinerParaObject( std::move(pOPO) ); + pTO->SetEmptyPresObj( false ); + pTO->ActionChanged(); + } + } + } + else if( pTO ) + { + // page object but no outline text: + // if the object is in the outline of the page -> default text + + // otherwise delete object + if( pPage->IsPresObj(pTO) ) + { + if( !pTO->IsEmptyPresObj() ) + { + DBG_ASSERT( pOlView->isRecordingUndo(), "sd::OutlineViewShell::UpdateOutlineObject(), no undo for model change!?" ); + + // delete old OutlinerParaObject, too + if( pOlView->isRecordingUndo() ) + pOlView->AddUndo(GetDoc()->GetSdrUndoFactory().CreateUndoObjectSetText(*pTO,0)); + pPage->RestoreDefaultText( pTO ); + pTO->SetEmptyPresObj(true); + pTO->ActionChanged(); + } + } + else + { + DBG_ASSERT( pOlView->isRecordingUndo(), "sd::OutlineViewShell::UpdateOutlineObject(), no undo for model change!?" ); + if( pOlView->isRecordingUndo() ) + pOlView->AddUndo(GetDoc()->GetSdrUndoFactory().CreateUndoRemoveObject(*pTO)); + pPage->RemoveObject(pTO->GetOrdNum()); + } + } +} + +/** + * Fill Outliner from Stream + */ +ErrCode OutlineViewShell::ReadRtf(SvStream& rInput) +{ + ErrCode bRet = ERRCODE_NONE; + + ::Outliner& rOutl = pOlView->GetOutliner(); + + OutlineViewPageChangesGuard aGuard( pOlView.get() ); + OutlineViewModelChangeGuard aGuard2( *pOlView ); + + bRet = rOutl.Read( rInput, OUString(), EETextFormat::Rtf, GetDocSh()->GetHeaderAttributes() ); + + SdPage* pPage = GetDoc()->GetSdPage( GetDoc()->GetSdPageCount(PageKind::Standard) - 1, PageKind::Standard ); + SfxStyleSheet* pTitleSheet = pPage->GetStyleSheetForPresObj( PresObjKind::Title ); + SfxStyleSheet* pOutlSheet = pPage->GetStyleSheetForPresObj( PresObjKind::Outline ); + + sal_Int32 nParaCount = rOutl.GetParagraphCount(); + if ( nParaCount > 0 ) + { + for ( sal_Int32 nPara = 0; nPara < nParaCount; nPara++ ) + { + pOlView->UpdateParagraph( nPara ); + + sal_Int16 nDepth = rOutl.GetDepth( nPara ); + + if( (nDepth == 0) || !nPara ) + { + Paragraph* pPara = rOutl.GetParagraph( nPara ); + rOutl.SetDepth(pPara, -1); + rOutl.SetParaFlag(pPara, ParaFlag::ISPAGE); + + rOutl.SetStyleSheet( nPara, pTitleSheet ); + + if( nPara ) // first slide already exists + pOlView->InsertSlideForParagraph( pPara ); + } + else + { + rOutl.SetDepth( rOutl.GetParagraph( nPara ), nDepth - 1 ); + OUString aStyleSheetName = pOutlSheet->GetName(); + if (!aStyleSheetName.isEmpty()) + aStyleSheetName = aStyleSheetName.copy(0, aStyleSheetName.getLength() - 1); + aStyleSheetName += OUString::number( nDepth ); + SfxStyleSheetBasePool* pStylePool = GetDoc()->GetStyleSheetPool(); + SfxStyleSheet* pStyle = static_cast<SfxStyleSheet*>( pStylePool->Find( aStyleSheetName, pOutlSheet->GetFamily() ) ); + DBG_ASSERT( pStyle, "AutoStyleSheetName - Style not found!" ); + if ( pStyle ) + rOutl.SetStyleSheet( nPara, pStyle ); + } + } + } + + rOutl.GetUndoManager().Clear(); + + return bRet; +} + +void OutlineViewShell::WriteUserDataSequence ( css::uno::Sequence < css::beans::PropertyValue >& rSequence ) +{ + WriteFrameViewData(); + + ViewShell::WriteUserDataSequence( rSequence ); +} + +void OutlineViewShell::ReadUserDataSequence ( const css::uno::Sequence < css::beans::PropertyValue >& rSequence ) +{ + WriteFrameViewData(); + + ViewShell::ReadUserDataSequence( rSequence ); + + ReadFrameViewData( mpFrameView ); +} + +void OutlineViewShell::VisAreaChanged(const ::tools::Rectangle& rRect) +{ + ViewShell::VisAreaChanged( rRect ); + + GetViewShellBase().GetDrawController().FireVisAreaChanged(rRect); +} + +/** If there is a valid controller then create a new instance of + <type>AccessibleDrawDocumentView</type>. Otherwise return an empty + reference. +*/ +css::uno::Reference<css::accessibility::XAccessible> + OutlineViewShell::CreateAccessibleDocumentView (::sd::Window* pWindow) +{ + OSL_ASSERT (GetViewShell()!=nullptr); + if (GetViewShell()->GetController() != nullptr) + { + rtl::Reference<::accessibility::AccessibleOutlineView> pDocumentView = + new ::accessibility::AccessibleOutlineView ( + pWindow, + this, + GetViewShell()->GetController(), + pWindow->GetAccessibleParentWindow()->GetAccessible()); + pDocumentView->Init(); + return pDocumentView; + } + + SAL_WARN("sd", "OutlineViewShell::CreateAccessibleDocumentView: no controller"); + return css::uno::Reference< css::accessibility::XAccessible >(); +} + +void OutlineViewShell::GetState (SfxItemSet& rSet) +{ + // Iterate over all requested items in the set. + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + while (nWhich) + { + switch (nWhich) + { + case SID_SEARCH_ITEM: + case SID_SEARCH_OPTIONS: + // Call common (old) implementation in the document shell. + GetDocSh()->GetState (rSet); + break; + default: + SAL_WARN("sd", "OutlineViewShell::GetState(): can not handle which id " << nWhich); + break; + } + nWhich = aIter.NextWhich(); + } +} + +void OutlineViewShell::SetCurrentPage (SdPage* pPage) +{ + // Adapt the selection of the model. + for (sal_uInt16 i=0; i<GetDoc()->GetSdPageCount(PageKind::Standard); i++) + GetDoc()->SetSelected( + GetDoc()->GetSdPage(i, PageKind::Standard), + false); + GetDoc()->SetSelected (pPage, true); + + DrawController& rController(GetViewShellBase().GetDrawController()); + rController.FireSelectionChangeListener(); + rController.FireSwitchCurrentPage (pPage); + + pOlView->SetActualPage(pPage); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/outlview.cxx b/sd/source/ui/view/outlview.cxx new file mode 100644 index 000000000..c3b7a57ca --- /dev/null +++ b/sd/source/ui/view/outlview.cxx @@ -0,0 +1,1720 @@ +/* -*- 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 <OutlineView.hxx> +#include <sfx2/progress.hxx> +#include <vcl/commandinfoprovider.hxx> +#include <vcl/svapp.hxx> +#include <svx/svxids.hrc> +#include <editeng/outliner.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/editstat.hxx> +#include <editeng/lrspitem.hxx> +#include <svx/svdotext.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/style.hxx> +#include <svx/svdundo.hxx> +#include <editeng/numitem.hxx> +#include <editeng/outlobj.hxx> +#include <editeng/editeng.hxx> +#include <xmloff/autolayout.hxx> +#include <tools/debug.hxx> + +#include <editeng/editobj.hxx> +#include <editeng/editund2.hxx> + +#include <editeng/editview.hxx> + +#include <com/sun/star/frame/XFrame.hpp> + +#include <DrawDocShell.hxx> +#include <drawdoc.hxx> +#include <Window.hxx> +#include <sdpage.hxx> +#include <pres.hxx> +#include <OutlineViewShell.hxx> +#include <app.hrc> +#include <strings.hrc> +#include <sdmod.hxx> +#include <sdresid.hxx> +#include <Outliner.hxx> +#include <EventMultiplexer.hxx> +#include <ViewShellBase.hxx> +#include <ViewShellManager.hxx> +#include <undo/undomanager.hxx> +#include <stlsheet.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::frame; + +namespace sd { + +// a progress bar gets displayed when more than +// PROCESS_WITH_PROGRESS_THRESHOLD pages are concerned +#define PROCESS_WITH_PROGRESS_THRESHOLD 5 + +OutlineView::OutlineView( DrawDocShell& rDocSh, vcl::Window* pWindow, OutlineViewShell& rOutlineViewShell) +: ::sd::View(*rDocSh.GetDoc(), pWindow->GetOutDev(), &rOutlineViewShell) +, mrOutlineViewShell(rOutlineViewShell) +, mrOutliner(*mrDoc.GetOutliner()) +, mnPagesToProcess(0) +, mnPagesProcessed(0) +, mbFirstPaint(true) +, maDocColor( COL_WHITE ) +, maLRSpaceItem( 0, 0, 2000, 0, EE_PARA_OUTLLRSPACE ) +{ + bool bInitOutliner = false; + + if (mrOutliner.GetViewCount() == 0) + { + // initialize Outliner: set Reference Device + bInitOutliner = true; + mrOutliner.Init( OutlinerMode::OutlineView ); + mrOutliner.SetRefDevice( SD_MOD()->GetVirtualRefDevice() ); + //viewsize without the width of the image and number in front + mnPaperWidth = (mrOutlineViewShell.GetActiveWindow()->GetViewSize().Width() - 4000); + mrOutliner.SetPaperSize(Size(mnPaperWidth, 400000000)); + } + else + { + // width: DIN A4, two margins at 1 cm each + mnPaperWidth = 19000; + } + + mpOutlinerViews[0].reset( new OutlinerView(&mrOutliner, pWindow) ); + mpOutlinerViews[0]->SetOutputArea(::tools::Rectangle()); + mrOutliner.SetUpdateLayout(false); + mrOutliner.InsertView(mpOutlinerViews[0].get(), EE_APPEND); + + onUpdateStyleSettings( true ); + + if (bInitOutliner) + { + // fill Outliner with contents + FillOutliner(); + } + + Link<tools::EventMultiplexerEvent&,void> aLink( LINK(this,OutlineView,EventMultiplexerListener) ); + mrOutlineViewShell.GetViewShellBase().GetEventMultiplexer()->AddEventListener(aLink); + + Reference<XFrame> xFrame = mrOutlineViewShell.GetViewShellBase().GetFrame()->GetFrame().GetFrameInterface(); + maSlideImage = vcl::CommandInfoProvider::GetImageForCommand(".uno:ShowSlide", xFrame, vcl::ImageType::Size26); + + // Tell undo manager of the document about the undo manager of the + // outliner, so that the former can synchronize with the later. + sd::UndoManager* pDocUndoMgr = dynamic_cast<sd::UndoManager*>(mpDocSh->GetUndoManager()); + if (pDocUndoMgr != nullptr) + pDocUndoMgr->SetLinkedUndoManager(&mrOutliner.GetUndoManager()); +} + +/** + * Destructor, restore Links, clear Outliner + */ +OutlineView::~OutlineView() +{ + DBG_ASSERT(maDragAndDropModelGuard == nullptr, + "sd::OutlineView::~OutlineView(), prior drag operation not finished correctly!"); + + Link<tools::EventMultiplexerEvent&,void> aLink( LINK(this,OutlineView,EventMultiplexerListener) ); + mrOutlineViewShell.GetViewShellBase().GetEventMultiplexer()->RemoveEventListener( aLink ); + DisconnectFromApplication(); + + mpProgress.reset(); + + // unregister OutlinerViews and destroy them + for (auto & rpView : mpOutlinerViews) + { + if (rpView) + { + mrOutliner.RemoveView( rpView.get() ); + rpView.reset(); + } + } + + if (mrOutliner.GetViewCount() == 0) + { + // uninitialize Outliner: enable color display + ResetLinks(); + EEControlBits nCntrl = mrOutliner.GetControlWord(); + mrOutliner.SetUpdateLayout(false); // otherwise there will be drawn on SetControlWord + mrOutliner.SetControlWord(nCntrl & ~EEControlBits::NOCOLORS); + SvtAccessibilityOptions aOptions; + mrOutliner.ForceAutoColor( aOptions.GetIsAutomaticFontColor() ); + mrOutliner.Clear(); + } +} + +void OutlineView::ConnectToApplication() +{ + // When the mode is switched to outline the main view shell grabs focus. + // This is done for getting cut/copy/paste commands on slides in the left + // pane (slide sorter view shell) to work properly. + SfxShell* pTopViewShell = mrOutlineViewShell.GetViewShellBase().GetViewShellManager()->GetTopViewShell(); + if (pTopViewShell && pTopViewShell == &mrOutlineViewShell) + { + mrOutlineViewShell.GetActiveWindow()->GrabFocus(); + } + + Application::AddEventListener(LINK(this, OutlineView, AppEventListenerHdl)); +} + +void OutlineView::DisconnectFromApplication() +{ + Application::RemoveEventListener(LINK(this, OutlineView, AppEventListenerHdl)); +} + +void OutlineView::Paint(const ::tools::Rectangle& rRect, ::sd::Window const * pWin) +{ + OutlinerView* pOlView = GetViewByWindow(pWin); + + if (pOlView) + { + pOlView->HideCursor(); + pOlView->Paint(rRect); + + pOlView->ShowCursor(mbFirstPaint); + + mbFirstPaint = false; + } +} + +void OutlineView::AddWindowToPaintView(OutputDevice* pWin, vcl::Window* pWindow) +{ + bool bAdded = false; + bool bValidArea = false; + ::tools::Rectangle aOutputArea; + const Color aWhiteColor( COL_WHITE ); + sal_uInt16 nView = 0; + + while (nView < MAX_OUTLINERVIEWS && !bAdded) + { + if (mpOutlinerViews[nView] == nullptr) + { + mpOutlinerViews[nView].reset( new OutlinerView(&mrOutliner, dynamic_cast< ::sd::Window* >(pWin->GetOwnerWindow())) ); + mpOutlinerViews[nView]->SetBackgroundColor( aWhiteColor ); + mrOutliner.InsertView(mpOutlinerViews[nView].get(), EE_APPEND); + bAdded = true; + + if (bValidArea) + { + mpOutlinerViews[nView]->SetOutputArea(aOutputArea); + } + } + else if (!bValidArea) + { + aOutputArea = mpOutlinerViews[nView]->GetOutputArea(); + bValidArea = true; + } + + nView++; + } + + // white background in Outliner + pWin->SetBackground( Wallpaper( aWhiteColor ) ); + + ::sd::View::AddWindowToPaintView(pWin, pWindow); +} + +void OutlineView::DeleteWindowFromPaintView(OutputDevice* pWin) +{ + bool bRemoved = false; + sal_uInt16 nView = 0; + vcl::Window* pWindow; + + while (nView < MAX_OUTLINERVIEWS && !bRemoved) + { + if (mpOutlinerViews[nView] != nullptr) + { + pWindow = mpOutlinerViews[nView]->GetWindow(); + + if (pWindow->GetOutDev() == pWin) + { + mrOutliner.RemoveView( mpOutlinerViews[nView].get() ); + mpOutlinerViews[nView].reset(); + bRemoved = true; + } + } + + nView++; + } + + ::sd::View::DeleteWindowFromPaintView(pWin); +} + +/** + * Return a pointer to the OutlinerView corresponding to the window + */ +OutlinerView* OutlineView::GetViewByWindow (vcl::Window const * pWin) const +{ + OutlinerView* pOlView = nullptr; + for (std::unique_ptr<OutlinerView> const & pView : mpOutlinerViews) + { + if (pView != nullptr) + { + if ( pWin == pView->GetWindow() ) + { + pOlView = pView.get(); + } + } + } + return pOlView; +} + +/** + * Return the title before a random paragraph + */ +Paragraph* OutlineView::GetPrevTitle(const Paragraph* pPara) +{ + sal_Int32 nPos = mrOutliner.GetAbsPos(pPara); + + if (nPos > 0) + { + while(nPos) + { + pPara = mrOutliner.GetParagraph(--nPos); + if( ::Outliner::HasParaFlag(pPara, ParaFlag::ISPAGE) ) + { + return const_cast< Paragraph* >( pPara ); + } + } + + } + return nullptr; +} + +/** + * Return the title after a random paragraph + */ +Paragraph* OutlineView::GetNextTitle(const Paragraph* pPara) +{ + Paragraph* pResult = const_cast< Paragraph* >( pPara ); + + sal_Int32 nPos = mrOutliner.GetAbsPos(pResult); + + do + { + pResult = mrOutliner.GetParagraph(++nPos); + if( pResult && ::Outliner::HasParaFlag(pResult, ParaFlag::ISPAGE) ) + return pResult; + } + while( pResult ); + + return nullptr; +} + +/** + * Handler for inserting pages (paragraphs) + */ +IMPL_LINK( OutlineView, ParagraphInsertedHdl, Outliner::ParagraphHdlParam, aParam, void ) +{ + // we get calls to this handler during binary insert of drag and drop contents but + // we ignore it here and handle it later in OnEndPasteOrDrop() + if (maDragAndDropModelGuard != nullptr) + return; + + OutlineViewPageChangesGuard aGuard(this); + + sal_Int32 nAbsPos = mrOutliner.GetAbsPos( aParam.pPara ); + + UpdateParagraph( nAbsPos ); + + if( (nAbsPos == 0) || + ::Outliner::HasParaFlag(aParam.pPara, ParaFlag::ISPAGE) || + ::Outliner::HasParaFlag(mrOutliner.GetParagraph( nAbsPos-1 ), ParaFlag::ISPAGE) ) + { + InsertSlideForParagraph( aParam.pPara ); + } +} + +/** creates and inserts an empty slide for the given paragraph */ +SdPage* OutlineView::InsertSlideForParagraph( Paragraph* pPara ) +{ + DBG_ASSERT( isRecordingUndo(), "sd::OutlineView::InsertSlideForParagraph(), model change without undo?!" ); + + OutlineViewPageChangesGuard aGuard(this); + + mrOutliner.SetParaFlag( pPara, ParaFlag::ISPAGE ); + // how many titles are there before the new title paragraph? + sal_uLong nExample = 0; // position of the "example" page + sal_uLong nTarget = 0; // position of insertion + while(pPara) + { + pPara = GetPrevTitle(pPara); + if (pPara) + nTarget++; + } + + // if a new paragraph is created via RETURN before the first paragraph, the + // Outliner reports the old paragraph (which was moved down) as a new + // paragraph + if (nTarget == 1) + { + OUString aTest = mrOutliner.GetText(mrOutliner.GetParagraph(0)); + if (aTest.isEmpty()) + { + nTarget = 0; + } + } + + // the "example" page is the previous page - if it is available + if (nTarget > 0) + { + nExample = nTarget - 1; + + sal_uInt16 nPageCount = mrDoc.GetSdPageCount( PageKind::Standard ); + if( nExample >= nPageCount ) + nExample = nPageCount - 1; + } + + /********************************************************************** + * All the time, a standard page is created before a notes page. + * It is ensured that after each standard page the corresponding notes page + * follows. A handout page is exactly one handout page. + **********************************************************************/ + + // this page is exemplary + SdPage* pExample = mrDoc.GetSdPage(static_cast<sal_uInt16>(nExample), PageKind::Standard); + rtl::Reference<SdPage> pPage = mrDoc.AllocSdPage(false); + + pPage->SetLayoutName(pExample->GetLayoutName()); + + // insert (page) + mrDoc.InsertPage(pPage.get(), static_cast<sal_uInt16>(nTarget) * 2 + 1); + if( isRecordingUndo() ) + AddUndo(mrDoc.GetSdrUndoFactory().CreateUndoNewPage(*pPage)); + + // assign a master page to the standard page + pPage->TRG_SetMasterPage(pExample->TRG_GetMasterPage()); + + // set page size + pPage->SetSize(pExample->GetSize()); + pPage->SetBorder( pExample->GetLeftBorder(), + pExample->GetUpperBorder(), + pExample->GetRightBorder(), + pExample->GetLowerBorder() ); + + // create new presentation objects (after <Title> or <Title with subtitle> + // follows <Title with outline>, otherwise apply the layout of the previous + // page + AutoLayout eAutoLayout = pExample->GetAutoLayout(); + if (eAutoLayout == AUTOLAYOUT_TITLE || + eAutoLayout == AUTOLAYOUT_TITLE_ONLY) + { + pPage->SetAutoLayout(AUTOLAYOUT_TITLE_CONTENT, true); + } + else + { + pPage->SetAutoLayout(pExample->GetAutoLayout(), true); + } + + /********************************************************************** + |* now the notes page + \*********************************************************************/ + pExample = mrDoc.GetSdPage(static_cast<sal_uInt16>(nExample), PageKind::Notes); + rtl::Reference<SdPage> pNotesPage = mrDoc.AllocSdPage(false); + + pNotesPage->SetLayoutName(pExample->GetLayoutName()); + + pNotesPage->SetPageKind(PageKind::Notes); + + // insert (notes page) + mrDoc.InsertPage(pNotesPage.get(), static_cast<sal_uInt16>(nTarget) * 2 + 2); + if( isRecordingUndo() ) + AddUndo(mrDoc.GetSdrUndoFactory().CreateUndoNewPage(*pNotesPage)); + + // assign a master page to the notes page + pNotesPage->TRG_SetMasterPage(pExample->TRG_GetMasterPage()); + + // set page size, there must be already one page available + pNotesPage->SetSize(pExample->GetSize()); + pNotesPage->SetBorder( pExample->GetLeftBorder(), + pExample->GetUpperBorder(), + pExample->GetRightBorder(), + pExample->GetLowerBorder() ); + + // create presentation objects + pNotesPage->SetAutoLayout(pExample->GetAutoLayout(), true); + + mrOutliner.UpdateFields(); + + return pPage.get(); +} + +/** + * Handler for deleting pages (paragraphs) + */ +IMPL_LINK( OutlineView, ParagraphRemovingHdl, ::Outliner::ParagraphHdlParam, aParam, void ) +{ + DBG_ASSERT( isRecordingUndo(), "sd::OutlineView::ParagraphRemovingHdl(), model change without undo?!" ); + + OutlineViewPageChangesGuard aGuard(this); + + Paragraph* pPara = aParam.pPara; + if( !::Outliner::HasParaFlag( pPara, ParaFlag::ISPAGE ) ) + return; + + // how many titles are in front of the title paragraph in question? + sal_uLong nPos = 0; + while(pPara) + { + pPara = GetPrevTitle(pPara); + if (pPara) nPos++; + } + + // delete page and notes page + sal_uInt16 nAbsPos = static_cast<sal_uInt16>(nPos) * 2 + 1; + SdrPage* pPage = mrDoc.GetPage(nAbsPos); + if( isRecordingUndo() ) + AddUndo(mrDoc.GetSdrUndoFactory().CreateUndoDeletePage(*pPage)); + mrDoc.RemovePage(nAbsPos); + + nAbsPos = static_cast<sal_uInt16>(nPos) * 2 + 1; + pPage = mrDoc.GetPage(nAbsPos); + if( isRecordingUndo() ) + AddUndo(mrDoc.GetSdrUndoFactory().CreateUndoDeletePage(*pPage)); + mrDoc.RemovePage(nAbsPos); + + // progress display if necessary + if (mnPagesToProcess) + { + mnPagesProcessed++; + + if(mpProgress) + mpProgress->SetState(mnPagesProcessed); + + if (mnPagesProcessed == mnPagesToProcess) + { + mpProgress.reset(); + mnPagesToProcess = 0; + mnPagesProcessed = 0; + } + } + aParam.pOutliner->UpdateFields(); +} + +/** + * Handler for changing the indentation depth of paragraphs (requires inserting + * or deleting of pages in some cases) + */ +IMPL_LINK( OutlineView, DepthChangedHdl, ::Outliner::DepthChangeHdlParam, aParam, void ) +{ + DBG_ASSERT( isRecordingUndo(), "sd::OutlineView::DepthChangedHdl(), no undo for model change?!" ); + + OutlineViewPageChangesGuard aGuard(this); + + Paragraph* pPara = aParam.pPara; + ::Outliner* pOutliner = aParam.pOutliner; + if( ::Outliner::HasParaFlag( pPara, ParaFlag::ISPAGE ) && ((aParam.nPrevFlags & ParaFlag::ISPAGE) == ParaFlag::NONE) ) + { + // the current paragraph is transformed into a slide + + mrOutliner.SetDepth( pPara, -1 ); + + // are multiple level 1 paragraphs being brought to level 0 and we + // should start a progress view or a timer and didn't already? + if (mnPagesToProcess == 0) + { + Window* pActWin = mrOutlineViewShell.GetActiveWindow(); + OutlinerView* pOlView = GetViewByWindow(pActWin); + + std::vector<Paragraph*> aSelList; + pOlView->CreateSelectionList(aSelList); + + mnPagesToProcess = std::count_if(aSelList.begin(), aSelList.end(), + [&pOutliner](const Paragraph *pParagraph) { + return !Outliner::HasParaFlag(pParagraph, ParaFlag::ISPAGE) && + (pOutliner->GetDepth(pOutliner->GetAbsPos(pParagraph)) <= 0); + }); + + mnPagesToProcess++; // the paragraph being in level 0 already + // should be included + mnPagesProcessed = 0; + + if (mnPagesToProcess > PROCESS_WITH_PROGRESS_THRESHOLD) + { + mpProgress.reset( new SfxProgress( GetDocSh(), SdResId(STR_CREATE_PAGES), mnPagesToProcess ) ); + } + else + { + mpDocSh->SetWaitCursor( true ); + } + } + + ParagraphInsertedHdl( { aParam.pOutliner, aParam.pPara } ); + + mnPagesProcessed++; + + // should there be a progress display? + if (mnPagesToProcess > PROCESS_WITH_PROGRESS_THRESHOLD) + { + if (mpProgress) + mpProgress->SetState(mnPagesProcessed); + } + + // was this the last page? + if (mnPagesProcessed == mnPagesToProcess) + { + if (mnPagesToProcess > PROCESS_WITH_PROGRESS_THRESHOLD && mpProgress) + { + mpProgress.reset(); + } + else + mpDocSh->SetWaitCursor( false ); + + mnPagesToProcess = 0; + mnPagesProcessed = 0; + } + pOutliner->UpdateFields(); + } + else if( !::Outliner::HasParaFlag( pPara, ParaFlag::ISPAGE ) && ((aParam.nPrevFlags & ParaFlag::ISPAGE) != ParaFlag::NONE) ) + { + // the paragraph was a page but now becomes a normal paragraph + + // how many titles are before the title paragraph in question? + sal_uLong nPos = 0; + Paragraph* pParagraph = pPara; + while(pParagraph) + { + pParagraph = GetPrevTitle(pParagraph); + if (pParagraph) + nPos++; + } + // delete page and notes page + + sal_uInt16 nAbsPos = static_cast<sal_uInt16>(nPos) * 2 + 1; + SdrPage* pPage = mrDoc.GetPage(nAbsPos); + if( isRecordingUndo() ) + AddUndo(mrDoc.GetSdrUndoFactory().CreateUndoDeletePage(*pPage)); + mrDoc.RemovePage(nAbsPos); + + nAbsPos = static_cast<sal_uInt16>(nPos) * 2 + 1; + pPage = mrDoc.GetPage(nAbsPos); + if( isRecordingUndo() ) + AddUndo(mrDoc.GetSdrUndoFactory().CreateUndoDeletePage(*pPage)); + mrDoc.RemovePage(nAbsPos); + + pPage = GetPageForParagraph( pPara ); + + mrOutliner.SetDepth( pPara, (pPage && (static_cast<SdPage*>(pPage)->GetAutoLayout() == AUTOLAYOUT_TITLE)) ? -1 : 0 ); + + // progress display if necessary + if (mnPagesToProcess) + { + mnPagesProcessed++; + if (mpProgress) + mpProgress->SetState(mnPagesProcessed); + + if (mnPagesProcessed == mnPagesToProcess) + { + mpProgress.reset(); + mnPagesToProcess = 0; + mnPagesProcessed = 0; + } + } + pOutliner->UpdateFields(); + } + else if ( (pOutliner->GetPrevDepth() == 1) && ( pOutliner->GetDepth( pOutliner->GetAbsPos( pPara ) ) == 2 ) ) + { + // how many titles are in front of the title paragraph in question? + sal_Int32 nPos = -1; + + Paragraph* pParagraph = pPara; + while(pParagraph) + { + pParagraph = GetPrevTitle(pParagraph); + if (pParagraph) + nPos++; + } + + if(nPos >= 0) + { + SdPage*pPage = mrDoc.GetSdPage( static_cast<sal_uInt16>(nPos), PageKind::Standard); + + if(pPage && pPage->GetPresObj(PresObjKind::Text)) + pOutliner->SetDepth( pPara, 0 ); + } + + } + // how many titles are in front of the title paragraph in question? + sal_Int32 nPos = -1; + + Paragraph* pTempPara = pPara; + while(pTempPara) + { + pTempPara = GetPrevTitle(pTempPara); + if (pTempPara) + nPos++; + } + + if( nPos < 0 ) + return; + + SdPage* pPage = mrDoc.GetSdPage( static_cast<sal_uInt16>(nPos), PageKind::Standard ); + + if( !pPage ) + return; + + SfxStyleSheet* pStyleSheet = nullptr; + sal_Int32 nPara = pOutliner->GetAbsPos( pPara ); + sal_Int16 nDepth = pOutliner->GetDepth( nPara ); + bool bSubTitle = pPage->GetPresObj(PresObjKind::Text) != nullptr; + + if( ::Outliner::HasParaFlag(pPara, ParaFlag::ISPAGE) ) + { + pStyleSheet = pPage->GetStyleSheetForPresObj( PresObjKind::Title ); + } + else if( bSubTitle ) + { + pStyleSheet = pPage->GetStyleSheetForPresObj( PresObjKind::Text ); + } + else + { + pStyleSheet = pPage->GetStyleSheetForPresObj( PresObjKind::Outline ); + + if( nDepth > 0 ) + { + OUString aNewStyleSheetName = pStyleSheet->GetName(); + if (!aNewStyleSheetName.isEmpty()) + aNewStyleSheetName = aNewStyleSheetName.copy(0, aNewStyleSheetName.getLength() - 1); + aNewStyleSheetName += OUString::number( nDepth+1 ); + SfxStyleSheetBasePool* pStylePool = mrDoc.GetStyleSheetPool(); + pStyleSheet = static_cast<SfxStyleSheet*>( pStylePool->Find( aNewStyleSheetName, pStyleSheet->GetFamily() ) ); + } + } + + // before we set the style sheet we need to preserve the bullet item + // since all items will be deleted while setting a new style sheet + SfxItemSet aOldAttrs( pOutliner->GetParaAttribs( nPara ) ); + + pOutliner->SetStyleSheet( nPara, pStyleSheet ); + + // restore the old bullet item but not if the style changed + if ( pOutliner->GetPrevDepth() != -1 && nDepth != -1 && + aOldAttrs.GetItemState( EE_PARA_NUMBULLET ) == SfxItemState::SET ) + { + SfxItemSet aAttrs( pOutliner->GetParaAttribs( nPara ) ); + aAttrs.Put( *aOldAttrs.GetItem( EE_PARA_NUMBULLET ) ); + pOutliner->SetParaAttribs( nPara, aAttrs ); + } +} + +/** + * Handler for StatusEvents + */ +IMPL_LINK_NOARG(OutlineView, StatusEventHdl, EditStatus&, void) +{ + ::sd::Window* pWin = mrOutlineViewShell.GetActiveWindow(); + OutlinerView* pOutlinerView = GetViewByWindow(pWin); + ::tools::Rectangle aVis = pOutlinerView->GetVisArea(); + ::tools::Rectangle aText(Point(0,0), + Size(mnPaperWidth, + mrOutliner.GetTextHeight())); + ::tools::Rectangle aWin(Point(0,0), pWin->GetOutputSizePixel()); + aWin = pWin->PixelToLogic(aWin); + + if (!aVis.IsEmpty()) // not when opening + { + if (aWin.GetHeight() > aText.Bottom()) + aText.SetBottom( aWin.GetHeight() ); + + mrOutlineViewShell.InitWindows(Point(0,0), aText.GetSize(), aVis.TopLeft()); + mrOutlineViewShell.UpdateScrollBars(); + } +} + +IMPL_LINK_NOARG(OutlineView, BeginDropHdl, EditView*, void) +{ + DBG_ASSERT(maDragAndDropModelGuard == nullptr, + "sd::OutlineView::BeginDropHdl(), prior drag operation not finished correctly!"); + + maDragAndDropModelGuard.reset( new OutlineViewModelChangeGuard( *this ) ); +} + +IMPL_LINK_NOARG(OutlineView, EndDropHdl, EditView*, void) +{ + maDragAndDropModelGuard.reset(); +} + +/** + * Handler for the start of a paragraph movement + */ +IMPL_LINK( OutlineView, BeginMovingHdl, ::Outliner *, pOutliner, void ) +{ + OutlineViewPageChangesGuard aGuard(this); + + // list of selected title paragraphs + mpOutlinerViews[0]->CreateSelectionList(maSelectedParas); + + maSelectedParas.erase(std::remove_if(maSelectedParas.begin(), maSelectedParas.end(), + [](const Paragraph* pPara) { return !Outliner::HasParaFlag(pPara, ParaFlag::ISPAGE); }), + maSelectedParas.end()); + + // select the pages belonging to the paragraphs on level 0 to select + sal_uInt16 nPos = 0; + sal_Int32 nParaPos = 0; + Paragraph* pPara = pOutliner->GetParagraph( 0 ); + std::vector<Paragraph*>::const_iterator fiter; + + while(pPara) + { + if( ::Outliner::HasParaFlag(pPara, ParaFlag::ISPAGE) ) // one page? + { + maOldParaOrder.push_back(pPara); + SdPage* pPage = mrDoc.GetSdPage(nPos, PageKind::Standard); + + fiter = std::find(maSelectedParas.begin(),maSelectedParas.end(),pPara); + + pPage->SetSelected(fiter != maSelectedParas.end()); + + ++nPos; + } + pPara = pOutliner->GetParagraph( ++nParaPos ); + } +} + +/** + * Handler for the end of a paragraph movement + */ +IMPL_LINK( OutlineView, EndMovingHdl, ::Outliner *, pOutliner, void ) +{ + OutlineViewPageChangesGuard aGuard(this); + + DBG_ASSERT( isRecordingUndo(), "sd::OutlineView::EndMovingHdl(), model change without undo?!" ); + + // look for insertion position via the first paragraph + Paragraph* pSearchIt = maSelectedParas.empty() ? nullptr : *(maSelectedParas.begin()); + + // look for the first of the selected paragraphs in the new ordering + sal_uInt16 nPosNewOrder = 0; + sal_Int32 nParaPos = 0; + Paragraph* pPara = pOutliner->GetParagraph( 0 ); + Paragraph* pPrev = nullptr; + while (pPara && pPara != pSearchIt) + { + if( ::Outliner::HasParaFlag(pPara, ParaFlag::ISPAGE) ) + { + nPosNewOrder++; + pPrev = pPara; + } + pPara = pOutliner->GetParagraph( ++nParaPos ); + } + + sal_uInt16 nPos = nPosNewOrder; // don't change nPosNewOrder + if (nPos == 0) + { + nPos = sal_uInt16(-1); // insert before the first page + } + else + { + // look for the predecessor in the old ordering + std::vector<Paragraph*>::const_iterator it = std::find(maOldParaOrder.begin(), + maOldParaOrder.end(), + pPrev); + + if (it != maOldParaOrder.end()) + nPos = static_cast<sal_uInt16>(it-maOldParaOrder.begin()); + else + nPos = 0xffff; + + DBG_ASSERT(nPos != 0xffff, "Paragraph not found"); + } + + mrDoc.MovePages(nPos); + + // deselect the pages again + sal_uInt16 nPageCount = static_cast<sal_uInt16>(maSelectedParas.size()); + while (nPageCount) + { + SdPage* pPage = mrDoc.GetSdPage(nPosNewOrder, PageKind::Standard); + pPage->SetSelected(false); + nPosNewOrder++; + nPageCount--; + } + + pOutliner->UpdateFields(); + + maSelectedParas.clear(); + maOldParaOrder.clear(); +} + +/** + * Look for the title text object in one page of the model + */ +SdrTextObj* OutlineView::GetTitleTextObject(SdrPage const * pPage) +{ + const size_t nObjectCount = pPage->GetObjCount(); + SdrTextObj* pResult = nullptr; + + for (size_t nObject = 0; nObject < nObjectCount; ++nObject) + { + SdrObject* pObject = pPage->GetObj(nObject); + if (pObject->GetObjInventor() == SdrInventor::Default && + pObject->GetObjIdentifier() == SdrObjKind::TitleText) + { + pResult = static_cast<SdrTextObj*>(pObject); + break; + } + } + return pResult; +} + +/** + * Look for the outline text object in one page of the model + */ +SdrTextObj* OutlineView::GetOutlineTextObject(SdrPage const * pPage) +{ + const size_t nObjectCount = pPage->GetObjCount(); + SdrTextObj* pResult = nullptr; + + for (size_t nObject = 0; nObject < nObjectCount; ++nObject) + { + SdrObject* pObject = pPage->GetObj(nObject); + if (pObject->GetObjInventor() == SdrInventor::Default && + pObject->GetObjIdentifier() == SdrObjKind::OutlineText) + { + pResult = static_cast<SdrTextObj*>(pObject); + break; + } + } + return pResult; +} + +SdrTextObj* OutlineView::CreateTitleTextObject(SdPage* pPage) +{ + DBG_ASSERT( GetTitleTextObject(pPage) == nullptr, "sd::OutlineView::CreateTitleTextObject(), there is already a title text object!" ); + + if( pPage->GetAutoLayout() == AUTOLAYOUT_NONE ) + { + // simple case + pPage->SetAutoLayout( AUTOLAYOUT_TITLE_ONLY, true ); + } + else + { + // we already have a layout with a title but the title + // object was deleted, create a new one + pPage->InsertAutoLayoutShape( nullptr, PresObjKind::Title, false, pPage->GetTitleRect(), true ); + } + + return GetTitleTextObject(pPage); +} + +SdrTextObj* OutlineView::CreateOutlineTextObject(SdPage* pPage) +{ + DBG_ASSERT( GetOutlineTextObject(pPage) == nullptr, "sd::OutlineView::CreateOutlineTextObject(), there is already a layout text object!" ); + + AutoLayout eNewLayout = pPage->GetAutoLayout(); + switch( eNewLayout ) + { + case AUTOLAYOUT_NONE: + case AUTOLAYOUT_TITLE_ONLY: + case AUTOLAYOUT_TITLE: eNewLayout = AUTOLAYOUT_TITLE_CONTENT; break; + + case AUTOLAYOUT_CHART: eNewLayout = AUTOLAYOUT_CHARTTEXT; break; + + case AUTOLAYOUT_ORG: + case AUTOLAYOUT_TAB: + case AUTOLAYOUT_OBJ: eNewLayout = AUTOLAYOUT_OBJTEXT; break; + default: + break; + } + + if( eNewLayout != pPage->GetAutoLayout() ) + { + pPage->SetAutoLayout( eNewLayout, true ); + } + else + { + // we already have a layout with a text but the text + // object was deleted, create a new one + pPage->InsertAutoLayoutShape( nullptr, + PresObjKind::Outline, + false, pPage->GetLayoutRect(), true ); + } + + return GetOutlineTextObject(pPage); +} + +/** updates draw model with all changes from outliner model */ +void OutlineView::PrepareClose() +{ + ::sd::UndoManager* pDocUndoMgr = dynamic_cast<sd::UndoManager*>(mpDocSh->GetUndoManager()); + if (pDocUndoMgr != nullptr) + pDocUndoMgr->SetLinkedUndoManager(nullptr); + + mrOutliner.GetUndoManager().Clear(); + + BegUndo(SdResId(STR_UNDO_CHANGE_TITLE_AND_LAYOUT)); + UpdateDocument(); + EndUndo(); + mrDoc.SetSelected(GetActualPage(), true); +} + +/** + * Set attributes of the selected text + */ +bool OutlineView::SetAttributes(const SfxItemSet& rSet, bool /*bSlide*/, bool /*bReplaceAll*/, bool /*bMaster*/) +{ + bool bOk = false; + + OutlinerView* pOlView = GetViewByWindow(mrOutlineViewShell.GetActiveWindow()); + + if (pOlView) + { + pOlView->SetAttribs(rSet); + bOk = true; + } + + mrOutlineViewShell.Invalidate (SID_PREVIEW_STATE); + + return bOk; +} + +/** + * Get attributes of the selected text + */ +void OutlineView::GetAttributes( SfxItemSet& rTargetSet, bool ) const +{ + OutlinerView* pOlView = GetViewByWindow( + mrOutlineViewShell.GetActiveWindow()); + assert(pOlView && "No OutlinerView found"); + + rTargetSet.Put( pOlView->GetAttribs(), false ); +} + +/** creates outliner model from draw model */ +void OutlineView::FillOutliner() +{ + mrOutliner.GetUndoManager().Clear(); + mrOutliner.EnableUndo(false); + ResetLinks(); + const bool bPrevUpdateLayout = mrOutliner.SetUpdateLayout(false); + + Paragraph* pTitleToSelect = nullptr; + sal_uInt16 nPageCount = mrDoc.GetSdPageCount(PageKind::Standard); + + // fill outliner with paragraphs from slides title & (outlines|subtitles) + for (sal_uInt16 nPage = 0; nPage < nPageCount; nPage++) + { + SdPage* pPage = mrDoc.GetSdPage(nPage, PageKind::Standard); + Paragraph * pPara = nullptr; + + // take text from title shape + SdrTextObj* pTO = GetTitleTextObject(pPage); + if(pTO && !(pTO->IsEmptyPresObj())) + { + OutlinerParaObject* pOPO = pTO->GetOutlinerParaObject(); + if (pOPO) + { + bool bVertical = pOPO->IsEffectivelyVertical(); + pOPO->SetVertical( false ); + mrOutliner.AddText(*pOPO); + pOPO->SetVertical( bVertical ); + pPara = mrOutliner.GetParagraph( mrOutliner.GetParagraphCount()-1 ); + } + } + + if( pPara == nullptr ) // no title, insert an empty paragraph + { + pPara = mrOutliner.Insert(OUString()); + mrOutliner.SetDepth(pPara, -1); + + // do not apply hard attributes from the previous paragraph + mrOutliner.SetParaAttribs( mrOutliner.GetAbsPos(pPara), + mrOutliner.GetEmptyItemSet() ); + + mrOutliner.SetStyleSheet( mrOutliner.GetAbsPos( pPara ), pPage->GetStyleSheetForPresObj( PresObjKind::Title ) ); + } + + mrOutliner.SetParaFlag( pPara, ParaFlag::ISPAGE ); + + sal_Int32 nPara = mrOutliner.GetAbsPos( pPara ); + + UpdateParagraph( nPara ); + + // remember paragraph of currently selected page + if (pPage->IsSelected()) + pTitleToSelect = pPara; + + // take text from subtitle or outline + pTO = static_cast<SdrTextObj*>(pPage->GetPresObj(PresObjKind::Text)); + const bool bSubTitle = pTO != nullptr; + + if (!pTO) // if no subtile found, try outline + pTO = GetOutlineTextObject(pPage); + + if(pTO && !(pTO->IsEmptyPresObj())) // found some text + { + OutlinerParaObject* pOPO = pTO->GetOutlinerParaObject(); + if (pOPO) + { + sal_Int32 nParaCount1 = mrOutliner.GetParagraphCount(); + bool bVertical = pOPO->IsEffectivelyVertical(); + pOPO->SetVertical( false ); + mrOutliner.AddText(*pOPO); + pOPO->SetVertical( bVertical ); + + sal_Int32 nParaCount2 = mrOutliner.GetParagraphCount(); + for (sal_Int32 n = nParaCount1; n < nParaCount2; n++) + { + if( bSubTitle ) + { + Paragraph* p = mrOutliner.GetParagraph(n); + if(p && mrOutliner.GetDepth( n ) > 0 ) + mrOutliner.SetDepth(p, 0); + } + + UpdateParagraph( n ); + } + } + } + } + + // place cursor at the start + Paragraph* pFirstPara = mrOutliner.GetParagraph( 0 ); + mpOutlinerViews[0]->Select( pFirstPara ); + mpOutlinerViews[0]->Select( pFirstPara, false ); + + // select title of slide that was selected + if (pTitleToSelect) + mpOutlinerViews[0]->Select(pTitleToSelect); + + SetLinks(); + + mrOutliner.EnableUndo(true); + + mrOutliner.SetUpdateLayout(bPrevUpdateLayout); +} + +/** + * Handler for deleting of level 0 paragraphs (pages): Warning + */ +IMPL_LINK_NOARG(OutlineView, RemovingPagesHdl, OutlinerView*, bool) +{ + sal_Int32 nNumOfPages = mrOutliner.GetSelPageCount(); + + if (nNumOfPages > PROCESS_WITH_PROGRESS_THRESHOLD) + { + mnPagesToProcess = nNumOfPages; + mnPagesProcessed = 0; + } + + if (mnPagesToProcess) + { + mpProgress.reset( new SfxProgress( GetDocSh(), SdResId(STR_DELETE_PAGES), mnPagesToProcess ) ); + } + mrOutliner.UpdateFields(); + + return true; +} + +/** + * Handler for indenting level 0 paragraphs (pages): Warning + */ +IMPL_LINK( OutlineView, IndentingPagesHdl, OutlinerView *, pOutlinerView, bool ) +{ + return RemovingPagesHdl(pOutlinerView); +} + +/** returns the first slide that is selected in the outliner or where + the cursor is located */ +SdPage* OutlineView::GetActualPage() +{ + ::sd::Window* pWin = mrOutlineViewShell.GetActiveWindow(); + OutlinerView* pActiveView = GetViewByWindow(pWin); + + std::vector<Paragraph*> aSelList; + pActiveView->CreateSelectionList(aSelList); + + Paragraph *pPar = aSelList.empty() ? nullptr : *(aSelList.begin()); + SdPage* pCurrent = GetPageForParagraph(pPar); + + DBG_ASSERT( pCurrent || + (mpDocSh->GetUndoManager() && static_cast< sd::UndoManager *>(mpDocSh->GetUndoManager())->IsDoing()) || + maDragAndDropModelGuard, + "sd::OutlineView::GetActualPage(), no current page?" ); + + if( pCurrent ) + return pCurrent; + + return mrDoc.GetSdPage( 0, PageKind::Standard ); +} + +SdPage* OutlineView::GetPageForParagraph( Paragraph* pPara ) +{ + if( !::Outliner::HasParaFlag(pPara,ParaFlag::ISPAGE) ) + pPara = GetPrevTitle(pPara); + + sal_uInt32 nPageToSelect = 0; + while(pPara) + { + pPara = GetPrevTitle(pPara); + if(pPara) + nPageToSelect++; + } + + if( nPageToSelect < static_cast<sal_uInt32>(mrDoc.GetSdPageCount( PageKind::Standard )) ) + return mrDoc.GetSdPage( static_cast<sal_uInt16>(nPageToSelect), PageKind::Standard ); + + return nullptr; +} + +Paragraph* OutlineView::GetParagraphForPage( ::Outliner const & rOutl, SdPage const * pPage ) +{ + // get the number of paragraphs with ident 0 we need to skip before + // we find the actual page + sal_uInt32 nPagesToSkip = (pPage->GetPageNum() - 1) >> 1; + + sal_Int32 nParaPos = 0; + Paragraph* pPara = rOutl.GetParagraph( 0 ); + while( pPara ) + { + // if this paragraph is a page... + if( ::Outliner::HasParaFlag(pPara,ParaFlag::ISPAGE) ) + { + // see if we already skipped enough pages + if( 0 == nPagesToSkip ) + break; // and if so, end the loop + + // we skipped another page + nPagesToSkip--; + } + + // get next paragraph + pPara = mrOutliner.GetParagraph( ++nParaPos ); + } + + return pPara; +} + +/** selects the paragraph for the given page at the outliner view*/ +void OutlineView::SetActualPage( SdPage const * pActual ) +{ + if( pActual && dynamic_cast<SdOutliner&>(mrOutliner).GetIgnoreCurrentPageChangesLevel()==0 && !mbFirstPaint) + { + // if we found a paragraph, select its text at the outliner view + Paragraph* pPara = GetParagraphForPage( mrOutliner, pActual ); + if( pPara ) + mpOutlinerViews[0]->Select( pPara ); + } +} + +/** + * Get StyleSheet from the selection + */ +SfxStyleSheet* OutlineView::GetStyleSheet() const +{ + ::sd::Window* pActWin = mrOutlineViewShell.GetActiveWindow(); + OutlinerView* pOlView = GetViewByWindow(pActWin); + SfxStyleSheet* pResult = pOlView->GetStyleSheet(); + return pResult; +} + +/** + * Mark pages as selected / not selected + */ +void OutlineView::SetSelectedPages() +{ + // list of selected title paragraphs + std::vector<Paragraph*> aSelParas; + mpOutlinerViews[0]->CreateSelectionList(aSelParas); + + aSelParas.erase(std::remove_if(aSelParas.begin(), aSelParas.end(), + [](const Paragraph* pPara) { return !Outliner::HasParaFlag(pPara, ParaFlag::ISPAGE); }), + aSelParas.end()); + + // select the pages belonging to the paragraphs on level 0 to select + sal_uInt16 nPos = 0; + sal_Int32 nParaPos = 0; + Paragraph *pPara = mrOutliner.GetParagraph( 0 ); + std::vector<Paragraph*>::const_iterator fiter; + + while(pPara) + { + if( ::Outliner::HasParaFlag(pPara, ParaFlag::ISPAGE) ) // one page + { + SdPage* pPage = mrDoc.GetSdPage(nPos, PageKind::Standard); + DBG_ASSERT(pPage!=nullptr, + "Trying to select non-existing page OutlineView::SetSelectedPages()"); + + if (pPage) + { + fiter = std::find(aSelParas.begin(),aSelParas.end(),pPara); + pPage->SetSelected(fiter != aSelParas.end()); + } + + nPos++; + } + + pPara = mrOutliner.GetParagraph( ++nParaPos ); + } +} + +/** + * Set new links + */ +void OutlineView::SetLinks() +{ + // set notification links + mrOutliner.SetParaInsertedHdl(LINK(this, OutlineView, ParagraphInsertedHdl)); + mrOutliner.SetParaRemovingHdl(LINK(this, OutlineView, ParagraphRemovingHdl)); + mrOutliner.SetDepthChangedHdl(LINK(this, OutlineView, DepthChangedHdl)); + mrOutliner.SetBeginMovingHdl(LINK(this, OutlineView, BeginMovingHdl)); + mrOutliner.SetEndMovingHdl(LINK(this, OutlineView, EndMovingHdl)); + mrOutliner.SetRemovingPagesHdl(LINK(this, OutlineView, RemovingPagesHdl)); + mrOutliner.SetIndentingPagesHdl(LINK(this, OutlineView, IndentingPagesHdl)); + mrOutliner.SetStatusEventHdl(LINK(this, OutlineView, StatusEventHdl)); + mrOutliner.SetBeginDropHdl(LINK(this,OutlineView, BeginDropHdl)); + mrOutliner.SetEndDropHdl(LINK(this,OutlineView, EndDropHdl)); + mrOutliner.SetPaintFirstLineHdl(LINK(this,OutlineView,PaintingFirstLineHdl)); + mrOutliner.SetBeginPasteOrDropHdl(LINK(this,OutlineView, BeginPasteOrDropHdl)); + mrOutliner.SetEndPasteOrDropHdl(LINK(this,OutlineView, EndPasteOrDropHdl)); +} + +/** + * Restore old links + */ +void OutlineView::ResetLinks() const +{ + mrOutliner.SetParaInsertedHdl(Link<::Outliner::ParagraphHdlParam,void>()); + mrOutliner.SetParaRemovingHdl(Link<::Outliner::ParagraphHdlParam,void>()); + mrOutliner.SetDepthChangedHdl(Link<::Outliner::DepthChangeHdlParam,void>()); + mrOutliner.SetBeginMovingHdl(Link<::Outliner*,void>()); + mrOutliner.SetEndMovingHdl(Link<::Outliner*,void>()); + mrOutliner.SetStatusEventHdl(Link<EditStatus&,void>()); + mrOutliner.SetRemovingPagesHdl(Link<OutlinerView*,bool>()); + mrOutliner.SetIndentingPagesHdl(Link<OutlinerView*,bool>()); + mrOutliner.SetDrawPortionHdl(Link<DrawPortionInfo*,void>()); + mrOutliner.SetBeginPasteOrDropHdl(Link<PasteOrDropInfos*,void>()); + mrOutliner.SetEndPasteOrDropHdl(Link<PasteOrDropInfos*,void>()); +} + +sal_Int8 OutlineView::AcceptDrop( const AcceptDropEvent&, DropTargetHelper&, SdrLayerID) +{ + return DND_ACTION_NONE; +} + +sal_Int8 OutlineView::ExecuteDrop( const ExecuteDropEvent&, ::sd::Window*, sal_uInt16, SdrLayerID) +{ + return DND_ACTION_NONE; +} + +// Re-implement GetScriptType for this view to get correct results +SvtScriptType OutlineView::GetScriptType() const +{ + SvtScriptType nScriptType = ::sd::View::GetScriptType(); + + std::optional<OutlinerParaObject> pTempOPObj = mrOutliner.CreateParaObject(); + if(pTempOPObj) + { + nScriptType = pTempOPObj->GetTextObject().GetScriptType(); + } + + return nScriptType; +} + +void OutlineView::onUpdateStyleSettings( bool bForceUpdate /* = false */ ) +{ + svtools::ColorConfig aColorConfig; + const Color aDocColor( aColorConfig.GetColorValue( svtools::DOCCOLOR ).nColor ); + if( !(bForceUpdate || (maDocColor != aDocColor)) ) + return; + + sal_uInt16 nView; + for( nView = 0; nView < MAX_OUTLINERVIEWS; nView++ ) + { + if (mpOutlinerViews[nView] != nullptr) + { + mpOutlinerViews[nView]->SetBackgroundColor( aDocColor ); + + vcl::Window* pWindow = mpOutlinerViews[nView]->GetWindow(); + + if( pWindow ) + pWindow->SetBackground( Wallpaper( aDocColor ) ); + + } + } + + mrOutliner.SetBackgroundColor( aDocColor ); + + maDocColor = aDocColor; +} + +IMPL_LINK_NOARG(OutlineView, AppEventListenerHdl, VclSimpleEvent&, void) +{ + onUpdateStyleSettings(false); +} + +IMPL_LINK(OutlineView, EventMultiplexerListener, ::sd::tools::EventMultiplexerEvent&, rEvent, void) +{ + switch (rEvent.meEventId) + { + case EventMultiplexerEventId::CurrentPageChanged: + SetActualPage(mrOutlineViewShell.GetActualPage()); + break; + + case EventMultiplexerEventId::PageOrder: + if (dynamic_cast<SdOutliner&>(mrOutliner).GetIgnoreCurrentPageChangesLevel()==0) + { + if (((mrDoc.GetPageCount()-1)%2) == 0) + { + mrOutliner.Clear(); + FillOutliner(); + ::sd::Window* pWindow = mrOutlineViewShell.GetActiveWindow(); + if (pWindow != nullptr) + pWindow->Invalidate(); + } + } + break; + + default: break; + } +} + +void OutlineView::IgnoreCurrentPageChanges (bool bIgnoreChanges) +{ + if (bIgnoreChanges) + dynamic_cast<SdOutliner&>(mrOutliner).IncreIgnoreCurrentPageChangesLevel(); + else + dynamic_cast<SdOutliner&>(mrOutliner).DecreIgnoreCurrentPageChangesLevel(); +} + +/** call this method before you do anything that can modify the outliner + and or the drawing document model. It will create needed undo actions */ +void OutlineView::BeginModelChange() +{ + mrOutliner.GetUndoManager().EnterListAction("", "", 0, mrOutlineViewShell.GetViewShellBase().GetViewShellId()); + BegUndo(SdResId(STR_UNDO_CHANGE_TITLE_AND_LAYOUT)); +} + +/** call this method after BeginModelChange(), when all possible model + changes are done. */ +void OutlineView::EndModelChange() +{ + UpdateDocument(); + + SfxUndoManager* pDocUndoMgr = mpDocSh->GetUndoManager(); + + bool bHasUndoActions = pDocUndoMgr->GetUndoActionCount() != 0; + + EndUndo(); + + DBG_ASSERT( bHasUndoActions == (mrOutliner.GetUndoManager().GetUndoActionCount() != 0), "sd::OutlineView::EndModelChange(), undo actions not in sync!" ); + + mrOutliner.GetUndoManager().LeaveListAction(); + + if( bHasUndoActions && mrOutliner.GetEditEngine().HasTriedMergeOnLastAddUndo() ) + TryToMergeUndoActions(); + + mrOutlineViewShell.Invalidate( SID_UNDO ); + mrOutlineViewShell.Invalidate( SID_REDO ); +} + +/** updates all changes in the outliner model to the draw model */ +void OutlineView::UpdateDocument() +{ + OutlineViewPageChangesGuard aGuard(this); + + const sal_uInt32 nPageCount = mrDoc.GetSdPageCount(PageKind::Standard); + Paragraph* pPara = mrOutliner.GetParagraph( 0 ); + sal_uInt32 nPage; + for (nPage = 0; nPage < nPageCount; nPage++) + { + SdPage* pPage = mrDoc.GetSdPage( static_cast<sal_uInt16>(nPage), PageKind::Standard); + mrDoc.SetSelected(pPage, false); + + mrOutlineViewShell.UpdateTitleObject( pPage, pPara ); + mrOutlineViewShell.UpdateOutlineObject( pPage, pPara ); + + if( pPara ) + pPara = GetNextTitle(pPara); + } + + DBG_ASSERT( pPara == nullptr, "sd::OutlineView::UpdateDocument(), slides are out of sync, creating missing ones" ); + while( pPara ) + { + SdPage* pPage = InsertSlideForParagraph( pPara ); + mrDoc.SetSelected(pPage, false); + + mrOutlineViewShell.UpdateTitleObject( pPage, pPara ); + mrOutlineViewShell.UpdateOutlineObject( pPage, pPara ); + + pPara = GetNextTitle(pPara); + } +} + +/** merge edit engine undo actions if possible */ +void OutlineView::TryToMergeUndoActions() +{ + SfxUndoManager& rOutlineUndo = mrOutliner.GetUndoManager(); + if( rOutlineUndo.GetUndoActionCount() <= 1 ) + return; + + SfxListUndoAction* pListAction = dynamic_cast< SfxListUndoAction* >( rOutlineUndo.GetUndoAction() ); + SfxListUndoAction* pPrevListAction = dynamic_cast< SfxListUndoAction* >( rOutlineUndo.GetUndoAction(1) ); + if( !(pListAction && pPrevListAction) ) + return; + + // find the top EditUndo action in the top undo action list + size_t nAction = pListAction->maUndoActions.size(); + EditUndo* pEditUndo = nullptr; + while( !pEditUndo && nAction ) + { + pEditUndo = dynamic_cast< EditUndo* >(pListAction->GetUndoAction(--nAction)); + } + + sal_uInt16 nEditPos = nAction; // we need this later to remove the merged undo actions + + // make sure it is the only EditUndo action in the top undo list + while( pEditUndo && nAction ) + { + if( dynamic_cast< EditUndo* >(pListAction->GetUndoAction(--nAction)) ) + pEditUndo = nullptr; + } + + // do we have one and only one EditUndo action in the top undo list? + if( !pEditUndo ) + return; + + // yes, see if we can merge it with the prev undo list + + nAction = pPrevListAction->maUndoActions.size(); + EditUndo* pPrevEditUndo = nullptr; + while( !pPrevEditUndo && nAction ) + pPrevEditUndo = dynamic_cast< EditUndo* >(pPrevListAction->GetUndoAction(--nAction)); + + if( !(pPrevEditUndo && pPrevEditUndo->Merge( pEditUndo )) ) + return; + + // ok we merged the only EditUndo of the top undo list with + // the top EditUndo of the previous undo list + + // first remove the merged undo action + assert( pListAction->GetUndoAction(nEditPos) == pEditUndo && + "sd::OutlineView::TryToMergeUndoActions(), wrong edit pos!" ); + pListAction->Remove(nEditPos); + + if ( !pListAction->maUndoActions.empty() ) + { + // now we have to move all remaining doc undo actions from the top undo + // list to the previous undo list and remove the top undo list + + size_t nCount = pListAction->maUndoActions.size(); + size_t nDestAction = pPrevListAction->maUndoActions.size(); + while( nCount-- ) + { + std::unique_ptr<SfxUndoAction> pTemp = pListAction->Remove(0); + pPrevListAction->Insert( std::move(pTemp), nDestAction++ ); + } + pPrevListAction->nCurUndoAction = pPrevListAction->maUndoActions.size(); + } + + rOutlineUndo.RemoveLastUndoAction(); +} + +IMPL_LINK(OutlineView, PaintingFirstLineHdl, PaintFirstLineInfo*, pInfo, void) +{ + if( !pInfo ) + return; + + Paragraph* pPara = mrOutliner.GetParagraph( pInfo->mnPara ); + EditEngine& rEditEngine = const_cast< EditEngine& >( mrOutliner.GetEditEngine() ); + + Size aImageSize( pInfo->mpOutDev->PixelToLogic( maSlideImage.GetSizePixel() ) ); + Size aOffset( 100, 100 ); + + // paint slide number + if( !(pPara && ::Outliner::HasParaFlag(pPara,ParaFlag::ISPAGE)) ) + return; + + ::tools::Long nPage = 0; // todo, printing?? + for ( sal_Int32 n = 0; n <= pInfo->mnPara; n++ ) + { + Paragraph* p = mrOutliner.GetParagraph( n ); + if ( ::Outliner::HasParaFlag(p,ParaFlag::ISPAGE) ) + nPage++; + } + + ::tools::Long nBulletHeight = static_cast<::tools::Long>(mrOutliner.GetLineHeight( pInfo->mnPara )); + ::tools::Long nFontHeight = 0; + if ( !rEditEngine.IsFlatMode() ) + { + nFontHeight = nBulletHeight / 5; + } + else + { + nFontHeight = (nBulletHeight * 10) / 25; + } + + Size aFontSz( 0, nFontHeight ); + + Size aOutSize( 2000, nBulletHeight ); + + const float fImageHeight = (static_cast<float>(aOutSize.Height()) * float(4)) / float(7); + if (aImageSize.Width() != 0) + { + const float fImageRatio = static_cast<float>(aImageSize.Height()) / static_cast<float>(aImageSize.Width()); + aImageSize.setWidth( static_cast<::tools::Long>( fImageRatio * fImageHeight ) ); + } + aImageSize.setHeight( static_cast<::tools::Long>(fImageHeight) ); + + Point aImagePos( pInfo->mrStartPos ); + aImagePos.AdjustX(aOutSize.Width() - aImageSize.Width() - aOffset.Width() ) ; + aImagePos.AdjustY((aOutSize.Height() - aImageSize.Height()) / 2 ); + + pInfo->mpOutDev->DrawImage( aImagePos, aImageSize, maSlideImage ); + + const bool bVertical = mrOutliner.IsVertical(); + const bool bRightToLeftPara = rEditEngine.IsRightToLeft( pInfo->mnPara ); + + LanguageType eLang = rEditEngine.GetDefaultLanguage(); + + Point aTextPos( aImagePos.X() - aOffset.Width(), pInfo->mrStartPos.Y() ); + vcl::Font aNewFont( OutputDevice::GetDefaultFont( DefaultFontType::SANS_UNICODE, eLang, GetDefaultFontFlags::NONE ) ); + aNewFont.SetFontSize( aFontSz ); + aNewFont.SetVertical( bVertical ); + aNewFont.SetOrientation( Degree10(bVertical ? 2700 : 0) ); + aNewFont.SetColor( COL_AUTO ); + pInfo->mpOutDev->SetFont( aNewFont ); + OUString aPageText = OUString::number( nPage ); + Size aTextSz; + aTextSz.setWidth( pInfo->mpOutDev->GetTextWidth( aPageText ) ); + aTextSz.setHeight( pInfo->mpOutDev->GetTextHeight() ); + if ( !bVertical ) + { + aTextPos.AdjustY((aOutSize.Height() - aTextSz.Height()) / 2 ); + if ( !bRightToLeftPara ) + { + aTextPos.AdjustX( -(aTextSz.Width()) ); + } + else + { + aTextPos.AdjustX(aTextSz.Width() ); + } + } + else + { + aTextPos.AdjustY( -(aTextSz.Width()) ); + aTextPos.AdjustX(nBulletHeight / 2 ); + } + pInfo->mpOutDev->DrawText( aTextPos, aPageText ); +} + +void OutlineView::UpdateParagraph( sal_Int32 nPara ) +{ + SfxItemSet aNewAttrs2( mrOutliner.GetParaAttribs( nPara ) ); + aNewAttrs2.Put( maLRSpaceItem ); + mrOutliner.SetParaAttribs( nPara, aNewAttrs2 ); +} + +void OutlineView::OnBeginPasteOrDrop( PasteOrDropInfos* /*pInfo*/ ) +{ +} + +/** this is called after a paste or drop operation, make sure that the newly inserted paragraphs + get the correct style sheet and new slides are inserted. */ +void OutlineView::OnEndPasteOrDrop( PasteOrDropInfos* pInfo ) +{ + SdPage* pPage = nullptr; + SfxStyleSheetBasePool* pStylePool = GetDoc().GetStyleSheetPool(); + + for( sal_Int32 nPara = pInfo->nStartPara; nPara <= pInfo->nEndPara; nPara++ ) + { + Paragraph* pPara = mrOutliner.GetParagraph( nPara ); + + bool bPage = ::Outliner::HasParaFlag( pPara, ParaFlag::ISPAGE ); + + if( !bPage ) + { + SdStyleSheet* pStyleSheet = dynamic_cast< SdStyleSheet* >( mrOutliner.GetStyleSheet( nPara ) ); + if( pStyleSheet ) + { + if ( pStyleSheet->GetApiName() == "title" ) + bPage = true; + } + } + + if( !pPara ) + continue; // fatality!? + + if( bPage && (nPara != pInfo->nStartPara) ) + { + // insert new slide for this paragraph + pPage = InsertSlideForParagraph( pPara ); + } + else + { + // newly inserted non page paragraphs get the outline style + if( !pPage ) + pPage = GetPageForParagraph( pPara ); + + if( pPage ) + { + SfxStyleSheet* pStyle = pPage->GetStyleSheetForPresObj( bPage ? PresObjKind::Title : PresObjKind::Outline ); + + if( !bPage ) + { + const sal_Int16 nDepth = mrOutliner.GetDepth( nPara ); + if( nDepth > 0 ) + { + OUString aStyleSheetName = pStyle->GetName(); + if (!aStyleSheetName.isEmpty()) + aStyleSheetName = aStyleSheetName.copy(0, aStyleSheetName.getLength() - 1); + aStyleSheetName += OUString::number( nDepth ); + pStyle = static_cast<SfxStyleSheet*>( pStylePool->Find( aStyleSheetName, pStyle->GetFamily() ) ); + DBG_ASSERT( pStyle, "sd::OutlineView::OnEndPasteOrDrop(), Style not found!" ); + } + } + + mrOutliner.SetStyleSheet( nPara, pStyle ); + } + + UpdateParagraph( nPara ); + } + } +} + + +OutlineViewModelChangeGuard::OutlineViewModelChangeGuard( OutlineView& rView ) +: mrView( rView ) +{ + mrView.BeginModelChange(); +} + +OutlineViewModelChangeGuard::~OutlineViewModelChangeGuard() COVERITY_NOEXCEPT_FALSE +{ + mrView.EndModelChange(); +} + + +OutlineViewPageChangesGuard::OutlineViewPageChangesGuard( OutlineView* pView ) +: mpView( pView ) +{ + if( mpView ) + mpView->IgnoreCurrentPageChanges( true ); +} + +OutlineViewPageChangesGuard::~OutlineViewPageChangesGuard() +{ + if( mpView ) + mpView->IgnoreCurrentPageChanges( false ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/presvish.cxx b/sd/source/ui/view/presvish.cxx new file mode 100644 index 000000000..34a789f4d --- /dev/null +++ b/sd/source/ui/view/presvish.cxx @@ -0,0 +1,172 @@ +/* -*- 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 <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> + +#include <PresentationViewShell.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/svxids.hrc> +#include <svx/ruler.hxx> +#include <FrameView.hxx> +#include <DrawDocShell.hxx> +#include <slideshow.hxx> +#include <app.hrc> +#include <ViewShellBase.hxx> + +#include <fupoor.hxx> +#include <Window.hxx> + +#define ShellClass_PresentationViewShell +using namespace sd; +#include <sdslots.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; + +namespace sd { + +SFX_IMPL_INTERFACE(PresentationViewShell, DrawViewShell) + +void PresentationViewShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_TOOLS, SfxVisibilityFlags::Standard | SfxVisibilityFlags::FullScreen | SfxVisibilityFlags::Server, + ToolbarId::Draw_Toolbox_Sd); + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_APPLICATION, SfxVisibilityFlags::Standard | SfxVisibilityFlags::Client | SfxVisibilityFlags::Viewer | SfxVisibilityFlags::ReadonlyDoc, + ToolbarId::Draw_Viewer_Toolbox); + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OPTIONS, SfxVisibilityFlags::Standard | SfxVisibilityFlags::Server, + ToolbarId::Draw_Options_Toolbox); + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_COMMONTASK, SfxVisibilityFlags::Standard | SfxVisibilityFlags::Server, + ToolbarId::Draw_CommonTask_Toolbox); +} + + +PresentationViewShell::PresentationViewShell( ViewShellBase& rViewShellBase, vcl::Window* pParentWindow, FrameView* pFrameView) + : DrawViewShell(rViewShellBase, pParentWindow, PageKind::Standard, pFrameView) + , mnAbortSlideShowEvent(nullptr) +{ + if( GetDocSh() && GetDocSh()->GetCreateMode() == SfxObjectCreateMode::EMBEDDED ) + maOldVisArea = GetDocSh()->GetVisArea( ASPECT_CONTENT ); + meShellType = ST_PRESENTATION; +} + +PresentationViewShell::~PresentationViewShell() +{ + if (mnAbortSlideShowEvent) + Application::RemoveUserEvent(mnAbortSlideShowEvent); + + if( GetDocSh() && GetDocSh()->GetCreateMode() == SfxObjectCreateMode::EMBEDDED && !maOldVisArea.IsEmpty() ) + GetDocSh()->SetVisArea( maOldVisArea ); +} + +void PresentationViewShell::FinishInitialization( FrameView* pFrameView ) +{ + DrawViewShell::Init(true); + + // Use the frame view that comes form the view shell that initiated our + // creation. + if (pFrameView != nullptr) + { + GetFrameView()->Disconnect(); + SetFrameView (pFrameView); + pFrameView->Connect(); + } + SetRuler(false); + WriteFrameViewData(); + + GetActiveWindow()->GrabFocus(); +} + +VclPtr<SvxRuler> PresentationViewShell::CreateHRuler(::sd::Window*) +{ + return nullptr; +} + +VclPtr<SvxRuler> PresentationViewShell::CreateVRuler(::sd::Window*) +{ + return nullptr; +} + +IMPL_LINK_NOARG(PresentationViewShell, AbortSlideShowHdl, void*, void) +{ + mnAbortSlideShowEvent = nullptr; + rtl::Reference<SlideShow> xSlideShow(SlideShow::GetSlideShow(GetViewShellBase())); + if (xSlideShow.is()) + xSlideShow->end(); +} + +void PresentationViewShell::Activate( bool bIsMDIActivate ) +{ + DrawViewShell::Activate( bIsMDIActivate ); + + if( bIsMDIActivate ) + { + SfxBoolItem aItem( SID_NAVIGATOR_INIT, true ); + + GetViewFrame()->GetDispatcher()->ExecuteList(SID_NAVIGATOR_INIT, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, { &aItem }); + + rtl::Reference< SlideShow > xSlideShow( SlideShow::GetSlideShow( GetViewShellBase() ) ); + if( xSlideShow.is() ) + { + bool bSuccess = xSlideShow->activate(GetViewShellBase()); + if (!bSuccess) + { + /* tdf#64711 PresentationViewShell is deleted by 'end' due to end closing + the object shell. So if we call xSlideShow->end during Activate there are + a lot of places in the call stack of Activate which understandable don't + expect this ViewShell to be deleted during use. Defer to the next event + loop the abort of the slideshow + */ + if (!mnAbortSlideShowEvent) + mnAbortSlideShowEvent = Application::PostUserEvent(LINK(this, PresentationViewShell, AbortSlideShowHdl)); + } + } + + if( HasCurrentFunction() ) + GetCurrentFunction()->Activate(); + + ReadFrameViewData(mpFrameView); + } + + GetDocSh()->Connect( this ); +} + +void PresentationViewShell::Paint( const ::tools::Rectangle& /*rRect*/, ::sd::Window* ) +{ + rtl::Reference< SlideShow > xSlideShow( SlideShow::GetSlideShow( GetViewShellBase() ) ); + if( xSlideShow.is() ) + xSlideShow->paint(); +} + +void PresentationViewShell::Resize() +{ + ViewShell::Resize(); // do not call DrawViewShell here! + + rtl::Reference< sd::SlideShow > xSlideshow( SlideShow::GetSlideShow( GetViewShellBase() ) ); + if( xSlideshow.is() ) + xSlideshow->resize(maViewSize); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/sdruler.cxx b/sd/source/ui/view/sdruler.cxx new file mode 100644 index 000000000..571ffb37f --- /dev/null +++ b/sd/source/ui/view/sdruler.cxx @@ -0,0 +1,148 @@ +/* -*- 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 <Ruler.hxx> +#include <svl/ptitem.hxx> +#include <svx/ruler.hxx> +#include <svx/svxids.hrc> +#include <sfx2/ctrlitem.hxx> +#include <sfx2/bindings.hxx> +#include <vcl/commandevent.hxx> + +#include <View.hxx> +#include <DrawViewShell.hxx> +#include <Window.hxx> + +#include <helpids.h> + +namespace sd { + +/** + * Controller-Item for ruler + */ +class RulerCtrlItem : public SfxControllerItem +{ + Ruler &rRuler; + + protected: + virtual void StateChangedAtToolBoxControl( sal_uInt16 nSId, SfxItemState eState, + const SfxPoolItem* pItem ) override; + + public: + RulerCtrlItem(Ruler& rRlr, SfxBindings& rBind); +}; + +RulerCtrlItem::RulerCtrlItem(Ruler& rRlr, SfxBindings& rBind) +: SfxControllerItem(SID_RULER_NULL_OFFSET, rBind) +, rRuler(rRlr) +{ +} + +void RulerCtrlItem::StateChangedAtToolBoxControl( sal_uInt16 nSId, SfxItemState, const SfxPoolItem* pState ) +{ + switch( nSId ) + { + case SID_RULER_NULL_OFFSET: + { + const SfxPointItem* pItem = dynamic_cast< const SfxPointItem* >(pState); + DBG_ASSERT(pState == nullptr || pItem != nullptr, "SfxPointItem expected"); + if ( pItem ) + rRuler.SetNullOffset(pItem->GetValue()); + } + break; + } +} + +Ruler::Ruler( DrawViewShell& rViewSh, vcl::Window* pParent, ::sd::Window* pWin, SvxRulerSupportFlags nRulerFlags, SfxBindings& rBindings, WinBits nWinStyle) + : SvxRuler(pParent, pWin, nRulerFlags, rBindings, nWinStyle) + , pDrViewShell(&rViewSh) +{ + rBindings.EnterRegistrations(); + pCtrlItem.reset( new RulerCtrlItem(*this, rBindings) ); + rBindings.LeaveRegistrations(); + + if ( nWinStyle & WB_HSCROLL ) + { + bHorz = true; + SetHelpId( HID_SD_RULER_HORIZONTAL ); + } + else + { + bHorz = false; + SetHelpId( HID_SD_RULER_VERTICAL ); + } +} + +Ruler::~Ruler() +{ + disposeOnce(); +} + +void Ruler::dispose() +{ + SfxBindings& rBindings = pCtrlItem->GetBindings(); + rBindings.EnterRegistrations(); + pCtrlItem.reset(); + rBindings.LeaveRegistrations(); + SvxRuler::dispose(); +} + +void Ruler::MouseButtonDown(const MouseEvent& rMEvt) +{ + Point aMPos = rMEvt.GetPosPixel(); + RulerType eType = GetRulerType(aMPos); + + if ( !pDrViewShell->GetView()->IsTextEdit() && + rMEvt.IsLeft() && rMEvt.GetClicks() == 1 && + (eType == RulerType::DontKnow || eType == RulerType::Outside) ) + { + pDrViewShell->StartRulerDrag(*this, rMEvt); + } + else + SvxRuler::MouseButtonDown(rMEvt); +} + +void Ruler::SetNullOffset(const Point& rOffset) +{ + ::tools::Long nOffset; + + if ( bHorz ) nOffset = rOffset.X(); + else nOffset = rOffset.Y(); + + SetNullOffsetLogic(nOffset); +} + +void Ruler::Command(const CommandEvent& rCEvt) +{ + if( rCEvt.GetCommand() == CommandEventId::ContextMenu && + !pDrViewShell->GetView()->IsTextEdit() ) + { + SvxRuler::Command( rCEvt ); + } +} + +void Ruler::ExtraDown() +{ + if( !pDrViewShell->GetView()->IsTextEdit() ) + SvxRuler::ExtraDown(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/sdview.cxx b/sd/source/ui/view/sdview.cxx new file mode 100644 index 000000000..f27622fd1 --- /dev/null +++ b/sd/source/ui/view/sdview.cxx @@ -0,0 +1,1395 @@ +/* -*- 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 <com/sun/star/embed/NoVisualAreaSizeException.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/linguistic2/XSpellChecker1.hpp> + +#include <View.hxx> +#include <avmedia/mediawindow.hxx> +#include <editeng/outlobj.hxx> +#include <editeng/unolingu.hxx> +#include <o3tl/deleter.hxx> +#include <svx/obj3d.hxx> +#include <svx/fmview.hxx> +#include <editeng/outliner.hxx> +#include <svx/svdograf.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdundo.hxx> + +#include <vcl/settings.hxx> + +#include <officecfg/Office/Common.hxx> +#include <sfx2/dispatch.hxx> +#include <svx/svdpagv.hxx> +#include <svx/svdoutl.hxx> +#include <svx/sdr/contact/displayinfo.hxx> + +#include <svx/svdetc.hxx> +#include <editeng/editstat.hxx> + +#include <sfx2/viewfrm.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <svx/xfillit0.hxx> + +#include <app.hrc> +#include <strings.hrc> +#include <Window.hxx> +#include <Client.hxx> +#include <drawdoc.hxx> +#include <DrawDocShell.hxx> +#include <sdmod.hxx> +#include <sdpage.hxx> +#include <sdresid.hxx> +#include <unokywds.hxx> +#include <ViewClipboard.hxx> +#include <undo/undomanager.hxx> +#include <svx/sdr/contact/viewobjectcontact.hxx> +#include <svx/sdr/contact/viewcontact.hxx> +#include <svx/svdotable.hxx> +#include <EventMultiplexer.hxx> +#include <ViewShellBase.hxx> +#include <ViewShell.hxx> + +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/color/bcolor.hxx> +#include <drawinglayer/attribute/lineattribute.hxx> +#include <drawinglayer/attribute/strokeattribute.hxx> +#include <drawinglayer/primitive2d/textlayoutdevice.hxx> +#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx> +#include <svx/sdr/contact/objectcontact.hxx> +#include <svx/sdr/table/tablecontroller.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <drawinglayer/primitive2d/textprimitive2d.hxx> +#include <svx/unoapi.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <comphelper/lok.hxx> +#include <sfx2/lokhelper.hxx> +#include <LibreOfficeKit/LibreOfficeKitEnums.h> +#include <DrawController.hxx> +#include <svtools/optionsdrawinglayer.hxx> + +#include <memory> +#include <numeric> + +using namespace com::sun::star; +using namespace com::sun::star::uno; +using namespace sdr::table; +namespace sd { + +View::View( + SdDrawDocument& rDrawDoc, + OutputDevice* pOutDev, + ViewShell* pViewShell) +: FmFormView(rDrawDoc, pOutDev), + mrDoc(rDrawDoc), + mpDocSh(rDrawDoc.GetDocSh()), + mpViewSh(pViewShell), + mpDropMarkerObj(nullptr), + mnDragSrcPgNum(SDRPAGE_NOTFOUND), + mnAction(DND_ACTION_NONE), + maDropErrorIdle("sd View DropError"), + maDropInsertFileIdle("sd View DropInsertFile"), + mnLockRedrawSmph(0), + mbIsDropAllowed(true), + maSmartTags(*this), + mpClipboard (new ViewClipboard (*this)) +{ + // #i73602# Use default from the configuration + SetBufferedOverlayAllowed(SvtOptionsDrawinglayer::IsOverlayBuffer_DrawImpress()); + + // #i74769#, #i75172# Use default from the configuration + SetBufferedOutputAllowed(SvtOptionsDrawinglayer::IsPaintBuffer_DrawImpress()); + + EnableExtendedKeyInputDispatcher(false); + EnableExtendedMouseEventDispatcher(false); + + SetUseIncompatiblePathCreateInterface(false); + + SetMinMoveDistancePixel(2); + SetHitTolerancePixel(2); + SetMeasureLayer(sUNO_LayerName_measurelines); + + // Timer for delayed drop (has to be for MAC) + maDropErrorIdle.SetInvokeHandler( LINK(this, View, DropErrorHdl) ); + maDropInsertFileIdle.SetInvokeHandler( LINK(this, View, DropInsertFileHdl) ); +} + +void View::ImplClearDrawDropMarker() +{ + mpDropMarker.reset(); +} + +View::~View() +{ + maSmartTags.Dispose(); + + // release content of selection clipboard, if we own the content + ClearSelectionClipboard(); + + if (mxDropMediaSizeListener) + { + suppress_fun_call_w_exception(mxDropMediaSizeListener->dispose()); + mxDropMediaSizeListener.clear(); + } + + maDropErrorIdle.Stop(); + maDropInsertFileIdle.Stop(); + + ImplClearDrawDropMarker(); + + while(PaintWindowCount()) + { + // remove all registered OutDevs + suppress_fun_call_w_exception(DeleteWindowFromPaintView(GetFirstOutputDevice())); + } +} + +namespace { + +class ViewRedirector : public sdr::contact::ViewObjectContactRedirector +{ +public: + ViewRedirector(); + + // all default implementations just call the same methods at the original. To do something + // different, override the method and at least do what the method does. + virtual void createRedirectedPrimitive2DSequence( + const sdr::contact::ViewObjectContact& rOriginal, + const sdr::contact::DisplayInfo& rDisplayInfo, + drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) override; +}; + +} + +ViewRedirector::ViewRedirector() +{ +} + +void ViewRedirector::createRedirectedPrimitive2DSequence( + const sdr::contact::ViewObjectContact& rOriginal, + const sdr::contact::DisplayInfo& rDisplayInfo, + drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) +{ + SdrObject* pObject = rOriginal.GetViewContact().TryToGetSdrObject(); + SdrPage* pSdrPage = pObject ? pObject->getSdrPageFromSdrObject() : nullptr; + if(!pObject || !pSdrPage) + { + // not a SdrObject visualisation (maybe e.g. page) or no page + sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(rOriginal, rDisplayInfo, rVisitor); + return; + } + + const bool bDoCreateGeometry(pSdrPage->checkVisibility( rOriginal, rDisplayInfo, true )); + + if(!bDoCreateGeometry && + (( pObject->GetObjInventor() != SdrInventor::Default ) || ( pObject->GetObjIdentifier() != SdrObjKind::Page )) ) + return; + + PresObjKind eKind(PresObjKind::NONE); + const bool bSubContentProcessing(rDisplayInfo.GetSubContentActive()); + const bool bIsMasterPageObject(pSdrPage->IsMasterPage()); + const bool bIsPrinting(rOriginal.GetObjectContact().isOutputToPrinter()); + const SdrPageView* pPageView = rOriginal.GetObjectContact().TryToGetSdrPageView(); + const SdrPage* pVisualizedPage = GetSdrPageFromXDrawPage(rOriginal.GetObjectContact().getViewInformation2D().getVisualizedPage()); + const SdPage* pObjectsSdPage = dynamic_cast< SdPage* >(pSdrPage); + const bool bIsInsidePageObj(pPageView && pPageView->GetPage() != pVisualizedPage); + + // check if we need to draw a placeholder border. Never do it for + // objects inside a SdrPageObj and never when printing + if(!bIsInsidePageObj && !bIsPrinting) + { + bool bCreateOutline(false); + + if( pObject->IsEmptyPresObj() && dynamic_cast< SdrTextObj *>( pObject ) != nullptr ) + { + if( !bSubContentProcessing || !pObject->IsNotVisibleAsMaster() ) + { + eKind = pObjectsSdPage ? pObjectsSdPage->GetPresObjKind(pObject) : PresObjKind::NONE; + bCreateOutline = true; + } + } + else if( ( pObject->GetObjInventor() == SdrInventor::Default ) && ( pObject->GetObjIdentifier() == SdrObjKind::Text ) ) + { + if( pObjectsSdPage ) + { + eKind = pObjectsSdPage->GetPresObjKind(pObject); + + if((eKind == PresObjKind::Footer) || (eKind == PresObjKind::Header) || (eKind == PresObjKind::DateTime) || (eKind == PresObjKind::SlideNumber) ) + { + if( !bSubContentProcessing ) + { + // only draw a boundary for header&footer objects on the masterpage itself + bCreateOutline = true; + } + } + } + } + else if( ( pObject->GetObjInventor() == SdrInventor::Default ) && ( pObject->GetObjIdentifier() == SdrObjKind::Page ) ) + { + // only for handout page, else this frame will be created for each + // page preview object in SlideSorter and PagePane + if(pObjectsSdPage && PageKind::Handout == pObjectsSdPage->GetPageKind()) + { + bCreateOutline = true; + } + } + + if(bCreateOutline) + { + // empty presentation objects get a gray frame + const svtools::ColorConfig aColorConfig; + const svtools::ColorConfigValue aColor( aColorConfig.GetColorValue( svtools::OBJECTBOUNDARIES ) ); + + if( aColor.bIsVisible ) + { + // get basic object transformation + const basegfx::BColor aRGBColor(aColor.nColor.getBColor()); + basegfx::B2DHomMatrix aObjectMatrix; + basegfx::B2DPolyPolygon aObjectPolyPolygon; + pObject->TRGetBaseGeometry(aObjectMatrix, aObjectPolyPolygon); + + // create dashed border + { + // create object polygon + basegfx::B2DPolygon aPolygon(basegfx::utils::createUnitPolygon()); + aPolygon.transform(aObjectMatrix); + + // create line and stroke attribute + ::std::vector< double > aDotDashArray { 160.0, 80.0 }; + + const double fFullDotDashLen(::std::accumulate(aDotDashArray.begin(), aDotDashArray.end(), 0.0)); + const drawinglayer::attribute::LineAttribute aLine(aRGBColor); + const drawinglayer::attribute::StrokeAttribute aStroke(std::move(aDotDashArray), fFullDotDashLen); + + // create primitive and add + const drawinglayer::primitive2d::Primitive2DReference xRef(new drawinglayer::primitive2d::PolygonStrokePrimitive2D( + aPolygon, + aLine, + aStroke)); + rVisitor.visit(xRef); + } + + // now paint the placeholder description, but only when masterpage + // is displayed as page directly (MasterPage view) + if(!bSubContentProcessing && bIsMasterPageObject) + { + OUString aObjectString; + + switch( eKind ) + { + case PresObjKind::Title: + { + if(pObjectsSdPage && pObjectsSdPage->GetPageKind() == PageKind::Standard) + { + static OUString aTitleAreaStr(SdResId(STR_PLACEHOLDER_DESCRIPTION_TITLE)); + aObjectString = aTitleAreaStr; + } + + break; + } + case PresObjKind::Outline: + { + static OUString aOutlineAreaStr(SdResId(STR_PLACEHOLDER_DESCRIPTION_OUTLINE)); + aObjectString = aOutlineAreaStr; + break; + } + case PresObjKind::Footer: + { + static OUString aFooterAreaStr(SdResId(STR_PLACEHOLDER_DESCRIPTION_FOOTER)); + aObjectString = aFooterAreaStr; + break; + } + case PresObjKind::Header: + { + static OUString aHeaderAreaStr(SdResId(STR_PLACEHOLDER_DESCRIPTION_HEADER)); + aObjectString = aHeaderAreaStr; + break; + } + case PresObjKind::DateTime: + { + static OUString aDateTimeStr(SdResId(STR_PLACEHOLDER_DESCRIPTION_DATETIME)); + aObjectString = aDateTimeStr; + break; + } + case PresObjKind::Notes: + { + static OUString aDateTimeStr(SdResId(STR_PLACEHOLDER_DESCRIPTION_NOTES)); + aObjectString = aDateTimeStr; + break; + } + case PresObjKind::SlideNumber: + { + if(pObjectsSdPage && pObjectsSdPage->GetPageKind() == PageKind::Standard) + { + static OUString aSlideAreaStr(SdResId(STR_PLACEHOLDER_DESCRIPTION_SLIDE)); + aObjectString = aSlideAreaStr; + } + else + { + static OUString aNumberAreaStr(SdResId(STR_PLACEHOLDER_DESCRIPTION_NUMBER)); + aObjectString = aNumberAreaStr; + } + break; + } + default: + { + break; + } + } + + if( !aObjectString.isEmpty() ) + { + // decompose object matrix to be able to place text correctly + basegfx::B2DTuple aScale; + basegfx::B2DTuple aTranslate; + double fRotate, fShearX; + aObjectMatrix.decompose(aScale, aTranslate, fRotate, fShearX); + + // create font + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( pObject ); + const SdrTextVertAdjust eTVA(pTextObj ? pTextObj->GetTextVerticalAdjust() : SDRTEXTVERTADJUST_CENTER); + vcl::Font aScaledVclFont; + + // use a text size factor to get more reliable text sizes from the text layouter + // (and from vcl), tipp from HDU + static const sal_uInt32 nTextSizeFactor(100); + + // use a factor to get more linear text size calculations + aScaledVclFont.SetFontHeight( 500 * nTextSizeFactor ); + + // get basic geometry and get text size + drawinglayer::primitive2d::TextLayouterDevice aTextLayouter; + aTextLayouter.setFont(aScaledVclFont); + const sal_Int32 nTextLength(aObjectString.getLength()); + + // do not forget to use the factor again to get the width for the 500 + const double fTextWidth(aTextLayouter.getTextWidth(aObjectString, 0, nTextLength) * (1.0 / nTextSizeFactor)); + const double fTextHeight(aTextLayouter.getTextHeight() * (1.0 / nTextSizeFactor)); + + // calculate text primitive position. If text is at bottom, use top for + // the extra text and vice versa + const double fHorDist(125); + const double fVerDist(125); + const double fPosX((aTranslate.getX() + aScale.getX()) - fTextWidth - fHorDist); + const double fPosY((SDRTEXTVERTADJUST_BOTTOM == eTVA) + ? aTranslate.getY() - fVerDist + fTextHeight + : (aTranslate.getY() + aScale.getY()) - fVerDist); + + // get font attributes; use normally scaled font + vcl::Font aVclFont; + basegfx::B2DVector aTextSizeAttribute; + + aVclFont.SetFontHeight( 500 ); + + const drawinglayer::attribute::FontAttribute aFontAttribute( + drawinglayer::primitive2d::getFontAttributeFromVclFont( + aTextSizeAttribute, + aVclFont, + false, + false)); + + // fill text matrix + const basegfx::B2DHomMatrix aTextMatrix(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix( + aTextSizeAttribute.getX(), aTextSizeAttribute.getY(), + fShearX, + fRotate, + fPosX, fPosY)); + + // create DXTextArray (can be empty one) + ::std::vector< double > aDXArray{}; + + // create locale; this may need some more information in the future + const css::lang::Locale aLocale; + + // create primitive and add + const drawinglayer::primitive2d::Primitive2DReference xRef( + new drawinglayer::primitive2d::TextSimplePortionPrimitive2D( + aTextMatrix, + aObjectString, + 0, + nTextLength, + std::move(aDXArray), + aFontAttribute, + aLocale, + aRGBColor)); + rVisitor.visit(xRef); + } + } + } + } + } + + if(bDoCreateGeometry) + { + sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence( + rOriginal, + rDisplayInfo, rVisitor); + } +} + +namespace +{ + void setOutlinerBgFromPage(::Outliner& rOutl, SdrPageView& rPgView, bool bScreenDisplay) + { + SdPage* pPage = static_cast<SdPage*>(rPgView.GetPage()); + if (pPage) + { + // #i75566# Name change GetBackgroundColor -> GetPageBackgroundColor and + // hint value if screen display. Only then the AutoColor mechanisms shall be applied + rOutl.SetBackgroundColor(pPage->GetPageBackgroundColor(&rPgView, bScreenDisplay)); + } + } +} + +/** + * The event will be forwarded to the View + */ +void View::CompleteRedraw(OutputDevice* pOutDev, const vcl::Region& rReg, sdr::contact::ViewObjectContactRedirector* pRedirector /*=0*/) +{ + // execute ?? + if (mnLockRedrawSmph != 0) + return; + + SdrPageView* pPgView = GetSdrPageView(); + + if (pPgView) + { + SdPage* pPage = static_cast<SdPage*>( pPgView->GetPage() ); + if( pPage ) + { + SdrOutliner& rOutl = mrDoc.GetDrawOutliner(); + bool bScreenDisplay(true); + + // #i75566# printing; suppress AutoColor BackgroundColor generation + // for visibility reasons by giving GetPageBackgroundColor() + // the needed hint + // #i75566# PDF export; suppress AutoColor BackgroundColor generation (see printing) + if (pOutDev && ((OUTDEV_PRINTER == pOutDev->GetOutDevType()) + || (OUTDEV_PDF == pOutDev->GetOutDevType()))) + bScreenDisplay = false; + + setOutlinerBgFromPage(rOutl, *pPgView, bScreenDisplay); + } + } + + ViewRedirector aViewRedirector; + FmFormView::CompleteRedraw(pOutDev, rReg, pRedirector ? pRedirector : &aViewRedirector); +} + +void View::MarkListHasChanged() +{ + FmFormView::MarkListHasChanged(); + + if( GetMarkedObjectCount() > 0 ) + maSmartTags.deselect(); +} + +bool View::SetAttributes(const SfxItemSet& rSet, bool bReplaceAll, bool /*bSlide*/, bool /*bMaster*/) +{ + bool bOk = FmFormView::SetAttributes(rSet, bReplaceAll); + return bOk; +} + +void View::GetAttributes( SfxItemSet& rTargetSet, bool bOnlyHardAttr ) const +{ + FmFormView::GetAttributes( rTargetSet, bOnlyHardAttr ); +} + +/** + * Is a presentation object selected? + */ +bool View::IsPresObjSelected(bool bOnPage, bool bOnMasterPage, bool bCheckPresObjListOnly, bool bCheckLayoutOnly) const +{ + SdrMarkList* pMarkList; + + if (mnDragSrcPgNum != SDRPAGE_NOTFOUND && + mnDragSrcPgNum != GetSdrPageView()->GetPage()->GetPageNum()) + { + /* Drag&Drop is in progress + Source and destination page are different: + we use the saved mark list */ + pMarkList = mpDragSrcMarkList.get(); + } + else + { + // We use the current mark list + pMarkList = new SdrMarkList(GetMarkedObjectList()); + } + + SdrMark* pMark; + SdPage* pPage; + + bool bSelected = false; + bool bMasterPage = false; + + for (size_t nMark = pMarkList->GetMarkCount(); nMark && !bSelected; ) + { + --nMark; + // Backwards through mark list + pMark = pMarkList->GetMark(nMark); + SdrObject* pObj = pMark->GetMarkedSdrObj(); + + if ( pObj && ( bCheckPresObjListOnly || pObj->IsEmptyPresObj() || pObj->GetUserCall() ) ) + { + pPage = static_cast<SdPage*>( pObj->getSdrPageFromSdrObject() ); + bMasterPage = pPage && pPage->IsMasterPage(); + + if ( (bMasterPage && bOnMasterPage) || (!bMasterPage && bOnPage) ) + { + if ( pPage && pPage->IsPresObj(pObj) ) + { + if( bCheckLayoutOnly ) + { + PresObjKind eKind = pPage->GetPresObjKind(pObj); + + if((eKind != PresObjKind::Footer) && (eKind != PresObjKind::Header) && (eKind != PresObjKind::DateTime) && (eKind != PresObjKind::SlideNumber) ) + bSelected = true; + } + else + { + bSelected = true; + } + } + } + } + } + + if (pMarkList != mpDragSrcMarkList.get()) + { + delete pMarkList; + } + + return bSelected; +} + +void View::SelectAll() +{ + if ( IsTextEdit() ) + { + OutlinerView* pOLV = GetTextEditOutlinerView(); + const ::Outliner* pOutliner = GetTextEditOutliner(); + pOLV->SelectRange( 0, pOutliner->GetParagraphCount() ); + } + else + { + MarkAll(); + } +} + +bool View::SetStyleSheet(SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr) +{ + // forward to SdrView + FmFormView::SetStyleSheet(pStyleSheet, bDontRemoveHardAttr); + return true; +} + +/** + * Start text input + */ +static void SetSpellOptions( const SdDrawDocument& rDoc, EEControlBits& rCntrl ) +{ + bool bOnlineSpell = rDoc.GetOnlineSpell(); + + if( bOnlineSpell ) + rCntrl |= EEControlBits::ONLINESPELLING; + else + rCntrl &= ~EEControlBits::ONLINESPELLING; +} + +void OutlinerMasterViewFilter::Start(SdrOutliner *pOutl) +{ + m_pOutl = pOutl; + OutlinerView* pOutlView = m_pOutl->GetView(0); + m_bReadOnly = pOutlView->IsReadOnly(); + pOutlView->SetReadOnly(true); +} + +void OutlinerMasterViewFilter::End() +{ + if (m_pOutl) + { + OutlinerView* pOutlView = m_pOutl->GetView(0); + pOutlView->SetReadOnly(m_bReadOnly); + m_pOutl = nullptr; + } +} + +SfxViewShell* View::GetSfxViewShell() const +{ + SfxViewShell* pRet = nullptr; + + if (mpViewSh) + pRet = &mpViewSh->GetViewShellBase(); + + return pRet; +} + +// Create a new view-local UndoManager manager for Impress/Draw +std::unique_ptr<SdrUndoManager> View::createLocalTextUndoManager() +{ + std::unique_ptr<SdrUndoManager> pUndoManager(new sd::UndoManager); + pUndoManager->SetDocShell(mpDocSh); + return pUndoManager; +} + +bool View::SdrBeginTextEdit( + SdrObject* pObj, SdrPageView* pPV, vcl::Window* pWin, + bool bIsNewObj, + SdrOutliner* pOutl, OutlinerView* pGivenOutlinerView, + bool bDontDeleteOutliner, bool bOnlyOneView, bool bGrabFocus ) +{ + SdrPage* pPage = pObj ? pObj->getSdrPageFromSdrObject() : nullptr; + bool bMasterPage = pPage && pPage->IsMasterPage(); + + GetViewShell()->GetViewShellBase().GetEventMultiplexer()->MultiplexEvent( + EventMultiplexerEventId::BeginTextEdit, static_cast<void*>(pObj) ); + + if( pOutl==nullptr && pObj ) + pOutl = SdrMakeOutliner(OutlinerMode::TextObject, pObj->getSdrModelFromSdrObject()).release(); + + // make draw&impress specific initialisations + if( pOutl ) + { + pOutl->SetStyleSheetPool(static_cast<SfxStyleSheetPool*>( mrDoc.GetStyleSheetPool() )); + pOutl->SetCalcFieldValueHdl(LINK(SD_MOD(), SdModule, CalcFieldValueHdl)); + EEControlBits nCntrl = pOutl->GetControlWord(); + nCntrl |= EEControlBits::ALLOWBIGOBJS; + nCntrl |= EEControlBits::MARKFIELDS; + nCntrl |= EEControlBits::AUTOCORRECT; + + nCntrl &= ~EEControlBits::ULSPACESUMMATION; + if ( mrDoc.IsSummationOfParagraphs() ) + nCntrl |= EEControlBits::ULSPACESUMMATION; + + SetSpellOptions( mrDoc, nCntrl ); + + pOutl->SetControlWord(nCntrl); + + Reference< linguistic2::XSpellChecker1 > xSpellChecker( LinguMgr::GetSpellChecker() ); + if ( xSpellChecker.is() ) + pOutl->SetSpeller( xSpellChecker ); + + Reference< linguistic2::XHyphenator > xHyphenator( LinguMgr::GetHyphenator() ); + if( xHyphenator.is() ) + pOutl->SetHyphenator( xHyphenator ); + + pOutl->SetDefaultLanguage( Application::GetSettings().GetLanguageTag().getLanguageType() ); + } + + bool bReturn = FmFormView::SdrBeginTextEdit( + pObj, pPV, pWin, bIsNewObj, pOutl, + pGivenOutlinerView, bDontDeleteOutliner, + bOnlyOneView, bGrabFocus); + + if ( mpViewSh ) + { + mpViewSh->GetViewShellBase().GetDrawController().FireSelectionChangeListener(); + + if (pObj && pObj->GetObjIdentifier() == SdrObjKind::Table) + mpViewSh->UpdateScrollBars(); + + if (comphelper::LibreOfficeKit::isActive()) + { + if (OutlinerView* pView = GetTextEditOutlinerView()) + { + ::tools::Rectangle aRectangle = pView->GetOutputArea(); + if (pWin && pWin->GetMapMode().GetMapUnit() == MapUnit::Map100thMM) + { + aRectangle = o3tl::convert(aRectangle, o3tl::Length::mm100, o3tl::Length::twip); + } + OString sRectangle = aRectangle.toString(); + SfxLokHelper::notifyOtherViews(&mpViewSh->GetViewShellBase(), LOK_CALLBACK_VIEW_LOCK, "rectangle", sRectangle); + } + } + } + + if (::Outliner* pOL = bReturn ? GetTextEditOutliner() : nullptr) + { + if (pObj) + { + if( pObj->GetObjInventor() == SdrInventor::Default && pObj->GetObjIdentifier() == SdrObjKind::Table ) + { + Color aBackground = GetTextEditBackgroundColor(*this); + pOL->SetBackgroundColor( aBackground ); + } + else + { + // tdf#148140 Set the background to determine autocolor. + // Use any explicit bg with fallback to underlying page if + // none found + if (!pObj->setSuitableOutlinerBg(*pOL) && pPV) + setOutlinerBgFromPage(*pOL, *pPV, true); + } + } + + pOL->SetParaInsertedHdl(LINK(this, View, OnParagraphInsertedHdl)); + pOL->SetParaRemovingHdl(LINK(this, View, OnParagraphRemovingHdl)); + } + + if (bMasterPage && bReturn && pOutl) + { + const SdrTextObj* pTextObj = pOutl->GetTextObj(); + const SdPage* pSdPage = pTextObj ? static_cast<const SdPage*>(pTextObj->getSdrPageFromSdrObject()) : nullptr; + const PresObjKind eKind = pSdPage ? pSdPage->GetPresObjKind(const_cast<SdrTextObj*>(pTextObj)) : PresObjKind::NONE; + switch (eKind) + { + case PresObjKind::Title: + case PresObjKind::Outline: + case PresObjKind::Text: + maMasterViewFilter.Start(pOutl); + break; + default: + break; + } + } + + return bReturn; +} + +/** ends current text editing */ +SdrEndTextEditKind View::SdrEndTextEdit(bool bDontDeleteReally) +{ + maMasterViewFilter.End(); + + ::tools::WeakReference<SdrTextObj> xObj( GetTextEditObject() ); + + bool bDefaultTextRestored = RestoreDefaultText( xObj.get() ); + + SdrEndTextEditKind eKind = FmFormView::SdrEndTextEdit(bDontDeleteReally); + + if( bDefaultTextRestored ) + { + if( xObj.is() && !xObj->IsEmptyPresObj() ) + { + xObj->SetEmptyPresObj( true ); + } + else + { + eKind = SdrEndTextEditKind::Unchanged; + } + } + else if( xObj.is() && xObj->IsEmptyPresObj() ) + { + SdrTextObj* pObj = xObj.get(); + if( pObj && pObj->HasText() ) + { + SdrPage* pPage = pObj->getSdrPageFromSdrObject(); + if( !pPage || !pPage->IsMasterPage() ) + pObj->SetEmptyPresObj( false ); + } + } + + GetViewShell()->GetViewShellBase().GetEventMultiplexer()->MultiplexEvent( + EventMultiplexerEventId::EndTextEdit, + static_cast<void*>(xObj.get()) ); + + if( xObj.is() ) + { + if ( mpViewSh ) + { + mpViewSh->GetViewShellBase().GetDrawController().FireSelectionChangeListener(); + + if (comphelper::LibreOfficeKit::isActive()) + SfxLokHelper::notifyOtherViews(&mpViewSh->GetViewShellBase(), LOK_CALLBACK_VIEW_LOCK, "rectangle", "EMPTY"); + + } + + SdPage* pPage = dynamic_cast< SdPage* >( xObj->getSdrPageFromSdrObject() ); + if( pPage ) + pPage->onEndTextEdit( xObj.get() ); + } + + return eKind; +} + +/** restores the default text if the given text object is currently in edit mode and + no text has been entered already. Is only useful just before text edit ends. */ +bool View::RestoreDefaultText( SdrTextObj* pTextObj ) +{ + bool bRestored = false; + + if( pTextObj && (pTextObj == GetTextEditObject()) ) + { + if( !pTextObj->HasText() ) + { + SdPage* pPage = dynamic_cast< SdPage* >( pTextObj->getSdrPageFromSdrObject() ); + + if(pPage) + { + bRestored = pPage->RestoreDefaultText( pTextObj ); + if( bRestored ) + { + SdrOutliner* pOutliner = GetTextEditOutliner(); + pTextObj->SetTextEditOutliner( pOutliner ); + OutlinerParaObject* pParaObj = pTextObj->GetOutlinerParaObject(); + if (pOutliner) + pOutliner->SetText(*pParaObj); + } + } + } + } + + return bRestored; +} + +/** + * Sets the original size of the marked objects. + */ +void View::SetMarkedOriginalSize() +{ + std::unique_ptr<SdrUndoGroup> pUndoGroup(new SdrUndoGroup(mrDoc)); + const size_t nCount = GetMarkedObjectCount(); + bool bOK = false; + + for( size_t i = 0; i < nCount; ++i ) + { + SdrObject* pObj = GetMarkedObjectByIndex(i); + + if( pObj->GetObjInventor() == SdrInventor::Default ) + { + if( pObj->GetObjIdentifier() == SdrObjKind::OLE2 ) + { + uno::Reference < embed::XEmbeddedObject > xObj = static_cast<SdrOle2Obj*>(pObj)->GetObjRef(); + if( xObj.is() ) + { + // TODO/LEAN: working with VisualArea can switch object to running state + + sal_Int64 nAspect = static_cast<SdrOle2Obj*>(pObj)->GetAspect(); + Size aOleSize; + + if ( nAspect == embed::Aspects::MSOLE_ICON ) + { + MapMode aMap100( MapUnit::Map100thMM ); + aOleSize = static_cast<SdrOle2Obj*>(pObj)->GetOrigObjSize( &aMap100 ); + bOK = true; + } + else + { + MapUnit aUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( nAspect ) ); + try + { + awt::Size aSz = xObj->getVisualAreaSize( nAspect ); + aOleSize = OutputDevice::LogicToLogic(Size(aSz.Width, aSz.Height), MapMode(aUnit), MapMode(MapUnit::Map100thMM)); + bOK = true; + } + catch( embed::NoVisualAreaSizeException& ) + {} + } + + if ( bOK ) + { + ::tools::Rectangle aDrawRect( pObj->GetLogicRect() ); + + pUndoGroup->AddAction( mrDoc.GetSdrUndoFactory().CreateUndoGeoObject( *pObj ) ); + pObj->Resize( aDrawRect.TopLeft(), Fraction( aOleSize.Width(), aDrawRect.GetWidth() ), + Fraction( aOleSize.Height(), aDrawRect.GetHeight() ) ); + } + } + } + else if( pObj->GetObjIdentifier() == SdrObjKind::Graphic ) + { + const SdrGrafObj* pSdrGrafObj = static_cast< const SdrGrafObj* >(pObj); + const Size aSize = pSdrGrafObj->getOriginalSize( ); + pUndoGroup->AddAction( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj ) ); + ::tools::Rectangle aRect( pObj->GetLogicRect() ); + aRect.SetSize( aSize ); + pObj->SetLogicRect( aRect ); + bOK = true; + } + } + } + + if( bOK ) + { + pUndoGroup->SetComment(SdResId(STR_UNDO_ORIGINALSIZE)); + mpDocSh->GetUndoManager()->AddUndoAction(std::move(pUndoGroup)); + } +} + +/** + * Connect OLE object to client. + */ +void View::DoConnect(SdrOle2Obj* pObj) +{ + if (!mpViewSh) + return; + + uno::Reference < embed::XEmbeddedObject > xObj( pObj->GetObjRef() ); + if( !xObj.is() ) + return; + + ::sd::Window* pWindow = mpViewSh->GetActiveWindow(); + SfxInPlaceClient* pSdClient = mpViewSh-> GetViewShellBase().FindIPClient( xObj, pWindow ); + if ( pSdClient ) + return; + + pSdClient = new Client(pObj, mpViewSh, pWindow); + ::tools::Rectangle aRect = pObj->GetLogicRect(); + { + // TODO/LEAN: working with visual area can switch object to running state + Size aDrawSize = aRect.GetSize(); + + MapMode aMapMode( mrDoc.GetScaleUnit() ); + Size aObjAreaSize = pObj->GetOrigObjSize( &aMapMode ); + + Fraction aScaleWidth (aDrawSize.Width(), aObjAreaSize.Width() ); + Fraction aScaleHeight(aDrawSize.Height(), aObjAreaSize.Height() ); + aScaleWidth.ReduceInaccurate(10); // compatible to SdrOle2Obj + aScaleHeight.ReduceInaccurate(10); + pSdClient->SetSizeScale(aScaleWidth, aScaleHeight); + + // visible area is only changed in-place! + // the object area must be set after the scaling, since it triggers resize + aRect.SetSize(aObjAreaSize); + pSdClient->SetObjArea(aRect); + } +} + +bool View::IsMorphingAllowed() const +{ + const SdrMarkList& rMarkList = GetMarkedObjectList(); + bool bRet = false; + + if ( rMarkList.GetMarkCount() == 2 ) + { + const SdrObject* pObj1 = rMarkList.GetMark( 0 )->GetMarkedSdrObj(); + const SdrObject* pObj2 = rMarkList.GetMark( 1 )->GetMarkedSdrObj(); + const SdrObjKind nKind1 = pObj1->GetObjIdentifier(); + const SdrObjKind nKind2 = pObj2->GetObjIdentifier(); + + if ( ( nKind1 != SdrObjKind::Text && nKind2 != SdrObjKind::Text ) && + ( nKind1 != SdrObjKind::TitleText && nKind2 != SdrObjKind::TitleText ) && + ( nKind1 != SdrObjKind::OutlineText && nKind2 != SdrObjKind::OutlineText ) && + ( nKind1 != SdrObjKind::Group && nKind2 != SdrObjKind::Group ) && + ( nKind1 != SdrObjKind::Line && nKind2 != SdrObjKind::Line ) && + ( nKind1 != SdrObjKind::PolyLine && nKind2 != SdrObjKind::PolyLine ) && + ( nKind1 != SdrObjKind::PathLine && nKind2 != SdrObjKind::PathLine ) && + ( nKind1 != SdrObjKind::FreehandLine && nKind2 != SdrObjKind::FreehandLine ) && + ( nKind1 != SdrObjKind::PathPolyLine && nKind2 != SdrObjKind::PathPolyLine ) && + ( nKind1 != SdrObjKind::Measure && nKind2 != SdrObjKind::Measure ) && + ( nKind1 != SdrObjKind::Edge && nKind2 != SdrObjKind::Edge ) && + ( nKind1 != SdrObjKind::Graphic && nKind2 != SdrObjKind::Graphic ) && + ( nKind1 != SdrObjKind::OLE2 && nKind2 != SdrObjKind::OLE2 ) && + ( nKind1 != SdrObjKind::Caption && nKind2 != SdrObjKind::Caption ) && + dynamic_cast< const E3dObject *>( pObj1 ) == nullptr && dynamic_cast< const E3dObject *>( pObj2 ) == nullptr ) + { + SfxItemSetFixed<XATTR_FILLSTYLE, XATTR_FILLSTYLE> aSet1( mrDoc.GetPool() ); + SfxItemSetFixed<XATTR_FILLSTYLE, XATTR_FILLSTYLE> aSet2( mrDoc.GetPool() ); + + aSet1.Put(pObj1->GetMergedItemSet()); + aSet2.Put(pObj2->GetMergedItemSet()); + + const drawing::FillStyle eFillStyle1 = aSet1.Get( XATTR_FILLSTYLE ).GetValue(); + const drawing::FillStyle eFillStyle2 = aSet2.Get( XATTR_FILLSTYLE ).GetValue(); + + if( ( eFillStyle1 == drawing::FillStyle_NONE || eFillStyle1 == drawing::FillStyle_SOLID ) && + ( eFillStyle2 == drawing::FillStyle_NONE || eFillStyle2 == drawing::FillStyle_SOLID ) ) + bRet = true; + } + } + + return bRet; +} + +bool View::IsVectorizeAllowed() const +{ + const SdrMarkList& rMarkList = GetMarkedObjectList(); + bool bRet = false; + + if( rMarkList.GetMarkCount() == 1 ) + { + const SdrGrafObj* pObj = dynamic_cast< const SdrGrafObj* >(rMarkList.GetMark( 0 )->GetMarkedSdrObj()); + + if(pObj) + { + if(GraphicType::Bitmap == pObj->GetGraphicType() && !pObj->isEmbeddedVectorGraphicData()) + { + bRet = true; + } + } + } + + return bRet; +} + +void View::onAccessibilityOptionsChanged() +{ + if( !mpViewSh ) + return; + + ::sd::Window* pWindow = mpViewSh->GetActiveWindow(); + if( !pWindow ) + return; + + const StyleSettings& rStyleSettings = pWindow->GetSettings().GetStyleSettings(); + + if( mpViewSh->GetViewFrame() && mpViewSh->GetViewFrame()->GetDispatcher() ) + { + sal_uInt16 nOutputSlot, nPreviewSlot; + + if( rStyleSettings.GetHighContrastMode() ) + { + nOutputSlot = SID_OUTPUT_QUALITY_CONTRAST; + } + else + { + nOutputSlot = SID_OUTPUT_QUALITY_COLOR; + } + + if( rStyleSettings.GetHighContrastMode() + && officecfg::Office::Common::Accessibility::IsForPagePreviews::get() ) + { + nPreviewSlot = SID_PREVIEW_QUALITY_CONTRAST; + } + else + { + nPreviewSlot = SID_PREVIEW_QUALITY_COLOR; + } + + mpViewSh->GetViewFrame()->GetDispatcher()->Execute( nOutputSlot, SfxCallMode::ASYNCHRON ); + mpViewSh->GetViewFrame()->GetDispatcher()->Execute( nPreviewSlot, SfxCallMode::ASYNCHRON ); + } + + mpViewSh->Invalidate(); +} + +IMPL_LINK( View, OnParagraphInsertedHdl, ::Outliner::ParagraphHdlParam, aParam, void ) +{ + SdrObject* pObj = GetTextEditObject(); + + if( aParam.pPara && pObj ) + { + SdPage* pPage = dynamic_cast< SdPage* >( pObj->getSdrPageFromSdrObject() ); + if( pPage ) + pPage->onParagraphInserted( aParam.pOutliner, aParam.pPara, pObj ); + } +} + +/** + * Handler for the deletion of the pages (paragraphs). + */ +IMPL_LINK( View, OnParagraphRemovingHdl, ::Outliner::ParagraphHdlParam, aParam, void ) +{ + SdrObject* pObj = GetTextEditObject(); + + if( aParam.pPara && pObj ) + { + SdPage* pPage = dynamic_cast< SdPage* >( pObj->getSdrPageFromSdrObject() ); + if( pPage ) + pPage->onParagraphRemoving( aParam.pOutliner, aParam.pPara, pObj ); + } +} + +bool View::isRecordingUndo() const +{ + if( mrDoc.IsUndoEnabled() ) + { + sd::UndoManager* pUndoManager = mrDoc.GetUndoManager(); + return pUndoManager && pUndoManager->IsInListAction(); + } + else + { + return false; + } +} + +void View::AddCustomHdl() +{ + maSmartTags.addCustomHandles( maHdlList ); +} + +void View::updateHandles() +{ + AdjustMarkHdl(); +} + +SdrViewContext View::GetContext() const +{ + SdrViewContext eContext = SdrViewContext::Standard; + if( maSmartTags.getContext( eContext ) ) + return eContext; + else + return FmFormView::GetContext(); +} + +bool View::HasMarkablePoints() const +{ + if( maSmartTags.HasMarkablePoints() ) + return true; + else + return FmFormView::HasMarkablePoints(); +} + +sal_Int32 View::GetMarkablePointCount() const +{ + sal_Int32 nCount = FmFormView::GetMarkablePointCount(); + nCount += maSmartTags.GetMarkablePointCount(); + return nCount; +} + +bool View::HasMarkedPoints() const +{ + if( maSmartTags.HasMarkedPoints() ) + return true; + else + return FmFormView::HasMarkedPoints(); +} + +bool View::MarkPoint(SdrHdl& rHdl, bool bUnmark ) +{ + if( maSmartTags.MarkPoint( rHdl, bUnmark ) ) + return true; + else + return FmFormView::MarkPoint( rHdl, bUnmark ); +} + +bool View::MarkPoints(const ::tools::Rectangle* pRect, bool bUnmark) +{ + if( maSmartTags.MarkPoints( pRect, bUnmark ) ) + return true; + else + return FmFormView::MarkPoints( pRect, bUnmark ); +} + +void View::CheckPossibilities() +{ + FmFormView::CheckPossibilities(); + maSmartTags.CheckPossibilities(); +} + +void View::OnBeginPasteOrDrop( PasteOrDropInfos* pInfo ) +{ + SdrOutliner* pOutliner = GetTextEditOutliner(); + if (!pOutliner) + return; + + // Turn character attributes of the paragraph of the insert position into + // character-level attributes, so they are not lost when OnEndPasteOrDrop() + // sets the paragraph stylesheet. + SfxItemSet aSet(pOutliner->GetParaAttribs(pInfo->nStartPara)); + pOutliner->SetCharAttribs(pInfo->nStartPara, aSet); +} + +/** this is called after a paste or drop operation, make sure that the newly inserted paragraphs + get the correct style sheet. */ +void View::OnEndPasteOrDrop( PasteOrDropInfos* pInfo ) +{ + /* Style Sheet handling */ + SdrTextObj* pTextObj = GetTextEditObject(); + SdrOutliner* pOutliner = GetTextEditOutliner(); + if( !pOutliner || !pTextObj || !pTextObj->getSdrPageFromSdrObject() ) + return; + + SdPage* pPage = static_cast< SdPage* >( pTextObj->getSdrPageFromSdrObject() ); + const PresObjKind eKind = pPage->GetPresObjKind(pTextObj); + + // outline kinds are taken care of in Outliner::ImplSetLevelDependentStyleSheet + if( eKind == PresObjKind::Outline ) + return; + + SfxStyleSheet* pStyleSheet = nullptr; + if( eKind != PresObjKind::NONE ) + pStyleSheet = pPage->GetStyleSheetForPresObj(eKind); + else + pStyleSheet = pTextObj->GetStyleSheet(); + // just put the object style on each new paragraph + for ( sal_Int32 nPara = pInfo->nStartPara; nPara <= pInfo->nEndPara; nPara++ ) + { + pOutliner->SetStyleSheet( nPara, pStyleSheet ); + } +} + +bool View::ShouldToggleOn( + const bool bBulletOnOffMode, + const bool bNormalBullet) +{ + // If setting bullets/numbering by the dialog, always should toggle on. + if (!bBulletOnOffMode) + return true; + SdrModel* pSdrModel = GetModel(); + if (!pSdrModel) + return false; + + bool bToggleOn = false; + std::unique_ptr<SdrOutliner> pOutliner(SdrMakeOutliner(OutlinerMode::TextObject, *pSdrModel)); + const size_t nMarkCount = GetMarkedObjectCount(); + for (size_t nIndex = 0; nIndex < nMarkCount && !bToggleOn; ++nIndex) + { + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >(GetMarkedObjectByIndex(nIndex)); + if (!pTextObj || pTextObj->IsTextEditActive()) + continue; + if( dynamic_cast< const SdrTableObj *>( pTextObj ) != nullptr) + { + SdrTableObj* pTableObj = dynamic_cast< SdrTableObj* >(pTextObj); + if (!pTableObj) + continue; + CellPos aStart, aEnd; + SvxTableController* pTableController = dynamic_cast< SvxTableController* >(getSelectionController().get()); + if (pTableController) + { + pTableController->getSelectedCells(aStart, aEnd); + } + else + { + aStart = SdrTableObj::getFirstCell(); + aEnd = pTableObj->getLastCell(); + } + sal_Int32 nColCount = pTableObj->getColumnCount(); + for (sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow && !bToggleOn; nRow++) + { + for (sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol && !bToggleOn; nCol++) + { + sal_Int32 nCellIndex = nRow * nColCount + nCol; + SdrText* pText = pTableObj->getText(nCellIndex); + if (!pText || !pText->GetOutlinerParaObject()) + continue; + pOutliner->SetText(*(pText->GetOutlinerParaObject())); + sal_Int16 nStatus = pOutliner->GetBulletsNumberingStatus(); + bToggleOn = (bNormalBullet && nStatus != 0) || (!bNormalBullet && nStatus != 1); + pOutliner->Clear(); + } + } + } + else + { + OutlinerParaObject* pParaObj = pTextObj->GetOutlinerParaObject(); + if (!pParaObj) + continue; + pOutliner->SetText(*pParaObj); + sal_Int16 nStatus = pOutliner->GetBulletsNumberingStatus(); + bToggleOn = (bNormalBullet && nStatus != 0) || (!bNormalBullet && nStatus != 1); + pOutliner->Clear(); + } + } + return bToggleOn; +} + +void View::ChangeMarkedObjectsBulletsNumbering( + const bool bToggle, + const bool bHandleBullets, + const SvxNumRule* pNumRule ) +{ + SdrModel* pSdrModel = GetModel(); + OutputDevice* pOut = GetFirstOutputDevice(); + vcl::Window* pWindow = pOut ? pOut->GetOwnerWindow() : nullptr; + if (!pSdrModel || !pWindow) + return; + + const bool bUndoEnabled = pSdrModel->IsUndoEnabled(); + std::unique_ptr<SdrUndoGroup> pUndoGroup(bUndoEnabled ? new SdrUndoGroup(*pSdrModel) : nullptr); + + const bool bToggleOn = ShouldToggleOn( bToggle, bHandleBullets ); + + std::unique_ptr<SdrOutliner> pOutliner(SdrMakeOutliner(OutlinerMode::TextObject, *pSdrModel)); + OutlinerView aOutlinerView(pOutliner.get(), pWindow); + + const size_t nMarkCount = GetMarkedObjectCount(); + for (size_t nIndex = 0; nIndex < nMarkCount; ++nIndex) + { + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >(GetMarkedObjectByIndex(nIndex)); + if (!pTextObj || pTextObj->IsTextEditActive()) + continue; + if( dynamic_cast< SdrTableObj *>( pTextObj ) != nullptr) + { + SdrTableObj* pTableObj = dynamic_cast< SdrTableObj* >(pTextObj); + if (!pTableObj) + continue; + CellPos aStart, aEnd; + SvxTableController* pTableController = dynamic_cast< SvxTableController* >(getSelectionController().get()); + if (pTableController) + { + pTableController->getSelectedCells(aStart, aEnd); + } + else + { + aStart = SdrTableObj::getFirstCell(); + aEnd = pTableObj->getLastCell(); + } + sal_Int32 nColCount = pTableObj->getColumnCount(); + for (sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++) + { + for (sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++) + { + sal_Int32 nCellIndex = nRow * nColCount + nCol; + SdrText* pText = pTableObj->getText(nCellIndex); + if (!pText || !pText->GetOutlinerParaObject()) + continue; + + pOutliner->SetText(*(pText->GetOutlinerParaObject())); + if (bUndoEnabled) + { + pUndoGroup->AddAction(pSdrModel->GetSdrUndoFactory().CreateUndoObjectSetText(*pTextObj, nCellIndex)); + } + if ( !bToggleOn ) + { + aOutlinerView.SwitchOffBulletsNumbering(); + } + else + { + aOutlinerView.ApplyBulletsNumbering( bHandleBullets, pNumRule, bToggle ); + } + sal_uInt32 nParaCount = pOutliner->GetParagraphCount(); + pText->SetOutlinerParaObject(pOutliner->CreateParaObject(0, static_cast<sal_uInt16>(nParaCount))); + pOutliner->Clear(); + } + } + // Broadcast the object change event. + if (!pTextObj->AdjustTextFrameWidthAndHeight()) + { + pTextObj->SetChanged(); + pTextObj->BroadcastObjectChange(); + } + } + else + { + OutlinerParaObject* pParaObj = pTextObj->GetOutlinerParaObject(); + if (!pParaObj) + continue; + pOutliner->SetText(*pParaObj); + if (bUndoEnabled) + { + pUndoGroup->AddAction( + pSdrModel->GetSdrUndoFactory().CreateUndoObjectSetText(*pTextObj, 0)); + } + if ( !bToggleOn ) + { + aOutlinerView.SwitchOffBulletsNumbering(); + } + else + { + aOutlinerView.ApplyBulletsNumbering( bHandleBullets, pNumRule, bToggle ); + } + sal_uInt32 nParaCount = pOutliner->GetParagraphCount(); + pTextObj->SetOutlinerParaObject(pOutliner->CreateParaObject(0, static_cast<sal_uInt16>(nParaCount))); + pOutliner->Clear(); + } + } + + if ( bUndoEnabled && pUndoGroup->GetActionCount() > 0 ) + { + pSdrModel->BegUndo(); + pSdrModel->AddUndo(std::move(pUndoGroup)); + pSdrModel->EndUndo(); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/sdview2.cxx b/sd/source/ui/view/sdview2.cxx new file mode 100644 index 000000000..a5b3d4413 --- /dev/null +++ b/sd/source/ui/view/sdview2.cxx @@ -0,0 +1,908 @@ +/* -*- 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 <View.hxx> + +#include <vector> +#include <com/sun/star/embed/XEmbedPersist.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <comphelper/sequenceashashmap.hxx> +#include <tools/urlobj.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svxdlg.hxx> +#include <sfx2/docfile.hxx> +#include <svx/svdundo.hxx> +#include <svx/svdpagv.hxx> +#include <svl/urlbmk.hxx> +#include <editeng/outliner.hxx> +#include <svx/xflclit.hxx> +#include <sot/formats.hxx> +#include <editeng/editeng.hxx> + +#include <svtools/embedtransfer.hxx> +#include <tools/debug.hxx> + +#include <anminfo.hxx> +#include <strings.hrc> +#include <sdxfer.hxx> +#include <sdresid.hxx> +#include <sdmod.hxx> +#include <sdtreelb.hxx> +#include <DrawViewShell.hxx> +#include <DrawDocShell.hxx> +#include <fudraw.hxx> +#include <drawdoc.hxx> +#include <Window.hxx> +#include <sdpage.hxx> +#include <unoaprms.hxx> +#include <helpids.h> +#include <vcl/svapp.hxx> + +#include <slideshow.hxx> +#include <memory> + +namespace sd { + +using namespace ::com::sun::star; + +namespace { + +struct SdNavigatorDropEvent : public ExecuteDropEvent +{ + VclPtr< ::sd::Window> mpTargetWindow; + + SdNavigatorDropEvent ( + const ExecuteDropEvent& rEvt, + ::sd::Window* pTargetWindow ) + : ExecuteDropEvent( rEvt ), + mpTargetWindow( pTargetWindow ) + {} +}; + +} + +css::uno::Reference< css::datatransfer::XTransferable > View::CreateClipboardDataObject() +{ + // since SdTransferable::CopyToClipboard is called, this + // dynamically created object is destroyed automatically + rtl::Reference<SdTransferable> pTransferable = new SdTransferable( &mrDoc, nullptr, false ); + + SD_MOD()->pTransferClip = pTransferable.get(); + + mrDoc.CreatingDataObj( pTransferable.get() ); + pTransferable->SetWorkDocument( static_cast<SdDrawDocument*>(CreateMarkedObjModel().release()) ); + mrDoc.CreatingDataObj( nullptr ); + + // #112978# need to use GetAllMarkedBoundRect instead of GetAllMarkedRect to get + // fat lines correctly + const ::tools::Rectangle aMarkRect( GetAllMarkedBoundRect() ); + std::unique_ptr<TransferableObjectDescriptor> pObjDesc(new TransferableObjectDescriptor); + SdrOle2Obj* pSdrOleObj = nullptr; + SdrPageView* pPgView = GetSdrPageView(); + SdPage* pOldPage = pPgView ? static_cast<SdPage*>( pPgView->GetPage() ) : nullptr; + SdPage* pNewPage = const_cast<SdPage*>(static_cast<const SdPage*>( pTransferable->GetWorkDocument()->GetPage( 0 ) )); + + if( pOldPage ) + { + pNewPage->SetSize( pOldPage->GetSize() ); + pNewPage->SetLayoutName( pOldPage->GetLayoutName() ); + } + + if( GetMarkedObjectCount() == 1 ) + { + SdrObject* pObj = GetMarkedObjectByIndex(0); + + if( auto pOle2Obj = dynamic_cast<SdrOle2Obj *>( pObj ) ) + if( pOle2Obj->GetObjRef() ) + { + // If object has no persistence it must be copied as part of the document + try + { + uno::Reference< embed::XEmbedPersist > xPersObj( pOle2Obj->GetObjRef(), uno::UNO_QUERY ); + if ( xPersObj.is() && xPersObj->hasEntry() ) + pSdrOleObj = pOle2Obj; + } + catch( uno::Exception& ) + {} + } + } + + if( pSdrOleObj ) + SvEmbedTransferHelper::FillTransferableObjectDescriptor( *pObjDesc, pSdrOleObj->GetObjRef(), pSdrOleObj->GetGraphic(), pSdrOleObj->GetAspect() ); + else + pTransferable->GetWorkDocument()->GetDocSh()->FillTransferableObjectDescriptor( *pObjDesc ); + + if( mpDocSh ) + pObjDesc->maDisplayName = mpDocSh->GetMedium()->GetURLObject().GetURLNoPass(); + + pObjDesc->maSize = aMarkRect.GetSize(); + + pTransferable->SetStartPos( aMarkRect.TopLeft() ); + pTransferable->SetObjectDescriptor( std::move(pObjDesc) ); + pTransferable->CopyToClipboard( mpViewSh->GetActiveWindow() ); + + return pTransferable; +} + +css::uno::Reference< css::datatransfer::XTransferable > View::CreateDragDataObject( View* pWorkView, vcl::Window& rWindow, const Point& rDragPos ) +{ + rtl::Reference<SdTransferable> pTransferable = new SdTransferable( &mrDoc, pWorkView, false ); + + SD_MOD()->pTransferDrag = pTransferable.get(); + + std::unique_ptr<TransferableObjectDescriptor> pObjDesc(new TransferableObjectDescriptor); + OUString aDisplayName; + SdrOle2Obj* pSdrOleObj = nullptr; + + if( GetMarkedObjectCount() == 1 ) + { + SdrObject* pObj = GetMarkedObjectByIndex( 0 ); + + if( auto pOle2Obj = dynamic_cast<SdrOle2Obj *>( pObj ) ) + if( pOle2Obj->GetObjRef() ) + { + // If object has no persistence it must be copied as part of the document + try + { + uno::Reference< embed::XEmbedPersist > xPersObj( pOle2Obj->GetObjRef(), uno::UNO_QUERY ); + if ( xPersObj.is() && xPersObj->hasEntry() ) + pSdrOleObj = pOle2Obj; + } + catch( uno::Exception& ) + {} + } + } + + if( mpDocSh ) + aDisplayName = mpDocSh->GetMedium()->GetURLObject().GetURLNoPass(); + + if( pSdrOleObj ) + SvEmbedTransferHelper::FillTransferableObjectDescriptor( *pObjDesc, pSdrOleObj->GetObjRef(), pSdrOleObj->GetGraphic(), pSdrOleObj->GetAspect() ); + else if (mpDocSh) + mpDocSh->FillTransferableObjectDescriptor( *pObjDesc ); + + pObjDesc->maSize = GetAllMarkedRect().GetSize(); + pObjDesc->maDragStartPos = rDragPos; + pObjDesc->maDisplayName = aDisplayName; + + pTransferable->SetStartPos( rDragPos ); + pTransferable->SetObjectDescriptor( std::move(pObjDesc) ); + pTransferable->StartDrag( &rWindow, DND_ACTION_COPYMOVE | DND_ACTION_LINK ); + + return pTransferable; +} + +css::uno::Reference< css::datatransfer::XTransferable > View::CreateSelectionDataObject( View* pWorkView ) +{ + rtl::Reference<SdTransferable> pTransferable = new SdTransferable( &mrDoc, pWorkView, true ); + std::unique_ptr<TransferableObjectDescriptor> pObjDesc(new TransferableObjectDescriptor); + const ::tools::Rectangle aMarkRect( GetAllMarkedRect() ); + + SD_MOD()->pTransferSelection = pTransferable.get(); + + if( mpDocSh ) + { + mpDocSh->FillTransferableObjectDescriptor( *pObjDesc ); + pObjDesc->maDisplayName = mpDocSh->GetMedium()->GetURLObject().GetURLNoPass(); + } + + pObjDesc->maSize = aMarkRect.GetSize(); + + pTransferable->SetStartPos( aMarkRect.TopLeft() ); + pTransferable->SetObjectDescriptor( std::move(pObjDesc) ); + pTransferable->CopyToPrimarySelection(); + + return pTransferable; +} + +void View::UpdateSelectionClipboard() // false case +{ + if (!mpViewSh) + return; + if (!mpViewSh->GetActiveWindow()) + return; + if (GetMarkedObjectList().GetMarkCount()) + CreateSelectionDataObject( this ); + else + ClearSelectionClipboard(); +} + +void View::ClearSelectionClipboard() // true case +{ + if (!mpViewSh) + return; + if (!mpViewSh->GetActiveWindow()) + return; + if (SD_MOD()->pTransferSelection && SD_MOD()->pTransferSelection->GetView() == this) + { + TransferableHelper::ClearPrimarySelection(); + SD_MOD()->pTransferSelection = nullptr; + } +} + +void View::DoCut() +{ + const OutlinerView* pOLV = GetTextEditOutlinerView(); + + if( pOLV ) + const_cast<OutlinerView*>(pOLV)->Cut(); + else if( AreObjectsMarked() ) + { + OUString aStr(SdResId(STR_UNDO_CUT)); + + DoCopy(); + BegUndo(aStr + " " + GetDescriptionOfMarkedObjects()); + DeleteMarked(); + EndUndo(); + } +} + +void View::DoCopy() +{ + const OutlinerView* pOLV = GetTextEditOutlinerView(); + + if( pOLV ) + const_cast<OutlinerView*>(pOLV)->Copy(); + else if( AreObjectsMarked() ) + { + BrkAction(); + CreateClipboardDataObject(); + } +} + +void View::DoPaste (::sd::Window* pWindow) +{ + TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( mpViewSh->GetActiveWindow() ) ); + if( !aDataHelper.GetTransferable().is() ) + return; // empty clipboard? + + const OutlinerView* pOLV = GetTextEditOutlinerView(); + + if( pOLV && EditEngine::HasValidData( aDataHelper.GetTransferable() ) ) + { + const_cast< OutlinerView* >(pOLV)->PasteSpecial(); + + SdrObject* pObj = GetTextEditObject(); + SdPage* pPage = static_cast<SdPage*>( pObj ? pObj->getSdrPageFromSdrObject() : nullptr ); + ::Outliner* pOutliner = pOLV->GetOutliner(); + + if( pOutliner) + { + if( pObj && pPage && pPage->GetPresObjKind(pObj) == PresObjKind::Title ) + { + // remove all hard linebreaks from the title + if (pOutliner->GetParagraphCount() > 1) + { + bool bOldUpdateMode = pOutliner->SetUpdateLayout( false ); + + const EditEngine& rEdit = pOutliner->GetEditEngine(); + const sal_Int32 nParaCount = rEdit.GetParagraphCount(); + + for( sal_Int32 nPara = nParaCount - 2; nPara >= 0; nPara-- ) + { + const sal_Int32 nParaLen = rEdit.GetTextLen( nPara ); + pOutliner->QuickDelete( ESelection( nPara, nParaLen, nPara+1, 0 ) ); + pOutliner->QuickInsertLineBreak( ESelection( nPara, nParaLen, nPara, nParaLen ) ); + } + + DBG_ASSERT( rEdit.GetParagraphCount() <= 1, "Titleobject contains hard line breaks" ); + pOutliner->SetUpdateLayout(bOldUpdateMode); + } + } + + if( !mrDoc.IsChanged() ) + { + if (pOutliner->IsModified()) + mrDoc.SetChanged(); + } + } + } + else + { + Point aPos = pWindow->GetVisibleCenter(); + DrawViewShell* pDrViewSh = static_cast<DrawViewShell*>( mpDocSh->GetViewShell() ); + + if (pDrViewSh != nullptr) + { + sal_Int8 nDnDAction = DND_ACTION_COPY; + if( !InsertData( aDataHelper, aPos, nDnDAction, false ) ) + { + INetBookmark aINetBookmark( "", "" ); + + if( ( aDataHelper.HasFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ) && + aDataHelper.GetINetBookmark( SotClipboardFormatId::NETSCAPE_BOOKMARK, aINetBookmark ) ) || + ( aDataHelper.HasFormat( SotClipboardFormatId::FILEGRPDESCRIPTOR ) && + aDataHelper.GetINetBookmark( SotClipboardFormatId::FILEGRPDESCRIPTOR, aINetBookmark ) ) || + ( aDataHelper.HasFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) && + aDataHelper.GetINetBookmark( SotClipboardFormatId::UNIFORMRESOURCELOCATOR, aINetBookmark ) ) ) + { + pDrViewSh->InsertURLField( aINetBookmark.GetURL(), aINetBookmark.GetDescription(), "" ); + } + } + } + } +} + +void View::StartDrag( const Point& rStartPos, vcl::Window* pWindow ) +{ + if (!AreObjectsMarked() || !IsAction() || !mpViewSh || !pWindow) + return; + + BrkAction(); + + if( IsTextEdit() ) + SdrEndTextEdit(); + + if (DrawViewShell* pDrawViewShell = dynamic_cast<DrawViewShell*>(mpDocSh ? mpDocSh->GetViewShell() : nullptr)) + { + const rtl::Reference<FuPoor>& xFunction(pDrawViewShell->GetCurrentFunction()); + if (FuDraw* pFunction = dynamic_cast<FuDraw*>(xFunction.get())) + pFunction->ForcePointer(); + } + + mpDragSrcMarkList.reset( new SdrMarkList(GetMarkedObjectList()) ); + mnDragSrcPgNum = GetSdrPageView()->GetPage()->GetPageNum(); + + CreateDragDataObject( this, *pWindow, rStartPos ); +} + +void View::DragFinished( sal_Int8 nDropAction ) +{ + const bool bUndo = IsUndoEnabled(); + const bool bGroupUndo = bUndo && mpDragSrcMarkList; + if (bGroupUndo) + { + OUString aStr(SdResId(STR_UNDO_DRAGDROP)); + BegUndo(aStr + " " + mpDragSrcMarkList->GetMarkDescription()); + } + + SdTransferable* pDragTransferable = SD_MOD()->pTransferDrag; + + if( pDragTransferable ) + pDragTransferable->SetView( nullptr ); + + if( ( nDropAction & DND_ACTION_MOVE ) && + pDragTransferable && !pDragTransferable->IsInternalMove() && + mpDragSrcMarkList && mpDragSrcMarkList->GetMarkCount() && + !IsPresObjSelected() ) + { + mpDragSrcMarkList->ForceSort(); + + if( bUndo ) + BegUndo(); + + const size_t nCnt = mpDragSrcMarkList->GetMarkCount(); + + for( size_t nm = nCnt; nm>0; ) + { + --nm; + SdrMark* pM=mpDragSrcMarkList->GetMark(nm); + if( bUndo ) + AddUndo(mrDoc.GetSdrUndoFactory().CreateUndoDeleteObject(*pM->GetMarkedSdrObj())); + } + + mpDragSrcMarkList->GetMark(0)->GetMarkedSdrObj()->GetOrdNum(); + + for (size_t nm = nCnt; nm>0;) + { + --nm; + SdrMark* pM=mpDragSrcMarkList->GetMark(nm); + SdrObject* pObj=pM->GetMarkedSdrObj(); + + if( pObj && pObj->getSdrPageFromSdrObject() ) + { + const size_t nOrdNum = pObj->GetOrdNumDirect(); + SdrObject* pChkObj = pObj->getSdrPageFromSdrObject()->RemoveObject(nOrdNum); + DBG_ASSERT(pChkObj==pObj,"pChkObj!=pObj in RemoveObject()"); + } + } + + if( bUndo ) + EndUndo(); + } + + if( pDragTransferable ) + pDragTransferable->SetInternalMove( false ); + + if (bGroupUndo) + EndUndo(); + mnDragSrcPgNum = SDRPAGE_NOTFOUND; + mpDragSrcMarkList.reset(); +} + +sal_Int8 View::AcceptDrop( const AcceptDropEvent& rEvt, DropTargetHelper& rTargetHelper, + SdrLayerID nLayer ) +{ + OUString aLayerName = GetActiveLayer(); + SdrPageView* pPV = GetSdrPageView(); + sal_Int8 nDropAction = rEvt.mnAction; + sal_Int8 nRet = DND_ACTION_NONE; + + if( nLayer != SDRLAYER_NOTFOUND ) + { + SdrLayerAdmin& rLayerAdmin = mrDoc.GetLayerAdmin(); + aLayerName = rLayerAdmin.GetLayerPerID(nLayer)->GetName(); + } + + if( mbIsDropAllowed && !pPV->IsLayerLocked( aLayerName ) && pPV->IsLayerVisible( aLayerName ) ) + { + const OutlinerView* pOLV = GetTextEditOutlinerView(); + bool bIsInsideOutlinerView = false; + + if( pOLV ) + { + ::tools::Rectangle aRect( pOLV->GetOutputArea() ); + + if (GetMarkedObjectCount() == 1) + { + SdrMark* pMark = GetSdrMarkByIndex(0); + SdrObject* pObj = pMark->GetMarkedSdrObj(); + aRect.Union( pObj->GetLogicRect() ); + } + + if( aRect.Contains( pOLV->GetWindow()->PixelToLogic( rEvt.maPosPixel ) ) ) + { + bIsInsideOutlinerView = true; + } + } + + if( !bIsInsideOutlinerView ) + { + SdTransferable* pDragTransferable = SD_MOD()->pTransferDrag; + + if(pDragTransferable && (nDropAction & DND_ACTION_LINK)) + { + // suppress own data when it's intention is to use it as fill information + pDragTransferable = nullptr; + } + + if( pDragTransferable ) + { + const View* pSourceView = pDragTransferable->GetView(); + + if( pDragTransferable->IsPageTransferable() ) + { + nRet = DND_ACTION_COPY; + } + else if( pSourceView ) + { + if( !( nDropAction & DND_ACTION_LINK ) || + !pSourceView->GetDocSh()->GetMedium()->GetName().isEmpty() ) + { + nRet = nDropAction; + } + } + } + else + { + const bool bDrawing = rTargetHelper.IsDropFormatSupported( SotClipboardFormatId::DRAWING ); + const bool bGraphic = rTargetHelper.IsDropFormatSupported( SotClipboardFormatId::SVXB ); + const bool bMtf = rTargetHelper.IsDropFormatSupported( SotClipboardFormatId::GDIMETAFILE ); + const bool bBitmap = rTargetHelper.IsDropFormatSupported( SotClipboardFormatId::BITMAP ); + bool bBookmark = rTargetHelper.IsDropFormatSupported( SotClipboardFormatId::NETSCAPE_BOOKMARK ); + bool bXFillExchange = rTargetHelper.IsDropFormatSupported( SotClipboardFormatId::XFA ); + + // check handle insert + if ((bXFillExchange && (SdrDragMode::Gradient == GetDragMode())) + || (SdrDragMode::Transparence == GetDragMode())) + { + const SdrHdlList& rHdlList = GetHdlList(); + + for( size_t n = 0; n < rHdlList.GetHdlCount(); ++n ) + { + SdrHdl* pIAOHandle = rHdlList.GetHdl( n ); + + if( pIAOHandle && ( SdrHdlKind::Color == pIAOHandle->GetKind() ) ) + { + if(pIAOHandle->getOverlayObjectList().isHitPixel(rEvt.maPosPixel)) + { + nRet = nDropAction; + static_cast< SdrHdlColor* >( pIAOHandle )->SetSize( SDR_HANDLE_COLOR_SIZE_SELECTED ); + } + else + { + static_cast< SdrHdlColor* >( pIAOHandle )->SetSize( SDR_HANDLE_COLOR_SIZE_NORMAL ); + } + } + } + } + + // check object insert + if( !nRet && ( bXFillExchange || ( ( bDrawing || bGraphic || bMtf || bBitmap || bBookmark ) && ( nDropAction & DND_ACTION_LINK ) ) ) ) + { + SdrPageView* pPageView = nullptr; + ::sd::Window* pWindow = mpViewSh->GetActiveWindow(); + Point aPos( pWindow->PixelToLogic( rEvt.maPosPixel ) ); + SdrObject* pPickObj = PickObj(aPos, getHitTolLog(), pPageView); + bool bIsPresTarget = false; + + if (pPickObj && (pPickObj->IsEmptyPresObj() || pPickObj->GetUserCall())) + { + SdPage* pPage = static_cast<SdPage*>( pPickObj->getSdrPageFromSdrObject() ); + + if( pPage && pPage->IsMasterPage() ) + bIsPresTarget = pPage->IsPresObj( pPickObj ); + } + + if (pPickObj && !bIsPresTarget && (bGraphic || bMtf || bBitmap || bXFillExchange)) + { + if( mpDropMarkerObj != pPickObj ) + { + mpDropMarkerObj = pPickObj; + ImplClearDrawDropMarker(); + + if(mpDropMarkerObj) + { + mpDropMarker.reset( new SdrDropMarkerOverlay(*this, *mpDropMarkerObj) ); + } + } + + nRet = nDropAction; + } + else + bXFillExchange = false; + } + + // check normal insert + if( !nRet ) + { + const bool bSBAFormat = rTargetHelper.IsDropFormatSupported( SotClipboardFormatId::SVX_FORMFIELDEXCH ); + const bool bEditEngineODF = rTargetHelper.IsDropFormatSupported( SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT ); + const bool bString = rTargetHelper.IsDropFormatSupported( SotClipboardFormatId::STRING ); + const bool bRTF = rTargetHelper.IsDropFormatSupported( SotClipboardFormatId::RTF ); + const bool bFile = rTargetHelper.IsDropFormatSupported( SotClipboardFormatId::SIMPLE_FILE ); + const bool bFileList = rTargetHelper.IsDropFormatSupported( SotClipboardFormatId::FILE_LIST ); + + if( mpDropMarker ) + { + ImplClearDrawDropMarker(); + mpDropMarkerObj = nullptr; + } + + if( bBookmark && bFile && ( nDropAction & DND_ACTION_MOVE ) && mpViewSh && SlideShow::IsRunning(mpViewSh->GetViewShellBase()) ) + bBookmark = false; + + if( bDrawing || bGraphic || bMtf || bBitmap || bBookmark || bFile || bFileList || bXFillExchange || bSBAFormat || bEditEngineODF || bString || bRTF ) + nRet = nDropAction; + + // For entries from the navigator, change action copy. + if (bBookmark + && rTargetHelper.IsDropFormatSupported( + SdPageObjsTLV::SdPageObjsTransferable::GetListBoxDropFormatId()) + && (nDropAction & DND_ACTION_MOVE)!=0) + { + nRet = DND_ACTION_COPY; + } + } + } + } + } + + // destroy drop marker if this is a leaving event + if( rEvt.mbLeaving && mpDropMarker ) + { + ImplClearDrawDropMarker(); + mpDropMarkerObj = nullptr; + } + + return nRet; +} + +sal_Int8 View::ExecuteDrop( const ExecuteDropEvent& rEvt, + ::sd::Window* pTargetWindow, sal_uInt16 nPage, SdrLayerID nLayer ) +{ + SdrPageView* pPV = GetSdrPageView(); + OUString aActiveLayer = GetActiveLayer(); + sal_Int8 nDropAction = rEvt.mnAction; + sal_Int8 nRet = DND_ACTION_NONE; + + // destroy drop marker if it is shown + if( mpDropMarker ) + { + ImplClearDrawDropMarker(); + mpDropMarkerObj = nullptr; + } + + if( !pPV->IsLayerLocked( aActiveLayer ) ) + { + const OutlinerView* pOLV = GetTextEditOutlinerView(); + bool bIsInsideOutlinerView = false; + + if( pOLV ) + { + ::tools::Rectangle aRect( pOLV->GetOutputArea() ); + + if( GetMarkedObjectCount() == 1 ) + { + SdrMark* pMark = GetSdrMarkByIndex(0); + SdrObject* pObj = pMark->GetMarkedSdrObj(); + aRect.Union( pObj->GetLogicRect() ); + } + + Point aPos( pOLV->GetWindow()->PixelToLogic( rEvt.maPosPixel ) ); + + if( aRect.Contains( aPos ) ) + { + bIsInsideOutlinerView = true; + } + } + + if( !bIsInsideOutlinerView ) + { + Point aPos; + TransferableDataHelper aDataHelper( rEvt.maDropEvent.Transferable ); + + if( pTargetWindow ) + aPos = pTargetWindow->PixelToLogic( rEvt.maPosPixel ); + + // handle insert? + if ((SdrDragMode::Gradient == GetDragMode()) + || ((SdrDragMode::Transparence == GetDragMode()) + && aDataHelper.HasFormat(SotClipboardFormatId::XFA))) + { + const SdrHdlList& rHdlList = GetHdlList(); + + for( size_t n = 0; !nRet && n < rHdlList.GetHdlCount(); ++n ) + { + SdrHdl* pIAOHandle = rHdlList.GetHdl( n ); + + if( pIAOHandle && ( SdrHdlKind::Color == pIAOHandle->GetKind() ) ) + { + if(pIAOHandle->getOverlayObjectList().isHitPixel(rEvt.maPosPixel)) + { + uno::Any const data(aDataHelper.GetAny(SotClipboardFormatId::XFA, "")); + uno::Sequence<beans::NamedValue> props; + if (data >>= props) + { + ::comphelper::SequenceAsHashMap const map(props); + Color aColor(COL_BLACK); + auto const it = map.find("FillColor"); + if (it != map.end()) + { + XFillColorItem color; + color.PutValue(it->second, 0); + aColor = color.GetColorValue(); + } + static_cast< SdrHdlColor* >( pIAOHandle )->SetColor( aColor, true ); + nRet = nDropAction; + } + } + } + } + } + + // standard insert? + if( !nRet && InsertData( aDataHelper, aPos, nDropAction, true, SotClipboardFormatId::NONE, nPage, nLayer ) ) + nRet = nDropAction; + + // special insert? + if( !nRet && mpViewSh ) + { + INetBookmark aINetBookmark( (OUString()), (OUString()) ); + + // insert bookmark + if( aDataHelper.HasFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ) && + aDataHelper.GetINetBookmark( SotClipboardFormatId::NETSCAPE_BOOKMARK, aINetBookmark ) ) + { + SdPageObjsTLV::SdPageObjsTransferable* pPageObjsTransferable = SdPageObjsTLV::SdPageObjsTransferable::getImplementation( aDataHelper.GetXTransferable() ); + + if( pPageObjsTransferable && + ( NAVIGATOR_DRAGTYPE_LINK == pPageObjsTransferable->GetDragType() || + NAVIGATOR_DRAGTYPE_EMBEDDED == pPageObjsTransferable->GetDragType() ) ) + { + // insert bookmark from own navigator (handled async. due to possible message box ) + Application::PostUserEvent( LINK( this, View, ExecuteNavigatorDrop ), + new SdNavigatorDropEvent( rEvt, pTargetWindow ) ); + nRet = nDropAction; + } + else + { + SdrPageView* pPageView = nullptr; + + SdrObject* pPickObj = PickObj(aPos, getHitTolLog(), pPageView); + if (pPickObj) + { + // insert as clip action => jump + OUString aBookmark( aINetBookmark.GetURL() ); + SdAnimationInfo* pInfo = SdDrawDocument::GetAnimationInfo( pPickObj ); + + if( !aBookmark.isEmpty() ) + { + bool bCreated = false; + + presentation::ClickAction eClickAction = presentation::ClickAction_DOCUMENT; + + sal_Int32 nIndex = aBookmark.indexOf( '#' ); + if( nIndex != -1 ) + { + const std::u16string_view aDocName( aBookmark.subView( 0, nIndex ) ); + + if (mpDocSh->GetMedium()->GetName() == aDocName || aDocName == mpDocSh->GetName()) + { + // internal jump, only use the part after and including '#' + eClickAction = presentation::ClickAction_BOOKMARK; + aBookmark = aBookmark.copy( nIndex+1 ); + } + } + + if( !pInfo ) + { + pInfo = SdDrawDocument::GetShapeUserData( *pPickObj, true ); + bCreated = true; + } + + // create undo action with old and new sizes + std::unique_ptr<SdAnimationPrmsUndoAction> pAction(new SdAnimationPrmsUndoAction(&mrDoc, pPickObj, bCreated)); + pAction->SetActive(pInfo->mbActive, pInfo->mbActive); + pAction->SetEffect(pInfo->meEffect, pInfo->meEffect); + pAction->SetTextEffect(pInfo->meTextEffect, pInfo->meTextEffect); + pAction->SetSpeed(pInfo->meSpeed, pInfo->meSpeed); + pAction->SetDim(pInfo->mbDimPrevious, pInfo->mbDimPrevious); + pAction->SetDimColor(pInfo->maDimColor, pInfo->maDimColor); + pAction->SetDimHide(pInfo->mbDimHide, pInfo->mbDimHide); + pAction->SetSoundOn(pInfo->mbSoundOn, pInfo->mbSoundOn); + pAction->SetSound(pInfo->maSoundFile, pInfo->maSoundFile); + pAction->SetPlayFull(pInfo->mbPlayFull, pInfo->mbPlayFull); + pAction->SetClickAction(pInfo->meClickAction, eClickAction); + pAction->SetBookmark(pInfo->GetBookmark(), aBookmark); + pAction->SetVerb(pInfo->mnVerb, pInfo->mnVerb); + pAction->SetSecondEffect(pInfo->meSecondEffect, pInfo->meSecondEffect); + pAction->SetSecondSpeed(pInfo->meSecondSpeed, pInfo->meSecondSpeed); + pAction->SetSecondSoundOn(pInfo->mbSecondSoundOn, pInfo->mbSecondSoundOn); + pAction->SetSecondPlayFull(pInfo->mbSecondPlayFull, pInfo->mbSecondPlayFull); + + OUString aString(SdResId(STR_UNDO_ANIMATION)); + pAction->SetComment(aString); + mpDocSh->GetUndoManager()->AddUndoAction(std::move(pAction)); + pInfo->meClickAction = eClickAction; + pInfo->SetBookmark( aBookmark ); + mrDoc.SetChanged(); + + nRet = nDropAction; + } + } + else if( auto pDrawViewShell = dynamic_cast< DrawViewShell *>( mpViewSh ) ) + { + // insert as normal URL button + pDrawViewShell->InsertURLButton( aINetBookmark.GetURL(), aINetBookmark.GetDescription(), OUString(), &aPos ); + nRet = nDropAction; + } + } + } + } + } + } + + return nRet; +} + +IMPL_LINK( View, ExecuteNavigatorDrop, void*, p, void ) +{ + SdNavigatorDropEvent* pSdNavigatorDropEvent = static_cast<SdNavigatorDropEvent*>(p); + TransferableDataHelper aDataHelper( pSdNavigatorDropEvent->maDropEvent.Transferable ); + SdPageObjsTLV::SdPageObjsTransferable* pPageObjsTransferable = SdPageObjsTLV::SdPageObjsTransferable::getImplementation( aDataHelper.GetXTransferable() ); + INetBookmark aINetBookmark; + + if( pPageObjsTransferable && aDataHelper.GetINetBookmark( SotClipboardFormatId::NETSCAPE_BOOKMARK, aINetBookmark ) ) + { + Point aPos; + OUString aBookmark; + SdPage* pPage = static_cast<SdPage*>( GetSdrPageView()->GetPage() ); + sal_uInt16 nPgPos = 0xFFFF; + + if( pSdNavigatorDropEvent->mpTargetWindow ) + aPos = pSdNavigatorDropEvent->mpTargetWindow->PixelToLogic( pSdNavigatorDropEvent->maPosPixel ); + + const OUString& aURL( aINetBookmark.GetURL() ); + sal_Int32 nIndex = aURL.indexOf( '#' ); + if( nIndex != -1 ) + aBookmark = aURL.copy( nIndex+1 ); + + std::vector<OUString> aExchangeList; + std::vector<OUString> aBookmarkList(1,aBookmark); + + if( !pPage->IsMasterPage() ) + { + if( pPage->GetPageKind() == PageKind::Standard ) + nPgPos = pPage->GetPageNum() + 2; + else if( pPage->GetPageKind() == PageKind::Notes ) + nPgPos = pPage->GetPageNum() + 1; + } + + /* In order t ensure unique page names, we test the ones we want to + insert. If necessary. we put them into and replacement list (bNameOK + == sal_False -> User canceled). */ + bool bLink = pPageObjsTransferable->GetDragType() == NAVIGATOR_DRAGTYPE_LINK; + bool bNameOK = GetExchangeList( aExchangeList, aBookmarkList, 2 ); + + /* Since we don't know the type (page or object), we fill a list with + pages and objects. + Of course we have problems if there are pages and objects with the + same name!!! */ + if( bNameOK ) + { + mrDoc.InsertBookmark( aBookmarkList, aExchangeList, + bLink, nPgPos, + &pPageObjsTransferable->GetDocShell(), + &aPos ); + } + } + + delete pSdNavigatorDropEvent; +} + +bool View::GetExchangeList (std::vector<OUString> &rExchangeList, + std::vector<OUString> &rBookmarkList, + const sal_uInt16 nType) +{ + assert(rExchangeList.empty()); + + bool bListIdentical = true; ///< Bookmark list and exchange list are identical + bool bNameOK = true; ///< name is unique + + for ( const auto& rBookmark : rBookmarkList ) + { + OUString aNewName = rBookmark; + + if( nType == 0 || nType == 2 ) + bNameOK = mpDocSh->CheckPageName(mpViewSh->GetFrameWeld(), aNewName); + + if( bNameOK && ( nType == 1 || nType == 2 ) ) + { + if( mrDoc.GetObj( aNewName ) ) + { + OUString aTitle(SdResId(STR_TITLE_NAMEGROUP)); + OUString aDesc(SdResId(STR_DESC_NAMEGROUP)); + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSvxNameDialog> pDlg(pFact->CreateSvxNameDialog(mpViewSh->GetFrameWeld(), aNewName, aDesc)); + + pDlg->SetEditHelpId( HID_SD_NAMEDIALOG_OBJECT ); + + bNameOK = false; + pDlg->SetText( aTitle ); + + while( !bNameOK && pDlg->Execute() == RET_OK ) + { + pDlg->GetName( aNewName ); + + if( !mrDoc.GetObj( aNewName ) ) + bNameOK = true; + } + } + } + + bListIdentical = rBookmark == aNewName; + + rExchangeList.push_back(aNewName); + + if (!bNameOK) + break; + } + + // Exchange list is identical to bookmark list + if( !rExchangeList.empty() && bListIdentical ) + rExchangeList.clear(); + + return bNameOK; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/sdview3.cxx b/sd/source/ui/view/sdview3.cxx new file mode 100644 index 000000000..b72e837c4 --- /dev/null +++ b/sd/source/ui/view/sdview3.cxx @@ -0,0 +1,1596 @@ +/* -*- 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 <View.hxx> +#include <com/sun/star/embed/XEmbedObjectClipboardCreator.hpp> +#include <com/sun/star/embed/NoVisualAreaSizeException.hpp> +#include <com/sun/star/embed/MSOLEObjectSystemCreator.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <sot/filelist.hxx> +#include <editeng/editdata.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xflclit.hxx> +#include <svx/xlnclit.hxx> +#include <svx/svdpagv.hxx> +#include <sfx2/docfile.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdograf.hxx> +#include <svx/svdundo.hxx> +#include <svl/itempool.hxx> +#include <sot/formats.hxx> +#include <editeng/outliner.hxx> +#include <svx/obj3d.hxx> +#include <svx/e3dundo.hxx> +#include <svx/unomodel.hxx> +#include <svx/ImageMapInfo.hxx> +#include <unotools/streamwrap.hxx> +#include <vcl/graph.hxx> +#include <vcl/metaact.hxx> +#include <vcl/pdfread.hxx> +#include <vcl/TypeSerializer.hxx> +#include <svx/svxids.hrc> +#include <toolkit/helper/vclunohelper.hxx> +#include <svtools/embedhlp.hxx> +#include <osl/diagnose.h> +#include <DrawDocShell.hxx> +#include <fupoor.hxx> +#include <tablefunction.hxx> +#include <Window.hxx> +#include <sdxfer.hxx> +#include <sdpage.hxx> +#include <drawdoc.hxx> +#include <sdmod.hxx> +#include <sdresid.hxx> +#include <strings.hrc> +#include <SlideSorterViewShell.hxx> +#include <unomodel.hxx> +#include <ViewClipboard.hxx> +#include <sfx2/ipclient.hxx> +#include <sfx2/classificationhelper.hxx> +#include <comphelper/sequenceashashmap.hxx> +#include <comphelper/storagehelper.hxx> +#include <comphelper/processfactory.hxx> +#include <svx/sdrhittesthelper.hxx> +#include <svx/xbtmpit.hxx> +#include <memory> + + +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::datatransfer; +using namespace ::com::sun::star::datatransfer::clipboard; + +namespace sd { + +#define CHECK_FORMAT_TRANS( _def_Type ) ( ( nFormat == (_def_Type) || nFormat == SotClipboardFormatId::NONE ) && aDataHelper.HasFormat( _def_Type ) ) + +/************************************************************************* +|* +|* Paste +|* +\************************************************************************/ + +namespace { + +struct ImpRememberOrigAndClone +{ + SdrObject* pOrig; + SdrObject* pClone; +}; + +} + +static SdrObject* ImpGetClone(std::vector<ImpRememberOrigAndClone>& aConnectorContainer, SdrObject const * pConnObj) +{ + for(const ImpRememberOrigAndClone& rImp : aConnectorContainer) + { + if(pConnObj == rImp.pOrig) + return rImp.pClone; + } + return nullptr; +} + +// restrict movement to WorkArea +static void ImpCheckInsertPos(Point& rPos, const Size& rSize, const ::tools::Rectangle& rWorkArea) +{ + if(rWorkArea.IsEmpty()) + return; + + ::tools::Rectangle aMarkRect(Point(rPos.X() - (rSize.Width() / 2), rPos.Y() - (rSize.Height() / 2)), rSize); + + if(aMarkRect.Contains(rWorkArea)) + return; + + if(aMarkRect.Left() < rWorkArea.Left()) + { + rPos.AdjustX(rWorkArea.Left() - aMarkRect.Left() ); + } + + if(aMarkRect.Right() > rWorkArea.Right()) + { + rPos.AdjustX( -(aMarkRect.Right() - rWorkArea.Right()) ); + } + + if(aMarkRect.Top() < rWorkArea.Top()) + { + rPos.AdjustY(rWorkArea.Top() - aMarkRect.Top() ); + } + + if(aMarkRect.Bottom() > rWorkArea.Bottom()) + { + rPos.AdjustY( -(aMarkRect.Bottom() - rWorkArea.Bottom()) ); + } +} + +bool View::InsertMetaFile( const TransferableDataHelper& rDataHelper, const Point& rPos, ImageMap const * pImageMap, bool bOptimize ) +{ + GDIMetaFile aMtf; + + if( !rDataHelper.GetGDIMetaFile( SotClipboardFormatId::GDIMETAFILE, aMtf ) ) + return false; + + bool bVector = false; + Graphic aGraphic; + + // check if metafile only contains a pixel image, if so insert a bitmap instead + if( bOptimize ) + { + MetaAction* pAction = aMtf.FirstAction(); + while( pAction && !bVector ) + { + switch( pAction->GetType() ) + { + case MetaActionType::POINT: + case MetaActionType::LINE: + case MetaActionType::RECT: + case MetaActionType::ROUNDRECT: + case MetaActionType::ELLIPSE: + case MetaActionType::ARC: + case MetaActionType::PIE: + case MetaActionType::CHORD: + case MetaActionType::POLYLINE: + case MetaActionType::POLYGON: + case MetaActionType::POLYPOLYGON: + case MetaActionType::TEXT: + case MetaActionType::TEXTARRAY: + case MetaActionType::STRETCHTEXT: + case MetaActionType::TEXTRECT: + case MetaActionType::GRADIENT: + case MetaActionType::HATCH: + case MetaActionType::WALLPAPER: + case MetaActionType::EPS: + case MetaActionType::TEXTLINE: + case MetaActionType::FLOATTRANSPARENT: + case MetaActionType::GRADIENTEX: + case MetaActionType::BMPSCALEPART: + case MetaActionType::BMPEXSCALEPART: + bVector = true; + break; + case MetaActionType::BMP: + case MetaActionType::BMPSCALE: + case MetaActionType::BMPEX: + case MetaActionType::BMPEXSCALE: + if( aGraphic.GetType() != GraphicType::NONE ) + { + bVector = true; + } + else switch( pAction->GetType() ) + { + case MetaActionType::BMP: + { + MetaBmpAction* pBmpAction = dynamic_cast< MetaBmpAction* >( pAction ); + if( pBmpAction ) + aGraphic = Graphic(BitmapEx(pBmpAction->GetBitmap())); + } + break; + case MetaActionType::BMPSCALE: + { + MetaBmpScaleAction* pBmpScaleAction = dynamic_cast< MetaBmpScaleAction* >( pAction ); + if( pBmpScaleAction ) + aGraphic = Graphic(BitmapEx(pBmpScaleAction->GetBitmap())); + } + break; + case MetaActionType::BMPEX: + { + MetaBmpExAction* pBmpExAction = dynamic_cast< MetaBmpExAction* >( pAction ); + if( pBmpExAction ) + aGraphic = Graphic(pBmpExAction->GetBitmapEx() ); + } + break; + case MetaActionType::BMPEXSCALE: + { + MetaBmpExScaleAction* pBmpExScaleAction = dynamic_cast< MetaBmpExScaleAction* >( pAction ); + if( pBmpExScaleAction ) + aGraphic = Graphic( pBmpExScaleAction->GetBitmapEx() ); + } + break; + default: break; + } + break; + default: break; + } + + pAction = aMtf.NextAction(); + } + } + + // it is not a vector metafile but it also has no graphic? + if( !bVector && (aGraphic.GetType() == GraphicType::NONE) ) + bVector = true; + + // restrict movement to WorkArea + Point aInsertPos( rPos ); + Size aImageSize = bVector ? aMtf.GetPrefSize() : aGraphic.GetSizePixel(); + ImpCheckInsertPos(aInsertPos, aImageSize, GetWorkArea()); + + if( bVector ) + aGraphic = Graphic( aMtf ); + + aGraphic.SetPrefMapMode( aMtf.GetPrefMapMode() ); + aGraphic.SetPrefSize( aMtf.GetPrefSize() ); + InsertGraphic( aGraphic, mnAction, aInsertPos, nullptr, pImageMap ); + + return true; +} + +bool View::InsertData( const TransferableDataHelper& rDataHelper, + const Point& rPos, sal_Int8& rDnDAction, bool bDrag, + SotClipboardFormatId nFormat, sal_uInt16 nPage, SdrLayerID nLayer ) +{ + maDropPos = rPos; + mnAction = rDnDAction; + mbIsDropAllowed = false; + + TransferableDataHelper aDataHelper( rDataHelper ); + SdrObject* pPickObj = nullptr; + SdPage* pPage = nullptr; + std::unique_ptr<ImageMap> pImageMap; + bool bReturn = false; + bool bLink = ( ( mnAction & DND_ACTION_LINK ) != 0 ); + bool bCopy = ( ( ( mnAction & DND_ACTION_COPY ) != 0 ) || bLink ); + SdrInsertFlags nPasteOptions = SdrInsertFlags::SETDEFLAYER; + + if (mpViewSh != nullptr) + { + OSL_ASSERT (mpViewSh->GetViewShell()!=nullptr); + SfxInPlaceClient* pIpClient = mpViewSh->GetViewShell()->GetIPClient(); + if( dynamic_cast< ::sd::slidesorter::SlideSorterViewShell *>( mpViewSh ) != nullptr + || (pIpClient!=nullptr && pIpClient->IsObjectInPlaceActive())) + nPasteOptions |= SdrInsertFlags::DONTMARK; + } + + if( bDrag ) + { + SdrPageView* pPV = nullptr; + pPickObj = PickObj(rPos, getHitTolLog(), pPV); + } + + if( nPage != SDRPAGE_NOTFOUND ) + pPage = static_cast<SdPage*>( mrDoc.GetPage( nPage ) ); + + SdTransferable* pOwnData = nullptr; + SdTransferable* pImplementation = SdTransferable::getImplementation( aDataHelper.GetTransferable() ); + + if(pImplementation && (rDnDAction & DND_ACTION_LINK)) + { + // suppress own data when it's intention is to use it as fill information + pImplementation = nullptr; + } + + bool bSelfDND = false; + + // try to get own transfer data + if( pImplementation ) + { + if( SD_MOD()->pTransferClip == pImplementation ) + pOwnData = SD_MOD()->pTransferClip; + else if( SD_MOD()->pTransferDrag == pImplementation ) + { + pOwnData = SD_MOD()->pTransferDrag; + bSelfDND = true; + } + else if( SD_MOD()->pTransferSelection == pImplementation ) + pOwnData = SD_MOD()->pTransferSelection; + } + + const bool bGroupUndoFromDragWithDrop = bSelfDND && mpDragSrcMarkList && IsUndoEnabled(); + if (bGroupUndoFromDragWithDrop) + { + OUString aStr(SdResId(STR_UNDO_DRAGDROP)); + BegUndo(aStr + " " + mpDragSrcMarkList->GetMarkDescription()); + } + + // ImageMap? + if( !pOwnData && aDataHelper.HasFormat( SotClipboardFormatId::SVIM ) ) + { + ::tools::SvRef<SotTempStream> xStm; + + if( aDataHelper.GetSotStorageStream( SotClipboardFormatId::SVIM, xStm ) ) + { + pImageMap.reset(new ImageMap); + // mba: clipboard always must contain absolute URLs (could be from alien source) + pImageMap->Read( *xStm ); + } + } + + bool bTable = false; + // check special cases for pasting table formats as RTL + if( !bLink && (nFormat == SotClipboardFormatId::NONE || (nFormat == SotClipboardFormatId::RTF) || (nFormat == SotClipboardFormatId::RICHTEXT)) ) + { + // if the object supports rtf and there is a table involved, default is to create a table + bool bIsRTF = aDataHelper.HasFormat( SotClipboardFormatId::RTF ); + if( ( bIsRTF || aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) ) + && ! aDataHelper.HasFormat( SotClipboardFormatId::DRAWING ) ) + { + ::tools::SvRef<SotTempStream> xStm; + + if( aDataHelper.GetSotStorageStream( bIsRTF ? SotClipboardFormatId::RTF : SotClipboardFormatId::RICHTEXT, xStm ) ) + { + xStm->Seek( 0 ); + + OStringBuffer aLine; + while (xStm->ReadLine(aLine)) + { + size_t x = std::string_view(aLine).find( "\\trowd" ); + if (x != std::string_view::npos) + { + bTable = true; + nFormat = bIsRTF ? SotClipboardFormatId::RTF : SotClipboardFormatId::RICHTEXT; + break; + } + } + } + } + } + + // Changed the whole decision tree to be dependent of bReturn as a flag that + // the work was done; this allows to check multiple formats and not just fail + // when a CHECK_FORMAT_TRANS(*format*) detected format does not work. This is + // e.g. necessary for SotClipboardFormatId::BITMAP + + if (!bReturn && pOwnData) + { + // Paste only if SfxClassificationHelper recommends so. + const SfxObjectShellRef& pSource = pOwnData->GetDocShell(); + SfxObjectShell* pDestination = mrDoc.GetDocSh(); + if (pSource.is() && pDestination) + { + SfxClassificationCheckPasteResult eResult = SfxClassificationHelper::CheckPaste(pSource->getDocProperties(), pDestination->getDocProperties()); + if (!SfxClassificationHelper::ShowPasteInfo(eResult)) + bReturn = true; + } + } + + if( !bReturn && pOwnData && nFormat == SotClipboardFormatId::NONE ) + { + const View* pSourceView = pOwnData->GetView(); + + if( pOwnData->GetDocShell().is() && pOwnData->IsPageTransferable() ) + { + mpClipboard->HandlePageDrop (*pOwnData); + bReturn = true; + } + else if( pSourceView ) + { + if( pSourceView == this ) + { + // same view + if( nLayer != SDRLAYER_NOTFOUND ) + { + // drop on layer tab bar + SdrLayerAdmin& rLayerAdmin = mrDoc.GetLayerAdmin(); + SdrLayer* pLayer = rLayerAdmin.GetLayerPerID( nLayer ); + SdrPageView* pPV = GetSdrPageView(); + OUString aLayer = pLayer->GetName(); + + if( !pPV->IsLayerLocked( aLayer ) ) + { + pOwnData->SetInternalMove( true ); + SortMarkedObjects(); + + for( size_t nM = 0; nM < GetMarkedObjectCount(); ++nM ) + { + SdrMark* pM = GetSdrMarkByIndex( nM ); + SdrObject* pO = pM->GetMarkedSdrObj(); + + if( pO ) + { + // #i11702# + if( IsUndoEnabled() ) + { + BegUndo(SdResId(STR_MODIFYLAYER)); + AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectLayerChange(*pO, pO->GetLayer(), nLayer)); + EndUndo(); + } + + pO->SetLayer( nLayer ); + } + } + + bReturn = true; + } + } + else + { + SdrPageView* pPV = GetSdrPageView(); + bool bDropOnTabBar = true; + + if( !pPage && pPV->GetPage()->GetPageNum() != mnDragSrcPgNum ) + { + pPage = static_cast<SdPage*>( pPV->GetPage() ); + bDropOnTabBar = false; + } + + if( pPage ) + { + // drop on other page + OUString aActiveLayer = GetActiveLayer(); + + if( !pPV->IsLayerLocked( aActiveLayer ) ) + { + if( !IsPresObjSelected() ) + { + SdrMarkList* pMarkList; + + if( (mnDragSrcPgNum != SDRPAGE_NOTFOUND) && (mnDragSrcPgNum != pPV->GetPage()->GetPageNum()) ) + { + pMarkList = mpDragSrcMarkList.get(); + } + else + { + // actual mark list is used + pMarkList = new SdrMarkList( GetMarkedObjectList()); + } + + pMarkList->ForceSort(); + + // stuff to remember originals and clones + std::vector<ImpRememberOrigAndClone> aConnectorContainer; + size_t nConnectorCount = 0; + Point aCurPos; + + // calculate real position of current + // source objects, if necessary (#103207) + if( pOwnData == SD_MOD()->pTransferSelection ) + { + ::tools::Rectangle aCurBoundRect; + + if( pMarkList->TakeBoundRect( pPV, aCurBoundRect ) ) + aCurPos = aCurBoundRect.TopLeft(); + else + aCurPos = pOwnData->GetStartPos(); + } + else + aCurPos = pOwnData->GetStartPos(); + + const Size aVector( maDropPos.X() - aCurPos.X(), maDropPos.Y() - aCurPos.Y() ); + + std::unordered_set<rtl::OUString> aNameSet; + for(size_t a = 0; a < pMarkList->GetMarkCount(); ++a) + { + SdrMark* pM = pMarkList->GetMark(a); + SdrObject* pObj(pM->GetMarkedSdrObj()->CloneSdrObject(pPage->getSdrModelFromSdrPage())); + + if(pObj) + { + if(!bDropOnTabBar) + { + // do a NbcMove(...) instead of setting SnapRects here + pObj->NbcMove(aVector); + } + + SdrObject* pMarkParent = pM->GetMarkedSdrObj()->getParentSdrObjectFromSdrObject(); + if (bCopy || (pMarkParent && pMarkParent->IsGroupObject())) + pPage->InsertObjectThenMakeNameUnique(pObj, aNameSet); + else + pPage->InsertObject(pObj); + + if( IsUndoEnabled() ) + { + BegUndo(SdResId(STR_UNDO_DRAGDROP)); + AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pObj)); + EndUndo(); + } + + ImpRememberOrigAndClone aRem; + aRem.pOrig = pM->GetMarkedSdrObj(); + aRem.pClone = pObj; + aConnectorContainer.push_back(aRem); + + if(dynamic_cast< SdrEdgeObj *>( pObj ) != nullptr) + nConnectorCount++; + } + } + + // try to re-establish connections at clones + if(nConnectorCount) + { + for(size_t a = 0; a < aConnectorContainer.size(); ++a) + { + ImpRememberOrigAndClone* pRem = &aConnectorContainer[a]; + + if(auto pCloneEdge = dynamic_cast<SdrEdgeObj *>( pRem->pClone )) + { + SdrEdgeObj* pOrigEdge = static_cast<SdrEdgeObj*>(pRem->pOrig); + + // test first connection + SdrObjConnection& rConn0 = pOrigEdge->GetConnection(false); + SdrObject* pConnObj = rConn0.GetObject(); + if(pConnObj) + { + SdrObject* pConnClone = ImpGetClone(aConnectorContainer, pConnObj); + if(pConnClone) + { + // if dest obj was cloned, too, re-establish connection + pCloneEdge->ConnectToNode(false, pConnClone); + pCloneEdge->GetConnection(false).SetConnectorId(rConn0.GetConnectorId()); + } + else + { + // set position of connection point of original connected object + const SdrGluePointList* pGlueList = pConnObj->GetGluePointList(); + if(pGlueList) + { + sal_uInt16 nInd = pGlueList->FindGluePoint(rConn0.GetConnectorId()); + + if(SDRGLUEPOINT_NOTFOUND != nInd) + { + const SdrGluePoint& rGluePoint = (*pGlueList)[nInd]; + Point aPosition = rGluePoint.GetAbsolutePos(*pConnObj); + aPosition.AdjustX(aVector.Width() ); + aPosition.AdjustY(aVector.Height() ); + pCloneEdge->SetTailPoint(false, aPosition); + } + } + } + } + + // test second connection + SdrObjConnection& rConn1 = pOrigEdge->GetConnection(true); + pConnObj = rConn1.GetObject(); + if(pConnObj) + { + SdrObject* pConnClone = ImpGetClone(aConnectorContainer, pConnObj); + if(pConnClone) + { + // if dest obj was cloned, too, re-establish connection + pCloneEdge->ConnectToNode(true, pConnClone); + pCloneEdge->GetConnection(true).SetConnectorId(rConn1.GetConnectorId()); + } + else + { + // set position of connection point of original connected object + const SdrGluePointList* pGlueList = pConnObj->GetGluePointList(); + if(pGlueList) + { + sal_uInt16 nInd = pGlueList->FindGluePoint(rConn1.GetConnectorId()); + + if(SDRGLUEPOINT_NOTFOUND != nInd) + { + const SdrGluePoint& rGluePoint = (*pGlueList)[nInd]; + Point aPosition = rGluePoint.GetAbsolutePos(*pConnObj); + aPosition.AdjustX(aVector.Width() ); + aPosition.AdjustY(aVector.Height() ); + pCloneEdge->SetTailPoint(true, aPosition); + } + } + } + } + } + } + } + + if( pMarkList != mpDragSrcMarkList.get() ) + delete pMarkList; + + bReturn = true; + } + else + { + maDropErrorIdle.Start(); + bReturn = false; + } + } + } + else + { + pOwnData->SetInternalMove( true ); + MoveAllMarked( Size( maDropPos.X() - pOwnData->GetStartPos().X(), + maDropPos.Y() - pOwnData->GetStartPos().Y() ), bCopy ); + bReturn = true; + } + } + } + else + { + // different views + if( !pSourceView->IsPresObjSelected() ) + { + // model is owned by from AllocModel() created DocShell + SdDrawDocument* pSourceDoc = static_cast<SdDrawDocument*>( pSourceView->GetModel() ); + pSourceDoc->CreatingDataObj( pOwnData ); + SdDrawDocument* pModel = static_cast<SdDrawDocument*>( pSourceView->CreateMarkedObjModel().release() ); + bReturn = Paste(*pModel, maDropPos, pPage, nPasteOptions); + + if( !pPage ) + pPage = static_cast<SdPage*>( GetSdrPageView()->GetPage() ); + + OUString aLayout = pPage->GetLayoutName(); + sal_Int32 nPos = aLayout.indexOf(SD_LT_SEPARATOR); + if (nPos != -1) + aLayout = aLayout.copy(0, nPos); + pPage->SetPresentationLayout( aLayout, false, false ); + pSourceDoc->CreatingDataObj( nullptr ); + } + else + { + maDropErrorIdle.Start(); + bReturn = false; + } + } + } + else + { + SdDrawDocument* pWorkModel = const_cast<SdDrawDocument*>(pOwnData->GetWorkDocument()); + SdPage* pWorkPage = pWorkModel->GetSdPage( 0, PageKind::Standard ); + + pWorkPage->SetSdrObjListRectsDirty(); + + // #i120393# Clipboard data uses full object geometry range + const Size aSize( pWorkPage->GetAllObjBoundRect().GetSize() ); + + maDropPos.setX( pOwnData->GetStartPos().X() + ( aSize.Width() >> 1 ) ); + maDropPos.setY( pOwnData->GetStartPos().Y() + ( aSize.Height() >> 1 ) ); + + // delete pages, that are not of any interest for us + for( ::tools::Long i = pWorkModel->GetPageCount() - 1; i >= 0; i-- ) + { + SdPage* pP = static_cast< SdPage* >( pWorkModel->GetPage( static_cast<sal_uInt16>(i) ) ); + + if( pP->GetPageKind() != PageKind::Standard ) + pWorkModel->DeletePage( static_cast<sal_uInt16>(i) ); + } + + bReturn = Paste(*pWorkModel, maDropPos, pPage, nPasteOptions); + + if( !pPage ) + pPage = static_cast<SdPage*>( GetSdrPageView()->GetPage() ); + + OUString aLayout = pPage->GetLayoutName(); + sal_Int32 nPos = aLayout.indexOf(SD_LT_SEPARATOR); + if (nPos != -1) + aLayout = aLayout.copy(0, nPos); + pPage->SetPresentationLayout( aLayout, false, false ); + } + } + + if(!bReturn && CHECK_FORMAT_TRANS( SotClipboardFormatId::PDF )) + { + ::tools::SvRef<SotTempStream> xStm; + if( aDataHelper.GetSotStorageStream( SotClipboardFormatId::PDF, xStm ) ) + { + Point aInsertPos(rPos); + Graphic aGraphic; + if (vcl::ImportPDF(*xStm, aGraphic)) + { + std::unique_ptr<sal_uInt8[]> pGraphicContent; + + const sal_Int32 nGraphicContentSize(xStm->Tell()); + pGraphicContent.reset(new sal_uInt8[nGraphicContentSize]); + xStm->Seek(0); + xStm->ReadBytes(pGraphicContent.get(), nGraphicContentSize); + aGraphic.SetGfxLink(std::make_shared<GfxLink>(std::move(pGraphicContent), nGraphicContentSize, GfxLinkType::NativePdf)); + + InsertGraphic(aGraphic, mnAction, aInsertPos, nullptr, nullptr); + bReturn = true; + } + } + } + + if(!bReturn && CHECK_FORMAT_TRANS( SotClipboardFormatId::DRAWING )) + { + ::tools::SvRef<SotTempStream> xStm; + + if( aDataHelper.GetSotStorageStream( SotClipboardFormatId::DRAWING, xStm ) ) + { + DrawDocShellRef xShell = new DrawDocShell(SfxObjectCreateMode::INTERNAL, false, DocumentType::Impress); + xShell->DoInitNew(); + + SdDrawDocument* pModel = xShell->GetDoc(); + pModel->InsertPage(pModel->AllocPage(false).get()); + + Reference< XComponent > xComponent = xShell->GetModel(); + xStm->Seek( 0 ); + + css::uno::Reference< css::io::XInputStream > xInputStream( new utl::OInputStreamWrapper( *xStm ) ); + bReturn = SvxDrawingLayerImport( pModel, xInputStream, xComponent, "com.sun.star.comp.Impress.XMLOasisImporter" ); + + if( pModel->GetPageCount() == 0 ) + { + OSL_FAIL("empty or invalid drawing xml document on clipboard!" ); + } + else + { + bool bChanged = false; + + if( bReturn ) + { + if( pModel->GetSdPage( 0, PageKind::Standard )->GetObjCount() == 1 ) + { + // only one object + SdrObject* pObj = pModel->GetSdPage( 0, PageKind::Standard )->GetObj( 0 ); + SdrPageView* pPV = nullptr; + SdrObject* pPickObj2 = PickObj(rPos, getHitTolLog(), pPV); + + if( ( mnAction & DND_ACTION_MOVE ) && pPickObj2 && pObj ) + { + // replace object + SdrPage* pWorkPage = GetSdrPageView()->GetPage(); + SdrObject* pNewObj(pObj->CloneSdrObject(pWorkPage->getSdrModelFromSdrPage())); + ::tools::Rectangle aPickObjRect( pPickObj2->GetCurrentBoundRect() ); + Size aPickObjSize( aPickObjRect.GetSize() ); + Point aVec( aPickObjRect.TopLeft() ); + ::tools::Rectangle aObjRect( pNewObj->GetCurrentBoundRect() ); + Size aObjSize( aObjRect.GetSize() ); + + Fraction aScaleWidth( aPickObjSize.Width(), aObjSize.Width() ); + Fraction aScaleHeight( aPickObjSize.Height(), aObjSize.Height() ); + pNewObj->NbcResize( aObjRect.TopLeft(), aScaleWidth, aScaleHeight ); + + aVec -= aObjRect.TopLeft(); + pNewObj->NbcMove( Size( aVec.X(), aVec.Y() ) ); + + const bool bUndo = IsUndoEnabled(); + + if( bUndo ) + BegUndo(SdResId(STR_UNDO_DRAGDROP)); + pNewObj->NbcSetLayer( pPickObj->GetLayer() ); + pWorkPage->InsertObject( pNewObj ); + if( bUndo ) + { + AddUndo( mrDoc.GetSdrUndoFactory().CreateUndoNewObject( *pNewObj ) ); + AddUndo( mrDoc.GetSdrUndoFactory().CreateUndoDeleteObject( *pPickObj2 ) ); + } + pWorkPage->RemoveObject( pPickObj2->GetOrdNum() ); + + if( bUndo ) + { + EndUndo(); + } + else + { + SdrObject::Free(pPickObj2 ); + } + bChanged = true; + mnAction = DND_ACTION_COPY; + } + else if( ( mnAction & DND_ACTION_LINK ) && pPickObj && pObj && + dynamic_cast< const SdrGrafObj *>( pPickObj ) == nullptr && + dynamic_cast< const SdrOle2Obj *>( pPickObj ) == nullptr ) + { + SfxItemSet aSet( mrDoc.GetPool() ); + + // set new attributes to object + const bool bUndo = IsUndoEnabled(); + if( bUndo ) + { + BegUndo( SdResId(STR_UNDO_DRAGDROP) ); + AddUndo( mrDoc.GetSdrUndoFactory().CreateUndoAttrObject( *pPickObj ) ); + } + + aSet.Put( pObj->GetMergedItemSet() ); + + /* Do not take over corner radius. There are + gradients (rectangles) in the gallery with corner + radius of 0. We should not use that on the + object. */ + aSet.ClearItem( SDRATTR_CORNER_RADIUS ); + + const SdrGrafObj* pSdrGrafObj = dynamic_cast< const SdrGrafObj* >(pObj); + + if(pSdrGrafObj) + { + // If we have a graphic as source object, use its graphic + // content as fill style + aSet.Put(XFillStyleItem(drawing::FillStyle_BITMAP)); + aSet.Put(XFillBitmapItem(pSdrGrafObj->GetGraphic())); + } + + pPickObj->SetMergedItemSetAndBroadcast( aSet ); + + if( dynamic_cast< E3dObject *>( pPickObj ) != nullptr && dynamic_cast< E3dObject *>( pObj ) != nullptr ) + { + // handle 3D attribute in addition + SfxItemSetFixed<SID_ATTR_3D_START, SID_ATTR_3D_END> aNewSet( mrDoc.GetPool() ); + SfxItemSetFixed<SID_ATTR_3D_START, SID_ATTR_3D_END> aOldSet( mrDoc.GetPool() ); + + aOldSet.Put(pPickObj->GetMergedItemSet()); + aNewSet.Put( pObj->GetMergedItemSet() ); + + if( bUndo ) + AddUndo( + std::make_unique<E3dAttributesUndoAction>( + *static_cast< E3dObject* >(pPickObj), + aNewSet, + aOldSet)); + pPickObj->SetMergedItemSetAndBroadcast( aNewSet ); + } + + if( bUndo ) + EndUndo(); + bChanged = true; + } + } + } + + if( !bChanged ) + { + SdrPage* pWorkPage = pModel->GetSdPage( 0, PageKind::Standard ); + + pWorkPage->SetSdrObjListRectsDirty(); + + if( pOwnData ) + { + // #i120393# Clipboard data uses full object geometry range + const Size aSize( pWorkPage->GetAllObjBoundRect().GetSize() ); + + maDropPos.setX( pOwnData->GetStartPos().X() + ( aSize.Width() >> 1 ) ); + maDropPos.setY( pOwnData->GetStartPos().Y() + ( aSize.Height() >> 1 ) ); + } + + bReturn = Paste(*pModel, maDropPos, pPage, nPasteOptions); + } + + xShell->DoClose(); + } + } + } + + if(!bReturn && CHECK_FORMAT_TRANS(SotClipboardFormatId::SBA_FIELDDATAEXCHANGE)) + { + OUString aOUString; + + if( aDataHelper.GetString( SotClipboardFormatId::SBA_FIELDDATAEXCHANGE, aOUString ) ) + { + SdrObjectUniquePtr pObj = CreateFieldControl( aOUString ); + + if( pObj ) + { + ::tools::Rectangle aRect( pObj->GetLogicRect() ); + Size aSize( aRect.GetSize() ); + + maDropPos.AdjustX( -( aSize.Width() >> 1 ) ); + maDropPos.AdjustY( -( aSize.Height() >> 1 ) ); + + aRect.SetPos( maDropPos ); + pObj->SetLogicRect( aRect ); + InsertObjectAtView( pObj.release(), *GetSdrPageView(), SdrInsertFlags::SETDEFLAYER ); + bReturn = true; + } + } + } + + if(!bReturn && + !bLink && + (CHECK_FORMAT_TRANS(SotClipboardFormatId::EMBED_SOURCE) || CHECK_FORMAT_TRANS(SotClipboardFormatId::EMBEDDED_OBJ)) && + aDataHelper.HasFormat(SotClipboardFormatId::OBJECTDESCRIPTOR)) + { + //TODO/LATER: is it possible that this format is binary?! (from old versions of SO) + uno::Reference < io::XInputStream > xStm; + TransferableObjectDescriptor aObjDesc; + + if (aDataHelper.GetTransferableObjectDescriptor(SotClipboardFormatId::OBJECTDESCRIPTOR, aObjDesc)) + { + OUString aDocShellID = SfxObjectShell::CreateShellID(mrDoc.GetDocSh()); + xStm = aDataHelper.GetInputStream(nFormat != SotClipboardFormatId::NONE ? nFormat : SotClipboardFormatId::EMBED_SOURCE, aDocShellID); + if (!xStm.is()) + xStm = aDataHelper.GetInputStream(SotClipboardFormatId::EMBEDDED_OBJ, aDocShellID); + } + + if (xStm.is()) + { + if( mrDoc.GetDocSh() && ( mrDoc.GetDocSh()->GetClassName() == aObjDesc.maClassName ) ) + { + uno::Reference < embed::XStorage > xStore( ::comphelper::OStorageHelper::GetStorageFromInputStream( xStm ) ); + ::sd::DrawDocShellRef xDocShRef( new ::sd::DrawDocShell( SfxObjectCreateMode::EMBEDDED, true, mrDoc.GetDocumentType() ) ); + + // mba: BaseURL doesn't make sense for clipboard functionality + SfxMedium *pMedium = new SfxMedium( xStore, OUString() ); + if( xDocShRef->DoLoad( pMedium ) ) + { + SdDrawDocument* pModel = xDocShRef->GetDoc(); + SdPage* pWorkPage = pModel->GetSdPage( 0, PageKind::Standard ); + + pWorkPage->SetSdrObjListRectsDirty(); + + if( pOwnData ) + { + // #i120393# Clipboard data uses full object geometry range + const Size aSize( pWorkPage->GetAllObjBoundRect().GetSize() ); + + maDropPos.setX( pOwnData->GetStartPos().X() + ( aSize.Width() >> 1 ) ); + maDropPos.setY( pOwnData->GetStartPos().Y() + ( aSize.Height() >> 1 ) ); + } + + // delete pages, that are not of any interest for us + for( ::tools::Long i = pModel->GetPageCount() - 1; i >= 0; i-- ) + { + SdPage* pP = static_cast< SdPage* >( pModel->GetPage( static_cast<sal_uInt16>(i) ) ); + + if( pP->GetPageKind() != PageKind::Standard ) + pModel->DeletePage( static_cast<sal_uInt16>(i) ); + } + + bReturn = Paste(*pModel, maDropPos, pPage, nPasteOptions); + + if( !pPage ) + pPage = static_cast<SdPage*>(GetSdrPageView()->GetPage()); + + OUString aLayout = pPage->GetLayoutName(); + sal_Int32 nPos = aLayout.indexOf(SD_LT_SEPARATOR); + if (nPos != -1) + aLayout = aLayout.copy(0, nPos); + pPage->SetPresentationLayout( aLayout, false, false ); + } + + xDocShRef->DoClose(); + xDocShRef.clear(); + + } + else + { + OUString aName; + uno::Reference < embed::XEmbeddedObject > xObj = mpDocSh->GetEmbeddedObjectContainer().InsertEmbeddedObject( xStm, aName ); + if ( xObj.is() ) + { + svt::EmbeddedObjectRef aObjRef( xObj, aObjDesc.mnViewAspect ); + + Size aSize; + if ( aObjDesc.mnViewAspect == embed::Aspects::MSOLE_ICON ) + { + if( aObjDesc.maSize.Width() && aObjDesc.maSize.Height() ) + aSize = aObjDesc.maSize; + else + { + MapMode aMapMode( MapUnit::Map100thMM ); + aSize = aObjRef.GetSize( &aMapMode ); + } + } + else + { + awt::Size aSz; + MapUnit aMapUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( aObjDesc.mnViewAspect ) ); + if( aObjDesc.maSize.Width() && aObjDesc.maSize.Height() ) + { + Size aTmp(OutputDevice::LogicToLogic(aObjDesc.maSize, MapMode(MapUnit::Map100thMM), MapMode(aMapUnit))); + aSz.Width = aTmp.Width(); + aSz.Height = aTmp.Height(); + xObj->setVisualAreaSize( aObjDesc.mnViewAspect, aSz ); + } + + try + { + aSz = xObj->getVisualAreaSize( aObjDesc.mnViewAspect ); + } + catch( embed::NoVisualAreaSizeException& ) + { + // if the size still was not set the default size will be set later + } + + aSize = Size( aSz.Width, aSz.Height ); + + if( !aSize.Width() || !aSize.Height() ) + { + aSize.setWidth( 14100 ); + aSize.setHeight( 10000 ); + aSize = OutputDevice::LogicToLogic(Size(14100, 10000), MapMode(MapUnit::Map100thMM), MapMode(aMapUnit)); + aSz.Width = aSize.Width(); + aSz.Height = aSize.Height(); + xObj->setVisualAreaSize( aObjDesc.mnViewAspect, aSz ); + } + + aSize = OutputDevice::LogicToLogic(aSize, MapMode(aMapUnit), MapMode(MapUnit::Map100thMM)); + } + + Size aMaxSize( mrDoc.GetMaxObjSize() ); + + maDropPos.AdjustX( -(std::min( aSize.Width(), aMaxSize.Width() ) >> 1) ); + maDropPos.AdjustY( -(std::min( aSize.Height(), aMaxSize.Height() ) >> 1) ); + + ::tools::Rectangle aRect( maDropPos, aSize ); + SdrOle2Obj* pObj = new SdrOle2Obj( + getSdrModelFromSdrView(), + aObjRef, + aName, + aRect); + SdrPageView* pPV = GetSdrPageView(); + SdrInsertFlags nOptions = SdrInsertFlags::SETDEFLAYER; + + if (mpViewSh!=nullptr) + { + OSL_ASSERT (mpViewSh->GetViewShell()!=nullptr); + SfxInPlaceClient* pIpClient + = mpViewSh->GetViewShell()->GetIPClient(); + if (pIpClient!=nullptr && pIpClient->IsObjectInPlaceActive()) + nOptions |= SdrInsertFlags::DONTMARK; + } + + // bInserted of false means that pObj has been deleted + bool bInserted = InsertObjectAtView( pObj, *pPV, nOptions ); + + if (bInserted && pImageMap) + pObj->AppendUserData( std::unique_ptr<SdrObjUserData>(new SvxIMapInfo( *pImageMap )) ); + + if (bInserted && pObj->IsChart()) + { + bool bDisableDataTableDialog = false; + svt::EmbeddedObjectRef::TryRunningState( xObj ); + uno::Reference< beans::XPropertySet > xProps( xObj->getComponent(), uno::UNO_QUERY ); + if ( xProps.is() && + ( xProps->getPropertyValue( "DisableDataTableDialog" ) >>= bDisableDataTableDialog ) && + bDisableDataTableDialog ) + { + xProps->setPropertyValue( "DisableDataTableDialog" , uno::Any( false ) ); + xProps->setPropertyValue( "DisableComplexChartTypes" , uno::Any( false ) ); + uno::Reference< util::XModifiable > xModifiable( xProps, uno::UNO_QUERY ); + if ( xModifiable.is() ) + { + xModifiable->setModified( true ); + } + } + } + + bReturn = true; + } + } + } + } + + if(!bReturn && + !bLink && + (CHECK_FORMAT_TRANS(SotClipboardFormatId::EMBEDDED_OBJ_OLE) || CHECK_FORMAT_TRANS(SotClipboardFormatId::EMBED_SOURCE_OLE)) && + aDataHelper.HasFormat(SotClipboardFormatId::OBJECTDESCRIPTOR_OLE)) + { + // online insert ole if format is forced or no gdi metafile is available + if( (nFormat != SotClipboardFormatId::NONE) || !aDataHelper.HasFormat( SotClipboardFormatId::GDIMETAFILE ) ) + { + uno::Reference < io::XInputStream > xStm; + TransferableObjectDescriptor aObjDesc; + + if ( aDataHelper.GetTransferableObjectDescriptor( SotClipboardFormatId::OBJECTDESCRIPTOR_OLE, aObjDesc ) ) + { + uno::Reference < embed::XEmbeddedObject > xObj; + OUString aName; + + xStm = aDataHelper.GetInputStream(nFormat != SotClipboardFormatId::NONE ? nFormat : SotClipboardFormatId::EMBED_SOURCE_OLE, OUString()); + if (!xStm.is()) + xStm = aDataHelper.GetInputStream(SotClipboardFormatId::EMBEDDED_OBJ_OLE, OUString()); + + if (xStm.is()) + { + xObj = mpDocSh->GetEmbeddedObjectContainer().InsertEmbeddedObject( xStm, aName ); + } + else + { + try + { + uno::Reference< embed::XStorage > xTmpStor = ::comphelper::OStorageHelper::GetTemporaryStorage(); + uno::Reference < embed::XEmbedObjectClipboardCreator > xClipboardCreator = + embed::MSOLEObjectSystemCreator::create( ::comphelper::getProcessComponentContext() ); + + embed::InsertedObjectInfo aInfo = xClipboardCreator->createInstanceInitFromClipboard( + xTmpStor, + "DummyName" , + uno::Sequence< beans::PropertyValue >() ); + + // TODO/LATER: in future InsertedObjectInfo will be used to get container related information + // for example whether the object should be an iconified one + xObj = aInfo.Object; + if ( xObj.is() ) + mpDocSh->GetEmbeddedObjectContainer().InsertEmbeddedObject( xObj, aName ); + } + catch( uno::Exception& ) + {} + } + + if ( xObj.is() ) + { + svt::EmbeddedObjectRef aObjRef( xObj, aObjDesc.mnViewAspect ); + + // try to get the replacement image from the clipboard + Graphic aGraphic; + SotClipboardFormatId nGrFormat = SotClipboardFormatId::NONE; + +// (for Selection Manager in Trusted Solaris) +#ifndef __sun + if( aDataHelper.GetGraphic( SotClipboardFormatId::SVXB, aGraphic ) ) + nGrFormat = SotClipboardFormatId::SVXB; + else if( aDataHelper.GetGraphic( SotClipboardFormatId::GDIMETAFILE, aGraphic ) ) + nGrFormat = SotClipboardFormatId::GDIMETAFILE; + else if( aDataHelper.GetGraphic( SotClipboardFormatId::BITMAP, aGraphic ) ) + nGrFormat = SotClipboardFormatId::BITMAP; +#endif + + // insert replacement image ( if there is one ) into the object helper + if ( nGrFormat != SotClipboardFormatId::NONE ) + { + datatransfer::DataFlavor aDataFlavor; + SotExchange::GetFormatDataFlavor( nGrFormat, aDataFlavor ); + aObjRef.SetGraphic( aGraphic, aDataFlavor.MimeType ); + } + + Size aSize; + if ( aObjDesc.mnViewAspect == embed::Aspects::MSOLE_ICON ) + { + if( aObjDesc.maSize.Width() && aObjDesc.maSize.Height() ) + aSize = aObjDesc.maSize; + else + { + MapMode aMapMode( MapUnit::Map100thMM ); + aSize = aObjRef.GetSize( &aMapMode ); + } + } + else + { + MapUnit aMapUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( aObjDesc.mnViewAspect ) ); + + awt::Size aSz; + try{ + aSz = xObj->getVisualAreaSize( aObjDesc.mnViewAspect ); + } + catch( embed::NoVisualAreaSizeException& ) + { + // the default size will be set later + } + + if( aObjDesc.maSize.Width() && aObjDesc.maSize.Height() ) + { + Size aTmp(OutputDevice::LogicToLogic(aObjDesc.maSize, MapMode(MapUnit::Map100thMM), MapMode(aMapUnit))); + if ( aSz.Width != aTmp.Width() || aSz.Height != aTmp.Height() ) + { + aSz.Width = aTmp.Width(); + aSz.Height = aTmp.Height(); + xObj->setVisualAreaSize( aObjDesc.mnViewAspect, aSz ); + } + } + + aSize = Size( aSz.Width, aSz.Height ); + + if( !aSize.Width() || !aSize.Height() ) + { + aSize = OutputDevice::LogicToLogic(Size(14100, 10000), MapMode(MapUnit::Map100thMM), MapMode(aMapUnit)); + aSz.Width = aSize.Width(); + aSz.Height = aSize.Height(); + xObj->setVisualAreaSize( aObjDesc.mnViewAspect, aSz ); + } + + aSize = OutputDevice::LogicToLogic(aSize, MapMode(aMapUnit), MapMode(MapUnit::Map100thMM)); + } + + Size aMaxSize( mrDoc.GetMaxObjSize() ); + + maDropPos.AdjustX( -(std::min( aSize.Width(), aMaxSize.Width() ) >> 1) ); + maDropPos.AdjustY( -(std::min( aSize.Height(), aMaxSize.Height() ) >> 1) ); + + ::tools::Rectangle aRect( maDropPos, aSize ); + SdrOle2Obj* pObj = new SdrOle2Obj( + getSdrModelFromSdrView(), + aObjRef, + aName, + aRect); + SdrPageView* pPV = GetSdrPageView(); + SdrInsertFlags nOptions = SdrInsertFlags::SETDEFLAYER; + + if (mpViewSh!=nullptr) + { + OSL_ASSERT (mpViewSh->GetViewShell()!=nullptr); + SfxInPlaceClient* pIpClient + = mpViewSh->GetViewShell()->GetIPClient(); + if (pIpClient!=nullptr && pIpClient->IsObjectInPlaceActive()) + nOptions |= SdrInsertFlags::DONTMARK; + } + + bReturn = InsertObjectAtView( pObj, *pPV, nOptions ); + + if (bReturn) + { + if( pImageMap ) + pObj->AppendUserData( std::unique_ptr<SdrObjUserData>(new SvxIMapInfo( *pImageMap )) ); + + // let the object stay in loaded state after insertion + pObj->Unload(); + } + } + } + } + + if( !bReturn && aDataHelper.HasFormat( SotClipboardFormatId::GDIMETAFILE ) ) + { + // if no object was inserted, insert a picture + InsertMetaFile( aDataHelper, rPos, pImageMap.get(), true ); + bReturn = true; + } + } + + if(!bReturn && (!bLink || pPickObj) && CHECK_FORMAT_TRANS(SotClipboardFormatId::SVXB)) + { + ::tools::SvRef<SotTempStream> xStm; + + if( aDataHelper.GetSotStorageStream( SotClipboardFormatId::SVXB, xStm ) ) + { + Point aInsertPos( rPos ); + Graphic aGraphic; + + TypeSerializer aSerializer(*xStm); + aSerializer.readGraphic(aGraphic); + + if( pOwnData && pOwnData->GetWorkDocument() ) + { + const SdDrawDocument* pWorkModel = pOwnData->GetWorkDocument(); + SdrPage* pWorkPage = const_cast<SdrPage*>( ( pWorkModel->GetPageCount() > 1 ) ? + pWorkModel->GetSdPage( 0, PageKind::Standard ) : + pWorkModel->GetPage( 0 ) ); + + pWorkPage->SetSdrObjListRectsDirty(); + + // #i120393# Clipboard data uses full object geometry range + const Size aSize( pWorkPage->GetAllObjBoundRect().GetSize() ); + + aInsertPos.setX( pOwnData->GetStartPos().X() + ( aSize.Width() >> 1 ) ); + aInsertPos.setY( pOwnData->GetStartPos().Y() + ( aSize.Height() >> 1 ) ); + } + + // restrict movement to WorkArea + Size aImageMapSize = OutputDevice::LogicToLogic(aGraphic.GetPrefSize(), + aGraphic.GetPrefMapMode(), MapMode(MapUnit::Map100thMM)); + + ImpCheckInsertPos(aInsertPos, aImageMapSize, GetWorkArea()); + + InsertGraphic( aGraphic, mnAction, aInsertPos, nullptr, pImageMap.get() ); + bReturn = true; + } + } + + if(!bReturn && (!bLink || pPickObj) && CHECK_FORMAT_TRANS(SotClipboardFormatId::GDIMETAFILE)) + { + Point aInsertPos( rPos ); + + if( pOwnData && pOwnData->GetWorkDocument() ) + + { + const SdDrawDocument* pWorkModel = pOwnData->GetWorkDocument(); + SdrPage* pWorkPage = const_cast<SdrPage*>( ( pWorkModel->GetPageCount() > 1 ) ? + pWorkModel->GetSdPage( 0, PageKind::Standard ) : + pWorkModel->GetPage( 0 ) ); + + pWorkPage->SetSdrObjListRectsDirty(); + + // #i120393# Clipboard data uses full object geometry range + const Size aSize( pWorkPage->GetAllObjBoundRect().GetSize() ); + + aInsertPos.setX( pOwnData->GetStartPos().X() + ( aSize.Width() >> 1 ) ); + aInsertPos.setY( pOwnData->GetStartPos().Y() + ( aSize.Height() >> 1 ) ); + } + + bReturn = InsertMetaFile( aDataHelper, aInsertPos, pImageMap.get(), nFormat == SotClipboardFormatId::NONE ); + } + + if(!bReturn && (!bLink || pPickObj) && CHECK_FORMAT_TRANS(SotClipboardFormatId::BITMAP)) + { + BitmapEx aBmpEx; + + // get basic Bitmap data + aDataHelper.GetBitmapEx(SotClipboardFormatId::BITMAP, aBmpEx); + + if(aBmpEx.IsEmpty()) + { + // if this did not work, try to get graphic formats and convert these to bitmap + Graphic aGraphic; + + if(aDataHelper.GetGraphic(SotClipboardFormatId::GDIMETAFILE, aGraphic)) + { + aBmpEx = aGraphic.GetBitmapEx(); + } + else if(aDataHelper.GetGraphic(SotClipboardFormatId::SVXB, aGraphic)) + { + aBmpEx = aGraphic.GetBitmapEx(); + } + else if(aDataHelper.GetGraphic(SotClipboardFormatId::BITMAP, aGraphic)) + { + aBmpEx = aGraphic.GetBitmapEx(); + } + } + + if(!aBmpEx.IsEmpty()) + { + Point aInsertPos( rPos ); + + if( pOwnData && pOwnData->GetWorkDocument() ) + { + const SdDrawDocument* pWorkModel = pOwnData->GetWorkDocument(); + SdrPage* pWorkPage = const_cast<SdrPage*>( ( pWorkModel->GetPageCount() > 1 ) ? + pWorkModel->GetSdPage( 0, PageKind::Standard ) : + pWorkModel->GetPage( 0 ) ); + + pWorkPage->SetSdrObjListRectsDirty(); + + // #i120393# Clipboard data uses full object geometry range + const Size aSize( pWorkPage->GetAllObjBoundRect().GetSize() ); + + aInsertPos.setX( pOwnData->GetStartPos().X() + ( aSize.Width() >> 1 ) ); + aInsertPos.setY( pOwnData->GetStartPos().Y() + ( aSize.Height() >> 1 ) ); + } + + // restrict movement to WorkArea + Size aImageMapSize(aBmpEx.GetPrefSize()); + ImpCheckInsertPos(aInsertPos, aImageMapSize, GetWorkArea()); + + InsertGraphic( aBmpEx, mnAction, aInsertPos, nullptr, pImageMap.get() ); + bReturn = true; + } + } + + if(!bReturn && pPickObj && CHECK_FORMAT_TRANS( SotClipboardFormatId::XFA ) ) + { + uno::Any const data(aDataHelper.GetAny(SotClipboardFormatId::XFA, "")); + uno::Sequence<beans::NamedValue> props; + if (data >>= props) + { + if( IsUndoEnabled() ) + { + BegUndo( SdResId(STR_UNDO_DRAGDROP) ); + AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoAttrObject( *pPickObj ) ); + EndUndo(); + } + + ::comphelper::SequenceAsHashMap const map(props); + drawing::FillStyle eFill(drawing::FillStyle_BITMAP); // default to something that's ignored + Color aColor(COL_BLACK); + auto it = map.find("FillStyle"); + if (it != map.end()) + { + XFillStyleItem style; + style.PutValue(it->second, 0); + eFill = style.GetValue(); + } + it = map.find("FillColor"); + if (it != map.end()) + { + XFillColorItem color; + color.PutValue(it->second, 0); + aColor = color.GetColorValue(); + } + + if( eFill == drawing::FillStyle_SOLID || eFill == drawing::FillStyle_NONE ) + { + SfxItemSet aSet( mrDoc.GetPool() ); + bool bClosed = pPickObj->IsClosedObj(); + ::sd::Window* pWin = mpViewSh->GetActiveWindow(); + sal_uInt16 nHitLog = static_cast<sal_uInt16>(pWin->PixelToLogic( + Size(FuPoor::HITPIX, 0 ) ).Width()); + const ::tools::Long n2HitLog = nHitLog << 1; + Point aHitPosR( rPos ); + Point aHitPosL( rPos ); + Point aHitPosT( rPos ); + Point aHitPosB( rPos ); + const SdrLayerIDSet* pVisiLayer = &GetSdrPageView()->GetVisibleLayers(); + + aHitPosR.AdjustX(n2HitLog ); + aHitPosL.AdjustX( -n2HitLog ); + aHitPosT.AdjustY(n2HitLog ); + aHitPosB.AdjustY( -n2HitLog ); + + if( bClosed && + SdrObjectPrimitiveHit(*pPickObj, aHitPosR, nHitLog, *GetSdrPageView(), pVisiLayer, false) && + SdrObjectPrimitiveHit(*pPickObj, aHitPosL, nHitLog, *GetSdrPageView(), pVisiLayer, false) && + SdrObjectPrimitiveHit(*pPickObj, aHitPosT, nHitLog, *GetSdrPageView(), pVisiLayer, false) && + SdrObjectPrimitiveHit(*pPickObj, aHitPosB, nHitLog, *GetSdrPageView(), pVisiLayer, false) ) + { + // area fill + if(eFill == drawing::FillStyle_SOLID ) + aSet.Put(XFillColorItem("", aColor)); + + aSet.Put( XFillStyleItem( eFill ) ); + } + else + aSet.Put( XLineColorItem( "", aColor ) ); + + // add text color + pPickObj->SetMergedItemSetAndBroadcast( aSet ); + } + bReturn = true; + } + } + + if(!bReturn && !bLink && CHECK_FORMAT_TRANS(SotClipboardFormatId::HTML)) + { + ::tools::SvRef<SotTempStream> xStm; + + if( aDataHelper.GetSotStorageStream( SotClipboardFormatId::HTML, xStm ) ) + { + xStm->Seek( 0 ); + // mba: clipboard always must contain absolute URLs (could be from alien source) + bReturn = SdrView::Paste( *xStm, EETextFormat::Html, maDropPos, pPage, nPasteOptions ); + } + } + + if(!bReturn && !bLink && CHECK_FORMAT_TRANS(SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT)) + { + ::tools::SvRef<SotTempStream> xStm; + if( aDataHelper.GetSotStorageStream( SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT, xStm ) ) + { + OutlinerView* pOLV = GetTextEditOutlinerView(); + + xStm->Seek( 0 ); + + if( pOLV ) + { + ::tools::Rectangle aRect( pOLV->GetOutputArea() ); + Point aPos( pOLV->GetWindow()->PixelToLogic( maDropPos ) ); + + if( aRect.Contains( aPos ) || ( !bDrag && IsTextEdit() ) ) + { + // mba: clipboard always must contain absolute URLs (could be from alien source) + pOLV->Read( *xStm, EETextFormat::Xml, mpDocSh->GetHeaderAttributes() ); + bReturn = true; + } + } + + if( !bReturn ) + // mba: clipboard always must contain absolute URLs (could be from alien source) + bReturn = SdrView::Paste( *xStm, EETextFormat::Xml, maDropPos, pPage, nPasteOptions ); + } + } + + if(!bReturn && !bLink) + { + bool bIsRTF = CHECK_FORMAT_TRANS(SotClipboardFormatId::RTF); + if (bIsRTF || CHECK_FORMAT_TRANS(SotClipboardFormatId::RICHTEXT)) + { + ::tools::SvRef<SotTempStream> xStm; + + if( aDataHelper.GetSotStorageStream( bIsRTF ? SotClipboardFormatId::RTF : SotClipboardFormatId::RICHTEXT, xStm ) ) + { + xStm->Seek( 0 ); + + if( bTable ) + { + bReturn = PasteRTFTable( xStm, pPage, nPasteOptions ); + } + else + { + OutlinerView* pOLV = GetTextEditOutlinerView(); + + if( pOLV ) + { + ::tools::Rectangle aRect( pOLV->GetOutputArea() ); + Point aPos( pOLV->GetWindow()->PixelToLogic( maDropPos ) ); + + if( aRect.Contains( aPos ) || ( !bDrag && IsTextEdit() ) ) + { + // mba: clipboard always must contain absolute URLs (could be from alien source) + pOLV->Read( *xStm, EETextFormat::Rtf, mpDocSh->GetHeaderAttributes() ); + bReturn = true; + } + } + + if( !bReturn ) + // mba: clipboard always must contain absolute URLs (could be from alien source) + bReturn = SdrView::Paste( *xStm, EETextFormat::Rtf, maDropPos, pPage, nPasteOptions ); + } + } + } + } + + if(!bReturn && CHECK_FORMAT_TRANS(SotClipboardFormatId::FILE_LIST)) + { + FileList aDropFileList; + + if( aDataHelper.GetFileList( SotClipboardFormatId::FILE_LIST, aDropFileList ) ) + { + maDropFileVector.clear(); + + for( sal_uLong i = 0, nCount = aDropFileList.Count(); i < nCount; i++ ) + maDropFileVector.push_back( aDropFileList.GetFile( i ) ); + + maDropInsertFileIdle.Start(); + } + + bReturn = true; + } + + if(!bReturn && CHECK_FORMAT_TRANS(SotClipboardFormatId::SIMPLE_FILE)) + { + OUString aDropFile; + + if( aDataHelper.GetString( SotClipboardFormatId::SIMPLE_FILE, aDropFile ) ) + { + maDropFileVector.clear(); + maDropFileVector.push_back( aDropFile ); + maDropInsertFileIdle.Start(); + } + + bReturn = true; + } + + if(!bReturn && !bLink && CHECK_FORMAT_TRANS(SotClipboardFormatId::STRING)) + { + if( ( SotClipboardFormatId::STRING == nFormat ) || + ( !aDataHelper.HasFormat( SotClipboardFormatId::SOLK ) && + !aDataHelper.HasFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ) && + !aDataHelper.HasFormat( SotClipboardFormatId::FILENAME ) ) ) + { + OUString aOUString; + + if( aDataHelper.GetString( SotClipboardFormatId::STRING, aOUString ) ) + { + OutlinerView* pOLV = GetTextEditOutlinerView(); + + if( pOLV ) + { + pOLV->InsertText( aOUString ); + bReturn = true; + } + + if( !bReturn ) + bReturn = SdrView::Paste( aOUString, maDropPos, pPage, nPasteOptions ); + } + } + } + + MarkListHasChanged(); + mbIsDropAllowed = true; + rDnDAction = mnAction; + + if (bGroupUndoFromDragWithDrop) + { + // this is called eventually by the underlying toolkit anyway in the case of a self-dnd + // but we call it early in this case to group its undo actions into this open dnd undo group + // and rely on that repeated calls to View::DragFinished are safe to do + DragFinished(mnAction); + EndUndo(); + } + + return bReturn; +} + +bool View::PasteRTFTable( const ::tools::SvRef<SotTempStream>& xStm, SdrPage* pPage, SdrInsertFlags nPasteOptions ) +{ + SdDrawDocument aModel( DocumentType::Impress, mpDocSh ); + aModel.NewOrLoadCompleted(DocCreationMode::New); + aModel.GetItemPool().SetDefaultMetric(MapUnit::Map100thMM); + aModel.InsertPage(aModel.AllocPage(false).get()); + + Reference< XComponent > xComponent( new SdXImpressDocument( &aModel, true ) ); + aModel.setUnoModel( Reference< XInterface >::query( xComponent ) ); + + CreateTableFromRTF( *xStm, &aModel ); + bool bRet = Paste(aModel, maDropPos, pPage, nPasteOptions); + + xComponent->dispose(); + xComponent.clear(); + + return bRet; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/sdview4.cxx b/sd/source/ui/view/sdview4.cxx new file mode 100644 index 000000000..7a3c7c226 --- /dev/null +++ b/sd/source/ui/view/sdview4.cxx @@ -0,0 +1,645 @@ +/* -*- 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 <config_features.h> + +#include <View.hxx> + +#include <comphelper/propertyvalue.hxx> +#include <osl/file.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/request.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/fcontnr.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/sfxsids.hrc> +#include <vcl/outdev.hxx> +#include <vcl/pdfread.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <svx/svdpagv.hxx> +#include <svx/xbtmpit.hxx> +#include <svx/svdundo.hxx> +#include <svx/xfillit0.hxx> +#include <svx/svdograf.hxx> +#include <svx/svdomedia.hxx> +#include <svx/svdoole2.hxx> +#include <svx/ImageMapInfo.hxx> +#include <sfx2/app.hxx> +#include <avmedia/mediawindow.hxx> +#include <svtools/ehdl.hxx> +#include <svtools/sfxecode.hxx> +#include <svtools/embedhlp.hxx> +#include <vcl/graphicfilter.hxx> +#include <app.hrc> +#include <Window.hxx> +#include <DrawDocShell.hxx> +#include <DrawViewShell.hxx> +#include <fuinsfil.hxx> +#include <drawdoc.hxx> +#include <sdresid.hxx> +#include <strings.hrc> +#include <sdpage.hxx> +#include <view/SlideSorterView.hxx> +#include <com/sun/star/embed/XEmbedPersist.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/embed/NoVisualAreaSizeException.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/media/XPlayer.hpp> +#include <svtools/soerr.hxx> +#include <sfx2/ipclient.hxx> +#include <tools/debug.hxx> + +using namespace com::sun::star; + +namespace sd { + +/** + * If an empty graphic object is provided, we fill it. Otherwise we fill an + * existing object at the specified position. If there is no object at the + * position, we create a new object and return a pointer to it. + */ +SdrGrafObj* View::InsertGraphic( const Graphic& rGraphic, sal_Int8& rAction, + const Point& rPos, SdrObject* pObj, ImageMap const * pImageMap ) +{ + SdrEndTextEdit(); + mnAction = rAction; + + // Is there an object at the position rPos? + SdrGrafObj* pNewGrafObj = nullptr; + SdrPageView* pPV = GetSdrPageView(); + SdrObject* pPickObj = pObj; + const bool bOnMaster = pPV && pPV->GetPage() && pPV->GetPage()->IsMasterPage(); + + if(pPV && dynamic_cast< const ::sd::slidesorter::view::SlideSorterView* >(this) != nullptr) + { + if(!pPV->GetPageRect().Contains(rPos)) + pPV = nullptr; + } + + if( !pPickObj && pPV ) + { + SdrPageView* pPageView = pPV; + pPickObj = PickObj(rPos, getHitTolLog(), pPageView); + } + + const bool bIsGraphic(dynamic_cast< const SdrGrafObj* >(pPickObj) != nullptr); + + if (DND_ACTION_LINK == mnAction + && pPickObj + && pPV + && (bIsGraphic || (pPickObj->IsEmptyPresObj() && !bOnMaster))) // #121603# Do not use pObj, it may be NULL + { + // hit on SdrGrafObj with wanted new linked graphic (or PresObj placeholder hit) + if( IsUndoEnabled() ) + BegUndo(SdResId(STR_INSERTGRAPHIC)); + + SdPage* pPage = static_cast<SdPage*>( pPickObj->getSdrPageFromSdrObject() ); + + if( bIsGraphic ) + { + // We fill the object with the Bitmap + pNewGrafObj = static_cast<SdrGrafObj*>( pPickObj->CloneSdrObject(pPickObj->getSdrModelFromSdrObject()) ); + pNewGrafObj->SetGraphic(rGraphic); + } + else + { + pNewGrafObj = new SdrGrafObj( + getSdrModelFromSdrView(), + rGraphic, + pPickObj->GetLogicRect()); + pNewGrafObj->SetEmptyPresObj(true); + } + + if ( pNewGrafObj->IsEmptyPresObj() ) + { + ::tools::Rectangle aRect( pNewGrafObj->GetLogicRect() ); + pNewGrafObj->AdjustToMaxRect( aRect ); + pNewGrafObj->SetOutlinerParaObject(std::nullopt); + pNewGrafObj->SetEmptyPresObj(false); + } + + if (pPage && pPage->IsPresObj(pPickObj)) + { + // Insert new PresObj into the list + pPage->InsertPresObj( pNewGrafObj, PresObjKind::Graphic ); + pNewGrafObj->SetUserCall(pPickObj->GetUserCall()); + } + + if (pImageMap) + pNewGrafObj->AppendUserData(std::unique_ptr<SdrObjUserData>(new SvxIMapInfo(*pImageMap))); + + ReplaceObjectAtView(pPickObj, *pPV, pNewGrafObj); // maybe ReplaceObjectAtView + + if( IsUndoEnabled() ) + EndUndo(); + } + else if (DND_ACTION_LINK == mnAction + && pPickObj + && !bIsGraphic + && pPickObj->IsClosedObj() + && !dynamic_cast< const SdrOle2Obj* >(pPickObj)) + { + // fill style change (fill object with graphic), independent of mnAction + // and thus of DND_ACTION_LINK or DND_ACTION_MOVE + if( IsUndoEnabled() ) + { + BegUndo(SdResId(STR_UNDO_DRAGDROP)); + AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoAttrObject(*pPickObj)); + EndUndo(); + } + + SfxItemSetFixed<XATTR_FILLSTYLE, XATTR_FILLBITMAP> aSet(mpDocSh->GetPool()); + + aSet.Put(XFillStyleItem(drawing::FillStyle_BITMAP)); + aSet.Put(XFillBitmapItem(rGraphic)); + pPickObj->SetMergedItemSetAndBroadcast(aSet); + } + + else if ( pPV ) + { + Size aSizePixel = rGraphic.GetSizePixel(); + + // create new object + Size aSize; + + if ( rGraphic.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel ) + { + ::OutputDevice* pOutDev = nullptr; + if( mpViewSh ) + pOutDev = mpViewSh->GetActiveWindow()->GetOutDev(); + + if( !pOutDev ) + pOutDev = Application::GetDefaultDevice(); + + if( pOutDev ) + aSize = pOutDev->PixelToLogic(rGraphic.GetPrefSize(), MapMode(MapUnit::Map100thMM)); + } + else + { + aSize = OutputDevice::LogicToLogic( rGraphic.GetPrefSize(), + rGraphic.GetPrefMapMode(), + MapMode( MapUnit::Map100thMM ) ); + } + + sal_Int32 nPreferredDPI = mrDoc.getImagePreferredDPI(); + + if (rGraphic.GetGfxLink().GetType() == GfxLinkType::NativePdf && nPreferredDPI == 0 && vcl::PDF_INSERT_MAGIC_SCALE_FACTOR > 1) + nPreferredDPI = Application::GetDefaultDevice()->GetDPIX() * vcl::PDF_INSERT_MAGIC_SCALE_FACTOR; + + if (nPreferredDPI > 0) + { + auto nWidth = o3tl::convert(aSizePixel.Width() / double(nPreferredDPI), o3tl::Length::in, o3tl::Length::mm100); + auto nHeight = o3tl::convert(aSizePixel.Height() / double(nPreferredDPI), o3tl::Length::in, o3tl::Length::mm100); + if (nWidth > 0 && nHeight > 0) + aSize = Size(nWidth, nHeight); + } + + pNewGrafObj = new SdrGrafObj(getSdrModelFromSdrView(), rGraphic, ::tools::Rectangle(rPos, aSize)); + + if (nPreferredDPI > 0) + { + // move to the center of insertion point + pNewGrafObj->NbcMove(Size(-aSize.Width() / 2, -aSize.Height() / 2)); + } + else + { + SdrPage* pPage = pPV->GetPage(); + Size aPageSize( pPage->GetSize() ); + aPageSize.AdjustWidth( -(pPage->GetLeftBorder() + pPage->GetRightBorder()) ); + aPageSize.AdjustHeight( -(pPage->GetUpperBorder() + pPage->GetLowerBorder()) ); + pNewGrafObj->AdjustToMaxRect( ::tools::Rectangle( Point(), aPageSize ), true ); + } + + SdrInsertFlags nOptions = SdrInsertFlags::SETDEFLAYER; + bool bIsPresTarget = false; + + if ((mpViewSh + && mpViewSh->GetViewShell()!=nullptr + && mpViewSh->GetViewShell()->GetIPClient() + && mpViewSh->GetViewShell()->GetIPClient()->IsObjectInPlaceActive()) + || dynamic_cast<const ::sd::slidesorter::view::SlideSorterView* >(this)) + nOptions |= SdrInsertFlags::DONTMARK; + + if( ( mnAction & DND_ACTION_MOVE ) && pPickObj && (pPickObj->IsEmptyPresObj() || pPickObj->GetUserCall()) ) + { + SdPage* pP = static_cast< SdPage* >( pPickObj->getSdrPageFromSdrObject() ); + + if ( pP && pP->IsMasterPage() ) + bIsPresTarget = pP->IsPresObj(pPickObj); + } + + if( ( mnAction & DND_ACTION_MOVE ) && pPickObj && !bIsPresTarget ) + { + // replace object + if (pImageMap) + pNewGrafObj->AppendUserData(std::unique_ptr<SdrObjUserData>(new SvxIMapInfo(*pImageMap))); + + ::tools::Rectangle aPickObjRect(pPickObj->GetCurrentBoundRect()); + Size aPickObjSize(aPickObjRect.GetSize()); + ::tools::Rectangle aObjRect(pNewGrafObj->GetCurrentBoundRect()); + Size aObjSize(aObjRect.GetSize()); + + Fraction aScaleWidth(aPickObjSize.Width(), aObjSize.Width()); + Fraction aScaleHeight(aPickObjSize.Height(), aObjSize.Height()); + pNewGrafObj->NbcResize(aObjRect.TopLeft(), aScaleWidth, aScaleHeight); + + Point aVec = aPickObjRect.TopLeft() - aObjRect.TopLeft(); + pNewGrafObj->NbcMove(Size(aVec.X(), aVec.Y())); + + const bool bUndo = IsUndoEnabled(); + + if( bUndo ) + BegUndo(SdResId(STR_UNDO_DRAGDROP)); + pNewGrafObj->NbcSetLayer(pPickObj->GetLayer()); + SdrPage* pP = pPV->GetPage(); + pP->InsertObject(pNewGrafObj); + if( bUndo ) + { + AddUndo(mrDoc.GetSdrUndoFactory().CreateUndoNewObject(*pNewGrafObj)); + AddUndo(mrDoc.GetSdrUndoFactory().CreateUndoDeleteObject(*pPickObj)); + } + pP->RemoveObject(pPickObj->GetOrdNum()); + + if( bUndo ) + { + EndUndo(); + } + else + { + SdrObject::Free(pPickObj); + } + mnAction = DND_ACTION_COPY; + } + else + { + bool bSuccess = InsertObjectAtView(pNewGrafObj, *pPV, nOptions); + if (!bSuccess) + pNewGrafObj = nullptr; + else if (pImageMap) + pNewGrafObj->AppendUserData(std::unique_ptr<SdrObjUserData>(new SvxIMapInfo(*pImageMap))); + } + } + + rAction = mnAction; + + return pNewGrafObj; +} + +void View::InsertMediaURL( const OUString& rMediaURL, sal_Int8& rAction, + const Point& rPos, const Size& rSize, + bool const bLink ) +{ + OUString realURL; + if (bLink) + { + realURL = rMediaURL; + } + else + { + uno::Reference<frame::XModel> const xModel( + GetDoc().GetObjectShell()->GetModel()); +#if HAVE_FEATURE_AVMEDIA + bool const bRet = ::avmedia::EmbedMedia(xModel, rMediaURL, realURL); + if (!bRet) { return; } +#else + return; +#endif + } + + InsertMediaObj( realURL, "application/vnd.sun.star.media", rAction, rPos, rSize ); +} + +SdrMediaObj* View::InsertMediaObj( const OUString& rMediaURL, const OUString& rMimeType, sal_Int8& rAction, + const Point& rPos, const Size& rSize ) +{ + SdrEndTextEdit(); + mnAction = rAction; + + SdrMediaObj* pNewMediaObj = nullptr; + SdrPageView* pPV = GetSdrPageView(); + SdrObject* pPickObj = GetEmptyPresentationObject( PresObjKind::Media ); + + if(pPV && dynamic_cast<const ::sd::slidesorter::view::SlideSorterView* >(this) ) + { + if(!pPV->GetPageRect().Contains(rPos)) + pPV = nullptr; + } + + if( mnAction == DND_ACTION_LINK && pPV && dynamic_cast< SdrMediaObj *>( pPickObj ) ) + { + pNewMediaObj = static_cast< SdrMediaObj* >( pPickObj->CloneSdrObject(pPickObj->getSdrModelFromSdrObject()) ); + pNewMediaObj->setURL( rMediaURL, ""/*TODO?*/, rMimeType ); + + BegUndo(SdResId(STR_UNDO_DRAGDROP)); + ReplaceObjectAtView(pPickObj, *pPV, pNewMediaObj); + EndUndo(); + } + else if( pPV ) + { + ::tools::Rectangle aRect( rPos, rSize ); + SdrObjUserCall* pUserCall = nullptr; + if( pPickObj ) + { + aRect = pPickObj->GetLogicRect(); + pUserCall = pPickObj->GetUserCall(); // ReplaceObjectAtView can free pPickObj + } + + pNewMediaObj = new SdrMediaObj( + getSdrModelFromSdrView(), + aRect); + + bool bIsPres = false; + if( pPickObj ) + { + SdPage* pPage = static_cast< SdPage* >(pPickObj->getSdrPageFromSdrObject()); + bIsPres = pPage && pPage->IsPresObj(pPickObj); + if( bIsPres ) + { + pPage->InsertPresObj( pNewMediaObj, PresObjKind::Media ); + } + } + + if( pPickObj ) + ReplaceObjectAtView(pPickObj, *pPV, pNewMediaObj); + else + { + if (!InsertObjectAtView(pNewMediaObj, *pPV, SdrInsertFlags::SETDEFLAYER)) + pNewMediaObj = nullptr; + } + + OUString referer; + DrawDocShell * sh = GetDocSh(); + if (sh != nullptr && sh->HasName()) { + referer = sh->GetMedium()->GetName(); + } + + if (pNewMediaObj) + { + pNewMediaObj->setURL( rMediaURL, referer, rMimeType ); + + if( pPickObj ) + { + pNewMediaObj->AdjustToMaxRect( aRect ); + if( bIsPres ) + pNewMediaObj->SetUserCall( pUserCall ); + } + } + } + + rAction = mnAction; + + return pNewMediaObj; +} + +/** + * Timer handler for InsertFile at Drop() + */ +IMPL_LINK_NOARG(View, DropInsertFileHdl, Timer *, void) +{ + DBG_ASSERT( mpViewSh, "sd::View::DropInsertFileHdl(), I need a view shell to work!" ); + if( !mpViewSh ) + return; + + SfxErrorContext aEc( ERRCTX_ERROR, mpViewSh->GetFrameWeld(), RID_SO_ERRCTX ); + ErrCode nError = ERRCODE_NONE; + + ::std::vector< OUString >::const_iterator aIter( maDropFileVector.begin() ); + + while( (aIter != maDropFileVector.end()) && !nError ) + { + OUString aCurrentDropFile( *aIter ); + INetURLObject aURL( aCurrentDropFile ); + bool bHandled = false; + + if( aURL.GetProtocol() == INetProtocol::NotValid ) + { + OUString aURLStr; + osl::FileBase::getFileURLFromSystemPath( aCurrentDropFile, aURLStr ); + aURL = INetURLObject( aURLStr ); + } + + GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter(); + Graphic aGraphic; + + aCurrentDropFile = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + +#if HAVE_FEATURE_AVMEDIA + if( !::avmedia::MediaWindow::isMediaURL( aCurrentDropFile, ""/*TODO?*/ ) ) +#else +#endif + { + if( !rGraphicFilter.ImportGraphic( aGraphic, aURL ) ) + { + sal_Int8 nTempAction = ( aIter == maDropFileVector.begin() ) ? mnAction : 0; + const bool bLink = ( ( nTempAction & DND_ACTION_LINK ) != 0 ); + SdrGrafObj* pGrafObj = InsertGraphic( aGraphic, nTempAction, maDropPos, nullptr, nullptr ); + if(pGrafObj && bLink) + { + pGrafObj->SetGraphicLink( aCurrentDropFile ); + } + + // return action from first inserted graphic + if( aIter == maDropFileVector.begin() ) + mnAction = nTempAction; + + bHandled = true; + } + if (!bHandled) + { + std::shared_ptr<const SfxFilter> pFoundFilter; + SfxMedium aSfxMedium( aCurrentDropFile, StreamMode::READ | StreamMode::SHARE_DENYNONE ); + ErrCode nErr = SfxGetpApp()->GetFilterMatcher().GuessFilter( aSfxMedium, pFoundFilter ); + + if( pFoundFilter && !nErr ) + { + ::std::vector< OUString > aFilterVector; + OUString aFilterName = pFoundFilter->GetFilterName(); + OUString aLowerAsciiFileName = aCurrentDropFile.toAsciiLowerCase(); + + FuInsertFile::GetSupportedFilterVector( aFilterVector ); + + if( ( ::std::find( aFilterVector.begin(), aFilterVector.end(), pFoundFilter->GetMimeType() ) != aFilterVector.end() ) || + aFilterName.indexOf( "Text" ) != -1 || + aFilterName.indexOf( "Rich" ) != -1 || + aFilterName.indexOf( "RTF" ) != -1 || + aFilterName.indexOf( "HTML" ) != -1 || + aLowerAsciiFileName.indexOf(".sdd") != -1 || + aLowerAsciiFileName.indexOf(".sda") != -1 || + aLowerAsciiFileName.indexOf(".sxd") != -1 || + aLowerAsciiFileName.indexOf(".sxi") != -1 || + aLowerAsciiFileName.indexOf(".std") != -1 || + aLowerAsciiFileName.indexOf(".sti") != -1 ) + { + ::sd::Window* pWin = mpViewSh->GetActiveWindow(); + SfxRequest aReq(SID_INSERTFILE, ::SfxCallMode::SLOT, mrDoc.GetItemPool()); + SfxStringItem aItem1( ID_VAL_DUMMY0, aCurrentDropFile ), aItem2( ID_VAL_DUMMY1, pFoundFilter->GetFilterName() ); + + aReq.AppendItem( aItem1 ); + aReq.AppendItem( aItem2 ); + FuInsertFile::Create( mpViewSh, pWin, this, &mrDoc, aReq ); + bHandled = true; + } + } + } + } + +#if HAVE_FEATURE_AVMEDIA + if (!bHandled) + { + bool bShallowDetect = ::avmedia::MediaWindow::isMediaURL(aCurrentDropFile, ""/*TODO?*/); + if (bShallowDetect) + { + mxDropMediaSizeListener.set(new avmedia::PlayerListener( + [this, aCurrentDropFile](const css::uno::Reference<css::media::XPlayer>& rPlayer){ + SolarMutexGuard g; + + css::awt::Size aSize = rPlayer->getPreferredPlayerWindowSize(); + Size aPrefSize(aSize.Width, aSize.Height); + + if (aPrefSize.Width() && aPrefSize.Height()) + { + ::sd::Window* pWin = mpViewSh->GetActiveWindow(); + + if( pWin ) + aPrefSize = pWin->PixelToLogic(aPrefSize, MapMode(MapUnit::Map100thMM)); + else + aPrefSize = Application::GetDefaultDevice()->PixelToLogic(aPrefSize, MapMode(MapUnit::Map100thMM)); + } + else + aPrefSize = Size( 5000, 5000 ); + + InsertMediaURL(aCurrentDropFile, mnAction, maDropPos, aPrefSize, true); + + mxDropMediaSizeListener.clear(); + })); + } + bHandled = bShallowDetect && ::avmedia::MediaWindow::isMediaURL(aCurrentDropFile, ""/*TODO?*/, true, mxDropMediaSizeListener); + } +#endif + + if (!bHandled) + { + if( mnAction & DND_ACTION_LINK ) + static_cast< DrawViewShell* >( mpViewSh )->InsertURLButton( aCurrentDropFile, aCurrentDropFile, OUString(), &maDropPos ); + else + { + if( mpViewSh ) + { + try + { + //TODO/MBA: testing + OUString aName; + uno::Sequence < beans::PropertyValue > aMedium{ comphelper::makePropertyValue( + "URL", aCurrentDropFile) }; + + uno::Reference < embed::XEmbeddedObject > xObj = mpDocSh->GetEmbeddedObjectContainer(). + InsertEmbeddedObject( aMedium, aName ); + + uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY ); + if ( xPersist.is()) + { + // TODO/LEAN: VisualArea access can switch the object to running state + sal_Int64 nAspect = embed::Aspects::MSOLE_CONTENT; + + xPersist->storeOwn(); + + awt::Size aSz; + try + { + aSz = xObj->getVisualAreaSize( nAspect ); + } + catch( embed::NoVisualAreaSizeException& ) + { + // the default size will be set later + } + + Size aSize( aSz.Width, aSz.Height ); + ::tools::Rectangle aRect; + + if (!aSize.Width() || !aSize.Height()) + { + aSize.setWidth( 1410 ); + aSize.setHeight( 1000 ); + } + + aRect = ::tools::Rectangle( maDropPos, aSize ); + + SdrOle2Obj* pOleObj = new SdrOle2Obj( + getSdrModelFromSdrView(), + svt::EmbeddedObjectRef(xObj, nAspect), + aName, + aRect); + SdrInsertFlags nOptions = SdrInsertFlags::SETDEFLAYER; + + if (mpViewSh != nullptr) + { + OSL_ASSERT (mpViewSh->GetViewShell()!=nullptr); + SfxInPlaceClient* pIpClient = + mpViewSh->GetViewShell()->GetIPClient(); + if (pIpClient!=nullptr && pIpClient->IsObjectInPlaceActive()) + nOptions |= SdrInsertFlags::DONTMARK; + } + + if (InsertObjectAtView( pOleObj, *GetSdrPageView(), nOptions )) + pOleObj->SetLogicRect( aRect ); + aSz.Width = aRect.GetWidth(); + aSz.Height = aRect.GetHeight(); + xObj->setVisualAreaSize( nAspect,aSz ); + } + } + catch( uno::Exception& ) + { + nError = ERRCODE_IO_GENERAL; + // TODO/LATER: better error handling + } + } + } + } + + ++aIter; + } + + if( nError ) + ErrorHandler::HandleError( nError ); +} + +/** + * Timer handler for Errorhandling at Drop() + */ +IMPL_LINK_NOARG(View, DropErrorHdl, Timer *, void) +{ + vcl::Window* pWin = mpViewSh ? mpViewSh->GetActiveWindow() : nullptr; + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pWin ? pWin->GetFrameWeld() : nullptr, + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); +} + +/** + * @returns StyleSheet from selection + */ +SfxStyleSheet* View::GetStyleSheet() const +{ + return SdrView::GetStyleSheet(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/sdview5.cxx b/sd/source/ui/view/sdview5.cxx new file mode 100644 index 000000000..c3ac066bc --- /dev/null +++ b/sd/source/ui/view/sdview5.cxx @@ -0,0 +1,118 @@ +/* -*- 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 <sdpage.hxx> +#include <View.hxx> +#include <pres.hxx> + +#include <svx/svdpagv.hxx> + +namespace sd { + +static bool implIsMultiPresObj( PresObjKind eKind ) +{ + switch( eKind ) + { + case PresObjKind::Outline: + case PresObjKind::Graphic: + case PresObjKind::Object: + case PresObjKind::Chart: + case PresObjKind::OrgChart: + case PresObjKind::Table: + case PresObjKind::Media: + return true; + default: + return false; + } +} + +SdPage* View::GetPage() +{ + SdPage* pPage = nullptr; + SdrPageView* pPV = GetSdrPageView(); + if( pPV ) + { + pPage = static_cast< SdPage* >( pPV->GetPage() ); + } + + return pPage; +} + +// returns selected object in case there's just one object in the selection +SdrObject* View::GetSelectedSingleObject(SdPage const * pPage) +{ + SdrObject* pRet = nullptr; + if( pPage ) + { + // first try selected shape + if ( AreObjectsMarked() ) + { + const SdrMarkList& rMarkList = GetMarkedObjectList(); + + if (rMarkList.GetMarkCount() == 1) + { + SdrMark* pMark = rMarkList.GetMark(0); + pRet = pMark->GetMarkedSdrObj(); + } + } + } + + return pRet; +} + +SdrObject* View::GetEmptyPresentationObject( PresObjKind eKind ) +{ + SdPage* pPage = GetPage(); + SdrObject* pEmptyObj = nullptr; + + if ( pPage && !pPage->IsMasterPage() ) { + SdrObject* pObj = GetSelectedSingleObject( pPage ); + + if( pObj && pObj->IsEmptyPresObj() && implIsMultiPresObj( pPage->GetPresObjKind(pObj) ) ) + pEmptyObj = pObj; + + // try to find empty pres obj of same type + if( !pEmptyObj ) + { + int nIndex = 1; + do + { + pEmptyObj = pPage->GetPresObj(eKind, nIndex++ ); + } + while( (pEmptyObj != nullptr) && (!pEmptyObj->IsEmptyPresObj()) ); + } + + // last try to find empty pres obj of multiple type + if( !pEmptyObj ) + { + const std::list< SdrObject* >& rShapes = pPage->GetPresentationShapeList().getList(); + + auto iter = std::find_if(rShapes.begin(), rShapes.end(), + [&pPage](SdrObject* pShape) { return pShape->IsEmptyPresObj() && implIsMultiPresObj(pPage->GetPresObjKind(pShape)); }); + if (iter != rShapes.end()) + pEmptyObj = (*iter); + } + } + + return pEmptyObj; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/sdwindow.cxx b/sd/source/ui/view/sdwindow.cxx new file mode 100644 index 000000000..f639b463e --- /dev/null +++ b/sd/source/ui/view/sdwindow.cxx @@ -0,0 +1,1097 @@ +/* -*- 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 <Window.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/request.hxx> + +#include <sfx2/viewfrm.hxx> +#include <svx/svxids.hrc> + +#include <editeng/outliner.hxx> +#include <editeng/editview.hxx> +#include <editeng/editeng.hxx> + +#include <app.hrc> +#include <ViewShell.hxx> +#include <DrawViewShell.hxx> +#include <DrawDocShell.hxx> +#include <PresentationViewShell.hxx> +#include <View.hxx> +#include <FrameView.hxx> +#include <OutlineViewShell.hxx> +#include <OutlineView.hxx> +#include <drawdoc.hxx> +#include <WindowUpdater.hxx> +#include <ViewShellBase.hxx> +#include <uiobject.hxx> + +#include <officecfg/Office/Common.hxx> +#include <sal/log.hxx> +#include <tools/debug.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/settings.hxx> +#include <comphelper/lok.hxx> +#include <sfx2/lokhelper.hxx> + +namespace sd { + +#define SCROLL_LINE_FACT 0.05 ///< factor for line scrolling +#define SCROLL_PAGE_FACT 0.5 ///< factor for page scrolling +#define SCROLL_SENSITIVE 20 ///< sensitive area in pixel +#define ZOOM_MULTIPLICATOR 10000 ///< multiplier to avoid rounding errors +#define MIN_ZOOM 5 ///< minimal zoom factor +#define MAX_ZOOM 3000 ///< maximal zoom factor + +Window::Window(vcl::Window* pParent) + : vcl::Window(pParent, WinBits(WB_CLIPCHILDREN | WB_DIALOGCONTROL)), + DropTargetHelper( this ), + maWinPos(0, 0), // precautionary; but the values should be set + maViewOrigin(0, 0), // again from the owner of the window + maViewSize(1000, 1000), + maPrevSize(-1,-1), + mnMinZoom(MIN_ZOOM), + mnMaxZoom(MAX_ZOOM), + mbMinZoomAutoCalc(false), + mbCenterAllowed(true), + mnTicks (0), + mpViewShell(nullptr), + mbUseDropScroll (true) +{ + SetDialogControlFlags( DialogControlFlags::Return | DialogControlFlags::WantFocus ); + + MapMode aMap(GetMapMode()); + aMap.SetMapUnit(MapUnit::Map100thMM); + SetMapMode(aMap); + + // with it, the vcl::WindowColor is used in the slide mode + SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetWindowColor() ) ); + + // adjust contrast mode initially + bool bUseContrast = GetSettings().GetStyleSettings().GetHighContrastMode(); + GetOutDev()->SetDrawMode( bUseContrast + ? sd::OUTPUT_DRAWMODE_CONTRAST + : sd::OUTPUT_DRAWMODE_COLOR ); + + // #i78183# Added after discussed with AF + EnableRTL(false); +} + +Window::~Window() +{ + disposeOnce(); +} + +void Window::dispose() +{ + if (mpViewShell != nullptr) + { + WindowUpdater* pWindowUpdater = mpViewShell->GetWindowUpdater(); + if (pWindowUpdater != nullptr) + pWindowUpdater->UnregisterWindow (this); + } + DropTargetHelper::dispose(); + vcl::Window::dispose(); +} + +void Window::SetViewShell (ViewShell* pViewSh) +{ + WindowUpdater* pWindowUpdater = nullptr; + // Unregister at device updater of old view shell. + if (mpViewShell != nullptr) + { + pWindowUpdater = mpViewShell->GetWindowUpdater(); + if (pWindowUpdater != nullptr) + pWindowUpdater->UnregisterWindow (this); + } + + mpViewShell = pViewSh; + + // Register at device updater of new view shell + if (mpViewShell != nullptr) + { + pWindowUpdater = mpViewShell->GetWindowUpdater(); + if (pWindowUpdater != nullptr) + pWindowUpdater->RegisterWindow (this); + } +} + +ViewShell* Window::GetViewShell() +{ + return mpViewShell; +} + +void Window::CalcMinZoom() +{ + // Are we entitled to change the minimal zoom factor? + if ( !mbMinZoomAutoCalc ) + return; + + // Get current zoom factor. + ::tools::Long nZoom = GetZoom(); + + // Get the rectangle of the output area in logical coordinates + // and calculate the scaling factors that would lead to the view + // area (also called application area) to completely fill the + // window. + Size aWinSize = PixelToLogic(GetOutputSizePixel()); + sal_uLong nX = static_cast<sal_uLong>(static_cast<double>(aWinSize.Width()) + * double(ZOOM_MULTIPLICATOR) / static_cast<double>(maViewSize.Width())); + sal_uLong nY = static_cast<sal_uLong>(static_cast<double>(aWinSize.Height()) + * double(ZOOM_MULTIPLICATOR) / static_cast<double>(maViewSize.Height())); + + // Decide whether to take the larger or the smaller factor. + sal_uLong nFact = std::min(nX, nY); + + // The factor is transformed according to the current zoom factor. + nFact = nFact * nZoom / ZOOM_MULTIPLICATOR; + mnMinZoom = std::max(sal_uInt16(MIN_ZOOM), static_cast<sal_uInt16>(nFact)); + + // If the current zoom factor is smaller than the calculated minimal + // zoom factor then set the new minimal factor as the current zoom + // factor. + if ( nZoom < static_cast<::tools::Long>(mnMinZoom) ) + SetZoomFactor(mnMinZoom); +} + +void Window::SetMinZoom (::tools::Long nMin) +{ + mnMinZoom = static_cast<sal_uInt16>(nMin); +} + +void Window::SetMaxZoom (::tools::Long nMax) +{ + mnMaxZoom = static_cast<sal_uInt16>(nMax); +} + +::tools::Long Window::GetZoom() const +{ + if( GetMapMode().GetScaleX().GetDenominator() ) + { + return ::tools::Long(GetMapMode().GetScaleX() * 100); + } + else + { + return 0; + } +} + +void Window::Resize() +{ + vcl::Window::Resize(); + CalcMinZoom(); + + if( mpViewShell && mpViewShell->GetViewFrame() ) + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_ATTR_ZOOMSLIDER ); +} + +void Window::PrePaint(vcl::RenderContext& /*rRenderContext*/) +{ + if ( mpViewShell ) + mpViewShell->PrePaint(); +} + +void Window::Paint(vcl::RenderContext& /*rRenderContext*/, const ::tools::Rectangle& rRect) +{ + if ( mpViewShell ) + mpViewShell->Paint(rRect, this); +} + +void Window::KeyInput(const KeyEvent& rKEvt) +{ + if (getenv("SD_DEBUG") && rKEvt.GetKeyCode().GetCode() == KEY_F12 && mpViewShell) + { + mpViewShell->GetDoc()->dumpAsXml(nullptr); + if (OutlinerView *pOLV = mpViewShell->GetView()->GetTextEditOutlinerView()) + pOLV->GetEditView().GetEditEngine()->dumpAsXmlEditDoc(nullptr); + return; + } + + if (!(mpViewShell && mpViewShell->KeyInput(rKEvt, this))) + { + if (mpViewShell && rKEvt.GetKeyCode().GetCode() == KEY_ESCAPE) + { + mpViewShell->GetViewShell()->Escape(); + } + else + { + vcl::Window::KeyInput(rKEvt); + } + } +} + +void Window::MouseButtonDown(const MouseEvent& rMEvt) +{ + if ( mpViewShell ) + mpViewShell->MouseButtonDown(rMEvt, this); +} + +void Window::MouseMove(const MouseEvent& rMEvt) +{ + if ( mpViewShell ) + mpViewShell->MouseMove(rMEvt, this); +} + +void Window::MouseButtonUp(const MouseEvent& rMEvt) +{ + mnTicks = 0; + + if ( mpViewShell ) + mpViewShell->MouseButtonUp(rMEvt, this); +} + +void Window::Command(const CommandEvent& rCEvt) +{ + if (mpViewShell) + mpViewShell->Command(rCEvt, this); + //pass at least alt press/release to parent impl + if (rCEvt.GetCommand() == CommandEventId::ModKeyChange) + vcl::Window::Command(rCEvt); + //show the text edit outliner view cursor + else if (mpViewShell && !HasFocus() && rCEvt.GetCommand() == CommandEventId::CursorPos) + { + // tdf#138855 Getting Focus may destroy TextEditOutlinerView so Grab if + // text editing active, but fetch the TextEditOutlinerView post-grab + if (mpViewShell->GetView()->IsTextEdit()) + { + GrabFocus(); + OutlinerView* pOLV = mpViewShell->GetView()->GetTextEditOutlinerView(); + if (pOLV && this == pOLV->GetWindow()) + pOLV->ShowCursor(); + } + } +} + +bool Window::EventNotify( NotifyEvent& rNEvt ) +{ + bool bResult = false; + if ( mpViewShell ) + { + bResult = mpViewShell->Notify(rNEvt, this); + } + if( !bResult ) + bResult = vcl::Window::EventNotify(rNEvt); + + return bResult; +} + +void Window::RequestHelp(const HelpEvent& rEvt) +{ + if (!mpViewShell || !mpViewShell->RequestHelp(rEvt)) + vcl::Window::RequestHelp( rEvt ); +} + +/** + * Set the position of the upper left corner from the visible area of the + * window. + */ +void Window::SetWinViewPos(const Point& rPnt) +{ + maWinPos = rPnt; +} + +/** + * Set origin of the representation in respect to the whole working area. + */ +void Window::SetViewOrigin(const Point& rPnt) +{ + maViewOrigin = rPnt; +} + +/** + * Set size of the whole working area which can be seen with the window. + */ +void Window::SetViewSize(const Size& rSize) +{ + maViewSize = rSize; + CalcMinZoom(); +} + +void Window::SetCenterAllowed (bool bIsAllowed) +{ + mbCenterAllowed = bIsAllowed; +} + +::tools::Long Window::SetZoomFactor(::tools::Long nZoom) +{ + // Clip the zoom factor to the valid range marked by nMinZoom as + // calculated by CalcMinZoom() and the constant MAX_ZOOM. + if ( nZoom > MAX_ZOOM ) + nZoom = MAX_ZOOM; + if ( nZoom < static_cast<::tools::Long>(mnMinZoom) ) + nZoom = mnMinZoom; + + // Set the zoom factor at the window's map mode. + if (!comphelper::LibreOfficeKit::isActive()) + { + MapMode aMap(GetMapMode()); + aMap.SetScaleX(Fraction(nZoom, 100)); + aMap.SetScaleY(Fraction(nZoom, 100)); + SetMapMode(aMap); + } + + // invalidate previous size - it was relative to the old scaling + maPrevSize = Size(-1,-1); + + // Update the map mode's origin (to what effect?). + UpdateMapOrigin(); + + // Update the view's snapping to the new zoom factor. + if ( auto pDrawViewShell = dynamic_cast< DrawViewShell *>( mpViewShell ) ) + pDrawViewShell->GetView()->RecalcLogicSnapMagnetic(*GetOutDev()); + + // Return the zoom factor just in case it has been changed above to lie + // inside the valid range. + return nZoom; +} + +void Window::SetZoomIntegral(::tools::Long nZoom) +{ + // Clip the zoom factor to the valid range marked by nMinZoom as + // previously calculated by <member>CalcMinZoom()</member> and the + // MAX_ZOOM constant. + if ( nZoom > MAX_ZOOM ) + nZoom = MAX_ZOOM; + if ( nZoom < static_cast<::tools::Long>(mnMinZoom) ) + nZoom = mnMinZoom; + + // Calculate the window's new origin. + Size aSize = PixelToLogic(GetOutputSizePixel()); + ::tools::Long nW = aSize.Width() * GetZoom() / nZoom; + ::tools::Long nH = aSize.Height() * GetZoom() / nZoom; + maWinPos.AdjustX((aSize.Width() - nW) / 2 ); + maWinPos.AdjustY((aSize.Height() - nH) / 2 ); + if ( maWinPos.X() < 0 ) maWinPos.setX( 0 ); + if ( maWinPos.Y() < 0 ) maWinPos.setY( 0 ); + + // Finally update this window's map mode to the given zoom factor that + // has been clipped to the valid range. + SetZoomFactor(nZoom); +} + +::tools::Long Window::GetZoomForRect( const ::tools::Rectangle& rZoomRect ) +{ + ::tools::Long nRetZoom = 100; + + if( (rZoomRect.GetWidth() != 0) && (rZoomRect.GetHeight() != 0)) + { + // Calculate the scale factors which will lead to the given + // rectangle being fully visible (when translated accordingly) as + // large as possible in the output area independently in both + // coordinate directions . + sal_uLong nX(0); + sal_uLong nY(0); + + const Size aWinSize( PixelToLogic(GetOutputSizePixel()) ); + if(rZoomRect.GetHeight()) + { + nX = static_cast<sal_uLong>(static_cast<double>(aWinSize.Height()) + * double(ZOOM_MULTIPLICATOR) / static_cast<double>(rZoomRect.GetHeight())); + } + + if(rZoomRect.GetWidth()) + { + nY = static_cast<sal_uLong>(static_cast<double>(aWinSize.Width()) + * double(ZOOM_MULTIPLICATOR) / static_cast<double>(rZoomRect.GetWidth())); + } + + // Use the smaller one of both so that the zoom rectangle will be + // fully visible with respect to both coordinate directions. + sal_uLong nFact = std::min(nX, nY); + + // Transform the current zoom factor so that it leads to the desired + // scaling. + nRetZoom = nFact * GetZoom() / ZOOM_MULTIPLICATOR; + + // Calculate the new origin. + if ( nFact == 0 ) + { + // Don't change anything if the scale factor is degenerate. + nRetZoom = GetZoom(); + } + else + { + // Clip the zoom factor to the valid range marked by nMinZoom as + // previously calculated by <member>CalcMinZoom()</member> and the + // MAX_ZOOM constant. + if ( nRetZoom > MAX_ZOOM ) + nRetZoom = MAX_ZOOM; + if ( nRetZoom < static_cast<::tools::Long>(mnMinZoom) ) + nRetZoom = mnMinZoom; + } + } + + return nRetZoom; +} + +/** Recalculate the zoom factor and translation so that the given rectangle + is displayed centered and as large as possible while still being fully + visible in the window. +*/ +::tools::Long Window::SetZoomRect (const ::tools::Rectangle& rZoomRect) +{ + ::tools::Long nNewZoom = 100; + + if (rZoomRect.GetWidth() == 0 || rZoomRect.GetHeight() == 0) + { + // The given rectangle is degenerate. Use the default zoom factor + // (above) of 100%. + SetZoomIntegral(nNewZoom); + } + else + { + Point aPos = rZoomRect.TopLeft(); + // Transform the output area from pixel coordinates into logical + // coordinates. + Size aWinSize = PixelToLogic(GetOutputSizePixel()); + // Paranoia! The degenerate case of zero width or height has been + // taken care of above. + DBG_ASSERT(rZoomRect.GetWidth(), "ZoomRect-Width = 0!"); + DBG_ASSERT(rZoomRect.GetHeight(), "ZoomRect-Height = 0!"); + + // Calculate the scale factors which will lead to the given + // rectangle being fully visible (when translated accordingly) as + // large as possible in the output area independently in both + // coordinate directions . + sal_uLong nX(0); + sal_uLong nY(0); + + if(rZoomRect.GetHeight()) + { + nX = static_cast<sal_uLong>(static_cast<double>(aWinSize.Height()) + * double(ZOOM_MULTIPLICATOR) / static_cast<double>(rZoomRect.GetHeight())); + } + + if(rZoomRect.GetWidth()) + { + nY = static_cast<sal_uLong>(static_cast<double>(aWinSize.Width()) + * double(ZOOM_MULTIPLICATOR) / static_cast<double>(rZoomRect.GetWidth())); + } + + // Use the smaller one of both so that the zoom rectangle will be + // fully visible with respect to both coordinate directions. + sal_uLong nFact = std::min(nX, nY); + + // Transform the current zoom factor so that it leads to the desired + // scaling. + ::tools::Long nZoom = nFact * GetZoom() / ZOOM_MULTIPLICATOR; + + // Calculate the new origin. + if ( nFact == 0 ) + { + // Don't change anything if the scale factor is degenerate. + nNewZoom = GetZoom(); + } + else + { + // Calculate the new window position that centers the given + // rectangle on the screen. + if ( nZoom > MAX_ZOOM ) + nFact = nFact * MAX_ZOOM / nZoom; + + maWinPos = maViewOrigin + aPos; + + aWinSize.setWidth( static_cast<::tools::Long>(static_cast<double>(aWinSize.Width()) * double(ZOOM_MULTIPLICATOR) / static_cast<double>(nFact)) ); + maWinPos.AdjustX((rZoomRect.GetWidth() - aWinSize.Width()) / 2 ); + aWinSize.setHeight( static_cast<::tools::Long>(static_cast<double>(aWinSize.Height()) * double(ZOOM_MULTIPLICATOR) / static_cast<double>(nFact)) ); + maWinPos.AdjustY((rZoomRect.GetHeight() - aWinSize.Height()) / 2 ); + + if ( maWinPos.X() < 0 ) maWinPos.setX( 0 ); + if ( maWinPos.Y() < 0 ) maWinPos.setY( 0 ); + + // Adapt the window's map mode to the new zoom factor. + nNewZoom = SetZoomFactor(nZoom); + } + } + + return nNewZoom; +} + +void Window::SetMinZoomAutoCalc (bool bAuto) +{ + mbMinZoomAutoCalc = bAuto; +} + +/** + * Calculate and set new MapMode origin. + * If aWinPos.X()/Y() == -1, then we center the corresponding position (e.g. for + * initialization). + */ +void Window::UpdateMapOrigin(bool bInvalidate) +{ + bool bChanged = false; + const Size aWinSize = PixelToLogic(GetOutputSizePixel()); + + if ( mbCenterAllowed ) + { + if( maPrevSize != Size(-1,-1) ) + { + // keep view centered around current pos, when window + // resizes + maWinPos.AdjustX( -((aWinSize.Width() - maPrevSize.Width()) / 2) ); + maWinPos.AdjustY( -((aWinSize.Height() - maPrevSize.Height()) / 2) ); + bChanged = true; + } + + if ( maWinPos.X() > maViewSize.Width() - aWinSize.Width() ) + { + maWinPos.setX( maViewSize.Width() - aWinSize.Width() ); + bChanged = true; + } + if ( maWinPos.Y() > maViewSize.Height() - aWinSize.Height() ) + { + maWinPos.setY( maViewSize.Height() - aWinSize.Height() ); + bChanged = true; + } + if ( aWinSize.Width() > maViewSize.Width() || maWinPos.X() < 0 ) + { + maWinPos.setX( maViewSize.Width() / 2 - aWinSize.Width() / 2 ); + bChanged = true; + } + if ( aWinSize.Height() > maViewSize.Height() || maWinPos.Y() < 0 ) + { + maWinPos.setY( maViewSize.Height() / 2 - aWinSize.Height() / 2 ); + bChanged = true; + } + } + + UpdateMapMode (); + + maPrevSize = aWinSize; + + // When tiled rendering, the above UpdateMapMode() call doesn't touch the map mode. + if (bChanged && bInvalidate && !comphelper::LibreOfficeKit::isActive()) + Invalidate(); +} + +void Window::UpdateMapMode() +{ + maWinPos -= maViewOrigin; + Size aPix(maWinPos.X(), maWinPos.Y()); + aPix = LogicToPixel(aPix); + // Size has to be a multiple of BRUSH_SIZE due to the correct depiction of + // pattern + // #i2237# + // removed old stuff here which still forced zoom to be + // %BRUSH_SIZE which is outdated now + + if (dynamic_cast< DrawViewShell *>( mpViewShell )) + { + // page should not "stick" to the window border + if (aPix.Width() == 0) + { + // #i2237# + // Since BRUSH_SIZE alignment is outdated now, i use the + // former constant here directly + aPix.AdjustWidth( -8 ); + } + if (aPix.Height() == 0) + { + // #i2237# + // Since BRUSH_SIZE alignment is outdated now, i use the + // former constant here directly + aPix.AdjustHeight( -8 ); + } + } + + aPix = PixelToLogic(aPix); + maWinPos.setX( aPix.Width() ); + maWinPos.setY( aPix.Height() ); + Point aNewOrigin (-maWinPos.X(), -maWinPos.Y()); + maWinPos += maViewOrigin; + + if (!comphelper::LibreOfficeKit::isActive()) + { + MapMode aMap(GetMapMode()); + aMap.SetOrigin(aNewOrigin); + SetMapMode(aMap); + } +} + +/** + * @returns X position of the visible area as fraction (< 1) of the whole + * working area. + */ +double Window::GetVisibleX() const +{ + return maViewSize.Width() == 0 ? 0 : (static_cast<double>(maWinPos.X()) / maViewSize.Width()); +} + +/** + * @returns Y position of the visible area as fraction (< 1) of the whole + * working area. + */ +double Window::GetVisibleY() const +{ + return maViewSize.Height() == 0 ? 0 : (static_cast<double>(maWinPos.Y()) / maViewSize.Height()); +} + +/** + * Set x and y position of the visible area as fraction (< 1) of the whole + * working area. Negative values are ignored. + */ +void Window::SetVisibleXY(double fX, double fY) +{ + ::tools::Long nOldX = maWinPos.X(); + ::tools::Long nOldY = maWinPos.Y(); + + if ( fX >= 0 ) + maWinPos.setX( static_cast<::tools::Long>(fX * maViewSize.Width()) ); + if ( fY >= 0 ) + maWinPos.setY( static_cast<::tools::Long>(fY * maViewSize.Height()) ); + UpdateMapOrigin(false); + Scroll(nOldX - maWinPos.X(), nOldY - maWinPos.Y(), ScrollFlags::Children); + PaintImmediately(); +} + +/** + * @returns width of the visible area in proportion to the width of the whole + * working area. + */ +double Window::GetVisibleWidth() const +{ + Size aWinSize = PixelToLogic(GetOutputSizePixel()); + if ( aWinSize.Width() > maViewSize.Width() ) + aWinSize.setWidth( maViewSize.Width() ); + return + maViewSize.Width() == 0 ? 0 : (static_cast<double>(aWinSize.Width()) / maViewSize.Width()); +} + +/** + * @returns height of the visible area in proportion to the height of the whole + * working area. + */ +double Window::GetVisibleHeight() const +{ + Size aWinSize = PixelToLogic(GetOutputSizePixel()); + if ( aWinSize.Height() > maViewSize.Height() ) + aWinSize.setHeight( maViewSize.Height() ); + return maViewSize.Height() == 0 + ? 0 : (static_cast<double>(aWinSize.Height()) / maViewSize.Height()); +} + +Point Window::GetVisibleCenter() +{ + Point aPos = ::tools::Rectangle(Point(), GetOutputSizePixel()).Center(); + + // For LOK + bool bMapModeWasEnabled(IsMapModeEnabled()); + EnableMapMode(/*true*/); + aPos = PixelToLogic(aPos); + EnableMapMode(bMapModeWasEnabled); + + return aPos; +} + +/** + * @returns width of a scroll column in proportion to the width of the whole + * working area. + */ +double Window::GetScrlLineWidth() const +{ + return (GetVisibleWidth() * SCROLL_LINE_FACT); +} + +/** + * @returns height of a scroll column in proportion to the height of the whole + * working area. + */ +double Window::GetScrlLineHeight() const +{ + return (GetVisibleHeight() * SCROLL_LINE_FACT); +} + +/** + * @returns width of a scroll page in proportion to the width of the whole + * working area. + */ +double Window::GetScrlPageWidth() const +{ + return (GetVisibleWidth() * SCROLL_PAGE_FACT); +} + +/** + * @returns height of a scroll page in proportion to the height of the whole + * working area. + */ +double Window::GetScrlPageHeight() const +{ + return (GetVisibleHeight() * SCROLL_PAGE_FACT); +} + +/** + * Deactivate window. + */ +void Window::LoseFocus() +{ + mnTicks = 0; + vcl::Window::LoseFocus (); +} + +/** + * Activate window. + */ +void Window::GrabFocus() +{ + mnTicks = 0; + vcl::Window::GrabFocus (); +} + +void Window::DataChanged( const DataChangedEvent& rDCEvt ) +{ + vcl::Window::DataChanged( rDCEvt ); + + /* Omit PRINTER by all documents which are not using a printer. + Omit FONTS and FONTSUBSTITUTION if no text output is available or if the + document does not allow text. */ + + if ( !((rDCEvt.GetType() == DataChangedEventType::PRINTER) || + (rDCEvt.GetType() == DataChangedEventType::DISPLAY) || + (rDCEvt.GetType() == DataChangedEventType::FONTS) || + (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) || + ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) && + (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))) ) + return; + + if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && + (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) ) + { + /* Rearrange or initiate Resize for scroll bars since the size of + the scroll bars my have changed. Within this, inside the resize- + handler, the size of the scroll bars will be asked from the + Settings. */ + Resize(); + + /* Re-set data, which are from system control or from Settings. May + have to re-set more data since the resolution may also has + changed. */ + if( mpViewShell ) + { + const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); + DrawModeFlags nOutputMode; + sal_uInt16 nPreviewSlot; + + if( rStyleSettings.GetHighContrastMode() ) + nOutputMode = sd::OUTPUT_DRAWMODE_CONTRAST; + else + nOutputMode = sd::OUTPUT_DRAWMODE_COLOR; + + if( rStyleSettings.GetHighContrastMode() + && officecfg::Office::Common::Accessibility::IsForPagePreviews::get() ) + nPreviewSlot = SID_PREVIEW_QUALITY_CONTRAST; + else + nPreviewSlot = SID_PREVIEW_QUALITY_COLOR; + + if( dynamic_cast< DrawViewShell *>( mpViewShell ) != nullptr ) + { + GetOutDev()->SetDrawMode( nOutputMode ); + mpViewShell->GetFrameView()->SetDrawMode( nOutputMode ); + Invalidate(); + } + + // Overwrite window color for OutlineView + if( dynamic_cast< OutlineViewShell *>( mpViewShell ) != nullptr ) + { + svtools::ColorConfig aColorConfig; + const Color aDocColor( aColorConfig.GetColorValue( svtools::DOCCOLOR ).nColor ); + SetBackground( Wallpaper( aDocColor ) ); + } + + SfxRequest aReq( nPreviewSlot, SfxCallMode::SLOT, mpViewShell->GetDocSh()->GetDoc()->GetItemPool() ); + mpViewShell->ExecReq( aReq ); + mpViewShell->Invalidate(); + mpViewShell->ArrangeGUIElements(); + + // re-create handles to show new outfit + if(dynamic_cast< DrawViewShell *>( mpViewShell ) != nullptr) + { + mpViewShell->GetView()->AdjustMarkHdl(); + } + } + } + + if ( (rDCEvt.GetType() == DataChangedEventType::DISPLAY) || + ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) && + (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) ) + { + /* Virtual devices, which also depends on the resolution or the + system control, should be updated. Otherwise, we should update + the virtual devices at least at DataChangedEventType::DISPLAY since some + systems allow to change the resolution and color depth during + runtime. Or the virtual devices have to be updated when the color + palette has changed since a different color matching can be used + when outputting. */ + } + + if ( rDCEvt.GetType() == DataChangedEventType::FONTS ) + { + /* If the document provides font choose boxes, we have to update + them. I don't know how this looks like (also not really me, I + only translated the comment ;). We may can handle it global. We + have to discuss it with PB, but he is ill at the moment. + Before we handle it here, discuss it with PB and me. */ + } + + if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) || + (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ) + { + /* Do reformatting since the fonts of the document may no longer + exist, or exist now, or are replaced with others. */ + if( mpViewShell ) + { + DrawDocShell* pDocSh = mpViewShell->GetDocSh(); + if( pDocSh ) + pDocSh->SetPrinter( pDocSh->GetPrinter( true ) ); + } + } + + if ( rDCEvt.GetType() == DataChangedEventType::PRINTER ) + { + /* I don't know how the handling should look like. Maybe we delete a + printer and look what we have to do. Maybe I have to add + something to the VCL, in case the used printer is deleted. + Otherwise I may recalculate the formatting here if the current + printer is destroyed. */ + if( mpViewShell ) + { + DrawDocShell* pDocSh = mpViewShell->GetDocSh(); + if( pDocSh ) + pDocSh->SetPrinter( pDocSh->GetPrinter( true ) ); + } + } + + // Update everything + Invalidate(); +} + +sal_Int8 Window::AcceptDrop( const AcceptDropEvent& rEvt ) +{ + sal_Int8 nRet = DND_ACTION_NONE; + + if( mpViewShell && !mpViewShell->GetDocSh()->IsReadOnly() ) + { + nRet = mpViewShell->AcceptDrop( rEvt, *this, this, SDRPAGE_NOTFOUND, SDRLAYER_NOTFOUND ); + + if (mbUseDropScroll && dynamic_cast< OutlineViewShell *>( mpViewShell ) == nullptr) + DropScroll( rEvt.maPosPixel ); + } + + return nRet; +} + +sal_Int8 Window::ExecuteDrop( const ExecuteDropEvent& rEvt ) +{ + sal_Int8 nRet = DND_ACTION_NONE; + + if( mpViewShell ) + { + nRet = mpViewShell->ExecuteDrop( rEvt, *this, this, SDRPAGE_NOTFOUND, SDRLAYER_NOTFOUND ); + } + + return nRet; +} + +void Window::SetUseDropScroll (bool bUseDropScroll) +{ + mbUseDropScroll = bUseDropScroll; +} + +void Window::DropScroll(const Point& rMousePos) +{ + short nDx = 0; + short nDy = 0; + + Size aSize = GetOutputSizePixel(); + + if (aSize.Width() > SCROLL_SENSITIVE * 3) + { + if ( rMousePos.X() < SCROLL_SENSITIVE ) + { + nDx = -1; + } + + if ( rMousePos.X() >= aSize.Width() - SCROLL_SENSITIVE ) + { + nDx = 1; + } + } + + if (aSize.Height() > SCROLL_SENSITIVE * 3) + { + if ( rMousePos.Y() < SCROLL_SENSITIVE ) + { + nDy = -1; + } + + if ( rMousePos.Y() >= aSize.Height() - SCROLL_SENSITIVE ) + { + nDy = 1; + } + } + + if ( (nDx || nDy) && (rMousePos.X()!=0 || rMousePos.Y()!=0 ) ) + { + if (mnTicks > 20) + mpViewShell->ScrollLines(nDx, nDy); + else + mnTicks ++; + } +} + +css::uno::Reference<css::accessibility::XAccessible> + Window::CreateAccessible() +{ + // If current viewshell is PresentationViewShell, just return empty because the correct ShowWin will be created later. + if (dynamic_cast< PresentationViewShell *>( mpViewShell )) + { + return vcl::Window::CreateAccessible (); + } + css::uno::Reference< css::accessibility::XAccessible > xAcc = GetAccessible(false); + if (xAcc) + { + return xAcc; + } + if (mpViewShell != nullptr) + { + xAcc = mpViewShell->CreateAccessibleDocumentView (this); + SetAccessible(xAcc); + return xAcc; + } + else + { + SAL_WARN("sd", "::sd::Window::CreateAccessible: no view shell"); + return vcl::Window::CreateAccessible (); + } +} + +OutlinerView* Window::GetOutlinerView() const +{ + OutlinerView *pOLV = nullptr; + sd::View* pView = mpViewShell->GetView(); + if (mpViewShell->GetShellType() == ViewShell::ST_OUTLINE) + { + if (OutlineView* pOView = dynamic_cast<OutlineView*>(pView)) + pOLV = pOView->GetViewByWindow(this); + } + else if (pView->IsTextEdit()) + { + pOLV = pView->GetTextEditOutlinerView(); + } + return pOLV; +} + +OUString Window::GetSurroundingText() const +{ + OutlinerView *pOLV = GetOutlinerView(); + if (pOLV) + return pOLV->GetEditView().GetSurroundingText(); + return OUString(); +} + +Selection Window::GetSurroundingTextSelection() const +{ + OutlinerView *pOLV = GetOutlinerView(); + if (pOLV) + return pOLV->GetEditView().GetSurroundingTextSelection(); + return Selection( 0, 0 ); +} + +bool Window::DeleteSurroundingText(const Selection& rSelection) +{ + OutlinerView *pOLV = GetOutlinerView(); + if (pOLV) + return pOLV->GetEditView().DeleteSurroundingText(rSelection); + return false; +} + +void Window::LogicInvalidate(const ::tools::Rectangle* pRectangle) +{ + DrawViewShell* pDrawViewShell = dynamic_cast<DrawViewShell*>(mpViewShell); + if (!pDrawViewShell || pDrawViewShell->IsInSwitchPage()) + return; + + if (!comphelper::LibreOfficeKit::isActive()) + return; + ::tools::Rectangle aRectangle; + ::tools::Rectangle* pResultRectangle; + if (!pRectangle) + pResultRectangle = nullptr; + else + { + aRectangle = *pRectangle; + if (GetMapMode().GetMapUnit() == MapUnit::Map100thMM) + { + aRectangle = o3tl::convert(aRectangle, o3tl::Length::mm100, o3tl::Length::twip); + } + pResultRectangle = &aRectangle; + } + SfxViewShell& rSfxViewShell = pDrawViewShell->GetViewShellBase(); + SfxLokHelper::notifyInvalidation(&rSfxViewShell, pResultRectangle); +} + +void Window::LogicMouseButtonDown(const MouseEvent& rMouseEvent) +{ + // When we're not doing tiled rendering, then positions must be passed as pixels. + assert(comphelper::LibreOfficeKit::isActive()); + + Point aPoint = GetPointerPosPixel(); + SetLastMousePos(rMouseEvent.GetPosPixel()); + + mpViewShell->MouseButtonDown(rMouseEvent, this); + + SetPointerPosPixel(aPoint); +} + +void Window::LogicMouseButtonUp(const MouseEvent& rMouseEvent) +{ + // When we're not doing tiled rendering, then positions must be passed as pixels. + assert(comphelper::LibreOfficeKit::isActive()); + + Point aPoint = GetPointerPosPixel(); + SetLastMousePos(rMouseEvent.GetPosPixel()); + + mpViewShell->MouseButtonUp(rMouseEvent, this); + + SetPointerPosPixel(aPoint); +} + +void Window::LogicMouseMove(const MouseEvent& rMouseEvent) +{ + // When we're not doing tiled rendering, then positions must be passed as pixels. + assert(comphelper::LibreOfficeKit::isActive()); + + Point aPoint = GetPointerPosPixel(); + SetLastMousePos(rMouseEvent.GetPosPixel()); + + mpViewShell->MouseMove(rMouseEvent, this); + + SetPointerPosPixel(aPoint); +} + +FactoryFunction Window::GetUITestFactory() const +{ + if (get_id() == "impress_win") + return ImpressWindowUIObject::create; + + return WindowUIObject::create; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/tabcontr.cxx b/sd/source/ui/view/tabcontr.cxx new file mode 100644 index 000000000..b09a254e9 --- /dev/null +++ b/sd/source/ui/view/tabcontr.cxx @@ -0,0 +1,358 @@ +/* -*- 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 <TabControl.hxx> + +#include <sfx2/viewfrm.hxx> +#include <sfx2/dispatch.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/vclevent.hxx> + +#include <app.hrc> + +#include <DrawViewShell.hxx> +#include <helpids.h> +#include <View.hxx> +#include <drawdoc.hxx> +#include <DrawDocShell.hxx> + +namespace sd { + + +TabControl::TabControlTransferable::~TabControlTransferable() +{ +} + +void TabControl::TabControlTransferable::AddSupportedFormats() +{ + AddFormat( SotClipboardFormatId::STARDRAW_TABBAR ); +} + +bool TabControl::TabControlTransferable::GetData( const css::datatransfer::DataFlavor& /*rFlavor*/, const OUString& /*rDestDoc*/ ) +{ + return false; +} + +void TabControl::TabControlTransferable::DragFinished( sal_Int8 /*nDropAction*/ ) +{ + mrParent.DragFinished(); +} + +TabControl::TabControl(DrawViewShell* pViewSh, vcl::Window* pParent) : + TabBar( pParent, WinBits( WB_BORDER | WB_3DLOOK | WB_SCROLL | WB_SIZEABLE | WB_DRAG) ), + DragSourceHelper( this ), + DropTargetHelper( this ), + pDrViewSh(pViewSh), + bInternalMove(false) +{ + EnableEditMode(); + SetSizePixel(Size(0, 0)); + SetMaxPageWidth( 150 ); + SetHelpId( HID_SD_TABBAR_PAGES ); +} + +TabControl::~TabControl() +{ + disposeOnce(); +} + +void TabControl::dispose() +{ + DragSourceHelper::dispose(); + DropTargetHelper::dispose(); + TabBar::dispose(); +} + +void TabControl::Select() +{ + SfxDispatcher* pDispatcher = pDrViewSh->GetViewFrame()->GetDispatcher(); + pDispatcher->Execute(SID_SWITCHPAGE, SfxCallMode::ASYNCHRON | + SfxCallMode::RECORD); +} + +void TabControl::MouseButtonDown(const MouseEvent& rMEvt) +{ + if (rMEvt.IsLeft() + && !rMEvt.IsMod1() + && !rMEvt.IsMod2() + && !rMEvt.IsShift()) + { + Point aPos = PixelToLogic( rMEvt.GetPosPixel() ); + sal_uInt16 aPageId = GetPageId(aPos); + + //initialize + if (aPageId == 0) + { + SfxDispatcher* pDispatcher = pDrViewSh->GetViewFrame()->GetDispatcher(); + + pDispatcher->Execute(SID_INSERTPAGE_QUICK, + SfxCallMode::SYNCHRON | SfxCallMode::RECORD); + } + } + + // A single left click with pressed control key on a tab page first + // switches to that page before the usual handling (copying with drag + // and drop) takes place. + else if (rMEvt.IsLeft() && rMEvt.IsMod1() && !rMEvt.IsMod2() && !rMEvt.IsShift()) + { + pDrViewSh->SwitchPage (GetPageId (rMEvt.GetPosPixel()) - 1); + } + + // When only the right button is pressed then first process a + // synthesized left button click to make the page the current one + // whose tab has been clicked. When then the actual right button + // click is processed the resulting context menu relates to the + // now current page. + if (rMEvt.IsRight() && ! rMEvt.IsLeft()) + { + MouseEvent aSyntheticEvent ( + rMEvt.GetPosPixel(), + rMEvt.GetClicks(), + rMEvt.GetMode(), + MOUSE_LEFT, + rMEvt.GetModifier()); + TabBar::MouseButtonDown(aSyntheticEvent); + } + + TabBar::MouseButtonDown(rMEvt); +} + +void TabControl::DoubleClick() +{ + if (GetCurPageId() != 0) + { + SfxDispatcher* pDispatcher = pDrViewSh->GetViewFrame()->GetDispatcher(); + pDispatcher->Execute( SID_MODIFYPAGE, + SfxCallMode::SYNCHRON | SfxCallMode::RECORD ); + } +} + +void TabControl::StartDrag( sal_Int8, const Point& ) +{ + bInternalMove = true; + + // object is delete by reference mechanism + ( new TabControl::TabControlTransferable( *this ) )->StartDrag( this, DND_ACTION_COPYMOVE ); +} + +void TabControl::DragFinished() +{ + bInternalMove = false; +} + +sal_Int8 TabControl::AcceptDrop( const AcceptDropEvent& rEvt ) +{ + sal_Int8 nRet = DND_ACTION_NONE; + + if( rEvt.mbLeaving ) + EndSwitchPage(); + + if( !pDrViewSh->GetDocSh()->IsReadOnly() ) + { + SdDrawDocument* pDoc = pDrViewSh->GetDoc(); + Point aPos( rEvt.maPosPixel ); + + if( bInternalMove ) + { + if( rEvt.mbLeaving || ( pDrViewSh->GetEditMode() == EditMode::MasterPage ) ) + HideDropPos(); + else + { + ShowDropPos( aPos ); + nRet = rEvt.mnAction; + } + } + else + { + HideDropPos(); + + sal_Int32 nPageId = GetPageId( aPos ) - 1; + + if( ( nPageId >= 0 ) && pDoc->GetPage( static_cast<sal_uInt16>(nPageId) ) ) + { + nRet = pDrViewSh->AcceptDrop( rEvt, *this, nullptr, static_cast<sal_uInt16>(nPageId), SDRLAYER_NOTFOUND ); + SwitchPage( aPos ); + } + } + } + + return nRet; +} + +sal_Int8 TabControl::ExecuteDrop( const ExecuteDropEvent& rEvt ) +{ + SdDrawDocument* pDoc = pDrViewSh->GetDoc(); + Point aPos( rEvt.maPosPixel ); + sal_Int8 nRet = DND_ACTION_NONE; + + if( bInternalMove ) + { + sal_uInt16 nPageId = ShowDropPos( aPos ) - 1; + + switch (rEvt.mnAction) + { + case DND_ACTION_MOVE: + if( pDrViewSh->IsSwitchPageAllowed() && pDoc->MovePages( nPageId ) ) + { + SfxDispatcher* pDispatcher = pDrViewSh->GetViewFrame()->GetDispatcher(); + pDispatcher->Execute(SID_SWITCHPAGE, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + } + break; + + case DND_ACTION_COPY: + { + // Copying the selected page to the place that rEvt points + // takes place in three steps: + // 1. Create a copy of the selected page. This copy will + // lie directly behind the selected page. + // 2. Move the copy to the desired place. + // 3. Select the copy. + if (pDrViewSh->IsSwitchPageAllowed()) + { + // 1. Create a copy. + sal_uInt16 nPageNumOfCopy = pDoc->DuplicatePage (GetCurPageId() - 1); + // 2. Move page. For this first switch to the copy: + // MovePages operates on the currently selected page(s). + pDrViewSh->SwitchPage (nPageNumOfCopy); + // Adapt target page id when necessary, i.e. page copy + // has been inserted in front of the target page. + sal_uInt16 nPageNum = nPageId; + if ((nPageNumOfCopy <= nPageNum) && (nPageNum != sal_uInt16(-1))) + nPageNum += 1; + if (pDoc->MovePages(nPageNum)) + { + // 3. Switch to the copy that has been moved to its + // final destination. Use an asynchron slot call to + // be executed after the still pending ones. + if (nPageNumOfCopy >= nPageNum || (nPageNum == sal_uInt16(-1))) + nPageNum += 1; + SetCurPageId (GetPageId(nPageNum)); + SfxDispatcher* pDispatcher = pDrViewSh->GetViewFrame()->GetDispatcher(); + pDispatcher->Execute(SID_SWITCHPAGE, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + } + } + + break; + } + } + + nRet = rEvt.mnAction; + } + else + { + sal_Int32 nPageId = GetPageId( aPos ) - 1; + + if( ( nPageId >= 0 ) && pDoc->GetPage( static_cast<sal_uInt16>(nPageId) ) ) + { + nRet = pDrViewSh->ExecuteDrop( rEvt, *this, nullptr, static_cast<sal_uInt16>(nPageId), SDRLAYER_NOTFOUND ); + } + } + + HideDropPos(); + EndSwitchPage(); + + return nRet; +} + +void TabControl::Command(const CommandEvent& rCEvt) +{ + if ( rCEvt.GetCommand() == CommandEventId::ContextMenu ) + { + SfxDispatcher* pDispatcher = pDrViewSh->GetViewFrame()->GetDispatcher(); + pDispatcher->ExecutePopup("pagetab"); + } +} + +bool TabControl::StartRenaming() +{ + bool bOK = false; + + if (pDrViewSh->GetPageKind() == PageKind::Standard) + { + bOK = true; + + ::sd::View* pView = pDrViewSh->GetView(); + + if ( pView->IsTextEdit() ) + pView->SdrEndTextEdit(); + } + + return bOK; +} + +TabBarAllowRenamingReturnCode TabControl::AllowRenaming() +{ + bool bOK = true; + + OUString aNewName( GetEditText() ); + OUString aCompareName( GetPageText( GetEditPageId() ) ); + + if( aCompareName != aNewName ) + { + // rename page + if (pDrViewSh->GetDocSh()->CheckPageName(GetFrameWeld(), aNewName)) + { + SetEditText( aNewName ); + EndRenaming(); + } + else + { + bOK = false; + } + } + return bOK ? TABBAR_RENAMING_YES : TABBAR_RENAMING_NO; +} + +void TabControl::EndRenaming() +{ + if( !IsEditModeCanceled() ) + pDrViewSh->RenameSlide( GetEditPageId(), GetEditText() ); +} + +void TabControl::ActivatePage() +{ + if ( /*IsInSwitching && */ pDrViewSh->IsSwitchPageAllowed() ) + { + SfxDispatcher* pDispatcher = pDrViewSh->GetViewFrame()->GetDispatcher(); + pDispatcher->Execute(SID_SWITCHPAGE, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + } +} + +bool TabControl::DeactivatePage() +{ + return pDrViewSh->IsSwitchPageAllowed(); +} + +void TabControl::SendActivatePageEvent() +{ + CallEventListeners (VclEventId::TabbarPageActivated, + reinterpret_cast<void*>(GetCurPageId())); +} + +void TabControl::SendDeactivatePageEvent() +{ + CallEventListeners (VclEventId::TabbarPageDeactivated, + reinterpret_cast<void*>(GetCurPageId())); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/unmodpg.cxx b/sd/source/ui/view/unmodpg.cxx new file mode 100644 index 000000000..03d907d14 --- /dev/null +++ b/sd/source/ui/view/unmodpg.cxx @@ -0,0 +1,210 @@ +/* -*- 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 <svx/svdlayer.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/svdviter.hxx> +#include <svx/svdview.hxx> +#include <tools/debug.hxx> + +#include <strings.hrc> +#include <strings.hxx> +#include <glob.hxx> +#include <app.hrc> + +#include <unmodpg.hxx> +#include <sdpage.hxx> +#include <sdresid.hxx> +#include <unokywds.hxx> +#include <drawdoc.hxx> + + +ModifyPageUndoAction::ModifyPageUndoAction( + SdDrawDocument* pTheDoc, + SdPage* pThePage, + const OUString& aTheNewName, + AutoLayout eTheNewAutoLayout, + bool bTheNewBckgrndVisible, + bool bTheNewBckgrndObjsVisible) +: SdUndoAction(pTheDoc) +{ + DBG_ASSERT(pThePage, "Undo without a page???"); + + mpPage = pThePage; + maNewName = aTheNewName; + meNewAutoLayout = eTheNewAutoLayout; + mbNewBckgrndVisible = bTheNewBckgrndVisible; + mbNewBckgrndObjsVisible = bTheNewBckgrndObjsVisible; + + meOldAutoLayout = mpPage->GetAutoLayout(); + + if (!mpPage->IsMasterPage()) + { + maOldName = mpPage->GetName(); + SdrLayerAdmin& rLayerAdmin = mpDoc->GetLayerAdmin(); + SdrLayerID aBckgrnd = rLayerAdmin.GetLayerID(sUNO_LayerName_background); + SdrLayerID aBckgrndObj = rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects); + SdrLayerIDSet aVisibleLayers = mpPage->TRG_GetMasterPageVisibleLayers(); + + mbOldBckgrndVisible = aVisibleLayers.IsSet(aBckgrnd); + mbOldBckgrndObjsVisible = aVisibleLayers.IsSet(aBckgrndObj); + } + else + { + mbOldBckgrndVisible = false; + mbOldBckgrndObjsVisible = false; + } + + if (pTheDoc && pTheDoc->GetDocumentType() == DocumentType::Draw) + SetComment( SdResId(STR_UNDO_MODIFY_PAGE_DRAW) ); + else + SetComment( SdResId(STR_UNDO_MODIFY_PAGE) ); +} + +void ModifyPageUndoAction::Undo() +{ + // invalidate Selection, there could be objects deleted in this UNDO + // which are no longer allowed to be selected then. + SdrViewIter aIter(mpPage); + SdrView* pView = aIter.FirstView(); + + while(pView) + { + if(pView->AreObjectsMarked()) + pView->UnmarkAll(); + pView = aIter.NextView(); + } + + mpPage->SetAutoLayout( meOldAutoLayout ); + + if (!mpPage->IsMasterPage()) + { + if (mpPage->GetName() != maOldName) + { + mpPage->SetName(maOldName); + + if (mpPage->GetPageKind() == PageKind::Standard) + { + SdPage* pNotesPage = static_cast<SdPage*>(mpDoc->GetPage(mpPage->GetPageNum() + 1)); + pNotesPage->SetName(maOldName); + } + } + + SdrLayerAdmin& rLayerAdmin = mpDoc->GetLayerAdmin(); + SdrLayerID aBckgrnd = rLayerAdmin.GetLayerID(sUNO_LayerName_background); + SdrLayerID aBckgrndObj = rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects); + SdrLayerIDSet aVisibleLayers; + aVisibleLayers.Set(aBckgrnd, mbOldBckgrndVisible); + aVisibleLayers.Set(aBckgrndObj, mbOldBckgrndObjsVisible); + mpPage->TRG_SetMasterPageVisibleLayers(aVisibleLayers); + } + + // Redisplay + SfxViewFrame* pCurrent = SfxViewFrame::Current(); + if( pCurrent ) + { + pCurrent->GetDispatcher()->Execute( + SID_SWITCHPAGE, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD ); + } +} + +void ModifyPageUndoAction::Redo() +{ + // invalidate Selection, there could be objects deleted in this UNDO + // which are no longer allowed to be selected then. + SdrViewIter aIter(mpPage); + SdrView* pView = aIter.FirstView(); + + while(pView) + { + if(pView->AreObjectsMarked()) + pView->UnmarkAll(); + pView = aIter.NextView(); + } + + mpPage->meAutoLayout = meNewAutoLayout; + + if (!mpPage->IsMasterPage()) + { + if (mpPage->GetName() != maNewName) + { + mpPage->SetName(maNewName); + + if (mpPage->GetPageKind() == PageKind::Standard) + { + SdPage* pNotesPage = static_cast<SdPage*>(mpDoc->GetPage(mpPage->GetPageNum() + 1)); + pNotesPage->SetName(maNewName); + } + } + + SdrLayerAdmin& rLayerAdmin = mpDoc->GetLayerAdmin(); + SdrLayerID aBckgrnd = rLayerAdmin.GetLayerID(sUNO_LayerName_background); + SdrLayerID aBckgrndObj = rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects); + SdrLayerIDSet aVisibleLayers; + aVisibleLayers.Set(aBckgrnd, mbNewBckgrndVisible); + aVisibleLayers.Set(aBckgrndObj, mbNewBckgrndObjsVisible); + mpPage->TRG_SetMasterPageVisibleLayers(aVisibleLayers); + } + + // Redisplay + SfxViewFrame* pCurrent = SfxViewFrame::Current(); + if( pCurrent ) + { + pCurrent->GetDispatcher()->Execute( + SID_SWITCHPAGE, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD ); + } +} + +ModifyPageUndoAction::~ModifyPageUndoAction() +{ +} + +RenameLayoutTemplateUndoAction::RenameLayoutTemplateUndoAction( + SdDrawDocument* pDocument, + const OUString& rOldLayoutName, + const OUString& rNewLayoutName) + : SdUndoAction(pDocument) + , maOldName(rOldLayoutName) + , maNewName(rNewLayoutName) + , maComment(SdResId(STR_TITLE_RENAMESLIDE)) +{ + sal_Int32 nPos = maOldName.indexOf(SD_LT_SEPARATOR); + if (nPos != -1) + maOldName = maOldName.copy(0, nPos); +} + +void RenameLayoutTemplateUndoAction::Undo() +{ + OUString aLayoutName(maNewName + SD_LT_SEPARATOR + STR_LAYOUT_OUTLINE); + mpDoc->RenameLayoutTemplate( aLayoutName, maOldName ); +} + +void RenameLayoutTemplateUndoAction::Redo() +{ + OUString aLayoutName(maOldName + SD_LT_SEPARATOR + STR_LAYOUT_OUTLINE); + mpDoc->RenameLayoutTemplate( aLayoutName, maNewName ); +} + +OUString RenameLayoutTemplateUndoAction::GetComment() const +{ + return maComment; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/viewoverlaymanager.cxx b/sd/source/ui/view/viewoverlaymanager.cxx new file mode 100644 index 000000000..3cdfb9787 --- /dev/null +++ b/sd/source/ui/view/viewoverlaymanager.cxx @@ -0,0 +1,546 @@ +/* -*- 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 <sal/config.h> + +#include <sfx2/viewfrm.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> + +#include <vcl/help.hxx> +#include <vcl/lazydelete.hxx> +#include <vcl/ptrstyle.hxx> +#include <vcl/svapp.hxx> + +#include <svx/sdrpagewindow.hxx> +#include <svx/sdrpaintwindow.hxx> +#include <svx/sdr/overlay/overlaybitmapex.hxx> +#include <svx/sdr/overlay/overlaymanager.hxx> +#include <svx/svxids.hrc> +#include <svx/svdpagv.hxx> + +#include <view/viewoverlaymanager.hxx> + + +#include <DrawDocShell.hxx> +#include <strings.hrc> +#include <bitmaps.hlst> +#include <sdresid.hxx> +#include <EventMultiplexer.hxx> +#include <View.hxx> +#include <ViewShellBase.hxx> +#include <ViewShell.hxx> +#include <sdpage.hxx> +#include <smarttag.hxx> + +using namespace ::com::sun::star::uno; + +namespace sd { + +namespace { + +class ImageButtonHdl; + +} + +const sal_uInt16 gButtonSlots[] = { SID_INSERT_TABLE, SID_INSERT_DIAGRAM, SID_INSERT_GRAPHIC, SID_INSERT_AVMEDIA }; +const TranslateId gButtonToolTips[] = { STR_INSERT_TABLE, STR_INSERT_CHART, STR_INSERT_PICTURE, STR_INSERT_MOVIE }; + +constexpr rtl::OUStringConstExpr aSmallPlaceHolders[] = +{ + BMP_PLACEHOLDER_TABLE_SMALL, + BMP_PLACEHOLDER_CHART_SMALL, + BMP_PLACEHOLDER_IMAGE_SMALL, + BMP_PLACEHOLDER_MOVIE_SMALL, + BMP_PLACEHOLDER_TABLE_SMALL_HOVER, + BMP_PLACEHOLDER_CHART_SMALL_HOVER, + BMP_PLACEHOLDER_IMAGE_SMALL_HOVER, + BMP_PLACEHOLDER_MOVIE_SMALL_HOVER +}; + +constexpr rtl::OUStringConstExpr aBigPlaceHolders[] = +{ + BMP_PLACEHOLDER_TABLE_LARGE, + BMP_PLACEHOLDER_CHART_LARGE, + BMP_PLACEHOLDER_IMAGE_LARGE, + BMP_PLACEHOLDER_MOVIE_LARGE, + BMP_PLACEHOLDER_TABLE_LARGE_HOVER, + BMP_PLACEHOLDER_CHART_LARGE_HOVER, + BMP_PLACEHOLDER_IMAGE_LARGE_HOVER, + BMP_PLACEHOLDER_MOVIE_LARGE_HOVER +}; + +static BitmapEx* getButtonImage( int index, bool large ) +{ + static vcl::DeleteOnDeinit< BitmapEx > gSmallButtonImages[SAL_N_ELEMENTS(aSmallPlaceHolders)] = { vcl::DeleteOnDeinitFlag::Empty, vcl::DeleteOnDeinitFlag::Empty, vcl::DeleteOnDeinitFlag::Empty, vcl::DeleteOnDeinitFlag::Empty, vcl::DeleteOnDeinitFlag::Empty, vcl::DeleteOnDeinitFlag::Empty, vcl::DeleteOnDeinitFlag::Empty, vcl::DeleteOnDeinitFlag::Empty }; + static vcl::DeleteOnDeinit< BitmapEx > gLargeButtonImages[SAL_N_ELEMENTS(aBigPlaceHolders)] = { vcl::DeleteOnDeinitFlag::Empty, vcl::DeleteOnDeinitFlag::Empty, vcl::DeleteOnDeinitFlag::Empty, vcl::DeleteOnDeinitFlag::Empty, vcl::DeleteOnDeinitFlag::Empty, vcl::DeleteOnDeinitFlag::Empty, vcl::DeleteOnDeinitFlag::Empty, vcl::DeleteOnDeinitFlag::Empty }; + + assert(SAL_N_ELEMENTS(aSmallPlaceHolders) == SAL_N_ELEMENTS(aBigPlaceHolders)); + + if( !gSmallButtonImages[0].get() ) + { + for (size_t i = 0; i < SAL_N_ELEMENTS(aSmallPlaceHolders); i++ ) + { + gSmallButtonImages[i].set(OUString(aSmallPlaceHolders[i])); + gLargeButtonImages[i].set(OUString(aBigPlaceHolders[i])); + } + } + + if( large ) + { + return gLargeButtonImages[index].get(); + } + else + { + return gSmallButtonImages[index].get(); + } +} + +const sal_uInt32 SMART_TAG_HDL_NUM = SAL_MAX_UINT32; + +namespace { + +class ChangePlaceholderTag : public SmartTag +{ + friend class ImageButtonHdl; +public: + ChangePlaceholderTag( ::sd::View& rView, SdrObject& rPlaceholderObj ); + + /** returns true if the SmartTag handled the event. */ + virtual bool MouseButtonDown( const MouseEvent&, SmartHdl& ) override; + + /** returns true if the SmartTag consumes this event. */ + virtual bool KeyInput( const KeyEvent& rKEvt ) override; + + BitmapEx createOverlayImage( int nHighlight ); + +protected: + virtual void addCustomHandles( SdrHdlList& rHandlerList ) override; + +private: + ::tools::WeakReference<SdrObject> mxPlaceholderObj; +}; + +class ImageButtonHdl : public SmartHdl +{ +public: + ImageButtonHdl( const SmartTagReference& xTag, /* sal_uInt16 nSID, const Image& rImage, const Image& rImageMO, */ const Point& rPnt ); + virtual ~ImageButtonHdl() override; + virtual void CreateB2dIAObject() override; + virtual bool IsFocusHdl() const override; + virtual PointerStyle GetPointer() const override; + + virtual void onMouseEnter(const MouseEvent& rMEvt) override; + virtual void onHelpRequest() override; + virtual void onMouseLeave() override; + + int getHighlightId() const { return mnHighlightId; } + + void ShowTip(); + static void HideTip(); + +private: + rtl::Reference< ChangePlaceholderTag > mxChangePlaceholderTag; + + int mnHighlightId; + Size maImageSize; +}; + +} + +ImageButtonHdl::ImageButtonHdl( const SmartTagReference& xTag /*, sal_uInt16 nSID, const Image& rImage, const Image& rImageMO*/, const Point& rPnt ) +: SmartHdl( xTag, rPnt, SdrHdlKind::SmartTag ) +, mxChangePlaceholderTag( dynamic_cast< ChangePlaceholderTag* >( xTag.get() ) ) +, mnHighlightId( -1 ) +, maImageSize( 42, 42 ) +{ +} + +ImageButtonHdl::~ImageButtonHdl() +{ + HideTip(); +} + +void ImageButtonHdl::HideTip() +{ + Help::HideBalloonAndQuickHelp(); +} + +void ImageButtonHdl::ShowTip() +{ + if (!pHdlList || !pHdlList->GetView() || mnHighlightId == -1) + return; + + OutputDevice* pDev = pHdlList->GetView()->GetFirstOutputDevice(); + if( pDev == nullptr ) + pDev = Application::GetDefaultDevice(); + + OUString aHelpText(SdResId(gButtonToolTips[mnHighlightId])); + Point aHelpPos(pDev->LogicToPixel(GetPos())); + if (mnHighlightId == 1) + aHelpPos.Move(maImageSize.Width(), 0); + else if (mnHighlightId == 2) + aHelpPos.Move(0, maImageSize.Height()); + else if (mnHighlightId == 3) + aHelpPos.Move(maImageSize.Width(), maImageSize.Height()); + ::tools::Rectangle aLogicPix(aHelpPos, maImageSize); + vcl::Window* pWindow = pHdlList->GetView()->GetFirstOutputDevice()->GetOwnerWindow(); + ::tools::Rectangle aScreenRect(pWindow->OutputToScreenPixel(aLogicPix.TopLeft()), + pWindow->OutputToScreenPixel(aLogicPix.BottomRight())); + Help::ShowQuickHelp(pWindow, aScreenRect, aHelpText); +} + +void ImageButtonHdl::onHelpRequest() +{ + ShowTip(); +} + +void ImageButtonHdl::onMouseEnter(const MouseEvent& rMEvt) +{ + if( !(pHdlList && pHdlList->GetView())) + return; + + int nHighlightId = 0; + OutputDevice* pDev = pHdlList->GetView()->GetFirstOutputDevice(); + if( pDev == nullptr ) + pDev = Application::GetDefaultDevice(); + + Point aMDPos( rMEvt.GetPosPixel() ); + aMDPos -= pDev->LogicToPixel( GetPos() ); + + nHighlightId += aMDPos.X() > maImageSize.Width() ? 1 : 0; + nHighlightId += aMDPos.Y() > maImageSize.Height() ? 2 : 0; + + if( mnHighlightId != nHighlightId ) + { + HideTip(); + + mnHighlightId = nHighlightId; + + ShowTip(); + + Touch(); + } +} + +void ImageButtonHdl::onMouseLeave() +{ + mnHighlightId = -1; + HideTip(); + Touch(); +} + +void ImageButtonHdl::CreateB2dIAObject() +{ + // first throw away old one + GetRidOfIAObject(); + + const Point aTagPos( GetPos() ); + basegfx::B2DPoint aPosition( aTagPos.X(), aTagPos.Y() ); + + BitmapEx aBitmapEx( mxChangePlaceholderTag->createOverlayImage( mnHighlightId ) ); // maImageMO.GetBitmapEx() : maImage.GetBitmapEx() ); + maImageSize = aBitmapEx.GetSizePixel(); + maImageSize.setWidth( maImageSize.Width() >> 1 ); + maImageSize.setHeight( maImageSize.Height() >> 1 ); + + if(!pHdlList) + return; + + SdrMarkView* pView = pHdlList->GetView(); + + if(!pView || pView->areMarkHandlesHidden()) + return; + + SdrPageView* pPageView = pView->GetSdrPageView(); + + if(!pPageView) + return; + + for(sal_uInt32 b = 0; b < pPageView->PageWindowCount(); b++) + { + const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b); + + SdrPaintWindow& rPaintWindow = rPageWindow.GetPaintWindow(); + const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager(); + if(rPaintWindow.OutputToWindow() && xManager.is() ) + { + std::unique_ptr<sdr::overlay::OverlayObject> pOverlayObject( + new sdr::overlay::OverlayBitmapEx( aPosition, aBitmapEx, 0, 0 )); + + // OVERLAYMANAGER + insertNewlyCreatedOverlayObjectForSdrHdl( + std::move(pOverlayObject), + rPageWindow.GetObjectContact(), + *xManager); + } + } +} + +bool ImageButtonHdl::IsFocusHdl() const +{ + return false; +} + +PointerStyle ImageButtonHdl::GetPointer() const +{ + return PointerStyle::Arrow; +} + +ChangePlaceholderTag::ChangePlaceholderTag( ::sd::View& rView, SdrObject& rPlaceholderObj ) +: SmartTag( rView ) +, mxPlaceholderObj( &rPlaceholderObj ) +{ +} + +/** returns true if the ChangePlaceholderTag handled the event. */ +bool ChangePlaceholderTag::MouseButtonDown( const MouseEvent& /*rMEvt*/, SmartHdl& rHdl ) +{ + int nHighlightId = static_cast< ImageButtonHdl& >(rHdl).getHighlightId(); + if( nHighlightId >= 0 ) + { + sal_uInt16 nSID = gButtonSlots[nHighlightId]; + + if( mxPlaceholderObj ) + { + // mark placeholder if it is not currently marked (or if also others are marked) + if( !mrView.IsObjMarked( mxPlaceholderObj.get() ) || (mrView.GetMarkedObjectList().GetMarkCount() != 1) ) + { + SdrPageView* pPV = mrView.GetSdrPageView(); + mrView.UnmarkAllObj(pPV ); + mrView.MarkObj(mxPlaceholderObj.get(), pPV); + } + } + + mrView.GetViewShell()->GetViewFrame()->GetDispatcher()->Execute( nSID, SfxCallMode::ASYNCHRON); + } + return false; +} + +/** returns true if the SmartTag consumes this event. */ +bool ChangePlaceholderTag::KeyInput( const KeyEvent& rKEvt ) +{ + sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode(); + switch( nCode ) + { + case KEY_DOWN: + case KEY_UP: + case KEY_LEFT: + case KEY_RIGHT: + case KEY_ESCAPE: + case KEY_TAB: + case KEY_RETURN: + case KEY_SPACE: + default: + return false; + } +} + +BitmapEx ChangePlaceholderTag::createOverlayImage( int nHighlight ) +{ + BitmapEx aRet; + if( mxPlaceholderObj.is() ) + { + SdrObject* pPlaceholder = mxPlaceholderObj.get(); + SmartTagReference xThis( this ); + const ::tools::Rectangle& rSnapRect = pPlaceholder->GetSnapRect(); + + OutputDevice* pDev = mrView.GetFirstOutputDevice(); + if( pDev == nullptr ) + pDev = Application::GetDefaultDevice(); + + Size aShapeSizePix = pDev->LogicToPixel(rSnapRect.GetSize()); + ::tools::Long nShapeSizePix = std::min(aShapeSizePix.Width(),aShapeSizePix.Height()); + + bool bLarge = nShapeSizePix > 250; + + Size aSize( getButtonImage( 0, bLarge )->GetSizePixel() ); + + aRet.Scale(Size(aSize.Width() << 1, aSize.Height() << 1)); + + const ::tools::Rectangle aRectSrc( Point( 0, 0 ), aSize ); + + aRet = *(getButtonImage((nHighlight == 0) ? 4 : 0, bLarge)); + aRet.Expand( aSize.Width(), aSize.Height(), true ); + + aRet.CopyPixel( ::tools::Rectangle( Point( aSize.Width(), 0 ), aSize ), aRectSrc, getButtonImage((nHighlight == 1) ? 5 : 1, bLarge) ); + aRet.CopyPixel( ::tools::Rectangle( Point( 0, aSize.Height() ), aSize ), aRectSrc, getButtonImage((nHighlight == 2) ? 6 : 2, bLarge) ); + aRet.CopyPixel( ::tools::Rectangle( Point( aSize.Width(), aSize.Height() ), aSize ), aRectSrc, getButtonImage((nHighlight == 3) ? 7 : 3, bLarge) ); + } + + return aRet; +} + +void ChangePlaceholderTag::addCustomHandles( SdrHdlList& rHandlerList ) +{ + if( !mxPlaceholderObj.is() ) + return; + + SdrObject* pPlaceholder = mxPlaceholderObj.get(); + SmartTagReference xThis( this ); + const ::tools::Rectangle& rSnapRect = pPlaceholder->GetSnapRect(); + const Point aPoint; + + OutputDevice* pDev = mrView.GetFirstOutputDevice(); + if( pDev == nullptr ) + pDev = Application::GetDefaultDevice(); + + Size aShapeSizePix = pDev->LogicToPixel(rSnapRect.GetSize()); + ::tools::Long nShapeSizePix = std::min(aShapeSizePix.Width(),aShapeSizePix.Height()); + if( 50 > nShapeSizePix ) + return; + + bool bLarge = nShapeSizePix > 250; + + Size aButtonSize( pDev->PixelToLogic( getButtonImage(0, bLarge )->GetSizePixel()) ); + + const int nColumns = 2; + const int nRows = 2; + + ::tools::Long all_width = nColumns * aButtonSize.Width(); + ::tools::Long all_height = nRows * aButtonSize.Height(); + + Point aPos( rSnapRect.Center() ); + aPos.AdjustX( -(all_width >> 1) ); + aPos.AdjustY( -(all_height >> 1) ); + + std::unique_ptr<ImageButtonHdl> pHdl(new ImageButtonHdl( xThis, aPoint )); + pHdl->SetObjHdlNum( SMART_TAG_HDL_NUM ); + pHdl->SetPageView( mrView.GetSdrPageView() ); + + pHdl->SetPos( aPos ); + + rHandlerList.AddHdl( std::move(pHdl) ); +} + +ViewOverlayManager::ViewOverlayManager( ViewShellBase& rViewShellBase ) +: mrBase( rViewShellBase ) +, mnUpdateTagsEvent( nullptr ) +{ + Link<tools::EventMultiplexerEvent&,void> aLink( LINK(this,ViewOverlayManager,EventMultiplexerListener) ); + mrBase.GetEventMultiplexer()->AddEventListener(aLink); + + StartListening( *mrBase.GetDocShell() ); +} + +ViewOverlayManager::~ViewOverlayManager() +{ + Link<tools::EventMultiplexerEvent&,void> aLink( LINK(this,ViewOverlayManager,EventMultiplexerListener) ); + mrBase.GetEventMultiplexer()->RemoveEventListener( aLink ); + + if( mnUpdateTagsEvent ) + { + Application::RemoveUserEvent( mnUpdateTagsEvent ); + mnUpdateTagsEvent = nullptr; + } + + DisposeTags(); +} + +void ViewOverlayManager::Notify(SfxBroadcaster&, const SfxHint& rHint) +{ + if (rHint.GetId() == SfxHintId::DocChanged) + { + UpdateTags(); + } +} + +void ViewOverlayManager::onZoomChanged() +{ + if( !maTagVector.empty() ) + { + UpdateTags(); + } +} + +void ViewOverlayManager::UpdateTags() +{ + if( !mnUpdateTagsEvent ) + mnUpdateTagsEvent = Application::PostUserEvent( LINK( this, ViewOverlayManager, UpdateTagsHdl ) ); +} + +IMPL_LINK_NOARG(ViewOverlayManager, UpdateTagsHdl, void*, void) +{ + mnUpdateTagsEvent = nullptr; + bool bChanges = DisposeTags(); + bChanges |= CreateTags(); + + if( bChanges && mrBase.GetDrawView() ) + static_cast< ::sd::View* >( mrBase.GetDrawView() )->updateHandles(); +} + +bool ViewOverlayManager::CreateTags() +{ + bool bChanges = false; + + std::shared_ptr<ViewShell> aMainShell = mrBase.GetMainViewShell(); + + SdPage* pPage = aMainShell ? aMainShell->getCurrentPage() : nullptr; + + if( pPage && !pPage->IsMasterPage() && (pPage->GetPageKind() == PageKind::Standard) ) + { + const std::list< SdrObject* >& rShapes = pPage->GetPresentationShapeList().getList(); + + for( SdrObject* pShape : rShapes ) + { + if( pShape->IsEmptyPresObj() && (pShape->GetObjIdentifier() == SdrObjKind::OutlineText) && (mrBase.GetDrawView()->GetTextEditObject() != pShape) ) + { + rtl::Reference< SmartTag > xTag( new ChangePlaceholderTag( *mrBase.GetMainViewShell()->GetView(), *pShape ) ); + maTagVector.push_back(xTag); + bChanges = true; + } + } + } + + return bChanges; +} + +bool ViewOverlayManager::DisposeTags() +{ + if( !maTagVector.empty() ) + { + ViewTagVector vec; + vec.swap( maTagVector ); + + for (auto& rxViewTag : vec) + rxViewTag->Dispose(); + return true; + } + + return false; +} + +IMPL_LINK(ViewOverlayManager,EventMultiplexerListener, + tools::EventMultiplexerEvent&, rEvent, void) +{ + switch (rEvent.meEventId) + { + case EventMultiplexerEventId::MainViewAdded: + case EventMultiplexerEventId::ViewAdded: + case EventMultiplexerEventId::BeginTextEdit: + case EventMultiplexerEventId::EndTextEdit: + case EventMultiplexerEventId::CurrentPageChanged: + UpdateTags(); + break; + default: break; + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/viewshe2.cxx b/sd/source/ui/view/viewshe2.cxx new file mode 100644 index 000000000..8b16124ba --- /dev/null +++ b/sd/source/ui/view/viewshe2.cxx @@ -0,0 +1,958 @@ +/* -*- 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 <com/sun/star/embed/EmbedVerbs.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> + +#include <ViewShell.hxx> +#include <ViewShellHint.hxx> + +#include <ViewShellImplementation.hxx> +#include <FactoryIds.hxx> + +#include <svx/svxids.hrc> +#include <vcl/scrbar.hxx> +#include <svx/svdpagv.hxx> +#include <sfx2/dispatch.hxx> +#include <svx/ruler.hxx> +#include <editeng/outliner.hxx> +#include <svtools/ehdl.hxx> +#include <svx/svdoole2.hxx> +#include <svtools/sfxecode.hxx> +#include <unotools/moduleoptions.hxx> +#include <comphelper/classids.hxx> +#include <osl/diagnose.h> + +#include <strings.hrc> +#include <app.hrc> +#include <unokywds.hxx> + +#include <sdundogr.hxx> +#include <FrameView.hxx> +#include <sdresid.hxx> +#include <drawdoc.hxx> +#include <View.hxx> +#include <fupoor.hxx> +#include <Client.hxx> +#include <DrawDocShell.hxx> +#include <sdpage.hxx> +#include <DrawViewShell.hxx> +#include <ViewShellBase.hxx> + +#include <Window.hxx> + +#include <sfx2/viewfrm.hxx> +#include <svtools/soerr.hxx> +#include <svx/charthelper.hxx> +#include <comphelper/lok.hxx> + +using namespace com::sun::star; + +namespace sd { + +/** + * adjust Thumbpos and VisibleSize + */ +void ViewShell::UpdateScrollBars() +{ + if (mpHorizontalScrollBar) + { + ::tools::Long nW = static_cast<::tools::Long>(mpContentWindow->GetVisibleWidth() * 32000); + ::tools::Long nX = static_cast<::tools::Long>(mpContentWindow->GetVisibleX() * 32000); + mpHorizontalScrollBar->SetVisibleSize(nW); + mpHorizontalScrollBar->SetThumbPos(nX); + nW = 32000 - nW; + ::tools::Long nLine = static_cast<::tools::Long>(mpContentWindow->GetScrlLineWidth() * nW); + ::tools::Long nPage = static_cast<::tools::Long>(mpContentWindow->GetScrlPageWidth() * nW); + mpHorizontalScrollBar->SetLineSize(nLine); + mpHorizontalScrollBar->SetPageSize(nPage); + } + + if (mpVerticalScrollBar) + { + ::tools::Long nH = static_cast<::tools::Long>(mpContentWindow->GetVisibleHeight() * 32000); + ::tools::Long nY = static_cast<::tools::Long>(mpContentWindow->GetVisibleY() * 32000); + + if(IsPageFlipMode()) // ie in zoom mode where no panning + { + SdPage* pPage = static_cast<DrawViewShell*>(this)->GetActualPage(); + sal_uInt16 nCurPage = (pPage->GetPageNum() - 1) / 2; + sal_uInt16 nTotalPages = GetDoc()->GetSdPageCount(pPage->GetPageKind()); + mpVerticalScrollBar->SetRange(Range(0,256*nTotalPages)); + mpVerticalScrollBar->SetVisibleSize(256); + mpVerticalScrollBar->SetThumbPos(256*nCurPage); + mpVerticalScrollBar->SetLineSize(256); + mpVerticalScrollBar->SetPageSize(256); + } + else + { + mpVerticalScrollBar->SetRange(Range(0,32000)); + mpVerticalScrollBar->SetVisibleSize(nH); + mpVerticalScrollBar->SetThumbPos(nY); + nH = 32000 - nH; + ::tools::Long nLine = static_cast<::tools::Long>(mpContentWindow->GetScrlLineHeight() * nH); + ::tools::Long nPage = static_cast<::tools::Long>(mpContentWindow->GetScrlPageHeight() * nH); + mpVerticalScrollBar->SetLineSize(nLine); + mpVerticalScrollBar->SetPageSize(nPage); + } + } + + if (mbHasRulers) + { + UpdateHRuler(); + UpdateVRuler(); + } + +} +/** + * Handling for horizontal Scrollbars + */ +IMPL_LINK(ViewShell, HScrollHdl, ScrollBar *, pHScroll, void ) +{ + VirtHScrollHdl(pHScroll); +} + +/** + * virtual scroll handler for horizontal Scrollbars + */ +void ViewShell::VirtHScrollHdl(ScrollBar* pHScroll) +{ + ::tools::Long nDelta = pHScroll->GetDelta(); + + if (nDelta == 0) + return; + + double fX = static_cast<double>(pHScroll->GetThumbPos()) / pHScroll->GetRange().Len(); + + // scroll all windows of the column + ::sd::View* pView = GetView(); + OutlinerView* pOLV = nullptr; + + if (pView) + pOLV = pView->GetTextEditOutlinerView(); + + if (pOLV) + pOLV->HideCursor(); + + mpContentWindow->SetVisibleXY(fX, -1); + + ::tools::Rectangle aVisArea = GetDocSh()->GetVisArea(ASPECT_CONTENT); + Point aVisAreaPos = GetActiveWindow()->PixelToLogic( Point(0,0) ); + aVisArea.SetPos(aVisAreaPos); + GetDocSh()->SetVisArea(aVisArea); + + Size aVisSizePixel = GetActiveWindow()->GetOutputSizePixel(); + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), aVisSizePixel) ); + VisAreaChanged(aVisAreaWin); + + if (pView) + { + pView->VisAreaChanged(GetActiveWindow()->GetOutDev()); + } + + if (pOLV) + pOLV->ShowCursor(); + + if (mbHasRulers) + UpdateHRuler(); +} + +/** + * handling for vertical Scrollbars + */ +IMPL_LINK(ViewShell, VScrollHdl, ScrollBar *, pVScroll, void ) +{ + VirtVScrollHdl(pVScroll); +} + +/** + * handling for vertical Scrollbars + */ +void ViewShell::VirtVScrollHdl(ScrollBar* pVScroll) +{ + if(IsPageFlipMode()) + { + SdPage* pPage = static_cast<DrawViewShell*>(this)->GetActualPage(); + sal_uInt16 nCurPage = (pPage->GetPageNum() - 1) >> 1; + sal_uInt16 nNewPage = static_cast<sal_uInt16>(pVScroll->GetThumbPos())/256; + if( nCurPage != nNewPage ) + static_cast<DrawViewShell*>(this)->SwitchPage(nNewPage); + } + else //panning mode + { + double fY = static_cast<double>(pVScroll->GetThumbPos()) / pVScroll->GetRange().Len(); + + ::sd::View* pView = GetView(); + OutlinerView* pOLV = nullptr; + + if (pView) + pOLV = pView->GetTextEditOutlinerView(); + + if (pOLV) + pOLV->HideCursor(); + + mpContentWindow->SetVisibleXY(-1, fY); + + ::tools::Rectangle aVisArea = GetDocSh()->GetVisArea(ASPECT_CONTENT); + Point aVisAreaPos = GetActiveWindow()->PixelToLogic( Point(0,0) ); + aVisArea.SetPos(aVisAreaPos); + GetDocSh()->SetVisArea(aVisArea); + + Size aVisSizePixel = GetActiveWindow()->GetOutputSizePixel(); + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), aVisSizePixel) ); + VisAreaChanged(aVisAreaWin); + + if (pView) + { + pView->VisAreaChanged(GetActiveWindow()->GetOutDev()); + } + + if (pOLV) + pOLV->ShowCursor(); + + if (mbHasRulers) + UpdateVRuler(); + + } +} + +VclPtr<SvxRuler> ViewShell::CreateHRuler(::sd::Window* ) +{ + return nullptr; +} + +VclPtr<SvxRuler> ViewShell::CreateVRuler(::sd::Window* ) +{ + return nullptr; +} + +void ViewShell::UpdateHRuler() +{ +} + +void ViewShell::UpdateVRuler() +{ +} + +/** + * Scroll a specific number of lines. Is used in the automatic scrolling + * (character/drag). + */ +void ViewShell::ScrollLines(::tools::Long nLinesX, ::tools::Long nLinesY) +{ + if ( nLinesX ) + { + nLinesX *= mpHorizontalScrollBar->GetLineSize(); + } + if ( nLinesY ) + { + nLinesY *= mpVerticalScrollBar->GetLineSize(); + } + + Scroll(nLinesX, nLinesY); +} + +void ViewShell::Scroll(::tools::Long nScrollX, ::tools::Long nScrollY) +{ + if (nScrollX) + { + ::tools::Long nNewThumb = mpHorizontalScrollBar->GetThumbPos() + nScrollX; + mpHorizontalScrollBar->SetThumbPos(nNewThumb); + } + if (nScrollY) + { + ::tools::Long nNewThumb = mpVerticalScrollBar->GetThumbPos() + nScrollY; + mpVerticalScrollBar->SetThumbPos(nNewThumb); + } + double fX = static_cast<double>(mpHorizontalScrollBar->GetThumbPos()) / + mpHorizontalScrollBar->GetRange().Len(); + double fY = static_cast<double>(mpVerticalScrollBar->GetThumbPos()) / + mpVerticalScrollBar->GetRange().Len(); + + GetActiveWindow()->SetVisibleXY(fX, fY); + + ::tools::Rectangle aVisArea = GetDocSh()->GetVisArea(ASPECT_CONTENT); + Point aVisAreaPos = GetActiveWindow()->PixelToLogic( Point(0,0) ); + aVisArea.SetPos(aVisAreaPos); + GetDocSh()->SetVisArea(aVisArea); + + Size aVisSizePixel = GetActiveWindow()->GetOutputSizePixel(); + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), aVisSizePixel) ); + VisAreaChanged(aVisAreaWin); + + ::sd::View* pView = GetView(); + if (pView) + { + pView->VisAreaChanged(GetActiveWindow()->GetOutDev()); + } + + if (mbHasRulers) + { + UpdateHRuler(); + UpdateVRuler(); + } +} + +/** + * Set zoom factor for all split windows. + */ +void ViewShell::SetZoom(::tools::Long nZoom) +{ + Fraction aUIScale(nZoom, 100); + aUIScale *= GetDoc()->GetUIScale(); + + if (mpHorizontalRuler) + mpHorizontalRuler->SetZoom(aUIScale); + + if (mpVerticalRuler) + mpVerticalRuler->SetZoom(aUIScale); + + if (mpContentWindow) + { + mpContentWindow->SetZoomIntegral(nZoom); + + // #i74769# Here is a 2nd way (besides Window::Scroll) to set the visible prt + // of the window. It needs - like Scroll(ScrollFlags::Children) does - also to move + // the child windows. I am trying InvalidateFlags::Children here which makes things better, + // but does not solve the problem completely. Need to ask PL. + mpContentWindow->Invalidate(InvalidateFlags::Children); + } + + Size aVisSizePixel = GetActiveWindow()->GetOutputSizePixel(); + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), aVisSizePixel) ); + VisAreaChanged(aVisAreaWin); + + ::sd::View* pView = GetView(); + if (pView) + { + pView->VisAreaChanged(GetActiveWindow()->GetOutDev()); + } + + UpdateScrollBars(); +} + +::tools::Long ViewShell::GetZoom() const +{ + if (mpContentWindow) + { + return mpContentWindow->GetZoom(); + } + + return 0; +} + +/** + * Set zoom rectangle for active window. Sets all split windows to the same zoom + * factor. + */ +void ViewShell::SetZoomRect(const ::tools::Rectangle& rZoomRect) +{ + ::tools::Long nZoom = GetActiveWindow()->SetZoomRect(rZoomRect); + Fraction aUIScale(nZoom, 100); + aUIScale *= GetDoc()->GetUIScale(); + + Point aPos = GetActiveWindow()->GetWinViewPos(); + + if (mpHorizontalRuler) + mpHorizontalRuler->SetZoom(aUIScale); + + if (mpVerticalRuler) + mpVerticalRuler->SetZoom(aUIScale); + + if (mpContentWindow) + { + Point aNewPos = mpContentWindow->GetWinViewPos(); + aNewPos.setX( aPos.X() ); + aNewPos.setY( aPos.Y() ); + mpContentWindow->SetZoomIntegral(nZoom); + mpContentWindow->SetWinViewPos(aNewPos); + mpContentWindow->UpdateMapOrigin(); + + // When tiled rendering, UpdateMapOrigin() doesn't touch the map mode. + if (!comphelper::LibreOfficeKit::isActive()) + // #i74769# see above + mpContentWindow->Invalidate(InvalidateFlags::Children); + } + + Size aVisSizePixel = GetActiveWindow()->GetOutputSizePixel(); + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), aVisSizePixel) ); + VisAreaChanged(aVisAreaWin); + + ::sd::View* pView = GetView(); + if (pView) + { + pView->VisAreaChanged(GetActiveWindow()->GetOutDev()); + } + + UpdateScrollBars(); +} + +/** + * Initialize imaging parameters for all split windows. + */ +void ViewShell::InitWindows(const Point& rViewOrigin, const Size& rViewSize, + const Point& rWinPos, bool bUpdate) +{ + if (mpContentWindow) + { + mpContentWindow->SetViewOrigin(rViewOrigin); + mpContentWindow->SetViewSize(rViewSize); + mpContentWindow->SetWinViewPos(rWinPos); + + if ( bUpdate ) + { + mpContentWindow->UpdateMapOrigin(); + mpContentWindow->Invalidate(); + } + } + + Size aVisSizePixel = GetActiveWindow()->GetOutputSizePixel(); + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), aVisSizePixel) ); + VisAreaChanged(aVisAreaWin); + + ::sd::View* pView = GetView(); + if (pView) + { + pView->VisAreaChanged(GetActiveWindow()->GetOutDev()); + } +} + +/** + * Invalidate all split windows below the ?provided rectangle. + */ +void ViewShell::InvalidateWindows() +{ + if (mpContentWindow) + mpContentWindow->Invalidate(); +} + +/** + * Draw a selection rectangle with the ?provided pen on all split windows. + */ +void ViewShell::DrawMarkRect(const ::tools::Rectangle& rRect) const +{ + if (mpContentWindow) + { + mpContentWindow->InvertTracking(rRect, ShowTrackFlags::Object | ShowTrackFlags::TrackWindow); + } +} + +void ViewShell::SetPageSizeAndBorder(PageKind ePageKind, const Size& rNewSize, + ::tools::Long nLeft, ::tools::Long nRight, + ::tools::Long nUpper, ::tools::Long nLower, bool bScaleAll, + Orientation eOrientation, sal_uInt16 nPaperBin, + bool bBackgroundFullSize) +{ + const sal_uInt16 nMasterPageCnt(GetDoc()->GetMasterSdPageCount(ePageKind)); + const sal_uInt16 nPageCnt(GetDoc()->GetSdPageCount(ePageKind)); + + if(0 == nPageCnt && 0 == nMasterPageCnt) + { + return; + } + + std::unique_ptr<SdUndoGroup> pUndoGroup; + SfxViewShell* pViewShell(GetViewShell()); + if (pViewShell) + { + pUndoGroup.reset(new SdUndoGroup(GetDoc())); + pUndoGroup->SetComment(SdResId(STR_UNDO_CHANGE_PAGEFORMAT)); + } + Broadcast (ViewShellHint(ViewShellHint::HINT_PAGE_RESIZE_START)); + + // use Model-based method at SdDrawDocument + GetDoc()->AdaptPageSizeForAllPages( + rNewSize, + ePageKind, + pUndoGroup.get(), + nLeft, + nRight, + nUpper, + nLower, + bScaleAll, + eOrientation, + nPaperBin, + bBackgroundFullSize); + + // adjust handout page to new format of the standard page + if(0 != nPageCnt && ((ePageKind == PageKind::Standard) || (ePageKind == PageKind::Handout))) + { + GetDoc()->GetSdPage(0, PageKind::Handout)->CreateTitleAndLayout(true); + } + + // handed over undo group to undo manager + if (pViewShell) + { + pViewShell->GetViewFrame()->GetObjectShell()->GetUndoManager()->AddUndoAction(std::move(pUndoGroup)); + } + + // calculate View-Sizes + SdPage* pPage(0 != nPageCnt + ? GetDoc()->GetSdPage(0, ePageKind) + : GetDoc()->GetMasterSdPage(0, ePageKind)); + const ::tools::Long nWidth(pPage->GetSize().Width()); + const ::tools::Long nHeight(pPage->GetSize().Height()); + const Point aPageOrg(nWidth, nHeight / 2); + const Size aViewSize(nWidth * 3, nHeight * 2); + Point aVisAreaPos; + ::sd::View* pView(GetView()); + const Point aNewOrigin(pPage->GetLeftBorder(), pPage->GetUpperBorder()); + + InitWindows(aPageOrg, aViewSize, Point(-1, -1), true); + + if ( GetDocSh()->GetCreateMode() == SfxObjectCreateMode::EMBEDDED ) + { + aVisAreaPos = GetDocSh()->GetVisArea(ASPECT_CONTENT).TopLeft(); + } + + if (pView) + { + pView->SetWorkArea(::tools::Rectangle(Point() - aVisAreaPos - aPageOrg, aViewSize)); + } + + UpdateScrollBars(); + + if (pView) + { + pView->GetSdrPageView()->SetPageOrigin(aNewOrigin); + } + + if(nullptr != pViewShell) + { + pViewShell->GetViewFrame()->GetBindings().Invalidate(SID_RULER_NULL_OFFSET); + // zoom onto (new) page size + pViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_SIZE_PAGE, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + } + + Broadcast(ViewShellHint(ViewShellHint::HINT_PAGE_RESIZE_END)); +} + +/** + * Set zoom factor for InPlace + */ +void ViewShell::SetZoomFactor(const Fraction& rZoomX, const Fraction&) +{ + ::tools::Long nZoom = static_cast<::tools::Long>(static_cast<double>(rZoomX) * 100); + SetZoom(nZoom); +} + +void ViewShell::SetActiveWindow (::sd::Window* pWin) +{ + SfxViewShell* pViewShell = GetViewShell(); + OSL_ASSERT (pViewShell!=nullptr); + + if (pViewShell->GetWindow() != pWin) + { + // #i31551# was wrong, it may have been a problem with the repaint at that time. + // For transparent form controls, it is necessary to have that flag set, all apps + // do set it. Enabling again. + if (pWin) + { + pWin->EnableChildTransparentMode(); + } + } + + if (mpActiveWindow.get() != pWin) + mpActiveWindow = pWin; + + // The rest of this function is not guarded anymore against calling this + // method with an already active window because the functions may still + // point to the old window when the new one has already been assigned to + // pWindow elsewhere. + ::sd::View* pView = GetView(); + if (pView) + { + pView->SetActualWin(pWin->GetOutDev()); + } + if(HasCurrentFunction()) + { + GetCurrentFunction()->SetWindow(pWin); + } +} + +bool ViewShell::RequestHelp(const HelpEvent& rHEvt) +{ + bool bReturn = false; + + if (bool(rHEvt.GetMode())) + { + if(HasCurrentFunction()) + { + bReturn = GetCurrentFunction()->RequestHelp(rHEvt); + } + } + + return bReturn; +} + +void ViewShell::SetFrameView (FrameView* pNewFrameView) +{ + mpFrameView = pNewFrameView; + ReadFrameViewData (mpFrameView); +} + +/************************************************************************* +|* +|* Read FrameViews data and set actual views data +|* +\************************************************************************/ + +void ViewShell::ReadFrameViewData(FrameView*) +{ +} + +/************************************************************************* +|* +|* Write actual views data to FrameView +|* +\************************************************************************/ + +void ViewShell::WriteFrameViewData() +{ +} + +bool ViewShell::ActivateObject(SdrOle2Obj* pObj, sal_Int32 nVerb) +{ + ErrCode aErrCode = ERRCODE_NONE; + + SfxErrorContext aEC(ERRCTX_SO_DOVERB, GetFrameWeld(), RID_SO_ERRCTX); + bool bAbort = false; + GetDocSh()->SetWaitCursor( true ); + SfxViewShell* pViewShell = GetViewShell(); + OSL_ASSERT (pViewShell!=nullptr); + bool bChangeDefaultsForChart = false; + + uno::Reference < embed::XEmbeddedObject > xObj = pObj->GetObjRef(); + if ( !xObj.is() ) + { + // provide OLE object to empty OLE object + OUString aName = pObj->GetProgName(); + OUString aObjName; + SvGlobalName aClass; + + if( aName == "StarChart" || aName == "StarOrg" ) + { + if( SvtModuleOptions().IsChart() ) + { + aClass = SvGlobalName( SO3_SCH_CLASSID ); + bChangeDefaultsForChart = true; + } + } + else if( aName == "StarCalc" ) + { + if( SvtModuleOptions().IsCalc() ) + aClass = SvGlobalName( SO3_SC_CLASSID ); + } + else if( aName == "StarMath" ) + { + if( SvtModuleOptions().IsMath() ) + aClass = SvGlobalName( SO3_SM_CLASSID ); + } + + if ( aClass != SvGlobalName() ) + xObj = GetDocSh()->GetEmbeddedObjectContainer().CreateEmbeddedObject( aClass.GetByteSequence(), aObjName ); + + if( !xObj.is() ) + { + aName.clear(); + + // call dialog "insert OLE object" + GetDocSh()->SetWaitCursor( false ); + pViewShell->GetViewFrame()->GetDispatcher()->Execute( + SID_INSERT_OBJECT, + SfxCallMode::SYNCHRON | SfxCallMode::RECORD); + xObj = pObj->GetObjRef(); + GetDocSh()->SetWaitCursor( true ); + + if (!xObj.is()) + { + bAbort = true; + } + } + + if ( xObj.is() ) + { + // OLE object is no longer empty + pObj->SetEmptyPresObj(false); + pObj->SetOutlinerParaObject(std::nullopt); + pObj->ClearGraphic(); + + // the empty OLE object gets a new IPObj + if (!aName.isEmpty()) + { + pObj->SetObjRef(xObj); + pObj->SetName(aObjName); + pObj->SetPersistName(aObjName); + } + else + { + // insertion was done by the dialog + pObj->SetObjRef(xObj); + } + + ::tools::Rectangle aRect = pObj->GetLogicRect(); + + if ( pObj->GetAspect() != embed::Aspects::MSOLE_ICON ) + { + awt::Size aSz; + aSz.Width = aRect.GetWidth(); + aSz.Height = aRect.GetHeight(); + xObj->setVisualAreaSize( pObj->GetAspect(), aSz ); + } + + GetViewShellBase().SetVerbs( xObj->getSupportedVerbs() ); + + nVerb = embed::EmbedVerbs::MS_OLEVERB_SHOW; + } + else + { + aErrCode = ERRCODE_SFX_OLEGENERAL; + } + } + + if( aErrCode == ERRCODE_NONE ) + { + ::sd::View* pView = GetView(); + + if (pView->IsTextEdit()) + { + pView->SdrEndTextEdit(); + } + + SfxInPlaceClient* pSdClient = + pViewShell->FindIPClient(pObj->GetObjRef(), GetActiveWindow()); + + if ( !pSdClient ) + { + pSdClient = new Client(pObj, this, GetActiveWindow()); + } + + ::tools::Rectangle aRect = pObj->GetLogicRect(); + + { + // #i118485# center on BoundRect for activation, + // OLE may be sheared/rotated now + const ::tools::Rectangle& rBoundRect = pObj->GetCurrentBoundRect(); + const Point aDelta(rBoundRect.Center() - aRect.Center()); + aRect.Move(aDelta.X(), aDelta.Y()); + } + + Size aDrawSize = aRect.GetSize(); + + MapMode aMapMode( GetDoc()->GetScaleUnit() ); + Size aObjAreaSize = pObj->GetOrigObjSize( &aMapMode ); + if( pObj->IsChart() ) //charts never should be stretched see #i84323# for example + aObjAreaSize = aDrawSize; + + Fraction aScaleWidth (aDrawSize.Width(), aObjAreaSize.Width() ); + Fraction aScaleHeight(aDrawSize.Height(), aObjAreaSize.Height() ); + aScaleWidth.ReduceInaccurate(10); // compatible to the SdrOle2Obj + aScaleHeight.ReduceInaccurate(10); + pSdClient->SetSizeScale(aScaleWidth, aScaleHeight); + + // visible section is only changed in-place! + aRect.SetSize(aObjAreaSize); + // the object area size must be set after scaling, since it triggers the resizing + pSdClient->SetObjArea(aRect); + + if( bChangeDefaultsForChart && xObj.is()) + { + ChartHelper::AdaptDefaultsForChart( xObj ); + } + + pSdClient->DoVerb(nVerb); // if necessary, ErrCode is outputted by Sfx + pViewShell->GetViewFrame()->GetBindings().Invalidate( + SID_NAVIGATOR_STATE, true); + } + + GetDocSh()->SetWaitCursor( false ); + + if (aErrCode != ERRCODE_NONE && !bAbort) + { + ErrorHandler::HandleError(* new StringErrorInfo(aErrCode, OUString() ) ); + } + + return aErrCode == ERRCODE_NONE; +} + +/** + * @returns enclosing rectangle of all (split-) windows. + */ +const ::tools::Rectangle& ViewShell::GetAllWindowRect() +{ + maAllWindowRectangle.SetPos( + mpContentWindow->OutputToScreenPixel(Point(0,0))); + return maAllWindowRectangle; +} + +void ViewShell::ReadUserData() +{ + // zoom onto VisArea from FrameView + GetViewShell()->GetViewFrame()->GetDispatcher()->Execute(SID_SIZE_VISAREA, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); +} + +void ViewShell::WriteUserData() +{ + // writing of our data is always done in WriteFrameViewData() + WriteFrameViewData(); +} + +/** + * Switch ruler on/off + */ +void ViewShell::SetRuler(bool bRuler) +{ + mbHasRulers = ( bRuler && !GetDocSh()->IsPreview() ); // no rulers on preview mode + + if (mpHorizontalRuler) + { + if (mbHasRulers) + { + mpHorizontalRuler->Show(); + } + else + { + mpHorizontalRuler->Hide(); + } + } + + if (mpVerticalRuler) + { + if (mbHasRulers) + { + mpVerticalRuler->Show(); + } + else + { + mpVerticalRuler->Hide(); + } + } + + OSL_ASSERT(GetViewShell()!=nullptr); + if (IsMainViewShell()) + GetViewShell()->InvalidateBorder(); +} + +void ViewShell::SetScrollBarsVisible(bool bVisible) +{ + if (mpVerticalScrollBar) + mpVerticalScrollBar->Show( bVisible ); + + if (mpHorizontalScrollBar) + mpHorizontalScrollBar->Show( bVisible ); + + if (mpScrollBarBox) + mpScrollBarBox->Show(bVisible); +} + +sal_Int8 ViewShell::AcceptDrop ( + const AcceptDropEvent& rEvt, + DropTargetHelper& rTargetHelper, + ::sd::Window* /*pTargetWindow*/, + sal_uInt16 /*nPage*/, + SdrLayerID nLayer) +{ + ::sd::View* pView = GetView(); + return( pView ? pView->AcceptDrop( rEvt, rTargetHelper, nLayer ) : DND_ACTION_NONE ); +} + +sal_Int8 ViewShell::ExecuteDrop ( + const ExecuteDropEvent& rEvt, + DropTargetHelper& /*rTargetHelper*/, + ::sd::Window* pTargetWindow, + sal_uInt16 nPage, + SdrLayerID nLayer) +{ + ::sd::View* pView = GetView(); + return pView ? pView->ExecuteDrop( rEvt, pTargetWindow, nPage, nLayer ) : DND_ACTION_NONE; +} + +void ViewShell::WriteUserDataSequence ( css::uno::Sequence < css::beans::PropertyValue >& rSequence ) +{ + const sal_Int32 nIndex = rSequence.getLength(); + rSequence.realloc( nIndex + 1 ); + auto pSequence = rSequence.getArray(); + + OSL_ASSERT (GetViewShell()!=nullptr); + // Get the view id from the view shell in the center pane. This will + // usually be the called view shell, but to be on the safe side we call + // the main view shell explicitly. + SfxInterfaceId nViewID (IMPRESS_FACTORY_ID); + if (GetViewShellBase().GetMainViewShell() != nullptr) + nViewID = GetViewShellBase().GetMainViewShell()->mpImpl->GetViewId(); + pSequence[nIndex].Name = sUNO_View_ViewId; + pSequence[nIndex].Value <<= "view" + OUString::number( static_cast<sal_uInt16>(nViewID)); + + mpFrameView->WriteUserDataSequence( rSequence ); +} + +void ViewShell::ReadUserDataSequence ( const css::uno::Sequence < css::beans::PropertyValue >& rSequence ) +{ + mpFrameView->ReadUserDataSequence( rSequence ); +} + +void ViewShell::VisAreaChanged(const ::tools::Rectangle& /*rRect*/) +{ + OSL_ASSERT (GetViewShell()!=nullptr); + GetViewShell()->VisAreaChanged(); +} + +void ViewShell::SetWinViewPos(const Point& rWinPos) +{ + if (mpContentWindow) + { + mpContentWindow->SetWinViewPos(rWinPos); + + mpContentWindow->UpdateMapOrigin(); + mpContentWindow->Invalidate(); + } + + if (mbHasRulers) + { + UpdateHRuler(); + UpdateVRuler(); + } + + UpdateScrollBars(); + + Size aVisSizePixel = GetActiveWindow()->GetOutputSizePixel(); + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), aVisSizePixel) ); + VisAreaChanged(aVisAreaWin); + + ::sd::View* pView = GetView(); + if (pView) + { + pView->VisAreaChanged(GetActiveWindow()->GetOutDev()); + } +} + +Point const & ViewShell::GetWinViewPos() const +{ + return mpContentWindow->GetWinViewPos(); +} + +Point const & ViewShell::GetViewOrigin() const +{ + return mpContentWindow->GetViewOrigin(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/viewshe3.cxx b/sd/source/ui/view/viewshe3.cxx new file mode 100644 index 000000000..7ebf88b44 --- /dev/null +++ b/sd/source/ui/view/viewshe3.cxx @@ -0,0 +1,383 @@ +/* -*- 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 <config_features.h> + +#include <ViewShell.hxx> +#include <ViewShellBase.hxx> + +#include <sfx2/viewfrm.hxx> +#include <svtools/strings.hrc> +#include <svtools/svtresid.hxx> + +#include <app.hrc> +#include <strings.hrc> + +#include <sal/log.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/bindings.hxx> +#include <svx/svdundo.hxx> +#include <svl/intitem.hxx> +#include <svl/style.hxx> +#include <svl/stritem.hxx> +#include <stlsheet.hxx> +#include <DrawViewShell.hxx> + +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <DrawDocShell.hxx> +#include <sdresid.hxx> +#include <unokywds.hxx> + +#include <svx/svxids.hrc> +#include <sfx2/request.hxx> +#include <basic/sbstar.hxx> +#include <basic/sberrors.hxx> +#include <xmloff/autolayout.hxx> + +using namespace ::com::sun::star; + +namespace sd { + +/** + * set state (enabled/disabled) of Menu SfxSlots + */ +void ViewShell::GetMenuState( SfxItemSet &rSet ) +{ + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_STYLE_FAMILY ) ) + { + SfxStyleFamily const nFamily = GetDocSh()->GetStyleFamily(); + + SdrView* pDrView = GetDrawView(); + + if( pDrView->AreObjectsMarked() ) + { + SfxStyleSheet* pStyleSheet = pDrView->GetStyleSheet(); + if( pStyleSheet ) + { + if (pStyleSheet->GetFamily() == SfxStyleFamily::Page) + pStyleSheet = static_cast<SdStyleSheet*>(pStyleSheet)->GetPseudoStyleSheet(); + + if( pStyleSheet ) + { + GetDocSh()->SetStyleFamily(pStyleSheet->GetFamily()); + } + } + } + + rSet.Put(SfxUInt16Item(SID_STYLE_FAMILY, static_cast<sal_uInt16>(nFamily))); + } + + if(SfxItemState::DEFAULT == rSet.GetItemState(SID_GETUNDOSTRINGS)) + { + ImpGetUndoStrings(rSet); + } + + if(SfxItemState::DEFAULT == rSet.GetItemState(SID_GETREDOSTRINGS)) + { + ImpGetRedoStrings(rSet); + } + + if(SfxItemState::DEFAULT == rSet.GetItemState(SID_UNDO)) + { + SfxUndoManager* pUndoManager = ImpGetUndoManager(); + if(pUndoManager) + { + if(pUndoManager->GetUndoActionCount() != 0) + { + // If another view created the first undo action, prevent redoing it from this view. + const SfxUndoAction* pAction = pUndoManager->GetUndoAction(); + if (pAction->GetViewShellId() != GetViewShellBase().GetViewShellId()) + { + rSet.Put(SfxUInt32Item(SID_UNDO, static_cast<sal_uInt32>(SID_REPAIRPACKAGE))); + } + else + { + // Set the necessary string like in + // sfx2/source/view/viewfrm.cxx ver 1.23 ln 1072 ff. + OUString aTmp = SvtResId(STR_UNDO) + + pUndoManager->GetUndoActionComment(); + rSet.Put(SfxStringItem(SID_UNDO, aTmp)); + } + } + else + { + rSet.DisableItem(SID_UNDO); + } + } + } + + if(SfxItemState::DEFAULT != rSet.GetItemState(SID_REDO)) + return; + + SfxUndoManager* pUndoManager = ImpGetUndoManager(); + if(!pUndoManager) + return; + + if(pUndoManager->GetRedoActionCount() != 0) + { + // If another view created the first undo action, prevent redoing it from this view. + const SfxUndoAction* pAction = pUndoManager->GetRedoAction(); + if (pAction->GetViewShellId() != GetViewShellBase().GetViewShellId()) + { + rSet.Put(SfxUInt32Item(SID_REDO, static_cast<sal_uInt32>(SID_REPAIRPACKAGE))); + } + else + { + // Set the necessary string like in + // sfx2/source/view/viewfrm.cxx ver 1.23 ln 1081 ff. + OUString aTmp = SvtResId(STR_REDO) + pUndoManager->GetRedoActionComment(); + rSet.Put(SfxStringItem(SID_REDO, aTmp)); + } + } + else + { + rSet.DisableItem(SID_REDO); + } +} + +/** This method consists basically of three parts: + 1. Process the arguments of the SFX request. + 2. Use the model to create a new page or duplicate an existing one. + 3. Update the tab control and switch to the new page. +*/ +SdPage* ViewShell::CreateOrDuplicatePage ( + SfxRequest& rRequest, + PageKind ePageKind, + SdPage* pPage, + const sal_Int32 nInsertPosition) +{ + sal_uInt16 nSId = rRequest.GetSlot(); + SdDrawDocument* pDocument = GetDoc(); + SdrLayerAdmin& rLayerAdmin = pDocument->GetLayerAdmin(); + SdrLayerID aBckgrnd = rLayerAdmin.GetLayerID(sUNO_LayerName_background); + SdrLayerID aBckgrndObj = rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects); + SdrLayerIDSet aVisibleLayers; + // Determine the page from which to copy some values, such as layers, + // size, master page, to the new page. This is usually the given page. + // When the given page is NULL then use the first page of the document. + SdPage* pTemplatePage = pPage; + if (pTemplatePage == nullptr) + pTemplatePage = pDocument->GetSdPage(0, ePageKind); + if (pTemplatePage != nullptr && pTemplatePage->TRG_HasMasterPage()) + aVisibleLayers = pTemplatePage->TRG_GetMasterPageVisibleLayers(); + else + aVisibleLayers.SetAll(); + + OUString aStandardPageName; + OUString aNotesPageName; + AutoLayout eStandardLayout (AUTOLAYOUT_NONE); + AutoLayout eNotesLayout (AUTOLAYOUT_NOTES); + bool bIsPageBack = aVisibleLayers.IsSet(aBckgrnd); + bool bIsPageObj = aVisibleLayers.IsSet(aBckgrndObj); + + // 1. Process the arguments. + const SfxItemSet* pArgs = rRequest.GetArgs(); + if (! pArgs) + { + // AutoLayouts must be ready + pDocument->StopWorkStartupDelay(); + + // Use the layouts of the previous page and notes page as template. + if (pTemplatePage != nullptr) + { + eStandardLayout = pTemplatePage->GetAutoLayout(); + if( eStandardLayout == AUTOLAYOUT_TITLE ) + eStandardLayout = AUTOLAYOUT_TITLE_CONTENT; + + SdPage* pNotesTemplatePage = static_cast<SdPage*>(pDocument->GetPage(pTemplatePage->GetPageNum()+1)); + if (pNotesTemplatePage != nullptr) + eNotesLayout = pNotesTemplatePage->GetAutoLayout(); + } + } + else if (pArgs->Count() == 1) + { + pDocument->StopWorkStartupDelay(); + const SfxUInt32Item* pLayout = rRequest.GetArg<SfxUInt32Item>(ID_VAL_WHATLAYOUT); + if( pLayout ) + { + if (ePageKind == PageKind::Notes) + { + eNotesLayout = static_cast<AutoLayout>(pLayout->GetValue ()); + } + else + { + eStandardLayout = static_cast<AutoLayout>(pLayout->GetValue ()); + } + } + } + else if (pArgs->Count() == 4) + { + // AutoLayouts must be ready + pDocument->StopWorkStartupDelay(); + + const SfxStringItem* pPageName = rRequest.GetArg<SfxStringItem>(ID_VAL_PAGENAME); + const SfxUInt32Item* pLayout = rRequest.GetArg<SfxUInt32Item>(ID_VAL_WHATLAYOUT); + const SfxBoolItem* pIsPageBack = rRequest.GetArg<SfxBoolItem>(ID_VAL_ISPAGEBACK); + const SfxBoolItem* pIsPageObj = rRequest.GetArg<SfxBoolItem>(ID_VAL_ISPAGEOBJ); + + if (CHECK_RANGE (AUTOLAYOUT_START, static_cast<AutoLayout>(pLayout->GetValue ()), AUTOLAYOUT_END)) + { + if (ePageKind == PageKind::Notes) + { + aNotesPageName = pPageName->GetValue (); + eNotesLayout = static_cast<AutoLayout>(pLayout->GetValue ()); + } + else + { + aStandardPageName = pPageName->GetValue (); + eStandardLayout = static_cast<AutoLayout>(pLayout->GetValue ()); + } + + bIsPageBack = pIsPageBack->GetValue (); + bIsPageObj = pIsPageObj->GetValue (); + } + else + { + Cancel(); + + if(HasCurrentFunction( SID_BEZIER_EDIT ) ) + GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_BAD_PROP_VALUE); +#endif + rRequest.Ignore (); + return nullptr; + } + } + else + { + Cancel(); + + if(HasCurrentFunction(SID_BEZIER_EDIT) ) + GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + rRequest.Ignore (); + return nullptr; + } + + // 2. Create a new page or duplicate an existing one. + View* pDrView = GetView(); + const bool bUndo = pDrView && pDrView->IsUndoEnabled(); + if( bUndo && GetDoc()->GetDocumentType() == DocumentType::Draw) + pDrView->BegUndo(SdResId(STR_INSERT_PAGE_DRAW)); + else if (bUndo) + pDrView->BegUndo(SdResId(STR_INSERTPAGE)); + + + + sal_uInt16 nNewPageIndex = 0xffff; + switch (nSId) + { + case SID_INSERTPAGE: + case SID_INSERTPAGE_QUICK: + case SID_INSERT_MASTER_PAGE: + // There are three cases. a) pPage is not NULL: we use it as a + // template and create a new slide behind it. b) pPage is NULL + // but the document is not empty: we use the first slide/notes + // page as template, create a new slide after it and move it + // then to the head of the document. c) pPage is NULL and the + // document is empty: We use CreateFirstPages to create the + // first page of the document. + if (pPage == nullptr) + if (pTemplatePage == nullptr) + { + pDocument->CreateFirstPages(); + nNewPageIndex = 0; + } + else + { + // Create a new page with the first page as template and + // insert it after the first page. + nNewPageIndex = pDocument->CreatePage ( + pTemplatePage, + ePageKind, + aStandardPageName, + aNotesPageName, + eStandardLayout, + eNotesLayout, + bIsPageBack, + bIsPageObj, + nInsertPosition); + // Select exactly the new page. + sal_uInt16 nPageCount (pDocument->GetSdPageCount(ePageKind)); + for (sal_uInt16 i=0; i<nPageCount; i++) + { + pDocument->GetSdPage(i, PageKind::Standard)->SetSelected( + i == nNewPageIndex); + pDocument->GetSdPage(i, PageKind::Notes)->SetSelected( + i == nNewPageIndex); + } + // Move the selected page to the head of the document + pDocument->MovePages (sal_uInt16(-1)); + nNewPageIndex = 0; + } + else + nNewPageIndex = pDocument->CreatePage ( + pPage, + ePageKind, + aStandardPageName, + aNotesPageName, + eStandardLayout, + eNotesLayout, + bIsPageBack, + bIsPageObj, + nInsertPosition); + break; + + case SID_DUPLICATE_PAGE: + // Duplication makes no sense when pPage is NULL. + if (pPage != nullptr) + nNewPageIndex = pDocument->DuplicatePage ( + pPage, + ePageKind, + aStandardPageName, + aNotesPageName, + bIsPageBack, + bIsPageObj, + nInsertPosition); + break; + + default: + SAL_INFO("sd", "wrong slot id given to CreateOrDuplicatePage"); + // Try to handle another slot id gracefully. + } + SdPage* pNewPage = nullptr; + if(nNewPageIndex != 0xffff) + pNewPage = pDocument->GetSdPage(nNewPageIndex, PageKind::Standard); + + if( bUndo ) + { + if( pNewPage ) + { + pDrView->AddUndo(pDocument->GetSdrUndoFactory().CreateUndoNewPage(*pNewPage)); + pDrView->AddUndo(pDocument->GetSdrUndoFactory().CreateUndoNewPage(*pDocument->GetSdPage (nNewPageIndex, PageKind::Notes))); + } + + pDrView->EndUndo(); + } + + return pNewPage; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/viewshel.cxx b/sd/source/ui/view/viewshel.cxx new file mode 100644 index 000000000..866b79461 --- /dev/null +++ b/sd/source/ui/view/viewshel.cxx @@ -0,0 +1,1634 @@ +/* -*- 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 <memory> +#include <ViewShell.hxx> +#include <ViewShellImplementation.hxx> +#include <createtableobjectbar.hxx> + +#include <ViewShellBase.hxx> +#include <ShellFactory.hxx> +#include <DrawController.hxx> +#include <LayerTabBar.hxx> + +#include <sal/log.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/scrbar.hxx> +#include <svl/eitem.hxx> +#include <svx/ruler.hxx> +#include <svx/svxids.hrc> +#include <svx/fmshell.hxx> +#include <WindowUpdater.hxx> +#include <sdxfer.hxx> + +#include <app.hrc> + +#include <OutlineView.hxx> +#include <DrawViewShell.hxx> +#include <DrawDocShell.hxx> +#include <slideshow.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <zoomlist.hxx> +#include <FrameView.hxx> +#include <BezierObjectBar.hxx> +#include <TextObjectBar.hxx> +#include <GraphicObjectBar.hxx> +#include <MediaObjectBar.hxx> +#include <SlideSorter.hxx> +#include <SlideSorterViewShell.hxx> +#include <ViewShellManager.hxx> +#include <FormShellManager.hxx> +#include <svx/extrusionbar.hxx> +#include <svx/fontworkbar.hxx> +#include <svx/svdoutl.hxx> +#include <tools/svborder.hxx> +#include <comphelper/lok.hxx> + +#include <svl/slstitm.hxx> +#include <sfx2/request.hxx> +#include <SpellDialogChildWindow.hxx> +#include <controller/SlideSorterController.hxx> +#include <controller/SlsPageSelector.hxx> +#include <controller/SlsSelectionObserver.hxx> +#include <view/SlideSorterView.hxx> + +#include <basegfx/utils/zoomtools.hxx> + +#include <Window.hxx> +#include <fupoor.hxx> +#include <futext.hxx> + +#include <editeng/numitem.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/editview.hxx> +#include <editeng/editeng.hxx> +#include <svl/itempool.hxx> +#include <svl/intitem.hxx> +#include <svl/poolitem.hxx> +#include <strings.hxx> +#include <sdmod.hxx> +#include <AccessibleDocumentViewBase.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::presentation; + +namespace { + +class ViewShellObjectBarFactory + : public ::sd::ShellFactory<SfxShell> +{ +public: + explicit ViewShellObjectBarFactory (::sd::ViewShell& rViewShell); + virtual SfxShell* CreateShell( ::sd::ShellId nId ) override; + virtual void ReleaseShell (SfxShell* pShell) override; +private: + ::sd::ViewShell& mrViewShell; +}; + +} // end of anonymous namespace + +namespace sd { + +bool ViewShell::IsPageFlipMode() const +{ + return dynamic_cast< const DrawViewShell *>( this ) != nullptr && mpContentWindow && + mpContentWindow->GetVisibleHeight() >= 1.0; +} + +SfxViewFrame* ViewShell::GetViewFrame() const +{ + const SfxViewShell* pViewShell = GetViewShell(); + if (pViewShell != nullptr) + { + return pViewShell->GetViewFrame(); + } + else + { + OSL_ASSERT (GetViewShell()!=nullptr); + return nullptr; + } +} + +/// declare SFX-Slotmap and standard interface + +ViewShell::ViewShell( vcl::Window* pParentWindow, ViewShellBase& rViewShellBase) +: SfxShell(&rViewShellBase) +, mpParentWindow(pParentWindow) +{ + construct(); +} + +ViewShell::~ViewShell() +{ + // Keep the content window from accessing in its destructor the + // WindowUpdater. + if (mpContentWindow) + mpContentWindow->SetViewShell(nullptr); + + mpZoomList.reset(); + + mpLayerTabBar.disposeAndClear(); + + if (mpImpl->mpSubShellFactory) + GetViewShellBase().GetViewShellManager()->RemoveSubShellFactory( + this,mpImpl->mpSubShellFactory); + + if (mpContentWindow) + { + SAL_INFO( + "sd.view", + "destroying mpContentWindow at " << mpContentWindow.get() + << " with parent " << mpContentWindow->GetParent()); + mpContentWindow.disposeAndClear(); + } + + mpScrollBarBox.disposeAndClear(); + mpVerticalRuler.disposeAndClear(); + mpHorizontalRuler.disposeAndClear(); + mpVerticalScrollBar.disposeAndClear(); + mpHorizontalScrollBar.disposeAndClear(); +} + +/** + * common initialization part of both constructors + */ +void ViewShell::construct() +{ + mbHasRulers = false; + mpActiveWindow = nullptr; + mpView = nullptr; + mpFrameView = nullptr; + mpZoomList = nullptr; + mbStartShowWithDialog = false; + mnPrintedHandoutPageNum = 1; + mnPrintedHandoutPageCount = 0; + mpWindowUpdater.reset( new ::sd::WindowUpdater() ); + mpImpl.reset(new Implementation(*this)); + meShellType = ST_NONE; + + OSL_ASSERT (GetViewShell()!=nullptr); + + if (IsMainViewShell()) + GetDocSh()->Connect (this); + + mpZoomList.reset( new ZoomList( this ) ); + + mpContentWindow.reset(VclPtr< ::sd::Window >::Create(GetParentWindow())); + SetActiveWindow (mpContentWindow.get()); + + GetParentWindow()->SetBackground (Wallpaper()); + mpContentWindow->SetBackground (Wallpaper()); + mpContentWindow->SetCenterAllowed(true); + mpContentWindow->SetViewShell(this); + mpContentWindow->SetPosSizePixel( + GetParentWindow()->GetPosPixel(),GetParentWindow()->GetSizePixel()); + + if ( ! GetDocSh()->IsPreview()) + { + // Create scroll bars and the filler between the scroll bars. + mpHorizontalScrollBar.reset (VclPtr<ScrollBar>::Create(GetParentWindow(), WinBits(WB_HSCROLL | WB_DRAG))); + mpHorizontalScrollBar->EnableRTL (false); + mpHorizontalScrollBar->SetRange(Range(0, 32000)); + mpHorizontalScrollBar->SetScrollHdl(LINK(this, ViewShell, HScrollHdl)); + + mpVerticalScrollBar.reset (VclPtr<ScrollBar>::Create(GetParentWindow(), WinBits(WB_VSCROLL | WB_DRAG))); + mpVerticalScrollBar->SetRange(Range(0, 32000)); + mpVerticalScrollBar->SetScrollHdl(LINK(this, ViewShell, VScrollHdl)); + + mpScrollBarBox.reset(VclPtr<ScrollBarBox>::Create(GetParentWindow(), WB_SIZEABLE)); + } + + SetName ("ViewShell"); + + GetDoc()->StartOnlineSpelling(false); + + mpWindowUpdater->SetDocument (GetDoc()); + + // Re-initialize the spell dialog. + ::sd::SpellDialogChildWindow* pSpellDialog = + static_cast< ::sd::SpellDialogChildWindow*> ( + GetViewFrame()->GetChildWindow ( + ::sd::SpellDialogChildWindow::GetChildWindowId())); + if (pSpellDialog != nullptr) + pSpellDialog->InvalidateSpellDialog(); + + // Register the sub shell factory. + mpImpl->mpSubShellFactory = std::make_shared<ViewShellObjectBarFactory>(*this); + GetViewShellBase().GetViewShellManager()->AddSubShellFactory(this,mpImpl->mpSubShellFactory); +} + +void ViewShell::doShow() +{ + mpContentWindow->Show(); + static_cast< vcl::Window*>(mpContentWindow.get())->Resize(); + SAL_INFO( + "sd.view", + "content window has size " << mpContentWindow->GetSizePixel().Width() + << " " << mpContentWindow->GetSizePixel().Height()); + + if ( ! GetDocSh()->IsPreview()) + { + // Show scroll bars + mpHorizontalScrollBar->Show(); + + mpVerticalScrollBar->Show(); + maScrBarWH = Size( + mpVerticalScrollBar->GetSizePixel().Width(), + mpHorizontalScrollBar->GetSizePixel().Height()); + + mpScrollBarBox->Show(); + } + + GetParentWindow()->Show(); +} + +void ViewShell::Init (bool bIsMainViewShell) +{ + mpImpl->mbIsInitialized = true; + SetIsMainViewShell(bIsMainViewShell); + if (bIsMainViewShell) + SetActiveWindow (mpContentWindow.get()); +} + +void ViewShell::Exit() +{ + sd::View* pView = GetView(); + if (pView!=nullptr && pView->IsTextEdit()) + { + pView->SdrEndTextEdit(); + pView->UnmarkAll(); + } + + Deactivate (true); + + if (IsMainViewShell()) + GetDocSh()->Disconnect(this); + + SetIsMainViewShell(false); +} + +/** + * set focus to working window + */ +void ViewShell::Activate(bool bIsMDIActivate) +{ + // Do not forward to SfxShell::Activate() + + /* According to MI, nobody is allowed to call GrabFocus, who does not + exactly know from which window the focus is grabbed. Since Activate() + is sent sometimes asynchronous, it can happen, that the wrong window + gets the focus. */ + + if (mpHorizontalRuler) + mpHorizontalRuler->SetActive(); + if (mpVerticalRuler) + mpVerticalRuler->SetActive(); + + if (bIsMDIActivate) + { + // thus, the Navigator will also get a current status + SfxBoolItem aItem( SID_NAVIGATOR_INIT, true ); + if (GetDispatcher() != nullptr) + GetDispatcher()->ExecuteList( + SID_NAVIGATOR_INIT, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aItem }); + + SfxViewShell* pViewShell = GetViewShell(); + OSL_ASSERT (pViewShell!=nullptr); + SfxBindings& rBindings = pViewShell->GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_3D_STATE, true ); + + rtl::Reference< SlideShow > xSlideShow( SlideShow::GetSlideShow( GetViewShellBase() ) ); + if (xSlideShow.is() && xSlideShow->isRunning()) + { + bool bSuccess = xSlideShow->activate(GetViewShellBase()); + assert(bSuccess && "can only return false with a PresentationViewShell"); (void)bSuccess; + } + + if(HasCurrentFunction()) + GetCurrentFunction()->Activate(); + + if(!GetDocSh()->IsUIActive()) + UpdatePreview( GetActualPage() ); + } + + ReadFrameViewData( mpFrameView ); + + if (IsMainViewShell()) + GetDocSh()->Connect(this); +} + +void ViewShell::UIActivating( SfxInPlaceClient* ) +{ + OSL_ASSERT (GetViewShell()!=nullptr); + GetViewShellBase().GetToolBarManager()->ToolBarsDestroyed(); +} + +void ViewShell::UIDeactivated( SfxInPlaceClient* ) +{ + OSL_ASSERT (GetViewShell()!=nullptr); + GetViewShellBase().GetToolBarManager()->ToolBarsDestroyed(); + if ( GetDrawView() ) + GetViewShellBase().GetToolBarManager()->SelectionHasChanged(*this, *GetDrawView()); +} + +void ViewShell::Deactivate(bool bIsMDIActivate) +{ + // remove view from a still active drag'n'drop session + SdTransferable* pDragTransferable = SD_MOD()->pTransferDrag; + + if (IsMainViewShell()) + GetDocSh()->Disconnect(this); + + if( pDragTransferable ) + pDragTransferable->SetView( nullptr ); + + OSL_ASSERT (GetViewShell()!=nullptr); + + // remember view attributes of FrameView + WriteFrameViewData(); + + if (bIsMDIActivate) + { + rtl::Reference< SlideShow > xSlideShow( SlideShow::GetSlideShow( GetViewShellBase() ) ); + if(xSlideShow.is() && xSlideShow->isRunning() ) + xSlideShow->deactivate(); + + if(HasCurrentFunction()) + GetCurrentFunction()->Deactivate(); + } + + if (mpHorizontalRuler) + mpHorizontalRuler->SetActive(false); + if (mpVerticalRuler) + mpVerticalRuler->SetActive(false); + + SfxShell::Deactivate(bIsMDIActivate); +} + +void ViewShell::Shutdown() +{ + Exit (); +} + +bool ViewShell::KeyInput(const KeyEvent& rKEvt, ::sd::Window* pWin) +{ + bool bReturn(false); + + if(pWin) + SetActiveWindow(pWin); + + // give key input first to SfxViewShell to give CTRL+Key + // (e.g. CTRL+SHIFT+'+', to front) priority. + OSL_ASSERT(GetViewShell() != nullptr); + bReturn = GetViewShell()->KeyInput(rKEvt); + + const size_t OriCount = GetView()->GetMarkedObjectList().GetMarkCount(); + if(!bReturn) + { + rtl::Reference< SlideShow > xSlideShow( SlideShow::GetSlideShow( GetViewShellBase() ) ); + if(xSlideShow.is() && xSlideShow->isRunning()) + { + bReturn = xSlideShow->keyInput(rKEvt); + } + else + { + bool bConsumed = false; + if( GetView() ) + bConsumed = GetView()->getSmartTags().KeyInput(rKEvt); + + if( !bConsumed ) + { + rtl::Reference< sdr::SelectionController > xSelectionController( GetView()->getSelectionController() ); + if( !xSelectionController.is() || !xSelectionController->onKeyInput( rKEvt, pWin ) ) + { + if(HasCurrentFunction()) + bReturn = GetCurrentFunction()->KeyInput(rKEvt); + } + else + { + bReturn = true; + if (HasCurrentFunction()) + { + FuText* pTextFunction = dynamic_cast<FuText*>(GetCurrentFunction().get()); + if(pTextFunction != nullptr) + pTextFunction->InvalidateBindings(); + } + } + } + } + } + const size_t EndCount = GetView()->GetMarkedObjectList().GetMarkCount(); + // Here, oriCount or endCount must have one value=0, another value > 0, then to switch focus between Document and shape objects + if(bReturn && (OriCount + EndCount > 0) && (OriCount * EndCount == 0)) + SwitchActiveViewFireFocus(); + + if(!bReturn && GetActiveWindow()) + { + vcl::KeyCode aKeyCode = rKEvt.GetKeyCode(); + + if (aKeyCode.IsMod1() && aKeyCode.IsShift() + && aKeyCode.GetCode() == KEY_R) + { + InvalidateWindows(); + bReturn = true; + } + } + + return bReturn; +} + +void ViewShell::MouseButtonDown(const MouseEvent& rMEvt, ::sd::Window* pWin) +{ + // We have to lock tool bar updates while the mouse button is pressed in + // order to prevent the shape under the mouse to be moved (this happens + // when the number of docked tool bars changes as result of a changed + // selection; this changes the window size and thus the mouse position + // in model coordinates: with respect to model coordinates the mouse + // moves.) + OSL_ASSERT(mpImpl->mpUpdateLockForMouse.expired()); + mpImpl->mpUpdateLockForMouse = ViewShell::Implementation::ToolBarManagerLock::Create( + GetViewShellBase().GetToolBarManager()); + + if ( pWin && !pWin->HasFocus() ) + { + pWin->GrabFocus(); + SetActiveWindow(pWin); + } + + // insert MouseEvent into E3dView + if (GetView() != nullptr) + GetView()->SetMouseEvent(rMEvt); + + bool bConsumed = false; + if( GetView() ) + bConsumed = GetView()->getSmartTags().MouseButtonDown( rMEvt ); + + if( bConsumed ) + return; + + rtl::Reference< sdr::SelectionController > xSelectionController( GetView()->getSelectionController() ); + if( !xSelectionController.is() || !xSelectionController->onMouseButtonDown( rMEvt, pWin ) ) + { + if(HasCurrentFunction()) + GetCurrentFunction()->MouseButtonDown(rMEvt); + } + else + { + if (HasCurrentFunction()) + { + FuText* pTextFunction = dynamic_cast<FuText*>(GetCurrentFunction().get()); + if (pTextFunction != nullptr) + pTextFunction->InvalidateBindings(); + } + } +} + +void ViewShell::SetCursorMm100Position(const Point& rPosition, bool bPoint, bool bClearMark) +{ + if (SdrView* pSdrView = GetView()) + { + rtl::Reference<sdr::SelectionController> xSelectionController(GetView()->getSelectionController()); + if (!xSelectionController.is() || !xSelectionController->setCursorLogicPosition(rPosition, bPoint)) + { + if (pSdrView->GetTextEditObject()) + { + EditView& rEditView = pSdrView->GetTextEditOutlinerView()->GetEditView(); + rEditView.SetCursorLogicPosition(rPosition, bPoint, bClearMark); + } + } + } +} + +uno::Reference<datatransfer::XTransferable> ViewShell::GetSelectionTransferrable() const +{ + SdrView* pSdrView = GetView(); + if (!pSdrView) + return uno::Reference<datatransfer::XTransferable>(); + + if (!pSdrView->GetTextEditObject()) + return uno::Reference<datatransfer::XTransferable>(); + + EditView& rEditView = pSdrView->GetTextEditOutlinerView()->GetEditView(); + return rEditView.GetEditEngine()->CreateTransferable(rEditView.GetSelection()); +} + +void ViewShell::SetGraphicMm100Position(bool bStart, const Point& rPosition) +{ + if (bStart) + { + MouseEvent aClickEvent(rPosition, 1, MouseEventModifiers::SIMPLECLICK, MOUSE_LEFT); + MouseButtonDown(aClickEvent, mpActiveWindow); + MouseEvent aMoveEvent(Point(rPosition.getX(), rPosition.getY()), 0, MouseEventModifiers::SIMPLEMOVE, MOUSE_LEFT); + MouseMove(aMoveEvent, mpActiveWindow); + } + else + { + MouseEvent aMoveEvent(Point(rPosition.getX(), rPosition.getY()), 0, MouseEventModifiers::SIMPLEMOVE, MOUSE_LEFT); + MouseMove(aMoveEvent, mpActiveWindow); + MouseEvent aClickEvent(rPosition, 1, MouseEventModifiers::SIMPLECLICK, MOUSE_LEFT); + MouseButtonUp(aClickEvent, mpActiveWindow); + } +} + +void ViewShell::MouseMove(const MouseEvent& rMEvt, ::sd::Window* pWin) +{ + if (rMEvt.IsLeaveWindow()) + { + if ( ! mpImpl->mpUpdateLockForMouse.expired()) + { + std::shared_ptr<ViewShell::Implementation::ToolBarManagerLock> pLock( + mpImpl->mpUpdateLockForMouse); + if (pLock != nullptr) + pLock->Release(); + } + } + + if ( pWin ) + { + SetActiveWindow(pWin); + } + + // insert MouseEvent into E3dView + if (GetView() != nullptr) + GetView()->SetMouseEvent(rMEvt); + + if(HasCurrentFunction()) + { + rtl::Reference< sdr::SelectionController > xSelectionController( GetView()->getSelectionController() ); + if( !xSelectionController.is() || !xSelectionController->onMouseMove( rMEvt, pWin ) ) + { + if(HasCurrentFunction()) + GetCurrentFunction()->MouseMove(rMEvt); + } + } +} + +void ViewShell::MouseButtonUp(const MouseEvent& rMEvt, ::sd::Window* pWin) +{ + if ( pWin ) + SetActiveWindow(pWin); + + // insert MouseEvent into E3dView + if (GetView() != nullptr) + GetView()->SetMouseEvent(rMEvt); + + if( HasCurrentFunction()) + { + rtl::Reference< sdr::SelectionController > xSelectionController( GetView()->getSelectionController() ); + if( !xSelectionController.is() || !xSelectionController->onMouseButtonUp( rMEvt, pWin ) ) + { + if(HasCurrentFunction()) + GetCurrentFunction()->MouseButtonUp(rMEvt); + } + else + { + if (HasCurrentFunction()) + { + FuText* pTextFunction = dynamic_cast<FuText*>(GetCurrentFunction().get()); + if (pTextFunction != nullptr) + pTextFunction->InvalidateBindings(); + } + } + } + + if ( ! mpImpl->mpUpdateLockForMouse.expired()) + { + std::shared_ptr<ViewShell::Implementation::ToolBarManagerLock> pLock( + mpImpl->mpUpdateLockForMouse); + if (pLock != nullptr) + pLock->Release(); + } +} + +void ViewShell::Command(const CommandEvent& rCEvt, ::sd::Window* pWin) +{ + bool bDone = HandleScrollCommand (rCEvt, pWin); + + if( bDone ) + return; + + if( rCEvt.GetCommand() == CommandEventId::InputLanguageChange ) + { + //#i42732# update state of fontname if input language changes + GetViewFrame()->GetBindings().Invalidate( SID_ATTR_CHAR_FONT ); + GetViewFrame()->GetBindings().Invalidate( SID_ATTR_CHAR_FONTHEIGHT ); + } + else + { + bool bConsumed = false; + if( GetView() ) + bConsumed = GetView()->getSmartTags().Command(rCEvt); + + if( !bConsumed && HasCurrentFunction()) + GetCurrentFunction()->Command(rCEvt); + } +} + +bool ViewShell::Notify(NotifyEvent const & rNEvt, ::sd::Window* pWin) +{ + // handle scroll commands when they arrived at child windows + bool bRet = false; + if( rNEvt.GetType() == MouseNotifyEvent::COMMAND ) + { + // note: dynamic_cast is not possible as GetData() returns a void* + CommandEvent* pCmdEvent = static_cast< CommandEvent* >(rNEvt.GetData()); + bRet = HandleScrollCommand(*pCmdEvent, pWin); + } + return bRet; +} + +bool ViewShell::HandleScrollCommand(const CommandEvent& rCEvt, ::sd::Window* pWin) +{ + bool bDone = false; + + switch( rCEvt.GetCommand() ) + { + case CommandEventId::Swipe: + { + rtl::Reference< SlideShow > xSlideShow( SlideShow::GetSlideShow( GetViewShellBase() ) ); + if (xSlideShow.is()) + { + const CommandSwipeData* pSwipeData = rCEvt.GetSwipeData(); + bDone = xSlideShow->swipe(*pSwipeData); + } + } + break; + case CommandEventId::LongPress: + { + rtl::Reference< SlideShow > xSlideShow( SlideShow::GetSlideShow( GetViewShellBase() ) ); + if (xSlideShow.is()) + { + const CommandLongPressData* pLongPressData = rCEvt.GetLongPressData(); + bDone = xSlideShow->longpress(*pLongPressData); + } + } + break; + + case CommandEventId::Wheel: + { + Reference< XSlideShowController > xSlideShowController( SlideShow::GetSlideShowController(GetViewShellBase() ) ); + if( xSlideShowController.is() ) + { + // We ignore zooming with control+mouse wheel. + const CommandWheelData* pData = rCEvt.GetWheelData(); + if( pData && !pData->GetModifier() && ( pData->GetMode() == CommandWheelMode::SCROLL ) && !pData->IsHorz() ) + { + ::tools::Long nDelta = pData->GetDelta(); + if( nDelta > 0 ) + xSlideShowController->gotoPreviousSlide(); + else if( nDelta < 0 ) + xSlideShowController->gotoNextEffect(); + } + break; + } + } + [[fallthrough]]; + case CommandEventId::StartAutoScroll: + case CommandEventId::AutoScroll: + { + const CommandWheelData* pData = rCEvt.GetWheelData(); + + if (pData != nullptr) + { + if (pData->IsMod1()) + { + if( !GetDocSh()->IsUIActive() ) + { + const ::tools::Long nOldZoom = GetActiveWindow()->GetZoom(); + ::tools::Long nNewZoom; + Point aOldMousePos = GetActiveWindow()->PixelToLogic(rCEvt.GetMousePosPixel()); + + if( pData->GetDelta() < 0 ) + nNewZoom = std::max<::tools::Long>( pWin->GetMinZoom(), basegfx::zoomtools::zoomOut( nOldZoom )); + else + nNewZoom = std::min<::tools::Long>( pWin->GetMaxZoom(), basegfx::zoomtools::zoomIn( nOldZoom )); + + SetZoom( nNewZoom ); + // Keep mouse at same doc point before zoom + Point aNewMousePos = GetActiveWindow()->PixelToLogic(rCEvt.GetMousePosPixel()); + SetWinViewPos(GetWinViewPos() - (aNewMousePos - aOldMousePos)); + + Invalidate( SID_ATTR_ZOOM ); + Invalidate( SID_ATTR_ZOOMSLIDER ); + + bDone = true; + } + } + else + { + if( mpContentWindow.get() == pWin ) + { + sal_uLong nScrollLines = pData->GetScrollLines(); + if(IsPageFlipMode()) + nScrollLines = COMMAND_WHEEL_PAGESCROLL; + CommandWheelData aWheelData( pData->GetDelta(),pData->GetNotchDelta(), + nScrollLines,pData->GetMode(),pData->GetModifier(),pData->IsHorz() ); + CommandEvent aReWrite( rCEvt.GetMousePosPixel(),rCEvt.GetCommand(), + rCEvt.IsMouseEvent(),static_cast<const void *>(&aWheelData) ); + bDone = pWin->HandleScrollCommand( aReWrite, + mpHorizontalScrollBar.get(), + mpVerticalScrollBar.get()); + } + } + } + } + break; + + default: + break; + } + + return bDone; +} + +void ViewShell::SetupRulers() +{ + if(!mbHasRulers || !mpContentWindow || SlideShow::IsRunning(GetViewShellBase())) + return; + + ::tools::Long nHRulerOfs = 0; + + if ( !mpVerticalRuler ) + { + mpVerticalRuler.reset(CreateVRuler(GetActiveWindow())); + if ( mpVerticalRuler ) + { + nHRulerOfs = mpVerticalRuler->GetSizePixel().Width(); + mpVerticalRuler->SetActive(); + mpVerticalRuler->Show(); + } + } + if ( !mpHorizontalRuler ) + { + mpHorizontalRuler.reset(CreateHRuler(GetActiveWindow())); + if ( mpHorizontalRuler ) + { + mpHorizontalRuler->SetWinPos(nHRulerOfs); + mpHorizontalRuler->SetActive(); + mpHorizontalRuler->Show(); + } + } +} + +const SvxNumBulletItem* ViewShell::GetNumBulletItem(SfxItemSet& aNewAttr, TypedWhichId<SvxNumBulletItem>& nNumItemId) +{ + const SvxNumBulletItem* pTmpItem = aNewAttr.GetItemIfSet(nNumItemId, false); + if(pTmpItem) + return pTmpItem; + + nNumItemId = aNewAttr.GetPool()->GetWhich(SID_ATTR_NUMBERING_RULE); + pTmpItem = aNewAttr.GetItemIfSet(nNumItemId, false); + if(pTmpItem) + return pTmpItem; + + bool bOutliner = false; + bool bTitle = false; + + if( mpView ) + { + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + const size_t nCount = rMarkList.GetMarkCount(); + + for(size_t nNum = 0; nNum < nCount; ++nNum) + { + SdrObject* pObj = rMarkList.GetMark(nNum)->GetMarkedSdrObj(); + if( pObj->GetObjInventor() == SdrInventor::Default ) + { + switch(pObj->GetObjIdentifier()) + { + case SdrObjKind::TitleText: + bTitle = true; + break; + case SdrObjKind::OutlineText: + bOutliner = true; + break; + default: + break; + } + } + } + } + + const SvxNumBulletItem *pItem = nullptr; + if(bOutliner) + { + SfxStyleSheetBasePool* pSSPool = mpView->GetDocSh()->GetStyleSheetPool(); + SfxStyleSheetBase* pFirstStyleSheet = pSSPool->Find( STR_LAYOUT_OUTLINE + " 1", SfxStyleFamily::Pseudo); + if( pFirstStyleSheet ) + pItem = pFirstStyleSheet->GetItemSet().GetItemIfSet(EE_PARA_NUMBULLET, false); + } + + if( pItem == nullptr ) + pItem = aNewAttr.GetPool()->GetSecondaryPool()->GetPoolDefaultItem(EE_PARA_NUMBULLET); + + aNewAttr.Put(pItem->CloneSetWhich(EE_PARA_NUMBULLET)); + + if(bTitle && aNewAttr.GetItemState(EE_PARA_NUMBULLET) == SfxItemState::SET ) + { + const SvxNumBulletItem* pBulletItem = aNewAttr.GetItem(EE_PARA_NUMBULLET); + const SvxNumRule& rRule = pBulletItem->GetNumRule(); + SvxNumRule aNewRule( rRule ); + aNewRule.SetFeatureFlag( SvxNumRuleFlags::NO_NUMBERS ); + + SvxNumBulletItem aNewItem( std::move(aNewRule), EE_PARA_NUMBULLET ); + aNewAttr.Put(aNewItem); + } + + pTmpItem = aNewAttr.GetItemIfSet(nNumItemId, false); + + return pTmpItem; +} + +void ViewShell::Resize() +{ + SetupRulers (); + + if (mpParentWindow == nullptr) + return; + + // Make sure that the new size is not degenerate. + const Size aSize (mpParentWindow->GetSizePixel()); + if (aSize.IsEmpty()) + return; + + // Remember the new position and size. + maViewPos = Point(0,0); + maViewSize = aSize; + + // Rearrange the UI elements to take care of the new position and size. + ArrangeGUIElements (); + // end of included AdjustPosSizePixel. + + ::sd::View* pView = GetView(); + + if (pView) + pView->VisAreaChanged(GetActiveWindow()->GetOutDev()); +} + +SvBorder ViewShell::GetBorder() +{ + SvBorder aBorder; + + // Horizontal scrollbar. + if (mpHorizontalScrollBar + && mpHorizontalScrollBar->IsVisible()) + { + aBorder.Bottom() = maScrBarWH.Height(); + } + + // Vertical scrollbar. + if (mpVerticalScrollBar + && mpVerticalScrollBar->IsVisible()) + { + aBorder.Right() = maScrBarWH.Width(); + } + + // Place horizontal ruler below tab bar. + if (mbHasRulers && mpContentWindow) + { + SetupRulers(); + if (mpHorizontalRuler) + aBorder.Top() = mpHorizontalRuler->GetSizePixel().Height(); + if (mpVerticalRuler) + aBorder.Left() = mpVerticalRuler->GetSizePixel().Width(); + } + + return aBorder; +} + +void ViewShell::ArrangeGUIElements() +{ + if (mpImpl->mbArrangeActive) + return; + if (maViewSize.IsEmpty()) + return; + mpImpl->mbArrangeActive = true; + + // Calculate border for in-place editing. + ::tools::Long nLeft = maViewPos.X(); + ::tools::Long nTop = maViewPos.Y(); + ::tools::Long nRight = maViewPos.X() + maViewSize.Width(); + ::tools::Long nBottom = maViewPos.Y() + maViewSize.Height(); + + // Horizontal scrollbar. + if (mpHorizontalScrollBar + && mpHorizontalScrollBar->IsVisible()) + { + nBottom -= maScrBarWH.Height(); + if (mpLayerTabBar && mpLayerTabBar->IsVisible()) + nBottom -= mpLayerTabBar->GetSizePixel().Height(); + mpHorizontalScrollBar->SetPosSizePixel ( + Point(nLeft, nBottom), + Size(nRight - nLeft - maScrBarWH.Width(), maScrBarWH.Height())); + } + + // Vertical scrollbar. + if (mpVerticalScrollBar + && mpVerticalScrollBar->IsVisible()) + { + nRight -= maScrBarWH.Width(); + mpVerticalScrollBar->SetPosSizePixel ( + Point(nRight,nTop), + Size (maScrBarWH.Width(), nBottom-nTop)); + } + + // Filler in the lower right corner. + if (mpScrollBarBox) + { + if (mpHorizontalScrollBar + && mpHorizontalScrollBar->IsVisible() + && mpVerticalScrollBar + && mpVerticalScrollBar->IsVisible()) + { + mpScrollBarBox->Show(); + mpScrollBarBox->SetPosSizePixel(Point(nRight, nBottom), maScrBarWH); + } + else + mpScrollBarBox->Hide(); + } + + // Place horizontal ruler below tab bar. + if (mbHasRulers && mpContentWindow) + { + if (mpHorizontalRuler) + { + Size aRulerSize = mpHorizontalRuler->GetSizePixel(); + aRulerSize.setWidth( nRight - nLeft ); + mpHorizontalRuler->SetPosSizePixel ( + Point(nLeft,nTop), aRulerSize); + if (mpVerticalRuler) + mpHorizontalRuler->SetBorderPos( + mpVerticalRuler->GetSizePixel().Width()-1); + nTop += aRulerSize.Height(); + } + if (mpVerticalRuler) + { + Size aRulerSize = mpVerticalRuler->GetSizePixel(); + aRulerSize.setHeight( nBottom - nTop ); + mpVerticalRuler->SetPosSizePixel ( + Point (nLeft,nTop), aRulerSize); + nLeft += aRulerSize.Width(); + } + } + + rtl::Reference< SlideShow > xSlideShow( SlideShow::GetSlideShow( GetViewShellBase() ) ); + + // The size of the window of the center pane is set differently from + // that of the windows in the docking windows. + bool bSlideShowActive = (xSlideShow.is() && xSlideShow->isRunning()) && !xSlideShow->isFullScreen() && xSlideShow->getAnimationMode() == ANIMATIONMODE_SHOW; + if ( !bSlideShowActive) + { + OSL_ASSERT (GetViewShell()!=nullptr); + + if (mpContentWindow) + mpContentWindow->SetPosSizePixel( + Point(nLeft,nTop), + Size(nRight-nLeft,nBottom-nTop)); + } + + // Windows in the center and rulers at the left and top side. + maAllWindowRectangle = ::tools::Rectangle( + maViewPos, + Size(maViewSize.Width()-maScrBarWH.Width(), + maViewSize.Height()-maScrBarWH.Height())); + + if (mpContentWindow) + mpContentWindow->UpdateMapOrigin(); + + UpdateScrollBars(); + + mpImpl->mbArrangeActive = false; +} + +void ViewShell::SetUIUnit(FieldUnit eUnit) +{ + // Set unit at horizontal and vertical rulers. + if (mpHorizontalRuler) + mpHorizontalRuler->SetUnit(eUnit); + + if (mpVerticalRuler) + mpVerticalRuler->SetUnit(eUnit); +} + +/** + * set DefTab at horizontal rulers + */ +void ViewShell::SetDefTabHRuler( sal_uInt16 nDefTab ) +{ + if (mpHorizontalRuler) + mpHorizontalRuler->SetDefTabDist( nDefTab ); +} + +/** Tell the FmFormShell that the view shell is closing. Give it the + opportunity to prevent that. +*/ +bool ViewShell::PrepareClose (bool bUI) +{ + bool bResult = true; + + FmFormShell* pFormShell = GetViewShellBase().GetFormShellManager()->GetFormShell(); + if (pFormShell != nullptr) + bResult = pFormShell->PrepareClose (bUI); + + return bResult; +} + +void ViewShell::UpdatePreview (SdPage*) +{ + // Do nothing. After the actual preview has been removed, + // OutlineViewShell::UpdatePreview() is the place where something + // useful is still done. +} + +SfxUndoManager* ViewShell::ImpGetUndoManager() const +{ + const ViewShell* pMainViewShell = GetViewShellBase().GetMainViewShell().get(); + + if( pMainViewShell == nullptr ) + pMainViewShell = this; + + ::sd::View* pView = pMainViewShell->GetView(); + + // check for text edit our outline view + if( pView ) + { + if( pMainViewShell->GetShellType() == ViewShell::ST_OUTLINE ) + { + OutlineView* pOlView = dynamic_cast< OutlineView* >( pView ); + if( pOlView ) + { + ::Outliner& rOutl = pOlView->GetOutliner(); + return &rOutl.GetUndoManager(); + } + } + else if( pView->IsTextEdit() ) + { + SdrOutliner* pOL = pView->GetTextEditOutliner(); + if( pOL ) + return &pOL->GetUndoManager(); + } + } + + if( GetDocSh() ) + return GetDocSh()->GetUndoManager(); + + return nullptr; +} + +void ViewShell::ImpGetUndoStrings(SfxItemSet &rSet) const +{ + SfxUndoManager* pUndoManager = ImpGetUndoManager(); + if(!pUndoManager) + return; + + sal_uInt16 nCount(pUndoManager->GetUndoActionCount()); + if(nCount) + { + // prepare list + std::vector<OUString> aStringList; + aStringList.reserve(nCount); + for (sal_uInt16 a = 0; a < nCount; ++a) + { + // generate one String in list per undo step + aStringList.push_back( pUndoManager->GetUndoActionComment(a) ); + } + + // set item + rSet.Put(SfxStringListItem(SID_GETUNDOSTRINGS, &aStringList)); + } + else + { + rSet.DisableItem(SID_GETUNDOSTRINGS); + } +} + +void ViewShell::ImpGetRedoStrings(SfxItemSet &rSet) const +{ + SfxUndoManager* pUndoManager = ImpGetUndoManager(); + if(!pUndoManager) + return; + + sal_uInt16 nCount(pUndoManager->GetRedoActionCount()); + if(nCount) + { + // prepare list + ::std::vector< OUString > aStringList; + aStringList.reserve(nCount); + for(sal_uInt16 a = 0; a < nCount; a++) + // generate one String in list per undo step + aStringList.push_back( pUndoManager->GetRedoActionComment(a) ); + + // set item + rSet.Put(SfxStringListItem(SID_GETREDOSTRINGS, &aStringList)); + } + else + { + rSet.DisableItem(SID_GETREDOSTRINGS); + } +} + +namespace { + +class KeepSlideSorterInSyncWithPageChanges +{ + sd::slidesorter::view::SlideSorterView::DrawLock m_aDrawLock; + sd::slidesorter::controller::SlideSorterController::ModelChangeLock m_aModelLock; + sd::slidesorter::controller::PageSelector::UpdateLock m_aUpdateLock; + sd::slidesorter::controller::SelectionObserver::Context m_aContext; + +public: + explicit KeepSlideSorterInSyncWithPageChanges(sd::slidesorter::SlideSorter const & rSlideSorter) + : m_aDrawLock(rSlideSorter) + , m_aModelLock(rSlideSorter.GetController()) + , m_aUpdateLock(rSlideSorter) + , m_aContext(rSlideSorter) + { + } +}; + +} + +void ViewShell::ImpSidUndo(SfxRequest& rReq) +{ + //The xWatcher keeps the SlideSorter selection in sync + //with the page insertions/deletions that Undo may introduce + std::unique_ptr<KeepSlideSorterInSyncWithPageChanges, o3tl::default_delete<KeepSlideSorterInSyncWithPageChanges>> xWatcher; + slidesorter::SlideSorterViewShell* pSlideSorterViewShell + = slidesorter::SlideSorterViewShell::GetSlideSorter(GetViewShellBase()); + if (pSlideSorterViewShell) + xWatcher.reset(new KeepSlideSorterInSyncWithPageChanges(pSlideSorterViewShell->GetSlideSorter())); + + SfxUndoManager* pUndoManager = ImpGetUndoManager(); + sal_uInt16 nNumber(1); + const SfxItemSet* pReqArgs = rReq.GetArgs(); + bool bRepair = false; + + if(pReqArgs) + { + const SfxUInt16Item* pUIntItem = static_cast<const SfxUInt16Item*>(&pReqArgs->Get(SID_UNDO)); + nNumber = pUIntItem->GetValue(); + + // Repair mode: allow undo/redo of all undo actions, even if access would + // be limited based on the view shell ID. + if (const SfxBoolItem* pRepairItem = pReqArgs->GetItemIfSet(SID_REPAIRPACKAGE, false)) + bRepair = pRepairItem->GetValue(); + } + + if(nNumber && pUndoManager) + { + sal_uInt16 nCount(pUndoManager->GetUndoActionCount()); + if(nCount >= nNumber) + { + if (comphelper::LibreOfficeKit::isActive() && !bRepair) + { + // If another view created the first undo action, prevent redoing it from this view. + const SfxUndoAction* pAction = pUndoManager->GetUndoAction(); + if (pAction->GetViewShellId() != GetViewShellBase().GetViewShellId()) + { + rReq.SetReturnValue(SfxUInt32Item(SID_UNDO, static_cast<sal_uInt32>(SID_REPAIRPACKAGE))); + return; + } + } + + try + { + // when UndoStack is cleared by ModifyPageUndoAction + // the nCount may have changed, so test GetUndoActionCount() + while(nNumber-- && pUndoManager->GetUndoActionCount()) + pUndoManager->Undo(); + } + catch( const Exception& ) + { + // no need to handle. By definition, the UndoManager handled this by clearing the + // Undo/Redo stacks + } + } + + // refresh rulers, maybe UNDO was move of TAB marker in ruler + if (mbHasRulers) + Invalidate(SID_ATTR_TABSTOP); + } + + // This one is corresponding to the default handling + // of SID_UNDO in sfx2 + GetViewFrame()->GetBindings().InvalidateAll(false); + + rReq.Done(); +} + +void ViewShell::ImpSidRedo(SfxRequest& rReq) +{ + //The xWatcher keeps the SlideSorter selection in sync + //with the page insertions/deletions that Undo may introduce + std::unique_ptr<KeepSlideSorterInSyncWithPageChanges, o3tl::default_delete<KeepSlideSorterInSyncWithPageChanges>> xWatcher; + slidesorter::SlideSorterViewShell* pSlideSorterViewShell + = slidesorter::SlideSorterViewShell::GetSlideSorter(GetViewShellBase()); + if (pSlideSorterViewShell) + xWatcher.reset(new KeepSlideSorterInSyncWithPageChanges(pSlideSorterViewShell->GetSlideSorter())); + + SfxUndoManager* pUndoManager = ImpGetUndoManager(); + sal_uInt16 nNumber(1); + const SfxItemSet* pReqArgs = rReq.GetArgs(); + bool bRepair = false; + + if(pReqArgs) + { + const SfxUInt16Item* pUIntItem = static_cast<const SfxUInt16Item*>(&pReqArgs->Get(SID_REDO)); + nNumber = pUIntItem->GetValue(); + // Repair mode: allow undo/redo of all undo actions, even if access would + // be limited based on the view shell ID. + if (const SfxBoolItem* pRepairItem = pReqArgs->GetItemIfSet(SID_REPAIRPACKAGE, false)) + bRepair = pRepairItem->GetValue(); + } + + if(nNumber && pUndoManager) + { + sal_uInt16 nCount(pUndoManager->GetRedoActionCount()); + if(nCount >= nNumber) + { + if (comphelper::LibreOfficeKit::isActive() && !bRepair) + { + // If another view created the first undo action, prevent redoing it from this view. + const SfxUndoAction* pAction = pUndoManager->GetRedoAction(); + if (pAction->GetViewShellId() != GetViewShellBase().GetViewShellId()) + { + rReq.SetReturnValue(SfxUInt32Item(SID_REDO, static_cast<sal_uInt32>(SID_REPAIRPACKAGE))); + return; + } + } + + try + { + // when UndoStack is cleared by ModifyPageRedoAction + // the nCount may have changed, so test GetRedoActionCount() + while(nNumber-- && pUndoManager->GetRedoActionCount()) + pUndoManager->Redo(); + } + catch( const Exception& ) + { + // no need to handle. By definition, the UndoManager handled this by clearing the + // Undo/Redo stacks + } + } + + // refresh rulers, maybe REDO was move of TAB marker in ruler + if (mbHasRulers) + { + Invalidate(SID_ATTR_TABSTOP); + } + } + + // This one is corresponding to the default handling + // of SID_UNDO in sfx2 + GetViewFrame()->GetBindings().InvalidateAll(false); + + rReq.Done(); +} + +void ViewShell::ExecReq( SfxRequest& rReq ) +{ + sal_uInt16 nSlot = rReq.GetSlot(); + switch( nSlot ) + { + case SID_MAIL_SCROLLBODY_PAGEDOWN: + { + rtl::Reference<FuPoor> xFunc( GetCurrentFunction() ); + if( xFunc.is() ) + ScrollLines( 0, -1 ); + + rReq.Done(); + } + break; + + case SID_OUTPUT_QUALITY_COLOR: + case SID_OUTPUT_QUALITY_GRAYSCALE: + case SID_OUTPUT_QUALITY_BLACKWHITE: + case SID_OUTPUT_QUALITY_CONTRAST: + { + DrawModeFlags nMode = OUTPUT_DRAWMODE_COLOR; + + switch( nSlot ) + { + case SID_OUTPUT_QUALITY_COLOR: nMode = OUTPUT_DRAWMODE_COLOR; break; + case SID_OUTPUT_QUALITY_GRAYSCALE: nMode = OUTPUT_DRAWMODE_GRAYSCALE; break; + case SID_OUTPUT_QUALITY_BLACKWHITE: nMode = OUTPUT_DRAWMODE_BLACKWHITE; break; + case SID_OUTPUT_QUALITY_CONTRAST: nMode = OUTPUT_DRAWMODE_CONTRAST; break; + } + + GetActiveWindow()->GetOutDev()->SetDrawMode( nMode ); + mpFrameView->SetDrawMode( nMode ); + + GetActiveWindow()->Invalidate(); + + Invalidate(); + rReq.Done(); + break; + } + } +} + +/** This default implementation returns only an empty reference. See derived + classes for more interesting examples. +*/ +css::uno::Reference<css::accessibility::XAccessible> +ViewShell::CreateAccessibleDocumentView (::sd::Window* ) +{ + OSL_FAIL("ViewShell::CreateAccessibleDocumentView should not be called!, perhaps Meyers, 3rd edition, Item 9:"); + + return css::uno::Reference<css::accessibility::XAccessible> (); +} + +::sd::WindowUpdater* ViewShell::GetWindowUpdater() const +{ + return mpWindowUpdater.get(); +} + +ViewShellBase& ViewShell::GetViewShellBase() const +{ + return *static_cast<ViewShellBase*>(GetViewShell()); +} + +ViewShell::ShellType ViewShell::GetShellType() const +{ + return meShellType; +} + +DrawDocShell* ViewShell::GetDocSh() const +{ + return GetViewShellBase().GetDocShell(); +} + +SdDrawDocument* ViewShell::GetDoc() const +{ + return GetViewShellBase().GetDocument(); +} + +ErrCode ViewShell::DoVerb(sal_Int32 /*nVerb*/) +{ + return ERRCODE_NONE; +} + +void ViewShell::SetCurrentFunction( const rtl::Reference<FuPoor>& xFunction) +{ + if( mxCurrentFunction.is() && (mxOldFunction != mxCurrentFunction) ) + mxCurrentFunction->Dispose(); + rtl::Reference<FuPoor> xDisposeAfterNewOne( mxCurrentFunction ); + mxCurrentFunction = xFunction; +} + +void ViewShell::SetOldFunction(const rtl::Reference<FuPoor>& xFunction) +{ + if( mxOldFunction.is() && (xFunction != mxOldFunction) && (mxCurrentFunction != mxOldFunction) ) + mxOldFunction->Dispose(); + + rtl::Reference<FuPoor> xDisposeAfterNewOne( mxOldFunction ); + mxOldFunction = xFunction; +} + +/** this method deactivates the current function. If an old function is + saved, this will become activated and current function. +*/ +void ViewShell::Cancel() +{ + if(mxCurrentFunction.is() && (mxCurrentFunction != mxOldFunction )) + { + rtl::Reference<FuPoor> xTemp( mxCurrentFunction ); + mxCurrentFunction.clear(); + xTemp->Deactivate(); + xTemp->Dispose(); + } + + if(mxOldFunction.is()) + { + mxCurrentFunction = mxOldFunction; + mxCurrentFunction->Activate(); + } +} + +void ViewShell::DeactivateCurrentFunction( bool bPermanent /* == false */ ) +{ + if( mxCurrentFunction.is() ) + { + if(bPermanent && (mxOldFunction == mxCurrentFunction)) + mxOldFunction.clear(); + + mxCurrentFunction->Deactivate(); + if( mxCurrentFunction != mxOldFunction ) + mxCurrentFunction->Dispose(); + + rtl::Reference<FuPoor> xDisposeAfterNewOne( mxCurrentFunction ); + mxCurrentFunction.clear(); + } +} + +void ViewShell::DisposeFunctions() +{ + if(mxCurrentFunction.is()) + { + rtl::Reference<FuPoor> xTemp( mxCurrentFunction ); + mxCurrentFunction.clear(); + xTemp->Deactivate(); + xTemp->Dispose(); + } + + if(mxOldFunction.is()) + { + rtl::Reference<FuPoor> xDisposeAfterNewOne( mxOldFunction ); + mxOldFunction->Dispose(); + mxOldFunction.clear(); + } +} + +bool ViewShell::IsMainViewShell() const +{ + return mpImpl->mbIsMainViewShell; +} + +void ViewShell::SetIsMainViewShell (bool bIsMainViewShell) +{ + if (bIsMainViewShell != mpImpl->mbIsMainViewShell) + { + mpImpl->mbIsMainViewShell = bIsMainViewShell; + if (bIsMainViewShell) + GetDocSh()->Connect (this); + else + GetDocSh()->Disconnect (this); + } +} + +void ViewShell::PrePaint() +{ +} + +void ViewShell::Paint (const ::tools::Rectangle&, ::sd::Window* ) +{ +} + +void ViewShell::ShowUIControls (bool bVisible) +{ + if (mbHasRulers) + { + if (mpHorizontalRuler) + mpHorizontalRuler->Show( bVisible ); + + if (mpVerticalRuler) + mpVerticalRuler->Show( bVisible ); + } + + if (mpVerticalScrollBar) + mpVerticalScrollBar->Show( bVisible ); + + if (mpHorizontalScrollBar) + mpHorizontalScrollBar->Show( bVisible ); + + if (mpScrollBarBox) + mpScrollBarBox->Show(bVisible); + + if (mpContentWindow) + mpContentWindow->Show( bVisible ); +} + +bool ViewShell::RelocateToParentWindow (vcl::Window* pParentWindow) +{ + mpParentWindow = pParentWindow; + + mpParentWindow->SetBackground (Wallpaper()); + + if (mpContentWindow) + mpContentWindow->SetParent(pParentWindow); + + if (mpHorizontalScrollBar) + mpHorizontalScrollBar->SetParent(mpParentWindow); + if (mpVerticalScrollBar) + mpVerticalScrollBar->SetParent(mpParentWindow); + if (mpScrollBarBox) + mpScrollBarBox->SetParent(mpParentWindow); + + return true; +} + +void ViewShell::SwitchViewFireFocus(const css::uno::Reference< css::accessibility::XAccessible >& xAcc ) +{ + if (xAcc) + { + ::accessibility::AccessibleDocumentViewBase* pBase = static_cast< ::accessibility::AccessibleDocumentViewBase* >(xAcc.get()); + if (pBase) + pBase->SwitchViewActivated(); + } +} +void ViewShell::SwitchActiveViewFireFocus() +{ + if (mpContentWindow) + { + SwitchViewFireFocus(mpContentWindow->GetAccessible(false)); + } +} +// move these two methods from DrawViewShell. +void ViewShell::fireSwitchCurrentPage(sal_Int32 pageIndex) +{ + GetViewShellBase().GetDrawController().fireSwitchCurrentPage(pageIndex); +} +void ViewShell::NotifyAccUpdate( ) +{ + GetViewShellBase().GetDrawController().NotifyAccUpdate(); +} + +weld::Window* ViewShell::GetFrameWeld() const +{ + return mpActiveWindow ? mpActiveWindow->GetFrameWeld() : nullptr; +} + +sd::Window* ViewShell::GetContentWindow() const +{ + return mpContentWindow.get(); +} + +} // end of namespace sd + +//===== ViewShellObjectBarFactory ============================================= + +namespace { + +ViewShellObjectBarFactory::ViewShellObjectBarFactory ( + ::sd::ViewShell& rViewShell) + : mrViewShell (rViewShell) +{ +} + +SfxShell* ViewShellObjectBarFactory::CreateShell( ::sd::ShellId nId ) +{ + SfxShell* pShell = nullptr; + + ::sd::View* pView = mrViewShell.GetView(); + switch (nId) + { + case ToolbarId::Bezier_Toolbox_Sd: + pShell = new ::sd::BezierObjectBar(&mrViewShell, pView); + break; + + case ToolbarId::Draw_Text_Toolbox_Sd: + pShell = new ::sd::TextObjectBar( + &mrViewShell, mrViewShell.GetDoc()->GetPool(), pView); + break; + + case ToolbarId::Draw_Graf_Toolbox: + pShell = new ::sd::GraphicObjectBar(&mrViewShell, pView); + break; + + case ToolbarId::Draw_Media_Toolbox: + pShell = new ::sd::MediaObjectBar(&mrViewShell, pView); + break; + + case ToolbarId::Draw_Table_Toolbox: + pShell = ::sd::ui::table::CreateTableObjectBar( mrViewShell, pView ); + break; + + case ToolbarId::Svx_Extrusion_Bar: + pShell = new svx::ExtrusionBar( + &mrViewShell.GetViewShellBase()); + break; + + case ToolbarId::Svx_Fontwork_Bar: + pShell = new svx::FontworkBar( + &mrViewShell.GetViewShellBase()); + break; + + default: + pShell = nullptr; + break; + } + + return pShell; +} + +void ViewShellObjectBarFactory::ReleaseShell (SfxShell* pShell) +{ + delete pShell; +} + +} // end of anonymous namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/zoomlist.cxx b/sd/source/ui/view/zoomlist.cxx new file mode 100644 index 000000000..86a3de63b --- /dev/null +++ b/sd/source/ui/view/zoomlist.cxx @@ -0,0 +1,94 @@ +/* -*- 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 <zoomlist.hxx> + +#include <svx/svxids.hrc> +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> + +#include <ViewShell.hxx> + +namespace sd +{ +#define MAX_ENTRIES 10 + +ZoomList::ZoomList(ViewShell* pViewShell) + : mpViewShell(pViewShell) + , mnCurPos(0) +{ +} + +void ZoomList::InsertZoomRect(const ::tools::Rectangle& rRect) +{ + size_t nRectCount = maRectangles.size(); + + if (nRectCount >= MAX_ENTRIES) + maRectangles.erase(maRectangles.begin()); + else if (nRectCount == 0) + mnCurPos = 0; + else + mnCurPos++; + + maRectangles.insert(maRectangles.begin() + mnCurPos, rRect); + + SfxBindings& rBindings = mpViewShell->GetViewFrame()->GetBindings(); + rBindings.Invalidate(SID_ZOOM_NEXT); + rBindings.Invalidate(SID_ZOOM_PREV); +} + +::tools::Rectangle const& ZoomList::GetNextZoomRect() +{ + mnCurPos++; + size_t nRectCount = maRectangles.size(); + + if (nRectCount > 0 && mnCurPos > nRectCount - 1) + mnCurPos = nRectCount - 1; + + SfxBindings& rBindings = mpViewShell->GetViewFrame()->GetBindings(); + rBindings.Invalidate(SID_ZOOM_NEXT); + rBindings.Invalidate(SID_ZOOM_PREV); + + return maRectangles[mnCurPos]; +} + +::tools::Rectangle const& ZoomList::GetPreviousZoomRect() +{ + if (mnCurPos > 0) + mnCurPos--; + + SfxBindings& rBindings = mpViewShell->GetViewFrame()->GetBindings(); + rBindings.Invalidate(SID_ZOOM_NEXT); + rBindings.Invalidate(SID_ZOOM_PREV); + + return maRectangles[mnCurPos]; +} + +bool ZoomList::IsNextPossible() const +{ + size_t nRectCount = maRectangles.size(); + + return nRectCount > 0 && mnCurPos < nRectCount - 1; +} + +bool ZoomList::IsPreviousPossible() const { return mnCurPos > 0; } + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |