diff options
Diffstat (limited to 'forms/source/richtext')
28 files changed, 5420 insertions, 0 deletions
diff --git a/forms/source/richtext/attributedispatcher.cxx b/forms/source/richtext/attributedispatcher.cxx new file mode 100644 index 0000000000..29dbbf6162 --- /dev/null +++ b/forms/source/richtext/attributedispatcher.cxx @@ -0,0 +1,115 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "attributedispatcher.hxx" + +#include <editeng/editview.hxx> +#include <sal/log.hxx> +#include <osl/diagnose.h> + + +namespace frm +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::frame; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::util; + using namespace ::com::sun::star::beans; + + OAttributeDispatcher::OAttributeDispatcher( EditView& _rView, AttributeId _nAttributeId, const URL& _rURL, + IMultiAttributeDispatcher* _pMasterDispatcher ) + :ORichTextFeatureDispatcher( _rView, _rURL ) + ,m_pMasterDispatcher( _pMasterDispatcher ) + ,m_nAttributeId( _nAttributeId ) + { + OSL_ENSURE( m_pMasterDispatcher, "OAttributeDispatcher::OAttributeDispatcher: invalid master dispatcher!" ); + } + + + OAttributeDispatcher::~OAttributeDispatcher( ) + { + acquire(); + dispose(); + } + + + void OAttributeDispatcher::disposing( ::osl::ClearableMutexGuard& _rClearBeforeNotify ) + { + m_pMasterDispatcher = nullptr; + ORichTextFeatureDispatcher::disposing( _rClearBeforeNotify ); + } + + + void OAttributeDispatcher::fillFeatureEventFromAttributeState( FeatureStateEvent& _rEvent, const AttributeState& _rState ) const + { + if ( _rState.eSimpleState == eChecked ) + _rEvent.State <<= true; + else if ( _rState.eSimpleState == eUnchecked ) + _rEvent.State <<= false; + } + + + FeatureStateEvent OAttributeDispatcher::buildStatusEvent() const + { + FeatureStateEvent aEvent( ORichTextFeatureDispatcher::buildStatusEvent() ); + aEvent.IsEnabled = getEditView() && !getEditView()->IsReadOnly(); + + AttributeState aState; + if ( m_pMasterDispatcher ) + aState = m_pMasterDispatcher->getState( m_nAttributeId ); + + fillFeatureEventFromAttributeState( aEvent, aState ); + + return aEvent; + } + + + void SAL_CALL OAttributeDispatcher::dispatch( const URL& _rURL, const Sequence< PropertyValue >& _rArguments ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + checkDisposed(); + + OSL_ENSURE( _rURL.Complete == getFeatureURL().Complete, "OAttributeDispatcher::dispatch: invalid URL!" ); + SAL_WARN_IF( _rArguments.hasElements(), "forms.richtext", + "OAttributeDispatcher::dispatch: found arguments, but can't handle arguments at all" + " (URL: " << _rURL.Complete << ")"); + + if ( m_pMasterDispatcher ) + m_pMasterDispatcher->executeAttribute( m_nAttributeId, nullptr ); + } + + + void OAttributeDispatcher::onAttributeStateChanged( AttributeId _nAttributeId ) + { + OSL_ENSURE( _nAttributeId == m_nAttributeId, "OAttributeDispatcher::onAttributeStateChanged: wrong attribute!" ); + + FeatureStateEvent aEvent( buildStatusEvent() ); + ::comphelper::OInterfaceIteratorHelper3 aIter( getStatusListeners() ); + while ( aIter.hasMoreElements() ) + doNotify( aIter.next(), aEvent ); + } + + +} // namespace frm + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/richtext/attributedispatcher.hxx b/forms/source/richtext/attributedispatcher.hxx new file mode 100644 index 0000000000..9c6769b601 --- /dev/null +++ b/forms/source/richtext/attributedispatcher.hxx @@ -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 . + */ +#pragma once + +#include "featuredispatcher.hxx" +#include "rtattributes.hxx" +#include "textattributelistener.hxx" + + +namespace frm +{ + + class OAttributeDispatcher :public ORichTextFeatureDispatcher + ,public ITextAttributeListener + { + protected: + IMultiAttributeDispatcher* m_pMasterDispatcher; + AttributeId m_nAttributeId; + + public: + /** ctor + @param _nAttributeId + the id of the attribute which this instance is responsible for + @param _rURL + the URL of the feature which this instance is responsible for + @param _pMasterDispatcher + the dispatcher which can execute the given attribute + @param _pConverter + an instance which is able to convert between SfxPoolItems and XDispatch-Parameters + If not <NULL/>, the parametrized version of IMultiAttributeDispatcher::executeAttribute + will be used. + */ + OAttributeDispatcher( + EditView& _rView, + AttributeId _nAttributeId, + const css::util::URL& _rURL, + IMultiAttributeDispatcher* _pMasterDispatcher + ); + + protected: + virtual ~OAttributeDispatcher( ) override; + + // XDispatch + virtual void SAL_CALL dispatch( const css::util::URL& URL, const css::uno::Sequence< css::beans::PropertyValue >& Arguments ) override; + + // ITextAttributeListener + virtual void onAttributeStateChanged( AttributeId _nAttributeId ) override; + + // ORichTextFeatureDispatcher + virtual void disposing( ::osl::ClearableMutexGuard& _rClearBeforeNotify ) override; + + // ORichTextFeatureDispatcher + virtual css::frame::FeatureStateEvent buildStatusEvent() const override; + + // own overridables + virtual void fillFeatureEventFromAttributeState( css::frame::FeatureStateEvent& _rEvent, const AttributeState& _rState ) const; + }; + + +} // namespace frm + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/richtext/clipboarddispatcher.cxx b/forms/source/richtext/clipboarddispatcher.cxx new file mode 100644 index 0000000000..70313edb33 --- /dev/null +++ b/forms/source/richtext/clipboarddispatcher.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 "clipboarddispatcher.hxx" +#include <editeng/editview.hxx> + +#include <com/sun/star/lang/DisposedException.hpp> +#include <svtools/cliplistener.hxx> +#include <vcl/transfer.hxx> +#include <osl/diagnose.h> + + +namespace frm +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::frame; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::util; + using namespace ::com::sun::star::beans; + + + namespace + { + URL createClipboardURL( OClipboardDispatcher::ClipboardFunc _eFunc ) + { + URL aURL; + switch ( _eFunc ) + { + case OClipboardDispatcher::eCut: + aURL.Complete = ".uno:Cut"; + break; + case OClipboardDispatcher::eCopy: + aURL.Complete = ".uno:Copy"; + break; + case OClipboardDispatcher::ePaste: + aURL.Complete = ".uno:Paste"; + break; + } + return aURL; + } + } + + OClipboardDispatcher::OClipboardDispatcher( EditView& _rView, ClipboardFunc _eFunc ) + :ORichTextFeatureDispatcher( _rView, createClipboardURL( _eFunc ) ) + ,m_eFunc( _eFunc ) + ,m_bLastKnownEnabled( true ) + { + } + + + bool OClipboardDispatcher::implIsEnabled( ) const + { + bool bEnabled = false; + switch ( m_eFunc ) + { + case eCut: + bEnabled = !getEditView()->IsReadOnly() && getEditView()->HasSelection(); + break; + + case eCopy: + bEnabled = getEditView()->HasSelection(); + break; + + case ePaste: + bEnabled = !getEditView()->IsReadOnly(); + break; + } + return bEnabled; + } + + + FeatureStateEvent OClipboardDispatcher::buildStatusEvent() const + { + FeatureStateEvent aEvent( ORichTextFeatureDispatcher::buildStatusEvent() ); + aEvent.IsEnabled = implIsEnabled(); + return aEvent; + } + + + void OClipboardDispatcher::invalidateFeatureState_Broadcast() + { + bool bEnabled = implIsEnabled(); + if ( m_bLastKnownEnabled == bEnabled ) + // nothing changed -> no notification + return; + m_bLastKnownEnabled = bEnabled; + + ORichTextFeatureDispatcher::invalidateFeatureState_Broadcast(); + } + + + void SAL_CALL OClipboardDispatcher::dispatch( const URL& /*_rURL*/, const Sequence< PropertyValue >& /*Arguments*/ ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !getEditView() ) + throw DisposedException(); + + switch ( m_eFunc ) + { + case eCut: + getEditView()->Cut(); + break; + + case eCopy: + getEditView()->Copy(); + break; + + case ePaste: + getEditView()->Paste(); + break; + } + } + + OPasteClipboardDispatcher::OPasteClipboardDispatcher( EditView& _rView ) + :OClipboardDispatcher( _rView, ePaste ) + ,m_bPastePossible( false ) + { + m_pClipListener = new TransferableClipboardListener( LINK( this, OPasteClipboardDispatcher, OnClipboardChanged ) ); + m_pClipListener->AddListener( _rView.GetWindow() ); + + // initial state + TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( _rView.GetWindow() ) ); + m_bPastePossible = ( aDataHelper.HasFormat( SotClipboardFormatId::STRING ) || + aDataHelper.HasFormat( SotClipboardFormatId::RTF ) || aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) ); + } + + + OPasteClipboardDispatcher::~OPasteClipboardDispatcher() + { + if ( !isDisposed() ) + { + acquire(); + dispose(); + } + } + + + IMPL_LINK( OPasteClipboardDispatcher, OnClipboardChanged, TransferableDataHelper*, _pDataHelper, void ) + { + OSL_ENSURE( _pDataHelper, "OPasteClipboardDispatcher::OnClipboardChanged: ooops!" ); + m_bPastePossible = _pDataHelper->HasFormat( SotClipboardFormatId::STRING ) + || _pDataHelper->HasFormat( SotClipboardFormatId::RTF ) + || _pDataHelper->HasFormat( SotClipboardFormatId::RICHTEXT ); + + invalidate(); + } + + + void OPasteClipboardDispatcher::disposing( ::osl::ClearableMutexGuard& _rClearBeforeNotify ) + { + OSL_ENSURE( getEditView() && getEditView()->GetWindow(), "OPasteClipboardDispatcher::disposing: EditView should not (yet) be disfunctional here!" ); + if (m_pClipListener.is()) + { + if (getEditView() && getEditView()->GetWindow()) + m_pClipListener->RemoveListener( getEditView()->GetWindow() ); + + m_pClipListener.clear(); + } + + OClipboardDispatcher::disposing( _rClearBeforeNotify ); + } + + + bool OPasteClipboardDispatcher::implIsEnabled( ) const + { + return m_bPastePossible && OClipboardDispatcher::implIsEnabled(); + } + + +} // namespace frm + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/richtext/clipboarddispatcher.hxx b/forms/source/richtext/clipboarddispatcher.hxx new file mode 100644 index 0000000000..47e561c676 --- /dev/null +++ b/forms/source/richtext/clipboarddispatcher.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 "featuredispatcher.hxx" +#include <tools/link.hxx> +#include <rtl/ref.hxx> + +class TransferableClipboardListener; +class TransferableDataHelper; + +namespace frm +{ + + class OClipboardDispatcher : public ORichTextFeatureDispatcher + { + public: + enum ClipboardFunc + { + eCut, + eCopy, + ePaste + }; + + private: + ClipboardFunc m_eFunc; + bool m_bLastKnownEnabled; + + public: + OClipboardDispatcher( EditView& _rView, ClipboardFunc _eFunc ); + + protected: + // XDispatch + virtual void SAL_CALL dispatch( const css::util::URL& URL, const css::uno::Sequence< css::beans::PropertyValue >& Arguments ) override; + + // ORichTextFeatureDispatcher + virtual void invalidateFeatureState_Broadcast() override; + virtual css::frame::FeatureStateEvent + buildStatusEvent() const override; + + protected: + /** determines whether our functionality is currently available + to be overridden for ePaste + */ + virtual bool implIsEnabled( ) const; + }; + + class OPasteClipboardDispatcher : public OClipboardDispatcher + { + private: + rtl::Reference<TransferableClipboardListener> m_pClipListener; + bool m_bPastePossible; + + public: + explicit OPasteClipboardDispatcher( EditView& _rView ); + + protected: + virtual ~OPasteClipboardDispatcher() override; + + // OClipboardDispatcher + virtual bool implIsEnabled( ) const override; + + // ORichTextFeatureDispatcher + virtual void disposing( ::osl::ClearableMutexGuard& _rClearBeforeNotify ) override; + + private: + DECL_LINK( OnClipboardChanged, TransferableDataHelper*, void ); + }; + + +} // namespace frm + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/richtext/featuredispatcher.cxx b/forms/source/richtext/featuredispatcher.cxx new file mode 100644 index 0000000000..f583a8e7c6 --- /dev/null +++ b/forms/source/richtext/featuredispatcher.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 "featuredispatcher.hxx" +#include <osl/diagnose.h> +#include <comphelper/diagnose_ex.hxx> + +#include <utility> + + +namespace frm +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::frame; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::util; + + ORichTextFeatureDispatcher::ORichTextFeatureDispatcher( EditView& _rView, URL _aURL ) + :m_aFeatureURL(std::move( _aURL )) + ,m_aStatusListeners( m_aMutex ) + ,m_pEditView( &_rView ) + ,m_bDisposed( false ) + { + } + + + ORichTextFeatureDispatcher::~ORichTextFeatureDispatcher( ) + { + if ( !m_bDisposed ) + { + acquire(); + dispose(); + } + } + + + void ORichTextFeatureDispatcher::dispose() + { + EventObject aEvent( *this ); + m_aStatusListeners.disposeAndClear( aEvent ); + + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + m_bDisposed = true; + disposing( aGuard ); + } + + + void ORichTextFeatureDispatcher::disposing( ::osl::ClearableMutexGuard& /*_rClearBeforeNotify*/ ) + { + m_pEditView = nullptr; + } + + + void SAL_CALL ORichTextFeatureDispatcher::addStatusListener( const Reference< XStatusListener >& _rxControl, const URL& _rURL ) + { + OSL_ENSURE( !m_bDisposed, "ORichTextFeatureDispatcher::addStatusListener: already disposed!" ); + if ( m_bDisposed ) + throw DisposedException(); + + OSL_ENSURE( _rURL.Complete == getFeatureURL().Complete, "ORichTextFeatureDispatcher::addStatusListener: invalid URL!" ); + if ( _rURL.Complete == getFeatureURL().Complete ) + if ( _rxControl.is() ) + { + m_aStatusListeners.addInterface( _rxControl ); + doNotify( _rxControl, buildStatusEvent() ); + } + } + + + void SAL_CALL ORichTextFeatureDispatcher::removeStatusListener( const Reference< XStatusListener >& _rxControl, const URL& /*_rURL*/ ) + { + m_aStatusListeners.removeInterface( _rxControl ); + } + + + void ORichTextFeatureDispatcher::invalidate() + { + invalidateFeatureState_Broadcast(); + } + + + FeatureStateEvent ORichTextFeatureDispatcher::buildStatusEvent() const + { + FeatureStateEvent aEvent; + aEvent.IsEnabled = false; + aEvent.Source = *const_cast< ORichTextFeatureDispatcher* >( this ); + aEvent.FeatureURL = getFeatureURL(); + aEvent.Requery = false; + return aEvent; + } + + + void ORichTextFeatureDispatcher::invalidateFeatureState_Broadcast() + { + FeatureStateEvent aEvent( buildStatusEvent() ); + ::comphelper::OInterfaceIteratorHelper3 aIter( getStatusListeners() ); + while ( aIter.hasMoreElements() ) + doNotify( aIter.next(), aEvent ); + } + + + void ORichTextFeatureDispatcher::doNotify( const Reference< XStatusListener >& _rxListener, const FeatureStateEvent& _rEvent ) + { + OSL_PRECOND( _rxListener.is(), "ORichTextFeatureDispatcher::doNotify: invalid listener!" ); + if ( _rxListener.is() ) + { + try + { + _rxListener->statusChanged( _rEvent ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "forms.richtext", "ORichTextFeatureDispatcher::doNotify" ); + } + } + } + + +} // namespace frm + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/richtext/featuredispatcher.hxx b/forms/source/richtext/featuredispatcher.hxx new file mode 100644 index 0000000000..5c863f10f8 --- /dev/null +++ b/forms/source/richtext/featuredispatcher.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 <com/sun/star/frame/XDispatch.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <comphelper/interfacecontainer3.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/basemutex.hxx> + +class EditView; + +namespace frm +{ + + typedef ::cppu::WeakImplHelper < css::frame::XDispatch + > ORichTextFeatureDispatcher_Base; + + class ORichTextFeatureDispatcher :public ::cppu::BaseMutex + ,public ORichTextFeatureDispatcher_Base + { + private: + css::util::URL m_aFeatureURL; + ::comphelper::OInterfaceContainerHelper3<css::frame::XStatusListener> m_aStatusListeners; + EditView* m_pEditView; + bool m_bDisposed; + + protected: + EditView* getEditView() { return m_pEditView; } + const EditView* getEditView() const { return m_pEditView; } + + protected: + const css::util::URL& getFeatureURL() const { return m_aFeatureURL; } + ::comphelper::OInterfaceContainerHelper3<css::frame::XStatusListener>& getStatusListeners() { return m_aStatusListeners; } + bool isDisposed() const { return m_bDisposed; } + void checkDisposed() const { if ( isDisposed() ) throw css::lang::DisposedException(); } + + protected: + ORichTextFeatureDispatcher( EditView& _rView, css::util::URL _aURL ); + virtual ~ORichTextFeatureDispatcher( ) override; + + public: + /// clean up resources associated with this instance + void dispose(); + + // invalidate the feature, re-retrieve it's state, and broadcast changes, if necessary + void invalidate(); + + protected: + // overridables + virtual void disposing( ::osl::ClearableMutexGuard& _rClearBeforeNotify ); + virtual void invalidateFeatureState_Broadcast(); + + // to be overridden, and filled with the info special do your derived class + virtual css::frame::FeatureStateEvent + buildStatusEvent() const; + + static void doNotify( + const css::uno::Reference< css::frame::XStatusListener >& _rxListener, + const css::frame::FeatureStateEvent& _rEvent + ); + + // XDispatch + virtual void SAL_CALL addStatusListener( const css::uno::Reference< css::frame::XStatusListener >& _rxControl, const css::util::URL& _rURL ) override; + virtual void SAL_CALL removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& _rxControl, const css::util::URL& _rURL ) override; + }; + + +} // namespace frm + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/richtext/parametrizedattributedispatcher.cxx b/forms/source/richtext/parametrizedattributedispatcher.cxx new file mode 100644 index 0000000000..d6666e2b02 --- /dev/null +++ b/forms/source/richtext/parametrizedattributedispatcher.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 "parametrizedattributedispatcher.hxx" +#include <editeng/editids.hrc> +#include <editeng/editview.hxx> +#include <svl/itemset.hxx> +#include <svl/itempool.hxx> +#include <osl/diagnose.h> + +#include <sfx2/sfxuno.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/beans/PropertyValue.hpp> + + +namespace frm +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::frame; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::util; + using namespace ::com::sun::star::beans; + + OParametrizedAttributeDispatcher::OParametrizedAttributeDispatcher( EditView& _rView, AttributeId _nAttributeId, const URL& _rURL, IMultiAttributeDispatcher* _pMasterDispatcher ) + :OAttributeDispatcher( _rView, _nAttributeId, _rURL, _pMasterDispatcher ) + { + } + + + OParametrizedAttributeDispatcher::~OParametrizedAttributeDispatcher() + { + acquire(); + dispose(); + } + + + namespace + { + SfxSlotId lcl_normalizeLatinScriptSlotId( SfxSlotId _nSlotId ) + { + switch ( _nSlotId ) + { + case SID_ATTR_CHAR_LATIN_FONT: return SID_ATTR_CHAR_FONT; + case SID_ATTR_CHAR_LATIN_LANGUAGE: return SID_ATTR_CHAR_LANGUAGE; + case SID_ATTR_CHAR_LATIN_POSTURE: return SID_ATTR_CHAR_POSTURE; + case SID_ATTR_CHAR_LATIN_WEIGHT: return SID_ATTR_CHAR_WEIGHT; + case SID_ATTR_CHAR_LATIN_FONTHEIGHT:return SID_ATTR_CHAR_FONTHEIGHT; + } + return _nSlotId; + } + } + + + void OParametrizedAttributeDispatcher::fillFeatureEventFromAttributeState( FeatureStateEvent& _rEvent, const AttributeState& _rState ) const + { + OSL_ENSURE( getEditView(), "OParametrizedAttributeDispatcher::notifyState: already disposed!" ); + if ( !getEditView() ) + return; + + SfxItemSet aEmptySet(getEditView()->GetEmptyItemSet()); + Sequence< PropertyValue > aUnoStateDescription; + if ( _rState.getItem() ) + { + aEmptySet.Put( *_rState.getItem() ); + SfxSlotId nSlotId = aEmptySet.GetPool()->GetSlotId( _rState.getItem()->Which() ); + TransformItems( nSlotId, aEmptySet, aUnoStateDescription ); + _rEvent.State <<= aUnoStateDescription; + } + else + OAttributeDispatcher::fillFeatureEventFromAttributeState( _rEvent, _rState ); + } + + + const SfxPoolItem* OParametrizedAttributeDispatcher::convertDispatchArgsToItem( const Sequence< PropertyValue >& _rArguments ) + { + // get the real slot id. This may differ from our attribute id: for instance, both + // SID_ATTR_CHAR_HEIGHT and SID_ATTR_CHAR_LATIN_HEIGHT are mapped to the same which id + SfxSlotId nSlotId = lcl_normalizeLatinScriptSlotId( static_cast<SfxSlotId>(m_nAttributeId) ); + + SfxAllItemSet aParameterSet( getEditView()->GetEmptyItemSet() ); + TransformParameters( nSlotId, _rArguments, aParameterSet ); + + const SfxPoolItem* pArgument = nullptr; + if ( aParameterSet.Count() ) + { + OSL_ENSURE( aParameterSet.Count() == 1, "OParametrizedAttributeDispatcher::convertDispatchArgsToItem: Arguments which form more than 1 item? How this?" ); + WhichId nAttributeWhich = aParameterSet.GetPool()->GetWhich( nSlotId ); + pArgument = aParameterSet.GetItem( nAttributeWhich ); + OSL_ENSURE( pArgument, "OParametrizedAttributeDispatcher::convertDispatchArgsToItem: suspicious: there were arguments, but they're not for my slot!" ); + } + + return pArgument; + } + + + void SAL_CALL OParametrizedAttributeDispatcher::dispatch( const URL& _rURL, const Sequence< PropertyValue >& _rArguments ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + OSL_ENSURE( _rURL.Complete == getFeatureURL().Complete, "OParametrizedAttributeDispatcher::dispatch: invalid URL!" ); + if ( m_pMasterDispatcher ) + { + const SfxPoolItem* pConvertedArgument = convertDispatchArgsToItem( _rArguments ); + m_pMasterDispatcher->executeAttribute( m_nAttributeId, pConvertedArgument ); + } + } + + +} // namespace frm + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/richtext/parametrizedattributedispatcher.hxx b/forms/source/richtext/parametrizedattributedispatcher.hxx new file mode 100644 index 0000000000..6984473161 --- /dev/null +++ b/forms/source/richtext/parametrizedattributedispatcher.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 "attributedispatcher.hxx" + +class SfxPoolItem; + +namespace frm +{ + + class OParametrizedAttributeDispatcher :public OAttributeDispatcher + { + public: + OParametrizedAttributeDispatcher( + EditView& _rView, + AttributeId _nAttributeId, + const css::util::URL& _rURL, + IMultiAttributeDispatcher* _pMasterDispatcher + ); + + protected: + virtual ~OParametrizedAttributeDispatcher() override; + + // XDispatch + virtual void SAL_CALL dispatch( const css::util::URL& URL, const css::uno::Sequence< css::beans::PropertyValue >& Arguments ) override; + + // OAttributeDispatcher + virtual void fillFeatureEventFromAttributeState( css::frame::FeatureStateEvent& _rEvent, const AttributeState& _rState ) const override; + + protected: + // own overridables + /** convert the arguments as got in a XDispatch::dispatch call into an SfxPoolItem, which can + be used with a IMultiAttributeDispatcher::executeAttribute + */ + virtual const SfxPoolItem* convertDispatchArgsToItem( + const css::uno::Sequence< css::beans::PropertyValue >& _rArguments ); + }; + + +} // namespace frm + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/richtext/richtextcontrol.cxx b/forms/source/richtext/richtextcontrol.cxx new file mode 100644 index 0000000000..b481b6bfc9 --- /dev/null +++ b/forms/source/richtext/richtextcontrol.cxx @@ -0,0 +1,652 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "richtextcontrol.hxx" +#include <frm_strings.hxx> +#include <services.hxx> + +#include "richtextmodel.hxx" +#include "richtextvclcontrol.hxx" +#include "clipboarddispatcher.hxx" +#include "parametrizedattributedispatcher.hxx" +#include "specialdispatchers.hxx" + +#include <com/sun/star/awt/PosSize.hpp> + +#include <toolkit/helper/vclunohelper.hxx> +#include <tools/debug.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <sal/log.hxx> +#include <vcl/svapp.hxx> + +#include <svx/svxids.hrc> +#include <editeng/editview.hxx> +#include <svl/itemset.hxx> +#include <svl/itempool.hxx> +#include <sfx2/msgpool.hxx> +#include <sfx2/msg.hxx> + +namespace frm +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::awt; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::frame; + + ORichTextControl::ORichTextControl() + { + } + + + ORichTextControl::~ORichTextControl() + { + } + + + IMPLEMENT_FORWARD_XTYPEPROVIDER2( ORichTextControl, UnoEditControl, ORichTextControl_Base ) + + + Any SAL_CALL ORichTextControl::queryAggregation( const Type& _rType ) + { + Any aReturn = UnoEditControl::queryAggregation( _rType ); + + if ( !aReturn.hasValue() ) + aReturn = ORichTextControl_Base::queryInterface( _rType ); + + return aReturn; + } + + + namespace + { + + void implAdjustTriStateFlag( const Reference< XPropertySet >& _rxProps, const OUString& _rPropertyName, + WinBits& _rAllBits, WinBits _nPositiveFlag, WinBits nNegativeFlag ) + { + bool bFlagValue = false; + if ( _rxProps->getPropertyValue( _rPropertyName ) >>= bFlagValue ) + _rAllBits |= ( bFlagValue ? _nPositiveFlag : nNegativeFlag ); + } + + + void implAdjustTwoStateFlag( const Any& _rValue, WinBits& _rAllBits, WinBits _nFlag, bool _bInvert ) + { + bool bFlagValue = false; + if ( _rValue >>= bFlagValue ) + { + if ( _bInvert ) + bFlagValue = !bFlagValue; + if ( bFlagValue ) + _rAllBits |= _nFlag; + else + _rAllBits &= ~_nFlag; + } + } + + + void implAdjustTwoStateFlag( const Reference< XPropertySet >& _rxProps, const OUString& _rPropertyName, + WinBits& _rAllBits, WinBits _nFlag, bool _bInvert = false ) + { + implAdjustTwoStateFlag( _rxProps->getPropertyValue( _rPropertyName ), _rAllBits, _nFlag, _bInvert ); + } + + + void adjustTwoStateWinBit( vcl::Window* _pWindow, const Any& _rValue, WinBits _nFlag, bool _bInvert = false ) + { + WinBits nBits = _pWindow->GetStyle(); + implAdjustTwoStateFlag( _rValue, nBits, _nFlag, _bInvert ); + _pWindow->SetStyle( nBits ); + } + + + WinBits getWinBits( const Reference< XControlModel >& _rxModel ) + { + WinBits nBits = 0; + try + { + Reference< XPropertySet > xProps( _rxModel, UNO_QUERY ); + if ( xProps.is() ) + { + sal_Int16 nBorder = 0; + xProps->getPropertyValue( PROPERTY_BORDER ) >>= nBorder; + if ( nBorder ) + nBits |= WB_BORDER; + + implAdjustTriStateFlag( xProps, PROPERTY_TABSTOP, nBits, WB_TABSTOP, WB_NOTABSTOP ); + implAdjustTwoStateFlag( xProps, PROPERTY_HSCROLL, nBits, WB_HSCROLL ); + implAdjustTwoStateFlag( xProps, PROPERTY_VSCROLL, nBits, WB_VSCROLL ); + implAdjustTwoStateFlag( xProps, PROPERTY_HARDLINEBREAKS, nBits, WB_WORDBREAK, true ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("forms.richtext"); + } + return nBits; + } + } + + + void SAL_CALL ORichTextControl::createPeer( const Reference< XToolkit >& _rToolkit, const Reference< XWindowPeer >& _rParentPeer ) + { + bool bReallyActAsRichText = false; + try + { + Reference< XPropertySet > xModelProps( getModel(), UNO_QUERY_THROW ); + xModelProps->getPropertyValue( PROPERTY_RICH_TEXT ) >>= bReallyActAsRichText; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("forms.richtext"); + } + + if ( !bReallyActAsRichText ) + { + UnoEditControl::createPeer( _rToolkit, _rParentPeer ); + return; + } + + SolarMutexGuard aGuard; + + if (getPeer().is()) + return; + + mbCreatingPeer = true; + + // determine the VCL window for the parent + vcl::Window* pParentWin = nullptr; + if ( _rParentPeer.is() ) + { + VCLXWindow* pParentXWin = dynamic_cast<VCLXWindow*>( _rParentPeer.get() ); + if ( pParentXWin ) + pParentWin = pParentXWin->GetWindow(); + DBG_ASSERT( pParentWin, "ORichTextControl::createPeer: could not obtain the VCL-level parent window!" ); + } + + // create the peer + Reference< XControlModel > xModel( getModel() ); + rtl::Reference<ORichTextPeer> pPeer = ORichTextPeer::Create( xModel, pParentWin, getWinBits( xModel ) ); + DBG_ASSERT( pPeer, "ORichTextControl::createPeer: invalid peer returned!" ); + if ( pPeer ) + { + // announce the peer to the base class + setPeer( pPeer ); + + // initialize ourself (and thus the peer) with the model properties + updateFromModel(); + + Reference< XView > xPeerView( getPeer(), UNO_QUERY ); + if ( xPeerView.is() ) + { + xPeerView->setZoom( maComponentInfos.nZoomX, maComponentInfos.nZoomY ); + xPeerView->setGraphics( mxGraphics ); + } + + // a lot of initial settings from our component infos + setPosSize( maComponentInfos.nX, maComponentInfos.nY, maComponentInfos.nWidth, maComponentInfos.nHeight, PosSize::POSSIZE ); + + pPeer->setVisible ( maComponentInfos.bVisible && !mbDesignMode ); + pPeer->setEnable ( maComponentInfos.bEnable ); + pPeer->setDesignMode( mbDesignMode ); + + peerCreated(); + } + + mbCreatingPeer = false; + } + + OUString SAL_CALL ORichTextControl::getImplementationName() + { + return "com.sun.star.comp.form.ORichTextControl"; + } + + Sequence< OUString > SAL_CALL ORichTextControl::getSupportedServiceNames() + { + return { "com.sun.star.awt.UnoControl", + "com.sun.star.awt.UnoControlEdit", + FRM_SUN_CONTROL_RICHTEXTCONTROL }; + } + + Reference< XDispatch > SAL_CALL ORichTextControl::queryDispatch( const css::util::URL& _rURL, const OUString& _rTargetFrameName, sal_Int32 _nSearchFlags ) + { + Reference< XDispatch > aReturn; + Reference< XDispatchProvider > xTypedPeer( getPeer(), UNO_QUERY ); + if ( xTypedPeer.is() ) + { + aReturn = xTypedPeer->queryDispatch( _rURL, _rTargetFrameName, _nSearchFlags ); + } + return aReturn; + } + + Sequence< Reference< XDispatch > > SAL_CALL ORichTextControl::queryDispatches( const Sequence< DispatchDescriptor >& _rRequests ) + { + Reference<XDispatchProvider> xTypedPeer(getPeer(), UNO_QUERY); + if (xTypedPeer.is()) + return xTypedPeer->queryDispatches(_rRequests); + return Sequence<Reference<XDispatch>>(); + } + + bool ORichTextControl::requiresNewPeer( const OUString& _rPropertyName ) const + { + return UnoControl::requiresNewPeer( _rPropertyName ) || _rPropertyName == PROPERTY_RICH_TEXT; + } + + // ORichTextPeer + rtl::Reference<ORichTextPeer> ORichTextPeer::Create( const Reference< XControlModel >& _rxModel, vcl::Window* _pParentWindow, WinBits _nStyle ) + { + DBG_TESTSOLARMUTEX(); + + // the EditEngine of the model + RichTextEngine* pEngine = ORichTextModel::getEditEngine( _rxModel ); + OSL_ENSURE( pEngine, "ORichTextPeer::Create: could not obtain the edit engine from the model!" ); + if ( !pEngine ) + return nullptr; + + // the peer itself + rtl::Reference<ORichTextPeer> pPeer(new ORichTextPeer); + + // the VCL control for the peer + VclPtrInstance<RichTextControl> pRichTextControl( pEngine, _pParentWindow, _nStyle, nullptr, pPeer.get() ); + + // some knittings + pRichTextControl->SetComponentInterface( pPeer ); + + // outta here + return pPeer; + } + + + ORichTextPeer::ORichTextPeer() + { + } + + + ORichTextPeer::~ORichTextPeer() + { + } + + + void ORichTextPeer::dispose( ) + { + { + SolarMutexGuard aGuard; + VclPtr< RichTextControl > pRichTextControl = GetAs< RichTextControl >(); + + if ( pRichTextControl ) + { + for (auto const& dispatcher : m_aDispatchers) + { + pRichTextControl->disableAttributeNotification(dispatcher.first); + dispatcher.second->dispose(); + } + } + + AttributeDispatchers().swap(m_aDispatchers); + } + + VCLXWindow::dispose(); + } + + + void SAL_CALL ORichTextPeer::draw( sal_Int32 _nX, sal_Int32 _nY ) + { + SolarMutexGuard aGuard; + + VclPtr< RichTextControl > pControl = GetAs< RichTextControl >(); + if ( !pControl ) + return; + + OutputDevice* pTargetDevice = VCLUnoHelper::GetOutputDevice( getGraphics() ); + OSL_ENSURE( pTargetDevice != nullptr, "ORichTextPeer::draw: no graphics -> no drawing!" ); + if ( !pTargetDevice ) + return; + + const MapUnit eTargetUnit = pTargetDevice->GetMapMode().GetMapUnit(); + ::Point aPos( _nX, _nY ); + // the XView::draw API talks about pixels, always ... + if ( eTargetUnit != MapUnit::MapPixel ) + aPos = pTargetDevice->PixelToLogic( aPos ); + + pControl->Draw( pTargetDevice, aPos, SystemTextColorFlags::NoControls ); + } + + + void SAL_CALL ORichTextPeer::setProperty( const OUString& _rPropertyName, const Any& _rValue ) + { + SolarMutexGuard g; + + if ( !GetWindow() ) + { + VCLXWindow::setProperty( _rPropertyName, _rValue ); + return; + } + + if ( _rPropertyName == PROPERTY_BACKGROUNDCOLOR ) + { + VclPtr< RichTextControl > pControl = GetAs< RichTextControl >(); + if ( !_rValue.hasValue() ) + { + pControl->SetBackgroundColor( ); + } + else + { + Color nColor = COL_TRANSPARENT; + _rValue >>= nColor; + pControl->SetBackgroundColor( nColor ); + } + } + else if ( _rPropertyName == PROPERTY_HSCROLL ) + { + adjustTwoStateWinBit( GetWindow(), _rValue, WB_HSCROLL ); + } + else if ( _rPropertyName == PROPERTY_VSCROLL ) + { + adjustTwoStateWinBit( GetWindow(), _rValue, WB_VSCROLL ); + } + else if ( _rPropertyName == PROPERTY_HARDLINEBREAKS ) + { + adjustTwoStateWinBit( GetWindow(), _rValue, WB_WORDBREAK, true ); + } + else if ( _rPropertyName == PROPERTY_READONLY ) + { + VclPtr< RichTextControl > pControl = GetAs< RichTextControl >(); + bool bReadOnly( pControl->IsReadOnly() ); + OSL_VERIFY( _rValue >>= bReadOnly ); + pControl->SetReadOnly( bReadOnly ); + + // update the dispatchers + for (auto const& dispatcher : m_aDispatchers) + { + dispatcher.second->invalidate(); + } + } + else if ( _rPropertyName == PROPERTY_HIDEINACTIVESELECTION ) + { + VclPtr< RichTextControl > pRichTextControl = GetAs< RichTextControl >(); + bool bHide = pRichTextControl->GetHideInactiveSelection(); + OSL_VERIFY( _rValue >>= bHide ); + pRichTextControl->SetHideInactiveSelection( bHide ); + } + else + VCLXWindow::setProperty( _rPropertyName, _rValue ); + } + + + IMPLEMENT_FORWARD_XINTERFACE2( ORichTextPeer, VCLXWindow, ORichTextPeer_Base ) + + + IMPLEMENT_FORWARD_XTYPEPROVIDER2( ORichTextPeer, VCLXWindow, ORichTextPeer_Base ) + + + namespace + { + SfxSlotId lcl_translateConflictingSlot( SfxSlotId _nIDFromPool ) + { + // HACK HACK HACK + // unfortunately, some of our applications have some conflicting slots, + // i.e. slots which have the same UNO name as an existing other (common) + // slot. + // For instance, both the slots SID_SET_SUPER_SCRIPT (from SVX) and FN_SET_SUPER_SCRIPT + // (from SW) have the UNO name "SuperScript". + // Now, if the controls lives in a text document, and asks the SfxSlotPool for + // the id belonging to "SuperScript", it gets the FN_SET_SUPER_SCRIPT - which + // is completely unknown to the EditEngine. + // So, we need to translate such conflicting ids. + + // Note that the real solution would be to fix the applications to + // *not* define conflicting slots. Alternatively, if SFX would provide a slot pool + // which is *static* (i.e. independent on the active application), then we + // would also never encounter such a conflict. + SfxSlotId nReturn( _nIDFromPool ); + switch ( _nIDFromPool ) + { + case 20411: /* FM_SET_SUPER_SCRIPT, originating in SW */ + nReturn = SID_SET_SUPER_SCRIPT; + break; + case 20412: /* FN_SET_SUB_SCRIPT, originating in SW */ + nReturn = SID_SET_SUB_SCRIPT; + break; + } + return nReturn; + } + } + + + ORichTextPeer::SingleAttributeDispatcher ORichTextPeer::implCreateDispatcher( SfxSlotId _nSlotId, const css::util::URL& _rURL ) + { + VclPtr< RichTextControl > pRichTextControl = GetAs< RichTextControl >(); + OSL_PRECOND( pRichTextControl, "ORichTextPeer::implCreateDispatcher: invalid window!" ); + if ( !pRichTextControl ) + return SingleAttributeDispatcher( nullptr ); + + rtl::Reference<ORichTextFeatureDispatcher> pDispatcher; + rtl::Reference<OAttributeDispatcher> pAttributeDispatcher; + switch ( _nSlotId ) + { + case SID_CUT: + pDispatcher = new OClipboardDispatcher( pRichTextControl->getView(), OClipboardDispatcher::eCut ); + break; + + case SID_COPY: + pDispatcher = new OClipboardDispatcher( pRichTextControl->getView(), OClipboardDispatcher::eCopy ); + break; + + case SID_PASTE: + pDispatcher = new OPasteClipboardDispatcher( pRichTextControl->getView() ); + break; + + case SID_SELECTALL: + pDispatcher = new OSelectAllDispatcher( pRichTextControl->getView(), _rURL ); + break; + + case SID_ATTR_PARA_LEFT_TO_RIGHT: + case SID_ATTR_PARA_RIGHT_TO_LEFT: + pAttributeDispatcher = new OParagraphDirectionDispatcher( pRichTextControl->getView(), _nSlotId, _rURL, pRichTextControl ); + break; + + case SID_TEXTDIRECTION_TOP_TO_BOTTOM: + case SID_TEXTDIRECTION_LEFT_TO_RIGHT: + pDispatcher = new OTextDirectionDispatcher( pRichTextControl->getView(), _rURL ); + break; + + case SID_ATTR_PARA_HANGPUNCTUATION: + case SID_ATTR_PARA_FORBIDDEN_RULES: + case SID_ATTR_PARA_SCRIPTSPACE: + pAttributeDispatcher = new OAsianFontLayoutDispatcher( pRichTextControl->getView(), _nSlotId, _rURL, pRichTextControl ); + break; + + default: + { + const SfxItemPool& rPool = *pRichTextControl->getView().GetEmptyItemSet().GetPool(); + bool bSupportedSlot = rPool.IsInRange( rPool.GetWhich( _nSlotId ) ); + + if ( !bSupportedSlot ) + bSupportedSlot = RichTextControl::isMappableSlot( _nSlotId ); + + if ( bSupportedSlot ) + { // it's really a slot which is supported by the EditEngine + + bool bNeedParametrizedDispatcher = true; + if ( ( _nSlotId == SID_ATTR_CHAR_POSTURE ) + || ( _nSlotId == SID_ATTR_CHAR_CJK_POSTURE ) + || ( _nSlotId == SID_ATTR_CHAR_CTL_POSTURE ) + || ( _nSlotId == SID_ATTR_CHAR_LATIN_POSTURE ) + || ( _nSlotId == SID_ATTR_CHAR_WEIGHT ) + || ( _nSlotId == SID_ATTR_CHAR_CJK_WEIGHT ) + || ( _nSlotId == SID_ATTR_CHAR_CTL_WEIGHT ) + || ( _nSlotId == SID_ATTR_CHAR_LATIN_WEIGHT ) + || ( _nSlotId == SID_ATTR_CHAR_LANGUAGE ) + || ( _nSlotId == SID_ATTR_CHAR_CJK_LANGUAGE ) + || ( _nSlotId == SID_ATTR_CHAR_CTL_LANGUAGE ) + || ( _nSlotId == SID_ATTR_CHAR_LATIN_LANGUAGE ) + || ( _nSlotId == SID_ATTR_CHAR_CONTOUR ) + || ( _nSlotId == SID_ATTR_CHAR_SHADOWED ) + || ( _nSlotId == SID_ATTR_CHAR_WORDLINEMODE ) + || ( _nSlotId == SID_ATTR_CHAR_COLOR ) + || ( _nSlotId == SID_ATTR_CHAR_RELIEF ) + || ( _nSlotId == SID_ATTR_CHAR_KERNING ) + || ( _nSlotId == SID_ATTR_CHAR_AUTOKERN ) + || ( _nSlotId == SID_ATTR_CHAR_SCALEWIDTH ) + ) + { + bNeedParametrizedDispatcher = true; + } + else + { + SfxSlotPool& rSlotPool = SfxSlotPool::GetSlotPool(); + const SfxSlot* pSlot = rSlotPool.GetSlot( _nSlotId ); + const SfxType* pType = pSlot ? pSlot->GetType() : nullptr; + if ( pType ) + { + bNeedParametrizedDispatcher = ( pType->nAttribs > 0 ); + } + } + + if ( bNeedParametrizedDispatcher ) + { + pAttributeDispatcher = new OParametrizedAttributeDispatcher( pRichTextControl->getView(), _nSlotId, _rURL, pRichTextControl ); + } + else + { + pAttributeDispatcher = new OAttributeDispatcher( pRichTextControl->getView(), _nSlotId, _rURL, pRichTextControl ); + } + } + else + { + SAL_WARN("forms.richtext", "ORichTextPeer::implCreateDispatcher: not creating dispatcher (unsupported slot) for " + << _rURL.Complete); + } + } + break; + } + + SingleAttributeDispatcher xDispatcher( pDispatcher ); + if ( pAttributeDispatcher ) + { + xDispatcher = SingleAttributeDispatcher( pAttributeDispatcher ); + pRichTextControl->enableAttributeNotification( _nSlotId, pAttributeDispatcher.get() ); + } + + return xDispatcher; + } + + + namespace + { + SfxSlotId lcl_getSlotFromUnoName( SfxSlotPool const & _rSlotPool, const OUString& _rUnoSlotName ) + { + const SfxSlot* pSlot = _rSlotPool.GetUnoSlot( _rUnoSlotName ); + if ( pSlot ) + { + // okay, there's a slot with the given UNO name + return lcl_translateConflictingSlot( pSlot->GetSlotId() ); + } + + // some hard-coded slots, which do not have a UNO name at SFX level, but which + // we nevertheless need to transport via UNO mechanisms, so we need a name + if ( _rUnoSlotName == "AllowHangingPunctuation" ) + return SID_ATTR_PARA_HANGPUNCTUATION; + if ( _rUnoSlotName == "ApplyForbiddenCharacterRules" ) + return SID_ATTR_PARA_FORBIDDEN_RULES; + if ( _rUnoSlotName == "UseScriptSpacing" ) + return SID_ATTR_PARA_SCRIPTSPACE; + + OSL_ENSURE( pSlot, "lcl_getSlotFromUnoName: unknown UNO slot name!" ); + return 0; + } + } + + + Reference< XDispatch > SAL_CALL ORichTextPeer::queryDispatch( const css::util::URL& _rURL, const OUString& /*_rTargetFrameName*/, sal_Int32 /*_nSearchFlags*/ ) + { + Reference< XDispatch > xReturn; + if ( !GetWindow() ) + { + OSL_FAIL( "ORichTextPeer::queryDispatch: already disposed?" ); + return xReturn; + } + + // is it a UNO slot? + static constexpr std::u16string_view sUnoProtocolPrefix( u".uno:" ); + if ( _rURL.Complete.startsWith( sUnoProtocolPrefix ) ) + { + OUString sUnoSlotName = _rURL.Complete.copy( sUnoProtocolPrefix.size() ); + SfxSlotId nSlotId = lcl_getSlotFromUnoName( SfxSlotPool::GetSlotPool(), sUnoSlotName ); + if ( nSlotId > 0 ) + { + // do we already have a dispatcher for this? + AttributeDispatchers::const_iterator aDispatcherPos = m_aDispatchers.find( nSlotId ); + if ( aDispatcherPos == m_aDispatchers.end() ) + { + SingleAttributeDispatcher pDispatcher = implCreateDispatcher( nSlotId, _rURL ); + if ( pDispatcher.is() ) + { + aDispatcherPos = m_aDispatchers.emplace( nSlotId, pDispatcher ).first; + } + } + + if ( aDispatcherPos != m_aDispatchers.end() ) + xReturn = aDispatcherPos->second.get(); + } + } + + return xReturn; + } + + + Sequence< Reference< XDispatch > > SAL_CALL ORichTextPeer::queryDispatches( const Sequence< DispatchDescriptor >& _rRequests ) + { + Sequence< Reference< XDispatch > > aReturn( _rRequests.getLength() ); + Reference< XDispatch >* pReturn = aReturn.getArray(); + + const DispatchDescriptor* pRequest = _rRequests.getConstArray(); + const DispatchDescriptor* pRequestEnd = pRequest + _rRequests.getLength(); + for ( ; pRequest != pRequestEnd; ++pRequest, ++pReturn ) + { + *pReturn = queryDispatch( pRequest->FeatureURL, pRequest->FrameName, pRequest->SearchFlags ); + } + return aReturn; + } + + + void ORichTextPeer::onSelectionChanged() + { + AttributeDispatchers::iterator aDispatcherPos = m_aDispatchers.find( SID_COPY ); + if ( aDispatcherPos != m_aDispatchers.end() ) + aDispatcherPos->second->invalidate(); + + aDispatcherPos = m_aDispatchers.find( SID_CUT ); + if ( aDispatcherPos != m_aDispatchers.end() ) + aDispatcherPos->second->invalidate(); + } + + +} // namespace frm + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_form_ORichTextControl_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new frm::ORichTextControl()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/richtext/richtextcontrol.hxx b/forms/source/richtext/richtextcontrol.hxx new file mode 100644 index 0000000000..81e0ad2a0f --- /dev/null +++ b/forms/source/richtext/richtextcontrol.hxx @@ -0,0 +1,128 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <toolkit/controls/unocontrols.hxx> +#include <toolkit/awt/vclxwindow.hxx> + +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <comphelper/uno3.hxx> +#include <cppuhelper/implbase1.hxx> +#include <rtl/ref.hxx> +#include <vcl/wintypes.hxx> +#include "rtattributes.hxx" +#include "textattributelistener.hxx" + +#include <map> + + +namespace frm +{ + + class ORichTextFeatureDispatcher; + + typedef ::cppu::ImplHelper1 < css::frame::XDispatchProvider + > ORichTextControl_Base; + class ORichTextControl :public UnoEditControl + ,public ORichTextControl_Base + { + public: + ORichTextControl(); + + protected: + virtual ~ORichTextControl() override; + + // UNO + DECLARE_UNO3_AGG_DEFAULTS( ORichTextControl, UnoEditControl ) + virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override; + + // XControl + virtual void SAL_CALL createPeer( const css::uno::Reference< css::awt::XToolkit >& _rToolkit, const css::uno::Reference< css::awt::XWindowPeer >& _rParent ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XTypeProvider + DECLARE_XTYPEPROVIDER() + + // XDispatchProvider + virtual css::uno::Reference< css::frame::XDispatch > SAL_CALL queryDispatch( const css::util::URL& _rURL, const OUString& _rTargetFrameName, sal_Int32 _rSearchFlags ) override; + virtual css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& Requests ) override; + + // UnoControl + virtual bool requiresNewPeer( const OUString& _rPropertyName ) const override; + }; + + typedef ::cppu::ImplHelper1 < css::frame::XDispatchProvider + > ORichTextPeer_Base; + class ORichTextPeer final :public VCLXWindow + ,public ORichTextPeer_Base + ,public ITextSelectionListener + { + private: + typedef rtl::Reference<ORichTextFeatureDispatcher> SingleAttributeDispatcher; + typedef ::std::map< SfxSlotId, SingleAttributeDispatcher > AttributeDispatchers; + AttributeDispatchers m_aDispatchers; + + public: + /** factory method + */ + static rtl::Reference<ORichTextPeer> Create( + const css::uno::Reference< css::awt::XControlModel >& _rxModel, + vcl::Window* _pParentWindow, + WinBits _nStyle + ); + + // XInterface + DECLARE_XINTERFACE( ) + + private: + ORichTextPeer(); + virtual ~ORichTextPeer() override; + + // XView + void SAL_CALL draw( sal_Int32 nX, sal_Int32 nY ) override; + + // XVclWindowPeer + virtual void SAL_CALL setProperty( const OUString& _rPropertyName, const css::uno::Any& _rValue ) override; + + // XTypeProvider + DECLARE_XTYPEPROVIDER( ) + + // XComponent + virtual void SAL_CALL dispose( ) override; + + // XDispatchProvider + virtual css::uno::Reference< css::frame::XDispatch > SAL_CALL queryDispatch( const css::util::URL& _rURL, const OUString& _rTargetFrameName, sal_Int32 _rSearchFlags ) override; + virtual css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& Requests ) override; + + // ITextSelectionListener + virtual void onSelectionChanged() override; + + private: + SingleAttributeDispatcher implCreateDispatcher( SfxSlotId _nSlotId, const css::util::URL& _rURL ); + }; + + +} // namespace frm + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/richtext/richtextengine.cxx b/forms/source/richtext/richtextengine.cxx new file mode 100644 index 0000000000..14f50a6fca --- /dev/null +++ b/forms/source/richtext/richtextengine.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 "richtextengine.hxx" +#include <svl/itempool.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/editobj.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/langitem.hxx> +#include <vcl/svapp.hxx> +#include <tools/mapunit.hxx> +#include <vcl/mapmod.hxx> +#include <vcl/outdev.hxx> +#include <vcl/settings.hxx> +#include <unotools/lingucfg.hxx> +#include <osl/diagnose.h> + +#include <algorithm> +#include <memory> + +namespace frm +{ + //= RichTextEngine + + + RichTextEngine* RichTextEngine::Create() + { + SolarMutexGuard g; + + rtl::Reference<SfxItemPool> pPool = EditEngine::CreatePool(); + pPool->FreezeIdRanges(); + + RichTextEngine* pReturn = new RichTextEngine( pPool.get() ); + OutputDevice* pOutputDevice = pReturn->GetRefDevice(); + const MapMode& aDeviceMapMode( pOutputDevice->GetMapMode() ); + + pReturn->SetStatusEventHdl( LINK( pReturn, RichTextEngine, EditEngineStatusChanged ) ); + + pPool->SetDefaultMetric(aDeviceMapMode.GetMapUnit()); + + // defaults + vcl::Font aFont = Application::GetSettings().GetStyleSettings().GetAppFont(); + aFont.SetFamilyName( "Times New Roman" ); + pPool->SetPoolDefaultItem( SvxFontItem( aFont.GetFamilyType(), aFont.GetFamilyName(), OUString(), aFont.GetPitch(), aFont.GetCharSet(), EE_CHAR_FONTINFO ) ); + + // 12 pt font size + MapMode aPointMapMode( MapUnit::MapPoint ); + Size a12PointSize( OutputDevice::LogicToLogic( Size( 12, 0 ), aPointMapMode, aDeviceMapMode ) ); + pPool->SetPoolDefaultItem( SvxFontHeightItem( a12PointSize.Width(), 100, EE_CHAR_FONTHEIGHT ) ); + + // font languages + SvtLinguOptions aLinguOpt; + pPool->SetPoolDefaultItem( SvxLanguageItem( aLinguOpt.nDefaultLanguage, EE_CHAR_LANGUAGE ) ); + pPool->SetPoolDefaultItem( SvxLanguageItem( aLinguOpt.nDefaultLanguage_CJK, EE_CHAR_LANGUAGE_CJK ) ); + pPool->SetPoolDefaultItem( SvxLanguageItem( aLinguOpt.nDefaultLanguage_CTL, EE_CHAR_LANGUAGE_CTL ) ); + + return pReturn; + } + + + RichTextEngine* RichTextEngine::Clone() + { + RichTextEngine* pClone( nullptr ); + { + SolarMutexGuard aGuard; + std::unique_ptr<EditTextObject> pMyText(CreateTextObject()); + OSL_ENSURE( pMyText, "RichTextEngine::Clone: CreateTextObject returned nonsense!" ); + + pClone = Create(); + + if ( pMyText ) + pClone->SetText( *pMyText ); + } + + return pClone; + } + + + RichTextEngine::RichTextEngine( SfxItemPool* _pPool ) + :EditEngine( _pPool ) + { + } + + + RichTextEngine::~RichTextEngine( ) + { + } + + + void RichTextEngine::registerEngineStatusListener( IEngineStatusListener* _pListener ) + { + OSL_ENSURE( _pListener, "RichTextEngine::registerEngineStatusListener: invalid listener!" ); + if ( _pListener ) + m_aStatusListeners.push_back( _pListener ); + } + + + void RichTextEngine::revokeEngineStatusListener( IEngineStatusListener const * _pListener ) + { + ::std::vector< IEngineStatusListener* >::iterator aPos = ::std::find( + m_aStatusListeners.begin(), + m_aStatusListeners.end(), + _pListener + ); + OSL_ENSURE( aPos != m_aStatusListeners.end(), "RichTextEngine::revokeEngineStatusListener: listener not registered!" ); + if ( aPos != m_aStatusListeners.end() ) + m_aStatusListeners.erase( aPos ); + } + + + IMPL_LINK( RichTextEngine, EditEngineStatusChanged, EditStatus&, _rStatus, void ) + { + for (auto const& statusListener : m_aStatusListeners) + statusListener->EditEngineStatusChanged( _rStatus ); + } + + +} // namespace frm + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/richtext/richtextengine.hxx b/forms/source/richtext/richtextengine.hxx new file mode 100644 index 0000000000..c9413e6bd0 --- /dev/null +++ b/forms/source/richtext/richtextengine.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 <editeng/editeng.hxx> +#include <tools/link.hxx> + +#include <vector> + +class SfxItemPool; +class EditStatus; + +namespace frm +{ + + class IEngineStatusListener + { + public: + virtual void EditEngineStatusChanged( const EditStatus& _rStatus ) = 0; + + protected: + ~IEngineStatusListener() {} + }; + + class RichTextEngine final : public EditEngine + { + private: + ::std::vector< IEngineStatusListener* > m_aStatusListeners; + + public: + static RichTextEngine* Create(); + RichTextEngine* Clone(); + + virtual ~RichTextEngine( ) override; + + // for multiplexing the StatusChanged events of the edit engine + void registerEngineStatusListener( IEngineStatusListener* _pListener ); + void revokeEngineStatusListener( IEngineStatusListener const * _pListener ); + + private: + /** constructs a new RichTextEngine. The instances takes the ownership of the given SfxItemPool + */ + explicit RichTextEngine( SfxItemPool* _pPool ); + + RichTextEngine( const RichTextEngine& ) = delete; + RichTextEngine& operator=( const RichTextEngine& ) = delete; + + DECL_LINK( EditEngineStatusChanged, EditStatus&, void ); + }; + + +} // namespace frm + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/richtext/richtextimplcontrol.cxx b/forms/source/richtext/richtextimplcontrol.cxx new file mode 100644 index 0000000000..790a3f88b2 --- /dev/null +++ b/forms/source/richtext/richtextimplcontrol.cxx @@ -0,0 +1,639 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "richtextimplcontrol.hxx" +#include "textattributelistener.hxx" +#include "richtextengine.hxx" +#include <sal/log.hxx> +#include <osl/diagnose.h> +#include <i18nlangtag/languagetag.hxx> +#include <editeng/editids.hrc> +#include <editeng/editview.hxx> +#include <editeng/editstat.hxx> +#include <editeng/scripttypeitem.hxx> + +#include <svl/itempool.hxx> +#include <svl/itemset.hxx> +#include <tools/mapunit.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <vcl/commandevent.hxx> + +#define EMPTY_PAPER_SIZE 0x7FFFFFFF + + +namespace frm +{ + + RichTextControlImpl::RichTextControlImpl( Control* _pAntiImpl, RichTextEngine* _pEngine, ITextAttributeListener* _pTextAttrListener, ITextSelectionListener* _pSelectionListener ) + :m_pAntiImpl ( _pAntiImpl ) + ,m_pViewport ( nullptr ) + ,m_pHScroll ( nullptr ) + ,m_pVScroll ( nullptr ) + ,m_pEngine ( _pEngine ) + ,m_pTextAttrListener ( _pTextAttrListener ) + ,m_pSelectionListener ( _pSelectionListener ) + ,m_bHasEverBeenShown ( false ) + { + OSL_ENSURE( m_pAntiImpl, "RichTextControlImpl::RichTextControlImpl: invalid window!" ); + OSL_ENSURE( m_pEngine, "RichTextControlImpl::RichTextControlImpl: invalid edit engine! This will *definitely* crash!" ); + + m_pViewport = VclPtr<RichTextViewPort>::Create( m_pAntiImpl ); + m_pViewport->setAttributeInvalidationHandler( LINK( this, RichTextControlImpl, OnInvalidateAllAttributes ) ); + m_pViewport->Show(); + + // ensure that both the window and the reference device have the same map unit + MapMode aRefDeviceMapMode( m_pEngine->GetRefDevice()->GetMapMode() ); + m_pAntiImpl->SetMapMode( aRefDeviceMapMode ); + m_pViewport->SetMapMode( aRefDeviceMapMode ); + + m_pView.reset(new EditView( m_pEngine, m_pViewport )); + m_pEngine->InsertView( m_pView.get() ); + m_pViewport->setView( *m_pView ); + + m_pEngine->registerEngineStatusListener( this ); + + { + EVControlBits nViewControlWord = m_pView->GetControlWord(); + nViewControlWord |= EVControlBits::AUTOSCROLL; + m_pView->SetControlWord( nViewControlWord ); + } + + // ensure that it's initially scrolled to the upper left + m_pView->SetVisArea( tools::Rectangle( Point( ), m_pViewport->GetOutDev()->GetOutputSize() ) ); + + ensureScrollbars(); + + m_pAntiImpl->SetBackground( Wallpaper( m_pAntiImpl->GetSettings().GetStyleSettings().GetFieldColor() ) ); + } + + RichTextControlImpl::~RichTextControlImpl( ) + { + m_pEngine->RemoveView( m_pView.get() ); + m_pEngine->revokeEngineStatusListener( this ); + m_pView.reset(); + m_pViewport.disposeAndClear(); + m_pHScroll.disposeAndClear(); + m_pVScroll.disposeAndClear(); + } + + void RichTextControlImpl::implUpdateAttribute( const AttributeHandlerPool::const_iterator& _pHandler ) + { + if ( ( _pHandler->first == sal_uInt16(SID_ATTR_CHAR_WEIGHT) ) + || ( _pHandler->first == sal_uInt16(SID_ATTR_CHAR_POSTURE) ) + || ( _pHandler->first == sal_uInt16(SID_ATTR_CHAR_FONT) ) + || ( _pHandler->first == sal_uInt16(SID_ATTR_CHAR_FONTHEIGHT) ) + ) + { + // these are attributes whose value depends on the current script type. + // I.e., in real, there are *three* items in the ItemSet: One for each script + // type (Latin, Asian, Complex). However, if we have an observer who is interested + // in the state of this attribute, we have to kind of *merge* the three attributes + // to only one. + // This is useful in case the observer is for instance a toolbox which contains only + // an, e.g., "bold" slot, and thus not interested in the particular script type of the + // current selection. + SvxScriptSetItem aNormalizedSet( static_cast<WhichId>(_pHandler->first), *m_pView->GetAttribs().GetPool() ); + normalizeScriptDependentAttribute( aNormalizedSet ); + + implCheckUpdateCache( _pHandler->first, _pHandler->second->getState( aNormalizedSet.GetItemSet() ) ); + } + else + implCheckUpdateCache( _pHandler->first, _pHandler->second->getState( m_pView->GetAttribs() ) ); + } + + + void RichTextControlImpl::updateAttribute( AttributeId _nAttribute ) + { + AttributeHandlerPool::const_iterator pHandler = m_aAttributeHandlers.find( _nAttribute ); + if ( pHandler != m_aAttributeHandlers.end() ) + implUpdateAttribute( pHandler ); + } + + + void RichTextControlImpl::updateAllAttributes( ) + { + for ( AttributeHandlerPool::const_iterator pHandler = m_aAttributeHandlers.begin(); + pHandler != m_aAttributeHandlers.end(); + ++pHandler + ) + { + implUpdateAttribute( pHandler ); + } + + // notify changes of the selection, if necessary + if ( m_pSelectionListener && m_pView ) + { + ESelection aCurrentSelection = m_pView->GetSelection(); + if ( aCurrentSelection != m_aLastKnownSelection ) + { + m_aLastKnownSelection = aCurrentSelection; + m_pSelectionListener->onSelectionChanged(); + } + } + } + + + AttributeState RichTextControlImpl::getAttributeState( AttributeId _nAttributeId ) const + { + StateCache::const_iterator aCachedStatePos = m_aLastKnownStates.find( _nAttributeId ); + if ( aCachedStatePos == m_aLastKnownStates.end() ) + { + OSL_FAIL( "RichTextControlImpl::getAttributeState: Don't ask for the state of an attribute which I never encountered!" ); + return AttributeState( eIndetermined ); + } + return aCachedStatePos->second; + } + + + bool RichTextControlImpl::executeAttribute( const SfxItemSet& _rCurrentAttribs, SfxItemSet& _rAttribs, AttributeId _nAttribute, const SfxPoolItem* _pArgument, SvtScriptType _nForScriptType ) + { + // let's see whether we have a handler for this attribute + AttributeHandlerPool::const_iterator aHandlerPos = m_aAttributeHandlers.find( _nAttribute ); + if ( aHandlerPos != m_aAttributeHandlers.end() ) + { + aHandlerPos->second->executeAttribute( _rCurrentAttribs, _rAttribs, _pArgument, _nForScriptType ); + return true; + } + return false; + } + + + void RichTextControlImpl::enableAttributeNotification( AttributeId _nAttributeId, ITextAttributeListener* _pListener ) + { + AttributeHandlerPool::const_iterator aHandlerPos = m_aAttributeHandlers.find( _nAttributeId ); + if ( aHandlerPos == m_aAttributeHandlers.end() ) + { + ::rtl::Reference< AttributeHandler > aHandler = AttributeHandlerFactory::getHandlerFor( _nAttributeId, *m_pEngine->GetEmptyItemSet().GetPool() ); + OSL_ENSURE( aHandler.is(), "RichTextControlImpl::enableAttributeNotification: no handler available for this attribute!" ); + if ( !aHandler.is() ) + return; + SAL_WARN_IF( _nAttributeId != aHandler->getAttributeId(), "forms.richtext", "RichTextControlImpl::enableAttributeNotification: suspicious handler!" ); + + aHandlerPos = m_aAttributeHandlers.emplace( _nAttributeId , aHandler ).first; + } + + // remember the listener + if ( _pListener ) + m_aAttributeListeners.emplace( _nAttributeId, _pListener ); + + // update (and broadcast) the state of this attribute + updateAttribute( _nAttributeId ); + } + + + void RichTextControlImpl::disableAttributeNotification( AttributeId _nAttributeId ) + { + // forget the handler for this attribute + AttributeHandlerPool::iterator aHandlerPos = m_aAttributeHandlers.find( _nAttributeId ); + if ( aHandlerPos != m_aAttributeHandlers.end() ) + m_aAttributeHandlers.erase( aHandlerPos ); + + // as well as the listener + AttributeListenerPool::iterator aListenerPos = m_aAttributeListeners.find( _nAttributeId ); + if ( aListenerPos != m_aAttributeListeners.end() ) + m_aAttributeListeners.erase( aListenerPos ); + } + + + void RichTextControlImpl::normalizeScriptDependentAttribute( SvxScriptSetItem& _rScriptSetItem ) + { + _rScriptSetItem.GetItemSet().Put( m_pView->GetAttribs(), false ); + const SfxPoolItem* pNormalizedItem = _rScriptSetItem.GetItemOfScript( getSelectedScriptType() ); + + WhichId nNormalizedWhichId = _rScriptSetItem.GetItemSet().GetPool()->GetWhich( _rScriptSetItem.Which() ); + if ( pNormalizedItem ) + { + _rScriptSetItem.GetItemSet().Put( pNormalizedItem->CloneSetWhich(nNormalizedWhichId) ); + } + else + _rScriptSetItem.GetItemSet().InvalidateItem( nNormalizedWhichId ); + } + + + void RichTextControlImpl::implCheckUpdateCache( AttributeId _nAttribute, const AttributeState& _rState ) + { + StateCache::iterator aCachePos = m_aLastKnownStates.find( _nAttribute ); + if ( aCachePos == m_aLastKnownStates.end() ) + { // nothing known about this attribute, yet + m_aLastKnownStates.emplace( _nAttribute, _rState ); + } + else + { + if ( aCachePos->second == _rState ) + { + // nothing to do + return; + } + aCachePos->second = _rState; + } + + // is there a dedicated listener for this particular attribute? + AttributeListenerPool::const_iterator aListenerPos = m_aAttributeListeners.find( _nAttribute ); + if ( aListenerPos != m_aAttributeListeners.end( ) ) + aListenerPos->second->onAttributeStateChanged( _nAttribute ); + + // call our global listener, if there is one + if ( m_pTextAttrListener ) + m_pTextAttrListener->onAttributeStateChanged( _nAttribute ); + } + + + SvtScriptType RichTextControlImpl::getSelectedScriptType() const + { + SvtScriptType nScript = m_pView->GetSelectedScriptType(); + if ( nScript == SvtScriptType::NONE ) + nScript = SvtLanguageOptions::GetScriptTypeOfLanguage( Application::GetSettings().GetLanguageTag().getLanguageType() ); + return nScript; + } + + + void RichTextControlImpl::EditEngineStatusChanged( const EditStatus& _rStatus ) + { + EditStatusFlags nStatusWord( _rStatus.GetStatusWord() ); + if ( ( nStatusWord & EditStatusFlags::TEXTWIDTHCHANGED ) + || ( nStatusWord & EditStatusFlags::TextHeightChanged ) + ) + { + if ( ( nStatusWord & EditStatusFlags::TextHeightChanged ) && windowHasAutomaticLineBreak() ) + m_pEngine->SetPaperSize( Size( m_pEngine->GetPaperSize().Width(), m_pEngine->GetTextHeight() ) ); + + updateScrollbars(); + } + + bool bHScroll = bool( nStatusWord & EditStatusFlags::HSCROLL ); + bool bVScroll = bool( nStatusWord & EditStatusFlags::VSCROLL ); + + // In case of *no* automatic line breaks, we also need to check for the *range* here. + // Normally, we would do this only after an EditStatusFlags::TEXTWIDTHCHANGED. However, due to a bug + // in the EditEngine (I believe so) this is not fired when the engine does not have + // the AutoPaperSize bits set. + // So in order to be properly notified, we would need the AutoPaperSize. But, with + // AutoPaperSize, other things do not work anymore: Either, when we set a MaxAutoPaperSize, + // then the view does automatic soft line breaks at the paper end - which we definitely do + // want. Or, if we did not set a MaxAutoPaperSize, then the view does not automatically scroll + // anymore in horizontal direction. + // So this is some kind of lose-lose situation ... :( + if ( !windowHasAutomaticLineBreak() && bHScroll ) + { + updateScrollbars(); + return; + } + + if ( bHScroll && m_pHScroll ) + m_pHScroll->SetThumbPos( m_pView->GetVisArea().Left() ); + if ( bVScroll && m_pVScroll ) + m_pVScroll->SetThumbPos( m_pView->GetVisArea().Top() ); + } + + IMPL_LINK_NOARG( RichTextControlImpl, OnInvalidateAllAttributes, LinkParamNone*, void ) + { + updateAllAttributes(); + } + + IMPL_LINK( RichTextControlImpl, OnHScroll, weld::Scrollbar&, rScrollbar, void ) + { + auto nDiff = m_pView->GetVisArea().Left() - rScrollbar.adjustment_get_value(); + m_pView->Scroll(nDiff, 0, ScrollRangeCheck::PaperWidthTextSize); + } + + IMPL_LINK(RichTextControlImpl, OnVScroll, weld::Scrollbar&, rScrollbar, void) + { + auto nDiff = m_pView->GetVisArea().Top() - rScrollbar.adjustment_get_value(); + m_pView->Scroll(0, nDiff, ScrollRangeCheck::PaperWidthTextSize); + } + + void RichTextControlImpl::ensureScrollbars() + { + bool bNeedVScroll = 0 != ( m_pAntiImpl->GetStyle() & WB_VSCROLL ); + bool bNeedHScroll = 0 != ( m_pAntiImpl->GetStyle() & WB_HSCROLL ); + + if ( ( bNeedVScroll == hasVScrollBar() ) && ( bNeedHScroll == hasHScrollBar( ) ) ) + // nothing to do + return; + + // create or delete the scrollbars, as necessary + if ( !bNeedVScroll ) + { + m_pVScroll.disposeAndClear(); + } + else + { + m_pVScroll = VclPtr<ScrollAdaptor>::Create( m_pAntiImpl, false ); + m_pVScroll->SetScrollHdl ( LINK( this, RichTextControlImpl, OnVScroll ) ); + m_pVScroll->Show(); + } + + if ( !bNeedHScroll ) + { + m_pHScroll.disposeAndClear(); + } + else + { + m_pHScroll = VclPtr<ScrollAdaptor>::Create( m_pAntiImpl, true ); + m_pHScroll->SetScrollHdl ( LINK( this, RichTextControlImpl, OnHScroll ) ); + m_pHScroll->Show(); + } + + layoutWindow(); + } + + void RichTextControlImpl::ensureLineBreakSetting() + { + if ( !windowHasAutomaticLineBreak() ) + m_pEngine->SetPaperSize( Size( EMPTY_PAPER_SIZE, EMPTY_PAPER_SIZE ) ); + + layoutWindow(); + } + + void RichTextControlImpl::layoutWindow() + { + if ( !m_bHasEverBeenShown ) + // no need to do anything. Especially, no need to set the paper size on the + // EditEngine to anything... + return; + + const StyleSettings& rStyleSettings = m_pAntiImpl->GetSettings().GetStyleSettings(); + + tools::Long nScrollBarWidth = m_pVScroll ? rStyleSettings.GetScrollBarSize() : 0; + tools::Long nScrollBarHeight = m_pHScroll ? rStyleSettings.GetScrollBarSize() : 0; + + if ( m_pAntiImpl->IsZoom() ) + { + nScrollBarWidth = m_pAntiImpl->CalcZoom( nScrollBarWidth ); + nScrollBarHeight = m_pAntiImpl->CalcZoom( nScrollBarHeight ); + } + + // the overall size we can use + Size aPlaygroundSizePixel( m_pAntiImpl->GetOutputSizePixel() ); + + // the size of the viewport - note that the viewport does *not* occupy all the place + // which is left when subtracting the scrollbar width/height + Size aViewportPlaygroundPixel( aPlaygroundSizePixel ); + aViewportPlaygroundPixel.setWidth( ::std::max( tools::Long( 10 ), tools::Long( aViewportPlaygroundPixel.Width() - nScrollBarWidth ) ) ); + aViewportPlaygroundPixel.setHeight( ::std::max( tools::Long( 10 ), tools::Long( aViewportPlaygroundPixel.Height() - nScrollBarHeight ) ) ); + Size aViewportPlaygroundLogic( m_pViewport->PixelToLogic( aViewportPlaygroundPixel ) ); + + const tools::Long nOffset = 2; + Size aViewportSizePixel( aViewportPlaygroundPixel.Width() - 2 * nOffset, aViewportPlaygroundPixel.Height() - 2 * nOffset ); + Size aViewportSizeLogic( m_pViewport->PixelToLogic( aViewportSizePixel ) ); + + // position the viewport + m_pViewport->SetPosSizePixel( Point( nOffset, nOffset ), aViewportSizePixel ); + // position the scrollbars + if ( m_pVScroll ) + { + m_pVScroll->SetThickness(nScrollBarWidth); + m_pVScroll->SetPosSizePixel( Point( aViewportPlaygroundPixel.Width(), 0 ), Size( nScrollBarWidth, aViewportPlaygroundPixel.Height() ) ); + } + if ( m_pHScroll ) + { + m_pHScroll->SetThickness(nScrollBarHeight); + m_pHScroll->SetPosSizePixel( Point( 0, aViewportPlaygroundPixel.Height() ), Size( aViewportPlaygroundPixel.Width(), nScrollBarHeight ) ); + } + + // paper size + if ( windowHasAutomaticLineBreak() ) + m_pEngine->SetPaperSize( Size( aViewportSizeLogic.Width(), m_pEngine->GetTextHeight() ) ); + + // output area of the view + m_pView->SetOutputArea( tools::Rectangle( Point( ), aViewportSizeLogic ) ); + m_pView->SetVisArea( tools::Rectangle( Point( ), aViewportSizeLogic ) ); + + if ( m_pVScroll ) + { + m_pVScroll->SetVisibleSize( aViewportPlaygroundLogic.Height() ); + + // the default height of a text line... + tools::Long nFontHeight = m_pEngine->GetStandardFont(0).GetFontSize().Height(); + // ... is the scroll size for the vertical scrollbar + m_pVScroll->SetLineSize( nFontHeight ); + // the viewport width, minus one line, is the page scroll size + m_pVScroll->SetPageSize( ::std::max( nFontHeight, aViewportPlaygroundLogic.Height() - nFontHeight ) ); + } + + // the font width + if ( m_pHScroll ) + { + m_pHScroll->SetVisibleSize( aViewportPlaygroundLogic.Width() ); + + tools::Long nFontWidth = m_pEngine->GetStandardFont(0).GetFontSize().Width(); + if ( !nFontWidth ) + { + m_pViewport->GetOutDev()->Push( vcl::PushFlags::FONT ); + m_pViewport->SetFont( m_pEngine->GetStandardFont(0) ); + nFontWidth = m_pViewport->GetTextWidth( "x" ); + m_pViewport->GetOutDev()->Pop(); + } + // ... is the scroll size for the horizontal scrollbar + m_pHScroll->SetLineSize( 5 * nFontWidth ); + // the viewport height, minus one character, is the page scroll size + m_pHScroll->SetPageSize( ::std::max( nFontWidth, aViewportPlaygroundLogic.Width() - nFontWidth ) ); + } + + // update range and position of the scrollbars + updateScrollbars(); + } + + + void RichTextControlImpl::updateScrollbars() + { + if ( m_pVScroll ) + { + tools::Long nOverallTextHeight = m_pEngine->GetTextHeight(); + m_pVScroll->SetRange( Range( 0, nOverallTextHeight ) ); + m_pVScroll->SetThumbPos( m_pView->GetVisArea().Top() ); + } + + if ( m_pHScroll ) + { + Size aPaperSize( m_pEngine->GetPaperSize() ); + tools::Long nOverallTextWidth = ( aPaperSize.Width() == EMPTY_PAPER_SIZE ) ? m_pEngine->CalcTextWidth() : aPaperSize.Width(); + m_pHScroll->SetRange( Range( 0, nOverallTextWidth ) ); + m_pHScroll->SetThumbPos( m_pView->GetVisArea().Left() ); + } + } + + + void RichTextControlImpl::notifyInitShow() + { + if ( !m_bHasEverBeenShown ) + { + m_bHasEverBeenShown = true; + layoutWindow(); + } + } + + + void RichTextControlImpl::notifyStyleChanged() + { + ensureScrollbars(); + ensureLineBreakSetting(); + } + + + void RichTextControlImpl::notifyZoomChanged() + { + const Fraction& rZoom = m_pAntiImpl->GetZoom(); + + MapMode aMapMode( m_pAntiImpl->GetMapMode() ); + aMapMode.SetScaleX( rZoom ); + aMapMode.SetScaleY( rZoom ); + m_pAntiImpl->SetMapMode( aMapMode ); + + m_pViewport->SetZoom( rZoom ); + m_pViewport->SetMapMode( aMapMode ); + + layoutWindow(); + } + + + bool RichTextControlImpl::windowHasAutomaticLineBreak() + { + return ( m_pAntiImpl->GetStyle() & WB_WORDBREAK ) != 0; + } + + + void RichTextControlImpl::SetReadOnly( bool _bReadOnly ) + { + m_pView->SetReadOnly( _bReadOnly ); + } + + + bool RichTextControlImpl::IsReadOnly() const + { + return m_pView->IsReadOnly( ); + } + + + namespace + { + void lcl_inflate( tools::Rectangle& _rRect, tools::Long _nInflateX, tools::Long _nInflateY ) + { + _rRect.AdjustLeft( -_nInflateX ); + _rRect.AdjustRight(_nInflateX ); + _rRect.AdjustTop( -_nInflateY ); + _rRect.AdjustBottom(_nInflateY ); + } + } + + bool RichTextControlImpl::HandleCommand( const CommandEvent& _rEvent ) + { + if ( ( _rEvent.GetCommand() == CommandEventId::Wheel ) + || ( _rEvent.GetCommand() == CommandEventId::StartAutoScroll ) + || ( _rEvent.GetCommand() == CommandEventId::AutoScroll ) + ) + { + m_pAntiImpl->HandleScrollCommand( _rEvent, m_pHScroll, m_pVScroll ); + return true; + } + return false; + } + + + void RichTextControlImpl::Draw( OutputDevice* _pDev, const Point& _rPos, const Size& _rSize ) + { + // need to normalize the map mode of the device - every paint operation on any device needs + // to use the same map mode + _pDev->Push( vcl::PushFlags::MAPMODE | vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR ); + + // enforce our "normalize map mode" on the device + MapMode aRefMapMode( m_pEngine->GetRefDevice()->GetMapMode() ); + MapMode aOriginalMapMode( _pDev->GetMapMode() ); + MapMode aNormalizedMapMode( aRefMapMode.GetMapUnit(), aRefMapMode.GetOrigin(), aOriginalMapMode.GetScaleX(), aOriginalMapMode.GetScaleY() ); + _pDev->SetMapMode( aNormalizedMapMode ); + + // translate coordinates + Point aPos( _rPos ); + Size aSize( _rSize ); + if ( aOriginalMapMode.GetMapUnit() == MapUnit::MapPixel ) + { + aPos = _pDev->PixelToLogic( _rPos, aNormalizedMapMode ); + aSize = _pDev->PixelToLogic( _rSize, aNormalizedMapMode ); + } + else + { + aPos = OutputDevice::LogicToLogic( _rPos, aOriginalMapMode, aNormalizedMapMode ); + aSize = OutputDevice::LogicToLogic( _rSize, aOriginalMapMode, aNormalizedMapMode ); + } + + tools::Rectangle aPlayground( aPos, aSize ); + Size aOnePixel( _pDev->PixelToLogic( Size( 1, 1 ) ) ); + aPlayground.AdjustRight( -(aOnePixel.Width()) ); + aPlayground.AdjustBottom( -(aOnePixel.Height()) ); + + // background + _pDev->SetLineColor(); + _pDev->DrawRect( aPlayground ); + + // do we need to draw a border? + bool bBorder = ( m_pAntiImpl->GetStyle() & WB_BORDER ); + if ( bBorder ) + _pDev->SetLineColor( m_pAntiImpl->GetSettings().GetStyleSettings().GetMonoColor() ); + else + _pDev->SetLineColor(); + _pDev->SetFillColor( m_pAntiImpl->GetBackground().GetColor() ); + _pDev->DrawRect( aPlayground ); + + if ( bBorder ) + // don't draw the text over the border + lcl_inflate( aPlayground, -aOnePixel.Width(), -aOnePixel.Height() ); + + // leave a space of two pixels between the "surroundings" of the control + // and the content + lcl_inflate( aPlayground, -aOnePixel.Width(), -aOnePixel.Height() ); + lcl_inflate( aPlayground, -aOnePixel.Width(), -aOnePixel.Height() ); + + // actually draw the content + m_pEngine->Draw(*_pDev, aPlayground, Point(), true); + + _pDev->Pop(); + } + + + void RichTextControlImpl::SetBackgroundColor( ) + { + SetBackgroundColor( Application::GetSettings().GetStyleSettings().GetFieldColor() ); + } + + + void RichTextControlImpl::SetBackgroundColor( const Color& _rColor ) + { + Wallpaper aWallpaper( _rColor ); + m_pAntiImpl->SetBackground( aWallpaper ); + m_pViewport->SetBackground( aWallpaper ); + } + + + void RichTextControlImpl::SetHideInactiveSelection( bool _bHide ) + { + m_pViewport->SetHideInactiveSelection( _bHide ); + } + + + bool RichTextControlImpl::GetHideInactiveSelection() const + { + return m_pViewport->GetHideInactiveSelection( ); + } + + +} // namespace frm + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/richtext/richtextimplcontrol.hxx b/forms/source/richtext/richtextimplcontrol.hxx new file mode 100644 index 0000000000..86b1a18316 --- /dev/null +++ b/forms/source/richtext/richtextimplcontrol.hxx @@ -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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include "rtattributehandler.hxx" +#include "richtextviewport.hxx" +#include "richtextengine.hxx" +#include <svtools/scrolladaptor.hxx> +#include <editeng/editdata.hxx> + +#include <map> + +class EditView; +class EditStatus; +namespace vcl { class Window; } +class SvxScriptSetItem; + +namespace frm +{ + + + class ITextAttributeListener; + class ITextSelectionListener; + class RichTextViewPort; + + class RichTextControlImpl : public IEngineStatusListener + { + typedef ::std::map< AttributeId, AttributeState > StateCache; + typedef ::std::map< AttributeId, ::rtl::Reference< AttributeHandler > > AttributeHandlerPool; + typedef ::std::map< AttributeId, ITextAttributeListener* > AttributeListenerPool; + + StateCache m_aLastKnownStates; + AttributeHandlerPool m_aAttributeHandlers; + AttributeListenerPool m_aAttributeListeners; + + ESelection m_aLastKnownSelection; + + VclPtr<Control> m_pAntiImpl; + VclPtr<RichTextViewPort> m_pViewport; + VclPtr<ScrollAdaptor> m_pHScroll; + VclPtr<ScrollAdaptor> m_pVScroll; + RichTextEngine* m_pEngine; + std::unique_ptr<EditView> m_pView; + ITextAttributeListener* m_pTextAttrListener; + ITextSelectionListener* m_pSelectionListener; + bool m_bHasEverBeenShown; + + public: + struct GrantAccess { friend class RichTextControl; private: GrantAccess() { } }; + EditView* getView( const GrantAccess& ) const { return m_pView.get(); } + RichTextEngine* getEngine( const GrantAccess& ) const { return m_pEngine; } + vcl::Window* getViewport( const GrantAccess& ) const { return m_pViewport; } + + public: + RichTextControlImpl( Control* _pAntiImpl, RichTextEngine* _pEngine, + ITextAttributeListener* _pTextAttrListener, ITextSelectionListener* _pSelectionListener ); + virtual ~RichTextControlImpl(); + + /** updates the cache with the state of all attribute values from the given set, notifies + the listener if the state changed + */ + void updateAllAttributes( ); + + /** updates the cache with the state of the attribute given by which id, notifies + the listener if the state changed + */ + void updateAttribute( AttributeId _nAttribute ); + + /// enables the callback for a particular attribute + void enableAttributeNotification( AttributeId _nAttributeId, ITextAttributeListener* _pListener ); + + /// disables the change notifications for a particular attribute + void disableAttributeNotification( AttributeId _nAttributeId ); + + /// executes a toggle of the given attribute + bool executeAttribute( const SfxItemSet& _rCurrentAttribs, SfxItemSet& _rNewAttribs, AttributeId _nAttribute, const SfxPoolItem* _pArgument, SvtScriptType _nForScriptType ); + + /// retrieves the state of the given attribute from the cache + AttributeState getAttributeState( AttributeId _nAttributeId ) const; + + /** normalizes the given item so that the state of script dependent attributes + is correct considering the current script type + + There are some attributes which are script dependent, e.g. the CharPosture. This means + that in real, there are 3 attributes for this, one for every possible script type (latin, + asian, complex). However, to the out world, we behave as if there is only one attribute: + E.g., if the outer world asks for the state of the "CharPosture" attribute, we return + the state of either CharPostureLatin, CharPostureAsian, or CharPostureComplex, depending + on the script type of the current selection. (In real, it may be more complex since + the current selection may contain more than one script type.) + + This method normalizes a script dependent attribute, so that it's state takes into account + the currently selected script type. + */ + void normalizeScriptDependentAttribute( SvxScriptSetItem& _rScriptSetItem ); + + // gets the script type of the selection in our edit view (with fallback) + SvtScriptType getSelectedScriptType() const; + + /** re-arranges the view and the scrollbars + */ + void layoutWindow(); + + /** to be called when the style of our window changed + */ + void notifyStyleChanged(); + + /** to be called when the zoom of our window changed + */ + void notifyZoomChanged(); + + /** to be called when the StateChangedType::InitShow event arrives + */ + void notifyInitShow(); + + // VCL "overrides" + void SetBackgroundColor( ); + void SetBackgroundColor( const Color& _rColor ); + + void SetReadOnly( bool _bReadOnly ); + bool IsReadOnly() const; + + void SetHideInactiveSelection( bool _bHide ); + bool GetHideInactiveSelection() const; + + /// draws the control onto a given output device + void Draw( OutputDevice* _pDev, const Point& _rPos, const Size& _rSize ); + + /// handles command events arrived at the anti-impl control + bool HandleCommand( const CommandEvent& _rEvent ); + + private: + // updates the cache with the state provided by the given attribute handler + void implUpdateAttribute( const AttributeHandlerPool::const_iterator& _pHandler ); + + // updates the cache with the given state, and calls listeners (if necessary) + void implCheckUpdateCache( AttributeId _nAttribute, const AttributeState& _rState ); + + // updates range and position of our scrollbars + void updateScrollbars(); + + // determines whether automatic (soft) line breaks are ON + bool windowHasAutomaticLineBreak(); + + /// hides or shows our scrollbars, according to the current WinBits of the window + void ensureScrollbars(); + + /// ensures that our "automatic line break" setting matches the current WinBits of the window + void ensureLineBreakSetting(); + + bool hasVScrollBar( ) const { return m_pVScroll != nullptr; } + bool hasHScrollBar( ) const { return m_pHScroll != nullptr; } + + // IEngineStatusListener overridables + virtual void EditEngineStatusChanged( const EditStatus& _rStatus ) override; + + private: + DECL_LINK( OnInvalidateAllAttributes, LinkParamNone*, void ); + DECL_LINK( OnHScroll, weld::Scrollbar&, void ); + DECL_LINK( OnVScroll, weld::Scrollbar&, void ); + }; + + +} // namespace frm + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/richtext/richtextmodel.cxx b/forms/source/richtext/richtextmodel.cxx new file mode 100644 index 0000000000..d9e01def22 --- /dev/null +++ b/forms/source/richtext/richtextmodel.cxx @@ -0,0 +1,641 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "richtextmodel.hxx" +#include "richtextengine.hxx" +#include "richtextunowrapper.hxx" + +#include <property.hxx> +#include <services.hxx> + +#include <com/sun/star/awt/LineEndFormat.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/form/FormComponentType.hpp> +#include <com/sun/star/text/WritingMode2.hpp> +#include <com/sun/star/style/VerticalAlignment.hpp> + +#include <comphelper/guarding.hxx> +#include <comphelper/servicehelper.hxx> +#include <toolkit/awt/vclxdevice.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <tools/debug.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <editeng/editstat.hxx> +#include <vcl/outdev.hxx> +#include <vcl/svapp.hxx> + + +namespace frm +{ + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::awt; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::io; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::form; + using namespace ::com::sun::star::util; + using namespace ::com::sun::star::style; + + namespace WritingMode2 = ::com::sun::star::text::WritingMode2; + + ORichTextModel::ORichTextModel( const Reference< XComponentContext >& _rxFactory ) + :OControlModel ( _rxFactory, OUString() ) + ,FontControlModel ( true ) + ,m_pEngine ( RichTextEngine::Create() ) + ,m_bSettingEngineText( false ) + ,m_aModifyListeners ( m_aMutex ) + { + m_nClassId = FormComponentType::TEXTFIELD; + + getPropertyDefaultByHandle( PROPERTY_ID_DEFAULTCONTROL ) >>= m_sDefaultControl; + // Default to 'flat' instead of '3D Look' for form controls, but don't change + // getPropertyDefaultByHandle, see tdf#152974 + m_nBorder = 2; + getPropertyDefaultByHandle( PROPERTY_ID_ENABLED ) >>= m_bEnabled; + getPropertyDefaultByHandle( PROPERTY_ID_ENABLEVISIBLE ) >>= m_bEnableVisible; + getPropertyDefaultByHandle( PROPERTY_ID_HARDLINEBREAKS ) >>= m_bHardLineBreaks; + getPropertyDefaultByHandle( PROPERTY_ID_HSCROLL ) >>= m_bHScroll; + getPropertyDefaultByHandle( PROPERTY_ID_VSCROLL ) >>= m_bVScroll; + getPropertyDefaultByHandle( PROPERTY_ID_READONLY ) >>= m_bReadonly; + getPropertyDefaultByHandle( PROPERTY_ID_PRINTABLE ) >>= m_bPrintable; + m_aAlign = getPropertyDefaultByHandle( PROPERTY_ID_ALIGN ); + getPropertyDefaultByHandle( PROPERTY_ID_ECHO_CHAR ) >>= m_nEchoChar; + getPropertyDefaultByHandle( PROPERTY_ID_MAXTEXTLEN ) >>= m_nMaxTextLength; + getPropertyDefaultByHandle( PROPERTY_ID_MULTILINE ) >>= m_bMultiLine; + getPropertyDefaultByHandle( PROPERTY_ID_RICH_TEXT ) >>= m_bReallyActAsRichText; + getPropertyDefaultByHandle( PROPERTY_ID_HIDEINACTIVESELECTION ) >>= m_bHideInactiveSelection; + getPropertyDefaultByHandle( PROPERTY_ID_LINEEND_FORMAT ) >>= m_nLineEndFormat; + getPropertyDefaultByHandle( PROPERTY_ID_WRITING_MODE ) >>= m_nTextWritingMode; + getPropertyDefaultByHandle( PROPERTY_ID_CONTEXT_WRITING_MODE ) >>= m_nContextWritingMode; + + implInit(); + } + + + ORichTextModel::ORichTextModel( const ORichTextModel* _pOriginal, const Reference< XComponentContext >& _rxFactory ) + :OControlModel ( _pOriginal, _rxFactory, false ) + ,FontControlModel ( _pOriginal ) + ,m_bSettingEngineText( false ) + ,m_aModifyListeners ( m_aMutex ) + { + + m_aTabStop = _pOriginal->m_aTabStop; + m_aBackgroundColor = _pOriginal->m_aBackgroundColor; + m_aBorderColor = _pOriginal->m_aBorderColor; + m_aVerticalAlignment = _pOriginal->m_aVerticalAlignment; + m_sDefaultControl = _pOriginal->m_sDefaultControl; + m_sHelpText = _pOriginal->m_sHelpText; + m_sHelpURL = _pOriginal->m_sHelpURL; + m_nBorder = _pOriginal->m_nBorder; + m_bEnabled = _pOriginal->m_bEnabled; + m_bEnableVisible = _pOriginal->m_bEnableVisible; + m_bHardLineBreaks = _pOriginal->m_bHardLineBreaks; + m_bHScroll = _pOriginal->m_bHScroll; + m_bVScroll = _pOriginal->m_bVScroll; + m_bReadonly = _pOriginal->m_bReadonly; + m_bPrintable = _pOriginal->m_bPrintable; + m_bReallyActAsRichText = _pOriginal->m_bReallyActAsRichText; + m_bHideInactiveSelection = _pOriginal->m_bHideInactiveSelection; + m_nLineEndFormat = _pOriginal->m_nLineEndFormat; + m_nTextWritingMode = _pOriginal->m_nTextWritingMode; + m_nContextWritingMode = _pOriginal->m_nContextWritingMode; + + m_aAlign = _pOriginal->m_aAlign; + m_nEchoChar = _pOriginal->m_nEchoChar; + m_nMaxTextLength = _pOriginal->m_nMaxTextLength; + m_bMultiLine = _pOriginal->m_bMultiLine; + + m_pEngine.reset(_pOriginal->m_pEngine->Clone()); + m_sLastKnownEngineText = m_pEngine->GetText(); + + implInit(); + } + + + void ORichTextModel::implInit() + { + OSL_ENSURE(m_pEngine, "ORichTextModel::implInit: where's the engine?"); + if (m_pEngine) + { + m_pEngine->SetModifyHdl( LINK( this, ORichTextModel, OnEngineContentModified ) ); + + EEControlBits nEngineControlWord = m_pEngine->GetControlWord(); + nEngineControlWord = nEngineControlWord & ~EEControlBits::AUTOPAGESIZE; + m_pEngine->SetControlWord( nEngineControlWord ); + + rtl::Reference<VCLXDevice> pUnoRefDevice = new VCLXDevice; + { + SolarMutexGuard g; + pUnoRefDevice->SetOutputDevice( m_pEngine->GetRefDevice() ); + } + m_xReferenceDevice = pUnoRefDevice; + } + + implDoAggregation(); + implRegisterProperties(); + } + + + void ORichTextModel::implDoAggregation() + { + osl_atomic_increment( &m_refCount ); + + { + m_xAggregate = new ORichTextUnoWrapper( *m_pEngine, this ); + setAggregation( m_xAggregate ); + doSetDelegator(); + } + + osl_atomic_decrement( &m_refCount ); + } + + + void ORichTextModel::implRegisterProperties() + { + registerProperty( PROPERTY_DEFAULTCONTROL, PROPERTY_ID_DEFAULTCONTROL, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT, + &m_sDefaultControl, cppu::UnoType<decltype(m_sDefaultControl)>::get() ); + registerProperty( PROPERTY_HELPTEXT, PROPERTY_ID_HELPTEXT, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT, + &m_sHelpText, cppu::UnoType<decltype(m_sHelpText)>::get() ); + registerProperty( PROPERTY_HELPURL, PROPERTY_ID_HELPURL, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT, + &m_sHelpURL, cppu::UnoType<decltype(m_sHelpURL)>::get() ); + registerProperty( PROPERTY_ENABLED, PROPERTY_ID_ENABLED, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT, + &m_bEnabled, cppu::UnoType<decltype(m_bEnabled)>::get() ); + registerProperty( PROPERTY_ENABLEVISIBLE, PROPERTY_ID_ENABLEVISIBLE, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT, + &m_bEnableVisible, cppu::UnoType<decltype(m_bEnableVisible)>::get() ); + registerProperty( PROPERTY_BORDER, PROPERTY_ID_BORDER, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT, + &m_nBorder, cppu::UnoType<decltype(m_nBorder)>::get() ); + registerProperty( PROPERTY_HARDLINEBREAKS, PROPERTY_ID_HARDLINEBREAKS, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT, + &m_bHardLineBreaks, cppu::UnoType<decltype(m_bHardLineBreaks)>::get() ); + registerProperty( PROPERTY_HSCROLL, PROPERTY_ID_HSCROLL, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT, + &m_bHScroll, cppu::UnoType<decltype(m_bHScroll)>::get() ); + registerProperty( PROPERTY_VSCROLL, PROPERTY_ID_VSCROLL, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT, + &m_bVScroll, cppu::UnoType<decltype(m_bVScroll)>::get() ); + registerProperty( PROPERTY_READONLY, PROPERTY_ID_READONLY, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT, + &m_bReadonly, cppu::UnoType<decltype(m_bReadonly)>::get() ); + registerProperty( PROPERTY_PRINTABLE, PROPERTY_ID_PRINTABLE, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT, + &m_bPrintable, cppu::UnoType<decltype(m_bPrintable)>::get() ); + registerProperty( PROPERTY_REFERENCE_DEVICE, PROPERTY_ID_REFERENCE_DEVICE, PropertyAttribute::BOUND | PropertyAttribute::TRANSIENT, + &m_xReferenceDevice, cppu::UnoType<decltype(m_xReferenceDevice)>::get() ); + registerProperty( PROPERTY_RICH_TEXT, PROPERTY_ID_RICH_TEXT, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT, + &m_bReallyActAsRichText, cppu::UnoType<decltype(m_bReallyActAsRichText)>::get() ); + registerProperty( PROPERTY_HIDEINACTIVESELECTION, PROPERTY_ID_HIDEINACTIVESELECTION, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT, + &m_bHideInactiveSelection, cppu::UnoType<decltype(m_bHideInactiveSelection)>::get() ); + + registerMayBeVoidProperty( PROPERTY_TABSTOP, PROPERTY_ID_TABSTOP, PropertyAttribute::MAYBEVOID | PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT, + &m_aTabStop, cppu::UnoType<sal_Bool>::get() ); + registerMayBeVoidProperty( PROPERTY_BACKGROUNDCOLOR, PROPERTY_ID_BACKGROUNDCOLOR, PropertyAttribute::MAYBEVOID | PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT, + &m_aBackgroundColor, cppu::UnoType<sal_Int32>::get() ); + registerMayBeVoidProperty( PROPERTY_BORDERCOLOR, PROPERTY_ID_BORDERCOLOR, PropertyAttribute::MAYBEVOID | PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT, + &m_aBorderColor, cppu::UnoType<sal_Int32>::get() ); + registerMayBeVoidProperty( PROPERTY_VERTICAL_ALIGN, PROPERTY_ID_VERTICAL_ALIGN, PropertyAttribute::MAYBEVOID | PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT, + &m_aVerticalAlignment, cppu::UnoType<VerticalAlignment>::get() ); + + // properties which exist only for compatibility with the css.swt.UnoControlEditModel, + // since we replace the default implementation for this service + registerProperty( PROPERTY_ECHO_CHAR, PROPERTY_ID_ECHO_CHAR, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT, + &m_nEchoChar, cppu::UnoType<decltype(m_nEchoChar)>::get() ); + registerProperty( PROPERTY_MAXTEXTLEN, PROPERTY_ID_MAXTEXTLEN, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT, + &m_nMaxTextLength, cppu::UnoType<decltype(m_nMaxTextLength)>::get() ); + registerProperty( PROPERTY_MULTILINE, PROPERTY_ID_MULTILINE, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT, + &m_bMultiLine, cppu::UnoType<decltype(m_bMultiLine)>::get() ); + registerProperty( PROPERTY_TEXT, PROPERTY_ID_TEXT, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT, + &m_sLastKnownEngineText, cppu::UnoType<decltype(m_sLastKnownEngineText)>::get() ); + registerProperty( PROPERTY_LINEEND_FORMAT, PROPERTY_ID_LINEEND_FORMAT, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT, + &m_nLineEndFormat, cppu::UnoType<decltype(m_nLineEndFormat)>::get() ); + registerProperty( PROPERTY_WRITING_MODE, PROPERTY_ID_WRITING_MODE, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT, + &m_nTextWritingMode, cppu::UnoType<decltype(m_nTextWritingMode)>::get() ); + + registerProperty( PROPERTY_CONTEXT_WRITING_MODE, PROPERTY_ID_CONTEXT_WRITING_MODE, PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT | + PropertyAttribute::TRANSIENT, &m_nContextWritingMode, cppu::UnoType<decltype(m_nContextWritingMode)>::get() ); + + registerMayBeVoidProperty( PROPERTY_ALIGN, PROPERTY_ID_ALIGN, PropertyAttribute::MAYBEVOID | PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT, + &m_aAlign, cppu::UnoType<sal_Int16>::get() ); + } + + + ORichTextModel::~ORichTextModel( ) + { + if ( !OComponentHelper::rBHelper.bDisposed ) + { + acquire(); + dispose(); + } + if (m_pEngine) + { + SolarMutexGuard g; + m_pEngine.reset(); + } + } + + + Any SAL_CALL ORichTextModel::queryAggregation( const Type& _rType ) + { + Any aReturn = ORichTextModel_BASE::queryInterface( _rType ); + + if ( !aReturn.hasValue() ) + aReturn = OControlModel::queryAggregation( _rType ); + + return aReturn; + } + + + IMPLEMENT_FORWARD_XTYPEPROVIDER2( ORichTextModel, OControlModel, ORichTextModel_BASE ) + + OUString SAL_CALL ORichTextModel::getImplementationName() + { + return "com.sun.star.comp.forms.ORichTextModel"; + } + + Sequence< OUString > SAL_CALL ORichTextModel::getSupportedServiceNames() + { + Sequence< OUString > aOwnNames { + FRM_SUN_COMPONENT_RICHTEXTCONTROL, + "com.sun.star.text.TextRange", + "com.sun.star.style.CharacterProperties", + "com.sun.star.style.ParagraphProperties", + "com.sun.star.style.CharacterPropertiesAsian", + "com.sun.star.style.CharacterPropertiesComplex", + "com.sun.star.style.ParagraphPropertiesAsian", + "com.sun.star.style.ParagraphPropertiesComplex" }; + + return ::comphelper::combineSequences( + getAggregateServiceNames(), + ::comphelper::concatSequences( + OControlModel::getSupportedServiceNames_Static(), + aOwnNames) + ); + } + + css::uno::Reference< css::util::XCloneable > SAL_CALL ORichTextModel::createClone() +{ + rtl::Reference<ORichTextModel> pClone = new ORichTextModel(this, getContext()); + pClone->clonedFrom(this); + return pClone; +} + + + void SAL_CALL ORichTextModel::disposing() + { + m_aModifyListeners.disposeAndClear( EventObject( *this ) ); + OControlModel::disposing(); + } + + + namespace + { + void lcl_removeProperty( Sequence< Property >& _rSeq, std::u16string_view _rPropertyName ) + { + Property* pLoop = _rSeq.getArray(); + Property* pEnd = _rSeq.getArray() + _rSeq.getLength(); + while ( pLoop != pEnd ) + { + if ( pLoop->Name == _rPropertyName ) + { + ::std::copy( pLoop + 1, pEnd, pLoop ); + _rSeq.realloc( _rSeq.getLength() - 1 ); + break; + } + ++pLoop; + } + } + } + + void ORichTextModel::describeFixedProperties( Sequence< Property >& _rProps ) const + { + OControlModel::describeFixedProperties( _rProps ); + sal_Int32 nOldCount = _rProps.getLength(); + _rProps.realloc( nOldCount + 1); + css::beans::Property* pProperties = _rProps.getArray() + nOldCount; + *pProperties++ = css::beans::Property(PROPERTY_TABINDEX, PROPERTY_ID_TABINDEX, cppu::UnoType<sal_Int16>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEDEFAULT); + DBG_ASSERT( pProperties == _rProps.getArray() + _rProps.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?"); + + // properties which the OPropertyContainerHelper is responsible for + Sequence< Property > aContainedProperties; + describeProperties( aContainedProperties ); + + // properties which the FontControlModel is responsible for + Sequence< Property > aFontProperties; + describeFontRelatedProperties( aFontProperties ); + + _rProps = concatSequences( aContainedProperties, aFontProperties, _rProps ); + } + + + void ORichTextModel::describeAggregateProperties( Sequence< Property >& _rAggregateProps ) const + { + OControlModel::describeAggregateProperties( _rAggregateProps ); + + // our aggregate (the SvxUnoText) declares a FontDescriptor property, as does + // our FormControlFont base class. We remove it from the base class' sequence + // here, and later on care for both instances being in sync + lcl_removeProperty( _rAggregateProps, PROPERTY_FONT ); + + // similar, the WritingMode property is declared in our aggregate, too, but we override + // it, since the aggregate does no proper PropertyState handling. + lcl_removeProperty( _rAggregateProps, PROPERTY_WRITING_MODE ); + } + + + void SAL_CALL ORichTextModel::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const + { + if ( isRegisteredProperty( _nHandle ) ) + { + OPropertyContainerHelper::getFastPropertyValue( _rValue, _nHandle ); + } + else if ( isFontRelatedProperty( _nHandle ) ) + { + FontControlModel::getFastPropertyValue( _rValue, _nHandle ); + } + else + { + OControlModel::getFastPropertyValue( _rValue, _nHandle ); + } + } + + + sal_Bool SAL_CALL ORichTextModel::convertFastPropertyValue( Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue ) + { + bool bModified = false; + + if ( isRegisteredProperty( _nHandle ) ) + { + bModified = OPropertyContainerHelper::convertFastPropertyValue( _rConvertedValue, _rOldValue, _nHandle, _rValue ); + } + else if ( isFontRelatedProperty( _nHandle ) ) + { + bModified = FontControlModel::convertFastPropertyValue( _rConvertedValue, _rOldValue, _nHandle, _rValue ); + } + else + { + bModified = OControlModel::convertFastPropertyValue( _rConvertedValue, _rOldValue, _nHandle, _rValue ); + } + + return bModified; + } + + + void SAL_CALL ORichTextModel::setFastPropertyValue_NoBroadcast( sal_Int32 _nHandle, const Any& _rValue ) + { + if ( isRegisteredProperty( _nHandle ) ) + { + OPropertyContainerHelper::setFastPropertyValue( _nHandle, _rValue ); + + switch ( _nHandle ) + { + case PROPERTY_ID_REFERENCE_DEVICE: + { + #if OSL_DEBUG_LEVEL > 0 + MapMode aOldMapMode = m_pEngine->GetRefDevice()->GetMapMode(); + #endif + + OutputDevice* pRefDevice = VCLUnoHelper::GetOutputDevice( m_xReferenceDevice ); + OSL_ENSURE( pRefDevice, "ORichTextModel::setFastPropertyValue_NoBroadcast: empty reference device?" ); + m_pEngine->SetRefDevice( pRefDevice ); + + #if OSL_DEBUG_LEVEL > 0 + MapMode aNewMapMode = m_pEngine->GetRefDevice()->GetMapMode(); + OSL_ENSURE( aNewMapMode.GetMapUnit() == aOldMapMode.GetMapUnit(), + "ORichTextModel::setFastPropertyValue_NoBroadcast: You should not tamper with the MapUnit of the ref device!" ); + // if this assertion here is triggered, then we would need to adjust all + // items in all text portions in all paragraphs in the attributes of the EditEngine, + // as long as they are MapUnit-dependent. This holds at least for the FontSize. + #endif + } + break; + + case PROPERTY_ID_TEXT: + { + MutexRelease aReleaseMutex( m_aMutex ); + impl_smlock_setEngineText( m_sLastKnownEngineText ); + } + break; + } // switch ( _nHandle ) + } + else if ( isFontRelatedProperty( _nHandle ) ) + { + FontControlModel::setFastPropertyValue_NoBroadcast_impl( + *this, &ORichTextModel::setDependentFastPropertyValue, + _nHandle, _rValue); + } + else + { + switch ( _nHandle ) + { + case PROPERTY_ID_WRITING_MODE: + { + // forward to our aggregate, so the EditEngine knows about it + if ( m_xAggregateSet.is() ) + m_xAggregateSet->setPropertyValue( "WritingMode", _rValue ); + } + break; + + default: + OControlModel::setFastPropertyValue_NoBroadcast( _nHandle, _rValue ); + break; + } + } + } + + // note tdf#152974, we can't simply change a default here because properties + // that match the default are not exported, so for compatibility these + // can't be changed without some sort of solution for that + Any ORichTextModel::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const + { + Any aDefault; + + switch ( _nHandle ) + { + case PROPERTY_ID_WRITING_MODE: + case PROPERTY_ID_CONTEXT_WRITING_MODE: + aDefault <<= WritingMode2::CONTEXT; + break; + + case PROPERTY_ID_LINEEND_FORMAT: + aDefault <<= sal_Int16(LineEndFormat::LINE_FEED); + break; + + case PROPERTY_ID_ECHO_CHAR: + case PROPERTY_ID_ALIGN: + case PROPERTY_ID_MAXTEXTLEN: + aDefault <<= sal_Int16(0); + break; + + case PROPERTY_ID_TABSTOP: + case PROPERTY_ID_BACKGROUNDCOLOR: + case PROPERTY_ID_BORDERCOLOR: + case PROPERTY_ID_VERTICAL_ALIGN: + /* void */ + break; + + case PROPERTY_ID_ENABLED: + case PROPERTY_ID_ENABLEVISIBLE: + case PROPERTY_ID_PRINTABLE: + case PROPERTY_ID_HIDEINACTIVESELECTION: + aDefault <<= true; + break; + + case PROPERTY_ID_HARDLINEBREAKS: + case PROPERTY_ID_HSCROLL: + case PROPERTY_ID_VSCROLL: + case PROPERTY_ID_READONLY: + case PROPERTY_ID_MULTILINE: + case PROPERTY_ID_RICH_TEXT: + aDefault <<= false; + break; + + case PROPERTY_ID_DEFAULTCONTROL: + aDefault <<= FRM_SUN_CONTROL_RICHTEXTCONTROL; + break; + + case PROPERTY_ID_HELPTEXT: + case PROPERTY_ID_HELPURL: + case PROPERTY_ID_TEXT: + aDefault <<= OUString(); + break; + + case PROPERTY_ID_BORDER: + aDefault <<= sal_Int16(1); + break; + + default: + if ( isFontRelatedProperty( _nHandle ) ) + aDefault = FontControlModel::getPropertyDefaultByHandle( _nHandle ); + else + aDefault = OControlModel::getPropertyDefaultByHandle( _nHandle ); + } + + return aDefault; + } + + + void ORichTextModel::impl_smlock_setEngineText( const OUString& _rText ) + { + if (m_pEngine) + { + SolarMutexGuard aSolarGuard; + m_bSettingEngineText = true; + m_pEngine->SetText( _rText ); + m_bSettingEngineText = false; + } + } + + + OUString SAL_CALL ORichTextModel::getServiceName() + { + return FRM_SUN_COMPONENT_RICHTEXTCONTROL; + } + + + RichTextEngine* ORichTextModel::getEditEngine( const Reference< XControlModel >& _rxModel ) + { + RichTextEngine* pEngine = nullptr; + + Reference< XUnoTunnel > xTunnel( _rxModel, UNO_QUERY ); + OSL_ENSURE( xTunnel.is(), "ORichTextModel::getEditEngine: invalid model!" ); + if ( xTunnel.is() ) + { + try + { + pEngine = comphelper::getSomething_cast<RichTextEngine>(xTunnel->getSomething(getUnoTunnelId())); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "forms.richtext", "ORichTextModel::getEditEngine" ); + } + } + return pEngine; + } + + + const Sequence<sal_Int8> & ORichTextModel::getUnoTunnelId() + { + static const comphelper::UnoIdInit aId; + return aId.getSeq(); + } + + + IMPL_LINK_NOARG( ORichTextModel, OnEngineContentModified, LinkParamNone*, void ) + { + if ( !m_bSettingEngineText ) + { + m_aModifyListeners.notifyEach( &XModifyListener::modified, EventObject( *this ) ); + + potentialTextChange(); + // is this a good idea? It may become expensive in case of larger texts, + // and this method here is called for every single changed character ... + // On the other hand, the API *requires* us to notify changes in the "Text" + // property immediately ... + } + } + + + sal_Int64 SAL_CALL ORichTextModel::getSomething( const Sequence< sal_Int8 >& _rId ) + { + if (comphelper::isUnoTunnelId<ORichTextModel>(_rId)) + return comphelper::getSomething_cast(m_pEngine.get()); // Note returning a different type + + Reference< XUnoTunnel > xAggTunnel; + if ( query_aggregation( m_xAggregate, xAggTunnel ) ) + return xAggTunnel->getSomething( _rId ); + + return 0; + } + + + void SAL_CALL ORichTextModel::addModifyListener( const Reference< XModifyListener >& _rxListener ) + { + m_aModifyListeners.addInterface( _rxListener ); + } + + + void SAL_CALL ORichTextModel::removeModifyListener( const Reference< XModifyListener >& _rxListener ) + { + m_aModifyListeners.removeInterface( _rxListener ); + } + + + void ORichTextModel::potentialTextChange( ) + { + OUString sCurrentEngineText; + if (m_pEngine) + sCurrentEngineText = m_pEngine->GetText(); + + if ( sCurrentEngineText != m_sLastKnownEngineText ) + { + sal_Int32 nHandle = PROPERTY_ID_TEXT; + Any aOldValue; aOldValue <<= m_sLastKnownEngineText; + Any aNewValue; aNewValue <<= sCurrentEngineText; + fire( &nHandle, &aNewValue, &aOldValue, 1, false ); + + m_sLastKnownEngineText = sCurrentEngineText; + } + } + + +} // namespace frm + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_forms_ORichTextModel_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new frm::ORichTextModel(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/richtext/richtextmodel.hxx b/forms/source/richtext/richtextmodel.hxx new file mode 100644 index 0000000000..cf0bae7db4 --- /dev/null +++ b/forms/source/richtext/richtextmodel.hxx @@ -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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include <FormComponent.hxx> +#include <formcontrolfont.hxx> +#include "richtextunowrapper.hxx" +#include <comphelper/interfacecontainer3.hxx> +#include <comphelper/propertycontainerhelper.hxx> + +#include <com/sun/star/awt/XDevice.hpp> +#include <com/sun/star/util/XModifyBroadcaster.hpp> +#include <cppuhelper/implbase3.hxx> +#include <tools/link.hxx> +#include <memory> + +class EditEngine; + +namespace frm +{ + + + class RichTextEngine; + + //= ORichTextModel + + typedef ::cppu::ImplHelper3 < css::awt::XControlModel + , css::lang::XUnoTunnel + , css::util::XModifyBroadcaster + > ORichTextModel_BASE; + + class ORichTextModel + :public OControlModel + ,public FontControlModel + ,public IEngineTextChangeListener + ,public ::comphelper::OPropertyContainerHelper + ,public ORichTextModel_BASE + { + public: + ORichTextModel( + const css::uno::Reference< css::uno::XComponentContext>& _rxFactory + ); + ORichTextModel( + const ORichTextModel* _pOriginal, + const css::uno::Reference< css::uno::XComponentContext>& _rxFactory + ); + virtual ~ORichTextModel() override; + + private: + // <properties> + css::uno::Reference< css::awt::XDevice > + m_xReferenceDevice; + css::uno::Any m_aTabStop; + css::uno::Any m_aBackgroundColor; + css::uno::Any m_aBorderColor; + css::uno::Any m_aVerticalAlignment; + OUString m_sDefaultControl; + OUString m_sHelpText; + OUString m_sHelpURL; + OUString m_sLastKnownEngineText; + sal_Int16 m_nLineEndFormat; + sal_Int16 m_nTextWritingMode; + sal_Int16 m_nContextWritingMode; + sal_Int16 m_nBorder; + bool m_bEnabled; + bool m_bEnableVisible; + bool m_bHardLineBreaks; + bool m_bHScroll; + bool m_bVScroll; + bool m_bReadonly; + bool m_bPrintable; + bool m_bReallyActAsRichText; // despite the class name, the RichTextControl later on + // will create "ordinary" text peers depending on this property + bool m_bHideInactiveSelection; + // </properties> + + // <properties_for_awt_edit_compatibility> + css::uno::Any m_aAlign; + sal_Int16 m_nEchoChar; + sal_Int16 m_nMaxTextLength; + bool m_bMultiLine; + // </properties_for_awt_edit_compatibility> + + ::std::unique_ptr<RichTextEngine> + m_pEngine; + bool m_bSettingEngineText; + + ::comphelper::OInterfaceContainerHelper3<css::util::XModifyListener> + m_aModifyListeners; + + public: + static RichTextEngine* getEditEngine( const css::uno::Reference< css::awt::XControlModel >& _rxModel ); + + // UNO + DECLARE_UNO3_AGG_DEFAULTS( ORichTextModel, OControlModel ) + virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual ::css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XPersistObject + virtual OUString SAL_CALL getServiceName() override; + + // XTypeProvider + DECLARE_XTYPEPROVIDER() + + // XCloneable + virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone( ) override; + + // XUnoTunnel + virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) override; + static const css::uno::Sequence<sal_Int8> & getUnoTunnelId(); + + // XModifyBroadcaster + virtual void SAL_CALL addModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override; + virtual void SAL_CALL removeModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override; + + // XPropertySet and friends + virtual void SAL_CALL getFastPropertyValue(css::uno::Any& rValue, sal_Int32 nHandle ) const override; + virtual sal_Bool SAL_CALL convertFastPropertyValue(css::uno::Any& rConvertedValue, css::uno::Any& rOldValue, + sal_Int32 nHandle, const css::uno::Any& rValue ) override; + virtual void SAL_CALL setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const css::uno::Any& rValue) override; + virtual css::uno::Any getPropertyDefaultByHandle( sal_Int32 nHandle ) const override; + + // OControlModel's property handling + virtual void describeFixedProperties( + css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps + ) const override; + virtual void describeAggregateProperties( + css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps + ) const override; + + // prevent method hiding + using OControlModel::disposing; + using OControlModel::getFastPropertyValue; + + // OComponentHelper + virtual void SAL_CALL disposing() override; + + // IEngineTextChangeListener + virtual void potentialTextChange( ) override; + + private: + void implInit(); + void implDoAggregation(); + void implRegisterProperties(); + + /** propagates a new text to the EditEngine + + This method needs to lock the global solar mutex, so our own mutex must not + be locked when calling. + + @precond + our mutex is not locked + */ + void impl_smlock_setEngineText( const OUString& _rText ); + + DECL_LINK( OnEngineContentModified, LinkParamNone*, void ); + + private: + ORichTextModel( const ORichTextModel& ) = delete; + ORichTextModel& operator=( const ORichTextModel& ) = delete; + }; + + +} // namespace frm + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/richtext/richtextunowrapper.cxx b/forms/source/richtext/richtextunowrapper.cxx new file mode 100644 index 0000000000..f75b9a7943 --- /dev/null +++ b/forms/source/richtext/richtextunowrapper.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 "richtextunowrapper.hxx" + +#include <com/sun/star/lang/Locale.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <editeng/unofored.hxx> +#include <editeng/editview.hxx> +#include <editeng/unoipset.hxx> +#include <svx/svdpool.hxx> +#include <svx/svdobj.hxx> +#include <editeng/unoprnms.hxx> + + +namespace frm +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::container; + + + namespace + { + const SvxItemPropertySet* getTextEnginePropertySet() + { + // property map for an outliner text + static const SfxItemPropertyMapEntry aTextEnginePropertyMap[] = + { + SVX_UNOEDIT_CHAR_PROPERTIES, + SVX_UNOEDIT_FONT_PROPERTIES, + SVX_UNOEDIT_PARA_PROPERTIES, + { u"TextUserDefinedAttributes"_ustr, EE_CHAR_XMLATTRIBS, cppu::UnoType<XNameContainer>::get(), 0, 0 }, + { u"ParaUserDefinedAttributes"_ustr, EE_PARA_XMLATTRIBS, cppu::UnoType<XNameContainer>::get(), 0, 0 }, + }; + static SvxItemPropertySet aTextEnginePropertySet( aTextEnginePropertyMap, SdrObject::GetGlobalDrawObjectItemPool() ); + return &aTextEnginePropertySet; + } + } + + ORichTextUnoWrapper::ORichTextUnoWrapper( EditEngine& _rEngine, IEngineTextChangeListener* _pTextChangeListener ) + :SvxUnoText( getTextEnginePropertySet() ) + { + SetEditSource( new RichTextEditSource( _rEngine, _pTextChangeListener ) ); + } + + + ORichTextUnoWrapper::~ORichTextUnoWrapper() noexcept + { + } + + RichTextEditSource::RichTextEditSource( EditEngine& _rEngine, IEngineTextChangeListener* _pTextChangeListener ) + :m_rEngine ( _rEngine ) + ,m_pTextForwarder ( new SvxEditEngineForwarder( _rEngine ) ) + ,m_pTextChangeListener ( _pTextChangeListener ) + { + } + + + RichTextEditSource::~RichTextEditSource() + { + } + + + std::unique_ptr<SvxEditSource> RichTextEditSource::Clone() const + { + return std::unique_ptr<SvxEditSource>(new RichTextEditSource( m_rEngine, m_pTextChangeListener )); + } + + + SvxTextForwarder* RichTextEditSource::GetTextForwarder() + { + return m_pTextForwarder.get(); + } + + + void RichTextEditSource::UpdateData() + { + // this means that the content of the EditEngine changed via the UNO API + // to reflect this in the views, we need to update them + sal_uInt16 viewCount = m_rEngine.GetViewCount(); + for ( sal_uInt16 view = 0; view < viewCount; ++view ) + { + EditView* pView = m_rEngine.GetView( view ); + if ( pView ) + pView->ForceLayoutCalculation(); + } + + if ( m_pTextChangeListener ) + m_pTextChangeListener->potentialTextChange(); + } + + +} // namespace frm + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/richtext/richtextunowrapper.hxx b/forms/source/richtext/richtextunowrapper.hxx new file mode 100644 index 0000000000..0acb3c53d3 --- /dev/null +++ b/forms/source/richtext/richtextunowrapper.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 <memory> +#include <editeng/unotext.hxx> +#include <editeng/unoedsrc.hxx> + + +namespace frm +{ + + class IEngineTextChangeListener + { + public: + virtual void potentialTextChange( ) = 0; + + protected: + ~IEngineTextChangeListener() {} + }; + + class ORichTextUnoWrapper : public SvxUnoText + { + public: + ORichTextUnoWrapper( EditEngine& _rEngine, IEngineTextChangeListener* _pTextChangeListener ); + + protected: + virtual ~ORichTextUnoWrapper() noexcept override; + + + private: + ORichTextUnoWrapper( const ORichTextUnoWrapper& ) = delete; + ORichTextUnoWrapper& operator=( const ORichTextUnoWrapper& ) = delete; + }; + + class RichTextEditSource : public SvxEditSource + { + private: + EditEngine& m_rEngine; + std::unique_ptr<SvxTextForwarder> + m_pTextForwarder; + IEngineTextChangeListener* m_pTextChangeListener; + + public: + RichTextEditSource( EditEngine& _rEngine, IEngineTextChangeListener* _pTextChangeListener ); + + // SvxEditSource + virtual std::unique_ptr<SvxEditSource> Clone() const override; + virtual SvxTextForwarder* GetTextForwarder() override; + virtual void UpdateData() override; + + protected: + virtual ~RichTextEditSource() override; + + private: + RichTextEditSource( const RichTextEditSource& _rSource ) = delete; + RichTextEditSource& operator=( const RichTextEditSource& ) = delete; + }; + + +} // namespace frm + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/richtext/richtextvclcontrol.cxx b/forms/source/richtext/richtextvclcontrol.cxx new file mode 100644 index 0000000000..e4f1f0d1f7 --- /dev/null +++ b/forms/source/richtext/richtextvclcontrol.cxx @@ -0,0 +1,351 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "richtextvclcontrol.hxx" +#include "richtextimplcontrol.hxx" +#include <svl/itemset.hxx> +#if OSL_DEBUG_LEVEL > 0 + #include <unotools/ucbstreamhelper.hxx> + #include <sfx2/filedlghelper.hxx> + #include <tools/urlobj.hxx> + #include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#endif +#include <editeng/editeng.hxx> +#include <editeng/editview.hxx> +#include <editeng/editids.hrc> +#include <svx/svxids.hrc> +#include <osl/diagnose.h> +#include <vcl/event.hxx> + +namespace frm +{ + + RichTextControl::RichTextControl( RichTextEngine* _pEngine, vcl::Window* _pParent, WinBits _nStyle, + ITextAttributeListener* _pTextAttribListener, ITextSelectionListener* _pSelectionListener ) + :Control( _pParent, implInitStyle( _nStyle ) ) + { + implInit( _pEngine, _pTextAttribListener, _pSelectionListener ); + } + + void RichTextControl::implInit( RichTextEngine* _pEngine, ITextAttributeListener* _pTextAttribListener, ITextSelectionListener* _pSelectionListener ) + { + m_pImpl.reset( new RichTextControlImpl( this, _pEngine, _pTextAttribListener, _pSelectionListener ) ); + SetCompoundControl( true ); + } + + RichTextControl::~RichTextControl( ) + { + disposeOnce(); + } + + void RichTextControl::dispose() + { + m_pImpl.reset(); + Control::dispose(); + } + + + AttributeState RichTextControl::getState( AttributeId _nAttributeId ) const + { + return m_pImpl->getAttributeState( _nAttributeId ); + } + + + void RichTextControl::executeAttribute( AttributeId _nAttributeId, const SfxPoolItem* _pArgument ) + { + SfxItemSet aToApplyAttributes( getView().GetEmptyItemSet() ); + if ( !m_pImpl->executeAttribute( getView().GetAttribs(), aToApplyAttributes, _nAttributeId, _pArgument, m_pImpl->getSelectedScriptType() ) ) + { + OSL_FAIL( "RichTextControl::executeAttribute: cannot handle the given attribute!" ); + return; + } + + applyAttributes( aToApplyAttributes ); + } + + + void RichTextControl::applyAttributes( const SfxItemSet& _rAttributesToApply ) + { + // apply + if ( HasChildPathFocus() ) + getView().HideCursor(); + + // TODO: guard? + bool bOldUpdateMode = getEngine().SetUpdateLayout( false ); + + getView().SetAttribs( _rAttributesToApply ); + + getEngine().SetUpdateLayout( bOldUpdateMode ); + getView().Invalidate(); + + if ( HasChildPathFocus() ) + getView().ShowCursor(); + + m_pImpl->updateAllAttributes(); + // TODO: maybe we should have a list of attributes which need to be updated + // (the handler for the just executed attribute should know) + } + + + void RichTextControl::enableAttributeNotification( AttributeId _nAttributeId, ITextAttributeListener* _pListener ) + { + m_pImpl->enableAttributeNotification( _nAttributeId, _pListener ); + } + + + void RichTextControl::disableAttributeNotification( AttributeId _nAttributeId ) + { + m_pImpl->disableAttributeNotification( _nAttributeId ); + } + + + bool RichTextControl::isMappableSlot( SfxSlotId _nSlotId ) + { + switch ( _nSlotId ) + { + case SID_ATTR_PARA_ADJUST_LEFT: + case SID_ATTR_PARA_ADJUST_CENTER: + case SID_ATTR_PARA_ADJUST_RIGHT: + case SID_ATTR_PARA_ADJUST_BLOCK: + case SID_SET_SUPER_SCRIPT: + case SID_SET_SUB_SCRIPT: + case SID_ATTR_PARA_LINESPACE_10: + case SID_ATTR_PARA_LINESPACE_15: + case SID_ATTR_PARA_LINESPACE_20: + case SID_ATTR_PARA_LEFT_TO_RIGHT: + case SID_ATTR_PARA_RIGHT_TO_LEFT: + case SID_TEXTDIRECTION_TOP_TO_BOTTOM: + case SID_TEXTDIRECTION_LEFT_TO_RIGHT: + case SID_ATTR_CHAR_LATIN_FONT: + case SID_ATTR_CHAR_LATIN_FONTHEIGHT: + case SID_ATTR_CHAR_LATIN_LANGUAGE: + case SID_ATTR_CHAR_LATIN_POSTURE: + case SID_ATTR_CHAR_LATIN_WEIGHT: + return true; + } + return false; + } + + + void RichTextControl::Resize() + { + m_pImpl->layoutWindow(); + Invalidate(); + } + + + void RichTextControl::GetFocus() + { + m_pImpl->getViewport( RichTextControlImpl::GrantAccess() )->GrabFocus(); + } + + + WinBits RichTextControl::implInitStyle( WinBits nStyle ) + { + if ( !( nStyle & WB_NOTABSTOP ) ) + nStyle |= WB_TABSTOP; + return nStyle; + } + + + void RichTextControl::StateChanged( StateChangedType _nStateChange ) + { + if ( _nStateChange == StateChangedType::Style ) + { + SetStyle( implInitStyle( GetStyle() ) ); + m_pImpl->notifyStyleChanged(); + } + else if ( _nStateChange == StateChangedType::Zoom ) + { + m_pImpl->notifyZoomChanged(); + } + else if ( _nStateChange == StateChangedType::InitShow ) + { + m_pImpl->notifyInitShow(); + } + Control::StateChanged( _nStateChange ); + } + + + bool RichTextControl::PreNotify( NotifyEvent& _rNEvt ) + { + if ( IsWindowOrChild( _rNEvt.GetWindow() ) ) + { + if ( NotifyEventType::KEYINPUT == _rNEvt.GetType() ) + { + const ::KeyEvent* pKeyEvent = _rNEvt.GetKeyEvent(); + + sal_uInt16 nCode = pKeyEvent->GetKeyCode().GetCode(); + bool bShift = pKeyEvent->GetKeyCode().IsShift(); + bool bCtrl = pKeyEvent->GetKeyCode().IsMod1(); + bool bAlt = pKeyEvent->GetKeyCode().IsMod2(); + if ( ( KEY_TAB == nCode ) && bCtrl && !bAlt ) + { + // Ctrl-Tab is used to step out of the control + // -> build a new key event without the Ctrl-key, and let the very base class handle it + vcl::KeyCode aNewCode( KEY_TAB, bShift, false, false, false ); + ::KeyEvent aNewEvent( pKeyEvent->GetCharCode(), aNewCode ); + Control::KeyInput( aNewEvent ); + return true; // handled + } + +#if OSL_DEBUG_LEVEL > 0 + if ( ( ( KEY_F12 == nCode ) + || ( KEY_F11 == nCode ) + ) + && bCtrl + && bAlt + ) + { + bool bLoad = KEY_F11 == nCode; + static struct + { + const char* pDescription; + const char* pExtension; + EETextFormat eFormat; + } const aExportFormats[] = + { + { "OASIS OpenDocument (*.xml)", "*.xml", EETextFormat::Xml }, + { "HyperText Markup Language (*.html)", "*.html", EETextFormat::Html }, + { "Rich Text format (*.rtf)", "*.rtf", EETextFormat::Rtf }, + { "Text (*.txt)", "*.txt", EETextFormat::Text } + }; + + ::sfx2::FileDialogHelper aFP( bLoad ? css::ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE : css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION, FileDialogFlags::NONE, GetFrameWeld() ); + + for (auto & aExportFormat : aExportFormats) + { + aFP.AddFilter( + OUString::createFromAscii( aExportFormat.pDescription ), + OUString::createFromAscii( aExportFormat.pExtension ) ); + } + ErrCode nResult = aFP.Execute(); + if ( nResult == ERRCODE_NONE ) + { + OUString sFileName = aFP.GetPath(); + std::unique_ptr<SvStream> pStream = ::utl::UcbStreamHelper::CreateStream( + sFileName, ( bLoad ? StreamMode::READ : StreamMode::WRITE | StreamMode::TRUNC ) | StreamMode::SHARE_DENYALL + ); + if ( pStream ) + { + EETextFormat eFormat = EETextFormat::Xml; + OUString sFilter = aFP.GetCurrentFilter(); + for (auto & aExportFormat : aExportFormats) + { + if ( sFilter.equalsAscii( aExportFormat.pDescription ) ) + { + eFormat = aExportFormat.eFormat; + break; + } + } + if ( bLoad ) + { + INetURLObject aURL( sFileName ); + aURL.removeSegment(); + getEngine().Read( *pStream, aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), eFormat ); + } + else + { + getEngine().Write( *pStream, eFormat ); + } + } + } + return true; // handled + } +#endif + } + } + return Control::PreNotify( _rNEvt ); + } + + + bool RichTextControl::EventNotify( NotifyEvent& _rNEvt ) + { + bool bDone = false; + if ( _rNEvt.GetType() == NotifyEventType::COMMAND ) + { + const CommandEvent& rEvent = *_rNEvt.GetCommandEvent(); + bDone = m_pImpl->HandleCommand( rEvent ); + } + return bDone || Control::EventNotify(_rNEvt); + } + + void RichTextControl::Draw( OutputDevice* _pDev, const Point& _rPos, SystemTextColorFlags /*_nFlags*/ ) + { + m_pImpl->Draw( _pDev, _rPos, _pDev->PixelToLogic(GetSizePixel()) ); + } + + EditView& RichTextControl::getView() + { + return *m_pImpl->getView( RichTextControlImpl::GrantAccess() ); + } + + + const EditView& RichTextControl::getView() const + { + return *m_pImpl->getView( RichTextControlImpl::GrantAccess() ); + } + + + EditEngine& RichTextControl::getEngine() const + { + return *m_pImpl->getEngine( RichTextControlImpl::GrantAccess() ); + } + + + void RichTextControl::SetReadOnly( bool _bReadOnly ) + { + m_pImpl->SetReadOnly( _bReadOnly ); + } + + + bool RichTextControl::IsReadOnly() const + { + return m_pImpl->IsReadOnly(); + } + + + void RichTextControl::SetBackgroundColor( ) + { + m_pImpl->SetBackgroundColor( ); + } + + + void RichTextControl::SetBackgroundColor( const Color& _rColor ) + { + m_pImpl->SetBackgroundColor( _rColor ); + } + + + void RichTextControl::SetHideInactiveSelection( bool _bHide ) + { + m_pImpl->SetHideInactiveSelection( _bHide ); + } + + + bool RichTextControl::GetHideInactiveSelection() const + { + return m_pImpl->GetHideInactiveSelection( ); + } + + +} // namespace frm + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/richtext/richtextvclcontrol.hxx b/forms/source/richtext/richtextvclcontrol.hxx new file mode 100644 index 0000000000..92d0c047d7 --- /dev/null +++ b/forms/source/richtext/richtextvclcontrol.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 <vcl/ctrl.hxx> +#include "rtattributes.hxx" +#include "textattributelistener.hxx" +#include <memory> + +class EditView; +class EditEngine; +class SfxItemSet; + +namespace frm +{ + + class RichTextControlImpl; + class RichTextEngine; + + class RichTextControl : public Control, public IMultiAttributeDispatcher + { + private: + std::unique_ptr<RichTextControlImpl> m_pImpl; + + public: + RichTextControl( + RichTextEngine* _pEngine, + vcl::Window* _pParent, + WinBits _nStyle, + ITextAttributeListener* _pTextAttribListener, + ITextSelectionListener* _pSelectionListener + ); + + virtual ~RichTextControl( ) override; + virtual void dispose() override; + + /* enables the change notifications for a particular attribute + + If you want to be notified of any changes in the state of an attribute, you need to call enableAttributeNotification. + + If you provide a dedicated listener for this attribute, this listener is called for every change in the state of + the attribute. + + No matter whether you provide such a dedicated listener, the "global" listener which you specified + in the constructor of the control is also called for all changes in the attribute state. + + If you previously already enabled the notification for this attribute, and specified a different listener, + then the previous listener will be replaced with the new listener, provided the latter is not <NULL/>. + */ + void enableAttributeNotification( AttributeId _nAttributeId, ITextAttributeListener* _pListener ); + + /** disables the change notifications for a particular attribute + + If there was a listener dedicated to this attribute, it will not be referenced and used anymore + after this method had been called + */ + void disableAttributeNotification( AttributeId _nAttributeId ); + + /** determines whether a given slot can be mapped to an aspect of an attribute of the EditEngine + + E.g. SID_ATTR_PARA_ADJUST_LEFT can, though it's not part of the EditEngine pool, be mapped + to the SID_ATTR_PARA_ADJUST slot, which in fact *is* usable with the EditEngine. + */ + static bool isMappableSlot( SfxSlotId _nSlotId ); + + // IMultiAttributeDispatcher + virtual AttributeState getState( AttributeId _nAttributeId ) const override; + virtual void executeAttribute( AttributeId _nAttributeId, const SfxPoolItem* _pArgument ) override; + + void SetBackgroundColor( ); + void SetBackgroundColor( const Color& _rColor ); + + void SetReadOnly( bool _bReadOnly ); + bool IsReadOnly() const; + + void SetHideInactiveSelection( bool _bHide ); + bool GetHideInactiveSelection() const; + + const EditView& getView() const; + EditView& getView(); + + // Window overridables + virtual void Draw( OutputDevice* _pDev, const Point& _rPos, SystemTextColorFlags _nFlags ) override; + + protected: + // Window overridables + virtual void Resize() override; + virtual void GetFocus() override; + virtual void StateChanged( StateChangedType nStateChange ) override; + virtual bool PreNotify( NotifyEvent& _rNEvt ) override; + virtual bool EventNotify( NotifyEvent& _rNEvt ) override; + + private: + void applyAttributes( const SfxItemSet& _rAttributesToApply ); + void implInit( RichTextEngine* _pEngine, ITextAttributeListener* _pTextAttribListener, ITextSelectionListener* _pSelectionListener ); + static WinBits implInitStyle( WinBits nStyle ); + + private: + EditEngine& getEngine() const; + }; + + +} // namespace frm + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/richtext/richtextviewport.cxx b/forms/source/richtext/richtextviewport.cxx new file mode 100644 index 0000000000..b4fda6652c --- /dev/null +++ b/forms/source/richtext/richtextviewport.cxx @@ -0,0 +1,102 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "richtextviewport.hxx" +#include <editeng/editview.hxx> + +namespace frm +{ + RichTextViewPort::RichTextViewPort( vcl::Window* _pParent ) + : Control ( _pParent ) + , m_pView(nullptr) + , m_bHideInactiveSelection( true ) + { + } + + void RichTextViewPort::setView( EditView& _rView ) + { + m_pView = &_rView; + SetPointer( _rView.GetPointer() ); + } + + void RichTextViewPort::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& _rRect ) + { + m_pView->Paint(_rRect, &rRenderContext); + } + + void RichTextViewPort::GetFocus() + { + Control::GetFocus(); + if (m_pView) + { + m_pView->SetSelectionMode( EESelectionMode::Std ); + m_pView->ShowCursor(); + } + } + + void RichTextViewPort::LoseFocus() + { + if (m_pView) + { + m_pView->HideCursor(); + m_pView->SetSelectionMode( m_bHideInactiveSelection ? EESelectionMode::Hidden : EESelectionMode::Std ); + } + Control::LoseFocus(); + } + + void RichTextViewPort::KeyInput( const KeyEvent& _rKEvt ) + { + if ( !m_pView->PostKeyEvent( _rKEvt ) ) + Control::KeyInput( _rKEvt ); + else + implInvalidateAttributes(); + } + + void RichTextViewPort::MouseMove( const MouseEvent& _rMEvt ) + { + Control::MouseMove( _rMEvt ); + m_pView->MouseMove( _rMEvt ); + } + + void RichTextViewPort::MouseButtonDown( const MouseEvent& _rMEvt ) + { + Control::MouseButtonDown( _rMEvt ); + m_pView->MouseButtonDown( _rMEvt ); + GrabFocus(); + } + + void RichTextViewPort::MouseButtonUp( const MouseEvent& _rMEvt ) + { + Control::MouseButtonUp( _rMEvt ); + m_pView->MouseButtonUp( _rMEvt ); + implInvalidateAttributes(); + } + + void RichTextViewPort::SetHideInactiveSelection( bool _bHide ) + { + if ( m_bHideInactiveSelection == _bHide ) + return; + m_bHideInactiveSelection = _bHide; + if ( !HasFocus() ) + m_pView->SetSelectionMode( m_bHideInactiveSelection ? EESelectionMode::Hidden : EESelectionMode::Std ); + } + +} // namespace frm + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/richtext/richtextviewport.hxx b/forms/source/richtext/richtextviewport.hxx new file mode 100644 index 0000000000..90df8cb0b4 --- /dev/null +++ b/forms/source/richtext/richtextviewport.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 <vcl/ctrl.hxx> + +class EditView; + +namespace frm +{ + class RichTextViewPort : public Control + { + private: + EditView* m_pView; + Link<LinkParamNone*,void> m_aInvalidationHandler; + bool m_bHideInactiveSelection; + + public: + explicit RichTextViewPort( vcl::Window* _pParent ); + + void setView( EditView& _rView ); + + void setAttributeInvalidationHandler( const Link<LinkParamNone*,void>& _rHandler ) { m_aInvalidationHandler = _rHandler; } + + void SetHideInactiveSelection( bool _bHide ); + bool GetHideInactiveSelection() const { return m_bHideInactiveSelection; } + + protected: + virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) override; + virtual void GetFocus() override; + virtual void LoseFocus() override; + virtual void KeyInput( const KeyEvent& _rKEvt ) override; + virtual void MouseMove( const MouseEvent& _rMEvt ) override; + virtual void MouseButtonDown( const MouseEvent& _rMEvt ) override; + virtual void MouseButtonUp( const MouseEvent& _rMEvt ) override; + + private: + void implInvalidateAttributes() const + { + m_aInvalidationHandler.Call( nullptr ); + } + }; + + +} // namespace frm + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/richtext/rtattributehandler.cxx b/forms/source/richtext/rtattributehandler.cxx new file mode 100644 index 0000000000..19ae60dc89 --- /dev/null +++ b/forms/source/richtext/rtattributehandler.cxx @@ -0,0 +1,441 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "rtattributehandler.hxx" + +#include <osl/diagnose.h> +#include <svx/svxids.hrc> +#include <editeng/eeitem.hxx> +#include <svl/itemset.hxx> +#include <svl/itempool.hxx> +#include <tools/mapunit.hxx> +#include <vcl/mapmod.hxx> +#include <vcl/outdev.hxx> + +#include <editeng/adjustitem.hxx> +#include <editeng/escapementitem.hxx> +#include <editeng/lspcitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/frmdiritem.hxx> +#include <editeng/scripttypeitem.hxx> + + +namespace frm +{ + + + AttributeHandler::AttributeHandler( AttributeId _nAttributeId, WhichId _nWhichId ) + :m_nAttribute( _nAttributeId ) + ,m_nWhich ( _nWhichId ) + { + } + + + AttributeHandler::~AttributeHandler() + { + } + + + AttributeId AttributeHandler::getAttributeId( ) const + { + return getAttribute(); + } + + + AttributeCheckState AttributeHandler::implGetCheckState( const SfxPoolItem& /*_rItem*/ ) const + { + OSL_FAIL( "AttributeHandler::implGetCheckState: not to be called!" ); + return eIndetermined; + } + + + void AttributeHandler::putItemForScript( SfxItemSet& _rAttribs, const SfxPoolItem& _rItem, SvtScriptType _nForScriptType ) const + { + SvxScriptSetItem aSetItem( static_cast<WhichId>(getAttributeId()), *_rAttribs.GetPool() ); + aSetItem.PutItemForScriptType( _nForScriptType, _rItem ); + _rAttribs.Put( aSetItem.GetItemSet(), false ); + } + + + AttributeCheckState AttributeHandler::getCheckState( const SfxItemSet& _rAttribs ) const + { + AttributeCheckState eSimpleState( eIndetermined ); + const SfxPoolItem* pItem = _rAttribs.GetItem( getWhich() ); + if ( pItem ) + eSimpleState = implGetCheckState( *pItem ); + return eSimpleState; + } + + + AttributeState AttributeHandler::getState( const SfxItemSet& _rAttribs ) const + { + AttributeState aState( eIndetermined ); + aState.eSimpleState = getCheckState( _rAttribs ); + return aState; + } + + + //= AttributeHandlerFactory + + + namespace + { + WhichId lcl_implGetWhich( const SfxItemPool& _rPool, AttributeId _nAttributeId ) + { + WhichId nWhich = 0; + switch ( _nAttributeId ) + { + case SID_ATTR_CHAR_LATIN_FONTHEIGHT:nWhich = EE_CHAR_FONTHEIGHT;break; + case SID_ATTR_CHAR_LATIN_FONT: nWhich = EE_CHAR_FONTINFO; break; + case SID_ATTR_CHAR_LATIN_LANGUAGE: nWhich = EE_CHAR_LANGUAGE; break; + case SID_ATTR_CHAR_LATIN_POSTURE: nWhich = EE_CHAR_ITALIC; break; + case SID_ATTR_CHAR_LATIN_WEIGHT: nWhich = EE_CHAR_WEIGHT; break; + + default: + nWhich = _rPool.GetWhich( static_cast<SfxSlotId>(_nAttributeId) ); + } + return nWhich; + } + } + + ::rtl::Reference< AttributeHandler > AttributeHandlerFactory::getHandlerFor( AttributeId _nAttributeId, const SfxItemPool& _rEditEnginePool ) + { + ::rtl::Reference< AttributeHandler > pReturn; + switch ( _nAttributeId ) + { + case SID_ATTR_PARA_ADJUST_LEFT : + case SID_ATTR_PARA_ADJUST_CENTER: + case SID_ATTR_PARA_ADJUST_RIGHT : + case SID_ATTR_PARA_ADJUST_BLOCK : + pReturn = new ParaAlignmentHandler( _nAttributeId ); + break; + + case SID_ATTR_PARA_LINESPACE_10: + case SID_ATTR_PARA_LINESPACE_15: + case SID_ATTR_PARA_LINESPACE_20: + pReturn = new LineSpacingHandler( _nAttributeId ); + break; + + case SID_SET_SUPER_SCRIPT: + case SID_SET_SUB_SCRIPT: + pReturn = new EscapementHandler( _nAttributeId ); + break; + + case SID_ATTR_CHAR_FONTHEIGHT: + case SID_ATTR_CHAR_CTL_FONTHEIGHT: + case SID_ATTR_CHAR_CJK_FONTHEIGHT: + case SID_ATTR_CHAR_LATIN_FONTHEIGHT: + pReturn = new FontSizeHandler( _nAttributeId, lcl_implGetWhich( _rEditEnginePool, _nAttributeId ) ); + break; + + case SID_ATTR_PARA_LEFT_TO_RIGHT: + case SID_ATTR_PARA_RIGHT_TO_LEFT: + pReturn = new ParagraphDirectionHandler( _nAttributeId ); + break; + + case SID_ATTR_PARA_HANGPUNCTUATION: + case SID_ATTR_PARA_FORBIDDEN_RULES: + case SID_ATTR_PARA_SCRIPTSPACE: + pReturn = new BooleanHandler( _nAttributeId, lcl_implGetWhich( _rEditEnginePool, _nAttributeId ) ); + break; + + default: + pReturn = new SlotHandler( static_cast<SfxSlotId>(_nAttributeId), lcl_implGetWhich( _rEditEnginePool, _nAttributeId ) ); + break; + + } + + return pReturn; + } + + ParaAlignmentHandler::ParaAlignmentHandler( AttributeId _nAttributeId ) + :AttributeHandler( _nAttributeId, EE_PARA_JUST ) + ,m_eAdjust( SvxAdjust::Center ) + { + switch ( getAttribute() ) + { + case SID_ATTR_PARA_ADJUST_LEFT : m_eAdjust = SvxAdjust::Left; break; + case SID_ATTR_PARA_ADJUST_CENTER: m_eAdjust = SvxAdjust::Center; break; + case SID_ATTR_PARA_ADJUST_RIGHT : m_eAdjust = SvxAdjust::Right; break; + case SID_ATTR_PARA_ADJUST_BLOCK : m_eAdjust = SvxAdjust::Block; break; + default: + OSL_FAIL( "ParaAlignmentHandler::ParaAlignmentHandler: invalid slot!" ); + break; + } + } + + + AttributeCheckState ParaAlignmentHandler::implGetCheckState( const SfxPoolItem& _rItem ) const + { + assert( dynamic_cast<const SvxAdjustItem*>( &_rItem) && "ParaAlignmentHandler::implGetCheckState: invalid pool item!" ); + SvxAdjust eAdjust = static_cast< const SvxAdjustItem& >( _rItem ).GetAdjust(); + return ( eAdjust == m_eAdjust ) ? eChecked : eUnchecked; + } + + + void ParaAlignmentHandler::executeAttribute( const SfxItemSet& /*_rCurrentAttribs*/, SfxItemSet& _rNewAttribs, const SfxPoolItem* _pAdditionalArg, SvtScriptType /*_nForScriptType*/ ) const + { + OSL_ENSURE( !_pAdditionalArg, "ParaAlignmentHandler::executeAttribute: this is a simple toggle attribute - no args possible!" ); + _rNewAttribs.Put( SvxAdjustItem( m_eAdjust, getWhich() ) ); + } + + LineSpacingHandler::LineSpacingHandler( AttributeId _nAttributeId ) + :AttributeHandler( _nAttributeId, EE_PARA_SBL ) + ,m_nLineSpace( 100 ) + { + switch ( getAttribute() ) + { + case SID_ATTR_PARA_LINESPACE_10: m_nLineSpace = 100; break; + case SID_ATTR_PARA_LINESPACE_15: m_nLineSpace = 150; break; + case SID_ATTR_PARA_LINESPACE_20: m_nLineSpace = 200; break; + default: + OSL_FAIL( "LineSpacingHandler::LineSpacingHandler: invalid slot!" ); + break; + } + } + + + AttributeCheckState LineSpacingHandler::implGetCheckState( const SfxPoolItem& _rItem ) const + { + assert( dynamic_cast<const SvxLineSpacingItem*>( &_rItem) && "LineSpacingHandler::implGetCheckState: invalid pool item!" ); + sal_uInt16 nLineSpace = static_cast< const SvxLineSpacingItem& >( _rItem ).GetPropLineSpace(); + return ( nLineSpace == m_nLineSpace ) ? eChecked : eUnchecked; + } + + + void LineSpacingHandler::executeAttribute( const SfxItemSet& /*_rCurrentAttribs*/, SfxItemSet& _rNewAttribs, const SfxPoolItem* _pAdditionalArg, SvtScriptType /*_nForScriptType*/ ) const + { + OSL_ENSURE( !_pAdditionalArg, "LineSpacingHandler::executeAttribute: this is a simple toggle attribute - no args possible!" ); + + SvxLineSpacingItem aLineSpacing( m_nLineSpace, getWhich() ); + aLineSpacing.SetLineSpaceRule( SvxLineSpaceRule::Auto ); + if ( 100 == m_nLineSpace ) + aLineSpacing.SetInterLineSpaceRule( SvxInterLineSpaceRule::Off ); + else + aLineSpacing.SetPropLineSpace( m_nLineSpace ); + + _rNewAttribs.Put( aLineSpacing ); + } + + EscapementHandler::EscapementHandler( AttributeId _nAttributeId ) + :AttributeHandler( _nAttributeId, EE_CHAR_ESCAPEMENT ) + ,m_eEscapement( SvxEscapement::Off ) + { + switch ( getAttribute() ) + { + case SID_SET_SUPER_SCRIPT : m_eEscapement = SvxEscapement::Superscript; break; + case SID_SET_SUB_SCRIPT : m_eEscapement = SvxEscapement::Subscript; break; + default: + OSL_FAIL( "EscapementHandler::EscapementHandler: invalid slot!" ); + break; + } + } + + + AttributeCheckState EscapementHandler::implGetCheckState( const SfxPoolItem& _rItem ) const + { + assert( dynamic_cast<const SvxEscapementItem*>( &_rItem) && "EscapementHandler::getState: invalid pool item!" ); + SvxEscapement eEscapement = static_cast< const SvxEscapementItem& >( _rItem ).GetEscapement(); + return ( eEscapement == m_eEscapement ) ? eChecked : eUnchecked; + } + + + void EscapementHandler::executeAttribute( const SfxItemSet& _rCurrentAttribs, SfxItemSet& _rNewAttribs, const SfxPoolItem* _pAdditionalArg, SvtScriptType /*_nForScriptType*/ ) const { + OSL_ENSURE( !_pAdditionalArg, "EscapementHandler::executeAttribute: this is a simple toggle attribute - no args possible!" ); + // well, in theory we could allow an SvxEscapementItem here, but this is not needed + + bool bIsChecked = getCheckState( _rCurrentAttribs ) == eChecked; + _rNewAttribs.Put( SvxEscapementItem( bIsChecked ? SvxEscapement::Off : m_eEscapement, getWhich() ) ); + } + + SlotHandler::SlotHandler( AttributeId _nAttributeId, WhichId _nWhichId ) + :AttributeHandler( _nAttributeId, _nWhichId ) + ,m_bScriptDependent( false ) + { + m_bScriptDependent = ( sal_uInt16(SID_ATTR_CHAR_WEIGHT) == _nAttributeId ) + || ( sal_uInt16(SID_ATTR_CHAR_POSTURE) == _nAttributeId ) + || ( sal_uInt16(SID_ATTR_CHAR_FONT) == _nAttributeId ); + } + + + AttributeState SlotHandler::getState( const SfxItemSet& _rAttribs ) const + { + AttributeState aState( eIndetermined ); + + const SfxPoolItem* pItem = _rAttribs.GetItem( getWhich() ); + if ( pItem ) + aState.setItem( pItem ); + + return aState; + } + + + void SlotHandler::executeAttribute( const SfxItemSet& /*_rCurrentAttribs*/, SfxItemSet& _rNewAttribs, const SfxPoolItem* _pAdditionalArg, SvtScriptType _nForScriptType ) const + { + if ( _pAdditionalArg ) + { + std::unique_ptr<SfxPoolItem> pCorrectWich(_pAdditionalArg->CloneSetWhich(getWhich())); + + if ( m_bScriptDependent ) + putItemForScript( _rNewAttribs, *pCorrectWich, _nForScriptType ); + else + _rNewAttribs.Put( std::move(pCorrectWich) ); + } + else + OSL_FAIL( "SlotHandler::executeAttribute: need attributes to do something!" ); + } + + FontSizeHandler::FontSizeHandler( AttributeId _nAttributeId, WhichId _nWhichId ) + :AttributeHandler( _nAttributeId, _nWhichId ) + { + OSL_ENSURE( ( _nAttributeId == sal_uInt16(SID_ATTR_CHAR_FONTHEIGHT) ) || ( _nAttributeId == sal_uInt16(SID_ATTR_CHAR_CTL_FONTHEIGHT) ) + || ( _nAttributeId == sal_uInt16(SID_ATTR_CHAR_CJK_FONTHEIGHT) ) || ( _nAttributeId == sal_uInt16(SID_ATTR_CHAR_LATIN_FONTHEIGHT) ), + "FontSizeHandler::FontSizeHandler: invalid attribute id!" ); + } + + + AttributeState FontSizeHandler::getState( const SfxItemSet& _rAttribs ) const + { + AttributeState aState( eIndetermined ); + + const SfxPoolItem* pItem = _rAttribs.GetItem( getWhich() ); + const SvxFontHeightItem* pFontHeightItem = dynamic_cast<const SvxFontHeightItem*>( pItem ); + OSL_ENSURE( pFontHeightItem || !pItem, "FontSizeHandler::getState: invalid item!" ); + if ( pFontHeightItem ) + { + // by definition, the item should have the unit twip + sal_uInt32 nHeight = pFontHeightItem->GetHeight(); + if ( _rAttribs.GetPool()->GetMetric( getWhich() ) != MapUnit::MapTwip ) + { + nHeight = OutputDevice::LogicToLogic( + Size( 0, nHeight ), + MapMode( _rAttribs.GetPool()->GetMetric( getWhich() ) ), + MapMode( MapUnit::MapTwip ) + ).Height(); + } + + SvxFontHeightItem* pNewItem = new SvxFontHeightItem( nHeight, 100, getWhich() ); + pNewItem->SetProp( pFontHeightItem->GetProp(), pFontHeightItem->GetPropUnit() ); + aState.setItem( pNewItem ); + } + + return aState; + } + + + void FontSizeHandler::executeAttribute( const SfxItemSet& /*_rCurrentAttribs*/, SfxItemSet& _rNewAttribs, const SfxPoolItem* _pAdditionalArg, SvtScriptType _nForScriptType ) const + { + const SvxFontHeightItem* pFontHeightItem = dynamic_cast<const SvxFontHeightItem*>( _pAdditionalArg ); + OSL_ENSURE( pFontHeightItem, "FontSizeHandler::executeAttribute: need a FontHeightItem!" ); + + if ( !pFontHeightItem ) + return; + + sal_uInt32 nHeight = pFontHeightItem->GetHeight(); + if ( _rNewAttribs.GetPool()->GetMetric( getWhich() ) != MapUnit::MapTwip ) + { + nHeight = OutputDevice::LogicToLogic( + Size( 0, nHeight ), + MapMode( MapUnit::MapTwip ), + MapMode( _rNewAttribs.GetPool()->GetMetric( getWhich() ) ) + ).Height(); + } + + SvxFontHeightItem aNewItem( nHeight, 100, getWhich() ); + aNewItem.SetProp( pFontHeightItem->GetProp(), pFontHeightItem->GetPropUnit() ); + + if ( ( getAttributeId() == sal_uInt16(SID_ATTR_CHAR_FONTHEIGHT) ) && _nForScriptType != SvtScriptType::NONE) + putItemForScript( _rNewAttribs, aNewItem, _nForScriptType ); + else + _rNewAttribs.Put( aNewItem ); + } + + ParagraphDirectionHandler::ParagraphDirectionHandler( AttributeId _nAttributeId ) + :AttributeHandler( _nAttributeId, EE_PARA_WRITINGDIR ) + ,m_eParagraphDirection( SvxFrameDirection::Horizontal_LR_TB ) + ,m_eDefaultAdjustment( SvxAdjust::Right ) + ,m_eOppositeDefaultAdjustment( SvxAdjust::Left ) + { + switch ( getAttributeId() ) + { + case SID_ATTR_PARA_LEFT_TO_RIGHT: m_eParagraphDirection = SvxFrameDirection::Horizontal_LR_TB; m_eDefaultAdjustment = SvxAdjust::Left; break; + case SID_ATTR_PARA_RIGHT_TO_LEFT: m_eParagraphDirection = SvxFrameDirection::Horizontal_RL_TB; m_eDefaultAdjustment = SvxAdjust::Right; break; + default: + OSL_FAIL( "ParagraphDirectionHandler::ParagraphDirectionHandler: invalid attribute id!" ); + } + + if ( SvxAdjust::Right == m_eDefaultAdjustment ) + m_eOppositeDefaultAdjustment = SvxAdjust::Left; + else + m_eOppositeDefaultAdjustment = SvxAdjust::Right; + } + + + AttributeCheckState ParagraphDirectionHandler::implGetCheckState( const SfxPoolItem& _rItem ) const + { + assert( dynamic_cast<const SvxFrameDirectionItem*>( &_rItem) && "ParagraphDirectionHandler::implGetCheckState: invalid pool item!" ); + SvxFrameDirection eDirection = static_cast< const SvxFrameDirectionItem& >( _rItem ).GetValue(); + return ( eDirection == m_eParagraphDirection ) ? eChecked : eUnchecked; + } + + + void ParagraphDirectionHandler::executeAttribute( const SfxItemSet& _rCurrentAttribs, SfxItemSet& _rNewAttribs, const SfxPoolItem* /*_pAdditionalArg*/, SvtScriptType /*_nForScriptType*/ ) const + { + _rNewAttribs.Put( SvxFrameDirectionItem( m_eParagraphDirection, getWhich() ) ); + + // if the current adjustment of the was the default adjustment for the *previous* text direction, + // then we toggle the adjustment, too + SvxAdjust eCurrentAdjustment = SvxAdjust::Left; + if ( const SvxAdjustItem* pCurrentAdjustment = _rCurrentAttribs.GetItemIfSet( EE_PARA_JUST ) ) + eCurrentAdjustment = pCurrentAdjustment->GetAdjust(); + + if ( eCurrentAdjustment == m_eOppositeDefaultAdjustment ) + _rNewAttribs.Put( SvxAdjustItem( m_eDefaultAdjustment, EE_PARA_JUST ) ); + } + + BooleanHandler::BooleanHandler( AttributeId _nAttributeId, WhichId _nWhichId ) + :AttributeHandler( _nAttributeId, _nWhichId ) + { + } + + + AttributeCheckState BooleanHandler::implGetCheckState( const SfxPoolItem& _rItem ) const + { + OSL_ENSURE( dynamic_cast<const SfxBoolItem*>( &_rItem) != nullptr, "BooleanHandler::implGetCheckState: invalid item!" ); + if ( auto pBoolItem = dynamic_cast<const SfxBoolItem*>( &_rItem) ) + return pBoolItem->GetValue() ? eChecked : eUnchecked; + + return eIndetermined; + } + + + void BooleanHandler::executeAttribute( const SfxItemSet& /*_rCurrentAttribs*/, SfxItemSet& _rNewAttribs, const SfxPoolItem* _pAdditionalArg, SvtScriptType /*_nForScriptType*/ ) const + { + OSL_ENSURE( dynamic_cast<const SfxBoolItem*>( _pAdditionalArg) != nullptr, "BooleanHandler::executeAttribute: invalid argument!" ); + if ( _pAdditionalArg ) + { + _rNewAttribs.Put( _pAdditionalArg->CloneSetWhich(getWhich()) ); + } + } + + +} // namespace frm + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/richtext/rtattributehandler.hxx b/forms/source/richtext/rtattributehandler.hxx new file mode 100644 index 0000000000..0f2badd640 --- /dev/null +++ b/forms/source/richtext/rtattributehandler.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 "rtattributes.hxx" +#include <rtl/ref.hxx> +#include <svl/languageoptions.hxx> +#include <editeng/svxenum.hxx> +#include <editeng/frmdir.hxx> +#include <salhelper/simplereferenceobject.hxx> + +class SfxItemSet; +class SfxPoolItem; +class SfxItemPool; + +namespace frm +{ + + class AttributeHandler : public salhelper::SimpleReferenceObject + { + private: + AttributeId m_nAttribute; + WhichId m_nWhich; + + protected: + AttributeId getAttribute() const { return m_nAttribute; } + WhichId getWhich() const { return m_nWhich; } + + public: + AttributeHandler( AttributeId _nAttributeId, WhichId _nWhichId ); + + AttributeId getAttributeId( ) const; + virtual AttributeState getState( const SfxItemSet& _rAttribs ) const; + virtual void executeAttribute( const SfxItemSet& _rCurrentAttribs, SfxItemSet& _rNewAttribs, const SfxPoolItem* _pAdditionalArg, SvtScriptType _nForScriptType ) const = 0; + + protected: + /// helper method calling implGetCheckState + AttributeCheckState getCheckState( const SfxItemSet& _rAttribs ) const; + + /// helper method putting an item into a set, respecting a script type + void putItemForScript( SfxItemSet& _rAttribs, const SfxPoolItem& _rItem, SvtScriptType _nForScriptType ) const; + + // pseudo-abstract + virtual AttributeCheckState implGetCheckState( const SfxPoolItem& _rItem ) const; + + protected: + virtual ~AttributeHandler() override; + }; + + namespace AttributeHandlerFactory + { + ::rtl::Reference< AttributeHandler > getHandlerFor( AttributeId _nAttributeId, const SfxItemPool& _rEditEnginePool ); + } + + class ParaAlignmentHandler : public AttributeHandler + { + private: + SvxAdjust m_eAdjust; + + public: + explicit ParaAlignmentHandler( AttributeId _nAttributeId ); + + public: + virtual AttributeCheckState implGetCheckState( const SfxPoolItem& _rItem ) const override; + virtual void executeAttribute( const SfxItemSet& _rCurrentAttribs, SfxItemSet& _rNewAttribs, const SfxPoolItem* _pAdditionalArg, SvtScriptType _nForScriptType ) const override; + }; + + class LineSpacingHandler : public AttributeHandler + { + private: + sal_uInt16 m_nLineSpace; + + public: + explicit LineSpacingHandler( AttributeId _nAttributeId ); + + public: + virtual AttributeCheckState implGetCheckState( const SfxPoolItem& _rItem ) const override; + virtual void executeAttribute( const SfxItemSet& _rCurrentAttribs, SfxItemSet& _rNewAttribs, const SfxPoolItem* _pAdditionalArg, SvtScriptType _nForScriptType ) const override; + }; + + class EscapementHandler : public AttributeHandler + { + private: + SvxEscapement m_eEscapement; + + public: + explicit EscapementHandler( AttributeId _nAttributeId ); + + public: + virtual AttributeCheckState implGetCheckState( const SfxPoolItem& _rItem ) const override; + virtual void executeAttribute( const SfxItemSet& _rCurrentAttribs, SfxItemSet& _rNewAttribs, const SfxPoolItem* _pAdditionalArg, SvtScriptType _nForScriptType ) const override; + }; + + class SlotHandler : public AttributeHandler + { + private: + bool m_bScriptDependent; + + public: + SlotHandler( AttributeId _nAttributeId, WhichId _nWhichId ); + + public: + virtual AttributeState getState( const SfxItemSet& _rAttribs ) const override; + virtual void executeAttribute( const SfxItemSet& _rCurrentAttribs, SfxItemSet& _rNewAttribs, const SfxPoolItem* _pAdditionalArg, SvtScriptType _nForScriptType ) const override; + }; + + class BooleanHandler : public AttributeHandler + { + public: + BooleanHandler( AttributeId _nAttributeId, WhichId _nWhichId ); + + public: + virtual AttributeCheckState implGetCheckState( const SfxPoolItem& _rItem ) const override; + virtual void executeAttribute( const SfxItemSet& _rCurrentAttribs, SfxItemSet& _rNewAttribs, const SfxPoolItem* _pAdditionalArg, SvtScriptType _nForScriptType ) const override; + }; + + class FontSizeHandler : public AttributeHandler + { + public: + FontSizeHandler( AttributeId _nAttributeId, WhichId _nWhichId ); + + public: + virtual AttributeState getState( const SfxItemSet& _rAttribs ) const override; + virtual void executeAttribute( const SfxItemSet& _rCurrentAttribs, SfxItemSet& _rNewAttribs, const SfxPoolItem* _pAdditionalArg, SvtScriptType _nForScriptType ) const override; + }; + + class ParagraphDirectionHandler : public AttributeHandler + { + private: + SvxFrameDirection m_eParagraphDirection; + SvxAdjust m_eDefaultAdjustment; + SvxAdjust m_eOppositeDefaultAdjustment; + + public: + explicit ParagraphDirectionHandler( AttributeId _nAttributeId ); + + public: + virtual AttributeCheckState implGetCheckState( const SfxPoolItem& _rItem ) const override; + virtual void executeAttribute( const SfxItemSet& _rCurrentAttribs, SfxItemSet& _rNewAttribs, const SfxPoolItem* _pAdditionalArg, SvtScriptType _nForScriptType ) const override; + }; + + +} // namespace frm + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/richtext/rtattributes.hxx b/forms/source/richtext/rtattributes.hxx new file mode 100644 index 0000000000..fa00add450 --- /dev/null +++ b/forms/source/richtext/rtattributes.hxx @@ -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 . + */ + +#pragma once + +#include <sal/types.h> +#include <svl/poolitem.hxx> + +namespace frm +{ + + /// the id of an attribute + typedef sal_Int32 AttributeId; + /// the "which id" of an item in an SfxItemSet + typedef sal_uInt16 WhichId; + /// a SFX slot id + typedef sal_uInt16 SfxSlotId; + + enum AttributeCheckState + { + eChecked, + eUnchecked, + eIndetermined + }; + + struct AttributeState + { + private: + std::unique_ptr<SfxPoolItem> pItemHandleItem; + + public: + AttributeCheckState eSimpleState; + + inline AttributeState( ); + inline explicit AttributeState( AttributeCheckState _eCheckState ); + inline AttributeState( const AttributeState& _rSource ); + + inline AttributeState& operator=( const AttributeState& _rSource ); + + inline bool operator==( const AttributeState& _rRHS ); + + inline const SfxPoolItem* getItem() const; + inline void setItem( const SfxPoolItem* _pItem ); + }; + + inline AttributeState::AttributeState( ) + :eSimpleState( eIndetermined ) + { + } + + inline AttributeState::AttributeState( AttributeCheckState _eCheckState ) + :eSimpleState( _eCheckState ) + { + } + + inline AttributeState::AttributeState( const AttributeState& _rSource ) + :eSimpleState( eIndetermined ) + { + operator=( _rSource ); + } + + inline AttributeState& AttributeState::operator=( const AttributeState& _rSource ) + { + if ( &_rSource == this ) + return *this; + + eSimpleState = _rSource.eSimpleState; + setItem( _rSource.getItem() ); + return *this; + } + + inline const SfxPoolItem* AttributeState::getItem() const + { + return pItemHandleItem.get(); + } + + inline void AttributeState::setItem( const SfxPoolItem* _pItem ) + { + if ( _pItem ) + pItemHandleItem.reset(_pItem->Clone()); + else + pItemHandleItem.reset(); + } + + inline bool AttributeState::operator==( const AttributeState& _rRHS ) + { + if ( eSimpleState != _rRHS.eSimpleState ) + return false; + + if ( pItemHandleItem && !_rRHS.pItemHandleItem ) + return false; + + if ( !pItemHandleItem && _rRHS.pItemHandleItem ) + return false; + + if ( pItemHandleItem == _rRHS.pItemHandleItem ) + return true; + + assert(pItemHandleItem && _rRHS.pItemHandleItem && "otherwise have already returned"); + + return *pItemHandleItem == *_rRHS.pItemHandleItem; + } + + class IMultiAttributeDispatcher + { + public: + virtual AttributeState getState( AttributeId _nAttributeId ) const = 0; + virtual void executeAttribute( AttributeId _nAttributeId, const SfxPoolItem* _pArgument ) = 0; + + protected: + ~IMultiAttributeDispatcher() {} + }; + +} // namespace frm + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/richtext/specialdispatchers.cxx b/forms/source/richtext/specialdispatchers.cxx new file mode 100644 index 0000000000..d75e06590f --- /dev/null +++ b/forms/source/richtext/specialdispatchers.cxx @@ -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 . + */ + +#include "specialdispatchers.hxx" +#include <editeng/editeng.hxx> +#include <editeng/editids.hrc> +#include <editeng/editview.hxx> +#include <editeng/scriptspaceitem.hxx> +#include <osl/diagnose.h> + + +namespace frm +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::util; + using namespace ::com::sun::star::frame; + using namespace ::com::sun::star::beans; + + OSelectAllDispatcher::OSelectAllDispatcher( EditView& _rView, const URL& _rURL ) + :ORichTextFeatureDispatcher( _rView, _rURL ) + { + } + + + OSelectAllDispatcher::~OSelectAllDispatcher( ) + { + if ( !isDisposed() ) + { + acquire(); + dispose(); + } + } + + + void SAL_CALL OSelectAllDispatcher::dispatch( const URL& _rURL, const Sequence< PropertyValue >& /*_rArguments*/ ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + OSL_ENSURE( _rURL.Complete == getFeatureURL().Complete, "OSelectAllDispatcher::dispatch: invalid URL!" ); + + checkDisposed(); + + EditEngine* pEngine = getEditView() ? getEditView()->GetEditEngine() : nullptr; + OSL_ENSURE( pEngine, "OSelectAllDispatcher::dispatch: no edit engine - but not yet disposed?" ); + if ( !pEngine ) + return; + + sal_Int32 nParagraphs = pEngine->GetParagraphCount(); + if ( nParagraphs ) + { + sal_Int32 nLastParaNumber = nParagraphs - 1; + sal_Int32 nParaLen = pEngine->GetTextLen( nLastParaNumber ); + getEditView()->SetSelection( ESelection( 0, 0, nLastParaNumber, nParaLen ) ); + } + } + + + FeatureStateEvent OSelectAllDispatcher::buildStatusEvent() const + { + FeatureStateEvent aEvent( ORichTextFeatureDispatcher::buildStatusEvent() ); + aEvent.IsEnabled = true; + return aEvent; + } + + OParagraphDirectionDispatcher::OParagraphDirectionDispatcher( EditView& _rView, AttributeId _nAttributeId, const URL& _rURL, + IMultiAttributeDispatcher* _pMasterDispatcher ) + :OAttributeDispatcher( _rView, _nAttributeId, _rURL, _pMasterDispatcher ) + { + } + + + FeatureStateEvent OParagraphDirectionDispatcher::buildStatusEvent() const + { + FeatureStateEvent aEvent( OAttributeDispatcher::buildStatusEvent() ); + + EditEngine* pEngine = getEditView() ? getEditView()->GetEditEngine() : nullptr; + OSL_ENSURE( pEngine, "OParagraphDirectionDispatcher::dispatch: no edit engine - but not yet disposed?" ); + if ( pEngine && pEngine->IsEffectivelyVertical() ) + aEvent.IsEnabled = false; + + return aEvent; + } + + OTextDirectionDispatcher::OTextDirectionDispatcher( EditView& _rView, const URL& _rURL ) + :ORichTextFeatureDispatcher( _rView, _rURL ) + { + } + + + void SAL_CALL OTextDirectionDispatcher::dispatch( const URL& _rURL, const Sequence< PropertyValue >& /*_rArguments*/ ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + OSL_ENSURE( _rURL.Complete == getFeatureURL().Complete, "OTextDirectionDispatcher::dispatch: invalid URL!" ); + + checkDisposed(); + + EditEngine* pEngine = getEditView() ? getEditView()->GetEditEngine() : nullptr; + OSL_ENSURE( pEngine, "OTextDirectionDispatcher::dispatch: no edit engine - but not yet disposed?" ); + if ( !pEngine ) + return; + + pEngine->SetVertical( !pEngine->IsEffectivelyVertical() ); + } + + + FeatureStateEvent OTextDirectionDispatcher::buildStatusEvent() const + { + FeatureStateEvent aEvent( ORichTextFeatureDispatcher::buildStatusEvent() ); + + EditEngine* pEngine = getEditView() ? getEditView()->GetEditEngine() : nullptr; + OSL_ENSURE( pEngine, "OTextDirectionDispatcher::dispatch: no edit engine - but not yet disposed?" ); + + aEvent.IsEnabled = true; + aEvent.State <<= pEngine && pEngine->IsEffectivelyVertical(); + + return aEvent; + } + + OAsianFontLayoutDispatcher::OAsianFontLayoutDispatcher( EditView& _rView, AttributeId _nAttributeId, const URL& _rURL, IMultiAttributeDispatcher* _pMasterDispatcher ) + :OParametrizedAttributeDispatcher( _rView, _nAttributeId, _rURL, _pMasterDispatcher ) + { + } + + + const SfxPoolItem* OAsianFontLayoutDispatcher::convertDispatchArgsToItem( const Sequence< PropertyValue >& _rArguments ) + { + // look for the "Enable" parameter + const PropertyValue* pLookup = _rArguments.getConstArray(); + const PropertyValue* pLookupEnd = _rArguments.getConstArray() + _rArguments.getLength(); + while ( pLookup != pLookupEnd ) + { + if ( pLookup->Name == "Enable" ) + break; + ++pLookup; + } + if ( pLookup != pLookupEnd ) + { + bool bEnable = true; + OSL_VERIFY( pLookup->Value >>= bEnable ); + if ( m_nAttributeId == sal_uInt16(SID_ATTR_PARA_SCRIPTSPACE) ) + return new SvxScriptSpaceItem( bEnable, static_cast<WhichId>(m_nAttributeId) ); + return new SfxBoolItem( static_cast<WhichId>(m_nAttributeId), bEnable ); + } + + OSL_FAIL( "OAsianFontLayoutDispatcher::convertDispatchArgsToItem: did not find the one and only argument!" ); + return nullptr; + } + + +} // namespace frm + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/richtext/specialdispatchers.hxx b/forms/source/richtext/specialdispatchers.hxx new file mode 100644 index 0000000000..9b4bfa1b79 --- /dev/null +++ b/forms/source/richtext/specialdispatchers.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 "parametrizedattributedispatcher.hxx" + + +namespace frm +{ + + class OSelectAllDispatcher : public ORichTextFeatureDispatcher + { + public: + OSelectAllDispatcher( EditView& _rView, const css::util::URL& _rURL ); + + protected: + virtual ~OSelectAllDispatcher() override; + + // XDispatch + virtual void SAL_CALL dispatch( const css::util::URL& URL, const css::uno::Sequence< css::beans::PropertyValue >& Arguments ) override; + + // ORichTextFeatureDispatcher + virtual css::frame::FeatureStateEvent buildStatusEvent() const override; + }; + + class OParagraphDirectionDispatcher : public OAttributeDispatcher + { + public: + OParagraphDirectionDispatcher( + EditView& _rView, + AttributeId _nAttributeId, + const css::util::URL& _rURL, + IMultiAttributeDispatcher* _pMasterDispatcher + ); + + protected: + // ORichTextFeatureDispatcher + virtual css::frame::FeatureStateEvent buildStatusEvent() const override; + }; + + class OTextDirectionDispatcher : public ORichTextFeatureDispatcher + { + public: + OTextDirectionDispatcher( EditView& _rView, const css::util::URL& _rURL ); + + protected: + // XDispatch + virtual void SAL_CALL dispatch( const css::util::URL& URL, const css::uno::Sequence< css::beans::PropertyValue >& Arguments ) override; + + // ORichTextFeatureDispatcher + virtual css::frame::FeatureStateEvent buildStatusEvent() const override; + }; + + class OAsianFontLayoutDispatcher : public OParametrizedAttributeDispatcher + { + public: + OAsianFontLayoutDispatcher( + EditView& _rView, + AttributeId _nAttributeId, + const css::util::URL& _rURL, + IMultiAttributeDispatcher* _pMasterDispatcher + ); + + protected: + // OParametrizedAttributeDispatcher + virtual const SfxPoolItem* convertDispatchArgsToItem( + const css::uno::Sequence< css::beans::PropertyValue >& _rArguments ) override; + }; + + +} // namespace frm + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/forms/source/richtext/textattributelistener.hxx b/forms/source/richtext/textattributelistener.hxx new file mode 100644 index 0000000000..3025d7b5d6 --- /dev/null +++ b/forms/source/richtext/textattributelistener.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 "rtattributes.hxx" + +struct ESelection; + +namespace frm +{ + + class ITextAttributeListener + { + public: + virtual void onAttributeStateChanged( AttributeId _nAttributeId ) = 0; + + protected: + ~ITextAttributeListener() {} + }; + + + //= ITextAttributeListener + + class ITextSelectionListener + { + public: + virtual void onSelectionChanged() = 0; + + protected: + ~ITextSelectionListener() {} + }; + + +} // namespace frm + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |