summaryrefslogtreecommitdiffstats
path: root/editeng/source
diff options
context:
space:
mode:
Diffstat (limited to 'editeng/source')
-rw-r--r--editeng/source/accessibility/AccessibleComponentBase.cxx149
-rw-r--r--editeng/source/accessibility/AccessibleContextBase.cxx513
-rw-r--r--editeng/source/accessibility/AccessibleEditableTextPara.cxx2675
-rw-r--r--editeng/source/accessibility/AccessibleHyperlink.cxx128
-rw-r--r--editeng/source/accessibility/AccessibleHyperlink.hxx64
-rw-r--r--editeng/source/accessibility/AccessibleImageBullet.cxx517
-rw-r--r--editeng/source/accessibility/AccessibleImageBullet.hxx201
-rw-r--r--editeng/source/accessibility/AccessibleParaManager.cxx408
-rw-r--r--editeng/source/accessibility/AccessibleSelectionBase.cxx91
-rw-r--r--editeng/source/accessibility/AccessibleStaticTextBase.cxx964
-rw-r--r--editeng/source/accessibility/AccessibleStringWrap.cxx90
-rw-r--r--editeng/source/editeng/editattr.cxx459
-rw-r--r--editeng/source/editeng/editdata.cxx16
-rw-r--r--editeng/source/editeng/editdbg.cxx531
-rw-r--r--editeng/source/editeng/editdoc.cxx3006
-rw-r--r--editeng/source/editeng/editeng.cxx2938
-rw-r--r--editeng/source/editeng/editobj.cxx759
-rw-r--r--editeng/source/editeng/editobj2.hxx282
-rw-r--r--editeng/source/editeng/editsel.cxx94
-rw-r--r--editeng/source/editeng/editsel.hxx58
-rw-r--r--editeng/source/editeng/editstt2.hxx99
-rw-r--r--editeng/source/editeng/editundo.cxx661
-rw-r--r--editeng/source/editeng/editundo.hxx291
-rw-r--r--editeng/source/editeng/editview.cxx1800
-rw-r--r--editeng/source/editeng/edtspell.cxx708
-rw-r--r--editeng/source/editeng/eehtml.cxx813
-rw-r--r--editeng/source/editeng/eehtml.hxx84
-rw-r--r--editeng/source/editeng/eeobj.cxx92
-rw-r--r--editeng/source/editeng/eeobj.hxx50
-rw-r--r--editeng/source/editeng/eerdll.cxx228
-rw-r--r--editeng/source/editeng/eertfpar.cxx633
-rw-r--r--editeng/source/editeng/eertfpar.hxx66
-rw-r--r--editeng/source/editeng/fieldupdater.cxx70
-rw-r--r--editeng/source/editeng/impedit.cxx2787
-rw-r--r--editeng/source/editeng/impedit.hxx1362
-rw-r--r--editeng/source/editeng/impedit2.cxx4509
-rw-r--r--editeng/source/editeng/impedit3.cxx4910
-rw-r--r--editeng/source/editeng/impedit4.cxx3141
-rw-r--r--editeng/source/editeng/impedit5.cxx845
-rw-r--r--editeng/source/editeng/misspellrange.cxx21
-rw-r--r--editeng/source/editeng/section.cxx19
-rw-r--r--editeng/source/editeng/textconv.cxx543
-rw-r--r--editeng/source/editeng/textconv.hxx107
-rw-r--r--editeng/source/items/CustomPropertyField.cxx72
-rw-r--r--editeng/source/items/borderline.cxx719
-rw-r--r--editeng/source/items/bulitem.cxx159
-rw-r--r--editeng/source/items/charhiddenitem.cxx53
-rw-r--r--editeng/source/items/flditem.cxx935
-rw-r--r--editeng/source/items/frmitems.cxx4709
-rw-r--r--editeng/source/items/itemtype.cxx232
-rw-r--r--editeng/source/items/justifyitem.cxx368
-rw-r--r--editeng/source/items/legacyitem.cxx826
-rw-r--r--editeng/source/items/numitem.cxx1156
-rw-r--r--editeng/source/items/optitems.cxx63
-rw-r--r--editeng/source/items/paperinf.cxx121
-rw-r--r--editeng/source/items/paraitem.cxx1317
-rw-r--r--editeng/source/items/svdfield.cxx34
-rw-r--r--editeng/source/items/svxfont.cxx906
-rw-r--r--editeng/source/items/textitem.cxx2808
-rw-r--r--editeng/source/items/writingmodeitem.cxx94
-rw-r--r--editeng/source/items/xmlcnitm.cxx206
-rw-r--r--editeng/source/lookuptree/Trie.cxx186
-rw-r--r--editeng/source/misc/SvXMLAutoCorrectExport.cxx110
-rw-r--r--editeng/source/misc/SvXMLAutoCorrectExport.hxx60
-rw-r--r--editeng/source/misc/SvXMLAutoCorrectImport.cxx163
-rw-r--r--editeng/source/misc/SvXMLAutoCorrectImport.hxx113
-rw-r--r--editeng/source/misc/SvXMLAutoCorrectTokenHandler.cxx54
-rw-r--r--editeng/source/misc/SvXMLAutoCorrectTokenHandler.hxx45
-rw-r--r--editeng/source/misc/acorrcfg.cxx698
-rw-r--r--editeng/source/misc/edtdlg.cxx29
-rw-r--r--editeng/source/misc/forbiddencharacterstable.cxx65
-rw-r--r--editeng/source/misc/hangulhanja.cxx1002
-rw-r--r--editeng/source/misc/splwrap.cxx472
-rw-r--r--editeng/source/misc/svxacorr.cxx3161
-rw-r--r--editeng/source/misc/swafopt.cxx81
-rw-r--r--editeng/source/misc/tokens.txt7
-rw-r--r--editeng/source/misc/txtrange.cxx667
-rw-r--r--editeng/source/misc/unolingu.cxx754
-rw-r--r--editeng/source/misc/urlfieldhelper.cxx49
-rw-r--r--editeng/source/outliner/outleeng.cxx203
-rw-r--r--editeng/source/outliner/outlin2.cxx600
-rw-r--r--editeng/source/outliner/outliner.cxx2163
-rw-r--r--editeng/source/outliner/outlobj.cxx254
-rw-r--r--editeng/source/outliner/outlundo.cxx164
-rw-r--r--editeng/source/outliner/outlundo.hxx118
-rw-r--r--editeng/source/outliner/outlvw.cxx1510
-rw-r--r--editeng/source/outliner/overflowingtxt.cxx227
-rw-r--r--editeng/source/outliner/paralist.cxx256
-rw-r--r--editeng/source/outliner/paralist.hxx82
-rw-r--r--editeng/source/rtf/rtfitem.cxx1873
-rw-r--r--editeng/source/rtf/svxrtf.cxx1162
-rw-r--r--editeng/source/uno/UnoForbiddenCharsTable.cxx132
-rw-r--r--editeng/source/uno/unoedhlp.cxx250
-rw-r--r--editeng/source/uno/unoedprx.cxx1209
-rw-r--r--editeng/source/uno/unoedsrc.cxx77
-rw-r--r--editeng/source/uno/unofdesc.cxx229
-rw-r--r--editeng/source/uno/unofield.cxx941
-rw-r--r--editeng/source/uno/unofored.cxx520
-rw-r--r--editeng/source/uno/unofored_internal.hxx28
-rw-r--r--editeng/source/uno/unoforou.cxx582
-rw-r--r--editeng/source/uno/unoipset.cxx333
-rw-r--r--editeng/source/uno/unonrule.cxx544
-rw-r--r--editeng/source/uno/unopracc.cxx92
-rw-r--r--editeng/source/uno/unotext.cxx2561
-rw-r--r--editeng/source/uno/unotext2.cxx629
-rw-r--r--editeng/source/uno/unoviwou.cxx128
-rw-r--r--editeng/source/xml/editsource.hxx43
-rw-r--r--editeng/source/xml/xmltxtexp.cxx354
-rw-r--r--editeng/source/xml/xmltxtimp.cxx233
109 files changed, 77773 insertions, 0 deletions
diff --git a/editeng/source/accessibility/AccessibleComponentBase.cxx b/editeng/source/accessibility/AccessibleComponentBase.cxx
new file mode 100644
index 0000000000..5e95afbd2f
--- /dev/null
+++ b/editeng/source/accessibility/AccessibleComponentBase.cxx
@@ -0,0 +1,149 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <editeng/AccessibleComponentBase.hxx>
+
+#include <com/sun/star/accessibility/XAccessibleSelection.hpp>
+
+#include <tools/color.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+namespace accessibility {
+
+// internal
+
+AccessibleComponentBase::AccessibleComponentBase()
+{
+}
+
+
+AccessibleComponentBase::~AccessibleComponentBase()
+{
+}
+
+// XAccessibleComponent
+
+sal_Bool SAL_CALL AccessibleComponentBase::containsPoint (
+ const css::awt::Point& aPoint)
+{
+ awt::Size aSize (getSize());
+ return (aPoint.X >= 0)
+ && (aPoint.X < aSize.Width)
+ && (aPoint.Y >= 0)
+ && (aPoint.Y < aSize.Height);
+}
+
+
+uno::Reference<XAccessible > SAL_CALL
+ AccessibleComponentBase::getAccessibleAtPoint (
+ const awt::Point& /*aPoint*/)
+{
+ return uno::Reference<XAccessible>();
+}
+
+
+awt::Rectangle SAL_CALL AccessibleComponentBase::getBounds()
+{
+ return awt::Rectangle();
+}
+
+
+awt::Point SAL_CALL AccessibleComponentBase::getLocation()
+{
+ awt::Rectangle aBBox (getBounds());
+ return awt::Point (aBBox.X, aBBox.Y);
+}
+
+
+awt::Point SAL_CALL AccessibleComponentBase::getLocationOnScreen()
+{
+ return awt::Point();
+}
+
+
+css::awt::Size SAL_CALL AccessibleComponentBase::getSize()
+{
+ awt::Rectangle aBBox (getBounds());
+ return awt::Size (aBBox.Width, aBBox.Height);
+}
+
+
+void SAL_CALL AccessibleComponentBase::grabFocus()
+{
+ uno::Reference<XAccessibleContext> xContext (this, uno::UNO_QUERY);
+ uno::Reference<XAccessibleSelection> xSelection (
+ xContext->getAccessibleParent(), uno::UNO_QUERY);
+ if (xSelection.is())
+ {
+ // Do a single selection on this object.
+ xSelection->clearAccessibleSelection();
+ xSelection->selectAccessibleChild (xContext->getAccessibleIndexInParent());
+ }
+}
+
+
+sal_Int32 SAL_CALL AccessibleComponentBase::getForeground()
+{
+ return sal_Int32(COL_BLACK);
+}
+
+
+sal_Int32 SAL_CALL AccessibleComponentBase::getBackground()
+{
+ return sal_Int32(COL_WHITE);
+}
+
+
+// XAccessibleExtendedComponent
+
+css::uno::Reference< css::awt::XFont > SAL_CALL
+ AccessibleComponentBase::getFont()
+{
+ return uno::Reference<awt::XFont>();
+}
+
+
+OUString SAL_CALL AccessibleComponentBase::getTitledBorderText()
+{
+ return OUString();
+}
+
+
+OUString SAL_CALL AccessibleComponentBase::getToolTipText()
+{
+ return OUString();
+}
+
+// XTypeProvider
+
+uno::Sequence<uno::Type>
+ AccessibleComponentBase::getTypes()
+{
+ static const uno::Sequence aTypeList {
+ cppu::UnoType<XAccessibleComponent>::get(),
+ cppu::UnoType<XAccessibleExtendedComponent>::get() };
+ return aTypeList;
+}
+
+
+} // end of namespace accessibility
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/accessibility/AccessibleContextBase.cxx b/editeng/source/accessibility/AccessibleContextBase.cxx
new file mode 100644
index 0000000000..df52b70e78
--- /dev/null
+++ b/editeng/source/accessibility/AccessibleContextBase.cxx
@@ -0,0 +1,513 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <editeng/AccessibleContextBase.hxx>
+
+#include <com/sun/star/accessibility/XAccessibleEventListener.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/IllegalAccessibleComponentStateException.hpp>
+
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <comphelper/accessibleeventnotifier.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <osl/mutex.hxx>
+#include <rtl/ref.hxx>
+
+#include <utility>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+namespace accessibility {
+
+// internal
+
+AccessibleContextBase::AccessibleContextBase (
+ uno::Reference<XAccessible> xParent,
+ const sal_Int16 aRole)
+ : WeakComponentImplHelper(m_aMutex),
+ mxParent(std::move(xParent)),
+ meDescriptionOrigin(NotSet),
+ meNameOrigin(NotSet),
+ mnClientId(0),
+ maRole(aRole)
+{
+ // Create the state set.
+ mnStateSet = 0;
+
+ // Set some states. Don't use the SetState method because no events
+ // shall be broadcasted (that is not yet initialized anyway).
+ mnStateSet |= AccessibleStateType::ENABLED;
+ mnStateSet |= AccessibleStateType::SENSITIVE;
+ mnStateSet |= AccessibleStateType::SHOWING;
+ mnStateSet |= AccessibleStateType::VISIBLE;
+ mnStateSet |= AccessibleStateType::FOCUSABLE;
+ mnStateSet |= AccessibleStateType::SELECTABLE;
+
+ // Create the relation set.
+ mxRelationSet = new ::utl::AccessibleRelationSetHelper ();
+}
+
+AccessibleContextBase::~AccessibleContextBase()
+{
+}
+
+bool AccessibleContextBase::SetState (sal_Int64 aState)
+{
+ ::osl::ClearableMutexGuard aGuard (m_aMutex);
+ if (!(mnStateSet & aState))
+ {
+ mnStateSet |= aState;
+ // Clear the mutex guard so that it is not locked during calls to
+ // listeners.
+ aGuard.clear();
+
+ // Send event for all states except the DEFUNC state.
+ if (aState != AccessibleStateType::DEFUNC)
+ {
+ uno::Any aNewValue;
+ aNewValue <<= aState;
+ CommitChange(
+ AccessibleEventId::STATE_CHANGED,
+ aNewValue,
+ uno::Any(), -1);
+ }
+ return true;
+ }
+ else
+ return false;
+}
+
+
+bool AccessibleContextBase::ResetState (sal_Int64 aState)
+{
+ ::osl::ClearableMutexGuard aGuard (m_aMutex);
+ if (mnStateSet & aState)
+ {
+ mnStateSet &= ~aState;
+ // Clear the mutex guard so that it is not locked during calls to listeners.
+ aGuard.clear();
+
+ uno::Any aOldValue;
+ aOldValue <<= aState;
+ CommitChange(
+ AccessibleEventId::STATE_CHANGED,
+ uno::Any(),
+ aOldValue, -1);
+ return true;
+ }
+ else
+ return false;
+}
+
+
+bool AccessibleContextBase::GetState (sal_Int64 aState)
+{
+ ::osl::MutexGuard aGuard (m_aMutex);
+ return mnStateSet & aState;
+}
+
+
+void AccessibleContextBase::SetRelationSet (
+ const rtl::Reference<utl::AccessibleRelationSetHelper>& rxNewRelationSet)
+{
+ // Try to emit some meaningful events indicating differing relations in
+ // both sets.
+ typedef std::pair<short int,short int> RD;
+ const RD aRelationDescriptors[] = {
+ RD(AccessibleRelationType::CONTROLLED_BY, AccessibleEventId::CONTROLLED_BY_RELATION_CHANGED),
+ RD(AccessibleRelationType::CONTROLLER_FOR, AccessibleEventId::CONTROLLER_FOR_RELATION_CHANGED),
+ RD(AccessibleRelationType::LABELED_BY, AccessibleEventId::LABELED_BY_RELATION_CHANGED),
+ RD(AccessibleRelationType::LABEL_FOR, AccessibleEventId::LABEL_FOR_RELATION_CHANGED),
+ RD(AccessibleRelationType::MEMBER_OF, AccessibleEventId::MEMBER_OF_RELATION_CHANGED),
+ RD(AccessibleRelationType::INVALID, -1),
+ };
+ for (int i=0; aRelationDescriptors[i].first!=AccessibleRelationType::INVALID; i++)
+ if (mxRelationSet->containsRelation(aRelationDescriptors[i].first)
+ != rxNewRelationSet->containsRelation(aRelationDescriptors[i].first))
+ CommitChange (aRelationDescriptors[i].second, uno::Any(), uno::Any(), -1);
+
+ mxRelationSet = rxNewRelationSet;
+}
+
+
+// XAccessible
+
+uno::Reference< XAccessibleContext> SAL_CALL
+ AccessibleContextBase::getAccessibleContext()
+{
+ return this;
+}
+
+
+// XAccessibleContext
+
+/** No children.
+*/
+sal_Int64 SAL_CALL
+ AccessibleContextBase::getAccessibleChildCount()
+{
+ return 0;
+}
+
+
+/** Forward the request to the shape. Return the requested shape or throw
+ an exception for a wrong index.
+*/
+uno::Reference<XAccessible> SAL_CALL
+ AccessibleContextBase::getAccessibleChild (sal_Int64 nIndex)
+{
+ ThrowIfDisposed ();
+ throw lang::IndexOutOfBoundsException (
+ "no child with index " + OUString::number(nIndex),
+ nullptr);
+}
+
+
+uno::Reference<XAccessible> SAL_CALL
+ AccessibleContextBase::getAccessibleParent()
+{
+ ThrowIfDisposed ();
+ return mxParent;
+}
+
+
+sal_Int64 SAL_CALL
+ AccessibleContextBase::getAccessibleIndexInParent()
+{
+ ThrowIfDisposed ();
+ // Use a simple but slow solution for now. Optimize later.
+
+ // Iterate over all the parent's children and search for this object.
+ if (!mxParent.is())
+ // Return -1 to indicate that this object's parent does not know about the
+ // object.
+ return -1;
+
+ uno::Reference<XAccessibleContext> xParentContext (
+ mxParent->getAccessibleContext());
+ if (xParentContext.is())
+ {
+ sal_Int64 nChildCount = xParentContext->getAccessibleChildCount();
+ for (sal_Int64 i=0; i<nChildCount; i++)
+ {
+ uno::Reference<XAccessible> xChild (xParentContext->getAccessibleChild (i));
+ if (xChild.is())
+ {
+ uno::Reference<XAccessibleContext> xChildContext = xChild->getAccessibleContext();
+ if (xChildContext == static_cast<XAccessibleContext*>(this))
+ return i;
+ }
+ }
+ }
+
+ // Return -1 to indicate that this object's parent does not know about the
+ // object.
+ return -1;
+}
+
+
+sal_Int16 SAL_CALL
+ AccessibleContextBase::getAccessibleRole()
+{
+ ThrowIfDisposed ();
+ return maRole;
+}
+
+
+OUString SAL_CALL
+ AccessibleContextBase::getAccessibleDescription()
+{
+ ThrowIfDisposed ();
+
+ return msDescription;
+}
+
+
+OUString SAL_CALL
+ AccessibleContextBase::getAccessibleName()
+{
+ ThrowIfDisposed ();
+
+ if (meNameOrigin == NotSet)
+ {
+ // Do not send an event because this is the first time it has been
+ // requested.
+ msName = CreateAccessibleName();
+ meNameOrigin = AutomaticallyCreated;
+ }
+
+ return msName;
+}
+
+
+/** Return a copy of the relation set.
+*/
+uno::Reference<XAccessibleRelationSet> SAL_CALL
+ AccessibleContextBase::getAccessibleRelationSet()
+{
+ ThrowIfDisposed ();
+
+ // Create a copy of the relation set and return it.
+ if (mxRelationSet)
+ {
+ return new ::utl::AccessibleRelationSetHelper(*mxRelationSet);
+ }
+ else
+ return uno::Reference<XAccessibleRelationSet>(nullptr);
+}
+
+
+/** Return a copy of the state set.
+ Possible states are:
+ ENABLED
+ SHOWING
+ VISIBLE
+*/
+sal_Int64 SAL_CALL
+ AccessibleContextBase::getAccessibleStateSet()
+{
+ if (rBHelper.bDisposed)
+ {
+ // We are already disposed!
+ // Create a new state set that has only set the DEFUNC state.
+ return AccessibleStateType::DEFUNC;
+ }
+ else
+ {
+ return mnStateSet;
+ }
+}
+
+
+lang::Locale SAL_CALL
+ AccessibleContextBase::getLocale()
+{
+ ThrowIfDisposed ();
+ // Delegate request to parent.
+ if (mxParent.is())
+ {
+ uno::Reference<XAccessibleContext> xParentContext (
+ mxParent->getAccessibleContext());
+ if (xParentContext.is())
+ return xParentContext->getLocale ();
+ }
+
+ // No locale and no parent. Therefore throw exception to indicate this
+ // cluelessness.
+ throw IllegalAccessibleComponentStateException ();
+}
+
+
+// XAccessibleEventListener
+
+void SAL_CALL AccessibleContextBase::addAccessibleEventListener (
+ const uno::Reference<XAccessibleEventListener >& rxListener)
+{
+ if (!rxListener.is())
+ return;
+
+ if (rBHelper.bDisposed || rBHelper.bInDispose)
+ {
+ uno::Reference<uno::XInterface> x (static_cast<lang::XComponent *>(this), uno::UNO_QUERY);
+ rxListener->disposing (lang::EventObject (x));
+ }
+ else
+ {
+ if (!mnClientId)
+ mnClientId = comphelper::AccessibleEventNotifier::registerClient( );
+ comphelper::AccessibleEventNotifier::addEventListener( mnClientId, rxListener );
+ }
+}
+
+
+void SAL_CALL AccessibleContextBase::removeAccessibleEventListener (
+ const uno::Reference<XAccessibleEventListener >& rxListener )
+{
+ ThrowIfDisposed ();
+ if (!(rxListener.is() && mnClientId))
+ return;
+
+ sal_Int32 nListenerCount = comphelper::AccessibleEventNotifier::removeEventListener( mnClientId, rxListener );
+ if ( !nListenerCount )
+ {
+ // no listeners anymore
+ // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client),
+ // and at least to us not firing any events anymore, in case somebody calls
+ // NotifyAccessibleEvent, again
+ comphelper::AccessibleEventNotifier::revokeClient( mnClientId );
+ mnClientId = 0;
+ }
+}
+
+// XServiceInfo
+OUString SAL_CALL AccessibleContextBase::getImplementationName()
+{
+ return "AccessibleContextBase";
+}
+
+sal_Bool SAL_CALL AccessibleContextBase::supportsService (const OUString& sServiceName)
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL
+ AccessibleContextBase::getSupportedServiceNames()
+{
+ return {
+ "com.sun.star.accessibility.Accessible",
+ "com.sun.star.accessibility.AccessibleContext"};
+}
+
+
+// XTypeProvider
+
+uno::Sequence<sal_Int8> SAL_CALL
+ AccessibleContextBase::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// internal
+
+void SAL_CALL AccessibleContextBase::disposing()
+{
+ SetState (AccessibleStateType::DEFUNC);
+
+ ::osl::MutexGuard aGuard (m_aMutex);
+
+ // Send a disposing to all listeners.
+ if ( mnClientId )
+ {
+ comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( mnClientId, *this );
+ mnClientId = 0;
+ }
+ mxParent.clear();
+ mxRelationSet.clear();
+}
+
+
+void AccessibleContextBase::SetAccessibleDescription (
+ const OUString& rDescription,
+ StringOrigin eDescriptionOrigin)
+{
+ if (!(eDescriptionOrigin < meDescriptionOrigin
+ || (eDescriptionOrigin == meDescriptionOrigin && msDescription != rDescription)))
+ return;
+
+ uno::Any aOldValue, aNewValue;
+ aOldValue <<= msDescription;
+ aNewValue <<= rDescription;
+
+ msDescription = rDescription;
+ meDescriptionOrigin = eDescriptionOrigin;
+
+ CommitChange(
+ AccessibleEventId::DESCRIPTION_CHANGED,
+ aNewValue,
+ aOldValue, -1);
+}
+
+
+void AccessibleContextBase::SetAccessibleName (
+ const OUString& rName,
+ StringOrigin eNameOrigin)
+{
+ if (!(eNameOrigin < meNameOrigin
+ || (eNameOrigin == meNameOrigin && msName != rName)))
+ return;
+
+ uno::Any aOldValue, aNewValue;
+ aOldValue <<= msName;
+ aNewValue <<= rName;
+
+ msName = rName;
+ meNameOrigin = eNameOrigin;
+
+ CommitChange(
+ AccessibleEventId::NAME_CHANGED,
+ aNewValue,
+ aOldValue, -1);
+}
+
+
+OUString AccessibleContextBase::CreateAccessibleName()
+{
+ return "Empty Name";
+}
+
+
+void AccessibleContextBase::CommitChange (
+ sal_Int16 nEventId,
+ const uno::Any& rNewValue,
+ const uno::Any& rOldValue,
+ sal_Int32 nValueIndex)
+{
+ // Do not call FireEvent and do not even create the event object when no
+ // listener has been registered yet. Creating the event object can
+ // otherwise lead to a crash. See issue 93419 for details.
+ if (mnClientId != 0)
+ {
+ AccessibleEventObject aEvent (
+ static_cast<XAccessibleContext*>(this),
+ nEventId,
+ rNewValue,
+ rOldValue,
+ nValueIndex);
+
+ FireEvent (aEvent);
+ }
+}
+
+
+void AccessibleContextBase::FireEvent (const AccessibleEventObject& aEvent)
+{
+ if (mnClientId)
+ comphelper::AccessibleEventNotifier::addEvent( mnClientId, aEvent );
+}
+
+
+void AccessibleContextBase::ThrowIfDisposed()
+{
+ if (rBHelper.bDisposed || rBHelper.bInDispose)
+ {
+ throw lang::DisposedException ("object has been already disposed",
+ getXWeak());
+ }
+}
+
+
+bool AccessibleContextBase::IsDisposed() const
+{
+ return (rBHelper.bDisposed || rBHelper.bInDispose);
+}
+
+
+void AccessibleContextBase::SetAccessibleRole( sal_Int16 _nRole )
+{
+ maRole = _nRole;
+}
+
+
+} // end of namespace accessibility
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/accessibility/AccessibleEditableTextPara.cxx b/editeng/source/accessibility/AccessibleEditableTextPara.cxx
new file mode 100644
index 0000000000..a4d0ca950b
--- /dev/null
+++ b/editeng/source/accessibility/AccessibleEditableTextPara.cxx
@@ -0,0 +1,2675 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+// Global header
+
+
+#include <algorithm>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <sal/log.hxx>
+#include <editeng/flditem.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/awt/Point.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/i18n/Boundary.hpp>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleTextType.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <comphelper/accessibleeventnotifier.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
+#include <vcl/unohelp.hxx>
+#include <vcl/settings.hxx>
+#include <i18nlangtag/languagetag.hxx>
+
+#include <editeng/editeng.hxx>
+#include <editeng/unoprnms.hxx>
+#include <editeng/unoipset.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/unoedprx.hxx>
+#include <editeng/unoedsrc.hxx>
+#include <svl/eitem.hxx>
+
+
+// Project-local header
+
+
+#include <com/sun/star/beans/PropertyState.hpp>
+
+#include <unopracc.hxx>
+#include <editeng/AccessibleEditableTextPara.hxx>
+#include "AccessibleHyperlink.hxx"
+#include "AccessibleImageBullet.hxx"
+
+#include <svtools/colorcfg.hxx>
+#include <editeng/editrids.hrc>
+#include <editeng/eerdll.hxx>
+#include <editeng/numitem.hxx>
+#include <memory>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::accessibility;
+
+
+// AccessibleEditableTextPara implementation
+
+
+namespace accessibility
+{
+ static const SvxItemPropertySet* ImplGetSvxCharAndParaPropertiesSet()
+ {
+ // PropertyMap for character and paragraph properties
+ static const SfxItemPropertyMapEntry aPropMap[] =
+ {
+ SVX_UNOEDIT_OUTLINER_PROPERTIES,
+ SVX_UNOEDIT_CHAR_PROPERTIES,
+ SVX_UNOEDIT_PARA_PROPERTIES,
+ SVX_UNOEDIT_NUMBERING_PROPERTY,
+ { u"TextUserDefinedAttributes"_ustr, EE_CHAR_XMLATTRIBS, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"ParaUserDefinedAttributes"_ustr, EE_PARA_XMLATTRIBS, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ };
+ static SvxItemPropertySet aPropSet( aPropMap, EditEngine::GetGlobalItemPool() );
+ return &aPropSet;
+ }
+
+ // #i27138# - add parameter <_pParaManager>
+ AccessibleEditableTextPara::AccessibleEditableTextPara(
+ uno::Reference< XAccessible > xParent,
+ const AccessibleParaManager* _pParaManager )
+ : mnParagraphIndex( 0 ),
+ mnIndexInParent( 0 ),
+ mpEditSource( nullptr ),
+ maEEOffset( 0, 0 ),
+ mxParent(std::move( xParent )),
+ // well, that's strictly (UNO) exception safe, though not
+ // really robust. We rely on the fact that this member is
+ // constructed last, and that the constructor body catches
+ // exceptions, thus no chance for exceptions once the Id is
+ // fetched. Nevertheless, normally should employ RAII here...
+ mnNotifierClientId(::comphelper::AccessibleEventNotifier::registerClient()),
+ // #i27138#
+ mpParaManager( _pParaManager )
+ {
+
+ // Create the state set.
+ mnStateSet = 0;
+
+ // these are always on
+ mnStateSet |= AccessibleStateType::MULTI_LINE;
+ mnStateSet |= AccessibleStateType::FOCUSABLE;
+ mnStateSet |= AccessibleStateType::VISIBLE;
+ mnStateSet |= AccessibleStateType::SHOWING;
+ mnStateSet |= AccessibleStateType::ENABLED;
+ mnStateSet |= AccessibleStateType::SENSITIVE;
+ }
+
+ AccessibleEditableTextPara::~AccessibleEditableTextPara()
+ {
+ // sign off from event notifier
+ if( getNotifierClientId() != -1 )
+ {
+ try
+ {
+ ::comphelper::AccessibleEventNotifier::revokeClient( getNotifierClientId() );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+ }
+
+ OUString AccessibleEditableTextPara::implGetText()
+ {
+ return GetTextRange( 0, GetTextLen() );
+ }
+
+ css::lang::Locale AccessibleEditableTextPara::implGetLocale()
+ {
+ DBG_ASSERT(GetParagraphIndex() >= 0,
+ "AccessibleEditableTextPara::getLocale: paragraph index value overflow");
+
+ // return locale of first character in the paragraph
+ return LanguageTag(GetTextForwarder().GetLanguage( GetParagraphIndex(), 0 )).getLocale();
+ }
+
+ void AccessibleEditableTextPara::implGetSelection( sal_Int32& nStartIndex, sal_Int32& nEndIndex )
+ {
+ sal_Int32 nStart, nEnd;
+
+ if( GetSelection( nStart, nEnd ) )
+ {
+ nStartIndex = nStart;
+ nEndIndex = nEnd;
+ }
+ else
+ {
+ // #102234# No exception, just set to 'invalid'
+ nStartIndex = -1;
+ nEndIndex = -1;
+ }
+ }
+
+ void AccessibleEditableTextPara::implGetParagraphBoundary( const OUString& rText, css::i18n::Boundary& rBoundary, sal_Int32 /*nIndex*/ )
+ {
+ SAL_INFO( "editeng", "AccessibleEditableTextPara::implGetParagraphBoundary: only a base implementation, ignoring the index" );
+
+ rBoundary.startPos = 0;
+ rBoundary.endPos = rText.getLength();
+ }
+
+ void AccessibleEditableTextPara::implGetLineBoundary( const OUString&, css::i18n::Boundary& rBoundary, sal_Int32 nIndex )
+ {
+ SvxTextForwarder& rCacheTF = GetTextForwarder();
+ const sal_Int32 nParaIndex = GetParagraphIndex();
+
+ DBG_ASSERT(nParaIndex >= 0,
+ "AccessibleEditableTextPara::implGetLineBoundary: paragraph index value overflow");
+
+ const sal_Int32 nTextLen = rCacheTF.GetTextLen( nParaIndex );
+
+ CheckPosition(nIndex);
+
+ rBoundary.startPos = rBoundary.endPos = -1;
+
+ const sal_Int32 nLineCount=rCacheTF.GetLineCount( nParaIndex );
+
+ if( nIndex == nTextLen )
+ {
+ // #i17014# Special-casing one-behind-the-end character
+ if( nLineCount <= 1 )
+ rBoundary.startPos = 0;
+ else
+ rBoundary.startPos = nTextLen - rCacheTF.GetLineLen( nParaIndex,
+ nLineCount-1 );
+
+ rBoundary.endPos = nTextLen;
+ }
+ else
+ {
+ // normal line search
+ sal_Int32 nLine;
+ sal_Int32 nCurIndex;
+ for( nLine=0, nCurIndex=0; nLine<nLineCount; ++nLine )
+ {
+ nCurIndex += rCacheTF.GetLineLen( nParaIndex, nLine);
+
+ if( nCurIndex > nIndex )
+ {
+ rBoundary.startPos = nCurIndex - rCacheTF.GetLineLen( nParaIndex, nLine);
+ rBoundary.endPos = nCurIndex;
+ break;
+ }
+ }
+ }
+ }
+
+
+ void AccessibleEditableTextPara::SetIndexInParent( sal_Int32 nIndex )
+ {
+ mnIndexInParent = nIndex;
+ }
+
+
+ void AccessibleEditableTextPara::SetParagraphIndex( sal_Int32 nIndex )
+ {
+ sal_Int32 nOldIndex = mnParagraphIndex;
+
+ mnParagraphIndex = nIndex;
+
+ auto aChild( maImageBullet.get() );
+ if( aChild.is() )
+ aChild->SetParagraphIndex(mnParagraphIndex);
+
+ try
+ {
+ if( nOldIndex != nIndex )
+ {
+ uno::Any aOldDesc;
+ uno::Any aOldName;
+
+ try
+ {
+ aOldDesc <<= getAccessibleDescription();
+ aOldName <<= getAccessibleName();
+ }
+ catch (const uno::Exception&) // optional behaviour
+ {
+ }
+ // index and therefore description changed
+ FireEvent( AccessibleEventId::DESCRIPTION_CHANGED, uno::Any( getAccessibleDescription() ), aOldDesc );
+ FireEvent( AccessibleEventId::NAME_CHANGED, uno::Any( getAccessibleName() ), aOldName );
+ }
+ }
+ catch (const uno::Exception&) // optional behaviour
+ {
+ }
+ }
+
+
+ void AccessibleEditableTextPara::Dispose()
+ {
+ int nClientId( getNotifierClientId() );
+
+ // #108212# drop all references before notifying dispose
+ mxParent = nullptr;
+ mnNotifierClientId = -1;
+ mpEditSource = nullptr;
+
+ // notify listeners
+ if( nClientId == -1 )
+ return;
+
+ try
+ {
+ uno::Reference < XAccessibleContext > xThis = getAccessibleContext();
+
+ // #106234# Delegate to EventNotifier
+ ::comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( nClientId, xThis );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+
+ void AccessibleEditableTextPara::SetEditSource( SvxEditSourceAdapter* pEditSource )
+ {
+ auto aChild( maImageBullet.get() );
+ if( aChild.is() )
+ aChild->SetEditSource(pEditSource);
+
+ if( !pEditSource )
+ {
+ // going defunc
+ UnSetState( AccessibleStateType::SHOWING );
+ UnSetState( AccessibleStateType::VISIBLE );
+ SetState( AccessibleStateType::INVALID );
+ SetState( AccessibleStateType::DEFUNC );
+
+ Dispose();
+ }
+ mpEditSource = pEditSource;
+ // #108900# Init last text content
+ try
+ {
+ TextChanged();
+ }
+ catch (const uno::RuntimeException&)
+ {
+ }
+ }
+
+ ESelection AccessibleEditableTextPara::MakeSelection( sal_Int32 nStartEEIndex, sal_Int32 nEndEEIndex )
+ {
+ // check overflow
+ DBG_ASSERT(nStartEEIndex >= 0 &&
+ nEndEEIndex >= 0 &&
+ GetParagraphIndex() >= 0,
+ "AccessibleEditableTextPara::MakeSelection: index value overflow");
+
+ sal_Int32 nParaIndex = GetParagraphIndex();
+ return ESelection(nParaIndex, nStartEEIndex, nParaIndex, nEndEEIndex);
+ }
+
+ ESelection AccessibleEditableTextPara::MakeSelection( sal_Int32 nEEIndex )
+ {
+ return MakeSelection( nEEIndex, nEEIndex+1 );
+ }
+
+ ESelection AccessibleEditableTextPara::MakeCursor( sal_Int32 nEEIndex )
+ {
+ return MakeSelection( nEEIndex, nEEIndex );
+ }
+
+ void AccessibleEditableTextPara::CheckIndex( sal_Int32 nIndex )
+ {
+ if( nIndex < 0 || nIndex >= getCharacterCount() )
+ throw lang::IndexOutOfBoundsException("AccessibleEditableTextPara: character index out of bounds",
+ getXWeak() );
+ }
+
+ void AccessibleEditableTextPara::CheckPosition( sal_Int32 nIndex )
+ {
+ if( nIndex < 0 || nIndex > getCharacterCount() )
+ throw lang::IndexOutOfBoundsException("AccessibleEditableTextPara: character position out of bounds",
+ getXWeak() );
+ }
+
+ void AccessibleEditableTextPara::CheckRange( sal_Int32 nStart, sal_Int32 nEnd )
+ {
+ CheckPosition( nStart );
+ CheckPosition( nEnd );
+ }
+
+ bool AccessibleEditableTextPara::GetSelection(sal_Int32 &nStartPos, sal_Int32 &nEndPos)
+ {
+ ESelection aSelection;
+ sal_Int32 nPara = GetParagraphIndex();
+ if( !GetEditViewForwarder().GetSelection( aSelection ) )
+ return false;
+
+ if( aSelection.nStartPara < aSelection.nEndPara )
+ {
+ if( aSelection.nStartPara > nPara ||
+ aSelection.nEndPara < nPara )
+ return false;
+
+ if( nPara == aSelection.nStartPara )
+ nStartPos = aSelection.nStartPos;
+ else
+ nStartPos = 0;
+
+ if( nPara == aSelection.nEndPara )
+ nEndPos = aSelection.nEndPos;
+ else
+ nEndPos = GetTextLen();
+ }
+ else
+ {
+ if( aSelection.nStartPara < nPara ||
+ aSelection.nEndPara > nPara )
+ return false;
+
+ if( nPara == aSelection.nStartPara )
+ nStartPos = aSelection.nStartPos;
+ else
+ nStartPos = GetTextLen();
+
+ if( nPara == aSelection.nEndPara )
+ nEndPos = aSelection.nEndPos;
+ else
+ nEndPos = 0;
+ }
+
+ return true;
+ }
+
+ OUString AccessibleEditableTextPara::GetTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
+ {
+ return GetTextForwarder().GetText( MakeSelection(nStartIndex, nEndIndex) );
+ }
+
+ sal_Int32 AccessibleEditableTextPara::GetTextLen() const
+ {
+ return GetTextForwarder().GetTextLen(GetParagraphIndex());
+ }
+
+ SvxEditSourceAdapter& AccessibleEditableTextPara::GetEditSource() const
+ {
+ if( !mpEditSource )
+ throw uno::RuntimeException("No edit source, object is defunct",
+ const_cast< AccessibleEditableTextPara* > (this)->getXWeak() );
+ return *mpEditSource;
+ }
+
+ SvxAccessibleTextAdapter& AccessibleEditableTextPara::GetTextForwarder() const
+ {
+ SvxEditSourceAdapter& rEditSource = GetEditSource();
+ SvxAccessibleTextAdapter* pTextForwarder = rEditSource.GetTextForwarderAdapter();
+
+ if( !pTextForwarder )
+ throw uno::RuntimeException("Unable to fetch text forwarder, object is defunct",
+ const_cast< AccessibleEditableTextPara* > (this)->getXWeak() );
+
+ if( !pTextForwarder->IsValid() )
+ throw uno::RuntimeException("Text forwarder is invalid, object is defunct",
+ const_cast< AccessibleEditableTextPara* > (this)->getXWeak() );
+ return *pTextForwarder;
+ }
+
+ SvxViewForwarder& AccessibleEditableTextPara::GetViewForwarder() const
+ {
+ SvxEditSource& rEditSource = GetEditSource();
+ SvxViewForwarder* pViewForwarder = rEditSource.GetViewForwarder();
+
+ if( !pViewForwarder )
+ {
+ throw uno::RuntimeException("Unable to fetch view forwarder, object is defunct",
+ const_cast< AccessibleEditableTextPara* > (this)->getXWeak() );
+ }
+
+ if( !pViewForwarder->IsValid() )
+ throw uno::RuntimeException("View forwarder is invalid, object is defunct",
+ const_cast< AccessibleEditableTextPara* > (this)->getXWeak() );
+ return *pViewForwarder;
+ }
+
+ SvxAccessibleTextEditViewAdapter& AccessibleEditableTextPara::GetEditViewForwarder( bool bCreate ) const
+ {
+ SvxEditSourceAdapter& rEditSource = GetEditSource();
+ SvxAccessibleTextEditViewAdapter* pTextEditViewForwarder = rEditSource.GetEditViewForwarderAdapter( bCreate );
+
+ if( !pTextEditViewForwarder )
+ {
+ if( bCreate )
+ throw uno::RuntimeException("Unable to fetch view forwarder, object is defunct",
+ const_cast< AccessibleEditableTextPara* > (this)->getXWeak() );
+ else
+ throw uno::RuntimeException("No view forwarder, object not in edit mode",
+ const_cast< AccessibleEditableTextPara* > (this)->getXWeak() );
+ }
+
+ if( pTextEditViewForwarder->IsValid() )
+ return *pTextEditViewForwarder;
+ else
+ {
+ if( bCreate )
+ throw uno::RuntimeException("View forwarder is invalid, object is defunct",
+ const_cast< AccessibleEditableTextPara* > (this)->getXWeak() );
+ else
+ throw uno::RuntimeException("View forwarder is invalid, object not in edit mode",
+ const_cast< AccessibleEditableTextPara* > (this)->getXWeak() );
+ }
+ }
+
+ bool AccessibleEditableTextPara::HaveEditView() const
+ {
+ SvxEditSource& rEditSource = GetEditSource();
+ SvxEditViewForwarder* pViewForwarder = rEditSource.GetEditViewForwarder();
+
+ if( !pViewForwarder )
+ return false;
+
+ if( !pViewForwarder->IsValid() )
+ return false;
+
+ return true;
+ }
+
+ bool AccessibleEditableTextPara::HaveChildren()
+ {
+ DBG_ASSERT(GetParagraphIndex() >= 0,
+ "AccessibleEditableTextPara::HaveChildren: paragraph index value overflow");
+
+ return GetTextForwarder().HaveImageBullet( GetParagraphIndex() );
+ }
+
+ tools::Rectangle AccessibleEditableTextPara::LogicToPixel( const tools::Rectangle& rRect, const MapMode& rMapMode, SvxViewForwarder const & rForwarder )
+ {
+ // convert to screen coordinates
+ return tools::Rectangle( rForwarder.LogicToPixel( rRect.TopLeft(), rMapMode ),
+ rForwarder.LogicToPixel( rRect.BottomRight(), rMapMode ) );
+ }
+
+
+ void AccessibleEditableTextPara::SetEEOffset( const Point& rOffset )
+ {
+ auto aChild( maImageBullet.get() );
+ if( aChild.is() )
+ aChild->SetEEOffset(rOffset);
+
+ maEEOffset = rOffset;
+ }
+
+ void AccessibleEditableTextPara::FireEvent(const sal_Int16 nEventId, const uno::Any& rNewValue, const uno::Any& rOldValue) const
+ {
+ uno::Reference < XAccessibleContext > xThis( const_cast< AccessibleEditableTextPara* > (this)->getAccessibleContext() );
+
+ AccessibleEventObject aEvent(xThis, nEventId, rNewValue, rOldValue, -1);
+
+ // #106234# Delegate to EventNotifier
+ if( getNotifierClientId() != -1 )
+ ::comphelper::AccessibleEventNotifier::addEvent( getNotifierClientId(),
+ aEvent );
+ }
+
+ void AccessibleEditableTextPara::SetState( const sal_Int64 nStateId )
+ {
+ if( !(mnStateSet & nStateId) )
+ {
+ mnStateSet |= nStateId;
+ FireEvent( AccessibleEventId::STATE_CHANGED, uno::Any( nStateId ) );
+ }
+ }
+
+ void AccessibleEditableTextPara::UnSetState( const sal_Int64 nStateId )
+ {
+ if( mnStateSet & nStateId )
+ {
+ mnStateSet &= ~nStateId;
+ FireEvent( AccessibleEventId::STATE_CHANGED, uno::Any(), uno::Any( nStateId ) );
+ }
+ }
+
+ void AccessibleEditableTextPara::TextChanged()
+ {
+ OUString aCurrentString( implGetText() );
+ uno::Any aDeleted;
+ uno::Any aInserted;
+ if( OCommonAccessibleText::implInitTextChangedEvent( maLastTextString, aCurrentString,
+ aDeleted, aInserted) )
+ {
+ FireEvent( AccessibleEventId::TEXT_CHANGED, aInserted, aDeleted );
+ maLastTextString = aCurrentString;
+ }
+ }
+
+ bool AccessibleEditableTextPara::GetAttributeRun( sal_Int32& nStartIndex, sal_Int32& nEndIndex, sal_Int32 nIndex )
+ {
+ DBG_ASSERT(nIndex >= 0,
+ "AccessibleEditableTextPara::GetAttributeRun: index value overflow");
+
+ DBG_ASSERT(GetParagraphIndex() >= 0,
+ "AccessibleEditableTextPara::getLocale: paragraph index value overflow");
+
+ return GetTextForwarder().GetAttributeRun( nStartIndex,
+ nEndIndex,
+ GetParagraphIndex(),
+ nIndex );
+ }
+
+ uno::Any SAL_CALL AccessibleEditableTextPara::queryInterface (const uno::Type & rType)
+ {
+ uno::Any aRet;
+
+ // must provide XAccessibleText by hand, since it comes publicly inherited by XAccessibleEditableText
+ if ( rType == cppu::UnoType<XAccessibleText>::get())
+ {
+ uno::Reference< XAccessibleText > aAccText = static_cast< XAccessibleEditableText * >(this);
+ aRet <<= aAccText;
+ }
+ else if ( rType == cppu::UnoType<XAccessibleEditableText>::get())
+ {
+ uno::Reference< XAccessibleEditableText > aAccEditText = this;
+ aRet <<= aAccEditText;
+ }
+ else if ( rType == cppu::UnoType<XAccessibleHypertext>::get())
+ {
+ uno::Reference< XAccessibleHypertext > aAccHyperText = this;
+ aRet <<= aAccHyperText;
+ }
+ else
+ {
+ aRet = AccessibleTextParaInterfaceBase::queryInterface(rType);
+ }
+
+ return aRet;
+ }
+
+ // XAccessible
+ uno::Reference< XAccessibleContext > SAL_CALL AccessibleEditableTextPara::getAccessibleContext()
+ {
+ // We implement the XAccessibleContext interface in the same object
+ return uno::Reference< XAccessibleContext > ( this );
+ }
+
+ // XAccessibleContext
+ sal_Int64 SAL_CALL AccessibleEditableTextPara::getAccessibleChildCount()
+ {
+ SolarMutexGuard aGuard;
+
+ return HaveChildren() ? 1 : 0;
+ }
+
+ uno::Reference< XAccessible > SAL_CALL AccessibleEditableTextPara::getAccessibleChild( sal_Int64 i )
+ {
+ SolarMutexGuard aGuard;
+
+ if( !HaveChildren() )
+ throw lang::IndexOutOfBoundsException("No children available",
+ getXWeak() );
+
+ if( i != 0 )
+ throw lang::IndexOutOfBoundsException("Invalid child index",
+ getXWeak() );
+
+ auto aChild( maImageBullet.get() );
+
+ if( !aChild.is() )
+ {
+ // there is no hard reference available, create object then
+ aChild = new AccessibleImageBullet(this);
+
+ aChild->SetEditSource( &GetEditSource() );
+ aChild->SetParagraphIndex( GetParagraphIndex() );
+ aChild->SetIndexInParent( i );
+
+ maImageBullet = aChild;
+ }
+
+ return aChild;
+ }
+
+ uno::Reference< XAccessible > SAL_CALL AccessibleEditableTextPara::getAccessibleParent()
+ {
+ SAL_WARN_IF(!mxParent.is(), "editeng", "AccessibleEditableTextPara::getAccessibleParent: no frontend set, did somebody forgot to call AccessibleTextHelper::SetEventSource()?");
+
+ return mxParent;
+ }
+
+ sal_Int64 SAL_CALL AccessibleEditableTextPara::getAccessibleIndexInParent()
+ {
+ return mnIndexInParent;
+ }
+
+ sal_Int16 SAL_CALL AccessibleEditableTextPara::getAccessibleRole()
+ {
+ return AccessibleRole::PARAGRAPH;
+ }
+
+ OUString SAL_CALL AccessibleEditableTextPara::getAccessibleDescription()
+ {
+ SolarMutexGuard aGuard;
+
+ // append first 40 characters from text, or first line, if shorter
+ // (writer takes first sentence here, but that's not supported
+ // from EditEngine)
+ // throws if defunc
+ OUString aLine;
+
+ if( getCharacterCount() )
+ aLine = getTextAtIndex(0, AccessibleTextType::LINE).SegmentText;
+
+ // Get the string from the resource for the specified id.
+ OUString sStr(EditResId(RID_SVXSTR_A11Y_PARAGRAPH_DESCRIPTION));
+ OUString sParaIndex = OUString::number(GetParagraphIndex());
+ sStr = sStr.replaceFirst("$(ARG)", sParaIndex);
+
+ if( aLine.getLength() > MaxDescriptionLen )
+ {
+ OUString aCurrWord;
+ sal_Int32 i;
+
+ // search backward from MaxDescriptionLen for previous word start
+ for( aCurrWord=getTextAtIndex(MaxDescriptionLen, AccessibleTextType::WORD).SegmentText,
+ i=MaxDescriptionLen,
+ aLine=OUString();
+ i>=0;
+ --i )
+ {
+ if( getTextAtIndex(i, AccessibleTextType::WORD).SegmentText != aCurrWord )
+ {
+ if( i == 0 )
+ // prevent completely empty string
+ aLine = getTextAtIndex(0, AccessibleTextType::WORD).SegmentText;
+ else
+ aLine = getTextRange(0, i);
+ }
+ }
+ }
+
+ return sStr + aLine;
+ }
+
+ OUString SAL_CALL AccessibleEditableTextPara::getAccessibleName()
+ {
+ //See tdf#101003 before implementing a body
+ return OUString();
+ }
+
+ uno::Reference< XAccessibleRelationSet > SAL_CALL AccessibleEditableTextPara::getAccessibleRelationSet()
+ {
+ // #i27138# - provide relations CONTENT_FLOWS_FROM
+ // and CONTENT_FLOWS_TO
+ if ( mpParaManager )
+ {
+ rtl::Reference<utl::AccessibleRelationSetHelper> pAccRelSetHelper =
+ new utl::AccessibleRelationSetHelper();
+ sal_Int32 nMyParaIndex( GetParagraphIndex() );
+ // relation CONTENT_FLOWS_FROM
+ if ( nMyParaIndex > 0 &&
+ mpParaManager->IsReferencable( nMyParaIndex - 1 ) )
+ {
+ uno::Sequence<uno::Reference<XInterface> > aSequence
+ { cppu::getXWeak(mpParaManager->GetChild( nMyParaIndex - 1 ).first.get().get()) };
+ AccessibleRelation aAccRel( AccessibleRelationType::CONTENT_FLOWS_FROM,
+ aSequence );
+ pAccRelSetHelper->AddRelation( aAccRel );
+ }
+
+ // relation CONTENT_FLOWS_TO
+ if ( (nMyParaIndex + 1) < mpParaManager->GetNum() &&
+ mpParaManager->IsReferencable( nMyParaIndex + 1 ) )
+ {
+ uno::Sequence<uno::Reference<XInterface> > aSequence
+ { cppu::getXWeak(mpParaManager->GetChild( nMyParaIndex + 1 ).first.get().get()) };
+ AccessibleRelation aAccRel( AccessibleRelationType::CONTENT_FLOWS_TO,
+ aSequence );
+ pAccRelSetHelper->AddRelation( aAccRel );
+ }
+
+ return pAccRelSetHelper;
+ }
+ else
+ {
+ // no relations, therefore empty
+ return uno::Reference< XAccessibleRelationSet >();
+ }
+ }
+
+ static uno::Sequence< OUString > const & getAttributeNames()
+ {
+ static const uno::Sequence<OUString> aNames{
+ "CharColor",
+ "CharContoured",
+ "CharEmphasis",
+ "CharEscapement",
+ "CharFontName",
+ "CharHeight",
+ "CharPosture",
+ "CharShadowed",
+ "CharStrikeout",
+ "CharCaseMap",
+ "CharUnderline",
+ "CharUnderlineColor",
+ "CharWeight",
+ "NumberingLevel",
+ "NumberingRules",
+ "ParaAdjust",
+ "ParaBottomMargin",
+ "ParaFirstLineIndent",
+ "ParaLeftMargin",
+ "ParaLineSpacing",
+ "ParaRightMargin",
+ "ParaTabStops"};
+
+ return aNames;
+ }
+
+ namespace {
+
+ struct IndexCompare
+ {
+ const PropertyValue* pValues;
+ explicit IndexCompare( const PropertyValue* pVals ) : pValues(pVals) {}
+ bool operator() ( sal_Int32 a, sal_Int32 b ) const
+ {
+ return pValues[a].Name < pValues[b].Name;
+ }
+ };
+
+ }
+}
+
+namespace
+{
+ OUString GetFieldTypeNameFromField(EFieldInfo const &ree)
+ {
+ OUString strFldType;
+ sal_Int32 nFieldType = -1;
+ if (ree.pFieldItem)
+ {
+ // So we get a field, check its type now.
+ nFieldType = ree.pFieldItem->GetField()->GetClassId() ;
+ }
+ switch (nFieldType)
+ {
+ case text::textfield::Type::DATE:
+ {
+ const SvxDateField* pDateField = static_cast< const SvxDateField* >(ree.pFieldItem->GetField());
+ if (pDateField)
+ {
+ if (pDateField->GetType() == SvxDateType::Fix)
+ strFldType = "date (fixed)";
+ else if (pDateField->GetType() == SvxDateType::Var)
+ strFldType = "date (variable)";
+ }
+ break;
+ }
+ case text::textfield::Type::PAGE:
+ strFldType = "page-number";
+ break;
+ //support the sheet name & pages fields
+ case text::textfield::Type::PAGES:
+ strFldType = "page-count";
+ break;
+ case text::textfield::Type::TABLE:
+ strFldType = "sheet-name";
+ break;
+ //End
+ case text::textfield::Type::TIME:
+ strFldType = "time";
+ break;
+ case text::textfield::Type::EXTENDED_TIME:
+ {
+ const SvxExtTimeField* pTimeField = static_cast< const SvxExtTimeField* >(ree.pFieldItem->GetField());
+ if (pTimeField)
+ {
+ if (pTimeField->GetType() == SvxTimeType::Fix)
+ strFldType = "time (fixed)";
+ else if (pTimeField->GetType() == SvxTimeType::Var)
+ strFldType = "time (variable)";
+ }
+ break;
+ }
+ case text::textfield::Type::AUTHOR:
+ strFldType = "author";
+ break;
+ case text::textfield::Type::EXTENDED_FILE:
+ case text::textfield::Type::DOCINFO_TITLE:
+ strFldType = "file name";
+ break;
+ case text::textfield::Type::DOCINFO_CUSTOM:
+ strFldType = "custom document property";
+ break;
+ default:
+ break;
+ }
+ return strFldType;
+ }
+}
+
+namespace accessibility
+{
+ OUString AccessibleEditableTextPara::GetFieldTypeNameAtIndex(sal_Int32 nIndex)
+ {
+ SvxAccessibleTextAdapter& rCacheTF = GetTextForwarder();
+ //For field object info
+ sal_Int32 nParaIndex = GetParagraphIndex();
+ sal_Int32 nAllFieldLen = 0;
+ sal_Int32 nField = rCacheTF.GetFieldCount(nParaIndex);
+ for (sal_Int32 j = 0; j < nField; ++j)
+ {
+ EFieldInfo ree = rCacheTF.GetFieldInfo(nParaIndex, j);
+ sal_Int32 reeBegin = ree.aPosition.nIndex + nAllFieldLen;
+ sal_Int32 reeEnd = reeBegin + ree.aCurrentText.getLength();
+ nAllFieldLen += (ree.aCurrentText.getLength() - 1);
+ if (nIndex < reeBegin)
+ break;
+ if (nIndex < reeEnd)
+ return GetFieldTypeNameFromField(ree);
+ }
+ return OUString();
+ }
+
+ sal_Int64 SAL_CALL AccessibleEditableTextPara::getAccessibleStateSet()
+ {
+ SolarMutexGuard aGuard;
+
+ // Create a copy of the state set and return it.
+
+ sal_Int64 nParentStates = 0;
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext();
+ nParentStates = xParentContext->getAccessibleStateSet();
+ }
+ if (nParentStates & AccessibleStateType::EDITABLE)
+ {
+ mnStateSet |= AccessibleStateType::EDITABLE;
+ }
+ return mnStateSet;
+ }
+
+ lang::Locale SAL_CALL AccessibleEditableTextPara::getLocale()
+ {
+ SolarMutexGuard aGuard;
+
+ return implGetLocale();
+ }
+
+ void SAL_CALL AccessibleEditableTextPara::addAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener )
+ {
+ if( getNotifierClientId() != -1 )
+ ::comphelper::AccessibleEventNotifier::addEventListener( getNotifierClientId(), xListener );
+ }
+
+ void SAL_CALL AccessibleEditableTextPara::removeAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener )
+ {
+ if( getNotifierClientId() == -1 )
+ return;
+
+ const sal_Int32 nListenerCount = ::comphelper::AccessibleEventNotifier::removeEventListener( getNotifierClientId(), xListener );
+ if ( !nListenerCount )
+ {
+ // no listeners anymore
+ // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client),
+ // and at least to us not firing any events anymore, in case somebody calls
+ // NotifyAccessibleEvent, again
+ ::comphelper::AccessibleEventNotifier::TClientId nId( getNotifierClientId() );
+ mnNotifierClientId = -1;
+ ::comphelper::AccessibleEventNotifier::revokeClient( nId );
+ }
+ }
+
+ // XAccessibleComponent
+ sal_Bool SAL_CALL AccessibleEditableTextPara::containsPoint( const awt::Point& aTmpPoint )
+ {
+ SolarMutexGuard aGuard;
+
+ DBG_ASSERT(GetParagraphIndex() >= 0,
+ "AccessibleEditableTextPara::contains: index value overflow");
+
+ awt::Rectangle aTmpRect = getBounds();
+ tools::Rectangle aRect( Point(aTmpRect.X, aTmpRect.Y), Size(aTmpRect.Width, aTmpRect.Height) );
+ Point aPoint( aTmpPoint.X, aTmpPoint.Y );
+
+ return aRect.Contains( aPoint );
+ }
+
+ uno::Reference< XAccessible > SAL_CALL AccessibleEditableTextPara::getAccessibleAtPoint( const awt::Point& _aPoint )
+ {
+ SolarMutexGuard aGuard;
+
+ if( HaveChildren() )
+ {
+ // #103862# No longer need to make given position relative
+ Point aPoint( _aPoint.X, _aPoint.Y );
+
+ // respect EditEngine offset to surrounding shape/cell
+ aPoint -= GetEEOffset();
+
+ // convert to EditEngine coordinate system
+ SvxTextForwarder& rCacheTF = GetTextForwarder();
+ Point aLogPoint( GetViewForwarder().PixelToLogic( aPoint, rCacheTF.GetMapMode() ) );
+
+ EBulletInfo aBulletInfo = rCacheTF.GetBulletInfo(GetParagraphIndex());
+
+ if( aBulletInfo.nParagraph != EE_PARA_NOT_FOUND &&
+ aBulletInfo.bVisible &&
+ aBulletInfo.nType == SVX_NUM_BITMAP )
+ {
+ tools::Rectangle aRect = aBulletInfo.aBounds;
+
+ if( aRect.Contains( aLogPoint ) )
+ return getAccessibleChild(0);
+ }
+ }
+
+ // no children at all, or none at given position
+ return uno::Reference< XAccessible >();
+ }
+
+ awt::Rectangle SAL_CALL AccessibleEditableTextPara::getBounds()
+ {
+ SolarMutexGuard aGuard;
+
+ DBG_ASSERT(GetParagraphIndex() >= 0,
+ "AccessibleEditableTextPara::getBounds: index value overflow");
+
+ SvxTextForwarder& rCacheTF = GetTextForwarder();
+ tools::Rectangle aRect = rCacheTF.GetParaBounds( GetParagraphIndex() );
+
+ // convert to screen coordinates
+ tools::Rectangle aScreenRect = AccessibleEditableTextPara::LogicToPixel( aRect,
+ rCacheTF.GetMapMode(),
+ GetViewForwarder() );
+
+ // offset from shape/cell
+ Point aOffset = GetEEOffset();
+
+ return awt::Rectangle( aScreenRect.Left() + aOffset.X(),
+ aScreenRect.Top() + aOffset.Y(),
+ aScreenRect.GetSize().Width(),
+ aScreenRect.GetSize().Height() );
+ }
+
+ awt::Point SAL_CALL AccessibleEditableTextPara::getLocation( )
+ {
+ SolarMutexGuard aGuard;
+
+ awt::Rectangle aRect = getBounds();
+
+ return awt::Point( aRect.X, aRect.Y );
+ }
+
+ awt::Point SAL_CALL AccessibleEditableTextPara::getLocationOnScreen( )
+ {
+ SolarMutexGuard aGuard;
+
+ // relate us to parent
+ uno::Reference< XAccessible > xParent = getAccessibleParent();
+ if( xParent.is() )
+ {
+ uno::Reference< XAccessibleComponent > xParentComponent( xParent, uno::UNO_QUERY );
+ if( xParentComponent.is() )
+ {
+ awt::Point aRefPoint = xParentComponent->getLocationOnScreen();
+ awt::Point aPoint = getLocation();
+ aPoint.X += aRefPoint.X;
+ aPoint.Y += aRefPoint.Y;
+
+ return aPoint;
+ }
+ // #i88070#
+ // fallback to parent's <XAccessibleContext> instance
+ else
+ {
+ uno::Reference< XAccessibleContext > xParentContext = xParent->getAccessibleContext();
+ if ( xParentContext.is() )
+ {
+ uno::Reference< XAccessibleComponent > xParentContextComponent( xParentContext, uno::UNO_QUERY );
+ if( xParentContextComponent.is() )
+ {
+ awt::Point aRefPoint = xParentContextComponent->getLocationOnScreen();
+ awt::Point aPoint = getLocation();
+ aPoint.X += aRefPoint.X;
+ aPoint.Y += aRefPoint.Y;
+
+ return aPoint;
+ }
+ }
+ }
+ }
+
+ throw uno::RuntimeException("Cannot access parent",
+ uno::Reference< uno::XInterface >
+ ( static_cast< XAccessible* > (this) ) ); // disambiguate hierarchy
+ }
+
+ awt::Size SAL_CALL AccessibleEditableTextPara::getSize( )
+ {
+ SolarMutexGuard aGuard;
+
+ awt::Rectangle aRect = getBounds();
+
+ return awt::Size( aRect.Width, aRect.Height );
+ }
+
+ void SAL_CALL AccessibleEditableTextPara::grabFocus( )
+ {
+ // set cursor to this paragraph
+ setSelection(0,0);
+ }
+
+ sal_Int32 SAL_CALL AccessibleEditableTextPara::getForeground( )
+ {
+ // #104444# Added to XAccessibleComponent interface
+ svtools::ColorConfig aColorConfig;
+ Color nColor = aColorConfig.GetColorValue( svtools::FONTCOLOR ).nColor;
+ return static_cast<sal_Int32>(nColor);
+ }
+
+ sal_Int32 SAL_CALL AccessibleEditableTextPara::getBackground( )
+ {
+ // #104444# Added to XAccessibleComponent interface
+ Color aColor( Application::GetSettings().GetStyleSettings().GetWindowColor() );
+
+ // the background is transparent
+ aColor.SetAlpha(0);
+
+ return static_cast<sal_Int32>( aColor );
+ }
+
+ // XAccessibleText
+ sal_Int32 SAL_CALL AccessibleEditableTextPara::getCaretPosition()
+ {
+ SolarMutexGuard aGuard;
+
+ if( !HaveEditView() )
+ return -1;
+
+ ESelection aSelection;
+ if( GetEditViewForwarder().GetSelection( aSelection ) &&
+ GetParagraphIndex() == aSelection.nEndPara )
+ {
+ // caret is always nEndPara,nEndPos
+ EBulletInfo aBulletInfo = GetTextForwarder().GetBulletInfo(GetParagraphIndex());
+ if( aBulletInfo.nParagraph != EE_PARA_NOT_FOUND &&
+ aBulletInfo.bVisible &&
+ aBulletInfo.nType != SVX_NUM_BITMAP )
+ {
+ sal_Int32 nBulletLen = aBulletInfo.aText.getLength();
+ if( aSelection.nEndPos - nBulletLen >= 0 )
+ return aSelection.nEndPos - nBulletLen;
+ }
+ return aSelection.nEndPos;
+ }
+
+ // not within this paragraph
+ return -1;
+ }
+
+ sal_Bool SAL_CALL AccessibleEditableTextPara::setCaretPosition( sal_Int32 nIndex )
+ {
+ return setSelection(nIndex, nIndex);
+ }
+
+ sal_Unicode SAL_CALL AccessibleEditableTextPara::getCharacter( sal_Int32 nIndex )
+ {
+ SolarMutexGuard aGuard;
+
+ DBG_ASSERT(GetParagraphIndex() >= 0,
+ "AccessibleEditableTextPara::getCharacter: index value overflow");
+
+ return OCommonAccessibleText::implGetCharacter( implGetText(), nIndex );
+ }
+
+ uno::Sequence< beans::PropertyValue > SAL_CALL AccessibleEditableTextPara::getCharacterAttributes( sal_Int32 nIndex, const css::uno::Sequence< OUString >& rRequestedAttributes )
+ {
+ SolarMutexGuard aGuard;
+
+ //Skip the bullet range to ignore the bullet text
+ SvxTextForwarder& rCacheTF = GetTextForwarder();
+ EBulletInfo aBulletInfo = rCacheTF.GetBulletInfo(GetParagraphIndex());
+ if (aBulletInfo.bVisible)
+ nIndex += aBulletInfo.aText.getLength();
+ CheckIndex(nIndex); // may throw IndexOutOfBoundsException
+
+ bool bSupplementalMode = false;
+ uno::Sequence< OUString > aPropertyNames = rRequestedAttributes;
+ if (!aPropertyNames.hasElements())
+ {
+ bSupplementalMode = true;
+ aPropertyNames = getAttributeNames();
+ }
+
+ // get default attributes...
+ ::comphelper::SequenceAsHashMap aPropHashMap( getDefaultAttributes( aPropertyNames ) );
+
+ // ... and override them with the direct attributes from the specific position
+ const uno::Sequence< beans::PropertyValue > aRunAttribs( getRunAttributes( nIndex, aPropertyNames ) );
+ for (auto const& rRunAttrib : aRunAttribs)
+ {
+ aPropHashMap[ rRunAttrib.Name ] = rRunAttrib.Value; //!! should not only be the value !!
+ }
+
+ // get resulting sequence
+ uno::Sequence< beans::PropertyValue > aRes;
+ aPropHashMap >> aRes;
+
+ // since SequenceAsHashMap ignores property handles and property state
+ // we have to restore the property state here (property handles are
+ // of no use to the accessibility API).
+ for (beans::PropertyValue & rRes : asNonConstRange(aRes))
+ {
+ bool bIsDirectVal = false;
+ for (auto const& rRunAttrib : aRunAttribs)
+ {
+ bIsDirectVal = rRes.Name == rRunAttrib.Name;
+ if (bIsDirectVal)
+ break;
+ }
+ rRes.Handle = -1;
+ rRes.State = bIsDirectVal ? PropertyState_DIRECT_VALUE : PropertyState_DEFAULT_VALUE;
+ }
+ if( bSupplementalMode )
+ {
+ _correctValues( aRes );
+ // NumberingPrefix
+ sal_Int32 nRes = aRes.getLength();
+ aRes.realloc( nRes + 1 );
+ beans::PropertyValue &rRes = aRes.getArray()[nRes];
+ rRes.Name = "NumberingPrefix";
+ OUString numStr;
+ if (aBulletInfo.nType != SVX_NUM_CHAR_SPECIAL && aBulletInfo.nType != SVX_NUM_BITMAP)
+ numStr = aBulletInfo.aText;
+ rRes.Value <<= numStr;
+ rRes.Handle = -1;
+ rRes.State = PropertyState_DIRECT_VALUE;
+ //For field object.
+ OUString strFieldType = GetFieldTypeNameAtIndex(nIndex);
+ if (!strFieldType.isEmpty())
+ {
+ nRes = aRes.getLength();
+ aRes.realloc( nRes + 1 );
+ beans::PropertyValue &rResField = aRes.getArray()[nRes];
+ rResField.Name = "FieldType";
+ rResField.Value <<= strFieldType.toAsciiLowerCase();
+ rResField.Handle = -1;
+ rResField.State = PropertyState_DIRECT_VALUE;
+ }
+ //sort property values
+ // build sorted index array
+ sal_Int32 nLength = aRes.getLength();
+ const beans::PropertyValue* pPairs = aRes.getConstArray();
+ std::unique_ptr<sal_Int32[]> pIndices(new sal_Int32[nLength]);
+ sal_Int32 i = 0;
+ for( i = 0; i < nLength; i++ )
+ pIndices[i] = i;
+ std::sort( &pIndices[0], &pIndices[nLength], IndexCompare(pPairs) );
+ // create sorted sequences according to index array
+ uno::Sequence<beans::PropertyValue> aNewValues( nLength );
+ beans::PropertyValue* pNewValues = aNewValues.getArray();
+ for( i = 0; i < nLength; i++ )
+ {
+ pNewValues[i] = pPairs[pIndices[i]];
+ }
+
+ return aNewValues;
+ }
+ return aRes;
+ }
+
+ awt::Rectangle SAL_CALL AccessibleEditableTextPara::getCharacterBounds( sal_Int32 nIndex )
+ {
+ SolarMutexGuard aGuard;
+
+ DBG_ASSERT(GetParagraphIndex() >= 0,
+ "AccessibleEditableTextPara::getCharacterBounds: index value overflow");
+
+ // #108900# Have position semantics now for nIndex, as
+ // one-past-the-end values are legal, too.
+ CheckPosition( nIndex );
+
+ SvxTextForwarder& rCacheTF = GetTextForwarder();
+ tools::Rectangle aRect = rCacheTF.GetCharBounds(GetParagraphIndex(), nIndex);
+
+ // convert to screen
+ tools::Rectangle aScreenRect = AccessibleEditableTextPara::LogicToPixel( aRect,
+ rCacheTF.GetMapMode(),
+ GetViewForwarder() );
+ // #109864# offset from parent (paragraph), but in screen
+ // coordinates. This makes sure the internal text offset in
+ // the outline view forwarder gets cancelled out here
+ awt::Rectangle aParaRect( getBounds() );
+ aScreenRect.Move( -aParaRect.X, -aParaRect.Y );
+
+ // offset from shape/cell
+ Point aOffset = GetEEOffset();
+
+ return awt::Rectangle( aScreenRect.Left() + aOffset.X(),
+ aScreenRect.Top() + aOffset.Y(),
+ aScreenRect.GetSize().Width(),
+ aScreenRect.GetSize().Height() );
+ }
+
+ sal_Int32 SAL_CALL AccessibleEditableTextPara::getCharacterCount()
+ {
+ SolarMutexGuard aGuard;
+
+ DBG_ASSERT(GetParagraphIndex() >= 0,
+ "AccessibleEditableTextPara::getCharacterCount: index value overflow");
+
+ return implGetText().getLength();
+ }
+
+ sal_Int32 SAL_CALL AccessibleEditableTextPara::getIndexAtPoint( const awt::Point& rPoint )
+ {
+ SolarMutexGuard aGuard;
+
+ sal_Int32 nPara;
+ sal_Int32 nIndex;
+
+ // offset from surrounding cell/shape
+ Point aOffset( GetEEOffset() );
+ Point aPoint( rPoint.X - aOffset.X(), rPoint.Y - aOffset.Y() );
+
+ // convert to logical coordinates
+ SvxTextForwarder& rCacheTF = GetTextForwarder();
+ Point aLogPoint( GetViewForwarder().PixelToLogic( aPoint, rCacheTF.GetMapMode() ) );
+
+ // re-offset to parent (paragraph)
+ tools::Rectangle aParaRect = rCacheTF.GetParaBounds( GetParagraphIndex() );
+ aLogPoint.Move( aParaRect.Left(), aParaRect.Top() );
+
+ if( rCacheTF.GetIndexAtPoint( aLogPoint, nPara, nIndex ) &&
+ GetParagraphIndex() == nPara )
+ {
+ // #102259# Double-check if we're _really_ on the given character
+ try
+ {
+ awt::Rectangle aRect1( getCharacterBounds(nIndex) );
+ tools::Rectangle aRect2( aRect1.X, aRect1.Y,
+ aRect1.Width + aRect1.X, aRect1.Height + aRect1.Y );
+ if( aRect2.Contains( Point( rPoint.X, rPoint.Y ) ) )
+ return nIndex;
+ else
+ return -1;
+ }
+ catch (const lang::IndexOutOfBoundsException&)
+ {
+ // #103927# Don't throw for invalid nIndex values
+ return -1;
+ }
+ }
+ else
+ {
+ // not within our paragraph
+ return -1;
+ }
+ }
+
+ OUString SAL_CALL AccessibleEditableTextPara::getSelectedText()
+ {
+ SolarMutexGuard aGuard;
+
+ DBG_ASSERT(GetParagraphIndex() >= 0,
+ "AccessibleEditableTextPara::getSelectedText: index value overflow");
+
+ if( !HaveEditView() )
+ return OUString();
+
+ return OCommonAccessibleText::getSelectedText();
+ }
+
+ sal_Int32 SAL_CALL AccessibleEditableTextPara::getSelectionStart()
+ {
+ SolarMutexGuard aGuard;
+
+ DBG_ASSERT(GetParagraphIndex() >= 0,
+ "AccessibleEditableTextPara::getSelectionStart: index value overflow");
+
+ if( !HaveEditView() )
+ return -1;
+
+ return OCommonAccessibleText::getSelectionStart();
+ }
+
+ sal_Int32 SAL_CALL AccessibleEditableTextPara::getSelectionEnd()
+ {
+ SolarMutexGuard aGuard;
+
+ DBG_ASSERT(GetParagraphIndex() >= 0,
+ "AccessibleEditableTextPara::getSelectionEnd: index value overflow");
+
+ if( !HaveEditView() )
+ return -1;
+
+ return OCommonAccessibleText::getSelectionEnd();
+ }
+
+ sal_Bool SAL_CALL AccessibleEditableTextPara::setSelection( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
+ {
+ SolarMutexGuard aGuard;
+
+ DBG_ASSERT(GetParagraphIndex() >= 0,
+ "AccessibleEditableTextPara::setSelection: paragraph index value overflow");
+
+ CheckRange(nStartIndex, nEndIndex);
+
+ try
+ {
+ SvxEditViewForwarder& rCacheVF = GetEditViewForwarder( true );
+ return rCacheVF.SetSelection( MakeSelection(nStartIndex, nEndIndex) );
+ }
+ catch (const uno::RuntimeException&)
+ {
+ return false;
+ }
+ }
+
+ OUString SAL_CALL AccessibleEditableTextPara::getText()
+ {
+ SolarMutexGuard aGuard;
+
+ DBG_ASSERT(GetParagraphIndex() >= 0,
+ "AccessibleEditableTextPara::getText: paragraph index value overflow");
+
+ return implGetText();
+ }
+
+ OUString SAL_CALL AccessibleEditableTextPara::getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
+ {
+ SolarMutexGuard aGuard;
+
+ DBG_ASSERT(GetParagraphIndex() >= 0,
+ "AccessibleEditableTextPara::getTextRange: paragraph index value overflow");
+
+ return OCommonAccessibleText::implGetTextRange(implGetText(), nStartIndex, nEndIndex);
+ }
+
+ void AccessibleEditableTextPara::_correctValues( uno::Sequence< PropertyValue >& rValues)
+ {
+ SvxTextForwarder& rCacheTF = GetTextForwarder();
+ sal_Int32 nRes = rValues.getLength();
+ beans::PropertyValue *pRes = rValues.getArray();
+ for (sal_Int32 i = 0; i < nRes; ++i)
+ {
+ beans::PropertyValue &rRes = pRes[i];
+ // Char color
+ if (rRes.Name == "CharColor")
+ {
+ uno::Any &anyChar = rRes.Value;
+ Color crChar(ColorTransparency, static_cast<sal_uInt32>( reinterpret_cast<sal_uIntPtr>(anyChar.pReserved)));
+ if (COL_AUTO == crChar )
+ {
+ uno::Reference< css::accessibility::XAccessibleComponent > xComponent(mxParent,uno::UNO_QUERY);
+ if (xComponent.is())
+ {
+ uno::Reference< css::accessibility::XAccessibleContext > xContext(xComponent,uno::UNO_QUERY);
+ if (xContext->getAccessibleRole() == AccessibleRole::SHAPE
+ || xContext->getAccessibleRole() == AccessibleRole::TABLE_CELL)
+ {
+ anyChar <<= COL_BLACK;
+ }
+ else
+ {
+ Color cr(ColorTransparency, xComponent->getBackground());
+ crChar = cr.IsDark() ? COL_WHITE : COL_BLACK;
+ anyChar <<= crChar;
+ }
+ }
+ }
+ continue;
+ }
+ // Underline
+ if (rRes.Name == "CharUnderline")
+ {
+ continue;
+ }
+ // Underline color && Mis-spell
+ if (rRes.Name == "CharUnderlineColor")
+ {
+ uno::Any &anyCharUnderLine = rRes.Value;
+ Color crCharUnderLine(ColorTransparency, static_cast<sal_uInt32>( reinterpret_cast<sal_uIntPtr>( anyCharUnderLine.pReserved)));
+ if (COL_AUTO == crCharUnderLine )
+ {
+ uno::Reference< css::accessibility::XAccessibleComponent > xComponent(mxParent,uno::UNO_QUERY);
+ if (xComponent.is())
+ {
+ uno::Reference< css::accessibility::XAccessibleContext > xContext(xComponent,uno::UNO_QUERY);
+ if (xContext->getAccessibleRole() == AccessibleRole::SHAPE
+ || xContext->getAccessibleRole() == AccessibleRole::TABLE_CELL)
+ {
+ anyCharUnderLine <<= COL_BLACK;
+ }
+ else
+ {
+ Color cr(ColorTransparency, xComponent->getBackground());
+ crCharUnderLine = cr.IsDark() ? COL_WHITE : COL_BLACK;
+ anyCharUnderLine <<= crCharUnderLine;
+ }
+ }
+ }
+ continue;
+ }
+ // NumberingLevel
+ if (rRes.Name == "NumberingLevel")
+ {
+ if(rCacheTF.GetParaAttribs(GetParagraphIndex()).Get(EE_PARA_NUMBULLET).GetNumRule().GetLevelCount()==0)
+ {
+ rRes.Value <<= sal_Int16(-1);
+ rRes.Handle = -1;
+ rRes.State = PropertyState_DIRECT_VALUE;
+ }
+ else
+ {
+// SvxAccessibleTextPropertySet aPropSet( &GetEditSource(),
+// ImplGetSvxCharAndParaPropertiesMap() );
+ // MT IA2 TODO: Check if this is the correct replacement for ImplGetSvxCharAndParaPropertiesMap
+ rtl::Reference< SvxAccessibleTextPropertySet > xPropSet( new SvxAccessibleTextPropertySet( &GetEditSource(), ImplGetSvxTextPortionSvxPropertySet() ) );
+
+ xPropSet->SetSelection( MakeSelection( 0, GetTextLen() ) );
+ rRes.Value = xPropSet->_getPropertyValue( rRes.Name, mnParagraphIndex );
+ rRes.State = xPropSet->_getPropertyState( rRes.Name, mnParagraphIndex );
+ rRes.Handle = -1;
+ }
+ continue;
+ }
+ // NumberingRules
+ if (rRes.Name == "NumberingRules")
+ {
+ SfxItemSet aAttribs = rCacheTF.GetParaAttribs(GetParagraphIndex());
+ bool bVis = aAttribs.Get( EE_PARA_BULLETSTATE ).GetValue();
+ if(bVis)
+ {
+ rRes.Value <<= sal_Int16(-1);
+ rRes.Handle = -1;
+ rRes.State = PropertyState_DIRECT_VALUE;
+ }
+ else
+ {
+ // MT IA2 TODO: Check if this is the correct replacement for ImplGetSvxCharAndParaPropertiesMap
+ rtl::Reference< SvxAccessibleTextPropertySet > xPropSet( new SvxAccessibleTextPropertySet( &GetEditSource(), ImplGetSvxTextPortionSvxPropertySet() ) );
+ xPropSet->SetSelection( MakeSelection( 0, GetTextLen() ) );
+ rRes.Value = xPropSet->_getPropertyValue( rRes.Name, mnParagraphIndex );
+ rRes.State = xPropSet->_getPropertyState( rRes.Name, mnParagraphIndex );
+ rRes.Handle = -1;
+ }
+ continue;
+ }
+ }
+ }
+ sal_Int32 AccessibleEditableTextPara::SkipField(sal_Int32 nIndex, bool bForward)
+ {
+ sal_Int32 nParaIndex = GetParagraphIndex();
+ SvxAccessibleTextAdapter& rCacheTF = GetTextForwarder();
+ sal_Int32 nAllFieldLen = 0;
+ sal_Int32 nField = rCacheTF.GetFieldCount(nParaIndex), nFoundFieldIndex = -1;
+ sal_Int32 reeBegin=0, reeEnd=0;
+ for (sal_Int32 j = 0; j < nField; ++j)
+ {
+ EFieldInfo ree = rCacheTF.GetFieldInfo(nParaIndex, j);
+ reeBegin = ree.aPosition.nIndex + nAllFieldLen;
+ reeEnd = reeBegin + ree.aCurrentText.getLength();
+ nAllFieldLen += (ree.aCurrentText.getLength() - 1);
+ if (nIndex < reeBegin)
+ break;
+ if (!ree.pFieldItem)
+ continue;
+ if (nIndex < reeEnd)
+ {
+ if (ree.pFieldItem->GetField()->GetClassId() != text::textfield::Type::URL)
+ {
+ nFoundFieldIndex = j;
+ break;
+ }
+ }
+ }
+ if( nFoundFieldIndex >= 0 )
+ {
+ if( bForward )
+ return reeEnd - 1;
+ else
+ return reeBegin;
+ }
+ return nIndex;
+ }
+ void AccessibleEditableTextPara::ExtendByField( css::accessibility::TextSegment& Segment )
+ {
+ sal_Int32 nParaIndex = GetParagraphIndex();
+ SvxAccessibleTextAdapter& rCacheTF = GetTextForwarder();
+ sal_Int32 nAllFieldLen = 0;
+ sal_Int32 nField = rCacheTF.GetFieldCount(nParaIndex), nFoundFieldIndex = -1;
+ sal_Int32 reeBegin=0, reeEnd=0;
+ for (sal_Int32 j = 0; j < nField; ++j)
+ {
+ EFieldInfo ree = rCacheTF.GetFieldInfo(nParaIndex, j);
+ reeBegin = ree.aPosition.nIndex + nAllFieldLen;
+ reeEnd = reeBegin + ree.aCurrentText.getLength();
+ nAllFieldLen += (ree.aCurrentText.getLength() - 1);
+ if( reeBegin > Segment.SegmentEnd )
+ {
+ break;
+ }
+ if (!ree.pFieldItem)
+ continue;
+ if( (Segment.SegmentEnd > reeBegin && Segment.SegmentEnd <= reeEnd) ||
+ (Segment.SegmentStart >= reeBegin && Segment.SegmentStart < reeEnd) )
+ {
+ if(ree.pFieldItem->GetField()->GetClassId() != text::textfield::Type::URL)
+ {
+ nFoundFieldIndex = j;
+ break;
+ }
+ }
+ }
+ if( nFoundFieldIndex < 0 )
+ return;
+
+ bool bExtend = false;
+ if( Segment.SegmentEnd < reeEnd )
+ {
+ Segment.SegmentEnd = reeEnd;
+ bExtend = true;
+ }
+ if( Segment.SegmentStart > reeBegin )
+ {
+ Segment.SegmentStart = reeBegin;
+ bExtend = true;
+ }
+ if( !bExtend )
+ return;
+
+ //If there is a bullet before the field, should add the bullet length into the segment.
+ EBulletInfo aBulletInfo = rCacheTF.GetBulletInfo(nParaIndex);
+ sal_Int32 nBulletLen = aBulletInfo.aText.getLength();
+ if (nBulletLen > 0)
+ {
+ Segment.SegmentEnd += nBulletLen;
+ if (nFoundFieldIndex > 0)
+ Segment.SegmentStart += nBulletLen;
+ Segment.SegmentText = GetTextRange(Segment.SegmentStart, Segment.SegmentEnd);
+ //After get the correct field name, should restore the offset value which don't contain the bullet.
+ Segment.SegmentEnd -= nBulletLen;
+ if (nFoundFieldIndex > 0)
+ Segment.SegmentStart -= nBulletLen;
+ }
+ else
+ Segment.SegmentText = GetTextRange(Segment.SegmentStart, Segment.SegmentEnd);
+ }
+
+ css::accessibility::TextSegment SAL_CALL AccessibleEditableTextPara::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType )
+ {
+ SolarMutexGuard aGuard;
+
+ DBG_ASSERT(GetParagraphIndex() >= 0,
+ "AccessibleEditableTextPara::getTextAtIndex: paragraph index value overflow");
+
+ css::accessibility::TextSegment aResult;
+ aResult.SegmentStart = -1;
+ aResult.SegmentEnd = -1;
+
+ switch( aTextType )
+ {
+ case AccessibleTextType::CHARACTER:
+ case AccessibleTextType::WORD:
+ {
+ aResult = OCommonAccessibleText::getTextAtIndex( nIndex, aTextType );
+ ExtendByField( aResult );
+ break;
+ }
+ // Not yet handled by OCommonAccessibleText. Missing
+ // implGetAttributeRunBoundary() method there
+ case AccessibleTextType::ATTRIBUTE_RUN:
+ {
+ const sal_Int32 nTextLen = GetTextForwarder().GetTextLen( GetParagraphIndex() );
+
+ if( nIndex == nTextLen )
+ {
+ // #i17014# Special-casing one-behind-the-end character
+ aResult.SegmentStart = aResult.SegmentEnd = nTextLen;
+ }
+ else
+ {
+ sal_Int32 nStartIndex, nEndIndex;
+ //For the bullet paragraph, the bullet string is ignored for IAText::attributes() function.
+ SvxTextForwarder& rCacheTF = GetTextForwarder();
+ // MT IA2: Not used? sal_Int32 nBulletLen = 0;
+ EBulletInfo aBulletInfo = rCacheTF.GetBulletInfo(GetParagraphIndex());
+ if (aBulletInfo.bVisible)
+ nIndex += aBulletInfo.aText.getLength();
+ if (nIndex != 0 && nIndex >= getCharacterCount())
+ nIndex = getCharacterCount()-1;
+ CheckPosition(nIndex);
+ if( GetAttributeRun(nStartIndex, nEndIndex, nIndex) )
+ {
+ aResult.SegmentText = GetTextRange(nStartIndex, nEndIndex);
+ if (aBulletInfo.bVisible)
+ {
+ nStartIndex -= aBulletInfo.aText.getLength();
+ nEndIndex -= aBulletInfo.aText.getLength();
+ }
+ aResult.SegmentStart = nStartIndex;
+ aResult.SegmentEnd = nEndIndex;
+ }
+ }
+ break;
+ }
+ case AccessibleTextType::LINE:
+ {
+ SvxTextForwarder& rCacheTF = GetTextForwarder();
+ sal_Int32 nParaIndex = GetParagraphIndex();
+ CheckPosition(nIndex);
+ if (nIndex != 0 && nIndex == getCharacterCount())
+ --nIndex;
+ sal_Int32 nLine, nLineCount=rCacheTF.GetLineCount( nParaIndex );
+ sal_Int32 nCurIndex;
+ //the problem is that rCacheTF.GetLineLen() will include the bullet length. But for the bullet line,
+ //the text value doesn't contain the bullet characters. all of the bullet and numbering info are exposed
+ //by the IAText::attributes(). So here must do special support for bullet line.
+ sal_Int32 nBulletLen = 0;
+ for( nLine=0, nCurIndex=0; nLine<nLineCount; ++nLine )
+ {
+ if (nLine == 0)
+ {
+ EBulletInfo aBulletInfo = rCacheTF.GetBulletInfo( nParaIndex );
+ if (aBulletInfo.bVisible)
+ {
+ //in bullet or numbering;
+ nBulletLen = aBulletInfo.aText.getLength();
+ }
+ }
+ sal_Int32 nLineLen = rCacheTF.GetLineLen(nParaIndex, nLine);
+ if (nLine == 0)
+ nCurIndex += nLineLen - nBulletLen;
+ else
+ nCurIndex += nLineLen;
+ if( nCurIndex > nIndex )
+ {
+ if (nLine ==0)
+ {
+ aResult.SegmentStart = 0;
+ aResult.SegmentEnd = nCurIndex;
+ aResult.SegmentText = GetTextRange( aResult.SegmentStart, aResult.SegmentEnd + nBulletLen);
+ break;
+ }
+ else
+ {
+ aResult.SegmentStart = nCurIndex - nLineLen;
+ aResult.SegmentEnd = nCurIndex;
+ //aResult.SegmentText = GetTextRange( aResult.SegmentStart, aResult.SegmentEnd );
+ aResult.SegmentText = GetTextRange( aResult.SegmentStart + nBulletLen, aResult.SegmentEnd + nBulletLen);
+ break;
+ }
+ }
+ }
+ break;
+ }
+ default:
+ aResult = OCommonAccessibleText::getTextAtIndex( nIndex, aTextType );
+ break;
+ } /* end of switch( aTextType ) */
+
+ return aResult;
+ }
+
+ css::accessibility::TextSegment SAL_CALL AccessibleEditableTextPara::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType )
+ {
+ SolarMutexGuard aGuard;
+
+ DBG_ASSERT(GetParagraphIndex() >= 0,
+ "AccessibleEditableTextPara::getTextBeforeIndex: paragraph index value overflow");
+
+ css::accessibility::TextSegment aResult;
+ aResult.SegmentStart = -1;
+ aResult.SegmentEnd = -1;
+ i18n::Boundary aBoundary;
+ switch( aTextType )
+ {
+ // Not yet handled by OCommonAccessibleText. Missing
+ // implGetAttributeRunBoundary() method there
+ case AccessibleTextType::ATTRIBUTE_RUN:
+ {
+ const sal_Int32 nTextLen = GetTextForwarder().GetTextLen( GetParagraphIndex() );
+ sal_Int32 nStartIndex, nEndIndex;
+
+ if( nIndex == nTextLen )
+ {
+ // #i17014# Special-casing one-behind-the-end character
+ if( nIndex > 0 &&
+ GetAttributeRun(nStartIndex, nEndIndex, nIndex-1) )
+ {
+ aResult.SegmentText = GetTextRange(nStartIndex, nEndIndex);
+ aResult.SegmentStart = nStartIndex;
+ aResult.SegmentEnd = nEndIndex;
+ }
+ }
+ else
+ {
+ if( GetAttributeRun(nStartIndex, nEndIndex, nIndex) )
+ {
+ // already at the left border? If not, query
+ // one index further left
+ if( nStartIndex > 0 &&
+ GetAttributeRun(nStartIndex, nEndIndex, nStartIndex-1) )
+ {
+ aResult.SegmentText = GetTextRange(nStartIndex, nEndIndex);
+ aResult.SegmentStart = nStartIndex;
+ aResult.SegmentEnd = nEndIndex;
+ }
+ }
+ }
+ break;
+ }
+ case AccessibleTextType::LINE:
+ {
+ SvxTextForwarder& rCacheTF = GetTextForwarder();
+ sal_Int32 nParaIndex = GetParagraphIndex();
+
+ CheckPosition(nIndex);
+
+ sal_Int32 nLine, nLineCount=rCacheTF.GetLineCount( nParaIndex );
+ //the problem is that rCacheTF.GetLineLen() will include the bullet length. But for the bullet line,
+ //the text value doesn't contain the bullet characters. all of the bullet and numbering info are exposed
+ //by the IAText::attributes(). So here must do special support for bullet line.
+ sal_Int32 nCurIndex=0, nLastIndex=0, nCurLineLen=0;
+ sal_Int32 nLastLineLen = 0, nBulletLen = 0;
+ // get the line before the line the index points into
+ for( nLine=0, nCurIndex=0; nLine<nLineCount; ++nLine )
+ {
+ nLastIndex = nCurIndex;
+ if (nLine == 0)
+ {
+ EBulletInfo aBulletInfo = rCacheTF.GetBulletInfo(nParaIndex);
+ if (aBulletInfo.bVisible)
+ {
+ //in bullet or numbering;
+ nBulletLen = aBulletInfo.aText.getLength();
+ }
+ }
+ if (nLine == 1)
+ nLastLineLen = nCurLineLen - nBulletLen;
+ else
+ nLastLineLen = nCurLineLen;
+ nCurLineLen = rCacheTF.GetLineLen( nParaIndex, nLine);
+ //nCurIndex += nCurLineLen;
+ if (nLine == 0)
+ nCurIndex += nCurLineLen - nBulletLen;
+ else
+ nCurIndex += nCurLineLen;
+
+ //if( nCurIndex > nIndex &&
+ //nLastIndex > nCurLineLen )
+ if (nCurIndex > nIndex)
+ {
+ if (nLine == 0)
+ {
+ break;
+ }
+ else if (nLine == 1)
+ {
+ aResult.SegmentStart = 0;
+ aResult.SegmentEnd = nLastIndex;
+ aResult.SegmentText = GetTextRange( aResult.SegmentStart, aResult.SegmentEnd + nBulletLen);
+ break;
+ }
+ else
+ {
+ //aResult.SegmentStart = nLastIndex - nCurLineLen;
+ aResult.SegmentStart = nLastIndex - nLastLineLen;
+ aResult.SegmentEnd = nLastIndex;
+ aResult.SegmentText = GetTextRange( aResult.SegmentStart + nBulletLen, aResult.SegmentEnd + nBulletLen);
+ break;
+ }
+ }
+ }
+
+ break;
+ }
+ case AccessibleTextType::WORD:
+ {
+ nIndex = SkipField( nIndex, false);
+ OUString sText( implGetText() );
+ sal_Int32 nLength = sText.getLength();
+
+ // get word at index
+ implGetWordBoundary( sText, aBoundary, nIndex );
+
+
+ //sal_Int32 curWordStart = aBoundary.startPos;
+ //sal_Int32 preWordStart = curWordStart;
+ sal_Int32 curWordStart , preWordStart;
+ if( aBoundary.startPos == -1 || aBoundary.startPos > nIndex)
+ curWordStart = preWordStart = nIndex;
+ else
+ curWordStart = preWordStart = aBoundary.startPos;
+
+ // get previous word
+
+ bool bWord = false;
+
+ //while ( preWordStart > 0 && aBoundary.startPos == curWordStart)
+ while ( (preWordStart >= 0 && !bWord ) || ( aBoundary.endPos > curWordStart ) )
+ {
+ preWordStart--;
+ bWord = implGetWordBoundary( sText, aBoundary, preWordStart );
+ }
+ if ( bWord && implIsValidBoundary( aBoundary, nLength ) )
+ {
+ aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
+ aResult.SegmentStart = aBoundary.startPos;
+ aResult.SegmentEnd = aBoundary.endPos;
+ ExtendByField( aResult );
+ }
+ }
+ break;
+ case AccessibleTextType::CHARACTER:
+ {
+ nIndex = SkipField( nIndex, false);
+ aResult = OCommonAccessibleText::getTextBeforeIndex( nIndex, aTextType );
+ ExtendByField( aResult );
+ break;
+ }
+ default:
+ aResult = OCommonAccessibleText::getTextBeforeIndex( nIndex, aTextType );
+ break;
+ } /* end of switch( aTextType ) */
+
+ return aResult;
+ }
+
+ css::accessibility::TextSegment SAL_CALL AccessibleEditableTextPara::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType )
+ {
+ SolarMutexGuard aGuard;
+
+ DBG_ASSERT(GetParagraphIndex() >= 0,
+ "AccessibleEditableTextPara::getTextBehindIndex: paragraph index value overflow");
+
+ css::accessibility::TextSegment aResult;
+ aResult.SegmentStart = -1;
+ aResult.SegmentEnd = -1;
+ i18n::Boundary aBoundary;
+ switch( aTextType )
+ {
+ case AccessibleTextType::ATTRIBUTE_RUN:
+ {
+ sal_Int32 nStartIndex, nEndIndex;
+
+ if( GetAttributeRun(nStartIndex, nEndIndex, nIndex) )
+ {
+ // already at the right border?
+ if( nEndIndex < GetTextLen() )
+ {
+ if( GetAttributeRun(nStartIndex, nEndIndex, nEndIndex) )
+ {
+ aResult.SegmentText = GetTextRange(nStartIndex, nEndIndex);
+ aResult.SegmentStart = nStartIndex;
+ aResult.SegmentEnd = nEndIndex;
+ }
+ }
+ }
+ break;
+ }
+
+ case AccessibleTextType::LINE:
+ {
+ SvxTextForwarder& rCacheTF = GetTextForwarder();
+ sal_Int32 nParaIndex = GetParagraphIndex();
+
+ CheckPosition(nIndex);
+
+ sal_Int32 nLine, nLineCount = rCacheTF.GetLineCount( nParaIndex );
+ sal_Int32 nCurIndex;
+ //the problem is that rCacheTF.GetLineLen() will include the bullet length. But for the bullet line,
+ //the text value doesn't contain the bullet characters. all of the bullet and numbering info are exposed
+ //by the IAText::attributes(). So here must do special support for bullet line.
+ sal_Int32 nBulletLen = 0;
+ // get the line after the line the index points into
+ for( nLine=0, nCurIndex=0; nLine<nLineCount; ++nLine )
+ {
+ if (nLine == 0)
+ {
+ EBulletInfo aBulletInfo = rCacheTF.GetBulletInfo(nParaIndex);
+ if (aBulletInfo.bVisible)
+ {
+ //in bullet or numbering;
+ nBulletLen = aBulletInfo.aText.getLength();
+ }
+ }
+ sal_Int32 nLineLen = rCacheTF.GetLineLen( nParaIndex, nLine);
+
+ if (nLine == 0)
+ nCurIndex += nLineLen - nBulletLen;
+ else
+ nCurIndex += nLineLen;
+
+ if( nCurIndex > nIndex &&
+ nLine < nLineCount-1 )
+ {
+ aResult.SegmentStart = nCurIndex;
+ aResult.SegmentEnd = nCurIndex + rCacheTF.GetLineLen( nParaIndex, nLine+1);
+ aResult.SegmentText = GetTextRange( aResult.SegmentStart + nBulletLen, aResult.SegmentEnd + nBulletLen);
+ break;
+ }
+ }
+
+ break;
+ }
+ case AccessibleTextType::WORD:
+ {
+ nIndex = SkipField( nIndex, true);
+ OUString sText( implGetText() );
+ sal_Int32 nLength = sText.getLength();
+
+ // get word at index
+ bool bWord = implGetWordBoundary( sText, aBoundary, nIndex );
+
+ // real current world
+ sal_Int32 nextWord = nIndex;
+ //if( nIndex >= aBoundary.startPos && nIndex <= aBoundary.endPos )
+ if( nIndex <= aBoundary.endPos )
+ {
+ nextWord = aBoundary.endPos;
+ if (nextWord < sText.getLength() && sText[nextWord] == u' ') nextWord++;
+ bWord = implGetWordBoundary( sText, aBoundary, nextWord );
+ }
+
+ if ( bWord && implIsValidBoundary( aBoundary, nLength ) )
+ {
+ aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos );
+ aResult.SegmentStart = aBoundary.startPos;
+ aResult.SegmentEnd = aBoundary.endPos;
+
+ // If the end position of aBoundary is inside a field, extend the result to the end of the field
+
+ ExtendByField( aResult );
+ }
+ }
+ break;
+
+ case AccessibleTextType::CHARACTER:
+ {
+ nIndex = SkipField( nIndex, true);
+ aResult = OCommonAccessibleText::getTextBehindIndex( nIndex, aTextType );
+ ExtendByField( aResult );
+ break;
+ }
+ default:
+ aResult = OCommonAccessibleText::getTextBehindIndex( nIndex, aTextType );
+ break;
+ } /* end of switch( aTextType ) */
+
+ return aResult;
+ }
+
+ sal_Bool SAL_CALL AccessibleEditableTextPara::copyText( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
+ {
+ SolarMutexGuard aGuard;
+
+ try
+ {
+ SvxEditViewForwarder& rCacheVF = GetEditViewForwarder( true );
+ GetTextForwarder(); // MUST be after GetEditViewForwarder(), see method docs
+
+ bool aRetVal;
+
+ DBG_ASSERT(GetParagraphIndex() >= 0,
+ "AccessibleEditableTextPara::copyText: index value overflow");
+
+ CheckRange(nStartIndex, nEndIndex);
+
+ //Because bullet may occupy one or more characters, the TextAdapter will include bullet to calculate the selection. Add offset to handle bullet
+ sal_Int32 nBulletLen = 0;
+ EBulletInfo aBulletInfo = GetTextForwarder().GetBulletInfo(GetParagraphIndex());
+ if( aBulletInfo.nParagraph != EE_PARA_NOT_FOUND && aBulletInfo.bVisible )
+ nBulletLen = aBulletInfo.aText.getLength();
+ // save current selection
+ ESelection aOldSelection;
+
+ rCacheVF.GetSelection( aOldSelection );
+ //rCacheVF.SetSelection( MakeSelection(nStartIndex, nEndIndex) );
+ rCacheVF.SetSelection( MakeSelection(nStartIndex + nBulletLen, nEndIndex + nBulletLen) );
+ aRetVal = rCacheVF.Copy();
+ rCacheVF.SetSelection( aOldSelection ); // restore
+
+ return aRetVal;
+ }
+ catch (const uno::RuntimeException&)
+ {
+ return false;
+ }
+ }
+
+ sal_Bool SAL_CALL AccessibleEditableTextPara::scrollSubstringTo( sal_Int32, sal_Int32, AccessibleScrollType )
+ {
+ return false;
+ }
+
+ // XAccessibleEditableText
+ sal_Bool SAL_CALL AccessibleEditableTextPara::cutText( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
+ {
+
+ SolarMutexGuard aGuard;
+
+ try
+ {
+ SvxEditViewForwarder& rCacheVF = GetEditViewForwarder( true );
+ SvxAccessibleTextAdapter& rCacheTF = GetTextForwarder(); // MUST be after GetEditViewForwarder(), see method docs
+
+ DBG_ASSERT(GetParagraphIndex() >= 0,
+ "AccessibleEditableTextPara::cutText: index value overflow");
+
+ CheckRange(nStartIndex, nEndIndex);
+
+ // Because bullet may occupy one or more characters, the TextAdapter will include bullet to calculate the selection. Add offset to handle bullet
+ sal_Int32 nBulletLen = 0;
+ EBulletInfo aBulletInfo = GetTextForwarder().GetBulletInfo(GetParagraphIndex());
+ if( aBulletInfo.nParagraph != EE_PARA_NOT_FOUND && aBulletInfo.bVisible )
+ nBulletLen = aBulletInfo.aText.getLength();
+ ESelection aSelection = MakeSelection (nStartIndex + nBulletLen, nEndIndex + nBulletLen);
+ //if( !rCacheTF.IsEditable( MakeSelection(nStartIndex, nEndIndex) ) )
+ if( !rCacheTF.IsEditable( aSelection ) )
+ return false; // non-editable area selected
+
+ // don't save selection, might become invalid after cut!
+ //rCacheVF.SetSelection( MakeSelection(nStartIndex, nEndIndex) );
+ rCacheVF.SetSelection( aSelection );
+
+ return rCacheVF.Cut();
+ }
+ catch (const uno::RuntimeException&)
+ {
+ return false;
+ }
+ }
+
+ sal_Bool SAL_CALL AccessibleEditableTextPara::pasteText( sal_Int32 nIndex )
+ {
+
+ SolarMutexGuard aGuard;
+
+ try
+ {
+ SvxEditViewForwarder& rCacheVF = GetEditViewForwarder( true );
+ SvxAccessibleTextAdapter& rCacheTF = GetTextForwarder(); // MUST be after GetEditViewForwarder(), see method docs
+
+ DBG_ASSERT(GetParagraphIndex() >= 0,
+ "AccessibleEditableTextPara::pasteText: index value overflow");
+
+ CheckPosition(nIndex);
+
+ // Because bullet may occupy one or more characters, the TextAdapter will include bullet to calculate the selection. Add offset to handle bullet
+ sal_Int32 nBulletLen = 0;
+ EBulletInfo aBulletInfo = GetTextForwarder().GetBulletInfo(GetParagraphIndex());
+ if( aBulletInfo.nParagraph != EE_PARA_NOT_FOUND && aBulletInfo.bVisible )
+ nBulletLen = aBulletInfo.aText.getLength();
+ if( !rCacheTF.IsEditable( MakeSelection(nIndex + nBulletLen) ) )
+ return false; // non-editable area selected
+
+ // #104400# set empty selection (=> cursor) to given index
+ //rCacheVF.SetSelection( MakeCursor(nIndex) );
+ rCacheVF.SetSelection( MakeCursor(nIndex + nBulletLen) );
+
+ return rCacheVF.Paste();
+ }
+ catch (const uno::RuntimeException&)
+ {
+ return false;
+ }
+ }
+
+ sal_Bool SAL_CALL AccessibleEditableTextPara::deleteText( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
+ {
+
+ SolarMutexGuard aGuard;
+
+ try
+ {
+ // #102710# Request edit view when doing changes
+ // AccessibleEmptyEditSource relies on this behaviour
+ GetEditViewForwarder( true );
+ SvxAccessibleTextAdapter& rCacheTF = GetTextForwarder(); // MUST be after GetEditViewForwarder(), see method docs
+
+ DBG_ASSERT(GetParagraphIndex() >= 0,
+ "AccessibleEditableTextPara::deleteText: index value overflow");
+
+ CheckRange(nStartIndex, nEndIndex);
+
+ // Because bullet may occupy one or more characters, the TextAdapter will include bullet to calculate the selection. Add offset to handle bullet
+ sal_Int32 nBulletLen = 0;
+ EBulletInfo aBulletInfo = GetTextForwarder().GetBulletInfo(GetParagraphIndex());
+ if( aBulletInfo.nParagraph != EE_PARA_NOT_FOUND && aBulletInfo.bVisible )
+ nBulletLen = aBulletInfo.aText.getLength();
+ ESelection aSelection = MakeSelection (nStartIndex + nBulletLen, nEndIndex + nBulletLen);
+
+ //if( !rCacheTF.IsEditable( MakeSelection(nStartIndex, nEndIndex) ) )
+ if( !rCacheTF.IsEditable( aSelection ) )
+ return false; // non-editable area selected
+
+ //sal_Bool bRet = rCacheTF.Delete( MakeSelection(nStartIndex, nEndIndex) );
+ bool bRet = rCacheTF.Delete( aSelection );
+
+ GetEditSource().UpdateData();
+
+ return bRet;
+ }
+ catch (const uno::RuntimeException&)
+ {
+ return false;
+ }
+ }
+
+ sal_Bool SAL_CALL AccessibleEditableTextPara::insertText( const OUString& sText, sal_Int32 nIndex )
+ {
+
+ SolarMutexGuard aGuard;
+
+ try
+ {
+ // #102710# Request edit view when doing changes
+ // AccessibleEmptyEditSource relies on this behaviour
+ GetEditViewForwarder( true );
+ SvxAccessibleTextAdapter& rCacheTF = GetTextForwarder(); // MUST be after GetEditViewForwarder(), see method docs
+
+ DBG_ASSERT(GetParagraphIndex() >= 0,
+ "AccessibleEditableTextPara::insertText: index value overflow");
+
+ CheckPosition(nIndex);
+
+ // Because bullet may occupy one or more characters, the TextAdapter will include bullet to calculate the selection. Add offset to handle bullet
+ sal_Int32 nBulletLen = 0;
+ EBulletInfo aBulletInfo = GetTextForwarder().GetBulletInfo(GetParagraphIndex());
+ if( aBulletInfo.nParagraph != EE_PARA_NOT_FOUND && aBulletInfo.bVisible )
+ nBulletLen = aBulletInfo.aText.getLength();
+
+ if( !rCacheTF.IsEditable( MakeSelection(nIndex + nBulletLen) ) )
+ return false; // non-editable area selected
+
+ // #104400# insert given text at empty selection (=> cursor)
+ bool bRet = rCacheTF.InsertText( sText, MakeCursor(nIndex + nBulletLen) );
+
+ rCacheTF.QuickFormatDoc();
+ GetEditSource().UpdateData();
+
+ return bRet;
+ }
+ catch (const uno::RuntimeException&)
+ {
+ return false;
+ }
+ }
+
+ sal_Bool SAL_CALL AccessibleEditableTextPara::replaceText( sal_Int32 nStartIndex, sal_Int32 nEndIndex, const OUString& sReplacement )
+ {
+
+ SolarMutexGuard aGuard;
+
+ try
+ {
+ // #102710# Request edit view when doing changes
+ // AccessibleEmptyEditSource relies on this behaviour
+ GetEditViewForwarder( true );
+ SvxAccessibleTextAdapter& rCacheTF = GetTextForwarder(); // MUST be after GetEditViewForwarder(), see method docs
+
+ DBG_ASSERT(GetParagraphIndex() >= 0,
+ "AccessibleEditableTextPara::replaceText: index value overflow");
+
+ CheckRange(nStartIndex, nEndIndex);
+
+ // Because bullet may occupy one or more characters, the TextAdapter will include bullet to calculate the selection. Add offset to handle bullet
+ sal_Int32 nBulletLen = 0;
+ EBulletInfo aBulletInfo = GetTextForwarder().GetBulletInfo(GetParagraphIndex());
+ if( aBulletInfo.nParagraph != EE_PARA_NOT_FOUND && aBulletInfo.bVisible )
+ nBulletLen = aBulletInfo.aText.getLength();
+ ESelection aSelection = MakeSelection (nStartIndex + nBulletLen, nEndIndex + nBulletLen);
+
+ //if( !rCacheTF.IsEditable( MakeSelection(nStartIndex, nEndIndex) ) )
+ if( !rCacheTF.IsEditable( aSelection ) )
+ return false; // non-editable area selected
+
+ // insert given text into given range => replace
+ //sal_Bool bRet = rCacheTF.InsertText( sReplacement, MakeSelection(nStartIndex, nEndIndex) );
+ bool bRet = rCacheTF.InsertText( sReplacement, aSelection );
+
+ rCacheTF.QuickFormatDoc();
+ GetEditSource().UpdateData();
+
+ return bRet;
+ }
+ catch (const uno::RuntimeException&)
+ {
+ return false;
+ }
+ }
+
+ sal_Bool SAL_CALL AccessibleEditableTextPara::setAttributes( sal_Int32 nStartIndex, sal_Int32 nEndIndex, const uno::Sequence< beans::PropertyValue >& aAttributeSet )
+ {
+
+ SolarMutexGuard aGuard;
+
+ try
+ {
+ // #102710# Request edit view when doing changes
+ // AccessibleEmptyEditSource relies on this behaviour
+ GetEditViewForwarder( true );
+ SvxAccessibleTextAdapter& rCacheTF = GetTextForwarder(); // MUST be after GetEditViewForwarder(), see method docs
+ sal_Int32 nPara = GetParagraphIndex();
+
+ DBG_ASSERT(GetParagraphIndex() >= 0,
+ "AccessibleEditableTextPara::setAttributes: index value overflow");
+
+ CheckRange(nStartIndex, nEndIndex);
+
+ if( !rCacheTF.IsEditable( MakeSelection(nStartIndex, nEndIndex) ) )
+ return false; // non-editable area selected
+
+ // do the indices span the whole paragraph? Then use the outliner map
+ // TODO: hold it as a member?
+ rtl::Reference< SvxAccessibleTextPropertySet > xPropSet( new SvxAccessibleTextPropertySet( &GetEditSource(),
+ 0 == nStartIndex &&
+ rCacheTF.GetTextLen(nPara) == nEndIndex ?
+ ImplGetSvxUnoOutlinerTextCursorSvxPropertySet() :
+ ImplGetSvxTextPortionSvxPropertySet() ) );
+
+ xPropSet->SetSelection( MakeSelection(nStartIndex, nEndIndex) );
+
+ // convert from PropertyValue to Any
+ for(const beans::PropertyValue& rProp : aAttributeSet)
+ {
+ try
+ {
+ xPropSet->setPropertyValue(rProp.Name, rProp.Value);
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "dbaccess", "AccessibleEditableTextPara::setAttributes exception in setPropertyValue");
+ }
+ }
+
+ rCacheTF.QuickFormatDoc();
+ GetEditSource().UpdateData();
+
+ return true;
+ }
+ catch (const uno::RuntimeException&)
+ {
+ return false;
+ }
+ }
+
+ sal_Bool SAL_CALL AccessibleEditableTextPara::setText( const OUString& sText )
+ {
+
+ SolarMutexGuard aGuard;
+
+ return replaceText(0, getCharacterCount(), sText);
+ }
+
+ // XAccessibleTextAttributes
+ uno::Sequence< beans::PropertyValue > SAL_CALL AccessibleEditableTextPara::getDefaultAttributes(
+ const uno::Sequence< OUString >& rRequestedAttributes )
+ {
+ SolarMutexGuard aGuard;
+
+ GetTextForwarder();
+
+ DBG_ASSERT(GetParagraphIndex() >= 0,
+ "AccessibleEditableTextPara::getCharacterAttributes: index value overflow");
+
+ // get XPropertySetInfo for paragraph attributes and
+ // character attributes that span all the paragraphs text.
+ rtl::Reference< SvxAccessibleTextPropertySet > xPropSet( new SvxAccessibleTextPropertySet( &GetEditSource(),
+ ImplGetSvxCharAndParaPropertiesSet() ) );
+ xPropSet->SetSelection( MakeSelection( 0, GetTextLen() ) );
+ uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo();
+ if (!xPropSetInfo.is())
+ throw uno::RuntimeException("Cannot query XPropertySetInfo",
+ uno::Reference< uno::XInterface >
+ ( static_cast< XAccessible* > (this) ) ); // disambiguate hierarchy
+
+ // build sequence of available properties to check
+ uno::Sequence< beans::Property > aProperties;
+ if (const sal_Int32 nLenReqAttr = rRequestedAttributes.getLength())
+ {
+ aProperties.realloc( nLenReqAttr );
+ beans::Property *pProperties = aProperties.getArray();
+ sal_Int32 nCurLen = 0;
+ for (const OUString& rRequestedAttribute : rRequestedAttributes)
+ {
+ beans::Property aProp;
+ try
+ {
+ aProp = xPropSetInfo->getPropertyByName( rRequestedAttribute );
+ }
+ catch (const beans::UnknownPropertyException&)
+ {
+ continue;
+ }
+ pProperties[ nCurLen++ ] = aProp;
+ }
+ aProperties.realloc( nCurLen );
+ }
+ else
+ aProperties = xPropSetInfo->getProperties();
+
+ // build resulting sequence
+ uno::Sequence< beans::PropertyValue > aOutSequence( aProperties.getLength() );
+ beans::PropertyValue* pOutSequence = aOutSequence.getArray();
+ sal_Int32 nOutLen = 0;
+ for (const beans::Property& rProperty : std::as_const(aProperties))
+ {
+ // calling implementation functions:
+ // _getPropertyState and _getPropertyValue (see below) to provide
+ // the proper paragraph number when retrieving paragraph attributes
+ PropertyState eState = xPropSet->_getPropertyState( rProperty.Name, mnParagraphIndex );
+ if ( eState == PropertyState_AMBIGUOUS_VALUE )
+ {
+ OSL_FAIL( "ambiguous property value encountered" );
+ }
+
+ //if (eState == PropertyState_DIRECT_VALUE)
+ // per definition all paragraph properties and all character
+ // properties spanning the whole paragraph should be returned
+ // and declared as default value
+ {
+ pOutSequence->Name = rProperty.Name;
+ pOutSequence->Handle = rProperty.Handle;
+ pOutSequence->Value = xPropSet->_getPropertyValue( rProperty.Name, mnParagraphIndex );
+ pOutSequence->State = PropertyState_DEFAULT_VALUE;
+
+ ++pOutSequence;
+ ++nOutLen;
+ }
+ }
+ aOutSequence.realloc( nOutLen );
+
+ return aOutSequence;
+ }
+
+
+ uno::Sequence< beans::PropertyValue > SAL_CALL AccessibleEditableTextPara::getRunAttributes(
+ sal_Int32 nIndex,
+ const uno::Sequence< OUString >& rRequestedAttributes )
+ {
+
+ SolarMutexGuard aGuard;
+
+ GetTextForwarder();
+
+ DBG_ASSERT(GetParagraphIndex() >= 0,
+ "AccessibleEditableTextPara::getCharacterAttributes: index value overflow");
+
+ if( getCharacterCount() > 0 )
+ CheckIndex(nIndex);
+ else
+ CheckPosition(nIndex);
+
+ rtl::Reference< SvxAccessibleTextPropertySet > xPropSet( new SvxAccessibleTextPropertySet( &GetEditSource(),
+ ImplGetSvxCharAndParaPropertiesSet() ) );
+ xPropSet->SetSelection( MakeSelection( nIndex ) );
+ uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo();
+ if (!xPropSetInfo.is())
+ throw uno::RuntimeException("Cannot query XPropertySetInfo",
+ uno::Reference< uno::XInterface >
+ ( static_cast< XAccessible* > (this) ) ); // disambiguate hierarchy
+
+ // build sequence of available properties to check
+ uno::Sequence< beans::Property > aProperties;
+ if (const sal_Int32 nLenReqAttr = rRequestedAttributes.getLength())
+ {
+ aProperties.realloc( nLenReqAttr );
+ beans::Property *pProperties = aProperties.getArray();
+ sal_Int32 nCurLen = 0;
+ for (const OUString& rRequestedAttribute : rRequestedAttributes)
+ {
+ beans::Property aProp;
+ try
+ {
+ aProp = xPropSetInfo->getPropertyByName( rRequestedAttribute );
+ }
+ catch (const beans::UnknownPropertyException&)
+ {
+ continue;
+ }
+ pProperties[ nCurLen++ ] = aProp;
+ }
+ aProperties.realloc( nCurLen );
+ }
+ else
+ aProperties = xPropSetInfo->getProperties();
+
+ // build resulting sequence
+ uno::Sequence< beans::PropertyValue > aOutSequence( aProperties.getLength() );
+ beans::PropertyValue* pOutSequence = aOutSequence.getArray();
+ sal_Int32 nOutLen = 0;
+ for (const beans::Property& rProperty : std::as_const(aProperties))
+ {
+ // calling 'regular' functions that will operate on the selection
+ PropertyState eState = xPropSet->getPropertyState( rProperty.Name );
+ if (eState == PropertyState_DIRECT_VALUE)
+ {
+ pOutSequence->Name = rProperty.Name;
+ pOutSequence->Handle = rProperty.Handle;
+ pOutSequence->Value = xPropSet->getPropertyValue( rProperty.Name );
+ pOutSequence->State = eState;
+
+ ++pOutSequence;
+ ++nOutLen;
+ }
+ }
+ aOutSequence.realloc( nOutLen );
+
+ return aOutSequence;
+ }
+
+ // XAccessibleHypertext
+ ::sal_Int32 SAL_CALL AccessibleEditableTextPara::getHyperLinkCount( )
+ {
+ SvxAccessibleTextAdapter& rT = GetTextForwarder();
+ const sal_Int32 nPara = GetParagraphIndex();
+
+ sal_Int32 nHyperLinks = 0;
+ sal_Int32 nFields = rT.GetFieldCount( nPara );
+ for (sal_Int32 n = 0; n < nFields; ++n)
+ {
+ EFieldInfo aField = rT.GetFieldInfo( nPara, n );
+ if ( dynamic_cast<const SvxURLField* >(aField.pFieldItem->GetField() ) != nullptr)
+ nHyperLinks++;
+ }
+ return nHyperLinks;
+ }
+
+ css::uno::Reference< css::accessibility::XAccessibleHyperlink > SAL_CALL AccessibleEditableTextPara::getHyperLink( ::sal_Int32 nLinkIndex )
+ {
+ css::uno::Reference< css::accessibility::XAccessibleHyperlink > xRef;
+
+ SvxAccessibleTextAdapter& rT = GetTextForwarder();
+ const sal_Int32 nPara = GetParagraphIndex();
+
+ sal_Int32 nHyperLink = 0;
+ sal_Int32 nFields = rT.GetFieldCount( nPara );
+ for (sal_Int32 n = 0; n < nFields; ++n)
+ {
+ EFieldInfo aField = rT.GetFieldInfo( nPara, n );
+ if ( dynamic_cast<const SvxURLField* >(aField.pFieldItem->GetField()) != nullptr )
+ {
+ if ( nHyperLink == nLinkIndex )
+ {
+ sal_Int32 nEEStart = aField.aPosition.nIndex;
+
+ // Translate EE Index to accessible index
+ sal_Int32 nStart = rT.CalcEditEngineIndex( nPara, nEEStart );
+ sal_Int32 nEnd = nStart + aField.aCurrentText.getLength();
+ xRef = new AccessibleHyperlink( rT, new SvxFieldItem( *aField.pFieldItem ), nStart, nEnd, aField.aCurrentText );
+ break;
+ }
+ nHyperLink++;
+ }
+ }
+
+ return xRef;
+ }
+
+ ::sal_Int32 SAL_CALL AccessibleEditableTextPara::getHyperLinkIndex( ::sal_Int32 nCharIndex )
+ {
+ const sal_Int32 nPara = GetParagraphIndex();
+ SvxAccessibleTextAdapter& rT = GetTextForwarder();
+
+ const sal_Int32 nEEIndex = rT.CalcEditEngineIndex( nPara, nCharIndex );
+ sal_Int32 nHLIndex = -1; //i123620
+ sal_Int32 nHyperLink = 0;
+ sal_Int32 nFields = rT.GetFieldCount( nPara );
+ for (sal_Int32 n = 0; n < nFields; ++n)
+ {
+ EFieldInfo aField = rT.GetFieldInfo( nPara, n );
+ if ( dynamic_cast<const SvxURLField* >( aField.pFieldItem->GetField() ) != nullptr)
+ {
+ if ( aField.aPosition.nIndex == nEEIndex )
+ {
+ nHLIndex = nHyperLink;
+ break;
+ }
+ nHyperLink++;
+ }
+ }
+
+ return nHLIndex;
+ }
+
+ // XAccessibleMultiLineText
+ sal_Int32 SAL_CALL AccessibleEditableTextPara::getLineNumberAtIndex( sal_Int32 nIndex )
+ {
+
+ sal_Int32 nRes = -1;
+ sal_Int32 nPara = GetParagraphIndex();
+
+ SvxTextForwarder &rCacheTF = GetTextForwarder();
+ const bool bValidPara = 0 <= nPara && nPara < rCacheTF.GetParagraphCount();
+ DBG_ASSERT( bValidPara, "getLineNumberAtIndex: current paragraph index out of range" );
+ if (bValidPara)
+ {
+ // we explicitly allow for the index to point at the character right behind the text
+ if (0 > nIndex || nIndex > rCacheTF.GetTextLen( nPara ))
+ throw lang::IndexOutOfBoundsException();
+ nRes = rCacheTF.GetLineNumberAtIndex( nPara, nIndex );
+ }
+ return nRes;
+ }
+
+ // XAccessibleMultiLineText
+ css::accessibility::TextSegment SAL_CALL AccessibleEditableTextPara::getTextAtLineNumber( sal_Int32 nLineNo )
+ {
+
+ css::accessibility::TextSegment aResult;
+ sal_Int32 nPara = GetParagraphIndex();
+ SvxTextForwarder &rCacheTF = GetTextForwarder();
+ const bool bValidPara = 0 <= nPara && nPara < rCacheTF.GetParagraphCount();
+ DBG_ASSERT( bValidPara, "getTextAtLineNumber: current paragraph index out of range" );
+ if (bValidPara)
+ {
+ if (0 > nLineNo || nLineNo >= rCacheTF.GetLineCount( nPara ))
+ throw lang::IndexOutOfBoundsException();
+ sal_Int32 nStart = 0, nEnd = 0;
+ rCacheTF.GetLineBoundaries( nStart, nEnd, nPara, nLineNo );
+ if (nStart >= 0 && nEnd >= 0)
+ {
+ try
+ {
+ aResult.SegmentText = getTextRange( nStart, nEnd );
+ aResult.SegmentStart = nStart;
+ aResult.SegmentEnd = nEnd;
+ }
+ catch (const lang::IndexOutOfBoundsException&)
+ {
+ // this is not the exception that should be raised in this function ...
+ DBG_UNHANDLED_EXCEPTION("editeng");
+ }
+ }
+ }
+ return aResult;
+ }
+
+ // XAccessibleMultiLineText
+ css::accessibility::TextSegment SAL_CALL AccessibleEditableTextPara::getTextAtLineWithCaret( )
+ {
+
+ css::accessibility::TextSegment aResult;
+ try
+ {
+ aResult = getTextAtLineNumber( getNumberOfLineWithCaret() );
+ }
+ catch (const lang::IndexOutOfBoundsException&)
+ {
+ // this one needs to be caught since this interface does not allow for it.
+ }
+ return aResult;
+ }
+
+ // XAccessibleMultiLineText
+ sal_Int32 SAL_CALL AccessibleEditableTextPara::getNumberOfLineWithCaret( )
+ {
+
+ sal_Int32 nRes = -1;
+ try
+ {
+ nRes = getLineNumberAtIndex( getCaretPosition() );
+ }
+ catch (const lang::IndexOutOfBoundsException&)
+ {
+ // this one needs to be caught since this interface does not allow for it.
+ }
+ return nRes;
+ }
+
+
+ // XServiceInfo
+ OUString SAL_CALL AccessibleEditableTextPara::getImplementationName()
+ {
+
+ return "AccessibleEditableTextPara";
+ }
+
+ sal_Bool SAL_CALL AccessibleEditableTextPara::supportsService (const OUString& sServiceName)
+ {
+
+ return cppu::supportsService(this, sServiceName);
+ }
+
+ uno::Sequence< OUString> SAL_CALL AccessibleEditableTextPara::getSupportedServiceNames()
+ {
+ // #105185# Using correct service now
+ return { OUString("com.sun.star.text.AccessibleParagraphView") };
+ }
+
+} // end of namespace accessibility
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/accessibility/AccessibleHyperlink.cxx b/editeng/source/accessibility/AccessibleHyperlink.cxx
new file mode 100644
index 0000000000..25d9683fce
--- /dev/null
+++ b/editeng/source/accessibility/AccessibleHyperlink.cxx
@@ -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 .
+ */
+
+
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <comphelper/accessiblekeybindinghelper.hxx>
+
+#include "AccessibleHyperlink.hxx"
+#include <editeng/unoedprx.hxx>
+#include <editeng/flditem.hxx>
+#include <vcl/keycodes.hxx>
+
+using namespace ::com::sun::star;
+
+
+// AccessibleHyperlink implementation
+
+
+namespace accessibility
+{
+
+ AccessibleHyperlink::AccessibleHyperlink( SvxAccessibleTextAdapter& r, SvxFieldItem* p, sal_Int32 nStt, sal_Int32 nEnd, const OUString& rD )
+ : rTA( r )
+ {
+ pFld.reset( p );
+ nStartIdx = nStt;
+ nEndIdx = nEnd;
+ aDescription = rD;
+ }
+
+ AccessibleHyperlink::~AccessibleHyperlink()
+ {
+ }
+
+ // XAccessibleAction
+ sal_Int32 SAL_CALL AccessibleHyperlink::getAccessibleActionCount()
+ {
+ return isValid() ? 1 : 0;
+ }
+
+ sal_Bool SAL_CALL AccessibleHyperlink::doAccessibleAction( sal_Int32 nIndex )
+ {
+ bool bRet = false;
+ if ( isValid() && ( nIndex == 0 ) )
+ {
+ rTA.FieldClicked( *pFld );
+ bRet = true;
+ }
+ return bRet;
+ }
+
+ OUString SAL_CALL AccessibleHyperlink::getAccessibleActionDescription( sal_Int32 nIndex )
+ {
+ OUString aDesc;
+
+ if ( isValid() && ( nIndex == 0 ) )
+ aDesc = aDescription;
+
+ return aDesc;
+ }
+
+ uno::Reference< css::accessibility::XAccessibleKeyBinding > SAL_CALL AccessibleHyperlink::getAccessibleActionKeyBinding( sal_Int32 nIndex )
+ {
+ uno::Reference< css::accessibility::XAccessibleKeyBinding > xKeyBinding;
+
+ if( isValid() && ( nIndex == 0 ) )
+ {
+ rtl::Reference<::comphelper::OAccessibleKeyBindingHelper> pKeyBindingHelper = new ::comphelper::OAccessibleKeyBindingHelper();
+ xKeyBinding = pKeyBindingHelper;
+
+ awt::KeyStroke aKeyStroke;
+ aKeyStroke.Modifiers = 0;
+ aKeyStroke.KeyCode = KEY_RETURN;
+ aKeyStroke.KeyChar = 0;
+ aKeyStroke.KeyFunc = 0;
+ pKeyBindingHelper->AddKeyBinding( aKeyStroke );
+ }
+
+ return xKeyBinding;
+ }
+
+ // XAccessibleHyperlink
+ uno::Any SAL_CALL AccessibleHyperlink::getAccessibleActionAnchor( sal_Int32 /*nIndex*/ )
+ {
+ return uno::Any();
+ }
+
+ uno::Any SAL_CALL AccessibleHyperlink::getAccessibleActionObject( sal_Int32 /*nIndex*/ )
+ {
+ return uno::Any();
+ }
+
+ sal_Int32 SAL_CALL AccessibleHyperlink::getStartIndex()
+ {
+ return nStartIdx;
+ }
+
+ sal_Int32 SAL_CALL AccessibleHyperlink::getEndIndex()
+ {
+ return nEndIdx;
+ }
+
+ sal_Bool SAL_CALL AccessibleHyperlink::isValid( )
+ {
+ return rTA.IsValid();
+ }
+
+} // end of namespace accessibility
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/accessibility/AccessibleHyperlink.hxx b/editeng/source/accessibility/AccessibleHyperlink.hxx
new file mode 100644
index 0000000000..7e4f36a6bb
--- /dev/null
+++ b/editeng/source/accessibility/AccessibleHyperlink.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 <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/accessibility/XAccessibleHyperlink.hpp>
+
+#include <memory>
+
+class SvxFieldItem;
+class SvxAccessibleTextAdapter;
+
+namespace accessibility
+{
+
+ class AccessibleHyperlink : public ::cppu::WeakImplHelper< css::accessibility::XAccessibleHyperlink >
+ {
+ private:
+
+ SvxAccessibleTextAdapter& rTA;
+ std::unique_ptr<SvxFieldItem> pFld;
+ sal_Int32 nStartIdx, nEndIdx; // translated values
+ OUString aDescription;
+
+ public:
+ AccessibleHyperlink( SvxAccessibleTextAdapter& r, SvxFieldItem* p, sal_Int32 nStt, sal_Int32 nEnd, const OUString& rD );
+ virtual ~AccessibleHyperlink() override;
+
+ // XAccessibleAction
+ virtual sal_Int32 SAL_CALL getAccessibleActionCount() override;
+ virtual sal_Bool SAL_CALL doAccessibleAction( sal_Int32 nIndex ) override;
+ virtual OUString SAL_CALL getAccessibleActionDescription( sal_Int32 nIndex ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleKeyBinding > SAL_CALL getAccessibleActionKeyBinding( sal_Int32 nIndex ) override;
+
+ // XAccessibleHyperlink
+ virtual css::uno::Any SAL_CALL getAccessibleActionAnchor( sal_Int32 nIndex ) override;
+ virtual css::uno::Any SAL_CALL getAccessibleActionObject( sal_Int32 nIndex ) override;
+ virtual sal_Int32 SAL_CALL getStartIndex() override;
+ virtual sal_Int32 SAL_CALL getEndIndex() override;
+ virtual sal_Bool SAL_CALL isValid() override;
+ };
+
+} // end of namespace accessibility
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/accessibility/AccessibleImageBullet.cxx b/editeng/source/accessibility/AccessibleImageBullet.cxx
new file mode 100644
index 0000000000..c3a051cf01
--- /dev/null
+++ b/editeng/source/accessibility/AccessibleImageBullet.cxx
@@ -0,0 +1,517 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <tools/gen.hxx>
+#include <tools/debug.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <rtl/ustring.hxx>
+#include <com/sun/star/awt/Point.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <comphelper/accessibleeventnotifier.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <editeng/AccessibleEditableTextPara.hxx>
+#include <editeng/eerdll.hxx>
+
+#include <editeng/editdata.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/editrids.hrc>
+#include <editeng/unoedsrc.hxx>
+#include <svtools/colorcfg.hxx>
+
+#include "AccessibleImageBullet.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+namespace accessibility
+{
+
+ AccessibleImageBullet::AccessibleImageBullet ( uno::Reference< XAccessible > xParent ) :
+ mnParagraphIndex( 0 ),
+ mnIndexInParent( 0 ),
+ mpEditSource( nullptr ),
+ maEEOffset( 0, 0 ),
+ mxParent(std::move( xParent )),
+ // well, that's strictly (UNO) exception safe, though not
+ // really robust. We rely on the fact that this member is
+ // constructed last, and that the constructor body catches
+ // exceptions, thus no chance for exceptions once the Id is
+ // fetched. Nevertheless, normally should employ RAII here...
+ mnNotifierClientId(::comphelper::AccessibleEventNotifier::registerClient())
+ {
+ try
+ {
+ // Create the state set.
+ mnStateSet = 0;
+
+ // these are always on
+ mnStateSet |= AccessibleStateType::VISIBLE;
+ mnStateSet |= AccessibleStateType::SHOWING;
+ mnStateSet |= AccessibleStateType::ENABLED;
+ mnStateSet |= AccessibleStateType::SENSITIVE;
+ }
+ catch( const uno::Exception& ) {}
+ }
+
+ AccessibleImageBullet::~AccessibleImageBullet()
+ {
+
+ // sign off from event notifier
+ if( getNotifierClientId() != -1 )
+ {
+ try
+ {
+ ::comphelper::AccessibleEventNotifier::revokeClient( getNotifierClientId() );
+ }
+ catch( const uno::Exception& ) {}
+ }
+ }
+
+ uno::Reference< XAccessibleContext > SAL_CALL AccessibleImageBullet::getAccessibleContext( )
+ {
+
+ // We implement the XAccessibleContext interface in the same object
+ return uno::Reference< XAccessibleContext > ( this );
+ }
+
+ sal_Int64 SAL_CALL AccessibleImageBullet::getAccessibleChildCount()
+ {
+
+ return 0;
+ }
+
+ uno::Reference< XAccessible > SAL_CALL AccessibleImageBullet::getAccessibleChild( sal_Int64 )
+ {
+ throw lang::IndexOutOfBoundsException("No children available",
+ getXWeak() );
+ }
+
+ uno::Reference< XAccessible > SAL_CALL AccessibleImageBullet::getAccessibleParent()
+ {
+
+ return mxParent;
+ }
+
+ sal_Int64 SAL_CALL AccessibleImageBullet::getAccessibleIndexInParent()
+ {
+
+ return mnIndexInParent;
+ }
+
+ sal_Int16 SAL_CALL AccessibleImageBullet::getAccessibleRole()
+ {
+
+ return AccessibleRole::GRAPHIC;
+ }
+
+ OUString SAL_CALL AccessibleImageBullet::getAccessibleDescription()
+ {
+ // Get the string from the resource for the specified id.
+ return EditResId(RID_SVXSTR_A11Y_IMAGEBULLET_DESCRIPTION);
+ }
+
+ OUString SAL_CALL AccessibleImageBullet::getAccessibleName()
+ {
+ // Get the string from the resource for the specified id.
+ return EditResId(RID_SVXSTR_A11Y_IMAGEBULLET_NAME);
+ }
+
+ uno::Reference< XAccessibleRelationSet > SAL_CALL AccessibleImageBullet::getAccessibleRelationSet()
+ {
+
+ // no relations, therefore empty
+ return uno::Reference< XAccessibleRelationSet >();
+ }
+
+ sal_Int64 SAL_CALL AccessibleImageBullet::getAccessibleStateSet()
+ {
+ SolarMutexGuard aGuard;
+
+ // Create a copy of the state set and return it.
+
+ return mnStateSet;
+ }
+
+ lang::Locale SAL_CALL AccessibleImageBullet::getLocale()
+ {
+
+ SolarMutexGuard aGuard;
+
+ DBG_ASSERT(GetParagraphIndex() >= 0,
+ "AccessibleImageBullet::getLocale: paragraph index value overflow");
+
+ // return locale of first character in the paragraph
+ return LanguageTag(GetTextForwarder().GetLanguage( GetParagraphIndex(), 0 )).getLocale();
+ }
+
+ void SAL_CALL AccessibleImageBullet::addAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener )
+ {
+
+ if( getNotifierClientId() != -1 )
+ ::comphelper::AccessibleEventNotifier::addEventListener( getNotifierClientId(), xListener );
+ }
+
+ void SAL_CALL AccessibleImageBullet::removeAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener )
+ {
+
+ if( getNotifierClientId() == -1 )
+ return;
+
+ const sal_Int32 nListenerCount = ::comphelper::AccessibleEventNotifier::removeEventListener( getNotifierClientId(), xListener );
+ if ( !nListenerCount )
+ {
+ // no listeners anymore
+ // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client),
+ // and at least to us not firing any events anymore, in case somebody calls
+ // NotifyAccessibleEvent, again
+ ::comphelper::AccessibleEventNotifier::TClientId nId( getNotifierClientId() );
+ mnNotifierClientId = -1;
+ ::comphelper::AccessibleEventNotifier::revokeClient( nId );
+ }
+ }
+
+ sal_Bool SAL_CALL AccessibleImageBullet::containsPoint( const awt::Point& rPoint )
+ {
+
+ SolarMutexGuard aGuard;
+
+ DBG_ASSERT(GetParagraphIndex() >= 0,
+ "AccessibleEditableTextPara::contains: index value overflow");
+
+ awt::Rectangle aTmpRect = implGetBounds();
+ tools::Rectangle aRect( Point(aTmpRect.X, aTmpRect.Y), Size(aTmpRect.Width, aTmpRect.Height) );
+ Point aPoint( rPoint.X, rPoint.Y );
+
+ return aRect.Contains( aPoint );
+ }
+
+ uno::Reference< XAccessible > SAL_CALL AccessibleImageBullet::getAccessibleAtPoint( const awt::Point& /*aPoint*/ )
+ {
+
+ // as we have no children, empty reference
+ return uno::Reference< XAccessible >();
+ }
+
+ awt::Rectangle SAL_CALL AccessibleImageBullet::getBounds( )
+ {
+ SolarMutexGuard aGuard;
+
+ return implGetBounds();
+ }
+ awt::Rectangle AccessibleImageBullet::implGetBounds( )
+ {
+
+ DBG_ASSERT(GetParagraphIndex() >= 0,
+ "AccessibleEditableTextPara::implGetBounds: index value overflow");
+
+ SvxTextForwarder& rCacheTF = GetTextForwarder();
+ EBulletInfo aBulletInfo = rCacheTF.GetBulletInfo( GetParagraphIndex() );
+ tools::Rectangle aParentRect = rCacheTF.GetParaBounds( GetParagraphIndex() );
+
+ if( aBulletInfo.nParagraph != EE_PARA_NOT_FOUND &&
+ aBulletInfo.bVisible &&
+ aBulletInfo.nType == SVX_NUM_BITMAP )
+ {
+ tools::Rectangle aRect = aBulletInfo.aBounds;
+
+ // subtract paragraph position (bullet pos is absolute in EditEngine/Outliner)
+ aRect.Move( -aParentRect.Left(), -aParentRect.Top() );
+
+ // convert to screen coordinates
+ tools::Rectangle aScreenRect = AccessibleEditableTextPara::LogicToPixel( aRect,
+ rCacheTF.GetMapMode(),
+ GetViewForwarder() );
+
+ // offset from shape/cell
+ Point aOffset = maEEOffset;
+
+ return awt::Rectangle( aScreenRect.Left() + aOffset.X(),
+ aScreenRect.Top() + aOffset.Y(),
+ aScreenRect.GetSize().Width(),
+ aScreenRect.GetSize().Height() );
+ }
+
+ return awt::Rectangle();
+ }
+
+ awt::Point SAL_CALL AccessibleImageBullet::getLocation( )
+ {
+
+ SolarMutexGuard aGuard;
+
+ awt::Rectangle aRect = implGetBounds();
+
+ return awt::Point( aRect.X, aRect.Y );
+ }
+
+ awt::Point SAL_CALL AccessibleImageBullet::getLocationOnScreen( )
+ {
+
+ SolarMutexGuard aGuard;
+
+ // relate us to parent
+ uno::Reference< XAccessible > xParent = getAccessibleParent();
+ if( xParent.is() )
+ {
+ uno::Reference< XAccessibleComponent > xParentComponent( xParent, uno::UNO_QUERY );
+ if( xParentComponent.is() )
+ {
+ awt::Point aRefPoint = xParentComponent->getLocationOnScreen();
+ awt::Point aPoint = getLocation();
+ aPoint.X += aRefPoint.X;
+ aPoint.Y += aRefPoint.Y;
+
+ return aPoint;
+ }
+ }
+
+ throw uno::RuntimeException("Cannot access parent",
+ uno::Reference< uno::XInterface >
+ ( static_cast< XAccessible* > (this) ) ); // disambiguate hierarchy
+ }
+
+ awt::Size SAL_CALL AccessibleImageBullet::getSize( )
+ {
+
+ SolarMutexGuard aGuard;
+
+ awt::Rectangle aRect = implGetBounds();
+
+ return awt::Size( aRect.Width, aRect.Height );
+ }
+
+ void SAL_CALL AccessibleImageBullet::grabFocus( )
+ {
+
+ throw uno::RuntimeException("Not focusable",
+ uno::Reference< uno::XInterface >
+ ( static_cast< XAccessible* > (this) ) ); // disambiguate hierarchy
+ }
+
+ sal_Int32 SAL_CALL AccessibleImageBullet::getForeground( )
+ {
+
+ // #104444# Added to XAccessibleComponent interface
+ svtools::ColorConfig aColorConfig;
+ Color nColor = aColorConfig.GetColorValue( svtools::FONTCOLOR ).nColor;
+ return static_cast<sal_Int32>(nColor);
+ }
+
+ sal_Int32 SAL_CALL AccessibleImageBullet::getBackground( )
+ {
+
+ // #104444# Added to XAccessibleComponent interface
+ Color aColor( Application::GetSettings().GetStyleSettings().GetWindowColor() );
+
+ // the background is transparent
+ aColor.SetAlpha(0);
+
+ return static_cast<sal_Int32>( aColor );
+ }
+
+ OUString SAL_CALL AccessibleImageBullet::getImplementationName()
+ {
+
+ return "AccessibleImageBullet";
+ }
+
+ sal_Bool SAL_CALL AccessibleImageBullet::supportsService (const OUString& sServiceName)
+ {
+
+ return cppu::supportsService(this, sServiceName);
+ }
+
+ uno::Sequence< OUString > SAL_CALL AccessibleImageBullet::getSupportedServiceNames()
+ {
+ return { "com.sun.star.accessibility.AccessibleContext" };
+ }
+
+ void AccessibleImageBullet::SetIndexInParent( sal_Int32 nIndex )
+ {
+
+ mnIndexInParent = nIndex;
+ }
+
+ void AccessibleImageBullet::SetEEOffset( const Point& rOffset )
+ {
+
+ maEEOffset = rOffset;
+ }
+
+ void AccessibleImageBullet::Dispose()
+ {
+
+ int nClientId( getNotifierClientId() );
+
+ // #108212# drop all references before notifying dispose
+ mxParent = nullptr;
+ mnNotifierClientId = -1;
+ mpEditSource = nullptr;
+
+ // notify listeners
+ if( nClientId != -1 )
+ {
+ try
+ {
+ uno::Reference < XAccessibleContext > xThis = getAccessibleContext();
+
+ // #106234# Delegate to EventNotifier
+ ::comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( nClientId, xThis );
+ }
+ catch( const uno::Exception& ) {}
+ }
+ }
+
+ void AccessibleImageBullet::SetEditSource( SvxEditSource* pEditSource )
+ {
+
+ mpEditSource = pEditSource;
+
+ if( !mpEditSource )
+ {
+ // going defunc
+ UnSetState( AccessibleStateType::SHOWING );
+ UnSetState( AccessibleStateType::VISIBLE );
+ SetState( AccessibleStateType::INVALID );
+ SetState( AccessibleStateType::DEFUNC );
+
+ Dispose();
+ }
+ }
+
+ void AccessibleImageBullet::FireEvent(const sal_Int16 nEventId, const uno::Any& rNewValue, const uno::Any& rOldValue ) const
+ {
+
+ uno::Reference < XAccessibleContext > xThis( const_cast< AccessibleImageBullet* > (this)->getAccessibleContext() );
+
+ AccessibleEventObject aEvent(xThis, nEventId, rNewValue, rOldValue, -1);
+
+ // #106234# Delegate to EventNotifier
+ ::comphelper::AccessibleEventNotifier::addEvent( getNotifierClientId(),
+ aEvent );
+ }
+
+ void AccessibleImageBullet::SetState( const sal_Int64 nStateId )
+ {
+ if( !(mnStateSet & nStateId) )
+ {
+ mnStateSet |= nStateId;
+ FireEvent( AccessibleEventId::STATE_CHANGED, uno::Any( nStateId ) );
+ }
+ }
+
+ void AccessibleImageBullet::UnSetState( const sal_Int64 nStateId )
+ {
+ if( mnStateSet & nStateId )
+ {
+ mnStateSet &= ~nStateId;
+ FireEvent( AccessibleEventId::STATE_CHANGED, uno::Any(), uno::Any( nStateId ) );
+ }
+ }
+
+
+ void AccessibleImageBullet::SetParagraphIndex( sal_Int32 nIndex )
+ {
+
+ uno::Any aOldDesc;
+ uno::Any aOldName;
+
+ try
+ {
+ aOldDesc <<= getAccessibleDescription();
+ aOldName <<= getAccessibleName();
+ }
+ catch( const uno::Exception& ) {} // optional behaviour
+
+ sal_Int32 nOldIndex = mnParagraphIndex;
+
+ mnParagraphIndex = nIndex;
+
+ try
+ {
+ if( nOldIndex != nIndex )
+ {
+ // index and therefore description changed
+ FireEvent( AccessibleEventId::DESCRIPTION_CHANGED, uno::Any( getAccessibleDescription() ), aOldDesc );
+ FireEvent( AccessibleEventId::NAME_CHANGED, uno::Any( getAccessibleName() ), aOldName );
+ }
+ }
+ catch( const uno::Exception& ) {} // optional behaviour
+ }
+
+
+ SvxEditSource& AccessibleImageBullet::GetEditSource() const
+ {
+
+ if( !mpEditSource )
+ throw uno::RuntimeException("No edit source, object is defunct",
+ cppu::getXWeak
+ ( const_cast< AccessibleImageBullet* > (this) ) ); // disambiguate hierarchy
+ return *mpEditSource;
+ }
+
+ SvxTextForwarder& AccessibleImageBullet::GetTextForwarder() const
+ {
+
+ SvxEditSource& rEditSource = GetEditSource();
+ SvxTextForwarder* pTextForwarder = rEditSource.GetTextForwarder();
+
+ if( !pTextForwarder )
+ throw uno::RuntimeException("Unable to fetch text forwarder, object is defunct",
+ cppu::getXWeak
+ ( const_cast< AccessibleImageBullet* > (this) ) ); // disambiguate hierarchy
+
+ if( !pTextForwarder->IsValid() )
+ throw uno::RuntimeException("Text forwarder is invalid, object is defunct",
+ cppu::getXWeak
+ ( const_cast< AccessibleImageBullet* > (this) ) ); // disambiguate hierarchy
+ return *pTextForwarder;
+ }
+
+ SvxViewForwarder& AccessibleImageBullet::GetViewForwarder() const
+ {
+
+ SvxEditSource& rEditSource = GetEditSource();
+ SvxViewForwarder* pViewForwarder = rEditSource.GetViewForwarder();
+
+ if( !pViewForwarder )
+ {
+ throw uno::RuntimeException("Unable to fetch view forwarder, object is defunct",
+ cppu::getXWeak
+ ( const_cast< AccessibleImageBullet* > (this) ) ); // disambiguate hierarchy
+ }
+
+ if( !pViewForwarder->IsValid() )
+ throw uno::RuntimeException("View forwarder is invalid, object is defunct",
+ cppu::getXWeak
+ ( const_cast< AccessibleImageBullet* > (this) ) ); // disambiguate hierarchy
+ return *pViewForwarder;
+ }
+
+
+} // end of namespace accessibility
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/accessibility/AccessibleImageBullet.hxx b/editeng/source/accessibility/AccessibleImageBullet.hxx
new file mode 100644
index 0000000000..97fd98cae7
--- /dev/null
+++ b/editeng/source/accessibility/AccessibleImageBullet.hxx
@@ -0,0 +1,201 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ref.hxx>
+#include <tools/gen.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/accessibility/XAccessible.hpp>
+#include <com/sun/star/accessibility/XAccessibleContext.hpp>
+#include <com/sun/star/accessibility/XAccessibleComponent.hpp>
+#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp>
+
+class SvxEditSource;
+class SvxTextForwarder;
+class SvxViewForwarder;
+
+namespace accessibility
+{
+ typedef ::cppu::WeakImplHelper< css::accessibility::XAccessible,
+ css::accessibility::XAccessibleContext,
+ css::accessibility::XAccessibleComponent,
+ css::accessibility::XAccessibleEventBroadcaster,
+ css::lang::XServiceInfo > AccessibleImageBulletInterfaceBase;
+
+ /** This class implements the image bullets for the EditEngine/Outliner UAA
+ */
+ class AccessibleImageBullet final : public AccessibleImageBulletInterfaceBase
+ {
+
+ public:
+ /// Create accessible object for given parent
+ AccessibleImageBullet ( css::uno::Reference< css::accessibility::XAccessible > xParent );
+
+ virtual ~AccessibleImageBullet () override;
+
+ // XAccessible
+ virtual css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL getAccessibleContext( ) override;
+
+ // XAccessibleContext
+ virtual sal_Int64 SAL_CALL getAccessibleChildCount() override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild( sal_Int64 i ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleParent() override;
+ virtual sal_Int64 SAL_CALL getAccessibleIndexInParent() override;
+ virtual sal_Int16 SAL_CALL getAccessibleRole() override;
+ virtual OUString SAL_CALL getAccessibleDescription() override;
+ virtual OUString SAL_CALL getAccessibleName() override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet() override;
+ virtual sal_Int64 SAL_CALL getAccessibleStateSet() override;
+ virtual css::lang::Locale SAL_CALL getLocale() override;
+
+ // XAccessibleEventBroadcaster
+ virtual void SAL_CALL addAccessibleEventListener( const css::uno::Reference< css::accessibility::XAccessibleEventListener >& xListener ) override;
+ virtual void SAL_CALL removeAccessibleEventListener( const css::uno::Reference< css::accessibility::XAccessibleEventListener >& xListener ) override;
+
+ // XAccessibleComponent
+ virtual sal_Bool SAL_CALL containsPoint( const css::awt::Point& aPoint ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleAtPoint( const css::awt::Point& aPoint ) override;
+ virtual css::awt::Rectangle SAL_CALL getBounds( ) override;
+ virtual css::awt::Point SAL_CALL getLocation( ) override;
+ virtual css::awt::Point SAL_CALL getLocationOnScreen( ) override;
+ virtual css::awt::Size SAL_CALL getSize( ) override;
+ virtual void SAL_CALL grabFocus( ) override;
+ virtual sal_Int32 SAL_CALL getForeground( ) override;
+ virtual sal_Int32 SAL_CALL getBackground( ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService (const OUString& sServiceName) override;
+ virtual css::uno::Sequence< OUString> SAL_CALL getSupportedServiceNames() override;
+
+ /** Set the current index in the accessibility parent
+
+ @attention This method does not lock the SolarMutex,
+ leaving that to the calling code. This is because only
+ there potential deadlock situations can be resolved. Thus,
+ make sure SolarMutex is locked when calling this.
+ */
+ void SetIndexInParent( sal_Int32 nIndex );
+
+ /** Set the edit engine offset
+
+ @attention This method does not lock the SolarMutex,
+ leaving that to the calling code. This is because only
+ there potential deadlock situations can be resolved. Thus,
+ make sure SolarMutex is locked when calling this.
+ */
+ void SetEEOffset( const Point& rOffset );
+
+ /** Set the EditEngine offset
+
+ @attention This method does not lock the SolarMutex,
+ leaving that to the calling code. This is because only
+ there potential deadlock situations can be resolved. Thus,
+ make sure SolarMutex is locked when calling this.
+ */
+ void SetEditSource( SvxEditSource* pEditSource );
+
+ /** Dispose this object
+
+ Notifies and deregisters the listeners, drops all references.
+ */
+ void Dispose();
+
+ /** Set the current paragraph number
+
+ @attention This method does not lock the SolarMutex,
+ leaving that to the calling code. This is because only
+ there potential deadlock situations can be resolved. Thus,
+ make sure SolarMutex is locked when calling this.
+ */
+ void SetParagraphIndex( sal_Int32 nIndex );
+
+ /** Query the current paragraph number (0 - nParas-1)
+
+ @attention This method does not lock the SolarMutex,
+ leaving that to the calling code. This is because only
+ there potential deadlock situations can be resolved. Thus,
+ make sure SolarMutex is locked when calling this.
+ */
+ sal_Int32 GetParagraphIndex() const { return mnParagraphIndex; }
+
+ /// Calls all Listener objects to tell them the change. Don't hold locks when calling this!
+ void FireEvent(const sal_Int16 nEventId, const css::uno::Any& rNewValue, const css::uno::Any& rOldValue = css::uno::Any() ) const;
+
+ private:
+ AccessibleImageBullet( const AccessibleImageBullet& ) = delete;
+ AccessibleImageBullet& operator= ( const AccessibleImageBullet& ) = delete;
+
+ // maintain state set and send STATE_CHANGE events
+ void SetState( const sal_Int64 nStateId );
+ void UnSetState( const sal_Int64 nStateId );
+
+ SvxEditSource& GetEditSource() const;
+
+ int getNotifierClientId() const { return mnNotifierClientId; }
+
+ /** Query the SvxTextForwarder for EditEngine access.
+
+ @attention This method does not lock the SolarMutex,
+ leaving that to the calling code. This is because only
+ there potential deadlock situations can be resolved. Thus,
+ make sure SolarMutex is locked when calling this.
+ */
+ SvxTextForwarder& GetTextForwarder() const;
+
+ /** Query the SvxViewForwarder for EditEngine access.
+
+ @attention This method does not lock the SolarMutex,
+ leaving that to the calling code. This is because only
+ there potential deadlock situations can be resolved. Thus,
+ make sure SolarMutex is locked when calling this.
+ */
+ SvxViewForwarder& GetViewForwarder() const;
+
+ css::awt::Rectangle implGetBounds();
+
+ // the paragraph index in the edit engine (guarded by solar mutex)
+ sal_Int32 mnParagraphIndex;
+
+ // our current index in the parent (guarded by solar mutex)
+ sal_Int32 mnIndexInParent;
+
+ // the current edit source (guarded by solar mutex)
+ SvxEditSource* mpEditSource;
+
+ // the offset of the underlying EditEngine from the shape/cell (guarded by solar mutex)
+ Point maEEOffset;
+
+ // the current state set (updated from SetState/UnSetState and guarded by solar mutex)
+ sal_Int64 mnStateSet = 0;
+
+ /// The shape we're the accessible for (unguarded)
+ css::uno::Reference< css::accessibility::XAccessible > mxParent;
+
+ /// Our listeners (guarded by maMutex)
+ int mnNotifierClientId;
+ };
+
+} // end of namespace accessibility
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/accessibility/AccessibleParaManager.cxx b/editeng/source/accessibility/AccessibleParaManager.cxx
new file mode 100644
index 0000000000..c88f82d677
--- /dev/null
+++ b/editeng/source/accessibility/AccessibleParaManager.cxx
@@ -0,0 +1,408 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+// Global header
+
+
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <o3tl/safeint.hxx>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+#include <com/sun/star/accessibility/XAccessible.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+
+
+// Project-local header
+
+
+#include <editeng/AccessibleParaManager.hxx>
+#include <editeng/AccessibleEditableTextPara.hxx>
+
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+
+namespace accessibility
+{
+ AccessibleParaManager::AccessibleParaManager() :
+ maChildren(1),
+ mnChildStates( 0 ),
+ maEEOffset( 0, 0 ),
+ mnFocusedChild( -1 ),
+ mbActive( false )
+ {
+ }
+
+ AccessibleParaManager::~AccessibleParaManager()
+ {
+ // owner is responsible for possible child death
+ }
+
+ void AccessibleParaManager::SetAdditionalChildStates( sal_Int64 nChildStates )
+ {
+ mnChildStates = nChildStates;
+ }
+
+ void AccessibleParaManager::SetNum( sal_Int32 nNumParas )
+ {
+ if( o3tl::make_unsigned(nNumParas) < maChildren.size() )
+ Release( nNumParas, maChildren.size() );
+
+ maChildren.resize( nNumParas );
+
+ if( mnFocusedChild >= nNumParas )
+ mnFocusedChild = -1;
+ }
+
+ sal_Int32 AccessibleParaManager::GetNum() const
+ {
+ size_t nSize = maChildren.size();
+ if (nSize > SAL_MAX_INT32)
+ {
+ SAL_WARN( "editeng", "AccessibleParaManager::GetNum - overflow " << nSize);
+ return SAL_MAX_INT32;
+ }
+ return static_cast<sal_Int32>(nSize);
+ }
+
+ AccessibleParaManager::VectorOfChildren::iterator AccessibleParaManager::begin()
+ {
+ return maChildren.begin();
+ }
+
+ AccessibleParaManager::VectorOfChildren::iterator AccessibleParaManager::end()
+ {
+ return maChildren.end();
+ }
+
+ void AccessibleParaManager::FireEvent( sal_Int32 nPara,
+ const sal_Int16 nEventId ) const
+ {
+ DBG_ASSERT( 0 <= nPara && maChildren.size() > o3tl::make_unsigned(nPara),
+ "AccessibleParaManager::FireEvent: invalid index" );
+
+ if( 0 <= nPara && maChildren.size() > o3tl::make_unsigned(nPara) )
+ {
+ auto aChild( GetChild( nPara ).first.get() );
+ if( aChild.is() )
+ aChild->FireEvent( nEventId );
+ }
+ }
+
+ bool AccessibleParaManager::IsReferencable(
+ rtl::Reference<AccessibleEditableTextPara> const & aChild)
+ {
+ return aChild.is();
+ }
+
+ bool AccessibleParaManager::IsReferencable( sal_Int32 nChild ) const
+ {
+ DBG_ASSERT( 0 <= nChild && maChildren.size() > o3tl::make_unsigned(nChild),
+ "AccessibleParaManager::IsReferencable: invalid index" );
+
+ if( 0 <= nChild && maChildren.size() > o3tl::make_unsigned(nChild) )
+ {
+ // retrieve hard reference from weak one
+ return IsReferencable( GetChild( nChild ).first.get() );
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ AccessibleParaManager::WeakChild AccessibleParaManager::GetChild( sal_Int32 nParagraphIndex ) const
+ {
+ DBG_ASSERT( 0 <= nParagraphIndex && maChildren.size() > o3tl::make_unsigned(nParagraphIndex),
+ "AccessibleParaManager::GetChild: invalid index" );
+
+ if( 0 <= nParagraphIndex && maChildren.size() > o3tl::make_unsigned(nParagraphIndex) )
+ {
+ return maChildren[ nParagraphIndex ];
+ }
+ else
+ {
+ return WeakChild();
+ }
+ }
+
+ bool AccessibleParaManager::HasCreatedChild( sal_Int32 nParagraphIndex ) const
+ {
+ if( 0 <= nParagraphIndex && maChildren.size() > o3tl::make_unsigned(nParagraphIndex) )
+ {
+ auto const & rChild = maChildren[ nParagraphIndex ];
+ return rChild.second.Width != 0 || rChild.second.Height != 0;
+ }
+ else
+ return false;
+ }
+
+ AccessibleParaManager::Child AccessibleParaManager::CreateChild( sal_Int32 nChild,
+ const uno::Reference< XAccessible >& xFrontEnd,
+ SvxEditSourceAdapter& rEditSource,
+ sal_Int32 nParagraphIndex )
+ {
+ DBG_ASSERT( 0 <= nParagraphIndex && maChildren.size() > o3tl::make_unsigned(nParagraphIndex),
+ "AccessibleParaManager::CreateChild: invalid index" );
+
+ if( 0 <= nParagraphIndex && maChildren.size() > o3tl::make_unsigned(nParagraphIndex) )
+ {
+ // retrieve hard reference from weak one
+ auto aChild( GetChild( nParagraphIndex ).first.get() );
+
+ if( !IsReferencable( nParagraphIndex ) )
+ {
+ // there is no hard reference available, create object then
+ // #i27138#
+ aChild = new AccessibleEditableTextPara(xFrontEnd, this);
+
+ InitChild( *aChild, rEditSource, nChild, nParagraphIndex );
+
+ maChildren[ nParagraphIndex ] = WeakChild( aChild, aChild->getBounds() );
+ }
+
+ return Child( aChild.get(), GetChild( nParagraphIndex ).second );
+ }
+ else
+ {
+ return Child();
+ }
+ }
+
+ void AccessibleParaManager::SetEEOffset( const Point& rOffset )
+ {
+ maEEOffset = rOffset;
+
+ MemFunAdapter< const Point& > aAdapter( &::accessibility::AccessibleEditableTextPara::SetEEOffset, rOffset );
+ std::for_each( begin(), end(), aAdapter );
+ }
+
+ void AccessibleParaManager::SetActive( bool bActive )
+ {
+ mbActive = bActive;
+
+ if( bActive )
+ {
+ SetState( AccessibleStateType::ACTIVE );
+ SetState( AccessibleStateType::EDITABLE );
+ }
+ else
+ {
+ UnSetState( AccessibleStateType::ACTIVE );
+ UnSetState( AccessibleStateType::EDITABLE );
+ }
+ }
+
+ void AccessibleParaManager::SetFocus( sal_Int32 nChild )
+ {
+ if( mnFocusedChild != -1 )
+ UnSetState( mnFocusedChild, AccessibleStateType::FOCUSED );
+
+ mnFocusedChild = nChild;
+
+ if( mnFocusedChild != -1 )
+ SetState( mnFocusedChild, AccessibleStateType::FOCUSED );
+ }
+
+ void AccessibleParaManager::InitChild( AccessibleEditableTextPara& rChild,
+ SvxEditSourceAdapter& rEditSource,
+ sal_Int32 nChild,
+ sal_Int32 nParagraphIndex ) const
+ {
+ rChild.SetEditSource( &rEditSource );
+ rChild.SetIndexInParent( nChild );
+ rChild.SetParagraphIndex( nParagraphIndex );
+
+ rChild.SetEEOffset( maEEOffset );
+
+ if( mbActive )
+ {
+ rChild.SetState( AccessibleStateType::ACTIVE );
+ rChild.SetState( AccessibleStateType::EDITABLE );
+ }
+
+ if( mnFocusedChild == nParagraphIndex )
+ rChild.SetState( AccessibleStateType::FOCUSED );
+
+ // add states passed from outside
+ for (int i=0; i<63; i++)
+ {
+ sal_Int64 nState = sal_Int64(1) << i;
+ if ( nState & mnChildStates )
+ rChild.SetState( nState );
+ }
+ }
+
+ void AccessibleParaManager::SetState( sal_Int32 nChild, const sal_Int64 nStateId )
+ {
+ MemFunAdapter< const sal_Int64 > aFunc( &AccessibleEditableTextPara::SetState,
+ nStateId );
+ aFunc( GetChild(nChild) );
+ }
+
+ void AccessibleParaManager::SetState( const sal_Int64 nStateId )
+ {
+ std::for_each( begin(), end(),
+ MemFunAdapter< const sal_Int64 >( &AccessibleEditableTextPara::SetState,
+ nStateId ) );
+ }
+
+ void AccessibleParaManager::UnSetState( sal_Int32 nChild, const sal_Int64 nStateId )
+ {
+ MemFunAdapter< const sal_Int64 > aFunc( &AccessibleEditableTextPara::UnSetState,
+ nStateId );
+ aFunc( GetChild(nChild) );
+ }
+
+ void AccessibleParaManager::UnSetState( const sal_Int64 nStateId )
+ {
+ std::for_each( begin(), end(),
+ MemFunAdapter< const sal_Int64 >( &AccessibleEditableTextPara::UnSetState,
+ nStateId ) );
+ }
+
+ namespace {
+
+ // not generic yet, no arguments...
+ class AccessibleParaManager_DisposeChildren
+ {
+ public:
+ AccessibleParaManager_DisposeChildren() {}
+ void operator()( ::accessibility::AccessibleEditableTextPara& rPara )
+ {
+ rPara.Dispose();
+ }
+ };
+
+ }
+
+ void AccessibleParaManager::Dispose()
+ {
+ AccessibleParaManager_DisposeChildren aFunctor;
+
+ std::for_each( begin(), end(),
+ WeakChildAdapter< AccessibleParaManager_DisposeChildren > (aFunctor) );
+ }
+
+ namespace {
+
+ // not generic yet, too many method arguments...
+ class StateChangeEvent
+ {
+ public:
+ StateChangeEvent( const sal_Int16 nEventId,
+ const uno::Any& rNewValue,
+ const uno::Any& rOldValue ) :
+ mnEventId( nEventId ),
+ mrNewValue( rNewValue ),
+ mrOldValue( rOldValue ) {}
+ void operator()( ::accessibility::AccessibleEditableTextPara const & rPara )
+ {
+ rPara.FireEvent( mnEventId, mrNewValue, mrOldValue );
+ }
+
+ private:
+ const sal_Int16 mnEventId;
+ const uno::Any& mrNewValue;
+ const uno::Any& mrOldValue;
+ };
+
+ }
+
+ void AccessibleParaManager::FireEvent( sal_Int32 nStartPara,
+ sal_Int32 nEndPara,
+ const sal_Int16 nEventId,
+ const uno::Any& rNewValue,
+ const uno::Any& rOldValue ) const
+ {
+ DBG_ASSERT( 0 <= nStartPara && 0 <= nEndPara &&
+ maChildren.size() > o3tl::make_unsigned(nStartPara) &&
+ maChildren.size() >= o3tl::make_unsigned(nEndPara) &&
+ nEndPara >= nStartPara, "AccessibleParaManager::FireEvent: invalid index" );
+
+
+ if( 0 <= nStartPara && 0 <= nEndPara &&
+ maChildren.size() > o3tl::make_unsigned(nStartPara) &&
+ maChildren.size() >= o3tl::make_unsigned(nEndPara) &&
+ nEndPara >= nStartPara )
+ {
+ VectorOfChildren::const_iterator front = maChildren.begin();
+ VectorOfChildren::const_iterator back = front;
+
+ std::advance( front, nStartPara );
+ std::advance( back, nEndPara );
+
+ StateChangeEvent aFunctor( nEventId, rNewValue, rOldValue );
+
+ std::for_each( front, back, AccessibleParaManager::WeakChildAdapter< StateChangeEvent >( aFunctor ) );
+ }
+ }
+
+ namespace {
+
+ class ReleaseChild
+ {
+ public:
+ AccessibleParaManager::WeakChild operator()( const AccessibleParaManager::WeakChild& rPara )
+ {
+ AccessibleParaManager::ShutdownPara( rPara );
+
+ // clear reference
+ return AccessibleParaManager::WeakChild();
+ }
+ };
+
+ }
+
+ void AccessibleParaManager::Release( sal_Int32 nStartPara, sal_Int32 nEndPara )
+ {
+ DBG_ASSERT( 0 <= nStartPara && 0 <= nEndPara &&
+ maChildren.size() > o3tl::make_unsigned(nStartPara) &&
+ maChildren.size() >= o3tl::make_unsigned(nEndPara),
+ "AccessibleParaManager::Release: invalid index" );
+
+ if( 0 <= nStartPara && 0 <= nEndPara &&
+ maChildren.size() > o3tl::make_unsigned(nStartPara) &&
+ maChildren.size() >= o3tl::make_unsigned(nEndPara) )
+ {
+ VectorOfChildren::iterator front = maChildren.begin();
+ VectorOfChildren::iterator back = front;
+
+ std::advance( front, nStartPara );
+ std::advance( back, nEndPara );
+
+ std::transform( front, back, front, ReleaseChild() );
+ }
+ }
+
+ void AccessibleParaManager::ShutdownPara( const WeakChild& rChild )
+ {
+ auto aChild( rChild.first.get() );
+
+ if( IsReferencable( aChild ) )
+ aChild->SetEditSource( nullptr );
+ }
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/accessibility/AccessibleSelectionBase.cxx b/editeng/source/accessibility/AccessibleSelectionBase.cxx
new file mode 100644
index 0000000000..e70b618408
--- /dev/null
+++ b/editeng/source/accessibility/AccessibleSelectionBase.cxx
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <editeng/AccessibleSelectionBase.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+namespace accessibility
+{
+
+ // - AccessibleSelectionBase -
+
+
+ AccessibleSelectionBase::AccessibleSelectionBase()
+ {
+ }
+
+
+ AccessibleSelectionBase::~AccessibleSelectionBase()
+ {
+ }
+
+
+ void SAL_CALL AccessibleSelectionBase::selectAccessibleChild( sal_Int64 nChildIndex )
+ {
+ ::osl::MutexGuard aGuard( implGetMutex() );
+ OCommonAccessibleSelection::selectAccessibleChild( nChildIndex );
+ }
+
+
+ sal_Bool SAL_CALL AccessibleSelectionBase::isAccessibleChildSelected( sal_Int64 nChildIndex )
+ {
+ ::osl::MutexGuard aGuard( implGetMutex() );
+ return OCommonAccessibleSelection::isAccessibleChildSelected( nChildIndex );
+ }
+
+
+ void SAL_CALL AccessibleSelectionBase::clearAccessibleSelection( )
+ {
+ ::osl::MutexGuard aGuard( implGetMutex() );
+ OCommonAccessibleSelection::clearAccessibleSelection();
+ }
+
+
+ void SAL_CALL AccessibleSelectionBase::selectAllAccessibleChildren( )
+ {
+ ::osl::MutexGuard aGuard( implGetMutex() );
+ OCommonAccessibleSelection::selectAllAccessibleChildren();
+ }
+
+
+ sal_Int64 SAL_CALL AccessibleSelectionBase::getSelectedAccessibleChildCount( )
+ {
+ ::osl::MutexGuard aGuard( implGetMutex() );
+ return OCommonAccessibleSelection::getSelectedAccessibleChildCount();
+ }
+
+
+ uno::Reference< XAccessible > SAL_CALL AccessibleSelectionBase::getSelectedAccessibleChild( sal_Int64 nSelectedChildIndex )
+ {
+ ::osl::MutexGuard aGuard( implGetMutex() );
+ return OCommonAccessibleSelection::getSelectedAccessibleChild( nSelectedChildIndex );
+ }
+
+
+ void SAL_CALL AccessibleSelectionBase::deselectAccessibleChild( sal_Int64 nSelectedChildIndex )
+ {
+ ::osl::MutexGuard aGuard( implGetMutex() );
+ OCommonAccessibleSelection::deselectAccessibleChild( nSelectedChildIndex );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/accessibility/AccessibleStaticTextBase.cxx b/editeng/source/accessibility/AccessibleStaticTextBase.cxx
new file mode 100644
index 0000000000..f7fd934dfc
--- /dev/null
+++ b/editeng/source/accessibility/AccessibleStaticTextBase.cxx
@@ -0,0 +1,964 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+// Global header
+
+
+#include <utility>
+#include <memory>
+#include <vector>
+#include <algorithm>
+#include <rtl/ustrbuf.hxx>
+#include <tools/debug.hxx>
+#include <vcl/svapp.hxx>
+#include <comphelper/sequence.hxx>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/awt/Point.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/accessibility/AccessibleTextType.hpp>
+
+
+// Project-local header
+
+
+#include <editeng/editdata.hxx>
+#include <editeng/unoedprx.hxx>
+#include <editeng/AccessibleStaticTextBase.hxx>
+#include <editeng/AccessibleEditableTextPara.hxx>
+
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+/* TODO:
+ =====
+
+ - separate adapter functionality from AccessibleStaticText class
+
+ - refactor common loops into templates, using mem_fun
+
+ */
+
+namespace accessibility
+{
+ typedef std::vector< beans::PropertyValue > PropertyValueVector;
+
+ namespace {
+
+ class PropertyValueEqualFunctor
+ {
+ const beans::PropertyValue& m_rPValue;
+
+ public:
+ explicit PropertyValueEqualFunctor(const beans::PropertyValue& rPValue)
+ : m_rPValue(rPValue)
+ {}
+ bool operator() ( const beans::PropertyValue& rhs ) const
+ {
+ return ( m_rPValue.Name == rhs.Name && m_rPValue.Value == rhs.Value );
+ }
+ };
+
+ }
+
+ sal_Unicode const cNewLine(0x0a);
+
+
+ // Static Helper
+
+
+ static ESelection MakeSelection( sal_Int32 nStartPara, sal_Int32 nStartIndex,
+ sal_Int32 nEndPara, sal_Int32 nEndIndex )
+ {
+ DBG_ASSERT(nStartPara >= 0 &&
+ nStartIndex >= 0 &&
+ nEndPara >= 0 &&
+ nEndIndex >= 0,
+ "AccessibleStaticTextBase_Impl::MakeSelection: index value overflow");
+
+ return ESelection(nStartPara, nStartIndex, nEndPara, nEndIndex);
+ }
+
+
+ // AccessibleStaticTextBase_Impl declaration
+
+
+ /** AccessibleStaticTextBase_Impl
+
+ This class implements the AccessibleStaticTextBase
+ functionality, mainly by forwarding the calls to an aggregated
+ AccessibleEditableTextPara. As this is a therefore non-trivial
+ adapter, factoring out the common functionality from
+ AccessibleEditableTextPara might be a profitable future task.
+ */
+ class AccessibleStaticTextBase_Impl
+ {
+ friend class AccessibleStaticTextBase;
+ public:
+
+ // receive pointer to our frontend class and view window
+ AccessibleStaticTextBase_Impl();
+
+ void SetEditSource( std::unique_ptr< SvxEditSource > && pEditSource );
+
+ void SetEventSource( const uno::Reference< XAccessible >& rInterface )
+ {
+ mpThis = rInterface.get();
+ }
+
+ void SetOffset( const Point& );
+
+ void Dispose();
+
+ AccessibleEditableTextPara& GetParagraph( sal_Int32 nPara ) const;
+ sal_Int32 GetParagraphCount() const;
+
+ EPosition Index2Internal( sal_Int32 nFlatIndex ) const
+ {
+
+ return ImpCalcInternal( nFlatIndex, false );
+ }
+
+ EPosition Range2Internal( sal_Int32 nFlatIndex ) const
+ {
+
+ return ImpCalcInternal( nFlatIndex, true );
+ }
+
+ sal_Int32 Internal2Index( EPosition nEEIndex ) const;
+
+ void CorrectTextSegment( TextSegment& aTextSegment,
+ int nPara ) const;
+
+ bool SetSelection( sal_Int32 nStartPara, sal_Int32 nStartIndex,
+ sal_Int32 nEndPara, sal_Int32 nEndIndex );
+ bool CopyText( sal_Int32 nStartPara, sal_Int32 nStartIndex,
+ sal_Int32 nEndPara, sal_Int32 nEndIndex );
+
+ tools::Rectangle GetParagraphBoundingBox() const;
+ bool RemoveLineBreakCount( sal_Int32& rIndex );
+
+ private:
+
+ EPosition ImpCalcInternal( sal_Int32 nFlatIndex, bool bExclusive ) const;
+
+ // our frontend class (the one implementing the actual
+ // interface). That's not necessarily the one containing the impl
+ // pointer. Note that this is not an uno::Reference to prevent ref-counting cycles and leaks.
+ XAccessible* mpThis;
+
+ // implements our functionality, we're just an adapter (guarded by solar mutex)
+ mutable rtl::Reference<AccessibleEditableTextPara> mxTextParagraph;
+
+ // a wrapper for the text forwarders (guarded by solar mutex)
+ mutable SvxEditSourceAdapter maEditSource;
+ };
+
+
+ // AccessibleStaticTextBase_Impl implementation
+
+
+ AccessibleStaticTextBase_Impl::AccessibleStaticTextBase_Impl()
+ : mpThis(nullptr)
+ , mxTextParagraph(new AccessibleEditableTextPara(nullptr))
+ {
+
+ // TODO: this is still somewhat of a hack, all the more since
+ // now the maTextParagraph has an empty parent reference set
+ }
+
+ void AccessibleStaticTextBase_Impl::SetEditSource( std::unique_ptr< SvxEditSource > && pEditSource )
+ {
+
+ maEditSource.SetEditSource( std::move(pEditSource) );
+ if( mxTextParagraph.is() )
+ mxTextParagraph->SetEditSource( &maEditSource );
+ }
+
+ void AccessibleStaticTextBase_Impl::SetOffset( const Point& rPoint )
+ {
+ if( mxTextParagraph.is() )
+ mxTextParagraph->SetEEOffset( rPoint );
+ }
+
+ void AccessibleStaticTextBase_Impl::Dispose()
+ {
+
+ // we're the owner of the paragraph, so destroy it, too
+ if( mxTextParagraph.is() )
+ mxTextParagraph->Dispose();
+
+ // drop references
+ mpThis = nullptr;
+ mxTextParagraph.clear();
+ }
+
+ AccessibleEditableTextPara& AccessibleStaticTextBase_Impl::GetParagraph( sal_Int32 nPara ) const
+ {
+
+ if( !mxTextParagraph.is() )
+ throw lang::DisposedException ("object has been already disposed", mpThis );
+
+ // TODO: Have a different method on AccessibleEditableTextPara
+ // that does not care about state changes
+ mxTextParagraph->SetParagraphIndex( nPara );
+
+ return *mxTextParagraph;
+ }
+
+ sal_Int32 AccessibleStaticTextBase_Impl::GetParagraphCount() const
+ {
+
+ if( !mxTextParagraph.is() )
+ return 0;
+ else
+ return mxTextParagraph->GetTextForwarder().GetParagraphCount();
+ }
+
+ sal_Int32 AccessibleStaticTextBase_Impl::Internal2Index( EPosition nEEIndex ) const
+ {
+ // XXX checks for overflow and returns maximum if so
+ sal_Int32 aRes(0);
+ for(sal_Int32 i=0; i<nEEIndex.nPara; ++i)
+ {
+ sal_Int32 nCount = GetParagraph(i).getCharacterCount();
+ if (SAL_MAX_INT32 - aRes > nCount)
+ return SAL_MAX_INT32;
+ aRes += nCount;
+ }
+
+ if (SAL_MAX_INT32 - aRes > nEEIndex.nIndex)
+ return SAL_MAX_INT32;
+ return aRes + nEEIndex.nIndex;
+ }
+
+ void AccessibleStaticTextBase_Impl::CorrectTextSegment( TextSegment& aTextSegment,
+ int nPara ) const
+ {
+ // Keep 'invalid' values at the TextSegment
+ if( aTextSegment.SegmentStart != -1 &&
+ aTextSegment.SegmentEnd != -1 )
+ {
+ // #112814# Correct TextSegment by paragraph offset
+ sal_Int32 nOffset(0);
+ int i;
+ for(i=0; i<nPara; ++i)
+ nOffset += GetParagraph(i).getCharacterCount();
+
+ aTextSegment.SegmentStart += nOffset;
+ aTextSegment.SegmentEnd += nOffset;
+ }
+ }
+
+ EPosition AccessibleStaticTextBase_Impl::ImpCalcInternal( sal_Int32 nFlatIndex, bool bExclusive ) const
+ {
+
+ if( nFlatIndex < 0 )
+ throw lang::IndexOutOfBoundsException("AccessibleStaticTextBase_Impl::Index2Internal: character index out of bounds",
+ mpThis);
+ // gratuitously accepting larger indices here, AccessibleEditableTextPara will throw eventually
+
+ sal_Int32 nCurrPara, nCurrIndex, nParas, nCurrCount;
+ for( nCurrPara=0, nParas=GetParagraphCount(), nCurrCount=0, nCurrIndex=0; nCurrPara<nParas; ++nCurrPara )
+ {
+ nCurrCount = GetParagraph( nCurrPara ).getCharacterCount();
+ nCurrIndex += nCurrCount;
+ if( nCurrIndex >= nFlatIndex )
+ {
+ // check overflow
+ DBG_ASSERT(nCurrPara >= 0 &&
+ nFlatIndex - nCurrIndex + nCurrCount >= 0,
+ "AccessibleStaticTextBase_Impl::Index2Internal: index value overflow");
+
+ return EPosition(nCurrPara, nFlatIndex - nCurrIndex + nCurrCount);
+ }
+ }
+
+ // #102170# Allow one-past the end for ranges
+ if( bExclusive && nCurrIndex == nFlatIndex )
+ {
+ // check overflow
+ DBG_ASSERT(nCurrPara > 0 &&
+ nFlatIndex - nCurrIndex + nCurrCount >= 0,
+ "AccessibleStaticTextBase_Impl::Index2Internal: index value overflow");
+
+ return EPosition(nCurrPara-1, nFlatIndex - nCurrIndex + nCurrCount);
+ }
+
+ // not found? Out of bounds
+ throw lang::IndexOutOfBoundsException("AccessibleStaticTextBase_Impl::Index2Internal: character index out of bounds",
+ mpThis);
+ }
+
+ bool AccessibleStaticTextBase_Impl::SetSelection( sal_Int32 nStartPara, sal_Int32 nStartIndex,
+ sal_Int32 nEndPara, sal_Int32 nEndIndex )
+ {
+
+ if( !mxTextParagraph.is() )
+ return false;
+
+ try
+ {
+ SvxEditViewForwarder& rCacheVF = mxTextParagraph->GetEditViewForwarder( true );
+ return rCacheVF.SetSelection( MakeSelection(nStartPara, nStartIndex, nEndPara, nEndIndex) );
+ }
+ catch( const uno::RuntimeException& )
+ {
+ return false;
+ }
+ }
+
+ bool AccessibleStaticTextBase_Impl::CopyText( sal_Int32 nStartPara, sal_Int32 nStartIndex,
+ sal_Int32 nEndPara, sal_Int32 nEndIndex )
+ {
+
+ if( !mxTextParagraph.is() )
+ return false;
+
+ try
+ {
+ SvxEditViewForwarder& rCacheVF = mxTextParagraph->GetEditViewForwarder( true );
+ mxTextParagraph->GetTextForwarder(); // MUST be after GetEditViewForwarder(), see method docs
+ bool aRetVal;
+
+ // save current selection
+ ESelection aOldSelection;
+
+ rCacheVF.GetSelection( aOldSelection );
+ rCacheVF.SetSelection( MakeSelection(nStartPara, nStartIndex, nEndPara, nEndIndex) );
+ aRetVal = rCacheVF.Copy();
+ rCacheVF.SetSelection( aOldSelection ); // restore
+
+ return aRetVal;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ return false;
+ }
+ }
+
+ tools::Rectangle AccessibleStaticTextBase_Impl::GetParagraphBoundingBox() const
+ {
+ tools::Rectangle aRect;
+ if( mxTextParagraph.is() )
+ {
+ awt::Rectangle aAwtRect = mxTextParagraph->getBounds();
+ aRect = tools::Rectangle( Point( aAwtRect.X, aAwtRect.Y ), Size( aAwtRect.Width, aAwtRect.Height ) );
+ }
+ else
+ {
+ aRect.SetEmpty();
+ }
+ return aRect;
+ }
+ //the input argument is the index(including "\n" ) in the string.
+ //the function will calculate the actual index(not including "\n") in the string.
+ //and return true if the index is just at a "\n"
+ bool AccessibleStaticTextBase_Impl::RemoveLineBreakCount( sal_Int32& rIndex )
+ {
+ // get the total char number inside the cell.
+ sal_Int32 i, nCount, nParas;
+ for( i=0, nCount=0, nParas=GetParagraphCount(); i<nParas; ++i )
+ nCount += GetParagraph(i).getCharacterCount();
+ nCount = nCount + (nParas-1);
+ if( nCount == 0 && rIndex == 0) return false;
+
+
+ sal_Int32 nCurrPara, nCurrCount;
+ sal_Int32 nLineBreakPos = 0, nLineBreakCount = 0;
+ sal_Int32 nParaCount = GetParagraphCount();
+ for ( nCurrCount = 0, nCurrPara = 0; nCurrPara < nParaCount; nCurrPara++ )
+ {
+ nCurrCount += GetParagraph( nCurrPara ).getCharacterCount();
+ nLineBreakPos = nCurrCount++;
+ if ( rIndex == nLineBreakPos )
+ {
+ rIndex -= (++nLineBreakCount);//(++nLineBreakCount);
+ if ( rIndex < 0)
+ {
+ rIndex = 0;
+ }
+ //if the index is at the last position of the last paragraph
+ //there is no "\n" , so we should increase rIndex by 1 and return false.
+ if ( (nCurrPara+1) == nParaCount )
+ {
+ rIndex++;
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+ }
+ else if ( rIndex < nLineBreakPos )
+ {
+ rIndex -= nLineBreakCount;
+ return false;
+ }
+ else
+ {
+ nLineBreakCount++;
+ }
+ }
+ return false;
+ }
+
+
+ // AccessibleStaticTextBase implementation
+
+ AccessibleStaticTextBase::AccessibleStaticTextBase( std::unique_ptr< SvxEditSource > && pEditSource ) :
+ mpImpl( new AccessibleStaticTextBase_Impl() )
+ {
+ SolarMutexGuard aGuard;
+
+ SetEditSource( std::move(pEditSource) );
+ }
+
+ AccessibleStaticTextBase::~AccessibleStaticTextBase()
+ {
+ }
+
+ void AccessibleStaticTextBase::SetEditSource( std::unique_ptr< SvxEditSource > && pEditSource )
+ {
+ // precondition: solar mutex locked
+ DBG_TESTSOLARMUTEX();
+
+ mpImpl->SetEditSource( std::move(pEditSource) );
+ }
+
+ void AccessibleStaticTextBase::SetEventSource( const uno::Reference< XAccessible >& rInterface )
+ {
+ mpImpl->SetEventSource( rInterface );
+
+ }
+
+ void AccessibleStaticTextBase::SetOffset( const Point& rPoint )
+ {
+ // precondition: solar mutex locked
+ DBG_TESTSOLARMUTEX();
+
+ mpImpl->SetOffset( rPoint );
+ }
+
+ void AccessibleStaticTextBase::Dispose()
+ {
+ mpImpl->Dispose();
+
+ }
+
+ // XAccessibleContext
+ sal_Int64 AccessibleStaticTextBase::getAccessibleChildCount()
+ {
+ // no children at all
+ return 0;
+ }
+
+ uno::Reference< XAccessible > AccessibleStaticTextBase::getAccessibleChild( sal_Int64 /*i*/ )
+ {
+ // no children at all
+ return uno::Reference< XAccessible >();
+ }
+
+ uno::Reference< XAccessible > AccessibleStaticTextBase::getAccessibleAtPoint( const awt::Point& /*_aPoint*/ )
+ {
+ // no children at all
+ return uno::Reference< XAccessible >();
+ }
+
+ // XAccessibleText
+ sal_Int32 SAL_CALL AccessibleStaticTextBase::getCaretPosition()
+ {
+ SolarMutexGuard aGuard;
+
+ sal_Int32 i, nPos, nParas;
+ for( i=0, nPos=-1, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
+ {
+ if( (nPos=mpImpl->GetParagraph(i).getCaretPosition()) != -1 )
+ return nPos;
+ }
+
+ return nPos;
+ }
+
+ sal_Bool SAL_CALL AccessibleStaticTextBase::setCaretPosition( sal_Int32 nIndex )
+ {
+ return setSelection(nIndex, nIndex);
+ }
+
+ sal_Unicode SAL_CALL AccessibleStaticTextBase::getCharacter( sal_Int32 nIndex )
+ {
+ SolarMutexGuard aGuard;
+
+ EPosition aPos( mpImpl->Index2Internal(nIndex) );
+
+ return mpImpl->GetParagraph( aPos.nPara ).getCharacter( aPos.nIndex );
+ }
+
+ uno::Sequence< beans::PropertyValue > SAL_CALL AccessibleStaticTextBase::getCharacterAttributes( sal_Int32 nIndex, const css::uno::Sequence< OUString >& aRequestedAttributes )
+ {
+ SolarMutexGuard aGuard;
+
+ //get the actual index without "\n"
+ mpImpl->RemoveLineBreakCount( nIndex );
+
+ EPosition aPos( mpImpl->Index2Internal(nIndex) );
+
+ return mpImpl->GetParagraph( aPos.nPara ).getCharacterAttributes( aPos.nIndex, aRequestedAttributes );
+ }
+
+ awt::Rectangle SAL_CALL AccessibleStaticTextBase::getCharacterBounds( sal_Int32 nIndex )
+ {
+ SolarMutexGuard aGuard;
+
+ // #108900# Allow ranges for nIndex, as one-past-the-end
+ // values are now legal, too.
+ EPosition aPos( mpImpl->Range2Internal(nIndex) );
+
+ // #i70916# Text in spread sheet cells return the wrong extents
+ AccessibleEditableTextPara& rPara = mpImpl->GetParagraph( aPos.nPara );
+ awt::Rectangle aParaBounds( rPara.getBounds() );
+ awt::Rectangle aBounds( rPara.getCharacterBounds( aPos.nIndex ) );
+ aBounds.X += aParaBounds.X;
+ aBounds.Y += aParaBounds.Y;
+
+ return aBounds;
+ }
+
+ sal_Int32 SAL_CALL AccessibleStaticTextBase::getCharacterCount()
+ {
+ SolarMutexGuard aGuard;
+
+ sal_Int32 i, nCount, nParas;
+ for( i=0, nCount=0, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
+ nCount += mpImpl->GetParagraph(i).getCharacterCount();
+ //count on the number of "\n" which equals number of paragraphs decrease 1.
+ nCount = nCount + (nParas-1);
+ return nCount;
+ }
+
+ sal_Int32 SAL_CALL AccessibleStaticTextBase::getIndexAtPoint( const awt::Point& rPoint )
+ {
+ SolarMutexGuard aGuard;
+
+ const sal_Int32 nParas( mpImpl->GetParagraphCount() );
+ sal_Int32 nIndex;
+ int i;
+ for( i=0; i<nParas; ++i )
+ {
+ // TODO: maybe exploit the fact that paragraphs are
+ // ordered vertically for early exit
+
+ // #i70916# Text in spread sheet cells return the wrong extents
+ AccessibleEditableTextPara& rPara = mpImpl->GetParagraph( i );
+ awt::Rectangle aParaBounds( rPara.getBounds() );
+ awt::Point aPoint( rPoint );
+ aPoint.X -= aParaBounds.X;
+ aPoint.Y -= aParaBounds.Y;
+
+ // #112814# Use correct index offset
+ if ( ( nIndex = rPara.getIndexAtPoint( aPoint ) ) != -1 )
+ return mpImpl->Internal2Index(EPosition(i, nIndex));
+ }
+
+ return -1;
+ }
+
+ OUString SAL_CALL AccessibleStaticTextBase::getSelectedText()
+ {
+ SolarMutexGuard aGuard;
+
+ sal_Int32 nStart( getSelectionStart() );
+ sal_Int32 nEnd( getSelectionEnd() );
+
+ // #104481# Return the empty string for 'no selection'
+ if( nStart < 0 || nEnd < 0 )
+ return OUString();
+
+ return getTextRange( nStart, nEnd );
+ }
+
+ sal_Int32 SAL_CALL AccessibleStaticTextBase::getSelectionStart()
+ {
+ SolarMutexGuard aGuard;
+
+ sal_Int32 i, nPos, nParas;
+ for( i=0, nPos=-1, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
+ {
+ if( (nPos=mpImpl->GetParagraph(i).getSelectionStart()) != -1 )
+ return nPos;
+ }
+
+ return nPos;
+ }
+
+ sal_Int32 SAL_CALL AccessibleStaticTextBase::getSelectionEnd()
+ {
+ SolarMutexGuard aGuard;
+
+ sal_Int32 i, nPos, nParas;
+ for( i=0, nPos=-1, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
+ {
+ if( (nPos=mpImpl->GetParagraph(i).getSelectionEnd()) != -1 )
+ return nPos;
+ }
+
+ return nPos;
+ }
+
+ sal_Bool SAL_CALL AccessibleStaticTextBase::setSelection( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
+ {
+ SolarMutexGuard aGuard;
+
+ EPosition aStartIndex( mpImpl->Range2Internal(nStartIndex) );
+ EPosition aEndIndex( mpImpl->Range2Internal(nEndIndex) );
+
+ return mpImpl->SetSelection( aStartIndex.nPara, aStartIndex.nIndex,
+ aEndIndex.nPara, aEndIndex.nIndex );
+ }
+
+ OUString SAL_CALL AccessibleStaticTextBase::getText()
+ {
+ SolarMutexGuard aGuard;
+
+ sal_Int32 i, nParas;
+ OUStringBuffer aRes;
+ for( i=0, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
+ aRes.append(mpImpl->GetParagraph(i).getText());
+
+ return aRes.makeStringAndClear();
+ }
+
+ OUString SAL_CALL AccessibleStaticTextBase::getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
+ {
+ SolarMutexGuard aGuard;
+
+ if( nStartIndex > nEndIndex )
+ std::swap(nStartIndex, nEndIndex);
+ //if startindex equals endindex we will get nothing. So return an empty string directly.
+ if ( nStartIndex == nEndIndex )
+ {
+ return OUString();
+ }
+ bool bStart = mpImpl->RemoveLineBreakCount( nStartIndex );
+ //if the start index is just at a "\n", we need to begin from the next char
+ if ( bStart )
+ {
+ nStartIndex++;
+ }
+ //we need to find out whether the previous position of the current endindex is at "\n" or not
+ //if yes we need to mark it and add "\n" at the end of the result
+ sal_Int32 nTemp = nEndIndex - 1;
+ bool bEnd = mpImpl->RemoveLineBreakCount( nTemp );
+ bool bTemp = mpImpl->RemoveLineBreakCount( nEndIndex );
+ //if the below condition is true it indicates an empty paragraph with just a "\n"
+ //so we need to set one "\n" flag to avoid duplication.
+ if ( bStart && bEnd && ( nStartIndex == nEndIndex) )
+ {
+ bEnd = false;
+ }
+ //if the current endindex is at a "\n", we need to increase endindex by 1 to make sure
+ //the char before "\n" is included. Because string returned by this function will not include
+ //the char at the endindex.
+ if ( bTemp )
+ {
+ nEndIndex++;
+ }
+ OUStringBuffer aRes;
+ EPosition aStartIndex( mpImpl->Range2Internal(nStartIndex) );
+ EPosition aEndIndex( mpImpl->Range2Internal(nEndIndex) );
+
+ // #102170# Special case: start and end paragraph are identical
+ if( aStartIndex.nPara == aEndIndex.nPara )
+ {
+ //we don't return the string directly now for that we have to do some further process for "\n"
+ aRes = mpImpl->GetParagraph( aStartIndex.nPara ).getTextRange( aStartIndex.nIndex, aEndIndex.nIndex );
+ }
+ else
+ {
+ sal_Int32 i( aStartIndex.nPara );
+ aRes = mpImpl->GetParagraph(i).getTextRange( aStartIndex.nIndex,
+ mpImpl->GetParagraph(i).getCharacterCount()/*-1*/);
+ ++i;
+
+ // paragraphs inbetween are fully included
+ for( ; i<aEndIndex.nPara; ++i )
+ {
+ aRes.append(OUStringChar(cNewLine) + mpImpl->GetParagraph(i).getText());
+ }
+
+ if( i<=aEndIndex.nPara )
+ {
+ //if the below condition is matched it means that endindex is at mid of the last paragraph
+ //we need to add a "\n" before we add the last part of the string.
+ if ( !bEnd && aEndIndex.nIndex )
+ {
+ aRes.append(cNewLine);
+ }
+ aRes.append(mpImpl->GetParagraph(i).getTextRange( 0, aEndIndex.nIndex ));
+ }
+ }
+ //According to the flag we marked before, we have to add "\n" at the beginning
+ //or at the end of the result string.
+ if ( bStart )
+ {
+ aRes.insert(0, OUStringChar(cNewLine));
+ }
+ if ( bEnd )
+ {
+ aRes.append(OUStringChar(cNewLine));
+ }
+ return aRes.makeStringAndClear();
+ }
+
+ css::accessibility::TextSegment SAL_CALL AccessibleStaticTextBase::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType )
+ {
+ SolarMutexGuard aGuard;
+
+ bool bLineBreak = mpImpl->RemoveLineBreakCount( nIndex );
+ EPosition aPos( mpImpl->Range2Internal(nIndex) );
+
+ css::accessibility::TextSegment aResult;
+
+ if( AccessibleTextType::PARAGRAPH == aTextType )
+ {
+ // #106393# Special casing one behind last paragraph is
+ // not necessary, since then, we return the content and
+ // boundary of that last paragraph. Range2Internal is
+ // tolerant against that, and returns the last paragraph
+ // in aPos.nPara.
+
+ // retrieve full text of the paragraph
+ aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara ).getText();
+
+ // #112814# Adapt the start index with the paragraph offset
+ aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara, 0 ) );
+ aResult.SegmentEnd = aResult.SegmentStart + aResult.SegmentText.getLength();
+ }
+ else if ( AccessibleTextType::ATTRIBUTE_RUN == aTextType )
+ {
+ SvxAccessibleTextAdapter& rTextForwarder = mpImpl->GetParagraph( aPos.nIndex ).GetTextForwarder();
+ sal_Int32 nStartIndex, nEndIndex;
+ if ( rTextForwarder.GetAttributeRun( nStartIndex, nEndIndex, aPos.nPara, aPos.nIndex, true ) )
+ {
+ aResult.SegmentText = getTextRange( nStartIndex, nEndIndex );
+ aResult.SegmentStart = nStartIndex;
+ aResult.SegmentEnd = nEndIndex;
+ }
+ }
+ else
+ {
+ // No special handling required, forward to wrapped class
+ aResult = mpImpl->GetParagraph( aPos.nPara ).getTextAtIndex( aPos.nIndex, aTextType );
+
+ // #112814# Adapt the start index with the paragraph offset
+ mpImpl->CorrectTextSegment( aResult, aPos.nPara );
+ if ( bLineBreak )
+ {
+ aResult.SegmentText = OUString(cNewLine);
+ }
+ }
+
+ return aResult;
+ }
+
+ css::accessibility::TextSegment SAL_CALL AccessibleStaticTextBase::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType )
+ {
+ SolarMutexGuard aGuard;
+
+ sal_Int32 nOldIdx = nIndex;
+ bool bLineBreak = mpImpl->RemoveLineBreakCount( nIndex );
+ EPosition aPos( mpImpl->Range2Internal(nIndex) );
+
+ css::accessibility::TextSegment aResult;
+
+ if( AccessibleTextType::PARAGRAPH == aTextType )
+ {
+ if( aPos.nIndex == mpImpl->GetParagraph( aPos.nPara ).getCharacterCount() )
+ {
+ // #103589# Special casing one behind the last paragraph
+ aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara ).getText();
+
+ // #112814# Adapt the start index with the paragraph offset
+ aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara, 0 ) );
+ }
+ else if( aPos.nPara > 0 )
+ {
+ aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara - 1 ).getText();
+
+ // #112814# Adapt the start index with the paragraph offset
+ aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara - 1, 0 ) );
+ }
+
+ aResult.SegmentEnd = aResult.SegmentStart + aResult.SegmentText.getLength();
+ }
+ else
+ {
+ // No special handling required, forward to wrapped class
+ aResult = mpImpl->GetParagraph( aPos.nPara ).getTextBeforeIndex( aPos.nIndex, aTextType );
+
+ // #112814# Adapt the start index with the paragraph offset
+ mpImpl->CorrectTextSegment( aResult, aPos.nPara );
+ if ( bLineBreak && (nOldIdx-1) >= 0)
+ {
+ aResult = getTextAtIndex( nOldIdx-1, aTextType );
+ }
+ }
+
+ return aResult;
+ }
+
+ css::accessibility::TextSegment SAL_CALL AccessibleStaticTextBase::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType )
+ {
+ SolarMutexGuard aGuard;
+
+ sal_Int32 nTemp = nIndex+1;
+ bool bLineBreak = mpImpl->RemoveLineBreakCount( nTemp );
+ mpImpl->RemoveLineBreakCount( nIndex );
+ EPosition aPos( mpImpl->Range2Internal(nIndex) );
+
+ css::accessibility::TextSegment aResult;
+
+ if( AccessibleTextType::PARAGRAPH == aTextType )
+ {
+ // Special casing one behind the last paragraph is not
+ // necessary, this case is invalid here for
+ // getTextBehindIndex
+ if( aPos.nPara + 1 < mpImpl->GetParagraphCount() )
+ {
+ aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara + 1 ).getText();
+
+ // #112814# Adapt the start index with the paragraph offset
+ aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara + 1, 0 ) );
+ aResult.SegmentEnd = aResult.SegmentStart + aResult.SegmentText.getLength();
+ }
+ }
+ else
+ {
+ // No special handling required, forward to wrapped class
+ aResult = mpImpl->GetParagraph( aPos.nPara ).getTextBehindIndex( aPos.nIndex, aTextType );
+
+ // #112814# Adapt the start index with the paragraph offset
+ mpImpl->CorrectTextSegment( aResult, aPos.nPara );
+ if ( bLineBreak )
+ {
+ aResult.SegmentText = OUStringChar(cNewLine) + aResult.SegmentText;
+ }
+ }
+
+ return aResult;
+ }
+
+ sal_Bool SAL_CALL AccessibleStaticTextBase::copyText( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
+ {
+ SolarMutexGuard aGuard;
+
+ if( nStartIndex > nEndIndex )
+ std::swap(nStartIndex, nEndIndex);
+
+ EPosition aStartIndex( mpImpl->Range2Internal(nStartIndex) );
+ EPosition aEndIndex( mpImpl->Range2Internal(nEndIndex) );
+
+ return mpImpl->CopyText( aStartIndex.nPara, aStartIndex.nIndex,
+ aEndIndex.nPara, aEndIndex.nIndex );
+ }
+
+ sal_Bool SAL_CALL AccessibleStaticTextBase::scrollSubstringTo( sal_Int32, sal_Int32, AccessibleScrollType )
+ {
+ return false;
+ }
+
+ // XAccessibleTextAttributes
+ uno::Sequence< beans::PropertyValue > AccessibleStaticTextBase::getDefaultAttributes( const uno::Sequence< OUString >& RequestedAttributes )
+ {
+ // get the intersection of the default attributes of all paragraphs
+
+ SolarMutexGuard aGuard;
+
+ PropertyValueVector aDefAttrVec(
+ comphelper::sequenceToContainer<PropertyValueVector>(mpImpl->GetParagraph( 0 ).getDefaultAttributes( RequestedAttributes )) );
+
+ const sal_Int32 nParaCount = mpImpl->GetParagraphCount();
+ for ( sal_Int32 nPara = 1; nPara < nParaCount; ++nPara )
+ {
+ uno::Sequence< beans::PropertyValue > aSeq = mpImpl->GetParagraph( nPara ).getDefaultAttributes( RequestedAttributes );
+ PropertyValueVector aIntersectionVec;
+
+ for ( const auto& rDefAttr : aDefAttrVec )
+ {
+ const beans::PropertyValue* pItr = aSeq.getConstArray();
+ const beans::PropertyValue* pEnd = pItr + aSeq.getLength();
+ const beans::PropertyValue* pFind = std::find_if( pItr, pEnd, PropertyValueEqualFunctor(rDefAttr) );
+ if ( pFind != pEnd )
+ {
+ aIntersectionVec.push_back( *pFind );
+ }
+ }
+
+ aDefAttrVec.swap( aIntersectionVec );
+
+ if ( aDefAttrVec.empty() )
+ {
+ break;
+ }
+ }
+
+ return comphelper::containerToSequence(aDefAttrVec);
+ }
+
+ uno::Sequence< beans::PropertyValue > SAL_CALL AccessibleStaticTextBase::getRunAttributes( sal_Int32 nIndex, const uno::Sequence< OUString >& RequestedAttributes )
+ {
+ // get those default attributes of the paragraph, which are not part
+ // of the intersection of all paragraphs and add them to the run attributes
+
+ SolarMutexGuard aGuard;
+
+ EPosition aPos( mpImpl->Index2Internal( nIndex ) );
+ AccessibleEditableTextPara& rPara = mpImpl->GetParagraph( aPos.nPara );
+ uno::Sequence< beans::PropertyValue > aDefAttrSeq = rPara.getDefaultAttributes( RequestedAttributes );
+ uno::Sequence< beans::PropertyValue > aRunAttrSeq = rPara.getRunAttributes( aPos.nIndex, RequestedAttributes );
+ uno::Sequence< beans::PropertyValue > aIntersectionSeq = getDefaultAttributes( RequestedAttributes );
+ PropertyValueVector aDiffVec;
+
+ const beans::PropertyValue* pDefAttr = aDefAttrSeq.getConstArray();
+ const sal_Int32 nLength = aDefAttrSeq.getLength();
+ for ( sal_Int32 i = 0; i < nLength; ++i )
+ {
+ const beans::PropertyValue* pItr = aIntersectionSeq.getConstArray();
+ const beans::PropertyValue* pEnd = pItr + aIntersectionSeq.getLength();
+ bool bNone = std::none_of( pItr, pEnd, PropertyValueEqualFunctor( pDefAttr[i] ) );
+ if ( bNone && pDefAttr[i].Handle != 0)
+ {
+ aDiffVec.push_back( pDefAttr[i] );
+ }
+ }
+
+ return ::comphelper::concatSequences( aRunAttrSeq, comphelper::containerToSequence(aDiffVec) );
+ }
+
+ tools::Rectangle AccessibleStaticTextBase::GetParagraphBoundingBox() const
+ {
+ return mpImpl->GetParagraphBoundingBox();
+ }
+
+} // end of namespace accessibility
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/accessibility/AccessibleStringWrap.cxx b/editeng/source/accessibility/AccessibleStringWrap.cxx
new file mode 100644
index 0000000000..5461aad9f4
--- /dev/null
+++ b/editeng/source/accessibility/AccessibleStringWrap.cxx
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <algorithm>
+#include <cstdlib>
+
+#include <tools/debug.hxx>
+#include <utility>
+#include <vcl/outdev.hxx>
+
+#include <editeng/svxfont.hxx>
+#include <AccessibleStringWrap.hxx>
+
+
+// AccessibleStringWrap implementation
+
+
+AccessibleStringWrap::AccessibleStringWrap( OutputDevice& rDev, SvxFont& rFont, OUString aText ) :
+ mrDev( rDev ),
+ mrFont( rFont ),
+ maText(std::move( aText ))
+{
+}
+
+void AccessibleStringWrap::GetCharacterBounds( sal_Int32 nIndex, tools::Rectangle& rRect )
+{
+ DBG_ASSERT(nIndex >= 0,
+ "SvxAccessibleStringWrap::GetCharacterBounds: index value overflow");
+
+ mrFont.SetPhysFont(mrDev);
+
+ // #108900# Handle virtual position one-past-the end of the string
+ if( nIndex >= maText.getLength() )
+ {
+ // create a caret bounding rect that has the height of the
+ // current font and is one pixel wide.
+ rRect.SetLeft( mrDev.GetTextWidth(maText) );
+ rRect.SetTop( 0 );
+ rRect.SetSize( Size(mrDev.GetTextHeight(), 1) );
+ }
+ else
+ {
+ KernArray aDXArray;
+ mrDev.GetTextArray(maText, &aDXArray, nIndex, 1);
+ rRect.SetLeft( 0 );
+ rRect.SetTop( 0 );
+ rRect.SetSize(Size(mrDev.GetTextHeight(), aDXArray[0]));
+ }
+
+ if( mrFont.IsVertical() )
+ {
+ // #101701# Rotate to vertical
+ rRect = tools::Rectangle( Point(-rRect.Top(), rRect.Left()),
+ Point(-rRect.Bottom(), rRect.Right()));
+ }
+}
+
+sal_Int32 AccessibleStringWrap::GetIndexAtPoint( const Point& rPoint )
+{
+ // search for character bounding box containing given point
+ tools::Rectangle aRect;
+ sal_Int32 i, nLen = maText.getLength();
+ for( i=0; i<nLen; ++i )
+ {
+ GetCharacterBounds(i, aRect);
+ if( aRect.Contains(rPoint) )
+ return i;
+ }
+
+ return -1;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/editattr.cxx b/editeng/source/editeng/editattr.cxx
new file mode 100644
index 0000000000..75bbcabc5a
--- /dev/null
+++ b/editeng/source/editeng/editattr.cxx
@@ -0,0 +1,459 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/outdev.hxx>
+
+#include <svl/grabbagitem.hxx>
+#include <svl/voiditem.hxx>
+#include <libxml/xmlwriter.h>
+#include <editeng/svxfont.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/kernitem.hxx>
+#include <editeng/autokernitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/cmapitem.hxx>
+
+#include <editattr.hxx>
+
+
+EditCharAttrib::EditCharAttrib(SfxItemPool& rPool, const SfxPoolItem& rItem, sal_Int32 nS, sal_Int32 nE )
+: maItemHolder(rPool, &rItem)
+, nStart(nS)
+, nEnd(nE)
+, bFeature(false)
+, bEdge(false)
+{
+ assert((rItem.Which() >= EE_ITEMS_START) && (rItem.Which() <= EE_ITEMS_END));
+ assert((rItem.Which() < EE_FEATURE_START) || (rItem.Which() > EE_FEATURE_END) || (nE == (nS+1)));
+}
+
+EditCharAttrib::~EditCharAttrib()
+{
+}
+
+void EditCharAttrib::SetFont( SvxFont&, OutputDevice* )
+{
+}
+
+void EditCharAttrib::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("EditCharAttrib"));
+ (void)xmlTextWriterWriteFormatAttribute(
+ pWriter, BAD_CAST("nStart"), "%" SAL_PRIdINT32, nStart);
+ (void)xmlTextWriterWriteFormatAttribute(
+ pWriter, BAD_CAST("nEnd"), "%" SAL_PRIdINT32, nEnd);
+ GetItem()->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+
+
+EditCharAttribFont::EditCharAttribFont(SfxItemPool& rPool, const SfxPoolItem& rItem, sal_Int32 _nStart, sal_Int32 _nEnd)
+: EditCharAttrib(rPool, rItem, _nStart, _nEnd)
+{
+ assert(rItem.Which() == EE_CHAR_FONTINFO || rItem.Which() == EE_CHAR_FONTINFO_CJK || rItem.Which() == EE_CHAR_FONTINFO_CTL);
+}
+
+void EditCharAttribFont::SetFont( SvxFont& rFont, OutputDevice* )
+{
+ const SvxFontItem& rAttr = static_cast<const SvxFontItem&>(*GetItem());
+
+ rFont.SetFamilyName( rAttr.GetFamilyName() );
+ rFont.SetFamily( rAttr.GetFamily() );
+ rFont.SetPitch( rAttr.GetPitch() );
+ rFont.SetCharSet( rAttr.GetCharSet() );
+}
+
+
+
+EditCharAttribItalic::EditCharAttribItalic(SfxItemPool& rPool, const SfxPoolItem& rItem, sal_Int32 _nStart, sal_Int32 _nEnd)
+: EditCharAttrib(rPool, rItem, _nStart, _nEnd)
+{
+ assert(rItem.Which() == EE_CHAR_ITALIC || rItem.Which() == EE_CHAR_ITALIC_CJK || rItem.Which() == EE_CHAR_ITALIC_CTL);
+}
+
+void EditCharAttribItalic::SetFont( SvxFont& rFont, OutputDevice* )
+{
+ rFont.SetItalic( static_cast<const SvxPostureItem*>(GetItem())->GetPosture() );
+}
+
+
+
+EditCharAttribWeight::EditCharAttribWeight(SfxItemPool& rPool, const SfxPoolItem& rItem, sal_Int32 _nStart, sal_Int32 _nEnd)
+: EditCharAttrib(rPool, rItem, _nStart, _nEnd)
+{
+ assert(rItem.Which() == EE_CHAR_WEIGHT || rItem.Which() == EE_CHAR_WEIGHT_CJK || rItem.Which() == EE_CHAR_WEIGHT_CTL);
+}
+
+void EditCharAttribWeight::SetFont( SvxFont& rFont, OutputDevice* )
+{
+ rFont.SetWeight( static_cast<const SvxWeightItem*>(GetItem())->GetValue() );
+}
+
+
+
+EditCharAttribUnderline::EditCharAttribUnderline(SfxItemPool& rPool, const SfxPoolItem& rItem, sal_Int32 _nStart, sal_Int32 _nEnd)
+: EditCharAttrib(rPool, rItem, _nStart, _nEnd)
+{
+ assert(rItem.Which() == EE_CHAR_UNDERLINE);
+}
+
+void EditCharAttribUnderline::SetFont( SvxFont& rFont, OutputDevice* pOutDev )
+{
+ rFont.SetUnderline( static_cast<const SvxUnderlineItem*>(GetItem())->GetValue() );
+
+ if ( pOutDev )
+ pOutDev->SetTextLineColor( static_cast<const SvxUnderlineItem*>(GetItem())->GetColor() );
+
+}
+
+
+
+EditCharAttribOverline::EditCharAttribOverline(SfxItemPool& rPool, const SfxPoolItem& rItem, sal_Int32 _nStart, sal_Int32 _nEnd)
+: EditCharAttrib(rPool, rItem, _nStart, _nEnd)
+{
+ assert(rItem.Which() == EE_CHAR_OVERLINE);
+}
+
+void EditCharAttribOverline::SetFont( SvxFont& rFont, OutputDevice* pOutDev )
+{
+ rFont.SetOverline( static_cast<const SvxOverlineItem*>(GetItem())->GetValue() );
+ if ( pOutDev )
+ pOutDev->SetOverlineColor( static_cast<const SvxOverlineItem*>(GetItem())->GetColor() );
+}
+
+
+
+EditCharAttribFontHeight::EditCharAttribFontHeight(SfxItemPool& rPool, const SfxPoolItem& rItem, sal_Int32 _nStart, sal_Int32 _nEnd)
+: EditCharAttrib(rPool, rItem, _nStart, _nEnd)
+{
+ assert(rItem.Which() == EE_CHAR_FONTHEIGHT || rItem.Which() == EE_CHAR_FONTHEIGHT_CJK || rItem.Which() == EE_CHAR_FONTHEIGHT_CTL);
+}
+
+void EditCharAttribFontHeight::SetFont( SvxFont& rFont, OutputDevice* )
+{
+ // Property is ignored
+ rFont.SetFontSize( Size( rFont.GetFontSize().Width(), static_cast<const SvxFontHeightItem*>(GetItem())->GetHeight() ) );
+}
+
+
+
+EditCharAttribFontWidth::EditCharAttribFontWidth(SfxItemPool& rPool, const SfxPoolItem& rItem, sal_Int32 _nStart, sal_Int32 _nEnd)
+: EditCharAttrib(rPool, rItem, _nStart, _nEnd)
+{
+ assert(rItem.Which() == EE_CHAR_FONTWIDTH);
+}
+
+void EditCharAttribFontWidth::SetFont( SvxFont& /*rFont*/, OutputDevice* )
+{
+ // must be calculated outside, because f(device)...
+}
+
+
+
+EditCharAttribStrikeout::EditCharAttribStrikeout(SfxItemPool& rPool, const SfxPoolItem& rItem, sal_Int32 _nStart, sal_Int32 _nEnd)
+: EditCharAttrib(rPool, rItem, _nStart, _nEnd)
+{
+ assert(rItem.Which() == EE_CHAR_STRIKEOUT);
+}
+
+void EditCharAttribStrikeout::SetFont( SvxFont& rFont, OutputDevice* )
+{
+ rFont.SetStrikeout( static_cast<const SvxCrossedOutItem*>(GetItem())->GetValue() );
+}
+
+
+
+EditCharAttribCaseMap::EditCharAttribCaseMap(SfxItemPool& rPool, const SfxPoolItem& rItem, sal_Int32 _nStart, sal_Int32 _nEnd)
+: EditCharAttrib(rPool, rItem, _nStart, _nEnd)
+{
+ assert(rItem.Which() == EE_CHAR_CASEMAP);
+}
+
+void EditCharAttribCaseMap::SetFont( SvxFont& rFont, OutputDevice* )
+{
+ rFont.SetCaseMap( static_cast<const SvxCaseMapItem*>(GetItem())->GetCaseMap() );
+}
+
+
+
+EditCharAttribColor::EditCharAttribColor(SfxItemPool& rPool, const SfxPoolItem& rItem, sal_Int32 _nStart, sal_Int32 _nEnd)
+: EditCharAttrib(rPool, rItem, _nStart, _nEnd)
+{
+ assert(rItem.Which() == EE_CHAR_COLOR);
+}
+
+void EditCharAttribColor::SetFont( SvxFont& rFont, OutputDevice* )
+{
+ Color aColor = static_cast<const SvxColorItem*>(GetItem())->GetValue();
+ rFont.SetColor( aColor);
+}
+
+
+EditCharAttribBackgroundColor::EditCharAttribBackgroundColor(SfxItemPool& rPool, const SfxPoolItem& rItem, sal_Int32 _nStart, sal_Int32 _nEnd)
+: EditCharAttrib(rPool, rItem, _nStart, _nEnd)
+{
+ assert(rItem.Which() == EE_CHAR_BKGCOLOR);
+}
+
+void EditCharAttribBackgroundColor::SetFont( SvxFont& rFont, OutputDevice* )
+{
+ Color aColor = static_cast<const SvxColorItem*>(GetItem())->GetValue();
+ rFont.SetTransparent(aColor.IsTransparent());
+ rFont.SetFillColor(aColor);
+}
+
+EditCharAttribLanguage::EditCharAttribLanguage(SfxItemPool& rPool, const SfxPoolItem& rItem, sal_Int32 _nStart, sal_Int32 _nEnd)
+: EditCharAttrib(rPool, rItem, _nStart, _nEnd)
+{
+ assert((rItem.Which() == EE_CHAR_LANGUAGE) || (rItem.Which() == EE_CHAR_LANGUAGE_CJK) || (rItem.Which() == EE_CHAR_LANGUAGE_CTL));
+}
+
+void EditCharAttribLanguage::SetFont( SvxFont& rFont, OutputDevice* )
+{
+ rFont.SetLanguage( static_cast<const SvxLanguageItem*>(GetItem())->GetLanguage() );
+}
+
+
+
+EditCharAttribShadow::EditCharAttribShadow(SfxItemPool& rPool, const SfxPoolItem& rItem, sal_Int32 _nStart, sal_Int32 _nEnd)
+: EditCharAttrib(rPool, rItem, _nStart, _nEnd)
+{
+ assert(rItem.Which() == EE_CHAR_SHADOW);
+}
+
+void EditCharAttribShadow::SetFont( SvxFont& rFont, OutputDevice* )
+{
+ rFont.SetShadow( static_cast<const SvxShadowedItem*>(GetItem())->GetValue() );
+}
+
+
+
+EditCharAttribEscapement::EditCharAttribEscapement(SfxItemPool& rPool, const SfxPoolItem& rItem, sal_Int32 _nStart, sal_Int32 _nEnd)
+: EditCharAttrib(rPool, rItem, _nStart, _nEnd)
+{
+ assert(rItem.Which() == EE_CHAR_ESCAPEMENT);
+}
+
+void EditCharAttribEscapement::SetFont( SvxFont& rFont, OutputDevice* pOutDev )
+{
+ sal_uInt16 const nProp = static_cast<const SvxEscapementItem*>(GetItem())->GetProportionalHeight();
+ rFont.SetPropr( static_cast<sal_uInt8>(nProp) );
+
+ short nEsc = static_cast<const SvxEscapementItem*>(GetItem())->GetEsc();
+ rFont.SetNonAutoEscapement( nEsc, pOutDev );
+}
+
+
+
+EditCharAttribOutline::EditCharAttribOutline(SfxItemPool& rPool, const SfxPoolItem& rItem, sal_Int32 _nStart, sal_Int32 _nEnd)
+: EditCharAttrib(rPool, rItem, _nStart, _nEnd)
+{
+ assert(rItem.Which() == EE_CHAR_OUTLINE);
+}
+
+void EditCharAttribOutline::SetFont( SvxFont& rFont, OutputDevice* )
+{
+ rFont.SetOutline( static_cast<const SvxContourItem*>(GetItem())->GetValue() );
+}
+
+
+
+EditCharAttribTab::EditCharAttribTab(SfxItemPool& rPool, const SfxPoolItem& rItem, sal_Int32 nPos)
+: EditCharAttrib(rPool, rItem, nPos, nPos+1)
+{
+ SetFeature( true );
+}
+
+void EditCharAttribTab::SetFont( SvxFont&, OutputDevice* )
+{
+}
+
+
+
+EditCharAttribLineBreak::EditCharAttribLineBreak(SfxItemPool& rPool, const SfxPoolItem& rItem, sal_Int32 nPos)
+: EditCharAttrib(rPool, rItem, nPos, nPos+1)
+{
+ SetFeature( true );
+}
+
+void EditCharAttribLineBreak::SetFont( SvxFont&, OutputDevice* )
+{
+}
+
+
+
+EditCharAttribField::EditCharAttribField(SfxItemPool& rPool, const SfxPoolItem& rItem, sal_Int32 nPos)
+: EditCharAttrib(rPool, rItem, nPos, nPos+1)
+{
+ SetFeature( true ); // !!!
+}
+
+void EditCharAttribField::SetFont( SvxFont& rFont, OutputDevice* )
+{
+ if ( mxFldColor )
+ {
+ rFont.SetFillColor( *mxFldColor );
+ rFont.SetTransparent( false );
+ }
+ if ( mxTxtColor )
+ rFont.SetColor( *mxTxtColor );
+ if ( mxFldLineStyle )
+ rFont.SetUnderline( *mxFldLineStyle );
+}
+
+
+void EditCharAttribField::SetFieldValue(const OUString& rVal)
+{
+ aFieldValue = rVal;
+}
+
+void EditCharAttribField::Reset()
+{
+ aFieldValue.clear();
+ mxTxtColor.reset();
+ mxFldColor.reset();
+ mxFldLineStyle.reset();
+}
+
+EditCharAttribField::EditCharAttribField(const EditCharAttribField& rAttr)
+: EditCharAttrib(rAttr.GetHolder().getPool(), *rAttr.GetHolder().getItem(), rAttr.GetStart(), rAttr.GetEnd())
+, aFieldValue( rAttr.aFieldValue )
+{
+ // Use this constructor only for temporary Objects, Item is not pooled.
+ mxTxtColor = rAttr.mxTxtColor;
+ mxFldColor = rAttr.mxFldColor;
+ mxFldLineStyle = rAttr.mxFldLineStyle;
+}
+
+EditCharAttribField::~EditCharAttribField()
+{
+ Reset();
+}
+
+bool EditCharAttribField::operator == ( const EditCharAttribField& rAttr ) const
+{
+ if ( aFieldValue != rAttr.aFieldValue )
+ return false;
+
+ if ( ( mxTxtColor && !rAttr.mxTxtColor ) || ( !mxTxtColor && rAttr.mxTxtColor ) )
+ return false;
+ if ( ( mxTxtColor && rAttr.mxTxtColor ) && ( *mxTxtColor != *rAttr.mxTxtColor ) )
+ return false;
+
+ if ( ( mxFldColor && !rAttr.mxFldColor ) || ( !mxFldColor && rAttr.mxFldColor ) )
+ return false;
+ if ( ( mxFldColor && rAttr.mxFldColor ) && ( *mxFldColor != *rAttr.mxFldColor ) )
+ return false;
+
+ if ( ( mxFldLineStyle && !rAttr.mxFldLineStyle ) || ( !mxFldLineStyle && rAttr.mxFldLineStyle ) )
+ return false;
+ if ( ( mxFldLineStyle && rAttr.mxFldLineStyle ) && ( *mxFldLineStyle != *rAttr.mxFldLineStyle ) )
+ return false;
+
+ return true;
+}
+
+
+
+EditCharAttribPairKerning::EditCharAttribPairKerning(SfxItemPool& rPool, const SfxPoolItem& rItem, sal_Int32 _nStart, sal_Int32 _nEnd)
+: EditCharAttrib(rPool, rItem, _nStart, _nEnd)
+{
+ assert(rItem.Which() == EE_CHAR_PAIRKERNING);
+}
+
+void EditCharAttribPairKerning::SetFont( SvxFont& rFont, OutputDevice* )
+{
+ rFont.SetKerning( static_cast<const SvxAutoKernItem*>(GetItem())->GetValue() ? FontKerning::FontSpecific : FontKerning::NONE );
+}
+
+
+
+EditCharAttribKerning::EditCharAttribKerning(SfxItemPool& rPool, const SfxPoolItem& rItem, sal_Int32 _nStart, sal_Int32 _nEnd)
+: EditCharAttrib(rPool, rItem, _nStart, _nEnd)
+{
+ assert(rItem.Which() == EE_CHAR_KERNING);
+}
+
+void EditCharAttribKerning::SetFont( SvxFont& rFont, OutputDevice* )
+{
+ rFont.SetFixKerning( static_cast<const SvxKerningItem*>(GetItem())->GetValue() );
+}
+
+
+
+EditCharAttribWordLineMode::EditCharAttribWordLineMode(SfxItemPool& rPool, const SfxPoolItem& rItem, sal_Int32 _nStart, sal_Int32 _nEnd)
+: EditCharAttrib(rPool, rItem, _nStart, _nEnd)
+{
+ assert(rItem.Which() == EE_CHAR_WLM);
+}
+
+void EditCharAttribWordLineMode::SetFont( SvxFont& rFont, OutputDevice* )
+{
+ rFont.SetWordLineMode( static_cast<const SvxWordLineModeItem*>(GetItem())->GetValue() );
+}
+
+
+
+EditCharAttribEmphasisMark::EditCharAttribEmphasisMark(SfxItemPool& rPool, const SfxPoolItem& rItem, sal_Int32 _nStart, sal_Int32 _nEnd)
+: EditCharAttrib(rPool, rItem, _nStart, _nEnd)
+{
+ assert(rItem.Which() == EE_CHAR_EMPHASISMARK);
+}
+
+void EditCharAttribEmphasisMark::SetFont( SvxFont& rFont, OutputDevice* )
+{
+ rFont.SetEmphasisMark( static_cast<const SvxEmphasisMarkItem*>(GetItem())->GetEmphasisMark() );
+}
+
+
+
+EditCharAttribRelief::EditCharAttribRelief(SfxItemPool& rPool, const SfxPoolItem& rItem, sal_Int32 _nStart, sal_Int32 _nEnd)
+: EditCharAttrib(rPool, rItem, _nStart, _nEnd)
+{
+ assert(rItem.Which() == EE_CHAR_RELIEF);
+}
+
+void EditCharAttribRelief::SetFont( SvxFont& rFont, OutputDevice* )
+{
+ rFont.SetRelief( static_cast<const SvxCharReliefItem*>(GetItem())->GetValue() );
+}
+
+
+EditCharAttribGrabBag::EditCharAttribGrabBag(SfxItemPool& rPool, const SfxPoolItem& rItem, sal_Int32 _nStart, sal_Int32 _nEnd)
+: EditCharAttrib(rPool, rItem, _nStart, _nEnd)
+{
+ assert(rItem.Which() == EE_CHAR_GRABBAG);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/editdata.cxx b/editeng/source/editeng/editdata.cxx
new file mode 100644
index 0000000000..f272a9afd5
--- /dev/null
+++ b/editeng/source/editeng/editdata.cxx
@@ -0,0 +1,16 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <editeng/editdata.hxx>
+
+#include <limits>
+
+const size_t EE_APPEND = std::numeric_limits<size_t>::max();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/editdbg.cxx b/editeng/source/editeng/editdbg.cxx
new file mode 100644
index 0000000000..31ec9d0930
--- /dev/null
+++ b/editeng/source/editeng/editdbg.cxx
@@ -0,0 +1,531 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <memory>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/window.hxx>
+
+#include <editeng/lspcitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/kernitem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <editeng/autokernitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/numitem.hxx>
+#include <editeng/tstpitem.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/frmdiritem.hxx>
+
+#include "impedit.hxx"
+#include <editeng/editeng.hxx>
+#include <editeng/editview.hxx>
+#include <editdoc.hxx>
+
+#include <rtl/strbuf.hxx>
+#include <osl/diagnose.h>
+
+#if defined( DBG_UTIL ) || ( OSL_DEBUG_LEVEL > 1 )
+
+namespace
+{
+struct DebOutBuffer
+{
+ OStringBuffer str;
+ void append(std::string_view descr, const SfxEnumItemInterface& rItem)
+ {
+ str.append(descr + OString::number(rItem.GetEnumValue()));
+ }
+ void append(std::string_view descr, const SvxLRSpaceItem& rItem)
+ {
+ str.append(OString::Concat(descr) + "FI=" + OString::number(rItem.GetTextFirstLineOffset())
+ + ", LI=" + OString::number(rItem.GetTextLeft())
+ + ", RI=" + OString::number(rItem.GetRight()));
+ }
+ void append(std::string_view descr, const SvxNumBulletItem& rItem)
+ {
+ str.append(descr);
+ for (sal_uInt16 nLevel = 0; nLevel < 3; nLevel++)
+ {
+ str.append("Level" + OString::number(nLevel) + "=");
+ const SvxNumberFormat* pFmt = rItem.GetNumRule().Get(nLevel);
+ if (pFmt)
+ {
+ str.append("(" + OString::number(pFmt->GetFirstLineOffset()) + ","
+ + OString::number(pFmt->GetAbsLSpace()) + ",");
+ if (pFmt->GetNumberingType() == SVX_NUM_BITMAP)
+ str.append("Bitmap");
+ else if (pFmt->GetNumberingType() != SVX_NUM_CHAR_SPECIAL)
+ str.append("Number");
+ else
+ {
+ str.append("Char=[" + OString::number(pFmt->GetBulletChar()) + "]");
+ }
+ str.append(") ");
+ }
+ }
+ }
+ void append(std::string_view descr, const SfxBoolItem& rItem)
+ {
+ str.append(descr + OString::number(static_cast<int>(rItem.GetValue())));
+ }
+ void append(std::string_view descr, const SfxInt16Item& rItem)
+ {
+ str.append(descr + OString::number(rItem.GetValue()));
+ }
+ void append(std::string_view descr, const SfxUInt16Item& rItem)
+ {
+ str.append(descr + OString::number(rItem.GetValue()));
+ }
+ void append(const SvxULSpaceItem& rItem)
+ {
+ str.append("SB=" + OString::number(rItem.GetUpper())
+ + ", SA=" + OString::number(rItem.GetLower()));
+ }
+ void append(std::string_view descr, const SvxLineSpacingItem& rItem)
+ {
+ str.append(descr);
+ if (rItem.GetLineSpaceRule() == SvxLineSpaceRule::Min)
+ {
+ str.append("Min: " + OString::number(rItem.GetInterLineSpace()));
+ }
+ else if (rItem.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Prop)
+ {
+ str.append("Prop: " + OString::number(rItem.GetPropLineSpace()));
+ }
+ else
+ str.append("Unsupported Type!");
+ }
+ void append(const SvxTabStopItem& rTabs)
+ {
+ str.append("Tabs: " + OString::number(rTabs.Count()));
+ if (rTabs.Count())
+ {
+ str.append("( ");
+ for (sal_uInt16 i = 0; i < rTabs.Count(); ++i)
+ {
+ const SvxTabStop& rTab = rTabs[i];
+ str.append(OString::number(rTab.GetTabPos()) + " ");
+ }
+ str.append(')');
+ }
+ }
+ void append(std::string_view descr, const SvxColorItem& rItem)
+ {
+ Color aColor(rItem.GetValue());
+ str.append(descr + OString::number(aColor.GetRed()) + ", "
+ + OString::number(aColor.GetGreen()) + ", " + OString::number(aColor.GetBlue()));
+ }
+ void append(std::string_view descr, const SvxFontItem& rItem)
+ {
+ str.append(descr + OUStringToOString(rItem.GetFamilyName(), RTL_TEXTENCODING_ASCII_US)
+ + " (CharSet: " + OString::number(rItem.GetCharSet()) + ")");
+ }
+ void append(std::string_view descr, const SvxEscapementItem& rItem)
+ {
+ str.append(descr + OString::number(rItem.GetEsc()) + ", "
+ + OString::number(rItem.GetProportionalHeight()));
+ }
+ void appendHeightAndPts(std::string_view descr, tools::Long h, MapUnit eUnit)
+ {
+ MapMode aItemMapMode(eUnit);
+ MapMode aPntMap(MapUnit::MapPoint);
+ Size aSz = OutputDevice::LogicToLogic(Size(0, h), aItemMapMode, aPntMap);
+ str.append(descr + OString::number(h) + " Points=" + OString::number(aSz.Height()));
+ }
+ void append(std::string_view descr, const SvxFontHeightItem& rItem, const SfxItemPool& rPool)
+ {
+ appendHeightAndPts(descr, rItem.GetHeight(), rPool.GetMetric(rItem.Which()));
+ }
+ void append(std::string_view descr, const SvxKerningItem& rItem, const SfxItemPool& rPool)
+ {
+ appendHeightAndPts(descr, rItem.GetValue(), rPool.GetMetric(rItem.Which()));
+ }
+};
+}
+
+static OString DbgOutItem(const SfxItemPool& rPool, const SfxPoolItem& rItem)
+{
+ DebOutBuffer buffer;
+ switch ( rItem.Which() )
+ {
+ case EE_PARA_WRITINGDIR:
+ buffer.append("WritingDir=", rItem.StaticWhichCast(EE_PARA_WRITINGDIR));
+ break;
+ case EE_PARA_OUTLLRSPACE:
+ buffer.append("Outline ", rItem.StaticWhichCast(EE_PARA_OUTLLRSPACE));
+ break;
+ case EE_PARA_LRSPACE:
+ buffer.append("", rItem.StaticWhichCast(EE_PARA_LRSPACE));
+ break;
+ case EE_PARA_NUMBULLET:
+ buffer.append("NumItem ", rItem.StaticWhichCast(EE_PARA_NUMBULLET));
+ break;
+ case EE_PARA_BULLETSTATE:
+ buffer.append("ShowBullet=", rItem.StaticWhichCast(EE_PARA_BULLETSTATE));
+ break;
+ case EE_PARA_HYPHENATE:
+ buffer.append("Hyphenate=", rItem.StaticWhichCast(EE_PARA_HYPHENATE));
+ break;
+ case EE_PARA_OUTLLEVEL:
+ buffer.append("Level=", rItem.StaticWhichCast(EE_PARA_OUTLLEVEL));
+ break;
+ case EE_PARA_ULSPACE:
+ buffer.append(rItem.StaticWhichCast(EE_PARA_ULSPACE));
+ break;
+ case EE_PARA_SBL:
+ buffer.append("SBL=", rItem.StaticWhichCast(EE_PARA_SBL));
+ break;
+ case EE_PARA_JUST:
+ buffer.append("SvxAdust=", rItem.StaticWhichCast(EE_PARA_JUST));
+ break;
+ case EE_PARA_TABS:
+ buffer.append(rItem.StaticWhichCast(EE_PARA_TABS));
+ break;
+ case EE_CHAR_LANGUAGE:
+ buffer.append("Language=", rItem.StaticWhichCast(EE_CHAR_LANGUAGE));
+ break;
+ case EE_CHAR_LANGUAGE_CJK:
+ buffer.append("LanguageCJK=", rItem.StaticWhichCast(EE_CHAR_LANGUAGE_CJK));
+ break;
+ case EE_CHAR_LANGUAGE_CTL:
+ buffer.append("LanguageCTL=", rItem.StaticWhichCast(EE_CHAR_LANGUAGE_CTL));
+ break;
+ case EE_CHAR_COLOR:
+ buffer.append("Color= ", rItem.StaticWhichCast(EE_CHAR_COLOR));
+ break;
+ case EE_CHAR_BKGCOLOR:
+ buffer.append("FillColor= ", rItem.StaticWhichCast(EE_CHAR_BKGCOLOR));
+ break;
+ case EE_CHAR_FONTINFO:
+ buffer.append("Font=", rItem.StaticWhichCast(EE_CHAR_FONTINFO));
+ break;
+ case EE_CHAR_FONTINFO_CJK:
+ buffer.append("FontCJK=", rItem.StaticWhichCast(EE_CHAR_FONTINFO_CJK));
+ break;
+ case EE_CHAR_FONTINFO_CTL:
+ buffer.append("FontCTL=", rItem.StaticWhichCast(EE_CHAR_FONTINFO_CTL));
+ break;
+ case EE_CHAR_FONTHEIGHT:
+ buffer.append("Size=", rItem.StaticWhichCast(EE_CHAR_FONTHEIGHT), rPool);
+ break;
+ case EE_CHAR_FONTHEIGHT_CJK:
+ buffer.append("SizeCJK=", rItem.StaticWhichCast(EE_CHAR_FONTHEIGHT_CJK), rPool);
+ break;
+ case EE_CHAR_FONTHEIGHT_CTL:
+ buffer.append("SizeCTL=", rItem.StaticWhichCast(EE_CHAR_FONTHEIGHT_CTL), rPool);
+ break;
+ case EE_CHAR_FONTWIDTH:
+ buffer.append("Width=", rItem.StaticWhichCast(EE_CHAR_FONTWIDTH));
+ break;
+ case EE_CHAR_WEIGHT:
+ buffer.append("FontWeight=", rItem.StaticWhichCast(EE_CHAR_WEIGHT));
+ break;
+ case EE_CHAR_WEIGHT_CJK:
+ buffer.append("FontWeightCJK=", rItem.StaticWhichCast(EE_CHAR_WEIGHT_CJK));
+ break;
+ case EE_CHAR_WEIGHT_CTL:
+ buffer.append("FontWeightCTL=", rItem.StaticWhichCast(EE_CHAR_WEIGHT_CTL));
+ break;
+ case EE_CHAR_UNDERLINE:
+ buffer.append("FontUnderline=", rItem.StaticWhichCast(EE_CHAR_UNDERLINE));
+ break;
+ case EE_CHAR_OVERLINE:
+ buffer.append("FontOverline=", rItem.StaticWhichCast(EE_CHAR_OVERLINE));
+ break;
+ case EE_CHAR_EMPHASISMARK:
+ buffer.append("FontEmphasisMark=", rItem.StaticWhichCast(EE_CHAR_EMPHASISMARK));
+ break;
+ case EE_CHAR_RELIEF:
+ buffer.append("FontRelief=", rItem.StaticWhichCast(EE_CHAR_RELIEF));
+ break;
+ case EE_CHAR_STRIKEOUT:
+ buffer.append("FontStrikeout=", rItem.StaticWhichCast(EE_CHAR_STRIKEOUT));
+ break;
+ case EE_CHAR_ITALIC:
+ buffer.append("FontPosture=", rItem.StaticWhichCast(EE_CHAR_ITALIC));
+ break;
+ case EE_CHAR_ITALIC_CJK:
+ buffer.append("FontPostureCJK=", rItem.StaticWhichCast(EE_CHAR_ITALIC_CJK));
+ break;
+ case EE_CHAR_ITALIC_CTL:
+ buffer.append("FontPostureCTL=", rItem.StaticWhichCast(EE_CHAR_ITALIC_CTL));
+ break;
+ case EE_CHAR_OUTLINE:
+ buffer.append("FontOutline=", rItem.StaticWhichCast(EE_CHAR_OUTLINE));
+ break;
+ case EE_CHAR_SHADOW:
+ buffer.append("FontShadowed=", rItem.StaticWhichCast(EE_CHAR_SHADOW));
+ break;
+ case EE_CHAR_ESCAPEMENT:
+ buffer.append("Escape=", rItem.StaticWhichCast(EE_CHAR_ESCAPEMENT));
+ break;
+ case EE_CHAR_PAIRKERNING:
+ buffer.append("PairKerning=", rItem.StaticWhichCast(EE_CHAR_PAIRKERNING));
+ break;
+ case EE_CHAR_KERNING:
+ buffer.append("Kerning=", rItem.StaticWhichCast(EE_CHAR_KERNING), rPool);
+ break;
+ case EE_CHAR_WLM:
+ buffer.append("WordLineMode=", rItem.StaticWhichCast(EE_CHAR_WLM));
+ break;
+ case EE_CHAR_XMLATTRIBS:
+ buffer.str.append("XMLAttribs=...");
+ break;
+ }
+ return buffer.str.makeStringAndClear();
+}
+
+static void DbgOutItemSet(FILE* fp, const SfxItemSet& rSet, bool bSearchInParent, bool bShowALL)
+{
+ for ( sal_uInt16 nWhich = EE_PARA_START; nWhich <= EE_CHAR_END; nWhich++ )
+ {
+ fprintf( fp, "\nWhich: %i\t", nWhich );
+ if ( rSet.GetItemState( nWhich, bSearchInParent ) == SfxItemState::DEFAULT )
+ fprintf( fp, "ITEM_OFF " );
+ else if ( rSet.GetItemState( nWhich, bSearchInParent ) == SfxItemState::DONTCARE )
+ fprintf( fp, "ITEM_DC " );
+ else if ( rSet.GetItemState( nWhich, bSearchInParent ) == SfxItemState::SET )
+ fprintf( fp, "ITEM_ON *" );
+
+ if ( !bShowALL && ( rSet.GetItemState( nWhich, bSearchInParent ) != SfxItemState::SET ) )
+ continue;
+
+ const SfxPoolItem& rItem = rSet.Get( nWhich, bSearchInParent );
+ OString aDebStr = DbgOutItem( *rSet.GetPool(), rItem );
+ fprintf( fp, "%s", aDebStr.getStr() );
+ }
+}
+
+void EditEngine::DumpData(const EditEngine* pEE, bool bInfoBox)
+{
+ if (!pEE)
+ return;
+
+ FILE* fp = fopen( "editenginedump.log", "w" );
+ if ( fp == nullptr )
+ {
+ OSL_FAIL( "Log file could not be created!" );
+ return;
+ }
+
+ const SfxItemPool& rPool = *pEE->GetEmptyItemSet().GetPool();
+
+ fprintf( fp, "================================================================================" );
+ fprintf( fp, "\n================== Document ================================================" );
+ fprintf( fp, "\n================================================================================" );
+ for ( sal_Int32 nPortion = 0; nPortion < pEE->pImpEditEngine->GetParaPortions().Count(); nPortion++)
+ {
+ ParaPortion* pPPortion = pEE->pImpEditEngine->GetParaPortions()[nPortion];
+ fprintf( fp, "\nParagraph %" SAL_PRIdINT32 ": Length = %" SAL_PRIdINT32 ", Invalid = %i\nText = '%s'",
+ nPortion, pPPortion->GetNode()->Len(), pPPortion->IsInvalid(),
+ OUStringToOString(pPPortion->GetNode()->GetString(), RTL_TEXTENCODING_UTF8).getStr() );
+ fprintf( fp, "\nVorlage:" );
+ SfxStyleSheet* pStyle = pPPortion->GetNode()->GetStyleSheet();
+ if ( pStyle )
+ fprintf( fp, " %s", OUStringToOString( pStyle->GetName(), RTL_TEXTENCODING_UTF8).getStr() );
+ fprintf( fp, "\nParagraph attribute:" );
+ DbgOutItemSet( fp, pPPortion->GetNode()->GetContentAttribs().GetItems(), false, false );
+
+ fprintf( fp, "\nCharacter attribute:" );
+ bool bZeroAttr = false;
+ for ( sal_Int32 z = 0; z < pPPortion->GetNode()->GetCharAttribs().Count(); ++z )
+ {
+ const std::unique_ptr<EditCharAttrib>& rAttr = pPPortion->GetNode()->GetCharAttribs().GetAttribs()[z];
+ OString aCharAttribs =
+ "\nA"
+ + OString::number(nPortion)
+ + ": "
+ + OString::number(rAttr->GetItem()->Which())
+ + "\t"
+ + OString::number(rAttr->GetStart())
+ + "\t"
+ + OString::number(rAttr->GetEnd());
+ if ( rAttr->IsEmpty() )
+ bZeroAttr = true;
+ fprintf(fp, "%s => ", aCharAttribs.getStr());
+
+ OString aDebStr = DbgOutItem( rPool, *rAttr->GetItem() );
+ fprintf( fp, "%s", aDebStr.getStr() );
+ }
+ if ( bZeroAttr )
+ fprintf( fp, "\nNULL-Attribute!" );
+
+ const sal_Int32 nTextPortions = pPPortion->GetTextPortions().Count();
+ OStringBuffer aPortionStr("\nText portions: #"
+ + OString::number(nTextPortions)
+ + " \nA"
+ + OString::number(nPortion)
+ + ": Paragraph Length = "
+ + OString::number(pPPortion->GetNode()->Len())
+ + "\nA"
+ + OString::number(nPortion)
+ + ": ");
+ sal_Int32 n = 0;
+ for ( sal_Int32 z = 0; z < nTextPortions; ++z )
+ {
+ TextPortion& rPortion = pPPortion->GetTextPortions()[z];
+ aPortionStr.append(" "
+ + OString::number(rPortion.GetLen())
+ + "("
+ + OString::number(rPortion.GetSize().Width())
+ + ")"
+ "["
+ + OString::number(static_cast<sal_Int32>(rPortion.GetKind()))
+ + "];");
+ n += rPortion.GetLen();
+ }
+ aPortionStr.append("\nA"
+ + OString::number(nPortion)
+ + ": Total length: "
+ + OString::number(n));
+ if ( pPPortion->GetNode()->Len() != n )
+ aPortionStr.append(" => Error !!!");
+ fprintf(fp, "%s", aPortionStr.getStr());
+
+ fprintf( fp, "\n\nLines:" );
+ // First the content ...
+ for ( sal_Int32 nLine = 0; nLine < pPPortion->GetLines().Count(); nLine++ )
+ {
+ EditLine& rLine = pPPortion->GetLines()[nLine];
+
+ OString aLine(OUStringToOString(pPPortion->GetNode()->Copy(rLine.GetStart(), rLine.GetEnd() - rLine.GetStart()), RTL_TEXTENCODING_ASCII_US));
+ fprintf( fp, "\nLine %" SAL_PRIdINT32 "\t>%s<", nLine, aLine.getStr() );
+ }
+ // then the internal data ...
+ for ( sal_Int32 nLine = 0; nLine < pPPortion->GetLines().Count(); nLine++ )
+ {
+ EditLine& rLine = pPPortion->GetLines()[nLine];
+ fprintf( fp, "\nLine %" SAL_PRIdINT32 ":\tStart: %" SAL_PRIdINT32 ",\tEnd: %" SAL_PRIdINT32, nLine, rLine.GetStart(), rLine.GetEnd() );
+ fprintf( fp, "\t\tPortions: %" SAL_PRIdINT32 " - %" SAL_PRIdINT32 ".\tHight: %i, Ascent=%i", rLine.GetStartPortion(), rLine.GetEndPortion(), rLine.GetHeight(), rLine.GetMaxAscent() );
+ }
+
+ fprintf( fp, "\n-----------------------------------------------------------------------------" );
+ }
+
+ if ( pEE->pImpEditEngine->GetStyleSheetPool() )
+ {
+ SfxStyleSheetIterator aIter( pEE->pImpEditEngine->GetStyleSheetPool(), SfxStyleFamily::All );
+ sal_uInt16 nStyles = aIter.Count();
+ fprintf( fp, "\n\n================================================================================" );
+ fprintf( fp, "\n================== Stylesheets =============================================" );
+ fprintf( fp, "\n================================================================================" );
+ fprintf( fp, "\n#Template: %" SAL_PRIuUINT32 "\n", sal_uInt32(nStyles) );
+ SfxStyleSheetBase* pStyle = aIter.First();
+ while ( pStyle )
+ {
+ fprintf( fp, "\nTemplate: %s", OUStringToOString( pStyle->GetName(), RTL_TEXTENCODING_ASCII_US ).getStr() );
+ fprintf( fp, "\nParent: %s", OUStringToOString( pStyle->GetParent(), RTL_TEXTENCODING_ASCII_US ).getStr() );
+ fprintf( fp, "\nFollow: %s", OUStringToOString( pStyle->GetFollow(), RTL_TEXTENCODING_ASCII_US ).getStr() );
+ DbgOutItemSet( fp, pStyle->GetItemSet(), false, false );
+ fprintf( fp, "\n----------------------------------" );
+
+ pStyle = aIter.Next();
+ }
+ }
+
+ fprintf( fp, "\n\n================================================================================" );
+ fprintf( fp, "\n================== Defaults ================================================" );
+ fprintf( fp, "\n================================================================================" );
+ DbgOutItemSet( fp, pEE->pImpEditEngine->GetEmptyItemSet(), true, true );
+
+ fprintf( fp, "\n\n================================================================================" );
+ fprintf( fp, "\n================== EditEngine & Views ======================================" );
+ fprintf( fp, "\n================================================================================" );
+ fprintf( fp, "\nControl: %x", unsigned( pEE->GetControlWord() ) );
+ fprintf( fp, "\nRefMapMode: %i", int( pEE->pImpEditEngine->pRefDev->GetMapMode().GetMapUnit() ) );
+ fprintf( fp, "\nPaperSize: %" SAL_PRIdINT64 " x %" SAL_PRIdINT64, sal_Int64(pEE->GetPaperSize().Width()), sal_Int64(pEE->GetPaperSize().Height()) );
+ fprintf( fp, "\nMaxAutoPaperSize: %" SAL_PRIdINT64 " x %" SAL_PRIdINT64, sal_Int64(pEE->GetMaxAutoPaperSize().Width()), sal_Int64(pEE->GetMaxAutoPaperSize().Height()) );
+ fprintf( fp, "\nMinAutoPaperSize: %" SAL_PRIdINT64 " x %" SAL_PRIdINT64 , sal_Int64(pEE->GetMinAutoPaperSize().Width()), sal_Int64(pEE->GetMinAutoPaperSize().Height()) );
+ fprintf( fp, "\nCalculateLayout: %i", pEE->IsUpdateLayout() );
+ fprintf( fp, "\nNumber of Views: %" SAL_PRI_SIZET "i", pEE->GetViewCount() );
+ for ( size_t nView = 0; nView < pEE->GetViewCount(); nView++ )
+ {
+ EditView* pV = pEE->GetView( nView );
+ DBG_ASSERT( pV, "View not found!" );
+ fprintf( fp, "\nView %zu: Focus=%i", nView, pV->GetWindow()->HasFocus() );
+ tools::Rectangle aR( pV->GetOutputArea() );
+ fprintf( fp, "\n OutputArea: nX=%" SAL_PRIdINT64 ", nY=%" SAL_PRIdINT64 ", dX=%" SAL_PRIdINT64 ", dY=%" SAL_PRIdINT64 ", MapMode = %i",
+ sal_Int64(aR.Left()), sal_Int64(aR.Top()), sal_Int64(aR.GetSize().Width()), sal_Int64(aR.GetSize().Height()) , int( pV->GetWindow()->GetMapMode().GetMapUnit() ) );
+ aR = pV->GetVisArea();
+ fprintf( fp, "\n VisArea: nX=%" SAL_PRIdINT64 ", nY=%" SAL_PRIdINT64 ", dX=%" SAL_PRIdINT64 ", dY=%" SAL_PRIdINT64,
+ sal_Int64(aR.Left()), sal_Int64(aR.Top()), sal_Int64(aR.GetSize().Width()), sal_Int64(aR.GetSize().Height()) );
+ ESelection aSel = pV->GetSelection();
+ fprintf( fp, "\n Selection: Start=%" SAL_PRIdINT32 ",%" SAL_PRIdINT32 ", End=%" SAL_PRIdINT32 ",%" SAL_PRIdINT32, aSel.nStartPara, aSel.nStartPos, aSel.nEndPara, aSel.nEndPos );
+ }
+ if ( pEE->GetActiveView() )
+ {
+ fprintf( fp, "\n\n================================================================================" );
+ fprintf( fp, "\n================== Current View ===========================================" );
+ fprintf( fp, "\n================================================================================" );
+ DbgOutItemSet( fp, pEE->GetActiveView()->GetAttribs(), true, false );
+ }
+ fclose( fp );
+ if ( bInfoBox )
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Info, VclButtonsType::Ok,
+ "Dumped editenginedump.log!" ));
+ xInfoBox->run();
+ }
+}
+#endif
+
+#if OSL_DEBUG_LEVEL > 0
+bool ParaPortion::DbgCheckTextPortions(ParaPortion const& rPara)
+{
+ // check, if Portion length ok:
+ sal_uInt16 nXLen = 0;
+ for (sal_Int32 nPortion = 0; nPortion < rPara.aTextPortionList.Count(); nPortion++)
+ {
+ nXLen = nXLen + rPara.aTextPortionList[nPortion].GetLen();
+ }
+ return nXLen == rPara.pNode->Len();
+}
+#endif
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+void CheckOrderedList(const CharAttribList::AttribsType& rAttribs)
+{
+ sal_Int32 nPrev = 0;
+ for (const std::unique_ptr<EditCharAttrib>& rAttr : rAttribs)
+ {
+ sal_Int32 const nCur = rAttr->GetStart();
+ assert(nCur >= nPrev);
+ nPrev = nCur;
+ }
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/editdoc.cxx b/editeng/source/editeng/editdoc.cxx
new file mode 100644
index 0000000000..d892bd1c3a
--- /dev/null
+++ b/editeng/source/editeng/editdoc.cxx
@@ -0,0 +1,3006 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <editeng/tstpitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/kernitem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/cmapitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/autokernitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/editids.hrc>
+#include <editeng/editdata.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/lspcitem.hxx>
+
+#include <editdoc.hxx>
+#include <editeng/eerdll.hxx>
+#include <eerdll2.hxx>
+#include "impedit.hxx"
+
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+
+#include <svl/grabbagitem.hxx>
+#include <svl/voiditem.hxx>
+#include <tools/debug.hxx>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <libxml/xmlwriter.h>
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <limits>
+#include <memory>
+#include <set>
+#include <string_view>
+#include <utility>
+
+using namespace ::com::sun::star;
+
+
+sal_uInt16 GetScriptItemId( sal_uInt16 nItemId, SvtScriptType nScriptType )
+{
+ sal_uInt16 nId = nItemId;
+
+ if ( ( nScriptType == SvtScriptType::ASIAN ) ||
+ ( nScriptType == SvtScriptType::COMPLEX ) )
+ {
+ switch ( nItemId )
+ {
+ case EE_CHAR_LANGUAGE:
+ nId = ( nScriptType == SvtScriptType::ASIAN ) ? EE_CHAR_LANGUAGE_CJK : EE_CHAR_LANGUAGE_CTL;
+ break;
+ case EE_CHAR_FONTINFO:
+ nId = ( nScriptType == SvtScriptType::ASIAN ) ? EE_CHAR_FONTINFO_CJK : EE_CHAR_FONTINFO_CTL;
+ break;
+ case EE_CHAR_FONTHEIGHT:
+ nId = ( nScriptType == SvtScriptType::ASIAN ) ? EE_CHAR_FONTHEIGHT_CJK : EE_CHAR_FONTHEIGHT_CTL;
+ break;
+ case EE_CHAR_WEIGHT:
+ nId = ( nScriptType == SvtScriptType::ASIAN ) ? EE_CHAR_WEIGHT_CJK : EE_CHAR_WEIGHT_CTL;
+ break;
+ case EE_CHAR_ITALIC:
+ nId = ( nScriptType == SvtScriptType::ASIAN ) ? EE_CHAR_ITALIC_CJK : EE_CHAR_ITALIC_CTL;
+ break;
+ }
+ }
+
+ return nId;
+}
+
+bool IsScriptItemValid( sal_uInt16 nItemId, short nScriptType )
+{
+ bool bValid = true;
+
+ switch ( nItemId )
+ {
+ case EE_CHAR_LANGUAGE:
+ bValid = nScriptType == i18n::ScriptType::LATIN;
+ break;
+ case EE_CHAR_LANGUAGE_CJK:
+ bValid = nScriptType == i18n::ScriptType::ASIAN;
+ break;
+ case EE_CHAR_LANGUAGE_CTL:
+ bValid = nScriptType == i18n::ScriptType::COMPLEX;
+ break;
+ case EE_CHAR_FONTINFO:
+ bValid = nScriptType == i18n::ScriptType::LATIN;
+ break;
+ case EE_CHAR_FONTINFO_CJK:
+ bValid = nScriptType == i18n::ScriptType::ASIAN;
+ break;
+ case EE_CHAR_FONTINFO_CTL:
+ bValid = nScriptType == i18n::ScriptType::COMPLEX;
+ break;
+ case EE_CHAR_FONTHEIGHT:
+ bValid = nScriptType == i18n::ScriptType::LATIN;
+ break;
+ case EE_CHAR_FONTHEIGHT_CJK:
+ bValid = nScriptType == i18n::ScriptType::ASIAN;
+ break;
+ case EE_CHAR_FONTHEIGHT_CTL:
+ bValid = nScriptType == i18n::ScriptType::COMPLEX;
+ break;
+ case EE_CHAR_WEIGHT:
+ bValid = nScriptType == i18n::ScriptType::LATIN;
+ break;
+ case EE_CHAR_WEIGHT_CJK:
+ bValid = nScriptType == i18n::ScriptType::ASIAN;
+ break;
+ case EE_CHAR_WEIGHT_CTL:
+ bValid = nScriptType == i18n::ScriptType::COMPLEX;
+ break;
+ case EE_CHAR_ITALIC:
+ bValid = nScriptType == i18n::ScriptType::LATIN;
+ break;
+ case EE_CHAR_ITALIC_CJK:
+ bValid = nScriptType == i18n::ScriptType::ASIAN;
+ break;
+ case EE_CHAR_ITALIC_CTL:
+ bValid = nScriptType == i18n::ScriptType::COMPLEX;
+ break;
+ }
+
+ return bValid;
+}
+
+const SfxItemInfo aItemInfos[EDITITEMCOUNT] =
+{
+ // _nSID, _bNeedsPoolRegistration, _bShareable
+ { SID_ATTR_FRAMEDIRECTION, false, true }, // EE_PARA_WRITINGDIR
+ { 0, true, true }, // EE_PARA_XMLATTRIBS
+ { SID_ATTR_PARA_HANGPUNCTUATION, false, true }, // EE_PARA_HANGINGPUNCTUATION
+ { SID_ATTR_PARA_FORBIDDEN_RULES, false, true }, // EE_PARA_FORBIDDENRULES
+ { SID_ATTR_PARA_SCRIPTSPACE, false, true }, // EE_PARA_ASIANCJKSPACING
+ { SID_ATTR_NUMBERING_RULE, false, true }, // EE_PARA_NUMBULL
+ { 0, false, true }, // EE_PARA_HYPHENATE
+ { 0, false, true }, // EE_PARA_HYPHENATE_NO_CAPS
+ { 0, false, true }, // EE_PARA_HYPHENATE_NO_LAST_WORD
+ { 0, false, true }, // EE_PARA_BULLETSTATE
+ { 0, false, true }, // EE_PARA_OUTLLRSPACE
+ { SID_ATTR_PARA_OUTLLEVEL, false, true }, // EE_PARA_OUTLLEVEL
+ { SID_ATTR_PARA_BULLET, false, true }, // EE_PARA_BULLET
+ { SID_ATTR_LRSPACE, false, true }, // EE_PARA_LRSPACE
+ { SID_ATTR_ULSPACE, false, true }, // EE_PARA_ULSPACE
+ { SID_ATTR_PARA_LINESPACE, false, true }, // EE_PARA_SBL
+ { SID_ATTR_PARA_ADJUST, false, true }, // EE_PARA_JUST
+ { SID_ATTR_TABSTOP, false, true }, // EE_PARA_TABS
+ { SID_ATTR_ALIGN_HOR_JUSTIFY_METHOD, false, true }, // EE_PARA_JUST_METHOD
+ { SID_ATTR_ALIGN_VER_JUSTIFY, false, true }, // EE_PARA_VER_JUST
+ { SID_ATTR_CHAR_COLOR, true, true }, // EE_CHAR_COLOR
+ { SID_ATTR_CHAR_FONT, true, true }, // EE_CHAR_FONTINFO
+ { SID_ATTR_CHAR_FONTHEIGHT, false, true }, // EE_CHAR_FONTHEIGHT
+ { SID_ATTR_CHAR_SCALEWIDTH, false, true }, // EE_CHAR_FONTWIDTH
+ { SID_ATTR_CHAR_WEIGHT, false, true }, // EE_CHAR_WEIGHT
+ { SID_ATTR_CHAR_UNDERLINE, false, true }, // EE_CHAR_UNDERLINE
+ { SID_ATTR_CHAR_STRIKEOUT, false, true }, // EE_CHAR_STRIKEOUT
+ { SID_ATTR_CHAR_POSTURE, false, true }, // EE_CHAR_ITALIC
+ { SID_ATTR_CHAR_CONTOUR, false, true }, // EE_CHAR_OUTLINE
+ { SID_ATTR_CHAR_SHADOWED, false, true }, // EE_CHAR_SHADOW
+ { SID_ATTR_CHAR_ESCAPEMENT, false, true }, // EE_CHAR_ESCAPEMENT
+ { SID_ATTR_CHAR_AUTOKERN, false, true }, // EE_CHAR_PAIRKERNING
+ { SID_ATTR_CHAR_KERNING, false, true }, // EE_CHAR_KERNING
+ { SID_ATTR_CHAR_WORDLINEMODE, false, true }, // EE_CHAR_WLM
+ { SID_ATTR_CHAR_LANGUAGE, false, true }, // EE_CHAR_LANGUAGE
+ { SID_ATTR_CHAR_CJK_LANGUAGE, false, true }, // EE_CHAR_LANGUAGE_CJK
+ { SID_ATTR_CHAR_CTL_LANGUAGE, false, true }, // EE_CHAR_LANGUAGE_CTL
+ { SID_ATTR_CHAR_CJK_FONT, true, true }, // EE_CHAR_FONTINFO_CJK
+ { SID_ATTR_CHAR_CTL_FONT, true, true }, // EE_CHAR_FONTINFO_CTL
+ { SID_ATTR_CHAR_CJK_FONTHEIGHT, false, true }, // EE_CHAR_FONTHEIGHT_CJK
+ { SID_ATTR_CHAR_CTL_FONTHEIGHT, false, true }, // EE_CHAR_FONTHEIGHT_CTL
+ { SID_ATTR_CHAR_CJK_WEIGHT, false, true }, // EE_CHAR_WEIGHT_CJK
+ { SID_ATTR_CHAR_CTL_WEIGHT, false, true }, // EE_CHAR_WEIGHT_CTL
+ { SID_ATTR_CHAR_CJK_POSTURE, false, true }, // EE_CHAR_ITALIC_CJK
+ { SID_ATTR_CHAR_CTL_POSTURE, false, true }, // EE_CHAR_ITALIC_CTL
+ { SID_ATTR_CHAR_EMPHASISMARK, false, true }, // EE_CHAR_EMPHASISMARK
+ { SID_ATTR_CHAR_RELIEF, false, true }, // EE_CHAR_RELIEF
+ { 0, false, true }, // EE_CHAR_RUBI_DUMMY
+ { 0, true, true }, // EE_CHAR_XMLATTRIBS
+ { SID_ATTR_CHAR_OVERLINE, false, true }, // EE_CHAR_OVERLINE
+ { SID_ATTR_CHAR_CASEMAP, false, true }, // EE_CHAR_CASEMAP
+ { SID_ATTR_CHAR_GRABBAG, false, true }, // EE_CHAR_GRABBAG
+ { SID_ATTR_CHAR_BACK_COLOR, false, true }, // EE_CHAR_BKGCOLOR
+ { 0, false, true }, // EE_FEATURE_TAB
+ { 0, false, true }, // EE_FEATURE_LINEBR
+ { SID_ATTR_CHAR_CHARSETCOLOR, false, true }, // EE_FEATURE_NOTCONV
+ { SID_FIELD, true, true }, // EE_FEATURE_FIELD
+};
+
+EditCharAttrib* MakeCharAttrib( SfxItemPool& rPool, const SfxPoolItem& rAttr, sal_Int32 nS, sal_Int32 nE )
+{
+ // Create a new attribute in the pool
+ switch( rAttr.Which() )
+ {
+ case EE_CHAR_LANGUAGE:
+ case EE_CHAR_LANGUAGE_CJK:
+ case EE_CHAR_LANGUAGE_CTL:
+ {
+ return new EditCharAttribLanguage(rPool, rAttr, nS, nE);
+ }
+ break;
+ case EE_CHAR_COLOR:
+ {
+ return new EditCharAttribColor(rPool, rAttr, nS, nE );
+ }
+ break;
+ case EE_CHAR_FONTINFO:
+ case EE_CHAR_FONTINFO_CJK:
+ case EE_CHAR_FONTINFO_CTL:
+ {
+ return new EditCharAttribFont(rPool, rAttr, nS, nE );
+ }
+ break;
+ case EE_CHAR_FONTHEIGHT:
+ case EE_CHAR_FONTHEIGHT_CJK:
+ case EE_CHAR_FONTHEIGHT_CTL:
+ {
+ return new EditCharAttribFontHeight(rPool, rAttr, nS, nE );
+ }
+ break;
+ case EE_CHAR_FONTWIDTH:
+ {
+ return new EditCharAttribFontWidth(rPool, rAttr, nS, nE );
+ }
+ break;
+ case EE_CHAR_WEIGHT:
+ case EE_CHAR_WEIGHT_CJK:
+ case EE_CHAR_WEIGHT_CTL:
+ {
+ return new EditCharAttribWeight(rPool, rAttr, nS, nE );
+ }
+ break;
+ case EE_CHAR_UNDERLINE:
+ {
+ return new EditCharAttribUnderline(rPool, rAttr, nS, nE );
+ }
+ break;
+ case EE_CHAR_OVERLINE:
+ {
+ return new EditCharAttribOverline(rPool, rAttr, nS, nE );
+ }
+ break;
+ case EE_CHAR_EMPHASISMARK:
+ {
+ return new EditCharAttribEmphasisMark(rPool, rAttr, nS, nE );
+ }
+ break;
+ case EE_CHAR_RELIEF:
+ {
+ return new EditCharAttribRelief(rPool, rAttr, nS, nE );
+ }
+ break;
+ case EE_CHAR_STRIKEOUT:
+ {
+ return new EditCharAttribStrikeout(rPool, rAttr, nS, nE );
+ }
+ break;
+ case EE_CHAR_ITALIC:
+ case EE_CHAR_ITALIC_CJK:
+ case EE_CHAR_ITALIC_CTL:
+ {
+ return new EditCharAttribItalic(rPool, rAttr, nS, nE );
+ }
+ break;
+ case EE_CHAR_OUTLINE:
+ {
+ return new EditCharAttribOutline(rPool, rAttr, nS, nE );
+ }
+ break;
+ case EE_CHAR_SHADOW:
+ {
+ return new EditCharAttribShadow(rPool, rAttr, nS, nE );
+ }
+ break;
+ case EE_CHAR_ESCAPEMENT:
+ {
+ return new EditCharAttribEscapement(rPool, rAttr, nS, nE );
+ }
+ break;
+ case EE_CHAR_PAIRKERNING:
+ {
+ return new EditCharAttribPairKerning(rPool, rAttr, nS, nE );
+ }
+ break;
+ case EE_CHAR_KERNING:
+ {
+ return new EditCharAttribKerning(rPool, rAttr, nS, nE );
+ }
+ break;
+ case EE_CHAR_WLM:
+ {
+ return new EditCharAttribWordLineMode(rPool, rAttr, nS, nE );
+ }
+ break;
+ case EE_CHAR_XMLATTRIBS:
+ {
+ return new EditCharAttrib(rPool, rAttr, nS, nE); // Attribute is only for holding XML information...
+ }
+ break;
+ case EE_CHAR_CASEMAP:
+ {
+ return new EditCharAttribCaseMap(rPool, rAttr, nS, nE );
+ }
+ break;
+ case EE_CHAR_GRABBAG:
+ {
+ return new EditCharAttribGrabBag(rPool, rAttr, nS, nE );
+ }
+ break;
+ case EE_FEATURE_TAB:
+ {
+ return new EditCharAttribTab(rPool, rAttr, nS );
+ }
+ break;
+ case EE_FEATURE_LINEBR:
+ {
+ return new EditCharAttribLineBreak(rPool, rAttr, nS );
+ }
+ break;
+ case EE_FEATURE_FIELD:
+ {
+ return new EditCharAttribField(rPool, rAttr, nS );
+ }
+ break;
+ case EE_CHAR_BKGCOLOR:
+ {
+ return new EditCharAttribBackgroundColor(rPool, rAttr, nS, nE );
+ }
+ break;
+ default:
+ break;
+ }
+
+ OSL_FAIL( "Invalid Attribute!" );
+ return nullptr;
+}
+
+TextPortionList::TextPortionList()
+{
+}
+
+TextPortionList::~TextPortionList()
+{
+ Reset();
+}
+
+void TextPortionList::Reset()
+{
+ maPortions.clear();
+}
+
+void TextPortionList::DeleteFromPortion(sal_Int32 nDelFrom)
+{
+ assert((nDelFrom < static_cast<sal_Int32>(maPortions.size())) || ((nDelFrom == 0) && maPortions.empty()));
+ PortionsType::iterator it = maPortions.begin();
+ std::advance(it, nDelFrom);
+ maPortions.erase(it, maPortions.end());
+}
+
+sal_Int32 TextPortionList::Count() const
+{
+ return static_cast<sal_Int32>(maPortions.size());
+}
+
+const TextPortion& TextPortionList::operator[](sal_Int32 nPos) const
+{
+ return *maPortions[nPos];
+}
+
+TextPortion& TextPortionList::operator[](sal_Int32 nPos)
+{
+ return *maPortions[nPos];
+}
+
+void TextPortionList::Append(TextPortion* p)
+{
+ maPortions.push_back(std::unique_ptr<TextPortion>(p));
+}
+
+void TextPortionList::Insert(sal_Int32 nPos, TextPortion* p)
+{
+ maPortions.insert(maPortions.begin()+nPos, std::unique_ptr<TextPortion>(p));
+}
+
+void TextPortionList::Remove(sal_Int32 nPos)
+{
+ maPortions.erase(maPortions.begin()+nPos);
+}
+
+namespace {
+
+class FindTextPortionByAddress
+{
+ const TextPortion* mp;
+public:
+ explicit FindTextPortionByAddress(const TextPortion* p) : mp(p) {}
+ bool operator() (const std::unique_ptr<TextPortion>& v) const
+ {
+ return v.get() == mp;
+ }
+};
+
+}
+
+sal_Int32 TextPortionList::GetPos(const TextPortion* p) const
+{
+ PortionsType::const_iterator it =
+ std::find_if(maPortions.begin(), maPortions.end(), FindTextPortionByAddress(p));
+
+ if (it == maPortions.end())
+ return std::numeric_limits<sal_Int32>::max(); // not found.
+
+ return std::distance(maPortions.begin(), it);
+}
+
+sal_Int32 TextPortionList::FindPortion(
+ sal_Int32 nCharPos, sal_Int32& nPortionStart, bool bPreferStartingPortion) const
+{
+ // When nCharPos at portion limit, the left portion is found
+ sal_Int32 nTmpPos = 0;
+ sal_Int32 n = maPortions.size();
+ for (sal_Int32 i = 0; i < n; ++i)
+ {
+ const TextPortion& rPortion = *maPortions[i];
+ nTmpPos = nTmpPos + rPortion.GetLen();
+ if ( nTmpPos >= nCharPos )
+ {
+ // take this one if we don't prefer the starting portion, or if it's the last one
+ if ( ( nTmpPos != nCharPos ) || !bPreferStartingPortion || ( i == n-1 ) )
+ {
+ nPortionStart = nTmpPos - rPortion.GetLen();
+ return i;
+ }
+ }
+ }
+ OSL_FAIL( "FindPortion: Not found!" );
+ return n - 1;
+}
+
+sal_Int32 TextPortionList::GetStartPos(sal_Int32 nPortion)
+{
+ sal_Int32 nPos = 0;
+ for (sal_Int32 i = 0; i < nPortion; ++i)
+ {
+ const TextPortion& rPortion = *maPortions[i];
+ nPos = nPos + rPortion.GetLen();
+ }
+ return nPos;
+}
+
+ExtraPortionInfo::ExtraPortionInfo()
+: nOrgWidth(0)
+, nWidthFullCompression(0)
+, nPortionOffsetX(0)
+, nMaxCompression100thPercent(0)
+, nAsianCompressionTypes(AsianCompressionFlags::Normal)
+, bFirstCharIsRightPunktuation(false)
+, bCompressed(false)
+{
+}
+
+ExtraPortionInfo::~ExtraPortionInfo()
+{
+}
+
+void ExtraPortionInfo::SaveOrgDXArray( const sal_Int32* pDXArray, sal_Int32 nLen )
+{
+ if (pDXArray)
+ {
+ pOrgDXArray.reset(new sal_Int32[nLen]);
+ memcpy( pOrgDXArray.get(), pDXArray, nLen * sizeof(sal_Int32) );
+ }
+ else
+ pOrgDXArray.reset();
+}
+
+ParaPortion::ParaPortion( ContentNode* pN ) :
+ pNode(pN),
+ nHeight(0),
+ nInvalidPosStart(0),
+ nFirstLineOffset(0),
+ nBulletX(0),
+ nInvalidDiff(0),
+ bInvalid(true),
+ bSimple(false),
+ bVisible(true),
+ bForceRepaint(false)
+{
+}
+
+ParaPortion::~ParaPortion()
+{
+}
+
+void ParaPortion::MarkInvalid( sal_Int32 nStart, sal_Int32 nDiff )
+{
+ if ( !bInvalid )
+ {
+// nInvalidPosEnd = nStart; // ??? => CreateLines
+ nInvalidPosStart = ( nDiff >= 0 ) ? nStart : ( nStart + nDiff );
+ nInvalidDiff = nDiff;
+ }
+ else
+ {
+ // Simple tap in succession
+ if ( ( nDiff > 0 ) && ( nInvalidDiff > 0 ) &&
+ ( ( nInvalidPosStart+nInvalidDiff ) == nStart ) )
+ {
+ nInvalidDiff = nInvalidDiff + nDiff;
+ }
+ // Simple delete in succession
+ else if ( ( nDiff < 0 ) && ( nInvalidDiff < 0 ) && ( nInvalidPosStart == nStart ) )
+ {
+ nInvalidPosStart = nInvalidPosStart + nDiff;
+ nInvalidDiff = nInvalidDiff + nDiff;
+ }
+ else
+ {
+// nInvalidPosEnd = pNode->Len();
+ DBG_ASSERT( ( nDiff >= 0 ) || ( (nStart+nDiff) >= 0 ), "MarkInvalid: Diff out of Range" );
+ nInvalidPosStart = std::min( nInvalidPosStart, ( nDiff < 0 ? nStart+nDiff : nDiff ) );
+ nInvalidDiff = 0;
+ bSimple = false;
+ }
+ }
+ bInvalid = true;
+ aScriptInfos.clear();
+ aWritingDirectionInfos.clear();
+}
+
+void ParaPortion::MarkSelectionInvalid( sal_Int32 nStart )
+{
+ if ( !bInvalid )
+ {
+ nInvalidPosStart = nStart;
+ }
+ else
+ {
+ nInvalidPosStart = std::min( nInvalidPosStart, nStart );
+ }
+ nInvalidDiff = 0;
+ bInvalid = true;
+ bSimple = false;
+ aScriptInfos.clear();
+ aWritingDirectionInfos.clear();
+}
+
+sal_Int32 ParaPortion::GetLineNumber( sal_Int32 nIndex ) const
+{
+ SAL_WARN_IF( !aLineList.Count(), "editeng", "Empty ParaPortion in GetLine!" );
+ DBG_ASSERT( bVisible, "Why GetLine() on an invisible paragraph?" );
+
+ for ( sal_Int32 nLine = 0; nLine < aLineList.Count(); nLine++ )
+ {
+ if ( aLineList[nLine].IsIn( nIndex ) )
+ return nLine;
+ }
+
+ // Then it should be at the end of the last line!
+ DBG_ASSERT( nIndex == aLineList[ aLineList.Count() - 1 ].GetEnd(), "Index dead wrong!" );
+ return (aLineList.Count()-1);
+}
+
+void ParaPortion::SetVisible( bool bMakeVisible )
+{
+ bVisible = bMakeVisible;
+}
+
+void ParaPortion::CorrectValuesBehindLastFormattedLine( sal_Int32 nLastFormattedLine )
+{
+ sal_Int32 nLines = aLineList.Count();
+ DBG_ASSERT( nLines, "CorrectPortionNumbersFromLine: Empty Portion?" );
+ if ( nLastFormattedLine < ( nLines - 1 ) )
+ {
+ const EditLine& rLastFormatted = aLineList[ nLastFormattedLine ];
+ const EditLine& rUnformatted = aLineList[ nLastFormattedLine+1 ];
+ sal_Int32 nPortionDiff = rUnformatted.GetStartPortion() - rLastFormatted.GetEndPortion();
+ sal_Int32 nTextDiff = rUnformatted.GetStart() - rLastFormatted.GetEnd();
+ nTextDiff++; // LastFormatted->GetEnd() was included => 1 deducted too much!
+
+ // The first unformatted must begin exactly one Portion behind the last
+ // of the formatted:
+ // If the modified line was split into one portion, can
+ // nLastEnd > nNextStart!
+ int nPDiff = -( nPortionDiff-1 );
+ int nTDiff = -( nTextDiff-1 );
+ if ( nPDiff || nTDiff )
+ {
+ for ( sal_Int32 nL = nLastFormattedLine+1; nL < nLines; nL++ )
+ {
+ EditLine& rLine = aLineList[ nL ];
+
+ rLine.GetStartPortion() = rLine.GetStartPortion() + nPDiff;
+ rLine.GetEndPortion() = rLine.GetEndPortion() + nPDiff;
+
+ rLine.GetStart() = rLine.GetStart() + nTDiff;
+ rLine.GetEnd() = rLine.GetEnd() + nTDiff;
+
+ rLine.SetValid();
+ }
+ }
+ }
+ DBG_ASSERT( aLineList[ aLineList.Count()-1 ].GetEnd() == pNode->Len(), "CorrectLines: The end is not right!" );
+}
+
+// Shared reverse lookup acceleration pieces ...
+
+namespace {
+
+template<typename Array, typename Val>
+sal_Int32 FastGetPos(const Array& rArray, const Val* p, sal_Int32& rLastPos)
+{
+ sal_Int32 nArrayLen = rArray.size();
+
+ // Through certain filter code-paths we do a lot of appends, which in
+ // turn call GetPos - creating some N^2 nightmares. If we have a
+ // non-trivially large list, do a few checks from the end first.
+ if (rLastPos > 16 && nArrayLen > 16)
+ {
+ sal_Int32 nEnd;
+ if (rLastPos > nArrayLen - 2)
+ nEnd = nArrayLen;
+ else
+ nEnd = rLastPos + 2;
+
+ for (sal_Int32 nIdx = rLastPos - 2; nIdx < nEnd; ++nIdx)
+ {
+ if (rArray.at(nIdx).get() == p)
+ {
+ rLastPos = nIdx;
+ return nIdx;
+ }
+ }
+ }
+ // The world's lamest linear search from svarray...
+ for (sal_Int32 nIdx = 0; nIdx < nArrayLen; ++nIdx)
+ if (rArray.at(nIdx).get() == p)
+ {
+ rLastPos = nIdx;
+ return rLastPos;
+ }
+
+ // XXX "not found" condition for sal_Int32 indexes
+ return EE_PARA_NOT_FOUND;
+}
+
+}
+
+ParaPortionList::ParaPortionList() : nLastCache( 0 )
+{
+}
+
+ParaPortionList::~ParaPortionList()
+{
+}
+
+sal_Int32 ParaPortionList::GetPos(const ParaPortion* p) const
+{
+ return FastGetPos(maPortions, p, nLastCache);
+}
+
+ParaPortion* ParaPortionList::operator [](sal_Int32 nPos)
+{
+ return 0 <= nPos && o3tl::make_unsigned(nPos) < maPortions.size() ? maPortions[nPos].get() : nullptr;
+}
+
+const ParaPortion* ParaPortionList::operator [](sal_Int32 nPos) const
+{
+ return 0 <= nPos && o3tl::make_unsigned(nPos) < maPortions.size() ? maPortions[nPos].get() : nullptr;
+}
+
+std::unique_ptr<ParaPortion> ParaPortionList::Release(sal_Int32 nPos)
+{
+ if (nPos < 0 || maPortions.size() <= o3tl::make_unsigned(nPos))
+ {
+ SAL_WARN( "editeng", "ParaPortionList::Release - out of bounds pos " << nPos);
+ return nullptr;
+ }
+ std::unique_ptr<ParaPortion> p = std::move(maPortions[nPos]);
+ maPortions.erase(maPortions.begin()+nPos);
+ return p;
+}
+
+void ParaPortionList::Remove(sal_Int32 nPos)
+{
+ if (nPos < 0 || maPortions.size() <= o3tl::make_unsigned(nPos))
+ {
+ SAL_WARN( "editeng", "ParaPortionList::Remove - out of bounds pos " << nPos);
+ return;
+ }
+ maPortions.erase(maPortions.begin()+nPos);
+}
+
+void ParaPortionList::Insert(sal_Int32 nPos, std::unique_ptr<ParaPortion> p)
+{
+ if (nPos < 0 || maPortions.size() < o3tl::make_unsigned(nPos))
+ {
+ SAL_WARN( "editeng", "ParaPortionList::Insert - out of bounds pos " << nPos);
+ return;
+ }
+ maPortions.insert(maPortions.begin()+nPos, std::move(p));
+}
+
+void ParaPortionList::Append(std::unique_ptr<ParaPortion> p)
+{
+ maPortions.push_back(std::move(p));
+}
+
+sal_Int32 ParaPortionList::Count() const
+{
+ size_t nSize = maPortions.size();
+ if (nSize > SAL_MAX_INT32)
+ {
+ SAL_WARN( "editeng", "ParaPortionList::Count - overflow " << nSize);
+ return SAL_MAX_INT32;
+ }
+ return nSize;
+}
+
+void ParaPortionList::Reset()
+{
+ maPortions.clear();
+}
+
+tools::Long ParaPortionList::GetYOffset(const ParaPortion* pPPortion) const
+{
+ tools::Long nHeight = 0;
+ for (const auto & rPortion : maPortions)
+ {
+ const ParaPortion* pTmpPortion = rPortion.get();
+ if ( pTmpPortion == pPPortion )
+ return nHeight;
+ nHeight += pTmpPortion->GetHeight();
+ }
+ OSL_FAIL( "GetYOffset: Portion not found" );
+ return nHeight;
+}
+
+sal_Int32 ParaPortionList::FindParagraph(tools::Long nYOffset) const
+{
+ tools::Long nY = 0;
+ for (size_t i = 0, n = maPortions.size(); i < n; ++i)
+ {
+ nY += maPortions[i]->GetHeight(); // should also be correct even in bVisible!
+ if ( nY > nYOffset )
+ return i <= SAL_MAX_INT32 ? static_cast<sal_Int32>(i) : SAL_MAX_INT32;
+ }
+ return EE_PARA_NOT_FOUND;
+}
+
+const ParaPortion* ParaPortionList::SafeGetObject(sal_Int32 nPos) const
+{
+ return 0 <= nPos && o3tl::make_unsigned(nPos) < maPortions.size() ? maPortions[nPos].get() : nullptr;
+}
+
+ParaPortion* ParaPortionList::SafeGetObject(sal_Int32 nPos)
+{
+ return 0 <= nPos && o3tl::make_unsigned(nPos) < maPortions.size() ? maPortions[nPos].get() : nullptr;
+}
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+void
+ParaPortionList::DbgCheck(ParaPortionList const& rParas, EditDoc const& rDoc)
+{
+ assert(rParas.Count() == rDoc.Count());
+ for (sal_Int32 i = 0; i < rParas.Count(); ++i)
+ {
+ assert(rParas.SafeGetObject(i) != nullptr);
+ assert(rParas.SafeGetObject(i)->GetNode() != nullptr);
+ assert(rParas.SafeGetObject(i)->GetNode() == rDoc.GetObject(i));
+ }
+}
+#endif
+
+ContentAttribsInfo::ContentAttribsInfo( SfxItemSet aParaAttribs ) :
+ aPrevParaAttribs(std::move( aParaAttribs))
+{
+}
+
+void ContentAttribsInfo::AppendCharAttrib(EditCharAttrib* pNew)
+{
+ aPrevCharAttribs.push_back(std::unique_ptr<EditCharAttrib>(pNew));
+}
+
+void ConvertItem( std::unique_ptr<SfxPoolItem>& rPoolItem, MapUnit eSourceUnit, MapUnit eDestUnit )
+{
+ DBG_ASSERT( eSourceUnit != eDestUnit, "ConvertItem - Why?!" );
+
+ switch ( rPoolItem->Which() )
+ {
+ case EE_PARA_LRSPACE:
+ {
+ assert(dynamic_cast<const SvxLRSpaceItem *>(rPoolItem.get()) != nullptr);
+ SvxLRSpaceItem& rItem = static_cast<SvxLRSpaceItem&>(*rPoolItem);
+ rItem.SetTextFirstLineOffset( sal::static_int_cast< short >( OutputDevice::LogicToLogic( rItem.GetTextFirstLineOffset(), eSourceUnit, eDestUnit ) ) );
+ rItem.SetTextLeft( OutputDevice::LogicToLogic( rItem.GetTextLeft(), eSourceUnit, eDestUnit ) );
+ rItem.SetRight( OutputDevice::LogicToLogic( rItem.GetRight(), eSourceUnit, eDestUnit ) );
+ }
+ break;
+ case EE_PARA_ULSPACE:
+ {
+ assert(dynamic_cast<const SvxULSpaceItem *>(rPoolItem.get()) != nullptr);
+ SvxULSpaceItem& rItem = static_cast<SvxULSpaceItem&>(*rPoolItem);
+ rItem.SetUpper( sal::static_int_cast< sal_uInt16 >( OutputDevice::LogicToLogic( rItem.GetUpper(), eSourceUnit, eDestUnit ) ) );
+ rItem.SetLower( sal::static_int_cast< sal_uInt16 >( OutputDevice::LogicToLogic( rItem.GetLower(), eSourceUnit, eDestUnit ) ) );
+ }
+ break;
+ case EE_PARA_SBL:
+ {
+ assert(dynamic_cast<const SvxLineSpacingItem *>(rPoolItem.get()) != nullptr);
+ SvxLineSpacingItem& rItem = static_cast<SvxLineSpacingItem&>(*rPoolItem);
+ // SetLineHeight changes also eLineSpace!
+ if ( rItem.GetLineSpaceRule() == SvxLineSpaceRule::Min )
+ rItem.SetLineHeight( sal::static_int_cast< sal_uInt16 >( OutputDevice::LogicToLogic( rItem.GetLineHeight(), eSourceUnit, eDestUnit ) ) );
+ }
+ break;
+ case EE_PARA_TABS:
+ {
+ assert(dynamic_cast<const SvxTabStopItem *>(rPoolItem.get()) != nullptr);
+ SvxTabStopItem& rItem = static_cast<SvxTabStopItem&>(*rPoolItem);
+ SvxTabStopItem* pNewItem(new SvxTabStopItem(EE_PARA_TABS));
+
+ if (sal_Int32 nDefTabDistance = rItem.GetDefaultDistance())
+ {
+ pNewItem->SetDefaultDistance(
+ OutputDevice::LogicToLogic(nDefTabDistance, eSourceUnit, eDestUnit));
+ }
+
+ for ( sal_uInt16 i = 0; i < rItem.Count(); i++ )
+ {
+ const SvxTabStop& rTab = rItem[i];
+ SvxTabStop aNewStop( OutputDevice::LogicToLogic( rTab.GetTabPos(), eSourceUnit, eDestUnit ), rTab.GetAdjustment(), rTab.GetDecimal(), rTab.GetFill() );
+ pNewItem->Insert( aNewStop );
+ }
+ rPoolItem.reset(pNewItem);
+ }
+ break;
+ case EE_CHAR_FONTHEIGHT:
+ case EE_CHAR_FONTHEIGHT_CJK:
+ case EE_CHAR_FONTHEIGHT_CTL:
+ {
+ assert(dynamic_cast<const SvxFontHeightItem *>(rPoolItem.get()) != nullptr);
+ SvxFontHeightItem& rItem = static_cast<SvxFontHeightItem&>(*rPoolItem);
+ rItem.SetHeight( OutputDevice::LogicToLogic( rItem.GetHeight(), eSourceUnit, eDestUnit ) );
+ }
+ break;
+ }
+}
+
+void ConvertAndPutItems( SfxItemSet& rDest, const SfxItemSet& rSource, const MapUnit* pSourceUnit, const MapUnit* pDestUnit )
+{
+ const SfxItemPool* pSourcePool = rSource.GetPool();
+ const SfxItemPool* pDestPool = rDest.GetPool();
+
+ for ( sal_uInt16 nWhich = EE_PARA_START; nWhich <= EE_CHAR_END; nWhich++ )
+ {
+ // If possible go through SlotID ...
+
+ sal_uInt16 nSourceWhich = nWhich;
+ sal_uInt16 nSlot = pDestPool->GetTrueSlotId( nWhich );
+ if ( nSlot )
+ {
+ sal_uInt16 nW = pSourcePool->GetTrueWhich( nSlot );
+ if ( nW )
+ nSourceWhich = nW;
+ }
+
+ if ( rSource.GetItemState( nSourceWhich, false ) == SfxItemState::SET )
+ {
+ MapUnit eSourceUnit = pSourceUnit ? *pSourceUnit : pSourcePool->GetMetric( nSourceWhich );
+ MapUnit eDestUnit = pDestUnit ? *pDestUnit : pDestPool->GetMetric( nWhich );
+ if ( eSourceUnit != eDestUnit )
+ {
+ std::unique_ptr<SfxPoolItem> pItem(rSource.Get( nSourceWhich ).Clone());
+ ConvertItem( pItem, eSourceUnit, eDestUnit );
+ pItem->SetWhich(nWhich);
+ rDest.Put( std::move(pItem) );
+ }
+ else
+ {
+ rDest.Put( rSource.Get( nSourceWhich ).CloneSetWhich(nWhich) );
+ }
+ }
+ }
+}
+
+EditLine::EditLine() :
+ nTxtWidth(0),
+ nStartPosX(0),
+ nStart(0),
+ nEnd(0),
+ nStartPortion(0), // to be able to tell the difference between a line
+ // without Portions from one with the Portion number 0
+ nEndPortion(0),
+ nHeight(0),
+ nTxtHeight(0),
+ nMaxAscent(0),
+ bHangingPunctuation(false),
+ bInvalid(true)
+{
+}
+
+EditLine::EditLine( const EditLine& r ) :
+ nTxtWidth(0),
+ nStartPosX(0),
+ nStart(r.nStart),
+ nEnd(r.nEnd),
+ nStartPortion(r.nStartPortion),
+ nEndPortion(r.nEndPortion),
+ nHeight(0),
+ nTxtHeight(0),
+ nMaxAscent(0),
+ bHangingPunctuation(r.bHangingPunctuation),
+ bInvalid(true)
+{
+}
+
+EditLine::~EditLine()
+{
+}
+
+
+EditLine* EditLine::Clone() const
+{
+ EditLine* pL = new EditLine;
+ pL->aPositions = aPositions;
+ pL->nStartPosX = nStartPosX;
+ pL->nStart = nStart;
+ pL->nEnd = nEnd;
+ pL->nStartPortion = nStartPortion;
+ pL->nEndPortion = nEndPortion;
+ pL->nHeight = nHeight;
+ pL->nTxtWidth = nTxtWidth;
+ pL->nTxtHeight = nTxtHeight;
+ pL->nMaxAscent = nMaxAscent;
+
+ return pL;
+}
+
+bool operator == ( const EditLine& r1, const EditLine& r2 )
+{
+ if ( r1.nStart != r2.nStart )
+ return false;
+
+ if ( r1.nEnd != r2.nEnd )
+ return false;
+
+ if ( r1.nStartPortion != r2.nStartPortion )
+ return false;
+
+ if ( r1.nEndPortion != r2.nEndPortion )
+ return false;
+
+ return true;
+}
+
+EditLine& EditLine::operator = ( const EditLine& r )
+{
+ nEnd = r.nEnd;
+ nStart = r.nStart;
+ nEndPortion = r.nEndPortion;
+ nStartPortion = r.nStartPortion;
+ return *this;
+}
+
+
+void EditLine::SetHeight( sal_uInt16 nH, sal_uInt16 nTxtH )
+{
+ nHeight = nH;
+ nTxtHeight = ( nTxtH ? nTxtH : nH );
+}
+
+void EditLine::SetStartPosX( sal_Int32 start )
+{
+ if (start > 0)
+ nStartPosX = start;
+ else
+ nStartPosX = 0;
+}
+
+Size EditLine::CalcTextSize( ParaPortion& rParaPortion )
+{
+ Size aSz;
+ Size aTmpSz;
+
+ DBG_ASSERT( rParaPortion.GetTextPortions().Count(), "GetTextSize before CreatePortions !" );
+
+ for ( sal_Int32 n = nStartPortion; n <= nEndPortion; n++ )
+ {
+ TextPortion& rPortion = rParaPortion.GetTextPortions()[n];
+ switch ( rPortion.GetKind() )
+ {
+ case PortionKind::TEXT:
+ case PortionKind::FIELD:
+ case PortionKind::HYPHENATOR:
+ {
+ aTmpSz = rPortion.GetSize();
+ aSz.AdjustWidth(aTmpSz.Width() );
+ if ( aSz.Height() < aTmpSz.Height() )
+ aSz.setHeight( aTmpSz.Height() );
+ }
+ break;
+ case PortionKind::TAB:
+ {
+ aSz.AdjustWidth(rPortion.GetSize().Width() );
+ }
+ break;
+ case PortionKind::LINEBREAK: break;
+ }
+ }
+
+ SetHeight( static_cast<sal_uInt16>(aSz.Height()) );
+ return aSz;
+}
+
+EditLineList::EditLineList()
+{
+}
+
+EditLineList::~EditLineList()
+{
+ Reset();
+}
+
+void EditLineList::Reset()
+{
+ maLines.clear();
+}
+
+void EditLineList::DeleteFromLine(sal_Int32 nDelFrom)
+{
+ assert(nDelFrom <= (static_cast<sal_Int32>(maLines.size()) - 1));
+ LinesType::iterator it = maLines.begin();
+ std::advance(it, nDelFrom);
+ maLines.erase(it, maLines.end());
+}
+
+sal_Int32 EditLineList::FindLine(sal_Int32 nChar, bool bInclEnd)
+{
+ sal_Int32 n = maLines.size();
+ for (sal_Int32 i = 0; i < n; ++i)
+ {
+ const EditLine& rLine = *maLines[i];
+ if ( (bInclEnd && (rLine.GetEnd() >= nChar)) ||
+ (rLine.GetEnd() > nChar) )
+ {
+ return i;
+ }
+ }
+
+ DBG_ASSERT( !bInclEnd, "Line not found: FindLine" );
+ return n - 1;
+}
+
+sal_Int32 EditLineList::Count() const
+{
+ return maLines.size();
+}
+
+const EditLine& EditLineList::operator[](sal_Int32 nPos) const
+{
+ return *maLines[nPos];
+}
+
+EditLine& EditLineList::operator[](sal_Int32 nPos)
+{
+ return *maLines[nPos];
+}
+
+void EditLineList::Append(EditLine* p)
+{
+ maLines.push_back(std::unique_ptr<EditLine>(p));
+}
+
+void EditLineList::Insert(sal_Int32 nPos, EditLine* p)
+{
+ maLines.insert(maLines.begin()+nPos, std::unique_ptr<EditLine>(p));
+}
+
+EditPaM::EditPaM() : pNode(nullptr), nIndex(0) {}
+EditPaM::EditPaM(ContentNode* p, sal_Int32 n) : pNode(p), nIndex(n) {}
+
+
+void EditPaM::SetNode(ContentNode* p)
+{
+ pNode = p;
+}
+
+bool EditPaM::DbgIsBuggy( EditDoc const & rDoc ) const
+{
+ return !pNode ||
+ rDoc.GetPos( pNode ) >= rDoc.Count() ||
+ nIndex > pNode->Len();
+}
+
+bool EditSelection::DbgIsBuggy( EditDoc const & rDoc ) const
+{
+ return aStartPaM.DbgIsBuggy( rDoc ) || aEndPaM.DbgIsBuggy( rDoc );
+}
+
+EditSelection::EditSelection()
+{
+}
+
+EditSelection::EditSelection( const EditPaM& rStartAndAnd ) :
+ aStartPaM(rStartAndAnd),
+ aEndPaM(rStartAndAnd)
+{
+}
+
+EditSelection::EditSelection( const EditPaM& rStart, const EditPaM& rEnd ) :
+ aStartPaM(rStart),
+ aEndPaM(rEnd)
+{
+}
+
+EditSelection& EditSelection::operator = ( const EditPaM& rPaM )
+{
+ aStartPaM = rPaM;
+ aEndPaM = rPaM;
+ return *this;
+}
+
+void EditSelection::Adjust( const EditDoc& rNodes )
+{
+ DBG_ASSERT( aStartPaM.GetIndex() <= aStartPaM.GetNode()->Len(), "Index out of range in Adjust(1)" );
+ DBG_ASSERT( aEndPaM.GetIndex() <= aEndPaM.GetNode()->Len(), "Index out of range in Adjust(2)" );
+
+ const ContentNode* pStartNode = aStartPaM.GetNode();
+ const ContentNode* pEndNode = aEndPaM.GetNode();
+
+ sal_Int32 nStartNode = rNodes.GetPos( pStartNode );
+ sal_Int32 nEndNode = rNodes.GetPos( pEndNode );
+
+ DBG_ASSERT( nStartNode != SAL_MAX_INT32, "Node out of range in Adjust(1)" );
+ DBG_ASSERT( nEndNode != SAL_MAX_INT32, "Node out of range in Adjust(2)" );
+
+ const bool bSwap = ( nStartNode > nEndNode ) ||
+ ( ( nStartNode == nEndNode ) &&
+ ( aStartPaM.GetIndex() > aEndPaM.GetIndex() ) );
+
+ if ( bSwap )
+ {
+ EditPaM aTmpPaM( aStartPaM );
+ aStartPaM = aEndPaM;
+ aEndPaM = aTmpPaM;
+ }
+}
+
+bool operator == ( const EditPaM& r1, const EditPaM& r2 )
+{
+ return ( r1.GetNode() == r2.GetNode() ) &&
+ ( r1.GetIndex() == r2.GetIndex() );
+}
+
+bool operator != ( const EditPaM& r1, const EditPaM& r2 )
+{
+ return !( r1 == r2 );
+}
+
+ContentNode::ContentNode( SfxItemPool& rPool ) : aContentAttribs( rPool )
+{
+}
+
+ContentNode::ContentNode( const OUString& rStr, const ContentAttribs& rContentAttribs ) :
+ maString(rStr), aContentAttribs(rContentAttribs)
+{
+}
+
+ContentNode::~ContentNode()
+{
+}
+
+void ContentNode::ExpandAttribs( sal_Int32 nIndex, sal_Int32 nNew )
+{
+ if ( !nNew )
+ return;
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(aCharAttribList);
+#endif
+
+ // Since features are treated differently than normal character attributes,
+ // but can also affect the order of the start list. // In every if ..., in the next (n) opportunities due to bFeature or
+ // an existing special case, must (n-1) opportunities be provided with
+ // bResort. The most likely possibility receives no bResort, so that is
+ // not sorted anew when all attributes are the same.
+ bool bResort = false;
+ bool bExpandedEmptyAtIndexNull = false;
+
+ std::size_t nAttr = 0;
+ CharAttribList::AttribsType& rAttribs = aCharAttribList.GetAttribs();
+ EditCharAttrib* pAttrib = GetAttrib(rAttribs, nAttr);
+ while ( pAttrib )
+ {
+ if ( pAttrib->GetEnd() >= nIndex )
+ {
+ // Move all attributes behind the insertion point...
+ if ( pAttrib->GetStart() > nIndex )
+ {
+ pAttrib->MoveForward( nNew );
+ }
+ // 0: Expand empty attribute, if at insertion point
+ else if ( pAttrib->IsEmpty() )
+ {
+ // Do not check Index, an empty one could only be there
+ // When later checking it anyhow:
+ // Special case: Start == 0; AbsLen == 1, nNew = 1
+ // => Expand, because of paragraph break!
+ // Start <= nIndex, End >= nIndex => Start=End=nIndex!
+// if ( pAttrib->GetStart() == nIndex )
+ pAttrib->Expand( nNew );
+ bResort = true;
+ if ( pAttrib->GetStart() == 0 )
+ bExpandedEmptyAtIndexNull = true;
+ }
+ // 1: Attribute starts before, goes to index ...
+ else if ( pAttrib->GetEnd() == nIndex ) // Start must be before
+ {
+ // Only expand when there is no feature
+ // and if not in exclude list!
+ // Otherwise, a UL will go on until a new ULDB, expanding both
+// if ( !pAttrib->IsFeature() && !rExclList.FindAttrib( pAttrib->Which() ) )
+ if ( !pAttrib->IsFeature() && !aCharAttribList.FindEmptyAttrib( pAttrib->Which(), nIndex ) )
+ {
+ if ( !pAttrib->IsEdge() )
+ pAttrib->Expand( nNew );
+ }
+ else
+ bResort = true;
+ }
+ // 2: Attribute starts before, goes past the Index...
+ else if ( ( pAttrib->GetStart() < nIndex ) && ( pAttrib->GetEnd() > nIndex ) )
+ {
+ DBG_ASSERT( !pAttrib->IsFeature(), "Large Feature?!" );
+ pAttrib->Expand( nNew );
+ }
+ // 3: Attribute starts on index...
+ else if ( pAttrib->GetStart() == nIndex )
+ {
+ if ( pAttrib->IsFeature() )
+ {
+ pAttrib->MoveForward( nNew );
+ bResort = true;
+ }
+ else
+ {
+ bool bExpand = false;
+ if ( nIndex == 0 )
+ {
+ bExpand = true;
+ if( bExpandedEmptyAtIndexNull )
+ {
+ // Check if this kind of attribute was empty and expanded here...
+ sal_uInt16 nW = pAttrib->GetItem()->Which();
+ for ( std::size_t nA = 0; nA < nAttr; nA++ )
+ {
+ const EditCharAttrib& r = *aCharAttribList.GetAttribs()[nA];
+ if ( ( r.GetStart() == 0 ) && ( r.GetItem()->Which() == nW ) )
+ {
+ bExpand = false;
+ break;
+ }
+ }
+
+ }
+ }
+ if ( bExpand )
+ {
+ pAttrib->Expand( nNew );
+ bResort = true;
+ }
+ else
+ {
+ pAttrib->MoveForward( nNew );
+ }
+ }
+ }
+ }
+
+ if ( pAttrib->IsEdge() )
+ pAttrib->SetEdge(false);
+
+ DBG_ASSERT( !pAttrib->IsFeature() || ( pAttrib->GetLen() == 1 ), "Expand: FeaturesLen != 1" );
+
+ DBG_ASSERT( pAttrib->GetStart() <= pAttrib->GetEnd(), "Expand: Attribute distorted!" );
+ DBG_ASSERT( ( pAttrib->GetEnd() <= Len() ), "Expand: Attribute larger than paragraph!" );
+ if ( pAttrib->IsEmpty() )
+ {
+ OSL_FAIL( "Empty Attribute after ExpandAttribs?" );
+ bResort = true;
+ rAttribs.erase(rAttribs.begin()+nAttr);
+ }
+ else
+ {
+ ++nAttr;
+ }
+ pAttrib = GetAttrib(rAttribs, nAttr);
+ }
+
+ if ( bResort )
+ aCharAttribList.ResortAttribs();
+
+ if (mpWrongList)
+ {
+ bool bSep = ( maString[ nIndex ] == ' ' ) || IsFeature( nIndex );
+ mpWrongList->TextInserted( nIndex, nNew, bSep );
+ }
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(aCharAttribList);
+#endif
+}
+
+void ContentNode::CollapseAttribs( sal_Int32 nIndex, sal_Int32 nDeleted )
+{
+ if ( !nDeleted )
+ return;
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(aCharAttribList);
+#endif
+
+ // Since features are treated differently than normal character attributes,
+ // but can also affect the order of the start list
+ bool bResort = false;
+ sal_Int32 nEndChanges = nIndex+nDeleted;
+
+ std::size_t nAttr = 0;
+ CharAttribList::AttribsType& rAttribs = aCharAttribList.GetAttribs();
+ EditCharAttrib* pAttrib = GetAttrib(rAttribs, nAttr);
+ while ( pAttrib )
+ {
+ bool bDelAttr = false;
+ if ( pAttrib->GetEnd() >= nIndex )
+ {
+ // Move all Attribute behind the insert point...
+ if ( pAttrib->GetStart() >= nEndChanges )
+ {
+ pAttrib->MoveBackward( nDeleted );
+ }
+ // 1. Delete Internal attributes...
+ else if ( ( pAttrib->GetStart() >= nIndex ) && ( pAttrib->GetEnd() <= nEndChanges ) )
+ {
+ // Special case: Attribute covers the area exactly
+ // => keep as empty Attribute.
+ if ( !pAttrib->IsFeature() && ( pAttrib->GetStart() == nIndex ) && ( pAttrib->GetEnd() == nEndChanges ) )
+ {
+ pAttrib->GetEnd() = nIndex; // empty
+ bResort = true;
+ }
+ else
+ bDelAttr = true;
+ }
+ // 2. Attribute starts earlier, ends inside or behind it ...
+ else if ( ( pAttrib->GetStart() <= nIndex ) && ( pAttrib->GetEnd() > nIndex ) )
+ {
+ DBG_ASSERT( !pAttrib->IsFeature(), "Collapsing Feature!" );
+ if ( pAttrib->GetEnd() <= nEndChanges ) // ends inside
+ pAttrib->GetEnd() = nIndex;
+ else
+ pAttrib->Collaps( nDeleted ); // ends behind
+ }
+ // 3. Attribute starts inside, ending behind ...
+ else if ( ( pAttrib->GetStart() >= nIndex ) && ( pAttrib->GetEnd() > nEndChanges ) )
+ {
+ // Features not allowed to expand!
+ if ( pAttrib->IsFeature() )
+ {
+ pAttrib->MoveBackward( nDeleted );
+ bResort = true;
+ }
+ else
+ {
+ pAttrib->GetStart() = nEndChanges;
+ pAttrib->MoveBackward( nDeleted );
+ }
+ }
+ }
+ DBG_ASSERT( !pAttrib->IsFeature() || ( pAttrib->GetLen() == 1 ), "Expand: FeaturesLen != 1" );
+
+ DBG_ASSERT( pAttrib->GetStart() <= pAttrib->GetEnd(), "Collapse: Attribute distorted!" );
+ DBG_ASSERT( ( pAttrib->GetEnd() <= Len()) || bDelAttr, "Collapse: Attribute larger than paragraph!" );
+ if ( bDelAttr )
+ {
+ bResort = true;
+ rAttribs.erase(rAttribs.begin()+nAttr);
+ }
+ else
+ {
+ if ( pAttrib->IsEmpty() )
+ aCharAttribList.SetHasEmptyAttribs(true);
+ nAttr++;
+ }
+
+ pAttrib = GetAttrib(rAttribs, nAttr);
+ }
+
+ if ( bResort )
+ aCharAttribList.ResortAttribs();
+
+ if (mpWrongList)
+ mpWrongList->TextDeleted(nIndex, nDeleted);
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(aCharAttribList);
+#endif
+}
+
+void ContentNode::CopyAndCutAttribs( ContentNode* pPrevNode, SfxItemPool& rPool, bool bKeepEndingAttribs )
+{
+ assert(pPrevNode);
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(aCharAttribList);
+ CharAttribList::DbgCheckAttribs(pPrevNode->aCharAttribList);
+#endif
+
+ sal_Int32 nCut = pPrevNode->Len();
+
+ std::size_t nAttr = 0;
+ CharAttribList::AttribsType& rPrevAttribs = pPrevNode->GetCharAttribs().GetAttribs();
+ EditCharAttrib* pAttrib = GetAttrib(rPrevAttribs, nAttr);
+ while ( pAttrib )
+ {
+ if ( pAttrib->GetEnd() < nCut )
+ {
+ // remain unchanged...
+ nAttr++;
+ }
+ else if ( pAttrib->GetEnd() == nCut )
+ {
+ // must be copied as an empty attributes.
+ if ( bKeepEndingAttribs && !pAttrib->IsFeature() && !aCharAttribList.FindAttrib( pAttrib->GetItem()->Which(), 0 ) )
+ {
+ EditCharAttrib* pNewAttrib = MakeCharAttrib( rPool, *(pAttrib->GetItem()), 0, 0 );
+ assert(pNewAttrib);
+ aCharAttribList.InsertAttrib( pNewAttrib );
+ }
+ nAttr++;
+ }
+ else if ( pAttrib->IsInside( nCut ) || ( !nCut && !pAttrib->GetStart() && !pAttrib->IsFeature() ) )
+ {
+ // If cut is done right at the front then the attribute must be
+ // kept! Has to be copied and changed.
+ EditCharAttrib* pNewAttrib = MakeCharAttrib( rPool, *(pAttrib->GetItem()), 0, pAttrib->GetEnd()-nCut );
+ assert(pNewAttrib);
+ aCharAttribList.InsertAttrib( pNewAttrib );
+ pAttrib->GetEnd() = nCut;
+ nAttr++;
+ }
+ else
+ {
+ // Move all attributes in the current node (this)
+ CharAttribList::AttribsType::iterator it = rPrevAttribs.begin() + nAttr;
+ aCharAttribList.InsertAttrib(it->release());
+ rPrevAttribs.erase(it);
+ pAttrib->MoveBackward( nCut );
+ }
+ pAttrib = GetAttrib(rPrevAttribs, nAttr);
+ }
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(aCharAttribList);
+ CharAttribList::DbgCheckAttribs(pPrevNode->aCharAttribList);
+#endif
+}
+
+void ContentNode::AppendAttribs( ContentNode* pNextNode )
+{
+ assert(pNextNode);
+
+ sal_Int32 nNewStart = maString.getLength();
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(aCharAttribList);
+ CharAttribList::DbgCheckAttribs(pNextNode->aCharAttribList);
+#endif
+
+ std::size_t nAttr = 0;
+ CharAttribList::AttribsType& rNextAttribs = pNextNode->GetCharAttribs().GetAttribs();
+ EditCharAttrib* pAttrib = GetAttrib(rNextAttribs, nAttr);
+ while ( pAttrib )
+ {
+ // Move all attributes in the current node (this)
+ bool bMelted = false;
+ if ( ( pAttrib->GetStart() == 0 ) && ( !pAttrib->IsFeature() ) )
+ {
+ // Attributes can possibly be summarized as:
+ std::size_t nTmpAttr = 0;
+ EditCharAttrib* pTmpAttrib = GetAttrib( aCharAttribList.GetAttribs(), nTmpAttr );
+ while ( !bMelted && pTmpAttrib )
+ {
+ ++nTmpAttr;
+ if ( pTmpAttrib->GetEnd() == nNewStart )
+ {
+ if (pTmpAttrib->Which() == pAttrib->Which())
+ {
+ // prevent adding 2 0-length attributes at same position
+ if ((*(pTmpAttrib->GetItem()) == *(pAttrib->GetItem()))
+ || (0 == pAttrib->GetLen()))
+ {
+ pTmpAttrib->GetEnd() =
+ pTmpAttrib->GetEnd() + pAttrib->GetLen();
+ rNextAttribs.erase(rNextAttribs.begin()+nAttr);
+ // Unsubscribe from the pool?!
+ bMelted = true;
+ }
+ else if (0 == pTmpAttrib->GetLen())
+ {
+ --nTmpAttr; // to cancel earlier increment...
+ aCharAttribList.Remove(nTmpAttr);
+ }
+ }
+ }
+ pTmpAttrib = GetAttrib( aCharAttribList.GetAttribs(), nTmpAttr );
+ }
+ }
+
+ if ( !bMelted )
+ {
+ pAttrib->GetStart() = pAttrib->GetStart() + nNewStart;
+ pAttrib->GetEnd() = pAttrib->GetEnd() + nNewStart;
+ CharAttribList::AttribsType::iterator it = rNextAttribs.begin() + nAttr;
+ aCharAttribList.InsertAttrib(it->release());
+ rNextAttribs.erase(it);
+ }
+ pAttrib = GetAttrib(rNextAttribs, nAttr);
+ }
+ // For the Attributes that just moved over:
+ rNextAttribs.clear();
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(aCharAttribList);
+ CharAttribList::DbgCheckAttribs(pNextNode->aCharAttribList);
+#endif
+}
+
+void ContentNode::CreateDefFont()
+{
+ // First use the information from the style ...
+ SfxStyleSheet* pS = aContentAttribs.GetStyleSheet();
+ if ( pS )
+ CreateFont( GetCharAttribs().GetDefFont(), pS->GetItemSet() );
+
+ // ... then iron out the hard paragraph formatting...
+ CreateFont( GetCharAttribs().GetDefFont(),
+ GetContentAttribs().GetItems(), pS == nullptr );
+}
+
+void ContentNode::SetStyleSheet( SfxStyleSheet* pS, const SvxFont& rFontFromStyle )
+{
+ aContentAttribs.SetStyleSheet( pS );
+
+
+ // First use the information from the style ...
+ GetCharAttribs().GetDefFont() = rFontFromStyle;
+ // ... then iron out the hard paragraph formatting...
+ CreateFont( GetCharAttribs().GetDefFont(),
+ GetContentAttribs().GetItems(), pS == nullptr );
+}
+
+void ContentNode::SetStyleSheet( SfxStyleSheet* pS, bool bRecalcFont )
+{
+ aContentAttribs.SetStyleSheet( pS );
+ if ( bRecalcFont )
+ CreateDefFont();
+}
+
+bool ContentNode::IsFeature( sal_Int32 nPos ) const
+{
+ return maString[nPos] == CH_FEATURE;
+}
+
+sal_Int32 ContentNode::Len() const
+{
+ return maString.getLength();
+}
+
+sal_Int32 ContentNode::GetExpandedLen() const
+{
+ sal_Int32 nLen = maString.getLength();
+
+ // Fields can be longer than the placeholder in the Node
+ const CharAttribList::AttribsType& rAttrs = GetCharAttribs().GetAttribs();
+ for (sal_Int32 nAttr = rAttrs.size(); nAttr; )
+ {
+ const EditCharAttrib& rAttr = *rAttrs[--nAttr];
+ if (rAttr.Which() == EE_FEATURE_FIELD)
+ {
+ nLen += static_cast<const EditCharAttribField&>(rAttr).GetFieldValue().getLength();
+ --nLen; // Standalone, to avoid corner cases when previous getLength() returns 0
+ }
+ }
+
+ return nLen;
+}
+
+OUString ContentNode::GetExpandedText(sal_Int32 nStartPos, sal_Int32 nEndPos) const
+{
+ if ( nEndPos < 0 || nEndPos > Len() )
+ nEndPos = Len();
+
+ DBG_ASSERT( nStartPos <= nEndPos, "Start and End reversed?" );
+
+ sal_Int32 nIndex = nStartPos;
+ OUStringBuffer aStr(256);
+ const EditCharAttrib* pNextFeature = GetCharAttribs().FindFeature( nIndex );
+ while ( nIndex < nEndPos )
+ {
+ sal_Int32 nEnd = nEndPos;
+ if ( pNextFeature && ( pNextFeature->GetStart() < nEnd ) )
+ nEnd = pNextFeature->GetStart();
+ else
+ pNextFeature = nullptr; // Feature does not interest the below
+
+ DBG_ASSERT( nEnd >= nIndex, "End in front of the index?" );
+ //!! beware of sub string length of -1
+ if (nEnd > nIndex)
+ aStr.append( GetString().subView(nIndex, nEnd - nIndex) );
+
+ if ( pNextFeature )
+ {
+ switch ( pNextFeature->GetItem()->Which() )
+ {
+ case EE_FEATURE_TAB: aStr.append( "\t" );
+ break;
+ case EE_FEATURE_LINEBR: aStr.append( "\x0A" );
+ break;
+ case EE_FEATURE_FIELD:
+ aStr.append( static_cast<const EditCharAttribField*>(pNextFeature)->GetFieldValue() );
+ break;
+ default: OSL_FAIL( "What feature?" );
+ }
+ pNextFeature = GetCharAttribs().FindFeature( ++nEnd );
+ }
+ nIndex = nEnd;
+ }
+ return aStr.makeStringAndClear();
+}
+
+void ContentNode::UnExpandPosition( sal_Int32 &rPos, bool bBiasStart )
+{
+ sal_Int32 nOffset = 0;
+
+ const CharAttribList::AttribsType& rAttrs = GetCharAttribs().GetAttribs();
+ for (size_t nAttr = 0; nAttr < rAttrs.size(); ++nAttr )
+ {
+ const EditCharAttrib& rAttr = *rAttrs[nAttr];
+ assert (!(nAttr < rAttrs.size() - 1) ||
+ rAttrs[nAttr]->GetStart() <= rAttrs[nAttr + 1]->GetStart());
+
+ nOffset = rAttr.GetStart();
+
+ if (nOffset >= rPos) // happens after the position
+ return;
+
+ if (rAttr.Which() == EE_FEATURE_FIELD)
+ {
+ sal_Int32 nChunk = static_cast<const EditCharAttribField&>(rAttr).GetFieldValue().getLength();
+ nChunk--; // Character representing the field in the string
+
+ if (nOffset + nChunk >= rPos) // we're inside the field
+ {
+ if (bBiasStart)
+ rPos = rAttr.GetStart();
+ else
+ rPos = rAttr.GetEnd();
+ return;
+ }
+ // Adjust for the position
+ rPos -= nChunk;
+ }
+ }
+ assert (rPos <= Len());
+}
+
+/*
+ * Fields are represented by a single character in the underlying string
+ * and/or selection, however, they can be expanded to the full value of
+ * the field. When we're dealing with selection / offsets however we need
+ * to deal in character positions inside the real (unexpanded) string.
+ * This method maps us back to character offsets.
+ */
+void ContentNode::UnExpandPositions( sal_Int32 &rStartPos, sal_Int32 &rEndPos )
+{
+ UnExpandPosition( rStartPos, true );
+ UnExpandPosition( rEndPos, false );
+}
+
+void ContentNode::SetChar(sal_Int32 nPos, sal_Unicode c)
+{
+ maString = maString.replaceAt(nPos, 1, rtl::OUStringChar(c));
+}
+
+void ContentNode::Insert(std::u16string_view rStr, sal_Int32 nPos)
+{
+ maString = maString.replaceAt(nPos, 0, rStr);
+}
+
+void ContentNode::Append(std::u16string_view rStr)
+{
+ maString += rStr;
+}
+
+void ContentNode::Erase(sal_Int32 nPos)
+{
+ maString = maString.copy(0, nPos);
+}
+
+void ContentNode::Erase(sal_Int32 nPos, sal_Int32 nCount)
+{
+ maString = maString.replaceAt(nPos, nCount, u"");
+}
+
+OUString ContentNode::Copy(sal_Int32 nPos) const
+{
+ return maString.copy(nPos);
+}
+
+OUString ContentNode::Copy(sal_Int32 nPos, sal_Int32 nCount) const
+{
+ return maString.copy(nPos, nCount);
+}
+
+sal_Unicode ContentNode::GetChar(sal_Int32 nPos) const
+{
+ return maString[nPos];
+}
+
+void ContentNode::EnsureWrongList()
+{
+ if (!mpWrongList)
+ CreateWrongList();
+}
+
+WrongList* ContentNode::GetWrongList()
+{
+ return mpWrongList.get();
+}
+
+const WrongList* ContentNode::GetWrongList() const
+{
+ return mpWrongList.get();
+}
+
+void ContentNode::SetWrongList( WrongList* p )
+{
+ mpWrongList.reset(p);
+}
+
+void ContentNode::CreateWrongList()
+{
+ SAL_WARN_IF( mpWrongList && !mpWrongList->empty(), "editeng", "WrongList already exist!");
+ if (!mpWrongList || !mpWrongList->empty())
+ mpWrongList.reset(new WrongList);
+}
+
+void ContentNode::DestroyWrongList()
+{
+ mpWrongList.reset();
+}
+
+void ContentNode::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("ContentNode"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("maString"), BAD_CAST(maString.toUtf8().getStr()));
+ aContentAttribs.dumpAsXml(pWriter);
+ aCharAttribList.dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void ContentNode::checkAndDeleteEmptyAttribs() const
+{
+ // Delete empty attributes, but only if paragraph is not empty!
+ if (GetCharAttribs().HasEmptyAttribs() && Len())
+ {
+ const_cast<ContentNode*>(this)->GetCharAttribs().DeleteEmptyAttribs();
+ }
+}
+
+ContentAttribs::ContentAttribs( SfxItemPool& rPool )
+: pStyle(nullptr)
+, aAttribSet( rPool )
+{
+}
+
+
+SvxTabStop ContentAttribs::FindTabStop( sal_Int32 nCurPos, sal_uInt16 nDefTab )
+{
+ const SvxTabStopItem& rTabs = GetItem( EE_PARA_TABS );
+ for ( sal_uInt16 i = 0; i < rTabs.Count(); i++ )
+ {
+ const SvxTabStop& rTab = rTabs[i];
+ if ( rTab.GetTabPos() > nCurPos )
+ return rTab;
+ }
+
+ // if there's a default tab size defined for this item use that instead
+ if (rTabs.GetDefaultDistance())
+ nDefTab = rTabs.GetDefaultDistance();
+
+ // Determine DefTab ...
+ SvxTabStop aTabStop;
+ const sal_Int32 x = nCurPos / nDefTab + 1;
+ aTabStop.GetTabPos() = nDefTab * x;
+ return aTabStop;
+}
+
+void ContentAttribs::SetStyleSheet( SfxStyleSheet* pS )
+{
+ bool bStyleChanged = ( pStyle != pS );
+ pStyle = pS;
+ // Only when other style sheet, not when current style sheet modified
+ if ( !(pStyle && bStyleChanged) )
+ return;
+
+ // Selectively remove the attributes from the paragraph formatting
+ // which are specified in the style, so that the attributes of the
+ // style can have an affect.
+ const SfxItemSet& rStyleAttribs = pStyle->GetItemSet();
+ for ( sal_uInt16 nWhich = EE_PARA_START; nWhich <= EE_CHAR_END; nWhich++ )
+ {
+ // Don't change bullet on/off
+ if ( ( nWhich != EE_PARA_BULLETSTATE ) && ( rStyleAttribs.GetItemState( nWhich ) == SfxItemState::SET ) )
+ aAttribSet.ClearItem( nWhich );
+ }
+}
+
+const SfxPoolItem& ContentAttribs::GetItem( sal_uInt16 nWhich ) const
+{
+ // Hard paragraph attributes take precedence!
+ const SfxItemSet* pTakeFrom = &aAttribSet;
+ if ( pStyle && ( aAttribSet.GetItemState( nWhich, false ) != SfxItemState::SET ) )
+ pTakeFrom = &pStyle->GetItemSet();
+
+ return pTakeFrom->Get( nWhich );
+}
+
+bool ContentAttribs::HasItem( sal_uInt16 nWhich ) const
+{
+ bool bHasItem = false;
+ if ( aAttribSet.GetItemState( nWhich, false ) == SfxItemState::SET )
+ bHasItem = true;
+ else if ( pStyle && pStyle->GetItemSet().GetItemState( nWhich ) == SfxItemState::SET )
+ bHasItem = true;
+
+ return bHasItem;
+}
+
+void ContentAttribs::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("ContentAttribs"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("style"), "%s", pStyle->GetName().toUtf8().getStr());
+ aAttribSet.dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+
+ItemList::ItemList() : CurrentItem( 0 )
+{
+}
+
+const SfxPoolItem* ItemList::First()
+{
+ CurrentItem = 0;
+ return aItemPool.empty() ? nullptr : aItemPool[ 0 ];
+}
+
+const SfxPoolItem* ItemList::Next()
+{
+ if ( CurrentItem + 1 < static_cast<sal_Int32>(aItemPool.size()) )
+ {
+ ++CurrentItem;
+ return aItemPool[ CurrentItem ];
+ }
+ return nullptr;
+}
+
+void ItemList::Insert( const SfxPoolItem* pItem )
+{
+ aItemPool.push_back( pItem );
+ CurrentItem = aItemPool.size() - 1;
+}
+
+
+EditDoc::EditDoc( SfxItemPool* pPool ) :
+ nLastCache(0),
+ pItemPool(pPool ? pPool : new EditEngineItemPool()),
+ nDefTab(DEFTAB),
+ bIsVertical(false),
+ mnRotation(TextRotation::NONE),
+ bIsFixedCellHeight(false),
+ bModified(false),
+ bDisableAttributeExpanding(false)
+{
+ // Don't create an empty node, Clear() will be called in EditEngine-CTOR
+};
+
+EditDoc::~EditDoc()
+{
+ maContents.clear();
+}
+
+void CreateFont( SvxFont& rFont, const SfxItemSet& rSet, bool bSearchInParent, SvtScriptType nScriptType )
+{
+ vcl::Font aPrevFont( rFont );
+ rFont.SetAlignment( ALIGN_BASELINE );
+
+ sal_uInt16 nWhich_FontInfo = GetScriptItemId( EE_CHAR_FONTINFO, nScriptType );
+ sal_uInt16 nWhich_Language = GetScriptItemId( EE_CHAR_LANGUAGE, nScriptType );
+ sal_uInt16 nWhich_FontHeight = GetScriptItemId( EE_CHAR_FONTHEIGHT, nScriptType );
+ sal_uInt16 nWhich_Weight = GetScriptItemId( EE_CHAR_WEIGHT, nScriptType );
+ sal_uInt16 nWhich_Italic = GetScriptItemId( EE_CHAR_ITALIC, nScriptType );
+
+ if ( bSearchInParent || ( rSet.GetItemState( nWhich_FontInfo ) == SfxItemState::SET ) )
+ {
+ const SvxFontItem& rFontItem = static_cast<const SvxFontItem&>(rSet.Get( nWhich_FontInfo ));
+ rFont.SetFamilyName( rFontItem.GetFamilyName() );
+ rFont.SetFamily( rFontItem.GetFamily() );
+ rFont.SetPitch( rFontItem.GetPitch() );
+ rFont.SetCharSet( rFontItem.GetCharSet() );
+ }
+ if ( bSearchInParent || ( rSet.GetItemState( nWhich_Language ) == SfxItemState::SET ) )
+ rFont.SetLanguage( static_cast<const SvxLanguageItem&>(rSet.Get( nWhich_Language )).GetLanguage() );
+ if ( bSearchInParent || ( rSet.GetItemState( EE_CHAR_COLOR ) == SfxItemState::SET ) )
+ rFont.SetColor( rSet.Get( EE_CHAR_COLOR ).GetValue() );
+ if ( bSearchInParent || ( rSet.GetItemState( EE_CHAR_BKGCOLOR ) == SfxItemState::SET ) )
+ {
+ auto& aColor = rSet.Get( EE_CHAR_BKGCOLOR ).GetValue();
+ rFont.SetTransparent(aColor.IsTransparent());
+ rFont.SetFillColor(aColor);
+ }
+ if ( bSearchInParent || ( rSet.GetItemState( nWhich_FontHeight ) == SfxItemState::SET ) )
+ rFont.SetFontSize( Size( rFont.GetFontSize().Width(), static_cast<const SvxFontHeightItem&>(rSet.Get( nWhich_FontHeight ) ).GetHeight() ) );
+ if ( bSearchInParent || ( rSet.GetItemState( nWhich_Weight ) == SfxItemState::SET ) )
+ rFont.SetWeight( static_cast<const SvxWeightItem&>(rSet.Get( nWhich_Weight )).GetWeight() );
+ if ( bSearchInParent || ( rSet.GetItemState( EE_CHAR_UNDERLINE ) == SfxItemState::SET ) )
+ rFont.SetUnderline( rSet.Get( EE_CHAR_UNDERLINE ).GetLineStyle() );
+ if ( bSearchInParent || ( rSet.GetItemState( EE_CHAR_OVERLINE ) == SfxItemState::SET ) )
+ rFont.SetOverline( rSet.Get( EE_CHAR_OVERLINE ).GetLineStyle() );
+ if ( bSearchInParent || ( rSet.GetItemState( EE_CHAR_STRIKEOUT ) == SfxItemState::SET ) )
+ rFont.SetStrikeout( rSet.Get( EE_CHAR_STRIKEOUT ).GetStrikeout() );
+ if ( bSearchInParent || ( rSet.GetItemState( EE_CHAR_CASEMAP ) == SfxItemState::SET ) )
+ rFont.SetCaseMap( rSet.Get( EE_CHAR_CASEMAP ).GetCaseMap() );
+ if ( bSearchInParent || ( rSet.GetItemState( nWhich_Italic ) == SfxItemState::SET ) )
+ rFont.SetItalic( static_cast<const SvxPostureItem&>(rSet.Get( nWhich_Italic )).GetPosture() );
+ if ( bSearchInParent || ( rSet.GetItemState( EE_CHAR_OUTLINE ) == SfxItemState::SET ) )
+ rFont.SetOutline( rSet.Get( EE_CHAR_OUTLINE ).GetValue() );
+ if ( bSearchInParent || ( rSet.GetItemState( EE_CHAR_SHADOW ) == SfxItemState::SET ) )
+ rFont.SetShadow( rSet.Get( EE_CHAR_SHADOW ).GetValue() );
+ if ( bSearchInParent || ( rSet.GetItemState( EE_CHAR_ESCAPEMENT ) == SfxItemState::SET ) )
+ {
+ const SvxEscapementItem& rEsc = rSet.Get( EE_CHAR_ESCAPEMENT );
+
+ sal_uInt16 const nProp = rEsc.GetProportionalHeight();
+ rFont.SetPropr( static_cast<sal_uInt8>(nProp) );
+
+ short nEsc = rEsc.GetEsc();
+ rFont.SetNonAutoEscapement( nEsc );
+ }
+ if ( bSearchInParent || ( rSet.GetItemState( EE_CHAR_PAIRKERNING ) == SfxItemState::SET ) )
+ rFont.SetKerning( rSet.Get( EE_CHAR_PAIRKERNING ).GetValue() ? FontKerning::FontSpecific : FontKerning::NONE );
+ if ( bSearchInParent || ( rSet.GetItemState( EE_CHAR_KERNING ) == SfxItemState::SET ) )
+ rFont.SetFixKerning( rSet.Get( EE_CHAR_KERNING ).GetValue() );
+ if ( bSearchInParent || ( rSet.GetItemState( EE_CHAR_WLM ) == SfxItemState::SET ) )
+ rFont.SetWordLineMode( rSet.Get( EE_CHAR_WLM ).GetValue() );
+ if ( bSearchInParent || ( rSet.GetItemState( EE_CHAR_EMPHASISMARK ) == SfxItemState::SET ) )
+ rFont.SetEmphasisMark( rSet.Get( EE_CHAR_EMPHASISMARK ).GetEmphasisMark() );
+ if ( bSearchInParent || ( rSet.GetItemState( EE_CHAR_RELIEF ) == SfxItemState::SET ) )
+ rFont.SetRelief( rSet.Get( EE_CHAR_RELIEF ).GetValue() );
+
+ // Operator == compares the individual members of the font if the impl pointer is
+ // not equal. If all members are the same, this assignment makes
+ // sure that both also point to the same internal instance of the font.
+ // To avoid this assignment, you would need to check in
+ // every if statement above whether or not the new value differs from the
+ // old value before making an assignment.
+ if ( rFont == aPrevFont )
+ rFont = aPrevFont; // => The same ImpPointer for IsSameInstance
+}
+
+void EditDoc::CreateDefFont( bool bUseStyles )
+{
+ SfxItemSetFixed<EE_PARA_START, EE_CHAR_END> aTmpSet( GetItemPool() );
+ CreateFont(maDefFont, aTmpSet);
+ maDefFont.SetVertical( IsEffectivelyVertical() );
+ maDefFont.SetOrientation( Degree10(IsEffectivelyVertical() ? (IsTopToBottom() ? 2700 : 900) : 0) );
+
+ for ( sal_Int32 nNode = 0; nNode < Count(); nNode++ )
+ {
+ ContentNode* pNode = GetObject( nNode );
+ pNode->GetCharAttribs().GetDefFont() = maDefFont;
+ if ( bUseStyles )
+ pNode->CreateDefFont();
+ }
+}
+
+bool EditDoc::IsEffectivelyVertical() const
+{
+ return (bIsVertical && mnRotation == TextRotation::NONE) ||
+ (!bIsVertical && mnRotation != TextRotation::NONE);
+}
+
+bool EditDoc::IsTopToBottom() const
+{
+ return (bIsVertical && mnRotation == TextRotation::NONE) ||
+ (!bIsVertical && mnRotation == TextRotation::TOPTOBOTTOM);
+}
+
+bool EditDoc::GetVertical() const
+{
+ return bIsVertical;
+}
+
+sal_Int32 EditDoc::GetPos(const ContentNode* p) const
+{
+ return FastGetPos(maContents, p, nLastCache);
+}
+
+const ContentNode* EditDoc::GetObject(sal_Int32 nPos) const
+{
+ return 0 <= nPos && o3tl::make_unsigned(nPos) < maContents.size() ? maContents[nPos].get() : nullptr;
+}
+
+ContentNode* EditDoc::GetObject(sal_Int32 nPos)
+{
+ return 0 <= nPos && o3tl::make_unsigned(nPos) < maContents.size() ? maContents[nPos].get() : nullptr;
+}
+
+const ContentNode* EditDoc::operator[](sal_Int32 nPos) const
+{
+ return GetObject(nPos);
+}
+
+ContentNode* EditDoc::operator[](sal_Int32 nPos)
+{
+ return GetObject(nPos);
+}
+
+void EditDoc::Insert(sal_Int32 nPos, ContentNode* p)
+{
+ if (nPos < 0 || nPos == SAL_MAX_INT32)
+ {
+ SAL_WARN( "editeng", "EditDoc::Insert - overflow pos " << nPos);
+ return;
+ }
+ maContents.insert(maContents.begin()+nPos, std::unique_ptr<ContentNode>(p));
+}
+
+void EditDoc::Remove(sal_Int32 nPos)
+{
+ if (nPos < 0 || o3tl::make_unsigned(nPos) >= maContents.size())
+ {
+ SAL_WARN( "editeng", "EditDoc::Remove - out of bounds pos " << nPos);
+ return;
+ }
+ maContents.erase(maContents.begin() + nPos);
+}
+
+void EditDoc::Release(sal_Int32 nPos)
+{
+ if (nPos < 0 || o3tl::make_unsigned(nPos) >= maContents.size())
+ {
+ SAL_WARN( "editeng", "EditDoc::Release - out of bounds pos " << nPos);
+ return;
+ }
+ // coverity[leaked_storage] - this is on purpose, ownership should be transferred to undo/redo
+ (void)maContents[nPos].release();
+ maContents.erase(maContents.begin() + nPos);
+}
+
+sal_Int32 EditDoc::Count() const
+{
+ size_t nSize = maContents.size();
+ if (nSize > SAL_MAX_INT32)
+ {
+ SAL_WARN( "editeng", "EditDoc::Count - overflow " << nSize);
+ return SAL_MAX_INT32;
+ }
+ return nSize;
+}
+
+OUString EditDoc::GetSepStr( LineEnd eEnd )
+{
+ if ( eEnd == LINEEND_CR )
+ return "\015"; // 0x0d
+ if ( eEnd == LINEEND_LF )
+ return "\012"; // 0x0a
+ return "\015\012"; // 0x0d, 0x0a
+}
+
+OUString EditDoc::GetText( LineEnd eEnd ) const
+{
+ const sal_Int32 nNodes = Count();
+ if (nNodes == 0)
+ return OUString();
+
+ const OUString aSep = EditDoc::GetSepStr( eEnd );
+ const sal_Int32 nSepSize = aSep.getLength();
+ const sal_Int32 nLen = GetTextLen() + (nNodes - 1)*nSepSize;
+
+ OUStringBuffer aBuffer(nLen + 16); // leave some slack
+
+ for ( sal_Int32 nNode = 0; nNode < nNodes; nNode++ )
+ {
+ if ( nSepSize && nNode>0 )
+ {
+ aBuffer.append(aSep);
+ }
+ aBuffer.append(GetParaAsString( GetObject(nNode) ));
+ }
+
+ return aBuffer.makeStringAndClear();
+}
+
+OUString EditDoc::GetParaAsString( sal_Int32 nNode ) const
+{
+ return GetParaAsString( GetObject( nNode ) );
+}
+
+OUString EditDoc::GetParaAsString(
+ const ContentNode* pNode, sal_Int32 nStartPos, sal_Int32 nEndPos)
+{
+ return pNode->GetExpandedText(nStartPos, nEndPos);
+}
+
+EditPaM EditDoc::GetStartPaM() const
+{
+ ContentNode* p = const_cast<ContentNode*>(GetObject(0));
+ return EditPaM(p, 0);
+}
+
+EditPaM EditDoc::GetEndPaM() const
+{
+ ContentNode* pLastNode = const_cast<ContentNode*>(GetObject(Count()-1));
+ return EditPaM( pLastNode, pLastNode->Len() );
+}
+
+sal_Int32 EditDoc::GetTextLen() const
+{
+ sal_Int32 nLen = 0;
+ for ( sal_Int32 nNode = 0; nNode < Count(); nNode++ )
+ {
+ const ContentNode* pNode = GetObject( nNode );
+ nLen += pNode->GetExpandedLen();
+ }
+ return nLen;
+}
+
+EditPaM EditDoc::Clear()
+{
+ maContents.clear();
+
+ ContentNode* pNode = new ContentNode( GetItemPool() );
+ Insert(0, pNode);
+
+ CreateDefFont(false);
+
+ SetModified(false);
+
+ return EditPaM( pNode, 0 );
+}
+
+namespace
+{
+struct ClearSpellErrorsHandler
+{
+ void operator() (std::unique_ptr<ContentNode> const & rNode)
+ {
+ rNode->DestroyWrongList();
+ }
+};
+}
+
+void EditDoc::ClearSpellErrors()
+{
+ std::for_each(maContents.begin(), maContents.end(), ClearSpellErrorsHandler());
+}
+
+void EditDoc::SetModified( bool b )
+{
+ bModified = b;
+ if ( bModified )
+ {
+ aModifyHdl.Call( nullptr );
+ }
+}
+
+EditPaM EditDoc::RemoveText()
+{
+ // Keep the old ItemSet, to keep the chart Font.
+ ContentNode* pPrevFirstNode = GetObject(0);
+ SfxStyleSheet* pPrevStyle = pPrevFirstNode->GetStyleSheet();
+ SfxItemSet aPrevSet( pPrevFirstNode->GetContentAttribs().GetItems() );
+ vcl::Font aPrevFont( pPrevFirstNode->GetCharAttribs().GetDefFont() );
+
+ maContents.clear();
+
+ ContentNode* pNode = new ContentNode( GetItemPool() );
+ Insert(0, pNode);
+
+ pNode->SetStyleSheet(pPrevStyle, false);
+ pNode->GetContentAttribs().GetItems().Set( aPrevSet );
+ pNode->GetCharAttribs().GetDefFont() = aPrevFont;
+
+ SetModified(true);
+
+ return EditPaM( pNode, 0 );
+}
+
+EditPaM EditDoc::InsertText( EditPaM aPaM, std::u16string_view rStr )
+{
+ DBG_ASSERT( rStr.find( 0x0A ) == std::u16string_view::npos, "EditDoc::InsertText: Newlines prohibited in paragraph!" );
+ DBG_ASSERT( rStr.find( 0x0D ) == std::u16string_view::npos, "EditDoc::InsertText: Newlines prohibited in paragraph!" );
+ DBG_ASSERT( rStr.find( '\t' ) == std::u16string_view::npos, "EditDoc::InsertText: Newlines prohibited in paragraph!" );
+ assert(aPaM.GetNode());
+
+ aPaM.GetNode()->Insert( rStr, aPaM.GetIndex() );
+ aPaM.GetNode()->ExpandAttribs( aPaM.GetIndex(), rStr.size() );
+ aPaM.SetIndex( aPaM.GetIndex() + rStr.size() );
+
+ SetModified( true );
+
+ return aPaM;
+}
+
+EditPaM EditDoc::InsertParaBreak( EditPaM aPaM, bool bKeepEndingAttribs )
+{
+ assert(aPaM.GetNode());
+ ContentNode* pCurNode = aPaM.GetNode();
+ sal_Int32 nPos = GetPos( pCurNode );
+ OUString aStr = aPaM.GetNode()->Copy( aPaM.GetIndex() );
+ aPaM.GetNode()->Erase( aPaM.GetIndex() );
+
+ // the paragraph attributes...
+ ContentAttribs aContentAttribs( aPaM.GetNode()->GetContentAttribs() );
+
+ // for a new paragraph we like to have the bullet/numbering visible by default
+ aContentAttribs.GetItems().Put( SfxBoolItem( EE_PARA_BULLETSTATE, true) );
+
+ // ContentNode constructor copies also the paragraph attributes
+ ContentNode* pNode = new ContentNode( aStr, std::move(aContentAttribs) );
+
+ // Copy the Default Font
+ pNode->GetCharAttribs().GetDefFont() = aPaM.GetNode()->GetCharAttribs().GetDefFont();
+ SfxStyleSheet* pStyle = aPaM.GetNode()->GetStyleSheet();
+ if ( pStyle )
+ {
+ OUString aFollow( pStyle->GetFollow() );
+ if ( !aFollow.isEmpty() && ( aFollow != pStyle->GetName() ) )
+ {
+ SfxStyleSheetBase* pNext = pStyle->GetPool()->Find( aFollow, pStyle->GetFamily() );
+ pNode->SetStyleSheet( static_cast<SfxStyleSheet*>(pNext) );
+ }
+ }
+
+ // Character attributes may need to be copied or trimmed:
+ pNode->CopyAndCutAttribs( aPaM.GetNode(), GetItemPool(), bKeepEndingAttribs );
+
+ Insert(nPos+1, pNode);
+
+ SetModified(true);
+
+ aPaM.SetNode( pNode );
+ aPaM.SetIndex( 0 );
+ return aPaM;
+}
+
+EditPaM EditDoc::InsertFeature( EditPaM aPaM, const SfxPoolItem& rItem )
+{
+ assert(aPaM.GetNode());
+
+ aPaM.GetNode()->Insert( rtl::OUStringChar(CH_FEATURE), aPaM.GetIndex() );
+ aPaM.GetNode()->ExpandAttribs( aPaM.GetIndex(), 1 );
+
+ // Create a feature-attribute for the feature...
+ EditCharAttrib* pAttrib = MakeCharAttrib( GetItemPool(), rItem, aPaM.GetIndex(), aPaM.GetIndex()+1 );
+ assert(pAttrib);
+ aPaM.GetNode()->GetCharAttribs().InsertAttrib( pAttrib );
+
+ SetModified( true );
+
+ aPaM.SetIndex( aPaM.GetIndex() + 1 );
+ return aPaM;
+}
+
+EditPaM EditDoc::ConnectParagraphs( ContentNode* pLeft, ContentNode* pRight )
+{
+ const EditPaM aPaM( pLeft, pLeft->Len() );
+
+ // First the attributes, otherwise nLen will not be correct!
+ pLeft->AppendAttribs( pRight );
+ // then the Text...
+ pLeft->Append(pRight->GetString());
+
+ // the one to the right disappears.
+ sal_Int32 nRight = GetPos( pRight );
+ Remove( nRight );
+
+ SetModified(true);
+
+ return aPaM;
+}
+
+void EditDoc::RemoveChars( EditPaM aPaM, sal_Int32 nChars )
+{
+ // Maybe remove Features!
+ aPaM.GetNode()->Erase( aPaM.GetIndex(), nChars );
+ aPaM.GetNode()->CollapseAttribs( aPaM.GetIndex(), nChars );
+
+ SetModified( true );
+}
+
+void EditDoc::InsertAttribInSelection( ContentNode* pNode, sal_Int32 nStart, sal_Int32 nEnd, const SfxPoolItem& rPoolItem )
+{
+ assert(pNode);
+ DBG_ASSERT( nEnd <= pNode->Len(), "InsertAttrib: Attribute too large!" );
+
+ // for Optimization:
+ // This ends at the beginning of the selection => can be expanded
+ EditCharAttrib* pEndingAttrib = nullptr;
+ // This starts at the end of the selection => can be expanded
+ EditCharAttrib* pStartingAttrib = nullptr;
+
+ DBG_ASSERT( nStart <= nEnd, "Small miscalculations in InsertAttribInSelection" );
+
+ RemoveAttribs( pNode, nStart, nEnd, pStartingAttrib, pEndingAttrib, rPoolItem.Which() );
+
+ // tdf#132288 By default inserting an attribute beside another that is of
+ // the same type expands the original instead of inserting another. But the
+ // spell check dialog doesn't want that behaviour
+ if (bDisableAttributeExpanding)
+ {
+ pStartingAttrib = nullptr;
+ pEndingAttrib = nullptr;
+ }
+
+ if ( pStartingAttrib && pEndingAttrib &&
+ ( *(pStartingAttrib->GetItem()) == rPoolItem ) &&
+ ( *(pEndingAttrib->GetItem()) == rPoolItem ) )
+ {
+ // Will become a large Attribute.
+ pEndingAttrib->GetEnd() = pStartingAttrib->GetEnd();
+ pNode->GetCharAttribs().Remove(pStartingAttrib);
+ }
+ else if ( pStartingAttrib && ( *(pStartingAttrib->GetItem()) == rPoolItem ) )
+ pStartingAttrib->GetStart() = nStart;
+ else if ( pEndingAttrib && ( *(pEndingAttrib->GetItem()) == rPoolItem ) )
+ pEndingAttrib->GetEnd() = nEnd;
+ else
+ InsertAttrib( rPoolItem, pNode, nStart, nEnd );
+
+ if ( pStartingAttrib )
+ pNode->GetCharAttribs().ResortAttribs();
+
+ SetModified(true);
+}
+
+bool EditDoc::RemoveAttribs( ContentNode* pNode, sal_Int32 nStart, sal_Int32 nEnd, sal_uInt16 nWhich )
+{
+ EditCharAttrib* pStarting;
+ EditCharAttrib* pEnding;
+ return RemoveAttribs( pNode, nStart, nEnd, pStarting, pEnding, nWhich );
+}
+
+bool EditDoc::RemoveAttribs( ContentNode* pNode, sal_Int32 nStart, sal_Int32 nEnd, EditCharAttrib*& rpStarting, EditCharAttrib*& rpEnding, sal_uInt16 nWhich )
+{
+
+ assert(pNode);
+ DBG_ASSERT( nEnd <= pNode->Len(), "InsertAttrib: Attribute too large!" );
+
+ // This ends at the beginning of the selection => can be expanded
+ rpEnding = nullptr;
+ // This starts at the end of the selection => can be expanded
+ rpStarting = nullptr;
+
+ bool bChanged = false;
+ bool bNeedsSorting = false;
+
+ DBG_ASSERT( nStart <= nEnd, "Small miscalculations in InsertAttribInSelection" );
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(pNode->GetCharAttribs());
+#endif
+
+ // iterate over the attributes ...
+ std::size_t nAttr = 0;
+ CharAttribList::AttribsType& rAttribs = pNode->GetCharAttribs().GetAttribs();
+ EditCharAttrib* pAttr = GetAttrib(rAttribs, nAttr);
+ while ( pAttr )
+ {
+ bool bRemoveAttrib = false;
+ sal_uInt16 nAttrWhich = pAttr->Which();
+ if ( ( nAttrWhich < EE_FEATURE_START ) && ( !nWhich || ( nAttrWhich == nWhich ) ) )
+ {
+ // Attribute starts in Selection
+ if ( ( pAttr->GetStart() >= nStart ) && ( pAttr->GetStart() <= nEnd ) )
+ {
+ bChanged = true;
+ if ( pAttr->GetEnd() > nEnd )
+ {
+ bNeedsSorting = true;
+ pAttr->GetStart() = nEnd; // then it starts after this
+ rpStarting = pAttr;
+ if ( nWhich )
+ break; // There can be no further attributes here
+ }
+ else if ( !pAttr->IsFeature() || ( pAttr->GetStart() == nStart ) )
+ {
+ // Delete feature only if on the exact spot
+ bRemoveAttrib = true;
+ }
+ }
+
+ // Attribute ends in Selection
+ else if ( ( pAttr->GetEnd() >= nStart ) && ( pAttr->GetEnd() <= nEnd ) )
+ {
+ bChanged = true;
+ if ( ( pAttr->GetStart() < nStart ) && !pAttr->IsFeature() )
+ {
+ pAttr->GetEnd() = nStart; // then it ends here
+ rpEnding = pAttr;
+ }
+ else if ( !pAttr->IsFeature() || ( pAttr->GetStart() == nStart ) )
+ {
+ // Delete feature only if on the exact spot
+ bRemoveAttrib = true;
+ }
+ }
+ // Attribute overlaps the selection
+ else if ( ( pAttr->GetStart() <= nStart ) && ( pAttr->GetEnd() >= nEnd ) )
+ {
+ bChanged = true;
+ if ( pAttr->GetStart() == nStart )
+ {
+ bNeedsSorting = true;
+ pAttr->GetStart() = nEnd;
+ rpStarting = pAttr;
+ if ( nWhich )
+ break; // There can be further attributes!
+ }
+ else if ( pAttr->GetEnd() == nEnd )
+ {
+ pAttr->GetEnd() = nStart;
+ rpEnding = pAttr;
+ if ( nWhich )
+ break; // There can be further attributes!
+ }
+ else // Attribute must be split ...
+ {
+ bNeedsSorting = true;
+ sal_Int32 nOldEnd = pAttr->GetEnd();
+ pAttr->GetEnd() = nStart;
+ rpEnding = pAttr;
+ InsertAttrib( *pAttr->GetItem(), pNode, nEnd, nOldEnd );
+ if ( nWhich )
+ break; // There can be further attributes!
+ }
+ }
+ }
+ if ( bRemoveAttrib )
+ {
+ DBG_ASSERT( ( pAttr != rpStarting ) && ( pAttr != rpEnding ), "Delete and retain the same attribute?" );
+ DBG_ASSERT( !pAttr->IsFeature(), "RemoveAttribs: Remove a feature?!" );
+ rAttribs.erase(rAttribs.begin()+nAttr);
+ }
+ else
+ {
+ nAttr++;
+ }
+ pAttr = GetAttrib(rAttribs, nAttr);
+ }
+
+ if ( bChanged )
+ {
+ // char attributes need to be sorted by start again
+ if (bNeedsSorting)
+ pNode->GetCharAttribs().ResortAttribs();
+ SetModified(true);
+ }
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(pNode->GetCharAttribs());
+#endif
+
+ return bChanged;
+}
+
+void EditDoc::InsertAttrib( const SfxPoolItem& rPoolItem, ContentNode* pNode, sal_Int32 nStart, sal_Int32 nEnd )
+{
+ // This method no longer checks whether a corresponding attribute already
+ // exists at this place!
+ EditCharAttrib* pAttrib = MakeCharAttrib( GetItemPool(), rPoolItem, nStart, nEnd );
+ assert(pAttrib);
+ pNode->GetCharAttribs().InsertAttrib( pAttrib );
+
+ SetModified( true );
+}
+
+void EditDoc::InsertAttrib( ContentNode* pNode, sal_Int32 nStart, sal_Int32 nEnd, const SfxPoolItem& rPoolItem )
+{
+ if ( nStart != nEnd )
+ {
+ InsertAttribInSelection( pNode, nStart, nEnd, rPoolItem );
+ }
+ else
+ {
+ // Check whether already a new attribute with WhichId exists at this place:
+ CharAttribList& rAttrList = pNode->GetCharAttribs();
+ EditCharAttrib* pAttr = rAttrList.FindEmptyAttrib( rPoolItem.Which(), nStart );
+ if ( pAttr )
+ {
+ // Remove attribute...
+ rAttrList.Remove(pAttr);
+ }
+
+ // check whether 'the same' attribute exist at this place.
+ pAttr = rAttrList.FindAttrib( rPoolItem.Which(), nStart );
+ if ( pAttr )
+ {
+ if ( pAttr->IsInside( nStart ) ) // split
+ {
+ // check again if really splitting, or return !
+ sal_Int32 nOldEnd = pAttr->GetEnd();
+ pAttr->GetEnd() = nStart;
+ EditCharAttrib* pNew = MakeCharAttrib( GetItemPool(), *(pAttr->GetItem()), nStart, nOldEnd );
+ rAttrList.InsertAttrib(pNew);
+ }
+ else if ( pAttr->GetEnd() == nStart )
+ {
+ DBG_ASSERT( !pAttr->IsEmpty(), "Still an empty attribute?" );
+ // Check if exactly the same attribute
+ if ( *(pAttr->GetItem()) == rPoolItem )
+ return;
+ }
+ }
+ InsertAttrib( rPoolItem, pNode, nStart, nStart );
+ }
+
+ SetModified( true );
+}
+
+void EditDoc::FindAttribs( ContentNode* pNode, sal_Int32 nStartPos, sal_Int32 nEndPos, SfxItemSet& rCurSet )
+{
+ assert(pNode);
+ DBG_ASSERT( nStartPos <= nEndPos, "Invalid region!" );
+
+ std::size_t nAttr = 0;
+ EditCharAttrib* pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
+ // No Selection...
+ if ( nStartPos == nEndPos )
+ {
+ while ( pAttr && ( pAttr->GetStart() <= nEndPos) )
+ {
+ const SfxPoolItem* pItem = nullptr;
+ // Attribute is about...
+ if ( ( pAttr->GetStart() < nStartPos ) && ( pAttr->GetEnd() > nStartPos ) )
+ pItem = pAttr->GetItem();
+ // Attribute ending here is not empty
+ else if ( ( pAttr->GetStart() < nStartPos ) && ( pAttr->GetEnd() == nStartPos ) )
+ {
+ if ( !pNode->GetCharAttribs().FindEmptyAttrib( pAttr->GetItem()->Which(), nStartPos ) )
+ pItem = pAttr->GetItem();
+ }
+ // Attribute ending here is empty
+ else if ( ( pAttr->GetStart() == nStartPos ) && ( pAttr->GetEnd() == nStartPos ) )
+ {
+ pItem = pAttr->GetItem();
+ }
+ // Attribute starts here
+ else if ( ( pAttr->GetStart() == nStartPos ) && ( pAttr->GetEnd() > nStartPos ) )
+ {
+ if ( nStartPos == 0 ) // special case
+ pItem = pAttr->GetItem();
+ }
+
+ if ( pItem )
+ {
+ sal_uInt16 nWhich = pItem->Which();
+ if ( rCurSet.GetItemState( nWhich ) == SfxItemState::DEFAULT )
+ {
+ rCurSet.Put( *pItem );
+ }
+ else if ( rCurSet.GetItemState( nWhich ) == SfxItemState::SET )
+ {
+ const SfxPoolItem& rItem = rCurSet.Get( nWhich );
+ if ( rItem != *pItem )
+ {
+ rCurSet.InvalidateItem( nWhich );
+ }
+ }
+ }
+ nAttr++;
+ pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
+ }
+ }
+ else // Selection
+ {
+ while ( pAttr && ( pAttr->GetStart() < nEndPos) )
+ {
+ const SfxPoolItem* pItem = nullptr;
+ // Attribute is about...
+ if ( ( pAttr->GetStart() <= nStartPos ) && ( pAttr->GetEnd() >= nEndPos ) )
+ pItem = pAttr->GetItem();
+ // Attribute starts right in the middle ...
+ else if ( pAttr->GetStart() >= nStartPos )
+ {
+ // !!! pItem = pAttr->GetItem();
+ // PItem is simply not enough, since one for example in case
+ // of Shadow, would never find an unequal item, since such a
+ // item represents its presence by absence!
+ // If (...)
+ // It needs to be examined on exactly the same attribute at the
+ // break point, which is quite expensive.
+ // Since optimization is done when inserting the attributes
+ // this case does not appear so fast...
+ // So based on the need for speed:
+ rCurSet.InvalidateItem( pAttr->GetItem()->Which() );
+
+ }
+ // Attribute ends in the middle of it ...
+ else if ( pAttr->GetEnd() > nStartPos )
+ {
+ rCurSet.InvalidateItem( pAttr->GetItem()->Which() );
+ }
+
+ if ( pItem )
+ {
+ sal_uInt16 nWhich = pItem->Which();
+ if ( rCurSet.GetItemState( nWhich ) == SfxItemState::DEFAULT )
+ {
+ rCurSet.Put( *pItem );
+ }
+ else if ( rCurSet.GetItemState( nWhich ) == SfxItemState::SET )
+ {
+ const SfxPoolItem& rItem = rCurSet.Get( nWhich );
+ if ( rItem != *pItem )
+ {
+ rCurSet.InvalidateItem( nWhich );
+ }
+ }
+ }
+ nAttr++;
+ pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
+ }
+ }
+}
+
+void EditDoc::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ bool bOwns = false;
+ if (!pWriter)
+ {
+ pWriter = xmlNewTextWriterFilename("editdoc.xml", 0);
+ xmlTextWriterSetIndent(pWriter,1);
+ (void)xmlTextWriterSetIndentString(pWriter, BAD_CAST(" "));
+ (void)xmlTextWriterStartDocument(pWriter, nullptr, nullptr, nullptr);
+ bOwns = true;
+ }
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("EditDoc"));
+ for (auto const & i : maContents)
+ {
+ i->dumpAsXml(pWriter);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+
+ if (bOwns)
+ {
+ (void)xmlTextWriterEndDocument(pWriter);
+ xmlFreeTextWriter(pWriter);
+ }
+}
+
+
+namespace {
+
+struct LessByStart
+{
+ bool operator() (const std::unique_ptr<EditCharAttrib>& left, const std::unique_ptr<EditCharAttrib>& right) const
+ {
+ return left->GetStart() < right->GetStart();
+ }
+};
+
+}
+
+CharAttribList::CharAttribList()
+: bHasEmptyAttribs(false)
+{
+}
+
+CharAttribList::~CharAttribList()
+{
+}
+
+void CharAttribList::InsertAttrib( EditCharAttrib* pAttrib )
+{
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+// optimize: binary search? !
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+ // Maybe just simply iterate backwards:
+ // The most common and critical case: Attributes are already sorted
+ // (InsertTextObject!) binary search would not be optimal here.
+ // => Would bring something!
+
+ const sal_Int32 nStart = pAttrib->GetStart(); // may be better for Comp.Opt.
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(*this);
+#endif
+
+ if ( pAttrib->IsEmpty() )
+ bHasEmptyAttribs = true;
+
+ bool bInsert(true);
+ for (sal_Int32 i = 0, n = aAttribs.size(); i < n; ++i)
+ {
+ const EditCharAttrib& rCurAttrib = *aAttribs[i];
+ if (rCurAttrib.GetStart() > nStart)
+ {
+ aAttribs.insert(aAttribs.begin()+i, std::unique_ptr<EditCharAttrib>(pAttrib));
+ bInsert = false;
+ break;
+ }
+ }
+
+ if (bInsert) aAttribs.push_back(std::unique_ptr<EditCharAttrib>(pAttrib));
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(*this);
+#endif
+}
+
+void CharAttribList::ResortAttribs()
+{
+ std::sort(aAttribs.begin(), aAttribs.end(), LessByStart());
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(*this);
+#endif
+}
+
+void CharAttribList::OptimizeRanges()
+{
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(*this);
+#endif
+ for (sal_Int32 i = 0; i < static_cast<sal_Int32>(aAttribs.size()); ++i)
+ {
+ EditCharAttrib& rAttr = *aAttribs[i];
+ for (sal_Int32 nNext = i+1; nNext < static_cast<sal_Int32>(aAttribs.size()); ++nNext)
+ {
+ EditCharAttrib& rNext = *aAttribs[nNext];
+ if (!rAttr.IsFeature() && rNext.GetStart() == rAttr.GetEnd() && rNext.Which() == rAttr.Which())
+ {
+ if (*rNext.GetItem() == *rAttr.GetItem())
+ {
+ rAttr.GetEnd() = rNext.GetEnd();
+ aAttribs.erase(aAttribs.begin()+nNext);
+ }
+ break; // only 1 attr with same which can start here.
+ }
+ else if (rNext.GetStart() > rAttr.GetEnd())
+ {
+ break;
+ }
+ }
+ }
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(*this);
+#endif
+}
+
+sal_Int32 CharAttribList::Count() const
+{
+ return aAttribs.size();
+}
+
+const EditCharAttrib* CharAttribList::FindAttrib( sal_uInt16 nWhich, sal_Int32 nPos ) const
+{
+ // Backwards, if one ends where the next starts.
+ // => The starting one is the valid one ...
+ AttribsType::const_reverse_iterator it = std::find_if(aAttribs.rbegin(), aAttribs.rend(),
+ [&nWhich, &nPos](const AttribsType::value_type& rxAttr) {
+ return rxAttr->Which() == nWhich && rxAttr->IsIn(nPos); });
+ if (it != aAttribs.rend())
+ {
+ const EditCharAttrib& rAttr = **it;
+ return &rAttr;
+ }
+ return nullptr;
+}
+
+EditCharAttrib* CharAttribList::FindAttrib( sal_uInt16 nWhich, sal_Int32 nPos )
+{
+ // Backwards, if one ends where the next starts.
+ // => The starting one is the valid one ...
+ AttribsType::reverse_iterator it = std::find_if(aAttribs.rbegin(), aAttribs.rend(),
+ [&nWhich, &nPos](AttribsType::value_type& rxAttr) {
+ return rxAttr->Which() == nWhich && rxAttr->IsIn(nPos); });
+ if (it != aAttribs.rend())
+ {
+ EditCharAttrib& rAttr = **it;
+ return &rAttr;
+ }
+ return nullptr;
+}
+
+const EditCharAttrib* CharAttribList::FindNextAttrib( sal_uInt16 nWhich, sal_Int32 nFromPos ) const
+{
+ assert(nWhich);
+ for (auto const& attrib : aAttribs)
+ {
+ const EditCharAttrib& rAttr = *attrib;
+ if (rAttr.GetStart() >= nFromPos && rAttr.Which() == nWhich)
+ return &rAttr;
+ }
+ return nullptr;
+}
+
+bool CharAttribList::HasAttrib( sal_Int32 nStartPos, sal_Int32 nEndPos ) const
+{
+ return std::any_of(aAttribs.rbegin(), aAttribs.rend(),
+ [&nStartPos, &nEndPos](const AttribsType::value_type& rxAttr) {
+ return rxAttr->GetStart() < nEndPos && rxAttr->GetEnd() > nStartPos; });
+}
+
+
+namespace {
+
+class FindByAddress
+{
+ const EditCharAttrib* mpAttr;
+public:
+ explicit FindByAddress(const EditCharAttrib* p) : mpAttr(p) {}
+ bool operator() (const std::unique_ptr<EditCharAttrib>& r) const
+ {
+ return r.get() == mpAttr;
+ }
+};
+
+}
+
+void CharAttribList::Remove(const EditCharAttrib* p)
+{
+ AttribsType::iterator it = std::find_if(aAttribs.begin(), aAttribs.end(), FindByAddress(p));
+ if (it != aAttribs.end())
+ aAttribs.erase(it);
+}
+
+void CharAttribList::Remove(sal_Int32 nPos)
+{
+ if (nPos >= static_cast<sal_Int32>(aAttribs.size()))
+ return;
+
+ aAttribs.erase(aAttribs.begin()+nPos);
+}
+
+void CharAttribList::SetHasEmptyAttribs(bool b)
+{
+ bHasEmptyAttribs = b;
+}
+
+bool CharAttribList::HasBoundingAttrib( sal_Int32 nBound ) const
+{
+ // Backwards, if one ends where the next starts.
+ // => The starting one is the valid one ...
+ AttribsType::const_reverse_iterator it = aAttribs.rbegin(), itEnd = aAttribs.rend();
+ for (; it != itEnd; ++it)
+ {
+ const EditCharAttrib& rAttr = **it;
+ if (rAttr.GetEnd() < nBound)
+ return false;
+
+ if (rAttr.GetStart() == nBound || rAttr.GetEnd() == nBound)
+ return true;
+ }
+ return false;
+}
+
+EditCharAttrib* CharAttribList::FindEmptyAttrib( sal_uInt16 nWhich, sal_Int32 nPos )
+{
+ if ( !bHasEmptyAttribs )
+ return nullptr;
+
+ for (const std::unique_ptr<EditCharAttrib>& rAttr : aAttribs)
+ {
+ if (rAttr->GetStart() == nPos && rAttr->GetEnd() == nPos && rAttr->Which() == nWhich)
+ return rAttr.get();
+ }
+ return nullptr;
+}
+
+namespace {
+
+class FindByStartPos
+{
+ sal_Int32 mnPos;
+public:
+ explicit FindByStartPos(sal_Int32 nPos) : mnPos(nPos) {}
+ bool operator() (const std::unique_ptr<EditCharAttrib>& r) const
+ {
+ return r->GetStart() >= mnPos;
+ }
+};
+
+}
+
+const EditCharAttrib* CharAttribList::FindFeature( sal_Int32 nPos ) const
+{
+ // First, find the first attribute that starts at or after specified position.
+ AttribsType::const_iterator it =
+ std::find_if(aAttribs.begin(), aAttribs.end(), FindByStartPos(nPos));
+
+ if (it == aAttribs.end())
+ // All attributes are before the specified position.
+ return nullptr;
+
+ // And find the first attribute with feature.
+ it = std::find_if(it, aAttribs.end(), [](const std::unique_ptr<EditCharAttrib>& aAttrib) { return aAttrib->IsFeature(); } );
+ return it == aAttribs.end() ? nullptr : it->get();
+}
+
+void CharAttribList::DeleteEmptyAttribs()
+{
+ std::erase_if(aAttribs, [](const std::unique_ptr<EditCharAttrib>& aAttrib) { return aAttrib->IsEmpty(); } );
+ bHasEmptyAttribs = false;
+}
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+void CharAttribList::DbgCheckAttribs(CharAttribList const& rAttribs)
+{
+ std::set<std::pair<sal_Int32, sal_uInt16>> zero_set;
+ for (const std::unique_ptr<EditCharAttrib>& rAttr : rAttribs.aAttribs)
+ {
+ assert(rAttr->GetStart() <= rAttr->GetEnd());
+ assert(!rAttr->IsFeature() || rAttr->GetLen() == 1);
+ if (0 == rAttr->GetLen())
+ {
+ // not sure if 0-length attributes allowed at all in non-empty para?
+ assert(zero_set.insert(std::make_pair(rAttr->GetStart(), rAttr->Which())).second && "duplicate 0-length attribute detected");
+ }
+ }
+ CheckOrderedList(rAttribs.GetAttribs());
+}
+#endif
+
+void CharAttribList::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("CharAttribList"));
+ for (auto const & i : aAttribs) {
+ i->dumpAsXml(pWriter);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+EditEngineItemPool::EditEngineItemPool()
+ : SfxItemPool( "EditEngineItemPool", EE_ITEMS_START, EE_ITEMS_END,
+ aItemInfos, nullptr )
+{
+ m_xDefItems = EditDLL::Get().GetGlobalData()->GetDefItems();
+ SetDefaults(&m_xDefItems->getDefaults());
+}
+
+EditEngineItemPool::~EditEngineItemPool()
+{
+ ClearDefaults();
+ SetSecondaryPool(nullptr);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/editeng.cxx b/editeng/source/editeng/editeng.cxx
new file mode 100644
index 0000000000..4dbb93ce2c
--- /dev/null
+++ b/editeng/source/editeng/editeng.cxx
@@ -0,0 +1,2938 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <utility>
+
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <config_global.h>
+#include <o3tl/safeint.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/window.hxx>
+
+#include <tools/stream.hxx>
+
+#include <editeng/svxfont.hxx>
+#include "impedit.hxx"
+#include <editeng/editeng.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/editstat.hxx>
+#include <editeng/eerdll.hxx>
+#include <editeng/editrids.hrc>
+#include <editeng/flditem.hxx>
+#include <editeng/txtrange.hxx>
+#include <editeng/cmapitem.hxx>
+
+#include <editeng/autokernitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/kernitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <editeng/charreliefitem.hxx>
+
+#include <sot/exchange.hxx>
+#include <sot/formats.hxx>
+
+#include <editeng/numitem.hxx>
+#include <rtl/strbuf.hxx>
+#include <sal/log.hxx>
+#include <vcl/help.hxx>
+#include <vcl/transfer.hxx>
+#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+
+#if OSL_DEBUG_LEVEL > 1
+#include <editeng/frmdiritem.hxx>
+#endif
+#include <basegfx/polygon/b2dpolygon.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::linguistic2;
+
+
+#if (OSL_DEBUG_LEVEL > 1) || defined ( DBG_UTIL )
+static bool bDebugPaint = false;
+#endif
+
+
+static rtl::Reference<SfxItemPool> pGlobalPool;
+
+EditEngine::EditEngine( SfxItemPool* pItemPool )
+{
+ pImpEditEngine.reset( new ImpEditEngine( this, pItemPool ) );
+}
+
+EditEngine::~EditEngine()
+{
+}
+
+void EditEngine::EnableUndo( bool bEnable )
+{
+ pImpEditEngine->EnableUndo( bEnable );
+}
+
+bool EditEngine::IsUndoEnabled() const
+{
+ return pImpEditEngine->IsUndoEnabled();
+}
+
+bool EditEngine::IsInUndo() const
+{
+ return pImpEditEngine->IsInUndo();
+}
+
+EditUndoManager& EditEngine::GetUndoManager()
+{
+ return pImpEditEngine->GetUndoManager();
+}
+
+EditUndoManager* EditEngine::SetUndoManager(EditUndoManager* pNew)
+{
+ return pImpEditEngine->SetUndoManager(pNew);
+}
+
+void EditEngine::UndoActionStart( sal_uInt16 nId )
+{
+ DBG_ASSERT( !pImpEditEngine->IsInUndo(), "Calling UndoActionStart in Undomode!" );
+ if ( !pImpEditEngine->IsInUndo() )
+ pImpEditEngine->UndoActionStart( nId );
+}
+
+void EditEngine::UndoActionStart(sal_uInt16 nId, const ESelection& rSel)
+{
+ pImpEditEngine->UndoActionStart(nId, rSel);
+}
+
+void EditEngine::UndoActionEnd()
+{
+ DBG_ASSERT( !pImpEditEngine->IsInUndo(), "Calling UndoActionEnd in Undomode!" );
+ if ( !pImpEditEngine->IsInUndo() )
+ pImpEditEngine->UndoActionEnd();
+}
+
+bool EditEngine::HasTriedMergeOnLastAddUndo() const
+{
+ return pImpEditEngine->mbLastTryMerge;
+}
+
+void EditEngine::SetRefDevice( OutputDevice* pRefDev )
+{
+ pImpEditEngine->SetRefDevice( pRefDev );
+}
+
+OutputDevice* EditEngine::GetRefDevice() const
+{
+ return pImpEditEngine->GetRefDevice();
+}
+
+void EditEngine::SetRefMapMode( const MapMode& rMapMode )
+{
+ pImpEditEngine->SetRefMapMode( rMapMode );
+}
+
+MapMode const & EditEngine::GetRefMapMode() const
+{
+ return pImpEditEngine->GetRefMapMode();
+}
+
+void EditEngine::SetBackgroundColor( const Color& rColor )
+{
+ pImpEditEngine->SetBackgroundColor( rColor );
+}
+
+Color const & EditEngine::GetBackgroundColor() const
+{
+ return pImpEditEngine->GetBackgroundColor();
+}
+
+Color EditEngine::GetAutoColor() const
+{
+ return pImpEditEngine->GetAutoColor();
+}
+
+void EditEngine::EnableAutoColor( bool b )
+{
+ pImpEditEngine->EnableAutoColor( b );
+}
+
+void EditEngine::ForceAutoColor( bool b )
+{
+ pImpEditEngine->ForceAutoColor( b );
+}
+
+bool EditEngine::IsForceAutoColor() const
+{
+ return pImpEditEngine->IsForceAutoColor();
+}
+
+const SfxItemSet& EditEngine::GetEmptyItemSet() const
+{
+ return pImpEditEngine->GetEmptyItemSet();
+}
+
+void EditEngine::Draw( OutputDevice& rOutDev, const tools::Rectangle& rOutRect )
+{
+ Draw( rOutDev, rOutRect, Point( 0, 0 ) );
+}
+
+void EditEngine::Draw( OutputDevice& rOutDev, const Point& rStartPos, Degree10 nOrientation )
+{
+ // Create with 2 points, as with positive points it will end up with
+ // LONGMAX as Size, Bottom and Right in the range > LONGMAX.
+ tools::Rectangle aBigRect( -0x3FFFFFFF, -0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF );
+ if( rOutDev.GetConnectMetaFile() )
+ rOutDev.Push();
+ Point aStartPos( rStartPos );
+ if ( IsEffectivelyVertical() )
+ {
+ aStartPos.AdjustX(GetPaperSize().Width() );
+ rStartPos.RotateAround(aStartPos, nOrientation);
+ }
+ pImpEditEngine->Paint(rOutDev, aBigRect, aStartPos, false, nOrientation);
+ if( rOutDev.GetConnectMetaFile() )
+ rOutDev.Pop();
+}
+
+void EditEngine::Draw( OutputDevice& rOutDev, const tools::Rectangle& rOutRect, const Point& rStartDocPos )
+{
+ Draw( rOutDev, rOutRect, rStartDocPos, true );
+}
+
+void EditEngine::Draw( OutputDevice& rOutDev, const tools::Rectangle& rOutRect, const Point& rStartDocPos, bool bClip )
+{
+#if defined( DBG_UTIL ) || (OSL_DEBUG_LEVEL > 1)
+ if ( bDebugPaint )
+ DumpData(this, false);
+#endif
+
+ // Align to the pixel boundary, so that it becomes exactly the same
+ // as Paint ()
+ tools::Rectangle aOutRect( rOutDev.LogicToPixel( rOutRect ) );
+ aOutRect = rOutDev.PixelToLogic( aOutRect );
+
+ Point aStartPos;
+ if ( !IsEffectivelyVertical() )
+ {
+ aStartPos.setX( aOutRect.Left() - rStartDocPos.X() );
+ aStartPos.setY( aOutRect.Top() - rStartDocPos.Y() );
+ }
+ else
+ {
+ aStartPos.setX( aOutRect.Right() + rStartDocPos.Y() );
+ aStartPos.setY( aOutRect.Top() - rStartDocPos.X() );
+ }
+
+ bool bClipRegion = rOutDev.IsClipRegion();
+ bool bMetafile = rOutDev.GetConnectMetaFile();
+ vcl::Region aOldRegion = rOutDev.GetClipRegion();
+
+ // If one existed => intersection!
+ // Use Push/pop for creating the Meta file
+ if ( bMetafile )
+ rOutDev.Push();
+
+ // Always use the Intersect method, it is a must for Metafile!
+ if ( bClip )
+ {
+ // Clip only if necessary...
+ if ( rStartDocPos.X() || rStartDocPos.Y() ||
+ ( rOutRect.GetHeight() < static_cast<tools::Long>(GetTextHeight()) ) ||
+ ( rOutRect.GetWidth() < static_cast<tools::Long>(CalcTextWidth()) ) )
+ {
+ // Some printer drivers cause problems if characters graze the
+ // ClipRegion, therefore rather add a pixel more ...
+ tools::Rectangle aClipRect( aOutRect );
+ if ( rOutDev.GetOutDevType() == OUTDEV_PRINTER )
+ {
+ Size aPixSz( 1, 0 );
+ aPixSz = rOutDev.PixelToLogic( aPixSz );
+ aClipRect.AdjustRight(aPixSz.Width() );
+ aClipRect.AdjustBottom(aPixSz.Width() );
+ }
+ rOutDev.IntersectClipRegion( aClipRect );
+ }
+ }
+
+ pImpEditEngine->Paint( rOutDev, aOutRect, aStartPos );
+
+ if ( bMetafile )
+ rOutDev.Pop();
+ else if ( bClipRegion )
+ rOutDev.SetClipRegion( aOldRegion );
+ else
+ rOutDev.SetClipRegion();
+}
+
+void EditEngine::InsertView(EditView* pEditView, size_t nIndex)
+{
+
+ if ( nIndex > pImpEditEngine->GetEditViews().size() )
+ nIndex = pImpEditEngine->GetEditViews().size();
+
+ ImpEditEngine::ViewsType& rViews = pImpEditEngine->GetEditViews();
+ rViews.insert(rViews.begin()+nIndex, pEditView);
+
+ EditSelection aStartSel = pImpEditEngine->GetEditDoc().GetStartPaM();
+ pEditView->pImpEditView->SetEditSelection( aStartSel );
+ if ( !pImpEditEngine->GetActiveView() )
+ pImpEditEngine->SetActiveView( pEditView );
+
+ pEditView->pImpEditView->AddDragAndDropListeners();
+}
+
+EditView* EditEngine::RemoveView( EditView* pView )
+{
+
+ pView->HideCursor();
+ EditView* pRemoved = nullptr;
+ ImpEditEngine::ViewsType& rViews = pImpEditEngine->GetEditViews();
+ ImpEditEngine::ViewsType::iterator it = std::find(rViews.begin(), rViews.end(), pView);
+
+ DBG_ASSERT( it != rViews.end(), "RemoveView with invalid index" );
+ if (it != rViews.end())
+ {
+ pRemoved = *it;
+ rViews.erase(it);
+ if ( pImpEditEngine->GetActiveView() == pView )
+ {
+ pImpEditEngine->SetActiveView( nullptr );
+ pImpEditEngine->GetSelEngine().SetCurView( nullptr );
+ }
+ pView->pImpEditView->RemoveDragAndDropListeners();
+
+ }
+ return pRemoved;
+}
+
+void EditEngine::RemoveView(size_t nIndex)
+{
+ ImpEditEngine::ViewsType& rViews = pImpEditEngine->GetEditViews();
+ if (nIndex >= rViews.size())
+ return;
+
+ EditView* pView = rViews[nIndex];
+ if ( pView )
+ RemoveView( pView );
+}
+
+EditView* EditEngine::GetView(size_t nIndex) const
+{
+ return pImpEditEngine->GetEditViews()[nIndex];
+}
+
+size_t EditEngine::GetViewCount() const
+{
+ return pImpEditEngine->GetEditViews().size();
+}
+
+bool EditEngine::HasView( EditView* pView ) const
+{
+ ImpEditEngine::ViewsType& rViews = pImpEditEngine->GetEditViews();
+ return std::find(rViews.begin(), rViews.end(), pView) != rViews.end();
+}
+
+EditView* EditEngine::GetActiveView() const
+{
+ return pImpEditEngine->GetActiveView();
+}
+
+void EditEngine::SetActiveView(EditView* pView)
+{
+ pImpEditEngine->SetActiveView(pView);
+}
+
+void EditEngine::SetDefTab( sal_uInt16 nDefTab )
+{
+ pImpEditEngine->GetEditDoc().SetDefTab( nDefTab );
+ if ( pImpEditEngine->IsFormatted() )
+ {
+ pImpEditEngine->FormatFullDoc();
+ pImpEditEngine->UpdateViews();
+ }
+}
+
+void EditEngine::SetPaperSize( const Size& rNewSize )
+{
+
+ Size aOldSize( pImpEditEngine->GetPaperSize() );
+ pImpEditEngine->SetValidPaperSize( rNewSize );
+ Size aNewSize( pImpEditEngine->GetPaperSize() );
+
+ bool bAutoPageSize = pImpEditEngine->GetStatus().AutoPageSize();
+ if ( !(bAutoPageSize || ( aNewSize.Width() != aOldSize.Width() )) )
+ return;
+
+ for (EditView* pView : pImpEditEngine->aEditViews)
+ {
+ if ( bAutoPageSize )
+ pView->pImpEditView->RecalcOutputArea();
+ else if ( pView->pImpEditView->DoAutoSize() )
+ {
+ pView->pImpEditView->ResetOutputArea( tools::Rectangle(
+ pView->pImpEditView->GetOutputArea().TopLeft(), aNewSize ) );
+ }
+ }
+
+ if ( bAutoPageSize || pImpEditEngine->IsFormatted() )
+ {
+ // Changing the width has no effect for AutoPageSize, as this is
+ // determined by the text width.
+ // Optimization first after Vobis delivery was enabled ...
+ pImpEditEngine->FormatFullDoc();
+
+ pImpEditEngine->UpdateViews( pImpEditEngine->GetActiveView() );
+
+ if ( pImpEditEngine->IsUpdateLayout() && pImpEditEngine->GetActiveView() )
+ pImpEditEngine->pActiveView->ShowCursor( false, false );
+ }
+}
+
+const Size& EditEngine::GetPaperSize() const
+{
+ return pImpEditEngine->GetPaperSize();
+}
+
+void EditEngine::SetVertical(bool bVertical)
+{
+ pImpEditEngine->SetVertical(bVertical);
+}
+
+void EditEngine::SetRotation(TextRotation nRotation)
+{
+ pImpEditEngine->SetRotation(nRotation);
+}
+
+TextRotation EditEngine::GetRotation() const
+{
+ return pImpEditEngine->GetRotation();
+}
+
+bool EditEngine::IsEffectivelyVertical() const
+{
+ return pImpEditEngine->IsEffectivelyVertical();
+}
+
+bool EditEngine::IsTopToBottom() const
+{
+ return pImpEditEngine->IsTopToBottom();
+}
+
+bool EditEngine::GetVertical() const
+{
+ return pImpEditEngine->GetVertical();
+}
+
+void EditEngine::SetTextColumns(sal_Int16 nColumns, sal_Int32 nSpacing)
+{
+ pImpEditEngine->SetTextColumns(nColumns, nSpacing);
+}
+
+void EditEngine::SetFixedCellHeight( bool bUseFixedCellHeight )
+{
+ pImpEditEngine->SetFixedCellHeight( bUseFixedCellHeight );
+}
+
+void EditEngine::SetDefaultHorizontalTextDirection( EEHorizontalTextDirection eHTextDir )
+{
+ pImpEditEngine->SetDefaultHorizontalTextDirection( eHTextDir );
+}
+
+EEHorizontalTextDirection EditEngine::GetDefaultHorizontalTextDirection() const
+{
+ return pImpEditEngine->GetDefaultHorizontalTextDirection();
+}
+
+SvtScriptType EditEngine::GetScriptType( const ESelection& rSelection ) const
+{
+ EditSelection aSel( pImpEditEngine->CreateSel( rSelection ) );
+ return pImpEditEngine->GetItemScriptType( aSel );
+}
+
+editeng::LanguageSpan EditEngine::GetLanguage(const EditPaM& rPaM) const
+{
+ return pImpEditEngine->GetLanguage(rPaM);
+}
+
+editeng::LanguageSpan EditEngine::GetLanguage( sal_Int32 nPara, sal_Int32 nPos ) const
+{
+ ContentNode* pNode = pImpEditEngine->GetEditDoc().GetObject( nPara );
+ DBG_ASSERT( pNode, "GetLanguage - nPara is invalid!" );
+ return pNode ? pImpEditEngine->GetLanguage( EditPaM( pNode, nPos ) ) : editeng::LanguageSpan{};
+}
+
+
+void EditEngine::TransliterateText( const ESelection& rSelection, TransliterationFlags nTransliterationMode )
+{
+ pImpEditEngine->TransliterateText( pImpEditEngine->CreateSel( rSelection ), nTransliterationMode );
+}
+
+EditSelection EditEngine::TransliterateText(const EditSelection& rSelection, TransliterationFlags nTransliterationMode)
+{
+ return pImpEditEngine->TransliterateText(rSelection, nTransliterationMode);
+}
+
+void EditEngine::SetAsianCompressionMode( CharCompressType n )
+{
+ pImpEditEngine->SetAsianCompressionMode( n );
+}
+
+void EditEngine::SetKernAsianPunctuation( bool b )
+{
+ pImpEditEngine->SetKernAsianPunctuation( b );
+}
+
+void EditEngine::SetAddExtLeading( bool b )
+{
+ pImpEditEngine->SetAddExtLeading( b );
+}
+
+void EditEngine::SetPolygon( const basegfx::B2DPolyPolygon& rPolyPolygon )
+{
+ SetPolygon( rPolyPolygon, nullptr );
+}
+
+void EditEngine::SetPolygon(const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::B2DPolyPolygon* pLinePolyPolygon)
+{
+ bool bSimple(false);
+
+ if(pLinePolyPolygon && 1 == rPolyPolygon.count())
+ {
+ if(rPolyPolygon.getB2DPolygon(0).isClosed())
+ {
+ // open polygon
+ bSimple = true;
+ }
+ }
+
+ TextRanger* pRanger = new TextRanger( rPolyPolygon, pLinePolyPolygon, 30, 2, 2, bSimple, true );
+ pImpEditEngine->SetTextRanger( std::unique_ptr<TextRanger>(pRanger) );
+ pImpEditEngine->SetPaperSize( pRanger->GetBoundRect().GetSize() );
+}
+
+void EditEngine::ClearPolygon()
+{
+ pImpEditEngine->SetTextRanger( nullptr );
+}
+
+const Size& EditEngine::GetMinAutoPaperSize() const
+{
+ return pImpEditEngine->GetMinAutoPaperSize();
+}
+
+void EditEngine::SetMinAutoPaperSize( const Size& rSz )
+{
+ pImpEditEngine->SetMinAutoPaperSize( rSz );
+}
+
+const Size& EditEngine::GetMaxAutoPaperSize() const
+{
+ return pImpEditEngine->GetMaxAutoPaperSize();
+}
+
+void EditEngine::SetMaxAutoPaperSize( const Size& rSz )
+{
+ pImpEditEngine->SetMaxAutoPaperSize( rSz );
+}
+
+void EditEngine::SetMinColumnWrapHeight(tools::Long nVal)
+{
+ pImpEditEngine->SetMinColumnWrapHeight(nVal);
+}
+
+OUString EditEngine::GetText( LineEnd eEnd ) const
+{
+ return pImpEditEngine->GetEditDoc().GetText( eEnd );
+}
+
+OUString EditEngine::GetText( const ESelection& rESelection ) const
+{
+ EditSelection aSel( pImpEditEngine->CreateSel( rESelection ) );
+ return pImpEditEngine->GetSelected( aSel );
+}
+
+sal_Int32 EditEngine::GetTextLen() const
+{
+ return pImpEditEngine->GetEditDoc().GetTextLen();
+}
+
+sal_Int32 EditEngine::GetParagraphCount() const
+{
+ return pImpEditEngine->maEditDoc.Count();
+}
+
+sal_Int32 EditEngine::GetLineCount( sal_Int32 nParagraph ) const
+{
+ if ( !pImpEditEngine->IsFormatted() )
+ pImpEditEngine->FormatDoc();
+ return pImpEditEngine->GetLineCount( nParagraph );
+}
+
+sal_Int32 EditEngine::GetLineLen( sal_Int32 nParagraph, sal_Int32 nLine ) const
+{
+ if ( !pImpEditEngine->IsFormatted() )
+ pImpEditEngine->FormatDoc();
+ return pImpEditEngine->GetLineLen( nParagraph, nLine );
+}
+
+void EditEngine::GetLineBoundaries( /*out*/sal_Int32& rStart, /*out*/sal_Int32& rEnd, sal_Int32 nParagraph, sal_Int32 nLine ) const
+{
+ if ( !pImpEditEngine->IsFormatted() )
+ pImpEditEngine->FormatDoc();
+ return pImpEditEngine->GetLineBoundaries( rStart, rEnd, nParagraph, nLine );
+}
+
+sal_Int32 EditEngine::GetLineNumberAtIndex( sal_Int32 nPara, sal_Int32 nIndex ) const
+{
+ if ( !pImpEditEngine->IsFormatted() )
+ pImpEditEngine->FormatDoc();
+ return pImpEditEngine->GetLineNumberAtIndex( nPara, nIndex );
+}
+
+sal_uInt32 EditEngine::GetLineHeight( sal_Int32 nParagraph )
+{
+ // If someone calls GetLineHeight() with an empty Engine.
+ if ( !pImpEditEngine->IsFormatted() )
+ pImpEditEngine->FormatDoc();
+ return pImpEditEngine->GetLineHeight( nParagraph, 0 );
+}
+
+tools::Rectangle EditEngine::GetParaBounds( sal_Int32 nPara )
+{
+ if ( !pImpEditEngine->IsFormatted() )
+ pImpEditEngine->FormatDoc();
+
+ Point aPnt = GetDocPosTopLeft( nPara );
+
+ if( IsEffectivelyVertical() )
+ {
+ sal_Int32 nTextHeight = pImpEditEngine->GetTextHeight();
+ sal_Int32 nParaWidth = pImpEditEngine->CalcParaWidth( nPara, true );
+ sal_Int32 nParaHeight = pImpEditEngine->GetParaHeight( nPara );
+
+ return tools::Rectangle( nTextHeight - aPnt.Y() - nParaHeight, 0, nTextHeight - aPnt.Y(), nParaWidth );
+ }
+ else
+ {
+ sal_Int32 nParaWidth = pImpEditEngine->CalcParaWidth( nPara, true );
+ sal_Int32 nParaHeight = pImpEditEngine->GetParaHeight( nPara );
+
+ return tools::Rectangle( 0, aPnt.Y(), nParaWidth, aPnt.Y() + nParaHeight );
+ }
+}
+
+sal_uInt32 EditEngine::GetTextHeight( sal_Int32 nParagraph ) const
+{
+ if ( !pImpEditEngine->IsFormatted() )
+ pImpEditEngine->FormatDoc();
+
+ sal_uInt32 nHeight = pImpEditEngine->GetParaHeight( nParagraph );
+ return nHeight;
+}
+
+OUString EditEngine::GetWord( sal_Int32 nPara, sal_Int32 nIndex )
+{
+ ESelection aESel( nPara, nIndex, nPara, nIndex );
+ EditSelection aSel( pImpEditEngine->CreateSel( aESel ) );
+ aSel = pImpEditEngine->SelectWord( aSel );
+ return pImpEditEngine->GetSelected( aSel );
+}
+
+ESelection EditEngine::GetWord( const ESelection& rSelection, sal_uInt16 nWordType ) const
+{
+ // ImpEditEngine-Iteration-Methods should be const!
+ EditEngine* pE = const_cast<EditEngine*>(this);
+
+ EditSelection aSel( pE->pImpEditEngine->CreateSel( rSelection ) );
+ aSel = pE->pImpEditEngine->SelectWord( aSel, nWordType );
+ return pE->pImpEditEngine->CreateESel( aSel );
+}
+
+void EditEngine::CheckIdleFormatter()
+{
+ pImpEditEngine->CheckIdleFormatter();
+}
+
+bool EditEngine::IsIdleFormatterActive() const
+{
+ return pImpEditEngine->aIdleFormatter.IsActive();
+}
+
+ParaPortion* EditEngine::FindParaPortion(ContentNode const * pNode)
+{
+ return pImpEditEngine->FindParaPortion(pNode);
+}
+
+const ParaPortion* EditEngine::FindParaPortion(ContentNode const * pNode) const
+{
+ return pImpEditEngine->FindParaPortion(pNode);
+}
+
+const ParaPortion* EditEngine::GetPrevVisPortion(const ParaPortion* pCurPortion) const
+{
+ return pImpEditEngine->GetPrevVisPortion(pCurPortion);
+}
+
+SvtScriptType EditEngine::GetScriptType(const EditSelection& rSel) const
+{
+ return pImpEditEngine->GetItemScriptType(rSel);
+}
+
+void EditEngine::RemoveParaPortion(sal_Int32 nNode)
+{
+ pImpEditEngine->GetParaPortions().Remove(nNode);
+}
+
+void EditEngine::SetCallParaInsertedOrDeleted(bool b)
+{
+ pImpEditEngine->SetCallParaInsertedOrDeleted(b);
+}
+
+bool EditEngine::IsCallParaInsertedOrDeleted() const
+{
+ return pImpEditEngine->IsCallParaInsertedOrDeleted();
+}
+
+void EditEngine::AppendDeletedNodeInfo(DeletedNodeInfo* pInfo)
+{
+ pImpEditEngine->aDeletedNodes.push_back(std::unique_ptr<DeletedNodeInfo>(pInfo));
+}
+
+void EditEngine::UpdateSelections()
+{
+ pImpEditEngine->UpdateSelections();
+}
+
+void EditEngine::InsertContent(ContentNode* pNode, sal_Int32 nPos)
+{
+ pImpEditEngine->InsertContent(pNode, nPos);
+}
+
+EditPaM EditEngine::SplitContent(sal_Int32 nNode, sal_Int32 nSepPos)
+{
+ return pImpEditEngine->SplitContent(nNode, nSepPos);
+}
+
+EditPaM EditEngine::ConnectContents(sal_Int32 nLeftNode, bool bBackward)
+{
+ return pImpEditEngine->ConnectContents(nLeftNode, bBackward);
+}
+
+void EditEngine::InsertFeature(const EditSelection& rEditSelection, const SfxPoolItem& rItem)
+{
+ pImpEditEngine->ImpInsertFeature(rEditSelection, rItem);
+}
+
+EditSelection EditEngine::MoveParagraphs(const Range& rParagraphs, sal_Int32 nNewPos)
+{
+ return pImpEditEngine->MoveParagraphs(rParagraphs, nNewPos, nullptr);
+}
+
+void EditEngine::RemoveCharAttribs(sal_Int32 nPara, sal_uInt16 nWhich, bool bRemoveFeatures)
+{
+ pImpEditEngine->RemoveCharAttribs(nPara, nWhich, bRemoveFeatures);
+}
+
+void EditEngine::RemoveCharAttribs(const EditSelection& rSel, bool bRemoveParaAttribs, sal_uInt16 nWhich)
+{
+ const EERemoveParaAttribsMode eMode = bRemoveParaAttribs?
+ EERemoveParaAttribsMode::RemoveAll :
+ EERemoveParaAttribsMode::RemoveCharItems;
+ pImpEditEngine->RemoveCharAttribs(rSel, eMode, nWhich);
+}
+
+void EditEngine::RemoveCharAttribs(const EditSelection& rSel, EERemoveParaAttribsMode eMode, sal_uInt16 nWhich)
+{
+ pImpEditEngine->RemoveCharAttribs(rSel, eMode, nWhich);
+}
+
+EditEngine::ViewsType& EditEngine::GetEditViews()
+{
+ return pImpEditEngine->GetEditViews();
+}
+
+const EditEngine::ViewsType& EditEngine::GetEditViews() const
+{
+ return pImpEditEngine->GetEditViews();
+}
+
+void EditEngine::SetUndoMode(bool b)
+{
+ pImpEditEngine->SetUndoMode(b);
+}
+
+void EditEngine::FormatAndLayout(EditView* pCurView, bool bCalledFromUndo)
+{
+ pImpEditEngine->FormatAndLayout(pCurView, bCalledFromUndo);
+}
+
+void EditEngine::Undo(EditView* pView)
+{
+ pImpEditEngine->Undo(pView);
+}
+
+void EditEngine::Redo(EditView* pView)
+{
+ pImpEditEngine->Redo(pView);
+}
+
+uno::Reference<datatransfer::XTransferable> EditEngine::CreateTransferable(const EditSelection& rSelection)
+{
+ return pImpEditEngine->CreateTransferable(rSelection);
+}
+
+void EditEngine::ParaAttribsToCharAttribs(ContentNode* pNode)
+{
+ pImpEditEngine->ParaAttribsToCharAttribs(pNode);
+}
+
+EditPaM EditEngine::CreateEditPaM(const EPaM& rEPaM)
+{
+ return pImpEditEngine->CreateEditPaM(rEPaM);
+}
+
+EditPaM EditEngine::ConnectParagraphs(
+ ContentNode* pLeft, ContentNode* pRight, bool bBackward)
+{
+ return pImpEditEngine->ImpConnectParagraphs(pLeft, pRight, bBackward);
+}
+
+EditPaM EditEngine::InsertField(const EditSelection& rEditSelection, const SvxFieldItem& rFld)
+{
+ return pImpEditEngine->InsertField(rEditSelection, rFld);
+}
+
+EditPaM EditEngine::InsertText(const EditSelection& aCurEditSelection, const OUString& rStr)
+{
+ return pImpEditEngine->InsertText(aCurEditSelection, rStr);
+}
+
+EditSelection EditEngine::InsertText(const EditTextObject& rTextObject, const EditSelection& rSel)
+{
+ return pImpEditEngine->InsertText(rTextObject, rSel);
+}
+
+EditSelection EditEngine::InsertText(
+ uno::Reference<datatransfer::XTransferable > const & rxDataObj,
+ const OUString& rBaseURL, const EditPaM& rPaM, bool bUseSpecial, SotClipboardFormatId format)
+{
+ return pImpEditEngine->PasteText(rxDataObj, rBaseURL, rPaM, bUseSpecial, format);
+}
+
+EditPaM EditEngine::EndOfWord(const EditPaM& rPaM)
+{
+ return pImpEditEngine->EndOfWord(rPaM);
+}
+
+EditPaM EditEngine::GetPaM(const Point& aDocPos, bool bSmart)
+{
+ return pImpEditEngine->GetPaM(aDocPos, bSmart);
+}
+
+EditSelection EditEngine::SelectWord(
+ const EditSelection& rCurSelection, sal_Int16 nWordType)
+{
+ return pImpEditEngine->SelectWord(rCurSelection, nWordType);
+}
+
+tools::Long EditEngine::GetXPos(
+ const ParaPortion* pParaPortion, const EditLine* pLine, sal_Int32 nIndex, bool bPreferPortionStart) const
+{
+ return pImpEditEngine->GetXPos(pParaPortion, pLine, nIndex, bPreferPortionStart);
+}
+
+Range EditEngine::GetLineXPosStartEnd(
+ const ParaPortion* pParaPortion, const EditLine* pLine) const
+{
+ return pImpEditEngine->GetLineXPosStartEnd(pParaPortion, pLine);
+}
+
+bool EditEngine::IsFormatted() const
+{
+ return pImpEditEngine->IsFormatted();
+}
+
+EditPaM EditEngine::CursorLeft(const EditPaM& rPaM, sal_uInt16 nCharacterIteratorMode)
+{
+ return pImpEditEngine->CursorLeft(rPaM, nCharacterIteratorMode);
+}
+
+EditPaM EditEngine::CursorRight(const EditPaM& rPaM, sal_uInt16 nCharacterIteratorMode)
+{
+ return pImpEditEngine->CursorRight(rPaM, nCharacterIteratorMode);
+}
+
+InternalEditStatus& EditEngine::GetInternalEditStatus()
+{
+ return pImpEditEngine->GetStatus();
+}
+
+EditDoc& EditEngine::GetEditDoc()
+{
+ return pImpEditEngine->GetEditDoc();
+}
+
+const EditDoc& EditEngine::GetEditDoc() const
+{
+ return pImpEditEngine->GetEditDoc();
+}
+
+void EditEngine::dumpAsXmlEditDoc(xmlTextWriterPtr pWriter) const
+{
+ pImpEditEngine->GetEditDoc().dumpAsXml(pWriter);
+}
+
+ParaPortionList& EditEngine::GetParaPortions()
+{
+ return pImpEditEngine->GetParaPortions();
+}
+
+const ParaPortionList& EditEngine::GetParaPortions() const
+{
+ return pImpEditEngine->GetParaPortions();
+}
+
+void EditEngine::SeekCursor(ContentNode* pNode, sal_Int32 nPos, SvxFont& rFont)
+{
+ pImpEditEngine->SeekCursor(pNode, nPos, rFont);
+}
+
+EditPaM EditEngine::DeleteSelection(const EditSelection& rSel)
+{
+ return pImpEditEngine->ImpDeleteSelection(rSel);
+}
+
+ESelection EditEngine::CreateESelection(const EditSelection& rSel) const
+{
+ return pImpEditEngine->CreateESel(rSel);
+}
+
+EditSelection EditEngine::CreateSelection(const ESelection& rSel)
+{
+ return pImpEditEngine->CreateSel(rSel);
+}
+
+const SfxItemSet& EditEngine::GetBaseParaAttribs(sal_Int32 nPara) const
+{
+ return pImpEditEngine->GetParaAttribs(nPara);
+}
+
+void EditEngine::SetParaAttribsOnly(sal_Int32 nPara, const SfxItemSet& rSet)
+{
+ pImpEditEngine->SetParaAttribs(nPara, rSet);
+}
+
+void EditEngine::SetAttribs(const EditSelection& rSel, const SfxItemSet& rSet, SetAttribsMode nSpecial)
+{
+ pImpEditEngine->SetAttribs(rSel, rSet, nSpecial);
+}
+
+OUString EditEngine::GetSelected(const EditSelection& rSel) const
+{
+ return pImpEditEngine->GetSelected(rSel);
+}
+
+EditPaM EditEngine::DeleteSelected(const EditSelection& rSel)
+{
+ return pImpEditEngine->DeleteSelected(rSel);
+}
+
+void EditEngine::HandleBeginPasteOrDrop(PasteOrDropInfos& rInfos)
+{
+ pImpEditEngine->aBeginPasteOrDropHdl.Call(rInfos);
+}
+
+void EditEngine::HandleEndPasteOrDrop(PasteOrDropInfos& rInfos)
+{
+ pImpEditEngine->aEndPasteOrDropHdl.Call(rInfos);
+}
+
+bool EditEngine::HasText() const
+{
+ return pImpEditEngine->ImplHasText();
+}
+
+const EditSelectionEngine& EditEngine::GetSelectionEngine() const
+{
+ return pImpEditEngine->aSelEngine;
+}
+
+void EditEngine::SetInSelectionMode(bool b)
+{
+ pImpEditEngine->mbInSelection = b;
+}
+
+bool EditEngine::PostKeyEvent( const KeyEvent& rKeyEvent, EditView* pEditView, vcl::Window const * pFrameWin )
+{
+ DBG_ASSERT( pEditView, "no View - no cookie !" );
+
+ bool bDone = true;
+
+ bool bModified = false;
+ bool bMoved = false;
+ bool bAllowIdle = true;
+ bool bReadOnly = pEditView->IsReadOnly();
+
+ GetCursorFlags nNewCursorFlags = GetCursorFlags::NONE;
+ bool bSetCursorFlags = true;
+
+ EditSelection aCurSel( pEditView->pImpEditView->GetEditSelection() );
+ DBG_ASSERT( !aCurSel.IsInvalid(), "Blinde Selection in EditEngine::PostKeyEvent" );
+
+ OUString aAutoText( pImpEditEngine->GetAutoCompleteText() );
+ if (!pImpEditEngine->GetAutoCompleteText().isEmpty())
+ pImpEditEngine->SetAutoCompleteText(OUString(), true);
+
+ sal_uInt16 nCode = rKeyEvent.GetKeyCode().GetCode();
+ KeyFuncType eFunc = rKeyEvent.GetKeyCode().GetFunction();
+ if ( eFunc != KeyFuncType::DONTKNOW )
+ {
+ switch ( eFunc )
+ {
+ case KeyFuncType::UNDO:
+ {
+ if ( !bReadOnly )
+ pEditView->Undo();
+ return true;
+ }
+ case KeyFuncType::REDO:
+ {
+ if ( !bReadOnly )
+ pEditView->Redo();
+ return true;
+ }
+
+ default: // is then possible edited below.
+ eFunc = KeyFuncType::DONTKNOW;
+ }
+ }
+
+ if ( eFunc == KeyFuncType::DONTKNOW )
+ {
+ switch ( nCode )
+ {
+#if defined( DBG_UTIL ) || (OSL_DEBUG_LEVEL > 1)
+ case KEY_F1:
+ {
+ if ( rKeyEvent.GetKeyCode().IsMod1() && rKeyEvent.GetKeyCode().IsMod2() )
+ {
+ sal_Int32 nParas = GetParagraphCount();
+ Point aPos;
+ Point aViewStart( pEditView->GetOutputArea().TopLeft() );
+ tools::Long n20 = 40 * pImpEditEngine->nOnePixelInRef;
+ for ( sal_Int32 n = 0; n < nParas; n++ )
+ {
+ tools::Long nH = GetTextHeight( n );
+ Point P1( aViewStart.X() + n20 + n20*(n%2), aViewStart.Y() + aPos.Y() );
+ Point P2( P1 );
+ P2.AdjustX(n20 );
+ P2.AdjustY(nH );
+ pEditView->GetWindow()->GetOutDev()->SetLineColor();
+ pEditView->GetWindow()->GetOutDev()->SetFillColor( (n%2) ? COL_YELLOW : COL_LIGHTGREEN );
+ pEditView->GetWindow()->GetOutDev()->DrawRect( tools::Rectangle( P1, P2 ) );
+ aPos.AdjustY(nH );
+ }
+ }
+ bDone = false;
+ }
+ break;
+ case KEY_F11:
+ {
+ if ( rKeyEvent.GetKeyCode().IsMod1() && rKeyEvent.GetKeyCode().IsMod2() )
+ {
+ bDebugPaint = !bDebugPaint;
+ OStringBuffer aInfo("DebugPaint: ");
+ aInfo.append(bDebugPaint ? "On" : "Off");
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pEditView->GetWindow()->GetFrameWeld(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ OStringToOUString(aInfo, RTL_TEXTENCODING_ASCII_US)));
+ xInfoBox->run();
+
+ }
+ bDone = false;
+ }
+ break;
+ case KEY_F12:
+ {
+ if ( rKeyEvent.GetKeyCode().IsMod1() && rKeyEvent.GetKeyCode().IsMod2() )
+ DumpData(this, true);
+ bDone = false;
+ }
+ break;
+#endif
+ case KEY_UP:
+ case KEY_DOWN:
+ case KEY_LEFT:
+ case KEY_RIGHT:
+ case KEY_HOME:
+ case KEY_END:
+ case KEY_PAGEUP:
+ case KEY_PAGEDOWN:
+ case css::awt::Key::MOVE_WORD_FORWARD:
+ case css::awt::Key::SELECT_WORD_FORWARD:
+ case css::awt::Key::MOVE_WORD_BACKWARD:
+ case css::awt::Key::SELECT_WORD_BACKWARD:
+ case css::awt::Key::MOVE_TO_BEGIN_OF_LINE:
+ case css::awt::Key::MOVE_TO_END_OF_LINE:
+ case css::awt::Key::SELECT_TO_BEGIN_OF_LINE:
+ case css::awt::Key::SELECT_TO_END_OF_LINE:
+ case css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH:
+ case css::awt::Key::MOVE_TO_END_OF_PARAGRAPH:
+ case css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH:
+ case css::awt::Key::SELECT_TO_END_OF_PARAGRAPH:
+ case css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT:
+ case css::awt::Key::MOVE_TO_END_OF_DOCUMENT:
+ case css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT:
+ case css::awt::Key::SELECT_TO_END_OF_DOCUMENT:
+ {
+ if ( !rKeyEvent.GetKeyCode().IsMod2() || ( nCode == KEY_LEFT ) || ( nCode == KEY_RIGHT ) )
+ {
+ if ( ImpEditEngine::DoVisualCursorTraveling() && ( ( nCode == KEY_LEFT ) || ( nCode == KEY_RIGHT ) /* || ( nCode == KEY_HOME ) || ( nCode == KEY_END ) */ ) )
+ bSetCursorFlags = false; // Will be manipulated within visual cursor move
+
+ aCurSel = pImpEditEngine->MoveCursor( rKeyEvent, pEditView );
+
+ if ( aCurSel.HasRange() ) {
+ Reference<css::datatransfer::clipboard::XClipboard> aSelection(GetSystemPrimarySelection());
+ pEditView->pImpEditView->CutCopy( aSelection, false );
+ }
+
+ bMoved = true;
+ if ( nCode == KEY_HOME )
+ nNewCursorFlags |= GetCursorFlags::StartOfLine;
+ else if ( nCode == KEY_END )
+ nNewCursorFlags |= GetCursorFlags::EndOfLine;
+
+ }
+#if OSL_DEBUG_LEVEL > 1
+ GetLanguage( pImpEditEngine->GetEditDoc().GetPos( aCurSel.Max().GetNode() ), aCurSel.Max().GetIndex() );
+#endif
+ }
+ break;
+ case KEY_BACKSPACE:
+ case KEY_DELETE:
+ case css::awt::Key::DELETE_WORD_BACKWARD:
+ case css::awt::Key::DELETE_WORD_FORWARD:
+ case css::awt::Key::DELETE_TO_BEGIN_OF_PARAGRAPH:
+ case css::awt::Key::DELETE_TO_END_OF_PARAGRAPH:
+ {
+ if ( !bReadOnly && !rKeyEvent.GetKeyCode().IsMod2() )
+ {
+ // check if we are behind a bullet and using the backspace key
+ ContentNode *pNode = aCurSel.Min().GetNode();
+ const SvxNumberFormat *pFmt = pImpEditEngine->GetNumberFormat( pNode );
+ if (pFmt && nCode == KEY_BACKSPACE &&
+ !aCurSel.HasRange() && aCurSel.Min().GetIndex() == 0)
+ {
+ // if the bullet is still visible, just make it invisible.
+ // Otherwise continue as usual.
+
+
+ sal_Int32 nPara = pImpEditEngine->GetEditDoc().GetPos( pNode );
+ SfxBoolItem aBulletState( pImpEditEngine->GetParaAttrib( nPara, EE_PARA_BULLETSTATE ) );
+
+ if ( aBulletState.GetValue() )
+ {
+
+ aBulletState.SetValue( false );
+ SfxItemSet aSet( pImpEditEngine->GetParaAttribs( nPara ) );
+ aSet.Put( aBulletState );
+ pImpEditEngine->SetParaAttribs( nPara, aSet );
+
+ // have this and the following paragraphs formatted and repainted.
+ // (not painting a numbering in the list may cause the following
+ // numberings to have different numbers than before and thus the
+ // length may have changed as well )
+ pImpEditEngine->FormatAndLayout( pImpEditEngine->GetActiveView() );
+
+ break;
+ }
+ }
+
+ sal_uInt8 nDel = 0;
+ DeleteMode nMode = DeleteMode::Simple;
+ switch( nCode )
+ {
+ case css::awt::Key::DELETE_WORD_BACKWARD:
+ nMode = DeleteMode::RestOfWord;
+ nDel = DEL_LEFT;
+ break;
+ case css::awt::Key::DELETE_WORD_FORWARD:
+ nMode = DeleteMode::RestOfWord;
+ nDel = DEL_RIGHT;
+ break;
+ case css::awt::Key::DELETE_TO_BEGIN_OF_PARAGRAPH:
+ nMode = DeleteMode::RestOfContent;
+ nDel = DEL_LEFT;
+ break;
+ case css::awt::Key::DELETE_TO_END_OF_PARAGRAPH:
+ nMode = DeleteMode::RestOfContent;
+ nDel = DEL_RIGHT;
+ break;
+ default:
+ nDel = ( nCode == KEY_DELETE ) ? DEL_RIGHT : DEL_LEFT;
+ nMode = rKeyEvent.GetKeyCode().IsMod1() ? DeleteMode::RestOfWord : DeleteMode::Simple;
+ if ( ( nMode == DeleteMode::RestOfWord ) && rKeyEvent.GetKeyCode().IsShift() )
+ nMode = DeleteMode::RestOfContent;
+ break;
+ }
+
+ pEditView->pImpEditView->DrawSelectionXOR();
+ pImpEditEngine->UndoActionStart( EDITUNDO_DELETE );
+ aCurSel = pImpEditEngine->DeleteLeftOrRight( aCurSel, nDel, nMode );
+ pImpEditEngine->UndoActionEnd();
+ bModified = true;
+ bAllowIdle = false;
+ }
+ }
+ break;
+ case KEY_TAB:
+ {
+ if ( !bReadOnly && !rKeyEvent.GetKeyCode().IsMod1() && !rKeyEvent.GetKeyCode().IsMod2() )
+ {
+ bool bShift = rKeyEvent.GetKeyCode().IsShift();
+ if ( !bShift )
+ {
+ bool bSel = pEditView->HasSelection();
+ if ( bSel )
+ pImpEditEngine->UndoActionStart( EDITUNDO_INSERT );
+ if ( pImpEditEngine->GetStatus().DoAutoCorrect() )
+ aCurSel = pImpEditEngine->AutoCorrect( aCurSel, 0, !pEditView->IsInsertMode(), pFrameWin );
+ aCurSel = pImpEditEngine->InsertTab( aCurSel );
+ if ( bSel )
+ pImpEditEngine->UndoActionEnd();
+ bModified = true;
+ }
+ }
+ else
+ bDone = false;
+ }
+ break;
+ case KEY_RETURN:
+ {
+ if ( !bReadOnly )
+ {
+ pEditView->pImpEditView->DrawSelectionXOR();
+ if ( !rKeyEvent.GetKeyCode().IsMod1() && !rKeyEvent.GetKeyCode().IsMod2() )
+ {
+ pImpEditEngine->UndoActionStart( EDITUNDO_INSERT );
+ if ( rKeyEvent.GetKeyCode().IsShift() )
+ {
+ aCurSel = pImpEditEngine->AutoCorrect( aCurSel, 0, !pEditView->IsInsertMode(), pFrameWin );
+ aCurSel = pImpEditEngine->InsertLineBreak( aCurSel );
+ }
+ else
+ {
+ if (aAutoText.isEmpty())
+ {
+ if ( pImpEditEngine->GetStatus().DoAutoCorrect() )
+ aCurSel = pImpEditEngine->AutoCorrect( aCurSel, 0, !pEditView->IsInsertMode(), pFrameWin );
+ aCurSel = pImpEditEngine->InsertParaBreak( aCurSel );
+ }
+ else
+ {
+ DBG_ASSERT( !aCurSel.HasRange(), "Selection on complete?!" );
+ EditPaM aStart( pImpEditEngine->WordLeft( aCurSel.Max() ) );
+ aCurSel = pImpEditEngine->InsertText(
+ EditSelection( aStart, aCurSel.Max() ), aAutoText );
+ pImpEditEngine->SetAutoCompleteText( OUString(), true );
+ }
+ }
+ pImpEditEngine->UndoActionEnd();
+ bModified = true;
+ }
+ }
+ }
+ break;
+ case KEY_INSERT:
+ {
+ if ( !rKeyEvent.GetKeyCode().IsMod1() && !rKeyEvent.GetKeyCode().IsMod2() )
+ pEditView->SetInsertMode( !pEditView->IsInsertMode() );
+ }
+ break;
+ default:
+ {
+ #if (OSL_DEBUG_LEVEL > 1) && defined(DBG_UTIL)
+ if ( ( nCode == KEY_W ) && rKeyEvent.GetKeyCode().IsMod1() && rKeyEvent.GetKeyCode().IsMod2() )
+ {
+ SfxItemSet aAttribs = pEditView->GetAttribs();
+ const SvxFrameDirectionItem& rCurrentWritingMode = (const SvxFrameDirectionItem&)aAttribs.Get( EE_PARA_WRITINGDIR );
+ SvxFrameDirectionItem aNewItem( SvxFrameDirection::Horizontal_LR_TB, EE_PARA_WRITINGDIR );
+ if ( rCurrentWritingMode.GetValue() != SvxFrameDirection::Horizontal_RL_TB )
+ aNewItem.SetValue( SvxFrameDirection::Horizontal_RL_TB );
+ aAttribs.Put( aNewItem );
+ pEditView->SetAttribs( aAttribs );
+ }
+ #endif
+ if ( !bReadOnly && IsSimpleCharInput( rKeyEvent ) )
+ {
+ sal_Unicode nCharCode = rKeyEvent.GetCharCode();
+ pEditView->pImpEditView->DrawSelectionXOR();
+ // Autocorrection?
+ if ( ( pImpEditEngine->GetStatus().DoAutoCorrect() ) &&
+ ( SvxAutoCorrect::IsAutoCorrectChar( nCharCode ) ||
+ pImpEditEngine->IsNbspRunNext() ) )
+ {
+ aCurSel = pImpEditEngine->AutoCorrect(
+ aCurSel, nCharCode, !pEditView->IsInsertMode(), pFrameWin );
+ }
+ else
+ {
+ aCurSel = pImpEditEngine->InsertTextUserInput( aCurSel, nCharCode, !pEditView->IsInsertMode() );
+ }
+ // AutoComplete ???
+ if ( pImpEditEngine->GetStatus().DoAutoComplete() && ( nCharCode != ' ' ) )
+ {
+ // Only at end of word...
+ sal_Int32 nIndex = aCurSel.Max().GetIndex();
+ if ( ( nIndex >= aCurSel.Max().GetNode()->Len() ) ||
+ ( pImpEditEngine->aWordDelimiters.indexOf( aCurSel.Max().GetNode()->GetChar( nIndex ) ) != -1 ) )
+ {
+ EditPaM aStart( pImpEditEngine->WordLeft( aCurSel.Max() ) );
+ OUString aWord = pImpEditEngine->GetSelected( EditSelection( aStart, aCurSel.Max() ) );
+ if ( aWord.getLength() >= 3 )
+ {
+ OUString aComplete;
+
+ LanguageType eLang = pImpEditEngine->GetLanguage( EditPaM( aStart.GetNode(), aStart.GetIndex()+1)).nLang;
+ LanguageTag aLanguageTag( eLang);
+
+ if (!pImpEditEngine->xLocaleDataWrapper.isInitialized())
+ pImpEditEngine->xLocaleDataWrapper.init( SvtSysLocale().GetLocaleData().getComponentContext(), aLanguageTag);
+ else
+ pImpEditEngine->xLocaleDataWrapper.changeLocale( aLanguageTag);
+
+ if (!pImpEditEngine->xTransliterationWrapper.isInitialized())
+ pImpEditEngine->xTransliterationWrapper.init( SvtSysLocale().GetLocaleData().getComponentContext(), eLang);
+ else
+ pImpEditEngine->xTransliterationWrapper.changeLocale( eLang);
+
+ const ::utl::TransliterationWrapper* pTransliteration = pImpEditEngine->xTransliterationWrapper.get();
+ Sequence< i18n::CalendarItem2 > xItem = pImpEditEngine->xLocaleDataWrapper->getDefaultCalendarDays();
+ sal_Int32 nCount = xItem.getLength();
+ const i18n::CalendarItem2* pArr = xItem.getConstArray();
+ for( sal_Int32 n = 0; n <= nCount; ++n )
+ {
+ const OUString& rDay = pArr[n].FullName;
+ if( pTransliteration->isMatch( aWord, rDay) )
+ {
+ aComplete = rDay;
+ break;
+ }
+ }
+
+ if ( aComplete.isEmpty() )
+ {
+ xItem = pImpEditEngine->xLocaleDataWrapper->getDefaultCalendarMonths();
+ sal_Int32 nMonthCount = xItem.getLength();
+ const i18n::CalendarItem2* pMonthArr = xItem.getConstArray();
+ for( sal_Int32 n = 0; n <= nMonthCount; ++n )
+ {
+ const OUString& rMon = pMonthArr[n].FullName;
+ if( pTransliteration->isMatch( aWord, rMon) )
+ {
+ aComplete = rMon;
+ break;
+ }
+ }
+ }
+
+ if( !aComplete.isEmpty() && ( ( aWord.getLength() + 1 ) < aComplete.getLength() ) )
+ {
+ pImpEditEngine->SetAutoCompleteText( aComplete, false );
+ Point aPos = pImpEditEngine->PaMtoEditCursor( aCurSel.Max() ).TopLeft();
+ aPos = pEditView->pImpEditView->GetWindowPos( aPos );
+ aPos = pEditView->pImpEditView->GetWindow()->LogicToPixel( aPos );
+ aPos = pEditView->GetWindow()->OutputToScreenPixel( aPos );
+ aPos.AdjustY( -3 );
+ Help::ShowQuickHelp( pEditView->GetWindow(), tools::Rectangle( aPos, Size( 1, 1 ) ), aComplete, QuickHelpFlags::Bottom|QuickHelpFlags::Left );
+ }
+ }
+ }
+ }
+ bModified = true;
+ }
+ else
+ bDone = false;
+ }
+ }
+ }
+
+ pEditView->pImpEditView->SetEditSelection( aCurSel );
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ pEditView->pImpEditView->DrawSelectionXOR();
+ }
+ pImpEditEngine->UpdateSelections();
+
+ if ( ( !IsEffectivelyVertical() && ( nCode != KEY_UP ) && ( nCode != KEY_DOWN ) ) ||
+ ( IsEffectivelyVertical() && ( nCode != KEY_LEFT ) && ( nCode != KEY_RIGHT ) ))
+ {
+ pEditView->pImpEditView->nTravelXPos = TRAVEL_X_DONTKNOW;
+ }
+
+ if ( /* ( nCode != KEY_HOME ) && ( nCode != KEY_END ) && */
+ ( !IsEffectivelyVertical() && ( nCode != KEY_LEFT ) && ( nCode != KEY_RIGHT ) ) ||
+ ( IsEffectivelyVertical() && ( nCode != KEY_UP ) && ( nCode != KEY_DOWN ) ))
+ {
+ pEditView->pImpEditView->SetCursorBidiLevel( CURSOR_BIDILEVEL_DONTKNOW );
+ }
+
+ if ( bSetCursorFlags )
+ pEditView->pImpEditView->nExtraCursorFlags = nNewCursorFlags;
+
+ if ( bModified )
+ {
+ DBG_ASSERT( !bReadOnly, "ReadOnly but modified???" );
+ // Idle-Formatter only when AnyInput.
+ if ( bAllowIdle && pImpEditEngine->GetStatus().UseIdleFormatter()
+ && Application::AnyInput( VclInputFlags::KEYBOARD) )
+ pImpEditEngine->IdleFormatAndLayout( pEditView );
+ else
+ pImpEditEngine->FormatAndLayout( pEditView );
+ }
+ else if ( bMoved )
+ {
+ bool bGotoCursor = pEditView->pImpEditView->DoAutoScroll();
+ pEditView->pImpEditView->ShowCursor( bGotoCursor, true );
+ pImpEditEngine->CallStatusHdl();
+ }
+
+ return bDone;
+}
+
+sal_uInt32 EditEngine::GetTextHeight() const
+{
+
+ if ( !pImpEditEngine->IsFormatted() )
+ pImpEditEngine->FormatDoc();
+
+ sal_uInt32 nHeight = !IsEffectivelyVertical() ? pImpEditEngine->GetTextHeight() : pImpEditEngine->CalcTextWidth( true );
+ return nHeight;
+}
+
+sal_uInt32 EditEngine::GetTextHeightNTP() const
+{
+
+ if ( !pImpEditEngine->IsFormatted() )
+ pImpEditEngine->FormatDoc();
+
+ if ( IsEffectivelyVertical() )
+ return pImpEditEngine->CalcTextWidth( true );
+
+ return pImpEditEngine->GetTextHeightNTP();
+}
+
+sal_uInt32 EditEngine::CalcTextWidth()
+{
+
+ if ( !pImpEditEngine->IsFormatted() )
+ pImpEditEngine->FormatDoc();
+
+ sal_uInt32 nWidth = !IsEffectivelyVertical() ? pImpEditEngine->CalcTextWidth( true ) : pImpEditEngine->GetTextHeight();
+ return nWidth;
+}
+
+bool EditEngine::SetUpdateLayout(bool bUpdate, bool bRestoring)
+{
+ bool bPrevUpdateLayout = pImpEditEngine->SetUpdateLayout( bUpdate );
+ if (pImpEditEngine->pActiveView)
+ {
+ // Not an activation if we are restoring the previous update mode.
+ pImpEditEngine->pActiveView->ShowCursor(false, false, /*bActivate=*/!bRestoring);
+ }
+ return bPrevUpdateLayout;
+}
+
+bool EditEngine::IsUpdateLayout() const
+{
+ return pImpEditEngine->IsUpdateLayout();
+}
+
+void EditEngine::Clear()
+{
+ pImpEditEngine->Clear();
+}
+
+void EditEngine::SetText( const OUString& rText )
+{
+ pImpEditEngine->SetText( rText );
+ if ( !rText.isEmpty() && pImpEditEngine->IsUpdateLayout() )
+ pImpEditEngine->FormatAndLayout();
+}
+
+ErrCode EditEngine::Read( SvStream& rInput, const OUString& rBaseURL, EETextFormat eFormat, SvKeyValueIterator* pHTTPHeaderAttrs /* = NULL */ )
+{
+ bool bUndoEnabled = pImpEditEngine->IsUndoEnabled();
+ pImpEditEngine->EnableUndo( false );
+ pImpEditEngine->SetText( OUString() );
+ EditPaM aPaM( pImpEditEngine->GetEditDoc().GetStartPaM() );
+ pImpEditEngine->Read( rInput, rBaseURL, eFormat, EditSelection( aPaM, aPaM ), pHTTPHeaderAttrs );
+ pImpEditEngine->EnableUndo( bUndoEnabled );
+ return rInput.GetError();
+}
+
+void EditEngine::Write( SvStream& rOutput, EETextFormat eFormat )
+{
+ EditPaM aStartPaM( pImpEditEngine->GetEditDoc().GetStartPaM() );
+ EditPaM aEndPaM( pImpEditEngine->GetEditDoc().GetEndPaM() );
+ pImpEditEngine->Write( rOutput, eFormat, EditSelection( aStartPaM, aEndPaM ) );
+}
+
+std::unique_ptr<EditTextObject> EditEngine::CreateTextObject()
+{
+ return pImpEditEngine->CreateTextObject();
+}
+
+std::unique_ptr<EditTextObject> EditEngine::CreateTextObject( const ESelection& rESelection )
+{
+ EditSelection aSel( pImpEditEngine->CreateSel( rESelection ) );
+ return pImpEditEngine->CreateTextObject( aSel );
+}
+
+std::unique_ptr<EditTextObject> EditEngine::GetEmptyTextObject() const
+{
+ return pImpEditEngine->GetEmptyTextObject();
+}
+
+
+void EditEngine::SetText( const EditTextObject& rTextObject )
+{
+ pImpEditEngine->SetText( rTextObject );
+ pImpEditEngine->FormatAndLayout();
+}
+
+void EditEngine::ShowParagraph( sal_Int32 nParagraph, bool bShow )
+{
+ pImpEditEngine->ShowParagraph( nParagraph, bShow );
+}
+
+void EditEngine::SetNotifyHdl( const Link<EENotify&,void>& rLink )
+{
+ pImpEditEngine->SetNotifyHdl( rLink );
+}
+
+Link<EENotify&,void> const & EditEngine::GetNotifyHdl() const
+{
+ return pImpEditEngine->GetNotifyHdl();
+}
+
+void EditEngine::SetStatusEventHdl( const Link<EditStatus&, void>& rLink )
+{
+ pImpEditEngine->SetStatusEventHdl( rLink );
+}
+
+Link<EditStatus&, void> const & EditEngine::GetStatusEventHdl() const
+{
+ return pImpEditEngine->GetStatusEventHdl();
+}
+
+void EditEngine::SetHtmlImportHdl( const Link<HtmlImportInfo&,void>& rLink )
+{
+ pImpEditEngine->aHtmlImportHdl = rLink;
+}
+
+const Link<HtmlImportInfo&,void>& EditEngine::GetHtmlImportHdl() const
+{
+ return pImpEditEngine->aHtmlImportHdl;
+}
+
+void EditEngine::SetRtfImportHdl( const Link<RtfImportInfo&,void>& rLink )
+{
+ pImpEditEngine->aRtfImportHdl = rLink;
+}
+
+const Link<RtfImportInfo&,void>& EditEngine::GetRtfImportHdl() const
+{
+ return pImpEditEngine->aRtfImportHdl;
+}
+
+void EditEngine::SetBeginMovingParagraphsHdl( const Link<MoveParagraphsInfo&,void>& rLink )
+{
+ pImpEditEngine->aBeginMovingParagraphsHdl = rLink;
+}
+
+void EditEngine::SetEndMovingParagraphsHdl( const Link<MoveParagraphsInfo&,void>& rLink )
+{
+ pImpEditEngine->aEndMovingParagraphsHdl = rLink;
+}
+
+void EditEngine::SetBeginPasteOrDropHdl( const Link<PasteOrDropInfos&,void>& rLink )
+{
+
+ pImpEditEngine->aBeginPasteOrDropHdl = rLink;
+}
+
+void EditEngine::SetEndPasteOrDropHdl( const Link<PasteOrDropInfos&,void>& rLink )
+{
+ pImpEditEngine->aEndPasteOrDropHdl = rLink;
+}
+
+std::unique_ptr<EditTextObject> EditEngine::CreateTextObject( sal_Int32 nPara, sal_Int32 nParas )
+{
+ DBG_ASSERT( 0 <= nPara && nPara < pImpEditEngine->GetEditDoc().Count(), "CreateTextObject: Startpara out of Range" );
+ DBG_ASSERT( nParas <= pImpEditEngine->GetEditDoc().Count() - nPara, "CreateTextObject: Endpara out of Range" );
+
+ ContentNode* pStartNode = pImpEditEngine->GetEditDoc().GetObject( nPara );
+ ContentNode* pEndNode = pImpEditEngine->GetEditDoc().GetObject( nPara+nParas-1 );
+ DBG_ASSERT( pStartNode, "Start-Paragraph does not exist: CreateTextObject" );
+ DBG_ASSERT( pEndNode, "End-Paragraph does not exist: CreateTextObject" );
+
+ if ( pStartNode && pEndNode )
+ {
+ EditSelection aTmpSel;
+ aTmpSel.Min() = EditPaM( pStartNode, 0 );
+ aTmpSel.Max() = EditPaM( pEndNode, pEndNode->Len() );
+ return pImpEditEngine->CreateTextObject( aTmpSel );
+ }
+ return nullptr;
+}
+
+void EditEngine::RemoveParagraph( sal_Int32 nPara )
+{
+ DBG_ASSERT( pImpEditEngine->GetEditDoc().Count() > 1, "The first paragraph should not be deleted!" );
+ if( pImpEditEngine->GetEditDoc().Count() <= 1 )
+ return;
+
+ ContentNode* pNode = pImpEditEngine->GetEditDoc().GetObject( nPara );
+ const ParaPortion* pPortion = pImpEditEngine->GetParaPortions().SafeGetObject( nPara );
+ DBG_ASSERT( pPortion && pNode, "Paragraph not found: RemoveParagraph" );
+ if ( pNode && pPortion )
+ {
+ // No Undo encapsulation needed.
+ pImpEditEngine->ImpRemoveParagraph( nPara );
+ pImpEditEngine->InvalidateFromParagraph( nPara );
+ pImpEditEngine->UpdateSelections();
+ if (pImpEditEngine->IsUpdateLayout())
+ pImpEditEngine->FormatAndLayout();
+ }
+}
+
+sal_Int32 EditEngine::GetTextLen( sal_Int32 nPara ) const
+{
+ ContentNode* pNode = pImpEditEngine->GetEditDoc().GetObject( nPara );
+ DBG_ASSERT( pNode, "Paragraph not found: GetTextLen" );
+ if ( pNode )
+ return pNode->Len();
+ return 0;
+}
+
+OUString EditEngine::GetText( sal_Int32 nPara ) const
+{
+ OUString aStr;
+ if ( 0 <= nPara && nPara < pImpEditEngine->GetEditDoc().Count() )
+ aStr = pImpEditEngine->GetEditDoc().GetParaAsString( nPara );
+ return aStr;
+}
+
+void EditEngine::SetModifyHdl( const Link<LinkParamNone*,void>& rLink )
+{
+ pImpEditEngine->SetModifyHdl( rLink );
+}
+
+void EditEngine::ClearModifyFlag()
+{
+ pImpEditEngine->SetModifyFlag( false );
+}
+
+void EditEngine::SetModified()
+{
+ pImpEditEngine->SetModifyFlag( true );
+}
+
+bool EditEngine::IsModified() const
+{
+ return pImpEditEngine->IsModified();
+}
+
+bool EditEngine::IsInSelectionMode() const
+{
+ return ( pImpEditEngine->IsInSelectionMode() ||
+ pImpEditEngine->GetSelEngine().IsInSelection() );
+}
+
+void EditEngine::InsertParagraph( sal_Int32 nPara, const EditTextObject& rTxtObj, bool bAppend )
+{
+ if ( nPara > GetParagraphCount() )
+ {
+ SAL_WARN_IF( nPara != EE_PARA_APPEND, "editeng", "Paragraph number too large, but not EE_PARA_APPEND!" );
+ nPara = GetParagraphCount();
+ }
+
+ pImpEditEngine->UndoActionStart( EDITUNDO_INSERT );
+
+ // No Undo compounding needed.
+ EditPaM aPaM( pImpEditEngine->InsertParagraph( nPara ) );
+ // When InsertParagraph from the outside, no hard attributes
+ // should be taken over!
+ pImpEditEngine->RemoveCharAttribs( nPara );
+ pImpEditEngine->InsertText( rTxtObj, EditSelection( aPaM, aPaM ) );
+
+ if ( bAppend && nPara )
+ pImpEditEngine->ConnectContents( nPara-1, /*bBackwards=*/false );
+
+ pImpEditEngine->UndoActionEnd();
+
+ if (pImpEditEngine->IsUpdateLayout())
+ pImpEditEngine->FormatAndLayout();
+}
+
+void EditEngine::InsertParagraph(sal_Int32 nPara, const OUString& rTxt)
+{
+ if ( nPara > GetParagraphCount() )
+ {
+ SAL_WARN_IF( nPara != EE_PARA_APPEND, "editeng", "Paragraph number too large, but not EE_PARA_APPEND!" );
+ nPara = GetParagraphCount();
+ }
+
+ pImpEditEngine->UndoActionStart( EDITUNDO_INSERT );
+ EditPaM aPaM( pImpEditEngine->InsertParagraph( nPara ) );
+ // When InsertParagraph from the outside, no hard attributes
+ // should be taken over!
+ pImpEditEngine->RemoveCharAttribs( nPara );
+ pImpEditEngine->UndoActionEnd();
+ pImpEditEngine->ImpInsertText( EditSelection( aPaM, aPaM ), rTxt );
+ if (pImpEditEngine->IsUpdateLayout())
+ pImpEditEngine->FormatAndLayout();
+}
+
+void EditEngine::SetText(sal_Int32 nPara, const OUString& rTxt)
+{
+ std::optional<EditSelection> pSel = pImpEditEngine->SelectParagraph( nPara );
+ if ( pSel )
+ {
+ pImpEditEngine->UndoActionStart( EDITUNDO_INSERT );
+ pImpEditEngine->ImpInsertText( *pSel, rTxt );
+ pImpEditEngine->UndoActionEnd();
+ if (pImpEditEngine->IsUpdateLayout())
+ pImpEditEngine->FormatAndLayout();
+ }
+}
+
+void EditEngine::SetParaAttribs( sal_Int32 nPara, const SfxItemSet& rSet )
+{
+ pImpEditEngine->SetParaAttribs( nPara, rSet );
+ if ( pImpEditEngine->IsUpdateLayout() )
+ pImpEditEngine->FormatAndLayout();
+}
+
+const SfxItemSet& EditEngine::GetParaAttribs( sal_Int32 nPara ) const
+{
+ return pImpEditEngine->GetParaAttribs( nPara );
+}
+
+bool EditEngine::HasParaAttrib( sal_Int32 nPara, sal_uInt16 nWhich ) const
+{
+ return pImpEditEngine->HasParaAttrib( nPara, nWhich );
+}
+
+const SfxPoolItem& EditEngine::GetParaAttrib( sal_Int32 nPara, sal_uInt16 nWhich ) const
+{
+ return pImpEditEngine->GetParaAttrib( nPara, nWhich );
+}
+
+void EditEngine::SetCharAttribs(sal_Int32 nPara, const SfxItemSet& rSet)
+{
+ EditSelection aSel(pImpEditEngine->ConvertSelection(nPara, 0, nPara, GetTextLen(nPara)));
+ // This is called by sd::View::OnBeginPasteOrDrop(), updating the cursor position on undo is not
+ // wanted.
+ pImpEditEngine->SetAttribs(aSel, rSet, /*nSpecial=*/SetAttribsMode::NONE, /*bSetSelection=*/false);
+ if (pImpEditEngine->IsUpdateLayout())
+ pImpEditEngine->FormatAndLayout();
+}
+
+void EditEngine::GetCharAttribs( sal_Int32 nPara, std::vector<EECharAttrib>& rLst ) const
+{
+ pImpEditEngine->GetCharAttribs( nPara, rLst );
+}
+
+SfxItemSet EditEngine::GetAttribs( const ESelection& rSel, EditEngineAttribs nOnlyHardAttrib )
+{
+ EditSelection aSel( pImpEditEngine->
+ ConvertSelection( rSel.nStartPara, rSel.nStartPos, rSel.nEndPara, rSel.nEndPos ) );
+ return pImpEditEngine->GetAttribs( aSel, nOnlyHardAttrib );
+}
+
+SfxItemSet EditEngine::GetAttribs( sal_Int32 nPara, sal_Int32 nStart, sal_Int32 nEnd, GetAttribsFlags nFlags ) const
+{
+ return pImpEditEngine->GetAttribs( nPara, nStart, nEnd, nFlags );
+}
+
+void EditEngine::RemoveAttribs( const ESelection& rSelection, bool bRemoveParaAttribs, sal_uInt16 nWhich )
+{
+ const EERemoveParaAttribsMode eMode = bRemoveParaAttribs?
+ EERemoveParaAttribsMode::RemoveAll :
+ EERemoveParaAttribsMode::RemoveCharItems;
+
+ pImpEditEngine->UndoActionStart( EDITUNDO_RESETATTRIBS );
+ EditSelection aSel( pImpEditEngine->ConvertSelection( rSelection.nStartPara, rSelection.nStartPos, rSelection.nEndPara, rSelection.nEndPos ) );
+ pImpEditEngine->RemoveCharAttribs( aSel, eMode, nWhich );
+ pImpEditEngine->UndoActionEnd();
+ if (pImpEditEngine->IsUpdateLayout())
+ pImpEditEngine->FormatAndLayout();
+}
+
+vcl::Font EditEngine::GetStandardFont( sal_Int32 nPara )
+{
+ return GetStandardSvxFont( nPara );
+}
+
+SvxFont EditEngine::GetStandardSvxFont( sal_Int32 nPara )
+{
+ ContentNode* pNode = pImpEditEngine->GetEditDoc().GetObject( nPara );
+ return pNode->GetCharAttribs().GetDefFont();
+}
+
+void EditEngine::StripPortions()
+{
+ ScopedVclPtrInstance< VirtualDevice > aTmpDev;
+ tools::Rectangle aBigRect( Point( 0, 0 ), Size( 0x7FFFFFFF, 0x7FFFFFFF ) );
+ if ( IsEffectivelyVertical() )
+ {
+ if( IsTopToBottom() )
+ {
+ aBigRect.SetRight( 0 );
+ aBigRect.SetLeft( -0x7FFFFFFF );
+ }
+ else
+ {
+ aBigRect.SetTop( -0x7FFFFFFF );
+ aBigRect.SetBottom( 0 );
+ }
+ }
+ pImpEditEngine->Paint(*aTmpDev, aBigRect, Point(), true);
+}
+
+void EditEngine::GetPortions( sal_Int32 nPara, std::vector<sal_Int32>& rList )
+{
+ if ( !pImpEditEngine->IsFormatted() )
+ pImpEditEngine->FormatFullDoc();
+
+ const ParaPortion* pParaPortion = pImpEditEngine->GetParaPortions().SafeGetObject( nPara );
+ if ( pParaPortion )
+ {
+ sal_Int32 nEnd = 0;
+ sal_Int32 nTextPortions = pParaPortion->GetTextPortions().Count();
+ for ( sal_Int32 n = 0; n < nTextPortions; n++ )
+ {
+ nEnd = nEnd + pParaPortion->GetTextPortions()[n].GetLen();
+ rList.push_back( nEnd );
+ }
+ }
+}
+
+void EditEngine::SetFlatMode( bool bFlat)
+{
+ pImpEditEngine->SetFlatMode( bFlat );
+}
+
+bool EditEngine::IsFlatMode() const
+{
+ return !( pImpEditEngine->GetStatus().UseCharAttribs() );
+}
+
+void EditEngine::SetSingleLine(bool bValue)
+{
+ if (bValue == pImpEditEngine->GetStatus().IsSingleLine())
+ return;
+
+ if (bValue)
+ pImpEditEngine->GetStatus().TurnOnFlags(EEControlBits::SINGLELINE);
+ else
+ pImpEditEngine->GetStatus().TurnOffFlags(EEControlBits::SINGLELINE);
+}
+
+void EditEngine::SetControlWord( EEControlBits nWord )
+{
+
+ if ( nWord == pImpEditEngine->GetStatus().GetControlWord() )
+ return;
+
+ EEControlBits nPrev = pImpEditEngine->GetStatus().GetControlWord();
+ pImpEditEngine->GetStatus().GetControlWord() = nWord;
+
+ EEControlBits nChanges = nPrev ^ nWord;
+ if ( pImpEditEngine->IsFormatted() )
+ {
+ // possibly reformat:
+ if ( ( nChanges & EEControlBits::USECHARATTRIBS ) ||
+ ( nChanges & EEControlBits::ONECHARPERLINE ) ||
+ ( nChanges & EEControlBits::STRETCHING ) ||
+ ( nChanges & EEControlBits::OUTLINER ) ||
+ ( nChanges & EEControlBits::NOCOLORS ) ||
+ ( nChanges & EEControlBits::OUTLINER2 ) )
+ {
+ if ( nChanges & EEControlBits::USECHARATTRIBS )
+ {
+ pImpEditEngine->GetEditDoc().CreateDefFont( true );
+ }
+
+ pImpEditEngine->FormatFullDoc();
+ pImpEditEngine->UpdateViews( pImpEditEngine->GetActiveView() );
+ }
+ }
+
+ bool bSpellingChanged = bool(nChanges & EEControlBits::ONLINESPELLING);
+
+ if ( !bSpellingChanged )
+ return;
+
+ pImpEditEngine->StopOnlineSpellTimer();
+ if (nWord & EEControlBits::ONLINESPELLING)
+ {
+ // Create WrongList, start timer...
+ sal_Int32 nNodes = pImpEditEngine->GetEditDoc().Count();
+ for ( sal_Int32 n = 0; n < nNodes; n++ )
+ {
+ ContentNode* pNode = pImpEditEngine->GetEditDoc().GetObject( n );
+ pNode->CreateWrongList();
+ }
+ if (pImpEditEngine->IsFormatted())
+ pImpEditEngine->StartOnlineSpellTimer();
+ }
+ else
+ {
+ tools::Long nY = 0;
+ sal_Int32 nNodes = pImpEditEngine->GetEditDoc().Count();
+ for ( sal_Int32 n = 0; n < nNodes; n++ )
+ {
+ ContentNode* pNode = pImpEditEngine->GetEditDoc().GetObject( n );
+ const ParaPortion* pPortion = pImpEditEngine->GetParaPortions()[n];
+ bool bWrongs = false;
+ if (pNode->GetWrongList() != nullptr)
+ bWrongs = !pNode->GetWrongList()->empty();
+ pNode->DestroyWrongList();
+ if ( bWrongs )
+ {
+ pImpEditEngine->aInvalidRect.SetLeft( 0 );
+ pImpEditEngine->aInvalidRect.SetRight( pImpEditEngine->GetPaperSize().Width() );
+ pImpEditEngine->aInvalidRect.SetTop( nY+1 );
+ pImpEditEngine->aInvalidRect.SetBottom( nY+pPortion->GetHeight()-1 );
+ pImpEditEngine->UpdateViews( pImpEditEngine->pActiveView );
+ }
+ nY += pPortion->GetHeight();
+ }
+ }
+}
+
+EEControlBits EditEngine::GetControlWord() const
+{
+ return pImpEditEngine->GetStatus().GetControlWord();
+}
+
+tools::Long EditEngine::GetFirstLineStartX( sal_Int32 nParagraph )
+{
+
+ tools::Long nX = 0;
+ const ParaPortion* pPPortion = pImpEditEngine->GetParaPortions().SafeGetObject( nParagraph );
+ if ( pPPortion )
+ {
+ DBG_ASSERT( pImpEditEngine->IsFormatted() || !pImpEditEngine->IsFormatting(), "GetFirstLineStartX: Doc not formatted - unable to format!" );
+ if ( !pImpEditEngine->IsFormatted() )
+ pImpEditEngine->FormatDoc();
+ const EditLine& rFirstLine = pPPortion->GetLines()[0];
+ nX = rFirstLine.GetStartPosX();
+ }
+ return nX;
+}
+
+Point EditEngine::GetDocPos( const Point& rPaperPos ) const
+{
+ Point aDocPos( rPaperPos );
+ if ( IsEffectivelyVertical() )
+ {
+ if ( IsTopToBottom() )
+ {
+ aDocPos.setX( rPaperPos.Y() );
+ aDocPos.setY( GetPaperSize().Width() - rPaperPos.X() );
+ }
+ else
+ {
+ aDocPos.setX( rPaperPos.Y() );
+ aDocPos.setY( rPaperPos.X() );
+ }
+ }
+ return aDocPos;
+}
+
+Point EditEngine::GetDocPosTopLeft( sal_Int32 nParagraph )
+{
+ const ParaPortion* pPPortion = pImpEditEngine->GetParaPortions().SafeGetObject( nParagraph );
+ DBG_ASSERT( pPPortion, "Paragraph not found: GetWindowPosTopLeft" );
+ Point aPoint;
+ if ( pPPortion )
+ {
+
+ // If someone calls GetLineHeight() with an empty Engine.
+ DBG_ASSERT( pImpEditEngine->IsFormatted() || !pImpEditEngine->IsFormatting(), "GetDocPosTopLeft: Doc not formatted - unable to format!" );
+ if ( !pImpEditEngine->IsFormatted() )
+ pImpEditEngine->FormatAndLayout();
+ if ( pPPortion->GetLines().Count() )
+ {
+ // Correct it if large Bullet.
+ const EditLine& rFirstLine = pPPortion->GetLines()[0];
+ aPoint.setX( rFirstLine.GetStartPosX() );
+ }
+ else
+ {
+ const SvxLRSpaceItem& rLRItem = pImpEditEngine->GetLRSpaceItem( pPPortion->GetNode() );
+ sal_Int32 nSpaceBefore = 0;
+ pImpEditEngine->GetSpaceBeforeAndMinLabelWidth( pPPortion->GetNode(), &nSpaceBefore );
+ short nX = static_cast<short>(rLRItem.GetTextLeft()
+ + rLRItem.GetTextFirstLineOffset()
+ + nSpaceBefore);
+
+ aPoint.setX(pImpEditEngine->scaleXSpacingValue(nX));
+ }
+ aPoint.setY( pImpEditEngine->GetParaPortions().GetYOffset( pPPortion ) );
+ }
+ return aPoint;
+}
+
+const SvxNumberFormat* EditEngine::GetNumberFormat( sal_Int32 ) const
+{
+ // derived objects may override this function to give access to
+ // bullet information (see Outliner)
+ return nullptr;
+}
+
+bool EditEngine::IsRightToLeft( sal_Int32 nPara ) const
+{
+ return pImpEditEngine->IsRightToLeft( nPara );
+}
+
+bool EditEngine::IsTextPos( const Point& rPaperPos, sal_uInt16 nBorder )
+{
+
+ if ( !pImpEditEngine->IsFormatted() )
+ pImpEditEngine->FormatDoc();
+
+ // take unrotated positions for calculation here
+ Point aDocPos = GetDocPos( rPaperPos );
+
+ if ( ( aDocPos.Y() > 0 ) && ( o3tl::make_unsigned(aDocPos.Y()) < pImpEditEngine->GetTextHeight() ) )
+ return pImpEditEngine->IsTextPos(aDocPos, nBorder);
+ return false;
+}
+
+void EditEngine::SetEditTextObjectPool( SfxItemPool* pPool )
+{
+ pImpEditEngine->SetEditTextObjectPool( pPool );
+}
+
+SfxItemPool* EditEngine::GetEditTextObjectPool() const
+{
+ return pImpEditEngine->GetEditTextObjectPool();
+}
+
+void EditEngine::QuickSetAttribs( const SfxItemSet& rSet, const ESelection& rSel )
+{
+
+ EditSelection aSel( pImpEditEngine->
+ ConvertSelection( rSel.nStartPara, rSel.nStartPos, rSel.nEndPara, rSel.nEndPos ) );
+
+ pImpEditEngine->SetAttribs( aSel, rSet );
+}
+
+void EditEngine::QuickMarkInvalid( const ESelection& rSel )
+{
+ DBG_ASSERT( rSel.nStartPara < pImpEditEngine->GetEditDoc().Count(), "MarkInvalid: Start out of Range!" );
+ DBG_ASSERT( rSel.nEndPara < pImpEditEngine->GetEditDoc().Count(), "MarkInvalid: End out of Range!" );
+ for ( sal_Int32 nPara = rSel.nStartPara; nPara <= rSel.nEndPara; nPara++ )
+ {
+ ParaPortion* pPortion = pImpEditEngine->GetParaPortions().SafeGetObject( nPara );
+ if ( pPortion )
+ pPortion->MarkSelectionInvalid( 0 );
+ }
+}
+
+void EditEngine::QuickInsertText(const OUString& rText, const ESelection& rSel)
+{
+
+ EditSelection aSel( pImpEditEngine->
+ ConvertSelection( rSel.nStartPara, rSel.nStartPos, rSel.nEndPara, rSel.nEndPos ) );
+
+ pImpEditEngine->ImpInsertText( aSel, rText );
+}
+
+void EditEngine::QuickDelete( const ESelection& rSel )
+{
+
+ EditSelection aSel( pImpEditEngine->
+ ConvertSelection( rSel.nStartPara, rSel.nStartPos, rSel.nEndPara, rSel.nEndPos ) );
+
+ pImpEditEngine->ImpDeleteSelection( aSel );
+}
+
+void EditEngine::QuickMarkToBeRepainted( sal_Int32 nPara )
+{
+ ParaPortion* pPortion = pImpEditEngine->GetParaPortions().SafeGetObject( nPara );
+ if ( pPortion )
+ pPortion->SetMustRepaint( true );
+}
+
+void EditEngine::QuickInsertLineBreak( const ESelection& rSel )
+{
+
+ EditSelection aSel( pImpEditEngine->
+ ConvertSelection( rSel.nStartPara, rSel.nStartPos, rSel.nEndPara, rSel.nEndPos ) );
+
+ pImpEditEngine->InsertLineBreak( aSel );
+}
+
+void EditEngine::QuickInsertField( const SvxFieldItem& rFld, const ESelection& rSel )
+{
+
+ EditSelection aSel( pImpEditEngine->
+ ConvertSelection( rSel.nStartPara, rSel.nStartPos, rSel.nEndPara, rSel.nEndPos ) );
+
+ pImpEditEngine->ImpInsertFeature( aSel, rFld );
+}
+
+void EditEngine::QuickFormatDoc( bool bFull )
+{
+ if ( bFull )
+ pImpEditEngine->FormatFullDoc();
+ else
+ pImpEditEngine->FormatDoc();
+
+ // Don't pass active view, maybe selection is not updated yet...
+ pImpEditEngine->UpdateViews();
+}
+
+void EditEngine::SetStyleSheet(const EditSelection& aSel, SfxStyleSheet* pStyle)
+{
+ pImpEditEngine->SetStyleSheet(aSel, pStyle);
+}
+
+void EditEngine::SetStyleSheet( sal_Int32 nPara, SfxStyleSheet* pStyle )
+{
+ pImpEditEngine->SetStyleSheet( nPara, pStyle );
+}
+
+const SfxStyleSheet* EditEngine::GetStyleSheet( sal_Int32 nPara ) const
+{
+ return pImpEditEngine->GetStyleSheet( nPara );
+}
+
+SfxStyleSheet* EditEngine::GetStyleSheet( sal_Int32 nPara )
+{
+ return pImpEditEngine->GetStyleSheet( nPara );
+}
+
+void EditEngine::SetStyleSheetPool( SfxStyleSheetPool* pSPool )
+{
+ pImpEditEngine->SetStyleSheetPool( pSPool );
+}
+
+SfxStyleSheetPool* EditEngine::GetStyleSheetPool()
+{
+ return pImpEditEngine->GetStyleSheetPool();
+}
+
+void EditEngine::SetWordDelimiters( const OUString& rDelimiters )
+{
+ pImpEditEngine->aWordDelimiters = rDelimiters;
+ if (pImpEditEngine->aWordDelimiters.indexOf(CH_FEATURE) == -1)
+ pImpEditEngine->aWordDelimiters += OUStringChar(CH_FEATURE);
+}
+
+const OUString& EditEngine::GetWordDelimiters() const
+{
+ return pImpEditEngine->aWordDelimiters;
+}
+
+void EditEngine::EraseVirtualDevice()
+{
+ pImpEditEngine->EraseVirtualDevice();
+}
+
+void EditEngine::SetSpeller( Reference< XSpellChecker1 > const &xSpeller )
+{
+ pImpEditEngine->SetSpeller( xSpeller );
+}
+
+Reference< XSpellChecker1 > const & EditEngine::GetSpeller()
+{
+ return pImpEditEngine->GetSpeller();
+}
+
+void EditEngine::SetHyphenator( Reference< XHyphenator > const & xHyph )
+{
+ pImpEditEngine->SetHyphenator( xHyph );
+}
+
+void EditEngine::GetAllMisspellRanges( std::vector<editeng::MisspellRanges>& rRanges ) const
+{
+ pImpEditEngine->GetAllMisspellRanges(rRanges);
+}
+
+void EditEngine::SetAllMisspellRanges( const std::vector<editeng::MisspellRanges>& rRanges )
+{
+ pImpEditEngine->SetAllMisspellRanges(rRanges);
+}
+
+void EditEngine::SetForbiddenCharsTable(const std::shared_ptr<SvxForbiddenCharactersTable>& xForbiddenChars)
+{
+ ImpEditEngine::SetForbiddenCharsTable( xForbiddenChars );
+}
+
+void EditEngine::SetDefaultLanguage( LanguageType eLang )
+{
+ pImpEditEngine->SetDefaultLanguage( eLang );
+}
+
+LanguageType EditEngine::GetDefaultLanguage() const
+{
+ return pImpEditEngine->GetDefaultLanguage();
+}
+
+bool EditEngine::SpellNextDocument()
+{
+ return false;
+}
+
+EESpellState EditEngine::HasSpellErrors()
+{
+ if ( !pImpEditEngine->GetSpeller().is() )
+ return EESpellState::NoSpeller;
+
+ return pImpEditEngine->HasSpellErrors();
+}
+
+void EditEngine::ClearSpellErrors()
+{
+ pImpEditEngine->ClearSpellErrors();
+}
+
+bool EditEngine::SpellSentence(EditView const & rView, svx::SpellPortions& rToFill )
+{
+ return pImpEditEngine->SpellSentence( rView, rToFill );
+}
+
+void EditEngine::PutSpellingToSentenceStart( EditView const & rEditView )
+{
+ pImpEditEngine->PutSpellingToSentenceStart( rEditView );
+}
+
+void EditEngine::ApplyChangedSentence(EditView const & rEditView, const svx::SpellPortions& rNewPortions, bool bRecheck )
+{
+ pImpEditEngine->ApplyChangedSentence( rEditView, rNewPortions, bRecheck );
+}
+
+bool EditEngine::HasConvertibleTextPortion( LanguageType nLang )
+{
+ return pImpEditEngine->HasConvertibleTextPortion( nLang );
+}
+
+bool EditEngine::ConvertNextDocument()
+{
+ return false;
+}
+
+bool EditEngine::HasText( const SvxSearchItem& rSearchItem )
+{
+ return pImpEditEngine->HasText( rSearchItem );
+}
+
+void EditEngine::setGlobalScale(double fFontScaleX, double fFontScaleY, double fSpacingScaleX, double fSpacingScaleY)
+{
+ pImpEditEngine->setScale(fFontScaleX, fFontScaleY, fSpacingScaleX, fSpacingScaleY);
+}
+
+void EditEngine::getGlobalSpacingScale(double& rX, double& rY) const
+{
+ pImpEditEngine->getSpacingScale(rX, rY);
+}
+
+basegfx::B2DTuple EditEngine::getGlobalSpacingScale() const
+{
+ double x = 0.0;
+ double y = 0.0;
+ pImpEditEngine->getSpacingScale(x, y);
+ return {x, y};
+}
+
+void EditEngine::getGlobalFontScale(double& rX, double& rY) const
+{
+ pImpEditEngine->getFontScale(rX, rY);
+}
+
+basegfx::B2DTuple EditEngine::getGlobalFontScale() const
+{
+ double x = 0.0;
+ double y = 0.0;
+ pImpEditEngine->getFontScale(x, y);
+ return {x, y};
+}
+
+void EditEngine::setRoundFontSizeToPt(bool bRound) const
+{
+ pImpEditEngine->setRoundToNearestPt(bRound);
+}
+
+bool EditEngine::ShouldCreateBigTextObject() const
+{
+ sal_Int32 nTextPortions = 0;
+ sal_Int32 nParas = pImpEditEngine->GetEditDoc().Count();
+ for ( sal_Int32 nPara = 0; nPara < nParas; nPara++ )
+ {
+ ParaPortion* pParaPortion = pImpEditEngine->GetParaPortions()[nPara];
+ nTextPortions = nTextPortions + pParaPortion->GetTextPortions().Count();
+ }
+ return nTextPortions >= pImpEditEngine->GetBigTextObjectStart();
+}
+
+sal_uInt16 EditEngine::GetFieldCount( sal_Int32 nPara ) const
+{
+ sal_uInt16 nFields = 0;
+ ContentNode* pNode = pImpEditEngine->GetEditDoc().GetObject( nPara );
+ if ( pNode )
+ {
+ for (auto const& attrib : pNode->GetCharAttribs().GetAttribs())
+ {
+ if (attrib->Which() == EE_FEATURE_FIELD)
+ ++nFields;
+ }
+ }
+
+ return nFields;
+}
+
+EFieldInfo EditEngine::GetFieldInfo( sal_Int32 nPara, sal_uInt16 nField ) const
+{
+ ContentNode* pNode = pImpEditEngine->GetEditDoc().GetObject( nPara );
+ if ( pNode )
+ {
+ sal_uInt16 nCurrentField = 0;
+ for (auto const& attrib : pNode->GetCharAttribs().GetAttribs())
+ {
+ const EditCharAttrib& rAttr = *attrib;
+ if (rAttr.Which() == EE_FEATURE_FIELD)
+ {
+ if ( nCurrentField == nField )
+ {
+ const SvxFieldItem* p = static_cast<const SvxFieldItem*>(rAttr.GetItem());
+ EFieldInfo aInfo(*p, nPara, rAttr.GetStart());
+ aInfo.aCurrentText = static_cast<const EditCharAttribField&>(rAttr).GetFieldValue();
+ return aInfo;
+ }
+
+ ++nCurrentField;
+ }
+ }
+ }
+ return EFieldInfo();
+}
+
+
+bool EditEngine::UpdateFields()
+{
+ bool bChanges = pImpEditEngine->UpdateFields();
+ if ( bChanges && pImpEditEngine->IsUpdateLayout())
+ pImpEditEngine->FormatAndLayout();
+ return bChanges;
+}
+
+bool EditEngine::UpdateFieldsOnly()
+{
+ return pImpEditEngine->UpdateFields();
+}
+
+void EditEngine::RemoveFields( const std::function<bool ( const SvxFieldData* )>& isFieldData )
+{
+ pImpEditEngine->UpdateFields();
+
+ sal_Int32 nParas = pImpEditEngine->GetEditDoc().Count();
+ for ( sal_Int32 nPara = 0; nPara < nParas; nPara++ )
+ {
+ ContentNode* pNode = pImpEditEngine->GetEditDoc().GetObject( nPara );
+ const CharAttribList::AttribsType& rAttrs = pNode->GetCharAttribs().GetAttribs();
+ for (size_t nAttr = rAttrs.size(); nAttr; )
+ {
+ const EditCharAttrib& rAttr = *rAttrs[--nAttr];
+ if (rAttr.Which() == EE_FEATURE_FIELD)
+ {
+ const SvxFieldData* pFldData = static_cast<const SvxFieldItem*>(rAttr.GetItem())->GetField();
+ if ( pFldData && ( isFieldData( pFldData ) ) )
+ {
+ DBG_ASSERT( dynamic_cast<const SvxFieldItem*>(rAttr.GetItem()), "no field item..." );
+ EditSelection aSel( EditPaM(pNode, rAttr.GetStart()), EditPaM(pNode, rAttr.GetEnd()) );
+ OUString aFieldText = static_cast<const EditCharAttribField&>(rAttr).GetFieldValue();
+ pImpEditEngine->ImpInsertText( aSel, aFieldText );
+ }
+ }
+ }
+ }
+}
+
+bool EditEngine::HasOnlineSpellErrors() const
+{
+ sal_Int32 nNodes = pImpEditEngine->GetEditDoc().Count();
+ for ( sal_Int32 n = 0; n < nNodes; n++ )
+ {
+ ContentNode* pNode = pImpEditEngine->GetEditDoc().GetObject( n );
+ if ( pNode->GetWrongList() && !pNode->GetWrongList()->empty() )
+ return true;
+ }
+ return false;
+}
+
+void EditEngine::CompleteOnlineSpelling()
+{
+ if ( pImpEditEngine->GetStatus().DoOnlineSpelling() )
+ {
+ if( !pImpEditEngine->IsFormatted() )
+ pImpEditEngine->FormatAndLayout();
+
+ pImpEditEngine->StopOnlineSpellTimer();
+ pImpEditEngine->DoOnlineSpelling( nullptr, true, false );
+ }
+}
+
+sal_Int32 EditEngine::FindParagraph( tools::Long nDocPosY )
+{
+ return pImpEditEngine->GetParaPortions().FindParagraph( nDocPosY );
+}
+
+EPosition EditEngine::FindDocPosition( const Point& rDocPos ) const
+{
+ EPosition aPos;
+ // From the point of the API, this is const...
+ EditPaM aPaM = const_cast<EditEngine*>(this)->pImpEditEngine->GetPaM( rDocPos, false );
+ if ( aPaM.GetNode() )
+ {
+ aPos.nPara = pImpEditEngine->maEditDoc.GetPos( aPaM.GetNode() );
+ aPos.nIndex = aPaM.GetIndex();
+ }
+ return aPos;
+}
+
+tools::Rectangle EditEngine::GetCharacterBounds( const EPosition& rPos ) const
+{
+ tools::Rectangle aBounds;
+ ContentNode* pNode = pImpEditEngine->GetEditDoc().GetObject( rPos.nPara );
+
+ // Check against index, not paragraph
+ if ( pNode && ( rPos.nIndex < pNode->Len() ) )
+ {
+ aBounds = pImpEditEngine->PaMtoEditCursor( EditPaM( pNode, rPos.nIndex ), GetCursorFlags::TextOnly );
+ tools::Rectangle aR2 = pImpEditEngine->PaMtoEditCursor( EditPaM( pNode, rPos.nIndex+1 ), GetCursorFlags::TextOnly|GetCursorFlags::EndOfLine );
+ if ( aR2.Right() > aBounds.Right() )
+ aBounds.SetRight( aR2.Right() );
+ }
+ return aBounds;
+}
+
+ParagraphInfos EditEngine::GetParagraphInfos( sal_Int32 nPara )
+{
+
+ // This only works if not already in the format ...
+ if ( !pImpEditEngine->IsFormatted() )
+ pImpEditEngine->FormatDoc();
+
+ ParagraphInfos aInfos;
+ aInfos.bValid = pImpEditEngine->IsFormatted();
+ if ( pImpEditEngine->IsFormatted() )
+ {
+ const ParaPortion* pParaPortion = pImpEditEngine->GetParaPortions()[nPara];
+ const EditLine* pLine = (pParaPortion && pParaPortion->GetLines().Count()) ?
+ &pParaPortion->GetLines()[0] : nullptr;
+ DBG_ASSERT( pParaPortion && pLine, "GetParagraphInfos - Paragraph out of range" );
+ if ( pParaPortion && pLine )
+ {
+ aInfos.nFirstLineHeight = pLine->GetHeight();
+ aInfos.nFirstLineTextHeight = pLine->GetTxtHeight();
+ aInfos.nFirstLineMaxAscent = pLine->GetMaxAscent();
+ }
+ }
+ return aInfos;
+}
+
+css::uno::Reference< css::datatransfer::XTransferable >
+ EditEngine::CreateTransferable( const ESelection& rSelection ) const
+{
+ EditSelection aSel( pImpEditEngine->CreateSel( rSelection ) );
+ return pImpEditEngine->CreateTransferable( aSel );
+}
+
+
+// ====================== Virtual Methods ========================
+
+void EditEngine::DrawingText( const Point&, const OUString&, sal_Int32, sal_Int32,
+ std::span<const sal_Int32>, std::span<const sal_Bool>,
+ const SvxFont&, sal_Int32 /*nPara*/, sal_uInt8 /*nRightToLeft*/,
+ const EEngineData::WrongSpellVector*, const SvxFieldData*, bool, bool,
+ const css::lang::Locale*, const Color&, const Color&)
+
+{
+}
+
+void EditEngine::DrawingTab( const Point& /*rStartPos*/, tools::Long /*nWidth*/,
+ const OUString& /*rChar*/, const SvxFont& /*rFont*/,
+ sal_Int32 /*nPara*/, sal_uInt8 /*nRightToLeft*/, bool /*bEndOfLine*/,
+ bool /*bEndOfParagraph*/, const Color& /*rOverlineColor*/,
+ const Color& /*rTextLineColor*/)
+{
+}
+
+void EditEngine::PaintingFirstLine(sal_Int32, const Point&, const Point&, Degree10, OutputDevice&)
+{
+}
+
+void EditEngine::ParagraphInserted( sal_Int32 nPara )
+{
+
+ if ( GetNotifyHdl().IsSet() )
+ {
+ EENotify aNotify( EE_NOTIFY_PARAGRAPHINSERTED );
+ aNotify.nParagraph = nPara;
+ pImpEditEngine->GetNotifyHdl().Call( aNotify );
+ }
+}
+
+void EditEngine::ParagraphDeleted( sal_Int32 nPara )
+{
+
+ if ( GetNotifyHdl().IsSet() )
+ {
+ EENotify aNotify( EE_NOTIFY_PARAGRAPHREMOVED );
+ aNotify.nParagraph = nPara;
+ pImpEditEngine->GetNotifyHdl().Call( aNotify );
+ }
+}
+void EditEngine::ParagraphConnected( sal_Int32 /*nLeftParagraph*/, sal_Int32 /*nRightParagraph*/ )
+{
+}
+
+void EditEngine::ParaAttribsChanged( sal_Int32 /* nParagraph */ )
+{
+}
+
+void EditEngine::StyleSheetChanged( SfxStyleSheet* /* pStyle */ )
+{
+}
+
+void EditEngine::ParagraphHeightChanged( sal_Int32 nPara )
+{
+ if ( GetNotifyHdl().IsSet() )
+ {
+ EENotify aNotify( EE_NOTIFY_TextHeightChanged );
+ aNotify.nParagraph = nPara;
+ pImpEditEngine->GetNotifyHdl().Call( aNotify );
+ }
+
+ for (EditView* pView : pImpEditEngine->aEditViews)
+ pView->pImpEditView->ScrollStateChange();
+}
+
+OUString EditEngine::GetUndoComment( sal_uInt16 nId ) const
+{
+ OUString aComment;
+ switch ( nId )
+ {
+ case EDITUNDO_REMOVECHARS:
+ case EDITUNDO_CONNECTPARAS:
+ case EDITUNDO_DELCONTENT:
+ case EDITUNDO_DELETE:
+ case EDITUNDO_CUT:
+ aComment = EditResId(RID_EDITUNDO_DEL);
+ break;
+ case EDITUNDO_MOVEPARAGRAPHS:
+ case EDITUNDO_MOVEPARAS:
+ case EDITUNDO_DRAGANDDROP:
+ aComment = EditResId(RID_EDITUNDO_MOVE);
+ break;
+ case EDITUNDO_INSERTFEATURE:
+ case EDITUNDO_SPLITPARA:
+ case EDITUNDO_INSERTCHARS:
+ case EDITUNDO_PASTE:
+ case EDITUNDO_INSERT:
+ case EDITUNDO_READ:
+ aComment = EditResId(RID_EDITUNDO_INSERT);
+ break;
+ case EDITUNDO_REPLACEALL:
+ aComment = EditResId(RID_EDITUNDO_REPLACE);
+ break;
+ case EDITUNDO_ATTRIBS:
+ case EDITUNDO_PARAATTRIBS:
+ aComment = EditResId(RID_EDITUNDO_SETATTRIBS);
+ break;
+ case EDITUNDO_RESETATTRIBS:
+ aComment = EditResId(RID_EDITUNDO_RESETATTRIBS);
+ break;
+ case EDITUNDO_STYLESHEET:
+ aComment = EditResId(RID_EDITUNDO_SETSTYLE);
+ break;
+ case EDITUNDO_TRANSLITERATE:
+ aComment = EditResId(RID_EDITUNDO_TRANSLITERATE);
+ break;
+ case EDITUNDO_INDENTBLOCK:
+ case EDITUNDO_UNINDENTBLOCK:
+ aComment = EditResId(RID_EDITUNDO_INDENT);
+ break;
+ }
+ return aComment;
+}
+
+tools::Rectangle EditEngine::GetBulletArea( sal_Int32 )
+{
+ return tools::Rectangle( Point(), Point() );
+}
+
+OUString EditEngine::CalcFieldValue( const SvxFieldItem&, sal_Int32, sal_Int32, std::optional<Color>&, std::optional<Color>&, std::optional<FontLineStyle>& )
+{
+ return OUString(' ');
+}
+
+bool EditEngine::FieldClicked( const SvxFieldItem& )
+{
+ return false;
+}
+
+
+// ====================== Static Methods =======================
+
+rtl::Reference<SfxItemPool> EditEngine::CreatePool()
+{
+ return new EditEngineItemPool();
+}
+
+
+/** If we let the libc runtime clean us up, we trigger a crash */
+namespace
+{
+class TerminateListener : public ::cppu::WeakImplHelper< css::frame::XTerminateListener >
+{
+ void SAL_CALL queryTermination( const lang::EventObject& ) override
+ {}
+ void SAL_CALL notifyTermination( const lang::EventObject& ) override
+ {
+ pGlobalPool.clear();
+ }
+ virtual void SAL_CALL disposing( const ::css::lang::EventObject& ) override
+ {}
+};
+};
+
+SfxItemPool& EditEngine::GetGlobalItemPool()
+{
+ if ( !pGlobalPool )
+ {
+ pGlobalPool = CreatePool();
+#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
+ // TerminateListener option not available, force it to leak
+ pGlobalPool->acquire();
+#else
+ uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create(comphelper::getProcessComponentContext());
+ uno::Reference< frame::XTerminateListener > xListener( new TerminateListener );
+ xDesktop->addTerminateListener( xListener );
+#endif
+ }
+ return *pGlobalPool;
+}
+
+void EditEngine::SetFontInfoInItemSet( SfxItemSet& rSet, const vcl::Font& rFont )
+{
+ SvxFont aSvxFont( rFont );
+ SetFontInfoInItemSet( rSet, aSvxFont );
+
+}
+
+void EditEngine::SetFontInfoInItemSet( SfxItemSet& rSet, const SvxFont& rFont )
+{
+ rSet.Put( SvxLanguageItem( rFont.GetLanguage(), EE_CHAR_LANGUAGE ) );
+ rSet.Put( SvxFontItem( rFont.GetFamilyType(), rFont.GetFamilyName(), OUString(), rFont.GetPitch(), rFont.GetCharSet(), EE_CHAR_FONTINFO ) );
+ rSet.Put( SvxFontHeightItem( rFont.GetFontSize().Height(), 100, EE_CHAR_FONTHEIGHT ) );
+ rSet.Put( SvxCharScaleWidthItem( 100, EE_CHAR_FONTWIDTH ) );
+ rSet.Put( SvxShadowedItem( rFont.IsShadow(), EE_CHAR_SHADOW ) );
+ rSet.Put( SvxEscapementItem( rFont.GetEscapement(), rFont.GetPropr(), EE_CHAR_ESCAPEMENT ) );
+ rSet.Put( SvxWeightItem( rFont.GetWeight(), EE_CHAR_WEIGHT ) );
+ rSet.Put( SvxColorItem( rFont.GetColor(), EE_CHAR_COLOR ) );
+ rSet.Put( SvxColorItem( rFont.GetFillColor(), EE_CHAR_BKGCOLOR ) );
+ rSet.Put( SvxUnderlineItem( rFont.GetUnderline(), EE_CHAR_UNDERLINE ) );
+ rSet.Put( SvxOverlineItem( rFont.GetOverline(), EE_CHAR_OVERLINE ) );
+ rSet.Put( SvxCrossedOutItem( rFont.GetStrikeout(), EE_CHAR_STRIKEOUT ) );
+ rSet.Put( SvxCaseMapItem( rFont.GetCaseMap(), EE_CHAR_CASEMAP ) );
+ rSet.Put( SvxPostureItem( rFont.GetItalic(), EE_CHAR_ITALIC ) );
+ rSet.Put( SvxContourItem( rFont.IsOutline(), EE_CHAR_OUTLINE ) );
+ rSet.Put( SvxAutoKernItem( rFont.IsKerning(), EE_CHAR_PAIRKERNING ) );
+ rSet.Put( SvxKerningItem( rFont.GetFixKerning(), EE_CHAR_KERNING ) );
+ rSet.Put( SvxWordLineModeItem( rFont.IsWordLineMode(), EE_CHAR_WLM ) );
+ rSet.Put( SvxEmphasisMarkItem( rFont.GetEmphasisMark(), EE_CHAR_EMPHASISMARK ) );
+ rSet.Put( SvxCharReliefItem( rFont.GetRelief(), EE_CHAR_RELIEF ) );
+}
+
+vcl::Font EditEngine::CreateFontFromItemSet( const SfxItemSet& rItemSet, SvtScriptType nScriptType )
+{
+ SvxFont aFont;
+ CreateFont( aFont, rItemSet, true, nScriptType );
+#if HAVE_P1155R3
+ return aFont;
+#else
+ return std::move(aFont);
+#endif
+}
+
+SvxFont EditEngine::CreateSvxFontFromItemSet( const SfxItemSet& rItemSet )
+{
+ SvxFont aFont;
+ CreateFont( aFont, rItemSet );
+ return aFont;
+}
+
+bool EditEngine::DoesKeyMoveCursor( const KeyEvent& rKeyEvent )
+{
+ bool bDoesMove = false;
+
+ switch ( rKeyEvent.GetKeyCode().GetCode() )
+ {
+ case KEY_UP:
+ case KEY_DOWN:
+ case KEY_LEFT:
+ case KEY_RIGHT:
+ case KEY_HOME:
+ case KEY_END:
+ case KEY_PAGEUP:
+ case KEY_PAGEDOWN:
+ {
+ if ( !rKeyEvent.GetKeyCode().IsMod2() )
+ bDoesMove = true;
+ }
+ break;
+ }
+ return bDoesMove;
+}
+
+bool EditEngine::DoesKeyChangeText( const KeyEvent& rKeyEvent )
+{
+ bool bDoesChange = false;
+
+ KeyFuncType eFunc = rKeyEvent.GetKeyCode().GetFunction();
+ if ( eFunc != KeyFuncType::DONTKNOW )
+ {
+ switch ( eFunc )
+ {
+ case KeyFuncType::UNDO:
+ case KeyFuncType::REDO:
+ case KeyFuncType::CUT:
+ case KeyFuncType::PASTE: bDoesChange = true;
+ break;
+ default: // is then possibly edited below.
+ eFunc = KeyFuncType::DONTKNOW;
+ }
+ }
+ if ( eFunc == KeyFuncType::DONTKNOW )
+ {
+ switch ( rKeyEvent.GetKeyCode().GetCode() )
+ {
+ case KEY_DELETE:
+ case KEY_BACKSPACE: bDoesChange = true;
+ break;
+ case KEY_RETURN:
+ case KEY_TAB:
+ {
+ if ( !rKeyEvent.GetKeyCode().IsMod1() && !rKeyEvent.GetKeyCode().IsMod2() )
+ bDoesChange = true;
+ }
+ break;
+ default:
+ {
+ bDoesChange = IsSimpleCharInput( rKeyEvent );
+ }
+ }
+ }
+ return bDoesChange;
+}
+
+bool EditEngine::IsSimpleCharInput( const KeyEvent& rKeyEvent )
+{
+ return EditEngine::IsPrintable( rKeyEvent.GetCharCode() ) &&
+ ( KEY_MOD2 != (rKeyEvent.GetKeyCode().GetModifier() & ~KEY_SHIFT ) ) &&
+ ( KEY_MOD1 != (rKeyEvent.GetKeyCode().GetModifier() & ~KEY_SHIFT ) );
+}
+
+bool EditEngine::HasValidData( const css::uno::Reference< css::datatransfer::XTransferable >& rTransferable )
+{
+ bool bValidData = false;
+
+ if ( comphelper::LibreOfficeKit::isActive())
+ return true;
+
+ if ( rTransferable.is() )
+ {
+ // Every application that copies rtf or any other text format also copies plain text into the clipboard...
+ datatransfer::DataFlavor aFlavor;
+ SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor );
+ bValidData = rTransferable->isDataFlavorSupported( aFlavor );
+ }
+
+ return bValidData;
+}
+
+/** sets a link that is called at the beginning of a drag operation at an edit view */
+void EditEngine::SetBeginDropHdl( const Link<EditView*,void>& rLink )
+{
+ pImpEditEngine->SetBeginDropHdl( rLink );
+}
+
+Link<EditView*,void> const & EditEngine::GetBeginDropHdl() const
+{
+ return pImpEditEngine->GetBeginDropHdl();
+}
+
+/** sets a link that is called at the end of a drag operation at an edit view */
+void EditEngine::SetEndDropHdl( const Link<EditView*,void>& rLink )
+{
+ pImpEditEngine->SetEndDropHdl( rLink );
+}
+
+Link<EditView*,void> const & EditEngine::GetEndDropHdl() const
+{
+ return pImpEditEngine->GetEndDropHdl();
+}
+
+void EditEngine::SetFirstWordCapitalization( bool bCapitalize )
+{
+ pImpEditEngine->SetFirstWordCapitalization( bCapitalize );
+}
+
+void EditEngine::SetReplaceLeadingSingleQuotationMark( bool bReplace )
+{
+ pImpEditEngine->SetReplaceLeadingSingleQuotationMark( bReplace );
+}
+
+bool EditEngine::IsHtmlImportHandlerSet() const
+{
+ return pImpEditEngine->aHtmlImportHdl.IsSet();
+}
+
+bool EditEngine::IsRtfImportHandlerSet() const
+{
+ return pImpEditEngine->aRtfImportHdl.IsSet();
+}
+
+bool EditEngine::IsImportRTFStyleSheetsSet() const
+{
+ return pImpEditEngine->GetStatus().DoImportRTFStyleSheets();
+}
+
+void EditEngine::CallHtmlImportHandler(HtmlImportInfo& rInfo)
+{
+ pImpEditEngine->aHtmlImportHdl.Call(rInfo);
+}
+
+void EditEngine::CallRtfImportHandler(RtfImportInfo& rInfo)
+{
+ pImpEditEngine->aRtfImportHdl.Call(rInfo);
+}
+
+EditPaM EditEngine::InsertParaBreak(const EditSelection& rEditSelection)
+{
+ return pImpEditEngine->ImpInsertParaBreak(rEditSelection);
+}
+
+EditPaM EditEngine::InsertLineBreak(const EditSelection& rEditSelection)
+{
+ return pImpEditEngine->InsertLineBreak(rEditSelection);
+}
+
+sal_Int32 EditEngine::GetOverflowingParaNum() const {
+ return pImpEditEngine->GetOverflowingParaNum();
+}
+
+sal_Int32 EditEngine::GetOverflowingLineNum() const {
+ return pImpEditEngine->GetOverflowingLineNum();
+}
+
+void EditEngine::ClearOverflowingParaNum() {
+ pImpEditEngine->ClearOverflowingParaNum();
+}
+
+bool EditEngine::IsPageOverflow() {
+ pImpEditEngine->CheckPageOverflow();
+ return pImpEditEngine->IsPageOverflow();
+}
+
+void EditEngine::DisableAttributeExpanding() {
+ pImpEditEngine->GetEditDoc().DisableAttributeExpanding();
+}
+
+void EditEngine::EnableSkipOutsideFormat(bool set)
+{
+ pImpEditEngine->EnableSkipOutsideFormat(set);
+}
+
+void EditEngine::SetLOKSpecialPaperSize(const Size& rSize)
+{
+ pImpEditEngine->SetLOKSpecialPaperSize(rSize);
+}
+
+const Size& EditEngine::GetLOKSpecialPaperSize() const
+{
+ return pImpEditEngine->GetLOKSpecialPaperSize();
+}
+
+EFieldInfo::EFieldInfo()
+{
+}
+
+
+EFieldInfo::EFieldInfo( const SvxFieldItem& rFieldItem, sal_Int32 nPara, sal_Int32 nPos ) :
+ pFieldItem( new SvxFieldItem( rFieldItem ) ),
+ aPosition( nPara, nPos )
+{
+}
+
+EFieldInfo::~EFieldInfo()
+{
+}
+
+EFieldInfo::EFieldInfo( const EFieldInfo& rFldInfo )
+{
+ *this = rFldInfo;
+}
+
+EFieldInfo& EFieldInfo::operator= ( const EFieldInfo& rFldInfo )
+{
+ if( this == &rFldInfo )
+ return *this;
+
+ pFieldItem.reset( rFldInfo.pFieldItem ? new SvxFieldItem( *rFldInfo.pFieldItem ) : nullptr );
+ aCurrentText = rFldInfo.aCurrentText;
+ aPosition = rFldInfo.aPosition;
+
+ return *this;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/editobj.cxx b/editeng/source/editeng/editobj.cxx
new file mode 100644
index 0000000000..762cac112d
--- /dev/null
+++ b/editeng/source/editeng/editobj.cxx
@@ -0,0 +1,759 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+
+#include <o3tl/safeint.hxx>
+#include <sal/log.hxx>
+
+#include <editeng/macros.hxx>
+#include <editeng/section.hxx>
+#include "editobj2.hxx"
+#include <editeng/editdata.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/flditem.hxx>
+
+#include <svl/sharedstringpool.hxx>
+
+#include <libxml/xmlwriter.h>
+#include <algorithm>
+#include <cassert>
+
+#if DEBUG_EDIT_ENGINE
+#include <iostream>
+using std::cout;
+using std::endl;
+#endif
+
+using namespace com::sun::star;
+
+
+XEditAttribute::XEditAttribute(SfxItemPool& rPool, const SfxPoolItem& rItem, sal_Int32 nS, sal_Int32 nE)
+: maItemHolder(rPool, &rItem)
+, nStart(nS)
+, nEnd(nE)
+{
+}
+
+bool XEditAttribute::IsFeature() const
+{
+ sal_uInt16 nWhich = GetItem()->Which();
+ return ((nWhich >= EE_FEATURE_START) && (nWhich <= EE_FEATURE_END));
+}
+
+void XEditAttribute::SetItem(SfxItemPool& rPool, const SfxPoolItem& rItem)
+{
+ maItemHolder = SfxPoolItemHolder(rPool, &rItem);
+}
+
+XParaPortionList::XParaPortionList(OutputDevice* pRefDev, sal_uInt32 nPW,
+ double fFontScaleX, double fFontScaleY,
+ double fSpacingScaleX, double fSpacingScaleY)
+ : pRefDevPtr(pRefDev)
+ , mfFontScaleX(fFontScaleX)
+ , mfFontScaleY(fFontScaleY)
+ , mfSpacingScaleX(fSpacingScaleX)
+ , mfSpacingScaleY(fSpacingScaleY)
+ , nPaperWidth(nPW)
+{
+}
+
+void XParaPortionList::push_back(XParaPortion* p)
+{
+ maList.push_back(std::unique_ptr<XParaPortion>(p));
+}
+
+const XParaPortion& XParaPortionList::operator [](size_t i) const
+{
+ return *maList[i];
+}
+
+ContentInfo::ContentInfo( SfxItemPool& rPool ) :
+ eFamily(SfxStyleFamily::Para),
+ aParaAttribs(rPool)
+{
+}
+
+// the real Copy constructor is nonsense, since I have to work with another Pool!
+ContentInfo::ContentInfo( const ContentInfo& rCopyFrom, SfxItemPool& rPoolToUse ) :
+ maText(rCopyFrom.maText),
+ aStyle(rCopyFrom.aStyle),
+ eFamily(rCopyFrom.eFamily),
+ aParaAttribs(rPoolToUse)
+{
+ // this should ensure that the Items end up in the correct Pool!
+ aParaAttribs.Set( rCopyFrom.GetParaAttribs() );
+
+ for (const XEditAttribute & rAttr : rCopyFrom.maCharAttribs)
+ {
+ maCharAttribs.emplace_back(rPoolToUse, *rAttr.GetItem(), rAttr.GetStart(), rAttr.GetEnd());
+ }
+
+ if ( rCopyFrom.GetWrongList() )
+ mpWrongs.reset(rCopyFrom.GetWrongList()->Clone());
+}
+
+ContentInfo::~ContentInfo()
+{
+ maCharAttribs.clear();
+}
+
+void ContentInfo::NormalizeString( svl::SharedStringPool& rPool )
+{
+ maText = rPool.intern(OUString(maText.getData()));
+}
+
+
+OUString ContentInfo::GetText() const
+{
+ rtl_uString* p = const_cast<rtl_uString*>(maText.getData());
+ return OUString(p);
+}
+
+void ContentInfo::SetText( const OUString& rStr )
+{
+ maText = svl::SharedString(rStr.pData, nullptr);
+}
+
+void ContentInfo::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("ContentInfo"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("style"), BAD_CAST(aStyle.toUtf8().getStr()));
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("text"));
+ OUString aText = GetText();
+ // TODO share code with sax_fastparser::FastSaxSerializer::write().
+ (void)xmlTextWriterWriteString(pWriter, BAD_CAST(aText.replaceAll("\x01", "&#1;").toUtf8().getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+ aParaAttribs.dumpAsXml(pWriter);
+ for (auto const& rCharAttribs : maCharAttribs)
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("attribs"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("start"), "%" SAL_PRIdINT32, rCharAttribs.GetStart());
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("end"), "%" SAL_PRIdINT32, rCharAttribs.GetEnd());
+ rCharAttribs.GetItem()->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+const WrongList* ContentInfo::GetWrongList() const
+{
+ return mpWrongs.get();
+}
+
+void ContentInfo::SetWrongList( WrongList* p )
+{
+ mpWrongs.reset(p);
+}
+
+// #i102062#
+bool ContentInfo::isWrongListEqual(const ContentInfo& rCompare) const
+{
+ if(GetWrongList() == rCompare.GetWrongList())
+ return true;
+
+ if(!GetWrongList() || !rCompare.GetWrongList())
+ return false;
+
+ return (*GetWrongList() == *rCompare.GetWrongList());
+}
+
+#if DEBUG_EDIT_ENGINE
+void ContentInfo::Dump() const
+{
+ cout << "--" << endl;
+ cout << "text: '" << OUString(const_cast<rtl_uString*>(maText.getData())) << "'" << endl;
+ cout << "style: '" << aStyle << "'" << endl;
+
+ for (auto const& attrib : aAttribs)
+ {
+ cout << "attribute: " << endl;
+ cout << " span: [begin=" << attrib.GetStart() << ", end=" << attrib.GetEnd() << "]" << endl;
+ cout << " feature: " << (attrib.IsFeature() ? "yes":"no") << endl;
+ }
+}
+#endif
+
+bool ContentInfo::Equals(const ContentInfo& rCompare, bool bComparePool) const
+{
+ return maText == rCompare.maText && aStyle == rCompare.aStyle && eFamily == rCompare.eFamily
+ && aParaAttribs.Equals(rCompare.aParaAttribs, bComparePool)
+ && maCharAttribs == rCompare.maCharAttribs;
+}
+
+EditTextObject::~EditTextObject() = default;
+
+std::unique_ptr<EditTextObject> EditTextObjectImpl::Clone() const
+{
+ return std::make_unique<EditTextObjectImpl>(*this);
+}
+
+bool EditTextObject::Equals( const EditTextObject& rCompare ) const
+{
+ return toImpl(*this).Equals(toImpl(rCompare), false /*bComparePool*/);
+}
+
+void EditTextObjectImpl::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ bool bOwns = false;
+ if (!pWriter)
+ {
+ pWriter = xmlNewTextWriterFilename("editTextObject.xml", 0);
+ xmlTextWriterSetIndent(pWriter,1);
+ (void)xmlTextWriterSetIndentString(pWriter, BAD_CAST(" "));
+ (void)xmlTextWriterStartDocument(pWriter, nullptr, nullptr, nullptr);
+ bOwns = true;
+ }
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("EditTextObject"));
+ sal_Int32 nCount = GetParagraphCount();
+ for (sal_Int32 i = 0; i < nCount; ++i)
+ {
+ maContents[i]->dumpAsXml(pWriter);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+
+ if (bOwns)
+ {
+ (void)xmlTextWriterEndDocument(pWriter);
+ xmlFreeTextWriter(pWriter);
+ }
+}
+
+#if DEBUG_EDIT_ENGINE
+void EditTextObjectImpl::Dump() const
+{
+ for (auto const& content : maContents)
+ content.Dump();
+}
+#endif
+
+static rtl::Reference<SfxItemPool> getEditEngineItemPool(SfxItemPool* pPool, MapUnit eDefaultMetric)
+{
+ // #i101239# ensure target is an EditEngineItemPool, so that at
+ // pool destruction time of an alien pool, the pool is still alive.
+ // When registering would happen at an alien pool which just uses an
+ // EditEngineItemPool as some sub-pool, that pool could already
+ // be decoupled and deleted which would lead to crashes.
+ for (; pPool; pPool = pPool->GetSecondaryPool())
+ if (dynamic_cast<EditEngineItemPool*>(pPool))
+ return pPool;
+
+ auto pRetval = EditEngine::CreatePool();
+ pRetval->SetDefaultMetric(eDefaultMetric);
+ return pRetval;
+}
+
+EditTextObjectImpl::EditTextObjectImpl(SfxItemPool* pP, MapUnit eDefaultMetric, bool bVertical,
+ TextRotation eRotation, SvtScriptType eScriptType)
+ : mpPool(getEditEngineItemPool(pP, eDefaultMetric))
+ , meUserType(OutlinerMode::DontKnow)
+ , meScriptType(eScriptType)
+ , meRotation(eRotation)
+ , meMetric(eDefaultMetric)
+ , mbVertical(bVertical)
+{
+}
+
+EditTextObjectImpl::EditTextObjectImpl( const EditTextObjectImpl& r )
+ : mpPool(r.mpPool)
+ , meUserType(r.meUserType)
+ , meScriptType(r.meScriptType)
+ , meRotation(r.meRotation)
+ , meMetric(r.meMetric)
+ , mbVertical(r.mbVertical)
+{
+ // Do not copy PortionInfo
+
+ maContents.reserve(r.maContents.size());
+ for (auto const& content : r.maContents)
+ maContents.push_back(std::unique_ptr<ContentInfo>(new ContentInfo(*content, *mpPool)));
+}
+
+EditTextObjectImpl::~EditTextObjectImpl()
+{
+ ClearPortionInfo();
+
+ // Remove contents before deleting the pool instance since each content
+ // has to access the pool instance in its destructor.
+ maContents.clear();
+}
+
+
+void EditTextObjectImpl::SetUserType( OutlinerMode n )
+{
+ meUserType = n;
+}
+
+void EditTextObjectImpl::NormalizeString( svl::SharedStringPool& rPool )
+{
+ for (auto const& content : maContents)
+ {
+ ContentInfo& rInfo = *content;
+ rInfo.NormalizeString(rPool);
+ }
+}
+
+std::vector<svl::SharedString> EditTextObjectImpl::GetSharedStrings() const
+{
+ std::vector<svl::SharedString> aSSs;
+ aSSs.reserve(maContents.size());
+ for (auto const& content : maContents)
+ {
+ const ContentInfo& rInfo = *content;
+ aSSs.push_back(rInfo.GetSharedString());
+ }
+ return aSSs;
+}
+
+bool EditTextObjectImpl::IsEffectivelyVertical() const
+{
+ return (mbVertical && meRotation == TextRotation::NONE) ||
+ (!mbVertical && meRotation != TextRotation::NONE);
+}
+
+bool EditTextObjectImpl::IsTopToBottom() const
+{
+ return (mbVertical && meRotation == TextRotation::NONE) ||
+ (!mbVertical && meRotation == TextRotation::TOPTOBOTTOM);
+}
+
+void EditTextObjectImpl::SetVertical( bool bVert)
+{
+ if (bVert != mbVertical)
+ {
+ mbVertical = bVert;
+ ClearPortionInfo();
+ }
+}
+
+bool EditTextObjectImpl::GetVertical() const
+{
+ return mbVertical;
+}
+
+void EditTextObjectImpl::SetRotation(TextRotation nRotation)
+{
+ if (meRotation != nRotation)
+ {
+ meRotation = nRotation;
+ ClearPortionInfo();
+ }
+}
+
+TextRotation EditTextObjectImpl::GetRotation() const
+{
+ return meRotation;
+}
+
+XEditAttribute EditTextObjectImpl::CreateAttrib( const SfxPoolItem& rItem, sal_Int32 nStart, sal_Int32 nEnd )
+{
+ return XEditAttribute(*mpPool, rItem, nStart, nEnd);
+}
+
+ContentInfo* EditTextObjectImpl::CreateAndInsertContent()
+{
+ maContents.push_back(std::unique_ptr<ContentInfo>(new ContentInfo(*mpPool)));
+ return maContents.back().get();
+}
+
+sal_Int32 EditTextObjectImpl::GetParagraphCount() const
+{
+ size_t nSize = maContents.size();
+ if (nSize > EE_PARA_MAX_COUNT)
+ {
+ SAL_WARN( "editeng", "EditTextObjectImpl::GetParagraphCount - overflow " << nSize);
+ return EE_PARA_MAX_COUNT;
+ }
+ return static_cast<sal_Int32>(nSize);
+}
+
+OUString EditTextObjectImpl::GetText(sal_Int32 nPara) const
+{
+ if (nPara < 0 || o3tl::make_unsigned(nPara) >= maContents.size())
+ return OUString();
+
+ return maContents[nPara]->GetText();
+}
+
+void EditTextObjectImpl::ClearPortionInfo()
+{
+ mpPortionInfo.reset();
+}
+
+bool EditTextObjectImpl::HasOnlineSpellErrors() const
+{
+ for (auto const& content : maContents)
+ {
+ if ( content->GetWrongList() && !content->GetWrongList()->empty() )
+ return true;
+ }
+ return false;
+}
+
+void EditTextObjectImpl::GetCharAttribs( sal_Int32 nPara, std::vector<EECharAttrib>& rLst ) const
+{
+ if (nPara < 0 || o3tl::make_unsigned(nPara) >= maContents.size())
+ return;
+
+ rLst.clear();
+ const ContentInfo& rC = *maContents[nPara];
+ for (const XEditAttribute & rAttr : rC.maCharAttribs)
+ {
+ EECharAttrib aEEAttr(rAttr.GetStart(), rAttr.GetEnd(), rAttr.GetItem());
+ rLst.push_back(aEEAttr);
+ }
+}
+
+bool EditTextObjectImpl::IsFieldObject() const
+{
+ return GetField() != nullptr;
+}
+
+const SvxFieldItem* EditTextObjectImpl::GetField() const
+{
+ if (maContents.size() == 1)
+ {
+ const ContentInfo& rC = *maContents[0];
+ if (rC.GetText().getLength() == 1)
+ {
+ size_t nAttribs = rC.maCharAttribs.size();
+ for (size_t nAttr = nAttribs; nAttr; )
+ {
+ const XEditAttribute& rX = rC.maCharAttribs[--nAttr];
+ if (rX.GetItem()->Which() == EE_FEATURE_FIELD)
+ return static_cast<const SvxFieldItem*>(rX.GetItem());
+ }
+ }
+ }
+ return nullptr;
+}
+
+const SvxFieldData* EditTextObjectImpl::GetFieldData(sal_Int32 nPara, size_t nPos, sal_Int32 nType) const
+{
+ if (nPara < 0 || o3tl::make_unsigned(nPara) >= maContents.size())
+ return nullptr;
+
+ const ContentInfo& rC = *maContents[nPara];
+ if (nPos >= rC.maCharAttribs.size())
+ // URL position is out-of-bound.
+ return nullptr;
+
+ size_t nCurPos = 0;
+ for (XEditAttribute const& rAttr : rC.maCharAttribs)
+ {
+ if (rAttr.GetItem()->Which() != EE_FEATURE_FIELD)
+ // Skip attributes that are not fields.
+ continue;
+
+ const SvxFieldItem* pField = static_cast<const SvxFieldItem*>(rAttr.GetItem());
+ const SvxFieldData* pFldData = pField->GetField();
+ if (nType != text::textfield::Type::UNSPECIFIED && nType != pFldData->GetClassId())
+ // Field type doesn't match. Skip it. UNSPECIFIED matches all field types.
+ continue;
+
+ if (nCurPos == nPos)
+ // Found it!
+ return pFldData;
+
+ ++nCurPos;
+ }
+
+ return nullptr; // field not found.
+}
+
+bool EditTextObjectImpl::HasField( sal_Int32 nType ) const
+{
+ size_t nParagraphs = maContents.size();
+ for (size_t nPara = 0; nPara < nParagraphs; ++nPara)
+ {
+ const ContentInfo& rC = *maContents[nPara];
+ size_t nAttrs = rC.maCharAttribs.size();
+ for (size_t nAttr = 0; nAttr < nAttrs; ++nAttr)
+ {
+ const XEditAttribute& rAttr = rC.maCharAttribs[nAttr];
+ if (rAttr.GetItem()->Which() != EE_FEATURE_FIELD)
+ continue;
+
+ if (nType == text::textfield::Type::UNSPECIFIED)
+ // Match any field type.
+ return true;
+
+ const SvxFieldData* pFldData = static_cast<const SvxFieldItem*>(rAttr.GetItem())->GetField();
+ if (pFldData && pFldData->GetClassId() == nType)
+ return true;
+ }
+ }
+ return false;
+}
+
+const SfxItemSet& EditTextObjectImpl::GetParaAttribs(sal_Int32 nPara) const
+{
+ const ContentInfo& rC = *maContents[nPara];
+ return rC.GetParaAttribs();
+}
+
+bool EditTextObjectImpl::RemoveCharAttribs( sal_uInt16 _nWhich )
+{
+ bool bChanged = false;
+
+ for ( size_t nPara = maContents.size(); nPara; )
+ {
+ ContentInfo& rC = *maContents[--nPara];
+
+ for (size_t nAttr = rC.maCharAttribs.size(); nAttr; )
+ {
+ XEditAttribute& rAttr = rC.maCharAttribs[--nAttr];
+ if ( !_nWhich || (rAttr.GetItem()->Which() == _nWhich) )
+ {
+ rC.maCharAttribs.erase(rC.maCharAttribs.begin()+nAttr);
+ bChanged = true;
+ }
+ }
+ }
+
+ if ( bChanged )
+ ClearPortionInfo();
+
+ return bChanged;
+}
+
+namespace {
+
+class FindByParagraph
+{
+ sal_Int32 mnPara;
+public:
+ explicit FindByParagraph(sal_Int32 nPara) : mnPara(nPara) {}
+ bool operator() (const editeng::Section& rAttr) const
+ {
+ return rAttr.mnParagraph == mnPara;
+ }
+};
+
+class FindBySectionStart
+{
+ sal_Int32 mnPara;
+ sal_Int32 mnStart;
+public:
+ FindBySectionStart(sal_Int32 nPara, sal_Int32 nStart) : mnPara(nPara), mnStart(nStart) {}
+ bool operator() (const editeng::Section& rAttr) const
+ {
+ return rAttr.mnParagraph == mnPara && rAttr.mnStart == mnStart;
+ }
+};
+
+}
+
+void EditTextObjectImpl::GetAllSections( std::vector<editeng::Section>& rAttrs ) const
+{
+ std::vector<editeng::Section> aAttrs;
+ aAttrs.reserve(maContents.size());
+ std::vector<size_t> aBorders;
+
+ for (size_t nPara = 0; nPara < maContents.size(); ++nPara)
+ {
+ aBorders.clear();
+ const ContentInfo& rC = *maContents[nPara];
+ aBorders.push_back(0);
+ aBorders.push_back(rC.GetText().getLength());
+ for (const XEditAttribute & rAttr : rC.maCharAttribs)
+ {
+ const SfxPoolItem* pItem = rAttr.GetItem();
+ if (!pItem)
+ continue;
+
+ aBorders.push_back(rAttr.GetStart());
+ aBorders.push_back(rAttr.GetEnd());
+ }
+
+ // Sort and remove duplicates for each paragraph.
+ std::sort(aBorders.begin(), aBorders.end());
+ auto itUniqueEnd = std::unique(aBorders.begin(), aBorders.end());
+ aBorders.erase(itUniqueEnd, aBorders.end());
+
+ // Create storage for each section. Note that this creates storage even
+ // for unformatted sections. The entries are sorted first by paragraph,
+ // then by section positions. They don't overlap with each other.
+
+ if (aBorders.size() == 1 && aBorders[0] == 0)
+ {
+ // Empty paragraph. Push an empty section.
+ aAttrs.emplace_back(nPara, 0, 0);
+ continue;
+ }
+
+ auto itBorder = aBorders.begin(), itBorderEnd = aBorders.end();
+ size_t nPrev = *itBorder;
+ size_t nCur;
+ for (++itBorder; itBorder != itBorderEnd; ++itBorder, nPrev = nCur)
+ {
+ nCur = *itBorder;
+ aAttrs.emplace_back(nPara, nPrev, nCur);
+ }
+ }
+
+ if (aAttrs.empty())
+ return;
+
+ // Go through all formatted paragraphs, and store format items.
+ std::vector<editeng::Section>::iterator itAttr = aAttrs.begin();
+ for (sal_Int32 nPara = 0; nPara < static_cast<sal_Int32>(maContents.size()); ++nPara)
+ {
+ const ContentInfo& rC = *maContents[nPara];
+
+ itAttr = std::find_if(itAttr, aAttrs.end(), FindByParagraph(nPara));
+ if (itAttr == aAttrs.end())
+ {
+ // This should never happen. There is a logic error somewhere...
+ assert(false);
+ return;
+ }
+
+ for (const XEditAttribute & rXAttr : rC.maCharAttribs)
+ {
+ const SfxPoolItem* pItem = rXAttr.GetItem();
+ if (!pItem)
+ continue;
+
+ sal_Int32 nStart = rXAttr.GetStart(), nEnd = rXAttr.GetEnd();
+
+ // Find the container whose start position matches.
+ std::vector<editeng::Section>::iterator itCurAttr = std::find_if(itAttr, aAttrs.end(), FindBySectionStart(nPara, nStart));
+ if (itCurAttr == aAttrs.end())
+ {
+ // This should never happen. There is a logic error somewhere...
+ assert(false);
+ return;
+ }
+
+ for (; itCurAttr != aAttrs.end() && itCurAttr->mnParagraph == nPara && itCurAttr->mnEnd <= nEnd; ++itCurAttr)
+ {
+ editeng::Section& rSecAttr = *itCurAttr;
+ // serious bug: will cause duplicate attributes to be exported
+ if (std::none_of(rSecAttr.maAttributes.begin(), rSecAttr.maAttributes.end(),
+ [&pItem](SfxPoolItem const*const pIt)
+ { return pIt->Which() == pItem->Which(); }))
+ {
+ rSecAttr.maAttributes.push_back(pItem);
+ }
+ else
+ {
+ SAL_WARN("editeng", "GetAllSections(): duplicate attribute suppressed");
+ }
+ }
+ }
+ }
+
+ rAttrs.swap(aAttrs);
+}
+
+void EditTextObjectImpl::GetStyleSheet(sal_Int32 nPara, OUString& rName, SfxStyleFamily& rFamily) const
+{
+ if (nPara < 0 || o3tl::make_unsigned(nPara) >= maContents.size())
+ return;
+
+ const ContentInfo& rC = *maContents[nPara];
+ rName = rC.GetStyle();
+ rFamily = rC.GetFamily();
+}
+
+void EditTextObjectImpl::SetStyleSheet(sal_Int32 nPara, const OUString& rName, const SfxStyleFamily& rFamily)
+{
+ if (nPara < 0 || o3tl::make_unsigned(nPara) >= maContents.size())
+ return;
+
+ ContentInfo& rC = *maContents[nPara];
+ rC.SetStyle(rName);
+ rC.SetFamily(rFamily);
+}
+
+bool EditTextObjectImpl::ImpChangeStyleSheets(
+ std::u16string_view rOldName, SfxStyleFamily eOldFamily,
+ const OUString& rNewName, SfxStyleFamily eNewFamily )
+{
+ const size_t nParagraphs = maContents.size();
+ bool bChanges = false;
+
+ for (size_t nPara = 0; nPara < nParagraphs; ++nPara)
+ {
+ ContentInfo& rC = *maContents[nPara];
+ if ( rC.GetFamily() == eOldFamily )
+ {
+ if ( rC.GetStyle() == rOldName )
+ {
+ rC.SetStyle(rNewName);
+ rC.SetFamily(eNewFamily);
+ bChanges = true;
+ }
+ }
+ }
+ return bChanges;
+}
+
+bool EditTextObjectImpl::ChangeStyleSheets(
+ std::u16string_view rOldName, SfxStyleFamily eOldFamily,
+ const OUString& rNewName, SfxStyleFamily eNewFamily)
+{
+ bool bChanges = ImpChangeStyleSheets( rOldName, eOldFamily, rNewName, eNewFamily );
+ if ( bChanges )
+ ClearPortionInfo();
+
+ return bChanges;
+}
+
+void EditTextObjectImpl::ChangeStyleSheetName( SfxStyleFamily eFamily,
+ std::u16string_view rOldName, const OUString& rNewName )
+{
+ ImpChangeStyleSheets( rOldName, eFamily, rNewName, eFamily );
+}
+
+bool EditTextObjectImpl::operator==( const EditTextObject& rCompare ) const
+{
+ return Equals(toImpl(rCompare), true);
+}
+
+bool EditTextObjectImpl::Equals( const EditTextObjectImpl& rCompare, bool bComparePool ) const
+{
+ if( this == &rCompare )
+ return true;
+
+ if( ( bComparePool && mpPool != rCompare.mpPool ) ||
+ ( meMetric != rCompare.meMetric ) ||
+ ( meUserType!= rCompare.meUserType ) ||
+ ( meScriptType != rCompare.meScriptType ) ||
+ ( mbVertical != rCompare.mbVertical ) ||
+ ( meRotation != rCompare.meRotation ) )
+ return false;
+
+ return std::equal(
+ maContents.begin(), maContents.end(), rCompare.maContents.begin(), rCompare.maContents.end(),
+ [bComparePool](const auto& c1, const auto& c2) { return c1->Equals(*c2, bComparePool); });
+}
+
+// #i102062#
+bool EditTextObjectImpl::isWrongListEqual(const EditTextObject& rComp) const
+{
+ const EditTextObjectImpl& rCompare = toImpl(rComp);
+ return std::equal(
+ maContents.begin(), maContents.end(), rCompare.maContents.begin(), rCompare.maContents.end(),
+ [](const auto& c1, const auto& c2) { return c1->isWrongListEqual(*c2); });
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/editobj2.hxx b/editeng/source/editeng/editobj2.hxx
new file mode 100644
index 0000000000..fd1f1437e9
--- /dev/null
+++ b/editeng/source/editeng/editobj2.hxx
@@ -0,0 +1,282 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in 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/editobj.hxx>
+#include <editeng/fieldupdater.hxx>
+#include <editeng/outliner.hxx>
+#include <editdoc.hxx>
+
+#include <svl/sharedstring.hxx>
+#include <svl/languageoptions.hxx>
+#include <tools/long.hxx>
+#include <tools/mapunit.hxx>
+
+#include <memory>
+#include <vector>
+
+namespace editeng {
+
+struct Section;
+
+}
+
+namespace svl {
+
+class SharedStringPool;
+
+}
+
+class XEditAttribute
+{
+private:
+ SfxPoolItemHolder maItemHolder;
+ sal_Int32 nStart;
+ sal_Int32 nEnd;
+
+public:
+ XEditAttribute(SfxItemPool&, const SfxPoolItem&, sal_Int32 nStart, sal_Int32 nEnd );
+
+ const SfxPoolItem* GetItem() const { return maItemHolder.getItem(); }
+
+ sal_Int32& GetStart() { return nStart; }
+ sal_Int32& GetEnd() { return nEnd; }
+
+ sal_Int32 GetStart() const { return nStart; }
+ sal_Int32 GetEnd() const { return nEnd; }
+
+ sal_Int32 GetLen() const { return nEnd-nStart; }
+
+ bool IsFeature() const;
+ void SetItem(SfxItemPool&, const SfxPoolItem&);
+
+ inline bool operator==( const XEditAttribute& rCompare ) const;
+};
+
+inline bool XEditAttribute::operator==( const XEditAttribute& rCompare ) const
+{
+ return (nStart == rCompare.nStart) &&
+ (nEnd == rCompare.nEnd) &&
+ SfxPoolItem::areSame(GetItem(), rCompare.GetItem());
+}
+
+struct XParaPortion
+{
+ tools::Long nHeight;
+ sal_uInt16 nFirstLineOffset;
+
+ EditLineList aLines;
+ TextPortionList aTextPortions;
+};
+
+class XParaPortionList
+{
+ typedef std::vector<std::unique_ptr<XParaPortion> > ListType;
+ ListType maList;
+
+ VclPtr<OutputDevice> pRefDevPtr;
+ double mfFontScaleX;
+ double mfFontScaleY;
+ double mfSpacingScaleX;
+ double mfSpacingScaleY;
+ sal_uInt32 nPaperWidth;
+
+public:
+ XParaPortionList(OutputDevice* pRefDev, sal_uInt32 nPW, double fFontScaleX, double fFontScaleY, double fSpacingScaleX, double fSpacingScaleY);
+
+ void push_back(XParaPortion* p);
+ const XParaPortion& operator[](size_t i) const;
+
+ OutputDevice* GetRefDevPtr() const { return pRefDevPtr; }
+ sal_uInt32 GetPaperWidth() const { return nPaperWidth; }
+ bool RefDevIsVirtual() const {return pRefDevPtr->IsVirtual();}
+ const MapMode& GetRefMapMode() const { return pRefDevPtr->GetMapMode(); }
+ double getFontScaleX() const { return mfFontScaleX; }
+ double getFontScaleY() const { return mfFontScaleY; }
+ double getSpacingScaleX() const { return mfSpacingScaleX; }
+ double getSpacingScaleY() const { return mfSpacingScaleY; }
+};
+
+class ContentInfo
+{
+ friend class EditTextObjectImpl;
+
+private:
+ svl::SharedString maText;
+ OUString aStyle;
+
+ std::vector<XEditAttribute> maCharAttribs;
+ SfxStyleFamily eFamily;
+ SfxItemSetFixed<EE_PARA_START, EE_CHAR_END> aParaAttribs;
+ std::unique_ptr<WrongList>
+ mpWrongs;
+
+ ContentInfo( SfxItemPool& rPool );
+ ContentInfo( const ContentInfo& rCopyFrom, SfxItemPool& rPoolToUse );
+
+public:
+ ~ContentInfo();
+ ContentInfo(const ContentInfo&) = delete;
+ ContentInfo& operator=(const ContentInfo&) = delete;
+
+ void NormalizeString( svl::SharedStringPool& rPool );
+ const svl::SharedString& GetSharedString() const { return maText;}
+ OUString GetText() const;
+ void SetText( const OUString& rStr );
+
+ void dumpAsXml(xmlTextWriterPtr pWriter) const;
+
+ const std::vector<XEditAttribute>& GetCharAttribs() const { return maCharAttribs; }
+ std::vector<XEditAttribute>& GetCharAttribs() { return maCharAttribs; }
+
+ const OUString& GetStyle() const { return aStyle; }
+ SfxStyleFamily GetFamily() const { return eFamily; }
+
+ void SetStyle(const OUString& rStyle) { aStyle = rStyle; }
+ void SetFamily(const SfxStyleFamily& rFamily) { eFamily = rFamily; }
+
+ const SfxItemSet& GetParaAttribs() const { return aParaAttribs; }
+ SfxItemSet& GetParaAttribs() { return aParaAttribs; }
+
+ const WrongList* GetWrongList() const;
+ void SetWrongList( WrongList* p );
+ bool Equals( const ContentInfo& rCompare, bool bComparePool ) const;
+
+ // #i102062#
+ bool isWrongListEqual(const ContentInfo& rCompare) const;
+
+#if DEBUG_EDIT_ENGINE
+ void Dump() const;
+#endif
+};
+
+class EditTextObjectImpl final : public EditTextObject
+{
+public:
+ typedef std::vector<std::unique_ptr<ContentInfo> > ContentInfosType;
+
+private:
+ ContentInfosType maContents;
+ rtl::Reference<SfxItemPool> mpPool;
+ std::unique_ptr<XParaPortionList> mpPortionInfo;
+
+ OutlinerMode meUserType;
+ SvtScriptType meScriptType;
+ TextRotation meRotation;
+ MapUnit meMetric;
+
+ bool mbVertical;
+
+ bool ImpChangeStyleSheets( std::u16string_view rOldName, SfxStyleFamily eOldFamily,
+ const OUString& rNewName, SfxStyleFamily eNewFamily );
+
+public:
+ EditTextObjectImpl(SfxItemPool* pPool, MapUnit eDefaultMetric, bool bVertical,
+ TextRotation eRotation, SvtScriptType eScriptType);
+ EditTextObjectImpl( const EditTextObjectImpl& r );
+ virtual ~EditTextObjectImpl() override;
+
+ EditTextObjectImpl& operator=(const EditTextObjectImpl&) = delete;
+
+ virtual OutlinerMode GetUserType() const override { return meUserType;}
+ virtual void SetUserType( OutlinerMode n ) override;
+
+ virtual void NormalizeString( svl::SharedStringPool& rPool ) override;
+ virtual std::vector<svl::SharedString> GetSharedStrings() const override;
+
+ virtual bool IsEffectivelyVertical() const override;
+ virtual bool GetVertical() const override;
+ virtual bool IsTopToBottom() const override;
+ virtual void SetVertical( bool bVert) override;
+ virtual void SetRotation(TextRotation nRotation) override;
+ virtual TextRotation GetRotation() const override;
+
+ virtual SvtScriptType GetScriptType() const override { return meScriptType;}
+
+ virtual std::unique_ptr<EditTextObject> Clone() const override;
+
+ ContentInfo* CreateAndInsertContent();
+ XEditAttribute CreateAttrib( const SfxPoolItem& rItem, sal_Int32 nStart, sal_Int32 nEnd );
+
+ ContentInfosType& GetContents() { return maContents;}
+ const ContentInfosType& GetContents() const { return maContents;}
+ SfxItemPool* GetPool() { return mpPool.get(); }
+ virtual const SfxItemPool* GetPool() const override { return mpPool.get(); }
+ XParaPortionList* GetPortionInfo() const { return mpPortionInfo.get(); }
+ void SetPortionInfo( std::unique_ptr<XParaPortionList> pP )
+ { mpPortionInfo = std::move(pP); }
+
+ virtual sal_Int32 GetParagraphCount() const override;
+ virtual OUString GetText(sal_Int32 nParagraph) const override;
+
+ virtual void ClearPortionInfo() override;
+
+ virtual bool HasOnlineSpellErrors() const override;
+
+ virtual void GetCharAttribs( sal_Int32 nPara, std::vector<EECharAttrib>& rLst ) const override;
+
+ virtual bool RemoveCharAttribs( sal_uInt16 nWhich ) override;
+
+ virtual void GetAllSections( std::vector<editeng::Section>& rAttrs ) const override;
+
+ virtual bool IsFieldObject() const override;
+ virtual const SvxFieldItem* GetField() const override;
+ virtual const SvxFieldData* GetFieldData(sal_Int32 nPara, size_t nPos, sal_Int32 nType) const override;
+
+ virtual bool HasField( sal_Int32 nType = css::text::textfield::Type::UNSPECIFIED ) const override;
+
+ virtual const SfxItemSet& GetParaAttribs(sal_Int32 nPara) const override;
+
+ virtual void GetStyleSheet(sal_Int32 nPara, OUString& rName, SfxStyleFamily& eFamily) const override;
+ virtual void SetStyleSheet(sal_Int32 nPara, const OUString& rName, const SfxStyleFamily& eFamily) override;
+ virtual bool ChangeStyleSheets(
+ std::u16string_view rOldName, SfxStyleFamily eOldFamily, const OUString& rNewName, SfxStyleFamily eNewFamily) override;
+ virtual void ChangeStyleSheetName(SfxStyleFamily eFamily, std::u16string_view rOldName, const OUString& rNewName) override;
+
+ virtual editeng::FieldUpdater GetFieldUpdater() override { return editeng::FieldUpdater(*this); }
+
+ bool HasMetric() const { return meMetric != MapUnit::LASTENUMDUMMY; }
+ MapUnit GetMetric() const { return meMetric; }
+
+ virtual bool operator==( const EditTextObject& rCompare ) const override;
+ bool Equals( const EditTextObjectImpl& rCompare, bool bComparePool ) const;
+
+ // #i102062#
+ virtual bool isWrongListEqual(const EditTextObject& rCompare) const override;
+
+#if DEBUG_EDIT_ENGINE
+ virtual void Dump() const override;
+#endif
+ virtual void dumpAsXml(xmlTextWriterPtr pWriter) const override;
+};
+
+inline EditTextObjectImpl& toImpl(EditTextObject& rObj)
+{
+ assert(dynamic_cast<EditTextObjectImpl*>(&rObj));
+ return static_cast<EditTextObjectImpl&>(rObj);
+}
+
+inline const EditTextObjectImpl& toImpl(const EditTextObject& rObj)
+{
+ assert(dynamic_cast<const EditTextObjectImpl*>(&rObj));
+ return static_cast<const EditTextObjectImpl&>(rObj);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/editsel.cxx b/editeng/source/editeng/editsel.cxx
new file mode 100644
index 0000000000..3aeed7a6e3
--- /dev/null
+++ b/editeng/source/editeng/editsel.cxx
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "editsel.hxx"
+#include "impedit.hxx"
+#include <editeng/editview.hxx>
+
+
+
+EditSelFunctionSet::EditSelFunctionSet()
+{
+ pCurView = nullptr;
+}
+
+void EditSelFunctionSet::CreateAnchor()
+{
+ if ( pCurView )
+ pCurView->pImpEditView->CreateAnchor();
+}
+
+void EditSelFunctionSet::DestroyAnchor()
+{
+ // Only with multiple selection
+}
+
+void EditSelFunctionSet::SetCursorAtPoint( const Point& rPointPixel, bool )
+{
+ if ( pCurView )
+ pCurView->pImpEditView->SetCursorAtPoint( rPointPixel );
+}
+
+bool EditSelFunctionSet::IsSelectionAtPoint( const Point& rPointPixel )
+{
+ if ( pCurView )
+ return pCurView->pImpEditView->IsSelectionAtPoint( rPointPixel );
+
+ return false;
+}
+
+void EditSelFunctionSet::DeselectAtPoint( const Point& )
+{
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+// ! Implement when multiple selection is possible !
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+}
+
+void EditSelFunctionSet::BeginDrag()
+{
+ // Only with multiple selection
+}
+
+
+void EditSelFunctionSet::DeselectAll()
+{
+ if ( pCurView )
+ pCurView->pImpEditView->DeselectAll();
+}
+
+
+
+EditSelectionEngine::EditSelectionEngine() : SelectionEngine( nullptr )
+{
+ SetSelectionMode( SelectionMode::Range );
+ EnableDrag( true );
+}
+
+void EditSelectionEngine::SetCurView( EditView* pNewView )
+{
+ if ( GetFunctionSet() )
+ const_cast<EditSelFunctionSet*>(static_cast<const EditSelFunctionSet*>(GetFunctionSet()))->SetCurView( pNewView );
+
+ if ( pNewView )
+ SetWindow( pNewView->GetWindow() );
+ else
+ SetWindow( nullptr );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/editsel.hxx b/editeng/source/editeng/editsel.hxx
new file mode 100644
index 0000000000..8d2adfb670
--- /dev/null
+++ b/editeng/source/editeng/editsel.hxx
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/seleng.hxx>
+
+class EditView;
+
+class EditSelFunctionSet: public FunctionSet
+{
+private:
+ EditView* pCurView;
+
+public:
+ EditSelFunctionSet();
+
+ virtual void BeginDrag() override;
+
+ virtual void CreateAnchor() override;
+ virtual void DestroyAnchor() override;
+
+ virtual void SetCursorAtPoint( const Point& rPointPixel, bool bDontSelectAtCursor = false ) override;
+
+ virtual bool IsSelectionAtPoint( const Point& rPointPixel ) override;
+ virtual void DeselectAtPoint( const Point& rPointPixel ) override;
+ virtual void DeselectAll() override;
+
+ void SetCurView( EditView* pView ) { pCurView = pView; }
+};
+
+class EditSelectionEngine : public SelectionEngine
+{
+private:
+
+public:
+ EditSelectionEngine();
+
+ void SetCurView( EditView* pNewView );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/editstt2.hxx b/editeng/source/editeng/editstt2.hxx
new file mode 100644
index 0000000000..334622b234
--- /dev/null
+++ b/editeng/source/editeng/editstt2.hxx
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <editeng/editstat.hxx>
+
+class InternalEditStatus : public EditStatus
+{
+
+public:
+ void TurnOnFlags( EEControlBits nFlags )
+ { nControlBits |= nFlags; }
+
+ void TurnOffFlags( EEControlBits nFlags )
+ { nControlBits &= ~nFlags; }
+
+ bool UseCharAttribs() const
+ { return bool( nControlBits & EEControlBits::USECHARATTRIBS ); }
+
+ bool UseIdleFormatter() const
+ { return bool( nControlBits & EEControlBits::DOIDLEFORMAT); }
+
+ bool AllowPasteSpecial() const
+ { return bool( nControlBits & EEControlBits::PASTESPECIAL ); }
+
+ bool DoAutoIndenting() const
+ { return bool( nControlBits & EEControlBits::AUTOINDENTING ); }
+
+ bool DoUndoAttribs() const
+ { return bool( nControlBits & EEControlBits::UNDOATTRIBS ); }
+
+ bool OneCharPerLine() const
+ { return bool( nControlBits & EEControlBits::ONECHARPERLINE ); }
+
+ bool IsOutliner() const
+ { return bool( nControlBits & EEControlBits::OUTLINER ); }
+
+ bool DoNotUseColors() const
+ { return bool( nControlBits & EEControlBits::NOCOLORS ); }
+
+ bool AllowBigObjects() const
+ { return bool( nControlBits & EEControlBits::ALLOWBIGOBJS ); }
+
+ bool DoOnlineSpelling() const
+ { return bool( nControlBits & EEControlBits::ONLINESPELLING ); }
+
+ bool DoStretch() const
+ { return bool( nControlBits & EEControlBits::STRETCHING ); }
+
+ bool AutoPageSize() const
+ { return bool( nControlBits & EEControlBits::AUTOPAGESIZE ); }
+ bool AutoPageWidth() const
+ { return bool( nControlBits & EEControlBits::AUTOPAGESIZEX ); }
+ bool AutoPageHeight() const
+ { return bool( nControlBits & EEControlBits::AUTOPAGESIZEY ); }
+
+ bool MarkNonUrlFields() const
+ { return bool( nControlBits & EEControlBits::MARKNONURLFIELDS ); }
+
+ bool MarkUrlFields() const
+ { return bool( nControlBits & EEControlBits::MARKURLFIELDS ); }
+
+ bool DoImportRTFStyleSheets() const
+ { return bool( nControlBits & EEControlBits::RTFSTYLESHEETS ); }
+
+ bool DoAutoCorrect() const
+ { return bool( nControlBits & EEControlBits::AUTOCORRECT ); }
+
+ bool DoAutoComplete() const
+ { return bool( nControlBits & EEControlBits::AUTOCOMPLETE ); }
+
+ bool DoFormat100() const
+ { return bool( nControlBits & EEControlBits::FORMAT100 ); }
+
+ bool ULSpaceSummation() const
+ { return bool( nControlBits & EEControlBits::ULSPACESUMMATION ); }
+
+ bool IsSingleLine() const
+ { return bool( nControlBits & EEControlBits::SINGLELINE ); }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/editundo.cxx b/editeng/source/editeng/editundo.cxx
new file mode 100644
index 0000000000..5854d1683e
--- /dev/null
+++ b/editeng/source/editeng/editundo.cxx
@@ -0,0 +1,661 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include "impedit.hxx"
+#include "editundo.hxx"
+#include <editeng/editview.hxx>
+#include <editeng/editeng.hxx>
+#include <utility>
+#include <osl/diagnose.h>
+
+
+static void lcl_DoSetSelection( EditView const * pView, sal_uInt16 nPara )
+{
+ EPaM aEPaM( nPara, 0 );
+ EditPaM aPaM( pView->GetImpEditEngine()->CreateEditPaM( aEPaM ) );
+ aPaM.SetIndex( aPaM.GetNode()->Len() );
+ EditSelection aSel( aPaM, aPaM );
+ pView->GetImpEditView()->SetEditSelection( aSel );
+}
+
+EditUndoManager::EditUndoManager(sal_uInt16 nMaxUndoActionCount )
+: SfxUndoManager(nMaxUndoActionCount),
+ mpEditEngine(nullptr)
+{
+}
+
+void EditUndoManager::SetEditEngine(EditEngine* pNew)
+{
+ mpEditEngine = pNew;
+}
+
+bool EditUndoManager::Undo()
+{
+ if ( !mpEditEngine || GetUndoActionCount() == 0 )
+ return false;
+
+ DBG_ASSERT( mpEditEngine->GetActiveView(), "Active View?" );
+
+ if ( !mpEditEngine->GetActiveView() )
+ {
+ if (!mpEditEngine->GetEditViews().empty())
+ mpEditEngine->SetActiveView(mpEditEngine->GetEditViews()[0]);
+ else
+ {
+ OSL_FAIL("Undo in engine is not possible without a View! ");
+ return false;
+ }
+ }
+
+ mpEditEngine->GetActiveView()->GetImpEditView()->DrawSelectionXOR(); // Remove the old selection
+
+ mpEditEngine->SetUndoMode( true );
+ bool bDone = SfxUndoManager::Undo();
+ mpEditEngine->SetUndoMode( false );
+
+ EditSelection aNewSel( mpEditEngine->GetActiveView()->GetImpEditView()->GetEditSelection() );
+ DBG_ASSERT( !aNewSel.IsInvalid(), "Invalid selection after Undo () ");
+ DBG_ASSERT( !aNewSel.DbgIsBuggy( mpEditEngine->GetEditDoc() ), "Broken selection afte Undo () ");
+
+ aNewSel.Min() = aNewSel.Max();
+ mpEditEngine->GetActiveView()->GetImpEditView()->SetEditSelection( aNewSel );
+ if (mpEditEngine->IsUpdateLayout())
+ mpEditEngine->FormatAndLayout( mpEditEngine->GetActiveView(), true );
+
+ return bDone;
+}
+
+bool EditUndoManager::Redo()
+{
+ if ( !mpEditEngine || GetRedoActionCount() == 0 )
+ return false;
+
+ DBG_ASSERT( mpEditEngine->GetActiveView(), "Active View?" );
+
+ if ( !mpEditEngine->GetActiveView() )
+ {
+ if (!mpEditEngine->GetEditViews().empty())
+ mpEditEngine->SetActiveView(mpEditEngine->GetEditViews()[0]);
+ else
+ {
+ OSL_FAIL( "Redo in Engine without View not possible!" );
+ return false;
+ }
+ }
+
+ mpEditEngine->GetActiveView()->GetImpEditView()->DrawSelectionXOR(); // Remove the old selection
+
+ mpEditEngine->SetUndoMode( true );
+ bool bDone = SfxUndoManager::Redo();
+ mpEditEngine->SetUndoMode( false );
+
+ EditSelection aNewSel( mpEditEngine->GetActiveView()->GetImpEditView()->GetEditSelection() );
+ DBG_ASSERT( !aNewSel.IsInvalid(), "Invalid selection after Undo () ");
+ DBG_ASSERT( !aNewSel.DbgIsBuggy( mpEditEngine->GetEditDoc() ), "Broken selection afte Undo () ");
+
+ aNewSel.Min() = aNewSel.Max();
+ mpEditEngine->GetActiveView()->GetImpEditView()->SetEditSelection( aNewSel );
+ if (mpEditEngine->IsUpdateLayout())
+ mpEditEngine->FormatAndLayout( mpEditEngine->GetActiveView() );
+
+ return bDone;
+}
+
+EditUndo::EditUndo(sal_uInt16 nI, EditEngine* pEE) :
+ nId(nI), mnViewShellId(-1), mpEditEngine(pEE)
+{
+ const EditView* pEditView = mpEditEngine ? mpEditEngine->GetActiveView() : nullptr;
+ const OutlinerViewShell* pViewShell = pEditView ? pEditView->GetImpEditView()->GetViewShell() : nullptr;
+ if (pViewShell)
+ mnViewShellId = pViewShell->GetViewShellId();
+}
+
+EditUndo::~EditUndo()
+{
+}
+
+
+sal_uInt16 EditUndo::GetId() const
+{
+ return nId;
+}
+
+bool EditUndo::CanRepeat(SfxRepeatTarget&) const
+{
+ return false;
+}
+
+OUString EditUndo::GetComment() const
+{
+ OUString aComment;
+
+ if (mpEditEngine)
+ aComment = mpEditEngine->GetUndoComment( GetId() );
+
+ return aComment;
+}
+
+ViewShellId EditUndo::GetViewShellId() const
+{
+ return mnViewShellId;
+}
+
+EditUndoDelContent::EditUndoDelContent(
+ EditEngine* pEE, ContentNode* pNode, sal_Int32 nPortion) :
+ EditUndo(EDITUNDO_DELCONTENT, pEE),
+ bDelObject(true),
+ nNode(nPortion),
+ pContentNode(pNode) {}
+
+EditUndoDelContent::~EditUndoDelContent()
+{
+ if ( bDelObject )
+ delete pContentNode;
+}
+
+void EditUndoDelContent::Undo()
+{
+ DBG_ASSERT( GetEditEngine()->GetActiveView(), "Undo/Redo: No Active View!" );
+ GetEditEngine()->InsertContent( pContentNode, nNode );
+ bDelObject = false; // belongs to the Engine again
+ EditSelection aSel( EditPaM( pContentNode, 0 ), EditPaM( pContentNode, pContentNode->Len() ) );
+ GetEditEngine()->GetActiveView()->GetImpEditView()->SetEditSelection(aSel);
+}
+
+void EditUndoDelContent::Redo()
+{
+ DBG_ASSERT( GetEditEngine()->GetActiveView(), "Undo/Redo: No Active View!" );
+
+ EditEngine* pEE = GetEditEngine();
+
+ // pNode is no longer correct, if the paragraphs where merged
+ // in between Undos
+ pContentNode = pEE->GetEditDoc().GetObject( nNode );
+ DBG_ASSERT( pContentNode, "EditUndoDelContent::Redo(): Node?!" );
+
+ pEE->RemoveParaPortion(nNode);
+
+ // Do not delete node, depends on the undo!
+ pEE->GetEditDoc().Release( nNode );
+ if (pEE->IsCallParaInsertedOrDeleted())
+ pEE->ParagraphDeleted( nNode );
+
+ DeletedNodeInfo* pInf = new DeletedNodeInfo( pContentNode, nNode );
+ pEE->AppendDeletedNodeInfo(pInf);
+ pEE->UpdateSelections();
+
+ ContentNode* pN = ( nNode < pEE->GetEditDoc().Count() )
+ ? pEE->GetEditDoc().GetObject( nNode )
+ : pEE->GetEditDoc().GetObject( nNode-1 );
+ DBG_ASSERT( pN && ( pN != pContentNode ), "?! RemoveContent !? " );
+ EditPaM aPaM( pN, pN->Len() );
+
+ bDelObject = true; // belongs to the Engine again
+
+ pEE->GetActiveView()->GetImpEditView()->SetEditSelection( EditSelection( aPaM, aPaM ) );
+}
+
+EditUndoConnectParas::EditUndoConnectParas(
+ EditEngine* pEE, sal_Int32 nN, sal_uInt16 nSP,
+ SfxItemSet _aLeftParaAttribs, SfxItemSet _aRightParaAttribs,
+ const SfxStyleSheet* pLeftStyle, const SfxStyleSheet* pRightStyle, bool bBkwrd) :
+ EditUndo(EDITUNDO_CONNECTPARAS, pEE),
+ nNode(nN),
+ nSepPos(nSP),
+ aLeftParaAttribs(std::move(_aLeftParaAttribs)),
+ aRightParaAttribs(std::move(_aRightParaAttribs)),
+ eLeftStyleFamily(SfxStyleFamily::All),
+ eRightStyleFamily(SfxStyleFamily::All),
+ bBackward(bBkwrd)
+{
+ if ( pLeftStyle )
+ {
+ aLeftStyleName = pLeftStyle->GetName();
+ eLeftStyleFamily = pLeftStyle->GetFamily();
+ }
+ if ( pRightStyle )
+ {
+ aRightStyleName = pRightStyle->GetName();
+ eRightStyleFamily = pRightStyle->GetFamily();
+ }
+}
+
+EditUndoConnectParas::~EditUndoConnectParas()
+{
+}
+
+void EditUndoConnectParas::Undo()
+{
+ DBG_ASSERT( GetEditEngine()->GetActiveView(), "Undo/Redo: No Active View!" );
+
+ // For SplitContent ParagraphInserted can not be called yet because the
+ // Outliner relies on the attributes to initialize the depth
+
+ bool bCall = GetEditEngine()->IsCallParaInsertedOrDeleted();
+ GetEditEngine()->SetCallParaInsertedOrDeleted(false);
+
+ EditPaM aPaM = GetEditEngine()->SplitContent(nNode, nSepPos);
+
+ GetEditEngine()->SetCallParaInsertedOrDeleted( bCall );
+ if (GetEditEngine()->IsCallParaInsertedOrDeleted())
+ {
+ GetEditEngine()->ParagraphInserted( nNode+1 );
+ GetEditEngine()->SetParaAttribs( nNode+1, aRightParaAttribs );
+ }
+
+ // Calling SetParaAttribs is effective only after ParagraphInserted
+ GetEditEngine()->SetParaAttribs( nNode, aLeftParaAttribs );
+
+ if (GetEditEngine()->GetStyleSheetPool())
+ {
+ if ( !aLeftStyleName.isEmpty() )
+ GetEditEngine()->SetStyleSheet( nNode, static_cast<SfxStyleSheet*>(GetEditEngine()->GetStyleSheetPool()->Find( aLeftStyleName, eLeftStyleFamily )) );
+ if ( !aRightStyleName.isEmpty() )
+ GetEditEngine()->SetStyleSheet( nNode+1, static_cast<SfxStyleSheet*>(GetEditEngine()->GetStyleSheetPool()->Find( aRightStyleName, eRightStyleFamily )) );
+ }
+
+ GetEditEngine()->GetActiveView()->GetImpEditView()->SetEditSelection( EditSelection( aPaM, aPaM ) );
+}
+
+void EditUndoConnectParas::Redo()
+{
+ DBG_ASSERT( GetEditEngine()->GetActiveView(), "Undo/Redo: Np Active View!" );
+ EditPaM aPaM = GetEditEngine()->ConnectContents( nNode, bBackward );
+
+ GetEditEngine()->GetActiveView()->GetImpEditView()->SetEditSelection( EditSelection( aPaM, aPaM ) );
+}
+
+EditUndoSplitPara::EditUndoSplitPara(
+ EditEngine* pEE, sal_Int32 nN, sal_uInt16 nSP) :
+ EditUndo(EDITUNDO_SPLITPARA, pEE),
+ nNode(nN), nSepPos(nSP) {}
+
+EditUndoSplitPara::~EditUndoSplitPara() {}
+
+void EditUndoSplitPara::Undo()
+{
+ DBG_ASSERT( GetEditEngine()->GetActiveView(), "Undo/Redo: No Active View!" );
+ EditPaM aPaM = GetEditEngine()->ConnectContents(nNode, false);
+ GetEditEngine()->GetActiveView()->GetImpEditView()->SetEditSelection( EditSelection( aPaM, aPaM ) );
+}
+
+void EditUndoSplitPara::Redo()
+{
+ DBG_ASSERT( GetEditEngine()->GetActiveView(), "Undo/Redo: No Active View!" );
+ EditPaM aPaM = GetEditEngine()->SplitContent(nNode, nSepPos);
+ GetEditEngine()->GetActiveView()->GetImpEditView()->SetEditSelection( EditSelection( aPaM, aPaM ) );
+}
+
+EditUndoInsertChars::EditUndoInsertChars(
+ EditEngine* pEE, const EPaM& rEPaM, OUString aStr) :
+ EditUndo(EDITUNDO_INSERTCHARS, pEE),
+ aEPaM(rEPaM),
+ aText(std::move(aStr)) {}
+
+void EditUndoInsertChars::Undo()
+{
+ DBG_ASSERT( GetEditEngine()->GetActiveView(), "Undo/Redo: No Active View!" );
+ EditPaM aPaM = GetEditEngine()->CreateEditPaM(aEPaM);
+ EditSelection aSel( aPaM, aPaM );
+ aSel.Max().SetIndex( aSel.Max().GetIndex() + aText.getLength() );
+ EditPaM aNewPaM( GetEditEngine()->DeleteSelection(aSel) );
+ GetEditEngine()->GetActiveView()->GetImpEditView()->SetEditSelection( EditSelection( aNewPaM, aNewPaM ) );
+}
+
+void EditUndoInsertChars::Redo()
+{
+ DBG_ASSERT( GetEditEngine()->GetActiveView(), "Undo/Redo: No Active View!" );
+ EditPaM aPaM = GetEditEngine()->CreateEditPaM(aEPaM);
+ GetEditEngine()->InsertText(EditSelection(aPaM, aPaM), aText);
+ EditPaM aNewPaM( aPaM );
+ aNewPaM.SetIndex( aNewPaM.GetIndex() + aText.getLength() );
+ GetEditEngine()->GetActiveView()->GetImpEditView()->SetEditSelection( EditSelection( aPaM, aNewPaM ) );
+}
+
+bool EditUndoInsertChars::Merge( SfxUndoAction* pNextAction )
+{
+ EditUndoInsertChars* pNext = dynamic_cast<EditUndoInsertChars*>(pNextAction);
+ if (!pNext)
+ return false;
+
+ if ( aEPaM.nPara != pNext->aEPaM.nPara )
+ return false;
+
+ if ( ( aEPaM.nIndex + aText.getLength() ) == pNext->aEPaM.nIndex )
+ {
+ aText += pNext->aText;
+ return true;
+ }
+ return false;
+}
+
+EditUndoRemoveChars::EditUndoRemoveChars(
+ EditEngine* pEE, const EPaM& rEPaM, OUString aStr) :
+ EditUndo(EDITUNDO_REMOVECHARS, pEE),
+ aEPaM(rEPaM), aText(std::move(aStr)) {}
+
+void EditUndoRemoveChars::Undo()
+{
+ DBG_ASSERT( GetEditEngine()->GetActiveView(), "Undo/Redo: No Active View!" );
+ EditPaM aPaM = GetEditEngine()->CreateEditPaM(aEPaM);
+ EditSelection aSel( aPaM, aPaM );
+ GetEditEngine()->InsertText(aSel, aText);
+ aSel.Max().SetIndex( aSel.Max().GetIndex() + aText.getLength() );
+ GetEditEngine()->GetActiveView()->GetImpEditView()->SetEditSelection(aSel);
+}
+
+void EditUndoRemoveChars::Redo()
+{
+ DBG_ASSERT( GetEditEngine()->GetActiveView(), "Undo/Redo: No Active View!" );
+ EditPaM aPaM = GetEditEngine()->CreateEditPaM(aEPaM);
+ EditSelection aSel( aPaM, aPaM );
+ aSel.Max().SetIndex( aSel.Max().GetIndex() + aText.getLength() );
+ EditPaM aNewPaM = GetEditEngine()->DeleteSelection(aSel);
+ GetEditEngine()->GetActiveView()->GetImpEditView()->SetEditSelection(aNewPaM);
+}
+
+EditUndoInsertFeature::EditUndoInsertFeature(
+ EditEngine* pEE, const EPaM& rEPaM, const SfxPoolItem& rFeature) :
+ EditUndo(EDITUNDO_INSERTFEATURE, pEE),
+ aEPaM(rEPaM),
+ pFeature(rFeature.Clone())
+{
+ DBG_ASSERT( pFeature, "Feature could not be duplicated: EditUndoInsertFeature" );
+}
+
+EditUndoInsertFeature::~EditUndoInsertFeature()
+{
+}
+
+void EditUndoInsertFeature::Undo()
+{
+ DBG_ASSERT( GetEditEngine()->GetActiveView(), "Undo/Redo: No Active View!" );
+ EditPaM aPaM = GetEditEngine()->CreateEditPaM(aEPaM);
+ EditSelection aSel( aPaM, aPaM );
+ // Attributes are then corrected implicitly by the document ...
+ aSel.Max().SetIndex( aSel.Max().GetIndex()+1 );
+ GetEditEngine()->DeleteSelection(aSel);
+ aSel.Max().SetIndex( aSel.Max().GetIndex()-1 ); // For Selection
+ GetEditEngine()->GetActiveView()->GetImpEditView()->SetEditSelection(aSel);
+}
+
+void EditUndoInsertFeature::Redo()
+{
+ DBG_ASSERT( GetEditEngine()->GetActiveView(), "Undo/Redo: No Active View!" );
+ EditPaM aPaM = GetEditEngine()->CreateEditPaM(aEPaM);
+ EditSelection aSel( aPaM, aPaM );
+ GetEditEngine()->InsertFeature(aSel, *pFeature);
+ if ( pFeature->Which() == EE_FEATURE_FIELD )
+ GetEditEngine()->UpdateFieldsOnly();
+ aSel.Max().SetIndex( aSel.Max().GetIndex()+1 );
+ GetEditEngine()->GetActiveView()->GetImpEditView()->SetEditSelection(aSel);
+}
+
+EditUndoMoveParagraphs::EditUndoMoveParagraphs(
+ EditEngine* pEE, const Range& rParas, sal_Int32 n) :
+ EditUndo(EDITUNDO_MOVEPARAGRAPHS, pEE), nParagraphs(rParas), nDest(n) {}
+
+EditUndoMoveParagraphs::~EditUndoMoveParagraphs() {}
+
+void EditUndoMoveParagraphs::Undo()
+{
+ DBG_ASSERT( GetEditEngine()->GetActiveView(), "Undo/Redo: No Active View!" );
+ Range aTmpRange( nParagraphs );
+ tools::Long nTmpDest = aTmpRange.Min();
+
+ tools::Long nDiff = nDest - aTmpRange.Min();
+ aTmpRange.Min() += nDiff;
+ aTmpRange.Max() += nDiff;
+
+ if ( nParagraphs.Min() < static_cast<tools::Long>(nDest) )
+ {
+ tools::Long nLen = aTmpRange.Len();
+ aTmpRange.Min() -= nLen;
+ aTmpRange.Max() -= nLen;
+ }
+ else
+ nTmpDest += aTmpRange.Len();
+
+ EditSelection aNewSel = GetEditEngine()->MoveParagraphs(aTmpRange, nTmpDest);
+ GetEditEngine()->GetActiveView()->GetImpEditView()->SetEditSelection( aNewSel );
+}
+
+void EditUndoMoveParagraphs::Redo()
+{
+ DBG_ASSERT( GetEditEngine()->GetActiveView(), "Undo/Redo: No Active View!" );
+ EditSelection aNewSel = GetEditEngine()->MoveParagraphs(nParagraphs, nDest);
+ GetEditEngine()->GetActiveView()->GetImpEditView()->SetEditSelection( aNewSel );
+}
+
+EditUndoSetStyleSheet::EditUndoSetStyleSheet(
+ EditEngine* pEE, sal_Int32 nP, OUString _aPrevName, SfxStyleFamily ePrevFam,
+ OUString _aNewName, SfxStyleFamily eNewFam, SfxItemSet _aPrevParaAttribs) :
+ EditUndo(EDITUNDO_STYLESHEET, pEE),
+ nPara(nP),
+ aPrevName(std::move(_aPrevName)),
+ aNewName(std::move(_aNewName)),
+ ePrevFamily(ePrevFam),
+ eNewFamily(eNewFam),
+ aPrevParaAttribs(std::move(_aPrevParaAttribs))
+{
+}
+
+EditUndoSetStyleSheet::~EditUndoSetStyleSheet()
+{
+}
+
+void EditUndoSetStyleSheet::Undo()
+{
+ DBG_ASSERT( GetEditEngine()->GetActiveView(), "Undo/Redo: No Active View!" );
+ GetEditEngine()->SetStyleSheet( nPara, static_cast<SfxStyleSheet*>(GetEditEngine()->GetStyleSheetPool()->Find( aPrevName, ePrevFamily )) );
+ GetEditEngine()->SetParaAttribsOnly( nPara, aPrevParaAttribs );
+ lcl_DoSetSelection( GetEditEngine()->GetActiveView(), nPara );
+}
+
+void EditUndoSetStyleSheet::Redo()
+{
+ DBG_ASSERT( GetEditEngine()->GetActiveView(), "Undo/Redo: No Active View!" );
+ GetEditEngine()->SetStyleSheet( nPara, static_cast<SfxStyleSheet*>(GetEditEngine()->GetStyleSheetPool()->Find( aNewName, eNewFamily )) );
+ lcl_DoSetSelection( GetEditEngine()->GetActiveView(), nPara );
+}
+
+EditUndoSetParaAttribs::EditUndoSetParaAttribs(
+ EditEngine* pEE, sal_Int32 nP, SfxItemSet _aPrevItems, SfxItemSet _aNewItems) :
+ EditUndo(EDITUNDO_PARAATTRIBS, pEE),
+ nPara(nP),
+ aPrevItems(std::move(_aPrevItems)),
+ aNewItems(std::move(_aNewItems)) {}
+
+EditUndoSetParaAttribs::~EditUndoSetParaAttribs() {}
+
+void EditUndoSetParaAttribs::Undo()
+{
+ DBG_ASSERT( GetEditEngine()->GetActiveView(), "Undo/Redo: No Active View!" );
+ GetEditEngine()->SetParaAttribsOnly( nPara, aPrevItems );
+ lcl_DoSetSelection( GetEditEngine()->GetActiveView(), nPara );
+}
+
+void EditUndoSetParaAttribs::Redo()
+{
+ DBG_ASSERT( GetEditEngine()->GetActiveView(), "Undo/Redo: No Active View!" );
+ GetEditEngine()->SetParaAttribsOnly( nPara, aNewItems );
+ lcl_DoSetSelection( GetEditEngine()->GetActiveView(), nPara );
+}
+
+EditUndoSetAttribs::EditUndoSetAttribs(EditEngine* pEE, const ESelection& rESel, SfxItemSet aNewItems) :
+ EditUndo(EDITUNDO_ATTRIBS, pEE),
+ aESel(rESel),
+ aNewAttribs(std::move(aNewItems)),
+ nSpecial(SetAttribsMode::NONE),
+ m_bSetSelection(true),
+ // When EditUndoSetAttribs actually is a RemoveAttribs this could be
+ // recognize by the empty itemset, but then it would have to be caught in
+ // its own place, which possible a setAttribs does with an empty itemset.
+ bSetIsRemove(false),
+ bRemoveParaAttribs(false),
+ nRemoveWhich(0)
+{
+}
+
+EditUndoSetAttribs::~EditUndoSetAttribs()
+{
+}
+
+void EditUndoSetAttribs::Undo()
+{
+ DBG_ASSERT( GetEditEngine()->GetActiveView(), "Undo/Redo: No Active View!" );
+ EditEngine* pEE = GetEditEngine();
+ bool bFields = false;
+ for ( sal_Int32 nPara = aESel.nStartPara; nPara <= aESel.nEndPara; nPara++ )
+ {
+ const ContentAttribsInfo& rInf = *aPrevAttribs[nPara-aESel.nStartPara];
+
+ // first the paragraph attributes ...
+ pEE->SetParaAttribsOnly(nPara, rInf.GetPrevParaAttribs());
+
+ // Then the character attributes ...
+ // Remove all attributes including features, are later re-established.
+ pEE->RemoveCharAttribs(nPara, 0, true);
+ DBG_ASSERT( pEE->GetEditDoc().GetObject( nPara ), "Undo (SetAttribs): pNode = NULL!" );
+ ContentNode* pNode = pEE->GetEditDoc().GetObject( nPara );
+ for (const auto & nAttr : rInf.GetPrevCharAttribs())
+ {
+ const EditCharAttrib& rX = *nAttr;
+ // is automatically "poolsized"
+ pEE->GetEditDoc().InsertAttrib(pNode, rX.GetStart(), rX.GetEnd(), *rX.GetItem());
+ if (rX.Which() == EE_FEATURE_FIELD)
+ bFields = true;
+ }
+ }
+ if ( bFields )
+ pEE->UpdateFieldsOnly();
+ if (m_bSetSelection)
+ {
+ ImpSetSelection();
+ }
+}
+
+void EditUndoSetAttribs::Redo()
+{
+ DBG_ASSERT( GetEditEngine()->GetActiveView(), "Undo/Redo: No Active View!" );
+ EditEngine* pEE = GetEditEngine();
+
+ EditSelection aSel = pEE->CreateSelection(aESel);
+ if ( !bSetIsRemove )
+ pEE->SetAttribs( aSel, aNewAttribs, nSpecial );
+ else
+ pEE->RemoveCharAttribs( aSel, bRemoveParaAttribs, nRemoveWhich );
+
+ if (m_bSetSelection)
+ {
+ ImpSetSelection();
+ }
+}
+
+void EditUndoSetAttribs::AppendContentInfo(ContentAttribsInfo* pNew)
+{
+ aPrevAttribs.push_back(std::unique_ptr<ContentAttribsInfo>(pNew));
+}
+
+void EditUndoSetAttribs::ImpSetSelection()
+{
+ EditEngine* pEE = GetEditEngine();
+ EditSelection aSel = pEE->CreateSelection(aESel);
+ pEE->GetActiveView()->GetImpEditView()->SetEditSelection(aSel);
+}
+
+EditUndoTransliteration::EditUndoTransliteration(EditEngine* pEE, const ESelection& rESel, TransliterationFlags nM) :
+ EditUndo(EDITUNDO_TRANSLITERATE, pEE),
+ aOldESel(rESel), nMode(nM) {}
+
+EditUndoTransliteration::~EditUndoTransliteration()
+{
+}
+
+void EditUndoTransliteration::Undo()
+{
+ DBG_ASSERT( GetEditEngine()->GetActiveView(), "Undo/Redo: No Active View!" );
+
+ EditEngine* pEE = GetEditEngine();
+
+ EditSelection aSel = pEE->CreateSelection(aNewESel);
+
+ // Insert text, but don't expand Attribs at the current position:
+ aSel = pEE->DeleteSelected( aSel );
+ EditSelection aDelSel( aSel );
+ aSel = pEE->InsertParaBreak( aSel );
+ aDelSel.Max() = aSel.Min();
+ aDelSel.Max().GetNode()->GetCharAttribs().DeleteEmptyAttribs();
+ EditSelection aNewSel;
+ if ( pTxtObj )
+ {
+ aNewSel = pEE->InsertText( *pTxtObj, aSel );
+ }
+ else
+ {
+ aNewSel = pEE->InsertText( aSel, aText );
+ }
+ if ( aNewSel.Min().GetNode() == aDelSel.Max().GetNode() )
+ {
+ aNewSel.Min().SetNode( aDelSel.Min().GetNode() );
+ aNewSel.Min().SetIndex( aNewSel.Min().GetIndex() + aDelSel.Min().GetIndex() );
+ }
+ if ( aNewSel.Max().GetNode() == aDelSel.Max().GetNode() )
+ {
+ aNewSel.Max().SetNode( aDelSel.Min().GetNode() );
+ aNewSel.Max().SetIndex( aNewSel.Max().GetIndex() + aDelSel.Min().GetIndex() );
+ }
+ pEE->DeleteSelected( aDelSel );
+ pEE->GetActiveView()->GetImpEditView()->SetEditSelection( aNewSel );
+}
+
+void EditUndoTransliteration::Redo()
+{
+ DBG_ASSERT( GetEditEngine()->GetActiveView(), "Undo/Redo: No Active View!" );
+ EditEngine* pEE = GetEditEngine();
+
+ EditSelection aSel = pEE->CreateSelection(aOldESel);
+ EditSelection aNewSel = pEE->TransliterateText( aSel, nMode );
+ pEE->GetActiveView()->GetImpEditView()->SetEditSelection( aNewSel );
+}
+
+EditUndoMarkSelection::EditUndoMarkSelection(EditEngine* pEE, const ESelection& rSel) :
+ EditUndo(EDITUNDO_MARKSELECTION, pEE), aSelection(rSel) {}
+
+EditUndoMarkSelection::~EditUndoMarkSelection() {}
+
+void EditUndoMarkSelection::Undo()
+{
+ DBG_ASSERT( GetEditEngine()->GetActiveView(), "Undo/Redo: No Active View!" );
+ if ( GetEditEngine()->GetActiveView() )
+ {
+ if ( GetEditEngine()->IsFormatted() )
+ GetEditEngine()->GetActiveView()->SetSelection( aSelection );
+ else
+ GetEditEngine()->GetActiveView()->GetImpEditView()->SetEditSelection( GetEditEngine()->CreateSelection(aSelection) );
+ }
+}
+
+void EditUndoMarkSelection::Redo()
+{
+ // For redo unimportant, because at the beginning of the undo parentheses
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/editundo.hxx b/editeng/source/editeng/editundo.hxx
new file mode 100644
index 0000000000..d08bc4810b
--- /dev/null
+++ b/editeng/source/editeng/editundo.hxx
@@ -0,0 +1,291 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <editdoc.hxx>
+#include <editeng/editund2.hxx>
+#include <editeng/editdata.hxx>
+#include <editeng/editobj.hxx>
+#include <vector>
+#include <memory>
+
+class EditTextObject;
+class EditEngine;
+enum class SetAttribsMode;
+enum class TransliterationFlags;
+
+// EditUndoDelContent
+
+class EditUndoDelContent : public EditUndo
+{
+private:
+ bool bDelObject;
+ sal_Int32 nNode;
+ ContentNode* pContentNode; // Points to the valid,
+ // undestroyed object!
+
+public:
+ EditUndoDelContent(EditEngine* pEE, ContentNode* pNode, sal_Int32 nPortion);
+ virtual ~EditUndoDelContent() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+};
+
+
+// EditUndoConnectParas
+
+class EditUndoConnectParas : public EditUndo
+{
+private:
+ sal_Int32 nNode;
+ sal_uInt16 nSepPos;
+ SfxItemSet aLeftParaAttribs;
+ SfxItemSet aRightParaAttribs;
+
+ // 2 Pointers would be nicer but then it would have to be a SfxListener.
+ OUString aLeftStyleName;
+ OUString aRightStyleName;
+ SfxStyleFamily eLeftStyleFamily;
+ SfxStyleFamily eRightStyleFamily;
+
+ bool bBackward;
+
+public:
+ EditUndoConnectParas(EditEngine* pEE, sal_Int32 nNode, sal_uInt16 nSepPos,
+ SfxItemSet aLeftParaAttribs, SfxItemSet aRightParaAttribs,
+ const SfxStyleSheet* pLeftStyle, const SfxStyleSheet* pRightStyle, bool bBackward);
+ virtual ~EditUndoConnectParas() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+};
+
+
+// EditUndoSplitPara
+
+class EditUndoSplitPara : public EditUndo
+{
+private:
+ sal_Int32 nNode;
+ sal_uInt16 nSepPos;
+
+public:
+ EditUndoSplitPara(EditEngine* pEE, sal_Int32 nNode, sal_uInt16 nSepPos);
+ virtual ~EditUndoSplitPara() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+};
+
+
+// EditUndoInsertChars
+
+class EditUndoInsertChars : public EditUndo
+{
+private:
+ EPaM aEPaM;
+ OUString aText;
+
+public:
+ EditUndoInsertChars(EditEngine* pEE, const EPaM& rEPaM, OUString aStr);
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+
+ virtual bool Merge( SfxUndoAction *pNextAction ) override;
+};
+
+
+// EditUndoRemoveChars
+
+class EditUndoRemoveChars : public EditUndo
+{
+private:
+ EPaM aEPaM;
+ OUString aText;
+
+public:
+ EditUndoRemoveChars(EditEngine* pEE, const EPaM& rEPaM, OUString aStr);
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+};
+
+
+// EditUndoInsertFeature
+
+class EditUndoInsertFeature : public EditUndo
+{
+private:
+ EPaM aEPaM;
+ std::unique_ptr<SfxPoolItem> pFeature;
+
+public:
+ EditUndoInsertFeature(EditEngine* pEE, const EPaM& rEPaM, const SfxPoolItem& rFeature);
+ virtual ~EditUndoInsertFeature() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+};
+
+
+// EditUndoMoveParagraphs
+
+class EditUndoMoveParagraphs: public EditUndo
+{
+private:
+ Range nParagraphs;
+ sal_Int32 nDest;
+
+public:
+ EditUndoMoveParagraphs(EditEngine* pEE, const Range& rParas, sal_Int32 nDest);
+ virtual ~EditUndoMoveParagraphs() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+};
+
+
+// EditUndoSetStyleSheet
+
+class EditUndoSetStyleSheet: public EditUndo
+{
+private:
+ sal_Int32 nPara;
+ OUString aPrevName;
+ OUString aNewName;
+ SfxStyleFamily ePrevFamily;
+ SfxStyleFamily eNewFamily;
+ SfxItemSet aPrevParaAttribs;
+
+public:
+ EditUndoSetStyleSheet(EditEngine* pEE, sal_Int32 nPara,
+ OUString aPrevName, SfxStyleFamily ePrevFamily,
+ OUString aNewName, SfxStyleFamily eNewFamily,
+ SfxItemSet aPrevParaAttribs);
+ virtual ~EditUndoSetStyleSheet() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+};
+
+
+// EditUndoSetParaAttribs
+
+class EditUndoSetParaAttribs: public EditUndo
+{
+private:
+ sal_Int32 nPara;
+ SfxItemSet aPrevItems;
+ SfxItemSet aNewItems;
+
+public:
+ EditUndoSetParaAttribs(EditEngine* pEE, sal_Int32 nPara, SfxItemSet aPrevItems, SfxItemSet aNewItems);
+ virtual ~EditUndoSetParaAttribs() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+};
+
+
+// EditUndoSetAttribs
+
+class EditUndoSetAttribs: public EditUndo
+{
+private:
+ typedef std::vector<std::unique_ptr<ContentAttribsInfo> > InfoArrayType;
+
+ ESelection aESel;
+ SfxItemSet aNewAttribs;
+ InfoArrayType aPrevAttribs;
+
+ SetAttribsMode nSpecial;
+ /// Once the attributes are set / unset, set the selection to the end of the formatted range?
+ bool m_bSetSelection;
+ bool bSetIsRemove;
+ bool bRemoveParaAttribs;
+ sal_uInt16 nRemoveWhich;
+
+ void ImpSetSelection();
+
+
+public:
+ EditUndoSetAttribs(EditEngine* pEE, const ESelection& rESel, SfxItemSet aNewItems);
+ virtual ~EditUndoSetAttribs() override;
+
+ SfxItemSet& GetNewAttribs() { return aNewAttribs; }
+
+ void SetSpecial( SetAttribsMode n ) { nSpecial = n; }
+ void SetUpdateSelection( bool bSetSelection ) { m_bSetSelection = bSetSelection; }
+ void SetRemoveAttribs( bool b ) { bSetIsRemove = b; }
+ void SetRemoveParaAttribs( bool b ) { bRemoveParaAttribs = b; }
+ void SetRemoveWhich( sal_uInt16 n ) { nRemoveWhich = n; }
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+
+ void AppendContentInfo(ContentAttribsInfo* pNew);
+};
+
+
+// EditUndoTransliteration
+
+class EditUndoTransliteration: public EditUndo
+{
+private:
+ ESelection aOldESel;
+ ESelection aNewESel;
+
+ TransliterationFlags
+ nMode;
+ std::unique_ptr<EditTextObject>
+ pTxtObj;
+ OUString aText;
+
+public:
+ EditUndoTransliteration(EditEngine* pEE, const ESelection& rESel, TransliterationFlags nMode);
+ virtual ~EditUndoTransliteration() override;
+
+ void SetText( const OUString& rText ) { aText = rText; }
+ void SetText( std::unique_ptr<EditTextObject> pObj ) { pTxtObj = std::move( pObj ); }
+ void SetNewSelection( const ESelection& rSel ) { aNewESel = rSel; }
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+};
+
+
+// EditUndoMarkSelection
+
+class EditUndoMarkSelection: public EditUndo
+{
+private:
+ ESelection aSelection;
+
+public:
+ EditUndoMarkSelection(EditEngine* pEE, const ESelection& rSel);
+ virtual ~EditUndoMarkSelection() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/editview.cxx b/editeng/source/editeng/editview.cxx
new file mode 100644
index 0000000000..e047e6a41f
--- /dev/null
+++ b/editeng/source/editeng/editview.cxx
@@ -0,0 +1,1800 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <memory>
+#include <vcl/image.hxx>
+
+#include <com/sun/star/i18n/WordType.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+
+#include <i18nlangtag/languagetag.hxx>
+#include <i18nlangtag/mslangid.hxx>
+#include <svl/languageoptions.hxx>
+#include <svtools/ctrltool.hxx>
+#include <svtools/langtab.hxx>
+#include <tools/stream.hxx>
+
+#include <svl/srchitem.hxx>
+
+#include "impedit.hxx"
+#include <comphelper/propertyvalue.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/svxacorr.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/eerdll.hxx>
+#include <eerdll2.hxx>
+#include <editeng/editrids.hrc>
+#include <editeng.hxx>
+#include <i18nlangtag/lang.h>
+#include <vcl/window.hxx>
+#include <editeng/acorrcfg.hxx>
+#include <editeng/unolingu.hxx>
+#include <unotools/lingucfg.hxx>
+
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/linguistic2/XDictionary.hpp>
+#include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp>
+#include <linguistic/lngprops.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <comphelper/lok.hxx>
+#include <sfx2/viewsh.hxx>
+#include <osl/diagnose.h>
+#include <boost/property_tree/json_parser.hpp>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::beans;
+
+
+// static
+LanguageType EditView::CheckLanguage(
+ const OUString &rText,
+ const Reference< linguistic2::XSpellChecker1 >& xSpell,
+ const Reference< linguistic2::XLanguageGuessing >& xLangGuess,
+ bool bIsParaText )
+{
+ LanguageType nLang = LANGUAGE_NONE;
+ if (bIsParaText) // check longer texts with language-guessing...
+ {
+ if (!xLangGuess.is())
+ return nLang;
+
+ LanguageTag aGuessTag( xLangGuess->guessPrimaryLanguage( rText, 0, rText.getLength()) );
+
+ // If the result from language guessing does not provide a 'Country'
+ // part, try to get it by looking up the locale setting of the office,
+ // "Tools/Options - Languages and Locales - General: Locale setting", if
+ // the language matches.
+ if ( aGuessTag.getCountry().isEmpty() )
+ {
+ const LanguageTag& rAppLocaleTag = Application::GetSettings().GetLanguageTag();
+ if (rAppLocaleTag.getLanguage() == aGuessTag.getLanguage())
+ nLang = rAppLocaleTag.getLanguageType();
+ }
+ if (nLang == LANGUAGE_NONE) // language not found by looking up the system language...
+ nLang = aGuessTag.makeFallback().getLanguageType(); // best known locale match
+ if (nLang == LANGUAGE_SYSTEM)
+ nLang = Application::GetSettings().GetLanguageTag().getLanguageType();
+ if (nLang == LANGUAGE_DONTKNOW)
+ nLang = LANGUAGE_NONE;
+ }
+ else // check single word
+ {
+ if (!xSpell.is())
+ return nLang;
+
+
+ // build list of languages to check
+
+ LanguageType aLangList[4];
+ const AllSettings& rSettings = Application::GetSettings();
+ SvtLinguOptions aLinguOpt;
+ SvtLinguConfig().GetOptions( aLinguOpt );
+ // The default document language from "Tools/Options - Languages and Locales - General: Western"
+ aLangList[0] = MsLangId::resolveSystemLanguageByScriptType( aLinguOpt.nDefaultLanguage,
+ css::i18n::ScriptType::LATIN);
+ // The one from "Tools/Options - Languages and Locales - General: User interface"
+ aLangList[1] = rSettings.GetUILanguageTag().getLanguageType();
+ // The one from "Tools/Options - Languages and Locales - General: Locale setting"
+ aLangList[2] = rSettings.GetLanguageTag().getLanguageType();
+ // en-US
+ aLangList[3] = LANGUAGE_ENGLISH_US;
+#ifdef DEBUG
+ lang::Locale a0( LanguageTag::convertToLocale( aLangList[0] ) );
+ lang::Locale a1( LanguageTag::convertToLocale( aLangList[1] ) );
+ lang::Locale a2( LanguageTag::convertToLocale( aLangList[2] ) );
+ lang::Locale a3( LanguageTag::convertToLocale( aLangList[3] ) );
+#endif
+
+ for (const LanguageType& nTmpLang : aLangList)
+ {
+ if (nTmpLang != LANGUAGE_NONE && nTmpLang != LANGUAGE_DONTKNOW)
+ {
+ if (xSpell->hasLanguage( static_cast<sal_uInt16>(nTmpLang) ) &&
+ xSpell->isValid( rText, static_cast<sal_uInt16>(nTmpLang), Sequence< PropertyValue >() ))
+ {
+ nLang = nTmpLang;
+ break;
+ }
+ }
+ }
+ }
+
+ return nLang;
+}
+
+EditViewCallbacks::~EditViewCallbacks()
+{
+}
+
+EditView::EditView( EditEngine* pEng, vcl::Window* pWindow )
+{
+ pImpEditView.reset( new ImpEditView( this, pEng, pWindow ) );
+}
+
+EditView::~EditView()
+{
+}
+
+void EditView::setEditViewCallbacks(EditViewCallbacks* pEditViewCallbacks)
+{
+ pImpEditView->setEditViewCallbacks(pEditViewCallbacks);
+}
+
+EditViewCallbacks* EditView::getEditViewCallbacks() const
+{
+ return pImpEditView->getEditViewCallbacks();
+}
+
+ImpEditEngine* EditView::GetImpEditEngine() const
+{
+ return pImpEditView->pEditEngine->pImpEditEngine.get();
+}
+
+EditEngine* EditView::GetEditEngine() const
+{
+ return pImpEditView->pEditEngine;
+}
+
+tools::Rectangle EditView::GetInvalidateRect() const
+{
+ if ( !pImpEditView->DoInvalidateMore() )
+ return pImpEditView->aOutArea;
+ else
+ {
+ tools::Rectangle aRect( pImpEditView->aOutArea );
+ tools::Long nMore = pImpEditView->GetOutputDevice().PixelToLogic( Size( pImpEditView->GetInvalidateMore(), 0 ) ).Width();
+ aRect.AdjustLeft( -nMore );
+ aRect.AdjustRight(nMore );
+ aRect.AdjustTop( -nMore );
+ aRect.AdjustBottom(nMore );
+ return aRect;
+ }
+}
+
+namespace {
+
+tools::Rectangle lcl_negateRectX(const tools::Rectangle& rRect)
+{
+ return tools::Rectangle(-rRect.Right(), rRect.Top(), -rRect.Left(), rRect.Bottom());
+}
+
+}
+
+void EditView::InvalidateWindow(const tools::Rectangle& rClipRect)
+{
+ bool bNegativeX = IsNegativeX();
+ if (EditViewCallbacks* pEditViewCallbacks = pImpEditView->getEditViewCallbacks())
+ {
+ // do not invalidate and trigger a global repaint, but forward
+ // the need for change to the applied EditViewCallback, can e.g.
+ // be used to visualize the active edit text in an OverlayObject
+ pEditViewCallbacks->EditViewInvalidate(bNegativeX ? lcl_negateRectX(rClipRect) : rClipRect);
+ }
+ else
+ {
+ // classic mode: invalidate and trigger full repaint
+ // of the changed area
+ GetWindow()->Invalidate(bNegativeX ? lcl_negateRectX(rClipRect) : rClipRect);
+ }
+}
+
+void EditView::InvalidateOtherViewWindows( const tools::Rectangle& rInvRect )
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ bool bNegativeX = IsNegativeX();
+ for (auto& pWin : pImpEditView->aOutWindowSet)
+ {
+ if (pWin)
+ pWin->Invalidate( bNegativeX ? lcl_negateRectX(rInvRect) : rInvRect );
+ }
+ }
+}
+
+void EditView::Invalidate()
+{
+ const tools::Rectangle& rInvRect = GetInvalidateRect();
+ pImpEditView->InvalidateAtWindow(rInvRect);
+ InvalidateOtherViewWindows(rInvRect);
+}
+
+void EditView::SetReadOnly( bool bReadOnly )
+{
+ pImpEditView->bReadOnly = bReadOnly;
+}
+
+bool EditView::IsReadOnly() const
+{
+ return pImpEditView->bReadOnly;
+}
+
+void EditView::SetSelection( const ESelection& rESel )
+{
+ // If someone has just left an empty attribute, and then the outliner manipulates the
+ // selection, call the CursorMoved method so that empty attributes get cleaned up.
+ if ( !HasSelection() )
+ {
+ // tdf#113591 Get node from EditDoc, as the selection might have a pointer to an
+ // already deleted node.
+ const ContentNode* pNode(pImpEditView->pEditEngine->GetEditDoc().GetEndPaM().GetNode());
+ if (nullptr != pNode)
+ pNode->checkAndDeleteEmptyAttribs();
+ }
+ EditSelection aNewSelection( pImpEditView->pEditEngine->pImpEditEngine->ConvertSelection(
+ rESel.nStartPara, rESel.nStartPos, rESel.nEndPara, rESel.nEndPos ) );
+
+ // If the selection is manipulated after a KeyInput:
+ pImpEditView->pEditEngine->CheckIdleFormatter();
+
+ // Selection may not start/end at an invisible paragraph:
+ const ParaPortion* pPortion = pImpEditView->pEditEngine->FindParaPortion( aNewSelection.Min().GetNode() );
+ if ( !pPortion->IsVisible() )
+ {
+ pPortion = pImpEditView->pEditEngine->GetPrevVisPortion( pPortion );
+ ContentNode* pNode = pPortion ? pPortion->GetNode() : pImpEditView->pEditEngine->GetEditDoc().GetObject( 0 );
+ aNewSelection.Min() = EditPaM( pNode, pNode->Len() );
+ }
+ pPortion = pImpEditView->pEditEngine->FindParaPortion( aNewSelection.Max().GetNode() );
+ if ( !pPortion->IsVisible() )
+ {
+ pPortion = pImpEditView->pEditEngine->GetPrevVisPortion( pPortion );
+ ContentNode* pNode = pPortion ? pPortion->GetNode() : pImpEditView->pEditEngine->GetEditDoc().GetObject( 0 );
+ aNewSelection.Max() = EditPaM( pNode, pNode->Len() );
+ }
+
+ pImpEditView->DrawSelectionXOR();
+ pImpEditView->SetEditSelection( aNewSelection );
+ pImpEditView->DrawSelectionXOR();
+ bool bGotoCursor = pImpEditView->DoAutoScroll();
+
+ // comments section in Writer:
+ // don't scroll to the selection if it is
+ // out of visible area of comment canvas.
+ if (HasSelection())
+ ShowCursor( bGotoCursor );
+}
+
+ESelection EditView::GetSelection() const
+{
+ ESelection aSelection;
+
+ aSelection.nStartPara = pImpEditView->pEditEngine->GetEditDoc().GetPos( pImpEditView->GetEditSelection().Min().GetNode() );
+ aSelection.nEndPara = pImpEditView->pEditEngine->GetEditDoc().GetPos( pImpEditView->GetEditSelection().Max().GetNode() );
+
+ aSelection.nStartPos = pImpEditView->GetEditSelection().Min().GetIndex();
+ aSelection.nEndPos = pImpEditView->GetEditSelection().Max().GetIndex();
+
+ return aSelection;
+}
+
+bool EditView::HasSelection() const
+{
+ return pImpEditView->HasSelection();
+}
+
+bool EditView::IsSelectionFullPara() const
+{
+ return pImpEditView->IsSelectionFullPara();
+}
+
+bool EditView::IsSelectionWithinSinglePara() const
+{
+ return pImpEditView->IsSelectionInSinglePara();
+}
+
+bool EditView::IsSelectionAtPoint(const Point& rPointPixel)
+{
+ return pImpEditView->IsSelectionAtPoint(rPointPixel);
+}
+
+void EditView::DeleteSelected()
+{
+ pImpEditView->DeleteSelected();
+}
+
+SvtScriptType EditView::GetSelectedScriptType() const
+{
+ return pImpEditView->pEditEngine->GetScriptType( pImpEditView->GetEditSelection() );
+}
+
+void EditView::GetSelectionRectangles(std::vector<tools::Rectangle>& rLogicRects) const
+{
+ return pImpEditView->GetSelectionRectangles(pImpEditView->GetEditSelection(), rLogicRects);
+}
+
+void EditView::Paint( const tools::Rectangle& rRect, OutputDevice* pTargetDevice )
+{
+ pImpEditView->pEditEngine->pImpEditEngine->Paint( pImpEditView.get(), rRect, pTargetDevice );
+}
+
+void EditView::SetEditEngine( EditEngine* pEditEng )
+{
+ pImpEditView->pEditEngine = pEditEng;
+ EditSelection aStartSel = pImpEditView->pEditEngine->GetEditDoc().GetStartPaM();
+ pImpEditView->SetEditSelection( aStartSel );
+}
+
+void EditView::SetWindow( vcl::Window* pWin )
+{
+ pImpEditView->pOutWin = pWin;
+ pImpEditView->pEditEngine->pImpEditEngine->GetSelEngine().Reset();
+}
+
+vcl::Window* EditView::GetWindow() const
+{
+ return pImpEditView->pOutWin;
+}
+
+OutputDevice& EditView::GetOutputDevice() const
+{
+ return pImpEditView->GetOutputDevice();
+}
+
+LanguageType EditView::GetInputLanguage() const
+{
+ // it might make sense to add this to getEditViewCallbacks
+ if (const vcl::Window* pWindow = GetWindow())
+ return pWindow->GetInputLanguage();
+ return LANGUAGE_DONTKNOW;
+}
+
+bool EditView::HasOtherViewWindow( vcl::Window* pWin )
+{
+ OutWindowSet& rOutWindowSet = pImpEditView->aOutWindowSet;
+ auto found = std::find(rOutWindowSet.begin(), rOutWindowSet.end(), pWin);
+ return (found != rOutWindowSet.end());
+}
+
+bool EditView::AddOtherViewWindow( vcl::Window* pWin )
+{
+ if (HasOtherViewWindow(pWin))
+ return false;
+ pImpEditView->aOutWindowSet.emplace_back(pWin);
+ return true;
+}
+
+bool EditView::RemoveOtherViewWindow( vcl::Window* pWin )
+{
+ OutWindowSet& rOutWindowSet = pImpEditView->aOutWindowSet;
+ auto found = std::find(rOutWindowSet.begin(), rOutWindowSet.end(), pWin);
+ if (found == rOutWindowSet.end())
+ return false;
+ rOutWindowSet.erase(found);
+ return true;
+}
+
+void EditView::SetVisArea( const tools::Rectangle& rRect )
+{
+ pImpEditView->SetVisDocStartPos( rRect.TopLeft() );
+}
+
+tools::Rectangle EditView::GetVisArea() const
+{
+ return pImpEditView->GetVisDocArea();
+}
+
+void EditView::SetOutputArea( const tools::Rectangle& rRect )
+{
+ pImpEditView->SetOutputArea( rRect );
+
+ // the rest here only if it is an API call:
+ pImpEditView->CalcAnchorPoint();
+ if ( pImpEditView->pEditEngine->pImpEditEngine->GetStatus().AutoPageSize() )
+ pImpEditView->RecalcOutputArea();
+ pImpEditView->ShowCursor( false, false );
+}
+
+const tools::Rectangle& EditView::GetOutputArea() const
+{
+ return pImpEditView->GetOutputArea();
+}
+
+PointerStyle EditView::GetPointer() const
+{
+ return pImpEditView->GetPointer();
+}
+
+vcl::Cursor* EditView::GetCursor() const
+{
+ return pImpEditView->pCursor.get();
+}
+
+void EditView::InsertText( const OUString& rStr, bool bSelect, bool bLOKShowSelect )
+{
+
+ EditEngine* pEE = pImpEditView->pEditEngine;
+
+ if (bLOKShowSelect)
+ pImpEditView->DrawSelectionXOR();
+
+ EditPaM aPaM1;
+ if ( bSelect )
+ {
+ EditSelection aTmpSel( pImpEditView->GetEditSelection() );
+ aTmpSel.Adjust( pEE->GetEditDoc() );
+ aPaM1 = aTmpSel.Min();
+ }
+
+ pEE->UndoActionStart( EDITUNDO_INSERT );
+ EditPaM aPaM2( pEE->InsertText( pImpEditView->GetEditSelection(), rStr ) );
+ pEE->UndoActionEnd();
+
+ if ( bSelect )
+ {
+ DBG_ASSERT( !aPaM1.DbgIsBuggy( pEE->GetEditDoc() ), "Insert: PaM broken" );
+ pImpEditView->SetEditSelection( EditSelection( aPaM1, aPaM2 ) );
+ }
+ else
+ pImpEditView->SetEditSelection( EditSelection( aPaM2, aPaM2 ) );
+
+ if (bLOKShowSelect)
+ pEE->FormatAndLayout( this );
+}
+
+bool EditView::PostKeyEvent( const KeyEvent& rKeyEvent, vcl::Window const * pFrameWin )
+{
+ return pImpEditView->PostKeyEvent( rKeyEvent, pFrameWin );
+}
+
+bool EditView::MouseButtonUp( const MouseEvent& rMouseEvent )
+{
+ return pImpEditView->MouseButtonUp( rMouseEvent );
+}
+
+void EditView::ReleaseMouse()
+{
+ return pImpEditView->ReleaseMouse();
+}
+
+bool EditView::MouseButtonDown( const MouseEvent& rMouseEvent )
+{
+ return pImpEditView->MouseButtonDown( rMouseEvent );
+}
+
+bool EditView::MouseMove( const MouseEvent& rMouseEvent )
+{
+ return pImpEditView->MouseMove( rMouseEvent );
+}
+
+bool EditView::Command(const CommandEvent& rCEvt)
+{
+ return pImpEditView->Command(rCEvt);
+}
+
+void EditView::SetBroadcastLOKViewCursor(bool bSet)
+{
+ pImpEditView->SetBroadcastLOKViewCursor(bSet);
+}
+
+tools::Rectangle EditView::GetEditCursor() const
+{
+ return pImpEditView->GetEditCursor();
+}
+
+void EditView::ShowCursor( bool bGotoCursor, bool bForceVisCursor, bool bActivate )
+{
+ if ( !pImpEditView->pEditEngine->HasView( this ) )
+ return;
+
+ // The control word is more important:
+ if ( !pImpEditView->DoAutoScroll() )
+ bGotoCursor = false;
+ pImpEditView->ShowCursor( bGotoCursor, bForceVisCursor );
+
+ if (pImpEditView->mpViewShell && !bActivate)
+ {
+ if (!pImpEditView->pOutWin)
+ return;
+ VclPtr<vcl::Window> pParent = pImpEditView->pOutWin->GetParentWithLOKNotifier();
+ if (pParent && pParent->GetLOKWindowId() != 0)
+ return;
+
+ static const OString aPayload = OString::boolean(true);
+ pImpEditView->mpViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CURSOR_VISIBLE, aPayload);
+ pImpEditView->mpViewShell->NotifyOtherViews(LOK_CALLBACK_VIEW_CURSOR_VISIBLE, "visible"_ostr, aPayload);
+ }
+}
+
+void EditView::HideCursor(bool bDeactivate)
+{
+ pImpEditView->GetCursor()->Hide();
+
+ if (pImpEditView->mpViewShell && !bDeactivate)
+ {
+ if (!pImpEditView->pOutWin)
+ return;
+ VclPtr<vcl::Window> pParent = pImpEditView->pOutWin->GetParentWithLOKNotifier();
+ if (pParent && pParent->GetLOKWindowId() != 0)
+ return;
+
+ OString aPayload = OString::boolean(false);
+ pImpEditView->mpViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CURSOR_VISIBLE, aPayload);
+ pImpEditView->mpViewShell->NotifyOtherViews(LOK_CALLBACK_VIEW_CURSOR_VISIBLE, "visible"_ostr, aPayload);
+ }
+}
+
+Pair EditView::Scroll( tools::Long ndX, tools::Long ndY, ScrollRangeCheck nRangeCheck )
+{
+ return pImpEditView->Scroll( ndX, ndY, nRangeCheck );
+}
+
+const SfxItemSet& EditView::GetEmptyItemSet() const
+{
+ return pImpEditView->pEditEngine->GetEmptyItemSet();
+}
+
+void EditView::SetAttribs( const SfxItemSet& rSet )
+{
+ DBG_ASSERT( !pImpEditView->aEditSelection.IsInvalid(), "Blind Selection in..." );
+
+ pImpEditView->DrawSelectionXOR();
+ pImpEditView->pEditEngine->SetAttribs( pImpEditView->GetEditSelection(), rSet, SetAttribsMode::WholeWord );
+ if (pImpEditView->pEditEngine->IsUpdateLayout())
+ pImpEditView->pEditEngine->FormatAndLayout( this );
+}
+
+void EditView::RemoveAttribsKeepLanguages( bool bRemoveParaAttribs )
+{
+
+ pImpEditView->DrawSelectionXOR();
+ pImpEditView->pEditEngine->UndoActionStart( EDITUNDO_RESETATTRIBS );
+ EditSelection aSelection( pImpEditView->GetEditSelection() );
+
+ for (sal_uInt16 nWID = EE_ITEMS_START; nWID <= EE_ITEMS_END; ++nWID)
+ {
+ bool bIsLang = EE_CHAR_LANGUAGE == nWID ||
+ EE_CHAR_LANGUAGE_CJK == nWID ||
+ EE_CHAR_LANGUAGE_CTL == nWID;
+ if (!bIsLang)
+ pImpEditView->pEditEngine->RemoveCharAttribs( aSelection, bRemoveParaAttribs, nWID );
+ }
+
+ pImpEditView->pEditEngine->UndoActionEnd();
+ if (pImpEditView->pEditEngine->IsUpdateLayout())
+ pImpEditView->pEditEngine->FormatAndLayout( this );
+}
+
+void EditView::RemoveAttribs( bool bRemoveParaAttribs, sal_uInt16 nWhich )
+{
+ RemoveAttribs(bRemoveParaAttribs ? EERemoveParaAttribsMode::RemoveAll
+ : EERemoveParaAttribsMode::RemoveCharItems, nWhich);
+}
+
+void EditView::RemoveAttribs( EERemoveParaAttribsMode eMode, sal_uInt16 nWhich )
+{
+ pImpEditView->DrawSelectionXOR();
+ pImpEditView->pEditEngine->UndoActionStart( EDITUNDO_RESETATTRIBS );
+ pImpEditView->pEditEngine->RemoveCharAttribs( pImpEditView->GetEditSelection(), eMode, nWhich );
+ pImpEditView->pEditEngine->UndoActionEnd();
+ if (pImpEditView->pEditEngine->IsUpdateLayout())
+ pImpEditView->pEditEngine->FormatAndLayout( this );
+}
+
+void EditView::RemoveCharAttribs( sal_Int32 nPara, sal_uInt16 nWhich )
+{
+ pImpEditView->pEditEngine->UndoActionStart( EDITUNDO_RESETATTRIBS );
+ pImpEditView->pEditEngine->RemoveCharAttribs( nPara, nWhich );
+ pImpEditView->pEditEngine->UndoActionEnd();
+ if (pImpEditView->pEditEngine->IsUpdateLayout())
+ pImpEditView->pEditEngine->FormatAndLayout( this );
+}
+
+SfxItemSet EditView::GetAttribs()
+{
+ DBG_ASSERT( !pImpEditView->aEditSelection.IsInvalid(), "Blind Selection in..." );
+ return pImpEditView->pEditEngine->pImpEditEngine->GetAttribs( pImpEditView->GetEditSelection() );
+}
+
+void EditView::Undo()
+{
+ pImpEditView->pEditEngine->Undo( this );
+}
+
+void EditView::Redo()
+{
+ pImpEditView->pEditEngine->Redo( this );
+}
+
+ErrCode EditView::Read( SvStream& rInput, EETextFormat eFormat, SvKeyValueIterator* pHTTPHeaderAttrs )
+{
+ EditSelection aOldSel( pImpEditView->GetEditSelection() );
+ pImpEditView->DrawSelectionXOR();
+ pImpEditView->pEditEngine->pImpEditEngine->UndoActionStart( EDITUNDO_READ );
+ EditPaM aEndPaM = pImpEditView->pEditEngine->pImpEditEngine->Read( rInput, "", eFormat, aOldSel, pHTTPHeaderAttrs );
+ pImpEditView->pEditEngine->pImpEditEngine->UndoActionEnd();
+ EditSelection aNewSel( aEndPaM, aEndPaM );
+
+ pImpEditView->SetEditSelection( aNewSel );
+ bool bGotoCursor = pImpEditView->DoAutoScroll();
+ ShowCursor( bGotoCursor );
+
+ return rInput.GetError();
+}
+
+void EditView::Cut()
+{
+ Reference<css::datatransfer::clipboard::XClipboard> aClipBoard(GetClipboard());
+ pImpEditView->CutCopy( aClipBoard, true );
+}
+
+Reference<css::datatransfer::clipboard::XClipboard> EditView::GetClipboard() const
+{
+ return pImpEditView->GetClipboard();
+}
+
+css::uno::Reference< css::datatransfer::XTransferable > EditView::GetTransferable() const
+{
+ uno::Reference< datatransfer::XTransferable > xData =
+ GetEditEngine()->CreateTransferable( pImpEditView->GetEditSelection() );
+ return xData;
+}
+
+void EditView::Copy()
+{
+ Reference<css::datatransfer::clipboard::XClipboard> aClipBoard(GetClipboard());
+ pImpEditView->CutCopy( aClipBoard, false );
+}
+
+void EditView::Paste()
+{
+ Reference<css::datatransfer::clipboard::XClipboard> aClipBoard(GetClipboard());
+ pImpEditView->Paste( aClipBoard );
+}
+
+void EditView::PasteSpecial(SotClipboardFormatId format)
+{
+ Reference<css::datatransfer::clipboard::XClipboard> aClipBoard(GetClipboard());
+ pImpEditView->Paste(aClipBoard, true, format );
+}
+
+Point EditView::GetWindowPosTopLeft( sal_Int32 nParagraph )
+{
+ Point aDocPos( pImpEditView->pEditEngine->GetDocPosTopLeft( nParagraph ) );
+ return pImpEditView->GetWindowPos( aDocPos );
+}
+
+void EditView::SetSelectionMode( EESelectionMode eMode )
+{
+ pImpEditView->SetSelectionMode( eMode );
+}
+
+OUString EditView::GetSelected() const
+{
+ return pImpEditView->pEditEngine->pImpEditEngine->GetSelected( pImpEditView->GetEditSelection() );
+}
+
+void EditView::MoveParagraphs( Range aParagraphs, sal_Int32 nNewPos )
+{
+ pImpEditView->pEditEngine->pImpEditEngine->UndoActionStart( EDITUNDO_MOVEPARAS );
+ pImpEditView->pEditEngine->pImpEditEngine->MoveParagraphs( aParagraphs, nNewPos, this );
+ pImpEditView->pEditEngine->pImpEditEngine->UndoActionEnd();
+}
+
+void EditView::MoveParagraphs( tools::Long nDiff )
+{
+ ESelection aSel = GetSelection();
+ Range aRange( aSel.nStartPara, aSel.nEndPara );
+ aRange.Normalize();
+ tools::Long nDest = ( nDiff > 0 ? aRange.Max() : aRange.Min() ) + nDiff;
+ if ( nDiff > 0 )
+ nDest++;
+ DBG_ASSERT( ( nDest >= 0 ) && ( nDest <= pImpEditView->pEditEngine->GetParagraphCount() ), "MoveParagraphs - wrong Parameters!" );
+ MoveParagraphs( aRange, sal::static_int_cast< sal_Int32 >( nDest ) );
+}
+
+void EditView::SetBackgroundColor( const Color& rColor )
+{
+ pImpEditView->SetBackgroundColor( rColor );
+ pImpEditView->pEditEngine->SetBackgroundColor( rColor );
+}
+
+Color const & EditView::GetBackgroundColor() const
+{
+ return pImpEditView->GetBackgroundColor();
+}
+
+void EditView::RegisterViewShell(OutlinerViewShell* pViewShell)
+{
+ pImpEditView->RegisterViewShell(pViewShell);
+}
+
+void EditView::RegisterOtherShell(OutlinerViewShell* pOtherShell)
+{
+ pImpEditView->RegisterOtherShell(pOtherShell);
+}
+
+void EditView::SetControlWord( EVControlBits nWord )
+{
+ pImpEditView->nControl = nWord;
+}
+
+EVControlBits EditView::GetControlWord() const
+{
+ return pImpEditView->nControl;
+}
+
+std::unique_ptr<EditTextObject> EditView::CreateTextObject()
+{
+ return pImpEditView->pEditEngine->pImpEditEngine->CreateTextObject( pImpEditView->GetEditSelection() );
+}
+
+void EditView::InsertText( const EditTextObject& rTextObject )
+{
+ pImpEditView->DrawSelectionXOR();
+
+ pImpEditView->pEditEngine->UndoActionStart( EDITUNDO_INSERT );
+ EditSelection aTextSel( pImpEditView->pEditEngine->InsertText( rTextObject, pImpEditView->GetEditSelection() ) );
+ pImpEditView->pEditEngine->UndoActionEnd();
+
+ aTextSel.Min() = aTextSel.Max(); // Selection not retained.
+ pImpEditView->SetEditSelection( aTextSel );
+ if (pImpEditView->pEditEngine->IsUpdateLayout())
+ pImpEditView->pEditEngine->FormatAndLayout( this );
+}
+
+void EditView::InsertText( css::uno::Reference< css::datatransfer::XTransferable > const & xDataObj, const OUString& rBaseURL, bool bUseSpecial )
+{
+ pImpEditView->pEditEngine->UndoActionStart( EDITUNDO_INSERT );
+ pImpEditView->DeleteSelected();
+ EditSelection aTextSel =
+ pImpEditView->pEditEngine->InsertText(xDataObj, rBaseURL, pImpEditView->GetEditSelection().Max(), bUseSpecial);
+ pImpEditView->pEditEngine->UndoActionEnd();
+
+ aTextSel.Min() = aTextSel.Max(); // Selection not retained.
+ pImpEditView->SetEditSelection( aTextSel );
+ if (pImpEditView->pEditEngine->IsUpdateLayout())
+ pImpEditView->pEditEngine->FormatAndLayout( this );
+}
+
+bool EditView::SetEditEngineUpdateLayout( bool bUpdate )
+{
+ return pImpEditView->pEditEngine->pImpEditEngine->SetUpdateLayout( bUpdate, this );
+}
+
+void EditView::ForceLayoutCalculation()
+{
+ pImpEditView->pEditEngine->pImpEditEngine->SetUpdateLayout( true, this, true );
+}
+
+SfxStyleSheet* EditView::GetStyleSheet()
+{
+ EditSelection aSel( pImpEditView->GetEditSelection() );
+ aSel.Adjust( pImpEditView->pEditEngine->GetEditDoc() );
+ sal_Int32 nStartPara = pImpEditView->pEditEngine->GetEditDoc().GetPos( aSel.Min().GetNode() );
+ sal_Int32 nEndPara = pImpEditView->pEditEngine->GetEditDoc().GetPos( aSel.Max().GetNode() );
+
+ SfxStyleSheet* pStyle = nullptr;
+ for ( sal_Int32 n = nStartPara; n <= nEndPara; n++ )
+ {
+ SfxStyleSheet* pTmpStyle = pImpEditView->pEditEngine->GetStyleSheet( n );
+ if ( ( n != nStartPara ) && ( pStyle != pTmpStyle ) )
+ return nullptr; // Not unique.
+ pStyle = pTmpStyle;
+ }
+ return pStyle;
+}
+
+const SfxStyleSheet* EditView::GetStyleSheet() const
+{
+ return const_cast< EditView* >( this )->GetStyleSheet();
+}
+
+bool EditView::IsInsertMode() const
+{
+ return pImpEditView->IsInsertMode();
+}
+
+void EditView::SetInsertMode( bool bInsert )
+{
+ pImpEditView->SetInsertMode( bInsert );
+}
+
+void EditView::SetAnchorMode( EEAnchorMode eMode )
+{
+ pImpEditView->SetAnchorMode( eMode );
+}
+
+EEAnchorMode EditView::GetAnchorMode() const
+{
+ return pImpEditView->GetAnchorMode();
+}
+
+void EditView::TransliterateText( TransliterationFlags nTransliterationMode )
+{
+ EditSelection aOldSel( pImpEditView->GetEditSelection() );
+ EditSelection aNewSel = pImpEditView->pEditEngine->TransliterateText( pImpEditView->GetEditSelection(), nTransliterationMode );
+ if ( aNewSel != aOldSel )
+ {
+ pImpEditView->DrawSelectionXOR();
+ pImpEditView->SetEditSelection( aNewSel );
+ pImpEditView->DrawSelectionXOR();
+ }
+}
+
+void EditView::CompleteAutoCorrect( vcl::Window const * pFrameWin )
+{
+ if ( !HasSelection() && pImpEditView->pEditEngine->pImpEditEngine->GetStatus().DoAutoCorrect() )
+ {
+ pImpEditView->DrawSelectionXOR();
+ EditSelection aSel = pImpEditView->GetEditSelection();
+ aSel = pImpEditView->pEditEngine->EndOfWord( aSel.Max() );
+ aSel = pImpEditView->pEditEngine->pImpEditEngine->AutoCorrect( aSel, 0, !IsInsertMode(), pFrameWin );
+ pImpEditView->SetEditSelection( aSel );
+ if ( pImpEditView->pEditEngine->IsModified() )
+ pImpEditView->pEditEngine->FormatAndLayout( this );
+ }
+}
+
+EESpellState EditView::StartSpeller(weld::Widget* pDialogParent, bool bMultipleDoc)
+{
+ if ( !pImpEditView->pEditEngine->pImpEditEngine->GetSpeller().is() )
+ return EESpellState::NoSpeller;
+
+ return pImpEditView->pEditEngine->pImpEditEngine->Spell(this, pDialogParent, bMultipleDoc);
+}
+
+EESpellState EditView::StartThesaurus(weld::Widget* pDialogParent)
+{
+ if ( !pImpEditView->pEditEngine->pImpEditEngine->GetSpeller().is() )
+ return EESpellState::NoSpeller;
+
+ return pImpEditView->pEditEngine->pImpEditEngine->StartThesaurus(this, pDialogParent);
+}
+
+void EditView::StartTextConversion(weld::Widget* pDialogParent,
+ LanguageType nSrcLang, LanguageType nDestLang, const vcl::Font *pDestFont,
+ sal_Int32 nOptions, bool bIsInteractive, bool bMultipleDoc )
+{
+ pImpEditView->pEditEngine->pImpEditEngine->Convert(this, pDialogParent, nSrcLang, nDestLang, pDestFont, nOptions, bIsInteractive, bMultipleDoc);
+}
+
+sal_Int32 EditView::StartSearchAndReplace( const SvxSearchItem& rSearchItem )
+{
+ return pImpEditView->pEditEngine->pImpEditEngine->StartSearchAndReplace( this, rSearchItem );
+}
+
+bool EditView::IsCursorAtWrongSpelledWord()
+{
+ bool bIsWrong = false;
+ if ( !HasSelection() )
+ {
+ EditPaM aPaM = pImpEditView->GetEditSelection().Max();
+ bIsWrong = pImpEditView->IsWrongSpelledWord( aPaM, false/*bMarkIfWrong*/ );
+ }
+ return bIsWrong;
+}
+
+bool EditView::IsWrongSpelledWordAtPos( const Point& rPosPixel, bool bMarkIfWrong )
+{
+ Point aPos(pImpEditView->GetOutputDevice().PixelToLogic(rPosPixel));
+ aPos = pImpEditView->GetDocPos( aPos );
+ EditPaM aPaM = pImpEditView->pEditEngine->GetPaM(aPos, false);
+ return pImpEditView->IsWrongSpelledWord( aPaM , bMarkIfWrong );
+}
+
+static void LOKSendSpellPopupMenu(const weld::Menu& rMenu, LanguageType nGuessLangWord,
+ LanguageType nGuessLangPara, sal_uInt16 nSuggestions)
+{
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ // Generate the menu structure and send it to the client code.
+ SfxViewShell* pViewShell = SfxViewShell::Current();
+ if (!pViewShell)
+ return;
+
+ boost::property_tree::ptree aMenu;
+
+ boost::property_tree::ptree aItemTree;
+ if (nSuggestions)
+ {
+ for(int i = 0; i < nSuggestions; ++i)
+ {
+ OUString sItemId = OUString::number(MN_ALTSTART + i);
+ OUString sText = rMenu.get_label(sItemId);
+ aItemTree.put("text", sText.toUtf8().getStr());
+ aItemTree.put("type", "command");
+ OUString sCommandString = ".uno:SpellCheckApplySuggestion?ApplyRule:string=Spelling_" + sText;
+ aItemTree.put("command", sCommandString.toUtf8().getStr());
+ aItemTree.put("enabled", rMenu.get_sensitive(sItemId));
+ aMenu.push_back(std::make_pair("", aItemTree));
+ aItemTree.clear();
+ }
+
+ aItemTree.put("type", "separator");
+ aMenu.push_back(std::make_pair("", aItemTree));
+ aItemTree.clear();
+ }
+
+ // First we need to set item commands for the context menu.
+ OUString aTmpWord( SvtLanguageTable::GetLanguageString( nGuessLangWord ) );
+ OUString aTmpPara( SvtLanguageTable::GetLanguageString( nGuessLangPara ) );
+
+ aItemTree.put("text", rMenu.get_label("ignore").toUtf8().getStr());
+ aItemTree.put("type", "command");
+ aItemTree.put("command", ".uno:SpellCheckIgnoreAll?Type:string=Spelling");
+ aItemTree.put("enabled", rMenu.get_sensitive("ignore"));
+ aMenu.push_back(std::make_pair("", aItemTree));
+ aItemTree.clear();
+
+ aItemTree.put("type", "separator");
+ aMenu.push_back(std::make_pair("", aItemTree));
+ aItemTree.clear();
+
+ aItemTree.put("text", rMenu.get_label("wordlanguage").toUtf8().getStr());
+ aItemTree.put("type", "command");
+ OUString sCommandString = ".uno:LanguageStatus?Language:string=Current_" + aTmpWord;
+ aItemTree.put("command", sCommandString.toUtf8().getStr());
+ aItemTree.put("enabled", rMenu.get_sensitive("wordlanguage"));
+ aMenu.push_back(std::make_pair("", aItemTree));
+ aItemTree.clear();
+
+ aItemTree.put("text", rMenu.get_label("paralanguage").toUtf8().getStr());
+ aItemTree.put("type", "command");
+ sCommandString = ".uno:LanguageStatus?Language:string=Paragraph_" + aTmpPara;
+ aItemTree.put("command", sCommandString.toUtf8().getStr());
+ aItemTree.put("enabled", rMenu.get_sensitive("paralanguage"));
+ aMenu.push_back(std::make_pair("", aItemTree));
+ aItemTree.clear();
+
+ boost::property_tree::ptree aRoot;
+ aRoot.add_child("menu", aMenu);
+
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aRoot, true);
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CONTEXT_MENU, OString(aStream.str()));
+}
+
+bool EditView::ExecuteSpellPopup(const Point& rPosPixel, const Link<SpellCallbackInfo&,void> &rCallBack)
+{
+ OutputDevice& rDevice = pImpEditView->GetOutputDevice();
+ Point aPos(rDevice.PixelToLogic(rPosPixel));
+ aPos = pImpEditView->GetDocPos( aPos );
+ EditPaM aPaM = pImpEditView->pEditEngine->GetPaM(aPos, false);
+ Reference< linguistic2::XSpellChecker1 > xSpeller( pImpEditView->pEditEngine->pImpEditEngine->GetSpeller() );
+ ESelection aOldSel = GetSelection();
+ if ( !(xSpeller.is() && pImpEditView->IsWrongSpelledWord( aPaM, true )) )
+ return false;
+
+ // PaMtoEditCursor returns Logical units
+ tools::Rectangle aTempRect = pImpEditView->pEditEngine->pImpEditEngine->PaMtoEditCursor( aPaM, GetCursorFlags::TextOnly );
+ // GetWindowPos works in Logical units
+ aTempRect = pImpEditView->GetWindowPos(aTempRect);
+ // Convert to pixels
+ aTempRect = rDevice.LogicToPixel(aTempRect);
+
+ weld::Widget* pPopupParent = pImpEditView->GetPopupParent(aTempRect);
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pPopupParent, "editeng/ui/spellmenu.ui"));
+ std::unique_ptr<weld::Menu> xPopupMenu(xBuilder->weld_menu("editviewspellmenu"));
+ std::unique_ptr<weld::Menu> xInsertMenu(xBuilder->weld_menu("insertmenu")); // add word to user-dictionaries
+ std::unique_ptr<weld::Menu> xAutoMenu(xBuilder->weld_menu("automenu"));
+
+ EditPaM aPaM2( aPaM );
+ aPaM2.SetIndex( aPaM2.GetIndex()+1 );
+
+ // Are there any replace suggestions?
+ OUString aSelected( GetSelected() );
+
+ // restrict the maximal number of suggestions displayed
+ // in the context menu.
+ // Note: That could of course be done by clipping the
+ // resulting sequence but the current third party
+ // implementations result differs greatly if the number of
+ // suggestions to be returned gets changed. Statistically
+ // it gets much better if told to return e.g. only 7 strings
+ // than returning e.g. 16 suggestions and using only the
+ // first 7. Thus we hand down the value to use to that
+ // implementation here by providing an additional parameter.
+ Sequence< PropertyValue > aPropVals { comphelper::makePropertyValue(UPN_MAX_NUMBER_OF_SUGGESTIONS, sal_Int16(7)) };
+
+ // Are there any replace suggestions?
+ Reference< linguistic2::XSpellAlternatives > xSpellAlt =
+ xSpeller->spell( aSelected, static_cast<sal_uInt16>(pImpEditView->pEditEngine->pImpEditEngine->GetLanguage( aPaM2 ).nLang), aPropVals );
+
+ Reference< linguistic2::XLanguageGuessing > xLangGuesser( EditDLL::Get().GetGlobalData()->GetLanguageGuesser() );
+
+ // check if text might belong to a different language...
+ LanguageType nGuessLangWord = LANGUAGE_NONE;
+ LanguageType nGuessLangPara = LANGUAGE_NONE;
+ if (xSpellAlt.is() && xLangGuesser.is())
+ {
+ OUString aParaText;
+ ContentNode *pNode = aPaM.GetNode();
+ if (pNode)
+ {
+ aParaText = pNode->GetString();
+ }
+ else
+ {
+ OSL_FAIL( "content node is NULL" );
+ }
+
+ nGuessLangWord = CheckLanguage( xSpellAlt->getWord(), xSpeller, xLangGuesser, false );
+ nGuessLangPara = CheckLanguage( aParaText, xSpeller, xLangGuesser, true );
+ }
+ if (nGuessLangWord != LANGUAGE_NONE || nGuessLangPara != LANGUAGE_NONE)
+ {
+ // make sure LANGUAGE_NONE gets not used as menu entry
+ if (nGuessLangWord == LANGUAGE_NONE)
+ nGuessLangWord = nGuessLangPara;
+ if (nGuessLangPara == LANGUAGE_NONE)
+ nGuessLangPara = nGuessLangWord;
+
+ xPopupMenu->append_separator("separator1");
+ OUString aTmpWord( SvtLanguageTable::GetLanguageString( nGuessLangWord ) );
+ OUString aTmpPara( SvtLanguageTable::GetLanguageString( nGuessLangPara ) );
+ OUString aWordStr( EditResId( RID_STR_WORD ) );
+ aWordStr = aWordStr.replaceFirst( "%x", aTmpWord );
+ OUString aParaStr( EditResId( RID_STR_PARAGRAPH ) );
+ aParaStr = aParaStr.replaceFirst( "%x", aTmpPara );
+ xPopupMenu->append("wordlanguage", aWordStr);
+ xPopupMenu->append("paralanguage", aParaStr);
+ }
+
+ // Replace suggestions...
+ Sequence< OUString > aAlt;
+ if (xSpellAlt.is())
+ aAlt = xSpellAlt->getAlternatives();
+ const OUString *pAlt = aAlt.getConstArray();
+ sal_uInt16 nWords = static_cast<sal_uInt16>(aAlt.getLength());
+ if ( nWords )
+ {
+ for ( sal_uInt16 nW = 0; nW < nWords; nW++ )
+ {
+ OUString aAlternate( pAlt[nW] );
+ OUString sId(OUString::number(MN_ALTSTART + nW));
+ xPopupMenu->insert(nW, sId, aAlternate, nullptr, nullptr, nullptr, TRISTATE_INDET);
+ xAutoMenu->append(sId, aAlternate);
+ }
+ xPopupMenu->insert_separator(nWords, "separator2");
+ }
+ else
+ {
+ xAutoMenu.reset();
+ xPopupMenu->remove("autocorrect");
+ }
+
+ SvtLinguConfig aCfg;
+
+ Reference< linguistic2::XSearchableDictionaryList > xDicList( LinguMgr::GetDictionaryList() );
+ Sequence< Reference< linguistic2::XDictionary > > aDics;
+ if (xDicList.is())
+ {
+ const Reference< linguistic2::XDictionary > *pDic = nullptr;
+ // add the default positive dictionary to dic-list (if not already done).
+ // This is to ensure that there is at least one dictionary to which
+ // words could be added.
+ uno::Reference< linguistic2::XDictionary > xDic( LinguMgr::GetStandardDic() );
+ if (xDic.is())
+ xDic->setActive( true );
+
+ aDics = xDicList->getDictionaries();
+ pDic = aDics.getConstArray();
+ LanguageType nCheckedLanguage = pImpEditView->pEditEngine->pImpEditEngine->GetLanguage( aPaM2 ).nLang;
+ sal_uInt16 nDicCount = static_cast<sal_uInt16>(aDics.getLength());
+ for (sal_uInt16 i = 0; i < nDicCount; i++)
+ {
+ uno::Reference< linguistic2::XDictionary > xDicTmp = pDic[i];
+ if (!xDicTmp.is() || LinguMgr::GetIgnoreAllList() == xDicTmp)
+ continue;
+
+ uno::Reference< frame::XStorable > xStor( xDicTmp, uno::UNO_QUERY );
+ LanguageType nActLanguage = LanguageTag( xDicTmp->getLocale() ).getLanguageType();
+ if( xDicTmp->isActive()
+ && xDicTmp->getDictionaryType() != linguistic2::DictionaryType_NEGATIVE
+ && (nCheckedLanguage == nActLanguage || LANGUAGE_NONE == nActLanguage )
+ && (!xStor.is() || !xStor->isReadonly()) )
+ {
+ OUString sImage;
+
+ uno::Reference< lang::XServiceInfo > xSvcInfo( xDicTmp, uno::UNO_QUERY );
+ if (xSvcInfo.is())
+ {
+ OUString aDictionaryImageUrl( aCfg.GetSpellAndGrammarContextDictionaryImage(
+ xSvcInfo->getImplementationName()) );
+ if (!aDictionaryImageUrl.isEmpty() )
+ sImage = aDictionaryImageUrl;
+ }
+
+ if (sImage.isEmpty())
+ {
+ xInsertMenu->append(OUString::number(MN_DICTSTART + i), xDicTmp->getName());
+ }
+ else
+ {
+ Image aImage(sImage);
+ ScopedVclPtr<VirtualDevice> xVirDev(pPopupParent->create_virtual_device());
+ Size aSize(aImage.GetSizePixel());
+ xVirDev->SetOutputSizePixel(aSize);
+ xVirDev->DrawImage(Point(0, 0), aImage);
+ xInsertMenu->append(OUString::number(MN_DICTSTART + i), xDicTmp->getName(), *xVirDev);
+ }
+ aDicNameSingle = xDicTmp->getName();
+ }
+ }
+ }
+
+ if (xInsertMenu->n_children() != 1)
+ xPopupMenu->remove("add");
+ if (xInsertMenu->n_children() < 2)
+ {
+ xInsertMenu.reset();
+ xPopupMenu->remove("insert");
+ }
+
+ //tdf#106123 store and restore the EditPaM around the menu Execute
+ //because the loss of focus in the current editeng causes writer
+ //annotations to save their contents, making the pContent of the
+ //current EditPams invalid
+ EPaM aP = pImpEditView->pEditEngine->pImpEditEngine->CreateEPaM(aPaM);
+ EPaM aP2 = pImpEditView->pEditEngine->pImpEditEngine->CreateEPaM(aPaM2);
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ xPopupMenu->remove("autocorrect");
+ xPopupMenu->remove("autocorrectdlg");
+
+ LOKSendSpellPopupMenu(*xPopupMenu, nGuessLangWord, nGuessLangPara, nWords);
+ return true;
+ }
+
+ OUString sId = xPopupMenu->popup_at_rect(pPopupParent, aTempRect);
+
+ aPaM2 = pImpEditView->pEditEngine->pImpEditEngine->CreateEditPaM(aP2);
+ aPaM = pImpEditView->pEditEngine->pImpEditEngine->CreateEditPaM(aP);
+
+ if (sId == "ignore")
+ {
+ OUString aWord = pImpEditView->SpellIgnoreWord();
+ SpellCallbackInfo aInf( SpellCallbackCommand::IGNOREWORD, aWord );
+ rCallBack.Call(aInf);
+ SetSelection( aOldSel );
+ }
+ else if (sId == "wordlanguage" || sId == "paralanguage")
+ {
+ LanguageType nLangToUse = (sId == "wordlanguage") ? nGuessLangWord : nGuessLangPara;
+ SvtScriptType nScriptType = SvtLanguageOptions::GetScriptTypeOfLanguage( nLangToUse );
+
+ SfxItemSet aAttrs = GetEditEngine()->GetEmptyItemSet();
+ if (nScriptType == SvtScriptType::LATIN)
+ aAttrs.Put( SvxLanguageItem( nLangToUse, EE_CHAR_LANGUAGE ) );
+ if (nScriptType == SvtScriptType::COMPLEX)
+ aAttrs.Put( SvxLanguageItem( nLangToUse, EE_CHAR_LANGUAGE_CTL ) );
+ if (nScriptType == SvtScriptType::ASIAN)
+ aAttrs.Put( SvxLanguageItem( nLangToUse, EE_CHAR_LANGUAGE_CJK ) );
+ if (sId == "paralanguage")
+ {
+ ESelection aSel = GetSelection();
+ aSel.nStartPos = 0;
+ aSel.nEndPos = EE_TEXTPOS_ALL;
+ SetSelection( aSel );
+ }
+ SetAttribs( aAttrs );
+ pImpEditView->pEditEngine->pImpEditEngine->StartOnlineSpellTimer();
+
+ SpellCallbackInfo aInf((sId == "wordlanguage") ? SpellCallbackCommand::WORDLANGUAGE : SpellCallbackCommand::PARALANGUAGE);
+ rCallBack.Call(aInf);
+ SetSelection( aOldSel );
+ }
+ else if (sId == "check")
+ {
+ SpellCallbackInfo aInf( SpellCallbackCommand::STARTSPELLDLG, OUString() );
+ rCallBack.Call(aInf);
+ }
+ else if (sId == "autocorrectdlg")
+ {
+ SpellCallbackInfo aInf( SpellCallbackCommand::AUTOCORRECT_OPTIONS, OUString() );
+ rCallBack.Call(aInf);
+ }
+ else if ( sId.toInt32() >= MN_DICTSTART || sId == "add")
+ {
+ OUString aDicName;
+ if (sId.toInt32() >= MN_DICTSTART)
+ {
+ assert(xInsertMenu && "this case only occurs when xInsertMenu exists");
+ // strip_mnemonic is necessary to retrieve the correct dictionary name
+ aDicName = pPopupParent->strip_mnemonic(xInsertMenu->get_label(sId));
+ }
+ else
+ aDicName = aDicNameSingle;
+
+ uno::Reference< linguistic2::XDictionary > xDic;
+ if (xDicList.is())
+ xDic = xDicList->getDictionaryByName( aDicName );
+
+ if (xDic.is())
+ xDic->add( aSelected, false, OUString() );
+ // save modified user-dictionary if it is persistent
+ Reference< frame::XStorable > xSavDic( xDic, UNO_QUERY );
+ if (xSavDic.is())
+ xSavDic->store();
+
+ aPaM.GetNode()->GetWrongList()->ResetInvalidRange(0, aPaM.GetNode()->Len());
+ pImpEditView->pEditEngine->pImpEditEngine->StartOnlineSpellTimer();
+
+ SpellCallbackInfo aInf( SpellCallbackCommand::ADDTODICTIONARY, aSelected );
+ rCallBack.Call(aInf);
+ SetSelection( aOldSel );
+ }
+ else if ( sId.toInt32() >= MN_AUTOSTART )
+ {
+ DBG_ASSERT(sId.toInt32() - MN_AUTOSTART < aAlt.getLength(), "index out of range");
+ OUString aWord = pAlt[sId.toInt32() - MN_AUTOSTART];
+ SvxAutoCorrect* pAutoCorrect = SvxAutoCorrCfg::Get().GetAutoCorrect();
+ if ( pAutoCorrect )
+ pAutoCorrect->PutText( aSelected, aWord, pImpEditView->pEditEngine->pImpEditEngine->GetLanguage( aPaM2 ).nLang );
+ InsertText( aWord );
+ }
+ else if ( sId.toInt32() >= MN_ALTSTART ) // Replace
+ {
+ DBG_ASSERT(sId.toInt32() - MN_ALTSTART < aAlt.getLength(), "index out of range");
+ OUString aWord = pAlt[sId.toInt32() - MN_ALTSTART];
+ InsertText( aWord );
+ }
+ else
+ {
+ SetSelection( aOldSel );
+ }
+ return true;
+}
+
+OUString EditView::SpellIgnoreWord()
+{
+ return pImpEditView->SpellIgnoreWord();
+}
+
+void EditView::SelectCurrentWord( sal_Int16 nWordType )
+{
+ EditSelection aCurSel( pImpEditView->GetEditSelection() );
+ pImpEditView->DrawSelectionXOR();
+ aCurSel = pImpEditView->pEditEngine->SelectWord(aCurSel.Max(), nWordType);
+ pImpEditView->SetEditSelection( aCurSel );
+ pImpEditView->DrawSelectionXOR();
+ ShowCursor( true, false );
+}
+
+void EditView::InsertParaBreak()
+{
+ pImpEditView->pEditEngine->UndoActionStart(EDITUNDO_INSERT);
+ pImpEditView->DeleteSelected();
+ EditPaM aPaM(pImpEditView->pEditEngine->InsertParaBreak(pImpEditView->GetEditSelection()));
+ pImpEditView->pEditEngine->UndoActionEnd();
+ pImpEditView->SetEditSelection(EditSelection(aPaM, aPaM));
+ if (pImpEditView->pEditEngine->IsUpdateLayout())
+ pImpEditView->pEditEngine->FormatAndLayout(this);
+}
+
+void EditView::InsertField( const SvxFieldItem& rFld )
+{
+ EditEngine* pEE = pImpEditView->pEditEngine;
+ pImpEditView->DrawSelectionXOR();
+ pEE->UndoActionStart( EDITUNDO_INSERT );
+ EditPaM aPaM( pEE->InsertField( pImpEditView->GetEditSelection(), rFld ) );
+ pEE->UndoActionEnd();
+ pImpEditView->SetEditSelection( EditSelection( aPaM, aPaM ) );
+ pEE->UpdateFields();
+ if (pImpEditView->pEditEngine->IsUpdateLayout())
+ pEE->FormatAndLayout( this );
+}
+
+const SvxFieldItem* EditView::GetFieldUnderMousePointer() const
+{
+ sal_Int32 nPara;
+ sal_Int32 nPos;
+ return GetFieldUnderMousePointer( nPara, nPos );
+}
+
+const SvxFieldItem* EditView::GetField( const Point& rPos, sal_Int32* pPara, sal_Int32* pPos ) const
+{
+ return pImpEditView->GetField( rPos, pPara, pPos );
+}
+
+const SvxFieldItem* EditView::GetFieldUnderMousePointer( sal_Int32& nPara, sal_Int32& nPos ) const
+{
+ Point aPos;
+ if (EditViewCallbacks* pEditViewCallbacks = pImpEditView->getEditViewCallbacks())
+ aPos = pEditViewCallbacks->EditViewPointerPosPixel();
+ else
+ aPos = pImpEditView->GetWindow()->GetPointerPosPixel();
+ OutputDevice& rDevice = pImpEditView->GetOutputDevice();
+ aPos = rDevice.PixelToLogic(aPos);
+ return GetField( aPos, &nPara, &nPos );
+}
+
+const SvxFieldItem* EditView::GetFieldAtSelection(bool bAlsoCheckBeforeCursor) const
+{
+ bool* pIsBeforeCursor = bAlsoCheckBeforeCursor ? &bAlsoCheckBeforeCursor : nullptr;
+ return GetFieldAtSelection(pIsBeforeCursor);
+}
+
+// If pIsBeforeCursor != nullptr, the position before the cursor will also be checked for a field
+// and pIsBeforeCursor will return true if that fallback field is returned.
+// If no field is returned, the value in pIsBeforeCursor is meaningless.
+const SvxFieldItem* EditView::GetFieldAtSelection(bool* pIsBeforeCursor) const
+{
+ // a field is a dummy character - so it cannot span nodes or be a selection larger than 1
+ EditSelection aSel( pImpEditView->GetEditSelection() );
+ if (aSel.Min().GetNode() != aSel.Max().GetNode())
+ return nullptr;
+
+ // normalize: min < max
+ aSel.Adjust( pImpEditView->pEditEngine->GetEditDoc() );
+
+ const sal_Int32 nMinIndex = aSel.Min().GetIndex();
+ const sal_Int32 nMaxIndex = aSel.Max().GetIndex();
+ if (nMaxIndex > nMinIndex + 1)
+ return nullptr;
+
+ // Only when cursor is in font of field, no selection,
+ // or only selecting field
+ bool bAlsoCheckBeforeCursor = false;
+ if (pIsBeforeCursor)
+ {
+ *pIsBeforeCursor = false;
+ bAlsoCheckBeforeCursor = nMaxIndex == nMinIndex;
+ }
+ const SvxFieldItem* pFoundBeforeCursor = nullptr;
+ const CharAttribList::AttribsType& rAttrs = aSel.Min().GetNode()->GetCharAttribs().GetAttribs();
+ for (const auto& rAttr: rAttrs)
+ {
+ if (rAttr->Which() == EE_FEATURE_FIELD)
+ {
+ DBG_ASSERT(dynamic_cast<const SvxFieldItem*>(rAttr->GetItem()), "No FieldItem...");
+ if (rAttr->GetStart() == nMinIndex)
+ return static_cast<const SvxFieldItem*>(rAttr->GetItem());
+
+ // perhaps the cursor is behind the field?
+ if (nMinIndex && rAttr->GetStart() == nMinIndex - 1)
+ pFoundBeforeCursor = static_cast<const SvxFieldItem*>(rAttr->GetItem());
+ }
+ }
+ if (bAlsoCheckBeforeCursor)
+ {
+ *pIsBeforeCursor = /*(bool)*/pFoundBeforeCursor;
+ return pFoundBeforeCursor;
+ }
+ return nullptr;
+}
+
+void EditView::SelectFieldAtCursor()
+{
+ bool bIsBeforeCursor = false;
+ const SvxFieldItem* pFieldItem = GetFieldAtSelection(&bIsBeforeCursor);
+ if (!pFieldItem)
+ return;
+
+ // Make sure the whole field is selected
+ // A field is represented by a dummy character - so it cannot be a selection larger than 1
+ ESelection aSel = GetSelection();
+ if (aSel.nStartPos == aSel.nEndPos) // not yet selected
+ {
+ if (bIsBeforeCursor)
+ {
+ assert (aSel.nStartPos);
+ --aSel.nStartPos;
+ }
+ else
+ aSel.nEndPos++;
+ SetSelection(aSel);
+ }
+ else
+ assert(std::abs(aSel.nStartPos - aSel.nEndPos) == 1);
+}
+
+const SvxFieldData* EditView::GetFieldUnderMouseOrInSelectionOrAtCursor(bool bAlsoCheckBeforeCursor) const
+{
+ const SvxFieldItem* pFieldItem = GetFieldUnderMousePointer();
+ if (!pFieldItem)
+ pFieldItem = GetFieldAtSelection(bAlsoCheckBeforeCursor);
+
+ return pFieldItem ? pFieldItem->GetField() : nullptr;
+}
+
+sal_Int32 EditView::countFieldsOffsetSum(sal_Int32 nPara, sal_Int32 nPos, bool bCanOverflow) const
+{
+ if (!pImpEditView || !pImpEditView->pEditEngine)
+ return 0;
+
+ int nOffset = 0;
+
+ for (int nCurrentPara = 0; nCurrentPara <= nPara; nCurrentPara++)
+ {
+ int nFields = pImpEditView->pEditEngine->GetFieldCount( nCurrentPara );
+ for (int nField = 0; nField < nFields; nField++)
+ {
+ EFieldInfo aFieldInfo
+ = pImpEditView->pEditEngine->GetFieldInfo( nCurrentPara, nField );
+
+ bool bLastPara = nCurrentPara == nPara;
+ sal_Int32 nFieldPos = aFieldInfo.aPosition.nIndex;
+
+ if (bLastPara && nFieldPos >= nPos)
+ break;
+
+ sal_Int32 nFieldLen = aFieldInfo.aCurrentText.getLength();
+
+ // position in the middle of a field
+ if (!bCanOverflow && bLastPara && nFieldPos + nFieldLen > nPos)
+ nFieldLen = nPos - nFieldPos;
+
+ nOffset += nFieldLen - 1;
+ }
+ }
+
+ return nOffset;
+}
+
+sal_Int32 EditView::GetPosNoField(sal_Int32 nPara, sal_Int32 nPos) const
+{
+ sal_Int32 nOffset = countFieldsOffsetSum(nPara, nPos, false);
+ assert(nPos >= nOffset);
+ return nPos - nOffset;
+}
+
+sal_Int32 EditView::GetPosWithField(sal_Int32 nPara, sal_Int32 nPos) const
+{
+ sal_Int32 nOffset = countFieldsOffsetSum(nPara, nPos, true);
+ return nPos + nOffset;
+}
+
+void EditView::SetInvalidateMore( sal_uInt16 nPixel )
+{
+ pImpEditView->SetInvalidateMore( nPixel );
+}
+
+sal_uInt16 EditView::GetInvalidateMore() const
+{
+ return pImpEditView->GetInvalidateMore();
+}
+
+static void ChangeFontSizeImpl( EditView* pEditView, bool bGrow, const ESelection& rSel, const FontList* pFontList )
+{
+ pEditView->SetSelection( rSel );
+
+ SfxItemSet aSet( pEditView->GetAttribs() );
+ if( EditView::ChangeFontSize( bGrow, aSet, pFontList ) )
+ {
+ SfxItemSet aNewSet( pEditView->GetEmptyItemSet() );
+ aNewSet.Put( aSet.Get( EE_CHAR_FONTHEIGHT ) );
+ aNewSet.Put( aSet.Get( EE_CHAR_FONTHEIGHT_CJK ) );
+ aNewSet.Put( aSet.Get( EE_CHAR_FONTHEIGHT_CTL ) );
+ pEditView->SetAttribs( aNewSet );
+ }
+}
+
+void EditView::ChangeFontSize( bool bGrow, const FontList* pFontList )
+{
+
+ EditEngine& rEditEngine = *pImpEditView->pEditEngine;
+
+ ESelection aSel( GetSelection() );
+ ESelection aOldSelection( aSel );
+ aSel.Adjust();
+
+ if( !aSel.HasRange() )
+ {
+ aSel = rEditEngine.GetWord( aSel, css::i18n::WordType::DICTIONARY_WORD );
+ }
+
+ if( aSel.HasRange() )
+ {
+ for( sal_Int32 nPara = aSel.nStartPara; nPara <= aSel.nEndPara; nPara++ )
+ {
+ std::vector<sal_Int32> aPortions;
+ rEditEngine.GetPortions( nPara, aPortions );
+
+ if( aPortions.empty() )
+ aPortions.push_back( rEditEngine.GetTextLen(nPara) );
+
+ const sal_Int32 nBeginPos = (nPara == aSel.nStartPara) ? aSel.nStartPos : 0;
+ const sal_Int32 nEndPos = (nPara == aSel.nEndPara) ? aSel.nEndPos : EE_TEXTPOS_ALL;
+
+ for ( size_t nPos = 0; nPos < aPortions.size(); ++nPos )
+ {
+ sal_Int32 nPortionEnd = aPortions[ nPos ];
+ sal_Int32 nPortionStart = nPos > 0 ? aPortions[ nPos - 1 ] : 0;
+
+ if( (nPortionEnd < nBeginPos) || (nPortionStart > nEndPos) )
+ continue;
+
+ if( nPortionStart < nBeginPos )
+ nPortionStart = nBeginPos;
+ if( nPortionEnd > nEndPos )
+ nPortionEnd = nEndPos;
+
+ if( nPortionStart == nPortionEnd )
+ continue;
+
+ ESelection aPortionSel( nPara, nPortionStart, nPara, nPortionEnd );
+ ChangeFontSizeImpl( this, bGrow, aPortionSel, pFontList );
+ }
+ }
+ }
+ else
+ {
+ ChangeFontSizeImpl( this, bGrow, aSel, pFontList );
+ }
+
+ SetSelection( aOldSelection );
+}
+
+bool EditView::ChangeFontSize( bool bGrow, SfxItemSet& rSet, const FontList* pFontList )
+{
+ if (!pFontList)
+ return false;
+
+ static const sal_uInt16 gFontSizeWichMap[] = { EE_CHAR_FONTHEIGHT, EE_CHAR_FONTHEIGHT_CJK, EE_CHAR_FONTHEIGHT_CTL, 0 };
+ bool bRet = false;
+
+ const sal_uInt16* pWhich = gFontSizeWichMap;
+ while( *pWhich )
+ {
+ SvxFontHeightItem aFontHeightItem( static_cast<const SvxFontHeightItem&>(rSet.Get( *pWhich )) );
+ tools::Long nHeight = aFontHeightItem.GetHeight();
+ const MapUnit eUnit = rSet.GetPool()->GetMetric( *pWhich );
+ nHeight = OutputDevice::LogicToLogic(nHeight * 10, eUnit, MapUnit::MapPoint);
+
+ const int* pAry = FontList::GetStdSizeAry();
+
+ if( bGrow )
+ {
+ while( *pAry )
+ {
+ if( *pAry > nHeight )
+ {
+ nHeight = *pAry;
+ break;
+ }
+ pAry++;
+ }
+
+ if( *pAry == 0 )
+ {
+ nHeight += (nHeight + 5) / 10;
+ if( nHeight > 9999 )
+ nHeight = 9999;
+ }
+
+ }
+ else if( *pAry )
+ {
+ bool bFound = false;
+ if( *pAry < nHeight )
+ {
+ pAry++;
+ while( *pAry )
+ {
+ if( *pAry >= nHeight )
+ {
+ nHeight = pAry[-1];
+ bFound = true;
+ break;
+ }
+ pAry++;
+ }
+ }
+
+ if( !bFound )
+ {
+ nHeight -= (nHeight + 5) / 10;
+ if( nHeight < 2 )
+ nHeight = 2;
+ }
+ }
+
+ if( (nHeight >= 2) && (nHeight <= 9999 ) )
+ {
+ nHeight = OutputDevice::LogicToLogic( nHeight, MapUnit::MapPoint, eUnit ) / 10;
+
+ if( nHeight != static_cast<tools::Long>(aFontHeightItem.GetHeight()) )
+ {
+ aFontHeightItem.SetHeight( nHeight );
+ rSet.Put( aFontHeightItem.CloneSetWhich(*pWhich) );
+ bRet = true;
+ }
+ }
+ pWhich++;
+ }
+ return bRet;
+}
+
+OUString EditView::GetSurroundingText() const
+{
+ EditSelection aSel( pImpEditView->GetEditSelection() );
+ aSel.Adjust( pImpEditView->pEditEngine->GetEditDoc() );
+
+ if( HasSelection() )
+ {
+ OUString aStr = pImpEditView->pEditEngine->GetSelected(aSel);
+
+ // Stop reconversion if the selected text includes a line break.
+ if ( aStr.indexOf( 0x0A ) == -1 )
+ return aStr;
+ else
+ return OUString();
+ }
+ else
+ {
+ aSel.Min().SetIndex( 0 );
+ aSel.Max().SetIndex( aSel.Max().GetNode()->Len() );
+ return pImpEditView->pEditEngine->GetSelected(aSel);
+ }
+}
+
+Selection EditView::GetSurroundingTextSelection() const
+{
+ ESelection aSelection( GetSelection() );
+ aSelection.Adjust();
+
+ if( HasSelection() )
+ {
+ EditSelection aSel( pImpEditView->GetEditSelection() );
+ aSel.Adjust( pImpEditView->pEditEngine->GetEditDoc() );
+ OUString aStr = pImpEditView->pEditEngine->GetSelected(aSel);
+
+ // Stop reconversion if the selected text includes a line break.
+ if ( aStr.indexOf( 0x0A ) == -1 )
+ return Selection( 0, aSelection.nEndPos - aSelection.nStartPos );
+ else
+ return Selection( 0, 0 );
+ }
+ else
+ {
+ return Selection( aSelection.nStartPos, aSelection.nEndPos );
+ }
+}
+
+bool EditView::DeleteSurroundingText(const Selection& rRange)
+{
+ ESelection aSel(GetSelection());
+ aSel.nEndPara = aSel.nStartPara;
+ aSel.nStartPos = rRange.Min();
+ aSel.nEndPos = rRange.Max();
+ SetSelection(aSel);
+ DeleteSelected();
+ return true;
+}
+
+void EditView::SetCursorLogicPosition(const Point& rPosition, bool bPoint, bool bClearMark)
+{
+ Point aDocPos(pImpEditView->GetDocPos(rPosition));
+ EditPaM aPaM = pImpEditView->pEditEngine->GetPaM(aDocPos);
+ EditSelection aSelection(pImpEditView->GetEditSelection());
+
+ // Explicitly create or delete the selection.
+ if (bClearMark)
+ {
+ pImpEditView->DeselectAll();
+ aSelection = pImpEditView->GetEditSelection();
+ }
+ else
+ pImpEditView->CreateAnchor();
+
+ if (bPoint)
+ aSelection.Max() = aPaM;
+ else
+ aSelection.Min() = aPaM;
+
+ if (pImpEditView->GetEditSelection().Min() != aSelection.Min())
+ {
+ const ContentNode* pNode(pImpEditView->GetEditSelection().Min().GetNode());
+ if (nullptr != pNode)
+ pNode->checkAndDeleteEmptyAttribs();
+ }
+
+ pImpEditView->DrawSelectionXOR(aSelection);
+ if (pImpEditView->GetEditSelection() != aSelection)
+ pImpEditView->SetEditSelection(aSelection);
+ ShowCursor(/*bGotoCursor=*/false);
+}
+
+void EditView::DrawSelectionXOR(OutlinerViewShell* pOtherShell)
+{
+ pImpEditView->RegisterOtherShell(pOtherShell);
+ pImpEditView->DrawSelectionXOR();
+ pImpEditView->RegisterOtherShell(nullptr);
+}
+
+void EditView::InitLOKSpecialPositioning(MapUnit eUnit,
+ const tools::Rectangle& rOutputArea,
+ const Point& rVisDocStartPos)
+{
+ pImpEditView->InitLOKSpecialPositioning(eUnit, rOutputArea, rVisDocStartPos);
+}
+
+void EditView::SetLOKSpecialOutputArea(const tools::Rectangle& rOutputArea)
+{
+ pImpEditView->SetLOKSpecialOutputArea(rOutputArea);
+}
+
+const tools::Rectangle & EditView::GetLOKSpecialOutputArea() const
+{
+ return pImpEditView->GetLOKSpecialOutputArea();
+}
+
+void EditView::SetLOKSpecialVisArea(const tools::Rectangle& rVisArea)
+{
+ pImpEditView->SetLOKSpecialVisArea(rVisArea);
+}
+
+tools::Rectangle EditView::GetLOKSpecialVisArea() const
+{
+ return pImpEditView->GetLOKSpecialVisArea();
+}
+
+bool EditView::HasLOKSpecialPositioning() const
+{
+ return pImpEditView->HasLOKSpecialPositioning();
+}
+
+void EditView::SetLOKSpecialFlags(LOKSpecialFlags eFlags)
+{
+ pImpEditView->SetLOKSpecialFlags(eFlags);
+}
+
+void EditView::SuppressLOKMessages(bool bSet)
+{
+ pImpEditView->SuppressLOKMessages(bSet);
+}
+
+bool EditView::IsSuppressLOKMessages() const
+{
+ return pImpEditView->IsSuppressLOKMessages();
+}
+
+void EditView::SetNegativeX(bool bSet)
+{
+ pImpEditView->SetNegativeX(bSet);
+}
+
+bool EditView::IsNegativeX() const
+{
+ return pImpEditView->IsNegativeX();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/edtspell.cxx b/editeng/source/editeng/edtspell.cxx
new file mode 100644
index 0000000000..c07361bd19
--- /dev/null
+++ b/editeng/source/editeng/edtspell.cxx
@@ -0,0 +1,708 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "impedit.hxx"
+#include <sal/log.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <editeng/editview.hxx>
+#include <editeng/editeng.hxx>
+#include <edtspell.hxx>
+#include <editeng/flditem.hxx>
+#include <svl/intitem.hxx>
+#include <svl/eitem.hxx>
+#include <editeng/unolingu.hxx>
+#include <com/sun/star/linguistic2/XDictionary.hpp>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::linguistic2;
+
+
+EditSpellWrapper::EditSpellWrapper(weld::Widget* pWindow,
+ bool bIsStart, EditView* pView )
+ : SvxSpellWrapper(pWindow, bIsStart, false/*bIsAllRight*/)
+{
+ SAL_WARN_IF( !pView, "editeng", "One view has to be abandoned!" );
+ // Keep IgnoreList, delete ReplaceList...
+ if (LinguMgr::GetChangeAllList().is())
+ LinguMgr::GetChangeAllList()->clear();
+ pEditView = pView;
+}
+
+void EditSpellWrapper::SpellStart( SvxSpellArea eArea )
+{
+ EditEngine* pEE = pEditView->GetEditEngine();
+ ImpEditEngine* pImpEE = pEditView->GetImpEditEngine();
+ SpellInfo* pSpellInfo = pImpEE->GetSpellInfo();
+
+ if ( eArea == SvxSpellArea::BodyStart )
+ {
+ // Is called when
+ // a) Spell-Forward has arrived at the end and should restart at the top
+ // IsEndDone() returns also true, when backward-spelling is started at the end!
+ if ( IsEndDone() )
+ {
+ pSpellInfo->bSpellToEnd = false;
+ pSpellInfo->aSpellTo = pSpellInfo->aSpellStart;
+ pEditView->GetImpEditView()->SetEditSelection(
+ pEE->GetEditDoc().GetStartPaM() );
+ }
+ else
+ {
+ pSpellInfo->bSpellToEnd = true;
+ pSpellInfo->aSpellTo = pImpEE->CreateEPaM(
+ pEE->GetEditDoc().GetStartPaM() );
+ }
+ }
+ else if ( eArea == SvxSpellArea::BodyEnd )
+ {
+ // Is called when
+ // a) Spell-Forward is launched
+ // IsStartDone() return also true, when forward-spelling is started at the beginning!
+ if ( !IsStartDone() )
+ {
+ pSpellInfo->bSpellToEnd = true;
+ pSpellInfo->aSpellTo = pImpEE->CreateEPaM(
+ pEE->GetEditDoc().GetEndPaM() );
+ }
+ else
+ {
+ pSpellInfo->bSpellToEnd = false;
+ pSpellInfo->aSpellTo = pSpellInfo->aSpellStart;
+ pEditView->GetImpEditView()->SetEditSelection(
+ pEE->GetEditDoc().GetEndPaM() );
+ }
+ }
+ else if ( eArea == SvxSpellArea::Body )
+ {
+ ; // Is handled by the App through SpellNextDocument
+ }
+ else
+ {
+ OSL_FAIL( "SpellStart: Unknown Area!" );
+ }
+}
+
+void EditSpellWrapper::SpellContinue()
+{
+ SetLast( pEditView->GetImpEditEngine()->ImpSpell( pEditView ) );
+}
+
+bool EditSpellWrapper::SpellMore()
+{
+ EditEngine* pEE = pEditView->GetEditEngine();
+ ImpEditEngine* pImpEE = pEditView->GetImpEditEngine();
+ SpellInfo* pSpellInfo = pImpEE->GetSpellInfo();
+ bool bMore = false;
+ if ( pSpellInfo->bMultipleDoc )
+ {
+ bMore = pEE->SpellNextDocument();
+ if ( bMore )
+ {
+ // The text has been entered into the engine, when backwards then
+ // it must be behind the selection.
+ pEditView->GetImpEditView()->SetEditSelection(
+ pEE->GetEditDoc().GetStartPaM() );
+ }
+ }
+ return bMore;
+}
+
+void EditSpellWrapper::ReplaceAll( const OUString &rNewText )
+{
+ // Is called when the word is in ReplaceList of the spell checker
+ pEditView->InsertText( rNewText );
+ CheckSpellTo();
+}
+
+void EditSpellWrapper::CheckSpellTo()
+{
+ ImpEditEngine* pImpEE = pEditView->GetImpEditEngine();
+ SpellInfo* pSpellInfo = pImpEE->GetSpellInfo();
+ EditPaM aPaM( pEditView->GetImpEditView()->GetEditSelection().Max() );
+ EPaM aEPaM = pImpEE->CreateEPaM( aPaM );
+ if ( aEPaM.nPara == pSpellInfo->aSpellTo.nPara )
+ {
+ // Check if SpellToEnd still has a valid Index, if replace has been
+ // performed in the paragraph.
+ if ( pSpellInfo->aSpellTo.nIndex > aPaM.GetNode()->Len() )
+ pSpellInfo->aSpellTo.nIndex = aPaM.GetNode()->Len();
+ }
+}
+
+WrongList::WrongList() : mnInvalidStart(0), mnInvalidEnd(Valid) {}
+
+void WrongList::SetRanges( std::vector<editeng::MisspellRange>&&rRanges )
+{
+ maRanges = std::move(rRanges);
+}
+
+bool WrongList::IsValid() const
+{
+ return mnInvalidStart == Valid;
+}
+
+void WrongList::SetValid()
+{
+ mnInvalidStart = Valid;
+ mnInvalidEnd = 0;
+}
+
+void WrongList::SetInvalidRange( size_t nStart, size_t nEnd )
+{
+ if (mnInvalidStart == Valid || nStart < mnInvalidStart)
+ mnInvalidStart = nStart;
+
+ if (mnInvalidEnd < nEnd)
+ mnInvalidEnd = nEnd;
+}
+
+void WrongList::ResetInvalidRange( size_t nStart, size_t nEnd )
+{
+ mnInvalidStart = nStart;
+ mnInvalidEnd = nEnd;
+}
+
+void WrongList::TextInserted( size_t nPos, size_t nLength, bool bPosIsSep )
+{
+ if (IsValid())
+ {
+ mnInvalidStart = nPos;
+ mnInvalidEnd = nPos + nLength;
+ }
+ else
+ {
+ if ( mnInvalidStart > nPos )
+ mnInvalidStart = nPos;
+ if ( mnInvalidEnd >= nPos )
+ mnInvalidEnd = mnInvalidEnd + nLength;
+ else
+ mnInvalidEnd = nPos + nLength;
+ }
+
+ for (size_t i = 0, n = maRanges.size(); i < n; ++i)
+ {
+ editeng::MisspellRange& rWrong = maRanges[i];
+ bool bRefIsValid = true;
+ if (rWrong.mnEnd >= nPos)
+ {
+ // Move all Wrongs after the insert position...
+ if (rWrong.mnStart > nPos)
+ {
+ rWrong.mnStart += nLength;
+ rWrong.mnEnd += nLength;
+ }
+ // 1: Starts before and goes until nPos...
+ else if (rWrong.mnEnd == nPos)
+ {
+ // Should be halted at a blank!
+ if ( !bPosIsSep )
+ rWrong.mnEnd += nLength;
+ }
+ // 2: Starts before and goes until after nPos...
+ else if ((rWrong.mnStart < nPos) && (rWrong.mnEnd > nPos))
+ {
+ rWrong.mnEnd += nLength;
+ // When a separator remove and re-examine the Wrong
+ if ( bPosIsSep )
+ {
+ // Split Wrong...
+ editeng::MisspellRange aNewWrong(rWrong.mnStart, nPos);
+ rWrong.mnStart = nPos + 1;
+ maRanges.insert(maRanges.begin() + i, aNewWrong);
+ // Reference no longer valid after Insert, the other
+ // was inserted in front of this position
+ bRefIsValid = false;
+ ++i; // Not this again...
+ }
+ }
+ // 3: Attribute starts at position ..
+ else if (rWrong.mnStart == nPos)
+ {
+ rWrong.mnEnd += nLength;
+ if ( bPosIsSep )
+ ++(rWrong.mnStart);
+ }
+ }
+ SAL_WARN_IF(bRefIsValid && rWrong.mnStart >= rWrong.mnEnd, "editeng",
+ "TextInserted, editeng::MisspellRange: Start >= End?!");
+ }
+
+ SAL_WARN_IF(DbgIsBuggy(), "editeng", "InsertWrong: WrongList broken!");
+}
+
+void WrongList::TextDeleted( size_t nPos, size_t nLength )
+{
+ size_t nEndPos = nPos + nLength;
+ if (IsValid())
+ {
+ const size_t nNewInvalidStart = nPos ? nPos - 1 : 0;
+ mnInvalidStart = nNewInvalidStart;
+ mnInvalidEnd = nNewInvalidStart + 1;
+ }
+ else
+ {
+ if ( mnInvalidStart > nPos )
+ mnInvalidStart = nPos;
+ if ( mnInvalidEnd > nPos )
+ {
+ if (mnInvalidEnd > nEndPos)
+ mnInvalidEnd = mnInvalidEnd - nLength;
+ else
+ mnInvalidEnd = nPos+1;
+ }
+ }
+
+ for (WrongList::iterator i = maRanges.begin(); i != maRanges.end(); )
+ {
+ bool bDelWrong = false;
+ if (i->mnEnd >= nPos)
+ {
+ // Move all Wrongs after the insert position...
+ if (i->mnStart >= nEndPos)
+ {
+ i->mnStart -= nLength;
+ i->mnEnd -= nLength;
+ }
+ // 1. Delete Internal Wrongs ...
+ else if (i->mnStart >= nPos && i->mnEnd <= nEndPos)
+ {
+ bDelWrong = true;
+ }
+ // 2. Wrong begins before, ends inside or behind it ...
+ else if (i->mnStart <= nPos && i->mnEnd > nPos)
+ {
+ if (i->mnEnd <= nEndPos) // ends inside
+ i->mnEnd = nPos;
+ else
+ i->mnEnd -= nLength; // ends after
+ }
+ // 3. Wrong begins inside, ending after ...
+ else if (i->mnStart >= nPos && i->mnEnd > nEndPos)
+ {
+ i->mnStart = nEndPos - nLength;
+ i->mnEnd -= nLength;
+ }
+ }
+ SAL_WARN_IF(i->mnStart >= i->mnEnd, "editeng",
+ "TextDeleted, editeng::MisspellRange: Start >= End?!");
+ if ( bDelWrong )
+ {
+ i = maRanges.erase(i);
+ }
+ else
+ {
+ ++i;
+ }
+ }
+
+ SAL_WARN_IF(DbgIsBuggy(), "editeng", "TextDeleted: WrongList broken!");
+}
+
+bool WrongList::NextWrong( size_t& rnStart, size_t& rnEnd ) const
+{
+ /*
+ rnStart get the start position, is possibly adjusted wrt. Wrong start
+ rnEnd does not have to be initialized.
+ */
+ for (auto const& range : maRanges)
+ {
+ if (range.mnEnd > rnStart)
+ {
+ rnStart = range.mnStart;
+ rnEnd = range.mnEnd;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool WrongList::HasWrong( size_t nStart, size_t nEnd ) const
+{
+ for (auto const& range : maRanges)
+ {
+ if (range.mnStart == nStart && range.mnEnd == nEnd)
+ return true;
+ else if (range.mnStart >= nStart)
+ break;
+ }
+ return false;
+}
+
+bool WrongList::HasAnyWrong( size_t nStart, size_t nEnd ) const
+{
+ for (auto const& range : maRanges)
+ {
+ if (range.mnEnd >= nStart && range.mnStart < nEnd)
+ return true;
+ else if (range.mnStart >= nEnd)
+ break;
+ }
+ return false;
+}
+
+void WrongList::ClearWrongs( size_t nStart, size_t nEnd,
+ const ContentNode* pNode )
+{
+ for (WrongList::iterator i = maRanges.begin(); i != maRanges.end(); )
+ {
+ if (i->mnEnd > nStart && i->mnStart < nEnd)
+ {
+ if (i->mnEnd > nEnd) // Runs out
+ {
+ i->mnStart = nEnd;
+ // Blanks?
+ while (i->mnStart < o3tl::make_unsigned(pNode->Len()) &&
+ (pNode->GetChar(i->mnStart) == ' ' ||
+ pNode->IsFeature(i->mnStart)))
+ {
+ ++i->mnStart;
+ }
+ ++i;
+ }
+ else
+ {
+ i = maRanges.erase(i);
+ // no increment here
+ }
+ }
+ else
+ {
+ ++i;
+ }
+ }
+
+ SAL_WARN_IF(DbgIsBuggy(), "editeng", "ClearWrongs: WrongList broken!");
+}
+
+void WrongList::InsertWrong( size_t nStart, size_t nEnd )
+{
+ WrongList::iterator nPos = std::find_if(maRanges.begin(), maRanges.end(),
+ [&nStart](const editeng::MisspellRange& rRange) { return rRange.mnStart >= nStart; });
+
+ if (nPos != maRanges.end())
+ {
+ {
+ // It can really only happen that the Wrong starts exactly here
+ // and runs along, but not that there are several ranges ...
+ // Exactly in the range is no one allowed to be, otherwise this
+ // Method can not be called!
+ SAL_WARN_IF((nPos->mnStart != nStart || nPos->mnEnd <= nEnd) && nPos->mnStart <= nEnd, "editeng", "InsertWrong: RangeMismatch!");
+ if (nPos->mnStart == nStart && nPos->mnEnd > nEnd)
+ nPos->mnStart = nEnd + 1;
+ }
+ maRanges.insert(nPos, editeng::MisspellRange(nStart, nEnd));
+ }
+ else
+ maRanges.emplace_back(nStart, nEnd);
+
+ SAL_WARN_IF(DbgIsBuggy(), "editeng", "InsertWrong: WrongList broken!");
+}
+
+void WrongList::MarkWrongsInvalid()
+{
+ if (!maRanges.empty())
+ SetInvalidRange(maRanges.front().mnStart, maRanges.back().mnEnd);
+}
+
+WrongList* WrongList::Clone() const
+{
+ return new WrongList(*this);
+}
+
+// #i102062#
+bool WrongList::operator==(const WrongList& rCompare) const
+{
+ // check direct members
+ if(GetInvalidStart() != rCompare.GetInvalidStart()
+ || GetInvalidEnd() != rCompare.GetInvalidEnd())
+ return false;
+
+ return std::equal(maRanges.begin(), maRanges.end(), rCompare.maRanges.begin(), rCompare.maRanges.end(),
+ [](const editeng::MisspellRange& a, const editeng::MisspellRange& b) {
+ return a.mnStart == b.mnStart && a.mnEnd == b.mnEnd; });
+}
+
+bool WrongList::empty() const
+{
+ return maRanges.empty();
+}
+
+void WrongList::push_back(const editeng::MisspellRange& rRange)
+{
+ maRanges.push_back(rRange);
+}
+
+editeng::MisspellRange& WrongList::back()
+{
+ return maRanges.back();
+}
+
+const editeng::MisspellRange& WrongList::back() const
+{
+ return maRanges.back();
+}
+
+WrongList::iterator WrongList::begin()
+{
+ return maRanges.begin();
+}
+
+WrongList::iterator WrongList::end()
+{
+ return maRanges.end();
+}
+
+WrongList::const_iterator WrongList::begin() const
+{
+ return maRanges.begin();
+}
+
+WrongList::const_iterator WrongList::end() const
+{
+ return maRanges.end();
+}
+
+bool WrongList::DbgIsBuggy() const
+{
+ // Check if the ranges overlap.
+ bool bError = false;
+ for (WrongList::const_iterator i = maRanges.begin(); !bError && (i != maRanges.end()); ++i)
+ {
+ bError = std::any_of(i + 1, maRanges.end(), [&i](const editeng::MisspellRange& rRange) {
+ return i->mnStart <= rRange.mnEnd && rRange.mnStart <= i->mnEnd; });
+ }
+ return bError;
+}
+
+
+EdtAutoCorrDoc::EdtAutoCorrDoc(
+ EditEngine* pE, ContentNode* pN, sal_Int32 nCrsr, sal_Unicode cIns) :
+ mpEditEngine(pE),
+ pCurNode(pN),
+ nCursor(nCrsr),
+ bAllowUndoAction(cIns != 0),
+ bUndoAction(false) {}
+
+EdtAutoCorrDoc::~EdtAutoCorrDoc()
+{
+ if ( bUndoAction )
+ mpEditEngine->UndoActionEnd();
+}
+
+bool EdtAutoCorrDoc::Delete(sal_Int32 nStt, sal_Int32 nEnd)
+{
+ EditSelection aSel( EditPaM( pCurNode, nStt ), EditPaM( pCurNode, nEnd ) );
+ mpEditEngine->DeleteSelection(aSel);
+ SAL_WARN_IF(nCursor < nEnd, "editeng",
+ "Cursor in the heart of the action?!");
+ nCursor -= ( nEnd-nStt );
+ bAllowUndoAction = false;
+ return true;
+}
+
+bool EdtAutoCorrDoc::Insert(sal_Int32 nPos, const OUString& rTxt)
+{
+ EditSelection aSel = EditPaM( pCurNode, nPos );
+ mpEditEngine->InsertText(aSel, rTxt);
+ SAL_WARN_IF(nCursor < nPos, "editeng",
+ "Cursor in the heart of the action?!");
+ nCursor = nCursor + rTxt.getLength();
+
+ if ( bAllowUndoAction && ( rTxt.getLength() == 1 ) )
+ ImplStartUndoAction();
+ bAllowUndoAction = false;
+
+ return true;
+}
+
+bool EdtAutoCorrDoc::Replace(sal_Int32 nPos, const OUString& rTxt)
+{
+ return ReplaceRange( nPos, rTxt.getLength(), rTxt );
+}
+
+bool EdtAutoCorrDoc::ReplaceRange(sal_Int32 nPos, sal_Int32 nSourceLength, const OUString& rTxt)
+{
+ // Actually a Replace introduce => corresponds to UNDO
+ sal_Int32 nEnd = nPos+nSourceLength;
+ if ( nEnd > pCurNode->Len() )
+ nEnd = pCurNode->Len();
+
+ // #i5925# First insert new text behind to be deleted text, for keeping attributes.
+ mpEditEngine->InsertText(EditSelection(EditPaM(pCurNode, nEnd)), rTxt);
+ mpEditEngine->DeleteSelection(
+ EditSelection(EditPaM(pCurNode, nPos), EditPaM(pCurNode, nEnd)));
+
+ if ( nPos == nCursor )
+ nCursor = nCursor + rTxt.getLength();
+
+ if ( bAllowUndoAction && ( rTxt.getLength() == 1 ) )
+ ImplStartUndoAction();
+
+ bAllowUndoAction = false;
+
+ return true;
+}
+
+void EdtAutoCorrDoc::SetAttr(sal_Int32 nStt, sal_Int32 nEnd,
+ sal_uInt16 nSlotId, SfxPoolItem& rItem)
+{
+ SfxItemPool* pPool = &mpEditEngine->GetEditDoc().GetItemPool();
+ while ( pPool->GetSecondaryPool() &&
+ pPool->GetName() != "EditEngineItemPool" )
+ {
+ pPool = pPool->GetSecondaryPool();
+
+ }
+ sal_uInt16 nWhich = pPool->GetWhich( nSlotId );
+ if ( nWhich )
+ {
+ rItem.SetWhich( nWhich );
+
+ SfxItemSet aSet = mpEditEngine->GetEmptyItemSet();
+ aSet.Put( rItem );
+
+ EditSelection aSel( EditPaM( pCurNode, nStt ), EditPaM( pCurNode, nEnd ) );
+ aSel.Max().SetIndex( nEnd ); // ???
+ mpEditEngine->SetAttribs( aSel, aSet, SetAttribsMode::Edge );
+ bAllowUndoAction = false;
+ }
+}
+
+bool EdtAutoCorrDoc::SetINetAttr(sal_Int32 nStt, sal_Int32 nEnd,
+ const OUString& rURL)
+{
+ // Turn the Text into a command field ...
+ EditSelection aSel( EditPaM( pCurNode, nStt ), EditPaM( pCurNode, nEnd ) );
+ OUString aText = mpEditEngine->GetSelected(aSel);
+ aSel = mpEditEngine->DeleteSelection(aSel);
+ SAL_WARN_IF(nCursor < nEnd, "editeng",
+ "Cursor in the heart of the action?!");
+ nCursor -= ( nEnd-nStt );
+ SvxFieldItem aField( SvxURLField( rURL, aText, SvxURLFormat::Repr ),
+ EE_FEATURE_FIELD );
+ mpEditEngine->InsertField(aSel, aField);
+ nCursor++;
+ mpEditEngine->UpdateFieldsOnly();
+ bAllowUndoAction = false;
+ return true;
+}
+
+OUString const* EdtAutoCorrDoc::GetPrevPara(bool const)
+{
+ // Return previous paragraph, so that it can be determined,
+ // whether the current word is at the beginning of a sentence.
+
+ bAllowUndoAction = false; // Not anymore ...
+
+ EditDoc& rNodes = mpEditEngine->GetEditDoc();
+ sal_Int32 nPos = rNodes.GetPos( pCurNode );
+
+ // Special case: Bullet => Paragraph start => simply return NULL...
+ const SfxBoolItem& rBulletState = mpEditEngine->GetParaAttrib( nPos, EE_PARA_BULLETSTATE );
+ bool bBullet = rBulletState.GetValue();
+ if ( !bBullet && (mpEditEngine->GetControlWord() & EEControlBits::OUTLINER) )
+ {
+ // The Outliner has still a Bullet at Level 0.
+ const SfxInt16Item& rLevel = mpEditEngine->GetParaAttrib( nPos, EE_PARA_OUTLLEVEL );
+ if ( rLevel.GetValue() == 0 )
+ bBullet = true;
+ }
+ if ( bBullet )
+ return nullptr;
+
+ for ( sal_Int32 n = nPos; n; )
+ {
+ n--;
+ ContentNode* pNode = rNodes[n];
+ if ( pNode->Len() )
+ return & pNode->GetString();
+ }
+ return nullptr;
+
+}
+
+bool EdtAutoCorrDoc::ChgAutoCorrWord( sal_Int32& rSttPos,
+ sal_Int32 nEndPos, SvxAutoCorrect& rACorrect,
+ OUString* pPara )
+{
+ // Paragraph-start or a blank found, search for the word
+ // shortcut in Auto
+ bAllowUndoAction = false; // Not anymore ...
+
+ OUString aShort( pCurNode->Copy( rSttPos, nEndPos - rSttPos ) );
+ bool bRet = false;
+
+ if( aShort.isEmpty() )
+ return bRet;
+
+ LanguageTag aLanguageTag( mpEditEngine->GetLanguage( EditPaM( pCurNode, rSttPos+1 ) ).nLang );
+ const SvxAutocorrWord* pFnd = rACorrect.SearchWordsInList(
+ pCurNode->GetString(), rSttPos, nEndPos, *this, aLanguageTag);
+ if( pFnd && pFnd->IsTextOnly() )
+ {
+
+ // replace also last colon of keywords surrounded by colons (for example, ":name:")
+ bool replaceLastChar = pFnd->GetShort()[0] == ':' && pFnd->GetShort().endsWith(":");
+
+ // then replace
+ EditSelection aSel( EditPaM( pCurNode, rSttPos ),
+ EditPaM( pCurNode, nEndPos + (replaceLastChar ? 1 : 0) ));
+ aSel = mpEditEngine->DeleteSelection(aSel);
+ SAL_WARN_IF(nCursor < nEndPos, "editeng",
+ "Cursor in the heart of the action?!");
+ nCursor -= ( nEndPos-rSttPos );
+ mpEditEngine->InsertText(aSel, pFnd->GetLong());
+ nCursor = nCursor + pFnd->GetLong().getLength();
+ if( pPara )
+ *pPara = pCurNode->GetString();
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+bool EdtAutoCorrDoc::TransliterateRTLWord( sal_Int32& /*rSttPos*/,
+ sal_Int32 /*nEndPos*/, bool /*bApply*/ )
+{
+ // Paragraph-start or a blank found, search for the word
+ // shortcut in Auto
+ bool bRet = false;
+
+ return bRet;
+}
+
+
+LanguageType EdtAutoCorrDoc::GetLanguage( sal_Int32 nPos ) const
+{
+ return mpEditEngine->GetLanguage( EditPaM( pCurNode, nPos+1 ) ).nLang;
+}
+
+void EdtAutoCorrDoc::ImplStartUndoAction()
+{
+ sal_Int32 nPara = mpEditEngine->GetEditDoc().GetPos( pCurNode );
+ ESelection aSel( nPara, nCursor, nPara, nCursor );
+ mpEditEngine->UndoActionStart( EDITUNDO_INSERT, aSel );
+ bUndoAction = true;
+ bAllowUndoAction = false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/eehtml.cxx b/editeng/source/editeng/eehtml.cxx
new file mode 100644
index 0000000000..b3ed283955
--- /dev/null
+++ b/editeng/source/editeng/eehtml.cxx
@@ -0,0 +1,813 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "eehtml.hxx"
+#include <editeng/adjustitem.hxx>
+#include <editeng/flditem.hxx>
+#include <tools/urlobj.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <svtools/htmltokn.h>
+#include <svtools/htmlkywd.hxx>
+#include <tools/tenccvt.hxx>
+
+#include <editeng/editeng.hxx>
+#include <utility>
+
+#define STYLE_PRE 101
+
+EditHTMLParser::EditHTMLParser( SvStream& rIn, OUString _aBaseURL, SvKeyValueIterator* pHTTPHeaderAttrs )
+ : HTMLParser( rIn, true ),
+ aBaseURL(std::move( _aBaseURL )),
+ mpEditEngine(nullptr),
+ bInPara(false),
+ bWasInPara(false),
+ bFieldsInserted(false),
+ bInTitle(false),
+ nInTable(0),
+ nInCell(0),
+ nDefListLevel(0)
+{
+ DBG_ASSERT( !IsSwitchToUCS2(), "EditHTMLParser::::EditHTMLParser: Switch to UCS2?" );
+
+ // Although the real default encoding is ISO8859-1, we use MS-1252
+ // as default encoding.
+ SetSrcEncoding( GetExtendedCompatibilityTextEncoding( RTL_TEXTENCODING_ISO_8859_1 ) );
+
+ // If the file starts with a BOM, switch to UCS2.
+ SetSwitchToUCS2( true );
+
+ if ( pHTTPHeaderAttrs )
+ SetEncodingByHTTPHeader( pHTTPHeaderAttrs );
+}
+
+EditHTMLParser::~EditHTMLParser()
+{
+}
+
+SvParserState EditHTMLParser::CallParser(EditEngine* pEE, const EditPaM& rPaM)
+{
+ DBG_ASSERT(pEE, "CallParser: ImpEditEngine ?!");
+ mpEditEngine = pEE;
+ SvParserState _eState = SvParserState::NotStarted;
+ if ( mpEditEngine )
+ {
+ // Build in wrap mimic in RTF import?
+ aCurSel = EditSelection( rPaM, rPaM );
+
+ if (mpEditEngine->IsHtmlImportHandlerSet())
+ {
+ HtmlImportInfo aImportInfo(HtmlImportState::Start, this, mpEditEngine->CreateESelection(aCurSel));
+ mpEditEngine->CallHtmlImportHandler(aImportInfo);
+ }
+
+ ImpSetStyleSheet( 0 );
+ _eState = HTMLParser::CallParser();
+
+ if (mpEditEngine->IsHtmlImportHandlerSet())
+ {
+ HtmlImportInfo aImportInfo(HtmlImportState::End, this, mpEditEngine->CreateESelection(aCurSel));
+ mpEditEngine->CallHtmlImportHandler(aImportInfo);
+ }
+
+ if ( bFieldsInserted )
+ mpEditEngine->UpdateFieldsOnly();
+ }
+ return _eState;
+}
+
+void EditHTMLParser::NextToken( HtmlTokenId nToken )
+{
+ switch( nToken )
+ {
+ case HtmlTokenId::META:
+ {
+ const HTMLOptions& aOptions = GetOptions();
+ size_t nArrLen = aOptions.size();
+ bool bEquiv = false;
+ for ( size_t i = 0; i < nArrLen; i++ )
+ {
+ const HTMLOption& aOption = aOptions[i];
+ switch( aOption.GetToken() )
+ {
+ case HtmlOptionId::HTTPEQUIV:
+ {
+ bEquiv = true;
+ }
+ break;
+ case HtmlOptionId::CONTENT:
+ {
+ if ( bEquiv )
+ {
+ rtl_TextEncoding eEnc = GetEncodingByMIME( aOption.GetString() );
+ if ( eEnc != RTL_TEXTENCODING_DONTKNOW )
+ SetSrcEncoding( eEnc );
+ }
+ }
+ break;
+ default: break;
+ }
+ }
+
+ }
+ break;
+ case HtmlTokenId::PLAINTEXT_ON:
+ case HtmlTokenId::PLAINTEXT2_ON:
+ bInPara = true;
+ break;
+ case HtmlTokenId::PLAINTEXT_OFF:
+ case HtmlTokenId::PLAINTEXT2_OFF:
+ bInPara = false;
+ break;
+
+ case HtmlTokenId::LINEBREAK:
+ case HtmlTokenId::NEWPARA:
+ {
+ if ( ( bInPara || nInTable ) &&
+ ( ( nToken == HtmlTokenId::LINEBREAK ) || HasTextInCurrentPara() ) )
+ {
+ ImpInsertParaBreak();
+ }
+ }
+ break;
+ case HtmlTokenId::HORZRULE:
+ {
+ if ( HasTextInCurrentPara() )
+ ImpInsertParaBreak();
+ ImpInsertParaBreak();
+ }
+ break;
+ case HtmlTokenId::NONBREAKSPACE:
+ {
+ if ( bInPara )
+ {
+ ImpInsertText( " " );
+ }
+ }
+ break;
+ case HtmlTokenId::RAWDATA:
+ if (IsReadStyle() && !aToken.isEmpty())
+ {
+ // Each token represents a single line.
+ maStyleSource.append(aToken);
+ maStyleSource.append('\n');
+ }
+ break;
+ case HtmlTokenId::TEXTTOKEN:
+ {
+ // #i110937# for <title> content, call aImportHdl (no SkipGroup), but don't insert the text into the EditEngine
+ if (!bInTitle)
+ {
+ if ( !bInPara )
+ StartPara( false );
+
+ OUString aText = aToken.toString();
+ if ( aText.startsWith(" ") && ThrowAwayBlank() && !IsReadPRE() )
+ aText = aText.copy( 1 );
+
+ if ( moCurAnchor )
+ {
+ moCurAnchor->aText += aText;
+ }
+ else
+ {
+ // Only written until HTML with 319?
+ if ( IsReadPRE() )
+ aText = aText.replaceAll(u"\t", u" ");
+ ImpInsertText( aText );
+ }
+ }
+ }
+ break;
+
+ case HtmlTokenId::CENTER_ON:
+ case HtmlTokenId::CENTER_OFF:
+ {
+ sal_Int32 nNode = mpEditEngine->GetEditDoc().GetPos( aCurSel.Max().GetNode() );
+ SfxItemSet aItems( aCurSel.Max().GetNode()->GetContentAttribs().GetItems() );
+ aItems.ClearItem( EE_PARA_JUST );
+ if ( nToken == HtmlTokenId::CENTER_ON )
+ aItems.Put( SvxAdjustItem( SvxAdjust::Center, EE_PARA_JUST ) );
+ mpEditEngine->SetParaAttribsOnly(nNode, aItems);
+ }
+ break;
+
+ case HtmlTokenId::ANCHOR_ON: AnchorStart();
+ break;
+ case HtmlTokenId::ANCHOR_OFF: AnchorEnd();
+ break;
+
+ case HtmlTokenId::PARABREAK_ON:
+ if( bInPara && HasTextInCurrentPara() )
+ EndPara();
+ StartPara( true );
+ break;
+
+ case HtmlTokenId::PARABREAK_OFF:
+ if( bInPara )
+ EndPara();
+ break;
+
+ case HtmlTokenId::HEAD1_ON:
+ case HtmlTokenId::HEAD2_ON:
+ case HtmlTokenId::HEAD3_ON:
+ case HtmlTokenId::HEAD4_ON:
+ case HtmlTokenId::HEAD5_ON:
+ case HtmlTokenId::HEAD6_ON:
+ {
+ HeadingStart( nToken );
+ }
+ break;
+
+ case HtmlTokenId::HEAD1_OFF:
+ case HtmlTokenId::HEAD2_OFF:
+ case HtmlTokenId::HEAD3_OFF:
+ case HtmlTokenId::HEAD4_OFF:
+ case HtmlTokenId::HEAD5_OFF:
+ case HtmlTokenId::HEAD6_OFF:
+ {
+ HeadingEnd();
+ }
+ break;
+
+ case HtmlTokenId::PREFORMTXT_ON:
+ case HtmlTokenId::XMP_ON:
+ case HtmlTokenId::LISTING_ON:
+ {
+ StartPara( true );
+ ImpSetStyleSheet( STYLE_PRE );
+ }
+ break;
+
+ case HtmlTokenId::DEFLIST_ON:
+ {
+ nDefListLevel++;
+ }
+ break;
+
+ case HtmlTokenId::DEFLIST_OFF:
+ {
+ if( nDefListLevel )
+ nDefListLevel--;
+ }
+ break;
+
+ case HtmlTokenId::TABLE_ON: nInTable++;
+ break;
+ case HtmlTokenId::TABLE_OFF: DBG_ASSERT( nInTable, "Not in Table, but TABLE_OFF?" );
+ nInTable--;
+ break;
+
+ case HtmlTokenId::TABLEHEADER_ON:
+ case HtmlTokenId::TABLEDATA_ON:
+ nInCell++;
+ [[fallthrough]];
+ case HtmlTokenId::BLOCKQUOTE_ON:
+ case HtmlTokenId::BLOCKQUOTE_OFF:
+ case HtmlTokenId::BLOCKQUOTE30_ON:
+ case HtmlTokenId::BLOCKQUOTE30_OFF:
+ case HtmlTokenId::LISTHEADER_ON:
+ case HtmlTokenId::LI_ON:
+ case HtmlTokenId::DD_ON:
+ case HtmlTokenId::DT_ON:
+ case HtmlTokenId::ORDERLIST_ON:
+ case HtmlTokenId::UNORDERLIST_ON:
+ {
+ bool bHasText = HasTextInCurrentPara();
+ if ( bHasText )
+ ImpInsertParaBreak();
+ StartPara( false );
+ }
+ break;
+
+ case HtmlTokenId::TABLEHEADER_OFF:
+ case HtmlTokenId::TABLEDATA_OFF:
+ {
+ if ( nInCell )
+ nInCell--;
+ [[fallthrough]];
+ }
+ case HtmlTokenId::LISTHEADER_OFF:
+ case HtmlTokenId::LI_OFF:
+ case HtmlTokenId::DD_OFF:
+ case HtmlTokenId::DT_OFF:
+ case HtmlTokenId::ORDERLIST_OFF:
+ case HtmlTokenId::UNORDERLIST_OFF: EndPara();
+ break;
+
+ case HtmlTokenId::TABLEROW_ON:
+ case HtmlTokenId::TABLEROW_OFF: // A RETURN only after a CELL, for Calc
+
+ case HtmlTokenId::COL_ON:
+ case HtmlTokenId::COLGROUP_ON:
+ case HtmlTokenId::COLGROUP_OFF: break;
+
+ case HtmlTokenId::FONT_ON:
+ break;
+ case HtmlTokenId::FONT_OFF:
+ break;
+
+ case HtmlTokenId::TITLE_ON:
+ bInTitle = true;
+ break;
+ case HtmlTokenId::TITLE_OFF:
+ bInTitle = false;
+ break;
+
+ // globals
+ case HtmlTokenId::HTML_ON:
+ case HtmlTokenId::HTML_OFF:
+ case HtmlTokenId::STYLE_ON:
+ case HtmlTokenId::STYLE_OFF:
+ case HtmlTokenId::BODY_ON:
+ case HtmlTokenId::BODY_OFF:
+ case HtmlTokenId::HEAD_ON:
+ case HtmlTokenId::HEAD_OFF:
+ case HtmlTokenId::FORM_ON:
+ case HtmlTokenId::FORM_OFF:
+ case HtmlTokenId::THEAD_ON:
+ case HtmlTokenId::THEAD_OFF:
+ case HtmlTokenId::TBODY_ON:
+ case HtmlTokenId::TBODY_OFF:
+ // inline elements, structural markup
+ // HTML 3.0
+ case HtmlTokenId::BANNER_ON:
+ case HtmlTokenId::BANNER_OFF:
+ case HtmlTokenId::DIVISION_ON:
+ case HtmlTokenId::DIVISION_OFF:
+// case HtmlTokenId::LISTHEADER_ON: //! special handling
+// case HtmlTokenId::LISTHEADER_OFF:
+ case HtmlTokenId::NOTE_ON:
+ case HtmlTokenId::NOTE_OFF:
+ // inline elements, logical markup
+ // HTML 2.0
+ case HtmlTokenId::ADDRESS_ON:
+ case HtmlTokenId::ADDRESS_OFF:
+// case HtmlTokenId::BLOCKQUOTE_ON: //! special handling
+// case HtmlTokenId::BLOCKQUOTE_OFF:
+ case HtmlTokenId::CITATION_ON:
+ case HtmlTokenId::CITATION_OFF:
+ case HtmlTokenId::CODE_ON:
+ case HtmlTokenId::CODE_OFF:
+ case HtmlTokenId::DEFINSTANCE_ON:
+ case HtmlTokenId::DEFINSTANCE_OFF:
+ case HtmlTokenId::EMPHASIS_ON:
+ case HtmlTokenId::EMPHASIS_OFF:
+ case HtmlTokenId::KEYBOARD_ON:
+ case HtmlTokenId::KEYBOARD_OFF:
+ case HtmlTokenId::SAMPLE_ON:
+ case HtmlTokenId::SAMPLE_OFF:
+ case HtmlTokenId::STRIKE_ON:
+ case HtmlTokenId::STRIKE_OFF:
+ case HtmlTokenId::STRONG_ON:
+ case HtmlTokenId::STRONG_OFF:
+ case HtmlTokenId::VARIABLE_ON:
+ case HtmlTokenId::VARIABLE_OFF:
+ // HTML 3.0
+ case HtmlTokenId::ABBREVIATION_ON:
+ case HtmlTokenId::ABBREVIATION_OFF:
+ case HtmlTokenId::ACRONYM_ON:
+ case HtmlTokenId::ACRONYM_OFF:
+ case HtmlTokenId::AUTHOR_ON:
+ case HtmlTokenId::AUTHOR_OFF:
+// case HtmlTokenId::BLOCKQUOTE30_ON: //! special handling
+// case HtmlTokenId::BLOCKQUOTE30_OFF:
+ case HtmlTokenId::DELETEDTEXT_ON:
+ case HtmlTokenId::DELETEDTEXT_OFF:
+ case HtmlTokenId::INSERTEDTEXT_ON:
+ case HtmlTokenId::INSERTEDTEXT_OFF:
+ case HtmlTokenId::LANGUAGE_ON:
+ case HtmlTokenId::LANGUAGE_OFF:
+ case HtmlTokenId::PERSON_ON:
+ case HtmlTokenId::PERSON_OFF:
+ case HtmlTokenId::SHORTQUOTE_ON:
+ case HtmlTokenId::SHORTQUOTE_OFF:
+ case HtmlTokenId::SUBSCRIPT_ON:
+ case HtmlTokenId::SUBSCRIPT_OFF:
+ case HtmlTokenId::SUPERSCRIPT_ON:
+ case HtmlTokenId::SUPERSCRIPT_OFF:
+ // inline elements, visual markup
+ // HTML 2.0
+ case HtmlTokenId::BOLD_ON:
+ case HtmlTokenId::BOLD_OFF:
+ case HtmlTokenId::ITALIC_ON:
+ case HtmlTokenId::ITALIC_OFF:
+ case HtmlTokenId::TELETYPE_ON:
+ case HtmlTokenId::TELETYPE_OFF:
+ case HtmlTokenId::UNDERLINE_ON:
+ case HtmlTokenId::UNDERLINE_OFF:
+ // HTML 3.0
+ case HtmlTokenId::BIGPRINT_ON:
+ case HtmlTokenId::BIGPRINT_OFF:
+ case HtmlTokenId::STRIKETHROUGH_ON:
+ case HtmlTokenId::STRIKETHROUGH_OFF:
+ case HtmlTokenId::SMALLPRINT_ON:
+ case HtmlTokenId::SMALLPRINT_OFF:
+ // figures
+ case HtmlTokenId::FIGURE_ON:
+ case HtmlTokenId::FIGURE_OFF:
+ case HtmlTokenId::CAPTION_ON:
+ case HtmlTokenId::CAPTION_OFF:
+ case HtmlTokenId::CREDIT_ON:
+ case HtmlTokenId::CREDIT_OFF:
+ // misc
+ case HtmlTokenId::DIRLIST_ON:
+ case HtmlTokenId::DIRLIST_OFF:
+ case HtmlTokenId::FOOTNOTE_ON: //! they land so in the text
+ case HtmlTokenId::FOOTNOTE_OFF:
+ case HtmlTokenId::MENULIST_ON:
+ case HtmlTokenId::MENULIST_OFF:
+// case HtmlTokenId::PLAINTEXT_ON: //! special handling
+// case HtmlTokenId::PLAINTEXT_OFF:
+// case HtmlTokenId::PREFORMTXT_ON: //! special handling
+// case HtmlTokenId::PREFORMTXT_OFF:
+ case HtmlTokenId::SPAN_ON:
+ case HtmlTokenId::SPAN_OFF:
+ // obsolete
+// case HtmlTokenId::XMP_ON: //! special handling
+// case HtmlTokenId::XMP_OFF:
+// case HtmlTokenId::LISTING_ON: //! special handling
+// case HtmlTokenId::LISTING_OFF:
+ // Netscape
+ case HtmlTokenId::BLINK_ON:
+ case HtmlTokenId::BLINK_OFF:
+ case HtmlTokenId::NOBR_ON:
+ case HtmlTokenId::NOBR_OFF:
+ case HtmlTokenId::NOEMBED_ON:
+ case HtmlTokenId::NOEMBED_OFF:
+ case HtmlTokenId::NOFRAMES_ON:
+ case HtmlTokenId::NOFRAMES_OFF:
+ // Internet Explorer
+ case HtmlTokenId::MARQUEE_ON:
+ case HtmlTokenId::MARQUEE_OFF:
+// case HtmlTokenId::PLAINTEXT2_ON: //! special handling
+// case HtmlTokenId::PLAINTEXT2_OFF:
+ break;
+
+ default:
+ {
+ if ( nToken >= HtmlTokenId::ONOFF_START )
+ {
+ if ( ( nToken == HtmlTokenId::UNKNOWNCONTROL_ON ) || ( nToken == HtmlTokenId::UNKNOWNCONTROL_OFF ) )
+ {
+ ;
+ }
+ else if ( !isOffToken(nToken) )
+ {
+ DBG_ASSERT( !isOffToken( nToken ), "No Start-Token ?!" );
+ SkipGroup( static_cast<HtmlTokenId>(static_cast<int>(nToken) + 1) );
+ }
+ }
+ }
+ } // SWITCH
+
+ if (mpEditEngine->IsHtmlImportHandlerSet())
+ {
+ HtmlImportInfo aImportInfo(HtmlImportState::NextToken, this, mpEditEngine->CreateESelection(aCurSel));
+ aImportInfo.nToken = nToken;
+ if ( nToken == HtmlTokenId::TEXTTOKEN )
+ aImportInfo.aText = aToken;
+ else if (nToken == HtmlTokenId::STYLE_OFF)
+ aImportInfo.aText = maStyleSource.makeStringAndClear();
+ mpEditEngine->CallHtmlImportHandler(aImportInfo);
+ }
+
+}
+
+void EditHTMLParser::ImpInsertParaBreak()
+{
+ if (mpEditEngine->IsHtmlImportHandlerSet())
+ {
+ HtmlImportInfo aImportInfo(HtmlImportState::InsertPara, this, mpEditEngine->CreateESelection(aCurSel));
+ mpEditEngine->CallHtmlImportHandler(aImportInfo);
+ }
+ aCurSel = mpEditEngine->InsertParaBreak(aCurSel);
+}
+
+void EditHTMLParser::ImpSetAttribs( const SfxItemSet& rItems )
+{
+ // pSel, when character attributes, otherwise paragraph attributes for
+ // the current paragraph.
+ DBG_ASSERT( aCurSel.Min().GetNode() == aCurSel.Max().GetNode(), "ImpInsertAttribs: Selection?" );
+
+ EditPaM aStartPaM( aCurSel.Min() );
+ EditPaM aEndPaM( aCurSel.Max() );
+
+ aStartPaM.SetIndex( 0 );
+ aEndPaM.SetIndex( aEndPaM.GetNode()->Len() );
+
+ if (mpEditEngine->IsHtmlImportHandlerSet())
+ {
+ EditSelection aSel( aStartPaM, aEndPaM );
+ HtmlImportInfo aImportInfo(HtmlImportState::SetAttr, this, mpEditEngine->CreateESelection(aSel));
+ mpEditEngine->CallHtmlImportHandler(aImportInfo);
+ }
+
+ ContentNode* pSN = aStartPaM.GetNode();
+ sal_Int32 nStartNode = mpEditEngine->GetEditDoc().GetPos( pSN );
+
+ // If an attribute goes from 0 to current Paragraph length,
+ // then it should be a paragraph attribute!
+
+ // Note: Selection can reach over several Paragraphs.
+ // All complete paragraphs are paragraph attributes ...
+
+ // not really HTML:
+#ifdef DBG_UTIL
+ ContentNode* pEN = aEndPaM.GetNode();
+ sal_Int32 nEndNode = mpEditEngine->GetEditDoc().GetPos( pEN );
+ DBG_ASSERT( nStartNode == nEndNode, "ImpSetAttribs: Several paragraphs?" );
+#endif
+
+ if ( ( aStartPaM.GetIndex() == 0 ) && ( aEndPaM.GetIndex() == aEndPaM.GetNode()->Len() ) )
+ {
+ // Has to be merged:
+ SfxItemSet aItems = mpEditEngine->GetBaseParaAttribs(nStartNode);
+ aItems.Put( rItems );
+ mpEditEngine->SetParaAttribsOnly(nStartNode, aItems);
+ }
+ else
+ mpEditEngine->SetAttribs( EditSelection( aStartPaM, aEndPaM ), rItems );
+}
+
+void EditHTMLParser::ImpSetStyleSheet( sal_uInt16 nHLevel )
+{
+ /*
+ nHLevel: 0: Turn off
+ 1-6: Heading
+ STYLE_PRE: Preformatted
+ */
+ // Create hard attributes ...
+ // Enough for Calc, would have to be clarified with StyleSheets
+ // that they should also be in the app so that when they are feed
+ // in a different engine still are here ...
+ sal_Int32 nNode = mpEditEngine->GetEditDoc().GetPos( aCurSel.Max().GetNode() );
+
+ SfxItemSet aItems( aCurSel.Max().GetNode()->GetContentAttribs().GetItems() );
+
+ aItems.ClearItem( EE_PARA_ULSPACE );
+
+ aItems.ClearItem( EE_CHAR_FONTHEIGHT );
+ aItems.ClearItem( EE_CHAR_FONTINFO );
+ aItems.ClearItem( EE_CHAR_WEIGHT );
+
+ aItems.ClearItem( EE_CHAR_FONTHEIGHT_CJK );
+ aItems.ClearItem( EE_CHAR_FONTINFO_CJK );
+ aItems.ClearItem( EE_CHAR_WEIGHT_CJK );
+
+ aItems.ClearItem( EE_CHAR_FONTHEIGHT_CTL );
+ aItems.ClearItem( EE_CHAR_FONTINFO_CTL );
+ aItems.ClearItem( EE_CHAR_WEIGHT_CTL );
+
+ // Bold in the first 3 Headings
+ if ( ( nHLevel >= 1 ) && ( nHLevel <= 3 ) )
+ {
+ SvxWeightItem aWeightItem( WEIGHT_BOLD, EE_CHAR_WEIGHT );
+ aItems.Put( aWeightItem );
+
+ SvxWeightItem aWeightItemCJK( WEIGHT_BOLD, EE_CHAR_WEIGHT_CJK );
+ aItems.Put( aWeightItemCJK );
+
+ SvxWeightItem aWeightItemCTL( WEIGHT_BOLD, EE_CHAR_WEIGHT_CTL );
+ aItems.Put( aWeightItemCTL );
+ }
+
+ // Font height and margins, when LogicToLogic is possible:
+ MapUnit eUnit = mpEditEngine->GetRefMapMode().GetMapUnit();
+ if ( ( eUnit != MapUnit::MapPixel ) && ( eUnit != MapUnit::MapSysFont ) &&
+ ( eUnit != MapUnit::MapAppFont ) && ( eUnit != MapUnit::MapRelative ) )
+ {
+ tools::Long nPoints = 10;
+ if ( nHLevel == 1 )
+ nPoints = 22;
+ else if ( nHLevel == 2 )
+ nPoints = 16;
+ else if ( nHLevel == 3 )
+ nPoints = 12;
+ else if ( nHLevel == 4 )
+ nPoints = 11;
+
+ nPoints = OutputDevice::LogicToLogic( nPoints, MapUnit::MapPoint, eUnit );
+
+ SvxFontHeightItem aHeightItem( nPoints, 100, EE_CHAR_FONTHEIGHT );
+ aItems.Put( aHeightItem );
+
+ SvxFontHeightItem aHeightItemCJK( nPoints, 100, EE_CHAR_FONTHEIGHT_CJK );
+ aItems.Put( aHeightItemCJK );
+
+ SvxFontHeightItem aHeightItemCTL( nPoints, 100, EE_CHAR_FONTHEIGHT_CTL );
+ aItems.Put( aHeightItemCTL );
+
+ // Paragraph margins, when Heading:
+ if (nHLevel <= 6)
+ {
+ SvxULSpaceItem aULSpaceItem( EE_PARA_ULSPACE );
+ aULSpaceItem.SetUpper( static_cast<sal_uInt16>(OutputDevice::LogicToLogic( 42, MapUnit::Map10thMM, eUnit )) );
+ aULSpaceItem.SetLower( static_cast<sal_uInt16>(OutputDevice::LogicToLogic( 35, MapUnit::Map10thMM, eUnit )) );
+ aItems.Put( aULSpaceItem );
+ }
+ }
+
+ // Choose a proportional Font for Pre
+ if ( nHLevel == STYLE_PRE )
+ {
+ vcl::Font aFont = OutputDevice::GetDefaultFont( DefaultFontType::FIXED, LANGUAGE_SYSTEM, GetDefaultFontFlags::NONE );
+ SvxFontItem aFontItem( aFont.GetFamilyType(), aFont.GetFamilyName(), OUString(), aFont.GetPitch(), aFont.GetCharSet(), EE_CHAR_FONTINFO );
+ aItems.Put( aFontItem );
+
+ SvxFontItem aFontItemCJK( aFont.GetFamilyType(), aFont.GetFamilyName(), OUString(), aFont.GetPitch(), aFont.GetCharSet(), EE_CHAR_FONTINFO_CJK );
+ aItems.Put( aFontItemCJK );
+
+ SvxFontItem aFontItemCTL( aFont.GetFamilyType(), aFont.GetFamilyName(), OUString(), aFont.GetPitch(), aFont.GetCharSet(), EE_CHAR_FONTINFO_CTL );
+ aItems.Put( aFontItemCTL );
+ }
+
+ mpEditEngine->SetParaAttribsOnly(nNode, aItems);
+}
+
+void EditHTMLParser::ImpInsertText( const OUString& rText )
+{
+ if (mpEditEngine->IsHtmlImportHandlerSet())
+ {
+ HtmlImportInfo aImportInfo(HtmlImportState::InsertText, this, mpEditEngine->CreateESelection(aCurSel));
+ aImportInfo.aText = rText;
+ mpEditEngine->CallHtmlImportHandler(aImportInfo);
+ }
+
+ aCurSel = mpEditEngine->InsertText(aCurSel, rText);
+}
+
+void EditHTMLParser::SkipGroup( HtmlTokenId nEndToken )
+{
+ // groups in cells are closed upon leaving the cell, because those
+ // ******* web authors don't know their job
+ // for example: <td><form></td> lacks a closing </form>
+ sal_uInt8 nCellLevel = nInCell;
+ HtmlTokenId nToken;
+ while( nCellLevel <= nInCell )
+ {
+ nToken = GetNextToken();
+ if (nToken == nEndToken || nToken == HtmlTokenId::NONE)
+ break;
+ switch ( nToken )
+ {
+ case HtmlTokenId::TABLEHEADER_ON:
+ case HtmlTokenId::TABLEDATA_ON:
+ nInCell++;
+ break;
+ case HtmlTokenId::TABLEHEADER_OFF:
+ case HtmlTokenId::TABLEDATA_OFF:
+ if ( nInCell )
+ nInCell--;
+ break;
+ default: break;
+ }
+ }
+}
+
+void EditHTMLParser::StartPara( bool bReal )
+{
+ if ( bReal )
+ {
+ const HTMLOptions& aOptions = GetOptions();
+ SvxAdjust eAdjust = SvxAdjust::Left;
+ for (const auto & aOption : aOptions)
+ {
+ if( aOption.GetToken() == HtmlOptionId::ALIGN )
+ {
+ OUString const& rTmp(aOption.GetString());
+ if (rTmp.equalsIgnoreAsciiCase(OOO_STRING_SVTOOLS_HTML_AL_right))
+ eAdjust = SvxAdjust::Right;
+ else if (rTmp.equalsIgnoreAsciiCase(OOO_STRING_SVTOOLS_HTML_AL_middle))
+ eAdjust = SvxAdjust::Center;
+ else if (rTmp.equalsIgnoreAsciiCase(OOO_STRING_SVTOOLS_HTML_AL_center))
+ eAdjust = SvxAdjust::Center;
+ else
+ eAdjust = SvxAdjust::Left;
+ }
+ }
+ SfxItemSet aItemSet = mpEditEngine->GetEmptyItemSet();
+ aItemSet.Put( SvxAdjustItem( eAdjust, EE_PARA_JUST ) );
+ ImpSetAttribs( aItemSet );
+ }
+ bInPara = true;
+}
+
+void EditHTMLParser::EndPara()
+{
+ if ( bInPara )
+ {
+ bool bHasText = HasTextInCurrentPara();
+ if ( bHasText )
+ ImpInsertParaBreak();
+ }
+ bInPara = false;
+}
+
+bool EditHTMLParser::ThrowAwayBlank()
+{
+ // A blank must be thrown away if the new text begins with a Blank and
+ // if the current paragraph is empty or ends with a Blank...
+ ContentNode* pNode = aCurSel.Max().GetNode();
+ return !(pNode->Len() && ( pNode->GetChar( pNode->Len()-1 ) != ' ' ));
+}
+
+bool EditHTMLParser::HasTextInCurrentPara()
+{
+ return aCurSel.Max().GetNode()->Len() != 0;
+}
+
+void EditHTMLParser::AnchorStart()
+{
+ // ignore anchor in anchor
+ if ( moCurAnchor )
+ return;
+
+ const HTMLOptions& aOptions = GetOptions();
+ OUString aRef;
+
+ for (const auto & aOption : aOptions)
+ {
+ if( aOption.GetToken() == HtmlOptionId::HREF)
+ aRef = aOption.GetString();
+ }
+
+ if ( aRef.isEmpty() )
+ return;
+
+ OUString aURL = aRef;
+ if ( !aURL.isEmpty() && ( aURL[ 0 ] != '#' ) )
+ {
+ INetURLObject aTargetURL;
+ INetURLObject aRootURL( aBaseURL );
+ aRootURL.GetNewAbsURL( aRef, &aTargetURL );
+ aURL = aTargetURL.GetMainURL( INetURLObject::DecodeMechanism::ToIUri );
+ }
+ moCurAnchor.emplace();
+ moCurAnchor->aHRef = aURL;
+}
+
+void EditHTMLParser::AnchorEnd()
+{
+ if ( !moCurAnchor )
+ return;
+
+ // Insert as URL-Field...
+ SvxFieldItem aFld( SvxURLField( moCurAnchor->aHRef, moCurAnchor->aText, SvxURLFormat::Repr ), EE_FEATURE_FIELD );
+ aCurSel = mpEditEngine->InsertField(aCurSel, aFld);
+ bFieldsInserted = true;
+ moCurAnchor.reset();
+
+ if (mpEditEngine->IsHtmlImportHandlerSet())
+ {
+ HtmlImportInfo aImportInfo(HtmlImportState::InsertField, this, mpEditEngine->CreateESelection(aCurSel));
+ mpEditEngine->CallHtmlImportHandler(aImportInfo);
+ }
+}
+
+void EditHTMLParser::HeadingStart( HtmlTokenId nToken )
+{
+ bWasInPara = bInPara;
+ StartPara( false );
+
+ if ( bWasInPara && HasTextInCurrentPara() )
+ ImpInsertParaBreak();
+
+ sal_uInt16 nId = sal::static_int_cast< sal_uInt16 >(
+ 1 + ( ( static_cast<int>(nToken) - int(HtmlTokenId::HEAD1_ON) ) / 2 ) );
+ DBG_ASSERT( (nId >= 1) && (nId <= 9), "HeadingStart: ID can not be correct!" );
+ ImpSetStyleSheet( nId );
+}
+
+void EditHTMLParser::HeadingEnd()
+{
+ EndPara();
+ ImpSetStyleSheet( 0 );
+
+ if ( bWasInPara )
+ {
+ bInPara = true;
+ bWasInPara = false;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/eehtml.hxx b/editeng/source/editeng/eehtml.hxx
new file mode 100644
index 0000000000..fddd567ac6
--- /dev/null
+++ b/editeng/source/editeng/eehtml.hxx
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <optional>
+#include <editdoc.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <svtools/parhtml.hxx>
+
+class EditEngine;
+
+struct AnchorInfo
+{
+ OUString aHRef;
+ OUString aText;
+};
+
+class EditHTMLParser : public HTMLParser
+{
+ using HTMLParser::CallParser;
+private:
+ OUStringBuffer maStyleSource;
+ EditSelection aCurSel;
+ OUString aBaseURL;
+ EditEngine* mpEditEngine;
+ std::optional<AnchorInfo> moCurAnchor;
+
+ bool bInPara:1;
+ bool bWasInPara:1; // Remember bInPara before HeadingStart, because afterwards it will be gone.
+ bool bFieldsInserted:1;
+ bool bInTitle:1;
+
+ sal_uInt8 nInTable;
+ sal_uInt8 nInCell;
+ sal_uInt8 nDefListLevel;
+
+ void StartPara( bool bReal );
+ void EndPara();
+ void AnchorStart();
+ void AnchorEnd();
+ void HeadingStart( HtmlTokenId nToken );
+ void HeadingEnd();
+ void SkipGroup( HtmlTokenId nEndToken );
+ bool ThrowAwayBlank();
+ bool HasTextInCurrentPara();
+
+ void ImpInsertParaBreak();
+ void ImpInsertText( const OUString& rText );
+ void ImpSetAttribs( const SfxItemSet& rItems );
+ void ImpSetStyleSheet( sal_uInt16 nHeadingLevel );
+
+protected:
+ virtual void NextToken( HtmlTokenId nToken ) override;
+
+public:
+ EditHTMLParser(SvStream& rIn, OUString aBaseURL, SvKeyValueIterator* pHTTPHeaderAttrs);
+ virtual ~EditHTMLParser() override;
+
+ SvParserState CallParser(EditEngine* pEE, const EditPaM& rPaM);
+
+ const EditSelection& GetCurSelection() const { return aCurSel; }
+};
+
+typedef tools::SvRef<EditHTMLParser> EditHTMLParserRef;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/eeobj.cxx b/editeng/source/editeng/eeobj.cxx
new file mode 100644
index 0000000000..bfd81c40c3
--- /dev/null
+++ b/editeng/source/editeng/eeobj.cxx
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>
+#include "eeobj.hxx"
+#include <sot/exchange.hxx>
+#include <sot/formats.hxx>
+
+using namespace ::com::sun::star;
+
+
+EditDataObject::EditDataObject()
+{
+}
+
+EditDataObject::~EditDataObject()
+{
+}
+
+// datatransfer::XTransferable
+uno::Any EditDataObject::getTransferData( const datatransfer::DataFlavor& rFlavor )
+{
+ uno::Any aAny;
+
+ SotClipboardFormatId nT = SotExchange::GetFormat( rFlavor );
+ if ( nT == SotClipboardFormatId::STRING )
+ {
+ aAny <<= GetString();
+ }
+ else if ( ( nT == SotClipboardFormatId::RTF ) || ( nT == SotClipboardFormatId::RICHTEXT ) || ( nT == SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT ) )
+ {
+ // No RTF on demand any more:
+ // 1) Was not working, because I had to flush() the clipboard immediately anyway
+ // 2) Don't have the old pool defaults and the StyleSheetPool here.
+
+ SvMemoryStream* pStream = (nT == SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT ) ? &GetODFStream() : &GetRTFStream();
+ sal_Int32 nLen = pStream->TellEnd();
+ if (nLen < 0) { abort(); }
+
+ aAny <<= uno::Sequence< sal_Int8 >( static_cast< const sal_Int8* >(pStream->GetData()), pStream->TellEnd() );
+ }
+ else
+ {
+ datatransfer::UnsupportedFlavorException aException;
+ throw aException;
+ }
+
+ return aAny;
+}
+
+uno::Sequence< datatransfer::DataFlavor > EditDataObject::getTransferDataFlavors( )
+{
+ uno::Sequence< datatransfer::DataFlavor > aDataFlavors(4);
+ SotExchange::GetFormatDataFlavor( SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT, aDataFlavors.getArray()[0] );
+ SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aDataFlavors.getArray()[1] );
+ SotExchange::GetFormatDataFlavor( SotClipboardFormatId::RTF, aDataFlavors.getArray()[2] );
+ SotExchange::GetFormatDataFlavor( SotClipboardFormatId::RICHTEXT, aDataFlavors.getArray()[3] );
+
+ return aDataFlavors;
+}
+
+sal_Bool EditDataObject::isDataFlavorSupported( const datatransfer::DataFlavor& rFlavor )
+{
+ bool bSupported = false;
+
+ SotClipboardFormatId nT = SotExchange::GetFormat( rFlavor );
+ if ( ( nT == SotClipboardFormatId::STRING ) || ( nT == SotClipboardFormatId::RTF ) || ( nT == SotClipboardFormatId::RICHTEXT )
+ || ( nT == SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT ) )
+ bSupported = true;
+
+ return bSupported;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/eeobj.hxx b/editeng/source/editeng/eeobj.hxx
new file mode 100644
index 0000000000..de06f1b703
--- /dev/null
+++ b/editeng/source/editeng/eeobj.hxx
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/datatransfer/XTransferable.hpp>
+
+#include <tools/stream.hxx>
+
+class EditDataObject : public ::cppu::WeakImplHelper<css::datatransfer::XTransferable>
+{
+private:
+ SvMemoryStream maRTFData;
+ SvMemoryStream maODFData;
+ OUString maText;
+ OUString maOfficeBookmark;
+
+public:
+ EditDataObject();
+ virtual ~EditDataObject() override;
+
+ SvMemoryStream& GetRTFStream() { return maRTFData; }
+ SvMemoryStream& GetODFStream() { return maODFData; }
+ OUString& GetString() { return maText; }
+ OUString& GetURL() { return maOfficeBookmark; }
+
+ // css::datatransfer::XTransferable
+ css::uno::Any SAL_CALL getTransferData( const css::datatransfer::DataFlavor& aFlavor ) override;
+ css::uno::Sequence< css::datatransfer::DataFlavor > SAL_CALL getTransferDataFlavors( ) override;
+ sal_Bool SAL_CALL isDataFlavorSupported( const css::datatransfer::DataFlavor& aFlavor ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/eerdll.cxx b/editeng/source/editeng/eerdll.cxx
new file mode 100644
index 0000000000..9e3e8c4cf8
--- /dev/null
+++ b/editeng/source/editeng/eerdll.cxx
@@ -0,0 +1,228 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <unotools/resmgr.hxx>
+#include <com/sun/star/linguistic2/LanguageGuessing.hpp>
+
+#include <comphelper/processfactory.hxx>
+
+#include <editeng/eeitem.hxx>
+#include <editeng/eerdll.hxx>
+#include <eerdll2.hxx>
+#include <editeng/lspcitem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/tstpitem.hxx>
+#include <editeng/bulletitem.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/scriptspaceitem.hxx>
+#include <editeng/hngpnctitem.hxx>
+#include <editeng/forbiddenruleitem.hxx>
+#include <svl/grabbagitem.hxx>
+#include <svl/voiditem.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/virdev.hxx>
+
+#include <editeng/autokernitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/kernitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <editeng/numitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/cmapitem.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/xmlcnitm.hxx>
+#include <editeng/forbiddencharacterstable.hxx>
+#include <editeng/justifyitem.hxx>
+#include <tools/mapunit.hxx>
+#include <vcl/lazydelete.hxx>
+
+using namespace ::com::sun::star;
+
+EditDLL& EditDLL::Get()
+{
+ /**
+ Prevent use-after-free errors during application shutdown.
+ Previously this data was function-static, but then data in i18npool would
+ be torn down before the destructor here ran, causing a crash.
+ */
+ static vcl::DeleteOnDeinit< EditDLL > gaEditDll;
+ return *gaEditDll.get();
+}
+
+DefItems::DefItems()
+ : mvDefItems(EDITITEMCOUNT)
+{
+ std::vector<SfxPoolItem*>& rDefItems = mvDefItems;
+
+ // Paragraph attributes:
+ SvxNumRule aDefaultNumRule( SvxNumRuleFlags::NONE, 0, false );
+
+ rDefItems[0] = new SvxFrameDirectionItem( SvxFrameDirection::Horizontal_LR_TB, EE_PARA_WRITINGDIR );
+ rDefItems[1] = new SvXMLAttrContainerItem( EE_PARA_XMLATTRIBS );
+ rDefItems[2] = new SvxHangingPunctuationItem(false, EE_PARA_HANGINGPUNCTUATION);
+ rDefItems[3] = new SvxForbiddenRuleItem(true, EE_PARA_FORBIDDENRULES);
+ rDefItems[4] = new SvxScriptSpaceItem( true, EE_PARA_ASIANCJKSPACING );
+ rDefItems[5] = new SvxNumBulletItem( aDefaultNumRule, EE_PARA_NUMBULLET );
+ rDefItems[6] = new SfxBoolItem( EE_PARA_HYPHENATE, false );
+ rDefItems[7] = new SfxBoolItem( EE_PARA_HYPHENATE_NO_CAPS, false );
+ rDefItems[8] = new SfxBoolItem( EE_PARA_HYPHENATE_NO_LAST_WORD, false );
+ rDefItems[9] = new SfxBoolItem( EE_PARA_BULLETSTATE, true );
+ rDefItems[10] = new SvxLRSpaceItem( EE_PARA_OUTLLRSPACE );
+ rDefItems[11] = new SfxInt16Item( EE_PARA_OUTLLEVEL, -1 );
+ rDefItems[12] = new SvxBulletItem( EE_PARA_BULLET );
+ rDefItems[13] = new SvxLRSpaceItem( EE_PARA_LRSPACE );
+ rDefItems[14] = new SvxULSpaceItem( EE_PARA_ULSPACE );
+ rDefItems[15] = new SvxLineSpacingItem( 0, EE_PARA_SBL );
+ rDefItems[16] = new SvxAdjustItem( SvxAdjust::Left, EE_PARA_JUST );
+ rDefItems[17] = new SvxTabStopItem( 0, 0, SvxTabAdjust::Left, EE_PARA_TABS );
+ rDefItems[18] = new SvxJustifyMethodItem( SvxCellJustifyMethod::Auto, EE_PARA_JUST_METHOD );
+ rDefItems[19] = new SvxVerJustifyItem( SvxCellVerJustify::Standard, EE_PARA_VER_JUST );
+
+ // Character attributes:
+ rDefItems[20] = new SvxColorItem( COL_AUTO, EE_CHAR_COLOR );
+ rDefItems[21] = new SvxFontItem( EE_CHAR_FONTINFO );
+ rDefItems[22] = new SvxFontHeightItem( 240, 100, EE_CHAR_FONTHEIGHT );
+ rDefItems[23] = new SvxCharScaleWidthItem( 100, EE_CHAR_FONTWIDTH );
+ rDefItems[24] = new SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT );
+ rDefItems[25] = new SvxUnderlineItem( LINESTYLE_NONE, EE_CHAR_UNDERLINE );
+ rDefItems[26] = new SvxCrossedOutItem( STRIKEOUT_NONE, EE_CHAR_STRIKEOUT );
+ rDefItems[27] = new SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC );
+ rDefItems[28] = new SvxContourItem( false, EE_CHAR_OUTLINE );
+ rDefItems[29] = new SvxShadowedItem( false, EE_CHAR_SHADOW );
+ rDefItems[30] = new SvxEscapementItem( 0, 100, EE_CHAR_ESCAPEMENT );
+ rDefItems[31] = new SvxAutoKernItem( false, EE_CHAR_PAIRKERNING );
+ rDefItems[32] = new SvxKerningItem( 0, EE_CHAR_KERNING );
+ rDefItems[33] = new SvxWordLineModeItem( false, EE_CHAR_WLM );
+ rDefItems[34] = new SvxLanguageItem( LANGUAGE_DONTKNOW, EE_CHAR_LANGUAGE );
+ rDefItems[35] = new SvxLanguageItem( LANGUAGE_DONTKNOW, EE_CHAR_LANGUAGE_CJK );
+ rDefItems[36] = new SvxLanguageItem( LANGUAGE_DONTKNOW, EE_CHAR_LANGUAGE_CTL );
+ rDefItems[37] = new SvxFontItem( EE_CHAR_FONTINFO_CJK );
+ rDefItems[38] = new SvxFontItem( EE_CHAR_FONTINFO_CTL );
+ rDefItems[39] = new SvxFontHeightItem( 240, 100, EE_CHAR_FONTHEIGHT_CJK );
+ rDefItems[40] = new SvxFontHeightItem( 240, 100, EE_CHAR_FONTHEIGHT_CTL );
+ rDefItems[41] = new SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT_CJK );
+ rDefItems[42] = new SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT_CTL );
+ rDefItems[43] = new SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC_CJK );
+ rDefItems[44] = new SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC_CTL );
+ rDefItems[45] = new SvxEmphasisMarkItem( FontEmphasisMark::NONE, EE_CHAR_EMPHASISMARK );
+ rDefItems[46] = new SvxCharReliefItem( FontRelief::NONE, EE_CHAR_RELIEF );
+ rDefItems[47] = new SfxVoidItem( EE_CHAR_RUBI_DUMMY );
+ rDefItems[48] = new SvXMLAttrContainerItem( EE_CHAR_XMLATTRIBS );
+ rDefItems[49] = new SvxOverlineItem( LINESTYLE_NONE, EE_CHAR_OVERLINE );
+ rDefItems[50] = new SvxCaseMapItem( SvxCaseMap::NotMapped, EE_CHAR_CASEMAP );
+ rDefItems[51] = new SfxGrabBagItem( EE_CHAR_GRABBAG );
+ rDefItems[52] = new SvxColorItem( COL_AUTO, EE_CHAR_BKGCOLOR );
+ // Features
+ rDefItems[53] = new SfxVoidItem( EE_FEATURE_TAB );
+ rDefItems[54] = new SfxVoidItem( EE_FEATURE_LINEBR );
+ rDefItems[55] = new SvxColorItem( COL_RED, EE_FEATURE_NOTCONV );
+ rDefItems[56] = new SvxFieldItem( SvxFieldData(), EE_FEATURE_FIELD );
+
+ assert(EDITITEMCOUNT == 57 && "ITEMCOUNT changed, adjust DefItems!");
+
+ // Init DefFonts:
+ GetDefaultFonts( *static_cast<SvxFontItem*>(rDefItems[EE_CHAR_FONTINFO - EE_ITEMS_START]),
+ *static_cast<SvxFontItem*>(rDefItems[EE_CHAR_FONTINFO_CJK - EE_ITEMS_START]),
+ *static_cast<SvxFontItem*>(rDefItems[EE_CHAR_FONTINFO_CTL - EE_ITEMS_START]) );
+}
+
+DefItems::~DefItems()
+{
+ for (const auto& rItem : mvDefItems)
+ delete rItem;
+}
+
+std::shared_ptr<DefItems> GlobalEditData::GetDefItems()
+{
+ auto xDefItems = m_xDefItems.lock();
+ if (!xDefItems)
+ {
+ xDefItems = std::make_shared<DefItems>();
+ m_xDefItems = xDefItems;
+ }
+ return xDefItems;
+}
+
+std::shared_ptr<SvxForbiddenCharactersTable> const & GlobalEditData::GetForbiddenCharsTable()
+{
+ if (!xForbiddenCharsTable)
+ xForbiddenCharsTable = SvxForbiddenCharactersTable::makeForbiddenCharactersTable(::comphelper::getProcessComponentContext());
+ return xForbiddenCharsTable;
+}
+
+uno::Reference< linguistic2::XLanguageGuessing > const & GlobalEditData::GetLanguageGuesser()
+{
+ if (!xLanguageGuesser.is())
+ {
+ xLanguageGuesser = linguistic2::LanguageGuessing::create( comphelper::getProcessComponentContext() );
+ }
+ return xLanguageGuesser;
+}
+
+OUString EditResId(TranslateId aId)
+{
+ return Translate::get(aId, Translate::Create("editeng"));
+}
+
+EditDLL::EditDLL()
+ : pGlobalData( new GlobalEditData )
+{
+}
+
+EditDLL::~EditDLL()
+{
+}
+
+editeng::SharedVclResources::SharedVclResources()
+ : m_pVirDev(VclPtr<VirtualDevice>::Create())
+{
+ m_pVirDev->SetMapMode(MapMode(MapUnit::MapTwip));
+}
+
+editeng::SharedVclResources::~SharedVclResources()
+ { m_pVirDev.disposeAndClear(); }
+
+VclPtr<VirtualDevice> const & editeng::SharedVclResources::GetVirtualDevice() const
+ { return m_pVirDev; }
+
+std::shared_ptr<editeng::SharedVclResources> EditDLL::GetSharedVclResources()
+{
+ SolarMutexGuard g;
+ auto pLocked(pSharedVcl.lock());
+ if(!pLocked)
+ pSharedVcl = pLocked = std::make_shared<editeng::SharedVclResources>();
+ return pLocked;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/eertfpar.cxx b/editeng/source/editeng/eertfpar.cxx
new file mode 100644
index 0000000000..948216f33d
--- /dev/null
+++ b/editeng/source/editeng/eertfpar.cxx
@@ -0,0 +1,633 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/string.hxx>
+#include <utility>
+
+#include "eertfpar.hxx"
+#include "impedit.hxx"
+#include <svl/intitem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/editeng.hxx>
+
+#include <svtools/rtftoken.h>
+#include <svtools/htmltokn.h>
+#include <unotools/configmgr.hxx>
+
+using namespace com::sun::star;
+
+HtmlImportInfo::HtmlImportInfo( HtmlImportState eSt, SvParser<HtmlTokenId>* pPrsrs, const ESelection& rSel )
+ : aSelection( rSel )
+{
+ pParser = pPrsrs;
+ eState = eSt;
+ nToken = HtmlTokenId::NONE;
+}
+
+HtmlImportInfo::~HtmlImportInfo()
+{
+}
+
+RtfImportInfo::RtfImportInfo( RtfImportState eSt, SvParser<int>* pPrsrs, const ESelection& rSel )
+ : aSelection( rSel )
+{
+ pParser = pPrsrs;
+ eState = eSt;
+ nToken = 0;
+ nTokenValue = 0;
+}
+
+constexpr MapUnit gRTFMapUnit = MapUnit::MapTwip;
+
+EditRTFParser::EditRTFParser(
+ SvStream& rIn, EditSelection aSel, SfxItemPool& rAttrPool, EditEngine* pEditEngine) :
+ SvxRTFParser(rAttrPool, rIn),
+ aCurSel(std::move(aSel)),
+ mpEditEngine(pEditEngine),
+ nDefFont(0),
+ bLastActionInsertParaBreak(false)
+{
+ SetInsPos(EditPosition(mpEditEngine, &aCurSel));
+
+ // Convert the twips values ...
+ SetCalcValue(true);
+ SetChkStyleAttr(mpEditEngine->IsImportRTFStyleSheetsSet());
+ SetNewDoc(false); // So that the Pool-Defaults are not overwritten...
+ aEditMapMode = MapMode(mpEditEngine->GetRefDevice()->GetMapMode().GetMapUnit());
+}
+
+EditRTFParser::~EditRTFParser()
+{
+}
+
+SvParserState EditRTFParser::CallParser()
+{
+ DBG_ASSERT( !aCurSel.HasRange(), "Selection for CallParser!" );
+ // Separate the part that is imported from the rest.
+ // This expression should be used for all imports.
+ // aStart1PaM: Last position before the imported content
+ // aEnd1PaM: First position after the imported content
+ // aStart2PaM: First position of the imported content
+ // aEnd2PaM: Last position of the imported content
+ EditPaM aStart1PaM( aCurSel.Min().GetNode(), aCurSel.Min().GetIndex() );
+ aCurSel = mpEditEngine->InsertParaBreak(aCurSel);
+ EditPaM aStart2PaM = aCurSel.Min();
+ // Useful or not?
+ aStart2PaM.GetNode()->GetContentAttribs().GetItems().ClearItem();
+ AddRTFDefaultValues( aStart2PaM, aStart2PaM );
+ EditPaM aEnd1PaM = mpEditEngine->InsertParaBreak(aCurSel.Max());
+ // aCurCel now points to the gap
+
+ if (mpEditEngine->IsRtfImportHandlerSet())
+ {
+ RtfImportInfo aImportInfo(RtfImportState::Start, this, mpEditEngine->CreateESelection(aCurSel));
+ mpEditEngine->CallRtfImportHandler(aImportInfo);
+ }
+
+ SvParserState _eState = SvxRTFParser::CallParser();
+
+ if (mpEditEngine->IsRtfImportHandlerSet())
+ {
+ RtfImportInfo aImportInfo(RtfImportState::End, this, mpEditEngine->CreateESelection(aCurSel));
+ mpEditEngine->CallRtfImportHandler(aImportInfo);
+ }
+
+ if (bLastActionInsertParaBreak)
+ {
+ ContentNode* pCurNode = aCurSel.Max().GetNode();
+ sal_Int32 nPara = mpEditEngine->GetEditDoc().GetPos(pCurNode);
+ ContentNode* pPrevNode = mpEditEngine->GetEditDoc().GetObject(nPara-1);
+ DBG_ASSERT( pPrevNode, "Invalid RTF-Document?!" );
+ EditSelection aSel;
+ aSel.Min() = EditPaM( pPrevNode, pPrevNode->Len() );
+ aSel.Max() = EditPaM( pCurNode, 0 );
+ aCurSel.Max() = mpEditEngine->DeleteSelection(aSel);
+ }
+ EditPaM aEnd2PaM( aCurSel.Max() );
+ //AddRTFDefaultValues( aStart2PaM, aEnd2PaM );
+ bool bOnlyOnePara = ( aEnd2PaM.GetNode() == aStart2PaM.GetNode() );
+ // Paste the chunk again ...
+ // Problem: Paragraph attributes may not possibly be taken over
+ // => Do Character attributes.
+
+ bool bSpecialBackward = aStart1PaM.GetNode()->Len() == 0;
+ if ( bOnlyOnePara || aStart1PaM.GetNode()->Len() )
+ mpEditEngine->ParaAttribsToCharAttribs( aStart2PaM.GetNode() );
+ aCurSel.Min() = mpEditEngine->ConnectParagraphs(
+ aStart1PaM.GetNode(), aStart2PaM.GetNode(), bSpecialBackward );
+ bSpecialBackward = aEnd1PaM.GetNode()->Len() != 0;
+ // when bOnlyOnePara, then the node is gone on Connect.
+ if ( !bOnlyOnePara && aEnd1PaM.GetNode()->Len() )
+ mpEditEngine->ParaAttribsToCharAttribs( aEnd2PaM.GetNode() );
+ aCurSel.Max() = mpEditEngine->ConnectParagraphs(
+ ( bOnlyOnePara ? aStart1PaM.GetNode() : aEnd2PaM.GetNode() ),
+ aEnd1PaM.GetNode(), bSpecialBackward );
+
+ return _eState;
+}
+
+void EditRTFParser::AddRTFDefaultValues( const EditPaM& rStart, const EditPaM& rEnd )
+{
+ // Problem: DefFont and DefFontHeight
+ Size aSz( 12, 0 );
+ MapMode aPntMode( MapUnit::MapPoint );
+ MapMode _aEditMapMode(mpEditEngine->GetRefDevice()->GetMapMode().GetMapUnit());
+ aSz = mpEditEngine->GetRefDevice()->LogicToLogic(aSz, &aPntMode, &_aEditMapMode);
+ SvxFontHeightItem aFontHeightItem( aSz.Width(), 100, EE_CHAR_FONTHEIGHT );
+ vcl::Font aDefFont( GetFont( nDefFont ) );
+ SvxFontItem aFontItem( aDefFont.GetFamilyType(), aDefFont.GetFamilyName(),
+ aDefFont.GetStyleName(), aDefFont.GetPitch(), aDefFont.GetCharSet(), EE_CHAR_FONTINFO );
+
+ sal_Int32 nStartPara = mpEditEngine->GetEditDoc().GetPos( rStart.GetNode() );
+ sal_Int32 nEndPara = mpEditEngine->GetEditDoc().GetPos( rEnd.GetNode() );
+ for ( sal_Int32 nPara = nStartPara; nPara <= nEndPara; nPara++ )
+ {
+ ContentNode* pNode = mpEditEngine->GetEditDoc().GetObject( nPara );
+ assert(pNode && "AddRTFDefaultValues - No paragraph?!");
+ if ( !pNode->GetContentAttribs().HasItem( EE_CHAR_FONTINFO ) )
+ pNode->GetContentAttribs().GetItems().Put( aFontItem );
+ if ( !pNode->GetContentAttribs().HasItem( EE_CHAR_FONTHEIGHT ) )
+ pNode->GetContentAttribs().GetItems().Put( aFontHeightItem );
+ }
+}
+
+void EditRTFParser::NextToken( int nToken )
+{
+ switch( nToken )
+ {
+ case RTF_DEFF:
+ {
+ nDefFont = sal_uInt16(nTokenValue);
+ }
+ break;
+ case RTF_DEFTAB:
+ break;
+ case RTF_CELL:
+ {
+ aCurSel = mpEditEngine->InsertParaBreak(aCurSel);
+ }
+ break;
+ case RTF_LINE:
+ {
+ aCurSel = mpEditEngine->InsertLineBreak(aCurSel);
+ }
+ break;
+ case RTF_FIELD:
+ {
+ ReadField();
+ }
+ break;
+ case RTF_SHPINST: // fdo#76776 process contents of shpinst
+ break;
+ case RTF_SP: // fdo#76776 but skip SP groups
+ {
+ SkipGroup();
+ }
+ break;
+ case RTF_LISTTEXT:
+ {
+ SkipGroup();
+ }
+ break;
+ default:
+ {
+ SvxRTFParser::NextToken( nToken );
+ if ( nToken == RTF_STYLESHEET )
+ CreateStyleSheets();
+ }
+ break;
+ }
+ if (mpEditEngine->IsRtfImportHandlerSet())
+ {
+ RtfImportInfo aImportInfo(RtfImportState::NextToken, this, mpEditEngine->CreateESelection(aCurSel));
+ aImportInfo.nToken = nToken;
+ aImportInfo.nTokenValue = short(nTokenValue);
+ mpEditEngine->CallRtfImportHandler(aImportInfo);
+ }
+}
+
+void EditRTFParser::UnknownAttrToken( int nToken )
+{
+ // for Tokens which are not evaluated in ReadAttr
+ // Actually, only for Calc (RTFTokenHdl), so that RTF_INTBL
+ if (mpEditEngine->IsRtfImportHandlerSet())
+ {
+ RtfImportInfo aImportInfo(RtfImportState::UnknownAttr, this, mpEditEngine->CreateESelection(aCurSel));
+ aImportInfo.nToken = nToken;
+ aImportInfo.nTokenValue = short(nTokenValue);
+ mpEditEngine->CallRtfImportHandler(aImportInfo);
+ }
+}
+
+void EditRTFParser::InsertText()
+{
+ OUString aText( aToken );
+ if (mpEditEngine->IsRtfImportHandlerSet())
+ {
+ RtfImportInfo aImportInfo(RtfImportState::InsertText, this, mpEditEngine->CreateESelection(aCurSel));
+ mpEditEngine->CallRtfImportHandler(aImportInfo);
+ }
+ aCurSel = mpEditEngine->InsertText(aCurSel, aText);
+ bLastActionInsertParaBreak = false;
+}
+
+void EditRTFParser::InsertPara()
+{
+ if (mpEditEngine->IsRtfImportHandlerSet())
+ {
+ RtfImportInfo aImportInfo(RtfImportState::InsertPara, this, mpEditEngine->CreateESelection(aCurSel));
+ mpEditEngine->CallRtfImportHandler(aImportInfo);
+ }
+ aCurSel = mpEditEngine->InsertParaBreak(aCurSel);
+ bLastActionInsertParaBreak = true;
+}
+
+void EditRTFParser::MovePos( bool const bForward )
+{
+ if( bForward )
+ aCurSel = mpEditEngine->CursorRight(
+ aCurSel.Max(), i18n::CharacterIteratorMode::SKIPCHARACTER);
+ else
+ aCurSel = mpEditEngine->CursorLeft(
+ aCurSel.Max(), i18n::CharacterIteratorMode::SKIPCHARACTER);
+}
+
+void EditRTFParser::SetEndPrevPara( std::optional<EditNodeIdx>& rpNodePos,
+ sal_Int32& rCntPos )
+{
+ // The Intention is to: determine the current insert position of the
+ // previous paragraph and set the end from this.
+ // This "\pard" always apply on the right paragraph.
+
+ ContentNode* pN = aCurSel.Max().GetNode();
+ sal_Int32 nCurPara = mpEditEngine->GetEditDoc().GetPos( pN );
+ DBG_ASSERT( nCurPara != 0, "Paragraph equal to 0: SetEnfPrevPara" );
+ if ( nCurPara )
+ nCurPara--;
+ ContentNode* pPrevNode = mpEditEngine->GetEditDoc().GetObject( nCurPara );
+ assert(pPrevNode && "pPrevNode = 0!");
+ rpNodePos = EditNodeIdx(mpEditEngine, pPrevNode);
+ rCntPos = pPrevNode->Len();
+}
+
+bool EditRTFParser::IsEndPara( EditNodeIdx* pNd, sal_Int32 nCnt ) const
+{
+ return nCnt == pNd->GetNode()->Len();
+}
+
+void EditRTFParser::SetAttrInDoc( SvxRTFItemStackType &rSet )
+{
+ ContentNode* pSttNode = const_cast<EditNodeIdx&>(rSet.GetSttNode()).GetNode();
+ ContentNode* pEndNode = const_cast<EditNodeIdx&>(rSet.GetEndNode()).GetNode();
+
+ EditPaM aStartPaM( pSttNode, rSet.GetSttCnt() );
+ EditPaM aEndPaM( pEndNode, rSet.GetEndCnt() );
+
+ // If possible adjust the Escapement-Item:
+
+ // #i66167# adapt font heights to destination MapUnit if necessary
+ const MapUnit eDestUnit = mpEditEngine->GetEditDoc().GetItemPool().GetMetric(0);
+ if (eDestUnit != gRTFMapUnit)
+ {
+ sal_uInt16 const aFntHeightIems[3] = { EE_CHAR_FONTHEIGHT, EE_CHAR_FONTHEIGHT_CJK, EE_CHAR_FONTHEIGHT_CTL };
+ for (unsigned short aFntHeightIem : aFntHeightIems)
+ {
+ const SfxPoolItem* pItem;
+ if (SfxItemState::SET == rSet.GetAttrSet().GetItemState( aFntHeightIem, false, &pItem ))
+ {
+ sal_uInt32 nHeight = static_cast<const SvxFontHeightItem*>(pItem)->GetHeight();
+ tools::Long nNewHeight;
+ nNewHeight = OutputDevice::LogicToLogic( static_cast<tools::Long>(nHeight), gRTFMapUnit, eDestUnit );
+
+ SvxFontHeightItem aFntHeightItem( nNewHeight, 100, aFntHeightIem );
+ aFntHeightItem.SetProp(
+ static_cast<const SvxFontHeightItem*>(pItem)->GetProp(),
+ static_cast<const SvxFontHeightItem*>(pItem)->GetPropUnit());
+ rSet.GetAttrSet().Put( aFntHeightItem );
+ }
+ }
+ }
+
+ if( const SvxEscapementItem* pItem = rSet.GetAttrSet().GetItemIfSet( EE_CHAR_ESCAPEMENT, false ) )
+ {
+ // the correct one
+ tools::Long nEsc = pItem->GetEsc();
+ tools::Long nEscFontHeight = 0;
+ if( ( DFLT_ESC_AUTO_SUPER != nEsc ) && ( DFLT_ESC_AUTO_SUB != nEsc ) )
+ {
+ nEsc *= 10; //HalfPoints => Twips was embezzled in RTFITEM.CXX!
+ SvxFont aFont;
+ if (utl::ConfigManager::IsFuzzing())
+ {
+ // ofz#24932 detecting RTL vs LTR is slow
+ aFont = aStartPaM.GetNode()->GetCharAttribs().GetDefFont();
+ }
+ else
+ mpEditEngine->SeekCursor(aStartPaM.GetNode(), aStartPaM.GetIndex()+1, aFont);
+ nEscFontHeight = aFont.GetFontSize().Height();
+ }
+ if (nEscFontHeight)
+ {
+ nEsc = nEsc * 100 / nEscFontHeight;
+
+ SvxEscapementItem aEscItem( static_cast<short>(nEsc), pItem->GetProportionalHeight(), EE_CHAR_ESCAPEMENT );
+ rSet.GetAttrSet().Put( aEscItem );
+ }
+ }
+
+ if (mpEditEngine->IsRtfImportHandlerSet())
+ {
+ EditSelection aSel( aStartPaM, aEndPaM );
+ RtfImportInfo aImportInfo(RtfImportState::SetAttr, this, mpEditEngine->CreateESelection(aSel));
+ mpEditEngine->CallRtfImportHandler(aImportInfo);
+ }
+
+ ContentNode* pSN = aStartPaM.GetNode();
+ ContentNode* pEN = aEndPaM.GetNode();
+ sal_Int32 nStartNode = mpEditEngine->GetEditDoc().GetPos( pSN );
+ sal_Int32 nEndNode = mpEditEngine->GetEditDoc().GetPos( pEN );
+ sal_Int16 nOutlLevel = 0xff;
+
+ if (rSet.StyleNo() && mpEditEngine->GetStyleSheetPool() && mpEditEngine->IsImportRTFStyleSheetsSet())
+ {
+ SvxRTFStyleTbl::iterator it = GetStyleTbl().find( rSet.StyleNo() );
+ DBG_ASSERT( it != GetStyleTbl().end(), "Template not defined in RTF!" );
+ if ( it != GetStyleTbl().end() )
+ {
+ auto const& pS = it->second;
+ mpEditEngine->SetStyleSheet(
+ EditSelection(aStartPaM, aEndPaM),
+ static_cast<SfxStyleSheet*>(mpEditEngine->GetStyleSheetPool()->Find(pS.sName, SfxStyleFamily::All)));
+ nOutlLevel = pS.nOutlineNo;
+ }
+ }
+
+ // When an Attribute goes from 0 to the current paragraph length,
+ // it should be a paragraph attribute!
+
+ // Note: Selection can reach over several paragraphs.
+ // All Complete paragraphs are paragraph attributes ...
+ for ( sal_Int32 z = nStartNode+1; z < nEndNode; z++ )
+ {
+ DBG_ASSERT(mpEditEngine->GetEditDoc().GetObject(z), "Node does not exist yet(RTF)");
+ mpEditEngine->SetParaAttribsOnly(z, rSet.GetAttrSet());
+ }
+
+ if ( aStartPaM.GetNode() != aEndPaM.GetNode() )
+ {
+ // The rest of the StartNodes...
+ if ( aStartPaM.GetIndex() == 0 )
+ mpEditEngine->SetParaAttribsOnly(nStartNode, rSet.GetAttrSet());
+ else
+ mpEditEngine->SetAttribs(
+ EditSelection(aStartPaM, EditPaM(aStartPaM.GetNode(), aStartPaM.GetNode()->Len())), rSet.GetAttrSet());
+
+ // the beginning of the EndNodes...
+ if ( aEndPaM.GetIndex() == aEndPaM.GetNode()->Len() )
+ mpEditEngine->SetParaAttribsOnly(nEndNode, rSet.GetAttrSet());
+ else
+ mpEditEngine->SetAttribs(
+ EditSelection(EditPaM(aEndPaM.GetNode(), 0), aEndPaM), rSet.GetAttrSet());
+ }
+ else
+ {
+ if ( ( aStartPaM.GetIndex() == 0 ) && ( aEndPaM.GetIndex() == aEndPaM.GetNode()->Len() ) )
+ {
+ // When settings char attribs as para attribs, we must merge with existing attribs, not overwrite the ItemSet!
+ SfxItemSet aAttrs = mpEditEngine->GetBaseParaAttribs(nStartNode);
+ aAttrs.Put( rSet.GetAttrSet() );
+ mpEditEngine->SetParaAttribsOnly(nStartNode, aAttrs);
+ }
+ else
+ {
+ mpEditEngine->SetAttribs(
+ EditSelection(aStartPaM, aEndPaM), rSet.GetAttrSet());
+ }
+ }
+
+ // OutlLevel...
+ if ( nOutlLevel != 0xff )
+ {
+ for ( sal_Int32 n = nStartNode; n <= nEndNode; n++ )
+ {
+ ContentNode* pNode = mpEditEngine->GetEditDoc().GetObject( n );
+ pNode->GetContentAttribs().GetItems().Put( SfxInt16Item( EE_PARA_OUTLLEVEL, nOutlLevel ) );
+ }
+ }
+}
+
+SvxRTFStyleType* EditRTFParser::FindStyleSheet( std::u16string_view rName )
+{
+ SvxRTFStyleTbl& rTable = GetStyleTbl();
+ for (auto & iter : rTable)
+ {
+ if (iter.second.sName == rName)
+ return &iter.second;
+ }
+ return nullptr;
+}
+
+SfxStyleSheet* EditRTFParser::CreateStyleSheet( SvxRTFStyleType const * pRTFStyle )
+{
+ // Check if a template exists, then it will not be changed!
+ SfxStyleSheet* pStyle = static_cast<SfxStyleSheet*>(mpEditEngine->GetStyleSheetPool()->Find( pRTFStyle->sName, SfxStyleFamily::All ));
+ if ( pStyle )
+ return pStyle;
+
+ OUString aName( pRTFStyle->sName );
+ OUString aParent;
+ if ( pRTFStyle->nBasedOn )
+ {
+ SvxRTFStyleTbl::iterator it = GetStyleTbl().find( pRTFStyle->nBasedOn );
+ if ( it != GetStyleTbl().end())
+ {
+ SvxRTFStyleType const& rS = it->second;
+ if ( &rS != pRTFStyle )
+ aParent = rS.sName;
+ }
+ }
+
+ pStyle = static_cast<SfxStyleSheet*>( &mpEditEngine->GetStyleSheetPool()->Make( aName, SfxStyleFamily::Para ) );
+
+ // 1) convert and take over Items ...
+ ConvertAndPutItems( pStyle->GetItemSet(), pRTFStyle->aAttrSet );
+
+ // 2) As long as Parent is not in the pool, also create this ...
+ if ( !aParent.isEmpty() && ( aParent != aName ) )
+ {
+ SfxStyleSheet* pS = static_cast<SfxStyleSheet*>(mpEditEngine->GetStyleSheetPool()->Find( aParent, SfxStyleFamily::All ));
+ if ( !pS )
+ {
+ // If not found anywhere, create from RTF ...
+ SvxRTFStyleType* _pRTFStyle = FindStyleSheet( aParent );
+ if ( _pRTFStyle )
+ pS = CreateStyleSheet( _pRTFStyle );
+ }
+ // 2b) Link Itemset with Parent ...
+ if ( pS )
+ pStyle->GetItemSet().SetParent( &pS->GetItemSet() );
+ }
+ return pStyle;
+}
+
+void EditRTFParser::CreateStyleSheets()
+{
+ // the SvxRTFParser has now created the template...
+ if (mpEditEngine->GetStyleSheetPool() && mpEditEngine->IsImportRTFStyleSheetsSet())
+ {
+ for (auto & elem : GetStyleTbl())
+ {
+ SvxRTFStyleType& rRTFStyle = elem.second;
+ CreateStyleSheet( &rRTFStyle );
+ }
+ }
+}
+
+void EditRTFParser::CalcValue()
+{
+ const MapUnit eDestUnit = aEditMapMode.GetMapUnit();
+ if (eDestUnit != gRTFMapUnit)
+ nTokenValue = OutputDevice::LogicToLogic( nTokenValue, gRTFMapUnit, eDestUnit );
+}
+
+void EditRTFParser::ReadField()
+{
+ // From SwRTFParser::ReadField()
+ int _nOpenBrackets = 1; // the first was already detected earlier
+ bool bFldInst = false;
+ bool bFldRslt = false;
+ OUString aFldInst;
+ OUString aFldRslt;
+
+ while( _nOpenBrackets && IsParserWorking() )
+ {
+ switch( GetNextToken() )
+ {
+ case '}':
+ {
+ _nOpenBrackets--;
+ if ( _nOpenBrackets == 1 )
+ {
+ bFldInst = false;
+ bFldRslt = false;
+ }
+ }
+ break;
+
+ case '{': _nOpenBrackets++;
+ break;
+
+ case RTF_FIELD: SkipGroup();
+ break;
+
+ case RTF_FLDINST: bFldInst = true;
+ break;
+
+ case RTF_FLDRSLT: bFldRslt = true;
+ break;
+
+ case RTF_TEXTTOKEN:
+ {
+ if ( bFldInst )
+ aFldInst += aToken;
+ else if ( bFldRslt )
+ aFldRslt += aToken;
+ }
+ break;
+ }
+ }
+ if ( !aFldInst.isEmpty() )
+ {
+ OUString aHyperLinkMarker( "HYPERLINK " );
+ if ( aFldInst.startsWithIgnoreAsciiCase( aHyperLinkMarker ) )
+ {
+ aFldInst = aFldInst.copy( aHyperLinkMarker.getLength() );
+ aFldInst = comphelper::string::strip(aFldInst, ' ');
+ // strip start and end quotes
+ aFldInst = aFldInst.copy( 1, aFldInst.getLength()-2 );
+
+ if ( aFldRslt.isEmpty() )
+ aFldRslt = aFldInst;
+
+ SvxFieldItem aField( SvxURLField( aFldInst, aFldRslt, SvxURLFormat::Repr ), EE_FEATURE_FIELD );
+ aCurSel = mpEditEngine->InsertField(aCurSel, aField);
+ mpEditEngine->UpdateFieldsOnly();
+ bLastActionInsertParaBreak = false;
+ }
+ }
+
+ SkipToken(); // the closing brace is evaluated "above"
+}
+
+void EditRTFParser::SkipGroup()
+{
+ int _nOpenBrackets = 1; // the first was already detected earlier
+
+ while( _nOpenBrackets && IsParserWorking() )
+ {
+ switch( GetNextToken() )
+ {
+ case '}':
+ {
+ _nOpenBrackets--;
+ }
+ break;
+
+ case '{':
+ {
+ _nOpenBrackets++;
+ }
+ break;
+ }
+ }
+
+ SkipToken(); // the closing brace is evaluated "above"
+}
+
+EditNodeIdx::EditNodeIdx(EditEngine* pEE, ContentNode* pNd) :
+ mpEditEngine(pEE), mpNode(pNd) {}
+
+sal_Int32 EditNodeIdx::GetIdx() const
+{
+ return mpEditEngine->GetEditDoc().GetPos(mpNode);
+}
+
+EditPosition::EditPosition(EditEngine* pEE, EditSelection* pSel) :
+ mpEditEngine(pEE), mpCurSel(pSel) {}
+
+EditNodeIdx EditPosition::MakeNodeIdx() const
+{
+ return EditNodeIdx(mpEditEngine, mpCurSel->Max().GetNode());
+}
+
+sal_Int32 EditPosition::GetNodeIdx() const
+{
+ ContentNode* pN = mpCurSel->Max().GetNode();
+ return mpEditEngine->GetEditDoc().GetPos(pN);
+}
+
+sal_Int32 EditPosition::GetCntIdx() const
+{
+ return mpCurSel->Max().GetIndex();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/eertfpar.hxx b/editeng/source/editeng/eertfpar.hxx
new file mode 100644
index 0000000000..19ce00ce32
--- /dev/null
+++ b/editeng/source/editeng/eertfpar.hxx
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <editeng/svxrtf.hxx>
+
+#include <editdoc.hxx>
+
+class EditEngine;
+
+class EditRTFParser final : public SvxRTFParser
+{
+private:
+ EditSelection aCurSel;
+ EditEngine* mpEditEngine;
+ MapMode aEditMapMode;
+
+ sal_uInt16 nDefFont;
+ bool bLastActionInsertParaBreak;
+
+ virtual void InsertPara() override;
+ virtual void InsertText() override;
+ virtual void MovePos( bool bForward = true ) override;
+ virtual void SetEndPrevPara( std::optional<EditNodeIdx>& rpNodePos,
+ sal_Int32& rCntPos ) override;
+
+ virtual void UnknownAttrToken( int nToken ) override;
+ virtual void NextToken( int nToken ) override;
+ virtual void SetAttrInDoc( SvxRTFItemStackType &rSet ) override;
+ virtual bool IsEndPara( EditNodeIdx* pNd, sal_Int32 nCnt ) const override;
+ virtual void CalcValue() override;
+ void CreateStyleSheets();
+ SfxStyleSheet* CreateStyleSheet( SvxRTFStyleType const * pRTFStyle );
+ SvxRTFStyleType* FindStyleSheet( std::u16string_view rName );
+ void AddRTFDefaultValues( const EditPaM& rStart, const EditPaM& rEnd );
+ void ReadField();
+ void SkipGroup();
+
+public:
+ EditRTFParser(SvStream& rIn, EditSelection aCurSel, SfxItemPool& rAttrPool, EditEngine* pEditEngine);
+ virtual ~EditRTFParser() override;
+
+ virtual SvParserState CallParser() override;
+
+ EditPaM const & GetCurPaM() const { return aCurSel.Max(); }
+};
+
+typedef tools::SvRef<EditRTFParser> EditRTFParserRef;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/fieldupdater.cxx b/editeng/source/editeng/fieldupdater.cxx
new file mode 100644
index 0000000000..05eca45755
--- /dev/null
+++ b/editeng/source/editeng/fieldupdater.cxx
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <memory>
+#include <editeng/fieldupdater.hxx>
+#include <editeng/flditem.hxx>
+#include "editobj2.hxx"
+
+#include <com/sun/star/text/textfield/Type.hpp>
+
+using namespace com::sun::star;
+
+namespace editeng {
+
+class FieldUpdaterImpl
+{
+ EditTextObjectImpl& mrObj;
+public:
+ explicit FieldUpdaterImpl(EditTextObject& rObj) : mrObj(toImpl(rObj)) {}
+
+ void updateTableFields(int nTab)
+ {
+ SfxItemPool* pPool = mrObj.GetPool();
+ EditTextObjectImpl::ContentInfosType& rContents = mrObj.GetContents();
+ for (std::unique_ptr<ContentInfo> & i : rContents)
+ {
+ ContentInfo& rContent = *i;
+ for (XEditAttribute & rAttr : rContent.GetCharAttribs())
+ {
+ const SfxPoolItem* pItem = rAttr.GetItem();
+ if (pItem->Which() != EE_FEATURE_FIELD)
+ // This is not a field item.
+ continue;
+
+ const SvxFieldItem* pFI = static_cast<const SvxFieldItem*>(pItem);
+ const SvxFieldData* pData = pFI->GetField();
+ if (pData->GetClassId() != text::textfield::Type::TABLE)
+ // This is not a table field.
+ continue;
+
+ // Create a new table field with the new ID, and set it to the
+ // attribute object.
+ SvxFieldItem aNewItem(SvxTableField(nTab), EE_FEATURE_FIELD);
+ rAttr.SetItem(*pPool, aNewItem);
+ }
+ }
+ }
+};
+
+FieldUpdater::FieldUpdater(EditTextObject& rObj) : mpImpl(new FieldUpdaterImpl(rObj)) {}
+FieldUpdater::FieldUpdater(const FieldUpdater& r) : mpImpl(new FieldUpdaterImpl(*r.mpImpl)) {}
+
+FieldUpdater::~FieldUpdater()
+{
+}
+
+void FieldUpdater::updateTableFields(int nTab)
+{
+ mpImpl->updateTableFields(nTab);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/impedit.cxx b/editeng/source/editeng/impedit.cxx
new file mode 100644
index 0000000000..92fb5affa6
--- /dev/null
+++ b/editeng/source/editeng/impedit.cxx
@@ -0,0 +1,2787 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "impedit.hxx"
+#include <sal/log.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/urlfieldhelper.hxx>
+#include <tools/poly.hxx>
+#include <editeng/unolingu.hxx>
+#include <com/sun/star/linguistic2/XDictionary.hpp>
+#include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
+#include <com/sun/star/datatransfer/dnd/XDragGestureRecognizer.hpp>
+#include <com/sun/star/datatransfer/dnd/XDropTarget.hpp>
+#include <com/sun/star/datatransfer/clipboard/XFlushableClipboard.hpp>
+#include <comphelper/lok.hxx>
+#include <editeng/flditem.hxx>
+#include <svl/intitem.hxx>
+#include <vcl/inputctx.hxx>
+#include <vcl/transfer.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weldutils.hxx>
+#include <vcl/window.hxx>
+#include <sot/exchange.hxx>
+#include <sot/formats.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <comphelper/string.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <boost/property_tree/ptree.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::linguistic2;
+
+#define SCRLRANGE 20 // Scroll 1/20 of the width/height, when in QueryDrop
+
+static void lcl_AlignToPixel(Point& rPoint, const OutputDevice& rOutDev, short nDiffX, short nDiffY)
+{
+ rPoint = rOutDev.LogicToPixel( rPoint );
+
+ if ( nDiffX )
+ rPoint.AdjustX(nDiffX );
+ if ( nDiffY )
+ rPoint.AdjustY(nDiffY );
+
+ rPoint = rOutDev.PixelToLogic( rPoint );
+}
+
+LOKSpecialPositioning::LOKSpecialPositioning(const ImpEditView& rImpEditView, MapUnit eUnit,
+ const tools::Rectangle& rOutputArea,
+ const Point& rVisDocStartPos) :
+ mrImpEditView(rImpEditView),
+ maOutArea(rOutputArea),
+ maVisDocStartPos(rVisDocStartPos),
+ meUnit(eUnit),
+ meFlags(LOKSpecialFlags::NONE)
+{
+}
+
+void LOKSpecialPositioning::ReInit(MapUnit eUnit, const tools::Rectangle& rOutputArea, const Point& rVisDocStartPos)
+{
+ meUnit = eUnit;
+ maOutArea = rOutputArea;
+ maVisDocStartPos = rVisDocStartPos;
+}
+
+void LOKSpecialPositioning::SetOutputArea(const tools::Rectangle& rOutputArea)
+{
+ maOutArea = rOutputArea;
+}
+
+const tools::Rectangle& LOKSpecialPositioning::GetOutputArea() const
+{
+ return maOutArea;
+}
+
+void LOKSpecialPositioning::SetVisDocStartPos(const Point& rVisDocStartPos)
+{
+ maVisDocStartPos = rVisDocStartPos;
+}
+
+tools::Rectangle LOKSpecialPositioning::GetVisDocArea() const
+{
+ return tools::Rectangle(GetVisDocLeft(), GetVisDocTop(), GetVisDocRight(), GetVisDocBottom());
+}
+
+bool LOKSpecialPositioning::IsVertical() const
+{
+ return mrImpEditView.IsVertical();
+}
+
+bool LOKSpecialPositioning::IsTopToBottom() const
+{
+ return mrImpEditView.IsTopToBottom();
+}
+
+Point LOKSpecialPositioning::GetWindowPos(const Point& rDocPos, MapUnit eDocPosUnit) const
+{
+ const Point aDocPos = convertUnit(rDocPos, eDocPosUnit);
+ Point aPoint;
+ if ( !IsVertical() )
+ {
+ aPoint.setX(aDocPos.X() + maOutArea.Left() - GetVisDocLeft());
+ aPoint.setY(aDocPos.Y() + maOutArea.Top() - GetVisDocTop());
+ }
+ else
+ {
+ if (IsTopToBottom())
+ {
+ aPoint.setX(maOutArea.Right() - aDocPos.Y() + GetVisDocTop());
+ aPoint.setY(aDocPos.X() + maOutArea.Top() - GetVisDocLeft());
+ }
+ else
+ {
+ aPoint.setX(maOutArea.Left() + aDocPos.Y() - GetVisDocTop());
+ aPoint.setY(maOutArea.Bottom() - aDocPos.X() + GetVisDocLeft());
+ }
+ }
+
+ return aPoint;
+}
+
+tools::Rectangle LOKSpecialPositioning::GetWindowPos(const tools::Rectangle& rDocRect, MapUnit eDocRectUnit) const
+{
+ const tools::Rectangle aDocRect = convertUnit(rDocRect, eDocRectUnit);
+ Point aPos(GetWindowPos(aDocRect.TopLeft(), meUnit));
+ Size aSz = aDocRect.GetSize();
+ tools::Rectangle aRect;
+ if (!IsVertical())
+ {
+ aRect = tools::Rectangle(aPos, aSz);
+ }
+ else
+ {
+ Point aNewPos(aPos.X() - aSz.Height(), aPos.Y());
+ // coverity[swapped_arguments : FALSE] - this is in the correct order
+ aRect = tools::Rectangle(aNewPos, Size(aSz.Height(), aSz.Width()));
+ }
+ return aRect;
+}
+
+Point LOKSpecialPositioning::convertUnit(const Point& rPos, MapUnit ePosUnit) const
+{
+ if (ePosUnit == meUnit)
+ return rPos;
+
+ return OutputDevice::LogicToLogic(rPos, MapMode(ePosUnit), MapMode(meUnit));
+}
+
+tools::Rectangle LOKSpecialPositioning::convertUnit(const tools::Rectangle& rRect, MapUnit eRectUnit) const
+{
+ if (eRectUnit == meUnit)
+ return rRect;
+
+ return OutputDevice::LogicToLogic(rRect, MapMode(eRectUnit), MapMode(meUnit));
+}
+
+Point LOKSpecialPositioning::GetRefPoint() const
+{
+ return maOutArea.TopLeft();
+}
+
+// class ImpEditView
+
+ImpEditView::ImpEditView( EditView* pView, EditEngine* pEng, vcl::Window* pWindow ) :
+ pEditView(pView),
+ mpViewShell(nullptr),
+ mpOtherShell(nullptr),
+ pEditEngine(pEng),
+ pOutWin(pWindow),
+ nInvMore(1),
+ nControl(EVControlBits::AUTOSCROLL | EVControlBits::ENABLEPASTE),
+ nTravelXPos(TRAVEL_X_DONTKNOW),
+ nExtraCursorFlags(GetCursorFlags::NONE),
+ nCursorBidiLevel(CURSOR_BIDILEVEL_DONTKNOW),
+ nScrollDiffX(0),
+ bReadOnly(false),
+ bClickedInSelection(false),
+ bActiveDragAndDropListener(false),
+ aOutArea( Point(), pEng->GetPaperSize() ),
+ eSelectionMode(EESelectionMode::Std),
+ eAnchorMode(EEAnchorMode::TopLeft),
+ mpEditViewCallbacks(nullptr),
+ mbBroadcastLOKViewCursor(comphelper::LibreOfficeKit::isActive()),
+ mbSuppressLOKMessages(false),
+ mbNegativeX(false)
+{
+ aEditSelection.Min() = pEng->GetEditDoc().GetStartPaM();
+ aEditSelection.Max() = pEng->GetEditDoc().GetEndPaM();
+
+ SelectionChanged();
+}
+
+ImpEditView::~ImpEditView()
+{
+ RemoveDragAndDropListeners();
+
+ if ( pOutWin && ( pOutWin->GetCursor() == pCursor.get() ) )
+ pOutWin->SetCursor( nullptr );
+}
+
+void ImpEditView::SetBackgroundColor( const Color& rColor )
+{
+ mxBackgroundColor = rColor;
+}
+
+const Color& ImpEditView::GetBackgroundColor() const
+{
+ return mxBackgroundColor ? *mxBackgroundColor : GetOutputDevice().GetBackground().GetColor();
+}
+
+void ImpEditView::RegisterViewShell(OutlinerViewShell* pViewShell)
+{
+ mpViewShell = pViewShell;
+}
+
+void ImpEditView::RegisterOtherShell(OutlinerViewShell* pOtherShell)
+{
+ mpOtherShell = pOtherShell;
+}
+
+const OutlinerViewShell* ImpEditView::GetViewShell() const
+{
+ return mpViewShell;
+}
+
+void ImpEditView::SetEditSelection( const EditSelection& rEditSelection )
+{
+ // set state before notification
+ aEditSelection = rEditSelection;
+
+ SelectionChanged();
+
+ if (comphelper::LibreOfficeKit::isActive())
+ // Tiled rendering: selections are only painted when we are in selection mode.
+ pEditEngine->SetInSelectionMode(aEditSelection.HasRange());
+
+ if ( pEditEngine->pImpEditEngine->GetNotifyHdl().IsSet() )
+ {
+ const EditDoc& rDoc = pEditEngine->GetEditDoc();
+ const EditPaM pmEnd = rDoc.GetEndPaM();
+ EENotifyType eNotifyType;
+ if (rDoc.Count() > 1 &&
+ pmEnd == rEditSelection.Min() &&
+ pmEnd == rEditSelection.Max())//if move cursor to the last para.
+ {
+ eNotifyType = EE_NOTIFY_TEXTVIEWSELECTIONCHANGED_ENDD_PARA;
+ }
+ else
+ {
+ eNotifyType = EE_NOTIFY_TEXTVIEWSELECTIONCHANGED;
+ }
+ EENotify aNotify( eNotifyType );
+ pEditEngine->pImpEditEngine->GetNotifyHdl().Call( aNotify );
+ }
+ if(pEditEngine->pImpEditEngine->IsFormatted())
+ {
+ EENotify aNotify(EE_NOTIFY_PROCESSNOTIFICATIONS);
+ pEditEngine->pImpEditEngine->GetNotifyHdl().Call(aNotify);
+ }
+}
+
+/// Translate absolute <-> relative twips: LOK wants absolute coordinates as output and gives absolute coordinates as input.
+static void lcl_translateTwips(const OutputDevice& rParent, OutputDevice& rChild)
+{
+ // Don't translate if we already have a non-zero origin.
+ // This prevents multiple translate calls that negate
+ // one another.
+ const Point aOrigin = rChild.GetMapMode().GetOrigin();
+ if (aOrigin.getX() != 0 || aOrigin.getY() != 0)
+ return;
+
+ // Set map mode, so that callback payloads will contain absolute coordinates instead of relative ones.
+ Point aOffset(rChild.GetOutOffXPixel() - rParent.GetOutOffXPixel(), rChild.GetOutOffYPixel() - rParent.GetOutOffYPixel());
+ if (!rChild.IsMapModeEnabled())
+ {
+ MapMode aMapMode(rChild.GetMapMode());
+ aMapMode.SetMapUnit(MapUnit::MapTwip);
+ aMapMode.SetScaleX(rParent.GetMapMode().GetScaleX());
+ aMapMode.SetScaleY(rParent.GetMapMode().GetScaleY());
+ rChild.SetMapMode(aMapMode);
+ rChild.EnableMapMode();
+ }
+ aOffset = rChild.PixelToLogic(aOffset);
+ MapMode aMapMode(rChild.GetMapMode());
+ aMapMode.SetOrigin(aOffset);
+ aMapMode.SetMapUnit(rParent.GetMapMode().GetMapUnit());
+ rChild.SetMapMode(aMapMode);
+ rChild.EnableMapMode(false);
+}
+
+// EditView never had a central/secure place to react on SelectionChange since
+// Selection was changed in many places, often by not using SetEditSelection()
+// but (mis)using GetEditSelection() and manipulating this non-const return
+// value. Sorted this out now to have such a place, this is needed for safely
+// change/update the Selection visualization for enhanced mechanisms
+void ImpEditView::SelectionChanged()
+{
+ if (EditViewCallbacks* pCallbacks = getEditViewCallbacks())
+ {
+ // use callback to tell about change in selection visualisation
+ pCallbacks->EditViewSelectionChange();
+ }
+}
+
+// This function is also called when a text's font || size is changed. Because its highlight rectangle must be updated.
+void ImpEditView::lokSelectionCallback(const std::optional<tools::PolyPolygon> &pPolyPoly, bool bStartHandleVisible, bool bEndHandleVisible) {
+ VclPtr<vcl::Window> pParent = pOutWin->GetParentWithLOKNotifier();
+ vcl::Region aRegion( *pPolyPoly );
+
+ if (pParent && pParent->GetLOKWindowId() != 0)
+ {
+ const tools::Long nX = pOutWin->GetOutOffXPixel() - pParent->GetOutOffXPixel();
+ const tools::Long nY = pOutWin->GetOutOffYPixel() - pParent->GetOutOffYPixel();
+
+ std::vector<tools::Rectangle> aRectangles;
+ aRegion.GetRegionRectangles(aRectangles);
+
+ std::vector<OString> v;
+ for (tools::Rectangle & rRectangle : aRectangles)
+ {
+ rRectangle = pOutWin->LogicToPixel(rRectangle);
+ rRectangle.Move(nX, nY);
+ v.emplace_back(rRectangle.toString().getStr());
+ }
+ OString sRectangle = comphelper::string::join("; ", v);
+
+ const vcl::ILibreOfficeKitNotifier* pNotifier = pParent->GetLOKNotifier();
+ std::vector<vcl::LOKPayloadItem> aItems;
+ aItems.emplace_back("rectangles", sRectangle);
+ aItems.emplace_back("startHandleVisible", OString::boolean(bStartHandleVisible));
+ aItems.emplace_back("endHandleVisible", OString::boolean(bEndHandleVisible));
+ pNotifier->notifyWindow(pParent->GetLOKWindowId(), "text_selection", aItems);
+ }
+ else if (mpViewShell)
+ {
+ pOutWin->GetOutDev()->Push(vcl::PushFlags::MAPMODE);
+ if (pOutWin->GetMapMode().GetMapUnit() == MapUnit::MapTwip)
+ {
+ // Find the parent that is not right
+ // on top of us to use its offset.
+ vcl::Window* parent = pOutWin->GetParent();
+ while (parent &&
+ parent->GetOutOffXPixel() == pOutWin->GetOutOffXPixel() &&
+ parent->GetOutOffYPixel() == pOutWin->GetOutOffYPixel())
+ {
+ parent = parent->GetParent();
+ }
+
+ if (parent)
+ {
+ lcl_translateTwips(*parent->GetOutDev(), *pOutWin->GetOutDev());
+ }
+ }
+
+ bool bMm100ToTwip = !mpLOKSpecialPositioning &&
+ (pOutWin->GetMapMode().GetMapUnit() == MapUnit::Map100thMM);
+
+ Point aOrigin;
+ if (pOutWin->GetMapMode().GetMapUnit() == MapUnit::MapTwip)
+ // Writer comments: they use editeng, but are separate widgets.
+ aOrigin = pOutWin->GetMapMode().GetOrigin();
+
+ OString sRectangle;
+ OString sRefPoint;
+ if (mpLOKSpecialPositioning)
+ sRefPoint = mpLOKSpecialPositioning->GetRefPoint().toString();
+
+ std::vector<tools::Rectangle> aRectangles;
+ aRegion.GetRegionRectangles(aRectangles);
+
+ if (!aRectangles.empty())
+ {
+ if (pOutWin->IsChart())
+ {
+ const vcl::Window* pViewShellWindow = mpViewShell->GetEditWindowForActiveOLEObj();
+ if (pViewShellWindow && pViewShellWindow->IsAncestorOf(*pOutWin))
+ {
+ Point aOffsetPx = pOutWin->GetOffsetPixelFrom(*pViewShellWindow);
+ Point aLogicOffset = pOutWin->PixelToLogic(aOffsetPx);
+ for (tools::Rectangle& rRect : aRectangles)
+ rRect.Move(aLogicOffset.getX(), aLogicOffset.getY());
+ }
+ }
+
+ std::vector<OString> v;
+ for (tools::Rectangle & rRectangle : aRectangles)
+ {
+ if (bMm100ToTwip)
+ {
+ rRectangle = o3tl::convert(rRectangle, o3tl::Length::mm100, o3tl::Length::twip);
+ }
+ rRectangle.Move(aOrigin.getX(), aOrigin.getY());
+ v.emplace_back(rRectangle.toString().getStr());
+ }
+ sRectangle = comphelper::string::join("; ", v);
+
+ if (mpLOKSpecialPositioning && !sRectangle.isEmpty())
+ sRectangle += ":: " + sRefPoint;
+
+ tools::Rectangle& rStart = aRectangles.front();
+ tools::Rectangle aStart(rStart.Left(), rStart.Top(), rStart.Left() + 1, rStart.Bottom());
+
+ OString aPayload = aStart.toString();
+ if (mpLOKSpecialPositioning)
+ aPayload += ":: " + sRefPoint;
+
+ mpViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION_START, aPayload);
+
+ tools::Rectangle& rEnd = aRectangles.back();
+ tools::Rectangle aEnd(rEnd.Right() - 1, rEnd.Top(), rEnd.Right(), rEnd.Bottom());
+
+ aPayload = aEnd.toString();
+ if (mpLOKSpecialPositioning)
+ aPayload += ":: " + sRefPoint;
+
+ mpViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION_END, aPayload);
+ }
+
+ if (mpOtherShell)
+ {
+ // Another shell wants to know about our existing selection.
+ if (mpViewShell != mpOtherShell)
+ mpViewShell->NotifyOtherView(mpOtherShell, LOK_CALLBACK_TEXT_VIEW_SELECTION, "selection"_ostr, sRectangle);
+ }
+ else
+ {
+ mpViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, sRectangle);
+ mpViewShell->NotifyOtherViews(LOK_CALLBACK_TEXT_VIEW_SELECTION, "selection"_ostr, sRectangle);
+ }
+
+ pOutWin->GetOutDev()->Pop();
+ }
+}
+
+// renamed from DrawSelection to DrawSelectionXOR to better reflect what this
+// method was used for: Paint Selection in XOR, change it and again paint it in XOR.
+// This can be safely assumed due to the EditView only being capable of painting the
+// selection in XOR until today.
+// This also means that all places calling DrawSelectionXOR are thoroughly weighted
+// and chosen to make this fragile XOR-paint water-proof and thus contain some
+// information in this sense.
+// Someone thankfully expanded it to collect the SelectionRectangles when called with
+// the Region*, see GetSelectionRectangles below.
+void ImpEditView::DrawSelectionXOR( EditSelection aTmpSel, vcl::Region* pRegion, OutputDevice* pTargetDevice )
+{
+ if (getEditViewCallbacks() && !pRegion && !comphelper::LibreOfficeKit::isActive())
+ {
+ // we are done, do *not* visualize self
+ // CAUTION: do not use when comphelper::LibreOfficeKit::isActive()
+ // due to event stuff triggered below. That *should* probably be moved
+ // to SelectionChanged() which exists now, but I do not know enough about
+ // that stuff to do it
+ return;
+ }
+
+ if ( eSelectionMode == EESelectionMode::Hidden )
+ return;
+
+ // It must be ensured before rendering the selection, that the contents of
+ // the window is completely valid! Must be here so that in any case if
+ // empty, then later on two-Paint Events! Must be done even before the
+ // query from bUpdate, if after Invalidate paints still in the queue,
+ // but someone switches the update mode!
+
+ // pRegion: When not NULL, then only calculate Region.
+
+ OutputDevice& rTarget = pTargetDevice ? *pTargetDevice : GetOutputDevice();
+ bool bClipRegion = rTarget.IsClipRegion();
+ vcl::Region aOldRegion = rTarget.GetClipRegion();
+
+ std::optional<tools::PolyPolygon> pPolyPoly;
+
+ if ( !pRegion && !comphelper::LibreOfficeKit::isActive())
+ {
+ if ( !pEditEngine->pImpEditEngine->IsUpdateLayout() )
+ return;
+ if ( pEditEngine->pImpEditEngine->IsInUndo() )
+ return;
+
+ if ( !aTmpSel.HasRange() )
+ return;
+
+ // aTmpOutArea: if OutputArea > Paper width and
+ // Text > Paper width ( over large fields )
+ tools::Rectangle aTmpOutArea( aOutArea );
+ if ( aTmpOutArea.GetWidth() > pEditEngine->pImpEditEngine->GetPaperSize().Width() )
+ aTmpOutArea.SetRight( aTmpOutArea.Left() + pEditEngine->pImpEditEngine->GetPaperSize().Width() );
+ rTarget.IntersectClipRegion( aTmpOutArea );
+
+ if (pOutWin && pOutWin->GetCursor())
+ pOutWin->GetCursor()->Hide();
+ }
+
+ if (comphelper::LibreOfficeKit::isActive() || pRegion)
+ pPolyPoly = tools::PolyPolygon();
+
+ DBG_ASSERT( !pEditEngine->IsIdleFormatterActive(), "DrawSelectionXOR: Not formatted!" );
+ aTmpSel.Adjust( pEditEngine->GetEditDoc() );
+
+ ContentNode* pStartNode = aTmpSel.Min().GetNode();
+ ContentNode* pEndNode = aTmpSel.Max().GetNode();
+ const sal_Int32 nStartPara = pEditEngine->GetEditDoc().GetPos(pStartNode);
+ const sal_Int32 nEndPara = pEditEngine->GetEditDoc().GetPos(pEndNode);
+ if (nStartPara == EE_PARA_NOT_FOUND || nEndPara == EE_PARA_NOT_FOUND)
+ return;
+
+ bool bStartHandleVisible = false;
+ bool bEndHandleVisible = false;
+ bool bLOKCalcRTL = mpLOKSpecialPositioning &&
+ (mpLOKSpecialPositioning->IsLayoutRTL() || pEditEngine->IsRightToLeft(nStartPara));
+
+ auto DrawHighlight = [&, nStartLine = sal_Int32(0), nEndLine = sal_Int32(0)](
+ const ImpEditEngine::LineAreaInfo& rInfo) mutable {
+ if (!rInfo.pLine) // Begin of ParaPortion
+ {
+ if (rInfo.nPortion < nStartPara)
+ return ImpEditEngine::CallbackResult::SkipThisPortion;
+ if (rInfo.nPortion > nEndPara)
+ return ImpEditEngine::CallbackResult::Stop;
+ DBG_ASSERT(!rInfo.rPortion.IsInvalid(), "Portion in Selection not formatted!");
+ if (rInfo.rPortion.IsInvalid())
+ return ImpEditEngine::CallbackResult::SkipThisPortion;
+
+ if (rInfo.nPortion == nStartPara)
+ nStartLine = rInfo.rPortion.GetLines().FindLine(aTmpSel.Min().GetIndex(), false);
+ else
+ nStartLine = 0;
+
+ if (rInfo.nPortion == nEndPara)
+ nEndLine = rInfo.rPortion.GetLines().FindLine(aTmpSel.Max().GetIndex(), true);
+ else
+ nEndLine = rInfo.rPortion.GetLines().Count() - 1;
+ }
+ else // This is a correct ParaPortion
+ {
+ if (rInfo.nLine < nStartLine)
+ return ImpEditEngine::CallbackResult::Continue;
+ if (rInfo.nLine > nEndLine)
+ return ImpEditEngine::CallbackResult::SkipThisPortion;
+
+ bool bPartOfLine = false;
+ sal_Int32 nStartIndex = rInfo.pLine->GetStart();
+ sal_Int32 nEndIndex = rInfo.pLine->GetEnd();
+ if ((rInfo.nPortion == nStartPara) && (rInfo.nLine == nStartLine)
+ && (nStartIndex != aTmpSel.Min().GetIndex()))
+ {
+ nStartIndex = aTmpSel.Min().GetIndex();
+ bPartOfLine = true;
+ }
+ if ((rInfo.nPortion == nEndPara) && (rInfo.nLine == nEndLine)
+ && (nEndIndex != aTmpSel.Max().GetIndex()))
+ {
+ nEndIndex = aTmpSel.Max().GetIndex();
+ bPartOfLine = true;
+ }
+
+ // Can happen if at the beginning of a wrapped line.
+ if (nEndIndex < nStartIndex)
+ nEndIndex = nStartIndex;
+
+ tools::Rectangle aTmpRect(pEditEngine->pImpEditEngine->GetEditCursor(
+ &rInfo.rPortion, rInfo.pLine, nStartIndex, GetCursorFlags::NONE));
+ const Size aLineOffset = pEditEngine->pImpEditEngine->getTopLeftDocOffset(rInfo.aArea);
+ aTmpRect.Move(0, aLineOffset.Height());
+
+ // Only paint if in the visible range ...
+ if (aTmpRect.Top() > GetVisDocBottom())
+ return ImpEditEngine::CallbackResult::Continue;
+
+ if (aTmpRect.Bottom() < GetVisDocTop())
+ return ImpEditEngine::CallbackResult::Continue;
+
+ if ((rInfo.nPortion == nStartPara) && (rInfo.nLine == nStartLine))
+ bStartHandleVisible = true;
+ if ((rInfo.nPortion == nEndPara) && (rInfo.nLine == nEndLine))
+ bEndHandleVisible = true;
+
+ // Now that we have Bidi, the first/last index doesn't have to be the 'most outside' position
+ if (!bPartOfLine)
+ {
+ Range aLineXPosStartEnd
+ = pEditEngine->GetLineXPosStartEnd(&rInfo.rPortion, rInfo.pLine);
+ aTmpRect.SetLeft(aLineXPosStartEnd.Min());
+ aTmpRect.SetRight(aLineXPosStartEnd.Max());
+ aTmpRect.Move(aLineOffset.Width(), 0);
+ ImplDrawHighlightRect(rTarget, aTmpRect.TopLeft(), aTmpRect.BottomRight(),
+ pPolyPoly ? &*pPolyPoly : nullptr, bLOKCalcRTL);
+ }
+ else
+ {
+ sal_Int32 nTmpStartIndex = nStartIndex;
+ sal_Int32 nWritingDirStart, nTmpEndIndex;
+
+ while (nTmpStartIndex < nEndIndex)
+ {
+ pEditEngine->pImpEditEngine->GetRightToLeft(rInfo.nPortion, nTmpStartIndex + 1,
+ &nWritingDirStart, &nTmpEndIndex);
+ if (nTmpEndIndex > nEndIndex)
+ nTmpEndIndex = nEndIndex;
+
+ DBG_ASSERT(nTmpEndIndex > nTmpStartIndex, "DrawSelectionXOR, Start >= End?");
+
+ tools::Long nX1
+ = pEditEngine->GetXPos(&rInfo.rPortion, rInfo.pLine, nTmpStartIndex, true);
+ tools::Long nX2
+ = pEditEngine->GetXPos(&rInfo.rPortion, rInfo.pLine, nTmpEndIndex);
+
+ aTmpRect.SetLeft(std::min(nX1, nX2));
+ aTmpRect.SetRight(std::max(nX1, nX2));
+ aTmpRect.Move(aLineOffset.Width(), 0);
+
+ ImplDrawHighlightRect(rTarget, aTmpRect.TopLeft(), aTmpRect.BottomRight(),
+ pPolyPoly ? &*pPolyPoly : nullptr, bLOKCalcRTL);
+ nTmpStartIndex = nTmpEndIndex;
+ }
+ }
+ }
+ return ImpEditEngine::CallbackResult::Continue;
+ };
+ pEditEngine->pImpEditEngine->IterateLineAreas(DrawHighlight, ImpEditEngine::IterFlag::none);
+
+ if (comphelper::LibreOfficeKit::isActive() && mpViewShell && pOutWin)
+ lokSelectionCallback(pPolyPoly, bStartHandleVisible, bEndHandleVisible);
+
+ if (pRegion || comphelper::LibreOfficeKit::isActive())
+ {
+ if (pRegion)
+ *pRegion = vcl::Region( *pPolyPoly );
+ pPolyPoly.reset();
+ }
+ else
+ {
+ if (pOutWin && pOutWin->GetCursor())
+ pOutWin->GetCursor()->Show();
+
+ if (bClipRegion)
+ rTarget.SetClipRegion(aOldRegion);
+ else
+ rTarget.SetClipRegion();
+ }
+}
+
+void ImpEditView::GetSelectionRectangles(EditSelection aTmpSel, std::vector<tools::Rectangle>& rLogicRects)
+{
+ vcl::Region aRegion;
+ DrawSelectionXOR(aTmpSel, &aRegion);
+ aRegion.GetRegionRectangles(rLogicRects);
+}
+
+void ImpEditView::ImplDrawHighlightRect( OutputDevice& rTarget, const Point& rDocPosTopLeft, const Point& rDocPosBottomRight, tools::PolyPolygon* pPolyPoly, bool bLOKCalcRTL )
+{
+ if ( rDocPosTopLeft.X() == rDocPosBottomRight.X() )
+ return;
+
+ if (mpLOKSpecialPositioning && pPolyPoly)
+ {
+ MapUnit eDevUnit = rTarget.GetMapMode().GetMapUnit();
+ tools::Rectangle aSelRect(rDocPosTopLeft, rDocPosBottomRight);
+ aSelRect = GetWindowPos(aSelRect);
+ Point aRefPointLogical = GetOutputArea().TopLeft();
+ // Get the relative coordinates w.r.t refpoint in display units.
+ aSelRect.Move(-aRefPointLogical.X(), -aRefPointLogical.Y());
+ if (bLOKCalcRTL)
+ {
+ tools::Long nMirrorW = GetOutputArea().GetWidth();
+ tools::Long nLeft = aSelRect.Left(), nRight = aSelRect.Right();
+ aSelRect.SetLeft(nMirrorW - nRight);
+ aSelRect.SetRight(nMirrorW - nLeft);
+ }
+ // Convert from display unit to twips.
+ aSelRect = OutputDevice::LogicToLogic(aSelRect, MapMode(eDevUnit), MapMode(MapUnit::MapTwip));
+
+ tools::Polygon aTmpPoly(4);
+ aTmpPoly[0] = aSelRect.TopLeft();
+ aTmpPoly[1] = aSelRect.TopRight();
+ aTmpPoly[2] = aSelRect.BottomRight();
+ aTmpPoly[3] = aSelRect.BottomLeft();
+ pPolyPoly->Insert(aTmpPoly);
+ return;
+ }
+
+ bool bPixelMode = rTarget.GetMapMode().GetMapUnit() == MapUnit::MapPixel;
+
+ Point aPnt1( GetWindowPos( rDocPosTopLeft ) );
+ Point aPnt2( GetWindowPos( rDocPosBottomRight ) );
+
+ if ( !IsVertical() )
+ {
+ lcl_AlignToPixel(aPnt1, rTarget, +1, 0);
+ lcl_AlignToPixel(aPnt2, rTarget, 0, (bPixelMode ? 0 : -1));
+ }
+ else
+ {
+ lcl_AlignToPixel(aPnt1, rTarget, 0, +1 );
+ lcl_AlignToPixel(aPnt2, rTarget, (bPixelMode ? 0 : +1), 0);
+ }
+
+ tools::Rectangle aRect( aPnt1, aPnt2 );
+ if ( pPolyPoly )
+ {
+ tools::Polygon aTmpPoly( 4 );
+ aTmpPoly[0] = aRect.TopLeft();
+ aTmpPoly[1] = aRect.TopRight();
+ aTmpPoly[2] = aRect.BottomRight();
+ aTmpPoly[3] = aRect.BottomLeft();
+ pPolyPoly->Insert( aTmpPoly );
+ }
+ else
+ {
+ vcl::Window* pWindow = rTarget.GetOwnerWindow();
+
+ if (pWindow)
+ {
+ pWindow->GetOutDev()->Invert( aRect );
+ }
+ else
+ {
+ rTarget.Push(vcl::PushFlags::LINECOLOR|vcl::PushFlags::FILLCOLOR|vcl::PushFlags::RASTEROP);
+ rTarget.SetLineColor();
+ rTarget.SetFillColor(COL_BLACK);
+ rTarget.SetRasterOp(RasterOp::Invert);
+ rTarget.DrawRect(aRect);
+ rTarget.Pop();
+ }
+ }
+}
+
+
+bool ImpEditView::IsVertical() const
+{
+ return pEditEngine->pImpEditEngine->IsEffectivelyVertical();
+}
+
+bool ImpEditView::IsTopToBottom() const
+{
+ return pEditEngine->pImpEditEngine->IsTopToBottom();
+}
+
+tools::Rectangle ImpEditView::GetVisDocArea() const
+{
+ return tools::Rectangle( GetVisDocLeft(), GetVisDocTop(), GetVisDocRight(), GetVisDocBottom() );
+}
+
+Point ImpEditView::GetDocPos( const Point& rWindowPos ) const
+{
+ // Window Position => Position Document
+ Point aPoint;
+
+ if ( !pEditEngine->pImpEditEngine->IsEffectivelyVertical() )
+ {
+ aPoint.setX( rWindowPos.X() - aOutArea.Left() + GetVisDocLeft() );
+ aPoint.setY( rWindowPos.Y() - aOutArea.Top() + GetVisDocTop() );
+ }
+ else
+ {
+ if (pEditEngine->pImpEditEngine->IsTopToBottom())
+ {
+ aPoint.setX( rWindowPos.Y() - aOutArea.Top() + GetVisDocLeft() );
+ aPoint.setY( aOutArea.Right() - rWindowPos.X() + GetVisDocTop() );
+ }
+ else
+ {
+ aPoint.setX( aOutArea.Bottom() - rWindowPos.Y() + GetVisDocLeft() );
+ aPoint.setY( rWindowPos.X() - aOutArea.Left() + GetVisDocTop() );
+ }
+ }
+
+ return aPoint;
+}
+
+Point ImpEditView::GetWindowPos( const Point& rDocPos ) const
+{
+ // Document position => window position
+ Point aPoint;
+
+ if ( !pEditEngine->pImpEditEngine->IsEffectivelyVertical() )
+ {
+ aPoint.setX( rDocPos.X() + aOutArea.Left() - GetVisDocLeft() );
+ aPoint.setY( rDocPos.Y() + aOutArea.Top() - GetVisDocTop() );
+ }
+ else
+ {
+ if (pEditEngine->pImpEditEngine->IsTopToBottom())
+ {
+ aPoint.setX( aOutArea.Right() - rDocPos.Y() + GetVisDocTop() );
+ aPoint.setY( rDocPos.X() + aOutArea.Top() - GetVisDocLeft() );
+ }
+ else
+ {
+ aPoint.setX( aOutArea.Left() + rDocPos.Y() - GetVisDocTop() );
+ aPoint.setY( aOutArea.Bottom() - rDocPos.X() + GetVisDocLeft() );
+ }
+ }
+
+ return aPoint;
+}
+
+tools::Rectangle ImpEditView::GetWindowPos( const tools::Rectangle& rDocRect ) const
+{
+ // Document position => window position
+ Point aPos( GetWindowPos( rDocRect.TopLeft() ) );
+ Size aSz = rDocRect.GetSize();
+ tools::Rectangle aRect;
+ if ( !pEditEngine->pImpEditEngine->IsEffectivelyVertical() )
+ {
+ aRect = tools::Rectangle( aPos, aSz );
+ }
+ else
+ {
+ Point aNewPos( aPos.X()-aSz.Height(), aPos.Y() );
+ // coverity[swapped_arguments : FALSE] - this is in the correct order
+ aRect = tools::Rectangle( aNewPos, Size( aSz.Height(), aSz.Width() ) );
+ }
+ return aRect;
+}
+
+void ImpEditView::SetSelectionMode( EESelectionMode eNewMode )
+{
+ if ( eSelectionMode != eNewMode )
+ {
+ DrawSelectionXOR();
+ eSelectionMode = eNewMode;
+ DrawSelectionXOR(); // redraw
+ }
+}
+
+OutputDevice& ImpEditView::GetOutputDevice() const
+{
+ if (EditViewCallbacks* pCallbacks = getEditViewCallbacks())
+ return pCallbacks->EditViewOutputDevice();
+ return *pOutWin->GetOutDev();
+}
+
+weld::Widget* ImpEditView::GetPopupParent(tools::Rectangle& rRect) const
+{
+ if (EditViewCallbacks* pCallbacks = getEditViewCallbacks())
+ {
+ weld::Widget* pParent = pCallbacks->EditViewPopupParent();
+ if (pParent)
+ return pParent;
+ }
+ return weld::GetPopupParent(*pOutWin, rRect);
+}
+
+void ImpEditView::SetOutputArea( const tools::Rectangle& rRect )
+{
+ const OutputDevice& rOutDev = GetOutputDevice();
+ // should be better be aligned on pixels!
+ tools::Rectangle aNewRect(rOutDev.LogicToPixel(rRect));
+ aNewRect = rOutDev.PixelToLogic(aNewRect);
+ aOutArea = aNewRect;
+ if ( !aOutArea.IsWidthEmpty() && aOutArea.Right() < aOutArea.Left() )
+ aOutArea.SetRight( aOutArea.Left() );
+ if ( !aOutArea.IsHeightEmpty() && aOutArea.Bottom() < aOutArea.Top() )
+ aOutArea.SetBottom( aOutArea.Top() );
+
+ SetScrollDiffX( static_cast<sal_uInt16>(aOutArea.GetWidth()) * 2 / 10 );
+}
+
+namespace {
+
+tools::Rectangle lcl_negateRectX(const tools::Rectangle& rRect)
+{
+ return tools::Rectangle(-rRect.Right(), rRect.Top(), -rRect.Left(), rRect.Bottom());
+}
+
+}
+
+void ImpEditView::InvalidateAtWindow(const tools::Rectangle& rRect)
+{
+ if (EditViewCallbacks* pCallbacks = getEditViewCallbacks())
+ {
+ // do not invalidate and trigger a global repaint, but forward
+ // the need for change to the applied EditViewCallback, can e.g.
+ // be used to visualize the active edit text in an OverlayObject
+ pCallbacks->EditViewInvalidate(mbNegativeX ? lcl_negateRectX(rRect) : rRect);
+ }
+ else
+ {
+ // classic mode: invalidate and trigger full repaint
+ // of the changed area
+ GetWindow()->Invalidate(mbNegativeX ? lcl_negateRectX(rRect) : rRect);
+ }
+}
+
+void ImpEditView::ResetOutputArea( const tools::Rectangle& rRect )
+{
+ // remember old out area
+ const tools::Rectangle aOldArea(aOutArea);
+
+ // apply new one
+ SetOutputArea(rRect);
+
+ // invalidate surrounding areas if update is true
+ if(aOldArea.IsEmpty() || !pEditEngine->pImpEditEngine->IsUpdateLayout())
+ return;
+
+ // #i119885# use grown area if needed; do when getting bigger OR smaller
+ const sal_Int32 nMore(DoInvalidateMore() ? GetOutputDevice().PixelToLogic(Size(nInvMore, 0)).Width() : 0);
+
+ if(aOldArea.Left() > aOutArea.Left())
+ {
+ const tools::Rectangle aRect(aOutArea.Left() - nMore, aOldArea.Top() - nMore, aOldArea.Left(), aOldArea.Bottom() + nMore);
+ InvalidateAtWindow(aRect);
+ }
+ else if(aOldArea.Left() < aOutArea.Left())
+ {
+ const tools::Rectangle aRect(aOldArea.Left() - nMore, aOldArea.Top() - nMore, aOutArea.Left(), aOldArea.Bottom() + nMore);
+ InvalidateAtWindow(aRect);
+ }
+
+ if(aOldArea.Right() > aOutArea.Right())
+ {
+ const tools::Rectangle aRect(aOutArea.Right(), aOldArea.Top() - nMore, aOldArea.Right() + nMore, aOldArea.Bottom() + nMore);
+ InvalidateAtWindow(aRect);
+ }
+ else if(aOldArea.Right() < aOutArea.Right())
+ {
+ const tools::Rectangle aRect(aOldArea.Right(), aOldArea.Top() - nMore, aOutArea.Right() + nMore, aOldArea.Bottom() + nMore);
+ InvalidateAtWindow(aRect);
+ }
+
+ if(aOldArea.Top() > aOutArea.Top())
+ {
+ const tools::Rectangle aRect(aOldArea.Left() - nMore, aOutArea.Top() - nMore, aOldArea.Right() + nMore, aOldArea.Top());
+ InvalidateAtWindow(aRect);
+ }
+ else if(aOldArea.Top() < aOutArea.Top())
+ {
+ const tools::Rectangle aRect(aOldArea.Left() - nMore, aOldArea.Top() - nMore, aOldArea.Right() + nMore, aOutArea.Top());
+ InvalidateAtWindow(aRect);
+ }
+
+ if(aOldArea.Bottom() > aOutArea.Bottom())
+ {
+ const tools::Rectangle aRect(aOldArea.Left() - nMore, aOutArea.Bottom(), aOldArea.Right() + nMore, aOldArea.Bottom() + nMore);
+ InvalidateAtWindow(aRect);
+ }
+ else if(aOldArea.Bottom() < aOutArea.Bottom())
+ {
+ const tools::Rectangle aRect(aOldArea.Left() - nMore, aOldArea.Bottom(), aOldArea.Right() + nMore, aOutArea.Bottom() + nMore);
+ InvalidateAtWindow(aRect);
+ }
+}
+
+void ImpEditView::RecalcOutputArea()
+{
+ Point aNewTopLeft( aOutArea.TopLeft() );
+ Size aNewSz( aOutArea.GetSize() );
+
+ // X:
+ if ( DoAutoWidth() )
+ {
+ if ( pEditEngine->pImpEditEngine->GetStatus().AutoPageWidth() )
+ aNewSz.setWidth( pEditEngine->pImpEditEngine->GetPaperSize().Width() );
+ switch ( eAnchorMode )
+ {
+ case EEAnchorMode::TopLeft:
+ case EEAnchorMode::VCenterLeft:
+ case EEAnchorMode::BottomLeft:
+ {
+ aNewTopLeft.setX( aAnchorPoint.X() );
+ }
+ break;
+ case EEAnchorMode::TopHCenter:
+ case EEAnchorMode::VCenterHCenter:
+ case EEAnchorMode::BottomHCenter:
+ {
+ aNewTopLeft.setX( aAnchorPoint.X() - aNewSz.Width() / 2 );
+ }
+ break;
+ case EEAnchorMode::TopRight:
+ case EEAnchorMode::VCenterRight:
+ case EEAnchorMode::BottomRight:
+ {
+ aNewTopLeft.setX( aAnchorPoint.X() - aNewSz.Width() - 1 );
+ }
+ break;
+ }
+ }
+
+ // Y:
+ if ( DoAutoHeight() )
+ {
+ if ( pEditEngine->pImpEditEngine->GetStatus().AutoPageHeight() )
+ aNewSz.setHeight( pEditEngine->pImpEditEngine->GetPaperSize().Height() );
+ switch ( eAnchorMode )
+ {
+ case EEAnchorMode::TopLeft:
+ case EEAnchorMode::TopHCenter:
+ case EEAnchorMode::TopRight:
+ {
+ aNewTopLeft.setY( aAnchorPoint.Y() );
+ }
+ break;
+ case EEAnchorMode::VCenterLeft:
+ case EEAnchorMode::VCenterHCenter:
+ case EEAnchorMode::VCenterRight:
+ {
+ aNewTopLeft.setY( aAnchorPoint.Y() - aNewSz.Height() / 2 );
+ }
+ break;
+ case EEAnchorMode::BottomLeft:
+ case EEAnchorMode::BottomHCenter:
+ case EEAnchorMode::BottomRight:
+ {
+ aNewTopLeft.setY( aAnchorPoint.Y() - aNewSz.Height() - 1 );
+ }
+ break;
+ }
+ }
+ ResetOutputArea( tools::Rectangle( aNewTopLeft, aNewSz ) );
+}
+
+void ImpEditView::SetAnchorMode( EEAnchorMode eMode )
+{
+ eAnchorMode = eMode;
+ CalcAnchorPoint();
+}
+
+void ImpEditView::CalcAnchorPoint()
+{
+ // GetHeight() and GetWidth() -1, because rectangle calculation not preferred.
+
+ // X:
+ switch ( eAnchorMode )
+ {
+ case EEAnchorMode::TopLeft:
+ case EEAnchorMode::VCenterLeft:
+ case EEAnchorMode::BottomLeft:
+ {
+ aAnchorPoint.setX( aOutArea.Left() );
+ }
+ break;
+ case EEAnchorMode::TopHCenter:
+ case EEAnchorMode::VCenterHCenter:
+ case EEAnchorMode::BottomHCenter:
+ {
+ aAnchorPoint.setX( aOutArea.Left() + (aOutArea.GetWidth()-1) / 2 );
+ }
+ break;
+ case EEAnchorMode::TopRight:
+ case EEAnchorMode::VCenterRight:
+ case EEAnchorMode::BottomRight:
+ {
+ aAnchorPoint.setX( aOutArea.Right() );
+ }
+ break;
+ }
+
+ // Y:
+ switch ( eAnchorMode )
+ {
+ case EEAnchorMode::TopLeft:
+ case EEAnchorMode::TopHCenter:
+ case EEAnchorMode::TopRight:
+ {
+ aAnchorPoint.setY( aOutArea.Top() );
+ }
+ break;
+ case EEAnchorMode::VCenterLeft:
+ case EEAnchorMode::VCenterHCenter:
+ case EEAnchorMode::VCenterRight:
+ {
+ aAnchorPoint.setY( aOutArea.Top() + (aOutArea.GetHeight()-1) / 2 );
+ }
+ break;
+ case EEAnchorMode::BottomLeft:
+ case EEAnchorMode::BottomHCenter:
+ case EEAnchorMode::BottomRight:
+ {
+ aAnchorPoint.setY( aOutArea.Bottom() - 1 );
+ }
+ break;
+ }
+}
+
+namespace
+{
+
+// For building JSON message to be sent to Online
+boost::property_tree::ptree getHyperlinkPropTree(const OUString& sText, const OUString& sLink)
+{
+ boost::property_tree::ptree aTree;
+ aTree.put("text", sText);
+ aTree.put("link", sLink);
+ return aTree;
+}
+
+} // End of anon namespace
+
+tools::Rectangle ImpEditView::ImplGetEditCursor(EditPaM& aPaM, GetCursorFlags nShowCursorFlags, sal_Int32& nTextPortionStart,
+ const ParaPortion* pParaPortion) const
+{
+ tools::Rectangle aEditCursor = pEditEngine->pImpEditEngine->PaMtoEditCursor( aPaM, nShowCursorFlags );
+ if ( !IsInsertMode() && !aEditSelection.HasRange() )
+ {
+ if ( aPaM.GetNode()->Len() && ( aPaM.GetIndex() < aPaM.GetNode()->Len() ) )
+ {
+ // If we are behind a portion, and the next portion has other direction, we must change position...
+ aEditCursor.SetLeft( pEditEngine->pImpEditEngine->PaMtoEditCursor( aPaM, GetCursorFlags::TextOnly|GetCursorFlags::PreferPortionStart ).Left() );
+ aEditCursor.SetRight( aEditCursor.Left() );
+
+ sal_Int32 nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nTextPortionStart, true );
+ const TextPortion& rTextPortion = pParaPortion->GetTextPortions()[nTextPortion];
+ if ( rTextPortion.GetKind() == PortionKind::TAB )
+ {
+ aEditCursor.AdjustRight(rTextPortion.GetSize().Width() );
+ }
+ else
+ {
+ EditPaM aNext = pEditEngine->CursorRight( aPaM );
+ tools::Rectangle aTmpRect = pEditEngine->pImpEditEngine->PaMtoEditCursor( aNext, GetCursorFlags::TextOnly );
+ if ( aTmpRect.Top() != aEditCursor.Top() )
+ aTmpRect = pEditEngine->pImpEditEngine->PaMtoEditCursor( aNext, GetCursorFlags::TextOnly|GetCursorFlags::EndOfLine );
+ aEditCursor.SetRight( aTmpRect.Left() );
+ }
+ }
+ }
+
+ tools::Long nMaxHeight = !IsVertical() ? aOutArea.GetHeight() : aOutArea.GetWidth();
+ if ( aEditCursor.GetHeight() > nMaxHeight )
+ {
+ aEditCursor.SetBottom( aEditCursor.Top() + nMaxHeight - 1 );
+ }
+
+ return aEditCursor;
+}
+
+tools::Rectangle ImpEditView::GetEditCursor() const
+{
+ EditPaM aPaM( aEditSelection.Max() );
+
+ sal_Int32 nTextPortionStart = 0;
+ sal_Int32 nPara = pEditEngine->GetEditDoc().GetPos( aPaM.GetNode() );
+ if (nPara == EE_PARA_NOT_FOUND) // #i94322
+ return tools::Rectangle();
+
+ const ParaPortion* pParaPortion = pEditEngine->GetParaPortions()[nPara];
+
+ GetCursorFlags nShowCursorFlags = nExtraCursorFlags | GetCursorFlags::TextOnly;
+
+ // Use CursorBidiLevel 0/1 in meaning of
+ // 0: prefer portion end, normal mode
+ // 1: prefer portion start
+
+ if ( ( GetCursorBidiLevel() != CURSOR_BIDILEVEL_DONTKNOW ) && GetCursorBidiLevel() )
+ {
+ nShowCursorFlags |= GetCursorFlags::PreferPortionStart;
+ }
+
+ return ImplGetEditCursor(aPaM, nShowCursorFlags, nTextPortionStart, pParaPortion);
+}
+
+void ImpEditView::ShowCursor( bool bGotoCursor, bool bForceVisCursor )
+{
+ // No ShowCursor in an empty View ...
+ if (aOutArea.IsEmpty())
+ return;
+ if ( ( aOutArea.Left() >= aOutArea.Right() ) && ( aOutArea.Top() >= aOutArea.Bottom() ) )
+ return;
+
+ pEditEngine->CheckIdleFormatter();
+ if (!pEditEngine->IsFormatted())
+ pEditEngine->pImpEditEngine->FormatDoc();
+
+ // For some reasons I end up here during the formatting, if the Outliner
+ // is initialized in Paint, because no SetPool();
+ if ( pEditEngine->pImpEditEngine->IsFormatting() )
+ return;
+ if ( !pEditEngine->pImpEditEngine->IsUpdateLayout() )
+ return;
+ if ( pEditEngine->pImpEditEngine->IsInUndo() )
+ return;
+
+ if (pOutWin && pOutWin->GetCursor() != GetCursor())
+ pOutWin->SetCursor(GetCursor());
+
+ EditPaM aPaM( aEditSelection.Max() );
+
+ sal_Int32 nTextPortionStart = 0;
+ sal_Int32 nPara = pEditEngine->GetEditDoc().GetPos( aPaM.GetNode() );
+ if (nPara == EE_PARA_NOT_FOUND) // #i94322
+ return;
+
+ const ParaPortion* pParaPortion = pEditEngine->GetParaPortions()[nPara];
+
+ GetCursorFlags nShowCursorFlags = nExtraCursorFlags | GetCursorFlags::TextOnly;
+
+ // Use CursorBidiLevel 0/1 in meaning of
+ // 0: prefer portion end, normal mode
+ // 1: prefer portion start
+
+ if ( ( GetCursorBidiLevel() != CURSOR_BIDILEVEL_DONTKNOW ) && GetCursorBidiLevel() )
+ {
+ nShowCursorFlags |= GetCursorFlags::PreferPortionStart;
+ }
+
+ tools::Rectangle aEditCursor = ImplGetEditCursor(aPaM, nShowCursorFlags, nTextPortionStart, pParaPortion);
+
+ if ( bGotoCursor ) // && (!pEditEngine->pImpEditEngine->GetStatus().AutoPageSize() ) )
+ {
+ // check if scrolling is necessary...
+ // if scrolling, then update () and Scroll ()!
+ tools::Long nDocDiffX = 0;
+ tools::Long nDocDiffY = 0;
+
+ tools::Rectangle aTmpVisArea( GetVisDocArea() );
+ // aTmpOutArea: if OutputArea > Paper width and
+ // Text > Paper width ( over large fields )
+ tools::Long nMaxTextWidth = !IsVertical() ? pEditEngine->pImpEditEngine->GetPaperSize().Width() : pEditEngine->pImpEditEngine->GetPaperSize().Height();
+ if ( aTmpVisArea.GetWidth() > nMaxTextWidth )
+ aTmpVisArea.SetRight( aTmpVisArea.Left() + nMaxTextWidth );
+
+ if ( aEditCursor.Bottom() > aTmpVisArea.Bottom() )
+ { // Scroll up, here positive
+ nDocDiffY = aEditCursor.Bottom() - aTmpVisArea.Bottom();
+ }
+ else if ( aEditCursor.Top() < aTmpVisArea.Top() )
+ { // Scroll down, here negative
+ nDocDiffY = aEditCursor.Top() - aTmpVisArea.Top();
+ }
+
+ if ( aEditCursor.Right() > aTmpVisArea.Right() )
+ {
+ // Scroll left, positive
+ nDocDiffX = aEditCursor.Right() - aTmpVisArea.Right();
+ // Can it be a little more?
+ if ( aEditCursor.Right() < ( nMaxTextWidth - GetScrollDiffX() ) )
+ nDocDiffX += GetScrollDiffX();
+ else
+ {
+ tools::Long n = nMaxTextWidth - aEditCursor.Right();
+ // If MapMode != RefMapMode then the EditCursor can go beyond
+ // the paper width!
+ nDocDiffX += ( n > 0 ? n : -n );
+ }
+ }
+ else if ( aEditCursor.Left() < aTmpVisArea.Left() )
+ {
+ // Scroll right, negative:
+ nDocDiffX = aEditCursor.Left() - aTmpVisArea.Left();
+ // Can it be a little more?
+ if ( aEditCursor.Left() > ( - static_cast<tools::Long>(GetScrollDiffX()) ) )
+ nDocDiffX -= GetScrollDiffX();
+ else
+ nDocDiffX -= aEditCursor.Left();
+ }
+ if ( aPaM.GetIndex() == 0 ) // Olli needed for the Outliner
+ {
+ // But make sure that the cursor is not leaving visible area
+ // because of this!
+ if ( aEditCursor.Left() < aTmpVisArea.GetWidth() )
+ {
+ nDocDiffX = -aTmpVisArea.Left();
+ }
+ }
+
+ if ( nDocDiffX | nDocDiffY )
+ {
+ tools::Long nDiffX = !IsVertical() ? nDocDiffX : (IsTopToBottom() ? -nDocDiffY : nDocDiffY);
+ tools::Long nDiffY = !IsVertical() ? nDocDiffY : (IsTopToBottom() ? nDocDiffX : -nDocDiffX);
+
+ if ( nDiffX )
+ pEditEngine->GetInternalEditStatus().GetStatusWord() = pEditEngine->GetInternalEditStatus().GetStatusWord() | EditStatusFlags::HSCROLL;
+ if ( nDiffY )
+ pEditEngine->GetInternalEditStatus().GetStatusWord() = pEditEngine->GetInternalEditStatus().GetStatusWord() | EditStatusFlags::VSCROLL;
+ Scroll( -nDiffX, -nDiffY );
+ pEditEngine->pImpEditEngine->DelayedCallStatusHdl();
+ }
+ }
+
+ // Cursor may trim a little ...
+ if ( ( aEditCursor.Bottom() > GetVisDocTop() ) &&
+ ( aEditCursor.Top() < GetVisDocBottom() ) )
+ {
+ if ( aEditCursor.Bottom() > GetVisDocBottom() )
+ aEditCursor.SetBottom( GetVisDocBottom() );
+ if ( aEditCursor.Top() < GetVisDocTop() )
+ aEditCursor.SetTop( GetVisDocTop() );
+ }
+
+ const OutputDevice& rOutDev = GetOutputDevice();
+
+ tools::Long nOnePixel = rOutDev.PixelToLogic( Size( 1, 0 ) ).Width();
+
+ if ( ( aEditCursor.Top() + nOnePixel >= GetVisDocTop() ) &&
+ ( aEditCursor.Bottom() - nOnePixel <= GetVisDocBottom() ) &&
+ ( aEditCursor.Left() + nOnePixel >= GetVisDocLeft() ) &&
+ ( aEditCursor.Right() - nOnePixel <= GetVisDocRight() ) )
+ {
+ tools::Rectangle aCursorRect = GetWindowPos( aEditCursor );
+ GetCursor()->SetPos( aCursorRect.TopLeft() );
+ Size aCursorSz( aCursorRect.GetSize() );
+ // Rectangle is inclusive
+ aCursorSz.AdjustWidth( -1 );
+ aCursorSz.AdjustHeight( -1 );
+ if ( !aCursorSz.Width() || !aCursorSz.Height() )
+ {
+ tools::Long nCursorSz = rOutDev.GetSettings().GetStyleSettings().GetCursorSize();
+ nCursorSz = rOutDev.PixelToLogic( Size( nCursorSz, 0 ) ).Width();
+ if ( !aCursorSz.Width() )
+ aCursorSz.setWidth( nCursorSz );
+ if ( !aCursorSz.Height() )
+ aCursorSz.setHeight( nCursorSz );
+ }
+ // #111036# Let VCL do orientation for cursor, otherwise problem when cursor has direction flag
+ if ( IsVertical() )
+ {
+ Size aOldSz( aCursorSz );
+ aCursorSz.setWidth( aOldSz.Height() );
+ aCursorSz.setHeight( aOldSz.Width() );
+ GetCursor()->SetPos( aCursorRect.TopRight() );
+ GetCursor()->SetOrientation( Degree10(IsTopToBottom() ? 2700 : 900) );
+ }
+ else
+ // #i32593# Reset correct orientation in horizontal layout
+ GetCursor()->SetOrientation();
+
+ GetCursor()->SetSize( aCursorSz );
+
+ if (comphelper::LibreOfficeKit::isActive() && mpViewShell && !mbSuppressLOKMessages)
+ {
+ Point aPos = GetCursor()->GetPos();
+ boost::property_tree::ptree aMessageParams;
+ if (mpLOKSpecialPositioning)
+ {
+ // Sending the absolute (pure) logical coordinates of the cursor to the client is not
+ // enough for it to accurately reconstruct the corresponding tile-twips coordinates of the cursor.
+ // This is because the editeng(doc) positioning is not pixel aligned for each cell involved in the output-area
+ // (it better not be!). A simple solution is to send the coordinates of a point ('refpoint') in the output-area
+ // along with the relative position of the cursor w.r.t this chosen 'refpoint'.
+
+ MapUnit eDevUnit = rOutDev.GetMapMode().GetMapUnit();
+ tools::Rectangle aCursorRectPureLogical(aEditCursor.TopLeft(), GetCursor()->GetSize());
+ // Get rectangle in window-coordinates from editeng(doc) coordinates in hmm.
+ aCursorRectPureLogical = GetWindowPos(aCursorRectPureLogical);
+ Point aRefPointLogical = GetOutputArea().TopLeft();
+ // Get the relative coordinates w.r.t refpoint in display hmm.
+ aCursorRectPureLogical.Move(-aRefPointLogical.X(), -aRefPointLogical.Y());
+ if (pEditEngine->IsRightToLeft(nPara) || mpLOKSpecialPositioning->IsLayoutRTL())
+ {
+ tools::Long nMirrorW = GetOutputArea().GetWidth();
+ tools::Long nLeft = aCursorRectPureLogical.Left(), nRight = aCursorRectPureLogical.Right();
+ aCursorRectPureLogical.SetLeft(nMirrorW - nRight);
+ aCursorRectPureLogical.SetRight(nMirrorW - nLeft);
+ }
+ // Convert to twips.
+ aCursorRectPureLogical = OutputDevice::LogicToLogic(aCursorRectPureLogical, MapMode(eDevUnit), MapMode(MapUnit::MapTwip));
+ // "refpoint" in print twips.
+ const Point aRefPoint = mpLOKSpecialPositioning->GetRefPoint();
+ aMessageParams.put("relrect", aCursorRectPureLogical.toString());
+ aMessageParams.put("refpoint", aRefPoint.toString());
+ }
+
+ if (pOutWin && pOutWin->IsChart())
+ {
+ const vcl::Window* pViewShellWindow = mpViewShell->GetEditWindowForActiveOLEObj();
+ if (pViewShellWindow && pViewShellWindow->IsAncestorOf(*pOutWin))
+ {
+ Point aOffsetPx = pOutWin->GetOffsetPixelFrom(*pViewShellWindow);
+ Point aLogicOffset = pOutWin->PixelToLogic(aOffsetPx);
+ aPos.Move(aLogicOffset.getX(), aLogicOffset.getY());
+ }
+ }
+
+ tools::Rectangle aRect(aPos.getX(), aPos.getY(), aPos.getX() + GetCursor()->GetWidth(), aPos.getY() + GetCursor()->GetHeight());
+
+ // LOK output is always in twips, convert from mm100 if necessary.
+ if (rOutDev.GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
+ {
+ aRect = o3tl::convert(aRect, o3tl::Length::mm100, o3tl::Length::twip);
+ }
+ else if (rOutDev.GetMapMode().GetMapUnit() == MapUnit::MapTwip)
+ {
+ // Writer comments: they use editeng, but are separate widgets.
+ Point aOrigin = rOutDev.GetMapMode().GetOrigin();
+ // Move the rectangle, so that we output absolute twips.
+ aRect.Move(aOrigin.getX(), aOrigin.getY());
+ }
+ // Let the LOK client decide the cursor width.
+ aRect.setWidth(0);
+
+ OString sRect = aRect.toString();
+ aMessageParams.put("rectangle", sRect);
+
+ SfxViewShell* pThisShell = dynamic_cast<SfxViewShell*>(mpViewShell);
+ SfxViewShell* pOtherShell = dynamic_cast<SfxViewShell*>(mpOtherShell);
+ assert(pThisShell);
+
+ if (pOtherShell && pThisShell != pOtherShell)
+ {
+ // Another shell wants to know about our existing cursor.
+ SfxLokHelper::notifyOtherView(pThisShell, pOtherShell,
+ LOK_CALLBACK_INVALIDATE_VIEW_CURSOR, aMessageParams);
+ }
+ else
+ {
+ // is cursor at a misspelled word ?
+ Reference< linguistic2::XSpellChecker1 > xSpeller( pEditEngine->pImpEditEngine->GetSpeller() );
+ bool bIsWrong = xSpeller.is() && IsWrongSpelledWord(aPaM, /*bMarkIfWrong*/ false);
+ EditView* pActiveView = GetEditViewPtr();
+
+ boost::property_tree::ptree aHyperlinkTree;
+ if (pActiveView && URLFieldHelper::IsCursorAtURLField(*pActiveView))
+ {
+ if (const SvxFieldItem* pFld = GetField(aPos, nullptr, nullptr))
+ if (auto pUrlField = dynamic_cast<const SvxURLField*>(pFld->GetField()))
+ aHyperlinkTree = getHyperlinkPropTree(pUrlField->GetRepresentation(), pUrlField->GetURL());
+ }
+ else if (GetEditSelection().HasRange())
+ {
+ if (pActiveView)
+ {
+ const SvxFieldItem* pFieldItem = pActiveView->GetFieldAtSelection();
+ if (pFieldItem)
+ {
+ const SvxFieldData* pField = pFieldItem->GetField();
+ if ( auto pUrlField = dynamic_cast<const SvxURLField*>( pField) )
+ {
+ aHyperlinkTree = getHyperlinkPropTree(pUrlField->GetRepresentation(), pUrlField->GetURL());
+ }
+ }
+ }
+ }
+
+ if (mbBroadcastLOKViewCursor)
+ SfxLokHelper::notifyOtherViews(pThisShell,
+ LOK_CALLBACK_INVALIDATE_VIEW_CURSOR, aMessageParams);
+
+ aMessageParams.put("mispelledWord", bIsWrong ? 1 : 0);
+ aMessageParams.add_child("hyperlink", aHyperlinkTree);
+
+ if (comphelper::LibreOfficeKit::isViewIdForVisCursorInvalidation())
+ SfxLokHelper::notifyOtherView(pThisShell, pThisShell,
+ LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR, aMessageParams);
+ else
+ pThisShell->libreOfficeKitViewCallback(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR,
+ OString(aMessageParams.get<std::string>("rectangle")));
+ }
+ }
+
+ CursorDirection nCursorDir = CursorDirection::NONE;
+ if ( IsInsertMode() && !aEditSelection.HasRange() && ( pEditEngine->pImpEditEngine->HasDifferentRTLLevels( aPaM.GetNode() ) ) )
+ {
+ sal_uInt16 nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nTextPortionStart, bool(nShowCursorFlags & GetCursorFlags::PreferPortionStart) );
+ const TextPortion& rTextPortion = pParaPortion->GetTextPortions()[nTextPortion];
+ if (rTextPortion.IsRightToLeft())
+ nCursorDir = CursorDirection::RTL;
+ else
+ nCursorDir = CursorDirection::LTR;
+
+ }
+ GetCursor()->SetDirection( nCursorDir );
+
+ if ( bForceVisCursor )
+ GetCursor()->Show();
+ {
+ SvxFont aFont;
+ pEditEngine->SeekCursor( aPaM.GetNode(), aPaM.GetIndex()+1, aFont );
+
+ InputContext aInputContext(std::move(aFont), InputContextFlags::Text | InputContextFlags::ExtText);
+ if (EditViewCallbacks* pCallbacks = getEditViewCallbacks())
+ pCallbacks->EditViewInputContext(aInputContext);
+ else if (auto xWindow = GetWindow())
+ xWindow->SetInputContext(aInputContext);
+ }
+ }
+ else
+ {
+ pEditEngine->pImpEditEngine->GetStatus().GetStatusWord() = pEditEngine->pImpEditEngine->GetStatus().GetStatusWord() | EditStatusFlags::CURSOROUT;
+ GetCursor()->Hide();
+ GetCursor()->SetPos( Point( -1, -1 ) );
+ GetCursor()->SetSize( Size( 0, 0 ) );
+ }
+}
+
+// call this so users of EditViewCallbacks can update their scrollbar state
+// so called when we have either scrolled to a new location
+// or the size of document has changed
+void ImpEditView::ScrollStateChange()
+{
+ if (EditViewCallbacks* pCallbacks = getEditViewCallbacks())
+ pCallbacks->EditViewScrollStateChange();
+}
+
+Pair ImpEditView::Scroll( tools::Long ndX, tools::Long ndY, ScrollRangeCheck nRangeCheck )
+{
+ DBG_ASSERT( pEditEngine->pImpEditEngine->IsFormatted(), "Scroll: Not formatted!" );
+ if ( !ndX && !ndY )
+ return Pair( 0, 0 );
+
+ const OutputDevice& rOutDev = GetOutputDevice();
+
+#ifdef DBG_UTIL
+ tools::Rectangle aR( aOutArea );
+ aR = rOutDev.LogicToPixel( aR );
+ aR = rOutDev.PixelToLogic( aR );
+ SAL_WARN_IF( aR != aOutArea, "editeng", "OutArea before Scroll not aligned" );
+#endif
+
+ tools::Rectangle aNewVisArea( GetVisDocArea() );
+
+ // Vertical:
+ if ( !IsVertical() )
+ {
+ aNewVisArea.AdjustTop( -ndY );
+ aNewVisArea.AdjustBottom( -ndY );
+ }
+ else
+ {
+ if( IsTopToBottom() )
+ {
+ aNewVisArea.AdjustTop(ndX );
+ aNewVisArea.AdjustBottom(ndX );
+ }
+ else
+ {
+ aNewVisArea.AdjustTop( -ndX );
+ aNewVisArea.AdjustBottom( -ndX );
+ }
+ }
+ if ( ( nRangeCheck == ScrollRangeCheck::PaperWidthTextSize ) && ( aNewVisArea.Bottom() > static_cast<tools::Long>(pEditEngine->pImpEditEngine->GetTextHeight()) ) )
+ {
+ // GetTextHeight still optimizing!
+ tools::Long nDiff = pEditEngine->pImpEditEngine->GetTextHeight() - aNewVisArea.Bottom(); // negative
+ aNewVisArea.Move( 0, nDiff ); // could end up in the negative area...
+ }
+ if ( aNewVisArea.Top() < 0 )
+ aNewVisArea.Move( 0, -aNewVisArea.Top() );
+
+ // Horizontal:
+ if ( !IsVertical() )
+ {
+ aNewVisArea.AdjustLeft( -ndX );
+ aNewVisArea.AdjustRight( -ndX );
+ }
+ else
+ {
+ if (IsTopToBottom())
+ {
+ aNewVisArea.AdjustLeft( -ndY );
+ aNewVisArea.AdjustRight( -ndY );
+ }
+ else
+ {
+ aNewVisArea.AdjustLeft(ndY );
+ aNewVisArea.AdjustRight(ndY );
+ }
+ }
+ if ( ( nRangeCheck == ScrollRangeCheck::PaperWidthTextSize ) && ( aNewVisArea.Right() > static_cast<tools::Long>(pEditEngine->pImpEditEngine->CalcTextWidth( false )) ) )
+ {
+ tools::Long nDiff = pEditEngine->pImpEditEngine->CalcTextWidth( false ) - aNewVisArea.Right(); // negative
+ aNewVisArea.Move( nDiff, 0 ); // could end up in the negative area...
+ }
+ if ( aNewVisArea.Left() < 0 )
+ aNewVisArea.Move( -aNewVisArea.Left(), 0 );
+
+ // The difference must be alignt on pixel (due to scroll!)
+ tools::Long nDiffX = !IsVertical() ? ( GetVisDocLeft() - aNewVisArea.Left() ) : (IsTopToBottom() ? -( GetVisDocTop() - aNewVisArea.Top() ) : (GetVisDocTop() - aNewVisArea.Top()));
+ tools::Long nDiffY = !IsVertical() ? ( GetVisDocTop() - aNewVisArea.Top() ) : (IsTopToBottom() ? (GetVisDocLeft() - aNewVisArea.Left()) : -(GetVisDocTop() - aNewVisArea.Top()));
+
+ Size aDiffs( nDiffX, nDiffY );
+ aDiffs = rOutDev.LogicToPixel( aDiffs );
+ aDiffs = rOutDev.PixelToLogic( aDiffs );
+
+ tools::Long nRealDiffX = aDiffs.Width();
+ tools::Long nRealDiffY = aDiffs.Height();
+
+
+ if ( nRealDiffX || nRealDiffY )
+ {
+ vcl::Cursor* pCrsr = GetCursor();
+ bool bVisCursor = pCrsr->IsVisible();
+ pCrsr->Hide();
+ if (pOutWin)
+ pOutWin->PaintImmediately();
+ if ( !IsVertical() )
+ aVisDocStartPos.Move( -nRealDiffX, -nRealDiffY );
+ else
+ {
+ if (IsTopToBottom())
+ aVisDocStartPos.Move(-nRealDiffY, nRealDiffX);
+ else
+ aVisDocStartPos.Move(nRealDiffY, -nRealDiffX);
+ }
+ // Move by aligned value does not necessarily result in aligned
+ // rectangle ...
+ aVisDocStartPos = rOutDev.LogicToPixel( aVisDocStartPos );
+ aVisDocStartPos = rOutDev.PixelToLogic( aVisDocStartPos );
+ tools::Rectangle aRect( aOutArea );
+
+ if (pOutWin)
+ {
+ pOutWin->Scroll( nRealDiffX, nRealDiffY, aRect, ScrollFlags::Clip );
+ }
+
+ if (comphelper::LibreOfficeKit::isActive() || getEditViewCallbacks())
+ {
+ // Need to invalidate the window, otherwise no tile will be re-painted.
+ pEditView->Invalidate();
+ }
+
+ if (pOutWin)
+ pOutWin->PaintImmediately();
+ pCrsr->SetPos( pCrsr->GetPos() + Point( nRealDiffX, nRealDiffY ) );
+ if ( bVisCursor )
+ {
+ tools::Rectangle aCursorRect( pCrsr->GetPos(), pCrsr->GetSize() );
+ if ( aOutArea.Contains( aCursorRect ) )
+ pCrsr->Show();
+ }
+
+ if ( pEditEngine->pImpEditEngine->GetNotifyHdl().IsSet() )
+ {
+ EENotify aNotify( EE_NOTIFY_TEXTVIEWSCROLLED );
+ pEditEngine->pImpEditEngine->GetNotifyHdl().Call( aNotify );
+ }
+
+ if (EditViewCallbacks* pCallbacks = getEditViewCallbacks())
+ pCallbacks->EditViewScrollStateChange();
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ DrawSelectionXOR();
+ }
+ }
+
+ return Pair( nRealDiffX, nRealDiffY );
+}
+
+Reference<css::datatransfer::clipboard::XClipboard> ImpEditView::GetClipboard() const
+{
+ if (EditViewCallbacks* pCallbacks = getEditViewCallbacks())
+ return pCallbacks->GetClipboard();
+ if (vcl::Window* pWindow = GetWindow())
+ return pWindow->GetClipboard();
+ SAL_WARN("editeng", "falling back to using GetSystemClipboard");
+ return GetSystemClipboard();
+}
+
+bool ImpEditView::PostKeyEvent( const KeyEvent& rKeyEvent, vcl::Window const * pFrameWin )
+{
+ bool bDone = false;
+
+ KeyFuncType eFunc = rKeyEvent.GetKeyCode().GetFunction();
+ if ( eFunc != KeyFuncType::DONTKNOW )
+ {
+ switch ( eFunc )
+ {
+ case KeyFuncType::CUT:
+ {
+ if ( !bReadOnly )
+ {
+ Reference<css::datatransfer::clipboard::XClipboard> aClipBoard(GetClipboard());
+ CutCopy( aClipBoard, true );
+ bDone = true;
+ }
+ }
+ break;
+ case KeyFuncType::COPY:
+ {
+ Reference<css::datatransfer::clipboard::XClipboard> aClipBoard(GetClipboard());
+ CutCopy( aClipBoard, false );
+ bDone = true;
+ }
+ break;
+ case KeyFuncType::PASTE:
+ {
+ if ( !bReadOnly && IsPasteEnabled() )
+ {
+ pEditEngine->pImpEditEngine->UndoActionStart( EDITUNDO_PASTE );
+ Reference<css::datatransfer::clipboard::XClipboard> aClipBoard(GetClipboard());
+ Paste( aClipBoard, pEditEngine->pImpEditEngine->GetStatus().AllowPasteSpecial() );
+ pEditEngine->pImpEditEngine->UndoActionEnd();
+ bDone = true;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if( !bDone )
+ bDone = pEditEngine->PostKeyEvent( rKeyEvent, GetEditViewPtr(), pFrameWin );
+
+ return bDone;
+}
+
+bool ImpEditView::MouseButtonUp( const MouseEvent& rMouseEvent )
+{
+ nTravelXPos = TRAVEL_X_DONTKNOW;
+ nCursorBidiLevel = CURSOR_BIDILEVEL_DONTKNOW;
+ nExtraCursorFlags = GetCursorFlags::NONE;
+ bClickedInSelection = false;
+
+ if ( rMouseEvent.IsMiddle() && !bReadOnly &&
+ Application::GetSettings().GetMouseSettings().GetMiddleButtonAction() == MouseMiddleButtonAction::PasteSelection )
+ {
+ Reference<css::datatransfer::clipboard::XClipboard> aClipBoard(GetSystemPrimarySelection());
+ Paste( aClipBoard );
+ }
+ else if ( rMouseEvent.IsLeft() && GetEditSelection().HasRange() )
+ {
+ Reference<css::datatransfer::clipboard::XClipboard> aClipBoard(GetSystemPrimarySelection());
+ CutCopy( aClipBoard, false );
+ }
+
+ return pEditEngine->pImpEditEngine->MouseButtonUp( rMouseEvent, GetEditViewPtr() );
+}
+
+void ImpEditView::ReleaseMouse()
+{
+ pEditEngine->pImpEditEngine->ReleaseMouse();
+}
+
+bool ImpEditView::MouseButtonDown( const MouseEvent& rMouseEvent )
+{
+ pEditEngine->CheckIdleFormatter(); // If fast typing and mouse button downs
+ nTravelXPos = TRAVEL_X_DONTKNOW;
+ nExtraCursorFlags = GetCursorFlags::NONE;
+ nCursorBidiLevel = CURSOR_BIDILEVEL_DONTKNOW;
+ bool bPrevUpdateLayout = pEditEngine->pImpEditEngine->SetUpdateLayout(true);
+ bClickedInSelection = IsSelectionAtPoint( rMouseEvent.GetPosPixel() );
+ bool bRet = pEditEngine->pImpEditEngine->MouseButtonDown( rMouseEvent, GetEditViewPtr() );
+ pEditEngine->pImpEditEngine->SetUpdateLayout(bPrevUpdateLayout);
+ return bRet;
+}
+
+bool ImpEditView::MouseMove( const MouseEvent& rMouseEvent )
+{
+ return pEditEngine->pImpEditEngine->MouseMove( rMouseEvent, GetEditViewPtr() );
+}
+
+bool ImpEditView::Command(const CommandEvent& rCEvt)
+{
+ pEditEngine->CheckIdleFormatter(); // If fast typing and mouse button down
+ return pEditEngine->pImpEditEngine->Command(rCEvt, GetEditViewPtr());
+}
+
+
+void ImpEditView::SetInsertMode( bool bInsert )
+{
+ if ( bInsert != IsInsertMode() )
+ {
+ SetFlags( nControl, EVControlBits::OVERWRITE, !bInsert );
+ ShowCursor( DoAutoScroll(), false );
+ }
+}
+
+bool ImpEditView::IsWrongSpelledWord( const EditPaM& rPaM, bool bMarkIfWrong )
+{
+ bool bIsWrong = false;
+ if ( rPaM.GetNode()->GetWrongList() )
+ {
+ EditSelection aSel = pEditEngine->SelectWord( rPaM, css::i18n::WordType::DICTIONARY_WORD );
+ bIsWrong = rPaM.GetNode()->GetWrongList()->HasWrong( aSel.Min().GetIndex(), aSel.Max().GetIndex() );
+ if ( bIsWrong && bMarkIfWrong )
+ {
+ DrawSelectionXOR();
+ SetEditSelection( aSel );
+ DrawSelectionXOR();
+ }
+ }
+ return bIsWrong;
+}
+
+OUString ImpEditView::SpellIgnoreWord()
+{
+ OUString aWord;
+ if ( pEditEngine->pImpEditEngine->GetSpeller().is() )
+ {
+ EditPaM aPaM = GetEditSelection().Max();
+ if ( !HasSelection() )
+ {
+ EditSelection aSel = pEditEngine->SelectWord(aPaM);
+ aWord = pEditEngine->pImpEditEngine->GetSelected( aSel );
+ }
+ else
+ {
+ aWord = pEditEngine->pImpEditEngine->GetSelected( GetEditSelection() );
+ // And deselect
+ DrawSelectionXOR();
+ SetEditSelection( EditSelection( aPaM, aPaM ) );
+ DrawSelectionXOR();
+ }
+
+ if ( !aWord.isEmpty() )
+ {
+ Reference< XDictionary > xDic = LinguMgr::GetIgnoreAllList();
+ if (xDic.is())
+ xDic->add( aWord, false, OUString() );
+ EditDoc& rDoc = pEditEngine->GetEditDoc();
+ sal_Int32 nNodes = rDoc.Count();
+ for ( sal_Int32 n = 0; n < nNodes; n++ )
+ {
+ ContentNode* pNode = rDoc.GetObject( n );
+ pNode->GetWrongList()->MarkWrongsInvalid();
+ }
+ pEditEngine->pImpEditEngine->DoOnlineSpelling( aPaM.GetNode() );
+ pEditEngine->pImpEditEngine->StartOnlineSpellTimer();
+ }
+ }
+ return aWord;
+}
+
+void ImpEditView::DeleteSelected()
+{
+ DrawSelectionXOR();
+
+ pEditEngine->pImpEditEngine->UndoActionStart( EDITUNDO_DELETE );
+
+ EditPaM aPaM = pEditEngine->pImpEditEngine->DeleteSelected( GetEditSelection() );
+
+ pEditEngine->pImpEditEngine->UndoActionEnd();
+
+ SetEditSelection( EditSelection( aPaM, aPaM ) );
+
+ DrawSelectionXOR();
+
+ pEditEngine->pImpEditEngine->FormatAndLayout( GetEditViewPtr() );
+ ShowCursor( DoAutoScroll(), true );
+}
+
+const SvxFieldItem* ImpEditView::GetField( const Point& rPos, sal_Int32* pPara, sal_Int32* pPos ) const
+{
+ if( !GetOutputArea().Contains( rPos ) )
+ return nullptr;
+
+ Point aDocPos( GetDocPos( rPos ) );
+ EditPaM aPaM = pEditEngine->GetPaM(aDocPos, false);
+ if (!aPaM)
+ return nullptr;
+
+ if ( aPaM.GetIndex() == aPaM.GetNode()->Len() )
+ {
+ // Otherwise, whenever the Field at the very end and mouse under the text
+ return nullptr;
+ }
+
+ const CharAttribList::AttribsType& rAttrs = aPaM.GetNode()->GetCharAttribs().GetAttribs();
+ const sal_Int32 nXPos = aPaM.GetIndex();
+ for (size_t nAttr = rAttrs.size(); nAttr; )
+ {
+ const EditCharAttrib& rAttr = *rAttrs[--nAttr];
+ if (rAttr.GetStart() == nXPos || rAttr.GetEnd() == nXPos)
+ {
+ if (rAttr.Which() == EE_FEATURE_FIELD)
+ {
+ DBG_ASSERT(dynamic_cast<const SvxFieldItem*>(rAttr.GetItem()), "No FieldItem...");
+ if ( pPara )
+ *pPara = pEditEngine->GetEditDoc().GetPos( aPaM.GetNode() );
+ if ( pPos )
+ *pPos = rAttr.GetStart();
+ return static_cast<const SvxFieldItem*>(rAttr.GetItem());
+ }
+ }
+ }
+ return nullptr;
+}
+
+bool ImpEditView::IsBulletArea( const Point& rPos, sal_Int32* pPara )
+{
+ if ( pPara )
+ *pPara = EE_PARA_NOT_FOUND;
+
+ if( !GetOutputArea().Contains( rPos ) )
+ return false;
+
+ Point aDocPos( GetDocPos( rPos ) );
+ EditPaM aPaM = pEditEngine->GetPaM(aDocPos, false);
+ if (!aPaM)
+ return false;
+
+ if ( aPaM.GetIndex() == 0 )
+ {
+ sal_Int32 nPara = pEditEngine->GetEditDoc().GetPos( aPaM.GetNode() );
+ tools::Rectangle aBulletArea = pEditEngine->GetBulletArea( nPara );
+ tools::Long nY = pEditEngine->GetDocPosTopLeft( nPara ).Y();
+ const ParaPortion* pParaPortion = pEditEngine->GetParaPortions()[nPara];
+ nY += pParaPortion->GetFirstLineOffset();
+ if ( ( aDocPos.Y() > ( nY + aBulletArea.Top() ) ) &&
+ ( aDocPos.Y() < ( nY + aBulletArea.Bottom() ) ) &&
+ ( aDocPos.X() > ( aBulletArea.Left() ) ) &&
+ ( aDocPos.X() < ( aBulletArea.Right() ) ) )
+ {
+ if ( pPara )
+ *pPara = nPara;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void ImpEditView::CutCopy( css::uno::Reference< css::datatransfer::clipboard::XClipboard > const & rxClipboard, bool bCut )
+{
+ if ( !(rxClipboard.is() && HasSelection()) )
+ return;
+
+ uno::Reference<datatransfer::XTransferable> xData = pEditEngine->CreateTransferable( GetEditSelection() );
+
+ {
+ SolarMutexReleaser aReleaser;
+
+ try
+ {
+ rxClipboard->setContents( xData, nullptr );
+
+ // #87756# FlushClipboard, but it would be better to become a TerminateListener to the Desktop and flush on demand...
+ uno::Reference< datatransfer::clipboard::XFlushableClipboard > xFlushableClipboard( rxClipboard, uno::UNO_QUERY );
+ if( xFlushableClipboard.is() )
+ xFlushableClipboard->flushClipboard();
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+
+ }
+
+ if (bCut)
+ {
+ pEditEngine->pImpEditEngine->UndoActionStart(EDITUNDO_CUT);
+ DeleteSelected();
+ pEditEngine->pImpEditEngine->UndoActionEnd();
+ }
+}
+
+void ImpEditView::Paste( css::uno::Reference< css::datatransfer::clipboard::XClipboard > const & rxClipboard, bool bUseSpecial, SotClipboardFormatId format)
+{
+ if ( !rxClipboard.is() )
+ return;
+
+ uno::Reference< datatransfer::XTransferable > xDataObj;
+
+ try
+ {
+ SolarMutexReleaser aReleaser;
+ xDataObj = rxClipboard->getContents();
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+
+ if ( !xDataObj.is() || !EditEngine::HasValidData( xDataObj ) )
+ return;
+
+ pEditEngine->pImpEditEngine->UndoActionStart( EDITUNDO_PASTE );
+
+ EditSelection aSel( GetEditSelection() );
+ if ( aSel.HasRange() )
+ {
+ DrawSelectionXOR();
+ aSel = pEditEngine->DeleteSelection(aSel);
+ }
+
+ PasteOrDropInfos aPasteOrDropInfos;
+ aPasteOrDropInfos.nStartPara = pEditEngine->GetEditDoc().GetPos( aSel.Min().GetNode() );
+ pEditEngine->HandleBeginPasteOrDrop(aPasteOrDropInfos);
+
+ if ( DoSingleLinePaste() )
+ {
+ datatransfer::DataFlavor aFlavor;
+ SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor );
+ if ( xDataObj->isDataFlavorSupported( aFlavor ) )
+ {
+ try
+ {
+ uno::Any aData = xDataObj->getTransferData( aFlavor );
+ OUString aTmpText;
+ aData >>= aTmpText;
+ OUString aText(convertLineEnd(aTmpText, LINEEND_LF));
+ aText = aText.replaceAll( OUStringChar(LINE_SEP), " " );
+ aSel = pEditEngine->InsertText(aSel, aText);
+ }
+ catch( ... )
+ {
+ ; // #i9286# can happen, even if isDataFlavorSupported returns true...
+ }
+ }
+ }
+ else
+ {
+ // Prevent notifications of paragraph inserts et al that would trigger
+ // a11y to format content in a half-ready state when obtaining
+ // paragraphs. Collect and broadcast when done instead.
+ aSel = pEditEngine->InsertText(
+ xDataObj, OUString(), aSel.Min(),
+ bUseSpecial && pEditEngine->GetInternalEditStatus().AllowPasteSpecial(), format);
+ }
+
+ aPasteOrDropInfos.nEndPara = pEditEngine->GetEditDoc().GetPos( aSel.Max().GetNode() );
+ pEditEngine->HandleEndPasteOrDrop(aPasteOrDropInfos);
+
+ pEditEngine->pImpEditEngine->UndoActionEnd();
+ SetEditSelection( aSel );
+ pEditEngine->pImpEditEngine->UpdateSelections();
+ pEditEngine->pImpEditEngine->FormatAndLayout( GetEditViewPtr() );
+ ShowCursor( DoAutoScroll(), true );
+}
+
+
+bool ImpEditView::IsInSelection( const EditPaM& rPaM )
+{
+ EditSelection aSel = GetEditSelection();
+ if ( !aSel.HasRange() )
+ return false;
+
+ aSel.Adjust( pEditEngine->GetEditDoc() );
+
+ sal_Int32 nStartNode = pEditEngine->GetEditDoc().GetPos( aSel.Min().GetNode() );
+ sal_Int32 nEndNode = pEditEngine->GetEditDoc().GetPos( aSel.Max().GetNode() );
+ sal_Int32 nCurNode = pEditEngine->GetEditDoc().GetPos( rPaM.GetNode() );
+
+ if ( ( nCurNode > nStartNode ) && ( nCurNode < nEndNode ) )
+ return true;
+
+ if ( nStartNode == nEndNode )
+ {
+ if ( nCurNode == nStartNode )
+ if ( ( rPaM.GetIndex() >= aSel.Min().GetIndex() ) && ( rPaM.GetIndex() < aSel.Max().GetIndex() ) )
+ return true;
+ }
+ else if ( ( nCurNode == nStartNode ) && ( rPaM.GetIndex() >= aSel.Min().GetIndex() ) )
+ return true;
+ else if ( ( nCurNode == nEndNode ) && ( rPaM.GetIndex() < aSel.Max().GetIndex() ) )
+ return true;
+
+ return false;
+}
+
+bool ImpEditView::IsSelectionFullPara() const
+{
+ if (!IsSelectionInSinglePara())
+ return false;
+
+ sal_Int32 nSelectionStartPos = GetEditSelection().Min().GetIndex();
+ sal_Int32 nSelectionEndPos = GetEditSelection().Max().GetIndex();
+
+ if (nSelectionStartPos > nSelectionEndPos)
+ std::swap(nSelectionStartPos, nSelectionEndPos);
+
+ if (nSelectionStartPos != 0)
+ return false;
+
+ const ContentNode* pNode = GetEditSelection().Min().GetNode();
+ return pNode->Len() == nSelectionEndPos;
+}
+
+bool ImpEditView::IsSelectionInSinglePara() const
+{
+ return GetEditSelection().Min().GetNode() == GetEditSelection().Max().GetNode();
+}
+
+void ImpEditView::CreateAnchor()
+{
+ pEditEngine->SetInSelectionMode(true);
+ EditSelection aNewSelection(GetEditSelection());
+ aNewSelection.Min() = aNewSelection.Max();
+ SetEditSelection(aNewSelection);
+ // const_cast<EditPaM&>(GetEditSelection().Min()) = GetEditSelection().Max();
+}
+
+void ImpEditView::DeselectAll()
+{
+ pEditEngine->SetInSelectionMode(false);
+ DrawSelectionXOR();
+ EditSelection aNewSelection(GetEditSelection());
+ aNewSelection.Min() = aNewSelection.Max();
+ SetEditSelection(aNewSelection);
+ // const_cast<EditPaM&>(GetEditSelection().Min()) = GetEditSelection().Max();
+
+ if (comphelper::LibreOfficeKit::isActive() && mpViewShell && pOutWin)
+ {
+ VclPtr<vcl::Window> pParent = pOutWin->GetParentWithLOKNotifier();
+ if (pParent && pParent->GetLOKWindowId())
+ {
+ const vcl::ILibreOfficeKitNotifier* pNotifier = pParent->GetLOKNotifier();
+ std::vector<vcl::LOKPayloadItem> aItems;
+ aItems.emplace_back("rectangles", "");
+ pNotifier->notifyWindow(pParent->GetLOKWindowId(), "text_selection", aItems);
+ }
+ }
+}
+
+bool ImpEditView::IsSelectionAtPoint( const Point& rPosPixel )
+{
+ if ( pDragAndDropInfo && pDragAndDropInfo->pField )
+ return true;
+
+ // Logical units ...
+ const OutputDevice& rOutDev = GetOutputDevice();
+ Point aMousePos = rOutDev.PixelToLogic(rPosPixel);
+
+ if ( ( !GetOutputArea().Contains( aMousePos ) ) && !pEditEngine->pImpEditEngine->IsInSelectionMode() )
+ {
+ return false;
+ }
+
+ Point aDocPos( GetDocPos( aMousePos ) );
+ EditPaM aPaM = pEditEngine->GetPaM(aDocPos, false);
+ return IsInSelection( aPaM );
+}
+
+bool ImpEditView::SetCursorAtPoint( const Point& rPointPixel )
+{
+ pEditEngine->CheckIdleFormatter();
+
+ Point aMousePos( rPointPixel );
+
+ // Logical units ...
+ const OutputDevice& rOutDev = GetOutputDevice();
+ aMousePos = rOutDev.PixelToLogic( aMousePos );
+
+ if ( ( !GetOutputArea().Contains( aMousePos ) ) && !pEditEngine->pImpEditEngine->IsInSelectionMode() )
+ {
+ return false;
+ }
+
+ Point aDocPos( GetDocPos( aMousePos ) );
+
+ // Can be optimized: first go through the lines within a paragraph for PAM,
+ // then again with the PaM for the Rect, even though the line is already
+ // known... This must not be, though!
+ EditPaM aPaM = pEditEngine->GetPaM(aDocPos);
+ bool bGotoCursor = DoAutoScroll();
+
+ // aTmpNewSel: Diff between old and new, not the new selection, unless for tiled rendering
+ EditSelection aTmpNewSel( comphelper::LibreOfficeKit::isActive() ? GetEditSelection().Min() : GetEditSelection().Max(), aPaM );
+
+ // #i27299#
+ // work on copy of current selection and set new selection, if it has changed.
+ EditSelection aNewEditSelection( GetEditSelection() );
+
+ aNewEditSelection.Max() = aPaM;
+ if (!pEditEngine->GetSelectionEngine().HasAnchor())
+ {
+ if ( aNewEditSelection.Min() != aPaM )
+ {
+ const ContentNode* pNode(aNewEditSelection.Min().GetNode());
+ if (nullptr != pNode)
+ pNode->checkAndDeleteEmptyAttribs();
+ }
+ aNewEditSelection.Min() = aPaM;
+ }
+ else
+ {
+ DrawSelectionXOR( aTmpNewSel );
+ }
+
+ // set changed text selection
+ if ( GetEditSelection() != aNewEditSelection )
+ {
+ SetEditSelection( aNewEditSelection );
+ }
+
+ bool bForceCursor = pDragAndDropInfo == nullptr && !pEditEngine->pImpEditEngine->IsInSelectionMode();
+ ShowCursor( bGotoCursor, bForceCursor );
+ return true;
+}
+
+void ImpEditView::HideDDCursor()
+{
+ if ( pDragAndDropInfo && pDragAndDropInfo->bVisCursor )
+ {
+ OutputDevice& rOutDev = GetOutputDevice();
+ rOutDev.DrawOutDev( pDragAndDropInfo->aCurSavedCursor.TopLeft(), pDragAndDropInfo->aCurSavedCursor.GetSize(),
+ Point(0,0), pDragAndDropInfo->aCurSavedCursor.GetSize(),*pDragAndDropInfo->pBackground );
+ pDragAndDropInfo->bVisCursor = false;
+ }
+}
+
+void ImpEditView::ShowDDCursor( const tools::Rectangle& rRect )
+{
+ if ( !pDragAndDropInfo || pDragAndDropInfo->bVisCursor )
+ return;
+
+ if (pOutWin && pOutWin->GetCursor())
+ pOutWin->GetCursor()->Hide();
+
+ OutputDevice& rOutDev = GetOutputDevice();
+ Color aOldFillColor = rOutDev.GetFillColor();
+ rOutDev.SetFillColor( Color(4210752) ); // GRAY BRUSH_50, OLDSV, change to DDCursor!
+
+ // Save background ...
+ tools::Rectangle aSaveRect( rOutDev.LogicToPixel( rRect ) );
+ // prefer to save some more ...
+ aSaveRect.AdjustRight(1 );
+ aSaveRect.AdjustBottom(1 );
+
+ if ( !pDragAndDropInfo->pBackground )
+ {
+ pDragAndDropInfo->pBackground = VclPtr<VirtualDevice>::Create(rOutDev);
+ MapMode aMapMode( rOutDev.GetMapMode() );
+ aMapMode.SetOrigin( Point( 0, 0 ) );
+ pDragAndDropInfo->pBackground->SetMapMode( aMapMode );
+
+ }
+
+ Size aNewSzPx( aSaveRect.GetSize() );
+ Size aCurSzPx( pDragAndDropInfo->pBackground->GetOutputSizePixel() );
+ if ( ( aCurSzPx.Width() < aNewSzPx.Width() ) ||( aCurSzPx.Height() < aNewSzPx.Height() ) )
+ {
+ bool bDone = pDragAndDropInfo->pBackground->SetOutputSizePixel( aNewSzPx );
+ DBG_ASSERT( bDone, "Virtual Device broken?" );
+ }
+
+ aSaveRect = rOutDev.PixelToLogic( aSaveRect );
+
+ pDragAndDropInfo->pBackground->DrawOutDev( Point(0,0), aSaveRect.GetSize(),
+ aSaveRect.TopLeft(), aSaveRect.GetSize(), rOutDev );
+ pDragAndDropInfo->aCurSavedCursor = aSaveRect;
+
+ // Draw Cursor...
+ rOutDev.DrawRect( rRect );
+
+ pDragAndDropInfo->bVisCursor = true;
+ pDragAndDropInfo->aCurCursor = rRect;
+
+ rOutDev.SetFillColor( aOldFillColor );
+}
+
+void ImpEditView::dragGestureRecognized(const css::datatransfer::dnd::DragGestureEvent& rDGE)
+{
+ DBG_ASSERT( !pDragAndDropInfo, "dragGestureRecognized - DragAndDropInfo exist!" );
+
+ SolarMutexGuard aVclGuard;
+
+ pDragAndDropInfo.reset();
+
+ Point aMousePosPixel( rDGE.DragOriginX, rDGE.DragOriginY );
+
+ EditSelection aCopySel( GetEditSelection() );
+ aCopySel.Adjust( pEditEngine->GetEditDoc() );
+
+ if ( HasSelection() && bClickedInSelection )
+ {
+ pDragAndDropInfo.reset(new DragAndDropInfo());
+ }
+ else
+ {
+ // Field?!
+ sal_Int32 nPara;
+ sal_Int32 nPos;
+ Point aMousePos = GetOutputDevice().PixelToLogic( aMousePosPixel );
+ const SvxFieldItem* pField = GetField( aMousePos, &nPara, &nPos );
+ if ( pField )
+ {
+ pDragAndDropInfo.reset(new DragAndDropInfo());
+ pDragAndDropInfo->pField = pField;
+ ContentNode* pNode = pEditEngine->GetEditDoc().GetObject( nPara );
+ aCopySel = EditSelection( EditPaM( pNode, nPos ), EditPaM( pNode, nPos+1 ) );
+ SetEditSelection(aCopySel);
+ DrawSelectionXOR();
+ bool bGotoCursor = DoAutoScroll();
+ ShowCursor( bGotoCursor, /*bForceCursor=*/false );
+ }
+ else if ( IsBulletArea( aMousePos, &nPara ) )
+ {
+ pDragAndDropInfo.reset(new DragAndDropInfo());
+ pDragAndDropInfo->bOutlinerMode = true;
+ EditPaM aStartPaM( pEditEngine->GetEditDoc().GetObject( nPara ), 0 );
+ EditPaM aEndPaM( aStartPaM );
+ const SfxInt16Item& rLevel = pEditEngine->GetParaAttrib( nPara, EE_PARA_OUTLLEVEL );
+ for ( sal_Int32 n = nPara +1; n < pEditEngine->GetEditDoc().Count(); n++ )
+ {
+ const SfxInt16Item& rL = pEditEngine->GetParaAttrib( n, EE_PARA_OUTLLEVEL );
+ if ( rL.GetValue() > rLevel.GetValue() )
+ {
+ aEndPaM.SetNode( pEditEngine->GetEditDoc().GetObject( n ) );
+ }
+ else
+ {
+ break;
+ }
+ }
+ aEndPaM.SetIndex( aEndPaM.GetNode()->Len() );
+ SetEditSelection( EditSelection( aStartPaM, aEndPaM ) );
+ }
+ }
+
+ if ( !pDragAndDropInfo )
+ return;
+
+
+ pDragAndDropInfo->bStarterOfDD = true;
+
+ // Sensitive area to be scrolled.
+ Size aSz( 5, 0 );
+ aSz = GetOutputDevice().PixelToLogic( aSz );
+ pDragAndDropInfo->nSensibleRange = static_cast<sal_uInt16>(aSz.Width());
+ pDragAndDropInfo->nCursorWidth = static_cast<sal_uInt16>(aSz.Width()) / 2;
+ pDragAndDropInfo->aBeginDragSel = pEditEngine->pImpEditEngine->CreateESel( aCopySel );
+
+ uno::Reference<datatransfer::XTransferable> xData = pEditEngine->CreateTransferable(aCopySel);
+
+ sal_Int8 nActions = bReadOnly ? datatransfer::dnd::DNDConstants::ACTION_COPY : datatransfer::dnd::DNDConstants::ACTION_COPY_OR_MOVE;
+
+ rDGE.DragSource->startDrag( rDGE, nActions, 0 /*cursor*/, 0 /*image*/, xData, mxDnDListener );
+ // If Drag&Move in an Engine, then Copy&Del has to be optional!
+ GetCursor()->Hide();
+}
+
+void ImpEditView::dragDropEnd( const css::datatransfer::dnd::DragSourceDropEvent& rDSDE )
+{
+ SolarMutexGuard aVclGuard;
+
+ DBG_ASSERT( pDragAndDropInfo, "ImpEditView::dragDropEnd: pDragAndDropInfo is NULL!" );
+
+ // #123688# Shouldn't happen, but seems to happen...
+ if ( !pDragAndDropInfo )
+ return;
+
+ if ( !bReadOnly && rDSDE.DropSuccess && !pDragAndDropInfo->bOutlinerMode && ( rDSDE.DropAction & datatransfer::dnd::DNDConstants::ACTION_MOVE ) )
+ {
+ if ( pDragAndDropInfo->bStarterOfDD && pDragAndDropInfo->bDroppedInMe )
+ {
+ // DropPos: Where was it dropped, irrespective of length.
+ ESelection aDropPos( pDragAndDropInfo->aDropSel.nStartPara, pDragAndDropInfo->aDropSel.nStartPos, pDragAndDropInfo->aDropSel.nStartPara, pDragAndDropInfo->aDropSel.nStartPos );
+ ESelection aToBeDelSel = pDragAndDropInfo->aBeginDragSel;
+ ESelection aNewSel( pDragAndDropInfo->aDropSel.nEndPara, pDragAndDropInfo->aDropSel.nEndPos,
+ pDragAndDropInfo->aDropSel.nEndPara, pDragAndDropInfo->aDropSel.nEndPos );
+ bool bBeforeSelection = aDropPos < pDragAndDropInfo->aBeginDragSel;
+ sal_Int32 nParaDiff = pDragAndDropInfo->aBeginDragSel.nEndPara - pDragAndDropInfo->aBeginDragSel.nStartPara;
+ if ( bBeforeSelection )
+ {
+ // Adjust aToBeDelSel.
+ DBG_ASSERT( pDragAndDropInfo->aBeginDragSel.nStartPara >= pDragAndDropInfo->aDropSel.nStartPara, "But not before? ");
+ aToBeDelSel.nStartPara = aToBeDelSel.nStartPara + nParaDiff;
+ aToBeDelSel.nEndPara = aToBeDelSel.nEndPara + nParaDiff;
+ // To correct the character?
+ if ( aToBeDelSel.nStartPara == pDragAndDropInfo->aDropSel.nEndPara )
+ {
+ sal_uInt16 nMoreChars;
+ if ( pDragAndDropInfo->aDropSel.nStartPara == pDragAndDropInfo->aDropSel.nEndPara )
+ nMoreChars = pDragAndDropInfo->aDropSel.nEndPos - pDragAndDropInfo->aDropSel.nStartPos;
+ else
+ nMoreChars = pDragAndDropInfo->aDropSel.nEndPos;
+ aToBeDelSel.nStartPos =
+ aToBeDelSel.nStartPos + nMoreChars;
+ if ( aToBeDelSel.nStartPara == aToBeDelSel.nEndPara )
+ aToBeDelSel.nEndPos =
+ aToBeDelSel.nEndPos + nMoreChars;
+ }
+ }
+ else
+ {
+ // aToBeDelSel is ok, but the selection of the View
+ // has to be adapted, if it was deleted before!
+ DBG_ASSERT( pDragAndDropInfo->aBeginDragSel.nStartPara <= pDragAndDropInfo->aDropSel.nStartPara, "But not before? ");
+ aNewSel.nStartPara = aNewSel.nStartPara - nParaDiff;
+ aNewSel.nEndPara = aNewSel.nEndPara - nParaDiff;
+ // To correct the character?
+ if ( pDragAndDropInfo->aBeginDragSel.nEndPara == pDragAndDropInfo->aDropSel.nStartPara )
+ {
+ sal_uInt16 nLessChars;
+ if ( pDragAndDropInfo->aBeginDragSel.nStartPara == pDragAndDropInfo->aBeginDragSel.nEndPara )
+ nLessChars = pDragAndDropInfo->aBeginDragSel.nEndPos - pDragAndDropInfo->aBeginDragSel.nStartPos;
+ else
+ nLessChars = pDragAndDropInfo->aBeginDragSel.nEndPos;
+ aNewSel.nStartPos = aNewSel.nStartPos - nLessChars;
+ if ( aNewSel.nStartPara == aNewSel.nEndPara )
+ aNewSel.nEndPos = aNewSel.nEndPos - nLessChars;
+ }
+ }
+
+ DrawSelectionXOR();
+ EditSelection aDelSel( pEditEngine->pImpEditEngine->CreateSel( aToBeDelSel ) );
+ DBG_ASSERT( !aDelSel.DbgIsBuggy( pEditEngine->GetEditDoc() ), "ToBeDel is buggy!" );
+ pEditEngine->DeleteSelection(aDelSel);
+ if ( !bBeforeSelection )
+ {
+ DBG_ASSERT( !pEditEngine->pImpEditEngine->CreateSel( aNewSel ).DbgIsBuggy(pEditEngine->GetEditDoc()), "Bad" );
+ SetEditSelection( pEditEngine->pImpEditEngine->CreateSel( aNewSel ) );
+ }
+ pEditEngine->pImpEditEngine->FormatAndLayout( pEditEngine->pImpEditEngine->GetActiveView() );
+ DrawSelectionXOR();
+ }
+ else
+ {
+ // other EditEngine ...
+ if (pEditEngine->HasText()) // #88630# SC is removing the content when switching the task
+ DeleteSelected();
+ }
+ }
+
+ if ( pDragAndDropInfo->bUndoAction )
+ pEditEngine->pImpEditEngine->UndoActionEnd();
+
+ HideDDCursor();
+ ShowCursor( DoAutoScroll(), true );
+ pDragAndDropInfo.reset();
+ pEditEngine->GetEndDropHdl().Call(GetEditViewPtr());
+}
+
+void ImpEditView::drop( const css::datatransfer::dnd::DropTargetDropEvent& rDTDE )
+{
+ SolarMutexGuard aVclGuard;
+
+ DBG_ASSERT( pDragAndDropInfo, "Drop - No Drag&Drop info?!" );
+
+ if ( !(pDragAndDropInfo && pDragAndDropInfo->bDragAccepted) )
+ return;
+
+ pEditEngine->GetBeginDropHdl().Call(GetEditViewPtr());
+ bool bChanges = false;
+
+ HideDDCursor();
+
+ if ( pDragAndDropInfo->bStarterOfDD )
+ {
+ pEditEngine->pImpEditEngine->UndoActionStart( EDITUNDO_DRAGANDDROP );
+ pDragAndDropInfo->bUndoAction = true;
+ }
+
+ if ( pDragAndDropInfo->bOutlinerMode )
+ {
+ bChanges = true;
+ GetEditViewPtr()->MoveParagraphs( Range( pDragAndDropInfo->aBeginDragSel.nStartPara, pDragAndDropInfo->aBeginDragSel.nEndPara ), pDragAndDropInfo->nOutlinerDropDest );
+ }
+ else
+ {
+ uno::Reference< datatransfer::XTransferable > xDataObj = rDTDE.Transferable;
+ if ( xDataObj.is() )
+ {
+ bChanges = true;
+ // remove Selection ...
+ DrawSelectionXOR();
+ EditPaM aPaM( pDragAndDropInfo->aDropDest );
+
+ PasteOrDropInfos aPasteOrDropInfos;
+ aPasteOrDropInfos.nStartPara = pEditEngine->GetEditDoc().GetPos( aPaM.GetNode() );
+ pEditEngine->HandleBeginPasteOrDrop(aPasteOrDropInfos);
+
+ EditSelection aNewSel = pEditEngine->InsertText(
+ xDataObj, OUString(), aPaM, pEditEngine->GetInternalEditStatus().AllowPasteSpecial());
+
+ aPasteOrDropInfos.nEndPara = pEditEngine->GetEditDoc().GetPos( aNewSel.Max().GetNode() );
+ pEditEngine->HandleEndPasteOrDrop(aPasteOrDropInfos);
+
+ SetEditSelection( aNewSel );
+ pEditEngine->pImpEditEngine->FormatAndLayout( pEditEngine->pImpEditEngine->GetActiveView() );
+ if ( pDragAndDropInfo->bStarterOfDD )
+ {
+ // Only set if the same engine!
+ pDragAndDropInfo->aDropSel.nStartPara = pEditEngine->GetEditDoc().GetPos( aPaM.GetNode() );
+ pDragAndDropInfo->aDropSel.nStartPos = aPaM.GetIndex();
+ pDragAndDropInfo->aDropSel.nEndPara = pEditEngine->GetEditDoc().GetPos( aNewSel.Max().GetNode() );
+ pDragAndDropInfo->aDropSel.nEndPos = aNewSel.Max().GetIndex();
+ pDragAndDropInfo->bDroppedInMe = true;
+ }
+ }
+ }
+
+ if ( bChanges )
+ {
+ rDTDE.Context->acceptDrop( rDTDE.DropAction );
+ }
+
+ if ( !pDragAndDropInfo->bStarterOfDD )
+ {
+ pDragAndDropInfo.reset();
+ }
+
+ rDTDE.Context->dropComplete( bChanges );
+}
+
+void ImpEditView::dragEnter( const css::datatransfer::dnd::DropTargetDragEnterEvent& rDTDEE )
+{
+ SolarMutexGuard aVclGuard;
+
+ if ( !pDragAndDropInfo )
+ pDragAndDropInfo.reset(new DragAndDropInfo());
+
+ pDragAndDropInfo->bHasValidData = false;
+
+ // Check for supported format...
+ // Only check for text, will also be there if bin or rtf
+ datatransfer::DataFlavor aTextFlavor;
+ SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aTextFlavor );
+ const css::datatransfer::DataFlavor* pFlavors = rDTDEE.SupportedDataFlavors.getConstArray();
+ int nFlavors = rDTDEE.SupportedDataFlavors.getLength();
+ for ( int n = 0; n < nFlavors; n++ )
+ {
+ if( TransferableDataHelper::IsEqual( pFlavors[n], aTextFlavor ) )
+ {
+ pDragAndDropInfo->bHasValidData = true;
+ break;
+ }
+ }
+
+ dragOver( rDTDEE );
+}
+
+void ImpEditView::dragExit( const css::datatransfer::dnd::DropTargetEvent& )
+{
+ SolarMutexGuard aVclGuard;
+
+ HideDDCursor();
+
+ if ( pDragAndDropInfo && !pDragAndDropInfo->bStarterOfDD )
+ {
+ pDragAndDropInfo.reset();
+ }
+}
+
+void ImpEditView::dragOver(const css::datatransfer::dnd::DropTargetDragEvent& rDTDE)
+{
+ SolarMutexGuard aVclGuard;
+
+ const OutputDevice& rOutDev = GetOutputDevice();
+
+ Point aMousePos( rDTDE.LocationX, rDTDE.LocationY );
+ aMousePos = rOutDev.PixelToLogic( aMousePos );
+
+ bool bAccept = false;
+
+ if ( GetOutputArea().Contains( aMousePos ) && !bReadOnly )
+ {
+ if ( pDragAndDropInfo && pDragAndDropInfo->bHasValidData )
+ {
+ bAccept = true;
+
+ bool bAllowScroll = DoAutoScroll();
+ if ( bAllowScroll )
+ {
+ tools::Long nScrollX = 0;
+ tools::Long nScrollY = 0;
+ // Check if in the sensitive area
+ if ( ( (aMousePos.X()-pDragAndDropInfo->nSensibleRange) < GetOutputArea().Left() ) && ( ( aMousePos.X() + pDragAndDropInfo->nSensibleRange ) > GetOutputArea().Left() ) )
+ nScrollX = GetOutputArea().GetWidth() / SCRLRANGE;
+ else if ( ( (aMousePos.X()+pDragAndDropInfo->nSensibleRange) > GetOutputArea().Right() ) && ( ( aMousePos.X() - pDragAndDropInfo->nSensibleRange ) < GetOutputArea().Right() ) )
+ nScrollX = -( GetOutputArea().GetWidth() / SCRLRANGE );
+
+ if ( ( (aMousePos.Y()-pDragAndDropInfo->nSensibleRange) < GetOutputArea().Top() ) && ( ( aMousePos.Y() + pDragAndDropInfo->nSensibleRange ) > GetOutputArea().Top() ) )
+ nScrollY = GetOutputArea().GetHeight() / SCRLRANGE;
+ else if ( ( (aMousePos.Y()+pDragAndDropInfo->nSensibleRange) > GetOutputArea().Bottom() ) && ( ( aMousePos.Y() - pDragAndDropInfo->nSensibleRange ) < GetOutputArea().Bottom() ) )
+ nScrollY = -( GetOutputArea().GetHeight() / SCRLRANGE );
+
+ if ( nScrollX || nScrollY )
+ {
+ HideDDCursor();
+ Scroll( nScrollX, nScrollY, ScrollRangeCheck::PaperWidthTextSize );
+ }
+ }
+
+ Point aDocPos( GetDocPos( aMousePos ) );
+ EditPaM aPaM = pEditEngine->GetPaM( aDocPos );
+ pDragAndDropInfo->aDropDest = aPaM;
+ if ( pDragAndDropInfo->bOutlinerMode )
+ {
+ sal_Int32 nPara = pEditEngine->GetEditDoc().GetPos( aPaM.GetNode() );
+ ParaPortion* pPPortion = pEditEngine->GetParaPortions().SafeGetObject( nPara );
+ if (pPPortion)
+ {
+ tools::Long nDestParaStartY = pEditEngine->GetParaPortions().GetYOffset( pPPortion );
+ tools::Long nRel = aDocPos.Y() - nDestParaStartY;
+ if ( nRel < ( pPPortion->GetHeight() / 2 ) )
+ {
+ pDragAndDropInfo->nOutlinerDropDest = nPara;
+ }
+ else
+ {
+ pDragAndDropInfo->nOutlinerDropDest = nPara+1;
+ }
+
+ if( ( pDragAndDropInfo->nOutlinerDropDest >= pDragAndDropInfo->aBeginDragSel.nStartPara ) &&
+ ( pDragAndDropInfo->nOutlinerDropDest <= (pDragAndDropInfo->aBeginDragSel.nEndPara+1) ) )
+ {
+ bAccept = false;
+ }
+ }
+ }
+ else if ( HasSelection() )
+ {
+ // it must not be dropped into a selection
+ EPaM aP = pEditEngine->pImpEditEngine->CreateEPaM( aPaM );
+ ESelection aDestSel( aP.nPara, aP.nIndex, aP.nPara, aP.nIndex);
+ ESelection aCurSel = pEditEngine->pImpEditEngine->CreateESel( GetEditSelection() );
+ aCurSel.Adjust();
+ if ( !(aDestSel < aCurSel) && !(aDestSel > aCurSel) )
+ {
+ bAccept = false;
+ }
+ }
+ if ( bAccept )
+ {
+ tools::Rectangle aEditCursor;
+ if ( pDragAndDropInfo->bOutlinerMode )
+ {
+ tools::Long nDDYPos(0);
+ if ( pDragAndDropInfo->nOutlinerDropDest < pEditEngine->GetEditDoc().Count() )
+ {
+ ParaPortion* pPPortion = pEditEngine->GetParaPortions().SafeGetObject( pDragAndDropInfo->nOutlinerDropDest );
+ if (pPPortion)
+ nDDYPos = pEditEngine->GetParaPortions().GetYOffset( pPPortion );
+ }
+ else
+ {
+ nDDYPos = pEditEngine->pImpEditEngine->GetTextHeight();
+ }
+ Point aStartPos( 0, nDDYPos );
+ aStartPos = GetWindowPos( aStartPos );
+ Point aEndPos( GetOutputArea().GetWidth(), nDDYPos );
+ aEndPos = GetWindowPos( aEndPos );
+ aEditCursor = rOutDev.LogicToPixel( tools::Rectangle( aStartPos, aEndPos ) );
+ if ( !pEditEngine->IsEffectivelyVertical() )
+ {
+ aEditCursor.AdjustTop( -1 );
+ aEditCursor.AdjustBottom( 1 );
+ }
+ else
+ {
+ if( IsTopToBottom() )
+ {
+ aEditCursor.AdjustLeft( -1 );
+ aEditCursor.AdjustRight( 1 );
+ }
+ else
+ {
+ aEditCursor.AdjustLeft( 1 );
+ aEditCursor.AdjustRight( -1 );
+ }
+ }
+ aEditCursor = rOutDev.PixelToLogic( aEditCursor );
+ }
+ else
+ {
+ aEditCursor = pEditEngine->pImpEditEngine->PaMtoEditCursor( aPaM );
+ Point aTopLeft( GetWindowPos( aEditCursor.TopLeft() ) );
+ aEditCursor.SetPos( aTopLeft );
+ aEditCursor.SetRight( aEditCursor.Left() + pDragAndDropInfo->nCursorWidth );
+ aEditCursor = rOutDev.LogicToPixel( aEditCursor );
+ aEditCursor = rOutDev.PixelToLogic( aEditCursor );
+ }
+
+ bool bCursorChanged = !pDragAndDropInfo->bVisCursor || ( pDragAndDropInfo->aCurCursor != aEditCursor );
+ if ( bCursorChanged )
+ {
+ HideDDCursor();
+ ShowDDCursor(aEditCursor );
+ }
+ pDragAndDropInfo->bDragAccepted = true;
+ rDTDE.Context->acceptDrag( rDTDE.DropAction );
+ }
+ }
+ }
+
+ if ( !bAccept )
+ {
+ HideDDCursor();
+ if (pDragAndDropInfo)
+ pDragAndDropInfo->bDragAccepted = false;
+ rDTDE.Context->rejectDrag();
+ }
+}
+
+void ImpEditView::AddDragAndDropListeners()
+{
+ if (bActiveDragAndDropListener)
+ return;
+
+ css::uno::Reference<css::datatransfer::dnd::XDropTarget> xDropTarget;
+ if (EditViewCallbacks* pCallbacks = getEditViewCallbacks())
+ xDropTarget = pCallbacks->GetDropTarget();
+ else if (auto xWindow = GetWindow())
+ xDropTarget = xWindow->GetDropTarget();
+
+ if (!xDropTarget.is())
+ return;
+
+ mxDnDListener = new vcl::unohelper::DragAndDropWrapper(this);
+
+ css::uno::Reference<css::datatransfer::dnd::XDragGestureRecognizer> xDragGestureRecognizer(xDropTarget, uno::UNO_QUERY);
+ if (xDragGestureRecognizer.is())
+ {
+ uno::Reference<datatransfer::dnd::XDragGestureListener> xDGL(mxDnDListener, uno::UNO_QUERY);
+ xDragGestureRecognizer->addDragGestureListener(xDGL);
+ }
+
+ uno::Reference<datatransfer::dnd::XDropTargetListener> xDTL(mxDnDListener, uno::UNO_QUERY);
+ xDropTarget->addDropTargetListener(xDTL);
+ xDropTarget->setActive(true);
+ xDropTarget->setDefaultActions(datatransfer::dnd::DNDConstants::ACTION_COPY_OR_MOVE);
+
+ bActiveDragAndDropListener = true;
+}
+
+void ImpEditView::RemoveDragAndDropListeners()
+{
+ if (!bActiveDragAndDropListener)
+ return;
+
+ css::uno::Reference<css::datatransfer::dnd::XDropTarget> xDropTarget;
+ if (EditViewCallbacks* pCallbacks = getEditViewCallbacks())
+ xDropTarget = pCallbacks->GetDropTarget();
+ else if (auto xWindow = GetWindow())
+ xDropTarget = xWindow->GetDropTarget();
+
+ if (xDropTarget.is())
+ {
+ css::uno::Reference<css::datatransfer::dnd::XDragGestureRecognizer> xDragGestureRecognizer(xDropTarget, uno::UNO_QUERY);
+ if (xDragGestureRecognizer.is())
+ {
+ uno::Reference<datatransfer::dnd::XDragGestureListener> xDGL(mxDnDListener, uno::UNO_QUERY);
+ xDragGestureRecognizer->removeDragGestureListener(xDGL);
+ }
+
+ uno::Reference<datatransfer::dnd::XDropTargetListener> xDTL(mxDnDListener, uno::UNO_QUERY);
+ xDropTarget->removeDropTargetListener(xDTL);
+ }
+
+ if ( mxDnDListener.is() )
+ {
+ mxDnDListener->disposing( lang::EventObject() ); // #95154# Empty Source means it's the Client
+ mxDnDListener.clear();
+ }
+
+ bActiveDragAndDropListener = false;
+}
+
+void ImpEditView::InitLOKSpecialPositioning(MapUnit eUnit,
+ const tools::Rectangle& rOutputArea,
+ const Point& rVisDocStartPos)
+{
+ if (!mpLOKSpecialPositioning)
+ mpLOKSpecialPositioning.reset(new LOKSpecialPositioning(*this, eUnit, rOutputArea, rVisDocStartPos));
+ else
+ mpLOKSpecialPositioning->ReInit(eUnit, rOutputArea, rVisDocStartPos);
+}
+
+void ImpEditView::SetLOKSpecialOutputArea(const tools::Rectangle& rOutputArea)
+{
+ assert(mpLOKSpecialPositioning);
+ mpLOKSpecialPositioning->SetOutputArea(rOutputArea);
+}
+
+const tools::Rectangle & ImpEditView::GetLOKSpecialOutputArea() const
+{
+ assert(mpLOKSpecialPositioning);
+ return mpLOKSpecialPositioning->GetOutputArea();
+}
+
+void ImpEditView::SetLOKSpecialVisArea(const tools::Rectangle& rVisArea)
+{
+ assert(mpLOKSpecialPositioning);
+ mpLOKSpecialPositioning->SetVisDocStartPos(rVisArea.TopLeft());
+}
+
+tools::Rectangle ImpEditView::GetLOKSpecialVisArea() const
+{
+ assert(mpLOKSpecialPositioning);
+ return mpLOKSpecialPositioning->GetVisDocArea();
+}
+
+bool ImpEditView::HasLOKSpecialPositioning() const
+{
+ return bool(mpLOKSpecialPositioning);
+}
+
+void ImpEditView::SetLOKSpecialFlags(LOKSpecialFlags eFlags)
+{
+ assert(mpLOKSpecialPositioning);
+ mpLOKSpecialPositioning->SetFlags(eFlags);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/impedit.hxx b/editeng/source/editeng/impedit.hxx
new file mode 100644
index 0000000000..e4352f298f
--- /dev/null
+++ b/editeng/source/editeng/impedit.hxx
@@ -0,0 +1,1362 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <eerdll2.hxx>
+#include <editdoc.hxx>
+#include "editsel.hxx"
+#include "editundo.hxx"
+#include "editstt2.hxx"
+#include <editeng/editdata.hxx>
+#include <editeng/SpellPortions.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editview.hxx>
+#include <svtools/colorcfg.hxx>
+#include <editeng/outliner.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/cursor.hxx>
+#include <vcl/vclptr.hxx>
+#include <tools/fract.hxx>
+#include <vcl/idle.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/ptrstyle.hxx>
+
+#include <vcl/dndhelp.hxx>
+#include <svl/ondemand.hxx>
+#include <svl/languageoptions.hxx>
+#include <com/sun/star/linguistic2/XSpellAlternatives.hpp>
+#include <com/sun/star/linguistic2/XSpellChecker1.hpp>
+#include <com/sun/star/linguistic2/XHyphenator.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <com/sun/star/i18n/XExtendedInputSequenceChecker.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+
+#include <i18nlangtag/lang.h>
+#include <o3tl/deleter.hxx>
+#include <o3tl/typed_flags_set.hxx>
+
+#include <functional>
+#include <optional>
+#include <memory>
+#include <tuple>
+#include <string_view>
+#include <vector>
+
+class EditView;
+class EditEngine;
+class OutlinerSearchable;
+
+class SvxSearchItem;
+class SvxLRSpaceItem;
+class TextRanger;
+class SvKeyValueIterator;
+class SvxForbiddenCharactersTable;
+namespace vcl { class Window; }
+class SvxNumberFormat;
+namespace com::sun::star::datatransfer::clipboard {
+ class XClipboard;
+}
+
+namespace editeng {
+ struct MisspellRanges;
+}
+
+#define DEL_LEFT 1
+#define DEL_RIGHT 2
+#define TRAVEL_X_DONTKNOW 0xFFFFFFFF
+#define CURSOR_BIDILEVEL_DONTKNOW 0xFFFF
+#define MAXCHARSINPARA 0x3FFF-CHARPOSGROW // Max 16K, because WYSIWYG array
+#define LINE_SEP '\x0A'
+
+#define ATTRSPECIAL_WHOLEWORD 1
+#define ATTRSPECIAL_EDGE 2
+
+enum class GetCursorFlags {
+ NONE = 0x0000,
+ TextOnly = 0x0001,
+ StartOfLine = 0x0002,
+ EndOfLine = 0x0004,
+ PreferPortionStart = 0x0008,
+};
+namespace o3tl {
+ template<> struct typed_flags<GetCursorFlags> : is_typed_flags<GetCursorFlags, 0x0f> {};
+}
+
+
+struct DragAndDropInfo
+{
+ tools::Rectangle aCurCursor;
+ tools::Rectangle aCurSavedCursor;
+ sal_uInt16 nSensibleRange;
+ sal_uInt16 nCursorWidth;
+ ESelection aBeginDragSel;
+ EditPaM aDropDest;
+ sal_Int32 nOutlinerDropDest;
+ ESelection aDropSel;
+ VclPtr<VirtualDevice> pBackground;
+ const SvxFieldItem* pField;
+ bool bVisCursor : 1;
+ bool bDroppedInMe : 1;
+ bool bStarterOfDD : 1;
+ bool bHasValidData : 1;
+ bool bUndoAction : 1;
+ bool bOutlinerMode : 1;
+ bool bDragAccepted : 1;
+
+ DragAndDropInfo()
+ : nSensibleRange(0), nCursorWidth(0), nOutlinerDropDest(0), pBackground(nullptr),
+ pField(nullptr), bVisCursor(false), bDroppedInMe(false), bStarterOfDD(false),
+ bHasValidData(false), bUndoAction(false), bOutlinerMode(false), bDragAccepted(false)
+ {
+ }
+ ~DragAndDropInfo()
+ {
+ pBackground.disposeAndClear();
+ }
+};
+
+struct ImplIMEInfos
+{
+ OUString aOldTextAfterStartPos;
+ std::unique_ptr<ExtTextInputAttr[]> pAttribs;
+ EditPaM aPos;
+ sal_Int32 nLen;
+ bool bWasCursorOverwrite;
+
+ ImplIMEInfos( const EditPaM& rPos, OUString aOldTextAfterStartPos );
+ ~ImplIMEInfos();
+
+ void CopyAttribs( const ExtTextInputAttr* pA, sal_uInt16 nL );
+ void DestroyAttribs();
+};
+
+// #i18881# to be able to identify the positions of changed words
+// the positions of each portion need to be saved
+typedef std::vector<EditSelection> SpellContentSelections;
+
+struct SpellInfo
+{
+ EditPaM aCurSentenceStart;
+ svx::SpellPortions aLastSpellPortions;
+ SpellContentSelections aLastSpellContentSelections;
+ EESpellState eState;
+ EPaM aSpellStart;
+ EPaM aSpellTo;
+ bool bSpellToEnd;
+ bool bMultipleDoc;
+ SpellInfo() : eState(EESpellState::Ok), bSpellToEnd(true), bMultipleDoc(false)
+ { }
+};
+
+// used for text conversion
+struct ConvInfo
+{
+ EPaM aConvStart;
+ EPaM aConvTo;
+ EPaM aConvContinue; // position to start search for next text portion (word) with
+ bool bConvToEnd;
+ bool bMultipleDoc;
+
+ ConvInfo() : bConvToEnd(true), bMultipleDoc(false) {}
+};
+
+struct FormatterFontMetric
+{
+ sal_uInt16 nMaxAscent;
+ sal_uInt16 nMaxDescent;
+
+ FormatterFontMetric() : nMaxAscent(0), nMaxDescent(0) { /* nMinLeading = 0xFFFF; */ }
+ sal_uInt16 GetHeight() const { return nMaxAscent+nMaxDescent; }
+};
+
+class IdleFormattter : public Idle
+{
+private:
+ EditView* pView;
+ int nRestarts;
+
+public:
+ IdleFormattter();
+ virtual ~IdleFormattter() override;
+
+ void DoIdleFormat( EditView* pV );
+ void ForceTimeout();
+ void ResetRestarts() { nRestarts = 0; }
+ EditView* GetView() { return pView; }
+};
+
+class ImpEditView;
+/// This is meant just for Calc, where all positions in logical units (twips for LOK) are computed by
+/// doing independent pixel-alignment for each cell's size. LOKSpecialPositioning stores
+/// both 'output-area' and 'visible-doc-position' in pure logical unit (twips for LOK).
+/// This allows the cursor/selection messages to be in regular(print) twips unit like in Writer.
+class LOKSpecialPositioning
+{
+public:
+ LOKSpecialPositioning(const ImpEditView& rImpEditView, MapUnit eUnit, const tools::Rectangle& rOutputArea,
+ const Point& rVisDocStartPos);
+
+ void ReInit(MapUnit eUnit, const tools::Rectangle& rOutputArea, const Point& rVisDocStartPos);
+
+ void SetOutputArea(const tools::Rectangle& rOutputArea);
+ const tools::Rectangle& GetOutputArea() const;
+ void SetVisDocStartPos(const Point& rVisDocStartPos);
+
+ bool IsVertical() const;
+ bool IsTopToBottom() const;
+
+ tools::Long GetVisDocLeft() const { return maVisDocStartPos.X(); }
+ tools::Long GetVisDocTop() const { return maVisDocStartPos.Y(); }
+ tools::Long GetVisDocRight() const { return maVisDocStartPos.X() + (!IsVertical() ? maOutArea.GetWidth() : maOutArea.GetHeight()); }
+ tools::Long GetVisDocBottom() const { return maVisDocStartPos.Y() + (!IsVertical() ? maOutArea.GetHeight() : maOutArea.GetWidth()); }
+ tools::Rectangle GetVisDocArea() const;
+
+ Point GetWindowPos(const Point& rDocPos, MapUnit eDocPosUnit) const;
+ tools::Rectangle GetWindowPos(const tools::Rectangle& rDocRect, MapUnit eDocRectUnit) const;
+
+ void SetFlags(LOKSpecialFlags eFlags) { meFlags = eFlags; }
+ bool IsLayoutRTL() { return bool(meFlags & LOKSpecialFlags::LayoutRTL); }
+
+ Point GetRefPoint() const;
+
+private:
+ Point convertUnit(const Point& rPos, MapUnit ePosUnit) const;
+ tools::Rectangle convertUnit(const tools::Rectangle& rRect, MapUnit eRectUnit) const;
+
+ const ImpEditView& mrImpEditView;
+ tools::Rectangle maOutArea;
+ Point maVisDocStartPos;
+ MapUnit meUnit;
+ LOKSpecialFlags meFlags;
+};
+
+
+
+class ImpEditView : public vcl::unohelper::DragAndDropClient
+{
+ friend class EditView;
+ friend class EditEngine;
+ friend class ImpEditEngine;
+ using vcl::unohelper::DragAndDropClient::dragEnter;
+ using vcl::unohelper::DragAndDropClient::dragExit;
+ using vcl::unohelper::DragAndDropClient::dragOver;
+
+private:
+ EditView* pEditView;
+ std::unique_ptr<vcl::Cursor, o3tl::default_delete<vcl::Cursor>> pCursor;
+ std::optional<Color> mxBackgroundColor;
+ /// Containing view shell, if any.
+ OutlinerViewShell* mpViewShell;
+ /// Another shell, just listening to our state, if any.
+ OutlinerViewShell* mpOtherShell;
+ EditEngine* pEditEngine;
+ VclPtr<vcl::Window> pOutWin;
+ EditView::OutWindowSet aOutWindowSet;
+ std::optional<PointerStyle> mxPointer;
+ std::unique_ptr<DragAndDropInfo> pDragAndDropInfo;
+
+ css::uno::Reference< css::datatransfer::dnd::XDragSourceListener > mxDnDListener;
+
+
+ tools::Long nInvMore;
+ EVControlBits nControl;
+ sal_uInt32 nTravelXPos;
+ GetCursorFlags nExtraCursorFlags;
+ sal_uInt16 nCursorBidiLevel;
+ sal_uInt16 nScrollDiffX;
+ bool bReadOnly;
+ bool bClickedInSelection;
+ bool bActiveDragAndDropListener;
+
+ Point aAnchorPoint;
+ tools::Rectangle aOutArea;
+ Point aVisDocStartPos;
+ EESelectionMode eSelectionMode;
+ EditSelection aEditSelection;
+ EEAnchorMode eAnchorMode;
+
+ /// mechanism to change from the classic refresh mode that simply
+ // invalidates the area where text was changed. When set, the invalidate
+ // and the direct repaint of the Window-plugged EditView will be suppressed.
+ // Instead, a consumer that has registered using an EditViewCallbacks
+ // incarnation has to handle that. Used e.g. to represent the edited text
+ // in Draw/Impress in an OverlayObject which avoids evtl. expensive full
+ // repaints of the EditView(s)
+ EditViewCallbacks* mpEditViewCallbacks;
+ std::unique_ptr<LOKSpecialPositioning> mpLOKSpecialPositioning;
+ bool mbBroadcastLOKViewCursor:1;
+ bool mbSuppressLOKMessages:1;
+ bool mbNegativeX:1;
+
+ EditViewCallbacks* getEditViewCallbacks() const
+ {
+ return mpEditViewCallbacks;
+ }
+
+ void lokSelectionCallback(const std::optional<tools::PolyPolygon> &pPolyPoly, bool bStartHandleVisible, bool bEndHandleVisible);
+
+ void setEditViewCallbacks(EditViewCallbacks* pEditViewCallbacks)
+ {
+ mpEditViewCallbacks = pEditViewCallbacks;
+ }
+
+ void InvalidateAtWindow(const tools::Rectangle& rRect);
+
+ css::uno::Reference<css::datatransfer::clipboard::XClipboard> GetClipboard() const;
+
+ void SetBroadcastLOKViewCursor(bool bSet)
+ {
+ mbBroadcastLOKViewCursor = bSet;
+ }
+
+protected:
+
+ // DragAndDropClient
+ void dragGestureRecognized(const css::datatransfer::dnd::DragGestureEvent& dge) override;
+ void dragDropEnd( const css::datatransfer::dnd::DragSourceDropEvent& dsde ) override;
+ void drop(const css::datatransfer::dnd::DropTargetDropEvent& dtde) override;
+ void dragEnter( const css::datatransfer::dnd::DropTargetDragEnterEvent& dtdee ) override;
+ void dragExit( const css::datatransfer::dnd::DropTargetEvent& dte ) override;
+ void dragOver(const css::datatransfer::dnd::DropTargetDragEvent& dtde) override;
+
+ void ShowDDCursor( const tools::Rectangle& rRect );
+ void HideDDCursor();
+
+ void ImplDrawHighlightRect(OutputDevice& rTarget, const Point& rDocPosTopLeft, const Point& rDocPosBottomRight, tools::PolyPolygon* pPolyPoly, bool bLOKCalcRTL);
+ tools::Rectangle ImplGetEditCursor(EditPaM& aPaM, GetCursorFlags nShowCursorFlags,
+ sal_Int32& nTextPortionStart, const ParaPortion* pParaPortion) const;
+
+public:
+ ImpEditView( EditView* pView, EditEngine* pEng, vcl::Window* pWindow );
+ virtual ~ImpEditView() override;
+
+ EditView* GetEditViewPtr() { return pEditView; }
+
+ sal_uInt16 GetScrollDiffX() const { return nScrollDiffX; }
+ void SetScrollDiffX( sal_uInt16 n ) { nScrollDiffX = n; }
+
+ sal_uInt16 GetCursorBidiLevel() const { return nCursorBidiLevel; }
+ void SetCursorBidiLevel( sal_uInt16 n ) { nCursorBidiLevel = n; }
+
+ Point GetDocPos( const Point& rWindowPos ) const;
+ Point GetWindowPos( const Point& rDocPos ) const;
+ tools::Rectangle GetWindowPos( const tools::Rectangle& rDocPos ) const;
+
+ void SetOutputArea( const tools::Rectangle& rRect );
+ void ResetOutputArea( const tools::Rectangle& rRect );
+ const tools::Rectangle& GetOutputArea() const { return aOutArea; }
+
+ bool IsVertical() const;
+ bool IsTopToBottom() const;
+
+ bool PostKeyEvent( const KeyEvent& rKeyEvent, vcl::Window const * pFrameWin );
+
+ bool MouseButtonUp( const MouseEvent& rMouseEvent );
+ bool MouseButtonDown( const MouseEvent& rMouseEvent );
+ void ReleaseMouse();
+ bool MouseMove( const MouseEvent& rMouseEvent );
+ bool Command(const CommandEvent& rCEvt);
+
+ void CutCopy( css::uno::Reference< css::datatransfer::clipboard::XClipboard > const & rxClipboard, bool bCut );
+ void Paste( css::uno::Reference< css::datatransfer::clipboard::XClipboard > const & rxClipboard, bool bUseSpecial = false, SotClipboardFormatId format = SotClipboardFormatId::NONE);
+
+ void SetVisDocStartPos( const Point& rPos ) { aVisDocStartPos = rPos; }
+
+ tools::Long GetVisDocLeft() const { return aVisDocStartPos.X(); }
+ tools::Long GetVisDocTop() const { return aVisDocStartPos.Y(); }
+ tools::Long GetVisDocRight() const { return aVisDocStartPos.X() + ( !IsVertical() ? aOutArea.GetWidth() : aOutArea.GetHeight() ); }
+ tools::Long GetVisDocBottom() const { return aVisDocStartPos.Y() + ( !IsVertical() ? aOutArea.GetHeight() : aOutArea.GetWidth() ); }
+ tools::Rectangle GetVisDocArea() const;
+
+ const EditSelection& GetEditSelection() const { return aEditSelection; }
+ void SetEditSelection( const EditSelection& rEditSelection );
+ bool HasSelection() const { return aEditSelection.HasRange(); }
+
+ void SelectionChanged();
+ void DrawSelectionXOR() { DrawSelectionXOR( aEditSelection ); }
+ void DrawSelectionXOR( EditSelection, vcl::Region* pRegion = nullptr, OutputDevice* pTargetDevice = nullptr );
+ void GetSelectionRectangles(EditSelection aTmpSel, std::vector<tools::Rectangle>& rLogicRects);
+
+ void ScrollStateChange();
+
+ OutputDevice& GetOutputDevice() const;
+ weld::Widget* GetPopupParent(tools::Rectangle& rRect) const;
+ vcl::Window* GetWindow() const { return pOutWin; }
+
+ void SetSelectionMode( EESelectionMode eMode );
+
+ inline PointerStyle GetPointer();
+
+ inline vcl::Cursor* GetCursor();
+
+ void AddDragAndDropListeners();
+ void RemoveDragAndDropListeners();
+
+ bool IsBulletArea( const Point& rPos, sal_Int32* pPara );
+
+// For the Selection Engine...
+ void CreateAnchor();
+ void DeselectAll();
+ bool SetCursorAtPoint( const Point& rPointPixel );
+ bool IsSelectionAtPoint( const Point& rPosPixel );
+ bool IsInSelection( const EditPaM& rPaM );
+
+ bool IsSelectionFullPara() const;
+ bool IsSelectionInSinglePara() const;
+
+ void SetAnchorMode( EEAnchorMode eMode );
+ EEAnchorMode GetAnchorMode() const { return eAnchorMode; }
+ void CalcAnchorPoint();
+ void RecalcOutputArea();
+
+ tools::Rectangle GetEditCursor() const;
+
+ void ShowCursor( bool bGotoCursor, bool bForceVisCursor );
+ Pair Scroll( tools::Long ndX, tools::Long ndY, ScrollRangeCheck nRangeCheck = ScrollRangeCheck::NoNegative );
+
+ void SetInsertMode( bool bInsert );
+ bool IsInsertMode() const { return !( nControl & EVControlBits::OVERWRITE ); }
+
+ bool IsPasteEnabled() const { return bool( nControl & EVControlBits::ENABLEPASTE ); }
+
+ bool DoSingleLinePaste() const { return bool( nControl & EVControlBits::SINGLELINEPASTE ); }
+ bool DoAutoScroll() const { return bool( nControl & EVControlBits::AUTOSCROLL ); }
+ bool DoAutoSize() const { return bool( nControl & EVControlBits::AUTOSIZE ); }
+ bool DoAutoWidth() const { return bool( nControl & EVControlBits::AUTOSIZEX); }
+ bool DoAutoHeight() const { return bool( nControl & EVControlBits::AUTOSIZEY); }
+ bool DoInvalidateMore() const { return bool( nControl & EVControlBits::INVONEMORE ); }
+
+ void SetBackgroundColor( const Color& rColor );
+ const Color& GetBackgroundColor() const;
+
+ /// Informs this edit view about which view shell contains it.
+ void RegisterViewShell(OutlinerViewShell* pViewShell);
+ const OutlinerViewShell* GetViewShell() const;
+ /// Informs this edit view about which other shell listens to it.
+ void RegisterOtherShell(OutlinerViewShell* pViewShell);
+
+ bool IsWrongSpelledWord( const EditPaM& rPaM, bool bMarkIfWrong );
+ OUString SpellIgnoreWord();
+
+ const SvxFieldItem* GetField( const Point& rPos, sal_Int32* pPara, sal_Int32* pPos ) const;
+ void DeleteSelected();
+
+ // If possible invalidate more than OutputArea, for the DrawingEngine text frame
+ void SetInvalidateMore( sal_uInt16 nPixel ) { nInvMore = nPixel; }
+ sal_uInt16 GetInvalidateMore() const { return static_cast<sal_uInt16>(nInvMore); }
+
+ void InitLOKSpecialPositioning(MapUnit eUnit, const tools::Rectangle& rOutputArea,
+ const Point& rVisDocStartPos);
+ void SetLOKSpecialOutputArea(const tools::Rectangle& rOutputArea);
+ const tools::Rectangle & GetLOKSpecialOutputArea() const;
+ void SetLOKSpecialVisArea(const tools::Rectangle& rVisArea);
+ tools::Rectangle GetLOKSpecialVisArea() const;
+ bool HasLOKSpecialPositioning() const;
+
+ void SetLOKSpecialFlags(LOKSpecialFlags eFlags);
+
+ void SuppressLOKMessages(bool bSet) { mbSuppressLOKMessages = bSet; }
+ bool IsSuppressLOKMessages() const { return mbSuppressLOKMessages; }
+
+ void SetNegativeX(bool bSet) { mbNegativeX = bSet; }
+ bool IsNegativeX() const { return mbNegativeX; }
+};
+
+
+// ImpEditEngine
+
+
+class ImpEditEngine : public SfxListener, public svl::StyleSheetUser
+{
+ friend class EditEngine;
+
+ typedef EditEngine::ViewsType ViewsType;
+
+private:
+ std::shared_ptr<editeng::SharedVclResources> pSharedVCL;
+
+ // Document Specific data ...
+ ParaPortionList maParaPortionList; // Formatting
+ Size maPaperSize; // Layout
+ Size maMinAutoPaperSize; // Layout ?
+ Size maMaxAutoPaperSize; // Layout ?
+ tools::Long mnMinColumnWrapHeight = 0; // Corresponds to graphic object height
+ EditDoc maEditDoc; // Document content
+
+ // Engine Specific data ...
+ EditEngine* pEditEngine;
+ ViewsType aEditViews;
+ EditView* pActiveView;
+ std::unique_ptr<TextRanger> pTextRanger;
+
+ SfxStyleSheetPool* pStylePool;
+ SfxItemPool* pTextObjectPool;
+
+ VclPtr< VirtualDevice> pVirtDev;
+ VclPtr< OutputDevice > pRefDev;
+ VclPtr<VirtualDevice> mpOwnDev;
+
+ svtools::ColorConfig maColorConfig;
+
+ mutable std::unique_ptr<SfxItemSet> pEmptyItemSet;
+ EditUndoManager* pUndoManager;
+ std::optional<ESelection> moUndoMarkSelection;
+
+ std::unique_ptr<ImplIMEInfos> mpIMEInfos;
+
+ OUString aWordDelimiters;
+
+ EditSelFunctionSet aSelFuncSet;
+ EditSelectionEngine aSelEngine;
+
+ Color maBackgroundColor;
+
+ double mfFontScaleX;
+ double mfFontScaleY;
+ double mfSpacingScaleX;
+ double mfSpacingScaleY;
+ bool mbRoundToNearestPt;
+
+ CharCompressType mnAsianCompressionMode;
+
+ EEHorizontalTextDirection eDefaultHorizontalTextDirection;
+
+ sal_Int32 mnBigTextObjectStart;
+ css::uno::Reference< css::linguistic2::XSpellChecker1 > xSpeller;
+ css::uno::Reference< css::linguistic2::XHyphenator > xHyphenator;
+ std::unique_ptr<SpellInfo> pSpellInfo;
+ mutable css::uno::Reference < css::i18n::XBreakIterator > xBI;
+ mutable css::uno::Reference < css::i18n::XExtendedInputSequenceChecker > xISC;
+
+ std::unique_ptr<ConvInfo> pConvInfo;
+
+ OUString maAutoCompleteText;
+
+ InternalEditStatus maStatus;
+
+ LanguageType meDefLanguage;
+
+ OnDemandLocaleDataWrapper xLocaleDataWrapper;
+ OnDemandTransliterationWrapper xTransliterationWrapper;
+
+ // For Formatting / Update...
+ std::vector<std::unique_ptr<DeletedNodeInfo> > aDeletedNodes;
+ tools::Rectangle aInvalidRect;
+ tools::Long nCurTextHeight;
+ tools::Long nCurTextHeightNTP; // without trailing empty paragraphs
+ sal_uInt16 nOnePixelInRef;
+
+ IdleFormattter aIdleFormatter;
+
+ Timer aOnlineSpellTimer;
+
+ // For Chaining
+ sal_Int32 mnOverflowingPara = -1;
+ sal_Int32 mnOverflowingLine = -1;
+ bool mbNeedsChainingHandling = false;
+
+ sal_Int16 mnColumns = 1;
+ sal_Int32 mnColumnSpacing = 0;
+
+ // If it is detected at one point that the StatusHdl has to be called, but
+ // this should not happen immediately (critical section):
+ Timer aStatusTimer;
+ Size aLOKSpecialPaperSize;
+
+ Link<EditStatus&,void> aStatusHdlLink;
+ Link<EENotify&,void> aNotifyHdl;
+ Link<HtmlImportInfo&,void> aHtmlImportHdl;
+ Link<RtfImportInfo&,void> aRtfImportHdl;
+ Link<MoveParagraphsInfo&,void> aBeginMovingParagraphsHdl;
+ Link<MoveParagraphsInfo&,void> aEndMovingParagraphsHdl;
+ Link<PasteOrDropInfos&,void> aBeginPasteOrDropHdl;
+ Link<PasteOrDropInfos&,void> aEndPasteOrDropHdl;
+ Link<LinkParamNone*,void> aModifyHdl;
+ Link<EditView*,void> maBeginDropHdl;
+ Link<EditView*,void> maEndDropHdl;
+
+ bool mbKernAsianPunctuation : 1;
+ bool mbAddExtLeading : 1;
+ bool mbIsFormatting : 1;
+ bool mbFormatted : 1;
+ bool mbInSelection : 1;
+ bool mbIsInUndo : 1;
+ bool mbUpdateLayout : 1;
+ bool mbUndoEnabled : 1;
+ bool mbDowning : 1;
+ bool mbUseAutoColor : 1;
+ bool mbForceAutoColor : 1;
+ bool mbCallParaInsertedOrDeleted : 1;
+ bool mbFirstWordCapitalization : 1; // specifies if auto-correction should capitalize the first word or not
+ bool mbLastTryMerge : 1;
+ bool mbReplaceLeadingSingleQuotationMark : 1;
+ bool mbSkipOutsideFormat : 1;
+ bool mbFuzzing : 1;
+
+ bool mbNbspRunNext; // can't be a bitfield as it is passed as bool&
+
+ // Methods...
+
+
+ void ParaAttribsChanged( ContentNode const * pNode, bool bIgnoreUndoCheck = false );
+ void TextModified();
+ void CalcHeight( ParaPortion* pPortion );
+
+ void InsertUndo( std::unique_ptr<EditUndo> pUndo, bool bTryMerge = false );
+ void ResetUndoManager();
+ bool HasUndoManager() const { return pUndoManager != nullptr; }
+
+ std::unique_ptr<EditUndoSetAttribs> CreateAttribUndo( EditSelection aSel, const SfxItemSet& rSet );
+
+ std::unique_ptr<EditTextObject> GetEmptyTextObject();
+
+ std::tuple<const ParaPortion*, const EditLine*, tools::Long> GetPortionAndLine(Point aDocPos);
+ EditPaM GetPaM( Point aDocPos, bool bSmart = true );
+ bool IsTextPos(const Point& rDocPos, sal_uInt16 nBorder);
+ tools::Long GetXPos(const ParaPortion* pParaPortion, const EditLine* pLine, sal_Int32 nIndex, bool bPreferPortionStart = false) const;
+ tools::Long GetPortionXOffset(const ParaPortion* pParaPortion, const EditLine* pLine, sal_Int32 nTextPortion) const;
+ sal_Int32 GetChar(const ParaPortion* pParaPortion, const EditLine* pLine, tools::Long nX, bool bSmart = true);
+ Range GetLineXPosStartEnd( const ParaPortion* pParaPortion, const EditLine* pLine ) const;
+
+ void ParaAttribsToCharAttribs( ContentNode* pNode );
+ void GetCharAttribs( sal_Int32 nPara, std::vector<EECharAttrib>& rLst ) const;
+
+ std::unique_ptr<EditTextObject>
+ CreateTextObject(EditSelection aSelection, SfxItemPool*, bool bAllowBigObjects = false, sal_Int32 nBigObjStart = 0);
+ EditSelection InsertTextObject( const EditTextObject&, EditPaM aPaM );
+ EditSelection PasteText( css::uno::Reference< css::datatransfer::XTransferable > const & rxDataObj, const OUString& rBaseURL, const EditPaM& rPaM, bool bUseSpecial, SotClipboardFormatId format = SotClipboardFormatId::NONE);
+
+ void CheckPageOverflow();
+
+ void Clear();
+ EditPaM RemoveText();
+ bool CreateLines( sal_Int32 nPara, sal_uInt32 nStartPosY );
+ void CreateAndInsertEmptyLine( ParaPortion* pParaPortion );
+ bool FinishCreateLines( ParaPortion* pParaPortion );
+ void CreateTextPortions( ParaPortion* pParaPortion, sal_Int32& rStartPos /*, sal_Bool bCreateBlockPortions */ );
+ void RecalcTextPortion( ParaPortion* pParaPortion, sal_Int32 nStartPos, sal_Int32 nNewChars );
+ sal_Int32 SplitTextPortion( ParaPortion* pParaPortion, sal_Int32 nPos, EditLine* pCurLine = nullptr );
+ void SeekCursor( ContentNode* pNode, sal_Int32 nPos, SvxFont& rFont, OutputDevice* pOut = nullptr );
+ void RecalcFormatterFontMetrics( FormatterFontMetric& rCurMetrics, SvxFont& rFont );
+ void CheckAutoPageSize();
+
+ void ImpBreakLine( ParaPortion* pParaPortion, EditLine* pLine, TextPortion const * pPortion, sal_Int32 nPortionStart, tools::Long nRemainingWidth, bool bCanHyphenate );
+ void ImpAdjustBlocks( ParaPortion* pParaPortion, EditLine* pLine, tools::Long nRemainingSpace );
+ EditPaM ImpConnectParagraphs( ContentNode* pLeft, ContentNode* pRight, bool bBackward = false );
+ EditPaM ImpDeleteSelection(const EditSelection& rCurSel);
+ EditPaM ImpInsertParaBreak( EditPaM& rPaM, bool bKeepEndingAttribs = true );
+ EditPaM ImpInsertParaBreak( const EditSelection& rEditSelection );
+ EditPaM ImpInsertText(const EditSelection& aCurEditSelection, const OUString& rStr);
+ EditPaM ImpInsertFeature(const EditSelection& rCurSel, const SfxPoolItem& rItem);
+ void ImpRemoveChars( const EditPaM& rPaM, sal_Int32 nChars );
+ void ImpRemoveParagraph( sal_Int32 nPara );
+ EditSelection ImpMoveParagraphs( Range aParagraphs, sal_Int32 nNewPos );
+
+ EditPaM ImpFastInsertText( EditPaM aPaM, const OUString& rStr );
+ EditPaM ImpFastInsertParagraph( sal_Int32 nPara );
+
+ bool ImpCheckRefMapMode();
+
+ bool ImplHasText() const;
+
+ void ImpFindKashidas( ContentNode* pNode, sal_Int32 nStart, sal_Int32 nEnd, std::vector<sal_Int32>& rArray );
+
+ void InsertContent( ContentNode* pNode, sal_Int32 nPos );
+ EditPaM SplitContent( sal_Int32 nNode, sal_Int32 nSepPos );
+ EditPaM ConnectContents( sal_Int32 nLeftNode, bool bBackward );
+
+ void ShowParagraph( sal_Int32 nParagraph, bool bShow );
+
+ EditPaM PageUp( const EditPaM& rPaM, EditView const * pView);
+ EditPaM PageDown( const EditPaM& rPaM, EditView const * pView);
+ EditPaM CursorUp( const EditPaM& rPaM, EditView const * pEditView );
+ EditPaM CursorDown( const EditPaM& rPaM, EditView const * pEditView );
+ EditPaM CursorLeft( const EditPaM& rPaM, sal_uInt16 nCharacterIteratorMode = css::i18n::CharacterIteratorMode::SKIPCELL );
+ EditPaM CursorRight( const EditPaM& rPaM, sal_uInt16 nCharacterIteratorMode = css::i18n::CharacterIteratorMode::SKIPCELL );
+ EditPaM CursorStartOfLine( const EditPaM& rPaM );
+ EditPaM CursorEndOfLine( const EditPaM& rPaM );
+ static EditPaM CursorStartOfParagraph( const EditPaM& rPaM );
+ static EditPaM CursorEndOfParagraph( const EditPaM& rPaM );
+ EditPaM CursorStartOfDoc();
+ EditPaM CursorEndOfDoc();
+ EditPaM WordLeft( const EditPaM& rPaM );
+ EditPaM WordRight( const EditPaM& rPaM, sal_Int16 nWordType = css::i18n::WordType::ANYWORD_IGNOREWHITESPACES );
+ EditPaM StartOfWord( const EditPaM& rPaM );
+ EditPaM EndOfWord( const EditPaM& rPaM );
+ EditSelection SelectWord( const EditSelection& rCurSelection, sal_Int16 nWordType = css::i18n::WordType::ANYWORD_IGNOREWHITESPACES, bool bAcceptStartOfWord = true );
+ EditSelection SelectSentence( const EditSelection& rCurSel ) const;
+ EditPaM CursorVisualLeftRight( EditView const * pEditView, const EditPaM& rPaM, sal_uInt16 nCharacterIteratorMode, bool bToLeft );
+ EditPaM CursorVisualStartEnd( EditView const * pEditView, const EditPaM& rPaM, bool bStart );
+
+
+ void InitScriptTypes( sal_Int32 nPara );
+ sal_uInt16 GetI18NScriptType( const EditPaM& rPaM, sal_Int32* pEndPos = nullptr ) const;
+ SvtScriptType GetItemScriptType( const EditSelection& rSel ) const;
+ bool IsScriptChange( const EditPaM& rPaM ) const;
+ bool HasScriptType( sal_Int32 nPara, sal_uInt16 nType ) const;
+
+ bool ImplCalcAsianCompression( ContentNode* pNode, TextPortion* pTextPortion, sal_Int32 nStartPos,
+ sal_Int32* pDXArray, sal_uInt16 n100thPercentFromMax, bool bManipulateDXArray );
+ void ImplExpandCompressedPortions( EditLine* pLine, ParaPortion* pParaPortion, tools::Long nRemainingWidth );
+
+ void ImplInitLayoutMode(OutputDevice& rOutDev, sal_Int32 nPara, sal_Int32 nIndex);
+ static LanguageType ImplCalcDigitLang(LanguageType eCurLang);
+ static void ImplInitDigitMode(OutputDevice& rOutDev, LanguageType eLang);
+ static OUString convertDigits(std::u16string_view rString, sal_Int32 nStt, sal_Int32 nLen, LanguageType eDigitLang);
+
+ EditPaM ReadText( SvStream& rInput, EditSelection aSel );
+ EditPaM ReadRTF( SvStream& rInput, EditSelection aSel );
+ EditPaM ReadXML( SvStream& rInput, EditSelection aSel );
+ EditPaM ReadHTML( SvStream& rInput, const OUString& rBaseURL, EditSelection aSel, SvKeyValueIterator* pHTTPHeaderAttrs );
+ ErrCode WriteText( SvStream& rOutput, EditSelection aSel );
+ ErrCode WriteRTF( SvStream& rOutput, EditSelection aSel );
+ void WriteXML(SvStream& rOutput, const EditSelection& rSel);
+
+ void WriteItemAsRTF( const SfxPoolItem& rItem, SvStream& rOutput, sal_Int32 nPara, sal_Int32 nPos,
+ std::vector<std::unique_ptr<SvxFontItem>>& rFontTable, SvxColorList& rColorList );
+ bool WriteItemListAsRTF( ItemList& rLst, SvStream& rOutput, sal_Int32 nPara, sal_Int32 nPos,
+ std::vector<std::unique_ptr<SvxFontItem>>& rFontTable, SvxColorList& rColorList );
+ sal_Int32 LogicToTwips( sal_Int32 n );
+
+ double scaleXSpacingValue(tools::Long nXValue) const
+ {
+ if (!maStatus.DoStretch() || mfSpacingScaleX == 100.0)
+ return nXValue;
+
+ return double(nXValue) * mfSpacingScaleX / 100.0;
+ }
+
+ double scaleYSpacingValue(sal_uInt16 nYValue) const
+ {
+ if (!maStatus.DoStretch() || mfSpacingScaleY == 100.0)
+ return nYValue;
+
+ return double(nYValue) * mfSpacingScaleY / 100.0;
+ }
+
+ double scaleYFontValue(sal_uInt16 nYValue) const
+ {
+ if (!maStatus.DoStretch() || (mfFontScaleY == 100.0))
+ return nYValue;
+
+ return double(nYValue) * mfFontScaleY / 100.0;
+ }
+
+ double scaleXFontValue(tools::Long nXValue) const
+ {
+ if (!maStatus.DoStretch() || (mfFontScaleX == 100.0))
+ return nXValue;
+
+ return double(nXValue) * mfFontScaleY / 100.0;
+ }
+
+ void setRoundToNearestPt(bool bRound) { mbRoundToNearestPt = bRound; }
+ double roundToNearestPt(double fInput) const;
+
+ ContentNode* GetPrevVisNode( ContentNode const * pCurNode );
+ ContentNode* GetNextVisNode( ContentNode const * pCurNode );
+
+ const ParaPortion* GetPrevVisPortion( const ParaPortion* pCurPortion ) const;
+ const ParaPortion* GetNextVisPortion( const ParaPortion* pCurPortion ) const;
+
+ void SetBackgroundColor( const Color& rColor ) { maBackgroundColor = rColor; }
+ const Color& GetBackgroundColor() const { return maBackgroundColor; }
+
+ tools::Long CalcVertLineSpacing(Point& rStartPos) const;
+
+ Color GetAutoColor() const;
+ void EnableAutoColor( bool b ) { mbUseAutoColor = b; }
+ bool IsAutoColorEnabled() const { return mbUseAutoColor; }
+ void ForceAutoColor( bool b ) { mbForceAutoColor = b; }
+ bool IsForceAutoColor() const { return mbForceAutoColor; }
+
+ inline VirtualDevice* GetVirtualDevice( const MapMode& rMapMode, DrawModeFlags nDrawMode );
+ void EraseVirtualDevice() { pVirtDev.disposeAndClear(); }
+
+ DECL_LINK( StatusTimerHdl, Timer *, void);
+ DECL_LINK( IdleFormatHdl, Timer *, void);
+ DECL_LINK( OnlineSpellHdl, Timer *, void);
+ DECL_LINK( DocModified, LinkParamNone*, void );
+
+ void CheckIdleFormatter();
+
+ inline const ParaPortion* FindParaPortion( const ContentNode* pNode ) const;
+ inline ParaPortion* FindParaPortion( ContentNode const * pNode );
+
+ css::uno::Reference< css::datatransfer::XTransferable > CreateTransferable( const EditSelection& rSelection );
+
+ void SetValidPaperSize( const Size& rSz );
+
+ css::uno::Reference < css::i18n::XBreakIterator > const & ImplGetBreakIterator() const;
+ css::uno::Reference < css::i18n::XExtendedInputSequenceChecker > const & ImplGetInputSequenceChecker() const;
+
+ void ImplUpdateOverflowingParaNum(tools::Long);
+ void ImplUpdateOverflowingLineNum(tools::Long, sal_uInt32, tools::Long);
+
+ void CreateSpellInfo( bool bMultipleDocs );
+ /// Obtains a view shell ID from the active EditView.
+ ViewShellId CreateViewShellId();
+
+ ImpEditEngine(EditEngine* pEditEngine, SfxItemPool* pPool);
+ void InitDoc(bool bKeepParaAttribs);
+ EditDoc& GetEditDoc() { return maEditDoc; }
+ const EditDoc& GetEditDoc() const { return maEditDoc; }
+
+ const ParaPortionList& GetParaPortions() const { return maParaPortionList; }
+ ParaPortionList& GetParaPortions() { return maParaPortionList; }
+
+ tools::Long Calc1ColumnTextHeight(tools::Long* pHeightNTP);
+
+ void IdleFormatAndLayout(EditView* pCurView) { aIdleFormatter.DoIdleFormat(pCurView); }
+
+protected:
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+public:
+ virtual ~ImpEditEngine() override;
+ ImpEditEngine(const ImpEditEngine&) = delete;
+ ImpEditEngine& operator=(const ImpEditEngine&) = delete;
+
+ inline EditUndoManager& GetUndoManager();
+ inline EditUndoManager* SetUndoManager(EditUndoManager* pNew);
+
+ // @return the previous bUpdateLayout state
+ bool SetUpdateLayout( bool bUpdate, EditView* pCurView = nullptr, bool bForceUpdate = false );
+ bool IsUpdateLayout() const { return mbUpdateLayout; }
+
+ ViewsType& GetEditViews() { return aEditViews; }
+ const ViewsType& GetEditViews() const { return aEditViews; }
+
+ const Size& GetPaperSize() const { return maPaperSize; }
+ void SetPaperSize(const Size& rSize) { maPaperSize = rSize; }
+
+ void SetVertical( bool bVertical);
+ bool IsEffectivelyVertical() const { return GetEditDoc().IsEffectivelyVertical(); }
+ bool IsTopToBottom() const { return GetEditDoc().IsTopToBottom(); }
+ bool GetVertical() const { return GetEditDoc().GetVertical(); }
+ void SetRotation( TextRotation nRotation);
+ TextRotation GetRotation() const { return GetEditDoc().GetRotation(); }
+
+ void SetTextColumns(sal_Int16 nColumns, sal_Int32 nSpacing);
+
+ bool IsPageOverflow( ) const;
+
+ void SetFixedCellHeight( bool bUseFixedCellHeight );
+ bool IsFixedCellHeight() const { return GetEditDoc().IsFixedCellHeight(); }
+
+ void SetDefaultHorizontalTextDirection( EEHorizontalTextDirection eHTextDir ) { eDefaultHorizontalTextDirection = eHTextDir; }
+ EEHorizontalTextDirection GetDefaultHorizontalTextDirection() const { return eDefaultHorizontalTextDirection; }
+
+
+ void InitWritingDirections( sal_Int32 nPara );
+ bool IsRightToLeft( sal_Int32 nPara ) const;
+ sal_uInt8 GetRightToLeft( sal_Int32 nPara, sal_Int32 nChar, sal_Int32* pStart = nullptr, sal_Int32* pEnd = nullptr );
+ bool HasDifferentRTLLevels( const ContentNode* pNode );
+
+ void SetTextRanger( std::unique_ptr<TextRanger> pRanger );
+ TextRanger* GetTextRanger() const { return pTextRanger.get(); }
+
+ const Size& GetMinAutoPaperSize() const { return maMinAutoPaperSize; }
+ void SetMinAutoPaperSize(const Size& rSize) { maMinAutoPaperSize = rSize; }
+
+ const Size& GetMaxAutoPaperSize() const { return maMaxAutoPaperSize; }
+ void SetMaxAutoPaperSize(const Size& rSize) { maMaxAutoPaperSize = rSize; }
+
+ void SetMinColumnWrapHeight(tools::Long nVal) { mnMinColumnWrapHeight = nVal; }
+
+ void FormatDoc();
+ void FormatFullDoc();
+ void UpdateViews( EditView* pCurView = nullptr );
+ void Paint( ImpEditView* pView, const tools::Rectangle& rRect, OutputDevice* pTargetDevice );
+ void Paint(OutputDevice& rOutDev, tools::Rectangle aClipRect, Point aStartPos, bool bStripOnly = false, Degree10 nOrientation = 0_deg10);
+
+ bool MouseButtonUp( const MouseEvent& rMouseEvent, EditView* pView );
+ bool MouseButtonDown( const MouseEvent& rMouseEvent, EditView* pView );
+ void ReleaseMouse();
+ bool MouseMove( const MouseEvent& rMouseEvent, EditView* pView );
+ bool Command(const CommandEvent& rCEvt, EditView* pView);
+
+ EditSelectionEngine& GetSelEngine() { return aSelEngine; }
+ OUString GetSelected( const EditSelection& rSel ) const;
+
+ const SfxItemSet& GetEmptyItemSet() const;
+
+ void UpdateSelections();
+
+ void EnableUndo( bool bEnable );
+ bool IsUndoEnabled() const { return mbUndoEnabled; }
+ void SetUndoMode( bool b ) { mbIsInUndo = b; }
+ bool IsInUndo() const { return mbIsInUndo; }
+
+ void SetCallParaInsertedOrDeleted( bool b ) { mbCallParaInsertedOrDeleted = b; }
+ bool IsCallParaInsertedOrDeleted() const { return mbCallParaInsertedOrDeleted; }
+
+ bool IsFormatted() const { return mbFormatted; }
+ bool IsFormatting() const { return mbIsFormatting; }
+
+ void SetText(const OUString& rText);
+ EditPaM DeleteSelected(const EditSelection& rEditSelection);
+ EditPaM InsertTextUserInput( const EditSelection& rCurEditSelection, sal_Unicode c, bool bOverwrite );
+ EditPaM InsertText(const EditSelection& aCurEditSelection, const OUString& rStr);
+ EditPaM AutoCorrect( const EditSelection& rCurEditSelection, sal_Unicode c, bool bOverwrite, vcl::Window const * pFrameWin = nullptr );
+ EditPaM DeleteLeftOrRight( const EditSelection& rEditSelection, sal_uInt8 nMode, DeleteMode nDelMode );
+ EditPaM InsertParaBreak(const EditSelection& rEditSelection);
+ EditPaM InsertLineBreak(const EditSelection& aEditSelection);
+ EditPaM InsertTab(const EditSelection& rEditSelection);
+ EditPaM InsertField(const EditSelection& rCurSel, const SvxFieldItem& rFld);
+ bool UpdateFields();
+
+ EditPaM Read(SvStream& rInput, const OUString& rBaseURL, EETextFormat eFormat, const EditSelection& rSel, SvKeyValueIterator* pHTTPHeaderAttrs = nullptr);
+ void Write(SvStream& rOutput, EETextFormat eFormat, const EditSelection& rSel);
+
+ std::unique_ptr<EditTextObject> CreateTextObject();
+ std::unique_ptr<EditTextObject> CreateTextObject(const EditSelection& rSel);
+ void SetText( const EditTextObject& rTextObject );
+ EditSelection InsertText( const EditTextObject& rTextObject, EditSelection aSel );
+
+ EditSelection const & MoveCursor( const KeyEvent& rKeyEvent, EditView* pEditView );
+
+ EditSelection MoveParagraphs( Range aParagraphs, sal_Int32 nNewPos, EditView* pCurView );
+
+ tools::Long CalcTextHeight( tools::Long* pHeightNTP );
+ sal_uInt32 GetTextHeight() const;
+ sal_uInt32 GetTextHeightNTP() const;
+ sal_uInt32 CalcTextWidth( bool bIgnoreExtraSpace);
+ sal_uInt32 CalcParaWidth( sal_Int32 nParagraph, bool bIgnoreExtraSpace );
+ sal_uInt32 CalcLineWidth( ParaPortion* pPortion, EditLine* pLine, bool bIgnoreExtraSpace);
+ sal_Int32 GetLineCount( sal_Int32 nParagraph ) const;
+ sal_Int32 GetLineLen( sal_Int32 nParagraph, sal_Int32 nLine ) const;
+ void GetLineBoundaries( /*out*/sal_Int32& rStart, /*out*/sal_Int32& rEnd, sal_Int32 nParagraph, sal_Int32 nLine ) const;
+ sal_Int32 GetLineNumberAtIndex( sal_Int32 nPara, sal_Int32 nIndex ) const;
+ sal_uInt16 GetLineHeight( sal_Int32 nParagraph, sal_Int32 nLine );
+ sal_uInt32 GetParaHeight( sal_Int32 nParagraph );
+
+ SfxItemSet GetAttribs( sal_Int32 nPara, sal_Int32 nStart, sal_Int32 nEnd, GetAttribsFlags nFlags = GetAttribsFlags::ALL ) const;
+ SfxItemSet GetAttribs( EditSelection aSel, EditEngineAttribs nOnlyHardAttrib = EditEngineAttribs::All );
+ void SetAttribs( EditSelection aSel, const SfxItemSet& rSet, SetAttribsMode nSpecial = SetAttribsMode::NONE, bool bSetSelection = true );
+ void RemoveCharAttribs( EditSelection aSel, EERemoveParaAttribsMode eMode, sal_uInt16 nWhich );
+ void RemoveCharAttribs( sal_Int32 nPara, sal_uInt16 nWhich = 0, bool bRemoveFeatures = false );
+ void SetFlatMode( bool bFlat );
+
+ void SetParaAttribs( sal_Int32 nPara, const SfxItemSet& rSet );
+ const SfxItemSet& GetParaAttribs( sal_Int32 nPara ) const;
+
+ bool HasParaAttrib( sal_Int32 nPara, sal_uInt16 nWhich ) const;
+ const SfxPoolItem& GetParaAttrib( sal_Int32 nPara, sal_uInt16 nWhich ) const;
+ template<class T>
+ const T& GetParaAttrib( sal_Int32 nPara, TypedWhichId<T> nWhich ) const
+ {
+ return static_cast<const T&>(GetParaAttrib(nPara, sal_uInt16(nWhich)));
+ }
+
+ tools::Rectangle PaMtoEditCursor( EditPaM aPaM, GetCursorFlags nFlags = GetCursorFlags::NONE );
+ tools::Rectangle GetEditCursor(const ParaPortion* pPortion, const EditLine* pLine,
+ sal_Int32 nIndex, GetCursorFlags nFlags);
+
+ bool IsModified() const { return maEditDoc.IsModified(); }
+ void SetModifyFlag(bool b) { maEditDoc.SetModified( b ); }
+ void SetModifyHdl( const Link<LinkParamNone*,void>& rLink ) { aModifyHdl = rLink; }
+
+ bool IsInSelectionMode() const { return mbInSelection; }
+
+// For Undo/Redo
+ void Undo( EditView* pView );
+ void Redo( EditView* pView );
+
+// OV-Special
+ void InvalidateFromParagraph( sal_Int32 nFirstInvPara );
+ EditPaM InsertParagraph( sal_Int32 nPara );
+ std::optional<EditSelection> SelectParagraph( sal_Int32 nPara );
+
+ void SetStatusEventHdl( const Link<EditStatus&, void>& rLink ) { aStatusHdlLink = rLink; }
+ const Link<EditStatus&,void>& GetStatusEventHdl() const { return aStatusHdlLink; }
+
+ void SetNotifyHdl( const Link<EENotify&,void>& rLink ) { aNotifyHdl = rLink; }
+ const Link<EENotify&,void>& GetNotifyHdl() const { return aNotifyHdl; }
+
+ void FormatAndLayout( EditView* pCurView = nullptr, bool bCalledFromUndo = false );
+
+ const svtools::ColorConfig& GetColorConfig() const { return maColorConfig; }
+ static bool IsVisualCursorTravelingEnabled();
+ static bool DoVisualCursorTraveling();
+
+ EditSelection ConvertSelection( sal_Int32 nStartPara, sal_Int32 nStartPos, sal_Int32 nEndPara, sal_Int32 nEndPos );
+ inline EPaM CreateEPaM( const EditPaM& rPaM ) const;
+ inline EditPaM CreateEditPaM( const EPaM& rEPaM );
+ inline ESelection CreateESel( const EditSelection& rSel ) const;
+ inline EditSelection CreateSel( const ESelection& rSel );
+
+ void SetStyleSheetPool( SfxStyleSheetPool* pSPool );
+ SfxStyleSheetPool* GetStyleSheetPool() const { return pStylePool; }
+
+ void SetStyleSheet( EditSelection aSel, SfxStyleSheet* pStyle );
+ void SetStyleSheet( sal_Int32 nPara, SfxStyleSheet* pStyle );
+ const SfxStyleSheet* GetStyleSheet( sal_Int32 nPara ) const;
+ SfxStyleSheet* GetStyleSheet( sal_Int32 nPara );
+
+ void UpdateParagraphsWithStyleSheet( SfxStyleSheet* pStyle );
+ void RemoveStyleFromParagraphs( SfxStyleSheet const * pStyle );
+
+ bool isUsedByModel() const override { return true; }
+
+ OutputDevice* GetRefDevice() const { return pRefDev.get(); }
+ void SetRefDevice( OutputDevice* pRefDef );
+
+ const MapMode& GetRefMapMode() const { return pRefDev->GetMapMode(); }
+ void SetRefMapMode( const MapMode& rMapMode );
+
+ InternalEditStatus& GetStatus() { return maStatus; }
+ void CallStatusHdl();
+ void DelayedCallStatusHdl() { aStatusTimer.Start(); }
+
+ void UndoActionStart( sal_uInt16 nId );
+ void UndoActionStart( sal_uInt16 nId, const ESelection& rSel );
+ void UndoActionEnd();
+
+ EditView* GetActiveView() const { return pActiveView; }
+ void SetActiveView( EditView* pView );
+
+ css::uno::Reference< css::linguistic2::XSpellChecker1 > const &
+ GetSpeller();
+ void SetSpeller( css::uno::Reference< css::linguistic2::XSpellChecker1 > const &xSpl )
+ { xSpeller = xSpl; }
+ const css::uno::Reference< css::linguistic2::XHyphenator >&
+ GetHyphenator() const { return xHyphenator; }
+ void SetHyphenator( css::uno::Reference< css::linguistic2::XHyphenator > const &xHyph )
+ { xHyphenator = xHyph; }
+
+ void GetAllMisspellRanges( std::vector<editeng::MisspellRanges>& rRanges ) const;
+ void SetAllMisspellRanges( const std::vector<editeng::MisspellRanges>& rRanges );
+
+ SpellInfo* GetSpellInfo() const { return pSpellInfo.get(); }
+
+ void SetDefaultLanguage(LanguageType eLang) { meDefLanguage = eLang; }
+ LanguageType GetDefaultLanguage() const { return meDefLanguage; }
+
+ editeng::LanguageSpan GetLanguage( const EditPaM& rPaM, sal_Int32* pEndPos = nullptr ) const;
+ css::lang::Locale GetLocale( const EditPaM& rPaM ) const;
+
+ void DoOnlineSpelling( ContentNode* pThisNodeOnly = nullptr, bool bSpellAtCursorPos = false, bool bInterruptible = true );
+ EESpellState Spell(EditView* pEditView, weld::Widget* pDialogParent, bool bMultipleDoc);
+ EESpellState HasSpellErrors();
+ void ClearSpellErrors();
+ EESpellState StartThesaurus(EditView* pEditView, weld::Widget* pDialogParent);
+ css::uno::Reference< css::linguistic2::XSpellAlternatives >
+ ImpSpell( EditView* pEditView );
+
+ // text conversion functions
+ void Convert(EditView* pEditView, weld::Widget* pDialogParent, LanguageType nSrcLang, LanguageType nDestLang, const vcl::Font *pDestFont, sal_Int32 nOptions, bool bIsInteractive, bool bMultipleDoc);
+ void ImpConvert( OUString &rConvTxt, LanguageType &rConvTxtLang, EditView* pEditView, LanguageType nSrcLang, const ESelection &rConvRange,
+ bool bAllowImplicitChangesForNotConvertibleText, LanguageType nTargetLang, const vcl::Font *pTargetFont );
+ ConvInfo * GetConvInfo() const { return pConvInfo.get(); }
+ bool HasConvertibleTextPortion( LanguageType nLang );
+ void SetLanguageAndFont( const ESelection &rESel,
+ LanguageType nLang, sal_uInt16 nLangWhichId,
+ const vcl::Font *pFont, sal_uInt16 nFontWhichId );
+
+ // returns true if input sequence checking should be applied
+ bool IsInputSequenceCheckingRequired( sal_Unicode nChar, const EditSelection& rCurSel ) const;
+
+ //find the next error within the given selection - forward only!
+ css::uno::Reference< css::linguistic2::XSpellAlternatives >
+ ImpFindNextError(EditSelection& rSelection);
+ //spell and return a sentence
+ bool SpellSentence(EditView const & rView, svx::SpellPortions& rToFill );
+ //put spelling back to start of current sentence - needed after switch of grammar support
+ void PutSpellingToSentenceStart( EditView const & rEditView );
+ //applies a changed sentence
+ void ApplyChangedSentence(EditView const & rEditView, const svx::SpellPortions& rNewPortions, bool bRecheck );
+ //adds one or more portions of text to the SpellPortions depending on language changes
+ void AddPortionIterated(
+ EditView const & rEditView,
+ const EditSelection &rSel,
+ const css::uno::Reference< css::linguistic2::XSpellAlternatives >& xAlt,
+ svx::SpellPortions& rToFill);
+ //adds one portion to the SpellPortions
+ void AddPortion(
+ const EditSelection &rSel,
+ const css::uno::Reference< css::linguistic2::XSpellAlternatives >& xAlt,
+ svx::SpellPortions& rToFill,
+ bool bIsField );
+
+ bool Search( const SvxSearchItem& rSearchItem, EditView* pView );
+ bool ImpSearch( const SvxSearchItem& rSearchItem, const EditSelection& rSearchSelection, const EditPaM& rStartPos, EditSelection& rFoundSel );
+ sal_Int32 StartSearchAndReplace( EditView* pEditView, const SvxSearchItem& rSearchItem );
+ bool HasText( const SvxSearchItem& rSearchItem );
+
+ void SetEditTextObjectPool( SfxItemPool* pP ) { pTextObjectPool = pP; }
+ SfxItemPool* GetEditTextObjectPool() const { return pTextObjectPool; }
+
+ const SvxNumberFormat * GetNumberFormat( const ContentNode* pNode ) const;
+ sal_Int32 GetSpaceBeforeAndMinLabelWidth( const ContentNode *pNode, sal_Int32 *pnSpaceBefore = nullptr, sal_Int32 *pnMinLabelWidth = nullptr ) const;
+
+ const SvxLRSpaceItem& GetLRSpaceItem( ContentNode* pNode );
+ SvxAdjust GetJustification( sal_Int32 nPara ) const;
+ SvxCellJustifyMethod GetJustifyMethod( sal_Int32 nPara ) const;
+ SvxCellVerJustify GetVerJustification( sal_Int32 nPara ) const;
+
+ void setScale(double fFontScaleX, double fFontScaleY, double fSpacingScaleX, double fSpacingScaleY);
+
+ void getFontScale(double& rX, double& rY) const
+ {
+ rX = mfFontScaleX;
+ rY = mfFontScaleY;
+ }
+
+ void getSpacingScale(double& rX, double& rY) const
+ {
+ rX = mfSpacingScaleX;
+ rY = mfSpacingScaleY;
+ }
+
+ sal_Int32 GetBigTextObjectStart() const { return mnBigTextObjectStart; }
+
+ EditEngine* GetEditEnginePtr() const { return pEditEngine; }
+
+ void StartOnlineSpellTimer() { aOnlineSpellTimer.Start(); }
+ void StopOnlineSpellTimer() { aOnlineSpellTimer.Stop(); }
+
+ const OUString& GetAutoCompleteText() const { return maAutoCompleteText; }
+ void SetAutoCompleteText(const OUString& rStr, bool bUpdateTipWindow);
+
+ EditSelection TransliterateText( const EditSelection& rSelection, TransliterationFlags nTransliterationMode );
+ short ReplaceTextOnly( ContentNode* pNode, sal_Int32 nCurrentStart, std::u16string_view rText, const css::uno::Sequence< sal_Int32 >& rOffsets );
+
+ void SetAsianCompressionMode( CharCompressType n );
+ CharCompressType GetAsianCompressionMode() const { return mnAsianCompressionMode; }
+
+ void SetKernAsianPunctuation( bool b );
+ bool IsKernAsianPunctuation() const { return mbKernAsianPunctuation; }
+
+ sal_Int32 GetOverflowingParaNum() const { return mnOverflowingPara; }
+ sal_Int32 GetOverflowingLineNum() const { return mnOverflowingLine; }
+ void ClearOverflowingParaNum() { mnOverflowingPara = -1; }
+
+
+ void SetAddExtLeading( bool b );
+ bool IsAddExtLeading() const { return mbAddExtLeading; }
+
+ static std::shared_ptr<SvxForbiddenCharactersTable> const & GetForbiddenCharsTable();
+ static void SetForbiddenCharsTable( const std::shared_ptr<SvxForbiddenCharactersTable>& xForbiddenChars );
+
+ /** sets a link that is called at the beginning of a drag operation at an edit view */
+ void SetBeginDropHdl( const Link<EditView*,void>& rLink ) { maBeginDropHdl = rLink; }
+ const Link<EditView*,void>& GetBeginDropHdl() const { return maBeginDropHdl; }
+
+ /** sets a link that is called at the end of a drag operation at an edit view */
+ void SetEndDropHdl( const Link<EditView*,void>& rLink ) { maEndDropHdl = rLink; }
+ const Link<EditView*,void>& GetEndDropHdl() const { return maEndDropHdl; }
+
+ /// specifies if auto-correction should capitalize the first word or not (default is on)
+ void SetFirstWordCapitalization( bool bCapitalize ) { mbFirstWordCapitalization = bCapitalize; }
+ bool IsFirstWordCapitalization() const { return mbFirstWordCapitalization; }
+
+ /** specifies if auto-correction should replace a leading single quotation
+ mark (apostrophe) or not (default is on) */
+ void SetReplaceLeadingSingleQuotationMark( bool bReplace ) { mbReplaceLeadingSingleQuotationMark = bReplace; }
+ bool IsReplaceLeadingSingleQuotationMark() const { return mbReplaceLeadingSingleQuotationMark; }
+
+ /** Whether last AutoCorrect inserted a NO-BREAK SPACE that may need to be removed again. */
+ bool IsNbspRunNext() const { return mbNbspRunNext; }
+
+ void EnableSkipOutsideFormat(bool set) { mbSkipOutsideFormat = set; }
+
+ void Dispose();
+ void SetLOKSpecialPaperSize(const Size& rSize) { aLOKSpecialPaperSize = rSize; }
+ const Size& GetLOKSpecialPaperSize() const { return aLOKSpecialPaperSize; }
+
+ enum class CallbackResult
+ {
+ Continue,
+ SkipThisPortion, // Do not call callback until next portion
+ Stop, // Stop iteration
+ };
+ struct LineAreaInfo
+ {
+ ParaPortion& rPortion; // Current ParaPortion
+ EditLine* pLine; // Current line, or nullptr for paragraph start
+ tools::Long nHeightNeededToNotWrap;
+ tools::Rectangle aArea; // The area for the line (or for rPortion's first line offset)
+ // Bottom coordinate *does not* belong to the area
+ sal_Int32 nPortion;
+ sal_Int32 nLine;
+ sal_Int16 nColumn; // Column number; when overflowing, equal to total number of columns
+ };
+ using IterateLinesAreasFunc = std::function<CallbackResult(const LineAreaInfo&)>;
+ enum IterFlag // bitmask
+ {
+ none = 0,
+ inclILS = 1, // rArea includes interline space
+ };
+
+ void IterateLineAreas(const IterateLinesAreasFunc& f, IterFlag eOptions);
+
+ tools::Long GetColumnWidth(const Size& rPaperSize) const;
+ Point MoveToNextLine(Point& rMovePos, tools::Long nLineHeight, sal_Int16& nColumn,
+ Point aOrigin, tools::Long* pnHeightNeededToNotWrap = nullptr) const;
+
+ tools::Long getWidthDirectionAware(const Size& sz) const;
+ tools::Long getHeightDirectionAware(const Size& sz) const;
+ void adjustXDirectionAware(Point& pt, tools::Long x) const;
+ void adjustYDirectionAware(Point& pt, tools::Long y) const;
+ void setXDirectionAwareFrom(Point& ptDest, const Point& ptSrc) const;
+ void setYDirectionAwareFrom(Point& ptDest, const Point& ptSrc) const;
+ tools::Long getYOverflowDirectionAware(const Point& pt, const tools::Rectangle& rectMax) const;
+ bool isXOverflowDirectionAware(const Point& pt, const tools::Rectangle& rectMax) const;
+ // Offset of the rectangle's direction-aware corners in document coordinates
+ tools::Long getBottomDocOffset(const tools::Rectangle& rect) const;
+ Size getTopLeftDocOffset(const tools::Rectangle& rect) const;
+};
+
+inline EPaM ImpEditEngine::CreateEPaM( const EditPaM& rPaM ) const
+{
+ const ContentNode* pNode = rPaM.GetNode();
+ return EPaM(maEditDoc.GetPos(pNode), rPaM.GetIndex());
+}
+
+inline EditPaM ImpEditEngine::CreateEditPaM( const EPaM& rEPaM )
+{
+ DBG_ASSERT( rEPaM.nPara < maEditDoc.Count(), "CreateEditPaM: invalid paragraph" );
+ DBG_ASSERT( maEditDoc[ rEPaM.nPara ]->Len() >= rEPaM.nIndex, "CreateEditPaM: invalid Index" );
+ return EditPaM( maEditDoc[ rEPaM.nPara], rEPaM.nIndex );
+}
+
+inline ESelection ImpEditEngine::CreateESel( const EditSelection& rSel ) const
+{
+ const ContentNode* pStartNode = rSel.Min().GetNode();
+ const ContentNode* pEndNode = rSel.Max().GetNode();
+ ESelection aESel;
+ aESel.nStartPara = maEditDoc.GetPos( pStartNode );
+ aESel.nStartPos = rSel.Min().GetIndex();
+ aESel.nEndPara = maEditDoc.GetPos( pEndNode );
+ aESel.nEndPos = rSel.Max().GetIndex();
+ return aESel;
+}
+
+inline EditSelection ImpEditEngine::CreateSel( const ESelection& rSel )
+{
+ DBG_ASSERT( rSel.nStartPara < maEditDoc.Count(), "CreateSel: invalid start paragraph" );
+ DBG_ASSERT( rSel.nEndPara < maEditDoc.Count(), "CreateSel: invalid end paragraph" );
+ EditSelection aSel;
+ aSel.Min().SetNode( maEditDoc[ rSel.nStartPara ] );
+ aSel.Min().SetIndex( rSel.nStartPos );
+ aSel.Max().SetNode( maEditDoc[ rSel.nEndPara ] );
+ aSel.Max().SetIndex( rSel.nEndPos );
+ DBG_ASSERT( !aSel.DbgIsBuggy( maEditDoc ), "CreateSel: incorrect selection!" );
+ return aSel;
+}
+
+inline VirtualDevice* ImpEditEngine::GetVirtualDevice( const MapMode& rMapMode, DrawModeFlags nDrawMode )
+{
+ if ( !pVirtDev )
+ pVirtDev = VclPtr<VirtualDevice>::Create();
+
+ if ( ( pVirtDev->GetMapMode().GetMapUnit() != rMapMode.GetMapUnit() ) ||
+ ( pVirtDev->GetMapMode().GetScaleX() != rMapMode.GetScaleX() ) ||
+ ( pVirtDev->GetMapMode().GetScaleY() != rMapMode.GetScaleY() ) )
+ {
+ MapMode aMapMode( rMapMode );
+ aMapMode.SetOrigin( Point( 0, 0 ) );
+ pVirtDev->SetMapMode( aMapMode );
+ }
+
+ pVirtDev->SetDrawMode( nDrawMode );
+
+ return pVirtDev;
+}
+
+inline EditUndoManager& ImpEditEngine::GetUndoManager()
+{
+ if ( !pUndoManager )
+ {
+ pUndoManager = new EditUndoManager();
+ pUndoManager->SetEditEngine(pEditEngine);
+ }
+ return *pUndoManager;
+}
+
+inline EditUndoManager* ImpEditEngine::SetUndoManager(EditUndoManager* pNew)
+{
+ EditUndoManager* pRetval = pUndoManager;
+
+ if(pUndoManager)
+ {
+ pUndoManager->SetEditEngine(nullptr);
+ }
+
+ pUndoManager = pNew;
+
+ if(pUndoManager)
+ {
+ pUndoManager->SetEditEngine(pEditEngine);
+ }
+
+ return pRetval;
+}
+
+inline const ParaPortion* ImpEditEngine::FindParaPortion( const ContentNode* pNode ) const
+{
+ sal_Int32 nPos = maEditDoc.GetPos( pNode );
+ DBG_ASSERT( nPos < GetParaPortions().Count(), "Portionloser Node?" );
+ return GetParaPortions()[ nPos ];
+}
+
+inline ParaPortion* ImpEditEngine::FindParaPortion( ContentNode const * pNode )
+{
+ sal_Int32 nPos = maEditDoc.GetPos( pNode );
+ DBG_ASSERT( nPos < GetParaPortions().Count(), "Portionloser Node?" );
+ return GetParaPortions()[ nPos ];
+}
+
+inline PointerStyle ImpEditView::GetPointer()
+{
+ if ( !mxPointer )
+ {
+ mxPointer = IsVertical() ? PointerStyle::TextVertical : PointerStyle::Text;
+ return *mxPointer;
+ }
+
+ if(PointerStyle::Text == *mxPointer && IsVertical())
+ {
+ mxPointer = PointerStyle::TextVertical;
+ }
+ else if(PointerStyle::TextVertical == *mxPointer && !IsVertical())
+ {
+ mxPointer = PointerStyle::Text;
+ }
+
+ return *mxPointer;
+}
+
+inline vcl::Cursor* ImpEditView::GetCursor()
+{
+ if ( !pCursor )
+ pCursor.reset( new vcl::Cursor );
+ return pCursor.get();
+}
+
+void ConvertItem( std::unique_ptr<SfxPoolItem>& rPoolItem, MapUnit eSourceUnit, MapUnit eDestUnit );
+void ConvertAndPutItems( SfxItemSet& rDest, const SfxItemSet& rSource, const MapUnit* pSourceUnit = nullptr, const MapUnit* pDestUnit = nullptr );
+AsianCompressionFlags GetCharTypeForCompression( sal_Unicode cChar );
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/impedit2.cxx b/editeng/source/editeng/impedit2.cxx
new file mode 100644
index 0000000000..4b8f0a6379
--- /dev/null
+++ b/editeng/source/editeng/impedit2.cxx
@@ -0,0 +1,4509 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+#include <editeng/lspcitem.hxx>
+#include <editeng/flditem.hxx>
+#include "impedit.hxx"
+#include <editeng/editeng.hxx>
+#include <editeng/editview.hxx>
+#include <eerdll2.hxx>
+#include <editeng/eerdll.hxx>
+#include <edtspell.hxx>
+#include "eeobj.hxx"
+#include <editeng/txtrange.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/mieclip.hxx>
+#include <svtools/colorcfg.hxx>
+#include <svl/ctloptions.hxx>
+#include <unotools/securityoptions.hxx>
+#include <editeng/acorrcfg.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <editeng/udlnitem.hxx>
+
+#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/i18n/InputSequenceCheckMode.hpp>
+#include <com/sun/star/system/SystemShellExecute.hpp>
+#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
+#include <com/sun/star/system/XSystemShellExecute.hpp>
+#include <com/sun/star/i18n/UnicodeType.hpp>
+
+#include <rtl/character.hxx>
+
+#include <sal/log.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <sot/exchange.hxx>
+#include <sot/formats.hxx>
+#include <svl/asiancfg.hxx>
+#include <svl/voiditem.hxx>
+#include <i18nutil/unicode.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <comphelper/flagguard.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <unotools/configmgr.hxx>
+
+#include <unicode/ubidi.h>
+#include <algorithm>
+#include <limits>
+#include <memory>
+#include <string_view>
+#include <fstream>
+
+using namespace ::com::sun::star;
+
+static sal_uInt16 lcl_CalcExtraSpace( const SvxLineSpacingItem& rLSItem )
+{
+ sal_uInt16 nExtra = 0;
+ if ( rLSItem.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Fix )
+ {
+ nExtra = rLSItem.GetInterLineSpace();
+ }
+
+ return nExtra;
+}
+
+ImpEditEngine::ImpEditEngine( EditEngine* pEE, SfxItemPool* pItemPool ) :
+ pSharedVCL(EditDLL::Get().GetSharedVclResources()),
+ maPaperSize( 0x7FFFFFFF, 0x7FFFFFFF ),
+ maMinAutoPaperSize( 0x0, 0x0 ),
+ maMaxAutoPaperSize( 0x7FFFFFFF, 0x7FFFFFFF ),
+ maEditDoc( pItemPool ),
+ pEditEngine(pEE),
+ pActiveView(nullptr),
+ pStylePool(nullptr),
+ pTextObjectPool(nullptr),
+ pUndoManager(nullptr),
+ aWordDelimiters(" .,;:-`'?!_=\"{}()[]"),
+ maBackgroundColor(COL_AUTO),
+ mfFontScaleX(100.0),
+ mfFontScaleY(100.0),
+ mfSpacingScaleX(100.0),
+ mfSpacingScaleY(100.0),
+ mbRoundToNearestPt(false),
+ mnAsianCompressionMode(CharCompressType::NONE),
+ eDefaultHorizontalTextDirection(EEHorizontalTextDirection::Default),
+ mnBigTextObjectStart(20),
+ meDefLanguage(LANGUAGE_DONTKNOW),
+ nCurTextHeight(0),
+ nCurTextHeightNTP(0),
+ aOnlineSpellTimer( "editeng::ImpEditEngine aOnlineSpellTimer" ),
+ aStatusTimer( "editeng::ImpEditEngine aStatusTimer" ),
+ mbKernAsianPunctuation(false),
+ mbAddExtLeading(false),
+ mbIsFormatting(false),
+ mbFormatted(false),
+ mbInSelection(false),
+ mbIsInUndo(false),
+ mbUpdateLayout(true),
+ mbUndoEnabled(true),
+ mbDowning(false),
+ mbUseAutoColor(true),
+ mbForceAutoColor(false),
+ mbCallParaInsertedOrDeleted(false),
+ mbFirstWordCapitalization(true),
+ mbLastTryMerge(false),
+ mbReplaceLeadingSingleQuotationMark(true),
+ mbSkipOutsideFormat(false),
+ mbFuzzing(utl::ConfigManager::IsFuzzing()),
+ mbNbspRunNext(false)
+{
+ maStatus.GetControlWord() = EEControlBits::USECHARATTRIBS | EEControlBits::DOIDLEFORMAT |
+ EEControlBits::PASTESPECIAL | EEControlBits::UNDOATTRIBS |
+ EEControlBits::ALLOWBIGOBJS | EEControlBits::RTFSTYLESHEETS |
+ EEControlBits::FORMAT100;
+
+ aSelEngine.SetFunctionSet( &aSelFuncSet );
+
+ aStatusTimer.SetTimeout( 200 );
+ aStatusTimer.SetInvokeHandler( LINK( this, ImpEditEngine, StatusTimerHdl ) );
+
+ aIdleFormatter.SetPriority( TaskPriority::REPAINT );
+ aIdleFormatter.SetInvokeHandler( LINK( this, ImpEditEngine, IdleFormatHdl ) );
+
+ aOnlineSpellTimer.SetTimeout( 100 );
+ aOnlineSpellTimer.SetInvokeHandler( LINK( this, ImpEditEngine, OnlineSpellHdl ) );
+
+ // Access data already from here on!
+ SetRefDevice( nullptr );
+ InitDoc( false );
+
+ mbCallParaInsertedOrDeleted = true;
+
+ maEditDoc.SetModifyHdl( LINK( this, ImpEditEngine, DocModified ) );
+ StartListening(*SfxGetpApp());
+}
+
+void ImpEditEngine::Dispose()
+{
+ SolarMutexGuard g;
+ auto pApp = SfxApplication::Get();
+ if(pApp)
+ EndListening(*pApp);
+ pVirtDev.disposeAndClear();
+ mpOwnDev.disposeAndClear();
+ pSharedVCL.reset();
+}
+
+ImpEditEngine::~ImpEditEngine()
+{
+ aStatusTimer.Stop();
+ aOnlineSpellTimer.Stop();
+ aIdleFormatter.Stop();
+
+ // Destroying templates may otherwise cause unnecessary formatting,
+ // when a parent template is destroyed.
+ // And this after the destruction of the data!
+ mbDowning = true;
+ SetUpdateLayout( false );
+
+ Dispose();
+ // it's only legal to delete the pUndoManager if it was created by
+ // ImpEditEngine; if it was set by SetUndoManager() it must be cleared
+ // before destroying the ImpEditEngine!
+ assert(!pUndoManager || typeid(*pUndoManager) == typeid(EditUndoManager));
+ delete pUndoManager;
+ pTextRanger.reset();
+ mpIMEInfos.reset();
+ pSpellInfo.reset();
+}
+
+void ImpEditEngine::SetRefDevice( OutputDevice* pRef )
+{
+ if (pRef)
+ pRefDev = pRef;
+ else
+ pRefDev = pSharedVCL->GetVirtualDevice();
+
+ nOnePixelInRef = static_cast<sal_uInt16>(pRefDev->PixelToLogic( Size( 1, 0 ) ).Width());
+
+ if ( IsFormatted() )
+ {
+ FormatFullDoc();
+ UpdateViews();
+ }
+}
+
+void ImpEditEngine::SetRefMapMode( const MapMode& rMapMode )
+{
+ if ( GetRefDevice()->GetMapMode() == rMapMode )
+ return;
+
+ mpOwnDev.disposeAndClear();
+ mpOwnDev = VclPtr<VirtualDevice>::Create();
+ pRefDev = mpOwnDev;
+ pRefDev->SetMapMode(MapMode(MapUnit::MapTwip));
+ SetRefDevice( pRefDev );
+
+ pRefDev->SetMapMode( rMapMode );
+ nOnePixelInRef = static_cast<sal_uInt16>(pRefDev->PixelToLogic( Size( 1, 0 ) ).Width());
+ if ( IsFormatted() )
+ {
+ FormatFullDoc();
+ UpdateViews();
+ }
+}
+
+void ImpEditEngine::InitDoc(bool bKeepParaAttribs)
+{
+ sal_Int32 nParas = maEditDoc.Count();
+ for ( sal_Int32 n = bKeepParaAttribs ? 1 : 0; n < nParas; n++ )
+ {
+ if ( maEditDoc[n]->GetStyleSheet() )
+ EndListening( *maEditDoc[n]->GetStyleSheet() );
+ }
+
+ if ( bKeepParaAttribs )
+ maEditDoc.RemoveText();
+ else
+ maEditDoc.Clear();
+
+ GetParaPortions().Reset();
+
+ GetParaPortions().Insert(0, std::make_unique<ParaPortion>( maEditDoc[0] ));
+
+ mbFormatted = false;
+
+ if ( IsCallParaInsertedOrDeleted() )
+ {
+ GetEditEnginePtr()->ParagraphDeleted( EE_PARA_ALL );
+ GetEditEnginePtr()->ParagraphInserted( 0 );
+ }
+
+ if ( GetStatus().DoOnlineSpelling() )
+ maEditDoc.GetObject( 0 )->CreateWrongList();
+}
+
+EditPaM ImpEditEngine::DeleteSelected(const EditSelection& rSel)
+{
+ EditPaM aPaM (ImpDeleteSelection(rSel));
+ return aPaM;
+}
+
+OUString ImpEditEngine::GetSelected( const EditSelection& rSel ) const
+{
+ if ( !rSel.HasRange() )
+ return OUString();
+
+ EditSelection aSel( rSel );
+ aSel.Adjust( maEditDoc );
+
+ ContentNode* pStartNode = aSel.Min().GetNode();
+ ContentNode* pEndNode = aSel.Max().GetNode();
+ sal_Int32 nStartNode = maEditDoc.GetPos( pStartNode );
+ sal_Int32 nEndNode = maEditDoc.GetPos( pEndNode );
+
+ OSL_ENSURE( nStartNode <= nEndNode, "Selection not sorted ?" );
+
+ OUStringBuffer aText(256);
+ const OUString aSep = EditDoc::GetSepStr( LINEEND_LF );
+
+ // iterate over the paragraphs ...
+ for ( sal_Int32 nNode = nStartNode; nNode <= nEndNode; nNode++ )
+ {
+ const ContentNode* pNode = maEditDoc.GetObject( nNode );
+ assert(pNode);
+
+ const sal_Int32 nStartPos = nNode==nStartNode ? aSel.Min().GetIndex() : 0;
+ const sal_Int32 nEndPos = nNode==nEndNode ? aSel.Max().GetIndex() : pNode->Len(); // can also be == nStart!
+
+ aText.append(EditDoc::GetParaAsString( pNode, nStartPos, nEndPos ));
+ if ( nNode < nEndNode )
+ aText.append(aSep);
+ }
+ return aText.makeStringAndClear();
+}
+
+bool ImpEditEngine::MouseButtonDown( const MouseEvent& rMEvt, EditView* pView )
+{
+ GetSelEngine().SetCurView( pView );
+ SetActiveView( pView );
+
+ if (!GetAutoCompleteText().isEmpty())
+ SetAutoCompleteText( OUString(), true );
+
+ GetSelEngine().SelMouseButtonDown( rMEvt );
+ // Special treatment
+ EditSelection aCurSel( pView->pImpEditView->GetEditSelection() );
+ if ( rMEvt.IsShift() )
+ return true;
+
+ if ( rMEvt.GetClicks() == 2 )
+ {
+ // So that the SelectionEngine knows about the anchor.
+ aSelEngine.CursorPosChanging( true, false );
+
+ EditSelection aNewSelection( SelectWord( aCurSel ) );
+ pView->pImpEditView->DrawSelectionXOR();
+ pView->pImpEditView->SetEditSelection( aNewSelection );
+ pView->pImpEditView->DrawSelectionXOR();
+ pView->ShowCursor();
+ }
+ else if ( rMEvt.GetClicks() == 3 )
+ {
+ // So that the SelectionEngine knows about the anchor.
+ aSelEngine.CursorPosChanging( true, false );
+
+ EditSelection aNewSelection( aCurSel );
+ aNewSelection.Min().SetIndex( 0 );
+ aNewSelection.Max().SetIndex( aCurSel.Min().GetNode()->Len() );
+ pView->pImpEditView->DrawSelectionXOR();
+ pView->pImpEditView->SetEditSelection( aNewSelection );
+ pView->pImpEditView->DrawSelectionXOR();
+ pView->ShowCursor();
+ }
+ return true;
+}
+
+bool ImpEditEngine::Command( const CommandEvent& rCEvt, EditView* pView )
+{
+ bool bConsumed = true;
+
+ GetSelEngine().SetCurView( pView );
+ SetActiveView( pView );
+ if ( rCEvt.GetCommand() == CommandEventId::StartExtTextInput )
+ {
+ pView->DeleteSelected();
+ mpIMEInfos.reset();
+ EditPaM aPaM = pView->GetImpEditView()->GetEditSelection().Max();
+ OUString aOldTextAfterStartPos = aPaM.GetNode()->Copy( aPaM.GetIndex() );
+ sal_Int32 nMax = aOldTextAfterStartPos.indexOf( CH_FEATURE );
+ if ( nMax != -1 ) // don't overwrite features!
+ aOldTextAfterStartPos = aOldTextAfterStartPos.copy( 0, nMax );
+ mpIMEInfos.reset( new ImplIMEInfos( aPaM, aOldTextAfterStartPos ) );
+ mpIMEInfos->bWasCursorOverwrite = !pView->IsInsertMode();
+ UndoActionStart( EDITUNDO_INSERT );
+ }
+ else if ( rCEvt.GetCommand() == CommandEventId::EndExtTextInput )
+ {
+ OSL_ENSURE( mpIMEInfos, "CommandEventId::EndExtTextInput => No start ?" );
+ if( mpIMEInfos )
+ {
+ // #102812# convert quotes in IME text
+ // works on the last input character, this is especially in Korean text often done
+ // quotes that are inside of the string are not replaced!
+ // Borrowed from sw: edtwin.cxx
+ if ( mpIMEInfos->nLen )
+ {
+ EditSelection aSel( mpIMEInfos->aPos );
+ aSel.Min().SetIndex( aSel.Min().GetIndex() + mpIMEInfos->nLen-1 );
+ aSel.Max().SetIndex( aSel.Max().GetIndex() + mpIMEInfos->nLen );
+ // #102812# convert quotes in IME text
+ // works on the last input character, this is especially in Korean text often done
+ // quotes that are inside of the string are not replaced!
+ // See also tdf#155350
+ const sal_Unicode nCharCode = aSel.Min().GetNode()->GetChar( aSel.Min().GetIndex() );
+ if ( ( GetStatus().DoAutoCorrect() ) && SvxAutoCorrect::IsAutoCorrectChar(nCharCode) )
+ {
+ aSel = DeleteSelected( aSel );
+ aSel = AutoCorrect( aSel, nCharCode, mpIMEInfos->bWasCursorOverwrite );
+ pView->pImpEditView->SetEditSelection( aSel );
+ }
+ }
+
+ ParaPortion* pPortion = FindParaPortion( mpIMEInfos->aPos.GetNode() );
+ if (pPortion)
+ pPortion->MarkSelectionInvalid( mpIMEInfos->aPos.GetIndex() );
+
+ bool bWasCursorOverwrite = mpIMEInfos->bWasCursorOverwrite;
+
+ mpIMEInfos.reset();
+
+ FormatAndLayout( pView );
+
+ pView->SetInsertMode( !bWasCursorOverwrite );
+ }
+ UndoActionEnd();
+ }
+ else if ( rCEvt.GetCommand() == CommandEventId::ExtTextInput )
+ {
+ OSL_ENSURE( mpIMEInfos, "CommandEventId::ExtTextInput => No Start ?" );
+ if( mpIMEInfos )
+ {
+ const CommandExtTextInputData* pData = rCEvt.GetExtTextInputData();
+
+ if ( !pData->IsOnlyCursorChanged() )
+ {
+ EditSelection aSel( mpIMEInfos->aPos );
+ aSel.Max().SetIndex( aSel.Max().GetIndex() + mpIMEInfos->nLen );
+ aSel = DeleteSelected( aSel );
+ aSel = ImpInsertText( aSel, pData->GetText() );
+
+ if ( mpIMEInfos->bWasCursorOverwrite )
+ {
+ sal_Int32 nOldIMETextLen = mpIMEInfos->nLen;
+ sal_Int32 nNewIMETextLen = pData->GetText().getLength();
+
+ if ( ( nOldIMETextLen > nNewIMETextLen ) &&
+ ( nNewIMETextLen < mpIMEInfos->aOldTextAfterStartPos.getLength() ) )
+ {
+ // restore old characters
+ sal_Int32 nRestore = nOldIMETextLen - nNewIMETextLen;
+ EditPaM aPaM( mpIMEInfos->aPos );
+ aPaM.SetIndex( aPaM.GetIndex() + nNewIMETextLen );
+ ImpInsertText( aPaM, mpIMEInfos->aOldTextAfterStartPos.copy( nNewIMETextLen, nRestore ) );
+ }
+ else if ( ( nOldIMETextLen < nNewIMETextLen ) &&
+ ( nOldIMETextLen < mpIMEInfos->aOldTextAfterStartPos.getLength() ) )
+ {
+ // overwrite
+ sal_Int32 nOverwrite = nNewIMETextLen - nOldIMETextLen;
+ if ( ( nOldIMETextLen + nOverwrite ) > mpIMEInfos->aOldTextAfterStartPos.getLength() )
+ nOverwrite = mpIMEInfos->aOldTextAfterStartPos.getLength() - nOldIMETextLen;
+ OSL_ENSURE( nOverwrite && (nOverwrite < 0xFF00), "IME Overwrite?!" );
+ EditPaM aPaM( mpIMEInfos->aPos );
+ aPaM.SetIndex( aPaM.GetIndex() + nNewIMETextLen );
+ EditSelection _aSel( aPaM );
+ _aSel.Max().SetIndex( _aSel.Max().GetIndex() + nOverwrite );
+ DeleteSelected( _aSel );
+ }
+ }
+ if ( pData->GetTextAttr() )
+ {
+ mpIMEInfos->CopyAttribs( pData->GetTextAttr(), pData->GetText().getLength() );
+ }
+ else
+ {
+ mpIMEInfos->DestroyAttribs();
+ mpIMEInfos->nLen = pData->GetText().getLength();
+ }
+
+ ParaPortion* pPortion = FindParaPortion( mpIMEInfos->aPos.GetNode() );
+ pPortion->MarkSelectionInvalid( mpIMEInfos->aPos.GetIndex() );
+ FormatAndLayout( pView );
+ }
+
+ EditSelection aNewSel = EditPaM( mpIMEInfos->aPos.GetNode(), mpIMEInfos->aPos.GetIndex()+pData->GetCursorPos() );
+ pView->SetSelection( CreateESel( aNewSel ) );
+ pView->SetInsertMode( !pData->IsCursorOverwrite() );
+
+ if ( pData->IsCursorVisible() )
+ pView->ShowCursor();
+ else
+ pView->HideCursor();
+ }
+ }
+ else if ( rCEvt.GetCommand() == CommandEventId::InputContextChange )
+ {
+ }
+ else if ( rCEvt.GetCommand() == CommandEventId::CursorPos )
+ {
+ if (mpIMEInfos)
+ {
+ EditPaM aPaM( pView->pImpEditView->GetEditSelection().Max() );
+ tools::Rectangle aR1 = PaMtoEditCursor( aPaM );
+
+ sal_Int32 nInputEnd = mpIMEInfos->aPos.GetIndex() + mpIMEInfos->nLen;
+
+ if ( !IsFormatted() )
+ FormatDoc();
+
+ ParaPortion* pParaPortion = GetParaPortions().SafeGetObject( GetEditDoc().GetPos( aPaM.GetNode() ) );
+ if (pParaPortion)
+ {
+ sal_Int32 nLine = pParaPortion->GetLines().FindLine( aPaM.GetIndex(), true );
+ const EditLine& rLine = pParaPortion->GetLines()[nLine];
+ if ( nInputEnd > rLine.GetEnd() )
+ nInputEnd = rLine.GetEnd();
+ tools::Rectangle aR2 = PaMtoEditCursor( EditPaM( aPaM.GetNode(), nInputEnd ), GetCursorFlags::EndOfLine );
+ tools::Rectangle aRect = pView->GetImpEditView()->GetWindowPos( aR1 );
+ auto nExtTextInputWidth = aR2.Left() - aR1.Right();
+ if (EditViewCallbacks* pEditViewCallbacks = pView->getEditViewCallbacks())
+ pEditViewCallbacks->EditViewCursorRect(aRect, nExtTextInputWidth);
+ else if (vcl::Window* pWindow = pView->GetWindow())
+ pWindow->SetCursorRect(&aRect, nExtTextInputWidth);
+ }
+ }
+ else
+ {
+ if (vcl::Window* pWindow = pView->GetWindow())
+ pWindow->SetCursorRect();
+ }
+ }
+ else if ( rCEvt.GetCommand() == CommandEventId::SelectionChange )
+ {
+ const CommandSelectionChangeData *pData = rCEvt.GetSelectionChangeData();
+
+ ESelection aSelection = pView->GetSelection();
+ aSelection.Adjust();
+
+ if( pView->HasSelection() )
+ {
+ aSelection.nEndPos = aSelection.nStartPos;
+ aSelection.nStartPos += pData->GetStart();
+ aSelection.nEndPos += pData->GetEnd();
+ }
+ else
+ {
+ aSelection.nStartPos = pData->GetStart();
+ aSelection.nEndPos = pData->GetEnd();
+ }
+ pView->SetSelection( aSelection );
+ }
+ else if ( rCEvt.GetCommand() == CommandEventId::PrepareReconversion )
+ {
+ if ( pView->HasSelection() )
+ {
+ ESelection aSelection = pView->GetSelection();
+ aSelection.Adjust();
+
+ if ( aSelection.nStartPara != aSelection.nEndPara )
+ {
+ sal_Int32 aParaLen = pEditEngine->GetTextLen( aSelection.nStartPara );
+ aSelection.nEndPara = aSelection.nStartPara;
+ aSelection.nEndPos = aParaLen;
+ pView->SetSelection( aSelection );
+ }
+ }
+ }
+ else if ( rCEvt.GetCommand() == CommandEventId::QueryCharPosition )
+ {
+ if (mpIMEInfos)
+ {
+ EditPaM aPaM( pView->pImpEditView->GetEditSelection().Max() );
+ if ( !IsFormatted() )
+ FormatDoc();
+
+ sal_Int32 nPortionPos = GetEditDoc().GetPos(aPaM.GetNode());
+ ParaPortion* pParaPortion = GetParaPortions().SafeGetObject(nPortionPos);
+ if (pParaPortion)
+ {
+ const sal_Int32 nMinPos = mpIMEInfos->aPos.GetIndex();
+ const sal_Int32 nMaxPos = nMinPos + mpIMEInfos->nLen - 1;
+ std::vector<tools::Rectangle> aRects(mpIMEInfos->nLen);
+
+ auto CollectCharPositions = [&](const LineAreaInfo& rInfo) {
+ if (!rInfo.pLine) // Start of ParaPortion
+ {
+ if (rInfo.nPortion < nPortionPos)
+ return CallbackResult::SkipThisPortion;
+ if (rInfo.nPortion > nPortionPos)
+ return CallbackResult::Stop;
+ assert(&rInfo.rPortion == pParaPortion);
+ }
+ else // This is the needed ParaPortion
+ {
+ if (rInfo.pLine->GetStart() > nMaxPos)
+ return CallbackResult::Stop;
+ if (rInfo.pLine->GetEnd() < nMinPos)
+ return CallbackResult::Continue;
+ for (sal_Int32 n = nMinPos; n <= nMaxPos; ++n)
+ {
+ if (rInfo.pLine->IsIn(n))
+ {
+ tools::Rectangle aR = GetEditCursor(pParaPortion, rInfo.pLine, n,
+ GetCursorFlags::NONE);
+ aR.Move(getTopLeftDocOffset(rInfo.aArea));
+ aRects[n - nMinPos] = pView->GetImpEditView()->GetWindowPos(aR);
+ }
+ }
+ }
+ return CallbackResult::Continue;
+ };
+ IterateLineAreas(CollectCharPositions, IterFlag::none);
+
+ if (vcl::Window* pWindow = pView->GetWindow())
+ pWindow->SetCompositionCharRect(aRects.data(), aRects.size());
+ }
+ }
+ }
+ else
+ bConsumed = false;
+
+ return GetSelEngine().Command(rCEvt) || bConsumed;
+}
+
+bool ImpEditEngine::MouseButtonUp( const MouseEvent& rMEvt, EditView* pView )
+{
+ GetSelEngine().SetCurView( pView );
+ GetSelEngine().SelMouseButtonUp( rMEvt );
+
+ // in the tiled rendering case, setting bInSelection here has unexpected
+ // consequences - further tiles painting removes the selection
+ // FIXME I believe resetting bInSelection should not be here even in the
+ // non-tiled-rendering case, but it has been here since 2000 (and before)
+ // so who knows what corner case it was supposed to solve back then
+ if (!comphelper::LibreOfficeKit::isActive())
+ mbInSelection = false;
+
+ // Special treatments
+ EditSelection aCurSel( pView->pImpEditView->GetEditSelection() );
+ if ( aCurSel.HasRange() )
+ return true;
+
+ if ( ( rMEvt.GetClicks() != 1 ) || !rMEvt.IsLeft() || rMEvt.IsMod2() )
+ return true;
+
+ const OutputDevice& rOutDev = pView->getEditViewCallbacks() ? pView->getEditViewCallbacks()->EditViewOutputDevice() : *pView->GetWindow()->GetOutDev();
+ Point aLogicClick = rOutDev.PixelToLogic(rMEvt.GetPosPixel());
+ const SvxFieldItem* pFld = pView->GetField(aLogicClick);
+ if (!pFld)
+ return true;
+
+ // tdf#121039 When in edit mode, editeng is responsible for opening the URL on mouse click
+ bool bUrlOpened = GetEditEnginePtr()->FieldClicked( *pFld );
+ if (bUrlOpened)
+ return true;
+
+ if (auto pUrlField = dynamic_cast<const SvxURLField*>(pFld->GetField()))
+ {
+ bool bCtrlClickHappened = rMEvt.IsMod1();
+ bool bCtrlClickSecOption
+ = SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::CtrlClickHyperlink);
+ if ((bCtrlClickHappened && bCtrlClickSecOption)
+ || (!bCtrlClickHappened && !bCtrlClickSecOption))
+ {
+ css::uno::Reference<css::system::XSystemShellExecute> exec(
+ css::system::SystemShellExecute::create(
+ comphelper::getProcessComponentContext()));
+ exec->execute(pUrlField->GetURL(), OUString(),
+ css::system::SystemShellExecuteFlags::DEFAULTS);
+ }
+ }
+ return true;
+}
+
+void ImpEditEngine::ReleaseMouse()
+{
+ GetSelEngine().ReleaseMouse();
+}
+
+bool ImpEditEngine::MouseMove( const MouseEvent& rMEvt, EditView* pView )
+{
+ // MouseMove is called directly after ShowQuickHelp()!
+ GetSelEngine().SetCurView( pView );
+ GetSelEngine().SelMouseMove( rMEvt );
+ return true;
+}
+
+EditPaM ImpEditEngine::InsertText(const EditSelection& aSel, const OUString& rStr)
+{
+ EditPaM aPaM = ImpInsertText( aSel, rStr );
+ return aPaM;
+}
+
+void ImpEditEngine::Clear()
+{
+ InitDoc( false );
+
+ EditPaM aPaM = maEditDoc.GetStartPaM();
+ EditSelection aSel( aPaM );
+
+ nCurTextHeight = 0;
+ nCurTextHeightNTP = 0;
+
+ ResetUndoManager();
+
+ for (size_t nView = aEditViews.size(); nView; )
+ {
+ EditView* pView = aEditViews[--nView];
+ pView->pImpEditView->SetEditSelection( aSel );
+ }
+
+ // Related: tdf#82115 Fix crash when handling input method events.
+ // The nodes in mpIMEInfos may be deleted in ImpEditEngine::Clear() which
+ // causes a crash in the CommandEventId::ExtTextInput and
+ // CommandEventId::EndExtTextInput event handlers.
+ mpIMEInfos.reset();
+}
+
+EditPaM ImpEditEngine::RemoveText()
+{
+ InitDoc( true );
+
+ EditPaM aStartPaM = maEditDoc.GetStartPaM();
+ EditSelection aEmptySel( aStartPaM, aStartPaM );
+ for (EditView* pView : aEditViews)
+ {
+ pView->pImpEditView->SetEditSelection( aEmptySel );
+ }
+ ResetUndoManager();
+ return maEditDoc.GetStartPaM();
+}
+
+
+void ImpEditEngine::SetText(const OUString& rText)
+{
+ // RemoveText deletes the undo list!
+ EditPaM aStartPaM = RemoveText();
+ bool bUndoCurrentlyEnabled = IsUndoEnabled();
+ // The text inserted manually can not be made reversible by the user
+ EnableUndo( false );
+
+ EditSelection aEmptySel( aStartPaM, aStartPaM );
+ EditPaM aPaM = aStartPaM;
+ if (!rText.isEmpty())
+ aPaM = ImpInsertText( aEmptySel, rText );
+
+ for (EditView* pView : aEditViews)
+ {
+ pView->pImpEditView->SetEditSelection( EditSelection( aPaM, aPaM ) );
+ // If no text then also no Format&Update
+ // => The text remains.
+ if (rText.isEmpty() && IsUpdateLayout())
+ {
+ tools::Rectangle aTmpRect( pView->GetOutputArea().TopLeft(),
+ Size( maPaperSize.Width(), nCurTextHeight ) );
+ aTmpRect.Intersection( pView->GetOutputArea() );
+ pView->InvalidateWindow( aTmpRect );
+ }
+ }
+ if (rText.isEmpty()) { // otherwise it must be invalidated later, !bFormatted is enough.
+ nCurTextHeight = 0;
+ nCurTextHeightNTP = 0;
+ }
+ EnableUndo( bUndoCurrentlyEnabled );
+ OSL_ENSURE( !HasUndoManager() || !GetUndoManager().GetUndoActionCount(), "Undo after SetText?" );
+}
+
+
+const SfxItemSet& ImpEditEngine::GetEmptyItemSet() const
+{
+ if ( !pEmptyItemSet )
+ {
+ pEmptyItemSet = std::make_unique<SfxItemSetFixed<EE_ITEMS_START, EE_ITEMS_END>>(const_cast<SfxItemPool&>(maEditDoc.GetItemPool()));
+ for ( sal_uInt16 nWhich = EE_ITEMS_START; nWhich <= EE_CHAR_END; nWhich++)
+ {
+ pEmptyItemSet->ClearItem( nWhich );
+ }
+ }
+ return *pEmptyItemSet;
+}
+
+
+// MISC
+
+void ImpEditEngine::TextModified()
+{
+ mbFormatted = false;
+
+ if ( GetNotifyHdl().IsSet() )
+ {
+ EENotify aNotify( EE_NOTIFY_TEXTMODIFIED );
+ GetNotifyHdl().Call( aNotify );
+ }
+}
+
+
+void ImpEditEngine::ParaAttribsChanged( ContentNode const * pNode, bool bIgnoreUndoCheck )
+{
+ assert(pNode && "ParaAttribsChanged: Which one?");
+
+ maEditDoc.SetModified( true );
+ mbFormatted = false;
+
+ ParaPortion* pPortion = FindParaPortion( pNode );
+ assert(pPortion);
+ pPortion->MarkSelectionInvalid( 0 );
+
+ sal_Int32 nPara = maEditDoc.GetPos( pNode );
+ if ( bIgnoreUndoCheck || pEditEngine->IsInUndo() )
+ pEditEngine->ParaAttribsChanged( nPara );
+
+ ParaPortion* pNextPortion = GetParaPortions().SafeGetObject( nPara+1 );
+ // => is formatted again anyway, if Invalid.
+ if ( pNextPortion && !pNextPortion->IsInvalid() )
+ CalcHeight( pNextPortion );
+}
+
+
+// Cursor movements
+
+
+EditSelection const & ImpEditEngine::MoveCursor( const KeyEvent& rKeyEvent, EditView* pEditView )
+{
+ // Actually, only necessary for up/down, but whatever.
+ CheckIdleFormatter();
+
+ EditPaM aPaM( pEditView->pImpEditView->GetEditSelection().Max() );
+
+ EditPaM aOldPaM( aPaM );
+
+ TextDirectionality eTextDirection = TextDirectionality::LeftToRight_TopToBottom;
+ if (IsEffectivelyVertical() && IsTopToBottom())
+ eTextDirection = TextDirectionality::TopToBottom_RightToLeft;
+ else if (IsEffectivelyVertical() && !IsTopToBottom())
+ eTextDirection = TextDirectionality::BottomToTop_LeftToRight;
+ else if ( IsRightToLeft( GetEditDoc().GetPos( aPaM.GetNode() ) ) )
+ eTextDirection = TextDirectionality::RightToLeft_TopToBottom;
+
+ KeyEvent aTranslatedKeyEvent = rKeyEvent.LogicalTextDirectionality( eTextDirection );
+
+ bool bCtrl = aTranslatedKeyEvent.GetKeyCode().IsMod1();
+ sal_uInt16 nCode = aTranslatedKeyEvent.GetKeyCode().GetCode();
+
+ if ( DoVisualCursorTraveling() )
+ {
+ // Only for simple cursor movement...
+ if ( !bCtrl && ( ( nCode == KEY_LEFT ) || ( nCode == KEY_RIGHT ) ) )
+ {
+ aPaM = CursorVisualLeftRight( pEditView, aPaM, rKeyEvent.GetKeyCode().IsMod2() ? i18n::CharacterIteratorMode::SKIPCHARACTER : i18n::CharacterIteratorMode::SKIPCELL, rKeyEvent.GetKeyCode().GetCode() == KEY_LEFT );
+ nCode = 0; // skip switch statement
+ }
+ }
+
+ bool bKeyModifySelection = aTranslatedKeyEvent.GetKeyCode().IsShift();
+ switch ( nCode )
+ {
+ case KEY_UP: aPaM = CursorUp( aPaM, pEditView );
+ break;
+ case KEY_DOWN: aPaM = CursorDown( aPaM, pEditView );
+ break;
+ case KEY_LEFT: aPaM = bCtrl ? WordLeft( aPaM ) : CursorLeft( aPaM, aTranslatedKeyEvent.GetKeyCode().IsMod2() ? i18n::CharacterIteratorMode::SKIPCHARACTER : i18n::CharacterIteratorMode::SKIPCELL );
+ break;
+ case KEY_RIGHT: aPaM = bCtrl ? WordRight( aPaM ) : CursorRight( aPaM, aTranslatedKeyEvent.GetKeyCode().IsMod2() ? i18n::CharacterIteratorMode::SKIPCHARACTER : i18n::CharacterIteratorMode::SKIPCELL );
+ break;
+ case KEY_HOME: aPaM = bCtrl ? CursorStartOfDoc() : CursorStartOfLine( aPaM );
+ break;
+ case KEY_END: aPaM = bCtrl ? CursorEndOfDoc() : CursorEndOfLine( aPaM );
+ break;
+ case KEY_PAGEUP: aPaM = bCtrl ? CursorStartOfDoc() : PageUp( aPaM, pEditView );
+ break;
+ case KEY_PAGEDOWN: aPaM = bCtrl ? CursorEndOfDoc() : PageDown( aPaM, pEditView );
+ break;
+ case css::awt::Key::MOVE_TO_BEGIN_OF_LINE:
+ aPaM = CursorStartOfLine( aPaM );
+ bKeyModifySelection = false;
+ break;
+ case css::awt::Key::MOVE_TO_END_OF_LINE:
+ aPaM = CursorEndOfLine( aPaM );
+ bKeyModifySelection = false;
+ break;
+ case css::awt::Key::MOVE_WORD_BACKWARD:
+ aPaM = WordLeft( aPaM );
+ bKeyModifySelection = false;
+ break;
+ case css::awt::Key::MOVE_WORD_FORWARD:
+ aPaM = WordRight( aPaM );
+ bKeyModifySelection = false;
+ break;
+ case css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH:
+ aPaM = CursorStartOfParagraph( aPaM );
+ if( aPaM == aOldPaM )
+ {
+ aPaM = CursorLeft( aPaM );
+ aPaM = CursorStartOfParagraph( aPaM );
+ }
+ bKeyModifySelection = false;
+ break;
+ case css::awt::Key::MOVE_TO_END_OF_PARAGRAPH:
+ aPaM = CursorEndOfParagraph( aPaM );
+ if( aPaM == aOldPaM )
+ {
+ aPaM = CursorRight( aPaM );
+ aPaM = CursorEndOfParagraph( aPaM );
+ }
+ bKeyModifySelection = false;
+ break;
+ case css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT:
+ aPaM = CursorStartOfDoc();
+ bKeyModifySelection = false;
+ break;
+ case css::awt::Key::MOVE_TO_END_OF_DOCUMENT:
+ aPaM = CursorEndOfDoc();
+ bKeyModifySelection = false;
+ break;
+ case css::awt::Key::SELECT_TO_BEGIN_OF_LINE:
+ aPaM = CursorStartOfLine( aPaM );
+ bKeyModifySelection = true;
+ break;
+ case css::awt::Key::SELECT_TO_END_OF_LINE:
+ aPaM = CursorEndOfLine( aPaM );
+ bKeyModifySelection = true;
+ break;
+ case css::awt::Key::SELECT_BACKWARD:
+ aPaM = CursorLeft( aPaM );
+ bKeyModifySelection = true;
+ break;
+ case css::awt::Key::SELECT_FORWARD:
+ aPaM = CursorRight( aPaM );
+ bKeyModifySelection = true;
+ break;
+ case css::awt::Key::SELECT_WORD_BACKWARD:
+ aPaM = WordLeft( aPaM );
+ bKeyModifySelection = true;
+ break;
+ case css::awt::Key::SELECT_WORD_FORWARD:
+ aPaM = WordRight( aPaM );
+ bKeyModifySelection = true;
+ break;
+ case css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH:
+ aPaM = CursorStartOfParagraph( aPaM );
+ if( aPaM == aOldPaM )
+ {
+ aPaM = CursorLeft( aPaM );
+ aPaM = CursorStartOfParagraph( aPaM );
+ }
+ bKeyModifySelection = true;
+ break;
+ case css::awt::Key::SELECT_TO_END_OF_PARAGRAPH:
+ aPaM = CursorEndOfParagraph( aPaM );
+ if( aPaM == aOldPaM )
+ {
+ aPaM = CursorRight( aPaM );
+ aPaM = CursorEndOfParagraph( aPaM );
+ }
+ bKeyModifySelection = true;
+ break;
+ case css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT:
+ aPaM = CursorStartOfDoc();
+ bKeyModifySelection = true;
+ break;
+ case css::awt::Key::SELECT_TO_END_OF_DOCUMENT:
+ aPaM = CursorEndOfDoc();
+ bKeyModifySelection = true;
+ break;
+ }
+
+ if ( aOldPaM != aPaM && nullptr != aOldPaM.GetNode() )
+ {
+ aOldPaM.GetNode()->checkAndDeleteEmptyAttribs();
+ }
+
+ // May cause, a CreateAnchor or deselection all
+ aSelEngine.SetCurView( pEditView );
+ aSelEngine.CursorPosChanging( bKeyModifySelection, aTranslatedKeyEvent.GetKeyCode().IsMod1() );
+ EditPaM aOldEnd( pEditView->pImpEditView->GetEditSelection().Max() );
+
+ {
+ EditSelection aNewSelection(pEditView->pImpEditView->GetEditSelection());
+ aNewSelection.Max() = aPaM;
+ pEditView->pImpEditView->SetEditSelection(aNewSelection);
+ // const_cast<EditPaM&>(pEditView->pImpEditView->GetEditSelection().Max()) = aPaM;
+ }
+
+ if ( bKeyModifySelection )
+ {
+ // Then the selection is expanded ... or the whole selection is painted in case of tiled rendering.
+ EditSelection aTmpNewSel( comphelper::LibreOfficeKit::isActive() ? pEditView->pImpEditView->GetEditSelection().Min() : aOldEnd, aPaM );
+ pEditView->pImpEditView->DrawSelectionXOR( aTmpNewSel );
+ }
+ else
+ {
+ EditSelection aNewSelection(pEditView->pImpEditView->GetEditSelection());
+ aNewSelection.Min() = aPaM;
+ pEditView->pImpEditView->SetEditSelection(aNewSelection);
+ // const_cast<EditPaM&>(pEditView->pImpEditView->GetEditSelection().Min()) = aPaM;
+ }
+
+ return pEditView->pImpEditView->GetEditSelection();
+}
+
+EditPaM ImpEditEngine::CursorVisualStartEnd( EditView const * pEditView, const EditPaM& rPaM, bool bStart )
+{
+ EditPaM aPaM( rPaM );
+
+ sal_Int32 nPara = GetEditDoc().GetPos( aPaM.GetNode() );
+ ParaPortion* pParaPortion = GetParaPortions().SafeGetObject( nPara );
+ if (!pParaPortion)
+ return aPaM;
+
+ sal_Int32 nLine = pParaPortion->GetLines().FindLine( aPaM.GetIndex(), false );
+ const EditLine& rLine = pParaPortion->GetLines()[nLine];
+ bool bEmptyLine = rLine.GetStart() == rLine.GetEnd();
+
+ pEditView->pImpEditView->nExtraCursorFlags = GetCursorFlags::NONE;
+
+ if ( !bEmptyLine )
+ {
+ OUString aLine = aPaM.GetNode()->GetString().copy(rLine.GetStart(), rLine.GetEnd() - rLine.GetStart());
+
+ UErrorCode nError = U_ZERO_ERROR;
+ UBiDi* pBidi = ubidi_openSized( aLine.getLength(), 0, &nError );
+
+ const UBiDiLevel nBidiLevel = IsRightToLeft( nPara ) ? 1 /*RTL*/ : 0 /*LTR*/;
+ ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aLine.getStr()), aLine.getLength(), nBidiLevel, nullptr, &nError );
+
+ sal_Int32 nVisPos = bStart ? 0 : aLine.getLength()-1;
+ const sal_Int32 nLogPos = ubidi_getLogicalIndex( pBidi, nVisPos, &nError );
+
+ ubidi_close( pBidi );
+
+ aPaM.SetIndex( nLogPos + rLine.GetStart() );
+
+ sal_Int32 nTmp;
+ sal_Int32 nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nTmp, true );
+ const TextPortion& rTextPortion = pParaPortion->GetTextPortions()[nTextPortion];
+ bool bPortionRTL = rTextPortion.IsRightToLeft();
+
+ if ( bStart )
+ {
+ pEditView->pImpEditView->SetCursorBidiLevel( bPortionRTL ? 0 : 1 );
+ // Maybe we must be *behind* the character
+ if ( bPortionRTL && pEditView->IsInsertMode() )
+ aPaM.SetIndex( aPaM.GetIndex()+1 );
+ }
+ else
+ {
+ pEditView->pImpEditView->SetCursorBidiLevel( bPortionRTL ? 1 : 0 );
+ if ( !bPortionRTL && pEditView->IsInsertMode() )
+ aPaM.SetIndex( aPaM.GetIndex()+1 );
+ }
+ }
+
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::CursorVisualLeftRight( EditView const * pEditView, const EditPaM& rPaM, sal_uInt16 nCharacterIteratorMode, bool bVisualToLeft )
+{
+ EditPaM aPaM( rPaM );
+
+ sal_Int32 nPara = GetEditDoc().GetPos( aPaM.GetNode() );
+ ParaPortion* pParaPortion = GetParaPortions().SafeGetObject( nPara );
+ if (!pParaPortion)
+ return aPaM;
+
+ sal_Int32 nLine = pParaPortion->GetLines().FindLine( aPaM.GetIndex(), false );
+ const EditLine& rLine = pParaPortion->GetLines()[nLine];
+ bool bEmptyLine = rLine.GetStart() == rLine.GetEnd();
+
+ pEditView->pImpEditView->nExtraCursorFlags = GetCursorFlags::NONE;
+
+ bool bParaRTL = IsRightToLeft( nPara );
+
+ bool bDone = false;
+
+ if ( bEmptyLine )
+ {
+ if ( bVisualToLeft )
+ {
+ aPaM = CursorUp( aPaM, pEditView );
+ if ( aPaM != rPaM )
+ aPaM = CursorVisualStartEnd( pEditView, aPaM, false );
+ }
+ else
+ {
+ aPaM = CursorDown( aPaM, pEditView );
+ if ( aPaM != rPaM )
+ aPaM = CursorVisualStartEnd( pEditView, aPaM, true );
+ }
+
+ bDone = true;
+ }
+
+ bool bLogicalBackward = bParaRTL ? !bVisualToLeft : bVisualToLeft;
+
+ if ( !bDone && pEditView->IsInsertMode() )
+ {
+ // Check if we are within a portion and don't have overwrite mode, then it's easy...
+ sal_Int32 nPortionStart;
+ sal_Int32 nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nPortionStart );
+ const TextPortion& rTextPortion = pParaPortion->GetTextPortions()[nTextPortion];
+
+ bool bPortionBoundary = ( aPaM.GetIndex() == nPortionStart ) || ( aPaM.GetIndex() == (nPortionStart+rTextPortion.GetLen()) );
+ sal_uInt16 nRTLLevel = rTextPortion.GetRightToLeftLevel();
+
+ // Portion boundary doesn't matter if both have same RTL level
+ sal_Int32 nRTLLevelNextPortion = -1;
+ if ( bPortionBoundary && aPaM.GetIndex() && ( aPaM.GetIndex() < aPaM.GetNode()->Len() ) )
+ {
+ sal_Int32 nTmp;
+ sal_Int32 nNextTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex()+1, nTmp, !bLogicalBackward );
+ const TextPortion& rNextTextPortion = pParaPortion->GetTextPortions()[nNextTextPortion];
+ nRTLLevelNextPortion = rNextTextPortion.GetRightToLeftLevel();
+ }
+
+ if ( !bPortionBoundary || ( nRTLLevel == nRTLLevelNextPortion ) )
+ {
+ if (bVisualToLeft != bool(nRTLLevel % 2))
+ {
+ aPaM = CursorLeft( aPaM, nCharacterIteratorMode );
+ pEditView->pImpEditView->SetCursorBidiLevel( 1 );
+ }
+ else
+ {
+ aPaM = CursorRight( aPaM, nCharacterIteratorMode );
+ pEditView->pImpEditView->SetCursorBidiLevel( 0 );
+ }
+ bDone = true;
+ }
+ }
+
+ if ( !bDone )
+ {
+ bool bGotoStartOfNextLine = false;
+ bool bGotoEndOfPrevLine = false;
+
+ OUString aLine = aPaM.GetNode()->GetString().copy(rLine.GetStart(), rLine.GetEnd() - rLine.GetStart());
+ const sal_Int32 nPosInLine = aPaM.GetIndex() - rLine.GetStart();
+
+ UErrorCode nError = U_ZERO_ERROR;
+ UBiDi* pBidi = ubidi_openSized( aLine.getLength(), 0, &nError );
+
+ const UBiDiLevel nBidiLevel = IsRightToLeft( nPara ) ? 1 /*RTL*/ : 0 /*LTR*/;
+ ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aLine.getStr()), aLine.getLength(), nBidiLevel, nullptr, &nError );
+
+ if ( !pEditView->IsInsertMode() )
+ {
+ bool bEndOfLine = nPosInLine == aLine.getLength();
+ sal_Int32 nVisPos = ubidi_getVisualIndex( pBidi, !bEndOfLine ? nPosInLine : nPosInLine-1, &nError );
+ if ( bVisualToLeft )
+ {
+ bGotoEndOfPrevLine = nVisPos == 0;
+ if ( !bEndOfLine )
+ nVisPos--;
+ }
+ else
+ {
+ bGotoStartOfNextLine = nVisPos == (aLine.getLength() - 1);
+ if ( !bEndOfLine )
+ nVisPos++;
+ }
+
+ if ( !bGotoEndOfPrevLine && !bGotoStartOfNextLine )
+ {
+ aPaM.SetIndex( rLine.GetStart() + ubidi_getLogicalIndex( pBidi, nVisPos, &nError ) );
+ pEditView->pImpEditView->SetCursorBidiLevel( 0 );
+ }
+ }
+ else
+ {
+ bool bWasBehind = false;
+ bool bBeforePortion = !nPosInLine || pEditView->pImpEditView->GetCursorBidiLevel() == 1;
+ if ( nPosInLine && ( !bBeforePortion ) ) // before the next portion
+ bWasBehind = true; // step one back, otherwise visual will be unusable when rtl portion follows.
+
+ sal_Int32 nPortionStart;
+ sal_Int32 nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nPortionStart, bBeforePortion );
+ const TextPortion& rTextPortion = pParaPortion->GetTextPortions()[nTextPortion];
+ bool bRTLPortion = rTextPortion.IsRightToLeft();
+
+ // -1: We are 'behind' the character
+ tools::Long nVisPos = static_cast<tools::Long>(ubidi_getVisualIndex( pBidi, bWasBehind ? nPosInLine-1 : nPosInLine, &nError ));
+ if ( bVisualToLeft )
+ {
+ if ( !bWasBehind || bRTLPortion )
+ nVisPos--;
+ }
+ else
+ {
+ if ( bWasBehind || bRTLPortion || bBeforePortion )
+ nVisPos++;
+ }
+
+ bGotoEndOfPrevLine = nVisPos < 0;
+ bGotoStartOfNextLine = nVisPos >= aLine.getLength();
+
+ if ( !bGotoEndOfPrevLine && !bGotoStartOfNextLine )
+ {
+ aPaM.SetIndex( rLine.GetStart() + ubidi_getLogicalIndex( pBidi, nVisPos, &nError ) );
+
+ // RTL portion, stay visually on the left side.
+ sal_Int32 _nPortionStart;
+ // sal_uInt16 nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nPortionStart, !bRTLPortion );
+ sal_Int32 _nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), _nPortionStart, true );
+ const TextPortion& _rTextPortion = pParaPortion->GetTextPortions()[_nTextPortion];
+ if ( bVisualToLeft && !bRTLPortion && _rTextPortion.IsRightToLeft() )
+ aPaM.SetIndex( aPaM.GetIndex()+1 );
+ else if ( !bVisualToLeft && bRTLPortion && ( bWasBehind || !_rTextPortion.IsRightToLeft() ) )
+ aPaM.SetIndex( aPaM.GetIndex()+1 );
+
+ pEditView->pImpEditView->SetCursorBidiLevel( _nPortionStart );
+ }
+ }
+
+ ubidi_close( pBidi );
+
+ if ( bGotoEndOfPrevLine )
+ {
+ aPaM = CursorUp( aPaM, pEditView );
+ if ( aPaM != rPaM )
+ aPaM = CursorVisualStartEnd( pEditView, aPaM, false );
+ }
+ else if ( bGotoStartOfNextLine )
+ {
+ aPaM = CursorDown( aPaM, pEditView );
+ if ( aPaM != rPaM )
+ aPaM = CursorVisualStartEnd( pEditView, aPaM, true );
+ }
+ }
+ return aPaM;
+}
+
+
+EditPaM ImpEditEngine::CursorLeft( const EditPaM& rPaM, sal_uInt16 nCharacterIteratorMode )
+{
+ EditPaM aCurPaM( rPaM );
+ EditPaM aNewPaM( aCurPaM );
+
+ if ( aCurPaM.GetIndex() )
+ {
+ sal_Int32 nCount = 1;
+ uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
+ aNewPaM.SetIndex(
+ _xBI->previousCharacters(
+ aNewPaM.GetNode()->GetString(), aNewPaM.GetIndex(), GetLocale( aNewPaM ), nCharacterIteratorMode, nCount, nCount));
+ }
+ else
+ {
+ ContentNode* pNode = aCurPaM.GetNode();
+ pNode = GetPrevVisNode( pNode );
+ if ( pNode )
+ {
+ aNewPaM.SetNode( pNode );
+ aNewPaM.SetIndex( pNode->Len() );
+ }
+ }
+
+ return aNewPaM;
+}
+
+EditPaM ImpEditEngine::CursorRight( const EditPaM& rPaM, sal_uInt16 nCharacterIteratorMode )
+{
+ EditPaM aCurPaM( rPaM );
+ EditPaM aNewPaM( aCurPaM );
+
+ if ( aCurPaM.GetIndex() < aCurPaM.GetNode()->Len() )
+ {
+ uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
+ sal_Int32 nCount = 1;
+ aNewPaM.SetIndex(
+ _xBI->nextCharacters(
+ aNewPaM.GetNode()->GetString(), aNewPaM.GetIndex(), GetLocale( aNewPaM ), nCharacterIteratorMode, nCount, nCount));
+ }
+ else
+ {
+ ContentNode* pNode = aCurPaM.GetNode();
+ pNode = GetNextVisNode( pNode );
+ if ( pNode )
+ {
+ aNewPaM.SetNode( pNode );
+ aNewPaM.SetIndex( 0 );
+ }
+ }
+
+ return aNewPaM;
+}
+
+EditPaM ImpEditEngine::CursorUp( const EditPaM& rPaM, EditView const * pView )
+{
+ assert(pView && "No View - No Cursor Movement!");
+
+ const ParaPortion* pPPortion = FindParaPortion( rPaM.GetNode() );
+ assert(pPPortion);
+ sal_Int32 nLine = pPPortion->GetLineNumber( rPaM.GetIndex() );
+ const EditLine& rLine = pPPortion->GetLines()[nLine];
+
+ tools::Long nX;
+ if ( pView->pImpEditView->nTravelXPos == TRAVEL_X_DONTKNOW )
+ {
+ nX = GetXPos( pPPortion, &rLine, rPaM.GetIndex() );
+ pView->pImpEditView->nTravelXPos = nX+nOnePixelInRef;
+ }
+ else
+ nX = pView->pImpEditView->nTravelXPos;
+
+ EditPaM aNewPaM( rPaM );
+ if ( nLine ) // same paragraph
+ {
+ const EditLine& rPrevLine = pPPortion->GetLines()[nLine-1];
+ aNewPaM.SetIndex( GetChar( pPPortion, &rPrevLine, nX ) );
+ // If a previous automatically wrapped line, and one has to be exactly
+ // at the end of this line, the cursor lands on the current line at the
+ // beginning. See Problem: Last character of an automatically wrapped
+ // Row = cursor
+ if ( aNewPaM.GetIndex() && ( aNewPaM.GetIndex() == rLine.GetStart() ) )
+ aNewPaM = CursorLeft( aNewPaM );
+ }
+ else // previous paragraph
+ {
+ const ParaPortion* pPrevPortion = GetPrevVisPortion( pPPortion );
+ if ( pPrevPortion )
+ {
+ const EditLine& rLine2 = pPrevPortion->GetLines()[pPrevPortion->GetLines().Count()-1];
+ aNewPaM.SetNode( pPrevPortion->GetNode() );
+ aNewPaM.SetIndex( GetChar( pPrevPortion, &rLine2, nX+nOnePixelInRef ) );
+ }
+ }
+
+ return aNewPaM;
+}
+
+EditPaM ImpEditEngine::CursorDown( const EditPaM& rPaM, EditView const * pView )
+{
+ assert(pView);
+
+ const ParaPortion* pPPortion = FindParaPortion( rPaM.GetNode() );
+ assert(pPPortion);
+ sal_Int32 nLine = pPPortion->GetLineNumber( rPaM.GetIndex() );
+
+ tools::Long nX;
+ if ( pView->pImpEditView->nTravelXPos == TRAVEL_X_DONTKNOW )
+ {
+ const EditLine& rLine = pPPortion->GetLines()[nLine];
+ nX = GetXPos( pPPortion, &rLine, rPaM.GetIndex() );
+ pView->pImpEditView->nTravelXPos = nX+nOnePixelInRef;
+ }
+ else
+ nX = pView->pImpEditView->nTravelXPos;
+
+ EditPaM aNewPaM( rPaM );
+ if ( nLine < pPPortion->GetLines().Count()-1 )
+ {
+ const EditLine& rNextLine = pPPortion->GetLines()[nLine+1];
+ aNewPaM.SetIndex( GetChar( pPPortion, &rNextLine, nX ) );
+ // Special treatment, see CursorUp ...
+ if ( ( aNewPaM.GetIndex() == rNextLine.GetEnd() ) && ( aNewPaM.GetIndex() > rNextLine.GetStart() ) && ( aNewPaM.GetIndex() < pPPortion->GetNode()->Len() ) )
+ aNewPaM = CursorLeft( aNewPaM );
+ }
+ else // next paragraph
+ {
+ const ParaPortion* pNextPortion = GetNextVisPortion( pPPortion );
+ if ( pNextPortion )
+ {
+ const EditLine& rLine = pNextPortion->GetLines()[0];
+ aNewPaM.SetNode( pNextPortion->GetNode() );
+ // Never at the very end when several lines, because then a line
+ // below the cursor appears.
+ aNewPaM.SetIndex( GetChar( pNextPortion, &rLine, nX+nOnePixelInRef ) );
+ if ( ( aNewPaM.GetIndex() == rLine.GetEnd() ) && ( aNewPaM.GetIndex() > rLine.GetStart() ) && ( pNextPortion->GetLines().Count() > 1 ) )
+ aNewPaM = CursorLeft( aNewPaM );
+ }
+ }
+
+ return aNewPaM;
+}
+
+EditPaM ImpEditEngine::CursorStartOfLine( const EditPaM& rPaM )
+{
+ const ParaPortion* pCurPortion = FindParaPortion( rPaM.GetNode() );
+ assert(pCurPortion);
+ sal_Int32 nLine = pCurPortion->GetLineNumber( rPaM.GetIndex() );
+ const EditLine& rLine = pCurPortion->GetLines()[nLine];
+
+ EditPaM aNewPaM( rPaM );
+ aNewPaM.SetIndex( rLine.GetStart() );
+ return aNewPaM;
+}
+
+EditPaM ImpEditEngine::CursorEndOfLine( const EditPaM& rPaM )
+{
+ const ParaPortion* pCurPortion = FindParaPortion( rPaM.GetNode() );
+ assert(pCurPortion);
+ sal_Int32 nLine = pCurPortion->GetLineNumber( rPaM.GetIndex() );
+ const EditLine& rLine = pCurPortion->GetLines()[nLine];
+
+ EditPaM aNewPaM( rPaM );
+ aNewPaM.SetIndex( rLine.GetEnd() );
+ if ( rLine.GetEnd() > rLine.GetStart() )
+ {
+ if ( aNewPaM.GetNode()->IsFeature( aNewPaM.GetIndex() - 1 ) )
+ {
+ // When a soft break, be in front of it!
+ const EditCharAttrib* pNextFeature = aNewPaM.GetNode()->GetCharAttribs().FindFeature( aNewPaM.GetIndex()-1 );
+ if ( pNextFeature && ( pNextFeature->GetItem()->Which() == EE_FEATURE_LINEBR ) )
+ aNewPaM = CursorLeft( aNewPaM );
+ }
+ else if ( ( aNewPaM.GetNode()->GetChar( aNewPaM.GetIndex() - 1 ) == ' ' ) && ( aNewPaM.GetIndex() != aNewPaM.GetNode()->Len() ) )
+ {
+ // For a Blank in an auto wrapped line, it makes sense, to stand
+ // in front of it, since the user wants to be after the word.
+ // If this is changed, special treatment for Pos1 to End!
+ aNewPaM = CursorLeft( aNewPaM );
+ }
+ }
+ return aNewPaM;
+}
+
+EditPaM ImpEditEngine::CursorStartOfParagraph( const EditPaM& rPaM )
+{
+ EditPaM aPaM(rPaM);
+ aPaM.SetIndex(0);
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::CursorEndOfParagraph( const EditPaM& rPaM )
+{
+ EditPaM aPaM(rPaM);
+ aPaM.SetIndex(rPaM.GetNode()->Len());
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::CursorStartOfDoc()
+{
+ EditPaM aPaM( maEditDoc.GetObject( 0 ), 0 );
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::CursorEndOfDoc()
+{
+ ContentNode* pLastNode = maEditDoc.GetObject( maEditDoc.Count()-1 );
+ ParaPortion* pLastPortion = GetParaPortions().SafeGetObject( maEditDoc.Count()-1 );
+ OSL_ENSURE( pLastNode && pLastPortion, "CursorEndOfDoc: Node or Portion not found" );
+ if (!(pLastNode && pLastPortion))
+ return EditPaM();
+
+ if ( !pLastPortion->IsVisible() )
+ {
+ pLastNode = GetPrevVisNode( pLastPortion->GetNode() );
+ OSL_ENSURE( pLastNode, "No visible paragraph?" );
+ if ( !pLastNode )
+ pLastNode = maEditDoc.GetObject( maEditDoc.Count()-1 );
+ }
+
+ EditPaM aPaM( pLastNode, pLastNode->Len() );
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::PageUp( const EditPaM& rPaM, EditView const * pView )
+{
+ tools::Rectangle aRect = PaMtoEditCursor( rPaM );
+ Point aTopLeft = aRect.TopLeft();
+ aTopLeft.AdjustY( -(pView->GetVisArea().GetHeight() *9/10) );
+ aTopLeft.AdjustX(nOnePixelInRef );
+ if ( aTopLeft.Y() < 0 )
+ {
+ aTopLeft.setY( 0 );
+ }
+ return GetPaM( aTopLeft );
+}
+
+EditPaM ImpEditEngine::PageDown( const EditPaM& rPaM, EditView const * pView )
+{
+ tools::Rectangle aRect = PaMtoEditCursor( rPaM );
+ Point aBottomRight = aRect.BottomRight();
+ aBottomRight.AdjustY(pView->GetVisArea().GetHeight() *9/10 );
+ aBottomRight.AdjustX(nOnePixelInRef );
+ tools::Long nHeight = GetTextHeight();
+ if ( aBottomRight.Y() > nHeight )
+ {
+ aBottomRight.setY( nHeight-2 );
+ }
+ return GetPaM( aBottomRight );
+}
+
+EditPaM ImpEditEngine::WordLeft( const EditPaM& rPaM )
+{
+ const sal_Int32 nCurrentPos = rPaM.GetIndex();
+ EditPaM aNewPaM( rPaM );
+ if ( nCurrentPos == 0 )
+ {
+ // Previous paragraph...
+ sal_Int32 nCurPara = maEditDoc.GetPos( aNewPaM.GetNode() );
+ ContentNode* pPrevNode = maEditDoc.GetObject( --nCurPara );
+ if ( pPrevNode )
+ {
+ aNewPaM.SetNode( pPrevNode );
+ aNewPaM.SetIndex( pPrevNode->Len() );
+ }
+ }
+ else
+ {
+ // we need to increase the position by 1 when retrieving the locale
+ // since the attribute for the char left to the cursor position is returned
+ EditPaM aTmpPaM( aNewPaM );
+ if ( aTmpPaM.GetIndex() < rPaM.GetNode()->Len() )
+ aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
+ lang::Locale aLocale( GetLocale( aTmpPaM ) );
+
+ uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
+ i18n::Boundary aBoundary =
+ _xBI->getWordBoundary(aNewPaM.GetNode()->GetString(), nCurrentPos, aLocale, css::i18n::WordType::ANYWORD_IGNOREWHITESPACES, true);
+ if ( aBoundary.startPos >= nCurrentPos )
+ aBoundary = _xBI->previousWord(
+ aNewPaM.GetNode()->GetString(), nCurrentPos, aLocale, css::i18n::WordType::ANYWORD_IGNOREWHITESPACES);
+ aNewPaM.SetIndex( ( aBoundary.startPos != -1 ) ? aBoundary.startPos : 0 );
+ }
+
+ return aNewPaM;
+}
+
+EditPaM ImpEditEngine::WordRight( const EditPaM& rPaM, sal_Int16 nWordType )
+{
+ const sal_Int32 nMax = rPaM.GetNode()->Len();
+ EditPaM aNewPaM( rPaM );
+ if ( aNewPaM.GetIndex() < nMax )
+ {
+ // we need to increase the position by 1 when retrieving the locale
+ // since the attribute for the char left to the cursor position is returned
+ EditPaM aTmpPaM( aNewPaM );
+ aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
+ lang::Locale aLocale( GetLocale( aTmpPaM ) );
+
+ uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
+ i18n::Boundary aBoundary = _xBI->nextWord(
+ aNewPaM.GetNode()->GetString(), aNewPaM.GetIndex(), aLocale, nWordType);
+ aNewPaM.SetIndex( aBoundary.startPos );
+ }
+ // not 'else', maybe the index reached nMax now...
+ if ( aNewPaM.GetIndex() >= nMax )
+ {
+ // Next paragraph ...
+ sal_Int32 nCurPara = maEditDoc.GetPos( aNewPaM.GetNode() );
+ ContentNode* pNextNode = maEditDoc.GetObject( ++nCurPara );
+ if ( pNextNode )
+ {
+ aNewPaM.SetNode( pNextNode );
+ aNewPaM.SetIndex( 0 );
+ }
+ }
+ return aNewPaM;
+}
+
+EditPaM ImpEditEngine::StartOfWord( const EditPaM& rPaM )
+{
+ EditPaM aNewPaM( rPaM );
+
+ // we need to increase the position by 1 when retrieving the locale
+ // since the attribute for the char left to the cursor position is returned
+ EditPaM aTmpPaM( aNewPaM );
+ if ( aTmpPaM.GetIndex() < rPaM.GetNode()->Len() )
+ aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
+ lang::Locale aLocale( GetLocale( aTmpPaM ) );
+
+ uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
+ // tdf#135761 - since this function is only used when a selection is deleted at the left,
+ // change the search preference of the word boundary from forward to backward.
+ // For further details of a deletion of a selection check ImpEditEngine::DeleteLeftOrRight.
+ i18n::Boundary aBoundary = _xBI->getWordBoundary(
+ rPaM.GetNode()->GetString(), rPaM.GetIndex(), aLocale, css::i18n::WordType::ANYWORD_IGNOREWHITESPACES, false);
+
+ aNewPaM.SetIndex( aBoundary.startPos );
+ return aNewPaM;
+}
+
+EditPaM ImpEditEngine::EndOfWord( const EditPaM& rPaM )
+{
+ EditPaM aNewPaM( rPaM );
+
+ // we need to increase the position by 1 when retrieving the locale
+ // since the attribute for the char left to the cursor position is returned
+ EditPaM aTmpPaM( aNewPaM );
+ if ( aTmpPaM.GetIndex() < rPaM.GetNode()->Len() )
+ aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
+ lang::Locale aLocale( GetLocale( aTmpPaM ) );
+
+ uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
+ i18n::Boundary aBoundary = _xBI->getWordBoundary(
+ rPaM.GetNode()->GetString(), rPaM.GetIndex(), aLocale, css::i18n::WordType::ANYWORD_IGNOREWHITESPACES, true);
+
+ aNewPaM.SetIndex( aBoundary.endPos );
+ return aNewPaM;
+}
+
+EditSelection ImpEditEngine::SelectWord( const EditSelection& rCurSel, sal_Int16 nWordType, bool bAcceptStartOfWord )
+{
+ EditSelection aNewSel( rCurSel );
+ EditPaM aPaM( rCurSel.Max() );
+
+ // we need to increase the position by 1 when retrieving the locale
+ // since the attribute for the char left to the cursor position is returned
+ EditPaM aTmpPaM( aPaM );
+ if ( aTmpPaM.GetIndex() < aPaM.GetNode()->Len() )
+ aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
+ lang::Locale aLocale( GetLocale( aTmpPaM ) );
+
+ uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
+ sal_Int16 nType = _xBI->getWordType(
+ aPaM.GetNode()->GetString(), aPaM.GetIndex(), aLocale);
+
+ if ( nType == i18n::WordType::ANY_WORD )
+ {
+ i18n::Boundary aBoundary = _xBI->getWordBoundary(
+ aPaM.GetNode()->GetString(), aPaM.GetIndex(), aLocale, nWordType, true);
+
+ // don't select when cursor at end of word
+ if ( ( aBoundary.endPos > aPaM.GetIndex() ) &&
+ ( ( aBoundary.startPos < aPaM.GetIndex() ) || ( bAcceptStartOfWord && ( aBoundary.startPos == aPaM.GetIndex() ) ) ) )
+ {
+ aNewSel.Min().SetIndex( aBoundary.startPos );
+ aNewSel.Max().SetIndex( aBoundary.endPos );
+ }
+ }
+
+ return aNewSel;
+}
+
+EditSelection ImpEditEngine::SelectSentence( const EditSelection& rCurSel )
+ const
+{
+ uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
+ const EditPaM& rPaM = rCurSel.Min();
+ const ContentNode* pNode = rPaM.GetNode();
+ // #i50710# line breaks are marked with 0x01 - the break iterator prefers 0x0a for that
+ const OUString sParagraph = pNode->GetString().replaceAll("\x01", "\x0a");
+ //return Null if search starts at the beginning of the string
+ sal_Int32 nStart = rPaM.GetIndex() ? _xBI->beginOfSentence( sParagraph, rPaM.GetIndex(), GetLocale( rPaM ) ) : 0;
+
+ sal_Int32 nEnd = _xBI->endOfSentence(
+ pNode->GetString(), rPaM.GetIndex(), GetLocale(rPaM));
+
+ EditSelection aNewSel( rCurSel );
+ OSL_ENSURE(pNode->Len() ? (nStart < pNode->Len()) : (nStart == 0), "sentence start index out of range");
+ OSL_ENSURE(nEnd <= pNode->Len(), "sentence end index out of range");
+ aNewSel.Min().SetIndex( nStart );
+ aNewSel.Max().SetIndex( nEnd );
+ return aNewSel;
+}
+
+bool ImpEditEngine::IsInputSequenceCheckingRequired( sal_Unicode nChar, const EditSelection& rCurSel ) const
+{
+ uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
+
+ // get the index that really is first
+ const sal_Int32 nFirstPos = std::min(rCurSel.Min().GetIndex(), rCurSel.Max().GetIndex());
+
+ bool bIsSequenceChecking =
+ SvtCTLOptions::IsCTLFontEnabled() &&
+ SvtCTLOptions::IsCTLSequenceChecking() &&
+ nFirstPos != 0 && /* first char needs not to be checked */
+ _xBI.is() && i18n::ScriptType::COMPLEX == _xBI->getScriptType( OUString( nChar ), 0 );
+
+ return bIsSequenceChecking;
+}
+
+static bool lcl_HasStrongLTR ( std::u16string_view rTxt, sal_Int32 nStart, sal_Int32 nEnd )
+ {
+ for( sal_Int32 nCharIdx = nStart; nCharIdx < nEnd; ++nCharIdx )
+ {
+ const UCharDirection nCharDir = u_charDirection ( rTxt[ nCharIdx ] );
+ if ( nCharDir == U_LEFT_TO_RIGHT ||
+ nCharDir == U_LEFT_TO_RIGHT_EMBEDDING ||
+ nCharDir == U_LEFT_TO_RIGHT_OVERRIDE )
+ return true;
+ }
+ return false;
+ }
+
+
+void ImpEditEngine::InitScriptTypes( sal_Int32 nPara )
+{
+ ParaPortion* pParaPortion = GetParaPortions().SafeGetObject( nPara );
+ if (!pParaPortion)
+ return;
+
+ ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
+ rTypes.clear();
+
+ ContentNode* pNode = pParaPortion->GetNode();
+ if ( !pNode->Len() )
+ return;
+
+ uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
+
+ OUString aText = pNode->GetString();
+
+ // To handle fields put the character from the field in the string,
+ // because endOfScript( ... ) will skip the CH_FEATURE, because this is WEAK
+ const EditCharAttrib* pField = pNode->GetCharAttribs().FindNextAttrib( EE_FEATURE_FIELD, 0 );
+ while ( pField )
+ {
+ const OUString aFldText = static_cast<const EditCharAttribField*>(pField)->GetFieldValue();
+ if ( !aFldText.isEmpty() )
+ {
+ aText = aText.replaceAt( pField->GetStart(), 1, aFldText.subView(0,1) );
+ short nFldScriptType = _xBI->getScriptType( aFldText, 0 );
+
+ for ( sal_Int32 nCharInField = 1; nCharInField < aFldText.getLength(); nCharInField++ )
+ {
+ short nTmpType = _xBI->getScriptType( aFldText, nCharInField );
+
+ // First char from field wins...
+ if ( nFldScriptType == i18n::ScriptType::WEAK )
+ {
+ nFldScriptType = nTmpType;
+ aText = aText.replaceAt( pField->GetStart(), 1, aFldText.subView(nCharInField,1) );
+ }
+
+ // ... but if the first one is LATIN, and there are CJK or CTL chars too,
+ // we prefer that ScriptType because we need another font.
+ if ( ( nTmpType == i18n::ScriptType::ASIAN ) || ( nTmpType == i18n::ScriptType::COMPLEX ) )
+ {
+ aText = aText.replaceAt( pField->GetStart(), 1, aFldText.subView(nCharInField,1) );
+ break;
+ }
+ }
+ }
+ // #112831# Last Field might go from 0xffff to 0x0000
+ pField = pField->GetEnd() ? pNode->GetCharAttribs().FindNextAttrib( EE_FEATURE_FIELD, pField->GetEnd() ) : nullptr;
+ }
+
+ sal_Int32 nTextLen = aText.getLength();
+
+ sal_Int32 nPos = 0;
+ short nScriptType = _xBI->getScriptType( aText, nPos );
+ rTypes.emplace_back( nScriptType, nPos, nTextLen );
+ nPos = _xBI->endOfScript( aText, nPos, nScriptType );
+ while ( ( nPos != -1 ) && ( nPos < nTextLen ) )
+ {
+ rTypes.back().nEndPos = nPos;
+
+ nScriptType = _xBI->getScriptType( aText, nPos );
+ tools::Long nEndPos = _xBI->endOfScript( aText, nPos, nScriptType );
+
+ if ( ( nScriptType == i18n::ScriptType::WEAK ) || ( nScriptType == rTypes.back().nScriptType ) )
+ {
+ // Expand last ScriptTypePosInfo, don't create weak or unnecessary portions
+ rTypes.back().nEndPos = nEndPos;
+ }
+ else
+ {
+ auto nPrevPos = nPos;
+ auto nPrevChar = aText.iterateCodePoints(&nPrevPos, -1);
+ if (_xBI->getScriptType(aText, nPrevPos) == i18n::ScriptType::WEAK)
+ {
+ auto nChar = aText.iterateCodePoints(&nPos, 0);
+ auto nType = unicode::getUnicodeType(nChar);
+ if (nType == css::i18n::UnicodeType::NON_SPACING_MARK ||
+ nType == css::i18n::UnicodeType::ENCLOSING_MARK ||
+ nType == css::i18n::UnicodeType::COMBINING_SPACING_MARK ||
+ (nPrevChar == 0x202F /* NNBSP, tdf#112594 */ &&
+ u_getIntPropertyValue(nChar, UCHAR_SCRIPT) == USCRIPT_MONGOLIAN))
+ {
+ rTypes.back().nEndPos = nPos = nPrevPos;
+ break;
+ }
+ }
+ rTypes.emplace_back( nScriptType, nPos, nTextLen );
+ }
+
+ nPos = nEndPos;
+ }
+
+ if ( rTypes[0].nScriptType == i18n::ScriptType::WEAK )
+ rTypes[0].nScriptType = ( rTypes.size() > 1 ) ? rTypes[1].nScriptType : SvtLanguageOptions::GetI18NScriptTypeOfLanguage( GetDefaultLanguage() );
+
+ // create writing direction information:
+ if ( pParaPortion->aWritingDirectionInfos.empty() )
+ InitWritingDirections( nPara );
+
+ // i89825: Use CTL font for numbers embedded into an RTL run:
+ WritingDirectionInfos& rDirInfos = pParaPortion->aWritingDirectionInfos;
+ for (const WritingDirectionInfo & rDirInfo : rDirInfos)
+ {
+ const sal_Int32 nStart = rDirInfo.nStartPos;
+ const sal_Int32 nEnd = rDirInfo.nEndPos;
+ const sal_uInt8 nCurrDirType = rDirInfo.nType;
+
+ if ( nCurrDirType % 2 == UBIDI_RTL || // text in RTL run
+ ( nCurrDirType > UBIDI_LTR && !lcl_HasStrongLTR( aText, nStart, nEnd ) ) ) // non-strong text in embedded LTR run
+ {
+ size_t nIdx = 0;
+
+ // Skip entries in ScriptArray which are not inside the RTL run:
+ while ( nIdx < rTypes.size() && rTypes[nIdx].nStartPos < nStart )
+ ++nIdx;
+
+ // Remove any entries *inside* the current run:
+ while (nIdx < rTypes.size() && rTypes[nIdx].nEndPos <= nEnd)
+ {
+ // coverity[use_iterator] - we're protected from a bad iterator by the above condition
+ rTypes.erase(rTypes.begin() + nIdx);
+ }
+
+ // special case:
+ if(nIdx < rTypes.size() && rTypes[nIdx].nStartPos < nStart && rTypes[nIdx].nEndPos > nEnd)
+ {
+ rTypes.insert( rTypes.begin()+nIdx, ScriptTypePosInfo( rTypes[nIdx].nScriptType, nEnd, rTypes[nIdx].nEndPos ) );
+ rTypes[nIdx].nEndPos = nStart;
+ }
+
+ if( nIdx )
+ rTypes[nIdx - 1].nEndPos = nStart;
+
+ rTypes.insert( rTypes.begin()+nIdx, ScriptTypePosInfo( i18n::ScriptType::COMPLEX, nStart, nEnd) );
+ ++nIdx;
+
+ if( nIdx < rTypes.size() )
+ rTypes[nIdx].nStartPos = nEnd;
+ }
+ }
+}
+
+namespace {
+
+struct FindByPos
+{
+ explicit FindByPos(sal_Int32 nPos)
+ : mnPos(nPos)
+ {
+ }
+
+ bool operator()(const ScriptTypePosInfos::value_type& rValue)
+ {
+ return rValue.nStartPos <= mnPos && rValue.nEndPos >= mnPos;
+ }
+
+private:
+ sal_Int32 mnPos;
+};
+
+}
+
+sal_uInt16 ImpEditEngine::GetI18NScriptType( const EditPaM& rPaM, sal_Int32* pEndPos ) const
+{
+ sal_uInt16 nScriptType = 0;
+
+ if ( pEndPos )
+ *pEndPos = rPaM.GetNode()->Len();
+
+ if ( rPaM.GetNode()->Len() )
+ {
+ sal_Int32 nPara = GetEditDoc().GetPos( rPaM.GetNode() );
+ const ParaPortion* pParaPortion = GetParaPortions().SafeGetObject( nPara );
+ if (pParaPortion)
+ {
+ if ( pParaPortion->aScriptInfos.empty() )
+ const_cast<ImpEditEngine*>(this)->InitScriptTypes( nPara );
+
+ const ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
+
+ const sal_Int32 nPos = rPaM.GetIndex();
+ ScriptTypePosInfos::const_iterator itr = std::find_if(rTypes.begin(), rTypes.end(), FindByPos(nPos));
+ if(itr != rTypes.end())
+ {
+ nScriptType = itr->nScriptType;
+ if( pEndPos )
+ *pEndPos = itr->nEndPos;
+ }
+ }
+ }
+ return nScriptType ? nScriptType : SvtLanguageOptions::GetI18NScriptTypeOfLanguage( GetDefaultLanguage() );
+}
+
+SvtScriptType ImpEditEngine::GetItemScriptType( const EditSelection& rSel ) const
+{
+ EditSelection aSel( rSel );
+ aSel.Adjust( maEditDoc );
+
+ SvtScriptType nScriptType = SvtScriptType::NONE;
+
+ sal_Int32 nStartPara = GetEditDoc().GetPos( aSel.Min().GetNode() );
+ sal_Int32 nEndPara = GetEditDoc().GetPos( aSel.Max().GetNode() );
+
+ for ( sal_Int32 nPara = nStartPara; nPara <= nEndPara; nPara++ )
+ {
+ const ParaPortion* pParaPortion = GetParaPortions().SafeGetObject( nPara );
+ if (!pParaPortion)
+ continue;
+
+ if ( pParaPortion->aScriptInfos.empty() )
+ const_cast<ImpEditEngine*>(this)->InitScriptTypes( nPara );
+
+ const ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
+
+ // find all the scripts of this range
+ sal_Int32 nS = ( nPara == nStartPara ) ? aSel.Min().GetIndex() : 0;
+ sal_Int32 nE = ( nPara == nEndPara ) ? aSel.Max().GetIndex() : pParaPortion->GetNode()->Len();
+
+ //no selection, just bare cursor
+ if (nStartPara == nEndPara && nS == nE)
+ {
+ //If we are not at the start of the paragraph we want the properties of the
+ //preceding character. Otherwise get the properties of the next (or what the
+ //next would have if it existed)
+ if (nS != 0)
+ --nS;
+ else
+ ++nE;
+ }
+
+ for (const ScriptTypePosInfo & rType : rTypes)
+ {
+ bool bStartInRange = rType.nStartPos <= nS && nS < rType.nEndPos;
+ bool bEndInRange = rType.nStartPos < nE && nE <= rType.nEndPos;
+
+ if (bStartInRange || bEndInRange)
+ {
+ if ( rType.nScriptType != i18n::ScriptType::WEAK )
+ nScriptType |= SvtLanguageOptions::FromI18NToSvtScriptType( rType.nScriptType );
+ }
+ }
+ }
+ return bool(nScriptType) ? nScriptType : SvtLanguageOptions::GetScriptTypeOfLanguage( GetDefaultLanguage() );
+}
+
+bool ImpEditEngine::IsScriptChange( const EditPaM& rPaM ) const
+{
+ bool bScriptChange = false;
+
+ if ( rPaM.GetNode()->Len() )
+ {
+ sal_Int32 nPara = GetEditDoc().GetPos( rPaM.GetNode() );
+ const ParaPortion* pParaPortion = GetParaPortions().SafeGetObject( nPara );
+ if (pParaPortion)
+ {
+ if ( pParaPortion->aScriptInfos.empty() )
+ const_cast<ImpEditEngine*>(this)->InitScriptTypes( nPara );
+
+ const ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
+ const sal_Int32 nPos = rPaM.GetIndex();
+ for (const ScriptTypePosInfo & rType : rTypes)
+ {
+ if ( rType.nStartPos == nPos )
+ {
+ bScriptChange = true;
+ break;
+ }
+ }
+ }
+ }
+ return bScriptChange;
+}
+
+bool ImpEditEngine::HasScriptType( sal_Int32 nPara, sal_uInt16 nType ) const
+{
+ bool bTypeFound = false;
+
+ const ParaPortion* pParaPortion = GetParaPortions().SafeGetObject( nPara );
+ if (pParaPortion)
+ {
+ if ( pParaPortion->aScriptInfos.empty() )
+ const_cast<ImpEditEngine*>(this)->InitScriptTypes( nPara );
+
+ const ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
+ for ( size_t n = rTypes.size(); n && !bTypeFound; )
+ {
+ if ( rTypes[--n].nScriptType == nType )
+ bTypeFound = true;
+ }
+ }
+ return bTypeFound;
+}
+
+void ImpEditEngine::InitWritingDirections( sal_Int32 nPara )
+{
+ ParaPortion* pParaPortion = GetParaPortions().SafeGetObject( nPara );
+ if (!pParaPortion)
+ return;
+
+ WritingDirectionInfos& rInfos = pParaPortion->aWritingDirectionInfos;
+ rInfos.clear();
+
+ if (pParaPortion->GetNode()->Len() && !mbFuzzing)
+ {
+ const OUString aText = pParaPortion->GetNode()->GetString();
+
+ // Bidi functions from icu 2.0
+
+ UErrorCode nError = U_ZERO_ERROR;
+ UBiDi* pBidi = ubidi_openSized( aText.getLength(), 0, &nError );
+ nError = U_ZERO_ERROR;
+
+ const UBiDiLevel nBidiLevel = IsRightToLeft(nPara) ? 1 /*RTL*/ : 0 /*LTR*/;
+ ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aText.getStr()), aText.getLength(), nBidiLevel, nullptr, &nError );
+ nError = U_ZERO_ERROR;
+
+ int32_t nCount = ubidi_countRuns( pBidi, &nError );
+
+ /* ubidi_countRuns can return -1 in case of error */
+ if (nCount > 0)
+ {
+ int32_t nStart = 0;
+ int32_t nEnd;
+ UBiDiLevel nCurrDir;
+
+ for (int32_t nIdx = 0; nIdx < nCount; ++nIdx)
+ {
+ ubidi_getLogicalRun( pBidi, nStart, &nEnd, &nCurrDir );
+ rInfos.emplace_back( nCurrDir, nStart, nEnd );
+ nStart = nEnd;
+ }
+ }
+
+ ubidi_close( pBidi );
+ }
+
+ // No infos mean ubidi error, default to LTR
+ if ( rInfos.empty() )
+ rInfos.emplace_back( 0, 0, pParaPortion->GetNode()->Len() );
+
+}
+
+bool ImpEditEngine::IsRightToLeft( sal_Int32 nPara ) const
+{
+ bool bR2L = false;
+ const SvxFrameDirectionItem* pFrameDirItem = nullptr;
+
+ if ( !IsEffectivelyVertical() )
+ {
+ bR2L = GetDefaultHorizontalTextDirection() == EEHorizontalTextDirection::R2L;
+ pFrameDirItem = &GetParaAttrib( nPara, EE_PARA_WRITINGDIR );
+ if ( pFrameDirItem->GetValue() == SvxFrameDirection::Environment )
+ {
+ // #103045# if DefaultHorizontalTextDirection is set, use that value, otherwise pool default.
+ if ( GetDefaultHorizontalTextDirection() != EEHorizontalTextDirection::Default )
+ {
+ pFrameDirItem = nullptr; // bR2L already set to default horizontal text direction
+ }
+ else
+ {
+ // Use pool default
+ pFrameDirItem = &GetEmptyItemSet().Get(EE_PARA_WRITINGDIR);
+ }
+ }
+ }
+
+ if ( pFrameDirItem )
+ bR2L = pFrameDirItem->GetValue() == SvxFrameDirection::Horizontal_RL_TB;
+
+ return bR2L;
+}
+
+bool ImpEditEngine::HasDifferentRTLLevels( const ContentNode* pNode )
+{
+ bool bHasDifferentRTLLevels = false;
+
+ sal_Int32 nPara = GetEditDoc().GetPos( pNode );
+ ParaPortion* pParaPortion = GetParaPortions().SafeGetObject( nPara );
+ if (pParaPortion)
+ {
+ sal_uInt16 nRTLLevel = IsRightToLeft( nPara ) ? 1 : 0;
+ for ( sal_Int32 n = 0; n < pParaPortion->GetTextPortions().Count(); n++ )
+ {
+ const TextPortion& rTextPortion = pParaPortion->GetTextPortions()[n];
+ if ( rTextPortion.GetRightToLeftLevel() != nRTLLevel )
+ {
+ bHasDifferentRTLLevels = true;
+ break;
+ }
+ }
+ }
+ return bHasDifferentRTLLevels;
+}
+
+
+sal_uInt8 ImpEditEngine::GetRightToLeft( sal_Int32 nPara, sal_Int32 nPos, sal_Int32* pStart, sal_Int32* pEnd )
+{
+ sal_uInt8 nRightToLeft = 0;
+
+ ContentNode* pNode = maEditDoc.GetObject( nPara );
+ if ( pNode && pNode->Len() )
+ {
+ ParaPortion* pParaPortion = GetParaPortions().SafeGetObject( nPara );
+ if (pParaPortion)
+ {
+ if ( pParaPortion->aWritingDirectionInfos.empty() )
+ InitWritingDirections( nPara );
+
+ WritingDirectionInfos& rDirInfos = pParaPortion->aWritingDirectionInfos;
+ for (const WritingDirectionInfo & rDirInfo : rDirInfos)
+ {
+ if ( ( rDirInfo.nStartPos <= nPos ) && ( rDirInfo.nEndPos >= nPos ) )
+ {
+ nRightToLeft = rDirInfo.nType;
+ if ( pStart )
+ *pStart = rDirInfo.nStartPos;
+ if ( pEnd )
+ *pEnd = rDirInfo.nEndPos;
+ break;
+ }
+ }
+ }
+ }
+ return nRightToLeft;
+}
+
+SvxAdjust ImpEditEngine::GetJustification( sal_Int32 nPara ) const
+{
+ SvxAdjust eJustification = SvxAdjust::Left;
+
+ if (!maStatus.IsOutliner())
+ {
+ eJustification = GetParaAttrib( nPara, EE_PARA_JUST ).GetAdjust();
+
+ if ( IsRightToLeft( nPara ) )
+ {
+ if ( eJustification == SvxAdjust::Left )
+ eJustification = SvxAdjust::Right;
+ else if ( eJustification == SvxAdjust::Right )
+ eJustification = SvxAdjust::Left;
+ }
+ }
+ return eJustification;
+}
+
+SvxCellJustifyMethod ImpEditEngine::GetJustifyMethod( sal_Int32 nPara ) const
+{
+ const SvxJustifyMethodItem& rItem = GetParaAttrib(nPara, EE_PARA_JUST_METHOD);
+ return static_cast<SvxCellJustifyMethod>(rItem.GetEnumValue());
+}
+
+SvxCellVerJustify ImpEditEngine::GetVerJustification( sal_Int32 nPara ) const
+{
+ const SvxVerJustifyItem& rItem = GetParaAttrib(nPara, EE_PARA_VER_JUST);
+ return static_cast<SvxCellVerJustify>(rItem.GetEnumValue());
+}
+
+// Text changes
+void ImpEditEngine::ImpRemoveChars( const EditPaM& rPaM, sal_Int32 nChars )
+{
+ if ( IsUndoEnabled() && !IsInUndo() )
+ {
+ const OUString aStr( rPaM.GetNode()->Copy( rPaM.GetIndex(), nChars ) );
+
+ // Check whether attributes are deleted or changed:
+ const sal_Int32 nStart = rPaM.GetIndex();
+ const sal_Int32 nEnd = nStart + nChars;
+ const CharAttribList::AttribsType& rAttribs = rPaM.GetNode()->GetCharAttribs().GetAttribs();
+ for (const auto & rAttrib : rAttribs)
+ {
+ const EditCharAttrib& rAttr = *rAttrib;
+ if (rAttr.GetEnd() >= nStart && rAttr.GetStart() < nEnd)
+ {
+ EditSelection aSel( rPaM );
+ aSel.Max().SetIndex( aSel.Max().GetIndex() + nChars );
+ InsertUndo( CreateAttribUndo( aSel, GetEmptyItemSet() ) );
+ break; // for
+ }
+ }
+ InsertUndo(std::make_unique<EditUndoRemoveChars>(pEditEngine, CreateEPaM(rPaM), aStr));
+ }
+
+ maEditDoc.RemoveChars( rPaM, nChars );
+}
+
+EditSelection ImpEditEngine::ImpMoveParagraphs( Range aOldPositions, sal_Int32 nNewPos )
+{
+ aOldPositions.Normalize();
+ bool bValidAction = ( static_cast<tools::Long>(nNewPos) < aOldPositions.Min() ) || ( static_cast<tools::Long>(nNewPos) > aOldPositions.Max() );
+ OSL_ENSURE( bValidAction, "Move in itself?" );
+ OSL_ENSURE( aOldPositions.Max() <= static_cast<tools::Long>(GetParaPortions().Count()), "totally over it: MoveParagraphs" );
+
+ EditSelection aSelection;
+
+ if ( !bValidAction )
+ {
+ aSelection = maEditDoc.GetStartPaM();
+ return aSelection;
+ }
+
+ sal_Int32 nParaCount = GetParaPortions().Count();
+
+ if ( nNewPos >= nParaCount )
+ nNewPos = nParaCount;
+
+ // Height may change when moving first or last Paragraph
+ ParaPortion* pRecalc1 = nullptr;
+ ParaPortion* pRecalc2 = nullptr;
+ ParaPortion* pRecalc3 = nullptr;
+ ParaPortion* pRecalc4 = nullptr;
+
+ if ( nNewPos == 0 ) // Move to Start
+ {
+ pRecalc1 = GetParaPortions()[0];
+ pRecalc2 = GetParaPortions()[aOldPositions.Min()];
+
+ }
+ else if ( nNewPos == nParaCount )
+ {
+ pRecalc1 = GetParaPortions()[nParaCount-1];
+ pRecalc2 = GetParaPortions()[aOldPositions.Max()];
+ }
+
+ if ( aOldPositions.Min() == 0 ) // Move from Start
+ {
+ pRecalc3 = GetParaPortions()[0];
+ pRecalc4 = GetParaPortions()[aOldPositions.Max()+1];
+ }
+ else if ( aOldPositions.Max() == (nParaCount-1) )
+ {
+ pRecalc3 = GetParaPortions()[aOldPositions.Max()];
+ pRecalc4 = GetParaPortions()[aOldPositions.Min()-1];
+ }
+
+ MoveParagraphsInfo aMoveParagraphsInfo( aOldPositions.Min(), aOldPositions.Max(), nNewPos );
+ aBeginMovingParagraphsHdl.Call( aMoveParagraphsInfo );
+
+ if ( IsUndoEnabled() && !IsInUndo())
+ InsertUndo(std::make_unique<EditUndoMoveParagraphs>(pEditEngine, aOldPositions, nNewPos));
+
+ // do not lose sight of the Position !
+ ParaPortion* pDestPortion = GetParaPortions().SafeGetObject( nNewPos );
+
+ ParaPortionList aTmpPortionList;
+ for (tools::Long i = aOldPositions.Min(); i <= aOldPositions.Max(); i++ )
+ {
+ // always aOldPositions.Min(), since Remove().
+ std::unique_ptr<ParaPortion> pTmpPortion = GetParaPortions().Release(aOldPositions.Min());
+ maEditDoc.Release( aOldPositions.Min() );
+ aTmpPortionList.Append(std::move(pTmpPortion));
+ }
+
+ sal_Int32 nRealNewPos = pDestPortion ? GetParaPortions().GetPos( pDestPortion ) : GetParaPortions().Count();
+ assert( nRealNewPos != EE_PARA_NOT_FOUND && "ImpMoveParagraphs: Invalid Position!" );
+
+ sal_Int32 i = 0;
+ while( aTmpPortionList.Count() > 0 )
+ {
+ std::unique_ptr<ParaPortion> pTmpPortion = aTmpPortionList.Release(0);
+ if ( i == 0 )
+ aSelection.Min().SetNode( pTmpPortion->GetNode() );
+
+ aSelection.Max().SetNode( pTmpPortion->GetNode() );
+ aSelection.Max().SetIndex( pTmpPortion->GetNode()->Len() );
+
+ ContentNode* pN = pTmpPortion->GetNode();
+ maEditDoc.Insert(nRealNewPos+i, pN);
+
+ GetParaPortions().Insert(nRealNewPos+i, std::move(pTmpPortion));
+ ++i;
+ }
+
+ aEndMovingParagraphsHdl.Call( aMoveParagraphsInfo );
+
+ if ( GetNotifyHdl().IsSet() )
+ {
+ EENotify aNotify( EE_NOTIFY_PARAGRAPHSMOVED );
+ aNotify.nParagraph = nNewPos;
+ aNotify.nParam1 = aOldPositions.Min();
+ aNotify.nParam2 = aOldPositions.Max();
+ GetNotifyHdl().Call( aNotify );
+ }
+
+ maEditDoc.SetModified( true );
+
+ if ( pRecalc1 )
+ CalcHeight( pRecalc1 );
+ if ( pRecalc2 )
+ CalcHeight( pRecalc2 );
+ if ( pRecalc3 )
+ CalcHeight( pRecalc3 );
+ if ( pRecalc4 )
+ CalcHeight( pRecalc4 );
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ ParaPortionList::DbgCheck(GetParaPortions(), maEditDoc);
+#endif
+ return aSelection;
+}
+
+
+EditPaM ImpEditEngine::ImpConnectParagraphs( ContentNode* pLeft, ContentNode* pRight, bool bBackward )
+{
+ OSL_ENSURE( pLeft != pRight, "Join together the same paragraph ?" );
+ OSL_ENSURE( maEditDoc.GetPos( pLeft ) != EE_PARA_NOT_FOUND, "Inserted node not found (1)" );
+ OSL_ENSURE( maEditDoc.GetPos( pRight ) != EE_PARA_NOT_FOUND, "Inserted node not found (2)" );
+
+ // #i120020# it is possible that left and right are *not* in the desired order (left/right)
+ // so correct it. This correction is needed, else an invalid SfxLinkUndoAction will be
+ // created from ConnectParagraphs below. Assert this situation, it should be corrected by the
+ // caller.
+ if (maEditDoc.GetPos( pLeft ) > maEditDoc.GetPos( pRight ))
+ {
+ OSL_ENSURE(false, "ImpConnectParagraphs with wrong order of pLeft/pRight nodes (!)");
+ std::swap(pLeft, pRight);
+ }
+
+ sal_Int32 nParagraphTobeDeleted = maEditDoc.GetPos( pRight );
+ aDeletedNodes.push_back(std::make_unique<DeletedNodeInfo>( pRight, nParagraphTobeDeleted ));
+
+ GetEditEnginePtr()->ParagraphConnected( maEditDoc.GetPos( pLeft ), maEditDoc.GetPos( pRight ) );
+
+ if ( IsUndoEnabled() && !IsInUndo() )
+ {
+ InsertUndo( std::make_unique<EditUndoConnectParas>(pEditEngine,
+ maEditDoc.GetPos( pLeft ), pLeft->Len(),
+ pLeft->GetContentAttribs().GetItems(), pRight->GetContentAttribs().GetItems(),
+ pLeft->GetStyleSheet(), pRight->GetStyleSheet(), bBackward ) );
+ }
+
+ if ( bBackward )
+ {
+ pLeft->SetStyleSheet( pRight->GetStyleSheet() );
+ // it feels wrong to set pLeft's attribs if pRight is empty, tdf#128046
+ if ( pRight->Len() )
+ pLeft->GetContentAttribs().GetItems().Set( pRight->GetContentAttribs().GetItems() );
+ pLeft->GetCharAttribs().GetDefFont() = pRight->GetCharAttribs().GetDefFont();
+ }
+
+ ParaAttribsChanged( pLeft, true );
+
+ // First search for Portions since pRight is gone after ConnectParagraphs.
+ ParaPortion* pLeftPortion = FindParaPortion( pLeft );
+ assert(pLeftPortion);
+
+ if ( GetStatus().DoOnlineSpelling() )
+ {
+ sal_Int32 nEnd = pLeft->Len();
+ sal_Int32 nInv = nEnd ? nEnd-1 : nEnd;
+ pLeft->GetWrongList()->ClearWrongs( nInv, static_cast<size_t>(-1), pLeft ); // Possibly remove one
+ pLeft->GetWrongList()->SetInvalidRange(nInv, nEnd+1);
+ // Take over misspelled words
+ WrongList* pRWrongs = pRight->GetWrongList();
+ for (auto & elem : *pRWrongs)
+ {
+ if (elem.mnStart != 0) // Not a subsequent
+ {
+ elem.mnStart = elem.mnStart + nEnd;
+ elem.mnEnd = elem.mnEnd + nEnd;
+ pLeft->GetWrongList()->push_back(elem);
+ }
+ }
+ }
+
+ if ( IsCallParaInsertedOrDeleted() )
+ GetEditEnginePtr()->ParagraphDeleted( nParagraphTobeDeleted );
+
+ EditPaM aPaM = maEditDoc.ConnectParagraphs( pLeft, pRight );
+ GetParaPortions().Remove( nParagraphTobeDeleted );
+
+ pLeftPortion->MarkSelectionInvalid( aPaM.GetIndex() );
+
+ // the right node is deleted by EditDoc:ConnectParagraphs().
+ if ( GetTextRanger() )
+ {
+ // By joining together the two, the left is although reformatted,
+ // however if its height does not change then the formatting receives
+ // the change of the total text height too late...
+ for ( sal_Int32 n = nParagraphTobeDeleted; n < GetParaPortions().Count(); n++ )
+ {
+ ParaPortion* pPP = GetParaPortions()[n];
+ pPP->MarkSelectionInvalid( 0 );
+ pPP->GetLines().Reset();
+ }
+ }
+
+ TextModified();
+
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::DeleteLeftOrRight( const EditSelection& rSel, sal_uInt8 nMode, DeleteMode nDelMode )
+{
+ OSL_ENSURE( !rSel.DbgIsBuggy( maEditDoc ), "Index out of range in DeleteLeftOrRight" );
+
+ if ( rSel.HasRange() ) // only then Delete Selection
+ return ImpDeleteSelection( rSel );
+
+ EditPaM aCurPos( rSel.Max() );
+ EditPaM aDelStart( aCurPos );
+ EditPaM aDelEnd( aCurPos );
+ if ( nMode == DEL_LEFT )
+ {
+ if ( nDelMode == DeleteMode::Simple )
+ {
+ sal_uInt16 nCharMode = i18n::CharacterIteratorMode::SKIPCHARACTER;
+ // If we are deleting a variation selector, we want to delete the
+ // whole sequence (cell).
+ sal_Int32 nIndex = aCurPos.GetIndex();
+ if (nIndex > 0)
+ {
+ const OUString& rString = aCurPos.GetNode()->GetString();
+ sal_Int32 nCode = rString.iterateCodePoints(&nIndex, -1);
+ if (unicode::isVariationSelector(nCode))
+ nCharMode = i18n::CharacterIteratorMode::SKIPCELL;
+ }
+ aDelStart = CursorLeft(aCurPos, nCharMode);
+ }
+ else if ( nDelMode == DeleteMode::RestOfWord )
+ {
+ aDelStart = StartOfWord( aCurPos );
+ if ( aDelStart.GetIndex() == aCurPos.GetIndex() )
+ aDelStart = WordLeft( aCurPos );
+ }
+ else // DELMODE_RESTOFCONTENT
+ {
+ aDelStart.SetIndex( 0 );
+ if ( aDelStart == aCurPos )
+ {
+ // Complete paragraph previous
+ ContentNode* pPrev = GetPrevVisNode( aCurPos.GetNode() );
+ if ( pPrev )
+ aDelStart = EditPaM( pPrev, 0 );
+ }
+ }
+ }
+ else
+ {
+ if ( nDelMode == DeleteMode::Simple )
+ {
+ aDelEnd = CursorRight( aCurPos );
+ }
+ else if ( nDelMode == DeleteMode::RestOfWord )
+ {
+ aDelEnd = EndOfWord( aCurPos );
+ if (aDelEnd.GetIndex() == aCurPos.GetIndex())
+ {
+ const sal_Int32 nLen(aCurPos.GetNode()->Len());
+ // end of para?
+ if (aDelEnd.GetIndex() == nLen)
+ {
+ ContentNode* pNext = GetNextVisNode( aCurPos.GetNode() );
+ if ( pNext )
+ aDelEnd = EditPaM( pNext, 0 );
+ }
+ else // there's still something to delete on the right
+ {
+ aDelEnd = EndOfWord( WordRight( aCurPos ) );
+ }
+ }
+ }
+ else // DELMODE_RESTOFCONTENT
+ {
+ aDelEnd.SetIndex( aCurPos.GetNode()->Len() );
+ if ( aDelEnd == aCurPos )
+ {
+ // Complete paragraph next
+ ContentNode* pNext = GetNextVisNode( aCurPos.GetNode() );
+ if ( pNext )
+ aDelEnd = EditPaM( pNext, pNext->Len() );
+ }
+ }
+ }
+
+ // ConnectParagraphs not enough for different Nodes when
+ // DeleteMode::RestOfContent.
+ if ( ( nDelMode == DeleteMode::RestOfContent ) || ( aDelStart.GetNode() == aDelEnd.GetNode() ) )
+ return ImpDeleteSelection( EditSelection( aDelStart, aDelEnd ) );
+
+ return ImpConnectParagraphs(aDelStart.GetNode(), aDelEnd.GetNode());
+}
+
+EditPaM ImpEditEngine::ImpDeleteSelection(const EditSelection& rCurSel)
+{
+ if ( !rCurSel.HasRange() )
+ return rCurSel.Min();
+
+ EditSelection aCurSel(rCurSel);
+ aCurSel.Adjust( maEditDoc );
+ EditPaM aStartPaM(aCurSel.Min());
+ EditPaM aEndPaM(aCurSel.Max());
+
+ if( nullptr != aStartPaM.GetNode() )
+ aStartPaM.GetNode()->checkAndDeleteEmptyAttribs(); // only so that newly set Attributes disappear...
+ if( nullptr != aEndPaM.GetNode() )
+ aEndPaM.GetNode()->checkAndDeleteEmptyAttribs(); // only so that newly set Attributes disappear...
+
+ OSL_ENSURE( aStartPaM.GetIndex() <= aStartPaM.GetNode()->Len(), "Index out of range in ImpDeleteSelection" );
+ OSL_ENSURE( aEndPaM.GetIndex() <= aEndPaM.GetNode()->Len(), "Index out of range in ImpDeleteSelection" );
+
+ sal_Int32 nStartNode = maEditDoc.GetPos( aStartPaM.GetNode() );
+ sal_Int32 nEndNode = maEditDoc.GetPos( aEndPaM.GetNode() );
+
+ OSL_ENSURE( nEndNode != EE_PARA_NOT_FOUND, "Start > End ?!" );
+ OSL_ENSURE( nStartNode <= nEndNode, "Start > End ?!" );
+
+ // Remove all nodes in between...
+ for ( sal_Int32 z = nStartNode+1; z < nEndNode; z++ )
+ {
+ // Always nStartNode+1, due to Remove()!
+ ImpRemoveParagraph( nStartNode+1 );
+ }
+
+ if ( aStartPaM.GetNode() != aEndPaM.GetNode() )
+ {
+ // The Rest of the StartNodes...
+ ImpRemoveChars( aStartPaM, aStartPaM.GetNode()->Len() - aStartPaM.GetIndex() );
+ ParaPortion* pPortion = FindParaPortion( aStartPaM.GetNode() );
+ assert(pPortion);
+ pPortion->MarkSelectionInvalid( aStartPaM.GetIndex() );
+
+ // The beginning of the EndNodes...
+ const sal_Int32 nChars = aEndPaM.GetIndex();
+ aEndPaM.SetIndex( 0 );
+ ImpRemoveChars( aEndPaM, nChars );
+ pPortion = FindParaPortion( aEndPaM.GetNode() );
+ assert(pPortion);
+ pPortion->MarkSelectionInvalid( 0 );
+ // Join together...
+ aStartPaM = ImpConnectParagraphs( aStartPaM.GetNode(), aEndPaM.GetNode() );
+ }
+ else
+ {
+ ImpRemoveChars( aStartPaM, aEndPaM.GetIndex() - aStartPaM.GetIndex() );
+ ParaPortion* pPortion = FindParaPortion( aStartPaM.GetNode() );
+ assert(pPortion);
+ pPortion->MarkInvalid( aEndPaM.GetIndex(), aStartPaM.GetIndex() - aEndPaM.GetIndex() );
+ }
+
+ UpdateSelections();
+ TextModified();
+ return aStartPaM;
+}
+
+void ImpEditEngine::ImpRemoveParagraph( sal_Int32 nPara )
+{
+ ContentNode* pNode = maEditDoc.GetObject( nPara );
+ ContentNode* pNextNode = maEditDoc.GetObject( nPara+1 );
+
+ assert(pNode);
+
+ aDeletedNodes.push_back(std::make_unique<DeletedNodeInfo>( pNode, nPara ));
+
+ // The node is managed by the undo and possibly destroyed!
+ maEditDoc.Release( nPara );
+ GetParaPortions().Remove( nPara );
+
+ if ( IsCallParaInsertedOrDeleted() )
+ {
+ GetEditEnginePtr()->ParagraphDeleted( nPara );
+ }
+
+ // Extra-Space may be determined again in the following. For
+ // ParaAttribsChanged the paragraph is unfortunately formatted again,
+ // however this method should not be time critical!
+ if ( pNextNode )
+ ParaAttribsChanged( pNextNode );
+
+ if ( IsUndoEnabled() && !IsInUndo() )
+ InsertUndo(std::make_unique<EditUndoDelContent>(pEditEngine, pNode, nPara));
+ else
+ {
+ if ( pNode->GetStyleSheet() )
+ EndListening( *pNode->GetStyleSheet() );
+ delete pNode;
+ }
+}
+
+EditPaM ImpEditEngine::AutoCorrect( const EditSelection& rCurSel, sal_Unicode c,
+ bool bOverwrite, vcl::Window const * pFrameWin )
+{
+ // i.e. Calc has special needs regarding a leading single quotation mark
+ // when starting cell input.
+ if (c == '\'' && !IsReplaceLeadingSingleQuotationMark() &&
+ rCurSel.Min() == rCurSel.Max() && rCurSel.Max().GetIndex() == 0)
+ {
+ return InsertTextUserInput( rCurSel, c, bOverwrite );
+ }
+
+ EditSelection aSel( rCurSel );
+ SvxAutoCorrect* pAutoCorrect = SvxAutoCorrCfg::Get().GetAutoCorrect();
+ if ( pAutoCorrect )
+ {
+ if ( aSel.HasRange() )
+ aSel = ImpDeleteSelection( rCurSel );
+
+ // #i78661 allow application to turn off capitalization of
+ // start sentence explicitly.
+ // (This is done by setting IsFirstWordCapitalization to sal_False.)
+ bool bOldCapitalStartSentence = pAutoCorrect->IsAutoCorrFlag( ACFlags::CapitalStartSentence );
+ if (!IsFirstWordCapitalization())
+ {
+ ESelection aESel( CreateESel(aSel) );
+ EditSelection aFirstWordSel;
+ EditSelection aSecondWordSel;
+ if (aESel.nEndPara == 0) // is this the first para?
+ {
+ // select first word...
+ // start by checking if para starts with word.
+ aFirstWordSel = SelectWord( CreateSel(ESelection()) );
+ if (aFirstWordSel.Min().GetIndex() == 0 && aFirstWordSel.Max().GetIndex() == 0)
+ {
+ // para does not start with word -> select next/first word
+ EditPaM aRightWord( WordRight( aFirstWordSel.Max() ) );
+ aFirstWordSel = SelectWord( EditSelection( aRightWord ) );
+ }
+
+ // select second word
+ // (sometimes aSel might not point to the end of the first word
+ // but to some following char like '.'. ':', ...
+ // In those cases we need aSecondWordSel to see if aSel
+ // will actually effect the first word.)
+ EditPaM aRight2Word( WordRight( aFirstWordSel.Max() ) );
+ aSecondWordSel = SelectWord( EditSelection( aRight2Word ) );
+ }
+ bool bIsFirstWordInFirstPara = aESel.nEndPara == 0 &&
+ aFirstWordSel.Max().GetIndex() <= aSel.Max().GetIndex() &&
+ aSel.Max().GetIndex() <= aSecondWordSel.Min().GetIndex();
+
+ if (bIsFirstWordInFirstPara)
+ pAutoCorrect->SetAutoCorrFlag( ACFlags::CapitalStartSentence, IsFirstWordCapitalization() );
+ }
+
+ ContentNode* pNode = aSel.Max().GetNode();
+ const sal_Int32 nIndex = aSel.Max().GetIndex();
+ EdtAutoCorrDoc aAuto(pEditEngine, pNode, nIndex, c);
+ // FIXME: this _must_ be called with reference to the actual node text!
+ OUString const& rNodeString(pNode->GetString());
+ pAutoCorrect->DoAutoCorrect(
+ aAuto, rNodeString, nIndex, c, !bOverwrite, mbNbspRunNext, pFrameWin );
+ aSel.Max().SetIndex( aAuto.GetCursor() );
+
+ // #i78661 since the SvxAutoCorrect object used here is
+ // shared we need to reset the value to its original state.
+ pAutoCorrect->SetAutoCorrFlag( ACFlags::CapitalStartSentence, bOldCapitalStartSentence );
+ }
+ return aSel.Max();
+}
+
+
+EditPaM ImpEditEngine::InsertTextUserInput( const EditSelection& rCurSel,
+ sal_Unicode c, bool bOverwrite )
+{
+ OSL_ENSURE( c != '\t', "Tab for InsertText ?" );
+ OSL_ENSURE( c != '\n', "Word wrapping for InsertText ?");
+
+ EditPaM aPaM( rCurSel.Min() );
+
+ bool bDoOverwrite = bOverwrite &&
+ ( aPaM.GetIndex() < aPaM.GetNode()->Len() );
+
+ bool bUndoAction = ( rCurSel.HasRange() || bDoOverwrite );
+
+ if ( bUndoAction )
+ UndoActionStart( EDITUNDO_INSERT );
+
+ if ( rCurSel.HasRange() )
+ {
+ aPaM = ImpDeleteSelection( rCurSel );
+ }
+ else if ( bDoOverwrite )
+ {
+ // If selected, then do not also overwrite a character!
+ EditSelection aTmpSel( aPaM );
+ aTmpSel.Max().SetIndex( aTmpSel.Max().GetIndex()+1 );
+ OSL_ENSURE( !aTmpSel.DbgIsBuggy( maEditDoc ), "Overwrite: Wrong selection! ");
+ ImpDeleteSelection( aTmpSel );
+ }
+
+ if ( aPaM.GetNode()->Len() < MAXCHARSINPARA )
+ {
+ if (IsInputSequenceCheckingRequired( c, rCurSel ))
+ {
+ uno::Reference < i18n::XExtendedInputSequenceChecker > _xISC( ImplGetInputSequenceChecker() );
+
+ if (_xISC)
+ {
+ const sal_Int32 nTmpPos = aPaM.GetIndex();
+ sal_Int16 nCheckMode = SvtCTLOptions::IsCTLSequenceCheckingRestricted() ?
+ i18n::InputSequenceCheckMode::STRICT : i18n::InputSequenceCheckMode::BASIC;
+
+ // the text that needs to be checked is only the one
+ // before the current cursor position
+ const OUString aOldText( aPaM.GetNode()->Copy(0, nTmpPos) );
+ OUString aNewText( aOldText );
+ if (SvtCTLOptions::IsCTLSequenceCheckingTypeAndReplace())
+ {
+ _xISC->correctInputSequence(aNewText, nTmpPos - 1, c, nCheckMode);
+
+ // find position of first character that has changed
+ sal_Int32 nOldLen = aOldText.getLength();
+ sal_Int32 nNewLen = aNewText.getLength();
+ const sal_Unicode *pOldTxt = aOldText.getStr();
+ const sal_Unicode *pNewTxt = aNewText.getStr();
+ sal_Int32 nChgPos = 0;
+ while ( nChgPos < nOldLen && nChgPos < nNewLen &&
+ pOldTxt[nChgPos] == pNewTxt[nChgPos] )
+ ++nChgPos;
+
+ const OUString aChgText( aNewText.copy( nChgPos ) );
+
+ // select text from first pos to be changed to current pos
+ EditSelection aSel( EditPaM( aPaM.GetNode(), nChgPos ), aPaM );
+
+ if (!aChgText.isEmpty())
+ return InsertText( aSel, aChgText ); // implicitly handles undo
+ else
+ return aPaM;
+ }
+ else
+ {
+ // should the character be ignored (i.e. not get inserted) ?
+ if (!_xISC->checkInputSequence( aOldText, nTmpPos - 1, c, nCheckMode ))
+ return aPaM; // nothing to be done -> no need for undo
+ }
+ }
+
+ // at this point now we will insert the character 'normally' some lines below...
+ }
+
+ if ( IsUndoEnabled() && !IsInUndo() )
+ {
+ std::unique_ptr<EditUndoInsertChars> pNewUndo(new EditUndoInsertChars(pEditEngine, CreateEPaM(aPaM), OUString(c)));
+ bool bTryMerge = !bDoOverwrite && ( c != ' ' );
+ InsertUndo( std::move(pNewUndo), bTryMerge );
+ }
+
+ maEditDoc.InsertText( aPaM, OUStringChar(c) );
+ ParaPortion* pPortion = FindParaPortion( aPaM.GetNode() );
+ assert(pPortion);
+ pPortion->MarkInvalid( aPaM.GetIndex(), 1 );
+ aPaM.SetIndex( aPaM.GetIndex()+1 ); // does not do EditDoc-Method anymore
+ }
+
+ TextModified();
+
+ if ( bUndoAction )
+ UndoActionEnd();
+
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::ImpInsertText(const EditSelection& aCurSel, const OUString& rStr)
+{
+ UndoActionStart( EDITUNDO_INSERT );
+
+ EditPaM aPaM;
+ if ( aCurSel.HasRange() )
+ aPaM = ImpDeleteSelection( aCurSel );
+ else
+ aPaM = aCurSel.Max();
+
+ EditPaM aCurPaM( aPaM ); // for the Invalidate
+
+ // get word boundaries in order to clear possible WrongList entries
+ // and invalidate all the necessary text (everything after and including the
+ // start of the word)
+ // #i107201# do the expensive SelectWord call only if online spelling is active
+ EditSelection aCurWord;
+ if ( GetStatus().DoOnlineSpelling() )
+ aCurWord = SelectWord( aCurPaM, i18n::WordType::DICTIONARY_WORD );
+
+ OUString aText(convertLineEnd(rStr, LINEEND_LF));
+ if (mbFuzzing) //tab expansion performance in editeng is appalling
+ aText = aText.replaceAll("\t","-");
+ SfxVoidItem aTabItem( EE_FEATURE_TAB );
+
+ // Converts to linesep = \n
+ // Token LINE_SEP query,
+ // since the MAC-Compiler makes something else from \n !
+
+ sal_Int32 nStart = 0;
+ while ( nStart < aText.getLength() )
+ {
+ sal_Int32 nEnd = !maStatus.IsSingleLine() ?
+ aText.indexOf( LINE_SEP, nStart ) : -1;
+ if ( nEnd == -1 )
+ nEnd = aText.getLength(); // not dereference!
+
+ // Start == End => empty line
+ if ( nEnd > nStart )
+ {
+ OUString aLine = aText.copy( nStart, nEnd-nStart );
+ sal_Int32 nExistingChars = aPaM.GetNode()->Len();
+ sal_Int32 nChars = nExistingChars + aLine.getLength();
+ if (nChars > MAXCHARSINPARA)
+ {
+ sal_Int32 nMaxNewChars = std::max<sal_Int32>(0, MAXCHARSINPARA - nExistingChars);
+ // Wherever we break, it may be wrong. However, try to find the
+ // previous non-alnum/non-letter character. Note this is only
+ // in the to be appended data, otherwise already existing
+ // characters would have to be moved and PaM to be updated.
+ // Restrict to 2*42, if not found by then assume other data or
+ // language-script uses only letters or idiographs.
+ sal_Int32 nPos = nMaxNewChars;
+ while (nPos-- > 0 && (nMaxNewChars - nPos) <= 84)
+ {
+ auto nNextPos = nPos;
+ const auto c = aLine.iterateCodePoints(&nNextPos);
+ switch (unicode::getUnicodeType(c))
+ {
+ case css::i18n::UnicodeType::UPPERCASE_LETTER:
+ case css::i18n::UnicodeType::LOWERCASE_LETTER:
+ case css::i18n::UnicodeType::TITLECASE_LETTER:
+ case css::i18n::UnicodeType::MODIFIER_LETTER:
+ case css::i18n::UnicodeType::OTHER_LETTER:
+ case css::i18n::UnicodeType::DECIMAL_DIGIT_NUMBER:
+ case css::i18n::UnicodeType::LETTER_NUMBER:
+ case css::i18n::UnicodeType::OTHER_NUMBER:
+ case css::i18n::UnicodeType::CURRENCY_SYMBOL:
+ break;
+ default:
+ {
+ // Ignore NO-BREAK spaces, NBSP, NNBSP, ZWNBSP.
+ if (c == 0x00A0 || c == 0x202F || c == 0xFEFF)
+ break;
+ const auto n = aLine.iterateCodePoints(&nNextPos, 0);
+ if (c == '-' && nNextPos < nMaxNewChars)
+ {
+ // Keep HYPHEN-MINUS with a number to the right.
+ const sal_Int16 t = unicode::getUnicodeType(n);
+ if ( t == css::i18n::UnicodeType::DECIMAL_DIGIT_NUMBER ||
+ t == css::i18n::UnicodeType::LETTER_NUMBER ||
+ t == css::i18n::UnicodeType::OTHER_NUMBER)
+ nMaxNewChars = nPos; // line break before
+ else
+ nMaxNewChars = nNextPos; // line break after
+ }
+ else
+ {
+ nMaxNewChars = nNextPos; // line break after
+ }
+ nPos = 0; // will break loop
+ }
+ }
+ }
+ // Remaining characters end up in the next paragraph. Note that
+ // new nStart will be nEnd+1 below so decrement by one more.
+ nEnd -= (aLine.getLength() - nMaxNewChars + 1);
+ aLine = aLine.copy( 0, nMaxNewChars ); // Delete the Rest...
+ }
+ if ( IsUndoEnabled() && !IsInUndo() )
+ InsertUndo(std::make_unique<EditUndoInsertChars>(pEditEngine, CreateEPaM(aPaM), aLine));
+ // Tabs ?
+ if ( aLine.indexOf( '\t' ) == -1 )
+ aPaM = maEditDoc.InsertText( aPaM, aLine );
+ else
+ {
+ sal_Int32 nStart2 = 0;
+ while ( nStart2 < aLine.getLength() )
+ {
+ sal_Int32 nEnd2 = aLine.indexOf( "\t", nStart2 );
+ if ( nEnd2 == -1 )
+ nEnd2 = aLine.getLength(); // not dereference!
+
+ if ( nEnd2 > nStart2 )
+ aPaM = maEditDoc.InsertText( aPaM, aLine.subView( nStart2, nEnd2-nStart2 ) );
+ if ( nEnd2 < aLine.getLength() )
+ {
+ aPaM = maEditDoc.InsertFeature( aPaM, aTabItem );
+ }
+ nStart2 = nEnd2+1;
+ }
+ }
+ ParaPortion* pPortion = FindParaPortion( aPaM.GetNode() );
+ assert(pPortion);
+
+ if ( GetStatus().DoOnlineSpelling() )
+ {
+ // now remove the Wrongs (red spell check marks) from both words...
+ WrongList *pWrongs = aCurPaM.GetNode()->GetWrongList();
+ if (pWrongs && !pWrongs->empty())
+ pWrongs->ClearWrongs( aCurWord.Min().GetIndex(), aPaM.GetIndex(), aPaM.GetNode() );
+ // ... and mark both words as 'to be checked again'
+ pPortion->MarkInvalid( aCurWord.Min().GetIndex(), aLine.getLength() );
+ }
+ else
+ pPortion->MarkInvalid( aCurPaM.GetIndex(), aLine.getLength() );
+ }
+ if ( nEnd < aText.getLength() )
+ aPaM = ImpInsertParaBreak( aPaM );
+
+ nStart = nEnd+1;
+ }
+
+ UndoActionEnd();
+
+ TextModified();
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::ImpFastInsertText( EditPaM aPaM, const OUString& rStr )
+{
+ OSL_ENSURE( rStr.indexOf( 0x0A ) == -1, "FastInsertText: Newline not allowed! ");
+ OSL_ENSURE( rStr.indexOf( 0x0D ) == -1, "FastInsertText: Newline not allowed! ");
+ OSL_ENSURE( rStr.indexOf( '\t' ) == -1, "FastInsertText: Newline not allowed! ");
+
+ if ( ( aPaM.GetNode()->Len() + rStr.getLength() ) < MAXCHARSINPARA )
+ {
+ if ( IsUndoEnabled() && !IsInUndo() )
+ InsertUndo(std::make_unique<EditUndoInsertChars>(pEditEngine, CreateEPaM(aPaM), rStr));
+
+ aPaM = maEditDoc.InsertText( aPaM, rStr );
+ TextModified();
+ }
+ else
+ {
+ aPaM = ImpInsertText( aPaM, rStr );
+ }
+
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::ImpInsertFeature(const EditSelection& rCurSel, const SfxPoolItem& rItem)
+{
+ EditPaM aPaM;
+ if ( rCurSel.HasRange() )
+ aPaM = ImpDeleteSelection( rCurSel );
+ else
+ aPaM = rCurSel.Max();
+
+ if ( aPaM.GetIndex() >= SAL_MAX_INT32-1 )
+ return aPaM;
+
+ if ( IsUndoEnabled() && !IsInUndo() )
+ InsertUndo(std::make_unique<EditUndoInsertFeature>(pEditEngine, CreateEPaM(aPaM), rItem));
+ aPaM = maEditDoc.InsertFeature( aPaM, rItem );
+ UpdateFields();
+
+ ParaPortion* pPortion = FindParaPortion( aPaM.GetNode() );
+ assert(pPortion);
+ pPortion->MarkInvalid( aPaM.GetIndex()-1, 1 );
+
+ TextModified();
+
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::ImpInsertParaBreak( const EditSelection& rCurSel )
+{
+ EditPaM aPaM;
+ if ( rCurSel.HasRange() )
+ aPaM = ImpDeleteSelection( rCurSel );
+ else
+ aPaM = rCurSel.Max();
+
+ return ImpInsertParaBreak( aPaM );
+}
+
+EditPaM ImpEditEngine::ImpInsertParaBreak( EditPaM& rPaM, bool bKeepEndingAttribs )
+{
+ if ( maEditDoc.Count() >= EE_PARA_MAX_COUNT )
+ {
+ SAL_WARN( "editeng", "ImpEditEngine::ImpInsertParaBreak - can't process more than "
+ << EE_PARA_MAX_COUNT << " paragraphs!");
+ return rPaM;
+ }
+
+ if ( IsUndoEnabled() && !IsInUndo() )
+ InsertUndo(std::make_unique<EditUndoSplitPara>(pEditEngine, maEditDoc.GetPos(rPaM.GetNode()), rPaM.GetIndex()));
+
+ EditPaM aPaM( maEditDoc.InsertParaBreak( rPaM, bKeepEndingAttribs ) );
+ if (auto pStyle = aPaM.GetNode()->GetStyleSheet())
+ StartListening(*pStyle, DuplicateHandling::Allow);
+
+ if ( GetStatus().DoOnlineSpelling() )
+ {
+ sal_Int32 nEnd = rPaM.GetNode()->Len();
+ aPaM.GetNode()->CreateWrongList();
+ WrongList* pLWrongs = rPaM.GetNode()->GetWrongList();
+ WrongList* pRWrongs = aPaM.GetNode()->GetWrongList();
+ // take over misspelled words:
+ for (auto & elem : *pLWrongs)
+ {
+ // Correct only if really a word gets overlapped in the process of
+ // Spell checking
+ if (elem.mnStart > o3tl::make_unsigned(nEnd))
+ {
+ pRWrongs->push_back(elem);
+ editeng::MisspellRange& rRWrong = pRWrongs->back();
+ rRWrong.mnStart = rRWrong.mnStart - nEnd;
+ rRWrong.mnEnd = rRWrong.mnEnd - nEnd;
+ }
+ else if (elem.mnStart < o3tl::make_unsigned(nEnd) && elem.mnEnd > o3tl::make_unsigned(nEnd))
+ elem.mnEnd = nEnd;
+ }
+ sal_Int32 nInv = nEnd ? nEnd-1 : nEnd;
+ if ( nEnd )
+ pLWrongs->SetInvalidRange(nInv, nEnd);
+ else
+ pLWrongs->SetValid();
+ pRWrongs->SetValid();
+ pRWrongs->SetInvalidRange(0, 1); // Only test the first word
+ }
+
+ ParaPortion* pPortion = FindParaPortion( rPaM.GetNode() );
+ assert(pPortion);
+ pPortion->MarkInvalid( rPaM.GetIndex(), 0 );
+
+ // Optimization: Do not place unnecessarily many getPos to Listen!
+ // Here, as in undo, but also in all other methods.
+ sal_Int32 nPos = GetParaPortions().GetPos( pPortion );
+ ParaPortion* pNewPortion = new ParaPortion( aPaM.GetNode() );
+ GetParaPortions().Insert(nPos+1, std::unique_ptr<ParaPortion>(pNewPortion));
+ ParaAttribsChanged( pNewPortion->GetNode() );
+ if ( IsCallParaInsertedOrDeleted() )
+ GetEditEnginePtr()->ParagraphInserted( nPos+1 );
+
+ if( nullptr != rPaM.GetNode() )
+ rPaM.GetNode()->checkAndDeleteEmptyAttribs(); // if empty Attributes have emerged.
+
+ TextModified();
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::ImpFastInsertParagraph( sal_Int32 nPara )
+{
+ if ( IsUndoEnabled() && !IsInUndo() )
+ {
+ if ( nPara )
+ {
+ assert(maEditDoc.GetObject(nPara - 1));
+ InsertUndo(std::make_unique<EditUndoSplitPara>(pEditEngine, nPara-1, maEditDoc.GetObject( nPara-1 )->Len()));
+ }
+ else
+ InsertUndo(std::make_unique<EditUndoSplitPara>(pEditEngine, 0, 0));
+ }
+
+ ContentNode* pNode = new ContentNode( maEditDoc.GetItemPool() );
+ // If flat mode, then later no Font is set:
+ pNode->GetCharAttribs().GetDefFont() = maEditDoc.GetDefFont();
+
+ if ( GetStatus().DoOnlineSpelling() )
+ pNode->CreateWrongList();
+
+ maEditDoc.Insert(nPara, pNode);
+
+ GetParaPortions().Insert(nPara, std::make_unique<ParaPortion>( pNode ));
+ if ( IsCallParaInsertedOrDeleted() )
+ GetEditEnginePtr()->ParagraphInserted( nPara );
+
+ return EditPaM( pNode, 0 );
+}
+
+EditPaM ImpEditEngine::InsertParaBreak(const EditSelection& rCurSel)
+{
+ EditPaM aPaM(ImpInsertParaBreak(rCurSel));
+ if ( maStatus.DoAutoIndenting() )
+ {
+ sal_Int32 nPara = maEditDoc.GetPos( aPaM.GetNode() );
+ OSL_ENSURE( nPara > 0, "AutoIndenting: Error!" );
+ const OUString aPrevParaText( GetEditDoc().GetParaAsString( nPara-1 ) );
+ sal_Int32 n = 0;
+ while ( ( n < aPrevParaText.getLength() ) &&
+ ( ( aPrevParaText[n] == ' ' ) || ( aPrevParaText[n] == '\t' ) ) )
+ {
+ if ( aPrevParaText[n] == '\t' )
+ aPaM = ImpInsertFeature( aPaM, SfxVoidItem( EE_FEATURE_TAB ) );
+ else
+ aPaM = ImpInsertText( aPaM, OUString(aPrevParaText[n]) );
+ n++;
+ }
+
+ }
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::InsertTab(const EditSelection& rCurSel)
+{
+ EditPaM aPaM( ImpInsertFeature(rCurSel, SfxVoidItem(EE_FEATURE_TAB )));
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::InsertField(const EditSelection& rCurSel, const SvxFieldItem& rFld)
+{
+ return ImpInsertFeature(rCurSel, rFld);
+}
+
+bool ImpEditEngine::UpdateFields()
+{
+ bool bChanges = false;
+ sal_Int32 nParas = GetEditDoc().Count();
+ for ( sal_Int32 nPara = 0; nPara < nParas; nPara++ )
+ {
+ bool bChangesInPara = false;
+ ContentNode* pNode = GetEditDoc().GetObject( nPara );
+ assert(pNode);
+ CharAttribList::AttribsType& rAttribs = pNode->GetCharAttribs().GetAttribs();
+ for (std::unique_ptr<EditCharAttrib> & rAttrib : rAttribs)
+ {
+ EditCharAttrib& rAttr = *rAttrib;
+ if (rAttr.Which() == EE_FEATURE_FIELD)
+ {
+ EditCharAttribField& rField = static_cast<EditCharAttribField&>(rAttr);
+ EditCharAttribField aCurrent(rField);
+ rField.Reset();
+
+ if (!maStatus.MarkNonUrlFields() && !maStatus.MarkUrlFields())
+ ; // nothing marked
+ else if (maStatus.MarkNonUrlFields() && maStatus.MarkUrlFields())
+ rField.GetFieldColor() = GetColorConfig().GetColorValue(svtools::WRITERFIELDSHADINGS).nColor;
+ else
+ {
+ bool bURL = false;
+ if (const SvxFieldItem* pFieldItem = dynamic_cast<const SvxFieldItem*>(rField.GetItem()))
+ {
+ if (const SvxFieldData* pFieldData = pFieldItem->GetField())
+ bURL = (dynamic_cast<const SvxURLField* >(pFieldData) != nullptr);
+ }
+ if ((bURL && maStatus.MarkUrlFields()) || (!bURL && maStatus.MarkNonUrlFields()))
+ rField.GetFieldColor() = GetColorConfig().GetColorValue( svtools::WRITERFIELDSHADINGS ).nColor;
+ }
+
+ const OUString aFldValue =
+ GetEditEnginePtr()->CalcFieldValue(
+ static_cast<const SvxFieldItem&>(*rField.GetItem()),
+ nPara, rField.GetStart(), rField.GetTextColor(), rField.GetFieldColor(), rField.GetFldLineStyle() );
+
+ rField.SetFieldValue(aFldValue);
+ if (rField != aCurrent)
+ {
+ bChanges = true;
+ bChangesInPara = true;
+ }
+ }
+ }
+ if ( bChangesInPara )
+ {
+ // If possible be more precise when invalidate.
+ ParaPortion* pPortion = GetParaPortions()[nPara];
+ assert(pPortion);
+ pPortion->MarkSelectionInvalid( 0 );
+ }
+ }
+ return bChanges;
+}
+
+EditPaM ImpEditEngine::InsertLineBreak(const EditSelection& aCurSel)
+{
+ EditPaM aPaM( ImpInsertFeature( aCurSel, SfxVoidItem( EE_FEATURE_LINEBR ) ) );
+ return aPaM;
+}
+
+
+// Helper functions
+
+tools::Rectangle ImpEditEngine::GetEditCursor(const ParaPortion* pPortion, const EditLine* pLine,
+ sal_Int32 nIndex, GetCursorFlags nFlags)
+{
+ assert(pPortion && pLine);
+ // nIndex might be not in the line
+ // Search within the line...
+ tools::Long nX;
+
+ if ((nIndex == pLine->GetStart()) && (nFlags & GetCursorFlags::StartOfLine))
+ {
+ Range aXRange = GetLineXPosStartEnd(pPortion, pLine);
+ nX = !IsRightToLeft(GetEditDoc().GetPos(pPortion->GetNode())) ? aXRange.Min()
+ : aXRange.Max();
+ }
+ else if ((nIndex == pLine->GetEnd()) && (nFlags & GetCursorFlags::EndOfLine))
+ {
+ Range aXRange = GetLineXPosStartEnd(pPortion, pLine);
+ nX = !IsRightToLeft(GetEditDoc().GetPos(pPortion->GetNode())) ? aXRange.Max()
+ : aXRange.Min();
+ }
+ else
+ {
+ nX = GetXPos(pPortion, pLine, nIndex, bool(nFlags & GetCursorFlags::PreferPortionStart));
+ }
+
+ tools::Rectangle aEditCursor;
+ aEditCursor.SetLeft(nX);
+ aEditCursor.SetRight(nX);
+
+ aEditCursor.SetBottom(pLine->GetHeight() - 1);
+ if (nFlags & GetCursorFlags::TextOnly)
+ aEditCursor.SetTop(aEditCursor.Bottom() - pLine->GetTxtHeight() + 1);
+ else
+ aEditCursor.SetTop(aEditCursor.Bottom()
+ - std::min(pLine->GetTxtHeight(), pLine->GetHeight()) + 1);
+ return aEditCursor;
+}
+
+tools::Rectangle ImpEditEngine::PaMtoEditCursor( EditPaM aPaM, GetCursorFlags nFlags )
+{
+ assert( IsUpdateLayout() && "Must not be reached when Update=FALSE: PaMtoEditCursor" );
+
+ tools::Rectangle aEditCursor;
+ const sal_Int32 nIndex = aPaM.GetIndex();
+ const ParaPortion* pPortion = nullptr;
+ const EditLine* pLastLine = nullptr;
+ tools::Rectangle aLineArea;
+
+ auto FindPortionLineAndArea
+ = [&, bEOL(bool(nFlags & GetCursorFlags::EndOfLine))](const LineAreaInfo& rInfo) {
+ if (!rInfo.pLine) // start of ParaPortion
+ {
+ ContentNode* pNode = rInfo.rPortion.GetNode();
+ OSL_ENSURE(pNode, "Invalid Node in Portion!");
+ if (pNode != aPaM.GetNode())
+ return CallbackResult::SkipThisPortion;
+ pPortion = &rInfo.rPortion;
+ }
+ else // guaranteed that this is the correct ParaPortion
+ {
+ pLastLine = rInfo.pLine;
+ aLineArea = rInfo.aArea;
+ if ((rInfo.pLine->GetStart() == nIndex) || (rInfo.pLine->IsIn(nIndex, bEOL)))
+ return CallbackResult::Stop;
+ }
+ return CallbackResult::Continue;
+ };
+ IterateLineAreas(FindPortionLineAndArea, IterFlag::none);
+
+ if (pLastLine)
+ {
+ aEditCursor = GetEditCursor(pPortion, pLastLine, nIndex, nFlags);
+ aEditCursor.Move(getTopLeftDocOffset(aLineArea));
+ }
+ else
+ OSL_FAIL("Line not found!");
+
+ return aEditCursor;
+}
+
+void ImpEditEngine::IterateLineAreas(const IterateLinesAreasFunc& f, IterFlag eOptions)
+{
+ const Point aOrigin(0, 0);
+ Point aLineStart(aOrigin);
+ const tools::Long nVertLineSpacing = CalcVertLineSpacing(aLineStart);
+ const tools::Long nColumnWidth = GetColumnWidth(maPaperSize);
+ sal_Int16 nColumn = 0;
+ for (sal_Int32 n = 0, nPortions = GetParaPortions().Count(); n < nPortions; ++n)
+ {
+ ParaPortion* pPortion = GetParaPortions()[n];
+ bool bSkipThis = true;
+ if (pPortion->IsVisible())
+ {
+ // when typing idle formatting, asynchronous Paint. Invisible Portions may be invalid.
+ if (pPortion->IsInvalid())
+ return;
+
+ LineAreaInfo aInfo{
+ *pPortion, // pPortion
+ nullptr, // pLine
+ 0, // nHeightNeededToNotWrap
+ { aLineStart, Size{ nColumnWidth, pPortion->GetFirstLineOffset() } }, // aArea
+ n, // nPortion
+ 0, // nLine
+ nColumn // nColumn
+ };
+ auto eResult = f(aInfo);
+ if (eResult == CallbackResult::Stop)
+ return;
+ bSkipThis = eResult == CallbackResult::SkipThisPortion;
+
+ sal_uInt16 nSBL = 0;
+ if (!maStatus.IsOutliner())
+ {
+ const SvxLineSpacingItem& rLSItem
+ = pPortion->GetNode()->GetContentAttribs().GetItem(EE_PARA_SBL);
+ nSBL = (rLSItem.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Fix)
+ ? scaleYSpacingValue(rLSItem.GetInterLineSpace())
+ : 0;
+ }
+
+ adjustYDirectionAware(aLineStart, pPortion->GetFirstLineOffset());
+ for (sal_Int32 nLine = 0, nLines = pPortion->GetLines().Count(); nLine < nLines; nLine++)
+ {
+ EditLine& rLine = pPortion->GetLines()[nLine];
+ tools::Long nLineHeight = rLine.GetHeight();
+ if (nLine != nLines - 1)
+ nLineHeight += nVertLineSpacing;
+ MoveToNextLine(aLineStart, nLineHeight, nColumn, aOrigin,
+ &aInfo.nHeightNeededToNotWrap);
+ const bool bInclILS = eOptions & IterFlag::inclILS;
+ if (bInclILS && (nLine != nLines - 1) && !maStatus.IsOutliner())
+ {
+ adjustYDirectionAware(aLineStart, nSBL);
+ nLineHeight += nSBL;
+ }
+
+ if (!bSkipThis)
+ {
+ Point aOtherCorner(aLineStart);
+ adjustXDirectionAware(aOtherCorner, nColumnWidth);
+ adjustYDirectionAware(aOtherCorner, -nLineHeight);
+
+ // Calls to f() for each line
+ aInfo.nColumn = nColumn;
+ aInfo.pLine = &rLine;
+ aInfo.nLine = nLine;
+ aInfo.aArea = tools::Rectangle::Normalize(aLineStart, aOtherCorner);
+ eResult = f(aInfo);
+ if (eResult == CallbackResult::Stop)
+ return;
+ bSkipThis = eResult == CallbackResult::SkipThisPortion;
+ }
+
+ if (!bInclILS && (nLine != nLines - 1) && !maStatus.IsOutliner())
+ adjustYDirectionAware(aLineStart, nSBL);
+ }
+ if (!maStatus.IsOutliner())
+ {
+ const SvxULSpaceItem& rULItem
+ = pPortion->GetNode()->GetContentAttribs().GetItem(EE_PARA_ULSPACE);
+ tools::Long nUL = scaleYSpacingValue(rULItem.GetLower());
+ adjustYDirectionAware(aLineStart, nUL);
+ }
+ }
+ // Invisible ParaPortion has no height (see ParaPortion::GetHeight), don't handle it
+ }
+}
+
+std::tuple<const ParaPortion*, const EditLine*, tools::Long>
+ImpEditEngine::GetPortionAndLine(Point aDocPos)
+{
+ // First find the column from the point
+ sal_Int32 nClickColumn = 0;
+ for (tools::Long nColumnStart = 0, nColumnWidth = GetColumnWidth(maPaperSize);;
+ nColumnStart += mnColumnSpacing + nColumnWidth, ++nClickColumn)
+ {
+ if (aDocPos.X() <= nColumnStart + nColumnWidth + mnColumnSpacing / 2)
+ break;
+ if (nClickColumn >= mnColumns - 1)
+ break;
+ }
+
+ const ParaPortion* pLastPortion = nullptr;
+ const EditLine* pLastLine = nullptr;
+ tools::Long nLineStartX = 0;
+ Point aPos;
+ adjustYDirectionAware(aPos, aDocPos.Y());
+
+ auto FindLastMatchingPortionAndLine = [&](const LineAreaInfo& rInfo) {
+ if (rInfo.pLine) // Only handle lines, not ParaPortion starts
+ {
+ if (rInfo.nColumn > nClickColumn)
+ return CallbackResult::Stop;
+ pLastPortion = &rInfo.rPortion; // Candidate paragraph
+ pLastLine = rInfo.pLine; // Last visible line not later than click position
+ nLineStartX = getTopLeftDocOffset(rInfo.aArea).Width();
+ if (rInfo.nColumn == nClickColumn && getYOverflowDirectionAware(aPos, rInfo.aArea) == 0)
+ return CallbackResult::Stop; // Found it
+ }
+ return CallbackResult::Continue;
+ };
+ IterateLineAreas(FindLastMatchingPortionAndLine, IterFlag::inclILS);
+
+ return { pLastPortion, pLastLine, nLineStartX };
+}
+
+EditPaM ImpEditEngine::GetPaM( Point aDocPos, bool bSmart )
+{
+ assert( IsUpdateLayout() && "Must not be reached when Update=FALSE: GetPaM" );
+
+ if (const auto& [pPortion, pLine, nLineStartX] = GetPortionAndLine(aDocPos); pPortion)
+ {
+ sal_Int32 nCurIndex
+ = GetChar(pPortion, pLine, aDocPos.X() - nLineStartX, bSmart);
+ EditPaM aPaM(pPortion->GetNode(), nCurIndex);
+
+ if (nCurIndex && (nCurIndex == pLine->GetEnd())
+ && (pLine != &pPortion->GetLines()[pPortion->GetLines().Count() - 1]))
+ {
+ aPaM = CursorLeft(aPaM);
+ }
+
+ return aPaM;
+ }
+ return {};
+}
+
+bool ImpEditEngine::IsTextPos(const Point& rDocPos, sal_uInt16 nBorder)
+{
+ if (const auto& [pPortion, pLine, nLineStartX] = GetPortionAndLine(rDocPos); pPortion)
+ {
+ Range aLineXPosStartEnd = GetLineXPosStartEnd(pPortion, pLine);
+ if ((rDocPos.X() >= nLineStartX + aLineXPosStartEnd.Min() - nBorder)
+ && (rDocPos.X() <= nLineStartX + aLineXPosStartEnd.Max() + nBorder))
+ return true;
+ }
+ return false;
+}
+
+sal_uInt32 ImpEditEngine::GetTextHeight() const
+{
+ assert( IsUpdateLayout() && "Should not be used for Update=FALSE: GetTextHeight" );
+ OSL_ENSURE( IsFormatted() || IsFormatting(), "GetTextHeight: Not formatted" );
+ return nCurTextHeight;
+}
+
+sal_uInt32 ImpEditEngine::CalcTextWidth( bool bIgnoreExtraSpace )
+{
+ // If still not formatted and not in the process.
+ // Will be brought in the formatting for AutoPageSize.
+ if ( !IsFormatted() && !IsFormatting() )
+ FormatDoc();
+
+ sal_uInt32 nMaxWidth = 0;
+
+ // Over all the paragraphs ...
+
+ sal_Int32 nParas = GetParaPortions().Count();
+ for ( sal_Int32 nPara = 0; nPara < nParas; nPara++ )
+ {
+ nMaxWidth = std::max(nMaxWidth, CalcParaWidth(nPara, bIgnoreExtraSpace));
+ }
+
+ return nMaxWidth;
+}
+
+sal_uInt32 ImpEditEngine::CalcParaWidth( sal_Int32 nPara, bool bIgnoreExtraSpace )
+{
+ // If still not formatted and not in the process.
+ // Will be brought in the formatting for AutoPageSize.
+ if ( !IsFormatted() && !IsFormatting() )
+ FormatDoc();
+
+ tools::Long nMaxWidth = 0;
+
+ // Over all the paragraphs ...
+
+ OSL_ENSURE( 0 <= nPara && nPara < GetParaPortions().Count(), "CalcParaWidth: Out of range" );
+ ParaPortion* pPortion = GetParaPortions()[nPara];
+ if ( pPortion && pPortion->IsVisible() )
+ {
+ const SvxLRSpaceItem& rLRItem = GetLRSpaceItem( pPortion->GetNode() );
+ sal_Int32 nSpaceBeforeAndMinLabelWidth = GetSpaceBeforeAndMinLabelWidth( pPortion->GetNode() );
+
+
+ // On the lines of the paragraph ...
+
+ sal_Int32 nLines = pPortion->GetLines().Count();
+ for ( sal_Int32 nLine = 0; nLine < nLines; nLine++ )
+ {
+ EditLine& rLine = pPortion->GetLines()[nLine];
+ // nCurWidth = pLine->GetStartPosX();
+ // For Center- or Right- alignment it depends on the paper
+ // width, here not preferred. I general, it is best not leave it
+ // to StartPosX, also the right indents have to be taken into
+ // account!
+ tools::Long nCurWidth = scaleXSpacingValue(rLRItem.GetTextLeft() + nSpaceBeforeAndMinLabelWidth);
+ if ( nLine == 0 )
+ {
+ tools::Long nFI = scaleXSpacingValue(rLRItem.GetTextFirstLineOffset());
+ nCurWidth -= nFI;
+ if ( pPortion->GetBulletX() > nCurWidth )
+ {
+ nCurWidth += nFI; // LI?
+ if ( pPortion->GetBulletX() > nCurWidth )
+ nCurWidth = pPortion->GetBulletX();
+ }
+ }
+ nCurWidth += scaleXSpacingValue(rLRItem.GetRight());
+ nCurWidth += CalcLineWidth( pPortion, &rLine, bIgnoreExtraSpace );
+ if ( nCurWidth > nMaxWidth )
+ {
+ nMaxWidth = nCurWidth;
+ }
+ }
+ }
+
+ nMaxWidth++; // widen it, because in CreateLines for >= is wrapped.
+ return static_cast<sal_uInt32>(nMaxWidth);
+}
+
+sal_uInt32 ImpEditEngine::CalcLineWidth( ParaPortion* pPortion, EditLine* pLine, bool bIgnoreExtraSpace )
+{
+ sal_Int32 nPara = GetEditDoc().GetPos( pPortion->GetNode() );
+
+ // #114278# Saving both layout mode and language (since I'm
+ // potentially changing both)
+ GetRefDevice()->Push( vcl::PushFlags::TEXTLAYOUTMODE|vcl::PushFlags::TEXTLANGUAGE );
+
+ ImplInitLayoutMode(*GetRefDevice(), nPara, -1);
+
+ SvxAdjust eJustification = GetJustification( nPara );
+
+ // Calculation of the width without the Indents ...
+ sal_uInt32 nWidth = 0;
+ sal_Int32 nPos = pLine->GetStart();
+ for ( sal_Int32 nTP = pLine->GetStartPortion(); nTP <= pLine->GetEndPortion(); nTP++ )
+ {
+ const TextPortion& rTextPortion = pPortion->GetTextPortions()[nTP];
+ switch ( rTextPortion.GetKind() )
+ {
+ case PortionKind::FIELD:
+ case PortionKind::HYPHENATOR:
+ case PortionKind::TAB:
+ {
+ nWidth += rTextPortion.GetSize().Width();
+ }
+ break;
+ case PortionKind::TEXT:
+ {
+ if ( ( eJustification != SvxAdjust::Block ) || ( !bIgnoreExtraSpace ) )
+ {
+ nWidth += rTextPortion.GetSize().Width();
+ }
+ else
+ {
+ SvxFont aTmpFont( pPortion->GetNode()->GetCharAttribs().GetDefFont() );
+ SeekCursor( pPortion->GetNode(), nPos+1, aTmpFont );
+ aTmpFont.SetPhysFont(*GetRefDevice());
+ ImplInitDigitMode(*GetRefDevice(), aTmpFont.GetLanguage());
+ nWidth += aTmpFont.QuickGetTextSize( GetRefDevice(),
+ pPortion->GetNode()->GetString(), nPos, rTextPortion.GetLen(), nullptr ).Width();
+ }
+ }
+ break;
+ case PortionKind::LINEBREAK: break;
+ }
+ nPos = nPos + rTextPortion.GetLen();
+ }
+
+ GetRefDevice()->Pop();
+
+ return nWidth;
+}
+
+sal_uInt32 ImpEditEngine::GetTextHeightNTP() const
+{
+ assert( IsUpdateLayout() && "Should not be used for Update=FALSE: GetTextHeight" );
+ DBG_ASSERT( IsFormatted() || IsFormatting(), "GetTextHeight: Not formatted" );
+ return nCurTextHeightNTP;
+}
+
+tools::Long ImpEditEngine::Calc1ColumnTextHeight(tools::Long* pHeightNTP)
+{
+ tools::Long nHeight = 0;
+ if (pHeightNTP)
+ *pHeightNTP = 0;
+ // Pretend that we have ~infinite height to get total height
+ comphelper::ValueRestorationGuard aGuard(nCurTextHeight,
+ std::numeric_limits<tools::Long>::max());
+
+ IterateLinesAreasFunc FindLastLineBottom = [&](const LineAreaInfo& rInfo) {
+ if (rInfo.pLine)
+ {
+ // bottom coordinate does not belong to area, so no need to do +1
+ nHeight = getBottomDocOffset(rInfo.aArea);
+ if (pHeightNTP && !rInfo.rPortion.IsEmpty())
+ *pHeightNTP = nHeight;
+ }
+ return CallbackResult::Continue;
+ };
+ IterateLineAreas(FindLastLineBottom, IterFlag::none);
+ return nHeight;
+}
+
+tools::Long ImpEditEngine::CalcTextHeight(tools::Long* pHeightNTP)
+{
+ assert( IsUpdateLayout() && "Should not be used when Update=FALSE: CalcTextHeight" );
+
+ if (mnColumns <= 1)
+ return Calc1ColumnTextHeight(pHeightNTP); // All text fits into a single column - done!
+
+ // The final column height can be smaller than total height divided by number of columns (taking
+ // into account first line offset and interline spacing, that aren't considered in positioning
+ // after the wrap). The wrap should only happen after the minimal height is exceeded.
+ tools::Long nTentativeColHeight = mnMinColumnWrapHeight;
+ tools::Long nWantedIncrease = 0;
+ tools::Long nCurrentTextHeight;
+
+ // This does the necessary column balancing for the case when the text does not fit min height.
+ // When the height of column (taken from nCurTextHeight) is too small, the last column will
+ // overflow, so the resulting height of the text will exceed the set column height. Increasing
+ // the column height step by step by the minimal value that allows one of columns to accommodate
+ // one line more, we finally get to the point where all the text fits. At each iteration, the
+ // height is only increased, so it's impossible to have infinite layout loops. The found value
+ // is the global minimum.
+ //
+ // E.g., given the following four line heights:
+ // Line 1: 10;
+ // Line 2: 12;
+ // Line 3: 10;
+ // Line 4: 10;
+ // number of columns 3, and the minimal paper height of 5, the iterations would be:
+ // * Tentative column height is set to 5
+ // <ITERATION 1>
+ // * Line 1 is attempted to go to column 0. Overflow is 5 => moved to column 1.
+ // * Line 2 is attempted to go to column 1 after Line 1; overflow is 17 => moved to column 2.
+ // * Line 3 is attempted to go to column 2 after Line 2; overflow is 17, stays in max column 2.
+ // * Line 4 goes to column 2 after Line 3.
+ // * Final iteration columns are: {empty}, {Line 1}, {Line 2, Line 3, Line 4}
+ // * Total text height is max({0, 10, 32}) == 32 > Tentative column height 5 => NEXT ITERATION
+ // * Minimal height increase that allows at least one column to accommodate one more line is
+ // min({5, 17, 17}) = 5.
+ // * Tentative column height is set to 5 + 5 = 10.
+ // <ITERATION 2>
+ // * Line 1 goes to column 0, no overflow.
+ // * Line 2 is attempted to go to column 0 after Line 1; overflow is 12 => moved to column 1.
+ // * Line 3 is attempted to go to column 1 after Line 2; overflow is 12 => moved to column 2.
+ // * Line 4 is attempted to go to column 2 after Line 3; overflow is 10, stays in max column 2.
+ // * Final iteration columns are: {Line 1}, {Line 2}, {Line 3, Line 4}
+ // * Total text height is max({10, 12, 20}) == 20 > Tentative column height 10 => NEXT ITERATION
+ // * Minimal height increase that allows at least one column to accommodate one more line is
+ // min({12, 12, 10}) = 10.
+ // * Tentative column height is set to 10 + 10 == 20.
+ // <ITERATION 3>
+ // * Line 1 goes to column 0, no overflow.
+ // * Line 2 is attempted to go to column 0 after Line 1; overflow is 2 => moved to column 1.
+ // * Line 3 is attempted to go to column 1 after Line 2; overflow is 2 => moved to column 2.
+ // * Line 4 is attempted to go to column 2 after Line 3; no overflow.
+ // * Final iteration columns are: {Line 1}, {Line 2}, {Line 3, Line 4}
+ // * Total text height is max({10, 12, 20}) == 20 == Tentative column height 20 => END.
+ do
+ {
+ nTentativeColHeight += nWantedIncrease;
+ nWantedIncrease = std::numeric_limits<tools::Long>::max();
+ nCurrentTextHeight = 0;
+ if (pHeightNTP)
+ *pHeightNTP = 0;
+ auto GetHeightAndWantedIncrease = [&, minHeight = tools::Long(0), lastCol = sal_Int16(0)](
+ const LineAreaInfo& rInfo) mutable {
+ if (rInfo.pLine)
+ {
+ if (lastCol != rInfo.nColumn)
+ {
+ minHeight = std::max(nCurrentTextHeight,
+ minHeight); // total height can't be less than previous columns
+ nWantedIncrease = std::min(rInfo.nHeightNeededToNotWrap, nWantedIncrease);
+ lastCol = rInfo.nColumn;
+ }
+ // bottom coordinate does not belong to area, so no need to do +1
+ nCurrentTextHeight = std::max(getBottomDocOffset(rInfo.aArea), minHeight);
+ if (pHeightNTP)
+ {
+ if (rInfo.rPortion.IsEmpty())
+ *pHeightNTP = std::max(*pHeightNTP, minHeight);
+ else
+ *pHeightNTP = nCurrentTextHeight;
+ }
+ }
+ return CallbackResult::Continue;
+ };
+ comphelper::ValueRestorationGuard aGuard(nCurTextHeight, nTentativeColHeight);
+ IterateLineAreas(GetHeightAndWantedIncrease, IterFlag::none);
+ } while (nCurrentTextHeight > nTentativeColHeight && nWantedIncrease > 0
+ && nWantedIncrease != std::numeric_limits<tools::Long>::max());
+ return nCurrentTextHeight;
+}
+
+sal_Int32 ImpEditEngine::GetLineCount( sal_Int32 nParagraph ) const
+{
+ OSL_ENSURE( 0 <= nParagraph && nParagraph < GetParaPortions().Count(), "GetLineCount: Out of range" );
+ const ParaPortion* pPPortion = GetParaPortions().SafeGetObject( nParagraph );
+ OSL_ENSURE( pPPortion, "Paragraph not found: GetLineCount" );
+ if ( pPPortion )
+ return pPPortion->GetLines().Count();
+
+ return -1;
+}
+
+sal_Int32 ImpEditEngine::GetLineLen( sal_Int32 nParagraph, sal_Int32 nLine ) const
+{
+ OSL_ENSURE( 0 <= nParagraph && nParagraph < GetParaPortions().Count(), "GetLineLen: Out of range" );
+ const ParaPortion* pPPortion = GetParaPortions().SafeGetObject( nParagraph );
+ OSL_ENSURE( pPPortion, "Paragraph not found: GetLineLen" );
+ if ( pPPortion && ( nLine < pPPortion->GetLines().Count() ) )
+ {
+ const EditLine& rLine = pPPortion->GetLines()[nLine];
+ return rLine.GetLen();
+ }
+
+ return -1;
+}
+
+void ImpEditEngine::GetLineBoundaries( /*out*/sal_Int32 &rStart, /*out*/sal_Int32 &rEnd, sal_Int32 nParagraph, sal_Int32 nLine ) const
+{
+ OSL_ENSURE( 0 <= nParagraph && nParagraph < GetParaPortions().Count(), "GetLineCount: Out of range" );
+ const ParaPortion* pPPortion = GetParaPortions().SafeGetObject( nParagraph );
+ OSL_ENSURE( pPPortion, "Paragraph not found: GetLineBoundaries" );
+ rStart = rEnd = -1; // default values in case of error
+ if ( pPPortion && ( nLine < pPPortion->GetLines().Count() ) )
+ {
+ const EditLine& rLine = pPPortion->GetLines()[nLine];
+ rStart = rLine.GetStart();
+ rEnd = rLine.GetEnd();
+ }
+}
+
+sal_Int32 ImpEditEngine::GetLineNumberAtIndex( sal_Int32 nPara, sal_Int32 nIndex ) const
+{
+ sal_Int32 nLineNo = -1;
+ const ContentNode* pNode = GetEditDoc().GetObject( nPara );
+ OSL_ENSURE( pNode, "GetLineNumberAtIndex: invalid paragraph index" );
+ if (pNode)
+ {
+ // we explicitly allow for the index to point at the character right behind the text
+ const bool bValidIndex = /*0 <= nIndex &&*/ nIndex <= pNode->Len();
+ OSL_ENSURE( bValidIndex, "GetLineNumberAtIndex: invalid index" );
+ const sal_Int32 nLineCount = GetLineCount( nPara );
+ if (nIndex == pNode->Len())
+ nLineNo = nLineCount > 0 ? nLineCount - 1 : 0;
+ else if (bValidIndex) // nIndex < pNode->Len()
+ {
+ sal_Int32 nStart = -1, nEnd = -1;
+ for (sal_Int32 i = 0; i < nLineCount && nLineNo == -1; ++i)
+ {
+ GetLineBoundaries( nStart, nEnd, nPara, i );
+ if (nStart >= 0 && nStart <= nIndex && nEnd >= 0 && nIndex < nEnd)
+ nLineNo = i;
+ }
+ }
+ }
+ return nLineNo;
+}
+
+sal_uInt16 ImpEditEngine::GetLineHeight( sal_Int32 nParagraph, sal_Int32 nLine )
+{
+ OSL_ENSURE( 0 <= nParagraph && nParagraph < GetParaPortions().Count(), "GetLineCount: Out of range" );
+ ParaPortion* pPPortion = GetParaPortions().SafeGetObject( nParagraph );
+ OSL_ENSURE( pPPortion, "Paragraph not found: GetLineHeight" );
+ if ( pPPortion && ( nLine < pPPortion->GetLines().Count() ) )
+ {
+ const EditLine& rLine = pPPortion->GetLines()[nLine];
+ return rLine.GetHeight();
+ }
+
+ return 0xFFFF;
+}
+
+sal_uInt32 ImpEditEngine::GetParaHeight( sal_Int32 nParagraph )
+{
+ sal_uInt32 nHeight = 0;
+
+ ParaPortion* pPPortion = GetParaPortions().SafeGetObject( nParagraph );
+ OSL_ENSURE( pPPortion, "Paragraph not found: GetParaHeight" );
+
+ if ( pPPortion )
+ nHeight = pPPortion->GetHeight();
+
+ return nHeight;
+}
+
+void ImpEditEngine::UpdateSelections()
+{
+ // Check whether one of the selections is at a deleted node...
+ // If the node is valid, the index has yet to be examined!
+ for (EditView* pView : aEditViews)
+ {
+ EditSelection aCurSel( pView->pImpEditView->GetEditSelection() );
+ bool bChanged = false;
+ for (const std::unique_ptr<DeletedNodeInfo> & aDeletedNode : aDeletedNodes)
+ {
+ const DeletedNodeInfo& rInf = *aDeletedNode;
+ if ( ( aCurSel.Min().GetNode() == rInf.GetNode() ) ||
+ ( aCurSel.Max().GetNode() == rInf.GetNode() ) )
+ {
+ // Use ParaPortions, as now also hidden paragraphs have to be
+ // taken into account!
+ sal_Int32 nPara = rInf.GetPosition();
+ if (!GetParaPortions().SafeGetObject(nPara)) // Last paragraph
+ {
+ nPara = GetParaPortions().Count()-1;
+ }
+ assert(GetParaPortions()[nPara] && "Empty Document in UpdateSelections ?");
+ // Do not end up from a hidden paragraph:
+ sal_Int32 nCurPara = nPara;
+ sal_Int32 nLastPara = GetParaPortions().Count()-1;
+ while ( nPara <= nLastPara && !GetParaPortions()[nPara]->IsVisible() )
+ nPara++;
+ if ( nPara > nLastPara ) // then also backwards ...
+ {
+ nPara = nCurPara;
+ while ( nPara && !GetParaPortions()[nPara]->IsVisible() )
+ nPara--;
+ }
+ OSL_ENSURE( GetParaPortions()[nPara]->IsVisible(), "No visible paragraph found: UpdateSelections" );
+
+ ParaPortion* pParaPortion = GetParaPortions()[nPara];
+ EditSelection aTmpSelection( EditPaM( pParaPortion->GetNode(), 0 ) );
+ pView->pImpEditView->SetEditSelection( aTmpSelection );
+ bChanged=true;
+ break; // for loop
+ }
+ }
+ if ( !bChanged )
+ {
+ // Check Index if node shrunk.
+ if ( aCurSel.Min().GetIndex() > aCurSel.Min().GetNode()->Len() )
+ {
+ aCurSel.Min().SetIndex( aCurSel.Min().GetNode()->Len() );
+ pView->pImpEditView->SetEditSelection( aCurSel );
+ }
+ if ( aCurSel.Max().GetIndex() > aCurSel.Max().GetNode()->Len() )
+ {
+ aCurSel.Max().SetIndex( aCurSel.Max().GetNode()->Len() );
+ pView->pImpEditView->SetEditSelection( aCurSel );
+ }
+ }
+ }
+ aDeletedNodes.clear();
+}
+
+EditSelection ImpEditEngine::ConvertSelection(
+ sal_Int32 nStartPara, sal_Int32 nStartPos, sal_Int32 nEndPara, sal_Int32 nEndPos )
+{
+ EditSelection aNewSelection;
+
+ // Start...
+ ContentNode* pNode = maEditDoc.GetObject( nStartPara );
+ sal_Int32 nIndex = nStartPos;
+ if ( !pNode )
+ {
+ pNode = maEditDoc[ maEditDoc.Count()-1 ];
+ nIndex = pNode->Len();
+ }
+ else if ( nIndex > pNode->Len() )
+ nIndex = pNode->Len();
+
+ aNewSelection.Min().SetNode( pNode );
+ aNewSelection.Min().SetIndex( nIndex );
+
+ // End...
+ pNode = maEditDoc.GetObject( nEndPara );
+ nIndex = nEndPos;
+ if ( !pNode )
+ {
+ pNode = maEditDoc[ maEditDoc.Count()-1 ];
+ nIndex = pNode->Len();
+ }
+ else if ( nIndex > pNode->Len() )
+ nIndex = pNode->Len();
+
+ aNewSelection.Max().SetNode( pNode );
+ aNewSelection.Max().SetIndex( nIndex );
+
+ return aNewSelection;
+}
+
+void ImpEditEngine::SetActiveView( EditView* pView )
+{
+ // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ // Actually, now bHasVisSel and HideSelection would be necessary !!!
+
+ if ( pView == pActiveView )
+ return;
+
+ if ( pActiveView && pActiveView->HasSelection() )
+ pActiveView->pImpEditView->DrawSelectionXOR();
+
+ pActiveView = pView;
+
+ if ( pActiveView && pActiveView->HasSelection() )
+ pActiveView->pImpEditView->DrawSelectionXOR();
+
+ // NN: Quick fix for #78668#:
+ // When editing of a cell in Calc is ended, the edit engine is not deleted,
+ // only the edit views are removed. If mpIMEInfos is still set in that case,
+ // mpIMEInfos->aPos points to an invalid selection.
+ // -> reset mpIMEInfos now
+ // (probably something like this is necessary whenever the content is modified
+ // from the outside)
+
+ if ( !pView && mpIMEInfos )
+ {
+ mpIMEInfos.reset();
+ }
+}
+
+uno::Reference< datatransfer::XTransferable > ImpEditEngine::CreateTransferable( const EditSelection& rSelection )
+{
+ EditSelection aSelection( rSelection );
+ aSelection.Adjust( GetEditDoc() );
+
+ rtl::Reference<EditDataObject> pDataObj = new EditDataObject;
+
+ pDataObj->GetString() = convertLineEnd(GetSelected(aSelection), GetSystemLineEnd()); // System specific
+
+ WriteRTF( pDataObj->GetRTFStream(), aSelection );
+ pDataObj->GetRTFStream().Seek( 0 );
+
+ WriteXML( pDataObj->GetODFStream(), aSelection );
+ pDataObj->GetODFStream().Seek( 0 );
+
+ //Dumping the ODFStream to a XML file for testing purpose
+ /*
+ std::filebuf afilebuf;
+ afilebuf.open ("gsoc17_clipboard_test.xml",std::ios::out);
+ std::ostream os(&afilebuf);
+ os.write((const char*)(pDataObj->GetODFStream().GetData()), pDataObj->GetODFStream().remainingSize());
+ afilebuf.close();
+ */
+ //dumping ends
+
+ if ( ( aSelection.Min().GetNode() == aSelection.Max().GetNode() )
+ && ( aSelection.Max().GetIndex() == (aSelection.Min().GetIndex()+1) ) )
+ {
+ const EditCharAttrib* pAttr = aSelection.Min().GetNode()->GetCharAttribs().
+ FindFeature( aSelection.Min().GetIndex() );
+ if ( pAttr &&
+ ( pAttr->GetStart() == aSelection.Min().GetIndex() ) &&
+ ( pAttr->Which() == EE_FEATURE_FIELD ) )
+ {
+ const SvxFieldItem* pField = static_cast<const SvxFieldItem*>(pAttr->GetItem());
+ const SvxFieldData* pFld = pField->GetField();
+ if ( auto pUrlField = dynamic_cast<const SvxURLField* >(pFld) )
+ {
+ // Office-Bookmark
+ pDataObj->GetURL() = pUrlField->GetURL();
+ }
+ }
+ }
+
+ return pDataObj;
+}
+
+EditSelection ImpEditEngine::PasteText( uno::Reference< datatransfer::XTransferable > const & rxDataObj, const OUString& rBaseURL, const EditPaM& rPaM, bool bUseSpecial, SotClipboardFormatId format)
+{
+ EditSelection aNewSelection( rPaM );
+
+ if ( !rxDataObj.is() )
+ return aNewSelection;
+
+ datatransfer::DataFlavor aFlavor;
+ bool bDone = false;
+
+ if ( bUseSpecial )
+ {
+ // XML
+ SotExchange::GetFormatDataFlavor( SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT, aFlavor );
+ if ( rxDataObj->isDataFlavorSupported( aFlavor ) && (SotClipboardFormatId::NONE == format || SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT == format))
+ {
+ try
+ {
+ uno::Any aData = rxDataObj->getTransferData( aFlavor );
+ uno::Sequence< sal_Int8 > aSeq;
+ aData >>= aSeq;
+ {
+ SvMemoryStream aODFStream( aSeq.getArray(), aSeq.getLength(), StreamMode::READ );
+ aNewSelection = Read( aODFStream, rBaseURL, EETextFormat::Xml, rPaM );
+ }
+ bDone = true;
+ }
+ catch( const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "editeng", "Unable to paste EDITENGINE_ODF_TEXT_FLAT" );
+ }
+ }
+
+ if ( !bDone )
+ {
+ // RTF
+ SotExchange::GetFormatDataFlavor( SotClipboardFormatId::RTF, aFlavor );
+ // RICHTEXT
+ datatransfer::DataFlavor aFlavorRichtext;
+ SotExchange::GetFormatDataFlavor( SotClipboardFormatId::RICHTEXT, aFlavorRichtext );
+ bool bRtfSupported = rxDataObj->isDataFlavorSupported( aFlavor );
+ bool bRichtextSupported = rxDataObj->isDataFlavorSupported( aFlavorRichtext );
+ if ( (bRtfSupported || bRichtextSupported) && (SotClipboardFormatId::NONE == format || SotClipboardFormatId::RICHTEXT == format || SotClipboardFormatId::RTF == format))
+ {
+ if(bRichtextSupported)
+ {
+ aFlavor = aFlavorRichtext;
+ }
+ try
+ {
+ uno::Any aData = rxDataObj->getTransferData( aFlavor );
+ uno::Sequence< sal_Int8 > aSeq;
+ aData >>= aSeq;
+ {
+ SvMemoryStream aRTFStream( aSeq.getArray(), aSeq.getLength(), StreamMode::READ );
+ aNewSelection = Read( aRTFStream, rBaseURL, EETextFormat::Rtf, rPaM );
+ }
+ bDone = true;
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+ }
+ }
+ if (!bDone) {
+ // HTML
+ SotExchange::GetFormatDataFlavor(SotClipboardFormatId::HTML_SIMPLE, aFlavor);
+ bool bHtmlSupported = rxDataObj->isDataFlavorSupported(aFlavor);
+ if (bHtmlSupported && (SotClipboardFormatId::NONE == format || SotClipboardFormatId::HTML_SIMPLE == format)) {
+ MSE40HTMLClipFormatObj aMSE40HTMLClipFormatObj;
+ try
+ {
+ uno::Any aData = rxDataObj->getTransferData(aFlavor);
+ uno::Sequence< sal_Int8 > aSeq;
+ aData >>= aSeq;
+ {
+ SvMemoryStream aHtmlStream(aSeq.getArray(), aSeq.getLength(), StreamMode::READ);
+ SvStream* pHtmlStream = aMSE40HTMLClipFormatObj.IsValid(aHtmlStream);
+ if (pHtmlStream != nullptr) {
+ aNewSelection = Read(*pHtmlStream, rBaseURL, EETextFormat::Html, rPaM);
+ }
+ }
+ bDone = true;
+ }
+ catch (const css::uno::Exception&)
+ {
+ }
+ }
+ }
+ }
+ if ( !bDone )
+ {
+ SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor );
+ if ( rxDataObj->isDataFlavorSupported( aFlavor ) )
+ {
+ try
+ {
+ uno::Any aData = rxDataObj->getTransferData( aFlavor );
+ OUString aText;
+ aData >>= aText;
+ aNewSelection = ImpInsertText( rPaM, aText );
+ }
+ catch( ... )
+ {
+ ; // #i9286# can happen, even if isDataFlavorSupported returns true...
+ }
+ }
+ }
+
+ return aNewSelection;
+}
+
+sal_Int32 ImpEditEngine::GetChar(
+ const ParaPortion* pParaPortion, const EditLine* pLine, tools::Long nXPos, bool bSmart)
+{
+ assert(pLine);
+
+ sal_Int32 nChar = -1;
+ sal_Int32 nCurIndex = pLine->GetStart();
+
+
+ // Search best matching portion with GetPortionXOffset()
+ for ( sal_Int32 i = pLine->GetStartPortion(); i <= pLine->GetEndPortion(); i++ )
+ {
+ const TextPortion& rPortion = pParaPortion->GetTextPortions()[i];
+ tools::Long nXLeft = GetPortionXOffset( pParaPortion, pLine, i );
+ tools::Long nXRight = nXLeft + rPortion.GetSize().Width();
+ if ( ( nXLeft <= nXPos ) && ( nXRight >= nXPos ) )
+ {
+ nChar = nCurIndex;
+
+ // Search within Portion...
+
+ // Don't search within special portions...
+ if ( rPortion.GetKind() != PortionKind::TEXT )
+ {
+ // ...but check on which side
+ if ( bSmart )
+ {
+ tools::Long nLeftDiff = nXPos-nXLeft;
+ tools::Long nRightDiff = nXRight-nXPos;
+ if ( nRightDiff < nLeftDiff )
+ nChar++;
+ }
+ }
+ else
+ {
+ sal_Int32 nMax = rPortion.GetLen();
+ sal_Int32 nOffset = -1;
+ sal_Int32 nTmpCurIndex = nChar - pLine->GetStart();
+
+ tools::Long nXInPortion = nXPos - nXLeft;
+ if ( rPortion.IsRightToLeft() )
+ nXInPortion = nXRight - nXPos;
+
+ // Search in Array...
+ for ( sal_Int32 x = 0; x < nMax; x++ )
+ {
+ tools::Long nTmpPosMax = pLine->GetCharPosArray()[nTmpCurIndex+x];
+ if ( nTmpPosMax > nXInPortion )
+ {
+ // Check whether this or the previous...
+ tools::Long nTmpPosMin = x ? pLine->GetCharPosArray()[nTmpCurIndex+x-1] : 0;
+ tools::Long nDiffLeft = nXInPortion - nTmpPosMin;
+ tools::Long nDiffRight = nTmpPosMax - nXInPortion;
+ OSL_ENSURE( nDiffLeft >= 0, "DiffLeft negative" );
+ OSL_ENSURE( nDiffRight >= 0, "DiffRight negative" );
+
+ if (bSmart && nDiffRight < nDiffLeft)
+ {
+ // I18N: If there are character position with the length of 0,
+ // they belong to the same character, we can not use this position as an index.
+ // Skip all 0-positions, cheaper than using XBreakIterator:
+ tools::Long nX = pLine->GetCharPosArray()[nTmpCurIndex + x];
+ while(x < nMax && pLine->GetCharPosArray()[nTmpCurIndex + x] == nX)
+ ++x;
+ }
+ nOffset = x;
+ break;
+ }
+ }
+
+ // There should not be any inaccuracies when using the
+ // CharPosArray! Maybe for kerning?
+ // 0xFFF happens for example for Outline-Font when at the very end.
+ if ( nOffset < 0 )
+ nOffset = nMax;
+
+ OSL_ENSURE( nOffset <= nMax, "nOffset > nMax" );
+
+ nChar = nChar + nOffset;
+
+ // Check if index is within a cell:
+ if ( nChar && ( nChar < pParaPortion->GetNode()->Len() ) )
+ {
+ EditPaM aPaM( pParaPortion->GetNode(), nChar+1 );
+ sal_uInt16 nScriptType = GetI18NScriptType( aPaM );
+ if ( nScriptType == i18n::ScriptType::COMPLEX )
+ {
+ uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
+ sal_Int32 nCount = 1;
+ lang::Locale aLocale = GetLocale( aPaM );
+ sal_Int32 nRight = _xBI->nextCharacters(
+ pParaPortion->GetNode()->GetString(), nChar, aLocale, css::i18n::CharacterIteratorMode::SKIPCELL, nCount, nCount );
+ sal_Int32 nLeft = _xBI->previousCharacters(
+ pParaPortion->GetNode()->GetString(), nRight, aLocale, css::i18n::CharacterIteratorMode::SKIPCELL, nCount, nCount );
+ if ( ( nLeft != nChar ) && ( nRight != nChar ) )
+ {
+ nChar = ( std::abs( nRight - nChar ) < std::abs( nLeft - nChar ) ) ? nRight : nLeft;
+ }
+ }
+ else
+ {
+ OUString aStr(pParaPortion->GetNode()->GetString());
+ // tdf#102625: don't select middle of a pair of surrogates with mouse cursor
+ if (rtl::isSurrogate(aStr[nChar]))
+ --nChar;
+ }
+ }
+ }
+ }
+
+ nCurIndex = nCurIndex + rPortion.GetLen();
+ }
+
+ if ( nChar == -1 )
+ {
+ nChar = ( nXPos <= pLine->GetStartPosX() ) ? pLine->GetStart() : pLine->GetEnd();
+ }
+
+ return nChar;
+}
+
+Range ImpEditEngine::GetLineXPosStartEnd( const ParaPortion* pParaPortion, const EditLine* pLine ) const
+{
+ Range aLineXPosStartEnd;
+
+ sal_Int32 nPara = GetEditDoc().GetPos( pParaPortion->GetNode() );
+ if ( !IsRightToLeft( nPara ) )
+ {
+ aLineXPosStartEnd.Min() = pLine->GetStartPosX();
+ aLineXPosStartEnd.Max() = pLine->GetStartPosX() + pLine->GetTextWidth();
+ }
+ else
+ {
+ aLineXPosStartEnd.Min() = GetPaperSize().Width() - ( pLine->GetStartPosX() + pLine->GetTextWidth() );
+ aLineXPosStartEnd.Max() = GetPaperSize().Width() - pLine->GetStartPosX();
+ }
+
+
+ return aLineXPosStartEnd;
+}
+
+tools::Long ImpEditEngine::GetPortionXOffset(
+ const ParaPortion* pParaPortion, const EditLine* pLine, sal_Int32 nTextPortion) const
+{
+ tools::Long nX = pLine->GetStartPosX();
+
+ for ( sal_Int32 i = pLine->GetStartPortion(); i < nTextPortion; i++ )
+ {
+ const TextPortion& rPortion = pParaPortion->GetTextPortions()[i];
+ switch ( rPortion.GetKind() )
+ {
+ case PortionKind::FIELD:
+ case PortionKind::TEXT:
+ case PortionKind::HYPHENATOR:
+ case PortionKind::TAB:
+ {
+ nX += rPortion.GetSize().Width();
+ }
+ break;
+ case PortionKind::LINEBREAK: break;
+ }
+ }
+
+ sal_Int32 nPara = GetEditDoc().GetPos( pParaPortion->GetNode() );
+ bool bR2LPara = IsRightToLeft( nPara );
+
+ const TextPortion& rDestPortion = pParaPortion->GetTextPortions()[nTextPortion];
+ if ( rDestPortion.GetKind() != PortionKind::TAB )
+ {
+ if ( !bR2LPara && rDestPortion.GetRightToLeftLevel() )
+ {
+ // Portions behind must be added, visual before this portion
+ sal_Int32 nTmpPortion = nTextPortion+1;
+ while ( nTmpPortion <= pLine->GetEndPortion() )
+ {
+ const TextPortion& rNextTextPortion = pParaPortion->GetTextPortions()[nTmpPortion];
+ if ( rNextTextPortion.GetRightToLeftLevel() && ( rNextTextPortion.GetKind() != PortionKind::TAB ) )
+ nX += rNextTextPortion.GetSize().Width();
+ else
+ break;
+ nTmpPortion++;
+ }
+ // Portions before must be removed, visual behind this portion
+ nTmpPortion = nTextPortion;
+ while ( nTmpPortion > pLine->GetStartPortion() )
+ {
+ --nTmpPortion;
+ const TextPortion& rPrevTextPortion = pParaPortion->GetTextPortions()[nTmpPortion];
+ if ( rPrevTextPortion.GetRightToLeftLevel() && ( rPrevTextPortion.GetKind() != PortionKind::TAB ) )
+ nX -= rPrevTextPortion.GetSize().Width();
+ else
+ break;
+ }
+ }
+ else if ( bR2LPara && !rDestPortion.IsRightToLeft() )
+ {
+ // Portions behind must be removed, visual behind this portion
+ sal_Int32 nTmpPortion = nTextPortion+1;
+ while ( nTmpPortion <= pLine->GetEndPortion() )
+ {
+ const TextPortion& rNextTextPortion = pParaPortion->GetTextPortions()[nTmpPortion];
+ if ( !rNextTextPortion.IsRightToLeft() && ( rNextTextPortion.GetKind() != PortionKind::TAB ) )
+ nX += rNextTextPortion.GetSize().Width();
+ else
+ break;
+ nTmpPortion++;
+ }
+ // Portions before must be added, visual before this portion
+ nTmpPortion = nTextPortion;
+ while ( nTmpPortion > pLine->GetStartPortion() )
+ {
+ --nTmpPortion;
+ const TextPortion& rPrevTextPortion = pParaPortion->GetTextPortions()[nTmpPortion];
+ if ( !rPrevTextPortion.IsRightToLeft() && ( rPrevTextPortion.GetKind() != PortionKind::TAB ) )
+ nX -= rPrevTextPortion.GetSize().Width();
+ else
+ break;
+ }
+ }
+ }
+ if ( bR2LPara )
+ {
+ // Switch X positions...
+ OSL_ENSURE( GetTextRanger() || GetPaperSize().Width(), "GetPortionXOffset - paper size?!" );
+ OSL_ENSURE( GetTextRanger() || (nX <= GetPaperSize().Width()), "GetPortionXOffset - position out of paper size!" );
+ nX = GetPaperSize().Width() - nX;
+ nX -= rDestPortion.GetSize().Width();
+ }
+
+ return nX;
+}
+
+tools::Long ImpEditEngine::GetXPos(
+ const ParaPortion* pParaPortion, const EditLine* pLine, sal_Int32 nIndex, bool bPreferPortionStart) const
+{
+ assert(pLine);
+ OSL_ENSURE( ( nIndex >= pLine->GetStart() ) && ( nIndex <= pLine->GetEnd() ) , "GetXPos has to be called properly!" );
+
+ bool bDoPreferPortionStart = bPreferPortionStart;
+ // Assure that the portion belongs to this line:
+ if ( nIndex == pLine->GetStart() )
+ bDoPreferPortionStart = true;
+ else if ( nIndex == pLine->GetEnd() )
+ bDoPreferPortionStart = false;
+
+ sal_Int32 nTextPortionStart = 0;
+ sal_Int32 nTextPortion = pParaPortion->GetTextPortions().FindPortion( nIndex, nTextPortionStart, bDoPreferPortionStart );
+
+ OSL_ENSURE( ( nTextPortion >= pLine->GetStartPortion() ) && ( nTextPortion <= pLine->GetEndPortion() ), "GetXPos: Portion not in current line! " );
+
+ const TextPortion& rPortion = pParaPortion->GetTextPortions()[nTextPortion];
+
+ tools::Long nX = GetPortionXOffset( pParaPortion, pLine, nTextPortion );
+
+ // calc text width, portion size may include CJK/CTL spacing...
+ // But the array might not be init yet, if using text ranger this method is called within CreateLines()...
+ tools::Long nPortionTextWidth = rPortion.GetSize().Width();
+ if ( ( rPortion.GetKind() == PortionKind::TEXT ) && rPortion.GetLen() && !GetTextRanger() )
+ nPortionTextWidth = pLine->GetCharPosArray()[nTextPortionStart + rPortion.GetLen() - 1 - pLine->GetStart()];
+
+ if ( nTextPortionStart != nIndex )
+ {
+ // Search within portion...
+ if ( nIndex == ( nTextPortionStart + rPortion.GetLen() ) )
+ {
+ // End of Portion
+ if ( rPortion.GetKind() == PortionKind::TAB )
+ {
+ if ( nTextPortion+1 < pParaPortion->GetTextPortions().Count() )
+ {
+ const TextPortion& rNextPortion = pParaPortion->GetTextPortions()[nTextPortion+1];
+ if ( rNextPortion.GetKind() != PortionKind::TAB )
+ {
+ if ( !bPreferPortionStart )
+ nX = GetXPos( pParaPortion, pLine, nIndex, true );
+ else if ( !IsRightToLeft( GetEditDoc().GetPos( pParaPortion->GetNode() ) ) )
+ nX += nPortionTextWidth;
+ }
+ }
+ else if ( !IsRightToLeft( GetEditDoc().GetPos( pParaPortion->GetNode() ) ) )
+ {
+ nX += nPortionTextWidth;
+ }
+ }
+ else if ( !rPortion.IsRightToLeft() )
+ {
+ nX += nPortionTextWidth;
+ }
+ }
+ else if ( rPortion.GetKind() == PortionKind::TEXT )
+ {
+ OSL_ENSURE( nIndex != pLine->GetStart(), "Strange behavior in new GetXPos()" );
+ OSL_ENSURE( !pLine->GetCharPosArray().empty(), "svx::ImpEditEngine::GetXPos(), portion in an empty line?" );
+
+ if( !pLine->GetCharPosArray().empty() )
+ {
+ sal_Int32 nPos = nIndex - 1 - pLine->GetStart();
+ if (nPos < 0 || o3tl::make_unsigned(nPos) >= pLine->GetCharPosArray().size())
+ {
+ nPos = pLine->GetCharPosArray().size()-1;
+ OSL_FAIL("svx::ImpEditEngine::GetXPos(), index out of range!");
+ }
+
+ // old code restored see #i112788 (which leaves #i74188 unfixed again)
+ tools::Long nPosInPortion = pLine->GetCharPosArray()[nPos];
+
+ if ( !rPortion.IsRightToLeft() )
+ {
+ nX += nPosInPortion;
+ }
+ else
+ {
+ nX += nPortionTextWidth - nPosInPortion;
+ }
+
+ if ( rPortion.GetExtraInfos() && rPortion.GetExtraInfos()->bCompressed )
+ {
+ nX += rPortion.GetExtraInfos()->nPortionOffsetX;
+ if ( rPortion.GetExtraInfos()->nAsianCompressionTypes & AsianCompressionFlags::PunctuationRight )
+ {
+ AsianCompressionFlags nType = GetCharTypeForCompression( pParaPortion->GetNode()->GetChar( nIndex ) );
+ if ( nType == AsianCompressionFlags::PunctuationRight && !pLine->GetCharPosArray().empty() )
+ {
+ sal_Int32 n = nIndex - nTextPortionStart;
+ const sal_Int32* pDXArray = pLine->GetCharPosArray().data()+( nTextPortionStart-pLine->GetStart() );
+ sal_Int32 nCharWidth = ( ( (n+1) < rPortion.GetLen() ) ? pDXArray[n] : rPortion.GetSize().Width() )
+ - ( n ? pDXArray[n-1] : 0 );
+ if ( (n+1) < rPortion.GetLen() )
+ {
+ // smaller, when char behind is AsianCompressionFlags::PunctuationRight also
+ nType = GetCharTypeForCompression( pParaPortion->GetNode()->GetChar( nIndex+1 ) );
+ if ( nType == AsianCompressionFlags::PunctuationRight )
+ {
+ sal_Int32 nNextCharWidth = ( ( (n+2) < rPortion.GetLen() ) ? pDXArray[n+1] : rPortion.GetSize().Width() )
+ - pDXArray[n];
+ sal_Int32 nCompressed = nNextCharWidth/2;
+ nCompressed *= rPortion.GetExtraInfos()->nMaxCompression100thPercent;
+ nCompressed /= 10000;
+ nCharWidth += nCompressed;
+ }
+ }
+ else
+ {
+ nCharWidth *= 2; // last char pos to portion end is only compressed size
+ }
+ nX += nCharWidth/2; // 50% compression
+ }
+ }
+ }
+ }
+ }
+ }
+ else // if ( nIndex == pLine->GetStart() )
+ {
+ if ( rPortion.IsRightToLeft() )
+ {
+ nX += nPortionTextWidth;
+ }
+ }
+
+ return nX;
+}
+
+void ImpEditEngine::CalcHeight( ParaPortion* pPortion )
+{
+ pPortion->nHeight = 0;
+ pPortion->nFirstLineOffset = 0;
+
+ if ( !pPortion->IsVisible() )
+ return;
+
+ OSL_ENSURE( pPortion->GetLines().Count(), "Paragraph with no lines in ParaPortion::CalcHeight" );
+ for (sal_Int32 nLine = 0; nLine < pPortion->GetLines().Count(); ++nLine)
+ pPortion->nHeight += pPortion->GetLines()[nLine].GetHeight();
+
+ if (maStatus.IsOutliner())
+ return;
+
+ const SvxULSpaceItem& rULItem = pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
+ const SvxLineSpacingItem& rLSItem = pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
+ sal_Int32 nSBL = ( rLSItem.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Fix ) ? scaleYSpacingValue(rLSItem.GetInterLineSpace()) : 0;
+
+ if ( nSBL )
+ {
+ if ( pPortion->GetLines().Count() > 1 )
+ pPortion->nHeight += ( pPortion->GetLines().Count() - 1 ) * nSBL;
+ if (maStatus.ULSpaceSummation())
+ pPortion->nHeight += nSBL;
+ }
+
+ sal_Int32 nPortion = GetParaPortions().GetPos( pPortion );
+ if ( nPortion )
+ {
+ sal_uInt16 nUpper = scaleYSpacingValue(rULItem.GetUpper());
+ pPortion->nHeight += nUpper;
+ pPortion->nFirstLineOffset = nUpper;
+ }
+
+ if ( nPortion != (GetParaPortions().Count()-1) )
+ {
+ pPortion->nHeight += scaleYSpacingValue(rULItem.GetLower()); // not in the last
+ }
+
+
+ if ( !nPortion || maStatus.ULSpaceSummation() )
+ return;
+
+ ParaPortion* pPrev = GetParaPortions().SafeGetObject( nPortion-1 );
+ if (!pPrev)
+ return;
+
+ const SvxULSpaceItem& rPrevULItem = pPrev->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
+ const SvxLineSpacingItem& rPrevLSItem = pPrev->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
+
+ // In relation between WinWord6/Writer3:
+ // With a proportional line spacing the paragraph spacing is
+ // also manipulated.
+ // Only Writer3: Do not add up, but minimum distance.
+
+ // check if distance by LineSpacing > Upper:
+ sal_uInt16 nExtraSpace = scaleYSpacingValue(lcl_CalcExtraSpace(rLSItem));
+ if ( nExtraSpace > pPortion->nFirstLineOffset )
+ {
+ // Paragraph becomes 'bigger':
+ pPortion->nHeight += ( nExtraSpace - pPortion->nFirstLineOffset );
+ pPortion->nFirstLineOffset = nExtraSpace;
+ }
+
+ // Determine nFirstLineOffset now f(pNode) => now f(pNode, pPrev):
+ sal_uInt16 nPrevLower = scaleYSpacingValue(rPrevULItem.GetLower());
+
+ // This PrevLower is still in the height of PrevPortion ...
+ if ( nPrevLower > pPortion->nFirstLineOffset )
+ {
+ // Paragraph is 'small':
+ pPortion->nHeight -= pPortion->nFirstLineOffset;
+ pPortion->nFirstLineOffset = 0;
+ }
+ else if ( nPrevLower )
+ {
+ // Paragraph becomes 'somewhat smaller':
+ pPortion->nHeight -= nPrevLower;
+ pPortion->nFirstLineOffset =
+ pPortion->nFirstLineOffset - nPrevLower;
+ }
+ // I find it not so good, but Writer3 feature:
+ // Check if distance by LineSpacing > Lower: this value is not
+ // stuck in the height of PrevPortion.
+ if ( pPrev->IsInvalid() )
+ return;
+
+ nExtraSpace = scaleYSpacingValue(lcl_CalcExtraSpace(rPrevLSItem));
+ if ( nExtraSpace > nPrevLower )
+ {
+ sal_uInt16 nMoreLower = nExtraSpace - nPrevLower;
+ // Paragraph becomes 'bigger', 'grows' downwards:
+ if ( nMoreLower > pPortion->nFirstLineOffset )
+ {
+ pPortion->nHeight += ( nMoreLower - pPortion->nFirstLineOffset );
+ pPortion->nFirstLineOffset = nMoreLower;
+ }
+ }
+}
+
+void ImpEditEngine::SetValidPaperSize( const Size& rNewSz )
+{
+ maPaperSize = rNewSz;
+
+ tools::Long nMinWidth = maStatus.AutoPageWidth() ? maMinAutoPaperSize.Width() : 0;
+ tools::Long nMaxWidth = maStatus.AutoPageWidth() ? maMaxAutoPaperSize.Width() : 0x7FFFFFFF;
+ tools::Long nMinHeight = maStatus.AutoPageHeight() ? maMinAutoPaperSize.Height() : 0;
+ tools::Long nMaxHeight = maStatus.AutoPageHeight() ? maMaxAutoPaperSize.Height() : 0x7FFFFFFF;
+
+ // Minimum/Maximum width:
+ if ( maPaperSize.Width() < nMinWidth )
+ maPaperSize.setWidth( nMinWidth );
+ else if ( maPaperSize.Width() > nMaxWidth )
+ maPaperSize.setWidth( nMaxWidth );
+
+ // Minimum/Maximum height:
+ if ( maPaperSize.Height() < nMinHeight )
+ maPaperSize.setHeight( nMinHeight );
+ else if ( maPaperSize.Height() > nMaxHeight )
+ maPaperSize.setHeight( nMaxHeight );
+}
+
+std::shared_ptr<SvxForbiddenCharactersTable> const & ImpEditEngine::GetForbiddenCharsTable()
+{
+ return EditDLL::Get().GetGlobalData()->GetForbiddenCharsTable();
+}
+
+void ImpEditEngine::SetForbiddenCharsTable(const std::shared_ptr<SvxForbiddenCharactersTable>& xForbiddenChars)
+{
+ EditDLL::Get().GetGlobalData()->SetForbiddenCharsTable( xForbiddenChars );
+}
+
+bool ImpEditEngine::IsVisualCursorTravelingEnabled()
+{
+ bool bVisualCursorTravaling = false;
+
+ if ( SvtCTLOptions::IsCTLFontEnabled() && ( SvtCTLOptions::GetCTLCursorMovement() == SvtCTLOptions::MOVEMENT_VISUAL ) )
+ {
+ bVisualCursorTravaling = true;
+ }
+
+ return bVisualCursorTravaling;
+
+}
+
+bool ImpEditEngine::DoVisualCursorTraveling()
+{
+ // Don't check if it's necessary, because we also need it when leaving the paragraph
+ return IsVisualCursorTravelingEnabled();
+}
+
+IMPL_LINK_NOARG(ImpEditEngine, DocModified, LinkParamNone*, void)
+{
+ aModifyHdl.Call( nullptr /*GetEditEnginePtr()*/ ); // NULL, because also used for Outliner
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/impedit3.cxx b/editeng/source/editeng/impedit3.cxx
new file mode 100644
index 0000000000..b24cc00401
--- /dev/null
+++ b/editeng/source/editeng/impedit3.cxx
@@ -0,0 +1,4910 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <vcl/svapp.hxx>
+#include <vcl/metaact.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/window.hxx>
+
+#include <editeng/outliner.hxx>
+#include <editeng/tstpitem.hxx>
+#include <editeng/lspcitem.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/forbiddenruleitem.hxx>
+#include "impedit.hxx"
+#include <editeng/editeng.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/txtrange.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/scriptspaceitem.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <editeng/numitem.hxx>
+#include <outleeng.hxx>
+
+#include <svtools/colorcfg.hxx>
+#include <svl/ctloptions.hxx>
+#include <svl/asiancfg.hxx>
+
+#include <svx/compatflags.hxx>
+#include <sfx2/viewsh.hxx>
+
+#include <editeng/hngpnctitem.hxx>
+#include <editeng/forbiddencharacterstable.hxx>
+
+#include <unotools/configmgr.hxx>
+
+#include <math.h>
+#include <vcl/metric.hxx>
+#include <com/sun/star/i18n/BreakIterator.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/i18n/InputSequenceChecker.hpp>
+#include <vcl/pdfextoutdevdata.hxx>
+#include <i18nlangtag/mslangid.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/lok.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/sorted_vector.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/string.hxx>
+#include <cstddef>
+#include <memory>
+#include <set>
+
+#include <vcl/outdev/ScopedStates.hxx>
+
+#include <unicode/uchar.h>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::linguistic2;
+
+constexpr OUString CH_HYPH = u"-"_ustr;
+
+constexpr tools::Long WRONG_SHOW_MIN = 5;
+
+namespace {
+
+struct TabInfo
+{
+ bool bValid;
+
+ SvxTabStop aTabStop;
+ sal_Int32 nTabPortion;
+ tools::Long nStartPosX;
+ tools::Long nTabPos;
+
+ TabInfo()
+ : bValid(false)
+ , nTabPortion(0)
+ , nStartPosX(0)
+ , nTabPos(0)
+ { }
+
+};
+
+}
+
+AsianCompressionFlags GetCharTypeForCompression( sal_Unicode cChar )
+{
+ switch ( cChar )
+ {
+ case 0x3008: case 0x300A: case 0x300C: case 0x300E:
+ case 0x3010: case 0x3014: case 0x3016: case 0x3018:
+ case 0x301A: case 0x301D: case 0xFF09: case 0xFF3D:
+ case 0xFF5D:
+ {
+ return AsianCompressionFlags::PunctuationRight;
+ }
+ case 0x3001: case 0x3002: case 0x3009: case 0x300B:
+ case 0x300D: case 0x300F: case 0x3011: case 0x3015:
+ case 0x3017: case 0x3019: case 0x301B: case 0x301E:
+ case 0x301F: case 0xFF08: case 0xFF0C: case 0xFF0E:
+ case 0xFF1A: case 0xFF1B: case 0xFF3B: case 0xFF5B:
+ {
+ return AsianCompressionFlags::PunctuationLeft;
+ }
+ default:
+ {
+ return ( ( 0x3040 <= cChar ) && ( 0x3100 > cChar ) ) ? AsianCompressionFlags::Kana : AsianCompressionFlags::Normal;
+ }
+ }
+}
+
+static void lcl_DrawRedLines( OutputDevice& rOutDev,
+ tools::Long nFontHeight,
+ const Point& rPoint,
+ size_t nIndex,
+ size_t nMaxEnd,
+ std::span<const sal_Int32> pDXArray,
+ WrongList const * pWrongs,
+ Degree10 nOrientation,
+ const Point& rOrigin,
+ bool bVertical,
+ bool bIsRightToLeft )
+{
+ // But only if font is not too small...
+ tools::Long nHeight = rOutDev.LogicToPixel(Size(0, nFontHeight)).Height();
+ if (WRONG_SHOW_MIN >= nHeight)
+ return;
+
+ size_t nEnd, nStart = nIndex;
+ bool bWrong = pWrongs->NextWrong(nStart, nEnd);
+
+ while (bWrong)
+ {
+ if (nStart >= nMaxEnd)
+ break;
+
+ if (nStart < nIndex) // Corrected
+ nStart = nIndex;
+
+ if (nEnd > nMaxEnd)
+ nEnd = nMaxEnd;
+
+ Point aPoint1(rPoint);
+ if (bVertical)
+ {
+ // VCL doesn't know that the text is vertical, and is manipulating
+ // the positions a little bit in y direction...
+ tools::Long nOnePixel = rOutDev.PixelToLogic(Size(0, 1)).Height();
+ tools::Long nCorrect = 2 * nOnePixel;
+ aPoint1.AdjustY(-nCorrect);
+ aPoint1.AdjustX(-nCorrect);
+ }
+ if (nStart > nIndex)
+ {
+ if (!bVertical)
+ {
+ // since for RTL portions rPoint is on the visual right end of the portion
+ // (i.e. at the start of the first RTL char) we need to subtract the offset
+ // for RTL portions...
+ aPoint1.AdjustX((bIsRightToLeft ? -1 : 1) * pDXArray[nStart - nIndex - 1]);
+ }
+ else
+ aPoint1.AdjustY(pDXArray[nStart - nIndex - 1]);
+ }
+ Point aPoint2(rPoint);
+ assert(nEnd > nIndex && "RedLine: aPnt2?");
+ if (!bVertical)
+ {
+ // since for RTL portions rPoint is on the visual right end of the portion
+ // (i.e. at the start of the first RTL char) we need to subtract the offset
+ // for RTL portions...
+ aPoint2.AdjustX((bIsRightToLeft ? -1 : 1) * pDXArray[nEnd - nIndex - 1]);
+ }
+ else
+ {
+ aPoint2.AdjustY(pDXArray[nEnd - nIndex - 1]);
+ }
+
+ if (nOrientation)
+ {
+ rOrigin.RotateAround(aPoint1, nOrientation);
+ rOrigin.RotateAround(aPoint2, nOrientation);
+ }
+
+ {
+ vcl::ScopedAntialiasing a(rOutDev, true);
+ rOutDev.DrawWaveLine(aPoint1, aPoint2);
+ }
+
+ nStart = nEnd + 1;
+ if (nEnd < nMaxEnd)
+ bWrong = pWrongs->NextWrong(nStart, nEnd);
+ else
+ bWrong = false;
+ }
+}
+
+// For Kashidas from sw/source/core/text/porlay.cxx
+
+#define IS_JOINING_GROUP(c, g) ( u_getIntPropertyValue( (c), UCHAR_JOINING_GROUP ) == U_JG_##g )
+#define isAinChar(c) IS_JOINING_GROUP((c), AIN)
+#define isAlefChar(c) IS_JOINING_GROUP((c), ALEF)
+#define isDalChar(c) IS_JOINING_GROUP((c), DAL)
+#define isFehChar(c) (IS_JOINING_GROUP((c), FEH) || IS_JOINING_GROUP((c), AFRICAN_FEH))
+#define isGafChar(c) IS_JOINING_GROUP((c), GAF)
+#define isHehChar(c) IS_JOINING_GROUP((c), HEH)
+#define isKafChar(c) IS_JOINING_GROUP((c), KAF)
+#define isLamChar(c) IS_JOINING_GROUP((c), LAM)
+#define isQafChar(c) (IS_JOINING_GROUP((c), QAF) || IS_JOINING_GROUP((c), AFRICAN_QAF))
+#define isRehChar(c) IS_JOINING_GROUP((c), REH)
+#define isTahChar(c) IS_JOINING_GROUP((c), TAH)
+#define isTehMarbutaChar(c) IS_JOINING_GROUP((c), TEH_MARBUTA)
+#define isWawChar(c) IS_JOINING_GROUP((c), WAW)
+#define isSeenOrSadChar(c) (IS_JOINING_GROUP((c), SAD) || IS_JOINING_GROUP((c), SEEN))
+
+// Beh and characters that behave like Beh in medial form.
+static bool isBehChar(sal_Unicode cCh)
+{
+ bool bRet = false;
+ switch (u_getIntPropertyValue(cCh, UCHAR_JOINING_GROUP))
+ {
+ case U_JG_BEH:
+ case U_JG_NOON:
+ case U_JG_AFRICAN_NOON:
+ case U_JG_NYA:
+ case U_JG_YEH:
+ case U_JG_FARSI_YEH:
+ case U_JG_BURUSHASKI_YEH_BARREE:
+ bRet = true;
+ break;
+ default:
+ bRet = false;
+ break;
+ }
+
+ return bRet;
+}
+
+// Yeh and characters that behave like Yeh in final form.
+static bool isYehChar(sal_Unicode cCh)
+{
+ bool bRet = false;
+ switch (u_getIntPropertyValue(cCh, UCHAR_JOINING_GROUP))
+ {
+ case U_JG_YEH:
+ case U_JG_FARSI_YEH:
+ case U_JG_YEH_BARREE:
+ case U_JG_BURUSHASKI_YEH_BARREE:
+ case U_JG_YEH_WITH_TAIL:
+ bRet = true;
+ break;
+ default:
+ bRet = false;
+ break;
+ }
+
+ return bRet;
+}
+
+static bool isTransparentChar ( sal_Unicode cCh )
+{
+ return u_getIntPropertyValue( cCh, UCHAR_JOINING_TYPE ) == U_JT_TRANSPARENT;
+}
+
+static bool lcl_IsLigature( sal_Unicode cCh, sal_Unicode cNextCh )
+{
+ // Lam + Alef
+ return ( isLamChar ( cCh ) && isAlefChar ( cNextCh ));
+}
+
+static bool lcl_ConnectToPrev( sal_Unicode cCh, sal_Unicode cPrevCh )
+{
+ const int32_t nJoiningType = u_getIntPropertyValue( cPrevCh, UCHAR_JOINING_TYPE );
+ bool bRet = nJoiningType != U_JT_RIGHT_JOINING && nJoiningType != U_JT_NON_JOINING;
+
+ // check for ligatures cPrevChar + cChar
+ if ( bRet )
+ bRet = ! lcl_IsLigature( cPrevCh, cCh );
+
+ return bRet;
+}
+
+
+
+void ImpEditEngine::UpdateViews( EditView* pCurView )
+{
+ if ( !IsUpdateLayout() || IsFormatting() || aInvalidRect.IsEmpty() )
+ return;
+
+ DBG_ASSERT( IsFormatted(), "UpdateViews: Doc not formatted!" );
+
+ for (EditView* pView : aEditViews)
+ {
+ pView->HideCursor();
+
+ tools::Rectangle aClipRect( aInvalidRect );
+ tools::Rectangle aVisArea( pView->GetVisArea() );
+ aClipRect.Intersection( aVisArea );
+
+ if ( !aClipRect.IsEmpty() )
+ {
+ // convert to window coordinates...
+ aClipRect = pView->pImpEditView->GetWindowPos( aClipRect );
+
+ // moved to one executing method to allow finer control
+ pView->InvalidateWindow(aClipRect);
+
+ pView->InvalidateOtherViewWindows( aClipRect );
+ }
+ }
+
+ if ( pCurView )
+ {
+ bool bGotoCursor = pCurView->pImpEditView->DoAutoScroll();
+ pCurView->ShowCursor( bGotoCursor );
+ }
+
+ aInvalidRect = tools::Rectangle();
+ CallStatusHdl();
+}
+
+IMPL_LINK_NOARG(ImpEditEngine, OnlineSpellHdl, Timer *, void)
+{
+ if ( !Application::AnyInput( VclInputFlags::KEYBOARD ) && IsUpdateLayout() && IsFormatted() )
+ DoOnlineSpelling();
+ else
+ aOnlineSpellTimer.Start();
+}
+
+IMPL_LINK_NOARG(ImpEditEngine, IdleFormatHdl, Timer *, void)
+{
+ aIdleFormatter.ResetRestarts();
+
+ // #i97146# check if that view is still available
+ // else probably the idle format timer fired while we're already
+ // downing
+ EditView* pView = aIdleFormatter.GetView();
+ for (EditView* aEditView : aEditViews)
+ {
+ if( aEditView == pView )
+ {
+ FormatAndLayout( pView );
+ break;
+ }
+ }
+}
+
+void ImpEditEngine::CheckIdleFormatter()
+{
+ aIdleFormatter.ForceTimeout();
+ // If not idle, but still not formatted:
+ if ( !IsFormatted() )
+ FormatDoc();
+}
+
+bool ImpEditEngine::IsPageOverflow( ) const
+{
+ return mbNeedsChainingHandling;
+}
+
+
+void ImpEditEngine::FormatFullDoc()
+{
+ for ( sal_Int32 nPortion = 0; nPortion < GetParaPortions().Count(); nPortion++ )
+ GetParaPortions()[nPortion]->MarkSelectionInvalid( 0 );
+ FormatDoc();
+}
+
+void ImpEditEngine::FormatDoc()
+{
+ if (!IsUpdateLayout() || IsFormatting())
+ return;
+
+ mbIsFormatting = true;
+
+ // Then I can also start the spell-timer...
+ if ( GetStatus().DoOnlineSpelling() )
+ StartOnlineSpellTimer();
+
+ tools::Long nY = 0;
+ bool bGrow = false;
+
+ // Here already, so that not always in CreateLines...
+ bool bMapChanged = ImpCheckRefMapMode();
+ sal_Int32 nParaCount = GetParaPortions().Count();
+ o3tl::sorted_vector<sal_Int32> aRepaintParas;
+ aRepaintParas.reserve(nParaCount);
+
+ for ( sal_Int32 nPara = 0; nPara < nParaCount; nPara++ )
+ {
+ ParaPortion* pParaPortion = GetParaPortions()[nPara];
+ if ( pParaPortion->MustRepaint() || ( pParaPortion->IsInvalid() && pParaPortion->IsVisible() ) )
+ {
+ // No formatting should be necessary for MustRepaint()!
+ if ( !pParaPortion->IsInvalid() || CreateLines( nPara, nY ) )
+ {
+ if ( !bGrow && GetTextRanger() )
+ {
+ // For a change in height all below must be reformatted...
+ for ( sal_Int32 n = nPara+1; n < GetParaPortions().Count(); n++ )
+ {
+ ParaPortion* pPP = GetParaPortions()[n];
+ pPP->MarkSelectionInvalid( 0 );
+ pPP->GetLines().Reset();
+ }
+ }
+ bGrow = true;
+ if ( IsCallParaInsertedOrDeleted() )
+ {
+ GetEditEnginePtr()->ParagraphHeightChanged( nPara );
+
+ for (EditView* pView : aEditViews)
+ {
+ ImpEditView* pImpView = pView->pImpEditView.get();
+ pImpView->ScrollStateChange();
+ }
+
+ }
+ pParaPortion->SetMustRepaint( false );
+ }
+
+ aRepaintParas.insert(nPara);
+ }
+ nY += pParaPortion->GetHeight();
+ }
+
+ aInvalidRect = tools::Rectangle(); // make empty
+
+ // One can also get into the formatting through UpdateMode ON=>OFF=>ON...
+ // enable optimization first after Vobis delivery...
+ {
+ tools::Long nNewHeightNTP;
+ tools::Long nNewHeight = CalcTextHeight(&nNewHeightNTP);
+ tools::Long nDiff = nNewHeight - nCurTextHeight;
+ if ( nDiff )
+ {
+ aInvalidRect.Union(tools::Rectangle::Normalize(
+ { 0, nNewHeight }, { getWidthDirectionAware(maPaperSize), nCurTextHeight }));
+ maStatus.GetStatusWord() |= !IsEffectivelyVertical() ? EditStatusFlags::TextHeightChanged : EditStatusFlags::TEXTWIDTHCHANGED;
+ }
+
+ nCurTextHeight = nNewHeight;
+ nCurTextHeightNTP = nNewHeightNTP;
+
+ if ( maStatus.AutoPageSize() )
+ CheckAutoPageSize();
+ else if ( nDiff )
+ {
+ for (EditView* pView : aEditViews)
+ {
+ ImpEditView* pImpView = pView->pImpEditView.get();
+ if ( pImpView->DoAutoHeight() )
+ {
+ Size aSz( pImpView->GetOutputArea().GetWidth(), nCurTextHeight );
+ if ( aSz.Height() > maMaxAutoPaperSize.Height() )
+ aSz.setHeight( maMaxAutoPaperSize.Height() );
+ else if ( aSz.Height() < maMinAutoPaperSize.Height() )
+ aSz.setHeight( maMinAutoPaperSize.Height() );
+ pImpView->ResetOutputArea( tools::Rectangle(
+ pImpView->GetOutputArea().TopLeft(), aSz ) );
+ }
+ }
+ }
+
+ if (!aRepaintParas.empty())
+ {
+ auto CombineRepaintParasAreas = [&](const LineAreaInfo& rInfo) {
+ if (aRepaintParas.count(rInfo.nPortion))
+ aInvalidRect.Union(rInfo.aArea);
+ return CallbackResult::Continue;
+ };
+ IterateLineAreas(CombineRepaintParasAreas, IterFlag::inclILS);
+ }
+ }
+
+ mbIsFormatting = false;
+ mbFormatted = true;
+
+ if ( bMapChanged )
+ GetRefDevice()->Pop();
+
+ CallStatusHdl(); // If Modified...
+}
+
+bool ImpEditEngine::ImpCheckRefMapMode()
+{
+ bool bChange = false;
+
+ if ( maStatus.DoFormat100() )
+ {
+ MapMode aMapMode( GetRefDevice()->GetMapMode() );
+ if ( aMapMode.GetScaleX().GetNumerator() != aMapMode.GetScaleX().GetDenominator() )
+ bChange = true;
+ else if ( aMapMode.GetScaleY().GetNumerator() != aMapMode.GetScaleY().GetDenominator() )
+ bChange = true;
+
+ if ( bChange )
+ {
+ Fraction Scale1( 1, 1 );
+ aMapMode.SetScaleX( Scale1 );
+ aMapMode.SetScaleY( Scale1 );
+ GetRefDevice()->Push();
+ GetRefDevice()->SetMapMode( aMapMode );
+ }
+ }
+
+ return bChange;
+}
+
+void ImpEditEngine::CheckAutoPageSize()
+{
+ Size aPrevPaperSize( GetPaperSize() );
+ if ( GetStatus().AutoPageWidth() )
+ maPaperSize.setWidth( !IsEffectivelyVertical() ? CalcTextWidth( true ) : GetTextHeight() );
+ if ( GetStatus().AutoPageHeight() )
+ maPaperSize.setHeight( !IsEffectivelyVertical() ? GetTextHeight() : CalcTextWidth( true ) );
+
+ SetValidPaperSize( maPaperSize ); // consider Min, Max
+
+ if ( maPaperSize == aPrevPaperSize )
+ return;
+
+ if ( ( !IsEffectivelyVertical() && ( maPaperSize.Width() != aPrevPaperSize.Width() ) )
+ || ( IsEffectivelyVertical() && ( maPaperSize.Height() != aPrevPaperSize.Height() ) ) )
+ {
+ // If ahead is centered / right or tabs...
+ maStatus.GetStatusWord() |= !IsEffectivelyVertical() ? EditStatusFlags::TEXTWIDTHCHANGED : EditStatusFlags::TextHeightChanged;
+ for ( sal_Int32 nPara = 0; nPara < GetParaPortions().Count(); nPara++ )
+ {
+ // Only paragraphs which are not aligned to the left need to be
+ // reformatted, the height can not be changed here anymore.
+ ParaPortion* pParaPortion = GetParaPortions()[nPara];
+ SvxAdjust eJustification = GetJustification( nPara );
+ if ( eJustification != SvxAdjust::Left )
+ {
+ pParaPortion->MarkSelectionInvalid( 0 );
+ CreateLines( nPara, 0 ); // 0: For AutoPageSize no TextRange!
+ }
+ }
+ }
+
+ Size aInvSize = maPaperSize;
+ if ( maPaperSize.Width() < aPrevPaperSize.Width() )
+ aInvSize.setWidth( aPrevPaperSize.Width() );
+ if ( maPaperSize.Height() < aPrevPaperSize.Height() )
+ aInvSize.setHeight( aPrevPaperSize.Height() );
+
+ Size aSz( aInvSize );
+ if ( IsEffectivelyVertical() )
+ {
+ aSz.setWidth( aInvSize.Height() );
+ aSz.setHeight( aInvSize.Width() );
+ }
+ aInvalidRect = tools::Rectangle( Point(), aSz );
+
+
+ for (EditView* pView : aEditViews)
+ {
+ pView->pImpEditView->RecalcOutputArea();
+ }
+}
+
+void ImpEditEngine::CheckPageOverflow()
+{
+ SAL_INFO("editeng.chaining", "[CONTROL_STATUS] AutoPageSize is " << (( maStatus.GetControlWord() & EEControlBits::AUTOPAGESIZE ) ? "ON" : "OFF") );
+
+ tools::Long nBoxHeight = GetMaxAutoPaperSize().Height();
+ SAL_INFO("editeng.chaining", "[OVERFLOW-CHECK] Current MaxAutoPaperHeight is " << nBoxHeight);
+
+ tools::Long nTxtHeight = CalcTextHeight(nullptr);
+ SAL_INFO("editeng.chaining", "[OVERFLOW-CHECK] Current Text Height is " << nTxtHeight);
+
+ sal_uInt32 nParaCount = GetParaPortions().Count();
+ sal_uInt32 nFirstLineCount = GetLineCount(0);
+ bool bOnlyOneEmptyPara = (nParaCount == 1) &&
+ (nFirstLineCount == 1) &&
+ (GetLineLen(0,0) == 0);
+
+ if (nTxtHeight > nBoxHeight && !bOnlyOneEmptyPara)
+ {
+ // which paragraph is the first to cause higher size of the box?
+ ImplUpdateOverflowingParaNum( nBoxHeight); // XXX: currently only for horizontal text
+ //maStatus.SetPageOverflow(true);
+ mbNeedsChainingHandling = true;
+ } else
+ {
+ // No overflow if within box boundaries
+ //maStatus.SetPageOverflow(false);
+ mbNeedsChainingHandling = false;
+ }
+
+}
+
+static sal_Int32 ImplCalculateFontIndependentLineSpacing( const sal_Int32 nFontHeight )
+{
+ constexpr const double f120Percent = 12.0 / 10.0;
+ return basegfx::fround(nFontHeight * f120Percent); // + 20%
+}
+
+tools::Long ImpEditEngine::GetColumnWidth(const Size& rPaperSize) const
+{
+ assert(mnColumns >= 1);
+ tools::Long nWidth = IsEffectivelyVertical() ? rPaperSize.Height() : rPaperSize.Width();
+ return (nWidth - mnColumnSpacing * (mnColumns - 1)) / mnColumns;
+}
+
+bool ImpEditEngine::CreateLines( sal_Int32 nPara, sal_uInt32 nStartPosY )
+{
+ ParaPortion* pParaPortion = GetParaPortions()[nPara];
+
+ // sal_Bool: Changes in the height of paragraph Yes / No - sal_True/sal_False
+ assert( pParaPortion->GetNode() && "Portion without Node in CreateLines" );
+ DBG_ASSERT( pParaPortion->IsVisible(), "Invisible paragraphs not formatted!" );
+ DBG_ASSERT( pParaPortion->IsInvalid(), "CreateLines: Portion not invalid!" );
+
+ bool bProcessingEmptyLine = ( pParaPortion->GetNode()->Len() == 0 );
+ bool bEmptyNodeWithPolygon = ( pParaPortion->GetNode()->Len() == 0 ) && GetTextRanger();
+
+
+ // Fast special treatment for empty paragraphs...
+
+ if ( ( pParaPortion->GetNode()->Len() == 0 ) && !GetTextRanger() )
+ {
+ // fast special treatment...
+ if ( pParaPortion->GetTextPortions().Count() )
+ pParaPortion->GetTextPortions().Reset();
+ if ( pParaPortion->GetLines().Count() )
+ pParaPortion->GetLines().Reset();
+ CreateAndInsertEmptyLine( pParaPortion );
+ return FinishCreateLines( pParaPortion );
+ }
+
+ sal_Int64 nCurrentPosY = nStartPosY;
+ // If we're allowed to skip parts outside and this cannot possibly fit in the given height,
+ // bail out to avoid possibly formatting a lot of text that will not be used. For the first
+ // paragraph still format at least a bit.
+ if( mbSkipOutsideFormat && nPara != 0
+ && !maStatus.AutoPageHeight() && maPaperSize.Height() < nCurrentPosY )
+ {
+ return false;
+ }
+
+ // Initialization...
+
+ // Always format for 100%:
+ bool bMapChanged = ImpCheckRefMapMode();
+
+ if ( pParaPortion->GetLines().Count() == 0 )
+ {
+ EditLine* pL = new EditLine;
+ pParaPortion->GetLines().Append(pL);
+ }
+
+
+ // Get Paragraph attributes...
+
+ ContentNode* const pNode = pParaPortion->GetNode();
+
+ bool bRightToLeftPara = IsRightToLeft( nPara );
+
+ SvxAdjust eJustification = GetJustification( nPara );
+ bool bHyphenatePara = pNode->GetContentAttribs().GetItem( EE_PARA_HYPHENATE ).GetValue();
+ sal_Int32 nSpaceBefore = 0;
+ sal_Int32 nMinLabelWidth = 0;
+ sal_Int32 nSpaceBeforeAndMinLabelWidth = GetSpaceBeforeAndMinLabelWidth( pNode, &nSpaceBefore, &nMinLabelWidth );
+ const SvxLRSpaceItem& rLRItem = GetLRSpaceItem( pNode );
+ const SvxLineSpacingItem& rLSItem = pNode->GetContentAttribs().GetItem( EE_PARA_SBL );
+ const bool bScriptSpace = pNode->GetContentAttribs().GetItem( EE_PARA_ASIANCJKSPACING ).GetValue();
+
+ const short nInvalidDiff = pParaPortion->GetInvalidDiff();
+ const sal_Int32 nInvalidStart = pParaPortion->GetInvalidPosStart();
+ const sal_Int32 nInvalidEnd = nInvalidStart + std::abs( nInvalidDiff );
+
+ bool bQuickFormat = false;
+ if ( !bEmptyNodeWithPolygon && !HasScriptType( nPara, i18n::ScriptType::COMPLEX ) )
+ {
+ if ( ( pParaPortion->IsSimpleInvalid() ) && ( nInvalidDiff > 0 ) &&
+ ( pNode->GetString().indexOf( CH_FEATURE, nInvalidStart ) > nInvalidEnd ) )
+ {
+ bQuickFormat = true;
+ }
+ else if ( ( pParaPortion->IsSimpleInvalid() ) && ( nInvalidDiff < 0 ) )
+ {
+ // check if delete over the portion boundaries was done...
+ sal_Int32 nStart = nInvalidStart; // DOUBLE !!!!!!!!!!!!!!!
+ sal_Int32 nEnd = nStart - nInvalidDiff; // negative
+ bQuickFormat = true;
+ sal_Int32 nPos = 0;
+ sal_Int32 nPortions = pParaPortion->GetTextPortions().Count();
+ for ( sal_Int32 nTP = 0; nTP < nPortions; nTP++ )
+ {
+ // There must be no start / end in the deleted area.
+ const TextPortion& rTP = pParaPortion->GetTextPortions()[ nTP ];
+ nPos = nPos + rTP.GetLen();
+ if ( ( nPos > nStart ) && ( nPos < nEnd ) )
+ {
+ bQuickFormat = false;
+ break;
+ }
+ }
+ }
+ }
+
+ // Saving both layout mode and language (since I'm potentially changing both)
+ GetRefDevice()->Push( vcl::PushFlags::TEXTLAYOUTMODE|vcl::PushFlags::TEXTLANGUAGE );
+
+ ImplInitLayoutMode(*GetRefDevice(), nPara, -1);
+
+ sal_Int32 nRealInvalidStart = nInvalidStart;
+
+ if ( bEmptyNodeWithPolygon )
+ {
+ TextPortion* pDummyPortion = new TextPortion( 0 );
+ pParaPortion->GetTextPortions().Reset();
+ pParaPortion->GetTextPortions().Append(pDummyPortion);
+ }
+ else if ( bQuickFormat )
+ {
+ // faster Method:
+ RecalcTextPortion( pParaPortion, nInvalidStart, nInvalidDiff );
+ }
+ else // nRealInvalidStart can be before InvalidStart, since Portions were deleted...
+ {
+ CreateTextPortions( pParaPortion, nRealInvalidStart );
+ }
+
+
+ // Search for line with InvalidPos, start one line before
+ // Flag the line => do not remove it !
+
+
+ sal_Int32 nLine = pParaPortion->GetLines().Count()-1;
+ for ( sal_Int32 nL = 0; nL <= nLine; nL++ )
+ {
+ EditLine& rLine = pParaPortion->GetLines()[nL];
+ if ( rLine.GetEnd() > nRealInvalidStart ) // not nInvalidStart!
+ {
+ nLine = nL;
+ break;
+ }
+ rLine.SetValid();
+ }
+ // Begin one line before...
+ // If it is typed at the end, the line in front cannot change.
+ if ( nLine && ( !pParaPortion->IsSimpleInvalid() || ( nInvalidEnd < pNode->Len() ) || ( nInvalidDiff <= 0 ) ) )
+ nLine--;
+
+ EditLine* pLine = &pParaPortion->GetLines()[nLine];
+
+ static const tools::Rectangle aZeroArea { Point(), Point() };
+ tools::Rectangle aBulletArea( aZeroArea );
+ if ( !nLine )
+ {
+ aBulletArea = GetEditEnginePtr()->GetBulletArea( GetParaPortions().GetPos( pParaPortion ) );
+ if ( !aBulletArea.IsWidthEmpty() && aBulletArea.Right() > 0 )
+ pParaPortion->SetBulletX(sal_Int32(scaleXSpacingValue(aBulletArea.Right())));
+ else
+ pParaPortion->SetBulletX( 0 ); // if Bullet is set incorrectly
+ }
+
+
+ // Reformat all lines from here...
+
+ sal_Int32 nDelFromLine = -1;
+ bool bLineBreak = false;
+
+ sal_Int32 nIndex = pLine->GetStart();
+ EditLine aSaveLine( *pLine );
+ SvxFont aTmpFont( pNode->GetCharAttribs().GetDefFont() );
+
+ KernArray aCharPositionArray;
+
+ bool bSameLineAgain = false; // For TextRanger, if the height changes.
+ TabInfo aCurrentTab;
+
+ bool bForceOneRun = bEmptyNodeWithPolygon;
+ bool bCompressedChars = false;
+
+ while ( ( nIndex < pNode->Len() ) || bForceOneRun )
+ {
+ assert(pLine);
+
+ bForceOneRun = false;
+
+ bool bEOL = false;
+ bool bEOC = false;
+ sal_Int32 nPortionStart = 0;
+ sal_Int32 nPortionEnd = 0;
+
+ tools::Long nStartX = scaleXSpacingValue(rLRItem.GetTextLeft() + nSpaceBeforeAndMinLabelWidth);
+ if ( nIndex == 0 )
+ {
+ tools::Long nFI = scaleXSpacingValue(rLRItem.GetTextFirstLineOffset());
+ nStartX += nFI;
+
+ if ( !nLine && ( pParaPortion->GetBulletX() > nStartX ) )
+ {
+ nStartX = pParaPortion->GetBulletX();
+ }
+ }
+
+ const bool bAutoSize = IsEffectivelyVertical() ? maStatus.AutoPageHeight() : maStatus.AutoPageWidth();
+ tools::Long nMaxLineWidth = GetColumnWidth(bAutoSize ? maMaxAutoPaperSize : maPaperSize);
+
+ nMaxLineWidth -= scaleXSpacingValue(rLRItem.GetRight());
+ nMaxLineWidth -= nStartX;
+
+ // If PaperSize == long_max, one cannot take away any negative
+ // first line indent. (Overflow)
+ if ( ( nMaxLineWidth < 0 ) && ( nStartX < 0 ) )
+ nMaxLineWidth = GetColumnWidth(maPaperSize) - scaleXSpacingValue(rLRItem.GetRight());
+
+ // If still less than 0, it may be just the right edge.
+ if ( nMaxLineWidth <= 0 )
+ nMaxLineWidth = 1;
+
+ // Problem:
+ // Since formatting starts a line _before_ the invalid position,
+ // the positions unfortunately have to be redefined...
+ // Solution:
+ // The line before can only become longer, not smaller
+ // =>...
+ pLine->GetCharPosArray().clear();
+
+ sal_Int32 nTmpPos = nIndex;
+ sal_Int32 nTmpPortion = pLine->GetStartPortion();
+ tools::Long nTmpWidth = 0;
+ tools::Long nXWidth = nMaxLineWidth;
+
+ std::deque<tools::Long>* pTextRanges = nullptr;
+ tools::Long nTextExtraYOffset = 0;
+ tools::Long nTextXOffset = 0;
+ tools::Long nTextLineHeight = 0;
+ if ( GetTextRanger() )
+ {
+ GetTextRanger()->SetVertical( IsEffectivelyVertical() );
+
+ tools::Long nTextY = nStartPosY + GetEditCursor( pParaPortion, pLine, pLine->GetStart(), GetCursorFlags::NONE ).Top();
+ if ( !bSameLineAgain )
+ {
+ SeekCursor( pNode, nTmpPos+1, aTmpFont );
+ aTmpFont.SetPhysFont(*GetRefDevice());
+ ImplInitDigitMode(*GetRefDevice(), aTmpFont.GetLanguage());
+
+ if ( IsFixedCellHeight() )
+ nTextLineHeight = ImplCalculateFontIndependentLineSpacing( aTmpFont.GetFontHeight() );
+ else
+ nTextLineHeight = aTmpFont.GetPhysTxtSize( GetRefDevice() ).Height();
+ // Metrics can be greater
+ FormatterFontMetric aTempFormatterMetrics;
+ RecalcFormatterFontMetrics( aTempFormatterMetrics, aTmpFont );
+ sal_uInt16 nLineHeight = aTempFormatterMetrics.GetHeight();
+ if ( nLineHeight > nTextLineHeight )
+ nTextLineHeight = nLineHeight;
+ }
+ else
+ nTextLineHeight = pLine->GetHeight();
+
+ nXWidth = 0;
+ while ( !nXWidth )
+ {
+ tools::Long nYOff = nTextY + nTextExtraYOffset;
+ tools::Long nYDiff = nTextLineHeight;
+ if ( IsEffectivelyVertical() )
+ {
+ tools::Long nMaxPolygonX = GetTextRanger()->GetBoundRect().Right();
+ nYOff = nMaxPolygonX-nYOff;
+ nYDiff = -nTextLineHeight;
+ }
+ pTextRanges = GetTextRanger()->GetTextRanges( Range( nYOff, nYOff + nYDiff ) );
+ assert( pTextRanges && "GetTextRanges?!" );
+ tools::Long nMaxRangeWidth = 0;
+ // Use the widest range...
+ // The widest range could be a bit confusing, so normally it
+ // is the first one. Best with gaps.
+ assert(pTextRanges->size() % 2 == 0 && "textranges are always in pairs");
+ if (!pTextRanges->empty())
+ {
+ tools::Long nA = pTextRanges->at(0);
+ tools::Long nB = pTextRanges->at(1);
+ DBG_ASSERT( nA <= nB, "TextRange distorted?" );
+ tools::Long nW = nB - nA;
+ if ( nW > nMaxRangeWidth )
+ {
+ nMaxRangeWidth = nW;
+ nTextXOffset = nA;
+ }
+ }
+ nXWidth = nMaxRangeWidth;
+ if ( nXWidth )
+ nMaxLineWidth = nXWidth - nStartX - scaleXSpacingValue(rLRItem.GetRight());
+ else
+ {
+ // Try further down in the polygon.
+ // Below the polygon use the Paper Width.
+ nTextExtraYOffset += std::max( static_cast<tools::Long>(nTextLineHeight / 10), tools::Long(1) );
+ if ( ( nTextY + nTextExtraYOffset ) > GetTextRanger()->GetBoundRect().Bottom() )
+ {
+ nXWidth = getWidthDirectionAware(GetPaperSize());
+ if ( !nXWidth ) // AutoPaperSize
+ nXWidth = 0x7FFFFFFF;
+ }
+ }
+ }
+ }
+
+ // search for Portion that no longer fits in line...
+ TextPortion* pPortion = nullptr;
+ sal_Int32 nPortionLen = 0;
+ bool bContinueLastPortion = false;
+ bool bBrokenLine = false;
+ bLineBreak = false;
+ const EditCharAttrib* pNextFeature = pNode->GetCharAttribs().FindFeature( pLine->GetStart() );
+ while ( ( nTmpWidth < nXWidth ) && !bEOL )
+ {
+ const sal_Int32 nTextPortions = pParaPortion->GetTextPortions().Count();
+ assert(nTextPortions > 0);
+ bContinueLastPortion = (nTmpPortion >= nTextPortions);
+ if (bContinueLastPortion)
+ {
+ if (nTmpPos >= pNode->Len())
+ break; // while
+
+ // Continue with remainder. This only to have *some* valid
+ // X-values and not endlessly create new lines until DOOM...
+ // Happened in the scenario of tdf#104152 where inserting a
+ // paragraph lead to a11y attempting to format the doc to
+ // obtain content when notified.
+ nTmpPortion = nTextPortions - 1;
+ SAL_WARN("editeng","ImpEditEngine::CreateLines - continuation of a broken portion");
+ }
+
+ nPortionStart = nTmpPos;
+ pPortion = &pParaPortion->GetTextPortions()[nTmpPortion];
+ if ( !bContinueLastPortion && pPortion->GetKind() == PortionKind::HYPHENATOR )
+ {
+ // Throw away a Portion, if necessary correct the one before,
+ // if the Hyph portion has swallowed a character...
+ sal_Int32 nTmpLen = pPortion->GetLen();
+ pParaPortion->GetTextPortions().Remove( nTmpPortion );
+ if (nTmpPortion && nTmpLen)
+ {
+ nTmpPortion--;
+ TextPortion& rPrev = pParaPortion->GetTextPortions()[nTmpPortion];
+ DBG_ASSERT( rPrev.GetKind() == PortionKind::TEXT, "Portion?!" );
+ nTmpWidth -= rPrev.GetSize().Width();
+ nTmpPos = nTmpPos - rPrev.GetLen();
+ rPrev.SetLen(rPrev.GetLen() + nTmpLen);
+ rPrev.setWidth(-1);
+ }
+
+ assert( nTmpPortion < pParaPortion->GetTextPortions().Count() && "No more Portions left!" );
+ pPortion = &pParaPortion->GetTextPortions()[nTmpPortion];
+ }
+
+ if (bContinueLastPortion)
+ {
+ // Note that this may point behind the portion and is only to
+ // be used with the node's string offsets to generate X-values.
+ nPortionLen = pNode->Len() - nPortionStart;
+ }
+ else
+ {
+ nPortionLen = pPortion->GetLen();
+ }
+
+ DBG_ASSERT( pPortion->GetKind() != PortionKind::HYPHENATOR, "CreateLines: Hyphenator-Portion!" );
+ DBG_ASSERT( nPortionLen || bProcessingEmptyLine, "Empty Portion in CreateLines ?!" );
+ if ( pNextFeature && ( pNextFeature->GetStart() == nTmpPos ) )
+ {
+ SAL_WARN_IF( bContinueLastPortion,
+ "editeng","ImpEditEngine::CreateLines - feature in continued portion will be wrong");
+ sal_uInt16 nWhich = pNextFeature->GetItem()->Which();
+ switch ( nWhich )
+ {
+ case EE_FEATURE_TAB:
+ {
+ tools::Long nOldTmpWidth = nTmpWidth;
+
+ // Search for Tab-Pos...
+ tools::Long nCurPos = nTmpWidth + nStartX;
+ // consider scaling
+ if (maStatus.DoStretch() && (mfFontScaleX != 100.0))
+ nCurPos = basegfx::fround(double(nCurPos) * 100.0 / std::max(mfFontScaleX, 1.0));
+
+ short nAllSpaceBeforeText = static_cast< short >(rLRItem.GetTextLeft()/* + rLRItem.GetTextLeft()*/ + nSpaceBeforeAndMinLabelWidth);
+ aCurrentTab.aTabStop = pNode->GetContentAttribs().FindTabStop( nCurPos - nAllSpaceBeforeText /*rLRItem.GetTextLeft()*/, maEditDoc.GetDefTab() );
+ aCurrentTab.nTabPos = scaleXFontValue(tools::Long(aCurrentTab.aTabStop.GetTabPos() + nAllSpaceBeforeText/*rLRItem.GetTextLeft()*/));
+ aCurrentTab.bValid = false;
+
+ // Switch direction in R2L para...
+ if ( bRightToLeftPara )
+ {
+ if ( aCurrentTab.aTabStop.GetAdjustment() == SvxTabAdjust::Right )
+ aCurrentTab.aTabStop.GetAdjustment() = SvxTabAdjust::Left;
+ else if ( aCurrentTab.aTabStop.GetAdjustment() == SvxTabAdjust::Left )
+ aCurrentTab.aTabStop.GetAdjustment() = SvxTabAdjust::Right;
+ }
+
+ if ( ( aCurrentTab.aTabStop.GetAdjustment() == SvxTabAdjust::Right ) ||
+ ( aCurrentTab.aTabStop.GetAdjustment() == SvxTabAdjust::Center ) ||
+ ( aCurrentTab.aTabStop.GetAdjustment() == SvxTabAdjust::Decimal ) )
+ {
+ // For LEFT / DEFAULT this tab is not considered.
+ aCurrentTab.bValid = true;
+ aCurrentTab.nStartPosX = nTmpWidth;
+ aCurrentTab.nTabPortion = nTmpPortion;
+ }
+
+ pPortion->SetKind(PortionKind::TAB);
+ pPortion->SetExtraValue( aCurrentTab.aTabStop.GetFill() );
+ pPortion->setWidth( aCurrentTab.nTabPos - (nTmpWidth+nStartX) );
+
+ // Height needed...
+ SeekCursor( pNode, nTmpPos+1, aTmpFont );
+ pPortion->setHeight( GetRefDevice()->GetTextHeight() );
+
+ DBG_ASSERT( pPortion->GetSize().Width() >= 0, "Tab incorrectly calculated!" );
+
+ nTmpWidth = aCurrentTab.nTabPos-nStartX;
+
+ // If this is the first token on the line,
+ // and nTmpWidth > maPaperSize.Width, => infinite loop!
+ if ( ( nTmpWidth >= nXWidth ) && ( nTmpPortion == pLine->GetStartPortion() ) )
+ {
+ // What now?
+ // make the tab fitting
+ pPortion->setWidth( nXWidth-nOldTmpWidth );
+ nTmpWidth = nXWidth-1;
+ bEOL = true;
+ bBrokenLine = true;
+ }
+ EditLine::CharPosArrayType& rArray = pLine->GetCharPosArray();
+ size_t nPos = nTmpPos - pLine->GetStart();
+ rArray.insert(rArray.begin()+nPos, pPortion->GetSize().Width());
+ bCompressedChars = false;
+ }
+ break;
+ case EE_FEATURE_LINEBR:
+ {
+ assert( pPortion );
+ pPortion->setWidth(0);
+ bEOL = true;
+ bLineBreak = true;
+ pPortion->SetKind( PortionKind::LINEBREAK );
+ bCompressedChars = false;
+ EditLine::CharPosArrayType& rArray = pLine->GetCharPosArray();
+ size_t nPos = nTmpPos - pLine->GetStart();
+ rArray.insert(rArray.begin()+nPos, pPortion->GetSize().Width());
+ }
+ break;
+ case EE_FEATURE_FIELD:
+ {
+ SeekCursor( pNode, nTmpPos+1, aTmpFont );
+ aTmpFont.SetPhysFont(*GetRefDevice());
+ ImplInitDigitMode(*GetRefDevice(), aTmpFont.GetLanguage());
+
+ OUString aFieldValue = static_cast<const EditCharAttribField*>(pNextFeature)->GetFieldValue();
+ // get size, but also DXArray to allow length information in line breaking below
+ KernArray aTmpDXArray;
+ pPortion->SetSize(aTmpFont.QuickGetTextSize(GetRefDevice(),
+ aFieldValue, 0, aFieldValue.getLength(), &aTmpDXArray));
+
+ // So no scrolling for oversized fields
+ if ( pPortion->GetSize().Width() > nXWidth )
+ {
+ // create ExtraPortionInfo on-demand, flush lineBreaksList
+ ExtraPortionInfo *pExtraInfo = pPortion->GetExtraInfos();
+
+ if(nullptr == pExtraInfo)
+ {
+ pExtraInfo = new ExtraPortionInfo();
+ pExtraInfo->nOrgWidth = nXWidth;
+ pPortion->SetExtraInfos(pExtraInfo);
+ }
+ else
+ {
+ pExtraInfo->lineBreaksList.clear();
+ }
+
+ // iterate over CellBreaks using XBreakIterator to be on the
+ // safe side with international texts/charSets
+ Reference < i18n::XBreakIterator > xBreakIterator(ImplGetBreakIterator());
+ const sal_Int32 nTextLength(aFieldValue.getLength());
+ const lang::Locale aLocale(GetLocale(EditPaM(pNode, nPortionStart)));
+ sal_Int32 nDone(0);
+ sal_Int32 nNextCellBreak(
+ xBreakIterator->nextCharacters(
+ aFieldValue,
+ 0,
+ aLocale,
+ css::i18n::CharacterIteratorMode::SKIPCELL,
+ 0,
+ nDone));
+ sal_Int32 nLastCellBreak(0);
+ sal_Int32 nLineStartX(0);
+
+ // always add 1st line break (safe, we already know we are larger than nXWidth)
+ pExtraInfo->lineBreaksList.push_back(0);
+
+ for(sal_Int32 a(0); a < nTextLength; a++)
+ {
+ if(a == nNextCellBreak)
+ {
+ // check width
+ if(aTmpDXArray[a] - nLineStartX > nXWidth)
+ {
+ // new CellBreak does not fit in current line, need to
+ // create a break at LastCellBreak - but do not add 1st
+ // line break twice for very tall frames
+ if(0 != a)
+ {
+ pExtraInfo->lineBreaksList.push_back(a);
+ }
+
+ // moveLineStart forward in X
+ nLineStartX = aTmpDXArray[nLastCellBreak];
+ }
+
+ // update CellBreak iteration values
+ nLastCellBreak = a;
+ nNextCellBreak = xBreakIterator->nextCharacters(
+ aFieldValue,
+ a,
+ aLocale,
+ css::i18n::CharacterIteratorMode::SKIPCELL,
+ 1,
+ nDone);
+ }
+ }
+ }
+ nTmpWidth += pPortion->GetSize().Width();
+ EditLine::CharPosArrayType& rArray = pLine->GetCharPosArray();
+ size_t nPos = nTmpPos - pLine->GetStart();
+ rArray.insert(rArray.begin()+nPos, pPortion->GetSize().Width());
+ pPortion->SetKind(PortionKind::FIELD);
+ // If this is the first token on the line,
+ // and nTmpWidth > maPaperSize.Width, => infinite loop!
+ if ( ( nTmpWidth >= nXWidth ) && ( nTmpPortion == pLine->GetStartPortion() ) )
+ {
+ nTmpWidth = nXWidth-1;
+ bEOL = true;
+ bBrokenLine = true;
+ }
+ // Compression in Fields????
+ // I think this could be a little bit difficult and is not very useful
+ bCompressedChars = false;
+ }
+ break;
+ default: OSL_FAIL( "What feature?" );
+ }
+ pNextFeature = pNode->GetCharAttribs().FindFeature( pNextFeature->GetStart() + 1 );
+ }
+ else
+ {
+ DBG_ASSERT( nPortionLen || bProcessingEmptyLine, "Empty Portion - Extra Space?!" );
+ SeekCursor( pNode, nTmpPos+1, aTmpFont );
+ aTmpFont.SetPhysFont(*GetRefDevice());
+ ImplInitDigitMode(*GetRefDevice(), aTmpFont.GetLanguage());
+
+ if (!bContinueLastPortion)
+ pPortion->SetRightToLeftLevel( GetRightToLeft( nPara, nTmpPos+1 ) );
+
+ if (bContinueLastPortion)
+ {
+ Size aSize( aTmpFont.QuickGetTextSize( GetRefDevice(),
+ pParaPortion->GetNode()->GetString(), nTmpPos, nPortionLen, &aCharPositionArray ));
+ pPortion->adjustSize(aSize.Width(), 0);
+ if (pPortion->GetSize().Height() < aSize.Height())
+ pPortion->setHeight(aSize.Height());
+ }
+ else
+ {
+ auto aSize = aTmpFont.QuickGetTextSize(GetRefDevice(), pParaPortion->GetNode()->GetString(), nTmpPos, nPortionLen, &aCharPositionArray);
+ pPortion->SetSize(aSize);
+ }
+
+ // #i9050# Do Kerning also behind portions...
+ if ( ( aTmpFont.GetFixKerning() > 0 ) && ( ( nTmpPos + nPortionLen ) < pNode->Len() ) )
+ pPortion->adjustSize(aTmpFont.GetFixKerning(), 0);
+ if ( IsFixedCellHeight() )
+ {
+ pPortion->setHeight( ImplCalculateFontIndependentLineSpacing( aTmpFont.GetFontHeight() ) );
+ }
+ // The array is generally flattened at the beginning
+ // => Always simply quick inserts.
+ size_t nPos = nTmpPos - pLine->GetStart();
+ EditLine::CharPosArrayType& rArray = pLine->GetCharPosArray();
+ assert(aCharPositionArray.get_factor() == 1);
+ std::vector<sal_Int32>& rKernArray = aCharPositionArray.get_subunit_array();
+ rArray.insert( rArray.begin() + nPos, rKernArray.data(), rKernArray.data() + nPortionLen);
+
+ // And now check for Compression:
+ if ( !bContinueLastPortion && nPortionLen && GetAsianCompressionMode() != CharCompressType::NONE )
+ {
+ sal_Int32* pDXArray = rArray.data() + nTmpPos - pLine->GetStart();
+ bCompressedChars |= ImplCalcAsianCompression(
+ pNode, pPortion, nTmpPos, pDXArray, 10000, false);
+ }
+
+ nTmpWidth += pPortion->GetSize().Width();
+
+ sal_Int32 _nPortionEnd = nTmpPos + nPortionLen;
+ if( bScriptSpace && ( _nPortionEnd < pNode->Len() ) && ( nTmpWidth < nXWidth ) && IsScriptChange( EditPaM( pNode, _nPortionEnd ) ) )
+ {
+ bool bAllow = false;
+ sal_uInt16 nScriptTypeLeft = GetI18NScriptType( EditPaM( pNode, _nPortionEnd ) );
+ sal_uInt16 nScriptTypeRight = GetI18NScriptType( EditPaM( pNode, _nPortionEnd+1 ) );
+ if ( ( nScriptTypeLeft == i18n::ScriptType::ASIAN ) || ( nScriptTypeRight == i18n::ScriptType::ASIAN ) )
+ bAllow = true;
+
+ // No spacing within L2R/R2L nesting
+ if ( bAllow )
+ {
+ tools::Long nExtraSpace = pPortion->GetSize().Height() / 5;
+ nExtraSpace = scaleXSpacingValue(nExtraSpace);
+ pPortion->adjustSize(nExtraSpace, 0);
+ nTmpWidth += nExtraSpace;
+ }
+ }
+ }
+
+ if ( aCurrentTab.bValid && ( nTmpPortion != aCurrentTab.nTabPortion ) )
+ {
+ tools::Long nWidthAfterTab = 0;
+ for ( sal_Int32 n = aCurrentTab.nTabPortion+1; n <= nTmpPortion; n++ )
+ {
+ const TextPortion& rTP = pParaPortion->GetTextPortions()[n];
+ nWidthAfterTab += rTP.GetSize().Width();
+ }
+ tools::Long nW = nWidthAfterTab; // Length before tab position
+ if ( aCurrentTab.aTabStop.GetAdjustment() == SvxTabAdjust::Right )
+ {
+ }
+ else if ( aCurrentTab.aTabStop.GetAdjustment() == SvxTabAdjust::Center )
+ {
+ nW = nWidthAfterTab/2;
+ }
+ else if ( aCurrentTab.aTabStop.GetAdjustment() == SvxTabAdjust::Decimal )
+ {
+ OUString aText = GetSelected( EditSelection( EditPaM( pParaPortion->GetNode(), nTmpPos ),
+ EditPaM( pParaPortion->GetNode(), nTmpPos + nPortionLen ) ) );
+ sal_Int32 nDecPos = aText.indexOf( aCurrentTab.aTabStop.GetDecimal() );
+ if ( nDecPos != -1 )
+ {
+ nW -= pParaPortion->GetTextPortions()[nTmpPortion].GetSize().Width();
+ nW += aTmpFont.QuickGetTextSize( GetRefDevice(), pParaPortion->GetNode()->GetString(),
+ nTmpPos, nDecPos, nullptr ).Width();
+ aCurrentTab.bValid = false;
+ }
+ }
+ else
+ {
+ OSL_FAIL( "CreateLines: Tab not handled!" );
+ }
+ tools::Long nMaxW = aCurrentTab.nTabPos - aCurrentTab.nStartPosX - nStartX;
+ if ( nW >= nMaxW )
+ {
+ nW = nMaxW;
+ aCurrentTab.bValid = false;
+ }
+ TextPortion& rTabPortion = pParaPortion->GetTextPortions()[aCurrentTab.nTabPortion];
+ rTabPortion.setWidth( aCurrentTab.nTabPos - aCurrentTab.nStartPosX - nW - nStartX );
+ nTmpWidth = aCurrentTab.nStartPosX + rTabPortion.GetSize().Width() + nWidthAfterTab;
+ }
+
+ nTmpPos = nTmpPos + nPortionLen;
+ nPortionEnd = nTmpPos;
+ nTmpPortion++;
+ if (maStatus.OneCharPerLine())
+ bEOL = true;
+ }
+
+ DBG_ASSERT( pPortion, "no portion!?" );
+
+ aCurrentTab.bValid = false;
+
+ assert(pLine);
+
+ // this was possibly a portion too far:
+ bool bFixedEnd = false;
+ if (maStatus.OneCharPerLine())
+ {
+ // State before Portion (apart from nTmpWidth):
+ nTmpPos -= pPortion ? nPortionLen : 0;
+ nPortionStart = nTmpPos;
+ nTmpPortion--;
+
+ bEOL = true;
+ bEOC = false;
+
+ // And now just one character:
+ nTmpPos++;
+ nTmpPortion++;
+ nPortionEnd = nTmpPortion;
+ // one Non-Feature-Portion has to be wrapped
+ if ( pPortion && nPortionLen > 1 )
+ {
+ DBG_ASSERT( pPortion->GetKind() == PortionKind::TEXT, "Len>1, but no TextPortion?" );
+ nTmpWidth -= pPortion->GetSize().Width();
+ sal_Int32 nP = SplitTextPortion( pParaPortion, nTmpPos, pLine );
+ nTmpWidth += pParaPortion->GetTextPortions()[nP].GetSize().Width();
+ }
+ }
+ else if ( nTmpWidth >= nXWidth )
+ {
+ nPortionEnd = nTmpPos;
+ nTmpPos -= pPortion ? nPortionLen : 0;
+ nPortionStart = nTmpPos;
+ nTmpPortion--;
+ bEOL = false;
+ bEOC = false;
+ if( pPortion ) switch ( pPortion->GetKind() )
+ {
+ case PortionKind::TEXT:
+ {
+ nTmpWidth -= pPortion->GetSize().Width();
+ }
+ break;
+ case PortionKind::FIELD:
+ case PortionKind::TAB:
+ {
+ nTmpWidth -= pPortion->GetSize().Width();
+ bEOL = true;
+ bFixedEnd = true;
+ }
+ break;
+ default:
+ {
+ // A feature is not wrapped:
+ DBG_ASSERT( ( pPortion->GetKind() == PortionKind::LINEBREAK ), "What Feature ?" );
+ bEOL = true;
+ bFixedEnd = true;
+ }
+ }
+ }
+ else
+ {
+ bEOL = true;
+ bEOC = true;
+ pLine->SetEnd( nPortionEnd );
+ assert( pParaPortion->GetTextPortions().Count() && "No TextPortions?" );
+ pLine->SetEndPortion( pParaPortion->GetTextPortions().Count() - 1 );
+ }
+
+ if (maStatus.OneCharPerLine())
+ {
+ pLine->SetEnd( nPortionEnd );
+ pLine->SetEndPortion( nTmpPortion-1 );
+ }
+ else if ( bFixedEnd )
+ {
+ pLine->SetEnd( nPortionStart );
+ pLine->SetEndPortion( nTmpPortion-1 );
+ }
+ else if ( bLineBreak || bBrokenLine )
+ {
+ pLine->SetEnd( nPortionStart+1 );
+ pLine->SetEndPortion( nTmpPortion-1 );
+ bEOC = false; // was set above, maybe change the sequence of the if's?
+ }
+ else if ( !bEOL && !bContinueLastPortion )
+ {
+ DBG_ASSERT( pPortion && ((nPortionEnd-nPortionStart) == pPortion->GetLen()), "However, another portion?!" );
+ tools::Long nRemainingWidth = !maStatus.IsSingleLine() ?
+ nMaxLineWidth - nTmpWidth : pLine->GetCharPosArray()[pLine->GetCharPosArray().size() - 1] + 1;
+ bool bCanHyphenate = ( aTmpFont.GetCharSet() != RTL_TEXTENCODING_SYMBOL );
+ if ( bCompressedChars && pPortion && ( pPortion->GetLen() > 1 ) && pPortion->GetExtraInfos() && pPortion->GetExtraInfos()->bCompressed )
+ {
+ // I need the manipulated DXArray for determining the break position...
+ sal_Int32* pDXArray = pLine->GetCharPosArray().data() + (nPortionStart - pLine->GetStart());
+ ImplCalcAsianCompression(
+ pNode, pPortion, nPortionStart, pDXArray, 10000, true);
+ }
+ if( pPortion )
+ ImpBreakLine( pParaPortion, pLine, pPortion, nPortionStart,
+ nRemainingWidth, bCanHyphenate && bHyphenatePara );
+ }
+
+
+ // Line finished => adjust
+
+
+ // CalcTextSize should be replaced by a continuous registering!
+ Size aTextSize = pLine->CalcTextSize( *pParaPortion );
+
+ if ( aTextSize.Height() == 0 )
+ {
+ SeekCursor( pNode, pLine->GetStart()+1, aTmpFont );
+ aTmpFont.SetPhysFont(*pRefDev);
+ ImplInitDigitMode(*pRefDev, aTmpFont.GetLanguage());
+
+ if ( IsFixedCellHeight() )
+ aTextSize.setHeight( ImplCalculateFontIndependentLineSpacing( aTmpFont.GetFontHeight() ) );
+ else
+ aTextSize.setHeight( aTmpFont.GetPhysTxtSize( pRefDev ).Height() );
+ pLine->SetHeight( static_cast<sal_uInt16>(aTextSize.Height()) );
+ }
+
+ // The font metrics can not be calculated continuously, if the font is
+ // set anyway, because a large font only after wrapping suddenly ends
+ // up in the next line => Font metrics too big.
+ FormatterFontMetric aFormatterMetrics;
+ sal_Int32 nTPos = pLine->GetStart();
+ for ( sal_Int32 nP = pLine->GetStartPortion(); nP <= pLine->GetEndPortion(); nP++ )
+ {
+ const TextPortion& rTP = pParaPortion->GetTextPortions()[nP];
+ // problem with hard font height attribute, when everything but the line break has this attribute
+ if ( rTP.GetKind() != PortionKind::LINEBREAK )
+ {
+ SeekCursor( pNode, nTPos+1, aTmpFont );
+ aTmpFont.SetPhysFont(*GetRefDevice());
+ ImplInitDigitMode(*GetRefDevice(), aTmpFont.GetLanguage());
+ RecalcFormatterFontMetrics( aFormatterMetrics, aTmpFont );
+ }
+ nTPos = nTPos + rTP.GetLen();
+ }
+ sal_uInt16 nLineHeight = aFormatterMetrics.GetHeight();
+ if ( nLineHeight > pLine->GetHeight() )
+ pLine->SetHeight( nLineHeight );
+ pLine->SetMaxAscent( aFormatterMetrics.nMaxAscent );
+
+ bSameLineAgain = false;
+ if ( GetTextRanger() && ( pLine->GetHeight() > nTextLineHeight ) )
+ {
+ // put down with the other size!
+ bSameLineAgain = true;
+ }
+
+ if (!bSameLineAgain && !maStatus.IsOutliner())
+ {
+ if ( rLSItem.GetLineSpaceRule() == SvxLineSpaceRule::Min )
+ {
+ double fMinHeight = scaleYSpacingValue(rLSItem.GetLineHeight());
+ sal_uInt16 nMinHeight = basegfx::fround(fMinHeight);
+
+ sal_uInt16 nTxtHeight = pLine->GetHeight();
+ if ( nTxtHeight < nMinHeight )
+ {
+ // The Ascent has to be adjusted for the difference:
+ tools::Long nDiff = nMinHeight - nTxtHeight;
+ pLine->SetMaxAscent( static_cast<sal_uInt16>(pLine->GetMaxAscent() + nDiff) );
+ pLine->SetHeight( nMinHeight, nTxtHeight );
+ }
+ }
+ else if ( rLSItem.GetLineSpaceRule() == SvxLineSpaceRule::Fix )
+ {
+ double fFixHeight = scaleYSpacingValue(rLSItem.GetLineHeight());
+ sal_uInt16 nFixHeight = basegfx::fround(fFixHeight);
+
+ sal_uInt16 nTxtHeight = pLine->GetHeight();
+ pLine->SetMaxAscent( static_cast<sal_uInt16>(pLine->GetMaxAscent() + ( nFixHeight - nTxtHeight ) ) );
+ pLine->SetHeight( nFixHeight, nTxtHeight );
+ }
+ else if ( rLSItem.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Prop )
+ {
+ // There are documents with PropLineSpace 0, why?
+ // (cmc: re above question :-) such documents can be seen by importing a .ppt
+ sal_uInt16 nPropLineSpace = rLSItem.GetPropLineSpace();
+ double fProportionalScale = double(nPropLineSpace) / 100.0;
+ constexpr const double f80Percent = 8.0 / 10.0;
+ double fSpacingFactor = mfSpacingScaleY / 100.0;
+ if (nPropLineSpace && nPropLineSpace < 100)
+ {
+ // Adapted code from sw/source/core/text/itrform2.cxx
+ sal_uInt16 nAscent = pLine->GetMaxAscent();
+ sal_uInt16 nNewAscent = basegfx::fround(pLine->GetTxtHeight() * fSpacingFactor * fProportionalScale * f80Percent);
+ if (!nAscent || nAscent > nNewAscent)
+ pLine->SetMaxAscent(nNewAscent);
+ sal_uInt16 nHeight = basegfx::fround(pLine->GetHeight() * fProportionalScale * fSpacingFactor);
+
+ pLine->SetHeight(nHeight, pLine->GetTxtHeight());
+ }
+ else if (nPropLineSpace && nPropLineSpace != 100)
+ {
+ sal_uInt16 nTxtHeight = pLine->GetHeight();
+ sal_Int32 nPropTextHeight = nTxtHeight * fProportionalScale * fSpacingFactor;
+ // The Ascent has to be adjusted for the difference:
+ tools::Long nDiff = pLine->GetHeight() - nPropTextHeight;
+ pLine->SetMaxAscent( static_cast<sal_uInt16>( pLine->GetMaxAscent() - nDiff ) );
+ pLine->SetHeight( static_cast<sal_uInt16>( nPropTextHeight ), nTxtHeight );
+ }
+ }
+ else if (rLSItem.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Off)
+ {
+ if (mfSpacingScaleY < 100.0)
+ {
+ double fSpacingFactor = mfSpacingScaleY / 100.0;
+ sal_uInt16 nPropLineSpace = basegfx::fround(100.0 * fSpacingFactor);
+ if (nPropLineSpace && nPropLineSpace < 100)
+ {
+ // Adapted code from sw/source/core/text/itrform2.cxx
+ sal_uInt16 nAscent = pLine->GetMaxAscent();
+ sal_uInt16 nNewAscent = basegfx::fround(pLine->GetTxtHeight() * fSpacingFactor);
+ if (!nAscent || nAscent > nNewAscent)
+ pLine->SetMaxAscent(nNewAscent);
+ sal_uInt16 nHeight = basegfx::fround(pLine->GetHeight() * fSpacingFactor);
+
+ pLine->SetHeight(nHeight, pLine->GetTxtHeight());
+ }
+
+ }
+ }
+ }
+
+ if ( ( !IsEffectivelyVertical() && maStatus.AutoPageWidth() ) ||
+ ( IsEffectivelyVertical() && maStatus.AutoPageHeight() ) )
+ {
+ // If the row fits within the current paper width, then this width
+ // has to be used for the Alignment. If it does not fit or if it
+ // will change the paper width, it will be formatted again for
+ // Justification! = LEFT anyway.
+ tools::Long nMaxLineWidthFix = GetColumnWidth(maPaperSize) - scaleXSpacingValue(rLRItem.GetRight()) - nStartX;
+ if ( aTextSize.Width() < nMaxLineWidthFix )
+ nMaxLineWidth = nMaxLineWidthFix;
+ }
+
+ if ( bCompressedChars )
+ {
+ tools::Long nRemainingWidth = nMaxLineWidth - aTextSize.Width();
+ if ( nRemainingWidth > 0 )
+ {
+ ImplExpandCompressedPortions( pLine, pParaPortion, nRemainingWidth );
+ aTextSize = pLine->CalcTextSize( *pParaPortion );
+ }
+ }
+
+ if ( pLine->IsHangingPunctuation() )
+ {
+ // Width from HangingPunctuation was set to 0 in ImpBreakLine,
+ // check for rel width now, maybe create compression...
+ tools::Long n = nMaxLineWidth - aTextSize.Width();
+ TextPortion& rTP = pParaPortion->GetTextPortions()[pLine->GetEndPortion()];
+ sal_Int32 nPosInArray = pLine->GetEnd()-1-pLine->GetStart();
+ tools::Long nNewValue = ( nPosInArray ? pLine->GetCharPosArray()[ nPosInArray-1 ] : 0 ) + n;
+ if (o3tl::make_unsigned(nPosInArray) < pLine->GetCharPosArray().size())
+ {
+ pLine->GetCharPosArray()[ nPosInArray ] = nNewValue;
+ }
+ rTP.adjustSize(n, 0);
+ }
+
+ pLine->SetTextWidth( aTextSize.Width() );
+ switch ( eJustification )
+ {
+ case SvxAdjust::Center:
+ {
+ tools::Long n = ( nMaxLineWidth - aTextSize.Width() ) / 2;
+ n += nStartX; // Indentation is kept.
+ pLine->SetStartPosX( n );
+ }
+ break;
+ case SvxAdjust::Right:
+ {
+ // For automatically wrapped lines, which has a blank at the end
+ // the blank must not be displayed!
+ tools::Long n = nMaxLineWidth - aTextSize.Width();
+ n += nStartX; // Indentation is kept.
+ pLine->SetStartPosX( n );
+ }
+ break;
+ case SvxAdjust::Block:
+ {
+ bool bDistLastLine = (GetJustifyMethod(nPara) == SvxCellJustifyMethod::Distribute);
+ tools::Long nRemainingSpace = nMaxLineWidth - aTextSize.Width();
+ pLine->SetStartPosX( nStartX );
+ if ( nRemainingSpace > 0 && (!bEOC || bDistLastLine) )
+ ImpAdjustBlocks( pParaPortion, pLine, nRemainingSpace );
+ }
+ break;
+ default:
+ {
+ pLine->SetStartPosX( nStartX ); // FI, LI
+ }
+ break;
+ }
+
+
+ // Check whether the line must be re-issued...
+
+ pLine->SetInvalid();
+
+ // If a portion was wrapped there may be far too many positions in
+ // CharPosArray:
+ EditLine::CharPosArrayType& rArray = pLine->GetCharPosArray();
+ size_t nLen = pLine->GetLen();
+ if (rArray.size() > nLen)
+ rArray.erase(rArray.begin()+nLen, rArray.end());
+
+ if ( GetTextRanger() )
+ {
+ if ( nTextXOffset )
+ pLine->SetStartPosX( pLine->GetStartPosX() + nTextXOffset );
+ if ( nTextExtraYOffset )
+ {
+ pLine->SetHeight( static_cast<sal_uInt16>( pLine->GetHeight() + nTextExtraYOffset ), 0 );
+ pLine->SetMaxAscent( static_cast<sal_uInt16>( pLine->GetMaxAscent() + nTextExtraYOffset ) );
+ }
+ }
+
+ // for <0 think over !
+ if ( pParaPortion->IsSimpleInvalid() )
+ {
+ // Change through simple Text changes...
+ // Do not cancel formatting since Portions possibly have to be split
+ // again! If at some point cancelable, then validate the following
+ // line! But if applicable, mark as valid, so there is less output...
+ if ( pLine->GetEnd() < nInvalidStart )
+ {
+ if ( *pLine == aSaveLine )
+ {
+ pLine->SetValid();
+ }
+ }
+ else
+ {
+ sal_Int32 nStart = pLine->GetStart();
+ sal_Int32 nEnd = pLine->GetEnd();
+
+ if ( nStart > nInvalidEnd )
+ {
+ if ( ( ( nStart-nInvalidDiff ) == aSaveLine.GetStart() ) &&
+ ( ( nEnd-nInvalidDiff ) == aSaveLine.GetEnd() ) )
+ {
+ pLine->SetValid();
+ if (bQuickFormat)
+ {
+ bLineBreak = false;
+ pParaPortion->CorrectValuesBehindLastFormattedLine( nLine );
+ break;
+ }
+ }
+ }
+ else if (bQuickFormat && (nEnd > nInvalidEnd))
+ {
+ // If the invalid line ends so that the next begins on the
+ // 'same' passage as before, i.e. not wrapped differently,
+ // then the text width does not have to be determined anew:
+ if ( nEnd == ( aSaveLine.GetEnd() + nInvalidDiff ) )
+ {
+ bLineBreak = false;
+ pParaPortion->CorrectValuesBehindLastFormattedLine( nLine );
+ break;
+ }
+ }
+ }
+ }
+
+ if ( !bSameLineAgain )
+ {
+ nIndex = pLine->GetEnd(); // next line start = last line end
+ // as nEnd points to the last character!
+
+ sal_Int32 nEndPortion = pLine->GetEndPortion();
+ nCurrentPosY += pLine->GetHeight();
+
+ // Next line or maybe a new line...
+ pLine = nullptr;
+ if ( nLine < pParaPortion->GetLines().Count()-1 )
+ pLine = &pParaPortion->GetLines()[++nLine];
+ if ( pLine && ( nIndex >= pNode->Len() ) )
+ {
+ nDelFromLine = nLine;
+ break;
+ }
+ // Stop processing if allowed and this is outside of the paper size height.
+ // Format at least two lines though, in case something detects whether
+ // the text has been wrapped or something similar.
+ if( mbSkipOutsideFormat && nLine > 2
+ && !maStatus.AutoPageHeight() && maPaperSize.Height() < nCurrentPosY )
+ {
+ if ( pLine && ( nIndex >= pNode->Len()) )
+ nDelFromLine = nLine;
+ break;
+ }
+ if ( !pLine )
+ {
+ if ( nIndex < pNode->Len() )
+ {
+ pLine = new EditLine;
+ pParaPortion->GetLines().Insert(++nLine, pLine);
+ }
+ else if ( nIndex && bLineBreak && GetTextRanger() )
+ {
+ // normally CreateAndInsertEmptyLine would be called, but I want to use
+ // CreateLines, so I need Polygon code only here...
+ TextPortion* pDummyPortion = new TextPortion( 0 );
+ pParaPortion->GetTextPortions().Append(pDummyPortion);
+ pLine = new EditLine;
+ pParaPortion->GetLines().Insert(++nLine, pLine);
+ bForceOneRun = true;
+ bProcessingEmptyLine = true;
+ }
+ }
+ if ( pLine )
+ {
+ aSaveLine = *pLine;
+ pLine->SetStart( nIndex );
+ pLine->SetEnd( nIndex );
+ pLine->SetStartPortion( nEndPortion+1 );
+ pLine->SetEndPortion( nEndPortion+1 );
+ }
+ }
+ } // while ( Index < Len )
+
+ if ( nDelFromLine >= 0 )
+ pParaPortion->GetLines().DeleteFromLine( nDelFromLine );
+
+ DBG_ASSERT( pParaPortion->GetLines().Count(), "No line after CreateLines!" );
+
+ if ( bLineBreak )
+ CreateAndInsertEmptyLine( pParaPortion );
+
+ bool bHeightChanged = FinishCreateLines( pParaPortion );
+
+ if ( bMapChanged )
+ GetRefDevice()->Pop();
+
+ GetRefDevice()->Pop();
+
+ return bHeightChanged;
+}
+
+void ImpEditEngine::CreateAndInsertEmptyLine( ParaPortion* pParaPortion )
+{
+ DBG_ASSERT( !GetTextRanger(), "Don't use CreateAndInsertEmptyLine with a polygon!" );
+
+ EditLine* pTmpLine = new EditLine;
+ pTmpLine->SetStart( pParaPortion->GetNode()->Len() );
+ pTmpLine->SetEnd( pParaPortion->GetNode()->Len() );
+ pParaPortion->GetLines().Append(pTmpLine);
+
+ bool bLineBreak = pParaPortion->GetNode()->Len() > 0;
+ sal_Int32 nSpaceBefore = 0;
+ sal_Int32 nSpaceBeforeAndMinLabelWidth = GetSpaceBeforeAndMinLabelWidth( pParaPortion->GetNode(), &nSpaceBefore );
+ const SvxLRSpaceItem& rLRItem = GetLRSpaceItem( pParaPortion->GetNode() );
+ const SvxLineSpacingItem& rLSItem = pParaPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
+ tools::Long nStartX = scaleXSpacingValue(rLRItem.GetTextLeft() + rLRItem.GetTextFirstLineOffset() + nSpaceBefore);
+
+ tools::Rectangle aBulletArea { Point(), Point() };
+ if ( bLineBreak )
+ {
+ nStartX = scaleXSpacingValue(rLRItem.GetTextLeft() + rLRItem.GetTextFirstLineOffset() + nSpaceBeforeAndMinLabelWidth);
+ }
+ else
+ {
+ aBulletArea = GetEditEnginePtr()->GetBulletArea( GetParaPortions().GetPos( pParaPortion ) );
+ if ( !aBulletArea.IsEmpty() && aBulletArea.Right() > 0 )
+ pParaPortion->SetBulletX(sal_Int32(scaleXSpacingValue(aBulletArea.Right())));
+ else
+ pParaPortion->SetBulletX( 0 ); // If Bullet set incorrectly.
+ if ( pParaPortion->GetBulletX() > nStartX )
+ {
+ nStartX = scaleXSpacingValue(rLRItem.GetTextLeft() + rLRItem.GetTextFirstLineOffset() + nSpaceBeforeAndMinLabelWidth);
+ if ( pParaPortion->GetBulletX() > nStartX )
+ nStartX = pParaPortion->GetBulletX();
+ }
+ }
+
+ SvxFont aTmpFont;
+ SeekCursor( pParaPortion->GetNode(), bLineBreak ? pParaPortion->GetNode()->Len() : 0, aTmpFont );
+ aTmpFont.SetPhysFont(*pRefDev);
+
+ TextPortion* pDummyPortion = new TextPortion( 0 );
+ pDummyPortion->SetSize(aTmpFont.GetPhysTxtSize(pRefDev));
+ if ( IsFixedCellHeight() )
+ pDummyPortion->setHeight( ImplCalculateFontIndependentLineSpacing( aTmpFont.GetFontHeight() ) );
+ pParaPortion->GetTextPortions().Append(pDummyPortion);
+ FormatterFontMetric aFormatterMetrics;
+ RecalcFormatterFontMetrics( aFormatterMetrics, aTmpFont );
+ pTmpLine->SetMaxAscent( aFormatterMetrics.nMaxAscent );
+ pTmpLine->SetHeight( static_cast<sal_uInt16>(pDummyPortion->GetSize().Height()) );
+ sal_uInt16 nLineHeight = aFormatterMetrics.GetHeight();
+ if ( nLineHeight > pTmpLine->GetHeight() )
+ pTmpLine->SetHeight( nLineHeight );
+
+ if (!maStatus.IsOutliner())
+ {
+ sal_Int32 nPara = GetParaPortions().GetPos( pParaPortion );
+ SvxAdjust eJustification = GetJustification( nPara );
+ tools::Long nMaxLineWidth = GetColumnWidth(maPaperSize);
+ nMaxLineWidth -= scaleXSpacingValue(rLRItem.GetRight());
+ if ( nMaxLineWidth < 0 )
+ nMaxLineWidth = 1;
+ if ( eJustification == SvxAdjust::Center )
+ nStartX = nMaxLineWidth / 2;
+ else if ( eJustification == SvxAdjust::Right )
+ nStartX = nMaxLineWidth;
+ }
+
+ pTmpLine->SetStartPosX( nStartX );
+
+ if (!maStatus.IsOutliner())
+ {
+ if ( rLSItem.GetLineSpaceRule() == SvxLineSpaceRule::Min )
+ {
+ sal_uInt16 nMinHeight = rLSItem.GetLineHeight();
+ sal_uInt16 nTxtHeight = pTmpLine->GetHeight();
+ if ( nTxtHeight < nMinHeight )
+ {
+ // The Ascent has to be adjusted for the difference:
+ tools::Long nDiff = nMinHeight - nTxtHeight;
+ pTmpLine->SetMaxAscent( static_cast<sal_uInt16>(pTmpLine->GetMaxAscent() + nDiff) );
+ pTmpLine->SetHeight( nMinHeight, nTxtHeight );
+ }
+ }
+ else if ( rLSItem.GetLineSpaceRule() == SvxLineSpaceRule::Fix )
+ {
+ sal_uInt16 nFixHeight = rLSItem.GetLineHeight();
+ sal_uInt16 nTxtHeight = pTmpLine->GetHeight();
+
+ pTmpLine->SetMaxAscent( static_cast<sal_uInt16>(pTmpLine->GetMaxAscent() + ( nFixHeight - nTxtHeight ) ) );
+ pTmpLine->SetHeight( nFixHeight, nTxtHeight );
+ }
+ else if ( rLSItem.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Prop )
+ {
+ sal_Int32 nPara = GetParaPortions().GetPos( pParaPortion );
+ if ( nPara || pTmpLine->GetStartPortion() ) // Not the very first line
+ {
+ // There are documents with PropLineSpace 0, why?
+ // (cmc: re above question :-) such documents can be seen by importing a .ppt
+ if ( rLSItem.GetPropLineSpace() && ( rLSItem.GetPropLineSpace() != 100 ) )
+ {
+ sal_uInt16 nTxtHeight = pTmpLine->GetHeight();
+ sal_Int32 nH = nTxtHeight;
+ nH *= rLSItem.GetPropLineSpace();
+ nH /= 100;
+ // The Ascent has to be adjusted for the difference:
+ tools::Long nDiff = pTmpLine->GetHeight() - nH;
+ if ( nDiff > pTmpLine->GetMaxAscent() )
+ nDiff = pTmpLine->GetMaxAscent();
+ pTmpLine->SetMaxAscent( static_cast<sal_uInt16>(pTmpLine->GetMaxAscent() - nDiff) );
+ pTmpLine->SetHeight( static_cast<sal_uInt16>(nH), nTxtHeight );
+ }
+ }
+ }
+ }
+
+ if ( !bLineBreak )
+ {
+ tools::Long nMinHeight = aBulletArea.GetHeight();
+ if ( nMinHeight > static_cast<tools::Long>(pTmpLine->GetHeight()) )
+ {
+ tools::Long nDiff = nMinHeight - static_cast<tools::Long>(pTmpLine->GetHeight());
+ // distribute nDiff upwards and downwards
+ pTmpLine->SetMaxAscent( static_cast<sal_uInt16>(pTmpLine->GetMaxAscent() + nDiff/2) );
+ pTmpLine->SetHeight( static_cast<sal_uInt16>(nMinHeight) );
+ }
+ }
+ else
+ {
+ // -2: The new one is already inserted.
+#ifdef DBG_UTIL
+ EditLine& rLastLine = pParaPortion->GetLines()[pParaPortion->GetLines().Count()-2];
+ DBG_ASSERT( rLastLine.GetEnd() == pParaPortion->GetNode()->Len(), "different anyway?" );
+#endif
+ sal_Int32 nPos = pParaPortion->GetTextPortions().Count() - 1 ;
+ pTmpLine->SetStartPortion( nPos );
+ pTmpLine->SetEndPortion( nPos );
+ }
+}
+
+bool ImpEditEngine::FinishCreateLines( ParaPortion* pParaPortion )
+{
+// CalcCharPositions( pParaPortion );
+ pParaPortion->SetValid();
+ tools::Long nOldHeight = pParaPortion->GetHeight();
+ CalcHeight( pParaPortion );
+
+ DBG_ASSERT( pParaPortion->GetTextPortions().Count(), "FinishCreateLines: No Text-Portion?" );
+ bool bRet = ( pParaPortion->GetHeight() != nOldHeight );
+ return bRet;
+}
+
+void ImpEditEngine::ImpBreakLine( ParaPortion* pParaPortion, EditLine* pLine, TextPortion const * pPortion, sal_Int32 nPortionStart, tools::Long nRemainingWidth, bool bCanHyphenate )
+{
+ ContentNode* const pNode = pParaPortion->GetNode();
+
+ sal_Int32 nBreakInLine = nPortionStart - pLine->GetStart();
+ sal_Int32 nMax = nBreakInLine + pPortion->GetLen();
+ while ( ( nBreakInLine < nMax ) && ( pLine->GetCharPosArray()[nBreakInLine] < nRemainingWidth ) )
+ nBreakInLine++;
+
+ sal_Int32 nMaxBreakPos = nBreakInLine + pLine->GetStart();
+ sal_Int32 nBreakPos = SAL_MAX_INT32;
+
+ bool bCompressBlank = false;
+ bool bHyphenated = false;
+ bool bHangingPunctuation = false;
+ sal_Unicode cAlternateReplChar = 0;
+ sal_Unicode cAlternateExtraChar = 0;
+ bool bAltFullLeft = false;
+ bool bAltFullRight = false;
+ sal_uInt32 nAltDelChar = 0;
+
+ if ( ( nMaxBreakPos < ( nMax + pLine->GetStart() ) ) && ( pNode->GetChar( nMaxBreakPos ) == ' ' ) )
+ {
+ // Break behind the blank, blank will be compressed...
+ nBreakPos = nMaxBreakPos + 1;
+ bCompressBlank = true;
+ }
+ else
+ {
+ sal_Int32 nMinBreakPos = pLine->GetStart();
+ const CharAttribList::AttribsType& rAttrs = pNode->GetCharAttribs().GetAttribs();
+ for (size_t nAttr = rAttrs.size(); nAttr; )
+ {
+ const EditCharAttrib& rAttr = *rAttrs[--nAttr];
+ if (rAttr.IsFeature() && rAttr.GetEnd() > nMinBreakPos && rAttr.GetEnd() <= nMaxBreakPos)
+ {
+ nMinBreakPos = rAttr.GetEnd();
+ break;
+ }
+ }
+ assert(nMinBreakPos <= nMaxBreakPos);
+
+ lang::Locale aLocale = GetLocale( EditPaM( pNode, nMaxBreakPos ) );
+
+ Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
+ const bool bAllowPunctuationOutsideMargin = static_cast<const SfxBoolItem&>(
+ pNode->GetContentAttribs().GetItem( EE_PARA_HANGINGPUNCTUATION )).GetValue();
+
+ if (nMinBreakPos == nMaxBreakPos)
+ {
+ nBreakPos = nMinBreakPos;
+ }
+ else
+ {
+ Reference< XHyphenator > xHyph;
+ if ( bCanHyphenate )
+ xHyph = GetHyphenator();
+ i18n::LineBreakHyphenationOptions aHyphOptions( xHyph, Sequence< PropertyValue >(), 1 );
+ i18n::LineBreakUserOptions aUserOptions;
+
+ const i18n::ForbiddenCharacters* pForbidden = GetForbiddenCharsTable()->GetForbiddenCharacters( LanguageTag::convertToLanguageType( aLocale ), true );
+ aUserOptions.forbiddenBeginCharacters = pForbidden->beginLine;
+ aUserOptions.forbiddenEndCharacters = pForbidden->endLine;
+ aUserOptions.applyForbiddenRules = static_cast<const SfxBoolItem&>(pNode->GetContentAttribs().GetItem( EE_PARA_FORBIDDENRULES )).GetValue();
+ aUserOptions.allowPunctuationOutsideMargin = bAllowPunctuationOutsideMargin;
+ aUserOptions.allowHyphenateEnglish = false;
+
+ if (!maStatus.IsSingleLine())
+ {
+ i18n::LineBreakResults aLBR = _xBI->getLineBreak(
+ pNode->GetString(), nMaxBreakPos, aLocale, nMinBreakPos, aHyphOptions, aUserOptions );
+ nBreakPos = aLBR.breakIndex;
+
+ // show soft hyphen
+ if ( nBreakPos && CH_SOFTHYPHEN == pNode->GetString()[ sal_Int32(nBreakPos) - 1 ] )
+ bHyphenated = true;
+ }
+ else
+ {
+ nBreakPos = nMaxBreakPos;
+ }
+
+ // BUG in I18N - under special condition (break behind field, #87327#) breakIndex is < nMinBreakPos
+ if ( nBreakPos < nMinBreakPos )
+ {
+ nBreakPos = nMinBreakPos;
+ }
+ else if ( ( nBreakPos > nMaxBreakPos ) && !aUserOptions.allowPunctuationOutsideMargin )
+ {
+ OSL_FAIL( "I18N: XBreakIterator::getLineBreak returns position > Max" );
+ nBreakPos = nMaxBreakPos;
+ }
+ // Hanging punctuation is the only case that increases nBreakPos and makes
+ // nBreakPos > nMaxBreakPos. It's expected that the hanging punctuation goes over
+ // the border of the object.
+ }
+
+ // BUG in I18N - the japanese dot is in the next line!
+ // !!! Test!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ if ( (nBreakPos + ( bAllowPunctuationOutsideMargin ? 0 : 1 ) ) <= nMaxBreakPos )
+ {
+ sal_Unicode cFirstInNextLine = ( (nBreakPos+1) < pNode->Len() ) ? pNode->GetChar( nBreakPos ) : 0;
+ if ( cFirstInNextLine == 12290 )
+ nBreakPos++;
+ }
+
+ bHangingPunctuation = nBreakPos > nMaxBreakPos;
+ pLine->SetHangingPunctuation( bHangingPunctuation );
+
+ // Whether a separator or not, push the word after the separator through
+ // hyphenation... NMaxBreakPos is the last character that fits into
+ // the line, nBreakPos is the beginning of the word.
+ // There is a problem if the Doc is so narrow that a word is broken
+ // into more than two lines...
+ if ( !bHangingPunctuation && bCanHyphenate && GetHyphenator().is() )
+ {
+ i18n::Boundary aBoundary = _xBI->getWordBoundary(
+ pNode->GetString(), nBreakPos, GetLocale( EditPaM( pNode, nBreakPos ) ), css::i18n::WordType::DICTIONARY_WORD, true);
+ sal_Int32 nWordStart = nBreakPos;
+ sal_Int32 nWordEnd = aBoundary.endPos;
+ DBG_ASSERT( nWordEnd >= nWordStart, "Start >= End?" );
+
+ sal_Int32 nWordLen = nWordEnd - nWordStart;
+ if ( ( nWordEnd >= nMaxBreakPos ) && ( nWordLen > 3 ) )
+ {
+ // May happen, because getLineBreak may differ from getWordBoundary with DICTIONARY_WORD
+ const OUString aWord = pNode->GetString().copy(nWordStart, nWordLen);
+ sal_Int32 nMinTrail = nWordEnd-nMaxBreakPos+1; //+1: Before the dickey letter
+ Reference< XHyphenatedWord > xHyphWord;
+ if (xHyphenator.is())
+ xHyphWord = xHyphenator->hyphenate( aWord, aLocale, aWord.getLength() - nMinTrail, Sequence< PropertyValue >() );
+ if (xHyphWord.is())
+ {
+ bool bAlternate = xHyphWord->isAlternativeSpelling();
+ sal_Int32 _nWordLen = 1 + xHyphWord->getHyphenPos();
+
+ if ( ( _nWordLen >= 2 ) && ( (nWordStart+_nWordLen) >= (pLine->GetStart() + 2 ) ) )
+ {
+ if ( !bAlternate )
+ {
+ bHyphenated = true;
+ nBreakPos = nWordStart + _nWordLen;
+ }
+ else
+ {
+ // TODO: handle all alternative hyphenations (see hyphen-1.2.8/tests/unicode.*)
+ OUString aAlt( xHyphWord->getHyphenatedWord() );
+ std::u16string_view aAltLeft(aAlt.subView(0, _nWordLen));
+ std::u16string_view aAltRight(aAlt.subView(_nWordLen));
+ bAltFullLeft = aWord.startsWith(aAltLeft);
+ bAltFullRight = aWord.endsWith(aAltRight);
+ nAltDelChar = aWord.getLength() - aAlt.getLength() + static_cast<int>(!bAltFullLeft) + static_cast<int>(!bAltFullRight);
+
+ // NOTE: improved for other cases, see fdo#63711
+
+ // We expect[ed] the two cases:
+ // 1) packen becomes pak-ken
+ // 2) Schiffahrt becomes Schiff-fahrt
+ // In case 1, a character has to be replaced
+ // in case 2 a character is added.
+ // The identification is complicated by long
+ // compound words because the Hyphenator separates
+ // all position of the word. [This is not true for libhyphen.]
+ // "Schiffahrtsbrennesseln" -> "Schifffahrtsbrennnesseln"
+ // We can thus actually not directly connect the index of the
+ // AlternativeWord to aWord. The whole issue will be simplified
+ // by a function in the Hyphenator as soon as AMA builds this in...
+ sal_Int32 nAltStart = _nWordLen - 1;
+ sal_Int32 nTxtStart = nAltStart - (aAlt.getLength() - aWord.getLength());
+ sal_Int32 nTxtEnd = nTxtStart;
+ sal_Int32 nAltEnd = nAltStart;
+
+ // The regions between the nStart and nEnd is the
+ // difference between alternative and original string.
+ while( nTxtEnd < aWord.getLength() && nAltEnd < aAlt.getLength() &&
+ aWord[nTxtEnd] != aAlt[nAltEnd] )
+ {
+ ++nTxtEnd;
+ ++nAltEnd;
+ }
+
+ // If a character is added, then we notice it now:
+ if( nAltEnd > nTxtEnd && nAltStart == nAltEnd &&
+ aWord[ nTxtEnd ] == aAlt[nAltEnd] )
+ {
+ ++nAltEnd;
+ ++nTxtStart;
+ ++nTxtEnd;
+ }
+
+ DBG_ASSERT( ( nAltEnd - nAltStart ) == 1, "Alternate: Wrong assumption!" );
+
+ if ( nTxtEnd > nTxtStart )
+ cAlternateReplChar = aAlt[nAltStart];
+ else
+ cAlternateExtraChar = aAlt[nAltStart];
+
+ bHyphenated = true;
+ nBreakPos = nWordStart + nTxtStart;
+ if ( cAlternateReplChar || aAlt.getLength() < aWord.getLength() || !bAltFullRight) // also for "oma-tje", "re-eel"
+ nBreakPos++;
+ }
+ }
+ }
+ }
+ }
+
+ if ( nBreakPos <= pLine->GetStart() )
+ {
+ // No separator in line => Chop!
+ nBreakPos = nMaxBreakPos;
+ // I18N nextCharacters !
+ if ( nBreakPos <= pLine->GetStart() )
+ nBreakPos = pLine->GetStart() + 1; // Otherwise infinite loop!
+ }
+ }
+
+ // the dickey portion is the end portion
+ pLine->SetEnd( nBreakPos );
+
+ sal_Int32 nEndPortion = SplitTextPortion( pParaPortion, nBreakPos, pLine );
+
+ if ( !bCompressBlank && !bHangingPunctuation )
+ {
+ // When justification is not SvxAdjust::Left, it's important to compress
+ // the trailing space even if there is enough room for the space...
+ // Don't check for SvxAdjust::Left, doesn't matter to compress in this case too...
+ assert( nBreakPos > pLine->GetStart() && "ImpBreakLines - BreakPos not expected!" );
+ if ( pNode->GetChar( nBreakPos-1 ) == ' ' )
+ bCompressBlank = true;
+ }
+
+ if ( bCompressBlank || bHangingPunctuation )
+ {
+ TextPortion& rTP = pParaPortion->GetTextPortions()[nEndPortion];
+ DBG_ASSERT( rTP.GetKind() == PortionKind::TEXT, "BlankRubber: No TextPortion!" );
+ DBG_ASSERT( nBreakPos > pLine->GetStart(), "SplitTextPortion at the beginning of the line?" );
+ sal_Int32 nPosInArray = nBreakPos - 1 - pLine->GetStart();
+ rTP.setWidth( ( nPosInArray && ( rTP.GetLen() > 1 ) ) ? pLine->GetCharPosArray()[ nPosInArray-1 ] : 0 );
+ if (o3tl::make_unsigned(nPosInArray) < pLine->GetCharPosArray().size())
+ {
+ pLine->GetCharPosArray()[ nPosInArray ] = rTP.GetSize().Width();
+ }
+ }
+ else if ( bHyphenated )
+ {
+ // A portion for inserting the separator...
+ TextPortion* pHyphPortion = new TextPortion( 0 );
+ pHyphPortion->SetKind( PortionKind::HYPHENATOR );
+ if ( (cAlternateReplChar || cAlternateExtraChar) && bAltFullRight ) // alternation after the break doesn't supported
+ {
+ TextPortion& rPrev = pParaPortion->GetTextPortions()[nEndPortion];
+ DBG_ASSERT( rPrev.GetLen(), "Hyphenate: Prev portion?!" );
+ rPrev.SetLen( rPrev.GetLen() - nAltDelChar );
+ pHyphPortion->SetLen( nAltDelChar );
+ if (cAlternateReplChar && !bAltFullLeft) pHyphPortion->SetExtraValue( cAlternateReplChar );
+ // Correct width of the portion above:
+ rPrev.setWidth(
+ pLine->GetCharPosArray()[ nBreakPos-1 - pLine->GetStart() - nAltDelChar ] );
+ }
+
+ // Determine the width of the Hyph-Portion:
+ SvxFont aFont;
+ SeekCursor( pParaPortion->GetNode(), nBreakPos, aFont );
+ aFont.SetPhysFont(*GetRefDevice());
+ pHyphPortion->SetSize(Size(GetRefDevice()->GetTextWidth(CH_HYPH), GetRefDevice()->GetTextHeight()));
+
+ pParaPortion->GetTextPortions().Insert(++nEndPortion, pHyphPortion);
+ }
+ pLine->SetEndPortion( nEndPortion );
+}
+
+void ImpEditEngine::ImpAdjustBlocks( ParaPortion* pParaPortion, EditLine* pLine, tools::Long nRemainingSpace )
+{
+ DBG_ASSERT( nRemainingSpace > 0, "AdjustBlocks: Somewhat too little..." );
+ assert( pLine && "AdjustBlocks: Line ?!" );
+ if ( ( nRemainingSpace < 0 ) || pLine->IsEmpty() )
+ return ;
+
+ const sal_Int32 nFirstChar = pLine->GetStart();
+ const sal_Int32 nLastChar = pLine->GetEnd() -1; // Last points behind
+ ContentNode* pNode = pParaPortion->GetNode();
+
+ DBG_ASSERT( nLastChar < pNode->Len(), "AdjustBlocks: Out of range!" );
+
+ // Search blanks or Kashidas...
+ std::vector<sal_Int32> aPositions;
+
+ // Kashidas ?
+ ImpFindKashidas( pNode, nFirstChar, nLastChar, aPositions );
+ auto nKashidas = aPositions.size();
+
+ sal_uInt16 nLastScript = i18n::ScriptType::LATIN;
+ for ( sal_Int32 nChar = nFirstChar; nChar <= nLastChar; nChar++ )
+ {
+ EditPaM aPaM( pNode, nChar+1 );
+ LanguageType eLang = GetLanguage(aPaM).nLang;
+ sal_uInt16 nScript = GetI18NScriptType(aPaM);
+ // Arabic script is handled above, but if no Kashida positions are found, use blanks.
+ if (MsLangId::getPrimaryLanguage(eLang) == LANGUAGE_ARABIC_PRIMARY_ONLY && nKashidas)
+ continue;
+
+ if ( pNode->GetChar(nChar) == ' ' )
+ {
+ // Normal latin script.
+ aPositions.push_back( nChar );
+ }
+ else if (nChar > nFirstChar)
+ {
+ if (nLastScript == i18n::ScriptType::ASIAN)
+ {
+ // Set break position between this and the last character if
+ // the last character is asian script.
+ aPositions.push_back( nChar-1 );
+ }
+ else if (nScript == i18n::ScriptType::ASIAN)
+ {
+ // Set break position between a latin script and asian script.
+ aPositions.push_back( nChar-1 );
+ }
+ }
+
+ nLastScript = nScript;
+ }
+
+ if ( aPositions.empty() )
+ return;
+
+ // If the last character is a blank, it is rejected!
+ // The width must be distributed to the blockers in front...
+ // But not if it is the only one.
+ if ( ( pNode->GetChar( nLastChar ) == ' ' ) && ( aPositions.size() > 1 ) &&
+ ( MsLangId::getPrimaryLanguage( GetLanguage( EditPaM( pNode, nLastChar ) ).nLang ) != LANGUAGE_ARABIC_PRIMARY_ONLY ) )
+ {
+ aPositions.pop_back();
+ sal_Int32 nPortionStart, nPortion;
+ nPortion = pParaPortion->GetTextPortions().FindPortion( nLastChar+1, nPortionStart );
+ TextPortion& rLastPortion = pParaPortion->GetTextPortions()[ nPortion ];
+ tools::Long nRealWidth = pLine->GetCharPosArray()[nLastChar-nFirstChar];
+ tools::Long nBlankWidth = nRealWidth;
+ if ( nLastChar > nPortionStart )
+ nBlankWidth -= pLine->GetCharPosArray()[nLastChar-nFirstChar-1];
+ // Possibly the blank has already been deducted in ImpBreakLine:
+ if ( nRealWidth == rLastPortion.GetSize().Width() )
+ {
+ // For the last character the portion must stop behind the blank
+ // => Simplify correction:
+ DBG_ASSERT( ( nPortionStart + rLastPortion.GetLen() ) == ( nLastChar+1 ), "Blank actually not at the end of the portion!?");
+ rLastPortion.adjustSize(-nBlankWidth, 0);
+ nRemainingSpace += nBlankWidth;
+ }
+ pLine->GetCharPosArray()[nLastChar-nFirstChar] -= nBlankWidth;
+ }
+
+ size_t nGaps = aPositions.size();
+ const tools::Long nMore4Everyone = nRemainingSpace / nGaps;
+ tools::Long nSomeExtraSpace = nRemainingSpace - nMore4Everyone*nGaps;
+
+ DBG_ASSERT( nSomeExtraSpace < static_cast<tools::Long>(nGaps), "AdjustBlocks: ExtraSpace too large" );
+ DBG_ASSERT( nSomeExtraSpace >= 0, "AdjustBlocks: ExtraSpace < 0 " );
+
+ // Mark Kashida positions, so that VCL knows where to insert Kashida and
+ // where to only expand the width.
+ if (nKashidas)
+ {
+ pLine->GetKashidaArray().resize(pLine->GetCharPosArray().size(), false);
+ for (size_t i = 0; i < nKashidas; i++)
+ {
+ auto nChar = aPositions[i];
+ if ( nChar < nLastChar )
+ pLine->GetKashidaArray()[nChar-nFirstChar] = 1 /*sal_True*/;
+ }
+ }
+
+ // Correct the positions in the Array and the portion widths:
+ // Last character won't be considered...
+ for (auto const& nChar : aPositions)
+ {
+ if ( nChar < nLastChar )
+ {
+ sal_Int32 nPortionStart, nPortion;
+ nPortion = pParaPortion->GetTextPortions().FindPortion( nChar, nPortionStart, true );
+ TextPortion& rLastPortion = pParaPortion->GetTextPortions()[ nPortion ];
+
+ // The width of the portion:
+ rLastPortion.adjustSize(nMore4Everyone, 0);
+ if (nSomeExtraSpace)
+ {
+ rLastPortion.adjustSize(1, 0);
+ }
+
+ // Correct positions in array
+ sal_Int32 nPortionEnd = nPortionStart + rLastPortion.GetLen();
+ for ( sal_Int32 _n = nChar; _n < nPortionEnd; _n++ )
+ {
+ pLine->GetCharPosArray()[_n-nFirstChar] += nMore4Everyone;
+ if ( nSomeExtraSpace )
+ pLine->GetCharPosArray()[_n-nFirstChar]++;
+ }
+
+ if ( nSomeExtraSpace )
+ nSomeExtraSpace--;
+ }
+ }
+
+ // Now the text width contains the extra width...
+ pLine->SetTextWidth( pLine->GetTextWidth() + nRemainingSpace );
+}
+
+// For Kashidas from sw/source/core/text/porlay.cxx
+void ImpEditEngine::ImpFindKashidas( ContentNode* pNode, sal_Int32 nStart, sal_Int32 nEnd, std::vector<sal_Int32>& rArray )
+{
+ // Kashida glyph looks suspicious, skip Kashida justification
+ if (GetRefDevice()->GetMinKashida() <= 0)
+ return;
+
+ std::vector<sal_Int32> aKashidaArray;
+
+ // the search has to be performed on a per word base
+
+ EditSelection aWordSel( EditPaM( pNode, nStart ) );
+ aWordSel = SelectWord( aWordSel, css::i18n::WordType::DICTIONARY_WORD );
+ if ( aWordSel.Min().GetIndex() < nStart )
+ aWordSel.Min().SetIndex( nStart );
+
+ while ( ( aWordSel.Min().GetNode() == pNode ) && ( aWordSel.Min().GetIndex() < nEnd ) )
+ {
+ const sal_Int32 nSavPos = aWordSel.Max().GetIndex();
+ if ( aWordSel.Max().GetIndex() > nEnd )
+ aWordSel.Max().SetIndex( nEnd );
+
+ OUString aWord = GetSelected( aWordSel );
+
+ // restore selection for proper iteration at the end of the function
+ aWordSel.Max().SetIndex( nSavPos );
+
+ sal_Int32 nIdx = 0, nPrevIdx = 0;
+ sal_Int32 nKashidaPos = -1;
+ sal_Unicode cCh, cPrevCh = 0;
+
+ int nPriorityLevel = 7; // 0..6 = level found
+ // 7 not found
+
+ sal_Int32 nWordLen = aWord.getLength();
+
+ // ignore trailing vowel chars
+ while( nWordLen && isTransparentChar( aWord[ nWordLen - 1 ] ))
+ --nWordLen;
+
+ while ( nIdx < nWordLen )
+ {
+ cCh = aWord[ nIdx ];
+
+ // 1. Priority:
+ // after user inserted kashida
+ if ( 0x640 == cCh )
+ {
+ nKashidaPos = aWordSel.Min().GetIndex() + nIdx;
+ nPriorityLevel = 0;
+ }
+
+ // 2. Priority:
+ // after a Seen or Sad
+ if (nPriorityLevel >= 1 && nIdx < nWordLen - 1)
+ {
+ if( isSeenOrSadChar( cCh )
+ && (aWord[ nIdx+1 ] != 0x200C) ) // #i98410#: prevent ZWNJ expansion
+ {
+ nKashidaPos = aWordSel.Min().GetIndex() + nIdx;
+ nPriorityLevel = 1;
+ }
+ }
+
+ // 3. Priority:
+ // before final form of Teh Marbuta, Heh, Dal
+ if ( nPriorityLevel >= 2 && nIdx > 0 )
+ {
+ if ( isTehMarbutaChar ( cCh ) || // Teh Marbuta (right joining)
+ isDalChar ( cCh ) || // Dal (right joining) final form may appear in the middle of word
+ ( isHehChar ( cCh ) && nIdx == nWordLen - 1)) // Heh (dual joining) only at end of word
+ {
+
+ SAL_WARN_IF( 0 == cPrevCh, "editeng", "No previous character" );
+ // check if character is connectable to previous character,
+ if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
+ {
+ nKashidaPos = aWordSel.Min().GetIndex() + nPrevIdx;
+ nPriorityLevel = 2;
+ }
+ }
+ }
+
+ // 4. Priority:
+ // before final form of Alef, Tah, Lam, Kaf or Gaf
+ if ( nPriorityLevel >= 3 && nIdx > 0 )
+ {
+ if ( isAlefChar ( cCh ) || // Alef (right joining) final form may appear in the middle of word
+ (( isLamChar ( cCh ) || // Lam,
+ isTahChar ( cCh ) || // Tah,
+ isKafChar ( cCh ) || // Kaf (all dual joining)
+ isGafChar ( cCh ) )
+ && nIdx == nWordLen - 1)) // only at end of word
+ {
+ SAL_WARN_IF( 0 == cPrevCh, "editeng", "No previous character" );
+ // check if character is connectable to previous character,
+ if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
+ {
+ nKashidaPos = aWordSel.Min().GetIndex() + nPrevIdx;
+ nPriorityLevel = 3;
+ }
+ }
+ }
+
+ // 5. Priority:
+ // before medial Beh-like
+ if ( nPriorityLevel >= 4 && nIdx > 0 && nIdx < nWordLen - 1 )
+ {
+ if ( isBehChar ( cCh ) )
+ {
+ // check if next character is Reh or Yeh-like
+ sal_Unicode cNextCh = aWord[ nIdx + 1 ];
+ if ( isRehChar ( cNextCh ) || isYehChar ( cNextCh ))
+ {
+ SAL_WARN_IF( 0 == cPrevCh, "editeng", "No previous character" );
+ // check if character is connectable to previous character,
+ if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
+ {
+ nKashidaPos = aWordSel.Min().GetIndex() + nPrevIdx;
+ nPriorityLevel = 4;
+ }
+ }
+ }
+ }
+
+ // 6. Priority:
+ // before the final form of Waw, Ain, Qaf and Feh
+ if ( nPriorityLevel >= 5 && nIdx > 0 )
+ {
+ if ( isWawChar ( cCh ) || // Wav (right joining)
+ // final form may appear in the middle of word
+ (( isAinChar ( cCh ) || // Ain (dual joining)
+ isQafChar ( cCh ) || // Qaf (dual joining)
+ isFehChar ( cCh ) ) // Feh (dual joining)
+ && nIdx == nWordLen - 1)) // only at end of word
+ {
+ SAL_WARN_IF( 0 == cPrevCh, "editeng", "No previous character" );
+ // check if character is connectable to previous character,
+ if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
+ {
+ nKashidaPos = aWordSel.Min().GetIndex() + nPrevIdx;
+ nPriorityLevel = 5;
+ }
+ }
+ }
+
+ // other connecting possibilities
+ if ( nPriorityLevel >= 6 && nIdx > 0 )
+ {
+ // Reh, Zain
+ if ( isRehChar ( cCh ) )
+ {
+ SAL_WARN_IF( 0 == cPrevCh, "editeng", "No previous character" );
+ // check if character is connectable to previous character,
+ if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
+ {
+ nKashidaPos = aWordSel.Min().GetIndex() + nPrevIdx;
+ nPriorityLevel = 6;
+ }
+ }
+ }
+
+ // Do not consider vowel marks when checking if a character
+ // can be connected to previous character.
+ if ( !isTransparentChar ( cCh) )
+ {
+ cPrevCh = cCh;
+ nPrevIdx = nIdx;
+ }
+
+ ++nIdx;
+ } // end of current word
+
+ if ( nKashidaPos>=0 )
+ aKashidaArray.push_back( nKashidaPos );
+
+ aWordSel = WordRight( aWordSel.Max(), css::i18n::WordType::DICTIONARY_WORD );
+ aWordSel = SelectWord( aWordSel, css::i18n::WordType::DICTIONARY_WORD );
+ }
+
+ // Validate
+ std::vector<sal_Int32> aDropped(aKashidaArray.size());
+ auto nOldLayout = GetRefDevice()->GetLayoutMode();
+ GetRefDevice()->SetLayoutMode(nOldLayout | vcl::text::ComplexTextLayoutFlags::BiDiRtl);
+ GetRefDevice()->ValidateKashidas(pNode->GetString(), nStart, nEnd - nStart,
+ aKashidaArray.size(), aKashidaArray.data(), aDropped.data());
+ GetRefDevice()->SetLayoutMode(nOldLayout);
+
+ for (auto const& pos : aKashidaArray)
+ if (std::find(aDropped.begin(), aDropped.end(), pos) == aDropped.end())
+ rArray.push_back(pos);
+}
+
+sal_Int32 ImpEditEngine::SplitTextPortion( ParaPortion* pPortion, sal_Int32 nPos, EditLine* pCurLine )
+{
+ // The portion at nPos is split, if there is not a transition at nPos anyway
+ if ( nPos == 0 )
+ return 0;
+
+ assert( pPortion && "SplitTextPortion: Which ?" );
+
+ sal_Int32 nSplitPortion;
+ sal_Int32 nTmpPos = 0;
+ TextPortion* pTextPortion = nullptr;
+ sal_Int32 nPortions = pPortion->GetTextPortions().Count();
+ for ( nSplitPortion = 0; nSplitPortion < nPortions; nSplitPortion++ )
+ {
+ TextPortion& rTP = pPortion->GetTextPortions()[nSplitPortion];
+ nTmpPos = nTmpPos + rTP.GetLen();
+ if ( nTmpPos >= nPos )
+ {
+ if ( nTmpPos == nPos ) // then nothing needs to be split
+ {
+ return nSplitPortion;
+ }
+ pTextPortion = &rTP;
+ break;
+ }
+ }
+
+ DBG_ASSERT( pTextPortion, "Position outside the area!" );
+
+ if (!pTextPortion)
+ return 0;
+
+ DBG_ASSERT( pTextPortion->GetKind() == PortionKind::TEXT, "SplitTextPortion: No TextPortion!" );
+
+ sal_Int32 nOverlapp = nTmpPos - nPos;
+ pTextPortion->SetLen( pTextPortion->GetLen() - nOverlapp );
+ TextPortion* pNewPortion = new TextPortion( nOverlapp );
+ pPortion->GetTextPortions().Insert(nSplitPortion+1, pNewPortion);
+ // Set sizes
+ if ( pCurLine )
+ {
+ // No new GetTextSize, instead use values from the Array:
+ assert( nPos > pCurLine->GetStart() && "SplitTextPortion at the beginning of the line?" );
+ pTextPortion->setWidth(pCurLine->GetCharPosArray()[nPos - pCurLine->GetStart() - 1]);
+
+ if ( pTextPortion->GetExtraInfos() && pTextPortion->GetExtraInfos()->bCompressed )
+ {
+ // We need the original size from the portion
+ sal_Int32 nTxtPortionStart = pPortion->GetTextPortions().GetStartPos( nSplitPortion );
+ SvxFont aTmpFont( pPortion->GetNode()->GetCharAttribs().GetDefFont() );
+ SeekCursor( pPortion->GetNode(), nTxtPortionStart+1, aTmpFont );
+ aTmpFont.SetPhysFont(*GetRefDevice());
+ GetRefDevice()->Push( vcl::PushFlags::TEXTLANGUAGE );
+ ImplInitDigitMode(*GetRefDevice(), aTmpFont.GetLanguage());
+ Size aSz = aTmpFont.QuickGetTextSize( GetRefDevice(), pPortion->GetNode()->GetString(),
+ nTxtPortionStart, pTextPortion->GetLen(), nullptr );
+ GetRefDevice()->Pop();
+ pTextPortion->GetExtraInfos()->nOrgWidth = aSz.Width();
+ }
+ }
+ else
+ pTextPortion->setWidth(-1);
+
+ return nSplitPortion;
+}
+
+void ImpEditEngine::CreateTextPortions( ParaPortion* pParaPortion, sal_Int32& rStart )
+{
+ sal_Int32 nStartPos = rStart;
+ ContentNode* pNode = pParaPortion->GetNode();
+ DBG_ASSERT( pNode->Len(), "CreateTextPortions should not be used for empty paragraphs!" );
+
+ o3tl::sorted_vector< sal_Int32 > aPositions;
+ aPositions.insert( 0 );
+
+ for (std::size_t nAttr = 0;; ++nAttr)
+ {
+ // Insert Start and End into the Array...
+ // The Insert method does not allow for duplicate values...
+ EditCharAttrib* pAttrib = GetAttrib(pNode->GetCharAttribs().GetAttribs(), nAttr);
+ if (!pAttrib)
+ break;
+ aPositions.insert( pAttrib->GetStart() );
+ aPositions.insert( pAttrib->GetEnd() );
+ }
+ aPositions.insert( pNode->Len() );
+
+ if ( pParaPortion->aScriptInfos.empty() )
+ InitScriptTypes( GetParaPortions().GetPos( pParaPortion ) );
+
+ const ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
+ for (const ScriptTypePosInfo& rType : rTypes)
+ aPositions.insert( rType.nStartPos );
+
+ const WritingDirectionInfos& rWritingDirections = pParaPortion->aWritingDirectionInfos;
+ for (const WritingDirectionInfo & rWritingDirection : rWritingDirections)
+ aPositions.insert( rWritingDirection.nStartPos );
+
+ if ( mpIMEInfos && mpIMEInfos->nLen && mpIMEInfos->pAttribs && ( mpIMEInfos->aPos.GetNode() == pNode ) )
+ {
+ ExtTextInputAttr nLastAttr = ExtTextInputAttr(0xFFFF);
+ for( sal_Int32 n = 0; n < mpIMEInfos->nLen; n++ )
+ {
+ if ( mpIMEInfos->pAttribs[n] != nLastAttr )
+ {
+ aPositions.insert( mpIMEInfos->aPos.GetIndex() + n );
+ nLastAttr = mpIMEInfos->pAttribs[n];
+ }
+ }
+ aPositions.insert( mpIMEInfos->aPos.GetIndex() + mpIMEInfos->nLen );
+ }
+
+ // From ... Delete:
+ // Unfortunately, the number of text portions does not have to match
+ // aPositions.Count(), since there might be line breaks...
+ sal_Int32 nPortionStart = 0;
+ sal_Int32 nInvPortion = 0;
+ sal_Int32 nP;
+ for ( nP = 0; nP < pParaPortion->GetTextPortions().Count(); nP++ )
+ {
+ const TextPortion& rTmpPortion = pParaPortion->GetTextPortions()[nP];
+ nPortionStart = nPortionStart + rTmpPortion.GetLen();
+ if ( nPortionStart >= nStartPos )
+ {
+ nPortionStart = nPortionStart - rTmpPortion.GetLen();
+ rStart = nPortionStart;
+ nInvPortion = nP;
+ break;
+ }
+ }
+ DBG_ASSERT( nP < pParaPortion->GetTextPortions().Count() || !pParaPortion->GetTextPortions().Count(), "Nothing to delete: CreateTextPortions" );
+ if ( nInvPortion && ( nPortionStart+pParaPortion->GetTextPortions()[nInvPortion].GetLen() > nStartPos ) )
+ {
+ // prefer one in front...
+ // But only if it was in the middle of the portion of, otherwise it
+ // might be the only one in the row in front!
+ nInvPortion--;
+ nPortionStart = nPortionStart - pParaPortion->GetTextPortions()[nInvPortion].GetLen();
+ }
+ pParaPortion->GetTextPortions().DeleteFromPortion( nInvPortion );
+
+ // A portion may also have been formed by a line break:
+ aPositions.insert( nPortionStart );
+
+ auto nInvPos = aPositions.find( nPortionStart );
+ DBG_ASSERT( (nInvPos != aPositions.end()), "InvPos ?!" );
+
+ auto i = nInvPos;
+ ++i;
+ while ( i != aPositions.end() )
+ {
+ TextPortion* pNew = new TextPortion( (*i++) - *nInvPos++ );
+ pParaPortion->GetTextPortions().Append(pNew);
+ }
+
+ DBG_ASSERT( pParaPortion->GetTextPortions().Count(), "No Portions?!" );
+#if OSL_DEBUG_LEVEL > 0
+ OSL_ENSURE( ParaPortion::DbgCheckTextPortions(*pParaPortion), "Portion is broken?" );
+#endif
+}
+
+void ImpEditEngine::RecalcTextPortion( ParaPortion* pParaPortion, sal_Int32 nStartPos, sal_Int32 nNewChars )
+{
+ DBG_ASSERT( pParaPortion->GetTextPortions().Count(), "No Portions!" );
+ DBG_ASSERT( nNewChars, "RecalcTextPortion with Diff == 0" );
+
+ ContentNode* const pNode = pParaPortion->GetNode();
+ if ( nNewChars > 0 )
+ {
+ // If an Attribute begins/ends at nStartPos, then a new portion starts
+ // otherwise the portion is extended at nStartPos.
+ if ( pNode->GetCharAttribs().HasBoundingAttrib( nStartPos ) || IsScriptChange( EditPaM( pNode, nStartPos ) ) )
+ {
+ sal_Int32 nNewPortionPos = 0;
+ if ( nStartPos )
+ nNewPortionPos = SplitTextPortion( pParaPortion, nStartPos ) + 1;
+
+ // A blank portion may be here, if the paragraph was empty,
+ // or if a line was created by a hard line break.
+ if ( ( nNewPortionPos < pParaPortion->GetTextPortions().Count() ) &&
+ !pParaPortion->GetTextPortions()[nNewPortionPos].GetLen() )
+ {
+ TextPortion& rTP = pParaPortion->GetTextPortions()[nNewPortionPos];
+ DBG_ASSERT( rTP.GetKind() == PortionKind::TEXT, "the empty portion was no TextPortion!" );
+ rTP.SetLen( rTP.GetLen() + nNewChars );
+ }
+ else
+ {
+ TextPortion* pNewPortion = new TextPortion( nNewChars );
+ pParaPortion->GetTextPortions().Insert(nNewPortionPos, pNewPortion);
+ }
+ }
+ else
+ {
+ sal_Int32 nPortionStart;
+ const sal_Int32 nTP = pParaPortion->GetTextPortions().
+ FindPortion( nStartPos, nPortionStart );
+ TextPortion& rTP = pParaPortion->GetTextPortions()[ nTP ];
+ rTP.SetLen( rTP.GetLen() + nNewChars );
+ rTP.setWidth(-1);
+ }
+ }
+ else
+ {
+ // Shrink or remove portion if necessary.
+ // Before calling this method it must be ensured that no portions were
+ // in the deleted area!
+
+ // There must be no portions extending into the area or portions starting in
+ // the area, so it must be:
+ // nStartPos <= nPos <= nStartPos - nNewChars(neg.)
+ sal_Int32 nPortion = 0;
+ sal_Int32 nPos = 0;
+ sal_Int32 nEnd = nStartPos-nNewChars;
+ sal_Int32 nPortions = pParaPortion->GetTextPortions().Count();
+ TextPortion* pTP = nullptr;
+ for ( nPortion = 0; nPortion < nPortions; nPortion++ )
+ {
+ pTP = &pParaPortion->GetTextPortions()[ nPortion ];
+ if ( ( nPos+pTP->GetLen() ) > nStartPos )
+ {
+ DBG_ASSERT( nPos <= nStartPos, "Wrong Start!" );
+ DBG_ASSERT( nPos+pTP->GetLen() >= nEnd, "Wrong End!" );
+ break;
+ }
+ nPos = nPos + pTP->GetLen();
+ }
+ assert( pTP && "RecalcTextPortion: Portion not found" );
+ if ( ( nPos == nStartPos ) && ( (nPos+pTP->GetLen()) == nEnd ) )
+ {
+ // Remove portion;
+ PortionKind nType = pTP->GetKind();
+ pParaPortion->GetTextPortions().Remove( nPortion );
+ if ( nType == PortionKind::LINEBREAK )
+ {
+ TextPortion& rNext = pParaPortion->GetTextPortions()[ nPortion ];
+ if ( !rNext.GetLen() )
+ {
+ // Remove dummy portion
+ pParaPortion->GetTextPortions().Remove( nPortion );
+ }
+ }
+ }
+ else
+ {
+ DBG_ASSERT( pTP->GetLen() > (-nNewChars), "Portion too small to shrink! ");
+ pTP->SetLen( pTP->GetLen() + nNewChars );
+ }
+
+ sal_Int32 nPortionCount = pParaPortion->GetTextPortions().Count();
+ assert( nPortionCount );
+ if (nPortionCount)
+ {
+ // No HYPHENATOR portion is allowed to get stuck right at the end...
+ sal_Int32 nLastPortion = nPortionCount - 1;
+ pTP = &pParaPortion->GetTextPortions()[nLastPortion];
+ if ( pTP->GetKind() == PortionKind::HYPHENATOR )
+ {
+ // Discard portion; if possible, correct the ones before,
+ // if the Hyphenator portion has swallowed one character...
+ if ( nLastPortion && pTP->GetLen() )
+ {
+ TextPortion& rPrev = pParaPortion->GetTextPortions()[nLastPortion - 1];
+ DBG_ASSERT( rPrev.GetKind() == PortionKind::TEXT, "Portion?!" );
+ rPrev.SetLen( rPrev.GetLen() + pTP->GetLen() );
+ rPrev.setWidth(-1);
+ }
+ pParaPortion->GetTextPortions().Remove( nLastPortion );
+ }
+ }
+ }
+#if OSL_DEBUG_LEVEL > 0
+ OSL_ENSURE( ParaPortion::DbgCheckTextPortions(*pParaPortion), "Portions are broken?" );
+#endif
+}
+
+void ImpEditEngine::SetTextRanger( std::unique_ptr<TextRanger> pRanger )
+{
+ pTextRanger = std::move(pRanger);
+
+ for ( sal_Int32 nPara = 0; nPara < GetParaPortions().Count(); nPara++ )
+ {
+ ParaPortion* pParaPortion = GetParaPortions()[nPara];
+ pParaPortion->MarkSelectionInvalid( 0 );
+ pParaPortion->GetLines().Reset();
+ }
+
+ FormatFullDoc();
+ UpdateViews( GetActiveView() );
+ if ( IsUpdateLayout() && GetActiveView() )
+ pActiveView->ShowCursor(false, false);
+}
+
+void ImpEditEngine::SetVertical( bool bVertical)
+{
+ if ( IsEffectivelyVertical() != bVertical)
+ {
+ GetEditDoc().SetVertical(bVertical);
+ bool bUseCharAttribs = bool(maStatus.GetControlWord() & EEControlBits::USECHARATTRIBS);
+ GetEditDoc().CreateDefFont( bUseCharAttribs );
+ if ( IsFormatted() )
+ {
+ FormatFullDoc();
+ UpdateViews( GetActiveView() );
+ }
+ }
+}
+
+void ImpEditEngine::SetRotation(TextRotation nRotation)
+{
+ if (GetEditDoc().GetRotation() == nRotation)
+ return; // not modified
+ GetEditDoc().SetRotation(nRotation);
+ bool bUseCharAttribs = bool(maStatus.GetControlWord() & EEControlBits::USECHARATTRIBS);
+ GetEditDoc().CreateDefFont( bUseCharAttribs );
+ if ( IsFormatted() )
+ {
+ FormatFullDoc();
+ UpdateViews( GetActiveView() );
+ }
+}
+
+void ImpEditEngine::SetTextColumns(sal_Int16 nColumns, sal_Int32 nSpacing)
+{
+ assert(nColumns >= 1);
+ if (mnColumns != nColumns || mnColumnSpacing != nSpacing)
+ {
+ if (nColumns == 0)
+ {
+ SAL_WARN("editeng", "bad nColumns value, ignoring");
+ nColumns = 1;
+ }
+ mnColumns = nColumns;
+ mnColumnSpacing = nSpacing;
+ if (IsFormatted())
+ {
+ FormatFullDoc();
+ UpdateViews(GetActiveView());
+ }
+ }
+}
+
+void ImpEditEngine::SetFixedCellHeight( bool bUseFixedCellHeight )
+{
+ if ( IsFixedCellHeight() != bUseFixedCellHeight )
+ {
+ GetEditDoc().SetFixedCellHeight( bUseFixedCellHeight );
+ if ( IsFormatted() )
+ {
+ FormatFullDoc();
+ UpdateViews( GetActiveView() );
+ }
+ }
+}
+
+void ImpEditEngine::SeekCursor( ContentNode* pNode, sal_Int32 nPos, SvxFont& rFont, OutputDevice* pOut )
+{
+ // It was planned, SeekCursor( nStartPos, nEndPos,... ), so that it would
+ // only be searched anew at the StartPosition.
+ // Problem: There would be two lists to consider/handle:
+ // OrderedByStart,OrderedByEnd.
+
+ if ( nPos > pNode->Len() )
+ nPos = pNode->Len();
+
+ rFont = pNode->GetCharAttribs().GetDefFont();
+
+ /*
+ * Set attributes for script types Asian and Complex
+ */
+ short nScriptTypeI18N = GetI18NScriptType( EditPaM( pNode, nPos ) );
+ SvtScriptType nScriptType = SvtLanguageOptions::FromI18NToSvtScriptType(nScriptTypeI18N);
+ if ( ( nScriptTypeI18N == i18n::ScriptType::ASIAN ) || ( nScriptTypeI18N == i18n::ScriptType::COMPLEX ) )
+ {
+ const SvxFontItem& rFontItem = static_cast<const SvxFontItem&>(pNode->GetContentAttribs().GetItem( GetScriptItemId( EE_CHAR_FONTINFO, nScriptType ) ));
+ rFont.SetFamilyName( rFontItem.GetFamilyName() );
+ rFont.SetFamily( rFontItem.GetFamily() );
+ rFont.SetPitch( rFontItem.GetPitch() );
+ rFont.SetCharSet( rFontItem.GetCharSet() );
+ Size aSz( rFont.GetFontSize() );
+ aSz.setHeight( static_cast<const SvxFontHeightItem&>(pNode->GetContentAttribs().GetItem( GetScriptItemId( EE_CHAR_FONTHEIGHT, nScriptType ) ) ).GetHeight() );
+ rFont.SetFontSize( aSz );
+ rFont.SetWeight( static_cast<const SvxWeightItem&>(pNode->GetContentAttribs().GetItem( GetScriptItemId( EE_CHAR_WEIGHT, nScriptType ))).GetWeight() );
+ rFont.SetItalic( static_cast<const SvxPostureItem&>(pNode->GetContentAttribs().GetItem( GetScriptItemId( EE_CHAR_ITALIC, nScriptType ))).GetPosture() );
+ rFont.SetLanguage( static_cast<const SvxLanguageItem&>(pNode->GetContentAttribs().GetItem( GetScriptItemId( EE_CHAR_LANGUAGE, nScriptType ))).GetLanguage() );
+ }
+
+ sal_uInt16 nRelWidth = pNode->GetContentAttribs().GetItem( EE_CHAR_FONTWIDTH).GetValue();
+
+ /*
+ * Set output device's line and overline colors
+ */
+ if ( pOut )
+ {
+ const SvxUnderlineItem& rTextLineColor = pNode->GetContentAttribs().GetItem( EE_CHAR_UNDERLINE );
+ if ( rTextLineColor.GetColor() != COL_TRANSPARENT )
+ pOut->SetTextLineColor( rTextLineColor.GetColor() );
+ else
+ pOut->SetTextLineColor();
+
+ const SvxOverlineItem& rOverlineColor = pNode->GetContentAttribs().GetItem( EE_CHAR_OVERLINE );
+ if ( rOverlineColor.GetColor() != COL_TRANSPARENT )
+ pOut->SetOverlineColor( rOverlineColor.GetColor() );
+ else
+ pOut->SetOverlineColor();
+ }
+
+ const SvxLanguageItem* pCJKLanguageItem = nullptr;
+
+ /*
+ * Scan through char attributes of pNode
+ */
+ if (maStatus.UseCharAttribs())
+ {
+ CharAttribList::AttribsType& rAttribs = pNode->GetCharAttribs().GetAttribs();
+ size_t nAttr = 0;
+ EditCharAttrib* pAttrib = GetAttrib(rAttribs, nAttr);
+ while ( pAttrib && ( pAttrib->GetStart() <= nPos ) )
+ {
+ // when seeking, ignore attributes which start there! Empty attributes
+ // are considered (used) as these are just set. But do not use empty
+ // attributes: When just set and empty => no effect on font
+ // In a blank paragraph, set characters take effect immediately.
+ if ( ( pAttrib->Which() != 0 ) &&
+ ( ( ( pAttrib->GetStart() < nPos ) && ( pAttrib->GetEnd() >= nPos ) )
+ || ( !pNode->Len() ) ) )
+ {
+ DBG_ASSERT( ( pAttrib->Which() >= EE_CHAR_START ) && ( pAttrib->Which() <= EE_FEATURE_END ), "Invalid Attribute in Seek() " );
+ if ( IsScriptItemValid( pAttrib->Which(), nScriptTypeI18N ) )
+ {
+ pAttrib->SetFont( rFont, pOut );
+ // #i1550# hard color attrib should win over text color from field
+ if ( pAttrib->Which() == EE_FEATURE_FIELD )
+ {
+ EditCharAttrib* pColorAttr = pNode->GetCharAttribs().FindAttrib( EE_CHAR_COLOR, nPos );
+ if ( pColorAttr )
+ pColorAttr->SetFont( rFont, pOut );
+ }
+ }
+ if ( pAttrib->Which() == EE_CHAR_FONTWIDTH )
+ nRelWidth = static_cast<const SvxCharScaleWidthItem*>(pAttrib->GetItem())->GetValue();
+ if ( pAttrib->Which() == EE_CHAR_LANGUAGE_CJK )
+ pCJKLanguageItem = static_cast<const SvxLanguageItem*>( pAttrib->GetItem() );
+ }
+ pAttrib = GetAttrib( rAttribs, ++nAttr );
+ }
+ }
+
+ if ( !pCJKLanguageItem )
+ pCJKLanguageItem = &pNode->GetContentAttribs().GetItem( EE_CHAR_LANGUAGE_CJK );
+
+ rFont.SetCJKContextLanguage( pCJKLanguageItem->GetLanguage() );
+
+ if ( (rFont.GetKerning() != FontKerning::NONE) && IsKernAsianPunctuation() && ( nScriptTypeI18N == i18n::ScriptType::ASIAN ) )
+ rFont.SetKerning( rFont.GetKerning() | FontKerning::Asian );
+
+ if (maStatus.DoNotUseColors())
+ {
+ rFont.SetColor( /* rColorItem.GetValue() */ COL_BLACK );
+ }
+
+ if (maStatus.DoStretch() || ( nRelWidth != 100 ))
+ {
+ // For the current Output device, because otherwise if RefDev=Printer its looks
+ // ugly on the screen!
+ OutputDevice* pDev = pOut ? pOut : GetRefDevice();
+ rFont.SetPhysFont(*pDev);
+ FontMetric aMetric( pDev->GetFontMetric() );
+
+ // before forcing nPropr to 100%, calculate a new escapement relative to this fake size.
+ sal_uInt8 nPropr = rFont.GetPropr();
+ sal_Int16 nEsc = rFont.GetEscapement();
+ if ( nPropr && nEsc && nPropr != 100 && abs(nEsc) != DFLT_ESC_AUTO_SUPER )
+ rFont.SetEscapement( 100.0/nPropr * nEsc );
+
+ // Set the font as we want it to look like & reset the Propr attribute
+ // so that it is not counted twice.
+ Size aRealSz( aMetric.GetFontSize() );
+ rFont.SetPropr( 100 );
+
+ if (maStatus.DoStretch())
+ {
+ if (mfFontScaleY != 100.0)
+ {
+ double fHeightRounded = roundToNearestPt(aRealSz.Height());
+ double fNewHeight = fHeightRounded * (mfFontScaleY / 100.0);
+ fNewHeight = roundToNearestPt(fNewHeight);
+ aRealSz.setHeight(basegfx::fround(fNewHeight));
+ }
+ if (mfFontScaleX != 100.0)
+ {
+ if (mfFontScaleX == mfFontScaleY && nRelWidth == 100 )
+ {
+ aRealSz.setWidth( 0 );
+ }
+ else
+ {
+ double fWidthRounded = roundToNearestPt(aRealSz.Width());
+ double fNewWidth = fWidthRounded * (mfFontScaleX / 100.0);
+ fNewWidth = roundToNearestPt(fNewWidth);
+ aRealSz.setWidth(basegfx::fround(fNewWidth));
+
+ // Also the Kerning: (long due to handle Interim results)
+ tools::Long nKerning = rFont.GetFixKerning();
+/*
+ The consideration was: If negative kerning, but StretchX = 200
+ => Do not double the kerning, thus pull the letters closer together
+ ---------------------------
+ Kern StretchX =>Kern
+ ---------------------------
+ >0 <100 < (Proportional)
+ <0 <100 < (Proportional)
+ >0 >100 > (Proportional)
+ <0 >100 < (The amount, thus disproportional)
+*/
+ if (nKerning < 0 && mfFontScaleX > 100.0)
+ {
+ // disproportional
+ nKerning = basegfx::fround((double(nKerning) * 100.0) / mfFontScaleX);
+ }
+ else if ( nKerning )
+ {
+ // Proportional
+ nKerning = basegfx::fround((double(nKerning) * mfFontScaleX) / 100.0);
+ }
+ rFont.SetFixKerning( static_cast<short>(nKerning) );
+ }
+ }
+ }
+ if ( nRelWidth != 100 )
+ {
+ aRealSz.setWidth( aRealSz.Width() * nRelWidth );
+ aRealSz.setWidth( aRealSz.Width() / 100 );
+ }
+ rFont.SetFontSize( aRealSz );
+ // Font is not restored...
+ }
+
+ if ( ( ( rFont.GetColor() == COL_AUTO ) || ( IsForceAutoColor() ) ) && pOut )
+ {
+ // #i75566# Do not use AutoColor when printing OR Pdf export
+ const bool bPrinting(OUTDEV_PRINTER == pOut->GetOutDevType());
+ const bool bPDFExporting(OUTDEV_PDF == pOut->GetOutDevType());
+
+ if ( IsAutoColorEnabled() && !bPrinting && !bPDFExporting)
+ {
+ // Never use WindowTextColor on the printer
+ rFont.SetColor( GetAutoColor() );
+ }
+ else
+ {
+ if ( ( GetBackgroundColor() != COL_AUTO ) && GetBackgroundColor().IsDark() )
+ rFont.SetColor( COL_WHITE );
+ else
+ rFont.SetColor( COL_BLACK );
+ }
+ }
+
+ if ( !(mpIMEInfos && mpIMEInfos->pAttribs && ( mpIMEInfos->aPos.GetNode() == pNode ) &&
+ ( nPos > mpIMEInfos->aPos.GetIndex() ) && ( nPos <= ( mpIMEInfos->aPos.GetIndex() + mpIMEInfos->nLen ) )) )
+ return;
+
+ ExtTextInputAttr nAttr = mpIMEInfos->pAttribs[ nPos - mpIMEInfos->aPos.GetIndex() - 1 ];
+ if ( nAttr & ExtTextInputAttr::Underline )
+ rFont.SetUnderline( LINESTYLE_SINGLE );
+ else if ( nAttr & ExtTextInputAttr::DoubleUnderline )
+ rFont.SetUnderline( LINESTYLE_DOUBLE );
+ else if ( nAttr & ExtTextInputAttr::BoldUnderline )
+ rFont.SetUnderline( LINESTYLE_BOLD );
+ else if ( nAttr & ExtTextInputAttr::DottedUnderline )
+ rFont.SetUnderline( LINESTYLE_DOTTED );
+ else if ( nAttr & ExtTextInputAttr::DashDotUnderline )
+ rFont.SetUnderline( LINESTYLE_DOTTED );
+ else if ( nAttr & ExtTextInputAttr::RedText )
+ rFont.SetColor( COL_RED );
+ else if ( nAttr & ExtTextInputAttr::HalfToneText )
+ rFont.SetColor( COL_LIGHTGRAY );
+ if ( nAttr & ExtTextInputAttr::Highlight )
+ {
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ rFont.SetColor( rStyleSettings.GetHighlightTextColor() );
+ rFont.SetFillColor( rStyleSettings.GetHighlightColor() );
+ rFont.SetTransparent( false );
+ }
+ else if ( nAttr & ExtTextInputAttr::GrayWaveline )
+ {
+ rFont.SetUnderline( LINESTYLE_WAVE );
+ if( pOut )
+ pOut->SetTextLineColor( COL_LIGHTGRAY );
+ }
+}
+
+void ImpEditEngine::RecalcFormatterFontMetrics( FormatterFontMetric& rCurMetrics, SvxFont& rFont )
+{
+ // for line height at high / low first without Propr!
+ sal_uInt16 nPropr = rFont.GetPropr();
+ DBG_ASSERT( ( nPropr == 100 ) || rFont.GetEscapement(), "Propr without Escape?!" );
+ if ( nPropr != 100 )
+ {
+ rFont.SetPropr( 100 );
+ rFont.SetPhysFont(*pRefDev);
+ }
+ sal_uInt16 nAscent, nDescent;
+
+ FontMetric aMetric( pRefDev->GetFontMetric() );
+ nAscent = static_cast<sal_uInt16>(aMetric.GetAscent());
+ if ( IsAddExtLeading() )
+ nAscent = sal::static_int_cast< sal_uInt16 >(
+ nAscent + aMetric.GetExternalLeading() );
+ nDescent = static_cast<sal_uInt16>(aMetric.GetDescent());
+
+ if ( IsFixedCellHeight() )
+ {
+ nAscent = sal::static_int_cast< sal_uInt16 >( rFont.GetFontHeight() );
+ nDescent= sal::static_int_cast< sal_uInt16 >( ImplCalculateFontIndependentLineSpacing( rFont.GetFontHeight() ) - nAscent );
+ }
+ else
+ {
+ sal_uInt16 nIntLeading = ( aMetric.GetInternalLeading() > 0 ) ? static_cast<sal_uInt16>(aMetric.GetInternalLeading()) : 0;
+ // Fonts without leading cause problems
+ if ( ( nIntLeading == 0 ) && ( pRefDev->GetOutDevType() == OUTDEV_PRINTER ) )
+ {
+ // Lets see what Leading one gets on the screen
+ VclPtr<VirtualDevice> pVDev = GetVirtualDevice( pRefDev->GetMapMode(), pRefDev->GetDrawMode() );
+ rFont.SetPhysFont(*pVDev);
+ aMetric = pVDev->GetFontMetric();
+
+ // This is so that the Leading does not count itself out again,
+ // if the whole line has the font, nTmpLeading.
+ nAscent = static_cast<sal_uInt16>(aMetric.GetAscent());
+ nDescent = static_cast<sal_uInt16>(aMetric.GetDescent());
+ }
+ }
+ if ( nAscent > rCurMetrics.nMaxAscent )
+ rCurMetrics.nMaxAscent = nAscent;
+ if ( nDescent > rCurMetrics.nMaxDescent )
+ rCurMetrics.nMaxDescent= nDescent;
+ // Special treatment of high/low:
+ if ( !rFont.GetEscapement() )
+ return;
+
+ // Now in consideration of Escape/Propr
+ // possibly enlarge Ascent or Descent
+ short nDiff = static_cast<short>(rFont.GetFontSize().Height()*rFont.GetEscapement()/100);
+ if ( rFont.GetEscapement() > 0 )
+ {
+ nAscent = static_cast<sal_uInt16>(static_cast<tools::Long>(nAscent)*nPropr/100 + nDiff);
+ if ( nAscent > rCurMetrics.nMaxAscent )
+ rCurMetrics.nMaxAscent = nAscent;
+ }
+ else // has to be < 0
+ {
+ nDescent = static_cast<sal_uInt16>(static_cast<tools::Long>(nDescent)*nPropr/100 - nDiff);
+ if ( nDescent > rCurMetrics.nMaxDescent )
+ rCurMetrics.nMaxDescent= nDescent;
+ }
+}
+
+tools::Long ImpEditEngine::getWidthDirectionAware(const Size& sz) const
+{
+ return !IsEffectivelyVertical() ? sz.Width() : sz.Height();
+}
+
+tools::Long ImpEditEngine::getHeightDirectionAware(const Size& sz) const
+{
+ return !IsEffectivelyVertical() ? sz.Height() : sz.Width();
+}
+
+void ImpEditEngine::adjustXDirectionAware(Point& pt, tools::Long x) const
+{
+ if (!IsEffectivelyVertical())
+ pt.AdjustX(x);
+ else
+ pt.AdjustY(IsTopToBottom() ? x : -x);
+}
+
+void ImpEditEngine::adjustYDirectionAware(Point& pt, tools::Long y) const
+{
+ if (!IsEffectivelyVertical())
+ pt.AdjustY(y);
+ else
+ pt.AdjustX(IsTopToBottom() ? -y : y);
+}
+
+void ImpEditEngine::setXDirectionAwareFrom(Point& ptDest, const Point& ptSrc) const
+{
+ if (!IsEffectivelyVertical())
+ ptDest.setX(ptSrc.X());
+ else
+ ptDest.setY(ptSrc.Y());
+}
+
+void ImpEditEngine::setYDirectionAwareFrom(Point& ptDest, const Point& ptSrc) const
+{
+ if (!IsEffectivelyVertical())
+ ptDest.setY(ptSrc.Y());
+ else
+ ptDest.setX(ptSrc.Y());
+}
+
+tools::Long ImpEditEngine::getYOverflowDirectionAware(const Point& pt,
+ const tools::Rectangle& rectMax) const
+{
+ tools::Long nRes;
+ if (!IsEffectivelyVertical())
+ nRes = pt.Y() - rectMax.Bottom();
+ else if (IsTopToBottom())
+ nRes = rectMax.Left() - pt.X();
+ else
+ nRes = pt.X() - rectMax.Right();
+ return std::max(nRes, tools::Long(0));
+}
+
+bool ImpEditEngine::isXOverflowDirectionAware(const Point& pt, const tools::Rectangle& rectMax) const
+{
+ if (!IsEffectivelyVertical())
+ return pt.X() > rectMax.Right();
+
+ if (IsTopToBottom())
+ return pt.Y() > rectMax.Bottom();
+ else
+ return pt.Y() < rectMax.Top();
+}
+
+tools::Long ImpEditEngine::getBottomDocOffset(const tools::Rectangle& rect) const
+{
+ if (!IsEffectivelyVertical())
+ return rect.Bottom();
+
+ if (IsTopToBottom())
+ return -rect.Left();
+ else
+ return rect.Right();
+}
+
+Size ImpEditEngine::getTopLeftDocOffset(const tools::Rectangle& rect) const
+{
+ if (!IsEffectivelyVertical())
+ return { rect.Left(), rect.Top() };
+
+ if (IsTopToBottom())
+ return { rect.Top(), -rect.Right() };
+ else
+ return { -rect.Bottom(), rect.Left() };
+}
+
+// Returns the resulting shift for the point; allows to apply the same shift to other points
+Point ImpEditEngine::MoveToNextLine(
+ Point& rMovePos, // [in, out] Point that will move to the next line
+ tools::Long nLineHeight, // [in] Y-direction move distance (direction-aware)
+ sal_Int16& rColumn, // [in, out] current column number
+ Point aOrigin, // [in] Origin point to calculate limits and initial Y position in a new column
+ tools::Long* pnHeightNeededToNotWrap // On column wrap, returns how much more height is needed
+) const
+{
+ const Point aOld = rMovePos;
+
+ // Move the point by the requested distance in Y direction
+ adjustYDirectionAware(rMovePos, nLineHeight);
+ // Check if the resulting position has moved beyond the limits, and more columns left.
+ // The limits are defined by a rectangle starting from aOrigin with width of maPaperSize
+ // and height of nCurTextHeight
+ Point aOtherCorner = aOrigin;
+ adjustXDirectionAware(aOtherCorner, getWidthDirectionAware(maPaperSize));
+ adjustYDirectionAware(aOtherCorner, nCurTextHeight);
+ tools::Long nNeeded
+ = getYOverflowDirectionAware(rMovePos, tools::Rectangle::Normalize(aOrigin, aOtherCorner));
+ if (pnHeightNeededToNotWrap)
+ *pnHeightNeededToNotWrap = nNeeded;
+ if (nNeeded && rColumn < mnColumns)
+ {
+ ++rColumn;
+ // If we didn't fit into the last column, indicate that only by setting the column number
+ // to the total number of columns; do not adjust
+ if (rColumn < mnColumns)
+ {
+ // Set Y position of the point to that of aOrigin
+ setYDirectionAwareFrom(rMovePos, aOrigin);
+ // Move the point by the requested distance in Y direction
+ adjustYDirectionAware(rMovePos, nLineHeight);
+ // Move the point by the column+spacing distance in X direction
+ adjustXDirectionAware(rMovePos, GetColumnWidth(maPaperSize) + mnColumnSpacing);
+ }
+ }
+
+ return rMovePos - aOld;
+}
+
+// TODO: use IterateLineAreas in ImpEditEngine::Paint, to avoid algorithm duplication
+
+void ImpEditEngine::Paint( OutputDevice& rOutDev, tools::Rectangle aClipRect, Point aStartPos, bool bStripOnly, Degree10 nOrientation )
+{
+ if ( !IsUpdateLayout() && !bStripOnly )
+ return;
+
+ if ( !IsFormatted() )
+ FormatDoc();
+
+ tools::Long nFirstVisXPos = - rOutDev.GetMapMode().GetOrigin().X();
+ tools::Long nFirstVisYPos = - rOutDev.GetMapMode().GetOrigin().Y();
+
+ DBG_ASSERT( GetParaPortions().Count(), "No ParaPortion?!" );
+ SvxFont aTmpFont( GetParaPortions()[0]->GetNode()->GetCharAttribs().GetDefFont() );
+ vcl::PDFExtOutDevData* const pPDFExtOutDevData = dynamic_cast< vcl::PDFExtOutDevData* >( rOutDev.GetExtOutDevData() );
+
+ // In the case of rotated text is aStartPos considered TopLeft because
+ // other information is missing, and since the whole object is shown anyway
+ // un-scrolled.
+ // The rectangle is infinite.
+ const Point aOrigin( aStartPos );
+
+ // #110496# Added some more optional metafile comments. This
+ // change: factored out some duplicated code.
+ GDIMetaFile* pMtf = rOutDev.GetConnectMetaFile();
+ const bool bMetafileValid( pMtf != nullptr );
+
+ const tools::Long nVertLineSpacing = CalcVertLineSpacing(aStartPos);
+
+ sal_Int16 nColumn = 0;
+
+ // Over all the paragraphs...
+
+ for ( sal_Int32 n = 0; n < GetParaPortions().Count(); n++ )
+ {
+ const ParaPortion* const pPortion = GetParaPortions()[n];
+ assert( pPortion && "NULL-Pointer in TokenList in Paint" );
+ // if when typing idle formatting, asynchronous Paint.
+ // Invisible Portions may be invalid.
+ if ( pPortion->IsVisible() && pPortion->IsInvalid() )
+ return;
+
+ if ( pPDFExtOutDevData )
+ pPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::Paragraph);
+
+ const tools::Long nParaHeight = pPortion->GetHeight();
+ if ( pPortion->IsVisible() && (
+ ( !IsEffectivelyVertical() && ( ( aStartPos.Y() + nParaHeight ) > aClipRect.Top() ) ) ||
+ ( IsEffectivelyVertical() && IsTopToBottom() && ( ( aStartPos.X() - nParaHeight ) < aClipRect.Right() ) ) ||
+ ( IsEffectivelyVertical() && !IsTopToBottom() && ( ( aStartPos.X() + nParaHeight ) > aClipRect.Left() ) ) ) )
+
+ {
+ Point aTmpPos;
+
+ // Over the lines of the paragraph...
+
+ const sal_Int32 nLines = pPortion->GetLines().Count();
+ const sal_Int32 nLastLine = nLines-1;
+
+ bool bEndOfParagraphWritten(false);
+
+ adjustYDirectionAware(aStartPos, pPortion->GetFirstLineOffset());
+
+ const SvxLineSpacingItem& rLSItem = pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
+ sal_uInt16 nSBL = ( rLSItem.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Fix )
+ ? scaleYSpacingValue(rLSItem.GetInterLineSpace()) : 0;
+ bool bPaintBullet (false);
+
+ for ( sal_Int32 nLine = 0; nLine < nLines; nLine++ )
+ {
+ const EditLine* const pLine = &pPortion->GetLines()[nLine];
+ assert( pLine && "NULL-Pointer in the line iterator in UpdateViews" );
+ sal_Int32 nIndex = pLine->GetStart();
+ tools::Long nLineHeight = pLine->GetHeight();
+ if (nLine != nLastLine)
+ nLineHeight += nVertLineSpacing;
+ MoveToNextLine(aStartPos, nLineHeight, nColumn, aOrigin);
+ aTmpPos = aStartPos;
+ adjustXDirectionAware(aTmpPos, pLine->GetStartPosX());
+ adjustYDirectionAware(aTmpPos, pLine->GetMaxAscent() - nLineHeight);
+
+ if ( ( !IsEffectivelyVertical() && ( aStartPos.Y() > aClipRect.Top() ) )
+ || ( IsEffectivelyVertical() && IsTopToBottom() && aStartPos.X() < aClipRect.Right() )
+ || ( IsEffectivelyVertical() && !IsTopToBottom() && aStartPos.X() > aClipRect.Left() ) )
+ {
+ bPaintBullet = false;
+
+ // Why not just also call when stripping portions? This will give the correct values
+ // and needs no position corrections in OutlinerEditEng::DrawingText which tries to call
+ // PaintBullet correctly; exactly what GetEditEnginePtr()->PaintingFirstLine
+ // does, too. No change for not-layouting (painting).
+ if(0 == nLine) // && !bStripOnly)
+ {
+ Point aLineStart(aStartPos);
+ adjustYDirectionAware(aLineStart, -nLineHeight);
+ GetEditEnginePtr()->PaintingFirstLine(n, aLineStart, aOrigin, nOrientation, rOutDev);
+
+ // Remember whether a bullet was painted.
+ const SfxBoolItem& rBulletState = pEditEngine->GetParaAttrib(n, EE_PARA_BULLETSTATE);
+ bPaintBullet = rBulletState.GetValue();
+ }
+
+
+ // Over the Portions of the line...
+
+ bool bParsingFields = false;
+ std::vector< sal_Int32 >::iterator itSubLines;
+
+ for ( sal_Int32 nPortion = pLine->GetStartPortion(); nPortion <= pLine->GetEndPortion(); nPortion++ )
+ {
+ DBG_ASSERT( pPortion->GetTextPortions().Count(), "Line without Textportion in Paint!" );
+ const TextPortion& rTextPortion = pPortion->GetTextPortions()[nPortion];
+
+ const tools::Long nPortionXOffset = GetPortionXOffset( pPortion, pLine, nPortion );
+ setXDirectionAwareFrom(aTmpPos, aStartPos);
+ adjustXDirectionAware(aTmpPos, nPortionXOffset);
+ if (isXOverflowDirectionAware(aTmpPos, aClipRect))
+ break; // No further output in line necessary
+
+ switch ( rTextPortion.GetKind() )
+ {
+ case PortionKind::TEXT:
+ case PortionKind::FIELD:
+ case PortionKind::HYPHENATOR:
+ {
+ SeekCursor( pPortion->GetNode(), nIndex+1, aTmpFont, &rOutDev );
+
+ bool bDrawFrame = false;
+
+ if ( ( rTextPortion.GetKind() == PortionKind::FIELD ) && !aTmpFont.IsTransparent() &&
+ ( GetBackgroundColor() != COL_AUTO ) && GetBackgroundColor().IsDark() &&
+ ( IsAutoColorEnabled() && ( rOutDev.GetOutDevType() != OUTDEV_PRINTER ) ) )
+ {
+ aTmpFont.SetTransparent( true );
+ rOutDev.SetFillColor();
+ rOutDev.SetLineColor( GetAutoColor() );
+ bDrawFrame = true;
+ }
+
+#if OSL_DEBUG_LEVEL > 2
+ // Do we really need this if statement?
+ if ( rTextPortion.GetKind() == PortionKind::HYPHENATOR )
+ {
+ aTmpFont.SetFillColor( COL_LIGHTGRAY );
+ aTmpFont.SetTransparent( sal_False );
+ }
+ if ( rTextPortion.IsRightToLeft() )
+ {
+ aTmpFont.SetFillColor( COL_LIGHTGRAY );
+ aTmpFont.SetTransparent( sal_False );
+ }
+ else if ( GetI18NScriptType( EditPaM( pPortion->GetNode(), nIndex+1 ) ) == i18n::ScriptType::COMPLEX )
+ {
+ aTmpFont.SetFillColor( COL_LIGHTCYAN );
+ aTmpFont.SetTransparent( sal_False );
+ }
+#endif
+ aTmpFont.SetPhysFont(rOutDev);
+
+ // #114278# Saving both layout mode and language (since I'm
+ // potentially changing both)
+ rOutDev.Push( vcl::PushFlags::TEXTLAYOUTMODE|vcl::PushFlags::TEXTLANGUAGE );
+ ImplInitLayoutMode(rOutDev, n, nIndex);
+ ImplInitDigitMode(rOutDev, aTmpFont.GetLanguage());
+
+ OUString aText;
+ sal_Int32 nTextStart = 0;
+ sal_Int32 nTextLen = 0;
+ std::span<const sal_Int32> pDXArray;
+ std::span<const sal_Bool> pKashidaArray;
+ KernArray aTmpDXArray;
+
+ if ( rTextPortion.GetKind() == PortionKind::TEXT )
+ {
+ aText = pPortion->GetNode()->GetString();
+ nTextStart = nIndex;
+ nTextLen = rTextPortion.GetLen();
+ pDXArray = std::span(pLine->GetCharPosArray().data() + (nIndex - pLine->GetStart()),
+ pLine->GetCharPosArray().size() - (nIndex - pLine->GetStart()));
+
+ if (!pLine->GetKashidaArray().empty())
+ {
+ pKashidaArray = std::span(pLine->GetKashidaArray().data() + (nIndex - pLine->GetStart()),
+ pLine->GetKashidaArray().size() - (nIndex - pLine->GetStart()));
+ }
+
+ // Paint control characters (#i55716#)
+ /* XXX: Given that there's special handling
+ * only for some specific characters
+ * (U+200B ZERO WIDTH SPACE and U+2060 WORD
+ * JOINER) it is assumed to be not relevant
+ * for MarkUrlFields(). */
+ if (maStatus.MarkNonUrlFields())
+ {
+ sal_Int32 nTmpIdx;
+ const sal_Int32 nTmpEnd = nTextStart + rTextPortion.GetLen();
+
+ for ( nTmpIdx = nTextStart; nTmpIdx <= nTmpEnd ; ++nTmpIdx )
+ {
+ const sal_Unicode cChar = ( nTmpIdx != aText.getLength() && ( nTmpIdx != nTextStart || 0 == nTextStart ) ) ?
+ aText[nTmpIdx] :
+ 0;
+
+ if ( 0x200B == cChar || 0x2060 == cChar )
+ {
+ tools::Long nHalfBlankWidth = aTmpFont.QuickGetTextSize( &rOutDev,
+ " ", 0, 1, nullptr ).Width() / 2;
+
+ const tools::Long nAdvanceX = ( nTmpIdx == nTmpEnd ?
+ rTextPortion.GetSize().Width() :
+ pDXArray[ nTmpIdx - nTextStart ] ) - nHalfBlankWidth;
+ const tools::Long nAdvanceY = -pLine->GetMaxAscent();
+
+ Point aTopLeftRectPos( aTmpPos );
+ adjustXDirectionAware(aTopLeftRectPos, nAdvanceX);
+ adjustYDirectionAware(aTopLeftRectPos, nAdvanceY);
+
+ Point aBottomRightRectPos( aTopLeftRectPos );
+ adjustXDirectionAware(aBottomRightRectPos, 2 * nHalfBlankWidth);
+ adjustYDirectionAware(aBottomRightRectPos, pLine->GetHeight());
+
+ rOutDev.Push( vcl::PushFlags::FILLCOLOR );
+ rOutDev.Push( vcl::PushFlags::LINECOLOR );
+ rOutDev.SetFillColor( COL_LIGHTGRAY );
+ rOutDev.SetLineColor( COL_LIGHTGRAY );
+
+ const tools::Rectangle aBackRect( aTopLeftRectPos, aBottomRightRectPos );
+ rOutDev.DrawRect( aBackRect );
+
+ rOutDev.Pop();
+ rOutDev.Pop();
+
+ if ( 0x200B == cChar )
+ {
+ const OUString aSlash( '/' );
+ const short nOldEscapement = aTmpFont.GetEscapement();
+ const sal_uInt8 nOldPropr = aTmpFont.GetPropr();
+
+ aTmpFont.SetEscapement( -20 );
+ aTmpFont.SetPropr( 25 );
+ aTmpFont.SetPhysFont(rOutDev);
+
+ const Size aSlashSize = aTmpFont.QuickGetTextSize( &rOutDev,
+ aSlash, 0, 1, nullptr );
+ Point aSlashPos( aTmpPos );
+ const tools::Long nAddX = nHalfBlankWidth - aSlashSize.Width() / 2;
+ setXDirectionAwareFrom(aSlashPos, aTopLeftRectPos);
+ adjustXDirectionAware(aSlashPos, nAddX);
+
+ aTmpFont.QuickDrawText( &rOutDev, aSlashPos, aSlash, 0, 1, {} );
+
+ aTmpFont.SetEscapement( nOldEscapement );
+ aTmpFont.SetPropr( nOldPropr );
+ aTmpFont.SetPhysFont(rOutDev);
+ }
+ }
+ }
+ }
+ }
+ else if ( rTextPortion.GetKind() == PortionKind::FIELD )
+ {
+ const EditCharAttrib* pAttr = pPortion->GetNode()->GetCharAttribs().FindFeature(nIndex);
+ assert( pAttr && "Field not found");
+ DBG_ASSERT( dynamic_cast< const SvxFieldItem* >( pAttr->GetItem() ) != nullptr, "Field of the wrong type! ");
+ aText = static_cast<const EditCharAttribField*>(pAttr)->GetFieldValue();
+ nTextStart = 0;
+ nTextLen = aText.getLength();
+ ExtraPortionInfo *pExtraInfo = rTextPortion.GetExtraInfos();
+ // Do not split the Fields into different lines while editing
+ // With EditView on Overlay bStripOnly is now set for stripping to
+ // primitives. To stay compatible in EditMode use pActiveView to detect
+ // when we are in EditMode. For whatever reason URLs are drawn as single
+ // line in edit mode, originally clipped against edit area (which is no
+ // longer done in Overlay mode and allows to *read* the URL).
+ // It would be difficult to change this due to needed adaptations in
+ // EditEngine (look for lineBreaksList creation)
+ if( nullptr == pActiveView && bStripOnly && !bParsingFields && pExtraInfo && !pExtraInfo->lineBreaksList.empty() )
+ {
+ bParsingFields = true;
+ itSubLines = pExtraInfo->lineBreaksList.begin();
+ }
+
+ if( bParsingFields )
+ {
+ if( itSubLines != pExtraInfo->lineBreaksList.begin() )
+ {
+ // only use GetMaxAscent(), pLine->GetHeight() will not
+ // proceed as needed (see PortionKind::TEXT above and nAdvanceY)
+ // what will lead to a compressed look with multiple lines
+ const sal_uInt16 nMaxAscent(pLine->GetMaxAscent());
+
+ aTmpPos += MoveToNextLine(aStartPos, nMaxAscent,
+ nColumn, aOrigin);
+ }
+ std::vector< sal_Int32 >::iterator curIt = itSubLines;
+ ++itSubLines;
+ if( itSubLines != pExtraInfo->lineBreaksList.end() )
+ {
+ nTextStart = *curIt;
+ nTextLen = *itSubLines - nTextStart;
+ }
+ else
+ {
+ nTextStart = *curIt;
+ nTextLen = nTextLen - nTextStart;
+ bParsingFields = false;
+
+ if (nLine + 1 < nLines)
+ {
+ // tdf#148966 don't paint the line break following a
+ // multiline field based on a compat flag
+ OutlinerEditEng* pOutlEditEng{ dynamic_cast<OutlinerEditEng*>(pEditEngine) };
+ if (pOutlEditEng
+ && pOutlEditEng->GetCompatFlag(SdrCompatibilityFlag::IgnoreBreakAfterMultilineField)
+ .value_or(false))
+ {
+ int nStartNextLine = pPortion->GetLines()[nLine + 1].GetStartPortion();
+ const TextPortion& rNextTextPortion = pPortion->GetTextPortions()[nStartNextLine];
+ if (rNextTextPortion.GetKind() == PortionKind::LINEBREAK)
+ ++nLine; //ignore the following linebreak
+ }
+ }
+ }
+ }
+
+ aTmpFont.SetPhysFont(*GetRefDevice());
+ aTmpFont.QuickGetTextSize( GetRefDevice(), aText, nTextStart, nTextLen,
+ &aTmpDXArray );
+ assert(aTmpDXArray.get_factor() == 1);
+ std::vector<sal_Int32>& rKernArray = aTmpDXArray.get_subunit_array();
+ pDXArray = rKernArray;
+
+ // add a meta file comment if we record to a metafile
+ if( bMetafileValid )
+ {
+ const SvxFieldItem* pFieldItem = dynamic_cast<const SvxFieldItem*>(pAttr->GetItem());
+ if( pFieldItem )
+ {
+ const SvxFieldData* pFieldData = pFieldItem->GetField();
+ if( pFieldData )
+ pMtf->AddAction( pFieldData->createBeginComment() );
+ }
+ }
+
+ }
+ else if ( rTextPortion.GetKind() == PortionKind::HYPHENATOR )
+ {
+ if ( rTextPortion.GetExtraValue() )
+ aText = OUString(rTextPortion.GetExtraValue());
+ aText += CH_HYPH;
+ nTextStart = 0;
+ nTextLen = aText.getLength();
+
+ // crash when accessing 0 pointer in pDXArray
+ aTmpFont.SetPhysFont(*GetRefDevice());
+ aTmpFont.QuickGetTextSize( GetRefDevice(), aText, 0, aText.getLength(),
+ &aTmpDXArray );
+ assert(aTmpDXArray.get_factor() == 1);
+ std::vector<sal_Int32>& rKernArray = aTmpDXArray.get_subunit_array();
+ pDXArray = rKernArray;
+ }
+
+ tools::Long nTxtWidth = rTextPortion.GetSize().Width();
+
+ Point aOutPos( aTmpPos );
+ Point aRedLineTmpPos = aTmpPos;
+ // In RTL portions spell markup pos should be at the start of the
+ // first chara as well. That is on the right end of the portion
+ if (rTextPortion.IsRightToLeft())
+ aRedLineTmpPos.AdjustX(rTextPortion.GetSize().Width() );
+
+ if ( bStripOnly )
+ {
+ EEngineData::WrongSpellVector aWrongSpellVector;
+
+ if(GetStatus().DoOnlineSpelling() && rTextPortion.GetLen())
+ {
+ WrongList* pWrongs = pPortion->GetNode()->GetWrongList();
+
+ if(pWrongs && !pWrongs->empty())
+ {
+ size_t nStart = nIndex, nEnd = 0;
+ bool bWrong = pWrongs->NextWrong(nStart, nEnd);
+ const size_t nMaxEnd(nIndex + rTextPortion.GetLen());
+
+ while(bWrong)
+ {
+ if(nStart >= nMaxEnd)
+ {
+ break;
+ }
+
+ if(nStart < o3tl::make_unsigned(nIndex))
+ {
+ nStart = nIndex;
+ }
+
+ if(nEnd > nMaxEnd)
+ {
+ nEnd = nMaxEnd;
+ }
+
+ // add to vector
+ aWrongSpellVector.emplace_back(nStart, nEnd);
+
+ // goto next index
+ nStart = nEnd + 1;
+
+ if(nEnd < nMaxEnd)
+ {
+ bWrong = pWrongs->NextWrong(nStart, nEnd);
+ }
+ else
+ {
+ bWrong = false;
+ }
+ }
+ }
+ }
+
+ const SvxFieldData* pFieldData = nullptr;
+
+ if(PortionKind::FIELD == rTextPortion.GetKind())
+ {
+ const EditCharAttrib* pAttr = pPortion->GetNode()->GetCharAttribs().FindFeature(nIndex);
+ const SvxFieldItem* pFieldItem = dynamic_cast<const SvxFieldItem*>(pAttr->GetItem());
+
+ if(pFieldItem)
+ {
+ pFieldData = pFieldItem->GetField();
+ }
+ }
+
+ // support for EOC, EOW, EOS TEXT comments. To support that,
+ // the locale is needed. With the locale and a XBreakIterator it is
+ // possible to re-create the text marking info on primitive level
+ const lang::Locale aLocale(GetLocale(EditPaM(pPortion->GetNode(), nIndex + 1)));
+
+ // create EOL and EOP bools
+ const bool bEndOfLine(nPortion == pLine->GetEndPortion());
+ const bool bEndOfParagraph(bEndOfLine && nLine + 1 == nLines);
+
+ // get Overline color (from ((const SvxOverlineItem*)GetItem())->GetColor() in
+ // consequence, but also already set at rOutDev)
+ const Color aOverlineColor(rOutDev.GetOverlineColor());
+
+ // get TextLine color (from ((const SvxUnderlineItem*)GetItem())->GetColor() in
+ // consequence, but also already set at rOutDev)
+ const Color aTextLineColor(rOutDev.GetTextLineColor());
+
+ // Unicode code points conversion according to ctl text numeral setting
+ aText = convertDigits(aText, nTextStart, nTextLen,
+ ImplCalcDigitLang(aTmpFont.GetLanguage()));
+
+ // StripPortions() data callback
+ GetEditEnginePtr()->DrawingText( aOutPos, aText, nTextStart, nTextLen, pDXArray, pKashidaArray,
+ aTmpFont, n, rTextPortion.GetRightToLeftLevel(),
+ !aWrongSpellVector.empty() ? &aWrongSpellVector : nullptr,
+ pFieldData,
+ bEndOfLine, bEndOfParagraph, // support for EOL/EOP TEXT comments
+ &aLocale,
+ aOverlineColor,
+ aTextLineColor);
+
+ // #108052# remember that EOP is written already for this ParaPortion
+ if(bEndOfParagraph)
+ {
+ bEndOfParagraphWritten = true;
+ }
+ }
+ else
+ {
+ short nEsc = aTmpFont.GetEscapement();
+ if ( nOrientation )
+ {
+ // In case of high/low do it yourself:
+ if ( aTmpFont.GetEscapement() )
+ {
+ tools::Long nDiff = aTmpFont.GetFontSize().Height() * aTmpFont.GetEscapement() / 100L;
+ adjustYDirectionAware(aOutPos, -nDiff);
+ aRedLineTmpPos = aOutPos;
+ aTmpFont.SetEscapement( 0 );
+ }
+
+ aOrigin.RotateAround(aOutPos, nOrientation);
+ aTmpFont.SetOrientation( aTmpFont.GetOrientation()+nOrientation );
+ aTmpFont.SetPhysFont(rOutDev);
+
+ }
+
+ // Take only what begins in the visible range:
+ // Important, because of a bug in some graphic cards
+ // when transparent font, output when negative
+ if ( nOrientation || ( !IsEffectivelyVertical() && ( ( aTmpPos.X() + nTxtWidth ) >= nFirstVisXPos ) )
+ || ( IsEffectivelyVertical() && ( ( aTmpPos.Y() + nTxtWidth ) >= nFirstVisYPos ) ) )
+ {
+ if ( nEsc && ( aTmpFont.GetUnderline() != LINESTYLE_NONE ) )
+ {
+ // Paint the high/low without underline,
+ // Display the Underline on the
+ // base line of the original font height...
+ // But only if there was something underlined before!
+ bool bSpecialUnderline = false;
+ EditCharAttrib* pPrev = pPortion->GetNode()->GetCharAttribs().FindAttrib( EE_CHAR_ESCAPEMENT, nIndex );
+ if ( pPrev )
+ {
+ SvxFont aDummy;
+ // Underscore in front?
+ if ( pPrev->GetStart() )
+ {
+ SeekCursor( pPortion->GetNode(), pPrev->GetStart(), aDummy );
+ if ( aDummy.GetUnderline() != LINESTYLE_NONE )
+ bSpecialUnderline = true;
+ }
+ if ( !bSpecialUnderline && ( pPrev->GetEnd() < pPortion->GetNode()->Len() ) )
+ {
+ SeekCursor( pPortion->GetNode(), pPrev->GetEnd()+1, aDummy );
+ if ( aDummy.GetUnderline() != LINESTYLE_NONE )
+ bSpecialUnderline = true;
+ }
+ }
+ if ( bSpecialUnderline )
+ {
+ Size aSz = aTmpFont.GetPhysTxtSize( &rOutDev, aText, nTextStart, nTextLen );
+ sal_uInt8 nProp = aTmpFont.GetPropr();
+ aTmpFont.SetEscapement( 0 );
+ aTmpFont.SetPropr( 100 );
+ aTmpFont.SetPhysFont(rOutDev);
+ OUStringBuffer aBlanks(nTextLen);
+ comphelper::string::padToLength( aBlanks, nTextLen, ' ' );
+ Point aUnderlinePos( aOutPos );
+ if ( nOrientation )
+ {
+ aUnderlinePos = aTmpPos;
+ aOrigin.RotateAround(aUnderlinePos, nOrientation);
+ }
+ rOutDev.DrawStretchText( aUnderlinePos, aSz.Width(), aBlanks.makeStringAndClear(), 0, nTextLen );
+
+ aTmpFont.SetUnderline( LINESTYLE_NONE );
+ if ( !nOrientation )
+ aTmpFont.SetEscapement( nEsc );
+ aTmpFont.SetPropr( nProp );
+ aTmpFont.SetPhysFont(rOutDev);
+ }
+ }
+ Point aRealOutPos( aOutPos );
+ if ( ( rTextPortion.GetKind() == PortionKind::TEXT )
+ && rTextPortion.GetExtraInfos() && rTextPortion.GetExtraInfos()->bCompressed
+ && rTextPortion.GetExtraInfos()->bFirstCharIsRightPunktuation )
+ {
+ aRealOutPos.AdjustX(rTextPortion.GetExtraInfos()->nPortionOffsetX );
+ }
+
+ // RTL portions with (#i37132#)
+ // compressed blank should not paint this blank:
+ if ( rTextPortion.IsRightToLeft() && nTextLen >= 2 &&
+ pDXArray[ nTextLen - 1 ] ==
+ pDXArray[ nTextLen - 2 ] &&
+ ' ' == aText[nTextStart + nTextLen - 1] )
+ --nTextLen;
+
+ // output directly
+ aTmpFont.QuickDrawText( &rOutDev, aRealOutPos, aText, nTextStart, nTextLen, pDXArray, pKashidaArray );
+
+ if ( bDrawFrame )
+ {
+ Point aTopLeft( aTmpPos );
+ aTopLeft.AdjustY( -(pLine->GetMaxAscent()) );
+ if ( nOrientation )
+ aOrigin.RotateAround(aTopLeft, nOrientation);
+ tools::Rectangle aRect( aTopLeft, rTextPortion.GetSize() );
+ rOutDev.DrawRect( aRect );
+ }
+
+ // PDF export:
+ if ( pPDFExtOutDevData )
+ {
+ if ( rTextPortion.GetKind() == PortionKind::FIELD )
+ {
+ const EditCharAttrib* pAttr = pPortion->GetNode()->GetCharAttribs().FindFeature(nIndex);
+ const SvxFieldItem* pFieldItem = dynamic_cast<const SvxFieldItem*>(pAttr->GetItem());
+ if( pFieldItem )
+ {
+ const SvxFieldData* pFieldData = pFieldItem->GetField();
+ if ( auto pUrlField = dynamic_cast< const SvxURLField* >( pFieldData ) )
+ {
+ Point aTopLeft( aTmpPos );
+ aTopLeft.AdjustY( -(pLine->GetMaxAscent()) );
+
+ tools::Rectangle aRect( aTopLeft, rTextPortion.GetSize() );
+ vcl::PDFExtOutDevBookmarkEntry aBookmark;
+ aBookmark.nLinkId = pPDFExtOutDevData->CreateLink(aRect, pUrlField->GetRepresentation());
+ aBookmark.aBookmark = pUrlField->GetURL();
+ std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = pPDFExtOutDevData->GetBookmarks();
+ rBookmarks.push_back( aBookmark );
+ }
+ }
+ }
+ }
+ }
+
+ const WrongList* const pWrongList = pPortion->GetNode()->GetWrongList();
+ if ( GetStatus().DoOnlineSpelling() && pWrongList && !pWrongList->empty() && rTextPortion.GetLen() )
+ {
+ {//#105750# adjust LinePos for superscript or subscript text
+ short _nEsc = aTmpFont.GetEscapement();
+ if( _nEsc )
+ {
+ tools::Long nShift = (_nEsc * aTmpFont.GetFontSize().Height()) / 100L;
+ adjustYDirectionAware(aRedLineTmpPos, -nShift);
+ }
+ }
+ Color aOldColor( rOutDev.GetLineColor() );
+ rOutDev.SetLineColor( GetColorConfig().GetColorValue( svtools::SPELL ).nColor );
+ lcl_DrawRedLines( rOutDev, aTmpFont.GetFontSize().Height(), aRedLineTmpPos, static_cast<size_t>(nIndex), static_cast<size_t>(nIndex) + rTextPortion.GetLen(), pDXArray, pPortion->GetNode()->GetWrongList(), nOrientation, aOrigin, IsEffectivelyVertical(), rTextPortion.IsRightToLeft() );
+ rOutDev.SetLineColor( aOldColor );
+ }
+ }
+
+ rOutDev.Pop();
+
+ if ( rTextPortion.GetKind() == PortionKind::FIELD )
+ {
+ // add a meta file comment if we record to a metafile
+ if( bMetafileValid )
+ {
+ const EditCharAttrib* pAttr = pPortion->GetNode()->GetCharAttribs().FindFeature(nIndex);
+ assert( pAttr && "Field not found" );
+
+ const SvxFieldItem* pFieldItem = dynamic_cast<const SvxFieldItem*>(pAttr->GetItem());
+ DBG_ASSERT( pFieldItem != nullptr, "Wrong type of field!" );
+
+ if( pFieldItem )
+ {
+ const SvxFieldData* pFieldData = pFieldItem->GetField();
+ if( pFieldData )
+ pMtf->AddAction( SvxFieldData::createEndComment() );
+ }
+ }
+
+ }
+
+ }
+ break;
+ case PortionKind::TAB:
+ {
+ if ( rTextPortion.GetExtraValue() && ( rTextPortion.GetExtraValue() != ' ' ) )
+ {
+ SeekCursor( pPortion->GetNode(), nIndex+1, aTmpFont, &rOutDev );
+ aTmpFont.SetTransparent( false );
+ aTmpFont.SetEscapement( 0 );
+ aTmpFont.SetPhysFont(rOutDev);
+ tools::Long nCharWidth = aTmpFont.QuickGetTextSize( &rOutDev,
+ OUString(rTextPortion.GetExtraValue()), 0, 1, {} ).Width();
+ sal_Int32 nChars = 2;
+ if( nCharWidth )
+ nChars = rTextPortion.GetSize().Width() / nCharWidth;
+ if ( nChars < 2 )
+ nChars = 2; // is compressed by DrawStretchText.
+ else if ( nChars == 2 )
+ nChars = 3; // looks better
+
+ OUStringBuffer aBuf(nChars);
+ comphelper::string::padToLength(aBuf, nChars, rTextPortion.GetExtraValue());
+ OUString aText(aBuf.makeStringAndClear());
+ aTmpFont.QuickDrawText( &rOutDev, aTmpPos, aText, 0, aText.getLength(), {} );
+ rOutDev.DrawStretchText( aTmpPos, rTextPortion.GetSize().Width(), aText );
+
+ if ( bStripOnly )
+ {
+ // create EOL and EOP bools
+ const bool bEndOfLine(nPortion == pLine->GetEndPortion());
+ const bool bEndOfParagraph(bEndOfLine && nLine + 1 == nLines);
+
+ const Color aOverlineColor(rOutDev.GetOverlineColor());
+ const Color aTextLineColor(rOutDev.GetTextLineColor());
+
+ // StripPortions() data callback
+ GetEditEnginePtr()->DrawingTab( aTmpPos,
+ rTextPortion.GetSize().Width(),
+ OUString(rTextPortion.GetExtraValue()),
+ aTmpFont, n, rTextPortion.GetRightToLeftLevel(),
+ bEndOfLine, bEndOfParagraph,
+ aOverlineColor, aTextLineColor);
+ }
+ }
+ else if ( bStripOnly )
+ {
+ // #i108052# When stripping, a callback for _empty_ paragraphs is also needed.
+ // This was optimized away (by not rendering the space-only tab portion), so do
+ // it manually here.
+ const bool bEndOfLine(nPortion == pLine->GetEndPortion());
+ const bool bEndOfParagraph(bEndOfLine && nLine + 1 == nLines);
+
+ const Color aOverlineColor(rOutDev.GetOverlineColor());
+ const Color aTextLineColor(rOutDev.GetTextLineColor());
+
+ GetEditEnginePtr()->DrawingText(
+ aTmpPos, OUString(), 0, 0, {}, {},
+ aTmpFont, n, 0,
+ nullptr,
+ nullptr,
+ bEndOfLine, bEndOfParagraph,
+ nullptr,
+ aOverlineColor,
+ aTextLineColor);
+ }
+ }
+ break;
+ case PortionKind::LINEBREAK: break;
+ }
+ if( bParsingFields )
+ nPortion--;
+ else
+ nIndex = nIndex + rTextPortion.GetLen();
+
+ }
+ }
+
+ if ((nLine != nLastLine ) && !maStatus.IsOutliner())
+ {
+ adjustYDirectionAware(aStartPos, nSBL);
+ }
+
+ // no more visible actions?
+ if (getYOverflowDirectionAware(aStartPos, aClipRect))
+ break;
+ }
+
+ if (!maStatus.IsOutliner())
+ {
+ const SvxULSpaceItem& rULItem = pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
+ tools::Long nUL = scaleYSpacingValue(rULItem.GetLower());
+ adjustYDirectionAware(aStartPos, nUL);
+ }
+
+ // #108052# Safer way for #i108052# and #i118881#: If for the current ParaPortion
+ // EOP is not written, do it now. This will be safer than before. It has shown
+ // that the reason for #i108052# was fixed/removed again, so this is a try to fix
+ // the number of paragraphs (and counting empty ones) now independent from the
+ // changes in EditEngine behaviour.
+ if(!bEndOfParagraphWritten && !bPaintBullet && bStripOnly)
+ {
+ const Color aOverlineColor(rOutDev.GetOverlineColor());
+ const Color aTextLineColor(rOutDev.GetTextLineColor());
+
+ GetEditEnginePtr()->DrawingText(
+ aTmpPos, OUString(), 0, 0, {}, {},
+ aTmpFont, n, 0,
+ nullptr,
+ nullptr,
+ false, true, // support for EOL/EOP TEXT comments
+ nullptr,
+ aOverlineColor,
+ aTextLineColor);
+ }
+ }
+ else
+ {
+ adjustYDirectionAware(aStartPos, nParaHeight);
+ }
+
+ if ( pPDFExtOutDevData )
+ pPDFExtOutDevData->EndStructureElement();
+
+ // no more visible actions?
+ if (getYOverflowDirectionAware(aStartPos, aClipRect))
+ break;
+ }
+}
+
+void ImpEditEngine::Paint( ImpEditView* pView, const tools::Rectangle& rRect, OutputDevice* pTargetDevice )
+{
+ if ( !IsUpdateLayout() || IsInUndo() )
+ return;
+
+ assert( pView && "No View - No Paint!" );
+
+ // Intersection of paint area and output area.
+ tools::Rectangle aClipRect( pView->GetOutputArea() );
+ aClipRect.Intersection( rRect );
+
+ OutputDevice& rTarget = pTargetDevice ? *pTargetDevice : *pView->GetWindow()->GetOutDev();
+
+ Point aStartPos;
+ if ( !IsEffectivelyVertical() )
+ aStartPos = pView->GetOutputArea().TopLeft();
+ else
+ {
+ if( IsTopToBottom() )
+ aStartPos = pView->GetOutputArea().TopRight();
+ else
+ aStartPos = pView->GetOutputArea().BottomLeft();
+ }
+ adjustXDirectionAware(aStartPos, -(pView->GetVisDocLeft()));
+ adjustYDirectionAware(aStartPos, -(pView->GetVisDocTop()));
+
+ // If Doc-width < Output Area,Width and not wrapped fields,
+ // the fields usually protrude if > line.
+ // (Not at the top, since there the Doc-width from formatting is already
+ // there)
+ if ( !IsEffectivelyVertical() && ( pView->GetOutputArea().GetWidth() > GetPaperSize().Width() ) )
+ {
+ tools::Long nMaxX = pView->GetOutputArea().Left() + GetPaperSize().Width();
+ if ( aClipRect.Left() > nMaxX )
+ return;
+ if ( aClipRect.Right() > nMaxX )
+ aClipRect.SetRight( nMaxX );
+ }
+
+ bool bClipRegion = rTarget.IsClipRegion();
+ vcl::Region aOldRegion = rTarget.GetClipRegion();
+ rTarget.IntersectClipRegion( aClipRect );
+
+ Paint(rTarget, aClipRect, aStartPos);
+
+ if ( bClipRegion )
+ rTarget.SetClipRegion( aOldRegion );
+ else
+ rTarget.SetClipRegion();
+
+ pView->DrawSelectionXOR(pView->GetEditSelection(), nullptr, &rTarget);
+}
+
+void ImpEditEngine::InsertContent( ContentNode* pNode, sal_Int32 nPos )
+{
+ DBG_ASSERT( pNode, "NULL-Pointer in InsertContent! " );
+ DBG_ASSERT( IsInUndo(), "InsertContent only for Undo()!" );
+ GetParaPortions().Insert(nPos, std::make_unique<ParaPortion>( pNode ));
+ maEditDoc.Insert(nPos, pNode);
+ if ( IsCallParaInsertedOrDeleted() )
+ GetEditEnginePtr()->ParagraphInserted( nPos );
+}
+
+EditPaM ImpEditEngine::SplitContent( sal_Int32 nNode, sal_Int32 nSepPos )
+{
+ ContentNode* pNode = maEditDoc.GetObject( nNode );
+ DBG_ASSERT( pNode, "Invalid Node in SplitContent" );
+ DBG_ASSERT( IsInUndo(), "SplitContent only for Undo()!" );
+ DBG_ASSERT( nSepPos <= pNode->Len(), "Index out of range: SplitContent" );
+ EditPaM aPaM( pNode, nSepPos );
+ return ImpInsertParaBreak( aPaM );
+}
+
+EditPaM ImpEditEngine::ConnectContents( sal_Int32 nLeftNode, bool bBackward )
+{
+ ContentNode* pLeftNode = maEditDoc.GetObject( nLeftNode );
+ ContentNode* pRightNode = maEditDoc.GetObject( nLeftNode+1 );
+ DBG_ASSERT( pLeftNode, "Invalid left node in ConnectContents ");
+ DBG_ASSERT( pRightNode, "Invalid right node in ConnectContents ");
+ return ImpConnectParagraphs( pLeftNode, pRightNode, bBackward );
+}
+
+bool ImpEditEngine::SetUpdateLayout( bool bUp, EditView* pCurView, bool bForceUpdate )
+{
+ const bool bPrevUpdateLayout = mbUpdateLayout;
+ const bool mbChanged = (mbUpdateLayout != bUp);
+
+ // When switching from true to false, all selections were visible,
+ // => paint over
+ // the other hand, were all invisible => paint
+ // If !bFormatted, e.g. after SetText, then if UpdateMode=true
+ // formatting is not needed immediately, probably because more text is coming.
+ // At latest it is formatted at a Paint/CalcTextWidth.
+ mbUpdateLayout = bUp;
+ if ( mbUpdateLayout && ( mbChanged || bForceUpdate ) )
+ FormatAndLayout( pCurView );
+ return bPrevUpdateLayout;
+}
+
+void ImpEditEngine::ShowParagraph( sal_Int32 nParagraph, bool bShow )
+{
+ ParaPortion* pPPortion = GetParaPortions().SafeGetObject( nParagraph );
+ DBG_ASSERT( pPPortion, "ShowParagraph: Paragraph does not exist! ");
+ if ( !(pPPortion && ( pPPortion->IsVisible() != bShow )) )
+ return;
+
+ pPPortion->SetVisible( bShow );
+
+ if ( !bShow )
+ {
+ // Mark as deleted, so that no selection will end or begin at
+ // this paragraph...
+ aDeletedNodes.push_back(std::make_unique<DeletedNodeInfo>( pPPortion->GetNode(), nParagraph ));
+ UpdateSelections();
+ // The region below will not be invalidated if UpdateMode = sal_False!
+ // If anyway, then save as sal_False before SetVisible !
+ }
+
+ if ( bShow && ( pPPortion->IsInvalid() || !pPPortion->nHeight ) )
+ {
+ if ( !GetTextRanger() )
+ {
+ if ( pPPortion->IsInvalid() )
+ {
+ CreateLines( nParagraph, 0 ); // 0: No TextRanger
+ }
+ else
+ {
+ CalcHeight( pPPortion );
+ }
+ nCurTextHeight += pPPortion->GetHeight();
+ }
+ else
+ {
+ nCurTextHeight = 0x7fffffff;
+ }
+ }
+
+ pPPortion->SetMustRepaint( true );
+ if ( IsUpdateLayout() && !IsInUndo() && !GetTextRanger() )
+ {
+ aInvalidRect = tools::Rectangle( Point( 0, GetParaPortions().GetYOffset( pPPortion ) ),
+ Point( GetPaperSize().Width(), nCurTextHeight ) );
+ UpdateViews( GetActiveView() );
+ }
+}
+
+EditSelection ImpEditEngine::MoveParagraphs( Range aOldPositions, sal_Int32 nNewPos, EditView* pCurView )
+{
+ DBG_ASSERT( GetParaPortions().Count() != 0, "No paragraphs found: MoveParagraphs" );
+ if ( GetParaPortions().Count() == 0 )
+ return EditSelection();
+ aOldPositions.Normalize();
+
+ EditSelection aSel( ImpMoveParagraphs( aOldPositions, nNewPos ) );
+
+ if ( nNewPos >= GetParaPortions().Count() )
+ nNewPos = GetParaPortions().Count() - 1;
+
+ // Where the paragraph was inserted it has to be properly redrawn:
+ // Where the paragraph was removed it has to be properly redrawn:
+ // ( and correspondingly in between as well...)
+ if ( pCurView && IsUpdateLayout() )
+ {
+ // in this case one can redraw directly without invalidating the
+ // Portions
+ sal_Int32 nFirstPortion = std::min( static_cast<sal_Int32>(aOldPositions.Min()), nNewPos );
+ sal_Int32 nLastPortion = std::max( static_cast<sal_Int32>(aOldPositions.Max()), nNewPos );
+
+ ParaPortion* pUpperPortion = GetParaPortions().SafeGetObject( nFirstPortion );
+ ParaPortion* pLowerPortion = GetParaPortions().SafeGetObject( nLastPortion );
+ if (pUpperPortion && pLowerPortion)
+ {
+ aInvalidRect = tools::Rectangle(); // make empty
+ aInvalidRect.SetLeft( 0 );
+ aInvalidRect.SetRight(GetColumnWidth(maPaperSize));
+ aInvalidRect.SetTop( GetParaPortions().GetYOffset( pUpperPortion ) );
+ aInvalidRect.SetBottom( GetParaPortions().GetYOffset( pLowerPortion ) + pLowerPortion->GetHeight() );
+
+ UpdateViews( pCurView );
+ }
+ }
+ else
+ {
+ // redraw from the upper invalid position
+ sal_Int32 nFirstInvPara = std::min( static_cast<sal_Int32>(aOldPositions.Min()), nNewPos );
+ InvalidateFromParagraph( nFirstInvPara );
+ }
+ return aSel;
+}
+
+void ImpEditEngine::InvalidateFromParagraph( sal_Int32 nFirstInvPara )
+{
+ // The following paragraphs are not invalidated, since ResetHeight()
+ // => size change => all the following are re-issued anyway.
+ ParaPortion* pTmpPortion;
+ if ( nFirstInvPara != 0 )
+ {
+ pTmpPortion = GetParaPortions()[nFirstInvPara-1];
+ pTmpPortion->MarkInvalid( pTmpPortion->GetNode()->Len(), 0 );
+ }
+ else
+ {
+ pTmpPortion = GetParaPortions()[0];
+ pTmpPortion->MarkSelectionInvalid( 0 );
+ }
+ pTmpPortion->ResetHeight();
+}
+
+IMPL_LINK_NOARG(ImpEditEngine, StatusTimerHdl, Timer *, void)
+{
+ CallStatusHdl();
+}
+
+void ImpEditEngine::CallStatusHdl()
+{
+ if ( aStatusHdlLink.IsSet() && bool(maStatus.GetStatusWord()) )
+ {
+ // The Status has to be reset before the Call,
+ // since other Flags might be set in the handler...
+ EditStatus aTmpStatus( maStatus );
+ maStatus.Clear();
+ aStatusHdlLink.Call( aTmpStatus );
+ aStatusTimer.Stop(); // If called by hand...
+ }
+}
+
+ContentNode* ImpEditEngine::GetPrevVisNode( ContentNode const * pCurNode )
+{
+ const ParaPortion* pPortion = FindParaPortion( pCurNode );
+ DBG_ASSERT( pPortion, "GetPrevVisibleNode: No matching portion!" );
+ pPortion = GetPrevVisPortion( pPortion );
+ if ( pPortion )
+ return pPortion->GetNode();
+ return nullptr;
+}
+
+ContentNode* ImpEditEngine::GetNextVisNode( ContentNode const * pCurNode )
+{
+ const ParaPortion* pPortion = FindParaPortion( pCurNode );
+ DBG_ASSERT( pPortion, "GetNextVisibleNode: No matching portion!" );
+ pPortion = GetNextVisPortion( pPortion );
+ if ( pPortion )
+ return pPortion->GetNode();
+ return nullptr;
+}
+
+const ParaPortion* ImpEditEngine::GetPrevVisPortion( const ParaPortion* pCurPortion ) const
+{
+ sal_Int32 nPara = GetParaPortions().GetPos( pCurPortion );
+ DBG_ASSERT( nPara < GetParaPortions().Count() , "Portion not found: GetPrevVisPortion" );
+ const ParaPortion* pPortion = nPara ? GetParaPortions()[--nPara] : nullptr;
+ while ( pPortion && !pPortion->IsVisible() )
+ pPortion = nPara ? GetParaPortions()[--nPara] : nullptr;
+
+ return pPortion;
+}
+
+const ParaPortion* ImpEditEngine::GetNextVisPortion( const ParaPortion* pCurPortion ) const
+{
+ sal_Int32 nPara = GetParaPortions().GetPos( pCurPortion );
+ DBG_ASSERT( nPara < GetParaPortions().Count() , "Portion not found: GetPrevVisNode" );
+ const ParaPortion* pPortion = GetParaPortions().SafeGetObject( ++nPara );
+ while ( pPortion && !pPortion->IsVisible() )
+ pPortion = GetParaPortions().SafeGetObject( ++nPara );
+
+ return pPortion;
+}
+
+tools::Long ImpEditEngine::CalcVertLineSpacing(Point& rStartPos) const
+{
+ tools::Long nTotalOccupiedHeight = 0;
+ sal_Int32 nTotalLineCount = 0;
+ const ParaPortionList& rParaPortions = GetParaPortions();
+ sal_Int32 nParaCount = rParaPortions.Count();
+
+ for (sal_Int32 i = 0; i < nParaCount; ++i)
+ {
+ if (GetVerJustification(i) != SvxCellVerJustify::Block)
+ // All paragraphs must have the block justification set.
+ return 0;
+
+ const ParaPortion* pPortion = rParaPortions[i];
+ nTotalOccupiedHeight += pPortion->GetFirstLineOffset();
+
+ const SvxLineSpacingItem& rLSItem = pPortion->GetNode()->GetContentAttribs().GetItem(EE_PARA_SBL);
+ sal_uInt16 nSBL = ( rLSItem.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Fix )
+ ? scaleYSpacingValue(rLSItem.GetInterLineSpace()) : 0;
+
+ const SvxULSpaceItem& rULItem = pPortion->GetNode()->GetContentAttribs().GetItem(EE_PARA_ULSPACE);
+ tools::Long nUL = scaleYSpacingValue(rULItem.GetLower());
+
+ const EditLineList& rLines = pPortion->GetLines();
+ sal_Int32 nLineCount = rLines.Count();
+ nTotalLineCount += nLineCount;
+ for (sal_Int32 j = 0; j < nLineCount; ++j)
+ {
+ const EditLine& rLine = rLines[j];
+ nTotalOccupiedHeight += rLine.GetHeight();
+ if (j < nLineCount-1)
+ nTotalOccupiedHeight += nSBL;
+ nTotalOccupiedHeight += nUL;
+ }
+ }
+
+ tools::Long nTotalSpace = getHeightDirectionAware(maPaperSize);
+ nTotalSpace -= nTotalOccupiedHeight;
+ if (nTotalSpace <= 0 || nTotalLineCount <= 1)
+ return 0;
+
+ // Shift the text to the right for the asian layout mode.
+ if (IsEffectivelyVertical())
+ adjustYDirectionAware(rStartPos, -nTotalSpace);
+
+ return nTotalSpace / (nTotalLineCount-1);
+}
+
+EditPaM ImpEditEngine::InsertParagraph( sal_Int32 nPara )
+{
+ EditPaM aPaM;
+ if ( nPara != 0 )
+ {
+ ContentNode* pNode = GetEditDoc().GetObject( nPara-1 );
+ if ( !pNode )
+ pNode = GetEditDoc().GetObject( GetEditDoc().Count() - 1 );
+ assert(pNode && "Not a single paragraph in InsertParagraph ?");
+ aPaM = EditPaM( pNode, pNode->Len() );
+ }
+ else
+ {
+ ContentNode* pNode = GetEditDoc().GetObject( 0 );
+ aPaM = EditPaM( pNode, 0 );
+ }
+
+ return ImpInsertParaBreak( aPaM );
+}
+
+std::optional<EditSelection> ImpEditEngine::SelectParagraph( sal_Int32 nPara )
+{
+ std::optional<EditSelection> pSel;
+ ContentNode* pNode = GetEditDoc().GetObject( nPara );
+ SAL_WARN_IF( !pNode, "editeng", "Paragraph does not exist: SelectParagraph" );
+ if ( pNode )
+ pSel.emplace( EditPaM( pNode, 0 ), EditPaM( pNode, pNode->Len() ) );
+
+ return pSel;
+}
+
+void ImpEditEngine::FormatAndLayout( EditView* pCurView, bool bCalledFromUndo )
+{
+ if (mbDowning)
+ return;
+
+ if ( IsInUndo() )
+ IdleFormatAndLayout( pCurView );
+ else
+ {
+ if (bCalledFromUndo)
+ // in order to make bullet points that have had their styles changed, redraw themselves
+ for ( sal_Int32 nPortion = 0; nPortion < GetParaPortions().Count(); nPortion++ )
+ GetParaPortions()[nPortion]->MarkInvalid( 0, 0 );
+ FormatDoc();
+ UpdateViews( pCurView );
+ }
+
+ EENotify aNotify(EE_NOTIFY_PROCESSNOTIFICATIONS);
+ GetNotifyHdl().Call(aNotify);
+}
+
+void ImpEditEngine::SetFlatMode( bool bFlat )
+{
+ if ( bFlat != maStatus.UseCharAttribs() )
+ return;
+
+ if ( !bFlat )
+ maStatus.TurnOnFlags( EEControlBits::USECHARATTRIBS );
+ else
+ maStatus.TurnOffFlags( EEControlBits::USECHARATTRIBS );
+
+ maEditDoc.CreateDefFont( !bFlat );
+
+ FormatFullDoc();
+ UpdateViews();
+ if ( pActiveView )
+ pActiveView->ShowCursor();
+}
+
+void ImpEditEngine::setScale(double fFontScaleX, double fFontScaleY, double fSpacingScaleX, double fSpacingScaleY)
+{
+ bool bChanged;
+
+ if (!IsEffectivelyVertical())
+ {
+ bChanged = mfFontScaleX != fFontScaleX || mfFontScaleY != fFontScaleY ||
+ mfSpacingScaleX != fSpacingScaleX || mfSpacingScaleY != fSpacingScaleY;
+ mfFontScaleX = fFontScaleX;
+ mfFontScaleY = fFontScaleY;
+ mfSpacingScaleX = fSpacingScaleX;
+ mfSpacingScaleY = fSpacingScaleY;
+ }
+ else
+ {
+ bChanged = mfFontScaleX != fFontScaleY || mfFontScaleY != fFontScaleX ||
+ mfSpacingScaleX != fSpacingScaleY || mfSpacingScaleY != fSpacingScaleX;
+ mfFontScaleX = fFontScaleY;
+ mfFontScaleY = fFontScaleX;
+ mfSpacingScaleX = fSpacingScaleY;
+ mfSpacingScaleY = fSpacingScaleX;
+ }
+
+ if (bChanged && maStatus.DoStretch())
+ {
+ FormatFullDoc();
+ // (potentially) need everything redrawn
+ aInvalidRect = tools::Rectangle(0, 0, 1000000, 1000000);
+ UpdateViews(GetActiveView());
+ }
+}
+
+const SvxNumberFormat* ImpEditEngine::GetNumberFormat( const ContentNode *pNode ) const
+{
+ const SvxNumberFormat *pRes = nullptr;
+
+ if (pNode)
+ {
+ // get index of paragraph
+ sal_Int32 nPara = GetEditDoc().GetPos( pNode );
+ DBG_ASSERT( nPara < EE_PARA_NOT_FOUND, "node not found in array" );
+ if (nPara < EE_PARA_NOT_FOUND)
+ {
+ // the called function may be overridden by an OutlinerEditEng
+ // object to provide
+ // access to the SvxNumberFormat of the Outliner.
+ // The EditEngine implementation will just return 0.
+ pRes = pEditEngine->GetNumberFormat( nPara );
+ }
+ }
+
+ return pRes;
+}
+
+sal_Int32 ImpEditEngine::GetSpaceBeforeAndMinLabelWidth(
+ const ContentNode *pNode,
+ sal_Int32 *pnSpaceBefore, sal_Int32 *pnMinLabelWidth ) const
+{
+ // nSpaceBefore matches the ODF attribute text:space-before
+ // nMinLabelWidth matches the ODF attribute text:min-label-width
+
+ const SvxNumberFormat *pNumFmt = GetNumberFormat( pNode );
+
+ // if no number format was found we have no Outliner or the numbering level
+ // within the Outliner is -1 which means no number format should be applied.
+ // Thus the default values to be returned are 0.
+ sal_Int32 nSpaceBefore = 0;
+ sal_Int32 nMinLabelWidth = 0;
+
+ if (pNumFmt)
+ {
+ nMinLabelWidth = -pNumFmt->GetFirstLineOffset();
+ nSpaceBefore = pNumFmt->GetAbsLSpace() - nMinLabelWidth;
+ DBG_ASSERT( nMinLabelWidth >= 0, "ImpEditEngine::GetSpaceBeforeAndMinLabelWidth: min-label-width < 0 encountered" );
+ }
+ if (pnSpaceBefore)
+ *pnSpaceBefore = nSpaceBefore;
+ if (pnMinLabelWidth)
+ *pnMinLabelWidth = nMinLabelWidth;
+
+ return nSpaceBefore + nMinLabelWidth;
+}
+
+const SvxLRSpaceItem& ImpEditEngine::GetLRSpaceItem( ContentNode* pNode )
+{
+ return pNode->GetContentAttribs().GetItem( maStatus.IsOutliner() ? EE_PARA_OUTLLRSPACE : EE_PARA_LRSPACE );
+}
+
+// select a representative text language for the digit type according to the
+// text numeral setting:
+LanguageType ImpEditEngine::ImplCalcDigitLang(LanguageType eCurLang)
+{
+ if (utl::ConfigManager::IsFuzzing())
+ return LANGUAGE_ENGLISH_US;
+
+ // #114278# Also setting up digit language from Svt options
+ // (cannot reliably inherit the outdev's setting)
+
+ LanguageType eLang = eCurLang;
+ const SvtCTLOptions::TextNumerals nCTLTextNumerals = SvtCTLOptions::GetCTLTextNumerals();
+
+ if ( SvtCTLOptions::NUMERALS_HINDI == nCTLTextNumerals )
+ eLang = LANGUAGE_ARABIC_SAUDI_ARABIA;
+ else if ( SvtCTLOptions::NUMERALS_ARABIC == nCTLTextNumerals )
+ eLang = LANGUAGE_ENGLISH;
+ else if ( SvtCTLOptions::NUMERALS_SYSTEM == nCTLTextNumerals )
+ eLang = Application::GetSettings().GetLanguageTag().getLanguageType();
+
+ return eLang;
+}
+
+OUString ImpEditEngine::convertDigits(std::u16string_view rString, sal_Int32 nStt, sal_Int32 nLen, LanguageType eDigitLang)
+{
+ OUStringBuffer aBuf(rString);
+ for (sal_Int32 nIdx = nStt, nEnd = nStt + nLen; nIdx < nEnd; ++nIdx)
+ {
+ sal_Unicode cChar = aBuf[nIdx];
+ if (cChar >= '0' && cChar <= '9')
+ aBuf[nIdx] = GetLocalizedChar(cChar, eDigitLang);
+ }
+ return aBuf.makeStringAndClear();
+}
+
+// Either sets the digit mode at the output device
+void ImpEditEngine::ImplInitDigitMode(OutputDevice& rOutDev, LanguageType eCurLang)
+{
+ rOutDev.SetDigitLanguage(ImplCalcDigitLang(eCurLang));
+}
+
+void ImpEditEngine::ImplInitLayoutMode(OutputDevice& rOutDev, sal_Int32 nPara, sal_Int32 nIndex)
+{
+ bool bCTL = false;
+ bool bR2L = false;
+ if ( nIndex == -1 )
+ {
+ bCTL = HasScriptType( nPara, i18n::ScriptType::COMPLEX );
+ bR2L = IsRightToLeft( nPara );
+ }
+ else
+ {
+ ContentNode* pNode = GetEditDoc().GetObject( nPara );
+ short nScriptType = GetI18NScriptType( EditPaM( pNode, nIndex+1 ) );
+ bCTL = nScriptType == i18n::ScriptType::COMPLEX;
+ // this change was discussed in issue 37190
+ bR2L = (GetRightToLeft( nPara, nIndex + 1) % 2) != 0;
+ // it also works for issue 55927
+ }
+
+ vcl::text::ComplexTextLayoutFlags nLayoutMode = rOutDev.GetLayoutMode();
+
+ // We always use the left position for DrawText()
+ nLayoutMode &= ~vcl::text::ComplexTextLayoutFlags::BiDiRtl;
+
+ if ( !bCTL && !bR2L)
+ {
+ // No Bidi checking necessary
+ nLayoutMode |= vcl::text::ComplexTextLayoutFlags::BiDiStrong;
+ }
+ else
+ {
+ // Bidi checking necessary
+ // Don't use BIDI_STRONG, VCL must do some checks.
+ nLayoutMode &= ~vcl::text::ComplexTextLayoutFlags::BiDiStrong;
+
+ if ( bR2L )
+ nLayoutMode |= vcl::text::ComplexTextLayoutFlags::BiDiRtl|vcl::text::ComplexTextLayoutFlags::TextOriginLeft;
+ }
+
+ rOutDev.SetLayoutMode( nLayoutMode );
+
+ // #114278# Also setting up digit language from Svt options
+ // (cannot reliably inherit the outdev's setting)
+ LanguageType eLang = Application::GetSettings().GetLanguageTag().getLanguageType();
+ ImplInitDigitMode(rOutDev, eLang);
+}
+
+Reference < i18n::XBreakIterator > const & ImpEditEngine::ImplGetBreakIterator() const
+{
+ if ( !xBI.is() )
+ {
+ Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ xBI = i18n::BreakIterator::create( xContext );
+ }
+ return xBI;
+}
+
+Reference < i18n::XExtendedInputSequenceChecker > const & ImpEditEngine::ImplGetInputSequenceChecker() const
+{
+ if ( !xISC.is() )
+ {
+ Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ xISC = i18n::InputSequenceChecker::create( xContext );
+ }
+ return xISC;
+}
+
+Color ImpEditEngine::GetAutoColor() const
+{
+ Color aColor;
+
+ if (comphelper::LibreOfficeKit::isActive() && SfxViewShell::Current())
+ {
+ // Get document background color from current view instead
+ aColor = SfxViewShell::Current()->GetColorConfigColor(svtools::DOCCOLOR);
+ if (aColor.IsDark())
+ aColor = COL_WHITE;
+ else
+ aColor = COL_BLACK;
+ }
+ else
+ {
+ aColor = GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor;
+
+ if ( GetBackgroundColor() != COL_AUTO )
+ {
+ if ( GetBackgroundColor().IsDark() && aColor.IsDark() )
+ aColor = COL_WHITE;
+ else if ( GetBackgroundColor().IsBright() && aColor.IsBright() )
+ aColor = COL_BLACK;
+ }
+ }
+
+ return aColor;
+}
+
+bool ImpEditEngine::ImplCalcAsianCompression(ContentNode* pNode,
+ TextPortion* pTextPortion, sal_Int32 nStartPos,
+ sal_Int32* pDXArray, sal_uInt16 n100thPercentFromMax,
+ bool bManipulateDXArray)
+{
+ DBG_ASSERT( GetAsianCompressionMode() != CharCompressType::NONE, "ImplCalcAsianCompression - Why?" );
+ DBG_ASSERT( pTextPortion->GetLen(), "ImplCalcAsianCompression - Empty Portion?" );
+
+ // Percent is 1/100 Percent...
+ if ( n100thPercentFromMax == 10000 )
+ pTextPortion->SetExtraInfos( nullptr );
+
+ bool bCompressed = false;
+
+ if ( GetI18NScriptType( EditPaM( pNode, nStartPos+1 ) ) == i18n::ScriptType::ASIAN )
+ {
+ tools::Long nNewPortionWidth = pTextPortion->GetSize().Width();
+ sal_Int32 nPortionLen = pTextPortion->GetLen();
+ for ( sal_Int32 n = 0; n < nPortionLen; n++ )
+ {
+ AsianCompressionFlags nType = GetCharTypeForCompression( pNode->GetChar( n+nStartPos ) );
+
+ bool bCompressPunctuation = ( nType == AsianCompressionFlags::PunctuationLeft ) || ( nType == AsianCompressionFlags::PunctuationRight );
+ bool bCompressKana = ( nType == AsianCompressionFlags::Kana ) && ( GetAsianCompressionMode() == CharCompressType::PunctuationAndKana );
+
+ // create Extra infos only if needed...
+ if ( bCompressPunctuation || bCompressKana )
+ {
+ if ( !pTextPortion->GetExtraInfos() )
+ {
+ ExtraPortionInfo* pExtraInfos = new ExtraPortionInfo;
+ pTextPortion->SetExtraInfos( pExtraInfos );
+ pExtraInfos->nOrgWidth = pTextPortion->GetSize().Width();
+ pExtraInfos->nAsianCompressionTypes = AsianCompressionFlags::Normal;
+ }
+ pTextPortion->GetExtraInfos()->nMaxCompression100thPercent = n100thPercentFromMax;
+ pTextPortion->GetExtraInfos()->nAsianCompressionTypes |= nType;
+
+ tools::Long nOldCharWidth;
+ if ( (n+1) < nPortionLen )
+ {
+ nOldCharWidth = pDXArray[n];
+ }
+ else
+ {
+ if ( bManipulateDXArray )
+ nOldCharWidth = nNewPortionWidth - pTextPortion->GetExtraInfos()->nPortionOffsetX;
+ else
+ nOldCharWidth = pTextPortion->GetExtraInfos()->nOrgWidth;
+ }
+ nOldCharWidth -= ( n ? pDXArray[n-1] : 0 );
+
+ tools::Long nCompress = 0;
+
+ if ( bCompressPunctuation )
+ {
+ nCompress = nOldCharWidth / 2;
+ }
+ else // Kana
+ {
+ nCompress = nOldCharWidth / 10;
+ }
+
+ if ( n100thPercentFromMax != 10000 )
+ {
+ nCompress *= n100thPercentFromMax;
+ nCompress /= 10000;
+ }
+
+ if ( nCompress )
+ {
+ bCompressed = true;
+ nNewPortionWidth -= nCompress;
+ pTextPortion->GetExtraInfos()->bCompressed = true;
+
+
+ // Special handling for rightpunctuation: For the 'compression' we must
+ // start the output before the normal char position...
+ if ( bManipulateDXArray && ( pTextPortion->GetLen() > 1 ) )
+ {
+ if ( !pTextPortion->GetExtraInfos()->pOrgDXArray )
+ pTextPortion->GetExtraInfos()->SaveOrgDXArray( pDXArray, pTextPortion->GetLen()-1 );
+
+ if ( nType == AsianCompressionFlags::PunctuationRight )
+ {
+ // If it's the first char, I must handle it in Paint()...
+ if ( n )
+ {
+ // -1: No entry for the last character
+ for ( sal_Int32 i = n-1; i < (nPortionLen-1); i++ )
+ pDXArray[i] -= nCompress;
+ }
+ else
+ {
+ pTextPortion->GetExtraInfos()->bFirstCharIsRightPunktuation = true;
+ pTextPortion->GetExtraInfos()->nPortionOffsetX = -nCompress;
+ }
+ }
+ else
+ {
+ // -1: No entry for the last character
+ for ( sal_Int32 i = n; i < (nPortionLen-1); i++ )
+ pDXArray[i] -= nCompress;
+ }
+ }
+ }
+ }
+ }
+
+ if ( bCompressed && ( n100thPercentFromMax == 10000 ) )
+ pTextPortion->GetExtraInfos()->nWidthFullCompression = nNewPortionWidth;
+
+ pTextPortion->setWidth(nNewPortionWidth);
+
+ if ( pTextPortion->GetExtraInfos() && ( n100thPercentFromMax != 10000 ) )
+ {
+ // Maybe rounding errors in nNewPortionWidth, assure that width not bigger than expected
+ tools::Long nShrink = pTextPortion->GetExtraInfos()->nOrgWidth - pTextPortion->GetExtraInfos()->nWidthFullCompression;
+ nShrink *= n100thPercentFromMax;
+ nShrink /= 10000;
+ tools::Long nNewWidth = pTextPortion->GetExtraInfos()->nOrgWidth - nShrink;
+ if ( nNewWidth < pTextPortion->GetSize().Width() )
+ pTextPortion->setWidth(nNewWidth);
+ }
+ }
+ return bCompressed;
+}
+
+
+void ImpEditEngine::ImplExpandCompressedPortions( EditLine* pLine, ParaPortion* pParaPortion, tools::Long nRemainingWidth )
+{
+ bool bFoundCompressedPortion = false;
+ tools::Long nCompressed = 0;
+ std::vector<TextPortion*> aCompressedPortions;
+
+ sal_Int32 nPortion = pLine->GetEndPortion();
+ TextPortion* pTP = &pParaPortion->GetTextPortions()[ nPortion ];
+ while ( pTP && ( pTP->GetKind() == PortionKind::TEXT ) )
+ {
+ if ( pTP->GetExtraInfos() && pTP->GetExtraInfos()->bCompressed )
+ {
+ bFoundCompressedPortion = true;
+ nCompressed += pTP->GetExtraInfos()->nOrgWidth - pTP->GetSize().Width();
+ aCompressedPortions.push_back(pTP);
+ }
+ pTP = ( nPortion > pLine->GetStartPortion() ) ? &pParaPortion->GetTextPortions()[ --nPortion ] : nullptr;
+ }
+
+ if ( !bFoundCompressedPortion )
+ return;
+
+ tools::Long nCompressPercent = 0;
+ if ( nCompressed > nRemainingWidth )
+ {
+ nCompressPercent = nCompressed - nRemainingWidth;
+ DBG_ASSERT( nCompressPercent < 200000, "ImplExpandCompressedPortions - Overflow!" );
+ nCompressPercent *= 10000;
+ nCompressPercent /= nCompressed;
+ }
+
+ for (TextPortion* pTP2 : aCompressedPortions)
+ {
+ pTP = pTP2;
+ pTP->GetExtraInfos()->bCompressed = false;
+ pTP->setWidth(pTP->GetExtraInfos()->nOrgWidth);
+ if ( nCompressPercent )
+ {
+ sal_Int32 nTxtPortion = pParaPortion->GetTextPortions().GetPos( pTP );
+ sal_Int32 nTxtPortionStart = pParaPortion->GetTextPortions().GetStartPos( nTxtPortion );
+ DBG_ASSERT( nTxtPortionStart >= pLine->GetStart(), "Portion doesn't belong to the line!!!" );
+ sal_Int32* pDXArray = pLine->GetCharPosArray().data() + (nTxtPortionStart - pLine->GetStart());
+ if ( pTP->GetExtraInfos()->pOrgDXArray )
+ memcpy( pDXArray, pTP->GetExtraInfos()->pOrgDXArray.get(), (pTP->GetLen()-1)*sizeof(sal_Int32) );
+ ImplCalcAsianCompression( pParaPortion->GetNode(), pTP, nTxtPortionStart, pDXArray, static_cast<sal_uInt16>(nCompressPercent), true );
+ }
+ }
+}
+
+void ImpEditEngine::ImplUpdateOverflowingParaNum(tools::Long nPaperHeight)
+{
+ tools::Long nY = 0;
+ tools::Long nPH;
+
+ for ( sal_Int32 nPara = 0; nPara < GetParaPortions().Count(); nPara++ ) {
+ ParaPortion* pPara = GetParaPortions()[nPara];
+ nPH = pPara->GetHeight();
+ nY += nPH;
+ if ( nY > nPaperHeight /*nCurTextHeight*/ ) // found first paragraph overflowing
+ {
+ mnOverflowingPara = nPara;
+ SAL_INFO("editeng.chaining", "[CHAINING] Setting first overflowing #Para#: " << nPara);
+ ImplUpdateOverflowingLineNum( nPaperHeight, nPara, nY-nPH);
+ return;
+ }
+ }
+}
+
+void ImpEditEngine::ImplUpdateOverflowingLineNum(tools::Long nPaperHeight,
+ sal_uInt32 nOverflowingPara,
+ tools::Long nHeightBeforeOverflowingPara)
+{
+ tools::Long nY = nHeightBeforeOverflowingPara;
+ tools::Long nLH;
+
+ ParaPortion *pPara = GetParaPortions()[nOverflowingPara];
+
+ // Like UpdateOverflowingParaNum but for each line in the first
+ // overflowing paragraph.
+ for ( sal_Int32 nLine = 0; nLine < pPara->GetLines().Count(); nLine++ ) {
+ // XXX: We must use a reference here because the copy constructor resets the height
+ EditLine &aLine = pPara->GetLines()[nLine];
+ nLH = aLine.GetHeight();
+ nY += nLH;
+
+ // Debugging output
+ if (nLine == 0) {
+ SAL_INFO("editeng.chaining", "[CHAINING] First line has height " << nLH);
+ }
+
+ if ( nY > nPaperHeight ) // found first line overflowing
+ {
+ mnOverflowingLine = nLine;
+ SAL_INFO("editeng.chaining", "[CHAINING] Setting first overflowing -Line- to: " << nLine);
+ return;
+ }
+ }
+
+ assert(false && "You should never get here");
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/impedit4.cxx b/editeng/source/editeng/impedit4.cxx
new file mode 100644
index 0000000000..57b3d65c54
--- /dev/null
+++ b/editeng/source/editeng/impedit4.cxx
@@ -0,0 +1,3141 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <svl/srchitem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/cmapitem.hxx>
+#include <editeng/lspcitem.hxx>
+#include <editeng/tstpitem.hxx>
+
+#include "eertfpar.hxx"
+#include <editeng/editeng.hxx>
+#include "impedit.hxx"
+#include <editeng/editview.hxx>
+#include "eehtml.hxx"
+#include "editobj2.hxx"
+#include <i18nlangtag/lang.h>
+#include <sal/log.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+
+#include <editxml.hxx>
+
+#include <editeng/autokernitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/kernitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include "textconv.hxx"
+#include <rtl/tencinfo.h>
+#include <svtools/rtfout.hxx>
+#include <tools/stream.hxx>
+#include <edtspell.hxx>
+#include <editeng/unolingu.hxx>
+#include <com/sun/star/linguistic2/XThesaurus.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <unotools/transliterationwrapper.hxx>
+#include <unotools/textsearch.hxx>
+#include <comphelper/processfactory.hxx>
+#include <vcl/help.hxx>
+#include <vcl/metric.hxx>
+#include <svtools/rtfkeywd.hxx>
+#include <editeng/edtdlg.hxx>
+
+#include <cstddef>
+#include <memory>
+#include <unordered_map>
+#include <vector>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::linguistic2;
+
+
+EditPaM ImpEditEngine::Read(SvStream& rInput, const OUString& rBaseURL, EETextFormat eFormat, const EditSelection& rSel, SvKeyValueIterator* pHTTPHeaderAttrs)
+{
+ bool _bUpdate = SetUpdateLayout( false );
+ EditPaM aPaM;
+ if ( eFormat == EETextFormat::Text )
+ aPaM = ReadText( rInput, rSel );
+ else if ( eFormat == EETextFormat::Rtf )
+ aPaM = ReadRTF( rInput, rSel );
+ else if ( eFormat == EETextFormat::Xml )
+ aPaM = ReadXML( rInput, rSel );
+ else if ( eFormat == EETextFormat::Html )
+ aPaM = ReadHTML( rInput, rBaseURL, rSel, pHTTPHeaderAttrs );
+ else
+ {
+ OSL_FAIL( "Read: Unknown Format" );
+ }
+
+ FormatFullDoc(); // perhaps a simple format is enough?
+ SetUpdateLayout( _bUpdate );
+
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::ReadText( SvStream& rInput, EditSelection aSel )
+{
+ if ( aSel.HasRange() )
+ aSel = ImpDeleteSelection( aSel );
+ EditPaM aPaM = aSel.Max();
+
+ OUString aTmpStr;
+ bool bDone = rInput.ReadByteStringLine( aTmpStr, rInput.GetStreamCharSet() );
+ while ( bDone )
+ {
+ aPaM = ImpInsertText( EditSelection( aPaM, aPaM ), aTmpStr );
+ aPaM = ImpInsertParaBreak( aPaM );
+ bDone = rInput.ReadByteStringLine( aTmpStr, rInput.GetStreamCharSet() );
+ }
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::ReadXML( SvStream& rInput, EditSelection aSel )
+{
+ if ( aSel.HasRange() )
+ aSel = ImpDeleteSelection( aSel );
+
+ ESelection aESel = CreateESel( aSel );
+
+ return ::SvxReadXML( *GetEditEnginePtr(), rInput, aESel );
+}
+
+EditPaM ImpEditEngine::ReadRTF( SvStream& rInput, EditSelection aSel )
+{
+ if ( aSel.HasRange() )
+ aSel = ImpDeleteSelection( aSel );
+
+ // The SvRTF parser expects the Which-mapping passed on in the pool, not
+ // dependent on a secondary.
+ SfxItemPool* pPool = &maEditDoc.GetItemPool();
+ while (pPool->GetSecondaryPool() && pPool->GetName() != "EditEngineItemPool")
+ {
+ pPool = pPool->GetSecondaryPool();
+ }
+
+ DBG_ASSERT(pPool && pPool->GetName() == "EditEngineItemPool",
+ "ReadRTF: no EditEnginePool!");
+
+ EditRTFParserRef xPrsr = new EditRTFParser(rInput, aSel, *pPool, pEditEngine);
+ SvParserState eState = xPrsr->CallParser();
+ if ( ( eState != SvParserState::Accepted ) && ( !rInput.GetError() ) )
+ {
+ rInput.SetError( EE_READWRITE_WRONGFORMAT );
+ return aSel.Min();
+ }
+ return xPrsr->GetCurPaM();
+}
+
+EditPaM ImpEditEngine::ReadHTML( SvStream& rInput, const OUString& rBaseURL, EditSelection aSel, SvKeyValueIterator* pHTTPHeaderAttrs )
+{
+ if ( aSel.HasRange() )
+ aSel = ImpDeleteSelection( aSel );
+
+ EditHTMLParserRef xPrsr = new EditHTMLParser( rInput, rBaseURL, pHTTPHeaderAttrs );
+ SvParserState eState = xPrsr->CallParser(pEditEngine, aSel.Max());
+ if ( ( eState != SvParserState::Accepted ) && ( !rInput.GetError() ) )
+ {
+ rInput.SetError( EE_READWRITE_WRONGFORMAT );
+ return aSel.Min();
+ }
+ return xPrsr->GetCurSelection().Max();
+}
+
+void ImpEditEngine::Write(SvStream& rOutput, EETextFormat eFormat, const EditSelection& rSel)
+{
+ if ( !rOutput.IsWritable() )
+ rOutput.SetError( SVSTREAM_WRITE_ERROR );
+
+ if ( rOutput.GetError() )
+ return;
+
+ if ( eFormat == EETextFormat::Text )
+ WriteText( rOutput, rSel );
+ else if ( eFormat == EETextFormat::Rtf )
+ WriteRTF( rOutput, rSel );
+ else if ( eFormat == EETextFormat::Xml )
+ WriteXML( rOutput, rSel );
+ else if ( eFormat == EETextFormat::Html )
+ ;
+ else
+ {
+ OSL_FAIL( "Write: Unknown Format" );
+ }
+}
+
+ErrCode ImpEditEngine::WriteText( SvStream& rOutput, EditSelection aSel )
+{
+ sal_Int32 nStartNode, nEndNode;
+ bool bRange = aSel.HasRange();
+ if ( bRange )
+ {
+ aSel.Adjust( maEditDoc );
+ nStartNode = maEditDoc.GetPos( aSel.Min().GetNode() );
+ nEndNode = maEditDoc.GetPos( aSel.Max().GetNode() );
+ }
+ else
+ {
+ nStartNode = 0;
+ nEndNode = maEditDoc.Count()-1;
+ }
+
+ // iterate over the paragraphs ...
+ for ( sal_Int32 nNode = nStartNode; nNode <= nEndNode; nNode++ )
+ {
+ ContentNode* pNode = maEditDoc.GetObject( nNode );
+ DBG_ASSERT( pNode, "Node not found: Search&Replace" );
+
+ sal_Int32 nStartPos = 0;
+ sal_Int32 nEndPos = pNode->Len();
+ if ( bRange )
+ {
+ if ( nNode == nStartNode )
+ nStartPos = aSel.Min().GetIndex();
+ if ( nNode == nEndNode ) // can also be == nStart!
+ nEndPos = aSel.Max().GetIndex();
+ }
+ OUString aTmpStr = EditDoc::GetParaAsString( pNode, nStartPos, nEndPos );
+ rOutput.WriteByteStringLine( aTmpStr, rOutput.GetStreamCharSet() );
+ }
+
+ return rOutput.GetError();
+}
+
+bool ImpEditEngine::WriteItemListAsRTF( ItemList& rLst, SvStream& rOutput, sal_Int32 nPara, sal_Int32 nPos,
+ std::vector<std::unique_ptr<SvxFontItem>>& rFontTable, SvxColorList& rColorList )
+{
+ const SfxPoolItem* pAttrItem = rLst.First();
+ while ( pAttrItem )
+ {
+ WriteItemAsRTF( *pAttrItem, rOutput, nPara, nPos,rFontTable, rColorList );
+ pAttrItem = rLst.Next();
+ }
+ return rLst.Count() != 0;
+}
+
+static void lcl_FindValidAttribs( ItemList& rLst, ContentNode* pNode, sal_Int32 nIndex, sal_uInt16 nScriptType )
+{
+ std::size_t nAttr = 0;
+ EditCharAttrib* pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
+ while ( pAttr && ( pAttr->GetStart() <= nIndex ) )
+ {
+ // Start is checked in while ...
+ if ( pAttr->GetEnd() > nIndex )
+ {
+ if ( IsScriptItemValid( pAttr->GetItem()->Which(), nScriptType ) )
+ rLst.Insert( pAttr->GetItem() );
+ }
+ nAttr++;
+ pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
+ }
+}
+
+void ImpEditEngine::WriteXML(SvStream& rOutput, const EditSelection& rSel)
+{
+ ESelection aESel = CreateESel(rSel);
+
+ SvxWriteXML( *GetEditEnginePtr(), rOutput, aESel );
+}
+
+ErrCode ImpEditEngine::WriteRTF( SvStream& rOutput, EditSelection aSel )
+{
+ assert( IsUpdateLayout() && "WriteRTF for UpdateMode = sal_False!" );
+ CheckIdleFormatter();
+ if ( !IsFormatted() )
+ FormatDoc();
+
+ sal_Int32 nStartNode, nEndNode;
+ aSel.Adjust( maEditDoc );
+
+ nStartNode = maEditDoc.GetPos( aSel.Min().GetNode() );
+ nEndNode = maEditDoc.GetPos( aSel.Max().GetNode() );
+
+ // RTF header ...
+ rOutput.WriteChar( '{' ) ;
+
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_RTF );
+
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_ANSI );
+ rtl_TextEncoding eDestEnc = RTL_TEXTENCODING_MS_1252;
+
+ // Generate and write out Font table ...
+ std::vector<std::unique_ptr<SvxFontItem>> aFontTable;
+ // default font must be up front, so DEF font in RTF
+ aFontTable.emplace_back( new SvxFontItem( maEditDoc.GetItemPool().GetDefaultItem( EE_CHAR_FONTINFO ) ) );
+ aFontTable.emplace_back( new SvxFontItem( maEditDoc.GetItemPool().GetDefaultItem( EE_CHAR_FONTINFO_CJK ) ) );
+ aFontTable.emplace_back( new SvxFontItem( maEditDoc.GetItemPool().GetDefaultItem( EE_CHAR_FONTINFO_CTL ) ) );
+ for ( sal_uInt16 nScriptType = 0; nScriptType < 3; nScriptType++ )
+ {
+ sal_uInt16 nWhich = EE_CHAR_FONTINFO;
+ if ( nScriptType == 1 )
+ nWhich = EE_CHAR_FONTINFO_CJK;
+ else if ( nScriptType == 2 )
+ nWhich = EE_CHAR_FONTINFO_CTL;
+
+ for (const SfxPoolItem* pItem : maEditDoc.GetItemPool().GetItemSurrogates(nWhich))
+ {
+ SvxFontItem const*const pFontItem = static_cast<const SvxFontItem*>(pItem);
+ bool bAlreadyExist = false;
+ size_t nTestMax = nScriptType ? aFontTable.size() : 1;
+ for ( size_t nTest = 0; !bAlreadyExist && ( nTest < nTestMax ); nTest++ )
+ {
+ bAlreadyExist = *aFontTable[ nTest ] == *pFontItem;
+ }
+
+ if ( !bAlreadyExist )
+ aFontTable.emplace_back( new SvxFontItem( *pFontItem ) );
+ }
+ }
+
+ rOutput << endl;
+ rOutput.WriteChar( '{' ).WriteOString( OOO_STRING_SVTOOLS_RTF_FONTTBL );
+ for ( std::vector<SvxFontItem*>::size_type j = 0; j < aFontTable.size(); j++ )
+ {
+ SvxFontItem* pFontItem = aFontTable[ j ].get();
+ rOutput.WriteChar( '{' );
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_F );
+ rOutput.WriteNumberAsString( j );
+ switch ( pFontItem->GetFamily() )
+ {
+ case FAMILY_DONTKNOW: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_FNIL );
+ break;
+ case FAMILY_DECORATIVE: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_FDECOR );
+ break;
+ case FAMILY_MODERN: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_FMODERN );
+ break;
+ case FAMILY_ROMAN: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_FROMAN );
+ break;
+ case FAMILY_SCRIPT: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_FSCRIPT );
+ break;
+ case FAMILY_SWISS: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_FSWISS );
+ break;
+ default:
+ break;
+ }
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_FPRQ );
+ sal_uInt16 nVal = 0;
+ switch( pFontItem->GetPitch() )
+ {
+ case PITCH_FIXED: nVal = 1; break;
+ case PITCH_VARIABLE: nVal = 2; break;
+ default:
+ break;
+ }
+ rOutput.WriteNumberAsString( nVal );
+
+ rtl_TextEncoding eChrSet = pFontItem->GetCharSet();
+ // tdf#47679 OpenSymbol is not encoded in Symbol Encoding
+ // and anyway we always attempt to write as eDestEnc
+ // of RTL_TEXTENCODING_MS_1252 and pay no attention
+ // on export what encoding we claim to use for these
+ // fonts.
+ if (IsOpenSymbol(pFontItem->GetFamilyName()))
+ {
+ SAL_WARN_IF(eChrSet == RTL_TEXTENCODING_SYMBOL, "editeng", "OpenSymbol should not have charset of RTL_TEXTENCODING_SYMBOL in new documents");
+ eChrSet = RTL_TEXTENCODING_UTF8;
+ }
+ DBG_ASSERT( eChrSet != 9, "SystemCharSet?!" );
+ if( RTL_TEXTENCODING_DONTKNOW == eChrSet )
+ eChrSet = osl_getThreadTextEncoding();
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_FCHARSET );
+ rOutput.WriteNumberAsString( rtl_getBestWindowsCharsetFromTextEncoding( eChrSet ) );
+
+ rOutput.WriteChar( ' ' );
+ RTFOutFuncs::Out_String( rOutput, pFontItem->GetFamilyName(), eDestEnc );
+ rOutput.WriteOString( ";}" );
+ }
+ rOutput.WriteChar( '}' );
+ rOutput << endl;
+
+ // Write out ColorList ...
+ SvxColorList aColorList;
+ // COL_AUTO should be the default color, always put it first
+ aColorList.emplace_back(COL_AUTO);
+ SvxColorItem const& rDefault(maEditDoc.GetItemPool().GetDefaultItem(EE_CHAR_COLOR));
+ if (rDefault.GetValue() != COL_AUTO) // is the default always AUTO?
+ {
+ aColorList.push_back(rDefault.GetValue());
+ }
+ for (const SfxPoolItem* pItem : maEditDoc.GetItemPool().GetItemSurrogates(EE_CHAR_COLOR))
+ {
+ auto pColorItem(dynamic_cast<SvxColorItem const*>(pItem));
+ if (pColorItem && pColorItem->GetValue() != COL_AUTO) // may be null!
+ {
+ aColorList.push_back(pColorItem->GetValue());
+ }
+ }
+
+ rOutput.WriteChar( '{' ).WriteOString( OOO_STRING_SVTOOLS_RTF_COLORTBL );
+ for ( SvxColorList::size_type j = 0; j < aColorList.size(); j++ )
+ {
+ Color const color = aColorList[j];
+ if (color != COL_AUTO) // auto is represented by "empty" element
+ {
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_RED );
+ rOutput.WriteNumberAsString( color.GetRed() );
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_GREEN );
+ rOutput.WriteNumberAsString( color.GetGreen() );
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_BLUE );
+ rOutput.WriteNumberAsString( color.GetBlue() );
+ }
+ rOutput.WriteChar( ';' );
+ }
+ rOutput.WriteChar( '}' );
+ rOutput << endl;
+
+ std::unordered_map<SfxStyleSheetBase*, sal_uInt32> aStyleSheetToIdMap;
+ // StyleSheets...
+ if ( GetStyleSheetPool() )
+ {
+ std::shared_ptr<SfxStyleSheetIterator> aSSSIterator = std::make_shared<SfxStyleSheetIterator>(GetStyleSheetPool(),
+ SfxStyleFamily::All);
+ // fill aStyleSheetToIdMap
+ sal_uInt32 nId = 1;
+ for ( SfxStyleSheetBase* pStyle = aSSSIterator->First(); pStyle;
+ pStyle = aSSSIterator->Next() )
+ {
+ aStyleSheetToIdMap[pStyle] = nId;
+ nId++;
+ }
+
+ if ( aSSSIterator->Count() )
+ {
+
+ sal_uInt32 nStyle = 0;
+ rOutput.WriteChar( '{' ).WriteOString( OOO_STRING_SVTOOLS_RTF_STYLESHEET );
+
+ for ( SfxStyleSheetBase* pStyle = aSSSIterator->First(); pStyle;
+ pStyle = aSSSIterator->Next() )
+ {
+
+ rOutput << endl;
+ rOutput.WriteChar( '{' ).WriteOString( OOO_STRING_SVTOOLS_RTF_S );
+ sal_uInt32 nNumber = nStyle + 1;
+ rOutput.WriteNumberAsString( nNumber );
+
+ // Attribute, also from Parent!
+ for ( sal_uInt16 nParAttr = EE_PARA_START; nParAttr <= EE_CHAR_END; nParAttr++ )
+ {
+ if ( pStyle->GetItemSet().GetItemState( nParAttr ) == SfxItemState::SET )
+ {
+ const SfxPoolItem& rItem = pStyle->GetItemSet().Get( nParAttr );
+ WriteItemAsRTF( rItem, rOutput, 0, 0, aFontTable, aColorList );
+ }
+ }
+
+ // Parent ... (only if necessary)
+ if ( !pStyle->GetParent().isEmpty() && ( pStyle->GetParent() != pStyle->GetName() ) )
+ {
+ SfxStyleSheet* pParent = static_cast<SfxStyleSheet*>(GetStyleSheetPool()->Find( pStyle->GetParent(), pStyle->GetFamily() ));
+ DBG_ASSERT( pParent, "Parent not found!" );
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_SBASEDON );
+ nNumber = aStyleSheetToIdMap.find(pParent)->second;
+ rOutput.WriteNumberAsString( nNumber );
+ }
+
+ // Next Style... (more)
+ // we assume that we have only SfxStyleSheet in the pool
+ SfxStyleSheet* pNext = static_cast<SfxStyleSheet*>(pStyle);
+ if ( !pStyle->GetFollow().isEmpty() && ( pStyle->GetFollow() != pStyle->GetName() ) )
+ pNext = static_cast<SfxStyleSheet*>(GetStyleSheetPool()->Find( pStyle->GetFollow(), pStyle->GetFamily() ));
+
+ DBG_ASSERT( pNext, "Next not found!" );
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_SNEXT );
+ nNumber = aStyleSheetToIdMap.find(pNext)->second;
+ rOutput.WriteNumberAsString( nNumber );
+
+ // Name of the template...
+ rOutput.WriteOString( " " );
+ RTFOutFuncs::Out_String( rOutput, pStyle->GetName(), eDestEnc );
+ rOutput.WriteOString( ";}" );
+ nStyle++;
+ }
+ rOutput.WriteChar( '}' );
+ rOutput << endl;
+ }
+ }
+
+ // Write the pool defaults in advance ...
+ rOutput.WriteChar( '{' ).WriteOString( OOO_STRING_SVTOOLS_RTF_IGNORE ).WriteOString( "\\EditEnginePoolDefaults" );
+ for ( sal_uInt16 nPoolDefItem = EE_PARA_START; nPoolDefItem <= EE_CHAR_END; nPoolDefItem++)
+ {
+ const SfxPoolItem& rItem = maEditDoc.GetItemPool().GetDefaultItem( nPoolDefItem );
+ WriteItemAsRTF( rItem, rOutput, 0, 0, aFontTable, aColorList );
+ }
+ rOutput.WriteChar( '}' ) << endl;
+
+ // DefTab:
+ MapMode aTwpMode( MapUnit::MapTwip );
+ sal_uInt16 nDefTabTwps = static_cast<sal_uInt16>(GetRefDevice()->LogicToLogic(
+ Point( maEditDoc.GetDefTab(), 0 ),
+ &GetRefMapMode(), &aTwpMode ).X());
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_DEFTAB );
+ rOutput.WriteNumberAsString( nDefTabTwps );
+ rOutput << endl;
+
+ // iterate over the paragraphs ...
+ rOutput.WriteChar( '{' ) << endl;
+ for ( sal_Int32 nNode = nStartNode; nNode <= nEndNode; nNode++ )
+ {
+ ContentNode* pNode = maEditDoc.GetObject( nNode );
+ DBG_ASSERT( pNode, "Node not found: Search&Replace" );
+
+ // The paragraph attributes in advance ...
+ bool bAttr = false;
+
+ // Template?
+ if ( pNode->GetStyleSheet() )
+ {
+ // Number of template
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_S );
+ sal_uInt32 nNumber = aStyleSheetToIdMap.find(pNode->GetStyleSheet())->second;
+ rOutput.WriteNumberAsString( nNumber );
+
+ // All Attribute
+ // Attribute, also from Parent!
+ for ( sal_uInt16 nParAttr = EE_PARA_START; nParAttr <= EE_CHAR_END; nParAttr++ )
+ {
+ if ( pNode->GetStyleSheet()->GetItemSet().GetItemState( nParAttr ) == SfxItemState::SET )
+ {
+ const SfxPoolItem& rItem = pNode->GetStyleSheet()->GetItemSet().Get( nParAttr );
+ WriteItemAsRTF( rItem, rOutput, nNode, 0, aFontTable, aColorList );
+ bAttr = true;
+ }
+ }
+ }
+
+ for ( sal_uInt16 nParAttr = EE_PARA_START; nParAttr <= EE_CHAR_END; nParAttr++ )
+ {
+ // Now where stylesheet processing, only hard paragraph attributes!
+ if ( pNode->GetContentAttribs().GetItems().GetItemState( nParAttr ) == SfxItemState::SET )
+ {
+ const SfxPoolItem& rItem = pNode->GetContentAttribs().GetItems().Get( nParAttr );
+ WriteItemAsRTF( rItem, rOutput, nNode, 0, aFontTable, aColorList );
+ bAttr = true;
+ }
+ }
+ if ( bAttr )
+ rOutput.WriteChar( ' ' ); // Separator
+
+ ItemList aAttribItems;
+ ParaPortion* pParaPortion = FindParaPortion( pNode );
+ DBG_ASSERT( pParaPortion, "Portion not found: WriteRTF" );
+
+ sal_Int32 nIndex = 0;
+ sal_Int32 nStartPos = 0;
+ sal_Int32 nEndPos = pNode->Len();
+ sal_Int32 nStartPortion = 0;
+ sal_Int32 nEndPortion = pParaPortion->GetTextPortions().Count() - 1;
+ bool bFinishPortion = false;
+ sal_Int32 nPortionStart;
+
+ if ( nNode == nStartNode )
+ {
+ nStartPos = aSel.Min().GetIndex();
+ nStartPortion = pParaPortion->GetTextPortions().FindPortion( nStartPos, nPortionStart );
+ if ( nStartPos != 0 )
+ {
+ aAttribItems.Clear();
+ lcl_FindValidAttribs( aAttribItems, pNode, nStartPos, GetI18NScriptType( EditPaM( pNode, 0 ) ) );
+ if ( aAttribItems.Count() )
+ {
+ // These attributes may not apply to the entire paragraph:
+ rOutput.WriteChar( '{' );
+ WriteItemListAsRTF( aAttribItems, rOutput, nNode, nStartPos, aFontTable, aColorList );
+ bFinishPortion = true;
+ }
+ aAttribItems.Clear();
+ }
+ }
+ if ( nNode == nEndNode ) // can also be == nStart!
+ {
+ nEndPos = aSel.Max().GetIndex();
+ nEndPortion = pParaPortion->GetTextPortions().FindPortion( nEndPos, nPortionStart );
+ }
+
+ const EditCharAttrib* pNextFeature = pNode->GetCharAttribs().FindFeature(nIndex);
+ // start at 0, so the index is right ...
+ for ( sal_Int32 n = 0; n <= nEndPortion; n++ )
+ {
+ const TextPortion& rTextPortion = pParaPortion->GetTextPortions()[n];
+ if ( n < nStartPortion )
+ {
+ nIndex = nIndex + rTextPortion.GetLen();
+ continue;
+ }
+
+ if ( pNextFeature && ( pNextFeature->GetStart() == nIndex ) && ( pNextFeature->GetItem()->Which() != EE_FEATURE_FIELD ) )
+ {
+ WriteItemAsRTF( *pNextFeature->GetItem(), rOutput, nNode, nIndex, aFontTable, aColorList );
+ pNextFeature = pNode->GetCharAttribs().FindFeature( pNextFeature->GetStart() + 1 );
+ }
+ else
+ {
+ aAttribItems.Clear();
+ sal_uInt16 nScriptTypeI18N = GetI18NScriptType( EditPaM( pNode, nIndex+1 ) );
+ SvtScriptType nScriptType = SvtLanguageOptions::FromI18NToSvtScriptType(nScriptTypeI18N);
+ if ( !n || IsScriptChange( EditPaM( pNode, nIndex ) ) )
+ {
+ SfxItemSet aAttribs = GetAttribs( nNode, nIndex+1, nIndex+1 );
+ aAttribItems.Insert( &aAttribs.Get( GetScriptItemId( EE_CHAR_FONTINFO, nScriptType ) ) );
+ aAttribItems.Insert( &aAttribs.Get( GetScriptItemId( EE_CHAR_FONTHEIGHT, nScriptType ) ) );
+ aAttribItems.Insert( &aAttribs.Get( GetScriptItemId( EE_CHAR_WEIGHT, nScriptType ) ) );
+ aAttribItems.Insert( &aAttribs.Get( GetScriptItemId( EE_CHAR_ITALIC, nScriptType ) ) );
+ aAttribItems.Insert( &aAttribs.Get( GetScriptItemId( EE_CHAR_LANGUAGE, nScriptType ) ) );
+ }
+ // Insert hard attribs AFTER CJK attribs...
+ lcl_FindValidAttribs( aAttribItems, pNode, nIndex, nScriptTypeI18N );
+
+ rOutput.WriteChar( '{' );
+ if ( WriteItemListAsRTF( aAttribItems, rOutput, nNode, nIndex, aFontTable, aColorList ) )
+ rOutput.WriteChar( ' ' );
+
+ sal_Int32 nS = nIndex;
+ sal_Int32 nE = nIndex + rTextPortion.GetLen();
+ if ( n == nStartPortion )
+ nS = nStartPos;
+ if ( n == nEndPortion )
+ nE = nEndPos;
+
+ OUString aRTFStr = EditDoc::GetParaAsString( pNode, nS, nE);
+ RTFOutFuncs::Out_String( rOutput, aRTFStr, eDestEnc );
+ rOutput.WriteChar( '}' );
+ }
+ if ( bFinishPortion )
+ {
+ rOutput.WriteChar( '}' );
+ bFinishPortion = false;
+ }
+
+ nIndex = nIndex + rTextPortion.GetLen();
+ }
+
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_PAR ).WriteOString( OOO_STRING_SVTOOLS_RTF_PARD ).WriteOString( OOO_STRING_SVTOOLS_RTF_PLAIN );
+ rOutput << endl;
+ }
+ // RTF-trailer ...
+ rOutput.WriteOString( "}}" ); // 1xparentheses paragraphs, 1xparentheses RTF document
+
+ aFontTable.clear();
+
+ return rOutput.GetError();
+}
+
+
+void ImpEditEngine::WriteItemAsRTF( const SfxPoolItem& rItem, SvStream& rOutput, sal_Int32 nPara, sal_Int32 nPos,
+ std::vector<std::unique_ptr<SvxFontItem>>& rFontTable, SvxColorList& rColorList )
+{
+ sal_uInt16 nWhich = rItem.Which();
+ switch ( nWhich )
+ {
+ case EE_PARA_WRITINGDIR:
+ {
+ const SvxFrameDirectionItem& rWritingMode = static_cast<const SvxFrameDirectionItem&>(rItem);
+ if ( rWritingMode.GetValue() == SvxFrameDirection::Horizontal_RL_TB )
+ rOutput.WriteOString( "\\rtlpar" );
+ else
+ rOutput.WriteOString( "\\ltrpar" );
+ }
+ break;
+ case EE_PARA_OUTLLEVEL:
+ {
+ sal_Int32 nLevel = static_cast<const SfxInt16Item&>(rItem).GetValue();
+ if( nLevel >= 0 )
+ {
+ rOutput.WriteOString( "\\level" );
+ rOutput.WriteNumberAsString( nLevel );
+ }
+ }
+ break;
+ case EE_PARA_OUTLLRSPACE:
+ case EE_PARA_LRSPACE:
+ {
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_FI );
+ sal_Int32 nTxtFirst = static_cast<const SvxLRSpaceItem&>(rItem).GetTextFirstLineOffset();
+ nTxtFirst = LogicToTwips( nTxtFirst );
+ rOutput.WriteNumberAsString( nTxtFirst );
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_LI );
+ sal_uInt32 nTxtLeft = static_cast< sal_uInt32 >(static_cast<const SvxLRSpaceItem&>(rItem).GetTextLeft());
+ nTxtLeft = static_cast<sal_uInt32>(LogicToTwips( nTxtLeft ));
+ rOutput.WriteNumberAsString( nTxtLeft );
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_RI );
+ sal_uInt32 nTxtRight = static_cast<const SvxLRSpaceItem&>(rItem).GetRight();
+ nTxtRight = LogicToTwips( nTxtRight);
+ rOutput.WriteNumberAsString( nTxtRight );
+ }
+ break;
+ case EE_PARA_ULSPACE:
+ {
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_SB );
+ sal_uInt32 nUpper = static_cast<const SvxULSpaceItem&>(rItem).GetUpper();
+ nUpper = static_cast<sal_uInt32>(LogicToTwips( nUpper ));
+ rOutput.WriteNumberAsString( nUpper );
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_SA );
+ sal_uInt32 nLower = static_cast<const SvxULSpaceItem&>(rItem).GetLower();
+ nLower = LogicToTwips( nLower );
+ rOutput.WriteNumberAsString( nLower );
+ }
+ break;
+ case EE_PARA_SBL:
+ {
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_SL );
+ sal_Int32 nVal = static_cast<const SvxLineSpacingItem&>(rItem).GetLineHeight();
+ char cMult = '0';
+ if ( static_cast<const SvxLineSpacingItem&>(rItem).GetInterLineSpaceRule() == SvxInterLineSpaceRule::Prop )
+ {
+ // From where do I get the value now?
+ // The SwRTF parser is based on a 240 Font!
+ nVal = static_cast<const SvxLineSpacingItem&>(rItem).GetPropLineSpace();
+ nVal *= 240;
+ nVal /= 100;
+ cMult = '1';
+ }
+ rOutput.WriteNumberAsString( nVal );
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_SLMULT ).WriteChar( cMult );
+ }
+ break;
+ case EE_PARA_JUST:
+ {
+ SvxAdjust eJustification = static_cast<const SvxAdjustItem&>(rItem).GetAdjust();
+ switch ( eJustification )
+ {
+ case SvxAdjust::Center: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_QC );
+ break;
+ case SvxAdjust::Right: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_QR );
+ break;
+ default: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_QL );
+ break;
+ }
+ }
+ break;
+ case EE_PARA_TABS:
+ {
+ const SvxTabStopItem& rTabs = static_cast<const SvxTabStopItem&>(rItem);
+ for ( sal_uInt16 i = 0; i < rTabs.Count(); i++ )
+ {
+ const SvxTabStop& rTab = rTabs[i];
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_TX );
+ rOutput.WriteNumberAsString( LogicToTwips( rTab.GetTabPos() ) );
+ }
+ }
+ break;
+ case EE_CHAR_COLOR:
+ {
+ SvxColorList::const_iterator const iter = std::find(
+ rColorList.begin(), rColorList.end(),
+ static_cast<SvxColorItem const&>(rItem).GetValue());
+ assert(iter != rColorList.end());
+ sal_uInt32 const n = iter - rColorList.begin();
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_CF );
+ rOutput.WriteNumberAsString( n );
+ }
+ break;
+ case EE_CHAR_FONTINFO:
+ case EE_CHAR_FONTINFO_CJK:
+ case EE_CHAR_FONTINFO_CTL:
+ {
+ sal_uInt32 n = 0;
+ for (size_t i = 0; i < rFontTable.size(); ++i)
+ {
+ if (*rFontTable[i] == rItem)
+ {
+ n = i;
+ break;
+ }
+ }
+
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_F );
+ rOutput.WriteNumberAsString( n );
+ }
+ break;
+ case EE_CHAR_FONTHEIGHT:
+ case EE_CHAR_FONTHEIGHT_CJK:
+ case EE_CHAR_FONTHEIGHT_CTL:
+ {
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_FS );
+ sal_Int32 nHeight = static_cast<const SvxFontHeightItem&>(rItem).GetHeight();
+ nHeight = LogicToTwips( nHeight );
+ // Twips => HalfPoints
+ nHeight /= 10;
+ rOutput.WriteNumberAsString( nHeight );
+ }
+ break;
+ case EE_CHAR_WEIGHT:
+ case EE_CHAR_WEIGHT_CJK:
+ case EE_CHAR_WEIGHT_CTL:
+ {
+ FontWeight e = static_cast<const SvxWeightItem&>(rItem).GetWeight();
+ switch ( e )
+ {
+ case WEIGHT_BOLD: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_B ); break;
+ default: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_B ).WriteChar( '0' ); break;
+ }
+ }
+ break;
+ case EE_CHAR_UNDERLINE:
+ {
+ // Must underlined if in WordLineMode, but the information is
+ // missing here
+ FontLineStyle e = static_cast<const SvxUnderlineItem&>(rItem).GetLineStyle();
+ switch ( e )
+ {
+ case LINESTYLE_NONE: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_ULNONE ); break;
+ case LINESTYLE_SINGLE: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_UL ); break;
+ case LINESTYLE_DOUBLE: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_ULDB ); break;
+ case LINESTYLE_DOTTED: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_ULD ); break;
+ default:
+ break;
+ }
+ }
+ break;
+ case EE_CHAR_OVERLINE:
+ {
+ FontLineStyle e = static_cast<const SvxOverlineItem&>(rItem).GetLineStyle();
+ switch ( e )
+ {
+ case LINESTYLE_NONE: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_OLNONE ); break;
+ case LINESTYLE_SINGLE: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_OL ); break;
+ case LINESTYLE_DOUBLE: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_OLDB ); break;
+ case LINESTYLE_DOTTED: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_OLD ); break;
+ default:
+ break;
+ }
+ }
+ break;
+ case EE_CHAR_STRIKEOUT:
+ {
+ FontStrikeout e = static_cast<const SvxCrossedOutItem&>(rItem).GetStrikeout();
+ switch ( e )
+ {
+ case STRIKEOUT_SINGLE:
+ case STRIKEOUT_DOUBLE: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_STRIKE ); break;
+ case STRIKEOUT_NONE: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_STRIKE ).WriteChar( '0' ); break;
+ default:
+ break;
+ }
+ }
+ break;
+ case EE_CHAR_ITALIC:
+ case EE_CHAR_ITALIC_CJK:
+ case EE_CHAR_ITALIC_CTL:
+ {
+ FontItalic e = static_cast<const SvxPostureItem&>(rItem).GetPosture();
+ switch ( e )
+ {
+ case ITALIC_OBLIQUE:
+ case ITALIC_NORMAL: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_I ); break;
+ case ITALIC_NONE: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_I ).WriteChar( '0' ); break;
+ default:
+ break;
+ }
+ }
+ break;
+ case EE_CHAR_OUTLINE:
+ {
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_OUTL );
+ if ( !static_cast<const SvxContourItem&>(rItem).GetValue() )
+ rOutput.WriteChar( '0' );
+ }
+ break;
+ case EE_CHAR_RELIEF:
+ {
+ FontRelief nRelief = static_cast<const SvxCharReliefItem&>(rItem).GetValue();
+ if ( nRelief == FontRelief::Embossed )
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_EMBO );
+ if ( nRelief == FontRelief::Engraved )
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_IMPR );
+ }
+ break;
+ case EE_CHAR_EMPHASISMARK:
+ {
+ FontEmphasisMark nMark = static_cast<const SvxEmphasisMarkItem&>(rItem).GetEmphasisMark();
+ if ( nMark == FontEmphasisMark::NONE )
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_ACCNONE );
+ else if ( nMark == (FontEmphasisMark::Accent | FontEmphasisMark::PosAbove) )
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_ACCCOMMA );
+ else
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_ACCDOT );
+ }
+ break;
+ case EE_CHAR_SHADOW:
+ {
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_SHAD );
+ if ( !static_cast<const SvxShadowedItem&>(rItem).GetValue() )
+ rOutput.WriteChar( '0' );
+ }
+ break;
+ case EE_FEATURE_TAB:
+ {
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_TAB );
+ }
+ break;
+ case EE_FEATURE_LINEBR:
+ {
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_SL );
+ }
+ break;
+ case EE_CHAR_KERNING:
+ {
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_EXPNDTW );
+ rOutput.WriteNumberAsString( LogicToTwips(
+ static_cast<const SvxKerningItem&>(rItem).GetValue() ) );
+ }
+ break;
+ case EE_CHAR_PAIRKERNING:
+ {
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_KERNING );
+ rOutput.WriteNumberAsString( static_cast<const SvxAutoKernItem&>(rItem).GetValue() ? 1 : 0 );
+ }
+ break;
+ case EE_CHAR_ESCAPEMENT:
+ {
+ SvxFont aFont;
+ ContentNode* pNode = maEditDoc.GetObject( nPara );
+ SeekCursor( pNode, nPos, aFont );
+ MapMode aPntMode( MapUnit::MapPoint );
+ tools::Long nFontHeight = GetRefDevice()->LogicToLogic(
+ aFont.GetFontSize(), &GetRefMapMode(), &aPntMode ).Height();
+ nFontHeight *=2; // Half Points
+ sal_uInt16 const nProp = static_cast<const SvxEscapementItem&>(rItem).GetProportionalHeight();
+ sal_uInt16 nProp100 = nProp*100; // For SWG-Token Prop in 100th percent.
+ short nEsc = static_cast<const SvxEscapementItem&>(rItem).GetEsc();
+ const FontMetric& rFontMetric = GetRefDevice()->GetFontMetric();
+ double fFontHeight = rFontMetric.GetAscent() + rFontMetric.GetDescent();
+ double fAutoAscent = .8;
+ double fAutoDescent = .2;
+ if ( fFontHeight )
+ {
+ fAutoAscent = rFontMetric.GetAscent() / fFontHeight;
+ fAutoDescent = rFontMetric.GetDescent() / fFontHeight;
+ }
+ if ( nEsc == DFLT_ESC_AUTO_SUPER )
+ {
+ nEsc = fAutoAscent * (100 - nProp);
+ nProp100++; // A 1 afterwards means 'automatic'.
+ }
+ else if ( nEsc == DFLT_ESC_AUTO_SUB )
+ {
+ nEsc = fAutoDescent * -(100 - nProp);
+ nProp100++;
+ }
+ // SWG:
+ if ( nEsc )
+ {
+ rOutput.WriteOString( "{\\*\\updnprop" ).WriteNumberAsString(
+ nProp100 ).WriteChar( '}' );
+ }
+ tools::Long nUpDown = nFontHeight * std::abs( nEsc ) / 100;
+ if ( nEsc < 0 )
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_DN );
+ else if ( nEsc > 0 )
+ rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_UP );
+ rOutput.WriteNumberAsString(nUpDown);
+ }
+ break;
+ case EE_CHAR_CASEMAP:
+ {
+ const SvxCaseMapItem& rCaseMap = static_cast<const SvxCaseMapItem&>(rItem);
+ switch (rCaseMap.GetValue())
+ {
+ case SvxCaseMap::SmallCaps:
+ rOutput.WriteOString(OOO_STRING_SVTOOLS_RTF_SCAPS);
+ break;
+ case SvxCaseMap::Uppercase:
+ rOutput.WriteOString(OOO_STRING_SVTOOLS_RTF_CAPS);
+ break;
+ default: // Something that rtf does not support
+ rOutput.WriteOString(OOO_STRING_SVTOOLS_RTF_SCAPS);
+ rOutput.WriteNumberAsString(0);
+ rOutput.WriteOString(OOO_STRING_SVTOOLS_RTF_CAPS);
+ rOutput.WriteNumberAsString(0);
+ break;
+ }
+ }
+ break;
+ }
+}
+
+std::unique_ptr<EditTextObject> ImpEditEngine::GetEmptyTextObject()
+{
+ EditSelection aEmptySel;
+ aEmptySel.Min() = maEditDoc.GetStartPaM();
+ aEmptySel.Max() = maEditDoc.GetStartPaM();
+
+ return CreateTextObject( aEmptySel );
+}
+
+std::unique_ptr<EditTextObject> ImpEditEngine::CreateTextObject()
+{
+ EditSelection aCompleteSelection;
+ aCompleteSelection.Min() = maEditDoc.GetStartPaM();
+ aCompleteSelection.Max() = maEditDoc.GetEndPaM();
+
+ return CreateTextObject( aCompleteSelection );
+}
+
+std::unique_ptr<EditTextObject> ImpEditEngine::CreateTextObject(const EditSelection& rSel)
+{
+ return CreateTextObject(rSel, GetEditTextObjectPool(), maStatus.AllowBigObjects(), mnBigTextObjectStart);
+}
+
+std::unique_ptr<EditTextObject> ImpEditEngine::CreateTextObject( EditSelection aSel, SfxItemPool* pPool, bool bAllowBigObjects, sal_Int32 nBigObjectStart )
+{
+ sal_Int32 nStartNode, nEndNode;
+ sal_Int32 nTextPortions = 0;
+
+ aSel.Adjust( maEditDoc );
+ nStartNode = maEditDoc.GetPos( aSel.Min().GetNode() );
+ nEndNode = maEditDoc.GetPos( aSel.Max().GetNode() );
+
+ bool bOnlyFullParagraphs = !( aSel.Min().GetIndex() ||
+ ( aSel.Max().GetIndex() < aSel.Max().GetNode()->Len() ) );
+
+ // Templates are not saved!
+ // (Only the name and family, template itself must be in App!)
+
+ const MapUnit eMapUnit = maEditDoc.GetItemPool().GetMetric(DEF_METRIC);
+ auto pTxtObj(std::make_unique<EditTextObjectImpl>(pPool, eMapUnit, GetVertical(), GetRotation(),
+ GetItemScriptType(aSel)));
+
+ // iterate over the paragraphs ...
+ sal_Int32 nNode;
+ for ( nNode = nStartNode; nNode <= nEndNode; nNode++ )
+ {
+ ContentNode* pNode = maEditDoc.GetObject( nNode );
+ DBG_ASSERT( pNode, "Node not found: Search&Replace" );
+
+ if ( bOnlyFullParagraphs )
+ {
+ const ParaPortion* pParaPortion = GetParaPortions()[nNode];
+ nTextPortions += pParaPortion->GetTextPortions().Count();
+ }
+
+ sal_Int32 nStartPos = 0;
+ sal_Int32 nEndPos = pNode->Len();
+
+ bool bEmptyPara = nEndPos == 0;
+
+ if ( ( nNode == nStartNode ) && !bOnlyFullParagraphs )
+ nStartPos = aSel.Min().GetIndex();
+ if ( ( nNode == nEndNode ) && !bOnlyFullParagraphs )
+ nEndPos = aSel.Max().GetIndex();
+
+
+ ContentInfo *pC = pTxtObj->CreateAndInsertContent();
+
+ // The paragraph attributes ...
+ pC->GetParaAttribs().Set( pNode->GetContentAttribs().GetItems() );
+
+ // The StyleSheet...
+ if ( pNode->GetStyleSheet() )
+ {
+ pC->SetStyle(pNode->GetStyleSheet()->GetName());
+ pC->SetFamily(pNode->GetStyleSheet()->GetFamily());
+ }
+
+ // The Text...
+ pC->SetText(pNode->Copy(nStartPos, nEndPos-nStartPos));
+ auto& rCAttriblist = pC->GetCharAttribs();
+
+ // and the Attribute...
+ std::size_t nAttr = 0;
+ EditCharAttrib* pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
+ rCAttriblist.reserve(rCAttriblist.size() + pNode->GetCharAttribs().GetAttribs().size());
+ while ( pAttr )
+ {
+ // In a blank paragraph keep the attributes!
+ if ( bEmptyPara ||
+ ( ( pAttr->GetEnd() > nStartPos ) && ( pAttr->GetStart() < nEndPos ) ) )
+ {
+ XEditAttribute aX = pTxtObj->CreateAttrib(*pAttr->GetItem(), pAttr->GetStart(), pAttr->GetEnd());
+ // Possibly Correct ...
+ if ( ( nNode == nStartNode ) && ( nStartPos != 0 ) )
+ {
+ aX.GetStart() = ( aX.GetStart() > nStartPos ) ? aX.GetStart()-nStartPos : 0;
+ aX.GetEnd() = aX.GetEnd() - nStartPos;
+
+ }
+ if ( nNode == nEndNode )
+ {
+ if ( aX.GetEnd() > (nEndPos-nStartPos) )
+ aX.GetEnd() = nEndPos-nStartPos;
+ }
+ DBG_ASSERT( aX.GetEnd() <= (nEndPos-nStartPos), "CreateTextObject: Attribute too long!" );
+ if ( aX.GetLen() || bEmptyPara )
+ rCAttriblist.push_back(std::move(aX));
+ }
+ nAttr++;
+ pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
+ }
+
+ // If possible online spelling
+ if ( bAllowBigObjects && bOnlyFullParagraphs && pNode->GetWrongList() )
+ pC->SetWrongList( pNode->GetWrongList()->Clone() );
+
+ }
+
+ // Remember the portions info in case of large text objects:
+ // sleeper set up when Olli paragraphs not hacked!
+ if ( bAllowBigObjects && bOnlyFullParagraphs && IsFormatted() && IsUpdateLayout() && ( nTextPortions >= nBigObjectStart ) )
+ {
+ XParaPortionList* pXList = new XParaPortionList(GetRefDevice(), GetColumnWidth(maPaperSize), mfFontScaleX, mfFontScaleY, mfSpacingScaleX, mfSpacingScaleY);
+ pTxtObj->SetPortionInfo(std::unique_ptr<XParaPortionList>(pXList));
+ for ( nNode = nStartNode; nNode <= nEndNode; nNode++ )
+ {
+ const ParaPortion* pParaPortion = GetParaPortions()[nNode];
+ XParaPortion* pX = new XParaPortion;
+ pXList->push_back(pX);
+
+ pX->nHeight = pParaPortion->GetHeight();
+ pX->nFirstLineOffset = pParaPortion->GetFirstLineOffset();
+
+ // The TextPortions
+ sal_uInt16 nCount = pParaPortion->GetTextPortions().Count();
+ sal_uInt16 n;
+ for ( n = 0; n < nCount; n++ )
+ {
+ const TextPortion& rTextPortion = pParaPortion->GetTextPortions()[n];
+ TextPortion* pNew = new TextPortion( rTextPortion );
+ pX->aTextPortions.Append(pNew);
+ }
+
+ // The lines
+ nCount = pParaPortion->GetLines().Count();
+ for ( n = 0; n < nCount; n++ )
+ {
+ const EditLine& rLine = pParaPortion->GetLines()[n];
+ EditLine* pNew = rLine.Clone();
+ pX->aLines.Append(pNew);
+ }
+#ifdef DBG_UTIL
+ sal_uInt16 nTest;
+ int nTPLen = 0, nTxtLen = 0;
+ for ( nTest = pParaPortion->GetTextPortions().Count(); nTest; )
+ nTPLen += pParaPortion->GetTextPortions()[--nTest].GetLen();
+ for ( nTest = pParaPortion->GetLines().Count(); nTest; )
+ nTxtLen += pParaPortion->GetLines()[--nTest].GetLen();
+ DBG_ASSERT( ( nTPLen == pParaPortion->GetNode()->Len() ) && ( nTxtLen == pParaPortion->GetNode()->Len() ), "CreateBinTextObject: ParaPortion not completely formatted!" );
+#endif
+ }
+ }
+ return pTxtObj;
+}
+
+void ImpEditEngine::SetText( const EditTextObject& rTextObject )
+{
+ // Since setting a text object is not undo-able!
+ ResetUndoManager();
+ bool _bUpdate = IsUpdateLayout();
+ bool _bUndo = IsUndoEnabled();
+
+ SetText( OUString() );
+ EditPaM aPaM = maEditDoc.GetStartPaM();
+
+ SetUpdateLayout( false );
+ EnableUndo( false );
+
+ InsertText( rTextObject, EditSelection( aPaM, aPaM ) );
+ SetVertical(rTextObject.GetVertical());
+ SetRotation(rTextObject.GetRotation());
+
+ DBG_ASSERT( !HasUndoManager() || !GetUndoManager().GetUndoActionCount(), "From where comes the Undo in SetText ?!" );
+ SetUpdateLayout( _bUpdate );
+ EnableUndo( _bUndo );
+}
+
+EditSelection ImpEditEngine::InsertText( const EditTextObject& rTextObject, EditSelection aSel )
+{
+ aSel.Adjust( maEditDoc );
+ if ( aSel.HasRange() )
+ aSel = ImpDeleteSelection( aSel );
+ EditSelection aNewSel = InsertTextObject( rTextObject, aSel.Max() );
+ return aNewSel;
+}
+
+EditSelection ImpEditEngine::InsertTextObject( const EditTextObject& rTextObject, EditPaM aPaM )
+{
+ // Optimize: No getPos undFindParaportion, instead calculate index!
+ EditSelection aSel( aPaM, aPaM );
+ DBG_ASSERT( !aSel.DbgIsBuggy( maEditDoc ), "InsertBibTextObject: Selection broken!(1)" );
+
+ bool bUsePortionInfo = false;
+ const EditTextObjectImpl& rTextObjectImpl = toImpl(rTextObject);
+ XParaPortionList* pPortionInfo = rTextObjectImpl.GetPortionInfo();
+
+ if (pPortionInfo && ( static_cast<tools::Long>(pPortionInfo->GetPaperWidth()) == GetColumnWidth(maPaperSize))
+ && pPortionInfo->GetRefMapMode() == GetRefDevice()->GetMapMode()
+ && pPortionInfo->getFontScaleX() == mfFontScaleX
+ && pPortionInfo->getFontScaleY() == mfFontScaleY
+ && pPortionInfo->getSpacingScaleX() == mfSpacingScaleX
+ && pPortionInfo->getSpacingScaleY() == mfSpacingScaleY)
+ {
+ if ( (pPortionInfo->GetRefDevPtr() == GetRefDevice()) ||
+ (pPortionInfo->RefDevIsVirtual() && GetRefDevice()->IsVirtual()) )
+ bUsePortionInfo = true;
+ }
+
+ bool bConvertMetricOfItems = false;
+ MapUnit eSourceUnit = MapUnit(), eDestUnit = MapUnit();
+ if (rTextObjectImpl.HasMetric())
+ {
+ eSourceUnit = rTextObjectImpl.GetMetric();
+ eDestUnit = maEditDoc.GetItemPool().GetMetric( DEF_METRIC );
+ if ( eSourceUnit != eDestUnit )
+ bConvertMetricOfItems = true;
+ }
+
+ // Before, paragraph count was of type sal_uInt16 so if nContents exceeded
+ // 0xFFFF this wouldn't have worked anyway, given that nPara is used to
+ // number paragraphs and is fearlessly incremented.
+ sal_Int32 nContents = static_cast<sal_Int32>(rTextObjectImpl.GetContents().size());
+ SAL_WARN_IF( nContents < 0, "editeng", "ImpEditEngine::InsertTextObject - contents overflow " << nContents);
+ sal_Int32 nPara = maEditDoc.GetPos( aPaM.GetNode() );
+
+ for (sal_Int32 n = 0; n < nContents; ++n, ++nPara)
+ {
+ const ContentInfo* pC = rTextObjectImpl.GetContents()[n].get();
+ bool bNewContent = aPaM.GetNode()->Len() == 0;
+ const sal_Int32 nStartPos = aPaM.GetIndex();
+
+ aPaM = ImpFastInsertText( aPaM, pC->GetText() );
+
+ ParaPortion* pPortion = FindParaPortion( aPaM.GetNode() );
+ DBG_ASSERT( pPortion, "Blind Portion in FastInsertText" );
+ pPortion->MarkInvalid( nStartPos, pC->GetText().getLength() );
+
+ // Character attributes ...
+ bool bAllreadyHasAttribs = aPaM.GetNode()->GetCharAttribs().Count() != 0;
+ size_t nNewAttribs = pC->GetCharAttribs().size();
+ if ( nNewAttribs )
+ {
+ bool bUpdateFields = false;
+ for (size_t nAttr = 0; nAttr < nNewAttribs; ++nAttr)
+ {
+ const XEditAttribute& rX = pC->GetCharAttribs()[nAttr];
+ // Can happen when paragraphs > 16K, it is simply wrapped.
+ //TODO! Still true, still needed?
+ if ( rX.GetEnd() <= aPaM.GetNode()->Len() )
+ {
+ if ( !bAllreadyHasAttribs || rX.IsFeature() )
+ {
+ // Normal attributes then go faster ...
+ // Features shall not be inserted through
+ // EditDoc:: InsertAttrib, using FastInsertText they are
+ // already in the flow
+ DBG_ASSERT( rX.GetEnd() <= aPaM.GetNode()->Len(), "InsertBinTextObject: Attribute too large!" );
+ EditCharAttrib* pAttr;
+ if ( !bConvertMetricOfItems )
+ pAttr = MakeCharAttrib( maEditDoc.GetItemPool(), *(rX.GetItem()), rX.GetStart()+nStartPos, rX.GetEnd()+nStartPos );
+ else
+ {
+ std::unique_ptr<SfxPoolItem> pNew(rX.GetItem()->Clone());
+ ConvertItem( pNew, eSourceUnit, eDestUnit );
+ pAttr = MakeCharAttrib( maEditDoc.GetItemPool(), *pNew, rX.GetStart()+nStartPos, rX.GetEnd()+nStartPos );
+ }
+ DBG_ASSERT( pAttr->GetEnd() <= aPaM.GetNode()->Len(), "InsertBinTextObject: Attribute does not fit! (1)" );
+ aPaM.GetNode()->GetCharAttribs().InsertAttrib( pAttr );
+ if ( pAttr->Which() == EE_FEATURE_FIELD )
+ bUpdateFields = true;
+ }
+ else
+ {
+ DBG_ASSERT( rX.GetEnd()+nStartPos <= aPaM.GetNode()->Len(), "InsertBinTextObject: Attribute does not fit! (2)" );
+ // Tabs and other Features can not be inserted through InsertAttrib:
+ maEditDoc.InsertAttrib( aPaM.GetNode(), rX.GetStart()+nStartPos, rX.GetEnd()+nStartPos, *rX.GetItem() );
+ }
+ }
+ }
+ if ( bUpdateFields )
+ UpdateFields();
+
+ // Otherwise, quick format => no attributes!
+ pPortion->MarkSelectionInvalid( nStartPos );
+ }
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(aPaM.GetNode()->GetCharAttribs());
+#endif
+
+ bool bParaAttribs = false;
+ if ( bNewContent || ( ( n > 0 ) && ( n < (nContents-1) ) ) )
+ {
+ // only style and ParaAttribs when new paragraph, or
+ // completely internal ...
+ bParaAttribs = pC->GetParaAttribs().Count() != 0;
+ if ( GetStyleSheetPool() && pC->GetStyle().getLength() )
+ {
+ SfxStyleSheet* pStyle = static_cast<SfxStyleSheet*>(GetStyleSheetPool()->Find( pC->GetStyle(), pC->GetFamily() ));
+ DBG_ASSERT( pStyle, "InsertBinTextObject - Style not found!" );
+ SetStyleSheet( nPara, pStyle );
+ }
+ if ( !bConvertMetricOfItems )
+ SetParaAttribs( maEditDoc.GetPos( aPaM.GetNode() ), pC->GetParaAttribs() );
+ else
+ {
+ SfxItemSet aAttribs( GetEmptyItemSet() );
+ ConvertAndPutItems( aAttribs, pC->GetParaAttribs(), &eSourceUnit, &eDestUnit );
+ SetParaAttribs( maEditDoc.GetPos( aPaM.GetNode() ), aAttribs );
+ }
+ if ( bNewContent && bUsePortionInfo )
+ {
+ const XParaPortion& rXP = (*pPortionInfo)[n];
+ ParaPortion* pParaPortion = GetParaPortions()[ nPara ];
+ DBG_ASSERT( pParaPortion, "InsertBinTextObject: ParaPortion?" );
+ pParaPortion->nHeight = rXP.nHeight;
+ pParaPortion->nFirstLineOffset = rXP.nFirstLineOffset;
+ pParaPortion->bForceRepaint = true;
+ pParaPortion->SetValid(); // Do not format
+
+ // The Text Portions
+ pParaPortion->GetTextPortions().Reset();
+ sal_uInt16 nCount = rXP.aTextPortions.Count();
+ for ( sal_uInt16 _n = 0; _n < nCount; _n++ )
+ {
+ const TextPortion& rTextPortion = rXP.aTextPortions[_n];
+ TextPortion* pNew = new TextPortion( rTextPortion );
+ pParaPortion->GetTextPortions().Append(pNew);
+ }
+
+ // The lines
+ pParaPortion->GetLines().Reset();
+ nCount = rXP.aLines.Count();
+ for ( sal_uInt16 m = 0; m < nCount; m++ )
+ {
+ const EditLine& rLine = rXP.aLines[m];
+ EditLine* pNew = rLine.Clone();
+ pNew->SetInvalid(); // Paint again!
+ pParaPortion->GetLines().Append(pNew);
+ }
+#ifdef DBG_UTIL
+ sal_uInt16 nTest;
+ int nTPLen = 0, nTxtLen = 0;
+ for ( nTest = pParaPortion->GetTextPortions().Count(); nTest; )
+ nTPLen += pParaPortion->GetTextPortions()[--nTest].GetLen();
+ for ( nTest = pParaPortion->GetLines().Count(); nTest; )
+ nTxtLen += pParaPortion->GetLines()[--nTest].GetLen();
+ DBG_ASSERT( ( nTPLen == pParaPortion->GetNode()->Len() ) && ( nTxtLen == pParaPortion->GetNode()->Len() ), "InsertTextObject: ParaPortion not completely formatted!" );
+#endif
+ }
+ }
+ if ( !bParaAttribs ) // DefFont is not calculated for FastInsertParagraph
+ {
+ aPaM.GetNode()->GetCharAttribs().GetDefFont() = maEditDoc.GetDefFont();
+ if (maStatus.UseCharAttribs())
+ aPaM.GetNode()->CreateDefFont();
+ }
+
+ if ( bNewContent && GetStatus().DoOnlineSpelling() && pC->GetWrongList() )
+ {
+ aPaM.GetNode()->SetWrongList( pC->GetWrongList()->Clone() );
+ }
+
+ // Wrap when followed by other ...
+ if ( n < ( nContents-1) )
+ {
+ if ( bNewContent )
+ aPaM = ImpFastInsertParagraph( nPara+1 );
+ else
+ aPaM = ImpInsertParaBreak( aPaM, false );
+ }
+ }
+
+ aSel.Max() = aPaM;
+ DBG_ASSERT( !aSel.DbgIsBuggy( maEditDoc ), "InsertBibTextObject: Selection broken!(1)" );
+ return aSel;
+}
+
+void ImpEditEngine::GetAllMisspellRanges( std::vector<editeng::MisspellRanges>& rRanges ) const
+{
+ std::vector<editeng::MisspellRanges> aRanges;
+ const EditDoc& rDoc = GetEditDoc();
+ for (sal_Int32 i = 0, n = rDoc.Count(); i < n; ++i)
+ {
+ const ContentNode* pNode = rDoc.GetObject(i);
+ const WrongList* pWrongList = pNode->GetWrongList();
+ if (!pWrongList)
+ continue;
+
+ aRanges.emplace_back(i, std::vector(pWrongList->GetRanges()));
+ }
+
+ aRanges.swap(rRanges);
+}
+
+void ImpEditEngine::SetAllMisspellRanges( const std::vector<editeng::MisspellRanges>& rRanges )
+{
+ EditDoc& rDoc = GetEditDoc();
+ for (auto const& rParaRanges : rRanges)
+ {
+ ContentNode* pNode = rDoc.GetObject(rParaRanges.mnParagraph);
+ if (!pNode)
+ continue;
+
+ pNode->CreateWrongList();
+ WrongList* pWrongList = pNode->GetWrongList();
+ pWrongList->SetRanges(std::vector(rParaRanges.maRanges));
+ }
+}
+
+editeng::LanguageSpan ImpEditEngine::GetLanguage( const EditPaM& rPaM, sal_Int32* pEndPos ) const
+{
+ short nScriptTypeI18N = GetI18NScriptType( rPaM, pEndPos ); // pEndPos will be valid now, pointing to ScriptChange or NodeLen
+ SvtScriptType nScriptType = SvtLanguageOptions::FromI18NToSvtScriptType(nScriptTypeI18N);
+ sal_uInt16 nLangId = GetScriptItemId( EE_CHAR_LANGUAGE, nScriptType );
+ const SvxLanguageItem* pLangItem = &static_cast<const SvxLanguageItem&>(rPaM.GetNode()->GetContentAttribs().GetItem( nLangId ));
+ const EditCharAttrib* pAttr = rPaM.GetNode()->GetCharAttribs().FindAttrib( nLangId, rPaM.GetIndex() );
+
+ editeng::LanguageSpan aLang;
+
+ if ( pAttr )
+ {
+ pLangItem = static_cast<const SvxLanguageItem*>(pAttr->GetItem());
+ aLang.nStart = pAttr->GetStart();
+ aLang.nEnd = pAttr->GetEnd();
+ }
+
+ if ( pEndPos && pAttr && ( pAttr->GetEnd() < *pEndPos ) )
+ *pEndPos = pAttr->GetEnd();
+
+ aLang.nLang = pLangItem->GetLanguage();
+
+ return aLang;
+}
+
+css::lang::Locale ImpEditEngine::GetLocale( const EditPaM& rPaM ) const
+{
+ return LanguageTag( GetLanguage( rPaM ).nLang ).getLocale();
+}
+
+Reference< XSpellChecker1 > const & ImpEditEngine::GetSpeller()
+{
+ if ( !xSpeller.is() )
+ xSpeller = LinguMgr::GetSpellChecker();
+ return xSpeller;
+}
+
+
+void ImpEditEngine::CreateSpellInfo( bool bMultipleDocs )
+{
+ if (!pSpellInfo)
+ pSpellInfo.reset( new SpellInfo );
+ else
+ *pSpellInfo = SpellInfo(); // reset to default values
+
+ pSpellInfo->bMultipleDoc = bMultipleDocs;
+ // always spell draw objects completely, starting at the top.
+ // (spelling in only a selection or not starting with the top requires
+ // further changes elsewhere to work properly)
+ pSpellInfo->aSpellStart = EPaM();
+ pSpellInfo->aSpellTo = EPaM( EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND );
+}
+
+
+EESpellState ImpEditEngine::Spell(EditView* pEditView, weld::Widget* pDialogParent, bool bMultipleDoc)
+{
+ SAL_WARN_IF( !xSpeller.is(), "editeng", "No Spell checker set!" );
+
+ if ( !xSpeller.is() )
+ return EESpellState::NoSpeller;
+
+ aOnlineSpellTimer.Stop();
+
+ // In MultipleDoc always from the front / rear ...
+ if ( bMultipleDoc )
+ {
+ pEditView->pImpEditView->SetEditSelection( maEditDoc.GetStartPaM() );
+ }
+
+ EditSelection aCurSel( pEditView->pImpEditView->GetEditSelection() );
+ CreateSpellInfo( bMultipleDoc );
+
+ bool bIsStart = false;
+ if ( bMultipleDoc )
+ bIsStart = true; // Accessible from the front or from behind ...
+ else if ( CreateEPaM( maEditDoc.GetStartPaM() ) == pSpellInfo->aSpellStart )
+ bIsStart = true;
+
+ {
+ EditSpellWrapper aWrp(pDialogParent, bIsStart, pEditView );
+ aWrp.SpellDocument();
+ }
+
+ if ( !bMultipleDoc )
+ {
+ pEditView->pImpEditView->DrawSelectionXOR();
+ if ( aCurSel.Max().GetIndex() > aCurSel.Max().GetNode()->Len() )
+ aCurSel.Max().SetIndex( aCurSel.Max().GetNode()->Len() );
+ aCurSel.Min() = aCurSel.Max();
+ pEditView->pImpEditView->SetEditSelection( aCurSel );
+ pEditView->pImpEditView->DrawSelectionXOR();
+ pEditView->ShowCursor( true, false );
+ }
+ EESpellState eState = pSpellInfo->eState;
+ pSpellInfo.reset();
+ return eState;
+}
+
+
+bool ImpEditEngine::HasConvertibleTextPortion( LanguageType nSrcLang )
+{
+ bool bHasConvTxt = false;
+
+ sal_Int32 nParas = pEditEngine->GetParagraphCount();
+ for (sal_Int32 k = 0; k < nParas; ++k)
+ {
+ std::vector<sal_Int32> aPortions;
+ pEditEngine->GetPortions( k, aPortions );
+ for ( size_t nPos = 0; nPos < aPortions.size(); ++nPos )
+ {
+ sal_Int32 nEnd = aPortions[ nPos ];
+ sal_Int32 nStart = nPos > 0 ? aPortions[ nPos - 1 ] : 0;
+
+ // if the paragraph is not empty we need to increase the index
+ // by one since the attribute of the character left to the
+ // specified position is evaluated.
+ if (nEnd > nStart) // empty para?
+ ++nStart;
+ LanguageType nLangFound = pEditEngine->GetLanguage( k, nStart ).nLang;
+#ifdef DEBUG
+ lang::Locale aLocale( LanguageTag::convertToLocale( nLangFound ) );
+#endif
+ bHasConvTxt = (nSrcLang == nLangFound) ||
+ (editeng::HangulHanjaConversion::IsChinese( nLangFound ) &&
+ editeng::HangulHanjaConversion::IsChinese( nSrcLang ));
+ if (bHasConvTxt)
+ return bHasConvTxt;
+ }
+ }
+
+ return bHasConvTxt;
+}
+
+void ImpEditEngine::Convert( EditView* pEditView, weld::Widget* pDialogParent,
+ LanguageType nSrcLang, LanguageType nDestLang, const vcl::Font *pDestFont,
+ sal_Int32 nOptions, bool bIsInteractive, bool bMultipleDoc )
+{
+ // modified version of ImpEditEngine::Spell
+
+ // In MultipleDoc always from the front / rear ...
+ if ( bMultipleDoc )
+ pEditView->pImpEditView->SetEditSelection( maEditDoc.GetStartPaM() );
+
+
+ // initialize pConvInfo
+ EditSelection aCurSel( pEditView->pImpEditView->GetEditSelection() );
+ aCurSel.Adjust( maEditDoc );
+ pConvInfo.reset(new ConvInfo);
+ pConvInfo->bMultipleDoc = bMultipleDoc;
+ pConvInfo->aConvStart = CreateEPaM( aCurSel.Min() );
+
+ // if it is not just a selection and we are about to begin
+ // with the current conversion for the very first time
+ // we need to find the start of the current (initial)
+ // convertible unit in order for the text conversion to give
+ // the correct result for that. Since it is easier to obtain
+ // the start of the word we use that though.
+ if (!aCurSel.HasRange() && ImplGetBreakIterator().is())
+ {
+ EditPaM aWordStartPaM( SelectWord( aCurSel, i18n::WordType::DICTIONARY_WORD ).Min() );
+
+ // since #118246 / #117803 still occurs if the cursor is placed
+ // between the two chinese characters to be converted (because both
+ // of them are words on their own!) using the word boundary here does
+ // not work. Thus since chinese conversion is not interactive we start
+ // at the begin of the paragraph to solve the problem, i.e. have the
+ // TextConversion service get those characters together in the same call.
+ pConvInfo->aConvStart.nIndex = editeng::HangulHanjaConversion::IsChinese( nSrcLang )
+ ? 0 : aWordStartPaM.GetIndex();
+ }
+
+ pConvInfo->aConvContinue = pConvInfo->aConvStart;
+
+ bool bIsStart = false;
+ if ( bMultipleDoc )
+ bIsStart = true; // Accessible from the front or from behind ...
+ else if ( CreateEPaM( maEditDoc.GetStartPaM() ) == pConvInfo->aConvStart )
+ bIsStart = true;
+
+ TextConvWrapper aWrp( pDialogParent,
+ ::comphelper::getProcessComponentContext(),
+ LanguageTag::convertToLocale( nSrcLang ),
+ LanguageTag::convertToLocale( nDestLang ),
+ pDestFont,
+ nOptions, bIsInteractive,
+ bIsStart, pEditView );
+
+
+ //!! optimization does not work since when update mode is false
+ //!! the object is 'lying' about it portions, paragraphs,
+ //!! EndPaM... later on.
+ //!! Should not be a great problem since text boxes or cells in
+ //!! Calc usually have only a rather short text.
+ //
+ // disallow formatting, updating the view, ... while
+ // non-interactively converting the document. (saves time)
+ //if (!bIsInteractive)
+ // SetUpdateMode( sal_False );
+
+ aWrp.Convert();
+
+ //if (!bIsInteractive)
+ //SetUpdateMode( sal_True, 0, sal_True );
+
+ if ( !bMultipleDoc )
+ {
+ pEditView->pImpEditView->DrawSelectionXOR();
+ if ( aCurSel.Max().GetIndex() > aCurSel.Max().GetNode()->Len() )
+ aCurSel.Max().SetIndex( aCurSel.Max().GetNode()->Len() );
+ aCurSel.Min() = aCurSel.Max();
+ pEditView->pImpEditView->SetEditSelection( aCurSel );
+ pEditView->pImpEditView->DrawSelectionXOR();
+ pEditView->ShowCursor( true, false );
+ }
+ pConvInfo.reset();
+}
+
+
+void ImpEditEngine::SetLanguageAndFont(
+ const ESelection &rESel,
+ LanguageType nLang, sal_uInt16 nLangWhichId,
+ const vcl::Font *pFont, sal_uInt16 nFontWhichId )
+{
+ ESelection aOldSel = pActiveView->GetSelection();
+ pActiveView->SetSelection( rESel );
+
+ // set new language attribute
+ SfxItemSet aNewSet( pActiveView->GetEmptyItemSet() );
+ aNewSet.Put( SvxLanguageItem( nLang, nLangWhichId ) );
+
+ // new font to be set?
+ DBG_ASSERT( pFont, "target font missing?" );
+ if (pFont)
+ {
+ // set new font attribute
+ SvxFontItem aFontItem = static_cast<const SvxFontItem&>( aNewSet.Get( nFontWhichId ) );
+ aFontItem.SetFamilyName( pFont->GetFamilyName());
+ aFontItem.SetFamily( pFont->GetFamilyType());
+ aFontItem.SetStyleName( pFont->GetStyleName());
+ aFontItem.SetPitch( pFont->GetPitch());
+ aFontItem.SetCharSet( pFont->GetCharSet() );
+ aNewSet.Put( aFontItem );
+ }
+
+ // apply new attributes
+ pActiveView->SetAttribs( aNewSet );
+
+ pActiveView->SetSelection( aOldSel );
+}
+
+
+void ImpEditEngine::ImpConvert( OUString &rConvTxt, LanguageType &rConvTxtLang,
+ EditView* pEditView, LanguageType nSrcLang, const ESelection &rConvRange,
+ bool bAllowImplicitChangesForNotConvertibleText,
+ LanguageType nTargetLang, const vcl::Font *pTargetFont )
+{
+ // modified version of ImpEditEngine::ImpSpell
+
+ // looks for next convertible text portion to be passed on to the wrapper
+
+ OUString aRes;
+ LanguageType nResLang = LANGUAGE_NONE;
+
+ EditPaM aPos( CreateEditPaM( pConvInfo->aConvContinue ) );
+ EditSelection aCurSel( aPos, aPos );
+
+ OUString aWord;
+
+ while (aRes.isEmpty())
+ {
+ // empty paragraph found that needs to have language and font set?
+ if (bAllowImplicitChangesForNotConvertibleText &&
+ pEditEngine->GetText( pConvInfo->aConvContinue.nPara ).isEmpty())
+ {
+ sal_Int32 nPara = pConvInfo->aConvContinue.nPara;
+ ESelection aESel( nPara, 0, nPara, 0 );
+ // see comment for below same function call
+ SetLanguageAndFont( aESel,
+ nTargetLang, EE_CHAR_LANGUAGE_CJK,
+ pTargetFont, EE_CHAR_FONTINFO_CJK );
+ }
+
+
+ if (pConvInfo->aConvContinue.nPara == pConvInfo->aConvTo.nPara &&
+ pConvInfo->aConvContinue.nIndex >= pConvInfo->aConvTo.nIndex)
+ break;
+
+ sal_Int32 nAttribStart = -1;
+ sal_Int32 nAttribEnd = -1;
+ sal_Int32 nCurPos = -1;
+ EPaM aCurStart = CreateEPaM( aCurSel.Min() );
+ std::vector<sal_Int32> aPortions;
+ pEditEngine->GetPortions( aCurStart.nPara, aPortions );
+ for ( size_t nPos = 0; nPos < aPortions.size(); ++nPos )
+ {
+ const sal_Int32 nEnd = aPortions[ nPos ];
+ const sal_Int32 nStart = nPos > 0 ? aPortions[ nPos - 1 ] : 0;
+
+ // the language attribute is obtained from the left character
+ // (like usually all other attributes)
+ // thus we usually have to add 1 in order to get the language
+ // of the text right to the cursor position
+ const sal_Int32 nLangIdx = nEnd > nStart ? nStart + 1 : nStart;
+ LanguageType nLangFound = pEditEngine->GetLanguage( aCurStart.nPara, nLangIdx ).nLang;
+#ifdef DEBUG
+ lang::Locale aLocale( LanguageTag::convertToLocale( nLangFound ) );
+#endif
+ bool bLangOk = (nLangFound == nSrcLang) ||
+ (editeng::HangulHanjaConversion::IsChinese( nLangFound ) &&
+ editeng::HangulHanjaConversion::IsChinese( nSrcLang ));
+
+ if (nAttribEnd>=0) // start already found?
+ {
+ DBG_ASSERT(nEnd >= aCurStart.nIndex, "error while scanning attributes (a)" );
+ DBG_ASSERT(nEnd >= nAttribEnd, "error while scanning attributes (b)" );
+ if (/*nEnd >= aCurStart.nIndex &&*/ nLangFound == nResLang)
+ nAttribEnd = nEnd;
+ else // language attrib has changed
+ break;
+ }
+ if (nAttribStart<0 && // start not yet found?
+ nEnd > aCurStart.nIndex && bLangOk)
+ {
+ nAttribStart = nStart;
+ nAttribEnd = nEnd;
+ nResLang = nLangFound;
+ }
+ //! the list of portions may have changed compared to the previous
+ //! call to this function (because of possibly changed language
+ //! attribute!)
+ //! But since we don't want to start in the already processed part
+ //! we clip the start accordingly.
+ if (nAttribStart >= 0 && nAttribStart < aCurStart.nIndex)
+ {
+ nAttribStart = aCurStart.nIndex;
+ }
+
+ // check script type to the right of the start of the current portion
+ EditPaM aPaM( CreateEditPaM( EPaM(aCurStart.nPara, nLangIdx) ) );
+ bool bIsAsianScript = (i18n::ScriptType::ASIAN == GetI18NScriptType( aPaM ));
+ // not yet processed text part with for conversion
+ // not suitable language found that needs to be changed?
+ if (bAllowImplicitChangesForNotConvertibleText &&
+ !bLangOk && !bIsAsianScript && nEnd > aCurStart.nIndex)
+ {
+ ESelection aESel( aCurStart.nPara, nStart, aCurStart.nPara, nEnd );
+ // set language and font to target language and font of conversion
+ //! Now this especially includes all non convertible text e.g.
+ //! spaces, empty paragraphs and western text.
+ // This is in order for every *new* text entered at *any* position to
+ // have the correct language and font attributes set.
+ SetLanguageAndFont( aESel,
+ nTargetLang, EE_CHAR_LANGUAGE_CJK,
+ pTargetFont, EE_CHAR_FONTINFO_CJK );
+ }
+
+ nCurPos = nEnd;
+ }
+
+ if (nAttribStart>=0 && nAttribEnd>=0)
+ {
+ aCurSel.Min().SetIndex( nAttribStart );
+ aCurSel.Max().SetIndex( nAttribEnd );
+ }
+ else if (nCurPos>=0)
+ {
+ // set selection to end of scanned text
+ // (used to set the position where to continue from later on)
+ aCurSel.Min().SetIndex( nCurPos );
+ aCurSel.Max().SetIndex( nCurPos );
+ }
+
+ if ( !pConvInfo->bConvToEnd )
+ {
+ EPaM aEPaM( CreateEPaM( aCurSel.Min() ) );
+ if ( !( aEPaM < pConvInfo->aConvTo ) )
+ break;
+ }
+
+ // clip selected word to the converted area
+ // (main use when conversion starts/ends **within** a word)
+ EditPaM aPaM( CreateEditPaM( pConvInfo->aConvStart ) );
+ if (pConvInfo->bConvToEnd &&
+ aCurSel.Min().GetNode() == aPaM.GetNode() &&
+ aCurSel.Min().GetIndex() < aPaM.GetIndex())
+ aCurSel.Min().SetIndex( aPaM.GetIndex() );
+ aPaM = CreateEditPaM( pConvInfo->aConvContinue );
+ if (aCurSel.Min().GetNode() == aPaM.GetNode() &&
+ aCurSel.Min().GetIndex() < aPaM.GetIndex())
+ aCurSel.Min().SetIndex( aPaM.GetIndex() );
+ aPaM = CreateEditPaM( pConvInfo->aConvTo );
+ if ((!pConvInfo->bConvToEnd || rConvRange.HasRange())&&
+ aCurSel.Max().GetNode() == aPaM.GetNode() &&
+ aCurSel.Max().GetIndex() > aPaM.GetIndex())
+ aCurSel.Max().SetIndex( aPaM.GetIndex() );
+
+ aWord = GetSelected( aCurSel );
+
+ if ( !aWord.isEmpty() /* && bLangOk */)
+ aRes = aWord;
+
+ // move to next word/paragraph if necessary
+ if ( aRes.isEmpty() )
+ aCurSel = WordRight( aCurSel.Min(), css::i18n::WordType::DICTIONARY_WORD );
+
+ pConvInfo->aConvContinue = CreateEPaM( aCurSel.Max() );
+ }
+
+ pEditView->pImpEditView->DrawSelectionXOR();
+ pEditView->pImpEditView->SetEditSelection( aCurSel );
+ pEditView->pImpEditView->DrawSelectionXOR();
+ pEditView->ShowCursor( true, false );
+
+ rConvTxt = aRes;
+ if ( !rConvTxt.isEmpty() )
+ rConvTxtLang = nResLang;
+}
+
+
+Reference< XSpellAlternatives > ImpEditEngine::ImpSpell( EditView* pEditView )
+{
+ DBG_ASSERT( xSpeller.is(), "No spell checker set!" );
+
+ ContentNode* pLastNode = maEditDoc.GetObject( maEditDoc.Count()-1 );
+ EditSelection aCurSel( pEditView->pImpEditView->GetEditSelection() );
+ aCurSel.Min() = aCurSel.Max();
+
+ Reference< XSpellAlternatives > xSpellAlt;
+ Sequence< PropertyValue > aEmptySeq;
+ while (!xSpellAlt.is())
+ {
+ // Known (most likely) bug: If SpellToCurrent, the current has to be
+ // corrected at each replacement, otherwise it may not fit exactly in
+ // the end ...
+ if ( pSpellInfo->bSpellToEnd || pSpellInfo->bMultipleDoc )
+ {
+ if ( aCurSel.Max().GetNode() == pLastNode )
+ {
+ if ( aCurSel.Max().GetIndex() >= pLastNode->Len() )
+ break;
+ }
+ }
+ else if ( !pSpellInfo->bSpellToEnd )
+ {
+ EPaM aEPaM( CreateEPaM( aCurSel.Max() ) );
+ if ( !( aEPaM < pSpellInfo->aSpellTo ) )
+ break;
+ }
+
+ aCurSel = SelectWord( aCurSel, css::i18n::WordType::DICTIONARY_WORD );
+ OUString aWord = GetSelected( aCurSel );
+
+ // If afterwards a dot, this must be handed over!
+ // If an abbreviation ...
+ if ( !aWord.isEmpty() && ( aCurSel.Max().GetIndex() < aCurSel.Max().GetNode()->Len() ) )
+ {
+ sal_Unicode cNext = aCurSel.Max().GetNode()->GetChar( aCurSel.Max().GetIndex() );
+ if ( cNext == '.' )
+ {
+ aCurSel.Max().SetIndex( aCurSel.Max().GetIndex()+1 );
+ aWord += OUStringChar(cNext);
+ }
+ }
+
+ if ( !aWord.isEmpty() )
+ {
+ LanguageType eLang = GetLanguage( aCurSel.Max() ).nLang;
+ SvxSpellWrapper::CheckSpellLang( xSpeller, eLang );
+ xSpellAlt = xSpeller->spell( aWord, static_cast<sal_uInt16>(eLang), aEmptySeq );
+ }
+
+ if ( !xSpellAlt.is() )
+ aCurSel = WordRight( aCurSel.Min(), css::i18n::WordType::DICTIONARY_WORD );
+ else
+ pSpellInfo->eState = EESpellState::ErrorFound;
+ }
+
+ pEditView->pImpEditView->DrawSelectionXOR();
+ pEditView->pImpEditView->SetEditSelection( aCurSel );
+ pEditView->pImpEditView->DrawSelectionXOR();
+ pEditView->ShowCursor( true, false );
+ return xSpellAlt;
+}
+
+Reference< XSpellAlternatives > ImpEditEngine::ImpFindNextError(EditSelection& rSelection)
+{
+ EditSelection aCurSel( rSelection.Min() );
+
+ Reference< XSpellAlternatives > xSpellAlt;
+ Sequence< PropertyValue > aEmptySeq;
+ while (!xSpellAlt.is())
+ {
+ //check if the end of the selection has been reached
+ {
+ EPaM aEPaM( CreateEPaM( aCurSel.Max() ) );
+ if ( !( aEPaM < CreateEPaM( rSelection.Max()) ) )
+ break;
+ }
+
+ aCurSel = SelectWord( aCurSel, css::i18n::WordType::DICTIONARY_WORD );
+ OUString aWord = GetSelected( aCurSel );
+
+ // If afterwards a dot, this must be handed over!
+ // If an abbreviation ...
+ if ( !aWord.isEmpty() && ( aCurSel.Max().GetIndex() < aCurSel.Max().GetNode()->Len() ) )
+ {
+ sal_Unicode cNext = aCurSel.Max().GetNode()->GetChar( aCurSel.Max().GetIndex() );
+ if ( cNext == '.' )
+ {
+ aCurSel.Max().SetIndex( aCurSel.Max().GetIndex()+1 );
+ aWord += OUStringChar(cNext);
+ }
+ }
+
+ if ( !aWord.isEmpty() )
+ xSpellAlt = xSpeller->spell( aWord, static_cast<sal_uInt16>(GetLanguage( aCurSel.Max() ).nLang), aEmptySeq );
+
+ if ( !xSpellAlt.is() )
+ aCurSel = WordRight( aCurSel.Min(), css::i18n::WordType::DICTIONARY_WORD );
+ else
+ {
+ pSpellInfo->eState = EESpellState::ErrorFound;
+ rSelection = aCurSel;
+ }
+ }
+ return xSpellAlt;
+}
+
+bool ImpEditEngine::SpellSentence(EditView const & rEditView,
+ svx::SpellPortions& rToFill )
+{
+ bool bRet = false;
+ EditSelection aCurSel( rEditView.pImpEditView->GetEditSelection() );
+ if(!pSpellInfo)
+ CreateSpellInfo( true );
+ pSpellInfo->aCurSentenceStart = aCurSel.Min();
+ DBG_ASSERT( xSpeller.is(), "No spell checker set!" );
+ pSpellInfo->aLastSpellPortions.clear();
+ pSpellInfo->aLastSpellContentSelections.clear();
+ rToFill.clear();
+ //if no selection previously exists the range is extended to the end of the object
+ if (!aCurSel.HasRange())
+ {
+ ContentNode* pLastNode = maEditDoc.GetObject( maEditDoc.Count()-1);
+ aCurSel.Max() = EditPaM(pLastNode, pLastNode->Len());
+ }
+ // check for next error in aCurSel and set aCurSel to that one if any was found
+ Reference< XSpellAlternatives > xAlt = ImpFindNextError(aCurSel);
+ if (xAlt.is())
+ {
+ bRet = true;
+ //find the sentence boundaries
+ EditSelection aSentencePaM = SelectSentence(aCurSel);
+ //make sure that the sentence is never smaller than the error range!
+ if(aSentencePaM.Max().GetIndex() < aCurSel.Max().GetIndex())
+ aSentencePaM.Max() = aCurSel.Max();
+ //add the portion preceding the error
+ EditSelection aStartSelection(aSentencePaM.Min(), aCurSel.Min());
+ if(aStartSelection.HasRange())
+ AddPortionIterated(rEditView, aStartSelection, nullptr, rToFill);
+ //add the error portion
+ AddPortionIterated(rEditView, aCurSel, xAlt, rToFill);
+ //find the end of the sentence
+ //search for all errors in the rest of the sentence and add all the portions
+ do
+ {
+ EditSelection aNextSel(aCurSel.Max(), aSentencePaM.Max());
+ xAlt = ImpFindNextError(aNextSel);
+ if(xAlt.is())
+ {
+ //add the part between the previous and the current error
+ AddPortionIterated(rEditView, EditSelection(aCurSel.Max(), aNextSel.Min()), nullptr, rToFill);
+ //add the current error
+ AddPortionIterated(rEditView, aNextSel, xAlt, rToFill);
+ }
+ else
+ AddPortionIterated(rEditView, EditSelection(aCurSel.Max(), aSentencePaM.Max()), xAlt, rToFill);
+ aCurSel = aNextSel;
+ }
+ while( xAlt.is() );
+
+ //set the selection to the end of the current sentence
+ rEditView.pImpEditView->SetEditSelection(aSentencePaM.Max());
+ }
+ return bRet;
+}
+
+// Adds one portion to the SpellPortions
+void ImpEditEngine::AddPortion(
+ const EditSelection& rSel,
+ const uno::Reference< XSpellAlternatives >& xAlt,
+ svx::SpellPortions& rToFill,
+ bool bIsField)
+{
+ if(!rSel.HasRange())
+ return;
+
+ svx::SpellPortion aPortion;
+ aPortion.sText = GetSelected( rSel );
+ aPortion.eLanguage = GetLanguage( rSel.Min() ).nLang;
+ aPortion.xAlternatives = xAlt;
+ aPortion.bIsField = bIsField;
+ rToFill.push_back(aPortion);
+
+ //save the spelled portions for later use
+ pSpellInfo->aLastSpellPortions.push_back(aPortion);
+ pSpellInfo->aLastSpellContentSelections.push_back(rSel);
+}
+
+// Adds one or more portions of text to the SpellPortions depending on language changes
+void ImpEditEngine::AddPortionIterated(
+ EditView const & rEditView,
+ const EditSelection& rSel,
+ const Reference< XSpellAlternatives >& xAlt,
+ svx::SpellPortions& rToFill)
+{
+ if (!rSel.HasRange())
+ return;
+
+ if(xAlt.is())
+ {
+ AddPortion(rSel, xAlt, rToFill, false);
+ }
+ else
+ {
+ //iterate and search for language attribute changes
+ //save the start and end positions
+ bool bTest = rSel.Min().GetIndex() <= rSel.Max().GetIndex();
+ EditPaM aStart(bTest ? rSel.Min() : rSel.Max());
+ EditPaM aEnd(bTest ? rSel.Max() : rSel.Min());
+ //iterate over the text to find changes in language
+ //set the mark equal to the point
+ EditPaM aCursor(aStart);
+ rEditView.pImpEditView->SetEditSelection( aCursor );
+ LanguageType eStartLanguage = GetLanguage( aCursor ).nLang;
+ //search for a field attribute at the beginning - only the end position
+ //of this field is kept to end a portion at that position
+ const EditCharAttrib* pFieldAttr = aCursor.GetNode()->GetCharAttribs().
+ FindFeature( aCursor.GetIndex() );
+ bool bIsField = pFieldAttr &&
+ pFieldAttr->GetStart() == aCursor.GetIndex() &&
+ pFieldAttr->GetStart() != pFieldAttr->GetEnd() &&
+ pFieldAttr->Which() == EE_FEATURE_FIELD;
+ sal_Int32 nEndField = bIsField ? pFieldAttr->GetEnd() : -1;
+ do
+ {
+ aCursor = CursorRight( aCursor);
+ //determine whether a field and has been reached
+ bool bIsEndField = nEndField == aCursor.GetIndex();
+ //search for a new field attribute
+ const EditCharAttrib* _pFieldAttr = aCursor.GetNode()->GetCharAttribs().
+ FindFeature( aCursor.GetIndex() );
+ bIsField = _pFieldAttr &&
+ _pFieldAttr->GetStart() == aCursor.GetIndex() &&
+ _pFieldAttr->GetStart() != _pFieldAttr->GetEnd() &&
+ _pFieldAttr->Which() == EE_FEATURE_FIELD;
+ //on every new field move the end position
+ if (bIsField)
+ nEndField = _pFieldAttr->GetEnd();
+
+ LanguageType eCurLanguage = GetLanguage( aCursor ).nLang;
+ if(eCurLanguage != eStartLanguage || bIsField || bIsEndField)
+ {
+ eStartLanguage = eCurLanguage;
+ //go one step back - the cursor currently selects the first character
+ //with a different language
+ //create a selection from start to the current Cursor
+ EditSelection aSelection(aStart, aCursor);
+ AddPortion(aSelection, xAlt, rToFill, bIsEndField);
+ aStart = aCursor;
+ }
+ }
+ while(aCursor.GetIndex() < aEnd.GetIndex());
+ EditSelection aSelection(aStart, aCursor);
+ AddPortion(aSelection, xAlt, rToFill, bIsField);
+ }
+}
+
+void ImpEditEngine::ApplyChangedSentence(EditView const & rEditView,
+ const svx::SpellPortions& rNewPortions,
+ bool bRecheck )
+{
+ // Note: rNewPortions.size() == 0 is valid and happens when the whole
+ // sentence got removed in the dialog
+
+ DBG_ASSERT(pSpellInfo, "pSpellInfo not initialized");
+ if (!pSpellInfo || pSpellInfo->aLastSpellPortions.empty()) // no portions -> no text to be changed
+ return;
+
+ // get current paragraph length to calculate later on how the sentence length changed,
+ // in order to place the cursor at the end of the sentence again
+ EditSelection aOldSel( rEditView.pImpEditView->GetEditSelection() );
+ sal_Int32 nOldLen = aOldSel.Max().GetNode()->Len();
+
+ UndoActionStart( EDITUNDO_INSERT );
+ if(pSpellInfo->aLastSpellPortions.size() == rNewPortions.size())
+ {
+ DBG_ASSERT( !rNewPortions.empty(), "rNewPortions should not be empty here" );
+ DBG_ASSERT( pSpellInfo->aLastSpellPortions.size() == pSpellInfo->aLastSpellContentSelections.size(),
+ "aLastSpellPortions and aLastSpellContentSelections size mismatch" );
+
+ //the simple case: the same number of elements on both sides
+ //each changed element has to be applied to the corresponding source element
+ svx::SpellPortions::const_iterator aCurrentNewPortion = rNewPortions.end();
+ svx::SpellPortions::const_iterator aCurrentOldPortion = pSpellInfo->aLastSpellPortions.end();
+ SpellContentSelections::const_iterator aCurrentOldPosition = pSpellInfo->aLastSpellContentSelections.end();
+ bool bSetToEnd = false;
+ do
+ {
+ --aCurrentNewPortion;
+ --aCurrentOldPortion;
+ --aCurrentOldPosition;
+ //set the cursor to the end of the sentence - necessary to
+ //resume there at the next step
+ if(!bSetToEnd)
+ {
+ bSetToEnd = true;
+ rEditView.pImpEditView->SetEditSelection( aCurrentOldPosition->Max() );
+ }
+
+ SvtScriptType nScriptType = SvtLanguageOptions::GetScriptTypeOfLanguage( aCurrentNewPortion->eLanguage );
+ sal_uInt16 nLangWhichId = EE_CHAR_LANGUAGE;
+ switch(nScriptType)
+ {
+ case SvtScriptType::ASIAN : nLangWhichId = EE_CHAR_LANGUAGE_CJK; break;
+ case SvtScriptType::COMPLEX : nLangWhichId = EE_CHAR_LANGUAGE_CTL; break;
+ default: break;
+ }
+ if(aCurrentNewPortion->sText != aCurrentOldPortion->sText)
+ {
+ //change text and apply language
+ SfxItemSet aSet( maEditDoc.GetItemPool(), nLangWhichId, nLangWhichId );
+ aSet.Put(SvxLanguageItem(aCurrentNewPortion->eLanguage, nLangWhichId));
+ SetAttribs( *aCurrentOldPosition, aSet );
+ ImpInsertText( *aCurrentOldPosition, aCurrentNewPortion->sText );
+ }
+ else if(aCurrentNewPortion->eLanguage != aCurrentOldPortion->eLanguage)
+ {
+ //apply language
+ SfxItemSet aSet( maEditDoc.GetItemPool(), nLangWhichId, nLangWhichId);
+ aSet.Put(SvxLanguageItem(aCurrentNewPortion->eLanguage, nLangWhichId));
+ SetAttribs( *aCurrentOldPosition, aSet );
+ }
+ }
+ while(aCurrentNewPortion != rNewPortions.begin());
+ }
+ else
+ {
+ DBG_ASSERT( !pSpellInfo->aLastSpellContentSelections.empty(), "aLastSpellContentSelections should not be empty here" );
+
+ //select the complete sentence
+ SpellContentSelections::const_iterator aCurrentEndPosition = pSpellInfo->aLastSpellContentSelections.end();
+ --aCurrentEndPosition;
+ SpellContentSelections::const_iterator aCurrentStartPosition = pSpellInfo->aLastSpellContentSelections.begin();
+ EditSelection aAllSentence(aCurrentStartPosition->Min(), aCurrentEndPosition->Max());
+
+ //delete the sentence completely
+ ImpDeleteSelection( aAllSentence );
+ EditPaM aCurrentPaM = aAllSentence.Min();
+ for(const auto& rCurrentNewPortion : rNewPortions)
+ {
+ //set the language attribute
+ LanguageType eCurLanguage = GetLanguage( aCurrentPaM ).nLang;
+ if(eCurLanguage != rCurrentNewPortion.eLanguage)
+ {
+ SvtScriptType nScriptType = SvtLanguageOptions::GetScriptTypeOfLanguage( rCurrentNewPortion.eLanguage );
+ sal_uInt16 nLangWhichId = EE_CHAR_LANGUAGE;
+ switch(nScriptType)
+ {
+ case SvtScriptType::ASIAN : nLangWhichId = EE_CHAR_LANGUAGE_CJK; break;
+ case SvtScriptType::COMPLEX : nLangWhichId = EE_CHAR_LANGUAGE_CTL; break;
+ default: break;
+ }
+ SfxItemSet aSet( maEditDoc.GetItemPool(), nLangWhichId, nLangWhichId);
+ aSet.Put(SvxLanguageItem(rCurrentNewPortion.eLanguage, nLangWhichId));
+ SetAttribs( aCurrentPaM, aSet );
+ }
+ //insert the new string and set the cursor to the end of the inserted string
+ aCurrentPaM = ImpInsertText( aCurrentPaM , rCurrentNewPortion.sText );
+ }
+ }
+ UndoActionEnd();
+
+ EditPaM aNext;
+ if (bRecheck)
+ aNext = pSpellInfo->aCurSentenceStart;
+ else
+ {
+ // restore cursor position to the end of the modified sentence.
+ // (This will define the continuation position for spell/grammar checking)
+ // First: check if the sentence/para length changed
+ const sal_Int32 nDelta = rEditView.pImpEditView->GetEditSelection().Max().GetNode()->Len() - nOldLen;
+ const sal_Int32 nEndOfSentence = aOldSel.Max().GetIndex() + nDelta;
+ aNext = EditPaM( aOldSel.Max().GetNode(), nEndOfSentence );
+ }
+ rEditView.pImpEditView->SetEditSelection( aNext );
+
+ if (IsUpdateLayout())
+ FormatAndLayout();
+ maEditDoc.SetModified(true);
+}
+
+void ImpEditEngine::PutSpellingToSentenceStart( EditView const & rEditView )
+{
+ if( pSpellInfo && !pSpellInfo->aLastSpellContentSelections.empty() )
+ {
+ rEditView.pImpEditView->SetEditSelection( pSpellInfo->aLastSpellContentSelections.begin()->Min() );
+ }
+}
+
+
+void ImpEditEngine::DoOnlineSpelling( ContentNode* pThisNodeOnly, bool bSpellAtCursorPos, bool bInterruptible )
+{
+ /*
+ It will iterate over all the paragraphs, paragraphs with only
+ invalidated wrong list will be checked ...
+
+ All the words are checked in the invalidated region. Is a word wrong,
+ but not in the wrong list, or vice versa, the range of the word will be
+ invalidated
+ (no Invalidate, but if only transitions wrong from right =>, simple Paint,
+ even out properly with VDev on transitions from wrong => right)
+ */
+
+ if ( !xSpeller.is() )
+ return;
+
+ EditPaM aCursorPos;
+ if( pActiveView && !bSpellAtCursorPos )
+ {
+ aCursorPos = pActiveView->pImpEditView->GetEditSelection().Max();
+ }
+
+ bool bRestartTimer = false;
+
+ ContentNode* pLastNode = maEditDoc.GetObject( maEditDoc.Count() - 1 );
+ sal_Int32 nNodes = GetEditDoc().Count();
+ sal_Int32 nInvalids = 0;
+ Sequence< PropertyValue > aEmptySeq;
+ for ( sal_Int32 n = 0; n < nNodes; n++ )
+ {
+ ContentNode* pNode = GetEditDoc().GetObject( n );
+ if ( pThisNodeOnly )
+ pNode = pThisNodeOnly;
+
+ pNode->EnsureWrongList();
+ if (!pNode->GetWrongList()->IsValid())
+ {
+ WrongList* pWrongList = pNode->GetWrongList();
+ const size_t nInvStart = pWrongList->GetInvalidStart();
+ const size_t nInvEnd = pWrongList->GetInvalidEnd();
+
+ sal_Int32 nPaintFrom = -1;
+ sal_Int32 nPaintTo = 0;
+ bool bSimpleRepaint = true;
+
+ pWrongList->SetValid();
+
+ EditPaM aPaM( pNode, nInvStart );
+ EditSelection aSel( aPaM, aPaM );
+ while ( aSel.Max().GetNode() == pNode )
+ {
+ if ( ( o3tl::make_unsigned(aSel.Min().GetIndex()) > nInvEnd )
+ || ( ( aSel.Max().GetNode() == pLastNode ) && ( aSel.Max().GetIndex() >= pLastNode->Len() ) ) )
+ break; // Document end or end of invalid region
+
+ aSel = SelectWord( aSel, i18n::WordType::DICTIONARY_WORD );
+ // If afterwards a dot, this must be handed over!
+ // If an abbreviation ...
+ bool bDottAdded = false;
+ if ( aSel.Max().GetIndex() < aSel.Max().GetNode()->Len() )
+ {
+ sal_Unicode cNext = aSel.Max().GetNode()->GetChar( aSel.Max().GetIndex() );
+ if ( cNext == '.' )
+ {
+ aSel.Max().SetIndex( aSel.Max().GetIndex()+1 );
+ bDottAdded = true;
+ }
+ }
+ OUString aWord = GetSelected(aSel);
+
+ bool bChanged = false;
+ if (!aWord.isEmpty())
+ {
+ const sal_Int32 nWStart = aSel.Min().GetIndex();
+ const sal_Int32 nWEnd = aSel.Max().GetIndex();
+ if ( !xSpeller->isValid( aWord, static_cast<sal_uInt16>(GetLanguage( EditPaM( aSel.Min().GetNode(), nWStart+1 ) ).nLang), aEmptySeq ) )
+ {
+ // Check if already marked correctly...
+ const sal_Int32 nXEnd = bDottAdded ? nWEnd -1 : nWEnd;
+ if ( !pWrongList->HasWrong( nWStart, nXEnd ) )
+ {
+ // Mark Word as wrong...
+ // But only when not at Cursor-Position...
+ bool bCursorPos = false;
+ if ( aCursorPos.GetNode() == pNode )
+ {
+ if ( ( nWStart <= aCursorPos.GetIndex() ) && nWEnd >= aCursorPos.GetIndex() )
+ bCursorPos = true;
+ }
+ if ( bCursorPos )
+ {
+ // Then continue to mark as invalid ...
+ pWrongList->ResetInvalidRange(nWStart, nWEnd);
+ bRestartTimer = true;
+ }
+ else
+ {
+ // It may be that the Wrongs in the list are not
+ // spanning exactly over words because the
+ // WordDelimiters during expansion are not
+ // evaluated.
+ pWrongList->InsertWrong(nWStart, nXEnd);
+ bChanged = true;
+ }
+ }
+ }
+ else
+ {
+ // Check if not marked as wrong
+ if ( pWrongList->HasAnyWrong( nWStart, nWEnd ) )
+ {
+ pWrongList->ClearWrongs( nWStart, nWEnd, pNode );
+ bSimpleRepaint = false;
+ bChanged = true;
+ }
+ }
+ if ( bChanged )
+ {
+ if ( nPaintFrom<0 )
+ nPaintFrom = nWStart;
+ nPaintTo = nWEnd;
+ }
+ }
+
+ EditPaM aLastEnd( aSel.Max() );
+ aSel = WordRight( aSel.Max(), i18n::WordType::DICTIONARY_WORD );
+ if ( bChanged && ( aSel.Min().GetNode() == pNode ) &&
+ ( aSel.Min().GetIndex()-aLastEnd.GetIndex() > 1 ) )
+ {
+ // If two words are separated by more than one blank, it
+ // can happen that when splitting a Wrongs the start of
+ // the second word is before the actually word
+ pWrongList->ClearWrongs( aLastEnd.GetIndex(), aSel.Min().GetIndex(), pNode );
+ }
+ }
+
+ // Invalidate?
+ if ( nPaintFrom>=0 )
+ {
+ maStatus.GetStatusWord() |= EditStatusFlags::WRONGWORDCHANGED;
+ CallStatusHdl();
+
+ if (!aEditViews.empty())
+ {
+ // For SimpleRepaint one was painted over a range without
+ // reaching VDEV, but then one would have to intersect, c
+ // clipping, ... over all views. Probably not worthwhile.
+ EditPaM aStartPaM( pNode, nPaintFrom );
+ EditPaM aEndPaM( pNode, nPaintTo );
+ tools::Rectangle aStartCursor( PaMtoEditCursor( aStartPaM ) );
+ tools::Rectangle aEndCursor( PaMtoEditCursor( aEndPaM ) );
+ DBG_ASSERT( aInvalidRect.IsEmpty(), "InvalidRect set!" );
+ aInvalidRect.SetLeft( 0 );
+ aInvalidRect.SetRight( GetPaperSize().Width() );
+ aInvalidRect.SetTop( aStartCursor.Top() );
+ aInvalidRect.SetBottom( aEndCursor.Bottom() );
+ if ( pActiveView && pActiveView->HasSelection() )
+ {
+ // Then no output through VDev.
+ UpdateViews();
+ }
+ else if ( bSimpleRepaint )
+ {
+ for (EditView* pView : aEditViews)
+ {
+ tools::Rectangle aClipRect( aInvalidRect );
+ aClipRect.Intersection( pView->GetVisArea() );
+ if ( !aClipRect.IsEmpty() )
+ {
+ // convert to window coordinates...
+ aClipRect.SetPos( pView->pImpEditView->GetWindowPos( aClipRect.TopLeft() ) );
+ pView->pImpEditView->InvalidateAtWindow(aClipRect);
+ }
+ }
+ }
+ else
+ {
+ UpdateViews( pActiveView );
+ }
+ aInvalidRect = tools::Rectangle();
+ }
+ }
+ // After two corrected nodes give up the control...
+ nInvalids++;
+ if ( bInterruptible && ( nInvalids >= 2 ) )
+ {
+ bRestartTimer = true;
+ break;
+ }
+ }
+
+ if ( pThisNodeOnly )
+ break;
+ }
+ if ( bRestartTimer )
+ aOnlineSpellTimer.Start();
+}
+
+
+EESpellState ImpEditEngine::HasSpellErrors()
+{
+ DBG_ASSERT( xSpeller.is(), "No spell checker set!" );
+
+ ContentNode* pLastNode = maEditDoc.GetObject( maEditDoc.Count() - 1 );
+ EditSelection aCurSel( maEditDoc.GetStartPaM() );
+
+ OUString aWord;
+ Reference< XSpellAlternatives > xSpellAlt;
+ Sequence< PropertyValue > aEmptySeq;
+ while ( !xSpellAlt.is() )
+ {
+ if ( ( aCurSel.Max().GetNode() == pLastNode ) &&
+ ( aCurSel.Max().GetIndex() >= pLastNode->Len() ) )
+ {
+ return EESpellState::Ok;
+ }
+
+ aCurSel = SelectWord( aCurSel, css::i18n::WordType::DICTIONARY_WORD );
+ aWord = GetSelected( aCurSel );
+ if ( !aWord.isEmpty() )
+ {
+ LanguageType eLang = GetLanguage( aCurSel.Max() ).nLang;
+ SvxSpellWrapper::CheckSpellLang( xSpeller, eLang );
+ xSpellAlt = xSpeller->spell( aWord, static_cast<sal_uInt16>(eLang), aEmptySeq );
+ }
+ aCurSel = WordRight( aCurSel.Max(), css::i18n::WordType::DICTIONARY_WORD );
+ }
+
+ return EESpellState::ErrorFound;
+}
+
+void ImpEditEngine::ClearSpellErrors()
+{
+ maEditDoc.ClearSpellErrors();
+}
+
+EESpellState ImpEditEngine::StartThesaurus(EditView* pEditView, weld::Widget* pDialogParent)
+{
+ EditSelection aCurSel( pEditView->pImpEditView->GetEditSelection() );
+ if ( !aCurSel.HasRange() )
+ aCurSel = SelectWord( aCurSel, css::i18n::WordType::DICTIONARY_WORD );
+ OUString aWord( GetSelected( aCurSel ) );
+
+ Reference< XThesaurus > xThes( LinguMgr::GetThesaurus() );
+ if (!xThes.is())
+ return EESpellState::ErrorFound;
+
+ EditAbstractDialogFactory* pFact = EditAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractThesaurusDialog> xDlg(pFact->CreateThesaurusDialog(pDialogParent, xThes,
+ aWord, GetLanguage( aCurSel.Max() ).nLang ));
+ if (xDlg->Execute() == RET_OK)
+ {
+ // Replace Word...
+ pEditView->pImpEditView->DrawSelectionXOR();
+ pEditView->pImpEditView->SetEditSelection( aCurSel );
+ pEditView->pImpEditView->DrawSelectionXOR();
+ pEditView->InsertText(xDlg->GetWord());
+ pEditView->ShowCursor(true, false);
+ }
+
+ return EESpellState::Ok;
+}
+
+sal_Int32 ImpEditEngine::StartSearchAndReplace( EditView* pEditView, const SvxSearchItem& rSearchItem )
+{
+ sal_Int32 nFound = 0;
+
+ EditSelection aCurSel( pEditView->pImpEditView->GetEditSelection() );
+
+ // FIND_ALL is not possible without multiple selection.
+ if ( ( rSearchItem.GetCommand() == SvxSearchCmd::FIND ) ||
+ ( rSearchItem.GetCommand() == SvxSearchCmd::FIND_ALL ) )
+ {
+ if ( Search( rSearchItem, pEditView ) )
+ nFound++;
+ }
+ else if ( rSearchItem.GetCommand() == SvxSearchCmd::REPLACE )
+ {
+ // The word is selected if the user not altered the selection
+ // in between:
+ if ( aCurSel.HasRange() )
+ {
+ pEditView->InsertText( rSearchItem.GetReplaceString() );
+ nFound = 1;
+ }
+ else
+ if( Search( rSearchItem, pEditView ) )
+ nFound = 1;
+ }
+ else if ( rSearchItem.GetCommand() == SvxSearchCmd::REPLACE_ALL )
+ {
+ // The Writer replaces all front beginning to end ...
+ SvxSearchItem aTmpItem( rSearchItem );
+ aTmpItem.SetBackward( false );
+
+ pEditView->pImpEditView->DrawSelectionXOR();
+
+ aCurSel.Adjust( maEditDoc );
+ EditPaM aStartPaM = aTmpItem.GetSelection() ? aCurSel.Min() : maEditDoc.GetStartPaM();
+ EditSelection aFoundSel( aCurSel.Max() );
+ bool bFound = ImpSearch( aTmpItem, aCurSel, aStartPaM, aFoundSel );
+ if ( bFound )
+ UndoActionStart( EDITUNDO_REPLACEALL );
+ while ( bFound )
+ {
+ nFound++;
+ aStartPaM = ImpInsertText( aFoundSel, rSearchItem.GetReplaceString() );
+ bFound = ImpSearch( aTmpItem, aCurSel, aStartPaM, aFoundSel );
+ }
+ if ( nFound )
+ {
+ EditPaM aNewPaM( aFoundSel.Max() );
+ if ( aNewPaM.GetIndex() > aNewPaM.GetNode()->Len() )
+ aNewPaM.SetIndex( aNewPaM.GetNode()->Len() );
+ pEditView->pImpEditView->SetEditSelection( aNewPaM );
+ FormatAndLayout( pEditView );
+ UndoActionEnd();
+ }
+ else
+ {
+ pEditView->pImpEditView->DrawSelectionXOR();
+ pEditView->ShowCursor( true, false );
+ }
+ }
+ return nFound;
+}
+
+bool ImpEditEngine::Search( const SvxSearchItem& rSearchItem, EditView* pEditView )
+{
+ EditSelection aSel( pEditView->pImpEditView->GetEditSelection() );
+ aSel.Adjust( maEditDoc );
+ EditPaM aStartPaM( aSel.Max() );
+ if ( rSearchItem.GetSelection() && !rSearchItem.GetBackward() )
+ aStartPaM = aSel.Min();
+
+ EditSelection aFoundSel;
+ bool bFound = ImpSearch( rSearchItem, aSel, aStartPaM, aFoundSel );
+ if ( bFound && ( aFoundSel == aSel ) ) // For backwards-search
+ {
+ aStartPaM = aSel.Min();
+ bFound = ImpSearch( rSearchItem, aSel, aStartPaM, aFoundSel );
+ }
+
+ pEditView->pImpEditView->DrawSelectionXOR();
+ if ( bFound )
+ {
+ // First, set the minimum, so the whole word is in the visible range.
+ pEditView->pImpEditView->SetEditSelection( aFoundSel.Min() );
+ pEditView->ShowCursor( true, false );
+ pEditView->pImpEditView->SetEditSelection( aFoundSel );
+ }
+ else
+ pEditView->pImpEditView->SetEditSelection( aSel.Max() );
+
+ pEditView->pImpEditView->DrawSelectionXOR();
+ pEditView->ShowCursor( true, false );
+ return bFound;
+}
+
+bool ImpEditEngine::ImpSearch( const SvxSearchItem& rSearchItem,
+ const EditSelection& rSearchSelection, const EditPaM& rStartPos, EditSelection& rFoundSel )
+{
+ i18nutil::SearchOptions2 aSearchOptions( rSearchItem.GetSearchOptions() );
+ aSearchOptions.Locale = GetLocale( rStartPos );
+
+ bool bBack = rSearchItem.GetBackward();
+ bool bSearchInSelection = rSearchItem.GetSelection();
+ sal_Int32 nStartNode = maEditDoc.GetPos( rStartPos.GetNode() );
+ sal_Int32 nEndNode;
+ if ( bSearchInSelection )
+ {
+ nEndNode = maEditDoc.GetPos( bBack ? rSearchSelection.Min().GetNode() : rSearchSelection.Max().GetNode() );
+ }
+ else
+ {
+ nEndNode = bBack ? 0 : maEditDoc.Count()-1;
+ }
+
+ utl::TextSearch aSearcher( aSearchOptions );
+
+ // iterate over the paragraphs ...
+ for ( sal_Int32 nNode = nStartNode;
+ bBack ? ( nNode >= nEndNode ) : ( nNode <= nEndNode) ;
+ bBack ? nNode-- : nNode++ )
+ {
+ // For backwards-search if nEndNode = 0:
+ if ( nNode < 0 )
+ return false;
+
+ ContentNode* pNode = maEditDoc.GetObject( nNode );
+
+ sal_Int32 nStartPos = 0;
+ sal_Int32 nEndPos = pNode->GetExpandedLen();
+ if ( nNode == nStartNode )
+ {
+ if ( bBack )
+ nEndPos = rStartPos.GetIndex();
+ else
+ nStartPos = rStartPos.GetIndex();
+ }
+ if ( ( nNode == nEndNode ) && bSearchInSelection )
+ {
+ if ( bBack )
+ nStartPos = rSearchSelection.Min().GetIndex();
+ else
+ nEndPos = rSearchSelection.Max().GetIndex();
+ }
+
+ // Searching ...
+ OUString aParaStr( pNode->GetExpandedText() );
+ bool bFound = false;
+ if ( bBack )
+ {
+ sal_Int32 nTemp;
+ nTemp = nStartPos;
+ nStartPos = nEndPos;
+ nEndPos = nTemp;
+
+ bFound = aSearcher.SearchBackward( aParaStr, &nStartPos, &nEndPos);
+ }
+ else
+ {
+ bFound = aSearcher.SearchForward( aParaStr, &nStartPos, &nEndPos);
+ }
+ if ( bFound )
+ {
+ pNode->UnExpandPositions( nStartPos, nEndPos );
+
+ rFoundSel.Min().SetNode( pNode );
+ rFoundSel.Min().SetIndex( nStartPos );
+ rFoundSel.Max().SetNode( pNode );
+ rFoundSel.Max().SetIndex( nEndPos );
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ImpEditEngine::HasText( const SvxSearchItem& rSearchItem )
+{
+ SvxSearchItem aTmpItem( rSearchItem );
+ aTmpItem.SetBackward( false );
+ aTmpItem.SetSelection( false );
+
+ EditPaM aStartPaM( maEditDoc.GetStartPaM() );
+ EditSelection aDummySel( aStartPaM );
+ EditSelection aFoundSel;
+ return ImpSearch( aTmpItem, aDummySel, aStartPaM, aFoundSel );
+}
+
+void ImpEditEngine::SetAutoCompleteText(const OUString& rStr, bool bClearTipWindow)
+{
+ maAutoCompleteText = rStr;
+ if ( bClearTipWindow && pActiveView )
+ Help::ShowQuickHelp( pActiveView->GetWindow(), tools::Rectangle(), OUString() );
+}
+
+namespace
+{
+ struct eeTransliterationChgData
+ {
+ sal_Int32 nStart;
+ sal_Int32 nLen;
+ EditSelection aSelection;
+ OUString aNewText;
+ uno::Sequence< sal_Int32 > aOffsets;
+ };
+}
+
+EditSelection ImpEditEngine::TransliterateText( const EditSelection& rSelection, TransliterationFlags nTransliterationMode )
+{
+ uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
+ if (!_xBI.is())
+ return rSelection;
+
+ EditSelection aSel( rSelection );
+ aSel.Adjust( maEditDoc );
+
+ if ( !aSel.HasRange() )
+ {
+ /* Cursor is inside of a word */
+ if (nTransliterationMode == TransliterationFlags::SENTENCE_CASE)
+ aSel = SelectSentence( aSel );
+ else
+ aSel = SelectWord( aSel );
+ }
+
+ // tdf#107176: if there's still no range, just return aSel
+ if ( !aSel.HasRange() )
+ return aSel;
+
+ EditSelection aNewSel( aSel );
+
+ const sal_Int32 nStartNode = maEditDoc.GetPos( aSel.Min().GetNode() );
+ const sal_Int32 nEndNode = maEditDoc.GetPos( aSel.Max().GetNode() );
+
+ bool bChanges = false;
+ bool bLenChanged = false;
+ std::unique_ptr<EditUndoTransliteration> pUndo;
+
+ utl::TransliterationWrapper aTransliterationWrapper( ::comphelper::getProcessComponentContext(), nTransliterationMode );
+ bool bConsiderLanguage = aTransliterationWrapper.needLanguageForTheMode();
+
+ for ( sal_Int32 nNode = nStartNode; nNode <= nEndNode; nNode++ )
+ {
+ ContentNode* pNode = maEditDoc.GetObject( nNode );
+ const OUString& aNodeStr = pNode->GetString();
+ const sal_Int32 nStartPos = nNode==nStartNode ? aSel.Min().GetIndex() : 0;
+ const sal_Int32 nEndPos = nNode==nEndNode ? aSel.Max().GetIndex() : aNodeStr.getLength(); // can also be == nStart!
+
+ sal_Int32 nCurrentStart = nStartPos;
+ sal_Int32 nCurrentEnd = nEndPos;
+ LanguageType nLanguage = LANGUAGE_SYSTEM;
+
+ // since we don't use Hiragana/Katakana or half-width/full-width transliterations here
+ // it is fine to use ANYWORD_IGNOREWHITESPACES. (ANY_WORD btw is broken and will
+ // occasionally miss words in consecutive sentences). Also with ANYWORD_IGNOREWHITESPACES
+ // text like 'just-in-time' will be converted to 'Just-In-Time' which seems to be the
+ // proper thing to do.
+ const sal_Int16 nWordType = i18n::WordType::ANYWORD_IGNOREWHITESPACES;
+
+ //! In order to have less trouble with changing text size, e.g. because
+ //! of ligatures or German small sz being resolved, we need to process
+ //! the text replacements from end to start.
+ //! This way the offsets for the yet to be changed words will be
+ //! left unchanged by the already replaced text.
+ //! For this we temporarily save the changes to be done in this vector
+ std::vector< eeTransliterationChgData > aChanges;
+ eeTransliterationChgData aChgData;
+
+ if (nTransliterationMode == TransliterationFlags::TITLE_CASE)
+ {
+ // for 'capitalize every word' we need to iterate over each word
+
+ i18n::Boundary aSttBndry;
+ i18n::Boundary aEndBndry;
+ aSttBndry = _xBI->getWordBoundary(
+ aNodeStr, nStartPos,
+ GetLocale( EditPaM( pNode, nStartPos + 1 ) ),
+ nWordType, true /*prefer forward direction*/);
+ aEndBndry = _xBI->getWordBoundary(
+ aNodeStr, nEndPos,
+ GetLocale( EditPaM( pNode, nEndPos + 1 ) ),
+ nWordType, false /*prefer backward direction*/);
+
+ // prevent backtracking to the previous word if selection is at word boundary
+ if (aSttBndry.endPos <= nStartPos)
+ {
+ aSttBndry = _xBI->nextWord(
+ aNodeStr, aSttBndry.endPos,
+ GetLocale( EditPaM( pNode, aSttBndry.endPos + 1 ) ),
+ nWordType);
+ }
+ // prevent advancing to the next word if selection is at word boundary
+ if (aEndBndry.startPos >= nEndPos)
+ {
+ aEndBndry = _xBI->previousWord(
+ aNodeStr, aEndBndry.startPos,
+ GetLocale( EditPaM( pNode, aEndBndry.startPos + 1 ) ),
+ nWordType);
+ }
+
+ /* Nothing to do if user selection lies entirely outside of word start and end boundary computed above.
+ * Skip this node, because otherwise the below logic for constraining to the selection will fail */
+ if (aSttBndry.startPos >= aSel.Max().GetIndex() || aEndBndry.endPos <= aSel.Min().GetIndex()) {
+ continue;
+ }
+
+ // prevent going outside of the user's selection, which may
+ // start or end in the middle of a word
+ if (nNode == nStartNode) {
+ aSttBndry.startPos = std::max(aSttBndry.startPos, aSel.Min().GetIndex());
+ aSttBndry.endPos = std::min(aSttBndry.endPos, aSel.Max().GetIndex());
+ aEndBndry.startPos = std::max(aEndBndry.startPos, aSttBndry.startPos);
+ aEndBndry.endPos = std::min(aEndBndry.endPos, aSel.Max().GetIndex());
+ }
+
+ i18n::Boundary aCurWordBndry( aSttBndry );
+ while (aCurWordBndry.endPos && aCurWordBndry.startPos <= aEndBndry.startPos)
+ {
+ nCurrentStart = aCurWordBndry.startPos;
+ nCurrentEnd = aCurWordBndry.endPos;
+ sal_Int32 nLen = nCurrentEnd - nCurrentStart;
+ DBG_ASSERT( nLen > 0, "invalid word length of 0" );
+
+ Sequence< sal_Int32 > aOffsets;
+ OUString aNewText( aTransliterationWrapper.transliterate(aNodeStr,
+ GetLanguage( EditPaM( pNode, nCurrentStart + 1 ) ).nLang,
+ nCurrentStart, nLen, &aOffsets ));
+
+ if (aNodeStr != aNewText)
+ {
+ aChgData.nStart = nCurrentStart;
+ aChgData.nLen = nLen;
+ aChgData.aSelection = EditSelection( EditPaM( pNode, nCurrentStart ), EditPaM( pNode, nCurrentEnd ) );
+ aChgData.aNewText = aNewText;
+ aChgData.aOffsets = aOffsets;
+ aChanges.push_back( aChgData );
+ }
+#if OSL_DEBUG_LEVEL > 1
+ OUString aSelTxt ( GetSelected( aChgData.aSelection ) );
+ (void) aSelTxt;
+#endif
+
+ aCurWordBndry = _xBI->nextWord(aNodeStr, nCurrentStart,
+ GetLocale( EditPaM( pNode, nCurrentStart + 1 ) ),
+ nWordType);
+ }
+ DBG_ASSERT( nCurrentEnd >= aEndBndry.endPos, "failed to reach end of transliteration" );
+ }
+ else if (nTransliterationMode == TransliterationFlags::SENTENCE_CASE)
+ {
+ // for 'sentence case' we need to iterate sentence by sentence
+
+ sal_Int32 nLastStart = _xBI->beginOfSentence(
+ aNodeStr, nEndPos,
+ GetLocale( EditPaM( pNode, nEndPos + 1 ) ) );
+ sal_Int32 nLastEnd = _xBI->endOfSentence(
+ aNodeStr, nLastStart,
+ GetLocale( EditPaM( pNode, nLastStart + 1 ) ) );
+
+ // extend nCurrentStart, nCurrentEnd to the current sentence boundaries
+ nCurrentStart = _xBI->beginOfSentence(
+ aNodeStr, nStartPos,
+ GetLocale( EditPaM( pNode, nStartPos + 1 ) ) );
+ nCurrentEnd = _xBI->endOfSentence(
+ aNodeStr, nCurrentStart,
+ GetLocale( EditPaM( pNode, nCurrentStart + 1 ) ) );
+
+ // prevent backtracking to the previous sentence if selection starts at end of a sentence
+ if (nCurrentEnd <= nStartPos)
+ {
+ // now nCurrentStart is probably located on a non-letter word. (unless we
+ // are in Asian text with no spaces...)
+ // Thus to get the real sentence start we should locate the next real word,
+ // that is one found by DICTIONARY_WORD
+ i18n::Boundary aBndry = _xBI->nextWord( aNodeStr, nCurrentEnd,
+ GetLocale( EditPaM( pNode, nCurrentEnd + 1 ) ),
+ i18n::WordType::DICTIONARY_WORD);
+
+ // now get new current sentence boundaries
+ nCurrentStart = _xBI->beginOfSentence(
+ aNodeStr, aBndry.startPos,
+ GetLocale( EditPaM( pNode, aBndry.startPos + 1 ) ) );
+ nCurrentEnd = _xBI->endOfSentence(
+ aNodeStr, nCurrentStart,
+ GetLocale( EditPaM( pNode, nCurrentStart + 1 ) ) );
+ }
+ // prevent advancing to the next sentence if selection ends at start of a sentence
+ if (nLastStart >= nEndPos)
+ {
+ // now nCurrentStart is probably located on a non-letter word. (unless we
+ // are in Asian text with no spaces...)
+ // Thus to get the real sentence start we should locate the previous real word,
+ // that is one found by DICTIONARY_WORD
+ i18n::Boundary aBndry = _xBI->previousWord( aNodeStr, nLastStart,
+ GetLocale( EditPaM( pNode, nLastStart + 1 ) ),
+ i18n::WordType::DICTIONARY_WORD);
+ nLastEnd = _xBI->endOfSentence(
+ aNodeStr, aBndry.startPos,
+ GetLocale( EditPaM( pNode, aBndry.startPos + 1 ) ) );
+ if (nCurrentEnd > nLastEnd)
+ nCurrentEnd = nLastEnd;
+ }
+
+ // prevent making any change outside of the user's selection
+ nCurrentStart = std::max(aSel.Min().GetIndex(), nCurrentStart);
+ nCurrentEnd = std::min(aSel.Max().GetIndex(), nCurrentEnd);
+ nLastStart = std::max(aSel.Min().GetIndex(), nLastStart);
+ nLastEnd = std::min(aSel.Max().GetIndex(), nLastEnd);
+
+ while (nCurrentStart < nLastEnd)
+ {
+ const sal_Int32 nLen = nCurrentEnd - nCurrentStart;
+ DBG_ASSERT( nLen > 0, "invalid word length of 0" );
+
+ Sequence< sal_Int32 > aOffsets;
+ OUString aNewText( aTransliterationWrapper.transliterate( aNodeStr,
+ GetLanguage( EditPaM( pNode, nCurrentStart + 1 ) ).nLang,
+ nCurrentStart, nLen, &aOffsets ));
+
+ if (aNodeStr != aNewText)
+ {
+ aChgData.nStart = nCurrentStart;
+ aChgData.nLen = nLen;
+ aChgData.aSelection = EditSelection( EditPaM( pNode, nCurrentStart ), EditPaM( pNode, nCurrentEnd ) );
+ aChgData.aNewText = aNewText;
+ aChgData.aOffsets = aOffsets;
+ aChanges.push_back( aChgData );
+ }
+
+ i18n::Boundary aFirstWordBndry = _xBI->nextWord(
+ aNodeStr, nCurrentEnd,
+ GetLocale( EditPaM( pNode, nCurrentEnd + 1 ) ),
+ nWordType);
+ nCurrentStart = aFirstWordBndry.startPos;
+ nCurrentEnd = _xBI->endOfSentence(
+ aNodeStr, nCurrentStart,
+ GetLocale( EditPaM( pNode, nCurrentStart + 1 ) ) );
+ }
+ DBG_ASSERT( nCurrentEnd >= nLastEnd, "failed to reach end of transliteration" );
+ }
+ else
+ {
+ do
+ {
+ if ( bConsiderLanguage )
+ {
+ nLanguage = GetLanguage( EditPaM( pNode, nCurrentStart+1 ), &nCurrentEnd ).nLang;
+ if ( nCurrentEnd > nEndPos )
+ nCurrentEnd = nEndPos;
+ }
+
+ const sal_Int32 nLen = nCurrentEnd - nCurrentStart;
+
+ Sequence< sal_Int32 > aOffsets;
+ OUString aNewText( aTransliterationWrapper.transliterate( aNodeStr, nLanguage, nCurrentStart, nLen, &aOffsets ) );
+
+ if (aNodeStr != aNewText)
+ {
+ aChgData.nStart = nCurrentStart;
+ aChgData.nLen = nLen;
+ aChgData.aSelection = EditSelection( EditPaM( pNode, nCurrentStart ), EditPaM( pNode, nCurrentEnd ) );
+ aChgData.aNewText = aNewText;
+ aChgData.aOffsets = aOffsets;
+ aChanges.push_back( aChgData );
+ }
+
+ nCurrentStart = nCurrentEnd;
+ } while( nCurrentEnd < nEndPos );
+ }
+
+ if (!aChanges.empty())
+ {
+ // Create a single UndoAction on Demand for all the changes ...
+ if ( !pUndo && IsUndoEnabled() && !IsInUndo() )
+ {
+ // adjust selection to include all changes
+ for (const eeTransliterationChgData & aChange : aChanges)
+ {
+ const EditSelection &rSel = aChange.aSelection;
+ if (aSel.Min().GetNode() == rSel.Min().GetNode() &&
+ aSel.Min().GetIndex() > rSel.Min().GetIndex())
+ aSel.Min().SetIndex( rSel.Min().GetIndex() );
+ if (aSel.Max().GetNode() == rSel.Max().GetNode() &&
+ aSel.Max().GetIndex() < rSel.Max().GetIndex())
+ aSel.Max().SetIndex( rSel.Max().GetIndex() );
+ }
+ aNewSel = aSel;
+
+ ESelection aESel( CreateESel( aSel ) );
+ pUndo.reset(new EditUndoTransliteration(pEditEngine, aESel, nTransliterationMode));
+
+ const bool bSingleNode = aSel.Min().GetNode()== aSel.Max().GetNode();
+ const bool bHasAttribs = aSel.Min().GetNode()->GetCharAttribs().HasAttrib( aSel.Min().GetIndex(), aSel.Max().GetIndex() );
+ if (bSingleNode && !bHasAttribs)
+ pUndo->SetText( aSel.Min().GetNode()->Copy( aSel.Min().GetIndex(), aSel.Max().GetIndex()-aSel.Min().GetIndex() ) );
+ else
+ pUndo->SetText( CreateTextObject( aSel, nullptr ) );
+ }
+
+ // now apply the changes from end to start to leave the offsets of the
+ // yet unchanged text parts remain the same.
+ for (size_t i = 0; i < aChanges.size(); ++i)
+ {
+ eeTransliterationChgData& rData = aChanges[ aChanges.size() - 1 - i ];
+
+ bChanges = true;
+ if (rData.nLen != rData.aNewText.getLength())
+ bLenChanged = true;
+
+ // Change text without losing the attributes
+ const sal_Int32 nDiffs =
+ ReplaceTextOnly( rData.aSelection.Min().GetNode(),
+ rData.nStart, rData.aNewText, rData.aOffsets );
+
+ // adjust selection in end node to possibly changed size
+ if (aSel.Max().GetNode() == rData.aSelection.Max().GetNode())
+ aNewSel.Max().SetIndex( aNewSel.Max().GetIndex() + nDiffs );
+
+ sal_Int32 nSelNode = maEditDoc.GetPos( rData.aSelection.Min().GetNode() );
+ ParaPortion* pParaPortion = GetParaPortions()[nSelNode];
+ pParaPortion->MarkSelectionInvalid( rData.nStart );
+ }
+ }
+ }
+
+ if ( pUndo )
+ {
+ ESelection aESel( CreateESel( aNewSel ) );
+ pUndo->SetNewSelection( aESel );
+ InsertUndo( std::move(pUndo) );
+ }
+
+ if ( bChanges )
+ {
+ TextModified();
+ SetModifyFlag( true );
+ if ( bLenChanged )
+ UpdateSelections();
+ if (IsUpdateLayout())
+ FormatAndLayout();
+ }
+
+ return aNewSel;
+}
+
+
+short ImpEditEngine::ReplaceTextOnly(
+ ContentNode* pNode,
+ sal_Int32 nCurrentStart,
+ std::u16string_view rNewText,
+ const uno::Sequence< sal_Int32 >& rOffsets )
+{
+ // Change text without losing the attributes
+ sal_Int32 nCharsAfterTransliteration = rOffsets.getLength();
+ const sal_Int32* pOffsets = rOffsets.getConstArray();
+ short nDiffs = 0;
+ for ( sal_Int32 n = 0; n < nCharsAfterTransliteration; n++ )
+ {
+ sal_Int32 nCurrentPos = nCurrentStart+n;
+ sal_Int32 nDiff = (nCurrentPos-nDiffs) - pOffsets[n];
+
+ if ( !nDiff )
+ {
+ DBG_ASSERT( nCurrentPos < pNode->Len(), "TransliterateText - String smaller than expected!" );
+ pNode->SetChar( nCurrentPos, rNewText[n] );
+ }
+ else if ( nDiff < 0 )
+ {
+ // Replace first char, delete the rest...
+ DBG_ASSERT( nCurrentPos < pNode->Len(), "TransliterateText - String smaller than expected!" );
+ pNode->SetChar( nCurrentPos, rNewText[n] );
+
+ DBG_ASSERT( (nCurrentPos+1) < pNode->Len(), "TransliterateText - String smaller than expected!" );
+ GetEditDoc().RemoveChars( EditPaM( pNode, nCurrentPos+1 ), -nDiff);
+ }
+ else
+ {
+ DBG_ASSERT( nDiff == 1, "TransliterateText - Diff other than expected! But should work..." );
+ GetEditDoc().InsertText( EditPaM( pNode, nCurrentPos ), OUStringChar(rNewText[n]) );
+
+ }
+ nDiffs = sal::static_int_cast< short >(nDiffs + nDiff);
+ }
+
+ return nDiffs;
+}
+
+
+void ImpEditEngine::SetAsianCompressionMode( CharCompressType n )
+{
+ if (n != mnAsianCompressionMode)
+ {
+ mnAsianCompressionMode = n;
+ if ( ImplHasText() )
+ {
+ FormatFullDoc();
+ UpdateViews();
+ }
+ }
+}
+
+void ImpEditEngine::SetKernAsianPunctuation( bool b )
+{
+ if ( b != mbKernAsianPunctuation )
+ {
+ mbKernAsianPunctuation = b;
+ if ( ImplHasText() )
+ {
+ FormatFullDoc();
+ UpdateViews();
+ }
+ }
+}
+
+void ImpEditEngine::SetAddExtLeading( bool bExtLeading )
+{
+ if ( IsAddExtLeading() != bExtLeading )
+ {
+ mbAddExtLeading = bExtLeading;
+ if ( ImplHasText() )
+ {
+ FormatFullDoc();
+ UpdateViews();
+ }
+ }
+};
+
+
+bool ImpEditEngine::ImplHasText() const
+{
+ return ( ( GetEditDoc().Count() > 1 ) || GetEditDoc().GetObject(0)->Len() );
+}
+
+sal_Int32 ImpEditEngine::LogicToTwips(sal_Int32 n)
+{
+ Size aSz(n, 0);
+ MapMode aTwipsMode( MapUnit::MapTwip );
+ aSz = pRefDev->LogicToLogic( aSz, nullptr, &aTwipsMode );
+ return aSz.Width();
+}
+
+double ImpEditEngine::roundToNearestPt(double fInput) const
+{
+ if (mbRoundToNearestPt)
+ {
+ double fInputPt = o3tl::convert(fInput, o3tl::Length::mm100, o3tl::Length::pt);
+ auto nInputRounded = basegfx::fround(fInputPt);
+ return o3tl::convert(double(nInputRounded), o3tl::Length::pt, o3tl::Length::mm100);
+ }
+ else
+ {
+ return fInput;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/impedit5.cxx b/editeng/source/editeng/impedit5.cxx
new file mode 100644
index 0000000000..0f5af2f75d
--- /dev/null
+++ b/editeng/source/editeng/impedit5.cxx
@@ -0,0 +1,845 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include "impedit.hxx"
+#include <editeng/editeng.hxx>
+#include <svl/hint.hxx>
+#include <sfx2/app.hxx>
+#include <utility>
+
+void ImpEditEngine::SetStyleSheetPool( SfxStyleSheetPool* pSPool )
+{
+ if ( pStylePool != pSPool )
+ {
+ pStylePool = pSPool;
+ }
+}
+
+const SfxStyleSheet* ImpEditEngine::GetStyleSheet( sal_Int32 nPara ) const
+{
+ const ContentNode* pNode = maEditDoc.GetObject( nPara );
+ return pNode ? pNode->GetContentAttribs().GetStyleSheet() : nullptr;
+}
+
+SfxStyleSheet* ImpEditEngine::GetStyleSheet( sal_Int32 nPara )
+{
+ ContentNode* pNode = maEditDoc.GetObject( nPara );
+ return pNode ? pNode->GetContentAttribs().GetStyleSheet() : nullptr;
+}
+
+void ImpEditEngine::SetStyleSheet( EditSelection aSel, SfxStyleSheet* pStyle )
+{
+ aSel.Adjust( maEditDoc );
+
+ sal_Int32 nStartPara = maEditDoc.GetPos( aSel.Min().GetNode() );
+ sal_Int32 nEndPara = maEditDoc.GetPos( aSel.Max().GetNode() );
+
+ bool _bUpdate = SetUpdateLayout( false );
+
+ for ( sal_Int32 n = nStartPara; n <= nEndPara; n++ )
+ SetStyleSheet( n, pStyle );
+
+ SetUpdateLayout( _bUpdate );
+}
+
+void ImpEditEngine::SetStyleSheet( sal_Int32 nPara, SfxStyleSheet* pStyle )
+{
+ DBG_ASSERT( GetStyleSheetPool() || !pStyle, "SetStyleSheet: No StyleSheetPool registered!" );
+ ContentNode* pNode = maEditDoc.GetObject( nPara );
+ SfxStyleSheet* pCurStyle = pNode->GetStyleSheet();
+ if ( pStyle != pCurStyle )
+ {
+ if ( IsUndoEnabled() && !IsInUndo() && maStatus.DoUndoAttribs() )
+ {
+ OUString aPrevStyleName;
+ if ( pCurStyle )
+ aPrevStyleName = pCurStyle->GetName();
+
+ OUString aNewStyleName;
+ if ( pStyle )
+ aNewStyleName = pStyle->GetName();
+
+ InsertUndo(
+ std::make_unique<EditUndoSetStyleSheet>(pEditEngine, maEditDoc.GetPos( pNode ),
+ aPrevStyleName, pCurStyle ? pCurStyle->GetFamily() : SfxStyleFamily::Para,
+ aNewStyleName, pStyle ? pStyle->GetFamily() : SfxStyleFamily::Para,
+ pNode->GetContentAttribs().GetItems() ) );
+ }
+ if ( pCurStyle )
+ EndListening( *pCurStyle );
+ pNode->SetStyleSheet( pStyle, maStatus.UseCharAttribs() );
+ if ( pStyle )
+ StartListening(*pStyle, DuplicateHandling::Allow);
+
+ if (pNode->GetWrongList())
+ pNode->GetWrongList()->ResetInvalidRange(0, pNode->Len());
+ ParaAttribsChanged( pNode );
+ }
+ if (IsUpdateLayout())
+ FormatAndLayout();
+}
+
+void ImpEditEngine::UpdateParagraphsWithStyleSheet( SfxStyleSheet* pStyle )
+{
+ SvxFont aFontFromStyle;
+ CreateFont( aFontFromStyle, pStyle->GetItemSet() );
+
+ bool bUsed = false;
+ for ( sal_Int32 nNode = 0; nNode < maEditDoc.Count(); nNode++ )
+ {
+ ContentNode* pNode = maEditDoc.GetObject( nNode );
+ if ( pNode->GetStyleSheet() == pStyle )
+ {
+ bUsed = true;
+ if (maStatus.UseCharAttribs())
+ pNode->SetStyleSheet( pStyle, aFontFromStyle );
+ else
+ pNode->SetStyleSheet( pStyle, false );
+
+ if (pNode->GetWrongList())
+ pNode->GetWrongList()->ResetInvalidRange(0, pNode->Len());
+ ParaAttribsChanged( pNode );
+ }
+ }
+ if ( bUsed )
+ {
+ GetEditEnginePtr()->StyleSheetChanged( pStyle );
+ if (IsUpdateLayout())
+ FormatAndLayout();
+ }
+}
+
+void ImpEditEngine::RemoveStyleFromParagraphs( SfxStyleSheet const * pStyle )
+{
+ for ( sal_Int32 nNode = 0; nNode < maEditDoc.Count(); nNode++ )
+ {
+ ContentNode* pNode = maEditDoc.GetObject(nNode);
+ if ( pNode->GetStyleSheet() == pStyle )
+ {
+ pNode->SetStyleSheet( nullptr );
+ ParaAttribsChanged( pNode );
+ }
+ }
+ if (IsUpdateLayout())
+ FormatAndLayout();
+}
+
+void ImpEditEngine::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ // So that not a lot of unnecessary formatting is done when destructing:
+ if (!mbDowning)
+ {
+ SfxHintId nId = rHint.GetId();
+ if ( ( nId == SfxHintId::StyleSheetInDestruction ) ||
+ ( nId == SfxHintId::StyleSheetErased ) )
+ {
+ const SfxStyleSheetHint* pStyleSheetHint = static_cast<const SfxStyleSheetHint*>(&rHint);
+ SfxStyleSheet* pStyle = static_cast<SfxStyleSheet*>( pStyleSheetHint->GetStyleSheet() );
+ RemoveStyleFromParagraphs( pStyle );
+ }
+ else if ( nId == SfxHintId::StyleSheetModified )
+ {
+ const SfxStyleSheetHint* pStyleSheetHint = static_cast<const SfxStyleSheetHint*>(&rHint);
+ SfxStyleSheet* pStyle = static_cast<SfxStyleSheet*>( pStyleSheetHint->GetStyleSheet() );
+ UpdateParagraphsWithStyleSheet( pStyle );
+ }
+ else if ( nId == SfxHintId::Dying )
+ {
+ if ( auto pStyle = dynamic_cast< SfxStyleSheet* >(&rBC) )
+ RemoveStyleFromParagraphs( pStyle );
+ }
+ else if ( nId == SfxHintId::DataChanged )
+ {
+ if ( auto pStyle = dynamic_cast< SfxStyleSheet* >(&rBC) )
+ UpdateParagraphsWithStyleSheet( pStyle );
+ }
+ }
+ if (rHint.GetId() == SfxHintId::Dying && dynamic_cast<const SfxApplication*>(&rBC))
+ Dispose();
+}
+
+std::unique_ptr<EditUndoSetAttribs> ImpEditEngine::CreateAttribUndo( EditSelection aSel, const SfxItemSet& rSet )
+{
+ DBG_ASSERT( !aSel.DbgIsBuggy( maEditDoc ), "CreateAttribUndo: Incorrect selection ");
+ aSel.Adjust( maEditDoc );
+
+ ESelection aESel( CreateESel( aSel ) );
+
+ sal_Int32 nStartNode = maEditDoc.GetPos( aSel.Min().GetNode() );
+ sal_Int32 nEndNode = maEditDoc.GetPos( aSel.Max().GetNode() );
+
+ DBG_ASSERT( nStartNode <= nEndNode, "CreateAttribUndo: Start > End ?!" );
+
+ std::unique_ptr<EditUndoSetAttribs> pUndo;
+ if ( rSet.GetPool() != &maEditDoc.GetItemPool() )
+ {
+ SfxItemSet aTmpSet( GetEmptyItemSet() );
+ aTmpSet.Put( rSet );
+ pUndo.reset( new EditUndoSetAttribs(pEditEngine, aESel, std::move(aTmpSet)) );
+ }
+ else
+ {
+ pUndo.reset( new EditUndoSetAttribs(pEditEngine, aESel, rSet) );
+ }
+
+ SfxItemPool* pPool = pUndo->GetNewAttribs().GetPool();
+
+ for ( sal_Int32 nPara = nStartNode; nPara <= nEndNode; nPara++ )
+ {
+ ContentNode* pNode = maEditDoc.GetObject( nPara );
+ DBG_ASSERT( maEditDoc.GetObject( nPara ), "Node not found: CreateAttribUndo" );
+ ContentAttribsInfo* pInf = new ContentAttribsInfo( pNode->GetContentAttribs().GetItems() );
+ pUndo->AppendContentInfo(pInf);
+
+ for ( sal_Int32 nAttr = 0; nAttr < pNode->GetCharAttribs().Count(); nAttr++ )
+ {
+ const EditCharAttrib& rAttr = *pNode->GetCharAttribs().GetAttribs()[nAttr];
+ if (rAttr.GetLen())
+ {
+ EditCharAttrib* pNew = MakeCharAttrib(*pPool, *rAttr.GetItem(), rAttr.GetStart(), rAttr.GetEnd());
+ pInf->AppendCharAttrib(pNew);
+ }
+ }
+ }
+ return pUndo;
+}
+
+ViewShellId ImpEditEngine::CreateViewShellId()
+{
+ ViewShellId nRet(-1);
+
+ const EditView* pEditView = pEditEngine ? pEditEngine->GetActiveView() : nullptr;
+ const OutlinerViewShell* pViewShell = pEditView ? pEditView->GetImpEditView()->GetViewShell() : nullptr;
+ if (pViewShell)
+ nRet = pViewShell->GetViewShellId();
+
+ return nRet;
+}
+
+void ImpEditEngine::UndoActionStart( sal_uInt16 nId, const ESelection& aSel )
+{
+ if ( IsUndoEnabled() && !IsInUndo() )
+ {
+ GetUndoManager().EnterListAction( GetEditEnginePtr()->GetUndoComment( nId ), OUString(), nId, CreateViewShellId() );
+ DBG_ASSERT( !moUndoMarkSelection, "UndoAction SelectionMarker?" );
+ moUndoMarkSelection = aSel;
+ }
+}
+
+void ImpEditEngine::UndoActionStart( sal_uInt16 nId )
+{
+ if ( IsUndoEnabled() && !IsInUndo() )
+ {
+ GetUndoManager().EnterListAction( GetEditEnginePtr()->GetUndoComment( nId ), OUString(), nId, CreateViewShellId() );
+ DBG_ASSERT( !moUndoMarkSelection, "UndoAction SelectionMarker?" );
+ }
+}
+
+void ImpEditEngine::UndoActionEnd()
+{
+ if ( IsUndoEnabled() && !IsInUndo() )
+ {
+ GetUndoManager().LeaveListAction();
+ moUndoMarkSelection.reset();
+ }
+}
+
+void ImpEditEngine::InsertUndo( std::unique_ptr<EditUndo> pUndo, bool bTryMerge )
+{
+ DBG_ASSERT( !IsInUndo(), "InsertUndo in Undo mode!" );
+ if ( moUndoMarkSelection )
+ {
+ GetUndoManager().AddUndoAction( std::make_unique<EditUndoMarkSelection>(pEditEngine, *moUndoMarkSelection) );
+ moUndoMarkSelection.reset();
+ }
+ GetUndoManager().AddUndoAction( std::move(pUndo), bTryMerge );
+
+ mbLastTryMerge = bTryMerge;
+}
+
+void ImpEditEngine::ResetUndoManager()
+{
+ if ( HasUndoManager() )
+ GetUndoManager().Clear();
+}
+
+void ImpEditEngine::EnableUndo( bool bEnable )
+{
+ // When switching the mode Delete list:
+ if ( bEnable != IsUndoEnabled() )
+ ResetUndoManager();
+
+ mbUndoEnabled = bEnable;
+}
+
+void ImpEditEngine::Undo( EditView* pView )
+{
+ if ( HasUndoManager() && GetUndoManager().GetUndoActionCount() )
+ {
+ SetActiveView( pView );
+ GetUndoManager().Undo();
+ }
+}
+
+void ImpEditEngine::Redo( EditView* pView )
+{
+ if ( HasUndoManager() && GetUndoManager().GetRedoActionCount() )
+ {
+ SetActiveView( pView );
+ GetUndoManager().Redo();
+ }
+}
+
+SfxItemSet ImpEditEngine::GetAttribs( EditSelection aSel, EditEngineAttribs nOnlyHardAttrib )
+{
+
+ aSel.Adjust( maEditDoc );
+
+ SfxItemSet aCurSet( GetEmptyItemSet() );
+
+ sal_Int32 nStartNode = maEditDoc.GetPos( aSel.Min().GetNode() );
+ sal_Int32 nEndNode = maEditDoc.GetPos( aSel.Max().GetNode() );
+
+ // iterate over the paragraphs ...
+ for ( sal_Int32 nNode = nStartNode; nNode <= nEndNode; nNode++ )
+ {
+ ContentNode* pNode = maEditDoc.GetObject( nNode );
+ assert( pNode && "Node not found: GetAttrib" );
+
+ const sal_Int32 nStartPos = nNode==nStartNode ? aSel.Min().GetIndex() : 0;
+ const sal_Int32 nEndPos = nNode==nEndNode ? aSel.Max().GetIndex() : pNode->Len(); // Can also be == nStart!
+
+ // Problem: Templates...
+ // => Other way:
+ // 1) Hard character attributes, as usual...
+ // 2) Examine Style and paragraph attributes only when OFF...
+
+ // First the very hard formatting...
+ if (pNode)
+ EditDoc::FindAttribs( pNode, nStartPos, nEndPos, aCurSet );
+
+ if( nOnlyHardAttrib != EditEngineAttribs::OnlyHard )
+ {
+ // and then paragraph formatting and template...
+ for ( sal_uInt16 nWhich = EE_ITEMS_START; nWhich <= EE_CHAR_END; nWhich++)
+ {
+ if ( aCurSet.GetItemState( nWhich ) == SfxItemState::DEFAULT )
+ {
+ if ( nOnlyHardAttrib == EditEngineAttribs::All )
+ {
+ const SfxPoolItem& rItem = pNode->GetContentAttribs().GetItem( nWhich );
+ aCurSet.Put( rItem );
+ }
+ else if ( pNode->GetContentAttribs().GetItems().GetItemState( nWhich ) == SfxItemState::SET )
+ {
+ const SfxPoolItem& rItem = pNode->GetContentAttribs().GetItems().Get( nWhich );
+ aCurSet.Put( rItem );
+ }
+ }
+ else if ( aCurSet.GetItemState( nWhich ) == SfxItemState::SET )
+ {
+ const SfxPoolItem* pItem = nullptr;
+ if ( nOnlyHardAttrib == EditEngineAttribs::All )
+ {
+ pItem = &pNode->GetContentAttribs().GetItem( nWhich );
+ }
+ else if ( pNode->GetContentAttribs().GetItems().GetItemState( nWhich ) == SfxItemState::SET )
+ {
+ pItem = &pNode->GetContentAttribs().GetItems().Get( nWhich );
+ }
+ // pItem can only be NULL when nOnlyHardAttrib...
+ if ( !pItem || ( *pItem != aCurSet.Get( nWhich ) ) )
+ {
+ // Problem: When Paragraph style with for example font,
+ // but the Font is hard and completely different,
+ // wrong in selection if invalidated....
+ // => better not invalidate, instead CHANGE!
+ // It would be better to fill each paragraph with
+ // an itemset and compare this in large.
+ if ( nWhich <= EE_PARA_END )
+ aCurSet.InvalidateItem( nWhich );
+ }
+ }
+ }
+ }
+ }
+
+ // fill empty slots with defaults ...
+ if ( nOnlyHardAttrib == EditEngineAttribs::All )
+ {
+ for ( sal_uInt16 nWhich = EE_ITEMS_START; nWhich <= EE_CHAR_END; nWhich++ )
+ {
+ if ( aCurSet.GetItemState( nWhich ) == SfxItemState::DEFAULT )
+ {
+ aCurSet.Put( maEditDoc.GetItemPool().GetDefaultItem( nWhich ) );
+ }
+ }
+ }
+ return aCurSet;
+}
+
+
+SfxItemSet ImpEditEngine::GetAttribs( sal_Int32 nPara, sal_Int32 nStart, sal_Int32 nEnd, GetAttribsFlags nFlags ) const
+{
+ // Optimized function with fewer Puts(), which cause unnecessary cloning from default items.
+ // If this works, change GetAttribs( EditSelection ) to use this for each paragraph and merge the results!
+
+
+ ContentNode* pNode = const_cast<ContentNode*>(maEditDoc.GetObject(nPara));
+ DBG_ASSERT( pNode, "GetAttribs - unknown paragraph!" );
+ DBG_ASSERT( nStart <= nEnd, "getAttribs: Start > End not supported!" );
+
+ SfxItemSet aAttribs(GetEmptyItemSet());
+
+ if ( pNode )
+ {
+ if ( nEnd > pNode->Len() )
+ nEnd = pNode->Len();
+
+ if ( nStart > nEnd )
+ nStart = nEnd;
+
+ // StyleSheet / Parattribs...
+
+ if ( pNode->GetStyleSheet() && ( nFlags & GetAttribsFlags::STYLESHEET ) )
+ aAttribs.Set(pNode->GetStyleSheet()->GetItemSet());
+
+ if ( nFlags & GetAttribsFlags::PARAATTRIBS )
+ aAttribs.Put( pNode->GetContentAttribs().GetItems() );
+
+ // CharAttribs...
+
+ if ( nFlags & GetAttribsFlags::CHARATTRIBS )
+ {
+ // Make testing easier...
+ pNode->GetCharAttribs().OptimizeRanges();
+
+ const CharAttribList::AttribsType& rAttrs = pNode->GetCharAttribs().GetAttribs();
+ for (const auto & nAttr : rAttrs)
+ {
+ const EditCharAttrib& rAttr = *nAttr;
+
+ if ( nStart == nEnd )
+ {
+ sal_Int32 nCursorPos = nStart;
+ if ( ( rAttr.GetStart() <= nCursorPos ) && ( rAttr.GetEnd() >= nCursorPos ) )
+ {
+ // To be used the attribute has to start BEFORE the position, or it must be a
+ // new empty attr AT the position, or we are on position 0.
+ if ( ( rAttr.GetStart() < nCursorPos ) || rAttr.IsEmpty() || !nCursorPos )
+ {
+ // maybe this attrib ends here and a new attrib with 0 Len may follow and be valid here,
+ // but that s no problem, the empty item will come later and win.
+ aAttribs.Put( *rAttr.GetItem() );
+ }
+ }
+ }
+ else
+ {
+ // Check every attribute covering the area, partial or full.
+ if ( ( rAttr.GetStart() < nEnd ) && ( rAttr.GetEnd() > nStart ) )
+ {
+ if ( ( rAttr.GetStart() <= nStart ) && ( rAttr.GetEnd() >= nEnd ) )
+ {
+ // full coverage
+ aAttribs.Put( *rAttr.GetItem() );
+ }
+ else
+ {
+ // OptimizeRanges() assures that not the same attr can follow for full coverage
+ // only partial, check with current, when using para/style, otherwise invalid.
+ if ( !( nFlags & (GetAttribsFlags::PARAATTRIBS|GetAttribsFlags::STYLESHEET) ) ||
+ ( *rAttr.GetItem() != aAttribs.Get( rAttr.Which() ) ) )
+ {
+ aAttribs.InvalidateItem( rAttr.Which() );
+ }
+ }
+ }
+ }
+
+ if ( rAttr.GetStart() > nEnd )
+ {
+ break;
+ }
+ }
+ }
+ }
+
+ return aAttribs;
+}
+
+
+void ImpEditEngine::SetAttribs( EditSelection aSel, const SfxItemSet& rSet, SetAttribsMode nSpecial, bool bSetSelection )
+{
+ aSel.Adjust( maEditDoc );
+
+ // When no selection => use the Attribute on the word.
+ // ( the RTF-parser should actually never call the Method without a Range )
+ if ( nSpecial == SetAttribsMode::WholeWord && !aSel.HasRange() )
+ aSel = SelectWord( aSel, css::i18n::WordType::ANYWORD_IGNOREWHITESPACES, false );
+
+ sal_Int32 nStartNode = maEditDoc.GetPos( aSel.Min().GetNode() );
+ sal_Int32 nEndNode = maEditDoc.GetPos( aSel.Max().GetNode() );
+
+ if (IsUndoEnabled() && !IsInUndo() && maStatus.DoUndoAttribs())
+ {
+ std::unique_ptr<EditUndoSetAttribs> pUndo = CreateAttribUndo( aSel, rSet );
+ pUndo->SetSpecial( nSpecial );
+ pUndo->SetUpdateSelection(bSetSelection);
+ InsertUndo( std::move(pUndo) );
+ }
+
+ bool bCheckLanguage = false;
+ if ( GetStatus().DoOnlineSpelling() )
+ {
+ bCheckLanguage = ( rSet.GetItemState( EE_CHAR_LANGUAGE ) == SfxItemState::SET ) ||
+ ( rSet.GetItemState( EE_CHAR_LANGUAGE_CJK ) == SfxItemState::SET ) ||
+ ( rSet.GetItemState( EE_CHAR_LANGUAGE_CTL ) == SfxItemState::SET );
+ }
+
+ // iterate over the paragraphs ...
+ for ( sal_Int32 nNode = nStartNode; nNode <= nEndNode; nNode++ )
+ {
+ bool bParaAttribFound = false;
+ bool bCharAttribFound = false;
+
+ DBG_ASSERT( maEditDoc.GetObject( nNode ), "Node not found: SetAttribs" );
+ DBG_ASSERT( GetParaPortions().SafeGetObject( nNode ), "Portion not found: SetAttribs" );
+
+ ContentNode* pNode = maEditDoc.GetObject( nNode );
+ ParaPortion* pPortion = GetParaPortions()[nNode];
+
+ const sal_Int32 nStartPos = nNode==nStartNode ? aSel.Min().GetIndex() : 0;
+ const sal_Int32 nEndPos = nNode==nEndNode ? aSel.Max().GetIndex() : pNode->Len(); // can also be == nStart!
+
+ // Iterate over the Items...
+ for ( sal_uInt16 nWhich = EE_ITEMS_START; nWhich <= EE_CHAR_END; nWhich++)
+ {
+ if ( rSet.GetItemState( nWhich ) == SfxItemState::SET )
+ {
+ const SfxPoolItem& rItem = rSet.Get( nWhich );
+ if ( nWhich <= EE_PARA_END )
+ {
+ pNode->GetContentAttribs().GetItems().Put( rItem );
+ bParaAttribFound = true;
+ }
+ else
+ {
+ maEditDoc.InsertAttrib( pNode, nStartPos, nEndPos, rItem );
+ bCharAttribFound = true;
+ if ( nSpecial == SetAttribsMode::Edge )
+ {
+ CharAttribList::AttribsType& rAttribs = pNode->GetCharAttribs().GetAttribs();
+ for (std::unique_ptr<EditCharAttrib> & rAttrib : rAttribs)
+ {
+ EditCharAttrib& rAttr = *rAttrib;
+ if (rAttr.GetStart() > nEndPos)
+ break;
+
+ if (rAttr.GetEnd() == nEndPos && rAttr.Which() == nWhich)
+ {
+ rAttr.SetEdge(true);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ( bParaAttribFound )
+ {
+ ParaAttribsChanged( pPortion->GetNode() );
+ }
+ else if ( bCharAttribFound )
+ {
+ mbFormatted = false;
+ if ( !pNode->Len() || ( nStartPos != nEndPos ) )
+ {
+ pPortion->MarkSelectionInvalid( nStartPos );
+ if ( bCheckLanguage )
+ pNode->GetWrongList()->SetInvalidRange(nStartPos, nEndPos);
+ }
+ }
+ }
+}
+
+void ImpEditEngine::RemoveCharAttribs( EditSelection aSel, EERemoveParaAttribsMode eMode, sal_uInt16 nWhich )
+{
+ aSel.Adjust( maEditDoc );
+
+ sal_Int32 nStartNode = maEditDoc.GetPos( aSel.Min().GetNode() );
+ sal_Int32 nEndNode = maEditDoc.GetPos( aSel.Max().GetNode() );
+ bool bRemoveParaAttribs = eMode == EERemoveParaAttribsMode::RemoveAll;
+ const SfxItemSet* _pEmptyItemSet = bRemoveParaAttribs ? &GetEmptyItemSet() : nullptr;
+
+ if (IsUndoEnabled() && !IsInUndo() && maStatus.DoUndoAttribs())
+ {
+ // Possibly a special Undo, or itemset*
+ std::unique_ptr<EditUndoSetAttribs> pUndo = CreateAttribUndo( aSel, GetEmptyItemSet() );
+ pUndo->SetRemoveAttribs( true );
+ pUndo->SetRemoveParaAttribs( bRemoveParaAttribs );
+ pUndo->SetRemoveWhich( nWhich );
+ InsertUndo( std::move(pUndo) );
+ }
+
+ // iterate over the paragraphs ...
+ for ( sal_Int32 nNode = nStartNode; nNode <= nEndNode; nNode++ )
+ {
+ ContentNode* pNode = maEditDoc.GetObject( nNode );
+ ParaPortion* pPortion = GetParaPortions()[nNode];
+
+ DBG_ASSERT( maEditDoc.GetObject( nNode ), "Node not found: SetAttribs" );
+ DBG_ASSERT( GetParaPortions().SafeGetObject( nNode ), "Portion not found: SetAttribs" );
+
+ const sal_Int32 nStartPos = nNode==nStartNode ? aSel.Min().GetIndex() : 0;
+ const sal_Int32 nEndPos = nNode==nEndNode ? aSel.Max().GetIndex() : pNode->Len(); // can also be == nStart!
+
+ // Optimize: If whole paragraph, then RemoveCharAttribs (nPara)?
+ bool bChanged = maEditDoc.RemoveAttribs( pNode, nStartPos, nEndPos, nWhich );
+ if ( bRemoveParaAttribs )
+ {
+ SetParaAttribs( nNode, *_pEmptyItemSet ); // Invalidated
+ }
+ else if (eMode == EERemoveParaAttribsMode::RemoveCharItems)
+ {
+ // For 'Format-Standard' also the character attributes should
+ // disappear, which were set as paragraph attributes by the
+ // DrawingEngine. These could not have been set by the user anyway.
+
+ // #106871# Not when nWhich
+ // Would have been better to offer a separate method for format/standard...
+ if ( !nWhich )
+ {
+ SfxItemSet aAttribs( GetParaAttribs( nNode ) );
+ for ( sal_uInt16 nW = EE_CHAR_START; nW <= EE_CHAR_END; nW++ )
+ aAttribs.ClearItem( nW );
+ SetParaAttribs( nNode, aAttribs );
+ }
+ }
+
+ if ( bChanged && !bRemoveParaAttribs )
+ {
+ mbFormatted = false;
+ pPortion->MarkSelectionInvalid( nStartPos );
+ }
+ }
+}
+
+void ImpEditEngine::RemoveCharAttribs( sal_Int32 nPara, sal_uInt16 nWhich, bool bRemoveFeatures )
+{
+ ContentNode* pNode = maEditDoc.GetObject( nPara );
+ ParaPortion* pPortion = GetParaPortions().SafeGetObject( nPara );
+
+ DBG_ASSERT( pNode, "Node not found: RemoveCharAttribs" );
+ DBG_ASSERT( pPortion, "Portion not found: RemoveCharAttribs" );
+
+ if ( !pNode || !pPortion )
+ return;
+
+ size_t nAttr = 0;
+ CharAttribList::AttribsType& rAttrs = pNode->GetCharAttribs().GetAttribs();
+ EditCharAttrib* pAttr = GetAttrib(rAttrs, nAttr);
+ while ( pAttr )
+ {
+ if ( ( !pAttr->IsFeature() || bRemoveFeatures ) &&
+ ( !nWhich || ( pAttr->GetItem()->Which() == nWhich ) ) )
+ {
+ pNode->GetCharAttribs().Remove(nAttr);
+ nAttr--;
+ }
+ nAttr++;
+ pAttr = GetAttrib(rAttrs, nAttr);
+ }
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ CharAttribList::DbgCheckAttribs(pNode->GetCharAttribs());
+#endif
+
+ pPortion->MarkSelectionInvalid( 0 );
+}
+
+void ImpEditEngine::SetParaAttribs( sal_Int32 nPara, const SfxItemSet& rSet )
+{
+ ContentNode* pNode = maEditDoc.GetObject( nPara );
+
+ if ( !pNode )
+ return;
+
+ if ( pNode->GetContentAttribs().GetItems() == rSet )
+ return;
+
+ if (IsUndoEnabled() && !IsInUndo() && maStatus.DoUndoAttribs())
+ {
+ if ( rSet.GetPool() != &maEditDoc.GetItemPool() )
+ {
+ SfxItemSet aTmpSet( GetEmptyItemSet() );
+ aTmpSet.Put( rSet );
+ InsertUndo(std::make_unique<EditUndoSetParaAttribs>(pEditEngine, nPara, pNode->GetContentAttribs().GetItems(), aTmpSet));
+ }
+ else
+ {
+ InsertUndo(std::make_unique<EditUndoSetParaAttribs>(pEditEngine, nPara, pNode->GetContentAttribs().GetItems(), rSet));
+ }
+ }
+
+ bool bCheckLanguage = ( rSet.GetItemState( EE_CHAR_LANGUAGE ) == SfxItemState::SET ) ||
+ ( rSet.GetItemState( EE_CHAR_LANGUAGE_CJK ) == SfxItemState::SET ) ||
+ ( rSet.GetItemState( EE_CHAR_LANGUAGE_CTL ) == SfxItemState::SET );
+
+ pNode->GetContentAttribs().GetItems().Set( rSet );
+
+ if ( bCheckLanguage && pNode->GetWrongList() )
+ pNode->GetWrongList()->ResetInvalidRange(0, pNode->Len());
+
+ if (maStatus.UseCharAttribs())
+ pNode->CreateDefFont();
+
+ ParaAttribsChanged( pNode );
+}
+
+const SfxItemSet& ImpEditEngine::GetParaAttribs( sal_Int32 nPara ) const
+{
+ const ContentNode* pNode = maEditDoc.GetObject( nPara );
+ assert(pNode && "Node not found: GetParaAttribs");
+ return pNode->GetContentAttribs().GetItems();
+}
+
+bool ImpEditEngine::HasParaAttrib( sal_Int32 nPara, sal_uInt16 nWhich ) const
+{
+ const ContentNode* pNode = maEditDoc.GetObject( nPara );
+ assert(pNode && "Node not found: HasParaAttrib");
+ return pNode->GetContentAttribs().HasItem( nWhich );
+}
+
+const SfxPoolItem& ImpEditEngine::GetParaAttrib( sal_Int32 nPara, sal_uInt16 nWhich ) const
+{
+ const ContentNode* pNode = maEditDoc.GetObject(nPara);
+ assert(pNode && "Node not found: GetParaAttrib");
+ return pNode->GetContentAttribs().GetItem(nWhich);
+}
+
+void ImpEditEngine::GetCharAttribs( sal_Int32 nPara, std::vector<EECharAttrib>& rLst ) const
+{
+ rLst.clear();
+ const ContentNode* pNode = maEditDoc.GetObject( nPara );
+ if ( !pNode )
+ return;
+
+ rLst.reserve(pNode->GetCharAttribs().Count());
+ const CharAttribList::AttribsType& rAttrs = pNode->GetCharAttribs().GetAttribs();
+ for (const auto & i : rAttrs)
+ {
+ const EditCharAttrib& rAttr = *i;
+ EECharAttrib aEEAttr(rAttr.GetStart(), rAttr.GetEnd(), rAttr.GetItem());
+ rLst.push_back(aEEAttr);
+ }
+}
+
+void ImpEditEngine::ParaAttribsToCharAttribs( ContentNode* pNode )
+{
+ pNode->GetCharAttribs().DeleteEmptyAttribs();
+ sal_Int32 nEndPos = pNode->Len();
+ for ( sal_uInt16 nWhich = EE_CHAR_START; nWhich <= EE_CHAR_END; nWhich++ )
+ {
+ if ( pNode->GetContentAttribs().HasItem( nWhich ) )
+ {
+ const SfxPoolItem& rItem = pNode->GetContentAttribs().GetItem( nWhich );
+ // Fill the gap:
+ sal_Int32 nLastEnd = 0;
+ const EditCharAttrib* pAttr = pNode->GetCharAttribs().FindNextAttrib( nWhich, nLastEnd );
+ while ( pAttr )
+ {
+ nLastEnd = pAttr->GetEnd();
+ if ( pAttr->GetStart() > nLastEnd )
+ maEditDoc.InsertAttrib( pNode, nLastEnd, pAttr->GetStart(), rItem );
+ // #112831# Last Attr might go from 0xffff to 0x0000
+ pAttr = nLastEnd ? pNode->GetCharAttribs().FindNextAttrib( nWhich, nLastEnd ) : nullptr;
+ }
+
+ // And the Rest:
+ if ( nLastEnd < nEndPos )
+ maEditDoc.InsertAttrib( pNode, nLastEnd, nEndPos, rItem );
+ }
+ }
+ mbFormatted = false;
+ // Portion does not need to be invalidated here, happens elsewhere.
+}
+
+IdleFormattter::IdleFormattter()
+ : Idle("editeng::ImpEditEngine aIdleFormatter")
+{
+ pView = nullptr;
+ nRestarts = 0;
+}
+
+IdleFormattter::~IdleFormattter()
+{
+ pView = nullptr;
+}
+
+void IdleFormattter::DoIdleFormat( EditView* pV )
+{
+ pView = pV;
+
+ if ( IsActive() )
+ nRestarts++;
+
+ if ( nRestarts > 4 )
+ ForceTimeout();
+ else
+ Start();
+}
+
+void IdleFormattter::ForceTimeout()
+{
+ if ( IsActive() )
+ {
+ Stop();
+ Invoke();
+ }
+}
+
+ImplIMEInfos::ImplIMEInfos( const EditPaM& rPos, OUString _aOldTextAfterStartPos )
+ : aOldTextAfterStartPos(std::move( _aOldTextAfterStartPos )),
+ aPos(rPos),
+ nLen(0),
+ bWasCursorOverwrite(false)
+ {
+ }
+
+ImplIMEInfos::~ImplIMEInfos()
+{
+}
+
+void ImplIMEInfos::CopyAttribs( const ExtTextInputAttr* pA, sal_uInt16 nL )
+{
+ nLen = nL;
+ pAttribs.reset( new ExtTextInputAttr[ nL ] );
+ memcpy( pAttribs.get(), pA, nL*sizeof(ExtTextInputAttr) );
+}
+
+void ImplIMEInfos::DestroyAttribs()
+{
+ pAttribs.reset();
+ nLen = 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/misspellrange.cxx b/editeng/source/editeng/misspellrange.cxx
new file mode 100644
index 0000000000..562a9905c2
--- /dev/null
+++ b/editeng/source/editeng/misspellrange.cxx
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <editeng/misspellrange.hxx>
+
+namespace editeng {
+
+MisspellRange::MisspellRange(size_t nStart, size_t nEnd) : mnStart(nStart), mnEnd(nEnd) {}
+
+MisspellRanges::MisspellRanges(sal_Int32 nParagraph, std::vector<MisspellRange>&& rRanges) :
+ mnParagraph(nParagraph), maRanges(std::move(rRanges)) {}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/section.cxx b/editeng/source/editeng/section.cxx
new file mode 100644
index 0000000000..f65b0158a9
--- /dev/null
+++ b/editeng/source/editeng/section.cxx
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <editeng/section.hxx>
+
+namespace editeng {
+
+Section::Section(sal_Int32 nPara, sal_Int32 nStart, sal_Int32 nEnd) :
+ mnParagraph(nPara), mnStart(nStart), mnEnd(nEnd){}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/textconv.cxx b/editeng/source/editeng/textconv.cxx
new file mode 100644
index 0000000000..e97be254d6
--- /dev/null
+++ b/editeng/source/editeng/textconv.cxx
@@ -0,0 +1,543 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "impedit.hxx"
+#include <editeng/editview.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/fontitem.hxx>
+#include "textconv.hxx"
+#include <osl/diagnose.h>
+#include <vcl/weld.hxx>
+
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::linguistic2;
+
+TextConvWrapper::TextConvWrapper( weld::Widget* pWindow,
+ const Reference< XComponentContext >& rxContext,
+ const lang::Locale& rSourceLocale,
+ const lang::Locale& rTargetLocale,
+ const vcl::Font* pTargetFont,
+ sal_Int32 nOptions,
+ bool bIsInteractive,
+ bool bIsStart,
+ EditView* pView ) :
+ HangulHanjaConversion( pWindow, rxContext, rSourceLocale, rTargetLocale, pTargetFont, nOptions, bIsInteractive )
+ , m_nConvTextLang(LANGUAGE_NONE)
+ , m_nUnitOffset(0)
+ , m_nLastPos(0)
+ , m_aConvSel(pView->GetSelection())
+ , m_pEditView(pView)
+ , m_pWin(pWindow)
+ , m_bStartChk(false)
+ , m_bStartDone(bIsStart)
+ , m_bEndDone(false)
+ , m_bAllowChange(false)
+{
+ DBG_ASSERT( pWindow, "TextConvWrapper: window missing" );
+
+ m_aConvSel.Adjust(); // make Start <= End
+}
+
+
+TextConvWrapper::~TextConvWrapper()
+{
+}
+
+
+bool TextConvWrapper::ConvNext_impl()
+{
+ // modified version of SvxSpellWrapper::SpellNext
+
+ if( m_bStartChk )
+ m_bStartDone = true;
+ else
+ m_bEndDone = true;
+
+ if ( m_bStartDone && m_bEndDone )
+ {
+ if ( ConvMore_impl() ) // examine another document?
+ {
+ m_bStartDone = true;
+ m_bEndDone = false;
+ ConvStart_impl( SvxSpellArea::Body );
+ return true;
+ }
+ return false;
+
+ }
+
+ if ( m_bStartDone && m_bEndDone )
+ {
+ if ( ConvMore_impl() ) // examine another document?
+ {
+ m_bStartDone = true;
+ m_bEndDone = false;
+ ConvStart_impl( SvxSpellArea::Body );
+ return true;
+ }
+ }
+ else if (!m_aConvSel.HasRange())
+ {
+ m_bStartChk = !m_bStartDone;
+ ConvStart_impl( m_bStartChk ? SvxSpellArea::BodyStart : SvxSpellArea::BodyEnd );
+ return true;
+ }
+
+ return false;
+}
+
+void TextConvWrapper::FindConvText_impl()
+{
+ // modified version of SvxSpellWrapper::FindSpellError
+ weld::WaitObject aWait(m_pWin);
+ while ( true )
+ {
+ if (ConvContinue_impl() || !ConvNext_impl())
+ break;
+ }
+}
+
+bool TextConvWrapper::ConvMore_impl()
+{
+ // modified version of SvxSpellWrapper::SpellMore
+
+ bool bMore = false;
+ EditEngine* pEE = m_pEditView->GetEditEngine();
+ ImpEditEngine* pImpEE = m_pEditView->GetImpEditEngine();
+ ConvInfo* pConvInfo = pImpEE->GetConvInfo();
+ if ( pConvInfo->bMultipleDoc )
+ {
+ bMore = pEE->ConvertNextDocument();
+ if ( bMore )
+ {
+ // The text has been entered in this engine ...
+ m_pEditView->GetImpEditView()->SetEditSelection(
+ pEE->GetEditDoc().GetStartPaM() );
+ }
+ }
+ return bMore;
+}
+
+
+void TextConvWrapper::ConvStart_impl( SvxSpellArea eArea )
+{
+ // modified version of EditSpellWrapper::SpellStart
+
+ EditEngine* pEE = m_pEditView->GetEditEngine();
+ ImpEditEngine* pImpEE = m_pEditView->GetImpEditEngine();
+ ConvInfo* pConvInfo = pImpEE->GetConvInfo();
+
+ if ( eArea == SvxSpellArea::BodyStart )
+ {
+ // Is called when Spell-forward has reached the end, and to start over
+ if ( m_bEndDone )
+ {
+ pConvInfo->bConvToEnd = false;
+ pConvInfo->aConvTo = pConvInfo->aConvStart;
+ pConvInfo->aConvContinue = EPaM( 0, 0 );
+ m_pEditView->GetImpEditView()->SetEditSelection(
+ pEE->GetEditDoc().GetStartPaM() );
+ }
+ else
+ {
+ pConvInfo->bConvToEnd = true;
+ pConvInfo->aConvTo = pImpEE->CreateEPaM(
+ pEE->GetEditDoc().GetStartPaM() );
+ }
+ }
+ else if ( eArea == SvxSpellArea::BodyEnd )
+ {
+ // Is called when Spell-forward starts
+ pConvInfo->bConvToEnd = true;
+ if (m_aConvSel.HasRange())
+ {
+ // user selection: convert to end of selection
+ pConvInfo->aConvTo.nPara = m_aConvSel.nEndPara;
+ pConvInfo->aConvTo.nIndex = m_aConvSel.nEndPos;
+ pConvInfo->bConvToEnd = false;
+ }
+ else
+ {
+ // nothing selected: convert to end of document
+ pConvInfo->aConvTo = pImpEE->CreateEPaM(
+ pEE->GetEditDoc().GetEndPaM() );
+ }
+ }
+ else if ( eArea == SvxSpellArea::Body )
+ {
+ // called by ConvNext_impl...
+ pConvInfo->aConvContinue = pConvInfo->aConvStart;
+ pConvInfo->aConvTo = pImpEE->CreateEPaM(
+ pEE->GetEditDoc().GetEndPaM() );
+ }
+ else
+ {
+ OSL_FAIL( "ConvStart_impl: Unknown Area!" );
+ }
+}
+
+
+bool TextConvWrapper::ConvContinue_impl()
+{
+ // modified version of EditSpellWrapper::SpellContinue
+
+ // get next convertible text portion and its language
+ m_aConvText.clear();
+ m_nConvTextLang = LANGUAGE_NONE;
+ m_pEditView->GetImpEditEngine()->ImpConvert( m_aConvText, m_nConvTextLang,
+ m_pEditView, GetSourceLanguage(), m_aConvSel,
+ m_bAllowChange, GetTargetLanguage(), GetTargetFont() );
+ return !m_aConvText.isEmpty();
+}
+
+
+void TextConvWrapper::SetLanguageAndFont( const ESelection &rESel,
+ LanguageType nLang, sal_uInt16 nLangWhichId,
+ const vcl::Font *pFont, sal_uInt16 nFontWhichId )
+{
+ ESelection aOldSel = m_pEditView->GetSelection();
+ m_pEditView->SetSelection( rESel );
+
+ // set new language attribute
+ SfxItemSet aNewSet( m_pEditView->GetEmptyItemSet() );
+ aNewSet.Put( SvxLanguageItem( nLang, nLangWhichId ) );
+
+ // new font to be set?
+ DBG_ASSERT( pFont, "target font missing?" );
+ if (pFont)
+ {
+ // set new font attribute
+ SvxFontItem aFontItem = static_cast<const SvxFontItem&>( aNewSet.Get( nFontWhichId ) );
+ aFontItem.SetFamilyName( pFont->GetFamilyName());
+ aFontItem.SetFamily( pFont->GetFamilyType());
+ aFontItem.SetStyleName( pFont->GetStyleName());
+ aFontItem.SetPitch( pFont->GetPitch());
+ aFontItem.SetCharSet(pFont->GetCharSet());
+ aNewSet.Put( aFontItem );
+ }
+
+ // apply new attributes
+ m_pEditView->SetAttribs( aNewSet );
+
+ m_pEditView->SetSelection( aOldSel );
+}
+
+
+void TextConvWrapper::SelectNewUnit_impl(
+ const sal_Int32 nUnitStart,
+ const sal_Int32 nUnitEnd )
+{
+ const bool bOK = 0 <= nUnitStart && 0 <= nUnitEnd && nUnitStart <= nUnitEnd;
+ DBG_ASSERT( bOK, "invalid arguments" );
+ if (!bOK)
+ return;
+
+ ESelection aSelection = m_pEditView->GetSelection();
+ DBG_ASSERT( aSelection.nStartPara == aSelection.nEndPara,
+ "paragraph mismatch in selection" );
+ aSelection.nStartPos = (m_nLastPos + m_nUnitOffset + nUnitStart);
+ aSelection.nEndPos = (m_nLastPos + m_nUnitOffset + nUnitEnd);
+ m_pEditView->SetSelection( aSelection );
+}
+
+
+void TextConvWrapper::GetNextPortion(
+ OUString& /* [out] */ rNextPortion,
+ LanguageType& /* [out] */ rLangOfPortion,
+ bool /* [in] */ _bAllowImplicitChangesForNotConvertibleText )
+{
+ m_bAllowChange = _bAllowImplicitChangesForNotConvertibleText;
+
+ FindConvText_impl();
+ rNextPortion = m_aConvText;
+ rLangOfPortion = m_nConvTextLang;
+ m_nUnitOffset = 0;
+
+ ESelection aSelection = m_pEditView->GetSelection();
+ DBG_ASSERT( aSelection.nStartPara == aSelection.nEndPara,
+ "paragraph mismatch in selection" );
+ DBG_ASSERT( aSelection.nStartPos <= aSelection.nEndPos,
+ "start pos > end pos" );
+ m_nLastPos = aSelection.nStartPos;
+}
+
+
+void TextConvWrapper::HandleNewUnit(
+ const sal_Int32 nUnitStart,
+ const sal_Int32 nUnitEnd )
+{
+ SelectNewUnit_impl( nUnitStart, nUnitEnd );
+}
+
+#ifdef DBG_UTIL
+namespace
+{
+ bool IsSimilarChinese( LanguageType nLang1, LanguageType nLang2 )
+ {
+ using namespace editeng;
+ return (HangulHanjaConversion::IsTraditional(nLang1) && HangulHanjaConversion::IsTraditional(nLang2)) ||
+ (HangulHanjaConversion::IsSimplified(nLang1) && HangulHanjaConversion::IsSimplified(nLang2));
+ }
+}
+#endif
+
+void TextConvWrapper::ReplaceUnit(
+ const sal_Int32 nUnitStart, const sal_Int32 nUnitEnd,
+ const OUString& rOrigText,
+ const OUString& rReplaceWith,
+ const css::uno::Sequence< sal_Int32 > &rOffsets,
+ ReplacementAction eAction,
+ LanguageType *pNewUnitLanguage )
+{
+ const bool bOK = 0 <= nUnitStart && 0 <= nUnitEnd && nUnitStart <= nUnitEnd;
+ DBG_ASSERT( bOK, "invalid arguments" );
+ if (!bOK)
+ return;
+
+ // select current unit
+ SelectNewUnit_impl( nUnitStart, nUnitEnd );
+
+ OUString aOrigTxt( m_pEditView->GetSelected() );
+ OUString aNewTxt( rReplaceWith );
+ switch (eAction)
+ {
+ case eExchange :
+ break;
+ case eReplacementBracketed :
+ aNewTxt = aOrigTxt + "(" + rReplaceWith + ")";
+ break;
+ case eOriginalBracketed :
+ aNewTxt = rReplaceWith + "(" + aOrigTxt + ")";
+ break;
+ case eReplacementAbove :
+ case eOriginalAbove :
+ case eReplacementBelow :
+ case eOriginalBelow :
+ OSL_FAIL( "Rubies not supported" );
+ break;
+ default:
+ OSL_FAIL( "unexpected case" );
+ }
+ m_nUnitOffset = m_nUnitOffset + nUnitStart + aNewTxt.getLength();
+
+ // remember current original language for later use
+ ImpEditEngine *pImpEditEng = m_pEditView->GetImpEditEngine();
+ ESelection aOldSel = m_pEditView->GetSelection();
+ //EditSelection aOldEditSel = pEditView->GetImpEditView()->GetEditSelection();
+
+#ifdef DBG_UTIL
+ LanguageType nOldLang = pImpEditEng->GetLanguage( pImpEditEng->CreateSel( aOldSel ).Min() ).nLang;
+#endif
+
+ pImpEditEng->UndoActionStart( EDITUNDO_INSERT );
+
+ // according to FT we should currently not bother about keeping
+ // attributes in Hangul/Hanja conversion and leave that untouched.
+ // Thus we do this only for Chinese translation...
+ bool bIsChineseConversion = IsChinese( GetSourceLanguage() );
+ if (bIsChineseConversion)
+ ChangeText( aNewTxt, rOrigText, &rOffsets, &aOldSel );
+ else
+ ChangeText( aNewTxt, rOrigText, nullptr, nullptr );
+
+ // change language and font if necessary
+ if (bIsChineseConversion)
+ {
+ DBG_ASSERT( GetTargetLanguage() == LANGUAGE_CHINESE_SIMPLIFIED || GetTargetLanguage() == LANGUAGE_CHINESE_TRADITIONAL,
+ "TextConvWrapper::ReplaceUnit : unexpected target language" );
+
+ ESelection aNewSel( aOldSel );
+ aNewSel.nStartPos = aNewSel.nStartPos - aNewTxt.getLength();
+
+ if (pNewUnitLanguage)
+ {
+#ifdef DBG_UTIL
+ DBG_ASSERT(!IsSimilarChinese( *pNewUnitLanguage, nOldLang ),
+ "similar language should not be changed!");
+#endif
+ SetLanguageAndFont( aNewSel, *pNewUnitLanguage, EE_CHAR_LANGUAGE_CJK,
+ GetTargetFont(), EE_CHAR_FONTINFO_CJK );
+ }
+ }
+
+ pImpEditEng->UndoActionEnd();
+
+ // adjust ConvContinue / ConvTo if necessary
+ ImpEditEngine* pImpEE = m_pEditView->GetImpEditEngine();
+ ConvInfo* pConvInfo = pImpEE->GetConvInfo();
+ sal_Int32 nDelta = aNewTxt.getLength() - aOrigTxt.getLength();
+ if (nDelta != 0)
+ {
+ // Note: replacement is always done in the current paragraph
+ // which is the one ConvContinue points to
+ pConvInfo->aConvContinue.nIndex = pConvInfo->aConvContinue.nIndex + nDelta;
+
+ // if that is the same as the one where the conversions ends
+ // the end needs to be updated also
+ if (pConvInfo->aConvTo.nPara == pConvInfo->aConvContinue.nPara)
+ pConvInfo->aConvTo.nIndex = pConvInfo->aConvTo.nIndex + nDelta;
+ }
+}
+
+
+void TextConvWrapper::ChangeText( const OUString &rNewText,
+ std::u16string_view rOrigText,
+ const uno::Sequence< sal_Int32 > *pOffsets,
+ ESelection *pESelection )
+{
+ //!! code is a modified copy of SwHHCWrapper::ChangeText from sw !!
+
+ DBG_ASSERT( !rNewText.isEmpty(), "unexpected empty string" );
+ if (rNewText.isEmpty())
+ return;
+
+ if (pOffsets && pESelection) // try to keep as much attributation as possible ?
+ {
+ pESelection->Adjust();
+
+ // remember cursor start position for later setting of the cursor
+ const sal_Int32 nStartIndex = pESelection->nStartPos;
+
+ const sal_Int32 nIndices = pOffsets->getLength();
+ const sal_Int32 *pIndices = pOffsets->getConstArray();
+ const sal_Int32 nConvTextLen = rNewText.getLength();
+ sal_Int32 nPos = 0;
+ sal_Int32 nChgPos = -1;
+ sal_Int32 nConvChgPos = -1;
+
+ // offset to calculate the position in the text taking into
+ // account that text may have been replaced with new text of
+ // different length. Negative values allowed!
+ sal_Int32 nCorrectionOffset = 0;
+
+ DBG_ASSERT(nIndices == 0 || nIndices == nConvTextLen,
+ "mismatch between string length and sequence length!" );
+
+ // find all substrings that need to be replaced (and only those)
+ while (true)
+ {
+ // get index in original text that matches nPos in new text
+ sal_Int32 nIndex;
+ if (nPos < nConvTextLen)
+ nIndex = nPos < nIndices ? pIndices[nPos] : nPos;
+ else
+ {
+ nPos = nConvTextLen;
+ nIndex = rOrigText.size();
+ }
+
+ // end of string also terminates non-matching char sequence
+ if (nPos == nConvTextLen || rOrigText[nIndex] == rNewText[nPos])
+ {
+ // substring that needs to be replaced found?
+ if (nChgPos>=0 && nConvChgPos>=0)
+ {
+ const sal_Int32 nChgLen = nIndex - nChgPos;
+ const sal_Int32 nConvChgLen = nPos - nConvChgPos;
+ OUString aInNew( rNewText.copy( nConvChgPos, nConvChgLen ) );
+
+ // set selection to sub string to be replaced in original text
+ ESelection aSel( *pESelection );
+ sal_Int32 nChgInNodeStartIndex = nStartIndex + nCorrectionOffset + nChgPos;
+ aSel.nStartPos = nChgInNodeStartIndex;
+ aSel.nEndPos = nChgInNodeStartIndex + nChgLen;
+ m_pEditView->SetSelection( aSel );
+
+ // replace selected sub string with the corresponding
+ // sub string from the new text while keeping as
+ // much from the attributes as possible
+ ChangeText_impl( aInNew, true );
+
+ nCorrectionOffset += nConvChgLen - nChgLen;
+
+ nChgPos = -1;
+ nConvChgPos = -1;
+ }
+ }
+ else
+ {
+ // begin of non-matching char sequence found ?
+ if (nChgPos<0 && nConvChgPos<0)
+ {
+ nChgPos = nIndex;
+ nConvChgPos = nPos;
+ }
+ }
+ if (nPos >= nConvTextLen)
+ break;
+ ++nPos;
+ }
+
+ // set cursor to the end of the inserted text
+ // (as it would happen after ChangeText_impl (Delete and Insert)
+ // of the whole text in the 'else' branch below)
+ pESelection->nStartPos = pESelection->nEndPos = nStartIndex + nConvTextLen;
+ }
+ else
+ {
+ ChangeText_impl( rNewText, false );
+ }
+}
+
+
+void TextConvWrapper::ChangeText_impl( const OUString &rNewText, bool bKeepAttributes )
+{
+ if (bKeepAttributes)
+ {
+ // save attributes to be restored
+ SfxItemSet aSet( m_pEditView->GetAttribs() );
+
+ // replace old text and select new text
+ m_pEditView->InsertText( rNewText, true );
+
+ // since 'SetAttribs' below function like merging with the attributes
+ // from the itemset with any existing ones we have to get rid of all
+ // all attributes now. (Those attributes that may take effect left
+ // to the position where the new text gets inserted after the old text
+ // was deleted)
+ m_pEditView->RemoveAttribs(EERemoveParaAttribsMode::RemoveNone, 0);
+ // apply saved attributes to new inserted text
+ m_pEditView->SetAttribs( aSet );
+ }
+ else
+ {
+ m_pEditView->InsertText( rNewText );
+ }
+}
+
+
+void TextConvWrapper::Convert()
+{
+ m_bStartChk = false;
+ ConvStart_impl( SvxSpellArea::BodyEnd );
+ ConvertDocument();
+}
+
+
+bool TextConvWrapper::HasRubySupport() const
+{
+ return false;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/textconv.hxx b/editeng/source/editeng/textconv.hxx
new file mode 100644
index 0000000000..96525a98f5
--- /dev/null
+++ b/editeng/source/editeng/textconv.hxx
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <editeng/editdata.hxx>
+#include <editeng/svxenum.hxx>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <editeng/hangulhanja.hxx>
+
+class EditView;
+
+class TextConvWrapper final : public editeng::HangulHanjaConversion
+{
+ OUString m_aConvText; // convertible text part found last time
+ LanguageType m_nConvTextLang; // language of aConvText
+ sal_uInt16 m_nUnitOffset; // offset of current unit in the current text portion (word)
+ sal_uInt16 m_nLastPos; // starting position of the last found text portion (word)
+
+ ESelection m_aConvSel; // selection to be converted if
+ // 'HasRange' is true, other conversion
+ // starts from the cursor position
+
+ EditView * m_pEditView;
+ weld::Widget* m_pWin;
+
+ bool m_bStartChk;
+ bool m_bStartDone;
+ bool m_bEndDone;
+ bool m_bAllowChange; // storage for _bAllowImplicitChangesForNotConvertibleText
+ // parameters value of function GetNextPortion.
+ // used to transport the value to where it is needed.
+
+
+ // from SvxSpellWrapper copied and modified
+ bool ConvNext_impl(); // former SpellNext
+ void FindConvText_impl(); // former FindSpellError
+ bool ConvMore_impl(); // former SpellMore
+
+ // from EditSpellWrapper copied and modified
+ void ConvStart_impl( SvxSpellArea eSpell ); // former SpellStart
+ bool ConvContinue_impl(); // former SpellContinue
+
+ void SelectNewUnit_impl( const sal_Int32 nUnitStart,
+ const sal_Int32 nUnitEnd );
+
+ void ChangeText( const OUString &rNewText,
+ std::u16string_view rOrigText,
+ const css::uno::Sequence< sal_Int32 > *pOffsets,
+ ESelection *pESelection );
+ void ChangeText_impl( const OUString &rNewText, bool bKeepAttributes );
+
+ TextConvWrapper (const TextConvWrapper &) = delete;
+ TextConvWrapper & operator= (const TextConvWrapper &) = delete;
+
+ virtual void GetNextPortion( OUString& /* [out] */ rNextPortion,
+ LanguageType& /* [out] */ rLangOfPortion,
+ bool /* [in] */ _bAllowImplicitChangesForNotConvertibleText ) override;
+ virtual void HandleNewUnit( const sal_Int32 nUnitStart,
+ const sal_Int32 nUnitEnd ) override;
+ virtual void ReplaceUnit(
+ const sal_Int32 nUnitStart, const sal_Int32 nUnitEnd,
+ const OUString& rOrigText,
+ const OUString& rReplaceWith,
+ const css::uno::Sequence< sal_Int32 > &rOffsets,
+ ReplacementAction eAction,
+ LanguageType *pNewUnitLanguage ) override;
+
+ virtual bool HasRubySupport() const override;
+
+ void SetLanguageAndFont( const ESelection &rESel,
+ LanguageType nLang, sal_uInt16 nLangWhichId,
+ const vcl::Font *pFont, sal_uInt16 nFontWhichId );
+
+
+public:
+ TextConvWrapper(weld::Widget* pWindow,
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::lang::Locale& rSourceLocale,
+ const css::lang::Locale& rTargetLocale,
+ const vcl::Font* pTargetFont,
+ sal_Int32 nOptions,
+ bool bIsInteractive,
+ bool bIsStart, EditView* pView );
+
+ virtual ~TextConvWrapper() override;
+
+ void Convert();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/items/CustomPropertyField.cxx b/editeng/source/items/CustomPropertyField.cxx
new file mode 100644
index 0000000000..eaad4c4c4d
--- /dev/null
+++ b/editeng/source/items/CustomPropertyField.cxx
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <editeng/CustomPropertyField.hxx>
+#include <utility>
+#include <vcl/metaact.hxx>
+#include <com/sun/star/beans/XPropertyContainer.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+
+using namespace css;
+
+namespace editeng
+{
+
+CustomPropertyField::CustomPropertyField(OUString aName, OUString aCurrentPresentation)
+ : msName(std::move(aName))
+ , msCurrentPresentation(std::move(aCurrentPresentation))
+{}
+
+CustomPropertyField::~CustomPropertyField()
+{}
+
+std::unique_ptr<SvxFieldData> CustomPropertyField::Clone() const
+{
+ return std::make_unique<CustomPropertyField>(msName, msCurrentPresentation);
+}
+
+bool CustomPropertyField::operator==(const SvxFieldData& rOther) const
+{
+ if (typeid(rOther) != typeid(*this))
+ return false;
+
+ const CustomPropertyField& rOtherField = static_cast<const CustomPropertyField&>(rOther);
+ return (msName == rOtherField.msName &&
+ msCurrentPresentation == rOtherField.msCurrentPresentation);
+}
+
+MetaAction* CustomPropertyField::createBeginComment() const
+{
+ return new MetaCommentAction("FIELD_SEQ_BEGIN"_ostr);
+}
+
+OUString CustomPropertyField::GetFormatted(uno::Reference<document::XDocumentProperties> const & xDocumentProperties)
+{
+ if (msName.isEmpty())
+ return OUString();
+ if (!xDocumentProperties.is())
+ return OUString();
+ uno::Reference<beans::XPropertyContainer> xPropertyContainer = xDocumentProperties->getUserDefinedProperties();
+ if (!xPropertyContainer.is())
+ return OUString();
+ uno::Reference<beans::XPropertySet> xPropertySet(xPropertyContainer, uno::UNO_QUERY);
+ if (!xPropertySet.is())
+ return OUString();
+ uno::Any aAny = xPropertySet->getPropertyValue(msName);
+ if (!aAny.has<OUString>())
+ return OUString();
+ msCurrentPresentation = aAny.get<OUString>();
+ return msCurrentPresentation;
+}
+
+} // end editeng namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/items/borderline.cxx b/editeng/source/items/borderline.cxx
new file mode 100644
index 0000000000..05742eb951
--- /dev/null
+++ b/editeng/source/items/borderline.cxx
@@ -0,0 +1,719 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+
+#include <basegfx/color/bcolor.hxx>
+#include <basegfx/color/bcolortools.hxx>
+
+#include <editeng/borderline.hxx>
+#include <editeng/itemtype.hxx>
+#include <editeng/editrids.hrc>
+#include <editeng/eerdll.hxx>
+#include <tools/bigint.hxx>
+
+#include <docmodel/uno/UnoComplexColor.hxx>
+#include <com/sun/star/util/XComplexColor.hpp>
+
+using namespace ::com::sun::star::table::BorderLineStyle;
+using namespace css;
+
+// class SvxBorderLine --------------------------------------------------
+
+namespace {
+
+ Color lcl_compute3DColor( Color aMain, int nLight, int nMedium, int nDark )
+ {
+ basegfx::BColor color = aMain.getBColor( );
+ basegfx::BColor hsl = basegfx::utils::rgb2hsl( color );
+
+ int nCoef = 0;
+ if ( hsl.getZ( ) >= 0.5 )
+ nCoef = nLight;
+ else if ( 0.5 > hsl.getZ() && hsl.getZ() >= 0.25 )
+ nCoef = nMedium;
+ else
+ nCoef = nDark;
+
+ double L = std::min(hsl.getZ() * 255.0 + nCoef, 255.0);
+ hsl.setZ( L / 255.0 );
+ color = basegfx::utils::hsl2rgb( hsl );
+
+ return Color( color );
+ }
+} // Anonymous namespace
+
+namespace editeng
+{
+
+bool SvxBorderLine::setComplexColorFromAny(css::uno::Any const& rValue)
+{
+ css::uno::Reference<css::util::XComplexColor> xComplexColor;
+ if (!(rValue >>= xComplexColor))
+ return false;
+
+ if (xComplexColor.is())
+ {
+ auto aComplexColor = model::color::getFromXComplexColor(xComplexColor);
+ setComplexColor(aComplexColor);
+ }
+ return true;
+}
+
+Color SvxBorderLine::darkColor( Color aMain )
+{
+ return aMain;
+}
+
+Color SvxBorderLine::lightColor( Color aMain )
+{
+
+ // Divide Luminance by 2
+ basegfx::BColor color = aMain.getBColor( );
+ basegfx::BColor hsl = basegfx::utils::rgb2hsl( color );
+ hsl.setZ( hsl.getZ() * 0.5 );
+ color = basegfx::utils::hsl2rgb( hsl );
+
+ return Color( color );
+}
+
+
+Color SvxBorderLine::threeDLightColor( Color aMain )
+{
+ // These values have been defined in an empirical way
+ return lcl_compute3DColor( aMain, 3, 40, 83 );
+}
+
+Color SvxBorderLine::threeDDarkColor( Color aMain )
+{
+ // These values have been defined in an empirical way
+ return lcl_compute3DColor( aMain, -85, -43, -1 );
+}
+
+Color SvxBorderLine::threeDMediumColor( Color aMain )
+{
+ // These values have been defined in an empirical way
+ return lcl_compute3DColor( aMain, -42, -0, 42 );
+}
+
+SvxBorderLine::SvxBorderLine( const Color *pCol, tools::Long nWidth,
+ SvxBorderLineStyle nStyle,
+ Color (*pColorOutFn)( Color ), Color (*pColorInFn)( Color ) )
+ : m_nWidth(nWidth)
+ , m_nMult(1)
+ , m_nDiv(1)
+ , m_pColorOutFn(pColorOutFn)
+ , m_pColorInFn(pColorInFn)
+ , m_pColorGapFn(nullptr)
+ , m_aWidthImpl(SvxBorderLine::getWidthImpl(nStyle))
+ , m_nStyle(nStyle)
+ , m_bMirrorWidths(false)
+ , m_bUseLeftTop(false)
+{
+ if (pCol)
+ m_aColor = *pCol;
+}
+
+SvxBorderLineStyle
+ConvertBorderStyleFromWord(int const nWordLineStyle)
+{
+ switch (nWordLineStyle)
+ {
+ // First the single lines
+ case 1:
+ case 2: // thick line
+ case 5: // hairline
+ // and the unsupported special cases which we map to a single line
+ case 20:
+ return SvxBorderLineStyle::SOLID;
+ case 6:
+ return SvxBorderLineStyle::DOTTED;
+ case 7:
+ return SvxBorderLineStyle::DASHED;
+ case 22:
+ return SvxBorderLineStyle::FINE_DASHED;
+ case 8:
+ return SvxBorderLineStyle::DASH_DOT;
+ case 9:
+ return SvxBorderLineStyle::DASH_DOT_DOT;
+ // then the shading beams which we represent by a double line
+ case 23:
+ return SvxBorderLineStyle::DOUBLE;
+ // then the double lines, for which we have good matches
+ case 3:
+ case 10: // Don't have triple so use double
+ case 21: // Don't have double wave: use double instead
+ return SvxBorderLineStyle::DOUBLE;
+ case 11:
+ return SvxBorderLineStyle::THINTHICK_SMALLGAP;
+ case 12:
+ case 13: // Don't have thin thick thin, so use thick thin
+ return SvxBorderLineStyle::THICKTHIN_SMALLGAP;
+ case 14:
+ return SvxBorderLineStyle::THINTHICK_MEDIUMGAP;
+ case 15:
+ case 16: // Don't have thin thick thin, so use thick thin
+ return SvxBorderLineStyle::THICKTHIN_MEDIUMGAP;
+ case 17:
+ return SvxBorderLineStyle::THINTHICK_LARGEGAP;
+ case 18:
+ case 19: // Don't have thin thick thin, so use thick thin
+ return SvxBorderLineStyle::THICKTHIN_LARGEGAP;
+ case 24:
+ return SvxBorderLineStyle::EMBOSSED;
+ case 25:
+ return SvxBorderLineStyle::ENGRAVED;
+ case 26:
+ return SvxBorderLineStyle::OUTSET;
+ case 27:
+ return SvxBorderLineStyle::INSET;
+ default:
+ return SvxBorderLineStyle::NONE;
+ }
+}
+
+const double THINTHICK_SMALLGAP_line2 = 15.0;
+const double THINTHICK_SMALLGAP_gap = 15.0;
+const double THINTHICK_LARGEGAP_line1 = 30.0;
+const double THINTHICK_LARGEGAP_line2 = 15.0;
+const double THICKTHIN_SMALLGAP_line1 = 15.0;
+const double THICKTHIN_SMALLGAP_gap = 15.0;
+const double THICKTHIN_LARGEGAP_line1 = 15.0;
+const double THICKTHIN_LARGEGAP_line2 = 30.0;
+const double OUTSET_line1 = 15.0;
+const double INSET_line2 = 15.0;
+
+double
+ConvertBorderWidthFromWord(SvxBorderLineStyle const eStyle, double const i_fWidth,
+ int const nWordLineStyle)
+{
+ // fdo#68779: at least for RTF, 0.75pt is the default if width is missing
+ double const fWidth((i_fWidth == 0.0) ? 15.0 : i_fWidth);
+ switch (eStyle)
+ {
+ // Single lines
+ case SvxBorderLineStyle::SOLID:
+ switch (nWordLineStyle)
+ {
+ case 2:
+ return (fWidth * 2.0); // thick
+ case 5: // fdo#55526: map 0 hairline width to > 0
+ return std::max(fWidth, 1.0);
+ default:
+ return fWidth;
+ }
+ break;
+
+ case SvxBorderLineStyle::DOTTED:
+ case SvxBorderLineStyle::DASHED:
+ case SvxBorderLineStyle::DASH_DOT:
+ case SvxBorderLineStyle::DASH_DOT_DOT:
+ return fWidth;
+
+ // Display a minimum effective border width of 1pt
+ case SvxBorderLineStyle::FINE_DASHED:
+ return (fWidth > 0 && fWidth < 20) ? 20 : fWidth;
+
+ // Double lines
+ case SvxBorderLineStyle::DOUBLE:
+ return fWidth * 3.0;
+
+ case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
+ case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
+ case SvxBorderLineStyle::EMBOSSED:
+ case SvxBorderLineStyle::ENGRAVED:
+ return fWidth * 2.0;
+
+ case SvxBorderLineStyle::THINTHICK_SMALLGAP:
+ return fWidth + THINTHICK_SMALLGAP_line2 + THINTHICK_SMALLGAP_gap;
+
+ case SvxBorderLineStyle::THINTHICK_LARGEGAP:
+ return fWidth + THINTHICK_LARGEGAP_line1 + THINTHICK_LARGEGAP_line2;
+
+ case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
+ return fWidth + THICKTHIN_SMALLGAP_line1 + THICKTHIN_SMALLGAP_gap;
+
+ case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
+ return fWidth + THICKTHIN_LARGEGAP_line1 + THICKTHIN_LARGEGAP_line2;
+
+ case SvxBorderLineStyle::OUTSET:
+ return (fWidth * 2.0) + OUTSET_line1;
+
+ case SvxBorderLineStyle::INSET:
+ return (fWidth * 2.0) + INSET_line2;
+
+ default:
+ assert(false); // should only be called for known border style
+ }
+ return 0;
+}
+
+double
+ConvertBorderWidthToWord(SvxBorderLineStyle const eStyle, double const fWidth)
+{
+ if ( !fWidth )
+ return 0;
+
+ switch (eStyle)
+ {
+ // Single lines
+ case SvxBorderLineStyle::SOLID:
+ case SvxBorderLineStyle::DOTTED:
+ case SvxBorderLineStyle::DASHED:
+ case SvxBorderLineStyle::FINE_DASHED:
+ case SvxBorderLineStyle::DASH_DOT:
+ case SvxBorderLineStyle::DASH_DOT_DOT:
+ return fWidth;
+
+ // Double lines
+ case SvxBorderLineStyle::DOUBLE:
+ case SvxBorderLineStyle::DOUBLE_THIN:
+ return std::max(1.0, fWidth / 3.0);
+
+ case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
+ case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
+ case SvxBorderLineStyle::EMBOSSED:
+ case SvxBorderLineStyle::ENGRAVED:
+ return std::max(1.0, fWidth / 2.0);
+
+ case SvxBorderLineStyle::THINTHICK_SMALLGAP:
+ return std::max(1.0, fWidth - THINTHICK_SMALLGAP_line2 - THINTHICK_SMALLGAP_gap);
+
+ case SvxBorderLineStyle::THINTHICK_LARGEGAP:
+ return std::max(1.0, fWidth - THINTHICK_LARGEGAP_line1 - THINTHICK_LARGEGAP_line2);
+
+ case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
+ return std::max(1.0, fWidth - THICKTHIN_SMALLGAP_line1 - THICKTHIN_SMALLGAP_gap);
+
+ case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
+ return std::max(1.0, fWidth - THICKTHIN_LARGEGAP_line1 - THICKTHIN_LARGEGAP_line2);
+
+ case SvxBorderLineStyle::OUTSET:
+ return std::max(1.0, (fWidth - OUTSET_line1) / 2.0);
+
+ case SvxBorderLineStyle::INSET:
+ return std::max(1.0, (fWidth - INSET_line2) / 2.0);
+
+ case SvxBorderLineStyle::NONE:
+ return 0;
+
+ default:
+ assert(false); // should only be called for known border style
+ return 0;
+ }
+}
+
+/** Get the BorderWithImpl object corresponding to the given #nStyle, all the
+ units handled by the resulting object are Twips and the
+ BorderWidthImpl::GetLine1() corresponds to the Outer Line.
+ */
+BorderWidthImpl SvxBorderLine::getWidthImpl( SvxBorderLineStyle nStyle )
+{
+ BorderWidthImpl aImpl;
+
+ switch ( nStyle )
+ {
+ // No line: no width
+ case SvxBorderLineStyle::NONE:
+ aImpl = BorderWidthImpl( BorderWidthImplFlags::FIXED, 0.0 );
+ break;
+
+ // Single lines
+ case SvxBorderLineStyle::SOLID:
+ case SvxBorderLineStyle::DOTTED:
+ case SvxBorderLineStyle::DASHED:
+ case SvxBorderLineStyle::FINE_DASHED:
+ case SvxBorderLineStyle::DASH_DOT:
+ case SvxBorderLineStyle::DASH_DOT_DOT:
+ aImpl = BorderWidthImpl( BorderWidthImplFlags::CHANGE_LINE1, 1.0 );
+ break;
+
+ // Double lines
+
+ case SvxBorderLineStyle::DOUBLE:
+ aImpl = BorderWidthImpl(
+ BorderWidthImplFlags::CHANGE_LINE1 | BorderWidthImplFlags::CHANGE_LINE2 | BorderWidthImplFlags::CHANGE_DIST,
+ // fdo#46112 fdo#38542 fdo#43249:
+ // non-constant widths must sum to 1
+ 1.0/3.0, 1.0/3.0, 1.0/3.0 );
+ break;
+
+ case SvxBorderLineStyle::DOUBLE_THIN:
+ aImpl = BorderWidthImpl(BorderWidthImplFlags::CHANGE_DIST, 10.0, 10.0, 1.0);
+ break;
+
+ case SvxBorderLineStyle::THINTHICK_SMALLGAP:
+ aImpl = BorderWidthImpl( BorderWidthImplFlags::CHANGE_LINE1, 1.0,
+ THINTHICK_SMALLGAP_line2, THINTHICK_SMALLGAP_gap );
+ break;
+
+ case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
+ aImpl = BorderWidthImpl(
+ BorderWidthImplFlags::CHANGE_LINE1 | BorderWidthImplFlags::CHANGE_LINE2 | BorderWidthImplFlags::CHANGE_DIST,
+ 0.5, 0.25, 0.25 );
+ break;
+
+ case SvxBorderLineStyle::THINTHICK_LARGEGAP:
+ aImpl = BorderWidthImpl( BorderWidthImplFlags::CHANGE_DIST,
+ THINTHICK_LARGEGAP_line1, THINTHICK_LARGEGAP_line2, 1.0 );
+ break;
+
+ case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
+ aImpl = BorderWidthImpl( BorderWidthImplFlags::CHANGE_LINE2, THICKTHIN_SMALLGAP_line1,
+ 1.0, THICKTHIN_SMALLGAP_gap );
+ break;
+
+ case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
+ aImpl = BorderWidthImpl(
+ BorderWidthImplFlags::CHANGE_LINE1 | BorderWidthImplFlags::CHANGE_LINE2 | BorderWidthImplFlags::CHANGE_DIST,
+ 0.25, 0.5, 0.25 );
+ break;
+
+ case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
+ aImpl = BorderWidthImpl( BorderWidthImplFlags::CHANGE_DIST, THICKTHIN_LARGEGAP_line1,
+ THICKTHIN_LARGEGAP_line2, 1.0 );
+ break;
+
+ // Engraved / Embossed
+ /*
+ * Word compat: the lines widths are exactly following this rule, should be:
+ * 0.75pt up to 3pt and then 3pt
+ */
+
+ case SvxBorderLineStyle::EMBOSSED:
+ case SvxBorderLineStyle::ENGRAVED:
+ aImpl = BorderWidthImpl(
+ BorderWidthImplFlags::CHANGE_LINE1 | BorderWidthImplFlags::CHANGE_LINE2 | BorderWidthImplFlags::CHANGE_DIST,
+ 0.25, 0.25, 0.5 );
+ break;
+
+ // Inset / Outset
+ /*
+ * Word compat: the gap width should be measured relatively to the biggest width for the
+ * row or column.
+ */
+ case SvxBorderLineStyle::OUTSET:
+ aImpl = BorderWidthImpl(
+ BorderWidthImplFlags::CHANGE_LINE2 | BorderWidthImplFlags::CHANGE_DIST,
+ OUTSET_line1, 0.5, 0.5 );
+ break;
+
+ case SvxBorderLineStyle::INSET:
+ aImpl = BorderWidthImpl(
+ BorderWidthImplFlags::CHANGE_LINE1 | BorderWidthImplFlags::CHANGE_DIST,
+ 0.5, INSET_line2, 0.5 );
+ break;
+ }
+
+ return aImpl;
+}
+
+void SvxBorderLine::ScaleMetrics( tools::Long nMult, tools::Long nDiv )
+{
+ m_nMult = nMult;
+ m_nDiv = nDiv;
+}
+
+void SvxBorderLine::GuessLinesWidths( SvxBorderLineStyle nStyle, sal_uInt16 nOut, sal_uInt16 nIn, sal_uInt16 nDist )
+{
+ if (SvxBorderLineStyle::NONE == nStyle)
+ {
+ nStyle = SvxBorderLineStyle::SOLID;
+ if ( nOut > 0 && nIn > 0 )
+ nStyle = SvxBorderLineStyle::DOUBLE;
+ }
+
+ if ( nStyle == SvxBorderLineStyle::DOUBLE )
+ {
+ static const SvxBorderLineStyle aDoubleStyles[] =
+ {
+ SvxBorderLineStyle::DOUBLE,
+ SvxBorderLineStyle::DOUBLE_THIN,
+ SvxBorderLineStyle::THINTHICK_SMALLGAP,
+ SvxBorderLineStyle::THINTHICK_MEDIUMGAP,
+ SvxBorderLineStyle::THINTHICK_LARGEGAP,
+ SvxBorderLineStyle::THICKTHIN_SMALLGAP,
+ SvxBorderLineStyle::THICKTHIN_MEDIUMGAP,
+ SvxBorderLineStyle::THICKTHIN_LARGEGAP
+ };
+
+ static size_t const len = SAL_N_ELEMENTS(aDoubleStyles);
+ tools::Long nWidth = 0;
+ SvxBorderLineStyle nTestStyle(SvxBorderLineStyle::NONE);
+ for (size_t i = 0; i < len && nWidth == 0; ++i)
+ {
+ nTestStyle = aDoubleStyles[i];
+ BorderWidthImpl aWidthImpl = getWidthImpl( nTestStyle );
+ nWidth = aWidthImpl.GuessWidth( nOut, nIn, nDist );
+ }
+
+ // If anything matched, then set it
+ if ( nWidth > 0 )
+ {
+ nStyle = nTestStyle;
+ SetBorderLineStyle(nStyle);
+ m_nWidth = nWidth;
+ }
+ else
+ {
+ // fdo#38542: not a known double, default to something custom...
+ SetBorderLineStyle(nStyle);
+ m_nWidth = nOut + nIn + nDist;
+ if (m_nWidth)
+ {
+ m_aWidthImpl = BorderWidthImpl(
+ BorderWidthImplFlags::CHANGE_LINE1 | BorderWidthImplFlags::CHANGE_LINE2 | BorderWidthImplFlags::CHANGE_DIST,
+ static_cast<double>(nOut ) / static_cast<double>(m_nWidth),
+ static_cast<double>(nIn ) / static_cast<double>(m_nWidth),
+ static_cast<double>(nDist) / static_cast<double>(m_nWidth));
+ }
+ }
+ }
+ else
+ {
+ SetBorderLineStyle(nStyle);
+ if (nOut == 0 && nIn > 0)
+ {
+ // If only inner width is given swap inner and outer widths for
+ // single line styles, otherwise GuessWidth() marks this as invalid
+ // and returns a 0 width.
+ switch (nStyle)
+ {
+ case SvxBorderLineStyle::SOLID:
+ case SvxBorderLineStyle::DOTTED:
+ case SvxBorderLineStyle::DASHED:
+ case SvxBorderLineStyle::FINE_DASHED:
+ case SvxBorderLineStyle::DASH_DOT:
+ case SvxBorderLineStyle::DASH_DOT_DOT:
+ std::swap( nOut, nIn);
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ m_nWidth = m_aWidthImpl.GuessWidth( nOut, nIn, nDist );
+ }
+}
+
+sal_uInt16 SvxBorderLine::GetOutWidth() const
+{
+ sal_uInt16 nOut = static_cast<sal_uInt16>(BigInt::Scale( m_aWidthImpl.GetLine1( m_nWidth ), m_nMult, m_nDiv ));
+ if ( m_bMirrorWidths )
+ nOut = static_cast<sal_uInt16>(BigInt::Scale( m_aWidthImpl.GetLine2( m_nWidth ), m_nMult, m_nDiv ));
+ return nOut;
+}
+
+sal_uInt16 SvxBorderLine::GetInWidth() const
+{
+ sal_uInt16 nIn = static_cast<sal_uInt16>(BigInt::Scale( m_aWidthImpl.GetLine2( m_nWidth ), m_nMult, m_nDiv ));
+ if ( m_bMirrorWidths )
+ nIn = static_cast<sal_uInt16>(BigInt::Scale( m_aWidthImpl.GetLine1( m_nWidth ), m_nMult, m_nDiv ));
+ return nIn;
+}
+
+sal_uInt16 SvxBorderLine::GetDistance() const
+{
+ return static_cast<sal_uInt16>(BigInt::Scale( m_aWidthImpl.GetGap( m_nWidth ), m_nMult, m_nDiv ));
+}
+
+
+bool SvxBorderLine::operator==( const SvxBorderLine& rCmp ) const
+{
+ return (m_aColor == rCmp.m_aColor &&
+ m_aComplexColor == rCmp.m_aComplexColor &&
+ m_nWidth == rCmp.m_nWidth &&
+ m_bMirrorWidths == rCmp.m_bMirrorWidths &&
+ m_aWidthImpl == rCmp.m_aWidthImpl &&
+ m_nStyle == rCmp.GetBorderLineStyle() &&
+ m_bUseLeftTop == rCmp.m_bUseLeftTop &&
+ m_pColorOutFn == rCmp.m_pColorOutFn &&
+ m_pColorInFn == rCmp.m_pColorInFn &&
+ m_pColorGapFn == rCmp.m_pColorGapFn);
+}
+
+void SvxBorderLine::SetBorderLineStyle( SvxBorderLineStyle nNew )
+{
+ m_nStyle = nNew;
+ m_aWidthImpl = getWidthImpl( m_nStyle );
+
+ switch ( nNew )
+ {
+ case SvxBorderLineStyle::EMBOSSED:
+ m_pColorOutFn = threeDLightColor;
+ m_pColorInFn = threeDDarkColor;
+ m_pColorGapFn = threeDMediumColor;
+ m_bUseLeftTop = true;
+ break;
+ case SvxBorderLineStyle::ENGRAVED:
+ m_pColorOutFn = threeDDarkColor;
+ m_pColorInFn = threeDLightColor;
+ m_pColorGapFn = threeDMediumColor;
+ m_bUseLeftTop = true;
+ break;
+ case SvxBorderLineStyle::OUTSET:
+ m_pColorOutFn = lightColor;
+ m_pColorInFn = darkColor;
+ m_bUseLeftTop = true;
+ m_pColorGapFn = nullptr;
+ break;
+ case SvxBorderLineStyle::INSET:
+ m_pColorOutFn = darkColor;
+ m_pColorInFn = lightColor;
+ m_bUseLeftTop = true;
+ m_pColorGapFn = nullptr;
+ break;
+ default:
+ m_pColorOutFn = darkColor;
+ m_pColorInFn = darkColor;
+ m_bUseLeftTop = false;
+ m_pColorGapFn = nullptr;
+ break;
+ }
+}
+
+Color SvxBorderLine::GetColorOut( bool bLeftOrTop ) const
+{
+ Color aResult = m_aColor;
+
+ if ( m_aWidthImpl.IsDouble() && m_pColorOutFn != nullptr )
+ {
+ if ( !bLeftOrTop && m_bUseLeftTop )
+ aResult = (*m_pColorInFn)(m_aColor);
+ else
+ aResult = (*m_pColorOutFn)(m_aColor);
+ }
+
+ return aResult;
+}
+
+Color SvxBorderLine::GetColorIn( bool bLeftOrTop ) const
+{
+ Color aResult = m_aColor;
+
+ if ( m_aWidthImpl.IsDouble() && m_pColorInFn != nullptr )
+ {
+ if ( !bLeftOrTop && m_bUseLeftTop )
+ aResult = (*m_pColorOutFn)(m_aColor);
+ else
+ aResult = (*m_pColorInFn)(m_aColor);
+ }
+
+ return aResult;
+}
+
+Color SvxBorderLine::GetColorGap( ) const
+{
+ Color aResult = m_aColor;
+
+ if ( m_aWidthImpl.IsDouble() && m_pColorGapFn != nullptr )
+ {
+ aResult = (*m_pColorGapFn)(m_aColor);
+ }
+
+ return aResult;
+}
+
+void SvxBorderLine::SetWidth( tools::Long nWidth )
+{
+ m_nWidth = nWidth;
+}
+
+OUString SvxBorderLine::GetValueString(MapUnit eSrcUnit,
+ MapUnit eDestUnit,
+ const IntlWrapper* pIntl,
+ bool bMetricStr) const
+{
+ static TranslateId aStyleIds[] =
+ {
+ RID_SOLID,
+ RID_DOTTED,
+ RID_DASHED,
+ RID_DOUBLE,
+ RID_THINTHICK_SMALLGAP,
+ RID_THINTHICK_MEDIUMGAP,
+ RID_THINTHICK_LARGEGAP,
+ RID_THICKTHIN_SMALLGAP,
+ RID_THICKTHIN_MEDIUMGAP,
+ RID_THICKTHIN_LARGEGAP,
+ RID_EMBOSSED,
+ RID_ENGRAVED,
+ RID_OUTSET,
+ RID_INSET,
+ RID_FINE_DASHED,
+ RID_DOUBLE_THIN,
+ RID_DASH_DOT,
+ RID_DASH_DOT_DOT
+ };
+ OUString aStr = "(" + ::GetColorString(m_aColor) + cpDelim;
+
+ if ( static_cast<int>(m_nStyle) < int(SAL_N_ELEMENTS(aStyleIds)) )
+ {
+ TranslateId pResId = aStyleIds[static_cast<int>(m_nStyle)];
+ aStr += EditResId(pResId);
+ }
+ else
+ {
+ OUString sMetric = EditResId(GetMetricId( eDestUnit ));
+ aStr += GetMetricText( static_cast<tools::Long>(GetInWidth()), eSrcUnit, eDestUnit, pIntl );
+ if ( bMetricStr )
+ aStr += sMetric;
+ aStr += cpDelim +
+ GetMetricText( static_cast<tools::Long>(GetOutWidth()), eSrcUnit, eDestUnit, pIntl );
+ if ( bMetricStr )
+ aStr += sMetric;
+ aStr += cpDelim +
+ GetMetricText( static_cast<tools::Long>(GetDistance()), eSrcUnit, eDestUnit, pIntl );
+ if ( bMetricStr )
+ aStr += sMetric;
+ }
+ aStr += ")";
+ return aStr;
+}
+
+bool SvxBorderLine::HasPriority( const SvxBorderLine& rOtherLine ) const
+{
+ const sal_uInt16 nThisSize = GetScaledWidth();
+ const sal_uInt16 nOtherSize = rOtherLine.GetScaledWidth();
+
+ if ( nThisSize > nOtherSize )
+ {
+ return true;
+ }
+ else if ( nThisSize < nOtherSize )
+ {
+ return false;
+ }
+ else if ( rOtherLine.GetInWidth() && !GetInWidth() )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+bool operator!=( const SvxBorderLine& rLeft, const SvxBorderLine& rRight )
+{
+ return !(rLeft == rRight);
+}
+
+} // namespace editeng
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/items/bulitem.cxx b/editeng/source/items/bulitem.cxx
new file mode 100644
index 0000000000..769179748b
--- /dev/null
+++ b/editeng/source/items/bulitem.cxx
@@ -0,0 +1,159 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/outdev.hxx>
+
+#include <editeng/bulletitem.hxx>
+
+SvxBulletItem::SvxBulletItem( sal_uInt16 _nWhich )
+ : SfxPoolItem(_nWhich)
+ , aFont(OutputDevice::GetDefaultFont( DefaultFontType::FIXED, LANGUAGE_SYSTEM, GetDefaultFontFlags::NONE ))
+ , nStart(1)
+ , nStyle(SvxBulletStyle::N123)
+ , nWidth(1200) // 1.2cm
+ , nScale(75)
+ , cSymbol(' ')
+{
+ aFont.SetAlignment(ALIGN_BOTTOM);
+ aFont.SetTransparent( true );
+}
+
+
+SvxBulletItem::SvxBulletItem( const SvxBulletItem& rItem )
+ : SfxPoolItem(rItem)
+ , aFont(rItem.aFont)
+ , pGraphicObject(rItem.pGraphicObject ? new GraphicObject( *rItem.pGraphicObject ) : nullptr)
+ , aPrevText(rItem.aPrevText)
+ , aFollowText(rItem.aFollowText)
+ , nStart(rItem.nStart)
+ , nStyle(rItem.nStyle)
+ , nWidth(rItem.nWidth)
+ , nScale(rItem.nScale)
+ , cSymbol(rItem.cSymbol)
+{
+}
+
+
+SvxBulletItem::~SvxBulletItem()
+{
+}
+
+SvxBulletItem* SvxBulletItem::Clone( SfxItemPool * /*pPool*/ ) const
+{
+ return new SvxBulletItem( *this );
+}
+
+void SvxBulletItem::CopyValidProperties( const SvxBulletItem& rCopyFrom )
+{
+ vcl::Font _aFont = GetFont();
+ vcl::Font aNewFont = rCopyFrom.GetFont();
+ _aFont.SetFamilyName( aNewFont.GetFamilyName() );
+ _aFont.SetFamily( aNewFont.GetFamilyType() );
+ _aFont.SetStyleName( aNewFont.GetStyleName() );
+ _aFont.SetColor( aNewFont.GetColor() );
+ SetSymbol( rCopyFrom.cSymbol );
+ SetGraphicObject( rCopyFrom.GetGraphicObject() );
+ SetScale( rCopyFrom.nScale );
+ SetStart( rCopyFrom.nStart );
+ SetStyle( rCopyFrom.nStyle );
+ aPrevText = rCopyFrom.aPrevText;
+ aFollowText = rCopyFrom.aFollowText;
+ SetFont( _aFont );
+}
+
+
+bool SvxBulletItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+ const SvxBulletItem& rBullet = static_cast<const SvxBulletItem&>(rItem);
+ // Compare with ValidMask, otherwise no put possible in an AttrSet if the
+ // item differs only in terms of the ValidMask from an existing one.
+ if( nStyle != rBullet.nStyle ||
+ nScale != rBullet.nScale ||
+ nWidth != rBullet.nWidth ||
+ nStart != rBullet.nStart ||
+ cSymbol != rBullet.cSymbol ||
+ aPrevText != rBullet.aPrevText ||
+ aFollowText != rBullet.aFollowText )
+ return false;
+
+ if( ( nStyle != SvxBulletStyle::BMP ) && ( aFont != rBullet.aFont ) )
+ return false;
+
+ if( nStyle == SvxBulletStyle::BMP )
+ {
+ if( ( pGraphicObject && !rBullet.pGraphicObject ) || ( !pGraphicObject && rBullet.pGraphicObject ) )
+ return false;
+
+ if( ( pGraphicObject && rBullet.pGraphicObject ) &&
+ ( ( *pGraphicObject != *rBullet.pGraphicObject ) ||
+ ( pGraphicObject->GetPrefSize() != rBullet.pGraphicObject->GetPrefSize() ) ) )
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+OUString SvxBulletItem::GetFullText() const
+{
+ return aPrevText + OUStringChar(cSymbol) + aFollowText;
+}
+
+
+bool SvxBulletItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText = GetFullText();
+ return true;
+}
+
+
+const GraphicObject& SvxBulletItem::GetGraphicObject() const
+{
+ if( pGraphicObject )
+ return *pGraphicObject;
+ else
+ {
+ static const GraphicObject aDefaultObject;
+ return aDefaultObject;
+ }
+}
+
+
+void SvxBulletItem::SetGraphicObject( const GraphicObject& rGraphicObject )
+{
+ if( ( GraphicType::NONE == rGraphicObject.GetType() ) || ( GraphicType::Default == rGraphicObject.GetType() ) )
+ {
+ pGraphicObject.reset();
+ }
+ else
+ {
+ pGraphicObject.reset( new GraphicObject( rGraphicObject ) );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/items/charhiddenitem.cxx b/editeng/source/items/charhiddenitem.cxx
new file mode 100644
index 0000000000..ec2a0af3c7
--- /dev/null
+++ b/editeng/source/items/charhiddenitem.cxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <editeng/charhiddenitem.hxx>
+#include <editeng/editrids.hrc>
+#include <editeng/eerdll.hxx>
+#include <unotools/resmgr.hxx>
+
+
+SvxCharHiddenItem::SvxCharHiddenItem( const bool bHidden, const sal_uInt16 nId ) :
+ SfxBoolItem( nId, bHidden )
+{
+}
+
+SvxCharHiddenItem* SvxCharHiddenItem::Clone( SfxItemPool * ) const
+{
+ return new SvxCharHiddenItem( *this );
+}
+
+bool SvxCharHiddenItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText,
+ const IntlWrapper & /*rIntl*/
+) const
+{
+ TranslateId pId = RID_SVXITEMS_CHARHIDDEN_FALSE;
+
+ if ( GetValue() )
+ pId = RID_SVXITEMS_CHARHIDDEN_TRUE;
+ rText = EditResId(pId);
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/items/flditem.cxx b/editeng/source/items/flditem.cxx
new file mode 100644
index 0000000000..b501d40ba9
--- /dev/null
+++ b/editeng/source/items/flditem.cxx
@@ -0,0 +1,935 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <osl/file.hxx>
+#include <utility>
+#include <vcl/metaact.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <tools/urlobj.hxx>
+
+#include <editeng/flditem.hxx>
+#include <editeng/CustomPropertyField.hxx>
+#include <editeng/measfld.hxx>
+#include <editeng/unonames.hxx>
+
+#include <tools/debug.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/text/XTextContent.hpp>
+#include <com/sun/star/text/FilenameDisplayFormat.hpp>
+#include <com/sun/star/util/DateTime.hpp>
+
+using namespace com::sun::star;
+
+SvxFieldData* SvxFieldData::Create(const uno::Reference<text::XTextContent>& xTextContent)
+{
+ uno::Reference<beans::XPropertySet> xPropSet(xTextContent, uno::UNO_QUERY);
+ if (!xPropSet.is())
+ return nullptr;
+
+ // we do not support these fields from Writer, so make sure we do not throw
+ // here - see fdo#63436 how to possibly extend Writer to make use of this
+ uno::Any aAny;
+ try {
+ aAny = xPropSet->getPropertyValue(UNO_TC_PROP_TEXTFIELD_TYPE);
+ if ( !aAny.has<sal_Int32>() )
+ return nullptr;
+
+ sal_Int32 nFieldType = aAny.get<sal_Int32>();
+
+ switch (nFieldType)
+ {
+ case text::textfield::Type::TIME:
+ case text::textfield::Type::EXTENDED_TIME:
+ case text::textfield::Type::DATE:
+ {
+ bool bIsDate = false;
+ xPropSet->getPropertyValue(UNO_TC_PROP_IS_DATE) >>= bIsDate;
+
+ if (bIsDate)
+ {
+ util::DateTime aDateTime = xPropSet->getPropertyValue(UNO_TC_PROP_DATE_TIME).get<util::DateTime>();
+ Date aDate(aDateTime.Day, aDateTime.Month, aDateTime.Year);
+ bool bIsFixed = false;
+ xPropSet->getPropertyValue(UNO_TC_PROP_IS_FIXED) >>= bIsFixed;
+
+ SvxDateField* pData = new SvxDateField(aDate, bIsFixed ? SvxDateType::Fix : SvxDateType::Var);
+ sal_Int32 nNumFmt = -1;
+ xPropSet->getPropertyValue(UNO_TC_PROP_NUMFORMAT) >>= nNumFmt;
+ if (static_cast<SvxDateFormat>(nNumFmt) >= SvxDateFormat::AppDefault &&
+ static_cast<SvxDateFormat>(nNumFmt) <= SvxDateFormat::F)
+ pData->SetFormat(static_cast<SvxDateFormat>(nNumFmt));
+
+ return pData;
+ }
+
+ if (nFieldType != text::textfield::Type::TIME)
+ {
+ util::DateTime aDateTime = xPropSet->getPropertyValue(UNO_TC_PROP_DATE_TIME).get<util::DateTime>();
+ tools::Time aTime(aDateTime);
+
+ bool bIsFixed = false;
+ xPropSet->getPropertyValue(UNO_TC_PROP_IS_FIXED) >>= bIsFixed;
+
+ SvxExtTimeField* pData = new SvxExtTimeField(aTime, bIsFixed ? SvxTimeType::Fix : SvxTimeType::Var);
+
+ sal_Int32 nNumFmt = -1;
+ xPropSet->getPropertyValue(UNO_TC_PROP_NUMFORMAT) >>= nNumFmt;
+ if (static_cast<SvxTimeFormat>(nNumFmt) >= SvxTimeFormat::AppDefault &&
+ static_cast<SvxTimeFormat>(nNumFmt) <= SvxTimeFormat::HH12_MM_SS_00_AMPM)
+ pData->SetFormat(static_cast<SvxTimeFormat>(nNumFmt));
+
+ return pData;
+ }
+
+ return new SvxTimeField();
+ }
+ case text::textfield::Type::URL:
+ {
+ OUString aRep, aTarget, aURL;
+ sal_Int16 nFmt = -1;
+ xPropSet->getPropertyValue(UNO_TC_PROP_URL_REPRESENTATION) >>= aRep;
+ xPropSet->getPropertyValue(UNO_TC_PROP_URL_TARGET) >>= aTarget;
+ xPropSet->getPropertyValue(UNO_TC_PROP_URL) >>= aURL;
+ xPropSet->getPropertyValue(UNO_TC_PROP_URL_FORMAT) >>= nFmt;
+ SvxURLField* pData = new SvxURLField(aURL, aRep, aRep.isEmpty() ? SvxURLFormat::Url : SvxURLFormat::Repr);
+ pData->SetTargetFrame(aTarget);
+ if (static_cast<SvxURLFormat>(nFmt) >= SvxURLFormat::AppDefault &&
+ static_cast<SvxURLFormat>(nFmt) <= SvxURLFormat::Repr)
+ pData->SetFormat(static_cast<SvxURLFormat>(nFmt));
+
+ return pData;
+ }
+ case text::textfield::Type::PAGE:
+ return new SvxPageField();
+ case text::textfield::Type::PAGES:
+ return new SvxPagesField();
+ case text::textfield::Type::PAGE_NAME:
+ return new SvxPageTitleField();
+ case text::textfield::Type::DOCINFO_TITLE:
+ return new SvxFileField();
+ case text::textfield::Type::TABLE:
+ {
+ sal_Int32 nTab = 0;
+ xPropSet->getPropertyValue(UNO_TC_PROP_TABLE_POSITION) >>= nTab;
+ return new SvxTableField(nTab);
+ }
+ case text::textfield::Type::EXTENDED_FILE:
+ {
+ OUString aPresentation;
+ bool bIsFixed = false;
+ sal_Int16 nFmt = text::FilenameDisplayFormat::FULL;
+ xPropSet->getPropertyValue(UNO_TC_PROP_IS_FIXED) >>= bIsFixed;
+ xPropSet->getPropertyValue(UNO_TC_PROP_CURRENT_PRESENTATION) >>= aPresentation;
+ xPropSet->getPropertyValue(UNO_TC_PROP_FILE_FORMAT) >>= nFmt;
+
+ SvxFileFormat eFmt = SvxFileFormat::NameAndExt;
+ switch (nFmt)
+ {
+ case text::FilenameDisplayFormat::FULL: eFmt = SvxFileFormat::PathFull; break;
+ case text::FilenameDisplayFormat::PATH: eFmt = SvxFileFormat::PathOnly; break;
+ case text::FilenameDisplayFormat::NAME: eFmt = SvxFileFormat::NameOnly; break;
+ default:;
+ }
+
+ // pass fixed attribute to constructor
+ return new SvxExtFileField(
+ aPresentation, bIsFixed ? SvxFileType::Fix : SvxFileType::Var, eFmt);
+ }
+ case text::textfield::Type::AUTHOR:
+ {
+ bool bIsFixed = false;
+ bool bFullName = false;
+ sal_Int16 nFmt = -1;
+ OUString aPresentation, aContent, aFirstName, aLastName;
+ xPropSet->getPropertyValue(UNO_TC_PROP_IS_FIXED) >>= bIsFixed;
+ xPropSet->getPropertyValue(UNO_TC_PROP_AUTHOR_FULLNAME) >>= bFullName;
+ xPropSet->getPropertyValue(UNO_TC_PROP_CURRENT_PRESENTATION) >>= aPresentation;
+ xPropSet->getPropertyValue(UNO_TC_PROP_AUTHOR_CONTENT) >>= aContent;
+ xPropSet->getPropertyValue(UNO_TC_PROP_AUTHOR_FORMAT) >>= nFmt;
+
+ // do we have CurrentPresentation given? Mimic behaviour of
+ // writer, which means: prefer CurrentPresentation over Content
+ // if both are given.
+ if (!aPresentation.isEmpty())
+ aContent = aPresentation;
+
+ sal_Int32 nPos = aContent.lastIndexOf(' ', 0);
+ if (nPos > 0)
+ {
+ aFirstName = aContent.copy(0, nPos);
+ aLastName = aContent.copy(nPos + 1);
+ }
+ else
+ {
+ aLastName = aContent;
+ }
+
+ // #92009# pass fixed attribute to constructor
+ SvxAuthorField* pData = new SvxAuthorField(
+ aFirstName, aLastName, OUString(), bIsFixed ? SvxAuthorType::Fix : SvxAuthorType::Var);
+
+ if (!bIsFixed)
+ {
+ if (!bFullName)
+ {
+ pData->SetFormat(SvxAuthorFormat::ShortName);
+ }
+ else if (static_cast<SvxAuthorFormat>(nFmt) >= SvxAuthorFormat::FullName &&
+ static_cast<SvxAuthorFormat>(nFmt) <= SvxAuthorFormat::ShortName)
+ {
+ pData->SetFormat(static_cast<SvxAuthorFormat>(nFmt));
+ }
+ }
+
+ return pData;
+ }
+ case text::textfield::Type::MEASURE:
+ {
+ SdrMeasureFieldKind eKind = SdrMeasureFieldKind::Value;
+ sal_Int16 nTmp = -1;
+ xPropSet->getPropertyValue(UNO_TC_PROP_MEASURE_KIND) >>= nTmp;
+ if (nTmp == static_cast<sal_Int16>(SdrMeasureFieldKind::Unit) ||
+ nTmp == static_cast<sal_Int16>(SdrMeasureFieldKind::Rotate90Blanks))
+ eKind = static_cast<SdrMeasureFieldKind>(nTmp);
+
+ return new SdrMeasureField(eKind);
+ }
+ case text::textfield::Type::PRESENTATION_HEADER:
+ return new SvxHeaderField();
+ case text::textfield::Type::PRESENTATION_FOOTER:
+ return new SvxFooterField();
+ case text::textfield::Type::PRESENTATION_DATE_TIME:
+ return new SvxDateTimeField();
+ case text::textfield::Type::DOCINFO_CUSTOM:
+ {
+ OUString sName;
+ xPropSet->getPropertyValue(UNO_TC_PROP_NAME) >>= sName;
+
+ OUString sCurrentPresentation;
+ xPropSet->getPropertyValue(UNO_TC_PROP_CURRENT_PRESENTATION) >>= sCurrentPresentation;
+
+ return new editeng::CustomPropertyField(sName, sCurrentPresentation);
+ }
+ default:
+ ;
+ };
+ } catch ( const beans::UnknownPropertyException& )
+ {
+ return nullptr;
+ }
+
+ return nullptr;
+}
+
+
+SvxFieldData::SvxFieldData()
+{
+}
+
+
+SvxFieldData::~SvxFieldData()
+{
+}
+
+
+std::unique_ptr<SvxFieldData> SvxFieldData::Clone() const
+{
+ return std::make_unique<SvxFieldData>();
+}
+
+
+bool SvxFieldData::operator==( const SvxFieldData& rFld ) const
+{
+ DBG_ASSERT( typeid(*this) == typeid(rFld), "==: Different Types" );
+ (void)rFld;
+ return true; // Basic class is always the same.
+}
+
+
+MetaAction* SvxFieldData::createBeginComment() const
+{
+ return new MetaCommentAction( "FIELD_SEQ_BEGIN"_ostr );
+}
+
+MetaAction* SvxFieldData::createEndComment()
+{
+ return new MetaCommentAction( "FIELD_SEQ_END"_ostr );
+}
+
+
+SvxFieldItem::SvxFieldItem( std::unique_ptr<SvxFieldData> pField, const sal_uInt16 nId ) :
+ SfxPoolItem( nId )
+ , mpField( std::move(pField) )
+{
+}
+
+SvxFieldItem::SvxFieldItem( const SvxFieldData& rField, const sal_uInt16 nId ) :
+ SfxPoolItem( nId )
+ , mpField( rField.Clone() )
+{
+}
+
+
+SvxFieldItem::SvxFieldItem( const SvxFieldItem& rItem ) :
+ SfxPoolItem ( rItem )
+ , mpField( rItem.mpField ? rItem.mpField->Clone() : nullptr )
+{
+}
+
+SvxFieldItem::~SvxFieldItem()
+{
+}
+
+SvxFieldItem* SvxFieldItem::Clone( SfxItemPool* ) const
+{
+ return new SvxFieldItem(*this);
+}
+
+bool SvxFieldItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+
+ const SvxFieldData* pOtherFld = static_cast<const SvxFieldItem&>(rItem).GetField();
+ if( mpField.get() == pOtherFld )
+ return true;
+ if( mpField == nullptr || pOtherFld == nullptr )
+ return false;
+ return ( typeid(*mpField) == typeid(*pOtherFld) )
+ && ( *mpField == *pOtherFld );
+}
+
+
+// The following are the derivatives of SvxFieldData ...
+
+
+SvxDateField::SvxDateField()
+{
+ nFixDate = Date( Date::SYSTEM ).GetDate();
+ eType = SvxDateType::Var;
+ eFormat = SvxDateFormat::StdSmall;
+}
+
+
+SvxDateField::SvxDateField( const Date& rDate, SvxDateType eT, SvxDateFormat eF )
+{
+ nFixDate = rDate.GetDate();
+ eType = eT;
+ eFormat = eF;
+}
+
+
+std::unique_ptr<SvxFieldData> SvxDateField::Clone() const
+{
+ return std::make_unique<SvxDateField>( *this );
+}
+
+
+bool SvxDateField::operator==( const SvxFieldData& rOther ) const
+{
+ if ( typeid(rOther) != typeid(*this) )
+ return false;
+
+ const SvxDateField& rOtherFld = static_cast<const SvxDateField&>(rOther);
+ return ( ( nFixDate == rOtherFld.nFixDate ) &&
+ ( eType == rOtherFld.eType ) &&
+ ( eFormat == rOtherFld.eFormat ) );
+}
+
+
+
+OUString SvxDateField::GetFormatted( SvNumberFormatter& rFormatter, LanguageType eLang ) const
+{
+ Date aDate( Date::EMPTY );
+ if ( eType == SvxDateType::Fix )
+ aDate.SetDate( nFixDate );
+ else
+ aDate = Date( Date::SYSTEM ); // current date
+
+ return GetFormatted( aDate, eFormat, rFormatter, eLang );
+}
+
+OUString SvxDateField::GetFormatted( Date const & aDate, SvxDateFormat eFormat, SvNumberFormatter& rFormatter, LanguageType eLang )
+{
+ if ( eFormat == SvxDateFormat::System )
+ {
+ OSL_FAIL( "SvxDateFormat::System not implemented!" );
+ eFormat = SvxDateFormat::StdSmall;
+ }
+ else if ( eFormat == SvxDateFormat::AppDefault )
+ {
+ OSL_FAIL( "SvxDateFormat::AppDefault: take them from where? ");
+ eFormat = SvxDateFormat::StdSmall;
+ }
+
+ sal_uInt32 nFormatKey;
+
+ switch( eFormat )
+ {
+ case SvxDateFormat::StdSmall:
+ // short
+ nFormatKey = rFormatter.GetFormatIndex( NF_DATE_SYSTEM_SHORT, eLang );
+ break;
+ case SvxDateFormat::StdBig:
+ // long
+ nFormatKey = rFormatter.GetFormatIndex( NF_DATE_SYSTEM_LONG, eLang );
+ break;
+ case SvxDateFormat::A:
+ // 13.02.96
+ nFormatKey = rFormatter.GetFormatIndex( NF_DATE_SYS_DDMMYY, eLang );
+ break;
+ case SvxDateFormat::B:
+ // 13.02.1996
+ nFormatKey = rFormatter.GetFormatIndex( NF_DATE_SYS_DDMMYYYY, eLang );
+ break;
+ case SvxDateFormat::C:
+ // 13. Feb 1996
+ nFormatKey = rFormatter.GetFormatIndex( NF_DATE_SYS_DMMMYYYY, eLang );
+ break;
+ case SvxDateFormat::D:
+ // 13. February 1996
+ nFormatKey = rFormatter.GetFormatIndex( NF_DATE_SYS_DMMMMYYYY, eLang );
+ break;
+ case SvxDateFormat::E:
+ // The, 13. February 1996
+ nFormatKey = rFormatter.GetFormatIndex( NF_DATE_SYS_NNDMMMMYYYY, eLang );
+ break;
+ case SvxDateFormat::F:
+ // Tuesday, 13. February 1996
+ nFormatKey = rFormatter.GetFormatIndex( NF_DATE_SYS_NNNNDMMMMYYYY, eLang );
+ break;
+ default:
+ nFormatKey = rFormatter.GetStandardFormat( SvNumFormatType::DATE, eLang );
+ }
+
+ double fDiffDate = aDate - rFormatter.GetNullDate();
+ OUString aStr;
+ const Color* pColor = nullptr;
+ rFormatter.GetOutputString( fDiffDate, nFormatKey, aStr, &pColor );
+ return aStr;
+}
+
+MetaAction* SvxDateField::createBeginComment() const
+{
+ return new MetaCommentAction( "FIELD_SEQ_BEGIN"_ostr );
+}
+
+SvxURLField::SvxURLField()
+{
+ eFormat = SvxURLFormat::Url;
+}
+
+
+SvxURLField::SvxURLField( OUString _aURL, OUString aRepres, SvxURLFormat eFmt )
+ : aURL(std::move( _aURL )), aRepresentation(std::move( aRepres ))
+{
+ eFormat = eFmt;
+}
+
+
+std::unique_ptr<SvxFieldData> SvxURLField::Clone() const
+{
+ return std::make_unique<SvxURLField>( *this );
+}
+
+
+bool SvxURLField::operator==( const SvxFieldData& rOther ) const
+{
+ if ( typeid(rOther) != typeid(*this) )
+ return false;
+
+ const SvxURLField& rOtherFld = static_cast<const SvxURLField&>(rOther);
+ return ( ( eFormat == rOtherFld.eFormat ) &&
+ ( aURL == rOtherFld.aURL ) &&
+ ( aRepresentation == rOtherFld.aRepresentation ) &&
+ ( aTargetFrame == rOtherFld.aTargetFrame ) );
+}
+
+
+MetaAction* SvxURLField::createBeginComment() const
+{
+ // #i46618# Adding target URL to metafile comment
+ return new MetaCommentAction( "FIELD_SEQ_BEGIN"_ostr,
+ 0,
+ reinterpret_cast<const sal_uInt8*>(aURL.getStr()),
+ 2*aURL.getLength() );
+}
+
+//
+// SvxPageTitleField methods
+//
+
+SvxPageTitleField::SvxPageTitleField() {}
+
+std::unique_ptr<SvxFieldData> SvxPageTitleField::Clone() const
+{
+ return std::make_unique<SvxPageTitleField>();
+}
+
+bool SvxPageTitleField::operator==( const SvxFieldData& rCmp ) const
+{
+ return ( dynamic_cast< const SvxPageTitleField *>(&rCmp) != nullptr );
+}
+
+MetaAction* SvxPageTitleField::createBeginComment() const
+{
+ return new MetaCommentAction( "FIELD_SEQ_BEGIN;PageTitleField"_ostr );
+}
+
+//
+// SvxPagesField
+//
+// The fields that were removed from Calc:
+
+
+SvxPageField::SvxPageField() {}
+
+std::unique_ptr<SvxFieldData> SvxPageField::Clone() const
+{
+ return std::make_unique<SvxPageField>(); // empty
+}
+
+bool SvxPageField::operator==( const SvxFieldData& rCmp ) const
+{
+ return ( dynamic_cast< const SvxPageField *>(&rCmp) != nullptr );
+}
+
+MetaAction* SvxPageField::createBeginComment() const
+{
+ return new MetaCommentAction( "FIELD_SEQ_BEGIN;PageField"_ostr );
+}
+
+
+SvxPagesField::SvxPagesField() {}
+
+std::unique_ptr<SvxFieldData> SvxPagesField::Clone() const
+{
+ return std::make_unique<SvxPagesField>(); // empty
+}
+
+bool SvxPagesField::operator==( const SvxFieldData& rCmp ) const
+{
+ return ( dynamic_cast< const SvxPagesField *>(&rCmp) != nullptr);
+}
+
+SvxTimeField::SvxTimeField() {}
+
+std::unique_ptr<SvxFieldData> SvxTimeField::Clone() const
+{
+ return std::make_unique<SvxTimeField>(); // empty
+}
+
+bool SvxTimeField::operator==( const SvxFieldData& rCmp ) const
+{
+ return ( dynamic_cast< const SvxTimeField *>(&rCmp) != nullptr);
+}
+
+MetaAction* SvxTimeField::createBeginComment() const
+{
+ return new MetaCommentAction( "FIELD_SEQ_BEGIN"_ostr );
+}
+
+SvxFileField::SvxFileField() {}
+
+std::unique_ptr<SvxFieldData> SvxFileField::Clone() const
+{
+ return std::make_unique<SvxFileField>(); // empty
+}
+
+bool SvxFileField::operator==( const SvxFieldData& rCmp ) const
+{
+ return ( dynamic_cast< const SvxFileField *>(&rCmp) != nullptr );
+}
+
+SvxTableField::SvxTableField() : mnTab(0) {}
+
+SvxTableField::SvxTableField(int nTab) : mnTab(nTab) {}
+
+void SvxTableField::SetTab(int nTab)
+{
+ mnTab = nTab;
+}
+
+
+std::unique_ptr<SvxFieldData> SvxTableField::Clone() const
+{
+ return std::make_unique<SvxTableField>(mnTab);
+}
+
+bool SvxTableField::operator==( const SvxFieldData& rCmp ) const
+{
+ if (dynamic_cast<const SvxTableField *>(&rCmp) == nullptr)
+ return false;
+
+ return mnTab == static_cast<const SvxTableField&>(rCmp).mnTab;
+}
+
+// SvxExtTimeField
+
+
+SvxExtTimeField::SvxExtTimeField()
+ : m_nFixTime( tools::Time(tools::Time::SYSTEM).GetTime() )
+{
+ eType = SvxTimeType::Var;
+ eFormat = SvxTimeFormat::Standard;
+}
+
+
+SvxExtTimeField::SvxExtTimeField( const tools::Time& rTime, SvxTimeType eT, SvxTimeFormat eF )
+ : m_nFixTime( rTime.GetTime() )
+{
+ eType = eT;
+ eFormat = eF;
+}
+
+
+std::unique_ptr<SvxFieldData> SvxExtTimeField::Clone() const
+{
+ return std::make_unique<SvxExtTimeField>( *this );
+}
+
+
+bool SvxExtTimeField::operator==( const SvxFieldData& rOther ) const
+{
+ if ( typeid(rOther) != typeid(*this) )
+ return false;
+
+ const SvxExtTimeField& rOtherFld = static_cast<const SvxExtTimeField&>(rOther);
+ return ((m_nFixTime == rOtherFld.m_nFixTime) &&
+ ( eType == rOtherFld.eType ) &&
+ ( eFormat == rOtherFld.eFormat ) );
+}
+
+
+OUString SvxExtTimeField::GetFormatted( SvNumberFormatter& rFormatter, LanguageType eLang ) const
+{
+ tools::Time aTime( tools::Time::EMPTY );
+ if ( eType == SvxTimeType::Fix )
+ aTime.SetTime(m_nFixTime);
+ else
+ aTime = tools::Time( tools::Time::SYSTEM ); // current time
+ return GetFormatted( aTime, eFormat, rFormatter, eLang );
+}
+
+OUString SvxExtTimeField::GetFormatted( tools::Time const & aTime, SvxTimeFormat eFormat, SvNumberFormatter& rFormatter, LanguageType eLang )
+{
+ switch( eFormat )
+ {
+ case SvxTimeFormat::System :
+ OSL_FAIL( "SvxTimeFormat::System: not implemented" );
+ eFormat = SvxTimeFormat::Standard;
+ break;
+ case SvxTimeFormat::AppDefault :
+ OSL_FAIL( "SvxTimeFormat::AppDefault: not implemented" );
+ eFormat = SvxTimeFormat::Standard;
+ break;
+ default: ;//prevent warning
+ }
+
+ sal_uInt32 nFormatKey;
+
+ switch( eFormat )
+ {
+ case SvxTimeFormat::HH12_MM:
+ nFormatKey = rFormatter.GetFormatIndex( NF_TIME_HHMMAMPM, eLang );
+ break;
+ case SvxTimeFormat::HH12_MM_SS_00:
+ {
+ // no builtin format available, try to insert or reuse
+ OUString aFormatCode( "HH:MM:SS.00 AM/PM" );
+ sal_Int32 nCheckPos;
+ SvNumFormatType nType;
+ rFormatter.PutandConvertEntry( aFormatCode, nCheckPos, nType,
+ nFormatKey, LANGUAGE_ENGLISH_US, eLang, true);
+ DBG_ASSERT( nCheckPos == 0, "SvxTimeFormat::HH12_MM_SS_00: could not insert format code" );
+ if ( nCheckPos )
+ {
+ nFormatKey = rFormatter.GetFormatIndex( NF_TIME_HH_MMSS00, eLang );
+ }
+ break;
+ }
+ case SvxTimeFormat::HH24_MM:
+ nFormatKey = rFormatter.GetFormatIndex( NF_TIME_HHMM, eLang );
+ break;
+ case SvxTimeFormat::HH24_MM_SS_00:
+ nFormatKey = rFormatter.GetFormatIndex( NF_TIME_HH_MMSS00, eLang );
+ break;
+ case SvxTimeFormat::HH12_MM_SS:
+ nFormatKey = rFormatter.GetFormatIndex( NF_TIME_HHMMSSAMPM, eLang );
+ break;
+ case SvxTimeFormat::HH24_MM_SS:
+ nFormatKey = rFormatter.GetFormatIndex( NF_TIME_HHMMSS, eLang );
+ break;
+ case SvxTimeFormat::Standard:
+ default:
+ nFormatKey = rFormatter.GetStandardFormat( SvNumFormatType::TIME, eLang );
+ }
+
+ double fFracTime = aTime.GetTimeInDays();
+ OUString aStr;
+ const Color* pColor = nullptr;
+ rFormatter.GetOutputString( fFracTime, nFormatKey, aStr, &pColor );
+ return aStr;
+}
+
+MetaAction* SvxExtTimeField::createBeginComment() const
+{
+ return new MetaCommentAction( "FIELD_SEQ_BEGIN"_ostr );
+}
+
+
+// SvxExtFileField
+
+
+SvxExtFileField::SvxExtFileField()
+{
+ eType = SvxFileType::Var;
+ eFormat = SvxFileFormat::PathFull;
+}
+
+
+SvxExtFileField::SvxExtFileField( const OUString& rStr, SvxFileType eT, SvxFileFormat eF )
+{
+ aFile = rStr;
+ eType = eT;
+ eFormat = eF;
+}
+
+
+std::unique_ptr<SvxFieldData> SvxExtFileField::Clone() const
+{
+ return std::make_unique<SvxExtFileField>( *this );
+}
+
+
+bool SvxExtFileField::operator==( const SvxFieldData& rOther ) const
+{
+ if ( typeid(rOther) != typeid(*this) )
+ return false;
+
+ const SvxExtFileField& rOtherFld = static_cast<const SvxExtFileField&>(rOther);
+ return ( ( aFile == rOtherFld.aFile ) &&
+ ( eType == rOtherFld.eType ) &&
+ ( eFormat == rOtherFld.eFormat ) );
+}
+
+
+OUString SvxExtFileField::GetFormatted() const
+{
+ OUString aString;
+
+ INetURLObject aURLObj( aFile );
+
+ if( INetProtocol::NotValid == aURLObj.GetProtocol() )
+ {
+ // invalid? try to interpret string as system file name
+ OUString aURLStr;
+
+ osl::FileBase::getFileURLFromSystemPath( aFile, aURLStr );
+
+ aURLObj.SetURL( aURLStr );
+ }
+
+ // #92009# Be somewhat liberate when trying to
+ // get formatted content out of the FileField
+ if( INetProtocol::NotValid == aURLObj.GetProtocol() )
+ {
+ // still not valid? Then output as is
+ aString = aFile;
+ }
+ else if( INetProtocol::File == aURLObj.GetProtocol() )
+ {
+ switch( eFormat )
+ {
+ case SvxFileFormat::PathFull:
+ aString = aURLObj.getFSysPath(FSysStyle::Detect);
+ break;
+
+ case SvxFileFormat::PathOnly:
+ aURLObj.removeSegment(INetURLObject::LAST_SEGMENT, false);
+ // #101742# Leave trailing slash at the pathname
+ aURLObj.setFinalSlash();
+ aString = aURLObj.getFSysPath(FSysStyle::Detect);
+ break;
+
+ case SvxFileFormat::NameOnly:
+ aString = aURLObj.getBase(INetURLObject::LAST_SEGMENT,true,INetURLObject::DecodeMechanism::Unambiguous);
+ break;
+
+ case SvxFileFormat::NameAndExt:
+ aString = aURLObj.getName(INetURLObject::LAST_SEGMENT,true,INetURLObject::DecodeMechanism::Unambiguous);
+ break;
+ }
+ }
+ else
+ {
+ switch( eFormat )
+ {
+ case SvxFileFormat::PathFull:
+ aString = aURLObj.GetMainURL( INetURLObject::DecodeMechanism::ToIUri );
+ break;
+
+ case SvxFileFormat::PathOnly:
+ aURLObj.removeSegment(INetURLObject::LAST_SEGMENT, false);
+ // #101742# Leave trailing slash at the pathname
+ aURLObj.setFinalSlash();
+ aString = aURLObj.GetMainURL( INetURLObject::DecodeMechanism::ToIUri );
+ break;
+
+ case SvxFileFormat::NameOnly:
+ aString = aURLObj.getBase();
+ break;
+
+ case SvxFileFormat::NameAndExt:
+ aString = aURLObj.getName();
+ break;
+ }
+ }
+
+ return aString;
+}
+
+
+// SvxAuthorField
+
+
+SvxAuthorField::SvxAuthorField( const OUString& rFirstName,
+ const OUString& rLastName,
+ const OUString& rShortName,
+ SvxAuthorType eT, SvxAuthorFormat eF )
+{
+ aName = rLastName;
+ aFirstName = rFirstName;
+ aShortName = rShortName;
+ eType = eT;
+ eFormat = eF;
+}
+
+
+std::unique_ptr<SvxFieldData> SvxAuthorField::Clone() const
+{
+ return std::make_unique<SvxAuthorField>( *this );
+}
+
+
+bool SvxAuthorField::operator==( const SvxFieldData& rOther ) const
+{
+ if ( typeid(rOther) != typeid(*this) )
+ return false;
+
+ const SvxAuthorField& rOtherFld = static_cast<const SvxAuthorField&>(rOther);
+ return ( ( aName == rOtherFld.aName ) &&
+ ( aFirstName == rOtherFld.aFirstName ) &&
+ ( aShortName == rOtherFld.aShortName ) &&
+ ( eType == rOtherFld.eType ) &&
+ ( eFormat == rOtherFld.eFormat ) );
+}
+
+
+OUString SvxAuthorField::GetFormatted() const
+{
+ OUString aString;
+
+ switch( eFormat )
+ {
+ case SvxAuthorFormat::FullName:
+ aString = aFirstName + " " + aName;
+ break;
+ case SvxAuthorFormat::LastName:
+ aString = aName;
+ break;
+
+ case SvxAuthorFormat::FirstName:
+ aString = aFirstName;
+ break;
+
+ case SvxAuthorFormat::ShortName:
+ aString = aShortName;
+ break;
+ }
+
+ return aString;
+}
+
+SvxHeaderField::SvxHeaderField() {}
+
+std::unique_ptr<SvxFieldData> SvxHeaderField::Clone() const
+{
+ return std::make_unique<SvxHeaderField>(); // empty
+}
+
+bool SvxHeaderField::operator==( const SvxFieldData& rCmp ) const
+{
+ return ( dynamic_cast< const SvxHeaderField *>(&rCmp) != nullptr );
+}
+
+SvxFooterField::SvxFooterField() {}
+
+std::unique_ptr<SvxFieldData> SvxFooterField::Clone() const
+{
+ return std::make_unique<SvxFooterField>(); // empty
+}
+
+bool SvxFooterField::operator==( const SvxFieldData& rCmp ) const
+{
+ return ( dynamic_cast< const SvxFooterField *>(&rCmp) != nullptr );
+}
+
+std::unique_ptr<SvxFieldData> SvxDateTimeField::Clone() const
+{
+ return std::make_unique<SvxDateTimeField>(); // empty
+}
+
+bool SvxDateTimeField::operator==( const SvxFieldData& rCmp ) const
+{
+ return ( dynamic_cast< const SvxDateTimeField *>(&rCmp) != nullptr );
+}
+
+SvxDateTimeField::SvxDateTimeField() {}
+
+OUString SvxDateTimeField::GetFormatted(
+ Date const & rDate, tools::Time const & rTime,
+ SvxDateFormat eDateFormat, SvxTimeFormat eTimeFormat,
+ SvNumberFormatter& rFormatter, LanguageType eLanguage )
+{
+ OUString aRet;
+
+ if(eDateFormat != SvxDateFormat::AppDefault)
+ {
+ aRet = SvxDateField::GetFormatted( rDate, eDateFormat, rFormatter, eLanguage );
+ }
+
+ if(eTimeFormat != SvxTimeFormat::AppDefault)
+ {
+ OUStringBuffer aBuf(aRet);
+
+ if (!aRet.isEmpty())
+ aBuf.append(' ');
+
+ aBuf.append(
+ SvxExtTimeField::GetFormatted(rTime, eTimeFormat, rFormatter, eLanguage));
+
+ aRet = aBuf.makeStringAndClear();
+ }
+
+ return aRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/items/frmitems.cxx b/editeng/source/items/frmitems.cxx
new file mode 100644
index 0000000000..94b7704303
--- /dev/null
+++ b/editeng/source/items/frmitems.cxx
@@ -0,0 +1,4709 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http: // mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http: // www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/drawing/LineStyle.hpp>
+#include <com/sun/star/script/Converter.hpp>
+#include <com/sun/star/table/ShadowLocation.hpp>
+#include <com/sun/star/table/ShadowFormat.hpp>
+#include <com/sun/star/table/BorderLine2.hpp>
+#include <com/sun/star/table/BorderLineStyle.hpp>
+#include <com/sun/star/style/BreakType.hpp>
+#include <com/sun/star/style/GraphicLocation.hpp>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/text/WritingMode2.hpp>
+#include <com/sun/star/frame/status/UpperLowerMarginScale.hpp>
+#include <com/sun/star/frame/status/LeftRightMarginScale.hpp>
+#include <com/sun/star/drawing/ShadingPattern.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/util/XComplexColor.hpp>
+
+#include <osl/diagnose.h>
+#include <i18nutil/unicode.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <utility>
+#include <vcl/GraphicObject.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/bigint.hxx>
+#include <svl/memberid.h>
+#include <rtl/math.hxx>
+#include <rtl/ustring.hxx>
+#include <tools/mapunit.hxx>
+#include <tools/UnitConversion.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <editeng/editrids.hrc>
+#include <editeng/pbinitem.hxx>
+#include <editeng/sizeitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/prntitem.hxx>
+#include <editeng/opaqitem.hxx>
+#include <editeng/protitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <editeng/keepitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/itemtype.hxx>
+#include <editeng/eerdll.hxx>
+#include <editeng/memberids.h>
+#include <libxml/xmlwriter.h>
+#include <o3tl/enumrange.hxx>
+#include <o3tl/safeint.hxx>
+#include <sal/log.hxx>
+#include <vcl/GraphicLoader.hxx>
+#include <unotools/securityoptions.hxx>
+#include <docmodel/uno/UnoComplexColor.hxx>
+
+#include <boost/property_tree/ptree.hpp>
+
+using namespace ::editeng;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::drawing;
+using namespace ::com::sun::star::table::BorderLineStyle;
+
+
+SfxPoolItem* SvxPaperBinItem::CreateDefault() { return new SvxPaperBinItem(0);}
+SfxPoolItem* SvxSizeItem::CreateDefault() { return new SvxSizeItem(0);}
+SfxPoolItem* SvxLRSpaceItem::CreateDefault() { return new SvxLRSpaceItem(0);}
+SfxPoolItem* SvxULSpaceItem::CreateDefault() { return new SvxULSpaceItem(0);}
+SfxPoolItem* SvxProtectItem::CreateDefault() { return new SvxProtectItem(0);}
+SfxPoolItem* SvxBrushItem::CreateDefault() { return new SvxBrushItem(0);}
+SfxPoolItem* SvxShadowItem::CreateDefault() { return new SvxShadowItem(0);}
+SfxPoolItem* SvxBoxItem::CreateDefault() { return new SvxBoxItem(0);}
+SfxPoolItem* SvxBoxInfoItem::CreateDefault() { return new SvxBoxInfoItem(0);}
+SfxPoolItem* SvxFormatBreakItem::CreateDefault() { return new SvxFormatBreakItem(SvxBreak::NONE, 0);}
+SfxPoolItem* SvxFormatKeepItem::CreateDefault() { return new SvxFormatKeepItem(false, 0);}
+SfxPoolItem* SvxLineItem::CreateDefault() { return new SvxLineItem(0);}
+
+SvxPaperBinItem* SvxPaperBinItem::Clone( SfxItemPool* ) const
+{
+ return new SvxPaperBinItem( *this );
+}
+
+bool SvxPaperBinItem::GetPresentation
+(
+ SfxItemPresentation ePres,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ switch ( ePres )
+ {
+ case SfxItemPresentation::Nameless:
+ rText = OUString::number( GetValue() );
+ return true;
+
+ case SfxItemPresentation::Complete:
+ {
+ sal_uInt8 nValue = GetValue();
+
+ if ( PAPERBIN_PRINTER_SETTINGS == nValue )
+ rText = EditResId(RID_SVXSTR_PAPERBIN_SETTINGS);
+ else
+ {
+ rText = EditResId(RID_SVXSTR_PAPERBIN) + " " + OUString::number( nValue );
+ }
+ return true;
+ }
+ //no break necessary
+ default: ; //prevent warning
+ }
+
+ return false;
+}
+
+
+SvxSizeItem::SvxSizeItem( const sal_uInt16 nId, const Size& rSize ) :
+
+ SfxPoolItem( nId ),
+
+ m_aSize( rSize )
+{
+}
+
+
+bool SvxSizeItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ bool bConvert = 0!=(nMemberId&CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+
+ awt::Size aTmp(m_aSize.Width(), m_aSize.Height());
+ if( bConvert )
+ {
+ aTmp.Height = convertTwipToMm100(aTmp.Height);
+ aTmp.Width = convertTwipToMm100(aTmp.Width);
+ }
+
+ switch( nMemberId )
+ {
+ case MID_SIZE_SIZE: rVal <<= aTmp; break;
+ case MID_SIZE_WIDTH: rVal <<= aTmp.Width; break;
+ case MID_SIZE_HEIGHT: rVal <<= aTmp.Height; break;
+ default: OSL_FAIL("Wrong MemberId!"); return false;
+ }
+
+ return true;
+}
+
+
+bool SvxSizeItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ bool bConvert = 0!=(nMemberId&CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+
+ switch( nMemberId )
+ {
+ case MID_SIZE_SIZE:
+ {
+ awt::Size aTmp;
+ if( rVal >>= aTmp )
+ {
+ if(bConvert)
+ {
+ aTmp.Height = o3tl::toTwips(aTmp.Height, o3tl::Length::mm100);
+ aTmp.Width = o3tl::toTwips(aTmp.Width, o3tl::Length::mm100);
+ }
+ m_aSize = Size( aTmp.Width, aTmp.Height );
+ }
+ else
+ {
+ return false;
+ }
+ }
+ break;
+ case MID_SIZE_WIDTH:
+ {
+ sal_Int32 nVal = 0;
+ if(!(rVal >>= nVal ))
+ return false;
+
+ m_aSize.setWidth( bConvert ? o3tl::toTwips(nVal, o3tl::Length::mm100) : nVal );
+ }
+ break;
+ case MID_SIZE_HEIGHT:
+ {
+ sal_Int32 nVal = 0;
+ if(!(rVal >>= nVal))
+ return true;
+
+ m_aSize.setHeight( bConvert ? o3tl::toTwips(nVal, o3tl::Length::mm100) : nVal );
+ }
+ break;
+ default: OSL_FAIL("Wrong MemberId!");
+ return false;
+ }
+ return true;
+}
+
+
+SvxSizeItem::SvxSizeItem( const sal_uInt16 nId ) :
+
+ SfxPoolItem( nId )
+{
+}
+
+
+bool SvxSizeItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ return ( m_aSize == static_cast<const SvxSizeItem&>( rAttr ).GetSize() );
+}
+
+SvxSizeItem* SvxSizeItem::Clone( SfxItemPool* ) const
+{
+ return new SvxSizeItem( *this );
+}
+
+bool SvxSizeItem::GetPresentation
+(
+ SfxItemPresentation ePres,
+ MapUnit eCoreUnit,
+ MapUnit ePresUnit,
+ OUString& rText, const IntlWrapper& rIntl
+) const
+{
+ OUString cpDelimTmp(cpDelim);
+ switch ( ePres )
+ {
+ case SfxItemPresentation::Nameless:
+ rText = GetMetricText( m_aSize.Width(), eCoreUnit, ePresUnit, &rIntl ) +
+ cpDelimTmp +
+ GetMetricText( m_aSize.Height(), eCoreUnit, ePresUnit, &rIntl );
+ return true;
+
+ case SfxItemPresentation::Complete:
+ rText = EditResId(RID_SVXITEMS_SIZE_WIDTH) +
+ GetMetricText( m_aSize.Width(), eCoreUnit, ePresUnit, &rIntl ) +
+ " " + EditResId(GetMetricId(ePresUnit)) +
+ cpDelimTmp +
+ EditResId(RID_SVXITEMS_SIZE_HEIGHT) +
+ GetMetricText( m_aSize.Height(), eCoreUnit, ePresUnit, &rIntl ) +
+ " " + EditResId(GetMetricId(ePresUnit));
+ return true;
+ // no break necessary
+ default: ; // prevent warning
+
+ }
+ return false;
+}
+
+
+void SvxSizeItem::ScaleMetrics( tools::Long nMult, tools::Long nDiv )
+{
+ m_aSize.setWidth( BigInt::Scale( m_aSize.Width(), nMult, nDiv ) );
+ m_aSize.setHeight( BigInt::Scale( m_aSize.Height(), nMult, nDiv ) );
+}
+
+
+bool SvxSizeItem::HasMetrics() const
+{
+ return true;
+}
+
+
+SvxLRSpaceItem::SvxLRSpaceItem(const sal_uInt16 nId)
+ : SfxPoolItem(nId)
+ , nFirstLineOffset(0)
+ , nLeftMargin(0)
+ , nRightMargin(0)
+ , m_nGutterMargin(0)
+ , m_nRightGutterMargin(0),
+ nPropFirstLineOffset( 100 ),
+ nPropLeftMargin( 100 ),
+ nPropRightMargin( 100 ),
+ bAutoFirst ( false ),
+ bExplicitZeroMarginValRight(false),
+ bExplicitZeroMarginValLeft(false)
+{
+}
+
+
+SvxLRSpaceItem::SvxLRSpaceItem( const tools::Long nLeft, const tools::Long nRight,
+ const short nOfset,
+ const sal_uInt16 nId )
+ : SfxPoolItem(nId)
+ , nFirstLineOffset(nOfset)
+ , nLeftMargin(nLeft)
+ , nRightMargin(nRight)
+ , m_nGutterMargin(0)
+ , m_nRightGutterMargin(0),
+ nPropFirstLineOffset( 100 ),
+ nPropLeftMargin( 100 ),
+ nPropRightMargin( 100 ),
+ bAutoFirst ( false ),
+ bExplicitZeroMarginValRight(false),
+ bExplicitZeroMarginValLeft(false)
+{
+}
+
+
+bool SvxLRSpaceItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ bool bRet = true;
+ bool bConvert = 0!=(nMemberId&CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+ switch( nMemberId )
+ {
+ // now all signed
+ case 0:
+ {
+ css::frame::status::LeftRightMarginScale aLRSpace;
+ aLRSpace.Left = static_cast<sal_Int32>(bConvert ? convertTwipToMm100(nLeftMargin) : nLeftMargin);
+ aLRSpace.TextLeft = static_cast<sal_Int32>(bConvert ? convertTwipToMm100(GetTextLeft()) : GetTextLeft());
+ aLRSpace.Right = static_cast<sal_Int32>(bConvert ? convertTwipToMm100(nRightMargin) : nRightMargin);
+ aLRSpace.ScaleLeft = static_cast<sal_Int16>(nPropLeftMargin);
+ aLRSpace.ScaleRight = static_cast<sal_Int16>(nPropRightMargin);
+ aLRSpace.FirstLine = static_cast<sal_Int32>(bConvert ? convertTwipToMm100(nFirstLineOffset) : nFirstLineOffset);
+ aLRSpace.ScaleFirstLine = static_cast<sal_Int16>(nPropFirstLineOffset);
+ aLRSpace.AutoFirstLine = IsAutoFirst();
+ rVal <<= aLRSpace;
+ break;
+ }
+ case MID_L_MARGIN:
+ rVal <<= static_cast<sal_Int32>(bConvert ? convertTwipToMm100(nLeftMargin) : nLeftMargin);
+ break;
+
+ case MID_TXT_LMARGIN :
+ rVal <<= static_cast<sal_Int32>(bConvert ? convertTwipToMm100(GetTextLeft()) : GetTextLeft());
+ break;
+ case MID_R_MARGIN:
+ rVal <<= static_cast<sal_Int32>(bConvert ? convertTwipToMm100(nRightMargin) : nRightMargin);
+ break;
+ case MID_L_REL_MARGIN:
+ rVal <<= static_cast<sal_Int16>(nPropLeftMargin);
+ break;
+ case MID_R_REL_MARGIN:
+ rVal <<= static_cast<sal_Int16>(nPropRightMargin);
+ break;
+
+ case MID_FIRST_LINE_INDENT:
+ rVal <<= static_cast<sal_Int32>(bConvert ? convertTwipToMm100(nFirstLineOffset) : nFirstLineOffset);
+ break;
+
+ case MID_FIRST_LINE_REL_INDENT:
+ rVal <<= static_cast<sal_Int16>(nPropFirstLineOffset);
+ break;
+
+ case MID_FIRST_AUTO:
+ rVal <<= IsAutoFirst();
+ break;
+
+ case MID_GUTTER_MARGIN:
+ rVal <<= static_cast<sal_Int32>(bConvert ? convertTwipToMm100(m_nGutterMargin)
+ : m_nGutterMargin);
+ break;
+
+ default:
+ bRet = false;
+ // SfxDispatchController_Impl::StateChanged calls this with hardcoded 0 triggering this; there used to be a MID_LR_MARGIN 0 but what type would it have?
+ OSL_FAIL("unknown MemberId");
+ }
+ return bRet;
+}
+
+
+bool SvxLRSpaceItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ bool bConvert = 0 != (nMemberId&CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+ sal_Int32 nVal = 0;
+ if( nMemberId != 0 && nMemberId != MID_FIRST_AUTO &&
+ nMemberId != MID_L_REL_MARGIN && nMemberId != MID_R_REL_MARGIN)
+ if(!(rVal >>= nVal))
+ return false;
+
+ switch( nMemberId )
+ {
+ case 0:
+ {
+ css::frame::status::LeftRightMarginScale aLRSpace;
+ if(!(rVal >>= aLRSpace))
+ return false;
+
+ SetLeft( bConvert ? o3tl::toTwips(aLRSpace.Left, o3tl::Length::mm100) : aLRSpace.Left );
+ SetTextLeft( bConvert ? o3tl::toTwips(aLRSpace.TextLeft, o3tl::Length::mm100) : aLRSpace.TextLeft );
+ SetRight(bConvert ? o3tl::toTwips(aLRSpace.Right, o3tl::Length::mm100) : aLRSpace.Right);
+ nPropLeftMargin = aLRSpace.ScaleLeft;
+ nPropRightMargin = aLRSpace.ScaleRight;
+ SetTextFirstLineOffset(bConvert ? o3tl::toTwips(aLRSpace.FirstLine, o3tl::Length::mm100) : aLRSpace.FirstLine);
+ SetPropTextFirstLineOffset ( aLRSpace.ScaleFirstLine );
+ SetAutoFirst( aLRSpace.AutoFirstLine );
+ break;
+ }
+ case MID_L_MARGIN:
+ SetLeft( bConvert ? o3tl::toTwips(nVal, o3tl::Length::mm100) : nVal );
+ break;
+
+ case MID_TXT_LMARGIN :
+ SetTextLeft( bConvert ? o3tl::toTwips(nVal, o3tl::Length::mm100) : nVal );
+ break;
+
+ case MID_R_MARGIN:
+ SetRight(bConvert ? o3tl::toTwips(nVal, o3tl::Length::mm100) : nVal);
+ break;
+ case MID_L_REL_MARGIN:
+ case MID_R_REL_MARGIN:
+ {
+ sal_Int32 nRel = 0;
+ if((rVal >>= nRel) && nRel >= 0 && nRel < SAL_MAX_UINT16)
+ {
+ if(MID_L_REL_MARGIN== nMemberId)
+ nPropLeftMargin = static_cast<sal_uInt16>(nRel);
+ else
+ nPropRightMargin = static_cast<sal_uInt16>(nRel);
+ }
+ else
+ return false;
+ }
+ break;
+ case MID_FIRST_LINE_INDENT :
+ SetTextFirstLineOffset(bConvert ? o3tl::toTwips(nVal, o3tl::Length::mm100) : nVal);
+ break;
+
+ case MID_FIRST_LINE_REL_INDENT:
+ SetPropTextFirstLineOffset ( nVal );
+ break;
+
+ case MID_FIRST_AUTO:
+ SetAutoFirst( Any2Bool(rVal) );
+ break;
+
+ case MID_GUTTER_MARGIN:
+ SetGutterMargin(bConvert ? o3tl::toTwips(nVal, o3tl::Length::mm100) : nVal);
+ break;
+
+ default:
+ OSL_FAIL("unknown MemberId");
+ return false;
+ }
+ return true;
+}
+
+void SvxLeftMarginItem::SetLeft(const tools::Long nL, const sal_uInt16 nProp)
+{
+ m_nLeftMargin = (nL * nProp) / 100;
+ m_nPropLeftMargin = nProp;
+}
+
+void SvxLRSpaceItem::SetLeft(const tools::Long nL, const sal_uInt16 nProp)
+{
+ nLeftMargin = (nL * nProp) / 100;
+ SAL_WARN_IF(nFirstLineOffset != 0, "editeng", "probably call SetTextLeft instead? looks inconsistent otherwise");
+ nPropLeftMargin = nProp;
+}
+
+void SvxRightMarginItem::SetRight(const tools::Long nR, const sal_uInt16 nProp)
+{
+ m_nRightMargin = (nR * nProp) / 100;
+ m_nPropRightMargin = nProp;
+}
+
+void SvxLRSpaceItem::SetRight(const tools::Long nR, const sal_uInt16 nProp)
+{
+ if (0 == nR)
+ {
+ SetExplicitZeroMarginValRight(true);
+ }
+ nRightMargin = (nR * nProp) / 100;
+ nPropRightMargin = nProp;
+}
+
+void SvxFirstLineIndentItem::SetTextFirstLineOffset(
+ const short nF, const sal_uInt16 nProp)
+{
+ m_nFirstLineOffset = short((tools::Long(nF) * nProp ) / 100);
+ m_nPropFirstLineOffset = nProp;
+}
+
+void SvxLRSpaceItem::SetTextFirstLineOffset(const short nF, const sal_uInt16 nProp)
+{
+ // note: left margin contains any negative first line offset - preserve it!
+ if (nFirstLineOffset < 0)
+ {
+ nLeftMargin -= nFirstLineOffset;
+ }
+ nFirstLineOffset = short((tools::Long(nF) * nProp ) / 100);
+ nPropFirstLineOffset = nProp;
+ if (nFirstLineOffset < 0)
+ {
+ nLeftMargin += nFirstLineOffset;
+ }
+}
+
+#if 0
+void SvxTextLeftMarginItem::SetLeft(SvxFirstLineIndentItem const& rFirstLine,
+ const tools::Long nL, const sal_uInt16 nProp)
+{
+ m_nTextLeftMargin = (nL * nProp) / 100;
+ m_nPropLeftMargin = nProp;
+ // note: text left margin contains any negative first line offset
+ if (rFirstLine.GetTextFirstLineOffset() < 0)
+ {
+ m_nTextLeftMargin += rFirstLine.GetTextFirstLineOffset();
+ }
+}
+#endif
+
+void SvxTextLeftMarginItem::SetTextLeft(const tools::Long nL, const sal_uInt16 nProp)
+{
+ m_nTextLeftMargin = (nL * nProp) / 100;
+ m_nPropLeftMargin = nProp;
+}
+
+void SvxLRSpaceItem::SetTextLeft(const tools::Long nL, const sal_uInt16 nProp)
+{
+ if (0 == nL)
+ {
+ SetExplicitZeroMarginValLeft(true);
+ }
+ auto const nTxtLeft = (nL * nProp) / 100;
+ nPropLeftMargin = nProp;
+ // note: left margin contains any negative first line offset
+ if ( 0 > nFirstLineOffset )
+ nLeftMargin = nTxtLeft + nFirstLineOffset;
+ else
+ nLeftMargin = nTxtLeft;
+}
+
+tools::Long SvxTextLeftMarginItem::GetTextLeft() const
+{
+ return m_nTextLeftMargin;
+}
+
+tools::Long SvxTextLeftMarginItem::GetLeft(SvxFirstLineIndentItem const& rFirstLine) const
+{
+ // add any negative first line offset to text left margin to get left
+ return (rFirstLine.GetTextFirstLineOffset() < 0)
+ ? m_nTextLeftMargin + rFirstLine.GetTextFirstLineOffset()
+ : m_nTextLeftMargin;
+}
+
+tools::Long SvxLRSpaceItem::GetTextLeft() const
+{
+ // remove any negative first line offset from left margin to get text-left
+ return (nFirstLineOffset < 0)
+ ? nLeftMargin - nFirstLineOffset
+ : nLeftMargin;
+}
+
+SvxLeftMarginItem::SvxLeftMarginItem(const sal_uInt16 nId)
+ : SfxPoolItem(nId)
+{
+}
+
+SvxLeftMarginItem::SvxLeftMarginItem(const tools::Long nLeft, const sal_uInt16 nId)
+ : SfxPoolItem(nId)
+ , m_nLeftMargin(nLeft)
+{
+}
+
+bool SvxLeftMarginItem::QueryValue(uno::Any& rVal, sal_uInt8 nMemberId) const
+{
+ bool bRet = true;
+ bool bConvert = 0 != (nMemberId & CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+ switch (nMemberId)
+ {
+ case MID_L_MARGIN:
+ rVal <<= static_cast<sal_Int32>(bConvert ? convertTwipToMm100(m_nLeftMargin) : m_nLeftMargin);
+ break;
+ case MID_L_REL_MARGIN:
+ rVal <<= static_cast<sal_Int16>(m_nPropLeftMargin);
+ break;
+ default:
+ assert(false);
+ bRet = false;
+ // SfxDispatchController_Impl::StateChanged calls this with hardcoded 0 triggering this; there used to be a MID_LR_MARGIN 0 but what type would it have?
+ OSL_FAIL("unknown MemberId");
+ }
+ return bRet;
+}
+
+bool SvxLeftMarginItem::PutValue(const uno::Any& rVal, sal_uInt8 nMemberId)
+{
+ bool bConvert = 0 != (nMemberId & CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+
+ switch (nMemberId)
+ {
+ case MID_L_MARGIN:
+ {
+ sal_Int32 nVal = 0;
+ if (!(rVal >>= nVal))
+ {
+ return false;
+ }
+ SetLeft(bConvert ? o3tl::toTwips(nVal, o3tl::Length::mm100) : nVal);
+ break;
+ }
+ case MID_L_REL_MARGIN:
+ {
+ sal_Int32 nRel = 0;
+ if ((rVal >>= nRel) && nRel >= 0 && nRel < SAL_MAX_UINT16)
+ {
+ m_nPropLeftMargin = static_cast<sal_uInt16>(nRel);
+ }
+ else
+ {
+ return false;
+ }
+ }
+ break;
+ default:
+ assert(false);
+ OSL_FAIL("unknown MemberId");
+ return false;
+ }
+ return true;
+}
+
+bool SvxLeftMarginItem::operator==(const SfxPoolItem& rAttr) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ const SvxLeftMarginItem& rOther = static_cast<const SvxLeftMarginItem&>(rAttr);
+
+ return (m_nLeftMargin == rOther.GetLeft()
+ && m_nPropLeftMargin == rOther.GetPropLeft());
+}
+
+SvxLeftMarginItem* SvxLeftMarginItem::Clone(SfxItemPool *) const
+{
+ return new SvxLeftMarginItem(*this);
+}
+
+bool SvxLeftMarginItem::GetPresentation
+(
+ SfxItemPresentation ePres,
+ MapUnit eCoreUnit,
+ MapUnit ePresUnit,
+ OUString& rText, const IntlWrapper& rIntl
+) const
+{
+ switch (ePres)
+ {
+ case SfxItemPresentation::Nameless:
+ {
+ if (100 != m_nPropLeftMargin)
+ {
+ rText = unicode::formatPercent(m_nPropLeftMargin,
+ Application::GetSettings().GetUILanguageTag());
+ }
+ else
+ {
+ rText = GetMetricText(m_nLeftMargin,
+ eCoreUnit, ePresUnit, &rIntl);
+ }
+ return true;
+ }
+ case SfxItemPresentation::Complete:
+ {
+ rText = EditResId(RID_SVXITEMS_LRSPACE_LEFT);
+ if (100 != m_nPropLeftMargin)
+ {
+ rText += unicode::formatPercent(m_nPropLeftMargin,
+ Application::GetSettings().GetUILanguageTag());
+ }
+ else
+ {
+ rText += GetMetricText(m_nLeftMargin, eCoreUnit, ePresUnit, &rIntl)
+ + " " + EditResId(GetMetricId(ePresUnit));
+ }
+ return true;
+ }
+ default: ; // prevent warning
+ }
+ return false;
+}
+
+void SvxLeftMarginItem::ScaleMetrics(tools::Long const nMult, tools::Long const nDiv)
+{
+ m_nLeftMargin = BigInt::Scale(m_nLeftMargin, nMult, nDiv);
+}
+
+bool SvxLeftMarginItem::HasMetrics() const
+{
+ return true;
+}
+
+void SvxLeftMarginItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxLeftMarginItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nLeftMargin"), BAD_CAST(OString::number(m_nLeftMargin).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nPropLeftMargin"), BAD_CAST(OString::number(m_nPropLeftMargin).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+boost::property_tree::ptree SvxLeftMarginItem::dumpAsJSON() const
+{
+ boost::property_tree::ptree aTree = SfxPoolItem::dumpAsJSON();
+
+ boost::property_tree::ptree aState;
+
+ MapUnit eTargetUnit = MapUnit::MapInch;
+
+ OUString sLeft = GetMetricText(GetLeft(),
+ MapUnit::MapTwip, eTargetUnit, nullptr);
+
+ aState.put("left", sLeft);
+ aState.put("unit", "inch");
+
+ aTree.push_back(std::make_pair("state", aState));
+
+ return aTree;
+}
+
+SvxTextLeftMarginItem::SvxTextLeftMarginItem(const sal_uInt16 nId)
+ : SfxPoolItem(nId)
+{
+}
+
+SvxTextLeftMarginItem::SvxTextLeftMarginItem(const tools::Long nLeft, const sal_uInt16 nId)
+ : SfxPoolItem(nId)
+ , m_nTextLeftMargin(nLeft)
+{
+}
+
+bool SvxTextLeftMarginItem::QueryValue(uno::Any& rVal, sal_uInt8 nMemberId) const
+{
+ bool bRet = true;
+ bool bConvert = 0 != (nMemberId & CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+ switch (nMemberId)
+ {
+ // tdf#154282 - return both values for the hardcoded 0 in SfxDispatchController_Impl::StateChanged
+ case 0:
+ {
+ css::frame::status::LeftRightMarginScale aLRSpace;
+ aLRSpace.TextLeft = static_cast<sal_Int32>(bConvert ? convertTwipToMm100(GetTextLeft()) : GetTextLeft());
+ aLRSpace.ScaleLeft = static_cast<sal_Int16>(m_nPropLeftMargin);
+ rVal <<= aLRSpace;
+ break;
+ }
+ case MID_TXT_LMARGIN :
+ rVal <<= static_cast<sal_Int32>(bConvert ? convertTwipToMm100(GetTextLeft()) : GetTextLeft());
+ break;
+ case MID_L_REL_MARGIN:
+ rVal <<= static_cast<sal_Int16>(m_nPropLeftMargin);
+ break;
+ default:
+ assert(false);
+ bRet = false;
+ // SfxDispatchController_Impl::StateChanged calls this with hardcoded 0 triggering this; there used to be a MID_LR_MARGIN 0 but what type would it have?
+ OSL_FAIL("unknown MemberId");
+ }
+ return bRet;
+}
+
+bool SvxTextLeftMarginItem::PutValue(const uno::Any& rVal, sal_uInt8 nMemberId)
+{
+ bool bConvert = 0 != (nMemberId & CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+
+ switch (nMemberId)
+ {
+ case MID_TXT_LMARGIN:
+ {
+ sal_Int32 nVal = 0;
+ if (!(rVal >>= nVal))
+ {
+ return false;
+ }
+ SetTextLeft(bConvert ? o3tl::toTwips(nVal, o3tl::Length::mm100) : nVal);
+ }
+ break;
+ case MID_L_REL_MARGIN:
+ {
+ sal_Int32 nRel = 0;
+ if ((rVal >>= nRel) && nRel >= 0 && nRel < SAL_MAX_UINT16)
+ {
+ m_nPropLeftMargin = static_cast<sal_uInt16>(nRel);
+ }
+ else
+ {
+ return false;
+ }
+ }
+ break;
+ default:
+ assert(false);
+ OSL_FAIL("unknown MemberId");
+ return false;
+ }
+ return true;
+}
+
+bool SvxTextLeftMarginItem::operator==(const SfxPoolItem& rAttr) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ const SvxTextLeftMarginItem& rOther = static_cast<const SvxTextLeftMarginItem&>(rAttr);
+
+ return (m_nTextLeftMargin == rOther.GetTextLeft()
+ && m_nPropLeftMargin == rOther.GetPropLeft());
+}
+
+SvxTextLeftMarginItem* SvxTextLeftMarginItem::Clone(SfxItemPool *) const
+{
+ return new SvxTextLeftMarginItem(*this);
+}
+
+bool SvxTextLeftMarginItem::GetPresentation
+(
+ SfxItemPresentation ePres,
+ MapUnit eCoreUnit,
+ MapUnit ePresUnit,
+ OUString& rText, const IntlWrapper& rIntl
+) const
+{
+ switch (ePres)
+ {
+ case SfxItemPresentation::Nameless:
+ {
+ if (100 != m_nPropLeftMargin)
+ {
+ rText = unicode::formatPercent(m_nPropLeftMargin,
+ Application::GetSettings().GetUILanguageTag());
+ }
+ else
+ {
+ rText = GetMetricText(m_nTextLeftMargin,
+ eCoreUnit, ePresUnit, &rIntl);
+ }
+ return true;
+ }
+ case SfxItemPresentation::Complete:
+ {
+ rText = EditResId(RID_SVXITEMS_LRSPACE_LEFT);
+ if (100 != m_nPropLeftMargin)
+ {
+ rText += unicode::formatPercent(m_nPropLeftMargin,
+ Application::GetSettings().GetUILanguageTag());
+ }
+ else
+ {
+ rText += GetMetricText(m_nTextLeftMargin, eCoreUnit, ePresUnit, &rIntl)
+ + " " + EditResId(GetMetricId(ePresUnit));
+ }
+ return true;
+ }
+ default: ; // prevent warning
+ }
+ return false;
+}
+
+void SvxTextLeftMarginItem::ScaleMetrics(tools::Long const nMult, tools::Long const nDiv)
+{
+ m_nTextLeftMargin = BigInt::Scale(m_nTextLeftMargin, nMult, nDiv);
+}
+
+bool SvxTextLeftMarginItem::HasMetrics() const
+{
+ return true;
+}
+
+void SvxTextLeftMarginItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxTextLeftMarginItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nTextLeftMargin"), BAD_CAST(OString::number(m_nTextLeftMargin).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nPropLeftMargin"), BAD_CAST(OString::number(m_nPropLeftMargin).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+boost::property_tree::ptree SvxTextLeftMarginItem::dumpAsJSON() const
+{
+ boost::property_tree::ptree aTree = SfxPoolItem::dumpAsJSON();
+
+ boost::property_tree::ptree aState;
+
+ MapUnit eTargetUnit = MapUnit::MapInch;
+
+ OUString sLeft = GetMetricText(GetTextLeft(),
+ MapUnit::MapTwip, eTargetUnit, nullptr);
+
+ aState.put("left", sLeft);
+ aState.put("unit", "inch");
+
+ aTree.push_back(std::make_pair("state", aState));
+
+ return aTree;
+}
+
+SvxFirstLineIndentItem::SvxFirstLineIndentItem(const sal_uInt16 nId)
+ : SfxPoolItem(nId)
+{
+}
+
+SvxFirstLineIndentItem::SvxFirstLineIndentItem(const short nFirst, const sal_uInt16 nId)
+ : SfxPoolItem(nId)
+ , m_nFirstLineOffset(nFirst)
+{
+}
+
+bool SvxFirstLineIndentItem::QueryValue(uno::Any& rVal, sal_uInt8 nMemberId) const
+{
+ bool bRet = true;
+ bool bConvert = 0 != (nMemberId & CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+ switch (nMemberId)
+ {
+ case MID_FIRST_LINE_INDENT:
+ rVal <<= static_cast<sal_Int32>(bConvert ? convertTwipToMm100(m_nFirstLineOffset) : m_nFirstLineOffset);
+ break;
+ case MID_FIRST_LINE_REL_INDENT:
+ rVal <<= static_cast<sal_Int16>(m_nPropFirstLineOffset);
+ break;
+ case MID_FIRST_AUTO:
+ rVal <<= IsAutoFirst();
+ break;
+ default:
+ assert(false);
+ bRet = false;
+ // SfxDispatchController_Impl::StateChanged calls this with hardcoded 0 triggering this; there used to be a MID_LR_MARGIN 0 but what type would it have?
+ OSL_FAIL("unknown MemberId");
+ }
+ return bRet;
+}
+
+bool SvxFirstLineIndentItem::PutValue(const uno::Any& rVal, sal_uInt8 nMemberId)
+{
+ bool bConvert = 0 != (nMemberId & CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+
+ switch (nMemberId)
+ {
+ case MID_FIRST_LINE_INDENT:
+ {
+ sal_Int32 nVal = 0;
+ if (!(rVal >>= nVal))
+ {
+ return false;
+ }
+ m_nFirstLineOffset = bConvert ? o3tl::toTwips(nVal, o3tl::Length::mm100) : nVal;
+ m_nPropFirstLineOffset = 100;
+ break;
+ }
+ case MID_FIRST_LINE_REL_INDENT:
+ {
+ sal_Int32 nRel = 0;
+ if ((rVal >>= nRel) && nRel >= 0 && nRel < SAL_MAX_UINT16)
+ {
+ SetPropTextFirstLineOffset(nRel);
+ }
+ else
+ {
+ return false;
+ }
+ break;
+ }
+ case MID_FIRST_AUTO:
+ SetAutoFirst(Any2Bool(rVal));
+ break;
+ default:
+ assert(false);
+ OSL_FAIL("unknown MemberId");
+ return false;
+ }
+ return true;
+}
+
+bool SvxFirstLineIndentItem::operator==(const SfxPoolItem& rAttr) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ const SvxFirstLineIndentItem& rOther = static_cast<const SvxFirstLineIndentItem&>(rAttr);
+
+ return (m_nFirstLineOffset == rOther.GetTextFirstLineOffset()
+ && m_nPropFirstLineOffset == rOther.GetPropTextFirstLineOffset()
+ && m_bAutoFirst == rOther.IsAutoFirst());
+}
+
+SvxFirstLineIndentItem* SvxFirstLineIndentItem::Clone(SfxItemPool *) const
+{
+ return new SvxFirstLineIndentItem(*this);
+}
+
+bool SvxFirstLineIndentItem::GetPresentation
+(
+ SfxItemPresentation ePres,
+ MapUnit eCoreUnit,
+ MapUnit ePresUnit,
+ OUString& rText, const IntlWrapper& rIntl
+) const
+{
+ switch (ePres)
+ {
+ case SfxItemPresentation::Nameless:
+ {
+ if (100 != m_nPropFirstLineOffset)
+ {
+ rText += unicode::formatPercent(m_nPropFirstLineOffset,
+ Application::GetSettings().GetUILanguageTag());
+ }
+ else
+ {
+ rText += GetMetricText(static_cast<tools::Long>(m_nFirstLineOffset),
+ eCoreUnit, ePresUnit, &rIntl);
+ }
+ return true;
+ }
+ case SfxItemPresentation::Complete:
+ {
+ rText += EditResId(RID_SVXITEMS_LRSPACE_FLINE);
+ if (100 != m_nPropFirstLineOffset)
+ {
+ rText += unicode::formatPercent(m_nPropFirstLineOffset,
+ Application::GetSettings().GetUILanguageTag());
+ }
+ else
+ {
+ rText += GetMetricText(static_cast<tools::Long>(m_nFirstLineOffset),
+ eCoreUnit, ePresUnit, &rIntl)
+ + " " + EditResId(GetMetricId(ePresUnit));
+ }
+ return true;
+ }
+ default: ; // prevent warning
+ }
+ return false;
+}
+
+void SvxFirstLineIndentItem::ScaleMetrics(tools::Long const nMult, tools::Long const nDiv)
+{
+ m_nFirstLineOffset = static_cast<short>(BigInt::Scale(m_nFirstLineOffset, nMult, nDiv));
+}
+
+bool SvxFirstLineIndentItem::HasMetrics() const
+{
+ return true;
+}
+
+void SvxFirstLineIndentItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxFirstLineIndentItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nFirstLineOffset"), BAD_CAST(OString::number(m_nFirstLineOffset).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nPropFirstLineOffset"), BAD_CAST(OString::number(m_nPropFirstLineOffset).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_bAutoFirst"), BAD_CAST(OString::number(int(m_bAutoFirst)).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+boost::property_tree::ptree SvxFirstLineIndentItem::dumpAsJSON() const
+{
+ boost::property_tree::ptree aTree = SfxPoolItem::dumpAsJSON();
+
+ boost::property_tree::ptree aState;
+
+ MapUnit eTargetUnit = MapUnit::MapInch;
+
+ OUString sFirstline = GetMetricText(GetTextFirstLineOffset(),
+ MapUnit::MapTwip, eTargetUnit, nullptr);
+
+ aState.put("firstline", sFirstline);
+ aState.put("unit", "inch");
+
+ aTree.push_back(std::make_pair("state", aState));
+
+ return aTree;
+}
+
+SvxRightMarginItem::SvxRightMarginItem(const sal_uInt16 nId)
+ : SfxPoolItem(nId)
+{
+}
+
+SvxRightMarginItem::SvxRightMarginItem(const tools::Long nRight, const sal_uInt16 nId)
+ : SfxPoolItem(nId)
+ , m_nRightMargin(nRight)
+{
+}
+
+bool SvxRightMarginItem::QueryValue(uno::Any& rVal, sal_uInt8 nMemberId) const
+{
+ bool bRet = true;
+ bool bConvert = 0 != (nMemberId & CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+ switch (nMemberId)
+ {
+ // tdf#154282 - return both values for the hardcoded 0 in SfxDispatchController_Impl::StateChanged
+ case 0:
+ {
+ css::frame::status::LeftRightMarginScale aLRSpace;
+ aLRSpace.Right = static_cast<sal_Int32>(bConvert ? convertTwipToMm100(m_nRightMargin) : m_nRightMargin);
+ aLRSpace.ScaleRight = static_cast<sal_Int16>(m_nPropRightMargin);
+ rVal <<= aLRSpace;
+ break;
+ }
+ case MID_R_MARGIN:
+ rVal <<= static_cast<sal_Int32>(bConvert ? convertTwipToMm100(m_nRightMargin) : m_nRightMargin);
+ break;
+ case MID_R_REL_MARGIN:
+ rVal <<= static_cast<sal_Int16>(m_nPropRightMargin);
+ break;
+ default:
+ assert(false);
+ bRet = false;
+ // SfxDispatchController_Impl::StateChanged calls this with hardcoded 0 triggering this; there used to be a MID_LR_MARGIN 0 but what type would it have?
+ OSL_FAIL("unknown MemberId");
+ }
+ return bRet;
+}
+
+bool SvxRightMarginItem::PutValue(const uno::Any& rVal, sal_uInt8 nMemberId)
+{
+ bool bConvert = 0 != (nMemberId & CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+
+ switch (nMemberId)
+ {
+ case MID_R_MARGIN:
+ {
+ sal_Int32 nVal = 0;
+ if (!(rVal >>= nVal))
+ {
+ return false;
+ }
+ SetRight(bConvert ? o3tl::toTwips(nVal, o3tl::Length::mm100) : nVal);
+ break;
+ }
+ case MID_R_REL_MARGIN:
+ {
+ sal_Int32 nRel = 0;
+ if ((rVal >>= nRel) && nRel >= 0 && nRel < SAL_MAX_UINT16)
+ {
+ m_nPropRightMargin = static_cast<sal_uInt16>(nRel);
+ }
+ else
+ {
+ return false;
+ }
+ }
+ break;
+ default:
+ assert(false);
+ OSL_FAIL("unknown MemberId");
+ return false;
+ }
+ return true;
+}
+
+bool SvxRightMarginItem::operator==(const SfxPoolItem& rAttr) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ const SvxRightMarginItem& rOther = static_cast<const SvxRightMarginItem&>(rAttr);
+
+ return (m_nRightMargin == rOther.GetRight()
+ && m_nPropRightMargin == rOther.GetPropRight());
+}
+
+SvxRightMarginItem* SvxRightMarginItem::Clone(SfxItemPool *) const
+{
+ return new SvxRightMarginItem(*this);
+}
+
+bool SvxRightMarginItem::GetPresentation
+(
+ SfxItemPresentation ePres,
+ MapUnit eCoreUnit,
+ MapUnit ePresUnit,
+ OUString& rText, const IntlWrapper& rIntl
+) const
+{
+ switch (ePres)
+ {
+ case SfxItemPresentation::Nameless:
+ {
+ if (100 != m_nRightMargin)
+ {
+ rText += unicode::formatPercent(m_nRightMargin,
+ Application::GetSettings().GetUILanguageTag());
+ }
+ else
+ {
+ rText += GetMetricText(m_nRightMargin,
+ eCoreUnit, ePresUnit, &rIntl);
+ }
+ return true;
+ }
+ case SfxItemPresentation::Complete:
+ {
+ rText += EditResId(RID_SVXITEMS_LRSPACE_RIGHT);
+ if (100 != m_nPropRightMargin)
+ {
+ rText += unicode::formatPercent(m_nPropRightMargin,
+ Application::GetSettings().GetUILanguageTag());
+ }
+ else
+ {
+ rText += GetMetricText(m_nRightMargin,
+ eCoreUnit, ePresUnit, &rIntl)
+ + " " + EditResId(GetMetricId(ePresUnit));
+ }
+ return true;
+ }
+ default: ; // prevent warning
+ }
+ return false;
+}
+
+void SvxRightMarginItem::ScaleMetrics(tools::Long const nMult, tools::Long const nDiv)
+{
+ m_nRightMargin = BigInt::Scale(m_nRightMargin, nMult, nDiv);
+}
+
+bool SvxRightMarginItem::HasMetrics() const
+{
+ return true;
+}
+
+void SvxRightMarginItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxRightMarginItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nRightMargin"), BAD_CAST(OString::number(m_nRightMargin).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nPropRightMargin"), BAD_CAST(OString::number(m_nPropRightMargin).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+boost::property_tree::ptree SvxRightMarginItem::dumpAsJSON() const
+{
+ boost::property_tree::ptree aTree = SfxPoolItem::dumpAsJSON();
+
+ boost::property_tree::ptree aState;
+
+ MapUnit eTargetUnit = MapUnit::MapInch;
+
+ OUString sRight = GetMetricText(GetRight(),
+ MapUnit::MapTwip, eTargetUnit, nullptr);
+
+ aState.put("right", sRight);
+ aState.put("unit", "inch");
+
+ aTree.push_back(std::make_pair("state", aState));
+
+ return aTree;
+}
+
+SvxGutterLeftMarginItem::SvxGutterLeftMarginItem(const sal_uInt16 nId)
+ : SfxPoolItem(nId)
+{
+}
+
+bool SvxGutterLeftMarginItem::QueryValue(uno::Any& rVal, sal_uInt8 nMemberId) const
+{
+ bool bRet = true;
+ bool bConvert = 0 != (nMemberId & CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+ switch (nMemberId)
+ {
+ case MID_GUTTER_MARGIN:
+ rVal <<= static_cast<sal_Int32>(bConvert ? convertTwipToMm100(m_nGutterMargin)
+ : m_nGutterMargin);
+ break;
+ default:
+ assert(false);
+ bRet = false;
+ // SfxDispatchController_Impl::StateChanged calls this with hardcoded 0 triggering this; there used to be a MID_LR_MARGIN 0 but what type would it have?
+ OSL_FAIL("unknown MemberId");
+ }
+ return bRet;
+}
+
+bool SvxGutterLeftMarginItem::PutValue(const uno::Any& rVal, sal_uInt8 nMemberId)
+{
+ bool bConvert = 0 != (nMemberId & CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+
+ switch (nMemberId)
+ {
+ case MID_GUTTER_MARGIN:
+ {
+ sal_Int32 nVal = 0;
+ if (!(rVal >>= nVal))
+ {
+ return false;
+ }
+ SetGutterMargin(bConvert ? o3tl::toTwips(nVal, o3tl::Length::mm100) : nVal);
+ break;
+ }
+ default:
+ assert(false);
+ OSL_FAIL("unknown MemberId");
+ return false;
+ }
+ return true;
+}
+
+bool SvxGutterLeftMarginItem::operator==(const SfxPoolItem& rAttr) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ const SvxGutterLeftMarginItem& rOther = static_cast<const SvxGutterLeftMarginItem&>(rAttr);
+
+ return (m_nGutterMargin == rOther.GetGutterMargin());
+}
+
+SvxGutterLeftMarginItem* SvxGutterLeftMarginItem::Clone(SfxItemPool * ) const
+{
+ return new SvxGutterLeftMarginItem(*this);
+}
+
+bool SvxGutterLeftMarginItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& /*rText*/, const IntlWrapper& /*rIntl*/
+) const
+{
+ // TODO?
+ return false;
+}
+
+void SvxGutterLeftMarginItem::ScaleMetrics(tools::Long const /*nMult*/, tools::Long const /*nDiv*/)
+{
+ // TODO?
+}
+
+bool SvxGutterLeftMarginItem::HasMetrics() const
+{
+ return true;
+}
+
+void SvxGutterLeftMarginItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxGutterLeftMarginItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nGutterMargin"),
+ BAD_CAST(OString::number(m_nGutterMargin).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+boost::property_tree::ptree SvxGutterLeftMarginItem::dumpAsJSON() const
+{
+ boost::property_tree::ptree aTree = SfxPoolItem::dumpAsJSON();
+
+ boost::property_tree::ptree aState;
+
+ // TODO?
+ aState.put("unit", "inch");
+
+ aTree.push_back(std::make_pair("state", aState));
+
+ return aTree;
+}
+
+SvxGutterRightMarginItem::SvxGutterRightMarginItem(const sal_uInt16 nId)
+ : SfxPoolItem(nId)
+{
+}
+
+bool SvxGutterRightMarginItem::QueryValue(uno::Any& /*rVal*/, sal_uInt8 nMemberId) const
+{
+ bool bRet = true;
+ //bool bConvert = 0 != (nMemberId & CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+#ifndef _MSC_VER
+ switch (nMemberId)
+ {
+ // TODO?
+ default:
+ assert(false);
+ bRet = false;
+ // SfxDispatchController_Impl::StateChanged calls this with hardcoded 0 triggering this; there used to be a MID_LR_MARGIN 0 but what type would it have?
+ OSL_FAIL("unknown MemberId");
+ }
+#else
+ (void) nMemberId;
+#endif
+ return bRet;
+}
+
+bool SvxGutterRightMarginItem::PutValue(const uno::Any& /*rVal*/, sal_uInt8 nMemberId)
+{
+ //bool bConvert = 0 != (nMemberId & CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+
+#ifndef _MSC_VER
+ switch (nMemberId)
+ {
+ // TODO?
+ default:
+ assert(false);
+ OSL_FAIL("unknown MemberId");
+ return false;
+ }
+#else
+ (void) nMemberId;
+#endif
+ return true;
+}
+
+
+bool SvxGutterRightMarginItem::operator==(const SfxPoolItem& rAttr) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ const SvxGutterRightMarginItem& rOther = static_cast<const SvxGutterRightMarginItem&>(rAttr);
+
+ return (m_nRightGutterMargin == rOther.GetRightGutterMargin());
+}
+
+SvxGutterRightMarginItem* SvxGutterRightMarginItem::Clone(SfxItemPool *) const
+{
+ return new SvxGutterRightMarginItem(*this);
+}
+
+bool SvxGutterRightMarginItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& /*rText*/, const IntlWrapper& /*rIntl*/
+) const
+{
+ // TODO?
+ return false;
+}
+
+void SvxGutterRightMarginItem::ScaleMetrics(tools::Long const /*nMult*/, tools::Long const /*nDiv*/)
+{
+ // TODO?
+}
+
+bool SvxGutterRightMarginItem::HasMetrics() const
+{
+ return true;
+}
+
+void SvxGutterRightMarginItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxGutterRightMarginItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nRightGutterMargin"),
+ BAD_CAST(OString::number(m_nRightGutterMargin).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+boost::property_tree::ptree SvxGutterRightMarginItem::dumpAsJSON() const
+{
+ boost::property_tree::ptree aTree = SfxPoolItem::dumpAsJSON();
+
+ boost::property_tree::ptree aState;
+
+ // TODO?
+ aState.put("unit", "inch");
+
+ aTree.push_back(std::make_pair("state", aState));
+
+ return aTree;
+}
+
+
+bool SvxLRSpaceItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ const SvxLRSpaceItem& rOther = static_cast<const SvxLRSpaceItem&>(rAttr);
+
+ return (
+ nFirstLineOffset == rOther.GetTextFirstLineOffset() &&
+ m_nGutterMargin == rOther.GetGutterMargin() &&
+ m_nRightGutterMargin == rOther.GetRightGutterMargin() &&
+ nLeftMargin == rOther.GetLeft() &&
+ nRightMargin == rOther.GetRight() &&
+ nPropFirstLineOffset == rOther.GetPropTextFirstLineOffset() &&
+ nPropLeftMargin == rOther.GetPropLeft() &&
+ nPropRightMargin == rOther.GetPropRight() &&
+ bAutoFirst == rOther.IsAutoFirst() &&
+ bExplicitZeroMarginValRight == rOther.IsExplicitZeroMarginValRight() &&
+ bExplicitZeroMarginValLeft == rOther.IsExplicitZeroMarginValLeft() );
+}
+
+SvxLRSpaceItem* SvxLRSpaceItem::Clone( SfxItemPool* ) const
+{
+ return new SvxLRSpaceItem( *this );
+}
+
+bool SvxLRSpaceItem::GetPresentation
+(
+ SfxItemPresentation ePres,
+ MapUnit eCoreUnit,
+ MapUnit ePresUnit,
+ OUString& rText, const IntlWrapper& rIntl
+) const
+{
+ switch ( ePres )
+ {
+ case SfxItemPresentation::Nameless:
+ {
+ if ( 100 != nPropLeftMargin )
+ {
+ rText = unicode::formatPercent(nPropLeftMargin,
+ Application::GetSettings().GetUILanguageTag());
+ }
+ else
+ rText = GetMetricText( nLeftMargin,
+ eCoreUnit, ePresUnit, &rIntl );
+ rText += cpDelim;
+ if ( 100 != nPropFirstLineOffset )
+ {
+ rText += unicode::formatPercent(nPropFirstLineOffset,
+ Application::GetSettings().GetUILanguageTag());
+ }
+ else
+ rText += GetMetricText( static_cast<tools::Long>(nFirstLineOffset),
+ eCoreUnit, ePresUnit, &rIntl );
+ rText += cpDelim;
+ if ( 100 != nRightMargin )
+ {
+ rText += unicode::formatPercent(nRightMargin,
+ Application::GetSettings().GetUILanguageTag());
+ }
+ else
+ rText += GetMetricText( nRightMargin,
+ eCoreUnit, ePresUnit, &rIntl );
+ return true;
+ }
+ case SfxItemPresentation::Complete:
+ {
+ rText = EditResId(RID_SVXITEMS_LRSPACE_LEFT);
+ if ( 100 != nPropLeftMargin )
+ rText += unicode::formatPercent(nPropLeftMargin,
+ Application::GetSettings().GetUILanguageTag());
+ else
+ {
+ rText += GetMetricText( nLeftMargin, eCoreUnit, ePresUnit, &rIntl ) +
+ " " + EditResId(GetMetricId(ePresUnit));
+ }
+ rText += cpDelim;
+ if ( 100 != nPropFirstLineOffset || nFirstLineOffset )
+ {
+ rText += EditResId(RID_SVXITEMS_LRSPACE_FLINE);
+ if ( 100 != nPropFirstLineOffset )
+ rText += unicode::formatPercent(nPropFirstLineOffset,
+ Application::GetSettings().GetUILanguageTag());
+ else
+ {
+ rText += GetMetricText( static_cast<tools::Long>(nFirstLineOffset),
+ eCoreUnit, ePresUnit, &rIntl ) +
+ " " + EditResId(GetMetricId(ePresUnit));
+ }
+ rText += cpDelim;
+ }
+ rText += EditResId(RID_SVXITEMS_LRSPACE_RIGHT);
+ if ( 100 != nPropRightMargin )
+ rText += unicode::formatPercent(nPropRightMargin,
+ Application::GetSettings().GetUILanguageTag());
+ else
+ {
+ rText += GetMetricText( nRightMargin,
+ eCoreUnit, ePresUnit, &rIntl ) +
+ " " + EditResId(GetMetricId(ePresUnit));
+ }
+ return true;
+ }
+ default: ; // prevent warning
+ }
+ return false;
+}
+
+
+void SvxLRSpaceItem::ScaleMetrics( tools::Long nMult, tools::Long nDiv )
+{
+ nFirstLineOffset = static_cast<short>(BigInt::Scale( nFirstLineOffset, nMult, nDiv ));
+ nLeftMargin = BigInt::Scale( nLeftMargin, nMult, nDiv );
+ nRightMargin = BigInt::Scale( nRightMargin, nMult, nDiv );
+}
+
+
+bool SvxLRSpaceItem::HasMetrics() const
+{
+ return true;
+}
+
+
+void SvxLRSpaceItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxLRSpaceItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nFirstLineOffset"), BAD_CAST(OString::number(nFirstLineOffset).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nLeftMargin"), BAD_CAST(OString::number(nLeftMargin).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nRightMargin"), BAD_CAST(OString::number(nRightMargin).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nGutterMargin"),
+ BAD_CAST(OString::number(m_nGutterMargin).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nRightGutterMargin"),
+ BAD_CAST(OString::number(m_nRightGutterMargin).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nPropFirstLineOffset"), BAD_CAST(OString::number(nPropFirstLineOffset).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nPropLeftMargin"), BAD_CAST(OString::number(nPropLeftMargin).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nPropRightMargin"), BAD_CAST(OString::number(nPropRightMargin).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("bAutoFirst"), BAD_CAST(OString::number(int(bAutoFirst)).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("bExplicitZeroMarginValRight"), BAD_CAST(OString::number(int(bExplicitZeroMarginValRight)).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("bExplicitZeroMarginValLeft"), BAD_CAST(OString::number(int(bExplicitZeroMarginValLeft)).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+
+boost::property_tree::ptree SvxLRSpaceItem::dumpAsJSON() const
+{
+ boost::property_tree::ptree aTree = SfxPoolItem::dumpAsJSON();
+
+ boost::property_tree::ptree aState;
+
+ MapUnit eTargetUnit = MapUnit::MapInch;
+
+ OUString sLeft = GetMetricText(GetLeft(),
+ MapUnit::MapTwip, eTargetUnit, nullptr);
+
+ OUString sRight = GetMetricText(GetRight(),
+ MapUnit::MapTwip, eTargetUnit, nullptr);
+
+ OUString sFirstline = GetMetricText(GetTextFirstLineOffset(),
+ MapUnit::MapTwip, eTargetUnit, nullptr);
+
+ aState.put("left", sLeft);
+ aState.put("right", sRight);
+ aState.put("firstline", sFirstline);
+ aState.put("unit", "inch");
+
+ aTree.push_back(std::make_pair("state", aState));
+
+ return aTree;
+}
+
+
+SvxULSpaceItem::SvxULSpaceItem( const sal_uInt16 nId )
+ : SfxPoolItem(nId)
+ , nUpper(0)
+ , nLower(0)
+ , bContext(false)
+ , nPropUpper(100)
+ , nPropLower(100)
+{
+}
+
+
+SvxULSpaceItem::SvxULSpaceItem( const sal_uInt16 nUp, const sal_uInt16 nLow,
+ const sal_uInt16 nId )
+ : SfxPoolItem(nId)
+ , nUpper(nUp)
+ , nLower(nLow)
+ , bContext(false)
+ , nPropUpper(100)
+ , nPropLower(100)
+{
+}
+
+
+bool SvxULSpaceItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ bool bConvert = 0!=(nMemberId&CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+ switch( nMemberId )
+ {
+ // now all signed
+ case 0:
+ {
+ css::frame::status::UpperLowerMarginScale aUpperLowerMarginScale;
+ aUpperLowerMarginScale.Upper = static_cast<sal_Int32>(bConvert ? convertTwipToMm100(nUpper) : nUpper);
+ aUpperLowerMarginScale.Lower = static_cast<sal_Int32>(bConvert ? convertTwipToMm100(nLower) : nPropUpper);
+ aUpperLowerMarginScale.ScaleUpper = static_cast<sal_Int16>(nPropUpper);
+ aUpperLowerMarginScale.ScaleLower = static_cast<sal_Int16>(nPropLower);
+ rVal <<= aUpperLowerMarginScale;
+ break;
+ }
+ case MID_UP_MARGIN: rVal <<= static_cast<sal_Int32>(bConvert ? convertTwipToMm100(nUpper) : nUpper); break;
+ case MID_LO_MARGIN: rVal <<= static_cast<sal_Int32>(bConvert ? convertTwipToMm100(nLower) : nLower); break;
+ case MID_CTX_MARGIN: rVal <<= bContext; break;
+ case MID_UP_REL_MARGIN: rVal <<= static_cast<sal_Int16>(nPropUpper); break;
+ case MID_LO_REL_MARGIN: rVal <<= static_cast<sal_Int16>(nPropLower); break;
+ }
+ return true;
+}
+
+
+bool SvxULSpaceItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ bool bConvert = 0!=(nMemberId&CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+ sal_Int32 nVal = 0;
+ bool bVal = false;
+ switch( nMemberId )
+ {
+ case 0:
+ {
+ css::frame::status::UpperLowerMarginScale aUpperLowerMarginScale;
+ if ( !(rVal >>= aUpperLowerMarginScale ))
+ return false;
+ {
+ SetUpper(bConvert ? o3tl::toTwips(aUpperLowerMarginScale.Upper, o3tl::Length::mm100) : aUpperLowerMarginScale.Upper);
+ SetLower(bConvert ? o3tl::toTwips(aUpperLowerMarginScale.Lower, o3tl::Length::mm100) : aUpperLowerMarginScale.Lower);
+ if( aUpperLowerMarginScale.ScaleUpper > 1 )
+ nPropUpper = aUpperLowerMarginScale.ScaleUpper;
+ if( aUpperLowerMarginScale.ScaleLower > 1 )
+ nPropUpper = aUpperLowerMarginScale.ScaleLower;
+ }
+ }
+ break;
+ case MID_UP_MARGIN :
+ if(!(rVal >>= nVal))
+ return false;
+ SetUpper(bConvert ? o3tl::toTwips(nVal, o3tl::Length::mm100) : nVal);
+ break;
+ case MID_LO_MARGIN :
+ if(!(rVal >>= nVal) || nVal < 0)
+ return false;
+ SetLower(bConvert ? o3tl::toTwips(nVal, o3tl::Length::mm100) : nVal);
+ break;
+ case MID_CTX_MARGIN :
+ if (!(rVal >>= bVal))
+ return false;
+ SetContextValue(bVal);
+ break;
+ case MID_UP_REL_MARGIN:
+ case MID_LO_REL_MARGIN:
+ {
+ sal_Int32 nRel = 0;
+ if((rVal >>= nRel) && nRel > 1 )
+ {
+ if(MID_UP_REL_MARGIN == nMemberId)
+ nPropUpper = static_cast<sal_uInt16>(nRel);
+ else
+ nPropLower = static_cast<sal_uInt16>(nRel);
+ }
+ else
+ return false;
+ }
+ break;
+
+ default:
+ OSL_FAIL("unknown MemberId");
+ return false;
+ }
+ return true;
+}
+
+
+bool SvxULSpaceItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ const SvxULSpaceItem& rSpaceItem = static_cast<const SvxULSpaceItem&>( rAttr );
+ return ( nUpper == rSpaceItem.nUpper &&
+ nLower == rSpaceItem.nLower &&
+ bContext == rSpaceItem.bContext &&
+ nPropUpper == rSpaceItem.nPropUpper &&
+ nPropLower == rSpaceItem.nPropLower );
+}
+
+SvxULSpaceItem* SvxULSpaceItem::Clone( SfxItemPool* ) const
+{
+ return new SvxULSpaceItem( *this );
+}
+
+bool SvxULSpaceItem::GetPresentation
+(
+ SfxItemPresentation ePres,
+ MapUnit eCoreUnit,
+ MapUnit ePresUnit,
+ OUString& rText,
+ const IntlWrapper& rIntl
+) const
+{
+ switch ( ePres )
+ {
+ case SfxItemPresentation::Nameless:
+ {
+ if ( 100 != nPropUpper )
+ {
+ rText = unicode::formatPercent(nPropUpper,
+ Application::GetSettings().GetUILanguageTag());
+ }
+ else
+ rText = GetMetricText( static_cast<tools::Long>(nUpper), eCoreUnit, ePresUnit, &rIntl );
+ rText += cpDelim;
+ if ( 100 != nPropLower )
+ {
+ rText += unicode::formatPercent(nPropLower,
+ Application::GetSettings().GetUILanguageTag());
+ }
+ else
+ rText += GetMetricText( static_cast<tools::Long>(nLower), eCoreUnit, ePresUnit, &rIntl );
+ return true;
+ }
+ case SfxItemPresentation::Complete:
+ {
+ rText = EditResId(RID_SVXITEMS_ULSPACE_UPPER);
+ if ( 100 != nPropUpper )
+ {
+ rText += unicode::formatPercent(nPropUpper,
+ Application::GetSettings().GetUILanguageTag());
+ }
+ else
+ {
+ rText += GetMetricText( static_cast<tools::Long>(nUpper), eCoreUnit, ePresUnit, &rIntl ) +
+ " " + EditResId(GetMetricId(ePresUnit));
+ }
+ rText += cpDelim + EditResId(RID_SVXITEMS_ULSPACE_LOWER);
+ if ( 100 != nPropLower )
+ {
+ rText += unicode::formatPercent(nPropLower,
+ Application::GetSettings().GetUILanguageTag());
+ }
+ else
+ {
+ rText += GetMetricText( static_cast<tools::Long>(nLower), eCoreUnit, ePresUnit, &rIntl ) +
+ " " + EditResId(GetMetricId(ePresUnit));
+ }
+ return true;
+ }
+ default: ; // prevent warning
+ }
+ return false;
+}
+
+
+void SvxULSpaceItem::ScaleMetrics( tools::Long nMult, tools::Long nDiv )
+{
+ nUpper = static_cast<sal_uInt16>(BigInt::Scale( nUpper, nMult, nDiv ));
+ nLower = static_cast<sal_uInt16>(BigInt::Scale( nLower, nMult, nDiv ));
+}
+
+
+bool SvxULSpaceItem::HasMetrics() const
+{
+ return true;
+}
+
+
+void SvxULSpaceItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxULSpaceItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nUpper"), BAD_CAST(OString::number(nUpper).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nLower"), BAD_CAST(OString::number(nLower).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("bContext"), BAD_CAST(OString::boolean(bContext).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nPropUpper"), BAD_CAST(OString::number(nPropUpper).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nPropLower"), BAD_CAST(OString::number(nPropLower).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+boost::property_tree::ptree SvxULSpaceItem::dumpAsJSON() const
+{
+ boost::property_tree::ptree aTree = SfxPoolItem::dumpAsJSON();
+
+ boost::property_tree::ptree aState;
+
+ MapUnit eTargetUnit = MapUnit::MapInch;
+
+ OUString sUpper = GetMetricText(GetUpper(),
+ MapUnit::MapTwip, eTargetUnit, nullptr);
+
+ OUString sLower = GetMetricText(GetLower(),
+ MapUnit::MapTwip, eTargetUnit, nullptr);
+
+ aState.put("upper", sUpper);
+ aState.put("lower", sLower);
+ aState.put("unit", "inch");
+
+ aTree.push_back(std::make_pair("state", aState));
+
+ return aTree;
+}
+
+SvxPrintItem* SvxPrintItem::Clone( SfxItemPool* ) const
+{
+ return new SvxPrintItem( *this );
+}
+
+bool SvxPrintItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ TranslateId pId = RID_SVXITEMS_PRINT_FALSE;
+
+ if ( GetValue() )
+ pId = RID_SVXITEMS_PRINT_TRUE;
+ rText = EditResId(pId);
+ return true;
+}
+
+SvxOpaqueItem* SvxOpaqueItem::Clone( SfxItemPool* ) const
+{
+ return new SvxOpaqueItem( *this );
+}
+
+bool SvxOpaqueItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ TranslateId pId = RID_SVXITEMS_OPAQUE_FALSE;
+
+ if ( GetValue() )
+ pId = RID_SVXITEMS_OPAQUE_TRUE;
+ rText = EditResId(pId);
+ return true;
+}
+
+
+bool SvxProtectItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ const SvxProtectItem& rItem = static_cast<const SvxProtectItem&>(rAttr);
+ return ( bCntnt == rItem.bCntnt &&
+ bSize == rItem.bSize &&
+ bPos == rItem.bPos );
+}
+
+
+bool SvxProtectItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bValue;
+ switch(nMemberId)
+ {
+ case MID_PROTECT_CONTENT : bValue = bCntnt; break;
+ case MID_PROTECT_SIZE : bValue = bSize; break;
+ case MID_PROTECT_POSITION: bValue = bPos; break;
+ default:
+ OSL_FAIL("Wrong MemberId");
+ return false;
+ }
+
+ rVal <<= bValue;
+ return true;
+}
+
+
+bool SvxProtectItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bVal( Any2Bool(rVal) );
+ switch(nMemberId)
+ {
+ case MID_PROTECT_CONTENT : bCntnt = bVal; break;
+ case MID_PROTECT_SIZE : bSize = bVal; break;
+ case MID_PROTECT_POSITION: bPos = bVal; break;
+ default:
+ OSL_FAIL("Wrong MemberId");
+ return false;
+ }
+ return true;
+}
+
+SvxProtectItem* SvxProtectItem::Clone( SfxItemPool* ) const
+{
+ return new SvxProtectItem( *this );
+}
+
+bool SvxProtectItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ TranslateId pId = RID_SVXITEMS_PROT_CONTENT_FALSE;
+
+ if ( bCntnt )
+ pId = RID_SVXITEMS_PROT_CONTENT_TRUE;
+ rText = EditResId(pId) + cpDelim;
+ pId = RID_SVXITEMS_PROT_SIZE_FALSE;
+
+ if ( bSize )
+ pId = RID_SVXITEMS_PROT_SIZE_TRUE;
+ rText += EditResId(pId) + cpDelim;
+ pId = RID_SVXITEMS_PROT_POS_FALSE;
+
+ if ( bPos )
+ pId = RID_SVXITEMS_PROT_POS_TRUE;
+ rText += EditResId(pId);
+ return true;
+}
+
+
+void SvxProtectItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxProtectItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("content"), BAD_CAST(OString::boolean(bCntnt).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("size"), BAD_CAST(OString::boolean(bSize).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("position"), BAD_CAST(OString::boolean(bPos).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+
+SvxShadowItem::SvxShadowItem( const sal_uInt16 nId,
+ const Color *pColor, const sal_uInt16 nW,
+ const SvxShadowLocation eLoc ) :
+ SfxEnumItemInterface( nId ),
+ aShadowColor(COL_GRAY),
+ nWidth ( nW ),
+ eLocation ( eLoc )
+{
+ if ( pColor )
+ aShadowColor = *pColor;
+}
+
+
+bool SvxShadowItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ bool bConvert = 0!=(nMemberId&CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+
+ table::ShadowFormat aShadow;
+ table::ShadowLocation eSet = table::ShadowLocation_NONE;
+ switch( eLocation )
+ {
+ case SvxShadowLocation::TopLeft : eSet = table::ShadowLocation_TOP_LEFT ; break;
+ case SvxShadowLocation::TopRight : eSet = table::ShadowLocation_TOP_RIGHT ; break;
+ case SvxShadowLocation::BottomLeft : eSet = table::ShadowLocation_BOTTOM_LEFT ; break;
+ case SvxShadowLocation::BottomRight: eSet = table::ShadowLocation_BOTTOM_RIGHT; break;
+ default: ; // prevent warning
+ }
+ aShadow.Location = eSet;
+ aShadow.ShadowWidth = bConvert ? convertTwipToMm100(nWidth) : nWidth;
+ aShadow.IsTransparent = aShadowColor.IsTransparent();
+ aShadow.Color = sal_Int32(aShadowColor);
+
+ sal_Int8 nTransparence = rtl::math::round((float(255 - aShadowColor.GetAlpha()) * 100) / 255);
+
+ switch ( nMemberId )
+ {
+ case MID_LOCATION: rVal <<= aShadow.Location; break;
+ case MID_WIDTH: rVal <<= aShadow.ShadowWidth; break;
+ case MID_TRANSPARENT: rVal <<= aShadow.IsTransparent; break;
+ case MID_BG_COLOR: rVal <<= aShadow.Color; break;
+ case 0: rVal <<= aShadow; break;
+ case MID_SHADOW_TRANSPARENCE: rVal <<= nTransparence; break;
+ default: OSL_FAIL("Wrong MemberId!"); return false;
+ }
+
+ return true;
+}
+
+bool SvxShadowItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ bool bConvert = 0!=(nMemberId&CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+
+ table::ShadowFormat aShadow;
+ uno::Any aAny;
+ bool bRet = QueryValue( aAny, bConvert ? CONVERT_TWIPS : 0 ) && ( aAny >>= aShadow );
+ switch ( nMemberId )
+ {
+ case MID_LOCATION:
+ {
+ bRet = (rVal >>= aShadow.Location);
+ if ( !bRet )
+ {
+ sal_Int16 nVal = 0;
+ bRet = (rVal >>= nVal);
+ aShadow.Location = static_cast<table::ShadowLocation>(nVal);
+ }
+
+ break;
+ }
+
+ case MID_WIDTH: rVal >>= aShadow.ShadowWidth; break;
+ case MID_TRANSPARENT: rVal >>= aShadow.IsTransparent; break;
+ case MID_BG_COLOR: rVal >>= aShadow.Color; break;
+ case 0: rVal >>= aShadow; break;
+ case MID_SHADOW_TRANSPARENCE:
+ {
+ sal_Int32 nTransparence = 0;
+ if ((rVal >>= nTransparence) && !o3tl::checked_multiply<sal_Int32>(nTransparence, 255, nTransparence))
+ {
+ Color aColor(ColorTransparency, aShadow.Color);
+ aColor.SetAlpha(255 - rtl::math::round(float(nTransparence) / 100));
+ aShadow.Color = sal_Int32(aColor);
+ }
+ break;
+ }
+ default: OSL_FAIL("Wrong MemberId!"); return false;
+ }
+
+ if ( bRet )
+ {
+ switch( aShadow.Location )
+ {
+ case table::ShadowLocation_NONE : eLocation = SvxShadowLocation::NONE; break;
+ case table::ShadowLocation_TOP_LEFT : eLocation = SvxShadowLocation::TopLeft; break;
+ case table::ShadowLocation_TOP_RIGHT : eLocation = SvxShadowLocation::TopRight; break;
+ case table::ShadowLocation_BOTTOM_LEFT : eLocation = SvxShadowLocation::BottomLeft ; break;
+ case table::ShadowLocation_BOTTOM_RIGHT: eLocation = SvxShadowLocation::BottomRight; break;
+ default: ; // prevent warning
+ }
+
+ nWidth = bConvert ? o3tl::toTwips(aShadow.ShadowWidth, o3tl::Length::mm100) : aShadow.ShadowWidth;
+ Color aSet(ColorTransparency, aShadow.Color);
+ aShadowColor = aSet;
+ }
+
+ return bRet;
+}
+
+
+bool SvxShadowItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ const SvxShadowItem& rItem = static_cast<const SvxShadowItem&>(rAttr);
+ return ( ( aShadowColor == rItem.aShadowColor ) &&
+ ( nWidth == rItem.GetWidth() ) &&
+ ( eLocation == rItem.GetLocation() ) );
+}
+
+SvxShadowItem* SvxShadowItem::Clone( SfxItemPool* ) const
+{
+ return new SvxShadowItem( *this );
+}
+
+sal_uInt16 SvxShadowItem::CalcShadowSpace( SvxShadowItemSide nShadow ) const
+{
+ sal_uInt16 nSpace = 0;
+
+ switch ( nShadow )
+ {
+ case SvxShadowItemSide::TOP:
+ if ( eLocation == SvxShadowLocation::TopLeft ||
+ eLocation == SvxShadowLocation::TopRight )
+ nSpace = nWidth;
+ break;
+
+ case SvxShadowItemSide::BOTTOM:
+ if ( eLocation == SvxShadowLocation::BottomLeft ||
+ eLocation == SvxShadowLocation::BottomRight )
+ nSpace = nWidth;
+ break;
+
+ case SvxShadowItemSide::LEFT:
+ if ( eLocation == SvxShadowLocation::TopLeft ||
+ eLocation == SvxShadowLocation::BottomLeft )
+ nSpace = nWidth;
+ break;
+
+ case SvxShadowItemSide::RIGHT:
+ if ( eLocation == SvxShadowLocation::TopRight ||
+ eLocation == SvxShadowLocation::BottomRight )
+ nSpace = nWidth;
+ break;
+
+ default:
+ OSL_FAIL( "wrong shadow" );
+ }
+ return nSpace;
+}
+
+static TranslateId RID_SVXITEMS_SHADOW[] =
+{
+ RID_SVXITEMS_SHADOW_NONE,
+ RID_SVXITEMS_SHADOW_TOPLEFT,
+ RID_SVXITEMS_SHADOW_TOPRIGHT,
+ RID_SVXITEMS_SHADOW_BOTTOMLEFT,
+ RID_SVXITEMS_SHADOW_BOTTOMRIGHT
+};
+
+bool SvxShadowItem::GetPresentation
+(
+ SfxItemPresentation ePres,
+ MapUnit eCoreUnit,
+ MapUnit ePresUnit,
+ OUString& rText, const IntlWrapper& rIntl
+) const
+{
+ switch ( ePres )
+ {
+ case SfxItemPresentation::Nameless:
+ {
+ rText = ::GetColorString( aShadowColor ) + cpDelim;
+ TranslateId pId = RID_SVXITEMS_TRANSPARENT_FALSE;
+
+ if ( aShadowColor.IsTransparent() )
+ pId = RID_SVXITEMS_TRANSPARENT_TRUE;
+ rText += EditResId(pId) +
+ cpDelim +
+ GetMetricText( static_cast<tools::Long>(nWidth), eCoreUnit, ePresUnit, &rIntl ) +
+ cpDelim +
+ EditResId(RID_SVXITEMS_SHADOW[static_cast<int>(eLocation)]);
+ return true;
+ }
+ case SfxItemPresentation::Complete:
+ {
+ rText = EditResId(RID_SVXITEMS_SHADOW_COMPLETE) +
+ ::GetColorString( aShadowColor ) +
+ cpDelim;
+
+ TranslateId pId = RID_SVXITEMS_TRANSPARENT_FALSE;
+ if ( aShadowColor.IsTransparent() )
+ pId = RID_SVXITEMS_TRANSPARENT_TRUE;
+ rText += EditResId(pId) +
+ cpDelim +
+ GetMetricText( static_cast<tools::Long>(nWidth), eCoreUnit, ePresUnit, &rIntl ) +
+ " " + EditResId(GetMetricId(ePresUnit)) +
+ cpDelim +
+ EditResId(RID_SVXITEMS_SHADOW[static_cast<int>(eLocation)]);
+ return true;
+ }
+ default: ; // prevent warning
+ }
+ return false;
+}
+
+
+void SvxShadowItem::ScaleMetrics( tools::Long nMult, tools::Long nDiv )
+{
+ nWidth = static_cast<sal_uInt16>(BigInt::Scale( nWidth, nMult, nDiv ));
+}
+
+
+bool SvxShadowItem::HasMetrics() const
+{
+ return true;
+}
+
+
+sal_uInt16 SvxShadowItem::GetValueCount() const
+{
+ return sal_uInt16(SvxShadowLocation::End); // SvxShadowLocation::BottomRight + 1
+}
+
+sal_uInt16 SvxShadowItem::GetEnumValue() const
+{
+ return static_cast<sal_uInt16>(GetLocation());
+}
+
+
+void SvxShadowItem::SetEnumValue( sal_uInt16 nVal )
+{
+ SetLocation( static_cast<SvxShadowLocation>(nVal) );
+}
+
+void SvxShadowItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxShadowItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("aShadowColor"), BAD_CAST(aShadowColor.AsRGBHexString().toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nWidth"), BAD_CAST(OString::number(nWidth).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eLocation"), BAD_CAST(OString::number(static_cast<int>(eLocation)).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("presentation"), BAD_CAST(EditResId(RID_SVXITEMS_SHADOW[static_cast<int>(eLocation)]).toUtf8().getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// class SvxBoxItem ------------------------------------------------------
+
+SvxBoxItem::SvxBoxItem(const SvxBoxItem& rCopy)
+ : SfxPoolItem (rCopy)
+ , mpTopBorderLine(rCopy.mpTopBorderLine ? new SvxBorderLine(*rCopy.mpTopBorderLine) : nullptr)
+ , mpBottomBorderLine(rCopy.mpBottomBorderLine ? new SvxBorderLine(*rCopy.mpBottomBorderLine) : nullptr)
+ , mpLeftBorderLine(rCopy.mpLeftBorderLine ? new SvxBorderLine(*rCopy.mpLeftBorderLine) : nullptr)
+ , mpRightBorderLine(rCopy.mpRightBorderLine ? new SvxBorderLine(*rCopy.mpRightBorderLine) : nullptr)
+ , mnTopDistance(rCopy.mnTopDistance)
+ , mnBottomDistance(rCopy.mnBottomDistance)
+ , mnLeftDistance(rCopy.mnLeftDistance)
+ , mnRightDistance(rCopy.mnRightDistance)
+ , maTempComplexColors(rCopy.maTempComplexColors)
+ , mbRemoveAdjCellBorder(rCopy.mbRemoveAdjCellBorder)
+{
+}
+
+
+SvxBoxItem::SvxBoxItem(const sal_uInt16 nId)
+ : SfxPoolItem(nId)
+{
+}
+
+
+SvxBoxItem::~SvxBoxItem()
+{
+}
+
+void SvxBoxItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxBoxItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("top-dist"),
+ BAD_CAST(OString::number(mnTopDistance).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("bottom-dist"),
+ BAD_CAST(OString::number(mnBottomDistance).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("left-dist"),
+ BAD_CAST(OString::number(mnLeftDistance).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("right-dist"),
+ BAD_CAST(OString::number(mnRightDistance).getStr()));
+ SfxPoolItem::dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+boost::property_tree::ptree SvxBoxItem::dumpAsJSON() const
+{
+ boost::property_tree::ptree aTree;
+
+ boost::property_tree::ptree aState;
+ aState.put("top", GetTop() && !GetTop()->isEmpty());
+ aState.put("bottom", GetBottom() && !GetBottom()->isEmpty());
+ aState.put("left", GetLeft() && !GetLeft()->isEmpty());
+ aState.put("right", GetRight() && !GetRight()->isEmpty());
+
+ aTree.push_back(std::make_pair("state", aState));
+ aTree.put("commandName", ".uno:BorderOuter");
+
+ return aTree;
+}
+
+
+static bool CompareBorderLine(const std::unique_ptr<SvxBorderLine> & pBrd1, const SvxBorderLine* pBrd2)
+{
+ if( pBrd1.get() == pBrd2 )
+ return true;
+ if( pBrd1 == nullptr || pBrd2 == nullptr)
+ return false;
+ return *pBrd1 == *pBrd2;
+}
+
+
+bool SvxBoxItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ const SvxBoxItem& rBoxItem = static_cast<const SvxBoxItem&>(rAttr);
+ return (
+ (mnTopDistance == rBoxItem.mnTopDistance) &&
+ (mnBottomDistance == rBoxItem.mnBottomDistance) &&
+ (mnLeftDistance == rBoxItem.mnLeftDistance) &&
+ (mnRightDistance == rBoxItem.mnRightDistance) &&
+ (mbRemoveAdjCellBorder == rBoxItem.mbRemoveAdjCellBorder ) &&
+ (maTempComplexColors == rBoxItem.maTempComplexColors) &&
+ CompareBorderLine(mpTopBorderLine, rBoxItem.GetTop()) &&
+ CompareBorderLine(mpBottomBorderLine, rBoxItem.GetBottom()) &&
+ CompareBorderLine(mpLeftBorderLine, rBoxItem.GetLeft()) &&
+ CompareBorderLine(mpRightBorderLine, rBoxItem.GetRight()));
+}
+
+
+table::BorderLine2 SvxBoxItem::SvxLineToLine(const SvxBorderLine* pLine, bool bConvert)
+{
+ table::BorderLine2 aLine;
+ if(pLine)
+ {
+ aLine.Color = sal_Int32(pLine->GetColor());
+ aLine.InnerLineWidth = sal_uInt16( bConvert ? convertTwipToMm100(pLine->GetInWidth() ): pLine->GetInWidth() );
+ aLine.OuterLineWidth = sal_uInt16( bConvert ? convertTwipToMm100(pLine->GetOutWidth()): pLine->GetOutWidth() );
+ aLine.LineDistance = sal_uInt16( bConvert ? convertTwipToMm100(pLine->GetDistance()): pLine->GetDistance() );
+ aLine.LineStyle = sal_Int16(pLine->GetBorderLineStyle());
+ aLine.LineWidth = sal_uInt32( bConvert ? convertTwipToMm100( pLine->GetWidth( ) ) : pLine->GetWidth( ) );
+ }
+ else
+ {
+ aLine.Color = aLine.InnerLineWidth = aLine.OuterLineWidth = aLine.LineDistance = 0;
+ aLine.LineStyle = table::BorderLineStyle::NONE; // 0 is SOLID!
+ }
+ return aLine;
+}
+
+bool SvxBoxItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ bool bConvert = 0!=(nMemberId&CONVERT_TWIPS);
+ table::BorderLine2 aRetLine;
+ sal_Int16 nDist = 0;
+ bool bDistMember = false;
+ nMemberId &= ~CONVERT_TWIPS;
+ switch(nMemberId)
+ {
+ case 0:
+ {
+ // 4 Borders and 5 distances
+ uno::Sequence< uno::Any > aSeq{
+ uno::Any(SvxBoxItem::SvxLineToLine(GetLeft(), bConvert)),
+ uno::Any(SvxBoxItem::SvxLineToLine(GetRight(), bConvert)),
+ uno::Any(SvxBoxItem::SvxLineToLine(GetBottom(), bConvert)),
+ uno::Any(SvxBoxItem::SvxLineToLine(GetTop(), bConvert)),
+ uno::Any(static_cast<sal_Int32>(bConvert ? convertTwipToMm100(GetSmallestDistance()) : GetSmallestDistance())),
+ uno::Any(static_cast<sal_Int32>(bConvert ? convertTwipToMm100(mnTopDistance) : mnTopDistance)),
+ uno::Any(static_cast<sal_Int32>(bConvert ? convertTwipToMm100(mnBottomDistance) : mnBottomDistance)),
+ uno::Any(static_cast<sal_Int32>(bConvert ? convertTwipToMm100(mnLeftDistance) : mnLeftDistance)),
+ uno::Any(static_cast<sal_Int32>(bConvert ? convertTwipToMm100(mnRightDistance) : mnRightDistance))
+ };
+ rVal <<= aSeq;
+ return true;
+ }
+ case MID_LEFT_BORDER:
+ case LEFT_BORDER:
+ aRetLine = SvxBoxItem::SvxLineToLine(GetLeft(), bConvert);
+ break;
+ case MID_RIGHT_BORDER:
+ case RIGHT_BORDER:
+ aRetLine = SvxBoxItem::SvxLineToLine(GetRight(), bConvert);
+ break;
+ case MID_BOTTOM_BORDER:
+ case BOTTOM_BORDER:
+ aRetLine = SvxBoxItem::SvxLineToLine(GetBottom(), bConvert);
+ break;
+ case MID_TOP_BORDER:
+ case TOP_BORDER:
+ aRetLine = SvxBoxItem::SvxLineToLine(GetTop(), bConvert);
+ break;
+ case BORDER_DISTANCE:
+ nDist = GetSmallestDistance();
+ bDistMember = true;
+ break;
+ case TOP_BORDER_DISTANCE:
+ nDist = mnTopDistance;
+ bDistMember = true;
+ break;
+ case BOTTOM_BORDER_DISTANCE:
+ nDist = mnBottomDistance;
+ bDistMember = true;
+ break;
+ case LEFT_BORDER_DISTANCE:
+ nDist = mnLeftDistance;
+ bDistMember = true;
+ break;
+ case RIGHT_BORDER_DISTANCE:
+ nDist = mnRightDistance;
+ bDistMember = true;
+ break;
+ case MID_BORDER_BOTTOM_COLOR:
+ {
+ if (mpBottomBorderLine)
+ {
+ rVal <<= model::color::createXComplexColor(mpBottomBorderLine->getComplexColor());
+ }
+ else if (maTempComplexColors[size_t(SvxBoxItemLine::BOTTOM)].getType() != model::ColorType::Unused)
+ {
+ rVal <<= model::color::createXComplexColor(maTempComplexColors[size_t(SvxBoxItemLine::BOTTOM)]);
+ }
+ return true;
+ }
+ case MID_BORDER_LEFT_COLOR:
+ {
+ if (mpLeftBorderLine)
+ {
+ rVal <<= model::color::createXComplexColor(mpLeftBorderLine->getComplexColor());
+ }
+ else if (maTempComplexColors[size_t(SvxBoxItemLine::LEFT)].getType() != model::ColorType::Unused)
+ {
+ rVal <<= model::color::createXComplexColor(maTempComplexColors[size_t(SvxBoxItemLine::LEFT)]);
+ }
+ return true;
+ }
+ case MID_BORDER_RIGHT_COLOR:
+ {
+ if (mpRightBorderLine)
+ {
+ rVal <<= model::color::createXComplexColor(mpRightBorderLine->getComplexColor());
+ }
+ else if (maTempComplexColors[size_t(SvxBoxItemLine::RIGHT)].getType() != model::ColorType::Unused)
+ {
+ rVal <<= model::color::createXComplexColor(maTempComplexColors[size_t(SvxBoxItemLine::RIGHT)]);
+ }
+ return true;
+ }
+ case MID_BORDER_TOP_COLOR:
+ {
+ if (mpTopBorderLine)
+ {
+ rVal <<= model::color::createXComplexColor(mpTopBorderLine->getComplexColor());
+ }
+ else if (maTempComplexColors[size_t(SvxBoxItemLine::TOP)].getType() != model::ColorType::Unused)
+ {
+ rVal <<= model::color::createXComplexColor(maTempComplexColors[size_t(SvxBoxItemLine::TOP)]);
+ }
+ return true;
+ }
+ case LINE_STYLE:
+ case LINE_WIDTH:
+ // it doesn't make sense to return a value for these since it's
+ // probably ambiguous
+ return true;
+ }
+
+ if( bDistMember )
+ rVal <<= static_cast<sal_Int32>(bConvert ? convertTwipToMm100(nDist) : nDist);
+ else
+ rVal <<= aRetLine;
+
+ return true;
+}
+
+namespace
+{
+
+bool
+lcl_lineToSvxLine(const table::BorderLine& rLine, SvxBorderLine& rSvxLine, bool bConvert, bool bGuessWidth)
+{
+ rSvxLine.SetColor( Color(ColorTransparency, rLine.Color));
+ if ( bGuessWidth )
+ {
+ rSvxLine.GuessLinesWidths( rSvxLine.GetBorderLineStyle(),
+ bConvert ? o3tl::toTwips(rLine.OuterLineWidth, o3tl::Length::mm100) : rLine.OuterLineWidth,
+ bConvert ? o3tl::toTwips(rLine.InnerLineWidth, o3tl::Length::mm100) : rLine.InnerLineWidth,
+ bConvert ? o3tl::toTwips(rLine.LineDistance, o3tl::Length::mm100) : rLine.LineDistance );
+ }
+
+ bool bRet = !rSvxLine.isEmpty();
+ return bRet;
+}
+
+}
+
+
+bool SvxBoxItem::LineToSvxLine(const css::table::BorderLine& rLine, SvxBorderLine& rSvxLine, bool bConvert)
+{
+ return lcl_lineToSvxLine(rLine, rSvxLine, bConvert, true);
+}
+
+bool
+SvxBoxItem::LineToSvxLine(const css::table::BorderLine2& rLine, SvxBorderLine& rSvxLine, bool bConvert)
+{
+ SvxBorderLineStyle const nStyle =
+ (rLine.LineStyle < 0 || BORDER_LINE_STYLE_MAX < rLine.LineStyle)
+ ? SvxBorderLineStyle::SOLID // default
+ : static_cast<SvxBorderLineStyle>(rLine.LineStyle);
+
+ rSvxLine.SetBorderLineStyle( nStyle );
+
+ bool bGuessWidth = true;
+ if ( rLine.LineWidth )
+ {
+ rSvxLine.SetWidth( bConvert? o3tl::toTwips(rLine.LineWidth, o3tl::Length::mm100) : rLine.LineWidth );
+ // fdo#46112: double does not necessarily mean symmetric
+ // for backwards compatibility
+ bGuessWidth = (SvxBorderLineStyle::DOUBLE == nStyle || SvxBorderLineStyle::DOUBLE_THIN == nStyle) &&
+ (rLine.InnerLineWidth > 0) && (rLine.OuterLineWidth > 0);
+ }
+
+ return lcl_lineToSvxLine(rLine, rSvxLine, bConvert, bGuessWidth);
+}
+
+
+namespace
+{
+
+bool
+lcl_extractBorderLine(const uno::Any& rAny, table::BorderLine2& rLine)
+{
+ if (rAny >>= rLine)
+ return true;
+
+ table::BorderLine aBorderLine;
+ if (rAny >>= aBorderLine)
+ {
+ rLine.Color = aBorderLine.Color;
+ rLine.InnerLineWidth = aBorderLine.InnerLineWidth;
+ rLine.OuterLineWidth = aBorderLine.OuterLineWidth;
+ rLine.LineDistance = aBorderLine.LineDistance;
+ rLine.LineStyle = table::BorderLineStyle::SOLID;
+ return true;
+ }
+
+ return false;
+}
+
+template<typename Item, typename Line>
+bool
+lcl_setLine(const uno::Any& rAny, Item& rItem, Line nLine, const bool bConvert)
+{
+ bool bDone = false;
+ table::BorderLine2 aBorderLine;
+ if (lcl_extractBorderLine(rAny, aBorderLine))
+ {
+ SvxBorderLine aLine;
+ bool bSet = SvxBoxItem::LineToSvxLine(aBorderLine, aLine, bConvert);
+ rItem.SetLine( bSet ? &aLine : nullptr, nLine);
+ bDone = true;
+ }
+ return bDone;
+}
+
+}
+
+bool SvxBoxItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ bool bConvert = 0!=(nMemberId&CONVERT_TWIPS);
+ SvxBoxItemLine nLine = SvxBoxItemLine::TOP;
+ bool bDistMember = false;
+ nMemberId &= ~CONVERT_TWIPS;
+ switch(nMemberId)
+ {
+ case 0:
+ {
+ uno::Sequence< uno::Any > aSeq;
+ if (( rVal >>= aSeq ) && ( aSeq.getLength() == 9 ))
+ {
+ // 4 Borders and 5 distances
+ const SvxBoxItemLine aBorders[] = { SvxBoxItemLine::LEFT, SvxBoxItemLine::RIGHT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::TOP };
+ for (size_t n(0); n != std::size(aBorders); ++n)
+ {
+ if (!lcl_setLine(aSeq[n], *this, aBorders[n], bConvert))
+ return false;
+ tryMigrateComplexColor(aBorders[n]);
+ }
+
+ // WTH are the borders and the distances saved in different order?
+ SvxBoxItemLine const nLines[4] = { SvxBoxItemLine::TOP, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::LEFT, SvxBoxItemLine::RIGHT };
+ for ( sal_Int32 n = 4; n < 9; n++ )
+ {
+ sal_Int32 nDist = 0;
+ if ( aSeq[n] >>= nDist )
+ {
+ if( bConvert )
+ nDist = o3tl::toTwips(nDist, o3tl::Length::mm100);
+ if ( n == 4 )
+ SetAllDistances(nDist);
+ else
+ SetDistance( nDist, nLines[n-5] );
+ }
+ else
+ return false;
+ }
+
+ return true;
+ }
+ else
+ return false;
+ }
+ case LEFT_BORDER_DISTANCE:
+ bDistMember = true;
+ [[fallthrough]];
+ case LEFT_BORDER:
+ case MID_LEFT_BORDER:
+ nLine = SvxBoxItemLine::LEFT;
+ break;
+ case RIGHT_BORDER_DISTANCE:
+ bDistMember = true;
+ [[fallthrough]];
+ case RIGHT_BORDER:
+ case MID_RIGHT_BORDER:
+ nLine = SvxBoxItemLine::RIGHT;
+ break;
+ case BOTTOM_BORDER_DISTANCE:
+ bDistMember = true;
+ [[fallthrough]];
+ case BOTTOM_BORDER:
+ case MID_BOTTOM_BORDER:
+ nLine = SvxBoxItemLine::BOTTOM;
+ break;
+ case TOP_BORDER_DISTANCE:
+ bDistMember = true;
+ [[fallthrough]];
+ case TOP_BORDER:
+ case MID_TOP_BORDER:
+ nLine = SvxBoxItemLine::TOP;
+ break;
+ case LINE_STYLE:
+ {
+ drawing::LineStyle eDrawingStyle;
+ rVal >>= eDrawingStyle;
+ SvxBorderLineStyle eBorderStyle = SvxBorderLineStyle::NONE;
+ switch ( eDrawingStyle )
+ {
+ default:
+ case drawing::LineStyle_NONE:
+ break;
+ case drawing::LineStyle_SOLID:
+ eBorderStyle = SvxBorderLineStyle::SOLID;
+ break;
+ case drawing::LineStyle_DASH:
+ eBorderStyle = SvxBorderLineStyle::DASHED;
+ break;
+ }
+
+ // Set the line style on all borders
+ for( SvxBoxItemLine n : o3tl::enumrange<SvxBoxItemLine>() )
+ {
+ editeng::SvxBorderLine* pLine = const_cast< editeng::SvxBorderLine* >( GetLine( n ) );
+ if( pLine )
+ pLine->SetBorderLineStyle( eBorderStyle );
+ }
+ return true;
+ }
+ break;
+ case LINE_WIDTH:
+ {
+ // Set the line width on all borders
+ tools::Long nWidth(0);
+ rVal >>= nWidth;
+ if( bConvert )
+ nWidth = o3tl::toTwips(nWidth, o3tl::Length::mm100);
+
+ // Set the line Width on all borders
+ for( SvxBoxItemLine n : o3tl::enumrange<SvxBoxItemLine>() )
+ {
+ editeng::SvxBorderLine* pLine = const_cast< editeng::SvxBorderLine* >( GetLine( n ) );
+ if( pLine )
+ pLine->SetWidth( nWidth );
+ }
+ }
+ return true;
+ case MID_BORDER_BOTTOM_COLOR:
+ {
+ if (mpBottomBorderLine)
+ return mpBottomBorderLine->setComplexColorFromAny(rVal);
+ else
+ {
+ css::uno::Reference<css::util::XComplexColor> xComplexColor;
+ if (!(rVal >>= xComplexColor))
+ return false;
+
+ if (xComplexColor.is())
+ maTempComplexColors[size_t(SvxBoxItemLine::BOTTOM)] = model::color::getFromXComplexColor(xComplexColor);
+ }
+ return true;
+ }
+ case MID_BORDER_LEFT_COLOR:
+ {
+ if (mpLeftBorderLine)
+ return mpLeftBorderLine->setComplexColorFromAny(rVal);
+ else
+ {
+ css::uno::Reference<css::util::XComplexColor> xComplexColor;
+ if (!(rVal >>= xComplexColor))
+ return false;
+
+ if (xComplexColor.is())
+ maTempComplexColors[size_t(SvxBoxItemLine::LEFT)] = model::color::getFromXComplexColor(xComplexColor);
+ }
+ return true;
+ }
+ case MID_BORDER_RIGHT_COLOR:
+ {
+ if (mpRightBorderLine)
+ return mpRightBorderLine->setComplexColorFromAny(rVal);
+ else
+ {
+ css::uno::Reference<css::util::XComplexColor> xComplexColor;
+ if (!(rVal >>= xComplexColor))
+ return false;
+
+ if (xComplexColor.is())
+ maTempComplexColors[size_t(SvxBoxItemLine::RIGHT)] = model::color::getFromXComplexColor(xComplexColor);
+ }
+ return true;
+ }
+ case MID_BORDER_TOP_COLOR:
+ {
+ if (mpTopBorderLine)
+ return mpTopBorderLine->setComplexColorFromAny(rVal);
+ else
+ {
+ css::uno::Reference<css::util::XComplexColor> xComplexColor;
+ if (!(rVal >>= xComplexColor))
+ return false;
+
+ if (xComplexColor.is())
+ maTempComplexColors[size_t(SvxBoxItemLine::TOP)] = model::color::getFromXComplexColor(xComplexColor);
+ }
+ return true;
+ }
+ }
+
+ if( bDistMember || nMemberId == BORDER_DISTANCE )
+ {
+ sal_Int32 nDist = 0;
+ if(!(rVal >>= nDist))
+ return false;
+
+ {
+ if( bConvert )
+ nDist = o3tl::toTwips(nDist, o3tl::Length::mm100);
+ if( nMemberId == BORDER_DISTANCE )
+ SetAllDistances(nDist);
+ else
+ SetDistance( nDist, nLine );
+ }
+ }
+ else
+ {
+ SvxBorderLine aLine;
+ if( !rVal.hasValue() )
+ return false;
+
+ table::BorderLine2 aBorderLine;
+ if( lcl_extractBorderLine(rVal, aBorderLine) )
+ {
+ // usual struct
+ }
+ else if (rVal.getValueTypeClass() == uno::TypeClass_SEQUENCE )
+ {
+ // serialization for basic macro recording
+ uno::Reference < script::XTypeConverter > xConverter
+ ( script::Converter::create(::comphelper::getProcessComponentContext()) );
+ uno::Sequence < uno::Any > aSeq;
+ uno::Any aNew;
+ try { aNew = xConverter->convertTo( rVal, cppu::UnoType<uno::Sequence < uno::Any >>::get() ); }
+ catch (const uno::Exception&) {}
+
+ aNew >>= aSeq;
+ if (aSeq.getLength() >= 4 && aSeq.getLength() <= 6)
+ {
+ sal_Int32 nVal = 0;
+ if ( aSeq[0] >>= nVal )
+ aBorderLine.Color = nVal;
+ if ( aSeq[1] >>= nVal )
+ aBorderLine.InnerLineWidth = static_cast<sal_Int16>(nVal);
+ if ( aSeq[2] >>= nVal )
+ aBorderLine.OuterLineWidth = static_cast<sal_Int16>(nVal);
+ if ( aSeq[3] >>= nVal )
+ aBorderLine.LineDistance = static_cast<sal_Int16>(nVal);
+ if (aSeq.getLength() >= 5) // fdo#40874 added fields
+ {
+ if (aSeq[4] >>= nVal)
+ {
+ aBorderLine.LineStyle = nVal;
+ }
+ if (aSeq.getLength() >= 6)
+ {
+ if (aSeq[5] >>= nVal)
+ {
+ aBorderLine.LineWidth = nVal;
+ }
+ }
+ }
+ }
+ else
+ return false;
+ }
+ else
+ return false;
+
+ bool bSet = SvxBoxItem::LineToSvxLine(aBorderLine, aLine, bConvert);
+ SetLine(bSet ? &aLine : nullptr, nLine);
+ tryMigrateComplexColor(nLine);
+ }
+
+ return true;
+}
+
+SvxBoxItem* SvxBoxItem::Clone( SfxItemPool* ) const
+{
+ return new SvxBoxItem( *this );
+}
+
+bool SvxBoxItem::GetPresentation
+(
+ SfxItemPresentation ePres,
+ MapUnit eCoreUnit,
+ MapUnit ePresUnit,
+ OUString& rText, const IntlWrapper& rIntl
+) const
+{
+ OUString cpDelimTmp(cpDelim);
+ switch ( ePres )
+ {
+ case SfxItemPresentation::Nameless:
+ {
+ rText.clear();
+
+ if (mpTopBorderLine)
+ {
+ rText = mpTopBorderLine->GetValueString( eCoreUnit, ePresUnit, &rIntl ) + cpDelimTmp;
+ }
+ if ( !(mpTopBorderLine && mpBottomBorderLine && mpLeftBorderLine && mpRightBorderLine &&
+ *mpTopBorderLine == *mpBottomBorderLine &&
+ *mpTopBorderLine == *mpLeftBorderLine &&
+ *mpTopBorderLine == *mpRightBorderLine))
+ {
+ if (mpBottomBorderLine)
+ {
+ rText += mpBottomBorderLine->GetValueString( eCoreUnit, ePresUnit, &rIntl ) + cpDelimTmp;
+ }
+ if (mpLeftBorderLine)
+ {
+ rText += mpLeftBorderLine->GetValueString( eCoreUnit, ePresUnit, &rIntl ) + cpDelimTmp;
+ }
+ if (mpRightBorderLine)
+ {
+ rText += mpRightBorderLine->GetValueString( eCoreUnit, ePresUnit, &rIntl ) + cpDelimTmp;
+ }
+ }
+ rText += GetMetricText( static_cast<tools::Long>(mnTopDistance), eCoreUnit, ePresUnit, &rIntl );
+ if (mnTopDistance != mnBottomDistance ||
+ mnTopDistance != mnLeftDistance ||
+ mnTopDistance != mnRightDistance)
+ {
+ rText += cpDelimTmp +
+ GetMetricText( tools::Long(mnBottomDistance), eCoreUnit, ePresUnit, &rIntl ) +
+ cpDelimTmp +
+ GetMetricText( tools::Long(mnLeftDistance), eCoreUnit, ePresUnit, &rIntl ) +
+ cpDelimTmp +
+ GetMetricText( tools::Long(mnRightDistance), eCoreUnit, ePresUnit, &rIntl );
+ }
+ return true;
+ }
+ case SfxItemPresentation::Complete:
+ {
+ if (!(mpTopBorderLine || mpBottomBorderLine || mpLeftBorderLine || mpRightBorderLine))
+ {
+ rText = EditResId(RID_SVXITEMS_BORDER_NONE) + cpDelimTmp;
+ }
+ else
+ {
+ rText = EditResId(RID_SVXITEMS_BORDER_COMPLETE);
+ if (mpTopBorderLine && mpBottomBorderLine && mpLeftBorderLine && mpRightBorderLine &&
+ *mpTopBorderLine == *mpBottomBorderLine &&
+ *mpTopBorderLine == *mpLeftBorderLine &&
+ *mpTopBorderLine == *mpRightBorderLine)
+ {
+ rText += mpTopBorderLine->GetValueString( eCoreUnit, ePresUnit, &rIntl, true ) + cpDelimTmp;
+ }
+ else
+ {
+ if (mpTopBorderLine)
+ {
+ rText += EditResId(RID_SVXITEMS_BORDER_TOP) +
+ mpTopBorderLine->GetValueString( eCoreUnit, ePresUnit, &rIntl, true ) +
+ cpDelimTmp;
+ }
+ if (mpBottomBorderLine)
+ {
+ rText += EditResId(RID_SVXITEMS_BORDER_BOTTOM) +
+ mpBottomBorderLine->GetValueString( eCoreUnit, ePresUnit, &rIntl, true ) +
+ cpDelimTmp;
+ }
+ if (mpLeftBorderLine)
+ {
+ rText += EditResId(RID_SVXITEMS_BORDER_LEFT) +
+ mpLeftBorderLine->GetValueString( eCoreUnit, ePresUnit, &rIntl, true ) +
+ cpDelimTmp;
+ }
+ if (mpRightBorderLine)
+ {
+ rText += EditResId(RID_SVXITEMS_BORDER_RIGHT) +
+ mpRightBorderLine->GetValueString( eCoreUnit, ePresUnit, &rIntl, true ) +
+ cpDelimTmp;
+ }
+ }
+ }
+
+ rText += EditResId(RID_SVXITEMS_BORDER_DISTANCE);
+ if (mnTopDistance == mnBottomDistance &&
+ mnTopDistance == mnLeftDistance &&
+ mnTopDistance == mnRightDistance)
+ {
+ rText += GetMetricText(tools::Long(mnTopDistance), eCoreUnit, ePresUnit, &rIntl ) +
+ " " + EditResId(GetMetricId(ePresUnit));
+ }
+ else
+ {
+ rText += EditResId(RID_SVXITEMS_BORDER_TOP) +
+ GetMetricText(tools::Long(mnTopDistance), eCoreUnit, ePresUnit, &rIntl) +
+ " " + EditResId(GetMetricId(ePresUnit)) +
+ cpDelimTmp +
+ EditResId(RID_SVXITEMS_BORDER_BOTTOM) +
+ GetMetricText(tools::Long(mnBottomDistance), eCoreUnit, ePresUnit, &rIntl) +
+ " " + EditResId(GetMetricId(ePresUnit)) +
+ cpDelimTmp +
+ EditResId(RID_SVXITEMS_BORDER_LEFT) +
+ GetMetricText(tools::Long(mnLeftDistance), eCoreUnit, ePresUnit, &rIntl) +
+ " " + EditResId(GetMetricId(ePresUnit)) +
+ cpDelimTmp +
+ EditResId(RID_SVXITEMS_BORDER_RIGHT) +
+ GetMetricText(tools::Long(mnRightDistance), eCoreUnit, ePresUnit, &rIntl) +
+ " " + EditResId(GetMetricId(ePresUnit));
+ }
+ return true;
+ }
+ default: ; // prevent warning
+ }
+ return false;
+}
+
+
+void SvxBoxItem::ScaleMetrics( tools::Long nMult, tools::Long nDiv )
+{
+ if (mpTopBorderLine)
+ mpTopBorderLine->ScaleMetrics( nMult, nDiv );
+ if (mpBottomBorderLine)
+ mpBottomBorderLine->ScaleMetrics( nMult, nDiv );
+ if (mpLeftBorderLine)
+ mpLeftBorderLine->ScaleMetrics( nMult, nDiv );
+ if (mpRightBorderLine)
+ mpRightBorderLine->ScaleMetrics( nMult, nDiv );
+
+ mnTopDistance = static_cast<sal_Int16>(BigInt::Scale(mnTopDistance, nMult, nDiv));
+ mnBottomDistance = static_cast<sal_Int16>(BigInt::Scale(mnBottomDistance, nMult, nDiv));
+ mnLeftDistance = static_cast<sal_Int16>(BigInt::Scale(mnLeftDistance, nMult, nDiv));
+ mnRightDistance = static_cast<sal_Int16>(BigInt::Scale(mnRightDistance, nMult, nDiv));
+}
+
+
+bool SvxBoxItem::HasMetrics() const
+{
+ return true;
+}
+
+
+const SvxBorderLine *SvxBoxItem::GetLine( SvxBoxItemLine nLine ) const
+{
+ const SvxBorderLine *pRet = nullptr;
+
+ switch ( nLine )
+ {
+ case SvxBoxItemLine::TOP:
+ pRet = mpTopBorderLine.get();
+ break;
+ case SvxBoxItemLine::BOTTOM:
+ pRet = mpBottomBorderLine.get();
+ break;
+ case SvxBoxItemLine::LEFT:
+ pRet = mpLeftBorderLine.get();
+ break;
+ case SvxBoxItemLine::RIGHT:
+ pRet = mpRightBorderLine.get();
+ break;
+ default:
+ OSL_FAIL( "wrong line" );
+ break;
+ }
+
+ return pRet;
+}
+
+
+void SvxBoxItem::SetLine( const SvxBorderLine* pNew, SvxBoxItemLine nLine )
+{
+ std::unique_ptr<SvxBorderLine> pTmp( pNew ? new SvxBorderLine( *pNew ) : nullptr );
+
+ switch ( nLine )
+ {
+ case SvxBoxItemLine::TOP:
+ mpTopBorderLine = std::move(pTmp);
+ break;
+ case SvxBoxItemLine::BOTTOM:
+ mpBottomBorderLine = std::move(pTmp);
+ break;
+ case SvxBoxItemLine::LEFT:
+ mpLeftBorderLine = std::move(pTmp);
+ break;
+ case SvxBoxItemLine::RIGHT:
+ mpRightBorderLine = std::move(pTmp);
+ break;
+ default:
+ OSL_FAIL( "wrong line" );
+ }
+}
+
+
+sal_uInt16 SvxBoxItem::GetSmallestDistance() const
+{
+ // The smallest distance that is not 0 will be returned.
+ sal_uInt16 nDist = mnTopDistance;
+ if (mnBottomDistance && (!nDist || mnBottomDistance < nDist))
+ nDist = mnBottomDistance;
+ if (mnLeftDistance && (!nDist || mnLeftDistance < nDist))
+ nDist = mnLeftDistance;
+ if (mnRightDistance && (!nDist || mnRightDistance < nDist))
+ nDist = mnRightDistance;
+
+ return nDist;
+}
+
+
+sal_Int16 SvxBoxItem::GetDistance( SvxBoxItemLine nLine, bool bAllowNegative ) const
+{
+ sal_Int16 nDist = 0;
+ switch ( nLine )
+ {
+ case SvxBoxItemLine::TOP:
+ nDist = mnTopDistance;
+ break;
+ case SvxBoxItemLine::BOTTOM:
+ nDist = mnBottomDistance;
+ break;
+ case SvxBoxItemLine::LEFT:
+ nDist = mnLeftDistance;
+ break;
+ case SvxBoxItemLine::RIGHT:
+ nDist = mnRightDistance;
+ break;
+ default:
+ OSL_FAIL( "wrong line" );
+ }
+
+ if (!bAllowNegative && nDist < 0)
+ {
+ nDist = 0;
+ }
+ return nDist;
+}
+
+
+void SvxBoxItem::SetDistance( sal_Int16 nNew, SvxBoxItemLine nLine )
+{
+ switch ( nLine )
+ {
+ case SvxBoxItemLine::TOP:
+ mnTopDistance = nNew;
+ break;
+ case SvxBoxItemLine::BOTTOM:
+ mnBottomDistance = nNew;
+ break;
+ case SvxBoxItemLine::LEFT:
+ mnLeftDistance = nNew;
+ break;
+ case SvxBoxItemLine::RIGHT:
+ mnRightDistance = nNew;
+ break;
+ default:
+ OSL_FAIL( "wrong line" );
+ }
+}
+
+sal_uInt16 SvxBoxItem::CalcLineWidth( SvxBoxItemLine nLine ) const
+{
+ SvxBorderLine* pTmp = nullptr;
+ sal_uInt16 nWidth = 0;
+ switch ( nLine )
+ {
+ case SvxBoxItemLine::TOP:
+ pTmp = mpTopBorderLine.get();
+ break;
+ case SvxBoxItemLine::BOTTOM:
+ pTmp = mpBottomBorderLine.get();
+ break;
+ case SvxBoxItemLine::LEFT:
+ pTmp = mpLeftBorderLine.get();
+ break;
+ case SvxBoxItemLine::RIGHT:
+ pTmp = mpRightBorderLine.get();
+ break;
+ default:
+ OSL_FAIL( "wrong line" );
+ }
+
+ if( pTmp )
+ nWidth = pTmp->GetScaledWidth();
+
+ return nWidth;
+}
+
+sal_Int16 SvxBoxItem::CalcLineSpace( SvxBoxItemLine nLine, bool bEvenIfNoLine, bool bAllowNegative ) const
+{
+ SvxBorderLine* pTmp = nullptr;
+ sal_Int16 nDist = 0;
+ switch ( nLine )
+ {
+ case SvxBoxItemLine::TOP:
+ pTmp = mpTopBorderLine.get();
+ nDist = mnTopDistance;
+ break;
+ case SvxBoxItemLine::BOTTOM:
+ pTmp = mpBottomBorderLine.get();
+ nDist = mnBottomDistance;
+ break;
+ case SvxBoxItemLine::LEFT:
+ pTmp = mpLeftBorderLine.get();
+ nDist = mnLeftDistance;
+ break;
+ case SvxBoxItemLine::RIGHT:
+ pTmp = mpRightBorderLine.get();
+ nDist = mnRightDistance;
+ break;
+ default:
+ OSL_FAIL( "wrong line" );
+ }
+
+ if( pTmp )
+ {
+ nDist = nDist + pTmp->GetScaledWidth();
+ }
+ else if( !bEvenIfNoLine )
+ nDist = 0;
+
+ if (!bAllowNegative && nDist < 0)
+ {
+ nDist = 0;
+ }
+
+ return nDist;
+}
+
+void SvxBoxItem::tryMigrateComplexColor(SvxBoxItemLine eLine)
+{
+ if (!GetLine(eLine))
+ return;
+
+ auto nIndex = size_t(eLine);
+
+ if (maTempComplexColors[nIndex].getType() == model::ColorType::Unused)
+ return;
+
+ switch (eLine)
+ {
+ case SvxBoxItemLine::TOP:
+ mpTopBorderLine->setComplexColor(maTempComplexColors[nIndex]);
+ break;
+ case SvxBoxItemLine::BOTTOM:
+ mpBottomBorderLine->setComplexColor(maTempComplexColors[nIndex]);
+ break;
+ case SvxBoxItemLine::LEFT:
+ mpLeftBorderLine->setComplexColor(maTempComplexColors[nIndex]);
+ break;
+ case SvxBoxItemLine::RIGHT:
+ mpRightBorderLine->setComplexColor(maTempComplexColors[nIndex]);
+ break;
+ }
+
+ maTempComplexColors[nIndex] = model::ComplexColor();
+}
+
+bool SvxBoxItem::HasBorder( bool bTreatPaddingAsBorder ) const
+{
+ return CalcLineSpace( SvxBoxItemLine::BOTTOM, bTreatPaddingAsBorder )
+ || CalcLineSpace( SvxBoxItemLine::RIGHT, bTreatPaddingAsBorder )
+ || CalcLineSpace( SvxBoxItemLine::TOP, bTreatPaddingAsBorder )
+ || CalcLineSpace( SvxBoxItemLine::LEFT, bTreatPaddingAsBorder );
+}
+
+// class SvxBoxInfoItem --------------------------------------------------
+
+SvxBoxInfoItem::SvxBoxInfoItem(const sal_uInt16 nId)
+ : SfxPoolItem(nId)
+ , mbDistance(false)
+ , mbMinimumDistance(false)
+{
+ ResetFlags();
+}
+
+
+SvxBoxInfoItem::SvxBoxInfoItem( const SvxBoxInfoItem& rCopy )
+ : SfxPoolItem(rCopy)
+ , mpHorizontalLine(rCopy.mpHorizontalLine ? new SvxBorderLine(*rCopy.mpHorizontalLine) : nullptr)
+ , mpVerticalLine(rCopy.mpVerticalLine ? new SvxBorderLine(*rCopy.mpVerticalLine) : nullptr)
+ , mbEnableHorizontalLine(rCopy.mbEnableHorizontalLine)
+ , mbEnableVerticalLine(rCopy.mbEnableVerticalLine)
+ , mbDistance(rCopy.mbDistance)
+ , mbMinimumDistance (rCopy.mbMinimumDistance)
+ , mnValidFlags(rCopy.mnValidFlags)
+ , mnDefaultMinimumDistance(rCopy.mnDefaultMinimumDistance)
+{
+}
+
+SvxBoxInfoItem::~SvxBoxInfoItem()
+{
+}
+
+
+boost::property_tree::ptree SvxBoxInfoItem::dumpAsJSON() const
+{
+ boost::property_tree::ptree aTree;
+
+ boost::property_tree::ptree aState;
+ aState.put("vertical", GetVert() && !GetVert()->isEmpty());
+ aState.put("horizontal", GetHori() && !GetHori()->isEmpty());
+
+ aTree.push_back(std::make_pair("state", aState));
+ aTree.put("commandName", ".uno:BorderInner");
+
+ return aTree;
+}
+
+
+bool SvxBoxInfoItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ const SvxBoxInfoItem& rBoxInfo = static_cast<const SvxBoxInfoItem&>(rAttr);
+
+ return (mbEnableHorizontalLine == rBoxInfo.mbEnableHorizontalLine
+ && mbEnableVerticalLine == rBoxInfo.mbEnableVerticalLine
+ && mbDistance == rBoxInfo.mbDistance
+ && mbMinimumDistance == rBoxInfo.mbMinimumDistance
+ && mnValidFlags == rBoxInfo.mnValidFlags
+ && mnDefaultMinimumDistance == rBoxInfo.mnDefaultMinimumDistance
+ && CompareBorderLine(mpHorizontalLine, rBoxInfo.GetHori())
+ && CompareBorderLine(mpVerticalLine, rBoxInfo.GetVert()));
+}
+
+
+void SvxBoxInfoItem::SetLine( const SvxBorderLine* pNew, SvxBoxInfoItemLine nLine )
+{
+ std::unique_ptr<SvxBorderLine> pCopy(pNew ? new SvxBorderLine(*pNew) : nullptr);
+
+ if ( SvxBoxInfoItemLine::HORI == nLine )
+ {
+ mpHorizontalLine = std::move(pCopy);
+ }
+ else if ( SvxBoxInfoItemLine::VERT == nLine )
+ {
+ mpVerticalLine = std::move(pCopy);
+ }
+ else
+ {
+ OSL_FAIL( "wrong line" );
+ }
+}
+
+SvxBoxInfoItem* SvxBoxInfoItem::Clone( SfxItemPool* ) const
+{
+ return new SvxBoxInfoItem( *this );
+}
+
+bool SvxBoxInfoItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText.clear();
+ return false;
+}
+
+
+void SvxBoxInfoItem::ScaleMetrics( tools::Long nMult, tools::Long nDiv )
+{
+ if (mpHorizontalLine)
+ mpHorizontalLine->ScaleMetrics(nMult, nDiv);
+ if (mpVerticalLine)
+ mpVerticalLine->ScaleMetrics(nMult, nDiv);
+ mnDefaultMinimumDistance = sal_uInt16(BigInt::Scale(mnDefaultMinimumDistance, nMult, nDiv));
+}
+
+
+bool SvxBoxInfoItem::HasMetrics() const
+{
+ return true;
+}
+
+
+void SvxBoxInfoItem::ResetFlags()
+{
+ mnValidFlags = static_cast<SvxBoxInfoItemValidFlags>(0x7F); // all valid except Disable
+}
+
+bool SvxBoxInfoItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ bool bConvert = 0 != (nMemberId & CONVERT_TWIPS);
+ table::BorderLine2 aRetLine;
+ sal_Int16 nVal=0;
+ bool bIntMember = false;
+ nMemberId &= ~CONVERT_TWIPS;
+ switch(nMemberId)
+ {
+ case 0:
+ {
+ // 2 BorderLines, flags, valid flags and distance
+ if ( IsTable() )
+ nVal |= 0x01;
+ if ( IsDist() )
+ nVal |= 0x02;
+ if ( IsMinDist() )
+ nVal |= 0x04;
+ css::uno::Sequence< css::uno::Any > aSeq{
+ uno::Any(SvxBoxItem::SvxLineToLine(mpHorizontalLine.get(), bConvert)),
+ uno::Any(SvxBoxItem::SvxLineToLine(mpVerticalLine.get(), bConvert)),
+ uno::Any(nVal),
+ uno::Any(static_cast<sal_Int16>(mnValidFlags)),
+ uno::Any(static_cast<sal_Int32>(bConvert ? convertTwipToMm100(GetDefDist()) : GetDefDist()))
+ };
+ rVal <<= aSeq;
+ return true;
+ }
+
+ case MID_HORIZONTAL:
+ aRetLine = SvxBoxItem::SvxLineToLine(mpHorizontalLine.get(), bConvert);
+ break;
+ case MID_VERTICAL:
+ aRetLine = SvxBoxItem::SvxLineToLine(mpVerticalLine.get(), bConvert);
+ break;
+ case MID_FLAGS:
+ bIntMember = true;
+ if ( IsTable() )
+ nVal |= 0x01;
+ if ( IsDist() )
+ nVal |= 0x02;
+ if ( IsMinDist() )
+ nVal |= 0x04;
+ rVal <<= nVal;
+ break;
+ case MID_VALIDFLAGS:
+ bIntMember = true;
+ rVal <<= static_cast<sal_Int16>(mnValidFlags);
+ break;
+ case MID_DISTANCE:
+ bIntMember = true;
+ rVal <<= static_cast<sal_Int32>(bConvert ? convertTwipToMm100(GetDefDist()) : GetDefDist());
+ break;
+ default: OSL_FAIL("Wrong MemberId!"); return false;
+ }
+
+ if( !bIntMember )
+ rVal <<= aRetLine;
+
+ return true;
+}
+
+
+bool SvxBoxInfoItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ bool bConvert = 0!=(nMemberId&CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet;
+ switch(nMemberId)
+ {
+ case 0:
+ {
+ css::uno::Sequence< css::uno::Any > aSeq;
+ if (( rVal >>= aSeq ) && ( aSeq.getLength() == 5 ))
+ {
+ // 2 BorderLines, flags, valid flags and distance
+ if (!lcl_setLine(aSeq[0], *this, SvxBoxInfoItemLine::HORI, bConvert))
+ return false;
+ if (!lcl_setLine(aSeq[1], *this, SvxBoxInfoItemLine::VERT, bConvert))
+ return false;
+
+ sal_Int16 nFlags( 0 );
+ sal_Int32 nVal( 0 );
+ if ( aSeq[2] >>= nFlags )
+ {
+ SetTable ( ( nFlags & 0x01 ) != 0 );
+ SetDist ( ( nFlags & 0x02 ) != 0 );
+ SetMinDist( ( nFlags & 0x04 ) != 0 );
+ }
+ else
+ return false;
+ if ( aSeq[3] >>= nFlags )
+ mnValidFlags = static_cast<SvxBoxInfoItemValidFlags>(nFlags);
+ else
+ return false;
+ if (( aSeq[4] >>= nVal ) && ( nVal >= 0 ))
+ {
+ if( bConvert )
+ nVal = o3tl::toTwips(nVal, o3tl::Length::mm100);
+ SetDefDist( nVal );
+ }
+ }
+ return true;
+ }
+
+ case MID_HORIZONTAL:
+ case MID_VERTICAL:
+ {
+ if( !rVal.hasValue() )
+ return false;
+
+ table::BorderLine2 aBorderLine;
+ if( lcl_extractBorderLine(rVal, aBorderLine) )
+ {
+ // usual struct
+ }
+ else if (rVal.getValueTypeClass() == uno::TypeClass_SEQUENCE )
+ {
+ // serialization for basic macro recording
+ uno::Reference < script::XTypeConverter > xConverter( script::Converter::create(::comphelper::getProcessComponentContext()) );
+ uno::Any aNew;
+ uno::Sequence < uno::Any > aSeq;
+ try { aNew = xConverter->convertTo( rVal, cppu::UnoType<uno::Sequence < uno::Any >>::get() ); }
+ catch (const uno::Exception&) {}
+
+ if ((aNew >>= aSeq) &&
+ aSeq.getLength() >= 4 && aSeq.getLength() <= 6)
+ {
+ sal_Int32 nVal = 0;
+ if ( aSeq[0] >>= nVal )
+ aBorderLine.Color = nVal;
+ if ( aSeq[1] >>= nVal )
+ aBorderLine.InnerLineWidth = static_cast<sal_Int16>(nVal);
+ if ( aSeq[2] >>= nVal )
+ aBorderLine.OuterLineWidth = static_cast<sal_Int16>(nVal);
+ if ( aSeq[3] >>= nVal )
+ aBorderLine.LineDistance = static_cast<sal_Int16>(nVal);
+ if (aSeq.getLength() >= 5) // fdo#40874 added fields
+ {
+ if (aSeq[4] >>= nVal)
+ {
+ aBorderLine.LineStyle = nVal;
+ }
+ if (aSeq.getLength() >= 6)
+ {
+ if (aSeq[5] >>= nVal)
+ {
+ aBorderLine.LineWidth = nVal;
+ }
+ }
+ }
+ }
+ else
+ return false;
+ }
+ else if (rVal.getValueType() == cppu::UnoType<css::uno::Sequence < sal_Int16 >>::get() )
+ {
+ // serialization for basic macro recording
+ css::uno::Sequence < sal_Int16 > aSeq;
+ rVal >>= aSeq;
+ if (aSeq.getLength() >= 4 && aSeq.getLength() <= 6)
+ {
+ aBorderLine.Color = aSeq[0];
+ aBorderLine.InnerLineWidth = aSeq[1];
+ aBorderLine.OuterLineWidth = aSeq[2];
+ aBorderLine.LineDistance = aSeq[3];
+ if (aSeq.getLength() >= 5) // fdo#40874 added fields
+ {
+ aBorderLine.LineStyle = aSeq[4];
+ if (aSeq.getLength() >= 6)
+ {
+ aBorderLine.LineWidth = aSeq[5];
+ }
+ }
+ }
+ else
+ return false;
+ }
+ else
+ return false;
+
+ SvxBorderLine aLine;
+ bool bSet = SvxBoxItem::LineToSvxLine(aBorderLine, aLine, bConvert);
+ if ( bSet )
+ SetLine( &aLine, nMemberId == MID_HORIZONTAL ? SvxBoxInfoItemLine::HORI : SvxBoxInfoItemLine::VERT );
+ break;
+ }
+ case MID_FLAGS:
+ {
+ sal_Int16 nFlags = sal_Int16();
+ bRet = (rVal >>= nFlags);
+ if ( bRet )
+ {
+ SetTable ( ( nFlags & 0x01 ) != 0 );
+ SetDist ( ( nFlags & 0x02 ) != 0 );
+ SetMinDist( ( nFlags & 0x04 ) != 0 );
+ }
+
+ break;
+ }
+ case MID_VALIDFLAGS:
+ {
+ sal_Int16 nFlags = sal_Int16();
+ bRet = (rVal >>= nFlags);
+ if ( bRet )
+ mnValidFlags = static_cast<SvxBoxInfoItemValidFlags>(nFlags);
+ break;
+ }
+ case MID_DISTANCE:
+ {
+ sal_Int32 nVal = 0;
+ bRet = (rVal >>= nVal);
+ if ( bRet && nVal>=0 )
+ {
+ if( bConvert )
+ nVal = o3tl::toTwips(nVal, o3tl::Length::mm100);
+ SetDefDist( static_cast<sal_uInt16>(nVal) );
+ }
+ break;
+ }
+ default: OSL_FAIL("Wrong MemberId!"); return false;
+ }
+
+ return true;
+}
+
+
+namespace editeng
+{
+
+void BorderDistanceFromWord(bool bFromEdge, sal_Int32& nMargin, sal_Int32& nBorderDistance,
+ sal_Int32 nBorderWidth)
+{
+ // See https://wiki.openoffice.org/wiki/Writer/MSInteroperability/PageBorder
+
+ sal_Int32 nNewMargin = nMargin;
+ sal_Int32 nNewBorderDistance = nBorderDistance;
+
+ if (bFromEdge)
+ {
+ nNewMargin = nBorderDistance;
+ nNewBorderDistance = nMargin - nBorderDistance - nBorderWidth;
+ }
+ else
+ {
+ nNewMargin -= nBorderDistance + nBorderWidth;
+ }
+
+ // Ensure correct distance from page edge to text in cases not supported by us:
+ // when border is outside entire page area (!bFromEdge && BorderDistance > Margin),
+ // and when border is inside page body area (bFromEdge && BorderDistance > Margin)
+ if (nNewMargin < 0)
+ {
+ nNewMargin = 0;
+ nNewBorderDistance = std::max<sal_Int32>(nMargin - nBorderWidth, 0);
+ }
+ else if (nNewBorderDistance < 0)
+ {
+ nNewMargin = nMargin;
+ }
+
+ nMargin = nNewMargin;
+ nBorderDistance = nNewBorderDistance;
+}
+
+// Heuristics to decide if we need to use "from edge" offset of borders
+//
+// There are two cases when we can safely use "from text" or "from edge" offset without distorting
+// border position (modulo rounding errors):
+// 1. When distance of all borders from text is no greater than 31 pt, we use "from text"
+// 2. Otherwise, if distance of all borders from edge is no greater than 31 pt, we use "from edge"
+// In all other cases, the position of borders would be distorted on export, because Word doesn't
+// support the offset of >31 pts (https://msdn.microsoft.com/en-us/library/ff533820), and we need
+// to decide which type of offset would provide less wrong result (i.e., the result would look
+// closer to original). Here, we just check sum of distances from text to borders, and if it is
+// less than sum of distances from borders to edges. The alternative would be to compare total areas
+// between text-and-borders and between borders-and-edges (taking into account different lengths of
+// borders, and visual impact of that).
+void BorderDistancesToWord(const SvxBoxItem& rBox, const WordPageMargins& rMargins,
+ WordBorderDistances& rDistances)
+{
+ // Use signed sal_Int32 that can hold sal_uInt16, to prevent overflow at subtraction below
+ const sal_Int32 nT = rBox.GetDistance(SvxBoxItemLine::TOP, /*bAllowNegative=*/true);
+ const sal_Int32 nL = rBox.GetDistance(SvxBoxItemLine::LEFT, /*bAllowNegative=*/true);
+ const sal_Int32 nB = rBox.GetDistance(SvxBoxItemLine::BOTTOM, /*bAllowNegative=*/true);
+ const sal_Int32 nR = rBox.GetDistance(SvxBoxItemLine::RIGHT, /*bAllowNegative=*/true);
+
+ // Only take into account existing borders
+ const SvxBorderLine* pLnT = rBox.GetLine(SvxBoxItemLine::TOP);
+ const SvxBorderLine* pLnL = rBox.GetLine(SvxBoxItemLine::LEFT);
+ const SvxBorderLine* pLnB = rBox.GetLine(SvxBoxItemLine::BOTTOM);
+ const SvxBorderLine* pLnR = rBox.GetLine(SvxBoxItemLine::RIGHT);
+
+ // We need to take border widths into account
+ const tools::Long nWidthT = pLnT ? pLnT->GetScaledWidth() : 0;
+ const tools::Long nWidthL = pLnL ? pLnL->GetScaledWidth() : 0;
+ const tools::Long nWidthB = pLnB ? pLnB->GetScaledWidth() : 0;
+ const tools::Long nWidthR = pLnR ? pLnR->GetScaledWidth() : 0;
+
+ // Resulting distances from text to borders
+ const sal_Int32 nT2BT = pLnT ? nT : 0;
+ const sal_Int32 nT2BL = pLnL ? nL : 0;
+ const sal_Int32 nT2BB = pLnB ? nB : 0;
+ const sal_Int32 nT2BR = pLnR ? nR : 0;
+
+ // Resulting distances from edge to borders
+ const sal_Int32 nE2BT = pLnT ? std::max<sal_Int32>(rMargins.nTop - nT - nWidthT, 0) : 0;
+ const sal_Int32 nE2BL = pLnL ? std::max<sal_Int32>(rMargins.nLeft - nL - nWidthL, 0) : 0;
+ const sal_Int32 nE2BB = pLnB ? std::max<sal_Int32>(rMargins.nBottom - nB - nWidthB, 0) : 0;
+ const sal_Int32 nE2BR = pLnR ? std::max<sal_Int32>(rMargins.nRight - nR - nWidthR, 0) : 0;
+
+ const sal_Int32 n32pt = 32 * 20;
+ // 1. If all borders are in range of 31 pts from text
+ if (nT2BT >= 0 && nT2BT < n32pt && nT2BL >= 0 && nT2BL < n32pt && nT2BB >= 0 && nT2BB < n32pt && nT2BR >= 0 && nT2BR < n32pt)
+ {
+ rDistances.bFromEdge = false;
+ }
+ else
+ {
+ // 2. If all borders are in range of 31 pts from edge
+ if (nE2BT < n32pt && nE2BL < n32pt && nE2BB < n32pt && nE2BR < n32pt)
+ {
+ rDistances.bFromEdge = true;
+ }
+ else
+ {
+ // Let's try to guess which would be the best approximation
+ rDistances.bFromEdge =
+ (nT2BT + nT2BL + nT2BB + nT2BR) > (nE2BT + nE2BL + nE2BB + nE2BR);
+ }
+ }
+
+ if (rDistances.bFromEdge)
+ {
+ rDistances.nTop = sal::static_int_cast<sal_uInt16>(nE2BT);
+ rDistances.nLeft = sal::static_int_cast<sal_uInt16>(nE2BL);
+ rDistances.nBottom = sal::static_int_cast<sal_uInt16>(nE2BB);
+ rDistances.nRight = sal::static_int_cast<sal_uInt16>(nE2BR);
+ }
+ else
+ {
+ rDistances.nTop = sal::static_int_cast<sal_uInt16>(nT2BT);
+ rDistances.nLeft = sal::static_int_cast<sal_uInt16>(nT2BL);
+ rDistances.nBottom = sal::static_int_cast<sal_uInt16>(nT2BB);
+ rDistances.nRight = sal::static_int_cast<sal_uInt16>(nT2BR);
+ }
+}
+
+}
+
+// class SvxFormatBreakItem -------------------------------------------------
+
+bool SvxFormatBreakItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ return GetValue() == static_cast<const SvxFormatBreakItem&>( rAttr ).GetValue();
+}
+
+
+bool SvxFormatBreakItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText = GetValueTextByPos( GetEnumValue() );
+ return true;
+}
+
+OUString SvxFormatBreakItem::GetValueTextByPos( sal_uInt16 nPos )
+{
+ static TranslateId RID_SVXITEMS_BREAK[] =
+ {
+ RID_SVXITEMS_BREAK_NONE,
+ RID_SVXITEMS_BREAK_COLUMN_BEFORE,
+ RID_SVXITEMS_BREAK_COLUMN_AFTER,
+ RID_SVXITEMS_BREAK_COLUMN_BOTH,
+ RID_SVXITEMS_BREAK_PAGE_BEFORE,
+ RID_SVXITEMS_BREAK_PAGE_AFTER,
+ RID_SVXITEMS_BREAK_PAGE_BOTH
+ };
+ static_assert(std::size(RID_SVXITEMS_BREAK) == size_t(SvxBreak::End), "unexpected size");
+ assert(nPos < sal_uInt16(SvxBreak::End) && "enum overflow!");
+ return EditResId(RID_SVXITEMS_BREAK[nPos]);
+}
+
+bool SvxFormatBreakItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) const
+{
+ style::BreakType eBreak = style::BreakType_NONE;
+ switch ( GetBreak() )
+ {
+ case SvxBreak::ColumnBefore: eBreak = style::BreakType_COLUMN_BEFORE; break;
+ case SvxBreak::ColumnAfter: eBreak = style::BreakType_COLUMN_AFTER ; break;
+ case SvxBreak::ColumnBoth: eBreak = style::BreakType_COLUMN_BOTH ; break;
+ case SvxBreak::PageBefore: eBreak = style::BreakType_PAGE_BEFORE ; break;
+ case SvxBreak::PageAfter: eBreak = style::BreakType_PAGE_AFTER ; break;
+ case SvxBreak::PageBoth: eBreak = style::BreakType_PAGE_BOTH ; break;
+ default: ; // prevent warning
+ }
+ rVal <<= eBreak;
+ return true;
+}
+
+bool SvxFormatBreakItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/ )
+{
+ style::BreakType nBreak;
+
+ if(!(rVal >>= nBreak))
+ {
+ sal_Int32 nValue = 0;
+ if(!(rVal >>= nValue))
+ return false;
+
+ nBreak = static_cast<style::BreakType>(nValue);
+ }
+
+ SvxBreak eBreak = SvxBreak::NONE;
+ switch( nBreak )
+ {
+ case style::BreakType_COLUMN_BEFORE: eBreak = SvxBreak::ColumnBefore; break;
+ case style::BreakType_COLUMN_AFTER: eBreak = SvxBreak::ColumnAfter; break;
+ case style::BreakType_COLUMN_BOTH: eBreak = SvxBreak::ColumnBoth; break;
+ case style::BreakType_PAGE_BEFORE: eBreak = SvxBreak::PageBefore; break;
+ case style::BreakType_PAGE_AFTER: eBreak = SvxBreak::PageAfter; break;
+ case style::BreakType_PAGE_BOTH: eBreak = SvxBreak::PageBoth; break;
+ default: ; // prevent warning
+ }
+ SetValue(eBreak);
+
+ return true;
+}
+
+SvxFormatBreakItem* SvxFormatBreakItem::Clone( SfxItemPool* ) const
+{
+ return new SvxFormatBreakItem( *this );
+}
+
+sal_uInt16 SvxFormatBreakItem::GetValueCount() const
+{
+ return sal_uInt16(SvxBreak::End); // SvxBreak::PageBoth + 1
+}
+
+SvxFormatKeepItem* SvxFormatKeepItem::Clone( SfxItemPool* ) const
+{
+ return new SvxFormatKeepItem( *this );
+}
+
+bool SvxFormatKeepItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+ ) const
+{
+ TranslateId pId = RID_SVXITEMS_FMTKEEP_FALSE;
+
+ if ( GetValue() )
+ pId = RID_SVXITEMS_FMTKEEP_TRUE;
+ rText = EditResId(pId);
+ return true;
+}
+
+void SvxFormatKeepItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxFormatKeepItem"));
+
+ SfxBoolItem::dumpAsXml(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SvxLineItem::SvxLineItem( const sal_uInt16 nId ) :
+ SfxPoolItem ( nId )
+{
+}
+
+
+SvxLineItem::SvxLineItem( const SvxLineItem& rCpy ) :
+ SfxPoolItem ( rCpy ),
+ pLine(rCpy.pLine ? new SvxBorderLine( *rCpy.pLine ) : nullptr)
+{
+}
+
+
+SvxLineItem::~SvxLineItem()
+{
+}
+
+
+bool SvxLineItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ return CompareBorderLine(pLine, static_cast<const SvxLineItem&>(rAttr).GetLine());
+}
+
+SvxLineItem* SvxLineItem::Clone( SfxItemPool* ) const
+{
+ return new SvxLineItem( *this );
+}
+
+bool SvxLineItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemId ) const
+{
+ bool bConvert = 0!=(nMemId&CONVERT_TWIPS);
+ nMemId &= ~CONVERT_TWIPS;
+ if ( nMemId == 0 )
+ {
+ rVal <<= SvxBoxItem::SvxLineToLine(pLine.get(), bConvert);
+ return true;
+ }
+ else if ( pLine )
+ {
+ switch ( nMemId )
+ {
+ case MID_FG_COLOR: rVal <<= pLine->GetColor(); break;
+ case MID_OUTER_WIDTH: rVal <<= sal_Int32(pLine->GetOutWidth()); break;
+ case MID_INNER_WIDTH: rVal <<= sal_Int32(pLine->GetInWidth( )); break;
+ case MID_DISTANCE: rVal <<= sal_Int32(pLine->GetDistance()); break;
+ default:
+ OSL_FAIL( "Wrong MemberId" );
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+bool SvxLineItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemId )
+{
+ bool bConvert = 0!=(nMemId&CONVERT_TWIPS);
+ nMemId &= ~CONVERT_TWIPS;
+ sal_Int32 nVal = 0;
+ if ( nMemId == 0 )
+ {
+ table::BorderLine2 aLine;
+ if ( lcl_extractBorderLine(rVal, aLine) )
+ {
+ if ( !pLine )
+ pLine.reset( new SvxBorderLine );
+ if( !SvxBoxItem::LineToSvxLine(aLine, *pLine, bConvert) )
+ pLine.reset();
+ return true;
+ }
+ return false;
+ }
+ else if ( rVal >>= nVal )
+ {
+ if ( !pLine )
+ pLine.reset( new SvxBorderLine );
+
+ switch ( nMemId )
+ {
+ case MID_FG_COLOR: pLine->SetColor( Color(ColorTransparency, nVal) ); break;
+ case MID_LINE_STYLE:
+ pLine->SetBorderLineStyle(static_cast<SvxBorderLineStyle>(nVal));
+ break;
+ default:
+ OSL_FAIL( "Wrong MemberId" );
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+
+bool SvxLineItem::GetPresentation
+(
+ SfxItemPresentation ePres,
+ MapUnit eCoreUnit,
+ MapUnit ePresUnit,
+ OUString& rText, const IntlWrapper& rIntl
+) const
+{
+ rText.clear();
+
+ if ( pLine )
+ rText = pLine->GetValueString( eCoreUnit, ePresUnit, &rIntl,
+ (SfxItemPresentation::Complete == ePres) );
+ return true;
+}
+
+
+void SvxLineItem::ScaleMetrics( tools::Long nMult, tools::Long nDiv )
+{
+ if ( pLine ) pLine->ScaleMetrics( nMult, nDiv );
+}
+
+
+bool SvxLineItem::HasMetrics() const
+{
+ return true;
+}
+
+
+void SvxLineItem::SetLine( const SvxBorderLine* pNew )
+{
+ pLine.reset( pNew ? new SvxBorderLine( *pNew ) : nullptr );
+}
+
+SvxBrushItem::SvxBrushItem(sal_uInt16 _nWhich)
+ : SfxPoolItem(_nWhich)
+ , aColor(COL_TRANSPARENT)
+ , aFilterColor(COL_TRANSPARENT)
+ , nShadingValue(ShadingPattern::CLEAR)
+ , nGraphicTransparency(0)
+ , eGraphicPos(GPOS_NONE)
+ , bLoadAgain(true)
+{
+}
+
+SvxBrushItem::SvxBrushItem(const Color& rColor, sal_uInt16 _nWhich)
+ : SfxPoolItem(_nWhich)
+ , aColor(rColor)
+ , aFilterColor(COL_TRANSPARENT)
+ , nShadingValue(ShadingPattern::CLEAR)
+ , nGraphicTransparency(0)
+ , eGraphicPos(GPOS_NONE)
+ , bLoadAgain(true)
+{
+}
+
+SvxBrushItem::SvxBrushItem(Color const& rColor, model::ComplexColor const& rComplexColor, sal_uInt16 nWhich)
+ : SfxPoolItem(nWhich)
+ , aColor(rColor)
+ , maComplexColor(rComplexColor)
+ , aFilterColor(COL_TRANSPARENT)
+ , nShadingValue(ShadingPattern::CLEAR)
+ , nGraphicTransparency(0)
+ , eGraphicPos(GPOS_NONE)
+ , bLoadAgain(true)
+{
+}
+
+SvxBrushItem::SvxBrushItem(const Graphic& rGraphic, SvxGraphicPosition ePos, sal_uInt16 _nWhich)
+ : SfxPoolItem(_nWhich)
+ , aColor(COL_TRANSPARENT)
+ , aFilterColor(COL_TRANSPARENT)
+ , nShadingValue(ShadingPattern::CLEAR)
+ , xGraphicObject(new GraphicObject(rGraphic))
+ , nGraphicTransparency(0)
+ , eGraphicPos((GPOS_NONE != ePos) ? ePos : GPOS_MM)
+ , bLoadAgain(true)
+{
+ DBG_ASSERT( GPOS_NONE != ePos, "SvxBrushItem-Ctor with GPOS_NONE == ePos" );
+}
+
+SvxBrushItem::SvxBrushItem(const GraphicObject& rGraphicObj, SvxGraphicPosition ePos, sal_uInt16 _nWhich)
+ : SfxPoolItem(_nWhich)
+ , aColor(COL_TRANSPARENT)
+ , aFilterColor(COL_TRANSPARENT)
+ , nShadingValue(ShadingPattern::CLEAR)
+ , xGraphicObject(new GraphicObject(rGraphicObj))
+ , nGraphicTransparency(0)
+ , eGraphicPos((GPOS_NONE != ePos) ? ePos : GPOS_MM)
+ , bLoadAgain(true)
+{
+ DBG_ASSERT( GPOS_NONE != ePos, "SvxBrushItem-Ctor with GPOS_NONE == ePos" );
+}
+
+SvxBrushItem::SvxBrushItem(OUString aLink, OUString aFilter,
+ SvxGraphicPosition ePos, sal_uInt16 _nWhich)
+ : SfxPoolItem(_nWhich)
+ , aColor(COL_TRANSPARENT)
+ , aFilterColor(COL_TRANSPARENT)
+ , nShadingValue(ShadingPattern::CLEAR)
+ , nGraphicTransparency(0)
+ , maStrLink(std::move(aLink))
+ , maStrFilter(std::move(aFilter))
+ , eGraphicPos((GPOS_NONE != ePos) ? ePos : GPOS_MM)
+ , bLoadAgain(true)
+{
+ DBG_ASSERT( GPOS_NONE != ePos, "SvxBrushItem-Ctor with GPOS_NONE == ePos" );
+}
+
+SvxBrushItem::SvxBrushItem(const SvxBrushItem& rItem)
+ : SfxPoolItem(rItem)
+ , aColor(rItem.aColor)
+ , maComplexColor(rItem.maComplexColor)
+ , aFilterColor(rItem.aFilterColor)
+ , nShadingValue(rItem.nShadingValue)
+ , xGraphicObject(rItem.xGraphicObject ? new GraphicObject(*rItem.xGraphicObject) : nullptr)
+ , nGraphicTransparency(rItem.nGraphicTransparency)
+ , maStrLink(rItem.maStrLink)
+ , maStrFilter(rItem.maStrFilter)
+ , eGraphicPos(rItem.eGraphicPos)
+ , bLoadAgain(rItem.bLoadAgain)
+{
+}
+
+SvxBrushItem::SvxBrushItem(SvxBrushItem&& rItem)
+ : SfxPoolItem(std::move(rItem))
+ , aColor(std::move(rItem.aColor))
+ , maComplexColor(std::move(rItem.maComplexColor))
+ , aFilterColor(std::move(rItem.aFilterColor))
+ , nShadingValue(std::move(rItem.nShadingValue))
+ , xGraphicObject(std::move(rItem.xGraphicObject))
+ , nGraphicTransparency(std::move(rItem.nGraphicTransparency))
+ , maStrLink(std::move(rItem.maStrLink))
+ , maStrFilter(std::move(rItem.maStrFilter))
+ , eGraphicPos(std::move(rItem.eGraphicPos))
+ , bLoadAgain(std::move(rItem.bLoadAgain))
+{
+}
+
+SvxBrushItem::~SvxBrushItem()
+{
+}
+
+bool SvxBrushItem::isUsed() const
+{
+ if (GPOS_NONE != GetGraphicPos())
+ {
+ // graphic used
+ return true;
+ }
+ else if (!GetColor().IsFullyTransparent())
+ {
+ // color used
+ return true;
+ }
+
+ return false;
+}
+
+
+static sal_Int8 lcl_PercentToTransparency(tools::Long nPercent)
+{
+ // 0xff must not be returned!
+ return sal_Int8(nPercent ? (50 + 0xfe * nPercent) / 100 : 0);
+}
+
+
+sal_Int8 SvxBrushItem::TransparencyToPercent(sal_Int32 nTrans)
+{
+ return static_cast<sal_Int8>((nTrans * 100 + 127) / 254);
+}
+
+
+bool SvxBrushItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch( nMemberId)
+ {
+ case MID_BACK_COLOR:
+ rVal <<= aColor;
+ break;
+ case MID_BACK_COLOR_R_G_B:
+ rVal <<= aColor.GetRGBColor();
+ break;
+ case MID_BACK_COLOR_TRANSPARENCY:
+ rVal <<= SvxBrushItem::TransparencyToPercent(255 - aColor.GetAlpha());
+ break;
+
+ case MID_BACKGROUND_COMPLEX_COLOR:
+ {
+ auto xComplexColor = model::color::createXComplexColor(maComplexColor);
+ rVal <<= xComplexColor;
+ break;
+ }
+ break;
+
+ case MID_GRAPHIC_POSITION:
+ rVal <<= static_cast<style::GraphicLocation>(static_cast<sal_Int16>(eGraphicPos));
+ break;
+
+ case MID_GRAPHIC_TRANSPARENT:
+ rVal <<= ( aColor.GetAlpha() == 0 );
+ break;
+
+ case MID_GRAPHIC_URL:
+ case MID_GRAPHIC:
+ {
+ uno::Reference<graphic::XGraphic> xGraphic;
+ if (!maStrLink.isEmpty())
+ {
+ Graphic aGraphic(vcl::graphic::loadFromURL(maStrLink));
+ xGraphic = aGraphic.GetXGraphic();
+ }
+ else if (xGraphicObject)
+ {
+ xGraphic = xGraphicObject->GetGraphic().GetXGraphic();
+ }
+ rVal <<= xGraphic;
+ }
+ break;
+
+ case MID_GRAPHIC_FILTER:
+ {
+ rVal <<= maStrFilter;
+ }
+ break;
+
+ case MID_GRAPHIC_TRANSPARENCY:
+ rVal <<= nGraphicTransparency;
+ break;
+
+ case MID_SHADING_VALUE:
+ {
+ rVal <<= nShadingValue;
+ }
+ break;
+ }
+
+ return true;
+}
+
+
+bool SvxBrushItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch( nMemberId)
+ {
+ case MID_BACK_COLOR:
+ case MID_BACK_COLOR_R_G_B:
+ {
+ Color aNewCol;
+ if ( !( rVal >>= aNewCol ) )
+ return false;
+ if(MID_BACK_COLOR_R_G_B == nMemberId)
+ {
+ aNewCol.SetAlpha(aColor.GetAlpha());
+ }
+ aColor = aNewCol;
+ }
+ break;
+ case MID_BACK_COLOR_TRANSPARENCY:
+ {
+ sal_Int32 nTrans = 0;
+ if ( !( rVal >>= nTrans ) || nTrans < 0 || nTrans > 100 )
+ return false;
+ aColor.SetAlpha(255 - lcl_PercentToTransparency(nTrans));
+ }
+ break;
+
+ case MID_BACKGROUND_COMPLEX_COLOR:
+ {
+ css::uno::Reference<css::util::XComplexColor> xComplexColor;
+ if (!(rVal >>= xComplexColor))
+ return false;
+
+ if (xComplexColor.is())
+ maComplexColor = model::color::getFromXComplexColor(xComplexColor);
+ }
+ break;
+
+ case MID_GRAPHIC_POSITION:
+ {
+ style::GraphicLocation eLocation;
+ if ( !( rVal>>=eLocation ) )
+ {
+ sal_Int32 nValue = 0;
+ if ( !( rVal >>= nValue ) )
+ return false;
+ eLocation = static_cast<style::GraphicLocation>(nValue);
+ }
+ SetGraphicPos( static_cast<SvxGraphicPosition>(static_cast<sal_uInt16>(eLocation)) );
+ }
+ break;
+
+ case MID_GRAPHIC_TRANSPARENT:
+ aColor.SetAlpha( Any2Bool( rVal ) ? 0 : 255 );
+ break;
+
+ case MID_GRAPHIC_URL:
+ case MID_GRAPHIC:
+ {
+ Graphic aGraphic;
+
+ if (rVal.getValueType() == ::cppu::UnoType<OUString>::get())
+ {
+ OUString aURL = rVal.get<OUString>();
+ aGraphic = vcl::graphic::loadFromURL(aURL);
+ }
+ else if (rVal.getValueType() == cppu::UnoType<graphic::XGraphic>::get())
+ {
+ auto xGraphic = rVal.get<uno::Reference<graphic::XGraphic>>();
+ aGraphic = Graphic(xGraphic);
+ }
+
+ if (!aGraphic.IsNone())
+ {
+ maStrLink.clear();
+
+ std::unique_ptr<GraphicObject> xOldGrfObj(std::move(xGraphicObject));
+ xGraphicObject.reset(new GraphicObject(aGraphic));
+ ApplyGraphicTransparency_Impl();
+ xOldGrfObj.reset();
+
+ if (!aGraphic.IsNone() && eGraphicPos == GPOS_NONE)
+ {
+ eGraphicPos = GPOS_MM;
+ }
+ else if (aGraphic.IsNone())
+ {
+ eGraphicPos = GPOS_NONE;
+ }
+ }
+ }
+ break;
+
+ case MID_GRAPHIC_FILTER:
+ {
+ if( rVal.getValueType() == ::cppu::UnoType<OUString>::get() )
+ {
+ OUString sLink;
+ rVal >>= sLink;
+ SetGraphicFilter( sLink );
+ }
+ }
+ break;
+ case MID_GRAPHIC_TRANSPARENCY :
+ {
+ sal_Int32 nTmp = 0;
+ rVal >>= nTmp;
+ if(nTmp >= 0 && nTmp <= 100)
+ {
+ nGraphicTransparency = sal_Int8(nTmp);
+ if (xGraphicObject)
+ ApplyGraphicTransparency_Impl();
+ }
+ }
+ break;
+
+ case MID_SHADING_VALUE:
+ {
+ sal_Int32 nVal = 0;
+ if (!(rVal >>= nVal))
+ return false;
+
+ nShadingValue = nVal;
+ }
+ break;
+ }
+
+ return true;
+}
+
+
+bool SvxBrushItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+ ) const
+{
+ if ( GPOS_NONE == eGraphicPos )
+ {
+ rText = ::GetColorString( aColor ) + cpDelim;
+ TranslateId pId = RID_SVXITEMS_TRANSPARENT_FALSE;
+
+ if ( aColor.IsTransparent() )
+ pId = RID_SVXITEMS_TRANSPARENT_TRUE;
+ rText += EditResId(pId);
+ }
+ else
+ {
+ rText = EditResId(RID_SVXITEMS_GRAPHIC);
+ }
+
+ return true;
+}
+
+bool SvxBrushItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ const SvxBrushItem& rCmp = static_cast<const SvxBrushItem&>(rAttr);
+ bool bEqual =
+ aColor == rCmp.aColor &&
+ maComplexColor == rCmp.maComplexColor &&
+ aFilterColor == rCmp.aFilterColor &&
+ eGraphicPos == rCmp.eGraphicPos &&
+ nGraphicTransparency == rCmp.nGraphicTransparency;
+
+ if ( bEqual )
+ {
+ if ( GPOS_NONE != eGraphicPos )
+ {
+ bEqual = maStrLink == rCmp.maStrLink;
+
+ if ( bEqual )
+ {
+ bEqual = maStrFilter == rCmp.maStrFilter;
+ }
+
+ if ( bEqual )
+ {
+ if (!rCmp.xGraphicObject)
+ bEqual = !xGraphicObject;
+ else
+ bEqual = xGraphicObject &&
+ (*xGraphicObject == *rCmp.xGraphicObject);
+ }
+ }
+
+ if (bEqual)
+ {
+ bEqual = nShadingValue == rCmp.nShadingValue;
+ }
+ }
+
+ return bEqual;
+}
+
+SvxBrushItem* SvxBrushItem::Clone( SfxItemPool* ) const
+{
+ return new SvxBrushItem( *this );
+}
+
+const GraphicObject* SvxBrushItem::GetGraphicObject(OUString const & referer) const
+{
+ if (bLoadAgain && !maStrLink.isEmpty() && !xGraphicObject)
+ // when graphics already loaded, use as a cache
+ {
+ if (SvtSecurityOptions::isUntrustedReferer(referer)) {
+ return nullptr;
+ }
+
+ // tdf#94088 prepare graphic and state
+ Graphic aGraphic;
+ bool bGraphicLoaded = false;
+
+ // try to create stream directly from given URL
+ std::unique_ptr<SvStream> xStream(utl::UcbStreamHelper::CreateStream(maStrLink, StreamMode::STD_READ));
+ // tdf#94088 if we have a stream, try to load it directly as graphic
+ if (xStream && !xStream->GetError())
+ {
+ if (ERRCODE_NONE == GraphicFilter::GetGraphicFilter().ImportGraphic(aGraphic, maStrLink, *xStream,
+ GRFILTER_FORMAT_DONTKNOW, nullptr, GraphicFilterImportFlags::DontSetLogsizeForJpeg))
+ {
+ bGraphicLoaded = true;
+ }
+ }
+
+ // tdf#94088 if no succeeded, try if the string (which is not empty) contains
+ // a 'data:' scheme url and try to load that (embedded graphics)
+ if(!bGraphicLoaded)
+ {
+ INetURLObject aGraphicURL( maStrLink );
+
+ if( INetProtocol::Data == aGraphicURL.GetProtocol() )
+ {
+ std::unique_ptr<SvMemoryStream> const xMemStream(aGraphicURL.getData());
+ if (xMemStream)
+ {
+ if (ERRCODE_NONE == GraphicFilter::GetGraphicFilter().ImportGraphic(aGraphic, u"", *xMemStream))
+ {
+ bGraphicLoaded = true;
+
+ // tdf#94088 delete the no longer needed data scheme URL which
+ // is potentially pretty // large, containing a base64 encoded copy of the graphic
+ const_cast< SvxBrushItem* >(this)->maStrLink.clear();
+ }
+ }
+ }
+ }
+
+ // tdf#94088 when we got a graphic, set it
+ if(bGraphicLoaded && GraphicType::NONE != aGraphic.GetType())
+ {
+ xGraphicObject.reset(new GraphicObject);
+ xGraphicObject->SetGraphic(aGraphic);
+ const_cast < SvxBrushItem*> (this)->ApplyGraphicTransparency_Impl();
+ }
+ else
+ {
+ bLoadAgain = false;
+ }
+ }
+
+ return xGraphicObject.get();
+}
+
+void SvxBrushItem::setGraphicTransparency(sal_Int8 nNew)
+{
+ if (nNew != nGraphicTransparency)
+ {
+ nGraphicTransparency = nNew;
+ ApplyGraphicTransparency_Impl();
+ }
+}
+
+const Graphic* SvxBrushItem::GetGraphic(OUString const & referer) const
+{
+ const GraphicObject* pGrafObj = GetGraphicObject(referer);
+ return( pGrafObj ? &( pGrafObj->GetGraphic() ) : nullptr );
+}
+
+void SvxBrushItem::SetGraphicPos( SvxGraphicPosition eNew )
+{
+ eGraphicPos = eNew;
+
+ if ( GPOS_NONE == eGraphicPos )
+ {
+ xGraphicObject.reset();
+ maStrLink.clear();
+ maStrFilter.clear();
+ }
+ else
+ {
+ if (!xGraphicObject && maStrLink.isEmpty())
+ {
+ xGraphicObject.reset(new GraphicObject); // Creating a dummy
+ }
+ }
+}
+
+void SvxBrushItem::SetGraphic( const Graphic& rNew )
+{
+ if ( maStrLink.isEmpty() )
+ {
+ if (xGraphicObject)
+ xGraphicObject->SetGraphic(rNew);
+ else
+ xGraphicObject.reset(new GraphicObject(rNew));
+
+ ApplyGraphicTransparency_Impl();
+
+ if ( GPOS_NONE == eGraphicPos )
+ eGraphicPos = GPOS_MM; // None would be brush, then Default: middle
+ }
+ else
+ {
+ OSL_FAIL( "SetGraphic() on linked graphic! :-/" );
+ }
+}
+
+void SvxBrushItem::SetGraphicObject( const GraphicObject& rNewObj )
+{
+ if ( maStrLink.isEmpty() )
+ {
+ if (xGraphicObject)
+ *xGraphicObject = rNewObj;
+ else
+ xGraphicObject.reset(new GraphicObject(rNewObj));
+
+ ApplyGraphicTransparency_Impl();
+
+ if ( GPOS_NONE == eGraphicPos )
+ eGraphicPos = GPOS_MM; // None would be brush, then Default: middle
+ }
+ else
+ {
+ OSL_FAIL( "SetGraphic() on linked graphic! :-/" );
+ }
+}
+
+void SvxBrushItem::SetGraphicLink( const OUString& rNew )
+{
+ if ( rNew.isEmpty() )
+ maStrLink.clear();
+ else
+ {
+ maStrLink = rNew;
+ xGraphicObject.reset();
+ }
+}
+
+void SvxBrushItem::SetGraphicFilter( const OUString& rNew )
+{
+ maStrFilter = rNew;
+}
+
+void SvxBrushItem::ApplyGraphicTransparency_Impl()
+{
+ DBG_ASSERT(xGraphicObject, "no GraphicObject available" );
+ if (xGraphicObject)
+ {
+ GraphicAttr aAttr(xGraphicObject->GetAttr());
+ aAttr.SetAlpha(255 - lcl_PercentToTransparency(
+ nGraphicTransparency));
+ xGraphicObject->SetAttr(aAttr);
+ }
+}
+
+void SvxBrushItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxBrushItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("color"), BAD_CAST(aColor.AsRGBHexString().toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("filtercolor"), BAD_CAST(aFilterColor.AsRGBHexString().toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("shadingValue"), BAD_CAST(OString::number(nShadingValue).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("link"), BAD_CAST(maStrLink.toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("filter"), BAD_CAST(maStrFilter.toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("graphicPos"), BAD_CAST(OString::number(eGraphicPos).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("loadAgain"), BAD_CAST(OString::boolean(bLoadAgain).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+
+SvxFrameDirectionItem::SvxFrameDirectionItem( SvxFrameDirection nValue ,
+ sal_uInt16 _nWhich )
+ : SfxEnumItem<SvxFrameDirection>( _nWhich, nValue )
+{
+}
+
+
+SvxFrameDirectionItem::~SvxFrameDirectionItem()
+{
+}
+
+SvxFrameDirectionItem* SvxFrameDirectionItem::Clone( SfxItemPool * ) const
+{
+ return new SvxFrameDirectionItem( *this );
+}
+
+TranslateId getFrmDirResId(size_t nIndex)
+{
+ TranslateId const RID_SVXITEMS_FRMDIR[] =
+ {
+ RID_SVXITEMS_FRMDIR_HORI_LEFT_TOP,
+ RID_SVXITEMS_FRMDIR_HORI_RIGHT_TOP,
+ RID_SVXITEMS_FRMDIR_VERT_TOP_RIGHT,
+ RID_SVXITEMS_FRMDIR_VERT_TOP_LEFT,
+ RID_SVXITEMS_FRMDIR_ENVIRONMENT,
+ RID_SVXITEMS_FRMDIR_VERT_BOT_LEFT,
+ RID_SVXITEMS_FRMDIR_VERT_TOP_RIGHT90
+ };
+ return RID_SVXITEMS_FRMDIR[nIndex];
+}
+
+bool SvxFrameDirectionItem::GetPresentation(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&) const
+{
+ rText = EditResId(getFrmDirResId(GetEnumValue()));
+ return true;
+}
+
+bool SvxFrameDirectionItem::PutValue( const css::uno::Any& rVal,
+ sal_uInt8 )
+{
+ sal_Int16 nVal = sal_Int16();
+ bool bRet = ( rVal >>= nVal );
+ if( bRet )
+ {
+ // translate WritingDirection2 constants into SvxFrameDirection
+ switch( nVal )
+ {
+ case text::WritingMode2::LR_TB:
+ SetValue( SvxFrameDirection::Horizontal_LR_TB );
+ break;
+ case text::WritingMode2::RL_TB:
+ SetValue( SvxFrameDirection::Horizontal_RL_TB );
+ break;
+ case text::WritingMode2::TB_RL:
+ SetValue( SvxFrameDirection::Vertical_RL_TB );
+ break;
+ case text::WritingMode2::TB_LR:
+ SetValue( SvxFrameDirection::Vertical_LR_TB );
+ break;
+ case text::WritingMode2::BT_LR:
+ SetValue( SvxFrameDirection::Vertical_LR_BT );
+ break;
+ case text::WritingMode2::TB_RL90:
+ SetValue(SvxFrameDirection::Vertical_RL_TB90);
+ break;
+ case text::WritingMode2::PAGE:
+ SetValue( SvxFrameDirection::Environment );
+ break;
+ default:
+ bRet = false;
+ break;
+ }
+ }
+
+ return bRet;
+}
+
+
+bool SvxFrameDirectionItem::QueryValue( css::uno::Any& rVal,
+ sal_uInt8 ) const
+{
+ // translate SvxFrameDirection into WritingDirection2
+ sal_Int16 nVal;
+ bool bRet = true;
+ switch( GetValue() )
+ {
+ case SvxFrameDirection::Horizontal_LR_TB:
+ nVal = text::WritingMode2::LR_TB;
+ break;
+ case SvxFrameDirection::Horizontal_RL_TB:
+ nVal = text::WritingMode2::RL_TB;
+ break;
+ case SvxFrameDirection::Vertical_RL_TB:
+ nVal = text::WritingMode2::TB_RL;
+ break;
+ case SvxFrameDirection::Vertical_LR_TB:
+ nVal = text::WritingMode2::TB_LR;
+ break;
+ case SvxFrameDirection::Vertical_LR_BT:
+ nVal = text::WritingMode2::BT_LR;
+ break;
+ case SvxFrameDirection::Vertical_RL_TB90:
+ nVal = text::WritingMode2::TB_RL90;
+ break;
+ case SvxFrameDirection::Environment:
+ nVal = text::WritingMode2::PAGE;
+ break;
+ default:
+ OSL_FAIL("Unknown SvxFrameDirection value!");
+ bRet = false;
+ break;
+ }
+
+ // return value + error state
+ if( bRet )
+ {
+ rVal <<= nVal;
+ }
+ return bRet;
+}
+
+void SvxFrameDirectionItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxFrameDirectionItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nWhich"),
+ BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("m_nValue"),
+ BAD_CAST(OString::number(static_cast<sal_Int16>(GetValue())).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/items/itemtype.cxx b/editeng/source/items/itemtype.cxx
new file mode 100644
index 0000000000..cbb83c83be
--- /dev/null
+++ b/editeng/source/items/itemtype.cxx
@@ -0,0 +1,232 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <osl/diagnose.h>
+#include <vcl/outdev.hxx>
+#include <editeng/editrids.hrc>
+#include <unotools/intlwrapper.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <editeng/itemtype.hxx>
+#include <editeng/eerdll.hxx>
+#include <rtl/ustrbuf.hxx>
+
+
+OUString GetMetricText( tools::Long nVal, MapUnit eSrcUnit, MapUnit eDestUnit, const IntlWrapper* pIntl )
+{
+ bool bNeg = false;
+ bool bShowAtLeastOneDecimalDigit = true;
+ sal_Int32 nRet = 0;
+
+ if ( nVal < 0 )
+ {
+ bNeg = true;
+ nVal *= -1;
+ }
+
+ switch ( eDestUnit )
+ {
+ case MapUnit::Map100thMM:
+ case MapUnit::Map10thMM:
+ case MapUnit::MapMM:
+ case MapUnit::MapCM:
+ {
+ nRet = OutputDevice::LogicToLogic( nVal, eSrcUnit, MapUnit::Map100thMM );
+
+ switch ( eDestUnit )
+ {
+ case MapUnit::Map100thMM: nRet *= 1000; break;
+ case MapUnit::Map10thMM: nRet *= 100; break;
+ case MapUnit::MapMM: nRet *= 10; break;
+ default: ;//prevent warning
+ }
+ break;
+ }
+
+ case MapUnit::Map1000thInch:
+ case MapUnit::Map100thInch:
+ case MapUnit::Map10thInch:
+ case MapUnit::MapInch:
+ {
+ nRet = OutputDevice::LogicToLogic( nVal, eSrcUnit, MapUnit::Map1000thInch );
+
+ switch ( eDestUnit )
+ {
+ case MapUnit::Map1000thInch: nRet *= 1000; break;
+ case MapUnit::Map100thInch: nRet *= 100; break;
+ case MapUnit::Map10thInch: nRet *= 10; break;
+ default: ;//prevent warning
+ }
+ break;
+ }
+
+ case MapUnit::MapPoint:
+ // fractions of a point are used, e.g., for font size
+ nRet = OutputDevice::LogicToLogic(nVal, eSrcUnit, MapUnit::MapTwip) * 50;
+ bShowAtLeastOneDecimalDigit = false;
+ break;
+
+ case MapUnit::MapTwip:
+ case MapUnit::MapPixel:
+ return OUString::number( OutputDevice::LogicToLogic(
+ nVal, eSrcUnit, eDestUnit ));
+
+ default:
+ OSL_FAIL( "not supported mapunit" );
+ return OUString();
+ }
+
+ if ( MapUnit::MapCM == eDestUnit || MapUnit::MapInch == eDestUnit )
+ {
+ sal_Int32 nMod = nRet % 10;
+
+ if ( nMod > 4 )
+ nRet += 10 - nMod;
+ else if ( nMod > 0 )
+ nRet -= nMod;
+ }
+
+ OUStringBuffer sRet;
+
+ if ( bNeg )
+ sRet.append('-');
+
+ tools::Long nDiff = 1000;
+ for( int nDigits = 4; nDigits; --nDigits, nDiff /= 10 )
+ {
+ if ( nRet < nDiff )
+ sRet.append('0');
+ else
+ sRet.append(nRet / nDiff);
+ nRet %= nDiff;
+ if( 4 == nDigits && (bShowAtLeastOneDecimalDigit || nRet) )
+ {
+ if(pIntl)
+ sRet.append(pIntl->getLocaleData()->getNumDecimalSep());
+ else
+ sRet.append(',');
+ if( !nRet )
+ {
+ sRet.append('0');
+ break;
+ }
+ }
+ else if( !nRet )
+ break;
+ }
+ return sRet.makeStringAndClear();
+}
+
+OUString GetColorString( const Color& rCol )
+{
+ if (rCol == COL_AUTO)
+ return EditResId(RID_SVXSTR_AUTOMATIC);
+
+ static const Color aColAry[] = {
+ COL_BLACK, COL_BLUE, COL_GREEN, COL_CYAN,
+ COL_RED, COL_MAGENTA, COL_BROWN, COL_GRAY,
+ COL_LIGHTGRAY, COL_LIGHTBLUE, COL_LIGHTGREEN, COL_LIGHTCYAN,
+ COL_LIGHTRED, COL_LIGHTMAGENTA, COL_YELLOW, COL_WHITE };
+
+ sal_uInt16 nColor = 0;
+ while ( nColor < SAL_N_ELEMENTS(aColAry) &&
+ aColAry[nColor] != rCol.GetRGBColor() )
+ {
+ nColor += 1;
+ }
+
+ static TranslateId RID_SVXITEMS_COLORS[] =
+ {
+ RID_SVXITEMS_COLOR_BLACK,
+ RID_SVXITEMS_COLOR_BLUE,
+ RID_SVXITEMS_COLOR_GREEN,
+ RID_SVXITEMS_COLOR_CYAN,
+ RID_SVXITEMS_COLOR_RED,
+ RID_SVXITEMS_COLOR_MAGENTA,
+ RID_SVXITEMS_COLOR_BROWN,
+ RID_SVXITEMS_COLOR_GRAY,
+ RID_SVXITEMS_COLOR_LIGHTGRAY,
+ RID_SVXITEMS_COLOR_LIGHTBLUE,
+ RID_SVXITEMS_COLOR_LIGHTGREEN,
+ RID_SVXITEMS_COLOR_LIGHTCYAN,
+ RID_SVXITEMS_COLOR_LIGHTRED,
+ RID_SVXITEMS_COLOR_LIGHTMAGENTA,
+ RID_SVXITEMS_COLOR_YELLOW,
+ RID_SVXITEMS_COLOR_WHITE
+ };
+
+ static_assert(SAL_N_ELEMENTS(aColAry) == SAL_N_ELEMENTS(RID_SVXITEMS_COLORS), "must match");
+
+ OUString sStr;
+ if ( nColor < SAL_N_ELEMENTS(aColAry) )
+ sStr = EditResId(RID_SVXITEMS_COLORS[nColor]);
+
+ if ( sStr.isEmpty() )
+ {
+ sStr += "RGB(" +
+ OUString::number( rCol.GetRed() ) + cpDelim +
+ OUString::number( rCol.GetGreen() ) + cpDelim +
+ OUString::number( rCol.GetBlue() ) + ")";
+ }
+ return sStr;
+}
+
+TranslateId GetMetricId( MapUnit eUnit )
+{
+ TranslateId pId = RID_SVXITEMS_METRIC_MM;
+
+ switch ( eUnit )
+ {
+ case MapUnit::Map100thMM:
+ case MapUnit::Map10thMM:
+ case MapUnit::MapMM:
+ pId = RID_SVXITEMS_METRIC_MM;
+ break;
+
+ case MapUnit::MapCM:
+ pId = RID_SVXITEMS_METRIC_CM;
+ break;
+
+ case MapUnit::Map1000thInch:
+ case MapUnit::Map100thInch:
+ case MapUnit::Map10thInch:
+ case MapUnit::MapInch:
+ pId = RID_SVXITEMS_METRIC_INCH;
+ break;
+
+ case MapUnit::MapPoint:
+ pId = RID_SVXITEMS_METRIC_POINT;
+ break;
+
+ case MapUnit::MapTwip:
+ pId = RID_SVXITEMS_METRIC_TWIP;
+ break;
+
+ case MapUnit::MapPixel:
+ pId = RID_SVXITEMS_METRIC_PIXEL;
+ break;
+
+ default:
+ OSL_FAIL( "not supported mapunit" );
+ }
+ return pId;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/items/justifyitem.cxx b/editeng/source/items/justifyitem.cxx
new file mode 100644
index 0000000000..7fe699cb2c
--- /dev/null
+++ b/editeng/source/items/justifyitem.cxx
@@ -0,0 +1,368 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <editeng/justifyitem.hxx>
+#include <editeng/memberids.h>
+#include <editeng/eerdll.hxx>
+
+#include <com/sun/star/table/CellHoriJustify.hpp>
+#include <com/sun/star/style/ParagraphAdjust.hpp>
+#include <com/sun/star/table/CellJustifyMethod.hpp>
+#include <com/sun/star/table/CellVertJustify2.hpp>
+#include <com/sun/star/style/VerticalAlignment.hpp>
+
+#include <strings.hrc>
+
+SfxPoolItem* SvxHorJustifyItem::CreateDefault() { return new SvxHorJustifyItem(SvxCellHorJustify::Standard, 0) ;}
+SfxPoolItem* SvxVerJustifyItem::CreateDefault() { return new SvxVerJustifyItem(SvxCellVerJustify::Standard, 0) ;}
+
+using namespace ::com::sun::star;
+
+
+SvxHorJustifyItem::SvxHorJustifyItem( const sal_uInt16 nId ) :
+ SfxEnumItem( nId, SvxCellHorJustify::Standard )
+{
+}
+
+SvxHorJustifyItem::SvxHorJustifyItem( const SvxCellHorJustify eJustify,
+ const sal_uInt16 nId ) :
+ SfxEnumItem( nId, eJustify )
+{
+}
+
+
+bool SvxHorJustifyItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&) const
+{
+ rText = GetValueText(GetValue());
+ return true;
+}
+
+
+bool SvxHorJustifyItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch ( nMemberId )
+ {
+ case MID_HORJUST_HORJUST:
+ {
+ table::CellHoriJustify eUno = table::CellHoriJustify_STANDARD;
+ switch ( GetValue() )
+ {
+ case SvxCellHorJustify::Standard: eUno = table::CellHoriJustify_STANDARD; break;
+ case SvxCellHorJustify::Left: eUno = table::CellHoriJustify_LEFT; break;
+ case SvxCellHorJustify::Center: eUno = table::CellHoriJustify_CENTER; break;
+ case SvxCellHorJustify::Right: eUno = table::CellHoriJustify_RIGHT; break;
+ case SvxCellHorJustify::Block: eUno = table::CellHoriJustify_BLOCK; break;
+ case SvxCellHorJustify::Repeat: eUno = table::CellHoriJustify_REPEAT; break;
+ }
+ rVal <<= eUno;
+ }
+ break;
+ case MID_HORJUST_ADJUST:
+ {
+ // ParagraphAdjust values, as in SvxAdjustItem
+ // (same value for ParaAdjust and ParaLastLineAdjust)
+
+ style::ParagraphAdjust nAdjust = style::ParagraphAdjust_LEFT;
+ switch ( GetValue() )
+ {
+ // ParagraphAdjust_LEFT is used for STANDARD and REPEAT
+ case SvxCellHorJustify::Standard:
+ case SvxCellHorJustify::Repeat:
+ case SvxCellHorJustify::Left: nAdjust = style::ParagraphAdjust_LEFT; break;
+ case SvxCellHorJustify::Center: nAdjust = style::ParagraphAdjust_CENTER; break;
+ case SvxCellHorJustify::Right: nAdjust = style::ParagraphAdjust_RIGHT; break;
+ case SvxCellHorJustify::Block: nAdjust = style::ParagraphAdjust_BLOCK; break;
+ }
+ rVal <<= static_cast<sal_Int16>(nAdjust); // as sal_Int16
+ }
+ break;
+ }
+ return true;
+}
+
+bool SvxHorJustifyItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch ( nMemberId )
+ {
+ case MID_HORJUST_HORJUST:
+ {
+ table::CellHoriJustify eUno;
+ if(!(rVal >>= eUno))
+ {
+ sal_Int32 nValue = 0;
+ if(!(rVal >>= nValue))
+ return false;
+ eUno = static_cast<table::CellHoriJustify>(nValue);
+ }
+ SvxCellHorJustify eSvx = SvxCellHorJustify::Standard;
+ switch (eUno)
+ {
+ case table::CellHoriJustify_STANDARD: eSvx = SvxCellHorJustify::Standard; break;
+ case table::CellHoriJustify_LEFT: eSvx = SvxCellHorJustify::Left; break;
+ case table::CellHoriJustify_CENTER: eSvx = SvxCellHorJustify::Center; break;
+ case table::CellHoriJustify_RIGHT: eSvx = SvxCellHorJustify::Right; break;
+ case table::CellHoriJustify_BLOCK: eSvx = SvxCellHorJustify::Block; break;
+ case table::CellHoriJustify_REPEAT: eSvx = SvxCellHorJustify::Repeat; break;
+ default: ; //prevent warning
+ }
+ SetValue( eSvx );
+ }
+ break;
+ case MID_HORJUST_ADJUST:
+ {
+ // property contains ParagraphAdjust values as sal_Int16
+ sal_Int16 nVal = sal_Int16();
+ if(!(rVal >>= nVal))
+ return false;
+
+ SvxCellHorJustify eSvx = SvxCellHorJustify::Standard;
+ switch (static_cast<style::ParagraphAdjust>(nVal))
+ {
+ // STRETCH is treated as BLOCK
+ case style::ParagraphAdjust_LEFT: eSvx = SvxCellHorJustify::Left; break;
+ case style::ParagraphAdjust_RIGHT: eSvx = SvxCellHorJustify::Right; break;
+ case style::ParagraphAdjust_STRETCH:
+ case style::ParagraphAdjust_BLOCK: eSvx = SvxCellHorJustify::Block; break;
+ case style::ParagraphAdjust_CENTER: eSvx = SvxCellHorJustify::Center; break;
+ default: break;
+ }
+ SetValue( eSvx );
+ }
+ }
+ return true;
+}
+
+OUString SvxHorJustifyItem::GetValueText(SvxCellHorJustify nVal)
+{
+ assert(nVal <= SvxCellHorJustify::Repeat && "enum overflow!");
+ return EditResId(RID_SVXITEMS_HORJUST[static_cast<size_t>(nVal)]);
+}
+
+SvxHorJustifyItem* SvxHorJustifyItem::Clone( SfxItemPool* ) const
+{
+ return new SvxHorJustifyItem( *this );
+}
+
+sal_uInt16 SvxHorJustifyItem::GetValueCount() const
+{
+ return sal_uInt16(SvxCellHorJustify::Repeat) + 1; // Last Enum value + 1
+}
+
+
+SvxVerJustifyItem::SvxVerJustifyItem( const sal_uInt16 nId ) :
+ SfxEnumItem( nId, SvxCellVerJustify::Standard )
+{
+}
+
+SvxVerJustifyItem::SvxVerJustifyItem( const SvxCellVerJustify eJustify,
+ const sal_uInt16 nId ) :
+ SfxEnumItem( nId, eJustify )
+{
+}
+
+
+bool SvxVerJustifyItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText,
+ const IntlWrapper& ) const
+{
+ rText = GetValueText( GetValue() );
+ return true;
+}
+
+
+bool SvxVerJustifyItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch ( nMemberId )
+ {
+ case MID_HORJUST_ADJUST:
+ {
+ style::VerticalAlignment eUno = style::VerticalAlignment_TOP;
+ switch ( GetValue() )
+ {
+ case SvxCellVerJustify::Top: eUno = style::VerticalAlignment_TOP; break;
+ case SvxCellVerJustify::Center: eUno = style::VerticalAlignment_MIDDLE; break;
+ case SvxCellVerJustify::Bottom: eUno = style::VerticalAlignment_BOTTOM; break;
+ default: ; //prevent warning
+ }
+ rVal <<= eUno;
+ break;
+ }
+ default:
+ {
+ sal_Int32 nUno = table::CellVertJustify2::STANDARD;
+ switch ( GetValue() )
+ {
+ case SvxCellVerJustify::Standard: nUno = table::CellVertJustify2::STANDARD; break;
+ case SvxCellVerJustify::Top: nUno = table::CellVertJustify2::TOP; break;
+ case SvxCellVerJustify::Center: nUno = table::CellVertJustify2::CENTER; break;
+ case SvxCellVerJustify::Bottom: nUno = table::CellVertJustify2::BOTTOM; break;
+ case SvxCellVerJustify::Block: nUno = table::CellVertJustify2::BLOCK; break;
+ default: ; //prevent warning
+ }
+ rVal <<= nUno;
+ break;
+ }
+ }
+ return true;
+}
+
+bool SvxVerJustifyItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch ( nMemberId )
+ {
+ case MID_HORJUST_ADJUST:
+ {
+ // property contains ParagraphAdjust values as sal_Int16
+ style::VerticalAlignment nVal = style::VerticalAlignment_TOP;
+ if(!(rVal >>= nVal))
+ return false;
+
+ SvxCellVerJustify eSvx = SvxCellVerJustify::Standard;
+ switch (nVal)
+ {
+ case style::VerticalAlignment_TOP: eSvx = SvxCellVerJustify::Top; break;
+ case style::VerticalAlignment_MIDDLE: eSvx = SvxCellVerJustify::Center; break;
+ case style::VerticalAlignment_BOTTOM: eSvx = SvxCellVerJustify::Bottom; break;
+ default:;
+ }
+ SetValue( eSvx );
+ break;
+ }
+ default:
+ {
+ sal_Int32 eUno = table::CellVertJustify2::STANDARD;
+ rVal >>= eUno;
+
+ SvxCellVerJustify eSvx = SvxCellVerJustify::Standard;
+ switch (eUno)
+ {
+ case table::CellVertJustify2::STANDARD: eSvx = SvxCellVerJustify::Standard; break;
+ case table::CellVertJustify2::TOP: eSvx = SvxCellVerJustify::Top; break;
+ case table::CellVertJustify2::CENTER: eSvx = SvxCellVerJustify::Center; break;
+ case table::CellVertJustify2::BOTTOM: eSvx = SvxCellVerJustify::Bottom; break;
+ case table::CellVertJustify2::BLOCK: eSvx = SvxCellVerJustify::Block; break;
+ default: ; //prevent warning
+ }
+ SetValue( eSvx );
+ break;
+ }
+ }
+
+ return true;
+}
+
+OUString SvxVerJustifyItem::GetValueText( SvxCellVerJustify nVal )
+{
+ assert(nVal <= SvxCellVerJustify::Block && "enum overflow!");
+ return EditResId(RID_SVXITEMS_VERJUST[static_cast<size_t>(nVal)]);
+}
+
+SvxVerJustifyItem* SvxVerJustifyItem::Clone( SfxItemPool* ) const
+{
+ return new SvxVerJustifyItem( *this );
+}
+
+sal_uInt16 SvxVerJustifyItem::GetValueCount() const
+{
+ return static_cast<sal_uInt16>(SvxCellVerJustify::Bottom) + 1; // Last Enum value + 1
+}
+
+SvxJustifyMethodItem::SvxJustifyMethodItem( const SvxCellJustifyMethod eJustify,
+ const sal_uInt16 nId ) :
+ SfxEnumItem( nId, eJustify )
+{
+}
+
+bool SvxJustifyMethodItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText,
+ const IntlWrapper& ) const
+{
+ rText = GetValueText( GetValue() );
+ return true;
+}
+
+
+bool SvxJustifyMethodItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) const
+{
+ sal_Int32 nUno = table::CellJustifyMethod::AUTO;
+ switch (GetValue())
+ {
+ case SvxCellJustifyMethod::Auto: nUno = table::CellJustifyMethod::AUTO; break;
+ case SvxCellJustifyMethod::Distribute: nUno = table::CellJustifyMethod::DISTRIBUTE; break;
+ default:;
+ }
+ rVal <<= nUno;
+ return true;
+}
+
+bool SvxJustifyMethodItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/ )
+{
+ sal_Int32 nVal = table::CellJustifyMethod::AUTO;
+ if (!(rVal >>= nVal))
+ return false;
+
+ SvxCellJustifyMethod eSvx = SvxCellJustifyMethod::Auto;
+ switch (nVal)
+ {
+ case table::CellJustifyMethod::AUTO:
+ eSvx = SvxCellJustifyMethod::Auto;
+ break;
+ case table::CellJustifyMethod::DISTRIBUTE:
+ eSvx = SvxCellJustifyMethod::Distribute;
+ break;
+ default:;
+ }
+ SetValue(eSvx);
+ return true;
+}
+
+OUString SvxJustifyMethodItem::GetValueText( SvxCellJustifyMethod nVal )
+{
+ assert(nVal <= SvxCellJustifyMethod::Distribute && "enum overflow!");
+ return EditResId(RID_SVXITEMS_JUSTMETHOD[static_cast<size_t>(nVal)]);
+}
+
+SvxJustifyMethodItem* SvxJustifyMethodItem::Clone( SfxItemPool* ) const
+{
+ return new SvxJustifyMethodItem( *this );
+}
+
+sal_uInt16 SvxJustifyMethodItem::GetValueCount() const
+{
+ return static_cast<sal_uInt16>(SvxCellJustifyMethod::Distribute) + 1; // Last Enum value + 1
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/items/legacyitem.cxx b/editeng/source/items/legacyitem.cxx
new file mode 100644
index 0000000000..96742f46fc
--- /dev/null
+++ b/editeng/source/items/legacyitem.cxx
@@ -0,0 +1,826 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <editeng/legacyitem.hxx>
+#include <unotools/fontdefs.hxx>
+#include <tools/tenccvt.hxx>
+#include <tools/stream.hxx>
+#include <comphelper/fileformat.h>
+#include <vcl/graph.hxx>
+#include <vcl/GraphicObject.hxx>
+#include <vcl/TypeSerializer.hxx>
+#include <osl/diagnose.h>
+#include <tools/urlobj.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/lineitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/editerr.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <editeng/keepitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <tools/GenericTypeSerializer.hxx>
+
+
+void Create_legacy_direct_set(SvxFontHeightItem& rItem, sal_uInt32 nH, sal_uInt16 nP, MapUnit eP)
+{
+ rItem.legacy_direct_set(nH, nP, eP);
+}
+
+namespace legacy
+{
+ namespace SvxFont
+ {
+ sal_uInt16 GetVersion(sal_uInt16)
+ {
+ return 0;
+ }
+
+ void Create(SvxFontItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ sal_uInt8 _eFamily, eFontPitch, eFontTextEncoding;
+ OUString aName, aStyle;
+ rStrm.ReadUChar( _eFamily );
+ rStrm.ReadUChar( eFontPitch );
+ rStrm.ReadUChar( eFontTextEncoding );
+
+ // UNICODE: rStrm >> aName;
+ aName = rStrm.ReadUniOrByteString(rStrm.GetStreamCharSet());
+
+ // UNICODE: rStrm >> aStyle;
+ aStyle = rStrm.ReadUniOrByteString(rStrm.GetStreamCharSet());
+
+ // Set the "correct" textencoding
+ eFontTextEncoding = static_cast<sal_uInt8>(GetSOLoadTextEncoding( eFontTextEncoding ));
+
+ // at some point, the StarBats changes from ANSI font to SYMBOL font
+ if ( RTL_TEXTENCODING_SYMBOL != eFontTextEncoding && aName == "StarBats" )
+ eFontTextEncoding = RTL_TEXTENCODING_SYMBOL;
+
+ // Check if we have stored unicode
+ sal_uInt64 const nStreamPos = rStrm.Tell();
+ // #define STORE_UNICODE_MAGIC_MARKER 0xFE331188
+ sal_uInt32 nMagic = 0xFE331188;
+ rStrm.ReadUInt32( nMagic );
+ if ( nMagic == 0xFE331188 )
+ {
+ aName = rStrm.ReadUniOrByteString( RTL_TEXTENCODING_UNICODE );
+ aStyle = rStrm.ReadUniOrByteString( RTL_TEXTENCODING_UNICODE );
+ }
+ else
+ {
+ rStrm.Seek( nStreamPos );
+ }
+
+ rItem.SetFamilyName(aName);
+ rItem.SetStyleName(aStyle);
+ rItem.SetFamily(static_cast<FontFamily>(_eFamily));
+ rItem.SetPitch(static_cast<FontPitch>(eFontPitch));
+ rItem.SetCharSet(static_cast<rtl_TextEncoding>(eFontTextEncoding));
+ }
+
+ SvStream& Store(const SvxFontItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ const bool bToBats(IsOpenSymbol(rItem.GetFamilyName()));
+
+ rStrm.WriteUChar(rItem.GetFamily()).WriteUChar(rItem.GetPitch()).WriteUChar(bToBats ?
+ RTL_TEXTENCODING_SYMBOL :
+ GetSOStoreTextEncoding(rItem.GetCharSet()));
+
+ const OUString aStoreFamilyName(bToBats ? "StarBats" : rItem.GetFamilyName());
+
+ rStrm.WriteUniOrByteString(aStoreFamilyName, rStrm.GetStreamCharSet());
+ rStrm.WriteUniOrByteString(rItem.GetStyleName(), rStrm.GetStreamCharSet());
+
+ return rStrm;
+ }
+ }
+
+ namespace SvxFontHeight
+ {
+ sal_uInt16 GetVersion(sal_uInt16 nFileFormatVersion)
+ {
+ return (nFileFormatVersion <= SOFFICE_FILEFORMAT_40)
+ ? FONTHEIGHT_16_VERSION
+ : FONTHEIGHT_UNIT_VERSION;
+ }
+
+ void Create(SvxFontHeightItem& rItem, SvStream& rStrm, sal_uInt16 nItemVersion)
+ {
+ sal_uInt16 nsize, nprop = 0;
+ MapUnit nPropUnit = MapUnit::MapRelative;
+
+ rStrm.ReadUInt16( nsize );
+
+ if( FONTHEIGHT_16_VERSION <= nItemVersion )
+ rStrm.ReadUInt16( nprop );
+ else
+ {
+ sal_uInt8 nP;
+ rStrm .ReadUChar( nP );
+ nprop = static_cast<sal_uInt16>(nP);
+ }
+
+ if( FONTHEIGHT_UNIT_VERSION <= nItemVersion )
+ {
+ sal_uInt16 nTmp;
+ rStrm.ReadUInt16( nTmp );
+ nPropUnit = static_cast<MapUnit>(nTmp);
+ }
+
+ Create_legacy_direct_set(rItem, nsize, nprop, nPropUnit);
+ }
+
+ SvStream& Store(const SvxFontHeightItem& rItem, SvStream& rStrm, sal_uInt16 nItemVersion)
+ {
+ rStrm.WriteUInt16( rItem.GetHeight() );
+
+ if( FONTHEIGHT_UNIT_VERSION <= nItemVersion )
+ rStrm.WriteUInt16( rItem.GetProp() ).WriteUInt16( static_cast<sal_uInt16>(rItem.GetPropUnit()) );
+ else
+ {
+ // When exporting to the old versions the relative information is lost
+ // when there is no percentage
+ sal_uInt16 _nProp = rItem.GetProp();
+ if( MapUnit::MapRelative != rItem.GetPropUnit() )
+ _nProp = 100;
+ rStrm.WriteUInt16( _nProp );
+ }
+ return rStrm;
+ }
+ }
+
+ namespace SvxWeight
+ {
+ sal_uInt16 GetVersion(sal_uInt16)
+ {
+ return 0;
+ }
+
+ void Create(SvxWeightItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ sal_uInt8 nWeight(0);
+ rStrm.ReadUChar(nWeight);
+ rItem.SetValue(static_cast<FontWeight>(nWeight));
+ }
+
+ SvStream& Store(const SvxWeightItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ rStrm.WriteUChar(rItem.GetValue());
+ return rStrm;
+ }
+ }
+
+ namespace SvxPosture
+ {
+ sal_uInt16 GetVersion(sal_uInt16)
+ {
+ return 0;
+ }
+
+ void Create(SvxPostureItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ sal_uInt8 nPosture(0);
+ rStrm.ReadUChar(nPosture);
+ rItem.SetValue(static_cast<FontItalic>(nPosture));
+ }
+
+ SvStream& Store(const SvxPostureItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ rStrm.WriteUChar( rItem.GetValue() );
+ return rStrm;
+ }
+ }
+
+ namespace SvxTextLine // SvxUnderlineItem, SvxOverlineItem -> SvxTextLineItem
+ {
+ sal_uInt16 GetVersion(sal_uInt16)
+ {
+ return 0;
+ }
+
+ void Create(SvxTextLineItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ sal_uInt8 nState(0);
+ rStrm.ReadUChar(nState);
+ rItem.SetValue(static_cast<FontLineStyle>(nState));
+ // GetColor() is *not* saved/loaded ?!?
+ }
+
+ SvStream& Store(const SvxTextLineItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ rStrm.WriteUChar(rItem.GetValue());
+ // GetColor() is *not* saved/loaded ?!?
+ return rStrm;
+ }
+ }
+
+ namespace SvxCrossedOut
+ {
+ sal_uInt16 GetVersion(sal_uInt16)
+ {
+ return 0;
+ }
+
+ void Create(SvxCrossedOutItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ sal_uInt8 eCross(0);
+ rStrm.ReadUChar(eCross);
+ rItem.SetValue(static_cast<FontStrikeout>(eCross));
+ }
+
+ SvStream& Store(const SvxCrossedOutItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ rStrm.WriteUChar(rItem.GetValue());
+ return rStrm;
+ }
+ }
+
+ namespace SvxColor
+ {
+ sal_uInt16 GetVersion(sal_uInt16 nFileFormatVersion)
+ {
+ DBG_ASSERT( SOFFICE_FILEFORMAT_31==nFileFormatVersion ||
+ SOFFICE_FILEFORMAT_40==nFileFormatVersion ||
+ SOFFICE_FILEFORMAT_50==nFileFormatVersion,
+ "SvxColorItem: Is there a new file format? ");
+ return SOFFICE_FILEFORMAT_50 >= nFileFormatVersion ? VERSION_USEAUTOCOLOR : 0;
+ }
+
+ void Create(SvxColorItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ Color aColor(COL_AUTO);
+ tools::GenericTypeSerializer aSerializer(rStrm);
+ aSerializer.readColor(aColor);
+ rItem.SetValue(aColor);
+ }
+
+ SvStream& Store(const SvxColorItem& rItem, SvStream& rStrm, sal_uInt16 nItemVersion)
+ {
+ tools::GenericTypeSerializer aSerializer(rStrm);
+ if( VERSION_USEAUTOCOLOR == nItemVersion && COL_AUTO == rItem.GetValue() )
+ aSerializer.writeColor(COL_BLACK);
+ else
+ aSerializer.writeColor(rItem.GetValue());
+ return rStrm;
+ }
+ }
+
+ namespace SvxBox
+ {
+ sal_uInt16 GetVersion(sal_uInt16 nFileFormatVersion)
+ {
+ DBG_ASSERT( SOFFICE_FILEFORMAT_31==nFileFormatVersion ||
+ SOFFICE_FILEFORMAT_40==nFileFormatVersion ||
+ SOFFICE_FILEFORMAT_50==nFileFormatVersion,
+ "SvxBoxItem: Is there a new file format?" );
+ return SOFFICE_FILEFORMAT_31==nFileFormatVersion ||
+ SOFFICE_FILEFORMAT_40==nFileFormatVersion ? 0 : BOX_BORDER_STYLE_VERSION;
+ }
+
+ /// Item version for saved border lines. The old version saves the line without style information.
+ const int BORDER_LINE_OLD_VERSION = 0;
+ /// Item version for saved border lies. The new version includes line style.
+ const int BORDER_LINE_WITH_STYLE_VERSION = 1;
+
+ /// Creates a border line from a stream.
+ static ::editeng::SvxBorderLine CreateBorderLine(SvStream &stream, sal_uInt16 version)
+ {
+ sal_uInt16 nOutline, nInline, nDistance;
+ sal_uInt16 nStyle = css::table::BorderLineStyle::NONE;
+ Color aColor;
+ tools::GenericTypeSerializer aSerializer(stream);
+ aSerializer.readColor(aColor);
+ stream.ReadUInt16( nOutline ).ReadUInt16( nInline ).ReadUInt16( nDistance );
+
+ if (version >= BORDER_LINE_WITH_STYLE_VERSION)
+ stream.ReadUInt16( nStyle );
+
+ ::editeng::SvxBorderLine border(&aColor);
+ border.GuessLinesWidths(static_cast<SvxBorderLineStyle>(nStyle), nOutline, nInline, nDistance);
+ return border;
+ }
+
+ /// Retrieves a BORDER_LINE_* version from a BOX_BORDER_* version.
+ static sal_uInt16 BorderLineVersionFromBoxVersion(sal_uInt16 boxVersion)
+ {
+ return (boxVersion >= BOX_BORDER_STYLE_VERSION)? BORDER_LINE_WITH_STYLE_VERSION : BORDER_LINE_OLD_VERSION;
+ }
+
+ void Create(SvxBoxItem& rItem, SvStream& rStrm, sal_uInt16 nItemVersion)
+ {
+ sal_uInt16 nDistance(0);
+ rStrm.ReadUInt16( nDistance );
+ SvxBoxItemLine aLineMap[4] = { SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT,
+ SvxBoxItemLine::RIGHT, SvxBoxItemLine::BOTTOM };
+ sal_Int8 cLine(0);
+
+ while (rStrm.good())
+ {
+ rStrm.ReadSChar( cLine );
+
+ if( cLine > 3 )
+ break;
+
+ ::editeng::SvxBorderLine aBorder = CreateBorderLine(rStrm, BorderLineVersionFromBoxVersion(nItemVersion));
+ rItem.SetLine( &aBorder, aLineMap[cLine] );
+ }
+
+ if( nItemVersion >= BOX_4DISTS_VERSION && (cLine&0x10) != 0 )
+ {
+ for(const SvxBoxItemLine & i : aLineMap)
+ {
+ sal_uInt16 nDist;
+ rStrm.ReadUInt16( nDist );
+ rItem.SetDistance( nDist, i );
+ }
+ }
+ else
+ {
+ rItem.SetAllDistances(nDistance);
+ }
+ }
+
+ /// Store a border line to a stream.
+ static SvStream& StoreBorderLine(SvStream &stream, const ::editeng::SvxBorderLine &l, sal_uInt16 version)
+ {
+ tools::GenericTypeSerializer aSerializer(stream);
+ aSerializer.writeColor(l.GetColor());
+
+ stream.WriteUInt16( l.GetOutWidth() )
+ .WriteUInt16( l.GetInWidth() )
+ .WriteUInt16( l.GetDistance() );
+
+ if (version >= BORDER_LINE_WITH_STYLE_VERSION)
+ stream.WriteUInt16( static_cast<sal_uInt16>(l.GetBorderLineStyle()) );
+
+ return stream;
+ }
+
+ SvStream& Store(const SvxBoxItem& rItem, SvStream& rStrm, sal_uInt16 nItemVersion)
+ {
+ rStrm.WriteUInt16( rItem.GetSmallestDistance() );
+ const ::editeng::SvxBorderLine* pLine[ 4 ]; // top, left, right, bottom
+ pLine[ 0 ] = rItem.GetTop();
+ pLine[ 1 ] = rItem.GetLeft();
+ pLine[ 2 ] = rItem.GetRight();
+ pLine[ 3 ] = rItem.GetBottom();
+
+ for( int i = 0; i < 4; i++ )
+ {
+ const ::editeng::SvxBorderLine* l = pLine[ i ];
+ if( l )
+ {
+ rStrm.WriteSChar(i);
+ StoreBorderLine(rStrm, *l, BorderLineVersionFromBoxVersion(nItemVersion));
+ }
+ }
+ sal_Int8 cLine = 4;
+ const sal_uInt16 nTopDist(rItem.GetDistance(SvxBoxItemLine::TOP));
+ const sal_uInt16 nLeftDist(rItem.GetDistance(SvxBoxItemLine::LEFT));
+ const sal_uInt16 nRightDist(rItem.GetDistance(SvxBoxItemLine::RIGHT));
+ const sal_uInt16 nBottomDist(rItem.GetDistance(SvxBoxItemLine::BOTTOM));
+
+ if( nItemVersion >= BOX_4DISTS_VERSION &&
+ !(nTopDist == nLeftDist &&
+ nTopDist == nRightDist &&
+ nTopDist == nBottomDist) )
+ {
+ cLine |= 0x10;
+ }
+
+ rStrm.WriteSChar( cLine );
+
+ if( nItemVersion >= BOX_4DISTS_VERSION && (cLine & 0x10) != 0 )
+ {
+ rStrm.WriteUInt16( nTopDist )
+ .WriteUInt16( nLeftDist )
+ .WriteUInt16( nRightDist )
+ .WriteUInt16( nBottomDist );
+ }
+
+ return rStrm;
+ }
+ }
+
+ namespace SvxLine
+ {
+ sal_uInt16 GetVersion(sal_uInt16)
+ {
+ return 0;
+ }
+
+ void Create(SvxLineItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ short nOutline, nInline, nDistance;
+ Color aColor;
+
+ tools::GenericTypeSerializer aSerializer(rStrm);
+ aSerializer.readColor(aColor);
+ rStrm.ReadInt16( nOutline ).ReadInt16( nInline ).ReadInt16( nDistance );
+ if( nOutline )
+ {
+ ::editeng::SvxBorderLine aLine( &aColor );
+ aLine.GuessLinesWidths(SvxBorderLineStyle::NONE, nOutline, nInline, nDistance);
+ rItem.SetLine( &aLine );
+ }
+ }
+
+ SvStream& Store(const SvxLineItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ const ::editeng::SvxBorderLine* pLine(rItem.GetLine());
+
+ if(nullptr != pLine)
+ {
+ tools::GenericTypeSerializer aSerializer(rStrm);
+ aSerializer.writeColor(pLine->GetColor());
+ rStrm.WriteInt16( pLine->GetOutWidth() )
+ .WriteInt16( pLine->GetInWidth() )
+ .WriteInt16( pLine->GetDistance() );
+ }
+ else
+ {
+ tools::GenericTypeSerializer aSerializer(rStrm);
+ aSerializer.writeColor(Color());
+ rStrm.WriteInt16( 0 ).WriteInt16( 0 ).WriteInt16( 0 );
+ }
+
+ return rStrm;
+ }
+ }
+
+ namespace SvxBrush
+ {
+ sal_uInt16 GetVersion(sal_uInt16)
+ {
+ return BRUSH_GRAPHIC_VERSION;
+ }
+
+ const sal_uInt16 LOAD_GRAPHIC = (sal_uInt16(0x0001));
+ const sal_uInt16 LOAD_LINK = (sal_uInt16(0x0002));
+ const sal_uInt16 LOAD_FILTER = (sal_uInt16(0x0004));
+
+ void Create(SvxBrushItem& rItem, SvStream& rStrm, sal_uInt16 nItemVersion)
+ {
+ bool bTrans;
+ Color aTempColor;
+ Color aTempFillColor;
+ sal_Int8 nStyle;
+
+ rStrm.ReadCharAsBool( bTrans );
+ TypeSerializer aSerializer(rStrm);
+ aSerializer.readColor(aTempColor);
+ aSerializer.readColor(aTempFillColor);
+ rStrm.ReadSChar( nStyle );
+
+ switch ( nStyle )
+ {
+ case 8: // BRUSH_25:
+ {
+ sal_uInt32 nRed = aTempColor.GetRed();
+ sal_uInt32 nGreen = aTempColor.GetGreen();
+ sal_uInt32 nBlue = aTempColor.GetBlue();
+ nRed += static_cast<sal_uInt32>(aTempFillColor.GetRed())*2;
+ nGreen += static_cast<sal_uInt32>(aTempFillColor.GetGreen())*2;
+ nBlue += static_cast<sal_uInt32>(aTempFillColor.GetBlue())*2;
+ rItem.SetColor(Color( static_cast<sal_Int8>(nRed/3), static_cast<sal_Int8>(nGreen/3), static_cast<sal_Int8>(nBlue/3) ));
+ }
+ break;
+
+ case 9: // BRUSH_50:
+ {
+ sal_uInt32 nRed = aTempColor.GetRed();
+ sal_uInt32 nGreen = aTempColor.GetGreen();
+ sal_uInt32 nBlue = aTempColor.GetBlue();
+ nRed += static_cast<sal_uInt32>(aTempFillColor.GetRed());
+ nGreen += static_cast<sal_uInt32>(aTempFillColor.GetGreen());
+ nBlue += static_cast<sal_uInt32>(aTempFillColor.GetBlue());
+ rItem.SetColor(Color( static_cast<sal_Int8>(nRed/2), static_cast<sal_Int8>(nGreen/2), static_cast<sal_Int8>(nBlue/2) ));
+ }
+ break;
+
+ case 10: // BRUSH_75:
+ {
+ sal_uInt32 nRed = aTempColor.GetRed()*2;
+ sal_uInt32 nGreen = aTempColor.GetGreen()*2;
+ sal_uInt32 nBlue = aTempColor.GetBlue()*2;
+ nRed += static_cast<sal_uInt32>(aTempFillColor.GetRed());
+ nGreen += static_cast<sal_uInt32>(aTempFillColor.GetGreen());
+ nBlue += static_cast<sal_uInt32>(aTempFillColor.GetBlue());
+ rItem.SetColor(Color( static_cast<sal_Int8>(nRed/3), static_cast<sal_Int8>(nGreen/3), static_cast<sal_Int8>(nBlue/3) ));
+ }
+ break;
+
+ case 0: // BRUSH_NULL:
+ rItem.SetColor(COL_TRANSPARENT);
+ break;
+
+ default:
+ rItem.SetColor(aTempColor);
+ }
+
+ if ( nItemVersion < BRUSH_GRAPHIC_VERSION )
+ return;
+
+ sal_uInt16 nDoLoad = 0;
+ sal_Int8 nPos;
+
+ rStrm.ReadUInt16( nDoLoad );
+
+ if ( nDoLoad & LOAD_GRAPHIC )
+ {
+ Graphic aGraphic;
+ aSerializer.readGraphic(aGraphic);
+ rItem.SetGraphicObject(GraphicObject(std::move(aGraphic)));
+
+ if( SVSTREAM_FILEFORMAT_ERROR == rStrm.GetError() )
+ {
+ rStrm.ResetError();
+ rStrm.SetError( ERRCODE_SVX_GRAPHIC_WRONG_FILEFORMAT.MakeWarning() );
+ }
+ }
+
+ if ( nDoLoad & LOAD_LINK )
+ {
+ // UNICODE: rStrm >> aRel;
+ OUString aRel = rStrm.ReadUniOrByteString(rStrm.GetStreamCharSet());
+
+ // TODO/MBA: how can we get a BaseURL here?!
+ OSL_FAIL("No BaseURL!");
+ OUString aAbs = INetURLObject::GetAbsURL( u"", aRel );
+ DBG_ASSERT( !aAbs.isEmpty(), "Invalid URL!" );
+ rItem.SetGraphicLink(aAbs);
+ }
+
+ if ( nDoLoad & LOAD_FILTER )
+ {
+ // UNICODE: rStrm >> maStrFilter;
+ rItem.SetGraphicFilter(rStrm.ReadUniOrByteString(rStrm.GetStreamCharSet()));
+ }
+
+ rStrm.ReadSChar( nPos );
+
+ rItem.SetGraphicPos(static_cast<SvxGraphicPosition>(nPos));
+ }
+
+ SvStream& Store(const SvxBrushItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ rStrm.WriteBool( false );
+ TypeSerializer aSerializer(rStrm);
+ aSerializer.writeColor(rItem.GetColor());
+ aSerializer.writeColor(rItem.GetColor());
+ rStrm.WriteSChar( rItem.GetColor().IsTransparent() ? 0 : 1 ); //BRUSH_NULL : BRUSH_SOLID
+
+ sal_uInt16 nDoLoad = 0;
+ const GraphicObject* pGraphicObject(rItem.GetGraphicObject());
+
+ if (nullptr != pGraphicObject && rItem.GetGraphicLink().isEmpty())
+ nDoLoad |= LOAD_GRAPHIC;
+ if ( !rItem.GetGraphicLink().isEmpty() )
+ nDoLoad |= LOAD_LINK;
+ if ( !rItem.GetGraphicFilter().isEmpty() )
+ nDoLoad |= LOAD_FILTER;
+ rStrm.WriteUInt16( nDoLoad );
+
+ if (nullptr != pGraphicObject && rItem.GetGraphicLink().isEmpty())
+ {
+ aSerializer.writeGraphic(pGraphicObject->GetGraphic());
+ }
+ if ( !rItem.GetGraphicLink().isEmpty() )
+ {
+ OSL_FAIL("No BaseURL!");
+ // TODO/MBA: how to get a BaseURL?!
+ OUString aRel = INetURLObject::GetRelURL( u"", rItem.GetGraphicLink() );
+ // UNICODE: rStrm << aRel;
+ rStrm.WriteUniOrByteString(aRel, rStrm.GetStreamCharSet());
+ }
+ if ( !rItem.GetGraphicFilter().isEmpty() )
+ {
+ // UNICODE: rStrm << rItem.GetGraphicFilter();
+ rStrm.WriteUniOrByteString(rItem.GetGraphicFilter(), rStrm.GetStreamCharSet());
+ }
+ rStrm.WriteSChar( rItem.GetGraphicPos() );
+ return rStrm;
+ }
+ }
+
+ namespace SvxAdjust
+ {
+ sal_uInt16 GetVersion(sal_uInt16 nFileFormatVersion)
+ {
+ return (nFileFormatVersion == SOFFICE_FILEFORMAT_31)
+ ? 0 : ADJUST_LASTBLOCK_VERSION;
+ }
+
+ void Create(SvxAdjustItem& rItem, SvStream& rStrm, sal_uInt16 nItemVersion)
+ {
+ char eAdjustment;
+ rStrm.ReadChar(eAdjustment);
+ rItem.SetAdjust(static_cast<::SvxAdjust>(eAdjustment));
+
+ if( nItemVersion >= ADJUST_LASTBLOCK_VERSION )
+ {
+ sal_Int8 nFlags;
+ rStrm.ReadSChar( nFlags );
+ rItem.SetAsFlags(nFlags);
+ }
+ }
+
+ SvStream& Store(const SvxAdjustItem& rItem, SvStream& rStrm, sal_uInt16 nItemVersion)
+ {
+ rStrm.WriteChar( static_cast<char>(rItem.GetAdjust()) );
+ if ( nItemVersion >= ADJUST_LASTBLOCK_VERSION )
+ {
+ const sal_Int8 nFlags(rItem.GetAsFlags());
+ rStrm.WriteSChar( nFlags );
+ }
+ return rStrm;
+ }
+ }
+
+ namespace SvxHorJustify
+ {
+ sal_uInt16 GetVersion(sal_uInt16)
+ {
+ return 0;
+ }
+
+ void Create(SvxHorJustifyItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ sal_uInt16 nVal(0);
+ rStrm.ReadUInt16( nVal );
+ rItem.SetValue(static_cast<::SvxCellHorJustify>(nVal));
+ }
+
+ SvStream& Store(const SvxHorJustifyItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ rStrm.WriteUInt16( static_cast<sal_uInt16>(rItem.GetValue()) );
+ return rStrm;
+ }
+ }
+
+ namespace SvxVerJustify
+ {
+ sal_uInt16 GetVersion(sal_uInt16)
+ {
+ return 0;
+ }
+
+ void Create(SvxVerJustifyItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ sal_uInt16 nVal(0);
+ rStrm.ReadUInt16( nVal );
+ rItem.SetValue(static_cast<::SvxCellVerJustify>(nVal));
+ }
+
+ SvStream& Store(const SvxVerJustifyItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ rStrm.WriteUInt16( static_cast<sal_uInt16>(rItem.GetValue()) );
+ return rStrm;
+ }
+ }
+
+ namespace SvxFrameDirection
+ {
+ sal_uInt16 GetVersion(sal_uInt16 nFileFormatVersion)
+ {
+ return SOFFICE_FILEFORMAT_50 > nFileFormatVersion ? USHRT_MAX : 0;
+ }
+
+ void Create(SvxFrameDirectionItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ sal_uInt16 nVal(0);
+ rStrm.ReadUInt16( nVal );
+ rItem.SetValue(static_cast<::SvxFrameDirection>(nVal));
+ }
+
+ SvStream& Store(const SvxFrameDirectionItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ rStrm.WriteUInt16( static_cast<sal_uInt16>(rItem.GetValue()) );
+ return rStrm;
+ }
+ }
+
+ namespace SvxFormatBreak
+ {
+ sal_uInt16 GetVersion(sal_uInt16 nFileFormatVersion)
+ {
+ DBG_ASSERT( SOFFICE_FILEFORMAT_31==nFileFormatVersion ||
+ SOFFICE_FILEFORMAT_40==nFileFormatVersion ||
+ SOFFICE_FILEFORMAT_50==nFileFormatVersion,
+ "SvxFormatBreakItem: Is there a new file format? ");
+ return SOFFICE_FILEFORMAT_31==nFileFormatVersion ||
+ SOFFICE_FILEFORMAT_40==nFileFormatVersion ? 0 : FMTBREAK_NOAUTO;
+ }
+
+ void Create(SvxFormatBreakItem& rItem, SvStream& rStrm, sal_uInt16 nItemVersion)
+ {
+ sal_Int8 eBreak, bDummy;
+ rStrm.ReadSChar( eBreak );
+ if( FMTBREAK_NOAUTO > nItemVersion )
+ rStrm.ReadSChar( bDummy );
+ rItem.SetValue(static_cast<::SvxBreak>(eBreak));
+ }
+
+ SvStream& Store(const SvxFormatBreakItem& rItem, SvStream& rStrm, sal_uInt16 nItemVersion)
+ {
+ rStrm.WriteSChar( rItem.GetEnumValue() );
+ if( FMTBREAK_NOAUTO > nItemVersion )
+ rStrm.WriteSChar( 0x01 );
+ return rStrm;
+ }
+ }
+
+ namespace SvxFormatKeep
+ {
+ sal_uInt16 GetVersion(sal_uInt16)
+ {
+ return 0;
+ }
+
+ void Create(SvxFormatKeepItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ // derived from SfxBoolItem, but that uses
+ // rStream.ReadCharAsBool( tmp );
+ sal_Int8 bIsKeep;
+ rStrm.ReadSChar( bIsKeep );
+ rItem.SetValue(static_cast<bool>(bIsKeep));
+ }
+
+ SvStream& Store(const SvxFormatKeepItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ // derived from SfxBoolItem, but that uses
+ // rStream.WriteBool( m_bValue ); // not bool for serialization!
+ rStrm.WriteSChar( static_cast<sal_Int8>(rItem.GetValue()) );
+ return rStrm;
+ }
+ }
+
+ namespace SvxShadow
+ {
+ sal_uInt16 GetVersion(sal_uInt16)
+ {
+ return 0;
+ }
+
+ void Create(SvxShadowItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ sal_Int8 cLoc;
+ sal_uInt16 _nWidth;
+ bool bTrans;
+ Color aColor;
+ Color aFillColor;
+ sal_Int8 nStyle;
+ rStrm.ReadSChar( cLoc ).ReadUInt16( _nWidth ).ReadCharAsBool( bTrans );
+ tools::GenericTypeSerializer aSerializer(rStrm);
+ aSerializer.readColor(aColor);
+ aSerializer.readColor(aFillColor);
+ rStrm.ReadSChar(nStyle);
+ aColor.SetAlpha(bTrans ? 0 : 255);
+
+ rItem.SetLocation(static_cast<SvxShadowLocation>(cLoc));
+ rItem.SetWidth(_nWidth);
+ rItem.SetColor(aColor);
+ }
+
+ SvStream& Store(const SvxShadowItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ rStrm.WriteSChar( static_cast<sal_uInt8>(rItem.GetLocation()) )
+ .WriteUInt16( rItem.GetWidth() )
+ .WriteBool( rItem.GetColor().IsTransparent() );
+ tools::GenericTypeSerializer aSerializer(rStrm);
+ aSerializer.writeColor(rItem.GetColor());
+ aSerializer.writeColor(rItem.GetColor());
+ rStrm.WriteSChar( rItem.GetColor().IsTransparent() ? 0 : 1 ); //BRUSH_NULL : BRUSH_SOLID
+ return rStrm;
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/items/numitem.cxx b/editeng/source/items/numitem.cxx
new file mode 100644
index 0000000000..983eff2779
--- /dev/null
+++ b/editeng/source/items/numitem.cxx
@@ -0,0 +1,1156 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+
+#include <editeng/numitem.hxx>
+
+#include <com/sun/star/text/VertOrientation.hpp>
+#include <comphelper/propertyvalue.hxx>
+#include <editeng/brushitem.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <vcl/font.hxx>
+#include <vcl/settings.hxx>
+#include <editeng/editids.hrc>
+#include <editeng/numdef.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/svapp.hxx>
+#include <com/sun/star/text/XNumberingFormatter.hpp>
+#include <com/sun/star/text/DefaultNumberingProvider.hpp>
+#include <com/sun/star/text/XDefaultNumberingProvider.hpp>
+#include <com/sun/star/style/NumberingType.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <comphelper/fileformat.h>
+#include <comphelper/processfactory.hxx>
+#include <tools/mapunit.hxx>
+#include <tools/stream.hxx>
+#include <tools/debug.hxx>
+#include <tools/GenericTypeSerializer.hxx>
+#include <unotools/configmgr.hxx>
+#include <libxml/xmlwriter.h>
+#include <editeng/unonrule.hxx>
+#include <sal/log.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <editeng/legacyitem.hxx>
+
+constexpr sal_Int32 DEF_WRITER_LSPACE = 500; //Standard Indentation
+constexpr sal_Int32 DEF_DRAW_LSPACE = 800; //Standard Indentation
+
+constexpr sal_uInt16 NUMITEM_VERSION_03 = 0x03;
+constexpr sal_uInt16 NUMITEM_VERSION_04 = 0x04;
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::style;
+
+sal_Int32 SvxNumberType::nRefCount = 0;
+css::uno::Reference<css::text::XNumberingFormatter> SvxNumberType::xFormatter;
+static void lcl_getFormatter(css::uno::Reference<css::text::XNumberingFormatter>& _xFormatter)
+{
+ if(_xFormatter.is())
+ return;
+
+ try
+ {
+ Reference<XComponentContext> xContext( ::comphelper::getProcessComponentContext() );
+ Reference<XDefaultNumberingProvider> xRet = text::DefaultNumberingProvider::create(xContext);
+ _xFormatter.set(xRet, UNO_QUERY);
+ }
+ catch(const Exception&)
+ {
+ SAL_WARN("editeng", "service missing: \"com.sun.star.text.DefaultNumberingProvider\"");
+ }
+}
+
+SvxNumberType::SvxNumberType(SvxNumType nType) :
+ nNumType(nType),
+ bShowSymbol(true)
+{
+ nRefCount++;
+}
+
+SvxNumberType::SvxNumberType(const SvxNumberType& rType) :
+ nNumType(rType.nNumType),
+ bShowSymbol(rType.bShowSymbol)
+{
+ nRefCount++;
+}
+
+SvxNumberType::~SvxNumberType()
+{
+ if(!--nRefCount)
+ xFormatter = nullptr;
+}
+
+OUString SvxNumberType::GetNumStr( sal_Int32 nNo ) const
+{
+ LanguageTag aLang = utl::ConfigManager::IsFuzzing() ?
+ LanguageTag("en-US") :
+ Application::GetSettings().GetLanguageTag();
+ return GetNumStr( nNo, aLang.getLocale() );
+}
+
+static bool isArabicNumberingType(SvxNumType t)
+{
+ return t == SVX_NUM_ARABIC || t == SVX_NUM_ARABIC_ZERO || t == SVX_NUM_ARABIC_ZERO3
+ || t == SVX_NUM_ARABIC_ZERO4 || t == SVX_NUM_ARABIC_ZERO5;
+}
+
+OUString SvxNumberType::GetNumStr( sal_Int32 nNo, const css::lang::Locale& rLocale, bool bIsLegal ) const
+{
+ lcl_getFormatter(xFormatter);
+ if(!xFormatter.is())
+ return OUString();
+
+ if(bShowSymbol)
+ {
+ switch(nNumType)
+ {
+ case NumberingType::CHAR_SPECIAL:
+ case NumberingType::BITMAP:
+ break;
+ default:
+ {
+ // '0' allowed for ARABIC numberings
+ if(NumberingType::ARABIC == nNumType && 0 == nNo )
+ return OUString('0');
+ else
+ {
+ SvxNumType nActType = !bIsLegal || isArabicNumberingType(nNumType) ? nNumType : SVX_NUM_ARABIC;
+ static constexpr OUString sNumberingType = u"NumberingType"_ustr;
+ static constexpr OUString sValue = u"Value"_ustr;
+ Sequence< PropertyValue > aProperties
+ {
+ comphelper::makePropertyValue(sNumberingType, static_cast<sal_uInt16>(nActType)),
+ comphelper::makePropertyValue(sValue, nNo)
+ };
+
+ try
+ {
+ return xFormatter->makeNumberingString( aProperties, rLocale );
+ }
+ catch(const Exception&)
+ {
+ }
+ }
+ }
+ }
+ }
+ return OUString();
+}
+
+void SvxNumberType::dumpAsXml( xmlTextWriterPtr pWriter ) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxNumberType"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("NumType"), BAD_CAST(OString::number(nNumType).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SvxNumberFormat::SvxNumberFormat( SvxNumType eType )
+ : SvxNumberType(eType),
+ eNumAdjust(SvxAdjust::Left),
+ nInclUpperLevels(1),
+ nStart(1),
+ cBullet(SVX_DEF_BULLET),
+ nBulletRelSize(100),
+ nBulletColor(COL_BLACK),
+ mePositionAndSpaceMode( LABEL_WIDTH_AND_POSITION ),
+ nFirstLineOffset(0),
+ nAbsLSpace(0),
+ nCharTextDistance(0),
+ meLabelFollowedBy( LISTTAB ),
+ mnListtabPos( 0 ),
+ mnFirstLineIndent( 0 ),
+ mnIndentAt( 0 ),
+ eVertOrient(text::VertOrientation::NONE)
+{
+}
+
+SvxNumberFormat::SvxNumberFormat(const SvxNumberFormat& rFormat) :
+ SvxNumberType(rFormat),
+ mePositionAndSpaceMode( rFormat.mePositionAndSpaceMode )
+{
+ *this = rFormat;
+}
+
+SvxNumberFormat::SvxNumberFormat( SvStream &rStream )
+ : nStart(0)
+ , nBulletRelSize(100)
+ , nFirstLineOffset(0)
+ , nAbsLSpace(0)
+ , nCharTextDistance(0)
+{
+ sal_uInt16 nTmp16(0);
+ sal_Int32 nTmp32(0);
+ rStream.ReadUInt16( nTmp16 ); // Version number
+
+ rStream.ReadUInt16( nTmp16 ); SetNumberingType( static_cast<SvxNumType>(nTmp16) );
+ rStream.ReadUInt16( nTmp16 ); eNumAdjust = static_cast<SvxAdjust>(nTmp16);
+ rStream.ReadUInt16( nTmp16 ); nInclUpperLevels = nTmp16;
+ rStream.ReadUInt16( nStart );
+ rStream.ReadUInt16( nTmp16 ); cBullet = static_cast<sal_Unicode>(nTmp16);
+
+ sal_Int16 temp = 0;
+ rStream.ReadInt16( temp );
+ nFirstLineOffset = temp;
+ temp = 0;
+ rStream.ReadInt16( temp );
+ nAbsLSpace = temp;
+ rStream.SeekRel(2); //skip old now unused nLSpace;
+
+ rStream.ReadInt16( nCharTextDistance );
+
+ sPrefix = rStream.ReadUniOrByteString( rStream.GetStreamCharSet() );
+ sSuffix = rStream.ReadUniOrByteString( rStream.GetStreamCharSet() );
+ sCharStyleName = rStream.ReadUniOrByteString( rStream.GetStreamCharSet() );
+
+ sal_uInt16 hasGraphicBrush = 0;
+ rStream.ReadUInt16( hasGraphicBrush );
+ if ( hasGraphicBrush )
+ {
+ pGraphicBrush.reset(new SvxBrushItem(SID_ATTR_BRUSH));
+ legacy::SvxBrush::Create(*pGraphicBrush, rStream, BRUSH_GRAPHIC_VERSION);
+ }
+ else pGraphicBrush = nullptr;
+ rStream.ReadUInt16( nTmp16 ); eVertOrient = nTmp16;
+
+ sal_uInt16 hasBulletFont = 0;
+ rStream.ReadUInt16( hasBulletFont );
+ if ( hasBulletFont )
+ {
+ pBulletFont.emplace();
+ ReadFont( rStream, *pBulletFont );
+ }
+ else pBulletFont.reset();
+
+ tools::GenericTypeSerializer aSerializer(rStream);
+ aSerializer.readSize(aGraphicSize);
+ aSerializer.readColor(nBulletColor);
+
+ rStream.ReadUInt16( nBulletRelSize );
+ rStream.ReadUInt16( nTmp16 ); SetShowSymbol( nTmp16 != 0 );
+
+ rStream.ReadUInt16( nTmp16 ); mePositionAndSpaceMode = static_cast<SvxNumPositionAndSpaceMode>(nTmp16);
+ rStream.ReadUInt16( nTmp16 ); meLabelFollowedBy = static_cast<LabelFollowedBy>(nTmp16);
+ rStream.ReadInt32( nTmp32 ); mnListtabPos = nTmp32;
+ rStream.ReadInt32( nTmp32 ); mnFirstLineIndent = nTmp32;
+ rStream.ReadInt32( nTmp32 ); mnIndentAt = nTmp32;
+}
+
+SvxNumberFormat::~SvxNumberFormat()
+{
+}
+
+void SvxNumberFormat::Store(SvStream &rStream, FontToSubsFontConverter pConverter)
+{
+ if(pConverter && pBulletFont)
+ {
+ cBullet = ConvertFontToSubsFontChar(pConverter, cBullet);
+ OUString sFontName = GetFontToSubsFontName(pConverter);
+ pBulletFont->SetFamilyName(sFontName);
+ }
+
+ tools::GenericTypeSerializer aSerializer(rStream);
+
+ rStream.WriteUInt16( NUMITEM_VERSION_04 );
+
+ rStream.WriteUInt16( GetNumberingType() );
+ rStream.WriteUInt16( static_cast<sal_uInt16>(eNumAdjust) );
+ rStream.WriteUInt16( nInclUpperLevels );
+ rStream.WriteUInt16( nStart );
+ rStream.WriteUInt16( cBullet );
+
+ rStream.WriteInt16(
+ sal_Int16(std::clamp<sal_Int32>(nFirstLineOffset, SAL_MIN_INT16, SAL_MAX_INT16)) );
+ //TODO: better way to handle out-of-bounds value?
+ rStream.WriteInt16(
+ sal_Int16(std::clamp<sal_Int32>(nAbsLSpace, SAL_MIN_INT16, SAL_MAX_INT16)) );
+ //TODO: better way to handle out-of-bounds value?
+ rStream.WriteInt16( 0 ); // write a dummy for old now unused nLSpace
+
+ rStream.WriteInt16( nCharTextDistance );
+ rtl_TextEncoding eEnc = osl_getThreadTextEncoding();
+ rStream.WriteUniOrByteString(sPrefix, eEnc);
+ rStream.WriteUniOrByteString(sSuffix, eEnc);
+ rStream.WriteUniOrByteString(sCharStyleName, eEnc);
+ if(pGraphicBrush)
+ {
+ rStream.WriteUInt16( 1 );
+
+ // in SD or SI force bullet itself to be stored,
+ // for that purpose throw away link when link and graphic
+ // are present, so Brush save is forced
+ if(!pGraphicBrush->GetGraphicLink().isEmpty() && pGraphicBrush->GetGraphic())
+ {
+ pGraphicBrush->SetGraphicLink("");
+ }
+
+ legacy::SvxBrush::Store(*pGraphicBrush, rStream, BRUSH_GRAPHIC_VERSION);
+ }
+ else
+ rStream.WriteUInt16( 0 );
+
+ rStream.WriteUInt16( eVertOrient );
+ if(pBulletFont)
+ {
+ rStream.WriteUInt16( 1 );
+ WriteFont( rStream, *pBulletFont );
+ }
+ else
+ rStream.WriteUInt16( 0 );
+
+ aSerializer.writeSize(aGraphicSize);
+
+ Color nTempColor = nBulletColor;
+ if(COL_AUTO == nBulletColor)
+ nTempColor = COL_BLACK;
+
+ aSerializer.writeColor(nTempColor);
+ rStream.WriteUInt16( nBulletRelSize );
+ rStream.WriteUInt16( sal_uInt16(IsShowSymbol()) );
+
+ rStream.WriteUInt16( mePositionAndSpaceMode );
+ rStream.WriteUInt16( meLabelFollowedBy );
+ rStream.WriteInt32( mnListtabPos );
+ rStream.WriteInt32( mnFirstLineIndent );
+ rStream.WriteInt32( mnIndentAt );
+}
+
+SvxNumberFormat& SvxNumberFormat::operator=( const SvxNumberFormat& rFormat )
+{
+ if (& rFormat == this) { return *this; }
+
+ SvxNumberType::SetNumberingType(rFormat.GetNumberingType());
+ eNumAdjust = rFormat.eNumAdjust ;
+ nInclUpperLevels = rFormat.nInclUpperLevels ;
+ nStart = rFormat.nStart ;
+ cBullet = rFormat.cBullet ;
+ mePositionAndSpaceMode = rFormat.mePositionAndSpaceMode;
+ nFirstLineOffset = rFormat.nFirstLineOffset;
+ nAbsLSpace = rFormat.nAbsLSpace ;
+ nCharTextDistance = rFormat.nCharTextDistance ;
+ meLabelFollowedBy = rFormat.meLabelFollowedBy;
+ mnListtabPos = rFormat.mnListtabPos;
+ mnFirstLineIndent = rFormat.mnFirstLineIndent;
+ mnIndentAt = rFormat.mnIndentAt;
+ eVertOrient = rFormat.eVertOrient;
+ sPrefix = rFormat.sPrefix;
+ sSuffix = rFormat.sSuffix;
+ sListFormat = rFormat.sListFormat;
+ aGraphicSize = rFormat.aGraphicSize ;
+ nBulletColor = rFormat.nBulletColor ;
+ nBulletRelSize = rFormat.nBulletRelSize;
+ SetShowSymbol(rFormat.IsShowSymbol());
+ sCharStyleName = rFormat.sCharStyleName;
+ pGraphicBrush.reset();
+ if(rFormat.pGraphicBrush)
+ {
+ pGraphicBrush.reset( new SvxBrushItem(*rFormat.pGraphicBrush) );
+ }
+ pBulletFont.reset();
+ if(rFormat.pBulletFont)
+ pBulletFont = *rFormat.pBulletFont;
+ mbIsLegal = rFormat.mbIsLegal;
+ return *this;
+}
+
+bool SvxNumberFormat::operator==( const SvxNumberFormat& rFormat) const
+{
+ if( GetNumberingType() != rFormat.GetNumberingType() ||
+ eNumAdjust != rFormat.eNumAdjust ||
+ nInclUpperLevels != rFormat.nInclUpperLevels ||
+ nStart != rFormat.nStart ||
+ cBullet != rFormat.cBullet ||
+ mePositionAndSpaceMode != rFormat.mePositionAndSpaceMode ||
+ nFirstLineOffset != rFormat.nFirstLineOffset ||
+ nAbsLSpace != rFormat.nAbsLSpace ||
+ nCharTextDistance != rFormat.nCharTextDistance ||
+ meLabelFollowedBy != rFormat.meLabelFollowedBy ||
+ mnListtabPos != rFormat.mnListtabPos ||
+ mnFirstLineIndent != rFormat.mnFirstLineIndent ||
+ mnIndentAt != rFormat.mnIndentAt ||
+ eVertOrient != rFormat.eVertOrient ||
+ sPrefix != rFormat.sPrefix ||
+ sSuffix != rFormat.sSuffix ||
+ sListFormat != rFormat.sListFormat ||
+ aGraphicSize != rFormat.aGraphicSize ||
+ nBulletColor != rFormat.nBulletColor ||
+ nBulletRelSize != rFormat.nBulletRelSize ||
+ IsShowSymbol() != rFormat.IsShowSymbol() ||
+ sCharStyleName != rFormat.sCharStyleName ||
+ mbIsLegal != rFormat.mbIsLegal
+ )
+ return false;
+ if (
+ (pGraphicBrush && !rFormat.pGraphicBrush) ||
+ (!pGraphicBrush && rFormat.pGraphicBrush) ||
+ (pGraphicBrush && *pGraphicBrush != *rFormat.pGraphicBrush)
+ )
+ {
+ return false;
+ }
+ if (
+ (pBulletFont && !rFormat.pBulletFont) ||
+ (!pBulletFont && rFormat.pBulletFont) ||
+ (pBulletFont && *pBulletFont != *rFormat.pBulletFont)
+ )
+ {
+ return false;
+ }
+ return true;
+}
+
+void SvxNumberFormat::SetGraphicBrush( const SvxBrushItem* pBrushItem,
+ const Size* pSize, const sal_Int16* pOrient)
+{
+ if (!pBrushItem)
+ pGraphicBrush.reset();
+ else if ( !pGraphicBrush || (*pBrushItem != *pGraphicBrush) )
+ pGraphicBrush.reset(pBrushItem->Clone());
+
+ if(pOrient)
+ eVertOrient = *pOrient;
+ else
+ eVertOrient = text::VertOrientation::NONE;
+ if(pSize)
+ aGraphicSize = *pSize;
+ else
+ {
+ aGraphicSize.setWidth(0);
+ aGraphicSize.setHeight(0);
+ }
+}
+
+void SvxNumberFormat::SetGraphic( const OUString& rName )
+{
+ if( pGraphicBrush && pGraphicBrush->GetGraphicLink() == rName )
+ return ;
+
+ pGraphicBrush.reset( new SvxBrushItem( rName, "", GPOS_AREA, 0 ) );
+ if( eVertOrient == text::VertOrientation::NONE )
+ eVertOrient = text::VertOrientation::TOP;
+
+ aGraphicSize.setWidth(0);
+ aGraphicSize.setHeight(0);
+}
+
+sal_Int16 SvxNumberFormat::GetVertOrient() const
+{
+ return eVertOrient;
+}
+
+void SvxNumberFormat::SetBulletFont(const vcl::Font* pFont)
+{
+ if (pFont)
+ pBulletFont = *pFont;
+ else
+ pBulletFont.reset();
+}
+
+void SvxNumberFormat::SetPositionAndSpaceMode( SvxNumPositionAndSpaceMode ePositionAndSpaceMode )
+{
+ mePositionAndSpaceMode = ePositionAndSpaceMode;
+}
+
+sal_Int32 SvxNumberFormat::GetAbsLSpace() const
+{
+ return mePositionAndSpaceMode == LABEL_WIDTH_AND_POSITION
+ ? nAbsLSpace
+ : static_cast<sal_Int32>( GetFirstLineIndent() + GetIndentAt() );
+}
+sal_Int32 SvxNumberFormat::GetFirstLineOffset() const
+{
+ return mePositionAndSpaceMode == LABEL_WIDTH_AND_POSITION
+ ? nFirstLineOffset
+ : static_cast<sal_Int32>( GetFirstLineIndent() );
+}
+short SvxNumberFormat::GetCharTextDistance() const
+{
+ return mePositionAndSpaceMode == LABEL_WIDTH_AND_POSITION ? nCharTextDistance : 0;
+}
+
+void SvxNumberFormat::SetLabelFollowedBy( const LabelFollowedBy eLabelFollowedBy )
+{
+ meLabelFollowedBy = eLabelFollowedBy;
+}
+
+OUString SvxNumberFormat::GetLabelFollowedByAsString() const
+{
+ switch (meLabelFollowedBy)
+ {
+ case LISTTAB:
+ return "\t";
+ case SPACE:
+ return " ";
+ case NEWLINE:
+ return "\n";
+ case NOTHING:
+ // intentionally left blank.
+ return OUString();
+ default:
+ SAL_WARN("editeng", "Unknown SvxNumberFormat::GetLabelFollowedBy() return value");
+ assert(false);
+ }
+ return OUString();
+}
+
+void SvxNumberFormat::SetListtabPos( const tools::Long nListtabPos )
+{
+ mnListtabPos = nListtabPos;
+}
+void SvxNumberFormat::SetFirstLineIndent( const tools::Long nFirstLineIndent )
+{
+ mnFirstLineIndent = nFirstLineIndent;
+}
+void SvxNumberFormat::SetIndentAt( const tools::Long nIndentAt )
+{
+ mnIndentAt = nIndentAt;
+}
+
+Size SvxNumberFormat::GetGraphicSizeMM100(const Graphic* pGraphic)
+{
+ const MapMode aMapMM100( MapUnit::Map100thMM );
+ const Size& rSize = pGraphic->GetPrefSize();
+ Size aRetSize;
+ if ( pGraphic->GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel )
+ {
+ OutputDevice* pOutDev = Application::GetDefaultDevice();
+ MapMode aOldMap( pOutDev->GetMapMode() );
+ pOutDev->SetMapMode( aMapMM100 );
+ aRetSize = pOutDev->PixelToLogic( rSize );
+ pOutDev->SetMapMode( aOldMap );
+ }
+ else
+ aRetSize = OutputDevice::LogicToLogic( rSize, pGraphic->GetPrefMapMode(), aMapMM100 );
+ return aRetSize;
+}
+
+OUString SvxNumberFormat::CreateRomanString( sal_Int32 nNo, bool bUpper )
+{
+ OUStringBuffer sRet;
+
+ constexpr char romans[][13] = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
+ constexpr sal_Int32 values[] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
+
+ for (size_t i = 0; i < std::size(romans); ++i)
+ {
+ while(nNo - values[i] >= 0)
+ {
+ sRet.appendAscii(romans[i]);
+ nNo -= values[i];
+ }
+ }
+
+ return bUpper ? sRet.makeStringAndClear()
+ : sRet.makeStringAndClear().toAsciiLowerCase();
+}
+
+void SvxNumberFormat::SetPrefix(const OUString& rSet)
+{
+ // ListFormat manages the prefix. If badly changed via this function, sListFormat is invalidated
+ if (sListFormat && rSet.getLength() != sPrefix.getLength())
+ sListFormat.reset();
+
+ sPrefix = rSet;
+}
+
+void SvxNumberFormat::SetSuffix(const OUString& rSet)
+{
+ // ListFormat manages the suffix. If badly changed via this function, sListFormat is invalidated
+ if (sListFormat && rSet.getLength() != sSuffix.getLength())
+ sListFormat.reset();
+
+ sSuffix = rSet;
+}
+
+void SvxNumberFormat::SetListFormat(const OUString& rPrefix, const OUString& rSuffix, int nLevel)
+{
+ sPrefix = rPrefix;
+ sSuffix = rSuffix;
+
+ // Generate list format
+ sListFormat = std::make_optional(sPrefix);
+
+ for (int i = 1; i <= nInclUpperLevels; i++)
+ {
+ int nLevelId = nLevel - nInclUpperLevels + i;
+ if (nLevelId < 0)
+ // There can be cases with current level 1, but request to show 10 upper levels. Trim it
+ continue;
+
+ *sListFormat += "%";
+ *sListFormat += OUString::number(nLevelId + 1);
+ *sListFormat += "%";
+ if (i != nInclUpperLevels)
+ *sListFormat += "."; // Default separator for older ODT
+ }
+
+ *sListFormat += sSuffix;
+}
+
+void SvxNumberFormat::SetListFormat(std::optional<OUString> oSet)
+{
+ sPrefix.clear();
+ sSuffix.clear();
+
+ sListFormat = oSet;
+
+ if (!oSet.has_value())
+ {
+ return;
+ }
+
+ // For backward compatibility and UI we should create something looking like
+ // a prefix, suffix and included levels also. This is not possible in general case
+ // since level format string is much more flexible. But for most cases is okay
+ sal_Int32 nFirstReplacement = sListFormat->indexOf('%');
+ sal_Int32 nLastReplacement = sListFormat->lastIndexOf('%') + 1;
+ if (nFirstReplacement > 0)
+ // Everything before first '%' will be prefix
+ sPrefix = sListFormat->copy(0, nFirstReplacement);
+ if (nLastReplacement >= 0 && nLastReplacement < sListFormat->getLength())
+ // Everything beyond last '%' is a suffix
+ sSuffix = sListFormat->copy(nLastReplacement);
+
+ sal_uInt8 nPercents = 0;
+ for (sal_Int32 i = 0; i < sListFormat->getLength(); i++)
+ {
+ if ((*sListFormat)[i] == '%')
+ nPercents++;
+ }
+ nInclUpperLevels = nPercents/2;
+ if (nInclUpperLevels < 1)
+ {
+ // There should be always at least one level. This will be not required
+ // in future (when we get rid of prefix/suffix), but nowadays there
+ // are too many conversions "list format" <-> "prefix/suffix/inclUpperLevel"
+ nInclUpperLevels = 1;
+ }
+}
+
+OUString SvxNumberFormat::GetListFormat(bool bIncludePrefixSuffix /*= true*/) const
+{
+ assert(sListFormat.has_value());
+
+ if (bIncludePrefixSuffix)
+ return *sListFormat;
+
+ // Strip prefix & suffix from string
+ return sListFormat->copy(sPrefix.getLength(), sListFormat->getLength() - sPrefix.getLength() - sSuffix.getLength());
+}
+
+OUString SvxNumberFormat::GetCharFormatName()const
+{
+ return sCharStyleName;
+}
+
+sal_Int32 SvxNumRule::nRefCount = 0;
+static SvxNumberFormat* pStdNumFmt = nullptr;
+static SvxNumberFormat* pStdOutlineNumFmt = nullptr;
+SvxNumRule::SvxNumRule( SvxNumRuleFlags nFeatures,
+ sal_uInt16 nLevels,
+ bool bCont,
+ SvxNumRuleType eType,
+ SvxNumberFormat::SvxNumPositionAndSpaceMode
+ eDefaultNumberFormatPositionAndSpaceMode )
+ : nLevelCount(nLevels),
+ nFeatureFlags(nFeatures),
+ eNumberingType(eType),
+ bContinuousNumbering(bCont)
+{
+ ++nRefCount;
+ for(sal_uInt16 i = 0; i < SVX_MAX_NUM; i++)
+ {
+ if(i < nLevels)
+ {
+ aFmts[i].reset( new SvxNumberFormat(SVX_NUM_CHARS_UPPER_LETTER) );
+ // It is a distinction between writer and draw
+ if(nFeatures & SvxNumRuleFlags::CONTINUOUS)
+ {
+ if ( eDefaultNumberFormatPositionAndSpaceMode ==
+ SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFmts[i]->SetAbsLSpace(o3tl::toTwips(DEF_WRITER_LSPACE * (i+1), o3tl::Length::mm100));
+ aFmts[i]->SetFirstLineOffset(o3tl::toTwips(-DEF_WRITER_LSPACE, o3tl::Length::mm100));
+ }
+ else if ( eDefaultNumberFormatPositionAndSpaceMode ==
+ SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ // first line indent of general numbering in inch: -0,25 inch
+ constexpr tools::Long cFirstLineIndent = o3tl::toTwips(-0.25, o3tl::Length::in);
+ // indent values of general numbering in inch:
+ // 0,5 0,75 1,0 1,25 1,5
+ // 1,75 2,0 2,25 2,5 2,75
+ constexpr tools::Long cIndentAt = o3tl::toTwips(0.25, o3tl::Length::in);
+ aFmts[i]->SetPositionAndSpaceMode( SvxNumberFormat::LABEL_ALIGNMENT );
+ aFmts[i]->SetLabelFollowedBy( SvxNumberFormat::LISTTAB );
+ aFmts[i]->SetListtabPos( cIndentAt * (i+2) );
+ aFmts[i]->SetFirstLineIndent( cFirstLineIndent );
+ aFmts[i]->SetIndentAt( cIndentAt * (i+2) );
+ }
+ }
+ else
+ {
+ aFmts[i]->SetAbsLSpace( DEF_DRAW_LSPACE * i );
+ }
+ }
+ else
+ aFmts[i] = nullptr;
+ aFmtsSet[i] = false;
+ }
+}
+
+SvxNumRule::SvxNumRule(const SvxNumRule& rCopy)
+{
+ ++nRefCount;
+ nLevelCount = rCopy.nLevelCount ;
+ nFeatureFlags = rCopy.nFeatureFlags ;
+ bContinuousNumbering = rCopy.bContinuousNumbering;
+ eNumberingType = rCopy.eNumberingType;
+ for(sal_uInt16 i = 0; i < SVX_MAX_NUM; i++)
+ {
+ if(rCopy.aFmts[i])
+ aFmts[i].reset( new SvxNumberFormat(*rCopy.aFmts[i]) );
+ else
+ aFmts[i].reset();
+ aFmtsSet[i] = rCopy.aFmtsSet[i];
+ }
+}
+
+SvxNumRule::SvxNumRule(SvxNumRule&& rCopy) noexcept
+{
+ ++nRefCount;
+ nLevelCount = rCopy.nLevelCount ;
+ nFeatureFlags = rCopy.nFeatureFlags ;
+ bContinuousNumbering = rCopy.bContinuousNumbering;
+ eNumberingType = rCopy.eNumberingType;
+ for(sal_uInt16 i = 0; i < SVX_MAX_NUM; i++)
+ {
+ if(rCopy.aFmts[i])
+ aFmts[i] = std::move(rCopy.aFmts[i]);
+ aFmtsSet[i] = rCopy.aFmtsSet[i];
+ }
+}
+
+SvxNumRule::SvxNumRule( SvStream &rStream )
+ : nLevelCount(0)
+{
+ sal_uInt16 nTmp16(0);
+ rStream.ReadUInt16( nTmp16 ); // NUM_ITEM_VERSION
+ rStream.ReadUInt16( nLevelCount );
+
+ if (nLevelCount > SVX_MAX_NUM)
+ {
+ SAL_WARN("editeng", "nLevelCount: " << nLevelCount << " greater than max of: " << SVX_MAX_NUM);
+ nLevelCount = SVX_MAX_NUM;
+ }
+
+ // first nFeatureFlags of old Versions
+ rStream.ReadUInt16( nTmp16 ); nFeatureFlags = static_cast<SvxNumRuleFlags>(nTmp16);
+ rStream.ReadUInt16( nTmp16 ); bContinuousNumbering = nTmp16;
+ rStream.ReadUInt16( nTmp16 ); eNumberingType = static_cast<SvxNumRuleType>(nTmp16);
+
+ for (sal_uInt16 i = 0; i < SVX_MAX_NUM; i++)
+ {
+ rStream.ReadUInt16( nTmp16 );
+ bool hasNumberingFormat = nTmp16 & 1;
+ aFmtsSet[i] = nTmp16 & 2; // fdo#68648 reset flag
+ if ( hasNumberingFormat ){
+ aFmts[i].reset( new SvxNumberFormat( rStream ) );
+ }
+ else
+ {
+ aFmts[i].reset();
+ aFmtsSet[i] = false; // actually only false is valid
+ }
+ }
+ //second nFeatureFlags for new versions
+ rStream.ReadUInt16( nTmp16 ); nFeatureFlags = static_cast<SvxNumRuleFlags>(nTmp16);
+}
+
+void SvxNumRule::Store( SvStream &rStream )
+{
+ rStream.WriteUInt16( NUMITEM_VERSION_03 );
+ rStream.WriteUInt16( nLevelCount );
+ //first save of nFeatureFlags for old versions
+ rStream.WriteUInt16( static_cast<sal_uInt16>(nFeatureFlags) );
+ rStream.WriteUInt16( sal_uInt16(bContinuousNumbering) );
+ rStream.WriteUInt16( static_cast<sal_uInt16>(eNumberingType) );
+
+ FontToSubsFontConverter pConverter = nullptr;
+ bool bConvertBulletFont = ( rStream.GetVersion() <= SOFFICE_FILEFORMAT_50 ) && ( rStream.GetVersion() );
+ for(sal_uInt16 i = 0; i < SVX_MAX_NUM; i++)
+ {
+ sal_uInt16 nSetFlag(aFmtsSet[i] ? 2 : 0); // fdo#68648 store that too
+ if(aFmts[i])
+ {
+ rStream.WriteUInt16( 1 | nSetFlag );
+ if(bConvertBulletFont && aFmts[i]->GetBulletFont())
+ {
+ if(!pConverter)
+ pConverter =
+ CreateFontToSubsFontConverter(aFmts[i]->GetBulletFont()->GetFamilyName(),
+ FontToSubsFontFlags::EXPORT);
+ }
+ aFmts[i]->Store(rStream, pConverter);
+ }
+ else
+ rStream.WriteUInt16( 0 | nSetFlag );
+ }
+ //second save of nFeatureFlags for new versions
+ rStream.WriteUInt16( static_cast<sal_uInt16>(nFeatureFlags) );
+}
+
+void SvxNumRule::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxNumRule"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("levelCount"), BAD_CAST(OString::number(nLevelCount).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("continuousNumbering"), BAD_CAST(OString::boolean(bContinuousNumbering).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("numberingType"), BAD_CAST(OString::number(static_cast<int>(eNumberingType)).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("featureFlags"), BAD_CAST(OString::number(static_cast<int>(nFeatureFlags)).getStr()));
+ for(sal_uInt16 i = 0; i < SVX_MAX_NUM; i++)
+ {
+ if(aFmts[i])
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("aFmts"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("i"), BAD_CAST(OString::number(i).getStr()));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", aFmts[i].get());
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+
+SvxNumRule::~SvxNumRule()
+{
+ if(!--nRefCount)
+ {
+ delete pStdNumFmt;
+ pStdNumFmt = nullptr;
+ delete pStdOutlineNumFmt;
+ pStdOutlineNumFmt = nullptr;
+ }
+}
+
+SvxNumRule& SvxNumRule::operator=( const SvxNumRule& rCopy )
+{
+ if (this != &rCopy)
+ {
+ nLevelCount = rCopy.nLevelCount;
+ nFeatureFlags = rCopy.nFeatureFlags;
+ bContinuousNumbering = rCopy.bContinuousNumbering;
+ eNumberingType = rCopy.eNumberingType;
+ for(sal_uInt16 i = 0; i < SVX_MAX_NUM; i++)
+ {
+ if(rCopy.aFmts[i])
+ aFmts[i].reset( new SvxNumberFormat(*rCopy.aFmts[i]) );
+ else
+ aFmts[i].reset();
+ aFmtsSet[i] = rCopy.aFmtsSet[i];
+ }
+ }
+ return *this;
+}
+
+SvxNumRule& SvxNumRule::operator=( SvxNumRule&& rCopy ) noexcept
+{
+ if (this != &rCopy)
+ {
+ nLevelCount = rCopy.nLevelCount;
+ nFeatureFlags = rCopy.nFeatureFlags;
+ bContinuousNumbering = rCopy.bContinuousNumbering;
+ eNumberingType = rCopy.eNumberingType;
+ for(sal_uInt16 i = 0; i < SVX_MAX_NUM; i++)
+ {
+ if(rCopy.aFmts[i])
+ aFmts[i] = std::move(rCopy.aFmts[i]);
+ aFmtsSet[i] = rCopy.aFmtsSet[i];
+ }
+ }
+ return *this;
+}
+
+bool SvxNumRule::operator==( const SvxNumRule& rCopy) const
+{
+ if(nLevelCount != rCopy.nLevelCount ||
+ nFeatureFlags != rCopy.nFeatureFlags ||
+ bContinuousNumbering != rCopy.bContinuousNumbering ||
+ eNumberingType != rCopy.eNumberingType)
+ return false;
+ for(sal_uInt16 i = 0; i < nLevelCount; i++)
+ {
+ if (
+ (aFmtsSet[i] != rCopy.aFmtsSet[i]) ||
+ (!aFmts[i] && rCopy.aFmts[i]) ||
+ (aFmts[i] && !rCopy.aFmts[i]) ||
+ (aFmts[i] && *aFmts[i] != *rCopy.aFmts[i])
+ )
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+const SvxNumberFormat* SvxNumRule::Get(sal_uInt16 nLevel)const
+{
+ DBG_ASSERT(nLevel < SVX_MAX_NUM, "Wrong Level" );
+ if( nLevel < SVX_MAX_NUM )
+ return aFmtsSet[nLevel] ? aFmts[nLevel].get() : nullptr;
+ else
+ return nullptr;
+}
+
+const SvxNumberFormat& SvxNumRule::GetLevel(sal_uInt16 nLevel)const
+{
+ if(!pStdNumFmt)
+ {
+ pStdNumFmt = new SvxNumberFormat(SVX_NUM_ARABIC);
+ pStdOutlineNumFmt = new SvxNumberFormat(SVX_NUM_NUMBER_NONE);
+ }
+
+ DBG_ASSERT(nLevel < SVX_MAX_NUM, "Wrong Level" );
+
+ return ( ( nLevel < SVX_MAX_NUM ) && aFmts[nLevel] ) ?
+ *aFmts[nLevel] : eNumberingType == SvxNumRuleType::NUMBERING ?
+ *pStdNumFmt : *pStdOutlineNumFmt;
+}
+
+void SvxNumRule::SetLevel( sal_uInt16 i, const SvxNumberFormat& rNumFmt, bool bIsValid )
+{
+ DBG_ASSERT(i < SVX_MAX_NUM, "Wrong Level" );
+
+ if( i >= SVX_MAX_NUM )
+ return;
+
+ bool bReplace = !aFmtsSet[i];
+ if (!bReplace)
+ {
+ const SvxNumberFormat *pFmt = Get(i);
+ bReplace = pFmt == nullptr || rNumFmt != *pFmt;
+ }
+
+ if (bReplace)
+ {
+ aFmts[i].reset( new SvxNumberFormat(rNumFmt) );
+ aFmtsSet[i] = bIsValid;
+ }
+}
+
+void SvxNumRule::SetLevel(sal_uInt16 nLevel, const SvxNumberFormat* pFmt)
+{
+ DBG_ASSERT(nLevel < SVX_MAX_NUM, "Wrong Level" );
+
+ if( nLevel < SVX_MAX_NUM )
+ {
+ aFmtsSet[nLevel] = nullptr != pFmt;
+ if(pFmt)
+ SetLevel(nLevel, *pFmt);
+ else
+ {
+ aFmts[nLevel].reset();
+ }
+ }
+}
+
+OUString SvxNumRule::MakeNumString( const SvxNodeNum& rNum ) const
+{
+ OUStringBuffer aStr;
+ if( SVX_NO_NUM > rNum.GetLevel() && !( SVX_NO_NUMLEVEL & rNum.GetLevel() ) )
+ {
+ const SvxNumberFormat& rMyNFmt = GetLevel( rNum.GetLevel() );
+ aStr.append(rMyNFmt.GetPrefix());
+ if( SVX_NUM_NUMBER_NONE != rMyNFmt.GetNumberingType() )
+ {
+ sal_uInt8 i = rNum.GetLevel();
+
+ if( !IsContinuousNumbering() &&
+ 1 < rMyNFmt.GetIncludeUpperLevels() ) // only on own level?
+ {
+ sal_uInt8 n = rMyNFmt.GetIncludeUpperLevels();
+ if( 1 < n )
+ {
+ if( i+1 >= n )
+ i -= n - 1;
+ else
+ i = 0;
+ }
+ }
+
+ for( ; i <= rNum.GetLevel(); ++i )
+ {
+ const SvxNumberFormat& rNFmt = GetLevel( i );
+ if( SVX_NUM_NUMBER_NONE == rNFmt.GetNumberingType() )
+ {
+ continue;
+ }
+
+ bool bDot = true;
+ if( rNum.GetLevelVal()[ i ] )
+ {
+ if(SVX_NUM_BITMAP != rNFmt.GetNumberingType())
+ {
+ const LanguageTag& rLang = Application::GetSettings().GetLanguageTag();
+ aStr.append(rNFmt.GetNumStr( rNum.GetLevelVal()[ i ], rLang.getLocale(), rMyNFmt.GetIsLegal() ));
+ }
+ else
+ bDot = false;
+ }
+ else
+ aStr.append("0"); // all 0-levels are a 0
+ if( i != rNum.GetLevel() && bDot)
+ aStr.append(".");
+ }
+ }
+
+ aStr.append(rMyNFmt.GetSuffix());
+ }
+ return aStr.makeStringAndClear();
+}
+
+// changes linked to embedded bitmaps
+void SvxNumRule::UnLinkGraphics()
+{
+ for(sal_uInt16 i = 0; i < GetLevelCount(); i++)
+ {
+ SvxNumberFormat aFmt(GetLevel(i));
+ const SvxBrushItem* pBrush = aFmt.GetBrush();
+ if(SVX_NUM_BITMAP == aFmt.GetNumberingType())
+ {
+ if(pBrush && !pBrush->GetGraphicLink().isEmpty())
+ {
+ const Graphic* pGraphic = pBrush->GetGraphic();
+ if (pGraphic)
+ {
+ SvxBrushItem aTempItem(*pBrush);
+ aTempItem.SetGraphicLink("");
+ aTempItem.SetGraphic(*pGraphic);
+ sal_Int16 eOrient = aFmt.GetVertOrient();
+ aFmt.SetGraphicBrush( &aTempItem, &aFmt.GetGraphicSize(), &eOrient );
+ }
+ }
+ }
+ else if((SVX_NUM_BITMAP|LINK_TOKEN) == static_cast<int>(aFmt.GetNumberingType()))
+ aFmt.SetNumberingType(SVX_NUM_BITMAP);
+ SetLevel(i, aFmt);
+ }
+}
+
+SvxNumBulletItem::SvxNumBulletItem(SvxNumRule const & rRule) :
+ SfxPoolItem(SID_ATTR_NUMBERING_RULE),
+ maNumRule(rRule)
+{
+}
+
+SvxNumBulletItem::SvxNumBulletItem(SvxNumRule && rRule) :
+ SfxPoolItem(SID_ATTR_NUMBERING_RULE),
+ maNumRule(std::move(rRule))
+{
+}
+
+SvxNumBulletItem::SvxNumBulletItem(SvxNumRule const & rRule, sal_uInt16 _nWhich ) :
+ SfxPoolItem(_nWhich),
+ maNumRule(rRule)
+{
+}
+
+SvxNumBulletItem::SvxNumBulletItem(SvxNumRule && rRule, sal_uInt16 _nWhich ) :
+ SfxPoolItem(_nWhich),
+ maNumRule(std::move(rRule))
+{
+}
+
+SvxNumBulletItem::SvxNumBulletItem(const SvxNumBulletItem& rCopy) :
+ SfxPoolItem(rCopy),
+ maNumRule(rCopy.maNumRule)
+{
+}
+
+SvxNumBulletItem::~SvxNumBulletItem()
+{
+}
+
+bool SvxNumBulletItem::operator==( const SfxPoolItem& rCopy) const
+{
+ return SfxPoolItem::operator==(rCopy) &&
+ maNumRule == static_cast<const SvxNumBulletItem&>(rCopy).maNumRule;
+}
+
+SvxNumBulletItem* SvxNumBulletItem::Clone( SfxItemPool * ) const
+{
+ return new SvxNumBulletItem(*this);
+}
+
+bool SvxNumBulletItem::QueryValue( css::uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) const
+{
+ rVal <<= SvxCreateNumRule( maNumRule );
+ return true;
+}
+
+bool SvxNumBulletItem::PutValue( const css::uno::Any& rVal, sal_uInt8 /*nMemberId*/ )
+{
+ uno::Reference< container::XIndexReplace > xRule;
+ if( rVal >>= xRule )
+ {
+ try
+ {
+ SvxNumRule aNewRule( SvxGetNumRule( xRule ) );
+ if( aNewRule.GetLevelCount() != maNumRule.GetLevelCount() ||
+ aNewRule.GetNumRuleType() != maNumRule.GetNumRuleType() )
+ {
+ aNewRule = SvxConvertNumRule( aNewRule, maNumRule.GetLevelCount(), maNumRule.GetNumRuleType() );
+ }
+ maNumRule = std::move( aNewRule );
+ return true;
+ }
+ catch(const lang::IllegalArgumentException&)
+ {
+ }
+ }
+ return false;
+}
+
+void SvxNumBulletItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxNumBulletItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ maNumRule.dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SvxNumRule SvxConvertNumRule( const SvxNumRule& rRule, sal_uInt16 nLevels, SvxNumRuleType eType )
+{
+ const sal_uInt16 nSrcLevels = rRule.GetLevelCount();
+ SvxNumRule aNewRule(rRule.GetFeatureFlags(), nLevels, rRule.IsContinuousNumbering(), eType );
+
+ for( sal_uInt16 nLevel = 0; (nLevel < nLevels) && (nLevel < nSrcLevels); nLevel++ )
+ aNewRule.SetLevel( nLevel, rRule.GetLevel( nLevel ) );
+
+ return aNewRule;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/items/optitems.cxx b/editeng/source/items/optitems.cxx
new file mode 100644
index 0000000000..254da79d91
--- /dev/null
+++ b/editeng/source/items/optitems.cxx
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <editeng/optitems.hxx>
+#include <editeng/eerdll.hxx>
+#include <editeng/editrids.hrc>
+
+
+// class SfxHyphenRegionItem -----------------------------------------------
+
+SfxHyphenRegionItem::SfxHyphenRegionItem( const sal_uInt16 nId ) :
+
+ SfxPoolItem( nId )
+{
+ nMinLead = nMinTrail = 0;
+}
+
+bool SfxHyphenRegionItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ return ( ( static_cast<const SfxHyphenRegionItem&>( rAttr ).nMinLead == nMinLead ) &&
+ ( static_cast<const SfxHyphenRegionItem&>( rAttr ).nMinTrail == nMinTrail ) );
+}
+
+SfxHyphenRegionItem* SfxHyphenRegionItem::Clone( SfxItemPool* ) const
+{
+ return new SfxHyphenRegionItem( *this );
+}
+
+bool SfxHyphenRegionItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit ,
+ MapUnit ,
+ OUString& rText,
+ const IntlWrapper&
+) const
+{
+ rText += EditResId(RID_SVXITEMS_HYPHEN_MINLEAD).replaceAll("%1", OUString::number(nMinLead)) +
+ "," +
+ EditResId(RID_SVXITEMS_HYPHEN_MINTRAIL).replaceAll("%1", OUString::number(nMinTrail));
+ return true;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/items/paperinf.cxx b/editeng/source/items/paperinf.cxx
new file mode 100644
index 0000000000..86401e63f3
--- /dev/null
+++ b/editeng/source/items/paperinf.cxx
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/print.hxx>
+#include <editeng/paperinf.hxx>
+
+/*--------------------------------------------------------------------
+ Description: Is the printer valid
+ --------------------------------------------------------------------*/
+
+static bool IsValidPrinter( const Printer* pPtr )
+{
+ return !pPtr->GetName().isEmpty();
+}
+
+
+Size SvxPaperInfo::GetPaperSize( Paper ePaper, MapUnit eUnit )
+{
+ PaperInfo aInfo(ePaper);
+ Size aRet(aInfo.getWidth(), aInfo.getHeight()); // in 100thMM
+ return eUnit == MapUnit::Map100thMM
+ ? aRet
+ : OutputDevice::LogicToLogic(aRet, MapMode(MapUnit::Map100thMM), MapMode(eUnit));
+}
+
+/*------------------------------------------------------------------------
+ Description: Return the paper size of the printer, aligned to our
+ own sizes. If no Printer is set in the system, A4 portrait
+ will be delivered as the default paper size.
+------------------------------------------------------------------------*/
+
+//Is this method may be confused about the units it returns ?
+//Always returns TWIPS for known paper sizes or on failure.
+//But in the case of PAPER_USER paper and with a Printer with a mapmode set
+//will return in those printer units ?
+Size SvxPaperInfo::GetPaperSize( const Printer* pPrinter )
+{
+ if ( !IsValidPrinter(pPrinter) )
+ return GetPaperSize( PAPER_A4 );
+ const Paper ePaper = pPrinter->GetPaper();
+
+ if ( ePaper == PAPER_USER )
+ {
+ // Orientation not take into account, as the right size has
+ // been already set by SV
+ Size aPaperSize = pPrinter->GetPaperSize();
+ const Size aInvalidSize;
+
+ if ( aPaperSize == aInvalidSize )
+ return GetPaperSize(PAPER_A4);
+ const MapMode& aMap1 = pPrinter->GetMapMode();
+ MapMode aMap2;
+
+ if ( aMap1 == aMap2 )
+ aPaperSize =
+ pPrinter->PixelToLogic( aPaperSize, MapMode( MapUnit::MapTwip ) );
+ return aPaperSize;
+ }
+
+ const Orientation eOrient = pPrinter->GetOrientation();
+ Size aSize( GetPaperSize( ePaper ) );
+ // for Landscape exchange the pages, has already been done by SV
+ if ( eOrient == Orientation::Landscape )
+ Swap( aSize );
+ return aSize;
+}
+
+
+Paper SvxPaperInfo::GetSvxPaper( const Size &rSize, MapUnit eUnit )
+{
+ Size aSize(eUnit == MapUnit::Map100thMM ? rSize : OutputDevice::LogicToLogic(rSize, MapMode(eUnit), MapMode(MapUnit::Map100thMM)));
+ PaperInfo aInfo(aSize.Width(), aSize.Height());
+ aInfo.doSloppyFit();
+ return aInfo.getPaper();
+}
+
+
+tools::Long SvxPaperInfo::GetSloppyPaperDimension( tools::Long nSize )
+{
+ nSize = o3tl::convert(nSize, o3tl::Length::twip, o3tl::Length::mm100);
+ nSize = PaperInfo::sloppyFitPageDimension(nSize);
+ return o3tl::convert(nSize, o3tl::Length::mm100, o3tl::Length::twip);
+}
+
+
+Size SvxPaperInfo::GetDefaultPaperSize( MapUnit eUnit )
+{
+ PaperInfo aInfo(PaperInfo::getSystemDefaultPaper());
+ Size aRet(aInfo.getWidth(), aInfo.getHeight());
+ return eUnit == MapUnit::Map100thMM
+ ? aRet
+ : OutputDevice::LogicToLogic(aRet, MapMode(MapUnit::Map100thMM), MapMode(eUnit));
+}
+
+/*------------------------------------------------------------------------
+ Description: String representation for the SV-defines of paper size
+------------------------------------------------------------------------*/
+
+OUString SvxPaperInfo::GetName( Paper ePaper )
+{
+ return Printer::GetPaperName( ePaper );
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/items/paraitem.cxx b/editeng/source/items/paraitem.cxx
new file mode 100644
index 0000000000..e10c323bcd
--- /dev/null
+++ b/editeng/source/items/paraitem.cxx
@@ -0,0 +1,1317 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/style/TabStop.hpp>
+#include <com/sun/star/style/LineSpacing.hpp>
+#include <com/sun/star/style/LineSpacingMode.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <libxml/xmlwriter.h>
+#include <comphelper/extract.hxx>
+#include <osl/diagnose.h>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/syslocale.hxx>
+#include <tools/mapunit.hxx>
+#include <tools/UnitConversion.hxx>
+#include <svl/itempool.hxx>
+#include <svl/memberid.h>
+#include <editeng/editrids.hrc>
+#include <editeng/lspcitem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/orphitem.hxx>
+#include <editeng/widwitem.hxx>
+#include <editeng/tstpitem.hxx>
+#include <editeng/pmdlitem.hxx>
+#include <editeng/spltitem.hxx>
+#include <editeng/hyphenzoneitem.hxx>
+#include <editeng/scriptspaceitem.hxx>
+#include <editeng/hngpnctitem.hxx>
+#include <editeng/forbiddenruleitem.hxx>
+#include <editeng/paravertalignitem.hxx>
+#include <editeng/pgrditem.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <editeng/memberids.h>
+#include <editeng/itemtype.hxx>
+#include <editeng/eerdll.hxx>
+
+using namespace ::com::sun::star;
+
+
+SfxPoolItem* SvxLineSpacingItem::CreateDefault() { return new SvxLineSpacingItem(LINE_SPACE_DEFAULT_HEIGHT, 0);}
+SfxPoolItem* SvxAdjustItem::CreateDefault() { return new SvxAdjustItem(SvxAdjust::Left, 0);}
+SfxPoolItem* SvxWidowsItem::CreateDefault() { return new SvxWidowsItem(0, 0);}
+SfxPoolItem* SvxOrphansItem::CreateDefault() { return new SvxOrphansItem(0, 0);}
+SfxPoolItem* SvxHyphenZoneItem::CreateDefault() { return new SvxHyphenZoneItem(false, 0);}
+SfxPoolItem* SvxTabStopItem::CreateDefault() { return new SvxTabStopItem(0);}
+SfxPoolItem* SvxFormatSplitItem::CreateDefault() { return new SvxFormatSplitItem(false, 0);}
+SfxPoolItem* SvxPageModelItem::CreateDefault() { return new SvxPageModelItem(TypedWhichId<SvxPageModelItem>(0));}
+SfxPoolItem* SvxParaVertAlignItem::CreateDefault() { return new SvxParaVertAlignItem(Align::Automatic, TypedWhichId<SvxParaVertAlignItem>(0));}
+
+namespace {
+
+enum class SvxSpecialLineSpace
+{
+ User,
+ OneLine,
+ OnePointFiveLines,
+ TwoLines,
+ End
+};
+
+}
+
+SvxLineSpacingItem::SvxLineSpacingItem( sal_uInt16 nHeight, const sal_uInt16 nId )
+ : SfxEnumItemInterface( nId )
+{
+ nPropLineSpace = 100;
+ nInterLineSpace = 0;
+ nLineHeight = nHeight;
+ eLineSpaceRule = SvxLineSpaceRule::Auto;
+ eInterLineSpaceRule = SvxInterLineSpaceRule::Off;
+}
+
+
+bool SvxLineSpacingItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ const SvxLineSpacingItem& rLineSpace = static_cast<const SvxLineSpacingItem&>(rAttr);
+ return
+ // Same Linespacing Rule?
+ (eLineSpaceRule == rLineSpace.eLineSpaceRule)
+ // For maximum and minimum Linespacing be the size must coincide.
+ && (eLineSpaceRule == SvxLineSpaceRule::Auto ||
+ nLineHeight == rLineSpace.nLineHeight)
+ // Same Linespacing Rule?
+ && ( eInterLineSpaceRule == rLineSpace.eInterLineSpaceRule )
+ // Either set proportional or additive.
+ && (( eInterLineSpaceRule == SvxInterLineSpaceRule::Off)
+ || (eInterLineSpaceRule == SvxInterLineSpaceRule::Prop
+ && nPropLineSpace == rLineSpace.nPropLineSpace)
+ || (eInterLineSpaceRule == SvxInterLineSpaceRule::Fix
+ && (nInterLineSpace == rLineSpace.nInterLineSpace)));
+}
+
+/* Who does still know why the LineSpacingItem is so complicated?
+ We can not use it for UNO since there are only two values:
+ - a sal_uInt16 for the mode
+ - a sal_uInt32 for all values (distance, height, rel. detail)
+*/
+bool SvxLineSpacingItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ bool bConvert = 0!=(nMemberId&CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+
+ style::LineSpacing aLSp;
+ switch( eLineSpaceRule )
+ {
+ case SvxLineSpaceRule::Auto:
+ if(eInterLineSpaceRule == SvxInterLineSpaceRule::Fix)
+ {
+ aLSp.Mode = style::LineSpacingMode::LEADING;
+ aLSp.Height = ( bConvert ? static_cast<short>(convertTwipToMm100(nInterLineSpace)) : nInterLineSpace);
+ }
+ else if(eInterLineSpaceRule == SvxInterLineSpaceRule::Off)
+ {
+ aLSp.Mode = style::LineSpacingMode::PROP;
+ aLSp.Height = 100;
+ }
+ else
+ {
+ aLSp.Mode = style::LineSpacingMode::PROP;
+ aLSp.Height = nPropLineSpace;
+ }
+ break;
+ case SvxLineSpaceRule::Fix :
+ case SvxLineSpaceRule::Min :
+ aLSp.Mode = eLineSpaceRule == SvxLineSpaceRule::Fix ? style::LineSpacingMode::FIX : style::LineSpacingMode::MINIMUM;
+ aLSp.Height = ( bConvert ? static_cast<short>(convertTwipToMm100(nLineHeight)) : nLineHeight );
+ break;
+ default:
+ ;//prevent warning about SvxLineSpaceRule::End
+ }
+
+ switch ( nMemberId )
+ {
+ case 0 : rVal <<= aLSp; break;
+ case MID_LINESPACE : rVal <<= aLSp.Mode; break;
+ case MID_HEIGHT : rVal <<= aLSp.Height; break;
+ default: OSL_FAIL("Wrong MemberId!"); break;
+ }
+
+ return true;
+}
+
+bool SvxLineSpacingItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ bool bConvert = 0!=(nMemberId&CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+
+ // fill with current data
+ style::LineSpacing aLSp;
+ uno::Any aAny;
+ bool bRet = QueryValue( aAny, bConvert ? CONVERT_TWIPS : 0 ) && ( aAny >>= aLSp );
+
+ // get new data
+ switch ( nMemberId )
+ {
+ case 0 : bRet = (rVal >>= aLSp); break;
+ case MID_LINESPACE : bRet = (rVal >>= aLSp.Mode); break;
+ case MID_HEIGHT : bRet = (rVal >>= aLSp.Height); break;
+ default: OSL_FAIL("Wrong MemberId!"); break;
+ }
+
+ if( bRet )
+ {
+ nLineHeight = aLSp.Height;
+ switch( aLSp.Mode )
+ {
+ case style::LineSpacingMode::LEADING:
+ {
+ eInterLineSpaceRule = SvxInterLineSpaceRule::Fix;
+ eLineSpaceRule = SvxLineSpaceRule::Auto;
+ nInterLineSpace = aLSp.Height;
+ if(bConvert)
+ nInterLineSpace = o3tl::toTwips(nInterLineSpace, o3tl::Length::mm100);
+
+ }
+ break;
+ case style::LineSpacingMode::PROP:
+ {
+ eLineSpaceRule = SvxLineSpaceRule::Auto;
+ nPropLineSpace = aLSp.Height;
+ if(100 == aLSp.Height)
+ eInterLineSpaceRule = SvxInterLineSpaceRule::Off;
+ else
+ eInterLineSpaceRule = SvxInterLineSpaceRule::Prop;
+ }
+ break;
+ case style::LineSpacingMode::FIX:
+ case style::LineSpacingMode::MINIMUM:
+ {
+ eInterLineSpaceRule = SvxInterLineSpaceRule::Off;
+ eLineSpaceRule = aLSp.Mode == style::LineSpacingMode::FIX ? SvxLineSpaceRule::Fix : SvxLineSpaceRule::Min;
+ nLineHeight = aLSp.Height;
+ if(bConvert)
+ nLineHeight = o3tl::toTwips(nLineHeight, o3tl::Length::mm100);
+ }
+ break;
+ }
+ }
+
+ return bRet;
+}
+
+SvxLineSpacingItem* SvxLineSpacingItem::Clone( SfxItemPool * ) const
+{
+ return new SvxLineSpacingItem( *this );
+}
+
+bool SvxLineSpacingItem::GetPresentation
+(
+ SfxItemPresentation ePres,
+ MapUnit eCoreUnit,
+ MapUnit ePresUnit,
+ OUString& rText, const IntlWrapper& rIntl
+) const
+{
+ switch ( ePres )
+ {
+ case SfxItemPresentation::Nameless:
+ case SfxItemPresentation::Complete:
+ {
+ switch( GetLineSpaceRule() )
+ {
+ case SvxLineSpaceRule::Auto:
+ {
+ SvxInterLineSpaceRule eInter = GetInterLineSpaceRule();
+
+ switch( eInter )
+ {
+ // Default single line spacing
+ case SvxInterLineSpaceRule::Off:
+ rText = EditResId(RID_SVXITEMS_LINESPACING_SINGLE);
+ break;
+
+ // Default single line spacing
+ case SvxInterLineSpaceRule::Prop:
+ if ( 100 == GetPropLineSpace() )
+ {
+ rText = EditResId(RID_SVXITEMS_LINESPACING_SINGLE);
+ break;
+ }
+ // 1.15 line spacing
+ if ( 115 == GetPropLineSpace() )
+ {
+ rText = EditResId(RID_SVXITEMS_LINESPACING_115);
+ break;
+ }
+ // 1.5 line spacing
+ if ( 150 == GetPropLineSpace() )
+ {
+ rText = EditResId(RID_SVXITEMS_LINESPACING_15);
+ break;
+ }
+ // double line spacing
+ if ( 200 == GetPropLineSpace() )
+ {
+ rText = EditResId(RID_SVXITEMS_LINESPACING_DOUBLE);
+ break;
+ }
+ // the set per cent value
+ rText = EditResId(RID_SVXITEMS_LINESPACING_PROPORTIONAL) + " " + OUString::number(GetPropLineSpace()) + "%";
+ break;
+
+ case SvxInterLineSpaceRule::Fix:
+ rText = EditResId(RID_SVXITEMS_LINESPACING_LEADING) +
+ " " + GetMetricText(GetInterLineSpace(), eCoreUnit, ePresUnit, &rIntl) +
+ " " + EditResId(GetMetricId(ePresUnit));
+ break;
+ default: ;//prevent warning
+ }
+ }
+ break;
+ case SvxLineSpaceRule::Fix:
+ rText = EditResId(RID_SVXITEMS_LINESPACING_FIXED) +
+ " " + GetMetricText(GetLineHeight(), eCoreUnit, ePresUnit, &rIntl) +
+ " " + EditResId(GetMetricId(ePresUnit));
+ break;
+
+ case SvxLineSpaceRule::Min:
+ rText = EditResId(RID_SVXITEMS_LINESPACING_MIN) +
+ " " + GetMetricText(GetLineHeight(), eCoreUnit, ePresUnit, &rIntl) +
+ " " + EditResId(GetMetricId(ePresUnit));
+ break;
+ default: ;//prevent warning
+ }
+ }
+ }
+ return true;
+}
+
+sal_uInt16 SvxLineSpacingItem::GetValueCount() const
+{
+ return sal_uInt16(SvxSpecialLineSpace::End); // SvxSpecialLineSpace::TwoLines + 1
+}
+
+
+sal_uInt16 SvxLineSpacingItem::GetEnumValue() const
+{
+ SvxSpecialLineSpace nVal;
+ switch ( nPropLineSpace )
+ {
+ case 100: nVal = SvxSpecialLineSpace::OneLine; break;
+ case 150: nVal = SvxSpecialLineSpace::OnePointFiveLines; break;
+ case 200: nVal = SvxSpecialLineSpace::TwoLines; break;
+ default: nVal = SvxSpecialLineSpace::User; break;
+ }
+ return static_cast<sal_uInt16>(nVal);
+}
+
+
+void SvxLineSpacingItem::SetEnumValue( sal_uInt16 nVal )
+{
+ switch ( static_cast<SvxSpecialLineSpace>(nVal) )
+ {
+ case SvxSpecialLineSpace::OneLine: nPropLineSpace = 100; break;
+ case SvxSpecialLineSpace::OnePointFiveLines: nPropLineSpace = 150; break;
+ case SvxSpecialLineSpace::TwoLines: nPropLineSpace = 200; break;
+ default: break;
+ }
+}
+
+// class SvxAdjustItem ---------------------------------------------------
+
+SvxAdjustItem::SvxAdjustItem(const SvxAdjust eAdjst, const sal_uInt16 nId )
+ : SfxEnumItemInterface( nId ),
+ bOneBlock( false ), bLastCenter( false ), bLastBlock( false )
+{
+ SetAdjust( eAdjst );
+}
+
+
+bool SvxAdjustItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ const SvxAdjustItem& rItem = static_cast<const SvxAdjustItem&>(rAttr);
+ return GetAdjust() == rItem.GetAdjust() &&
+ bOneBlock == rItem.bOneBlock &&
+ bLastCenter == rItem.bLastCenter &&
+ bLastBlock == rItem.bLastBlock;
+}
+
+bool SvxAdjustItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch( nMemberId )
+ {
+ case MID_PARA_ADJUST : rVal <<= static_cast<sal_Int16>(GetAdjust()); break;
+ case MID_LAST_LINE_ADJUST : rVal <<= static_cast<sal_Int16>(GetLastBlock()); break;
+ case MID_EXPAND_SINGLE :
+ {
+ rVal <<= bOneBlock;
+ break;
+ }
+ default: ;//prevent warning
+ }
+ return true;
+}
+
+bool SvxAdjustItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch( nMemberId )
+ {
+ case MID_PARA_ADJUST :
+ case MID_LAST_LINE_ADJUST :
+ {
+ sal_Int32 eVal = - 1;
+ ::cppu::enum2int(eVal,rVal);
+ if(eVal >= 0 && eVal <= 4)
+ {
+ SvxAdjust eAdjust = static_cast<SvxAdjust>(eVal);
+ if(MID_LAST_LINE_ADJUST == nMemberId &&
+ eAdjust != SvxAdjust::Left &&
+ eAdjust != SvxAdjust::Block &&
+ eAdjust != SvxAdjust::Center)
+ return false;
+ nMemberId == MID_PARA_ADJUST ? SetAdjust(eAdjust) : SetLastBlock(eAdjust);
+ }
+ }
+ break;
+ case MID_EXPAND_SINGLE :
+ bOneBlock = Any2Bool(rVal);
+ break;
+ }
+ return true;
+}
+
+SvxAdjustItem* SvxAdjustItem::Clone( SfxItemPool * ) const
+{
+ return new SvxAdjustItem( *this );
+}
+
+bool SvxAdjustItem::GetPresentation
+(
+ SfxItemPresentation ePres,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ switch ( ePres )
+ {
+ case SfxItemPresentation::Nameless:
+ case SfxItemPresentation::Complete:
+ rText = GetValueTextByPos( static_cast<sal_uInt16>(GetAdjust()) );
+ return true;
+ default: ;//prevent warning
+ }
+ return false;
+}
+
+
+sal_uInt16 SvxAdjustItem::GetValueCount() const
+{
+ return sal_uInt16(SvxAdjust::End); // SvxAdjust::BlockLine + 1
+}
+
+OUString SvxAdjustItem::GetValueTextByPos( sal_uInt16 nPos )
+{
+ static TranslateId RID_SVXITEMS_ADJUST[] =
+ {
+ RID_SVXITEMS_ADJUST_LEFT,
+ RID_SVXITEMS_ADJUST_RIGHT,
+ RID_SVXITEMS_ADJUST_BLOCK,
+ RID_SVXITEMS_ADJUST_CENTER,
+ RID_SVXITEMS_ADJUST_BLOCKLINE
+ };
+ static_assert(SAL_N_ELEMENTS(RID_SVXITEMS_ADJUST) - 1 == size_t(SvxAdjust::BlockLine), "unexpected size");
+ assert(nPos <= sal_uInt16(SvxAdjust::BlockLine) && "enum overflow!");
+ return EditResId(RID_SVXITEMS_ADJUST[nPos]);
+}
+
+sal_uInt16 SvxAdjustItem::GetEnumValue() const
+{
+ return static_cast<sal_uInt16>(GetAdjust());
+}
+
+
+void SvxAdjustItem::SetEnumValue( sal_uInt16 nVal )
+{
+ SetAdjust( static_cast<SvxAdjust>(nVal) );
+}
+
+
+// class SvxWidowsItem ---------------------------------------------------
+
+SvxWidowsItem::SvxWidowsItem(const sal_uInt8 nL, const sal_uInt16 nId ) :
+ SfxByteItem( nId, nL )
+{
+}
+
+SvxWidowsItem* SvxWidowsItem::Clone( SfxItemPool * ) const
+{
+ return new SvxWidowsItem( *this );
+}
+
+bool SvxWidowsItem::GetPresentation
+(
+ SfxItemPresentation ePres,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ switch ( ePres )
+ {
+ case SfxItemPresentation::Nameless:
+ {
+ rText = EditResId(RID_SVXITEMS_LINES);
+ break;
+ }
+
+ case SfxItemPresentation::Complete:
+ {
+ rText = EditResId(RID_SVXITEMS_WIDOWS_COMPLETE) + " " + EditResId(RID_SVXITEMS_LINES);
+ break;
+ }
+
+ default:
+ {
+ SAL_WARN( "editeng.items", "SvxWidowsItem::GetPresentation(): unknown SfxItemPresentation" );
+ }
+ }
+
+ rText = rText.replaceFirst( "%1", OUString::number( GetValue() ) );
+ return true;
+}
+
+// class SvxOrphansItem --------------------------------------------------
+
+SvxOrphansItem::SvxOrphansItem(const sal_uInt8 nL, const sal_uInt16 nId ) :
+ SfxByteItem( nId, nL )
+{
+}
+
+SvxOrphansItem* SvxOrphansItem::Clone( SfxItemPool * ) const
+{
+ return new SvxOrphansItem( *this );
+}
+
+bool SvxOrphansItem::GetPresentation
+(
+ SfxItemPresentation ePres,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ switch ( ePres )
+ {
+ case SfxItemPresentation::Nameless:
+ {
+ rText = EditResId(RID_SVXITEMS_LINES);
+ break;
+ }
+
+ case SfxItemPresentation::Complete:
+ {
+ rText = EditResId(RID_SVXITEMS_ORPHANS_COMPLETE) + " " + EditResId(RID_SVXITEMS_LINES);
+ break;
+ }
+
+ default:
+ {
+ SAL_WARN( "editeng.items", "SvxOrphansItem::GetPresentation(): unknown SfxItemPresentation" );
+ }
+ }
+
+ rText = rText.replaceFirst( "%1", OUString::number( GetValue() ) );
+ return true;
+}
+
+// class SvxHyphenZoneItem -----------------------------------------------
+
+SvxHyphenZoneItem::SvxHyphenZoneItem( const bool bHyph, const sal_uInt16 nId ) :
+ SfxPoolItem( nId ),
+ bHyphen(bHyph),
+ bPageEnd(true),
+ bNoCapsHyphenation(false),
+ bNoLastWordHyphenation(false),
+ nMinLead(0),
+ nMinTrail(0),
+ nMaxHyphens(255),
+ nMinWordLength(0),
+ nTextHyphenZone(0)
+{
+}
+
+
+bool SvxHyphenZoneItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch(nMemberId)
+ {
+ case MID_IS_HYPHEN:
+ rVal <<= bHyphen;
+ break;
+ case MID_HYPHEN_MIN_LEAD:
+ rVal <<= static_cast<sal_Int16>(nMinLead);
+ break;
+ case MID_HYPHEN_MIN_TRAIL:
+ rVal <<= static_cast<sal_Int16>(nMinTrail);
+ break;
+ case MID_HYPHEN_MAX_HYPHENS:
+ rVal <<= static_cast<sal_Int16>(nMaxHyphens);
+ break;
+ case MID_HYPHEN_NO_CAPS:
+ rVal <<= bNoCapsHyphenation;
+ break;
+ case MID_HYPHEN_NO_LAST_WORD:
+ rVal <<= bNoLastWordHyphenation;
+ break;
+ case MID_HYPHEN_MIN_WORD_LENGTH:
+ rVal <<= static_cast<sal_Int16>(nMinWordLength);
+ break;
+ case MID_HYPHEN_ZONE:
+ rVal <<= static_cast<sal_Int16>(nTextHyphenZone);
+ break;
+ }
+ return true;
+}
+
+bool SvxHyphenZoneItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ sal_Int16 nNewVal = 0;
+
+ if( nMemberId != MID_IS_HYPHEN && nMemberId != MID_HYPHEN_NO_CAPS &&
+ nMemberId != MID_HYPHEN_NO_LAST_WORD )
+ {
+ if(!(rVal >>= nNewVal))
+ return false;
+ }
+
+ switch(nMemberId)
+ {
+ case MID_IS_HYPHEN:
+ bHyphen = Any2Bool(rVal);
+ break;
+ case MID_HYPHEN_MIN_LEAD:
+ nMinLead = static_cast<sal_uInt8>(nNewVal);
+ break;
+ case MID_HYPHEN_MIN_TRAIL:
+ nMinTrail = static_cast<sal_uInt8>(nNewVal);
+ break;
+ case MID_HYPHEN_MAX_HYPHENS:
+ nMaxHyphens = static_cast<sal_uInt8>(nNewVal);
+ break;
+ case MID_HYPHEN_NO_CAPS:
+ bNoCapsHyphenation = Any2Bool(rVal);
+ break;
+ case MID_HYPHEN_NO_LAST_WORD:
+ bNoLastWordHyphenation = Any2Bool(rVal);
+ break;
+ case MID_HYPHEN_MIN_WORD_LENGTH:
+ nMinWordLength = static_cast<sal_uInt8>(nNewVal);
+ break;
+ case MID_HYPHEN_ZONE:
+ nTextHyphenZone = nNewVal;
+ break;
+ }
+ return true;
+}
+
+
+bool SvxHyphenZoneItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ const SvxHyphenZoneItem& rItem = static_cast<const SvxHyphenZoneItem&>(rAttr);
+ return ( rItem.bHyphen == bHyphen
+ && rItem.bNoCapsHyphenation == bNoCapsHyphenation
+ && rItem.bNoLastWordHyphenation == bNoLastWordHyphenation
+ && rItem.bPageEnd == bPageEnd
+ && rItem.nMinLead == nMinLead
+ && rItem.nMinTrail == nMinTrail
+ && rItem.nMaxHyphens == nMaxHyphens
+ && rItem.nMinWordLength == nMinWordLength
+ && rItem.nTextHyphenZone == nTextHyphenZone );
+}
+
+SvxHyphenZoneItem* SvxHyphenZoneItem::Clone( SfxItemPool * ) const
+{
+ return new SvxHyphenZoneItem( *this );
+}
+
+bool SvxHyphenZoneItem::GetPresentation
+(
+ SfxItemPresentation ePres,
+ MapUnit eCoreUnit,
+ MapUnit ePresUnit,
+ OUString& rText, const IntlWrapper& rIntl
+) const
+{
+ OUString cpDelimTmp(cpDelim);
+ switch ( ePres )
+ {
+ case SfxItemPresentation::Nameless:
+ {
+ TranslateId pId = RID_SVXITEMS_HYPHEN_FALSE;
+
+ if ( bHyphen )
+ pId = RID_SVXITEMS_HYPHEN_TRUE;
+ rText = EditResId(pId) + cpDelimTmp;
+ pId = RID_SVXITEMS_PAGE_END_FALSE;
+
+ if ( bPageEnd )
+ pId = RID_SVXITEMS_PAGE_END_TRUE;
+ rText += EditResId(pId) + cpDelimTmp +
+ OUString::number( nMinLead ) + cpDelimTmp +
+ OUString::number( nMinTrail ) + cpDelimTmp +
+ OUString::number( nMaxHyphens ) + cpDelimTmp +
+ OUString::number( nMinWordLength ) + cpDelimTmp +
+ GetMetricText( nTextHyphenZone, eCoreUnit, ePresUnit, &rIntl ) +
+ " " + EditResId(GetMetricId(ePresUnit));
+
+ if ( bNoCapsHyphenation )
+ rText += cpDelimTmp + EditResId(RID_SVXITEMS_HYPHEN_NO_CAPS_TRUE);
+
+ if ( bNoLastWordHyphenation )
+ rText += cpDelimTmp + EditResId(RID_SVXITEMS_HYPHEN_LAST_WORD_TRUE);
+
+ return true;
+ }
+ case SfxItemPresentation::Complete:
+ {
+ TranslateId pId = RID_SVXITEMS_HYPHEN_FALSE;
+
+ if ( bHyphen )
+ pId = RID_SVXITEMS_HYPHEN_TRUE;
+ rText = EditResId(pId) + cpDelimTmp;
+ pId = RID_SVXITEMS_PAGE_END_FALSE;
+
+ if ( bPageEnd )
+ pId = RID_SVXITEMS_PAGE_END_TRUE;
+ rText += EditResId(pId) +
+ cpDelimTmp +
+ EditResId(RID_SVXITEMS_HYPHEN_MINLEAD).replaceAll("%1", OUString::number(nMinLead)) +
+ cpDelimTmp +
+ EditResId(RID_SVXITEMS_HYPHEN_MINTRAIL).replaceAll("%1", OUString::number(nMinTrail)) +
+ cpDelimTmp +
+ EditResId(RID_SVXITEMS_HYPHEN_MAX).replaceAll("%1", OUString::number(nMaxHyphens)) +
+ cpDelimTmp +
+ EditResId(RID_SVXITEMS_HYPHEN_MINWORDLEN).replaceAll("%1", OUString::number(nMinWordLength));
+
+ if ( nTextHyphenZone > 0 )
+ {
+ rText += cpDelimTmp + EditResId(RID_SVXITEMS_HYPHEN_ZONE) +
+ GetMetricText( nTextHyphenZone, eCoreUnit, ePresUnit, &rIntl ) +
+ " " + EditResId(GetMetricId(ePresUnit));
+ }
+
+ if ( bNoCapsHyphenation )
+ rText += cpDelimTmp + EditResId(RID_SVXITEMS_HYPHEN_NO_CAPS_TRUE);
+
+ if ( bNoLastWordHyphenation )
+ rText += cpDelimTmp + EditResId(RID_SVXITEMS_HYPHEN_LAST_WORD_TRUE);
+
+ return true;
+ }
+ default: ;//prevent warning
+ }
+ return false;
+}
+
+
+// class SvxTabStop ------------------------------------------------------
+
+SvxTabStop::SvxTabStop()
+{
+ nTabPos = 0;
+ eAdjustment = SvxTabAdjust::Left;
+ m_cDecimal = cDfltDecimalChar;
+ cFill = cDfltFillChar;
+}
+
+
+SvxTabStop::SvxTabStop( const sal_Int32 nPos, const SvxTabAdjust eAdjst,
+ const sal_Unicode cDec, const sal_Unicode cFil )
+{
+ nTabPos = nPos;
+ eAdjustment = eAdjst;
+ m_cDecimal = cDec;
+ cFill = cFil;
+}
+
+void SvxTabStop::fillDecimal() const
+{
+ if ( cDfltDecimalChar == m_cDecimal )
+ m_cDecimal = SvtSysLocale().GetLocaleData().getNumDecimalSep()[0];
+}
+
+void SvxTabStop::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxTabStop"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nTabPos"),
+ BAD_CAST(OString::number(nTabPos).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eAdjustment"),
+ BAD_CAST(OString::number(static_cast<int>(eAdjustment)).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// class SvxTabStopItem --------------------------------------------------
+
+SvxTabStopItem::SvxTabStopItem( sal_uInt16 _nWhich ) :
+ SfxPoolItem( _nWhich )
+{
+ const sal_uInt16 nTabs = SVX_TAB_DEFCOUNT, nDist = SVX_TAB_DEFDIST;
+ const SvxTabAdjust eAdjst= SvxTabAdjust::Default;
+
+ for (sal_uInt16 i = 0; i < nTabs; ++i)
+ {
+ SvxTabStop aTab( (i + 1) * nDist, eAdjst );
+ maTabStops.insert( aTab );
+ }
+}
+
+
+SvxTabStopItem::SvxTabStopItem( const sal_uInt16 nTabs,
+ const sal_uInt16 nDist,
+ const SvxTabAdjust eAdjst,
+ sal_uInt16 _nWhich ) :
+ SfxPoolItem( _nWhich )
+{
+ for ( sal_uInt16 i = 0; i < nTabs; ++i )
+ {
+ SvxTabStop aTab( (i + 1) * nDist, eAdjst );
+ maTabStops.insert( aTab );
+ }
+}
+
+
+sal_uInt16 SvxTabStopItem::GetPos( const SvxTabStop& rTab ) const
+{
+ SvxTabStopArr::const_iterator it = maTabStops.find( rTab );
+ return it != maTabStops.end() ? it - maTabStops.begin() : SVX_TAB_NOTFOUND;
+}
+
+
+sal_uInt16 SvxTabStopItem::GetPos( const sal_Int32 nPos ) const
+{
+ SvxTabStopArr::const_iterator it = maTabStops.find( SvxTabStop( nPos ) );
+ return it != maTabStops.end() ? it - maTabStops.begin() : SVX_TAB_NOTFOUND;
+}
+
+void SvxTabStopItem::SetDefaultDistance(sal_Int32 nDefaultDistance)
+{
+ mnDefaultDistance = nDefaultDistance;
+}
+
+sal_Int32 SvxTabStopItem::GetDefaultDistance() const
+{
+ return mnDefaultDistance;
+}
+
+bool SvxTabStopItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ bool bConvert = 0!=(nMemberId&CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+ switch ( nMemberId )
+ {
+ case MID_TABSTOPS:
+ {
+ sal_uInt16 nCount = Count();
+ uno::Sequence< style::TabStop> aSeq(nCount);
+ style::TabStop* pArr = aSeq.getArray();
+ for(sal_uInt16 i = 0; i < nCount; i++)
+ {
+ const SvxTabStop& rTab = (*this)[i];
+ pArr[i].Position = bConvert ? convertTwipToMm100(rTab.GetTabPos()) : rTab.GetTabPos();
+ switch(rTab.GetAdjustment())
+ {
+ case SvxTabAdjust::Left : pArr[i].Alignment = style::TabAlign_LEFT; break;
+ case SvxTabAdjust::Right : pArr[i].Alignment = style::TabAlign_RIGHT; break;
+ case SvxTabAdjust::Decimal: pArr[i].Alignment = style::TabAlign_DECIMAL; break;
+ case SvxTabAdjust::Center : pArr[i].Alignment = style::TabAlign_CENTER; break;
+ default: //SvxTabAdjust::Default
+ pArr[i].Alignment = style::TabAlign_DEFAULT;
+
+ }
+ pArr[i].DecimalChar = rTab.GetDecimal();
+ pArr[i].FillChar = rTab.GetFill();
+ }
+ rVal <<= aSeq;
+ break;
+ }
+ case MID_STD_TAB:
+ {
+ const SvxTabStop &rTab = maTabStops.front();
+ rVal <<= static_cast<sal_Int32>(bConvert ? convertTwipToMm100(rTab.GetTabPos()) : rTab.GetTabPos());
+ break;
+ }
+ case MID_TABSTOP_DEFAULT_DISTANCE:
+ {
+ rVal <<= static_cast<sal_Int32>(bConvert ? convertTwipToMm100(mnDefaultDistance) : mnDefaultDistance);
+ break;
+ }
+ }
+ return true;
+}
+
+bool SvxTabStopItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ bool bConvert = 0!=(nMemberId&CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+ switch ( nMemberId )
+ {
+ case MID_TABSTOPS:
+ {
+ uno::Sequence< style::TabStop> aSeq;
+ if(!(rVal >>= aSeq))
+ {
+ uno::Sequence < uno::Sequence < uno::Any > > aAnySeq;
+ if (!(rVal >>= aAnySeq))
+ return false;
+ auto aAnySeqRange = asNonConstRange(aAnySeq);
+ sal_Int32 nLength = aAnySeq.getLength();
+ aSeq.realloc( nLength );
+ auto pSeq = aSeq.getArray();
+ for ( sal_Int32 n=0; n<nLength; n++ )
+ {
+ uno::Sequence < uno::Any >& rAnySeq = aAnySeqRange[n];
+ if ( rAnySeq.getLength() == 4 )
+ {
+ if (!(rAnySeq[0] >>= pSeq[n].Position)) return false;
+ if (!(rAnySeq[1] >>= pSeq[n].Alignment))
+ {
+ sal_Int32 nVal = 0;
+ if (rAnySeq[1] >>= nVal)
+ pSeq[n].Alignment = static_cast<css::style::TabAlign>(nVal);
+ else
+ return false;
+ }
+ if (!(rAnySeq[2] >>= pSeq[n].DecimalChar))
+ {
+ OUString aVal;
+ if ( (rAnySeq[2] >>= aVal) && aVal.getLength() == 1 )
+ pSeq[n].DecimalChar = aVal.toChar();
+ else
+ return false;
+ }
+ if (!(rAnySeq[3] >>= pSeq[n].FillChar))
+ {
+ OUString aVal;
+ if ( (rAnySeq[3] >>= aVal) && aVal.getLength() == 1 )
+ pSeq[n].FillChar = aVal.toChar();
+ else
+ return false;
+ }
+ }
+ else
+ return false;
+ }
+ }
+
+ maTabStops.clear();
+ const style::TabStop* pArr = aSeq.getConstArray();
+ const sal_uInt16 nCount = static_cast<sal_uInt16>(aSeq.getLength());
+ for(sal_uInt16 i = 0; i < nCount ; i++)
+ {
+ SvxTabAdjust eAdjust = SvxTabAdjust::Default;
+ switch(pArr[i].Alignment)
+ {
+ case style::TabAlign_LEFT : eAdjust = SvxTabAdjust::Left; break;
+ case style::TabAlign_CENTER : eAdjust = SvxTabAdjust::Center; break;
+ case style::TabAlign_RIGHT : eAdjust = SvxTabAdjust::Right; break;
+ case style::TabAlign_DECIMAL: eAdjust = SvxTabAdjust::Decimal; break;
+ default: ;//prevent warning
+ }
+ sal_Unicode cFill = pArr[i].FillChar;
+ sal_Unicode cDecimal = pArr[i].DecimalChar;
+ SvxTabStop aTab( bConvert ? o3tl::toTwips(pArr[i].Position, o3tl::Length::mm100) : pArr[i].Position,
+ eAdjust,
+ cDecimal,
+ cFill );
+ Insert(aTab);
+ }
+ break;
+ }
+ case MID_STD_TAB:
+ {
+ sal_Int32 nNewPos = 0;
+ if (!(rVal >>= nNewPos) )
+ return false;
+ if (bConvert)
+ nNewPos = o3tl::toTwips(nNewPos, o3tl::Length::mm100);
+ if (nNewPos <= 0)
+ return false;
+ const SvxTabStop& rTab = maTabStops.front();
+ SvxTabStop aNewTab ( nNewPos, rTab.GetAdjustment(), rTab.GetDecimal(), rTab.GetFill() );
+ Remove( 0 );
+ Insert( aNewTab );
+ break;
+ }
+ case MID_TABSTOP_DEFAULT_DISTANCE:
+ {
+ sal_Int32 nNewDefaultDistance = 0;
+ if (!(rVal >>= nNewDefaultDistance))
+ return false;
+ if (bConvert)
+ nNewDefaultDistance = o3tl::toTwips(nNewDefaultDistance, o3tl::Length::mm100);
+ if (nNewDefaultDistance < 0)
+ return false;
+ mnDefaultDistance = nNewDefaultDistance;
+ break;
+ }
+ }
+ return true;
+}
+
+
+bool SvxTabStopItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ const SvxTabStopItem& rTSI = static_cast<const SvxTabStopItem&>(rAttr);
+
+ if ( mnDefaultDistance != rTSI.GetDefaultDistance() )
+ return false;
+
+ if ( Count() != rTSI.Count() )
+ return false;
+
+ for ( sal_uInt16 i = 0; i < Count(); ++i )
+ if( (*this)[i] != rTSI[i] )
+ return false;
+ return true;
+}
+
+SvxTabStopItem* SvxTabStopItem::Clone( SfxItemPool * ) const
+{
+ return new SvxTabStopItem( *this );
+}
+
+bool SvxTabStopItem::GetPresentation
+(
+ SfxItemPresentation ePres,
+ MapUnit eCoreUnit,
+ MapUnit ePresUnit,
+ OUString& rText, const IntlWrapper& rIntl
+) const
+{
+ rText.clear();
+ // TODO also consider mnDefaultTabDistance here
+
+ bool bComma = false;
+
+ for ( sal_uInt16 i = 0; i < Count(); ++i )
+ {
+ if ( SvxTabAdjust::Default != ((*this)[i]).GetAdjustment() )
+ {
+ if ( bComma )
+ rText += ",";
+ rText += GetMetricText(
+ ((*this)[i]).GetTabPos(), eCoreUnit, ePresUnit, &rIntl );
+ if ( SfxItemPresentation::Complete == ePres )
+ {
+ rText += " " + EditResId(GetMetricId(ePresUnit));
+ }
+ bComma = true;
+ }
+ }
+ return true;
+}
+
+
+bool SvxTabStopItem::Insert( const SvxTabStop& rTab )
+{
+ sal_uInt16 nTabPos = GetPos(rTab);
+ if(SVX_TAB_NOTFOUND != nTabPos )
+ Remove(nTabPos);
+ return maTabStops.insert( rTab ).second;
+}
+
+void SvxTabStopItem::Insert( const SvxTabStopItem* pTabs )
+{
+ for( sal_uInt16 i = 0; i < pTabs->Count(); i++ )
+ {
+ const SvxTabStop& rTab = (*pTabs)[i];
+ sal_uInt16 nTabPos = GetPos(rTab);
+ if(SVX_TAB_NOTFOUND != nTabPos)
+ Remove(nTabPos);
+ }
+ for( sal_uInt16 i = 0; i < pTabs->Count(); i++ )
+ {
+ maTabStops.insert( (*pTabs)[i] );
+ }
+}
+
+void SvxTabStopItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxTabStopItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("mnDefaultDistance"),
+ BAD_CAST(OString::number(mnDefaultDistance).getStr()));
+ for (const auto& rTabStop : maTabStops)
+ rTabStop.dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// class SvxFormatSplitItem -------------------------------------------------
+SvxFormatSplitItem::~SvxFormatSplitItem()
+{
+}
+
+SvxFormatSplitItem* SvxFormatSplitItem::Clone( SfxItemPool * ) const
+{
+ return new SvxFormatSplitItem( *this );
+}
+
+bool SvxFormatSplitItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ TranslateId pId = RID_SVXITEMS_FMTSPLIT_FALSE;
+
+ if ( GetValue() )
+ pId = RID_SVXITEMS_FMTSPLIT_TRUE;
+ rText = EditResId(pId);
+ return true;
+}
+
+SvxPageModelItem* SvxPageModelItem::Clone( SfxItemPool* ) const
+{
+ return new SvxPageModelItem( *this );
+}
+
+bool SvxPageModelItem::QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+
+ switch ( nMemberId )
+ {
+ case MID_AUTO: rVal <<= bAuto; break;
+ case MID_NAME: rVal <<= GetValue(); break;
+ default: OSL_FAIL("Wrong MemberId!"); return false;
+ }
+
+ return true;
+}
+
+bool SvxPageModelItem::PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet;
+ OUString aStr;
+ switch ( nMemberId )
+ {
+ case MID_AUTO: bRet = ( rVal >>= bAuto ); break;
+ case MID_NAME: bRet = ( rVal >>= aStr ); if ( bRet ) SetValue(aStr); break;
+ default: OSL_FAIL("Wrong MemberId!"); return false;
+ }
+
+ return bRet;
+}
+
+bool SvxPageModelItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ return SfxStringItem::operator==(rAttr) &&
+ bAuto == static_cast<const SvxPageModelItem&>( rAttr ).bAuto;
+}
+
+bool SvxPageModelItem::GetPresentation
+(
+ SfxItemPresentation ePres,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText.clear();
+ bool bSet = !GetValue().isEmpty();
+
+ switch ( ePres )
+ {
+ case SfxItemPresentation::Nameless:
+ if ( bSet )
+ rText = GetValue();
+ return true;
+
+ case SfxItemPresentation::Complete:
+ if ( bSet )
+ {
+ rText = EditResId(RID_SVXITEMS_PAGEMODEL_COMPLETE) + GetValue();
+ }
+ return true;
+ default: ;//prevent warning
+ }
+ return false;
+}
+
+
+SvxScriptSpaceItem::SvxScriptSpaceItem( bool bOn, const sal_uInt16 nId )
+ : SfxBoolItem( nId, bOn )
+{
+}
+
+SvxScriptSpaceItem* SvxScriptSpaceItem::Clone( SfxItemPool * ) const
+{
+ return new SvxScriptSpaceItem( *this );
+}
+
+bool SvxScriptSpaceItem::GetPresentation(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/,
+ OUString &rText, const IntlWrapper& /*rIntl*/ ) const
+{
+ rText = EditResId( !GetValue()
+ ? RID_SVXITEMS_SCRPTSPC_OFF
+ : RID_SVXITEMS_SCRPTSPC_ON );
+ return true;
+}
+
+
+SvxHangingPunctuationItem::SvxHangingPunctuationItem(
+ bool bOn, const sal_uInt16 nId )
+ : SfxBoolItem( nId, bOn )
+{
+}
+
+SvxHangingPunctuationItem* SvxHangingPunctuationItem::Clone( SfxItemPool * ) const
+{
+ return new SvxHangingPunctuationItem( *this );
+}
+
+bool SvxHangingPunctuationItem::GetPresentation(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/,
+ OUString &rText, const IntlWrapper& /*rIntl*/ ) const
+{
+ rText = EditResId( !GetValue()
+ ? RID_SVXITEMS_HNGPNCT_OFF
+ : RID_SVXITEMS_HNGPNCT_ON );
+ return true;
+}
+
+
+SvxForbiddenRuleItem::SvxForbiddenRuleItem(
+ bool bOn, const sal_uInt16 nId )
+ : SfxBoolItem( nId, bOn )
+{
+}
+
+SvxForbiddenRuleItem* SvxForbiddenRuleItem::Clone( SfxItemPool * ) const
+{
+ return new SvxForbiddenRuleItem( *this );
+}
+
+bool SvxForbiddenRuleItem::GetPresentation(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/,
+ OUString &rText, const IntlWrapper& /*rIntl*/ ) const
+{
+ rText = EditResId( !GetValue()
+ ? RID_SVXITEMS_FORBIDDEN_RULE_OFF
+ : RID_SVXITEMS_FORBIDDEN_RULE_ON );
+ return true;
+}
+
+/*************************************************************************
+|* class SvxParaVertAlignItem
+*************************************************************************/
+
+SvxParaVertAlignItem::SvxParaVertAlignItem( Align nValue,
+ TypedWhichId<SvxParaVertAlignItem> nW )
+ : SfxUInt16Item( nW, static_cast<sal_uInt16>(nValue) )
+{
+}
+
+SvxParaVertAlignItem* SvxParaVertAlignItem::Clone( SfxItemPool* ) const
+{
+ return new SvxParaVertAlignItem( *this );
+}
+
+bool SvxParaVertAlignItem::GetPresentation(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/,
+ OUString &rText, const IntlWrapper& ) const
+{
+ TranslateId pTmp;
+ switch( GetValue() )
+ {
+ case Align::Automatic: pTmp = RID_SVXITEMS_PARAVERTALIGN_AUTO; break;
+ case Align::Top: pTmp = RID_SVXITEMS_PARAVERTALIGN_TOP; break;
+ case Align::Center: pTmp = RID_SVXITEMS_PARAVERTALIGN_CENTER; break;
+ case Align::Bottom: pTmp = RID_SVXITEMS_PARAVERTALIGN_BOTTOM; break;
+ default: pTmp = RID_SVXITEMS_PARAVERTALIGN_BASELINE; break;
+ }
+ rText = EditResId(pTmp);
+ return true;
+}
+
+bool SvxParaVertAlignItem::QueryValue( css::uno::Any& rVal,
+ sal_uInt8 /*nMemberId*/ ) const
+{
+ rVal <<= static_cast<sal_Int16>(GetValue());
+ return true;
+}
+
+bool SvxParaVertAlignItem::PutValue( const css::uno::Any& rVal,
+ sal_uInt8 /*nMemberId*/ )
+{
+ sal_Int16 nVal = sal_Int16();
+ if((rVal >>= nVal) && nVal >=0 && nVal <= sal_uInt16(Align::Bottom) )
+ {
+ SetValue( static_cast<Align>(nVal) );
+ return true;
+ }
+ else
+ return false;
+}
+
+SvxParaGridItem::SvxParaGridItem( bool bOn, const sal_uInt16 nId )
+ : SfxBoolItem( nId, bOn )
+{
+}
+
+SvxParaGridItem* SvxParaGridItem::Clone( SfxItemPool * ) const
+{
+ return new SvxParaGridItem( *this );
+}
+
+bool SvxParaGridItem::GetPresentation(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/,
+ OUString &rText, const IntlWrapper& /*rIntl*/ ) const
+{
+ rText = GetValue() ?
+ EditResId( RID_SVXITEMS_PARASNAPTOGRID_ON ) :
+ EditResId( RID_SVXITEMS_PARASNAPTOGRID_OFF );
+
+ return true;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/items/svdfield.cxx b/editeng/source/items/svdfield.cxx
new file mode 100644
index 0000000000..a9b78148c0
--- /dev/null
+++ b/editeng/source/items/svdfield.cxx
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <editeng/measfld.hxx>
+
+SdrMeasureField::~SdrMeasureField() {}
+
+std::unique_ptr<SvxFieldData> SdrMeasureField::Clone() const
+{
+ return std::make_unique<SdrMeasureField>(*this);
+}
+
+bool SdrMeasureField::operator==(const SvxFieldData& rSrc) const
+{
+ return eMeasureFieldKind == static_cast<const SdrMeasureField&>(rSrc).GetMeasureFieldKind();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/items/svxfont.cxx b/editeng/source/items/svxfont.cxx
new file mode 100644
index 0000000000..876bc06868
--- /dev/null
+++ b/editeng/source/items/svxfont.cxx
@@ -0,0 +1,906 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <editeng/svxfont.hxx>
+
+#include <vcl/glyphitemcache.hxx>
+#include <vcl/metric.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/print.hxx>
+#include <tools/debug.hxx>
+#include <tools/gen.hxx>
+#include <tools/poly.hxx>
+#include <unotools/charclass.hxx>
+#include <com/sun/star/i18n/KCharacterType.hpp>
+#include <editeng/escapementitem.hxx>
+#include <editeng/smallcaps.hxx>
+#include <sal/log.hxx>
+#include <limits>
+
+static tools::Long GetTextArray( const OutputDevice* pOut, const OUString& rStr, KernArray* pDXAry,
+ sal_Int32 nIndex, sal_Int32 nLen )
+
+{
+ const SalLayoutGlyphs* layoutGlyphs = SalLayoutGlyphsCache::self()->GetLayoutGlyphs(pOut, rStr, nIndex, nLen);
+ return pOut->GetTextArray( rStr, pDXAry, nIndex, nLen, true, nullptr, layoutGlyphs);
+}
+
+SvxFont::SvxFont()
+{
+ nEsc = 0;
+ nPropr = 100;
+ eCaseMap = SvxCaseMap::NotMapped;
+ SetLanguage(LANGUAGE_SYSTEM);
+}
+
+SvxFont::SvxFont( const vcl::Font &rFont )
+ : Font( rFont )
+{
+ nEsc = 0;
+ nPropr = 100;
+ eCaseMap = SvxCaseMap::NotMapped;
+ SetLanguage(LANGUAGE_SYSTEM);
+}
+
+SvxFont::SvxFont( const SvxFont &rFont )
+ : Font( rFont )
+{
+ nEsc = rFont.GetEscapement();
+ nPropr = rFont.GetPropr();
+ eCaseMap = rFont.GetCaseMap();
+ SetLanguage(rFont.GetLanguage());
+}
+
+void SvxFont::SetNonAutoEscapement(short nNewEsc, const OutputDevice* pOutDev)
+{
+ nEsc = nNewEsc;
+ if ( abs(nEsc) == DFLT_ESC_AUTO_SUPER )
+ {
+ double fAutoAscent = .8;
+ double fAutoDescent = .2;
+ if ( pOutDev )
+ {
+ const FontMetric& rFontMetric = pOutDev->GetFontMetric();
+ double fFontHeight = rFontMetric.GetAscent() + rFontMetric.GetDescent();
+ if ( fFontHeight )
+ {
+ fAutoAscent = rFontMetric.GetAscent() / fFontHeight;
+ fAutoDescent = rFontMetric.GetDescent() / fFontHeight;
+ }
+ }
+
+ if ( nEsc == DFLT_ESC_AUTO_SUPER )
+ nEsc = fAutoAscent * (100 - nPropr);
+ else //DFLT_ESC_AUTO_SUB
+ nEsc = fAutoDescent * -(100 - nPropr);
+ }
+
+ if ( nEsc > MAX_ESC_POS )
+ nEsc = MAX_ESC_POS;
+ else if ( nEsc < -MAX_ESC_POS )
+ nEsc = -MAX_ESC_POS;
+}
+
+tools::Polygon SvxFont::DrawArrow( OutputDevice &rOut, const tools::Rectangle& rRect,
+ const Size& rSize, const Color& rCol, bool bLeftOrTop, bool bVertical )
+{
+ tools::Polygon aPoly;
+ Point aTmp;
+ Point aNxt;
+ if (bVertical)
+ {
+ tools::Long nLeft = ((rRect.Left() + rRect.Right()) / 2) - (rSize.Height() / 2);
+ tools::Long nRight = ((rRect.Left() + rRect.Right()) / 2) + (rSize.Height() / 2);
+ tools::Long nMid = (rRect.Left() + rRect.Right()) / 2;
+ tools::Long nTop = ((rRect.Top() + rRect.Bottom()) / 2) - (rSize.Height() / 2);
+ tools::Long nBottom = nTop + rSize.Height();
+ if (nTop < rRect.Top())
+ {
+ if (bLeftOrTop)
+ {
+ nTop = rRect.Top();
+ nBottom = rRect.Bottom();
+ }
+ else
+ {
+ nTop = rRect.Bottom();
+ nBottom = rRect.Bottom() - (rSize.Height() / 2);
+ }
+ }
+ aTmp.setX(nRight);
+ aTmp.setY(nBottom);
+ aNxt.setX(nMid);
+ aNxt.setY(nTop);
+ aPoly.Insert(0, aTmp);
+ aPoly.Insert(0, aNxt);
+ aTmp.setX(nLeft);
+ aPoly.Insert(0, aTmp);
+ }
+ else
+ {
+ tools::Long nLeft = (rRect.Left() + rRect.Right() - rSize.Width()) / 2;
+ tools::Long nRight = nLeft + rSize.Width();
+ tools::Long nMid = (rRect.Top() + rRect.Bottom()) / 2;
+ tools::Long nTop = nMid - rSize.Height() / 2;
+ tools::Long nBottom = nTop + rSize.Height();
+ if (nLeft < rRect.Left())
+ {
+ nLeft = rRect.Left();
+ nRight = rRect.Right();
+ }
+ aTmp.setX(bLeftOrTop ? nLeft : nRight);
+ aTmp.setY(nMid);
+ aNxt.setX(bLeftOrTop ? nRight : nLeft);
+ aNxt.setY(nTop);
+ aPoly.Insert(0, aTmp);
+ aPoly.Insert(0, aNxt);
+ aNxt.setY(nBottom);
+ aPoly.Insert(0, aNxt);
+ }
+ Color aOldLineColor = rOut.GetLineColor();
+ Color aOldFillColor = rOut.GetFillColor();
+ rOut.SetFillColor( rCol );
+ rOut.SetLineColor( COL_BLACK );
+ rOut.DrawPolygon( aPoly );
+ rOut.DrawLine( aTmp, aNxt );
+ rOut.SetLineColor( aOldLineColor );
+ rOut.SetFillColor( aOldFillColor );
+ return aPoly;
+}
+
+OUString SvxFont::CalcCaseMap(const OUString &rTxt) const
+{
+ if (!IsCaseMap() || rTxt.isEmpty())
+ return rTxt;
+ OUString aTxt(rTxt);
+ // I still have to get the language
+ const LanguageType eLang = LANGUAGE_DONTKNOW == GetLanguage()
+ ? LANGUAGE_SYSTEM : GetLanguage();
+
+ CharClass aCharClass(( LanguageTag(eLang) ));
+
+ switch( eCaseMap )
+ {
+ case SvxCaseMap::SmallCaps:
+ case SvxCaseMap::Uppercase:
+ {
+ aTxt = aCharClass.uppercase( aTxt );
+ break;
+ }
+
+ case SvxCaseMap::Lowercase:
+ {
+ aTxt = aCharClass.lowercase( aTxt );
+ break;
+ }
+ case SvxCaseMap::Capitalize:
+ {
+ // Every beginning of a word is capitalized, the rest of the word
+ // is taken over as is.
+ // Bug: if the attribute starts in the middle of the word.
+ bool bBlank = true;
+
+ for (sal_Int32 i = 0; i < aTxt.getLength(); ++i)
+ {
+ if( aTxt[i] == ' ' || aTxt[i] == '\t')
+ bBlank = true;
+ else
+ {
+ if (bBlank)
+ {
+ OUString sTitle(aCharClass.uppercase(OUString(aTxt[i])));
+ aTxt = aTxt.replaceAt(i, 1, sTitle);
+ }
+ bBlank = false;
+ }
+ }
+ break;
+ }
+ default:
+ {
+ SAL_WARN( "editeng", "SvxFont::CaseMapTxt: unknown casemap");
+ break;
+ }
+ }
+ return aTxt;
+}
+
+void SvxDoCapitals::DoSpace( const bool /*bDraw*/ ) { }
+
+void SvxDoCapitals::SetSpace() { }
+
+/*************************************************************************
+ * SvxFont::DoOnCapitals() const
+ * Decomposes the String into uppercase and lowercase letters and then
+ * calls the method SvxDoCapitals::Do( ).
+ *************************************************************************/
+
+void SvxFont::DoOnCapitals(SvxDoCapitals &rDo) const
+{
+ const OUString &rTxt = rDo.GetTxt();
+ const sal_Int32 nIdx = rDo.GetIdx();
+ const sal_Int32 nLen = rDo.GetLen();
+
+ const OUString aTxt( CalcCaseMap( rTxt ) );
+ const sal_Int32 nTxtLen = std::min( rTxt.getLength(), nLen );
+ sal_Int32 nPos = 0;
+ sal_Int32 nOldPos = nPos;
+
+ // Test if string length differ between original and CaseMapped
+ bool bCaseMapLengthDiffers(aTxt.getLength() != rTxt.getLength());
+
+ const LanguageType eLang = LANGUAGE_DONTKNOW == GetLanguage()
+ ? LANGUAGE_SYSTEM : GetLanguage();
+
+ CharClass aCharClass(( LanguageTag(eLang) ));
+ OUString aCharString;
+
+ while( nPos < nTxtLen )
+ {
+ // first in turn are the uppercase letters
+
+ // There are characters that are both upper- and lower-case L (eg blank)
+ // Such ambiguities lead to chaos, this is why these characters are
+ // allocated to the lowercase characters!
+
+ while( nPos < nTxtLen )
+ {
+ aCharString = rTxt.copy( nPos + nIdx, 1 );
+ sal_Int32 nCharacterType = aCharClass.getCharacterType( aCharString, 0 );
+ if ( nCharacterType & css::i18n::KCharacterType::LOWER )
+ break;
+ if ( ! ( nCharacterType & css::i18n::KCharacterType::UPPER ) )
+ break;
+ ++nPos;
+ }
+ if( nOldPos != nPos )
+ {
+ if(bCaseMapLengthDiffers)
+ {
+ // If strings differ work preparing the necessary snippet to address that
+ // potential difference
+ const OUString aSnippet = rTxt.copy(nIdx + nOldPos, nPos-nOldPos);
+ OUString aNewText = CalcCaseMap(aSnippet);
+
+ rDo.Do( aNewText, 0, aNewText.getLength(), true );
+ }
+ else
+ {
+ rDo.Do( aTxt, nIdx + nOldPos, nPos-nOldPos, true );
+ }
+
+ nOldPos = nPos;
+ }
+ // Now the lowercase are processed (without blanks)
+ while( nPos < nTxtLen )
+ {
+ sal_uInt32 nCharacterType = aCharClass.getCharacterType( aCharString, 0 );
+ if ( nCharacterType & css::i18n::KCharacterType::UPPER )
+ break;
+ if ( aCharString == " " )
+ break;
+ if( ++nPos < nTxtLen )
+ aCharString = rTxt.copy( nPos + nIdx, 1 );
+ }
+ if( nOldPos != nPos )
+ {
+ if(bCaseMapLengthDiffers)
+ {
+ // If strings differ work preparing the necessary snippet to address that
+ // potential difference
+ const OUString aSnippet = rTxt.copy(nIdx + nOldPos, nPos - nOldPos);
+ OUString aNewText = CalcCaseMap(aSnippet);
+
+ rDo.Do( aNewText, 0, aNewText.getLength(), false );
+ }
+ else
+ {
+ rDo.Do( aTxt, nIdx + nOldPos, nPos-nOldPos, false );
+ }
+
+ nOldPos = nPos;
+ }
+ // Now the blanks are<processed
+ while( nPos < nTxtLen && aCharString == " " && ++nPos < nTxtLen )
+ aCharString = rTxt.copy( nPos + nIdx, 1 );
+
+ if( nOldPos != nPos )
+ {
+ rDo.DoSpace( false );
+
+ if(bCaseMapLengthDiffers)
+ {
+ // If strings differ work preparing the necessary snippet to address that
+ // potential difference
+ const OUString aSnippet = rTxt.copy(nIdx + nOldPos, nPos - nOldPos);
+ OUString aNewText = CalcCaseMap(aSnippet);
+
+ rDo.Do( aNewText, 0, aNewText.getLength(), false );
+ }
+ else
+ {
+ rDo.Do( aTxt, nIdx + nOldPos, nPos - nOldPos, false );
+ }
+
+ nOldPos = nPos;
+ rDo.SetSpace();
+ }
+ }
+ rDo.DoSpace( true );
+}
+
+
+void SvxFont::SetPhysFont(OutputDevice& rOut) const
+{
+ const vcl::Font& rCurrentFont = rOut.GetFont();
+ if ( nPropr == 100 )
+ {
+ if ( !rCurrentFont.IsSameInstance( *this ) )
+ rOut.SetFont( *this );
+ }
+ else
+ {
+ Font aNewFont( *this );
+ Size aSize( aNewFont.GetFontSize() );
+ aNewFont.SetFontSize( Size( aSize.Width() * nPropr / 100,
+ aSize.Height() * nPropr / 100 ) );
+ if ( !rCurrentFont.IsSameInstance( aNewFont ) )
+ rOut.SetFont( aNewFont );
+ }
+}
+
+vcl::Font SvxFont::ChgPhysFont(OutputDevice& rOut) const
+{
+ vcl::Font aOldFont(rOut.GetFont());
+ SetPhysFont(rOut);
+ return aOldFont;
+}
+
+Size SvxFont::GetPhysTxtSize( const OutputDevice *pOut, const OUString &rTxt,
+ const sal_Int32 nIdx, const sal_Int32 nLen ) const
+{
+ if ( !IsCaseMap() && !IsFixKerning() )
+ return Size( pOut->GetTextWidth( rTxt, nIdx, nLen ),
+ pOut->GetTextHeight() );
+
+ Size aTxtSize;
+ aTxtSize.setHeight( pOut->GetTextHeight() );
+ if ( !IsCaseMap() )
+ aTxtSize.setWidth( pOut->GetTextWidth( rTxt, nIdx, nLen ) );
+ else
+ {
+ const OUString aNewText = CalcCaseMap(rTxt);
+ bool bCaseMapLengthDiffers(aNewText.getLength() != rTxt.getLength());
+ sal_Int32 nWidth(0);
+
+ if(bCaseMapLengthDiffers)
+ {
+ // If strings differ work preparing the necessary snippet to address that
+ // potential difference
+ const OUString aSnippet = rTxt.copy(nIdx, nLen);
+ OUString _aNewText = CalcCaseMap(aSnippet);
+ nWidth = pOut->GetTextWidth( _aNewText, 0, _aNewText.getLength() );
+ }
+ else
+ {
+ nWidth = pOut->GetTextWidth( aNewText, nIdx, nLen );
+ }
+
+ aTxtSize.setWidth(nWidth);
+ }
+
+ if( IsFixKerning() && ( nLen > 1 ) )
+ {
+ auto nKern = GetFixKerning();
+ KernArray aDXArray;
+ GetTextArray(pOut, rTxt, &aDXArray, nIdx, nLen);
+ tools::Long nOldValue = aDXArray[0];
+ sal_Int32 nSpaceCount = 0;
+ for(sal_Int32 i = 1; i < nLen; ++i)
+ {
+ if (aDXArray[i] != nOldValue)
+ {
+ nOldValue = aDXArray[i];
+ ++nSpaceCount;
+ }
+ }
+ aTxtSize.AdjustWidth( nSpaceCount * tools::Long( nKern ) );
+ }
+
+ return aTxtSize;
+}
+
+Size SvxFont::GetPhysTxtSize( const OutputDevice *pOut )
+{
+ if ( !IsCaseMap() && !IsFixKerning() )
+ return Size( pOut->GetTextWidth( "" ), pOut->GetTextHeight() );
+
+ Size aTxtSize;
+ aTxtSize.setHeight( pOut->GetTextHeight() );
+ if ( !IsCaseMap() )
+ aTxtSize.setWidth( pOut->GetTextWidth( "" ) );
+ else
+ aTxtSize.setWidth( pOut->GetTextWidth( CalcCaseMap( "" ) ) );
+
+ return aTxtSize;
+}
+
+Size SvxFont::QuickGetTextSize( const OutputDevice *pOut, const OUString &rTxt,
+ const sal_Int32 nIdx, const sal_Int32 nLen, KernArray* pDXArray ) const
+{
+ if ( !IsCaseMap() && !IsFixKerning() )
+ {
+ SAL_INFO( "editeng.quicktextsize", "SvxFont::QuickGetTextSize before GetTextArray(): Case map: " << IsCaseMap() << " Fix kerning: " << IsFixKerning());
+ Size aTxtSize( GetTextArray( pOut, rTxt, pDXArray, nIdx, nLen ),
+ pOut->GetTextHeight() );
+ SAL_INFO( "editeng.quicktextsize", "SvxFont::QuickGetTextSize after GetTextArray(): Text length: " << nLen << " Text size: " << aTxtSize.Width() << "x" << aTxtSize.Height());
+ return aTxtSize;
+ }
+
+ KernArray aDXArray;
+
+ // We always need pDXArray to count the number of kern spaces
+ if (!pDXArray && IsFixKerning() && nLen > 1)
+ {
+ pDXArray = &aDXArray;
+ aDXArray.reserve(nLen);
+ }
+
+ Size aTxtSize;
+ aTxtSize.setHeight( pOut->GetTextHeight() );
+ SAL_INFO( "editeng.quicktextsize", "SvxFont::QuickGetTextSize before GetTextArray(): Case map: " << IsCaseMap() << " Fix kerning: " << IsFixKerning());
+ if ( !IsCaseMap() )
+ aTxtSize.setWidth( GetTextArray( pOut, rTxt, pDXArray, nIdx, nLen ) );
+ else
+ {
+ if (IsCapital() && !rTxt.isEmpty())
+ aTxtSize = GetCapitalSize(pOut, rTxt, pDXArray, nIdx, nLen);
+ else
+ aTxtSize.setWidth( GetTextArray( pOut, CalcCaseMap( rTxt ),
+ pDXArray, nIdx, nLen ) );
+ }
+ SAL_INFO( "editeng.quicktextsize", "SvxFont::QuickGetTextSize after GetTextArray(): Text length: " << nLen << " Text size: " << aTxtSize.Width() << "x" << aTxtSize.Height());
+
+ if( IsFixKerning() && ( nLen > 1 ) )
+ {
+ auto nKern = GetFixKerning();
+ tools::Long nOldValue = (*pDXArray)[0];
+ tools::Long nSpaceSum = nKern;
+ pDXArray->adjust(0, nSpaceSum);
+
+ for ( sal_Int32 i = 1; i < nLen; i++ )
+ {
+ if ( (*pDXArray)[i] != nOldValue )
+ {
+ nOldValue = (*pDXArray)[i];
+ nSpaceSum += nKern;
+ }
+ pDXArray->adjust(i, nSpaceSum);
+ }
+
+ // The last one is a nKern too big:
+ nOldValue = (*pDXArray)[nLen - 1];
+ tools::Long nNewValue = nOldValue - nKern;
+ for ( sal_Int32 i = nLen - 1; i >= 0 && (*pDXArray)[i] == nOldValue; --i)
+ pDXArray->set(i, nNewValue);
+
+ aTxtSize.AdjustWidth(nSpaceSum - nKern);
+ }
+
+ return aTxtSize;
+}
+
+Size SvxFont::GetTextSize(const OutputDevice& rOut, const OUString &rTxt,
+ const sal_Int32 nIdx, const sal_Int32 nLen) const
+{
+ sal_Int32 nTmp = nLen;
+ if ( nTmp == SAL_MAX_INT32 ) // already initialized?
+ nTmp = rTxt.getLength();
+ Font aOldFont( ChgPhysFont(const_cast<OutputDevice&>(rOut)));
+ Size aTxtSize;
+ if( IsCapital() && !rTxt.isEmpty() )
+ {
+ aTxtSize = GetCapitalSize(&rOut, rTxt, nullptr, nIdx, nTmp);
+ }
+ else aTxtSize = GetPhysTxtSize(&rOut,rTxt,nIdx,nTmp);
+ const_cast<OutputDevice&>(rOut).SetFont(aOldFont);
+ return aTxtSize;
+}
+
+static void DrawTextArray( OutputDevice* pOut, const Point& rStartPt, const OUString& rStr,
+ std::span<const sal_Int32> pDXAry,
+ std::span<const sal_Bool> pKashidaAry,
+ sal_Int32 nIndex, sal_Int32 nLen )
+{
+ const SalLayoutGlyphs* layoutGlyphs = SalLayoutGlyphsCache::self()->GetLayoutGlyphs(pOut, rStr, nIndex, nLen);
+ pOut->DrawTextArray(rStartPt, rStr, pDXAry, pKashidaAry, nIndex, nLen, SalLayoutFlags::NONE, layoutGlyphs);
+}
+
+void SvxFont::QuickDrawText( OutputDevice *pOut,
+ const Point &rPos, const OUString &rTxt,
+ const sal_Int32 nIdx, const sal_Int32 nLen,
+ std::span<const sal_Int32> pDXArray,
+ std::span<const sal_Bool> pKashidaArray) const
+{
+
+ // Font has to be selected in OutputDevice...
+ if ( !IsCaseMap() && !IsCapital() && !IsFixKerning() && !IsEsc() )
+ {
+ DrawTextArray( pOut, rPos, rTxt, pDXArray, pKashidaArray, nIdx, nLen );
+ return;
+ }
+
+ Point aPos( rPos );
+
+ if ( nEsc )
+ {
+ tools::Long nDiff = GetFontSize().Height();
+ nDiff *= nEsc;
+ nDiff /= 100;
+
+ if ( !IsVertical() )
+ aPos.AdjustY( -nDiff );
+ else
+ aPos.AdjustX(nDiff );
+ }
+
+ if( IsCapital() )
+ {
+ DrawCapital( pOut, aPos, rTxt, pDXArray, pKashidaArray, nIdx, nLen );
+ }
+ else
+ {
+ if ( IsFixKerning() && pDXArray.empty() )
+ {
+ Size aSize = GetPhysTxtSize( pOut, rTxt, nIdx, nLen );
+
+ if ( !IsCaseMap() )
+ pOut->DrawStretchText( aPos, aSize.Width(), rTxt, nIdx, nLen );
+ else
+ pOut->DrawStretchText( aPos, aSize.Width(), CalcCaseMap( rTxt ), nIdx, nLen );
+ }
+ else
+ {
+ if ( !IsCaseMap() )
+ DrawTextArray( pOut, aPos, rTxt, pDXArray, pKashidaArray, nIdx, nLen );
+ else
+ DrawTextArray( pOut, aPos, CalcCaseMap( rTxt ), pDXArray, pKashidaArray, nIdx, nLen );
+ }
+ }
+}
+
+
+void SvxFont::DrawPrev( OutputDevice *pOut, Printer* pPrinter,
+ const Point &rPos, const OUString &rTxt,
+ const sal_Int32 nIdx, const sal_Int32 nLen ) const
+{
+ if ( !nLen || rTxt.isEmpty() )
+ return;
+ sal_Int32 nTmp = nLen;
+
+ if ( nTmp == SAL_MAX_INT32 ) // already initialized?
+ nTmp = rTxt.getLength();
+ Point aPos( rPos );
+
+ if ( nEsc )
+ {
+ short nTmpEsc;
+ if( DFLT_ESC_AUTO_SUPER == nEsc )
+ {
+ nTmpEsc = .8 * (100 - nPropr);
+ assert (nTmpEsc == DFLT_ESC_SUPER && "I'm sure this formula needs to be changed, but how to confirm that???");
+ nTmpEsc = DFLT_ESC_SUPER;
+ }
+ else if( DFLT_ESC_AUTO_SUB == nEsc )
+ {
+ nTmpEsc = .2 * -(100 - nPropr);
+ assert (nTmpEsc == -20 && "I'm sure this formula needs to be changed, but how to confirm that???");
+ nTmpEsc = -20;
+ }
+ else
+ nTmpEsc = nEsc;
+ Size aSize = GetFontSize();
+ aPos.AdjustY( -(( nTmpEsc * aSize.Height() ) / 100) );
+ }
+ Font aOldFont( ChgPhysFont(*pOut) );
+ Font aOldPrnFont( ChgPhysFont(*pPrinter) );
+
+ if ( IsCapital() )
+ DrawCapital( pOut, aPos, rTxt, {}, {}, nIdx, nTmp );
+ else
+ {
+ Size aSize = GetPhysTxtSize( pPrinter, rTxt, nIdx, nTmp );
+
+ if ( !IsCaseMap() )
+ pOut->DrawStretchText( aPos, aSize.Width(), rTxt, nIdx, nTmp );
+ else
+ {
+ const OUString aNewText = CalcCaseMap(rTxt);
+ bool bCaseMapLengthDiffers(aNewText.getLength() != rTxt.getLength());
+
+ if(bCaseMapLengthDiffers)
+ {
+ // If strings differ work preparing the necessary snippet to address that
+ // potential difference
+ const OUString aSnippet(rTxt.copy( nIdx, nTmp));
+ OUString _aNewText = CalcCaseMap(aSnippet);
+
+ pOut->DrawStretchText( aPos, aSize.Width(), _aNewText, 0, _aNewText.getLength() );
+ }
+ else
+ {
+ pOut->DrawStretchText( aPos, aSize.Width(), CalcCaseMap( rTxt ), nIdx, nTmp );
+ }
+ }
+ }
+ pOut->SetFont(aOldFont);
+ pPrinter->SetFont( aOldPrnFont );
+}
+
+
+SvxFont& SvxFont::operator=( const vcl::Font& rFont )
+{
+ Font::operator=( rFont );
+ return *this;
+}
+
+SvxFont& SvxFont::operator=( const SvxFont& rFont )
+{
+ Font::operator=( rFont );
+ eCaseMap = rFont.eCaseMap;
+ nEsc = rFont.nEsc;
+ nPropr = rFont.nPropr;
+ return *this;
+}
+
+namespace {
+
+class SvxDoGetCapitalSize : public SvxDoCapitals
+{
+protected:
+ VclPtr<OutputDevice> pOut;
+ SvxFont* pFont;
+ Size aTxtSize;
+ short nKern;
+ KernArray* pDXAry;
+public:
+ SvxDoGetCapitalSize( SvxFont *_pFnt, const OutputDevice *_pOut,
+ const OUString &_rTxt, KernArray* _pDXAry, const sal_Int32 _nIdx,
+ const sal_Int32 _nLen, const short _nKrn )
+ : SvxDoCapitals( _rTxt, _nIdx, _nLen ),
+ pOut( const_cast<OutputDevice*>(_pOut) ),
+ pFont( _pFnt ),
+ nKern( _nKrn ),
+ pDXAry( _pDXAry )
+ {
+ if (pDXAry)
+ {
+ pDXAry->clear();
+ pDXAry->reserve(_nLen);
+ }
+ }
+
+ virtual void Do( const OUString &rTxt, const sal_Int32 nIdx,
+ const sal_Int32 nLen, const bool bUpper ) override;
+
+ const Size &GetSize() const { return aTxtSize; };
+};
+
+}
+
+void SvxDoGetCapitalSize::Do( const OUString &_rTxt, const sal_Int32 _nIdx,
+ const sal_Int32 _nLen, const bool bUpper )
+{
+ Size aPartSize;
+ sal_uInt8 nProp(0);
+ if ( !bUpper )
+ {
+ nProp = pFont->GetPropr();
+ pFont->SetProprRel( SMALL_CAPS_PERCENTAGE );
+ pFont->SetPhysFont( *pOut );
+ }
+
+ if (pDXAry)
+ {
+ KernArray aKernArray;
+ aPartSize.setWidth(pOut->GetTextArray(_rTxt, &aKernArray, _nIdx, _nLen));
+ assert(pDXAry->get_factor() == aKernArray.get_factor());
+ auto& dest = pDXAry->get_subunit_array();
+ sal_Int32 nStart = dest.empty() ? 0 : dest.back();
+ size_t nSrcLen = aKernArray.size();
+ dest.reserve(dest.size() + nSrcLen);
+ const auto& src = aKernArray.get_subunit_array();
+ for (size_t i = 0; i < nSrcLen; ++i)
+ dest.push_back(src[i] + nStart);
+ }
+ else
+ {
+ aPartSize.setWidth( pOut->GetTextWidth( _rTxt, _nIdx, _nLen ) );
+ }
+
+ aPartSize.setHeight( pOut->GetTextHeight() );
+
+ if ( !bUpper )
+ {
+ aTxtSize.setHeight( aPartSize.Height() );
+ pFont->SetPropr( nProp );
+ pFont->SetPhysFont( *pOut );
+ }
+
+ aTxtSize.AdjustWidth(aPartSize.Width() );
+ aTxtSize.AdjustWidth( _nLen * tools::Long( nKern ) );
+}
+
+Size SvxFont::GetCapitalSize( const OutputDevice *pOut, const OUString &rTxt, KernArray* pDXAry,
+ const sal_Int32 nIdx, const sal_Int32 nLen) const
+{
+ // Start:
+ SvxDoGetCapitalSize aDo( const_cast<SvxFont *>(this), pOut, rTxt, pDXAry, nIdx, nLen, GetFixKerning() );
+ DoOnCapitals( aDo );
+ Size aTxtSize( aDo.GetSize() );
+
+ // End:
+ if( !aTxtSize.Height() )
+ {
+ aTxtSize.setWidth( 0 );
+ aTxtSize.setHeight( pOut->GetTextHeight() );
+ }
+ return aTxtSize;
+}
+
+namespace {
+
+class SvxDoDrawCapital : public SvxDoCapitals
+{
+protected:
+ VclPtr<OutputDevice> pOut;
+ SvxFont *pFont;
+ Point aPos;
+ Point aSpacePos;
+ short nKern;
+ std::span<const sal_Int32> pDXArray;
+ std::span<const sal_Bool> pKashidaArray;
+public:
+ SvxDoDrawCapital( SvxFont *pFnt, OutputDevice *_pOut, const OUString &_rTxt,
+ std::span<const sal_Int32> _pDXArray,
+ std::span<const sal_Bool> _pKashidaArray,
+ const sal_Int32 _nIdx, const sal_Int32 _nLen,
+ const Point &rPos, const short nKrn )
+ : SvxDoCapitals( _rTxt, _nIdx, _nLen ),
+ pOut( _pOut ),
+ pFont( pFnt ),
+ aPos( rPos ),
+ aSpacePos( rPos ),
+ nKern( nKrn ),
+ pDXArray(_pDXArray),
+ pKashidaArray(_pKashidaArray)
+ { }
+ virtual void DoSpace( const bool bDraw ) override;
+ virtual void SetSpace() override;
+ virtual void Do( const OUString &rTxt, const sal_Int32 nIdx,
+ const sal_Int32 nLen, const bool bUpper ) override;
+};
+
+}
+
+void SvxDoDrawCapital::DoSpace( const bool bDraw )
+{
+ if ( !(bDraw || pFont->IsWordLineMode()) )
+ return;
+
+ sal_Int32 nDiff = static_cast<sal_Int32>(aPos.X() - aSpacePos.X());
+ if ( nDiff )
+ {
+ bool bWordWise = pFont->IsWordLineMode();
+ bool bTrans = pFont->IsTransparent();
+ pFont->SetWordLineMode( false );
+ pFont->SetTransparent( true );
+ pFont->SetPhysFont(*pOut);
+ pOut->DrawStretchText( aSpacePos, nDiff, " ", 0, 2 );
+ pFont->SetWordLineMode( bWordWise );
+ pFont->SetTransparent( bTrans );
+ pFont->SetPhysFont(*pOut);
+ }
+}
+
+void SvxDoDrawCapital::SetSpace()
+{
+ if ( pFont->IsWordLineMode() )
+ aSpacePos.setX( aPos.X() );
+}
+
+void SvxDoDrawCapital::Do( const OUString &_rTxt, const sal_Int32 nSpanIdx,
+ const sal_Int32 nSpanLen, const bool bUpper)
+{
+ sal_uInt8 nProp = 0;
+
+ // Set the desired font
+ FontLineStyle eUnder = pFont->GetUnderline();
+ FontLineStyle eOver = pFont->GetOverline();
+ FontStrikeout eStrike = pFont->GetStrikeout();
+ pFont->SetUnderline( LINESTYLE_NONE );
+ pFont->SetOverline( LINESTYLE_NONE );
+ pFont->SetStrikeout( STRIKEOUT_NONE );
+ if ( !bUpper )
+ {
+ nProp = pFont->GetPropr();
+ pFont->SetProprRel( SMALL_CAPS_PERCENTAGE );
+ }
+ pFont->SetPhysFont(*pOut);
+
+ if (pDXArray.empty())
+ {
+ auto nWidth = pOut->GetTextWidth(_rTxt, nSpanIdx, nSpanLen);
+ if (nKern)
+ {
+ aPos.AdjustX(nKern/2);
+ if (nSpanLen)
+ nWidth += (nSpanLen * nKern);
+ }
+ pOut->DrawStretchText(aPos, nWidth-nKern, _rTxt, nSpanIdx, nSpanLen);
+ // in this case we move aPos along to be the start of each subspan
+ aPos.AdjustX(nWidth-(nKern/2) );
+ }
+ else
+ {
+ const sal_Int32 nStartOffset = nSpanIdx - nIdx;
+ sal_Int32 nStartX = nStartOffset ? pDXArray[nStartOffset - 1] : 0;
+
+ Point aStartPos(aPos.X() + nStartX, aPos.Y());
+
+ std::vector<sal_Int32> aDXArray;
+ aDXArray.reserve(nSpanLen);
+ for (sal_Int32 i = 0; i < nSpanLen; ++i)
+ aDXArray.push_back(pDXArray[nStartOffset + i] - nStartX);
+
+ auto aKashidaArray = !pKashidaArray.empty() ?
+ std::span<const sal_Bool>(pKashidaArray.data() + nStartOffset, nSpanLen) :
+ std::span<const sal_Bool>();
+
+ DrawTextArray(pOut, aStartPos, _rTxt, aDXArray, aKashidaArray, nSpanIdx, nSpanLen);
+ // in this case we leave aPos at the start and use the DXArray to find the start
+ // of each subspan
+ }
+
+ // Restore Font
+ pFont->SetUnderline( eUnder );
+ pFont->SetOverline( eOver );
+ pFont->SetStrikeout( eStrike );
+ if ( !bUpper )
+ pFont->SetPropr( nProp );
+ pFont->SetPhysFont(*pOut);
+}
+
+/*************************************************************************
+ * SvxFont::DrawCapital() draws the uppercase letter.
+ *************************************************************************/
+
+void SvxFont::DrawCapital( OutputDevice *pOut,
+ const Point &rPos, const OUString &rTxt,
+ std::span<const sal_Int32> pDXArray,
+ std::span<const sal_Bool> pKashidaArray,
+ const sal_Int32 nIdx, const sal_Int32 nLen ) const
+{
+ SvxDoDrawCapital aDo(const_cast<SvxFont *>(this), pOut,
+ rTxt, pDXArray, pKashidaArray,
+ nIdx, nLen, rPos, GetFixKerning());
+ DoOnCapitals( aDo );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/items/textitem.cxx b/editeng/source/items/textitem.cxx
new file mode 100644
index 0000000000..e242566bd3
--- /dev/null
+++ b/editeng/source/items/textitem.cxx
@@ -0,0 +1,2808 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/style/CaseMap.hpp>
+#include <com/sun/star/awt/FontDescriptor.hpp>
+#include <com/sun/star/frame/status/FontHeight.hpp>
+#include <math.h>
+#include <sal/log.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <unotools/configmgr.hxx>
+#include <unotools/fontdefs.hxx>
+#include <unotools/intlwrapper.hxx>
+#include <unotools/syslocale.hxx>
+#include <utility>
+#include <vcl/outdev.hxx>
+#include <vcl/unohelp.hxx>
+#include <svtools/unitconv.hxx>
+
+#include <editeng/editids.hrc>
+#include <editeng/editrids.hrc>
+#include <tools/bigint.hxx>
+#include <tools/mapunit.hxx>
+#include <tools/UnitConversion.hxx>
+
+#include <rtl/math.hxx>
+#include <rtl/ustring.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <svl/itemset.hxx>
+
+#include <svtools/langtab.hxx>
+#include <svl/itempool.hxx>
+#include <svtools/ctrltool.hxx>
+#include <com/sun/star/awt/FontSlant.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/text/FontEmphasis.hpp>
+#include <editeng/rsiditem.hxx>
+#include <editeng/memberids.h>
+#include <editeng/flstitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/autokernitem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/kernitem.hxx>
+#include <editeng/cmapitem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/nhypitem.hxx>
+#include <editeng/blinkitem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/twolinesitem.hxx>
+#include <editeng/scripttypeitem.hxx>
+#include <editeng/charrotateitem.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/itemtype.hxx>
+#include <editeng/eerdll.hxx>
+#include <docmodel/color/ComplexColorJSON.hxx>
+#include <docmodel/uno/UnoComplexColor.hxx>
+#include <docmodel/color/ComplexColor.hxx>
+#include <libxml/xmlwriter.h>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::text;
+
+SfxPoolItem* SvxFontItem::CreateDefault() {return new SvxFontItem(0);}
+SfxPoolItem* SvxPostureItem::CreateDefault() { return new SvxPostureItem(ITALIC_NONE, 0);}
+SfxPoolItem* SvxWeightItem::CreateDefault() {return new SvxWeightItem(WEIGHT_NORMAL, 0);}
+SfxPoolItem* SvxFontHeightItem::CreateDefault() {return new SvxFontHeightItem(240, 100, 0);}
+SfxPoolItem* SvxUnderlineItem::CreateDefault() {return new SvxUnderlineItem(LINESTYLE_NONE, 0);}
+SfxPoolItem* SvxOverlineItem::CreateDefault() {return new SvxOverlineItem(LINESTYLE_NONE, 0);}
+SfxPoolItem* SvxCrossedOutItem::CreateDefault() {return new SvxCrossedOutItem(STRIKEOUT_NONE, 0);}
+SfxPoolItem* SvxShadowedItem::CreateDefault() {return new SvxShadowedItem(false, 0);}
+SfxPoolItem* SvxAutoKernItem::CreateDefault() {return new SvxAutoKernItem(false, 0);}
+SfxPoolItem* SvxWordLineModeItem::CreateDefault() {return new SvxWordLineModeItem(false, 0);}
+SfxPoolItem* SvxContourItem::CreateDefault() {return new SvxContourItem(false, 0);}
+SfxPoolItem* SvxColorItem::CreateDefault() {return new SvxColorItem(0);}
+SfxPoolItem* SvxKerningItem::CreateDefault() {return new SvxKerningItem(0, 0);}
+SfxPoolItem* SvxCaseMapItem::CreateDefault() {return new SvxCaseMapItem(SvxCaseMap::NotMapped, 0);}
+SfxPoolItem* SvxEscapementItem::CreateDefault() {return new SvxEscapementItem(0);}
+SfxPoolItem* SvxLanguageItem::CreateDefault() {return new SvxLanguageItem(LANGUAGE_GERMAN, 0);}
+SfxPoolItem* SvxEmphasisMarkItem::CreateDefault() {return new SvxEmphasisMarkItem(FontEmphasisMark::NONE, TypedWhichId<SvxEmphasisMarkItem>(0));}
+SfxPoolItem* SvxCharRotateItem::CreateDefault() {return new SvxCharRotateItem(0_deg10, false, TypedWhichId<SvxCharRotateItem>(0));}
+SfxPoolItem* SvxCharScaleWidthItem::CreateDefault() {return new SvxCharScaleWidthItem(100, TypedWhichId<SvxCharScaleWidthItem>(0));}
+SfxPoolItem* SvxCharReliefItem::CreateDefault() {return new SvxCharReliefItem(FontRelief::NONE, 0);}
+
+
+// class SvxFontListItem -------------------------------------------------
+
+SvxFontListItem::SvxFontListItem( const FontList* pFontLst,
+ const sal_uInt16 nId ) :
+ SfxPoolItem( nId ),
+ pFontList( pFontLst )
+{
+ if ( pFontList )
+ {
+ sal_Int32 nCount = pFontList->GetFontNameCount();
+ aFontNameSeq.realloc( nCount );
+ auto pFontNameSeq = aFontNameSeq.getArray();
+
+ for ( sal_Int32 i = 0; i < nCount; i++ )
+ pFontNameSeq[i] = pFontList->GetFontName(i).GetFamilyName();
+ }
+}
+
+SvxFontListItem* SvxFontListItem::Clone( SfxItemPool* ) const
+{
+ return new SvxFontListItem( *this );
+}
+
+bool SvxFontListItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ return( pFontList == static_cast<const SvxFontListItem&>(rAttr).pFontList );
+}
+
+bool SvxFontListItem::QueryValue( css::uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) const
+{
+ rVal <<= aFontNameSeq;
+ return true;
+}
+
+
+bool SvxFontListItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper& /*rIntl*/
+) const
+{
+ rText.clear();
+ return false;
+}
+
+// class SvxFontItem -----------------------------------------------------
+
+SvxFontItem::SvxFontItem( const sal_uInt16 nId ) :
+ SfxPoolItem( nId )
+{
+ eFamily = FAMILY_SWISS;
+ ePitch = PITCH_VARIABLE;
+ eTextEncoding = RTL_TEXTENCODING_DONTKNOW;
+}
+
+
+SvxFontItem::SvxFontItem( const FontFamily eFam, OUString aName,
+ OUString aStName, const FontPitch eFontPitch,
+ const rtl_TextEncoding eFontTextEncoding, const sal_uInt16 nId ) :
+
+ SfxPoolItem( nId ),
+
+ aFamilyName(std::move(aName)),
+ aStyleName(std::move(aStName))
+{
+ eFamily = eFam;
+ ePitch = eFontPitch;
+ eTextEncoding = eFontTextEncoding;
+}
+
+
+bool SvxFontItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch(nMemberId)
+ {
+ case 0:
+ {
+ css::awt::FontDescriptor aFontDescriptor;
+ aFontDescriptor.Name = aFamilyName;
+ aFontDescriptor.StyleName = aStyleName;
+ aFontDescriptor.Family = static_cast<sal_Int16>(eFamily);
+ aFontDescriptor.CharSet = static_cast<sal_Int16>(eTextEncoding);
+ aFontDescriptor.Pitch = static_cast<sal_Int16>(ePitch);
+ rVal <<= aFontDescriptor;
+ }
+ break;
+ case MID_FONT_FAMILY_NAME:
+ rVal <<= aFamilyName;
+ break;
+ case MID_FONT_STYLE_NAME:
+ rVal <<= aStyleName;
+ break;
+ case MID_FONT_FAMILY : rVal <<= static_cast<sal_Int16>(eFamily); break;
+ case MID_FONT_CHAR_SET : rVal <<= static_cast<sal_Int16>(eTextEncoding); break;
+ case MID_FONT_PITCH : rVal <<= static_cast<sal_Int16>(ePitch); break;
+ }
+ return true;
+}
+
+bool SvxFontItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId)
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch(nMemberId)
+ {
+ case 0:
+ {
+ css::awt::FontDescriptor aFontDescriptor;
+ if ( !( rVal >>= aFontDescriptor ))
+ return false;
+
+ aFamilyName = aFontDescriptor.Name;
+ aStyleName = aFontDescriptor.StyleName;
+ eFamily = static_cast<FontFamily>(aFontDescriptor.Family);
+ eTextEncoding = static_cast<rtl_TextEncoding>(aFontDescriptor.CharSet);
+ ePitch = static_cast<FontPitch>(aFontDescriptor.Pitch);
+ }
+ break;
+ case MID_FONT_FAMILY_NAME :
+ {
+ OUString aStr;
+ if(!(rVal >>= aStr))
+ return false;
+ aFamilyName = aStr;
+ }
+ break;
+ case MID_FONT_STYLE_NAME:
+ {
+ OUString aStr;
+ if(!(rVal >>= aStr))
+ return false;
+ aStyleName = aStr;
+ }
+ break;
+ case MID_FONT_FAMILY :
+ {
+ sal_Int16 nFamily = sal_Int16();
+ if(!(rVal >>= nFamily))
+ return false;
+ eFamily = static_cast<FontFamily>(nFamily);
+ }
+ break;
+ case MID_FONT_CHAR_SET :
+ {
+ sal_Int16 nSet = sal_Int16();
+ if(!(rVal >>= nSet))
+ return false;
+ eTextEncoding = static_cast<rtl_TextEncoding>(nSet);
+ }
+ break;
+ case MID_FONT_PITCH :
+ {
+ sal_Int16 nPitch = sal_Int16();
+ if(!(rVal >>= nPitch))
+ return false;
+ ePitch = static_cast<FontPitch>(nPitch);
+ }
+ break;
+ }
+ return true;
+}
+
+
+bool SvxFontItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ const SvxFontItem& rItem = static_cast<const SvxFontItem&>(rAttr);
+
+ bool bRet = ( eFamily == rItem.eFamily &&
+ aFamilyName == rItem.aFamilyName &&
+ aStyleName == rItem.aStyleName );
+
+ if ( bRet )
+ {
+ if ( ePitch != rItem.ePitch || eTextEncoding != rItem.eTextEncoding )
+ {
+ bRet = false;
+ SAL_INFO( "editeng.items", "FontItem::operator==(): only pitch or rtl_TextEncoding different ");
+ }
+ }
+ return bRet;
+}
+
+SvxFontItem* SvxFontItem::Clone( SfxItemPool * ) const
+{
+ return new SvxFontItem( *this );
+}
+
+bool SvxFontItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper& /*rIntl*/
+) const
+{
+ rText = aFamilyName;
+ return true;
+}
+
+
+void SvxFontItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxFontItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("familyName"), BAD_CAST(aFamilyName.toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("styleName"), BAD_CAST(aStyleName.toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("family"), BAD_CAST(OString::number(eFamily).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("pitch"), BAD_CAST(OString::number(ePitch).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("textEncoding"), BAD_CAST(OString::number(eTextEncoding).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// class SvxPostureItem --------------------------------------------------
+
+SvxPostureItem::SvxPostureItem( const FontItalic ePosture, const sal_uInt16 nId ) :
+ SfxEnumItem( nId, ePosture )
+{
+}
+
+SvxPostureItem* SvxPostureItem::Clone( SfxItemPool * ) const
+{
+ return new SvxPostureItem( *this );
+}
+
+sal_uInt16 SvxPostureItem::GetValueCount() const
+{
+ return ITALIC_NORMAL + 1; // ITALIC_NONE also belongs here
+}
+
+
+bool SvxPostureItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper& /*rIntl*/
+) const
+{
+ rText = GetValueTextByPos( GetValue() );
+ return true;
+}
+
+
+OUString SvxPostureItem::GetValueTextByPos( sal_uInt16 nPos )
+{
+ DBG_ASSERT( nPos <= sal_uInt16(ITALIC_NORMAL), "enum overflow!" );
+
+ FontItalic eItalic = static_cast<FontItalic>(nPos);
+ TranslateId pId;
+
+ switch ( eItalic )
+ {
+ case ITALIC_NONE: pId = RID_SVXITEMS_ITALIC_NONE; break;
+ case ITALIC_OBLIQUE: pId = RID_SVXITEMS_ITALIC_OBLIQUE; break;
+ case ITALIC_NORMAL: pId = RID_SVXITEMS_ITALIC_NORMAL; break;
+ default: ;//prevent warning
+ }
+
+ return pId ? EditResId(pId) : OUString();
+}
+
+bool SvxPostureItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch( nMemberId )
+ {
+ case MID_ITALIC:
+ rVal <<= GetBoolValue();
+ break;
+ case MID_POSTURE:
+ rVal <<= vcl::unohelper::ConvertFontSlant(GetValue());
+ break;
+ }
+ return true;
+}
+
+bool SvxPostureItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch( nMemberId )
+ {
+ case MID_ITALIC:
+ SetBoolValue(Any2Bool(rVal));
+ break;
+ case MID_POSTURE:
+ {
+ awt::FontSlant eSlant;
+ if(!(rVal >>= eSlant))
+ {
+ sal_Int32 nValue = 0;
+ if(!(rVal >>= nValue))
+ return false;
+
+ eSlant = static_cast<awt::FontSlant>(nValue);
+ }
+ SetValue(vcl::unohelper::ConvertFontSlant(eSlant));
+ }
+ }
+ return true;
+}
+
+bool SvxPostureItem::HasBoolValue() const
+{
+ return true;
+}
+
+bool SvxPostureItem::GetBoolValue() const
+{
+ return ( GetValue() >= ITALIC_OBLIQUE );
+}
+
+void SvxPostureItem::SetBoolValue( bool bVal )
+{
+ SetValue( bVal ? ITALIC_NORMAL : ITALIC_NONE );
+}
+
+void SvxPostureItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxPostureItem"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("whichId"), "%d", Which());
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("value"), "%d", GetValue());
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("presentation"), BAD_CAST(GetValueTextByPos(GetValue()).toUtf8().getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// class SvxWeightItem ---------------------------------------------------
+
+SvxWeightItem::SvxWeightItem( const FontWeight eWght, const sal_uInt16 nId ) :
+ SfxEnumItem( nId, eWght )
+{
+}
+
+
+bool SvxWeightItem::HasBoolValue() const
+{
+ return true;
+}
+
+
+bool SvxWeightItem::GetBoolValue() const
+{
+ return GetValue() >= WEIGHT_BOLD;
+}
+
+
+void SvxWeightItem::SetBoolValue( bool bVal )
+{
+ SetValue( bVal ? WEIGHT_BOLD : WEIGHT_NORMAL );
+}
+
+
+sal_uInt16 SvxWeightItem::GetValueCount() const
+{
+ return WEIGHT_BLACK; // WEIGHT_DONTKNOW does not belong
+}
+
+SvxWeightItem* SvxWeightItem::Clone( SfxItemPool * ) const
+{
+ return new SvxWeightItem( *this );
+}
+
+bool SvxWeightItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper& /*rIntl*/
+) const
+{
+ rText = GetValueTextByPos( GetValue() );
+ return true;
+}
+
+OUString SvxWeightItem::GetValueTextByPos( sal_uInt16 nPos )
+{
+ static TranslateId RID_SVXITEMS_WEIGHTS[] =
+ {
+ RID_SVXITEMS_WEIGHT_DONTKNOW,
+ RID_SVXITEMS_WEIGHT_THIN,
+ RID_SVXITEMS_WEIGHT_ULTRALIGHT,
+ RID_SVXITEMS_WEIGHT_LIGHT,
+ RID_SVXITEMS_WEIGHT_SEMILIGHT,
+ RID_SVXITEMS_WEIGHT_NORMAL,
+ RID_SVXITEMS_WEIGHT_MEDIUM,
+ RID_SVXITEMS_WEIGHT_SEMIBOLD,
+ RID_SVXITEMS_WEIGHT_BOLD,
+ RID_SVXITEMS_WEIGHT_ULTRABOLD,
+ RID_SVXITEMS_WEIGHT_BLACK
+ };
+
+ static_assert(std::size(RID_SVXITEMS_WEIGHTS) - 1 == WEIGHT_BLACK, "must match");
+ assert(nPos <= sal_uInt16(WEIGHT_BLACK) && "enum overflow!" );
+ return EditResId(RID_SVXITEMS_WEIGHTS[nPos]);
+}
+
+bool SvxWeightItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch( nMemberId )
+ {
+ case MID_BOLD :
+ rVal <<= GetBoolValue();
+ break;
+ case MID_WEIGHT:
+ {
+ rVal <<= vcl::unohelper::ConvertFontWeight( GetValue() );
+ }
+ break;
+ }
+ return true;
+}
+
+bool SvxWeightItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch( nMemberId )
+ {
+ case MID_BOLD :
+ SetBoolValue(Any2Bool(rVal));
+ break;
+ case MID_WEIGHT:
+ {
+ double fValue = 0;
+ if(!(rVal >>= fValue))
+ {
+ sal_Int32 nValue = 0;
+ if(!(rVal >>= nValue))
+ return false;
+ fValue = static_cast<float>(nValue);
+ }
+ SetValue( vcl::unohelper::ConvertFontWeight(static_cast<float>(fValue)) );
+ }
+ break;
+ }
+ return true;
+}
+
+void SvxWeightItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxWeightItem"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("whichId"), "%d", Which());
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("value"), "%d", GetValue());
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("presentation"), BAD_CAST(GetValueTextByPos(GetValue()).toUtf8().getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// class SvxFontHeightItem -----------------------------------------------
+
+SvxFontHeightItem::SvxFontHeightItem( const sal_uInt32 nSz,
+ const sal_uInt16 nPrp,
+ const sal_uInt16 nId ) :
+ SfxPoolItem( nId )
+{
+ SetHeight( nSz,nPrp ); // calculate in percentage
+}
+
+SvxFontHeightItem* SvxFontHeightItem::Clone( SfxItemPool * ) const
+{
+ return new SvxFontHeightItem( *this );
+}
+
+bool SvxFontHeightItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+ return GetHeight() == static_cast<const SvxFontHeightItem&>(rItem).GetHeight() &&
+ GetProp() == static_cast<const SvxFontHeightItem&>(rItem).GetProp() &&
+ GetPropUnit() == static_cast<const SvxFontHeightItem&>(rItem).GetPropUnit();
+}
+
+bool SvxFontHeightItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ // In StarOne is the uno::Any always 1/100mm. Through the MemberId it is
+ // controlled if the value in the Item should be 1/100mm or Twips.
+
+ bool bConvert = 0!=(nMemberId&CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+ switch( nMemberId )
+ {
+ case 0:
+ {
+ css::frame::status::FontHeight aFontHeight;
+
+ // Point (i.e. Twips) is asked for, thus re-calculate if
+ // CONVERT_TWIPS is not set.
+ if( bConvert )
+ {
+ aFontHeight.Height = o3tl::convert<double>(nHeight, o3tl::Length::twip, o3tl::Length::pt);
+ }
+ else
+ {
+ double fPoints = o3tl::convert<double>(nHeight, o3tl::Length::mm100, o3tl::Length::pt);
+ aFontHeight.Height = rtl::math::round(fPoints, 1);
+ }
+
+ aFontHeight.Prop = MapUnit::MapRelative == ePropUnit ? nProp : 100;
+
+ float fRet = nProp;
+ switch( ePropUnit )
+ {
+ case MapUnit::MapRelative:
+ fRet = 0.;
+ break;
+ case MapUnit::Map100thMM:
+ fRet = o3tl::convert(fRet, o3tl::Length::mm100, o3tl::Length::pt);
+ break;
+ case MapUnit::MapPoint:
+
+ break;
+ case MapUnit::MapTwip:
+ fRet = o3tl::convert(fRet, o3tl::Length::twip, o3tl::Length::pt);
+ break;
+ default: ;//prevent warning
+ }
+ aFontHeight.Diff = fRet;
+ rVal <<= aFontHeight;
+ }
+ break;
+ case MID_FONTHEIGHT:
+ {
+ // Point (i.e. Twips) is asked for, thus re-calculate if
+ // CONVERT_TWIPS is not set.
+ if( bConvert )
+ {
+ rVal <<= static_cast<float>(o3tl::convert<double>(nHeight, o3tl::Length::twip, o3tl::Length::pt));
+ }
+ else
+ {
+ double fPoints = o3tl::convert<double>(nHeight, o3tl::Length::mm100, o3tl::Length::pt);
+ rVal <<= static_cast<float>(::rtl::math::round(fPoints, 1));
+ }
+ }
+ break;
+ case MID_FONTHEIGHT_PROP:
+ rVal <<= static_cast<sal_Int16>(MapUnit::MapRelative == ePropUnit ? nProp : 100);
+ break;
+ case MID_FONTHEIGHT_DIFF:
+ {
+ float fRet = nProp;
+ switch( ePropUnit )
+ {
+ case MapUnit::MapRelative:
+ fRet = 0.;
+ break;
+ case MapUnit::Map100thMM:
+ fRet = o3tl::convert(fRet, o3tl::Length::mm100, o3tl::Length::pt);
+ break;
+ case MapUnit::MapPoint:
+
+ break;
+ case MapUnit::MapTwip:
+ fRet = o3tl::convert(fRet, o3tl::Length::twip, o3tl::Length::pt);
+ break;
+ default: ;//prevent warning
+ }
+ rVal <<= fRet;
+ }
+ break;
+ }
+ return true;
+}
+
+// Try to reconstruct the original height input value from the modified height
+// and the prop data; this seems somewhat futile given the various ways how the
+// modified height is calculated (with and without conversion between twips and
+// 100th mm; with an additional eCoreMetric input in one of the SetHeight
+// overloads), and indeed known to occasionally produce nRet values that would
+// be negative, so just guard against negative results here and throw the hands
+// up in despair:
+static sal_uInt32 lcl_GetRealHeight_Impl(sal_uInt32 nHeight, sal_uInt16 nProp, MapUnit eProp, bool bCoreInTwip)
+{
+ sal_uInt32 nRet = nHeight;
+ short nDiff = 0;
+ switch( eProp )
+ {
+ case MapUnit::MapRelative:
+ if (nProp)
+ {
+ nRet *= 100;
+ nRet /= nProp;
+ }
+ break;
+ case MapUnit::MapPoint:
+ {
+ short nTemp = static_cast<short>(nProp);
+ nDiff = nTemp * 20;
+ if(!bCoreInTwip)
+ nDiff = static_cast<short>(convertTwipToMm100(static_cast<tools::Long>(nDiff)));
+ break;
+ }
+ case MapUnit::Map100thMM:
+ //then the core is surely also in 1/100 mm
+ nDiff = static_cast<short>(nProp);
+ break;
+ case MapUnit::MapTwip:
+ // Here surely TWIP
+ nDiff = static_cast<short>(nProp);
+ break;
+ default:
+ break;
+ }
+ nRet = (nDiff < 0 || nRet >= o3tl::make_unsigned(nDiff))
+ ? nRet - nDiff : 0;
+ //TODO: overflow in case nDiff < 0 and nRet - nDiff > SAL_MAX_UINT32
+
+ return nRet;
+}
+
+bool SvxFontHeightItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ bool bConvert = 0!=(nMemberId&CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+ switch( nMemberId )
+ {
+ case 0:
+ {
+ css::frame::status::FontHeight aFontHeight;
+ if ( rVal >>= aFontHeight )
+ {
+ // Height
+ ePropUnit = MapUnit::MapRelative;
+ nProp = 100;
+ double fPoint = aFontHeight.Height;
+ if( fPoint < 0. || fPoint > 10000. )
+ return false;
+
+ nHeight = static_cast<tools::Long>( fPoint * 20.0 + 0.5 ); // Twips
+ if (!bConvert)
+ nHeight = convertTwipToMm100(nHeight); // Convert, if the item contains 1/100mm
+
+ nProp = aFontHeight.Prop;
+ }
+ else
+ return false;
+ }
+ break;
+ case MID_FONTHEIGHT:
+ {
+ ePropUnit = MapUnit::MapRelative;
+ nProp = 100;
+ double fPoint = 0;
+ if(!(rVal >>= fPoint))
+ {
+ sal_Int32 nValue = 0;
+ if(!(rVal >>= nValue))
+ return false;
+ fPoint = static_cast<float>(nValue);
+ }
+
+ if (fPoint < 0. || fPoint > 10000.)
+ return false;
+ static bool bFuzzing = utl::ConfigManager::IsFuzzing();
+ if (bFuzzing && fPoint > 240)
+ {
+ SAL_WARN("editeng.items", "SvxFontHeightItem ignoring font size of " << fPoint << " for performance");
+ return false;
+ }
+
+ nHeight = static_cast<tools::Long>( fPoint * 20.0 + 0.5 ); // Twips
+ if (!bConvert)
+ nHeight = convertTwipToMm100(nHeight); // Convert, if the item contains 1/100mm
+ }
+ break;
+ case MID_FONTHEIGHT_PROP:
+ {
+ sal_Int16 nNew = sal_Int16();
+ if(!(rVal >>= nNew))
+ return true;
+
+ nHeight = lcl_GetRealHeight_Impl(nHeight, nProp, ePropUnit, bConvert);
+
+ nHeight *= nNew;
+ nHeight /= 100;
+ nProp = nNew;
+ ePropUnit = MapUnit::MapRelative;
+ }
+ break;
+ case MID_FONTHEIGHT_DIFF:
+ {
+ nHeight = lcl_GetRealHeight_Impl(nHeight, nProp, ePropUnit, bConvert);
+ float fValue = 0;
+ if(!(rVal >>= fValue))
+ {
+ sal_Int32 nValue = 0;
+ if(!(rVal >>= nValue))
+ return false;
+ fValue = static_cast<float>(nValue);
+ }
+ sal_Int16 nCoreDiffValue = static_cast<sal_Int16>(fValue * 20.);
+ nHeight += bConvert ? nCoreDiffValue : convertTwipToMm100(nCoreDiffValue);
+ nProp = static_cast<sal_uInt16>(static_cast<sal_Int16>(fValue));
+ ePropUnit = MapUnit::MapPoint;
+ }
+ break;
+ }
+ return true;
+}
+
+
+bool SvxFontHeightItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit eCoreUnit,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper& rIntl
+) const
+{
+ if( MapUnit::MapRelative != ePropUnit )
+ {
+ rText = OUString::number( static_cast<short>(nProp) ) +
+ " " + EditResId( GetMetricId( ePropUnit ) );
+ if( 0 <= static_cast<short>(nProp) )
+ rText = "+" + rText;
+ }
+ else if( 100 == nProp )
+ {
+ rText = GetMetricText( static_cast<tools::Long>(nHeight),
+ eCoreUnit, MapUnit::MapPoint, &rIntl ) +
+ " " + EditResId(GetMetricId(MapUnit::MapPoint));
+ }
+ else
+ rText = OUString::number( nProp ) + "%";
+ return true;
+}
+
+
+void SvxFontHeightItem::ScaleMetrics( tools::Long nMult, tools::Long nDiv )
+{
+ nHeight = static_cast<sal_uInt32>(BigInt::Scale( nHeight, nMult, nDiv ));
+}
+
+
+bool SvxFontHeightItem::HasMetrics() const
+{
+ return true;
+}
+
+void SvxFontHeightItem::SetHeight( sal_uInt32 nNewHeight, const sal_uInt16 nNewProp,
+ MapUnit eUnit )
+{
+ DBG_ASSERT( GetRefCount() == 0, "SetValue() with pooled item" );
+
+ if( MapUnit::MapRelative != eUnit )
+ nHeight = nNewHeight + ::ItemToControl( short(nNewProp), eUnit,
+ FieldUnit::TWIP );
+ else if( 100 != nNewProp )
+ nHeight = sal_uInt32(( nNewHeight * nNewProp ) / 100 );
+ else
+ nHeight = nNewHeight;
+
+ nProp = nNewProp;
+ ePropUnit = eUnit;
+}
+
+void SvxFontHeightItem::SetHeight( sal_uInt32 nNewHeight, sal_uInt16 nNewProp,
+ MapUnit eMetric, MapUnit eCoreMetric )
+{
+ DBG_ASSERT( GetRefCount() == 0, "SetValue() with pooled item" );
+
+ if( MapUnit::MapRelative != eMetric )
+ nHeight = nNewHeight +
+ ::ControlToItem( ::ItemToControl(static_cast<short>(nNewProp), eMetric,
+ FieldUnit::TWIP ), FieldUnit::TWIP,
+ eCoreMetric );
+ else if( 100 != nNewProp )
+ nHeight = sal_uInt32(( nNewHeight * nNewProp ) / 100 );
+ else
+ nHeight = nNewHeight;
+
+ nProp = nNewProp;
+ ePropUnit = eMetric;
+}
+
+void SvxFontHeightItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxFontHeightItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("height"), BAD_CAST(OString::number(nHeight).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("prop"), BAD_CAST(OString::number(nProp).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("propUnit"), BAD_CAST(OString::number(static_cast<int>(ePropUnit)).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// class SvxTextLineItem ------------------------------------------------
+
+SvxTextLineItem::SvxTextLineItem( const FontLineStyle eSt, const sal_uInt16 nId )
+ : SfxEnumItem(nId, eSt)
+ , maColor(COL_TRANSPARENT)
+{
+}
+
+
+bool SvxTextLineItem::HasBoolValue() const
+{
+ return true;
+}
+
+
+bool SvxTextLineItem::GetBoolValue() const
+{
+ return GetValue() != LINESTYLE_NONE;
+}
+
+
+void SvxTextLineItem::SetBoolValue( bool bVal )
+{
+ SetValue( bVal ? LINESTYLE_SINGLE : LINESTYLE_NONE );
+}
+
+SvxTextLineItem* SvxTextLineItem::Clone( SfxItemPool * ) const
+{
+ return new SvxTextLineItem( *this );
+}
+
+sal_uInt16 SvxTextLineItem::GetValueCount() const
+{
+ return LINESTYLE_DOTTED + 1; // LINESTYLE_NONE also belongs here
+}
+
+
+bool SvxTextLineItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper& /*rIntl*/
+) const
+{
+ rText = GetValueTextByPos( GetValue() );
+ if( !maColor.IsTransparent() )
+ rText += cpDelim + ::GetColorString(maColor);
+ return true;
+}
+
+
+OUString SvxTextLineItem::GetValueTextByPos( sal_uInt16 /*nPos*/ ) const
+{
+ OSL_FAIL("SvxTextLineItem::GetValueTextByPos: Pure virtual method");
+ return OUString();
+}
+
+bool SvxTextLineItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch(nMemberId)
+ {
+ case MID_TEXTLINED:
+ rVal <<= GetBoolValue();
+ break;
+ case MID_TL_STYLE:
+ rVal <<= static_cast<sal_Int16>(GetValue());
+ break;
+ case MID_TL_COLOR:
+ rVal <<= maColor;
+ break;
+ case MID_TL_COMPLEX_COLOR:
+ {
+ auto xComplexColor = model::color::createXComplexColor(maComplexColor);
+ rVal <<= xComplexColor;
+ break;
+ }
+ case MID_TL_HASCOLOR:
+ rVal <<= maColor.GetAlpha() == 255;
+ break;
+ }
+ return true;
+}
+
+bool SvxTextLineItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch(nMemberId)
+ {
+ case MID_TEXTLINED:
+ SetBoolValue(Any2Bool(rVal));
+ break;
+ case MID_TL_STYLE:
+ {
+ sal_Int32 nValue = 0;
+ if(!(rVal >>= nValue))
+ bRet = false;
+ else
+ SetValue(static_cast<FontLineStyle>(nValue));
+ }
+ break;
+ case MID_TL_COLOR:
+ {
+ Color nCol;
+ if( !( rVal >>= nCol ) )
+ bRet = false;
+ else
+ {
+ // Keep transparence, because it contains the information
+ // whether the font color or the stored color should be used
+ sal_uInt8 nAlpha = maColor.GetAlpha();
+ maColor = nCol;
+ maColor.SetAlpha( nAlpha );
+ }
+ }
+ break;
+ case MID_TL_COMPLEX_COLOR:
+ {
+ css::uno::Reference<css::util::XComplexColor> xComplexColor;
+ if (!(rVal >>= xComplexColor))
+ return false;
+
+ if (xComplexColor.is())
+ maComplexColor = model::color::getFromXComplexColor(xComplexColor);
+ }
+ break;
+ case MID_TL_HASCOLOR:
+ maColor.SetAlpha( Any2Bool( rVal ) ? 255 : 0 );
+ break;
+ }
+ return bRet;
+}
+
+bool SvxTextLineItem::operator==( const SfxPoolItem& rItem ) const
+{
+ return SfxEnumItem::operator==( rItem ) &&
+ maColor == static_cast<const SvxTextLineItem&>(rItem).maColor &&
+ maComplexColor == static_cast<const SvxTextLineItem&>(rItem).maComplexColor;
+}
+
+// class SvxUnderlineItem ------------------------------------------------
+
+
+SvxUnderlineItem::SvxUnderlineItem( const FontLineStyle eSt, const sal_uInt16 nId )
+ : SvxTextLineItem( eSt, nId )
+{
+}
+
+SvxUnderlineItem* SvxUnderlineItem::Clone( SfxItemPool * ) const
+{
+ return new SvxUnderlineItem( *this );
+}
+
+OUString SvxUnderlineItem::GetValueTextByPos( sal_uInt16 nPos ) const
+{
+ static TranslateId RID_SVXITEMS_UL[] =
+ {
+ RID_SVXITEMS_UL_NONE,
+ RID_SVXITEMS_UL_SINGLE,
+ RID_SVXITEMS_UL_DOUBLE,
+ RID_SVXITEMS_UL_DOTTED,
+ RID_SVXITEMS_UL_DONTKNOW,
+ RID_SVXITEMS_UL_DASH,
+ RID_SVXITEMS_UL_LONGDASH,
+ RID_SVXITEMS_UL_DASHDOT,
+ RID_SVXITEMS_UL_DASHDOTDOT,
+ RID_SVXITEMS_UL_SMALLWAVE,
+ RID_SVXITEMS_UL_WAVE,
+ RID_SVXITEMS_UL_DOUBLEWAVE,
+ RID_SVXITEMS_UL_BOLD,
+ RID_SVXITEMS_UL_BOLDDOTTED,
+ RID_SVXITEMS_UL_BOLDDASH,
+ RID_SVXITEMS_UL_BOLDLONGDASH,
+ RID_SVXITEMS_UL_BOLDDASHDOT,
+ RID_SVXITEMS_UL_BOLDDASHDOTDOT,
+ RID_SVXITEMS_UL_BOLDWAVE
+ };
+ static_assert(std::size(RID_SVXITEMS_UL) - 1 == LINESTYLE_BOLDWAVE, "must match");
+ assert(nPos <= sal_uInt16(LINESTYLE_BOLDWAVE) && "enum overflow!");
+ return EditResId(RID_SVXITEMS_UL[nPos]);
+}
+
+// class SvxOverlineItem ------------------------------------------------
+
+SvxOverlineItem::SvxOverlineItem( const FontLineStyle eSt, const sal_uInt16 nId )
+ : SvxTextLineItem( eSt, nId )
+{
+}
+
+SvxOverlineItem* SvxOverlineItem::Clone( SfxItemPool * ) const
+{
+ return new SvxOverlineItem( *this );
+}
+
+OUString SvxOverlineItem::GetValueTextByPos( sal_uInt16 nPos ) const
+{
+ static TranslateId RID_SVXITEMS_OL[] =
+ {
+ RID_SVXITEMS_OL_NONE,
+ RID_SVXITEMS_OL_SINGLE,
+ RID_SVXITEMS_OL_DOUBLE,
+ RID_SVXITEMS_OL_DOTTED,
+ RID_SVXITEMS_OL_DONTKNOW,
+ RID_SVXITEMS_OL_DASH,
+ RID_SVXITEMS_OL_LONGDASH,
+ RID_SVXITEMS_OL_DASHDOT,
+ RID_SVXITEMS_OL_DASHDOTDOT,
+ RID_SVXITEMS_OL_SMALLWAVE,
+ RID_SVXITEMS_OL_WAVE,
+ RID_SVXITEMS_OL_DOUBLEWAVE,
+ RID_SVXITEMS_OL_BOLD,
+ RID_SVXITEMS_OL_BOLDDOTTED,
+ RID_SVXITEMS_OL_BOLDDASH,
+ RID_SVXITEMS_OL_BOLDLONGDASH,
+ RID_SVXITEMS_OL_BOLDDASHDOT,
+ RID_SVXITEMS_OL_BOLDDASHDOTDOT,
+ RID_SVXITEMS_OL_BOLDWAVE
+ };
+ static_assert(std::size(RID_SVXITEMS_OL) - 1 == LINESTYLE_BOLDWAVE, "must match");
+ assert(nPos <= sal_uInt16(LINESTYLE_BOLDWAVE) && "enum overflow!");
+ return EditResId(RID_SVXITEMS_OL[nPos]);
+}
+
+// class SvxCrossedOutItem -----------------------------------------------
+
+SvxCrossedOutItem::SvxCrossedOutItem( const FontStrikeout eSt, const sal_uInt16 nId )
+ : SfxEnumItem( nId, eSt )
+{
+}
+
+
+bool SvxCrossedOutItem::HasBoolValue() const
+{
+ return true;
+}
+
+
+bool SvxCrossedOutItem::GetBoolValue() const
+{
+ return GetValue() != STRIKEOUT_NONE;
+}
+
+
+void SvxCrossedOutItem::SetBoolValue( bool bVal )
+{
+ SetValue( bVal ? STRIKEOUT_SINGLE : STRIKEOUT_NONE );
+}
+
+
+sal_uInt16 SvxCrossedOutItem::GetValueCount() const
+{
+ return STRIKEOUT_DOUBLE + 1; // STRIKEOUT_NONE belongs also here
+}
+
+SvxCrossedOutItem* SvxCrossedOutItem::Clone( SfxItemPool * ) const
+{
+ return new SvxCrossedOutItem( *this );
+}
+
+bool SvxCrossedOutItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper& /*rIntl*/
+) const
+{
+ rText = GetValueTextByPos( GetValue() );
+ return true;
+}
+
+OUString SvxCrossedOutItem::GetValueTextByPos( sal_uInt16 nPos )
+{
+ static TranslateId RID_SVXITEMS_STRIKEOUT[] =
+ {
+ RID_SVXITEMS_STRIKEOUT_NONE,
+ RID_SVXITEMS_STRIKEOUT_SINGLE,
+ RID_SVXITEMS_STRIKEOUT_DOUBLE,
+ RID_SVXITEMS_STRIKEOUT_DONTKNOW,
+ RID_SVXITEMS_STRIKEOUT_BOLD,
+ RID_SVXITEMS_STRIKEOUT_SLASH,
+ RID_SVXITEMS_STRIKEOUT_X
+ };
+ static_assert(std::size(RID_SVXITEMS_STRIKEOUT) - 1 == STRIKEOUT_X, "must match");
+ assert(nPos <= sal_uInt16(STRIKEOUT_X) && "enum overflow!");
+ return EditResId(RID_SVXITEMS_STRIKEOUT[nPos]);
+}
+
+bool SvxCrossedOutItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch(nMemberId)
+ {
+ case MID_CROSSED_OUT:
+ rVal <<= GetBoolValue();
+ break;
+ case MID_CROSS_OUT:
+ rVal <<= static_cast<sal_Int16>(GetValue());
+ break;
+ }
+ return true;
+}
+
+bool SvxCrossedOutItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch(nMemberId)
+ {
+ case MID_CROSSED_OUT:
+ SetBoolValue(Any2Bool(rVal));
+ break;
+ case MID_CROSS_OUT:
+ {
+ sal_Int32 nValue = 0;
+ if(!(rVal >>= nValue))
+ return false;
+ SetValue(static_cast<FontStrikeout>(nValue));
+ }
+ break;
+ }
+ return true;
+}
+// class SvxShadowedItem -------------------------------------------------
+
+SvxShadowedItem::SvxShadowedItem( const bool bShadowed, const sal_uInt16 nId ) :
+ SfxBoolItem( nId, bShadowed )
+{
+}
+
+SvxShadowedItem* SvxShadowedItem::Clone( SfxItemPool * ) const
+{
+ return new SvxShadowedItem( *this );
+}
+
+bool SvxShadowedItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper& /*rIntl*/
+) const
+{
+ TranslateId pId = RID_SVXITEMS_SHADOWED_FALSE;
+
+ if ( GetValue() )
+ pId = RID_SVXITEMS_SHADOWED_TRUE;
+ rText = EditResId(pId);
+ return true;
+}
+
+// class SvxAutoKernItem -------------------------------------------------
+
+SvxAutoKernItem::SvxAutoKernItem( const bool bAutoKern, const sal_uInt16 nId ) :
+ SfxBoolItem( nId, bAutoKern )
+{
+}
+
+SvxAutoKernItem* SvxAutoKernItem::Clone( SfxItemPool * ) const
+{
+ return new SvxAutoKernItem( *this );
+}
+
+bool SvxAutoKernItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper& /*rIntl*/
+) const
+{
+ TranslateId pId = RID_SVXITEMS_AUTOKERN_FALSE;
+
+ if ( GetValue() )
+ pId = RID_SVXITEMS_AUTOKERN_TRUE;
+ rText = EditResId(pId);
+ return true;
+}
+
+// class SvxWordLineModeItem ---------------------------------------------
+
+SvxWordLineModeItem::SvxWordLineModeItem( const bool bWordLineMode,
+ const sal_uInt16 nId ) :
+ SfxBoolItem( nId, bWordLineMode )
+{
+}
+
+SvxWordLineModeItem* SvxWordLineModeItem::Clone( SfxItemPool * ) const
+{
+ return new SvxWordLineModeItem( *this );
+}
+
+bool SvxWordLineModeItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper& /*rIntl*/
+) const
+{
+ TranslateId pId = RID_SVXITEMS_WORDLINE_FALSE;
+
+ if ( GetValue() )
+ pId = RID_SVXITEMS_WORDLINE_TRUE;
+ rText = EditResId(pId);
+ return true;
+}
+
+// class SvxContourItem --------------------------------------------------
+
+SvxContourItem::SvxContourItem( const bool bContoured, const sal_uInt16 nId ) :
+ SfxBoolItem( nId, bContoured )
+{
+}
+
+SvxContourItem* SvxContourItem::Clone( SfxItemPool * ) const
+{
+ return new SvxContourItem( *this );
+}
+
+bool SvxContourItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper& /*rIntl*/
+) const
+{
+ TranslateId pId = RID_SVXITEMS_CONTOUR_FALSE;
+
+ if ( GetValue() )
+ pId = RID_SVXITEMS_CONTOUR_TRUE;
+ rText = EditResId(pId);
+ return true;
+}
+
+// class SvxColorItem ----------------------------------------------------
+SvxColorItem::SvxColorItem( const sal_uInt16 nId ) :
+ SfxPoolItem(nId),
+ mColor( COL_BLACK )
+{
+}
+
+SvxColorItem::SvxColorItem( const Color& rCol, const sal_uInt16 nId ) :
+ SfxPoolItem( nId ),
+ mColor( rCol )
+{
+}
+
+SvxColorItem::SvxColorItem(Color const& rColor, model::ComplexColor const& rComplexColor, const sal_uInt16 nId)
+ : SfxPoolItem(nId)
+ , mColor(rColor)
+ , maComplexColor(rComplexColor)
+{
+}
+
+SvxColorItem::~SvxColorItem()
+{
+}
+
+bool SvxColorItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ const SvxColorItem& rColorItem = static_cast<const SvxColorItem&>(rAttr);
+
+ return mColor == rColorItem.mColor &&
+ maComplexColor == rColorItem.maComplexColor;
+}
+
+bool SvxColorItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch (nMemberId)
+ {
+ case MID_COLOR_ALPHA:
+ {
+ auto fTransparency = static_cast<double>(255 - mColor.GetAlpha()) * 100 / 255;
+ rVal <<= static_cast<sal_Int16>(basegfx::fround(fTransparency));
+ break;
+ }
+ case MID_GRAPHIC_TRANSPARENT:
+ {
+ rVal <<= mColor.GetAlpha() == 0;
+ break;
+ }
+ case MID_COLOR_THEME_INDEX:
+ {
+ rVal <<= sal_Int16(maComplexColor.getThemeColorType());
+ break;
+ }
+ case MID_COLOR_TINT_OR_SHADE:
+ {
+ sal_Int16 nValue = 0;
+ for (auto const& rTransform : maComplexColor.getTransformations())
+ {
+ if (rTransform.meType == model::TransformationType::Tint)
+ nValue = rTransform.mnValue;
+ else if (rTransform.meType == model::TransformationType::Shade)
+ nValue = -rTransform.mnValue;
+ }
+ rVal <<= nValue;
+ break;
+ }
+ case MID_COLOR_LUM_MOD:
+ {
+ sal_Int16 nValue = 10000;
+ for (auto const& rTransform : maComplexColor.getTransformations())
+ {
+ if (rTransform.meType == model::TransformationType::LumMod)
+ nValue = rTransform.mnValue;
+ }
+ rVal <<= nValue;
+ break;
+ }
+ case MID_COLOR_LUM_OFF:
+ {
+ sal_Int16 nValue = 0;
+ for (auto const& rTransform : maComplexColor.getTransformations())
+ {
+ if (rTransform.meType == model::TransformationType::LumOff)
+ nValue = rTransform.mnValue;
+ }
+ rVal <<= nValue;
+ break;
+ }
+ case MID_COMPLEX_COLOR_JSON:
+ {
+ rVal <<= OStringToOUString(model::color::convertToJSON(maComplexColor), RTL_TEXTENCODING_UTF8);
+ break;
+ }
+ case MID_COMPLEX_COLOR:
+ {
+ auto xComplexColor = model::color::createXComplexColor(maComplexColor);
+ rVal <<= xComplexColor;
+ break;
+ }
+ case MID_COLOR_RGB:
+ default:
+ {
+ rVal <<= mColor;
+ break;
+ }
+ }
+ return true;
+}
+
+bool SvxColorItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch(nMemberId)
+ {
+ case MID_COLOR_ALPHA:
+ {
+ sal_Int16 nTransparency = 0;
+ bool bRet = rVal >>= nTransparency;
+ if (bRet)
+ {
+ auto fTransparency = static_cast<double>(nTransparency) * 255 / 100;
+ mColor.SetAlpha(255 - static_cast<sal_uInt8>(basegfx::fround(fTransparency)));
+ }
+ return bRet;
+ }
+ case MID_GRAPHIC_TRANSPARENT:
+ {
+ mColor.SetAlpha( Any2Bool( rVal ) ? 0 : 255 );
+ return true;
+ }
+ case MID_COLOR_THEME_INDEX:
+ {
+ sal_Int16 nIndex = -1;
+ if (!(rVal >>= nIndex))
+ return false;
+ maComplexColor.setThemeColor(model::convertToThemeColorType(nIndex));
+ }
+ break;
+ case MID_COLOR_TINT_OR_SHADE:
+ {
+ sal_Int16 nTintShade = 0;
+ if (!(rVal >>= nTintShade))
+ return false;
+
+ maComplexColor.removeTransformations(model::TransformationType::Tint);
+ maComplexColor.removeTransformations(model::TransformationType::Shade);
+
+ if (nTintShade > 0)
+ maComplexColor.addTransformation({model::TransformationType::Tint, nTintShade});
+ else if (nTintShade < 0)
+ {
+ sal_Int16 nShade = o3tl::narrowing<sal_Int16>(-nTintShade);
+ maComplexColor.addTransformation({model::TransformationType::Shade, nShade});
+ }
+ }
+ break;
+ case MID_COLOR_LUM_MOD:
+ {
+ sal_Int16 nLumMod = 10000;
+ if (!(rVal >>= nLumMod))
+ return false;
+ maComplexColor.removeTransformations(model::TransformationType::LumMod);
+ maComplexColor.addTransformation({model::TransformationType::LumMod, nLumMod});
+ }
+ break;
+ case MID_COLOR_LUM_OFF:
+ {
+ sal_Int16 nLumOff = 0;
+ if (!(rVal >>= nLumOff))
+ return false;
+ maComplexColor.removeTransformations(model::TransformationType::LumOff);
+ maComplexColor.addTransformation({model::TransformationType::LumOff, nLumOff});
+ }
+ break;
+ case MID_COMPLEX_COLOR_JSON:
+ {
+ OUString sComplexColorJson;
+ if (!(rVal >>= sComplexColorJson))
+ return false;
+
+ if (sComplexColorJson.isEmpty())
+ return false;
+
+ OString aJSON = OUStringToOString(sComplexColorJson, RTL_TEXTENCODING_ASCII_US);
+ if (!model::color::convertFromJSON(aJSON, maComplexColor))
+ return false;
+ }
+ break;
+ case MID_COMPLEX_COLOR:
+ {
+ css::uno::Reference<css::util::XComplexColor> xComplexColor;
+ if (!(rVal >>= xComplexColor))
+ return false;
+
+ if (xComplexColor.is())
+ maComplexColor = model::color::getFromXComplexColor(xComplexColor);
+ }
+ break;
+ case MID_COLOR_RGB:
+ default:
+ {
+ if (!(rVal >>= mColor))
+ return false;
+ }
+ break;
+ }
+ return true;
+}
+
+SvxColorItem* SvxColorItem::Clone( SfxItemPool * ) const
+{
+ return new SvxColorItem( *this );
+}
+
+bool SvxColorItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper& /*rIntl*/
+) const
+{
+ rText = ::GetColorString( mColor );
+ return true;
+}
+
+void SvxColorItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxColorItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+
+ std::stringstream ss;
+ ss << mColor;
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(ss.str().c_str()));
+
+ OUString aStr;
+ IntlWrapper aIntlWrapper(SvtSysLocale().GetUILanguageTag());
+ GetPresentation( SfxItemPresentation::Complete, MapUnit::Map100thMM, MapUnit::Map100thMM, aStr, aIntlWrapper);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("presentation"), BAD_CAST(OUStringToOString(aStr, RTL_TEXTENCODING_UTF8).getStr()));
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("complex-color"));
+
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("type"),
+ BAD_CAST(OString::number(sal_Int16(maComplexColor.getType())).getStr()));
+
+ for (auto const& rTransform : maComplexColor.getTransformations())
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("transformation"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("type"),
+ BAD_CAST(OString::number(sal_Int16(rTransform.meType)).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::number(rTransform.mnValue).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// class SvxKerningItem --------------------------------------------------
+
+SvxKerningItem::SvxKerningItem( const short nKern, const sal_uInt16 nId ) :
+ SfxInt16Item( nId, nKern )
+{
+}
+
+SvxKerningItem* SvxKerningItem::Clone( SfxItemPool * ) const
+{
+ return new SvxKerningItem( *this );
+}
+
+void SvxKerningItem::ScaleMetrics( tools::Long nMult, tools::Long nDiv )
+{
+ SetValue( static_cast<sal_Int16>(BigInt::Scale( GetValue(), nMult, nDiv )) );
+}
+
+
+bool SvxKerningItem::HasMetrics() const
+{
+ return true;
+}
+
+
+bool SvxKerningItem::GetPresentation
+(
+ SfxItemPresentation ePres,
+ MapUnit eCoreUnit,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper& rIntl
+) const
+{
+ switch ( ePres )
+ {
+ case SfxItemPresentation::Nameless:
+ rText = GetMetricText( static_cast<tools::Long>(GetValue()), eCoreUnit, MapUnit::MapPoint, &rIntl ) +
+ " " + EditResId(GetMetricId(MapUnit::MapPoint));
+ return true;
+ case SfxItemPresentation::Complete:
+ {
+ rText = EditResId(RID_SVXITEMS_KERNING_COMPLETE);
+ TranslateId pId;
+
+ if ( GetValue() > 0 )
+ pId = RID_SVXITEMS_KERNING_EXPANDED;
+ else if ( GetValue() < 0 )
+ pId = RID_SVXITEMS_KERNING_CONDENSED;
+
+ if (pId)
+ rText += EditResId(pId);
+ rText += GetMetricText( static_cast<tools::Long>(GetValue()), eCoreUnit, MapUnit::MapPoint, &rIntl ) +
+ " " + EditResId(GetMetricId(MapUnit::MapPoint));
+ return true;
+ }
+ default: ; //prevent warning
+ }
+ return false;
+}
+
+bool SvxKerningItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ sal_Int16 nVal = GetValue();
+ if(nMemberId & CONVERT_TWIPS)
+ nVal = static_cast<sal_Int16>(convertTwipToMm100(nVal));
+ rVal <<= nVal;
+ return true;
+}
+
+bool SvxKerningItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId)
+{
+ sal_Int16 nVal = sal_Int16();
+ if(!(rVal >>= nVal))
+ return false;
+ if(nMemberId & CONVERT_TWIPS)
+ nVal = o3tl::toTwips(nVal, o3tl::Length::mm100);
+ SetValue(nVal);
+ return true;
+}
+
+// class SvxCaseMapItem --------------------------------------------------
+
+SvxCaseMapItem::SvxCaseMapItem( const SvxCaseMap eMap, const sal_uInt16 nId ) :
+ SfxEnumItem( nId, eMap )
+{
+}
+
+sal_uInt16 SvxCaseMapItem::GetValueCount() const
+{
+ return sal_uInt16(SvxCaseMap::End); // SvxCaseMap::SmallCaps + 1
+}
+
+SvxCaseMapItem* SvxCaseMapItem::Clone( SfxItemPool * ) const
+{
+ return new SvxCaseMapItem( *this );
+}
+
+bool SvxCaseMapItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper& /*rIntl*/
+) const
+{
+ rText = GetValueTextByPos( static_cast<sal_uInt16>(GetValue()) );
+ return true;
+}
+
+OUString SvxCaseMapItem::GetValueTextByPos( sal_uInt16 nPos )
+{
+ static TranslateId RID_SVXITEMS_CASEMAP[] =
+ {
+ RID_SVXITEMS_CASEMAP_NONE,
+ RID_SVXITEMS_CASEMAP_UPPERCASE,
+ RID_SVXITEMS_CASEMAP_LOWERCASE,
+ RID_SVXITEMS_CASEMAP_TITLE,
+ RID_SVXITEMS_CASEMAP_SMALLCAPS
+ };
+
+ static_assert(std::size(RID_SVXITEMS_CASEMAP) == size_t(SvxCaseMap::End), "must match");
+ assert(nPos < sal_uInt16(SvxCaseMap::End) && "enum overflow!");
+ return EditResId(RID_SVXITEMS_CASEMAP[nPos]);
+}
+
+bool SvxCaseMapItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) const
+{
+ sal_Int16 nRet = style::CaseMap::NONE;
+ switch( GetValue() )
+ {
+ case SvxCaseMap::Uppercase : nRet = style::CaseMap::UPPERCASE; break;
+ case SvxCaseMap::Lowercase : nRet = style::CaseMap::LOWERCASE; break;
+ case SvxCaseMap::Capitalize : nRet = style::CaseMap::TITLE ; break;
+ case SvxCaseMap::SmallCaps: nRet = style::CaseMap::SMALLCAPS; break;
+ default: break;
+ }
+ rVal <<= nRet;
+ return true;
+}
+
+bool SvxCaseMapItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/ )
+{
+ sal_uInt16 nVal = sal_uInt16();
+ if(!(rVal >>= nVal))
+ return false;
+
+ SvxCaseMap eVal;
+ switch( nVal )
+ {
+ case style::CaseMap::NONE : eVal = SvxCaseMap::NotMapped; break;
+ case style::CaseMap::UPPERCASE: eVal = SvxCaseMap::Uppercase; break;
+ case style::CaseMap::LOWERCASE: eVal = SvxCaseMap::Lowercase; break;
+ case style::CaseMap::TITLE : eVal = SvxCaseMap::Capitalize; break;
+ case style::CaseMap::SMALLCAPS: eVal = SvxCaseMap::SmallCaps; break;
+ default: return false;
+ }
+ SetValue(eVal);
+ return true;
+}
+
+// class SvxEscapementItem -----------------------------------------------
+
+SvxEscapementItem::SvxEscapementItem( const sal_uInt16 nId ) :
+ SfxEnumItemInterface( nId ),
+
+ nEsc ( 0 ),
+ nProp ( 100 )
+{
+}
+
+
+SvxEscapementItem::SvxEscapementItem( const SvxEscapement eEscape,
+ const sal_uInt16 nId ) :
+ SfxEnumItemInterface( nId ),
+ nProp( 100 )
+{
+ SetEscapement( eEscape );
+ if( nEsc )
+ nProp = DFLT_ESC_PROP;
+}
+
+
+SvxEscapementItem::SvxEscapementItem( const short _nEsc,
+ const sal_uInt8 _nProp,
+ const sal_uInt16 nId ) :
+ SfxEnumItemInterface( nId ),
+ nEsc ( _nEsc ),
+ nProp ( _nProp )
+{
+}
+
+
+bool SvxEscapementItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ return( nEsc == static_cast<const SvxEscapementItem&>(rAttr).nEsc &&
+ nProp == static_cast<const SvxEscapementItem&>(rAttr).nProp );
+}
+
+SvxEscapementItem* SvxEscapementItem::Clone( SfxItemPool * ) const
+{
+ return new SvxEscapementItem( *this );
+}
+
+sal_uInt16 SvxEscapementItem::GetValueCount() const
+{
+ return sal_uInt16(SvxEscapement::End); // SvxEscapement::Subscript + 1
+}
+
+
+bool SvxEscapementItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper& /*rIntl*/
+) const
+{
+ rText = GetValueTextByPos( GetEnumValue() );
+
+ if ( nEsc != 0 )
+ {
+ if( DFLT_ESC_AUTO_SUPER == nEsc || DFLT_ESC_AUTO_SUB == nEsc )
+ rText += EditResId(RID_SVXITEMS_ESCAPEMENT_AUTO);
+ else
+ rText += OUString::number( nEsc ) + "%";
+ }
+ return true;
+}
+
+OUString SvxEscapementItem::GetValueTextByPos( sal_uInt16 nPos )
+{
+ static TranslateId RID_SVXITEMS_ESCAPEMENT[] =
+ {
+ RID_SVXITEMS_ESCAPEMENT_OFF,
+ RID_SVXITEMS_ESCAPEMENT_SUPER,
+ RID_SVXITEMS_ESCAPEMENT_SUB
+ };
+
+ static_assert(std::size(RID_SVXITEMS_ESCAPEMENT) == size_t(SvxEscapement::End), "must match");
+ assert(nPos < sal_uInt16(SvxEscapement::End) && "enum overflow!");
+ return EditResId(RID_SVXITEMS_ESCAPEMENT[nPos]);
+}
+
+sal_uInt16 SvxEscapementItem::GetEnumValue() const
+{
+ if ( nEsc < 0 )
+ return sal_uInt16(SvxEscapement::Subscript);
+ else if ( nEsc > 0 )
+ return sal_uInt16(SvxEscapement::Superscript);
+ return sal_uInt16(SvxEscapement::Off);
+}
+
+
+void SvxEscapementItem::SetEnumValue( sal_uInt16 nVal )
+{
+ SetEscapement( static_cast<SvxEscapement>(nVal) );
+}
+
+bool SvxEscapementItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch(nMemberId)
+ {
+ case MID_ESC:
+ rVal <<= static_cast<sal_Int16>(nEsc);
+ break;
+ case MID_ESC_HEIGHT:
+ rVal <<= static_cast<sal_Int8>(nProp);
+ break;
+ case MID_AUTO_ESC:
+ rVal <<= (DFLT_ESC_AUTO_SUB == nEsc || DFLT_ESC_AUTO_SUPER == nEsc);
+ break;
+ }
+ return true;
+}
+
+bool SvxEscapementItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch(nMemberId)
+ {
+ case MID_ESC:
+ {
+ sal_Int16 nVal = sal_Int16();
+ if( (rVal >>= nVal) && (std::abs(nVal) <= MAX_ESC_POS+1))
+ nEsc = nVal;
+ else
+ return false;
+ }
+ break;
+ case MID_ESC_HEIGHT:
+ {
+ sal_Int8 nVal = sal_Int8();
+ if( (rVal >>= nVal) && (nVal <= 100))
+ nProp = nVal;
+ else
+ return false;
+ }
+ break;
+ case MID_AUTO_ESC:
+ {
+ bool bVal = Any2Bool(rVal);
+ if(bVal)
+ {
+ if(nEsc < 0)
+ nEsc = DFLT_ESC_AUTO_SUB;
+ else
+ nEsc = DFLT_ESC_AUTO_SUPER;
+ }
+ else
+ if(DFLT_ESC_AUTO_SUPER == nEsc )
+ --nEsc;
+ else if(DFLT_ESC_AUTO_SUB == nEsc)
+ ++nEsc;
+ }
+ break;
+ }
+ return true;
+}
+
+// class SvxLanguageItem -------------------------------------------------
+
+SvxLanguageItem::SvxLanguageItem( const LanguageType eLang, const sal_uInt16 nId )
+ : SvxLanguageItem_Base( nId , eLang )
+{
+}
+
+
+sal_uInt16 SvxLanguageItem::GetValueCount() const
+{
+ // #i50205# got rid of class International
+ SAL_WARN( "editeng.items", "SvxLanguageItem::GetValueCount: supposed to return a count of what?");
+ // Could be SvtLanguageTable::GetEntryCount() (all locales with resource string)?
+ // Could be LocaleDataWrapper::getInstalledLanguageTypes() (all locales with locale data)?
+ return 0;
+}
+
+SvxLanguageItem* SvxLanguageItem::Clone( SfxItemPool * ) const
+{
+ return new SvxLanguageItem( *this );
+}
+
+bool SvxLanguageItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper& /*rIntl*/
+) const
+{
+ rText = SvtLanguageTable::GetLanguageString( GetValue() );
+ return true;
+}
+
+bool SvxLanguageItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch(nMemberId)
+ {
+ case MID_LANG_INT: // for basic conversions!
+ rVal <<= static_cast<sal_Int16>(static_cast<sal_uInt16>(GetValue()));
+ break;
+ case MID_LANG_LOCALE:
+ lang::Locale aRet( LanguageTag::convertToLocale( GetValue(), false));
+ rVal <<= aRet;
+ break;
+ }
+ return true;
+}
+
+bool SvxLanguageItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch(nMemberId)
+ {
+ case MID_LANG_INT: // for basic conversions!
+ {
+ sal_Int32 nValue = 0;
+ if(!(rVal >>= nValue))
+ return false;
+
+ SetValue(LanguageType(nValue));
+ }
+ break;
+ case MID_LANG_LOCALE:
+ {
+ lang::Locale aLocale;
+ if(!(rVal >>= aLocale))
+ return false;
+
+ SetValue( LanguageTag::convertToLanguageType( aLocale, false));
+ }
+ break;
+ }
+ return true;
+}
+
+// class SvxNoHyphenItem -------------------------------------------------
+
+SvxNoHyphenItem::SvxNoHyphenItem( const sal_uInt16 nId ) :
+ SfxBoolItem( nId , true )
+{
+}
+
+SvxNoHyphenItem* SvxNoHyphenItem::Clone( SfxItemPool* ) const
+{
+ return new SvxNoHyphenItem( *this );
+}
+
+bool SvxNoHyphenItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper& /*rIntl*/
+) const
+{
+ rText.clear();
+ return false;
+}
+
+/*
+ * Dummy item for ToolBox controls:
+ *
+ */
+
+
+// class SvxBlinkItem -------------------------------------------------
+
+
+SvxBlinkItem::SvxBlinkItem( const bool bBlink, const sal_uInt16 nId ) :
+ SfxBoolItem( nId, bBlink )
+{
+}
+
+SvxBlinkItem* SvxBlinkItem::Clone( SfxItemPool * ) const
+{
+ return new SvxBlinkItem( *this );
+}
+
+bool SvxBlinkItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper& /*rIntl*/
+) const
+{
+ TranslateId pId = RID_SVXITEMS_BLINK_FALSE;
+
+ if ( GetValue() )
+ pId = RID_SVXITEMS_BLINK_TRUE;
+ rText = EditResId(pId);
+ return true;
+}
+
+// class SvxEmphaisMarkItem ---------------------------------------------------
+
+SvxEmphasisMarkItem::SvxEmphasisMarkItem( const FontEmphasisMark nValue,
+ TypedWhichId<SvxEmphasisMarkItem> nId )
+ : SfxUInt16Item( nId, static_cast<sal_uInt16>(nValue) )
+{
+}
+
+SvxEmphasisMarkItem* SvxEmphasisMarkItem::Clone( SfxItemPool * ) const
+{
+ return new SvxEmphasisMarkItem( *this );
+}
+
+bool SvxEmphasisMarkItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText,
+ const IntlWrapper& /*rIntl*/
+) const
+{
+ static TranslateId RID_SVXITEMS_EMPHASIS[] =
+ {
+ RID_SVXITEMS_EMPHASIS_NONE_STYLE,
+ RID_SVXITEMS_EMPHASIS_DOT_STYLE,
+ RID_SVXITEMS_EMPHASIS_CIRCLE_STYLE,
+ RID_SVXITEMS_EMPHASIS_DISC_STYLE,
+ RID_SVXITEMS_EMPHASIS_ACCENT_STYLE
+ };
+
+ FontEmphasisMark nVal = GetEmphasisMark();
+ rText = EditResId(RID_SVXITEMS_EMPHASIS[
+ static_cast<sal_uInt16>(static_cast<FontEmphasisMark>( nVal & FontEmphasisMark::Style ))]);
+ TranslateId pId = ( FontEmphasisMark::PosAbove & nVal )
+ ? RID_SVXITEMS_EMPHASIS_ABOVE_POS
+ : ( FontEmphasisMark::PosBelow & nVal )
+ ? RID_SVXITEMS_EMPHASIS_BELOW_POS
+ : TranslateId();
+ if( pId )
+ rText += EditResId( pId );
+ return true;
+}
+
+bool SvxEmphasisMarkItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch( nMemberId )
+ {
+ case MID_EMPHASIS:
+ {
+ FontEmphasisMark nValue = GetEmphasisMark();
+ sal_Int16 nRet = 0;
+ switch(nValue & FontEmphasisMark::Style)
+ {
+ case FontEmphasisMark::NONE : nRet = FontEmphasis::NONE; break;
+ case FontEmphasisMark::Dot : nRet = FontEmphasis::DOT_ABOVE; break;
+ case FontEmphasisMark::Circle : nRet = FontEmphasis::CIRCLE_ABOVE; break;
+ case FontEmphasisMark::Disc : nRet = FontEmphasis::DISK_ABOVE; break;
+ case FontEmphasisMark::Accent : nRet = FontEmphasis::ACCENT_ABOVE; break;
+ default: break;
+ }
+ if(nRet && nValue & FontEmphasisMark::PosBelow)
+ nRet += 10;
+ rVal <<= nRet;
+ }
+ break;
+ }
+ return true;
+}
+
+bool SvxEmphasisMarkItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch( nMemberId )
+ {
+ case MID_EMPHASIS:
+ {
+ sal_Int32 nValue = -1;
+ rVal >>= nValue;
+ FontEmphasisMark nMark;
+ switch(nValue)
+ {
+ case FontEmphasis::NONE : nMark = FontEmphasisMark::NONE; break;
+ case FontEmphasis::DOT_ABOVE : nMark = FontEmphasisMark::Dot|FontEmphasisMark::PosAbove; break;
+ case FontEmphasis::CIRCLE_ABOVE: nMark = FontEmphasisMark::Circle|FontEmphasisMark::PosAbove; break;
+ case FontEmphasis::DISK_ABOVE : nMark = FontEmphasisMark::Disc|FontEmphasisMark::PosAbove; break;
+ case FontEmphasis::ACCENT_ABOVE: nMark = FontEmphasisMark::Accent|FontEmphasisMark::PosAbove; break;
+ case FontEmphasis::DOT_BELOW : nMark = FontEmphasisMark::Dot|FontEmphasisMark::PosBelow; break;
+ case FontEmphasis::CIRCLE_BELOW: nMark = FontEmphasisMark::Circle|FontEmphasisMark::PosBelow; break;
+ case FontEmphasis::DISK_BELOW : nMark = FontEmphasisMark::Disc|FontEmphasisMark::PosBelow; break;
+ case FontEmphasis::ACCENT_BELOW: nMark = FontEmphasisMark::Accent|FontEmphasisMark::PosBelow; break;
+ default: return false;
+ }
+ SetValue( static_cast<sal_Int16>(nMark) );
+ }
+ break;
+ }
+ return true;
+}
+
+/*************************************************************************
+|* class SvxTwoLinesItem
+*************************************************************************/
+
+SvxTwoLinesItem::SvxTwoLinesItem( bool bFlag, sal_Unicode nStartBracket,
+ sal_Unicode nEndBracket, sal_uInt16 nW )
+ : SfxPoolItem( nW ),
+ cStartBracket( nStartBracket ), cEndBracket( nEndBracket ), bOn( bFlag )
+{
+}
+
+SvxTwoLinesItem::~SvxTwoLinesItem()
+{
+}
+
+bool SvxTwoLinesItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ return bOn == static_cast<const SvxTwoLinesItem&>(rAttr).bOn &&
+ cStartBracket == static_cast<const SvxTwoLinesItem&>(rAttr).cStartBracket &&
+ cEndBracket == static_cast<const SvxTwoLinesItem&>(rAttr).cEndBracket;
+}
+
+SvxTwoLinesItem* SvxTwoLinesItem::Clone( SfxItemPool* ) const
+{
+ return new SvxTwoLinesItem( *this );
+}
+
+bool SvxTwoLinesItem::QueryValue( css::uno::Any& rVal,
+ sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch( nMemberId )
+ {
+ case MID_TWOLINES:
+ rVal <<= bOn;
+ break;
+ case MID_START_BRACKET:
+ {
+ OUString s;
+ if( cStartBracket )
+ s = OUString( cStartBracket );
+ rVal <<= s;
+ }
+ break;
+ case MID_END_BRACKET:
+ {
+ OUString s;
+ if( cEndBracket )
+ s = OUString( cEndBracket );
+ rVal <<= s;
+ }
+ break;
+ default:
+ bRet = false;
+ break;
+ }
+ return bRet;
+}
+
+bool SvxTwoLinesItem::PutValue( const css::uno::Any& rVal,
+ sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = false;
+ OUString s;
+ switch( nMemberId )
+ {
+ case MID_TWOLINES:
+ bOn = Any2Bool( rVal );
+ bRet = true;
+ break;
+ case MID_START_BRACKET:
+ if( rVal >>= s )
+ {
+ cStartBracket = s.isEmpty() ? 0 : s[ 0 ];
+ bRet = true;
+ }
+ break;
+ case MID_END_BRACKET:
+ if( rVal >>= s )
+ {
+ cEndBracket = s.isEmpty() ? 0 : s[ 0 ];
+ bRet = true;
+ }
+ break;
+ }
+ return bRet;
+}
+
+bool SvxTwoLinesItem::GetPresentation( SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/,
+ OUString &rText, const IntlWrapper& /*rIntl*/ ) const
+{
+ if( !GetValue() )
+ rText = EditResId( RID_SVXITEMS_TWOLINES_OFF );
+ else
+ {
+ rText = EditResId( RID_SVXITEMS_TWOLINES );
+ if( GetStartBracket() )
+ rText = OUStringChar(GetStartBracket()) + rText;
+ if( GetEndBracket() )
+ rText += OUStringChar(GetEndBracket());
+ }
+ return true;
+}
+
+
+/*************************************************************************
+|* class SvxTextRotateItem
+*************************************************************************/
+
+SvxTextRotateItem::SvxTextRotateItem(Degree10 nValue, TypedWhichId<SvxTextRotateItem> nW)
+ : SfxUInt16Item(nW, nValue.get())
+{
+}
+
+SvxTextRotateItem* SvxTextRotateItem::Clone(SfxItemPool*) const
+{
+ return new SvxTextRotateItem(*this);
+}
+
+bool SvxTextRotateItem::GetPresentation(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/,
+ OUString &rText, const IntlWrapper&) const
+{
+ if (!GetValue())
+ rText = EditResId(RID_SVXITEMS_TEXTROTATE_OFF);
+ else
+ {
+ rText = EditResId(RID_SVXITEMS_TEXTROTATE);
+ rText = rText.replaceFirst("$(ARG1)",
+ OUString::number(toDegrees(GetValue())));
+ }
+ return true;
+}
+
+bool SvxTextRotateItem::QueryValue(css::uno::Any& rVal,
+ sal_uInt8 nMemberId) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch (nMemberId)
+ {
+ case MID_ROTATE:
+ rVal <<= static_cast<sal_Int16>(GetValue());
+ break;
+ default:
+ bRet = false;
+ break;
+ }
+ return bRet;
+}
+
+bool SvxTextRotateItem::PutValue(const css::uno::Any& rVal, sal_uInt8 nMemberId)
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch (nMemberId)
+ {
+ case MID_ROTATE:
+ {
+ sal_Int16 nVal = 0;
+ if ((rVal >>= nVal) && (0 == nVal || 900 == nVal || 2700 == nVal))
+ SetValue(Degree10(nVal));
+ else
+ bRet = false;
+ break;
+ }
+ default:
+ bRet = false;
+ }
+ return bRet;
+}
+
+void SvxTextRotateItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxTextRotateItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(OString::number(GetValue().get()).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+
+/*************************************************************************
+|* class SvxCharRotateItem
+*************************************************************************/
+
+SvxCharRotateItem::SvxCharRotateItem( Degree10 nValue,
+ bool bFitIntoLine,
+ TypedWhichId<SvxCharRotateItem> nW )
+ : SvxTextRotateItem(nValue, nW), bFitToLine( bFitIntoLine )
+{
+}
+
+SvxCharRotateItem* SvxCharRotateItem::Clone( SfxItemPool* ) const
+{
+ return new SvxCharRotateItem( *this );
+}
+
+bool SvxCharRotateItem::GetPresentation(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/,
+ OUString &rText, const IntlWrapper&) const
+{
+ if( !GetValue() )
+ rText = EditResId( RID_SVXITEMS_CHARROTATE_OFF );
+ else
+ {
+ rText = EditResId( RID_SVXITEMS_CHARROTATE );
+ rText = rText.replaceFirst( "$(ARG1)",
+ OUString::number( toDegrees(GetValue()) ));
+ if( IsFitToLine() )
+ rText += EditResId( RID_SVXITEMS_CHARROTATE_FITLINE );
+ }
+ return true;
+}
+
+bool SvxCharRotateItem::QueryValue( css::uno::Any& rVal,
+ sal_uInt8 nMemberId ) const
+{
+ bool bRet = true;
+ switch(nMemberId & ~CONVERT_TWIPS)
+ {
+ case MID_ROTATE:
+ SvxTextRotateItem::QueryValue(rVal, nMemberId);
+ break;
+ case MID_FITTOLINE:
+ rVal <<= IsFitToLine();
+ break;
+ default:
+ bRet = false;
+ break;
+ }
+ return bRet;
+}
+
+bool SvxCharRotateItem::PutValue( const css::uno::Any& rVal,
+ sal_uInt8 nMemberId )
+{
+ bool bRet = true;
+ switch(nMemberId & ~CONVERT_TWIPS)
+ {
+ case MID_ROTATE:
+ {
+ bRet = SvxTextRotateItem::PutValue(rVal, nMemberId);
+ break;
+ }
+
+ case MID_FITTOLINE:
+ SetFitToLine( Any2Bool( rVal ) );
+ break;
+ default:
+ bRet = false;
+ }
+ return bRet;
+}
+
+bool SvxCharRotateItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+ return SvxTextRotateItem::operator==( rItem ) &&
+ IsFitToLine() == static_cast<const SvxCharRotateItem&>(rItem).IsFitToLine();
+}
+
+void SvxCharRotateItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxCharRotateItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(OString::number(GetValue().get()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("fitToLine"), BAD_CAST(OString::boolean(IsFitToLine()).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/*************************************************************************
+|* class SvxCharScaleItem
+*************************************************************************/
+
+SvxCharScaleWidthItem::SvxCharScaleWidthItem( sal_uInt16 nValue,
+ TypedWhichId<SvxCharScaleWidthItem> nW )
+ : SfxUInt16Item( nW, nValue )
+{
+}
+
+SvxCharScaleWidthItem* SvxCharScaleWidthItem::Clone( SfxItemPool* ) const
+{
+ return new SvxCharScaleWidthItem( *this );
+}
+
+bool SvxCharScaleWidthItem::GetPresentation(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/,
+ OUString &rText, const IntlWrapper&) const
+{
+ if( !GetValue() )
+ rText = EditResId( RID_SVXITEMS_CHARSCALE_OFF );
+ else
+ {
+ rText = EditResId( RID_SVXITEMS_CHARSCALE );
+ rText = rText.replaceFirst( "$(ARG1)",
+ OUString::number( GetValue() ));
+ }
+ return true;
+}
+
+bool SvxCharScaleWidthItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/ )
+{
+ // SfxUInt16Item::QueryValue returns sal_Int32 in Any now... (srx642w)
+ // where we still want this to be a sal_Int16
+ sal_Int16 nValue = sal_Int16();
+ if (rVal >>= nValue)
+ {
+ SetValue( static_cast<sal_uInt16>(nValue) );
+ return true;
+ }
+
+ SAL_WARN("editeng.items", "SvxCharScaleWidthItem::PutValue - Wrong type!" );
+ return false;
+}
+
+bool SvxCharScaleWidthItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) const
+{
+ // SfxUInt16Item::QueryValue returns sal_Int32 in Any now... (srx642w)
+ // where we still want this to be a sal_Int16
+ rVal <<= static_cast<sal_Int16>(GetValue());
+ return true;
+}
+
+/*************************************************************************
+|* class SvxCharReliefItem
+*************************************************************************/
+
+SvxCharReliefItem::SvxCharReliefItem( FontRelief eValue,
+ const sal_uInt16 nId )
+ : SfxEnumItem( nId, eValue )
+{
+}
+
+SvxCharReliefItem* SvxCharReliefItem::Clone( SfxItemPool * ) const
+{
+ return new SvxCharReliefItem( *this );
+}
+
+static TranslateId RID_SVXITEMS_RELIEF[] =
+{
+ RID_SVXITEMS_RELIEF_NONE,
+ RID_SVXITEMS_RELIEF_EMBOSSED,
+ RID_SVXITEMS_RELIEF_ENGRAVED
+};
+
+OUString SvxCharReliefItem::GetValueTextByPos(sal_uInt16 nPos)
+{
+ assert(nPos < std::size(RID_SVXITEMS_RELIEF) && "enum overflow");
+ return EditResId(RID_SVXITEMS_RELIEF[nPos]);
+}
+
+sal_uInt16 SvxCharReliefItem::GetValueCount() const
+{
+ return std::size(RID_SVXITEMS_RELIEF) - 1;
+}
+
+bool SvxCharReliefItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper& /*rIntl*/
+) const
+{
+ rText = GetValueTextByPos( static_cast<sal_uInt16>(GetValue()) );
+ return true;
+}
+
+bool SvxCharReliefItem::PutValue( const css::uno::Any& rVal,
+ sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch( nMemberId )
+ {
+ case MID_RELIEF:
+ {
+ sal_Int16 nVal = -1;
+ rVal >>= nVal;
+ if(nVal >= 0 && nVal <= sal_Int16(FontRelief::Engraved))
+ SetValue( static_cast<FontRelief>(nVal) );
+ else
+ bRet = false;
+ }
+ break;
+ default:
+ bRet = false;
+ break;
+ }
+ return bRet;
+}
+
+bool SvxCharReliefItem::QueryValue( css::uno::Any& rVal,
+ sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch( nMemberId )
+ {
+ case MID_RELIEF:
+ rVal <<= static_cast<sal_Int16>(GetValue());
+ break;
+ default:
+ bRet = false;
+ break;
+ }
+ return bRet;
+}
+
+/*************************************************************************
+|* class SvxScriptSetItem
+*************************************************************************/
+
+SvxScriptSetItem::SvxScriptSetItem( sal_uInt16 nSlotId, SfxItemPool& rPool )
+ : SfxSetItem( nSlotId, SfxItemSet( rPool,
+ svl::Items<SID_ATTR_CHAR_FONT, SID_ATTR_CHAR_FONT> ))
+{
+ sal_uInt16 nLatin, nAsian, nComplex;
+ GetWhichIds( nLatin, nAsian, nComplex );
+ GetItemSet().MergeRange( nLatin, nLatin );
+ GetItemSet().MergeRange( nAsian, nAsian );
+ GetItemSet().MergeRange( nComplex, nComplex );
+}
+
+SvxScriptSetItem* SvxScriptSetItem::Clone( SfxItemPool * ) const
+{
+ SvxScriptSetItem* p = new SvxScriptSetItem( Which(), *GetItemSet().GetPool() );
+ p->GetItemSet().Put( GetItemSet(), false );
+ return p;
+}
+
+const SfxPoolItem* SvxScriptSetItem::GetItemOfScriptSet(
+ const SfxItemSet& rSet, sal_uInt16 nId )
+{
+ const SfxPoolItem* pI;
+ SfxItemState eSt = rSet.GetItemState( nId, false, &pI );
+ if( SfxItemState::SET != eSt )
+ pI = SfxItemState::DEFAULT == eSt ? &rSet.Get( nId ) : nullptr;
+ return pI;
+}
+
+const SfxPoolItem* SvxScriptSetItem::GetItemOfScript( sal_uInt16 nSlotId, const SfxItemSet& rSet, SvtScriptType nScript )
+{
+ sal_uInt16 nLatin, nAsian, nComplex;
+ GetWhichIds( nSlotId, rSet, nLatin, nAsian, nComplex );
+
+ const SfxPoolItem *pRet, *pAsn, *pCmplx;
+ if (nScript == SvtScriptType::ASIAN)
+ {
+ pRet = GetItemOfScriptSet( rSet, nAsian );
+ } else if (nScript == SvtScriptType::COMPLEX)
+ {
+ pRet = GetItemOfScriptSet( rSet, nComplex );
+ } else if (nScript == (SvtScriptType::LATIN|SvtScriptType::ASIAN))
+ {
+ if( nullptr == (pRet = GetItemOfScriptSet( rSet, nLatin )) ||
+ nullptr == (pAsn = GetItemOfScriptSet( rSet, nAsian )) ||
+ *pRet != *pAsn )
+ pRet = nullptr;
+ } else if (nScript == (SvtScriptType::LATIN|SvtScriptType::COMPLEX))
+ {
+ if( nullptr == (pRet = GetItemOfScriptSet( rSet, nLatin )) ||
+ nullptr == (pCmplx = GetItemOfScriptSet( rSet, nComplex )) ||
+ *pRet != *pCmplx )
+ pRet = nullptr;
+ } else if (nScript == (SvtScriptType::ASIAN|SvtScriptType::COMPLEX))
+ {
+ if( nullptr == (pRet = GetItemOfScriptSet( rSet, nAsian )) ||
+ nullptr == (pCmplx = GetItemOfScriptSet( rSet, nComplex )) ||
+ *pRet != *pCmplx )
+ pRet = nullptr;
+ } else if (nScript == (SvtScriptType::LATIN|SvtScriptType::ASIAN|SvtScriptType::COMPLEX))
+ {
+ if( nullptr == (pRet = GetItemOfScriptSet( rSet, nLatin )) ||
+ nullptr == (pAsn = GetItemOfScriptSet( rSet, nAsian )) ||
+ nullptr == (pCmplx = GetItemOfScriptSet( rSet, nComplex )) ||
+ *pRet != *pAsn || *pRet != *pCmplx )
+ pRet = nullptr;
+ } else {
+ //no one valid -> match to latin
+ pRet = GetItemOfScriptSet( rSet, nLatin );
+ }
+ return pRet;
+}
+
+const SfxPoolItem* SvxScriptSetItem::GetItemOfScript( SvtScriptType nScript ) const
+{
+ return GetItemOfScript( Which(), GetItemSet(), nScript );
+}
+
+void SvxScriptSetItem::PutItemForScriptType( SvtScriptType nScriptType,
+ const SfxPoolItem& rItem )
+{
+ sal_uInt16 nLatin, nAsian, nComplex;
+ GetWhichIds( nLatin, nAsian, nComplex );
+
+ if( SvtScriptType::LATIN & nScriptType )
+ {
+ GetItemSet().Put( rItem.CloneSetWhich(nLatin) );
+ }
+ if( SvtScriptType::ASIAN & nScriptType )
+ {
+ GetItemSet().Put( rItem.CloneSetWhich(nAsian) );
+ }
+ if( SvtScriptType::COMPLEX & nScriptType )
+ {
+ GetItemSet().Put( rItem.CloneSetWhich(nComplex) );
+ }
+}
+
+void SvxScriptSetItem::GetWhichIds( sal_uInt16 nSlotId, const SfxItemSet& rSet, sal_uInt16& rLatin, sal_uInt16& rAsian, sal_uInt16& rComplex )
+{
+ const SfxItemPool& rPool = *rSet.GetPool();
+ GetSlotIds( nSlotId, rLatin, rAsian, rComplex );
+ rLatin = rPool.GetWhich( rLatin );
+ rAsian = rPool.GetWhich( rAsian );
+ rComplex = rPool.GetWhich( rComplex );
+}
+
+void SvxScriptSetItem::GetWhichIds( sal_uInt16& rLatin, sal_uInt16& rAsian,
+ sal_uInt16& rComplex ) const
+{
+ GetWhichIds( Which(), GetItemSet(), rLatin, rAsian, rComplex );
+}
+
+void SvxScriptSetItem::GetSlotIds( sal_uInt16 nSlotId, sal_uInt16& rLatin,
+ sal_uInt16& rAsian, sal_uInt16& rComplex )
+{
+ switch( nSlotId )
+ {
+ default:
+ SAL_WARN( "editeng.items", "wrong SlotId for class SvxScriptSetItem" );
+ [[fallthrough]]; // default to font - Id Range !!
+
+ case SID_ATTR_CHAR_FONT:
+ rLatin = SID_ATTR_CHAR_FONT;
+ rAsian = SID_ATTR_CHAR_CJK_FONT;
+ rComplex = SID_ATTR_CHAR_CTL_FONT;
+ break;
+ case SID_ATTR_CHAR_FONTHEIGHT:
+ rLatin = SID_ATTR_CHAR_FONTHEIGHT;
+ rAsian = SID_ATTR_CHAR_CJK_FONTHEIGHT;
+ rComplex = SID_ATTR_CHAR_CTL_FONTHEIGHT;
+ break;
+ case SID_ATTR_CHAR_WEIGHT:
+ rLatin = SID_ATTR_CHAR_WEIGHT;
+ rAsian = SID_ATTR_CHAR_CJK_WEIGHT;
+ rComplex = SID_ATTR_CHAR_CTL_WEIGHT;
+ break;
+ case SID_ATTR_CHAR_POSTURE:
+ rLatin = SID_ATTR_CHAR_POSTURE;
+ rAsian = SID_ATTR_CHAR_CJK_POSTURE;
+ rComplex = SID_ATTR_CHAR_CTL_POSTURE;
+ break;
+ case SID_ATTR_CHAR_LANGUAGE:
+ rLatin = SID_ATTR_CHAR_LANGUAGE;
+ rAsian = SID_ATTR_CHAR_CJK_LANGUAGE;
+ rComplex = SID_ATTR_CHAR_CTL_LANGUAGE;
+ break;
+ case SID_ATTR_CHAR_SHADOWED:
+ rLatin = SID_ATTR_CHAR_SHADOWED;
+ rAsian = SID_ATTR_CHAR_SHADOWED;
+ rComplex = SID_ATTR_CHAR_SHADOWED;
+ break;
+ case SID_ATTR_CHAR_STRIKEOUT:
+ rLatin = SID_ATTR_CHAR_STRIKEOUT;
+ rAsian = SID_ATTR_CHAR_STRIKEOUT;
+ rComplex = SID_ATTR_CHAR_STRIKEOUT;
+ break;
+ }
+}
+
+void GetDefaultFonts( SvxFontItem& rLatin, SvxFontItem& rAsian, SvxFontItem& rComplex )
+{
+ const sal_uInt16 nItemCnt = 3;
+
+ static struct
+ {
+ DefaultFontType nFontType;
+ LanguageType nLanguage;
+ }
+ const aOutTypeArr[ nItemCnt ] =
+ {
+ { DefaultFontType::LATIN_TEXT, LANGUAGE_ENGLISH_US },
+ { DefaultFontType::CJK_TEXT, LANGUAGE_ENGLISH_US },
+ { DefaultFontType::CTL_TEXT, LANGUAGE_ARABIC_SAUDI_ARABIA }
+ };
+
+ SvxFontItem* aItemArr[ nItemCnt ] = { &rLatin, &rAsian, &rComplex };
+
+ for ( sal_uInt16 n = 0; n < nItemCnt; ++n )
+ {
+ vcl::Font aFont( OutputDevice::GetDefaultFont( aOutTypeArr[ n ].nFontType,
+ aOutTypeArr[ n ].nLanguage,
+ GetDefaultFontFlags::OnlyOne ) );
+ SvxFontItem* pItem = aItemArr[ n ];
+ pItem->SetFamily( aFont.GetFamilyType() );
+ pItem->SetFamilyName( aFont.GetFamilyName() );
+ pItem->SetStyleName( OUString() );
+ pItem->SetPitch( aFont.GetPitch());
+ pItem->SetCharSet(aFont.GetCharSet());
+ }
+}
+
+
+bool SvxRsidItem::QueryValue( uno::Any& rVal, sal_uInt8 ) const
+{
+ rVal <<= GetValue();
+ return true;
+}
+
+bool SvxRsidItem::PutValue( const uno::Any& rVal, sal_uInt8 )
+{
+ sal_uInt32 nRsid = 0;
+ if( !( rVal >>= nRsid ) )
+ return false;
+
+ SetValue( nRsid );
+ return true;
+}
+
+SvxRsidItem* SvxRsidItem::Clone( SfxItemPool * ) const
+{
+ return new SvxRsidItem( *this );
+}
+
+bool SvxRsidItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper& /*rIntl*/
+) const
+{
+ rText.clear();
+ return false;
+}
+
+void SvxRsidItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxRsidItem"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("whichId"), "%d", Which());
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("value"), "%" SAL_PRIuUINT32, GetValue());
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/items/writingmodeitem.cxx b/editeng/source/items/writingmodeitem.cxx
new file mode 100644
index 0000000000..35dbddabab
--- /dev/null
+++ b/editeng/source/items/writingmodeitem.cxx
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <editeng/writingmodeitem.hxx>
+#include <editeng/frmdir.hxx>
+#include <editeng/eerdll.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::text;
+
+
+SvxWritingModeItem::SvxWritingModeItem( WritingMode eValue, TypedWhichId<SvxWritingModeItem> _nWhich )
+ : SfxUInt16Item( _nWhich, static_cast<sal_uInt16>(eValue) )
+{
+}
+
+SvxWritingModeItem::~SvxWritingModeItem()
+{
+}
+
+SvxWritingModeItem* SvxWritingModeItem::Clone( SfxItemPool * ) const
+{
+ return new SvxWritingModeItem( *this );
+}
+
+bool SvxWritingModeItem::GetPresentation( SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreMetric*/,
+ MapUnit /*ePresMetric*/,
+ OUString &rText,
+ const IntlWrapper& ) const
+{
+ rText = EditResId(getFrmDirResId(static_cast<int>(GetValue())));
+ return true;
+}
+
+bool SvxWritingModeItem::PutValue( const css::uno::Any& rVal, sal_uInt8 )
+{
+ sal_Int32 nVal = 0;
+ bool bRet = ( rVal >>= nVal );
+
+ if( !bRet )
+ {
+ WritingMode eMode;
+ bRet = rVal >>= eMode;
+
+ if( bRet )
+ {
+ nVal = static_cast<sal_Int32>(eMode);
+ }
+ }
+
+ if( bRet )
+ {
+ switch( static_cast<WritingMode>(nVal) )
+ {
+ case WritingMode_LR_TB:
+ case WritingMode_RL_TB:
+ case WritingMode_TB_RL:
+ SetValue( static_cast<sal_uInt16>(nVal) );
+ bRet = true;
+ break;
+ default:
+ bRet = false;
+ break;
+ }
+ }
+
+ return bRet;
+}
+
+bool SvxWritingModeItem::QueryValue( css::uno::Any& rVal,
+ sal_uInt8 ) const
+{
+ rVal <<= GetValue();
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/items/xmlcnitm.cxx b/editeng/source/items/xmlcnitm.cxx
new file mode 100644
index 0000000000..7507ed2afd
--- /dev/null
+++ b/editeng/source/items/xmlcnitm.cxx
@@ -0,0 +1,206 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+
+#include <comphelper/servicehelper.hxx>
+#include <com/sun/star/xml/AttributeData.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <o3tl/any.hxx>
+#include <xmloff/xmlcnimp.hxx>
+#include <xmloff/unoatrcn.hxx>
+#include <editeng/xmlcnitm.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::xml;
+
+
+SvXMLAttrContainerItem::SvXMLAttrContainerItem( sal_uInt16 _nWhich ) :
+ SfxPoolItem( _nWhich )
+{
+}
+
+SvXMLAttrContainerItem::SvXMLAttrContainerItem(
+ const SvXMLAttrContainerItem& rItem ) :
+ SfxPoolItem( rItem ),
+ maContainerData( rItem.maContainerData )
+{
+}
+
+SvXMLAttrContainerItem::~SvXMLAttrContainerItem()
+{
+}
+
+bool SvXMLAttrContainerItem::operator==( const SfxPoolItem& rItem ) const
+{
+ return SfxPoolItem::operator==(rItem) &&
+ maContainerData == static_cast<const SvXMLAttrContainerItem&>(rItem).maContainerData;
+}
+
+bool SvXMLAttrContainerItem::GetPresentation(
+ SfxItemPresentation /*ePresentation*/,
+ MapUnit /*eCoreMetric*/,
+ MapUnit /*ePresentationMetric*/,
+ OUString & /*rText*/,
+ const IntlWrapper& /*rIntlWrapper*/ ) const
+{
+ return false;
+}
+
+bool SvXMLAttrContainerItem::QueryValue( css::uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) const
+{
+ Reference<XNameContainer> xContainer
+ = new SvUnoAttributeContainer(std::make_unique<SvXMLAttrContainerData>(maContainerData));
+
+ rVal <<= xContainer;
+ return true;
+}
+
+bool SvXMLAttrContainerItem::PutValue( const css::uno::Any& rVal, sal_uInt8 /*nMemberId*/ )
+{
+ Reference<XInterface> xTunnel(rVal, UNO_QUERY);
+ if (auto pContainer = dynamic_cast<SvUnoAttributeContainer*>(xTunnel.get()))
+ {
+ maContainerData = *pContainer->GetContainerImpl();
+ }
+ else
+ {
+ SvXMLAttrContainerData aNewImpl;
+
+ try
+ {
+ Reference<XNameContainer> xContainer( rVal, UNO_QUERY );
+ if( !xContainer.is() )
+ return false;
+
+ const Sequence< OUString > aNameSequence( xContainer->getElementNames() );
+ const OUString* pNames = aNameSequence.getConstArray();
+ const sal_Int32 nCount = aNameSequence.getLength();
+ Any aAny;
+ sal_Int32 nAttr;
+
+ for( nAttr = 0; nAttr < nCount; nAttr++ )
+ {
+ const OUString aName( *pNames++ );
+
+ aAny = xContainer->getByName( aName );
+ auto pData = o3tl::tryAccess<AttributeData>(aAny);
+ if( !pData )
+ return false;
+
+ sal_Int32 pos = aName.indexOf( ':' );
+ if( pos != -1 )
+ {
+ const OUString aPrefix( aName.copy( 0, pos ));
+ const OUString aLName( aName.copy( pos+1 ));
+
+ if( pData->Namespace.isEmpty() )
+ {
+ if( !aNewImpl.AddAttr( aPrefix, aLName, pData->Value ) )
+ break;
+ }
+ else
+ {
+ if( !aNewImpl.AddAttr( aPrefix, pData->Namespace, aLName, pData->Value ) )
+ break;
+ }
+ }
+ else
+ {
+ if( !aNewImpl.AddAttr( aName, pData->Value ) )
+ break;
+ }
+ }
+
+ if( nAttr == nCount )
+ maContainerData = std::move(aNewImpl);
+ else
+ return false;
+ }
+ catch(...)
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+bool SvXMLAttrContainerItem::AddAttr( const OUString& rLName,
+ const OUString& rValue )
+{
+ return maContainerData.AddAttr( rLName, rValue );
+}
+
+bool SvXMLAttrContainerItem::AddAttr( const OUString& rPrefix,
+ const OUString& rNamespace, const OUString& rLName,
+ const OUString& rValue )
+{
+ return maContainerData.AddAttr( rPrefix, rNamespace, rLName, rValue );
+}
+
+sal_uInt16 SvXMLAttrContainerItem::GetAttrCount() const
+{
+ return static_cast<sal_uInt16>(maContainerData.GetAttrCount());
+}
+
+OUString SvXMLAttrContainerItem::GetAttrNamespace( sal_uInt16 i ) const
+{
+ return maContainerData.GetAttrNamespace( i );
+}
+
+OUString SvXMLAttrContainerItem::GetAttrPrefix( sal_uInt16 i ) const
+{
+ return maContainerData.GetAttrPrefix( i );
+}
+
+const OUString& SvXMLAttrContainerItem::GetAttrLName( sal_uInt16 i ) const
+{
+ return maContainerData.GetAttrLName( i );
+}
+
+const OUString& SvXMLAttrContainerItem::GetAttrValue( sal_uInt16 i ) const
+{
+ return maContainerData.GetAttrValue( i );
+}
+
+
+sal_uInt16 SvXMLAttrContainerItem::GetFirstNamespaceIndex() const
+{
+ return maContainerData.GetFirstNamespaceIndex();
+}
+
+sal_uInt16 SvXMLAttrContainerItem::GetNextNamespaceIndex( sal_uInt16 nIdx ) const
+{
+ return maContainerData.GetNextNamespaceIndex( nIdx );
+}
+
+const OUString& SvXMLAttrContainerItem::GetNamespace( sal_uInt16 i ) const
+{
+ return maContainerData.GetNamespace( i );
+}
+
+const OUString& SvXMLAttrContainerItem::GetPrefix( sal_uInt16 i ) const
+{
+ return maContainerData.GetPrefix( i );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/lookuptree/Trie.cxx b/editeng/source/lookuptree/Trie.cxx
new file mode 100644
index 0000000000..6505c00e43
--- /dev/null
+++ b/editeng/source/lookuptree/Trie.cxx
@@ -0,0 +1,186 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <editeng/Trie.hxx>
+
+namespace editeng
+{
+
+/* TrieNode */
+
+struct TrieNode final
+{
+ static const int LATIN_ARRAY_SIZE = 26;
+
+ sal_Unicode mCharacter;
+ bool mMarker;
+ std::vector<std::unique_ptr<TrieNode>> mChildren;
+ std::unique_ptr<TrieNode> mLatinArray[LATIN_ARRAY_SIZE];
+
+ explicit TrieNode(sal_Unicode aCharacter = '\0');
+
+ void markWord();
+ TrieNode* findChild(sal_Unicode aCharacter);
+ TrieNode* traversePath(std::u16string_view sPath);
+ void addNewChild(TrieNode* pChild);
+ void collectSuggestions(std::u16string_view sPath, std::vector<OUString>& rSuggestionList);
+ static void collectSuggestionsForCurrentNode(TrieNode* pCurrent, std::u16string_view sPath, std::vector<OUString>& rSuggestionList);
+};
+
+TrieNode::TrieNode(sal_Unicode aCharacter) :
+ mCharacter(aCharacter),
+ mMarker(false)
+{
+ for (auto & i : mLatinArray)
+ {
+ i = nullptr;
+ }
+}
+
+void TrieNode::markWord()
+{
+ mMarker = true;
+}
+
+void TrieNode::addNewChild(TrieNode* pChild)
+{
+ if (pChild->mCharacter >= 'a' &&
+ pChild->mCharacter <= 'z')
+ {
+ mLatinArray[pChild->mCharacter - u'a'].reset(pChild);
+ }
+ else
+ {
+ mChildren.push_back(std::unique_ptr<TrieNode>(pChild));
+ }
+}
+
+TrieNode* TrieNode::findChild(sal_Unicode aInputCharacter)
+{
+ if (aInputCharacter >= 'a' &&
+ aInputCharacter <= 'z')
+ {
+ return mLatinArray[aInputCharacter - u'a'].get();
+ }
+
+ for(auto const & pCurrent : mChildren)
+ {
+ if ( pCurrent->mCharacter == aInputCharacter )
+ return pCurrent.get();
+ }
+
+ return nullptr;
+}
+
+void TrieNode::collectSuggestions(std::u16string_view sPath, std::vector<OUString>& rSuggestionList)
+{
+ // first traverse nodes for alphabet characters
+ for (auto const & pCurrent : mLatinArray)
+ {
+ if (pCurrent != nullptr)
+ collectSuggestionsForCurrentNode(pCurrent.get(), sPath, rSuggestionList);
+ }
+
+ // traverse nodes for other characters
+ for(auto const & pCurrent : mChildren)
+ {
+ if (pCurrent != nullptr)
+ collectSuggestionsForCurrentNode(pCurrent.get(), sPath, rSuggestionList);
+ }
+}
+
+void TrieNode::collectSuggestionsForCurrentNode(TrieNode* pCurrent, std::u16string_view sPath, std::vector<OUString>& rSuggestionList)
+{
+ OUString aStringPath = sPath + OUStringChar(pCurrent->mCharacter);
+ if(pCurrent->mMarker)
+ {
+ rSuggestionList.push_back(aStringPath);
+ }
+ // recursively descend tree
+ pCurrent->collectSuggestions(aStringPath, rSuggestionList);
+}
+
+TrieNode* TrieNode::traversePath(std::u16string_view sPath)
+{
+ TrieNode* pCurrent = this;
+
+ for ( const auto aCurrentChar : sPath )
+ {
+ pCurrent = pCurrent->findChild(aCurrentChar);
+ if ( pCurrent == nullptr )
+ return nullptr;
+ }
+
+ return pCurrent;
+}
+
+/* TRIE */
+
+Trie::Trie() :
+ mRoot(new TrieNode())
+{}
+
+Trie::~Trie()
+{}
+
+void Trie::insert(std::u16string_view sInputString) const
+{
+ // adding an empty word is not allowed
+ if ( sInputString.empty() )
+ {
+ return;
+ }
+
+ // traverse the input string and modify the tree with new nodes / characters
+
+ TrieNode* pCurrent = mRoot.get();
+
+ for ( const auto aCurrentChar : sInputString )
+ {
+ TrieNode* pChild = pCurrent->findChild(aCurrentChar);
+ if ( pChild == nullptr )
+ {
+ TrieNode* pNewNode = new TrieNode(aCurrentChar);
+ pCurrent->addNewChild(pNewNode);
+ pCurrent = pNewNode;
+ }
+ else
+ {
+ pCurrent = pChild;
+ }
+ }
+
+ pCurrent->markWord();
+}
+
+void Trie::findSuggestions(std::u16string_view sWordPart, std::vector<OUString>& rSuggestionList) const
+{
+ TrieNode* pNode = mRoot->traversePath(sWordPart);
+
+ if (pNode != nullptr)
+ {
+ pNode->collectSuggestions(sWordPart, rSuggestionList);
+ }
+}
+
+size_t Trie::size() const
+{
+ if (!mRoot)
+ return 0;
+ std::vector<OUString> entries;
+ mRoot->collectSuggestions(std::u16string_view(), entries);
+ return entries.size();
+}
+
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/misc/SvXMLAutoCorrectExport.cxx b/editeng/source/misc/SvXMLAutoCorrectExport.cxx
new file mode 100644
index 0000000000..8faf434116
--- /dev/null
+++ b/editeng/source/misc/SvXMLAutoCorrectExport.cxx
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "SvXMLAutoCorrectExport.hxx"
+
+#include <com/sun/star/util/MeasureUnit.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+#include <xmloff/namespacemap.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltoken.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star;
+using namespace ::xmloff::token;
+
+SvXMLAutoCorrectExport::SvXMLAutoCorrectExport(
+ const css::uno::Reference< css::uno::XComponentContext > & xContext,
+ const SvxAutocorrWordList * pNewAutocorr_List,
+ const OUString &rFileName,
+ css::uno::Reference< css::xml::sax::XDocumentHandler> const &rHandler)
+: SvXMLExport( xContext, "", rFileName, util::MeasureUnit::CM, rHandler ),
+ pAutocorr_List( pNewAutocorr_List )
+{
+ GetNamespaceMap_().Add( GetXMLToken ( XML_NP_BLOCK_LIST),
+ GetXMLToken ( XML_N_BLOCK_LIST ),
+ XML_NAMESPACE_BLOCKLIST );
+}
+
+ErrCode SvXMLAutoCorrectExport::exportDoc(enum XMLTokenEnum /*eClass*/)
+{
+ GetDocHandler()->startDocument();
+
+ addChaffWhenEncryptedStorage();
+
+ AddAttribute ( XML_NAMESPACE_NONE,
+ GetNamespaceMap_().GetAttrNameByKey ( XML_NAMESPACE_BLOCKLIST ),
+ GetNamespaceMap_().GetNameByKey ( XML_NAMESPACE_BLOCKLIST ) );
+ {
+ SvXMLElementExport aRoot (*this, XML_NAMESPACE_BLOCKLIST, XML_BLOCK_LIST, true, true);
+ const SvxAutocorrWordList::AutocorrWordSetType& rContent = pAutocorr_List->getSortedContent();
+ for (auto const& content : rContent)
+ {
+ AddAttribute( XML_NAMESPACE_BLOCKLIST,
+ XML_ABBREVIATED_NAME,
+ content.GetShort());
+ AddAttribute( XML_NAMESPACE_BLOCKLIST,
+ XML_NAME,
+ content.IsTextOnly() ? content.GetLong() : content.GetShort());
+
+ SvXMLElementExport aBlock( *this, XML_NAMESPACE_BLOCKLIST, XML_BLOCK, true, true);
+ }
+ }
+ GetDocHandler()->endDocument();
+ return ERRCODE_NONE;
+}
+
+SvXMLExceptionListExport::SvXMLExceptionListExport(
+ const css::uno::Reference< css::uno::XComponentContext > & xContext,
+ const SvStringsISortDtor &rNewList,
+ const OUString &rFileName,
+ css::uno::Reference< css::xml::sax::XDocumentHandler> const &rHandler)
+: SvXMLExport( xContext, "", rFileName, util::MeasureUnit::CM, rHandler ),
+ rList( rNewList )
+{
+ GetNamespaceMap_().Add( GetXMLToken ( XML_NP_BLOCK_LIST ),
+ GetXMLToken ( XML_N_BLOCK_LIST ),
+ XML_NAMESPACE_BLOCKLIST );
+}
+
+ErrCode SvXMLExceptionListExport::exportDoc(enum XMLTokenEnum /*eClass*/)
+{
+ GetDocHandler()->startDocument();
+
+ addChaffWhenEncryptedStorage();
+
+ AddAttribute ( XML_NAMESPACE_NONE,
+ GetNamespaceMap_().GetAttrNameByKey ( XML_NAMESPACE_BLOCKLIST ),
+ GetNamespaceMap_().GetNameByKey ( XML_NAMESPACE_BLOCKLIST ) );
+ {
+ SvXMLElementExport aRoot (*this, XML_NAMESPACE_BLOCKLIST, XML_BLOCK_LIST, true, true);
+ sal_uInt16 nBlocks= rList.size();
+ for ( sal_uInt16 i = 0; i < nBlocks; i++)
+ {
+ AddAttribute( XML_NAMESPACE_BLOCKLIST,
+ XML_ABBREVIATED_NAME,
+ rList[i] );
+ SvXMLElementExport aBlock( *this, XML_NAMESPACE_BLOCKLIST, XML_BLOCK, true, true);
+ }
+ }
+ GetDocHandler()->endDocument();
+ return ERRCODE_NONE;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/misc/SvXMLAutoCorrectExport.hxx b/editeng/source/misc/SvXMLAutoCorrectExport.hxx
new file mode 100644
index 0000000000..58570e3ad1
--- /dev/null
+++ b/editeng/source/misc/SvXMLAutoCorrectExport.hxx
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <xmloff/xmlexp.hxx>
+#include <editeng/svxacorr.hxx>
+
+class SvXMLAutoCorrectExport : public SvXMLExport
+{
+private:
+ const SvxAutocorrWordList *pAutocorr_List;
+public:
+ SvXMLAutoCorrectExport(
+ const css::uno::Reference< css::uno::XComponentContext > & xContext,
+ const SvxAutocorrWordList * pNewAutocorr_List,
+ const OUString &rFileName,
+ css::uno::Reference< css::xml::sax::XDocumentHandler> const &rHandler);
+
+ ErrCode exportDoc(enum ::xmloff::token::XMLTokenEnum eClass = ::xmloff::token::XML_TOKEN_INVALID) override;
+ void ExportAutoStyles_() override {}
+ void ExportMasterStyles_ () override {}
+ void ExportContent_() override {}
+};
+
+class SvStringsISortDtor;
+
+class SvXMLExceptionListExport : public SvXMLExport
+{
+private:
+ const SvStringsISortDtor & rList;
+public:
+ SvXMLExceptionListExport(
+ const css::uno::Reference< css::uno::XComponentContext > & xContext,
+ const SvStringsISortDtor &rNewList,
+ const OUString &rFileName,
+ css::uno::Reference< css::xml::sax::XDocumentHandler> const &rHandler);
+
+ ErrCode exportDoc(enum ::xmloff::token::XMLTokenEnum eClass = ::xmloff::token::XML_TOKEN_INVALID) override;
+ void ExportAutoStyles_() override {}
+ void ExportMasterStyles_ () override {}
+ void ExportContent_() override {}
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/misc/SvXMLAutoCorrectImport.cxx b/editeng/source/misc/SvXMLAutoCorrectImport.cxx
new file mode 100644
index 0000000000..baeef88612
--- /dev/null
+++ b/editeng/source/misc/SvXMLAutoCorrectImport.cxx
@@ -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 .
+ */
+
+#include <utility>
+
+#include "SvXMLAutoCorrectImport.hxx"
+#include "SvXMLAutoCorrectTokenHandler.hxx"
+
+using namespace css;
+using namespace css::xml::sax;
+
+SvXMLAutoCorrectImport::SvXMLAutoCorrectImport(
+ const uno::Reference< uno::XComponentContext > & xContext,
+ SvxAutocorrWordList *pNewAutocorr_List,
+ SvxAutoCorrect &rNewAutoCorrect,
+ css::uno::Reference < css::embed::XStorage > xNewStorage)
+: SvXMLImport( xContext, "" ),
+ pAutocorr_List (pNewAutocorr_List),
+ rAutoCorrect ( rNewAutoCorrect ),
+ xStorage (std::move( xNewStorage ))
+{
+}
+
+SvXMLAutoCorrectImport::~SvXMLAutoCorrectImport() noexcept
+{
+}
+
+SvXMLImportContext *SvXMLAutoCorrectImport::CreateFastContext( sal_Int32 Element,
+ const uno::Reference< xml::sax::XFastAttributeList > & /*xAttrList*/ )
+{
+ if( Element == SvXMLAutoCorrectToken::BLOCKLIST )
+ return new SvXMLWordListContext( *this );
+ return nullptr;
+}
+
+SvXMLWordListContext::SvXMLWordListContext(
+ SvXMLAutoCorrectImport& rImport ) :
+ SvXMLImportContext ( rImport ),
+ rLocalRef(rImport)
+{
+ rLocalRef.rAutoCorrect.refreshBlockList( rLocalRef.xStorage );
+}
+
+css::uno::Reference<XFastContextHandler> SAL_CALL SvXMLWordListContext::createFastChildContext(
+ sal_Int32 Element, const uno::Reference< xml::sax::XFastAttributeList > & xAttrList )
+{
+ if ( Element == SvXMLAutoCorrectToken::BLOCK )
+ return new SvXMLWordContext (rLocalRef, xAttrList);
+ return nullptr;
+}
+
+SvXMLWordListContext::~SvXMLWordListContext()
+{
+}
+
+SvXMLWordContext::SvXMLWordContext(
+ SvXMLAutoCorrectImport& rImport,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) :
+ SvXMLImportContext ( rImport )
+{
+ OUString sWrong, sRight;
+ if ( xAttrList.is() && xAttrList->hasAttribute( SvXMLAutoCorrectToken::ABBREVIATED_NAME ) )
+ sWrong = xAttrList->getValue( SvXMLAutoCorrectToken::ABBREVIATED_NAME );
+
+ if ( xAttrList.is() && xAttrList->hasAttribute( SvXMLAutoCorrectToken::NAME ) )
+ sRight = xAttrList->getValue( SvXMLAutoCorrectToken::NAME );
+
+ if ( sWrong.isEmpty() || sRight.isEmpty())
+ return;
+
+ bool bOnlyTxt = sRight != sWrong;
+ if( !bOnlyTxt )
+ {
+ const OUString sLongSave( sRight );
+ if( !rImport.rAutoCorrect.GetLongText( sWrong, sRight ) &&
+ !sLongSave.isEmpty() )
+ {
+ sRight = sLongSave;
+ bOnlyTxt = true;
+ }
+ }
+ rImport.pAutocorr_List->LoadEntry( sWrong, sRight, bOnlyTxt );
+}
+
+SvXMLWordContext::~SvXMLWordContext()
+{
+}
+
+SvXMLExceptionListImport::SvXMLExceptionListImport(
+ const uno::Reference< uno::XComponentContext > & xContext,
+ SvStringsISortDtor & rNewList )
+: SvXMLImport( xContext, "" ),
+ rList (rNewList)
+{
+}
+
+SvXMLExceptionListImport::~SvXMLExceptionListImport() noexcept
+{
+}
+
+SvXMLImportContext *SvXMLExceptionListImport::CreateFastContext(sal_Int32 Element,
+ const uno::Reference< xml::sax::XFastAttributeList > & /*xAttrList*/ )
+{
+ if( Element == SvXMLAutoCorrectToken::BLOCKLIST )
+ return new SvXMLExceptionListContext( *this );
+ return nullptr;
+}
+
+SvXMLExceptionListContext::SvXMLExceptionListContext(
+ SvXMLExceptionListImport& rImport ) :
+ SvXMLImportContext ( rImport ),
+ rLocalRef(rImport)
+{
+}
+
+css::uno::Reference<xml::sax::XFastContextHandler> SAL_CALL SvXMLExceptionListContext::createFastChildContext(
+ sal_Int32 Element, const uno::Reference< xml::sax::XFastAttributeList > & xAttrList )
+{
+ if ( Element == SvXMLAutoCorrectToken::BLOCK )
+ return new SvXMLExceptionContext (rLocalRef, xAttrList);
+ return nullptr;
+}
+
+SvXMLExceptionListContext::~SvXMLExceptionListContext()
+{
+}
+
+SvXMLExceptionContext::SvXMLExceptionContext(
+ SvXMLExceptionListImport& rImport,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) :
+ SvXMLImportContext ( rImport )
+{
+ OUString sWord;
+ if( xAttrList.is() && xAttrList->hasAttribute( SvXMLAutoCorrectToken::ABBREVIATED_NAME ) )
+ sWord = xAttrList->getValue( SvXMLAutoCorrectToken::ABBREVIATED_NAME );
+
+ if (sWord.isEmpty())
+ return;
+
+ rImport.rList.insert( sWord );
+}
+
+SvXMLExceptionContext::~SvXMLExceptionContext()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/misc/SvXMLAutoCorrectImport.hxx b/editeng/source/misc/SvXMLAutoCorrectImport.hxx
new file mode 100644
index 0000000000..961e6963d7
--- /dev/null
+++ b/editeng/source/misc/SvXMLAutoCorrectImport.hxx
@@ -0,0 +1,113 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <sot/storage.hxx>
+#include <xmloff/xmlictxt.hxx>
+#include <xmloff/xmlimp.hxx>
+#include <editeng/svxacorr.hxx>
+
+class SvXMLAutoCorrectImport : public SvXMLImport
+{
+protected:
+
+ // This method is called after the namespace map has been updated, but
+ // before a context for the current element has been pushed.
+ virtual SvXMLImportContext *CreateFastContext( sal_Int32 Element,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override;
+
+public:
+ SvxAutocorrWordList *pAutocorr_List;
+ SvxAutoCorrect &rAutoCorrect;
+ css::uno::Reference < css::embed::XStorage > xStorage;
+
+ SvXMLAutoCorrectImport(
+ const css::uno::Reference< css::uno::XComponentContext > & xContext,
+ SvxAutocorrWordList *pNewAutocorr_List,
+ SvxAutoCorrect &rNewAutoCorrect,
+ css::uno::Reference < css::embed::XStorage > xNewStorage);
+
+ virtual ~SvXMLAutoCorrectImport() noexcept override;
+};
+
+class SvXMLWordListContext : public SvXMLImportContext
+{
+private:
+ SvXMLAutoCorrectImport & rLocalRef;
+public:
+ SvXMLWordListContext ( SvXMLAutoCorrectImport& rImport );
+
+ virtual css::uno::Reference<XFastContextHandler> SAL_CALL createFastChildContext( sal_Int32 Element,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override;
+
+ virtual ~SvXMLWordListContext() override;
+};
+
+class SvXMLWordContext : public SvXMLImportContext
+{
+public:
+ SvXMLWordContext ( SvXMLAutoCorrectImport& rImport,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList );
+
+ virtual ~SvXMLWordContext() override;
+};
+
+
+class SvXMLExceptionListImport : public SvXMLImport
+{
+protected:
+
+ // This method is called after the namespace map has been updated, but
+ // before a context for the current element has been pushed.
+ virtual SvXMLImportContext *CreateFastContext( sal_Int32 Element, const css::uno::Reference<
+ css::xml::sax::XFastAttributeList > & xAttrList ) override;
+public:
+ SvStringsISortDtor &rList;
+
+ SvXMLExceptionListImport(
+ const css::uno::Reference< css::uno::XComponentContext > & xContext,
+ SvStringsISortDtor & rNewList );
+
+ virtual ~SvXMLExceptionListImport() noexcept override;
+};
+
+class SvXMLExceptionListContext : public SvXMLImportContext
+{
+private:
+ SvXMLExceptionListImport & rLocalRef;
+public:
+ SvXMLExceptionListContext ( SvXMLExceptionListImport& rImport );
+
+ virtual css::uno::Reference<XFastContextHandler> SAL_CALL createFastChildContext( sal_Int32 Element,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override;
+
+ virtual ~SvXMLExceptionListContext() override;
+};
+
+class SvXMLExceptionContext : public SvXMLImportContext
+{
+public:
+ SvXMLExceptionContext ( SvXMLExceptionListImport& rImport,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList );
+
+ virtual ~SvXMLExceptionContext() override;
+};
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/misc/SvXMLAutoCorrectTokenHandler.cxx b/editeng/source/misc/SvXMLAutoCorrectTokenHandler.cxx
new file mode 100644
index 0000000000..4bdadcdcde
--- /dev/null
+++ b/editeng/source/misc/SvXMLAutoCorrectTokenHandler.cxx
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "SvXMLAutoCorrectTokenHandler.hxx"
+#include <xmloff/xmltoken.hxx>
+#if defined __clang__
+#if __has_warning("-Wdeprecated-register")
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-register"
+#endif
+#endif
+#include <tokens.cxx>
+#if defined __clang__
+#if __has_warning("-Wdeprecated-register")
+#pragma GCC diagnostic pop
+#endif
+#endif
+
+using namespace css::uno;
+using namespace ::xmloff::token;
+
+SvXMLAutoCorrectTokenHandler::SvXMLAutoCorrectTokenHandler()
+{
+}
+
+SvXMLAutoCorrectTokenHandler::~SvXMLAutoCorrectTokenHandler()
+{
+}
+
+sal_Int32 SAL_CALL SvXMLAutoCorrectTokenHandler::getTokenFromUTF8( const Sequence< sal_Int8 >& Identifier )
+{
+ return getTokenDirect( reinterpret_cast< const char* >( Identifier.getConstArray() ), Identifier.getLength() );
+}
+
+Sequence< sal_Int8 > SAL_CALL SvXMLAutoCorrectTokenHandler::getUTF8Identifier( sal_Int32 )
+{
+ return Sequence< sal_Int8 >();
+}
+
+sal_Int32 SvXMLAutoCorrectTokenHandler::getTokenDirect( const char *pTag, sal_Int32 nLength ) const
+{
+ if( !nLength )
+ nLength = strlen( pTag );
+ const struct xmltoken* pToken = Perfect_Hash::in_word_set( pTag, nLength );
+ return pToken ? pToken->nToken : XML_TOKEN_INVALID;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/misc/SvXMLAutoCorrectTokenHandler.hxx b/editeng/source/misc/SvXMLAutoCorrectTokenHandler.hxx
new file mode 100644
index 0000000000..df913dbe6b
--- /dev/null
+++ b/editeng/source/misc/SvXMLAutoCorrectTokenHandler.hxx
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <com/sun/star/xml/sax/FastToken.hpp>
+#include <sax/fastattribs.hxx>
+
+using namespace css::xml::sax;
+using namespace ::xmloff::token;
+
+enum SvXMLAutoCorrectToken : sal_Int32
+{
+ NAMESPACE = FastToken::NAMESPACE | XML_NAMESPACE_BLOCKLIST, //65553
+ ABBREVIATED_NAME = FastToken::NAMESPACE | XML_NAMESPACE_BLOCKLIST | XML_ABBREVIATED_NAME, //65655
+ BLOCK = FastToken::NAMESPACE | XML_NAMESPACE_BLOCKLIST | XML_BLOCK, //65791
+ BLOCKLIST = FastToken::NAMESPACE | XML_NAMESPACE_BLOCKLIST | XML_BLOCK_LIST, //65792
+ NAME = FastToken::NAMESPACE | XML_NAMESPACE_BLOCKLIST | XML_NAME //66737
+};
+
+class SvXMLAutoCorrectTokenHandler :
+ public sax_fastparser::FastTokenHandlerBase
+{
+public:
+ explicit SvXMLAutoCorrectTokenHandler();
+ virtual ~SvXMLAutoCorrectTokenHandler() override;
+
+ //XFastTokenHandler
+ virtual sal_Int32 SAL_CALL getTokenFromUTF8( const css::uno::Sequence< sal_Int8 >& Identifier ) override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getUTF8Identifier( sal_Int32 Token ) override;
+
+ // Much faster direct C++ shortcut to the method that matters
+ virtual sal_Int32 getTokenDirect( const char *pToken, sal_Int32 nLength ) const override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/misc/acorrcfg.cxx b/editeng/source/misc/acorrcfg.cxx
new file mode 100644
index 0000000000..616d75c696
--- /dev/null
+++ b/editeng/source/misc/acorrcfg.cxx
@@ -0,0 +1,698 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/processfactory.hxx>
+#include <editeng/acorrcfg.hxx>
+#include <o3tl/any.hxx>
+#include <tools/debug.hxx>
+#include <tools/urlobj.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <ucbhelper/content.hxx>
+#include <unotools/pathoptions.hxx>
+#include <unotools/ucbhelper.hxx>
+#include <svtools/langtab.hxx>
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+
+#include <editeng/svxacorr.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+
+using namespace utl;
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+
+
+/** An autocorrection file dropped into such directory may create a language
+ list entry if one didn't exist already.
+ */
+static void scanAutoCorrectDirForLanguageTags( const OUString& rURL )
+{
+ // Silently ignore all errors.
+ try
+ {
+ ::ucbhelper::Content aContent( rURL,
+ uno::Reference<ucb::XCommandEnvironment>(), comphelper::getProcessComponentContext());
+ if (aContent.isFolder())
+ {
+ // Title is file name here.
+ uno::Reference<sdbc::XResultSet> xResultSet = aContent.createCursor(
+ {"Title"}, ::ucbhelper::INCLUDE_DOCUMENTS_ONLY);
+ uno::Reference<sdbc::XRow> xRow( xResultSet, UNO_QUERY);
+ if (xResultSet.is() && xRow.is())
+ {
+ while (xResultSet->next())
+ {
+ try
+ {
+ const OUString aTitle( xRow->getString(1));
+ if (aTitle.getLength() <= 9 || !(aTitle.startsWith("acor_") && aTitle.endsWith(".dat")))
+ continue;
+
+ const OUString aBcp47( aTitle.copy( 5, aTitle.getLength() - 9));
+ OUString aCanonicalized;
+ // Ignore invalid langtags and canonicalize for good,
+ // allow private-use tags.
+ if (!LanguageTag::isValidBcp47( aBcp47, &aCanonicalized))
+ continue;
+
+ const LanguageTag aLanguageTag( aCanonicalized);
+ if (SvtLanguageTable::HasLanguageType( aLanguageTag.getLanguageType()))
+ continue;
+
+ // Insert language(-script)-only tags only if there is
+ // no known matching fallback locale, otherwise we'd
+ // end up with unwanted entries where a language
+ // autocorrection file covers several locales. We do
+ // know a few art-x-... though so exclude those and any
+ // other private-use tag (which should not fallback,
+ // but avoid).
+ if (aLanguageTag.getCountry().isEmpty()
+ && LanguageTag::isValidBcp47( aCanonicalized, nullptr,
+ LanguageTag::PrivateUse::DISALLOW))
+ {
+ LanguageTag aFallback( aLanguageTag);
+ aFallback.makeFallback();
+ if (aFallback.getLanguageAndScript() == aLanguageTag.getLanguageAndScript())
+ continue;
+ }
+
+ // Finally add this one.
+ SvtLanguageTable::AddLanguageTag( aLanguageTag);
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("editeng", "Unable to get a directory entry from '" << rURL << "'");
+ }
+ }
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("editeng", "Unable to iterate directory '" << rURL << "'");
+ }
+}
+
+SvxAutoCorrCfg::SvxAutoCorrCfg() :
+ aBaseConfig(*this),
+ aSwConfig(*this),
+ bFileRel(true),
+ bNetRel(true),
+ bAutoTextTip(true),
+ bAutoTextPreview(false),
+ bAutoFmtByInput(true),
+ bSearchInAllCategories(false)
+{
+ SvtPathOptions aPathOpt;
+ OUString sSharePath, sUserPath;
+ OUString const & sAutoPath( aPathOpt.GetAutoCorrectPath() );
+
+ sSharePath = sAutoPath.getToken(0, ';');
+ sUserPath = sAutoPath.getToken(1, ';');
+
+ //fdo#67743 ensure the userdir exists so that any later attempt to copy the
+ //shared autocorrect file into the user dir will succeed
+ ::ucbhelper::Content aContent;
+ Reference < ucb::XCommandEnvironment > xEnv;
+ ::utl::UCBContentHelper::ensureFolder(comphelper::getProcessComponentContext(), xEnv, sUserPath, aContent);
+
+ for( OUString* pS : { &sSharePath, &sUserPath } )
+ {
+ INetURLObject aPath( *pS );
+ scanAutoCorrectDirForLanguageTags( aPath.GetMainURL(INetURLObject::DecodeMechanism::ToIUri));
+ aPath.insertName(u"acor");
+ *pS = aPath.GetMainURL(INetURLObject::DecodeMechanism::ToIUri);
+ }
+ pAutoCorrect.reset( new SvxAutoCorrect( sSharePath, sUserPath ) );
+
+ aBaseConfig.Load(true);
+ aSwConfig.Load(true);
+}
+
+SvxAutoCorrCfg::~SvxAutoCorrCfg()
+{
+}
+
+void SvxAutoCorrCfg::SetAutoCorrect(SvxAutoCorrect *const pNew)
+{
+ if (pNew != pAutoCorrect.get())
+ {
+ if (pNew && (pAutoCorrect->GetFlags() != pNew->GetFlags()))
+ {
+ aBaseConfig.SetModified();
+ aSwConfig.SetModified();
+ }
+ pAutoCorrect.reset( pNew );
+ }
+}
+
+Sequence<OUString> SvxBaseAutoCorrCfg::GetPropertyNames()
+{
+ static const char* aPropNames[] =
+ {
+ "Exceptions/TwoCapitalsAtStart", // 0
+ "Exceptions/CapitalAtStartSentence", // 1
+ "UseReplacementTable", // 2
+ "TwoCapitalsAtStart", // 3
+ "CapitalAtStartSentence", // 4
+ "ChangeUnderlineWeight", // 5
+ "SetInetAttribute", // 6
+ "ChangeOrdinalNumber", // 7
+ "AddNonBreakingSpace", // 8
+ "ChangeDash", // 9
+ "RemoveDoubleSpaces", // 10
+ "ReplaceSingleQuote", // 11
+ "SingleQuoteAtStart", // 12
+ "SingleQuoteAtEnd", // 13
+ "ReplaceDoubleQuote", // 14
+ "DoubleQuoteAtStart", // 15
+ "DoubleQuoteAtEnd", // 16
+ "CorrectAccidentalCapsLock", // 17
+ "TransliterateRTL", // 18
+ "ChangeAngleQuotes", // 19
+ "SetDOIAttribute", // 20
+ };
+ const int nCount = 21;
+ Sequence<OUString> aNames(nCount);
+ OUString* pNames = aNames.getArray();
+ for(int i = 0; i < nCount; i++)
+ pNames[i] = OUString::createFromAscii(aPropNames[i]);
+ return aNames;
+}
+
+void SvxBaseAutoCorrCfg::Load(bool bInit)
+{
+ Sequence<OUString> aNames = GetPropertyNames();
+ Sequence<Any> aValues = GetProperties(aNames);
+ if(bInit)
+ EnableNotification(aNames);
+ const Any* pValues = aValues.getConstArray();
+ DBG_ASSERT(aValues.getLength() == aNames.getLength(), "GetProperties failed");
+ if(aValues.getLength() != aNames.getLength())
+ return;
+
+ ACFlags nFlags = ACFlags::NONE; // default all off
+ sal_Int32 nTemp = 0;
+ for(int nProp = 0; nProp < aNames.getLength(); nProp++)
+ {
+ if(pValues[nProp].hasValue())
+ {
+ switch(nProp)
+ {
+ case 0:
+ if(*o3tl::doAccess<bool>(pValues[nProp]))
+ nFlags |= ACFlags::SaveWordCplSttLst;
+ break;//"Exceptions/TwoCapitalsAtStart",
+ case 1:
+ if(*o3tl::doAccess<bool>(pValues[nProp]))
+ nFlags |= ACFlags::SaveWordWordStartLst;
+ break;//"Exceptions/CapitalAtStartSentence",
+ case 2:
+ if(*o3tl::doAccess<bool>(pValues[nProp]))
+ nFlags |= ACFlags::Autocorrect;
+ break;//"UseReplacementTable",
+ case 3:
+ if(*o3tl::doAccess<bool>(pValues[nProp]))
+ nFlags |= ACFlags::CapitalStartWord;
+ break;//"TwoCapitalsAtStart",
+ case 4:
+ if(*o3tl::doAccess<bool>(pValues[nProp]))
+ nFlags |= ACFlags::CapitalStartSentence;
+ break;//"CapitalAtStartSentence",
+ case 5:
+ if(*o3tl::doAccess<bool>(pValues[nProp]))
+ nFlags |= ACFlags::ChgWeightUnderl;
+ break;//"ChangeUnderlineWeight",
+ case 6:
+ if(*o3tl::doAccess<bool>(pValues[nProp]))
+ nFlags |= ACFlags::SetINetAttr;
+ break;//"SetInetAttribute",
+ case 7:
+ if(*o3tl::doAccess<bool>(pValues[nProp]))
+ nFlags |= ACFlags::ChgOrdinalNumber;
+ break;//"ChangeOrdinalNumber",
+ case 8:
+ if(*o3tl::doAccess<bool>(pValues[nProp]))
+ nFlags |= ACFlags::AddNonBrkSpace;
+ break;//"AddNonBreakingSpace"
+ case 9:
+ if(*o3tl::doAccess<bool>(pValues[nProp]))
+ nFlags |= ACFlags::ChgToEnEmDash;
+ break;//"ChangeDash",
+ case 10:
+ if(*o3tl::doAccess<bool>(pValues[nProp]))
+ nFlags |= ACFlags::IgnoreDoubleSpace;
+ break;//"RemoveDoubleSpaces",
+ case 11:
+ if(*o3tl::doAccess<bool>(pValues[nProp]))
+ nFlags |= ACFlags::ChgSglQuotes;
+ break;//"ReplaceSingleQuote",
+ case 12:
+ pValues[nProp] >>= nTemp;
+ rParent.pAutoCorrect->SetStartSingleQuote(
+ sal::static_int_cast< sal_Unicode >( nTemp ) );
+ break;//"SingleQuoteAtStart",
+ case 13:
+ pValues[nProp] >>= nTemp;
+ rParent.pAutoCorrect->SetEndSingleQuote(
+ sal::static_int_cast< sal_Unicode >( nTemp ) );
+ break;//"SingleQuoteAtEnd",
+ case 14:
+ if(*o3tl::doAccess<bool>(pValues[nProp]))
+ nFlags |= ACFlags::ChgQuotes;
+ break;//"ReplaceDoubleQuote",
+ case 15:
+ pValues[nProp] >>= nTemp;
+ rParent.pAutoCorrect->SetStartDoubleQuote(
+ sal::static_int_cast< sal_Unicode >( nTemp ) );
+ break;//"DoubleQuoteAtStart",
+ case 16:
+ pValues[nProp] >>= nTemp;
+ rParent.pAutoCorrect->SetEndDoubleQuote(
+ sal::static_int_cast< sal_Unicode >( nTemp ) );
+ break;//"DoubleQuoteAtEnd"
+ case 17:
+ if(*o3tl::doAccess<bool>(pValues[nProp]))
+ nFlags |= ACFlags::CorrectCapsLock;
+ break;//"CorrectAccidentalCapsLock"
+ case 18:
+ if(*o3tl::doAccess<bool>(pValues[nProp]))
+ nFlags |= ACFlags::TransliterateRTL;
+ break;//"TransliterateRTL"
+ case 19:
+ if(*o3tl::doAccess<bool>(pValues[nProp]))
+ nFlags |= ACFlags::ChgAngleQuotes;
+ break;//"ChangeAngleQuotes"
+ case 20:
+ if(*o3tl::doAccess<bool>(pValues[nProp]))
+ nFlags |= ACFlags::SetDOIAttr;
+ break;//"SetDOIAttr",
+ }
+ }
+ }
+ if( nFlags != ACFlags::NONE )
+ rParent.pAutoCorrect->SetAutoCorrFlag( nFlags );
+ rParent.pAutoCorrect->SetAutoCorrFlag( ( static_cast<ACFlags>(0xffff) & ~nFlags ), false );
+}
+
+SvxBaseAutoCorrCfg::SvxBaseAutoCorrCfg(SvxAutoCorrCfg& rPar) :
+ utl::ConfigItem("Office.Common/AutoCorrect"),
+ rParent(rPar)
+{
+}
+
+SvxBaseAutoCorrCfg::~SvxBaseAutoCorrCfg()
+{
+}
+
+void SvxBaseAutoCorrCfg::ImplCommit()
+{
+ const ACFlags nFlags = rParent.pAutoCorrect->GetFlags();
+ PutProperties(
+ GetPropertyNames(),
+ {css::uno::Any(bool(nFlags & ACFlags::SaveWordCplSttLst)),
+ // "Exceptions/TwoCapitalsAtStart"
+ css::uno::Any(bool(nFlags & ACFlags::SaveWordWordStartLst)),
+ // "Exceptions/CapitalAtStartSentence"
+ css::uno::Any(bool(nFlags & ACFlags::Autocorrect)), // "UseReplacementTable"
+ css::uno::Any(bool(nFlags & ACFlags::CapitalStartWord)),
+ // "TwoCapitalsAtStart"
+ css::uno::Any(bool(nFlags & ACFlags::CapitalStartSentence)),
+ // "CapitalAtStartSentence"
+ css::uno::Any(bool(nFlags & ACFlags::ChgWeightUnderl)),
+ // "ChangeUnderlineWeight"
+ css::uno::Any(bool(nFlags & ACFlags::SetINetAttr)), // "SetInetAttribute"
+ css::uno::Any(bool(nFlags & ACFlags::ChgOrdinalNumber)),
+ // "ChangeOrdinalNumber"
+ css::uno::Any(bool(nFlags & ACFlags::AddNonBrkSpace)), // "AddNonBreakingSpace"
+ css::uno::Any(bool(nFlags & ACFlags::ChgToEnEmDash)), // "ChangeDash"
+ css::uno::Any(bool(nFlags & ACFlags::IgnoreDoubleSpace)),
+ // "RemoveDoubleSpaces"
+ css::uno::Any(bool(nFlags & ACFlags::ChgSglQuotes)), // "ReplaceSingleQuote"
+ css::uno::Any(sal_Int32(rParent.pAutoCorrect->GetStartSingleQuote())),
+ // "SingleQuoteAtStart"
+ css::uno::Any(sal_Int32(rParent.pAutoCorrect->GetEndSingleQuote())),
+ // "SingleQuoteAtEnd"
+ css::uno::Any(bool(nFlags & ACFlags::ChgQuotes)), // "ReplaceDoubleQuote"
+ css::uno::Any(sal_Int32(rParent.pAutoCorrect->GetStartDoubleQuote())),
+ // "DoubleQuoteAtStart"
+ css::uno::Any(sal_Int32(rParent.pAutoCorrect->GetEndDoubleQuote())),
+ // "DoubleQuoteAtEnd"
+ css::uno::Any(bool(nFlags & ACFlags::CorrectCapsLock)),
+ // "CorrectAccidentalCapsLock"
+ css::uno::Any(bool(nFlags & ACFlags::TransliterateRTL)),
+ // "TransliterateRTL"
+ css::uno::Any(bool(nFlags & ACFlags::ChgAngleQuotes)),
+ // "ChangeAngleQuotes"
+ css::uno::Any(bool(nFlags & ACFlags::SetDOIAttr)), // "SetDOIAttribute"
+ });
+}
+
+void SvxBaseAutoCorrCfg::Notify( const Sequence<OUString>& /* aPropertyNames */)
+{
+ Load(false);
+}
+
+Sequence<OUString> SvxSwAutoCorrCfg::GetPropertyNames()
+{
+ static const char* aPropNames[] =
+ {
+ "Text/FileLinks", // 0
+ "Text/InternetLinks", // 1
+ "Text/ShowPreview", // 2
+ "Text/ShowToolTip", // 3
+ "Text/SearchInAllCategories", // 4
+ "Format/Option/UseReplacementTable", // 5
+ "Format/Option/TwoCapitalsAtStart", // 6
+ "Format/Option/CapitalAtStartSentence", // 7
+ "Format/Option/ChangeUnderlineWeight", // 8
+ "Format/Option/SetInetAttribute", // 9
+ "Format/Option/ChangeOrdinalNumber", //10
+ "Format/Option/AddNonBreakingSpace", //11
+ "Format/Option/ChangeDash", //12
+ "Format/Option/DelEmptyParagraphs", //13
+ "Format/Option/ReplaceUserStyle", //14
+ "Format/Option/ChangeToBullets/Enable", //15
+ "Format/Option/ChangeToBullets/SpecialCharacter/Char", //16
+ "Format/Option/ChangeToBullets/SpecialCharacter/Font", //17
+ "Format/Option/ChangeToBullets/SpecialCharacter/FontFamily", //18
+ "Format/Option/ChangeToBullets/SpecialCharacter/FontCharset", //19
+ "Format/Option/ChangeToBullets/SpecialCharacter/FontPitch", //20
+ "Format/Option/CombineParagraphs", //21
+ "Format/Option/CombineValue", //22
+ "Format/Option/DelSpacesAtStartEnd", //23
+ "Format/Option/DelSpacesBetween", //24
+ "Format/ByInput/Enable", //25
+ "Format/ByInput/ChangeDash", //26
+ "Format/ByInput/ApplyNumbering/Enable", //27
+ "Format/ByInput/ChangeToBorders", //28
+ "Format/ByInput/ChangeToTable", //29
+ "Format/ByInput/ReplaceStyle", //30
+ "Format/ByInput/DelSpacesAtStartEnd", //31
+ "Format/ByInput/DelSpacesBetween", //32
+ "Completion/Enable", //33
+ "Completion/MinWordLen", //34
+ "Completion/MaxListLen", //35
+ "Completion/CollectWords", //36
+ "Completion/EndlessList", //37
+ "Completion/AppendBlank", //38
+ "Completion/ShowAsTip", //39
+ "Completion/AcceptKey", //40
+ "Completion/KeepList", //41
+ "Format/ByInput/ApplyNumbering/SpecialCharacter/Char", //42
+ "Format/ByInput/ApplyNumbering/SpecialCharacter/Font", //43
+ "Format/ByInput/ApplyNumbering/SpecialCharacter/FontFamily", //44
+ "Format/ByInput/ApplyNumbering/SpecialCharacter/FontCharset", //45
+ "Format/ByInput/ApplyNumbering/SpecialCharacter/FontPitch", //46
+ "Format/Option/SetDOIAttribute", //47
+ "Format/ByInput/ApplyBulletsAfterSpace", //48
+ };
+ const int nCount = 49;
+ Sequence<OUString> aNames(nCount);
+ OUString* pNames = aNames.getArray();
+ for(int i = 0; i < nCount; i++)
+ pNames[i] = OUString::createFromAscii(aPropNames[i]);
+ return aNames;
+}
+
+void SvxSwAutoCorrCfg::Load(bool bInit)
+{
+ Sequence<OUString> aNames = GetPropertyNames();
+ Sequence<Any> aValues = GetProperties(aNames);
+ if(bInit)
+ EnableNotification(aNames);
+ const Any* pValues = aValues.getConstArray();
+ DBG_ASSERT(aValues.getLength() == aNames.getLength(), "GetProperties failed");
+ if(aValues.getLength() != aNames.getLength())
+ return;
+
+ SvxSwAutoFormatFlags& rSwFlags = rParent.pAutoCorrect->GetSwFlags();
+ for(int nProp = 0; nProp < aNames.getLength(); nProp++)
+ {
+ if(pValues[nProp].hasValue())
+ {
+ switch(nProp)
+ {
+ case 0: rParent.bFileRel = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Text/FileLinks",
+ case 1: rParent.bNetRel = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Text/InternetLinks",
+ case 2: rParent.bAutoTextPreview = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Text/ShowPreview",
+ case 3: rParent.bAutoTextTip = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Text/ShowToolTip",
+ case 4: rParent.bSearchInAllCategories = *o3tl::doAccess<bool>(pValues[nProp]); break; //"Text/SearchInAllCategories"
+ case 5: rSwFlags.bAutoCorrect = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Format/Option/UseReplacementTable",
+ case 6: rSwFlags.bCapitalStartSentence = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Format/Option/TwoCapitalsAtStart",
+ case 7: rSwFlags.bCapitalStartWord = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Format/Option/CapitalAtStartSentence",
+ case 8: rSwFlags.bChgWeightUnderl = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Format/Option/ChangeUnderlineWeight",
+ case 9: rSwFlags.bSetINetAttr = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Format/Option/SetInetAttribute",
+ case 10: rSwFlags.bChgOrdinalNumber = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Format/Option/ChangeOrdinalNumber",
+ case 11: rSwFlags.bAddNonBrkSpace = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Format/Option/AddNonBreakingSpace",
+// it doesn't exist here - the common flags are used for that -> LM
+// case 12: rSwFlags.bChgToEnEmDash = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Format/Option/ChangeDash",
+ case 13: rSwFlags.bDelEmptyNode = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Format/Option/DelEmptyParagraphs",
+ case 14: rSwFlags.bChgUserColl = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Format/Option/ReplaceUserStyle",
+ case 15: rSwFlags.bChgEnumNum = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Format/Option/ChangeToBullets/Enable",
+ case 16:
+ {
+ sal_Int32 nVal = 0; pValues[nProp] >>= nVal;
+ rSwFlags.cBullet =
+ sal::static_int_cast< sal_Unicode >(nVal);
+ }
+ break; // "Format/Option/ChangeToBullets/SpecialCharacter/Char",
+ case 17:
+ {
+ OUString sTemp; pValues[nProp] >>= sTemp;
+ rSwFlags.aBulletFont.SetFamilyName(sTemp);
+ }
+ break; // "Format/Option/ChangeToBullets/SpecialCharacter/Font",
+ case 18:
+ {
+ sal_Int32 nVal = 0; pValues[nProp] >>= nVal;
+ rSwFlags.aBulletFont.SetFamily(FontFamily(nVal));
+ }
+ break; // "Format/Option/ChangeToBullets/SpecialCharacter/FontFamily",
+ case 19:
+ {
+ sal_Int32 nVal = 0; pValues[nProp] >>= nVal;
+ rSwFlags.aBulletFont.SetCharSet(rtl_TextEncoding(nVal));
+ }
+ break; // "Format/Option/ChangeToBullets/SpecialCharacter/FontCharset",
+ case 20:
+ {
+ sal_Int32 nVal = 0; pValues[nProp] >>= nVal;
+ rSwFlags.aBulletFont.SetPitch(FontPitch(nVal));
+ }
+ break; // "Format/Option/ChangeToBullets/SpecialCharacter/FontPitch",
+ case 21: rSwFlags.bRightMargin = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Format/Option/CombineParagraphs",
+ case 22:
+ {
+ sal_Int32 nVal = 0; pValues[nProp] >>= nVal;
+ rSwFlags.nRightMargin =
+ sal::static_int_cast< sal_uInt8 >(nVal);
+ }
+ break; // "Format/Option/CombineValue",
+ case 23: rSwFlags.bAFormatDelSpacesAtSttEnd = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Format/Option/DelSpacesAtStartEnd",
+ case 24: rSwFlags.bAFormatDelSpacesBetweenLines = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Format/Option/DelSpacesBetween",
+ case 25: rParent.bAutoFmtByInput = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Format/ByInput/Enable",
+ case 26: rSwFlags.bChgToEnEmDash = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Format/ByInput/ChangeDash",
+ case 27: rSwFlags.bSetNumRule = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Format/ByInput/ApplyNumbering/Enable",
+ case 28: rSwFlags.bSetBorder = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Format/ByInput/ChangeToBorders",
+ case 29: rSwFlags.bCreateTable = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Format/ByInput/ChangeToTable",
+ case 30: rSwFlags.bReplaceStyles = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Format/ByInput/ReplaceStyle",
+ case 31: rSwFlags.bAFormatByInpDelSpacesAtSttEnd = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Format/ByInput/DelSpacesAtStartEnd",
+ case 32: rSwFlags.bAFormatByInpDelSpacesBetweenLines = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Format/ByInput/DelSpacesBetween",
+ case 33: rSwFlags.bAutoCompleteWords = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Completion/Enable",
+ case 34:
+ {
+ sal_Int32 nVal = 0; pValues[nProp] >>= nVal;
+ rSwFlags.nAutoCmpltWordLen =
+ sal::static_int_cast< sal_uInt16 >(nVal);
+ }
+ break; // "Completion/MinWordLen",
+ case 35:
+ {
+ sal_Int32 nVal = 0; pValues[nProp] >>= nVal;
+ rSwFlags.nAutoCmpltListLen =
+ sal::static_int_cast< sal_uInt32 >(nVal);
+ }
+ break; // "Completion/MaxListLen",
+ case 36: rSwFlags.bAutoCmpltCollectWords = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Completion/CollectWords",
+ case 37: rSwFlags.bAutoCmpltEndless = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Completion/EndlessList",
+ case 38: rSwFlags.bAutoCmpltAppendBlank = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Completion/AppendBlank",
+ case 39: rSwFlags.bAutoCmpltShowAsTip = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Completion/ShowAsTip",
+ case 40:
+ {
+ sal_Int32 nVal = 0; pValues[nProp] >>= nVal;
+ rSwFlags.nAutoCmpltExpandKey =
+ sal::static_int_cast< sal_uInt16 >(nVal);
+ }
+ break; // "Completion/AcceptKey"
+ case 41 :rSwFlags.bAutoCmpltKeepList = *o3tl::doAccess<bool>(pValues[nProp]); break;//"Completion/KeepList"
+ case 42 :
+ {
+ sal_Int32 nVal = 0; pValues[nProp] >>= nVal;
+ rSwFlags.cByInputBullet =
+ sal::static_int_cast< sal_Unicode >(nVal);
+ }
+ break;// "Format/ByInput/ApplyNumbering/SpecialCharacter/Char",
+ case 43 :
+ {
+ OUString sTemp; pValues[nProp] >>= sTemp;
+ rSwFlags.aByInputBulletFont.SetFamilyName(sTemp);
+ }
+ break;// "Format/ByInput/ApplyNumbering/SpecialCharacter/Font",
+ case 44 :
+ {
+ sal_Int32 nVal = 0; pValues[nProp] >>= nVal;
+ rSwFlags.aByInputBulletFont.SetFamily(FontFamily(nVal));
+ }
+ break;// "Format/ByInput/ApplyNumbering/SpecialCharacter/FontFamily",
+ case 45 :
+ {
+ sal_Int32 nVal = 0; pValues[nProp] >>= nVal;
+ rSwFlags.aByInputBulletFont.SetCharSet(rtl_TextEncoding(nVal));
+ }
+ break;// "Format/ByInput/ApplyNumbering/SpecialCharacter/FontCharset",
+ case 46 :
+ {
+ sal_Int32 nVal = 0; pValues[nProp] >>= nVal;
+ rSwFlags.aByInputBulletFont.SetPitch(FontPitch(nVal));
+ }
+ break;// "Format/ByInput/ApplyNumbering/SpecialCharacter/FontPitch",
+ case 47: rSwFlags.bSetDOIAttr = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Format/Option/SetDOIAttribute",
+ case 48 : rSwFlags.bSetNumRuleAfterSpace = *o3tl::doAccess<bool>(pValues[nProp]); break; // "Format/ByInput/ApplyNumberingAfterSpace",
+ }
+ }
+ }
+}
+
+SvxSwAutoCorrCfg::SvxSwAutoCorrCfg(SvxAutoCorrCfg& rPar) :
+ utl::ConfigItem("Office.Writer/AutoFunction"),
+ rParent(rPar)
+{
+}
+
+SvxSwAutoCorrCfg::~SvxSwAutoCorrCfg()
+{
+}
+
+void SvxSwAutoCorrCfg::ImplCommit()
+{
+ SvxSwAutoFormatFlags& rSwFlags = rParent.pAutoCorrect->GetSwFlags();
+ PutProperties(
+ GetPropertyNames(),
+ {css::uno::Any(rParent.bFileRel), // "Text/FileLinks"
+ css::uno::Any(rParent.bNetRel), // "Text/InternetLinks"
+ css::uno::Any(rParent.bAutoTextPreview), // "Text/ShowPreview"
+ css::uno::Any(rParent.bAutoTextTip), // "Text/ShowToolTip"
+ css::uno::Any(rParent.bSearchInAllCategories),
+ // "Text/SearchInAllCategories"
+ css::uno::Any(rSwFlags.bAutoCorrect),
+ // "Format/Option/UseReplacementTable"
+ css::uno::Any(rSwFlags.bCapitalStartSentence),
+ // "Format/Option/TwoCapitalsAtStart"
+ css::uno::Any(rSwFlags.bCapitalStartWord),
+ // "Format/Option/CapitalAtStartSentence"
+ css::uno::Any(rSwFlags.bChgWeightUnderl),
+ // "Format/Option/ChangeUnderlineWeight"
+ css::uno::Any(rSwFlags.bSetINetAttr),
+ // "Format/Option/SetInetAttribute"
+ css::uno::Any(rSwFlags.bChgOrdinalNumber),
+ // "Format/Option/ChangeOrdinalNumber"
+ css::uno::Any(rSwFlags.bAddNonBrkSpace),
+ // "Format/Option/AddNonBreakingSpace"
+ css::uno::Any(true),
+ // "Format/Option/ChangeDash"; it doesn't exist here - the common
+ // flags are used for that -> LM
+ css::uno::Any(rSwFlags.bDelEmptyNode),
+ // "Format/Option/DelEmptyParagraphs"
+ css::uno::Any(rSwFlags.bChgUserColl),
+ // "Format/Option/ReplaceUserStyle"
+ css::uno::Any(rSwFlags.bChgEnumNum),
+ // "Format/Option/ChangeToBullets/Enable"
+ css::uno::Any(sal_Int32(rSwFlags.cBullet)),
+ // "Format/Option/ChangeToBullets/SpecialCharacter/Char"
+ css::uno::Any(rSwFlags.aBulletFont.GetFamilyName()),
+ // "Format/Option/ChangeToBullets/SpecialCharacter/Font"
+ css::uno::Any(sal_Int32(rSwFlags.aBulletFont.GetFamilyType())),
+ // "Format/Option/ChangeToBullets/SpecialCharacter/FontFamily"
+ css::uno::Any(sal_Int32(rSwFlags.aBulletFont.GetCharSet())),
+ // "Format/Option/ChangeToBullets/SpecialCharacter/FontCharset"
+ css::uno::Any(sal_Int32(rSwFlags.aBulletFont.GetPitch())),
+ // "Format/Option/ChangeToBullets/SpecialCharacter/FontPitch"
+ css::uno::Any(rSwFlags.bRightMargin),
+ // "Format/Option/CombineParagraphs"
+ css::uno::Any(sal_Int32(rSwFlags.nRightMargin)),
+ // "Format/Option/CombineValue"
+ css::uno::Any(rSwFlags.bAFormatDelSpacesAtSttEnd),
+ // "Format/Option/DelSpacesAtStartEnd"
+ css::uno::Any(rSwFlags.bAFormatDelSpacesBetweenLines),
+ // "Format/Option/DelSpacesBetween"
+ css::uno::Any(rParent.bAutoFmtByInput), // "Format/ByInput/Enable"
+ css::uno::Any(rSwFlags.bChgToEnEmDash), // "Format/ByInput/ChangeDash"
+ css::uno::Any(rSwFlags.bSetNumRule),
+ // "Format/ByInput/ApplyNumbering/Enable"
+ css::uno::Any(rSwFlags.bSetBorder), // "Format/ByInput/ChangeToBorders"
+ css::uno::Any(rSwFlags.bCreateTable), // "Format/ByInput/ChangeToTable"
+ css::uno::Any(rSwFlags.bReplaceStyles),
+ // "Format/ByInput/ReplaceStyle"
+ css::uno::Any(rSwFlags.bAFormatByInpDelSpacesAtSttEnd),
+ // "Format/ByInput/DelSpacesAtStartEnd"
+ css::uno::Any(rSwFlags.bAFormatByInpDelSpacesBetweenLines),
+ // "Format/ByInput/DelSpacesBetween"
+ css::uno::Any(rSwFlags.bAutoCompleteWords), // "Completion/Enable"
+ css::uno::Any(sal_Int32(rSwFlags.nAutoCmpltWordLen)),
+ // "Completion/MinWordLen"
+ css::uno::Any(sal_Int32(rSwFlags.nAutoCmpltListLen)),
+ // "Completion/MaxListLen"
+ css::uno::Any(rSwFlags.bAutoCmpltCollectWords),
+ // "Completion/CollectWords"
+ css::uno::Any(rSwFlags.bAutoCmpltEndless), // "Completion/EndlessList"
+ css::uno::Any(rSwFlags.bAutoCmpltAppendBlank),
+ // "Completion/AppendBlank"
+ css::uno::Any(rSwFlags.bAutoCmpltShowAsTip), // "Completion/ShowAsTip"
+ css::uno::Any(sal_Int32(rSwFlags.nAutoCmpltExpandKey)),
+ // "Completion/AcceptKey"
+ css::uno::Any(rSwFlags.bAutoCmpltKeepList), // "Completion/KeepList"
+ css::uno::Any(sal_Int32(rSwFlags.cByInputBullet)),
+ // "Format/ByInput/ApplyNumbering/SpecialCharacter/Char"
+ css::uno::Any(rSwFlags.aByInputBulletFont.GetFamilyName()),
+ // "Format/ByInput/ApplyNumbering/SpecialCharacter/Font"
+ css::uno::Any(sal_Int32(rSwFlags.aByInputBulletFont.GetFamilyType())),
+ // "Format/ByInput/ApplyNumbering/SpecialCharacter/FontFamily"
+ css::uno::Any(sal_Int32(rSwFlags.aByInputBulletFont.GetCharSet())),
+ // "Format/ByInput/ApplyNumbering/SpecialCharacter/FontCharset"
+ css::uno::Any(sal_Int32(rSwFlags.aByInputBulletFont.GetPitch())),
+ // "Format/ByInput/ApplyNumbering/SpecialCharacter/FontPitch"
+ css::uno::Any(rSwFlags.bSetDOIAttr),
+ css::uno::Any(rSwFlags.bSetNumRuleAfterSpace), // "Format/ByInput/ApplyNumberingAfterSpace"
+ });
+ // "Format/Option/SetDOIAttribute"
+}
+
+void SvxSwAutoCorrCfg::Notify( const Sequence<OUString>& /* aPropertyNames */ )
+{
+ Load(false);
+}
+
+SvxAutoCorrCfg& SvxAutoCorrCfg::Get()
+{
+ static SvxAutoCorrCfg theSvxAutoCorrCfg;
+ return theSvxAutoCorrCfg;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/misc/edtdlg.cxx b/editeng/source/misc/edtdlg.cxx
new file mode 100644
index 0000000000..a3f4390ab0
--- /dev/null
+++ b/editeng/source/misc/edtdlg.cxx
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <editeng/edtdlg.hxx>
+
+EditAbstractDialogFactory* EditAbstractDialogFactory::Create()
+{
+ return dynamic_cast<EditAbstractDialogFactory*>(VclAbstractDialogFactory::Create());
+}
+
+EditAbstractDialogFactory::~EditAbstractDialogFactory() {}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/misc/forbiddencharacterstable.cxx b/editeng/source/misc/forbiddencharacterstable.cxx
new file mode 100644
index 0000000000..7276da584b
--- /dev/null
+++ b/editeng/source/misc/forbiddencharacterstable.cxx
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <editeng/forbiddencharacterstable.hxx>
+
+#include <unotools/localedatawrapper.hxx>
+#include <utility>
+
+SvxForbiddenCharactersTable::SvxForbiddenCharactersTable(
+ css::uno::Reference<css::uno::XComponentContext> xContext)
+ : m_xContext(std::move(xContext))
+{
+}
+
+std::shared_ptr<SvxForbiddenCharactersTable>
+SvxForbiddenCharactersTable::makeForbiddenCharactersTable(
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext)
+{
+ return std::shared_ptr<SvxForbiddenCharactersTable>(new SvxForbiddenCharactersTable(rxContext));
+}
+
+const css::i18n::ForbiddenCharacters*
+SvxForbiddenCharactersTable::GetForbiddenCharacters(LanguageType nLanguage, bool bGetDefault)
+{
+ css::i18n::ForbiddenCharacters* pForbiddenCharacters = nullptr;
+ Map::iterator it = maMap.find(nLanguage);
+ if (it != maMap.end())
+ pForbiddenCharacters = &(it->second);
+ else if (bGetDefault && m_xContext.is())
+ {
+ LocaleDataWrapper aWrapper(m_xContext, LanguageTag(nLanguage));
+ maMap[nLanguage] = aWrapper.getForbiddenCharacters();
+ pForbiddenCharacters = &maMap[nLanguage];
+ }
+ return pForbiddenCharacters;
+}
+
+void SvxForbiddenCharactersTable::SetForbiddenCharacters(
+ LanguageType nLanguage, const css::i18n::ForbiddenCharacters& rForbiddenChars)
+{
+ maMap[nLanguage] = rForbiddenChars;
+}
+
+void SvxForbiddenCharactersTable::ClearForbiddenCharacters(LanguageType nLanguage)
+{
+ maMap.erase(nLanguage);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/misc/hangulhanja.cxx b/editeng/source/misc/hangulhanja.cxx
new file mode 100644
index 0000000000..5a9a8c1034
--- /dev/null
+++ b/editeng/source/misc/hangulhanja.cxx
@@ -0,0 +1,1002 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <editeng/hangulhanja.hxx>
+#include <unotools/lingucfg.hxx>
+#include <unotools/linguprops.hxx>
+
+#include <set>
+#include <map>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/i18n/BreakIterator.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/i18n/UnicodeScript.hpp>
+#include <com/sun/star/i18n/TextConversion.hpp>
+#include <com/sun/star/i18n/XExtendedTextConversion.hpp>
+#include <com/sun/star/i18n/TextConversionType.hpp>
+#include <com/sun/star/i18n/TextConversionOption.hpp>
+#include <vcl/weld.hxx>
+#include <unotools/charclass.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <editeng/edtdlg.hxx>
+
+#define HHC HangulHanjaConversion
+
+
+namespace editeng
+{
+
+
+ using namespace ::com::sun::star;
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::i18n;
+ using namespace ::com::sun::star::i18n::TextConversionOption;
+ using namespace ::com::sun::star::i18n::TextConversionType;
+
+ class HangulHanjaConversion_Impl
+ {
+ private:
+ typedef std::set<OUString> StringBag;
+ typedef std::map<OUString, OUString> StringMap;
+
+ private:
+ StringBag m_sIgnoreList;
+ StringMap m_aChangeList;
+ static StringMap m_aRecentlyUsedList;
+
+ // general
+ VclPtr<AbstractHangulHanjaConversionDialog>
+ m_pConversionDialog; // the dialog to display for user interaction
+ weld::Widget* m_pUIParent; // the parent window for any UI we raise
+ Reference< XComponentContext >
+ m_xContext; // the service factory to use
+ Reference< XExtendedTextConversion >
+ m_xConverter; // the text conversion service
+ lang::Locale m_aSourceLocale; // the locale we're working with
+
+ // additions for Chinese simplified / traditional conversion
+ HHC::ConversionType m_eConvType; // conversion type (Hangul/Hanja, simplified/traditional Chinese,...)
+ LanguageType m_nSourceLang; // just a 'copy' of m_aSourceLocale in order to
+ // save the applications from always converting to this
+ // type in their implementations
+ LanguageType m_nTargetLang; // target language of new replacement text
+ const vcl::Font* m_pTargetFont; // target font of new replacement text
+ sal_Int32 m_nConvOptions; // text conversion options (as used by 'getConversions')
+ bool m_bIsInteractive; // specifies if the conversion requires user interaction
+ // (and likely a specialised dialog) or if it is to run
+ // automatically without any user interaction.
+ // True for Hangul / Hanja conversion
+ // False for Chinese simplified / traditional conversion
+
+ HangulHanjaConversion* m_pAntiImpl; // our "anti-impl" instance
+
+ // options
+ bool m_bByCharacter; // are we in "by character" mode currently?
+ HHC::ConversionFormat m_eConversionFormat; // the current format for the conversion
+ HHC::ConversionDirection m_ePrimaryConversionDirection; // the primary conversion direction
+ HHC::ConversionDirection m_eCurrentConversionDirection; // the primary conversion direction
+
+ //options from Hangul/Hanja Options dialog (also saved to configuration)
+ bool m_bIgnorePostPositionalWord;
+ bool m_bShowRecentlyUsedFirst;
+ bool m_bAutoReplaceUnique;
+
+ // state
+ OUString m_sCurrentPortion; // the text which we are currently working on
+ LanguageType m_nCurrentPortionLang; // language of m_sCurrentPortion found
+ sal_Int32 m_nCurrentStartIndex; // the start index within m_sCurrentPortion of the current convertible portion
+ sal_Int32 m_nCurrentEndIndex; // the end index (excluding) within m_sCurrentPortion of the current convertible portion
+ sal_Int32 m_nReplacementBaseIndex;// index which ReplaceUnit-calls need to be relative to
+ sal_Int32 m_nCurrentConversionOption;
+ sal_Int16 m_nCurrentConversionType;
+ Sequence< OUString >
+ m_aCurrentSuggestions; // the suggestions for the current unit
+ // (means for the text [m_nCurrentStartIndex, m_nCurrentEndIndex) in m_sCurrentPortion)
+ bool m_bTryBothDirections; // specifies if other conversion directions should be tried when looking for convertible characters
+
+
+ public:
+ HangulHanjaConversion_Impl(
+ weld::Widget* pUIParent,
+ const Reference< XComponentContext >& rxContext,
+ const lang::Locale& _rSourceLocale,
+ const lang::Locale& _rTargetLocale,
+ const vcl::Font* _pTargetFont,
+ sal_Int32 _nConvOptions,
+ bool _bIsInteractive,
+ HangulHanjaConversion* _pAntiImpl );
+
+ public:
+ void DoDocumentConversion( );
+
+ bool IsValid() const { return m_xConverter.is(); }
+
+ weld::Widget* GetUIParent() const { return m_pUIParent; }
+ LanguageType GetSourceLang() const { return m_nSourceLang; }
+ LanguageType GetTargetLang() const { return m_nTargetLang; }
+ const vcl::Font * GetTargetFont() const { return m_pTargetFont; }
+ sal_Int32 GetConvOptions() const { return m_nConvOptions; }
+ bool IsInteractive() const { return m_bIsInteractive; }
+
+ protected:
+ void createDialog();
+
+ /** continue with the conversion, return <TRUE/> if and only if the complete conversion is done
+ @param _bRepeatCurrentUnit
+ if <TRUE/>, an implNextConvertible will be called initially to advance to the next convertible.
+ if <FALSE/>, the method will initially work with the current convertible unit
+ */
+ bool ContinueConversion( bool _bRepeatCurrentUnit );
+
+ private:
+ DECL_LINK( OnOptionsChanged, LinkParamNone*, void );
+ DECL_LINK( OnIgnore, weld::Button&, void );
+ DECL_LINK( OnIgnoreAll, weld::Button&, void );
+ DECL_LINK( OnChange, weld::Button&, void );
+ DECL_LINK( OnChangeAll, weld::Button&, void );
+ DECL_LINK( OnByCharClicked, weld::Toggleable&, void );
+ DECL_LINK( OnConversionTypeChanged, weld::Toggleable&, void );
+ DECL_LINK( OnFind, weld::Button&, void );
+
+ /** proceed, after the current convertible has been handled
+
+ <p><b>Attention:</b>
+ When returning from this method, the dialog may have been deleted!</p>
+
+ @param _bRepeatCurrentUnit
+ will be passed to the <member>ContinueConversion</member> call
+ */
+ void implProceed( bool _bRepeatCurrentUnit );
+
+ // change the current convertible, and do _not_ proceed
+ void implChange( const OUString& _rChangeInto );
+
+ /** find the next convertible piece of text, with possibly advancing to the next portion
+
+ @see HangulHanjaConversion::GetNextPortion
+ */
+ bool implNextConvertible( bool _bRepeatUnit );
+
+ /** find the next convertible unit within the current portion
+ @param _bRepeatUnit
+ if <TRUE/>, the search will start at the beginning of the current unit,
+ if <FALSE/>, it will start at the end of the current unit
+ */
+ bool implNextConvertibleUnit( const sal_Int32 _nStartAt );
+
+ /** retrieves the next portion, with setting the index members properly
+ @return
+ <TRUE/> if and only if there is a next portion
+ */
+ bool implRetrieveNextPortion( );
+
+ /** determine the ConversionDirection for m_sCurrentPortion
+ @return
+ <FALSE/> if and only if something went wrong
+ */
+ bool implGetConversionDirectionForCurrentPortion( HHC::ConversionDirection& rDirection );
+
+ /** member m_aCurrentSuggestions and m_nCurrentEndIndex are updated according to the other settings and current dictionaries
+
+ if _bAllowSearchNextConvertibleText is true _nStartAt is used as starting point to search the next
+ convertible text portion. This may result in changing of the member m_nCurrentStartIndex additionally.
+
+ @return
+ <TRUE/> if Suggestions were found
+ */
+ bool implUpdateSuggestions( const bool _bAllowSearchNextConvertibleText=false, const sal_Int32 _nStartAt=-1 );
+
+ /** reads the options from Hangul/Hanja Options dialog that are saved to configuration
+ */
+ void implReadOptionsFromConfiguration();
+
+ /** get the string currently considered to be replaced or ignored
+ */
+ OUString GetCurrentUnit() const;
+
+ /** read options from configuration, update suggestion list and dialog content
+ */
+ void implUpdateData();
+
+ /** get the conversion direction dependent from m_eConvType and m_eCurrentConversionDirection
+ in case of switching the direction is allowed this can be triggered with parameter bSwitchDirection
+ */
+ sal_Int16 implGetConversionType( bool bSwitchDirection=false ) const;
+ };
+
+ HangulHanjaConversion_Impl::StringMap HangulHanjaConversion_Impl::m_aRecentlyUsedList = HangulHanjaConversion_Impl::StringMap();
+
+ HangulHanjaConversion_Impl::HangulHanjaConversion_Impl( weld::Widget* pUIParent,
+ const Reference< XComponentContext >& rxContext,
+ const lang::Locale& _rSourceLocale,
+ const lang::Locale& _rTargetLocale,
+ const vcl::Font* _pTargetFont,
+ sal_Int32 _nOptions,
+ bool _bIsInteractive,
+ HangulHanjaConversion* _pAntiImpl )
+ : m_pUIParent( pUIParent )
+ , m_xContext( rxContext )
+ , m_aSourceLocale( _rSourceLocale )
+ , m_nSourceLang( LanguageTag::convertToLanguageType( _rSourceLocale ) )
+ , m_nTargetLang( LanguageTag::convertToLanguageType( _rTargetLocale ) )
+ , m_pTargetFont( _pTargetFont )
+ , m_nConvOptions(_nOptions)
+ , m_bIsInteractive( _bIsInteractive )
+ , m_pAntiImpl( _pAntiImpl )
+ , m_bByCharacter((_nOptions & CHARACTER_BY_CHARACTER) != 0)
+ , m_eConversionFormat( HHC::eSimpleConversion)
+ , m_ePrimaryConversionDirection( HHC::eHangulToHanja) // used for eConvHangulHanja
+ , m_eCurrentConversionDirection( HHC::eHangulToHanja) // used for eConvHangulHanja
+ , m_nCurrentPortionLang( LANGUAGE_NONE )
+ , m_nCurrentStartIndex( 0 )
+ , m_nCurrentEndIndex( 0 )
+ , m_nReplacementBaseIndex( 0 )
+ , m_nCurrentConversionOption( TextConversionOption::NONE )
+ , m_nCurrentConversionType( -1 ) // not yet known
+ , m_bTryBothDirections( true )
+ {
+ implReadOptionsFromConfiguration();
+
+ DBG_ASSERT( m_xContext.is(), "HangulHanjaConversion_Impl::HangulHanjaConversion_Impl: no ORB!" );
+
+ // determine conversion type
+ if (m_nSourceLang == LANGUAGE_KOREAN && m_nTargetLang == LANGUAGE_KOREAN)
+ m_eConvType = HHC::eConvHangulHanja;
+ else if ( (m_nSourceLang == LANGUAGE_CHINESE_TRADITIONAL && m_nTargetLang == LANGUAGE_CHINESE_SIMPLIFIED) ||
+ (m_nSourceLang == LANGUAGE_CHINESE_SIMPLIFIED && m_nTargetLang == LANGUAGE_CHINESE_TRADITIONAL) )
+ m_eConvType = HHC::eConvSimplifiedTraditional;
+ else
+ {
+ m_eConvType = HHC::eConvHangulHanja;
+ OSL_FAIL( "failed to determine conversion type from languages" );
+ }
+
+ m_xConverter = TextConversion::create( m_xContext );
+ }
+
+ void HangulHanjaConversion_Impl::createDialog()
+ {
+ DBG_ASSERT( m_bIsInteractive, "createDialog when the conversion should not be interactive?" );
+ if ( !m_bIsInteractive || m_pConversionDialog )
+ return;
+
+ EditAbstractDialogFactory* pFact = EditAbstractDialogFactory::Create();
+ m_pConversionDialog = pFact->CreateHangulHanjaConversionDialog(m_pUIParent);
+
+ m_pConversionDialog->EnableRubySupport( m_pAntiImpl->HasRubySupport() );
+
+ m_pConversionDialog->SetByCharacter( m_bByCharacter );
+ m_pConversionDialog->SetConversionFormat( m_eConversionFormat );
+ m_pConversionDialog->SetConversionDirectionState( m_bTryBothDirections, m_ePrimaryConversionDirection );
+
+ // the handlers
+ m_pConversionDialog->SetOptionsChangedHdl( LINK( this, HangulHanjaConversion_Impl, OnOptionsChanged ) );
+ m_pConversionDialog->SetIgnoreHdl( LINK( this, HangulHanjaConversion_Impl, OnIgnore ) );
+ m_pConversionDialog->SetIgnoreAllHdl( LINK( this, HangulHanjaConversion_Impl, OnIgnoreAll ) );
+ m_pConversionDialog->SetChangeHdl( LINK( this, HangulHanjaConversion_Impl, OnChange ) );
+ m_pConversionDialog->SetChangeAllHdl( LINK( this, HangulHanjaConversion_Impl, OnChangeAll ) );
+ m_pConversionDialog->SetClickByCharacterHdl( LINK( this, HangulHanjaConversion_Impl, OnByCharClicked ) );
+ m_pConversionDialog->SetConversionFormatChangedHdl( LINK( this, HangulHanjaConversion_Impl, OnConversionTypeChanged ) );
+ m_pConversionDialog->SetFindHdl( LINK( this, HangulHanjaConversion_Impl, OnFind ) );
+ }
+
+ sal_Int16 HangulHanjaConversion_Impl::implGetConversionType( bool bSwitchDirection ) const
+ {
+ sal_Int16 nConversionType = -1;
+ if (m_eConvType == HHC::eConvHangulHanja)
+ nConversionType = ( HHC::eHangulToHanja == m_eCurrentConversionDirection && !bSwitchDirection ) ? TO_HANJA : TO_HANGUL;
+ else if (m_eConvType == HHC::eConvSimplifiedTraditional)
+ nConversionType = LANGUAGE_CHINESE_SIMPLIFIED == m_nTargetLang ? TO_SCHINESE : TO_TCHINESE;
+ DBG_ASSERT( nConversionType != -1, "unexpected conversion type" );
+ return nConversionType;
+ }
+
+ bool HangulHanjaConversion_Impl::implUpdateSuggestions( bool _bAllowSearchNextConvertibleText, const sal_Int32 _nStartAt )
+ {
+ // parameters for the converter
+ sal_Int32 nStartSearch = m_nCurrentStartIndex;
+ if( _bAllowSearchNextConvertibleText )
+ nStartSearch = _nStartAt;
+
+ sal_Int32 nLength = m_sCurrentPortion.getLength() - nStartSearch;
+ m_nCurrentConversionType = implGetConversionType();
+ m_nCurrentConversionOption = m_bByCharacter ? CHARACTER_BY_CHARACTER : css::i18n::TextConversionOption::NONE;
+ if( m_bIgnorePostPositionalWord )
+ m_nCurrentConversionOption = m_nCurrentConversionOption | IGNORE_POST_POSITIONAL_WORD;
+
+ // no need to check both directions for chinese conversion (saves time)
+ if (m_eConvType == HHC::eConvSimplifiedTraditional)
+ m_bTryBothDirections = false;
+
+ bool bFoundAny = true;
+ try
+ {
+ TextConversionResult aResult = m_xConverter->getConversions(
+ m_sCurrentPortion,
+ nStartSearch,
+ nLength,
+ m_aSourceLocale,
+ m_nCurrentConversionType,
+ m_nCurrentConversionOption
+ );
+ const bool bFoundPrimary = aResult.Boundary.startPos < aResult.Boundary.endPos;
+ bFoundAny = bFoundPrimary;
+
+ if ( m_bTryBothDirections )
+ { // see if we find another convertible when assuming the other direction
+ TextConversionResult aSecondResult = m_xConverter->getConversions(
+ m_sCurrentPortion,
+ nStartSearch,
+ nLength,
+ m_aSourceLocale,
+ implGetConversionType( true ), // switched!
+ m_nCurrentConversionOption
+ );
+ if ( aSecondResult.Boundary.startPos < aSecondResult.Boundary.endPos )
+ { // we indeed found such a convertible
+
+ // in case the first attempt (with the original conversion direction)
+ // didn't find anything
+ if ( !bFoundPrimary
+ // or if the second location is _before_ the first one
+ || ( aSecondResult.Boundary.startPos < aResult.Boundary.startPos )
+ )
+ {
+ // then use the second finding
+ aResult = aSecondResult;
+
+ // our current conversion direction changed now
+ m_eCurrentConversionDirection = ( HHC::eHangulToHanja == m_eCurrentConversionDirection )
+ ? HHC::eHanjaToHangul : HHC::eHangulToHanja;
+ bFoundAny = true;
+ }
+ }
+ }
+
+ if( _bAllowSearchNextConvertibleText )
+ {
+ //this might change the current position
+ m_aCurrentSuggestions = aResult.Candidates;
+ m_nCurrentStartIndex = aResult.Boundary.startPos;
+ m_nCurrentEndIndex = aResult.Boundary.endPos;
+ }
+ else
+ {
+ //the change of starting position is not allowed
+ if( m_nCurrentStartIndex == aResult.Boundary.startPos
+ && aResult.Boundary.endPos != aResult.Boundary.startPos )
+ {
+ m_aCurrentSuggestions = aResult.Candidates;
+ m_nCurrentEndIndex = aResult.Boundary.endPos;
+ }
+ else
+ {
+ m_aCurrentSuggestions.realloc( 0 );
+ if( m_sCurrentPortion.getLength() >= m_nCurrentStartIndex+1 )
+ m_nCurrentEndIndex = m_nCurrentStartIndex+1;
+ }
+ }
+
+ //put recently used string to front:
+ if( m_bShowRecentlyUsedFirst && m_aCurrentSuggestions.getLength()>1 )
+ {
+ OUString sCurrentUnit( GetCurrentUnit() );
+ StringMap::const_iterator aRecentlyUsed = m_aRecentlyUsedList.find( sCurrentUnit );
+ bool bUsedBefore = aRecentlyUsed != m_aRecentlyUsedList.end();
+ if( bUsedBefore && m_aCurrentSuggestions[0] != aRecentlyUsed->second )
+ {
+ sal_Int32 nCount = m_aCurrentSuggestions.getLength();
+ Sequence< OUString > aTmp(nCount);
+ auto pTmp = aTmp.getArray();
+ pTmp[0]=aRecentlyUsed->second;
+ sal_Int32 nDiff = 1;
+ for( sal_Int32 n=1; n<nCount; n++)//we had 0 already
+ {
+ if( nDiff && m_aCurrentSuggestions[n-nDiff]==aRecentlyUsed->second )
+ nDiff=0;
+ pTmp[n]=m_aCurrentSuggestions[n-nDiff];
+ }
+ m_aCurrentSuggestions = aTmp;
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "editeng", "HangulHanjaConversion_Impl::implNextConvertibleUnit" );
+
+ //!!! at least we want to move on in the text in order
+ //!!! to avoid an endless loop...
+ return false;
+ }
+ return bFoundAny;
+ }
+
+ bool HangulHanjaConversion_Impl::implNextConvertibleUnit( const sal_Int32 _nStartAt )
+ {
+ m_aCurrentSuggestions.realloc( 0 );
+
+ // ask the TextConversion service for the next convertible piece of text
+
+ // get current values from dialog
+ if( m_eConvType == HHC::eConvHangulHanja && m_pConversionDialog )
+ {
+ m_bTryBothDirections = m_pConversionDialog->GetUseBothDirections();
+ HHC::ConversionDirection eDialogDirection = m_pConversionDialog->GetDirection( HHC::eHangulToHanja );
+
+ if( !m_bTryBothDirections && eDialogDirection != m_eCurrentConversionDirection )
+ {
+ m_eCurrentConversionDirection = eDialogDirection;
+ }
+
+ // save currently used value for possible later use
+ HangulHanjaConversion::m_bTryBothDirectionsSave = m_bTryBothDirections;
+ HangulHanjaConversion::m_ePrimaryConversionDirectionSave = m_eCurrentConversionDirection;
+ }
+
+ bool bFoundAny = implUpdateSuggestions( true, _nStartAt );
+
+ return bFoundAny &&
+ (m_nCurrentStartIndex < m_sCurrentPortion.getLength());
+ }
+
+ bool HangulHanjaConversion_Impl::implRetrieveNextPortion( )
+ {
+ const bool bAllowImplicitChanges = m_eConvType == HHC::eConvSimplifiedTraditional;
+
+ m_sCurrentPortion.clear();
+ m_nCurrentPortionLang = LANGUAGE_NONE;
+ m_pAntiImpl->GetNextPortion( m_sCurrentPortion, m_nCurrentPortionLang, bAllowImplicitChanges );
+ m_nReplacementBaseIndex = 0;
+ m_nCurrentStartIndex = m_nCurrentEndIndex = 0;
+
+ bool bRet = !m_sCurrentPortion.isEmpty();
+
+ if (m_eConvType == HHC::eConvHangulHanja && m_bTryBothDirections)
+ implGetConversionDirectionForCurrentPortion( m_eCurrentConversionDirection );
+
+ return bRet;
+ }
+
+ bool HangulHanjaConversion_Impl::implNextConvertible( bool _bRepeatUnit )
+ {
+ if ( _bRepeatUnit || ( m_nCurrentEndIndex < m_sCurrentPortion.getLength() ) )
+ {
+ if ( implNextConvertibleUnit(
+ _bRepeatUnit
+ ? m_nCurrentStartIndex
+ : m_nCurrentEndIndex
+ ) )
+ return true;
+ }
+
+ // no convertible text in the current portion anymore
+ // -> advance to the next portion
+ do
+ {
+ // next portion
+ if ( implRetrieveNextPortion( ) )
+ { // there is a next portion
+ // -> find the next convertible unit in the current portion
+ if ( implNextConvertibleUnit( 0 ) )
+ return true;
+ }
+ }
+ while ( !m_sCurrentPortion.isEmpty() );
+
+ // no more portions
+ return false;
+ }
+
+ OUString HangulHanjaConversion_Impl::GetCurrentUnit() const
+ {
+ DBG_ASSERT( m_nCurrentStartIndex < m_sCurrentPortion.getLength(),
+ "HangulHanjaConversion_Impl::GetCurrentUnit: invalid index into current portion!" );
+ DBG_ASSERT( m_nCurrentEndIndex <= m_sCurrentPortion.getLength(),
+ "HangulHanjaConversion_Impl::GetCurrentUnit: invalid index into current portion!" );
+ DBG_ASSERT( m_nCurrentStartIndex <= m_nCurrentEndIndex,
+ "HangulHanjaConversion_Impl::GetCurrentUnit: invalid interval!" );
+
+ OUString sCurrentUnit = m_sCurrentPortion.copy( m_nCurrentStartIndex, m_nCurrentEndIndex - m_nCurrentStartIndex );
+ return sCurrentUnit;
+ }
+
+ bool HangulHanjaConversion_Impl::ContinueConversion( bool _bRepeatCurrentUnit )
+ {
+ while ( implNextConvertible( _bRepeatCurrentUnit ) )
+ {
+ OUString sCurrentUnit( GetCurrentUnit() );
+
+ // do we need to ignore it?
+ const bool bAlwaysIgnoreThis = m_sIgnoreList.end() != m_sIgnoreList.find( sCurrentUnit );
+
+ // do we need to change it?
+ StringMap::const_iterator aChangeListPos = m_aChangeList.find( sCurrentUnit );
+ const bool bAlwaysChangeThis = m_aChangeList.end() != aChangeListPos;
+
+ // do we automatically change this?
+ const bool bAutoChange = m_bAutoReplaceUnique && m_aCurrentSuggestions.getLength() == 1;
+
+ if (!m_bIsInteractive)
+ {
+ // silent conversion (e.g. for simplified/traditional Chinese)...
+ if(m_aCurrentSuggestions.hasElements())
+ implChange( m_aCurrentSuggestions.getConstArray()[0] );
+ }
+ else if (bAutoChange)
+ {
+ implChange( m_aCurrentSuggestions.getConstArray()[0] );
+ }
+ else if ( bAlwaysChangeThis )
+ {
+ implChange( aChangeListPos->second );
+ }
+ else if ( !bAlwaysIgnoreThis )
+ {
+ // here we need to ask the user for what to do with the text
+ // for this, allow derivees to highlight the current text unit in a possible document view
+ m_pAntiImpl->HandleNewUnit( m_nCurrentStartIndex - m_nReplacementBaseIndex, m_nCurrentEndIndex - m_nReplacementBaseIndex );
+
+ DBG_ASSERT( m_pConversionDialog, "we should always have a dialog here!" );
+ if( m_pConversionDialog )
+ m_pConversionDialog->SetCurrentString( sCurrentUnit, m_aCurrentSuggestions );
+
+ // do not look for the next convertible: We have to wait for the user to interactively
+ // decide what happens with the current convertible
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ bool HangulHanjaConversion_Impl::implGetConversionDirectionForCurrentPortion( HHC::ConversionDirection& rDirection )
+ {
+ // - For eConvHangulHanja the direction is determined by
+ // the first encountered Korean character.
+ // - For eConvSimplifiedTraditional the conversion direction
+ // is already specified by the source language.
+
+ bool bSuccess = true;
+
+ if (m_eConvType == HHC::eConvHangulHanja)
+ {
+ bSuccess = false;
+ try
+ {
+ // get the break iterator service
+ Reference< XBreakIterator > xBreakIter = i18n::BreakIterator::create( m_xContext );
+ sal_Int32 nNextAsianScript = xBreakIter->beginOfScript( m_sCurrentPortion, m_nCurrentStartIndex, css::i18n::ScriptType::ASIAN );
+ if ( -1 == nNextAsianScript )
+ nNextAsianScript = xBreakIter->nextScript( m_sCurrentPortion, m_nCurrentStartIndex, css::i18n::ScriptType::ASIAN );
+ if ( ( nNextAsianScript >= m_nCurrentStartIndex ) && ( nNextAsianScript < m_sCurrentPortion.getLength() ) )
+ { // found asian text
+
+ // determine if it's Hangul
+ CharClass aCharClassificaton( m_xContext, LanguageTag( m_aSourceLocale) );
+ css::i18n::UnicodeScript nScript = aCharClassificaton.getScript( m_sCurrentPortion, sal::static_int_cast< sal_uInt16 >(nNextAsianScript) );
+ if ( ( UnicodeScript_kHangulJamo == nScript )
+ || ( UnicodeScript_kHangulCompatibilityJamo == nScript )
+ || ( UnicodeScript_kHangulSyllable == nScript )
+ )
+ {
+ rDirection = HHC::eHangulToHanja;
+ }
+ else
+ {
+ rDirection = HHC::eHanjaToHangul;
+ }
+
+ bSuccess = true;
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "editeng", "HangulHanjaConversion_Impl::implGetConversionDirectionForCurrentPortion" );
+ }
+ }
+
+ return bSuccess;
+ }
+
+ void HangulHanjaConversion_Impl::DoDocumentConversion( )
+ {
+ // clear the change-all list - it's to be re-initialized for every single document
+ StringMap().swap(m_aChangeList);
+
+ // first of all, we need to guess the direction of our conversion - it is determined by the first
+ // hangul or hanja character in the first text
+ if ( !implRetrieveNextPortion() )
+ {
+ SAL_INFO( "editeng", "HangulHanjaConversion_Impl::DoDocumentConversion: why did you call me if you do have nothing to convert?" );
+ // nothing to do
+ return;
+ }
+ if( m_eConvType == HHC::eConvHangulHanja )
+ {
+ //init conversion direction from saved value
+ HHC::ConversionDirection eDirection = HHC::eHangulToHanja;
+ if(!implGetConversionDirectionForCurrentPortion( eDirection ))
+ // something went wrong, has already been asserted
+ return;
+
+ if (HangulHanjaConversion::IsUseSavedConversionDirectionState())
+ {
+ m_ePrimaryConversionDirection = HangulHanjaConversion::m_ePrimaryConversionDirectionSave;
+ m_bTryBothDirections = HangulHanjaConversion::m_bTryBothDirectionsSave;
+ if( m_bTryBothDirections )
+ m_eCurrentConversionDirection = eDirection;
+ else
+ m_eCurrentConversionDirection = m_ePrimaryConversionDirection;
+ }
+ else
+ {
+ m_ePrimaryConversionDirection = eDirection;
+ m_eCurrentConversionDirection = eDirection;
+ }
+ }
+
+ if (m_bIsInteractive && m_eConvType == HHC::eConvHangulHanja)
+ {
+ //always open dialog if at least having a hangul or hanja text portion
+ createDialog();
+ if(HangulHanjaConversion::IsUseSavedConversionDirectionState())
+ ContinueConversion( false );
+ else
+ implUpdateData();
+ m_pConversionDialog->Execute();
+ m_pConversionDialog.disposeAndClear();
+ }
+ else
+ {
+ const bool bCompletelyDone = ContinueConversion( false );
+ DBG_ASSERT( bCompletelyDone, "HangulHanjaConversion_Impl::DoDocumentConversion: ContinueConversion should have returned true here!" );
+ }
+ }
+
+ void HangulHanjaConversion_Impl::implProceed( bool _bRepeatCurrentUnit )
+ {
+ if ( ContinueConversion( _bRepeatCurrentUnit ) )
+ { // we're done with the whole document
+ DBG_ASSERT( !m_bIsInteractive || m_pConversionDialog, "HangulHanjaConversion_Impl::implProceed: we should not reach this here without dialog!" );
+ if ( m_pConversionDialog )
+ m_pConversionDialog->EndDialog( RET_OK );
+ }
+ }
+
+ void HangulHanjaConversion_Impl::implChange( const OUString& _rChangeInto )
+ {
+ if( _rChangeInto.isEmpty() )
+ return;
+
+ // translate the conversion format into a replacement action
+ // this translation depends on whether we have a Hangul original, or a Hanja original
+
+ HHC::ReplacementAction eAction( HHC::eExchange );
+
+ if (m_eConvType == HHC::eConvHangulHanja)
+ {
+ // is the original we're about to change in Hangul?
+ const bool bOriginalIsHangul = HHC::eHangulToHanja == m_eCurrentConversionDirection;
+
+ switch ( m_eConversionFormat )
+ {
+ case HHC::eSimpleConversion: eAction = HHC::eExchange; break;
+ case HHC::eHangulBracketed: eAction = bOriginalIsHangul ? HHC::eOriginalBracketed : HHC::eReplacementBracketed; break;
+ case HHC::eHanjaBracketed: eAction = bOriginalIsHangul ? HHC::eReplacementBracketed : HHC::eOriginalBracketed; break;
+ case HHC::eRubyHanjaAbove: eAction = bOriginalIsHangul ? HHC::eReplacementAbove : HHC::eOriginalAbove; break;
+ case HHC::eRubyHanjaBelow: eAction = bOriginalIsHangul ? HHC::eReplacementBelow : HHC::eOriginalBelow; break;
+ case HHC::eRubyHangulAbove: eAction = bOriginalIsHangul ? HHC::eOriginalAbove : HHC::eReplacementAbove; break;
+ case HHC::eRubyHangulBelow: eAction = bOriginalIsHangul ? HHC::eOriginalBelow : HHC::eReplacementBelow; break;
+ default:
+ OSL_FAIL( "HangulHanjaConversion_Impl::implChange: invalid/unexpected conversion format!" );
+ }
+ }
+
+ // the proper indices (the wrapper implementation needs indices relative to the
+ // previous replacement)
+ DBG_ASSERT( ( m_nReplacementBaseIndex <= m_nCurrentStartIndex ) && ( m_nReplacementBaseIndex <= m_nCurrentEndIndex ),
+ "HangulHanjaConversion_Impl::implChange: invalid replacement base!" );
+
+ sal_Int32 nStartIndex = m_nCurrentStartIndex - m_nReplacementBaseIndex;
+ sal_Int32 nEndIndex = m_nCurrentEndIndex - m_nReplacementBaseIndex;
+
+ //remind this decision
+ m_aRecentlyUsedList[ GetCurrentUnit() ] = _rChangeInto;
+
+ LanguageType *pNewUnitLang = nullptr;
+ LanguageType nNewUnitLang = LANGUAGE_NONE;
+ if (m_eConvType == HHC::eConvSimplifiedTraditional)
+ {
+ // check if language needs to be changed
+ if ( m_pAntiImpl->GetTargetLanguage() == LANGUAGE_CHINESE_TRADITIONAL &&
+ !HangulHanjaConversion::IsTraditional( m_nCurrentPortionLang ))
+ nNewUnitLang = LANGUAGE_CHINESE_TRADITIONAL;
+ else if ( m_pAntiImpl->GetTargetLanguage() == LANGUAGE_CHINESE_SIMPLIFIED &&
+ !HangulHanjaConversion::IsSimplified( m_nCurrentPortionLang ))
+ nNewUnitLang = LANGUAGE_CHINESE_SIMPLIFIED;
+ if (nNewUnitLang != LANGUAGE_NONE)
+ pNewUnitLang = &nNewUnitLang;
+ }
+
+ // according to FT we should not (yet) bother about Hangul/Hanja conversion here
+ //
+ // aOffsets is needed in ReplaceUnit below in order to find out
+ // exactly which characters are really changed in order to keep as much
+ // from attributation for the text as possible.
+ Sequence< sal_Int32 > aOffsets;
+ if (m_eConvType == HHC::eConvSimplifiedTraditional && m_xConverter.is())
+ {
+ try
+ {
+ m_xConverter->getConversionWithOffset(
+ m_sCurrentPortion,
+ m_nCurrentStartIndex,
+ m_nCurrentEndIndex - m_nCurrentStartIndex,
+ m_aSourceLocale,
+ m_nCurrentConversionType,
+ m_nCurrentConversionOption,
+ aOffsets
+ );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "editeng", "HangulHanjaConversion_Impl::implChange: caught unexpected exception!" );
+ aOffsets.realloc(0);
+ }
+ }
+
+ // do the replacement
+ m_pAntiImpl->ReplaceUnit( nStartIndex, nEndIndex, m_sCurrentPortion,
+ _rChangeInto, aOffsets, eAction, pNewUnitLang );
+
+
+ // adjust the replacement base
+ m_nReplacementBaseIndex = m_nCurrentEndIndex;
+ }
+
+ void HangulHanjaConversion_Impl::implReadOptionsFromConfiguration()
+ {
+ SvtLinguConfig aLngCfg;
+ aLngCfg.GetProperty( UPH_IS_IGNORE_POST_POSITIONAL_WORD ) >>= m_bIgnorePostPositionalWord;
+ aLngCfg.GetProperty( UPH_IS_SHOW_ENTRIES_RECENTLY_USED_FIRST ) >>= m_bShowRecentlyUsedFirst;
+ aLngCfg.GetProperty( UPH_IS_AUTO_REPLACE_UNIQUE_ENTRIES ) >>= m_bAutoReplaceUnique;
+ }
+
+ void HangulHanjaConversion_Impl::implUpdateData()
+ {
+ implReadOptionsFromConfiguration();
+ implUpdateSuggestions();
+
+ if(m_pConversionDialog)
+ {
+ OUString sCurrentUnit( GetCurrentUnit() );
+
+ m_pConversionDialog->SetCurrentString( sCurrentUnit, m_aCurrentSuggestions );
+ m_pConversionDialog->FocusSuggestion();
+ }
+
+ m_pAntiImpl->HandleNewUnit( m_nCurrentStartIndex - m_nReplacementBaseIndex, m_nCurrentEndIndex - m_nReplacementBaseIndex );
+ }
+
+ IMPL_LINK_NOARG(HangulHanjaConversion_Impl, OnOptionsChanged, LinkParamNone*, void)
+ {
+ //options and dictionaries might have been changed
+ //-> update our internal settings and the dialog
+ implUpdateData();
+ }
+
+ IMPL_LINK_NOARG(HangulHanjaConversion_Impl, OnIgnore, weld::Button&, void)
+ {
+ // simply ignore, and proceed
+ implProceed( false );
+ }
+
+ IMPL_LINK_NOARG(HangulHanjaConversion_Impl, OnIgnoreAll, weld::Button&, void)
+ {
+ DBG_ASSERT( m_pConversionDialog, "HangulHanjaConversion_Impl::OnIgnoreAll: no dialog! How this?" );
+
+ if ( m_pConversionDialog )
+ {
+ OUString sCurrentUnit = m_pConversionDialog->GetCurrentString();
+ DBG_ASSERT( m_sIgnoreList.end() == m_sIgnoreList.find( sCurrentUnit ),
+ "HangulHanjaConversion_Impl, OnIgnoreAll: shouldn't this have been ignored before" );
+
+ // put into the "ignore all" list
+ m_sIgnoreList.insert( sCurrentUnit );
+
+ // and proceed
+ implProceed( false );
+ }
+ }
+
+ IMPL_LINK_NOARG(HangulHanjaConversion_Impl, OnChange, weld::Button&, void)
+ {
+ // change
+ DBG_ASSERT( m_pConversionDialog, "we should always have a dialog here!" );
+ if( m_pConversionDialog )
+ implChange( m_pConversionDialog->GetCurrentSuggestion( ) );
+ // and proceed
+ implProceed( false );
+ }
+
+ IMPL_LINK_NOARG(HangulHanjaConversion_Impl, OnChangeAll, weld::Button&, void)
+ {
+ DBG_ASSERT( m_pConversionDialog, "HangulHanjaConversion_Impl::OnChangeAll: no dialog! How this?" );
+ if ( !m_pConversionDialog )
+ return;
+
+ OUString sCurrentUnit( m_pConversionDialog->GetCurrentString() );
+ OUString sChangeInto( m_pConversionDialog->GetCurrentSuggestion( ) );
+
+ if( !sChangeInto.isEmpty() )
+ {
+ // change the current occurrence
+ implChange( sChangeInto );
+
+ // put into the "change all" list
+ m_aChangeList.emplace( sCurrentUnit, sChangeInto );
+ }
+
+ // and proceed
+ implProceed( false );
+ }
+
+ IMPL_LINK(HangulHanjaConversion_Impl, OnByCharClicked, weld::Toggleable&, rBox, void)
+ {
+ m_bByCharacter = rBox.get_active();
+
+ // continue conversion, without advancing to the next unit, but instead continuing with the current unit
+ implProceed( true );
+ }
+
+ IMPL_LINK_NOARG(HangulHanjaConversion_Impl, OnConversionTypeChanged, weld::Toggleable&, void)
+ {
+ DBG_ASSERT( m_pConversionDialog, "we should always have a dialog here!" );
+ if( m_pConversionDialog )
+ m_eConversionFormat = m_pConversionDialog->GetConversionFormat( );
+ }
+
+ IMPL_LINK_NOARG(HangulHanjaConversion_Impl, OnFind, weld::Button&, void)
+ {
+ DBG_ASSERT( m_pConversionDialog, "HangulHanjaConversion_Impl::OnFind: where did this come from?" );
+ if ( !m_pConversionDialog )
+ return;
+
+ try
+ {
+ OUString sNewOriginal( m_pConversionDialog->GetCurrentSuggestion( ) );
+ Sequence< OUString > aSuggestions;
+
+ DBG_ASSERT( m_xConverter.is(), "HangulHanjaConversion_Impl::OnFind: no converter!" );
+ TextConversionResult aToHanja = m_xConverter->getConversions(
+ sNewOriginal,
+ 0, sNewOriginal.getLength(),
+ m_aSourceLocale,
+ TextConversionType::TO_HANJA,
+ TextConversionOption::NONE
+ );
+ TextConversionResult aToHangul = m_xConverter->getConversions(
+ sNewOriginal,
+ 0, sNewOriginal.getLength(),
+ m_aSourceLocale,
+ TextConversionType::TO_HANGUL,
+ TextConversionOption::NONE
+ );
+
+ bool bHaveToHanja = ( aToHanja.Boundary.startPos < aToHanja.Boundary.endPos );
+ bool bHaveToHangul = ( aToHangul.Boundary.startPos < aToHangul.Boundary.endPos );
+
+ TextConversionResult* pResult = nullptr;
+ if ( bHaveToHanja && bHaveToHangul )
+ { // it found convertibles in both directions -> use the first
+ if ( aToHangul.Boundary.startPos < aToHanja.Boundary.startPos )
+ pResult = &aToHangul;
+ else
+ pResult = &aToHanja;
+ }
+ else if ( bHaveToHanja )
+ { // only found toHanja
+ pResult = &aToHanja;
+ }
+ else
+ { // only found toHangul
+ pResult = &aToHangul;
+ }
+ if ( pResult )
+ aSuggestions = pResult->Candidates;
+
+ m_pConversionDialog->SetCurrentString( sNewOriginal, aSuggestions, false );
+ m_pConversionDialog->FocusSuggestion();
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "editeng", "HangulHanjaConversion_Impl::OnFind" );
+ }
+ }
+
+ bool HangulHanjaConversion::m_bUseSavedValues = false;
+ bool HangulHanjaConversion::m_bTryBothDirectionsSave = false;
+ HHC::ConversionDirection HangulHanjaConversion::m_ePrimaryConversionDirectionSave = HHC::eHangulToHanja;
+
+ HangulHanjaConversion::HangulHanjaConversion( weld::Widget* pUIParent,
+ const Reference< XComponentContext >& rxContext,
+ const lang::Locale& _rSourceLocale, const lang::Locale& _rTargetLocale,
+ const vcl::Font* _pTargetFont,
+ sal_Int32 _nOptions, bool _bIsInteractive)
+ :m_pImpl( new HangulHanjaConversion_Impl( pUIParent, rxContext, _rSourceLocale, _rTargetLocale, _pTargetFont, _nOptions, _bIsInteractive, this ) )
+ {
+ }
+
+ HangulHanjaConversion::~HangulHanjaConversion() COVERITY_NOEXCEPT_FALSE
+ {
+ }
+
+ void HangulHanjaConversion::SetUseSavedConversionDirectionState( bool bVal )
+ {
+ m_bUseSavedValues = bVal;
+ }
+
+ bool HangulHanjaConversion::IsUseSavedConversionDirectionState()
+ {
+ return m_bUseSavedValues;
+ }
+
+ weld::Widget* HangulHanjaConversion::GetUIParent() const
+ {
+ return m_pImpl->GetUIParent();
+ }
+
+ LanguageType HangulHanjaConversion::GetSourceLanguage( ) const
+ {
+ return m_pImpl->GetSourceLang();
+ }
+
+ LanguageType HangulHanjaConversion::GetTargetLanguage( ) const
+ {
+ return m_pImpl->GetTargetLang();
+ }
+
+ const vcl::Font * HangulHanjaConversion::GetTargetFont( ) const
+ {
+ return m_pImpl->GetTargetFont();
+ }
+
+ sal_Int32 HangulHanjaConversion::GetConversionOptions( ) const
+ {
+ return m_pImpl->GetConvOptions();
+ }
+
+ bool HangulHanjaConversion::IsInteractive( ) const
+ {
+ return m_pImpl->IsInteractive();
+ }
+
+ void HangulHanjaConversion::ConvertDocument()
+ {
+ if ( m_pImpl->IsValid() )
+ m_pImpl->DoDocumentConversion( );
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/misc/splwrap.cxx b/editeng/source/misc/splwrap.cxx
new file mode 100644
index 0000000000..dd59113a69
--- /dev/null
+++ b/editeng/source/misc/splwrap.cxx
@@ -0,0 +1,472 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <rtl/ustring.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <svtools/langtab.hxx>
+
+#include <vcl/errinf.hxx>
+#include <editeng/unolingu.hxx>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/linguistic2/XLinguProperties.hpp>
+#include <com/sun/star/linguistic2/XSpellChecker1.hpp>
+#include <com/sun/star/linguistic2/XHyphenator.hpp>
+#include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp>
+#include <com/sun/star/linguistic2/XDictionary.hpp>
+
+#include <editeng/svxenum.hxx>
+#include <editeng/splwrap.hxx>
+#include <editeng/edtdlg.hxx>
+#include <editeng/eerdll.hxx>
+#include <editeng/editrids.hrc>
+#include <editeng/editerr.hxx>
+
+#include <map>
+#include <memory>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::linguistic2;
+
+
+// misc functions ---------------------------------------------
+
+void SvxPrepareAutoCorrect( OUString &rOldText, std::u16string_view rNewText )
+{
+ // This function should be used to strip (or add) trailing '.' from
+ // the strings before passing them on to the autocorrect function in
+ // order that the autocorrect function will hopefully
+ // works properly with normal words and abbreviations (with trailing '.')
+ // independent of if they are at the end of the sentence or not.
+ //
+ // rOldText: text to be replaced
+ // rNewText: replacement text
+
+ sal_Int32 nOldLen = rOldText.getLength();
+ sal_Int32 nNewLen = rNewText.size();
+ if (nOldLen && nNewLen)
+ {
+ bool bOldHasDot = '.' == rOldText[ nOldLen - 1 ],
+ bNewHasDot = '.' == rNewText[ nNewLen - 1 ];
+ if (bOldHasDot && !bNewHasDot
+ /*this is: !(bOldHasDot && bNewHasDot) && bOldHasDot*/)
+ rOldText = rOldText.copy( 0, nOldLen - 1 );
+ }
+}
+
+#define SVX_LANG_NEED_CHECK 0
+#define SVX_LANG_OK 1
+#define SVX_LANG_MISSING 2
+#define SVX_LANG_MISSING_DO_WARN 3
+
+typedef std::map< LanguageType, sal_uInt16 > LangCheckState_map_t;
+
+static LangCheckState_map_t & GetLangCheckState()
+{
+ static LangCheckState_map_t aLangCheckState;
+ return aLangCheckState;
+}
+
+void SvxSpellWrapper::ShowLanguageErrors()
+{
+ // display message boxes for languages not available for
+ // spellchecking or hyphenation
+ LangCheckState_map_t &rLCS = GetLangCheckState();
+ for (auto const& elem : rLCS)
+ {
+ LanguageType nLang = elem.first;
+ sal_uInt16 nVal = elem.second;
+ sal_uInt16 nTmpSpell = nVal & 0x00FF;
+ sal_uInt16 nTmpHyph = (nVal >> 8) & 0x00FF;
+
+ if (SVX_LANG_MISSING_DO_WARN == nTmpSpell)
+ {
+ OUString aErr( SvtLanguageTable::GetLanguageString( nLang ) );
+ ErrorHandler::HandleError(
+ ErrCodeMsg( ERRCODE_SVX_LINGU_LANGUAGENOTEXISTS, aErr ) );
+ nTmpSpell = SVX_LANG_MISSING;
+ }
+ if (SVX_LANG_MISSING_DO_WARN == nTmpHyph)
+ {
+ OUString aErr( SvtLanguageTable::GetLanguageString( nLang ) );
+ ErrorHandler::HandleError(
+ ErrCodeMsg( ERRCODE_SVX_LINGU_LANGUAGENOTEXISTS, aErr ) );
+ nTmpHyph = SVX_LANG_MISSING;
+ }
+
+ rLCS[ nLang ] = (nTmpHyph << 8) | nTmpSpell;
+ }
+
+}
+
+SvxSpellWrapper::~SvxSpellWrapper()
+{
+}
+
+/*--------------------------------------------------------------------
+ * Description: Constructor, the test sequence is determined
+ *
+ * !bStart && !bOtherCntnt: BODY_END, BODY_START, OTHER
+ * !bStart && bOtherCntnt: OTHER, BODY
+ * bStart && !bOtherCntnt: BODY_END, OTHER
+ * bStart && bOtherCntnt: OTHER
+ *
+ --------------------------------------------------------------------*/
+
+SvxSpellWrapper::SvxSpellWrapper( weld::Widget* pWn,
+ const bool bStart, const bool bIsAllRight ) :
+
+ pWin ( pWn ),
+ bOtherCntnt ( false ),
+ bStartChk ( false ),
+ bRevAllowed ( true ),
+ bAllRight ( bIsAllRight )
+{
+ Reference< linguistic2::XLinguProperties > xProp( LinguMgr::GetLinguPropertySet() );
+ bool bWrapReverse = xProp.is() && xProp->getIsWrapReverse();
+ bReverse = bWrapReverse;
+ bStartDone = !bReverse && bStart;
+ bEndDone = bReverse && bStart;
+}
+
+
+SvxSpellWrapper::SvxSpellWrapper( weld::Widget* pWn,
+ Reference< XHyphenator > const &xHyphenator,
+ const bool bStart, const bool bOther ) :
+ pWin ( pWn ),
+ xHyph ( xHyphenator ),
+ bOtherCntnt ( bOther ),
+ bReverse ( false ),
+ bStartDone ( bOther || bStart ),
+ bEndDone ( false ),
+ bStartChk ( bOther ),
+ bRevAllowed ( false ),
+ bAllRight ( true )
+{
+}
+
+
+sal_Int16 SvxSpellWrapper::CheckSpellLang(
+ Reference< XSpellChecker1 > const & xSpell, LanguageType nLang)
+{
+ LangCheckState_map_t &rLCS = GetLangCheckState();
+
+ LangCheckState_map_t::iterator aIt( rLCS.find( nLang ) );
+ sal_uInt16 nVal = aIt == rLCS.end() ? SVX_LANG_NEED_CHECK : aIt->second;
+
+ if (aIt == rLCS.end())
+ rLCS[ nLang ] = nVal;
+
+ if (SVX_LANG_NEED_CHECK == (nVal & 0x00FF))
+ {
+ sal_uInt16 nTmpVal = SVX_LANG_MISSING_DO_WARN;
+ if (xSpell.is() && xSpell->hasLanguage( static_cast<sal_uInt16>(nLang) ))
+ nTmpVal = SVX_LANG_OK;
+ nVal &= 0xFF00;
+ nVal |= nTmpVal;
+
+ rLCS[ nLang ] = nVal;
+ }
+
+ return static_cast<sal_Int16>(nVal);
+}
+
+sal_Int16 SvxSpellWrapper::CheckHyphLang(
+ Reference< XHyphenator > const & xHyph, LanguageType nLang)
+{
+ LangCheckState_map_t &rLCS = GetLangCheckState();
+
+ LangCheckState_map_t::iterator aIt( rLCS.find( nLang ) );
+ sal_uInt16 nVal = aIt == rLCS.end() ? 0 : aIt->second;
+
+ if (aIt == rLCS.end())
+ rLCS[ nLang ] = nVal;
+
+ if (SVX_LANG_NEED_CHECK == ((nVal >> 8) & 0x00FF))
+ {
+ sal_uInt16 nTmpVal = SVX_LANG_MISSING_DO_WARN;
+ if (xHyph.is() && xHyph->hasLocale( LanguageTag::convertToLocale( nLang ) ))
+ nTmpVal = SVX_LANG_OK;
+ nVal &= 0x00FF;
+ nVal |= nTmpVal << 8;
+
+ rLCS[ nLang ] = nVal;
+ }
+
+ return static_cast<sal_Int16>(nVal);
+}
+
+
+void SvxSpellWrapper::SpellStart( SvxSpellArea /*eSpell*/ )
+{ // Here, the necessary preparations be made for SpellContinue in the
+} // given area.
+
+
+bool SvxSpellWrapper::SpellMore()
+{
+ return false; // Should additional documents be examined?
+}
+
+
+void SvxSpellWrapper::SpellEnd()
+{ // Area is complete, tidy up if necessary
+
+ // display error for last language not found
+ ShowLanguageErrors();
+}
+
+void SvxSpellWrapper::SpellContinue()
+{
+}
+
+void SvxSpellWrapper::ReplaceAll( const OUString & )
+{ // Replace Word from the Replace list
+}
+
+void SvxSpellWrapper::InsertHyphen( const sal_Int32 )
+{ // inserting and deleting Hyphen
+}
+
+// Testing of the document areas in the order specified by the flags
+void SvxSpellWrapper::SpellDocument( )
+{
+#if ENABLE_WASM_STRIP_HUNSPELL
+ return;
+#else
+ if ( bOtherCntnt )
+ {
+ bReverse = false;
+ SpellStart( SvxSpellArea::Other );
+ }
+ else
+ {
+ bStartChk = bReverse;
+ SpellStart( bReverse ? SvxSpellArea::BodyStart : SvxSpellArea::BodyEnd );
+ }
+
+ if ( !FindSpellError() )
+ return;
+
+ Reference< XHyphenatedWord > xHyphWord( GetLast(), UNO_QUERY );
+
+ if (xHyphWord.is())
+ {
+ EditAbstractDialogFactory* pFact = EditAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractHyphenWordDialog> pDlg(pFact->CreateHyphenWordDialog(
+ pWin,
+ xHyphWord->getWord(),
+ LanguageTag( xHyphWord->getLocale() ).getLanguageType(),
+ xHyph, this ));
+ pDlg->Execute();
+ }
+#endif
+}
+
+
+// Select the next area
+
+
+bool SvxSpellWrapper::SpellNext( )
+{
+ Reference< linguistic2::XLinguProperties > xProp( LinguMgr::GetLinguPropertySet() );
+ bool bWrapReverse = xProp.is() && xProp->getIsWrapReverse();
+ bool bActRev = bRevAllowed && bWrapReverse;
+
+ // bActRev is the direction after Spell checking, bReverse is the one
+ // at the beginning.
+ if( bActRev == bReverse )
+ { // No change of direction, thus is the
+ if( bStartChk ) // desired area ( bStartChk )
+ bStartDone = true; // completely processed.
+ else
+ bEndDone = true;
+ }
+ else if( bReverse == bStartChk ) //For a change of direction, an area can
+ { // be processed during certain circumstances
+ if( bStartChk ) // If the first part is spell checked in backwards
+ bEndDone = true; // and this is reversed in the process, then
+ else // then the end part is processed (and vice-versa).
+ bStartDone = true;
+ }
+
+ bReverse = bActRev;
+ if( bOtherCntnt && bStartDone && bEndDone ) // Document has been fully checked?
+ {
+ if ( SpellMore() ) // spell check another document?
+ {
+ bOtherCntnt = false;
+ bStartDone = !bReverse;
+ bEndDone = bReverse;
+ SpellStart( SvxSpellArea::Body );
+ return true;
+ }
+ return false;
+ }
+
+ bool bGoOn = false;
+
+ if ( bOtherCntnt )
+ {
+ bStartChk = false;
+ SpellStart( SvxSpellArea::Body );
+ bGoOn = true;
+ }
+ else if ( bStartDone && bEndDone )
+ {
+ if ( SpellMore() ) // check another document?
+ {
+ bOtherCntnt = false;
+ bStartDone = !bReverse;
+ bEndDone = bReverse;
+ SpellStart( SvxSpellArea::Body );
+ return true;
+ }
+ }
+ else
+ {
+ // a BODY_area done, ask for the other BODY_area
+ xWait.reset();
+
+ TranslateId pResId = bReverse ? RID_SVXSTR_QUERY_BW_CONTINUE : RID_SVXSTR_QUERY_CONTINUE;
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pWin,
+ VclMessageType::Question, VclButtonsType::YesNo,
+ EditResId(pResId)));
+ if (xBox->run() != RET_YES)
+ {
+ // sacrifice the other area if necessary ask for special area
+ xWait.reset(new weld::WaitObject(pWin));
+ bStartDone = bEndDone = true;
+ return SpellNext();
+ }
+ else
+ {
+ bStartChk = !bStartDone;
+ SpellStart( bStartChk ? SvxSpellArea::BodyStart : SvxSpellArea::BodyEnd );
+ bGoOn = true;
+ }
+ xWait.reset(new weld::WaitObject(pWin));
+ }
+ return bGoOn;
+}
+
+
+Reference< XDictionary > SvxSpellWrapper::GetAllRightDic()
+{
+ Reference< XDictionary > xDic;
+
+ Reference< XSearchableDictionaryList > xDicList( LinguMgr::GetDictionaryList() );
+ if (xDicList.is())
+ {
+ Sequence< Reference< XDictionary > > aDics( xDicList->getDictionaries() );
+ const Reference< XDictionary > *pDic = aDics.getConstArray();
+ sal_Int32 nCount = aDics.getLength();
+
+ sal_Int32 i = 0;
+ while (!xDic.is() && i < nCount)
+ {
+ Reference< XDictionary > xTmp = pDic[i];
+ if (xTmp.is())
+ {
+ if ( xTmp->isActive() &&
+ xTmp->getDictionaryType() != DictionaryType_NEGATIVE &&
+ LanguageTag( xTmp->getLocale() ).getLanguageType() == LANGUAGE_NONE )
+ {
+ Reference< frame::XStorable > xStor( xTmp, UNO_QUERY );
+ if (xStor.is() && xStor->hasLocation() && !xStor->isReadonly())
+ {
+ xDic = xTmp;
+ }
+ }
+ }
+ ++i;
+ }
+
+ if (!xDic.is())
+ {
+ xDic = LinguMgr::GetStandardDic();
+ if (xDic.is())
+ xDic->setActive( true );
+ }
+ }
+
+ return xDic;
+}
+
+
+bool SvxSpellWrapper::FindSpellError()
+{
+ ShowLanguageErrors();
+
+ xWait.reset(new weld::WaitObject(pWin));
+ bool bSpell = true;
+
+ Reference< XDictionary > xAllRightDic;
+ if (IsAllRight())
+ xAllRightDic = GetAllRightDic();
+
+ while ( bSpell )
+ {
+ SpellContinue();
+
+ Reference< XSpellAlternatives > xAlt( GetLast(), UNO_QUERY );
+ Reference< XHyphenatedWord > xHyphWord( GetLast(), UNO_QUERY );
+
+ if (xAlt.is())
+ {
+ if (IsAllRight() && xAllRightDic.is())
+ {
+ xAllRightDic->add( xAlt->getWord(), false, OUString() );
+ }
+ else
+ {
+ // look up in ChangeAllList for misspelled word
+ Reference< XDictionary > xChangeAllList =
+ LinguMgr::GetChangeAllList();
+ Reference< XDictionaryEntry > xEntry;
+ if (xChangeAllList.is())
+ xEntry = xChangeAllList->getEntry( xAlt->getWord() );
+
+ if (xEntry.is())
+ {
+ // replace word without asking
+ ReplaceAll( xEntry->getReplacementText() );
+ }
+ else
+ bSpell = false;
+ }
+ }
+ else if (xHyphWord.is())
+ bSpell = false;
+ else
+ {
+ SpellEnd();
+ bSpell = SpellNext();
+ }
+ }
+ xWait.reset();
+ return GetLast().is();
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/misc/svxacorr.cxx b/editeng/source/misc/svxacorr.cxx
new file mode 100644
index 0000000000..ff2c4518aa
--- /dev/null
+++ b/editeng/source/misc/svxacorr.cxx
@@ -0,0 +1,3161 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <utility>
+#include <algorithm>
+#include <string_view>
+#include <sal/config.h>
+
+#include <com/sun/star/linguistic2/XSpellChecker1.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/XStream.hpp>
+#include <tools/urlobj.hxx>
+#include <i18nlangtag/mslangid.hxx>
+#include <i18nutil/transliteration.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <svl/fstathelper.hxx>
+#include <svl/urihelper.hxx>
+#include <unotools/charclass.hxx>
+#include <com/sun/star/i18n/UnicodeType.hpp>
+#include <unotools/collatorwrapper.hxx>
+#include <com/sun/star/i18n/UnicodeScript.hpp>
+#include <com/sun/star/i18n/OrdinalSuffix.hpp>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/transliterationwrapper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <o3tl/string_view.hxx>
+#include <editeng/editids.hrc>
+#include <sot/storage.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/svxacorr.hxx>
+#include <editeng/unolingu.hxx>
+#include <vcl/window.hxx>
+#include <com/sun/star/xml/sax/InputSource.hpp>
+#include <com/sun/star/xml/sax/FastParser.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/xml/sax/SAXParseException.hpp>
+#include <unotools/streamwrap.hxx>
+#include "SvXMLAutoCorrectImport.hxx"
+#include "SvXMLAutoCorrectExport.hxx"
+#include "SvXMLAutoCorrectTokenHandler.hxx"
+#include <ucbhelper/content.hxx>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/ucb/TransferInfo.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <comphelper/diagnose_ex.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <unordered_map>
+#include <rtl/character.hxx>
+
+using namespace ::com::sun::star::ucb;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::xml::sax;
+using namespace ::com::sun::star;
+using namespace ::xmloff::token;
+using namespace ::utl;
+
+namespace {
+
+enum class Flags {
+ NONE = 0x00,
+ FullStop = 0x01,
+ ExclamationMark = 0x02,
+ QuestionMark = 0x04,
+};
+
+}
+
+namespace o3tl {
+ template<> struct typed_flags<Flags> : is_typed_flags<Flags, 0x07> {};
+}
+const sal_Unicode cNonBreakingSpace = 0xA0; // UNICODE code for no break space
+
+constexpr OUString pXMLImplWordStart_ExcptLstStr = u"WordExceptList.xml"_ustr;
+constexpr OUString pXMLImplCplStt_ExcptLstStr = u"SentenceExceptList.xml"_ustr;
+constexpr OUString pXMLImplAutocorr_ListStr = u"DocumentList.xml"_ustr;
+
+// tdf#54409 check also typographical quotation marks in the case of skipped ASCII quotation marks
+// Curious, why these \u0083\u0084\u0089\u0091\u0092\u0093\u0094 are handled as "begin characters"?
+constexpr std::u16string_view
+ /* also at these beginnings - Brackets and all kinds of begin characters */
+ sImplSttSkipChars = u"\"'([{\u2018\u2019\u201a\u201b\u201c\u201d\u201e\u201f\u0083\u0084\u0089\u0091\u0092\u0093\u0094",
+ /* also at these ends - Brackets and all kinds of begin characters */
+ sImplEndSkipChars = u"\"')]}\u2018\u2019\u201a\u201b\u201c\u201d\u201e\u201f\u0083\u0084\u0089\u0091\u0092\u0093\u0094";
+
+static OUString EncryptBlockName_Imp(std::u16string_view rName);
+
+static bool NonFieldWordDelim( const sal_Unicode c )
+{
+ return ' ' == c || '\t' == c || 0x0a == c ||
+ cNonBreakingSpace == c || 0x2011 == c;
+}
+
+static bool IsWordDelim( const sal_Unicode c )
+{
+ return c == 0x1 || NonFieldWordDelim(c);
+}
+
+
+static bool IsLowerLetter( sal_Int32 nCharType )
+{
+ return CharClass::isLetterType( nCharType ) &&
+ ( css::i18n::KCharacterType::LOWER & nCharType);
+}
+
+static bool IsUpperLetter( sal_Int32 nCharType )
+{
+ return CharClass::isLetterType( nCharType ) &&
+ ( css::i18n::KCharacterType::UPPER & nCharType);
+}
+
+static bool lcl_IsUnsupportedUnicodeChar( CharClass const & rCC, const OUString& rTxt,
+ sal_Int32 nStt, sal_Int32 nEnd )
+{
+ for( ; nStt < nEnd; ++nStt )
+ {
+ css::i18n::UnicodeScript nScript = rCC.getScript( rTxt, nStt );
+ switch( nScript )
+ {
+ case css::i18n::UnicodeScript_kCJKRadicalsSupplement:
+ case css::i18n::UnicodeScript_kHangulJamo:
+ case css::i18n::UnicodeScript_kCJKSymbolPunctuation:
+ case css::i18n::UnicodeScript_kHiragana:
+ case css::i18n::UnicodeScript_kKatakana:
+ case css::i18n::UnicodeScript_kHangulCompatibilityJamo:
+ case css::i18n::UnicodeScript_kEnclosedCJKLetterMonth:
+ case css::i18n::UnicodeScript_kCJKCompatibility:
+ case css::i18n::UnicodeScript_kCJKUnifiedIdeographsExtensionA:
+ case css::i18n::UnicodeScript_kCJKUnifiedIdeograph:
+ case css::i18n::UnicodeScript_kHangulSyllable:
+ case css::i18n::UnicodeScript_kCJKCompatibilityIdeograph:
+ case css::i18n::UnicodeScript_kHalfwidthFullwidthForm:
+ return true;
+ default: ; //do nothing
+ }
+ }
+ return false;
+}
+
+static bool lcl_IsSymbolChar( CharClass const & rCC, const OUString& rTxt,
+ sal_Int32 nStt, sal_Int32 nEnd )
+{
+ for( ; nStt < nEnd; ++nStt )
+ {
+ if( css::i18n::UnicodeType::PRIVATE_USE == rCC.getType( rTxt, nStt ))
+ return true;
+ }
+ return false;
+}
+
+static bool lcl_IsInArr(std::u16string_view arr, const sal_uInt32 c)
+{
+ return std::any_of(arr.begin(), arr.end(), [c](const auto c1) { return c1 == c; });
+}
+
+SvxAutoCorrDoc::~SvxAutoCorrDoc()
+{
+}
+
+// Called by the functions:
+// - FnCapitalStartWord
+// - FnCapitalStartSentence
+// after the exchange of characters. Then the words, if necessary, can be inserted
+// into the exception list.
+void SvxAutoCorrDoc::SaveCpltSttWord( ACFlags, sal_Int32, const OUString&,
+ sal_Unicode )
+{
+}
+
+LanguageType SvxAutoCorrDoc::GetLanguage( sal_Int32 ) const
+{
+ return LANGUAGE_SYSTEM;
+}
+
+static const LanguageTag& GetAppLang()
+{
+ return Application::GetSettings().GetLanguageTag();
+}
+
+/// Never use an unresolved LANGUAGE_SYSTEM.
+static LanguageType GetDocLanguage( const SvxAutoCorrDoc& rDoc, sal_Int32 nPos )
+{
+ LanguageType eLang = rDoc.GetLanguage( nPos );
+ if (eLang == LANGUAGE_SYSTEM)
+ eLang = GetAppLang().getLanguageType(); // the current work locale
+ return eLang;
+}
+
+static LocaleDataWrapper& GetLocaleDataWrapper( LanguageType nLang )
+{
+ static std::unique_ptr<LocaleDataWrapper> xLclDtWrp;
+ LanguageTag aLcl( nLang );
+ if (!xLclDtWrp || xLclDtWrp->getLoadedLanguageTag() != aLcl)
+ xLclDtWrp.reset(new LocaleDataWrapper(std::move(aLcl)));
+ return *xLclDtWrp;
+}
+static TransliterationWrapper& GetIgnoreTranslWrapper()
+{
+ static int bIsInit = 0;
+ static TransliterationWrapper aWrp( ::comphelper::getProcessComponentContext(),
+ TransliterationFlags::IGNORE_KANA |
+ TransliterationFlags::IGNORE_WIDTH );
+ if( !bIsInit )
+ {
+ aWrp.loadModuleIfNeeded( GetAppLang().getLanguageType() );
+ bIsInit = 1;
+ }
+ return aWrp;
+}
+static CollatorWrapper& GetCollatorWrapper()
+{
+ static CollatorWrapper aCollWrp = []()
+ {
+ CollatorWrapper tmp( ::comphelper::getProcessComponentContext() );
+ tmp.loadDefaultCollator( GetAppLang().getLocale(), 0 );
+ return tmp;
+ }();
+ return aCollWrp;
+}
+
+bool SvxAutoCorrect::IsAutoCorrectChar( sal_Unicode cChar )
+{
+ return cChar == '\0' || cChar == '\t' || cChar == 0x0a ||
+ cChar == ' ' || cChar == '\'' || cChar == '\"' ||
+ cChar == '*' || cChar == '_' || cChar == '%' ||
+ cChar == '.' || cChar == ',' || cChar == ';' ||
+ cChar == ':' || cChar == '?' || cChar == '!' ||
+ cChar == '<' || cChar == '>' ||
+ cChar == '/' || cChar == '-';
+}
+
+namespace
+{
+ bool IsCompoundWordDelimChar(sal_Unicode cChar)
+ {
+ return cChar == '-' || SvxAutoCorrect::IsAutoCorrectChar(cChar);
+ }
+}
+
+bool SvxAutoCorrect::NeedsHardspaceAutocorr( sal_Unicode cChar )
+{
+ return cChar == '%' || cChar == ';' || cChar == ':' || cChar == '?' || cChar == '!' ||
+ cChar == '/' /*case for the urls exception*/;
+}
+
+ACFlags SvxAutoCorrect::GetDefaultFlags()
+{
+ ACFlags nRet = ACFlags::Autocorrect
+ | ACFlags::CapitalStartSentence
+ | ACFlags::CapitalStartWord
+ | ACFlags::ChgOrdinalNumber
+ | ACFlags::ChgToEnEmDash
+ | ACFlags::AddNonBrkSpace
+ | ACFlags::TransliterateRTL
+ | ACFlags::ChgAngleQuotes
+ | ACFlags::ChgWeightUnderl
+ | ACFlags::SetINetAttr
+ | ACFlags::SetDOIAttr
+ | ACFlags::ChgQuotes
+ | ACFlags::SaveWordCplSttLst
+ | ACFlags::SaveWordWordStartLst
+ | ACFlags::CorrectCapsLock;
+ LanguageType eLang = GetAppLang().getLanguageType();
+ if( eLang.anyOf(
+ LANGUAGE_ENGLISH,
+ LANGUAGE_ENGLISH_US,
+ LANGUAGE_ENGLISH_UK,
+ LANGUAGE_ENGLISH_AUS,
+ LANGUAGE_ENGLISH_CAN,
+ LANGUAGE_ENGLISH_NZ,
+ LANGUAGE_ENGLISH_EIRE,
+ LANGUAGE_ENGLISH_SAFRICA,
+ LANGUAGE_ENGLISH_JAMAICA,
+ LANGUAGE_ENGLISH_CARIBBEAN))
+ nRet &= ~ACFlags(ACFlags::ChgQuotes|ACFlags::ChgSglQuotes);
+ return nRet;
+}
+
+constexpr sal_Unicode cEmDash = 0x2014;
+constexpr sal_Unicode cEnDash = 0x2013;
+constexpr OUString sEmDash(u"\u2014"_ustr);
+constexpr OUString sEnDash(u"\u2013"_ustr);
+constexpr sal_Unicode cApostrophe = 0x2019;
+constexpr sal_Unicode cLeftDoubleAngleQuote = 0xAB;
+constexpr sal_Unicode cRightDoubleAngleQuote = 0xBB;
+constexpr sal_Unicode cLeftSingleAngleQuote = 0x2039;
+constexpr sal_Unicode cRightSingleAngleQuote = 0x203A;
+// stop characters for searching preceding quotes
+// (the first character is also the opening quote we are looking for)
+const sal_Unicode aStopDoubleAngleQuoteStart[] = { 0x201E, 0x201D, 0x201C, 0 }; // preceding ,,
+const sal_Unicode aStopDoubleAngleQuoteEnd[] = { cRightDoubleAngleQuote, cLeftDoubleAngleQuote, 0x201D, 0x201E, 0 }; // preceding >>
+// preceding << for Romanian, handle also alternative primary closing quotation mark U+201C
+const sal_Unicode aStopDoubleAngleQuoteEndRo[] = { cLeftDoubleAngleQuote, cRightDoubleAngleQuote, 0x201D, 0x201E, 0x201C, 0 };
+const sal_Unicode aStopSingleQuoteEnd[] = { 0x201A, 0x2018, 0x201C, 0x201E, 0 };
+const sal_Unicode aStopSingleQuoteEndRuUa[] = { 0x201E, 0x201C, cRightDoubleAngleQuote, cLeftDoubleAngleQuote, 0 };
+
+SvxAutoCorrect::SvxAutoCorrect( OUString aShareAutocorrFile,
+ OUString aUserAutocorrFile )
+ : sShareAutoCorrFile(std::move( aShareAutocorrFile ))
+ , sUserAutoCorrFile(std::move( aUserAutocorrFile ))
+ , eCharClassLang( LANGUAGE_DONTKNOW )
+ , nFlags(SvxAutoCorrect::GetDefaultFlags())
+ , cStartDQuote( 0 )
+ , cEndDQuote( 0 )
+ , cStartSQuote( 0 )
+ , cEndSQuote( 0 )
+{
+}
+
+SvxAutoCorrect::SvxAutoCorrect( const SvxAutoCorrect& rCpy )
+ : sShareAutoCorrFile( rCpy.sShareAutoCorrFile )
+ , sUserAutoCorrFile( rCpy.sUserAutoCorrFile )
+ , aSwFlags( rCpy.aSwFlags )
+ , eCharClassLang(rCpy.eCharClassLang)
+ , nFlags( rCpy.nFlags & ~ACFlags(ACFlags::ChgWordLstLoad|ACFlags::CplSttLstLoad|ACFlags::WordStartLstLoad))
+ , cStartDQuote( rCpy.cStartDQuote )
+ , cEndDQuote( rCpy.cEndDQuote )
+ , cStartSQuote( rCpy.cStartSQuote )
+ , cEndSQuote( rCpy.cEndSQuote )
+{
+}
+
+
+SvxAutoCorrect::~SvxAutoCorrect()
+{
+}
+
+void SvxAutoCorrect::GetCharClass_( LanguageType eLang )
+{
+ moCharClass.emplace( LanguageTag( eLang) );
+ eCharClassLang = eLang;
+}
+
+void SvxAutoCorrect::SetAutoCorrFlag( ACFlags nFlag, bool bOn )
+{
+ ACFlags nOld = nFlags;
+ nFlags = bOn ? nFlags | nFlag
+ : nFlags & ~nFlag;
+
+ if( !bOn )
+ {
+ if( (nOld & ACFlags::CapitalStartSentence) != (nFlags & ACFlags::CapitalStartSentence) )
+ nFlags &= ~ACFlags::CplSttLstLoad;
+ if( (nOld & ACFlags::CapitalStartWord) != (nFlags & ACFlags::CapitalStartWord) )
+ nFlags &= ~ACFlags::WordStartLstLoad;
+ if( (nOld & ACFlags::Autocorrect) != (nFlags & ACFlags::Autocorrect) )
+ nFlags &= ~ACFlags::ChgWordLstLoad;
+ }
+}
+
+
+// Correct TWo INitial CApitals
+void SvxAutoCorrect::FnCapitalStartWord( SvxAutoCorrDoc& rDoc, const OUString& rTxt,
+ sal_Int32 nSttPos, sal_Int32 nEndPos,
+ LanguageType eLang )
+{
+ CharClass& rCC = GetCharClass( eLang );
+
+ // Delete all non alphanumeric. Test the characters at the beginning/end of
+ // the word ( recognizes: "(min.", "/min.", and so on.)
+ for( ; nSttPos < nEndPos; ++nSttPos )
+ if( rCC.isLetterNumeric( rTxt, nSttPos ))
+ break;
+ for( ; nSttPos < nEndPos; --nEndPos )
+ if( rCC.isLetterNumeric( rTxt, nEndPos - 1 ))
+ break;
+
+ // Is the word a compounded word separated by delimiters?
+ // If so, keep track of all delimiters so each constituent
+ // word can be checked for two initial capital letters.
+ std::deque<sal_Int32> aDelimiters;
+
+ // Always check for two capitals at the beginning
+ // of the entire word, so start at nSttPos.
+ aDelimiters.push_back(nSttPos);
+
+ // Find all compound word delimiters
+ for (sal_Int32 n = nSttPos; n < nEndPos; ++n)
+ {
+ if (IsCompoundWordDelimChar(rTxt[ n ]))
+ {
+ aDelimiters.push_back( n + 1 ); // Get position of char after delimiter
+ }
+ }
+
+ // Decide where to put the terminating delimiter.
+ // If the last AutoCorrect char was a newline, then the AutoCorrect
+ // char will not be included in rTxt.
+ // If the last AutoCorrect char was not a newline, then the AutoCorrect
+ // character will be the last character in rTxt.
+ if (!IsCompoundWordDelimChar(rTxt[nEndPos-1]))
+ aDelimiters.push_back(nEndPos);
+
+ // Iterate through the word and all words that compose it.
+ // Two capital letters at the beginning of word?
+ for (size_t nI = 0; nI < aDelimiters.size() - 1; ++nI)
+ {
+ nSttPos = aDelimiters[nI];
+ nEndPos = aDelimiters[nI + 1];
+
+ if( nSttPos+2 < nEndPos &&
+ IsUpperLetter( rCC.getCharacterType( rTxt, nSttPos )) &&
+ IsUpperLetter( rCC.getCharacterType( rTxt, ++nSttPos )) &&
+ // Is the third character a lower case
+ IsLowerLetter( rCC.getCharacterType( rTxt, nSttPos +1 )) &&
+ // Do not replace special attributes
+ 0x1 != rTxt[ nSttPos ] && 0x2 != rTxt[ nSttPos ])
+ {
+ // test if the word is in an exception list
+ OUString sWord( rTxt.copy( nSttPos - 1, nEndPos - nSttPos + 1 ));
+ if( !FindInWordStartExceptList(eLang, sWord) )
+ {
+ // Check that word isn't correctly spelt before correcting:
+ css::uno::Reference< css::linguistic2::XSpellChecker1 > xSpeller =
+ LinguMgr::GetSpellChecker();
+ if( xSpeller->hasLanguage(static_cast<sal_uInt16>(eLang)) )
+ {
+ Sequence< css::beans::PropertyValue > aEmptySeq;
+ if (xSpeller->isValid(sWord, static_cast<sal_uInt16>(eLang), aEmptySeq))
+ {
+ return;
+ }
+ }
+ sal_Unicode cSave = rTxt[ nSttPos ];
+ OUString sChar = rCC.lowercase( OUString(cSave) );
+ if( sChar[0] != cSave && rDoc.ReplaceRange( nSttPos, 1, sChar ))
+ {
+ if( ACFlags::SaveWordWordStartLst & nFlags )
+ rDoc.SaveCpltSttWord( ACFlags::CapitalStartWord, nSttPos, sWord, cSave );
+ }
+ }
+ }
+ }
+}
+
+// Format ordinal numbers suffixes (1st -> 1^st)
+bool SvxAutoCorrect::FnChgOrdinalNumber(
+ SvxAutoCorrDoc& rDoc, const OUString& rTxt,
+ sal_Int32 nSttPos, sal_Int32 nEndPos,
+ LanguageType eLang)
+{
+ // 1st, 2nd, 3rd, 4 - 0th
+ // 201th or 201st
+ // 12th or 12nd
+ bool bChg = false;
+
+ // In some languages ordinal suffixes should never be
+ // changed to superscript. Let's break for those languages.
+ if (!eLang.anyOf(
+ LANGUAGE_SWEDISH,
+ LANGUAGE_SWEDISH_FINLAND))
+ {
+ CharClass& rCC = GetCharClass(eLang);
+
+ for (; nSttPos < nEndPos; ++nSttPos)
+ if (!lcl_IsInArr(sImplSttSkipChars, rTxt[nSttPos]))
+ break;
+ for (; nSttPos < nEndPos; --nEndPos)
+ if (!lcl_IsInArr(sImplEndSkipChars, rTxt[nEndPos - 1]))
+ break;
+
+
+ // Get the last number in the string to check
+ sal_Int32 nNumEnd = nEndPos;
+ bool bFoundEnd = false;
+ bool isValidNumber = true;
+ sal_Int32 i = nEndPos;
+ while (i > nSttPos)
+ {
+ i--;
+ bool isDigit = rCC.isDigit(rTxt, i);
+ if (bFoundEnd)
+ isValidNumber &= (isDigit || !rCC.isLetter(rTxt, i));
+
+ if (isDigit && !bFoundEnd)
+ {
+ bFoundEnd = true;
+ nNumEnd = i;
+ }
+ }
+
+ if (bFoundEnd && isValidNumber) {
+ sal_Int32 nNum = o3tl::toInt32(rTxt.subView(nSttPos, nNumEnd - nSttPos + 1));
+
+ // Check if the characters after that number correspond to the ordinal suffix
+ uno::Reference< i18n::XOrdinalSuffix > xOrdSuffix
+ = i18n::OrdinalSuffix::create(comphelper::getProcessComponentContext());
+
+ const uno::Sequence< OUString > aSuffixes = xOrdSuffix->getOrdinalSuffix(nNum, rCC.getLanguageTag().getLocale());
+ for (OUString const & sSuffix : aSuffixes)
+ {
+ std::u16string_view sEnd = rTxt.subView(nNumEnd + 1, nEndPos - nNumEnd - 1);
+
+ if (sSuffix == sEnd)
+ {
+ // Check if the ordinal suffix has to be set as super script
+ if (rCC.isLetter(sSuffix))
+ {
+ // Do the change
+ SvxEscapementItem aSvxEscapementItem(DFLT_ESC_AUTO_SUPER,
+ DFLT_ESC_PROP, SID_ATTR_CHAR_ESCAPEMENT);
+ rDoc.SetAttr(nNumEnd + 1, nEndPos,
+ SID_ATTR_CHAR_ESCAPEMENT,
+ aSvxEscapementItem);
+ bChg = true;
+ }
+ }
+ }
+ }
+ }
+ return bChg;
+}
+
+// Replace dashes
+bool SvxAutoCorrect::FnChgToEnEmDash(
+ SvxAutoCorrDoc& rDoc, const OUString& rTxt,
+ sal_Int32 nSttPos, sal_Int32 nEndPos,
+ LanguageType eLang )
+{
+ bool bRet = false;
+ CharClass& rCC = GetCharClass( eLang );
+ if (eLang == LANGUAGE_SYSTEM)
+ eLang = GetAppLang().getLanguageType();
+ bool bAlwaysUseEmDash = (eLang == LANGUAGE_RUSSIAN || eLang == LANGUAGE_UKRAINIAN);
+
+ // rTxt may refer to the frame text that will change in the calls to rDoc.Delete / rDoc.Insert;
+ // keep a local copy for later use
+ OUString aOrigTxt = rTxt;
+ sal_Int32 nFirstReplacementTextLengthChange = 0;
+
+ // replace " - " or " --" with "enDash"
+ if( 1 < nSttPos && 1 <= nEndPos - nSttPos )
+ {
+ sal_Unicode cCh = rTxt[ nSttPos ];
+ if( '-' == cCh )
+ {
+ if( 1 < nEndPos - nSttPos &&
+ ' ' == rTxt[ nSttPos-1 ] &&
+ '-' == rTxt[ nSttPos+1 ])
+ {
+ sal_Int32 n;
+ for( n = nSttPos+2; n < nEndPos && lcl_IsInArr(
+ sImplSttSkipChars,(cCh = rTxt[ n ]));
+ ++n )
+ ;
+
+ // found: " --[<AnySttChars>][A-z0-9]
+ if( rCC.isLetterNumeric( OUString(cCh) ) )
+ {
+ for( n = nSttPos-1; n && lcl_IsInArr(
+ sImplEndSkipChars,(cCh = rTxt[ --n ])); )
+ ;
+
+ // found: "[A-z0-9][<AnyEndChars>] --[<AnySttChars>][A-z0-9]
+ if( rCC.isLetterNumeric( OUString(cCh) ))
+ {
+ rDoc.Delete( nSttPos, nSttPos + 2 );
+ rDoc.Insert( nSttPos, bAlwaysUseEmDash ? sEmDash : sEnDash );
+ nFirstReplacementTextLengthChange = -1; // 2 ch -> 1 ch
+ bRet = true;
+ }
+ }
+ }
+ }
+ else if( 3 < nSttPos &&
+ ' ' == rTxt[ nSttPos-1 ] &&
+ '-' == rTxt[ nSttPos-2 ])
+ {
+ sal_Int32 n, nLen = 1, nTmpPos = nSttPos - 2;
+ if( '-' == ( cCh = rTxt[ nTmpPos-1 ]) )
+ {
+ --nTmpPos;
+ ++nLen;
+ cCh = rTxt[ nTmpPos-1 ];
+ }
+ if( ' ' == cCh )
+ {
+ for( n = nSttPos; n < nEndPos && lcl_IsInArr(
+ sImplSttSkipChars,(cCh = rTxt[ n ]));
+ ++n )
+ ;
+
+ // found: " - [<AnySttChars>][A-z0-9]
+ if( rCC.isLetterNumeric( OUString(cCh) ) )
+ {
+ cCh = ' ';
+ for( n = nTmpPos-1; n && lcl_IsInArr(
+ sImplEndSkipChars,(cCh = rTxt[ --n ])); )
+ ;
+ // found: "[A-z0-9][<AnyEndChars>] - [<AnySttChars>][A-z0-9]
+ if( rCC.isLetterNumeric( OUString(cCh) ))
+ {
+ rDoc.Delete( nTmpPos, nTmpPos + nLen );
+ rDoc.Insert( nTmpPos, bAlwaysUseEmDash ? sEmDash : sEnDash );
+ nFirstReplacementTextLengthChange = 1 - nLen; // nLen ch -> 1 ch
+ bRet = true;
+ }
+ }
+ }
+ }
+ }
+
+ // Replace [A-z0-9]--[A-z0-9] double dash with "emDash" or "enDash"
+ // [0-9]--[0-9] double dash always replaced with "enDash"
+ // Finnish and Hungarian use enDash instead of emDash.
+ bool bEnDash = (eLang == LANGUAGE_HUNGARIAN || eLang == LANGUAGE_FINNISH);
+ if( 4 <= nEndPos - nSttPos )
+ {
+ std::u16string_view sTmpView( aOrigTxt.subView( nSttPos, nEndPos - nSttPos ) );
+ size_t nFndPos = sTmpView.find(u"--");
+ if (nFndPos > 0 && nFndPos < sTmpView.size() - 2)
+ {
+ // Use proper codepoints. Currently, CharClass::isLetterNumeric is broken, it
+ // uses the index *both* as code unit index (when checking it as ASCII), *and*
+ // as code point index (when passes to css::i18n::XCharacterClassification).
+ // Oh well... Anyway, single-codepoint strings will workaround it.
+ sal_Int32 nStart = nSttPos + nFndPos;
+ sal_uInt32 chStart = aOrigTxt.iterateCodePoints(&nStart, -1);
+ OUString sStart(&chStart, 1);
+ // No idea why sImplEndSkipChars is checked at start
+ if (rCC.isLetterNumeric(sStart, 0) || lcl_IsInArr(sImplEndSkipChars, chStart))
+ {
+ sal_Int32 nEnd = nSttPos + nFndPos + 2;
+ sal_uInt32 chEnd = aOrigTxt.iterateCodePoints(&nEnd, 1);
+ OUString sEnd(&chEnd, 1);
+ // No idea why sImplSttSkipChars is checked at end
+ if (rCC.isLetterNumeric(sEnd, 0) || lcl_IsInArr(sImplSttSkipChars, chEnd))
+ {
+ nSttPos = nSttPos + nFndPos + nFirstReplacementTextLengthChange;
+ rDoc.Delete(nSttPos, nSttPos + 2);
+ rDoc.Insert(nSttPos,
+ (bEnDash || (rCC.isDigit(sStart, 0) && rCC.isDigit(sEnd, 0))
+ ? sEnDash
+ : sEmDash));
+ bRet = true;
+ }
+ }
+ }
+ }
+ return bRet;
+}
+
+// Add non-breaking space before specific punctuation marks in French text
+sal_Int32 SvxAutoCorrect::FnAddNonBrkSpace(
+ SvxAutoCorrDoc& rDoc, std::u16string_view rTxt,
+ sal_Int32 nEndPos,
+ LanguageType eLang, bool& io_bNbspRunNext )
+{
+ sal_Int32 nRet = -1;
+
+ CharClass& rCC = GetCharClass( eLang );
+
+ if ( rCC.getLanguageTag().getLanguage() == "fr" )
+ {
+ bool bFrCA = (rCC.getLanguageTag().getCountry() == "CA");
+ OUString allChars = ":;?!%";
+ OUString chars( allChars );
+ if ( bFrCA )
+ chars = ":";
+
+ sal_Unicode cChar = rTxt[ nEndPos ];
+ bool bHasSpace = chars.indexOf( cChar ) != -1;
+ bool bIsSpecial = allChars.indexOf( cChar ) != -1;
+ if ( bIsSpecial )
+ {
+ // Get the last word delimiter position
+ sal_Int32 nSttWdPos = nEndPos;
+ bool bWasWordDelim = false;
+ while( nSttWdPos )
+ {
+ bWasWordDelim = IsWordDelim( rTxt[ --nSttWdPos ]);
+ if (bWasWordDelim)
+ break;
+ }
+
+ //See if the text is the start of a protocol string, e.g. have text of
+ //"http" see if it is the start of "http:" and if so leave it alone
+ size_t nIndex = nSttWdPos + (bWasWordDelim ? 1 : 0);
+ size_t nProtocolLen = nEndPos - nSttWdPos + 1;
+ if (nIndex + nProtocolLen <= rTxt.size())
+ {
+ if (INetURLObject::CompareProtocolScheme(rTxt.substr(nIndex, nProtocolLen)) != INetProtocol::NotValid)
+ return -1;
+ }
+
+ // Check the presence of "://" in the word
+ size_t nStrPos = rTxt.find( u"://", nSttWdPos + 1 );
+ if ( nStrPos == std::u16string_view::npos && nEndPos > 0 )
+ {
+ // Check the previous char
+ sal_Unicode cPrevChar = rTxt[ nEndPos - 1 ];
+ if ( ( chars.indexOf( cPrevChar ) == -1 ) && cPrevChar != '\t' )
+ {
+ // Remove any previous normal space
+ sal_Int32 nPos = nEndPos - 1;
+ while ( cPrevChar == ' ' || cPrevChar == cNonBreakingSpace )
+ {
+ if ( nPos == 0 ) break;
+ nPos--;
+ cPrevChar = rTxt[ nPos ];
+ }
+
+ nPos++;
+ if ( nEndPos - nPos > 0 )
+ rDoc.Delete( nPos, nEndPos );
+
+ // Add the non-breaking space at the end pos
+ if ( bHasSpace )
+ rDoc.Insert( nPos, OUString(cNonBreakingSpace) );
+ io_bNbspRunNext = true;
+ nRet = nPos;
+ }
+ else if ( chars.indexOf( cPrevChar ) != -1 )
+ io_bNbspRunNext = true;
+ }
+ }
+ else if ( cChar == '/' && nEndPos > 1 && static_cast<sal_Int32>(rTxt.size()) > (nEndPos - 1) )
+ {
+ // Remove the hardspace right before to avoid formatting URLs
+ sal_Unicode cPrevChar = rTxt[ nEndPos - 1 ];
+ sal_Unicode cMaybeSpaceChar = rTxt[ nEndPos - 2 ];
+ if ( cPrevChar == ':' && cMaybeSpaceChar == cNonBreakingSpace )
+ {
+ rDoc.Delete( nEndPos - 2, nEndPos - 1 );
+ nRet = nEndPos - 1;
+ }
+ }
+ }
+
+ return nRet;
+}
+
+// URL recognition
+bool SvxAutoCorrect::FnSetINetAttr( SvxAutoCorrDoc& rDoc, const OUString& rTxt,
+ sal_Int32 nSttPos, sal_Int32 nEndPos,
+ LanguageType eLang )
+{
+ OUString sURL( URIHelper::FindFirstURLInText( rTxt, nSttPos, nEndPos,
+ GetCharClass( eLang ) ));
+ bool bRet = !sURL.isEmpty();
+ if( bRet ) // so, set attribute:
+ rDoc.SetINetAttr( nSttPos, nEndPos, sURL );
+ return bRet;
+}
+
+// DOI citation recognition
+bool SvxAutoCorrect::FnSetDOIAttr( SvxAutoCorrDoc& rDoc, const OUString& rTxt,
+ sal_Int32 nSttPos, sal_Int32 nEndPos,
+ LanguageType eLang )
+{
+ OUString sURL( URIHelper::FindFirstDOIInText( rTxt, nSttPos, nEndPos, GetCharClass( eLang ) ));
+ bool bRet = !sURL.isEmpty();
+ if( bRet ) // so, set attribute:
+ rDoc.SetINetAttr( nSttPos, nEndPos, sURL );
+ return bRet;
+}
+
+// Automatic *bold*, /italic/, -strikeout- and _underline_
+bool SvxAutoCorrect::FnChgWeightUnderl( SvxAutoCorrDoc& rDoc, const OUString& rTxt,
+ sal_Int32 nEndPos )
+{
+ // Condition:
+ // at the beginning: _, *, / or ~ after Space with the following !Space
+ // at the end: _, *, / or ~ before Space (word delimiter?)
+
+ sal_Unicode cInsChar = rTxt[ nEndPos ]; // underline, bold, italic or strikeout
+ if( ++nEndPos != rTxt.getLength() &&
+ !IsWordDelim( rTxt[ nEndPos ] ) )
+ return false;
+
+ --nEndPos;
+
+ bool bAlphaNum = false;
+ sal_Int32 nPos = nEndPos;
+ sal_Int32 nFndPos = -1;
+ CharClass& rCC = GetCharClass( LANGUAGE_SYSTEM );
+
+ while( nPos )
+ {
+ switch( sal_Unicode c = rTxt[ --nPos ] )
+ {
+ case '_':
+ case '-':
+ case '/':
+ case '*':
+ if( c == cInsChar )
+ {
+ if( bAlphaNum && nPos+1 < nEndPos && ( !nPos ||
+ IsWordDelim( rTxt[ nPos-1 ])) &&
+ !IsWordDelim( rTxt[ nPos+1 ]))
+ nFndPos = nPos;
+ else
+ // Condition is not satisfied, so cancel
+ nFndPos = -1;
+ nPos = 0;
+ }
+ break;
+ default:
+ if( !bAlphaNum )
+ bAlphaNum = rCC.isLetterNumeric( rTxt, nPos );
+ }
+ }
+
+ if( -1 != nFndPos )
+ {
+ // first delete the Character at the end - this allows insertion
+ // of an empty hint in SetAttr which would be removed by Delete
+ // (fdo#62536, AUTOFMT in Writer)
+ rDoc.Delete( nEndPos, nEndPos + 1 );
+
+ // Span the Attribute over the area
+ // the end.
+ if( '*' == cInsChar ) // Bold
+ {
+ SvxWeightItem aSvxWeightItem( WEIGHT_BOLD, SID_ATTR_CHAR_WEIGHT );
+ rDoc.SetAttr( nFndPos + 1, nEndPos,
+ SID_ATTR_CHAR_WEIGHT,
+ aSvxWeightItem);
+ }
+ else if( '/' == cInsChar ) // Italic
+ {
+ SvxPostureItem aSvxPostureItem( ITALIC_NORMAL, SID_ATTR_CHAR_POSTURE );
+ rDoc.SetAttr( nFndPos + 1, nEndPos,
+ SID_ATTR_CHAR_POSTURE,
+ aSvxPostureItem);
+ }
+ else if( '-' == cInsChar ) // Strikeout
+ {
+ SvxCrossedOutItem aSvxCrossedOutItem( STRIKEOUT_SINGLE, SID_ATTR_CHAR_STRIKEOUT );
+ rDoc.SetAttr( nFndPos + 1, nEndPos,
+ SID_ATTR_CHAR_STRIKEOUT,
+ aSvxCrossedOutItem);
+ }
+ else // Underline
+ {
+ SvxUnderlineItem aSvxUnderlineItem( LINESTYLE_SINGLE, SID_ATTR_CHAR_UNDERLINE );
+ rDoc.SetAttr( nFndPos + 1, nEndPos,
+ SID_ATTR_CHAR_UNDERLINE,
+ aSvxUnderlineItem);
+ }
+ rDoc.Delete( nFndPos, nFndPos + 1 );
+ }
+
+ return -1 != nFndPos;
+}
+
+// Capitalize first letter of every sentence
+void SvxAutoCorrect::FnCapitalStartSentence( SvxAutoCorrDoc& rDoc,
+ const OUString& rTxt, bool bNormalPos,
+ sal_Int32 nSttPos, sal_Int32 nEndPos,
+ LanguageType eLang )
+{
+
+ if( rTxt.isEmpty() || nEndPos <= nSttPos )
+ return;
+
+ CharClass& rCC = GetCharClass( eLang );
+ OUString aText( rTxt );
+ const sal_Unicode *pStart = aText.getStr(),
+ *pStr = pStart + nEndPos,
+ *pWordStt = nullptr,
+ *pDelim = nullptr;
+
+ bool bAtStart = false;
+ do {
+ --pStr;
+ if (rCC.isLetter(aText, pStr - pStart))
+ {
+ if( !pWordStt )
+ pDelim = pStr+1;
+ pWordStt = pStr;
+ }
+ else if (pWordStt && !rCC.isDigit(aText, pStr - pStart))
+ {
+ if( (lcl_IsInArr( u"-'", *pStr ) || *pStr == cApostrophe) && // These characters are allowed in words
+ pWordStt - 1 == pStr &&
+ // Installation at beginning of paragraph. Replaced < by <= (#i38971#)
+ (pStart + 1) <= pStr &&
+ rCC.isLetter(aText, pStr-1 - pStart))
+ pWordStt = --pStr;
+ else
+ break;
+ }
+ bAtStart = (pStart == pStr);
+ } while( !bAtStart );
+
+ if (!pWordStt)
+ return; // no character to be replaced
+
+
+ if (rCC.isDigit(aText, pStr - pStart))
+ return; // already ok
+
+ if (IsUpperLetter(rCC.getCharacterType(aText, pWordStt - pStart)))
+ return; // already ok
+
+ //See if the text is the start of a protocol string, e.g. have text of
+ //"http" see if it is the start of "http:" and if so leave it alone
+ sal_Int32 nIndex = pWordStt - pStart;
+ sal_Int32 nProtocolLen = pDelim - pWordStt + 1;
+ if (nIndex + nProtocolLen <= rTxt.getLength())
+ {
+ if (INetURLObject::CompareProtocolScheme(rTxt.subView(nIndex, nProtocolLen)) != INetProtocol::NotValid)
+ return; // already ok
+ }
+
+ if (0x1 == *pWordStt || 0x2 == *pWordStt)
+ return; // already ok
+
+ // Only capitalize, if string before specified characters is long enough
+ if( *pDelim && 2 >= pDelim - pWordStt &&
+ lcl_IsInArr( u".-)>", *pDelim ) )
+ return;
+
+ // tdf#59666 don't capitalize single Greek letters (except in Greek texts)
+ if ( 1 == pDelim - pWordStt && 0x03B1 <= *pWordStt && *pWordStt <= 0x03C9 && eLang != LANGUAGE_GREEK )
+ return;
+
+ if( !bAtStart ) // Still no beginning of a paragraph?
+ {
+ if (NonFieldWordDelim(*pStr))
+ {
+ for (;;)
+ {
+ bAtStart = (pStart == pStr--);
+ if (bAtStart || !NonFieldWordDelim(*pStr))
+ break;
+ }
+ }
+ // Asian full stop, full width full stop, full width exclamation mark
+ // and full width question marks are treated as word delimiters
+ else if ( 0x3002 != *pStr && 0xFF0E != *pStr && 0xFF01 != *pStr &&
+ 0xFF1F != *pStr )
+ return; // no valid separator -> no replacement
+ }
+
+ // No replacement for words in TWo INitial CApitals or sMALL iNITIAL list
+ if (FindInWordStartExceptList(eLang, OUString(pWordStt, pDelim - pWordStt)))
+ return;
+
+ if( bAtStart ) // at the beginning of a paragraph?
+ {
+ // Check out the previous paragraph, if it exists.
+ // If so, then check to paragraph separator at the end.
+ OUString const*const pPrevPara = rDoc.GetPrevPara(bNormalPos);
+ if (!pPrevPara)
+ {
+ // valid separator -> replace
+ OUString sChar( *pWordStt );
+ sChar = rCC.titlecase(sChar); //see fdo#56740
+ if (sChar != OUStringChar(*pWordStt))
+ rDoc.ReplaceRange( pWordStt - pStart, 1, sChar );
+ return;
+ }
+
+ aText = *pPrevPara;
+ bAtStart = false;
+ pStart = aText.getStr();
+ pStr = pStart + aText.getLength();
+
+ do { // overwrite all blanks
+ --pStr;
+ if (!NonFieldWordDelim(*pStr))
+ break;
+ bAtStart = (pStart == pStr);
+ } while( !bAtStart );
+
+ if( bAtStart )
+ return; // no valid separator -> no replacement
+ }
+
+ // Found [ \t]+[A-Z0-9]+ until here. Test now on the paragraph separator.
+ // all three can happen, but not more than once!
+ const sal_Unicode* pExceptStt = nullptr;
+ bool bContinue = true;
+ Flags nFlag = Flags::NONE;
+ do
+ {
+ switch (*pStr)
+ {
+ // Western and Asian full stop
+ case '.':
+ case 0x3002:
+ case 0xFF0E:
+ {
+ if (pStr >= pStart + 2 && *(pStr - 2) == '.')
+ {
+ //e.g. text "f.o.o. word": Now currently considering
+ //capitalizing word but second last character of
+ //previous word is a . So probably last word is an
+ //anagram that ends in . and not truly the end of a
+ //previous sentence, so don't autocapitalize this word
+ return;
+ }
+ if (nFlag & Flags::FullStop)
+ return; // no valid separator -> no replacement
+ nFlag |= Flags::FullStop;
+ pExceptStt = pStr;
+ }
+ break;
+ case '!':
+ case 0xFF01:
+ {
+ if (nFlag & Flags::ExclamationMark)
+ return; // no valid separator -> no replacement
+ nFlag |= Flags::ExclamationMark;
+ }
+ break;
+ case '?':
+ case 0xFF1F:
+ {
+ if (nFlag & Flags::QuestionMark)
+ return; // no valid separator -> no replacement
+ nFlag |= Flags::QuestionMark;
+ }
+ break;
+ default:
+ if (nFlag == Flags::NONE)
+ return; // no valid separator -> no replacement
+ else
+ bContinue = false;
+ break;
+ }
+
+ if (bContinue && pStr-- == pStart)
+ {
+ return; // no valid separator -> no replacement
+ }
+ } while (bContinue);
+ if (Flags::FullStop != nFlag)
+ pExceptStt = nullptr;
+
+ // Only capitalize, if string is long enough
+ if( 2 > ( pStr - pStart ) )
+ return;
+
+ if (!rCC.isLetterNumeric(aText, pStr-- - pStart))
+ {
+ bool bValid = false, bAlphaFnd = false;
+ const sal_Unicode* pTmpStr = pStr;
+ while( !bValid )
+ {
+ if( rCC.isDigit( aText, pTmpStr - pStart ) )
+ {
+ bValid = true;
+ pStr = pTmpStr - 1;
+ }
+ else if( rCC.isLetter( aText, pTmpStr - pStart ) )
+ {
+ if( bAlphaFnd )
+ {
+ bValid = true;
+ pStr = pTmpStr;
+ }
+ else
+ bAlphaFnd = true;
+ }
+ else if (bAlphaFnd || NonFieldWordDelim(*pTmpStr))
+ break;
+
+ if( pTmpStr == pStart )
+ break;
+
+ --pTmpStr;
+ }
+
+ if( !bValid )
+ return; // no valid separator -> no replacement
+ }
+
+ bool bNumericOnly = '0' <= *(pStr+1) && *(pStr+1) <= '9';
+
+ // Search for the beginning of the word
+ while (!NonFieldWordDelim(*pStr))
+ {
+ if( bNumericOnly && rCC.isLetter( aText, pStr - pStart ) )
+ bNumericOnly = false;
+
+ if( pStart == pStr )
+ break;
+
+ --pStr;
+ }
+
+ if( bNumericOnly ) // consists of only numbers, then not
+ return;
+
+ if (NonFieldWordDelim(*pStr))
+ ++pStr;
+
+ OUString sWord;
+
+ // check on the basis of the exception list
+ if( pExceptStt )
+ {
+ sWord = OUString(pStr, pExceptStt - pStr + 1);
+ if( FindInCplSttExceptList(eLang, sWord) )
+ return;
+
+ // Delete all non alphanumeric. Test the characters at the
+ // beginning/end of the word ( recognizes: "(min.", "/min.", and so on.)
+ OUString sTmp( sWord );
+ while( !sTmp.isEmpty() &&
+ !rCC.isLetterNumeric( sTmp, 0 ) )
+ sTmp = sTmp.copy(1);
+
+ // Remove all non alphanumeric characters towards the end up until
+ // the last one.
+ sal_Int32 nLen = sTmp.getLength();
+ while( nLen && !rCC.isLetterNumeric( sTmp, nLen-1 ) )
+ --nLen;
+ if( nLen + 1 < sTmp.getLength() )
+ sTmp = sTmp.copy( 0, nLen + 1 );
+
+ if( !sTmp.isEmpty() && sTmp.getLength() != sWord.getLength() &&
+ FindInCplSttExceptList(eLang, sTmp))
+ return;
+
+ if(FindInCplSttExceptList(eLang, sWord, true))
+ return;
+ }
+
+ // Ok, then replace
+ sal_Unicode cSave = *pWordStt;
+ nSttPos = pWordStt - rTxt.getStr();
+ OUString sChar = rCC.titlecase(OUString(cSave)); //see fdo#56740
+ bool bRet = sChar[0] != cSave && rDoc.ReplaceRange( nSttPos, 1, sChar );
+
+ // Perhaps someone wants to have the word
+ if( bRet && ACFlags::SaveWordCplSttLst & nFlags )
+ rDoc.SaveCpltSttWord( ACFlags::CapitalStartSentence, nSttPos, sWord, cSave );
+}
+
+// Correct accidental use of cAPS LOCK key
+bool SvxAutoCorrect::FnCorrectCapsLock( SvxAutoCorrDoc& rDoc, const OUString& rTxt,
+ sal_Int32 nSttPos, sal_Int32 nEndPos,
+ LanguageType eLang )
+{
+ if (nEndPos - nSttPos < 2)
+ // string must be at least 2-character long.
+ return false;
+
+ CharClass& rCC = GetCharClass( eLang );
+
+ // Check the first 2 letters.
+ if ( !IsLowerLetter(rCC.getCharacterType(rTxt, nSttPos)) )
+ return false;
+
+ if ( !IsUpperLetter(rCC.getCharacterType(rTxt, nSttPos+1)) )
+ return false;
+
+ OUStringBuffer aConverted;
+ aConverted.append( rCC.uppercase(OUString(rTxt[nSttPos])) );
+ aConverted.append( rCC.lowercase(OUString(rTxt[nSttPos+1])) );
+
+ // No replacement for words in TWo INitial CApitals or sMALL iNITIAL list
+ if (FindInWordStartExceptList(eLang, rTxt.copy(nSttPos, nEndPos - nSttPos)))
+ return false;
+
+ for( sal_Int32 i = nSttPos+2; i < nEndPos; ++i )
+ {
+ if ( IsLowerLetter(rCC.getCharacterType(rTxt, i)) )
+ // A lowercase letter disqualifies the whole text.
+ return false;
+
+ if ( IsUpperLetter(rCC.getCharacterType(rTxt, i)) )
+ // Another uppercase letter. Convert it.
+ aConverted.append( rCC.lowercase(OUString(rTxt[i])) );
+ else
+ // This is not an alphabetic letter. Leave it as-is.
+ aConverted.append( rTxt[i] );
+ }
+
+ // Replace the word.
+ rDoc.Delete(nSttPos, nEndPos);
+ rDoc.Insert(nSttPos, aConverted.makeStringAndClear());
+
+ return true;
+}
+
+
+sal_Unicode SvxAutoCorrect::GetQuote( sal_Unicode cInsChar, bool bSttQuote,
+ LanguageType eLang ) const
+{
+ sal_Unicode cRet = bSttQuote ? ( '\"' == cInsChar
+ ? GetStartDoubleQuote()
+ : GetStartSingleQuote() )
+ : ( '\"' == cInsChar
+ ? GetEndDoubleQuote()
+ : GetEndSingleQuote() );
+ if( !cRet )
+ {
+ // then through the Language find the right character
+ if( LANGUAGE_NONE == eLang )
+ cRet = cInsChar;
+ else
+ {
+ LocaleDataWrapper& rLcl = GetLocaleDataWrapper( eLang );
+ OUString sRet( bSttQuote
+ ? ( '\"' == cInsChar
+ ? rLcl.getDoubleQuotationMarkStart()
+ : rLcl.getQuotationMarkStart() )
+ : ( '\"' == cInsChar
+ ? rLcl.getDoubleQuotationMarkEnd()
+ : rLcl.getQuotationMarkEnd() ));
+ cRet = !sRet.isEmpty() ? sRet[0] : cInsChar;
+ }
+ }
+ return cRet;
+}
+
+void SvxAutoCorrect::InsertQuote( SvxAutoCorrDoc& rDoc, sal_Int32 nInsPos,
+ sal_Unicode cInsChar, bool bSttQuote,
+ bool bIns, LanguageType eLang, ACQuotes eType ) const
+{
+ sal_Unicode cRet;
+
+ if ( eType == ACQuotes::DoubleAngleQuote )
+ {
+ bool bSwiss = eLang == LANGUAGE_FRENCH_SWISS;
+ // pressing " inside a quotation -> use second level angle quotes
+ bool bLeftQuote = '\"' == cInsChar &&
+ // start position and Romanian OR
+ // not start position and Hungarian
+ bSttQuote == (eLang != LANGUAGE_HUNGARIAN);
+ cRet = ( '<' == cInsChar || bLeftQuote )
+ ? ( bSwiss ? cLeftSingleAngleQuote : cLeftDoubleAngleQuote )
+ : ( bSwiss ? cRightSingleAngleQuote : cRightDoubleAngleQuote );
+ }
+ else if ( eType == ACQuotes::UseApostrophe )
+ cRet = cApostrophe;
+ else
+ cRet = GetQuote( cInsChar, bSttQuote, eLang );
+
+ OUString sChg( cInsChar );
+ if( bIns )
+ rDoc.Insert( nInsPos, sChg );
+ else
+ rDoc.Replace( nInsPos, sChg );
+
+ sChg = OUString(cRet);
+
+ if( eType == ACQuotes::NonBreakingSpace )
+ {
+ if( rDoc.Insert( bSttQuote ? nInsPos+1 : nInsPos, OUStringChar(cNonBreakingSpace) ))
+ {
+ if( !bSttQuote )
+ ++nInsPos;
+ }
+ }
+ else if( eType == ACQuotes::DoubleAngleQuote && cInsChar != '\"' )
+ {
+ rDoc.Delete( nInsPos-1, nInsPos);
+ --nInsPos;
+ }
+
+ rDoc.Replace( nInsPos, sChg );
+
+ // i' -> I' in English (last step for the Undo)
+ if( eType == ACQuotes::CapitalizeIAm )
+ rDoc.Replace( nInsPos-1, "I" );
+}
+
+OUString SvxAutoCorrect::GetQuote( SvxAutoCorrDoc const & rDoc, sal_Int32 nInsPos,
+ sal_Unicode cInsChar, bool bSttQuote )
+{
+ const LanguageType eLang = GetDocLanguage( rDoc, nInsPos );
+ sal_Unicode cRet = GetQuote( cInsChar, bSttQuote, eLang );
+
+ OUString sRet(cRet);
+
+ if( '\"' == cInsChar )
+ {
+ if (primary(eLang) == primary(LANGUAGE_FRENCH) && eLang != LANGUAGE_FRENCH_SWISS)
+ {
+ if( bSttQuote )
+ sRet += " ";
+ else
+ sRet = " " + sRet;
+ }
+ }
+ return sRet;
+}
+
+// search preceding opening quote in the paragraph before the insert position
+static bool lcl_HasPrecedingChar( std::u16string_view rTxt, sal_Int32 nPos,
+ const sal_Unicode sPrecedingChar, const sal_Unicode sStopChar, const sal_Unicode* aStopChars )
+{
+ sal_Unicode cTmpChar;
+
+ do {
+ cTmpChar = rTxt[ --nPos ];
+ if ( cTmpChar == sPrecedingChar )
+ return true;
+
+ if ( cTmpChar == sStopChar )
+ return false;
+
+ for ( const sal_Unicode* pCh = aStopChars; *pCh; ++pCh )
+ if ( cTmpChar == *pCh )
+ return false;
+
+ } while ( nPos > 0 );
+
+ return false;
+}
+
+// WARNING: rText may become invalid, see comment below
+void SvxAutoCorrect::DoAutoCorrect( SvxAutoCorrDoc& rDoc, const OUString& rTxt,
+ sal_Int32 nInsPos, sal_Unicode cChar,
+ bool bInsert, bool& io_bNbspRunNext, vcl::Window const * pFrameWin )
+{
+ bool bIsNextRun = io_bNbspRunNext;
+ io_bNbspRunNext = false; // if it was set, then it has to be turned off
+
+ do{ // only for middle check loop !!
+ if( cChar )
+ {
+ // Prevent double space
+ if( nInsPos && ' ' == cChar &&
+ IsAutoCorrFlag( ACFlags::IgnoreDoubleSpace ) &&
+ ' ' == rTxt[ nInsPos - 1 ])
+ {
+ break;
+ }
+
+ bool bSingle = '\'' == cChar;
+ bool bIsReplaceQuote =
+ (IsAutoCorrFlag( ACFlags::ChgQuotes ) && ('\"' == cChar )) ||
+ (IsAutoCorrFlag( ACFlags::ChgSglQuotes ) && bSingle );
+ if( bIsReplaceQuote )
+ {
+ bool bSttQuote = !nInsPos;
+ ACQuotes eType = ACQuotes::NONE;
+ const LanguageType eLang = GetDocLanguage( rDoc, nInsPos );
+ if (!bSttQuote)
+ {
+ sal_Unicode cPrev = rTxt[ nInsPos-1 ];
+ bSttQuote = NonFieldWordDelim(cPrev) ||
+ lcl_IsInArr( u"([{", cPrev ) ||
+ ( cEmDash == cPrev ) ||
+ ( cEnDash == cPrev );
+ // tdf#38394 use opening quotation mark << in French l'<<word>>
+ if ( !bSingle && !bSttQuote && cPrev == cApostrophe &&
+ primary(eLang) == primary(LANGUAGE_FRENCH) &&
+ ( ( ( nInsPos == 2 || ( nInsPos > 2 && IsWordDelim( rTxt[ nInsPos-3 ] ) ) ) &&
+ // abbreviated form of ce, de, je, la, le, ne, me, te, se or si
+ OUString("cdjlnmtsCDJLNMTS").indexOf( rTxt[ nInsPos-2 ] ) > -1 ) ||
+ ( ( nInsPos == 3 || (nInsPos > 3 && IsWordDelim( rTxt[ nInsPos-4 ] ) ) ) &&
+ // abbreviated form of que
+ ( rTxt[ nInsPos-2 ] == 'u' || rTxt[ nInsPos-2 ] == 'U' ) &&
+ ( rTxt[ nInsPos-3 ] == 'q' || rTxt[ nInsPos-3 ] == 'Q' ) ) ) )
+ {
+ bSttQuote = true;
+ }
+ // tdf#108423 for capitalization of English i'm
+ else if ( bSingle && ( cPrev == 'i' ) &&
+ primary(eLang) == primary(LANGUAGE_ENGLISH) &&
+ ( nInsPos == 1 || IsWordDelim( rTxt[ nInsPos-2 ] ) ) )
+ {
+ eType = ACQuotes::CapitalizeIAm;
+ }
+ // tdf#133524 support >>Hungarian<< and <<Romanian>> secondary level quotations
+ else if ( !bSingle && nInsPos &&
+ ( ( eLang == LANGUAGE_HUNGARIAN &&
+ lcl_HasPrecedingChar( rTxt, nInsPos,
+ bSttQuote ? aStopDoubleAngleQuoteStart[0] : aStopDoubleAngleQuoteEnd[0],
+ bSttQuote ? aStopDoubleAngleQuoteStart[1] : aStopDoubleAngleQuoteEnd[1],
+ bSttQuote ? aStopDoubleAngleQuoteStart + 1 : aStopDoubleAngleQuoteEnd + 2 ) ) ||
+ ( eLang.anyOf(
+ LANGUAGE_ROMANIAN,
+ LANGUAGE_ROMANIAN_MOLDOVA ) &&
+ lcl_HasPrecedingChar( rTxt, nInsPos,
+ bSttQuote ? aStopDoubleAngleQuoteStart[0] : aStopDoubleAngleQuoteEndRo[0],
+ bSttQuote ? aStopDoubleAngleQuoteStart[1] : aStopDoubleAngleQuoteEndRo[1],
+ bSttQuote ? aStopDoubleAngleQuoteStart + 1 : aStopDoubleAngleQuoteEndRo + 2 ) ) ) )
+ {
+ LocaleDataWrapper& rLcl = GetLocaleDataWrapper( eLang );
+ // only if the opening double quotation mark is the default one
+ if ( rLcl.getDoubleQuotationMarkStart() == OUStringChar(aStopDoubleAngleQuoteStart[0]) )
+ eType = ACQuotes::DoubleAngleQuote;
+ }
+ else if ( bSingle && nInsPos && !bSttQuote &&
+ // tdf#128860 use apostrophe outside of second level quotation in Czech, German, Icelandic,
+ // Slovak and Slovenian instead of the – in this case, bad – closing quotation mark U+2018.
+ // tdf#123786 the same for Russian and Ukrainian
+ ( eLang.anyOf (
+ LANGUAGE_CZECH,
+ LANGUAGE_GERMAN,
+ LANGUAGE_GERMAN_SWISS,
+ LANGUAGE_GERMAN_AUSTRIAN,
+ LANGUAGE_GERMAN_LUXEMBOURG,
+ LANGUAGE_GERMAN_LIECHTENSTEIN,
+ LANGUAGE_ICELANDIC,
+ LANGUAGE_SLOVAK,
+ LANGUAGE_SLOVENIAN ) ) )
+ {
+ sal_Unicode sStartChar = GetStartSingleQuote();
+ sal_Unicode sEndChar = GetEndSingleQuote();
+ if ( !sStartChar || !sEndChar ) {
+ LocaleDataWrapper& rLcl = GetLocaleDataWrapper( eLang );
+ if ( !sStartChar ) sStartChar = rLcl.getQuotationMarkStart()[0];
+ if ( !sEndChar ) sEndChar = rLcl.getQuotationMarkStart()[0];
+ }
+ if ( !lcl_HasPrecedingChar( rTxt, nInsPos, sStartChar, sEndChar, aStopSingleQuoteEnd + 1 ) )
+ {
+ CharClass& rCC = GetCharClass( eLang );
+ if ( rCC.isLetter(rTxt, nInsPos-1) )
+ {
+ eType = ACQuotes::UseApostrophe;
+ }
+ }
+ }
+ else if ( bSingle && nInsPos && !bSttQuote &&
+ ( eLang.anyOf (
+ LANGUAGE_RUSSIAN,
+ LANGUAGE_UKRAINIAN ) &&
+ !lcl_HasPrecedingChar( rTxt, nInsPos, aStopSingleQuoteEndRuUa[0], aStopSingleQuoteEndRuUa[1], aStopSingleQuoteEndRuUa + 2 ) ) )
+ {
+ LocaleDataWrapper& rLcl = GetLocaleDataWrapper( eLang );
+ CharClass& rCC = GetCharClass( eLang );
+ if ( rLcl.getQuotationMarkStart() == OUStringChar(aStopSingleQuoteEndRuUa[0]) &&
+ // use apostrophe only after letters, not after digits or punctuation
+ rCC.isLetter(rTxt, nInsPos-1) )
+ {
+ eType = ACQuotes::UseApostrophe;
+ }
+ }
+ }
+
+ if ( eType == ACQuotes::NONE && !bSingle &&
+ ( primary(eLang) == primary(LANGUAGE_FRENCH) && eLang != LANGUAGE_FRENCH_SWISS ) )
+ eType = ACQuotes::NonBreakingSpace;
+
+ InsertQuote( rDoc, nInsPos, cChar, bSttQuote, bInsert, eLang, eType );
+ break;
+ }
+ // tdf#133524 change "<<" and ">>" to double angle quotation marks
+ else if ( IsAutoCorrFlag( ACFlags::ChgQuotes ) &&
+ IsAutoCorrFlag( ACFlags::ChgAngleQuotes ) &&
+ ('<' == cChar || '>' == cChar) &&
+ nInsPos > 0 && cChar == rTxt[ nInsPos-1 ] )
+ {
+ const LanguageType eLang = GetDocLanguage( rDoc, nInsPos );
+ if ( eLang.anyOf(
+ LANGUAGE_CATALAN, // primary level
+ LANGUAGE_CATALAN_VALENCIAN, // primary level
+ LANGUAGE_FINNISH, // alternative primary level
+ LANGUAGE_FRENCH_SWISS, // second level
+ LANGUAGE_GALICIAN, // primary level
+ LANGUAGE_HUNGARIAN, // second level
+ LANGUAGE_POLISH, // second level
+ LANGUAGE_PORTUGUESE, // primary level
+ LANGUAGE_PORTUGUESE_BRAZILIAN, // primary level
+ LANGUAGE_ROMANIAN, // second level
+ LANGUAGE_ROMANIAN_MOLDOVA, // second level
+ LANGUAGE_SWEDISH, // alternative primary level
+ LANGUAGE_SWEDISH_FINLAND, // alternative primary level
+ LANGUAGE_UKRAINIAN, // primary level
+ LANGUAGE_USER_ARAGONESE, // primary level
+ LANGUAGE_USER_ASTURIAN ) || // primary level
+ primary(eLang) == primary(LANGUAGE_GERMAN) || // alternative primary level
+ primary(eLang) == primary(LANGUAGE_SPANISH) ) // primary level
+ {
+ InsertQuote( rDoc, nInsPos, cChar, false, bInsert, eLang, ACQuotes::DoubleAngleQuote );
+ break;
+ }
+ }
+
+ if( bInsert )
+ rDoc.Insert( nInsPos, OUString(cChar) );
+ else
+ rDoc.Replace( nInsPos, OUString(cChar) );
+
+ // Hardspaces autocorrection
+ if ( IsAutoCorrFlag( ACFlags::AddNonBrkSpace ) )
+ {
+ // WARNING ATTENTION: rTxt is an alias of the text node's OUString
+ // and its length may change (even become shorter) if FnAddNonBrkSpace succeeds!
+ sal_Int32 nUpdatedPos = -1;
+ if (NeedsHardspaceAutocorr(cChar))
+ nUpdatedPos = FnAddNonBrkSpace( rDoc, rTxt, nInsPos, GetDocLanguage( rDoc, nInsPos ), io_bNbspRunNext );
+ if (nUpdatedPos >= 0)
+ {
+ nInsPos = nUpdatedPos;
+ }
+ else if ( bIsNextRun && !IsAutoCorrectChar( cChar ) )
+ {
+ // Remove the NBSP if it wasn't an autocorrection
+ if ( nInsPos != 0 && NeedsHardspaceAutocorr( rTxt[ nInsPos - 1 ] ) &&
+ cChar != ' ' && cChar != '\t' && cChar != cNonBreakingSpace )
+ {
+ // Look for the last HARD_SPACE
+ sal_Int32 nPos = nInsPos - 1;
+ bool bContinue = true;
+ while ( bContinue )
+ {
+ const sal_Unicode cTmpChar = rTxt[ nPos ];
+ if ( cTmpChar == cNonBreakingSpace )
+ {
+ rDoc.Delete( nPos, nPos + 1 );
+ bContinue = false;
+ }
+ else if ( !NeedsHardspaceAutocorr( cTmpChar ) || nPos == 0 )
+ bContinue = false;
+ nPos--;
+ }
+ }
+ }
+ }
+ }
+
+ if( !nInsPos )
+ break;
+
+ sal_Int32 nPos = nInsPos - 1;
+
+ if( IsWordDelim( rTxt[ nPos ]))
+ break;
+
+ // Set bold or underline automatically?
+ if (('*' == cChar || '_' == cChar || '/' == cChar || '-' == cChar) && (nPos+1 < rTxt.getLength()))
+ {
+ if( IsAutoCorrFlag( ACFlags::ChgWeightUnderl ) )
+ {
+ FnChgWeightUnderl( rDoc, rTxt, nPos+1 );
+ }
+ break;
+ }
+
+ while( nPos && !IsWordDelim( rTxt[ --nPos ]))
+ ;
+
+ // Found a Paragraph-start or a Blank, search for the word shortcut in
+ // auto.
+ sal_Int32 nCapLttrPos = nPos+1; // on the 1st Character
+ if( !nPos && !IsWordDelim( rTxt[ 0 ]))
+ --nCapLttrPos; // begin of paragraph and no blank
+
+ const LanguageType eLang = GetDocLanguage( rDoc, nCapLttrPos );
+ CharClass& rCC = GetCharClass( eLang );
+
+ // no symbol characters
+ if( lcl_IsSymbolChar( rCC, rTxt, nCapLttrPos, nInsPos ))
+ break;
+
+ if( IsAutoCorrFlag( ACFlags::Autocorrect ) &&
+ // tdf#134940 fix regression of arrow "-->" resulted by premature
+ // replacement of "--" since '>' was added to IsAutoCorrectChar()
+ '>' != cChar )
+ {
+ // WARNING ATTENTION: rTxt is an alias of the text node's OUString
+ // and becomes INVALID if ChgAutoCorrWord returns true!
+ // => use aPara/pPara to create a valid copy of the string!
+ OUString aPara;
+ OUString* pPara = IsAutoCorrFlag(ACFlags::CapitalStartSentence) ? &aPara : nullptr;
+
+ bool bChgWord = rDoc.ChgAutoCorrWord( nCapLttrPos, nInsPos,
+ *this, pPara );
+ if( !bChgWord )
+ {
+ sal_Int32 nCapLttrPos1 = nCapLttrPos, nInsPos1 = nInsPos;
+ while( nCapLttrPos1 < nInsPos &&
+ lcl_IsInArr( sImplSttSkipChars, rTxt[ nCapLttrPos1 ] )
+ )
+ ++nCapLttrPos1;
+ while( nCapLttrPos1 < nInsPos1 && nInsPos1 &&
+ lcl_IsInArr( sImplEndSkipChars, rTxt[ nInsPos1-1 ] )
+ )
+ --nInsPos1;
+
+ if( (nCapLttrPos1 != nCapLttrPos || nInsPos1 != nInsPos ) &&
+ nCapLttrPos1 < nInsPos1 &&
+ rDoc.ChgAutoCorrWord( nCapLttrPos1, nInsPos1, *this, pPara ))
+ {
+ bChgWord = true;
+ nCapLttrPos = nCapLttrPos1;
+ }
+ }
+
+ if( bChgWord )
+ {
+ if( !aPara.isEmpty() )
+ {
+ sal_Int32 nEnd = nCapLttrPos;
+ while( nEnd < aPara.getLength() &&
+ !IsWordDelim( aPara[ nEnd ]))
+ ++nEnd;
+
+ // Capital letter at beginning of paragraph?
+ if( IsAutoCorrFlag( ACFlags::CapitalStartSentence ) )
+ {
+ FnCapitalStartSentence( rDoc, aPara, false,
+ nCapLttrPos, nEnd, eLang );
+ }
+
+ if( IsAutoCorrFlag( ACFlags::ChgToEnEmDash ) )
+ {
+ FnChgToEnEmDash( rDoc, aPara, nCapLttrPos, nEnd, eLang );
+ }
+ }
+ break;
+ }
+ }
+
+ if( IsAutoCorrFlag( ACFlags::TransliterateRTL ) && GetDocLanguage( rDoc, nInsPos ) == LANGUAGE_HUNGARIAN )
+ {
+ // WARNING ATTENTION: rTxt is an alias of the text node's OUString
+ // and becomes INVALID if TransliterateRTLWord returns true!
+ if ( rDoc.TransliterateRTLWord( nCapLttrPos, nInsPos ) )
+ break;
+ }
+
+ if( ( IsAutoCorrFlag( ACFlags::ChgOrdinalNumber ) &&
+ (nInsPos >= 2 ) && // fdo#69762 avoid autocorrect for 2e-3
+ ( '-' != cChar || 'E' != rtl::toAsciiUpperCase(rTxt[nInsPos-1]) || '0' > rTxt[nInsPos-2] || '9' < rTxt[nInsPos-2] ) &&
+ FnChgOrdinalNumber( rDoc, rTxt, nCapLttrPos, nInsPos, eLang ) ) ||
+ ( IsAutoCorrFlag( ACFlags::SetINetAttr ) &&
+ ( ' ' == cChar || '\t' == cChar || 0x0a == cChar || !cChar ) &&
+ FnSetINetAttr( rDoc, rTxt, nCapLttrPos, nInsPos, eLang ) ) ||
+ ( IsAutoCorrFlag( ACFlags::SetDOIAttr ) &&
+ ( ' ' == cChar || '\t' == cChar || 0x0a == cChar || !cChar ) &&
+ FnSetDOIAttr( rDoc, rTxt, nCapLttrPos, nInsPos, eLang ) ) )
+ ;
+ else
+ {
+ bool bLockKeyOn = pFrameWin && (pFrameWin->GetIndicatorState() & KeyIndicatorState::CAPSLOCK);
+ bool bUnsupported = lcl_IsUnsupportedUnicodeChar( rCC, rTxt, nCapLttrPos, nInsPos );
+
+ if ( bLockKeyOn && IsAutoCorrFlag( ACFlags::CorrectCapsLock ) &&
+ FnCorrectCapsLock( rDoc, rTxt, nCapLttrPos, nInsPos, eLang ) )
+ {
+ // Correct accidental use of cAPS LOCK key (do this only when
+ // the caps or shift lock key is pressed). Turn off the caps
+ // lock afterwards.
+ pFrameWin->SimulateKeyPress( KEY_CAPSLOCK );
+ }
+
+ // Capital letter at beginning of paragraph ?
+ if( !bUnsupported &&
+ IsAutoCorrFlag( ACFlags::CapitalStartSentence ) )
+ {
+ FnCapitalStartSentence( rDoc, rTxt, true, nCapLttrPos, nInsPos, eLang );
+ }
+
+ // Two capital letters at beginning of word ??
+ if( !bUnsupported &&
+ IsAutoCorrFlag( ACFlags::CapitalStartWord ) )
+ {
+ FnCapitalStartWord( rDoc, rTxt, nCapLttrPos, nInsPos, eLang );
+ }
+
+ if( IsAutoCorrFlag( ACFlags::ChgToEnEmDash ) )
+ {
+ FnChgToEnEmDash( rDoc, rTxt, nCapLttrPos, nInsPos, eLang );
+ }
+ }
+
+ } while( false );
+}
+
+SvxAutoCorrectLanguageLists& SvxAutoCorrect::GetLanguageList_(
+ LanguageType eLang )
+{
+ LanguageTag aLanguageTag( eLang);
+ if (m_aLangTable.find(aLanguageTag) == m_aLangTable.end())
+ (void)CreateLanguageFile(aLanguageTag);
+ const auto iter = m_aLangTable.find(aLanguageTag);
+ assert(iter != m_aLangTable.end());
+ return iter->second;
+}
+
+void SvxAutoCorrect::SaveCplSttExceptList( LanguageType eLang )
+{
+ auto const iter = m_aLangTable.find(LanguageTag(eLang));
+ if (iter != m_aLangTable.end())
+ iter->second.SaveCplSttExceptList();
+ else
+ {
+ SAL_WARN("editeng", "Save an empty list? ");
+ }
+}
+
+void SvxAutoCorrect::SaveWordStartExceptList(LanguageType eLang)
+{
+ auto const iter = m_aLangTable.find(LanguageTag(eLang));
+ if (iter != m_aLangTable.end())
+ iter->second.SaveWordStartExceptList();
+ else
+ {
+ SAL_WARN("editeng", "Save an empty list? ");
+ }
+}
+
+// Adds a single word. The list will immediately be written to the file!
+bool SvxAutoCorrect::AddCplSttException( const OUString& rNew,
+ LanguageType eLang )
+{
+ SvxAutoCorrectLanguageLists* pLists = nullptr;
+ // either the right language is present or it will be this in the general list
+ auto iter = m_aLangTable.find(LanguageTag(eLang));
+ if (iter != m_aLangTable.end())
+ pLists = &iter->second;
+ else
+ {
+ LanguageTag aLangTagUndetermined( LANGUAGE_UNDETERMINED);
+ iter = m_aLangTable.find(aLangTagUndetermined);
+ if (iter != m_aLangTable.end())
+ pLists = &iter->second;
+ else if(CreateLanguageFile(aLangTagUndetermined))
+ {
+ iter = m_aLangTable.find(aLangTagUndetermined);
+ assert(iter != m_aLangTable.end());
+ pLists = &iter->second;
+ }
+ }
+ OSL_ENSURE(pLists, "No auto correction data");
+ return pLists && pLists->AddToCplSttExceptList(rNew);
+}
+
+// Adds a single word. The list will immediately be written to the file!
+bool SvxAutoCorrect::AddWordStartException( const OUString& rNew,
+ LanguageType eLang )
+{
+ SvxAutoCorrectLanguageLists* pLists = nullptr;
+ //either the right language is present or it is set in the general list
+ auto iter = m_aLangTable.find(LanguageTag(eLang));
+ if (iter != m_aLangTable.end())
+ pLists = &iter->second;
+ else
+ {
+ LanguageTag aLangTagUndetermined( LANGUAGE_UNDETERMINED);
+ iter = m_aLangTable.find(aLangTagUndetermined);
+ if (iter != m_aLangTable.end())
+ pLists = &iter->second;
+ else if(CreateLanguageFile(aLangTagUndetermined))
+ {
+ iter = m_aLangTable.find(aLangTagUndetermined);
+ assert(iter != m_aLangTable.end());
+ pLists = &iter->second;
+ }
+ }
+ OSL_ENSURE(pLists, "No auto correction file!");
+ return pLists && pLists->AddToWordStartExceptList(rNew);
+}
+
+OUString SvxAutoCorrect::GetPrevAutoCorrWord(SvxAutoCorrDoc const& rDoc, const OUString& rTxt,
+ sal_Int32 nPos)
+{
+ OUString sRet;
+ if( !nPos )
+ return sRet;
+
+ sal_Int32 nEnd = nPos;
+
+ // it must be followed by a blank or tab!
+ if( ( nPos < rTxt.getLength() &&
+ !IsWordDelim( rTxt[ nPos ])) ||
+ IsWordDelim( rTxt[ --nPos ]))
+ return sRet;
+
+ while( nPos && !IsWordDelim( rTxt[ --nPos ]))
+ ;
+
+ // Found a Paragraph-start or a Blank, search for the word shortcut in
+ // auto.
+ sal_Int32 nCapLttrPos = nPos+1; // on the 1st Character
+ if( !nPos && !IsWordDelim( rTxt[ 0 ]))
+ --nCapLttrPos; // Beginning of paragraph and no Blank!
+
+ while( lcl_IsInArr( sImplSttSkipChars, rTxt[ nCapLttrPos ]) )
+ if( ++nCapLttrPos >= nEnd )
+ return sRet;
+
+ if( 3 > nEnd - nCapLttrPos )
+ return sRet;
+
+ const LanguageType eLang = GetDocLanguage( rDoc, nCapLttrPos );
+
+ CharClass& rCC = GetCharClass(eLang);
+
+ if( lcl_IsSymbolChar( rCC, rTxt, nCapLttrPos, nEnd ))
+ return sRet;
+
+ sRet = rTxt.copy( nCapLttrPos, nEnd - nCapLttrPos );
+ return sRet;
+}
+
+// static
+std::vector<OUString> SvxAutoCorrect::GetChunkForAutoText(std::u16string_view rTxt,
+ const sal_Int32 nPos)
+{
+ constexpr sal_Int32 nMinLen = 3;
+ constexpr sal_Int32 nMaxLen = 9;
+ std::vector<OUString> aRes;
+ if (nPos >= nMinLen)
+ {
+ sal_Int32 nBegin = std::max<sal_Int32>(nPos - nMaxLen, 0);
+ // TODO: better detect word boundaries (not only whitespaces, but also e.g. punctuation)
+ if (nBegin > 0 && !IsWordDelim(rTxt[nBegin-1]))
+ {
+ while (nBegin + nMinLen <= nPos && !IsWordDelim(rTxt[nBegin]))
+ ++nBegin;
+ }
+ if (nBegin + nMinLen <= nPos)
+ {
+ OUString sRes( rTxt.substr(nBegin, nPos - nBegin) );
+ aRes.push_back(sRes);
+ bool bLastStartedWithDelim = IsWordDelim(sRes[0]);
+ for (sal_Int32 i = 1; i <= sRes.getLength() - nMinLen; ++i)
+ {
+ bool bAdd = bLastStartedWithDelim;
+ bLastStartedWithDelim = IsWordDelim(sRes[i]);
+ bAdd = bAdd || bLastStartedWithDelim;
+ if (bAdd)
+ aRes.push_back(sRes.copy(i));
+ }
+ }
+ }
+ return aRes;
+}
+
+bool SvxAutoCorrect::CreateLanguageFile( const LanguageTag& rLanguageTag, bool bNewFile )
+{
+ OSL_ENSURE(m_aLangTable.find(rLanguageTag) == m_aLangTable.end(), "Language already exists ");
+
+ OUString sUserDirFile( GetAutoCorrFileName( rLanguageTag, true ));
+ OUString sShareDirFile( sUserDirFile );
+
+ SvxAutoCorrectLanguageLists* pLists = nullptr;
+
+ tools::Time nMinTime( 0, 2 ), nAktTime( tools::Time::SYSTEM ), nLastCheckTime( tools::Time::EMPTY );
+
+ auto nFndPos = aLastFileTable.find(rLanguageTag);
+ if(nFndPos != aLastFileTable.end() &&
+ (nLastCheckTime.SetTime(nFndPos->second), nLastCheckTime < nAktTime) &&
+ nAktTime - nLastCheckTime < nMinTime)
+ {
+ // no need to test the file, because the last check is not older then
+ // 2 minutes.
+ if( bNewFile )
+ {
+ sShareDirFile = sUserDirFile;
+ auto itBool = m_aLangTable.emplace(std::piecewise_construct,
+ std::forward_as_tuple(rLanguageTag),
+ std::forward_as_tuple(*this, sShareDirFile, sUserDirFile));
+ pLists = &itBool.first->second;
+ aLastFileTable.erase(nFndPos);
+ }
+ }
+ else if(
+ ( FStatHelper::IsDocument( sUserDirFile ) ||
+ FStatHelper::IsDocument( sShareDirFile =
+ GetAutoCorrFileName( rLanguageTag ) ) ||
+ FStatHelper::IsDocument( sShareDirFile =
+ GetAutoCorrFileName( rLanguageTag, false, false, true) )
+ ) ||
+ ( sShareDirFile = sUserDirFile, bNewFile )
+ )
+ {
+ auto itBool = m_aLangTable.emplace(std::piecewise_construct,
+ std::forward_as_tuple(rLanguageTag),
+ std::forward_as_tuple(*this, sShareDirFile, sUserDirFile));
+ pLists = &itBool.first->second;
+ if (nFndPos != aLastFileTable.end())
+ aLastFileTable.erase(nFndPos);
+ }
+ else if( !bNewFile )
+ {
+ aLastFileTable[rLanguageTag] = nAktTime.GetTime();
+ }
+ return pLists != nullptr;
+}
+
+bool SvxAutoCorrect::PutText( const OUString& rShort, const OUString& rLong,
+ LanguageType eLang )
+{
+ LanguageTag aLanguageTag( eLang);
+ if (auto const iter = m_aLangTable.find(aLanguageTag); iter != m_aLangTable.end())
+ return iter->second.PutText(rShort, rLong);
+ if (CreateLanguageFile(aLanguageTag))
+ {
+ auto const iter = m_aLangTable.find(aLanguageTag);
+ assert (iter != m_aLangTable.end());
+ return iter->second.PutText(rShort, rLong);
+ }
+ return false;
+}
+
+void SvxAutoCorrect::MakeCombinedChanges( std::vector<SvxAutocorrWord>& aNewEntries,
+ std::vector<SvxAutocorrWord>& aDeleteEntries,
+ LanguageType eLang )
+{
+ LanguageTag aLanguageTag( eLang);
+ auto iter = m_aLangTable.find(aLanguageTag);
+ if (iter != m_aLangTable.end())
+ {
+ iter->second.MakeCombinedChanges( aNewEntries, aDeleteEntries );
+ }
+ else if(CreateLanguageFile( aLanguageTag ))
+ {
+ iter = m_aLangTable.find(aLanguageTag);
+ assert(iter != m_aLangTable.end());
+ iter->second.MakeCombinedChanges( aNewEntries, aDeleteEntries );
+ }
+}
+
+// - return the replacement text (only for SWG-Format, all other
+// can be taken from the word list!)
+bool SvxAutoCorrect::GetLongText( const OUString&, OUString& )
+{
+ return false;
+}
+
+void SvxAutoCorrect::refreshBlockList( const uno::Reference< embed::XStorage >& )
+{
+}
+
+// Text with attribution (only the SWG - SWG format!)
+bool SvxAutoCorrect::PutText( const css::uno::Reference < css::embed::XStorage >&,
+ const OUString&, const OUString&, SfxObjectShell&, OUString& )
+{
+ return false;
+}
+
+OUString EncryptBlockName_Imp(std::u16string_view rName)
+{
+ OUStringBuffer aName;
+ aName.append('#').append(rName);
+ for (size_t nLen = rName.size(), nPos = 1; nPos < nLen; ++nPos)
+ {
+ if (lcl_IsInArr( u"!/:.\\", aName[nPos]))
+ aName[nPos] &= 0x0f;
+ }
+ return aName.makeStringAndClear();
+}
+
+/* This code is copied from SwXMLTextBlocks::GeneratePackageName */
+static void GeneratePackageName ( std::u16string_view rShort, OUString& rPackageName )
+{
+ OString sByte(OUStringToOString(rShort, RTL_TEXTENCODING_UTF7));
+ OUStringBuffer aBuf(OStringToOUString(sByte, RTL_TEXTENCODING_ASCII_US));
+
+ for (sal_Int32 nPos = 0; nPos < aBuf.getLength(); ++nPos)
+ {
+ switch (aBuf[nPos])
+ {
+ case '!':
+ case '/':
+ case ':':
+ case '.':
+ case '\\':
+ aBuf[nPos] = '_';
+ break;
+ default:
+ break;
+ }
+ }
+
+ rPackageName = aBuf.makeStringAndClear();
+}
+
+static const SvxAutocorrWord* lcl_SearchWordsInList(
+ SvxAutoCorrectLanguageLists* pList, std::u16string_view rTxt,
+ sal_Int32& rStt, sal_Int32 nEndPos)
+{
+ const SvxAutocorrWordList* pAutoCorrWordList = pList->GetAutocorrWordList();
+ return pAutoCorrWordList->SearchWordsInList( rTxt, rStt, nEndPos );
+}
+
+// the search for the words in the substitution table
+const SvxAutocorrWord* SvxAutoCorrect::SearchWordsInList(
+ std::u16string_view rTxt, sal_Int32& rStt, sal_Int32 nEndPos,
+ SvxAutoCorrDoc&, LanguageTag& rLang )
+{
+ const SvxAutocorrWord* pRet = nullptr;
+ LanguageTag aLanguageTag( rLang);
+ if( aLanguageTag.isSystemLocale() )
+ aLanguageTag.reset( MsLangId::getConfiguredSystemLanguage());
+
+ /* TODO-BCP47: this is so ugly, should all maybe be a proper fallback
+ * list instead? */
+
+ // First search for eLang, then US-English -> English
+ // and last in LANGUAGE_UNDETERMINED
+ if (m_aLangTable.find(aLanguageTag) != m_aLangTable.end() || CreateLanguageFile(aLanguageTag, false))
+ {
+ //the language is available - so bring it on
+ const auto iter = m_aLangTable.find(aLanguageTag);
+ assert(iter != m_aLangTable.end());
+ SvxAutoCorrectLanguageLists & rList = iter->second;
+ pRet = lcl_SearchWordsInList( &rList, rTxt, rStt, nEndPos );
+ if( pRet )
+ {
+ rLang = aLanguageTag;
+ return pRet;
+ }
+ else
+ return nullptr;
+ }
+
+ // If it still could not be found here, then keep on searching
+ LanguageType eLang = aLanguageTag.getLanguageType();
+ // the primary language for example EN
+ aLanguageTag.reset(aLanguageTag.getLanguage());
+ LanguageType nTmpKey = aLanguageTag.getLanguageType(false);
+ if (nTmpKey != eLang && nTmpKey != LANGUAGE_UNDETERMINED &&
+ (m_aLangTable.find(aLanguageTag) != m_aLangTable.end() ||
+ CreateLanguageFile(aLanguageTag, false)))
+ {
+ //the language is available - so bring it on
+ SvxAutoCorrectLanguageLists& rList = m_aLangTable.find(aLanguageTag)->second;
+ pRet = lcl_SearchWordsInList( &rList, rTxt, rStt, nEndPos );
+ if( pRet )
+ {
+ rLang = aLanguageTag;
+ return pRet;
+ }
+ }
+
+ if (m_aLangTable.find(aLanguageTag.reset(LANGUAGE_UNDETERMINED)) != m_aLangTable.end() ||
+ CreateLanguageFile(aLanguageTag, false))
+ {
+ //the language is available - so bring it on
+ const auto iter = m_aLangTable.find(aLanguageTag);
+ assert(iter != m_aLangTable.end());
+ SvxAutoCorrectLanguageLists& rList = iter->second;
+ pRet = lcl_SearchWordsInList( &rList, rTxt, rStt, nEndPos );
+ if( pRet )
+ {
+ rLang = aLanguageTag;
+ return pRet;
+ }
+ }
+ return nullptr;
+}
+
+bool SvxAutoCorrect::FindInWordStartExceptList( LanguageType eLang,
+ const OUString& sWord )
+{
+ LanguageTag aLanguageTag( eLang);
+
+ /* TODO-BCP47: again horrible ugliness */
+
+ // First search for eLang, then primary language of eLang
+ // and last in LANGUAGE_UNDETERMINED
+
+ if (m_aLangTable.find(aLanguageTag) != m_aLangTable.end() || CreateLanguageFile(aLanguageTag, false))
+ {
+ //the language is available - so bring it on
+ const auto iter = m_aLangTable.find(aLanguageTag);
+ assert(iter != m_aLangTable.end() && "CreateLanguageFile can't fail");
+ auto& rList = iter->second;
+ if(rList.GetWordStartExceptList()->find(sWord) != rList.GetWordStartExceptList()->end() )
+ return true;
+ }
+
+ // If it still could not be found here, then keep on searching
+ // the primary language for example EN
+ aLanguageTag.reset(aLanguageTag.getLanguage());
+ LanguageType nTmpKey = aLanguageTag.getLanguageType(false);
+ if (nTmpKey != eLang && nTmpKey != LANGUAGE_UNDETERMINED &&
+ (m_aLangTable.find(aLanguageTag) != m_aLangTable.end() ||
+ CreateLanguageFile(aLanguageTag, false)))
+ {
+ //the language is available - so bring it on
+ const auto iter = m_aLangTable.find(aLanguageTag);
+ assert(iter != m_aLangTable.end() && "CreateLanguageFile can't fail");
+ auto& rList = iter->second;
+ if(rList.GetWordStartExceptList()->find(sWord) != rList.GetWordStartExceptList()->end() )
+ return true;
+ }
+
+ if (m_aLangTable.find(aLanguageTag.reset(LANGUAGE_UNDETERMINED)) != m_aLangTable.end() ||
+ CreateLanguageFile(aLanguageTag, false))
+ {
+ //the language is available - so bring it on
+ const auto iter = m_aLangTable.find(aLanguageTag);
+ assert(iter != m_aLangTable.end());
+ auto& rList = iter->second;
+ if(rList.GetWordStartExceptList()->find(sWord) != rList.GetWordStartExceptList()->end() )
+ return true;
+ }
+ return false;
+}
+
+static bool lcl_FindAbbreviation(const SvStringsISortDtor* pList, const OUString& sWord)
+{
+ SvStringsISortDtor::const_iterator it = pList->find( "~" );
+ SvStringsISortDtor::size_type nPos = it - pList->begin();
+ if( nPos < pList->size() )
+ {
+ OUString sLowerWord(sWord.toAsciiLowerCase());
+ OUString sAbr;
+ for( SvStringsISortDtor::size_type n = nPos; n < pList->size(); ++n )
+ {
+ sAbr = (*pList)[ n ];
+ if (sAbr[0] != '~')
+ break;
+ // ~ and ~. are not allowed!
+ if( 2 < sAbr.getLength() && sAbr.getLength() - 1 <= sWord.getLength() )
+ {
+ OUString sLowerAbk(sAbr.toAsciiLowerCase());
+ for (sal_Int32 i = sLowerAbk.getLength(), ii = sLowerWord.getLength(); i;)
+ {
+ if( !--i ) // agrees
+ return true;
+
+ if( sLowerAbk[i] != sLowerWord[--ii])
+ break;
+ }
+ }
+ }
+ }
+ OSL_ENSURE( !(nPos && '~' == (*pList)[ --nPos ][ 0 ] ),
+ "Wrongly sorted exception list?" );
+ return false;
+}
+
+bool SvxAutoCorrect::FindInCplSttExceptList(LanguageType eLang,
+ const OUString& sWord, bool bAbbreviation)
+{
+ LanguageTag aLanguageTag( eLang);
+
+ /* TODO-BCP47: did I mention terrible horrible ugliness? */
+
+ // First search for eLang, then primary language of eLang
+ // and last in LANGUAGE_UNDETERMINED
+
+ if (m_aLangTable.find(aLanguageTag) != m_aLangTable.end() || CreateLanguageFile(aLanguageTag, false))
+ {
+ //the language is available - so bring it on
+ const auto iter = m_aLangTable.find(aLanguageTag);
+ assert(iter != m_aLangTable.end() && "CreateLanguageFile can't fail");
+ const SvStringsISortDtor* pList = iter->second.GetCplSttExceptList();
+ if(bAbbreviation ? lcl_FindAbbreviation(pList, sWord) : pList->find(sWord) != pList->end() )
+ return true;
+ }
+
+ // If it still could not be found here, then keep on searching
+ // the primary language for example EN
+ aLanguageTag.reset(aLanguageTag.getLanguage());
+ LanguageType nTmpKey = aLanguageTag.getLanguageType(false);
+ if (nTmpKey != eLang && nTmpKey != LANGUAGE_UNDETERMINED &&
+ (m_aLangTable.find(aLanguageTag) != m_aLangTable.end() ||
+ CreateLanguageFile(aLanguageTag, false)))
+ {
+ //the language is available - so bring it on
+ const auto iter = m_aLangTable.find(aLanguageTag);
+ assert(iter != m_aLangTable.end() && "CreateLanguageFile can't fail");
+ const SvStringsISortDtor* pList = iter->second.GetCplSttExceptList();
+ if(bAbbreviation ? lcl_FindAbbreviation(pList, sWord) : pList->find(sWord) != pList->end() )
+ return true;
+ }
+
+ if (m_aLangTable.find(aLanguageTag.reset(LANGUAGE_UNDETERMINED)) != m_aLangTable.end() ||
+ CreateLanguageFile(aLanguageTag, false))
+ {
+ //the language is available - so bring it on
+ const auto iter = m_aLangTable.find(aLanguageTag);
+ assert(iter != m_aLangTable.end() && "CreateLanguageFile can't fail");
+ const SvStringsISortDtor* pList = iter->second.GetCplSttExceptList();
+ if(bAbbreviation ? lcl_FindAbbreviation(pList, sWord) : pList->find(sWord) != pList->end() )
+ return true;
+ }
+ return false;
+}
+
+OUString SvxAutoCorrect::GetAutoCorrFileName( const LanguageTag& rLanguageTag,
+ bool bNewFile, bool bTst, bool bUnlocalized ) const
+{
+ OUString sRet, sExt( rLanguageTag.getBcp47() );
+ if (bUnlocalized)
+ {
+ // we don't want variant, so we'll take "fr" instead of "fr-CA" for example
+ std::vector< OUString > vecFallBackStrings = rLanguageTag.getFallbackStrings(false);
+ if (!vecFallBackStrings.empty())
+ sExt = vecFallBackStrings[0];
+ }
+
+ sExt = "_" + sExt + ".dat";
+ if( bNewFile )
+ sRet = sUserAutoCorrFile + sExt;
+ else if( !bTst )
+ sRet = sShareAutoCorrFile + sExt;
+ else
+ {
+ // test first in the user directory - if not exist, then
+ sRet = sUserAutoCorrFile + sExt;
+ if( !FStatHelper::IsDocument( sRet ))
+ sRet = sShareAutoCorrFile + sExt;
+ }
+ return sRet;
+}
+
+SvxAutoCorrectLanguageLists::SvxAutoCorrectLanguageLists(
+ SvxAutoCorrect& rParent,
+ OUString aShareAutoCorrectFile,
+ OUString aUserAutoCorrectFile)
+: sShareAutoCorrFile(std::move( aShareAutoCorrectFile )),
+ sUserAutoCorrFile(std::move( aUserAutoCorrectFile )),
+ aModifiedDate( Date::EMPTY ),
+ aModifiedTime( tools::Time::EMPTY ),
+ aLastCheckTime( tools::Time::EMPTY ),
+ rAutoCorrect(rParent),
+ nFlags(ACFlags::NONE)
+{
+}
+
+SvxAutoCorrectLanguageLists::~SvxAutoCorrectLanguageLists()
+{
+}
+
+bool SvxAutoCorrectLanguageLists::IsFileChanged_Imp()
+{
+ // Access the file system only every 2 minutes to check the date stamp
+ bool bRet = false;
+
+ tools::Time nMinTime( 0, 2 );
+ tools::Time nAktTime( tools::Time::SYSTEM );
+ if( aLastCheckTime <= nAktTime) // overflow?
+ return false;
+ nAktTime -= aLastCheckTime;
+ if( nAktTime > nMinTime ) // min time past
+ {
+ Date aTstDate( Date::EMPTY ); tools::Time aTstTime( tools::Time::EMPTY );
+ if( FStatHelper::GetModifiedDateTimeOfFile( sShareAutoCorrFile,
+ &aTstDate, &aTstTime ) &&
+ ( aModifiedDate != aTstDate || aModifiedTime != aTstTime ))
+ {
+ bRet = true;
+ // then remove all the lists fast!
+ if( (ACFlags::CplSttLstLoad & nFlags) && pCplStt_ExcptLst )
+ {
+ pCplStt_ExcptLst.reset();
+ }
+ if( (ACFlags::WordStartLstLoad & nFlags) && pWordStart_ExcptLst )
+ {
+ pWordStart_ExcptLst.reset();
+ }
+ if( (ACFlags::ChgWordLstLoad & nFlags) && pAutocorr_List )
+ {
+ pAutocorr_List.reset();
+ }
+ nFlags &= ~ACFlags(ACFlags::CplSttLstLoad | ACFlags::WordStartLstLoad | ACFlags::ChgWordLstLoad );
+ }
+ aLastCheckTime = tools::Time( tools::Time::SYSTEM );
+ }
+ return bRet;
+}
+
+void SvxAutoCorrectLanguageLists::LoadXMLExceptList_Imp(
+ std::unique_ptr<SvStringsISortDtor>& rpLst,
+ const OUString& sStrmName,
+ tools::SvRef<SotStorage>& rStg)
+{
+ if( rpLst )
+ rpLst->clear();
+ else
+ rpLst.reset( new SvStringsISortDtor );
+
+ {
+ if( rStg.is() && rStg->IsStream( sStrmName ) )
+ {
+ tools::SvRef<SotStorageStream> xStrm = rStg->OpenSotStream( sStrmName,
+ ( StreamMode::READ | StreamMode::SHARE_DENYWRITE | StreamMode::NOCREATE ) );
+ if( ERRCODE_NONE != xStrm->GetError())
+ {
+ xStrm.clear();
+ rStg.clear();
+ RemoveStream_Imp( sStrmName );
+ }
+ else
+ {
+ uno::Reference< uno::XComponentContext > xContext =
+ comphelper::getProcessComponentContext();
+
+ xml::sax::InputSource aParserInput;
+ aParserInput.sSystemId = sStrmName;
+
+ xStrm->Seek( 0 );
+ xStrm->SetBufferSize( 8 * 1024 );
+ aParserInput.aInputStream = new utl::OInputStreamWrapper( *xStrm );
+
+ // get filter
+ uno::Reference< xml::sax::XFastDocumentHandler > xFilter = new SvXMLExceptionListImport ( xContext, *rpLst );
+
+ // connect parser and filter
+ uno::Reference< xml::sax::XFastParser > xParser = xml::sax::FastParser::create( xContext );
+ uno::Reference<xml::sax::XFastTokenHandler> xTokenHandler = new SvXMLAutoCorrectTokenHandler;
+ xParser->setFastDocumentHandler( xFilter );
+ xParser->registerNamespace( "http://openoffice.org/2001/block-list", SvXMLAutoCorrectToken::NAMESPACE );
+ xParser->setTokenHandler( xTokenHandler );
+
+ // parse
+ try
+ {
+ xParser->parseStream( aParserInput );
+ }
+ catch( const xml::sax::SAXParseException& )
+ {
+ // re throw ?
+ }
+ catch( const xml::sax::SAXException& )
+ {
+ // re throw ?
+ }
+ catch( const io::IOException& )
+ {
+ // re throw ?
+ }
+ }
+ }
+
+ // Set time stamp
+ FStatHelper::GetModifiedDateTimeOfFile( sShareAutoCorrFile,
+ &aModifiedDate, &aModifiedTime );
+ aLastCheckTime = tools::Time( tools::Time::SYSTEM );
+ }
+
+}
+
+void SvxAutoCorrectLanguageLists::SaveExceptList_Imp(
+ const SvStringsISortDtor& rLst,
+ const OUString& sStrmName,
+ tools::SvRef<SotStorage> const &rStg,
+ bool bConvert )
+{
+ if( !rStg.is() )
+ return;
+
+ if( rLst.empty() )
+ {
+ rStg->Remove( sStrmName );
+ rStg->Commit();
+ }
+ else
+ {
+ tools::SvRef<SotStorageStream> xStrm = rStg->OpenSotStream( sStrmName,
+ ( StreamMode::READ | StreamMode::WRITE | StreamMode::SHARE_DENYWRITE ) );
+ if( xStrm.is() )
+ {
+ xStrm->SetSize( 0 );
+ xStrm->SetBufferSize( 8192 );
+ xStrm->SetProperty( "MediaType", Any(OUString( "text/xml" )) );
+
+
+ uno::Reference< uno::XComponentContext > xContext =
+ comphelper::getProcessComponentContext();
+
+ uno::Reference < xml::sax::XWriter > xWriter = xml::sax::Writer::create(xContext);
+ uno::Reference < io::XOutputStream> xOut = new utl::OOutputStreamWrapper( *xStrm );
+ xWriter->setOutputStream(xOut);
+
+ uno::Reference < xml::sax::XDocumentHandler > xHandler(xWriter, UNO_QUERY_THROW);
+ rtl::Reference< SvXMLExceptionListExport > xExp( new SvXMLExceptionListExport( xContext, rLst, sStrmName, xHandler ) );
+
+ xExp->exportDoc( XML_BLOCK_LIST );
+
+ xStrm->Commit();
+ if( xStrm->GetError() == ERRCODE_NONE )
+ {
+ xStrm.clear();
+ if (!bConvert)
+ {
+ rStg->Commit();
+ if( ERRCODE_NONE != rStg->GetError() )
+ {
+ rStg->Remove( sStrmName );
+ rStg->Commit();
+ }
+ }
+ }
+ }
+ }
+}
+
+SvxAutocorrWordList* SvxAutoCorrectLanguageLists::LoadAutocorrWordList()
+{
+ if( pAutocorr_List )
+ pAutocorr_List->DeleteAndDestroyAll();
+ else
+ pAutocorr_List.reset( new SvxAutocorrWordList() );
+
+ try
+ {
+ uno::Reference < embed::XStorage > xStg = comphelper::OStorageHelper::GetStorageFromURL( sShareAutoCorrFile, embed::ElementModes::READ );
+ uno::Reference < io::XStream > xStrm = xStg->openStreamElement( pXMLImplAutocorr_ListStr, embed::ElementModes::READ );
+ uno::Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext();
+
+ xml::sax::InputSource aParserInput;
+ aParserInput.sSystemId = pXMLImplAutocorr_ListStr;
+ aParserInput.aInputStream = xStrm->getInputStream();
+
+ // get parser
+ uno::Reference< xml::sax::XFastParser > xParser = xml::sax::FastParser::create(xContext);
+ SAL_INFO("editeng", "AutoCorrect Import" );
+ uno::Reference< xml::sax::XFastDocumentHandler > xFilter = new SvXMLAutoCorrectImport( xContext, pAutocorr_List.get(), rAutoCorrect, xStg );
+ uno::Reference<xml::sax::XFastTokenHandler> xTokenHandler = new SvXMLAutoCorrectTokenHandler;
+
+ // connect parser and filter
+ xParser->setFastDocumentHandler( xFilter );
+ xParser->registerNamespace( "http://openoffice.org/2001/block-list", SvXMLAutoCorrectToken::NAMESPACE );
+ xParser->setTokenHandler(xTokenHandler);
+
+ // parse
+ xParser->parseStream( aParserInput );
+ }
+ catch ( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("editeng", "when loading " << sShareAutoCorrFile);
+ }
+
+ // Set time stamp
+ FStatHelper::GetModifiedDateTimeOfFile( sShareAutoCorrFile,
+ &aModifiedDate, &aModifiedTime );
+ aLastCheckTime = tools::Time( tools::Time::SYSTEM );
+
+ return pAutocorr_List.get();
+}
+
+const SvxAutocorrWordList* SvxAutoCorrectLanguageLists::GetAutocorrWordList()
+{
+ if( !( ACFlags::ChgWordLstLoad & nFlags ) || IsFileChanged_Imp() )
+ {
+ LoadAutocorrWordList();
+ if( !pAutocorr_List )
+ {
+ OSL_ENSURE( false, "No valid list" );
+ pAutocorr_List.reset( new SvxAutocorrWordList() );
+ }
+ nFlags |= ACFlags::ChgWordLstLoad;
+ }
+ return pAutocorr_List.get();
+}
+
+SvStringsISortDtor* SvxAutoCorrectLanguageLists::GetCplSttExceptList()
+{
+ if( !( ACFlags::CplSttLstLoad & nFlags ) || IsFileChanged_Imp() )
+ {
+ LoadCplSttExceptList();
+ if( !pCplStt_ExcptLst )
+ {
+ OSL_ENSURE( false, "No valid list" );
+ pCplStt_ExcptLst.reset( new SvStringsISortDtor );
+ }
+ nFlags |= ACFlags::CplSttLstLoad;
+ }
+ return pCplStt_ExcptLst.get();
+}
+
+bool SvxAutoCorrectLanguageLists::AddToCplSttExceptList(const OUString& rNew)
+{
+ bool bRet = false;
+ if( !rNew.isEmpty() && GetCplSttExceptList()->insert( rNew ).second )
+ {
+ MakeUserStorage_Impl();
+ tools::SvRef<SotStorage> xStg = new SotStorage( sUserAutoCorrFile, StreamMode::READWRITE );
+
+ SaveExceptList_Imp( *pCplStt_ExcptLst, pXMLImplCplStt_ExcptLstStr, xStg );
+
+ xStg = nullptr;
+ // Set time stamp
+ FStatHelper::GetModifiedDateTimeOfFile( sUserAutoCorrFile,
+ &aModifiedDate, &aModifiedTime );
+ aLastCheckTime = tools::Time( tools::Time::SYSTEM );
+ bRet = true;
+ }
+ return bRet;
+}
+
+bool SvxAutoCorrectLanguageLists::AddToWordStartExceptList(const OUString& rNew)
+{
+ bool bRet = false;
+ if( !rNew.isEmpty() && GetWordStartExceptList()->insert( rNew ).second )
+ {
+ MakeUserStorage_Impl();
+ tools::SvRef<SotStorage> xStg = new SotStorage( sUserAutoCorrFile, StreamMode::READWRITE );
+
+ SaveExceptList_Imp( *pWordStart_ExcptLst, pXMLImplWordStart_ExcptLstStr, xStg );
+
+ xStg = nullptr;
+ // Set time stamp
+ FStatHelper::GetModifiedDateTimeOfFile( sUserAutoCorrFile,
+ &aModifiedDate, &aModifiedTime );
+ aLastCheckTime = tools::Time( tools::Time::SYSTEM );
+ bRet = true;
+ }
+ return bRet;
+}
+
+SvStringsISortDtor* SvxAutoCorrectLanguageLists::LoadCplSttExceptList()
+{
+ try
+ {
+ tools::SvRef<SotStorage> xStg = new SotStorage( sShareAutoCorrFile, StreamMode::READ | StreamMode::SHARE_DENYNONE );
+ if( xStg.is() && xStg->IsContained( pXMLImplCplStt_ExcptLstStr ) )
+ LoadXMLExceptList_Imp( pCplStt_ExcptLst, pXMLImplCplStt_ExcptLstStr, xStg );
+ }
+ catch (const css::ucb::ContentCreationException&)
+ {
+ }
+ return pCplStt_ExcptLst.get();
+}
+
+void SvxAutoCorrectLanguageLists::SaveCplSttExceptList()
+{
+ MakeUserStorage_Impl();
+ tools::SvRef<SotStorage> xStg = new SotStorage( sUserAutoCorrFile, StreamMode::READWRITE );
+
+ SaveExceptList_Imp( *pCplStt_ExcptLst, pXMLImplCplStt_ExcptLstStr, xStg );
+
+ xStg = nullptr;
+
+ // Set time stamp
+ FStatHelper::GetModifiedDateTimeOfFile( sUserAutoCorrFile,
+ &aModifiedDate, &aModifiedTime );
+ aLastCheckTime = tools::Time( tools::Time::SYSTEM );
+}
+
+SvStringsISortDtor* SvxAutoCorrectLanguageLists::LoadWordStartExceptList()
+{
+ try
+ {
+ tools::SvRef<SotStorage> xStg = new SotStorage( sShareAutoCorrFile, StreamMode::READ | StreamMode::SHARE_DENYNONE );
+ if( xStg.is() && xStg->IsContained( pXMLImplWordStart_ExcptLstStr ) )
+ LoadXMLExceptList_Imp( pWordStart_ExcptLst, pXMLImplWordStart_ExcptLstStr, xStg );
+ }
+ catch (const css::ucb::ContentCreationException &)
+ {
+ TOOLS_WARN_EXCEPTION("editeng", "SvxAutoCorrectLanguageLists::LoadWordStartExceptList");
+ }
+ return pWordStart_ExcptLst.get();
+}
+
+void SvxAutoCorrectLanguageLists::SaveWordStartExceptList()
+{
+ MakeUserStorage_Impl();
+ tools::SvRef<SotStorage> xStg = new SotStorage( sUserAutoCorrFile, StreamMode::READWRITE );
+
+ SaveExceptList_Imp( *pWordStart_ExcptLst, pXMLImplWordStart_ExcptLstStr, xStg );
+
+ xStg = nullptr;
+ // Set time stamp
+ FStatHelper::GetModifiedDateTimeOfFile( sUserAutoCorrFile,
+ &aModifiedDate, &aModifiedTime );
+ aLastCheckTime = tools::Time( tools::Time::SYSTEM );
+}
+
+SvStringsISortDtor* SvxAutoCorrectLanguageLists::GetWordStartExceptList()
+{
+ if( !( ACFlags::WordStartLstLoad & nFlags ) || IsFileChanged_Imp() )
+ {
+ LoadWordStartExceptList();
+ if( !pWordStart_ExcptLst )
+ {
+ OSL_ENSURE( false, "No valid list" );
+ pWordStart_ExcptLst.reset( new SvStringsISortDtor );
+ }
+ nFlags |= ACFlags::WordStartLstLoad;
+ }
+ return pWordStart_ExcptLst.get();
+}
+
+void SvxAutoCorrectLanguageLists::RemoveStream_Imp( const OUString& rName )
+{
+ if( sShareAutoCorrFile != sUserAutoCorrFile )
+ {
+ tools::SvRef<SotStorage> xStg = new SotStorage( sUserAutoCorrFile, StreamMode::READWRITE );
+ if( xStg.is() && ERRCODE_NONE == xStg->GetError() &&
+ xStg->IsStream( rName ) )
+ {
+ xStg->Remove( rName );
+ xStg->Commit();
+
+ xStg = nullptr;
+ }
+ }
+}
+
+void SvxAutoCorrectLanguageLists::MakeUserStorage_Impl()
+{
+ // The conversion needs to happen if the file is already in the user
+ // directory and is in the old format. Additionally it needs to
+ // happen when the file is being copied from share to user.
+
+ bool bError = false, bConvert = false, bCopy = false;
+ INetURLObject aDest;
+ INetURLObject aSource;
+
+ if (sUserAutoCorrFile != sShareAutoCorrFile )
+ {
+ aSource = INetURLObject ( sShareAutoCorrFile );
+ aDest = INetURLObject ( sUserAutoCorrFile );
+ if ( SotStorage::IsOLEStorage ( sShareAutoCorrFile ) )
+ {
+ aDest.SetExtension ( u"bak" );
+ bConvert = true;
+ }
+ bCopy = true;
+ }
+ else if ( SotStorage::IsOLEStorage ( sUserAutoCorrFile ) )
+ {
+ aSource = INetURLObject ( sUserAutoCorrFile );
+ aDest = INetURLObject ( sUserAutoCorrFile );
+ aDest.SetExtension ( u"bak" );
+ bCopy = bConvert = true;
+ }
+ if (bCopy)
+ {
+ try
+ {
+ OUString sMain(aDest.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ));
+ sal_Int32 nSlashPos = sMain.lastIndexOf('/');
+ sMain = sMain.copy(0, nSlashPos);
+ ::ucbhelper::Content aNewContent( sMain, uno::Reference< XCommandEnvironment >(), comphelper::getProcessComponentContext() );
+ TransferInfo aInfo;
+ aInfo.NameClash = NameClash::OVERWRITE;
+ aInfo.NewTitle = aDest.GetLastName();
+ aInfo.SourceURL = aSource.GetMainURL( INetURLObject::DecodeMechanism::ToIUri );
+ aInfo.MoveData = false;
+ aNewContent.executeCommand( "transfer", Any(aInfo));
+ }
+ catch (...)
+ {
+ bError = true;
+ }
+ }
+ if (bConvert && !bError)
+ {
+ tools::SvRef<SotStorage> xSrcStg = new SotStorage( aDest.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ), StreamMode::READ );
+ tools::SvRef<SotStorage> xDstStg = new SotStorage( sUserAutoCorrFile, StreamMode::WRITE );
+
+ if( xSrcStg.is() && xDstStg.is() )
+ {
+ std::unique_ptr<SvStringsISortDtor> pTmpWordList;
+
+ if (xSrcStg->IsContained( pXMLImplWordStart_ExcptLstStr ) )
+ LoadXMLExceptList_Imp( pTmpWordList, pXMLImplWordStart_ExcptLstStr, xSrcStg );
+
+ if (pTmpWordList)
+ {
+ SaveExceptList_Imp( *pTmpWordList, pXMLImplWordStart_ExcptLstStr, xDstStg, true );
+ pTmpWordList.reset();
+ }
+
+
+ if (xSrcStg->IsContained( pXMLImplCplStt_ExcptLstStr ) )
+ LoadXMLExceptList_Imp( pTmpWordList, pXMLImplCplStt_ExcptLstStr, xSrcStg );
+
+ if (pTmpWordList)
+ {
+ SaveExceptList_Imp( *pTmpWordList, pXMLImplCplStt_ExcptLstStr, xDstStg, true );
+ pTmpWordList->clear();
+ }
+
+ GetAutocorrWordList();
+ MakeBlocklist_Imp( *xDstStg );
+ sShareAutoCorrFile = sUserAutoCorrFile;
+ xDstStg = nullptr;
+ try
+ {
+ ::ucbhelper::Content aContent ( aDest.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ), uno::Reference < XCommandEnvironment >(), comphelper::getProcessComponentContext() );
+ aContent.executeCommand ( "delete", Any ( true ) );
+ }
+ catch (...)
+ {
+ }
+ }
+ }
+ else if( bCopy && !bError )
+ sShareAutoCorrFile = sUserAutoCorrFile;
+}
+
+bool SvxAutoCorrectLanguageLists::MakeBlocklist_Imp( SotStorage& rStg )
+{
+ bool bRet = true, bRemove = !pAutocorr_List || pAutocorr_List->empty();
+ if( !bRemove )
+ {
+ tools::SvRef<SotStorageStream> refList = rStg.OpenSotStream( pXMLImplAutocorr_ListStr,
+ ( StreamMode::READ | StreamMode::WRITE | StreamMode::SHARE_DENYWRITE ) );
+ if( refList.is() )
+ {
+ refList->SetSize( 0 );
+ refList->SetBufferSize( 8192 );
+ refList->SetProperty( "MediaType", Any(OUString( "text/xml" )) );
+
+ uno::Reference< uno::XComponentContext > xContext =
+ comphelper::getProcessComponentContext();
+
+ uno::Reference < xml::sax::XWriter > xWriter = xml::sax::Writer::create(xContext);
+ uno::Reference < io::XOutputStream> xOut = new utl::OOutputStreamWrapper( *refList );
+ xWriter->setOutputStream(xOut);
+
+ rtl::Reference< SvXMLAutoCorrectExport > xExp( new SvXMLAutoCorrectExport( xContext, pAutocorr_List.get(), pXMLImplAutocorr_ListStr, xWriter ) );
+
+ xExp->exportDoc( XML_BLOCK_LIST );
+
+ refList->Commit();
+ bRet = ERRCODE_NONE == refList->GetError();
+ if( bRet )
+ {
+ refList.clear();
+ rStg.Commit();
+ if( ERRCODE_NONE != rStg.GetError() )
+ {
+ bRemove = true;
+ bRet = false;
+ }
+ }
+ }
+ else
+ bRet = false;
+ }
+
+ if( bRemove )
+ {
+ rStg.Remove( pXMLImplAutocorr_ListStr );
+ rStg.Commit();
+ }
+
+ return bRet;
+}
+
+bool SvxAutoCorrectLanguageLists::MakeCombinedChanges( std::vector<SvxAutocorrWord>& aNewEntries, std::vector<SvxAutocorrWord>& aDeleteEntries )
+{
+ // First get the current list!
+ GetAutocorrWordList();
+
+ MakeUserStorage_Impl();
+ tools::SvRef<SotStorage> xStorage = new SotStorage( sUserAutoCorrFile, StreamMode::READWRITE );
+
+ bool bRet = xStorage.is() && ERRCODE_NONE == xStorage->GetError();
+
+ if( bRet )
+ {
+ for (SvxAutocorrWord & aWordToDelete : aDeleteEntries)
+ {
+ std::optional<SvxAutocorrWord> xFoundEntry = pAutocorr_List->FindAndRemove( &aWordToDelete );
+ if( xFoundEntry )
+ {
+ if( !xFoundEntry->IsTextOnly() )
+ {
+ OUString aName( aWordToDelete.GetShort() );
+ if (xStorage->IsOLEStorage())
+ aName = EncryptBlockName_Imp(aName);
+ else
+ GeneratePackageName ( aWordToDelete.GetShort(), aName );
+
+ if( xStorage->IsContained( aName ) )
+ {
+ xStorage->Remove( aName );
+ bRet = xStorage->Commit();
+ }
+ }
+ }
+ }
+
+ for (const SvxAutocorrWord & aNewEntrie : aNewEntries)
+ {
+ SvxAutocorrWord aWordToAdd(aNewEntrie.GetShort(), aNewEntrie.GetLong(), true );
+ std::optional<SvxAutocorrWord> xRemoved = pAutocorr_List->FindAndRemove( &aWordToAdd );
+ if( xRemoved )
+ {
+ if( !xRemoved->IsTextOnly() )
+ {
+ // Still have to remove the Storage
+ OUString sStorageName( aWordToAdd.GetShort() );
+ if (xStorage->IsOLEStorage())
+ sStorageName = EncryptBlockName_Imp(sStorageName);
+ else
+ GeneratePackageName ( aWordToAdd.GetShort(), sStorageName);
+
+ if( xStorage->IsContained( sStorageName ) )
+ xStorage->Remove( sStorageName );
+ }
+ }
+ bRet = pAutocorr_List->Insert( std::move(aWordToAdd) );
+
+ if ( !bRet )
+ {
+ break;
+ }
+ }
+
+ if ( bRet )
+ {
+ bRet = MakeBlocklist_Imp( *xStorage );
+ }
+ }
+ return bRet;
+}
+
+bool SvxAutoCorrectLanguageLists::PutText( const OUString& rShort, const OUString& rLong )
+{
+ // First get the current list!
+ GetAutocorrWordList();
+
+ MakeUserStorage_Impl();
+ tools::SvRef<SotStorage> xStg = new SotStorage( sUserAutoCorrFile, StreamMode::READWRITE );
+
+ bool bRet = xStg.is() && ERRCODE_NONE == xStg->GetError();
+
+ // Update the word list
+ if( bRet )
+ {
+ SvxAutocorrWord aNew(rShort, rLong, true );
+ std::optional<SvxAutocorrWord> xRemove = pAutocorr_List->FindAndRemove( &aNew );
+ if( xRemove )
+ {
+ if( !xRemove->IsTextOnly() )
+ {
+ // Still have to remove the Storage
+ OUString sStgNm( rShort );
+ if (xStg->IsOLEStorage())
+ sStgNm = EncryptBlockName_Imp(sStgNm);
+ else
+ GeneratePackageName ( rShort, sStgNm);
+
+ if( xStg->IsContained( sStgNm ) )
+ xStg->Remove( sStgNm );
+ }
+ }
+
+ if( pAutocorr_List->Insert( std::move(aNew) ) )
+ {
+ bRet = MakeBlocklist_Imp( *xStg );
+ xStg = nullptr;
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ return bRet;
+}
+
+void SvxAutoCorrectLanguageLists::PutText( const OUString& rShort,
+ SfxObjectShell& rShell )
+{
+ // First get the current list!
+ GetAutocorrWordList();
+
+ MakeUserStorage_Impl();
+
+ try
+ {
+ uno::Reference < embed::XStorage > xStg = comphelper::OStorageHelper::GetStorageFromURL( sUserAutoCorrFile, embed::ElementModes::READWRITE );
+ OUString sLong;
+ bool bRet = rAutoCorrect.PutText( xStg, sUserAutoCorrFile, rShort, rShell, sLong );
+ xStg = nullptr;
+
+ // Update the word list
+ if( bRet )
+ {
+ if( pAutocorr_List->Insert( SvxAutocorrWord(rShort, sLong, false) ) )
+ {
+ tools::SvRef<SotStorage> xStor = new SotStorage( sUserAutoCorrFile, StreamMode::READWRITE );
+ MakeBlocklist_Imp( *xStor );
+ }
+ }
+ }
+ catch ( const uno::Exception& )
+ {
+ }
+}
+
+// Keep the list sorted ...
+struct SvxAutocorrWordList::CompareSvxAutocorrWordList
+{
+ bool operator()( SvxAutocorrWord const & lhs, SvxAutocorrWord const & rhs ) const
+ {
+ CollatorWrapper& rCmp = ::GetCollatorWrapper();
+ return rCmp.compareString( lhs.GetShort(), rhs.GetShort() ) < 0;
+ }
+};
+
+namespace {
+
+typedef std::unordered_map<OUString, SvxAutocorrWord> AutocorrWordHashType;
+
+}
+
+struct SvxAutocorrWordList::Impl
+{
+
+ // only one of these contains the data
+ // maSortedVector is manually sorted so we can optimise data movement
+ mutable AutocorrWordSetType maSortedVector;
+ mutable AutocorrWordHashType maHash; // key is 'Short'
+
+ void DeleteAndDestroyAll()
+ {
+ maHash.clear();
+ maSortedVector.clear();
+ }
+};
+
+SvxAutocorrWordList::SvxAutocorrWordList() : mpImpl(new Impl) {}
+
+SvxAutocorrWordList::~SvxAutocorrWordList()
+{
+}
+
+void SvxAutocorrWordList::DeleteAndDestroyAll()
+{
+ mpImpl->DeleteAndDestroyAll();
+}
+
+// returns true if inserted
+const SvxAutocorrWord* SvxAutocorrWordList::Insert(SvxAutocorrWord aWord) const
+{
+ if ( mpImpl->maSortedVector.empty() ) // use the hash
+ {
+ OUString aShort = aWord.GetShort();
+ auto [it,inserted] = mpImpl->maHash.emplace( std::move(aShort), std::move(aWord) );
+ if (inserted)
+ return &(it->second);
+ return nullptr;
+ }
+ else
+ {
+ auto it = std::lower_bound(mpImpl->maSortedVector.begin(), mpImpl->maSortedVector.end(), aWord, CompareSvxAutocorrWordList());
+ CollatorWrapper& rCmp = ::GetCollatorWrapper();
+ if (it == mpImpl->maSortedVector.end() || rCmp.compareString( aWord.GetShort(), it->GetShort() ) != 0)
+ {
+ it = mpImpl->maSortedVector.insert(it, std::move(aWord));
+ return &*it;
+ }
+ return nullptr;
+ }
+}
+
+void SvxAutocorrWordList::LoadEntry(const OUString& sWrong, const OUString& sRight, bool bOnlyTxt)
+{
+ (void)Insert(SvxAutocorrWord( sWrong, sRight, bOnlyTxt ));
+}
+
+bool SvxAutocorrWordList::empty() const
+{
+ return mpImpl->maHash.empty() && mpImpl->maSortedVector.empty();
+}
+
+std::optional<SvxAutocorrWord> SvxAutocorrWordList::FindAndRemove(const SvxAutocorrWord *pWord)
+{
+
+ if ( mpImpl->maSortedVector.empty() ) // use the hash
+ {
+ AutocorrWordHashType::iterator it = mpImpl->maHash.find( pWord->GetShort() );
+ if( it != mpImpl->maHash.end() )
+ {
+ SvxAutocorrWord pMatch = std::move(it->second);
+ mpImpl->maHash.erase (it);
+ return pMatch;
+ }
+ }
+ else
+ {
+ auto it = std::lower_bound(mpImpl->maSortedVector.begin(), mpImpl->maSortedVector.end(), *pWord, CompareSvxAutocorrWordList());
+ if (it != mpImpl->maSortedVector.end() && !CompareSvxAutocorrWordList()(*pWord, *it))
+ {
+ SvxAutocorrWord pMatch = std::move(*it);
+ mpImpl->maSortedVector.erase (it);
+ return pMatch;
+ }
+ }
+ return std::optional<SvxAutocorrWord>();
+}
+
+// return the sorted contents - defer sorting until we have to.
+const SvxAutocorrWordList::AutocorrWordSetType& SvxAutocorrWordList::getSortedContent() const
+{
+ // convert from hash to set permanently
+ if ( mpImpl->maSortedVector.empty() )
+ {
+ std::vector<SvxAutocorrWord> tmp;
+ tmp.reserve(mpImpl->maHash.size());
+ for (auto & rPair : mpImpl->maHash)
+ tmp.emplace_back(std::move(rPair.second));
+ mpImpl->maHash.clear();
+ // sort twice - this gets the list into mostly-sorted order, which
+ // reduces the number of times we need to invoke the expensive ICU collate fn.
+ std::sort(tmp.begin(), tmp.end(),
+ [] ( SvxAutocorrWord const & lhs, SvxAutocorrWord const & rhs )
+ {
+ return lhs.GetShort() < rhs.GetShort();
+ });
+ // This beast has some O(N log(N)) in a terribly slow ICU collate fn.
+ // stable_sort is twice as fast as sort in this situation because it does
+ // fewer comparison operations.
+ std::stable_sort(tmp.begin(), tmp.end(), CompareSvxAutocorrWordList());
+ mpImpl->maSortedVector = std::move(tmp);
+ }
+ return mpImpl->maSortedVector;
+}
+
+const SvxAutocorrWord* SvxAutocorrWordList::WordMatches(const SvxAutocorrWord *pFnd,
+ std::u16string_view rTxt,
+ sal_Int32 &rStt,
+ sal_Int32 nEndPos) const
+{
+ const OUString& rChk = pFnd->GetShort();
+
+ sal_Int32 left_wildcard = rChk.startsWith( ".*" ) ? 2 : 0; // ".*word" pattern?
+ sal_Int32 right_wildcard = rChk.endsWith( ".*" ) ? 2 : 0; // "word.*" pattern?
+ assert(nEndPos >= 0);
+ size_t nSttWdPos = nEndPos;
+
+ // direct replacement of keywords surrounded by colons (for example, ":name:")
+ bool bColonNameColon = static_cast<sal_Int32>(rTxt.size()) > nEndPos &&
+ rTxt[nEndPos] == ':' && rChk[0] == ':' && rChk.endsWith(":");
+ if ( nEndPos + (bColonNameColon ? 1 : 0) < rChk.getLength() - left_wildcard - right_wildcard )
+ return nullptr;
+
+ bool bWasWordDelim = false;
+ sal_Int32 nCalcStt = nEndPos - rChk.getLength() + left_wildcard;
+ if (bColonNameColon)
+ nCalcStt++;
+ if( !right_wildcard && ( !nCalcStt || nCalcStt == rStt || left_wildcard || bColonNameColon ||
+ ( nCalcStt < rStt &&
+ IsWordDelim( rTxt[ nCalcStt - 1 ] ))) )
+ {
+ TransliterationWrapper& rCmp = GetIgnoreTranslWrapper();
+ OUString sWord( rTxt.substr(nCalcStt, rChk.getLength() - left_wildcard) );
+ if( (!left_wildcard && rCmp.isEqual( rChk, sWord )) || (left_wildcard && rCmp.isEqual( rChk.copy(left_wildcard), sWord) ))
+ {
+ rStt = nCalcStt;
+ if (!left_wildcard)
+ {
+ // fdo#33899 avoid "1/2", "1/3".. to be replaced by fractions in dates, eg. 1/2/14
+ if (static_cast<sal_Int32>(rTxt.size()) > nEndPos && rTxt[nEndPos] == '/' && rChk.indexOf('/') != -1)
+ return nullptr;
+ return pFnd;
+ }
+ // get the first word delimiter position before the matching ".*word" pattern
+ while( rStt && !(bWasWordDelim = IsWordDelim( rTxt[ --rStt ])))
+ ;
+ if (bWasWordDelim) rStt++;
+ OUString left_pattern( rTxt.substr(rStt, nEndPos - rStt - rChk.getLength() + left_wildcard) );
+ // avoid double spaces before simple "word" replacement
+ left_pattern += (left_pattern.getLength() == 0 && pFnd->GetLong()[0] == 0x20) ? pFnd->GetLong().subView(1) : pFnd->GetLong();
+ if( const SvxAutocorrWord* pNew = Insert( SvxAutocorrWord(OUString(rTxt.substr(rStt, nEndPos - rStt)), left_pattern) ) )
+ return pNew;
+ }
+ } else
+ // match "word.*" or ".*word.*" patterns, eg. "i18n.*", ".*---.*", TODO: add transliteration support
+ if ( right_wildcard )
+ {
+
+ OUString sTmp( rChk.copy( left_wildcard, rChk.getLength() - left_wildcard - right_wildcard ) );
+ // Get the last word delimiter position
+ bool not_suffix;
+
+ while( nSttWdPos && !(bWasWordDelim = IsWordDelim( rTxt[ --nSttWdPos ])))
+ ;
+ // search the first occurrence (with a left word delimitation, if needed)
+ size_t nFndPos = std::u16string_view::npos;
+ do {
+ nFndPos = rTxt.find( sTmp, nFndPos + 1);
+ if (nFndPos == std::u16string_view::npos)
+ break;
+ not_suffix = bWasWordDelim && (nSttWdPos >= (nFndPos + sTmp.getLength()));
+ } while ( (!left_wildcard && nFndPos && !IsWordDelim( rTxt[ nFndPos - 1 ])) || not_suffix );
+
+ if ( nFndPos != std::u16string_view::npos )
+ {
+ sal_Int32 extra_repl = static_cast<sal_Int32>(nFndPos) + sTmp.getLength() > nEndPos ? 1: 0; // for patterns with terminating characters, eg. "a:"
+
+ if ( left_wildcard )
+ {
+ // get the first word delimiter position before the matching ".*word.*" pattern
+ while( nFndPos && !(bWasWordDelim = IsWordDelim( rTxt[ --nFndPos ])))
+ ;
+ if (bWasWordDelim) nFndPos++;
+ }
+ if (nEndPos + extra_repl <= static_cast<sal_Int32>(nFndPos))
+ {
+ return nullptr;
+ }
+ // store matching pattern and its replacement as a new list item, eg. "i18ns" -> "internationalizations"
+ OUString aShort( rTxt.substr(nFndPos, nEndPos - nFndPos + extra_repl) );
+
+ OUString aLong;
+ rStt = nFndPos;
+ if ( !left_wildcard )
+ {
+ sal_Int32 siz = nEndPos - nFndPos - sTmp.getLength();
+ aLong = pFnd->GetLong() + (siz > 0 ? rTxt.substr(nFndPos + sTmp.getLength(), siz) : u"");
+ } else {
+ OUStringBuffer buf;
+ do {
+ nSttWdPos = rTxt.find( sTmp, nFndPos);
+ if (nSttWdPos != std::u16string_view::npos)
+ {
+ sal_Int32 nTmp(nFndPos);
+ while (nTmp < static_cast<sal_Int32>(nSttWdPos) && !IsWordDelim(rTxt[nTmp]))
+ nTmp++;
+ if (nTmp < static_cast<sal_Int32>(nSttWdPos))
+ break; // word delimiter found
+ buf.append(rTxt.substr(nFndPos, nSttWdPos - nFndPos)).append(pFnd->GetLong());
+ nFndPos = nSttWdPos + sTmp.getLength();
+ }
+ } while (nSttWdPos != std::u16string_view::npos);
+ if (static_cast<sal_Int32>(nEndPos - nFndPos) > extra_repl)
+ buf.append(rTxt.substr(nFndPos, nEndPos - nFndPos));
+ aLong = buf.makeStringAndClear();
+ }
+ if ( const SvxAutocorrWord* pNew = Insert( SvxAutocorrWord(aShort, aLong) ) )
+ {
+ if ( (static_cast<sal_Int32>(rTxt.size()) > nEndPos && IsWordDelim(rTxt[nEndPos])) || static_cast<sal_Int32>(rTxt.size()) == nEndPos )
+ return pNew;
+ }
+ }
+ }
+ return nullptr;
+}
+
+const SvxAutocorrWord* SvxAutocorrWordList::SearchWordsInList(std::u16string_view rTxt, sal_Int32& rStt,
+ sal_Int32 nEndPos) const
+{
+ for (auto const& elem : mpImpl->maHash)
+ {
+ if( const SvxAutocorrWord *pTmp = WordMatches( &elem.second, rTxt, rStt, nEndPos ) )
+ return pTmp;
+ }
+
+ for (auto const& elem : mpImpl->maSortedVector)
+ {
+ if( const SvxAutocorrWord *pTmp = WordMatches( &elem, rTxt, rStt, nEndPos ) )
+ return pTmp;
+ }
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/misc/swafopt.cxx b/editeng/source/misc/swafopt.cxx
new file mode 100644
index 0000000000..25f3b1b466
--- /dev/null
+++ b/editeng/source/misc/swafopt.cxx
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <editeng/swafopt.hxx>
+#include <tools/gen.hxx>
+#include <vcl/keycodes.hxx>
+
+SvxSwAutoFormatFlags::SvxSwAutoFormatFlags()
+ : aBulletFont( "OpenSymbol", Size( 0, 14 ) )
+{
+ bAutoCorrect =
+ bCapitalStartSentence =
+ bCapitalStartWord =
+ bChgEnumNum =
+ bAddNonBrkSpace =
+ bChgOrdinalNumber =
+ bTransliterateRTL =
+ bChgAngleQuotes =
+ bChgToEnEmDash =
+ bChgWeightUnderl =
+ bSetINetAttr =
+ bSetDOIAttr =
+ bAFormatDelSpacesAtSttEnd =
+ bAFormatDelSpacesBetweenLines =
+ bAFormatByInpDelSpacesAtSttEnd =
+ bAFormatByInpDelSpacesBetweenLines = true;
+
+ bChgUserColl =
+ bReplaceStyles =
+ bDelEmptyNode =
+ bWithRedlining =
+ bAutoCmpltEndless =
+ bSetNumRuleAfterSpace =
+ bAutoCmpltAppendBlank = false;
+
+ bAutoCmpltShowAsTip =
+ bSetBorder =
+ bCreateTable =
+ bSetNumRule =
+ bAFormatByInput =
+ bRightMargin =
+ bAutoCompleteWords =
+ bAutoCmpltCollectWords =
+ bAutoCmpltKeepList = true;
+
+ nRightMargin = 50; // default 50%
+ nAutoCmpltExpandKey = KEY_RETURN;
+
+ aBulletFont.SetCharSet( RTL_TEXTENCODING_SYMBOL );
+ aBulletFont.SetFamily( FAMILY_DONTKNOW );
+ aBulletFont.SetPitch( PITCH_DONTKNOW );
+ aBulletFont.SetWeight( WEIGHT_DONTKNOW );
+ aBulletFont.SetTransparent( true );
+
+ cBullet = 0x2022;
+ cByInputBullet = cBullet;
+ aByInputBulletFont = aBulletFont;
+
+ nAutoCmpltWordLen = 8;
+ nAutoCmpltListLen = 1000;
+ m_pAutoCompleteList = nullptr;
+ pSmartTagMgr = nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/misc/tokens.txt b/editeng/source/misc/tokens.txt
new file mode 100644
index 0000000000..0b5a64607b
--- /dev/null
+++ b/editeng/source/misc/tokens.txt
@@ -0,0 +1,7 @@
+abbreviated-name
+block
+block-list
+list-name
+name
+package-name
+unformatted-text
diff --git a/editeng/source/misc/txtrange.cxx b/editeng/source/misc/txtrange.cxx
new file mode 100644
index 0000000000..2f02a1150f
--- /dev/null
+++ b/editeng/source/misc/txtrange.cxx
@@ -0,0 +1,667 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <editeng/txtrange.hxx>
+#include <math.h>
+#include <tools/poly.hxx>
+#include <tools/debug.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+
+#include <vector>
+
+TextRanger::TextRanger( const basegfx::B2DPolyPolygon& rPolyPolygon,
+ const basegfx::B2DPolyPolygon* pLinePolyPolygon,
+ sal_uInt16 nCacheSz, sal_uInt16 nLft, sal_uInt16 nRght,
+ bool bSimpl, bool bInnr, bool bVert ) :
+ maPolyPolygon( rPolyPolygon.count() ),
+ nCacheSize( nCacheSz ),
+ nRight( nRght ),
+ nLeft( nLft ),
+ nUpper( 0 ),
+ nLower( 0 ),
+ nPointCount( 0 ),
+ bSimple( bSimpl ),
+ bInner( bInnr ),
+ bVertical( bVert )
+{
+ sal_uInt32 nCount(rPolyPolygon.count());
+
+ for(sal_uInt32 i(0); i < nCount; i++)
+ {
+ const basegfx::B2DPolygon aCandidate(rPolyPolygon.getB2DPolygon(i).getDefaultAdaptiveSubdivision());
+ nPointCount += aCandidate.count();
+ maPolyPolygon.Insert( tools::Polygon(aCandidate), static_cast<sal_uInt16>(i) );
+ }
+
+ if( pLinePolyPolygon )
+ {
+ nCount = pLinePolyPolygon->count();
+ mpLinePolyPolygon = tools::PolyPolygon(nCount);
+
+ for(sal_uInt32 i(0); i < nCount; i++)
+ {
+ const basegfx::B2DPolygon aCandidate(pLinePolyPolygon->getB2DPolygon(i).getDefaultAdaptiveSubdivision());
+ nPointCount += aCandidate.count();
+ mpLinePolyPolygon->Insert( tools::Polygon(aCandidate), static_cast<sal_uInt16>(i) );
+ }
+ }
+ else
+ mpLinePolyPolygon.reset();
+}
+
+
+TextRanger::~TextRanger()
+{
+ mRangeCache.clear();
+}
+
+/* TextRanger::SetVertical(..)
+ If there's is a change in the writing direction,
+ the cache has to be cleared.
+*/
+void TextRanger::SetVertical( bool bNew )
+{
+ if( IsVertical() != bNew )
+ {
+ bVertical = bNew;
+ mRangeCache.clear();
+ }
+}
+
+namespace {
+
+//! SvxBoundArgs is used to perform temporary calculations on a range array.
+//! Temporary instances are created in TextRanger::GetTextRanges()
+class SvxBoundArgs
+{
+ std::vector<bool> aBoolArr;
+ std::deque<tools::Long>* pLongArr;
+ TextRanger *pTextRanger;
+ tools::Long nMin;
+ tools::Long nMax;
+ tools::Long nTop;
+ tools::Long nBottom;
+ tools::Long nUpDiff;
+ tools::Long nLowDiff;
+ tools::Long nUpper;
+ tools::Long nLower;
+ tools::Long nStart;
+ tools::Long nEnd;
+ sal_uInt16 nCut;
+ sal_uInt16 nLast;
+ sal_uInt16 nNext;
+ sal_uInt8 nAct;
+ sal_uInt8 nFirst;
+ bool bClosed : 1;
+ bool bInner : 1;
+ bool bMultiple : 1;
+ bool bConcat : 1;
+ bool bRotate : 1;
+ void NoteRange( bool bToggle );
+ tools::Long Cut( tools::Long nY, const Point& rPt1, const Point& rPt2 );
+ void Add();
+ void NoteFarPoint_( tools::Long nPx, tools::Long nPyDiff, tools::Long nDiff );
+ void NoteFarPoint( tools::Long nPx, tools::Long nPyDiff, tools::Long nDiff )
+ { if( nDiff ) NoteFarPoint_( nPx, nPyDiff, nDiff ); }
+ tools::Long CalcMax( const Point& rPt1, const Point& rPt2, tools::Long nRange, tools::Long nFar );
+ void CheckCut( const Point& rLst, const Point& rNxt );
+ tools::Long A( const Point& rP ) const { return bRotate ? rP.Y() : rP.X(); }
+ tools::Long B( const Point& rP ) const { return bRotate ? rP.X() : rP.Y(); }
+public:
+ SvxBoundArgs( TextRanger* pRanger, std::deque<tools::Long>* pLong, const Range& rRange );
+ void NotePoint( const tools::Long nA ) { NoteMargin( nA - nStart, nA + nEnd ); }
+ void NoteMargin( const tools::Long nL, const tools::Long nR )
+ { if( nMin > nL ) nMin = nL; if( nMax < nR ) nMax = nR; }
+ sal_uInt16 Area( const Point& rPt );
+ void NoteUpLow( tools::Long nA, const sal_uInt8 nArea );
+ void Calc( const tools::PolyPolygon& rPoly );
+ void Concat( const tools::PolyPolygon* pPoly );
+ // inlines
+ void NoteLast() { if( bMultiple ) NoteRange( nAct == nFirst ); }
+ void SetConcat( const bool bNew ){ bConcat = bNew; }
+ bool IsConcat() const { return bConcat; }
+};
+
+}
+
+SvxBoundArgs::SvxBoundArgs( TextRanger* pRanger, std::deque<tools::Long>* pLong,
+ const Range& rRange )
+ : pLongArr(pLong)
+ , pTextRanger(pRanger)
+ , nMin(0)
+ , nMax(0)
+ , nTop(rRange.Min())
+ , nBottom(rRange.Max())
+ , nCut(0)
+ , nLast(0)
+ , nNext(0)
+ , nAct(0)
+ , nFirst(0)
+ , bClosed(false)
+ , bInner(pRanger->IsInner())
+ , bMultiple(bInner || !pRanger->IsSimple())
+ , bConcat(false)
+ , bRotate(pRanger->IsVertical())
+{
+ if( bRotate )
+ {
+ nStart = pRanger->GetUpper();
+ nEnd = pRanger->GetLower();
+ nLowDiff = pRanger->GetLeft();
+ nUpDiff = pRanger->GetRight();
+ }
+ else
+ {
+ nStart = pRanger->GetLeft();
+ nEnd = pRanger->GetRight();
+ nLowDiff = pRanger->GetUpper();
+ nUpDiff = pRanger->GetLower();
+ }
+ nUpper = nTop - nUpDiff;
+ nLower = nBottom + nLowDiff;
+ pLongArr->clear();
+}
+
+tools::Long SvxBoundArgs::CalcMax( const Point& rPt1, const Point& rPt2,
+ tools::Long nRange, tools::Long nFarRange )
+{
+ double nDa = Cut( nRange, rPt1, rPt2 ) - Cut( nFarRange, rPt1, rPt2 );
+ double nB;
+ if( nDa < 0 )
+ {
+ nDa = -nDa;
+ nB = nEnd;
+ }
+ else
+ nB = nStart;
+
+ nB = std::hypot(nB, nDa);
+
+ if (nB == 0) // avoid div / 0
+ return 0;
+
+ nB = nRange + nDa * ( nFarRange - nRange ) / nB;
+
+ bool bNote;
+ if( nB < B(rPt2) )
+ bNote = nB > B(rPt1);
+ else
+ bNote = nB < B(rPt1);
+ if( bNote )
+ return( tools::Long( nB ) );
+ return 0;
+}
+
+void SvxBoundArgs::CheckCut( const Point& rLst, const Point& rNxt )
+{
+ if( nCut & 1 )
+ NotePoint( Cut( nBottom, rLst, rNxt ) );
+ if( nCut & 2 )
+ NotePoint( Cut( nTop, rLst, rNxt ) );
+ if( rLst.X() == rNxt.X() || rLst.Y() == rNxt.Y() )
+ return;
+
+ tools::Long nYps;
+ if( nLowDiff && ( ( nCut & 1 ) || nLast == 1 || nNext == 1 ) )
+ {
+ nYps = CalcMax( rLst, rNxt, nBottom, nLower );
+ if( nYps )
+ NoteFarPoint_( Cut( nYps, rLst, rNxt ), nLower-nYps, nLowDiff );
+ }
+ if( nUpDiff && ( ( nCut & 2 ) || nLast == 2 || nNext == 2 ) )
+ {
+ nYps = CalcMax( rLst, rNxt, nTop, nUpper );
+ if( nYps )
+ NoteFarPoint_( Cut( nYps, rLst, rNxt ), nYps-nUpper, nUpDiff );
+ }
+}
+
+void SvxBoundArgs::NoteFarPoint_( tools::Long nPa, tools::Long nPbDiff, tools::Long nDiff )
+{
+ tools::Long nTmpA;
+ double nQuot = 2 * nDiff - nPbDiff;
+ nQuot *= nPbDiff;
+ nQuot = sqrt( nQuot );
+ nQuot /= nDiff;
+ nTmpA = nPa - tools::Long( nStart * nQuot );
+ nPbDiff = nPa + tools::Long( nEnd * nQuot );
+ NoteMargin( nTmpA, nPbDiff );
+}
+
+void SvxBoundArgs::NoteRange( bool bToggle )
+{
+ DBG_ASSERT( nMax >= nMin || bInner, "NoteRange: Min > Max?");
+ if( nMax < nMin )
+ return;
+ if( !bClosed )
+ bToggle = false;
+ sal_uInt16 nIdx = 0;
+ sal_uInt16 nCount = pLongArr->size();
+ DBG_ASSERT( nCount == 2 * aBoolArr.size(), "NoteRange: Incompatible Sizes" );
+ while( nIdx < nCount && (*pLongArr)[ nIdx ] < nMin )
+ ++nIdx;
+ bool bOdd = (nIdx % 2) != 0;
+ // No overlap with existing intervals?
+ if( nIdx == nCount || ( !bOdd && nMax < (*pLongArr)[ nIdx ] ) )
+ { // Then a new one is inserted ...
+ pLongArr->insert( pLongArr->begin() + nIdx, nMin );
+ pLongArr->insert( pLongArr->begin() + nIdx + 1, nMax );
+ aBoolArr.insert( aBoolArr.begin() + (nIdx/2), bToggle );
+ }
+ else
+ { // expand an existing interval ...
+ sal_uInt16 nMaxIdx = nIdx;
+ // If we end up on a left interval boundary, it must be reduced to nMin.
+ if( bOdd )
+ --nIdx;
+ else
+ (*pLongArr)[ nIdx ] = nMin;
+ while( nMaxIdx < nCount && (*pLongArr)[ nMaxIdx ] < nMax )
+ ++nMaxIdx;
+ DBG_ASSERT( nMaxIdx > nIdx || nMin == nMax, "NoteRange: Funny Situation." );
+ if( nMaxIdx )
+ --nMaxIdx;
+ if( nMaxIdx < nIdx )
+ nMaxIdx = nIdx;
+ // If we end up on a right interval boundary, it must be raised to nMax.
+ if( nMaxIdx % 2 )
+ (*pLongArr)[ nMaxIdx-- ] = nMax;
+ // Possible merge of intervals.
+ sal_uInt16 nDiff = nMaxIdx - nIdx;
+ nMaxIdx = nIdx / 2; // From here on is nMaxIdx the Index in BoolArray.
+ if( nDiff )
+ {
+ pLongArr->erase( pLongArr->begin() + nIdx + 1, pLongArr->begin() + nIdx + 1 + nDiff );
+ nDiff /= 2;
+ sal_uInt16 nStop = nMaxIdx + nDiff;
+ for( sal_uInt16 i = nMaxIdx; i < nStop; ++i )
+ bToggle ^= aBoolArr[ i ];
+ aBoolArr.erase( aBoolArr.begin() + nMaxIdx, aBoolArr.begin() + (nMaxIdx + nDiff) );
+ }
+ DBG_ASSERT( nMaxIdx < aBoolArr.size(), "NoteRange: Too much deleted" );
+ aBoolArr[ nMaxIdx ] = aBoolArr[ nMaxIdx ] != bToggle;
+ }
+}
+
+void SvxBoundArgs::Calc( const tools::PolyPolygon& rPoly )
+{
+ sal_uInt16 nCount;
+ nAct = 0;
+ for( sal_uInt16 i = 0; i < rPoly.Count(); ++i )
+ {
+ const tools::Polygon& rPol = rPoly[ i ];
+ nCount = rPol.GetSize();
+ if( nCount )
+ {
+ const Point& rNull = rPol[ 0 ];
+ bClosed = IsConcat() || ( rNull == rPol[ nCount - 1 ] );
+ nLast = Area( rNull );
+ if( nLast & 12 )
+ {
+ nFirst = 3;
+ if( bMultiple )
+ nAct = 0;
+ }
+ else
+ {
+ // The first point of the polygon is within the line.
+ if( nLast )
+ {
+ if( bMultiple || !nAct )
+ {
+ nMin = USHRT_MAX;
+ nMax = 0;
+ }
+ if( nLast & 1 )
+ NoteFarPoint( A(rNull), nLower - B(rNull), nLowDiff );
+ else
+ NoteFarPoint( A(rNull), B(rNull) - nUpper, nUpDiff );
+ }
+ else
+ {
+ if( bMultiple || !nAct )
+ {
+ nMin = A(rNull);
+ nMax = nMin + nEnd;
+ nMin -= nStart;
+ }
+ else
+ NotePoint( A(rNull) );
+ }
+ nFirst = 0; // leaving the line in which direction?
+ nAct = 3; // we are within the line at the moment.
+ }
+ if( nCount > 1 )
+ {
+ sal_uInt16 nIdx = 1;
+ while( true )
+ {
+ const Point& rLast = rPol[ nIdx - 1 ];
+ if( nIdx == nCount )
+ nIdx = 0;
+ const Point& rNext = rPol[ nIdx ];
+ nNext = Area( rNext );
+ nCut = nNext ^ nLast;
+ sal_uInt16 nOldAct = nAct;
+ if( nAct )
+ CheckCut( rLast, rNext );
+ if( nCut & 4 )
+ {
+ NoteUpLow( Cut( nLower, rLast, rNext ), 2 );
+ if( nAct && nAct != nOldAct )
+ {
+ nOldAct = nAct;
+ CheckCut( rLast, rNext );
+ }
+ }
+ if( nCut & 8 )
+ {
+ NoteUpLow( Cut( nUpper, rLast, rNext ), 1 );
+ if( nAct && nAct != nOldAct )
+ CheckCut( rLast, rNext );
+ }
+ if( !nIdx )
+ {
+ if( !( nNext & 12 ) )
+ NoteLast();
+ break;
+ }
+ if( !( nNext & 12 ) )
+ {
+ if( !nNext )
+ NotePoint( A(rNext) );
+ else if( nNext & 1 )
+ NoteFarPoint( A(rNext), nLower-B(rNext), nLowDiff );
+ else
+ NoteFarPoint( A(rNext), B(rNext)-nUpper, nUpDiff );
+ }
+ nLast = nNext;
+ if( ++nIdx == nCount && !bClosed )
+ {
+ if( !( nNext & 12 ) )
+ NoteLast();
+ break;
+ }
+ }
+ }
+ if( bMultiple && IsConcat() )
+ {
+ Add();
+ nAct = 0;
+ }
+ }
+ }
+ if( !bMultiple )
+ {
+ DBG_ASSERT( pLongArr->empty(), "I said: Simple!" );
+ if( nAct )
+ {
+ if( bInner )
+ {
+ tools::Long nTmpMin = nMin + 2 * nStart;
+ tools::Long nTmpMax = nMax - 2 * nEnd;
+ if( nTmpMin <= nTmpMax )
+ {
+ pLongArr->push_front(nTmpMax);
+ pLongArr->push_front(nTmpMin);
+ }
+ }
+ else
+ {
+ pLongArr->push_front(nMax);
+ pLongArr->push_front(nMin);
+ }
+ }
+ }
+ else if( !IsConcat() )
+ Add();
+}
+
+void SvxBoundArgs::Add()
+{
+ size_t nCount = aBoolArr.size();
+ if( nCount && ( !bInner || !pTextRanger->IsSimple() ) )
+ {
+ bool bDelete = aBoolArr.front();
+ if( bInner )
+ bDelete = !bDelete;
+ sal_uInt16 nLongIdx = 1;
+ for( size_t nBoolIdx = 1; nBoolIdx < nCount; ++nBoolIdx )
+ {
+ if( bDelete )
+ {
+ sal_uInt16 next = 2;
+ while( nBoolIdx < nCount && !aBoolArr[ nBoolIdx++ ] &&
+ (!bInner || nBoolIdx < nCount ) )
+ next += 2;
+ pLongArr->erase( pLongArr->begin() + nLongIdx, pLongArr->begin() + nLongIdx + next );
+ next /= 2;
+ nBoolIdx = nBoolIdx - next;
+ nCount = nCount - next;
+ aBoolArr.erase( aBoolArr.begin() + nBoolIdx, aBoolArr.begin() + (nBoolIdx + next) );
+ if( nBoolIdx )
+ aBoolArr[ nBoolIdx - 1 ] = false;
+#if OSL_DEBUG_LEVEL > 1
+ else
+ ++next;
+#endif
+ }
+ bDelete = nBoolIdx < nCount && aBoolArr[ nBoolIdx ];
+ nLongIdx += 2;
+ DBG_ASSERT( nLongIdx == 2*nBoolIdx+1, "BoundArgs: Array-Idx Confusion" );
+ DBG_ASSERT( aBoolArr.size()*2 == pLongArr->size(),
+ "BoundArgs: Array-Count: Confusion" );
+ }
+ }
+ if( pLongArr->empty() )
+ return;
+
+ if( !bInner )
+ return;
+
+ pLongArr->pop_front();
+ pLongArr->pop_back();
+
+ // Here the line is held inside a large rectangle for "simple"
+ // contour wrap. Currently (April 1999) the EditEngine evaluates
+ // only the first rectangle. If it one day is able to output a line
+ // in several parts, it may be advisable to delete the following lines.
+ if( pTextRanger->IsSimple() && pLongArr->size() > 2 )
+ pLongArr->erase( pLongArr->begin() + 1, pLongArr->end() - 1 );
+}
+
+void SvxBoundArgs::Concat( const tools::PolyPolygon* pPoly )
+{
+ SetConcat( true );
+ DBG_ASSERT( pPoly, "Nothing to do?" );
+ std::deque<tools::Long>* pOld = pLongArr;
+ pLongArr = new std::deque<tools::Long>;
+ aBoolArr.clear();
+ bInner = false;
+ Calc( *pPoly ); // Note that this updates pLongArr, which is why we swapped it out earlier.
+ std::deque<tools::Long>::size_type nCount = pLongArr->size();
+ std::deque<tools::Long>::size_type nIdx = 0;
+ std::deque<tools::Long>::size_type i = 0;
+ bool bSubtract = pTextRanger->IsInner();
+ while( i < nCount )
+ {
+ std::deque<tools::Long>::size_type nOldCount = pOld->size();
+ if( nIdx == nOldCount )
+ { // Reached the end of the old Array...
+ if( !bSubtract )
+ pOld->insert( pOld->begin() + nIdx, pLongArr->begin() + i, pLongArr->end() );
+ break;
+ }
+ tools::Long nLeft = (*pLongArr)[ i++ ];
+ tools::Long nRight = (*pLongArr)[ i++ ];
+ std::deque<tools::Long>::size_type nLeftPos = nIdx + 1;
+ while( nLeftPos < nOldCount && nLeft > (*pOld)[ nLeftPos ] )
+ nLeftPos += 2;
+ if( nLeftPos >= nOldCount )
+ { // The current interval belongs to the end of the old array ...
+ if( !bSubtract )
+ pOld->insert( pOld->begin() + nOldCount, pLongArr->begin() + i - 2, pLongArr->end() );
+ break;
+ }
+ std::deque<tools::Long>::size_type nRightPos = nLeftPos - 1;
+ while( nRightPos < nOldCount && nRight >= (*pOld)[ nRightPos ] )
+ nRightPos += 2;
+ if( nRightPos < nLeftPos )
+ { // The current interval belongs between two old intervals
+ if( !bSubtract )
+ pOld->insert( pOld->begin() + nRightPos, pLongArr->begin() + i - 2, pLongArr->begin() + i );
+ }
+ else if( bSubtract ) // Subtract, if necessary separate
+ {
+ const tools::Long nOld = (*pOld)[nLeftPos - 1];
+ if (nLeft > nOld)
+ { // Now we split the left part...
+ if( nLeft - 1 > nOld )
+ {
+ pOld->insert( pOld->begin() + nLeftPos - 1, nOld );
+ pOld->insert( pOld->begin() + nLeftPos, nLeft - 1 );
+ nLeftPos += 2;
+ nRightPos += 2;
+ }
+ }
+ if( nRightPos - nLeftPos > 1 )
+ pOld->erase( pOld->begin() + nLeftPos, pOld->begin() + nRightPos - 1 );
+ if (++nRight >= (*pOld)[nLeftPos])
+ pOld->erase( pOld->begin() + nLeftPos - 1, pOld->begin() + nLeftPos + 1 );
+ else
+ (*pOld)[ nLeftPos - 1 ] = nRight;
+ }
+ else // Merge
+ {
+ if( nLeft < (*pOld)[ nLeftPos - 1 ] )
+ (*pOld)[ nLeftPos - 1 ] = nLeft;
+ if( nRight > (*pOld)[ nRightPos - 1 ] )
+ (*pOld)[ nRightPos - 1 ] = nRight;
+ if( nRightPos - nLeftPos > 1 )
+ pOld->erase( pOld->begin() + nLeftPos, pOld->begin() + nRightPos - 1 );
+
+ }
+ nIdx = nLeftPos - 1;
+ }
+ delete pLongArr;
+}
+
+/*************************************************************************
+ * SvxBoundArgs::Area returns the area in which the point is located.
+ * 0 = within the line
+ * 1 = below, but within the upper edge
+ * 2 = above, but within the lower edge
+ * 5 = below the upper edge
+ *10 = above the lower edge
+ *************************************************************************/
+
+sal_uInt16 SvxBoundArgs::Area( const Point& rPt )
+{
+ tools::Long nB = B( rPt );
+ if( nB >= nBottom )
+ {
+ if( nB >= nLower )
+ return 5;
+ return 1;
+ }
+ if( nB <= nTop )
+ {
+ if( nB <= nUpper )
+ return 10;
+ return 2;
+ }
+ return 0;
+}
+
+/*************************************************************************
+ * lcl_Cut calculates the X-Coordinate of the distance (Pt1-Pt2) at the
+ * Y-Coordinate nY.
+ * It is assumed that the one of the points are located above and the other
+ * one below the Y-Coordinate.
+ *************************************************************************/
+
+tools::Long SvxBoundArgs::Cut( tools::Long nB, const Point& rPt1, const Point& rPt2 )
+{
+ if( pTextRanger->IsVertical() )
+ {
+ double nQuot = nB - rPt1.X();
+ nQuot /= ( rPt2.X() - rPt1.X() );
+ nQuot *= ( rPt2.Y() - rPt1.Y() );
+ return tools::Long( rPt1.Y() + nQuot );
+ }
+ double nQuot = nB - rPt1.Y();
+ nQuot /= ( rPt2.Y() - rPt1.Y() );
+ nQuot *= ( rPt2.X() - rPt1.X() );
+ return tools::Long( rPt1.X() + nQuot );
+}
+
+void SvxBoundArgs::NoteUpLow( tools::Long nA, const sal_uInt8 nArea )
+{
+ if( nAct )
+ {
+ NoteMargin( nA, nA );
+ if( bMultiple )
+ {
+ NoteRange( nArea != nAct );
+ nAct = 0;
+ }
+ if( !nFirst )
+ nFirst = nArea;
+ }
+ else
+ {
+ nAct = nArea;
+ nMin = nA;
+ nMax = nA;
+ }
+}
+
+std::deque<tools::Long>* TextRanger::GetTextRanges( const Range& rRange )
+{
+ DBG_ASSERT( rRange.Min() || rRange.Max(), "Zero-Range not allowed, Bye Bye" );
+ //Can we find the result we need in the cache?
+ for (auto & elem : mRangeCache)
+ {
+ if (elem.range == rRange)
+ return &(elem.results);
+ }
+ //Calculate a new result
+ RangeCacheItem rngCache(rRange);
+ SvxBoundArgs aArg( this, &(rngCache.results), rRange );
+ aArg.Calc( maPolyPolygon );
+ if( mpLinePolyPolygon )
+ aArg.Concat( &*mpLinePolyPolygon );
+ //Add new result to the cache
+ mRangeCache.push_back(std::move(rngCache));
+ if (mRangeCache.size() > nCacheSize)
+ mRangeCache.pop_front();
+ return &(mRangeCache.back().results);
+}
+
+const tools::Rectangle& TextRanger::GetBoundRect_() const
+{
+ DBG_ASSERT( !mxBound, "Don't call twice." );
+ mxBound = maPolyPolygon.GetBoundRect();
+ return *mxBound;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/misc/unolingu.cxx b/editeng/source/misc/unolingu.cxx
new file mode 100644
index 0000000000..1e7b69a25f
--- /dev/null
+++ b/editeng/source/misc/unolingu.cxx
@@ -0,0 +1,754 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <memory>
+#include <editeng/unolingu.hxx>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/lang/XEventListener.hpp>
+#include <com/sun/star/linguistic2/XHyphenatedWord.hpp>
+#include <com/sun/star/linguistic2/DictionaryList.hpp>
+#include <com/sun/star/linguistic2/LinguServiceManager.hpp>
+#include <com/sun/star/linguistic2/LinguProperties.hpp>
+#include <com/sun/star/linguistic2/XSpellChecker1.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <unotools/lingucfg.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <linguistic/misc.hxx>
+#include <editeng/eerdll.hxx>
+#include <editeng/editrids.hrc>
+#include <svtools/strings.hrc>
+#include <unotools/resmgr.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::comphelper;
+using namespace ::linguistic;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::linguistic2;
+
+static uno::Reference< XLinguServiceManager2 > GetLngSvcMgr_Impl()
+{
+ uno::Reference< XComponentContext > xContext = comphelper::getProcessComponentContext();
+ uno::Reference< XLinguServiceManager2 > xRes = LinguServiceManager::create(xContext);
+ return xRes;
+}
+
+namespace {
+
+//! Dummy implementation in order to avoid loading of lingu DLL
+//! when only the XSupportedLocales interface is used.
+//! The dummy accesses the real implementation (and thus loading the DLL)
+//! when "real" work needs to be done only.
+class ThesDummy_Impl :
+ public cppu::WeakImplHelper< XThesaurus >
+{
+ uno::Reference< XThesaurus > xThes; // the real one...
+ std::unique_ptr<Sequence< lang::Locale >> pLocaleSeq;
+
+ void GetCfgLocales();
+
+ void GetThes_Impl();
+
+public:
+ ThesDummy_Impl() {}
+
+ // XSupportedLocales
+ virtual css::uno::Sequence< css::lang::Locale > SAL_CALL
+ getLocales() override;
+ virtual sal_Bool SAL_CALL
+ hasLocale( const css::lang::Locale& rLocale ) override;
+
+ // XThesaurus
+ virtual css::uno::Sequence<
+ css::uno::Reference< css::linguistic2::XMeaning > > SAL_CALL
+ queryMeanings( const OUString& rTerm,
+ const css::lang::Locale& rLocale,
+ const css::uno::Sequence< css::beans::PropertyValue >& rProperties ) override;
+};
+
+}
+
+void ThesDummy_Impl::GetCfgLocales()
+{
+ if (pLocaleSeq)
+ return;
+
+ SvtLinguConfig aCfg;
+ Sequence < OUString > aNodeNames( aCfg.GetNodeNames( "ServiceManager/ThesaurusList" ) );
+ const OUString *pNodeNames = aNodeNames.getConstArray();
+ sal_Int32 nLen = aNodeNames.getLength();
+ pLocaleSeq.reset( new Sequence< lang::Locale >( nLen ) );
+ lang::Locale *pLocale = pLocaleSeq->getArray();
+ for (sal_Int32 i = 0; i < nLen; ++i)
+ {
+ pLocale[i] = LanguageTag::convertToLocaleWithFallback( pNodeNames[i] );
+ }
+}
+
+
+void ThesDummy_Impl::GetThes_Impl()
+{
+ if (!xThes.is())
+ {
+ uno::Reference< XLinguServiceManager2 > xLngSvcMgr( GetLngSvcMgr_Impl() );
+ xThes = xLngSvcMgr->getThesaurus();
+
+ if (xThes.is())
+ {
+ // no longer needed...
+ pLocaleSeq.reset();
+ }
+ }
+}
+
+
+uno::Sequence< lang::Locale > SAL_CALL
+ ThesDummy_Impl::getLocales()
+{
+ GetThes_Impl();
+ if (xThes.is())
+ return xThes->getLocales();
+ else if (!pLocaleSeq) // if not already loaded save startup time by avoiding loading them now
+ GetCfgLocales();
+ return *pLocaleSeq;
+}
+
+
+sal_Bool SAL_CALL
+ ThesDummy_Impl::hasLocale( const lang::Locale& rLocale )
+{
+ GetThes_Impl();
+ if (xThes.is())
+ return xThes->hasLocale( rLocale );
+ else if (!pLocaleSeq) // if not already loaded save startup time by avoiding loading them now
+ GetCfgLocales();
+ bool bFound = false;
+ sal_Int32 nLen = pLocaleSeq->getLength();
+ const lang::Locale *pLocale = pLocaleSeq->getConstArray();
+ const lang::Locale *pEnd = pLocale + nLen;
+ for ( ; pLocale < pEnd && !bFound; ++pLocale)
+ {
+ bFound = pLocale->Language == rLocale.Language &&
+ pLocale->Country == rLocale.Country &&
+ pLocale->Variant == rLocale.Variant;
+ }
+ return bFound;
+}
+
+
+uno::Sequence< uno::Reference< linguistic2::XMeaning > > SAL_CALL
+ ThesDummy_Impl::queryMeanings(
+ const OUString& rTerm,
+ const lang::Locale& rLocale,
+ const css::uno::Sequence< css::beans::PropertyValue >& rProperties )
+{
+ GetThes_Impl();
+ uno::Sequence< uno::Reference< linguistic2::XMeaning > > aRes;
+ OSL_ENSURE( xThes.is(), "Thesaurus missing" );
+ if (xThes.is())
+ aRes = xThes->queryMeanings( rTerm, rLocale, rProperties );
+ return aRes;
+}
+
+namespace {
+
+//! Dummy implementation in order to avoid loading of lingu DLL.
+//! The dummy accesses the real implementation (and thus loading the DLL)
+//! when it needs to be done only.
+class SpellDummy_Impl :
+ public cppu::WeakImplHelper< XSpellChecker1 >
+{
+ uno::Reference< XSpellChecker1 > xSpell; // the real one...
+
+ void GetSpell_Impl();
+
+public:
+
+ // XSupportedLanguages (for XSpellChecker1)
+ virtual css::uno::Sequence< sal_Int16 > SAL_CALL
+ getLanguages() override;
+ virtual sal_Bool SAL_CALL
+ hasLanguage( sal_Int16 nLanguage ) override;
+
+ // XSpellChecker1 (same as XSpellChecker but sal_Int16 for language)
+ virtual sal_Bool SAL_CALL
+ isValid( const OUString& rWord, sal_Int16 nLanguage,
+ const css::uno::Sequence< css::beans::PropertyValue >& rProperties ) override;
+ virtual css::uno::Reference< css::linguistic2::XSpellAlternatives > SAL_CALL
+ spell( const OUString& rWord, sal_Int16 nLanguage,
+ const css::uno::Sequence< css::beans::PropertyValue >& rProperties ) override;
+};
+
+}
+
+void SpellDummy_Impl::GetSpell_Impl()
+{
+ if (!xSpell.is())
+ {
+ uno::Reference< XLinguServiceManager2 > xLngSvcMgr( GetLngSvcMgr_Impl() );
+ xSpell.set( xLngSvcMgr->getSpellChecker(), UNO_QUERY );
+ }
+}
+
+
+uno::Sequence< sal_Int16 > SAL_CALL
+ SpellDummy_Impl::getLanguages()
+{
+ GetSpell_Impl();
+ if (xSpell.is())
+ return xSpell->getLanguages();
+ else
+ return uno::Sequence< sal_Int16 >();
+}
+
+
+sal_Bool SAL_CALL
+ SpellDummy_Impl::hasLanguage( sal_Int16 nLanguage )
+{
+ GetSpell_Impl();
+ bool bRes = false;
+ if (xSpell.is())
+ bRes = xSpell->hasLanguage( nLanguage );
+ return bRes;
+}
+
+
+sal_Bool SAL_CALL
+ SpellDummy_Impl::isValid( const OUString& rWord, sal_Int16 nLanguage,
+ const css::uno::Sequence< css::beans::PropertyValue >& rProperties )
+{
+ GetSpell_Impl();
+ bool bRes = true;
+ if (xSpell.is())
+ bRes = xSpell->isValid( rWord, nLanguage, rProperties );
+ return bRes;
+}
+
+
+uno::Reference< linguistic2::XSpellAlternatives > SAL_CALL
+ SpellDummy_Impl::spell( const OUString& rWord, sal_Int16 nLanguage,
+ const css::uno::Sequence< css::beans::PropertyValue >& rProperties )
+{
+ GetSpell_Impl();
+ uno::Reference< linguistic2::XSpellAlternatives > xRes;
+ if (xSpell.is())
+ xRes = xSpell->spell( rWord, nLanguage, rProperties );
+ return xRes;
+}
+
+namespace {
+
+//! Dummy implementation in order to avoid loading of lingu DLL.
+//! The dummy accesses the real implementation (and thus loading the DLL)
+//! when it needs to be done only.
+class HyphDummy_Impl :
+ public cppu::WeakImplHelper< XHyphenator >
+{
+ uno::Reference< XHyphenator > xHyph; // the real one...
+
+ void GetHyph_Impl();
+
+public:
+
+ // XSupportedLocales
+ virtual css::uno::Sequence<
+ css::lang::Locale > SAL_CALL
+ getLocales() override;
+ virtual sal_Bool SAL_CALL
+ hasLocale( const css::lang::Locale& rLocale ) override;
+
+ // XHyphenator
+ virtual css::uno::Reference<
+ css::linguistic2::XHyphenatedWord > SAL_CALL
+ hyphenate( const OUString& rWord,
+ const css::lang::Locale& rLocale,
+ sal_Int16 nMaxLeading,
+ const css::uno::Sequence< css::beans::PropertyValue >& rProperties ) override;
+ virtual css::uno::Reference<
+ css::linguistic2::XHyphenatedWord > SAL_CALL
+ queryAlternativeSpelling( const OUString& rWord,
+ const css::lang::Locale& rLocale,
+ sal_Int16 nIndex,
+ const css::uno::Sequence< css::beans::PropertyValue >& rProperties ) override;
+ virtual css::uno::Reference<
+ css::linguistic2::XPossibleHyphens > SAL_CALL
+ createPossibleHyphens(
+ const OUString& rWord,
+ const css::lang::Locale& rLocale,
+ const css::uno::Sequence< css::beans::PropertyValue >& rProperties ) override;
+};
+
+}
+
+void HyphDummy_Impl::GetHyph_Impl()
+{
+ if (!xHyph.is())
+ {
+ uno::Reference< XLinguServiceManager2 > xLngSvcMgr( GetLngSvcMgr_Impl() );
+ xHyph = xLngSvcMgr->getHyphenator();
+ }
+}
+
+
+uno::Sequence< lang::Locale > SAL_CALL
+ HyphDummy_Impl::getLocales()
+{
+ GetHyph_Impl();
+ if (xHyph.is())
+ return xHyph->getLocales();
+ else
+ return uno::Sequence< lang::Locale >();
+}
+
+
+sal_Bool SAL_CALL
+ HyphDummy_Impl::hasLocale( const lang::Locale& rLocale )
+{
+ GetHyph_Impl();
+ bool bRes = false;
+ if (xHyph.is())
+ bRes = xHyph->hasLocale( rLocale );
+ return bRes;
+}
+
+
+uno::Reference< linguistic2::XHyphenatedWord > SAL_CALL
+ HyphDummy_Impl::hyphenate(
+ const OUString& rWord,
+ const lang::Locale& rLocale,
+ sal_Int16 nMaxLeading,
+ const css::uno::Sequence< css::beans::PropertyValue >& rProperties )
+{
+ GetHyph_Impl();
+ uno::Reference< linguistic2::XHyphenatedWord > xRes;
+ if (xHyph.is())
+ xRes = xHyph->hyphenate( rWord, rLocale, nMaxLeading, rProperties );
+ return xRes;
+}
+
+
+uno::Reference< linguistic2::XHyphenatedWord > SAL_CALL
+ HyphDummy_Impl::queryAlternativeSpelling(
+ const OUString& rWord,
+ const lang::Locale& rLocale,
+ sal_Int16 nIndex,
+ const css::uno::Sequence< css::beans::PropertyValue >& rProperties )
+{
+ GetHyph_Impl();
+ uno::Reference< linguistic2::XHyphenatedWord > xRes;
+ if (xHyph.is())
+ xRes = xHyph->queryAlternativeSpelling( rWord, rLocale, nIndex, rProperties );
+ return xRes;
+}
+
+
+uno::Reference< linguistic2::XPossibleHyphens > SAL_CALL
+ HyphDummy_Impl::createPossibleHyphens(
+ const OUString& rWord,
+ const lang::Locale& rLocale,
+ const css::uno::Sequence< css::beans::PropertyValue >& rProperties )
+{
+ GetHyph_Impl();
+ uno::Reference< linguistic2::XPossibleHyphens > xRes;
+ if (xHyph.is())
+ xRes = xHyph->createPossibleHyphens( rWord, rLocale, rProperties );
+ return xRes;
+}
+
+class LinguMgrExitLstnr : public cppu::WeakImplHelper<XEventListener>
+{
+ uno::Reference< XDesktop2 > xDesktop;
+
+ static void AtExit();
+
+public:
+ LinguMgrExitLstnr();
+ virtual ~LinguMgrExitLstnr() override;
+
+ // lang::XEventListener
+ virtual void SAL_CALL disposing(const EventObject& rSource) override;
+};
+
+LinguMgrExitLstnr::LinguMgrExitLstnr()
+{
+ // add object to frame::Desktop EventListeners in order to properly call
+ // the AtExit function at application exit.
+
+ uno::Reference< XComponentContext > xContext = getProcessComponentContext();
+ xDesktop = Desktop::create( xContext );
+ xDesktop->addEventListener( this );
+}
+
+LinguMgrExitLstnr::~LinguMgrExitLstnr()
+{
+ if (xDesktop.is())
+ {
+ xDesktop->removeEventListener( this );
+ xDesktop = nullptr; //! release reference to desktop
+ }
+ OSL_ENSURE(!xDesktop.is(), "reference to desktop should be released");
+}
+
+void LinguMgrExitLstnr::disposing(const EventObject& rSource)
+{
+ if (xDesktop.is() && rSource.Source == xDesktop)
+ {
+ xDesktop->removeEventListener( this );
+ xDesktop = nullptr; //! release reference to desktop
+
+ AtExit();
+ }
+}
+
+void LinguMgrExitLstnr::AtExit()
+{
+ SolarMutexGuard g;
+
+ // release references
+ LinguMgr::xLngSvcMgr = nullptr;
+ LinguMgr::xSpell = nullptr;
+ LinguMgr::xHyph = nullptr;
+ LinguMgr::xThes = nullptr;
+ LinguMgr::xDicList = nullptr;
+ LinguMgr::xProp = nullptr;
+ LinguMgr::xIgnoreAll = nullptr;
+ LinguMgr::xChangeAll = nullptr;
+
+ LinguMgr::bExiting = true;
+
+ LinguMgr::pExitLstnr = nullptr;
+}
+
+
+rtl::Reference<LinguMgrExitLstnr> LinguMgr::pExitLstnr;
+bool LinguMgr::bExiting = false;
+uno::Reference< XLinguServiceManager2 > LinguMgr::xLngSvcMgr;
+uno::Reference< XSpellChecker1 > LinguMgr::xSpell;
+uno::Reference< XHyphenator > LinguMgr::xHyph;
+uno::Reference< XThesaurus > LinguMgr::xThes;
+uno::Reference< XSearchableDictionaryList > LinguMgr::xDicList;
+uno::Reference< XLinguProperties > LinguMgr::xProp;
+uno::Reference< XDictionary > LinguMgr::xIgnoreAll;
+uno::Reference< XDictionary > LinguMgr::xChangeAll;
+
+
+uno::Reference< XLinguServiceManager2 > LinguMgr::GetLngSvcMgr()
+{
+ if (bExiting)
+ return nullptr;
+
+ if (!pExitLstnr)
+ pExitLstnr = new LinguMgrExitLstnr;
+
+ if (!xLngSvcMgr.is())
+ xLngSvcMgr = GetLngSvcMgr_Impl();
+
+ return xLngSvcMgr;
+}
+
+
+uno::Reference< XSpellChecker1 > LinguMgr::GetSpellChecker()
+{
+ return xSpell.is() ? xSpell : GetSpell();
+}
+
+uno::Reference< XHyphenator > LinguMgr::GetHyphenator()
+{
+ return xHyph.is() ? xHyph : GetHyph();
+}
+
+uno::Reference< XThesaurus > LinguMgr::GetThesaurus()
+{
+ return xThes.is() ? xThes : GetThes();
+}
+
+uno::Reference< XSearchableDictionaryList > LinguMgr::GetDictionaryList()
+{
+ return xDicList.is() ? xDicList : GetDicList();
+}
+
+uno::Reference< linguistic2::XLinguProperties > LinguMgr::GetLinguPropertySet()
+{
+ return xProp.is() ? xProp : GetProp();
+}
+
+uno::Reference< XDictionary > LinguMgr::GetStandardDic()
+{
+ //! don't hold reference to this
+ //! (it may be removed from dictionary list and needs to be
+ //! created empty if accessed again)
+ return GetStandard();
+}
+
+uno::Reference< XDictionary > LinguMgr::GetIgnoreAllList()
+{
+ return xIgnoreAll.is() ? xIgnoreAll : GetIgnoreAll();
+}
+
+uno::Reference< XDictionary > LinguMgr::GetChangeAllList()
+{
+ return xChangeAll.is() ? xChangeAll : GetChangeAll();
+}
+
+uno::Reference< XSpellChecker1 > LinguMgr::GetSpell()
+{
+ if (bExiting)
+ return nullptr;
+
+ if (!pExitLstnr)
+ pExitLstnr = new LinguMgrExitLstnr;
+
+ //! use dummy implementation in order to avoid loading of lingu DLL
+ xSpell = new SpellDummy_Impl;
+ return xSpell;
+}
+
+uno::Reference< XHyphenator > LinguMgr::GetHyph()
+{
+ if (bExiting)
+ return nullptr;
+
+ if (!pExitLstnr)
+ pExitLstnr = new LinguMgrExitLstnr;
+
+ //! use dummy implementation in order to avoid loading of lingu DLL
+ xHyph = new HyphDummy_Impl;
+ return xHyph;
+}
+
+uno::Reference< XThesaurus > LinguMgr::GetThes()
+{
+ if (bExiting)
+ return nullptr;
+
+ if (!pExitLstnr)
+ pExitLstnr = new LinguMgrExitLstnr;
+
+ //! use dummy implementation in order to avoid loading of lingu DLL
+ //! when only the XSupportedLocales interface is used.
+ //! The dummy accesses the real implementation (and thus loading the DLL)
+ //! when "real" work needs to be done only.
+ xThes = new ThesDummy_Impl;
+ return xThes;
+}
+
+uno::Reference< XSearchableDictionaryList > LinguMgr::GetDicList()
+{
+ if (bExiting)
+ return nullptr;
+
+ if (!pExitLstnr)
+ pExitLstnr = new LinguMgrExitLstnr;
+
+ xDicList = linguistic2::DictionaryList::create( getProcessComponentContext() );
+ return xDicList;
+}
+
+uno::Reference< linguistic2::XLinguProperties > LinguMgr::GetProp()
+{
+ if (bExiting)
+ return nullptr;
+
+ if (!pExitLstnr)
+ pExitLstnr = new LinguMgrExitLstnr;
+
+ xProp = linguistic2::LinguProperties::create( getProcessComponentContext() );
+ return xProp;
+}
+
+uno::Reference< XDictionary > LinguMgr::GetIgnoreAll()
+{
+ if (bExiting)
+ return nullptr;
+
+ if (!pExitLstnr)
+ pExitLstnr = new LinguMgrExitLstnr;
+
+ uno::Reference< XSearchableDictionaryList > xTmpDicList( GetDictionaryList() );
+ if (xTmpDicList.is())
+ {
+ std::locale loc(Translate::Create("svt"));
+ xIgnoreAll = xTmpDicList->getDictionaryByName(
+ Translate::get(STR_DESCRIPTION_IGNOREALLLIST, loc) );
+ }
+ return xIgnoreAll;
+}
+
+uno::Reference< XDictionary > LinguMgr::GetChangeAll()
+{
+ if (bExiting)
+ return nullptr;
+
+ if (!pExitLstnr)
+ pExitLstnr = new LinguMgrExitLstnr;
+
+ uno::Reference< XSearchableDictionaryList > _xDicList = GetDictionaryList();
+ if (_xDicList.is())
+ {
+ xChangeAll = _xDicList->createDictionary(
+ "ChangeAllList",
+ LanguageTag::convertToLocale( LANGUAGE_NONE ),
+ DictionaryType_NEGATIVE, OUString() );
+ }
+ return xChangeAll;
+}
+
+uno::Reference< XDictionary > LinguMgr::GetStandard()
+{
+ // Tries to return a dictionary which may hold positive entries is
+ // persistent and not read-only.
+
+ if (bExiting)
+ return nullptr;
+
+ uno::Reference< XSearchableDictionaryList > xTmpDicList( GetDictionaryList() );
+ if (!xTmpDicList.is())
+ return nullptr;
+
+ static constexpr OUString aDicName( u"standard.dic"_ustr );
+ uno::Reference< XDictionary > xDic = xTmpDicList->getDictionaryByName( aDicName );
+ if (!xDic.is())
+ {
+ // try to create standard dictionary
+ uno::Reference< XDictionary > xTmp;
+ try
+ {
+ xTmp = xTmpDicList->createDictionary( aDicName,
+ LanguageTag::convertToLocale( LANGUAGE_NONE ),
+ DictionaryType_POSITIVE,
+ linguistic::GetWritableDictionaryURL( aDicName ) );
+ }
+ catch(const css::uno::Exception &)
+ {
+ }
+
+ // add new dictionary to list
+ if (xTmp.is())
+ {
+ xTmpDicList->addDictionary( xTmp );
+ xTmp->setActive( true );
+ }
+ xDic = xTmp;
+ }
+#if OSL_DEBUG_LEVEL > 1
+ uno::Reference< XStorable > xStor( xDic, UNO_QUERY );
+ OSL_ENSURE( xDic.is() && xDic->getDictionaryType() == DictionaryType_POSITIVE,
+ "wrong dictionary type");
+ OSL_ENSURE( xDic.is() && LanguageTag( xDic->getLocale() ).getLanguageType() == LANGUAGE_NONE,
+ "wrong dictionary language");
+ OSL_ENSURE( !xStor.is() || (xStor->hasLocation() && !xStor->isReadonly()),
+ "dictionary not editable" );
+#endif
+
+ return xDic;
+}
+
+SvxAlternativeSpelling SvxGetAltSpelling(
+ const css::uno::Reference< css::linguistic2::XHyphenatedWord > & rHyphWord )
+{
+ SvxAlternativeSpelling aRes;
+ if (rHyphWord.is() && rHyphWord->isAlternativeSpelling())
+ {
+ OUString aWord( rHyphWord->getWord() ),
+ aAltWord( rHyphWord->getHyphenatedWord() );
+ sal_Int16 nHyphenationPos = rHyphWord->getHyphenationPos(),
+ nHyphenPos = rHyphWord->getHyphenPos();
+ sal_Int16 nLen = static_cast<sal_Int16>(aWord.getLength());
+ sal_Int16 nAltLen = static_cast<sal_Int16>(aAltWord.getLength());
+ const sal_Unicode *pWord = aWord.getStr(),
+ *pAltWord = aAltWord.getStr();
+
+ // count number of chars from the left to the
+ // hyphenation pos / hyphen pos that are equal
+ sal_Int16 nL = 0;
+ while (nL <= nHyphenationPos && nL <= nHyphenPos
+ && pWord[ nL ] == pAltWord[ nL ])
+ ++nL;
+ // count number of chars from the right to the
+ // hyphenation pos / hyphen pos that are equal
+ sal_Int16 nR = 0;
+ sal_Int32 nIdx = nLen - 1;
+ sal_Int32 nAltIdx = nAltLen - 1;
+ while (nIdx > nHyphenationPos && nAltIdx > nHyphenPos
+ && pWord[ nIdx-- ] == pAltWord[ nAltIdx-- ])
+ ++nR;
+
+ aRes.aReplacement = aAltWord.copy( nL, nAltLen - nL - nR );
+ aRes.nChangedPos = nL;
+ aRes.nChangedLength = nLen - nL - nR;
+ aRes.bIsAltSpelling = true;
+ }
+ return aRes;
+}
+
+
+SvxDicListChgClamp::SvxDicListChgClamp( uno::Reference< XSearchableDictionaryList > _xDicList ) :
+ xDicList (std::move( _xDicList ))
+{
+ if (xDicList.is())
+ {
+ xDicList->beginCollectEvents();
+ }
+}
+
+SvxDicListChgClamp::~SvxDicListChgClamp()
+{
+ if (xDicList.is())
+ {
+ xDicList->endCollectEvents();
+ }
+}
+
+short SvxDicError(weld::Window *pParent, linguistic::DictionaryError nError)
+{
+ short nRes = 0;
+ if (linguistic::DictionaryError::NONE != nError)
+ {
+ TranslateId pRid;
+ switch (nError)
+ {
+ case linguistic::DictionaryError::FULL : pRid = RID_SVXSTR_DIC_ERR_FULL; break;
+ case linguistic::DictionaryError::READONLY : pRid = RID_SVXSTR_DIC_ERR_READONLY; break;
+ default:
+ pRid = RID_SVXSTR_DIC_ERR_UNKNOWN;
+ SAL_WARN("editeng", "unexpected case");
+ }
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pParent,
+ VclMessageType::Info, VclButtonsType::Ok,
+ EditResId(pRid)));
+ nRes = xInfoBox->run();
+
+ }
+ return nRes;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/misc/urlfieldhelper.cxx b/editeng/source/misc/urlfieldhelper.cxx
new file mode 100644
index 0000000000..57f2a042c6
--- /dev/null
+++ b/editeng/source/misc/urlfieldhelper.cxx
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <editeng/urlfieldhelper.hxx>
+
+#include <editeng/flditem.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/editeng.hxx>
+
+void URLFieldHelper::RemoveURLField(EditView& pEditView)
+{
+ pEditView.SelectFieldAtCursor();
+ const SvxFieldItem* pFieldItem = pEditView.GetFieldAtSelection();
+ const SvxFieldData* pField = pFieldItem ? pFieldItem->GetField() : nullptr;
+ if (auto pUrlField = dynamic_cast<const SvxURLField*>(pField))
+ {
+ ESelection aSel = pEditView.GetSelection();
+ pEditView.GetEditEngine()->QuickInsertText(pUrlField->GetRepresentation(), aSel);
+ pEditView.Invalidate();
+ }
+}
+
+bool URLFieldHelper::IsCursorAtURLField(const EditView& pEditView, bool bAlsoCheckBeforeCursor)
+{
+ // tdf#128666 Make sure only URL field (or nothing) is selected
+ ESelection aSel = pEditView.GetSelection();
+ auto nSelectedParas = aSel.nEndPara - aSel.nStartPara;
+ auto nSelectedChars = aSel.nEndPos - aSel.nStartPos;
+ bool bIsValidSelection
+ = nSelectedParas == 0
+ && (nSelectedChars == 0 || nSelectedChars == 1 || nSelectedChars == -1);
+ if (!bIsValidSelection)
+ return false;
+
+ const SvxFieldData* pField
+ = pEditView.GetFieldUnderMouseOrInSelectionOrAtCursor(bAlsoCheckBeforeCursor);
+ if (dynamic_cast<const SvxURLField*>(pField))
+ return true;
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/editeng/source/outliner/outleeng.cxx b/editeng/source/outliner/outleeng.cxx
new file mode 100644
index 0000000000..1136ef37b9
--- /dev/null
+++ b/editeng/source/outliner/outleeng.cxx
@@ -0,0 +1,203 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <editeng/editeng.hxx>
+#include <editeng/eerdll.hxx>
+
+#include <editeng/outliner.hxx>
+#include <outleeng.hxx>
+#include "paralist.hxx"
+#include <editeng/editrids.hrc>
+#include <optional>
+#include <svl/itemset.hxx>
+#include <editeng/editstat.hxx>
+#include "outlundo.hxx"
+
+OutlinerEditEng::OutlinerEditEng( Outliner* pEngOwner, SfxItemPool* pPool )
+ : EditEngine( pPool )
+{
+ pOwner = pEngOwner;
+}
+
+OutlinerEditEng::~OutlinerEditEng()
+{
+}
+
+void OutlinerEditEng::PaintingFirstLine(sal_Int32 nPara, const Point& rStartPos, const Point& rOrigin, Degree10 nOrientation, OutputDevice& rOutDev)
+{
+ if( GetControlWord() & EEControlBits::OUTLINER )
+ {
+ PaintFirstLineInfo aInfo(nPara, rStartPos, &rOutDev);
+ pOwner->maPaintFirstLineHdl.Call( &aInfo );
+ }
+
+ pOwner->PaintBullet(nPara, rStartPos, rOrigin, nOrientation, rOutDev);
+}
+
+const SvxNumberFormat* OutlinerEditEng::GetNumberFormat( sal_Int32 nPara ) const
+{
+ const SvxNumberFormat* pFmt = nullptr;
+ if (pOwner)
+ pFmt = pOwner->GetNumberFormat( nPara );
+ return pFmt;
+}
+
+
+tools::Rectangle OutlinerEditEng::GetBulletArea( sal_Int32 nPara )
+{
+ tools::Rectangle aBulletArea { Point(), Point() };
+ if ( nPara < pOwner->pParaList->GetParagraphCount() )
+ {
+ if ( pOwner->ImplHasNumberFormat( nPara ) )
+ aBulletArea = pOwner->ImpCalcBulletArea( nPara, false, false );
+ }
+ return aBulletArea;
+}
+
+std::optional<bool> OutlinerEditEng::GetCompatFlag(SdrCompatibilityFlag eFlag) const
+{
+ if(pOwner)
+ {
+ return pOwner->GetCompatFlag(eFlag);
+ }
+ return {};
+}
+
+void OutlinerEditEng::ParagraphInserted( sal_Int32 nNewParagraph )
+{
+ pOwner->ParagraphInserted( nNewParagraph );
+
+ EditEngine::ParagraphInserted( nNewParagraph );
+}
+
+void OutlinerEditEng::ParagraphDeleted( sal_Int32 nDeletedParagraph )
+{
+ pOwner->ParagraphDeleted( nDeletedParagraph );
+
+ EditEngine::ParagraphDeleted( nDeletedParagraph );
+}
+
+void OutlinerEditEng::ParagraphConnected( sal_Int32 /*nLeftParagraph*/, sal_Int32 nRightParagraph )
+{
+ if( pOwner && pOwner->IsUndoEnabled() && !pOwner->GetEditEngine().IsInUndo() )
+ {
+ Paragraph* pPara = pOwner->GetParagraph( nRightParagraph );
+ if( pPara && Outliner::HasParaFlag( pPara, ParaFlag::ISPAGE ) )
+ {
+ pOwner->InsertUndo( std::make_unique<OutlinerUndoChangeParaFlags>( pOwner, nRightParagraph, ParaFlag::ISPAGE, ParaFlag::NONE ) );
+ }
+ }
+}
+
+
+void OutlinerEditEng::StyleSheetChanged( SfxStyleSheet* pStyle )
+{
+ pOwner->StyleSheetChanged( pStyle );
+}
+
+void OutlinerEditEng::ParaAttribsChanged( sal_Int32 nPara )
+{
+ pOwner->ParaAttribsChanged( nPara );
+}
+
+bool OutlinerEditEng::SpellNextDocument()
+{
+ return pOwner->SpellNextDocument();
+}
+
+bool OutlinerEditEng::ConvertNextDocument()
+{
+ return pOwner->ConvertNextDocument();
+}
+
+OUString OutlinerEditEng::GetUndoComment( sal_uInt16 nUndoId ) const
+{
+ switch( nUndoId )
+ {
+ case OLUNDO_DEPTH:
+ return EditResId(RID_OUTLUNDO_DEPTH);
+
+ case OLUNDO_EXPAND:
+ return EditResId(RID_OUTLUNDO_EXPAND);
+
+ case OLUNDO_COLLAPSE:
+ return EditResId(RID_OUTLUNDO_COLLAPSE);
+
+ case OLUNDO_ATTR:
+ return EditResId(RID_OUTLUNDO_ATTR);
+
+ case OLUNDO_INSERT:
+ return EditResId(RID_OUTLUNDO_INSERT);
+
+ default:
+ return EditEngine::GetUndoComment( nUndoId );
+ }
+}
+
+void OutlinerEditEng::DrawingText( const Point& rStartPos, const OUString& rText, sal_Int32 nTextStart, sal_Int32 nTextLen,
+ std::span<const sal_Int32> pDXArray, std::span<const sal_Bool> pKashidaArray,
+ const SvxFont& rFont, sal_Int32 nPara, sal_uInt8 nRightToLeft,
+ const EEngineData::WrongSpellVector* pWrongSpellVector,
+ const SvxFieldData* pFieldData,
+ bool bEndOfLine,
+ bool bEndOfParagraph,
+ const css::lang::Locale* pLocale,
+ const Color& rOverlineColor,
+ const Color& rTextLineColor)
+{
+ pOwner->DrawingText(rStartPos,rText,nTextStart,nTextLen,pDXArray,pKashidaArray,rFont,nPara,nRightToLeft,
+ pWrongSpellVector, pFieldData, bEndOfLine, bEndOfParagraph, false/*bEndOfBullet*/, pLocale, rOverlineColor, rTextLineColor);
+}
+
+void OutlinerEditEng::DrawingTab( const Point& rStartPos, tools::Long nWidth, const OUString& rChar,
+ const SvxFont& rFont, sal_Int32 nPara, sal_uInt8 nRightToLeft,
+ bool bEndOfLine, bool bEndOfParagraph,
+ const Color& rOverlineColor, const Color& rTextLineColor)
+{
+ pOwner->DrawingTab(rStartPos, nWidth, rChar, rFont, nPara, nRightToLeft,
+ bEndOfLine, bEndOfParagraph, rOverlineColor, rTextLineColor );
+}
+
+OUString OutlinerEditEng::CalcFieldValue( const SvxFieldItem& rField, sal_Int32 nPara, sal_Int32 nPos, std::optional<Color>& rpTxtColor, std::optional<Color>& rpFldColor, std::optional<FontLineStyle>& rpFldLineStyle )
+{
+ return pOwner->CalcFieldValue( rField, nPara, nPos, rpTxtColor, rpFldColor, rpFldLineStyle );
+}
+
+void OutlinerEditEng::SetParaAttribs( sal_Int32 nPara, const SfxItemSet& rSet )
+{
+ Paragraph* pPara = pOwner->pParaList->GetParagraph( nPara );
+ if( !pPara )
+ return;
+
+ if ( !IsInUndo() && IsUndoEnabled() )
+ pOwner->UndoActionStart( OLUNDO_ATTR );
+
+ EditEngine::SetParaAttribs( nPara, rSet );
+
+ pOwner->ImplCheckNumBulletItem( nPara );
+ // #i100014#
+ // It is not a good idea to subtract 1 from a count and cast the result
+ // to sal_uInt16 without check, if the count is 0.
+ pOwner->ImplCheckParagraphs( nPara, pOwner->pParaList->GetParagraphCount() );
+
+ if ( !IsInUndo() && IsUndoEnabled() )
+ pOwner->UndoActionEnd();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/outliner/outlin2.cxx b/editeng/source/outliner/outlin2.cxx
new file mode 100644
index 0000000000..e4d0386cf2
--- /dev/null
+++ b/editeng/source/outliner/outlin2.cxx
@@ -0,0 +1,600 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <editeng/editeng.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/editdata.hxx>
+#include <editeng/editund2.hxx>
+
+#include <svl/style.hxx>
+#include <vcl/mapmod.hxx>
+
+#include <editeng/forbiddencharacterstable.hxx>
+
+#include <editeng/outliner.hxx>
+#include "paralist.hxx"
+#include <outleeng.hxx>
+#include <editeng/editstat.hxx>
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::linguistic2;
+
+
+// ====================== Simple pass-through =======================
+
+
+bool Outliner::SetUpdateLayout( bool bUpdate )
+{
+ return pEditEngine->SetUpdateLayout( bUpdate );
+}
+
+
+bool Outliner::IsUpdateLayout() const
+{
+ return pEditEngine->IsUpdateLayout();
+}
+
+const SfxItemSet& Outliner::GetEmptyItemSet() const
+{
+ return pEditEngine->GetEmptyItemSet();
+}
+
+void Outliner::EnableUndo( bool bEnable )
+{
+ pEditEngine->EnableUndo( bEnable );
+}
+
+bool Outliner::IsUndoEnabled() const
+{
+ return pEditEngine->IsUndoEnabled();
+}
+
+MapMode const & Outliner::GetRefMapMode() const
+{
+ return pEditEngine->GetRefMapMode();
+}
+
+void Outliner::SetRefMapMode( const MapMode& rMMode )
+{
+ pEditEngine->SetRefMapMode( rMMode );
+}
+
+void Outliner::SetBackgroundColor( const Color& rColor )
+{
+ pEditEngine->SetBackgroundColor( rColor );
+}
+
+Color const & Outliner::GetBackgroundColor() const
+{
+ return pEditEngine->GetBackgroundColor();
+}
+
+
+void Outliner::ClearModifyFlag()
+{
+ pEditEngine->ClearModifyFlag();
+}
+
+bool Outliner::IsModified() const
+{
+ return pEditEngine->IsModified();
+}
+
+sal_uInt32 Outliner::GetTextHeight() const
+{
+ return pEditEngine->GetTextHeight();
+}
+
+void Outliner::SetModifyHdl( const Link<LinkParamNone*,void>& rLink )
+{
+ pEditEngine->SetModifyHdl( rLink );
+}
+
+void Outliner::SetNotifyHdl( const Link<EENotify&,void>& rLink )
+{
+ pEditEngine->aOutlinerNotifyHdl = rLink;
+
+ if ( rLink.IsSet() )
+ pEditEngine->SetNotifyHdl( LINK( this, Outliner, EditEngineNotifyHdl ) );
+ else
+ pEditEngine->SetNotifyHdl( Link<EENotify&,void>() );
+}
+
+void Outliner::SetStatusEventHdl( const Link<EditStatus&, void>& rLink )
+{
+ pEditEngine->SetStatusEventHdl( rLink );
+}
+
+Link<EditStatus&, void> const & Outliner::GetStatusEventHdl() const
+{
+ return pEditEngine->GetStatusEventHdl();
+}
+
+void Outliner::SetDefTab( sal_uInt16 nTab )
+{
+ pEditEngine->SetDefTab( nTab );
+}
+
+bool Outliner::IsFlatMode() const
+{
+ return pEditEngine->IsFlatMode();
+}
+
+bool Outliner::UpdateFields()
+{
+ return pEditEngine->UpdateFields();
+}
+
+void Outliner::RemoveFields( const std::function<bool ( const SvxFieldData* )>& isFieldData )
+{
+ pEditEngine->RemoveFields( isFieldData );
+}
+
+void Outliner::SetWordDelimiters( const OUString& rDelimiters )
+{
+ pEditEngine->SetWordDelimiters( rDelimiters );
+}
+
+OUString const & Outliner::GetWordDelimiters() const
+{
+ return pEditEngine->GetWordDelimiters();
+}
+
+OUString Outliner::GetWord( sal_Int32 nPara, sal_Int32 nIndex )
+{
+ return pEditEngine->GetWord( nPara, nIndex );
+}
+
+void Outliner::Draw( OutputDevice& rOutDev, const tools::Rectangle& rOutRect )
+{
+ pEditEngine->Draw( rOutDev, rOutRect );
+}
+
+void Outliner::Draw( OutputDevice& rOutDev, const Point& rStartPos )
+{
+ pEditEngine->Draw( rOutDev, rStartPos );
+}
+
+void Outliner::SetPaperSize( const Size& rSize )
+{
+ pEditEngine->SetPaperSize( rSize );
+}
+
+const Size& Outliner::GetPaperSize() const
+{
+ return pEditEngine->GetPaperSize();
+}
+
+void Outliner::SetPolygon( const basegfx::B2DPolyPolygon& rPolyPolygon )
+{
+ pEditEngine->SetPolygon( rPolyPolygon );
+}
+
+void Outliner::SetPolygon( const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::B2DPolyPolygon* pLinePolyPolygon)
+{
+ pEditEngine->SetPolygon( rPolyPolygon, pLinePolyPolygon);
+}
+
+void Outliner::ClearPolygon()
+{
+ pEditEngine->ClearPolygon();
+}
+
+const Size& Outliner::GetMinAutoPaperSize() const
+{
+ return pEditEngine->GetMinAutoPaperSize();
+}
+
+void Outliner::SetMinAutoPaperSize( const Size& rSz )
+{
+ pEditEngine->SetMinAutoPaperSize( rSz );
+}
+
+const Size& Outliner::GetMaxAutoPaperSize() const
+{
+ return pEditEngine->GetMaxAutoPaperSize();
+}
+
+void Outliner::SetMaxAutoPaperSize( const Size& rSz )
+{
+ pEditEngine->SetMaxAutoPaperSize( rSz );
+}
+
+void Outliner::SetMinColumnWrapHeight(tools::Long nVal)
+{
+ pEditEngine->SetMinColumnWrapHeight(nVal);
+}
+
+bool Outliner::IsExpanded( Paragraph const * pPara ) const
+{
+ return pParaList->HasVisibleChildren( pPara );
+}
+
+Paragraph* Outliner::GetParent( Paragraph const * pParagraph ) const
+{
+ return pParaList->GetParent( pParagraph );
+}
+
+sal_Int32 Outliner::GetChildCount( Paragraph const * pParent ) const
+{
+ return pParaList->GetChildCount( pParent );
+}
+
+Size Outliner::CalcTextSize()
+{
+ return Size(pEditEngine->CalcTextWidth(),pEditEngine->GetTextHeight());
+}
+
+Size Outliner::CalcTextSizeNTP()
+{
+ return Size(pEditEngine->CalcTextWidth(),pEditEngine->GetTextHeightNTP());
+}
+
+void Outliner::SetStyleSheetPool( SfxStyleSheetPool* pSPool )
+{
+ pEditEngine->SetStyleSheetPool( pSPool );
+}
+
+SfxStyleSheetPool* Outliner::GetStyleSheetPool()
+{
+ return pEditEngine->GetStyleSheetPool();
+}
+
+SfxStyleSheet* Outliner::GetStyleSheet( sal_Int32 nPara )
+{
+ return pEditEngine->GetStyleSheet( nPara );
+}
+
+bool Outliner::IsInSelectionMode() const
+{
+ return pEditEngine->IsInSelectionMode();
+}
+
+void Outliner::SetControlWord( EEControlBits nWord )
+{
+ pEditEngine->SetControlWord( nWord );
+}
+
+EEControlBits Outliner::GetControlWord() const
+{
+ return pEditEngine->GetControlWord();
+}
+
+void Outliner::SetAsianCompressionMode( CharCompressType n )
+{
+ pEditEngine->SetAsianCompressionMode( n );
+}
+
+void Outliner::SetKernAsianPunctuation( bool b )
+{
+ pEditEngine->SetKernAsianPunctuation( b );
+}
+
+void Outliner::SetAddExtLeading( bool bExtLeading )
+{
+ pEditEngine->SetAddExtLeading( bExtLeading );
+}
+
+void Outliner::UndoActionStart( sal_uInt16 nId )
+{
+ pEditEngine->UndoActionStart( nId );
+}
+
+void Outliner::UndoActionEnd()
+{
+ pEditEngine->UndoActionEnd();
+}
+
+void Outliner::InsertUndo( std::unique_ptr<EditUndo> pUndo )
+{
+ pEditEngine->GetUndoManager().AddUndoAction( std::move(pUndo) );
+}
+
+bool Outliner::IsInUndo() const
+{
+ return pEditEngine->IsInUndo();
+}
+
+sal_uInt32 Outliner::GetLineCount( sal_Int32 nParagraph ) const
+{
+ return pEditEngine->GetLineCount( nParagraph );
+}
+
+sal_Int32 Outliner::GetLineLen( sal_Int32 nParagraph, sal_Int32 nLine ) const
+{
+ return pEditEngine->GetLineLen( nParagraph, nLine );
+}
+
+sal_uInt32 Outliner::GetLineHeight( sal_Int32 nParagraph )
+{
+ return pEditEngine->GetLineHeight( nParagraph );
+}
+
+void Outliner::RemoveCharAttribs( sal_Int32 nPara, sal_uInt16 nWhich )
+{
+ pEditEngine->RemoveCharAttribs( nPara, nWhich );
+}
+
+EESpellState Outliner::HasSpellErrors()
+{
+ return pEditEngine->HasSpellErrors();
+}
+
+bool Outliner::HasConvertibleTextPortion( LanguageType nLang )
+{
+ return pEditEngine->HasConvertibleTextPortion( nLang );
+}
+
+bool Outliner::ConvertNextDocument()
+{
+ return false;
+}
+
+void Outliner::SetDefaultLanguage( LanguageType eLang )
+{
+ pEditEngine->SetDefaultLanguage( eLang );
+}
+
+void Outliner::CompleteOnlineSpelling()
+{
+ pEditEngine->CompleteOnlineSpelling();
+}
+
+bool Outliner::HasText( const SvxSearchItem& rSearchItem )
+{
+ return pEditEngine->HasText( rSearchItem );
+}
+
+void Outliner::SetEditTextObjectPool( SfxItemPool* pPool )
+{
+ pEditEngine->SetEditTextObjectPool( pPool );
+}
+
+SfxItemPool* Outliner::GetEditTextObjectPool() const
+{
+ return pEditEngine->GetEditTextObjectPool();
+}
+
+bool Outliner::SpellNextDocument()
+{
+ return false;
+}
+
+
+void Outliner::SetSpeller( Reference< XSpellChecker1 > const &xSpeller )
+{
+ pEditEngine->SetSpeller( xSpeller );
+}
+
+Reference< XSpellChecker1 > const & Outliner::GetSpeller()
+{
+ return pEditEngine->GetSpeller();
+}
+
+void Outliner::SetForbiddenCharsTable(const std::shared_ptr<SvxForbiddenCharactersTable>& xForbiddenChars)
+{
+ EditEngine::SetForbiddenCharsTable(xForbiddenChars);
+}
+
+void Outliner::SetHyphenator( Reference< XHyphenator > const & xHyph )
+{
+ pEditEngine->SetHyphenator( xHyph );
+}
+
+OutputDevice* Outliner::GetRefDevice() const
+{
+ return pEditEngine->GetRefDevice();
+}
+
+tools::Rectangle Outliner::GetParaBounds( sal_Int32 nParagraph ) const
+{
+ return pEditEngine->GetParaBounds(nParagraph );
+}
+
+Point Outliner::GetDocPos( const Point& rPaperPos ) const
+{
+ return pEditEngine->GetDocPos( rPaperPos );
+}
+
+bool Outliner::IsTextPos( const Point& rPaperPos, sal_uInt16 nBorder )
+{
+ return IsTextPos( rPaperPos, nBorder, nullptr );
+}
+
+bool Outliner::IsTextPos( const Point& rPaperPos, sal_uInt16 nBorder, bool* pbBullet )
+{
+ if ( pbBullet)
+ *pbBullet = false;
+ bool bTextPos = pEditEngine->IsTextPos( rPaperPos, nBorder );
+ if ( !bTextPos )
+ {
+ Point aDocPos = GetDocPos( rPaperPos );
+ sal_Int32 nPara = pEditEngine->FindParagraph( aDocPos.Y() );
+ if ( ( nPara != EE_PARA_NOT_FOUND ) && ImplHasNumberFormat( nPara ) )
+ {
+ tools::Rectangle aBulArea = ImpCalcBulletArea( nPara, true, true );
+ if ( aBulArea.Contains( rPaperPos ) )
+ {
+ bTextPos = true;
+ if ( pbBullet)
+ *pbBullet = true;
+ }
+ }
+ }
+
+ return bTextPos;
+}
+
+void Outliner::QuickSetAttribs( const SfxItemSet& rSet, const ESelection& rSel )
+{
+ pEditEngine->QuickSetAttribs( rSet, rSel );
+}
+
+void Outliner::QuickInsertText( const OUString& rText, const ESelection& rSel )
+{
+ bFirstParaIsEmpty = false;
+ pEditEngine->QuickInsertText( rText, rSel );
+}
+
+void Outliner::QuickDelete( const ESelection& rSel )
+{
+ bFirstParaIsEmpty = false;
+ pEditEngine->QuickDelete( rSel );
+}
+
+void Outliner::QuickInsertField( const SvxFieldItem& rFld, const ESelection& rSel )
+{
+ bFirstParaIsEmpty = false;
+ pEditEngine->QuickInsertField( rFld, rSel );
+}
+
+void Outliner::QuickInsertLineBreak( const ESelection& rSel )
+{
+ bFirstParaIsEmpty = false;
+ pEditEngine->QuickInsertLineBreak( rSel );
+}
+
+void Outliner::QuickFormatDoc()
+{
+ pEditEngine->QuickFormatDoc();
+}
+
+void Outliner::setGlobalScale(double rFontX, double rFontY, double rSpacingX, double rSpacingY)
+{
+ // reset bullet size
+ sal_Int32 nParagraphs = pParaList->GetParagraphCount();
+ for ( sal_Int32 nPara = 0; nPara < nParagraphs; nPara++ )
+ {
+ Paragraph* pPara = pParaList->GetParagraph( nPara );
+ if ( pPara )
+ pPara->aBulSize.setWidth( -1 );
+ }
+
+ pEditEngine->setGlobalScale(rFontX, rFontY, rSpacingX, rSpacingY);
+}
+
+void Outliner::getGlobalScale(double& rFontX, double& rFontY, double& rSpacingX, double& rSpacingY) const
+{
+ pEditEngine->getGlobalFontScale(rFontX, rFontY);
+ pEditEngine->getGlobalSpacingScale(rSpacingX, rSpacingY);
+}
+
+void Outliner::setRoundFontSizeToPt(bool bRound) const
+{
+ pEditEngine->setRoundFontSizeToPt(bRound);
+}
+
+void Outliner::EraseVirtualDevice()
+{
+ pEditEngine->EraseVirtualDevice();
+}
+
+bool Outliner::ShouldCreateBigTextObject() const
+{
+ return pEditEngine->ShouldCreateBigTextObject();
+}
+
+const EditEngine& Outliner::GetEditEngine() const
+{
+ return *pEditEngine;
+}
+
+void Outliner::SetVertical(bool bVertical)
+{
+ pEditEngine->SetVertical(bVertical);
+}
+
+void Outliner::SetRotation(TextRotation nRotation)
+{
+ pEditEngine->SetRotation(nRotation);
+}
+
+bool Outliner::IsVertical() const
+{
+ return pEditEngine->IsEffectivelyVertical();
+}
+
+bool Outliner::IsTopToBottom() const
+{
+ return pEditEngine->IsTopToBottom();
+}
+
+void Outliner::SetTextColumns(sal_Int16 nColumns, sal_Int32 nSpacing)
+{
+ pEditEngine->SetTextColumns(nColumns, nSpacing);
+}
+
+void Outliner::SetFixedCellHeight( bool bUseFixedCellHeight )
+{
+ pEditEngine->SetFixedCellHeight( bUseFixedCellHeight );
+}
+
+void Outliner::SetDefaultHorizontalTextDirection( EEHorizontalTextDirection eHTextDir )
+{
+ pEditEngine->SetDefaultHorizontalTextDirection( eHTextDir );
+}
+
+EEHorizontalTextDirection Outliner::GetDefaultHorizontalTextDirection() const
+{
+ return pEditEngine->GetDefaultHorizontalTextDirection();
+}
+
+LanguageType Outliner::GetLanguage( sal_Int32 nPara, sal_Int32 nPos ) const
+{
+ return pEditEngine->GetLanguage( nPara, nPos ).nLang;
+}
+
+void Outliner::RemoveAttribs( const ESelection& rSelection, bool bRemoveParaAttribs, sal_uInt16 nWhich )
+{
+ pEditEngine->RemoveAttribs( rSelection, bRemoveParaAttribs, nWhich );
+}
+
+void Outliner::EnableAutoColor( bool b )
+{
+ pEditEngine->EnableAutoColor( b );
+}
+
+void Outliner::ForceAutoColor( bool b )
+{
+ pEditEngine->ForceAutoColor( b );
+}
+
+bool Outliner::IsForceAutoColor() const
+{
+ return pEditEngine->IsForceAutoColor();
+}
+
+bool Outliner::SpellSentence(EditView const & rEditView, svx::SpellPortions& rToFill )
+{
+ return pEditEngine->SpellSentence(rEditView, rToFill );
+}
+
+void Outliner::PutSpellingToSentenceStart( EditView const & rEditView )
+{
+ pEditEngine->PutSpellingToSentenceStart( rEditView );
+}
+
+void Outliner::ApplyChangedSentence(EditView const & rEditView, const svx::SpellPortions& rNewPortions, bool bRecheck )
+{
+ pEditEngine->ApplyChangedSentence( rEditView, rNewPortions, bRecheck );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/outliner/outliner.cxx b/editeng/source/outliner/outliner.cxx
new file mode 100644
index 0000000000..bb8a8ac419
--- /dev/null
+++ b/editeng/source/outliner/outliner.cxx
@@ -0,0 +1,2163 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/string.hxx>
+#include <svl/eitem.hxx>
+#include <svl/intitem.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/editdata.hxx>
+#include <editeng/lrspitem.hxx>
+
+#include <math.h>
+#include <svl/style.hxx>
+#include <editeng/outliner.hxx>
+#include "paralist.hxx"
+#include <editeng/outlobj.hxx>
+#include <outleeng.hxx>
+#include "outlundo.hxx"
+#include <editeng/eeitem.hxx>
+#include <editeng/editstat.hxx>
+#include <editeng/overflowingtxt.hxx>
+#include <editeng/editobj.hxx>
+#include <svl/itemset.hxx>
+#include <vcl/metric.hxx>
+#include <editeng/numitem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <vcl/GraphicObject.hxx>
+#include <editeng/svxfont.hxx>
+#include <editeng/brushitem.hxx>
+#include <svl/itempool.hxx>
+#include <libxml/xmlwriter.h>
+#include <sal/log.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <o3tl/temporary.hxx>
+#include <osl/diagnose.h>
+
+#include <memory>
+using std::advance;
+
+
+// Outliner
+
+
+void Outliner::ImplCheckDepth( sal_Int16& rnDepth ) const
+{
+ if( rnDepth < gnMinDepth )
+ rnDepth = gnMinDepth;
+ else if( rnDepth > nMaxDepth )
+ rnDepth = nMaxDepth;
+}
+
+Paragraph* Outliner::Insert(const OUString& rText, sal_Int32 nAbsPos, sal_Int16 nDepth)
+{
+ DBG_ASSERT(pParaList->GetParagraphCount(),"Insert:No Paras");
+
+ Paragraph* pPara;
+
+ ImplCheckDepth( nDepth );
+
+ sal_Int32 nParagraphCount = pParaList->GetParagraphCount();
+ if( nAbsPos > nParagraphCount )
+ nAbsPos = nParagraphCount;
+
+ if( bFirstParaIsEmpty )
+ {
+ pPara = pParaList->GetParagraph( 0 );
+ if( pPara->GetDepth() != nDepth )
+ {
+ nDepthChangedHdlPrevDepth = pPara->GetDepth();
+ ParaFlag nPrevFlags = pPara->nFlags;
+ pPara->SetDepth( nDepth );
+ DepthChangedHdl(pPara, nPrevFlags);
+ }
+ pPara->nFlags |= ParaFlag::HOLDDEPTH;
+ SetText( rText, pPara );
+ }
+ else
+ {
+ bool bUpdate = pEditEngine->SetUpdateLayout( false );
+ ImplBlockInsertionCallbacks( true );
+ pPara = new Paragraph( nDepth );
+ pParaList->Insert( std::unique_ptr<Paragraph>(pPara), nAbsPos );
+ pEditEngine->InsertParagraph( nAbsPos, OUString() );
+ DBG_ASSERT(pPara==pParaList->GetParagraph(nAbsPos),"Insert:Failed");
+ ImplInitDepth( nAbsPos, nDepth, false );
+ ParagraphInsertedHdl(pPara);
+ pPara->nFlags |= ParaFlag::HOLDDEPTH;
+ SetText( rText, pPara );
+ ImplBlockInsertionCallbacks( false );
+ pEditEngine->SetUpdateLayout( bUpdate );
+ }
+ bFirstParaIsEmpty = false;
+ DBG_ASSERT(pEditEngine->GetParagraphCount()==pParaList->GetParagraphCount(),"SetText failed");
+ return pPara;
+}
+
+
+void Outliner::ParagraphInserted( sal_Int32 nPara )
+{
+
+ if ( nBlockInsCallback )
+ return;
+
+ if( bPasting || pEditEngine->IsInUndo() )
+ {
+ Paragraph* pPara = new Paragraph( -1 );
+ pParaList->Insert( std::unique_ptr<Paragraph>(pPara), nPara );
+ if( pEditEngine->IsInUndo() )
+ {
+ pPara->bVisible = true;
+ const SfxInt16Item& rLevel = pEditEngine->GetParaAttrib( nPara, EE_PARA_OUTLLEVEL );
+ pPara->SetDepth( rLevel.GetValue() );
+ }
+ }
+ else
+ {
+ sal_Int16 nDepth = -1;
+ Paragraph* pParaBefore = pParaList->GetParagraph( nPara-1 );
+ if ( pParaBefore )
+ nDepth = pParaBefore->GetDepth();
+
+ Paragraph* pPara = new Paragraph( nDepth );
+ pParaList->Insert( std::unique_ptr<Paragraph>(pPara), nPara );
+
+ if( !pEditEngine->IsInUndo() )
+ {
+ ImplCalcBulletText( nPara, true, false );
+ ParagraphInsertedHdl(pPara);
+ }
+ }
+}
+
+void Outliner::ParagraphDeleted( sal_Int32 nPara )
+{
+
+ if ( nBlockInsCallback || ( nPara == EE_PARA_ALL ) )
+ return;
+
+ Paragraph* pPara = pParaList->GetParagraph( nPara );
+ if (!pPara)
+ return;
+
+ sal_Int16 nDepth = pPara->GetDepth();
+
+ if( !pEditEngine->IsInUndo() )
+ {
+ aParaRemovingHdl.Call( { this, pPara } );
+ }
+
+ pParaList->Remove( nPara );
+
+ if( pEditEngine->IsInUndo() || bPasting )
+ return;
+
+ pPara = pParaList->GetParagraph( nPara );
+ if ( pPara && ( pPara->GetDepth() > nDepth ) )
+ {
+ ImplCalcBulletText( nPara, true, false );
+ // Search for next on the this level ...
+ while ( pPara && pPara->GetDepth() > nDepth )
+ pPara = pParaList->GetParagraph( ++nPara );
+ }
+
+ if ( pPara && ( pPara->GetDepth() == nDepth ) )
+ ImplCalcBulletText( nPara, true, false );
+}
+
+void Outliner::Init( OutlinerMode nMode )
+{
+ nOutlinerMode = nMode;
+
+ Clear();
+
+ EEControlBits nCtrl = pEditEngine->GetControlWord();
+ nCtrl &= ~EEControlBits(EEControlBits::OUTLINER|EEControlBits::OUTLINER2);
+
+ SetMaxDepth( 9 );
+
+ switch ( GetOutlinerMode() )
+ {
+ case OutlinerMode::TextObject:
+ case OutlinerMode::TitleObject:
+ break;
+
+ case OutlinerMode::OutlineObject:
+ nCtrl |= EEControlBits::OUTLINER2;
+ break;
+ case OutlinerMode::OutlineView:
+ nCtrl |= EEControlBits::OUTLINER;
+ break;
+
+ default: OSL_FAIL( "Outliner::Init - Invalid Mode!" );
+ }
+
+ pEditEngine->SetControlWord( nCtrl );
+
+ const bool bWasUndoEnabled(IsUndoEnabled());
+ EnableUndo(false);
+ ImplInitDepth( 0, -1, false );
+ GetUndoManager().Clear();
+ EnableUndo(bWasUndoEnabled);
+}
+
+void Outliner::SetMaxDepth( sal_Int16 nDepth )
+{
+ if( nMaxDepth != nDepth )
+ {
+ nMaxDepth = std::min( nDepth, sal_Int16(SVX_MAX_NUM-1) );
+ }
+}
+
+sal_Int16 Outliner::GetDepth( sal_Int32 nPara ) const
+{
+ Paragraph* pPara = pParaList->GetParagraph( nPara );
+ DBG_ASSERT( pPara, "Outliner::GetDepth - Paragraph not found!" );
+ return pPara ? pPara->GetDepth() : -1;
+}
+
+void Outliner::SetDepth( Paragraph* pPara, sal_Int16 nNewDepth )
+{
+
+ ImplCheckDepth( nNewDepth );
+
+ if ( nNewDepth == pPara->GetDepth() )
+ return;
+
+ nDepthChangedHdlPrevDepth = pPara->GetDepth();
+ ParaFlag nPrevFlags = pPara->nFlags;
+
+ sal_Int32 nPara = GetAbsPos( pPara );
+ ImplInitDepth( nPara, nNewDepth, true );
+ ImplCalcBulletText( nPara, false, false );
+
+ if ( GetOutlinerMode() == OutlinerMode::OutlineObject )
+ ImplSetLevelDependentStyleSheet( nPara );
+
+ DepthChangedHdl(pPara, nPrevFlags);
+}
+
+sal_Int16 Outliner::GetNumberingStartValue( sal_Int32 nPara ) const
+{
+ Paragraph* pPara = pParaList->GetParagraph( nPara );
+ DBG_ASSERT( pPara, "Outliner::GetNumberingStartValue - Paragraph not found!" );
+ return pPara ? pPara->GetNumberingStartValue() : -1;
+}
+
+void Outliner::SetNumberingStartValue( sal_Int32 nPara, sal_Int16 nNumberingStartValue )
+{
+ Paragraph* pPara = pParaList->GetParagraph( nPara );
+ DBG_ASSERT( pPara, "Outliner::GetNumberingStartValue - Paragraph not found!" );
+ if( pPara && pPara->GetNumberingStartValue() != nNumberingStartValue )
+ {
+ if( IsUndoEnabled() && !IsInUndo() )
+ InsertUndo( std::make_unique<OutlinerUndoChangeParaNumberingRestart>( this, nPara,
+ pPara->GetNumberingStartValue(), nNumberingStartValue,
+ pPara->IsParaIsNumberingRestart(), pPara->IsParaIsNumberingRestart() ) );
+
+ pPara->SetNumberingStartValue( nNumberingStartValue );
+ ImplCheckParagraphs( nPara, pParaList->GetParagraphCount() );
+ pEditEngine->SetModified();
+ }
+}
+
+bool Outliner::IsParaIsNumberingRestart( sal_Int32 nPara ) const
+{
+ Paragraph* pPara = pParaList->GetParagraph( nPara );
+ DBG_ASSERT( pPara, "Outliner::IsParaIsNumberingRestart - Paragraph not found!" );
+ return pPara && pPara->IsParaIsNumberingRestart();
+}
+
+void Outliner::SetParaIsNumberingRestart( sal_Int32 nPara, bool bParaIsNumberingRestart )
+{
+ Paragraph* pPara = pParaList->GetParagraph( nPara );
+ DBG_ASSERT( pPara, "Outliner::SetParaIsNumberingRestart - Paragraph not found!" );
+ if( pPara && (pPara->IsParaIsNumberingRestart() != bParaIsNumberingRestart) )
+ {
+ if( IsUndoEnabled() && !IsInUndo() )
+ InsertUndo( std::make_unique<OutlinerUndoChangeParaNumberingRestart>( this, nPara,
+ pPara->GetNumberingStartValue(), pPara->GetNumberingStartValue(),
+ pPara->IsParaIsNumberingRestart(), bParaIsNumberingRestart ) );
+
+ pPara->SetParaIsNumberingRestart( bParaIsNumberingRestart );
+ ImplCheckParagraphs( nPara, pParaList->GetParagraphCount() );
+ pEditEngine->SetModified();
+ }
+}
+
+sal_Int32 Outliner::GetBulletsNumberingStatus(
+ const sal_Int32 nParaStart,
+ const sal_Int32 nParaEnd ) const
+{
+ if ( nParaStart > nParaEnd
+ || nParaEnd >= pParaList->GetParagraphCount() )
+ {
+ SAL_WARN("editeng", "<Outliner::GetBulletsNumberingStatus> - unexpected parameter values" );
+ return 2;
+ }
+
+ sal_Int32 nBulletsCount = 0;
+ sal_Int32 nNumberingCount = 0;
+ for (sal_Int32 nPara = nParaStart; nPara <= nParaEnd; ++nPara)
+ {
+ if ( !pParaList->GetParagraph(nPara) )
+ {
+ break;
+ }
+ const SvxNumberFormat* pFmt = GetNumberFormat(nPara);
+ if (!pFmt)
+ {
+ // At least, exists one paragraph that has no Bullets/Numbering.
+ break;
+ }
+ else if ((pFmt->GetNumberingType() == SVX_NUM_BITMAP) || (pFmt->GetNumberingType() == SVX_NUM_CHAR_SPECIAL))
+ {
+ // Having Bullets in this paragraph.
+ nBulletsCount++;
+ }
+ else
+ {
+ // Having Numbering in this paragraph.
+ nNumberingCount++;
+ }
+ }
+
+ const sal_Int32 nParaCount = nParaEnd - nParaStart + 1;
+ if ( nBulletsCount == nParaCount )
+ {
+ return 0;
+ }
+ else if ( nNumberingCount == nParaCount )
+ {
+ return 1;
+ }
+ return 2;
+}
+
+sal_Int32 Outliner::GetBulletsNumberingStatus() const
+{
+ return pParaList->GetParagraphCount() > 0
+ ? GetBulletsNumberingStatus( 0, pParaList->GetParagraphCount()-1 )
+ : 2;
+}
+
+std::optional<OutlinerParaObject> Outliner::CreateParaObject( sal_Int32 nStartPara, sal_Int32 nCount ) const
+{
+ if ( static_cast<sal_uInt64>(nStartPara) + nCount >
+ o3tl::make_unsigned(pParaList->GetParagraphCount()) )
+ nCount = pParaList->GetParagraphCount() - nStartPara;
+
+ // When a new OutlinerParaObject is created because a paragraph is just being deleted,
+ // it can happen that the ParaList is not updated yet...
+ if ( ( nStartPara + nCount ) > pEditEngine->GetParagraphCount() )
+ nCount = pEditEngine->GetParagraphCount() - nStartPara;
+
+ if (nCount <= 0)
+ return std::nullopt;
+
+ std::unique_ptr<EditTextObject> xText = pEditEngine->CreateTextObject( nStartPara, nCount );
+ const bool bIsEditDoc(OutlinerMode::TextObject == GetOutlinerMode());
+ ParagraphDataVector aParagraphDataVector(nCount);
+ const sal_Int32 nLastPara(nStartPara + nCount - 1);
+
+ for(sal_Int32 nPara(nStartPara); nPara <= nLastPara; nPara++)
+ {
+ aParagraphDataVector[nPara-nStartPara] = *GetParagraph(nPara);
+ }
+
+ xText->ClearPortionInfo(); // tdf#147166 the PortionInfo is unwanted here
+ OutlinerParaObject aPObj(std::move(xText), std::move(aParagraphDataVector), bIsEditDoc);
+ aPObj.SetOutlinerMode(GetOutlinerMode());
+
+ return aPObj;
+}
+
+void Outliner::SetToEmptyText()
+{
+ SetText(GetEmptyParaObject());
+}
+
+void Outliner::SetText( const OUString& rText, Paragraph* pPara )
+{
+ DBG_ASSERT(pPara,"SetText:No Para");
+
+ const sal_Int32 nPara = pParaList->GetAbsPos( pPara );
+
+ if (pEditEngine->GetText( nPara ) == rText)
+ {
+ // short-circuit logic to improve performance
+ bFirstParaIsEmpty = false;
+ return;
+ }
+
+ const bool bUpdate = pEditEngine->SetUpdateLayout( false );
+ ImplBlockInsertionCallbacks( true );
+
+ if (rText.isEmpty())
+ {
+ pEditEngine->SetText( nPara, rText );
+ ImplInitDepth( nPara, pPara->GetDepth(), false );
+ }
+ else
+ {
+ const OUString aText(convertLineEnd(rText, LINEEND_LF));
+
+ sal_Int32 nPos = 0;
+ sal_Int32 nInsPos = nPara+1;
+ sal_Int32 nIdx {0};
+ // Loop over all tokens, but ignore the last one if empty
+ // (i.e. if strings ends with the delimiter, detected by
+ // checking nIdx against string length). This check also
+ // handle empty strings.
+ while( nIdx>=0 && nIdx<aText.getLength() )
+ {
+ std::u16string_view aStr = o3tl::getToken(aText, 0, '\x0A', nIdx );
+
+ sal_Int16 nCurDepth;
+ if( nPos )
+ {
+ pPara = new Paragraph( -1 );
+ nCurDepth = -1;
+ }
+ else
+ nCurDepth = pPara->GetDepth();
+
+ // In the outliner mode, filter the tabs and set the indentation
+ // about a LRSpaceItem. In EditEngine mode intend over old tabs
+ if( ( GetOutlinerMode() == OutlinerMode::OutlineObject ) ||
+ ( GetOutlinerMode() == OutlinerMode::OutlineView ) )
+ {
+ // Extract Tabs
+ size_t nTabs = 0;
+ while ( ( nTabs < aStr.size() ) && ( aStr[nTabs] == '\t' ) )
+ nTabs++;
+ if ( nTabs )
+ aStr = aStr.substr(nTabs);
+
+ // Keep depth? (see Outliner::Insert)
+ if( !(pPara->nFlags & ParaFlag::HOLDDEPTH) )
+ {
+ nCurDepth = nTabs-1; //TODO: sal_Int32 -> sal_Int16!
+ ImplCheckDepth( nCurDepth );
+ pPara->SetDepth( nCurDepth );
+ }
+ }
+ if( nPos ) // not with the first paragraph
+ {
+ pParaList->Insert( std::unique_ptr<Paragraph>(pPara), nInsPos );
+ pEditEngine->InsertParagraph( nInsPos, OUString(aStr) );
+ ParagraphInsertedHdl(pPara);
+ }
+ else
+ {
+ nInsPos--;
+ pEditEngine->SetText( nInsPos, OUString(aStr) );
+ }
+ ImplInitDepth( nInsPos, nCurDepth, false );
+ nInsPos++;
+ nPos++;
+ }
+ }
+
+ DBG_ASSERT(pParaList->GetParagraphCount()==pEditEngine->GetParagraphCount(),"SetText failed!");
+ bFirstParaIsEmpty = false;
+ ImplBlockInsertionCallbacks( false );
+ // Restore the update mode.
+ pEditEngine->SetUpdateLayout(bUpdate, /*bRestoring=*/true);
+}
+
+// pView == 0 -> Ignore tabs
+
+bool Outliner::ImpConvertEdtToOut( sal_Int32 nPara )
+{
+
+ bool bConverted = false;
+ sal_Int32 nTabs = 0;
+ ESelection aDelSel;
+
+ OUString aName;
+
+ OUString aStr( pEditEngine->GetText( nPara ) );
+ const sal_Unicode* pPtr = aStr.getStr();
+
+ sal_Int32 nHeadingNumberStart = 0;
+ sal_Int32 nNumberingNumberStart = 0;
+ SfxStyleSheet* pStyle= pEditEngine->GetStyleSheet( nPara );
+ if( pStyle )
+ {
+ OUString aHeading_US( "heading" );
+ OUString aNumber_US( "Numbering" );
+ aName = pStyle->GetName();
+ sal_Int32 nSearch;
+ if ( ( nSearch = aName.indexOf( aHeading_US ) ) != -1 )
+ nHeadingNumberStart = nSearch + aHeading_US.getLength();
+ else if ( ( nSearch = aName.indexOf( aNumber_US ) ) != -1 )
+ nNumberingNumberStart = nSearch + aNumber_US.getLength();
+ }
+
+ if ( nHeadingNumberStart || nNumberingNumberStart )
+ {
+ // PowerPoint import ?
+ if( nHeadingNumberStart && ( aStr.getLength() >= 2 ) &&
+ ( pPtr[0] != '\t' ) && ( pPtr[1] == '\t' ) )
+ {
+ // Extract Bullet and Tab
+ aDelSel = ESelection( nPara, 0, nPara, 2 );
+ }
+
+ sal_Int32 nPos = nHeadingNumberStart ? nHeadingNumberStart : nNumberingNumberStart;
+ std::u16string_view aLevel = comphelper::string::stripStart(aName.subView(nPos), ' ');
+ nTabs = o3tl::toInt32(aLevel);
+ if( nTabs )
+ nTabs--; // Level 0 = "heading 1"
+ bConverted = true;
+ }
+ else
+ {
+ // filter leading tabs
+ while( *pPtr == '\t' )
+ {
+ pPtr++;
+ nTabs++;
+ }
+ // Remove tabs from the text
+ if( nTabs )
+ aDelSel = ESelection( nPara, 0, nPara, nTabs );
+ }
+
+ if ( aDelSel.HasRange() )
+ {
+ pEditEngine->QuickDelete( aDelSel );
+ }
+
+ const SfxInt16Item& rLevel = pEditEngine->GetParaAttrib( nPara, EE_PARA_OUTLLEVEL );
+ sal_Int16 nOutlLevel = rLevel.GetValue();
+
+ ImplCheckDepth( nOutlLevel );
+ ImplInitDepth( nPara, nOutlLevel, false );
+
+ return bConverted;
+}
+
+void Outliner::SetText( const OutlinerParaObject& rPObj )
+{
+ bool bUpdate = pEditEngine->SetUpdateLayout( false );
+
+ bool bUndo = pEditEngine->IsUndoEnabled();
+ EnableUndo( false );
+
+ Init( rPObj.GetOutlinerMode() );
+
+ ImplBlockInsertionCallbacks( true );
+ pEditEngine->SetText(rPObj.GetTextObject());
+
+ bFirstParaIsEmpty = false;
+
+ pParaList->Clear();
+ for( sal_Int32 nCurPara = 0; nCurPara < rPObj.Count(); nCurPara++ )
+ {
+ std::unique_ptr<Paragraph> pPara(new Paragraph( rPObj.GetParagraphData(nCurPara)));
+ ImplCheckDepth( pPara->nDepth );
+
+ pParaList->Append(std::move(pPara));
+ ImplCheckNumBulletItem( nCurPara );
+ }
+
+ ImplCheckParagraphs( 0, pParaList->GetParagraphCount() );
+
+ EnableUndo( bUndo );
+ ImplBlockInsertionCallbacks( false );
+ pEditEngine->SetUpdateLayout( bUpdate );
+
+ DBG_ASSERT( pParaList->GetParagraphCount()==rPObj.Count(),"SetText failed");
+ DBG_ASSERT( pEditEngine->GetParagraphCount()==rPObj.Count(),"SetText failed");
+}
+
+void Outliner::AddText( const OutlinerParaObject& rPObj, bool bAppend )
+{
+ bool bUpdate = pEditEngine->SetUpdateLayout( false );
+
+ ImplBlockInsertionCallbacks( true );
+ sal_Int32 nPara;
+ if( bFirstParaIsEmpty )
+ {
+ pParaList->Clear();
+ pEditEngine->SetText(rPObj.GetTextObject());
+ nPara = 0;
+ bAppend = false;
+ }
+ else
+ {
+ nPara = pParaList->GetParagraphCount();
+ pEditEngine->InsertParagraph( EE_PARA_APPEND, rPObj.GetTextObject(), bAppend );
+ }
+ bFirstParaIsEmpty = false;
+
+ for( sal_Int32 n = 0; n < rPObj.Count(); n++ )
+ {
+ if ( n == 0 && bAppend )
+ {
+ // This first "paragraph" was just appended to an existing (incomplete) paragraph.
+ // Since no new paragraph will be added, the assumed increase-by-1 also won't happen.
+ --nPara;
+ continue;
+ }
+
+ Paragraph* pPara = new Paragraph( rPObj.GetParagraphData(n) );
+ pParaList->Append(std::unique_ptr<Paragraph>(pPara));
+ sal_Int32 nP = nPara+n;
+ DBG_ASSERT(pParaList->GetAbsPos(pPara)==nP,"AddText:Out of sync");
+ ImplInitDepth( nP, pPara->GetDepth(), false );
+ }
+ DBG_ASSERT( pEditEngine->GetParagraphCount()==pParaList->GetParagraphCount(), "SetText: OutOfSync" );
+
+ ImplCheckParagraphs( nPara, pParaList->GetParagraphCount() );
+
+ ImplBlockInsertionCallbacks( false );
+ pEditEngine->SetUpdateLayout( bUpdate );
+}
+
+OUString Outliner::CalcFieldValue( const SvxFieldItem& rField, sal_Int32 nPara, sal_Int32 nPos, std::optional<Color>& rpTxtColor, std::optional<Color>& rpFldColor, std::optional<FontLineStyle>& rpFldLineStyle )
+{
+ if ( !aCalcFieldValueHdl.IsSet() )
+ return OUString( ' ' );
+
+ EditFieldInfo aFldInfo( this, rField, nPara, nPos );
+ // The FldColor is preset with COL_LIGHTGRAY.
+ if ( rpFldColor )
+ aFldInfo.SetFieldColor( *rpFldColor );
+
+ aCalcFieldValueHdl.Call( &aFldInfo );
+ if ( aFldInfo.GetTextColor() )
+ {
+ rpTxtColor = *aFldInfo.GetTextColor();
+ }
+
+ if ( aFldInfo.GetFontLineStyle() )
+ {
+ rpFldLineStyle = *aFldInfo.GetFontLineStyle();
+ }
+
+ if (aFldInfo.GetFieldColor())
+ rpFldColor = *aFldInfo.GetFieldColor();
+ else
+ rpFldColor.reset();
+
+ return aFldInfo.GetRepresentation();
+}
+
+void Outliner::SetStyleSheet( sal_Int32 nPara, SfxStyleSheet* pStyle )
+{
+ Paragraph* pPara = pParaList->GetParagraph( nPara );
+ if (pPara)
+ {
+ pEditEngine->SetStyleSheet( nPara, pStyle );
+ ImplCheckNumBulletItem( nPara );
+ }
+}
+
+void Outliner::ImplCheckNumBulletItem( sal_Int32 nPara )
+{
+ Paragraph* pPara = pParaList->GetParagraph( nPara );
+ if (pPara)
+ pPara->aBulSize.setWidth( -1 );
+}
+
+void Outliner::ImplSetLevelDependentStyleSheet( sal_Int32 nPara )
+{
+
+ DBG_ASSERT( ( GetOutlinerMode() == OutlinerMode::OutlineObject ) || ( GetOutlinerMode() == OutlinerMode::OutlineView ), "SetLevelDependentStyleSheet: Wrong Mode!" );
+
+ SfxStyleSheet* pStyle = GetStyleSheet( nPara );
+
+ if ( !pStyle )
+ return;
+
+ sal_Int16 nDepth = GetDepth( nPara );
+ if( nDepth < 0 )
+ nDepth = 0;
+
+ OUString aNewStyleSheetName( pStyle->GetName() );
+ aNewStyleSheetName = aNewStyleSheetName.subView( 0, aNewStyleSheetName.getLength()-1 ) +
+ OUString::number( nDepth+1 );
+ SfxStyleSheet* pNewStyle = static_cast<SfxStyleSheet*>(GetStyleSheetPool()->Find( aNewStyleSheetName, pStyle->GetFamily() ));
+ DBG_ASSERT( pNewStyle, "AutoStyleSheetName - Style not found!" );
+ if ( pNewStyle && ( pNewStyle != GetStyleSheet( nPara ) ) )
+ {
+ SfxItemSet aOldAttrs( GetParaAttribs( nPara ) );
+ SetStyleSheet( nPara, pNewStyle );
+ if ( aOldAttrs.GetItemState( EE_PARA_NUMBULLET ) == SfxItemState::SET )
+ {
+ SfxItemSet aAttrs( GetParaAttribs( nPara ) );
+ aAttrs.Put( aOldAttrs.Get( EE_PARA_NUMBULLET ) );
+ SetParaAttribs( nPara, aAttrs );
+ }
+ }
+}
+
+void Outliner::ImplInitDepth( sal_Int32 nPara, sal_Int16 nDepth, bool bCreateUndo )
+{
+
+ DBG_ASSERT( ( nDepth >= gnMinDepth ) && ( nDepth <= nMaxDepth ), "ImplInitDepth - Depth is invalid!" );
+
+ Paragraph* pPara = pParaList->GetParagraph( nPara );
+ if (!pPara)
+ return;
+ sal_Int16 nOldDepth = pPara->GetDepth();
+ pPara->SetDepth( nDepth );
+
+ // For IsInUndo attributes and style do not have to be set, there
+ // the old values are restored by the EditEngine.
+ if( IsInUndo() )
+ return;
+
+ bool bUpdate = pEditEngine->SetUpdateLayout( false );
+
+ bool bUndo = bCreateUndo && IsUndoEnabled();
+
+ SfxItemSet aAttrs( pEditEngine->GetParaAttribs( nPara ) );
+ aAttrs.Put( SfxInt16Item( EE_PARA_OUTLLEVEL, nDepth ) );
+ pEditEngine->SetParaAttribs( nPara, aAttrs );
+ ImplCheckNumBulletItem( nPara );
+ ImplCalcBulletText( nPara, false, false );
+
+ if ( bUndo )
+ {
+ InsertUndo( std::make_unique<OutlinerUndoChangeDepth>( this, nPara, nOldDepth, nDepth ) );
+ }
+
+ pEditEngine->SetUpdateLayout( bUpdate );
+}
+
+void Outliner::SetParaAttribs( sal_Int32 nPara, const SfxItemSet& rSet )
+{
+
+ pEditEngine->SetParaAttribs( nPara, rSet );
+}
+
+void Outliner::SetCharAttribs(sal_Int32 nPara, const SfxItemSet& rSet)
+{
+ pEditEngine->SetCharAttribs(nPara, rSet);
+}
+
+bool Outliner::Expand( Paragraph const * pPara )
+{
+ if ( !pParaList->HasHiddenChildren( pPara ) )
+ return false;
+
+ std::unique_ptr<OLUndoExpand> pUndo;
+ bool bUndo = IsUndoEnabled() && !IsInUndo();
+ if( bUndo )
+ {
+ UndoActionStart( OLUNDO_EXPAND );
+ pUndo.reset( new OLUndoExpand( this, OLUNDO_EXPAND ) );
+ pUndo->nCount = pParaList->GetAbsPos( pPara );
+ }
+ pParaList->Expand( pPara );
+ InvalidateBullet(pParaList->GetAbsPos(pPara));
+ if( bUndo )
+ {
+ InsertUndo( std::move(pUndo) );
+ UndoActionEnd();
+ }
+ return true;
+}
+
+bool Outliner::Collapse( Paragraph const * pPara )
+{
+ if ( !pParaList->HasVisibleChildren( pPara ) ) // collapsed
+ return false;
+
+ std::unique_ptr<OLUndoExpand> pUndo;
+ bool bUndo = false;
+
+ if( !IsInUndo() && IsUndoEnabled() )
+ bUndo = true;
+ if( bUndo )
+ {
+ UndoActionStart( OLUNDO_COLLAPSE );
+ pUndo.reset( new OLUndoExpand( this, OLUNDO_COLLAPSE ) );
+ pUndo->nCount = pParaList->GetAbsPos( pPara );
+ }
+
+ pParaList->Collapse( pPara );
+ InvalidateBullet(pParaList->GetAbsPos(pPara));
+ if( bUndo )
+ {
+ InsertUndo( std::move(pUndo) );
+ UndoActionEnd();
+ }
+ return true;
+}
+
+vcl::Font Outliner::ImpCalcBulletFont( sal_Int32 nPara ) const
+{
+ const SvxNumberFormat* pFmt = GetNumberFormat( nPara );
+ DBG_ASSERT( pFmt && ( pFmt->GetNumberingType() != SVX_NUM_BITMAP ) && ( pFmt->GetNumberingType() != SVX_NUM_NUMBER_NONE ), "ImpCalcBulletFont: Missing or BitmapBullet!" );
+
+ vcl::Font aStdFont;
+ if ( !pEditEngine->IsFlatMode() )
+ {
+ ESelection aSel( nPara, 0, nPara, 0 );
+ aStdFont = EditEngine::CreateFontFromItemSet( pEditEngine->GetAttribs( aSel ), pEditEngine->GetScriptType( aSel ) );
+ }
+ else
+ {
+ aStdFont = pEditEngine->GetStandardFont( nPara );
+ }
+
+ vcl::Font aBulletFont;
+ std::optional<vcl::Font> pSourceFont;
+ if ( pFmt->GetNumberingType() == SVX_NUM_CHAR_SPECIAL )
+ {
+ pSourceFont = pFmt->GetBulletFont();
+ }
+
+ if (pSourceFont)
+ {
+ aBulletFont = *pSourceFont;
+ }
+ else
+ {
+ aBulletFont = aStdFont;
+ aBulletFont.SetUnderline( LINESTYLE_NONE );
+ aBulletFont.SetOverline( LINESTYLE_NONE );
+ aBulletFont.SetStrikeout( STRIKEOUT_NONE );
+ aBulletFont.SetEmphasisMark( FontEmphasisMark::NONE );
+ aBulletFont.SetRelief( FontRelief::NONE );
+ }
+
+ // Use original scale...
+ double nStretchY = 100.0;
+ getGlobalScale(o3tl::temporary(double()), nStretchY, o3tl::temporary(double()), o3tl::temporary(double()));
+
+ double fScale = pFmt->GetBulletRelSize() * nStretchY / 100.0;
+ double fScaledLineHeight = aStdFont.GetFontSize().Height();
+ fScaledLineHeight *= fScale * 10;
+ fScaledLineHeight /= 1000.0;
+
+ aBulletFont.SetAlignment( ALIGN_BOTTOM );
+ aBulletFont.SetFontSize(Size(0, basegfx::fround(fScaledLineHeight)));
+ bool bVertical = IsVertical();
+ aBulletFont.SetVertical( bVertical );
+ aBulletFont.SetOrientation( Degree10(bVertical ? (IsTopToBottom() ? 2700 : 900) : 0) );
+
+ Color aColor( COL_AUTO );
+ if( !pEditEngine->IsFlatMode() && !( pEditEngine->GetControlWord() & EEControlBits::NOCOLORS ) )
+ {
+ aColor = pFmt->GetBulletColor();
+ }
+
+ if ( ( aColor == COL_AUTO ) || ( IsForceAutoColor() ) )
+ aColor = pEditEngine->GetAutoColor();
+
+ aBulletFont.SetColor( aColor );
+ return aBulletFont;
+}
+
+void Outliner::PaintBullet(sal_Int32 nPara, const Point& rStartPos, const Point& rOrigin,
+ Degree10 nOrientation, OutputDevice& rOutDev)
+{
+
+ bool bDrawBullet = false;
+ if (pEditEngine)
+ {
+ const SfxBoolItem& rBulletState = pEditEngine->GetParaAttrib( nPara, EE_PARA_BULLETSTATE );
+ bDrawBullet = rBulletState.GetValue();
+ }
+
+ if (!(bDrawBullet && ImplHasNumberFormat(nPara)))
+ return;
+
+ bool bVertical = IsVertical();
+ bool bTopToBottom = IsTopToBottom();
+
+ bool bRightToLeftPara = pEditEngine->IsRightToLeft( nPara );
+
+ tools::Rectangle aBulletArea( ImpCalcBulletArea( nPara, true, false ) );
+
+ double nStretchX = 100.0;
+ getGlobalScale(o3tl::temporary(double()), o3tl::temporary(double()),
+ nStretchX, o3tl::temporary(double()));
+
+ tools::Long nStretchBulletX = basegfx::fround(double(aBulletArea.Left()) * nStretchX / 100.0);
+ tools::Long nStretchBulletWidth = basegfx::fround(double(aBulletArea.GetWidth()) * nStretchX / 100.0);
+ aBulletArea = tools::Rectangle(Point(nStretchBulletX, aBulletArea.Top()),
+ Size(nStretchBulletWidth, aBulletArea.GetHeight()) );
+
+ Paragraph* pPara = pParaList->GetParagraph( nPara );
+ const SvxNumberFormat* pFmt = GetNumberFormat( nPara );
+ if ( pFmt && ( pFmt->GetNumberingType() != SVX_NUM_NUMBER_NONE ) )
+ {
+ if( pFmt->GetNumberingType() != SVX_NUM_BITMAP )
+ {
+ vcl::Font aBulletFont( ImpCalcBulletFont( nPara ) );
+ // Use baseline
+ bool bSymbol = pFmt->GetNumberingType() == SVX_NUM_CHAR_SPECIAL;
+ aBulletFont.SetAlignment( bSymbol ? ALIGN_BOTTOM : ALIGN_BASELINE );
+ vcl::Font aOldFont = rOutDev.GetFont();
+ rOutDev.SetFont( aBulletFont );
+
+ ParagraphInfos aParaInfos = pEditEngine->GetParagraphInfos( nPara );
+ Point aTextPos;
+ if ( !bVertical )
+ {
+// aTextPos.Y() = rStartPos.Y() + aBulletArea.Bottom();
+ aTextPos.setY( rStartPos.Y() + ( bSymbol ? aBulletArea.Bottom() : aParaInfos.nFirstLineMaxAscent ) );
+ if ( !bRightToLeftPara )
+ aTextPos.setX( rStartPos.X() + aBulletArea.Left() );
+ else
+ aTextPos.setX( rStartPos.X() + GetPaperSize().Width() - aBulletArea.Right() );
+ }
+ else
+ {
+ if (bTopToBottom)
+ {
+// aTextPos.X() = rStartPos.X() - aBulletArea.Bottom();
+ aTextPos.setX( rStartPos.X() - (bSymbol ? aBulletArea.Bottom() : aParaInfos.nFirstLineMaxAscent) );
+ aTextPos.setY( rStartPos.Y() + aBulletArea.Left() );
+ }
+ else
+ {
+ aTextPos.setX( rStartPos.X() + (bSymbol ? aBulletArea.Bottom() : aParaInfos.nFirstLineMaxAscent) );
+ aTextPos.setY( rStartPos.Y() + aBulletArea.Left() );
+ }
+ }
+
+ if ( nOrientation )
+ {
+ // Both TopLeft and bottom left is not quite correct,
+ // since in EditEngine baseline ...
+ rOrigin.RotateAround(aTextPos, nOrientation);
+
+ vcl::Font aRotatedFont( aBulletFont );
+ aRotatedFont.SetOrientation( nOrientation );
+ rOutDev.SetFont( aRotatedFont );
+ }
+
+ // VCL will take care of brackets and so on...
+ vcl::text::ComplexTextLayoutFlags nLayoutMode = rOutDev.GetLayoutMode();
+ nLayoutMode &= ~vcl::text::ComplexTextLayoutFlags(vcl::text::ComplexTextLayoutFlags::BiDiRtl|vcl::text::ComplexTextLayoutFlags::BiDiStrong);
+ if ( bRightToLeftPara )
+ nLayoutMode |= vcl::text::ComplexTextLayoutFlags::BiDiRtl | vcl::text::ComplexTextLayoutFlags::TextOriginLeft | vcl::text::ComplexTextLayoutFlags::BiDiStrong;
+ rOutDev.SetLayoutMode( nLayoutMode );
+
+ if(bStrippingPortions)
+ {
+ const vcl::Font& aSvxFont(rOutDev.GetFont());
+ KernArray aBuf;
+ rOutDev.GetTextArray( pPara->GetText(), &aBuf );
+
+ if(bSymbol)
+ {
+ // aTextPos is Bottom, go to Baseline
+ FontMetric aMetric(rOutDev.GetFontMetric());
+ aTextPos.AdjustY( -(aMetric.GetDescent()) );
+ }
+
+ assert(aBuf.get_factor() == 1);
+ DrawingText(aTextPos, pPara->GetText(), 0, pPara->GetText().getLength(), aBuf.get_subunit_array(), {},
+ aSvxFont, nPara, bRightToLeftPara ? 1 : 0, nullptr, nullptr, false, false, true, nullptr, Color(), Color());
+ }
+ else
+ {
+ rOutDev.DrawText( aTextPos, pPara->GetText() );
+ }
+
+ rOutDev.SetFont( aOldFont );
+ }
+ else
+ {
+ if ( pFmt->GetBrush()->GetGraphicObject() )
+ {
+ Point aBulletPos;
+ if ( !bVertical )
+ {
+ aBulletPos.setY( rStartPos.Y() + aBulletArea.Top() );
+ if ( !bRightToLeftPara )
+ aBulletPos.setX( rStartPos.X() + aBulletArea.Left() );
+ else
+ aBulletPos.setX( rStartPos.X() + GetPaperSize().Width() - aBulletArea.Right() );
+ }
+ else
+ {
+ if (bTopToBottom)
+ {
+ aBulletPos.setX( rStartPos.X() - aBulletArea.Bottom() );
+ aBulletPos.setY( rStartPos.Y() + aBulletArea.Left() );
+ }
+ else
+ {
+ aBulletPos.setX( rStartPos.X() + aBulletArea.Top() );
+ aBulletPos.setY( rStartPos.Y() - aBulletArea.Right() );
+ }
+ }
+
+ if(bStrippingPortions)
+ {
+ if(aDrawBulletHdl.IsSet())
+ {
+ // call something analog to aDrawPortionHdl (if set) and feed it something
+ // analog to DrawPortionInfo...
+ // created aDrawBulletHdl, Set/GetDrawBulletHdl.
+ // created DrawBulletInfo and added handling to sdrtextdecomposition.cxx
+ DrawBulletInfo aDrawBulletInfo(
+ *pFmt->GetBrush()->GetGraphicObject(),
+ aBulletPos,
+ pPara->aBulSize);
+
+ aDrawBulletHdl.Call(&aDrawBulletInfo);
+ }
+ }
+ else
+ {
+ pFmt->GetBrush()->GetGraphicObject()->Draw(rOutDev, aBulletPos, pPara->aBulSize);
+ }
+ }
+ }
+ }
+
+ // In case of collapsed subparagraphs paint a line before the text.
+ if( !pParaList->HasChildren(pPara) || pParaList->HasVisibleChildren(pPara) ||
+ bStrippingPortions || nOrientation )
+ return;
+
+ tools::Long nWidth = rOutDev.PixelToLogic( Size( 10, 0 ) ).Width();
+
+ Point aStartPos, aEndPos;
+ if ( !bVertical )
+ {
+ aStartPos.setY( rStartPos.Y() + aBulletArea.Bottom() );
+ if ( !bRightToLeftPara )
+ aStartPos.setX( rStartPos.X() + aBulletArea.Right() );
+ else
+ aStartPos.setX( rStartPos.X() + GetPaperSize().Width() - aBulletArea.Left() );
+ aEndPos = aStartPos;
+ aEndPos.AdjustX(nWidth );
+ }
+ else
+ {
+ aStartPos.setX( rStartPos.X() - aBulletArea.Bottom() );
+ aStartPos.setY( rStartPos.Y() + aBulletArea.Right() );
+ aEndPos = aStartPos;
+ aEndPos.AdjustY(nWidth );
+ }
+
+ const Color& rOldLineColor = rOutDev.GetLineColor();
+ rOutDev.SetLineColor( COL_BLACK );
+ rOutDev.DrawLine( aStartPos, aEndPos );
+ rOutDev.SetLineColor( rOldLineColor );
+}
+
+void Outliner::InvalidateBullet(sal_Int32 nPara)
+{
+ tools::Long nLineHeight = static_cast<tools::Long>(pEditEngine->GetLineHeight(nPara ));
+ for (OutlinerView* pView : aViewList)
+ {
+ Point aPos( pView->pEditView->GetWindowPosTopLeft(nPara ) );
+ tools::Rectangle aRect( pView->GetOutputArea() );
+ aRect.SetRight( aPos.X() );
+ aRect.SetTop( aPos.Y() );
+ aRect.SetBottom( aPos.Y() );
+ aRect.AdjustBottom(nLineHeight );
+
+ pView->pEditView->InvalidateWindow(aRect);
+ }
+}
+
+ErrCode Outliner::Read( SvStream& rInput, const OUString& rBaseURL, EETextFormat eFormat, SvKeyValueIterator* pHTTPHeaderAttrs )
+{
+
+ bool bOldUndo = pEditEngine->IsUndoEnabled();
+ EnableUndo( false );
+
+ bool bUpdate = pEditEngine->SetUpdateLayout( false );
+
+ Clear();
+
+ ImplBlockInsertionCallbacks( true );
+ ErrCode nRet = pEditEngine->Read( rInput, rBaseURL, eFormat, pHTTPHeaderAttrs );
+
+ bFirstParaIsEmpty = false;
+
+ sal_Int32 nParas = pEditEngine->GetParagraphCount();
+ pParaList->Clear();
+ for ( sal_Int32 n = 0; n < nParas; n++ )
+ {
+ std::unique_ptr<Paragraph> pPara(new Paragraph( 0 ));
+ pParaList->Append(std::move(pPara));
+ }
+
+ ImpFilterIndents( 0, nParas-1 );
+
+ ImplBlockInsertionCallbacks( false );
+ pEditEngine->SetUpdateLayout( bUpdate );
+ EnableUndo( bOldUndo );
+
+ return nRet;
+}
+
+
+void Outliner::ImpFilterIndents( sal_Int32 nFirstPara, sal_Int32 nLastPara )
+{
+ bool bUpdate = pEditEngine->SetUpdateLayout( false );
+
+ Paragraph* pLastConverted = nullptr;
+ for( sal_Int32 nPara = nFirstPara; nPara <= nLastPara; nPara++ )
+ {
+ Paragraph* pPara = pParaList->GetParagraph( nPara );
+ if (pPara)
+ {
+ if( ImpConvertEdtToOut( nPara ) )
+ {
+ pLastConverted = pPara;
+ }
+ else if ( pLastConverted )
+ {
+ // Arrange normal paragraphs below the heading ...
+ pPara->SetDepth( pLastConverted->GetDepth() );
+ }
+
+ ImplInitDepth( nPara, pPara->GetDepth(), false );
+ }
+ }
+
+ pEditEngine->SetUpdateLayout( bUpdate );
+}
+
+EditUndoManager& Outliner::GetUndoManager()
+{
+ return pEditEngine->GetUndoManager();
+}
+
+EditUndoManager* Outliner::SetUndoManager(EditUndoManager* pNew)
+{
+ return pEditEngine->SetUndoManager(pNew);
+}
+
+void Outliner::ImpTextPasted( sal_Int32 nStartPara, sal_Int32 nCount )
+{
+ bool bUpdate = pEditEngine->SetUpdateLayout( false );
+
+ const sal_Int32 nStart = nStartPara;
+
+ Paragraph* pPara = pParaList->GetParagraph( nStartPara );
+
+ while( nCount && pPara )
+ {
+ if( GetOutlinerMode() != OutlinerMode::TextObject )
+ {
+ nDepthChangedHdlPrevDepth = pPara->GetDepth();
+ ParaFlag nPrevFlags = pPara->nFlags;
+
+ ImpConvertEdtToOut( nStartPara );
+
+ if( nStartPara == nStart )
+ {
+ // the existing paragraph has changed depth or flags
+ if( (pPara->GetDepth() != nDepthChangedHdlPrevDepth) || (pPara->nFlags != nPrevFlags) )
+ DepthChangedHdl(pPara, nPrevFlags);
+ }
+ }
+ else // EditEngine mode
+ {
+ sal_Int16 nDepth = -1;
+ const SfxItemSet& rAttrs = pEditEngine->GetParaAttribs( nStartPara );
+ if ( rAttrs.GetItemState( EE_PARA_OUTLLEVEL ) == SfxItemState::SET )
+ {
+ const SfxInt16Item& rLevel = rAttrs.Get( EE_PARA_OUTLLEVEL );
+ nDepth = rLevel.GetValue();
+ }
+ if ( nDepth != GetDepth( nStartPara ) )
+ ImplInitDepth( nStartPara, nDepth, false );
+ }
+
+ nCount--;
+ nStartPara++;
+ pPara = pParaList->GetParagraph( nStartPara );
+ }
+
+ pEditEngine->SetUpdateLayout( bUpdate );
+
+ DBG_ASSERT(pParaList->GetParagraphCount()==pEditEngine->GetParagraphCount(),"ImpTextPasted failed");
+}
+
+bool Outliner::IndentingPagesHdl( OutlinerView* pView )
+{
+ if( !aIndentingPagesHdl.IsSet() )
+ return true;
+ return aIndentingPagesHdl.Call( pView );
+}
+
+bool Outliner::ImpCanIndentSelectedPages( OutlinerView* pCurView )
+{
+ // The selected pages must already be set in advance through
+ // ImpCalcSelectedPages
+
+ // If the first paragraph is on level 0 it can not indented in any case,
+ // possible there might be indentations in the following on the 0 level.
+ if ( ( mnFirstSelPage == 0 ) && ( GetOutlinerMode() != OutlinerMode::TextObject ) )
+ {
+ if ( nDepthChangedHdlPrevDepth == 1 ) // is the only page
+ return false;
+ else
+ (void)pCurView->ImpCalcSelectedPages( false ); // without the first
+ }
+ return IndentingPagesHdl( pCurView );
+}
+
+
+bool Outliner::ImpCanDeleteSelectedPages( OutlinerView* pCurView )
+{
+ // The selected pages must already be set in advance through
+ // ImpCalcSelectedPages
+ return RemovingPagesHdl( pCurView );
+}
+
+Outliner::Outliner(SfxItemPool* pPool, OutlinerMode nMode)
+ : mnFirstSelPage(0)
+ , nDepthChangedHdlPrevDepth(0)
+ , nMaxDepth(9)
+ , bFirstParaIsEmpty(true)
+ , nBlockInsCallback(0)
+ , bStrippingPortions(false)
+ , bPasting(false)
+{
+
+ pParaList.reset( new ParagraphList );
+ pParaList->SetVisibleStateChangedHdl( LINK( this, Outliner, ParaVisibleStateChangedHdl ) );
+ std::unique_ptr<Paragraph> pPara(new Paragraph( 0 ));
+ pParaList->Append(std::move(pPara));
+
+ pEditEngine.reset( new OutlinerEditEng( this, pPool ) );
+ pEditEngine->SetBeginMovingParagraphsHdl( LINK( this, Outliner, BeginMovingParagraphsHdl ) );
+ pEditEngine->SetEndMovingParagraphsHdl( LINK( this, Outliner, EndMovingParagraphsHdl ) );
+ pEditEngine->SetBeginPasteOrDropHdl( LINK( this, Outliner, BeginPasteOrDropHdl ) );
+ pEditEngine->SetEndPasteOrDropHdl( LINK( this, Outliner, EndPasteOrDropHdl ) );
+
+ Init( nMode );
+}
+
+Outliner::~Outliner()
+{
+ pParaList->Clear();
+ pParaList.reset();
+ pEditEngine.reset();
+}
+
+size_t Outliner::InsertView( OutlinerView* pView, size_t nIndex )
+{
+ size_t ActualIndex;
+
+ if ( nIndex >= aViewList.size() )
+ {
+ aViewList.push_back( pView );
+ ActualIndex = aViewList.size() - 1;
+ }
+ else
+ {
+ ViewList::iterator it = aViewList.begin();
+ advance( it, nIndex );
+ ActualIndex = nIndex;
+ }
+ pEditEngine->InsertView( pView->pEditView.get(), nIndex );
+ return ActualIndex;
+}
+
+void Outliner::RemoveView( OutlinerView const * pView )
+{
+ ViewList::iterator it = std::find(aViewList.begin(), aViewList.end(), pView);
+ if (it != aViewList.end())
+ {
+ pView->pEditView->HideCursor(); // HACK
+ pEditEngine->RemoveView( pView->pEditView.get() );
+ aViewList.erase( it );
+ }
+}
+
+void Outliner::RemoveView( size_t nIndex )
+{
+ EditView* pEditView = pEditEngine->GetView( nIndex );
+ pEditView->HideCursor(); // HACK
+
+ pEditEngine->RemoveView( nIndex );
+
+ {
+ ViewList::iterator it = aViewList.begin();
+ advance( it, nIndex );
+ aViewList.erase( it );
+ }
+}
+
+
+OutlinerView* Outliner::GetView( size_t nIndex ) const
+{
+ return ( nIndex >= aViewList.size() ) ? nullptr : aViewList[ nIndex ];
+}
+
+size_t Outliner::GetViewCount() const
+{
+ return aViewList.size();
+}
+
+void Outliner::ParagraphInsertedHdl(Paragraph* pPara)
+{
+ if( !IsInUndo() )
+ aParaInsertedHdl.Call( { this, pPara } );
+}
+
+
+void Outliner::DepthChangedHdl(Paragraph* pPara, ParaFlag nPrevFlags)
+{
+ if( !IsInUndo() )
+ aDepthChangedHdl.Call( { this, pPara, nPrevFlags } );
+}
+
+
+sal_Int32 Outliner::GetAbsPos( Paragraph const * pPara ) const
+{
+ DBG_ASSERT(pPara,"GetAbsPos:No Para");
+ return pParaList->GetAbsPos( pPara );
+}
+
+sal_Int32 Outliner::GetParagraphCount() const
+{
+ return pParaList->GetParagraphCount();
+}
+
+Paragraph* Outliner::GetParagraph( sal_Int32 nAbsPos ) const
+{
+ return pParaList->GetParagraph( nAbsPos );
+}
+
+bool Outliner::HasChildren( Paragraph const * pParagraph ) const
+{
+ return pParaList->HasChildren( pParagraph );
+}
+
+bool Outliner::ImplHasNumberFormat( sal_Int32 nPara ) const
+{
+ return GetNumberFormat(nPara) != nullptr;
+}
+
+const SvxNumberFormat* Outliner::GetNumberFormat( sal_Int32 nPara ) const
+{
+ const SvxNumberFormat* pFmt = nullptr;
+
+ Paragraph* pPara = pParaList->GetParagraph( nPara );
+ if (!pPara)
+ return nullptr;
+
+ sal_Int16 nDepth = pPara->GetDepth();
+
+ if( nDepth >= 0 )
+ {
+ const SvxNumBulletItem& rNumBullet = pEditEngine->GetParaAttrib( nPara, EE_PARA_NUMBULLET );
+ if ( rNumBullet.GetNumRule().GetLevelCount() > nDepth )
+ pFmt = rNumBullet.GetNumRule().Get( nDepth );
+ }
+
+ return pFmt;
+}
+
+Size Outliner::ImplGetBulletSize( sal_Int32 nPara )
+{
+ Paragraph* pPara = pParaList->GetParagraph( nPara );
+ if (!pPara)
+ return Size();
+
+ if( pPara->aBulSize.Width() == -1 )
+ {
+ const SvxNumberFormat* pFmt = GetNumberFormat( nPara );
+ DBG_ASSERT( pFmt, "ImplGetBulletSize - no Bullet!" );
+
+ if ( pFmt->GetNumberingType() == SVX_NUM_NUMBER_NONE )
+ {
+ pPara->aBulSize = Size( 0, 0 );
+ }
+ else if( pFmt->GetNumberingType() != SVX_NUM_BITMAP )
+ {
+ OUString aBulletText = ImplGetBulletText( nPara );
+ OutputDevice* pRefDev = pEditEngine->GetRefDevice();
+ vcl::Font aBulletFont( ImpCalcBulletFont( nPara ) );
+ vcl::Font aRefFont( pRefDev->GetFont());
+ pRefDev->SetFont( aBulletFont );
+ pPara->aBulSize.setWidth( pRefDev->GetTextWidth( aBulletText ) );
+ pPara->aBulSize.setHeight( pRefDev->GetTextHeight() );
+ pRefDev->SetFont( aRefFont );
+ }
+ else
+ {
+ pPara->aBulSize = OutputDevice::LogicToLogic(pFmt->GetGraphicSize(),
+ MapMode(MapUnit::Map100thMM),
+ pEditEngine->GetRefDevice()->GetMapMode());
+ }
+ }
+
+ return pPara->aBulSize;
+}
+
+void Outliner::ImplCheckParagraphs( sal_Int32 nStart, sal_Int32 nEnd )
+{
+
+ for ( sal_Int32 n = nStart; n < nEnd; n++ )
+ {
+ Paragraph* pPara = pParaList->GetParagraph( n );
+ if (pPara)
+ {
+ pPara->Invalidate();
+ ImplCalcBulletText( n, false, false );
+ }
+ }
+}
+
+void Outliner::SetRefDevice( OutputDevice* pRefDev )
+{
+ pEditEngine->SetRefDevice( pRefDev );
+ for ( sal_Int32 n = pParaList->GetParagraphCount(); n; )
+ {
+ Paragraph* pPara = pParaList->GetParagraph( --n );
+ pPara->Invalidate();
+ }
+}
+
+void Outliner::ParaAttribsChanged( sal_Int32 nPara )
+{
+ // The Outliner does not have an undo of its own, when paragraphs are
+ // separated/merged. When ParagraphInserted the attribute EE_PARA_OUTLLEVEL
+ // may not be set, this is however needed when the depth of the paragraph
+ // is to be determined.
+ if (!pEditEngine->IsInUndo())
+ return;
+ if (pParaList->GetParagraphCount() != pEditEngine->GetParagraphCount())
+ return;
+ Paragraph* pPara = pParaList->GetParagraph(nPara);
+ if (!pPara)
+ return;
+ // tdf#100734: force update of bullet
+ pPara->Invalidate();
+ const SfxInt16Item& rLevel = pEditEngine->GetParaAttrib( nPara, EE_PARA_OUTLLEVEL );
+ if (pPara->GetDepth() == rLevel.GetValue())
+ return;
+ pPara->SetDepth(rLevel.GetValue());
+ ImplCalcBulletText(nPara, true, true);
+}
+
+void Outliner::StyleSheetChanged( SfxStyleSheet const * pStyle )
+{
+
+ // The EditEngine calls StyleSheetChanged also for derived styles.
+ // Here all the paragraphs, which had the said template, used to be
+ // hunted by an ImpRecalcParaAttribs, why?
+ // => only the Bullet-representation can really change...
+ sal_Int32 nParas = pParaList->GetParagraphCount();
+ for( sal_Int32 nPara = 0; nPara < nParas; nPara++ )
+ {
+ if ( pEditEngine->GetStyleSheet( nPara ) == pStyle )
+ {
+ ImplCheckNumBulletItem( nPara );
+ ImplCalcBulletText( nPara, false, false );
+ // EditEngine formats changed paragraphs before calling this method,
+ // so they are not reformatted now and use wrong bullet indent
+ pEditEngine->QuickMarkInvalid( ESelection( nPara, 0, nPara, 0 ) );
+ }
+ }
+}
+
+tools::Rectangle Outliner::ImpCalcBulletArea( sal_Int32 nPara, bool bAdjust, bool bReturnPaperPos )
+{
+ // Bullet area within the paragraph ...
+ tools::Rectangle aBulletArea;
+
+ const SvxNumberFormat* pFmt = GetNumberFormat( nPara );
+ if ( pFmt )
+ {
+ Point aTopLeft;
+ Size aBulletSize( ImplGetBulletSize( nPara ) );
+
+ bool bOutlineMode = bool( pEditEngine->GetControlWord() & EEControlBits::OUTLINER );
+
+ // the ODF attribute text:space-before which holds the spacing to add to the left of the label
+ const auto nSpaceBefore = pFmt->GetAbsLSpace() + pFmt->GetFirstLineOffset();
+
+ const SvxLRSpaceItem& rLR = pEditEngine->GetParaAttrib( nPara, bOutlineMode ? EE_PARA_OUTLLRSPACE : EE_PARA_LRSPACE );
+ aTopLeft.setX( rLR.GetTextLeft() + rLR.GetTextFirstLineOffset() + nSpaceBefore );
+
+ tools::Long nBulletWidth = std::max( static_cast<tools::Long>(-rLR.GetTextFirstLineOffset()), static_cast<tools::Long>((-pFmt->GetFirstLineOffset()) + pFmt->GetCharTextDistance()) );
+ if ( nBulletWidth < aBulletSize.Width() ) // The Bullet creates its space
+ nBulletWidth = aBulletSize.Width();
+
+ if ( bAdjust && !bOutlineMode )
+ {
+ // Adjust when centered or align right
+ const SvxAdjustItem& rItem = pEditEngine->GetParaAttrib( nPara, EE_PARA_JUST );
+ if ( ( !pEditEngine->IsRightToLeft( nPara ) && ( rItem.GetAdjust() != SvxAdjust::Left ) ) ||
+ ( pEditEngine->IsRightToLeft( nPara ) && ( rItem.GetAdjust() != SvxAdjust::Right ) ) )
+ {
+ aTopLeft.setX( pEditEngine->GetFirstLineStartX( nPara ) - nBulletWidth );
+ }
+ }
+
+ // Vertical:
+ ParagraphInfos aInfos = pEditEngine->GetParagraphInfos( nPara );
+ if ( aInfos.bValid )
+ {
+ aTopLeft.setY( /* aInfos.nFirstLineOffset + */ // nFirstLineOffset is already added to the StartPos (PaintBullet) from the EditEngine
+ aInfos.nFirstLineHeight - aInfos.nFirstLineTextHeight
+ + aInfos.nFirstLineTextHeight / 2
+ - aBulletSize.Height() / 2 );
+ // may prefer to print out on the baseline ...
+ if( ( pFmt->GetNumberingType() != SVX_NUM_NUMBER_NONE ) && ( pFmt->GetNumberingType() != SVX_NUM_BITMAP ) && ( pFmt->GetNumberingType() != SVX_NUM_CHAR_SPECIAL ) )
+ {
+ vcl::Font aBulletFont( ImpCalcBulletFont( nPara ) );
+ if ( aBulletFont.GetCharSet() != RTL_TEXTENCODING_SYMBOL )
+ {
+ OutputDevice* pRefDev = pEditEngine->GetRefDevice();
+ vcl::Font aOldFont = pRefDev->GetFont();
+ pRefDev->SetFont( aBulletFont );
+ FontMetric aMetric( pRefDev->GetFontMetric() );
+ // Leading on the first line ...
+ aTopLeft.setY( /* aInfos.nFirstLineOffset + */ aInfos.nFirstLineMaxAscent );
+ aTopLeft.AdjustY( -(aMetric.GetAscent()) );
+ pRefDev->SetFont( aOldFont );
+ }
+ }
+ }
+
+ // Horizontal:
+ if( pFmt->GetNumAdjust() == SvxAdjust::Right )
+ {
+ aTopLeft.AdjustX(nBulletWidth - aBulletSize.Width() );
+ }
+ else if( pFmt->GetNumAdjust() == SvxAdjust::Center )
+ {
+ aTopLeft.AdjustX(( nBulletWidth - aBulletSize.Width() ) / 2 );
+ }
+
+ if ( aTopLeft.X() < 0 ) // then push
+ aTopLeft.setX( 0 );
+
+ aBulletArea = tools::Rectangle( aTopLeft, aBulletSize );
+ }
+ if ( bReturnPaperPos )
+ {
+ Size aBulletSize( aBulletArea.GetSize() );
+ Point aBulletDocPos( aBulletArea.TopLeft() );
+ aBulletDocPos.AdjustY(pEditEngine->GetDocPosTopLeft( nPara ).Y() );
+ Point aBulletPos( aBulletDocPos );
+
+ if ( IsVertical() )
+ {
+ aBulletPos.setY( aBulletDocPos.X() );
+ aBulletPos.setX( GetPaperSize().Width() - aBulletDocPos.Y() );
+ // Rotate:
+ aBulletPos.AdjustX( -(aBulletSize.Height()) );
+ Size aSz( aBulletSize );
+ aBulletSize.setWidth( aSz.Height() );
+ aBulletSize.setHeight( aSz.Width() );
+ }
+ else if ( pEditEngine->IsRightToLeft( nPara ) )
+ {
+ aBulletPos.setX( GetPaperSize().Width() - aBulletDocPos.X() - aBulletSize.Width() );
+ }
+
+ aBulletArea = tools::Rectangle( aBulletPos, aBulletSize );
+ }
+ return aBulletArea;
+}
+
+EBulletInfo Outliner::GetBulletInfo( sal_Int32 nPara )
+{
+ EBulletInfo aInfo;
+
+ aInfo.nParagraph = nPara;
+ aInfo.bVisible = ImplHasNumberFormat( nPara );
+
+ const SvxNumberFormat* pFmt = GetNumberFormat( nPara );
+ aInfo.nType = pFmt ? pFmt->GetNumberingType() : 0;
+
+ if( pFmt )
+ {
+ if( pFmt->GetNumberingType() != SVX_NUM_BITMAP )
+ {
+ aInfo.aText = ImplGetBulletText( nPara );
+
+ if( pFmt->GetBulletFont() )
+ aInfo.aFont = *pFmt->GetBulletFont();
+ }
+ }
+
+ if ( aInfo.bVisible )
+ {
+ aInfo.aBounds = ImpCalcBulletArea( nPara, true, true );
+ }
+
+ return aInfo;
+}
+
+OUString Outliner::GetText( Paragraph const * pParagraph, sal_Int32 nCount ) const
+{
+
+ OUStringBuffer aText(128);
+ sal_Int32 nStartPara = pParaList->GetAbsPos( pParagraph );
+ for ( sal_Int32 n = 0; n < nCount; n++ )
+ {
+ aText.append(pEditEngine->GetText( nStartPara + n ));
+ if ( (n+1) < nCount )
+ aText.append("\n");
+ }
+ return aText.makeStringAndClear();
+}
+
+void Outliner::Remove( Paragraph const * pPara, sal_Int32 nParaCount )
+{
+
+ sal_Int32 nPos = pParaList->GetAbsPos( pPara );
+ if( !nPos && ( nParaCount >= pParaList->GetParagraphCount() ) )
+ {
+ Clear();
+ }
+ else
+ {
+ for( sal_Int32 n = 0; n < nParaCount; n++ )
+ pEditEngine->RemoveParagraph( nPos );
+ }
+}
+
+void Outliner::StripPortions()
+{
+ bStrippingPortions = true;
+ pEditEngine->StripPortions();
+ bStrippingPortions = false;
+}
+
+void Outliner::DrawingText( const Point& rStartPos, const OUString& rText, sal_Int32 nTextStart,
+ sal_Int32 nTextLen, std::span<const sal_Int32> pDXArray,
+ std::span<const sal_Bool> pKashidaArray, const SvxFont& rFont,
+ sal_Int32 nPara, sal_uInt8 nRightToLeft,
+ const EEngineData::WrongSpellVector* pWrongSpellVector,
+ const SvxFieldData* pFieldData,
+ bool bEndOfLine,
+ bool bEndOfParagraph,
+ bool bEndOfBullet,
+ const css::lang::Locale* pLocale,
+ const Color& rOverlineColor,
+ const Color& rTextLineColor)
+{
+ if(aDrawPortionHdl.IsSet())
+ {
+ DrawPortionInfo aInfo( rStartPos, rText, nTextStart, nTextLen, rFont, nPara, pDXArray, pKashidaArray, pWrongSpellVector,
+ pFieldData, pLocale, rOverlineColor, rTextLineColor, nRightToLeft, false, 0, bEndOfLine, bEndOfParagraph, bEndOfBullet);
+
+ aDrawPortionHdl.Call( &aInfo );
+ }
+}
+
+void Outliner::DrawingTab( const Point& rStartPos, tools::Long nWidth, const OUString& rChar, const SvxFont& rFont,
+ sal_Int32 nPara, sal_uInt8 nRightToLeft, bool bEndOfLine, bool bEndOfParagraph,
+ const Color& rOverlineColor, const Color& rTextLineColor)
+{
+ if(aDrawPortionHdl.IsSet())
+ {
+ DrawPortionInfo aInfo( rStartPos, rChar, 0, rChar.getLength(), rFont, nPara, {}, {}, nullptr,
+ nullptr, nullptr, rOverlineColor, rTextLineColor, nRightToLeft, true, nWidth, bEndOfLine, bEndOfParagraph, false);
+
+ aDrawPortionHdl.Call( &aInfo );
+ }
+}
+
+bool Outliner::RemovingPagesHdl( OutlinerView* pView )
+{
+ return !aRemovingPagesHdl.IsSet() || aRemovingPagesHdl.Call( pView );
+}
+
+bool Outliner::ImpCanDeleteSelectedPages( OutlinerView* pCurView, sal_Int32 _nFirstPage, sal_Int32 nPages )
+{
+
+ nDepthChangedHdlPrevDepth = nPages;
+ mnFirstSelPage = _nFirstPage;
+ return RemovingPagesHdl( pCurView );
+}
+
+SfxItemSet const & Outliner::GetParaAttribs( sal_Int32 nPara ) const
+{
+ return pEditEngine->GetParaAttribs( nPara );
+}
+
+IMPL_LINK( Outliner, ParaVisibleStateChangedHdl, Paragraph&, rPara, void )
+{
+ sal_Int32 nPara = pParaList->GetAbsPos( &rPara );
+ pEditEngine->ShowParagraph( nPara, rPara.IsVisible() );
+}
+
+IMPL_LINK_NOARG(Outliner, BeginMovingParagraphsHdl, MoveParagraphsInfo&, void)
+{
+ if( !IsInUndo() )
+ aBeginMovingHdl.Call( this );
+}
+
+IMPL_LINK( Outliner, BeginPasteOrDropHdl, PasteOrDropInfos&, rInfos, void )
+{
+ UndoActionStart( EDITUNDO_DRAGANDDROP );
+ maBeginPasteOrDropHdl.Call(&rInfos);
+}
+
+IMPL_LINK( Outliner, EndPasteOrDropHdl, PasteOrDropInfos&, rInfos, void )
+{
+ bPasting = false;
+ ImpTextPasted( rInfos.nStartPara, rInfos.nEndPara - rInfos.nStartPara + 1 );
+ maEndPasteOrDropHdl.Call( &rInfos );
+ UndoActionEnd();
+}
+
+IMPL_LINK( Outliner, EndMovingParagraphsHdl, MoveParagraphsInfo&, rInfos, void )
+{
+ pParaList->MoveParagraphs( rInfos.nStartPara, rInfos.nDestPara, rInfos.nEndPara - rInfos.nStartPara + 1 );
+ sal_Int32 nChangesStart = std::min( rInfos.nStartPara, rInfos.nDestPara );
+ sal_Int32 nParas = pParaList->GetParagraphCount();
+ for ( sal_Int32 n = nChangesStart; n < nParas; n++ )
+ ImplCalcBulletText( n, false, false );
+
+ if( !IsInUndo() )
+ aEndMovingHdl.Call( this );
+}
+
+static bool isSameNumbering( const SvxNumberFormat& rN1, const SvxNumberFormat& rN2 )
+{
+ if( rN1.GetNumberingType() != rN2.GetNumberingType() )
+ return false;
+
+ if( rN1.GetNumStr(1) != rN2.GetNumStr(1) )
+ return false;
+
+ if( (rN1.GetPrefix() != rN2.GetPrefix()) || (rN1.GetSuffix() != rN2.GetSuffix()) )
+ return false;
+
+ return true;
+}
+
+sal_uInt16 Outliner::ImplGetNumbering( sal_Int32 nPara, const SvxNumberFormat* pParaFmt )
+{
+ sal_uInt16 nNumber = pParaFmt->GetStart() - 1;
+
+ Paragraph* pPara = pParaList->GetParagraph( nPara );
+ const sal_Int16 nParaDepth = pPara->GetDepth();
+
+ do
+ {
+ pPara = pParaList->GetParagraph( nPara );
+ const sal_Int16 nDepth = pPara->GetDepth();
+
+ // ignore paragraphs that are below our paragraph or have no numbering
+ if( (nDepth > nParaDepth) || (nDepth == -1) )
+ continue;
+
+ // stop on paragraphs that are above our paragraph
+ if( nDepth < nParaDepth )
+ break;
+
+ const SvxNumberFormat* pFmt = GetNumberFormat( nPara );
+
+ if( pFmt == nullptr )
+ continue; // ignore paragraphs without bullets
+
+ // check if numbering less than or equal to pParaFmt
+ if( !isSameNumbering( *pFmt, *pParaFmt ) || ( pFmt->GetStart() < pParaFmt->GetStart() ) )
+ break;
+
+ if ( pFmt->GetStart() > pParaFmt->GetStart() )
+ {
+ nNumber += pFmt->GetStart() - pParaFmt->GetStart();
+ pParaFmt = pFmt;
+ }
+
+ const SfxBoolItem& rBulletState = pEditEngine->GetParaAttrib( nPara, EE_PARA_BULLETSTATE );
+
+ if( rBulletState.GetValue() )
+ nNumber += 1;
+
+ // same depth, same number format, check for restart
+ const sal_Int16 nNumberingStartValue = pPara->GetNumberingStartValue();
+ if( (nNumberingStartValue != -1) || pPara->IsParaIsNumberingRestart() )
+ {
+ if( nNumberingStartValue != -1 )
+ nNumber += nNumberingStartValue - 1;
+ break;
+ }
+ }
+ while( nPara-- );
+
+ return nNumber;
+}
+
+void Outliner::ImplCalcBulletText( sal_Int32 nPara, bool bRecalcLevel, bool bRecalcChildren )
+{
+
+ Paragraph* pPara = pParaList->GetParagraph( nPara );
+
+ while ( pPara )
+ {
+ OUString aBulletText;
+ const SvxNumberFormat* pFmt = GetNumberFormat( nPara );
+ if( pFmt && ( pFmt->GetNumberingType() != SVX_NUM_BITMAP ) )
+ {
+ aBulletText += pFmt->GetPrefix();
+ if( pFmt->GetNumberingType() == SVX_NUM_CHAR_SPECIAL )
+ {
+ sal_UCS4 cChar = pFmt->GetBulletChar();
+ aBulletText += OUString(&cChar, 1);
+ }
+ else if( pFmt->GetNumberingType() != SVX_NUM_NUMBER_NONE )
+ {
+ aBulletText += pFmt->GetNumStr( ImplGetNumbering( nPara, pFmt ) );
+ }
+ aBulletText += pFmt->GetSuffix();
+ }
+
+ if (pPara->GetText() != aBulletText)
+ pPara->SetText( aBulletText );
+
+ if ( bRecalcLevel )
+ {
+ sal_Int16 nDepth = pPara->GetDepth();
+ pPara = pParaList->GetParagraph( ++nPara );
+ if ( !bRecalcChildren )
+ {
+ while ( pPara && ( pPara->GetDepth() > nDepth ) )
+ pPara = pParaList->GetParagraph( ++nPara );
+ }
+
+ if ( pPara && ( pPara->GetDepth() < nDepth ) )
+ pPara = nullptr;
+ }
+ else
+ {
+ pPara = nullptr;
+ }
+ }
+}
+
+void Outliner::Clear()
+{
+
+ if( !bFirstParaIsEmpty )
+ {
+ ImplBlockInsertionCallbacks( true );
+ pEditEngine->Clear();
+ pParaList->Clear();
+ pParaList->Append( std::unique_ptr<Paragraph>(new Paragraph( gnMinDepth )));
+ bFirstParaIsEmpty = true;
+ ImplBlockInsertionCallbacks( false );
+ }
+ else
+ {
+ Paragraph* pPara = pParaList->GetParagraph( 0 );
+ if(pPara)
+ pPara->SetDepth( gnMinDepth );
+ }
+}
+
+void Outliner::SetFlatMode( bool bFlat )
+{
+
+ if( bFlat != pEditEngine->IsFlatMode() )
+ {
+ for ( sal_Int32 nPara = pParaList->GetParagraphCount(); nPara; )
+ pParaList->GetParagraph( --nPara )->aBulSize.setWidth( -1 );
+
+ pEditEngine->SetFlatMode( bFlat );
+ }
+}
+
+OUString Outliner::ImplGetBulletText( sal_Int32 nPara )
+{
+ OUString aRes;
+ Paragraph* pPara = pParaList->GetParagraph( nPara );
+ if (pPara)
+ {
+ ImplCalcBulletText( nPara, false, false );
+ aRes = pPara->GetText();
+ }
+ return aRes;
+}
+
+// this is needed for StarOffice Api
+void Outliner::SetLevelDependentStyleSheet( sal_Int32 nPara )
+{
+ SfxItemSet aOldAttrs( pEditEngine->GetParaAttribs( nPara ) );
+ ImplSetLevelDependentStyleSheet( nPara );
+ pEditEngine->SetParaAttribs( nPara, aOldAttrs );
+}
+
+void Outliner::ImplBlockInsertionCallbacks( bool b )
+{
+ if ( b )
+ {
+ nBlockInsCallback++;
+ }
+ else
+ {
+ DBG_ASSERT( nBlockInsCallback, "ImplBlockInsertionCallbacks ?!" );
+ nBlockInsCallback--;
+ if ( !nBlockInsCallback )
+ {
+ // Call blocked notify events...
+ while(!pEditEngine->aNotifyCache.empty())
+ {
+ EENotify aNotify(pEditEngine->aNotifyCache.front());
+ // Remove from list before calling, maybe we enter LeaveBlockNotifications while calling the handler...
+ pEditEngine->aNotifyCache.erase(pEditEngine->aNotifyCache.begin());
+ pEditEngine->aOutlinerNotifyHdl.Call( aNotify );
+ }
+ }
+ }
+}
+
+IMPL_LINK( Outliner, EditEngineNotifyHdl, EENotify&, rNotify, void )
+{
+ if ( !nBlockInsCallback )
+ pEditEngine->aOutlinerNotifyHdl.Call( rNotify );
+ else
+ pEditEngine->aNotifyCache.push_back(rNotify);
+}
+
+/** sets a link that is called at the beginning of a drag operation at an edit view */
+void Outliner::SetBeginDropHdl( const Link<EditView*,void>& rLink )
+{
+ pEditEngine->SetBeginDropHdl( rLink );
+}
+
+/** sets a link that is called at the end of a drag operation at an edit view */
+void Outliner::SetEndDropHdl( const Link<EditView*,void>& rLink )
+{
+ pEditEngine->SetEndDropHdl( rLink );
+}
+
+/** sets a link that is called before a drop or paste operation. */
+void Outliner::SetBeginPasteOrDropHdl( const Link<PasteOrDropInfos*,void>& rLink )
+{
+ maBeginPasteOrDropHdl = rLink;
+}
+
+/** sets a link that is called after a drop or paste operation. */
+void Outliner::SetEndPasteOrDropHdl( const Link<PasteOrDropInfos*,void>& rLink )
+{
+ maEndPasteOrDropHdl = rLink;
+}
+
+void Outliner::SetParaFlag( Paragraph* pPara, ParaFlag nFlag )
+{
+ if( pPara && !pPara->HasFlag( nFlag ) )
+ {
+ if( IsUndoEnabled() && !IsInUndo() )
+ InsertUndo( std::make_unique<OutlinerUndoChangeParaFlags>( this, GetAbsPos( pPara ), pPara->nFlags, pPara->nFlags|nFlag ) );
+
+ pPara->SetFlag( nFlag );
+ }
+}
+
+bool Outliner::HasParaFlag( const Paragraph* pPara, ParaFlag nFlag )
+{
+ return pPara && pPara->HasFlag( nFlag );
+}
+
+
+bool Outliner::IsPageOverflow()
+{
+ return pEditEngine->IsPageOverflow();
+}
+
+std::optional<NonOverflowingText> Outliner::GetNonOverflowingText() const
+{
+ /* XXX:
+ * nCount should be the number of paragraphs of the non overflowing text
+ * nStart should be the starting paragraph of the non overflowing text (XXX: Always 0?)
+ */
+
+ if ( GetParagraphCount() < 1 )
+ return {};
+
+ // last non-overflowing paragraph is before the first overflowing one
+ sal_Int32 nCount = pEditEngine->GetOverflowingParaNum();
+ sal_Int32 nOverflowLine = pEditEngine->GetOverflowingLineNum(); // XXX: Unused for now
+
+ // Defensive check: overflowing para index beyond actual # of paragraphs?
+ if ( nCount > GetParagraphCount()-1) {
+ SAL_INFO("editeng.chaining",
+ "[Overflowing] Ops, trying to retrieve para "
+ << nCount << " when max index is " << GetParagraphCount()-1 );
+ return {};
+ }
+
+ if (nCount < 0)
+ {
+ SAL_INFO("editeng.chaining",
+ "[Overflowing] No Overflowing text but GetNonOverflowinText called?!");
+ return {};
+ }
+
+ // NOTE: We want the selection of the overflowing text from here
+ // At the same time we may want to consider the beginning of such text
+ // in a more fine grained way (i.e. as GetNonOverflowingText did)
+
+/*
+ sal_Int32 nHeadPara = pEditEngine->GetOverflowingParaNum();
+ sal_uInt32 nParaCount = GetParagraphCount();
+
+ sal_uInt32 nLen = 0;
+ for ( sal_Int32 nLine = 0;
+ nLine < pEditEngine->GetOverflowingLineNum();
+ nLine++) {
+ nLen += GetLineLen(nHeadPara, nLine);
+ }
+
+ sal_uInt32 nOverflowingPara = pEditEngine->GetOverflowingParaNum();
+ ESelection aOverflowingTextSel;
+ sal_Int32 nLastPara = nParaCount-1;
+ sal_Int32 nLastParaLen = GetText(GetParagraph(nLastPara)).getLength();
+ aOverflowingTextSel = ESelection(nOverflowingPara, nLen,
+ nLastPara, nLastParaLen);
+ bool bLastParaInterrupted =
+ pEditEngine->GetOverflowingLineNum() > 0;
+
+ return new NonOverflowingText(aOverflowingTextSel, bLastParaInterrupted);
+ **/
+
+
+ // Only overflowing text, i.e. 1st line of 1st paragraph overflowing
+ bool bItAllOverflew = nCount == 0 && nOverflowLine == 0;
+ if ( bItAllOverflew )
+ {
+ ESelection aEmptySel(0,0,0,0);
+ //EditTextObject *pTObj = pEditEngine->CreateTextObject(aEmptySel);
+ bool const bLastParaInterrupted = true; // Last Para was interrupted since everything overflew
+ return NonOverflowingText(aEmptySel, bLastParaInterrupted);
+ } else { // Get the lines that of the overflowing para fit in the box
+
+ sal_Int32 nOverflowingPara = nCount;
+ sal_uInt32 nLen = 0;
+
+ for ( sal_Int32 nLine = 0;
+ nLine < pEditEngine->GetOverflowingLineNum();
+ nLine++)
+ {
+ nLen += GetLineLen(nOverflowingPara, nLine);
+ }
+
+ //sal_Int32 nStartPara = 0;
+ //sal_Int32 nStartPos = 0;
+ ESelection aOverflowingTextSelection;
+
+ const sal_Int32 nEndPara = GetParagraphCount()-1;
+ const sal_Int32 nEndPos = pEditEngine->GetTextLen(nEndPara);
+
+ if (nLen == 0) {
+ // XXX: What happens inside this case might be dependent on the joining paragraph or not-thingy
+ // Overflowing paragraph is empty or first line overflowing: it's not "Non-Overflowing" text then
+ sal_Int32 nParaLen = GetText(GetParagraph(nOverflowingPara-1)).getLength();
+ aOverflowingTextSelection =
+ ESelection(nOverflowingPara-1, nParaLen, nEndPara, nEndPos);
+ } else {
+ // We take until we have to from the overflowing paragraph
+ aOverflowingTextSelection =
+ ESelection(nOverflowingPara, nLen, nEndPara, nEndPos);
+ }
+ //EditTextObject *pTObj = pEditEngine->CreateTextObject(aNonOverflowingTextSelection);
+
+ //sal_Int32 nLastLine = GetLineCount(nOverflowingPara)-1;
+ bool bLastParaInterrupted =
+ pEditEngine->GetOverflowingLineNum() > 0;
+
+ return NonOverflowingText(aOverflowingTextSelection, bLastParaInterrupted);
+ }
+}
+
+OutlinerParaObject Outliner::GetEmptyParaObject() const
+{
+ std::unique_ptr<EditTextObject> pEmptyText = pEditEngine->GetEmptyTextObject();
+ OutlinerParaObject aPObj( std::move(pEmptyText) );
+ aPObj.SetOutlinerMode(GetOutlinerMode());
+ return aPObj;
+}
+
+std::optional<OverflowingText> Outliner::GetOverflowingText() const
+{
+ if ( pEditEngine->GetOverflowingParaNum() < 0)
+ return {};
+
+
+ // Defensive check: overflowing para index beyond actual # of paragraphs?
+ if ( pEditEngine->GetOverflowingParaNum() > GetParagraphCount()-1) {
+ SAL_INFO("editeng.chaining",
+ "[Overflowing] Ops, trying to retrieve para "
+ << pEditEngine->GetOverflowingParaNum() << " when max index is "
+ << GetParagraphCount()-1 );
+ return {};
+ }
+
+ sal_Int32 nHeadPara = pEditEngine->GetOverflowingParaNum();
+ sal_uInt32 nParaCount = GetParagraphCount();
+
+ sal_uInt32 nLen = 0;
+ for ( sal_Int32 nLine = 0;
+ nLine < pEditEngine->GetOverflowingLineNum();
+ nLine++) {
+ nLen += GetLineLen(nHeadPara, nLine);
+ }
+
+ sal_uInt32 nOverflowingPara = pEditEngine->GetOverflowingParaNum();
+ ESelection aOverflowingTextSel;
+ sal_Int32 nLastPara = nParaCount-1;
+ sal_Int32 nLastParaLen = GetText(GetParagraph(nLastPara)).getLength();
+ aOverflowingTextSel = ESelection(nOverflowingPara, nLen,
+ nLastPara, nLastParaLen);
+ return OverflowingText(pEditEngine->CreateTransferable(aOverflowingTextSel));
+
+}
+
+void Outliner::ClearOverflowingParaNum()
+{
+ pEditEngine->ClearOverflowingParaNum();
+}
+
+void Outliner::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ bool bOwns = false;
+ if (!pWriter)
+ {
+ pWriter = xmlNewTextWriterFilename("outliner.xml", 0);
+ xmlTextWriterSetIndent(pWriter,1);
+ (void)xmlTextWriterSetIndentString(pWriter, BAD_CAST(" "));
+ (void)xmlTextWriterStartDocument(pWriter, nullptr, nullptr, nullptr);
+ bOwns = true;
+ }
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("Outliner"));
+ pParaList->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+
+ if (bOwns)
+ {
+ (void)xmlTextWriterEndDocument(pWriter);
+ xmlFreeTextWriter(pWriter);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/outliner/outlobj.cxx b/editeng/source/outliner/outlobj.cxx
new file mode 100644
index 0000000000..e6dc6e691c
--- /dev/null
+++ b/editeng/source/outliner/outlobj.cxx
@@ -0,0 +1,254 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <editeng/editdata.hxx>
+
+#include <editeng/outliner.hxx>
+#include <editeng/outlobj.hxx>
+#include <editeng/editobj.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <o3tl/cow_wrapper.hxx>
+#include <o3tl/safeint.hxx>
+#include <libxml/xmlwriter.h>
+
+OutlinerParaObjData::OutlinerParaObjData( std::unique_ptr<EditTextObject> pEditTextObject, ParagraphDataVector&& rParagraphDataVector, bool bIsEditDoc ) :
+ mpEditTextObject(std::move(pEditTextObject)),
+ maParagraphDataVector(std::move(rParagraphDataVector)),
+ mbIsEditDoc(bIsEditDoc)
+{
+ if( maParagraphDataVector.empty() && (mpEditTextObject->GetParagraphCount() != 0) )
+ maParagraphDataVector.resize(mpEditTextObject->GetParagraphCount());
+}
+
+OutlinerParaObjData::OutlinerParaObjData( const OutlinerParaObjData& r ):
+ mpEditTextObject(r.mpEditTextObject->Clone()),
+ maParagraphDataVector(r.maParagraphDataVector),
+ mbIsEditDoc(r.mbIsEditDoc)
+{
+}
+
+OutlinerParaObjData::~OutlinerParaObjData()
+{
+}
+
+bool OutlinerParaObjData::operator==(const OutlinerParaObjData& rCandidate) const
+{
+ return (*mpEditTextObject == *rCandidate.mpEditTextObject
+ && maParagraphDataVector == rCandidate.maParagraphDataVector
+ && mbIsEditDoc == rCandidate.mbIsEditDoc);
+}
+
+bool OutlinerParaObjData::isWrongListEqual(const OutlinerParaObjData& rCompare) const
+{
+ return mpEditTextObject->isWrongListEqual(*rCompare.mpEditTextObject);
+}
+
+OutlinerParaObject::OutlinerParaObject(
+ std::unique_ptr<EditTextObject> xTextObj, ParagraphDataVector&& rParagraphDataVector, bool bIsEditDoc ) :
+ mpImpl(OutlinerParaObjData(std::move(xTextObj), std::move(rParagraphDataVector), bIsEditDoc))
+{
+}
+
+OutlinerParaObject::OutlinerParaObject( std::unique_ptr<EditTextObject> pTextObj ) :
+ mpImpl(OutlinerParaObjData(std::move(pTextObj), ParagraphDataVector(), true))
+{
+}
+
+OutlinerParaObject::OutlinerParaObject( const OutlinerParaObject& r ) :
+ mpImpl(r.mpImpl)
+{
+}
+
+OutlinerParaObject::OutlinerParaObject( OutlinerParaObject&& r ) noexcept :
+ mpImpl(std::move(r.mpImpl))
+{
+}
+
+OutlinerParaObject::~OutlinerParaObject()
+{
+}
+
+OutlinerParaObject& OutlinerParaObject::operator=( const OutlinerParaObject& r )
+{
+ mpImpl = r.mpImpl;
+ return *this;
+}
+
+OutlinerParaObject& OutlinerParaObject::operator=( OutlinerParaObject&& r ) noexcept
+{
+ mpImpl = std::move(r.mpImpl);
+ return *this;
+}
+
+bool OutlinerParaObject::operator==( const OutlinerParaObject& r ) const
+{
+ return r.mpImpl == mpImpl;
+}
+
+// #i102062#
+bool OutlinerParaObject::isWrongListEqual( const OutlinerParaObject& r ) const
+{
+ if (r.mpImpl.same_object(mpImpl))
+ {
+ return true;
+ }
+
+ return mpImpl->isWrongListEqual(*r.mpImpl);
+}
+
+OutlinerMode OutlinerParaObject::GetOutlinerMode() const
+{
+ return mpImpl->mpEditTextObject->GetUserType();
+}
+
+void OutlinerParaObject::SetOutlinerMode(OutlinerMode nNew)
+{
+ // create a const pointer to avoid an early call to
+ // make_unique() in the dereference of mpImpl
+ const ::o3tl::cow_wrapper< OutlinerParaObjData >* pImpl = &mpImpl;
+ if ( ( *pImpl )->mpEditTextObject->GetUserType() != nNew )
+ {
+ mpImpl->mpEditTextObject->SetUserType(nNew);
+ }
+}
+
+bool OutlinerParaObject::IsEffectivelyVertical() const
+{
+ return mpImpl->mpEditTextObject->IsEffectivelyVertical();
+}
+
+bool OutlinerParaObject::GetVertical() const
+{
+ return mpImpl->mpEditTextObject->GetVertical();
+}
+
+bool OutlinerParaObject::IsTopToBottom() const
+{
+ return mpImpl->mpEditTextObject->IsTopToBottom();
+}
+
+void OutlinerParaObject::SetVertical(bool bNew)
+{
+ const ::o3tl::cow_wrapper< OutlinerParaObjData >* pImpl = &mpImpl;
+ if ( ( *pImpl )->mpEditTextObject->IsEffectivelyVertical() != bNew)
+ {
+ mpImpl->mpEditTextObject->SetVertical(bNew);
+ }
+}
+void OutlinerParaObject::SetRotation(TextRotation nRotation)
+{
+ mpImpl->mpEditTextObject->SetRotation(nRotation);
+}
+
+TextRotation OutlinerParaObject::GetRotation() const
+{
+ return mpImpl->mpEditTextObject->GetRotation();
+}
+
+sal_Int32 OutlinerParaObject::Count() const
+{
+ size_t nSize = mpImpl->maParagraphDataVector.size();
+ if (nSize > EE_PARA_MAX_COUNT)
+ {
+ SAL_WARN( "editeng", "OutlinerParaObject::Count - overflow " << nSize);
+ return EE_PARA_MAX_COUNT;
+ }
+ return static_cast<sal_Int32>(nSize);
+}
+
+sal_Int16 OutlinerParaObject::GetDepth(sal_Int32 nPara) const
+{
+ if(0 <= nPara && o3tl::make_unsigned(nPara) < mpImpl->maParagraphDataVector.size())
+ {
+ return mpImpl->maParagraphDataVector[nPara].getDepth();
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+const EditTextObject& OutlinerParaObject::GetTextObject() const
+{
+ return *mpImpl->mpEditTextObject;
+}
+
+const ParagraphData& OutlinerParaObject::GetParagraphData(sal_Int32 nIndex) const
+{
+ if(0 <= nIndex && o3tl::make_unsigned(nIndex) < mpImpl->maParagraphDataVector.size())
+ {
+ return mpImpl->maParagraphDataVector[nIndex];
+ }
+ else
+ {
+ OSL_FAIL("OutlinerParaObject::GetParagraphData: Access out of range (!)");
+ static ParagraphData aEmptyParagraphData;
+ return aEmptyParagraphData;
+ }
+}
+
+void OutlinerParaObject::ClearPortionInfo()
+{
+ mpImpl->mpEditTextObject->ClearPortionInfo();
+}
+
+bool OutlinerParaObject::ChangeStyleSheets(std::u16string_view rOldName,
+ SfxStyleFamily eOldFamily, const OUString& rNewName, SfxStyleFamily eNewFamily)
+{
+ return mpImpl->mpEditTextObject->ChangeStyleSheets(rOldName, eOldFamily, rNewName, eNewFamily);
+}
+
+void OutlinerParaObject::ChangeStyleSheetName(SfxStyleFamily eFamily,
+ std::u16string_view rOldName, const OUString& rNewName)
+{
+ mpImpl->mpEditTextObject->ChangeStyleSheetName(eFamily, rOldName, rNewName);
+}
+
+void OutlinerParaObject::SetStyleSheets(sal_uInt16 nLevel, const OUString& rNewName,
+ const SfxStyleFamily& rNewFamily)
+{
+ const sal_Int32 nCount(Count());
+
+ if(nCount)
+ {
+ sal_Int32 nDecrementer(nCount);
+
+ while(nDecrementer > 0)
+ {
+ if(GetDepth(--nDecrementer) == nLevel)
+ {
+ mpImpl->mpEditTextObject->SetStyleSheet(nDecrementer, rNewName, rNewFamily);
+ }
+ }
+ }
+}
+
+void OutlinerParaObject::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("OutlinerParaObject"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ mpImpl->mpEditTextObject->dumpAsXml(pWriter);
+ for (ParagraphData const & p : mpImpl->maParagraphDataVector)
+ Paragraph(p).dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/outliner/outlundo.cxx b/editeng/source/outliner/outlundo.cxx
new file mode 100644
index 0000000000..c2db1a77f3
--- /dev/null
+++ b/editeng/source/outliner/outlundo.cxx
@@ -0,0 +1,164 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <editeng/outliner.hxx>
+#include <tools/debug.hxx>
+#include "outlundo.hxx"
+
+
+OutlinerUndoBase::OutlinerUndoBase( sal_uInt16 _nId, Outliner* pOutliner )
+ : EditUndo( _nId, nullptr )
+{
+ DBG_ASSERT( pOutliner, "Undo: Outliner?!" );
+ mpOutliner = pOutliner;
+}
+
+OutlinerUndoChangeParaFlags::OutlinerUndoChangeParaFlags( Outliner* pOutliner, sal_Int32 nPara, ParaFlag nOldFlags, ParaFlag nNewFlags )
+: OutlinerUndoBase( OLUNDO_DEPTH, pOutliner ), mnPara(nPara), mnOldFlags(nOldFlags), mnNewFlags(nNewFlags)
+{
+}
+
+void OutlinerUndoChangeParaFlags::Undo()
+{
+ ImplChangeFlags( mnOldFlags );
+}
+
+void OutlinerUndoChangeParaFlags::Redo()
+{
+ ImplChangeFlags( mnNewFlags );
+}
+
+void OutlinerUndoChangeParaFlags::ImplChangeFlags( ParaFlag nFlags )
+{
+ Outliner* pOutliner = GetOutliner();
+ Paragraph* pPara = pOutliner->GetParagraph( mnPara );
+ if( pPara )
+ {
+ pOutliner->nDepthChangedHdlPrevDepth = pPara->GetDepth();
+ ParaFlag nPrevFlags = pPara->nFlags;
+
+ pPara->nFlags = nFlags;
+ pOutliner->DepthChangedHdl(pPara, nPrevFlags);
+ }
+}
+
+OutlinerUndoChangeParaNumberingRestart::OutlinerUndoChangeParaNumberingRestart( Outliner* pOutliner, sal_Int32 nPara,
+ sal_Int16 nOldNumberingStartValue, sal_Int16 nNewNumberingStartValue,
+ bool bOldParaIsNumberingRestart, bool bNewParaIsNumberingRestart )
+: OutlinerUndoBase( OLUNDO_DEPTH, pOutliner ), mnPara(nPara)
+{
+ maUndoData.mnNumberingStartValue = nOldNumberingStartValue;
+ maUndoData.mbParaIsNumberingRestart = bOldParaIsNumberingRestart;
+ maRedoData.mnNumberingStartValue = nNewNumberingStartValue;
+ maRedoData.mbParaIsNumberingRestart = bNewParaIsNumberingRestart;
+}
+
+void OutlinerUndoChangeParaNumberingRestart::Undo()
+{
+ ImplApplyData( maUndoData );
+}
+
+void OutlinerUndoChangeParaNumberingRestart::Redo()
+{
+ ImplApplyData( maRedoData );
+}
+
+void OutlinerUndoChangeParaNumberingRestart::ImplApplyData( const ParaRestartData& rData )
+{
+ Outliner* pOutliner = GetOutliner();
+ pOutliner->SetNumberingStartValue( mnPara, rData.mnNumberingStartValue );
+ pOutliner->SetParaIsNumberingRestart( mnPara, rData.mbParaIsNumberingRestart );
+}
+
+OutlinerUndoChangeDepth::OutlinerUndoChangeDepth( Outliner* pOutliner, sal_Int32 nPara, sal_Int16 nOldDepth, sal_Int16 nNewDepth )
+ : OutlinerUndoBase( OLUNDO_DEPTH, pOutliner ), mnPara(nPara), mnOldDepth(nOldDepth), mnNewDepth(nNewDepth)
+{
+}
+
+void OutlinerUndoChangeDepth::Undo()
+{
+ GetOutliner()->ImplInitDepth( mnPara, mnOldDepth, false );
+}
+
+void OutlinerUndoChangeDepth::Redo()
+{
+ GetOutliner()->ImplInitDepth( mnPara, mnNewDepth, false );
+}
+
+OutlinerUndoCheckPara::OutlinerUndoCheckPara( Outliner* pOutliner, sal_Int32 nPara )
+ : OutlinerUndoBase( OLUNDO_DEPTH, pOutliner ), mnPara(nPara)
+{
+}
+
+void OutlinerUndoCheckPara::Undo()
+{
+ Paragraph* pPara = GetOutliner()->GetParagraph( mnPara );
+ pPara->Invalidate();
+ GetOutliner()->ImplCalcBulletText( mnPara, false, false );
+}
+
+void OutlinerUndoCheckPara::Redo()
+{
+ Paragraph* pPara = GetOutliner()->GetParagraph( mnPara );
+ pPara->Invalidate();
+ GetOutliner()->ImplCalcBulletText( mnPara, false, false );
+}
+
+OLUndoExpand::OLUndoExpand(Outliner* pOut, sal_uInt16 _nId )
+ : EditUndo( _nId, nullptr ), pOutliner(pOut), nCount(0)
+{
+ DBG_ASSERT(pOut,"Undo:No Outliner");
+}
+
+
+OLUndoExpand::~OLUndoExpand()
+{
+}
+
+
+void OLUndoExpand::Restore( bool bUndo )
+{
+ DBG_ASSERT(pOutliner,"Undo:No Outliner");
+ DBG_ASSERT(pOutliner->pEditEngine,"Outliner already deleted");
+ Paragraph* pPara;
+
+ bool bExpand = false;
+ sal_uInt16 _nId = GetId();
+ if((_nId == OLUNDO_EXPAND && !bUndo) || (_nId == OLUNDO_COLLAPSE && bUndo))
+ bExpand = true;
+
+ pPara = pOutliner->GetParagraph( nCount );
+ if( bExpand )
+ pOutliner->Expand( pPara );
+ else
+ pOutliner->Collapse( pPara );
+}
+
+void OLUndoExpand::Undo()
+{
+ Restore( true );
+}
+
+void OLUndoExpand::Redo()
+{
+ Restore( false );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/outliner/outlundo.hxx b/editeng/source/outliner/outlundo.hxx
new file mode 100644
index 0000000000..066b415e13
--- /dev/null
+++ b/editeng/source/outliner/outlundo.hxx
@@ -0,0 +1,118 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <editeng/outliner.hxx>
+#include <editeng/editund2.hxx>
+
+class OutlinerUndoBase : public EditUndo
+{
+private:
+ Outliner* mpOutliner;
+
+public:
+ OutlinerUndoBase( sal_uInt16 nId, Outliner* pOutliner );
+
+ Outliner* GetOutliner() const { return mpOutliner; }
+};
+
+class OutlinerUndoChangeParaFlags : public OutlinerUndoBase
+{
+private:
+ sal_Int32 mnPara;
+ ParaFlag mnOldFlags;
+ ParaFlag mnNewFlags;
+
+ void ImplChangeFlags( ParaFlag nFlags );
+
+public:
+ OutlinerUndoChangeParaFlags( Outliner* pOutliner, sal_Int32 nPara, ParaFlag nOldFlags, ParaFlag nNewFlags );
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+};
+
+class OutlinerUndoChangeParaNumberingRestart : public OutlinerUndoBase
+{
+private:
+ sal_Int32 mnPara;
+
+ struct ParaRestartData
+ {
+ sal_Int16 mnNumberingStartValue;
+ bool mbParaIsNumberingRestart;
+ };
+
+ ParaRestartData maUndoData;
+ ParaRestartData maRedoData;
+
+ void ImplApplyData( const ParaRestartData& rData );
+public:
+ OutlinerUndoChangeParaNumberingRestart( Outliner* pOutliner, sal_Int32 nPara,
+ sal_Int16 nOldNumberingStartValue, sal_Int16 mnNewNumberingStartValue,
+ bool bOldbParaIsNumberingRestart, bool bNewParaIsNumberingRestart );
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+};
+
+class OutlinerUndoChangeDepth : public OutlinerUndoBase
+{
+private:
+ sal_Int32 mnPara;
+ sal_Int16 mnOldDepth;
+ sal_Int16 mnNewDepth;
+
+public:
+ OutlinerUndoChangeDepth( Outliner* pOutliner, sal_Int32 nPara, sal_Int16 nOldDepth, sal_Int16 nNewDepth );
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+};
+
+// Help-Undo: If it does not exist an OutlinerUndoAction for a certain action
+// because this is handled by the EditEngine, but for example the bullet has
+// to be recalculated.
+class OutlinerUndoCheckPara : public OutlinerUndoBase
+{
+private:
+ sal_Int32 mnPara;
+
+public:
+ OutlinerUndoCheckPara( Outliner* pOutliner, sal_Int32 nPara );
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+};
+
+class OLUndoExpand : public EditUndo
+{
+ void Restore( bool bUndo );
+public:
+ OLUndoExpand( Outliner* pOut, sal_uInt16 nId );
+ virtual ~OLUndoExpand() override;
+ virtual void Undo() override;
+ virtual void Redo() override;
+
+ Outliner* pOutliner;
+ sal_Int32 nCount;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/outliner/outlvw.cxx b/editeng/source/outliner/outlvw.cxx
new file mode 100644
index 0000000000..136ecd776c
--- /dev/null
+++ b/editeng/source/outliner/outlvw.cxx
@@ -0,0 +1,1510 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <com/sun/star/i18n/WordType.hpp>
+
+#include <svl/itempool.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/editdata.hxx>
+
+#include <svl/style.hxx>
+#include <svl/languageoptions.hxx>
+#include <i18nlangtag/languagetag.hxx>
+
+#include <editeng/outliner.hxx>
+#include <outleeng.hxx>
+#include "paralist.hxx"
+#include "outlundo.hxx"
+#include <editeng/outlobj.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/numitem.hxx>
+#include <vcl/window.hxx>
+#include <vcl/event.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <svl/itemset.hxx>
+#include <svl/eitem.hxx>
+#include <editeng/editstat.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <tools/debug.hxx>
+
+using namespace ::com::sun::star;
+
+
+OutlinerView::OutlinerView( Outliner* pOut, vcl::Window* pWin )
+{
+ pOwner = pOut;
+ pEditView.reset( new EditView( pOut->pEditEngine.get(), pWin ) );
+}
+
+OutlinerView::~OutlinerView()
+{
+}
+
+void OutlinerView::Paint( const tools::Rectangle& rRect, OutputDevice* pTargetDevice )
+{
+ // For the first Paint/KeyInput/Drop an empty Outliner is turned into
+ // an Outliner with exactly one paragraph.
+ if( pOwner->bFirstParaIsEmpty )
+ pOwner->Insert( OUString() );
+
+ pEditView->Paint( rRect, pTargetDevice );
+}
+
+bool OutlinerView::PostKeyEvent( const KeyEvent& rKEvt, vcl::Window const * pFrameWin )
+{
+ // For the first Paint/KeyInput/Drop an empty Outliner is turned into
+ // an Outliner with exactly one paragraph.
+ if( pOwner->bFirstParaIsEmpty )
+ pOwner->Insert( OUString() );
+
+ bool bKeyProcessed = false;
+ ESelection aSel( pEditView->GetSelection() );
+ bool bSelection = aSel.HasRange();
+ vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
+ KeyFuncType eFunc = aKeyCode.GetFunction();
+ sal_uInt16 nCode = aKeyCode.GetCode();
+ bool bReadOnly = IsReadOnly();
+
+ if( bSelection && ( nCode != KEY_TAB ) && EditEngine::DoesKeyChangeText( rKEvt ) )
+ {
+ if ( ImpCalcSelectedPages( false ) && !pOwner->ImpCanDeleteSelectedPages( this ) )
+ return true;
+ }
+
+ if ( eFunc != KeyFuncType::DONTKNOW )
+ {
+ switch ( eFunc )
+ {
+ case KeyFuncType::CUT:
+ {
+ if ( !bReadOnly )
+ {
+ Cut();
+ bKeyProcessed = true;
+ }
+ }
+ break;
+ case KeyFuncType::COPY:
+ {
+ Copy();
+ bKeyProcessed = true;
+ }
+ break;
+ case KeyFuncType::PASTE:
+ {
+ if ( !bReadOnly )
+ {
+ PasteSpecial();
+ bKeyProcessed = true;
+ }
+ }
+ break;
+ case KeyFuncType::DELETE:
+ {
+ if( !bReadOnly && !bSelection && ( pOwner->GetOutlinerMode() != OutlinerMode::TextObject ) )
+ {
+ if( aSel.nEndPos == pOwner->pEditEngine->GetTextLen( aSel.nEndPara ) )
+ {
+ Paragraph* pNext = pOwner->pParaList->GetParagraph( aSel.nEndPara+1 );
+ if( pNext && pNext->HasFlag(ParaFlag::ISPAGE) )
+ {
+ if( !pOwner->ImpCanDeleteSelectedPages( this, aSel.nEndPara, 1 ) )
+ return false;
+ }
+ }
+ }
+ }
+ break;
+ default: // is then possibly edited below.
+ eFunc = KeyFuncType::DONTKNOW;
+ }
+ }
+ if ( eFunc == KeyFuncType::DONTKNOW )
+ {
+ switch ( nCode )
+ {
+ case KEY_TAB:
+ {
+ if ( !bReadOnly && !aKeyCode.IsMod1() && !aKeyCode.IsMod2() )
+ {
+ if ( ( pOwner->GetOutlinerMode() != OutlinerMode::TextObject ) &&
+ ( pOwner->GetOutlinerMode() != OutlinerMode::TitleObject ) &&
+ ( bSelection || !aSel.nStartPos ) )
+ {
+ Indent( aKeyCode.IsShift() ? -1 : +1 );
+ bKeyProcessed = true;
+ }
+ else if ( ( pOwner->GetOutlinerMode() == OutlinerMode::TextObject ) &&
+ !bSelection && !aSel.nEndPos && pOwner->ImplHasNumberFormat( aSel.nEndPara ) )
+ {
+ Indent( aKeyCode.IsShift() ? -1 : +1 );
+ bKeyProcessed = true;
+ }
+ }
+ }
+ break;
+ case KEY_BACKSPACE:
+ {
+ if( !bReadOnly && !bSelection && aSel.nEndPara && !aSel.nEndPos )
+ {
+ Paragraph* pPara = pOwner->pParaList->GetParagraph( aSel.nEndPara );
+ Paragraph* pPrev = pOwner->pParaList->GetParagraph( aSel.nEndPara-1 );
+ if( !pPrev->IsVisible() )
+ return true;
+ if( !pPara->GetDepth() )
+ {
+ if(!pOwner->ImpCanDeleteSelectedPages(this, aSel.nEndPara , 1 ) )
+ return true;
+ }
+ }
+ }
+ break;
+ case KEY_RETURN:
+ {
+ if ( !bReadOnly )
+ {
+ // Special treatment: hard return at the end of a paragraph,
+ // which has collapsed subparagraphs.
+ Paragraph* pPara = pOwner->pParaList->GetParagraph( aSel.nEndPara );
+
+ if( !aKeyCode.IsShift() )
+ {
+ // ImpGetCursor again???
+ if( !bSelection &&
+ aSel.nEndPos == pOwner->pEditEngine->GetTextLen( aSel.nEndPara ) )
+ {
+ sal_Int32 nChildren = pOwner->pParaList->GetChildCount(pPara);
+ if( nChildren && !pOwner->pParaList->HasVisibleChildren(pPara))
+ {
+ pOwner->UndoActionStart( OLUNDO_INSERT );
+ sal_Int32 nTemp = aSel.nEndPara;
+ nTemp += nChildren;
+ nTemp++; // insert above next Non-Child
+ SAL_WARN_IF( nTemp < 0, "editeng", "OutlinerView::PostKeyEvent - overflow");
+ if (nTemp >= 0)
+ {
+ pOwner->Insert( OUString(),nTemp,pPara->GetDepth());
+ // Position the cursor
+ ESelection aTmpSel(nTemp,0,nTemp,0);
+ pEditView->SetSelection( aTmpSel );
+ }
+ pEditView->ShowCursor();
+ pOwner->UndoActionEnd();
+ bKeyProcessed = true;
+ }
+ }
+ }
+ if( !bKeyProcessed && !bSelection &&
+ !aKeyCode.IsShift() && aKeyCode.IsMod1() &&
+ ( aSel.nEndPos == pOwner->pEditEngine->GetTextLen(aSel.nEndPara) ) )
+ {
+ pOwner->UndoActionStart( OLUNDO_INSERT );
+ sal_Int32 nTemp = aSel.nEndPara;
+ nTemp++;
+ pOwner->Insert( OUString(), nTemp, pPara->GetDepth()+1 );
+
+ // Position the cursor
+ ESelection aTmpSel(nTemp,0,nTemp,0);
+ pEditView->SetSelection( aTmpSel );
+ pEditView->ShowCursor();
+ pOwner->UndoActionEnd();
+ bKeyProcessed = true;
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ return bKeyProcessed || pEditView->PostKeyEvent( rKEvt, pFrameWin );
+}
+
+sal_Int32 OutlinerView::ImpCheckMousePos(const Point& rPosPix, MouseTarget& reTarget)
+{
+ sal_Int32 nPara = EE_PARA_NOT_FOUND;
+
+ Point aMousePosWin = pEditView->GetOutputDevice().PixelToLogic( rPosPix );
+ if( !pEditView->GetOutputArea().Contains( aMousePosWin ) )
+ {
+ reTarget = MouseTarget::Outside;
+ }
+ else
+ {
+ reTarget = MouseTarget::Text;
+
+ Point aPaperPos( aMousePosWin );
+ tools::Rectangle aOutArea = pEditView->GetOutputArea();
+ tools::Rectangle aVisArea = pEditView->GetVisArea();
+ aPaperPos.AdjustX( -(aOutArea.Left()) );
+ aPaperPos.AdjustX(aVisArea.Left() );
+ aPaperPos.AdjustY( -(aOutArea.Top()) );
+ aPaperPos.AdjustY(aVisArea.Top() );
+
+ bool bBullet;
+ if ( pOwner->IsTextPos( aPaperPos, 0, &bBullet ) )
+ {
+ Point aDocPos = pOwner->GetDocPos( aPaperPos );
+ nPara = pOwner->pEditEngine->FindParagraph( aDocPos.Y() );
+
+ if ( bBullet )
+ {
+ reTarget = MouseTarget::Bullet;
+ }
+ else
+ {
+ // Check for hyperlink
+ const SvxFieldItem* pFieldItem = pEditView->GetField( aMousePosWin );
+ if ( pFieldItem && pFieldItem->GetField() && dynamic_cast< const SvxURLField* >(pFieldItem->GetField()) != nullptr )
+ reTarget = MouseTarget::Hypertext;
+ }
+ }
+ }
+ return nPara;
+}
+
+bool OutlinerView::MouseMove( const MouseEvent& rMEvt )
+{
+ if( ( pOwner->GetOutlinerMode() == OutlinerMode::TextObject ) || pEditView->GetEditEngine()->IsInSelectionMode())
+ return pEditView->MouseMove( rMEvt );
+
+ Point aMousePosWin( pEditView->GetOutputDevice().PixelToLogic( rMEvt.GetPosPixel() ) );
+ if( !pEditView->GetOutputArea().Contains( aMousePosWin ) )
+ return false;
+
+ PointerStyle aPointer = GetPointer( rMEvt.GetPosPixel() );
+ pEditView->GetWindow()->SetPointer( aPointer );
+ return pEditView->MouseMove( rMEvt );
+}
+
+
+bool OutlinerView::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ if ( ( pOwner->GetOutlinerMode() == OutlinerMode::TextObject ) || pEditView->GetEditEngine()->IsInSelectionMode() )
+ return pEditView->MouseButtonDown( rMEvt );
+
+ Point aMousePosWin( pEditView->GetOutputDevice().PixelToLogic( rMEvt.GetPosPixel() ) );
+ if( !pEditView->GetOutputArea().Contains( aMousePosWin ) )
+ return false;
+
+ PointerStyle aPointer = GetPointer( rMEvt.GetPosPixel() );
+ pEditView->GetWindow()->SetPointer( aPointer );
+
+ MouseTarget eTarget;
+ sal_Int32 nPara = ImpCheckMousePos( rMEvt.GetPosPixel(), eTarget );
+ if ( eTarget == MouseTarget::Bullet )
+ {
+ Paragraph* pPara = pOwner->pParaList->GetParagraph( nPara );
+ bool bHasChildren = (pPara && pOwner->pParaList->HasChildren(pPara));
+ if( rMEvt.GetClicks() == 1 )
+ {
+ sal_Int32 nEndPara = nPara;
+ if ( bHasChildren && pOwner->pParaList->HasVisibleChildren(pPara) )
+ nEndPara += pOwner->pParaList->GetChildCount( pPara );
+ // The selection is inverted, so that EditEngine does not scroll
+ ESelection aSel(nEndPara, EE_TEXTPOS_ALL, nPara, 0 );
+ pEditView->SetSelection( aSel );
+ }
+ else if( rMEvt.GetClicks() == 2 && bHasChildren )
+ ImpToggleExpand( pPara );
+
+ return true;
+ }
+
+ // special case for outliner view in impress, check if double click hits the page icon for toggle
+ if( (nPara == EE_PARA_NOT_FOUND) && (pOwner->GetOutlinerMode() == OutlinerMode::OutlineView) && (eTarget == MouseTarget::Text) && (rMEvt.GetClicks() == 2) )
+ {
+ ESelection aSel( pEditView->GetSelection() );
+ nPara = aSel.nStartPara;
+ Paragraph* pPara = pOwner->pParaList->GetParagraph( nPara );
+ if( (pPara && pOwner->pParaList->HasChildren(pPara)) && pPara->HasFlag(ParaFlag::ISPAGE) )
+ {
+ ImpToggleExpand( pPara );
+ }
+ }
+ return pEditView->MouseButtonDown( rMEvt );
+}
+
+
+bool OutlinerView::MouseButtonUp( const MouseEvent& rMEvt )
+{
+ if ( ( pOwner->GetOutlinerMode() == OutlinerMode::TextObject ) || pEditView->GetEditEngine()->IsInSelectionMode() )
+ return pEditView->MouseButtonUp( rMEvt );
+
+ Point aMousePosWin( pEditView->GetOutputDevice().PixelToLogic( rMEvt.GetPosPixel() ) );
+ if( !pEditView->GetOutputArea().Contains( aMousePosWin ) )
+ return false;
+
+ PointerStyle aPointer = GetPointer( rMEvt.GetPosPixel() );
+ pEditView->GetWindow()->SetPointer( aPointer );
+
+ return pEditView->MouseButtonUp( rMEvt );
+}
+
+void OutlinerView::ReleaseMouse()
+{
+ pEditView->ReleaseMouse();
+}
+
+void OutlinerView::ImpToggleExpand( Paragraph const * pPara )
+{
+ sal_Int32 nPara = pOwner->pParaList->GetAbsPos( pPara );
+ pEditView->SetSelection( ESelection( nPara, 0, nPara, 0 ) );
+ ImplExpandOrCollaps( nPara, nPara, !pOwner->pParaList->HasVisibleChildren( pPara ) );
+ pEditView->ShowCursor();
+}
+
+void OutlinerView::Select( Paragraph const * pParagraph, bool bSelect )
+{
+ sal_Int32 nPara = pOwner->pParaList->GetAbsPos( pParagraph );
+ sal_Int32 nEnd = 0;
+ if ( bSelect )
+ nEnd = SAL_MAX_INT32;
+
+ ESelection aSel( nPara, 0, nPara, nEnd );
+ pEditView->SetSelection( aSel );
+}
+
+
+void OutlinerView::SetAttribs( const SfxItemSet& rAttrs )
+{
+ bool bUpdate = pOwner->pEditEngine->SetUpdateLayout( false );
+
+ if( !pOwner->IsInUndo() && pOwner->IsUndoEnabled() )
+ pOwner->UndoActionStart( OLUNDO_ATTR );
+
+ ParaRange aSel = ImpGetSelectedParagraphs( false );
+
+ pEditView->SetAttribs( rAttrs );
+
+ // Update Bullet text
+ for( sal_Int32 nPara= aSel.nStartPara; nPara <= aSel.nEndPara; nPara++ )
+ {
+ pOwner->ImplCheckNumBulletItem( nPara );
+ pOwner->ImplCalcBulletText( nPara, false, false );
+
+ if( !pOwner->IsInUndo() && pOwner->IsUndoEnabled() )
+ pOwner->InsertUndo( std::make_unique<OutlinerUndoCheckPara>( pOwner, nPara ) );
+ }
+
+ if( !pOwner->IsInUndo() && pOwner->IsUndoEnabled() )
+ pOwner->UndoActionEnd();
+
+ pEditView->SetEditEngineUpdateLayout( bUpdate );
+}
+
+ParaRange OutlinerView::ImpGetSelectedParagraphs( bool bIncludeHiddenChildren )
+{
+ ESelection aSel = pEditView->GetSelection();
+ ParaRange aParas( aSel.nStartPara, aSel.nEndPara );
+ aParas.Adjust();
+
+ // Record the invisible Children of the last Parents in the selection
+ if ( bIncludeHiddenChildren )
+ {
+ Paragraph* pLast = pOwner->pParaList->GetParagraph( aParas.nEndPara );
+ if ( pOwner->pParaList->HasHiddenChildren( pLast ) )
+ aParas.nEndPara = aParas.nEndPara + pOwner->pParaList->GetChildCount( pLast );
+ }
+ return aParas;
+}
+
+// TODO: Name should be changed!
+void OutlinerView::AdjustDepth( short nDX )
+{
+ Indent( nDX );
+}
+
+void OutlinerView::Indent( short nDiff )
+{
+ if( !nDiff || ( ( nDiff > 0 ) && ImpCalcSelectedPages( true ) && !pOwner->ImpCanIndentSelectedPages( this ) ) )
+ return;
+
+ const bool bOutlinerView = bool(pOwner->pEditEngine->GetControlWord() & EEControlBits::OUTLINER);
+ bool bUpdate = pOwner->pEditEngine->SetUpdateLayout( false );
+
+ bool bUndo = !pOwner->IsInUndo() && pOwner->IsUndoEnabled();
+
+ if( bUndo )
+ pOwner->UndoActionStart( OLUNDO_DEPTH );
+
+ sal_Int16 nMinDepth = -1; // Optimization: avoid recalculate too many paragraphs if not really needed.
+
+ ParaRange aSel = ImpGetSelectedParagraphs( true );
+ for ( sal_Int32 nPara = aSel.nStartPara; nPara <= aSel.nEndPara; nPara++ )
+ {
+ Paragraph* pPara = pOwner->pParaList->GetParagraph( nPara );
+
+ sal_Int16 nOldDepth = pPara->GetDepth();
+ sal_Int16 nNewDepth = nOldDepth + nDiff;
+
+ if( bOutlinerView && nPara )
+ {
+ const bool bPage = pPara->HasFlag(ParaFlag::ISPAGE);
+ if( (bPage && (nDiff == +1)) || (!bPage && (nDiff == -1) && (nOldDepth <= 0)) )
+ {
+ // Notify App
+ pOwner->nDepthChangedHdlPrevDepth = nOldDepth;
+ ParaFlag nPrevFlags = pPara->nFlags;
+
+ if( bPage )
+ pPara->RemoveFlag( ParaFlag::ISPAGE );
+ else
+ pPara->SetFlag( ParaFlag::ISPAGE );
+
+ pOwner->DepthChangedHdl(pPara, nPrevFlags);
+ pOwner->pEditEngine->QuickMarkInvalid( ESelection( nPara, 0, nPara, 0 ) );
+
+ if( bUndo )
+ pOwner->InsertUndo( std::make_unique<OutlinerUndoChangeParaFlags>( pOwner, nPara, nPrevFlags, pPara->nFlags ) );
+
+ continue;
+ }
+ }
+
+ // do not switch off numeration with tab
+ if( (nOldDepth == 0) && (nNewDepth == -1) )
+ continue;
+
+ // do not indent if there is no numeration enabled
+ if( nOldDepth == -1 )
+ continue;
+
+ if ( nNewDepth < Outliner::gnMinDepth )
+ nNewDepth = Outliner::gnMinDepth;
+ if ( nNewDepth > pOwner->nMaxDepth )
+ nNewDepth = pOwner->nMaxDepth;
+
+ if( nOldDepth < nMinDepth )
+ nMinDepth = nOldDepth;
+ if( nNewDepth < nMinDepth )
+ nMinDepth = nNewDepth;
+
+ if( nOldDepth != nNewDepth )
+ {
+ if ( ( nPara == aSel.nStartPara ) && aSel.nStartPara && ( pOwner->GetOutlinerMode() != OutlinerMode::TextObject ))
+ {
+ // Special case: the predecessor of an indented paragraph is
+ // invisible and is now on the same level as the visible
+ // paragraph. In this case, the next visible paragraph is
+ // searched for and fluffed.
+#ifdef DBG_UTIL
+ Paragraph* _pPara = pOwner->pParaList->GetParagraph( aSel.nStartPara );
+ DBG_ASSERT(_pPara->IsVisible(),"Selected Paragraph invisible ?!");
+#endif
+ Paragraph* pPrev= pOwner->pParaList->GetParagraph( aSel.nStartPara-1 );
+
+ if( !pPrev->IsVisible() && ( pPrev->GetDepth() == nNewDepth ) )
+ {
+ // Predecessor is collapsed and is on the same level
+ // => find next visible paragraph and expand it
+ pPrev = pOwner->pParaList->GetParent( pPrev );
+ while( !pPrev->IsVisible() )
+ pPrev = pOwner->pParaList->GetParent( pPrev );
+
+ pOwner->Expand( pPrev );
+ pOwner->InvalidateBullet(pOwner->pParaList->GetAbsPos(pPrev));
+ }
+ }
+
+ pOwner->nDepthChangedHdlPrevDepth = nOldDepth;
+ ParaFlag nPrevFlags = pPara->nFlags;
+
+ pOwner->ImplInitDepth( nPara, nNewDepth, true );
+ pOwner->ImplCalcBulletText( nPara, false, false );
+
+ if ( pOwner->GetOutlinerMode() == OutlinerMode::OutlineObject )
+ pOwner->ImplSetLevelDependentStyleSheet( nPara );
+
+ // Notify App
+ pOwner->DepthChangedHdl(pPara, nPrevFlags);
+ }
+ else
+ {
+ // Needs at least a repaint...
+ pOwner->pEditEngine->QuickMarkInvalid( ESelection( nPara, 0, nPara, 0 ) );
+ }
+ }
+
+ sal_Int32 nParas = pOwner->pParaList->GetParagraphCount();
+ for ( sal_Int32 n = aSel.nEndPara+1; n < nParas; n++ )
+ {
+ Paragraph* pPara = pOwner->pParaList->GetParagraph( n );
+ if ( pPara->GetDepth() < nMinDepth )
+ break;
+ pOwner->ImplCalcBulletText( n, false, false );
+ }
+
+ if ( bUpdate )
+ {
+ pEditView->SetEditEngineUpdateLayout( true );
+ pEditView->ShowCursor();
+ }
+
+ if( bUndo )
+ pOwner->UndoActionEnd();
+}
+
+void OutlinerView::AdjustHeight( tools::Long nDY )
+{
+ pEditView->MoveParagraphs( nDY );
+}
+
+tools::Rectangle OutlinerView::GetVisArea() const
+{
+ return pEditView->GetVisArea();
+}
+
+void OutlinerView::Expand()
+{
+ ParaRange aParas = ImpGetSelectedParagraphs( false );
+ ImplExpandOrCollaps( aParas.nStartPara, aParas.nEndPara, true );
+}
+
+
+void OutlinerView::Collapse()
+{
+ ParaRange aParas = ImpGetSelectedParagraphs( false );
+ ImplExpandOrCollaps( aParas.nStartPara, aParas.nEndPara, false );
+}
+
+
+void OutlinerView::ExpandAll()
+{
+ ImplExpandOrCollaps( 0, pOwner->pParaList->GetParagraphCount()-1, true );
+}
+
+
+void OutlinerView::CollapseAll()
+{
+ ImplExpandOrCollaps( 0, pOwner->pParaList->GetParagraphCount()-1, false );
+}
+
+void OutlinerView::ImplExpandOrCollaps( sal_Int32 nStartPara, sal_Int32 nEndPara, bool bExpand )
+{
+ bool bUpdate = pOwner->SetUpdateLayout( false );
+
+ bool bUndo = !pOwner->IsInUndo() && pOwner->IsUndoEnabled();
+ if( bUndo )
+ pOwner->UndoActionStart( bExpand ? OLUNDO_EXPAND : OLUNDO_COLLAPSE );
+
+ for ( sal_Int32 nPara = nStartPara; nPara <= nEndPara; nPara++ )
+ {
+ Paragraph* pPara = pOwner->pParaList->GetParagraph( nPara );
+ bool bDone = bExpand ? pOwner->Expand( pPara ) : pOwner->Collapse( pPara );
+ if( bDone )
+ {
+ // The line under the paragraph should disappear ...
+ pOwner->pEditEngine->QuickMarkToBeRepainted( nPara );
+ }
+ }
+
+ if( bUndo )
+ pOwner->UndoActionEnd();
+
+ if ( bUpdate )
+ {
+ pOwner->SetUpdateLayout( true );
+ pEditView->ShowCursor();
+ }
+}
+
+void OutlinerView::InsertText( const OutlinerParaObject& rParaObj )
+{
+ // Like Paste, only EditView::Insert, instead of EditView::Paste.
+ // Actually not quite true that possible indentations must be corrected,
+ // but that comes later by a universal import. The indentation level is
+ // then determined right in the Inserted method.
+ // Possible structure:
+ // pImportInfo with DestPara, DestPos, nFormat, pParaObj...
+ // Possibly problematic:
+ // EditEngine, RTF => Splitting the area, later join together.
+
+ if ( ImpCalcSelectedPages( false ) && !pOwner->ImpCanDeleteSelectedPages( this ) )
+ return;
+
+ pOwner->UndoActionStart( OLUNDO_INSERT );
+
+ const bool bPrevUpdateLayout = pOwner->pEditEngine->SetUpdateLayout( false );
+ sal_Int32 nStart, nParaCount;
+ nParaCount = pOwner->pEditEngine->GetParagraphCount();
+ sal_uInt16 nSize = ImpInitPaste( nStart );
+ pEditView->InsertText( rParaObj.GetTextObject() );
+ ImpPasted( nStart, nParaCount, nSize);
+ pEditView->SetEditEngineUpdateLayout( bPrevUpdateLayout );
+
+ pOwner->UndoActionEnd();
+
+ pEditView->ShowCursor();
+}
+
+
+void OutlinerView::Cut()
+{
+ if ( !ImpCalcSelectedPages( false ) || pOwner->ImpCanDeleteSelectedPages( this ) ) {
+ pEditView->Cut();
+ // Chaining handling
+ aEndCutPasteLink.Call(nullptr);
+ }
+}
+
+void OutlinerView::PasteSpecial(SotClipboardFormatId format)
+{
+ Paste( true, format );
+}
+
+void OutlinerView::Paste( bool bUseSpecial, SotClipboardFormatId format)
+{
+ if ( ImpCalcSelectedPages( false ) && !pOwner->ImpCanDeleteSelectedPages( this ) )
+ return;
+
+ pOwner->UndoActionStart( OLUNDO_INSERT );
+
+ const bool bPrevUpdateLayout = pOwner->pEditEngine->SetUpdateLayout( false );
+ pOwner->bPasting = true;
+
+ if ( bUseSpecial )
+ pEditView->PasteSpecial(format);
+ else
+ pEditView->Paste();
+
+ if ( pOwner->GetOutlinerMode() == OutlinerMode::OutlineObject )
+ {
+ const sal_Int32 nParaCount = pOwner->pEditEngine->GetParagraphCount();
+
+ for( sal_Int32 nPara = 0; nPara < nParaCount; nPara++ )
+ pOwner->ImplSetLevelDependentStyleSheet( nPara );
+ }
+
+ pEditView->SetEditEngineUpdateLayout( bPrevUpdateLayout );
+ pOwner->UndoActionEnd();
+ pEditView->ShowCursor();
+
+ // Chaining handling
+ // NOTE: We need to do this last because it pEditView may be deleted if a switch of box occurs
+ aEndCutPasteLink.Call(nullptr);
+}
+
+void OutlinerView::CreateSelectionList (std::vector<Paragraph*> &aSelList)
+{
+ ParaRange aParas = ImpGetSelectedParagraphs( true );
+
+ for ( sal_Int32 nPara = aParas.nStartPara; nPara <= aParas.nEndPara; nPara++ )
+ {
+ Paragraph* pPara = pOwner->pParaList->GetParagraph( nPara );
+ aSelList.push_back(pPara);
+ }
+}
+
+void OutlinerView::SetStyleSheet(const OUString& rStyleName)
+{
+ ParaRange aParas = ImpGetSelectedParagraphs(false);
+
+ auto pStyle = pOwner->GetStyleSheetPool()->Find(rStyleName, SfxStyleFamily::Para);
+ if (!pStyle)
+ return;
+
+ for (sal_Int32 nPara = aParas.nStartPara; nPara <= aParas.nEndPara; nPara++)
+ pOwner->SetStyleSheet(nPara, static_cast<SfxStyleSheet*>(pStyle));
+}
+
+const SfxStyleSheet* OutlinerView::GetStyleSheet() const
+{
+ return pEditView->GetStyleSheet();
+}
+
+SfxStyleSheet* OutlinerView::GetStyleSheet()
+{
+ return pEditView->GetStyleSheet();
+}
+
+PointerStyle OutlinerView::GetPointer( const Point& rPosPixel )
+{
+ MouseTarget eTarget;
+ ImpCheckMousePos( rPosPixel, eTarget );
+
+ PointerStyle ePointerStyle = PointerStyle::Arrow;
+ if ( eTarget == MouseTarget::Text )
+ {
+ ePointerStyle = GetOutliner()->IsVertical() ? PointerStyle::TextVertical : PointerStyle::Text;
+ }
+ else if ( eTarget == MouseTarget::Hypertext )
+ {
+ ePointerStyle = PointerStyle::RefHand;
+ }
+ else if ( eTarget == MouseTarget::Bullet )
+ {
+ ePointerStyle = PointerStyle::Move;
+ }
+
+ return ePointerStyle;
+}
+
+
+sal_Int32 OutlinerView::ImpInitPaste( sal_Int32& rStart )
+{
+ pOwner->bPasting = true;
+ ESelection aSelection( pEditView->GetSelection() );
+ aSelection.Adjust();
+ rStart = aSelection.nStartPara;
+ sal_Int32 nSize = aSelection.nEndPara - aSelection.nStartPara + 1;
+ return nSize;
+}
+
+
+void OutlinerView::ImpPasted( sal_Int32 nStart, sal_Int32 nPrevParaCount, sal_Int32 nSize)
+{
+ pOwner->bPasting = false;
+ sal_Int32 nCurParaCount = pOwner->pEditEngine->GetParagraphCount();
+ if( nCurParaCount < nPrevParaCount )
+ nSize = nSize - ( nPrevParaCount - nCurParaCount );
+ else
+ nSize = nSize + ( nCurParaCount - nPrevParaCount );
+ pOwner->ImpTextPasted( nStart, nSize );
+}
+
+bool OutlinerView::Command(const CommandEvent& rCEvt)
+{
+ return pEditView->Command(rCEvt);
+}
+
+void OutlinerView::SelectRange( sal_Int32 nFirst, sal_Int32 nCount )
+{
+ sal_Int32 nLast = nFirst+nCount;
+ nCount = pOwner->pParaList->GetParagraphCount();
+ if( nLast <= nCount )
+ nLast = nCount - 1;
+ ESelection aSel( nFirst, 0, nLast, EE_TEXTPOS_ALL );
+ pEditView->SetSelection( aSel );
+}
+
+
+sal_Int32 OutlinerView::ImpCalcSelectedPages( bool bIncludeFirstSelected )
+{
+ ESelection aSel( pEditView->GetSelection() );
+ aSel.Adjust();
+
+ sal_Int32 nPages = 0;
+ sal_Int32 nFirstPage = EE_PARA_MAX_COUNT;
+ sal_Int32 nStartPara = aSel.nStartPara;
+ if ( !bIncludeFirstSelected )
+ nStartPara++; // All paragraphs after StartPara will be deleted
+ for ( sal_Int32 nPara = nStartPara; nPara <= aSel.nEndPara; nPara++ )
+ {
+ Paragraph* pPara = pOwner->pParaList->GetParagraph( nPara );
+ DBG_ASSERT(pPara, "ImpCalcSelectedPages: invalid Selection? ");
+ if( pPara->HasFlag(ParaFlag::ISPAGE) )
+ {
+ nPages++;
+ if( nFirstPage == EE_PARA_MAX_COUNT )
+ nFirstPage = nPara;
+ }
+ }
+
+ if( nPages )
+ {
+ pOwner->nDepthChangedHdlPrevDepth = nPages;
+ pOwner->mnFirstSelPage = nFirstPage;
+ }
+
+ return nPages;
+}
+
+
+void OutlinerView::ToggleBullets()
+{
+ pOwner->UndoActionStart( OLUNDO_DEPTH );
+
+ ESelection aSel( pEditView->GetSelection() );
+ aSel.Adjust();
+
+ const bool bUpdate = pOwner->pEditEngine->SetUpdateLayout( false );
+
+ sal_Int16 nNewDepth = -2;
+ const SvxNumRule* pDefaultBulletNumRule = nullptr;
+
+ for ( sal_Int32 nPara = aSel.nStartPara; nPara <= aSel.nEndPara; nPara++ )
+ {
+ Paragraph* pPara = pOwner->pParaList->GetParagraph( nPara );
+ DBG_ASSERT(pPara, "OutlinerView::ToggleBullets(), illegal selection?");
+
+ if( pPara )
+ {
+ if( nNewDepth == -2 )
+ {
+ nNewDepth = (pOwner->GetDepth(nPara) == -1) ? 0 : -1;
+ if ( nNewDepth == 0 )
+ {
+ // determine default numbering rule for bullets
+ const ESelection aSelection(nPara, 0);
+ const SfxItemSet aTmpSet(pOwner->pEditEngine->GetAttribs(aSelection));
+ const SfxPoolItem& rPoolItem = aTmpSet.GetPool()->GetDefaultItem( EE_PARA_NUMBULLET );
+ const SvxNumBulletItem* pNumBulletItem = dynamic_cast< const SvxNumBulletItem* >(&rPoolItem);
+ pDefaultBulletNumRule = pNumBulletItem ? &pNumBulletItem->GetNumRule() : nullptr;
+ }
+ }
+
+ pOwner->SetDepth( pPara, nNewDepth );
+
+ if( nNewDepth == -1 )
+ {
+ const SfxItemSet& rAttrs = pOwner->GetParaAttribs( nPara );
+ if ( rAttrs.GetItemState( EE_PARA_BULLETSTATE ) == SfxItemState::SET )
+ {
+ SfxItemSet aAttrs(rAttrs);
+ aAttrs.ClearItem( EE_PARA_BULLETSTATE );
+ pOwner->SetParaAttribs( nPara, aAttrs );
+ }
+ }
+ else
+ {
+ if ( pDefaultBulletNumRule )
+ {
+ const SvxNumberFormat* pFmt = pOwner ->GetNumberFormat( nPara );
+ if ( !pFmt
+ || ( pFmt->GetNumberingType() != SVX_NUM_BITMAP
+ && pFmt->GetNumberingType() != SVX_NUM_CHAR_SPECIAL ) )
+ {
+ SfxItemSet aAttrs( pOwner->GetParaAttribs( nPara ) );
+ SvxNumRule aNewNumRule( *pDefaultBulletNumRule );
+ aAttrs.Put( SvxNumBulletItem( std::move(aNewNumRule), EE_PARA_NUMBULLET ) );
+ pOwner->SetParaAttribs( nPara, aAttrs );
+ }
+ }
+ }
+ }
+ }
+
+ const sal_Int32 nParaCount = pOwner->pParaList->GetParagraphCount();
+ pOwner->ImplCheckParagraphs( aSel.nStartPara, nParaCount );
+
+ sal_Int32 nEndPara = (nParaCount > 0) ? nParaCount-1 : nParaCount;
+ pOwner->pEditEngine->QuickMarkInvalid( ESelection( aSel.nStartPara, 0, nEndPara, 0 ) );
+
+ pOwner->pEditEngine->SetUpdateLayout( bUpdate );
+
+ pOwner->UndoActionEnd();
+}
+
+
+void OutlinerView::ToggleBulletsNumbering(
+ const bool bToggle,
+ const bool bHandleBullets,
+ const SvxNumRule* pNumRule )
+{
+ ESelection aSel( pEditView->GetSelection() );
+ aSel.Adjust();
+
+ bool bToggleOn = true;
+ if ( bToggle )
+ {
+ bToggleOn = false;
+ const sal_Int16 nBulletNumberingStatus( pOwner->GetBulletsNumberingStatus( aSel.nStartPara, aSel.nEndPara ) );
+ if ( nBulletNumberingStatus != 0 && bHandleBullets )
+ {
+ // not all paragraphs have bullets and method called to toggle bullets --> bullets on
+ bToggleOn = true;
+ }
+ else if ( nBulletNumberingStatus != 1 && !bHandleBullets )
+ {
+ // not all paragraphs have numbering and method called to toggle numberings --> numberings on
+ bToggleOn = true;
+ }
+ }
+ if ( bToggleOn )
+ {
+ // apply bullets/numbering for selected paragraphs
+ ApplyBulletsNumbering( bHandleBullets, pNumRule, bToggle, true );
+ }
+ else
+ {
+ // switch off bullets/numbering for selected paragraphs
+ SwitchOffBulletsNumbering( true );
+ }
+}
+
+void OutlinerView::EnsureNumberingIsOn()
+{
+ pOwner->UndoActionStart(OLUNDO_DEPTH);
+
+ ESelection aSel(pEditView->GetSelection());
+ aSel.Adjust();
+
+ const bool bUpdate = pOwner->pEditEngine->IsUpdateLayout();
+ pOwner->pEditEngine->SetUpdateLayout(false);
+
+ for (sal_Int32 nPara = aSel.nStartPara; nPara <= aSel.nEndPara; nPara++)
+ {
+ Paragraph* pPara = pOwner->pParaList->GetParagraph(nPara);
+ DBG_ASSERT(pPara, "OutlinerView::EnableBullets(), illegal selection?");
+
+ if (pPara && pOwner->GetDepth(nPara) == -1)
+ pOwner->SetDepth(pPara, 0);
+ }
+
+ sal_Int32 nParaCount = pOwner->pParaList->GetParagraphCount();
+ pOwner->ImplCheckParagraphs(aSel.nStartPara, nParaCount);
+
+ const sal_Int32 nEndPara = (nParaCount > 0) ? nParaCount-1 : nParaCount;
+ pOwner->pEditEngine->QuickMarkInvalid(ESelection(aSel.nStartPara, 0, nEndPara, 0));
+
+ pOwner->pEditEngine->SetUpdateLayout(bUpdate);
+
+ pOwner->UndoActionEnd();
+}
+
+void OutlinerView::ApplyBulletsNumbering(
+ const bool bHandleBullets,
+ const SvxNumRule* pNewNumRule,
+ const bool bCheckCurrentNumRuleBeforeApplyingNewNumRule,
+ const bool bAtSelection )
+{
+ if (!pOwner || !pOwner->pEditEngine || !pOwner->pParaList)
+ {
+ return;
+ }
+
+ pOwner->UndoActionStart(OLUNDO_DEPTH);
+ const bool bUpdate = pOwner->pEditEngine->SetUpdateLayout(false);
+
+ sal_Int32 nStartPara = 0;
+ sal_Int32 nEndPara = 0;
+ if ( bAtSelection )
+ {
+ ESelection aSel( pEditView->GetSelection() );
+ aSel.Adjust();
+ nStartPara = aSel.nStartPara;
+ nEndPara = aSel.nEndPara;
+ }
+ else
+ {
+ nStartPara = 0;
+ nEndPara = pOwner->pParaList->GetParagraphCount() - 1;
+ }
+
+ for (sal_Int32 nPara = nStartPara; nPara <= nEndPara; ++nPara)
+ {
+ Paragraph* pPara = pOwner->pParaList->GetParagraph(nPara);
+ DBG_ASSERT(pPara, "OutlinerView::ApplyBulletsNumbering(..), illegal selection?");
+
+ if (pPara)
+ {
+ const sal_Int16 nDepth = pOwner->GetDepth(nPara);
+ if ( nDepth == -1 )
+ {
+ pOwner->SetDepth( pPara, 0 );
+ }
+
+ const SfxItemSet& rAttrs = pOwner->GetParaAttribs(nPara);
+ SfxItemSet aAttrs(rAttrs);
+ aAttrs.Put(SfxBoolItem(EE_PARA_BULLETSTATE, true));
+
+ // apply new numbering rule
+ if ( pNewNumRule )
+ {
+ bool bApplyNumRule = false;
+ if ( !bCheckCurrentNumRuleBeforeApplyingNewNumRule )
+ {
+ bApplyNumRule = true;
+ }
+ else
+ {
+ const SvxNumberFormat* pFmt = pOwner ->GetNumberFormat(nPara);
+ if (!pFmt)
+ {
+ bApplyNumRule = true;
+ }
+ else
+ {
+ sal_Int16 nNumType = pFmt->GetNumberingType();
+ if ( bHandleBullets
+ && nNumType != SVX_NUM_BITMAP && nNumType != SVX_NUM_CHAR_SPECIAL)
+ {
+ // Set to Normal bullet, old bullet type is Numbering bullet.
+ bApplyNumRule = true;
+ }
+ else if ( !bHandleBullets
+ && (nNumType == SVX_NUM_BITMAP || nNumType == SVX_NUM_CHAR_SPECIAL))
+ {
+ // Set to Numbering bullet, old bullet type is Normal bullet.
+ bApplyNumRule = true;
+ }
+ }
+ }
+
+ if ( bApplyNumRule )
+ {
+ SvxNumRule aNewRule(*pNewNumRule);
+
+ // Get old bullet space.
+ {
+ const SvxNumBulletItem* pNumBulletItem = rAttrs.GetItemIfSet(EE_PARA_NUMBULLET, false);
+ if (pNumBulletItem)
+ {
+ // Use default value when has not contain bullet item.
+ ESelection aSelection(nPara, 0);
+ SfxItemSet aTmpSet(pOwner->pEditEngine->GetAttribs(aSelection));
+ pNumBulletItem = aTmpSet.GetItem(EE_PARA_NUMBULLET);
+ }
+
+ if (pNumBulletItem)
+ {
+ const sal_uInt16 nLevelCnt = std::min(pNumBulletItem->GetNumRule().GetLevelCount(), aNewRule.GetLevelCount());
+ for ( sal_uInt16 nLevel = 0; nLevel < nLevelCnt; ++nLevel )
+ {
+ const SvxNumberFormat* pOldFmt = pNumBulletItem->GetNumRule().Get(nLevel);
+ const SvxNumberFormat* pNewFmt = aNewRule.Get(nLevel);
+ if (pOldFmt && pNewFmt && (pOldFmt->GetFirstLineOffset() != pNewFmt->GetFirstLineOffset() || pOldFmt->GetAbsLSpace() != pNewFmt->GetAbsLSpace()))
+ {
+ SvxNumberFormat aNewFmtClone(*pNewFmt);
+ aNewFmtClone.SetFirstLineOffset(pOldFmt->GetFirstLineOffset());
+ aNewFmtClone.SetAbsLSpace(pOldFmt->GetAbsLSpace());
+ aNewRule.SetLevel(nLevel, &aNewFmtClone);
+ }
+ }
+ }
+ }
+
+ aAttrs.Put(SvxNumBulletItem(std::move(aNewRule), EE_PARA_NUMBULLET));
+ }
+ }
+ pOwner->SetParaAttribs(nPara, aAttrs);
+ }
+ }
+
+ const sal_uInt16 nParaCount = static_cast<sal_uInt16>(pOwner->pParaList->GetParagraphCount());
+ pOwner->ImplCheckParagraphs( nStartPara, nParaCount );
+ pOwner->pEditEngine->QuickMarkInvalid( ESelection( nStartPara, 0, nParaCount, 0 ) );
+
+ pOwner->pEditEngine->SetUpdateLayout( bUpdate );
+
+ pOwner->UndoActionEnd();
+}
+
+
+void OutlinerView::SwitchOffBulletsNumbering(
+ const bool bAtSelection )
+{
+ sal_Int32 nStartPara = 0;
+ sal_Int32 nEndPara = 0;
+ if ( bAtSelection )
+ {
+ ESelection aSel( pEditView->GetSelection() );
+ aSel.Adjust();
+ nStartPara = aSel.nStartPara;
+ nEndPara = aSel.nEndPara;
+ }
+ else
+ {
+ nStartPara = 0;
+ nEndPara = pOwner->pParaList->GetParagraphCount() - 1;
+ }
+
+ pOwner->UndoActionStart( OLUNDO_DEPTH );
+ const bool bUpdate = pOwner->pEditEngine->SetUpdateLayout( false );
+
+ for ( sal_Int32 nPara = nStartPara; nPara <= nEndPara; ++nPara )
+ {
+ Paragraph* pPara = pOwner->pParaList->GetParagraph( nPara );
+ DBG_ASSERT(pPara, "OutlinerView::SwitchOffBulletsNumbering(...), illegal paragraph index?");
+
+ if( pPara )
+ {
+ pOwner->SetDepth( pPara, -1 );
+
+ const SfxItemSet& rAttrs = pOwner->GetParaAttribs( nPara );
+ if (rAttrs.GetItemState( EE_PARA_BULLETSTATE ) == SfxItemState::SET)
+ {
+ SfxItemSet aAttrs(rAttrs);
+ aAttrs.ClearItem( EE_PARA_BULLETSTATE );
+ pOwner->SetParaAttribs( nPara, aAttrs );
+ }
+ }
+ }
+
+ const sal_uInt16 nParaCount = static_cast<sal_uInt16>(pOwner->pParaList->GetParagraphCount());
+ pOwner->ImplCheckParagraphs( nStartPara, nParaCount );
+ pOwner->pEditEngine->QuickMarkInvalid( ESelection( nStartPara, 0, nParaCount, 0 ) );
+
+ pOwner->pEditEngine->SetUpdateLayout( bUpdate );
+ pOwner->UndoActionEnd();
+}
+
+
+void OutlinerView::RemoveAttribsKeepLanguages( bool bRemoveParaAttribs )
+{
+ RemoveAttribs( bRemoveParaAttribs, true /*keep language attribs*/ );
+}
+
+void OutlinerView::RemoveAttribs( bool bRemoveParaAttribs, bool bKeepLanguages )
+{
+ bool bUpdate = pOwner->SetUpdateLayout( false );
+ pOwner->UndoActionStart( OLUNDO_ATTR );
+ if (bKeepLanguages)
+ pEditView->RemoveAttribsKeepLanguages( bRemoveParaAttribs );
+ else
+ pEditView->RemoveAttribs( bRemoveParaAttribs );
+ if ( bRemoveParaAttribs )
+ {
+ // Loop through all paragraphs and set indentation and level
+ ESelection aSel = pEditView->GetSelection();
+ aSel.Adjust();
+ for ( sal_Int32 nPara = aSel.nStartPara; nPara <= aSel.nEndPara; nPara++ )
+ {
+ Paragraph* pPara = pOwner->pParaList->GetParagraph( nPara );
+ pOwner->ImplInitDepth( nPara, pPara->GetDepth(), false );
+ }
+ }
+ pOwner->UndoActionEnd();
+ pOwner->SetUpdateLayout( bUpdate );
+}
+
+
+// ====================== Simple pass-through =======================
+
+
+void OutlinerView::InsertText( const OUString& rNew, bool bSelect )
+{
+ if( pOwner->bFirstParaIsEmpty )
+ pOwner->Insert( OUString() );
+ pEditView->InsertText( rNew, bSelect );
+}
+
+void OutlinerView::SetVisArea( const tools::Rectangle& rRect )
+{
+ pEditView->SetVisArea( rRect );
+}
+
+
+void OutlinerView::SetSelection( const ESelection& rSel )
+{
+ pEditView->SetSelection( rSel );
+}
+
+void OutlinerView::GetSelectionRectangles(std::vector<tools::Rectangle>& rLogicRects) const
+{
+ pEditView->GetSelectionRectangles(rLogicRects);
+}
+
+void OutlinerView::SetReadOnly( bool bReadOnly )
+{
+ pEditView->SetReadOnly( bReadOnly );
+}
+
+bool OutlinerView::IsReadOnly() const
+{
+ return pEditView->IsReadOnly();
+}
+
+bool OutlinerView::HasSelection() const
+{
+ return pEditView->HasSelection();
+}
+
+void OutlinerView::ShowCursor( bool bGotoCursor, bool bActivate )
+{
+ pEditView->ShowCursor( bGotoCursor, /*bForceVisCursor=*/true, bActivate );
+}
+
+void OutlinerView::HideCursor(bool bDeactivate)
+{
+ pEditView->HideCursor(bDeactivate);
+}
+
+void OutlinerView::SetWindow( vcl::Window* pWin )
+{
+ pEditView->SetWindow( pWin );
+}
+
+vcl::Window* OutlinerView::GetWindow() const
+{
+ return pEditView->GetWindow();
+}
+
+void OutlinerView::SetOutputArea( const tools::Rectangle& rRect )
+{
+ pEditView->SetOutputArea( rRect );
+}
+
+tools::Rectangle const & OutlinerView::GetOutputArea() const
+{
+ return pEditView->GetOutputArea();
+}
+
+OUString OutlinerView::GetSelected() const
+{
+ return pEditView->GetSelected();
+}
+
+void OutlinerView::StartSpeller(weld::Widget* pDialogParent)
+{
+ pEditView->StartSpeller(pDialogParent);
+}
+
+EESpellState OutlinerView::StartThesaurus(weld::Widget* pDialogParent)
+{
+ return pEditView->StartThesaurus(pDialogParent);
+}
+
+void OutlinerView::StartTextConversion(weld::Widget* pDialogParent,
+ LanguageType nSrcLang, LanguageType nDestLang, const vcl::Font *pDestFont,
+ sal_Int32 nOptions, bool bIsInteractive, bool bMultipleDoc )
+{
+ if (
+ (LANGUAGE_KOREAN == nSrcLang && LANGUAGE_KOREAN == nDestLang) ||
+ (LANGUAGE_CHINESE_SIMPLIFIED == nSrcLang && LANGUAGE_CHINESE_TRADITIONAL == nDestLang) ||
+ (LANGUAGE_CHINESE_TRADITIONAL == nSrcLang && LANGUAGE_CHINESE_SIMPLIFIED == nDestLang)
+ )
+ {
+ pEditView->StartTextConversion(pDialogParent, nSrcLang, nDestLang, pDestFont, nOptions, bIsInteractive, bMultipleDoc);
+ }
+ else
+ {
+ OSL_FAIL( "unexpected language" );
+ }
+}
+
+
+sal_Int32 OutlinerView::StartSearchAndReplace( const SvxSearchItem& rSearchItem )
+{
+ return pEditView->StartSearchAndReplace( rSearchItem );
+}
+
+void OutlinerView::TransliterateText( TransliterationFlags nTransliterationMode )
+{
+ pEditView->TransliterateText( nTransliterationMode );
+}
+
+ESelection OutlinerView::GetSelection() const
+{
+ return pEditView->GetSelection();
+}
+
+
+void OutlinerView::Scroll( tools::Long nHorzScroll, tools::Long nVertScroll )
+{
+ pEditView->Scroll( nHorzScroll, nVertScroll );
+}
+
+void OutlinerView::SetControlWord( EVControlBits nWord )
+{
+ pEditView->SetControlWord( nWord );
+}
+
+EVControlBits OutlinerView::GetControlWord() const
+{
+ return pEditView->GetControlWord();
+}
+
+void OutlinerView::SetAnchorMode( EEAnchorMode eMode )
+{
+ pEditView->SetAnchorMode( eMode );
+}
+
+EEAnchorMode OutlinerView::GetAnchorMode() const
+{
+ return pEditView->GetAnchorMode();
+}
+
+void OutlinerView::Copy()
+{
+ pEditView->Copy();
+}
+
+void OutlinerView::InsertField( const SvxFieldItem& rFld )
+{
+ pEditView->InsertField( rFld );
+}
+
+const SvxFieldItem* OutlinerView::GetFieldUnderMousePointer() const
+{
+ return pEditView->GetFieldUnderMousePointer();
+}
+
+const SvxFieldItem* OutlinerView::GetFieldAtSelection(bool bAlsoCheckBeforeCursor) const
+{
+ return pEditView->GetFieldAtSelection(bAlsoCheckBeforeCursor);
+}
+
+void OutlinerView::SelectFieldAtCursor()
+{
+ pEditView->SelectFieldAtCursor();
+}
+
+void OutlinerView::SetInvalidateMore( sal_uInt16 nPixel )
+{
+ pEditView->SetInvalidateMore( nPixel );
+}
+
+
+sal_uInt16 OutlinerView::GetInvalidateMore() const
+{
+ return pEditView->GetInvalidateMore();
+}
+
+
+bool OutlinerView::IsCursorAtWrongSpelledWord()
+{
+ return pEditView->IsCursorAtWrongSpelledWord();
+}
+
+
+bool OutlinerView::IsWrongSpelledWordAtPos( const Point& rPosPixel )
+{
+ return pEditView->IsWrongSpelledWordAtPos( rPosPixel, /*bMarkIfWrong*/false );
+}
+
+void OutlinerView::ExecuteSpellPopup(const Point& rPosPixel, const Link<SpellCallbackInfo&,void>& rStartDlg)
+{
+ pEditView->ExecuteSpellPopup(rPosPixel, rStartDlg);
+}
+
+void OutlinerView::Read( SvStream& rInput, EETextFormat eFormat, SvKeyValueIterator* pHTTPHeaderAttrs )
+{
+ sal_Int32 nOldParaCount = pEditView->GetEditEngine()->GetParagraphCount();
+ ESelection aOldSel = pEditView->GetSelection();
+ aOldSel.Adjust();
+
+ pEditView->Read( rInput, eFormat, pHTTPHeaderAttrs );
+
+ tools::Long nParaDiff = pEditView->GetEditEngine()->GetParagraphCount() - nOldParaCount;
+ sal_Int32 nChangesStart = aOldSel.nStartPara;
+ sal_Int32 nChangesEnd = nChangesStart + nParaDiff + (aOldSel.nEndPara-aOldSel.nStartPara);
+
+ for ( sal_Int32 n = nChangesStart; n <= nChangesEnd; n++ )
+ {
+ if ( pOwner->GetOutlinerMode() == OutlinerMode::OutlineObject )
+ pOwner->ImplSetLevelDependentStyleSheet( n );
+ }
+
+ pOwner->ImpFilterIndents( nChangesStart, nChangesEnd );
+}
+
+void OutlinerView::SetBackgroundColor( const Color& rColor )
+{
+ pEditView->SetBackgroundColor( rColor );
+}
+
+void OutlinerView::RegisterViewShell(OutlinerViewShell* pViewShell)
+{
+ pEditView->RegisterViewShell(pViewShell);
+}
+
+Color const & OutlinerView::GetBackgroundColor() const
+{
+ return pEditView->GetBackgroundColor();
+}
+
+SfxItemSet OutlinerView::GetAttribs()
+{
+ return pEditView->GetAttribs();
+}
+
+SvtScriptType OutlinerView::GetSelectedScriptType() const
+{
+ return pEditView->GetSelectedScriptType();
+}
+
+OUString OutlinerView::GetSurroundingText() const
+{
+ return pEditView->GetSurroundingText();
+}
+
+Selection OutlinerView::GetSurroundingTextSelection() const
+{
+ return pEditView->GetSurroundingTextSelection();
+}
+
+bool OutlinerView::DeleteSurroundingText(const Selection& rSelection)
+{
+ return pEditView->DeleteSurroundingText(rSelection);
+}
+
+// ===== some code for thesaurus sub menu within context menu
+
+namespace {
+
+bool isSingleScriptType( SvtScriptType nScriptType )
+{
+ sal_uInt8 nScriptCount = 0;
+
+ if (nScriptType & SvtScriptType::LATIN)
+ ++nScriptCount;
+ if (nScriptType & SvtScriptType::ASIAN)
+ ++nScriptCount;
+ if (nScriptType & SvtScriptType::COMPLEX)
+ ++nScriptCount;
+
+ return nScriptCount == 1;
+}
+
+}
+
+// returns: true if a word for thesaurus look-up was found at the current cursor position.
+// The status string will be word + iso language string (e.g. "light#en-US")
+bool GetStatusValueForThesaurusFromContext(
+ OUString &rStatusVal,
+ LanguageType &rLang,
+ const EditView &rEditView )
+{
+ // get text and locale for thesaurus look up
+ OUString aText;
+ EditEngine *pEditEngine = rEditView.GetEditEngine();
+ ESelection aTextSel( rEditView.GetSelection() );
+ if (!aTextSel.HasRange())
+ aTextSel = pEditEngine->GetWord( aTextSel, i18n::WordType::DICTIONARY_WORD );
+ aText = pEditEngine->GetText( aTextSel );
+ aTextSel.Adjust();
+
+ if (!isSingleScriptType(pEditEngine->GetScriptType(aTextSel)))
+ return false;
+
+ LanguageType nLang = pEditEngine->GetLanguage( aTextSel.nStartPara, aTextSel.nStartPos ).nLang;
+ OUString aLangText( LanguageTag::convertToBcp47( nLang ) );
+
+ // set word and locale to look up as status value
+ rStatusVal = aText + "#" + aLangText;
+ rLang = nLang;
+
+ return aText.getLength() > 0;
+}
+
+
+void ReplaceTextWithSynonym( EditView &rEditView, const OUString &rSynonmText )
+{
+ // get selection to use
+ ESelection aCurSel( rEditView.GetSelection() );
+ if (!rEditView.HasSelection())
+ {
+ // select the same word that was used in GetStatusValueForThesaurusFromContext by calling GetWord.
+ // (In the end both functions will call ImpEditEngine::SelectWord)
+ rEditView.SelectCurrentWord( i18n::WordType::DICTIONARY_WORD );
+ aCurSel = rEditView.GetSelection();
+ }
+
+ // replace word ...
+ rEditView.InsertText( rSynonmText );
+ rEditView.ShowCursor( true, false );
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/outliner/overflowingtxt.cxx b/editeng/source/outliner/overflowingtxt.cxx
new file mode 100644
index 0000000000..42316fa1fa
--- /dev/null
+++ b/editeng/source/outliner/overflowingtxt.cxx
@@ -0,0 +1,227 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+
+#include <editeng/overflowingtxt.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/outlobj.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/editdata.hxx>
+
+#include <editdoc.hxx>
+#include <utility>
+
+
+std::optional<OutlinerParaObject> TextChainingUtils::JuxtaposeParaObject(
+ css::uno::Reference< css::datatransfer::XTransferable > const & xOverflowingContent,
+ Outliner *pOutl,
+ OutlinerParaObject const *pNextPObj)
+{
+ if (!pNextPObj) {
+ pOutl->SetToEmptyText();
+ } else {
+ pOutl->SetText(*pNextPObj);
+ }
+
+ // Special case: if only empty text remove it at the end
+ bool bOnlyOneEmptyPara = !pNextPObj ||
+ (pOutl->GetParagraphCount() == 1 &&
+ pNextPObj->GetTextObject().GetText(0).isEmpty());
+
+ EditEngine &rEditEngine = const_cast<EditEngine &>(pOutl->GetEditEngine());
+
+ // XXX: this code should be moved in Outliner directly
+ // creating Outliner::InsertText(...transferable...)
+ EditSelection aStartSel(rEditEngine.CreateSelection(ESelection(0,0)));
+ EditSelection aNewSel = rEditEngine.InsertText(xOverflowingContent,
+ OUString(),
+ aStartSel.Min(),
+ true);
+
+ if (!bOnlyOneEmptyPara) {
+ // Separate Paragraphs
+ rEditEngine.InsertParaBreak(aNewSel);
+ }
+
+
+ return pOutl->CreateParaObject();
+}
+
+std::optional<OutlinerParaObject> TextChainingUtils::DeeplyMergeParaObject(
+ css::uno::Reference< css::datatransfer::XTransferable > const & xOverflowingContent,
+ Outliner *pOutl,
+ OutlinerParaObject const *pNextPObj)
+{
+ if (!pNextPObj) {
+ pOutl->SetToEmptyText();
+ } else {
+ pOutl->SetText(*pNextPObj);
+ }
+
+ EditEngine &rEditEngine = const_cast<EditEngine &>(pOutl->GetEditEngine());
+
+ // XXX: this code should be moved in Outliner directly
+ // creating Outliner::InsertText(...transferable...)
+ EditSelection aStartSel(rEditEngine.CreateSelection(ESelection(0,0)));
+ // We don't need to mark the selection
+ // EditSelection aNewSel =
+ rEditEngine.InsertText(xOverflowingContent,
+ OUString(),
+ aStartSel.Min(),
+ true);
+
+ return pOutl->CreateParaObject();
+}
+
+css::uno::Reference< css::datatransfer::XTransferable > TextChainingUtils::CreateTransferableFromText(Outliner const *pOutl)
+{
+ const EditEngine &rEditEngine = pOutl->GetEditEngine();
+ sal_Int32 nLastPara = pOutl->GetParagraphCount()-1;
+ ESelection aWholeTextSel(0, 0, nLastPara, rEditEngine.GetTextLen(nLastPara));
+
+ return rEditEngine.CreateTransferable(aWholeTextSel);
+}
+
+
+
+OverflowingText::OverflowingText(css::uno::Reference< css::datatransfer::XTransferable > xOverflowingContent) :
+ mxOverflowingContent(std::move(xOverflowingContent))
+{
+
+}
+
+
+
+NonOverflowingText::NonOverflowingText(const ESelection &aSel, bool bLastParaInterrupted)
+ : maContentSel(aSel)
+ , mbLastParaInterrupted(bLastParaInterrupted)
+{
+}
+
+bool NonOverflowingText::IsLastParaInterrupted() const
+{
+ return mbLastParaInterrupted;
+}
+
+
+std::optional<OutlinerParaObject> NonOverflowingText::RemoveOverflowingText(Outliner *pOutliner) const
+{
+ pOutliner->QuickDelete(maContentSel);
+ SAL_INFO("editeng.chaining", "Deleting selection from (Para: " << maContentSel.nStartPara
+ << ", Pos: " << maContentSel.nStartPos << ") to (Para: " << maContentSel.nEndPara
+ << ", Pos: " << maContentSel.nEndPos << ")");
+ return pOutliner->CreateParaObject();
+}
+
+ESelection NonOverflowingText::GetOverflowPointSel() const
+{
+ //return getLastPositionSel(mpContentTextObj);
+
+ // return the starting point of the selection we are removing
+ return ESelection(maContentSel.nStartPara, maContentSel.nStartPos); //XXX
+}
+
+// The equivalent of ToParaObject for OverflowingText. Here we are prepending the overflowing text to the old dest box's text
+// XXX: In a sense a better name for OverflowingText and NonOverflowingText are respectively DestLinkText and SourceLinkText
+std::optional<OutlinerParaObject> OverflowingText::JuxtaposeParaObject(Outliner *pOutl, OutlinerParaObject const *pNextPObj)
+{
+ return TextChainingUtils::JuxtaposeParaObject(mxOverflowingContent, pOutl, pNextPObj);
+}
+
+std::optional<OutlinerParaObject> OverflowingText::DeeplyMergeParaObject(Outliner *pOutl, OutlinerParaObject const *pNextPObj)
+{
+ return TextChainingUtils::DeeplyMergeParaObject(mxOverflowingContent, pOutl, pNextPObj);
+}
+
+
+OFlowChainedText::OFlowChainedText(Outliner const *pOutl, bool bIsDeepMerge)
+{
+ mpOverflowingTxt = pOutl->GetOverflowingText();
+ mpNonOverflowingTxt = pOutl->GetNonOverflowingText();
+
+ mbIsDeepMerge = bIsDeepMerge;
+}
+
+OFlowChainedText::~OFlowChainedText()
+{
+}
+
+
+ESelection OFlowChainedText::GetOverflowPointSel() const
+{
+ return mpNonOverflowingTxt->GetOverflowPointSel();
+}
+
+std::optional<OutlinerParaObject> OFlowChainedText::InsertOverflowingText(Outliner *pOutliner, OutlinerParaObject const *pTextToBeMerged)
+{
+ // Just return the roughly merged paras for now
+ if (!mpOverflowingTxt)
+ return std::nullopt;
+
+ if (mbIsDeepMerge) {
+ SAL_INFO("editeng.chaining", "[TEXTCHAINFLOW - OF] Deep merging paras" );
+ return mpOverflowingTxt->DeeplyMergeParaObject(pOutliner, pTextToBeMerged );
+ } else {
+ SAL_INFO("editeng.chaining", "[TEXTCHAINFLOW - OF] Juxtaposing paras" );
+ return mpOverflowingTxt->JuxtaposeParaObject(pOutliner, pTextToBeMerged );
+ }
+}
+
+
+std::optional<OutlinerParaObject> OFlowChainedText::RemoveOverflowingText(Outliner *pOutliner)
+{
+ if (!mpNonOverflowingTxt)
+ return std::nullopt;
+
+ return mpNonOverflowingTxt->RemoveOverflowingText(pOutliner);
+}
+
+bool OFlowChainedText::IsLastParaInterrupted() const
+{
+ return mpNonOverflowingTxt->IsLastParaInterrupted();
+}
+
+
+
+UFlowChainedText::UFlowChainedText(Outliner const *pOutl, bool bIsDeepMerge)
+{
+ mxUnderflowingTxt = TextChainingUtils::CreateTransferableFromText(pOutl);
+ mbIsDeepMerge = bIsDeepMerge;
+}
+
+std::optional<OutlinerParaObject> UFlowChainedText::CreateMergedUnderflowParaObject(Outliner *pOutl, OutlinerParaObject const *pNextLinkWholeText)
+{
+ std::optional<OutlinerParaObject> pNewText;
+
+ if (mbIsDeepMerge) {
+ SAL_INFO("editeng.chaining", "[TEXTCHAINFLOW - UF] Deep merging paras" );
+ pNewText = TextChainingUtils::DeeplyMergeParaObject(mxUnderflowingTxt, pOutl, pNextLinkWholeText);
+ } else {
+ // NewTextForCurBox = Txt(CurBox) ++ Txt(NextBox)
+ SAL_INFO("editeng.chaining", "[TEXTCHAINFLOW - UF] Juxtaposing paras" );
+ pNewText = TextChainingUtils::JuxtaposeParaObject(mxUnderflowingTxt, pOutl, pNextLinkWholeText);
+ }
+
+ return pNewText;
+
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/outliner/paralist.cxx b/editeng/source/outliner/paralist.cxx
new file mode 100644
index 0000000000..5b9f214498
--- /dev/null
+++ b/editeng/source/outliner/paralist.cxx
@@ -0,0 +1,256 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "paralist.hxx"
+
+#include <editeng/outliner.hxx>
+#include <editeng/numdef.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+#include <libxml/xmlwriter.h>
+
+ParagraphData::ParagraphData()
+: nDepth( -1 )
+, mnNumberingStartValue( -1 )
+, mbParaIsNumberingRestart( false )
+{
+}
+
+bool ParagraphData::operator==(const ParagraphData& rCandidate) const
+{
+ return (nDepth == rCandidate.nDepth
+ && mnNumberingStartValue == rCandidate.mnNumberingStartValue
+ && mbParaIsNumberingRestart == rCandidate.mbParaIsNumberingRestart);
+}
+
+Paragraph::Paragraph( sal_Int16 nDDepth )
+: aBulSize( -1, -1)
+{
+
+ DBG_ASSERT( ( nDDepth >= -1 ) && ( nDDepth < SVX_MAX_NUM ), "Paragraph-CTOR: nDepth invalid!" );
+
+ nDepth = nDDepth;
+ nFlags = ParaFlag::NONE;
+ bVisible = true;
+}
+
+Paragraph::Paragraph( const ParagraphData& rData )
+: aBulSize( -1, -1)
+, nFlags( ParaFlag::NONE )
+, bVisible( true )
+{
+ nDepth = rData.nDepth;
+ mnNumberingStartValue = rData.mnNumberingStartValue;
+ mbParaIsNumberingRestart = rData.mbParaIsNumberingRestart;
+}
+
+Paragraph::~Paragraph()
+{
+}
+
+void Paragraph::SetNumberingStartValue( sal_Int16 nNumberingStartValue )
+{
+ mnNumberingStartValue = nNumberingStartValue;
+ if( mnNumberingStartValue != -1 )
+ mbParaIsNumberingRestart = true;
+}
+
+void Paragraph::SetParaIsNumberingRestart( bool bParaIsNumberingRestart )
+{
+ mbParaIsNumberingRestart = bParaIsNumberingRestart;
+ if( !mbParaIsNumberingRestart )
+ mnNumberingStartValue = -1;
+}
+
+void Paragraph::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("Paragraph"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("nDepth"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(nDepth));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("mnNumberingStartValue"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(mnNumberingStartValue));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("mbParaIsNumberingRestart"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(mbParaIsNumberingRestart));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void ParagraphList::Clear()
+{
+ maEntries.clear();
+}
+
+void ParagraphList::Append( std::unique_ptr<Paragraph> pPara)
+{
+ SAL_WARN_IF( maEntries.size() >= EE_PARA_MAX_COUNT, "editeng", "ParagraphList::Append - overflow");
+ maEntries.push_back(std::move(pPara));
+}
+
+void ParagraphList::Insert( std::unique_ptr<Paragraph> pPara, sal_Int32 nAbsPos)
+{
+ SAL_WARN_IF( nAbsPos < 0 || (maEntries.size() < o3tl::make_unsigned(nAbsPos) && nAbsPos != EE_PARA_APPEND),
+ "editeng", "ParagraphList::Insert - bad insert position " << nAbsPos);
+ SAL_WARN_IF( maEntries.size() >= EE_PARA_MAX_COUNT, "editeng", "ParagraphList::Insert - overflow");
+
+ if (nAbsPos < 0 || maEntries.size() <= o3tl::make_unsigned(nAbsPos))
+ Append( std::move(pPara) );
+ else
+ maEntries.insert(maEntries.begin()+nAbsPos, std::move(pPara));
+}
+
+void ParagraphList::Remove( sal_Int32 nPara )
+{
+ if (nPara < 0 || maEntries.size() <= o3tl::make_unsigned(nPara))
+ {
+ SAL_WARN( "editeng", "ParagraphList::Remove - out of bounds " << nPara);
+ return;
+ }
+
+ maEntries.erase(maEntries.begin() + nPara );
+}
+
+void ParagraphList::MoveParagraphs( sal_Int32 nStart, sal_Int32 nDest, sal_Int32 _nCount )
+{
+ OSL_ASSERT(o3tl::make_unsigned(nStart) < maEntries.size() && o3tl::make_unsigned(nDest) < maEntries.size());
+
+ if ( (( nDest < nStart ) || ( nDest >= ( nStart + _nCount ) )) && nStart >= 0 && nDest >= 0 && _nCount >= 0 )
+ {
+ std::vector<std::unique_ptr<Paragraph>> aParas;
+ auto iterBeg = maEntries.begin() + nStart;
+ auto iterEnd = iterBeg + _nCount;
+
+ for (auto it = iterBeg; it != iterEnd; ++it)
+ aParas.push_back(std::move(*it));
+
+ maEntries.erase(iterBeg,iterEnd);
+
+ if ( nDest > nStart )
+ nDest -= _nCount;
+
+ for (auto & i : aParas)
+ {
+ maEntries.insert(maEntries.begin() + nDest, std::move(i));
+ ++nDest;
+ }
+ }
+ else
+ {
+ OSL_FAIL( "MoveParagraphs: Invalid Parameters" );
+ }
+}
+
+bool ParagraphList::HasChildren( Paragraph const * pParagraph ) const
+{
+ sal_Int32 n = GetAbsPos( pParagraph );
+ Paragraph* pNext = GetParagraph( ++n );
+ return pNext && ( pNext->GetDepth() > pParagraph->GetDepth() );
+}
+
+bool ParagraphList::HasHiddenChildren( Paragraph const * pParagraph ) const
+{
+ sal_Int32 n = GetAbsPos( pParagraph );
+ Paragraph* pNext = GetParagraph( ++n );
+ return pNext && ( pNext->GetDepth() > pParagraph->GetDepth() ) && !pNext->IsVisible();
+}
+
+bool ParagraphList::HasVisibleChildren( Paragraph const * pParagraph ) const
+{
+ sal_Int32 n = GetAbsPos( pParagraph );
+ Paragraph* pNext = GetParagraph( ++n );
+ return pNext && ( pNext->GetDepth() > pParagraph->GetDepth() ) && pNext->IsVisible();
+}
+
+sal_Int32 ParagraphList::GetChildCount( Paragraph const * pParent ) const
+{
+ sal_Int32 nChildCount = 0;
+ sal_Int32 n = GetAbsPos( pParent );
+ Paragraph* pPara = GetParagraph( ++n );
+ while ( pPara && ( pPara->GetDepth() > pParent->GetDepth() ) )
+ {
+ nChildCount++;
+ pPara = GetParagraph( ++n );
+ }
+ return nChildCount;
+}
+
+Paragraph* ParagraphList::GetParent( Paragraph const * pParagraph ) const
+{
+ sal_Int32 n = GetAbsPos( pParagraph );
+ Paragraph* pPrev = GetParagraph( --n );
+ while ( pPrev && ( pPrev->GetDepth() >= pParagraph->GetDepth() ) )
+ {
+ pPrev = GetParagraph( --n );
+ }
+
+ return pPrev;
+}
+
+void ParagraphList::Expand( Paragraph const * pParent )
+{
+ sal_Int32 nChildCount = GetChildCount( pParent );
+ sal_Int32 nPos = GetAbsPos( pParent );
+
+ for ( sal_Int32 n = 1; n <= nChildCount; n++ )
+ {
+ Paragraph* pPara = GetParagraph( nPos+n );
+ if ( !( pPara->IsVisible() ) )
+ {
+ pPara->bVisible = true;
+ aVisibleStateChangedHdl.Call( *pPara );
+ }
+ }
+}
+
+void ParagraphList::Collapse( Paragraph const * pParent )
+{
+ sal_Int32 nChildCount = GetChildCount( pParent );
+ sal_Int32 nPos = GetAbsPos( pParent );
+
+ for ( sal_Int32 n = 1; n <= nChildCount; n++ )
+ {
+ Paragraph* pPara = GetParagraph( nPos+n );
+ if ( pPara->IsVisible() )
+ {
+ pPara->bVisible = false;
+ aVisibleStateChangedHdl.Call( *pPara );
+ }
+ }
+}
+
+sal_Int32 ParagraphList::GetAbsPos( Paragraph const * pParent ) const
+{
+ sal_Int32 pos = 0;
+ for (auto const& entry : maEntries)
+ {
+ if (entry.get() == pParent)
+ return pos;
+ ++pos;
+ }
+
+ return EE_PARA_NOT_FOUND;
+}
+
+void ParagraphList::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("ParagraphList"));
+ for (auto const & pParagraph : maEntries)
+ pParagraph->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/outliner/paralist.hxx b/editeng/source/outliner/paralist.hxx
new file mode 100644
index 0000000000..47413ff5ff
--- /dev/null
+++ b/editeng/source/outliner/paralist.hxx
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <memory>
+#include <vector>
+
+#include <editeng/outliner.hxx>
+#include <o3tl/safeint.hxx>
+#include <tools/link.hxx>
+
+class Paragraph;
+typedef struct _xmlTextWriter* xmlTextWriterPtr;
+
+class ParagraphList
+{
+public:
+ void Clear();
+
+ sal_Int32 GetParagraphCount() const
+ {
+ size_t nSize = maEntries.size();
+ if (nSize > SAL_MAX_INT32)
+ {
+ SAL_WARN( "editeng", "ParagraphList::GetParagraphCount - overflow " << nSize);
+ return SAL_MAX_INT32;
+ }
+ return nSize;
+ }
+
+ Paragraph* GetParagraph( sal_Int32 nPos ) const
+ {
+ return 0 <= nPos && o3tl::make_unsigned(nPos) < maEntries.size() ? maEntries[nPos].get() : nullptr;
+ }
+
+ sal_Int32 GetAbsPos( Paragraph const * pParent ) const;
+
+ void Append( std::unique_ptr<Paragraph> pPara);
+ void Insert( std::unique_ptr<Paragraph> pPara, sal_Int32 nAbsPos);
+ void Remove( sal_Int32 nPara );
+ void MoveParagraphs( sal_Int32 nStart, sal_Int32 nDest, sal_Int32 nCount );
+
+ Paragraph* GetParent( Paragraph const * pParagraph ) const;
+ bool HasChildren( Paragraph const * pParagraph ) const;
+ bool HasHiddenChildren( Paragraph const * pParagraph ) const;
+ bool HasVisibleChildren( Paragraph const * pParagraph ) const;
+ sal_Int32 GetChildCount( Paragraph const * pParagraph ) const;
+
+ void Expand( Paragraph const * pParent );
+ void Collapse( Paragraph const * pParent );
+
+ void SetVisibleStateChangedHdl( const Link<Paragraph&,void>& rLink ) { aVisibleStateChangedHdl = rLink; }
+
+ void dumpAsXml(xmlTextWriterPtr pWriter) const;
+
+private:
+
+ Link<Paragraph&,void> aVisibleStateChangedHdl;
+ std::vector<std::unique_ptr<Paragraph>> maEntries;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/rtf/rtfitem.cxx b/editeng/source/rtf/rtfitem.cxx
new file mode 100644
index 0000000000..bf6b002f97
--- /dev/null
+++ b/editeng/source/rtf/rtfitem.cxx
@@ -0,0 +1,1873 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <editeng/fontitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/autokernitem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/kernitem.hxx>
+#include <editeng/cmapitem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/twolinesitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/keepitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/lspcitem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/tstpitem.hxx>
+#include <editeng/spltitem.hxx>
+#include <editeng/hyphenzoneitem.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <editeng/charrotateitem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/paravertalignitem.hxx>
+#include <editeng/forbiddenruleitem.hxx>
+#include <editeng/hngpnctitem.hxx>
+#include <editeng/scriptspaceitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/charhiddenitem.hxx>
+
+#include <svtools/rtftoken.h>
+#include <svl/itempool.hxx>
+#include <svl/itemiter.hxx>
+#include <sal/log.hxx>
+#include <vcl/font.hxx>
+
+#include <editeng/svxrtf.hxx>
+#include <editeng/editids.hrc>
+
+#include <limits.h>
+
+#define BRACELEFT '{'
+#define BRACERIGHT '}'
+
+using namespace ::com::sun::star;
+using namespace editeng;
+
+void SvxRTFParser::SetScriptAttr( RTF_CharTypeDef eType, SfxItemSet& rSet,
+ SfxPoolItem& rItem )
+{
+ std::optional<sal_uInt16> pNormal;
+ std::optional<sal_uInt16> pCJK;
+ std::optional<sal_uInt16> pCTL;
+ switch( rItem.Which() )
+ {
+ case SID_ATTR_CHAR_FONT:
+ pNormal = aPlainMap[SID_ATTR_CHAR_FONT];
+ pCJK = aPlainMap[SID_ATTR_CHAR_CJK_FONT];
+ pCTL = aPlainMap[SID_ATTR_CHAR_CTL_FONT];
+ break;
+
+ case SID_ATTR_CHAR_FONTHEIGHT:
+ pNormal = aPlainMap[SID_ATTR_CHAR_FONTHEIGHT];
+ pCJK = aPlainMap[SID_ATTR_CHAR_CJK_FONTHEIGHT];
+ pCTL = aPlainMap[SID_ATTR_CHAR_CTL_FONTHEIGHT];
+ break;
+
+ case SID_ATTR_CHAR_POSTURE:
+ pNormal = aPlainMap[SID_ATTR_CHAR_POSTURE];
+ pCJK = aPlainMap[SID_ATTR_CHAR_CJK_POSTURE];
+ pCTL = aPlainMap[SID_ATTR_CHAR_CTL_POSTURE];
+ break;
+
+ case SID_ATTR_CHAR_WEIGHT:
+ pNormal = aPlainMap[SID_ATTR_CHAR_WEIGHT];
+ pCJK = aPlainMap[SID_ATTR_CHAR_CJK_WEIGHT];
+ pCTL = aPlainMap[SID_ATTR_CHAR_CTL_WEIGHT];
+ break;
+
+ case SID_ATTR_CHAR_LANGUAGE:
+ pNormal = aPlainMap[SID_ATTR_CHAR_LANGUAGE];
+ pCJK = aPlainMap[SID_ATTR_CHAR_CJK_LANGUAGE];
+ pCTL = aPlainMap[SID_ATTR_CHAR_CTL_LANGUAGE];
+ break;
+
+ case 0:
+ // it exist no WhichId - don't set this item
+ break;
+
+ default:
+ rSet.Put( rItem );
+ break;
+ }
+
+ if( DOUBLEBYTE_CHARTYPE == eType )
+ {
+ if( bIsLeftToRightDef && pCJK )
+ {
+ rItem.SetWhich( *pCJK );
+ rSet.Put( rItem );
+ }
+ }
+ else if( !bIsLeftToRightDef )
+ {
+ if( pCTL )
+ {
+ rItem.SetWhich( *pCTL );
+ rSet.Put( rItem );
+ }
+ }
+ else
+ {
+ if( LOW_CHARTYPE == eType )
+ {
+ if( pNormal )
+ {
+ rItem.SetWhich( *pNormal );
+ rSet.Put( rItem );
+ }
+ }
+ else if( HIGH_CHARTYPE == eType )
+ {
+ if( pCTL )
+ {
+ rItem.SetWhich( *pCTL );
+ rSet.Put( rItem );
+ }
+ }
+ else
+ {
+ if( pCJK )
+ {
+ rItem.SetWhich( *pCJK );
+ rSet.Put( rItem );
+ }
+ if( pCTL )
+ {
+ rItem.SetWhich( *pCTL );
+ rSet.Put( rItem );
+ }
+ if( pNormal )
+ {
+ rItem.SetWhich( *pNormal );
+ rSet.Put( rItem );
+ }
+ }
+ }
+}
+
+
+void SvxRTFParser::ReadAttr( int nToken, SfxItemSet* pSet )
+{
+ DBG_ASSERT( pSet, "A SfxItemSet has to be provided as argument!" );
+ bool bFirstToken = true;
+ bool bContinue = true;
+ FontLineStyle eUnderline;
+ FontLineStyle eOverline;
+ FontEmphasisMark eEmphasis;
+ RTF_CharTypeDef eCharType = NOTDEF_CHARTYPE;
+ SvxParaVertAlignItem::Align nFontAlign;
+
+ bool bChkStkPos = !bNewGroup && !aAttrStack.empty();
+
+ while( bContinue && IsParserWorking() ) // as long as known Attribute are recognized
+ {
+ switch( nToken )
+ {
+ case RTF_PARD:
+ RTFPardPlain( true, &pSet );
+ break;
+
+ case RTF_PLAIN:
+ RTFPardPlain( false, &pSet );
+ break;
+
+ default:
+ do { // middle checked loop
+ if( !bChkStkPos )
+ break;
+
+ SvxRTFItemStackType* pCurrent = aAttrStack.empty() ? nullptr : aAttrStack.back().get();
+ if( !pCurrent || (pCurrent->mxStartNodeIdx->GetIdx() == mxInsertPosition->GetNodeIdx() &&
+ pCurrent->nSttCnt == mxInsertPosition->GetCntIdx() ))
+ break;
+
+ int nLastToken = GetStackPtr(-1)->nTokenId;
+ if( RTF_PARD == nLastToken || RTF_PLAIN == nLastToken )
+ break;
+
+ if (pCurrent->aAttrSet.Count() || !pCurrent->maChildList.empty() ||
+ pCurrent->nStyleNo )
+ {
+ // Open a new Group
+ auto xNew(std::make_unique<SvxRTFItemStackType>(*pCurrent, *mxInsertPosition, true));
+ xNew->SetRTFDefaults( GetRTFDefaults() );
+
+ // "Set" all valid attributes up until this point
+ AttrGroupEnd();
+ pCurrent = aAttrStack.empty() ? nullptr : aAttrStack.back().get(); // can be changed after AttrGroupEnd!
+ xNew->aAttrSet.SetParent( pCurrent ? &pCurrent->aAttrSet : nullptr );
+
+ aAttrStack.push_back( std::move(xNew) );
+ pCurrent = aAttrStack.back().get();
+ }
+ else
+ // continue to use this entry as a new one
+ pCurrent->SetStartPos( *mxInsertPosition );
+
+ pSet = &pCurrent->aAttrSet;
+ } while( false );
+
+ switch( nToken )
+ {
+ case RTF_INTBL:
+ case RTF_PAGEBB:
+ case RTF_SBYS:
+ case RTF_CS:
+ case RTF_LS:
+ case RTF_ILVL:
+ UnknownAttrToken( nToken );
+ break;
+
+ case RTF_S:
+ if( bIsInReadStyleTab )
+ {
+ if( !bFirstToken )
+ SkipToken();
+ bContinue = false;
+ }
+ else
+ {
+ sal_uInt16 nStyleNo = -1 == nTokenValue ? 0 : sal_uInt16(nTokenValue);
+ // set StyleNo to the current style on the AttrStack
+ SvxRTFItemStackType* pCurrent = aAttrStack.empty() ? nullptr : aAttrStack.back().get();
+ if( !pCurrent )
+ break;
+
+ pCurrent->nStyleNo = nStyleNo;
+ }
+ break;
+
+ case RTF_KEEP:
+ if (const TypedWhichId<SvxFormatSplitItem> wid = aPardMap[SID_ATTR_PARA_SPLIT])
+ {
+ pSet->Put(SvxFormatSplitItem(false, wid));
+ }
+ break;
+
+ case RTF_KEEPN:
+ if (const TypedWhichId<SvxFormatKeepItem> wid = aPardMap[SID_ATTR_PARA_KEEP])
+ {
+ pSet->Put(SvxFormatKeepItem(true, wid));
+ }
+ break;
+
+ case RTF_LEVEL:
+ if (const TypedWhichId<SfxInt16Item> wid = aPardMap[SID_ATTR_PARA_OUTLLEVEL])
+ {
+ pSet->Put(SfxInt16Item(wid, static_cast<sal_uInt16>(nTokenValue)));
+ }
+ break;
+
+ case RTF_QL:
+ if (const TypedWhichId<SvxAdjustItem> wid = aPardMap[SID_ATTR_PARA_ADJUST])
+ {
+ pSet->Put(SvxAdjustItem(SvxAdjust::Left, wid));
+ }
+ break;
+ case RTF_QR:
+ if (const TypedWhichId<SvxAdjustItem> wid = aPardMap[SID_ATTR_PARA_ADJUST])
+ {
+ pSet->Put(SvxAdjustItem(SvxAdjust::Right, wid));
+ }
+ break;
+ case RTF_QJ:
+ if (const TypedWhichId<SvxAdjustItem> wid = aPardMap[SID_ATTR_PARA_ADJUST])
+ {
+ pSet->Put(SvxAdjustItem(SvxAdjust::Block, wid));
+ }
+ break;
+ case RTF_QC:
+ if (const TypedWhichId<SvxAdjustItem> wid = aPardMap[SID_ATTR_PARA_ADJUST])
+ {
+ pSet->Put(SvxAdjustItem(SvxAdjust::Center, wid));
+ }
+ break;
+
+ case RTF_FI:
+ if (const TypedWhichId<SvxLRSpaceItem> wid = aPardMap[SID_ATTR_LRSPACE])
+ {
+ SvxLRSpaceItem aLR(pSet->Get(wid));
+ sal_uInt16 nSz = 0;
+ if( -1 != nTokenValue )
+ {
+ if( IsCalcValue() )
+ CalcValue();
+ nSz = sal_uInt16(nTokenValue);
+ }
+ aLR.SetTextFirstLineOffset( nSz );
+ pSet->Put( aLR );
+ }
+ break;
+
+ case RTF_LI:
+ case RTF_LIN:
+ if (const TypedWhichId<SvxLRSpaceItem> wid = aPardMap[SID_ATTR_LRSPACE])
+ {
+ SvxLRSpaceItem aLR(pSet->Get(wid));
+ sal_uInt16 nSz = 0;
+ if( 0 < nTokenValue )
+ {
+ if( IsCalcValue() )
+ CalcValue();
+ nSz = sal_uInt16(nTokenValue);
+ }
+ aLR.SetTextLeft( nSz );
+ pSet->Put( aLR );
+ }
+ break;
+
+ case RTF_RI:
+ case RTF_RIN:
+ if (const TypedWhichId<SvxLRSpaceItem> wid = aPardMap[SID_ATTR_LRSPACE])
+ {
+ SvxLRSpaceItem aLR(pSet->Get(wid));
+ sal_uInt16 nSz = 0;
+ if( 0 < nTokenValue )
+ {
+ if( IsCalcValue() )
+ CalcValue();
+ nSz = sal_uInt16(nTokenValue);
+ }
+ aLR.SetRight( nSz );
+ pSet->Put( aLR );
+ }
+ break;
+
+ case RTF_SB:
+ if (const TypedWhichId<SvxULSpaceItem> wid = aPardMap[SID_ATTR_ULSPACE])
+ {
+ SvxULSpaceItem aUL(pSet->Get(wid));
+ sal_uInt16 nSz = 0;
+ if( 0 < nTokenValue )
+ {
+ if( IsCalcValue() )
+ CalcValue();
+ nSz = sal_uInt16(nTokenValue);
+ }
+ aUL.SetUpper( nSz );
+ pSet->Put( aUL );
+ }
+ break;
+
+ case RTF_SA:
+ if (const TypedWhichId<SvxULSpaceItem> wid = aPardMap[SID_ATTR_ULSPACE])
+ {
+ SvxULSpaceItem aUL(pSet->Get(wid));
+ sal_uInt16 nSz = 0;
+ if( 0 < nTokenValue )
+ {
+ if( IsCalcValue() )
+ CalcValue();
+ nSz = sal_uInt16(nTokenValue);
+ }
+ aUL.SetLower( nSz );
+ pSet->Put( aUL );
+ }
+ break;
+
+ case RTF_SLMULT:
+ if (const TypedWhichId<SvxLineSpacingItem> wid = aPardMap[SID_ATTR_PARA_LINESPACE];
+ wid && 1 == nTokenValue)
+ {
+ // then switches to multi-line!
+ SvxLineSpacingItem aLSpace(pSet->Get(wid, false));
+
+ // how much do you get from the line height value?
+
+ // Proportional-Size:
+ // Ie, the ratio is (n / 240) twips
+
+ nTokenValue = 240;
+ if( IsCalcValue() )
+ CalcValue();
+
+ nTokenValue = short( 100 * aLSpace.GetLineHeight() / nTokenValue );
+
+ aLSpace.SetPropLineSpace( static_cast<sal_uInt16>(nTokenValue) );
+ aLSpace.SetLineSpaceRule( SvxLineSpaceRule::Auto );
+
+ pSet->Put( aLSpace );
+ }
+ break;
+
+ case RTF_SL:
+ if (const TypedWhichId<SvxLineSpacingItem> wid = aPardMap[SID_ATTR_PARA_LINESPACE])
+ {
+ // Calculate the ratio between the default font and the
+ // specified size. The distance consists of the line height
+ // (100%) and the space above the line (20%).
+ SvxLineSpacingItem aLSpace(0, wid);
+
+ nTokenValue = !bTokenHasValue ? 0 : nTokenValue;
+ if (1000 == nTokenValue )
+ nTokenValue = 240;
+
+ SvxLineSpaceRule eLnSpc;
+ if (nTokenValue < 0)
+ {
+ eLnSpc = SvxLineSpaceRule::Fix;
+ nTokenValue = -nTokenValue;
+ }
+ else if (nTokenValue == 0)
+ {
+ //if \sl0 is used, the line spacing is automatically
+ //determined
+ eLnSpc = SvxLineSpaceRule::Auto;
+ }
+ else
+ eLnSpc = SvxLineSpaceRule::Min;
+
+ if (IsCalcValue())
+ CalcValue();
+
+ if (eLnSpc != SvxLineSpaceRule::Auto)
+ aLSpace.SetLineHeight( static_cast<sal_uInt16>(nTokenValue) );
+
+ aLSpace.SetLineSpaceRule(eLnSpc);
+ pSet->Put(aLSpace);
+ }
+ break;
+
+ case RTF_NOCWRAP:
+ if (const TypedWhichId<SvxForbiddenRuleItem> wid = aPardMap[SID_ATTR_PARA_FORBIDDEN_RULES])
+ {
+ pSet->Put(SvxForbiddenRuleItem(false, wid));
+ }
+ break;
+ case RTF_NOOVERFLOW:
+ if (const TypedWhichId<SvxHangingPunctuationItem> wid = aPardMap[SID_ATTR_PARA_HANGPUNCTUATION])
+ {
+ pSet->Put(SvxHangingPunctuationItem(false, wid));
+ }
+ break;
+
+ case RTF_ASPALPHA:
+ if (const TypedWhichId<SvxScriptSpaceItem> wid = aPardMap[SID_ATTR_PARA_SCRIPTSPACE])
+ {
+ pSet->Put(SvxScriptSpaceItem(true, wid));
+ }
+ break;
+
+ case RTF_FAFIXED:
+ case RTF_FAAUTO: nFontAlign = SvxParaVertAlignItem::Align::Automatic;
+ goto SET_FONTALIGNMENT;
+ case RTF_FAHANG: nFontAlign = SvxParaVertAlignItem::Align::Top;
+ goto SET_FONTALIGNMENT;
+ case RTF_FAVAR: nFontAlign = SvxParaVertAlignItem::Align::Bottom;
+ goto SET_FONTALIGNMENT;
+ case RTF_FACENTER: nFontAlign = SvxParaVertAlignItem::Align::Center;
+ goto SET_FONTALIGNMENT;
+ case RTF_FAROMAN: nFontAlign = SvxParaVertAlignItem::Align::Baseline;
+ goto SET_FONTALIGNMENT;
+SET_FONTALIGNMENT:
+ if (const TypedWhichId<SvxParaVertAlignItem> wid = aPardMap[SID_PARA_VERTALIGN])
+ {
+ pSet->Put(SvxParaVertAlignItem(nFontAlign, wid));
+ }
+ break;
+
+ case RTF_B:
+ case RTF_AB:
+ if( IsAttrSttPos() ) // not in the text flow?
+ {
+
+ SvxWeightItem aTmpItem(
+ nTokenValue ? WEIGHT_BOLD : WEIGHT_NORMAL,
+ SID_ATTR_CHAR_WEIGHT );
+ SetScriptAttr( eCharType, *pSet, aTmpItem);
+ }
+ break;
+
+ case RTF_CAPS:
+ case RTF_SCAPS:
+ if (const sal_uInt16 wid = aPlainMap[SID_ATTR_CHAR_CASEMAP];
+ wid && IsAttrSttPos()) // not in the text flow?
+ {
+ SvxCaseMap eCaseMap;
+ if( !nTokenValue )
+ eCaseMap = SvxCaseMap::NotMapped;
+ else if( RTF_CAPS == nToken )
+ eCaseMap = SvxCaseMap::Uppercase;
+ else
+ eCaseMap = SvxCaseMap::SmallCaps;
+
+ pSet->Put(SvxCaseMapItem(eCaseMap, wid));
+ }
+ break;
+
+ case RTF_DN:
+ case RTF_SUB:
+ if (const sal_uInt16 nEsc = aPlainMap[SID_ATTR_CHAR_ESCAPEMENT])
+ {
+ if( -1 == nTokenValue )
+ nTokenValue = 6; //RTF default \dn value in half-points
+ if( IsCalcValue() )
+ CalcValue();
+ const SvxEscapementItem& rOld =
+ static_cast<const SvxEscapementItem&>(pSet->Get( nEsc,false));
+ sal_Int16 nEs;
+ sal_uInt8 nProp;
+ if( DFLT_ESC_AUTO_SUPER == rOld.GetEsc() )
+ {
+ nEs = DFLT_ESC_AUTO_SUB;
+ nProp = rOld.GetProportionalHeight();
+ }
+ else
+ {
+ nEs = (nToken == RTF_SUB) ? DFLT_ESC_AUTO_SUB : -nTokenValue;
+ nProp = (nToken == RTF_SUB) ? DFLT_ESC_PROP : 100;
+ }
+ pSet->Put( SvxEscapementItem( nEs, nProp, nEsc ));
+ }
+ break;
+
+ case RTF_NOSUPERSUB:
+ if (const sal_uInt16 nEsc = aPlainMap[SID_ATTR_CHAR_ESCAPEMENT])
+ {
+ pSet->Put( SvxEscapementItem( nEsc ));
+ }
+ break;
+
+ case RTF_EXPND:
+ if (TypedWhichId<SvxKerningItem> wid = aPlainMap[SID_ATTR_CHAR_KERNING])
+ {
+ if( -1 == nTokenValue )
+ nTokenValue = 0;
+ else
+ nTokenValue *= 5;
+ if( IsCalcValue() )
+ CalcValue();
+ pSet->Put(SvxKerningItem(static_cast<short>(nTokenValue), wid));
+ }
+ break;
+
+ case RTF_KERNING:
+ if (const TypedWhichId<SvxAutoKernItem> wid = aPlainMap[SID_ATTR_CHAR_AUTOKERN])
+ {
+ if( -1 == nTokenValue )
+ nTokenValue = 0;
+ else
+ nTokenValue *= 10;
+ if( IsCalcValue() )
+ CalcValue();
+ pSet->Put(SvxAutoKernItem(0 != nTokenValue, wid));
+ }
+ break;
+
+ case RTF_EXPNDTW:
+ if (TypedWhichId<SvxKerningItem> wid = aPlainMap[SID_ATTR_CHAR_KERNING])
+ {
+ if( -1 == nTokenValue )
+ nTokenValue = 0;
+ if( IsCalcValue() )
+ CalcValue();
+ pSet->Put(SvxKerningItem(static_cast<short>(nTokenValue), wid));
+ }
+ break;
+
+ case RTF_F:
+ case RTF_AF:
+ {
+ const vcl::Font& rSVFont = GetFont( sal_uInt16(nTokenValue) );
+ SvxFontItem aTmpItem( rSVFont.GetFamilyType(),
+ rSVFont.GetFamilyName(), rSVFont.GetStyleName(),
+ rSVFont.GetPitch(), rSVFont.GetCharSet(),
+ SID_ATTR_CHAR_FONT );
+ SetScriptAttr( eCharType, *pSet, aTmpItem );
+ if( RTF_F == nToken )
+ {
+ SetEncoding( rSVFont.GetCharSet() );
+ RereadLookahead();
+ }
+ }
+ break;
+
+ case RTF_FS:
+ case RTF_AFS:
+ {
+ if( -1 == nTokenValue )
+ nTokenValue = 240;
+ else
+ nTokenValue *= 10;
+// #i66167#
+// for the SwRTFParser 'IsCalcValue' will be false and for the EditRTFParser
+// the conversion takes now place in EditRTFParser since for other reasons
+// the wrong MapUnit might still be use there
+// if( IsCalcValue() )
+// CalcValue();
+ SvxFontHeightItem aTmpItem(
+ static_cast<sal_uInt16>(nTokenValue), 100,
+ SID_ATTR_CHAR_FONTHEIGHT );
+ SetScriptAttr( eCharType, *pSet, aTmpItem );
+ }
+ break;
+
+ case RTF_I:
+ case RTF_AI:
+ if( IsAttrSttPos() ) // not in the text flow?
+ {
+ SvxPostureItem aTmpItem(
+ nTokenValue ? ITALIC_NORMAL : ITALIC_NONE,
+ SID_ATTR_CHAR_POSTURE );
+ SetScriptAttr( eCharType, *pSet, aTmpItem );
+ }
+ break;
+
+ case RTF_OUTL:
+ if (const TypedWhichId<SvxContourItem> wid = aPlainMap[SID_ATTR_CHAR_CONTOUR];
+ wid && IsAttrSttPos()) // not in the text flow?
+ {
+ pSet->Put(SvxContourItem(nTokenValue != 0, wid));
+ }
+ break;
+
+ case RTF_SHAD:
+ if (const TypedWhichId<SvxShadowedItem> wid = aPlainMap[SID_ATTR_CHAR_SHADOWED];
+ wid && IsAttrSttPos()) // not in the text flow?
+ {
+ pSet->Put(SvxShadowedItem(nTokenValue != 0, wid));
+ }
+ break;
+
+ case RTF_STRIKE:
+ if (const TypedWhichId<SvxCrossedOutItem> wid = aPlainMap[SID_ATTR_CHAR_STRIKEOUT];
+ wid && IsAttrSttPos()) // not in the text flow?
+ {
+ pSet->Put( SvxCrossedOutItem(
+ nTokenValue ? STRIKEOUT_SINGLE : STRIKEOUT_NONE,
+ wid ));
+ }
+ break;
+
+ case RTF_STRIKED:
+ if (const TypedWhichId<SvxCrossedOutItem> wid = aPlainMap[SID_ATTR_CHAR_STRIKEOUT]) // not in the text flow?
+ {
+ pSet->Put( SvxCrossedOutItem(
+ nTokenValue ? STRIKEOUT_DOUBLE : STRIKEOUT_NONE,
+ wid ));
+ }
+ break;
+
+ case RTF_UL:
+ if( !IsAttrSttPos() )
+ break;
+ eUnderline = nTokenValue ? LINESTYLE_SINGLE : LINESTYLE_NONE;
+ goto ATTR_SETUNDERLINE;
+
+ case RTF_ULD:
+ eUnderline = LINESTYLE_DOTTED;
+ goto ATTR_SETUNDERLINE;
+ case RTF_ULDASH:
+ eUnderline = LINESTYLE_DASH;
+ goto ATTR_SETUNDERLINE;
+ case RTF_ULDASHD:
+ eUnderline = LINESTYLE_DASHDOT;
+ goto ATTR_SETUNDERLINE;
+ case RTF_ULDASHDD:
+ eUnderline = LINESTYLE_DASHDOTDOT;
+ goto ATTR_SETUNDERLINE;
+ case RTF_ULDB:
+ eUnderline = LINESTYLE_DOUBLE;
+ goto ATTR_SETUNDERLINE;
+ case RTF_ULNONE:
+ eUnderline = LINESTYLE_NONE;
+ goto ATTR_SETUNDERLINE;
+ case RTF_ULTH:
+ eUnderline = LINESTYLE_BOLD;
+ goto ATTR_SETUNDERLINE;
+ case RTF_ULWAVE:
+ eUnderline = LINESTYLE_WAVE;
+ goto ATTR_SETUNDERLINE;
+ case RTF_ULTHD:
+ eUnderline = LINESTYLE_BOLDDOTTED;
+ goto ATTR_SETUNDERLINE;
+ case RTF_ULTHDASH:
+ eUnderline = LINESTYLE_BOLDDASH;
+ goto ATTR_SETUNDERLINE;
+ case RTF_ULLDASH:
+ eUnderline = LINESTYLE_LONGDASH;
+ goto ATTR_SETUNDERLINE;
+ case RTF_ULTHLDASH:
+ eUnderline = LINESTYLE_BOLDLONGDASH;
+ goto ATTR_SETUNDERLINE;
+ case RTF_ULTHDASHD:
+ eUnderline = LINESTYLE_BOLDDASHDOT;
+ goto ATTR_SETUNDERLINE;
+ case RTF_ULTHDASHDD:
+ eUnderline = LINESTYLE_BOLDDASHDOTDOT;
+ goto ATTR_SETUNDERLINE;
+ case RTF_ULHWAVE:
+ eUnderline = LINESTYLE_BOLDWAVE;
+ goto ATTR_SETUNDERLINE;
+ case RTF_ULULDBWAVE:
+ eUnderline = LINESTYLE_DOUBLEWAVE;
+ goto ATTR_SETUNDERLINE;
+
+ case RTF_ULW:
+ eUnderline = LINESTYLE_SINGLE;
+
+ if (const TypedWhichId<SvxWordLineModeItem> wid = aPlainMap[SID_ATTR_CHAR_WORDLINEMODE])
+ {
+ pSet->Put(SvxWordLineModeItem(true, wid));
+ }
+ goto ATTR_SETUNDERLINE;
+
+ATTR_SETUNDERLINE:
+ if (const sal_uInt16 wid = aPlainMap[SID_ATTR_CHAR_UNDERLINE])
+ {
+ pSet->Put(SvxUnderlineItem(eUnderline, wid));
+ }
+ break;
+
+ case RTF_ULC:
+ if (const sal_uInt16 wid = aPlainMap[SID_ATTR_CHAR_UNDERLINE])
+ {
+ std::unique_ptr<SvxUnderlineItem> aUL(std::make_unique<SvxUnderlineItem>(LINESTYLE_SINGLE, wid));
+ const SfxPoolItem* pItem(nullptr);
+
+ if (SfxItemState::SET == pSet->GetItemState(wid, false, &pItem))
+ {
+ // is switched off ?
+ if( LINESTYLE_NONE == static_cast<const SvxUnderlineItem*>(pItem)->GetLineStyle() )
+ break;
+
+ aUL.reset(static_cast<SvxUnderlineItem*>(pItem->Clone()));
+ }
+ else
+ {
+ aUL.reset(static_cast<SvxUnderlineItem*>(pSet->Get(wid, false).Clone()));
+ }
+
+ if(LINESTYLE_NONE == aUL->GetLineStyle())
+ {
+ aUL->SetLineStyle(LINESTYLE_SINGLE);
+ }
+
+ aUL->SetColor(GetColor(sal_uInt16(nTokenValue)));
+
+ pSet->Put(std::move(aUL));
+ }
+ break;
+
+ case RTF_OL:
+ if( !IsAttrSttPos() )
+ break;
+ eOverline = nTokenValue ? LINESTYLE_SINGLE : LINESTYLE_NONE;
+ goto ATTR_SETOVERLINE;
+
+ case RTF_OLD:
+ eOverline = LINESTYLE_DOTTED;
+ goto ATTR_SETOVERLINE;
+ case RTF_OLDASH:
+ eOverline = LINESTYLE_DASH;
+ goto ATTR_SETOVERLINE;
+ case RTF_OLDASHD:
+ eOverline = LINESTYLE_DASHDOT;
+ goto ATTR_SETOVERLINE;
+ case RTF_OLDASHDD:
+ eOverline = LINESTYLE_DASHDOTDOT;
+ goto ATTR_SETOVERLINE;
+ case RTF_OLDB:
+ eOverline = LINESTYLE_DOUBLE;
+ goto ATTR_SETOVERLINE;
+ case RTF_OLNONE:
+ eOverline = LINESTYLE_NONE;
+ goto ATTR_SETOVERLINE;
+ case RTF_OLTH:
+ eOverline = LINESTYLE_BOLD;
+ goto ATTR_SETOVERLINE;
+ case RTF_OLWAVE:
+ eOverline = LINESTYLE_WAVE;
+ goto ATTR_SETOVERLINE;
+ case RTF_OLTHD:
+ eOverline = LINESTYLE_BOLDDOTTED;
+ goto ATTR_SETOVERLINE;
+ case RTF_OLTHDASH:
+ eOverline = LINESTYLE_BOLDDASH;
+ goto ATTR_SETOVERLINE;
+ case RTF_OLLDASH:
+ eOverline = LINESTYLE_LONGDASH;
+ goto ATTR_SETOVERLINE;
+ case RTF_OLTHLDASH:
+ eOverline = LINESTYLE_BOLDLONGDASH;
+ goto ATTR_SETOVERLINE;
+ case RTF_OLTHDASHD:
+ eOverline = LINESTYLE_BOLDDASHDOT;
+ goto ATTR_SETOVERLINE;
+ case RTF_OLTHDASHDD:
+ eOverline = LINESTYLE_BOLDDASHDOTDOT;
+ goto ATTR_SETOVERLINE;
+ case RTF_OLHWAVE:
+ eOverline = LINESTYLE_BOLDWAVE;
+ goto ATTR_SETOVERLINE;
+ case RTF_OLOLDBWAVE:
+ eOverline = LINESTYLE_DOUBLEWAVE;
+ goto ATTR_SETOVERLINE;
+
+ case RTF_OLW:
+ eOverline = LINESTYLE_SINGLE;
+
+ if (const TypedWhichId<SvxWordLineModeItem> wid = aPlainMap[SID_ATTR_CHAR_WORDLINEMODE])
+ {
+ pSet->Put(SvxWordLineModeItem(true, wid));
+ }
+ goto ATTR_SETOVERLINE;
+
+ATTR_SETOVERLINE:
+ if (const TypedWhichId<SvxOverlineItem> wid = aPlainMap[SID_ATTR_CHAR_OVERLINE])
+ {
+ pSet->Put(SvxOverlineItem(eOverline, wid));
+ }
+ break;
+
+ case RTF_OLC:
+ if (const TypedWhichId<SvxOverlineItem> wid = aPlainMap[SID_ATTR_CHAR_OVERLINE])
+ {
+ std::unique_ptr<SvxOverlineItem> aOL(std::make_unique<SvxOverlineItem>(LINESTYLE_SINGLE, wid));
+ const SfxPoolItem* pItem(nullptr);
+
+ if (SfxItemState::SET == pSet->GetItemState(wid, false, &pItem))
+ {
+ // is switched off ?
+ if( LINESTYLE_NONE == static_cast<const SvxOverlineItem*>(pItem)->GetLineStyle() )
+ break;
+
+ aOL.reset(static_cast<SvxOverlineItem*>(pItem->Clone()));
+ }
+ else
+ {
+ aOL.reset(pSet->Get(wid, false).Clone());
+ }
+
+ if(LINESTYLE_NONE == aOL->GetLineStyle())
+ {
+ aOL->SetLineStyle(LINESTYLE_SINGLE);
+ }
+
+ aOL->SetColor(GetColor(sal_uInt16(nTokenValue)));
+
+ pSet->Put(std::move(aOL));
+ }
+ break;
+
+ case RTF_UP:
+ case RTF_SUPER:
+ if (const sal_uInt16 nEsc = aPlainMap[SID_ATTR_CHAR_ESCAPEMENT])
+ {
+ if( -1 == nTokenValue )
+ nTokenValue = 6; //RTF default \up value in half-points
+ if( IsCalcValue() )
+ CalcValue();
+ const SvxEscapementItem& rOld =
+ static_cast<const SvxEscapementItem&>(pSet->Get( nEsc,false));
+ sal_Int16 nEs;
+ sal_uInt8 nProp;
+ if( DFLT_ESC_AUTO_SUB == rOld.GetEsc() )
+ {
+ nEs = DFLT_ESC_AUTO_SUPER;
+ nProp = rOld.GetProportionalHeight();
+ }
+ else
+ {
+ nEs = (nToken == RTF_SUPER) ? DFLT_ESC_AUTO_SUPER : nTokenValue;
+ nProp = (nToken == RTF_SUPER) ? DFLT_ESC_PROP : 100;
+ }
+ pSet->Put( SvxEscapementItem( nEs, nProp, nEsc ));
+ }
+ break;
+
+ case RTF_CF:
+ if (const sal_uInt16 wid = aPlainMap[SID_ATTR_CHAR_COLOR])
+ {
+ pSet->Put(SvxColorItem(GetColor(sal_uInt16(nTokenValue)), wid));
+ }
+ break;
+ //#i12501# While cb is clearly documented in the rtf spec, word
+ //doesn't accept it at all
+#if 0
+ case RTF_CB:
+ if (const sal_uInt16 wid = aPlainMap[SID_ATTR_BRUSH_CHAR])
+ {
+ pSet->Put(SvxBrushItem(GetColor(sal_uInt16(nTokenValue)), wid));
+ }
+ break;
+#endif
+
+ case RTF_LANG:
+ if (const sal_uInt16 wid = aPlainMap[SID_ATTR_CHAR_LANGUAGE])
+ {
+ pSet->Put(SvxLanguageItem(LanguageType(nTokenValue), wid));
+ }
+ break;
+
+ case RTF_LANGFE:
+ if (const sal_uInt16 wid = aPlainMap[SID_ATTR_CHAR_CJK_LANGUAGE])
+ {
+ pSet->Put(SvxLanguageItem(LanguageType(nTokenValue), wid));
+ }
+ break;
+ case RTF_ALANG:
+ {
+ SvxLanguageItem aTmpItem( LanguageType(nTokenValue),
+ SID_ATTR_CHAR_LANGUAGE );
+ SetScriptAttr( eCharType, *pSet, aTmpItem );
+ }
+ break;
+
+ case RTF_RTLCH:
+ bIsLeftToRightDef = false;
+ break;
+ case RTF_LTRCH:
+ bIsLeftToRightDef = true;
+ break;
+ case RTF_RTLPAR:
+ if (const TypedWhichId<SvxFrameDirectionItem> wid = aPardMap[SID_ATTR_FRAMEDIRECTION])
+ {
+ pSet->Put(SvxFrameDirectionItem(SvxFrameDirection::Horizontal_RL_TB, wid));
+ }
+ break;
+ case RTF_LTRPAR:
+ if (const TypedWhichId<SvxFrameDirectionItem> wid = aPardMap[SID_ATTR_FRAMEDIRECTION])
+ {
+ pSet->Put(SvxFrameDirectionItem(SvxFrameDirection::Horizontal_LR_TB, wid));
+ }
+ break;
+ case RTF_LOCH: eCharType = LOW_CHARTYPE; break;
+ case RTF_HICH: eCharType = HIGH_CHARTYPE; break;
+ case RTF_DBCH: eCharType = DOUBLEBYTE_CHARTYPE; break;
+
+
+ case RTF_ACCNONE:
+ eEmphasis = FontEmphasisMark::NONE;
+ goto ATTR_SETEMPHASIS;
+ case RTF_ACCDOT:
+ eEmphasis = (FontEmphasisMark::Dot | FontEmphasisMark::PosAbove);
+ goto ATTR_SETEMPHASIS;
+
+ case RTF_ACCCOMMA:
+ eEmphasis = (FontEmphasisMark::Accent | FontEmphasisMark::PosAbove);
+ATTR_SETEMPHASIS:
+ if (const TypedWhichId<SvxEmphasisMarkItem> wid = aPlainMap[SID_ATTR_CHAR_EMPHASISMARK])
+ {
+ pSet->Put(SvxEmphasisMarkItem(eEmphasis, wid));
+ }
+ break;
+
+ case RTF_TWOINONE:
+ if (const TypedWhichId<SvxTwoLinesItem> wid = aPlainMap[SID_ATTR_CHAR_TWO_LINES])
+ {
+ sal_Unicode cStt, cEnd;
+ switch ( nTokenValue )
+ {
+ case 1: cStt = '('; cEnd = ')'; break;
+ case 2: cStt = '['; cEnd = ']'; break;
+ case 3: cStt = '<'; cEnd = '>'; break;
+ case 4: cStt = '{'; cEnd = '}'; break;
+ default: cStt = 0; cEnd = 0; break;
+ }
+
+ pSet->Put(SvxTwoLinesItem(true, cStt, cEnd, wid));
+ }
+ break;
+
+ case RTF_CHARSCALEX :
+ if (const TypedWhichId<SvxCharScaleWidthItem> wid = aPlainMap[SID_ATTR_CHAR_SCALEWIDTH])
+ {
+ //i21372
+ if (nTokenValue < 1 || nTokenValue > 600)
+ nTokenValue = 100;
+ pSet->Put(SvxCharScaleWidthItem(sal_uInt16(nTokenValue), wid));
+ }
+ break;
+
+ case RTF_HORZVERT:
+ if (const TypedWhichId<SvxCharRotateItem> wid = aPlainMap[SID_ATTR_CHAR_ROTATED])
+ {
+ // RTF knows only 90deg
+ pSet->Put(SvxCharRotateItem(900_deg10, 1 == nTokenValue, wid));
+ }
+ break;
+
+ case RTF_EMBO:
+ if (const TypedWhichId<SvxCharReliefItem> wid = aPlainMap[SID_ATTR_CHAR_RELIEF])
+ {
+ pSet->Put(SvxCharReliefItem(FontRelief::Embossed, wid));
+ }
+ break;
+ case RTF_IMPR:
+ if (const TypedWhichId<SvxCharReliefItem> wid = aPlainMap[SID_ATTR_CHAR_RELIEF])
+ {
+ pSet->Put(SvxCharReliefItem(FontRelief::Engraved, wid));
+ }
+ break;
+ case RTF_V:
+ if (const TypedWhichId<SvxCharHiddenItem> wid = aPlainMap[SID_ATTR_CHAR_HIDDEN])
+ {
+ pSet->Put(SvxCharHiddenItem(nTokenValue != 0, wid));
+ }
+ break;
+ case RTF_CHBGFDIAG:
+ case RTF_CHBGDKVERT:
+ case RTF_CHBGDKHORIZ:
+ case RTF_CHBGVERT:
+ case RTF_CHBGHORIZ:
+ case RTF_CHBGDKFDIAG:
+ case RTF_CHBGDCROSS:
+ case RTF_CHBGCROSS:
+ case RTF_CHBGBDIAG:
+ case RTF_CHBGDKDCROSS:
+ case RTF_CHBGDKCROSS:
+ case RTF_CHBGDKBDIAG:
+ case RTF_CHCBPAT:
+ case RTF_CHCFPAT:
+ case RTF_CHSHDNG:
+ if (aPlainMap[SID_ATTR_BRUSH_CHAR])
+ ReadBackgroundAttr( nToken, *pSet );
+ break;
+
+ case BRACELEFT:
+ {
+ // tests on Swg internal tokens
+ bool bHandled = false;
+ short nSkip = 0;
+ if( RTF_IGNOREFLAG != GetNextToken())
+ nSkip = -1;
+ else if( (nToken = GetNextToken() ) & RTF_SWGDEFS )
+ {
+ bHandled = true;
+ switch( nToken )
+ {
+ case RTF_PGDSCNO:
+ case RTF_PGBRK:
+ case RTF_SOUTLVL:
+ UnknownAttrToken( nToken );
+ // overwrite the closing parenthesis
+ break;
+
+ case RTF_SWG_ESCPROP:
+ {
+ // Store percentage change!
+ sal_uInt8 nProp = sal_uInt8( nTokenValue / 100 );
+ short nEsc = 0;
+ if( 1 == ( nTokenValue % 100 ))
+ // Recognize own auto-flags!
+ nEsc = DFLT_ESC_AUTO_SUPER;
+
+ if (const sal_uInt16 wid = aPlainMap[SID_ATTR_CHAR_ESCAPEMENT])
+ pSet->Put(SvxEscapementItem(nEsc, nProp, wid));
+ }
+ break;
+
+ case RTF_HYPHEN:
+ {
+ SvxHyphenZoneItem aHypenZone(
+ (nTokenValue & 1) != 0,
+ aPardMap[SID_ATTR_PARA_HYPHENZONE]);
+ aHypenZone.SetPageEnd((nTokenValue & 2) != 0);
+
+ if( aPardMap[SID_ATTR_PARA_HYPHENZONE] &&
+ RTF_HYPHLEAD == GetNextToken() &&
+ RTF_HYPHTRAIL == GetNextToken() &&
+ RTF_HYPHMAX == GetNextToken() )
+ {
+ aHypenZone.GetMinLead() =
+ sal_uInt8(GetStackPtr( -2 )->nTokenValue);
+ aHypenZone.GetMinTrail() =
+ sal_uInt8(GetStackPtr( -1 )->nTokenValue);
+ aHypenZone.GetMaxHyphens() =
+ sal_uInt8(nTokenValue);
+
+ pSet->Put( aHypenZone );
+ }
+ else
+ SkipGroup(); // at the end of the group
+ }
+ break;
+
+ // We expect these to be preceded by a RTF_HYPHEN and
+ // so normally are handled by the RTF_HYPHEN case, but
+ // if they appear 'bare' in a document then safely skip
+ // them here
+ case RTF_HYPHLEAD:
+ case RTF_HYPHTRAIL:
+ case RTF_HYPHMAX:
+ SkipGroup();
+ break;
+
+ case RTF_SHADOW:
+ {
+ bool bSkip = true;
+ do { // middle check loop
+ SvxShadowLocation eSL = SvxShadowLocation( nTokenValue );
+ if( RTF_SHDW_DIST != GetNextToken() )
+ break;
+ sal_uInt16 nDist = sal_uInt16( nTokenValue );
+
+ if( RTF_SHDW_STYLE != GetNextToken() )
+ break;
+
+ if( RTF_SHDW_COL != GetNextToken() )
+ break;
+ sal_uInt16 nCol = sal_uInt16( nTokenValue );
+
+ if( RTF_SHDW_FCOL != GetNextToken() )
+ break;
+
+ Color aColor = GetColor( nCol );
+
+ if (const TypedWhichId<SvxShadowItem> wid = aPardMap[SID_ATTR_BORDER_SHADOW])
+ pSet->Put(SvxShadowItem(wid, &aColor, nDist, eSL));
+
+ bSkip = false;
+ } while( false );
+
+ if( bSkip )
+ SkipGroup(); // at the end of the group
+ }
+ break;
+
+ default:
+ bHandled = false;
+ if( (nToken & ~(0xff | RTF_SWGDEFS)) == RTF_TABSTOPDEF )
+ {
+ nToken = SkipToken( -2 );
+ ReadTabAttr( nToken, *pSet );
+
+ /*
+ cmc: #i76140, he who consumed the { must consume the }
+ We rewound to a state of { being the current
+ token so it is our responsibility to consume the }
+ token if we consumed the {. We will not have consumed
+ the { if it belonged to our caller, i.e. if the { we
+ are handling is the "firsttoken" passed to us then
+ the *caller* must consume it, not us. Otherwise *we*
+ should consume it.
+ */
+ if (nToken == BRACELEFT && !bFirstToken)
+ {
+ nToken = GetNextToken();
+ SAL_WARN_IF( nToken != BRACERIGHT,
+ "editeng",
+ "} did not follow { as expected");
+ }
+ }
+ else if( (nToken & ~(0xff| RTF_SWGDEFS)) == RTF_BRDRDEF)
+ {
+ nToken = SkipToken( -2 );
+ ReadBorderAttr( nToken, *pSet );
+ }
+ else // so no more attribute
+ nSkip = -2;
+ break;
+ }
+
+#if 1
+ /*
+ cmc: #i4727# / #i12713# Who owns this closing bracket?
+ If we read the opening one, we must read this one, if
+ other is counting the brackets so as to push/pop off
+ the correct environment then we will have pushed a new
+ environment for the start { of this, but will not see
+ the } and so is out of sync for the rest of the
+ document.
+ */
+ if (bHandled && !bFirstToken)
+ GetNextToken();
+#endif
+ }
+ else
+ nSkip = -2;
+
+ if( nSkip ) // all completely unknown
+ {
+ if (!bFirstToken)
+ --nSkip; // BRACELEFT: is the next token
+ SkipToken( nSkip );
+ bContinue = false;
+ }
+ }
+ break;
+ default:
+ if( (nToken & ~0xff ) == RTF_TABSTOPDEF )
+ ReadTabAttr( nToken, *pSet );
+ else if( (nToken & ~0xff ) == RTF_BRDRDEF )
+ ReadBorderAttr( nToken, *pSet );
+ else if( (nToken & ~0xff ) == RTF_SHADINGDEF )
+ ReadBackgroundAttr( nToken, *pSet );
+ else
+ {
+ // unknown token, so token "returned in Parser"
+ if( !bFirstToken )
+ SkipToken();
+ bContinue = false;
+ }
+ }
+ }
+ if( bContinue )
+ {
+ nToken = GetNextToken();
+ }
+ bFirstToken = false;
+ }
+}
+
+void SvxRTFParser::ReadTabAttr( int nToken, SfxItemSet& rSet )
+{
+ bool bMethodOwnsToken = false; // #i52542# patch from cmc.
+// then read all the TabStops
+ SvxTabStop aTabStop;
+ SvxTabStopItem aAttr(0, 0, SvxTabAdjust::Default, aPardMap[SID_ATTR_TABSTOP]);
+ bool bContinue = true;
+ do {
+ switch( nToken )
+ {
+ case RTF_TB: // BarTab ???
+ case RTF_TX:
+ {
+ if( IsCalcValue() )
+ CalcValue();
+ aTabStop.GetTabPos() = nTokenValue;
+ aAttr.Insert( aTabStop );
+ aTabStop = SvxTabStop(); // all values default
+ }
+ break;
+
+ case RTF_TQL:
+ aTabStop.GetAdjustment() = SvxTabAdjust::Left;
+ break;
+ case RTF_TQR:
+ aTabStop.GetAdjustment() = SvxTabAdjust::Right;
+ break;
+ case RTF_TQC:
+ aTabStop.GetAdjustment() = SvxTabAdjust::Center;
+ break;
+ case RTF_TQDEC:
+ aTabStop.GetAdjustment() = SvxTabAdjust::Decimal;
+ break;
+
+ case RTF_TLDOT: aTabStop.GetFill() = '.'; break;
+ case RTF_TLHYPH: aTabStop.GetFill() = ' '; break;
+ case RTF_TLUL: aTabStop.GetFill() = '_'; break;
+ case RTF_TLTH: aTabStop.GetFill() = '-'; break;
+ case RTF_TLEQ: aTabStop.GetFill() = '='; break;
+
+ case BRACELEFT:
+ {
+ // Swg - control BRACELEFT RTF_IGNOREFLAG RTF_TLSWG BRACERIGHT
+ short nSkip = 0;
+ if( RTF_IGNOREFLAG != GetNextToken() )
+ nSkip = -1;
+ else if( RTF_TLSWG != ( nToken = GetNextToken() ))
+ nSkip = -2;
+ else
+ {
+ aTabStop.GetDecimal() = sal_uInt8(nTokenValue & 0xff);
+ aTabStop.GetFill() = sal_uInt8((nTokenValue >> 8) & 0xff);
+ // overwrite the closing parenthesis
+ if (bMethodOwnsToken)
+ GetNextToken();
+ }
+ if( nSkip )
+ {
+ SkipToken( nSkip ); // Ignore back again
+ bContinue = false;
+ }
+ }
+ break;
+
+ default:
+ bContinue = false;
+ }
+ if( bContinue )
+ {
+ nToken = GetNextToken();
+ bMethodOwnsToken = true;
+ }
+ } while( bContinue );
+
+ // Fill with defaults is still missing!
+ rSet.Put( aAttr );
+ SkipToken();
+}
+
+static void SetBorderLine( int nBorderTyp, SvxBoxItem& rItem,
+ const SvxBorderLine& rBorder )
+{
+ switch( nBorderTyp )
+ {
+ case RTF_BOX: // run through all levels
+ case RTF_BRDRT:
+ rItem.SetLine( &rBorder, SvxBoxItemLine::TOP );
+ if( RTF_BOX != nBorderTyp )
+ return;
+ [[fallthrough]];
+ case RTF_BRDRB:
+ rItem.SetLine( &rBorder, SvxBoxItemLine::BOTTOM );
+ if( RTF_BOX != nBorderTyp )
+ return;
+ [[fallthrough]];
+ case RTF_BRDRL:
+ rItem.SetLine( &rBorder, SvxBoxItemLine::LEFT );
+ if( RTF_BOX != nBorderTyp )
+ return;
+ [[fallthrough]];
+ case RTF_BRDRR:
+ rItem.SetLine( &rBorder, SvxBoxItemLine::RIGHT );
+ if( RTF_BOX != nBorderTyp )
+ return;
+ }
+}
+
+void SvxRTFParser::ReadBorderAttr( int nToken, SfxItemSet& rSet,
+ bool bTableDef )
+{
+ // then read the border attribute
+ std::unique_ptr<SvxBoxItem> aAttr(std::make_unique<SvxBoxItem>(aPardMap[SID_ATTR_BORDER_OUTER]));
+ const SfxPoolItem* pItem(nullptr);
+
+ if (SfxItemState::SET == rSet.GetItemState(aPardMap[SID_ATTR_BORDER_OUTER], false, &pItem))
+ {
+ aAttr.reset(static_cast<SvxBoxItem*>(pItem->Clone()));
+ }
+
+ SvxBorderLine aBrd( nullptr, SvxBorderLineWidth::Hairline );
+ bool bContinue = true;
+ int nBorderTyp = 0;
+
+ tools::Long nWidth = 1;
+ bool bDoubleWidth = false;
+
+ do {
+ switch( nToken )
+ {
+ case RTF_BOX:
+ case RTF_BRDRT:
+ case RTF_BRDRB:
+ case RTF_BRDRL:
+ case RTF_BRDRR:
+ nBorderTyp = nToken;
+ break;
+
+ case RTF_CLBRDRT: // Cell top border
+ {
+ if( bTableDef )
+ {
+ if (nBorderTyp != 0)
+ SetBorderLine( nBorderTyp, *aAttr, aBrd );
+ nBorderTyp = RTF_BRDRT;
+ }
+ break;
+ }
+ case RTF_CLBRDRB: // Cell bottom border
+ {
+ if( bTableDef )
+ {
+ if (nBorderTyp != 0)
+ SetBorderLine( nBorderTyp, *aAttr, aBrd );
+ nBorderTyp = RTF_BRDRB;
+ }
+ break;
+ }
+ case RTF_CLBRDRL: // Cell left border
+ {
+ if( bTableDef )
+ {
+ if (nBorderTyp != 0)
+ SetBorderLine( nBorderTyp, *aAttr, aBrd );
+ nBorderTyp = RTF_BRDRL;
+ }
+ break;
+ }
+ case RTF_CLBRDRR: // Cell right border
+ {
+ if( bTableDef )
+ {
+ if (nBorderTyp != 0)
+ SetBorderLine( nBorderTyp, *aAttr, aBrd );
+ nBorderTyp = RTF_BRDRR;
+ }
+ break;
+ }
+
+ case RTF_BRDRDOT: // dotted border
+ aBrd.SetBorderLineStyle(SvxBorderLineStyle::DOTTED);
+ break;
+ case RTF_BRDRDASH: // dashed border
+ aBrd.SetBorderLineStyle(SvxBorderLineStyle::DASHED);
+ break;
+ case RTF_BRDRHAIR: // hairline border
+ {
+ aBrd.SetBorderLineStyle( SvxBorderLineStyle::SOLID);
+ aBrd.SetWidth( SvxBorderLineWidth::Hairline );
+ }
+ break;
+ case RTF_BRDRDB: // Double border
+ aBrd.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE);
+ break;
+ case RTF_BRDRINSET: // inset border
+ aBrd.SetBorderLineStyle(SvxBorderLineStyle::INSET);
+ break;
+ case RTF_BRDROUTSET: // outset border
+ aBrd.SetBorderLineStyle(SvxBorderLineStyle::OUTSET);
+ break;
+ case RTF_BRDRTNTHSG: // ThinThick Small gap
+ aBrd.SetBorderLineStyle(SvxBorderLineStyle::THINTHICK_SMALLGAP);
+ break;
+ case RTF_BRDRTNTHMG: // ThinThick Medium gap
+ aBrd.SetBorderLineStyle(SvxBorderLineStyle::THINTHICK_MEDIUMGAP);
+ break;
+ case RTF_BRDRTNTHLG: // ThinThick Large gap
+ aBrd.SetBorderLineStyle(SvxBorderLineStyle::THINTHICK_LARGEGAP);
+ break;
+ case RTF_BRDRTHTNSG: // ThickThin Small gap
+ aBrd.SetBorderLineStyle(SvxBorderLineStyle::THICKTHIN_SMALLGAP);
+ break;
+ case RTF_BRDRTHTNMG: // ThickThin Medium gap
+ aBrd.SetBorderLineStyle(SvxBorderLineStyle::THICKTHIN_MEDIUMGAP);
+ break;
+ case RTF_BRDRTHTNLG: // ThickThin Large gap
+ aBrd.SetBorderLineStyle(SvxBorderLineStyle::THICKTHIN_LARGEGAP);
+ break;
+ case RTF_BRDREMBOSS: // Embossed border
+ aBrd.SetBorderLineStyle(SvxBorderLineStyle::EMBOSSED);
+ break;
+ case RTF_BRDRENGRAVE: // Engraved border
+ aBrd.SetBorderLineStyle(SvxBorderLineStyle::ENGRAVED);
+ break;
+
+ case RTF_BRDRS: // single thickness border
+ bDoubleWidth = false;
+ break;
+ case RTF_BRDRTH: // double thickness border width*2
+ bDoubleWidth = true;
+ break;
+ case RTF_BRDRW: // border width <255
+ nWidth = nTokenValue;
+ break;
+
+ case RTF_BRDRCF: // Border color
+ aBrd.SetColor( GetColor( sal_uInt16(nTokenValue) ) );
+ break;
+
+ case RTF_BRDRSH: // Shadowed border
+ rSet.Put( SvxShadowItem( aPardMap[SID_ATTR_BORDER_SHADOW], nullptr, 60 /*3pt*/,
+ SvxShadowLocation::BottomRight ) );
+ break;
+
+ case RTF_BRSP: // Spacing to content in twip
+ {
+ switch( nBorderTyp )
+ {
+ case RTF_BRDRB:
+ aAttr->SetDistance( static_cast<sal_uInt16>(nTokenValue), SvxBoxItemLine::BOTTOM );
+ break;
+
+ case RTF_BRDRT:
+ aAttr->SetDistance( static_cast<sal_uInt16>(nTokenValue), SvxBoxItemLine::TOP );
+ break;
+
+ case RTF_BRDRL:
+ aAttr->SetDistance( static_cast<sal_uInt16>(nTokenValue), SvxBoxItemLine::LEFT );
+ break;
+
+ case RTF_BRDRR:
+ aAttr->SetDistance( static_cast<sal_uInt16>(nTokenValue), SvxBoxItemLine::RIGHT );
+ break;
+
+ case RTF_BOX:
+ aAttr->SetAllDistances( static_cast<sal_uInt16>(nTokenValue) );
+ break;
+ }
+ }
+ break;
+
+ case RTF_BRDRBTW: // Border formatting group
+ case RTF_BRDRBAR: // Border outside
+ // TODO unhandled ATM
+ break;
+
+ default:
+ bContinue = (nToken & ~(0xff| RTF_SWGDEFS)) == RTF_BRDRDEF;
+ }
+ if( bContinue )
+ nToken = GetNextToken();
+ } while( bContinue );
+
+ // Finally compute the border width
+ if ( bDoubleWidth ) nWidth *= 2;
+ aBrd.SetWidth( nWidth );
+
+ SetBorderLine( nBorderTyp, *aAttr, aBrd );
+
+ rSet.Put( std::move(aAttr) );
+ SkipToken();
+}
+
+static sal_uInt32 CalcShading( sal_uInt32 nColor, sal_uInt32 nFillColor, sal_uInt8 nShading )
+{
+ nColor = (nColor * nShading) / 100;
+ nFillColor = (nFillColor * ( 100 - nShading )) / 100;
+ return nColor + nFillColor;
+}
+
+void SvxRTFParser::ReadBackgroundAttr( int nToken, SfxItemSet& rSet,
+ bool bTableDef )
+{
+ // then read the border attribute
+ bool bContinue = true;
+ sal_uInt16 nColor = USHRT_MAX, nFillColor = USHRT_MAX;
+ sal_uInt8 nFillValue = 0;
+
+ sal_uInt16 nWh = ( nToken & ~0xff ) == RTF_CHRFMT
+ ? aPlainMap[SID_ATTR_BRUSH_CHAR]
+ : aPardMap[SID_ATTR_BRUSH];
+
+ do {
+ switch( nToken )
+ {
+ case RTF_CLCBPAT:
+ case RTF_CHCBPAT:
+ case RTF_CBPAT:
+ nFillColor = sal_uInt16( nTokenValue );
+ break;
+
+ case RTF_CLCFPAT:
+ case RTF_CHCFPAT:
+ case RTF_CFPAT:
+ nColor = sal_uInt16( nTokenValue );
+ break;
+
+ case RTF_CLSHDNG:
+ case RTF_CHSHDNG:
+ case RTF_SHADING:
+ nFillValue = static_cast<sal_uInt8>( nTokenValue / 100 );
+ break;
+
+ case RTF_CLBGDKHOR:
+ case RTF_CHBGDKHORIZ:
+ case RTF_BGDKHORIZ:
+ case RTF_CLBGDKVERT:
+ case RTF_CHBGDKVERT:
+ case RTF_BGDKVERT:
+ case RTF_CLBGDKBDIAG:
+ case RTF_CHBGDKBDIAG:
+ case RTF_BGDKBDIAG:
+ case RTF_CLBGDKFDIAG:
+ case RTF_CHBGDKFDIAG:
+ case RTF_BGDKFDIAG:
+ case RTF_CLBGDKCROSS:
+ case RTF_CHBGDKCROSS:
+ case RTF_BGDKCROSS:
+ case RTF_CLBGDKDCROSS:
+ case RTF_CHBGDKDCROSS:
+ case RTF_BGDKDCROSS:
+ // dark -> 60%
+ nFillValue = 60;
+ break;
+
+ case RTF_CLBGHORIZ:
+ case RTF_CHBGHORIZ:
+ case RTF_BGHORIZ:
+ case RTF_CLBGVERT:
+ case RTF_CHBGVERT:
+ case RTF_BGVERT:
+ case RTF_CLBGBDIAG:
+ case RTF_CHBGBDIAG:
+ case RTF_BGBDIAG:
+ case RTF_CLBGFDIAG:
+ case RTF_CHBGFDIAG:
+ case RTF_BGFDIAG:
+ case RTF_CLBGCROSS:
+ case RTF_CHBGCROSS:
+ case RTF_BGCROSS:
+ case RTF_CLBGDCROSS:
+ case RTF_CHBGDCROSS:
+ case RTF_BGDCROSS:
+ // light -> 20%
+ nFillValue = 20;
+ break;
+
+ default:
+ if( bTableDef )
+ bContinue = (nToken & ~(0xff | RTF_TABLEDEF) ) == RTF_SHADINGDEF;
+ else
+ bContinue = (nToken & ~0xff) == RTF_SHADINGDEF;
+ }
+ if( bContinue )
+ nToken = GetNextToken();
+ } while( bContinue );
+
+ Color aCol( COL_WHITE ), aFCol;
+ if( !nFillValue )
+ {
+ // there was only one of two colors specified or no BrushType
+ if( USHRT_MAX != nFillColor )
+ {
+ nFillValue = 100;
+ aCol = GetColor( nFillColor );
+ }
+ else if( USHRT_MAX != nColor )
+ aFCol = GetColor( nColor );
+ }
+ else
+ {
+ if( USHRT_MAX != nColor )
+ aCol = GetColor( nColor );
+ else
+ aCol = COL_BLACK;
+
+ if( USHRT_MAX != nFillColor )
+ aFCol = GetColor( nFillColor );
+ else
+ aFCol = COL_WHITE;
+ }
+
+ Color aColor;
+ if( 0 == nFillValue || 100 == nFillValue )
+ aColor = aCol;
+ else
+ aColor = Color(
+ static_cast<sal_uInt8>(CalcShading( aCol.GetRed(), aFCol.GetRed(), nFillValue )),
+ static_cast<sal_uInt8>(CalcShading( aCol.GetGreen(), aFCol.GetGreen(), nFillValue )),
+ static_cast<sal_uInt8>(CalcShading( aCol.GetBlue(), aFCol.GetBlue(), nFillValue )) );
+
+ rSet.Put( SvxBrushItem( aColor, nWh ) );
+ SkipToken();
+}
+
+
+// pard / plain handling
+void SvxRTFParser::RTFPardPlain( bool const bPard, SfxItemSet** ppSet )
+{
+ if( bNewGroup || aAttrStack.empty() ) // not at the beginning of a new group
+ return;
+
+ SvxRTFItemStackType* pCurrent = aAttrStack.back().get();
+
+ int nLastToken = GetStackPtr(-1)->nTokenId;
+ bool bNewStkEntry = true;
+ if( RTF_PARD != nLastToken &&
+ RTF_PLAIN != nLastToken &&
+ BRACELEFT != nLastToken )
+ {
+ if (pCurrent->aAttrSet.Count() || !pCurrent->maChildList.empty() || pCurrent->nStyleNo)
+ {
+ // open a new group
+ auto xNew(std::make_unique<SvxRTFItemStackType>(*pCurrent, *mxInsertPosition, true));
+ xNew->SetRTFDefaults( GetRTFDefaults() );
+
+ // Set all until here valid attributes
+ AttrGroupEnd();
+ pCurrent = aAttrStack.empty() ? nullptr : aAttrStack.back().get(); // can be changed after AttrGroupEnd!
+ xNew->aAttrSet.SetParent( pCurrent ? &pCurrent->aAttrSet : nullptr );
+ aAttrStack.push_back( std::move(xNew) );
+ pCurrent = aAttrStack.back().get();
+ }
+ else
+ {
+ // continue to use this entry as new
+ pCurrent->SetStartPos( *mxInsertPosition );
+ bNewStkEntry = false;
+ }
+ }
+
+ // now reset all to default
+ if( bNewStkEntry &&
+ ( pCurrent->aAttrSet.GetParent() || pCurrent->aAttrSet.Count() ))
+ {
+ const SfxPoolItem *pItem, *pDef;
+ std::map<sal_uInt16, sal_uInt16>::const_iterator aIt;
+ std::map<sal_uInt16, sal_uInt16>::const_iterator aEnd;
+ const SfxItemSet* pDfltSet = &GetRTFDefaults();
+ if( bPard )
+ {
+ pCurrent->nStyleNo = 0;
+ aIt = aPardMap.data.begin();
+ aEnd = aPardMap.data.end();
+ }
+ else
+ {
+ aIt = aPlainMap.data.begin();
+ aEnd = aPlainMap.data.end();
+ }
+
+ for (; aIt != aEnd; ++aIt)
+ {
+ const sal_uInt16 wid = aIt->second;
+ // Item set and different -> Set the Default Pool
+ if (!wid)
+ ;
+ else if (SfxItemPool::IsSlot(wid))
+ pCurrent->aAttrSet.ClearItem(wid);
+ else if( IsChkStyleAttr() )
+ pCurrent->aAttrSet.Put(pDfltSet->Get(wid));
+ else if( !pCurrent->aAttrSet.GetParent() )
+ {
+ if (SfxItemState::SET == pDfltSet->GetItemState(wid, false, &pDef))
+ pCurrent->aAttrSet.Put( *pDef );
+ else
+ pCurrent->aAttrSet.ClearItem(wid);
+ }
+ else if( SfxItemState::SET == pCurrent->aAttrSet.GetParent()->
+ GetItemState(wid, true, &pItem) &&
+ *( pDef = &pDfltSet->Get(wid)) != *pItem )
+ pCurrent->aAttrSet.Put( *pDef );
+ else
+ {
+ if (SfxItemState::SET == pDfltSet->GetItemState(wid, false, &pDef))
+ pCurrent->aAttrSet.Put( *pDef );
+ else
+ pCurrent->aAttrSet.ClearItem(wid);
+ }
+ }
+ }
+ else if( bPard )
+ pCurrent->nStyleNo = 0; // reset Style number
+
+ *ppSet = &pCurrent->aAttrSet;
+
+ if (bPard)
+ return;
+
+ //Once we have a default font, then any text without a font specifier is
+ //in the default font, and thus has the default font charset, otherwise
+ //we can fall back to the ansicpg set codeset
+ if (nDfltFont != -1)
+ {
+ const vcl::Font& rSVFont = GetFont(sal_uInt16(nDfltFont));
+ SetEncoding(rSVFont.GetCharSet());
+ }
+ else
+ SetEncoding(GetCodeSet());
+}
+
+void SvxRTFParser::SetDefault( int nToken, int nValue )
+{
+ if( !bNewDoc )
+ return;
+
+ SfxItemSet aTmp(*pAttrPool, aWhichMap);
+ bool bOldFlag = bIsLeftToRightDef;
+ bIsLeftToRightDef = true;
+ switch( nToken )
+ {
+ case RTF_ADEFF:
+ bIsLeftToRightDef = false;
+ [[fallthrough]];
+ case RTF_DEFF:
+ {
+ if( -1 == nValue )
+ nValue = 0;
+ const vcl::Font& rSVFont = GetFont( sal_uInt16(nValue) );
+ SvxFontItem aTmpItem(
+ rSVFont.GetFamilyType(), rSVFont.GetFamilyName(),
+ rSVFont.GetStyleName(), rSVFont.GetPitch(),
+ rSVFont.GetCharSet(), SID_ATTR_CHAR_FONT );
+ SetScriptAttr( NOTDEF_CHARTYPE, aTmp, aTmpItem );
+ }
+ break;
+
+ case RTF_ADEFLANG:
+ bIsLeftToRightDef = false;
+ [[fallthrough]];
+ case RTF_DEFLANG:
+ // store default Language
+ if( -1 != nValue )
+ {
+ SvxLanguageItem aTmpItem( LanguageType(nValue), SID_ATTR_CHAR_LANGUAGE );
+ SetScriptAttr( NOTDEF_CHARTYPE, aTmp, aTmpItem );
+ }
+ break;
+
+ case RTF_DEFTAB:
+ if (const sal_uInt16 wid = aPardMap[SID_ATTR_TABSTOP])
+ {
+ // RTF defines 720 twips as default
+ bIsSetDfltTab = true;
+ if( -1 == nValue || !nValue )
+ nValue = 720;
+
+ // who would like to have no twips ...
+ if( IsCalcValue() )
+ {
+ nTokenValue = nValue;
+ CalcValue();
+ nValue = nTokenValue;
+ }
+
+ // Calculate the ratio of default TabWidth / Tabs and
+ // calculate the corresponding new number.
+ // ?? how did one come up with 13 ??
+ sal_uInt16 nTabCount = (SVX_TAB_DEFDIST * 13 ) / sal_uInt16(nValue);
+ /*
+ cmc, make sure we have at least one, or all hell breaks loose in
+ everybody exporters, #i8247#
+ */
+ if (nTabCount < 1)
+ nTabCount = 1;
+
+ // we want Defaulttabs
+ SvxTabStopItem aNewTab(nTabCount, sal_uInt16(nValue), SvxTabAdjust::Default, wid);
+ while( nTabCount )
+ const_cast<SvxTabStop&>(aNewTab[ --nTabCount ]).GetAdjustment() = SvxTabAdjust::Default;
+
+ pAttrPool->SetPoolDefaultItem( aNewTab );
+ }
+ break;
+ }
+ bIsLeftToRightDef = bOldFlag;
+
+ if( aTmp.Count() )
+ {
+ SfxItemIter aIter( aTmp );
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+ do
+ {
+ pAttrPool->SetPoolDefaultItem( *pItem );
+ pItem = aIter.NextItem();
+ } while (pItem);
+ }
+}
+
+// default: no conversion, leaving everything in twips.
+void SvxRTFParser::CalcValue()
+{
+}
+
+// for tokens that are not evaluated in ReadAttr
+void SvxRTFParser::UnknownAttrToken( int )
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/rtf/svxrtf.cxx b/editeng/source/rtf/svxrtf.cxx
new file mode 100644
index 0000000000..1ef6f30b40
--- /dev/null
+++ b/editeng/source/rtf/svxrtf.cxx
@@ -0,0 +1,1162 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <queue>
+#include <comphelper/diagnose_ex.hxx>
+#include <rtl/tencinfo.h>
+#include <svl/itemiter.hxx>
+#include <svl/whiter.hxx>
+#include <svtools/rtftoken.h>
+#include <svl/itempool.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <tools/debug.hxx>
+#include <unotools/configmgr.hxx>
+
+#include <comphelper/string.hxx>
+
+#include <editeng/scriptspaceitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/svxrtf.hxx>
+#include <editeng/editids.hrc>
+#include <vcl/font.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+
+
+using namespace ::com::sun::star;
+
+
+static rtl_TextEncoding lcl_GetDefaultTextEncodingForRTF()
+{
+
+ OUString aLangString( Application::GetSettings().GetLanguageTag().getLanguage());
+
+ if ( aLangString == "ru" || aLangString == "uk" )
+ return RTL_TEXTENCODING_MS_1251;
+ if ( aLangString == "tr" )
+ return RTL_TEXTENCODING_MS_1254;
+ else
+ return RTL_TEXTENCODING_MS_1252;
+}
+
+// -------------- Methods --------------------
+
+SvxRTFParser::SvxRTFParser( SfxItemPool& rPool, SvStream& rIn )
+ : SvRTFParser( rIn, 5 )
+ , pAttrPool( &rPool )
+ , nDfltFont( 0)
+ , bNewDoc( true )
+ , bNewGroup( false)
+ , bIsSetDfltTab( false)
+ , bChkStyleAttr( false )
+ , bCalcValue( false )
+ , bIsLeftToRightDef( true)
+ , bIsInReadStyleTab( false)
+{
+ pDfltFont.emplace();
+ mxDefaultColor = Color();
+
+ // generate the correct WhichId table from the set WhichIds.
+ BuildWhichTable();
+}
+
+SvxRTFParser::~SvxRTFParser()
+{
+ if( !aAttrStack.empty() )
+ ClearAttrStack();
+}
+
+void SvxRTFParser::SetInsPos( const EditPosition& rNew )
+{
+ mxInsertPosition = rNew;
+}
+
+SvParserState SvxRTFParser::CallParser()
+{
+ DBG_ASSERT( mxInsertPosition, "no insertion position");
+
+ if( !mxInsertPosition )
+ return SvParserState::Error;
+
+ if( !maColorTable.empty() )
+ ClearColorTbl();
+ m_FontTable.clear();
+ m_StyleTable.clear();
+ if( !aAttrStack.empty() )
+ ClearAttrStack();
+
+ bIsSetDfltTab = false;
+ bNewGroup = false;
+ nDfltFont = 0;
+
+ return SvRTFParser::CallParser();
+}
+
+void SvxRTFParser::Continue( int nToken )
+{
+ SvRTFParser::Continue( nToken );
+
+ SvParserState eStatus = GetStatus();
+ if (eStatus != SvParserState::Pending && eStatus != SvParserState::Error)
+ {
+ SetAllAttrOfStk();
+ //Regardless of what "color 0" is, word defaults to auto as the default colour.
+ //e.g. see #i7713#
+ }
+}
+
+
+// is called for each token that is recognized in CallParser
+void SvxRTFParser::NextToken( int nToken )
+{
+ sal_Unicode cCh;
+ switch( nToken )
+ {
+ case RTF_COLORTBL: ReadColorTable(); break;
+ case RTF_FONTTBL: ReadFontTable(); break;
+ case RTF_STYLESHEET: ReadStyleTable(); break;
+
+ case RTF_DEFF:
+ if( bNewDoc )
+ {
+ if (!m_FontTable.empty())
+ // Can immediately be set
+ SetDefault( nToken, nTokenValue );
+ else
+ // is set after reading the font table
+ nDfltFont = int(nTokenValue);
+ }
+ break;
+
+ case RTF_DEFTAB:
+ case RTF_DEFLANG:
+ if( bNewDoc )
+ SetDefault( nToken, nTokenValue );
+ break;
+
+
+ case RTF_PICT: ReadBitmapData(); break;
+
+ case RTF_LINE: cCh = '\n'; goto INSINGLECHAR;
+ case RTF_TAB: cCh = '\t'; goto INSINGLECHAR;
+ case RTF_SUBENTRYINDEX: cCh = ':'; goto INSINGLECHAR;
+
+ case RTF_EMDASH: cCh = 0x2014; goto INSINGLECHAR;
+ case RTF_ENDASH: cCh = 0x2013; goto INSINGLECHAR;
+ case RTF_BULLET: cCh = 0x2022; goto INSINGLECHAR;
+ case RTF_LQUOTE: cCh = 0x2018; goto INSINGLECHAR;
+ case RTF_RQUOTE: cCh = 0x2019; goto INSINGLECHAR;
+ case RTF_LDBLQUOTE: cCh = 0x201C; goto INSINGLECHAR;
+ case RTF_RDBLQUOTE: cCh = 0x201D; goto INSINGLECHAR;
+INSINGLECHAR:
+ aToken = OUStringChar(cCh);
+ [[fallthrough]]; // aToken is set as Text
+ case RTF_TEXTTOKEN:
+ {
+ InsertText();
+ // all collected Attributes are set
+ for (size_t n = m_AttrSetList.size(); n; )
+ {
+ auto const& pStkSet = m_AttrSetList[--n];
+ SetAttrSet( *pStkSet );
+ m_AttrSetList.pop_back();
+ }
+ }
+ break;
+
+
+ case RTF_PAR:
+ InsertPara();
+ break;
+ case '{':
+ if (bNewGroup) // Nesting!
+ GetAttrSet_();
+ bNewGroup = true;
+ break;
+ case '}':
+ if( !bNewGroup ) // Empty Group ??
+ AttrGroupEnd();
+ bNewGroup = false;
+ break;
+ case RTF_INFO:
+ SkipGroup();
+ break;
+
+ // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ // First overwrite all (all have to be in one group!!)
+ // Could also appear in the RTF-file without the IGNORE-Flag; all Groups
+ // with the IGNORE-Flag are overwritten in the default branch.
+
+ case RTF_SWG_PRTDATA:
+ case RTF_FIELD:
+ case RTF_ATNID:
+ case RTF_ANNOTATION:
+
+ case RTF_BKMKSTART:
+ case RTF_BKMKEND:
+ case RTF_BKMK_KEY:
+ case RTF_XE:
+ case RTF_TC:
+ case RTF_NEXTFILE:
+ case RTF_TEMPLATE:
+ // RTF_SHPRSLT disabled for #i19718#
+ SkipGroup();
+ break;
+ // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+ case RTF_PGDSCNO:
+ case RTF_PGBRK:
+ case RTF_SHADOW:
+ if( RTF_IGNOREFLAG != GetStackPtr( -1 )->nTokenId )
+ break;
+ nToken = SkipToken();
+ if( '{' == GetStackPtr( -1 )->nTokenId )
+ nToken = SkipToken();
+
+ ReadAttr( nToken, &GetAttrSet() );
+ break;
+
+ default:
+ switch( nToken & ~(0xff | RTF_SWGDEFS) )
+ {
+ case RTF_PARFMT: // here are no SWGDEFS
+ ReadAttr( nToken, &GetAttrSet() );
+ break;
+
+ case RTF_CHRFMT:
+ case RTF_BRDRDEF:
+ case RTF_TABSTOPDEF:
+
+ if( RTF_SWGDEFS & nToken)
+ {
+ if( RTF_IGNOREFLAG != GetStackPtr( -1 )->nTokenId )
+ break;
+ nToken = SkipToken();
+ if( '{' == GetStackPtr( -1 )->nTokenId )
+ {
+ nToken = SkipToken();
+ }
+ }
+ ReadAttr( nToken, &GetAttrSet() );
+ break;
+ default:
+ {
+ if( RTF_IGNOREFLAG == GetStackPtr( -1 )->nTokenId &&
+ '{' == GetStackPtr( -2 )->nTokenId )
+ SkipGroup();
+ }
+ break;
+ }
+ break;
+ }
+}
+
+void SvxRTFParser::ReadStyleTable()
+{
+ int bSaveChkStyleAttr = bChkStyleAttr ? 1 : 0;
+ sal_uInt16 nStyleNo = 0;
+ bool bHasStyleNo = false;
+ int _nOpenBrackets = 1; // the first was already detected earlier!!
+ std::optional<SvxRTFStyleType> xStyle(SvxRTFStyleType(*pAttrPool, aWhichMap));
+ xStyle->aAttrSet.Put( GetRTFDefaults() );
+
+ bIsInReadStyleTab = true;
+ bChkStyleAttr = false; // Do not check Attribute against the Styles
+
+ while( _nOpenBrackets && IsParserWorking() )
+ {
+ int nToken = GetNextToken();
+ switch( nToken )
+ {
+ case '}': if( --_nOpenBrackets && IsParserWorking() )
+ // Style has been completely read,
+ // so this is still a stable status
+ SaveState( RTF_STYLESHEET );
+ break;
+ case '{':
+ {
+ if( RTF_IGNOREFLAG != GetNextToken() )
+ SkipToken();
+ else if( RTF_UNKNOWNCONTROL != ( nToken = GetNextToken() ) &&
+ RTF_PN != nToken )
+ SkipToken( -2 );
+ else
+ {
+ // filter out at once
+ ReadUnknownData();
+ nToken = GetNextToken();
+ if( '}' != nToken )
+ eState = SvParserState::Error;
+ break;
+ }
+ ++_nOpenBrackets;
+ }
+ break;
+
+ case RTF_SBASEDON: xStyle->nBasedOn = sal_uInt16(nTokenValue); break;
+ case RTF_SNEXT: break;
+ case RTF_OUTLINELEVEL:
+ case RTF_SOUTLVL: xStyle->nOutlineNo = sal_uInt8(nTokenValue); break;
+ case RTF_S: nStyleNo = static_cast<short>(nTokenValue);
+ bHasStyleNo = true;
+ break;
+ case RTF_CS: nStyleNo = static_cast<short>(nTokenValue);
+ bHasStyleNo = true;
+ break;
+
+ case RTF_TEXTTOKEN:
+ if (bHasStyleNo)
+ {
+ DelCharAtEnd( aToken, ';' );
+ xStyle->sName = aToken.toString();
+
+ if (!m_StyleTable.empty())
+ {
+ m_StyleTable.erase(nStyleNo);
+ }
+ // All data from the font is available, so off to the table
+ m_StyleTable.emplace(nStyleNo, std::move(*xStyle));
+ xStyle.emplace(*pAttrPool, aWhichMap);
+ xStyle->aAttrSet.Put( GetRTFDefaults() );
+ nStyleNo = 0;
+ bHasStyleNo = false;
+ }
+ break;
+ default:
+ switch( nToken & ~(0xff | RTF_SWGDEFS) )
+ {
+ case RTF_PARFMT: // here are no SWGDEFS
+ ReadAttr( nToken, &xStyle->aAttrSet );
+ break;
+
+ case RTF_CHRFMT:
+ case RTF_BRDRDEF:
+ case RTF_TABSTOPDEF:
+#ifndef NDEBUG
+ auto nEnteringToken = nToken;
+#endif
+ auto nEnteringIndex = m_nTokenIndex;
+ int nSkippedTokens = 0;
+ if( RTF_SWGDEFS & nToken)
+ {
+ if( RTF_IGNOREFLAG != GetStackPtr( -1 )->nTokenId )
+ break;
+ nToken = SkipToken();
+ ++nSkippedTokens;
+ if( '{' == GetStackPtr( -1 )->nTokenId )
+ {
+ nToken = SkipToken();
+ ++nSkippedTokens;
+ }
+ }
+ ReadAttr( nToken, &xStyle->aAttrSet );
+ if (nSkippedTokens && m_nTokenIndex == nEnteringIndex - nSkippedTokens)
+ {
+ // we called SkipToken to go back one or two, but ReadAttrs
+ // read nothing, so on next loop of the outer while we
+ // would end up in the same state again (assert that)
+ assert(nEnteringToken == GetNextToken());
+ // and loop endlessly, skip format a token
+ // instead to avoid that
+ SkipToken(nSkippedTokens);
+ }
+ break;
+ }
+ break;
+ }
+ }
+ xStyle.reset(); // Delete the Last Style
+ SkipToken(); // the closing brace is evaluated "above"
+
+ // Flag back to old state
+ bChkStyleAttr = bSaveChkStyleAttr;
+ bIsInReadStyleTab = false;
+}
+
+void SvxRTFParser::ReadColorTable()
+{
+ int nToken;
+ sal_uInt8 nRed = 0xff, nGreen = 0xff, nBlue = 0xff;
+
+ for (;;)
+ {
+ nToken = GetNextToken();
+ if ( '}' == nToken || !IsParserWorking() )
+ break;
+ switch( nToken )
+ {
+ case RTF_RED: nRed = sal_uInt8(nTokenValue); break;
+ case RTF_GREEN: nGreen = sal_uInt8(nTokenValue); break;
+ case RTF_BLUE: nBlue = sal_uInt8(nTokenValue); break;
+
+ case RTF_TEXTTOKEN:
+ if( 1 == aToken.getLength()
+ ? aToken[ 0 ] != ';'
+ : -1 == aToken.indexOf( ";" ) )
+ break; // At least the ';' must be found
+
+ [[fallthrough]];
+
+ case ';':
+ if( IsParserWorking() )
+ {
+ // one color is finished, fill in the table
+ // try to map the values to SV internal names
+ Color aColor( nRed, nGreen, nBlue );
+ if( maColorTable.empty() &&
+ sal_uInt8(-1) == nRed && sal_uInt8(-1) == nGreen && sal_uInt8(-1) == nBlue )
+ aColor = COL_AUTO;
+ maColorTable.push_back( aColor );
+ nRed = 0;
+ nGreen = 0;
+ nBlue = 0;
+
+ // Color has been completely read,
+ // so this is still a stable status
+ SaveState( RTF_COLORTBL );
+ }
+ break;
+ }
+ }
+ SkipToken(); // the closing brace is evaluated "above"
+}
+
+void SvxRTFParser::ReadFontTable()
+{
+ int _nOpenBrackets = 1; // the first was already detected earlier!!
+ vcl::Font aFont;
+ short nFontNo(0), nInsFontNo (0);
+ OUString sAltNm, sFntNm;
+ bool bIsAltFntNm = false;
+
+ rtl_TextEncoding nSystemChar = lcl_GetDefaultTextEncodingForRTF();
+ aFont.SetCharSet( nSystemChar );
+ SetEncoding( nSystemChar );
+
+ while( _nOpenBrackets && IsParserWorking() )
+ {
+ bool bCheckNewFont = false;
+ int nToken = GetNextToken();
+ switch( nToken )
+ {
+ case '}':
+ bIsAltFntNm = false;
+ // Style has been completely read,
+ // so this is still a stable status
+ if( --_nOpenBrackets <= 1 && IsParserWorking() )
+ SaveState( RTF_FONTTBL );
+ bCheckNewFont = true;
+ nInsFontNo = nFontNo;
+ break;
+ case '{':
+ if( RTF_IGNOREFLAG != GetNextToken() )
+ SkipToken();
+ // immediately skip unknown and all known but non-evaluated
+ // groups
+ else if( RTF_UNKNOWNCONTROL != ( nToken = GetNextToken() ) &&
+ RTF_PANOSE != nToken && RTF_FNAME != nToken &&
+ RTF_FONTEMB != nToken && RTF_FONTFILE != nToken )
+ SkipToken( -2 );
+ else
+ {
+ // filter out at once
+ ReadUnknownData();
+ nToken = GetNextToken();
+ if( '}' != nToken )
+ eState = SvParserState::Error;
+ break;
+ }
+ ++_nOpenBrackets;
+ break;
+ case RTF_FROMAN:
+ aFont.SetFamily( FAMILY_ROMAN );
+ break;
+ case RTF_FSWISS:
+ aFont.SetFamily( FAMILY_SWISS );
+ break;
+ case RTF_FMODERN:
+ aFont.SetFamily( FAMILY_MODERN );
+ break;
+ case RTF_FSCRIPT:
+ aFont.SetFamily( FAMILY_SCRIPT );
+ break;
+ case RTF_FDECOR:
+ aFont.SetFamily( FAMILY_DECORATIVE );
+ break;
+ // for technical/symbolic font of the rtl_TextEncoding is changed!
+ case RTF_FTECH:
+ aFont.SetCharSet( RTL_TEXTENCODING_SYMBOL );
+ [[fallthrough]];
+ case RTF_FNIL:
+ aFont.SetFamily( FAMILY_DONTKNOW );
+ break;
+ case RTF_FCHARSET:
+ if (-1 != nTokenValue)
+ {
+ rtl_TextEncoding nrtl_TextEncoding = rtl_getTextEncodingFromWindowsCharset(
+ static_cast<sal_uInt8>(nTokenValue));
+ aFont.SetCharSet(nrtl_TextEncoding);
+ //When we're in a font, the fontname is in the font
+ //charset, except for symbol fonts I believe
+ if (nrtl_TextEncoding == RTL_TEXTENCODING_SYMBOL)
+ nrtl_TextEncoding = RTL_TEXTENCODING_DONTKNOW;
+ SetEncoding(nrtl_TextEncoding);
+ }
+ break;
+ case RTF_FPRQ:
+ switch( nTokenValue )
+ {
+ case 1:
+ aFont.SetPitch( PITCH_FIXED );
+ break;
+ case 2:
+ aFont.SetPitch( PITCH_VARIABLE );
+ break;
+ }
+ break;
+ case RTF_F:
+ bCheckNewFont = true;
+ nInsFontNo = nFontNo;
+ nFontNo = static_cast<short>(nTokenValue);
+ break;
+ case RTF_FALT:
+ bIsAltFntNm = true;
+ break;
+ case RTF_TEXTTOKEN:
+ DelCharAtEnd( aToken, ';' );
+ if ( !aToken.isEmpty() )
+ {
+ if( bIsAltFntNm )
+ sAltNm = aToken;
+ else
+ sFntNm = aToken;
+ }
+ break;
+ }
+
+ if( bCheckNewFont && 1 >= _nOpenBrackets && !sFntNm.isEmpty() ) // one font is ready
+ {
+ // All data from the font is available, so off to the table
+ if (!sAltNm.isEmpty())
+ sFntNm += ";" + sAltNm;
+
+ aFont.SetFamilyName( sFntNm );
+ m_FontTable.insert(std::make_pair(nInsFontNo, aFont));
+ aFont = vcl::Font();
+ aFont.SetCharSet( nSystemChar );
+ sAltNm.clear();
+ sFntNm.clear();
+ }
+ }
+ SkipToken(); // the closing brace is evaluated "above"
+
+ // set the default font in the Document
+ if( bNewDoc && IsParserWorking() )
+ SetDefault( RTF_DEFF, nDfltFont );
+}
+
+void SvxRTFParser::ClearColorTbl()
+{
+ maColorTable.clear();
+}
+
+void SvxRTFParser::ClearAttrStack()
+{
+ aAttrStack.clear();
+}
+
+void SvxRTFParser::DelCharAtEnd( OUStringBuffer& rStr, const sal_Unicode cDel )
+{
+ rStr.strip(' ');
+ if( !rStr.isEmpty() && cDel == rStr[ rStr.getLength()-1 ])
+ rStr.setLength( rStr.getLength()-1 );
+}
+
+
+const vcl::Font& SvxRTFParser::GetFont( sal_uInt16 nId )
+{
+ SvxRTFFontTbl::const_iterator it = m_FontTable.find( nId );
+ if (it != m_FontTable.end())
+ {
+ return it->second;
+ }
+ const SvxFontItem& rDfltFont =
+ pAttrPool->GetDefaultItem(aPlainMap[SID_ATTR_CHAR_FONT]);
+ pDfltFont->SetFamilyName( rDfltFont.GetStyleName() );
+ pDfltFont->SetFamily( rDfltFont.GetFamily() );
+ return *pDfltFont;
+}
+
+std::unique_ptr<SvxRTFItemStackType> SvxRTFItemStackType::createSvxRTFItemStackType(
+ SfxItemPool& rPool, const WhichRangesContainer& pWhichRange, const EditPosition& rEditPosition)
+{
+ struct MakeUniqueEnabler : public SvxRTFItemStackType
+ {
+ MakeUniqueEnabler(SfxItemPool& rPool, const WhichRangesContainer& pWhichRange, const EditPosition& rEditPosition)
+ : SvxRTFItemStackType(rPool, pWhichRange, rEditPosition)
+ {
+ }
+ };
+ return std::make_unique<MakeUniqueEnabler>(rPool, pWhichRange, rEditPosition);
+}
+
+SvxRTFItemStackType* SvxRTFParser::GetAttrSet_()
+{
+ SvxRTFItemStackType* pCurrent = aAttrStack.empty() ? nullptr : aAttrStack.back().get();
+ std::unique_ptr<SvxRTFItemStackType> xNew;
+ if( pCurrent )
+ xNew = std::make_unique<SvxRTFItemStackType>(*pCurrent, *mxInsertPosition, false/*bCopyAttr*/);
+ else
+ xNew = SvxRTFItemStackType::createSvxRTFItemStackType(*pAttrPool, aWhichMap, *mxInsertPosition);
+ xNew->SetRTFDefaults( GetRTFDefaults() );
+
+ aAttrStack.push_back( std::move(xNew) );
+
+ if (aAttrStack.size() > 96 && utl::ConfigManager::IsFuzzing())
+ throw std::range_error("ecStackOverflow");
+
+ bNewGroup = false;
+ return aAttrStack.back().get();
+}
+
+void SvxRTFParser::ClearStyleAttr_( SvxRTFItemStackType& rStkType )
+{
+ // check attributes to the attributes of the stylesheet or to
+ // the default attrs of the document
+ SfxItemSet &rSet = rStkType.GetAttrSet();
+ const SfxItemPool& rPool = *rSet.GetPool();
+ const SfxPoolItem* pItem;
+ SfxWhichIter aIter( rSet );
+
+ if( !IsChkStyleAttr() ||
+ !rStkType.GetAttrSet().Count() ||
+ m_StyleTable.count( rStkType.nStyleNo ) == 0 )
+ {
+ for( sal_uInt16 nWhich = aIter.GetCurWhich(); nWhich; nWhich = aIter.NextWhich() )
+ {
+ if (SfxItemPool::IsWhich(nWhich) &&
+ SfxItemState::SET == aIter.GetItemState( false, &pItem ) &&
+ rPool.GetDefaultItem( nWhich ) == *pItem )
+ aIter.ClearItem(); // delete
+ }
+ }
+ else
+ {
+ // Delete all Attributes, which are already defined in the Style,
+ // from the current AttrSet.
+ auto & rStyle = m_StyleTable.find(rStkType.nStyleNo)->second;
+ SfxItemSet &rStyleSet = rStyle.aAttrSet;
+ const SfxPoolItem* pSItem;
+ for( sal_uInt16 nWhich = aIter.GetCurWhich(); nWhich; nWhich = aIter.NextWhich() )
+ {
+ if( SfxItemState::SET == rStyleSet.GetItemState( nWhich, true, &pSItem ))
+ {
+ if( SfxItemState::SET == aIter.GetItemState( false, &pItem )
+ && *pItem == *pSItem )
+ rSet.ClearItem( nWhich ); // delete
+ }
+ else if (SfxItemPool::IsWhich(nWhich) &&
+ SfxItemState::SET == aIter.GetItemState( false, &pItem ) &&
+ rPool.GetDefaultItem( nWhich ) == *pItem )
+ rSet.ClearItem( nWhich ); // delete
+ }
+ }
+}
+
+void SvxRTFParser::AttrGroupEnd() // process the current, delete from Stack
+{
+ if( aAttrStack.empty() )
+ return;
+
+ std::unique_ptr<SvxRTFItemStackType> pOld = std::move(aAttrStack.back());
+ aAttrStack.pop_back();
+ SvxRTFItemStackType *pCurrent = aAttrStack.empty() ? nullptr : aAttrStack.back().get();
+
+ do { // middle check loop
+ sal_Int32 nOldSttNdIdx = pOld->mxStartNodeIdx->GetIdx();
+ if (pOld->maChildList.empty() &&
+ ((!pOld->aAttrSet.Count() && !pOld->nStyleNo ) ||
+ (nOldSttNdIdx == mxInsertPosition->GetNodeIdx() &&
+ pOld->nSttCnt == mxInsertPosition->GetCntIdx() )))
+ break; // no attributes or Area
+
+ // set only the attributes that are different from the parent
+ if( pCurrent && pOld->aAttrSet.Count() )
+ {
+ SfxItemIter aIter( pOld->aAttrSet );
+ const SfxPoolItem* pItem = aIter.GetCurItem(), *pGet;
+ do
+ {
+ if( SfxItemState::SET == pCurrent->aAttrSet.GetItemState(
+ pItem->Which(), false, &pGet ) &&
+ *pItem == *pGet )
+ aIter.ClearItem();
+
+ pItem = aIter.NextItem();
+ } while (pItem);
+
+ if (!pOld->aAttrSet.Count() && pOld->maChildList.empty() &&
+ !pOld->nStyleNo )
+ break;
+ }
+
+ // Set all attributes which have been defined from start until here
+ bool bCrsrBack = !mxInsertPosition->GetCntIdx();
+ if( bCrsrBack )
+ {
+ // at the beginning of a paragraph? Move back one position
+ sal_Int32 nNd = mxInsertPosition->GetNodeIdx();
+ MovePos(false);
+ // if can not move backward then later don't move forward !
+ bCrsrBack = nNd != mxInsertPosition->GetNodeIdx();
+ }
+
+ if( pOld->mxStartNodeIdx->GetIdx() < mxInsertPosition->GetNodeIdx() ||
+ ( pOld->mxStartNodeIdx->GetIdx() == mxInsertPosition->GetNodeIdx() &&
+ pOld->nSttCnt <= mxInsertPosition->GetCntIdx() ) )
+ {
+ if( !bCrsrBack )
+ {
+ // all pard attributes are only valid until the previous
+ // paragraph !!
+ if( nOldSttNdIdx == mxInsertPosition->GetNodeIdx() )
+ {
+ }
+ else
+ {
+ // Now it gets complicated:
+ // - all character attributes sre keep the area
+ // - all paragraph attributes to get the area
+ // up to the previous paragraph
+ auto xNew = std::make_unique<SvxRTFItemStackType>(*pOld, *mxInsertPosition, true);
+ xNew->aAttrSet.SetParent( pOld->aAttrSet.GetParent() );
+
+ // Delete all paragraph attributes from xNew
+ for (const auto& pair : aPardMap.data)
+ if (sal_uInt16 wid = pair.second)
+ xNew->aAttrSet.ClearItem(wid);
+ xNew->SetRTFDefaults( GetRTFDefaults() );
+
+ // Were there any?
+ if( xNew->aAttrSet.Count() == pOld->aAttrSet.Count() )
+ {
+ xNew.reset();
+ }
+ else
+ {
+ xNew->nStyleNo = 0;
+
+ // Now span the real area of xNew from old
+ SetEndPrevPara( pOld->mxEndNodeIdx, pOld->nEndCnt );
+ xNew->nSttCnt = 0;
+
+ if( IsChkStyleAttr() )
+ {
+ ClearStyleAttr_( *pOld );
+ ClearStyleAttr_( *xNew ); //#i10381#, methinks.
+ }
+
+ if( pCurrent )
+ {
+ pCurrent->Add(std::move(pOld));
+ pCurrent->Add(std::move(xNew));
+ }
+ else
+ {
+ // Last off the stack, thus cache it until the next text was
+ // read. (Span no attributes!)
+
+ m_AttrSetList.push_back(std::move(pOld));
+ m_AttrSetList.push_back(std::move(xNew));
+ }
+ break;
+ }
+ }
+ }
+
+ pOld->mxEndNodeIdx = mxInsertPosition->MakeNodeIdx();
+ pOld->nEndCnt = mxInsertPosition->GetCntIdx();
+
+ /*
+ #i21422#
+ If the parent (pCurrent) sets something e.g. , and the child (pOld)
+ unsets it and the style both are based on has it unset then
+ clearing the pOld by looking at the style is clearly a disaster
+ as the text ends up with pCurrents bold and not pOlds no bold, this
+ should be rethought out. For the moment its safest to just do
+ the clean if we have no parent, all we suffer is too many
+ redundant properties.
+ */
+ if (IsChkStyleAttr() && !pCurrent)
+ ClearStyleAttr_( *pOld );
+
+ if( pCurrent )
+ {
+ pCurrent->Add(std::move(pOld));
+ // split up and create new entry, because it makes no sense
+ // to create a "so long" depend list. Bug 95010
+ if (bCrsrBack && 50 < pCurrent->maChildList.size())
+ {
+ // at the beginning of a paragraph? Move back one position
+ MovePos();
+ bCrsrBack = false;
+
+ // Open a new Group.
+ auto xNew(std::make_unique<SvxRTFItemStackType>(*pCurrent, *mxInsertPosition, true));
+ xNew->SetRTFDefaults( GetRTFDefaults() );
+
+ // Set all until here valid Attributes
+ AttrGroupEnd();
+ pCurrent = aAttrStack.empty() ? nullptr : aAttrStack.back().get(); // can be changed after AttrGroupEnd!
+ xNew->aAttrSet.SetParent( pCurrent ? &pCurrent->aAttrSet : nullptr );
+ aAttrStack.push_back( std::move(xNew) );
+ }
+ }
+ else
+ // Last off the stack, thus cache it until the next text was
+ // read. (Span no attributes!)
+ m_AttrSetList.push_back(std::move(pOld));
+ }
+
+ if( bCrsrBack )
+ // at the beginning of a paragraph? Move back one position
+ MovePos();
+
+ } while( false );
+
+ bNewGroup = false;
+}
+
+void SvxRTFParser::SetAllAttrOfStk() // end all Attr. and set it into doc
+{
+ // repeat until all attributes will be taken from stack
+ while( !aAttrStack.empty() )
+ AttrGroupEnd();
+
+ for (size_t n = m_AttrSetList.size(); n; )
+ {
+ auto const& pStkSet = m_AttrSetList[--n];
+ SetAttrSet( *pStkSet );
+ pStkSet->DropChildList();
+ m_AttrSetList.pop_back();
+ }
+}
+
+// sets all the attributes that are different from the current
+void SvxRTFParser::SetAttrSet( SvxRTFItemStackType &rSet )
+{
+ // Was DefTab never read? then set to default
+ if( !bIsSetDfltTab )
+ SetDefault( RTF_DEFTAB, 720 );
+
+ if (!rSet.maChildList.empty())
+ rSet.Compress( *this );
+ if( rSet.aAttrSet.Count() || rSet.nStyleNo )
+ SetAttrInDoc( rSet );
+
+ // then process all the children
+ for (size_t n = 0; n < rSet.maChildList.size(); ++n)
+ SetAttrSet( *(rSet.maChildList[ n ]) );
+}
+
+// Has no text been inserted yet? (SttPos from the top Stack entry!)
+bool SvxRTFParser::IsAttrSttPos()
+{
+ SvxRTFItemStackType* pCurrent = aAttrStack.empty() ? nullptr : aAttrStack.back().get();
+ return !pCurrent || (pCurrent->mxStartNodeIdx->GetIdx() == mxInsertPosition->GetNodeIdx() &&
+ pCurrent->nSttCnt == mxInsertPosition->GetCntIdx());
+}
+
+
+void SvxRTFParser::SetAttrInDoc( SvxRTFItemStackType & )
+{
+}
+
+void SvxRTFParser::BuildWhichTable()
+{
+ aWhichMap.reset();
+
+ // Here are the IDs for all paragraph attributes, which can be detected by
+ // SvxParser and can be set in a SfxItemSet. The IDs are set correctly through
+ // the SlotIds from POOL.
+ static constexpr sal_uInt16 WIDS1[] {
+ SID_ATTR_PARA_LINESPACE,
+ SID_ATTR_PARA_ADJUST,
+ SID_ATTR_TABSTOP,
+ SID_ATTR_PARA_HYPHENZONE,
+ SID_ATTR_LRSPACE,
+ SID_ATTR_ULSPACE,
+ SID_ATTR_BRUSH,
+ SID_ATTR_BORDER_OUTER,
+ SID_ATTR_BORDER_SHADOW,
+ SID_ATTR_PARA_OUTLLEVEL,
+ SID_ATTR_PARA_SPLIT,
+ SID_ATTR_PARA_KEEP,
+ SID_PARA_VERTALIGN,
+ SID_ATTR_PARA_SCRIPTSPACE,
+ SID_ATTR_PARA_HANGPUNCTUATION,
+ SID_ATTR_PARA_FORBIDDEN_RULES,
+ SID_ATTR_FRAMEDIRECTION,
+ };
+ for (sal_uInt16 nWid : WIDS1)
+ {
+ sal_uInt16 nTrueWid = pAttrPool->GetTrueWhich(nWid, false);
+ aPardMap.data[nWid] = nTrueWid;
+ if (nTrueWid == 0)
+ continue;
+ aWhichMap = aWhichMap.MergeRange(nTrueWid, nTrueWid);
+ }
+
+ // Here are the IDs for all character attributes, which can be detected by
+ // SvxParser and can be set in a SfxItemSet. The IDs are set correctly through
+ // the SlotIds from POOL.
+ static constexpr sal_uInt16 WIDS[] {
+ SID_ATTR_CHAR_CASEMAP, SID_ATTR_BRUSH_CHAR, SID_ATTR_CHAR_COLOR,
+ SID_ATTR_CHAR_CONTOUR, SID_ATTR_CHAR_STRIKEOUT, SID_ATTR_CHAR_ESCAPEMENT,
+ SID_ATTR_CHAR_FONT, SID_ATTR_CHAR_FONTHEIGHT, SID_ATTR_CHAR_KERNING,
+ SID_ATTR_CHAR_LANGUAGE, SID_ATTR_CHAR_POSTURE, SID_ATTR_CHAR_SHADOWED,
+ SID_ATTR_CHAR_UNDERLINE, SID_ATTR_CHAR_OVERLINE, SID_ATTR_CHAR_WEIGHT,
+ SID_ATTR_CHAR_WORDLINEMODE, SID_ATTR_CHAR_AUTOKERN, SID_ATTR_CHAR_CJK_FONT,
+ SID_ATTR_CHAR_CJK_FONTHEIGHT, sal_uInt16(SID_ATTR_CHAR_CJK_LANGUAGE), SID_ATTR_CHAR_CJK_POSTURE,
+ SID_ATTR_CHAR_CJK_WEIGHT, SID_ATTR_CHAR_CTL_FONT, SID_ATTR_CHAR_CTL_FONTHEIGHT,
+ SID_ATTR_CHAR_CTL_LANGUAGE, SID_ATTR_CHAR_CTL_POSTURE, SID_ATTR_CHAR_CTL_WEIGHT,
+ SID_ATTR_CHAR_EMPHASISMARK, SID_ATTR_CHAR_TWO_LINES, SID_ATTR_CHAR_SCALEWIDTH,
+ SID_ATTR_CHAR_ROTATED, SID_ATTR_CHAR_RELIEF, SID_ATTR_CHAR_HIDDEN,
+ };
+ for (sal_uInt16 nWid : WIDS)
+ {
+ sal_uInt16 nTrueWid = pAttrPool->GetTrueWhich(nWid, false);
+ aPlainMap.data[nWid] = nTrueWid;
+ if (nTrueWid == 0)
+ continue;
+ aWhichMap = aWhichMap.MergeRange(nTrueWid, nTrueWid);
+ }
+}
+
+const SfxItemSet& SvxRTFParser::GetRTFDefaults()
+{
+ if( !pRTFDefaults )
+ {
+ pRTFDefaults.reset(new SfxItemSet(*pAttrPool, aWhichMap));
+ if (const sal_uInt16 nId = aPardMap[SID_ATTR_PARA_SCRIPTSPACE])
+ {
+ SvxScriptSpaceItem aItem( false, nId );
+ if( bNewDoc )
+ pAttrPool->SetPoolDefaultItem( aItem );
+ else
+ pRTFDefaults->Put( aItem );
+ }
+ }
+ return *pRTFDefaults;
+}
+
+
+SvxRTFStyleType::SvxRTFStyleType(SfxItemPool& rPool, const WhichRangesContainer& pWhichRange)
+ : aAttrSet(rPool, pWhichRange)
+ , nBasedOn(0)
+ , nOutlineNo(sal_uInt8(-1)) // not set
+{
+}
+
+SvxRTFItemStackType::SvxRTFItemStackType(
+ SfxItemPool& rPool, const WhichRangesContainer& pWhichRange,
+ const EditPosition& rPos )
+ : aAttrSet( rPool, pWhichRange )
+ , mxStartNodeIdx(rPos.MakeNodeIdx())
+#if !defined(__COVERITY__)
+ // coverity 2020 has difficulty wrt std::optional leading to bogus 'Uninitialized scalar variable'
+ , mxEndNodeIdx(mxStartNodeIdx)
+#endif
+ , nSttCnt(rPos.GetCntIdx())
+ , nEndCnt(nSttCnt)
+ , nStyleNo(0)
+{
+}
+
+SvxRTFItemStackType::SvxRTFItemStackType(
+ const SvxRTFItemStackType& rCpy,
+ const EditPosition& rPos,
+ bool const bCopyAttr )
+ : aAttrSet( *rCpy.aAttrSet.GetPool(), rCpy.aAttrSet.GetRanges() )
+ , mxStartNodeIdx(rPos.MakeNodeIdx())
+#if !defined(__COVERITY__)
+ // coverity 2020 has difficulty wrt std::optional leading to bogus 'Uninitialized scalar variable'
+ , mxEndNodeIdx(mxStartNodeIdx)
+#endif
+ , nSttCnt(rPos.GetCntIdx())
+ , nEndCnt(nSttCnt)
+ , nStyleNo(rCpy.nStyleNo)
+{
+ aAttrSet.SetParent( &rCpy.aAttrSet );
+ if( bCopyAttr )
+ aAttrSet.Put( rCpy.aAttrSet );
+}
+
+/* ofz#13491 SvxRTFItemStackType dtor recursively
+ calls the dtor of its m_pChildList. The recurse
+ depth can grow sufficiently to trigger asan.
+
+ So breadth-first iterate through the nodes
+ and make a flat vector of them which can
+ be iterated through in order of most
+ distant from root first and release
+ their children linearly
+*/
+void SvxRTFItemStackType::DropChildList()
+{
+ if (maChildList.empty())
+ return;
+
+ std::vector<SvxRTFItemStackType*> bfs;
+ std::queue<SvxRTFItemStackType*> aQueue;
+ aQueue.push(this);
+
+ while (!aQueue.empty())
+ {
+ auto* front = aQueue.front();
+ aQueue.pop();
+ if (!front->maChildList.empty())
+ {
+ for (const auto& a : front->maChildList)
+ aQueue.push(a.get());
+ bfs.push_back(front);
+ }
+ }
+
+ for (auto it = bfs.rbegin(); it != bfs.rend(); ++it)
+ {
+ SvxRTFItemStackType* pNode = *it;
+ pNode->maChildList.clear();
+ }
+}
+
+SvxRTFItemStackType::~SvxRTFItemStackType()
+{
+}
+
+void SvxRTFItemStackType::Add(std::unique_ptr<SvxRTFItemStackType> pIns)
+{
+ maChildList.push_back(std::move(pIns));
+}
+
+void SvxRTFItemStackType::SetStartPos( const EditPosition& rPos )
+{
+ mxStartNodeIdx = rPos.MakeNodeIdx();
+ mxEndNodeIdx = mxStartNodeIdx;
+ nSttCnt = rPos.GetCntIdx();
+}
+
+void SvxRTFItemStackType::Compress( const SvxRTFParser& rParser )
+{
+ ENSURE_OR_RETURN_VOID(!maChildList.empty(), "Compress: ChildList empty");
+
+ SvxRTFItemStackType* pTmp = maChildList[0].get();
+
+ if( !pTmp->aAttrSet.Count() ||
+ mxStartNodeIdx->GetIdx() != pTmp->mxStartNodeIdx->GetIdx() ||
+ nSttCnt != pTmp->nSttCnt )
+ return;
+
+ EditNodeIdx aLastNd = *pTmp->mxEndNodeIdx;
+ sal_Int32 nLastCnt = pTmp->nEndCnt;
+
+ SfxItemSet aMrgSet( pTmp->aAttrSet );
+ for (size_t n = 1; n < maChildList.size(); ++n)
+ {
+ pTmp = maChildList[n].get();
+ if (!pTmp->maChildList.empty())
+ pTmp->Compress( rParser );
+
+ if( !pTmp->nSttCnt
+ ? (aLastNd.GetIdx()+1 != pTmp->mxStartNodeIdx->GetIdx() ||
+ !rParser.IsEndPara( &aLastNd, nLastCnt ) )
+ : ( pTmp->nSttCnt != nLastCnt ||
+ aLastNd.GetIdx() != pTmp->mxStartNodeIdx->GetIdx() ))
+ {
+ while (++n < maChildList.size())
+ {
+ pTmp = maChildList[n].get();
+ if (!pTmp->maChildList.empty())
+ pTmp->Compress( rParser );
+ }
+ return;
+ }
+
+ if( n )
+ {
+ // Search for all which are set over the whole area
+ SfxItemIter aIter( aMrgSet );
+ const SfxPoolItem* pItem;
+ const SfxPoolItem* pIterItem = aIter.GetCurItem();
+ do {
+ sal_uInt16 nWhich = pIterItem->Which();
+ if( SfxItemState::SET != pTmp->aAttrSet.GetItemState( nWhich,
+ false, &pItem ) || *pItem != *pIterItem)
+ aIter.ClearItem();
+
+ pIterItem = aIter.NextItem();
+ } while(pIterItem);
+
+ if( !aMrgSet.Count() )
+ return;
+ }
+
+ aLastNd = *pTmp->mxEndNodeIdx;
+ nLastCnt = pTmp->nEndCnt;
+ }
+
+ if( mxEndNodeIdx->GetIdx() != aLastNd.GetIdx() || nEndCnt != nLastCnt )
+ return;
+
+ // It can be merged
+ aAttrSet.Put( aMrgSet );
+
+ size_t n = 0, nChildLen = maChildList.size();
+ while (n < nChildLen)
+ {
+ pTmp = maChildList[n].get();
+ pTmp->aAttrSet.Differentiate( aMrgSet );
+
+ if (pTmp->maChildList.empty() && !pTmp->aAttrSet.Count() && !pTmp->nStyleNo)
+ {
+ maChildList.erase( maChildList.begin() + n );
+ --nChildLen;
+ continue;
+ }
+ ++n;
+ }
+}
+void SvxRTFItemStackType::SetRTFDefaults( const SfxItemSet& rDefaults )
+{
+ if( rDefaults.Count() )
+ {
+ SfxItemIter aIter( rDefaults );
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+ do {
+ sal_uInt16 nWhich = pItem->Which();
+ if( SfxItemState::SET != aAttrSet.GetItemState( nWhich, false ))
+ aAttrSet.Put(*pItem);
+
+ pItem = aIter.NextItem();
+ } while(pItem);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/uno/UnoForbiddenCharsTable.cxx b/editeng/source/uno/UnoForbiddenCharsTable.cxx
new file mode 100644
index 0000000000..5e77a9252f
--- /dev/null
+++ b/editeng/source/uno/UnoForbiddenCharsTable.cxx
@@ -0,0 +1,132 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/container/NoSuchElementException.hpp>
+#include <editeng/UnoForbiddenCharsTable.hxx>
+#include <editeng/forbiddencharacterstable.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::i18n;
+using namespace ::cppu;
+
+SvxUnoForbiddenCharsTable::SvxUnoForbiddenCharsTable(std::shared_ptr<SvxForbiddenCharactersTable> xForbiddenChars)
+ : mxForbiddenChars(std::move(xForbiddenChars))
+{
+}
+
+SvxUnoForbiddenCharsTable::~SvxUnoForbiddenCharsTable()
+{
+}
+
+void SvxUnoForbiddenCharsTable::onChange()
+{
+}
+
+ForbiddenCharacters SvxUnoForbiddenCharsTable::getForbiddenCharacters( const lang::Locale& rLocale )
+{
+ SolarMutexGuard aGuard;
+
+ if (!mxForbiddenChars)
+ throw RuntimeException("No Forbidden Characters present");
+
+ const LanguageType eLang = LanguageTag::convertToLanguageType( rLocale );
+ const ForbiddenCharacters* pForbidden = mxForbiddenChars->GetForbiddenCharacters( eLang, false );
+ if(!pForbidden)
+ throw NoSuchElementException();
+
+ return *pForbidden;
+}
+
+sal_Bool SvxUnoForbiddenCharsTable::hasForbiddenCharacters( const lang::Locale& rLocale )
+{
+ SolarMutexGuard aGuard;
+
+ if (!mxForbiddenChars)
+ return false;
+
+ const LanguageType eLang = LanguageTag::convertToLanguageType( rLocale );
+ const ForbiddenCharacters* pForbidden = mxForbiddenChars->GetForbiddenCharacters( eLang, false );
+
+ return nullptr != pForbidden;
+}
+
+void SvxUnoForbiddenCharsTable::setForbiddenCharacters(const lang::Locale& rLocale, const ForbiddenCharacters& rForbiddenCharacters )
+{
+ SolarMutexGuard aGuard;
+
+ if (!mxForbiddenChars)
+ throw RuntimeException("No Forbidden Characters present");
+
+ const LanguageType eLang = LanguageTag::convertToLanguageType( rLocale );
+ mxForbiddenChars->SetForbiddenCharacters( eLang, rForbiddenCharacters );
+
+ onChange();
+}
+
+void SvxUnoForbiddenCharsTable::removeForbiddenCharacters( const lang::Locale& rLocale )
+{
+ SolarMutexGuard aGuard;
+
+ if (!mxForbiddenChars)
+ throw RuntimeException("No Forbidden Characters present");
+
+ const LanguageType eLang = LanguageTag::convertToLanguageType( rLocale );
+ mxForbiddenChars->ClearForbiddenCharacters( eLang );
+
+ onChange();
+}
+
+// XSupportedLocales
+Sequence< lang::Locale > SAL_CALL SvxUnoForbiddenCharsTable::getLocales()
+{
+ SolarMutexGuard aGuard;
+
+ const sal_Int32 nCount = mxForbiddenChars ? mxForbiddenChars->GetMap().size() : 0;
+
+ Sequence< lang::Locale > aLocales( nCount );
+ if( nCount )
+ {
+ lang::Locale* pLocales = aLocales.getArray();
+
+ for (auto const& elem : mxForbiddenChars->GetMap())
+ {
+ const LanguageType nLanguage = elem.first;
+ *pLocales++ = LanguageTag( nLanguage ).getLocale();
+ }
+ }
+
+ return aLocales;
+}
+
+sal_Bool SAL_CALL SvxUnoForbiddenCharsTable::hasLocale( const lang::Locale& aLocale )
+{
+ SolarMutexGuard aGuard;
+
+ return hasForbiddenCharacters( aLocale );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/uno/unoedhlp.cxx b/editeng/source/uno/unoedhlp.cxx
new file mode 100644
index 0000000000..2a1b1e2bd5
--- /dev/null
+++ b/editeng/source/uno/unoedhlp.cxx
@@ -0,0 +1,250 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <editeng/unoedhlp.hxx>
+#include <editeng/editdata.hxx>
+#include <editeng/editeng.hxx>
+#include <svl/itemset.hxx>
+
+#include <osl/diagnose.h>
+
+
+SvxEditSourceHint::SvxEditSourceHint( SfxHintId _nId ) :
+ TextHint( _nId ),
+ mnStart( 0 ),
+ mnEnd( 0 )
+{
+}
+
+SvxEditSourceHint::SvxEditSourceHint( SfxHintId _nId, sal_Int32 nValue, sal_Int32 nStart, sal_Int32 nEnd ) :
+ TextHint( _nId, nValue ),
+ mnStart( nStart),
+ mnEnd( nEnd )
+{
+}
+
+
+std::unique_ptr<SfxHint> SvxEditSourceHelper::EENotification2Hint( EENotify const * aNotify )
+{
+ if( aNotify )
+ {
+ switch( aNotify->eNotificationType )
+ {
+ case EE_NOTIFY_TEXTMODIFIED:
+ return std::unique_ptr<SfxHint>( new TextHint( SfxHintId::TextModified, aNotify->nParagraph ) );
+
+ case EE_NOTIFY_PARAGRAPHINSERTED:
+ return std::unique_ptr<SfxHint>( new TextHint( SfxHintId::TextParaInserted, aNotify->nParagraph ) );
+
+ case EE_NOTIFY_PARAGRAPHREMOVED:
+ return std::unique_ptr<SfxHint>( new TextHint( SfxHintId::TextParaRemoved, aNotify->nParagraph ) );
+
+ case EE_NOTIFY_PARAGRAPHSMOVED:
+ return std::unique_ptr<SfxHint>( new SvxEditSourceHint( SfxHintId::EditSourceParasMoved, aNotify->nParagraph, aNotify->nParam1, aNotify->nParam2 ) );
+
+ case EE_NOTIFY_TextHeightChanged:
+ return std::unique_ptr<SfxHint>( new TextHint( SfxHintId::TextHeightChanged, aNotify->nParagraph ) );
+
+ case EE_NOTIFY_TEXTVIEWSCROLLED:
+ return std::unique_ptr<SfxHint>( new TextHint( SfxHintId::TextViewScrolled ) );
+
+ case EE_NOTIFY_TEXTVIEWSELECTIONCHANGED:
+ return std::unique_ptr<SfxHint>( new SvxEditSourceHint( SfxHintId::EditSourceSelectionChanged ) );
+
+ case EE_NOTIFY_PROCESSNOTIFICATIONS:
+ return std::unique_ptr<SfxHint>( new TextHint( SfxHintId::TextProcessNotifications ));
+
+ case EE_NOTIFY_TEXTVIEWSELECTIONCHANGED_ENDD_PARA:
+ return std::unique_ptr<SfxHint>( new SvxEditSourceHintEndPara );
+ default:
+ OSL_FAIL( "SvxEditSourceHelper::EENotification2Hint unknown notification" );
+ break;
+ }
+ }
+
+ return std::make_unique<SfxHint>( );
+}
+
+void SvxEditSourceHelper::GetAttributeRun( sal_Int32& nStartIndex, sal_Int32& nEndIndex, const EditEngine& rEE, sal_Int32 nPara, sal_Int32 nIndex, bool bInCell )
+{
+ // IA2 CWS introduced bInCell, but also did many other changes here.
+ // Need to verify implementation with AT (IA2 and ATK)
+ // Old implementation at the end of the method for reference...
+
+ //added dummy attributes for the default text
+ std::vector<EECharAttrib> aCharAttribs, aTempCharAttribs;
+ rEE.GetCharAttribs( nPara, aTempCharAttribs );
+
+ if (!aTempCharAttribs.empty())
+ {
+ sal_Int32 nIndex2 = 0;
+ sal_Int32 nParaLen = rEE.GetTextLen(nPara);
+ for (size_t nAttr = 0; nAttr < aTempCharAttribs.size(); ++nAttr)
+ {
+ if (nIndex2 < aTempCharAttribs[nAttr].nStart)
+ {
+ EECharAttrib aEEAttr(nIndex2, aTempCharAttribs[nAttr].nStart);
+ aCharAttribs.insert(aCharAttribs.begin() + nAttr, aEEAttr);
+ }
+ nIndex2 = aTempCharAttribs[nAttr].nEnd;
+ aCharAttribs.push_back(aTempCharAttribs[nAttr]);
+ }
+ if ( nIndex2 != nParaLen )
+ {
+ EECharAttrib aEEAttr(nIndex2, nParaLen);
+ aCharAttribs.push_back(aEEAttr);
+ }
+ }
+ // find closest index in front of nIndex
+ sal_Int32 nCurrIndex;
+ sal_Int32 nClosestStartIndex_s = 0, nClosestStartIndex_e = 0;
+ for (auto const& charAttrib : aCharAttribs)
+ {
+ nCurrIndex = charAttrib.nStart;
+
+ if( nCurrIndex > nClosestStartIndex_s &&
+ nCurrIndex <= nIndex)
+ {
+ nClosestStartIndex_s = nCurrIndex;
+ }
+ nCurrIndex = charAttrib.nEnd;
+ if ( nCurrIndex > nClosestStartIndex_e &&
+ nCurrIndex < nIndex )
+ {
+ nClosestStartIndex_e = nCurrIndex;
+ }
+ }
+ sal_Int32 nClosestStartIndex = std::max(nClosestStartIndex_s, nClosestStartIndex_e);
+
+ // find closest index behind of nIndex
+ sal_Int32 nClosestEndIndex_s, nClosestEndIndex_e;
+ nClosestEndIndex_s = nClosestEndIndex_e = rEE.GetTextLen(nPara);
+ for (auto const& charAttrib : aCharAttribs)
+ {
+ nCurrIndex = charAttrib.nEnd;
+
+ if( nCurrIndex > nIndex &&
+ nCurrIndex < nClosestEndIndex_e )
+ {
+ nClosestEndIndex_e = nCurrIndex;
+ }
+ nCurrIndex = charAttrib.nStart;
+ if ( nCurrIndex > nIndex &&
+ nCurrIndex < nClosestEndIndex_s)
+ {
+ nClosestEndIndex_s = nCurrIndex;
+ }
+ }
+ sal_Int32 nClosestEndIndex = std::min(nClosestEndIndex_s, nClosestEndIndex_e);
+
+ nStartIndex = nClosestStartIndex;
+ nEndIndex = nClosestEndIndex;
+
+ if ( !bInCell )
+ return;
+
+ EPosition aStartPos( nPara, nStartIndex ), aEndPos( nPara, nEndIndex );
+ sal_Int32 nParaCount = rEE.GetParagraphCount();
+ sal_Int32 nCrrntParaLen = rEE.GetTextLen(nPara);
+ //need to find closest index in front of nIndex in the previous paragraphs
+ if ( aStartPos.nIndex == 0 )
+ {
+ SfxItemSet aCrrntSet = rEE.GetAttribs( nPara, 0, 1, GetAttribsFlags::CHARATTRIBS );
+ for ( sal_Int32 nParaIdx = nPara-1; nParaIdx >= 0; nParaIdx-- )
+ {
+ sal_uInt32 nLen = rEE.GetTextLen(nParaIdx);
+ if ( nLen )
+ {
+ sal_Int32 nStartIdx, nEndIdx;
+ GetAttributeRun( nStartIdx, nEndIdx, rEE, nParaIdx, nLen );
+ SfxItemSet aSet = rEE.GetAttribs( nParaIdx, nLen-1, nLen, GetAttribsFlags::CHARATTRIBS );
+ if ( aSet == aCrrntSet )
+ {
+ aStartPos.nPara = nParaIdx;
+ aStartPos.nIndex = nStartIdx;
+ if ( aStartPos.nIndex != 0 )
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
+ //need find closest index behind nIndex in the following paragraphs
+ if ( aEndPos.nIndex == nCrrntParaLen )
+ {
+ SfxItemSet aCrrntSet = rEE.GetAttribs( nPara, nCrrntParaLen-1, nCrrntParaLen, GetAttribsFlags::CHARATTRIBS );
+ for ( sal_Int32 nParaIdx = nPara+1; nParaIdx < nParaCount; nParaIdx++ )
+ {
+ sal_Int32 nLen = rEE.GetTextLen( nParaIdx );
+ if ( nLen )
+ {
+ sal_Int32 nStartIdx, nEndIdx;
+ GetAttributeRun( nStartIdx, nEndIdx, rEE, nParaIdx, 0 );
+ SfxItemSet aSet = rEE.GetAttribs( nParaIdx, 0, 1, GetAttribsFlags::CHARATTRIBS );
+ if ( aSet == aCrrntSet )
+ {
+ aEndPos.nPara = nParaIdx;
+ aEndPos.nIndex = nEndIdx;
+ if ( aEndPos.nIndex != nLen )
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
+ nStartIndex = 0;
+ if ( aStartPos.nPara > 0 )
+ {
+ for ( sal_Int32 i = 0; i < aStartPos.nPara; i++ )
+ {
+ nStartIndex += rEE.GetTextLen(i)+1;
+ }
+ }
+ nStartIndex += aStartPos.nIndex;
+ nEndIndex = 0;
+ if ( aEndPos.nPara > 0 )
+ {
+ for ( sal_Int32 i = 0; i < aEndPos.nPara; i++ )
+ {
+ nEndIndex += rEE.GetTextLen(i)+1;
+ }
+ }
+ nEndIndex += aEndPos.nIndex;
+}
+
+Point SvxEditSourceHelper::EEToUserSpace( const Point& rPoint, const Size& rEESize, bool bIsVertical )
+{
+ return bIsVertical ? Point( -rPoint.Y() + rEESize.Height(), rPoint.X() ) : rPoint;
+}
+
+Point SvxEditSourceHelper::UserSpaceToEE( const Point& rPoint, const Size& rEESize, bool bIsVertical )
+{
+ return bIsVertical ? Point( rPoint.Y(), -rPoint.X() + rEESize.Height() ) : rPoint;
+}
+
+tools::Rectangle SvxEditSourceHelper::EEToUserSpace( const tools::Rectangle& rRect, const Size& rEESize, bool bIsVertical )
+{
+ return bIsVertical ? tools::Rectangle( EEToUserSpace(rRect.BottomLeft(), rEESize, bIsVertical),
+ EEToUserSpace(rRect.TopRight(), rEESize, bIsVertical) ) : rRect;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/uno/unoedprx.cxx b/editeng/source/uno/unoedprx.cxx
new file mode 100644
index 0000000000..20d5df281b
--- /dev/null
+++ b/editeng/source/uno/unoedprx.cxx
@@ -0,0 +1,1209 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+// Global header
+
+
+#include <utility>
+#include <memory>
+#include <vector>
+#include <algorithm>
+#include <osl/diagnose.h>
+#include <svl/itemset.hxx>
+#include <tools/debug.hxx>
+
+
+// Project-local header
+
+
+#include <editeng/unoedprx.hxx>
+#include <editeng/editdata.hxx>
+#include <editeng/editeng.hxx>
+#include <AccessibleStringWrap.hxx>
+#include <editeng/outliner.hxx>
+
+using namespace ::com::sun::star;
+
+namespace {
+
+class SvxAccessibleTextIndex
+{
+public:
+ SvxAccessibleTextIndex() :
+ mnPara(0),
+ mnIndex(0),
+ mnEEIndex(0),
+ mnFieldOffset(0),
+ mnFieldLen(0),
+ mnBulletOffset(0),
+ mnBulletLen(0),
+ mbInField(false),
+ mbInBullet(false) {};
+
+ // Get/Set current paragraph
+ void SetParagraph( sal_Int32 nPara )
+ {
+ mnPara = nPara;
+ }
+ sal_Int32 GetParagraph() const { return mnPara; }
+
+ /** Set the index in the UAA semantic
+
+ @param nIndex
+ The index from the UA API (fields and bullets are expanded)
+
+ @param rTF
+ The text forwarder to use in the calculations
+ */
+ void SetIndex( sal_Int32 nIndex, const SvxTextForwarder& rTF );
+ void SetIndex( sal_Int32 nPara, sal_Int32 nIndex, const SvxTextForwarder& rTF ) { SetParagraph(nPara); SetIndex(nIndex, rTF); }
+ sal_Int32 GetIndex() const { return mnIndex; }
+
+ /** Set the index in the edit engine semantic
+
+ Update the object state to reflect the given index position in
+ EditEngine/Outliner index values
+
+ @param nEEIndex
+ The index from the edit engine (fields span exactly one index increment)
+
+ @param rTF
+ The text forwarder to use in the calculations
+ */
+ void SetEEIndex( sal_Int32 nEEIndex, const SvxTextForwarder& rTF );
+ void SetEEIndex( sal_Int32 nPara, sal_Int32 nEEIndex, const SvxTextForwarder& rTF ) { SetParagraph(nPara); SetEEIndex(nEEIndex, rTF); }
+ sal_Int32 GetEEIndex() const;
+
+ void SetFieldOffset( sal_Int32 nOffset, sal_Int32 nLen ) { mnFieldOffset = nOffset; mnFieldLen = nLen; }
+ sal_Int32 GetFieldOffset() const { return mnFieldOffset; }
+ sal_Int32 GetFieldLen() const { return mnFieldLen; }
+ void AreInField() { mbInField = true; }
+ bool InField() const { return mbInField; }
+
+ void SetBulletOffset( sal_Int32 nOffset, sal_Int32 nLen ) { mnBulletOffset = nOffset; mnBulletLen = nLen; }
+ sal_Int32 GetBulletOffset() const { return mnBulletOffset; }
+ sal_Int32 GetBulletLen() const { return mnBulletLen; }
+ bool InBullet() const { return mbInBullet; }
+
+ /// returns false if the given range is non-editable (e.g. contains bullets or _parts_ of fields)
+ bool IsEditableRange( const SvxAccessibleTextIndex& rEnd ) const;
+
+private:
+ sal_Int32 mnPara;
+ sal_Int32 mnIndex;
+ sal_Int32 mnEEIndex;
+ sal_Int32 mnFieldOffset;
+ sal_Int32 mnFieldLen;
+ sal_Int32 mnBulletOffset;
+ sal_Int32 mnBulletLen;
+ bool mbInField;
+ bool mbInBullet;
+};
+
+}
+
+static ESelection MakeEESelection( const SvxAccessibleTextIndex& rStart, const SvxAccessibleTextIndex& rEnd )
+{
+ // deal with field special case: to really get a field contained
+ // within a selection, the start index must be before or on the
+ // field, the end index after it.
+
+ // The SvxAccessibleTextIndex.GetEEIndex method gives the index on
+ // the field, as long the input index is on the field. Thus,
+ // correction necessary for the end index
+
+ // Therefore, for _ranges_, if part of the field is touched, all
+ // of the field must be selected
+ if( rStart.GetParagraph() <= rEnd.GetParagraph() ||
+ (rStart.GetParagraph() == rEnd.GetParagraph() &&
+ rStart.GetEEIndex() <= rEnd.GetEEIndex()) )
+ {
+ if( rEnd.InField() && rEnd.GetFieldOffset() )
+ return ESelection( rStart.GetParagraph(), rStart.GetEEIndex(),
+ rEnd.GetParagraph(), rEnd.GetEEIndex()+1 );
+ }
+ else if( rStart.GetParagraph() > rEnd.GetParagraph() ||
+ (rStart.GetParagraph() == rEnd.GetParagraph() &&
+ rStart.GetEEIndex() > rEnd.GetEEIndex()) )
+ {
+ if( rStart.InField() && rStart.GetFieldOffset() )
+ return ESelection( rStart.GetParagraph(), rStart.GetEEIndex()+1,
+ rEnd.GetParagraph(), rEnd.GetEEIndex() );
+ }
+
+ return ESelection( rStart.GetParagraph(), rStart.GetEEIndex(),
+ rEnd.GetParagraph(), rEnd.GetEEIndex() );
+}
+
+static ESelection MakeEESelection( const SvxAccessibleTextIndex& rIndex )
+{
+ return ESelection( rIndex.GetParagraph(), rIndex.GetEEIndex(),
+ rIndex.GetParagraph(), rIndex.GetEEIndex() + 1 );
+}
+
+sal_Int32 SvxAccessibleTextIndex::GetEEIndex() const
+{
+ DBG_ASSERT(mnEEIndex >= 0,
+ "SvxAccessibleTextIndex::GetEEIndex: index value overflow");
+
+ return mnEEIndex;
+}
+
+void SvxAccessibleTextIndex::SetEEIndex( sal_Int32 nEEIndex, const SvxTextForwarder& rTF )
+{
+ // reset
+ mnFieldOffset = 0;
+ mbInField = false;
+ mnFieldLen = 0;
+ mnBulletOffset = 0;
+ mbInBullet = false;
+ mnBulletLen = 0;
+
+ // set known values
+ mnEEIndex = nEEIndex;
+
+ // calculate unknowns
+ sal_Int32 nCurrField, nFieldCount = rTF.GetFieldCount( GetParagraph() );
+
+ mnIndex = nEEIndex;
+
+ EBulletInfo aBulletInfo = rTF.GetBulletInfo( GetParagraph() );
+
+ // any text bullets?
+ if( aBulletInfo.nParagraph != EE_PARA_NOT_FOUND &&
+ aBulletInfo.bVisible &&
+ aBulletInfo.nType != SVX_NUM_BITMAP )
+ {
+ mnIndex += aBulletInfo.aText.getLength();
+ }
+
+ for( nCurrField=0; nCurrField < nFieldCount; ++nCurrField )
+ {
+ EFieldInfo aFieldInfo( rTF.GetFieldInfo( GetParagraph(), nCurrField ) );
+
+ if( aFieldInfo.aPosition.nIndex > nEEIndex )
+ break;
+
+ if( aFieldInfo.aPosition.nIndex == nEEIndex )
+ {
+ AreInField();
+ break;
+ }
+
+ mnIndex += std::max(aFieldInfo.aCurrentText.getLength()-1, sal_Int32(0));
+ }
+}
+
+void SvxAccessibleTextIndex::SetIndex( sal_Int32 nIndex, const SvxTextForwarder& rTF )
+{
+ // reset
+ mnFieldOffset = 0;
+ mbInField = false;
+ mnFieldLen = 0;
+ mnBulletOffset = 0;
+ mbInBullet = false;
+ mnBulletLen = 0;
+
+ // set known values
+ mnIndex = nIndex;
+
+ // calculate unknowns
+ sal_Int32 nCurrField, nFieldCount = rTF.GetFieldCount( GetParagraph() );
+
+ DBG_ASSERT(nIndex >= 0,
+ "SvxAccessibleTextIndex::SetIndex: index value overflow");
+
+ mnEEIndex = nIndex;
+
+ EBulletInfo aBulletInfo = rTF.GetBulletInfo( GetParagraph() );
+
+ // any text bullets?
+ if( aBulletInfo.nParagraph != EE_PARA_NOT_FOUND &&
+ aBulletInfo.bVisible &&
+ aBulletInfo.nType != SVX_NUM_BITMAP )
+ {
+ sal_Int32 nBulletLen = aBulletInfo.aText.getLength();
+
+ if( nIndex < nBulletLen )
+ {
+ mbInBullet = true;
+ SetBulletOffset( nIndex, nBulletLen );
+ mnEEIndex = 0;
+ return;
+ }
+
+ mnEEIndex = mnEEIndex - nBulletLen;
+ }
+
+ for( nCurrField=0; nCurrField < nFieldCount; ++nCurrField )
+ {
+ EFieldInfo aFieldInfo( rTF.GetFieldInfo( GetParagraph(), nCurrField ) );
+
+ // we're before a field
+ if( aFieldInfo.aPosition.nIndex > mnEEIndex )
+ break;
+
+ mnEEIndex -= std::max(aFieldInfo.aCurrentText.getLength()-1, sal_Int32(0));
+
+ // we're within a field
+ if( aFieldInfo.aPosition.nIndex >= mnEEIndex )
+ {
+ AreInField();
+ SetFieldOffset( std::max(aFieldInfo.aCurrentText.getLength()-1, sal_Int32(0)) - (aFieldInfo.aPosition.nIndex - mnEEIndex),
+ aFieldInfo.aCurrentText.getLength() );
+ mnEEIndex = aFieldInfo.aPosition.nIndex ;
+ break;
+ }
+ }
+}
+
+bool SvxAccessibleTextIndex::IsEditableRange( const SvxAccessibleTextIndex& rEnd ) const
+{
+ if( GetIndex() > rEnd.GetIndex() )
+ return rEnd.IsEditableRange( *this );
+
+ if( InBullet() || rEnd.InBullet() )
+ return false;
+
+ if( InField() && GetFieldOffset() )
+ return false; // within field
+
+ if( rEnd.InField() && rEnd.GetFieldOffset() >= rEnd.GetFieldLen() - 1 )
+ return false; // within field
+
+ return true;
+}
+
+
+SvxEditSourceAdapter::SvxEditSourceAdapter() : mbEditSourceValid( false )
+{
+}
+
+SvxEditSourceAdapter::~SvxEditSourceAdapter()
+{
+}
+
+std::unique_ptr<SvxEditSource> SvxEditSourceAdapter::Clone() const
+{
+ if( mbEditSourceValid && mpAdaptee )
+ {
+ std::unique_ptr< SvxEditSource > pClonedAdaptee( mpAdaptee->Clone() );
+
+ if (pClonedAdaptee)
+ {
+ std::unique_ptr<SvxEditSourceAdapter> pClone(new SvxEditSourceAdapter());
+ pClone->SetEditSource( std::move(pClonedAdaptee) );
+ return std::unique_ptr< SvxEditSource >(pClone.release());
+ }
+ }
+
+ return nullptr;
+}
+
+SvxAccessibleTextAdapter* SvxEditSourceAdapter::GetTextForwarderAdapter()
+{
+ if( mbEditSourceValid && mpAdaptee )
+ {
+ SvxTextForwarder* pTextForwarder = mpAdaptee->GetTextForwarder();
+
+ if( pTextForwarder )
+ {
+ maTextAdapter.SetForwarder(*pTextForwarder);
+
+ return &maTextAdapter;
+ }
+ }
+
+ return nullptr;
+}
+
+SvxTextForwarder* SvxEditSourceAdapter::GetTextForwarder()
+{
+ return GetTextForwarderAdapter();
+}
+
+SvxViewForwarder* SvxEditSourceAdapter::GetViewForwarder()
+{
+ if( mbEditSourceValid && mpAdaptee )
+ return mpAdaptee->GetViewForwarder();
+
+ return nullptr;
+}
+
+SvxAccessibleTextEditViewAdapter* SvxEditSourceAdapter::GetEditViewForwarderAdapter( bool bCreate )
+{
+ if( mbEditSourceValid && mpAdaptee )
+ {
+ SvxEditViewForwarder* pEditViewForwarder = mpAdaptee->GetEditViewForwarder(bCreate);
+
+ if( pEditViewForwarder )
+ {
+ SvxAccessibleTextAdapter* pTextAdapter = GetTextForwarderAdapter();
+
+ if( pTextAdapter )
+ {
+ maEditViewAdapter.SetForwarder(*pEditViewForwarder, *pTextAdapter);
+
+ return &maEditViewAdapter;
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+SvxEditViewForwarder* SvxEditSourceAdapter::GetEditViewForwarder( bool bCreate )
+{
+ return GetEditViewForwarderAdapter( bCreate );
+}
+
+void SvxEditSourceAdapter::UpdateData()
+{
+ if( mbEditSourceValid && mpAdaptee )
+ mpAdaptee->UpdateData();
+}
+
+SfxBroadcaster& SvxEditSourceAdapter::GetBroadcaster() const
+{
+ if( mbEditSourceValid && mpAdaptee )
+ return mpAdaptee->GetBroadcaster();
+
+ return maDummyBroadcaster;
+}
+
+void SvxEditSourceAdapter::SetEditSource( std::unique_ptr< SvxEditSource > && pAdaptee )
+{
+ if (pAdaptee)
+ {
+ mpAdaptee = std::move(pAdaptee);
+ mbEditSourceValid = true;
+ }
+ else
+ {
+ // do a lazy delete (prevents us from deleting the broadcaster
+ // from within a broadcast in
+ // AccessibleTextHelper_Impl::Notify)
+ mbEditSourceValid = false;
+ }
+}
+
+SvxAccessibleTextAdapter::SvxAccessibleTextAdapter()
+ : mpTextForwarder(nullptr)
+{
+}
+
+SvxAccessibleTextAdapter::~SvxAccessibleTextAdapter()
+{
+}
+
+sal_Int32 SvxAccessibleTextAdapter::GetParagraphCount() const
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ return mpTextForwarder->GetParagraphCount();
+}
+
+sal_Int32 SvxAccessibleTextAdapter::GetTextLen( sal_Int32 nParagraph ) const
+{
+ SvxAccessibleTextIndex aIndex;
+ aIndex.SetEEIndex( nParagraph, mpTextForwarder->GetTextLen( nParagraph ), *this );
+
+ return aIndex.GetIndex();
+}
+
+OUString SvxAccessibleTextAdapter::GetText( const ESelection& rSel ) const
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ SvxAccessibleTextIndex aStartIndex;
+ SvxAccessibleTextIndex aEndIndex;
+
+ aStartIndex.SetIndex( rSel.nStartPara, rSel.nStartPos, *this );
+ aEndIndex.SetIndex( rSel.nEndPara, rSel.nEndPos, *this );
+
+ // normalize selection
+ if( rSel.nStartPara > rSel.nEndPara ||
+ (rSel.nStartPara == rSel.nEndPara && rSel.nStartPos > rSel.nEndPos) )
+ {
+ std::swap( aStartIndex, aEndIndex );
+ }
+
+ OUString sStr = mpTextForwarder->GetText( MakeEESelection(aStartIndex, aEndIndex) );
+
+ // trim field text, if necessary
+ if( aStartIndex.InField() )
+ {
+ DBG_ASSERT(aStartIndex.GetFieldOffset() >= 0,
+ "SvxAccessibleTextIndex::GetText: index value overflow");
+
+ sStr = sStr.copy( aStartIndex.GetFieldOffset() );
+ }
+ if( aEndIndex.InField() && aEndIndex.GetFieldOffset() )
+ {
+ DBG_ASSERT(sStr.getLength() - (aEndIndex.GetFieldLen() - aEndIndex.GetFieldOffset()) >= 0,
+ "SvxAccessibleTextIndex::GetText: index value overflow");
+
+ sStr = sStr.copy(0, sStr.getLength() - (aEndIndex.GetFieldLen() - aEndIndex.GetFieldOffset()) );
+ }
+
+ EBulletInfo aBulletInfo2 = GetBulletInfo( aEndIndex.GetParagraph() );
+
+ if( aEndIndex.InBullet() )
+ {
+ // append trailing bullet
+ sStr += aBulletInfo2.aText;
+
+ DBG_ASSERT(sStr.getLength() - (aEndIndex.GetBulletLen() - aEndIndex.GetBulletOffset()) >= 0,
+ "SvxAccessibleTextIndex::GetText: index value overflow");
+
+ sStr = sStr.copy(0, sStr.getLength() - (aEndIndex.GetBulletLen() - aEndIndex.GetBulletOffset()) );
+ }
+ else if( aStartIndex.GetParagraph() != aEndIndex.GetParagraph() &&
+ HaveTextBullet( aEndIndex.GetParagraph() ) )
+ {
+ OUString sBullet = aBulletInfo2.aText;
+
+ DBG_ASSERT(sBullet.getLength() - (aEndIndex.GetBulletLen() - aEndIndex.GetBulletOffset()) >= 0,
+ "SvxAccessibleTextIndex::GetText: index value overflow");
+
+ sBullet = sBullet.copy(0, sBullet.getLength() - (aEndIndex.GetBulletLen() - aEndIndex.GetBulletOffset()) );
+
+ // insert bullet
+ sStr = sStr.replaceAt( GetTextLen(aStartIndex.GetParagraph()) - aStartIndex.GetIndex(), 0, sBullet );
+ }
+
+ return sStr;
+}
+
+SfxItemSet SvxAccessibleTextAdapter::GetAttribs( const ESelection& rSel, EditEngineAttribs nOnlyHardAttrib ) const
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ SvxAccessibleTextIndex aStartIndex;
+ SvxAccessibleTextIndex aEndIndex;
+
+ aStartIndex.SetIndex( rSel.nStartPara, rSel.nStartPos, *this );
+ aEndIndex.SetIndex( rSel.nEndPara, rSel.nEndPos, *this );
+
+ return mpTextForwarder->GetAttribs( MakeEESelection(aStartIndex, aEndIndex), nOnlyHardAttrib );
+}
+
+SfxItemSet SvxAccessibleTextAdapter::GetParaAttribs( sal_Int32 nPara ) const
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ return mpTextForwarder->GetParaAttribs( nPara );
+}
+
+void SvxAccessibleTextAdapter::SetParaAttribs( sal_Int32 nPara, const SfxItemSet& rSet )
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ mpTextForwarder->SetParaAttribs( nPara, rSet );
+}
+
+void SvxAccessibleTextAdapter::RemoveAttribs( const ESelection& )
+{
+}
+
+void SvxAccessibleTextAdapter::GetPortions( sal_Int32 nPara, std::vector<sal_Int32>& rList ) const
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ mpTextForwarder->GetPortions( nPara, rList );
+}
+
+OUString SvxAccessibleTextAdapter::GetStyleSheet(sal_Int32 nPara) const
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ return mpTextForwarder->GetStyleSheet(nPara);
+}
+
+void SvxAccessibleTextAdapter::SetStyleSheet(sal_Int32 nPara, const OUString& rStyleName)
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ mpTextForwarder->SetStyleSheet(nPara, rStyleName);
+}
+
+SfxItemState SvxAccessibleTextAdapter::GetItemState( const ESelection& rSel, sal_uInt16 nWhich ) const
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ SvxAccessibleTextIndex aStartIndex;
+ SvxAccessibleTextIndex aEndIndex;
+
+ aStartIndex.SetIndex( rSel.nStartPara, rSel.nStartPos, *this );
+ aEndIndex.SetIndex( rSel.nEndPara, rSel.nEndPos, *this );
+
+ return mpTextForwarder->GetItemState( MakeEESelection(aStartIndex, aEndIndex),
+ nWhich );
+}
+
+SfxItemState SvxAccessibleTextAdapter::GetItemState( sal_Int32 nPara, sal_uInt16 nWhich ) const
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ return mpTextForwarder->GetItemState( nPara, nWhich );
+}
+
+void SvxAccessibleTextAdapter::QuickInsertText( const OUString& rText, const ESelection& rSel )
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ SvxAccessibleTextIndex aStartIndex;
+ SvxAccessibleTextIndex aEndIndex;
+
+ aStartIndex.SetIndex( rSel.nStartPara, rSel.nStartPos, *this );
+ aEndIndex.SetIndex( rSel.nEndPara, rSel.nEndPos, *this );
+
+ mpTextForwarder->QuickInsertText( rText,
+ MakeEESelection(aStartIndex, aEndIndex) );
+}
+
+void SvxAccessibleTextAdapter::QuickInsertField( const SvxFieldItem& rFld, const ESelection& rSel )
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ SvxAccessibleTextIndex aStartIndex;
+ SvxAccessibleTextIndex aEndIndex;
+
+ aStartIndex.SetIndex( rSel.nStartPara, rSel.nStartPos, *this );
+ aEndIndex.SetIndex( rSel.nEndPara, rSel.nEndPos, *this );
+
+ mpTextForwarder->QuickInsertField( rFld,
+ MakeEESelection(aStartIndex, aEndIndex) );
+}
+
+void SvxAccessibleTextAdapter::QuickSetAttribs( const SfxItemSet& rSet, const ESelection& rSel )
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ SvxAccessibleTextIndex aStartIndex;
+ SvxAccessibleTextIndex aEndIndex;
+
+ aStartIndex.SetIndex( rSel.nStartPara, rSel.nStartPos, *this );
+ aEndIndex.SetIndex( rSel.nEndPara, rSel.nEndPos, *this );
+
+ mpTextForwarder->QuickSetAttribs( rSet,
+ MakeEESelection(aStartIndex, aEndIndex) );
+}
+
+void SvxAccessibleTextAdapter::QuickInsertLineBreak( const ESelection& rSel )
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ SvxAccessibleTextIndex aStartIndex;
+ SvxAccessibleTextIndex aEndIndex;
+
+ aStartIndex.SetIndex( rSel.nStartPara, rSel.nStartPos, *this );
+ aEndIndex.SetIndex( rSel.nEndPara, rSel.nEndPos, *this );
+
+ mpTextForwarder->QuickInsertLineBreak( MakeEESelection(aStartIndex, aEndIndex) );
+}
+
+SfxItemPool* SvxAccessibleTextAdapter::GetPool() const
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ return mpTextForwarder->GetPool();
+}
+
+OUString SvxAccessibleTextAdapter::CalcFieldValue( const SvxFieldItem& rField, sal_Int32 nPara, sal_Int32 nPos, std::optional<Color>& rpTxtColor, std::optional<Color>& rpFldColor, std::optional<FontLineStyle>& rpFldLineStyle )
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ return mpTextForwarder->CalcFieldValue( rField, nPara, nPos, rpTxtColor, rpFldColor, rpFldLineStyle );
+}
+
+void SvxAccessibleTextAdapter::FieldClicked( const SvxFieldItem& rField )
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ mpTextForwarder->FieldClicked( rField );
+}
+
+sal_Int32 SvxAccessibleTextAdapter::CalcEditEngineIndex( sal_Int32 nPara, sal_Int32 nLogicalIndex )
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ SvxAccessibleTextIndex aIndex;
+ aIndex.SetIndex(nPara, nLogicalIndex, *mpTextForwarder);
+ return aIndex.GetEEIndex();
+}
+
+bool SvxAccessibleTextAdapter::IsValid() const
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ if( mpTextForwarder )
+ return mpTextForwarder->IsValid();
+ else
+ return false;
+}
+
+LanguageType SvxAccessibleTextAdapter::GetLanguage( sal_Int32 nPara, sal_Int32 nPos ) const
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ SvxAccessibleTextIndex aIndex;
+
+ aIndex.SetIndex( nPara, nPos, *this );
+
+ return mpTextForwarder->GetLanguage( nPara, aIndex.GetEEIndex() );
+}
+
+sal_Int32 SvxAccessibleTextAdapter::GetFieldCount( sal_Int32 nPara ) const
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ return mpTextForwarder->GetFieldCount( nPara );
+}
+
+EFieldInfo SvxAccessibleTextAdapter::GetFieldInfo( sal_Int32 nPara, sal_uInt16 nField ) const
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ return mpTextForwarder->GetFieldInfo( nPara, nField );
+}
+
+EBulletInfo SvxAccessibleTextAdapter::GetBulletInfo( sal_Int32 nPara ) const
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ return mpTextForwarder->GetBulletInfo( nPara );
+}
+
+tools::Rectangle SvxAccessibleTextAdapter::GetCharBounds( sal_Int32 nPara, sal_Int32 nIndex ) const
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ SvxAccessibleTextIndex aIndex;
+ aIndex.SetIndex( nPara, nIndex, *this );
+
+ // preset if anything goes wrong below
+ // n-th char in GetParagraphIndex's paragraph
+ tools::Rectangle aRect = mpTextForwarder->GetCharBounds( nPara, aIndex.GetEEIndex() );
+
+ if( aIndex.InBullet() )
+ {
+ EBulletInfo aBulletInfo = GetBulletInfo( nPara );
+
+ OutputDevice* pOutDev = GetRefDevice();
+
+ DBG_ASSERT(pOutDev!=nullptr, "SvxAccessibleTextAdapter::GetCharBounds: No ref device");
+
+ // preset if anything goes wrong below
+ aRect = aBulletInfo.aBounds; // better than nothing
+ if( pOutDev )
+ {
+ AccessibleStringWrap aStringWrap( *pOutDev, aBulletInfo.aFont, aBulletInfo.aText );
+
+ aStringWrap.GetCharacterBounds( aIndex.GetBulletOffset(), aRect );
+ aRect.Move( aBulletInfo.aBounds.Left(), aBulletInfo.aBounds.Top() );
+ }
+ }
+ else
+ {
+ // handle field content manually
+ if( aIndex.InField() )
+ {
+ OutputDevice* pOutDev = GetRefDevice();
+
+ DBG_ASSERT(pOutDev!=nullptr, "SvxAccessibleTextAdapter::GetCharBounds: No ref device");
+
+ if( pOutDev )
+ {
+ ESelection aSel = MakeEESelection( aIndex );
+
+ SvxFont aFont = EditEngine::CreateSvxFontFromItemSet( mpTextForwarder->GetAttribs( aSel ) );
+ AccessibleStringWrap aStringWrap( *pOutDev,
+ aFont,
+ mpTextForwarder->GetText( aSel ) );
+
+ tools::Rectangle aStartRect = mpTextForwarder->GetCharBounds( nPara, aIndex.GetEEIndex() );
+
+ aStringWrap.GetCharacterBounds( aIndex.GetFieldOffset(), aRect );
+ aRect.Move( aStartRect.Left(), aStartRect.Top() );
+ }
+ }
+ }
+
+ return aRect;
+}
+
+tools::Rectangle SvxAccessibleTextAdapter::GetParaBounds( sal_Int32 nPara ) const
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ EBulletInfo aBulletInfo = GetBulletInfo( nPara );
+
+ if( aBulletInfo.nParagraph != EE_PARA_NOT_FOUND &&
+ aBulletInfo.bVisible &&
+ aBulletInfo.nType != SVX_NUM_BITMAP )
+ {
+ // include bullet in para bounding box
+ tools::Rectangle aRect( mpTextForwarder->GetParaBounds( nPara ) );
+
+ aRect.Union( aBulletInfo.aBounds );
+
+ return aRect;
+ }
+
+ return mpTextForwarder->GetParaBounds( nPara );
+}
+
+MapMode SvxAccessibleTextAdapter::GetMapMode() const
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ return mpTextForwarder->GetMapMode();
+}
+
+OutputDevice* SvxAccessibleTextAdapter::GetRefDevice() const
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ return mpTextForwarder->GetRefDevice();
+}
+
+bool SvxAccessibleTextAdapter::GetIndexAtPoint( const Point& rPoint, sal_Int32& nPara, sal_Int32& nIndex ) const
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ if( !mpTextForwarder->GetIndexAtPoint( rPoint, nPara, nIndex ) )
+ return false;
+
+ SvxAccessibleTextIndex aIndex;
+ aIndex.SetEEIndex(nPara, nIndex, *this);
+
+ DBG_ASSERT(aIndex.GetIndex() >= 0,
+ "SvxAccessibleTextIndex::SetIndex: index value overflow");
+
+ nIndex = aIndex.GetIndex();
+
+ EBulletInfo aBulletInfo = GetBulletInfo( nPara );
+
+ // any text bullets?
+ if( aBulletInfo.nParagraph != EE_PARA_NOT_FOUND &&
+ aBulletInfo.bVisible &&
+ aBulletInfo.nType != SVX_NUM_BITMAP )
+ {
+ if( aBulletInfo.aBounds.Contains( rPoint) )
+ {
+ OutputDevice* pOutDev = GetRefDevice();
+
+ DBG_ASSERT(pOutDev!=nullptr, "SvxAccessibleTextAdapter::GetIndexAtPoint: No ref device");
+
+ if( !pOutDev )
+ return false;
+
+ AccessibleStringWrap aStringWrap( *pOutDev, aBulletInfo.aFont, aBulletInfo.aText );
+
+ Point aPoint = rPoint;
+ aPoint.Move( -aBulletInfo.aBounds.Left(), -aBulletInfo.aBounds.Top() );
+
+ DBG_ASSERT(aStringWrap.GetIndexAtPoint( aPoint ) >= 0,
+ "SvxAccessibleTextIndex::SetIndex: index value overflow");
+
+ nIndex = aStringWrap.GetIndexAtPoint( aPoint );
+ return true;
+ }
+ }
+
+ if( !aIndex.InField() )
+ return true;
+
+ OutputDevice* pOutDev = GetRefDevice();
+
+ DBG_ASSERT(pOutDev!=nullptr, "SvxAccessibleTextAdapter::GetIndexAtPoint: No ref device");
+
+ if( !pOutDev )
+ return false;
+
+ ESelection aSelection = MakeEESelection( aIndex );
+ SvxFont aFont = EditEngine::CreateSvxFontFromItemSet( mpTextForwarder->GetAttribs( aSelection ) );
+ AccessibleStringWrap aStringWrap( *pOutDev,
+ aFont,
+ mpTextForwarder->GetText( aSelection ) );
+
+ tools::Rectangle aRect = mpTextForwarder->GetCharBounds( nPara, aIndex.GetEEIndex() );
+ Point aPoint = rPoint;
+ aPoint.Move( -aRect.Left(), -aRect.Top() );
+
+ DBG_ASSERT(aIndex.GetIndex() + aStringWrap.GetIndexAtPoint( rPoint ) >= 0,
+ "SvxAccessibleTextIndex::SetIndex: index value overflow");
+
+ nIndex = (aIndex.GetIndex() + aStringWrap.GetIndexAtPoint( aPoint ));
+ return true;
+}
+
+bool SvxAccessibleTextAdapter::GetWordIndices( sal_Int32 nPara, sal_Int32 nIndex, sal_Int32& nStart, sal_Int32& nEnd ) const
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ SvxAccessibleTextIndex aIndex;
+ aIndex.SetIndex(nPara, nIndex, *this);
+ nIndex = aIndex.GetEEIndex();
+
+ if( aIndex.InBullet() )
+ {
+ DBG_ASSERT(aIndex.GetBulletLen() >= 0,
+ "SvxAccessibleTextIndex::SetIndex: index value overflow");
+
+ // always treat bullet as separate word
+ nStart = 0;
+ nEnd = aIndex.GetBulletLen();
+
+ return true;
+ }
+
+ if( aIndex.InField() )
+ {
+ DBG_ASSERT(aIndex.GetIndex() - aIndex.GetFieldOffset() >= 0 &&
+ nStart + aIndex.GetFieldLen() >= 0,
+ "SvxAccessibleTextIndex::SetIndex: index value overflow");
+
+ // always treat field as separate word
+ // TODO: to circumvent this, _we_ would have to do the break iterator stuff!
+ nStart = aIndex.GetIndex() - aIndex.GetFieldOffset();
+ nEnd = nStart + aIndex.GetFieldLen();
+
+ return true;
+ }
+
+ if( !mpTextForwarder->GetWordIndices( nPara, nIndex, nStart, nEnd ) )
+ return false;
+
+ aIndex.SetEEIndex( nPara, nStart, *this );
+ DBG_ASSERT(aIndex.GetIndex() >= 0,
+ "SvxAccessibleTextIndex::SetIndex: index value overflow");
+ nStart = aIndex.GetIndex();
+
+ aIndex.SetEEIndex( nPara, nEnd, *this );
+ DBG_ASSERT(aIndex.GetIndex() >= 0,
+ "SvxAccessibleTextIndex::SetIndex: index value overflow");
+ nEnd = aIndex.GetIndex();
+
+ return true;
+}
+
+bool SvxAccessibleTextAdapter::GetAttributeRun( sal_Int32& nStartIndex, sal_Int32& nEndIndex, sal_Int32 nPara, sal_Int32 nIndex, bool /* bInCell */ ) const
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ SvxAccessibleTextIndex aIndex;
+ aIndex.SetIndex(nPara, nIndex, *this);
+ nIndex = aIndex.GetEEIndex();
+
+ if( aIndex.InBullet() )
+ {
+ DBG_ASSERT(aIndex.GetBulletLen() >= 0,
+ "SvxAccessibleTextIndex::SetIndex: index value overflow");
+
+ // always treat bullet as distinct attribute
+ nStartIndex = 0;
+ nEndIndex = aIndex.GetBulletLen();
+
+ return true;
+ }
+
+ if( aIndex.InField() )
+ {
+ DBG_ASSERT(aIndex.GetIndex() - aIndex.GetFieldOffset() >= 0,
+ "SvxAccessibleTextIndex::SetIndex: index value overflow");
+
+ // always treat field as distinct attribute
+ nStartIndex = aIndex.GetIndex() - aIndex.GetFieldOffset();
+ nEndIndex = nStartIndex + aIndex.GetFieldLen();
+
+ return true;
+ }
+
+ if( !mpTextForwarder->GetAttributeRun( nStartIndex, nEndIndex, nPara, nIndex ) )
+ return false;
+
+ aIndex.SetEEIndex( nPara, nStartIndex, *this );
+ DBG_ASSERT(aIndex.GetIndex() >= 0,
+ "SvxAccessibleTextIndex::SetIndex: index value overflow");
+ nStartIndex = aIndex.GetIndex();
+
+ aIndex.SetEEIndex( nPara, nEndIndex, *this );
+ DBG_ASSERT(aIndex.GetIndex() >= 0,
+ "SvxAccessibleTextIndex::SetIndex: index value overflow");
+ nEndIndex = aIndex.GetIndex();
+
+ return true;
+}
+
+sal_Int32 SvxAccessibleTextAdapter::GetLineCount( sal_Int32 nPara ) const
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ return mpTextForwarder->GetLineCount( nPara );
+}
+
+sal_Int32 SvxAccessibleTextAdapter::GetLineLen( sal_Int32 nPara, sal_Int32 nLine ) const
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ SvxAccessibleTextIndex aEndIndex;
+ sal_Int32 nCurrLine;
+ sal_Int32 nCurrIndex, nLastIndex;
+ for( nCurrLine=0, nCurrIndex=0, nLastIndex=0; nCurrLine<=nLine; ++nCurrLine )
+ {
+ nLastIndex = nCurrIndex;
+ nCurrIndex =
+ nCurrIndex + mpTextForwarder->GetLineLen( nPara, nCurrLine );
+ }
+
+ aEndIndex.SetEEIndex( nPara, nCurrIndex, *this );
+ if( nLine > 0 )
+ {
+ SvxAccessibleTextIndex aStartIndex;
+ aStartIndex.SetEEIndex( nPara, nLastIndex, *this );
+
+ return aEndIndex.GetIndex() - aStartIndex.GetIndex();
+ }
+ else
+ return aEndIndex.GetIndex();
+}
+
+void SvxAccessibleTextAdapter::GetLineBoundaries( /*out*/sal_Int32 &rStart, /*out*/sal_Int32 &rEnd, sal_Int32 nParagraph, sal_Int32 nLine ) const
+{
+ mpTextForwarder->GetLineBoundaries( rStart, rEnd, nParagraph, nLine );
+}
+
+sal_Int32 SvxAccessibleTextAdapter::GetLineNumberAtIndex( sal_Int32 nPara, sal_Int32 nIndex ) const
+{
+ return mpTextForwarder->GetLineNumberAtIndex( nPara, nIndex );
+}
+
+bool SvxAccessibleTextAdapter::Delete( const ESelection& rSel )
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ SvxAccessibleTextIndex aStartIndex;
+ SvxAccessibleTextIndex aEndIndex;
+
+ aStartIndex.SetIndex( rSel.nStartPara, rSel.nStartPos, *this );
+ aEndIndex.SetIndex( rSel.nEndPara, rSel.nEndPos, *this );
+
+ return mpTextForwarder->Delete( MakeEESelection(aStartIndex, aEndIndex ) );
+}
+
+bool SvxAccessibleTextAdapter::InsertText( const OUString& rStr, const ESelection& rSel )
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ SvxAccessibleTextIndex aStartIndex;
+ SvxAccessibleTextIndex aEndIndex;
+
+ aStartIndex.SetIndex( rSel.nStartPara, rSel.nStartPos, *this );
+ aEndIndex.SetIndex( rSel.nEndPara, rSel.nEndPos, *this );
+
+ return mpTextForwarder->InsertText( rStr, MakeEESelection(aStartIndex, aEndIndex) );
+}
+
+bool SvxAccessibleTextAdapter::QuickFormatDoc( bool bFull )
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ return mpTextForwarder->QuickFormatDoc( bFull );
+}
+
+sal_Int16 SvxAccessibleTextAdapter::GetDepth( sal_Int32 nPara ) const
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ return mpTextForwarder->GetDepth( nPara );
+}
+
+bool SvxAccessibleTextAdapter::SetDepth( sal_Int32 nPara, sal_Int16 nNewDepth )
+{
+ assert(mpTextForwarder && "SvxAccessibleTextAdapter: no forwarder");
+
+ return mpTextForwarder->SetDepth( nPara, nNewDepth );
+}
+
+void SvxAccessibleTextAdapter::SetForwarder( SvxTextForwarder& rForwarder )
+{
+ mpTextForwarder = &rForwarder;
+}
+
+bool SvxAccessibleTextAdapter::HaveImageBullet( sal_Int32 nPara ) const
+{
+ EBulletInfo aBulletInfo = GetBulletInfo( nPara );
+
+ return ( aBulletInfo.nParagraph != EE_PARA_NOT_FOUND &&
+ aBulletInfo.bVisible &&
+ aBulletInfo.nType == SVX_NUM_BITMAP );
+}
+
+bool SvxAccessibleTextAdapter::HaveTextBullet( sal_Int32 nPara ) const
+{
+ EBulletInfo aBulletInfo = GetBulletInfo( nPara );
+
+ return ( aBulletInfo.nParagraph != EE_PARA_NOT_FOUND &&
+ aBulletInfo.bVisible &&
+ aBulletInfo.nType != SVX_NUM_BITMAP );
+}
+
+bool SvxAccessibleTextAdapter::IsEditable( const ESelection& rSel ) const
+{
+ SvxAccessibleTextIndex aStartIndex;
+ SvxAccessibleTextIndex aEndIndex;
+
+ aStartIndex.SetIndex( rSel.nStartPara, rSel.nStartPos, *this );
+ aEndIndex.SetIndex( rSel.nEndPara, rSel.nEndPos, *this );
+
+ // normalize selection
+ if( rSel.nStartPara > rSel.nEndPara ||
+ (rSel.nStartPara == rSel.nEndPara && rSel.nStartPos > rSel.nEndPos) )
+ {
+ std::swap( aStartIndex, aEndIndex );
+ }
+
+ return aStartIndex.IsEditableRange( aEndIndex );
+}
+
+const SfxItemSet * SvxAccessibleTextAdapter::GetEmptyItemSetPtr()
+{
+ OSL_FAIL( "not implemented" );
+ return nullptr;
+}
+
+void SvxAccessibleTextAdapter::AppendParagraph()
+{
+ OSL_FAIL( "not implemented" );
+}
+
+sal_Int32 SvxAccessibleTextAdapter::AppendTextPortion( sal_Int32, const OUString &, const SfxItemSet & )
+{
+ OSL_FAIL( "not implemented" );
+ return 0;
+}
+void SvxAccessibleTextAdapter::CopyText(const SvxTextForwarder&)
+{
+ OSL_FAIL( "not implemented" );
+}
+
+SvxAccessibleTextEditViewAdapter::SvxAccessibleTextEditViewAdapter()
+ : mpViewForwarder(nullptr)
+ , mpTextForwarder(nullptr)
+{
+}
+
+SvxAccessibleTextEditViewAdapter::~SvxAccessibleTextEditViewAdapter()
+{
+}
+
+bool SvxAccessibleTextEditViewAdapter::IsValid() const
+{
+ DBG_ASSERT(mpViewForwarder, "SvxAccessibleTextEditViewAdapter: no forwarder");
+
+ if( mpViewForwarder )
+ return mpViewForwarder->IsValid();
+ else
+ return false;
+}
+
+Point SvxAccessibleTextEditViewAdapter::LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ DBG_ASSERT(mpViewForwarder, "SvxAccessibleTextEditViewAdapter: no forwarder");
+
+ return mpViewForwarder->LogicToPixel(rPoint, rMapMode);
+}
+
+Point SvxAccessibleTextEditViewAdapter::PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ DBG_ASSERT(mpViewForwarder, "SvxAccessibleTextEditViewAdapter: no forwarder");
+
+ return mpViewForwarder->PixelToLogic(rPoint, rMapMode);
+}
+
+bool SvxAccessibleTextEditViewAdapter::GetSelection( ESelection& rSel ) const
+{
+ DBG_ASSERT(mpViewForwarder, "SvxAccessibleTextEditViewAdapter: no forwarder");
+
+ ESelection aSelection;
+
+ if( !mpViewForwarder->GetSelection( aSelection ) )
+ return false;
+
+ SvxAccessibleTextIndex aStartIndex;
+ SvxAccessibleTextIndex aEndIndex;
+
+ aStartIndex.SetEEIndex( aSelection.nStartPara, aSelection.nStartPos, *mpTextForwarder );
+ aEndIndex.SetEEIndex( aSelection.nEndPara, aSelection.nEndPos, *mpTextForwarder );
+
+ DBG_ASSERT(aStartIndex.GetIndex() >= 0 &&
+ aEndIndex.GetIndex() >= 0,
+ "SvxAccessibleTextEditViewAdapter::GetSelection: index value overflow");
+
+ rSel = ESelection( aStartIndex.GetParagraph(), aStartIndex.GetIndex(),
+ aEndIndex.GetParagraph(), aEndIndex.GetIndex() );
+
+ return true;
+}
+
+bool SvxAccessibleTextEditViewAdapter::SetSelection( const ESelection& rSel )
+{
+ DBG_ASSERT(mpViewForwarder, "SvxAccessibleTextEditViewAdapter: no forwarder");
+
+ SvxAccessibleTextIndex aStartIndex;
+ SvxAccessibleTextIndex aEndIndex;
+
+ aStartIndex.SetIndex( rSel.nStartPara, rSel.nStartPos, *mpTextForwarder );
+ aEndIndex.SetIndex( rSel.nEndPara, rSel.nEndPos, *mpTextForwarder );
+
+ return mpViewForwarder->SetSelection( MakeEESelection(aStartIndex, aEndIndex) );
+}
+
+bool SvxAccessibleTextEditViewAdapter::Copy()
+{
+ DBG_ASSERT(mpViewForwarder, "SvxAccessibleTextEditViewAdapter: no forwarder");
+
+ return mpViewForwarder->Copy();
+}
+
+bool SvxAccessibleTextEditViewAdapter::Cut()
+{
+ DBG_ASSERT(mpViewForwarder, "SvxAccessibleTextEditViewAdapter: no forwarder");
+
+ return mpViewForwarder->Cut();
+}
+
+bool SvxAccessibleTextEditViewAdapter::Paste()
+{
+ DBG_ASSERT(mpViewForwarder, "SvxAccessibleTextEditViewAdapter: no forwarder");
+
+ return mpViewForwarder->Paste();
+}
+
+void SvxAccessibleTextEditViewAdapter::SetForwarder( SvxEditViewForwarder& rForwarder,
+ SvxAccessibleTextAdapter& rTextForwarder )
+{
+ mpViewForwarder = &rForwarder;
+ mpTextForwarder = &rTextForwarder;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/uno/unoedsrc.cxx b/editeng/source/uno/unoedsrc.cxx
new file mode 100644
index 0000000000..1c827472d1
--- /dev/null
+++ b/editeng/source/uno/unoedsrc.cxx
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svl/SfxBroadcaster.hxx>
+
+#include <editeng/unoedsrc.hxx>
+
+#include <osl/diagnose.h>
+
+
+void SvxEditSource::addRange( SvxUnoTextRangeBase* )
+{
+}
+
+
+void SvxEditSource::removeRange( SvxUnoTextRangeBase* )
+{
+}
+
+
+const SvxUnoTextRangeBaseVec& SvxEditSource::getRanges() const
+{
+ static SvxUnoTextRangeBaseVec gList;
+ return gList;
+}
+
+
+SvxTextForwarder::~SvxTextForwarder() COVERITY_NOEXCEPT_FALSE
+{
+}
+
+
+SvxViewForwarder::~SvxViewForwarder()
+{
+}
+
+
+SvxEditSource::~SvxEditSource()
+{
+}
+
+SvxViewForwarder* SvxEditSource::GetViewForwarder()
+{
+ return nullptr;
+}
+
+SvxEditViewForwarder* SvxEditSource::GetEditViewForwarder( bool )
+{
+ return nullptr;
+}
+
+SfxBroadcaster& SvxEditSource::GetBroadcaster() const
+{
+ OSL_FAIL("SvxEditSource::GetBroadcaster called for implementation missing this feature!");
+
+ static SfxBroadcaster aBroadcaster;
+
+ return aBroadcaster;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/uno/unofdesc.cxx b/editeng/source/uno/unofdesc.cxx
new file mode 100644
index 0000000000..722ae7d7f9
--- /dev/null
+++ b/editeng/source/uno/unofdesc.cxx
@@ -0,0 +1,229 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <editeng/eeitem.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/awt/FontDescriptor.hpp>
+
+#include <editeng/fontitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <editeng/memberids.h>
+#include <svl/itempool.hxx>
+#include <vcl/font.hxx>
+#include <vcl/unohelp.hxx>
+#include <tools/gen.hxx>
+
+#include <editeng/unofdesc.hxx>
+
+using namespace ::com::sun::star;
+
+
+void SvxUnoFontDescriptor::ConvertToFont( const awt::FontDescriptor& rDesc, vcl::Font& rFont )
+{
+ rFont.SetFamilyName( rDesc.Name );
+ rFont.SetStyleName( rDesc.StyleName );
+ rFont.SetFontSize( Size( rDesc.Width, rDesc.Height ) );
+ rFont.SetFamily( static_cast<FontFamily>(rDesc.Family) );
+ rFont.SetCharSet( static_cast<rtl_TextEncoding>(rDesc.CharSet) );
+ rFont.SetPitch( static_cast<FontPitch>(rDesc.Pitch) );
+ rFont.SetOrientation( Degree10(static_cast<sal_Int16>(rDesc.Orientation*10)) );
+ rFont.SetKerning( rDesc.Kerning ? FontKerning::FontSpecific : FontKerning::NONE );
+ rFont.SetWeight( vcl::unohelper::ConvertFontWeight(rDesc.Weight) );
+ rFont.SetItalic( static_cast<FontItalic>(rDesc.Slant) );
+ rFont.SetUnderline( static_cast<FontLineStyle>(rDesc.Underline) );
+ rFont.SetStrikeout( static_cast<FontStrikeout>(rDesc.Strikeout) );
+ rFont.SetWordLineMode( rDesc.WordLineMode );
+}
+
+void SvxUnoFontDescriptor::ConvertFromFont( const vcl::Font& rFont, awt::FontDescriptor& rDesc )
+{
+ rDesc.Name = rFont.GetFamilyName();
+ rDesc.StyleName = rFont.GetStyleName();
+ rDesc.Width = sal::static_int_cast< sal_Int16 >(rFont.GetFontSize().Width());
+ rDesc.Height = sal::static_int_cast< sal_Int16 >(rFont.GetFontSize().Height());
+ rDesc.Family = sal::static_int_cast< sal_Int16 >(rFont.GetFamilyType());
+ rDesc.CharSet = rFont.GetCharSet();
+ rDesc.Pitch = sal::static_int_cast< sal_Int16 >(rFont.GetPitch());
+ rDesc.Orientation = static_cast< float >(rFont.GetOrientation().get() / 10);
+ rDesc.Kerning = rFont.IsKerning();
+ rDesc.Weight = vcl::unohelper::ConvertFontWeight( rFont.GetWeight() );
+ rDesc.Slant = vcl::unohelper::ConvertFontSlant( rFont.GetItalic() );
+ rDesc.Underline = sal::static_int_cast< sal_Int16 >(rFont.GetUnderline());
+ rDesc.Strikeout = sal::static_int_cast< sal_Int16 >(rFont.GetStrikeout());
+ rDesc.WordLineMode = rFont.IsWordLineMode();
+}
+
+void SvxUnoFontDescriptor::FillItemSet( const awt::FontDescriptor& rDesc, SfxItemSet& rSet )
+{
+ uno::Any aTemp;
+
+ {
+ SvxFontItem aFontItem( EE_CHAR_FONTINFO );
+ aFontItem.SetFamilyName( rDesc.Name);
+ aFontItem.SetStyleName( rDesc.StyleName);
+ aFontItem.SetFamily( static_cast<FontFamily>(rDesc.Family));
+ aFontItem.SetCharSet( rDesc.CharSet );
+ aFontItem.SetPitch( static_cast<FontPitch>(rDesc.Pitch));
+ rSet.Put(aFontItem);
+ }
+
+ {
+ SvxFontHeightItem aFontHeightItem( 0, 100, EE_CHAR_FONTHEIGHT );
+ aTemp <<= static_cast<float>(rDesc.Height);
+ static_cast<SfxPoolItem*>(&aFontHeightItem)->PutValue( aTemp, MID_FONTHEIGHT|CONVERT_TWIPS );
+ rSet.Put(aFontHeightItem);
+ }
+
+ {
+ SvxPostureItem aPostureItem( ITALIC_NONE, EE_CHAR_ITALIC );
+ aTemp <<= rDesc.Slant;
+ static_cast<SfxPoolItem*>(&aPostureItem)->PutValue( aTemp, MID_POSTURE );
+ rSet.Put(aPostureItem);
+ }
+
+ {
+ SvxUnderlineItem aUnderlineItem( LINESTYLE_NONE, EE_CHAR_UNDERLINE );
+ aTemp <<= rDesc.Underline;
+ static_cast<SfxPoolItem*>(&aUnderlineItem)->PutValue( aTemp, MID_TL_STYLE );
+ rSet.Put( aUnderlineItem );
+ }
+
+ {
+ SvxWeightItem aWeightItem( WEIGHT_DONTKNOW, EE_CHAR_WEIGHT );
+ aTemp <<= rDesc.Weight;
+ static_cast<SfxPoolItem*>(&aWeightItem)->PutValue( aTemp, MID_WEIGHT );
+ rSet.Put( aWeightItem );
+ }
+
+ {
+ SvxCrossedOutItem aCrossedOutItem( STRIKEOUT_NONE, EE_CHAR_STRIKEOUT );
+ aTemp <<= rDesc.Strikeout;
+ static_cast<SfxPoolItem*>(&aCrossedOutItem)->PutValue( aTemp, MID_CROSS_OUT );
+ rSet.Put( aCrossedOutItem );
+ }
+
+ {
+ SvxWordLineModeItem aWLMItem( rDesc.WordLineMode, EE_CHAR_WLM );
+ rSet.Put( aWLMItem );
+ }
+}
+
+void SvxUnoFontDescriptor::FillFromItemSet( const SfxItemSet& rSet, awt::FontDescriptor& rDesc )
+{
+ const SfxPoolItem* pItem = nullptr;
+ {
+ const SvxFontItem* pFontItem = &rSet.Get( EE_CHAR_FONTINFO );
+ rDesc.Name = pFontItem->GetFamilyName();
+ rDesc.StyleName = pFontItem->GetStyleName();
+ rDesc.Family = sal::static_int_cast< sal_Int16 >(
+ pFontItem->GetFamily());
+ rDesc.CharSet = pFontItem->GetCharSet();
+ rDesc.Pitch = sal::static_int_cast< sal_Int16 >(
+ pFontItem->GetPitch());
+ }
+ {
+ pItem = &rSet.Get( EE_CHAR_FONTHEIGHT );
+ uno::Any aHeight;
+ if( pItem->QueryValue( aHeight, MID_FONTHEIGHT ) )
+ aHeight >>= rDesc.Height;
+ }
+ {
+ pItem = &rSet.Get( EE_CHAR_ITALIC );
+ uno::Any aFontSlant;
+ if(pItem->QueryValue( aFontSlant, MID_POSTURE ))
+ aFontSlant >>= rDesc.Slant;
+ }
+ {
+ pItem = &rSet.Get( EE_CHAR_UNDERLINE );
+ uno::Any aUnderline;
+ if(pItem->QueryValue( aUnderline, MID_TL_STYLE ))
+ aUnderline >>= rDesc.Underline;
+ }
+ {
+ pItem = &rSet.Get( EE_CHAR_WEIGHT );
+ uno::Any aWeight;
+ if(pItem->QueryValue( aWeight, MID_WEIGHT ))
+ aWeight >>= rDesc.Weight;
+ }
+ {
+ pItem = &rSet.Get( EE_CHAR_STRIKEOUT );
+ uno::Any aStrikeOut;
+ if(pItem->QueryValue( aStrikeOut, MID_CROSS_OUT ))
+ aStrikeOut >>= rDesc.Strikeout;
+ }
+ {
+ const SvxWordLineModeItem* pWLMItem = &rSet.Get( EE_CHAR_WLM );
+ rDesc.WordLineMode = pWLMItem->GetValue();
+ }
+}
+
+void SvxUnoFontDescriptor::setPropertyToDefault( SfxItemSet& rSet )
+{
+ rSet.InvalidateItem( EE_CHAR_FONTINFO );
+ rSet.InvalidateItem( EE_CHAR_FONTHEIGHT );
+ rSet.InvalidateItem( EE_CHAR_ITALIC );
+ rSet.InvalidateItem( EE_CHAR_UNDERLINE );
+ rSet.InvalidateItem( EE_CHAR_WEIGHT );
+ rSet.InvalidateItem( EE_CHAR_STRIKEOUT );
+ rSet.InvalidateItem( EE_CHAR_WLM );
+}
+
+uno::Any SvxUnoFontDescriptor::getPropertyDefault( SfxItemPool* pPool )
+{
+ SfxItemSetFixed<
+ EE_CHAR_FONTINFO, EE_CHAR_FONTHEIGHT,
+ EE_CHAR_WEIGHT, EE_CHAR_ITALIC,
+ EE_CHAR_WLM, EE_CHAR_WLM> aSet(*pPool);
+
+ uno::Any aAny;
+
+ if(!SfxItemPool::IsWhich(EE_CHAR_FONTINFO)||
+ !SfxItemPool::IsWhich(EE_CHAR_FONTHEIGHT)||
+ !SfxItemPool::IsWhich(EE_CHAR_ITALIC)||
+ !SfxItemPool::IsWhich(EE_CHAR_UNDERLINE)||
+ !SfxItemPool::IsWhich(EE_CHAR_WEIGHT)||
+ !SfxItemPool::IsWhich(EE_CHAR_STRIKEOUT)||
+ !SfxItemPool::IsWhich(EE_CHAR_WLM))
+ return aAny;
+
+ aSet.Put(pPool->GetDefaultItem(EE_CHAR_FONTINFO));
+ aSet.Put(pPool->GetDefaultItem(EE_CHAR_FONTHEIGHT));
+ aSet.Put(pPool->GetDefaultItem(EE_CHAR_ITALIC));
+ aSet.Put(pPool->GetDefaultItem(EE_CHAR_UNDERLINE));
+ aSet.Put(pPool->GetDefaultItem(EE_CHAR_WEIGHT));
+ aSet.Put(pPool->GetDefaultItem(EE_CHAR_STRIKEOUT));
+ aSet.Put(pPool->GetDefaultItem(EE_CHAR_WLM));
+
+ awt::FontDescriptor aDesc;
+
+ FillFromItemSet( aSet, aDesc );
+
+ aAny <<= aDesc;
+
+ return aAny;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/uno/unofield.cxx b/editeng/source/uno/unofield.cxx
new file mode 100644
index 0000000000..6f2e84a957
--- /dev/null
+++ b/editeng/source/uno/unofield.cxx
@@ -0,0 +1,941 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <com/sun/star/util/DateTime.hpp>
+#include <com/sun/star/text/FilenameDisplayFormat.hpp>
+#include <o3tl/string_view.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <tools/debug.hxx>
+#include <svl/itemprop.hxx>
+
+#include <editeng/flditem.hxx>
+#include <editeng/CustomPropertyField.hxx>
+#include <editeng/measfld.hxx>
+#include <editeng/unofield.hxx>
+#include <editeng/unotext.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <sal/log.hxx>
+
+#include <editeng/unonames.hxx>
+
+using namespace ::cppu;
+using namespace ::com::sun::star;
+
+#define QUERYINT( xint ) \
+ if( rType == cppu::UnoType<xint>::get() ) \
+ aAny <<= uno::Reference< xint >(this)
+
+
+#define WID_DATE 0
+#define WID_BOOL1 1
+#define WID_BOOL2 2
+#define WID_INT32 3
+#define WID_INT16 4
+#define WID_STRING1 5
+#define WID_STRING2 6
+#define WID_STRING3 7
+
+class SvxUnoFieldData_Impl
+{
+public:
+ bool mbBoolean1;
+ bool mbBoolean2;
+ sal_Int32 mnInt32;
+ sal_Int16 mnInt16;
+ OUString msString1;
+ OUString msString2;
+ OUString msString3;
+ util::DateTime maDateTime;
+
+ OUString msPresentation;
+};
+
+static const SfxItemPropertySet* ImplGetFieldItemPropertySet( sal_Int32 mnId )
+{
+ static const SfxItemPropertyMapEntry aExDateTimeFieldPropertyMap_Impl[] =
+ {
+ { UNO_TC_PROP_DATE_TIME, WID_DATE, ::cppu::UnoType<util::DateTime>::get(), 0, 0 },
+ { UNO_TC_PROP_IS_FIXED, WID_BOOL1, cppu::UnoType<bool>::get(), 0, 0 },
+ { UNO_TC_PROP_IS_DATE, WID_BOOL2, cppu::UnoType<bool>::get(), 0, 0 },
+ { UNO_TC_PROP_NUMFORMAT, WID_INT32, ::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ };
+ static const SfxItemPropertySet aExDateTimeFieldPropertySet_Impl(aExDateTimeFieldPropertyMap_Impl);
+
+ static const SfxItemPropertyMapEntry aDateTimeFieldPropertyMap_Impl[] =
+ {
+ { UNO_TC_PROP_IS_DATE, WID_BOOL2, cppu::UnoType<bool>::get(), 0, 0 },
+ };
+ static const SfxItemPropertySet aDateTimeFieldPropertySet_Impl(aDateTimeFieldPropertyMap_Impl);
+
+ static const SfxItemPropertyMapEntry aUrlFieldPropertyMap_Impl[] =
+ {
+ { UNO_TC_PROP_URL_FORMAT, WID_INT16, ::cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ { UNO_TC_PROP_URL_REPRESENTATION, WID_STRING1, ::cppu::UnoType<OUString>::get(), 0, 0 },
+ { UNO_TC_PROP_URL_TARGET, WID_STRING2, ::cppu::UnoType<OUString>::get(), 0, 0 },
+ { UNO_TC_PROP_URL, WID_STRING3, ::cppu::UnoType<OUString>::get(), 0, 0 },
+ };
+ static const SfxItemPropertySet aUrlFieldPropertySet_Impl(aUrlFieldPropertyMap_Impl);
+
+ static const SfxItemPropertySet aEmptyPropertySet_Impl({});
+
+ static const SfxItemPropertyMapEntry aExtFileFieldPropertyMap_Impl[] =
+ {
+ { UNO_TC_PROP_IS_FIXED, WID_BOOL1, cppu::UnoType<bool>::get(), 0, 0 },
+ { UNO_TC_PROP_FILE_FORMAT, WID_INT16, ::cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ { UNO_TC_PROP_CURRENT_PRESENTATION, WID_STRING1, ::cppu::UnoType<OUString>::get(), 0, 0 },
+ };
+ static const SfxItemPropertySet aExtFileFieldPropertySet_Impl(aExtFileFieldPropertyMap_Impl);
+
+ static const SfxItemPropertyMapEntry aAuthorFieldPropertyMap_Impl[] =
+ {
+ { UNO_TC_PROP_IS_FIXED, WID_BOOL1, cppu::UnoType<bool>::get(), 0, 0 },
+ { UNO_TC_PROP_CURRENT_PRESENTATION, WID_STRING1,::cppu::UnoType<OUString>::get(), 0, 0 },
+ { UNO_TC_PROP_AUTHOR_CONTENT, WID_STRING2,::cppu::UnoType<OUString>::get(), 0, 0 },
+ { UNO_TC_PROP_AUTHOR_FORMAT, WID_INT16, ::cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ { UNO_TC_PROP_AUTHOR_FULLNAME, WID_BOOL2, cppu::UnoType<bool>::get(), 0, 0 },
+ };
+ static const SfxItemPropertySet aAuthorFieldPropertySet_Impl(aAuthorFieldPropertyMap_Impl);
+
+ static const SfxItemPropertyMapEntry aMeasureFieldPropertyMap_Impl[] =
+ {
+ { UNO_TC_PROP_MEASURE_KIND, WID_INT16, ::cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ };
+ static const SfxItemPropertySet aMeasureFieldPropertySet_Impl(aMeasureFieldPropertyMap_Impl);
+
+ static const SfxItemPropertyMapEntry aDocInfoCustomFieldPropertyMap_Impl[] =
+ {
+ { UNO_TC_PROP_NAME, WID_STRING1, cppu::UnoType<OUString>::get(), 0, 0 },
+ { UNO_TC_PROP_CURRENT_PRESENTATION, WID_STRING2, cppu::UnoType<OUString>::get(), 0, 0 },
+ { UNO_TC_PROP_IS_FIXED, WID_BOOL1, cppu::UnoType<bool>::get(), 0, 0 },
+ { UNO_TC_PROP_NUMFORMAT, WID_INT32, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { UNO_TC_PROP_IS_FIXED_LANGUAGE, WID_BOOL2, cppu::UnoType<bool>::get(), 0, 0 },
+ };
+ static const SfxItemPropertySet aDocInfoCustomFieldPropertySet_Impl(aDocInfoCustomFieldPropertyMap_Impl);
+
+ switch( mnId )
+ {
+ case text::textfield::Type::EXTENDED_TIME:
+ case text::textfield::Type::DATE:
+ return &aExDateTimeFieldPropertySet_Impl;
+ case text::textfield::Type::URL:
+ return &aUrlFieldPropertySet_Impl;
+ case text::textfield::Type::TIME:
+ return &aDateTimeFieldPropertySet_Impl;
+ case text::textfield::Type::EXTENDED_FILE:
+ return &aExtFileFieldPropertySet_Impl;
+ case text::textfield::Type::AUTHOR:
+ return &aAuthorFieldPropertySet_Impl;
+ case text::textfield::Type::MEASURE:
+ return &aMeasureFieldPropertySet_Impl;
+ case text::textfield::Type::DOCINFO_CUSTOM:
+ return &aDocInfoCustomFieldPropertySet_Impl;
+ default:
+ return &aEmptyPropertySet_Impl;
+ }
+}
+
+/* conversion routines */
+
+static sal_Int16 getFileNameDisplayFormat( SvxFileFormat nFormat )
+{
+ switch( nFormat )
+ {
+ case SvxFileFormat::NameAndExt: return text::FilenameDisplayFormat::NAME_AND_EXT;
+ case SvxFileFormat::PathFull: return text::FilenameDisplayFormat::FULL;
+ case SvxFileFormat::PathOnly: return text::FilenameDisplayFormat::PATH;
+// case SvxFileFormat::NameOnly:
+ default: return text::FilenameDisplayFormat::NAME;
+ }
+}
+
+static SvxFileFormat setFileNameDisplayFormat( sal_Int16 nFormat )
+{
+ switch( nFormat )
+ {
+ case text::FilenameDisplayFormat::FULL: return SvxFileFormat::PathFull;
+ case text::FilenameDisplayFormat::PATH: return SvxFileFormat::PathOnly;
+ case text::FilenameDisplayFormat::NAME: return SvxFileFormat::NameOnly;
+// case text::FilenameDisplayFormat::NAME_AND_EXT:
+ default:
+ return SvxFileFormat::NameAndExt;
+ }
+}
+
+static util::DateTime getDate( sal_Int32 nDate )
+{
+ util::DateTime aDate;
+
+ Date aTempDate( nDate );
+
+ aDate.Day = aTempDate.GetDay();
+ aDate.Month = aTempDate.GetMonth();
+ aDate.Year = aTempDate.GetYear();
+
+ return aDate;
+}
+
+static Date setDate( util::DateTime const & rDate )
+{
+ return Date( rDate.Day, rDate.Month, rDate.Year );
+}
+
+static util::DateTime getTime(sal_Int64 const nTime)
+{
+ util::DateTime aTime;
+
+ tools::Time aTempTime( nTime );
+
+ aTime.NanoSeconds = aTempTime.GetNanoSec();
+ aTime.Seconds = aTempTime.GetSec();
+ aTime.Minutes = aTempTime.GetMin();
+ aTime.Hours = aTempTime.GetHour();
+
+ return aTime;
+}
+
+static tools::Time setTime( util::DateTime const & rDate )
+{
+ return tools::Time( rDate );
+}
+
+
+
+SvxUnoTextField::SvxUnoTextField( sal_Int32 nServiceId ) noexcept
+: OComponentHelper( m_aMutex )
+, mpPropSet(nullptr)
+, mnServiceId(nServiceId)
+, mpImpl( new SvxUnoFieldData_Impl )
+{
+ mpPropSet = ImplGetFieldItemPropertySet(mnServiceId);
+
+ mpImpl->maDateTime.NanoSeconds = 0;
+ mpImpl->maDateTime.Seconds = 0;
+ mpImpl->maDateTime.Minutes = 0;
+ mpImpl->maDateTime.Hours = 0;
+ mpImpl->maDateTime.Day = 0;
+ mpImpl->maDateTime.Month = 0;
+ mpImpl->maDateTime.Year = 0;
+ mpImpl->maDateTime.IsUTC = false;
+
+ switch( nServiceId )
+ {
+ case text::textfield::Type::DATE:
+ mpImpl->mbBoolean2 = true;
+ mpImpl->mnInt32 = static_cast<sal_Int32>(SvxDateFormat::StdSmall);
+ mpImpl->mbBoolean1 = false;
+ break;
+
+ case text::textfield::Type::EXTENDED_TIME:
+ case text::textfield::Type::TIME:
+ mpImpl->mbBoolean2 = false;
+ mpImpl->mbBoolean1 = false;
+ mpImpl->mnInt32 = static_cast<sal_Int32>(SvxTimeFormat::Standard);
+ break;
+
+ case text::textfield::Type::URL:
+ mpImpl->mnInt16 = static_cast<sal_uInt16>(SvxURLFormat::Repr);
+ break;
+
+ case text::textfield::Type::EXTENDED_FILE:
+ mpImpl->mbBoolean1 = false;
+ mpImpl->mnInt16 = text::FilenameDisplayFormat::FULL;
+ break;
+
+ case text::textfield::Type::AUTHOR:
+ mpImpl->mnInt16 = static_cast<sal_uInt16>(SvxAuthorFormat::FullName);
+ mpImpl->mbBoolean1 = false;
+ mpImpl->mbBoolean2 = true;
+ break;
+
+ case text::textfield::Type::MEASURE:
+ mpImpl->mnInt16 = static_cast<sal_uInt16>(SdrMeasureFieldKind::Value);
+ break;
+
+ case text::textfield::Type::DOCINFO_CUSTOM:
+ mpImpl->mbBoolean1 = true;
+ mpImpl->mbBoolean2 = true;
+ mpImpl->mnInt32 = 0;
+ break;
+
+ default:
+ mpImpl->mbBoolean1 = false;
+ mpImpl->mbBoolean2 = false;
+ mpImpl->mnInt32 = 0;
+ mpImpl->mnInt16 = 0;
+
+ }
+}
+
+SvxUnoTextField::SvxUnoTextField( uno::Reference< text::XTextRange > xAnchor, const OUString& rPresentation, const SvxFieldData* pData ) noexcept
+: OComponentHelper( m_aMutex )
+, mxAnchor(std::move( xAnchor ))
+, mpPropSet(nullptr)
+, mnServiceId(text::textfield::Type::UNSPECIFIED)
+, mpImpl( new SvxUnoFieldData_Impl )
+{
+ DBG_ASSERT(pData, "pFieldData == NULL! [CL]" );
+
+ mpImpl->msPresentation = rPresentation;
+
+ if(pData)
+ {
+ mnServiceId = pData->GetClassId();
+ DBG_ASSERT(mnServiceId != text::textfield::Type::UNSPECIFIED, "unknown SvxFieldData! [CL]");
+ if (mnServiceId != text::textfield::Type::UNSPECIFIED)
+ {
+ // extract field properties from data class
+ switch( mnServiceId )
+ {
+ case text::textfield::Type::DATE:
+ {
+ mpImpl->mbBoolean2 = true;
+ // #i35416# for variable date field, don't use invalid "0000-00-00" date,
+ // use current date instead
+ bool bFixed = static_cast<const SvxDateField*>(pData)->GetType() == SvxDateType::Fix;
+ mpImpl->maDateTime = getDate( bFixed ?
+ static_cast<const SvxDateField*>(pData)->GetFixDate() :
+ Date( Date::SYSTEM ).GetDate() );
+ mpImpl->mnInt32 = static_cast<sal_Int32>(static_cast<const SvxDateField*>(pData)->GetFormat());
+ mpImpl->mbBoolean1 = bFixed;
+ }
+ break;
+
+ case text::textfield::Type::TIME:
+ mpImpl->mbBoolean2 = false;
+ mpImpl->mbBoolean1 = false;
+ mpImpl->mnInt32 = static_cast<sal_Int32>(SvxTimeFormat::Standard);
+ break;
+
+ case text::textfield::Type::EXTENDED_TIME:
+ mpImpl->mbBoolean2 = false;
+ mpImpl->maDateTime = getTime( static_cast<const SvxExtTimeField*>(pData)->GetFixTime() );
+ mpImpl->mbBoolean1 = static_cast<const SvxExtTimeField*>(pData)->GetType() == SvxTimeType::Fix;
+ mpImpl->mnInt32 = static_cast<sal_Int32>(static_cast<const SvxExtTimeField*>(pData)->GetFormat());
+ break;
+
+ case text::textfield::Type::URL:
+ mpImpl->msString1 = static_cast<const SvxURLField*>(pData)->GetRepresentation();
+ mpImpl->msString2 = static_cast<const SvxURLField*>(pData)->GetTargetFrame();
+ mpImpl->msString3 = static_cast<const SvxURLField*>(pData)->GetURL();
+ mpImpl->mnInt16 = sal::static_int_cast< sal_Int16 >(
+ static_cast<const SvxURLField*>(pData)->GetFormat());
+ break;
+
+ case text::textfield::Type::EXTENDED_FILE:
+ mpImpl->msString1 = static_cast<const SvxExtFileField*>(pData)->GetFile();
+ mpImpl->mbBoolean1 = static_cast<const SvxExtFileField*>(pData)->GetType() == SvxFileType::Fix;
+ mpImpl->mnInt16 = getFileNameDisplayFormat(static_cast<const SvxExtFileField*>(pData)->GetFormat());
+ break;
+
+ case text::textfield::Type::AUTHOR:
+ mpImpl->msString1 = static_cast<const SvxAuthorField*>(pData)->GetFormatted();
+ mpImpl->msString2 = static_cast<const SvxAuthorField*>(pData)->GetFormatted();
+ mpImpl->mnInt16 = sal::static_int_cast< sal_Int16 >(
+ static_cast<const SvxAuthorField*>(pData)->GetFormat());
+ mpImpl->mbBoolean1 = static_cast<const SvxAuthorField*>(pData)->GetType() == SvxAuthorType::Fix;
+ mpImpl->mbBoolean2 = static_cast<const SvxAuthorField*>(pData)->GetFormat() != SvxAuthorFormat::ShortName;
+ break;
+
+ case text::textfield::Type::MEASURE:
+ mpImpl->mnInt16 = sal::static_int_cast< sal_Int16 >(static_cast<const SdrMeasureField*>(pData)->GetMeasureFieldKind());
+ break;
+
+ case text::textfield::Type::DOCINFO_CUSTOM:
+ mpImpl->msString1 = static_cast<const editeng::CustomPropertyField*>(pData)->GetName();
+ mpImpl->msString2 = static_cast<const editeng::CustomPropertyField*>(pData)->GetCurrentPresentation();
+ mpImpl->mbBoolean1 = false;
+ mpImpl->mbBoolean2 = false;
+ mpImpl->mnInt32 = 0;
+ break;
+
+ default:
+ SAL_INFO("editeng", "Id service unknown: " << mnServiceId);
+ break;
+ }
+ }
+ }
+
+ mpPropSet = ImplGetFieldItemPropertySet(mnServiceId);
+}
+
+SvxUnoTextField::~SvxUnoTextField() noexcept
+{
+}
+
+std::unique_ptr<SvxFieldData> SvxUnoTextField::CreateFieldData() const noexcept
+{
+ std::unique_ptr<SvxFieldData> pData;
+
+ switch( mnServiceId )
+ {
+ case text::textfield::Type::TIME:
+ case text::textfield::Type::EXTENDED_TIME:
+ case text::textfield::Type::DATE:
+ {
+ if( mpImpl->mbBoolean2 ) // IsDate?
+ {
+ Date aDate( setDate( mpImpl->maDateTime ) );
+ pData.reset( new SvxDateField( aDate, mpImpl->mbBoolean1?SvxDateType::Fix:SvxDateType::Var ) );
+ if( mpImpl->mnInt32 >= static_cast<sal_Int32>(SvxDateFormat::AppDefault) &&
+ mpImpl->mnInt32 <= static_cast<sal_Int32>(SvxDateFormat::F) )
+ static_cast<SvxDateField*>(pData.get())->SetFormat( static_cast<SvxDateFormat>(mpImpl->mnInt32) );
+ }
+ else
+ {
+ if( mnServiceId != text::textfield::Type::TIME && mnServiceId != text::textfield::Type::DATE )
+ {
+ tools::Time aTime( setTime( mpImpl->maDateTime ) );
+ pData.reset( new SvxExtTimeField( aTime, mpImpl->mbBoolean1?SvxTimeType::Fix:SvxTimeType::Var ) );
+
+ if( static_cast<SvxTimeFormat>(mpImpl->mnInt32) >= SvxTimeFormat::AppDefault &&
+ static_cast<SvxTimeFormat>(mpImpl->mnInt32) <= SvxTimeFormat::HH12_MM_SS_00_AMPM )
+ static_cast<SvxExtTimeField*>(pData.get())->SetFormat( static_cast<SvxTimeFormat>(mpImpl->mnInt32) );
+ }
+ else
+ {
+ pData.reset( new SvxTimeField() );
+ }
+ }
+
+ }
+ break;
+
+ case text::textfield::Type::URL:
+ pData.reset( new SvxURLField( mpImpl->msString3, mpImpl->msString1, !mpImpl->msString1.isEmpty() ? SvxURLFormat::Repr : SvxURLFormat::Url ) );
+ static_cast<SvxURLField*>(pData.get())->SetTargetFrame( mpImpl->msString2 );
+ if( static_cast<SvxURLFormat>(mpImpl->mnInt16) >= SvxURLFormat::AppDefault &&
+ static_cast<SvxURLFormat>(mpImpl->mnInt16) <= SvxURLFormat::Repr )
+ static_cast<SvxURLField*>(pData.get())->SetFormat( static_cast<SvxURLFormat>(mpImpl->mnInt16) );
+ break;
+
+ case text::textfield::Type::PAGE:
+ pData.reset( new SvxPageField() );
+ break;
+
+ case text::textfield::Type::PAGES:
+ pData.reset( new SvxPagesField() );
+ break;
+
+ case text::textfield::Type::DOCINFO_TITLE:
+ pData.reset( new SvxFileField() );
+ break;
+
+ case text::textfield::Type::TABLE:
+ pData.reset( new SvxTableField() );
+ break;
+
+ case text::textfield::Type::EXTENDED_FILE:
+ {
+ // #92009# pass fixed attribute to constructor
+ pData.reset( new SvxExtFileField( mpImpl->msString1,
+ mpImpl->mbBoolean1 ? SvxFileType::Fix : SvxFileType::Var,
+ setFileNameDisplayFormat(mpImpl->mnInt16 ) ) );
+ break;
+ }
+
+ case text::textfield::Type::AUTHOR:
+ {
+ OUString aContent;
+ OUString aFirstName;
+ OUString aLastName;
+
+ // do we have CurrentPresentation given?
+ // mimic behaviour of writer, which means:
+ // prefer CurrentPresentation over Content
+ // if both are given.
+ if( !mpImpl->msString1.isEmpty() )
+ aContent = mpImpl->msString1;
+ else
+ aContent = mpImpl->msString2;
+
+ sal_Int32 nPos = aContent.lastIndexOf( ' ', 0 );
+ if( nPos > 0 )
+ {
+ aFirstName = aContent.copy( 0, nPos );
+ aLastName = aContent.copy( nPos + 1 );
+ }
+ else
+ {
+ aLastName = aContent;
+ }
+
+ // #92009# pass fixed attribute to constructor
+ pData.reset( new SvxAuthorField( aFirstName, aLastName, "",
+ mpImpl->mbBoolean1 ? SvxAuthorType::Fix : SvxAuthorType::Var ) );
+
+ if( !mpImpl->mbBoolean2 )
+ {
+ static_cast<SvxAuthorField*>(pData.get())->SetFormat( SvxAuthorFormat::ShortName );
+ }
+ else if( static_cast<SvxAuthorFormat>(mpImpl->mnInt16) >= SvxAuthorFormat::FullName &&
+ static_cast<SvxAuthorFormat>(mpImpl->mnInt16) <= SvxAuthorFormat::ShortName )
+ {
+ static_cast<SvxAuthorField*>(pData.get())->SetFormat( static_cast<SvxAuthorFormat>(mpImpl->mnInt16) );
+ }
+
+ break;
+ }
+
+ case text::textfield::Type::MEASURE:
+ {
+ SdrMeasureFieldKind eKind = SdrMeasureFieldKind::Value;
+ if( mpImpl->mnInt16 == sal_Int16(SdrMeasureFieldKind::Unit) || mpImpl->mnInt16 == sal_Int16(SdrMeasureFieldKind::Rotate90Blanks) )
+ eKind = static_cast<SdrMeasureFieldKind>(mpImpl->mnInt16);
+ pData.reset( new SdrMeasureField( eKind) );
+ break;
+ }
+ case text::textfield::Type::PRESENTATION_HEADER:
+ pData.reset( new SvxHeaderField() );
+ break;
+ case text::textfield::Type::PRESENTATION_FOOTER:
+ pData.reset( new SvxFooterField() );
+ break;
+ case text::textfield::Type::PRESENTATION_DATE_TIME:
+ pData.reset( new SvxDateTimeField() );
+ break;
+ case text::textfield::Type::PAGE_NAME:
+ pData.reset( new SvxPageTitleField() );
+ break;
+ case text::textfield::Type::DOCINFO_CUSTOM:
+ pData.reset( new editeng::CustomPropertyField(mpImpl->msString1, mpImpl->msString2) );
+ break;
+ }
+
+ return pData;
+}
+
+// uno::XInterface
+uno::Any SAL_CALL SvxUnoTextField::queryAggregation( const uno::Type & rType )
+{
+ uno::Any aAny;
+
+ QUERYINT( beans::XPropertySet );
+ else QUERYINT( text::XTextContent );
+ else QUERYINT( text::XTextField );
+ else QUERYINT( lang::XServiceInfo );
+ else
+ return OComponentHelper::queryAggregation( rType );
+
+ return aAny;
+}
+
+// XTypeProvider
+
+uno::Sequence< uno::Type > SAL_CALL SvxUnoTextField::getTypes()
+{
+ if( !maTypeSequence.hasElements() )
+ {
+ maTypeSequence = comphelper::concatSequences(
+ OComponentHelper::getTypes(),
+ uno::Sequence {
+ cppu::UnoType<text::XTextField>::get(),
+ cppu::UnoType<beans::XPropertySet>::get(),
+ cppu::UnoType<lang::XServiceInfo>::get(),
+ cppu::UnoType<lang::XUnoTunnel>::get() });
+ }
+ return maTypeSequence;
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL SvxUnoTextField::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+uno::Any SAL_CALL SvxUnoTextField::queryInterface( const uno::Type & rType )
+{
+ return OComponentHelper::queryInterface(rType);
+}
+
+void SAL_CALL SvxUnoTextField::acquire() noexcept
+{
+ OComponentHelper::acquire();
+}
+
+void SAL_CALL SvxUnoTextField::release() noexcept
+{
+ OComponentHelper::release();
+}
+
+// Interface text::XTextField
+OUString SAL_CALL SvxUnoTextField::getPresentation( sal_Bool bShowCommand )
+{
+ SolarMutexGuard aGuard;
+ if (bShowCommand)
+ {
+ switch (mnServiceId)
+ {
+ case text::textfield::Type::DATE:
+ return "Date";
+ case text::textfield::Type::URL:
+ return "URL";
+ case text::textfield::Type::PAGE:
+ return "Page";
+ case text::textfield::Type::PAGES:
+ return "Pages";
+ case text::textfield::Type::TIME:
+ return "Time";
+ case text::textfield::Type::DOCINFO_TITLE:
+ return "File";
+ case text::textfield::Type::TABLE:
+ return "Table";
+ case text::textfield::Type::EXTENDED_TIME:
+ return "ExtTime";
+ case text::textfield::Type::EXTENDED_FILE:
+ return "ExtFile";
+ case text::textfield::Type::AUTHOR:
+ return "Author";
+ case text::textfield::Type::MEASURE:
+ return "Measure";
+ case text::textfield::Type::PRESENTATION_HEADER:
+ return "Header";
+ case text::textfield::Type::PRESENTATION_FOOTER:
+ return "Footer";
+ case text::textfield::Type::PRESENTATION_DATE_TIME:
+ return "DateTime";
+ case text::textfield::Type::PAGE_NAME:
+ return "PageName";
+ case text::textfield::Type::DOCINFO_CUSTOM:
+ return "Custom";
+ default:
+ return "Unknown";
+ }
+ }
+ else
+ {
+ return mpImpl->msPresentation;
+ }
+}
+
+// Interface text::XTextContent
+void SAL_CALL SvxUnoTextField::attach( const uno::Reference< text::XTextRange >& xTextRange )
+{
+ SvxUnoTextRangeBase* pRange = comphelper::getFromUnoTunnel<SvxUnoTextRange>( xTextRange );
+ if(pRange == nullptr)
+ throw lang::IllegalArgumentException();
+
+ std::unique_ptr<SvxFieldData> pData = CreateFieldData();
+ if( pData )
+ pRange->attachField( std::move(pData) );
+}
+
+uno::Reference< text::XTextRange > SAL_CALL SvxUnoTextField::getAnchor()
+{
+ return mxAnchor;
+}
+
+// lang::XComponent
+void SAL_CALL SvxUnoTextField::dispose()
+{
+ OComponentHelper::dispose();
+ mxAnchor.clear();
+}
+
+void SAL_CALL SvxUnoTextField::addEventListener( const uno::Reference< lang::XEventListener >& xListener )
+{
+ OComponentHelper::addEventListener(xListener);
+}
+
+void SAL_CALL SvxUnoTextField::removeEventListener( const uno::Reference< lang::XEventListener >& aListener )
+{
+ OComponentHelper::removeEventListener(aListener);
+}
+
+
+// Interface beans::XPropertySet
+uno::Reference< beans::XPropertySetInfo > SAL_CALL SvxUnoTextField::getPropertySetInfo( )
+{
+ SolarMutexGuard aGuard;
+ return mpPropSet->getPropertySetInfo();
+}
+
+void SAL_CALL SvxUnoTextField::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+
+ if( mpImpl == nullptr )
+ throw uno::RuntimeException();
+
+ if (aPropertyName == UNO_TC_PROP_ANCHOR)
+ {
+ aValue >>= mxAnchor;
+ return;
+ }
+
+ const SfxItemPropertyMapEntry* pMap = mpPropSet->getPropertyMap().getByName( aPropertyName );
+ if ( !pMap )
+ throw beans::UnknownPropertyException(aPropertyName);
+
+ switch( pMap->nWID )
+ {
+ case WID_DATE:
+ if(aValue >>= mpImpl->maDateTime)
+ return;
+ break;
+ case WID_BOOL1:
+ if(aValue >>= mpImpl->mbBoolean1)
+ return;
+ break;
+ case WID_BOOL2:
+ if(aValue >>= mpImpl->mbBoolean2)
+ return;
+ break;
+ case WID_INT16:
+ if(aValue >>= mpImpl->mnInt16)
+ return;
+ break;
+ case WID_INT32:
+ if(aValue >>= mpImpl->mnInt32)
+ return;
+ break;
+ case WID_STRING1:
+ if(aValue >>= mpImpl->msString1)
+ return;
+ break;
+ case WID_STRING2:
+ if(aValue >>= mpImpl->msString2)
+ return;
+ break;
+ case WID_STRING3:
+ if(aValue >>= mpImpl->msString3)
+ return;
+ break;
+ }
+
+ throw lang::IllegalArgumentException();
+}
+
+uno::Any SAL_CALL SvxUnoTextField::getPropertyValue( const OUString& PropertyName )
+{
+ SolarMutexGuard aGuard;
+
+ if (PropertyName == UNO_TC_PROP_ANCHOR)
+ return uno::Any(mxAnchor);
+
+ if (PropertyName == UNO_TC_PROP_TEXTFIELD_TYPE)
+ return uno::Any(mnServiceId);
+
+ uno::Any aValue;
+
+ const SfxItemPropertyMapEntry* pMap = mpPropSet->getPropertyMap().getByName( PropertyName );
+ if ( !pMap )
+ throw beans::UnknownPropertyException(PropertyName);
+
+ switch( pMap->nWID )
+ {
+ case WID_DATE:
+ aValue <<= mpImpl->maDateTime;
+ break;
+ case WID_BOOL1:
+ aValue <<= mpImpl->mbBoolean1;
+ break;
+ case WID_BOOL2:
+ aValue <<= mpImpl->mbBoolean2;
+ break;
+ case WID_INT16:
+ aValue <<= mpImpl->mnInt16;
+ break;
+ case WID_INT32:
+ aValue <<= mpImpl->mnInt32;
+ break;
+ case WID_STRING1:
+ aValue <<= mpImpl->msString1;
+ break;
+ case WID_STRING2:
+ aValue <<= mpImpl->msString2;
+ break;
+ case WID_STRING3:
+ aValue <<= mpImpl->msString3;
+ break;
+ }
+
+ return aValue;
+}
+
+void SAL_CALL SvxUnoTextField::addPropertyChangeListener( const OUString&, const uno::Reference< beans::XPropertyChangeListener >& ) {}
+void SAL_CALL SvxUnoTextField::removePropertyChangeListener( const OUString&, const uno::Reference< beans::XPropertyChangeListener >& ) {}
+void SAL_CALL SvxUnoTextField::addVetoableChangeListener( const OUString&, const uno::Reference< beans::XVetoableChangeListener >& ) {}
+void SAL_CALL SvxUnoTextField::removeVetoableChangeListener( const OUString&, const uno::Reference< beans::XVetoableChangeListener >& ) {}
+
+// OComponentHelper
+void SvxUnoTextField::disposing()
+{
+ // nothing to do
+}
+
+// lang::XServiceInfo
+OUString SAL_CALL SvxUnoTextField::getImplementationName()
+{
+ return "SvxUnoTextField";
+}
+
+uno::Sequence< OUString > SAL_CALL SvxUnoTextField::getSupportedServiceNames()
+{
+ uno::Sequence<OUString> aSeq(4);
+ OUString* pServices = aSeq.getArray();
+ pServices[0] = "com.sun.star.text.TextContent";
+ pServices[1] = "com.sun.star.text.TextField";
+
+ switch (mnServiceId)
+ {
+ case text::textfield::Type::DATE:
+ pServices[2] = "com.sun.star.text.TextField.DateTime";
+ pServices[3] = "com.sun.star.text.textfield.DateTime";
+ break;
+ case text::textfield::Type::URL:
+ pServices[2] = "com.sun.star.text.TextField.URL";
+ pServices[3] = "com.sun.star.text.textfield.URL";
+ break;
+ case text::textfield::Type::PAGE:
+ pServices[2] = "com.sun.star.text.TextField.PageNumber";
+ pServices[3] = "com.sun.star.text.textfield.PageNumber";
+ break;
+ case text::textfield::Type::PAGES:
+ pServices[2] = "com.sun.star.text.TextField.PageCount";
+ pServices[3] = "com.sun.star.text.textfield.PageCount";
+ break;
+ case text::textfield::Type::TIME:
+ pServices[2] = "com.sun.star.text.TextField.DateTime";
+ pServices[3] = "com.sun.star.text.textfield.DateTime";
+ break;
+ case text::textfield::Type::DOCINFO_TITLE:
+ pServices[2] = "com.sun.star.text.TextField.docinfo.Title";
+ pServices[3] = "com.sun.star.text.textfield.docinfo.Title";
+ break;
+ case text::textfield::Type::TABLE:
+ pServices[2] = "com.sun.star.text.TextField.SheetName";
+ pServices[3] = "com.sun.star.text.textfield.SheetName";
+ break;
+ case text::textfield::Type::EXTENDED_TIME:
+ pServices[2] = "com.sun.star.text.TextField.DateTime";
+ pServices[3] = "com.sun.star.text.textfield.DateTime";
+ break;
+ case text::textfield::Type::EXTENDED_FILE:
+ pServices[2] = "com.sun.star.text.TextField.FileName";
+ pServices[3] = "com.sun.star.text.textfield.FileName";
+ break;
+ case text::textfield::Type::AUTHOR:
+ pServices[2] = "com.sun.star.text.TextField.Author";
+ pServices[3] = "com.sun.star.text.textfield.Author";
+ break;
+ case text::textfield::Type::MEASURE:
+ pServices[2] = "com.sun.star.text.TextField.Measure";
+ pServices[3] = "com.sun.star.text.textfield.Measure";
+ break;
+ case text::textfield::Type::PRESENTATION_HEADER:
+ pServices[2] = "com.sun.star.presentation.TextField.Header";
+ pServices[3] = "com.sun.star.presentation.textfield.Header";
+ break;
+ case text::textfield::Type::PRESENTATION_FOOTER:
+ pServices[2] = "com.sun.star.presentation.TextField.Footer";
+ pServices[3] = "com.sun.star.presentation.textfield.Footer";
+ break;
+ case text::textfield::Type::PRESENTATION_DATE_TIME:
+ pServices[2] = "com.sun.star.presentation.TextField.DateTime";
+ pServices[3] = "com.sun.star.presentation.textfield.DateTime";
+ break;
+ case text::textfield::Type::PAGE_NAME:
+ pServices[2] = "com.sun.star.text.TextField.PageName";
+ pServices[3] = "com.sun.star.text.textfield.PageName";
+ break;
+ case text::textfield::Type::DOCINFO_CUSTOM:
+ pServices[2] = "com.sun.star.text.TextField.DocInfo.Custom";
+ pServices[3] = "com.sun.star.text.textfield.DocInfo.Custom";
+ break;
+ default:
+ aSeq.realloc(0);
+ }
+
+ return aSeq;
+}
+
+sal_Bool SAL_CALL SvxUnoTextField::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService( this, ServiceName );
+}
+
+uno::Reference< uno::XInterface > SvxUnoTextCreateTextField( std::u16string_view ServiceSpecifier )
+{
+ uno::Reference< uno::XInterface > xRet;
+
+ // #i93308# up to OOo 3.2 we used this wrong namespace name with the capital T & F. This is
+ // fixed since OOo 3.2 but for compatibility we will still provide support for the wrong notation.
+
+ std::u16string_view aFieldType;
+ if( (o3tl::starts_with( ServiceSpecifier, u"com.sun.star.text.textfield.", &aFieldType )) ||
+ (o3tl::starts_with( ServiceSpecifier, u"com.sun.star.text.TextField.", &aFieldType )) )
+ {
+ sal_Int32 nId = text::textfield::Type::UNSPECIFIED;
+
+ if ( aFieldType == u"DateTime" )
+ {
+ nId = text::textfield::Type::DATE;
+ }
+ else if ( aFieldType == u"URL" )
+ {
+ nId = text::textfield::Type::URL;
+ }
+ else if ( aFieldType == u"PageNumber" )
+ {
+ nId = text::textfield::Type::PAGE;
+ }
+ else if ( aFieldType == u"PageCount" )
+ {
+ nId = text::textfield::Type::PAGES;
+ }
+ else if ( aFieldType == u"SheetName" )
+ {
+ nId = text::textfield::Type::TABLE;
+ }
+ else if ( aFieldType == u"FileName" )
+ {
+ nId = text::textfield::Type::EXTENDED_FILE;
+ }
+ else if (aFieldType == u"docinfo.Title" ||
+ aFieldType == u"DocInfo.Title" )
+ {
+ nId = text::textfield::Type::DOCINFO_TITLE;
+ }
+ else if ( aFieldType == u"Author" )
+ {
+ nId = text::textfield::Type::AUTHOR;
+ }
+ else if ( aFieldType == u"Measure" )
+ {
+ nId = text::textfield::Type::MEASURE;
+ }
+ else if (aFieldType == u"DocInfo.Custom")
+ {
+ nId = text::textfield::Type::DOCINFO_CUSTOM;
+ }
+
+ if (nId != text::textfield::Type::UNSPECIFIED)
+ xRet = getXWeak(new SvxUnoTextField( nId ));
+ }
+
+ return xRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/uno/unofored.cxx b/editeng/source/uno/unofored.cxx
new file mode 100644
index 0000000000..66f4fde2bf
--- /dev/null
+++ b/editeng/source/uno/unofored.cxx
@@ -0,0 +1,520 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <osl/diagnose.h>
+#include <tools/debug.hxx>
+#include <editeng/eeitem.hxx>
+#include <com/sun/star/i18n/WordType.hpp>
+
+#include <svl/itemset.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/unoedhlp.hxx>
+#include <editeng/editdata.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/editobj.hxx>
+
+#include <editeng/unofored.hxx>
+#include "unofored_internal.hxx"
+
+using namespace ::com::sun::star;
+
+
+SvxEditEngineForwarder::SvxEditEngineForwarder( EditEngine& rEngine ) :
+ rEditEngine( rEngine )
+{
+}
+
+SvxEditEngineForwarder::~SvxEditEngineForwarder()
+{
+ // the EditEngine may need to be deleted from the outside
+}
+
+sal_Int32 SvxEditEngineForwarder::GetParagraphCount() const
+{
+ return rEditEngine.GetParagraphCount();
+}
+
+sal_Int32 SvxEditEngineForwarder::GetTextLen( sal_Int32 nParagraph ) const
+{
+ return rEditEngine.GetTextLen( nParagraph );
+}
+
+OUString SvxEditEngineForwarder::GetText( const ESelection& rSel ) const
+{
+ return convertLineEnd(rEditEngine.GetText(rSel), GetSystemLineEnd());
+}
+
+SfxItemSet SvxEditEngineForwarder::GetAttribs( const ESelection& rSel, EditEngineAttribs nOnlyHardAttrib ) const
+{
+ if( rSel.nStartPara == rSel.nEndPara )
+ {
+ GetAttribsFlags nFlags = GetAttribsFlags::NONE;
+ switch( nOnlyHardAttrib )
+ {
+ case EditEngineAttribs::All:
+ nFlags = GetAttribsFlags::ALL;
+ break;
+ case EditEngineAttribs::OnlyHard:
+ nFlags = GetAttribsFlags::CHARATTRIBS;
+ break;
+ default:
+ OSL_FAIL("unknown flags for SvxOutlinerForwarder::GetAttribs");
+ }
+
+ return rEditEngine.GetAttribs( rSel.nStartPara, rSel.nStartPos, rSel.nEndPos, nFlags );
+ }
+ else
+ {
+ return rEditEngine.GetAttribs( rSel, nOnlyHardAttrib );
+ }
+}
+
+SfxItemSet SvxEditEngineForwarder::GetParaAttribs( sal_Int32 nPara ) const
+{
+ SfxItemSet aSet( rEditEngine.GetParaAttribs( nPara ) );
+
+ sal_uInt16 nWhich = EE_PARA_START;
+ while( nWhich <= EE_PARA_END )
+ {
+ if( aSet.GetItemState( nWhich ) != SfxItemState::SET )
+ {
+ if( rEditEngine.HasParaAttrib( nPara, nWhich ) )
+ aSet.Put( rEditEngine.GetParaAttrib( nPara, nWhich ) );
+ }
+ nWhich++;
+ }
+
+ return aSet;
+}
+
+void SvxEditEngineForwarder::SetParaAttribs( sal_Int32 nPara, const SfxItemSet& rSet )
+{
+ rEditEngine.SetParaAttribs( nPara, rSet );
+}
+
+void SvxEditEngineForwarder::RemoveAttribs( const ESelection& rSelection )
+{
+ rEditEngine.RemoveAttribs( rSelection, false/*bRemoveParaAttribs*/, 0 );
+}
+
+SfxItemPool* SvxEditEngineForwarder::GetPool() const
+{
+ return rEditEngine.GetEmptyItemSet().GetPool();
+}
+
+void SvxEditEngineForwarder::GetPortions( sal_Int32 nPara, std::vector<sal_Int32>& rList ) const
+{
+ rEditEngine.GetPortions( nPara, rList );
+}
+
+OUString SvxEditEngineForwarder::GetStyleSheet(sal_Int32 nPara) const
+{
+ if (auto pStyle = rEditEngine.GetStyleSheet(nPara))
+ return pStyle->GetName();
+ return OUString();
+}
+
+void SvxEditEngineForwarder::SetStyleSheet(sal_Int32 nPara, const OUString& rStyleName)
+{
+ auto pStyleSheetPool = rEditEngine.GetStyleSheetPool();
+ if (auto pStyle = pStyleSheetPool ? pStyleSheetPool->Find(rStyleName, SfxStyleFamily::Para) : nullptr)
+ rEditEngine.SetStyleSheet(nPara, static_cast<SfxStyleSheet*>(pStyle));
+}
+
+void SvxEditEngineForwarder::QuickInsertText( const OUString& rText, const ESelection& rSel )
+{
+ rEditEngine.QuickInsertText( rText, rSel );
+}
+
+void SvxEditEngineForwarder::QuickInsertLineBreak( const ESelection& rSel )
+{
+ rEditEngine.QuickInsertLineBreak( rSel );
+}
+
+void SvxEditEngineForwarder::QuickInsertField( const SvxFieldItem& rFld, const ESelection& rSel )
+{
+ rEditEngine.QuickInsertField( rFld, rSel );
+}
+
+void SvxEditEngineForwarder::QuickSetAttribs( const SfxItemSet& rSet, const ESelection& rSel )
+{
+ rEditEngine.QuickSetAttribs( rSet, rSel );
+}
+
+bool SvxEditEngineForwarder::IsValid() const
+{
+ // cannot reliably query EditEngine state
+ // while in the middle of an update
+ return rEditEngine.IsUpdateLayout();
+}
+
+OUString SvxEditEngineForwarder::CalcFieldValue( const SvxFieldItem& rField, sal_Int32 nPara, sal_Int32 nPos, std::optional<Color>& rpTxtColor, std::optional<Color>& rpFldColor, std::optional<FontLineStyle>& rpFldLineStyle )
+{
+ return rEditEngine.CalcFieldValue( rField, nPara, nPos, rpTxtColor, rpFldColor, rpFldLineStyle );
+}
+
+void SvxEditEngineForwarder::FieldClicked( const SvxFieldItem& rField )
+{
+ rEditEngine.FieldClicked( rField );
+}
+
+SfxItemState GetSvxEditEngineItemState( EditEngine const & rEditEngine, const ESelection& rSel, sal_uInt16 nWhich )
+{
+ std::vector<EECharAttrib> aAttribs;
+
+ const SfxPoolItem* pLastItem = nullptr;
+
+ SfxItemState eState = SfxItemState::DEFAULT;
+
+ // check all paragraphs inside the selection
+ for( sal_Int32 nPara = rSel.nStartPara; nPara <= rSel.nEndPara; nPara++ )
+ {
+ SfxItemState eParaState = SfxItemState::DEFAULT;
+
+ // calculate start and endpos for this paragraph
+ sal_Int32 nPos = 0;
+ if( rSel.nStartPara == nPara )
+ nPos = rSel.nStartPos;
+
+ sal_Int32 nEndPos = rSel.nEndPos;
+ if( rSel.nEndPara != nPara )
+ nEndPos = rEditEngine.GetTextLen( nPara );
+
+
+ // get list of char attribs
+ rEditEngine.GetCharAttribs( nPara, aAttribs );
+
+ bool bEmpty = true; // we found no item inside the selection of this paragraph
+ bool bGaps = false; // we found items but there are gaps between them
+ sal_Int32 nLastEnd = nPos;
+
+ const SfxPoolItem* pParaItem = nullptr;
+
+ for (auto const& attrib : aAttribs)
+ {
+ DBG_ASSERT(attrib.pAttr, "GetCharAttribs gives corrupt data");
+
+ const bool bEmptyPortion = attrib.nStart == attrib.nEnd;
+ if((!bEmptyPortion && attrib.nStart >= nEndPos) ||
+ (bEmptyPortion && attrib.nStart > nEndPos))
+ break; // break if we are already behind our selection
+
+ if((!bEmptyPortion && attrib.nEnd <= nPos) ||
+ (bEmptyPortion && attrib.nEnd < nPos))
+ continue; // or if the attribute ends before our selection
+
+ if(attrib.pAttr->Which() != nWhich)
+ continue; // skip if is not the searched item
+
+ // if we already found an item
+ if( pParaItem )
+ {
+ // ... and its different to this one than the state is don't care
+ if(*pParaItem != *(attrib.pAttr))
+ return SfxItemState::DONTCARE;
+ }
+ else
+ pParaItem = attrib.pAttr;
+
+ if( bEmpty )
+ bEmpty = false;
+
+ if(!bGaps && attrib.nStart > nLastEnd)
+ bGaps = true;
+
+ nLastEnd = attrib.nEnd;
+ }
+
+ if( !bEmpty && !bGaps && nLastEnd < ( nEndPos - 1 ) )
+ bGaps = true;
+
+ if( bEmpty )
+ eParaState = SfxItemState::DEFAULT;
+ else if( bGaps )
+ eParaState = SfxItemState::DONTCARE;
+ else
+ eParaState = SfxItemState::SET;
+
+ // if we already found an item check if we found the same
+ if( pLastItem )
+ {
+ if( (pParaItem == nullptr) || (*pLastItem != *pParaItem) )
+ return SfxItemState::DONTCARE;
+ }
+ else
+ {
+ pLastItem = pParaItem;
+ eState = eParaState;
+ }
+ }
+
+ return eState;
+}
+
+SfxItemState SvxEditEngineForwarder::GetItemState( const ESelection& rSel, sal_uInt16 nWhich ) const
+{
+ return GetSvxEditEngineItemState( rEditEngine, rSel, nWhich );
+}
+
+SfxItemState SvxEditEngineForwarder::GetItemState( sal_Int32 nPara, sal_uInt16 nWhich ) const
+{
+ const SfxItemSet& rSet = rEditEngine.GetParaAttribs( nPara );
+ return rSet.GetItemState( nWhich );
+}
+
+LanguageType SvxEditEngineForwarder::GetLanguage( sal_Int32 nPara, sal_Int32 nIndex ) const
+{
+ return rEditEngine.GetLanguage(nPara, nIndex).nLang;
+}
+
+sal_Int32 SvxEditEngineForwarder::GetFieldCount( sal_Int32 nPara ) const
+{
+ return rEditEngine.GetFieldCount(nPara);
+}
+
+EFieldInfo SvxEditEngineForwarder::GetFieldInfo( sal_Int32 nPara, sal_uInt16 nField ) const
+{
+ return rEditEngine.GetFieldInfo( nPara, nField );
+}
+
+EBulletInfo SvxEditEngineForwarder::GetBulletInfo( sal_Int32 ) const
+{
+ return EBulletInfo();
+}
+
+tools::Rectangle SvxEditEngineForwarder::GetCharBounds( sal_Int32 nPara, sal_Int32 nIndex ) const
+{
+ // EditEngine's 'internal' methods like GetCharacterBounds()
+ // don't rotate for vertical text.
+ Size aSize( rEditEngine.CalcTextWidth(), rEditEngine.GetTextHeight() );
+ // swap width and height
+ tools::Long tmp = aSize.Width();
+ aSize.setWidth(aSize.Height());
+ aSize.setHeight(tmp);
+ bool bIsVertical( rEditEngine.IsEffectivelyVertical() );
+
+ // #108900# Handle virtual position one-past-the end of the string
+ if( nIndex >= rEditEngine.GetTextLen(nPara) )
+ {
+ tools::Rectangle aLast;
+
+ if( nIndex )
+ {
+ // use last character, if possible
+ aLast = rEditEngine.GetCharacterBounds( EPosition(nPara, nIndex-1) );
+
+ // move at end of this last character, make one pixel wide
+ aLast.Move( aLast.Right() - aLast.Left(), 0 );
+ aLast.SetSize( Size(1, aLast.GetHeight()) );
+
+ // take care for CTL
+ aLast = SvxEditSourceHelper::EEToUserSpace( aLast, aSize, bIsVertical );
+ }
+ else
+ {
+ // #109864# Bounds must lie within the paragraph
+ aLast = GetParaBounds( nPara );
+
+ // #109151# Don't use paragraph height, but line height
+ // instead. aLast is already CTL-correct
+ if( bIsVertical)
+ aLast.SetSize( Size( rEditEngine.GetLineHeight(nPara), 1 ) );
+ else
+ aLast.SetSize( Size( 1, rEditEngine.GetLineHeight(nPara) ) );
+ }
+
+ return aLast;
+ }
+ else
+ {
+ return SvxEditSourceHelper::EEToUserSpace( rEditEngine.GetCharacterBounds( EPosition(nPara, nIndex) ),
+ aSize, bIsVertical );
+ }
+}
+
+tools::Rectangle SvxEditEngineForwarder::GetParaBounds( sal_Int32 nPara ) const
+{
+ const Point aPnt = rEditEngine.GetDocPosTopLeft( nPara );
+ sal_uInt32 nWidth;
+ sal_uInt32 nHeight;
+
+ if( rEditEngine.IsEffectivelyVertical() )
+ {
+ // Hargl. EditEngine's 'external' methods return the rotated
+ // dimensions, 'internal' methods like GetTextHeight( n )
+ // don't rotate.
+ nWidth = rEditEngine.GetTextHeight( nPara );
+ nHeight = rEditEngine.GetTextHeight();
+ sal_uInt32 nTextWidth = rEditEngine.GetTextHeight();
+
+ return tools::Rectangle( nTextWidth - aPnt.Y() - nWidth, 0, nTextWidth - aPnt.Y(), nHeight );
+ }
+ else
+ {
+ nWidth = rEditEngine.CalcTextWidth();
+ nHeight = rEditEngine.GetTextHeight( nPara );
+
+ return tools::Rectangle( 0, aPnt.Y(), nWidth, aPnt.Y() + nHeight );
+ }
+}
+
+MapMode SvxEditEngineForwarder::GetMapMode() const
+{
+ return rEditEngine.GetRefMapMode();
+}
+
+OutputDevice* SvxEditEngineForwarder::GetRefDevice() const
+{
+ return rEditEngine.GetRefDevice();
+}
+
+bool SvxEditEngineForwarder::GetIndexAtPoint( const Point& rPos, sal_Int32& nPara, sal_Int32& nIndex ) const
+{
+ Size aSize( rEditEngine.CalcTextWidth(), rEditEngine.GetTextHeight() );
+ // swap width and height
+ tools::Long tmp = aSize.Width();
+ aSize.setWidth(aSize.Height());
+ aSize.setHeight(tmp);
+ Point aEEPos( SvxEditSourceHelper::UserSpaceToEE( rPos,
+ aSize,
+ rEditEngine.IsEffectivelyVertical() ));
+
+ EPosition aDocPos = rEditEngine.FindDocPosition( aEEPos );
+
+ nPara = aDocPos.nPara;
+ nIndex = aDocPos.nIndex;
+
+ return true;
+}
+
+bool SvxEditEngineForwarder::GetWordIndices( sal_Int32 nPara, sal_Int32 nIndex, sal_Int32& nStart, sal_Int32& nEnd ) const
+{
+ ESelection aRes = rEditEngine.GetWord( ESelection(nPara, nIndex, nPara, nIndex), css::i18n::WordType::DICTIONARY_WORD );
+
+ if( aRes.nStartPara == nPara &&
+ aRes.nStartPara == aRes.nEndPara )
+ {
+ nStart = aRes.nStartPos;
+ nEnd = aRes.nEndPos;
+
+ return true;
+ }
+
+ return false;
+}
+
+bool SvxEditEngineForwarder::GetAttributeRun( sal_Int32& nStartIndex, sal_Int32& nEndIndex, sal_Int32 nPara, sal_Int32 nIndex, bool bInCell ) const
+{
+ SvxEditSourceHelper::GetAttributeRun( nStartIndex, nEndIndex, rEditEngine, nPara, nIndex, bInCell );
+ return true;
+}
+
+sal_Int32 SvxEditEngineForwarder::GetLineCount( sal_Int32 nPara ) const
+{
+ return rEditEngine.GetLineCount(nPara);
+}
+
+sal_Int32 SvxEditEngineForwarder::GetLineLen( sal_Int32 nPara, sal_Int32 nLine ) const
+{
+ return rEditEngine.GetLineLen(nPara, nLine);
+}
+
+void SvxEditEngineForwarder::GetLineBoundaries( /*out*/sal_Int32 &rStart, /*out*/sal_Int32 &rEnd, sal_Int32 nPara, sal_Int32 nLine ) const
+{
+ rEditEngine.GetLineBoundaries(rStart, rEnd, nPara, nLine);
+}
+
+sal_Int32 SvxEditEngineForwarder::GetLineNumberAtIndex( sal_Int32 nPara, sal_Int32 nIndex ) const
+{
+ return rEditEngine.GetLineNumberAtIndex(nPara, nIndex);
+}
+
+
+bool SvxEditEngineForwarder::QuickFormatDoc( bool )
+{
+ rEditEngine.QuickFormatDoc();
+
+ return true;
+}
+
+bool SvxEditEngineForwarder::Delete( const ESelection& rSelection )
+{
+ rEditEngine.QuickDelete( rSelection );
+ rEditEngine.QuickFormatDoc();
+
+ return true;
+}
+
+bool SvxEditEngineForwarder::InsertText( const OUString& rStr, const ESelection& rSelection )
+{
+ rEditEngine.QuickInsertText( rStr, rSelection );
+ rEditEngine.QuickFormatDoc();
+
+ return true;
+}
+
+sal_Int16 SvxEditEngineForwarder::GetDepth( sal_Int32 ) const
+{
+ // EditEngine does not support outline depth
+ return -1;
+}
+
+bool SvxEditEngineForwarder::SetDepth( sal_Int32, sal_Int16 nNewDepth )
+{
+ // EditEngine does not support outline depth
+ return nNewDepth == -1;
+}
+
+const SfxItemSet * SvxEditEngineForwarder::GetEmptyItemSetPtr()
+{
+ return &rEditEngine.GetEmptyItemSet();
+}
+
+void SvxEditEngineForwarder::AppendParagraph()
+{
+ rEditEngine.InsertParagraph( rEditEngine.GetParagraphCount(), OUString() );
+}
+
+sal_Int32 SvxEditEngineForwarder::AppendTextPortion( sal_Int32 nPara, const OUString &rText, const SfxItemSet & /*rSet*/ )
+{
+ sal_Int32 nLen = 0;
+
+ sal_Int32 nParaCount = rEditEngine.GetParagraphCount();
+ DBG_ASSERT( nPara < nParaCount, "paragraph index out of bounds" );
+ if (0 <= nPara && nPara < nParaCount)
+ {
+ nLen = rEditEngine.GetTextLen( nPara );
+ rEditEngine.QuickInsertText( rText, ESelection( nPara, nLen, nPara, nLen ) );
+ }
+
+ return nLen;
+}
+
+void SvxEditEngineForwarder::CopyText(const SvxTextForwarder& rSource)
+{
+ const SvxEditEngineForwarder* pSourceForwarder = dynamic_cast< const SvxEditEngineForwarder* >( &rSource );
+ if( !pSourceForwarder )
+ return;
+ std::unique_ptr<EditTextObject> pNewTextObject = pSourceForwarder->rEditEngine.CreateTextObject();
+ rEditEngine.SetText( *pNewTextObject );
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/uno/unofored_internal.hxx b/editeng/source/uno/unofored_internal.hxx
new file mode 100644
index 0000000000..cadb194159
--- /dev/null
+++ b/editeng/source/uno/unofored_internal.hxx
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <editeng/editeng.hxx>
+#include <svl/poolitem.hxx>
+
+SfxItemState GetSvxEditEngineItemState( EditEngine const & rEditEngine, const ESelection& rSel, sal_uInt16 nWhich );
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/uno/unoforou.cxx b/editeng/source/uno/unoforou.cxx
new file mode 100644
index 0000000000..8772ff9a77
--- /dev/null
+++ b/editeng/source/uno/unoforou.cxx
@@ -0,0 +1,582 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <osl/diagnose.h>
+#include <tools/debug.hxx>
+#include <svl/style.hxx>
+#include <com/sun/star/i18n/WordType.hpp>
+
+#include <svl/itemset.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editdata.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/unoedhlp.hxx>
+#include <svl/poolitem.hxx>
+
+#include <editeng/unoforou.hxx>
+#include <editeng/outlobj.hxx>
+#include "unofored_internal.hxx"
+
+using namespace ::com::sun::star;
+
+
+SvxOutlinerForwarder::SvxOutlinerForwarder( Outliner& rOutl, bool bOutlText /* = false */ ) :
+ rOutliner( rOutl ),
+ bOutlinerText( bOutlText ),
+ mnParaAttribsCache( 0 )
+{
+}
+
+SvxOutlinerForwarder::~SvxOutlinerForwarder()
+{
+ flushCache();
+}
+
+sal_Int32 SvxOutlinerForwarder::GetParagraphCount() const
+{
+ return rOutliner.GetParagraphCount();
+}
+
+sal_Int32 SvxOutlinerForwarder::GetTextLen( sal_Int32 nParagraph ) const
+{
+ return rOutliner.GetEditEngine().GetTextLen( nParagraph );
+}
+
+OUString SvxOutlinerForwarder::GetText( const ESelection& rSel ) const
+{
+ //! GetText (ESelection) should probably also be in the Outliner
+ // in the time being use as the hack for the EditEngine:
+ EditEngine* pEditEngine = const_cast<EditEngine*>(&rOutliner.GetEditEngine());
+ return pEditEngine->GetText( rSel );
+}
+
+static SfxItemSet ImplOutlinerForwarderGetAttribs( const ESelection& rSel, EditEngineAttribs nOnlyHardAttrib, EditEngine& rEditEngine )
+{
+ if( rSel.nStartPara == rSel.nEndPara )
+ {
+ GetAttribsFlags nFlags = GetAttribsFlags::NONE;
+
+ switch( nOnlyHardAttrib )
+ {
+ case EditEngineAttribs::All:
+ nFlags = GetAttribsFlags::ALL;
+ break;
+ case EditEngineAttribs::OnlyHard:
+ nFlags = GetAttribsFlags::CHARATTRIBS;
+ break;
+ default:
+ OSL_FAIL("unknown flags for SvxOutlinerForwarder::GetAttribs");
+ }
+ return rEditEngine.GetAttribs( rSel.nStartPara, rSel.nStartPos, rSel.nEndPos, nFlags );
+ }
+ else
+ {
+ return rEditEngine.GetAttribs( rSel, nOnlyHardAttrib );
+ }
+}
+
+SfxItemSet SvxOutlinerForwarder::GetAttribs( const ESelection& rSel, EditEngineAttribs nOnlyHardAttrib ) const
+{
+ if( moAttribsCache && ( EditEngineAttribs::All == nOnlyHardAttrib ) )
+ {
+ // have we the correct set in cache?
+ if( maAttribCacheSelection == rSel )
+ {
+ // yes! just return the cache
+ return *moAttribsCache;
+ }
+ else
+ {
+ // no, we need delete the old cache
+ moAttribsCache.reset();
+ }
+ }
+
+ //! Does it not exist on the Outliner?
+ //! and why is the GetAttribs on the EditEngine not a const?
+ EditEngine& rEditEngine = const_cast<EditEngine&>(rOutliner.GetEditEngine());
+
+ SfxItemSet aSet( ImplOutlinerForwarderGetAttribs( rSel, nOnlyHardAttrib, rEditEngine ) );
+
+ if( EditEngineAttribs::All == nOnlyHardAttrib )
+ {
+ moAttribsCache.emplace( aSet );
+ maAttribCacheSelection = rSel;
+ }
+
+ SfxStyleSheet* pStyle = rEditEngine.GetStyleSheet( rSel.nStartPara );
+ if( pStyle )
+ aSet.SetParent( &(pStyle->GetItemSet() ) );
+
+ return aSet;
+}
+
+SfxItemSet SvxOutlinerForwarder::GetParaAttribs( sal_Int32 nPara ) const
+{
+ if( moParaAttribsCache )
+ {
+ // have we the correct set in cache?
+ if( nPara == mnParaAttribsCache )
+ {
+ // yes! just return the cache
+ return *moParaAttribsCache;
+ }
+ else
+ {
+ // no, we need delete the old cache
+ moParaAttribsCache.reset();
+ }
+ }
+
+ moParaAttribsCache.emplace( rOutliner.GetParaAttribs( nPara ) );
+ mnParaAttribsCache = nPara;
+
+ EditEngine& rEditEngine = const_cast<EditEngine&>(rOutliner.GetEditEngine());
+
+ SfxStyleSheet* pStyle = rEditEngine.GetStyleSheet( nPara );
+ if( pStyle )
+ moParaAttribsCache->SetParent( &(pStyle->GetItemSet() ) );
+
+ return *moParaAttribsCache;
+}
+
+void SvxOutlinerForwarder::SetParaAttribs( sal_Int32 nPara, const SfxItemSet& rSet )
+{
+ flushCache();
+
+ const SfxItemSet* pOldParent = rSet.GetParent();
+ if( pOldParent )
+ const_cast<SfxItemSet*>(&rSet)->SetParent( nullptr );
+
+ rOutliner.SetParaAttribs( nPara, rSet );
+
+ if( pOldParent )
+ const_cast<SfxItemSet*>(&rSet)->SetParent( pOldParent );
+}
+
+void SvxOutlinerForwarder::RemoveAttribs( const ESelection& rSelection )
+{
+ rOutliner.RemoveAttribs( rSelection, false/*bRemoveParaAttribs*/, 0 );
+}
+
+SfxItemPool* SvxOutlinerForwarder::GetPool() const
+{
+ return rOutliner.GetEmptyItemSet().GetPool();
+}
+
+void SvxOutlinerForwarder::GetPortions( sal_Int32 nPara, std::vector<sal_Int32>& rList ) const
+{
+ const_cast<EditEngine&>(rOutliner.GetEditEngine()).GetPortions( nPara, rList );
+}
+
+OUString SvxOutlinerForwarder::GetStyleSheet(sal_Int32 nPara) const
+{
+ if (auto pStyle = rOutliner.GetStyleSheet(nPara))
+ return pStyle->GetName();
+ return OUString();
+}
+
+void SvxOutlinerForwarder::SetStyleSheet(sal_Int32 nPara, const OUString& rStyleName)
+{
+ auto pStyleSheetPool = rOutliner.GetStyleSheetPool();
+ if (auto pStyle = pStyleSheetPool ? pStyleSheetPool->Find(rStyleName, SfxStyleFamily::Para) : nullptr)
+ rOutliner.SetStyleSheet(nPara, static_cast<SfxStyleSheet*>(pStyle));
+}
+
+void SvxOutlinerForwarder::QuickInsertText( const OUString& rText, const ESelection& rSel )
+{
+ flushCache();
+ if( rText.isEmpty() )
+ {
+ rOutliner.QuickDelete( rSel );
+ }
+ else
+ {
+ rOutliner.QuickInsertText( rText, rSel );
+ }
+}
+
+void SvxOutlinerForwarder::QuickInsertLineBreak( const ESelection& rSel )
+{
+ flushCache();
+ rOutliner.QuickInsertLineBreak( rSel );
+}
+
+void SvxOutlinerForwarder::QuickInsertField( const SvxFieldItem& rFld, const ESelection& rSel )
+{
+ flushCache();
+ rOutliner.QuickInsertField( rFld, rSel );
+}
+
+void SvxOutlinerForwarder::QuickSetAttribs( const SfxItemSet& rSet, const ESelection& rSel )
+{
+ flushCache();
+ rOutliner.QuickSetAttribs( rSet, rSel );
+}
+
+OUString SvxOutlinerForwarder::CalcFieldValue( const SvxFieldItem& rField, sal_Int32 nPara, sal_Int32 nPos, std::optional<Color>& rpTxtColor, std::optional<Color>& rpFldColor, std::optional<FontLineStyle>& rpFldLineStyle )
+{
+ return rOutliner.CalcFieldValue( rField, nPara, nPos, rpTxtColor, rpFldColor, rpFldLineStyle );
+}
+
+void SvxOutlinerForwarder::FieldClicked( const SvxFieldItem& /*rField*/ )
+{
+}
+
+bool SvxOutlinerForwarder::IsValid() const
+{
+ // cannot reliably query outliner state
+ // while in the middle of an update
+ return rOutliner.IsUpdateLayout();
+}
+
+SfxItemState SvxOutlinerForwarder::GetItemState( const ESelection& rSel, sal_uInt16 nWhich ) const
+{
+ return GetSvxEditEngineItemState( rOutliner.GetEditEngine(), rSel, nWhich );
+}
+
+SfxItemState SvxOutlinerForwarder::GetItemState( sal_Int32 nPara, sal_uInt16 nWhich ) const
+{
+ const SfxItemSet& rSet = rOutliner.GetParaAttribs( nPara );
+ return rSet.GetItemState( nWhich );
+}
+
+
+void SvxOutlinerForwarder::flushCache()
+{
+ moAttribsCache.reset();
+ moParaAttribsCache.reset();
+}
+
+LanguageType SvxOutlinerForwarder::GetLanguage( sal_Int32 nPara, sal_Int32 nIndex ) const
+{
+ return rOutliner.GetLanguage(nPara, nIndex);
+}
+
+sal_Int32 SvxOutlinerForwarder::GetFieldCount( sal_Int32 nPara ) const
+{
+ return rOutliner.GetEditEngine().GetFieldCount(nPara);
+}
+
+EFieldInfo SvxOutlinerForwarder::GetFieldInfo( sal_Int32 nPara, sal_uInt16 nField ) const
+{
+ return rOutliner.GetEditEngine().GetFieldInfo( nPara, nField );
+}
+
+EBulletInfo SvxOutlinerForwarder::GetBulletInfo( sal_Int32 nPara ) const
+{
+ return rOutliner.GetBulletInfo( nPara );
+}
+
+tools::Rectangle SvxOutlinerForwarder::GetCharBounds( sal_Int32 nPara, sal_Int32 nIndex ) const
+{
+ // EditEngine's 'internal' methods like GetCharacterBounds()
+ // don't rotate for vertical text.
+ Size aSize( rOutliner.CalcTextSize() );
+ // swap width and height
+ tools::Long tmp = aSize.Width();
+ aSize.setWidth(aSize.Height());
+ aSize.setHeight(tmp);
+ bool bIsVertical( rOutliner.IsVertical() );
+
+ // #108900# Handle virtual position one-past-the end of the string
+ if( nIndex >= GetTextLen(nPara) )
+ {
+ tools::Rectangle aLast;
+
+ if( nIndex )
+ {
+ // use last character, if possible
+ aLast = rOutliner.GetEditEngine().GetCharacterBounds( EPosition(nPara, nIndex-1) );
+
+ // move at end of this last character, make one pixel wide
+ aLast.Move( aLast.Right() - aLast.Left(), 0 );
+ aLast.SetSize( Size(1, aLast.GetHeight()) );
+
+ // take care for CTL
+ aLast = SvxEditSourceHelper::EEToUserSpace( aLast, aSize, bIsVertical );
+ }
+ else
+ {
+ // #109864# Bounds must lie within the paragraph
+ aLast = GetParaBounds( nPara );
+
+ // #109151# Don't use paragraph height, but line height
+ // instead. aLast is already CTL-correct
+ if( bIsVertical)
+ aLast.SetSize( Size( rOutliner.GetLineHeight(nPara), 1 ) );
+ else
+ aLast.SetSize( Size( 1, rOutliner.GetLineHeight(nPara) ) );
+ }
+
+ return aLast;
+ }
+ else
+ {
+ return SvxEditSourceHelper::EEToUserSpace( rOutliner.GetEditEngine().GetCharacterBounds( EPosition(nPara, nIndex) ),
+ aSize, bIsVertical );
+ }
+}
+
+tools::Rectangle SvxOutlinerForwarder::GetParaBounds( sal_Int32 nPara ) const
+{
+ return rOutliner.GetParaBounds( nPara );
+}
+
+MapMode SvxOutlinerForwarder::GetMapMode() const
+{
+ return rOutliner.GetRefMapMode();
+}
+
+OutputDevice* SvxOutlinerForwarder::GetRefDevice() const
+{
+ return rOutliner.GetRefDevice();
+}
+
+bool SvxOutlinerForwarder::GetIndexAtPoint( const Point& rPos, sal_Int32& nPara, sal_Int32& nIndex ) const
+{
+ Size aSize( rOutliner.CalcTextSize() );
+ // swap width and height
+ tools::Long tmp = aSize.Width();
+ aSize.setWidth(aSize.Height());
+ aSize.setHeight(tmp);
+ Point aEEPos( SvxEditSourceHelper::UserSpaceToEE( rPos,
+ aSize,
+ rOutliner.IsVertical() ));
+
+ EPosition aDocPos = rOutliner.GetEditEngine().FindDocPosition( aEEPos );
+
+ nPara = aDocPos.nPara;
+ nIndex = aDocPos.nIndex;
+
+ return true;
+}
+
+bool SvxOutlinerForwarder::GetWordIndices( sal_Int32 nPara, sal_Int32 nIndex, sal_Int32& nStart, sal_Int32& nEnd ) const
+{
+ ESelection aRes = rOutliner.GetEditEngine().GetWord( ESelection(nPara, nIndex, nPara, nIndex), css::i18n::WordType::DICTIONARY_WORD );
+
+ if( aRes.nStartPara == nPara &&
+ aRes.nStartPara == aRes.nEndPara )
+ {
+ nStart = aRes.nStartPos;
+ nEnd = aRes.nEndPos;
+
+ return true;
+ }
+
+ return false;
+}
+
+bool SvxOutlinerForwarder::GetAttributeRun( sal_Int32& nStartIndex, sal_Int32& nEndIndex, sal_Int32 nPara, sal_Int32 nIndex, bool bInCell ) const
+{
+ SvxEditSourceHelper::GetAttributeRun( nStartIndex, nEndIndex, rOutliner.GetEditEngine(), nPara, nIndex, bInCell );
+ return true;
+}
+
+sal_Int32 SvxOutlinerForwarder::GetLineCount( sal_Int32 nPara ) const
+{
+ return rOutliner.GetLineCount(nPara);
+}
+
+sal_Int32 SvxOutlinerForwarder::GetLineLen( sal_Int32 nPara, sal_Int32 nLine ) const
+{
+ return rOutliner.GetLineLen(nPara, nLine);
+}
+
+void SvxOutlinerForwarder::GetLineBoundaries( /*out*/sal_Int32 &rStart, /*out*/sal_Int32 &rEnd, sal_Int32 nPara, sal_Int32 nLine ) const
+{
+ return rOutliner.GetEditEngine().GetLineBoundaries( rStart, rEnd, nPara, nLine );
+}
+
+sal_Int32 SvxOutlinerForwarder::GetLineNumberAtIndex( sal_Int32 nPara, sal_Int32 nIndex ) const
+{
+ return rOutliner.GetEditEngine().GetLineNumberAtIndex( nPara, nIndex );
+}
+
+bool SvxOutlinerForwarder::QuickFormatDoc( bool )
+{
+ rOutliner.QuickFormatDoc();
+
+ return true;
+}
+
+bool SvxOutlinerForwarder::Delete( const ESelection& rSelection )
+{
+ flushCache();
+ rOutliner.QuickDelete( rSelection );
+ rOutliner.QuickFormatDoc();
+
+ return true;
+}
+
+bool SvxOutlinerForwarder::InsertText( const OUString& rStr, const ESelection& rSelection )
+{
+ flushCache();
+ rOutliner.QuickInsertText( rStr, rSelection );
+ rOutliner.QuickFormatDoc();
+
+ return true;
+}
+
+sal_Int16 SvxOutlinerForwarder::GetDepth( sal_Int32 nPara ) const
+{
+ DBG_ASSERT( 0 <= nPara && nPara < GetParagraphCount(), "SvxOutlinerForwarder::GetDepth: Invalid paragraph index");
+
+ Paragraph* pPara = rOutliner.GetParagraph( nPara );
+
+ sal_Int16 nLevel = -1;
+
+ if( pPara )
+ nLevel = rOutliner.GetDepth( nPara );
+
+ return nLevel;
+}
+
+bool SvxOutlinerForwarder::SetDepth( sal_Int32 nPara, sal_Int16 nNewDepth )
+{
+ DBG_ASSERT( 0 <= nPara && nPara < GetParagraphCount(), "SvxOutlinerForwarder::SetDepth: Invalid paragraph index");
+
+ if( (nNewDepth >= -1) && (nNewDepth <= 9) && (0 <= nPara && nPara < GetParagraphCount()) )
+ {
+ Paragraph* pPara = rOutliner.GetParagraph( nPara );
+ if( pPara )
+ {
+ rOutliner.SetDepth( pPara, nNewDepth );
+
+// const bool bOutlinerText = pSdrObject && (pSdrObject->GetObjInventor() == SdrInventor::Default) && (pSdrObject->GetObjIdentifier() == OBJ_OUTLINETEXT);
+ if( bOutlinerText )
+ rOutliner.SetLevelDependentStyleSheet( nPara );
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+sal_Int32 SvxOutlinerForwarder::GetNumberingStartValue( sal_Int32 nPara )
+{
+ if( 0 <= nPara && nPara < GetParagraphCount() )
+ {
+ return rOutliner.GetNumberingStartValue( nPara );
+ }
+ else
+ {
+ OSL_FAIL( "SvxOutlinerForwarder::GetNumberingStartValue)(), Invalid paragraph index");
+ return -1;
+ }
+}
+
+void SvxOutlinerForwarder::SetNumberingStartValue( sal_Int32 nPara, sal_Int32 nNumberingStartValue )
+{
+ if( 0 <= nPara && nPara < GetParagraphCount() )
+ {
+ rOutliner.SetNumberingStartValue( nPara, nNumberingStartValue );
+ }
+ else
+ {
+ OSL_FAIL( "SvxOutlinerForwarder::SetNumberingStartValue)(), Invalid paragraph index");
+ }
+}
+
+bool SvxOutlinerForwarder::IsParaIsNumberingRestart( sal_Int32 nPara )
+{
+ if( 0 <= nPara && nPara < GetParagraphCount() )
+ {
+ return rOutliner.IsParaIsNumberingRestart( nPara );
+ }
+ else
+ {
+ OSL_FAIL( "SvxOutlinerForwarder::IsParaIsNumberingRestart)(), Invalid paragraph index");
+ return false;
+ }
+}
+
+void SvxOutlinerForwarder::SetParaIsNumberingRestart( sal_Int32 nPara, bool bParaIsNumberingRestart )
+{
+ if( 0 <= nPara && nPara < GetParagraphCount() )
+ {
+ rOutliner.SetParaIsNumberingRestart( nPara, bParaIsNumberingRestart );
+ }
+ else
+ {
+ OSL_FAIL( "SvxOutlinerForwarder::SetParaIsNumberingRestart)(), Invalid paragraph index");
+ }
+}
+
+const SfxItemSet * SvxOutlinerForwarder::GetEmptyItemSetPtr()
+{
+ EditEngine& rEditEngine = const_cast< EditEngine& >( rOutliner.GetEditEngine() );
+ return &rEditEngine.GetEmptyItemSet();
+}
+
+void SvxOutlinerForwarder::AppendParagraph()
+{
+ EditEngine& rEditEngine = const_cast< EditEngine& >( rOutliner.GetEditEngine() );
+ rEditEngine.InsertParagraph( rEditEngine.GetParagraphCount(), OUString() );
+}
+
+sal_Int32 SvxOutlinerForwarder::AppendTextPortion( sal_Int32 nPara, const OUString &rText, const SfxItemSet & /*rSet*/ )
+{
+ sal_Int32 nLen = 0;
+
+ EditEngine& rEditEngine = const_cast< EditEngine& >( rOutliner.GetEditEngine() );
+ sal_Int32 nParaCount = rEditEngine.GetParagraphCount();
+ DBG_ASSERT( 0 <= nPara && nPara < nParaCount, "paragraph index out of bounds" );
+ if (0 <= nPara && nPara < nParaCount)
+ {
+ nLen = rEditEngine.GetTextLen( nPara );
+ rEditEngine.QuickInsertText( rText, ESelection( nPara, nLen, nPara, nLen ) );
+ }
+
+ return nLen;
+}
+
+void SvxOutlinerForwarder::CopyText(const SvxTextForwarder& rSource)
+{
+ const SvxOutlinerForwarder* pSourceForwarder = dynamic_cast< const SvxOutlinerForwarder* >( &rSource );
+ if( !pSourceForwarder )
+ return;
+ std::optional<OutlinerParaObject> pNewOutlinerParaObject = pSourceForwarder->rOutliner.CreateParaObject();
+ rOutliner.SetText( *pNewOutlinerParaObject );
+}
+
+
+sal_Int32 SvxTextForwarder::GetNumberingStartValue( sal_Int32 )
+{
+ return -1;
+}
+
+void SvxTextForwarder::SetNumberingStartValue( sal_Int32, sal_Int32 )
+{
+}
+
+bool SvxTextForwarder::IsParaIsNumberingRestart( sal_Int32 )
+{
+ return false;
+}
+
+void SvxTextForwarder::SetParaIsNumberingRestart( sal_Int32, bool )
+{
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/uno/unoipset.cxx b/editeng/source/uno/unoipset.cxx
new file mode 100644
index 0000000000..4a4dd9f5e5
--- /dev/null
+++ b/editeng/source/uno/unoipset.cxx
@@ -0,0 +1,333 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <svl/itemprop.hxx>
+#include <tools/UnitConversion.hxx>
+#include <editeng/unoipset.hxx>
+#include <svl/itempool.hxx>
+#include <svl/solar.hrc>
+#include <o3tl/any.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <algorithm>
+
+using namespace ::com::sun::star;
+
+
+SvxItemPropertySet::SvxItemPropertySet( std::span<const SfxItemPropertyMapEntry> pMap, SfxItemPool& rItemPool )
+: m_aPropertyMap( pMap ),
+ mrItemPool( rItemPool )
+{
+}
+
+
+SvxItemPropertySet::~SvxItemPropertySet()
+{
+}
+
+
+static bool SvxUnoCheckForPositiveValue( const uno::Any& rVal )
+{
+ bool bConvert = true; // the default is that all metric items must be converted
+ sal_Int32 nValue = 0;
+ if( rVal >>= nValue )
+ bConvert = (nValue > 0);
+ return bConvert;
+}
+
+
+uno::Any SvxItemPropertySet::getPropertyValue( const SfxItemPropertyMapEntry* pMap, const SfxItemSet& rSet, bool bSearchInParent, bool bDontConvertNegativeValues )
+{
+ uno::Any aVal;
+ if(!pMap || !pMap->nWID)
+ return aVal;
+
+ const SfxPoolItem* pItem = nullptr;
+ SfxItemPool* pPool = rSet.GetPool();
+ (void)rSet.GetItemState( pMap->nWID, bSearchInParent, &pItem );
+ if( nullptr == pItem && pPool )
+ pItem = &(pPool->GetDefaultItem( pMap->nWID ));
+
+ const MapUnit eMapUnit = pPool ? pPool->GetMetric(pMap->nWID) : MapUnit::Map100thMM;
+ sal_uInt8 nMemberId = pMap->nMemberId;
+ if( eMapUnit == MapUnit::Map100thMM )
+ nMemberId &= (~CONVERT_TWIPS);
+
+ if(pItem)
+ {
+ pItem->QueryValue( aVal, nMemberId );
+ if( pMap->nMoreFlags & PropertyMoreFlags::METRIC_ITEM )
+ {
+ if( eMapUnit != MapUnit::Map100thMM )
+ {
+ if ( !bDontConvertNegativeValues || SvxUnoCheckForPositiveValue( aVal ) )
+ SvxUnoConvertToMM( eMapUnit, aVal );
+ }
+ }
+ else if ( pMap->aType.getTypeClass() == uno::TypeClass_ENUM &&
+ aVal.getValueType() == ::cppu::UnoType<sal_Int32>::get() )
+ {
+ // convert typeless SfxEnumItem to enum type
+ sal_Int32 nEnum;
+ aVal >>= nEnum;
+ aVal.setValue( &nEnum, pMap->aType );
+ }
+ }
+ else
+ {
+ OSL_FAIL( "No SfxPoolItem found for property!" );
+ }
+
+ return aVal;
+}
+
+
+void SvxItemPropertySet::setPropertyValue( const SfxItemPropertyMapEntry* pMap, const uno::Any& rVal, SfxItemSet& rSet, bool bDontConvertNegativeValues )
+{
+ if(!pMap || !pMap->nWID)
+ return;
+
+ // Get item
+ const SfxPoolItem* pItem = nullptr;
+ SfxItemState eState = rSet.GetItemState( pMap->nWID, true, &pItem );
+ SfxItemPool* pPool = rSet.GetPool();
+
+ // Put UnoAny in the item value
+ if(eState < SfxItemState::DEFAULT || pItem == nullptr)
+ {
+ if( pPool == nullptr )
+ {
+ OSL_FAIL( "No default item and no pool?" );
+ return;
+ }
+
+ pItem = &pPool->GetDefaultItem( pMap->nWID );
+ }
+
+ uno::Any aValue(rVal);
+
+ const MapUnit eMapUnit = pPool ? pPool->GetMetric(pMap->nWID) : MapUnit::Map100thMM;
+
+ // check for needed metric translation
+ if ((pMap->nMoreFlags & PropertyMoreFlags::METRIC_ITEM) && eMapUnit != MapUnit::Map100thMM)
+ {
+ if (!bDontConvertNegativeValues || SvxUnoCheckForPositiveValue(aValue))
+ SvxUnoConvertFromMM(eMapUnit, aValue);
+ }
+
+ std::unique_ptr<SfxPoolItem> pNewItem(pItem->Clone());
+
+ sal_uInt8 nMemberId = pMap->nMemberId;
+ if (eMapUnit == MapUnit::Map100thMM)
+ nMemberId &= (~CONVERT_TWIPS);
+
+ if (pNewItem->PutValue(aValue, nMemberId))
+ {
+ // Set new item in item set
+ pNewItem->SetWhich(pMap->nWID);
+ rSet.Put(std::move(pNewItem));
+ }
+}
+
+
+uno::Any SvxItemPropertySet::getPropertyValue( const SfxItemPropertyMapEntry* pMap, SvxItemPropertySetUsrAnys& rAnys ) const
+{
+ // Already entered a value? Then finish quickly
+ uno::Any* pUsrAny = rAnys.GetUsrAnyForID(*pMap);
+ if(pUsrAny)
+ return *pUsrAny;
+
+ // No UsrAny detected yet, generate Default entry and return this
+ const MapUnit eMapUnit = mrItemPool.GetMetric(pMap->nWID);
+ sal_uInt8 nMemberId = pMap->nMemberId;
+ if( eMapUnit == MapUnit::Map100thMM )
+ nMemberId &= (~CONVERT_TWIPS);
+ uno::Any aVal;
+ SfxItemSet aSet( mrItemPool, pMap->nWID, pMap->nWID);
+
+ if( (pMap->nWID < OWN_ATTR_VALUE_START) || (pMap->nWID > OWN_ATTR_VALUE_END ) )
+ {
+ // Get Default from ItemPool
+ if(SfxItemPool::IsWhich(pMap->nWID))
+ aSet.Put(mrItemPool.GetDefaultItem(pMap->nWID));
+ }
+
+ if(aSet.Count())
+ {
+ const SfxPoolItem* pItem = nullptr;
+ SfxItemState eState = aSet.GetItemState( pMap->nWID, true, &pItem );
+ if(eState >= SfxItemState::DEFAULT && pItem)
+ {
+ pItem->QueryValue( aVal, nMemberId );
+ rAnys.AddUsrAnyForID(aVal, *pMap);
+ }
+ }
+
+ // check for needed metric translation
+ if(pMap->nMoreFlags & PropertyMoreFlags::METRIC_ITEM && eMapUnit != MapUnit::Map100thMM)
+ {
+ SvxUnoConvertToMM( eMapUnit, aVal );
+ }
+
+ if ( pMap->aType.getTypeClass() == uno::TypeClass_ENUM &&
+ aVal.getValueType() == ::cppu::UnoType<sal_Int32>::get() )
+ {
+ sal_Int32 nEnum;
+ aVal >>= nEnum;
+
+ aVal.setValue( &nEnum, pMap->aType );
+ }
+
+ return aVal;
+}
+
+
+void SvxItemPropertySet::setPropertyValue( const SfxItemPropertyMapEntry* pMap, const uno::Any& rVal, SvxItemPropertySetUsrAnys& rAnys )
+{
+ uno::Any* pUsrAny = rAnys.GetUsrAnyForID(*pMap);
+ if(!pUsrAny)
+ rAnys.AddUsrAnyForID(rVal, *pMap);
+ else
+ *pUsrAny = rVal;
+}
+
+
+const SfxItemPropertyMapEntry* SvxItemPropertySet::getPropertyMapEntry(std::u16string_view rName) const
+{
+ return m_aPropertyMap.getByName( rName );
+ }
+
+
+uno::Reference< beans::XPropertySetInfo > const & SvxItemPropertySet::getPropertySetInfo() const
+{
+ if( !m_xInfo.is() )
+ m_xInfo = new SfxItemPropertySetInfo( m_aPropertyMap );
+ return m_xInfo;
+}
+
+
+/** converts the given any with a metric to 100th/mm if needed */
+void SvxUnoConvertToMM( const MapUnit eSourceMapUnit, uno::Any & rMetric ) noexcept
+{
+ // map the metric of the itempool to 100th mm
+ switch(eSourceMapUnit)
+ {
+ case MapUnit::MapTwip :
+ {
+ switch( rMetric.getValueTypeClass() )
+ {
+ case uno::TypeClass_BYTE:
+ rMetric <<= static_cast<sal_Int8>(convertTwipToMm100(*o3tl::forceAccess<sal_Int8>(rMetric)));
+ break;
+ case uno::TypeClass_SHORT:
+ rMetric <<= static_cast<sal_Int16>(convertTwipToMm100(*o3tl::forceAccess<sal_Int16>(rMetric)));
+ break;
+ case uno::TypeClass_UNSIGNED_SHORT:
+ rMetric <<= static_cast<sal_uInt16>(convertTwipToMm100(*o3tl::forceAccess<sal_uInt16>(rMetric)));
+ break;
+ case uno::TypeClass_LONG:
+ rMetric <<= static_cast<sal_Int32>(convertTwipToMm100(*o3tl::forceAccess<sal_Int32>(rMetric)));
+ break;
+ case uno::TypeClass_UNSIGNED_LONG:
+ rMetric <<= static_cast<sal_uInt32>(convertTwipToMm100(*o3tl::forceAccess<sal_uInt32>(rMetric)));
+ break;
+ default:
+ SAL_WARN("editeng", "AW: Missing unit translation to 100th mm, " << OString::number(static_cast<sal_Int32>(rMetric.getValueTypeClass())));
+ assert(false);
+ }
+ break;
+ }
+ default:
+ {
+ OSL_FAIL("AW: Missing unit translation to 100th mm!");
+ }
+ }
+}
+
+
+/** converts the given any with a metric from 100th/mm to the given metric if needed */
+void SvxUnoConvertFromMM( const MapUnit eDestinationMapUnit, uno::Any & rMetric ) noexcept
+{
+ switch(eDestinationMapUnit)
+ {
+ case MapUnit::MapTwip :
+ {
+ switch( rMetric.getValueTypeClass() )
+ {
+ case uno::TypeClass_BYTE:
+ rMetric <<= static_cast<sal_Int8>(sanitiseMm100ToTwip(*o3tl::forceAccess<sal_Int8>(rMetric)));
+ break;
+ case uno::TypeClass_SHORT:
+ rMetric <<= static_cast<sal_Int16>(sanitiseMm100ToTwip(*o3tl::forceAccess<sal_Int16>(rMetric)));
+ break;
+ case uno::TypeClass_UNSIGNED_SHORT:
+ rMetric <<= static_cast<sal_uInt16>(sanitiseMm100ToTwip(*o3tl::forceAccess<sal_uInt16>(rMetric)));
+ break;
+ case uno::TypeClass_LONG:
+ rMetric <<= static_cast<sal_Int32>(sanitiseMm100ToTwip(*o3tl::forceAccess<sal_Int32>(rMetric)));
+ break;
+ case uno::TypeClass_UNSIGNED_LONG:
+ rMetric <<= static_cast<sal_uInt32>(sanitiseMm100ToTwip(*o3tl::forceAccess<sal_uInt32>(rMetric)));
+ break;
+ default:
+ OSL_FAIL("AW: Missing unit translation to 100th mm!");
+ }
+ break;
+ }
+ default:
+ {
+ OSL_FAIL("AW: Missing unit translation to PoolMetrics!");
+ }
+ }
+}
+
+SvxItemPropertySetUsrAnys::SvxItemPropertySetUsrAnys() = default;
+
+SvxItemPropertySetUsrAnys::~SvxItemPropertySetUsrAnys()
+{
+ ClearAllUsrAny();
+}
+
+uno::Any* SvxItemPropertySetUsrAnys::GetUsrAnyForID(SfxItemPropertyMapEntry const & entry) const
+{
+ for (auto const & rActual : aCombineList)
+ {
+ if( rActual.nWID == entry.nWID && rActual.memberId == entry.nMemberId )
+ return const_cast<uno::Any*>(&rActual.aAny);
+ }
+ return nullptr;
+}
+
+void SvxItemPropertySetUsrAnys::AddUsrAnyForID(
+ const uno::Any& rAny, SfxItemPropertyMapEntry const & entry)
+{
+ SvxIDPropertyCombine aNew;
+ aNew.nWID = entry.nWID;
+ aNew.memberId = entry.nMemberId;
+ aNew.aAny = rAny;
+ aCombineList.push_back( std::move(aNew) );
+}
+
+void SvxItemPropertySetUsrAnys::ClearAllUsrAny()
+{
+ aCombineList.clear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/uno/unonrule.cxx b/editeng/source/uno/unonrule.cxx
new file mode 100644
index 0000000000..5083f4d8e8
--- /dev/null
+++ b/editeng/source/uno/unonrule.cxx
@@ -0,0 +1,544 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/awt/FontDescriptor.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/text/HoriOrientation.hpp>
+#include <com/sun/star/awt/XBitmap.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/implbase1.hxx>
+#include <utility>
+#include <vcl/font.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/GraphicObject.hxx>
+#include <vcl/GraphicLoader.hxx>
+#include <tools/debug.hxx>
+
+#include <editeng/brushitem.hxx>
+#include <editeng/unoprnms.hxx>
+#include <editeng/numitem.hxx>
+#include <editeng/unofdesc.hxx>
+#include <editeng/unonrule.hxx>
+#include <editeng/editids.hrc>
+#include <o3tl/enumarray.hxx>
+#include <o3tl/temporary.hxx>
+#include <memory>
+
+using ::com::sun::star::util::XCloneable;
+using ::com::sun::star::ucb::XAnyCompare;
+
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+
+const SvxAdjust aUnoToSvxAdjust[] =
+{
+ SvxAdjust::Left,
+ SvxAdjust::Right,
+ SvxAdjust::Center,
+ SvxAdjust::Left,
+ SvxAdjust::Left,
+ SvxAdjust::Left,
+ SvxAdjust::Block
+};
+
+const o3tl::enumarray<SvxAdjust, sal_Int16> aSvxToUnoAdjust
+{
+ text::HoriOrientation::LEFT,
+ text::HoriOrientation::RIGHT,
+ text::HoriOrientation::FULL,
+ text::HoriOrientation::CENTER,
+ text::HoriOrientation::FULL,
+ text::HoriOrientation::LEFT
+};
+
+static SvxAdjust ConvertUnoAdjust( unsigned short nAdjust )
+{
+ DBG_ASSERT( nAdjust <= 7, "Enum has changed! [CL]" );
+ return aUnoToSvxAdjust[nAdjust];
+}
+
+static unsigned short ConvertUnoAdjust( SvxAdjust eAdjust )
+{
+ DBG_ASSERT( static_cast<int>(eAdjust) <= 6, "Enum has changed! [CL]" );
+ return aSvxToUnoAdjust[eAdjust];
+}
+
+SvxUnoNumberingRules::SvxUnoNumberingRules(SvxNumRule aRule)
+: maRule(std::move( aRule ))
+{
+}
+
+SvxUnoNumberingRules::~SvxUnoNumberingRules() noexcept
+{
+}
+
+//XIndexReplace
+void SAL_CALL SvxUnoNumberingRules::replaceByIndex( sal_Int32 Index, const uno::Any& Element )
+{
+ SolarMutexGuard aGuard;
+
+ if( Index < 0 || Index >= maRule.GetLevelCount() )
+ throw IndexOutOfBoundsException();
+
+ Sequence< beans::PropertyValue > aSeq;
+
+ if( !( Element >>= aSeq) )
+ throw IllegalArgumentException();
+ setNumberingRuleByIndex( aSeq, Index );
+}
+
+// XIndexAccess
+sal_Int32 SAL_CALL SvxUnoNumberingRules::getCount()
+{
+ SolarMutexGuard aGuard;
+
+ return maRule.GetLevelCount();
+}
+
+Any SAL_CALL SvxUnoNumberingRules::getByIndex( sal_Int32 Index )
+{
+ SolarMutexGuard aGuard;
+
+ if( Index < 0 || Index >= maRule.GetLevelCount() )
+ throw IndexOutOfBoundsException();
+
+ return Any( getNumberingRuleByIndex(Index) );
+}
+
+//XElementAccess
+Type SAL_CALL SvxUnoNumberingRules::getElementType()
+{
+ return cppu::UnoType<Sequence< beans::PropertyValue >>::get();
+}
+
+sal_Bool SAL_CALL SvxUnoNumberingRules::hasElements()
+{
+ return true;
+}
+
+// XAnyCompare
+sal_Int16 SAL_CALL SvxUnoNumberingRules::compare( const Any& rAny1, const Any& rAny2 )
+{
+ return SvxUnoNumberingRules::Compare( rAny1, rAny2 );
+}
+
+// XCloneable
+Reference< XCloneable > SAL_CALL SvxUnoNumberingRules::createClone( )
+{
+ return new SvxUnoNumberingRules(maRule);
+}
+
+OUString SAL_CALL SvxUnoNumberingRules::getImplementationName( )
+{
+ return "SvxUnoNumberingRules";
+}
+
+sal_Bool SAL_CALL SvxUnoNumberingRules::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+Sequence< OUString > SAL_CALL SvxUnoNumberingRules::getSupportedServiceNames( )
+{
+ return { "com.sun.star.text.NumberingRules" };
+}
+
+Sequence<beans::PropertyValue> SvxUnoNumberingRules::getNumberingRuleByIndex(sal_Int32 nIndex) const
+{
+ // NumberingRule aRule;
+ const SvxNumberFormat& rFmt = maRule.GetLevel(static_cast<sal_uInt16>(nIndex));
+ sal_uInt16 nIdx = 0;
+
+ const int nProps = 15;
+ std::unique_ptr<beans::PropertyValue[]> pArray(new beans::PropertyValue[nProps]);
+
+ Any aVal;
+ {
+ aVal <<= static_cast<sal_uInt16>(rFmt.GetNumberingType());
+ beans::PropertyValue aAlignProp( UNO_NAME_NRULE_NUMBERINGTYPE, -1, aVal, beans::PropertyState_DIRECT_VALUE);
+ pArray[nIdx++] = aAlignProp;
+ }
+
+ {
+ SvxAdjust eAdj = rFmt.GetNumAdjust();
+ aVal <<= ConvertUnoAdjust(eAdj);
+ pArray[nIdx++] = beans::PropertyValue( UNO_NAME_NRULE_ADJUST, -1, aVal, beans::PropertyState_DIRECT_VALUE);
+ }
+
+ {
+ aVal <<= rFmt.GetPrefix();
+ beans::PropertyValue aPrefixProp( UNO_NAME_NRULE_PREFIX, -1, aVal, beans::PropertyState_DIRECT_VALUE);
+ pArray[nIdx++] = aPrefixProp;
+ }
+
+ {
+ aVal <<= rFmt.GetSuffix();
+ beans::PropertyValue aSuffixProp( UNO_NAME_NRULE_SUFFIX, -1, aVal, beans::PropertyState_DIRECT_VALUE);
+ pArray[nIdx++] = aSuffixProp;
+ }
+
+ if(SVX_NUM_CHAR_SPECIAL == rFmt.GetNumberingType())
+ {
+ sal_UCS4 nCode = rFmt.GetBulletChar();
+ OUString aStr( &nCode, 1 );
+ aVal <<= aStr;
+ pArray[nIdx++] = beans::PropertyValue("BulletChar", -1, aVal, beans::PropertyState_DIRECT_VALUE);
+ }
+
+ if( rFmt.GetBulletFont() )
+ {
+ awt::FontDescriptor aDesc;
+ SvxUnoFontDescriptor::ConvertFromFont( *rFmt.GetBulletFont(), aDesc );
+ aVal <<= aDesc;
+ pArray[nIdx++] = beans::PropertyValue( UNO_NAME_NRULE_BULLET_FONT, -1, aVal, beans::PropertyState_DIRECT_VALUE);
+ }
+
+ {
+ const SvxBrushItem* pBrush = rFmt.GetBrush();
+ const Graphic* pGraphic = nullptr;
+ if (pBrush)
+ pGraphic = pBrush->GetGraphic();
+ if (pGraphic)
+ {
+ uno::Reference<awt::XBitmap> xBitmap(pGraphic->GetXGraphic(), uno::UNO_QUERY);
+ aVal <<= xBitmap;
+
+ const beans::PropertyValue aGraphicProp("GraphicBitmap", -1, aVal, beans::PropertyState_DIRECT_VALUE);
+ pArray[nIdx++] = aGraphicProp;
+ }
+ }
+
+ {
+ const Size aSize( rFmt.GetGraphicSize() );
+ const awt::Size aUnoSize( aSize.Width(), aSize.Height() );
+ aVal <<= aUnoSize;
+ const beans::PropertyValue aGraphicSizeProp("GraphicSize", -1, aVal, beans::PropertyState_DIRECT_VALUE );
+ pArray[nIdx++] = aGraphicSizeProp;
+ }
+
+ aVal <<= static_cast<sal_Int16>(rFmt.GetStart());
+ pArray[nIdx++] = beans::PropertyValue(UNO_NAME_NRULE_START_WITH, -1, aVal, beans::PropertyState_DIRECT_VALUE);
+
+ aVal <<= rFmt.GetAbsLSpace();
+ pArray[nIdx++] = beans::PropertyValue(UNO_NAME_NRULE_LEFT_MARGIN, -1, aVal, beans::PropertyState_DIRECT_VALUE);
+
+ aVal <<= rFmt.GetFirstLineOffset();
+ pArray[nIdx++] = beans::PropertyValue(UNO_NAME_NRULE_FIRST_LINE_OFFSET, -1, aVal, beans::PropertyState_DIRECT_VALUE);
+
+ pArray[nIdx++] = beans::PropertyValue("SymbolTextDistance", -1, aVal, beans::PropertyState_DIRECT_VALUE);
+
+ aVal <<= rFmt.GetBulletColor();
+ pArray[nIdx++] = beans::PropertyValue(UNO_NAME_NRULE_BULLET_COLOR, -1, aVal, beans::PropertyState_DIRECT_VALUE);
+
+ aVal <<= static_cast<sal_Int16>(rFmt.GetBulletRelSize());
+ pArray[nIdx++] = beans::PropertyValue(UNO_NAME_NRULE_BULLET_RELSIZE, -1, aVal, beans::PropertyState_DIRECT_VALUE);
+
+ DBG_ASSERT( nIdx <= nProps, "FixMe: overflow in Array!!! [CL]" );
+ Sequence< beans::PropertyValue> aSeq(pArray.get(), nIdx);
+
+ return aSeq;
+}
+
+void SvxUnoNumberingRules::setNumberingRuleByIndex(const Sequence<beans::PropertyValue >& rProperties, sal_Int32 nIndex)
+{
+ SvxNumberFormat aFmt(maRule.GetLevel( static_cast<sal_uInt16>(nIndex) ));
+ for(const beans::PropertyValue& rProp : rProperties)
+ {
+ const OUString& rPropName = rProp.Name;
+ const Any& aVal = rProp.Value;
+
+ if ( rPropName == UNO_NAME_NRULE_NUMBERINGTYPE )
+ {
+ sal_Int16 nSet = sal_Int16();
+ aVal >>= nSet;
+
+ // There is no reason to limit numbering types.
+ if ( nSet>=0 )
+ {
+ aFmt.SetNumberingType(static_cast<SvxNumType>(nSet));
+ continue;
+ }
+ }
+ else if ( rPropName == UNO_NAME_NRULE_PREFIX )
+ {
+ OUString aPrefix;
+ if( aVal >>= aPrefix )
+ {
+ aFmt.SetPrefix(aPrefix);
+ continue;
+ }
+ }
+ else if ( rPropName == UNO_NAME_NRULE_SUFFIX )
+ {
+ OUString aSuffix;
+ if( aVal >>= aSuffix )
+ {
+ aFmt.SetSuffix(aSuffix);
+ continue;
+ }
+ }
+ else if ( rPropName == UNO_NAME_NRULE_BULLETID )
+ {
+ sal_Int16 nSet = sal_Int16();
+ if( aVal >>= nSet )
+ {
+ if(nSet < 0x100)
+ {
+ aFmt.SetBulletChar(nSet);
+ continue;
+ }
+ }
+ }
+ else if ( rPropName == "BulletChar" )
+ {
+ OUString aStr;
+ if( aVal >>= aStr )
+ {
+ if(!aStr.isEmpty())
+ {
+ aFmt.SetBulletChar(aStr.iterateCodePoints(&o3tl::temporary(sal_Int32(0))));
+ }
+ else
+ {
+ aFmt.SetBulletChar(0);
+ }
+ continue;
+ }
+ }
+ else if ( rPropName == UNO_NAME_NRULE_ADJUST )
+ {
+ sal_Int16 nAdjust = sal_Int16();
+ if( aVal >>= nAdjust )
+ {
+ aFmt.SetNumAdjust(ConvertUnoAdjust( static_cast<unsigned short>(nAdjust) ));
+ continue;
+ }
+ }
+ else if ( rPropName == UNO_NAME_NRULE_BULLET_FONT )
+ {
+ awt::FontDescriptor aDesc;
+ if( aVal >>= aDesc )
+ {
+ vcl::Font aFont;
+ SvxUnoFontDescriptor::ConvertToFont( aDesc, aFont );
+ aFmt.SetBulletFont(&aFont);
+ continue;
+ }
+ }
+ else if ( rPropName == "GraphicURL" )
+ {
+ OUString aURL;
+ if (aVal >>= aURL)
+ {
+ Graphic aGraphic = vcl::graphic::loadFromURL(aURL);
+ if (!aGraphic.IsNone())
+ {
+ SvxBrushItem aBrushItem(aGraphic, GPOS_AREA, SID_ATTR_BRUSH);
+ aFmt.SetGraphicBrush(&aBrushItem);
+ }
+ continue;
+ }
+ }
+ else if ( rPropName == "GraphicBitmap" )
+ {
+ uno::Reference<awt::XBitmap> xBitmap;
+ if (aVal >>= xBitmap)
+ {
+ uno::Reference<graphic::XGraphic> xGraphic(xBitmap, uno::UNO_QUERY);
+ Graphic aGraphic(xGraphic);
+ SvxBrushItem aBrushItem(aGraphic, GPOS_AREA, SID_ATTR_BRUSH);
+ aFmt.SetGraphicBrush( &aBrushItem );
+ continue;
+ }
+ }
+ else if ( rPropName == "GraphicSize" )
+ {
+ awt::Size aUnoSize;
+ if( aVal >>= aUnoSize )
+ {
+ aFmt.SetGraphicSize( Size( aUnoSize.Width, aUnoSize.Height ) );
+ continue;
+ }
+ }
+ else if ( rPropName == UNO_NAME_NRULE_START_WITH )
+ {
+ sal_Int16 nStart = sal_Int16();
+ if( aVal >>= nStart )
+ {
+ aFmt.SetStart( nStart );
+ continue;
+ }
+ }
+ else if ( rPropName == UNO_NAME_NRULE_LEFT_MARGIN )
+ {
+ sal_Int32 nMargin = 0;
+ if( aVal >>= nMargin )
+ {
+ aFmt.SetAbsLSpace(nMargin);
+ continue;
+ }
+ }
+ else if ( rPropName == UNO_NAME_NRULE_FIRST_LINE_OFFSET )
+ {
+ sal_Int32 nMargin = 0;
+ if( aVal >>= nMargin )
+ {
+ aFmt.SetFirstLineOffset(nMargin);
+ continue;
+ }
+ }
+ else if ( rPropName == "SymbolTextDistance" )
+ {
+ sal_Int32 nTextDistance = 0;
+ if( aVal >>= nTextDistance )
+ {
+ aFmt.SetCharTextDistance(static_cast<sal_uInt16>(nTextDistance));
+ continue;
+ }
+ }
+ else if ( rPropName == UNO_NAME_NRULE_BULLET_COLOR )
+ {
+ Color aColor;
+ if( aVal >>= aColor )
+ {
+ aFmt.SetBulletColor( aColor );
+ continue;
+ }
+ }
+ else if ( rPropName == UNO_NAME_NRULE_BULLET_RELSIZE )
+ {
+ sal_Int16 nSize = sal_Int16();
+ if( aVal >>= nSize )
+ {
+ // [AOO Bug 120650] the slide content corrupt when open in Aoo
+ // [TDF# 126234] when MS Office document being imported, the value of the relative size
+ // of the bullet could be as high as 400%
+ if ((nSize>400)||(nSize<=0))
+ {
+ nSize = 100;
+ }
+
+ aFmt.SetBulletRelSize( static_cast<short>(nSize) );
+ continue;
+ }
+ }
+ else
+ {
+ continue;
+ }
+
+ throw IllegalArgumentException();
+ }
+
+ // check that we always have a brush item for bitmap numbering
+ if( aFmt.GetNumberingType() == SVX_NUM_BITMAP )
+ {
+ if( nullptr == aFmt.GetBrush() )
+ {
+ GraphicObject aGrafObj;
+ SvxBrushItem aBrushItem( aGrafObj, GPOS_AREA, SID_ATTR_BRUSH );
+ aFmt.SetGraphicBrush( &aBrushItem );
+ }
+ }
+ maRule.SetLevel( static_cast<sal_uInt16>(nIndex), aFmt );
+}
+
+const SvxNumRule& SvxGetNumRule( Reference< XIndexReplace > const & xRule )
+{
+ SvxUnoNumberingRules* pRule = dynamic_cast<SvxUnoNumberingRules*>( xRule.get() );
+ if( pRule == nullptr )
+ throw IllegalArgumentException();
+
+ return pRule->getNumRule();
+}
+
+css::uno::Reference< css::container::XIndexReplace > SvxCreateNumRule(const SvxNumRule& rRule)
+{
+ return new SvxUnoNumberingRules( rRule );
+}
+
+namespace {
+
+class SvxUnoNumberingRulesCompare : public ::cppu::WeakImplHelper< XAnyCompare >
+{
+public:
+ virtual sal_Int16 SAL_CALL compare( const Any& Any1, const Any& Any2 ) override;
+};
+
+}
+
+sal_Int16 SAL_CALL SvxUnoNumberingRulesCompare::compare( const Any& Any1, const Any& Any2 )
+{
+ return SvxUnoNumberingRules::Compare( Any1, Any2 );
+}
+
+sal_Int16 SvxUnoNumberingRules::Compare( const Any& Any1, const Any& Any2 )
+{
+ Reference< XIndexReplace > x1( Any1, UNO_QUERY ), x2( Any2, UNO_QUERY );
+ if( !x1 || !x2 )
+ return -1;
+
+ if( x1.get() == x2.get() )
+ return 0;
+
+ SvxUnoNumberingRules* pRule1 = dynamic_cast<SvxUnoNumberingRules*>( x1.get() );
+ if( !pRule1 )
+ return -1;
+ SvxUnoNumberingRules* pRule2 = dynamic_cast<SvxUnoNumberingRules*>( x2.get() );
+ if( !pRule2 )
+ return -1;
+
+ const SvxNumRule& rRule1 = pRule1->getNumRule();
+ const SvxNumRule& rRule2 = pRule2->getNumRule();
+
+ const sal_uInt16 nLevelCount1 = rRule1.GetLevelCount();
+ const sal_uInt16 nLevelCount2 = rRule2.GetLevelCount();
+
+ if( nLevelCount1 == 0 || nLevelCount2 == 0 )
+ return -1;
+
+ for( sal_uInt16 i = 0; (i < nLevelCount1) && (i < nLevelCount2); i++ )
+ {
+ if( rRule1.GetLevel(i) != rRule2.GetLevel(i) )
+ return -1;
+ }
+ return 0;
+}
+
+Reference< XAnyCompare > SvxCreateNumRuleCompare() noexcept
+{
+ return new SvxUnoNumberingRulesCompare;
+}
+
+css::uno::Reference< css::container::XIndexReplace > SvxCreateNumRule()
+{
+ SvxNumRule aTempRule( SvxNumRuleFlags::NONE, 10, false );
+ return SvxCreateNumRule( aTempRule );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/uno/unopracc.cxx b/editeng/source/uno/unopracc.cxx
new file mode 100644
index 0000000000..c36fc152e2
--- /dev/null
+++ b/editeng/source/uno/unopracc.cxx
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <cppuhelper/typeprovider.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <unopracc.hxx>
+#include <editeng/unoedsrc.hxx>
+
+using namespace ::com::sun::star;
+
+
+SvxAccessibleTextPropertySet::SvxAccessibleTextPropertySet( const SvxEditSource* pEditSrc, const SvxItemPropertySet* pPropSet )
+ : SvxUnoTextRangeBase( pEditSrc, pPropSet )
+{
+}
+
+SvxAccessibleTextPropertySet::~SvxAccessibleTextPropertySet() noexcept
+{
+}
+
+uno::Reference< text::XText > SAL_CALL SvxAccessibleTextPropertySet::getText()
+{
+ // TODO (empty?)
+ return uno::Reference< text::XText > ();
+}
+
+uno::Any SAL_CALL SvxAccessibleTextPropertySet::queryInterface( const uno::Type & rType )
+{
+ return OWeakObject::queryInterface(rType);
+}
+
+void SAL_CALL SvxAccessibleTextPropertySet::acquire()
+ noexcept
+{
+ OWeakObject::acquire();
+}
+
+void SAL_CALL SvxAccessibleTextPropertySet::release()
+ noexcept
+{
+ OWeakObject::release();
+}
+
+// XTypeProvider
+uno::Sequence< uno::Type > SAL_CALL SvxAccessibleTextPropertySet::getTypes()
+{
+ static ::cppu::OTypeCollection ourTypeCollection(
+ ::cppu::UnoType<beans::XPropertySet>::get(),
+ ::cppu::UnoType<beans::XMultiPropertySet>::get(),
+ ::cppu::UnoType<beans::XPropertyState>::get(),
+ ::cppu::UnoType<lang::XServiceInfo>::get(),
+ ::cppu::UnoType<lang::XTypeProvider>::get() );
+
+ return ourTypeCollection.getTypes() ;
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL SvxAccessibleTextPropertySet::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// XServiceInfo
+OUString SAL_CALL SAL_CALL SvxAccessibleTextPropertySet::getImplementationName()
+{
+ return "SvxAccessibleTextPropertySet";
+}
+
+sal_Bool SAL_CALL SvxAccessibleTextPropertySet::supportsService (const OUString& sServiceName)
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/uno/unotext.cxx b/editeng/source/uno/unotext.cxx
new file mode 100644
index 0000000000..f74d7f67c3
--- /dev/null
+++ b/editeng/source/uno/unotext.cxx
@@ -0,0 +1,2561 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/svapp.hxx>
+#include <com/sun/star/text/ControlCharacter.hpp>
+#include <com/sun/star/text/XTextField.hpp>
+#include <com/sun/star/text/TextRangeSelection.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+
+#include <svl/itemset.hxx>
+#include <svl/itempool.hxx>
+#include <svl/eitem.hxx>
+#include <tools/debug.hxx>
+
+#include <editeng/unoprnms.hxx>
+#include <editeng/unotext.hxx>
+#include <editeng/unoedsrc.hxx>
+#include <editeng/unonrule.hxx>
+#include <editeng/unofdesc.hxx>
+#include <editeng/unofield.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/numitem.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/unoipset.hxx>
+#include <editeng/colritem.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <editeng/unonames.hxx>
+
+#include <initializer_list>
+#include <memory>
+#include <string_view>
+
+using namespace ::cppu;
+using namespace ::com::sun::star;
+
+namespace {
+
+ESelection toESelection(const text::TextRangeSelection& rSel)
+{
+ ESelection aESel;
+ aESel.nStartPara = rSel.Start.Paragraph;
+ aESel.nStartPos = rSel.Start.PositionInParagraph;
+ aESel.nEndPara = rSel.End.Paragraph;
+ aESel.nEndPos = rSel.End.PositionInParagraph;
+ return aESel;
+}
+
+}
+
+#define QUERYINT( xint ) \
+ if( rType == cppu::UnoType<xint>::get() ) \
+ return uno::Any(uno::Reference< xint >(this))
+
+const SvxItemPropertySet* ImplGetSvxUnoOutlinerTextCursorSvxPropertySet()
+{
+ static SvxItemPropertySet aTextCursorSvxPropertySet( ImplGetSvxUnoOutlinerTextCursorPropertyMap(), EditEngine::GetGlobalItemPool() );
+ return &aTextCursorSvxPropertySet;
+}
+
+std::span<const SfxItemPropertyMapEntry> ImplGetSvxTextPortionPropertyMap()
+{
+ // Propertymap for an Outliner Text
+ static const SfxItemPropertyMapEntry aSvxTextPortionPropertyMap[] =
+ {
+ SVX_UNOEDIT_CHAR_PROPERTIES,
+ SVX_UNOEDIT_FONT_PROPERTIES,
+ SVX_UNOEDIT_OUTLINER_PROPERTIES,
+ SVX_UNOEDIT_PARA_PROPERTIES,
+ { u"TextField"_ustr, EE_FEATURE_FIELD, cppu::UnoType<text::XTextField>::get(), beans::PropertyAttribute::READONLY, 0 },
+ { u"TextPortionType"_ustr, WID_PORTIONTYPE, ::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0 },
+ { u"TextUserDefinedAttributes"_ustr, EE_CHAR_XMLATTRIBS, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"ParaUserDefinedAttributes"_ustr, EE_PARA_XMLATTRIBS, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ };
+ return aSvxTextPortionPropertyMap;
+}
+const SvxItemPropertySet* ImplGetSvxTextPortionSvxPropertySet()
+{
+ static SvxItemPropertySet aSvxTextPortionPropertySet( ImplGetSvxTextPortionPropertyMap(), EditEngine::GetGlobalItemPool() );
+ return &aSvxTextPortionPropertySet;
+}
+
+static const SfxItemPropertySet* ImplGetSvxTextPortionSfxPropertySet()
+{
+ static SfxItemPropertySet aSvxTextPortionSfxPropertySet( ImplGetSvxTextPortionPropertyMap() );
+ return &aSvxTextPortionSfxPropertySet;
+}
+
+std::span<const SfxItemPropertyMapEntry> ImplGetSvxUnoOutlinerTextCursorPropertyMap()
+{
+ // Propertymap for an Outliner Text
+ static const SfxItemPropertyMapEntry aSvxUnoOutlinerTextCursorPropertyMap[] =
+ {
+ SVX_UNOEDIT_CHAR_PROPERTIES,
+ SVX_UNOEDIT_FONT_PROPERTIES,
+ SVX_UNOEDIT_OUTLINER_PROPERTIES,
+ SVX_UNOEDIT_PARA_PROPERTIES,
+ { u"TextUserDefinedAttributes"_ustr, EE_CHAR_XMLATTRIBS, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"ParaUserDefinedAttributes"_ustr, EE_PARA_XMLATTRIBS, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ };
+
+ return aSvxUnoOutlinerTextCursorPropertyMap;
+}
+static const SfxItemPropertySet* ImplGetSvxUnoOutlinerTextCursorSfxPropertySet()
+{
+ static SfxItemPropertySet aTextCursorSfxPropertySet( ImplGetSvxUnoOutlinerTextCursorPropertyMap() );
+ return &aTextCursorSfxPropertySet;
+}
+
+
+// helper for Item/Property conversion
+
+
+void GetSelection( struct ESelection& rSel, SvxTextForwarder const * pForwarder ) noexcept
+{
+ DBG_ASSERT( pForwarder, "I need a valid SvxTextForwarder!" );
+ if( pForwarder )
+ {
+ sal_Int32 nParaCount = pForwarder->GetParagraphCount();
+ if(nParaCount>0)
+ nParaCount--;
+
+ rSel = ESelection( 0,0, nParaCount, pForwarder->GetTextLen( nParaCount ));
+ }
+}
+
+void CheckSelection( struct ESelection& rSel, SvxTextForwarder const * pForwarder ) noexcept
+{
+ DBG_ASSERT( pForwarder, "I need a valid SvxTextForwarder!" );
+ if( !pForwarder )
+ return;
+
+ if( rSel.nStartPara == EE_PARA_MAX_COUNT )
+ {
+ ::GetSelection( rSel, pForwarder );
+ }
+ else
+ {
+ ESelection aMaxSelection;
+ GetSelection( aMaxSelection, pForwarder );
+
+ // check start position
+ if( rSel.nStartPara < aMaxSelection.nStartPara )
+ {
+ rSel.nStartPara = aMaxSelection.nStartPara;
+ rSel.nStartPos = aMaxSelection.nStartPos;
+ }
+ else if( rSel.nStartPara > aMaxSelection.nEndPara )
+ {
+ rSel.nStartPara = aMaxSelection.nEndPara;
+ rSel.nStartPos = aMaxSelection.nEndPos;
+ }
+ else if( rSel.nStartPos > pForwarder->GetTextLen( rSel.nStartPara ) )
+ {
+ rSel.nStartPos = pForwarder->GetTextLen( rSel.nStartPara );
+ }
+
+ // check end position
+ if( rSel.nEndPara < aMaxSelection.nStartPara )
+ {
+ rSel.nEndPara = aMaxSelection.nStartPara;
+ rSel.nEndPos = aMaxSelection.nStartPos;
+ }
+ else if( rSel.nEndPara > aMaxSelection.nEndPara )
+ {
+ rSel.nEndPara = aMaxSelection.nEndPara;
+ rSel.nEndPos = aMaxSelection.nEndPos;
+ }
+ else if( rSel.nEndPos > pForwarder->GetTextLen( rSel.nEndPara ) )
+ {
+ rSel.nEndPos = pForwarder->GetTextLen( rSel.nEndPara );
+ }
+ }
+}
+
+static void CheckSelection( struct ESelection& rSel, SvxEditSource *pEdit ) noexcept
+{
+ if (!pEdit)
+ return;
+ CheckSelection( rSel, pEdit->GetTextForwarder() );
+}
+
+
+
+
+UNO3_GETIMPLEMENTATION_IMPL( SvxUnoTextRangeBase );
+
+SvxUnoTextRangeBase::SvxUnoTextRangeBase(const SvxItemPropertySet* _pSet)
+ : mpPropSet(_pSet)
+{
+}
+
+SvxUnoTextRangeBase::SvxUnoTextRangeBase(const SvxEditSource* pSource, const SvxItemPropertySet* _pSet)
+: mpPropSet(_pSet)
+{
+ SolarMutexGuard aGuard;
+
+ DBG_ASSERT(pSource,"SvxUnoTextRangeBase: I need a valid SvxEditSource!");
+
+ mpEditSource = pSource->Clone();
+ if (mpEditSource != nullptr)
+ {
+ ESelection aSelection;
+ ::GetSelection( aSelection, mpEditSource->GetTextForwarder() );
+ SetSelection( aSelection );
+
+ mpEditSource->addRange( this );
+ }
+}
+
+SvxUnoTextRangeBase::SvxUnoTextRangeBase(const SvxUnoTextRangeBase& rRange)
+: text::XTextRange()
+, beans::XPropertySet()
+, beans::XMultiPropertySet()
+, beans::XMultiPropertyStates()
+, beans::XPropertyState()
+, lang::XServiceInfo()
+, text::XTextRangeCompare()
+, lang::XUnoTunnel()
+, osl::DebugBase<SvxUnoTextRangeBase>()
+, mpPropSet(rRange.getPropertySet())
+{
+ SolarMutexGuard aGuard;
+
+ if (rRange.mpEditSource)
+ mpEditSource = rRange.mpEditSource->Clone();
+
+ SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
+ if( pForwarder )
+ {
+ maSelection = rRange.maSelection;
+ CheckSelection( maSelection, pForwarder );
+ }
+
+ if( mpEditSource )
+ mpEditSource->addRange( this );
+}
+
+SvxUnoTextRangeBase::~SvxUnoTextRangeBase() noexcept
+{
+ if( mpEditSource )
+ mpEditSource->removeRange( this );
+}
+
+void SvxUnoTextRangeBase::SetEditSource( SvxEditSource* pSource ) noexcept
+{
+ DBG_ASSERT(pSource,"SvxUnoTextRangeBase: I need a valid SvxEditSource!");
+ DBG_ASSERT(mpEditSource==nullptr,"SvxUnoTextRangeBase::SetEditSource called while SvxEditSource already set" );
+
+ mpEditSource.reset( pSource );
+
+ maSelection.nStartPara = EE_PARA_MAX_COUNT;
+
+ if( mpEditSource )
+ mpEditSource->addRange( this );
+}
+
+/** puts a field item with a copy of the given FieldData into the itemset
+ corresponding with this range */
+void SvxUnoTextRangeBase::attachField( std::unique_ptr<SvxFieldData> pData ) noexcept
+{
+ SolarMutexGuard aGuard;
+
+ SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
+ if( pForwarder )
+ {
+ SvxFieldItem aField( std::move(pData), EE_FEATURE_FIELD );
+ pForwarder->QuickInsertField( std::move(aField), maSelection );
+ }
+}
+
+void SvxUnoTextRangeBase::SetSelection( const ESelection& rSelection ) noexcept
+{
+ SolarMutexGuard aGuard;
+
+ maSelection = rSelection;
+ CheckSelection( maSelection, mpEditSource.get() );
+}
+
+// Interface XTextRange ( XText )
+
+uno::Reference< text::XTextRange > SAL_CALL SvxUnoTextRangeBase::getStart()
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference< text::XTextRange > xRange;
+
+ SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
+ if( pForwarder )
+ {
+ CheckSelection( maSelection, pForwarder );
+
+ SvxUnoTextBase* pText = comphelper::getFromUnoTunnel<SvxUnoTextBase>( getText() );
+
+ if(pText == nullptr)
+ throw uno::RuntimeException();
+
+ rtl::Reference<SvxUnoTextRange> pRange = new SvxUnoTextRange( *pText );
+ xRange = pRange;
+
+ ESelection aNewSel = maSelection;
+ aNewSel.nEndPara = aNewSel.nStartPara;
+ aNewSel.nEndPos = aNewSel.nStartPos;
+ pRange->SetSelection( aNewSel );
+ }
+
+ return xRange;
+}
+
+uno::Reference< text::XTextRange > SAL_CALL SvxUnoTextRangeBase::getEnd()
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference< text::XTextRange > xRet;
+
+ SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
+ if( pForwarder )
+ {
+ CheckSelection( maSelection, pForwarder );
+
+ SvxUnoTextBase* pText = comphelper::getFromUnoTunnel<SvxUnoTextBase>( getText() );
+
+ if(pText == nullptr)
+ throw uno::RuntimeException();
+
+ rtl::Reference<SvxUnoTextRange> pNew = new SvxUnoTextRange( *pText );
+ xRet = pNew;
+
+ ESelection aNewSel = maSelection;
+ aNewSel.nStartPara = aNewSel.nEndPara;
+ aNewSel.nStartPos = aNewSel.nEndPos;
+ pNew->SetSelection( aNewSel );
+ }
+ return xRet;
+}
+
+OUString SAL_CALL SvxUnoTextRangeBase::getString()
+{
+ SolarMutexGuard aGuard;
+
+ SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
+ if( pForwarder )
+ {
+ CheckSelection( maSelection, pForwarder );
+
+ return pForwarder->GetText( maSelection );
+ }
+ else
+ {
+ return OUString();
+ }
+}
+
+void SAL_CALL SvxUnoTextRangeBase::setString(const OUString& aString)
+{
+ SolarMutexGuard aGuard;
+
+ SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
+ if( !pForwarder )
+ return;
+
+ CheckSelection( maSelection, pForwarder );
+
+ OUString aConverted(convertLineEnd(aString, LINEEND_LF)); // Simply count the number of line endings
+
+ pForwarder->QuickInsertText( aConverted, maSelection );
+ mpEditSource->UpdateData();
+
+ // Adapt selection
+ //! It would be easier if the EditEngine would return the selection
+ //! on QuickInsertText...
+ CollapseToStart();
+
+ sal_Int32 nLen = aConverted.getLength();
+ if (nLen)
+ GoRight( nLen, true );
+}
+
+// Interface beans::XPropertySet
+uno::Reference< beans::XPropertySetInfo > SAL_CALL SvxUnoTextRangeBase::getPropertySetInfo()
+{
+ return mpPropSet->getPropertySetInfo();
+}
+
+void SAL_CALL SvxUnoTextRangeBase::setPropertyValue(const OUString& PropertyName, const uno::Any& aValue)
+{
+ if (PropertyName == UNO_TR_PROP_SELECTION)
+ {
+ text::TextRangeSelection aSel = aValue.get<text::TextRangeSelection>();
+ SetSelection(toESelection(aSel));
+
+ return;
+ }
+
+ _setPropertyValue( PropertyName, aValue );
+}
+
+void SvxUnoTextRangeBase::_setPropertyValue( const OUString& PropertyName, const uno::Any& aValue, sal_Int32 nPara )
+{
+ SolarMutexGuard aGuard;
+
+ SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
+ if( pForwarder )
+ {
+ CheckSelection( maSelection, pForwarder );
+
+ const SfxItemPropertyMapEntry* pMap = mpPropSet->getPropertyMapEntry(PropertyName );
+ if ( pMap )
+ {
+ ESelection aSel( GetSelection() );
+ bool bParaAttrib = (pMap->nWID >= EE_PARA_START) && ( pMap->nWID <= EE_PARA_END );
+
+ if (pMap->nWID == WID_PARASTYLENAME)
+ {
+ OUString aStyle = aValue.get<OUString>();
+
+ sal_Int32 nEndPara;
+
+ if( nPara == -1 )
+ {
+ nPara = aSel.nStartPara;
+ nEndPara = aSel.nEndPara;
+ }
+ else
+ {
+ // only one paragraph
+ nEndPara = nPara;
+ }
+
+ while( nPara <= nEndPara )
+ {
+ pForwarder->SetStyleSheet(nPara, aStyle);
+ nPara++;
+ }
+ }
+ else if ( nPara == -1 && !bParaAttrib )
+ {
+ SfxItemSet aOldSet( pForwarder->GetAttribs( aSel ) );
+ // we have a selection and no para attribute
+ SfxItemSet aNewSet( *aOldSet.GetPool(), aOldSet.GetRanges() );
+
+ setPropertyValue( pMap, aValue, maSelection, aOldSet, aNewSet );
+
+
+ pForwarder->QuickSetAttribs( aNewSet, GetSelection() );
+ }
+ else
+ {
+ sal_Int32 nEndPara;
+
+ if( nPara == -1 )
+ {
+ nPara = aSel.nStartPara;
+ nEndPara = aSel.nEndPara;
+ }
+ else
+ {
+ // only one paragraph
+ nEndPara = nPara;
+ }
+
+ while( nPara <= nEndPara )
+ {
+ // we have a paragraph
+ SfxItemSet aSet( pForwarder->GetParaAttribs( nPara ) );
+ setPropertyValue( pMap, aValue, maSelection, aSet, aSet );
+ pForwarder->SetParaAttribs( nPara, aSet );
+ nPara++;
+ }
+ }
+
+ GetEditSource()->UpdateData();
+ return;
+ }
+ }
+
+ throw beans::UnknownPropertyException(PropertyName);
+}
+
+void SvxUnoTextRangeBase::setPropertyValue( const SfxItemPropertyMapEntry* pMap, const uno::Any& rValue, const ESelection& rSelection, const SfxItemSet& rOldSet, SfxItemSet& rNewSet )
+{
+ if(!SetPropertyValueHelper( pMap, rValue, rNewSet, &rSelection, GetEditSource() ))
+ {
+ // For parts of composite items with multiple properties (eg background)
+ // must be taken from the document before the old item.
+ rNewSet.Put(rOldSet.Get(pMap->nWID)); // Old Item in new Set
+ SvxItemPropertySet::setPropertyValue(pMap, rValue, rNewSet, false );
+ }
+}
+
+bool SvxUnoTextRangeBase::SetPropertyValueHelper( const SfxItemPropertyMapEntry* pMap, const uno::Any& aValue, SfxItemSet& rNewSet, const ESelection* pSelection /* = NULL */, SvxEditSource* pEditSource /* = NULL*/ )
+{
+ switch( pMap->nWID )
+ {
+ case WID_FONTDESC:
+ {
+ awt::FontDescriptor aDesc;
+ if(aValue >>= aDesc)
+ {
+ SvxUnoFontDescriptor::FillItemSet( aDesc, rNewSet );
+ return true;
+ }
+ }
+ break;
+
+ case EE_PARA_NUMBULLET:
+ {
+ uno::Reference< container::XIndexReplace > xRule;
+ return !aValue.hasValue() || ((aValue >>= xRule) && !xRule.is());
+ }
+
+ case EE_PARA_OUTLLEVEL:
+ {
+ SvxTextForwarder* pForwarder = pEditSource? pEditSource->GetTextForwarder() : nullptr;
+ if(pForwarder && pSelection)
+ {
+ sal_Int16 nLevel = sal_Int16();
+ if( aValue >>= nLevel )
+ {
+ // #101004# Call interface method instead of unsafe cast
+ if(! pForwarder->SetDepth( pSelection->nStartPara, nLevel ) )
+ throw lang::IllegalArgumentException();
+
+ // If valid, then not yet finished. Also needs to be added to paragraph props.
+ return nLevel < -1 || nLevel > 9;
+ }
+ }
+ }
+ break;
+ case WID_NUMBERINGSTARTVALUE:
+ {
+ SvxTextForwarder* pForwarder = pEditSource? pEditSource->GetTextForwarder() : nullptr;
+ if(pForwarder && pSelection)
+ {
+ sal_Int16 nStartValue = -1;
+ if( aValue >>= nStartValue )
+ {
+ pForwarder->SetNumberingStartValue( pSelection->nStartPara, nStartValue );
+ return true;
+ }
+ }
+ }
+ break;
+ case WID_PARAISNUMBERINGRESTART:
+ {
+ SvxTextForwarder* pForwarder = pEditSource? pEditSource->GetTextForwarder() : nullptr;
+ if(pForwarder && pSelection)
+ {
+ bool bParaIsNumberingRestart = false;
+ if( aValue >>= bParaIsNumberingRestart )
+ {
+ pForwarder->SetParaIsNumberingRestart( pSelection->nStartPara, bParaIsNumberingRestart );
+ return true;
+ }
+ }
+ }
+ break;
+ case EE_PARA_BULLETSTATE:
+ {
+ bool bBullet = true;
+ if( aValue >>= bBullet )
+ {
+ SfxBoolItem aItem( EE_PARA_BULLETSTATE, bBullet );
+ rNewSet.Put(aItem);
+ return true;
+ }
+ }
+ break;
+
+ default:
+ return false;
+ }
+
+ throw lang::IllegalArgumentException();
+}
+
+uno::Any SAL_CALL SvxUnoTextRangeBase::getPropertyValue(const OUString& PropertyName)
+{
+ if (PropertyName == UNO_TR_PROP_SELECTION)
+ {
+ const ESelection& rSel = GetSelection();
+ text::TextRangeSelection aSel;
+ aSel.Start.Paragraph = rSel.nStartPara;
+ aSel.Start.PositionInParagraph = rSel.nStartPos;
+ aSel.End.Paragraph = rSel.nEndPara;
+ aSel.End.PositionInParagraph = rSel.nEndPos;
+ return uno::Any(aSel);
+ }
+
+ return _getPropertyValue( PropertyName );
+}
+
+uno::Any SvxUnoTextRangeBase::_getPropertyValue(const OUString& PropertyName, sal_Int32 nPara )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Any aAny;
+
+ SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
+ if( pForwarder )
+ {
+ const SfxItemPropertyMapEntry* pMap = mpPropSet->getPropertyMapEntry(PropertyName );
+ if( pMap )
+ {
+ std::optional<SfxItemSet> oAttribs;
+ if( nPara != -1 )
+ oAttribs.emplace(pForwarder->GetParaAttribs( nPara ).CloneAsValue());
+ else
+ oAttribs.emplace(pForwarder->GetAttribs( GetSelection() ).CloneAsValue());
+
+ // Replace Dontcare with Default, so that one always has a mirror
+ oAttribs->ClearInvalidItems();
+
+ getPropertyValue( pMap, aAny, *oAttribs );
+
+ return aAny;
+ }
+ }
+
+ throw beans::UnknownPropertyException(PropertyName);
+}
+
+void SvxUnoTextRangeBase::getPropertyValue( const SfxItemPropertyMapEntry* pMap, uno::Any& rAny, const SfxItemSet& rSet )
+{
+ switch( pMap->nWID )
+ {
+ case EE_FEATURE_FIELD:
+ if ( rSet.GetItemState( EE_FEATURE_FIELD, false ) == SfxItemState::SET )
+ {
+ const SvxFieldItem* pItem = rSet.GetItem<SvxFieldItem>( EE_FEATURE_FIELD );
+ const SvxFieldData* pData = pItem->GetField();
+ uno::Reference< text::XTextRange > xAnchor( this );
+
+ // get presentation string for field
+ std::optional<Color> pTColor;
+ std::optional<Color> pFColor;
+ std::optional<FontLineStyle> pFldLineStyle;
+
+ SvxTextForwarder* pForwarder = mpEditSource->GetTextForwarder();
+ OUString aPresentation( pForwarder->CalcFieldValue( SvxFieldItem(*pData, EE_FEATURE_FIELD), maSelection.nStartPara, maSelection.nStartPos, pTColor, pFColor, pFldLineStyle ) );
+
+ uno::Reference< text::XTextField > xField( new SvxUnoTextField( xAnchor, aPresentation, pData ) );
+ rAny <<= xField;
+ }
+ break;
+
+ case WID_PORTIONTYPE:
+ if ( rSet.GetItemState( EE_FEATURE_FIELD, false ) == SfxItemState::SET )
+ {
+ rAny <<= OUString("TextField");
+ }
+ else
+ {
+ rAny <<= OUString("Text");
+ }
+ break;
+
+ case WID_PARASTYLENAME:
+ {
+ rAny <<= GetEditSource()->GetTextForwarder()->GetStyleSheet(maSelection.nStartPara);
+ }
+ break;
+
+ default:
+ if(!GetPropertyValueHelper( *const_cast<SfxItemSet*>(&rSet), pMap, rAny, &maSelection, GetEditSource() ))
+ rAny = SvxItemPropertySet::getPropertyValue(pMap, rSet, true, false );
+ }
+}
+
+bool SvxUnoTextRangeBase::GetPropertyValueHelper( SfxItemSet const & rSet, const SfxItemPropertyMapEntry* pMap, uno::Any& aAny, const ESelection* pSelection /* = NULL */, SvxEditSource* pEditSource /* = NULL */ )
+{
+ switch( pMap->nWID )
+ {
+ case WID_FONTDESC:
+ {
+ awt::FontDescriptor aDesc;
+ SvxUnoFontDescriptor::FillFromItemSet( rSet, aDesc );
+ aAny <<= aDesc;
+ }
+ break;
+
+ case EE_PARA_NUMBULLET:
+ {
+ SfxItemState eState = rSet.GetItemState( EE_PARA_NUMBULLET );
+ if( eState != SfxItemState::SET && eState != SfxItemState::DEFAULT)
+ throw uno::RuntimeException();
+
+ const SvxNumBulletItem* pBulletItem = rSet.GetItem( EE_PARA_NUMBULLET );
+
+ if( pBulletItem == nullptr )
+ throw uno::RuntimeException();
+
+ aAny <<= SvxCreateNumRule( pBulletItem->GetNumRule() );
+ }
+ break;
+
+ case EE_PARA_OUTLLEVEL:
+ {
+ SvxTextForwarder* pForwarder = pEditSource? pEditSource->GetTextForwarder() : nullptr;
+ if(pForwarder && pSelection)
+ {
+ sal_Int16 nLevel = pForwarder->GetDepth( pSelection->nStartPara );
+ if( nLevel >= 0 )
+ aAny <<= nLevel;
+ }
+ }
+ break;
+ case WID_NUMBERINGSTARTVALUE:
+ {
+ SvxTextForwarder* pForwarder = pEditSource? pEditSource->GetTextForwarder() : nullptr;
+ if(pForwarder && pSelection)
+ aAny <<= pForwarder->GetNumberingStartValue( pSelection->nStartPara );
+ }
+ break;
+ case WID_PARAISNUMBERINGRESTART:
+ {
+ SvxTextForwarder* pForwarder = pEditSource? pEditSource->GetTextForwarder() : nullptr;
+ if(pForwarder && pSelection)
+ aAny <<= pForwarder->IsParaIsNumberingRestart( pSelection->nStartPara );
+ }
+ break;
+
+ case EE_PARA_BULLETSTATE:
+ {
+ bool bState = false;
+ SfxItemState eState = rSet.GetItemState( EE_PARA_BULLETSTATE );
+ if( eState == SfxItemState::SET || eState == SfxItemState::DEFAULT )
+ {
+ const SfxBoolItem* pItem = rSet.GetItem<SfxBoolItem>( EE_PARA_BULLETSTATE );
+ bState = pItem->GetValue();
+ }
+
+ aAny <<= bState;
+ }
+ break;
+ default:
+
+ return false;
+ }
+
+ return true;
+}
+
+// is not (yet) supported
+void SAL_CALL SvxUnoTextRangeBase::addPropertyChangeListener( const OUString& , const uno::Reference< beans::XPropertyChangeListener >& ) {}
+void SAL_CALL SvxUnoTextRangeBase::removePropertyChangeListener( const OUString& , const uno::Reference< beans::XPropertyChangeListener >& ) {}
+void SAL_CALL SvxUnoTextRangeBase::addVetoableChangeListener( const OUString& , const uno::Reference< beans::XVetoableChangeListener >& ) {}
+void SAL_CALL SvxUnoTextRangeBase::removeVetoableChangeListener( const OUString& , const uno::Reference< beans::XVetoableChangeListener >& ) {}
+
+// XMultiPropertySet
+void SAL_CALL SvxUnoTextRangeBase::setPropertyValues( const uno::Sequence< OUString >& aPropertyNames, const uno::Sequence< uno::Any >& aValues )
+{
+ _setPropertyValues( aPropertyNames, aValues );
+}
+
+void SvxUnoTextRangeBase::_setPropertyValues( const uno::Sequence< OUString >& aPropertyNames, const uno::Sequence< uno::Any >& aValues, sal_Int32 nPara )
+{
+ if (aPropertyNames.getLength() != aValues.getLength())
+ throw lang::IllegalArgumentException("lengths do not match",
+ static_cast<css::beans::XPropertySet*>(this), -1);
+
+ SolarMutexGuard aGuard;
+
+ SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
+ if( !pForwarder )
+ return;
+
+ CheckSelection( maSelection, pForwarder );
+
+ ESelection aSel( GetSelection() );
+
+ const OUString* pPropertyNames = aPropertyNames.getConstArray();
+ const uno::Any* pValues = aValues.getConstArray();
+ sal_Int32 nCount = aPropertyNames.getLength();
+
+ sal_Int32 nEndPara = nPara;
+ sal_Int32 nTempPara = nPara;
+
+ if( nTempPara == -1 )
+ {
+ nTempPara = aSel.nStartPara;
+ nEndPara = aSel.nEndPara;
+ }
+
+ std::optional<SfxItemSet> pOldAttrSet;
+ std::optional<SfxItemSet> pNewAttrSet;
+
+ std::optional<SfxItemSet> pOldParaSet;
+ std::optional<SfxItemSet> pNewParaSet;
+
+ std::optional<OUString> aStyleName;
+
+ for( ; nCount; nCount--, pPropertyNames++, pValues++ )
+ {
+ const SfxItemPropertyMapEntry* pMap = mpPropSet->getPropertyMapEntry( *pPropertyNames );
+
+ if( pMap )
+ {
+ bool bParaAttrib = (pMap->nWID >= EE_PARA_START) && ( pMap->nWID <= EE_PARA_END );
+
+ if (pMap->nWID == WID_PARASTYLENAME)
+ {
+ aStyleName.emplace((*pValues).get<OUString>());
+ }
+ else if( (nPara == -1) && !bParaAttrib )
+ {
+ if( !pNewAttrSet )
+ {
+ pOldAttrSet.emplace( pForwarder->GetAttribs( aSel ) );
+ pNewAttrSet.emplace( *pOldAttrSet->GetPool(), pOldAttrSet->GetRanges() );
+ }
+
+ setPropertyValue( pMap, *pValues, GetSelection(), *pOldAttrSet, *pNewAttrSet );
+
+ if( pMap->nWID >= EE_ITEMS_START && pMap->nWID <= EE_ITEMS_END )
+ {
+ const SfxPoolItem* pItem;
+ if( pNewAttrSet->GetItemState( pMap->nWID, true, &pItem ) == SfxItemState::SET )
+ {
+ pOldAttrSet->Put( *pItem );
+ }
+ }
+ }
+ else
+ {
+ if( !pNewParaSet )
+ {
+ const SfxItemSet & rSet = pForwarder->GetParaAttribs( nTempPara );
+ pOldParaSet.emplace( rSet );
+ pNewParaSet.emplace( *pOldParaSet->GetPool(), pOldParaSet->GetRanges() );
+ }
+
+ setPropertyValue( pMap, *pValues, GetSelection(), *pOldParaSet, *pNewParaSet );
+
+ if( pMap->nWID >= EE_ITEMS_START && pMap->nWID <= EE_ITEMS_END )
+ {
+ const SfxPoolItem* pItem;
+ if( pNewParaSet->GetItemState( pMap->nWID, true, &pItem ) == SfxItemState::SET )
+ {
+ pOldParaSet->Put( *pItem );
+ }
+ }
+
+ }
+ }
+ }
+
+ bool bNeedsUpdate = false;
+
+ if( pNewParaSet || aStyleName )
+ {
+ if( pNewParaSet->Count() )
+ {
+ while( nTempPara <= nEndPara )
+ {
+ SfxItemSet aSet( pForwarder->GetParaAttribs( nTempPara ) );
+ aSet.Put( *pNewParaSet );
+ pForwarder->SetParaAttribs( nTempPara, aSet );
+ if (aStyleName)
+ pForwarder->SetStyleSheet(nTempPara, *aStyleName);
+ nTempPara++;
+ }
+ bNeedsUpdate = true;
+ }
+
+ pNewParaSet.reset();
+ pOldParaSet.reset();
+ }
+
+ if( pNewAttrSet )
+ {
+ if( pNewAttrSet->Count() )
+ {
+ pForwarder->QuickSetAttribs( *pNewAttrSet, GetSelection() );
+ bNeedsUpdate = true;
+ }
+ pNewAttrSet.reset();
+ pOldAttrSet.reset();
+ }
+
+ if( bNeedsUpdate )
+ GetEditSource()->UpdateData();
+}
+
+uno::Sequence< uno::Any > SAL_CALL SvxUnoTextRangeBase::getPropertyValues( const uno::Sequence< OUString >& aPropertyNames )
+{
+ return _getPropertyValues( aPropertyNames );
+}
+
+uno::Sequence< uno::Any > SvxUnoTextRangeBase::_getPropertyValues( const uno::Sequence< OUString >& aPropertyNames, sal_Int32 nPara )
+{
+ SolarMutexGuard aGuard;
+
+ sal_Int32 nCount = aPropertyNames.getLength();
+
+
+ uno::Sequence< uno::Any > aValues( nCount );
+
+ SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
+ if( pForwarder )
+ {
+ std::optional<SfxItemSet> oAttribs;
+ if( nPara != -1 )
+ oAttribs.emplace(pForwarder->GetParaAttribs( nPara ).CloneAsValue());
+ else
+ oAttribs.emplace(pForwarder->GetAttribs( GetSelection() ).CloneAsValue() );
+
+ oAttribs->ClearInvalidItems();
+
+ const OUString* pPropertyNames = aPropertyNames.getConstArray();
+ uno::Any* pValues = aValues.getArray();
+
+ for( ; nCount; nCount--, pPropertyNames++, pValues++ )
+ {
+ const SfxItemPropertyMapEntry* pMap = mpPropSet->getPropertyMapEntry( *pPropertyNames );
+ if( pMap )
+ {
+ getPropertyValue( pMap, *pValues, *oAttribs );
+ }
+ }
+ }
+
+ return aValues;
+}
+
+void SAL_CALL SvxUnoTextRangeBase::addPropertiesChangeListener( const uno::Sequence< OUString >& , const uno::Reference< beans::XPropertiesChangeListener >& )
+{
+}
+
+void SAL_CALL SvxUnoTextRangeBase::removePropertiesChangeListener( const uno::Reference< beans::XPropertiesChangeListener >& )
+{
+}
+
+void SAL_CALL SvxUnoTextRangeBase::firePropertiesChangeEvent( const uno::Sequence< OUString >& , const uno::Reference< beans::XPropertiesChangeListener >& )
+{
+}
+
+// beans::XPropertyState
+beans::PropertyState SAL_CALL SvxUnoTextRangeBase::getPropertyState( const OUString& PropertyName )
+{
+ return _getPropertyState( PropertyName );
+}
+
+const sal_uInt16 aSvxUnoFontDescriptorWhichMap[] = { EE_CHAR_FONTINFO, EE_CHAR_FONTHEIGHT, EE_CHAR_ITALIC,
+ EE_CHAR_UNDERLINE, EE_CHAR_WEIGHT, EE_CHAR_STRIKEOUT, EE_CHAR_CASEMAP,
+ EE_CHAR_WLM, 0 };
+
+beans::PropertyState SvxUnoTextRangeBase::_getPropertyState(const SfxItemPropertyMapEntry* pMap, sal_Int32 nPara)
+{
+ if ( pMap )
+ {
+ SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
+ if( pForwarder )
+ {
+ SfxItemState eItemState(SfxItemState::DEFAULT);
+ bool bItemStateSet(false);
+
+ switch( pMap->nWID )
+ {
+ case WID_FONTDESC:
+ {
+ const sal_uInt16* pWhichId = aSvxUnoFontDescriptorWhichMap;
+ while( *pWhichId )
+ {
+ const SfxItemState eTempItemState(nPara != -1
+ ? pForwarder->GetItemState( nPara, *pWhichId )
+ : pForwarder->GetItemState( GetSelection(), *pWhichId ));
+
+ switch( eTempItemState )
+ {
+ case SfxItemState::DISABLED:
+ case SfxItemState::DONTCARE:
+ eItemState = SfxItemState::DONTCARE;
+ bItemStateSet = true;
+ break;
+
+ case SfxItemState::DEFAULT:
+ if( !bItemStateSet )
+ {
+ eItemState = SfxItemState::DEFAULT;
+ bItemStateSet = true;
+ }
+ break;
+
+ case SfxItemState::SET:
+ if( !bItemStateSet )
+ {
+ eItemState = SfxItemState::SET;
+ bItemStateSet = true;
+ }
+ break;
+ default:
+ throw beans::UnknownPropertyException();
+ }
+
+ pWhichId++;
+ }
+ }
+ break;
+
+ case WID_NUMBERINGSTARTVALUE:
+ case WID_PARAISNUMBERINGRESTART:
+ case WID_PARASTYLENAME:
+ eItemState = SfxItemState::SET;
+ bItemStateSet = true;
+ break;
+
+ default:
+ if(0 != pMap->nWID)
+ {
+ if( nPara != -1 )
+ eItemState = pForwarder->GetItemState( nPara, pMap->nWID );
+ else
+ eItemState = pForwarder->GetItemState( GetSelection(), pMap->nWID );
+
+ bItemStateSet = true;
+ }
+ break;
+ }
+
+ if(bItemStateSet)
+ {
+ switch( eItemState )
+ {
+ case SfxItemState::DONTCARE:
+ case SfxItemState::DISABLED:
+ return beans::PropertyState_AMBIGUOUS_VALUE;
+ case SfxItemState::SET:
+ return beans::PropertyState_DIRECT_VALUE;
+ case SfxItemState::DEFAULT:
+ return beans::PropertyState_DEFAULT_VALUE;
+ default: break;
+ }
+ }
+ }
+ }
+ throw beans::UnknownPropertyException();
+}
+
+beans::PropertyState SvxUnoTextRangeBase::_getPropertyState(std::u16string_view PropertyName, sal_Int32 nPara /* = -1 */)
+{
+ SolarMutexGuard aGuard;
+
+ return _getPropertyState( mpPropSet->getPropertyMapEntry( PropertyName ), nPara);
+}
+
+uno::Sequence< beans::PropertyState > SAL_CALL SvxUnoTextRangeBase::getPropertyStates( const uno::Sequence< OUString >& aPropertyName )
+{
+ return _getPropertyStates( aPropertyName );
+}
+
+uno::Sequence< beans::PropertyState > SvxUnoTextRangeBase::_getPropertyStates(const uno::Sequence< OUString >& PropertyName, sal_Int32 nPara /* = -1 */)
+{
+ uno::Sequence< beans::PropertyState > aRet( PropertyName.getLength() );
+
+ SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
+ if( pForwarder )
+ {
+ std::optional<SfxItemSet> pSet;
+ if( nPara != -1 )
+ {
+ pSet.emplace( pForwarder->GetParaAttribs( nPara ) );
+ }
+ else
+ {
+ ESelection aSel( GetSelection() );
+ CheckSelection( aSel, pForwarder );
+ pSet.emplace( pForwarder->GetAttribs( aSel, EditEngineAttribs::OnlyHard ) );
+ }
+
+ beans::PropertyState* pState = aRet.getArray();
+ for( const OUString& rName : PropertyName )
+ {
+ const SfxItemPropertyMapEntry* pMap = mpPropSet->getPropertyMapEntry( rName );
+ if( !_getOnePropertyStates(*pSet, pMap, *pState++) )
+ {
+ throw beans::UnknownPropertyException(rName);
+ }
+ }
+ }
+
+ return aRet;
+}
+
+bool SvxUnoTextRangeBase::_getOnePropertyStates(const SfxItemSet& rSet, const SfxItemPropertyMapEntry* pMap, beans::PropertyState& rState)
+{
+ if (!pMap)
+ return true;
+ SfxItemState eItemState = SfxItemState::DEFAULT;
+ bool bItemStateSet(false);
+
+ bool bUnknownPropertyFound = false;
+ switch( pMap->nWID )
+ {
+ case WID_FONTDESC:
+ {
+ const sal_uInt16* pWhichId = aSvxUnoFontDescriptorWhichMap;
+ while( *pWhichId )
+ {
+ const SfxItemState eTempItemState(rSet.GetItemState( *pWhichId ));
+
+ switch( eTempItemState )
+ {
+ case SfxItemState::DISABLED:
+ case SfxItemState::DONTCARE:
+ eItemState = SfxItemState::DONTCARE;
+ bItemStateSet = true;
+ break;
+
+ case SfxItemState::DEFAULT:
+ if( !bItemStateSet )
+ {
+ eItemState = SfxItemState::DEFAULT;
+ bItemStateSet = true;
+ }
+ break;
+
+ case SfxItemState::SET:
+ if( !bItemStateSet )
+ {
+ eItemState = SfxItemState::SET;
+ bItemStateSet = true;
+ }
+ break;
+ default:
+ bUnknownPropertyFound = true;
+ break;
+ }
+
+ pWhichId++;
+ }
+ }
+ break;
+
+ case WID_NUMBERINGSTARTVALUE:
+ case WID_PARAISNUMBERINGRESTART:
+ case WID_PARASTYLENAME:
+ eItemState = SfxItemState::SET;
+ bItemStateSet = true;
+ break;
+
+ default:
+ if(0 != pMap->nWID)
+ {
+ eItemState = rSet.GetItemState( pMap->nWID, false );
+ bItemStateSet = true;
+ }
+ break;
+ }
+
+ if( bUnknownPropertyFound )
+ return false;
+
+ if(bItemStateSet)
+ {
+ if (pMap->nWID == EE_CHAR_COLOR)
+ {
+ // Theme & effects can be DEFAULT_VALUE, even if the same pool item has a color
+ // which is a DIRECT_VALUE.
+ const SvxColorItem* pColor = rSet.GetItem<SvxColorItem>(EE_CHAR_COLOR);
+ if (!pColor)
+ {
+ SAL_WARN("editeng", "Missing EE_CHAR_COLOR SvxColorItem");
+ return false;
+ }
+ switch (pMap->nMemberId)
+ {
+ case MID_COLOR_THEME_INDEX:
+ if (!pColor->getComplexColor().isValidThemeType())
+ {
+ eItemState = SfxItemState::DEFAULT;
+ }
+ break;
+ case MID_COLOR_LUM_MOD:
+ {
+ sal_Int16 nLumMod = 10000;
+ for (auto const& rTransform : pColor->getComplexColor().getTransformations())
+ {
+ if (rTransform.meType == model::TransformationType::LumMod)
+ nLumMod = rTransform.mnValue;
+ }
+ if (nLumMod == 10000)
+ {
+ eItemState = SfxItemState::DEFAULT;
+ }
+ break;
+ }
+ case MID_COLOR_LUM_OFF:
+ {
+ sal_Int16 nLumOff = 0;
+ for (auto const& rTransform : pColor->getComplexColor().getTransformations())
+ {
+ if (rTransform.meType == model::TransformationType::LumOff)
+ nLumOff = rTransform.mnValue;
+ }
+ if (nLumOff == 0)
+ {
+ eItemState = SfxItemState::DEFAULT;
+ }
+ break;
+ }
+ case MID_COMPLEX_COLOR:
+ if (pColor->getComplexColor().getType() == model::ColorType::Unused)
+ {
+ eItemState = SfxItemState::DEFAULT;
+ }
+ break;
+ }
+ }
+
+ switch( eItemState )
+ {
+ case SfxItemState::SET:
+ rState = beans::PropertyState_DIRECT_VALUE;
+ break;
+ case SfxItemState::DEFAULT:
+ rState = beans::PropertyState_DEFAULT_VALUE;
+ break;
+// case SfxItemState::DONTCARE:
+// case SfxItemState::DISABLED:
+ default:
+ rState = beans::PropertyState_AMBIGUOUS_VALUE;
+ }
+ }
+ else
+ {
+ rState = beans::PropertyState_AMBIGUOUS_VALUE;
+ }
+ return true;
+}
+
+void SAL_CALL SvxUnoTextRangeBase::setPropertyToDefault( const OUString& PropertyName )
+{
+ _setPropertyToDefault( PropertyName );
+}
+
+void SvxUnoTextRangeBase::_setPropertyToDefault(const OUString& PropertyName, sal_Int32 nPara /* = -1 */)
+{
+ SolarMutexGuard aGuard;
+
+ SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
+
+ if( pForwarder )
+ {
+ const SfxItemPropertyMapEntry* pMap = mpPropSet->getPropertyMapEntry( PropertyName );
+ if ( pMap )
+ {
+ CheckSelection( maSelection, mpEditSource->GetTextForwarder() );
+ _setPropertyToDefault( pForwarder, pMap, nPara );
+ return;
+ }
+ }
+
+ throw beans::UnknownPropertyException(PropertyName);
+}
+
+void SvxUnoTextRangeBase::_setPropertyToDefault(SvxTextForwarder* pForwarder, const SfxItemPropertyMapEntry* pMap, sal_Int32 nPara )
+{
+ do
+ {
+ SfxItemSet aSet(*pForwarder->GetPool());
+
+ if( pMap->nWID == WID_FONTDESC )
+ {
+ SvxUnoFontDescriptor::setPropertyToDefault( aSet );
+ }
+ else if( pMap->nWID == WID_NUMBERINGSTARTVALUE )
+ {
+ pForwarder->SetNumberingStartValue( maSelection.nStartPara, -1 );
+ }
+ else if( pMap->nWID == WID_PARAISNUMBERINGRESTART )
+ {
+ pForwarder->SetParaIsNumberingRestart( maSelection.nStartPara, false );
+ }
+ else
+ {
+ aSet.InvalidateItem( pMap->nWID );
+ }
+
+ if(nPara != -1)
+ pForwarder->SetParaAttribs( nPara, aSet );
+ else
+ pForwarder->QuickSetAttribs( aSet, GetSelection() );
+
+ GetEditSource()->UpdateData();
+
+ return;
+ }
+ while(false);
+}
+
+uno::Any SAL_CALL SvxUnoTextRangeBase::getPropertyDefault( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+
+ SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
+ if( pForwarder )
+ {
+ const SfxItemPropertyMapEntry* pMap = mpPropSet->getPropertyMapEntry( aPropertyName );
+ if( pMap )
+ {
+ SfxItemPool* pPool = pForwarder->GetPool();
+
+ switch( pMap->nWID )
+ {
+ case WID_FONTDESC:
+ return SvxUnoFontDescriptor::getPropertyDefault( pPool );
+
+ case EE_PARA_OUTLLEVEL:
+ {
+ uno::Any aAny;
+ return aAny;
+ }
+
+ case WID_NUMBERINGSTARTVALUE:
+ return uno::Any( sal_Int16(-1) );
+
+ case WID_PARAISNUMBERINGRESTART:
+ return uno::Any( false );
+
+ default:
+ {
+ // Get Default from ItemPool
+ if(SfxItemPool::IsWhich(pMap->nWID))
+ {
+ SfxItemSet aSet( *pPool, pMap->nWID, pMap->nWID );
+ aSet.Put(pPool->GetDefaultItem(pMap->nWID));
+ return SvxItemPropertySet::getPropertyValue(pMap, aSet, true, false );
+ }
+ }
+ }
+ }
+ }
+ throw beans::UnknownPropertyException(aPropertyName);
+}
+
+// beans::XMultiPropertyStates
+void SAL_CALL SvxUnoTextRangeBase::setAllPropertiesToDefault()
+{
+ SolarMutexGuard aGuard;
+
+ SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
+
+ if( pForwarder )
+ {
+ for (const SfxItemPropertyMapEntry* entry : mpPropSet->getPropertyMap().getPropertyEntries())
+ {
+ _setPropertyToDefault( pForwarder, entry, -1 );
+ }
+ }
+}
+
+void SAL_CALL SvxUnoTextRangeBase::setPropertiesToDefault( const uno::Sequence< OUString >& aPropertyNames )
+{
+ for( const OUString& rName : aPropertyNames )
+ {
+ setPropertyToDefault( rName );
+ }
+}
+
+uno::Sequence< uno::Any > SAL_CALL SvxUnoTextRangeBase::getPropertyDefaults( const uno::Sequence< OUString >& aPropertyNames )
+{
+ uno::Sequence< uno::Any > ret( aPropertyNames.getLength() );
+ uno::Any* pDefaults = ret.getArray();
+
+ for( const OUString& rName : aPropertyNames )
+ {
+ *pDefaults++ = getPropertyDefault( rName );
+ }
+
+ return ret;
+}
+
+// internal
+void SvxUnoTextRangeBase::CollapseToStart() noexcept
+{
+ CheckSelection( maSelection, mpEditSource.get() );
+
+ maSelection.nEndPara = maSelection.nStartPara;
+ maSelection.nEndPos = maSelection.nStartPos;
+}
+
+void SvxUnoTextRangeBase::CollapseToEnd() noexcept
+{
+ CheckSelection( maSelection, mpEditSource.get() );
+
+ maSelection.nStartPara = maSelection.nEndPara;
+ maSelection.nStartPos = maSelection.nEndPos;
+}
+
+bool SvxUnoTextRangeBase::IsCollapsed() noexcept
+{
+ CheckSelection( maSelection, mpEditSource.get() );
+
+ return ( maSelection.nStartPara == maSelection.nEndPara &&
+ maSelection.nStartPos == maSelection.nEndPos );
+}
+
+bool SvxUnoTextRangeBase::GoLeft(sal_Int32 nCount, bool Expand) noexcept
+{
+ CheckSelection( maSelection, mpEditSource.get() );
+
+ // #75098# use end position, as in Writer (start is anchor, end is cursor)
+ sal_Int32 nNewPos = maSelection.nEndPos;
+ sal_Int32 nNewPar = maSelection.nEndPara;
+
+ bool bOk = true;
+ SvxTextForwarder* pForwarder = nullptr;
+ while ( nCount > nNewPos && bOk )
+ {
+ if ( nNewPar == 0 )
+ bOk = false;
+ else
+ {
+ if ( !pForwarder )
+ pForwarder = mpEditSource->GetTextForwarder(); // first here, it is necessary...
+
+ --nNewPar;
+ nCount -= nNewPos + 1;
+ nNewPos = pForwarder->GetTextLen( nNewPar );
+ }
+ }
+
+ if ( bOk )
+ {
+ nNewPos = nNewPos - nCount;
+ maSelection.nStartPara = nNewPar;
+ maSelection.nStartPos = nNewPos;
+ }
+
+ if (!Expand)
+ CollapseToStart();
+
+ return bOk;
+}
+
+bool SvxUnoTextRangeBase::GoRight(sal_Int32 nCount, bool Expand) noexcept
+{
+ if (!mpEditSource)
+ return false;
+ SvxTextForwarder* pForwarder = mpEditSource->GetTextForwarder();
+ if( !pForwarder )
+ return false;
+
+ CheckSelection( maSelection, pForwarder );
+
+ sal_Int32 nNewPos = maSelection.nEndPos + nCount;
+ sal_Int32 nNewPar = maSelection.nEndPara;
+
+ bool bOk = true;
+ sal_Int32 nParCount = pForwarder->GetParagraphCount();
+ sal_Int32 nThisLen = pForwarder->GetTextLen( nNewPar );
+ while ( nNewPos > nThisLen && bOk )
+ {
+ if ( nNewPar + 1 >= nParCount )
+ bOk = false;
+ else
+ {
+ nNewPos -= nThisLen+1;
+ ++nNewPar;
+ nThisLen = pForwarder->GetTextLen( nNewPar );
+ }
+ }
+
+ if (bOk)
+ {
+ maSelection.nEndPara = nNewPar;
+ maSelection.nEndPos = nNewPos;
+ }
+
+ if (!Expand)
+ CollapseToEnd();
+
+ return bOk;
+}
+
+void SvxUnoTextRangeBase::GotoStart(bool Expand) noexcept
+{
+ maSelection.nStartPara = 0;
+ maSelection.nStartPos = 0;
+
+ if (!Expand)
+ CollapseToStart();
+}
+
+void SvxUnoTextRangeBase::GotoEnd(bool Expand) noexcept
+{
+ CheckSelection( maSelection, mpEditSource.get() );
+
+ SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
+ if( !pForwarder )
+ return;
+
+ sal_Int32 nPar = pForwarder->GetParagraphCount();
+ if (nPar)
+ --nPar;
+
+ maSelection.nEndPara = nPar;
+ maSelection.nEndPos = pForwarder->GetTextLen( nPar );
+
+ if (!Expand)
+ CollapseToEnd();
+}
+
+// lang::XServiceInfo
+sal_Bool SAL_CALL SvxUnoTextRangeBase::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService( this, ServiceName );
+}
+
+uno::Sequence< OUString > SAL_CALL SvxUnoTextRangeBase::getSupportedServiceNames()
+{
+ return getSupportedServiceNames_Static();
+}
+
+uno::Sequence< OUString > SvxUnoTextRangeBase::getSupportedServiceNames_Static()
+{
+ return { "com.sun.star.style.CharacterProperties",
+ "com.sun.star.style.CharacterPropertiesComplex",
+ "com.sun.star.style.CharacterPropertiesAsian" };
+}
+
+// XTextRangeCompare
+sal_Int16 SAL_CALL SvxUnoTextRangeBase::compareRegionStarts( const uno::Reference< text::XTextRange >& xR1, const uno::Reference< text::XTextRange >& xR2 )
+{
+ SvxUnoTextRangeBase* pR1 = comphelper::getFromUnoTunnel<SvxUnoTextRangeBase>( xR1 );
+ SvxUnoTextRangeBase* pR2 = comphelper::getFromUnoTunnel<SvxUnoTextRangeBase>( xR2 );
+
+ if( (pR1 == nullptr) || (pR2 == nullptr) )
+ throw lang::IllegalArgumentException();
+
+ const ESelection& r1 = pR1->maSelection;
+ const ESelection& r2 = pR2->maSelection;
+
+ if( r1.nStartPara == r2.nStartPara )
+ {
+ if( r1.nStartPos == r2.nStartPos )
+ return 0;
+ else
+ return r1.nStartPos < r2.nStartPos ? 1 : -1;
+ }
+ else
+ {
+ return r1.nStartPara < r2.nStartPara ? 1 : -1;
+ }
+}
+
+sal_Int16 SAL_CALL SvxUnoTextRangeBase::compareRegionEnds( const uno::Reference< text::XTextRange >& xR1, const uno::Reference< text::XTextRange >& xR2 )
+{
+ SvxUnoTextRangeBase* pR1 = comphelper::getFromUnoTunnel<SvxUnoTextRangeBase>( xR1 );
+ SvxUnoTextRangeBase* pR2 = comphelper::getFromUnoTunnel<SvxUnoTextRangeBase>( xR2 );
+
+ if( (pR1 == nullptr) || (pR2 == nullptr) )
+ throw lang::IllegalArgumentException();
+
+ const ESelection& r1 = pR1->maSelection;
+ const ESelection& r2 = pR2->maSelection;
+
+ if( r1.nEndPara == r2.nEndPara )
+ {
+ if( r1.nEndPos == r2.nEndPos )
+ return 0;
+ else
+ return r1.nEndPos < r2.nEndPos ? 1 : -1;
+ }
+ else
+ {
+ return r1.nEndPara < r2.nEndPara ? 1 : -1;
+ }
+}
+
+SvxUnoTextRange::SvxUnoTextRange(const SvxUnoTextBase& rParent, bool bPortion /* = false */)
+:SvxUnoTextRangeBase( rParent.GetEditSource(), bPortion ? ImplGetSvxTextPortionSvxPropertySet() : rParent.getPropertySet() ),
+ mbPortion( bPortion )
+{
+ xParentText = static_cast<text::XText*>(const_cast<SvxUnoTextBase *>(&rParent));
+}
+
+SvxUnoTextRange::~SvxUnoTextRange() noexcept
+{
+}
+
+uno::Any SAL_CALL SvxUnoTextRange::queryAggregation( const uno::Type & rType )
+{
+ QUERYINT( text::XTextRange );
+ else if( rType == cppu::UnoType<beans::XMultiPropertyStates>::get())
+ return uno::Any(uno::Reference< beans::XMultiPropertyStates >(this));
+ else if( rType == cppu::UnoType<beans::XPropertySet>::get())
+ return uno::Any(uno::Reference< beans::XPropertySet >(this));
+ else QUERYINT( beans::XPropertyState );
+ else QUERYINT( text::XTextRangeCompare );
+ else if( rType == cppu::UnoType<beans::XMultiPropertySet>::get())
+ return uno::Any(uno::Reference< beans::XMultiPropertySet >(this));
+ else QUERYINT( lang::XServiceInfo );
+ else QUERYINT( lang::XTypeProvider );
+ else QUERYINT( lang::XUnoTunnel );
+ else
+ return OWeakAggObject::queryAggregation( rType );
+}
+
+uno::Any SAL_CALL SvxUnoTextRange::queryInterface( const uno::Type & rType )
+{
+ return OWeakAggObject::queryInterface(rType);
+}
+
+void SAL_CALL SvxUnoTextRange::acquire()
+ noexcept
+{
+ OWeakAggObject::acquire();
+}
+
+void SAL_CALL SvxUnoTextRange::release()
+ noexcept
+{
+ OWeakAggObject::release();
+}
+
+// XTypeProvider
+
+uno::Sequence< uno::Type > SAL_CALL SvxUnoTextRange::getTypes()
+{
+ static const uno::Sequence< uno::Type > TYPES {
+ cppu::UnoType<text::XTextRange>::get(),
+ cppu::UnoType<beans::XPropertySet>::get(),
+ cppu::UnoType<beans::XMultiPropertySet>::get(),
+ cppu::UnoType<beans::XMultiPropertyStates>::get(),
+ cppu::UnoType<beans::XPropertyState>::get(),
+ cppu::UnoType<lang::XServiceInfo>::get(),
+ cppu::UnoType<lang::XTypeProvider>::get(),
+ cppu::UnoType<lang::XUnoTunnel>::get(),
+ cppu::UnoType<text::XTextRangeCompare>::get() };
+ return TYPES;
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL SvxUnoTextRange::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// XTextRange
+uno::Reference< text::XText > SAL_CALL SvxUnoTextRange::getText()
+{
+ return xParentText;
+}
+
+// lang::XServiceInfo
+OUString SAL_CALL SvxUnoTextRange::getImplementationName()
+{
+ return "SvxUnoTextRange";
+}
+
+
+
+
+SvxUnoTextBase::SvxUnoTextBase(const SvxItemPropertySet* _pSet)
+ : SvxUnoTextRangeBase(_pSet)
+{
+}
+
+SvxUnoTextBase::SvxUnoTextBase(const SvxEditSource* pSource, const SvxItemPropertySet* _pSet, uno::Reference < text::XText > const & xParent)
+ : SvxUnoTextRangeBase(pSource, _pSet)
+{
+ xParentText = xParent;
+ ESelection aSelection;
+ ::GetSelection( aSelection, GetEditSource()->GetTextForwarder() );
+ SetSelection( aSelection );
+}
+
+SvxUnoTextBase::SvxUnoTextBase(const SvxUnoTextBase& rText)
+: SvxUnoTextRangeBase( rText )
+, text::XTextAppend()
+, text::XTextCopy()
+, container::XEnumerationAccess()
+, text::XTextRangeMover()
+, lang::XTypeProvider()
+{
+ xParentText = rText.xParentText;
+}
+
+SvxUnoTextBase::~SvxUnoTextBase() noexcept
+{
+}
+
+// XInterface
+uno::Any SAL_CALL SvxUnoTextBase::queryAggregation( const uno::Type & rType )
+{
+ QUERYINT( text::XText );
+ QUERYINT( text::XSimpleText );
+ if( rType == cppu::UnoType<text::XTextRange>::get())
+ return uno::Any(uno::Reference< text::XTextRange >(static_cast<text::XText*>(this)));
+ QUERYINT(container::XEnumerationAccess );
+ QUERYINT( container::XElementAccess );
+ QUERYINT( beans::XMultiPropertyStates );
+ QUERYINT( beans::XPropertySet );
+ QUERYINT( beans::XMultiPropertySet );
+ QUERYINT( beans::XPropertyState );
+ QUERYINT( text::XTextRangeCompare );
+ QUERYINT( lang::XServiceInfo );
+ QUERYINT( text::XTextRangeMover );
+ QUERYINT( text::XTextCopy );
+ QUERYINT( text::XTextAppend );
+ QUERYINT( text::XParagraphAppend );
+ QUERYINT( text::XTextPortionAppend );
+ QUERYINT( lang::XTypeProvider );
+ QUERYINT( lang::XUnoTunnel );
+
+ return uno::Any();
+}
+
+// XTypeProvider
+
+uno::Sequence< uno::Type > SAL_CALL SvxUnoTextBase::getTypes()
+{
+ static const uno::Sequence< uno::Type > TYPES {
+ cppu::UnoType<text::XText>::get(),
+ cppu::UnoType<container::XEnumerationAccess>::get(),
+ cppu::UnoType<beans::XPropertySet>::get(),
+ cppu::UnoType<beans::XMultiPropertySet>::get(),
+ cppu::UnoType<beans::XMultiPropertyStates>::get(),
+ cppu::UnoType<beans::XPropertyState>::get(),
+ cppu::UnoType<text::XTextRangeMover>::get(),
+ cppu::UnoType<text::XTextAppend>::get(),
+ cppu::UnoType<text::XTextCopy>::get(),
+ cppu::UnoType<text::XParagraphAppend>::get(),
+ cppu::UnoType<text::XTextPortionAppend>::get(),
+ cppu::UnoType<lang::XServiceInfo>::get(),
+ cppu::UnoType<lang::XTypeProvider>::get(),
+ cppu::UnoType<lang::XUnoTunnel>::get(),
+ cppu::UnoType<text::XTextRangeCompare>::get() };
+ return TYPES;
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL SvxUnoTextBase::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+uno::Reference< text::XTextCursor > SvxUnoTextBase::createTextCursorBySelection( const ESelection& rSel )
+{
+ rtl::Reference<SvxUnoTextCursor> pCursor = new SvxUnoTextCursor( *this );
+ pCursor->SetSelection( rSel );
+ return pCursor;
+}
+
+// XSimpleText
+
+uno::Reference< text::XTextCursor > SAL_CALL SvxUnoTextBase::createTextCursor()
+{
+ SolarMutexGuard aGuard;
+ return new SvxUnoTextCursor( *this );
+}
+
+uno::Reference< text::XTextCursor > SAL_CALL SvxUnoTextBase::createTextCursorByRange( const uno::Reference< text::XTextRange >& aTextPosition )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference< text::XTextCursor > xCursor;
+
+ if( aTextPosition.is() )
+ {
+ SvxUnoTextRangeBase* pRange = comphelper::getFromUnoTunnel<SvxUnoTextRangeBase>( aTextPosition );
+ if(pRange)
+ xCursor = createTextCursorBySelection( pRange->GetSelection() );
+ }
+
+ return xCursor;
+}
+
+void SAL_CALL SvxUnoTextBase::insertString( const uno::Reference< text::XTextRange >& xRange, const OUString& aString, sal_Bool bAbsorb )
+{
+ SolarMutexGuard aGuard;
+
+ if( !xRange.is() )
+ return;
+
+ SvxUnoTextRangeBase* pRange = comphelper::getFromUnoTunnel<SvxUnoTextRange>( xRange );
+ if(!pRange)
+ return;
+
+ // setString on SvxUnoTextRangeBase instead of itself QuickInsertText
+ // and UpdateData, so that the selection will be adjusted to
+ // SvxUnoTextRangeBase. Actually all cursor objects of this Text must
+ // to be statement to be adapted!
+
+ if (!bAbsorb) // do not replace -> append on tail
+ pRange->CollapseToEnd();
+
+ pRange->setString( aString );
+
+ pRange->CollapseToEnd();
+
+ if (GetEditSource())
+ {
+ ESelection aSelection;
+ ::GetSelection( aSelection, GetEditSource()->GetTextForwarder() );
+ SetSelection( aSelection );
+ }
+}
+
+void SAL_CALL SvxUnoTextBase::insertControlCharacter( const uno::Reference< text::XTextRange >& xRange, sal_Int16 nControlCharacter, sal_Bool bAbsorb )
+{
+ SolarMutexGuard aGuard;
+
+ SvxTextForwarder* pForwarder = GetEditSource() ? GetEditSource()->GetTextForwarder() : nullptr;
+
+ if( !pForwarder )
+ return;
+
+ ESelection aSelection;
+ ::GetSelection( aSelection, pForwarder );
+ SetSelection( aSelection );
+
+ switch( nControlCharacter )
+ {
+ case text::ControlCharacter::PARAGRAPH_BREAK:
+ {
+ insertString( xRange, "\x0D", bAbsorb );
+
+ return;
+ }
+ case text::ControlCharacter::LINE_BREAK:
+ {
+ SvxUnoTextRangeBase* pRange = comphelper::getFromUnoTunnel<SvxUnoTextRange>( xRange );
+ if(pRange)
+ {
+ ESelection aRange = pRange->GetSelection();
+
+ if( bAbsorb )
+ {
+ pForwarder->QuickInsertText( "", aRange );
+
+ aRange.nEndPos = aRange.nStartPos;
+ aRange.nEndPara = aRange.nStartPara;
+ }
+ else
+ {
+ aRange.nStartPara = aRange.nEndPara;
+ aRange.nStartPos = aRange.nEndPos;
+ }
+
+ pForwarder->QuickInsertLineBreak( aRange );
+ GetEditSource()->UpdateData();
+
+ aRange.nEndPos += 1;
+ if( !bAbsorb )
+ aRange.nStartPos += 1;
+
+ pRange->SetSelection( aRange );
+ }
+ return;
+ }
+ case text::ControlCharacter::APPEND_PARAGRAPH:
+ {
+ SvxUnoTextRangeBase* pRange = comphelper::getFromUnoTunnel<SvxUnoTextRange>( xRange );
+ if(pRange)
+ {
+ ESelection aRange = pRange->GetSelection();
+// ESelection aOldSelection = aRange;
+
+ aRange.nStartPos = pForwarder->GetTextLen( aRange.nStartPara );
+
+ aRange.nEndPara = aRange.nStartPara;
+ aRange.nEndPos = aRange.nStartPos;
+
+ pRange->SetSelection( aRange );
+ static constexpr OUStringLiteral CR = u"\x0D";
+ pRange->setString( CR );
+
+ aRange.nStartPos = 0;
+ aRange.nStartPara += 1;
+ aRange.nEndPos = 0;
+ aRange.nEndPara += 1;
+
+ pRange->SetSelection( aRange );
+
+ return;
+ }
+ [[fallthrough]];
+ }
+ default:
+ throw lang::IllegalArgumentException();
+ }
+}
+
+// XText
+void SAL_CALL SvxUnoTextBase::insertTextContent( const uno::Reference< text::XTextRange >& xRange, const uno::Reference< text::XTextContent >& xContent, sal_Bool bAbsorb )
+{
+ SolarMutexGuard aGuard;
+
+ SvxTextForwarder* pForwarder = GetEditSource() ? GetEditSource()->GetTextForwarder() : nullptr;
+ if (!pForwarder)
+ return;
+
+ uno::Reference<beans::XPropertySet> xPropSet(xRange, uno::UNO_QUERY);
+ if (!xPropSet.is())
+ throw lang::IllegalArgumentException();
+
+ uno::Any aAny = xPropSet->getPropertyValue(UNO_TR_PROP_SELECTION);
+ text::TextRangeSelection aSel = aAny.get<text::TextRangeSelection>();
+ if (!bAbsorb)
+ aSel.Start = aSel.End;
+
+ std::unique_ptr<SvxFieldData> pFieldData(SvxFieldData::Create(xContent));
+ if (!pFieldData)
+ throw lang::IllegalArgumentException();
+
+ SvxFieldItem aField( *pFieldData, EE_FEATURE_FIELD );
+ pForwarder->QuickInsertField(aField, toESelection(aSel));
+ GetEditSource()->UpdateData();
+
+ uno::Reference<beans::XPropertySet> xPropSetContent(xContent, uno::UNO_QUERY);
+ if (!xPropSetContent.is())
+ throw lang::IllegalArgumentException();
+
+ xPropSetContent->setPropertyValue(UNO_TC_PROP_ANCHOR, uno::Any(xRange));
+
+ aSel.End.PositionInParagraph += 1;
+ aSel.Start.PositionInParagraph = aSel.End.PositionInParagraph;
+ xPropSet->setPropertyValue(UNO_TR_PROP_SELECTION, uno::Any(aSel));
+}
+
+void SAL_CALL SvxUnoTextBase::removeTextContent( const uno::Reference< text::XTextContent >& )
+{
+}
+
+// XTextRange
+
+uno::Reference< text::XText > SAL_CALL SvxUnoTextBase::getText()
+{
+ SolarMutexGuard aGuard;
+
+ if (GetEditSource())
+ {
+ ESelection aSelection;
+ ::GetSelection( aSelection, GetEditSource()->GetTextForwarder() );
+ SetSelection( aSelection );
+ }
+
+ return static_cast<text::XText*>(this);
+}
+
+uno::Reference< text::XTextRange > SAL_CALL SvxUnoTextBase::getStart()
+{
+ return SvxUnoTextRangeBase::getStart();
+}
+
+uno::Reference< text::XTextRange > SAL_CALL SvxUnoTextBase::getEnd()
+{
+ return SvxUnoTextRangeBase::getEnd();
+}
+
+OUString SAL_CALL SvxUnoTextBase::getString()
+{
+ return SvxUnoTextRangeBase::getString();
+}
+
+void SAL_CALL SvxUnoTextBase::setString( const OUString& aString )
+{
+ SvxUnoTextRangeBase::setString(aString);
+}
+
+
+// XEnumerationAccess
+uno::Reference< container::XEnumeration > SAL_CALL SvxUnoTextBase::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+
+ if (!GetEditSource())
+ return uno::Reference< container::XEnumeration >();
+
+ if( maSelection == ESelection(0,0,0,0) || maSelection == ESelection(EE_PARA_MAX_COUNT,0,0,0) )
+ {
+ ESelection aSelection;
+ ::GetSelection( aSelection, GetEditSource()->GetTextForwarder() );
+ return new SvxUnoTextContentEnumeration(*this, aSelection);
+ }
+ else
+ {
+ return new SvxUnoTextContentEnumeration(*this, maSelection);
+ }
+}
+
+// XElementAccess ( container::XEnumerationAccess )
+uno::Type SAL_CALL SvxUnoTextBase::getElementType( )
+{
+ return cppu::UnoType<text::XTextRange>::get();
+}
+
+sal_Bool SAL_CALL SvxUnoTextBase::hasElements( )
+{
+ SolarMutexGuard aGuard;
+
+ if(GetEditSource())
+ {
+ SvxTextForwarder* pForwarder = GetEditSource()->GetTextForwarder();
+ if(pForwarder)
+ return pForwarder->GetParagraphCount() != 0;
+ }
+
+ return false;
+}
+
+// text::XTextRangeMover
+void SAL_CALL SvxUnoTextBase::moveTextRange( const uno::Reference< text::XTextRange >&, sal_Int16 )
+{
+}
+
+/// @throws lang::IllegalArgumentException
+/// @throws beans::UnknownPropertyException
+/// @throws uno::RuntimeException
+static void SvxPropertyValuesToItemSet(
+ SfxItemSet &rItemSet,
+ const uno::Sequence< beans::PropertyValue >& rPropertyValues,
+ const SfxItemPropertySet *pPropSet,
+ SvxTextForwarder *pForwarder,
+ sal_Int32 nPara)
+{
+ for (const beans::PropertyValue& rProp : rPropertyValues)
+ {
+ const SfxItemPropertyMapEntry *pEntry = pPropSet->getPropertyMap().getByName( rProp.Name );
+ if (!pEntry)
+ throw beans::UnknownPropertyException( "Unknown property: " + rProp.Name );
+ // Note: there is no need to take special care of the properties
+ // TextField (EE_FEATURE_FIELD) and
+ // TextPortionType (WID_PORTIONTYPE)
+ // since they are read-only and thus are already taken care of below.
+
+ if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ // should be PropertyVetoException which is not yet defined for the new import API's functions
+ throw uno::RuntimeException("Property is read-only: " + rProp.Name );
+ //throw PropertyVetoException ("Property is read-only: " + rProp.Name );
+
+ if (pEntry->nWID == WID_FONTDESC)
+ {
+ awt::FontDescriptor aDesc;
+ if (rProp.Value >>= aDesc)
+ SvxUnoFontDescriptor::FillItemSet( aDesc, rItemSet );
+ }
+ else if (pEntry->nWID == WID_NUMBERINGSTARTVALUE )
+ {
+ if( pForwarder )
+ {
+ sal_Int16 nStartValue = -1;
+ if( !(rProp.Value >>= nStartValue) )
+ throw lang::IllegalArgumentException();
+
+ pForwarder->SetNumberingStartValue( nPara, nStartValue );
+ }
+ }
+ else if (pEntry->nWID == WID_PARAISNUMBERINGRESTART )
+ {
+ if( pForwarder )
+ {
+ bool bParaIsNumberingRestart = false;
+ if( !(rProp.Value >>= bParaIsNumberingRestart) )
+ throw lang::IllegalArgumentException();
+
+ pForwarder->SetParaIsNumberingRestart( nPara, bParaIsNumberingRestart );
+ }
+ }
+ else
+ pPropSet->setPropertyValue( rProp.Name, rProp.Value, rItemSet );
+ }
+}
+
+uno::Reference< text::XTextRange > SAL_CALL SvxUnoTextBase::finishParagraphInsert(
+ const uno::Sequence< beans::PropertyValue >& /*rCharAndParaProps*/,
+ const uno::Reference< text::XTextRange >& /*rTextRange*/ )
+{
+ uno::Reference< text::XTextRange > xRet;
+ return xRet;
+}
+
+uno::Reference< text::XTextRange > SAL_CALL SvxUnoTextBase::finishParagraph(
+ const uno::Sequence< beans::PropertyValue >& rCharAndParaProps )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference< text::XTextRange > xRet;
+ SvxEditSource *pEditSource = GetEditSource();
+ SvxTextForwarder *pTextForwarder = pEditSource ? pEditSource->GetTextForwarder() : nullptr;
+ if (pTextForwarder)
+ {
+ sal_Int32 nParaCount = pTextForwarder->GetParagraphCount();
+ DBG_ASSERT( nParaCount > 0, "paragraph count is 0 or negative" );
+ pTextForwarder->AppendParagraph();
+
+ // set properties for the previously last paragraph
+ sal_Int32 nPara = nParaCount - 1;
+ ESelection aSel( nPara, 0, nPara, 0 );
+ SfxItemSet aItemSet( *pTextForwarder->GetEmptyItemSetPtr() );
+ SvxPropertyValuesToItemSet( aItemSet, rCharAndParaProps,
+ ImplGetSvxUnoOutlinerTextCursorSfxPropertySet(), pTextForwarder, nPara );
+ pTextForwarder->QuickSetAttribs( aItemSet, aSel );
+ pEditSource->UpdateData();
+ rtl::Reference<SvxUnoTextRange> pRange = new SvxUnoTextRange( *this );
+ xRet = pRange;
+ pRange->SetSelection( aSel );
+ }
+ return xRet;
+}
+
+uno::Reference< text::XTextRange > SAL_CALL SvxUnoTextBase::insertTextPortion(
+ const OUString& rText,
+ const uno::Sequence< beans::PropertyValue >& rCharAndParaProps,
+ const uno::Reference< text::XTextRange>& rTextRange )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference< text::XTextRange > xRet;
+
+ if (!rTextRange.is())
+ return xRet;
+
+ SvxUnoTextRangeBase* pRange = comphelper::getFromUnoTunnel<SvxUnoTextRange>(rTextRange);
+ if (!pRange)
+ return xRet;
+
+ SvxEditSource *pEditSource = GetEditSource();
+ SvxTextForwarder *pTextForwarder = pEditSource ? pEditSource->GetTextForwarder() : nullptr;
+
+ if (pTextForwarder)
+ {
+ pRange->setString(rText);
+
+ ESelection aSelection(pRange->GetSelection());
+
+ pTextForwarder->RemoveAttribs(aSelection);
+ pEditSource->UpdateData();
+
+ SfxItemSet aItemSet( *pTextForwarder->GetEmptyItemSetPtr() );
+ SvxPropertyValuesToItemSet( aItemSet, rCharAndParaProps,
+ ImplGetSvxTextPortionSfxPropertySet(), pTextForwarder, aSelection.nStartPara );
+ pTextForwarder->QuickSetAttribs( aItemSet, aSelection);
+ rtl::Reference<SvxUnoTextRange> pNewRange = new SvxUnoTextRange( *this );
+ xRet = pNewRange;
+ pNewRange->SetSelection(aSelection);
+ for( const beans::PropertyValue& rProp : rCharAndParaProps )
+ pNewRange->setPropertyValue( rProp.Name, rProp.Value );
+ }
+ return xRet;
+}
+
+// css::text::XTextPortionAppend (new import API)
+uno::Reference< text::XTextRange > SAL_CALL SvxUnoTextBase::appendTextPortion(
+ const OUString& rText,
+ const uno::Sequence< beans::PropertyValue >& rCharAndParaProps )
+{
+ SolarMutexGuard aGuard;
+
+ SvxEditSource *pEditSource = GetEditSource();
+ SvxTextForwarder *pTextForwarder = pEditSource ? pEditSource->GetTextForwarder() : nullptr;
+ uno::Reference< text::XTextRange > xRet;
+ if (pTextForwarder)
+ {
+ sal_Int32 nParaCount = pTextForwarder->GetParagraphCount();
+ DBG_ASSERT( nParaCount > 0, "paragraph count is 0 or negative" );
+ sal_Int32 nPara = nParaCount - 1;
+ SfxItemSet aSet( pTextForwarder->GetParaAttribs( nPara ) );
+ sal_Int32 nStart = pTextForwarder->AppendTextPortion( nPara, rText, aSet );
+ pEditSource->UpdateData();
+ sal_Int32 nEnd = pTextForwarder->GetTextLen( nPara );
+
+ // set properties for the new text portion
+ ESelection aSel( nPara, nStart, nPara, nEnd );
+ pTextForwarder->RemoveAttribs( aSel );
+ pEditSource->UpdateData();
+
+ SfxItemSet aItemSet( *pTextForwarder->GetEmptyItemSetPtr() );
+ SvxPropertyValuesToItemSet( aItemSet, rCharAndParaProps,
+ ImplGetSvxTextPortionSfxPropertySet(), pTextForwarder, nPara );
+ pTextForwarder->QuickSetAttribs( aItemSet, aSel );
+ rtl::Reference<SvxUnoTextRange> pRange = new SvxUnoTextRange( *this );
+ xRet = pRange;
+ pRange->SetSelection( aSel );
+ for( const beans::PropertyValue& rProp : rCharAndParaProps )
+ pRange->setPropertyValue( rProp.Name, rProp.Value );
+ }
+ return xRet;
+}
+
+void SvxUnoTextBase::copyText(
+ const uno::Reference< text::XTextCopy >& xSource )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference< lang::XUnoTunnel > xUT( xSource, uno::UNO_QUERY );
+ SvxEditSource *pEditSource = GetEditSource();
+ SvxTextForwarder *pTextForwarder = pEditSource ? pEditSource->GetTextForwarder() : nullptr;
+ if( !pTextForwarder )
+ return;
+ if (auto pSource = comphelper::getFromUnoTunnel<SvxUnoTextBase>(xUT))
+ {
+ SvxEditSource *pSourceEditSource = pSource->GetEditSource();
+ SvxTextForwarder *pSourceTextForwarder = pSourceEditSource ? pSourceEditSource->GetTextForwarder() : nullptr;
+ if( pSourceTextForwarder )
+ {
+ pTextForwarder->CopyText( *pSourceTextForwarder );
+ pEditSource->UpdateData();
+ }
+ }
+ else
+ {
+ uno::Reference< text::XText > xSourceText( xSource, uno::UNO_QUERY );
+ if( xSourceText.is() )
+ {
+ setString( xSourceText->getString() );
+ }
+ }
+}
+
+// lang::XServiceInfo
+OUString SAL_CALL SvxUnoTextBase::getImplementationName()
+{
+ return "SvxUnoTextBase";
+}
+
+uno::Sequence< OUString > SAL_CALL SvxUnoTextBase::getSupportedServiceNames( )
+{
+ return getSupportedServiceNames_Static();
+}
+
+uno::Sequence< OUString > SAL_CALL SvxUnoTextBase::getSupportedServiceNames_Static( )
+{
+ return comphelper::concatSequences(
+ SvxUnoTextRangeBase::getSupportedServiceNames_Static(),
+ std::initializer_list<std::u16string_view>{ u"com.sun.star.text.Text" });
+}
+
+const uno::Sequence< sal_Int8 > & SvxUnoTextBase::getUnoTunnelId() noexcept
+{
+ static const comphelper::UnoIdInit theSvxUnoTextBaseUnoTunnelId;
+ return theSvxUnoTextBaseUnoTunnelId.getSeq();
+}
+
+sal_Int64 SAL_CALL SvxUnoTextBase::getSomething( const uno::Sequence< sal_Int8 >& rId )
+{
+ return comphelper::getSomethingImpl(
+ rId, this, comphelper::FallbackToGetSomethingOf<SvxUnoTextRangeBase>{});
+}
+
+SvxUnoText::SvxUnoText( const SvxItemPropertySet* _pSet ) noexcept
+: SvxUnoTextBase( _pSet )
+{
+}
+
+SvxUnoText::SvxUnoText( const SvxEditSource* pSource, const SvxItemPropertySet* _pSet, uno::Reference < text::XText > const & xParent ) noexcept
+: SvxUnoTextBase( pSource, _pSet, xParent )
+{
+}
+
+SvxUnoText::SvxUnoText( const SvxUnoText& rText ) noexcept
+: SvxUnoTextBase( rText )
+, cppu::OWeakAggObject()
+{
+}
+
+SvxUnoText::~SvxUnoText() noexcept
+{
+}
+
+// uno::XInterface
+uno::Any SAL_CALL SvxUnoText::queryAggregation( const uno::Type & rType )
+{
+ uno::Any aAny( SvxUnoTextBase::queryAggregation( rType ) );
+ if( !aAny.hasValue() )
+ aAny = OWeakAggObject::queryAggregation( rType );
+
+ return aAny;
+}
+
+uno::Any SAL_CALL SvxUnoText::queryInterface( const uno::Type & rType )
+{
+ return OWeakAggObject::queryInterface( rType );
+}
+
+void SAL_CALL SvxUnoText::acquire() noexcept
+{
+ OWeakAggObject::acquire();
+}
+
+void SAL_CALL SvxUnoText::release() noexcept
+{
+ OWeakAggObject::release();
+}
+
+// lang::XTypeProvider
+uno::Sequence< uno::Type > SAL_CALL SvxUnoText::getTypes( )
+{
+ return SvxUnoTextBase::getTypes();
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL SvxUnoText::getImplementationId( )
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+const uno::Sequence< sal_Int8 > & SvxUnoText::getUnoTunnelId() noexcept
+{
+ static const comphelper::UnoIdInit theSvxUnoTextUnoTunnelId;
+ return theSvxUnoTextUnoTunnelId.getSeq();
+}
+
+sal_Int64 SAL_CALL SvxUnoText::getSomething( const uno::Sequence< sal_Int8 >& rId )
+{
+ return comphelper::getSomethingImpl(rId, this,
+ comphelper::FallbackToGetSomethingOf<SvxUnoTextBase>{});
+}
+
+
+SvxDummyTextSource::~SvxDummyTextSource()
+{
+};
+
+std::unique_ptr<SvxEditSource> SvxDummyTextSource::Clone() const
+{
+ return std::unique_ptr<SvxEditSource>(new SvxDummyTextSource);
+}
+
+SvxTextForwarder* SvxDummyTextSource::GetTextForwarder()
+{
+ return this;
+}
+
+void SvxDummyTextSource::UpdateData()
+{
+}
+
+sal_Int32 SvxDummyTextSource::GetParagraphCount() const
+{
+ return 0;
+}
+
+sal_Int32 SvxDummyTextSource::GetTextLen( sal_Int32 ) const
+{
+ return 0;
+}
+
+OUString SvxDummyTextSource::GetText( const ESelection& ) const
+{
+ return OUString();
+}
+
+SfxItemSet SvxDummyTextSource::GetAttribs( const ESelection&, EditEngineAttribs ) const
+{
+ // Very dangerous: The former implementation used a SfxItemPool created on the
+ // fly which of course was deleted again ASAP. Thus, the returned SfxItemSet was using
+ // a deleted Pool by design.
+ return SfxItemSet(EditEngine::GetGlobalItemPool());
+}
+
+SfxItemSet SvxDummyTextSource::GetParaAttribs( sal_Int32 ) const
+{
+ return GetAttribs(ESelection());
+}
+
+void SvxDummyTextSource::SetParaAttribs( sal_Int32, const SfxItemSet& )
+{
+}
+
+void SvxDummyTextSource::RemoveAttribs( const ESelection& )
+{
+}
+
+void SvxDummyTextSource::GetPortions( sal_Int32, std::vector<sal_Int32>& ) const
+{
+}
+
+OUString SvxDummyTextSource::GetStyleSheet(sal_Int32) const
+{
+ return OUString();
+}
+
+void SvxDummyTextSource::SetStyleSheet(sal_Int32, const OUString&)
+{
+}
+
+SfxItemState SvxDummyTextSource::GetItemState( const ESelection&, sal_uInt16 ) const
+{
+ return SfxItemState::UNKNOWN;
+}
+
+SfxItemState SvxDummyTextSource::GetItemState( sal_Int32, sal_uInt16 ) const
+{
+ return SfxItemState::UNKNOWN;
+}
+
+SfxItemPool* SvxDummyTextSource::GetPool() const
+{
+ return nullptr;
+}
+
+void SvxDummyTextSource::QuickInsertText( const OUString&, const ESelection& )
+{
+}
+
+void SvxDummyTextSource::QuickInsertField( const SvxFieldItem&, const ESelection& )
+{
+}
+
+void SvxDummyTextSource::QuickSetAttribs( const SfxItemSet&, const ESelection& )
+{
+}
+
+void SvxDummyTextSource::QuickInsertLineBreak( const ESelection& )
+{
+};
+
+OUString SvxDummyTextSource::CalcFieldValue( const SvxFieldItem&, sal_Int32, sal_Int32, std::optional<Color>&, std::optional<Color>&, std::optional<FontLineStyle>& )
+{
+ return OUString();
+}
+
+void SvxDummyTextSource::FieldClicked( const SvxFieldItem& )
+{
+}
+
+bool SvxDummyTextSource::IsValid() const
+{
+ return false;
+}
+
+LanguageType SvxDummyTextSource::GetLanguage( sal_Int32, sal_Int32 ) const
+{
+ return LANGUAGE_DONTKNOW;
+}
+
+sal_Int32 SvxDummyTextSource::GetFieldCount( sal_Int32 ) const
+{
+ return 0;
+}
+
+EFieldInfo SvxDummyTextSource::GetFieldInfo( sal_Int32, sal_uInt16 ) const
+{
+ return EFieldInfo();
+}
+
+EBulletInfo SvxDummyTextSource::GetBulletInfo( sal_Int32 ) const
+{
+ return EBulletInfo();
+}
+
+tools::Rectangle SvxDummyTextSource::GetCharBounds( sal_Int32, sal_Int32 ) const
+{
+ return tools::Rectangle();
+}
+
+tools::Rectangle SvxDummyTextSource::GetParaBounds( sal_Int32 ) const
+{
+ return tools::Rectangle();
+}
+
+MapMode SvxDummyTextSource::GetMapMode() const
+{
+ return MapMode();
+}
+
+OutputDevice* SvxDummyTextSource::GetRefDevice() const
+{
+ return nullptr;
+}
+
+bool SvxDummyTextSource::GetIndexAtPoint( const Point&, sal_Int32&, sal_Int32& ) const
+{
+ return false;
+}
+
+bool SvxDummyTextSource::GetWordIndices( sal_Int32, sal_Int32, sal_Int32&, sal_Int32& ) const
+{
+ return false;
+}
+
+bool SvxDummyTextSource::GetAttributeRun( sal_Int32&, sal_Int32&, sal_Int32, sal_Int32, bool ) const
+{
+ return false;
+}
+
+sal_Int32 SvxDummyTextSource::GetLineCount( sal_Int32 ) const
+{
+ return 0;
+}
+
+sal_Int32 SvxDummyTextSource::GetLineLen( sal_Int32, sal_Int32 ) const
+{
+ return 0;
+}
+
+void SvxDummyTextSource::GetLineBoundaries( /*out*/sal_Int32 &rStart, /*out*/sal_Int32 &rEnd, sal_Int32 /*nParagraph*/, sal_Int32 /*nLine*/ ) const
+{
+ rStart = rEnd = 0;
+}
+
+sal_Int32 SvxDummyTextSource::GetLineNumberAtIndex( sal_Int32 /*nPara*/, sal_Int32 /*nIndex*/ ) const
+{
+ return 0;
+}
+
+bool SvxDummyTextSource::QuickFormatDoc( bool )
+{
+ return false;
+}
+
+sal_Int16 SvxDummyTextSource::GetDepth( sal_Int32 ) const
+{
+ return -1;
+}
+
+bool SvxDummyTextSource::SetDepth( sal_Int32, sal_Int16 nNewDepth )
+{
+ return nNewDepth == 0;
+}
+
+bool SvxDummyTextSource::Delete( const ESelection& )
+{
+ return false;
+}
+
+bool SvxDummyTextSource::InsertText( const OUString&, const ESelection& )
+{
+ return false;
+}
+
+const SfxItemSet * SvxDummyTextSource::GetEmptyItemSetPtr()
+{
+ return nullptr;
+}
+
+void SvxDummyTextSource::AppendParagraph()
+{
+}
+
+sal_Int32 SvxDummyTextSource::AppendTextPortion( sal_Int32, const OUString &, const SfxItemSet & )
+{
+ return 0;
+}
+
+void SvxDummyTextSource::CopyText(const SvxTextForwarder& )
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/uno/unotext2.cxx b/editeng/source/uno/unotext2.cxx
new file mode 100644
index 0000000000..54714027b3
--- /dev/null
+++ b/editeng/source/uno/unotext2.cxx
@@ -0,0 +1,629 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <initializer_list>
+#include <string_view>
+
+#include <o3tl/safeint.hxx>
+#include <vcl/svapp.hxx>
+
+#include <editeng/unotext.hxx>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+using namespace ::cppu;
+using namespace ::com::sun::star;
+
+#define QUERYINT( xint ) \
+ if( rType == cppu::UnoType<xint>::get() ) \
+ return uno::Any(uno::Reference< xint >(this))
+
+
+// SvxUnoTextContentEnumeration
+
+
+SvxUnoTextContentEnumeration::SvxUnoTextContentEnumeration( const SvxUnoTextBase& rText, const ESelection& rSel ) noexcept
+{
+ mxParentText = const_cast<SvxUnoTextBase*>(&rText);
+ if( rText.GetEditSource() )
+ mpEditSource = rText.GetEditSource()->Clone();
+ mnNextParagraph = 0;
+
+ if (!mpEditSource)
+ return;
+
+ const SvxTextForwarder* pTextForwarder = rText.GetEditSource()->GetTextForwarder();
+ const sal_Int32 maxParaIndex = std::min( rSel.nEndPara + 1, pTextForwarder->GetParagraphCount() );
+
+ for( sal_Int32 currentPara = rSel.nStartPara; currentPara < maxParaIndex; currentPara++ )
+ {
+ const SvxUnoTextRangeBaseVec& rRanges( mpEditSource->getRanges() );
+ rtl::Reference<SvxUnoTextContent> pContent;
+ sal_Int32 nStartPos = 0;
+ sal_Int32 nEndPos = pTextForwarder->GetTextLen( currentPara );
+ if( currentPara == rSel.nStartPara )
+ nStartPos = std::max(nStartPos, rSel.nStartPos);
+ if( currentPara == rSel.nEndPara )
+ nEndPos = std::min(nEndPos, rSel.nEndPos);
+ ESelection aCurrentParaSel( currentPara, nStartPos, currentPara, nEndPos );
+ for (auto const& elemRange : rRanges)
+ {
+ if (pContent)
+ break;
+ SvxUnoTextContent* pIterContent = dynamic_cast< SvxUnoTextContent* >( elemRange );
+ if( pIterContent && (pIterContent->mnParagraph == currentPara) )
+ {
+ ESelection aIterSel = pIterContent->GetSelection();
+ if( aIterSel == aCurrentParaSel )
+ {
+ pContent = pIterContent;
+ maContents.emplace_back(pContent );
+ }
+ }
+ }
+ if( pContent == nullptr )
+ {
+ pContent = new SvxUnoTextContent( rText, currentPara );
+ pContent->SetSelection( aCurrentParaSel );
+ maContents.emplace_back(pContent );
+ }
+ }
+}
+
+SvxUnoTextContentEnumeration::~SvxUnoTextContentEnumeration() noexcept
+{
+}
+
+// container::XEnumeration
+sal_Bool SAL_CALL SvxUnoTextContentEnumeration::hasMoreElements()
+{
+ SolarMutexGuard aGuard;
+ if( mpEditSource && !maContents.empty() )
+ return o3tl::make_unsigned(mnNextParagraph) < maContents.size();
+ else
+ return false;
+}
+
+uno::Any SvxUnoTextContentEnumeration::nextElement()
+{
+ SolarMutexGuard aGuard;
+
+ if(!hasMoreElements())
+ throw container::NoSuchElementException();
+
+ uno::Reference< text::XTextContent > xRef( maContents.at(mnNextParagraph) );
+ mnNextParagraph++;
+ return uno::Any( xRef );
+}
+
+
+
+
+SvxUnoTextContent::SvxUnoTextContent( const SvxUnoTextBase& rText, sal_Int32 nPara ) noexcept
+: SvxUnoTextRangeBase(rText)
+, mnParagraph(nPara)
+, mrParentText(rText)
+, mbDisposing( false )
+{
+ mxParentText = const_cast<SvxUnoTextBase*>(&rText);
+}
+
+SvxUnoTextContent::SvxUnoTextContent( const SvxUnoTextContent& rContent ) noexcept
+: SvxUnoTextRangeBase(rContent)
+, text::XTextContent()
+, container::XEnumerationAccess()
+, lang::XTypeProvider()
+, cppu::OWeakAggObject()
+, mrParentText(rContent.mrParentText)
+, mbDisposing( false )
+{
+ mxParentText = rContent.mxParentText;
+ mnParagraph = rContent.mnParagraph;
+ SetSelection( rContent.GetSelection() );
+}
+
+SvxUnoTextContent::~SvxUnoTextContent() noexcept
+{
+}
+
+// uno::XInterface
+uno::Any SAL_CALL SvxUnoTextContent::queryAggregation( const uno::Type & rType )
+{
+ QUERYINT( text::XTextRange );
+ else QUERYINT( beans::XMultiPropertyStates );
+ else QUERYINT( beans::XPropertySet );
+ else QUERYINT( beans::XMultiPropertySet );
+ else QUERYINT( beans::XPropertyState );
+ else QUERYINT( text::XTextContent );
+ else QUERYINT( text::XTextRangeCompare );
+ else QUERYINT( lang::XComponent );
+ else QUERYINT( container::XEnumerationAccess );
+ else QUERYINT( container::XElementAccess );
+ else QUERYINT( lang::XServiceInfo );
+ else QUERYINT( lang::XTypeProvider );
+ else QUERYINT( lang::XUnoTunnel );
+ else
+ return OWeakAggObject::queryAggregation( rType );
+}
+
+uno::Any SAL_CALL SvxUnoTextContent::queryInterface( const uno::Type & rType )
+{
+ return OWeakAggObject::queryInterface(rType);
+}
+
+void SAL_CALL SvxUnoTextContent::acquire() noexcept
+{
+ OWeakAggObject::acquire();
+}
+
+void SAL_CALL SvxUnoTextContent::release() noexcept
+{
+ OWeakAggObject::release();
+}
+
+// XTypeProvider
+
+uno::Sequence< uno::Type > SAL_CALL SvxUnoTextContent::getTypes()
+{
+ static const uno::Sequence< uno::Type > TYPES {
+ cppu::UnoType<text::XTextRange>::get(),
+ cppu::UnoType<beans::XPropertySet>::get(),
+ cppu::UnoType<beans::XMultiPropertySet>::get(),
+ cppu::UnoType<beans::XMultiPropertyStates>::get(),
+ cppu::UnoType<beans::XPropertyState>::get(),
+ cppu::UnoType<text::XTextRangeCompare>::get(),
+ cppu::UnoType<text::XTextContent>::get(),
+ cppu::UnoType<container::XEnumerationAccess>::get(),
+ cppu::UnoType<lang::XServiceInfo>::get(),
+ cppu::UnoType<lang::XTypeProvider>::get(),
+ cppu::UnoType<lang::XUnoTunnel>::get() };
+ return TYPES;
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL SvxUnoTextContent::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// text::XTextRange
+
+uno::Reference< text::XText > SAL_CALL SvxUnoTextContent::getText()
+{
+ return mxParentText;
+}
+
+// text::XTextContent
+void SAL_CALL SvxUnoTextContent::attach( const uno::Reference< text::XTextRange >& )
+{
+}
+
+uno::Reference< text::XTextRange > SAL_CALL SvxUnoTextContent::getAnchor()
+{
+ return mxParentText;
+}
+
+// XComponent
+
+void SAL_CALL SvxUnoTextContent::dispose()
+{
+ SolarMutexGuard aGuard;
+
+ if( mbDisposing )
+ return; // caught a recursion
+
+ mbDisposing = true;
+
+ lang::EventObject aEvt;
+ aEvt.Source = *static_cast<OWeakAggObject*>(this);
+ {
+ std::unique_lock aMutexGuard(maDisposeContainerMutex);
+ maDisposeListeners.disposeAndClear(aMutexGuard, aEvt);
+ }
+
+ if( mxParentText.is() )
+ {
+ mxParentText->removeTextContent( this );
+ mxParentText.clear();
+ }
+}
+
+void SAL_CALL SvxUnoTextContent::addEventListener( const uno::Reference< lang::XEventListener >& xListener )
+{
+ std::unique_lock aGuard(maDisposeContainerMutex);
+ maDisposeListeners.addInterface(aGuard, xListener);
+}
+
+void SAL_CALL SvxUnoTextContent::removeEventListener( const uno::Reference< lang::XEventListener >& aListener )
+{
+ std::unique_lock aGuard(maDisposeContainerMutex);
+ maDisposeListeners.removeInterface(aGuard, aListener);
+}
+
+// XEnumerationAccess
+
+uno::Reference< container::XEnumeration > SAL_CALL SvxUnoTextContent::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+
+ return new SvxUnoTextRangeEnumeration( mrParentText, mnParagraph, maSelection );
+}
+
+// XElementAccess ( container::XEnumerationAccess )
+
+uno::Type SAL_CALL SvxUnoTextContent::getElementType()
+{
+ return cppu::UnoType<text::XTextRange>::get();
+}
+
+sal_Bool SAL_CALL SvxUnoTextContent::hasElements()
+{
+ SolarMutexGuard aGuard;
+
+ SvxTextForwarder* pForwarder = GetEditSource() ? GetEditSource()->GetTextForwarder() : nullptr;
+ if( pForwarder )
+ {
+ std::vector<sal_Int32> aPortions;
+ pForwarder->GetPortions( mnParagraph, aPortions );
+ return !aPortions.empty();
+ }
+ else
+ {
+ return false;
+ }
+}
+
+// XPropertySet
+
+void SAL_CALL SvxUnoTextContent::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
+{
+ _setPropertyValue( aPropertyName, aValue, mnParagraph );
+}
+
+uno::Any SAL_CALL SvxUnoTextContent::getPropertyValue( const OUString& PropertyName )
+{
+ return _getPropertyValue( PropertyName, mnParagraph );
+}
+
+// XMultiPropertySet
+void SAL_CALL SvxUnoTextContent::setPropertyValues( const uno::Sequence< OUString >& aPropertyNames, const uno::Sequence< uno::Any >& aValues )
+{
+ _setPropertyValues( aPropertyNames, aValues, mnParagraph );
+}
+
+uno::Sequence< uno::Any > SAL_CALL SvxUnoTextContent::getPropertyValues( const uno::Sequence< OUString >& aPropertyNames )
+{
+ return _getPropertyValues( aPropertyNames, mnParagraph );
+}
+
+/*// XTolerantMultiPropertySet
+uno::Sequence< beans::SetPropertyTolerantFailed > SAL_CALL SvxUnoTextContent::setPropertyValuesTolerant( const uno::Sequence< OUString >& aPropertyNames, const uno::Sequence< uno::Any >& aValues ) throw (lang::IllegalArgumentException, uno::RuntimeException)
+{
+ return _setPropertyValuesTolerant(aPropertyNames, aValues, mnParagraph);
+}
+
+uno::Sequence< beans::GetPropertyTolerantResult > SAL_CALL SvxUnoTextContent::getPropertyValuesTolerant( const uno::Sequence< OUString >& aPropertyNames ) throw (uno::RuntimeException)
+{
+ return _getPropertyValuesTolerant(aPropertyNames, mnParagraph);
+}
+
+uno::Sequence< beans::GetDirectPropertyTolerantResult > SAL_CALL SvxUnoTextContent::getDirectPropertyValuesTolerant( const uno::Sequence< OUString >& aPropertyNames )
+ throw (uno::RuntimeException)
+{
+ return _getDirectPropertyValuesTolerant(aPropertyNames, mnParagraph);
+}*/
+
+// beans::XPropertyState
+beans::PropertyState SAL_CALL SvxUnoTextContent::getPropertyState( const OUString& PropertyName )
+{
+ return _getPropertyState( PropertyName, mnParagraph );
+}
+
+uno::Sequence< beans::PropertyState > SAL_CALL SvxUnoTextContent::getPropertyStates( const uno::Sequence< OUString >& aPropertyName )
+{
+ return _getPropertyStates( aPropertyName, mnParagraph );
+}
+
+void SAL_CALL SvxUnoTextContent::setPropertyToDefault( const OUString& PropertyName )
+{
+ _setPropertyToDefault( PropertyName, mnParagraph );
+}
+
+// lang::XServiceInfo
+
+OUString SAL_CALL SvxUnoTextContent::getImplementationName()
+{
+ return "SvxUnoTextContent";
+}
+
+uno::Sequence< OUString > SAL_CALL SvxUnoTextContent::getSupportedServiceNames()
+{
+ return comphelper::concatSequences(
+ SvxUnoTextRangeBase::getSupportedServiceNames(),
+ std::initializer_list<std::u16string_view>{ u"com.sun.star.style.ParagraphProperties",
+ u"com.sun.star.style.ParagraphPropertiesComplex",
+ u"com.sun.star.style.ParagraphPropertiesAsian",
+ u"com.sun.star.text.TextContent",
+ u"com.sun.star.text.Paragraph" });
+}
+
+
+
+
+SvxUnoTextRangeEnumeration::SvxUnoTextRangeEnumeration(const SvxUnoTextBase& rParentText, sal_Int32 nParagraph, const ESelection& rSel)
+: mxParentText( const_cast<SvxUnoTextBase*>(&rParentText) ),
+ mnNextPortion( 0 )
+{
+ if (rParentText.GetEditSource())
+ mpEditSource = rParentText.GetEditSource()->Clone();
+
+ if( !(mpEditSource && mpEditSource->GetTextForwarder() && (nParagraph == rSel.nStartPara && nParagraph == rSel.nEndPara)) )
+ return;
+
+ std::vector<sal_Int32> aPortions;
+ mpEditSource->GetTextForwarder()->GetPortions( nParagraph, aPortions );
+ for( size_t aPortionIndex = 0; aPortionIndex < aPortions.size(); aPortionIndex++ )
+ {
+ sal_uInt16 nStartPos = 0;
+ if ( aPortionIndex > 0 )
+ nStartPos = aPortions.at( aPortionIndex - 1 );
+ if( nStartPos > rSel.nEndPos )
+ continue;
+ sal_uInt16 nEndPos = aPortions.at( aPortionIndex );
+ if( nEndPos < rSel.nStartPos )
+ continue;
+
+ nStartPos = std::max<int>(nStartPos, rSel.nStartPos);
+ nEndPos = std::min<sal_uInt16>(nEndPos, rSel.nEndPos);
+ ESelection aSel( nParagraph, nStartPos, nParagraph, nEndPos );
+
+ const SvxUnoTextRangeBaseVec& rRanges( mpEditSource->getRanges() );
+ rtl::Reference<SvxUnoTextRange> pRange;
+ for (auto const& elemRange : rRanges)
+ {
+ if (pRange)
+ break;
+ SvxUnoTextRange* pIterRange = dynamic_cast< SvxUnoTextRange* >( elemRange );
+ if( pIterRange && pIterRange->mbPortion && (aSel == pIterRange->maSelection) )
+ pRange = pIterRange;
+ }
+ if( pRange == nullptr )
+ {
+ pRange = new SvxUnoTextRange( rParentText, true );
+ pRange->SetSelection( aSel );
+ }
+ maPortions.emplace_back(pRange );
+ }
+}
+
+SvxUnoTextRangeEnumeration::~SvxUnoTextRangeEnumeration() noexcept
+{
+}
+
+// container::XEnumeration
+
+sal_Bool SAL_CALL SvxUnoTextRangeEnumeration::hasMoreElements()
+{
+ SolarMutexGuard aGuard;
+
+ return !maPortions.empty() && mnNextPortion < maPortions.size();
+}
+
+uno::Any SAL_CALL SvxUnoTextRangeEnumeration::nextElement()
+{
+ SolarMutexGuard aGuard;
+
+ if( maPortions.empty() || mnNextPortion >= maPortions.size() )
+ throw container::NoSuchElementException();
+
+ uno::Reference< text::XTextRange > xRange = maPortions.at(mnNextPortion);
+ mnNextPortion++;
+ return uno::Any( xRange );
+}
+
+SvxUnoTextCursor::SvxUnoTextCursor( const SvxUnoTextBase& rText ) noexcept
+: SvxUnoTextRangeBase(rText),
+ mxParentText( const_cast<SvxUnoTextBase*>(&rText) )
+{
+}
+
+SvxUnoTextCursor::SvxUnoTextCursor( const SvxUnoTextCursor& rCursor ) noexcept
+: SvxUnoTextRangeBase(rCursor)
+, text::XTextCursor()
+, lang::XTypeProvider()
+, cppu::OWeakAggObject()
+, mxParentText(rCursor.mxParentText)
+{
+}
+
+SvxUnoTextCursor::~SvxUnoTextCursor() noexcept
+{
+}
+
+// Comment out automatically - [getIdlClass(es) or queryInterface]
+// Please use the XTypeProvider!
+//sal_Bool SvxUnoTextCursor::queryInterface( uno::Uik aUIK, Reference< uno::XInterface > & xRef)
+uno::Any SAL_CALL SvxUnoTextCursor::queryAggregation( const uno::Type & rType )
+{
+ if( rType == cppu::UnoType<text::XTextRange>::get())
+ return uno::Any(uno::Reference< text::XTextRange >(static_cast<SvxUnoTextRangeBase *>(this)));
+ else QUERYINT( text::XTextCursor );
+ else QUERYINT( beans::XMultiPropertyStates );
+ else QUERYINT( beans::XPropertySet );
+ else QUERYINT( beans::XMultiPropertySet );
+ else QUERYINT( beans::XPropertyState );
+ else QUERYINT( text::XTextRangeCompare );
+ else QUERYINT( lang::XServiceInfo );
+ else QUERYINT( lang::XTypeProvider );
+ else QUERYINT( lang::XUnoTunnel );
+ else
+ return OWeakAggObject::queryAggregation( rType );
+}
+
+uno::Any SAL_CALL SvxUnoTextCursor::queryInterface( const uno::Type & rType )
+{
+ return OWeakAggObject::queryInterface(rType);
+}
+
+void SAL_CALL SvxUnoTextCursor::acquire() noexcept
+{
+ OWeakAggObject::acquire();
+}
+
+void SAL_CALL SvxUnoTextCursor::release() noexcept
+{
+ OWeakAggObject::release();
+}
+
+// XTypeProvider
+uno::Sequence< uno::Type > SAL_CALL SvxUnoTextCursor::getTypes()
+{
+ static const uno::Sequence< uno::Type > TYPES {
+ cppu::UnoType<text::XTextRange>::get(),
+ cppu::UnoType<text::XTextCursor>::get(),
+ cppu::UnoType<beans::XPropertySet>::get(),
+ cppu::UnoType<beans::XMultiPropertySet>::get(),
+ cppu::UnoType<beans::XMultiPropertyStates>::get(),
+ cppu::UnoType<beans::XPropertyState>::get(),
+ cppu::UnoType<text::XTextRangeCompare>::get(),
+ cppu::UnoType<lang::XServiceInfo>::get(),
+ cppu::UnoType<lang::XTypeProvider>::get(),
+ cppu::UnoType<lang::XUnoTunnel>::get() };
+ return TYPES;
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL SvxUnoTextCursor::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// text::XTextCursor
+void SAL_CALL SvxUnoTextCursor::collapseToStart()
+{
+ SolarMutexGuard aGuard;
+ CollapseToStart();
+}
+
+void SAL_CALL SvxUnoTextCursor::collapseToEnd()
+{
+ SolarMutexGuard aGuard;
+ CollapseToEnd();
+}
+
+sal_Bool SAL_CALL SvxUnoTextCursor::isCollapsed()
+{
+ SolarMutexGuard aGuard;
+ return IsCollapsed();
+}
+
+sal_Bool SAL_CALL SvxUnoTextCursor::goLeft( sal_Int16 nCount, sal_Bool bExpand )
+{
+ SolarMutexGuard aGuard;
+ return GoLeft( nCount, bExpand );
+}
+
+sal_Bool SAL_CALL SvxUnoTextCursor::goRight( sal_Int16 nCount, sal_Bool bExpand )
+{
+ SolarMutexGuard aGuard;
+ return GoRight( nCount, bExpand );
+}
+
+void SAL_CALL SvxUnoTextCursor::gotoStart( sal_Bool bExpand )
+{
+ SolarMutexGuard aGuard;
+ GotoStart( bExpand );
+}
+
+void SAL_CALL SvxUnoTextCursor::gotoEnd( sal_Bool bExpand )
+{
+ SolarMutexGuard aGuard;
+ GotoEnd( bExpand );
+}
+
+void SAL_CALL SvxUnoTextCursor::gotoRange( const uno::Reference< text::XTextRange >& xRange, sal_Bool bExpand )
+{
+ if( !xRange.is() )
+ return;
+
+ SvxUnoTextRangeBase* pRange = comphelper::getFromUnoTunnel<SvxUnoTextRangeBase>( xRange );
+
+ if( !pRange )
+ return;
+
+ ESelection aNewSel = pRange->GetSelection();
+
+ if( bExpand )
+ {
+ const ESelection& rOldSel = GetSelection();
+ aNewSel.nStartPara = rOldSel.nStartPara;
+ aNewSel.nStartPos = rOldSel.nStartPos;
+ }
+
+ SetSelection( aNewSel );
+}
+
+// text::XTextRange (rest in SvxTextRange)
+uno::Reference< text::XText > SAL_CALL SvxUnoTextCursor::getText()
+{
+ return mxParentText;
+}
+
+uno::Reference< text::XTextRange > SAL_CALL SvxUnoTextCursor::getStart()
+{
+ return SvxUnoTextRangeBase::getStart();
+}
+
+uno::Reference< text::XTextRange > SAL_CALL SvxUnoTextCursor::getEnd()
+{
+ return SvxUnoTextRangeBase::getEnd();
+}
+
+OUString SAL_CALL SvxUnoTextCursor::getString()
+{
+ return SvxUnoTextRangeBase::getString();
+}
+
+void SAL_CALL SvxUnoTextCursor::setString( const OUString& aString )
+{
+ SvxUnoTextRangeBase::setString(aString);
+}
+// lang::XServiceInfo
+OUString SAL_CALL SvxUnoTextCursor::getImplementationName()
+{
+ return "SvxUnoTextCursor";
+}
+
+sal_Bool SAL_CALL SvxUnoTextCursor::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService( this, ServiceName );
+}
+
+uno::Sequence< OUString > SAL_CALL SvxUnoTextCursor::getSupportedServiceNames()
+{
+ return comphelper::concatSequences(
+ SvxUnoTextRangeBase::getSupportedServiceNames(),
+ std::initializer_list<std::u16string_view>{ u"com.sun.star.style.ParagraphProperties",
+ u"com.sun.star.style.ParagraphPropertiesComplex",
+ u"com.sun.star.style.ParagraphPropertiesAsian",
+ u"com.sun.star.text.TextCursor" });
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/uno/unoviwou.cxx b/editeng/source/uno/unoviwou.cxx
new file mode 100644
index 0000000000..19f38794e8
--- /dev/null
+++ b/editeng/source/uno/unoviwou.cxx
@@ -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 .
+ */
+
+#include <vcl/outdev.hxx>
+#include <vcl/window.hxx>
+
+#include <editeng/unoviwou.hxx>
+#include <editeng/outliner.hxx>
+
+SvxDrawOutlinerViewForwarder::SvxDrawOutlinerViewForwarder( OutlinerView& rOutl ) :
+ mrOutlinerView ( rOutl )
+{
+}
+
+SvxDrawOutlinerViewForwarder::SvxDrawOutlinerViewForwarder( OutlinerView& rOutl, const Point& rShapePosTopLeft ) :
+ mrOutlinerView ( rOutl ), maTextShapeTopLeft( rShapePosTopLeft )
+{
+}
+
+SvxDrawOutlinerViewForwarder::~SvxDrawOutlinerViewForwarder()
+{
+}
+
+Point SvxDrawOutlinerViewForwarder::GetTextOffset() const
+{
+ // calc text offset from shape anchor
+ tools::Rectangle aOutputRect( mrOutlinerView.GetOutputArea() );
+
+ return aOutputRect.TopLeft() - maTextShapeTopLeft;
+}
+
+bool SvxDrawOutlinerViewForwarder::IsValid() const
+{
+ return true;
+}
+
+Point SvxDrawOutlinerViewForwarder::LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ OutputDevice* pOutDev = mrOutlinerView.GetWindow()->GetOutDev();
+
+ if( pOutDev )
+ {
+ Point aPoint1( rPoint );
+ Point aTextOffset( GetTextOffset() );
+
+ aPoint1.AdjustX(aTextOffset.X() );
+ aPoint1.AdjustY(aTextOffset.Y() );
+
+ MapMode aMapMode(pOutDev->GetMapMode());
+ Point aPoint2( OutputDevice::LogicToLogic( aPoint1, rMapMode,
+ MapMode(aMapMode.GetMapUnit())));
+ aMapMode.SetOrigin(Point());
+ return pOutDev->LogicToPixel( aPoint2, aMapMode );
+ }
+
+ return Point();
+}
+
+Point SvxDrawOutlinerViewForwarder::PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ OutputDevice* pOutDev = mrOutlinerView.GetWindow()->GetOutDev();
+
+ if( pOutDev )
+ {
+ MapMode aMapMode(pOutDev->GetMapMode());
+ aMapMode.SetOrigin(Point());
+ Point aPoint1( pOutDev->PixelToLogic( rPoint, aMapMode ) );
+ Point aPoint2( OutputDevice::LogicToLogic( aPoint1,
+ MapMode(aMapMode.GetMapUnit()),
+ rMapMode ) );
+ Point aTextOffset( GetTextOffset() );
+
+ aPoint2.AdjustX( -(aTextOffset.X()) );
+ aPoint2.AdjustY( -(aTextOffset.Y()) );
+
+ return aPoint2;
+ }
+
+ return Point();
+}
+
+bool SvxDrawOutlinerViewForwarder::GetSelection( ESelection& rSelection ) const
+{
+ rSelection = mrOutlinerView.GetSelection();
+ return true;
+}
+
+bool SvxDrawOutlinerViewForwarder::SetSelection( const ESelection& rSelection )
+{
+ mrOutlinerView.SetSelection( rSelection );
+ return true;
+}
+
+bool SvxDrawOutlinerViewForwarder::Copy()
+{
+ mrOutlinerView.Copy();
+ return true;
+}
+
+bool SvxDrawOutlinerViewForwarder::Cut()
+{
+ mrOutlinerView.Cut();
+ return true;
+}
+
+bool SvxDrawOutlinerViewForwarder::Paste()
+{
+ mrOutlinerView.PasteSpecial();
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/xml/editsource.hxx b/editeng/source/xml/editsource.hxx
new file mode 100644
index 0000000000..726ddabc8c
--- /dev/null
+++ b/editeng/source/xml/editsource.hxx
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <editeng/unoedsrc.hxx>
+
+class EditEngine;
+class SvxEditEngineSourceImpl;
+
+class SvxEditEngineSource : public SvxEditSource
+{
+public:
+ explicit SvxEditEngineSource( EditEngine* pEditEngine );
+ virtual ~SvxEditEngineSource() override;
+
+ virtual std::unique_ptr<SvxEditSource> Clone() const override;
+ virtual SvxTextForwarder* GetTextForwarder() override;
+ virtual void UpdateData() override;
+
+private:
+ explicit SvxEditEngineSource( SvxEditEngineSourceImpl* pImpl );
+
+ rtl::Reference<SvxEditEngineSourceImpl> mxImpl;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/xml/xmltxtexp.cxx b/editeng/source/xml/xmltxtexp.cxx
new file mode 100644
index 0000000000..444a435c3d
--- /dev/null
+++ b/editeng/source/xml/xmltxtexp.cxx
@@ -0,0 +1,354 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+/** this file implements an export of a selected EditEngine content into
+ a xml stream. See editeng/source/inc/xmledit.hxx for interface */
+#include <memory>
+#include <com/sun/star/ucb/XAnyCompareFactory.hpp>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <svl/itemprop.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <xmloff/xmlmetae.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <unotools/streamwrap.hxx>
+#include <xmloff/xmlexp.hxx>
+#include <editeng/unoedsrc.hxx>
+#include <editeng/unofored.hxx>
+#include <editeng/unotext.hxx>
+#include <editeng/unoprnms.hxx>
+#include <editeng/unofield.hxx>
+#include <editeng/editeng.hxx>
+#include "editsource.hxx"
+#include <editxml.hxx>
+#include <editeng/unonrule.hxx>
+#include <editeng/unoipset.hxx>
+#include <unomodel.hxx>
+
+using namespace com::sun::star;
+using namespace com::sun::star::container;
+using namespace com::sun::star::document;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::awt;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::xml::sax;
+using namespace cppu;
+
+class SvxEditEngineSourceImpl;
+
+class SvxEditEngineSourceImpl : public salhelper::SimpleReferenceObject
+{
+private:
+ EditEngine* mpEditEngine;
+ std::unique_ptr<SvxTextForwarder> mpTextForwarder;
+
+ virtual ~SvxEditEngineSourceImpl() override;
+
+public:
+ explicit SvxEditEngineSourceImpl( EditEngine* pEditEngine );
+
+ SvxTextForwarder* GetTextForwarder();
+};
+
+SvxEditEngineSourceImpl::SvxEditEngineSourceImpl( EditEngine* pEditEngine )
+: mpEditEngine( pEditEngine )
+{
+}
+
+SvxEditEngineSourceImpl::~SvxEditEngineSourceImpl()
+{
+}
+
+SvxTextForwarder* SvxEditEngineSourceImpl::GetTextForwarder()
+{
+ if (!mpTextForwarder)
+ mpTextForwarder.reset( new SvxEditEngineForwarder( *mpEditEngine ) );
+
+ return mpTextForwarder.get();
+}
+
+// SvxTextEditSource
+SvxEditEngineSource::SvxEditEngineSource( EditEngine* pEditEngine )
+ : mxImpl( new SvxEditEngineSourceImpl( pEditEngine ) )
+{
+}
+
+SvxEditEngineSource::SvxEditEngineSource( SvxEditEngineSourceImpl* pImpl )
+ : mxImpl(pImpl)
+{
+}
+
+SvxEditEngineSource::~SvxEditEngineSource()
+{
+}
+
+std::unique_ptr<SvxEditSource> SvxEditEngineSource::Clone() const
+{
+ return std::unique_ptr<SvxEditSource>(new SvxEditEngineSource( mxImpl.get() ));
+}
+
+SvxTextForwarder* SvxEditEngineSource::GetTextForwarder()
+{
+ return mxImpl->GetTextForwarder();
+}
+
+
+void SvxEditEngineSource::UpdateData()
+{
+}
+
+
+SvxSimpleUnoModel::SvxSimpleUnoModel()
+{
+}
+
+// XMultiServiceFactory ( SvxFmMSFactory )
+uno::Reference< uno::XInterface > SAL_CALL SvxSimpleUnoModel::createInstance( const OUString& aServiceSpecifier )
+{
+ if( aServiceSpecifier == "com.sun.star.text.NumberingRules" )
+ {
+ return uno::Reference< uno::XInterface >(
+ SvxCreateNumRule(), uno::UNO_QUERY );
+ }
+ if ( aServiceSpecifier == "com.sun.star.text.textfield.DateTime"
+ || aServiceSpecifier == "com.sun.star.text.TextField.DateTime"
+ )
+ {
+ return cppu::getXWeak(new SvxUnoTextField( text::textfield::Type::DATE ));
+ }
+
+ if( aServiceSpecifier == "com.sun.star.text.TextField.URL" )
+ {
+ return cppu::getXWeak(new SvxUnoTextField(text::textfield::Type::URL));
+ }
+
+ return SvxUnoTextCreateTextField( aServiceSpecifier );
+
+}
+
+uno::Reference< css::uno::XInterface > SAL_CALL SvxSimpleUnoModel::createInstanceWithArguments( const OUString& ServiceSpecifier, const css::uno::Sequence< css::uno::Any >& )
+{
+ return createInstance( ServiceSpecifier );
+}
+
+Sequence< OUString > SAL_CALL SvxSimpleUnoModel::getAvailableServiceNames( )
+{
+ Sequence< OUString > aSeq;
+ return aSeq;
+}
+
+// XAnyCompareFactory
+uno::Reference< css::ucb::XAnyCompare > SAL_CALL SvxSimpleUnoModel::createAnyCompareByName( const OUString& )
+{
+ return SvxCreateNumRuleCompare();
+}
+
+// XStyleFamiliesSupplier
+uno::Reference< container::XNameAccess > SAL_CALL SvxSimpleUnoModel::getStyleFamilies( )
+{
+ uno::Reference< container::XNameAccess > xStyles;
+ return xStyles;
+}
+
+// XModel
+sal_Bool SAL_CALL SvxSimpleUnoModel::attachResource( const OUString&, const css::uno::Sequence< css::beans::PropertyValue >& )
+{
+ return false;
+}
+
+OUString SAL_CALL SvxSimpleUnoModel::getURL( )
+{
+ return OUString();
+}
+
+css::uno::Sequence< css::beans::PropertyValue > SAL_CALL SvxSimpleUnoModel::getArgs( )
+{
+ Sequence< beans::PropertyValue > aSeq;
+ return aSeq;
+}
+
+void SAL_CALL SvxSimpleUnoModel::connectController( const css::uno::Reference< css::frame::XController >& )
+{
+}
+
+void SAL_CALL SvxSimpleUnoModel::disconnectController( const css::uno::Reference< css::frame::XController >& )
+{
+}
+
+void SAL_CALL SvxSimpleUnoModel::lockControllers( )
+{
+}
+
+void SAL_CALL SvxSimpleUnoModel::unlockControllers( )
+{
+}
+
+sal_Bool SAL_CALL SvxSimpleUnoModel::hasControllersLocked( )
+{
+ return true;
+}
+
+css::uno::Reference< css::frame::XController > SAL_CALL SvxSimpleUnoModel::getCurrentController( )
+{
+ uno::Reference< frame::XController > xRet;
+ return xRet;
+}
+
+void SAL_CALL SvxSimpleUnoModel::setCurrentController( const css::uno::Reference< css::frame::XController >& )
+{
+}
+
+css::uno::Reference< css::uno::XInterface > SAL_CALL SvxSimpleUnoModel::getCurrentSelection( )
+{
+ uno::Reference< XInterface > xRet;
+ return xRet;
+}
+
+
+// XComponent
+void SAL_CALL SvxSimpleUnoModel::dispose( )
+{
+}
+
+void SAL_CALL SvxSimpleUnoModel::addEventListener( const css::uno::Reference< css::lang::XEventListener >& )
+{
+}
+
+void SAL_CALL SvxSimpleUnoModel::removeEventListener( const css::uno::Reference< css::lang::XEventListener >& )
+{
+}
+
+namespace {
+
+class SvxXMLTextExportComponent : public SvXMLExport
+{
+public:
+ SvxXMLTextExportComponent(
+ const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ EditEngine* pEditEngine,
+ const ESelection& rSel,
+ const css::uno::Reference< css::xml::sax::XDocumentHandler >& rHandler );
+
+ // methods without content:
+ virtual void ExportAutoStyles_() override;
+ virtual void ExportMasterStyles_() override;
+ virtual void ExportContent_() override;
+
+private:
+ css::uno::Reference< css::text::XText > mxText;
+};
+
+}
+
+SvxXMLTextExportComponent::SvxXMLTextExportComponent(
+ const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ EditEngine* pEditEngine,
+ const ESelection& rSel,
+ const css::uno::Reference< css::xml::sax::XDocumentHandler > & xHandler)
+: SvXMLExport( xContext, "", /*rFileName*/"", xHandler, static_cast<frame::XModel*>(new SvxSimpleUnoModel()), FieldUnit::CM,
+ SvXMLExportFlags::OASIS | SvXMLExportFlags::AUTOSTYLES | SvXMLExportFlags::CONTENT )
+{
+ SvxEditEngineSource aEditSource( pEditEngine );
+
+ static const SfxItemPropertyMapEntry SvxXMLTextExportComponentPropertyMap[] =
+ {
+ SVX_UNOEDIT_CHAR_PROPERTIES,
+ SVX_UNOEDIT_FONT_PROPERTIES,
+ { UNO_NAME_NUMBERING_RULES, EE_PARA_NUMBULLET, cppu::UnoType<css::container::XIndexReplace>::get(), 0, 0 },
+ { UNO_NAME_NUMBERING, EE_PARA_BULLETSTATE,cppu::UnoType<bool>::get(), 0, 0 },
+ { UNO_NAME_NUMBERING_LEVEL, EE_PARA_OUTLLEVEL, ::cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ SVX_UNOEDIT_PARA_PROPERTIES,
+ };
+ static SvxItemPropertySet aSvxXMLTextExportComponentPropertySet( SvxXMLTextExportComponentPropertyMap, EditEngine::GetGlobalItemPool() );
+
+ rtl::Reference<SvxUnoText> pUnoText = new SvxUnoText( &aEditSource, &aSvxXMLTextExportComponentPropertySet, mxText );
+ pUnoText->SetSelection( rSel );
+ mxText = pUnoText;
+
+}
+
+void SvxWriteXML( EditEngine& rEditEngine, SvStream& rStream, const ESelection& rSel )
+{
+ try
+ {
+ do
+ {
+ // create service factory
+ uno::Reference<uno::XComponentContext> xContext( ::comphelper::getProcessComponentContext() );
+
+ // create document handler
+ uno::Reference< xml::sax::XWriter > xWriter = xml::sax::Writer::create( xContext );
+
+ // create output stream and active data source
+ uno::Reference<io::XOutputStream> xOut( new utl::OOutputStreamWrapper( rStream ) );
+
+/* testcode
+ static constexpr OUStringLiteral aURL( u"file:///e:/test.xml" );
+ SvFileStream aStream(aURL, StreamMode::WRITE | StreamMode::TRUNC);
+ xOut = new utl::OOutputStreamWrapper(aStream);
+*/
+
+
+ xWriter->setOutputStream( xOut );
+
+ // export text
+
+ // SvxXMLTextExportComponent aExporter( &rEditEngine, rSel, aName, xHandler );
+ uno::Reference< xml::sax::XDocumentHandler > xHandler(xWriter, UNO_QUERY_THROW);
+ rtl::Reference< SvxXMLTextExportComponent > xExporter( new SvxXMLTextExportComponent( xContext, &rEditEngine, rSel, xHandler ) );
+
+ xExporter->exportDoc();
+
+/* testcode
+ aStream.Close();
+*/
+
+ }
+ while( false );
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("editeng", "exception during xml export");
+ }
+}
+
+// methods without content:
+void SvxXMLTextExportComponent::ExportAutoStyles_()
+{
+ rtl::Reference< XMLTextParagraphExport > xTextExport( GetTextParagraphExport() );
+
+ xTextExport->collectTextAutoStyles( mxText );
+ xTextExport->exportTextAutoStyles();
+}
+
+void SvxXMLTextExportComponent::ExportContent_()
+{
+ rtl::Reference< XMLTextParagraphExport > xTextExport( GetTextParagraphExport() );
+
+ xTextExport->exportText( mxText );
+}
+
+void SvxXMLTextExportComponent::ExportMasterStyles_() {}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/xml/xmltxtimp.cxx b/editeng/source/xml/xmltxtimp.cxx
new file mode 100644
index 0000000000..c752a0eb70
--- /dev/null
+++ b/editeng/source/xml/xmltxtimp.cxx
@@ -0,0 +1,233 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/text/XText.hpp>
+#include <comphelper/processfactory.hxx>
+#include <unotools/streamwrap.hxx>
+#include <svl/itemprop.hxx>
+#include <utility>
+#include <xmloff/xmlimp.hxx>
+#include <xmloff/xmlictxt.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmlstyle.hxx>
+#include "editsource.hxx"
+#include <editxml.hxx>
+#include <editdoc.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/unotext.hxx>
+#include <editeng/unoprnms.hxx>
+#include <editeng/unoipset.hxx>
+#include <cassert>
+#include <unomodel.hxx>
+
+using namespace com::sun::star;
+using namespace com::sun::star::document;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::xml::sax;
+using namespace com::sun::star::text;
+using namespace cppu;
+using namespace xmloff::token;
+
+namespace {
+
+class SvxXMLTextImportContext : public SvXMLImportContext
+{
+public:
+ SvxXMLTextImportContext( SvXMLImport& rImport, uno::Reference< XText > xText );
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList) override;
+
+private:
+ const uno::Reference< XText > mxText;
+};
+
+}
+
+SvxXMLTextImportContext::SvxXMLTextImportContext( SvXMLImport& rImport, uno::Reference< XText > xText )
+: SvXMLImportContext( rImport ), mxText(std::move( xText ))
+{
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > SvxXMLTextImportContext::createFastChildContext(
+ sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList)
+{
+ SvXMLImportContext* pContext = nullptr;
+ if(nElement == XML_ELEMENT(OFFICE, XML_BODY ))
+ {
+ pContext = new SvxXMLTextImportContext( GetImport(), mxText );
+ }
+ else if( nElement == XML_ELEMENT(OFFICE, XML_AUTOMATIC_STYLES ) )
+ {
+ pContext = new SvXMLStylesContext( GetImport() );
+ GetImport().GetTextImport()->SetAutoStyles( static_cast<SvXMLStylesContext*>(pContext) );
+ }
+ else
+ pContext = GetImport().GetTextImport()->CreateTextChildContext( GetImport(), nElement, xAttrList );
+ return pContext;
+}
+
+namespace {
+
+class SvxXMLXTextImportComponent : public SvXMLImport
+{
+public:
+ SvxXMLXTextImportComponent(
+ const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ uno::Reference< XText > xText );
+
+ virtual SvXMLImportContext* CreateFastContext(sal_Int32 nElement,
+ const ::css::uno::Reference< ::css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+private:
+ const uno::Reference< XText > mxText;
+};
+
+}
+
+SvXMLImportContext *SvxXMLXTextImportComponent::CreateFastContext(
+ sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList >& /*xAttrList*/)
+{
+ SvXMLImportContext* pContext = nullptr;
+
+ if(nElement == XML_ELEMENT(OFFICE, XML_DOCUMENT_CONTENT ) )
+ {
+ pContext = new SvxXMLTextImportContext( *this, mxText );
+ }
+
+ return pContext;
+}
+
+SvxXMLXTextImportComponent::SvxXMLXTextImportComponent(
+ const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ uno::Reference< XText > xText )
+: SvXMLImport(xContext, ""),
+ mxText(std::move( xText ))
+{
+ GetTextImport()->SetCursor( mxText->createTextCursor() );
+ SvXMLImport::setTargetDocument(new SvxSimpleUnoModel);
+}
+
+EditPaM SvxReadXML( EditEngine& rEditEngine, SvStream& rStream, const ESelection& rSel )
+{
+ SvxEditEngineSource aEditSource( &rEditEngine );
+
+ static const SfxItemPropertyMapEntry SvxXMLTextImportComponentPropertyMap[] =
+ {
+ SVX_UNOEDIT_CHAR_PROPERTIES,
+ SVX_UNOEDIT_FONT_PROPERTIES,
+// bullets & numbering props, tdf#128046
+ { UNO_NAME_NUMBERING_RULES, EE_PARA_NUMBULLET, cppu::UnoType<css::container::XIndexReplace>::get(), 0, 0 },
+ { UNO_NAME_NUMBERING, EE_PARA_BULLETSTATE,cppu::UnoType<bool>::get(), 0, 0 },
+ { UNO_NAME_NUMBERING_LEVEL, EE_PARA_OUTLLEVEL, ::cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ SVX_UNOEDIT_PARA_PROPERTIES,
+ };
+ static SvxItemPropertySet aSvxXMLTextImportComponentPropertySet( SvxXMLTextImportComponentPropertyMap, EditEngine::GetGlobalItemPool() );
+
+ assert(!rSel.HasRange());
+ //get the initial para count before paste
+ sal_uInt32 initialParaCount = rEditEngine.GetEditDoc().Count();
+ //insert para breaks before inserting the copied text
+ rEditEngine.InsertParaBreak( rEditEngine.CreateSelection( rSel ).Max() );
+ rEditEngine.InsertParaBreak( rEditEngine.CreateSelection( rSel ).Max() );
+
+ // Init return PaM.
+ EditPaM aPaM( rEditEngine.CreateSelection( rSel ).Max());
+
+ ESelection aSel(rSel.nStartPara+1, 0, rSel.nEndPara+1, 0);
+ uno::Reference<text::XText > xParent;
+ rtl::Reference<SvxUnoText> pUnoText = new SvxUnoText( &aEditSource, &aSvxXMLTextImportComponentPropertySet, xParent );
+ pUnoText->SetSelection( aSel );
+
+ try
+ {
+ do
+ {
+ uno::Reference<uno::XComponentContext> xContext( ::comphelper::getProcessComponentContext() );
+
+ uno::Reference<io::XInputStream> xInputStream = new utl::OInputStreamWrapper( rStream );
+
+/* testcode
+ static constexpr OUStringLiteral aURL( u"file:///e:/test.xml" );
+ SfxMedium aMedium( aURL, StreamMode::READ | STREAM_NOCREATE, sal_True );
+ uno::Reference<io::XOutputStream> xOut( new utl::OOutputStreamWrapper( *aMedium.GetOutStream() ) );
+
+ aMedium.GetInStream()->Seek( 0 );
+ uno::Reference< io::XActiveDataSource > xSource( aMedium.GetDataSource() );
+
+ if( !xSource.is() )
+ {
+ OSL_FAIL( "got no data source from medium" );
+ break;
+ }
+
+ uno::Reference< XInterface > xPipe( Pipe::create(comphelper::getComponentContext(xServiceFactory)), UNO_QUERY );
+
+ // connect pipe's output stream to the data source
+ xSource->setOutputStream( uno::Reference< io::XOutputStream >::query( xPipe ) );
+
+ xml::sax::InputSource aParserInput;
+ aParserInput.aInputStream.set( xPipe, UNO_QUERY );
+ aParserInput.sSystemId = aMedium.GetName();
+
+
+ if( xSource.is() )
+ {
+ uno::Reference< io::XActiveDataControl > xSourceControl( xSource, UNO_QUERY );
+ xSourceControl->start();
+ }
+
+*/
+
+ // uno::Reference< XDocumentHandler > xHandler( new SvxXMLXTextImportComponent( xText ) );
+ rtl::Reference< SvxXMLXTextImportComponent > xImport( new SvxXMLXTextImportComponent( xContext, pUnoText ) );
+
+ xml::sax::InputSource aParserInput;
+ aParserInput.aInputStream = xInputStream;
+// aParserInput.sSystemId = aMedium.GetName();
+ xImport->parseStream( aParserInput );
+ }
+ while(false);
+
+ //remove the extra para breaks
+ EditDoc& pDoc = rEditEngine.GetEditDoc();
+ rEditEngine.ParaAttribsToCharAttribs( pDoc.GetObject( rSel.nEndPara ) );
+ rEditEngine.ConnectParagraphs( pDoc.GetObject( rSel.nEndPara ),
+ pDoc.GetObject( rSel.nEndPara + 1 ), true );
+ rEditEngine.ParaAttribsToCharAttribs( pDoc.GetObject( pDoc.Count() - initialParaCount + aSel.nEndPara - 2 ) );
+ rEditEngine.ConnectParagraphs( pDoc.GetObject( pDoc.Count() - initialParaCount + aSel.nEndPara - 2 ),
+ pDoc.GetObject( pDoc.Count() - initialParaCount + aSel.nEndPara -1 ), true );
+
+ // The final join is to be returned.
+ aPaM = rEditEngine.ConnectParagraphs( pDoc.GetObject( pDoc.Count() - initialParaCount + aSel.nEndPara - 2 ),
+ pDoc.GetObject( pDoc.Count() - initialParaCount + aSel.nEndPara -1 ), true );
+ }
+ catch( const uno::Exception& )
+ {
+ }
+
+ return aPaM;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */