summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--framework/Library_fwk.mk1
-rw-r--r--framework/inc/uielement/styletoolbarcontroller.hxx81
-rw-r--r--framework/source/uielement/menubarmanager.cxx5
-rw-r--r--framework/source/uielement/styletoolbarcontroller.cxx238
-rw-r--r--framework/source/uielement/toolbarmanager.cxx6
5 files changed, 330 insertions, 1 deletions
diff --git a/framework/Library_fwk.mk b/framework/Library_fwk.mk
index e20b8b22cc17..9057aaa6048a 100644
--- a/framework/Library_fwk.mk
+++ b/framework/Library_fwk.mk
@@ -153,6 +153,7 @@ $(eval $(call gb_Library_add_exception_objects,fwk,\
framework/source/uielement/statusbarmerger \
framework/source/uielement/statusbarwrapper \
framework/source/uielement/statusindicatorinterfacewrapper \
+ framework/source/uielement/styletoolbarcontroller \
framework/source/uielement/subtoolbarcontroller \
framework/source/uielement/thesaurusmenucontroller \
framework/source/uielement/togglebuttontoolbarcontroller \
diff --git a/framework/inc/uielement/styletoolbarcontroller.hxx b/framework/inc/uielement/styletoolbarcontroller.hxx
new file mode 100644
index 000000000000..8947647d6fc9
--- /dev/null
+++ b/framework/inc/uielement/styletoolbarcontroller.hxx
@@ -0,0 +1,81 @@
+/* -*- 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/.
+ */
+
+#ifndef INCLUDED_FRAMEWORK_INC_UIELEMENT_STYLETOOLBARCONTROLLER_HXX
+#define INCLUDED_FRAMEWORK_INC_UIELEMENT_STYLETOOLBARCONTROLLER_HXX
+
+#include <svtools/toolboxcontroller.hxx>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+
+namespace framework {
+
+/**
+ * A dispatcher that serves as a proxy for style commands with arguments
+ * i.e. .uno:StyleApply?... in order to provide useful status updates to
+ * generic UI elements such as toolbars or menubar. It listens to special
+ * status commands, and computes a boolean status out of them. Then it
+ * forwards that boolean status to the listener, as if it was the status
+ * of the original command.
+ *
+ * Note that the implementation is minimal: Although the UI element appears
+ * to be the owner of the dispatcher, it's still responsible, as usual, to
+ * call removeStatusListener same amount of times as addStatusListener,
+ * otherwise the dispatcher might not be destructed. In addition this
+ * implementation might hold a hard reference on the owner, and it's the
+ * responsibility of the owner to destroy the dispatcher first, in order
+ * to break the cycle.
+ */
+class StyleDispatcher : public cppu::WeakImplHelper< css::frame::XDispatch, css::frame::XStatusListener >
+{
+public:
+ StyleDispatcher( const css::uno::Reference< css::frame::XFrame >& rFrame,
+ const css::uno::Reference< css::util::XURLTransformer >& rUrlTransformer,
+ const css::util::URL& rURL );
+
+ // XDispatch
+ void SAL_CALL dispatch( const css::util::URL& rURL, const css::uno::Sequence< css::beans::PropertyValue >& rArguments ) override;
+ void SAL_CALL addStatusListener( const css::uno::Reference< css::frame::XStatusListener >& rListener, const css::util::URL& rURL ) override;
+ void SAL_CALL removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& rListener, const css::util::URL& rURL ) override;
+
+ // XStatusListener
+ void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override;
+
+ // XEventListener
+ void SAL_CALL disposing( const css::lang::EventObject& rSource ) override;
+
+private:
+ OUString m_aStyleName, m_aCommand, m_aStatusCommand;
+ css::uno::Reference< css::util::XURLTransformer > m_xUrlTransformer;
+ css::uno::Reference< css::frame::XDispatchProvider > m_xFrame;
+ css::uno::Reference< css::frame::XDispatch > m_xStatusDispatch;
+ css::uno::Reference< css::frame::XStatusListener > m_xOwner;
+};
+
+class StyleToolbarController : public svt::ToolboxController
+{
+public:
+ StyleToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ const css::uno::Reference< css::frame::XFrame >& rFrame,
+ const OUString& rCommand );
+
+ // XUpdatable
+ void SAL_CALL update() override;
+
+ // XStatusListener
+ void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override;
+
+ // XComponent
+ void SAL_CALL dispose() override;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/framework/source/uielement/menubarmanager.cxx b/framework/source/uielement/menubarmanager.cxx
index 6ed14c865dc1..2e99be82a8bb 100644
--- a/framework/source/uielement/menubarmanager.cxx
+++ b/framework/source/uielement/menubarmanager.cxx
@@ -18,6 +18,7 @@
*/
#include <uielement/menubarmanager.hxx>
+#include <uielement/styletoolbarcontroller.hxx>
#include <framework/menuconfiguration.hxx>
#include <framework/addonmenu.hxx>
#include <framework/addonsoptions.hxx>
@@ -823,7 +824,9 @@ IMPL_LINK( MenuBarManager, Activate, Menu *, pMenu, bool )
pMenu->HideItem( pMenuItemHandler->nItemId );
}
- if ( m_bIsBookmarkMenu )
+ if ( aTargetURL.Complete.startsWith( ".uno:StyleApply?" ) )
+ xMenuItemDispatch = new StyleDispatcher( m_xFrame, m_xURLTransformer, aTargetURL );
+ else if ( m_bIsBookmarkMenu )
xMenuItemDispatch = xDispatchProvider->queryDispatch( aTargetURL, pMenuItemHandler->aTargetFrame, 0 );
else
xMenuItemDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
diff --git a/framework/source/uielement/styletoolbarcontroller.cxx b/framework/source/uielement/styletoolbarcontroller.cxx
new file mode 100644
index 000000000000..2a078c2a7900
--- /dev/null
+++ b/framework/source/uielement/styletoolbarcontroller.cxx
@@ -0,0 +1,238 @@
+/* -*- 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 <uielement/styletoolbarcontroller.hxx>
+
+#include <tools/urlobj.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/toolbox.hxx>
+
+#include <com/sun/star/frame/status/Template.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+
+namespace {
+
+OUString MapFamilyToCommand( const OUString& rFamily )
+{
+ if ( rFamily == "ParagraphStyles" ||
+ rFamily == "CellStyles" || // In sc
+ rFamily == "graphics" ) // In sd
+ return OUString( ".uno:ParaStyle" );
+ else if ( rFamily == "CharacterStyles" )
+ return OUString( ".uno:CharStyle" );
+ else if ( rFamily == "PageStyles" )
+ return OUString( ".uno:PageStyle" );
+ else if ( rFamily == "FrameStyles" )
+ return OUString( ".uno:FrameStyle" );
+ else if ( rFamily == "NumberingStyles" )
+ return OUString( ".uno:ListStyle" );
+ else if ( rFamily == "TableStyles" )
+ return OUString( ".uno:TableStyle" );
+
+ return OUString();
+}
+
+OUString GetDisplayFromInternalName( const css::uno::Reference< css::frame::XFrame >& rFrame,
+ const OUString& rStyleName,
+ const OUString& rFamilyName )
+{
+ try
+ {
+ css::uno::Reference< css::frame::XController > xController(
+ rFrame->getController(), css::uno::UNO_SET_THROW );
+ css::uno::Reference< css::style::XStyleFamiliesSupplier > xStylesSupplier(
+ xController->getModel(), css::uno::UNO_QUERY_THROW );
+ css::uno::Reference< css::container::XNameAccess > xFamilies(
+ xStylesSupplier->getStyleFamilies(), css::uno::UNO_SET_THROW );
+
+ css::uno::Reference< css::container::XNameAccess > xStyleSet;
+ xFamilies->getByName( rFamilyName ) >>= xStyleSet;
+ css::uno::Reference< css::beans::XPropertySet > xStyle;
+ xStyleSet->getByName( rStyleName ) >>= xStyle;
+
+ OUString aDisplayName;
+ if ( xStyle.is() )
+ xStyle->getPropertyValue( "DisplayName" ) >>= aDisplayName;
+ return aDisplayName;
+ }
+ catch ( const css::uno::Exception& )
+ {
+ // We couldn't get the display name. As a last resort we'll
+ // try to use the internal name, as was specified in the URL.
+ }
+
+ return rStyleName;
+}
+
+}
+
+namespace framework {
+
+StyleDispatcher::StyleDispatcher( const css::uno::Reference< css::frame::XFrame >& rFrame,
+ const css::uno::Reference< css::util::XURLTransformer >& rUrlTransformer,
+ const css::util::URL& rURL )
+ : m_aCommand( rURL.Complete )
+ , m_xUrlTransformer( rUrlTransformer )
+ , m_xFrame( rFrame, css::uno::UNO_QUERY )
+{
+ SAL_WARN_IF( !m_aCommand.startsWith( ".uno:StyleApply?" ), "fwk.uielement", "Wrong dispatcher!" );
+
+ OUString aParams = rURL.Arguments;
+ OUString aStyleName, aFamilyName;
+ sal_Int32 nIndex = 0;
+ do
+ {
+ OUString aParam = aParams.getToken( 0, '&', nIndex );
+
+ sal_Int32 nParamIndex = 0;
+ OUString aParamName = aParam.getToken( 0, '=', nParamIndex );
+ if ( nParamIndex < 0 )
+ break;
+
+ if ( aParamName == "Style:string" )
+ {
+ OUString aValue = aParam.getToken( 0, '=', nParamIndex );
+ aStyleName = INetURLObject::decode( aValue, INetURLObject::DecodeMechanism::WithCharset );
+ }
+ else if ( aParamName == "FamilyName:string" )
+ {
+ aFamilyName = aParam.getToken( 0, '=', nParamIndex );
+ }
+
+ } while ( nIndex >= 0 );
+
+ m_aStatusCommand = MapFamilyToCommand( aFamilyName );
+ if ( m_aStatusCommand.isEmpty() || aStyleName.isEmpty() )
+ {
+ // We can't provide status updates for this command, but just executing
+ // the command should still work (given that the command is valid).
+ SAL_WARN( "fwk.uielement", "Unable to parse as a style command: " << m_aCommand );
+ return;
+ }
+
+ m_aStyleName = GetDisplayFromInternalName( rFrame, aStyleName, aFamilyName );
+ if ( m_xFrame.is() )
+ {
+ css::util::URL aStatusURL;
+ aStatusURL.Complete = m_aStatusCommand;
+ m_xUrlTransformer->parseStrict( aStatusURL );
+ m_xStatusDispatch = m_xFrame->queryDispatch( aStatusURL, OUString(), 0 );
+ }
+}
+
+void StyleDispatcher::dispatch( const css::util::URL& rURL,
+ const css::uno::Sequence< css::beans::PropertyValue >& rArguments )
+{
+ if ( !m_xFrame.is() )
+ return;
+
+ css::uno::Reference< css::frame::XDispatch > xDispatch( m_xFrame->queryDispatch( rURL, OUString(), 0 ) );
+ if ( xDispatch.is() )
+ xDispatch->dispatch( rURL, rArguments );
+}
+
+void StyleDispatcher::addStatusListener( const css::uno::Reference< css::frame::XStatusListener >& rListener,
+ const css::util::URL& /*rURL*/ )
+{
+ if ( m_xStatusDispatch.is() )
+ {
+ if ( !m_xOwner.is() )
+ m_xOwner.set( rListener );
+
+ css::util::URL aStatusURL;
+ aStatusURL.Complete = m_aStatusCommand;
+ m_xUrlTransformer->parseStrict( aStatusURL );
+ m_xStatusDispatch->addStatusListener( this, aStatusURL );
+ }
+}
+
+void StyleDispatcher::removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& /*rListener*/,
+ const css::util::URL& /*rURL*/ )
+{
+ if ( m_xStatusDispatch.is() )
+ {
+ css::util::URL aStatusURL;
+ aStatusURL.Complete = m_aStatusCommand;
+ m_xUrlTransformer->parseStrict( aStatusURL );
+ m_xStatusDispatch->removeStatusListener( this, aStatusURL );
+ }
+}
+
+void StyleDispatcher::statusChanged( const css::frame::FeatureStateEvent& rEvent )
+{
+ css::frame::status::Template aTemplate;
+ rEvent.State >>= aTemplate;
+
+ css::frame::FeatureStateEvent aEvent;
+ aEvent.FeatureURL.Complete = m_aCommand;
+ m_xUrlTransformer->parseStrict( aEvent.FeatureURL );
+
+ aEvent.IsEnabled = rEvent.IsEnabled;
+ aEvent.Requery = rEvent.Requery;
+ aEvent.State <<= m_aStyleName == aTemplate.StyleName;
+ m_xOwner->statusChanged( aEvent );
+}
+
+void StyleDispatcher::disposing( const css::lang::EventObject& /*rSource*/ )
+{
+ m_xStatusDispatch.clear();
+}
+
+StyleToolbarController::StyleToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ const css::uno::Reference< css::frame::XFrame >& rFrame,
+ const OUString& rCommand )
+ : ToolboxController( rContext, rFrame, rCommand )
+{
+}
+
+void StyleToolbarController::update()
+{
+ if ( m_bDisposed )
+ throw css::lang::DisposedException();
+
+ css::util::URL aURL;
+ aURL.Complete = m_aCommandURL;
+ m_xUrlTransformer->parseStrict( aURL );
+
+ auto& xDispatcher = m_aListenerMap[m_aCommandURL];
+ if ( xDispatcher.is() )
+ xDispatcher->removeStatusListener( this, aURL );
+
+ xDispatcher.set( new StyleDispatcher( m_xFrame, m_xUrlTransformer, aURL ) );
+ xDispatcher->addStatusListener( this, aURL );
+}
+
+void StyleToolbarController::statusChanged( const css::frame::FeatureStateEvent& rEvent )
+{
+ SolarMutexGuard aGuard;
+
+ if ( m_bDisposed )
+ throw css::lang::DisposedException();
+
+ ToolBox* pToolBox = nullptr;
+ sal_uInt16 nItemId = 0;
+ if ( getToolboxId( nItemId, &pToolBox ) )
+ {
+ bool bChecked = false;
+ rEvent.State >>= bChecked;
+ pToolBox->CheckItem( nItemId, bChecked );
+ pToolBox->EnableItem( nItemId, rEvent.IsEnabled );
+ }
+}
+
+void StyleToolbarController::dispose()
+{
+ ToolboxController::dispose();
+ m_aListenerMap.clear(); // Break the cycle with StyleDispatcher.
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/framework/source/uielement/toolbarmanager.cxx b/framework/source/uielement/toolbarmanager.cxx
index 2ab500e34b04..ae83a001224b 100644
--- a/framework/source/uielement/toolbarmanager.cxx
+++ b/framework/source/uielement/toolbarmanager.cxx
@@ -23,6 +23,7 @@
#include <uielement/toolbarmanager.hxx>
#include <uielement/generictoolbarcontroller.hxx>
+#include <uielement/styletoolbarcontroller.hxx>
#include "services.h"
#include "general.h"
#include "properties.h"
@@ -754,6 +755,11 @@ void ToolBarManager::CreateControllers()
xController = xStatusListener;
}
+ else if ( aCommandURL.startsWith( ".uno:StyleApply?" ) )
+ {
+ xController.set( new StyleToolbarController( m_xContext, m_xFrame, aCommandURL ));
+ m_pToolBar->SetItemBits( nId, m_pToolBar->GetItemBits( nId ) | ToolBoxItemBits::CHECKABLE );
+ }
else if ( aCommandURL.startsWith( "private:resource/menubar/" ) )
{
xController.set( new MenuToolbarController );